diff --git a/src/sfg_walberla/api.py b/src/sfg_walberla/api.py
index dd50def1eb7d99ac86f2fc28b48cb2c1272dc277..2c8caeef9bb6715af061e5d508a79cfdeddd4566 100644
--- a/src/sfg_walberla/api.py
+++ b/src/sfg_walberla/api.py
@@ -149,13 +149,59 @@ class StructuredBlockForest(AugExpr):
         return AugExpr(uint_t).bind("{}.getLevel({})", self, block)
 
 
-class GhostLayerFieldPtr(SrcField):
+class GenericWalberlaField(SrcField):
+    """Common base class for GhostLayerField and GpuField defining their shared interface."""
 
+    def __init__(
+        self,
+        element_type: PsType,
+        field_type: PsCustomType,
+        ptr: bool = False,
+        ref: bool = False
+    ):
+        if ptr and ref:
+            raise ValueError("At most one of `ptr` and `ref` may be true.")
+        
+        self._element_type = element_type
+        self._field_type = field_type
+
+        if ptr:
+            obj_type = PsPointerType(self._field_type)
+        elif ref:
+            obj_type = Ref(self._field_type)
+        else:
+            obj_type = self._field_type
+
+        super().__init__(obj_type)
+
+    @property
+    def _a(self) -> AugExpr:
+        """Member access"""
+        if isinstance(self.dtype, PsPointerType):
+            return AugExpr.format("{}->", self)
+        else:
+            return AugExpr.format("{}.", self)
+        
+    @property
+    def field_type(self) -> PsCustomType:
+        return self._field_type
+
+    def get_extraction(self) -> IFieldExtraction:
+        return GhostLayerFieldExtraction(self, None)
+    
+    def cloneUninitialized(self) -> AugExpr:
+        return AugExpr.format("{}cloneUninitialized()", self._a)
+    
+    def swapDataPointers(self, other: AugExpr) -> AugExpr:
+        return AugExpr.format("{}swapDataPointers({});", self._a, other)
+
+
+class GhostLayerFieldPtr(GenericWalberlaField):
     @staticmethod
     def create(field: Field):
         if field.index_dimensions > 1:
             raise ValueError(
-                "Cannot map fields with more than one index dimension to nfield::FieldBuffer."
+                "Cannot map fields with more than one index dimension to field::GhostLayerField."
             )
 
         element_type = field.dtype
@@ -168,31 +214,56 @@ class GhostLayerFieldPtr(SrcField):
         element_type: UserTypeSpec,
         fsize: int,
     ):
-        self._element_type = create_type(element_type)
-
-        elmt_type_str = self._element_type.c_string()
-        self._field_type = PsCustomType(
+        element_type = create_type(element_type)
+        elmt_type_str = element_type.c_string()
+        field_type = PsCustomType(
             f"walberla::field::GhostLayerField< {elmt_type_str}, {fsize} >"
         )
 
-        super().__init__(PsPointerType(self._field_type))
-
-    @property
-    def field_type(self) -> PsCustomType:
-        return self._field_type
-
-    def get_extraction(self) -> IFieldExtraction:
-        return GhostLayerFieldExtraction(self, None)
-
+        super().__init__(
+            element_type,
+            field_type,
+            ptr=True
+        )
+    
     @property
     def required_includes(self) -> set[SfgHeaderInclude]:
         return {SfgHeaderInclude("field/GhostLayerField.h")}
 
-    def cloneUninitialized(self) -> AugExpr:
-        return AugExpr.format("{}->cloneUninitialized()", self)
 
-    def swapDataPointers(self, other: GhostLayerFieldPtr) -> AugExpr:
-        return AugExpr.format("{}->swapDataPointers({});", self, other)
+class GpuFieldPtr(GenericWalberlaField):
+    @staticmethod
+    def create(field: Field):
+        if field.index_dimensions > 1:
+            raise ValueError(
+                "Cannot map fields with more than one index dimension to gpu::GpuField."
+            )
+
+        element_type = field.dtype
+        fsize = field.index_shape[0] if field.index_shape else 1
+
+        return GpuFieldPtr(element_type, fsize).var(field.name)
+
+    def __init__(
+        self,
+        element_type: UserTypeSpec,
+        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} >"
+        )
+
+        super().__init__(
+            element_type,
+            field_type,
+            ptr=True
+        )
+    
+    @property
+    def required_includes(self) -> set[SfgHeaderInclude]:
+        return {SfgHeaderInclude("gpu/GpuField.h")}
 
 
 class GhostLayerFieldExtraction(IFieldExtraction):
diff --git a/src/sfg_walberla/postprocessing.py b/src/sfg_walberla/postprocessing.py
index 603ffb099d6b16b42daf7573a05bee02735ad844..d4a911f1318cca77802af0d321a0e01d89b8c8ab 100644
--- a/src/sfg_walberla/postprocessing.py
+++ b/src/sfg_walberla/postprocessing.py
@@ -44,7 +44,7 @@ class CaptureToClass(SfgDeferredNode):
                 members_block.append_member(member)
                 captured.append(var)
                 statements.append(
-                    SfgStatements(f"{var.dtype} {var.name} {{ {member} }};", [var], [])
+                    SfgStatements(f"{var.dtype.c_string()} {var.name} {{ {member} }};", [var], [])
                 )
 
         cls.append_visibility_block(members_block)
diff --git a/src/sfg_walberla/sweep.py b/src/sfg_walberla/sweep.py
index a3998d72e731fdacc00db805bc81f4b074f8c722..1f094eae0e24603deac25b753d1b59b525c54e1d 100644
--- a/src/sfg_walberla/sweep.py
+++ b/src/sfg_walberla/sweep.py
@@ -8,15 +8,17 @@ import sympy as sp
 
 from pystencilssfg.composer.custom import CustomGenerator
 from pystencilssfg.composer.class_composer import SfgClassComposer
-from pystencilssfg.ir import SfgMethod
+from pystencilssfg.ir import SfgMethod, SfgKernelHandle
 
 from pystencils import (
     Assignment,
     AssignmentCollection,
     CreateKernelConfig,
     Field,
+    Target,
 )
 from pystencils.types import PsType, constify, deconstify, PsCustomType
+from pystencils.backend.kernelfunction import GpuKernelFunction
 
 from pystencilssfg import SfgComposer
 from pystencilssfg.lang import (
@@ -29,8 +31,11 @@ from pystencilssfg.lang import (
     strip_ptr_ref,
 )
 from .api import (
+    uint_t,
     StructuredBlockForest,
+    GenericWalberlaField,
     GhostLayerFieldPtr,
+    GpuFieldPtr,
     IBlockPtr,
     BlockDataID,
     Vector2,
@@ -249,7 +254,7 @@ class BlockforestParameters:
 @dataclass
 class FieldInfo:
     field: Field
-    glfield_ptr: GhostLayerFieldPtr
+    glfield_ptr: GenericWalberlaField
     data_id: BlockDataID
 
 
@@ -314,10 +319,25 @@ class ShadowFieldCache:
     def _getter(self, orig_name: str) -> str:
         return f"_getShadow_{orig_name}"
 
-    def _cache_ptr(self, orig: GhostLayerFieldPtr) -> str:
+    def _cache_ptr(self, orig: GenericWalberlaField) -> str:
         return f"shadow_{str(orig)}_"
 
 
+class CudaInvocation:
+    def __init__(self, khandle: SfgKernelHandle):
+        self._khandle = khandle
+
+        gpu_func = khandle.get_kernel_function()
+        assert isinstance(gpu_func, GpuKernelFunction)
+        self._gpu_func = gpu_func
+
+        self._block_size = Vector3(uint_t).var("gpuBlockSize")
+        
+    def render(self, sfg: SfgComposer):
+        pass
+
+
+
 def combine_vectors(scalars: set[SfgVar]) -> dict[SrcVector, tuple[SfgVar, ...]]:
     """Attempt to combine vector component symbols into vectors.
 
@@ -408,6 +428,15 @@ class Sweep(CustomGenerator):
             self._assignments = AssignmentCollection(assignments)
         self._gen_config = config
 
+        if self._gen_config.target == Target.CUDA:
+            self._glfield_type = GpuFieldPtr
+        elif self._gen_config.target.is_cpu():
+            self._glfield_type = GhostLayerFieldPtr
+        else:
+            raise ValueError(
+                f"Cannot generate sweep for target {self._gen_config.target}"
+            )
+
         #   Map from shadow field to shadowed field
         self._shadow_fields: dict[Field, Field] = dict()
 
@@ -431,7 +460,7 @@ class Sweep(CustomGenerator):
 
         all_fields: dict[str, FieldInfo] = {
             f.name: FieldInfo(
-                f, GhostLayerFieldPtr.create(f), BlockDataID().var(f"{f.name}Id")
+                f, self._glfield_type.create(f), BlockDataID().var(f"{f.name}Id")
             )
             for f in khandle.fields
         }
@@ -464,6 +493,10 @@ class Sweep(CustomGenerator):
         for s in sorted(parameters, key=lambda p: p.name):
             props.add_property(s, setter=True, getter=True)
 
+        #   GPU invocation
+        if self._gen_config.target == Target.CUDA:
+
+
         sfg.klass(self._name)(
             *props.render(sfg),
             sfg.public(