diff --git a/src/walberla/codegen/symbolic.py b/src/walberla/codegen/symbolic.py
index 04e02f20dd972019d7842731d80afa25ed6bd75e..7d591beaaa8ff198c66f40f0509c96511b09c1e8 100644
--- a/src/walberla/codegen/symbolic.py
+++ b/src/walberla/codegen/symbolic.py
@@ -235,24 +235,27 @@ class CellCoordinates:
     @staticmethod
     def local_x():
         """X coordinate of the current cell's center in the current block's local coordinate system"""
-        return CellCoordinates.cell_extents[0] * tcast.as_numeric(
-            block.ci_min[0] + DEFAULTS.spatial_counters[0]
+        return CellCoordinates.cell_extents[0] * (
+            tcast.as_numeric(block.ci_min[0] + DEFAULTS.spatial_counters[0])
+            + sp.Rational(1, 2)
         )
 
     @expandable("cell.local_y")
     @staticmethod
     def local_y():
         """Y coordinate of the current cell's center in the current block's local coordinate system"""
-        return CellCoordinates.cell_extents[1] * tcast.as_numeric(
-            block.ci_min[1] + DEFAULTS.spatial_counters[1]
+        return CellCoordinates.cell_extents[1] * (
+            tcast.as_numeric(block.ci_min[1] + DEFAULTS.spatial_counters[1])
+            + sp.Rational(1, 2)
         )
 
     @expandable("cell.local_z")
     @staticmethod
     def local_z():
         """Z coordinate of the current cell's center in the current block's local coordinate system"""
-        return CellCoordinates.cell_extents[2] * tcast.as_numeric(
-            block.ci_min[2] + DEFAULTS.spatial_counters[2]
+        return CellCoordinates.cell_extents[2] * (
+            tcast.as_numeric(block.ci_min[2] + DEFAULTS.spatial_counters[2])
+            + sp.Rational(1, 2)
         )
 
     @expandable("cell.dx")
@@ -310,5 +313,23 @@ class CellIndex:
             + DEFAULTS.spatial_counters[2]
         )
 
+    @expandable("cell_index.x_local")
+    @staticmethod
+    def x_local():
+        """X component of the current cell's index in the current block's local cell grid."""
+        return block.ci_min[0] + DEFAULTS.spatial_counters[0]
+
+    @expandable("cell_index.y_local")
+    @staticmethod
+    def y_local():
+        """Y component of the current cell's index in the current block's local cell grid."""
+        return block.ci_min[1] + DEFAULTS.spatial_counters[1]
+
+    @expandable("cell_index.z_local")
+    @staticmethod
+    def z_local():
+        """Z component of the current cell's index in the current block's local cell grid."""
+        return block.ci_min[2] + DEFAULTS.spatial_counters[2]
+
 
 cell_index = CellIndex
diff --git a/tests/CodegenFeatures/GeometryKernels.py b/tests/CodegenFeatures/GeometryKernels.py
index 6b3d42cd1be94c3db64543bd287ee6b1a38ea769..98078d81547d952f85df2d91f4cc4b8115a0fe9f 100644
--- a/tests/CodegenFeatures/GeometryKernels.py
+++ b/tests/CodegenFeatures/GeometryKernels.py
@@ -10,7 +10,7 @@ with SourceFileGenerator() as sfg:
     out_real = ps.fields("out(3): double[3D]")
     out_int = ps.fields("out(3): int64_t[3D]")
 
-    #   Cell Centers
+    #   Global Cell Centers
 
     asms = [
         ps.Assignment(out_real.center(0), cell.x()),
@@ -18,7 +18,29 @@ with SourceFileGenerator() as sfg:
         ps.Assignment(out_real.center(2), cell.z())
     ]
 
-    sweep = Sweep("CellCenter", asms)
+    sweep = Sweep("CellCentersGlobal", asms)
+    sfg.generate(sweep)
+
+    #   Local Cell Centers
+
+    asms = [
+        ps.Assignment(out_real.center(0), cell.local_x()),
+        ps.Assignment(out_real.center(1), cell.local_y()),
+        ps.Assignment(out_real.center(2), cell.local_z())
+    ]
+
+    sweep = Sweep("CellCentersLocal", asms)
+    sfg.generate(sweep)
+
+    #   Local Cell Indices
+
+    asms = [
+        ps.Assignment(out_int.center(0), cell_index.x_local()),
+        ps.Assignment(out_int.center(1), cell_index.y_local()),
+        ps.Assignment(out_int.center(2), cell_index.z_local())
+    ]
+
+    sweep = Sweep("CellIndicesLocal", asms)
     sfg.generate(sweep)
 
     #   Global Cell Indices
@@ -29,5 +51,6 @@ with SourceFileGenerator() as sfg:
         ps.Assignment(out_int.center(2), cell_index.z_global())
     ]
 
-    sweep = Sweep("CellIndexGlobal", asms)
+    sweep = Sweep("CellIndicesGlobal", asms)
     sfg.generate(sweep)
+
diff --git a/tests/CodegenFeatures/TestBlockforestGeometry.cpp b/tests/CodegenFeatures/TestBlockforestGeometry.cpp
index 720497b4d72542cee96be41822a53a524e3a6ed8..610c588ce09dd196cd46e1888eb67c38e37bc75e 100644
--- a/tests/CodegenFeatures/TestBlockforestGeometry.cpp
+++ b/tests/CodegenFeatures/TestBlockforestGeometry.cpp
@@ -1,60 +1,92 @@
-#include "core/all.h"
 #include "blockforest/all.h"
+
+#include "core/all.h"
+
 #include "field/all.h"
-#include "walberla/experimental/Sweep.hpp"
 
 #include "gen/GeometryKernels.hpp"
+#include "walberla/experimental/Sweep.hpp"
 
 using namespace walberla;
 namespace wex = walberla::experimental;
 
 using Int64Field = field::GhostLayerField< int64_t, 3 >;
-using RealField = field::GhostLayerField< real_t, 3 >;
+using RealField  = field::GhostLayerField< real_t, 3 >;
 
-int main(int argc, char ** argv) {
-    Environment env{ argc, argv };
+int main(int argc, char** argv)
+{
+   Environment env{ argc, argv };
 
-    auto blocks = blockforest::createUniformBlockGrid(
-        2, 2, 2,
-        4, 4, 4,
-        1.2
-    );
+   auto blocks = blockforest::createUniformBlockGrid(2, 2, 2, 4, 4, 4, 1.2);
 
-    auto intFieldId = field::addToStorage< Int64Field >(blocks, "intField");
-    auto realFieldId = field::addToStorage< RealField >(blocks, "realField");
+   auto intFieldId  = field::addToStorage< Int64Field >(blocks, "intField");
+   auto realFieldId = field::addToStorage< RealField >(blocks, "realField");
 
-    wex::sweep::SerialSweeper sweeper { blocks };
+   wex::sweep::SerialSweeper sweeper{ blocks };
 
-    CellCenter cellCenters { blocks, realFieldId };
-    sweeper.sweep(cellCenters);
+   {
+      CellCentersGlobal cellCenters{ blocks, realFieldId };
+      sweeper.sweep(cellCenters);
 
-    sweeper.sweep([&](IBlock& block){
-        RealField & outp = *block.getData< RealField >(realFieldId);
-        sweeper.forAllCells([&](Cell c) {
-            Cell globalCell{ c };
-            blocks->transformBlockLocalToGlobalCell( globalCell, block );
-            Vector3< real_t > desired = blocks->getCellCenter( globalCell, blocks->getLevel( block ) );
+      sweeper.sweep([&](IBlock& block) {
+         RealField& outp = *block.getData< RealField >(realFieldId);
+         sweeper.forAllCells([&](Cell c) {
+            Vector3< real_t > desired = blocks->getBlockLocalCellCenter(block, c);
 
             WALBERLA_CHECK_FLOAT_EQUAL(outp.get(c, 0), desired[0]);
             WALBERLA_CHECK_FLOAT_EQUAL(outp.get(c, 1), desired[1]);
             WALBERLA_CHECK_FLOAT_EQUAL(outp.get(c, 2), desired[2]);
-        });
-    });
+         });
+      });
+   }
+
+   {
+      CellCentersLocal cellCenters{ blocks, realFieldId };
+      sweeper.sweep(cellCenters);
+
+      sweeper.sweep([&](IBlock& block) {
+         RealField& outp = *block.getData< RealField >(realFieldId);
+         AABB blockAbb = block.getAABB();
+         sweeper.forAllCells([&](Cell c) {
+            Vector3< real_t > desired = blocks->getBlockLocalCellCenter(block, c) - blockAbb.min();
+
+            WALBERLA_CHECK_FLOAT_EQUAL(outp.get(c, 0), desired[0]);
+            WALBERLA_CHECK_FLOAT_EQUAL(outp.get(c, 1), desired[1]);
+            WALBERLA_CHECK_FLOAT_EQUAL(outp.get(c, 2), desired[2]);
+         });
+      });
+   }
+
+   {
+    CellIndicesLocal localIndices{ blocks, intFieldId };
+      sweeper.sweep(localIndices);
+
+      sweeper.sweep([&](IBlock& block) {
+         Int64Field& outp = *block.getData< Int64Field >(intFieldId);
+         sweeper.forAllCells([&](Cell c) {
+            WALBERLA_CHECK_EQUAL(outp.get(c, 0), c[0]);
+            WALBERLA_CHECK_EQUAL(outp.get(c, 1), c[1]);
+            WALBERLA_CHECK_EQUAL(outp.get(c, 2), c[2]);
+         });
+      });
+   }
 
-    CellIndexGlobal glocalIndices { blocks, intFieldId };
-    sweeper.sweep(glocalIndices);
+   {
+    CellIndicesGlobal glocalIndices{ blocks, intFieldId };
+      sweeper.sweep(glocalIndices);
 
-    sweeper.sweep([&](IBlock& block){
-        Int64Field & outp = *block.getData< Int64Field >(intFieldId);
-        sweeper.forAllCells([&](Cell c) {
+      sweeper.sweep([&](IBlock& block) {
+         Int64Field& outp = *block.getData< Int64Field >(intFieldId);
+         sweeper.forAllCells([&](Cell c) {
             Cell globalCell{ c };
-            blocks->transformBlockLocalToGlobalCell( globalCell, block );
+            blocks->transformBlockLocalToGlobalCell(globalCell, block);
 
             WALBERLA_CHECK_EQUAL(outp.get(c, 0), globalCell[0]);
             WALBERLA_CHECK_EQUAL(outp.get(c, 1), globalCell[1]);
             WALBERLA_CHECK_EQUAL(outp.get(c, 2), globalCell[2]);
-        });
-    });
+         });
+      });
+   }
 
-    return EXIT_SUCCESS;
+   return EXIT_SUCCESS;
 }
\ No newline at end of file