diff --git a/docs/source/api/composer.rst b/docs/source/api/composer.rst index a969d4ff91455f0fda51813aab93e23c4b0b7098..667b0de60f9564e98de46b562e0fcc0afe7cf2a1 100644 --- a/docs/source/api/composer.rst +++ b/docs/source/api/composer.rst @@ -1,6 +1,6 @@ -*************************************** -Composer API (`pystencilssfg.composer`) -*************************************** +***************************************** +Composer API (``pystencilssfg.composer``) +***************************************** .. autoclass:: pystencilssfg.composer.SfgComposer :members: diff --git a/docs/source/api/lang.rst b/docs/source/api/lang.rst index efc69d98c3b278a3fa34b5164a35f58f76c6e2d5..c83317e506549eb9ca9d86bc71765d196306ef09 100644 --- a/docs/source/api/lang.rst +++ b/docs/source/api/lang.rst @@ -9,8 +9,20 @@ Expressions .. automodule:: pystencilssfg.lang.expressions :members: -C++ Standard Library (`pystencilssfg.lang.cpp`) ------------------------------------------------ +Header Files +------------ + +.. automodule:: pystencilssfg.lang.headers + :members: + +Data Types +---------- + +.. automodule:: pystencilssfg.lang.types + :members: + +C++ Standard Library (``pystencilssfg.lang.cpp``) +------------------------------------------------- Quick Access ^^^^^^^^^^^^ diff --git a/docs/source/conf.py b/docs/source/conf.py index 40d5a0fbccc11bddd40d0da96a3dc883b64cdc6e..4bdf700da8e949023ddc1a56fd44d915a9f842db 100644 --- a/docs/source/conf.py +++ b/docs/source/conf.py @@ -65,12 +65,15 @@ html_theme_options = { intersphinx_mapping = { "python": ("https://docs.python.org/3.8", None), - "numpy": ("https://docs.scipy.org/doc/numpy/", None), - "matplotlib": ("https://matplotlib.org/", None), + "numpy": ("https://numpy.org/doc/stable/", None), "sympy": ("https://docs.sympy.org/latest/", None), "pystencils": ("https://da15siwa.pages.i10git.cs.fau.de/dev-docs/pystencils-nbackend/", None), } +# References + +# Treat `single-quoted` code blocks as references to any +default_role = "any" # Autodoc options diff --git a/src/pystencilssfg/composer/basic_composer.py b/src/pystencilssfg/composer/basic_composer.py index c48410c0619482dcbe44de21742727e1b27ade8e..90146e596123eedc73eea5f813964f7593b6f43f 100644 --- a/src/pystencilssfg/composer/basic_composer.py +++ b/src/pystencilssfg/composer/basic_composer.py @@ -286,7 +286,7 @@ class SfgBasicComposer(SfgIComposer): When using `call`, the given kernel will simply be called as a function. To invoke a GPU kernel on a specified launch grid, use `cuda_invoke` - or the interfaces of `pystencilssfg.extensions.sycl` instead. + or the interfaces of ``pystencilssfg.extensions.sycl`` instead. Args: kernel_handle: Handle to a kernel previously added to some kernel namespace. @@ -300,6 +300,7 @@ class SfgBasicComposer(SfgIComposer): threads_per_block: ExprLike, stream: ExprLike | None, ): + """Dispatch a CUDA kernel to the device.""" num_blocks_str = str(num_blocks) tpb_str = str(threads_per_block) stream_str = str(stream) if stream is not None else None diff --git a/src/pystencilssfg/lang/cpp/std_mdspan.py b/src/pystencilssfg/lang/cpp/std_mdspan.py index 7c3fd3598cb59ff1157c63619eba092b40cc6654..4f7616011339d6640412305493f064972c3f081f 100644 --- a/src/pystencilssfg/lang/cpp/std_mdspan.py +++ b/src/pystencilssfg/lang/cpp/std_mdspan.py @@ -17,17 +17,61 @@ from ...lang import SrcField, IFieldExtraction, cpptype, Ref, HeaderFile class StdMdspan(SrcField): """Represents an `std::mdspan` instance. - **On Standard Library Adoption** + The `std::mdspan <https://en.cppreference.com/w/cpp/container/mdspan>`_ + provides non-owning views into contiguous or strided n-dimensional arrays. + It has been added to the C++ STL with the C++23 standard. + As such, it is a natural data structure to target with pystencils kernels. - Since `std::mdspan` is not yet widely adopted + **Concerning Headers and Namespaces** + + Since ``std::mdspan`` is not yet widely adopted (libc++ ships it as of LLVM 18, but GCC libstdc++ does not include it yet), you might have to manually include an implementation in your project - (you can get a reference implementation [here](https://github.com/kokkos/mdspan)). + (you can get a reference implementation at https://github.com/kokkos/mdspan). However, when working with a non-standard mdspan implementation, the path to its the header and the namespace it is defined in will likely be different. - To tell pystencils-sfg which headers to include and which namespace to use for `mdspan`, - use `StdMdspan.configure`. + To tell pystencils-sfg which headers to include and which namespace to use for ``mdspan``, + use `StdMdspan.configure`; + for instance, adding this call before creating any ``mdspan`` objects will + set their namespace to `std::experimental`, and require ``<experimental/mdspan>`` to be imported: + + >>> from pystencilssfg.lang.cpp import std + >>> std.mdspan.configure("std::experimental", "<experimental/mdspan>") + + **Creation from pystencils fields** + + Using `from_field`, ``mdspan`` objects can be created directly from `Field <pystencils.Field>` instances. + The `extents`_ and `layout_policy`_ of the ``mdspan`` type will be inferred from the field. + Each fixed entry in the field's shape will become a fixed entry of the ``mdspan``'s extents. + The field's memory layout will be mapped onto a predefined ``LayoutPolicy`` according to this table: + + +------------------------+--------------------------+ + | pystencils Layout Name | ``mdspan`` Layout Policy | + +========================+==========================+ + | ``"fzyx"`` | `std::layout_left`_ | + | ``"soa"`` | | + | ``"f"`` | | + | ``"reverse_numpy"`` | | + +------------------------+--------------------------+ + | ``"c"`` | `std::layout_right`_ | + | ``"numpy"`` | | + +------------------------+--------------------------+ + | ``"zyxf"`` | `std::layout_stride`_ | + | ``"aos"`` | | + +------------------------+--------------------------+ + + The structure-of-arrays (or ZYXF) layout has no equivalent layout policy in the C++ standard, + so it can only be mapped onto ``layout_stride``, which models user-defined strides. + + .. _extents: https://en.cppreference.com/w/cpp/container/mdspan/extents + .. _layout_policy: https://en.cppreference.com/w/cpp/named_req/LayoutMappingPolicy + .. _std::layout_left: https://en.cppreference.com/w/cpp/container/mdspan/layout_left + .. _std::layout_right: https://en.cppreference.com/w/cpp/container/mdspan/layout_right + .. _std::layout_stride: https://en.cppreference.com/w/cpp/container/mdspan/layout_stride + + Args: + T: Element type of the mdspan """ dynamic_extent = "std::dynamic_extent" @@ -37,7 +81,7 @@ class StdMdspan(SrcField): @classmethod def configure(cls, namespace: str = "std", header: str | HeaderFile = "<mdspan>"): - """Configure the namespace and header `mdspan` is defined in.""" + """Configure the namespace and header ``std::mdspan`` is defined in.""" cls._namespace = namespace cls._template = cpptype( f"{namespace}::mdspan< {{T}}, {{extents}}, {{layout_policy}} >", header @@ -47,14 +91,14 @@ class StdMdspan(SrcField): self, T: UserTypeSpec, extents: tuple[int | str, ...], - extents_type: PsType = PsUnsignedIntegerType(64), + extents_type: UserTypeSpec = PsUnsignedIntegerType(64), layout_policy: str | None = None, ref: bool = False, const: bool = False, ): T = create_type(T) - extents_type_str = extents_type.c_string() + extents_type_str = create_type(extents_type).c_string() extents_str = f"{self._namespace}::extents< {extents_type_str}, {', '.join(str(e) for e in extents)} >" layout_policy = ( @@ -97,23 +141,23 @@ class StdMdspan(SrcField): def from_field( cls, field: Field, - extents_type: PsType = PsUnsignedIntegerType(64), + extents_type: UserTypeSpec = PsUnsignedIntegerType(64), + layout_policy: str | None = None, ref: bool = False, const: bool = False, ): """Creates a `std::mdspan` instance for a given pystencils field.""" from pystencils.field import layout_string_to_tuple - layout_policy: str - - if field.layout == layout_string_to_tuple("fzyx", field.spatial_dimensions): - # f is the rightmost extent, which is slowest with `layout_left` - layout_policy = f"{cls._namespace}::layout_left" - elif field.layout == layout_string_to_tuple("zyxf", field.spatial_dimensions): - # f, as the rightmost extent, is the fastest - layout_policy = f"{cls._namespace}::layout_right" - else: - layout_policy = f"{cls._namespace}::layout_stride" + if layout_policy is None: + if field.layout == layout_string_to_tuple("fzyx", field.spatial_dimensions): + # f is the rightmost extent, which is slowest with `layout_left` + layout_policy = f"{cls._namespace}::layout_left" + elif field.layout == layout_string_to_tuple("c", field.spatial_dimensions): + # f, as the rightmost extent, is the fastest + layout_policy = f"{cls._namespace}::layout_right" + else: + layout_policy = f"{cls._namespace}::layout_stride" extents: list[str | int] = [] diff --git a/src/pystencilssfg/lang/expressions.py b/src/pystencilssfg/lang/expressions.py index ccaf2d1bf3fe224e35be1508f13dfeb3251a84ee..53064bd13a201b0716545bf1e84677baa8df3be9 100644 --- a/src/pystencilssfg/lang/expressions.py +++ b/src/pystencilssfg/lang/expressions.py @@ -180,7 +180,7 @@ class AugExpr: - Calling `var <AugExpr.var>` on an unbound `AugExpr` turns it into a *variable* with the given name. This variable expression takes its set of required header files from the - `required_headers <PsType.required_headers>` field of the data type of the `AugExpr`. + `required_headers <pystencils.types.PsType.required_headers>` field of the data type of the `AugExpr`. - Using `bind <AugExpr.bind>`, an unbound `AugExpr` can be bound to an arbitrary string of code. The `bind` method mirrors the interface of `str.format` to combine sub-expressions and collect their dependencies. @@ -206,6 +206,7 @@ class AugExpr: self._is_variable = False def var(self, name: str): + """Bind an unbound `AugExpr` instance as a new variable of given name.""" v = SfgVar(name, self.get_dtype()) expr = VarExpr(v) return self._bind(expr) @@ -220,6 +221,7 @@ class AugExpr: return AugExpr().bind(fmt, *deps, **kwdeps) def bind(self, fmt: str | AugExpr, *deps, **kwdeps): + """Bind an unbound `AugExpr` instance to an expression.""" if isinstance(fmt, AugExpr): if bool(deps) or bool(kwdeps): raise ValueError( @@ -342,7 +344,8 @@ def asvar(var: VarLike) -> SfgVar: Raises: ValueError: If given a non-variable `AugExpr`, - a `TypedSymbol` with a `DynamicType`, + a `TypedSymbol <pystencils.TypedSymbol>` + with a `DynamicType <pystencils.sympyextensions.typed_sympy.DynamicType>`, or any non-variable-like object. """ match var: