diff --git a/.gitignore b/.gitignore index ef18ef29c682c0c471e5ecc4c974c7f9fe602763..c970c0ef7370d24590b25d9e42386114300323f3 100644 --- a/.gitignore +++ b/.gitignore @@ -21,5 +21,8 @@ dist htmlcov coverage.xml +# cmake +CMakeUserPresets.json + # scratch scratch \ No newline at end of file diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml new file mode 100644 index 0000000000000000000000000000000000000000..bf5e6fa193d4df6336eee83fc8a1def861ae5c46 --- /dev/null +++ b/.gitlab-ci.yml @@ -0,0 +1,29 @@ +stages: + - Documentation + - Deploy + + +build-user-manual: + image: i10git.cs.fau.de:5005/pycodegen/pycodegen/nox:alpine + tags: + - docker + stage: "Documentation" + needs: [] + script: + - nox --session user_manual + artifacts: + paths: + - user_manual/_sphinx_build/html + +pages: + image: alpine:latest + stage: "Deploy" + script: + - mv user_manual/_sphinx_build/html public # folder has to be named "public" for gitlab to publish it + artifacts: + paths: + - public + tags: + - docker + only: + - master@da15siwa/sfg-walberla diff --git a/CMakeLists.txt b/CMakeLists.txt index bab7c8fd4560a1c0e8359d6ba0cb4e7307468d02..15cdc03ad250bf7d9b197a8801a867f204ae4113 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,8 +1,11 @@ cmake_minimum_required( VERSION 3.24 ) project ( sfg-walberla ) -SET ( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${CMAKE_CURRENT_SOURCE_DIR}/cmake ) -find_package( PystencilsSfg REQUIRED ) +set(sfg_walberla_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}) + +list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake ) + +include( PrepareSFG ) add_library( sfg_walberla INTERFACE ) diff --git a/cmake/CodegenConfig.template.py b/cmake/CodegenConfig.template.py new file mode 100644 index 0000000000000000000000000000000000000000..07f29998fd188b7f4f95a2bd5409cb02f77f6c8d --- /dev/null +++ b/cmake/CodegenConfig.template.py @@ -0,0 +1,31 @@ +from pystencilssfg import SfgConfig +from sfg_walberla import WalberlaBuildConfig + + +def configure_sfg(cfg: SfgConfig): + cfg.extensions.header = "hpp" + cfg.extensions.impl = "cpp" + + +def project_info() -> WalberlaBuildConfig: + from sfg_walberla.build_config import cmake_parse_bool + + return WalberlaBuildConfig( + c_compiler_id="${CMAKE_C_COMPILER_ID}", + cxx_compiler_id="${CMAKE_CXX_COMPILER_ID}", + use_double_precision=cmake_parse_bool("${WALBERLA_DOUBLE_ACCURACY}"), + optimize_for_localhost=cmake_parse_bool("${WALBERLA_OPTIMIZE_FOR_LOCALHOST}"), + mpi_enabled=cmake_parse_bool("${WALBERLA_BUILD_WITH_MPI}"), + openmp_enabled=cmake_parse_bool("${WALBERLA_BUILD_WITH_OPENMP}"), + cuda_enabled=cmake_parse_bool("${WALBERLA_BUILD_WITH_CUDA}"), + hip_enabled=cmake_parse_bool("${WALBERLA_BUILD_WITH_HIP}"), + likwid_enabled=cmake_parse_bool("${WALBERLA_BUILD_WITH_LIKWID_MARKERS}"), + ) + + +def validate(): + _ = project_info() + + +if __name__ == "__main__": + validate() diff --git a/cmake/FindPystencilsSfg.cmake b/cmake/FindPystencilsSfg.cmake index a5e7b11d09ddb55da6802291a28be77d6a44f4f6..20a3fd596d99cf2db18e346609b8060bf86d32bc 100644 --- a/cmake/FindPystencilsSfg.cmake +++ b/cmake/FindPystencilsSfg.cmake @@ -1,16 +1,42 @@ -set( PystencilsSfg_FOUND OFF CACHE BOOL "pystencils source file generator found" ) +#[[ +Find-Module for pystencils-sfg. -mark_as_advanced( PystencilsSfg_FOUND ) +# Setting the Python interpreter -find_package( Python COMPONENTS Interpreter REQUIRED ) +If the cache entry PystencilsSfg_PYTHON_INTERPRETER is set, e.g. via the commandline +(`-DPystencilsSfg_PYTHON_INTERPRETER=<...>`), its value be taken as the Python interpreter +used to find and run pystencils-sfg. + +If the cache entry is unset, but the hint PystencilsSfg_PYTHON_PATH is set, its value will +be used as the Python interpreter. + +If none of these is set, a Python interpreter will be selected using the `FindPython` module. + +#]] + +if(NOT DEFINED CACHE{PystencilsSfg_PYTHON_INTERPRETER}) + # The Python interpreter cache variable is not set externally, so... + if(DEFINED PystencilsSfg_PYTHON_PATH) + # ... either initialize it from the hint variable ... + set( _sfg_cache_python_init ${PystencilsSfg_PYTHON_PATH} ) + else() + # ... or, if that is also unset, use the system Python + find_package( Python COMPONENTS Interpreter REQUIRED ) + set( _sfg_cache_python_init ${Python_EXECUTABLE} ) + endif() +endif() + +set(PystencilsSfg_PYTHON_INTERPRETER ${_sfg_cache_python_init} CACHE PATH "Path to the Python executable used to run pystencils-sfg") # Try to find pystencils-sfg in the python environment -execute_process(COMMAND ${Python_EXECUTABLE} -m pystencilssfg version --no-newline +execute_process(COMMAND ${PystencilsSfg_PYTHON_INTERPRETER} -m pystencilssfg version --no-newline RESULT_VARIABLE _PystencilsSfgFindResult OUTPUT_VARIABLE PystencilsSfg_VERSION ) if(${_PystencilsSfgFindResult} EQUAL 0) set( PystencilsSfg_FOUND ON ) +else() + set( PystencilsSfg_FOUND OFF ) endif() if(DEFINED PystencilsSfg_FIND_REQUIRED) @@ -21,8 +47,9 @@ endif() if(${PystencilsSfg_FOUND}) message( STATUS "Found pystencils Source File Generator (Version ${PystencilsSfg_VERSION})") + message( STATUS "Using Python interpreter ${PystencilsSfg_PYTHON_INTERPRETER} for SFG generator scripts.") - execute_process(COMMAND ${Python_EXECUTABLE} -m pystencilssfg cmake modulepath --no-newline + execute_process(COMMAND ${PystencilsSfg_PYTHON_INTERPRETER} -m pystencilssfg cmake modulepath --no-newline OUTPUT_VARIABLE _PystencilsSfg_CMAKE_MODULE_PATH) set( CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${_PystencilsSfg_CMAKE_MODULE_PATH}) diff --git a/cmake/PrepareSFG.cmake b/cmake/PrepareSFG.cmake new file mode 100644 index 0000000000000000000000000000000000000000..1f34e3c4006f8b2ab1883a5ccd38018f06b5d7c5 --- /dev/null +++ b/cmake/PrepareSFG.cmake @@ -0,0 +1,129 @@ +# Maybe set up private virtual environment + +set( + WALBERLA_CODEGEN_USE_PRIVATE_VENV ON + CACHE BOOL + "Create a private virtual Python environment inside the build tree for code generation" +) + +if( WALBERLA_CODEGEN_USE_PRIVATE_VENV ) + set(_codegen_venv_path ${CMAKE_CURRENT_BINARY_DIR}/codegen-venv) + set(_venv_python_exe ${_codegen_venv_path}/bin/python) + + find_package( Python COMPONENTS Interpreter REQUIRED ) + + if(NOT _sfg_private_venv_done) + message( STATUS "Setting up Python virtual environment at ${_codegen_venv_path}" ) + + # Create the venv and register its interpreter with pystencils-sfg + execute_process( + COMMAND ${Python_EXECUTABLE} -m venv ${_codegen_venv_path} + ) + + message( STATUS "Installing required Python packages..." ) + + execute_process( + COMMAND ${_venv_python_exe} -m pip install -r ${sfg_walberla_SOURCE_DIR}/cmake/venv-reqs.txt + OUTPUT_QUIET + ) + + execute_process( + COMMAND ${_venv_python_exe} -m pip install -e ${sfg_walberla_SOURCE_DIR} + OUTPUT_QUIET + ) + + set( _sfg_private_venv_done TRUE CACHE BOOL "" ) + set( _wlb_codegen_python_init ${_venv_python_exe} ) + mark_as_advanced(_sfg_private_venv_done) + endif() +else() + # Use the external Python environment, but check if all packages are installed + find_package( Python COMPONENTS Interpreter REQUIRED ) + + execute_process( + COMMAND ${Python_EXECUTABLE} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/check_python_env.py + RESULT_VARIABLE _package_check_status + OUTPUT_VARIABLE _package_check_output + ERROR_VARIABLE _package_check_error + ) + + message( STATUS ${_package_check_output} ) + + if(NOT (${_package_check_status} EQUAL 0) ) + message( FATAL_ERROR ${_package_check_error} ) + endif() + + set( _wlb_codegen_python_init ${Python_EXECUTABLE} ) +endif() + +set(PystencilsSfg_PYTHON_PATH ${_wlb_codegen_python_init}) +set( + WALBERLA_CODEGEN_PYTHON + ${_wlb_codegen_python_init} + CACHE PATH + "Path to the Python interpreter used for code generation" +) +mark_as_advanced(WALBERLA_CODEGEN_PYTHON) + +# Find pystencils-sfg + +find_package( PystencilsSfg REQUIRED ) + +# Project Configuration Module + +set( + WALBERLA_CODEGEN_CONFIG_MODULE + ${CMAKE_BINARY_DIR}/CodegenConfig.py + CACHE + FILEPATH + "Path to waLBerla-wide codegen config module" +) +mark_as_advanced( WALBERLA_CODEGEN_CONFIG_MODULE ) + +configure_file( + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/CodegenConfig.template.py + ${WALBERLA_CODEGEN_CONFIG_MODULE} +) + +message( STATUS "Wrote project-wide code generator configuration to ${WALBERLA_CODEGEN_CONFIG_MODULE}" ) + +#[[ +Run `pip install` in the code generation environment with the given arguments. +#]] +function(walberla_codegen_venv_install) + if(NOT WALBERLA_CODEGEN_USE_PRIVATE_VENV) + message( FATAL_ERROR "The private virtual environment for code generation is disabled" ) + endif() + + if(NOT _sfg_private_venv_done) + message( FATAL_ERROR "The private virtual environment for code generation was not initialized yet" ) + endif() + + execute_process( + COMMAND ${WALBERLA_CODEGEN_PYTHON} -m pip install ${ARGV} + ) +endfunction() + +# Code Generation Functions + +#[[ +Register code generation scripts for a CMake target. + +Signature: + +``` +walberla_generate_sources( <target> + SCRIPTS script1.py [script2.py ...] + [DEPENDS dependency1.py [dependency2.py...] ] + [FILE_EXTENSIONS <header-extension> <impl-extension>] + [OUTPUT_MODE <standalone|inline|header-only>] +) +``` + +This is a wrapper around `pystencilssfg_generate_target_sources` +without the `CONFIG_MODULE` parameter. +See also https://pycodegen.pages.i10git.cs.fau.de/pystencils-sfg/usage/project_integration.html#add-generator-scripts +#]] +function(walberla_generate_sources TARGET) + pystencilssfg_generate_target_sources(${ARGV} CONFIG_MODULE $CACHE{WALBERLA_CODEGEN_CONFIG_MODULE}) +endfunction() diff --git a/cmake/check_python_env.py b/cmake/check_python_env.py new file mode 100644 index 0000000000000000000000000000000000000000..7d00009b56992b3b16fb1dc07f2854e30e9d49b3 --- /dev/null +++ b/cmake/check_python_env.py @@ -0,0 +1,29 @@ +import sys + + +def error(msg: str): + msg = "Required Python packages could not be found:\n" + msg + print(msg, file=sys.stderr) + sys.exit(1) + + +print("Checking Python environment - ", end="") + +try: + import pystencils +except ImportError: + error("pystencils is not installed in the current Python environment.") + +try: + import pystencilssfg +except ImportError: + error("pystencils-sfg is not installed in the current Python environment.") + +try: + import sfg_walberla +except ImportError: + error("sfg_walberla is not installed in the current Python environment.") + +print("found required packages pystencils, pystencils-sfg, sfg-walberla", end="") + +sys.exit(0) diff --git a/cmake/venv-reqs.txt b/cmake/venv-reqs.txt new file mode 100644 index 0000000000000000000000000000000000000000..97f53bbb590d9fd5e0fede0c74ca92b59014b804 --- /dev/null +++ b/cmake/venv-reqs.txt @@ -0,0 +1,8 @@ +# pystencils 2.0 Development Branch +git+https://i10git.cs.fau.de/pycodegen/pystencils.git@v2.0-dev + +# lbmpy: feature branch for pystencils-2.0 compatibility +git+https://i10git.cs.fau.de/pycodegen/lbmpy.git@fhennig/pystencils2.0-compat + +# pystencils-sfg: (development branch with updated CMake modules and cpptypes) +git+https://i10git.cs.fau.de/pycodegen/pystencils-sfg.git@fhennig/devel diff --git a/noxfile.py b/noxfile.py new file mode 100644 index 0000000000000000000000000000000000000000..0d4bf13187c7c6a37309469d5945c12abf8a4e5e --- /dev/null +++ b/noxfile.py @@ -0,0 +1,12 @@ +import nox + + +@nox.session +def user_manual(session: nox.Session): + session.chdir("user_manual") + session.install("-r", "requirements.txt") + + if "--clean" in session.posargs: + session.run("make", "clean", external=True) + + session.run("make", "html", external=True) diff --git a/src/sfg_walberla/__init__.py b/src/sfg_walberla/__init__.py index 063b350bec25105d9167aa6b0a7476e448c6a574..50097ef8b19500c16e44a5e25622d4fbaf6889d9 100644 --- a/src/sfg_walberla/__init__.py +++ b/src/sfg_walberla/__init__.py @@ -1,6 +1,7 @@ from .api import real_t, Vector3, GhostLayerFieldPtr, glfield, IBlockPtr from .postprocessing import CaptureToClass from .sweep import Sweep +from .build_config import WalberlaBuildConfig __all__ = [ "real_t", @@ -10,6 +11,7 @@ __all__ = [ "IBlockPtr", "CaptureToClass", "Sweep", + "WalberlaBuildConfig", ] from . import _version diff --git a/src/sfg_walberla/api.py b/src/sfg_walberla/api.py index ad2819acb6f725ea2a240f221671bc4deb7d69ef..2818b43f0ebe7bec7ef17859bc6857e7c40d8430 100644 --- a/src/sfg_walberla/api.py +++ b/src/sfg_walberla/api.py @@ -1,5 +1,7 @@ from __future__ import annotations +from typing import Callable + from pystencils import Field from pystencils.types import ( UserTypeSpec, @@ -15,8 +17,9 @@ from pystencilssfg.lang import ( SrcVector, Ref, ExprLike, + cpptype, ) -from pystencilssfg.ir import SfgHeaderInclude +from pystencilssfg.lang.types import CppType real_t = PsCustomType("walberla::real_t") @@ -24,39 +27,53 @@ cell_idx_t = PsCustomType("walberla::cell_idx_t") uint_t = PsCustomType("walberla::uint_t") +class _CppClass(AugExpr): + _type: Callable[..., CppType] + + def __init__(self, const: bool = False, ref: bool = False): + dtype = self._type(const=const, ref=ref) + super().__init__(dtype) + + class Vector2(SrcVector): - def __init__(self, val_type: UserTypeSpec): - self._value_type = create_type(val_type) - val_type_str = self._value_type.c_string() - super().__init__(PsCustomType(f"walberla::Vector2< {val_type_str} >")) + _template = cpptype("walberla::Vector2< {element_type} >", "core/math/Vector2.h") + + def __init__( + self, element_type: UserTypeSpec, const: bool = False, ref: bool = False + ): + self._element_type = create_type(element_type) + dtype = self._template(element_type=element_type, const=const, ref=ref) + super().__init__(dtype) def extract_component(self, coordinate: int) -> AugExpr: if coordinate > 1: raise ValueError(f"Cannot extract component {coordinate} from Vector2") - return AugExpr(self._value_type).bind("{}[{}]", self, coordinate) + return AugExpr(self._element_type).bind("{}[{}]", self, coordinate) class Vector3(SrcVector): - def __init__(self, val_type: UserTypeSpec): - self._value_type = create_type(val_type) - val_type_str = self._value_type.c_string() - super().__init__(PsCustomType(f"walberla::Vector3< {val_type_str} >")) + _template = cpptype("walberla::Vector3< {element_type} >", "core/math/Vector3.h") + + def __init__( + self, element_type: UserTypeSpec, const: bool = False, ref: bool = False + ): + self._element_type = create_type(element_type) + dtype = self._template(element_type=element_type, const=const, ref=ref) + super().__init__(dtype) def extract_component(self, coordinate: int) -> AugExpr: if coordinate > 2: raise ValueError(f"Cannot extract component {coordinate} from Vector3") - return AugExpr(self._value_type).bind("{}[{}]", self, coordinate) + return AugExpr(self._element_type).bind("{}[{}]", self, coordinate) def __getitem__(self, idx: int | ExprLike): - return AugExpr(self._value_type).bind("{}[{}]", self, idx) + return AugExpr(self._element_type).bind("{}[{}]", self, idx) -class AABB(AugExpr): - def __init__(self): - dtype = PsCustomType("walberla::AABB") - super().__init__(dtype) +class AABB(_CppClass): + _type = cpptype("walberla::AABB", "core/math/AABB.h") def min(self) -> Vector3: return Vector3(real_t).bind("{}.min()", self) @@ -65,26 +82,16 @@ class AABB(AugExpr): return Vector3(real_t).bind("{}.max()", self) -class CellInterval(AugExpr): - def __init__(self, const: bool = False, ref: bool = False): - dtype = PsCustomType("walberla::CellInterval", const=const) - if ref: - dtype = Ref(dtype) - super().__init__(dtype) +class CellInterval(_CppClass): + _type = cpptype("walberla::CellInterval", "core/cell/CellInterval.h") -class BlockDataID(AugExpr): - def __init__(self): - super().__init__(PsCustomType("walberla::BlockDataID")) +class BlockDataID(_CppClass): + _type = cpptype("walberla::BlockDataID", "domain_decomposition/BlockDataID.h") - @property - def required_includes(self) -> set[SfgHeaderInclude]: - return {SfgHeaderInclude.parse("domain_decomposition/BlockDataID.h")} - -class IBlockPtr(AugExpr): - def __init__(self): - super().__init__(PsCustomType("walberla::IBlock *")) +class IBlockPtr(_CppClass): + _type = cpptype("walberla::IBlock *", "domain_decomposition/IBlock.h") def getData(self, dtype: str | PsType, id: BlockDataID) -> AugExpr: return AugExpr.format("{}->template getData< {} >({})", self, dtype, id) @@ -92,10 +99,6 @@ class IBlockPtr(AugExpr): def getAABB(self) -> AABB: return AABB().bind("{}->getAABB()", self) - @property - def required_includes(self) -> set[SfgHeaderInclude]: - return {SfgHeaderInclude.parse("domain_decomposition/IBlock.h")} - def deref(self) -> AugExpr: return AugExpr.format("*{}", self) @@ -208,6 +211,11 @@ class GenericWalberlaField(SrcField): class GhostLayerFieldPtr(GenericWalberlaField): + _template = cpptype( + "walberla::field::GhostLayerField< {element_type}, {fsize} >", + "field/GhostLayerField.h", + ) + @staticmethod def create(field: Field): if field.index_dimensions > 1: @@ -226,19 +234,17 @@ class GhostLayerFieldPtr(GenericWalberlaField): fsize: int, ): element_type = create_type(element_type) - elmt_type_str = element_type.c_string() - field_type = PsCustomType( - f"walberla::field::GhostLayerField< {elmt_type_str}, {fsize} >" - ) + field_type = self._template(element_type=element_type, fsize=fsize) super().__init__(element_type, field_type, ptr=True) - @property - def required_includes(self) -> set[SfgHeaderInclude]: - return {SfgHeaderInclude("field/GhostLayerField.h")} - class GpuFieldPtr(GenericWalberlaField): + _template = cpptype( + "walberla::gpu::GpuField< {element_type} >", + "gpu/GpuField.h", + ) + @staticmethod def create(field: Field): if field.index_dimensions > 1: @@ -257,15 +263,10 @@ class GpuFieldPtr(GenericWalberlaField): fsize: int, ): element_type = create_type(element_type) - elmt_type_str = element_type.c_string() - field_type = PsCustomType(f"walberla::gpu::GpuField< {elmt_type_str} >") + field_type = self._template(element_type=element_type) super().__init__(element_type, field_type, ptr=True) - @property - def required_includes(self) -> set[SfgHeaderInclude]: - return {SfgHeaderInclude("gpu/GpuField.h")} - class GhostLayerFieldExtraction(IFieldExtraction): def __init__( diff --git a/src/sfg_walberla/build_config.py b/src/sfg_walberla/build_config.py new file mode 100644 index 0000000000000000000000000000000000000000..c47539d357be23d5c922ebd728e45061282a8eaf --- /dev/null +++ b/src/sfg_walberla/build_config.py @@ -0,0 +1,74 @@ +from __future__ import annotations + +from dataclasses import dataclass + +from pystencils import CreateKernelConfig +from pystencils.types.quick import Fp +from pystencils.jit import no_jit + +from pystencilssfg import SfgContext +from pystencilssfg.composer import SfgIComposer + + +def cmake_parse_bool(var: str): + var = var.upper() + if var in ("ON", "1", "TRUE"): + return True + elif var in ("OFF", "0", "FALSE"): + return False + else: + raise ValueError(f"Could not parse cmake value `{var}` as boolean.") + + +@dataclass +class WalberlaBuildConfig: + """Represents a waLBerla build system configuration""" + + c_compiler_id: str + """Value of `CMAKE_C_COMPILER_ID`.""" + + cxx_compiler_id: str + """Value of `CMAKE_CXX_COMPILER_ID`.""" + + use_double_precision: bool + """Value of `WALBERLA_DOUBLE_ACCURACY`""" + + optimize_for_localhost: bool + """Value of `WALBERLA_OPTIMIZE_FOR_LOCALHOST`.""" + + mpi_enabled: bool + """Value of `WALBERLA_BUILD_WITH_MPI`.""" + + openmp_enabled: bool + """Value of `WALBERLA_BUILD_WITH_OPENMP`.""" + + cuda_enabled: bool + """Value of `WALBERLA_BUILD_WITH_CUDA`.""" + + hip_enabled: bool + """Value of `WALBERLA_BUILD_WITH_HIP`.""" + + likwid_enabled: bool + """Value of `WALBERLA_BUILD_WITH_LIKWID_MARKERS`""" + + @staticmethod + def from_sfg(sfg: SfgContext | SfgIComposer) -> WalberlaBuildConfig: + if isinstance(sfg, SfgIComposer): + ctx = sfg.context + else: + ctx = sfg + + if isinstance(ctx.project_info, WalberlaBuildConfig): + return ctx.project_info + else: + raise ValueError( + "The given SfgContext does not encapsulate a waLBerla build config object." + ) + + def get_pystencils_config(self) -> CreateKernelConfig: + dtype = Fp(64) if self.use_double_precision else Fp(32) + + return CreateKernelConfig( + default_dtype=dtype, + jit=no_jit, + ) diff --git a/user_manual/.gitignore b/user_manual/.gitignore new file mode 100644 index 0000000000000000000000000000000000000000..4741371e5883899742b08cf256ca454fefa6267a --- /dev/null +++ b/user_manual/.gitignore @@ -0,0 +1,3 @@ +_sphinx_build +downloads +zipped-examples diff --git a/user_manual/CMakeLists.txt b/user_manual/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..59c36c295de0103a5eb5657faee2ff1dfadc1fb4 --- /dev/null +++ b/user_manual/CMakeLists.txt @@ -0,0 +1,17 @@ +cmake_minimum_required( VERSION 3.24 ) +project( walberla-codegen-examples ) + +include(FetchContent) + +FetchContent_Declare( + walberla + GIT_REPOSITORY https://i10git.cs.fau.de/walberla/walberla.git +) + +message( STATUS "Fetching waLBerla sources (this might take a while)..." ) +FetchContent_MakeAvailable(walberla) + +add_subdirectory(${CMAKE_SOURCE_DIR}/.. ${CMAKE_BINARY_DIR}/sfg-walberla) + +add_subdirectory( GeneratorScriptBasics ) +add_subdirectory( ForceDrivenChannel ) diff --git a/user_manual/CMakeSetup/CMakeLists.txt b/user_manual/CMakeSetup/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..36d2851483f0469106030983921772cb5a658dc9 --- /dev/null +++ b/user_manual/CMakeSetup/CMakeLists.txt @@ -0,0 +1,16 @@ +cmake_minimum_required( VERSION 3.24 ) +project( <your-project-name> ) + +include(FetchContent) + +FetchContent_Declare( + walberla + GIT_REPOSITORY https://i10git.cs.fau.de/walberla/walberla.git +) + +FetchContent_Declare( + sfg-walberla + GIT_REPOSITORY https://i10git.cs.fau.de/da15siwa/sfg-walberla.git +) + +FetchContent_MakeAvailable(walberla sfg-walberla) diff --git a/user_manual/CMakeSetup/CMakeSetup.md b/user_manual/CMakeSetup/CMakeSetup.md new file mode 100644 index 0000000000000000000000000000000000000000..003caad5711019cd0dc3a55a6f45a5667c750a2c --- /dev/null +++ b/user_manual/CMakeSetup/CMakeSetup.md @@ -0,0 +1,73 @@ +(EnvSetup)= +# CMake Project Setup + +This chapter describes the necessary steps to set up a CMake project to use waLBerla +with the nextcodegen features provided by sfg-walberla. + +## Prequesites + +You are going to need at least +- a C++ compiler supporting at least C++17; +- a Python installation of version >= 3.10; +- a CMake installation of version >= 3.24; +- an up-to-date installation of Git. + +Also, the following optional dependencies are highly recommended: +- clang-format for prettier code generator output + +If you are starting from scratch, create a new, empty directory for your CMake project. +Then, set up its `CMakeLists.txt` with the following minimal settings: + +:::{literalinclude} CMakeLists.txt +:lines: 1-2 +::: + +On the other hand. if you already have a CMake project set up, you can just as well extend it. + +## Add sfg-walberla to your CMake Project + +There are many ways to include `waLBerla` and `sfg-walberla` into your project: +you can clone them locally and use `add_subdirectory`, add them as submodules, et cetera. +For getting started, though, the easiest way is to use [FetchContent][FetchContent] to dynamically +pull their sources into your build tree. + +Place the following code in your `CMakeLists.txt`: + +::::{card} {download}`CMakeLists.txt` +:::{literalinclude} CMakeLists.txt +:lines: 4- +::: +:::: + +Now, you can run `cmake` to configure your build system: + +```bash +mkdir build +cmake -S . -B build -DPython_EXECUTABLE=`pwd`/.venv/bin/python +``` + +If, near the end of the long configuration log, you see two messages like this: + +``` +-- Found pystencils Source File Generator (Version 0.1a4+25.g9d3e553) +-- Using Python interpreter <your-project-dir>/build/_deps/sfg-walberla-build/codegen-venv/bin/python for SFG generator scripts. +``` + +Then your build system setup was successful! +Now you can get down to business and populate your project with simulation applications. + +(adding_examples)= +## Adding Examples from this Manual + +As you explore the examples in this book, +you can try out each by downloading, extracting, and including them into your project. +For example, to include the [force-driven poiseulle channel example](#ForceDrivenChannel), +extract its code into the `ForceDrivenChannel` subdirectory and add + +```CMake +add_subdirectory( ForceDrivenChannel ) +``` + +to your root `CMakeLists.txt`. + +[FetchContent]: https://cmake.org/cmake/help/latest/module/FetchContent.html diff --git a/user_manual/ForceDrivenChannel/CMakeLists.txt b/user_manual/ForceDrivenChannel/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..4fbb89077b6d17f564cad91113e1f74cc0cd81b6 --- /dev/null +++ b/user_manual/ForceDrivenChannel/CMakeLists.txt @@ -0,0 +1,14 @@ +waLBerla_link_files_to_builddir( Channel.prm ) + +add_executable( Ex_ForceDrivenChannel ) +target_sources( Ex_ForceDrivenChannel PRIVATE ForceDrivenChannel.cpp ) + +pystencilssfg_generate_target_sources( Ex_ForceDrivenChannel + SCRIPTS LbmAlgorithms.py +) + +target_link_libraries( + Ex_ForceDrivenChannel + PRIVATE + core stencil blockforest geometry vtk sfg_walberla +) diff --git a/user_manual/ForceDrivenChannel/Channel.prm b/user_manual/ForceDrivenChannel/Channel.prm new file mode 100644 index 0000000000000000000000000000000000000000..36aa90daf3229784037bd6b99d89412df5a7f9f8 --- /dev/null +++ b/user_manual/ForceDrivenChannel/Channel.prm @@ -0,0 +1,23 @@ +DomainSetup +{ + blocks < 1, 1, 1 >; + cellsPerBlock < 4, 4, 32 >; + periodic < 1, 1, 0 >; +} + +Parameters +{ + omega 1.0; + force < 6.25e-5, 0, 0 >; + timesteps 5000; +} + +Boundaries { + Border { direction T; walldistance -1; flag NoSlip; } + Border { direction B; walldistance -1; flag NoSlip; } +} + +Output +{ + vtkWriteFrequency 100; +} \ No newline at end of file diff --git a/user_manual/ForceDrivenChannel/ForceDrivenChannel.cpp b/user_manual/ForceDrivenChannel/ForceDrivenChannel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..52238a70a2ebcb079d0f61257174e7aef82102bf --- /dev/null +++ b/user_manual/ForceDrivenChannel/ForceDrivenChannel.cpp @@ -0,0 +1,145 @@ +#include "blockforest/all.h" +#include "blockforest/communication/UniformBufferedScheme.h" + +#include "core/all.h" + +#include "domain_decomposition/SharedSweep.h" + +#include "field/all.h" +#include "field/communication/PackInfo.h" +#include "field/communication/StencilRestrictedPackInfo.h" + +#include "geometry/InitBoundaryHandling.h" + +#include "stencil/all.h" + +#include "timeloop/all.h" + +#include "vtk/all.h" + +#include "gen/Ex_ForceDrivenChannel/LbmAlgorithms.hpp" + +namespace Ex_ForceDrivenChannel +{ + using namespace walberla; + using namespace blockforest; + + using std::make_unique; + using std::shared_ptr; + + using ScalarField_T = field::GhostLayerField<real_t, 1>; + using VectorField_T = field::GhostLayerField<real_t, 3>; + + using LbStencil = stencil::D3Q19; + using PdfField_T = field::GhostLayerField<real_t, LbStencil::Q>; + + using CommScheme = blockforest::communication::UniformBufferedScheme<LbStencil>; + using PdfsPackInfo = field::communication::StencilRestrictedPackInfo<PdfField_T, LbStencil>; + + using FlagField_T = FlagField<uint8_t>; + + void run(const shared_ptr<Config> &config) + { + auto blocks = createUniformBlockGridFromConfig(config); + + Config::BlockHandle simParams = config->getBlock("Parameters"); + const real_t omega{simParams.getParameter<real_t>("omega")}; + + const Vector3<real_t> force = simParams.getParameter<Vector3<real_t>>("force"); + + BlockDataID pdfsId = field::addToStorage<PdfField_T>(blocks, "pdfs", real_c(0.0), field::fzyx, 1); + BlockDataID rhoId = field::addToStorage<ScalarField_T>(blocks, "rho", real_c(1.0), field::fzyx, 0); + BlockDataID uId = field::addToStorage<VectorField_T>(blocks, "u", real_c(0.0), field::fzyx, 0); + + gen::LbInit lbInit{pdfsId, rhoId, uId, force}; + + for (auto &block : *blocks) + { + lbInit(&block); + } + + // Stream-Collide + + auto streamCollide = make_shared<gen::LbStreamCollide>(pdfsId, rhoId, uId, force, omega); + + // Communication Setup + + CommScheme comm{blocks}; + auto pdfsPackInfo = std::make_shared<PdfsPackInfo>(pdfsId); + comm.addPackInfo(pdfsPackInfo); + + // Boundary Conditions + + const BlockDataID flagFieldId = field::addFlagFieldToStorage<FlagField_T>(blocks, "flagField"); + const FlagUID fluidFlagUid{"Fluid"}; + const FlagUID noSlipFlagUid{"NoSlip"}; + + auto boundariesConfig = config->getBlock("Boundaries"); + if (boundariesConfig) + { + WALBERLA_LOG_INFO_ON_ROOT("Setting boundary conditions") + geometry::initBoundaryHandling<FlagField_T>(*blocks, flagFieldId, boundariesConfig); + } + + geometry::setNonBoundaryCellsToDomain<FlagField_T>(*blocks, flagFieldId, fluidFlagUid); + + auto flagFieldOutput = field::createVTKOutput<FlagField_T>(flagFieldId, *blocks, "flagField", 1, 0); + flagFieldOutput(); + + auto noSlip = make_unique<gen::NoSlip>(blocks, pdfsId); + noSlip->fillFromFlagField<FlagField_T>(*blocks, flagFieldId, noSlipFlagUid, fluidFlagUid); + + auto boundarySweep = [&](IBlock *block) + { + (*noSlip)(block); + }; + + // Timeloop + const uint_t numTimesteps{simParams.getParameter<uint_t>("timesteps")}; + SweepTimeloop loop{blocks->getBlockStorage(), numTimesteps}; + + loop.add() << Sweep(makeSharedSweep(streamCollide)); + loop.add() << Sweep(boundarySweep) << AfterFunction(comm); + + RemainingTimeLogger logger{numTimesteps}; + loop.addFuncAfterTimeStep(logger); + + // VTK Output + + Config::BlockHandle outputParams = config->getBlock("Output"); + + const uint_t vtkWriteFrequency = outputParams.getParameter<uint_t>("vtkWriteFrequency", 0); + if (vtkWriteFrequency > 0) + { + auto vtkOutput = vtk::createVTKOutput_BlockData(*blocks, "vtk", vtkWriteFrequency, 0, false, "vtk_out", + "simulation_step", false, true, true, false, 0); + + auto densityWriter = make_shared<field::VTKWriter<ScalarField_T, float32>>(rhoId, "density"); + vtkOutput->addCellDataWriter(densityWriter); + + auto velWriter = make_shared<field::VTKWriter<VectorField_T, float32>>(uId, "velocity"); + vtkOutput->addCellDataWriter(velWriter); + + const cell_idx_t xCells = cell_idx_c(blocks->getNumberOfXCells()); + const cell_idx_t yCells = cell_idx_c(blocks->getNumberOfYCells()); + const cell_idx_t zCells = cell_idx_c(blocks->getNumberOfZCells()); + + loop.addFuncAfterTimeStep(vtk::writeFiles(vtkOutput), "VTK Output"); + } + + // Run the Simulation + + WALBERLA_LOG_INFO_ON_ROOT("Commencing simulation with " << numTimesteps << " timesteps") + + loop.run(); + } +} + +int main(int argc, char **argv) +{ + walberla::Environment env{argc, argv}; + + Ex_ForceDrivenChannel::run(env.config()); + + return EXIT_SUCCESS; +} diff --git a/user_manual/ForceDrivenChannel/ForceDrivenChannel.md b/user_manual/ForceDrivenChannel/ForceDrivenChannel.md new file mode 100644 index 0000000000000000000000000000000000000000..1cc8d1481dd7767d6e111bceebb60646890b77e0 --- /dev/null +++ b/user_manual/ForceDrivenChannel/ForceDrivenChannel.md @@ -0,0 +1,34 @@ +(ForceDrivenChannel)= +# Force-Driven Poiseuille Channel + +This example aims to illustrate the basic code generation features of `sfg-walberla` +by building a force-driven channel flow application. + +## Files + +{download}`ForceDrivenChannel.zip </zipped-examples/ForceDrivenChannel.zip>`. + +This example comprises the following files: + + - `ForceDrivenChannel.cpp`: The main simulation application; + - `LbmAlgorithms.py`: The code generation script producing the lattice Boltzmann algorithms; + - `CMakeLists.txt`: The CMake build system configuration; + - `Channel.prm`: the parameter file describing the channel. + + +## CMake Target Definition + +:::{card} `CMakeLists.txt` +::::{literalinclude} CMakeLists.txt +:language: CMake +:::: +::: + +The CMake target setup for this example is quite straight-forward. +We create a new executable called `Ex_PoiseuilleChannel` +and add to it the single C++ source file `PoiseuilleChannel.cpp`. +Then, we register our code generator script `LbmAlgorithms.py` via the +[`pystencilssfg_generate_target_sources`][sfg_add_gen_scripts] CMake function. + + +[sfg_add_gen_scripts]: https://pycodegen.pages.i10git.cs.fau.de/pystencils-sfg/usage/project_integration.html#add-generator-scripts "pystencils-sfg Documentation" \ No newline at end of file diff --git a/user_manual/ForceDrivenChannel/LbmAlgorithms.py b/user_manual/ForceDrivenChannel/LbmAlgorithms.py new file mode 100644 index 0000000000000000000000000000000000000000..fe9b7c1c314090e88db71ee82afe01311e6983c4 --- /dev/null +++ b/user_manual/ForceDrivenChannel/LbmAlgorithms.py @@ -0,0 +1,66 @@ +import sympy as sp + +from pystencilssfg import SourceFileGenerator +from sfg_walberla import Sweep +from sfg_walberla.boundaries import SimpleHbbBoundary + +from pystencils import Target, fields +import pystencils.codegen.config as cfg +from lbmpy import ( + LBStencil, + Stencil, + Method, + LBMConfig, + LBMOptimisation, + create_lb_method, + create_lb_update_rule, +) + +from lbmpy.boundaries import NoSlip +from lbmpy.macroscopic_value_kernels import macroscopic_values_setter + +stencil = LBStencil(Stencil.D3Q19) +dim = stencil.D +f, f_tmp, rho, u = fields( + f"f({stencil.Q}), f_tmp({stencil.Q}), rho(1), u({dim}): [{dim}D]", layout="fzyx" +) +omega = sp.Symbol("omega") +force = sp.symbols(f"F_:{dim}") + +lbm_config = LBMConfig( + stencil=stencil, + method=Method.CENTRAL_MOMENT, + relaxation_rate=omega, + force=force, + compressible=True, + zero_centered=False, + output={"density": rho, "velocity": u}, +) + +lb_method = create_lb_method(lbm_config) + +with SourceFileGenerator() as sfg: + sfg.namespace("Ex_ForceDrivenChannel::gen") + + sfg.include(f"stencil/{stencil.name}.h") + sfg.code(f"using LbStencil = walberla::stencil::{stencil.name};") + + lbm_opt = LBMOptimisation(symbolic_field=f, symbolic_temporary_field=f_tmp) + + gen_config = cfg.CreateKernelConfig( + target=Target.CPU, cpu_optim=cfg.CpuOptimConfig(openmp=True) + ) + + lb_update = create_lb_update_rule(lbm_config=lbm_config, lbm_optimisation=lbm_opt) + lb_update_sweep = Sweep("LbStreamCollide", lb_update, gen_config) + lb_update_sweep.swap_fields(f, f_tmp) + sfg.generate(lb_update_sweep) + + lb_init = macroscopic_values_setter( + lb_update.method, density=rho, velocity=u, pdfs=f, set_pre_collision_pdfs=True + ) + lb_init_sweep = Sweep("LbInit", lb_init, gen_config) + sfg.generate(lb_init_sweep) + + # No-Slip Wall + sfg.generate(SimpleHbbBoundary(NoSlip(), lb_method, f)) diff --git a/user_manual/FullyPeriodicAde/AdvectionDiffusionSweep.py b/user_manual/FullyPeriodicAde/AdvectionDiffusionSweep.py new file mode 100644 index 0000000000000000000000000000000000000000..876da3b324faa49e13a18a589161ffe08dfb204a --- /dev/null +++ b/user_manual/FullyPeriodicAde/AdvectionDiffusionSweep.py @@ -0,0 +1,8 @@ +from pystencilssfg import SourceFileGenerator + +from sfg_walberla import Sweep +from sfg_walberla.symbolic import cell + + +with SourceFileGenerator() as sfg: + pass diff --git a/user_manual/FullyPeriodicAde/CMakeLists.txt b/user_manual/FullyPeriodicAde/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/user_manual/GeneratorScriptBasics/BasicCodegen.py b/user_manual/GeneratorScriptBasics/BasicCodegen.py new file mode 100644 index 0000000000000000000000000000000000000000..86ff253100201ffa23ea4aa9b115dc758201d1d5 --- /dev/null +++ b/user_manual/GeneratorScriptBasics/BasicCodegen.py @@ -0,0 +1,6 @@ +from pystencilssfg import SourceFileGenerator + +with SourceFileGenerator() as sfg: + sfg.include("<cstdint>") + sfg.namespace("gen") + sfg.code("constexpr uint64_t MAGIC_NUMBER = 0xcafe;") diff --git a/user_manual/GeneratorScriptBasics/BasicCodegenApp.cpp b/user_manual/GeneratorScriptBasics/BasicCodegenApp.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0cb852b61fda67ad65a4ba18612acf40f23f7882 --- /dev/null +++ b/user_manual/GeneratorScriptBasics/BasicCodegenApp.cpp @@ -0,0 +1,6 @@ +#include "gen/Ex_GeneratorScriptBasics/BasicCodegen.hpp" +#include <iostream> + +int main(void) { + std::cout << gen::MAGIC_NUMBER << std::endl; +} diff --git a/user_manual/GeneratorScriptBasics/CMakeLists.txt b/user_manual/GeneratorScriptBasics/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..5804fe28101f15e23ead12756d362a476ff823c5 --- /dev/null +++ b/user_manual/GeneratorScriptBasics/CMakeLists.txt @@ -0,0 +1,6 @@ +add_executable( Ex_GeneratorScriptBasics ) +target_sources( Ex_GeneratorScriptBasics PRIVATE BasicCodegenApp.cpp ) + +walberla_generate_sources( Ex_GeneratorScriptBasics + SCRIPTS BasicCodegen.py +) \ No newline at end of file diff --git a/user_manual/GeneratorScriptBasics/GeneratorScriptBasics.md b/user_manual/GeneratorScriptBasics/GeneratorScriptBasics.md new file mode 100644 index 0000000000000000000000000000000000000000..f0577069ae5cf79de01ddeafaf49ca2dd9439178 --- /dev/null +++ b/user_manual/GeneratorScriptBasics/GeneratorScriptBasics.md @@ -0,0 +1,96 @@ +# Getting Started with Generator Scripts + +This chapter aims to give an introduction on working with *generator scripts*, +which are the driving force of the next-codegen system in waLBerla. + +In the course of this guide, we will + - set up the basic structure of a generator script; + - register that script with CMake; + - and include the generated files into a simple *Hello World*-like application. + +This guide assumes that you already have a CMake project and Python environment correctly +set up, as described in [](EnvSetup). + +:::{dropdown} Get The Files + +{download}`GeneratorScriptBasics.zip </zipped-examples/GeneratorScriptBasics.zip>`. + +This example comprises the following files: + + - `GeneratorScriptBasics.cpp`: The demo simulation; + - `BasicCodegen.py`: A sample code generation script; + - `CMakeLists.txt`: The CMake build system configuration. + +::: + +## Our First Generator Script + +Create and open the file `BasicCodegen.py` and populate it with the following code: + +```{literalinclude} BasicCodegen.py +:caption: BasicCodegen.py +:lineno-start: 1 +``` + +Let's take this apart. +- In line 1, we import the `SourceFileGenerator`, which is the object responsible for running code generation + and interacting with the build system. + It is exposed by [pystencils-sfg][pystencils-sfg], a lower-level package that `sfg-walberla` relies on. +- In line 3, we enter code generation mode by opening up a managed block controlled by the `SourceFileGenerator`. + It gives us the `sfg` object, which is our primary interaction point with the code generation engine. +- In lines 4 to 6, we use the `sfg` object to do three things: + - Include a header file; this will cause the respective `#include` directive to be generated. + - Set the namespace; this will place all generated code into the `gen` namespace. + - Define a constant; this will print the definition verbatim into the output header file. + +To see the generator script in effect, we need to create a CMake target and a simple application we can include it into. +Create both the `BasicCodegenApp.cpp` and `CMakeLists.txt` files, open up `CMakeLists.txt`, and add the following: + +:::{literalinclude} CMakeLists.txt +:language: CMake +:caption: CMakeLists.txt +:lineno-start: 1 +:emphasize-lines: 4-6 +::: + +At first, we create a new executable target called `Ex_GeneratorScriptBasics` +and add the `BasicCodegen.cpp` source file to it. +To us code generators, however, the second part is more interesting: +we register our generator script `BasicCodegen.py` at the target using +`walberla_generate_sources`. +This will cause the build system to execute the generator script during build, +compile its generated sources, +and make its generated headers available to us. + +To observe this, add the following to your `BasicCodegenApp.cpp`: + +```{literalinclude} BasicCodegenApp.cpp +:caption: BasicCodegenApp.cpp +:lineno-start: 1 +``` + +Don't worry if your IDE tells you `BasicCodegen.hpp` does not exist yet; +the file will be generated in a moment. +Use CMake to build the application `Ex_GeneratorScriptBasics` and run it, +and you should see it print `51966` to stdout. + +Let's take a look at what is happening during the build. +Your CMake build output should contain a line somewhat like this: + +``` +Generating sfg_sources/gen/Ex_GeneratorScriptBasics/BasicCodegen.hpp, sfg_sources/gen/Ex_GeneratorScriptBasics/BasicCodegen.cpp +``` + +This indicates that the generator script was executed and produced exactly two files below the `sfg_sources` directory: +- a header file `gen/Ex_GeneratorScriptBasics/BasicCodegen.hpp`, which is the one we included into our application, and +- a source file `gen/Ex_GeneratorScriptBasics/BasicCodegen.cpp`, which at this time is still empty + because we haven't defined any kernels or functions yet. + +The `walberla_generate_sources` CMake command made sure that the `sfg_sources` directory was placed in your +target's include path, so you can include the generated files. + +That covers the basics of setting up and running a code generator script; +you're all set now to generate and run your first numerical kernels. + +[pystencils-sfg]: https://pycodegen.pages.i10git.cs.fau.de/pystencils-sfg/index.html +[sfg_add_gen_scripts]: https://pycodegen.pages.i10git.cs.fau.de/pystencils-sfg/usage/project_integration.html#add-generator-scripts "pystencils-sfg Documentation" diff --git a/user_manual/Makefile b/user_manual/Makefile new file mode 100644 index 0000000000000000000000000000000000000000..d931dfac6dcefe86452b20b310c390c0ea648d86 --- /dev/null +++ b/user_manual/Makefile @@ -0,0 +1,39 @@ +# Minimal makefile for Sphinx documentation +# + +# You can set these variables from the command line, and also +# from the environment for the first two. +SPHINXOPTS ?= +SPHINXBUILD ?= sphinx-build +SOURCEDIR = . +BUILDDIR = _sphinx_build + +ZIPPED_EXAMPLES := zipped-examples + +include examples.mk + +MKDIR := mkdir -p +dir_guard = $(MKDIR) $(@D) + +# Put it first so that "make" without argument is like "make help". +help: + @$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +.PHONY: help html clean Makefile ZipExamples + +# Catch-all target: route all unknown targets to Sphinx using the new +# "make mode" option. $(O) is meant as a shortcut for $(SPHINXOPTS). +html: Makefile ZipExamples + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +clean: + @echo "Removing generated downloadable files" + @rm -rf $(ZIPPED_EXAMPLES) + @$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) + +ZipExamples: $(foreach example, $(EXAMPLES), $(ZIPPED_EXAMPLES)/$(example).zip) + +$(ZIPPED_EXAMPLES)/%.zip: %/* + @$(dir_guard) + @echo Zipping $(<D) + @zip -r $@ $(<D) diff --git a/user_manual/conf.py b/user_manual/conf.py new file mode 100644 index 0000000000000000000000000000000000000000..cfa54f38b4a54d40be07e51cb183f088bdf36c32 --- /dev/null +++ b/user_manual/conf.py @@ -0,0 +1,38 @@ +# Configuration file for the Sphinx documentation builder. +# +# For the full list of built-in configuration values, see the documentation: +# https://www.sphinx-doc.org/en/master/usage/configuration.html + +# -- Project information ----------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#project-information + +project = 'waLBerla next-codegen' +copyright = '2024, Frederik Hennig' +author = 'Frederik Hennig' + + +# -- General configuration --------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#general-configuration + +extensions = [ + "myst_parser", + "sphinx_design", + "sphinx_copybutton", +] + +# templates_path = ['_templates'] +exclude_patterns = ["build"] + +myst_enable_extensions = [ + "colon_fence", + "dollarmath", + "attrs_inline", + "attrs_block", +] + +# -- Options for HTML output ------------------------------------------------- +# https://www.sphinx-doc.org/en/master/usage/configuration.html#options-for-html-output + +html_theme = 'sphinx_book_theme' +html_static_path = ['zipped-examples'] +html_title = "waLBerla next-codegen" diff --git a/user_manual/examples.mk b/user_manual/examples.mk new file mode 100644 index 0000000000000000000000000000000000000000..2d78f20423a18f80a6bc70282b541fdaee4ac96d --- /dev/null +++ b/user_manual/examples.mk @@ -0,0 +1 @@ +EXAMPLES = GeneratorScriptBasics ForceDrivenChannel diff --git a/user_manual/index.md b/user_manual/index.md new file mode 100644 index 0000000000000000000000000000000000000000..473b4f1a05c50df6418adf4cad216475acf8e0dd --- /dev/null +++ b/user_manual/index.md @@ -0,0 +1,45 @@ +# The Next Generation of waLBerla Code Generation + +Welcome to *The Next Generation of waLBerla Code Generation*. +This book is aimed at teaching you how to use the next generation of code generators for waLBerla +by walking through a set of example applications, each designed to highlight specific features. + +The next-gen code generators for waLBerla are based on bleeding-edge developments in +[pystencils 2.0][pystencils_2_0] and [pystencils-sfg][pystencils-sfg], +and are currently located in the separate [sfg-walberla][sfg-walberla] repository. +This project is still unstable and immature, but growing steadily. + +Until the next-gen code generators are stabilized and merged with the waLBerla master, +setting up a build system and development environment for working with them is slightly more +complicated. Since you will need such an environment to follow along with the examples +in this book, start by reading the chapter [](EnvSetup). +Afterward, you are free to explore the remainder of the book. + +## Table of Contents + +:::{toctree} +:caption: Fundamentals +:maxdepth: 1 + +CMakeSetup/CMakeSetup +GeneratorScriptBasics/GeneratorScriptBasics +::: + +:::{toctree} +:caption: Basic LBM Simulations +:maxdepth: 1 + +ForceDrivenChannel/ForceDrivenChannel +::: + +:::{toctree} +:caption: Reference +:maxdepth: 1 + +Python Environment <reference/PythonEnvironment> +::: + + +[pystencils_2_0]: https://da15siwa.pages.i10git.cs.fau.de/dev-docs/pystencils-nbackend/ "pystencils 2.0 Documentation" +[pystencils-sfg]: https://pycodegen.pages.i10git.cs.fau.de/pystencils-sfg/index.html "pystencils-sfg Documentation" +[sfg-walberla]: https://i10git.cs.fau.de/da15siwa/sfg-walberla "SFG-waLBerla Repository" diff --git a/user_manual/reference/PythonEnvironment.md b/user_manual/reference/PythonEnvironment.md new file mode 100644 index 0000000000000000000000000000000000000000..614cf1fb3610fbb5e1d7e0ac58631f29e4a0a494 --- /dev/null +++ b/user_manual/reference/PythonEnvironment.md @@ -0,0 +1,38 @@ +# Managing the Code Generator's Python Environment + +On this page, you can find information on managing, customizing, and extending the Python environment +used by the waLBerla code generation system. + +## Using the Default Virtual Environment + +By default, `sfg-walberla` creates a new Python virtual environment within the CMake build tree, +and there installs all packages required for code generation. + +### Install Additional Packages + +For projects that require external dependencies, *sfg-walberla* exposes the CMake function +`walberla_codegen_venv_install`, which can be used to install additional packages into the +code generator virtual environment; +for instance, the following invocation installs `pyyaml`: + +```CMake +walberla_codegen_venv_install( pyyaml ) +``` + +The arguments passed to `walberla_codegen_venv_install` are forwarded directly to `pip install`. +You can therefore use any parameters that `pip` can interpret, for instance `-e` to perform an +editable install, or `-r <requirements-file>` to install packages from a requirements file. + +## Using an External Virtual Environment + +To have even more control over your Python environment, you can configure sfg-walberla to +forego creating a private virtual environment, and instead use the Python interpreter +supplied from the outside. + +To explicitly specify a Python interpreter, you need to set the `WALBERLA_CODEGEN_USE_PRIVATE_VENV` cache +variable to `FALSE`, and set `Python_EXECUTABLE` to point at your Python binary. +For instance, at configuration time: + +```bash +cmake -S . -B build -DWALBERLA_CODEGEN_USE_PRIVATE_VENV=FALSE -DPython_EXECUTABLE=<path-to-python> +``` diff --git a/user_manual/requirements.txt b/user_manual/requirements.txt new file mode 100644 index 0000000000000000000000000000000000000000..c353de95b9606e7e9e6fc2a65de09b763257fed3 --- /dev/null +++ b/user_manual/requirements.txt @@ -0,0 +1,5 @@ +sphinx +sphinx-design +myst-parser +sphinx-copybutton +sphinx-book-theme