From dde18985d3245a188e13feba7b5d8b0c45112c5f Mon Sep 17 00:00:00 2001
From: Frederik Hennig <frederik.hennig@fau.de>
Date: Wed, 12 Feb 2025 21:03:51 +0100
Subject: [PATCH] Write new Getting Started guide. Enable usage of mockup
 generator for docs.

---
 docs/source/.gitignore                 |   1 +
 docs/source/_util/sfg_mockup.py        |  19 ++++
 docs/source/conf.py                    |  14 ---
 docs/source/getting_started.md         | 136 +++++++++++++++++++++++++
 docs/source/index.md                   |   7 ++
 docs/source/usage/generator_scripts.md |  30 ++++--
 src/pystencilssfg/emission/emitter.py  |   4 +-
 src/pystencilssfg/generator.py         |  21 ++--
 8 files changed, 201 insertions(+), 31 deletions(-)
 create mode 100644 docs/source/.gitignore
 create mode 100644 docs/source/_util/sfg_mockup.py
 create mode 100644 docs/source/getting_started.md

diff --git a/docs/source/.gitignore b/docs/source/.gitignore
new file mode 100644
index 0000000..6577d58
--- /dev/null
+++ b/docs/source/.gitignore
@@ -0,0 +1 @@
+**/_sfg_out
\ No newline at end of file
diff --git a/docs/source/_util/sfg_mockup.py b/docs/source/_util/sfg_mockup.py
new file mode 100644
index 0000000..4bcaad1
--- /dev/null
+++ b/docs/source/_util/sfg_mockup.py
@@ -0,0 +1,19 @@
+from pystencilssfg import SourceFileGenerator
+from pystencilssfg.config import SfgConfig
+
+
+class DocsMockupGenerator(SourceFileGenerator):
+    scriptname: str = "script"
+
+    def _scriptname(self) -> str:
+        return f"{DocsMockupGenerator.scriptname}.py"
+
+    def __init__(
+        self, sfg_config: SfgConfig | None = None, keep_unknown_argv: bool = False
+    ):
+        if sfg_config is None:
+            sfg_config = SfgConfig()
+
+        sfg_config.output_directory = "_sfg_out"
+
+        super().__init__(sfg_config, keep_unknown_argv=True)
diff --git a/docs/source/conf.py b/docs/source/conf.py
index d6aab17..db8f782 100644
--- a/docs/source/conf.py
+++ b/docs/source/conf.py
@@ -89,17 +89,3 @@ myst_enable_extensions = [
     "dollarmath",
     "colon_fence",
 ]
-
-#   Prepare code generation examples
-
-def build_examples():
-    import subprocess
-    import os
-
-    examples_dir = os.path.join("usage", "examples",)
-
-    subprocess.run(["python", "build.py"], cwd=examples_dir).check_returncode()
-
-
-print("Generating output of example scripts...")
-build_examples()
diff --git a/docs/source/getting_started.md b/docs/source/getting_started.md
new file mode 100644
index 0000000..822ea0b
--- /dev/null
+++ b/docs/source/getting_started.md
@@ -0,0 +1,136 @@
+---
+file_format: mystnb
+kernelspec:
+  name: python3
+---
+
+# Getting Started
+
+## Prequesites
+
+To use pystencils-sfg, you will need at least Python 3.10.
+You will also need the appropriate compilers for building the generated code,
+such as
+ - a modern C++ compiler (e.g. GCC, clang)
+ - `nvcc` for CUDA or `hipcc` for HIP
+ - Intel OneAPI or AdaptiveCpp for SYCL
+
+Furthermore, an installation of clang-format for automatic code formatting is strongly recommended. 
+
+## Install the Latest Development Revision
+
+As pystencils-sfg is still unreleased, it can at this time only be obtained directly
+from its Git repository.
+
+Create a fresh [virtual environment](https://docs.python.org/3/library/venv.html) or activate
+an existing one. Install both the pystencils 2.0 and pystencils-sfg development revisions from Git:
+
+```{code-block} bash
+pip install "git+https://i10git.cs.fau.de/pycodegen/pystencils.git@v2.0-dev"
+pip install "git+https://i10git.cs.fau.de/pycodegen/pystencils-sfg.git"
+```
+
+````{caution}
+
+*pystencils-sfg* is not compatible with the *pystencils 1.3.x* releases available from PyPI;
+at the moment, you will still have to manually install the latest version of pystencils 2.0.
+````
+
+## Check your Installation
+
+To verify that the SFG was successfully installed, execute the following command:
+
+```{code-block} bash
+sfg-cli version
+```
+
+You should see an output like `0.1a4+...`.
+
+## Writing a Basic Generator Script
+
+To start using pystencils-sfg, create a new empty Python file and populate it with the
+following minimal skeleton:
+
+```{code-block} python
+from pystencilssfg import SourceFileGenerator
+
+with SourceFileGenerator() as sfg:
+    ...
+```
+
+The above snippet defines the basic structure of a *generator script*.
+When executed, the above will produce two (nearly) empty C++ files
+in the current folder, both with the same name as your Python script
+but with `.hpp` and `.cpp` file extensions instead.
+
+Generator scripts are the primary mode of using pystencils-sfg;
+in them, code generation is orchestrated by the `SourceFileGenerator` context manager.
+When entering into the region controlled by the `SourceFileGenerator`,
+it supplies us with a *composer object*, customarily called `sfg`.
+Through the composer, we can declaratively populate the generated files with code.
+
+## Adding a pystencils Kernel
+
+One of the core applications of pystencils-sfg is to generate and wrap pystencils-kernels
+for usage within C++ applications.
+To register a kernel, pass its assignments to `sfg.kernels.create`.
+This gives you a *kernel handle*, through which you can call the kernel from a function:
+
+```{code-cell} ipython3
+:tags: [remove-cell]
+
+import sys
+from pathlib import Path
+
+mockup_path = Path("_util").resolve()
+sys.path.append(str(mockup_path))
+
+from sfg_mockup import DocsMockupGenerator as SourceFileGenerator
+```
+
+```{code-cell} ipython3
+:tags: [remove-cell]
+SourceFileGenerator.scriptname = "demo1"
+```
+
+```{code-cell} ipython3
+import pystencils as ps
+import sympy as sp
+
+with SourceFileGenerator() as sfg:
+    #   Define a copy kernel
+    src, dst = ps.fields("src, dst: [1D]")
+    c = sp.Symbol("c")
+
+    @ps.kernel
+    def scale():
+        dst.center @= c * src.center()
+
+    #   Register the kernel for code generation
+    scale_kernel = sfg.kernels.create(scale, "scale_kernel")
+
+    #   Wrap it in a function
+    sfg.function("scale")(
+        sfg.call(scale_kernel)
+    )
+
+```
+
+::::{tab-set}
+
+:::{tab-item} Generated Header
+```{literalinclude} _sfg_out/demo1.hpp
+:language: C++
+```
+:::
+
+:::{tab-item} Generated Implementation
+```{literalinclude} _sfg_out/demo1.cpp
+:language: C++
+```
+:::
+
+::::
+
+## Next Steps
+
diff --git a/docs/source/index.md b/docs/source/index.md
index 0cab083..b11cb53 100644
--- a/docs/source/index.md
+++ b/docs/source/index.md
@@ -1,5 +1,12 @@
 # The pystencils Source File Generator
 
+```{toctree}
+:maxdepth: 1
+:hidden:
+
+getting_started
+```
+
 ```{toctree}
 :maxdepth: 1
 :hidden:
diff --git a/docs/source/usage/generator_scripts.md b/docs/source/usage/generator_scripts.md
index 3141fee..343fd74 100644
--- a/docs/source/usage/generator_scripts.md
+++ b/docs/source/usage/generator_scripts.md
@@ -1,14 +1,32 @@
+
 (guide:generator_scripts)=
-# Generator Scripts
+# Generator Script Configuration and Command-Line Interface
 
-Writing generator scripts is the primary usage idiom of *pystencils-sfg*.
-A generator script is a Python script, say `kernels.py`, which contains *pystencils-sfg*
-code at the top level that, when executed, emits source code to a pair of files `kernels.hpp`
-and `kernels.cpp`. This guide describes how to write such a generator script, its structure, and how
-it can be used to generate code.
+Generator scripts are the primary mode of using pystencils-sfg.
+A generator script is a Python script, say `kernels.py`, which uses the pystencils-sfg API
+at the top level that, when executed, emits source code to a pair of files `kernels.hpp`
+and `kernels.cpp`.
+This guide describes the basic structure of generator scripts, their execution from the command line,
+the configuration of the code generator both inline and from the shell,
+as well as customization of their command-line options.
 
 ## Anatomy
 
+At minimum, each generator script must contain the following code:
+
+```{code-block} python
+from pystencilssfg import SourceFileGenerator
+
+with SourceFileGenerator() as sfg:
+    ...
+```
+
+The code generation process begins as the above code enters the region controlled by the
+`SourceFileGenerator`.
+The object `sfg` in the above snippet is the *composer*, which exposes the primary API
+for interacting with the code generator.
+
+
 The code generation process in a generator script is controlled by the `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,
diff --git a/src/pystencilssfg/emission/emitter.py b/src/pystencilssfg/emission/emitter.py
index c1b6e9c..7f8870b 100644
--- a/src/pystencilssfg/emission/emitter.py
+++ b/src/pystencilssfg/emission/emitter.py
@@ -13,8 +13,8 @@ class SfgCodeEmitter:
     def __init__(
         self,
         output_directory: Path,
-        code_style: CodeStyle,
-        clang_format: ClangFormatOptions,
+        code_style: CodeStyle = CodeStyle(),
+        clang_format: ClangFormatOptions = ClangFormatOptions(),
     ):
         self._output_dir = output_directory
         self._code_style = code_style
diff --git a/src/pystencilssfg/generator.py b/src/pystencilssfg/generator.py
index f3f67a0..379b24d 100644
--- a/src/pystencilssfg/generator.py
+++ b/src/pystencilssfg/generator.py
@@ -32,14 +32,7 @@ class SourceFileGenerator:
             `sfg.context.argv`.
     """
 
-    def __init__(
-        self,
-        sfg_config: SfgConfig | None = None,
-        keep_unknown_argv: bool = False,
-    ):
-        if sfg_config and not isinstance(sfg_config, SfgConfig):
-            raise TypeError("sfg_config is not an SfgConfiguration.")
-
+    def _scriptname(self) -> str:
         import __main__
 
         if not hasattr(__main__, "__file__"):
@@ -50,7 +43,17 @@ class SourceFileGenerator:
             )
 
         scriptpath = Path(__main__.__file__)
-        scriptname = scriptpath.name
+        return scriptpath.name
+
+    def __init__(
+        self,
+        sfg_config: SfgConfig | None = None,
+        keep_unknown_argv: bool = False,
+    ):
+        if sfg_config and not isinstance(sfg_config, SfgConfig):
+            raise TypeError("sfg_config is not an SfgConfiguration.")
+
+        scriptname = self._scriptname()
         basename = scriptname.rsplit(".")[0]
 
         from argparse import ArgumentParser
-- 
GitLab