diff --git a/examples/ForceDrivenChannel/LbmAlgorithms.py b/examples/ForceDrivenChannel/LbmAlgorithms.py
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..c27d3ee7d8e490de863ba5fff77d581b6b8f33e2 100644
--- a/examples/ForceDrivenChannel/LbmAlgorithms.py
+++ b/examples/ForceDrivenChannel/LbmAlgorithms.py
@@ -0,0 +1,61 @@
+import sympy as sp
+
+from pystencilssfg import SourceFileGenerator
+from sfg_walberla import Sweep
+from pystencils import Target, fields
+import pystencils.config as cfg
+from lbmpy import (
+    LBStencil,
+    Stencil,
+    Method,
+    LBMConfig,
+    LBMOptimisation,
+    create_lb_method,
+    create_lb_update_rule,
+)
+
+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("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)
+
diff --git a/examples/SimpleK b/examples/SimpleK
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
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__(