From 734b370a9f968a8234303cbb404c432c5c2069eb Mon Sep 17 00:00:00 2001 From: Frederik Hennig <frederik.hennig@fau.de> Date: Wed, 22 Nov 2023 14:52:25 +0900 Subject: [PATCH] small mdspan refactor. Added install instructions and usage primer. --- docs/index.md | 58 ++++++++++++++++++- mkdocs.yml | 8 ++- .../source_concepts/cpp/__init__.py | 5 +- .../source_concepts/cpp/std_mdspan.py | 27 ++++++++- tests/cmake_integration/kernels.py | 2 - tests/mdspan/Makefile | 2 +- tests/mdspan/kernels.py | 27 +++------ tests/mdspan/main.cpp | 16 +++-- 8 files changed, 113 insertions(+), 32 deletions(-) diff --git a/docs/index.md b/docs/index.md index 68eb439..e830190 100644 --- a/docs/index.md +++ b/docs/index.md @@ -3,5 +3,61 @@ A bridge over the semantic gap between code emitted by [pystencils](https://pypi.org/project/pystencils/) 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. diff --git a/mkdocs.yml b/mkdocs.yml index 96fb45b..5125538 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,7 +1,9 @@ site_name: pystencils Source File Generator Documentation theme: name: material - features: navigation.tabs + features: + - navigation.tabs + - content.code.copy palette: scheme: slate primary: deep purple @@ -27,6 +29,10 @@ plugins: show_signature_annotations: True signature_crossrefs: True +markdown_extensions: + - pymdownx.highlight: + anchor_linenums: true + - pymdownx.superfences nav: - Home: index.md diff --git a/src/pystencilssfg/source_concepts/cpp/__init__.py b/src/pystencilssfg/source_concepts/cpp/__init__.py index 206e2e3..ed0b13e 100644 --- a/src/pystencilssfg/source_concepts/cpp/__init__.py +++ b/src/pystencilssfg/source_concepts/cpp/__init__.py @@ -1,6 +1,7 @@ -from .std_mdspan import std_mdspan +from .std_mdspan import StdMdspan, mdspan_ref from .std_vector import std_vector, std_vector_ref __all__ = [ - "std_mdspan", "std_vector", "std_vector_ref" + "StdMdspan", "std_vector", "std_vector_ref", + "mdspan_ref" ] diff --git a/src/pystencilssfg/source_concepts/cpp/std_mdspan.py b/src/pystencilssfg/source_concepts/cpp/std_mdspan.py index bd5c0ea..d600387 100644 --- a/src/pystencilssfg/source_concepts/cpp/std_mdspan.py +++ b/src/pystencilssfg/source_concepts/cpp/std_mdspan.py @@ -1,5 +1,8 @@ from typing import Union +import numpy as np + +from pystencils import Field from pystencils.typing import FieldPointerSymbol, FieldStrideSymbol, FieldShapeSymbol from ...tree import SfgStatements @@ -9,7 +12,7 @@ from ...types import PsType, cpp_typename, SrcType from ...exceptions import SfgException -class std_mdspan(SrcField): +class StdMdspan(SrcField): dynamic_extent = "std::dynamic_extent" def __init__(self, identifer: str, @@ -77,3 +80,25 @@ class std_mdspan(SrcField): f"assert( {self._identifier}.stride({coordinate}) == {stride} );", (), (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) diff --git a/tests/cmake_integration/kernels.py b/tests/cmake_integration/kernels.py index 901bdb0..5fcac7c 100644 --- a/tests/cmake_integration/kernels.py +++ b/tests/cmake_integration/kernels.py @@ -10,8 +10,6 @@ from pystencilssfg import SourceFileGenerator with SourceFileGenerator() as sfg: src, dst = fields("src, dst(1) : double[2D]") - h = sp.Symbol('h') - @kernel def poisson_jacobi(): dst[0, 0] @= (src[1, 0] + src[-1, 0] + src[0, 1] + src[0, -1]) / 4 diff --git a/tests/mdspan/Makefile b/tests/mdspan/Makefile index 1761528..5a2fb0e 100644 --- a/tests/mdspan/Makefile +++ b/tests/mdspan/Makefile @@ -20,7 +20,7 @@ clean: $(GEN_SRC)/kernels.cpp $(GEN_SRC)/kernels.h &: kernels.py @$(dir_guard) - $(PYTHON) $< -d $(@D) + $(PYTHON) $< --sfg-output-dir $(@D) $(OBJ)/kernels.o: $(GEN_SRC)/kernels.cpp $(GEN_SRC)/kernels.h @$(dir_guard) diff --git a/tests/mdspan/kernels.py b/tests/mdspan/kernels.py index 3f045ba..dc790d2 100644 --- a/tests/mdspan/kernels.py +++ b/tests/mdspan/kernels.py @@ -1,32 +1,23 @@ import sympy as sp -import numpy as np -from pystencils.session import * +from pystencils import fields, kernel from pystencilssfg import SourceFileGenerator -from pystencilssfg.source_concepts.cpp import std_mdspan - -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) - +from pystencilssfg.source_concepts.cpp import mdspan_ref with SourceFileGenerator() as sfg: - src, dst = ps.fields("src, dst(1) : double[2D]") - - h = sp.Symbol('h') + u_src, u_dst, f = fields("u_src, u_dst, f(1) : double[2D]", layout="fzyx") + h = sp.Symbol("h") - @ps.kernel + @kernel 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) sfg.function("jacobi_smooth")( - sfg.map_field(src, field_t(src)), - sfg.map_field(dst, field_t(dst)), + 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) ) diff --git a/tests/mdspan/main.cpp b/tests/mdspan/main.cpp index d8247a5..21c8015 100644 --- a/tests/mdspan/main.cpp +++ b/tests/mdspan/main.cpp @@ -25,34 +25,38 @@ int main(int argc, char ** argv){ std::vector< double > data_dst(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 j = 0; j < N; ++j){ if(i == 0 || j == 0 || i == N-1 || j == N-1){ src[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){ - poisson::jacobi_smooth(dst, src); + jacobi_smooth(f, h, dst, src); 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"; } else { for(uint32_t i = 0; i < N; ++i){ 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; } -- GitLab