diff --git a/CMakeLists.txt b/CMakeLists.txt
index 8a733abdfff2a70241370c9f6cf8e190ab4ef589..15cdc03ad250bf7d9b197a8801a867f204ae4113 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,27 +1,11 @@
 cmake_minimum_required( VERSION 3.24 )
 project ( sfg-walberla )
 
-list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake )
-
-find_package( PystencilsSfg REQUIRED )
-
-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 )
+set(sfg_walberla_SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR})
 
-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}" )
+list (APPEND CMAKE_MODULE_PATH ${CMAKE_CURRENT_SOURCE_DIR}/cmake )
 
-include( CodegenFunctions )
+include( PrepareSFG )
 
 add_library( sfg_walberla INTERFACE )
 
diff --git a/cmake/CodegenFunctions.cmake b/cmake/CodegenFunctions.cmake
deleted file mode 100644
index 318dad835d95d23a005201f887bfd7a7c78e1db6..0000000000000000000000000000000000000000
--- a/cmake/CodegenFunctions.cmake
+++ /dev/null
@@ -1,21 +0,0 @@
-#[[
-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/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..e1665a44e37291fa53e98b2161d86563a3912352
--- /dev/null
+++ b/cmake/PrepareSFG.cmake
@@ -0,0 +1,88 @@
+#   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 ${sfg_walberla_SOURCE_DIR}
+            OUTPUT_QUIET
+        )
+
+        set(
+            _sfg_private_venv_done TRUE CACHE BOOL ""
+        )
+        mark_as_advanced(_sfg_private_venv_done)
+    endif()
+
+    set(PystencilsSfg_PYTHON_INTERPRETER ${_venv_python_exe})
+endif()
+
+#   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}" )
+
+#   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/venv-reqs.txt b/cmake/venv-reqs.txt
new file mode 100644
index 0000000000000000000000000000000000000000..f82e5aae7f4ca722991030c572bb2391d70096bc
--- /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)
+git+https://i10git.cs.fau.de/pycodegen/pystencils-sfg.git@fhennig/cmake-custom-python
diff --git a/src/sfg_walberla/build_config.py b/src/sfg_walberla/build_config.py
index 55fe4117da493680f323945039029ac07ecb4be7..c47539d357be23d5c922ebd728e45061282a8eaf 100644
--- a/src/sfg_walberla/build_config.py
+++ b/src/sfg_walberla/build_config.py
@@ -4,7 +4,7 @@ from dataclasses import dataclass
 
 from pystencils import CreateKernelConfig
 from pystencils.types.quick import Fp
-from pystencils.backend.jit import no_jit
+from pystencils.jit import no_jit
 
 from pystencilssfg import SfgContext
 from pystencilssfg.composer import SfgIComposer
diff --git a/src/sfg_walberla/sweep.py b/src/sfg_walberla/sweep.py
index 7bc1b75912383dd764b16c5cd9b460ba71c65ce9..4657a8a72665c0eeb3f4e71f75b88fcaabffd0dd 100644
--- a/src/sfg_walberla/sweep.py
+++ b/src/sfg_walberla/sweep.py
@@ -18,7 +18,7 @@ from pystencils import (
     Target,
 )
 from pystencils.types import PsType, constify, deconstify, PsCustomType
-from pystencils.backend.kernelfunction import GpuKernelFunction
+from pystencils.codegen import GpuKernel
 
 from pystencilssfg import SfgComposer
 from pystencilssfg.lang import (
@@ -333,7 +333,7 @@ class CudaInvocation:
         self._khandle = khandle
 
         gpu_func = khandle.get_kernel_function()
-        assert isinstance(gpu_func, GpuKernelFunction)
+        assert isinstance(gpu_func, GpuKernel)
         self._gpu_func = gpu_func
 
         self._block_size = Vector3(uint_t).var("gpuBlockSize")