diff --git a/cmake/waLBerlaFunctions.cmake b/cmake/waLBerlaFunctions.cmake
index 320afbb1de3518c1ccb7a25f5cdb7be3a25dd71e..f0f5296614c5c06849ef6f1ac74d9743f53d4f44 100644
--- a/cmake/waLBerlaFunctions.cmake
+++ b/cmake/waLBerlaFunctions.cmake
@@ -211,6 +211,7 @@ function ( waLBerla_add_executable )
     set( generatedSourceFiles )
     set( generatorSourceFiles )
     handle_python_codegen(sourceFiles generatedSourceFiles generatorSourceFiles codeGenRequired ${sourceFiles})
+
     if( NOT WALBERLA_BUILD_WITH_CODEGEN AND codeGenRequired)
         message(STATUS "Skipping ${ARG_NAME} since pystencils code generation is not enabled")
         return()
diff --git a/cmake/waLBerlaHelperFunctions.cmake b/cmake/waLBerlaHelperFunctions.cmake
index f3fdc0fcd8ea10828ebb5d58f98ed7a52d2fd1b9..face2f95f2fdb187cefc274fe314bfc20875b5d0 100644
--- a/cmake/waLBerlaHelperFunctions.cmake
+++ b/cmake/waLBerlaHelperFunctions.cmake
@@ -19,14 +19,17 @@ endfunction ( add_flag )
 
 #######################################################################################################################
 #
-# Function to handle source files of type .gen.py and .gen.cuda.py
+# Function to handle python code generation files
 #
-# files .gen.py generate a .h and a .cpp file
-# files .gen.cuda.py generate a .h and a .cu file
-# Takes a list of source files that contain .py files, and returns a list of source files
-# where the generated version are added and the .py files are removed.
-# Additionally creates a custom build rule for the code generation
+# parameters:
+#  sourceFilesOut:  variable where source files without python files are written to
+#  generatedSourceFilesOut: variable where generated source files (with custom command) are written to
+#  generatorsOut: only the python files that have been passed
+#  codeGenRequired: true if at least one python file was part of the sources
 #
+# The list of generated files is determined via the pystencils_walberla package mechanism.
+# The python script, when called with -l, should return a semicolon-separated list of generated files
+# if this list changes, CMake has to be run manually again.
 #######################################################################################################################
 function( handle_python_codegen sourceFilesOut generatedSourceFilesOut generatorsOut codeGenRequiredOut )
     set(result )
@@ -34,25 +37,27 @@ function( handle_python_codegen sourceFilesOut generatedSourceFilesOut generator
     set(generatorsResult )
     set(codeGenRequired NO)
     foreach( sourceFile ${ARGN} )
-        if( ${sourceFile} MATCHES ".*\\.gen\\.py$" )
-            get_filename_component(sourceFileName ${sourceFile} NAME)
-            if( ${sourceFileName} MATCHES ".*\\.cuda\\.gen\\.py$" )
-                string(REPLACE ".cuda.gen.py" ".h"  genHeaderFile ${sourceFileName})
-                string(REPLACE ".cuda.gen.py" ".cu" genSourceFile ${sourceFileName})
-            else()
-                string(REPLACE ".gen.py" ".h"  genHeaderFile ${sourceFileName})
-                string(REPLACE ".gen.py" ".cpp" genSourceFile ${sourceFileName})
-            endif()
-            list(APPEND generatedResult ${CMAKE_CURRENT_BINARY_DIR}/${genSourceFile}
-                                        ${CMAKE_CURRENT_BINARY_DIR}/${genHeaderFile})
-            add_custom_command(OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/${genSourceFile}
-                                      ${CMAKE_CURRENT_BINARY_DIR}/${genHeaderFile}
-                               DEPENDS ${sourceFile}
-                               COMMAND ${PYTHON_EXECUTABLE} ${sourceFile}
-                               WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
-            include_directories(${CMAKE_CURRENT_BINARY_DIR})
-            list(APPEND generatorsResult ${sourceFile} )
+        if( ${sourceFile} MATCHES ".*\\.py$" )
             set(codeGenRequired YES)
+            if( WALBERLA_BUILD_WITH_CODEGEN)
+                execute_process(COMMAND ${PYTHON_EXECUTABLE} ${sourceFile} -l
+                            OUTPUT_VARIABLE generatedSourceFiles)
+                string(REGEX REPLACE "\n$" "" generatedSourceFiles "${generatedSourceFiles}")
+
+                set(generatedWithAbsolutePath )
+                foreach( filename ${generatedSourceFiles} )
+                    list(APPEND generatedWithAbsolutePath ${CMAKE_CURRENT_BINARY_DIR}/${filename})
+                endforeach()
+
+                list(APPEND generatedResult  ${generatedWithAbsolutePath} )
+                list(APPEND generatorsResult ${sourceFile} )
+
+                add_custom_command(OUTPUT ${generatedWithAbsolutePath}
+                                   DEPENDS ${sourceFile}
+                                   COMMAND ${PYTHON_EXECUTABLE} ${sourceFile} -g
+                                   WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+                include_directories(${CMAKE_CURRENT_BINARY_DIR})
+            endif()
         else()
             list(APPEND result ${sourceFile})
         endif()
diff --git a/python/waLBerla/tools/jobscripts/__init__.py b/python/waLBerla/tools/jobscripts/__init__.py
index 0a006cd3289ddd6b99f47ef21a8ecf948a2fd581..cafc3408aa0d926410aaef882e86ce1c359b34ea 100644
--- a/python/waLBerla/tools/jobscripts/__init__.py
+++ b/python/waLBerla/tools/jobscripts/__init__.py
@@ -8,6 +8,8 @@ from waLBerla.tools.jobscripts.hornet          import createJobscript as _cr_hor
 from waLBerla.tools.jobscripts.supermuc        import createJobscript as _cr_supermuc
 from waLBerla.tools.jobscripts.supermuc_phase2 import createJobscript as _cr_supermuc2
 from waLBerla.tools.jobscripts.juqueen         import createJobscript as _cr_juqueen
+from waLBerla.tools.jobscripts.pizdaint_hybrid import createJobscript as _cr_pizdainth
+
 
 def createJobscript(*args, **kwargs):
     """
@@ -43,6 +45,6 @@ def createJobscript(*args, **kwargs):
     if kwargs['machine'].lower() == 'supermuc_phase2': return _cr_supermuc2 ( *args, **kwargs )
     if kwargs['machine'].lower() == 'juqueen' :        return _cr_juqueen   ( *args, **kwargs )
     if kwargs['machine'].lower() == 'hornet'  :        return _cr_hornet    ( *args, **kwargs )
-    
-    raise ValueError( "Unknown Machine: supported machines <supermuc,supermuc_phase2,juqueen,hornet>" )
+    if kwargs['machine'].lower() == 'pizdaint_hybrid': return _cr_pizdainth ( *args, **kwargs )
+    raise ValueError( "Unknown Machine: supported machines <supermuc,supermuc_phase2,juqueen,hornet,pizdaint_hybrid>" )
     
\ No newline at end of file
diff --git a/tests/cuda/CMakeLists.txt b/tests/cuda/CMakeLists.txt
index 364b8bbea1b3980955955b22d3795ffc4048a2f9..7cde4254bbfbe376d04d666d23fdfb81b73f11eb 100644
--- a/tests/cuda/CMakeLists.txt
+++ b/tests/cuda/CMakeLists.txt
@@ -17,8 +17,7 @@ waLBerla_compile_test( FILES FieldIndexing3DTest.cpp FieldIndexing3DTest.cu )
 waLBerla_execute_test( NAME  FieldIndexing3DTest )
 
 waLBerla_compile_test( FILES codegen/CodegenJacobiGPU.cpp
-                             codegen/JacobiKernel2D.cuda.gen.py
-                             codegen/JacobiKernel3D.cuda.gen.py
+                             codegen/CudaJacobiKernel.py
                        DEPENDS blockforest timeloop gui )
 waLBerla_execute_test( NAME CodegenJacobiGPU )
 
diff --git a/tests/cuda/codegen/CodegenJacobiGPU.cpp b/tests/cuda/codegen/CodegenJacobiGPU.cpp
index f81ecf1a6c2a2c91481b5203b3ecad99489865b8..a8ebd370e44f6304ec2f0f6c17c8028b65ff10e1 100644
--- a/tests/cuda/codegen/CodegenJacobiGPU.cpp
+++ b/tests/cuda/codegen/CodegenJacobiGPU.cpp
@@ -18,8 +18,8 @@
 //
 //======================================================================================================================
 
-#include "JacobiKernel2D.h"
-#include "JacobiKernel3D.h"
+#include "CudaJacobiKernel2D.h"
+#include "CudaJacobiKernel3D.h"
 
 #include "cuda/HostFieldAllocator.h"
 #include "blockforest/Initialization.h"
@@ -110,7 +110,7 @@ void testJacobi2D()
 
    // Registering the sweep
    timeloop.add() << BeforeFunction(  commScheme, "Communication" )
-                  << Sweep( pystencils::JacobiKernel2D(gpuField, 1.0), "Jacobi Kernel" );
+                  << Sweep( pystencils::CudaJacobiKernel2D(gpuField, 1.0), "Jacobi Kernel" );
 
 
    cuda::fieldCpy<GPUField, ScalarField>( blocks, gpuField, cpuFieldID );
@@ -165,7 +165,7 @@ void testJacobi3D()
 
    // Registering the sweep
    timeloop.add() << BeforeFunction(  commScheme, "Communication" )
-                  << Sweep( pystencils::JacobiKernel3D(gpuField, 1.0), "Jacobi Kernel" );
+                  << Sweep( pystencils::CudaJacobiKernel3D(gpuField, 1.0), "Jacobi Kernel" );
 
 
    cuda::fieldCpy<GPUField, ScalarField>( blocks, gpuField, cpuFieldID );
diff --git a/tests/cuda/codegen/CudaJacobiKernel.py b/tests/cuda/codegen/CudaJacobiKernel.py
new file mode 100644
index 0000000000000000000000000000000000000000..d30da2edcb663a351634490c93d503742e34e418
--- /dev/null
+++ b/tests/cuda/codegen/CudaJacobiKernel.py
@@ -0,0 +1,17 @@
+from pystencils_walberla.sweep import Sweep
+from pystencils_walberla.cmake_integration import codegen
+
+def jacobi2D(sweep):
+    src = sweep.field("f1")
+    dst = sweep.temporaryField(src)
+
+    dst[0, 0] @= (src[1, 0] + src[-1, 0] + src[0, 1] + src[0, -1]) / (4 * S.h ** 2)
+
+def jacobi3D(sweep):
+    src = sweep.field("f1")
+    dst = sweep.temporaryField(src)
+
+    dst[0,0,0] @= (src[1,0,0] + src[-1,0,0] + src[0,1,0] + src[0, -1, 0] + src[0, 0, 1] + src[0, 0 , -1] ) / (6 * S.h**2)
+
+Sweep.generate('CudaJacobiKernel2D', jacobi2D, dim=2, target='gpu')
+Sweep.generate('CudaJacobiKernel3D', jacobi3D, dim=3, target='gpu')
\ No newline at end of file
diff --git a/tests/cuda/codegen/JacobiKernel2D.cuda.gen.py b/tests/cuda/codegen/JacobiKernel2D.cuda.gen.py
deleted file mode 100644
index ca6fca69cd5702bb2ec856b312f4e52890f1ec64..0000000000000000000000000000000000000000
--- a/tests/cuda/codegen/JacobiKernel2D.cuda.gen.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from pystencils_walberla import Sweep
-
-k = Sweep(dim=2)
-
-src = k.field("f1")
-dst = k.temporaryField(src)
-h = k.constant("h")
-
-rhs = (src[1,0] + src[-1,0] + src[0,1] + src[0, -1] ) / (4 * h**2)
-k.addEq(dst[0,0], rhs)
-
-k.generate()
diff --git a/tests/cuda/codegen/JacobiKernel3D.cuda.gen.py b/tests/cuda/codegen/JacobiKernel3D.cuda.gen.py
deleted file mode 100644
index 32ac6d17eff7cd85a103c38383e000f678ca04d9..0000000000000000000000000000000000000000
--- a/tests/cuda/codegen/JacobiKernel3D.cuda.gen.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from pystencils_walberla import Sweep
-
-k = Sweep(dim=3)
-
-src = k.field("f1")
-dst = k.temporaryField(src)
-h = k.constant("h")
-
-rhs = (src[1,0,0] + src[-1,0,0] + src[0,1,0] + src[0, -1, 0] + src[0, 0, 1] + src[0, 0 , -1] ) / (6 * h**2)
-k.addEq(dst[0,0,0], rhs)
-
-k.generate()
diff --git a/tests/field/CMakeLists.txt b/tests/field/CMakeLists.txt
index d7534a81375d2e85498a6c8fae5b51c0c5b0a68d..47a1549e231c0265c4bd5f55602a5082ed63bf87 100644
--- a/tests/field/CMakeLists.txt
+++ b/tests/field/CMakeLists.txt
@@ -59,7 +59,7 @@ endif( WALBERLA_BUILD_WITH_MPI )
 
 # CodeGen Tests
 
-waLBerla_compile_test( FILES codegen/CodegenJacobiCPU.cpp codegen/JacobiKernel2D.gen.py codegen/JacobiKernel3D.gen.py
+waLBerla_compile_test( FILES codegen/CodegenJacobiCPU.cpp codegen/JacobiKernel.py
                        DEPENDS gui timeloop )
 waLBerla_execute_test( NAME CodegenJacobiCPU )
 
diff --git a/tests/field/codegen/JacobiKernel.py b/tests/field/codegen/JacobiKernel.py
new file mode 100644
index 0000000000000000000000000000000000000000..bcdc4c72e5f3999fb66ccc98bc827fbeb9991eac
--- /dev/null
+++ b/tests/field/codegen/JacobiKernel.py
@@ -0,0 +1,16 @@
+from pystencils_walberla.sweep import Sweep
+
+def jacobi2D(sweep):
+    src = sweep.field("f1")
+    dst = sweep.temporaryField(src)
+
+    dst[0, 0] @= (src[1, 0] + src[-1, 0] + src[0, 1] + src[0, -1]) / (4 * S.h ** 2)
+
+def jacobi3D(sweep):
+    src = sweep.field("f1")
+    dst = sweep.temporaryField(src)
+
+    dst[0,0,0] @= (src[1,0,0] + src[-1,0,0] + src[0,1,0] + src[0, -1, 0] + src[0, 0, 1] + src[0, 0 , -1] ) / (6 * S.h**2)
+
+Sweep.generate('JacobiKernel2D', jacobi2D, dim=2)
+Sweep.generate('JacobiKernel3D', jacobi3D, dim=3)
\ No newline at end of file
diff --git a/tests/field/codegen/JacobiKernel2D.gen.py b/tests/field/codegen/JacobiKernel2D.gen.py
deleted file mode 100644
index ca6fca69cd5702bb2ec856b312f4e52890f1ec64..0000000000000000000000000000000000000000
--- a/tests/field/codegen/JacobiKernel2D.gen.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from pystencils_walberla import Sweep
-
-k = Sweep(dim=2)
-
-src = k.field("f1")
-dst = k.temporaryField(src)
-h = k.constant("h")
-
-rhs = (src[1,0] + src[-1,0] + src[0,1] + src[0, -1] ) / (4 * h**2)
-k.addEq(dst[0,0], rhs)
-
-k.generate()
diff --git a/tests/field/codegen/JacobiKernel3D.gen.py b/tests/field/codegen/JacobiKernel3D.gen.py
deleted file mode 100644
index 32ac6d17eff7cd85a103c38383e000f678ca04d9..0000000000000000000000000000000000000000
--- a/tests/field/codegen/JacobiKernel3D.gen.py
+++ /dev/null
@@ -1,12 +0,0 @@
-from pystencils_walberla import Sweep
-
-k = Sweep(dim=3)
-
-src = k.field("f1")
-dst = k.temporaryField(src)
-h = k.constant("h")
-
-rhs = (src[1,0,0] + src[-1,0,0] + src[0,1,0] + src[0, -1, 0] + src[0, 0, 1] + src[0, 0 , -1] ) / (6 * h**2)
-k.addEq(dst[0,0,0], rhs)
-
-k.generate()
diff --git a/tests/lbm/codegen/SrtWithForceField.cpp b/tests/lbm/codegen/SrtWithForceField.cpp
index c93dde10f184bd9e5b8d6d7fce05b5c66020e030..57e8674bf64f64d896ed151e839fd4c95883c33b 100644
--- a/tests/lbm/codegen/SrtWithForceField.cpp
+++ b/tests/lbm/codegen/SrtWithForceField.cpp
@@ -26,9 +26,16 @@
 #include "field/all.h"
 #include "geometry/all.h"
 #include "gui/all.h"
-#include "lbm/all.h"
 #include "timeloop/all.h"
 
+#include "lbm/field/PdfField.h"
+#include "lbm/field/AddToStorage.h"
+#include "lbm/communication/PdfFieldPackInfo.h"
+#include "lbm/gui/Connection.h"
+#include "lbm/vtk/VTKOutput.h"
+
+#include "MyUBB.h"
+#include "MyNoSlip.h"
 
 
 using namespace walberla;
@@ -60,8 +67,6 @@ int main( int argc, char ** argv )
 
    const double remainingTimeLoggerFrequency = parameters.getParameter< double >( "remainingTimeLoggerFrequency", 3.0 ); // in seconds
 
-   // create force field
-
    // create fields
    BlockDataID forceFieldId = field::addToStorage<ForceField_T>(blocks, "Force", real_t(0.0) );
 
@@ -72,18 +77,17 @@ int main( int argc, char ** argv )
    // create and initialize boundary handling
    const FlagUID fluidFlagUID( "Fluid" );
 
+
    auto boundariesConfig = walberlaEnv.config()->getOneBlock( "Boundaries" );
 
-   typedef lbm::DefaultBoundaryHandlingFactory< LatticeModel_T, FlagField_T > BHFactory;
+   lbm::MyUBB ubb(blocks, pdfFieldId);
+   lbm::MyNoSlip noSlip(blocks, pdfFieldId);
 
-   BlockDataID boundaryHandlingId = BHFactory::addBoundaryHandlingToStorage( blocks, "boundary handling", flagFieldId, pdfFieldId, fluidFlagUID,
-                                                                             boundariesConfig.getParameter< Vector3<real_t> >( "velocity0", Vector3<real_t>() ),
-                                                                             boundariesConfig.getParameter< Vector3<real_t> >( "velocity1", Vector3<real_t>() ),
-                                                                             boundariesConfig.getParameter< real_t > ( "pressure0", real_c( 1.0 ) ),
-                                                                             boundariesConfig.getParameter< real_t > ( "pressure1", real_c( 1.0 ) ) );
+   geometry::initBoundaryHandling<FlagField_T>(*blocks, flagFieldId, boundariesConfig);
+   geometry::setNonBoundaryCellsToDomain<FlagField_T>(*blocks, flagFieldId, fluidFlagUID);
 
-   geometry::initBoundaryHandling<BHFactory::BoundaryHandling>( *blocks, boundaryHandlingId, boundariesConfig );
-   geometry::setNonBoundaryCellsToDomain<BHFactory::BoundaryHandling> ( *blocks, boundaryHandlingId );
+   ubb.fillFromFlagField<FlagField_T>( blocks, flagFieldId, FlagUID("UBB"), fluidFlagUID );
+   noSlip.fillFromFlagField<FlagField_T>( blocks, flagFieldId, FlagUID("NoSlip"), fluidFlagUID );
 
    // create time loop
    SweepTimeloop timeloop( blocks->getBlockStorage(), timesteps );
@@ -94,7 +98,8 @@ int main( int argc, char ** argv )
 
    // add LBM sweep and communication to time loop
    timeloop.add() << BeforeFunction( communication, "communication" )
-                  << Sweep( BHFactory::BoundaryHandling::getBlockSweep( boundaryHandlingId ), "boundary handling" );
+                  << Sweep( noSlip, "noSlip boundary" );
+   timeloop.add() << Sweep( ubb, "ubb boundary" );
    timeloop.add() << Sweep( LatticeModel_T::Sweep( pdfFieldId ), "LB stream & collide" );
 
    // LBM stability check
diff --git a/tests/lbm/codegen/SrtWithForceFieldModel.gen.py b/tests/lbm/codegen/SrtWithForceFieldModel.gen.py
index d847c51c5d07f9751209d323d02958d71b395a91..2ecb139fbbba9d4562a6781441f2d929f9404086 100644
--- a/tests/lbm/codegen/SrtWithForceFieldModel.gen.py
+++ b/tests/lbm/codegen/SrtWithForceFieldModel.gen.py
@@ -1,6 +1,11 @@
 import sympy as sp
+from lbmpy.boundaries import NoSlip, UBB
 from lbmpy_walberla import Field, generateLatticeModelFiles, RefinementScaling
+from lbmpy.creationfunctions import createLatticeBoltzmannMethod
+from lbmpy_walberla.boundary import createBoundaryClass
+from pystencils_walberla.cmake_integration import codegen
 
+# ------------- Lattice Model ------------------------------
 forceField = Field.createGeneric('force', spatialDimensions=3, indexDimensions=1, layout='fzyx')
 force = [forceField(0), forceField(1), forceField(2)]
 
@@ -10,6 +15,21 @@ scaling = RefinementScaling()
 scaling.addStandardRelaxationRateScaling(omega)
 scaling.addForceScaling(forceField)
 
-generateLatticeModelFiles(method='srt', stencil='D3Q19', forceModel='guo', force=force,
+generateLatticeModelFiles(className='SrtWithForceFieldModel',
+                          method='srt', stencil='D3Q19', forceModel='guo', force=force,
                           relaxationRates=[omega], refinementScaling=scaling)
 
+
+def genBoundary():
+    boundary = UBB([0.05, 0, 0], dim=3, name="MyUBB")
+    method = createLatticeBoltzmannMethod(stencil='D3Q19', method='srt')
+    return createBoundaryClass(boundary, method)
+
+def genNoSlip():
+    boundary = NoSlip(name='MyNoSlip')
+    method = createLatticeBoltzmannMethod(stencil='D3Q19', method='srt')
+    return createBoundaryClass(boundary, method)
+
+codegen.register(['MyUBB.h', 'MyUBB.cpp'], genBoundary)
+codegen.register(['MyNoSlip.h', 'MyNoSlip.cpp',], genNoSlip)
+