diff --git a/tests/BasicLbmScenarios/TestBasicLbmScenarios.cpp b/tests/BasicLbmScenarios/TestBasicLbmScenarios.cpp
index 4be1a2dbb1a6c139c08973f23ae9b049a48f83bd..e058065c434bba7f2ae9bc005ed84eeff2858bcb 100644
--- a/tests/BasicLbmScenarios/TestBasicLbmScenarios.cpp
+++ b/tests/BasicLbmScenarios/TestBasicLbmScenarios.cpp
@@ -53,7 +53,7 @@ void fullyPeriodic(Environment& env)
 #if defined(LBM_SCENARIOS_GPU_BUILD)
 /**
  * Periodic channel flow with a no-slip boundary at the top
- * and a mirror boundary at the bottom.
+ * and a symmetry plane at the bottom implemented using the free-slip boundary.
  * Flow is governed by the Hagen-Poiseuille-law,
  * with the maximum at the bottom.
  */
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
index 0c98d01393c421c02262dd50034b065ada593d16..ce709453b068d38fd994f918daa68ade3083bafa 100644
--- a/tests/CMakeLists.txt
+++ b/tests/CMakeLists.txt
@@ -27,3 +27,4 @@ include(CTest)
 add_custom_target( SfgTests )
 
 add_subdirectory( BasicLbmScenarios )
+add_subdirectory( SparseSpiral )
diff --git a/tests/SparseSpiral/CMakeLists.txt b/tests/SparseSpiral/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..1e5977dd40907baa93070de9c36bb50406994e67
--- /dev/null
+++ b/tests/SparseSpiral/CMakeLists.txt
@@ -0,0 +1,6 @@
+
+add_executable( SparseSpiral SparseSpiral.cpp )
+walberla_generate_sources( SparseSpiral SCRIPTS SpiralSweep.py )
+target_link_libraries( SparseSpiral PRIVATE walberla::core walberla::blockforest walberla::field walberla::vtk walberla::experimental )
+
+add_test( NAME SparseSpiral COMMAND SparseSpiral )
diff --git a/tests/SparseSpiral/SparseSpiral.cpp b/tests/SparseSpiral/SparseSpiral.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fe96364af00fd05478164004b1ec3a22d136c909
--- /dev/null
+++ b/tests/SparseSpiral/SparseSpiral.cpp
@@ -0,0 +1,105 @@
+#include "core/all.h"
+#include "blockforest/all.h"
+#include "field/GhostLayerField.h"
+#include "field/AddToStorage.h"
+#include "field/vtk/all.h"
+#include "walberla/experimental/sweep/SparseIndexList.hpp"
+
+#include <array>
+
+#include "gen/SpiralSweep.hpp"
+
+namespace SparseSpiral
+{
+    using namespace walberla;
+    using namespace walberla::experimental;
+
+    using std::array;
+    using std::vector;
+
+    using PointField = field::GhostLayerField<real_t, 3>;
+
+    constexpr real_t radius { 15.0 };
+    constexpr Vector3< real_t > center { 20, 20, 0 };
+    constexpr real_t height { 120. };
+    constexpr real_t width { 4.0 };
+
+    Vector3<real_t> pointOnSpiral(real_t z)
+    {
+        real_t t{4.0 * math::pi * z / height};
+        return center + Vector3<real_t>{radius * cos(t), radius * sin(t), z};
+    }
+
+    void main(int argc, char **argv)
+    {
+        Environment env{ argc, argv };
+
+        AABB domainAabb{0., 0., 0., 40., 40., height };
+        array<uint_t, 3> gridSize{4, 4, 12};
+        array<uint_t, 3> cpb{10, 10, 10};
+
+        auto blocks = blockforest::createUniformBlockGrid(
+            domainAabb,
+            gridSize[0], gridSize[1], gridSize[2],
+            cpb[0], cpb[1], cpb[2]);
+
+        BlockDataID fID = field::addToStorage<PointField>(blocks, "f", real_c(0.0), field::fzyx);
+        sweep::SparseIndexList indexList{*blocks};
+
+        auto isOnSpiral = [&](Cell globalCell) -> bool
+        {
+            Vector3<real_t> globalPoint{blocks->getCellCenter(globalCell)};
+            Vector3<real_t> spiralPoint{pointOnSpiral(globalPoint[2])};
+            return (globalPoint - spiralPoint).sqrLength() < width;
+        };
+
+        CellInterval allCells{{0, 0, 0}, {cell_idx_c(cpb[0]), cell_idx_c(cpb[1]), cell_idx_c(cpb[2])}};
+
+        for (auto &b : *blocks)
+        {
+            auto & idxVec{indexList.getVector(b)};
+            for (Cell c : allCells)
+            {
+                Cell globalCell{c};
+                blocks->transformBlockLocalToGlobalCell(globalCell, b);
+                if (isOnSpiral(globalCell))
+                {
+                    idxVec.emplace_back(c);
+                }
+            }
+        }
+
+        gen::SpiralSweep sweep{blocks, fID, indexList};
+
+        for (auto &b : *blocks)
+        {
+            sweep(&b);
+        }
+
+        for (auto &b : *blocks)
+        {
+            PointField & f = *b.getData<PointField>(fID);
+            for (Cell c : allCells)
+            {
+                Cell globalCell{c};
+                blocks->transformBlockLocalToGlobalCell(globalCell, b);
+                if (isOnSpiral(c))
+                {
+                    Vector3< real_t > cellCenter { blocks->getCellCenter(globalCell) };
+                    WALBERLA_CHECK_FLOAT_EQUAL(f.get(c, 0), cellCenter[0]);
+                    WALBERLA_CHECK_FLOAT_EQUAL(f.get(c, 1), cellCenter[1]);
+                    WALBERLA_CHECK_FLOAT_EQUAL(f.get(c, 2), cellCenter[2]);
+                }
+            }
+        }
+
+        auto vtkWriter = field::createVTKOutput< PointField >(fID, *blocks, "f", 1, 0);
+        vtkWriter();
+    }
+}
+
+int main(int argc, char **argv)
+{
+    SparseSpiral::main(argc, argv);
+    return 0;
+}
diff --git a/tests/SparseSpiral/SpiralSweep.py b/tests/SparseSpiral/SpiralSweep.py
new file mode 100644
index 0000000000000000000000000000000000000000..4fd59bd19a737b1b01e7473bc9343fa8e8022b16
--- /dev/null
+++ b/tests/SparseSpiral/SpiralSweep.py
@@ -0,0 +1,21 @@
+from pystencilssfg import SourceFileGenerator
+from walberla.codegen import Sweep
+from walberla.codegen.symbolic import cell
+from pystencils import fields, Assignment, CreateKernelConfig, Target
+
+with SourceFileGenerator() as sfg:
+    sfg.namespace("SparseSpiral::gen")
+    f = fields("f(3): double[3D]", layout="fzyx")
+
+    asms = [
+        Assignment(f.center(0), cell.x()),
+        Assignment(f.center(1), cell.y()),
+        Assignment(f.center(2), cell.z()),
+    ]
+
+    cfg = CreateKernelConfig(target=Target.GenericCPU)
+
+    sweep = Sweep("SpiralSweep", asms, cfg)
+    sweep.sparse = True
+
+    sfg.generate(sweep)
diff --git a/user_manual/examples/CMakeLists.txt b/user_manual/examples/CMakeLists.txt
index 499f1f083bf4419e3f4c8d72c7ccb55123500ad4..2f19f387483abcbec59bd001091250c3932ff1dc 100644
--- a/user_manual/examples/CMakeLists.txt
+++ b/user_manual/examples/CMakeLists.txt
@@ -1,6 +1,9 @@
 cmake_minimum_required( VERSION 3.24 )
 project( walberla-codegen-examples )
 
+set(CMAKE_CXX_STANDARD 20)
+set(CMAKE_CXX_STANDARD_REQUIRED)
+
 include(FetchContent)
 
 FetchContent_Declare(
diff --git a/user_manual/examples/SparseSpiral/SparseSpiral.cpp b/user_manual/examples/SparseSpiral/SparseSpiral.cpp
index 6d11b4bebac9a6d50f43562342ca076a5d5d4db0..7f3704e7cff1bed9e2d4d87eb9a9d9ddb9a0ee00 100644
--- a/user_manual/examples/SparseSpiral/SparseSpiral.cpp
+++ b/user_manual/examples/SparseSpiral/SparseSpiral.cpp
@@ -57,7 +57,7 @@ namespace SparseSpiral
 
         for (auto &b : *blocks)
         {
-            vector< sweep::CellIdx > & idxVec{indexList.get(b)};
+            auto & idxVec{indexList.get(b)};
             for (Cell c : allCells)
             {
                 Cell globalCell{c};
@@ -69,7 +69,7 @@ namespace SparseSpiral
             }
         }
 
-        gen::SpiralSweep sweep{blocks, fID, indexList.bufferId()};
+        gen::SpiralSweep sweep{blocks, fID, indexList};
 
         for (auto &b : *blocks)
         {