diff --git a/python/pystencils_walberla/codegen.py b/python/pystencils_walberla/codegen.py
index 5c2667342816e4b3f144e98f54babe31ff99d3b1..fea8c04b0a97cb161f1d34cbdb0858a9de490ec0 100644
--- a/python/pystencils_walberla/codegen.py
+++ b/python/pystencils_walberla/codegen.py
@@ -146,6 +146,8 @@ def generate_selective_sweep(generation_context, class_name, selection_tree, int
 
 def generate_pack_info_for_field(generation_context, class_name: str, field: Field,
                                  direction_subset: Optional[Tuple[Tuple[int, int, int]]] = None,
+                                 operator=None,
+                                 gl_to_inner=False,
                                  **create_kernel_params):
     """Creates a pack info for a pystencils field assuming a pull-type stencil, packing all cell elements.
 
@@ -155,18 +157,21 @@ def generate_pack_info_for_field(generation_context, class_name: str, field: Fie
         field: pystencils field for which to generate pack info
         direction_subset: optional sequence of directions for which values should be packed
                           otherwise a D3Q27 stencil is assumed
+        operator: optional operator for, e.g., reduction pack infos
+        gl_to_inner: communicates values from ghost layers of sender to interior of receiver
         **create_kernel_params: remaining keyword arguments are passed to `pystencils.create_kernel`
     """
+
     if not direction_subset:
         direction_subset = tuple((i, j, k) for i, j, k in product(*[(-1, 0, 1)] * 3))
 
     all_index_accesses = [field(*ind) for ind in product(*[range(s) for s in field.index_shape])]
-    return generate_pack_info(generation_context, class_name, {direction_subset: all_index_accesses},
-                              **create_kernel_params)
+    return generate_pack_info(generation_context, class_name, {direction_subset: all_index_accesses}, operator=operator,
+                              gl_to_inner=gl_to_inner, **create_kernel_params)
 
 
 def generate_pack_info_from_kernel(generation_context, class_name: str, assignments: Sequence[Assignment],
-                                   kind='pull', **create_kernel_params):
+                                   kind='pull', operator=None, **create_kernel_params):
     """Generates a waLBerla GPU PackInfo from a (pull) kernel.
 
     Args:
@@ -175,6 +180,7 @@ def generate_pack_info_from_kernel(generation_context, class_name: str, assignme
         assignments: list of assignments from the compute kernel - generates PackInfo for "pull" part only
                      i.e. the kernel is expected to only write to the center
         kind: can either be pull or push
+        operator: optional operator for, e.g., reduction pack infos
         **create_kernel_params: remaining keyword arguments are passed to `pystencils.create_kernel`
     """
     assert kind in ('push', 'pull')
@@ -207,12 +213,12 @@ def generate_pack_info_from_kernel(generation_context, class_name: str, assignme
                 spec[(comm_dir,)].add(fa)
     else:
         raise ValueError("Invalid 'kind' parameter")
-    return generate_pack_info(generation_context, class_name, spec, **create_kernel_params)
+    return generate_pack_info(generation_context, class_name, spec, operator=operator, **create_kernel_params)
 
 
 def generate_pack_info(generation_context, class_name: str,
                        directions_to_pack_terms: Dict[Tuple[Tuple], Sequence[Field.Access]],
-                       namespace='pystencils',
+                       namespace='pystencils', operator=None, gl_to_inner=False,
                        **create_kernel_params):
     """Generates a waLBerla GPU PackInfo
 
@@ -222,6 +228,8 @@ def generate_pack_info(generation_context, class_name: str,
         directions_to_pack_terms: maps tuples of directions to read field accesses, specifying which values have to be
                                   packed for which direction
         namespace: inner namespace of the generated class
+        operator: optional operator for, e.g., reduction pack infos
+        gl_to_inner: communicates values from ghost layers of sender to interior of receiver
         **create_kernel_params: remaining keyword arguments are passed to `pystencils.create_kernel`
     """
     items = [(e[0], sorted(e[1], key=lambda x: str(x))) for e in directions_to_pack_terms.items()]
@@ -274,7 +282,10 @@ def generate_pack_info(generation_context, class_name: str,
         pack_ast = create_kernel(pack_assignments, **create_kernel_params, ghost_layers=0)
         pack_ast.function_name = 'pack_{}'.format("_".join(direction_strings))
         pack_ast.assumed_inner_stride_one = create_kernel_params['cpu_vectorize_info']['assume_inner_stride_one']
-        unpack_assignments = [Assignment(term, buffer(i)) for i, term in enumerate(terms)]
+        if operator is None:
+            unpack_assignments = [Assignment(term, buffer(i)) for i, term in enumerate(terms)]
+        else:
+            unpack_assignments = [Assignment(term, operator(term, buffer(i))) for i, term in enumerate(terms)]
         unpack_ast = create_kernel(unpack_assignments, **create_kernel_params, ghost_layers=0)
         unpack_ast.function_name = 'unpack_{}'.format("_".join(direction_strings))
         unpack_ast.assumed_inner_stride_one = create_kernel_params['cpu_vectorize_info']['assume_inner_stride_one']
@@ -296,6 +307,7 @@ def generate_pack_info(generation_context, class_name: str,
         'dtype': dtype,
         'field_name': field_names.pop(),
         'namespace': namespace,
+        'gl_to_inner': gl_to_inner,
     }
     env = Environment(loader=PackageLoader('pystencils_walberla'), undefined=StrictUndefined)
     add_pystencils_filters_to_jinja_env(env)
diff --git a/python/pystencils_walberla/templates/CpuPackInfo.tmpl.cpp b/python/pystencils_walberla/templates/CpuPackInfo.tmpl.cpp
index 41dd20ed21af9652606d9215faf8351dcd3118b7..d56ec573032eaddba9ba9b959883a864a3f3ce63 100644
--- a/python/pystencils_walberla/templates/CpuPackInfo.tmpl.cpp
+++ b/python/pystencils_walberla/templates/CpuPackInfo.tmpl.cpp
@@ -37,7 +37,11 @@ void {{class_name}}::pack(Direction dir, unsigned char * byte_buffer, IBlock * b
 
     {{fused_kernel|generate_block_data_to_field_extraction(parameters_to_ignore=['buffer'])|indent(4)}}
     CellInterval ci;
+    {% if gl_to_inner -%}
+    {{field_name}}->getGhostRegion(dir, ci, 1, false);
+    {%- else -%}
     {{field_name}}->getSliceBeforeGhostLayer(dir, ci, 1, false);
+    {%- endif %}
 
     switch( dir )
     {
@@ -63,7 +67,11 @@ void {{class_name}}::unpack(Direction dir, unsigned char * byte_buffer, IBlock *
 
     {{fused_kernel|generate_block_data_to_field_extraction(parameters_to_ignore=['buffer'])|indent(4)}}
     CellInterval ci;
+    {% if gl_to_inner -%}
+    {{field_name}}->getSliceBeforeGhostLayer(dir, ci, 1, false);
+    {%- else -%}
     {{field_name}}->getGhostRegion(dir, ci, 1, false);
+    {%- endif %}
     auto communciationDirection = stencil::inverseDir[dir];
 
     switch( communciationDirection )
diff --git a/python/pystencils_walberla/templates/GpuPackInfo.tmpl.cpp b/python/pystencils_walberla/templates/GpuPackInfo.tmpl.cpp
index d2624b18800f4ee4f8f6d16833cc0f327224ea02..db79ae375bf8c815da1623abe6961aa727bc1ede 100644
--- a/python/pystencils_walberla/templates/GpuPackInfo.tmpl.cpp
+++ b/python/pystencils_walberla/templates/GpuPackInfo.tmpl.cpp
@@ -35,7 +35,11 @@ void {{class_name}}::pack(Direction dir, unsigned char * byte_buffer, IBlock * b
 
     {{fused_kernel|generate_block_data_to_field_extraction(parameters_to_ignore=['buffer'])|indent(4)}}
     CellInterval ci;
+    {% if gl_to_inner -%}
+    {{field_name}}->getGhostRegion(dir, ci, 1, false);
+    {%- else -%}
     {{field_name}}->getSliceBeforeGhostLayer(dir, ci, 1, false);
+    {%- endif %}
 
     switch( dir )
     {
@@ -61,7 +65,11 @@ void {{class_name}}::unpack(Direction dir, unsigned char * byte_buffer, IBlock *
 
     {{fused_kernel|generate_block_data_to_field_extraction(parameters_to_ignore=['buffer'])|indent(4)}}
     CellInterval ci;
+    {% if gl_to_inner -%}
+    {{field_name}}->getSliceBeforeGhostLayer(dir, ci, 1, false);
+    {%- else -%}
     {{field_name}}->getGhostRegion(dir, ci, 1, false);
+    {%- endif %}
     auto communciationDirection = stencil::inverseDir[dir];
 
     switch( communciationDirection )
diff --git a/src/cuda/communication/UniformGPUScheme.impl.h b/src/cuda/communication/UniformGPUScheme.impl.h
index 03b65f3b58981a1239152ddb1f444798e1f9a90e..c2bdb32056762ee9b68bdd68f7916ee793c11966 100644
--- a/src/cuda/communication/UniformGPUScheme.impl.h
+++ b/src/cuda/communication/UniformGPUScheme.impl.h
@@ -118,6 +118,7 @@ UniformGPUScheme<Stencil>::UniformGPUScheme( weak_ptr <StructuredBlockForest> bf
          auto parallelSection = parallelSectionManager_.parallelSection( stream );
          for( auto recvInfo = bufferSystemGPU_.begin(); recvInfo != bufferSystemGPU_.end(); ++recvInfo )
          {
+            recvInfo.buffer().clear();
             for( auto &header : headers_[recvInfo.rank()] )
             {
                auto block = dynamic_cast< Block * >( forest->getBlock( header.blockId ));
@@ -141,6 +142,7 @@ UniformGPUScheme<Stencil>::UniformGPUScheme( weak_ptr <StructuredBlockForest> bf
          {
             auto &gpuBuffer = bufferSystemGPU_.sendBuffer( recvInfo.rank());
 
+            recvInfo.buffer().clear();
             gpuBuffer.clear();
             for( auto &header : headers_[recvInfo.rank()] ) {
                auto block = dynamic_cast< Block * >( forest->getBlock( header.blockId ));
diff --git a/tests/cuda/CMakeLists.txt b/tests/cuda/CMakeLists.txt
index b164640649283ab5683ee295c2d4931517aee223..d301b7eb5ccc1fd707cdb5b40d9ecd6f1b63a836 100644
--- a/tests/cuda/CMakeLists.txt
+++ b/tests/cuda/CMakeLists.txt
@@ -47,4 +47,10 @@ waLBerla_generate_target_from_python(NAME MicroBenchmarkGpuLbmGenerated FILE cod
       OUT_FILES MicroBenchmarkStreamKernel.cu MicroBenchmarkCopyKernel.cu MicroBenchmarkStreamKernel.h MicroBenchmarkCopyKernel.h)
 waLBerla_compile_test( FILES codegen/MicroBenchmarkGpuLbm.cpp DEPENDS MicroBenchmarkGpuLbmGenerated)
 
+waLBerla_generate_target_from_python(NAME CodegenGeneratedGPUFieldPackInfo FILE codegen/GeneratedFieldPackInfoTestGPU.py
+        OUT_FILES ScalarFieldCommunicationGPU.cu ScalarFieldCommunicationGPU.h
+        ScalarFieldPullReductionGPU.cu ScalarFieldPullReductionGPU.h )
+waLBerla_compile_test( FILES codegen/GeneratedFieldPackInfoTestGPU.cpp
+        DEPENDS blockforest core field CodegenGeneratedGPUFieldPackInfo )
+waLBerla_execute_test( NAME GeneratedFieldPackInfoTestGPU )
 endif()
\ No newline at end of file
diff --git a/tests/cuda/codegen/GeneratedFieldPackInfoTestGPU.cpp b/tests/cuda/codegen/GeneratedFieldPackInfoTestGPU.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4c4bed8b4e3d87fa1498a9fc2d8f942a7444093e
--- /dev/null
+++ b/tests/cuda/codegen/GeneratedFieldPackInfoTestGPU.cpp
@@ -0,0 +1,213 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can 
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of 
+//  the License, or (at your option) any later version.
+//  
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT 
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
+//  for more details.
+//  
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file GeneratedFieldPackInfoTestGPU.cpp
+//! \ingroup field
+//! \author Helen Schottenhamml <helen.schottenhamml@fau.de>
+//! \brief Tests if a GPU Field is correctly communicated using generated pack info
+//
+//======================================================================================================================
+
+#include "field/AddToStorage.h"
+#include "field/GhostLayerField.h"
+
+#include "blockforest/Initialization.h"
+
+#include "core/debug/TestSubsystem.h"
+#include "core/Environment.h"
+
+#include "cuda/FieldCopy.h"
+#include "cuda/communication/UniformGPUScheme.h"
+
+#include "stencil/D3Q27.h"
+
+// include generated files
+#include "ScalarFieldCommunicationGPU.h"
+#include "ScalarFieldPullReductionGPU.h"
+
+
+namespace walberla {
+
+using Stencil_T = stencil::D3Q27;
+
+cuda::GPUField<int> * createGPUField( IBlock* const block, StructuredBlockStorage* const storage ) {
+
+   return new cuda::GPUField<int> (
+      storage->getNumberOfXCells( *block ), // number of cells in x direction
+      storage->getNumberOfYCells( *block ), // number of cells in y direction
+      storage->getNumberOfZCells( *block ), // number of cells in z direction
+      1,                                    // fSize
+      1,                                    // number of ghost layers
+      field::fzyx );
+
+}
+
+cuda::GPUField<int> * createSmallGPUField( IBlock * const , StructuredBlockStorage * const ) {
+   return new cuda::GPUField<int> (2, 2, 2, 1, 1, field::fzyx );
+}
+
+
+void testScalarField( std::shared_ptr<blockforest::StructuredBlockForest> & sbf, BlockDataID gpuFieldId ) {
+
+   cuda::communication::UniformGPUScheme< Stencil_T > us{ sbf };
+   us.addPackInfo(std::make_shared< pystencils::ScalarFieldCommunicationGPU >(gpuFieldId));
+
+   for( auto & block : *sbf ) {
+
+      auto & gpuField = *(block.getData< cuda::GPUField< int > >(gpuFieldId));
+
+      field::GhostLayerField< int, 1 > cpuField(gpuField.xSize(), gpuField.ySize(), gpuField.zSize(), 1, 0,
+                                                field::fzyx);
+      cpuField.setWithGhostLayer(0);
+
+      WALBERLA_CHECK_EQUAL(cpuField.xSize(), 2)
+      WALBERLA_CHECK_EQUAL(cpuField.ySize(), 2)
+      WALBERLA_CHECK_EQUAL(cpuField.zSize(), 2)
+
+      // initialize the bottom boundary
+      cpuField(0, 0, 0) = 1;
+      cpuField(0, 1, 0) = 2;
+      cpuField(1, 0, 0) = 3;
+      cpuField(1, 1, 0) = 4;
+
+      cuda::fieldCpy(gpuField, cpuField);
+
+      // communicate
+      us.communicate();
+
+      cuda::fieldCpy(cpuField, gpuField);
+
+      WALBERLA_CHECK_EQUAL(cpuField(0, 0, +2), 1)
+      WALBERLA_CHECK_EQUAL(cpuField(0, 1, +2), 2)
+      WALBERLA_CHECK_EQUAL(cpuField(1, 0, +2), 3)
+      WALBERLA_CHECK_EQUAL(cpuField(1, 1, +2), 4)
+
+   }
+}
+
+void testScalarFieldPullReduction( std::shared_ptr<blockforest::StructuredBlockForest> & sbf, BlockDataID gpuFieldId ) {
+
+   cuda::communication::UniformGPUScheme< Stencil_T > us1{ sbf };
+   us1.addPackInfo(std::make_shared< pystencils::ScalarFieldPullReductionGPU >(gpuFieldId));
+
+   cuda::communication::UniformGPUScheme< Stencil_T > us2{ sbf };
+   us2.addPackInfo(std::make_shared< pystencils::ScalarFieldCommunicationGPU >(gpuFieldId));
+
+   for( auto & block : *sbf ) {
+
+      auto& gpuField = *(block.getData< cuda::GPUField< int > >(gpuFieldId));
+
+      field::GhostLayerField< int, 1 > cpuField(gpuField.xSize(), gpuField.ySize(), gpuField.zSize(), 1, 0,
+                                                field::fzyx);
+      cpuField.setWithGhostLayer(0);
+
+      WALBERLA_CHECK_EQUAL(cpuField.xSize(), 2)
+      WALBERLA_CHECK_EQUAL(cpuField.ySize(), 2)
+      WALBERLA_CHECK_EQUAL(cpuField.zSize(), 2)
+
+      // initialize the bottom ghost layer cells
+      cpuField(0, 0, -1) = 1;
+      cpuField(0, 1, -1) = 2;
+      cpuField(1, 0, -1) = 3;
+      cpuField(1, 1, -1) = 4;
+
+      // initialize the top interior cells
+      cpuField(0, 0, 1) = 1;
+      cpuField(0, 1, 1) = 1;
+      cpuField(1, 0, 1) = 1;
+      cpuField(1, 1, 1) = 1;
+
+      cuda::fieldCpy(gpuField, cpuField);
+
+      // communicate pull += reduction
+      us1.communicate();
+
+      cuda::fieldCpy(cpuField, gpuField);
+
+      // check values in top ghost layer
+      WALBERLA_CHECK_EQUAL(cpuField(0, 0, 2), 0)
+      WALBERLA_CHECK_EQUAL(cpuField(0, 1, 2), 0)
+      WALBERLA_CHECK_EQUAL(cpuField(1, 0, 2), 0)
+      WALBERLA_CHECK_EQUAL(cpuField(1, 1, 2), 0)
+
+      // check values in top interior cells
+      WALBERLA_CHECK_EQUAL(cpuField(0, 0, 1), 2)
+      WALBERLA_CHECK_EQUAL(cpuField(0, 1, 1), 3)
+      WALBERLA_CHECK_EQUAL(cpuField(1, 0, 1), 4)
+      WALBERLA_CHECK_EQUAL(cpuField(1, 1, 1), 5)
+
+      // communicate to sync ghost layers
+      us2.communicate();
+
+      cuda::fieldCpy(cpuField, gpuField);
+
+      // check values in bottom ghost layer
+      WALBERLA_CHECK_EQUAL(cpuField(0, 0, -1), 2)
+      WALBERLA_CHECK_EQUAL(cpuField(0, 1, -1), 3)
+      WALBERLA_CHECK_EQUAL(cpuField(1, 0, -1), 4)
+      WALBERLA_CHECK_EQUAL(cpuField(1, 1, -1), 5)
+
+      // check values in top interior cells
+      WALBERLA_CHECK_EQUAL(cpuField(0, 0, 1), 2)
+      WALBERLA_CHECK_EQUAL(cpuField(0, 1, 1), 3)
+      WALBERLA_CHECK_EQUAL(cpuField(1, 0, 1), 4)
+      WALBERLA_CHECK_EQUAL(cpuField(1, 1, 1), 5)
+
+   }
+}
+
+int main(int argc, char **argv) {
+
+   using blockforest::createUniformBlockGrid;
+
+   debug::enterTestMode();
+   Environment walberlaEnv(argc,argv);
+
+   // Create a BlockForest with 2x2x2 cells per block
+   uint_t processes = uint_c( MPIManager::instance()->numProcesses() );
+   auto blocks = createUniformBlockGrid(processes,1 ,1, //blocks
+                                        2,2,2, //cells
+                                        1, //dx
+                                        true, //one block per process
+                                        true,true,true); //periodicity
+
+   // Create a Field with the same number of cells as the block
+   BlockDataID scalarGPUFieldId = blocks->addStructuredBlockData<cuda::GPUField<int> > ( &createGPUField, "ScalarGPUField" );
+
+   testScalarField( blocks, scalarGPUFieldId );
+
+   // Create a BlockForest with 8x8x8 cells per block
+   blocks = createUniformBlockGrid(processes,1 ,1, //blocks
+                                   8,8,8,          //cells
+                                   1,              //dx
+                                   true,          //one block per process
+                                   true,true,true);//periodicity
+
+   // Create a Field with one quarter as many cells per dimension, i.e. a field with the same size as the one above
+   scalarGPUFieldId = blocks->addStructuredBlockData<cuda::GPUField<int> > ( &createSmallGPUField, "ScalarGPUField" );
+
+   testScalarField( blocks, scalarGPUFieldId );
+
+   testScalarFieldPullReduction( blocks, scalarGPUFieldId );
+
+   return 0;
+
+}
+
+} // namespace walberla
+
+int main( int argc, char* argv[] ) {
+   return walberla::main( argc, argv );
+}
\ No newline at end of file
diff --git a/tests/cuda/codegen/GeneratedFieldPackInfoTestGPU.py b/tests/cuda/codegen/GeneratedFieldPackInfoTestGPU.py
new file mode 100644
index 0000000000000000000000000000000000000000..da0cd947461a7b9f3d65af04eec9be987df536f0
--- /dev/null
+++ b/tests/cuda/codegen/GeneratedFieldPackInfoTestGPU.py
@@ -0,0 +1,14 @@
+import operator as op
+import pystencils as ps
+from pystencils_walberla import CodeGeneration, generate_pack_info_for_field
+
+with CodeGeneration() as ctx:
+
+    layout = 'fzyx'
+
+    field = ps.fields("field: int32[3D]", layout=layout)
+
+    # communication
+    generate_pack_info_for_field(ctx, 'ScalarFieldCommunicationGPU', field, target='gpu')
+    generate_pack_info_for_field(ctx, 'ScalarFieldPullReductionGPU', field, target='gpu', operator=op.add,
+                                 gl_to_inner=True)
diff --git a/tests/field/CMakeLists.txt b/tests/field/CMakeLists.txt
index 992dc42982244a84bf0f979d543dfdcd17ce54e3..199e0180199dcf6a7c75ab06dfa76f62bc614cb5 100644
--- a/tests/field/CMakeLists.txt
+++ b/tests/field/CMakeLists.txt
@@ -77,4 +77,11 @@ waLBerla_generate_target_from_python(NAME CodeGenMultipleFieldSwaps FILE codegen
         OUT_FILES MultipleFieldSwaps.cpp MultipleFieldSwaps.h )
 waLBerla_compile_test( FILES codegen/MultipleFieldSwaps.cpp DEPENDS gui timeloop CodeGenMultipleFieldSwaps)
 waLBerla_execute_test( NAME MultipleFieldSwaps )
+
+waLBerla_generate_target_from_python(NAME CodegenGeneratedCPUFieldPackInfo FILE codegen/GeneratedFieldPackInfoTest.py
+        OUT_FILES ScalarFieldCommunication.cpp ScalarFieldCommunication.h
+                  ScalarFieldPullReduction.cpp ScalarFieldPullReduction.h )
+waLBerla_compile_test( FILES codegen/GeneratedFieldPackInfoTest.cpp
+        DEPENDS blockforest core field CodegenGeneratedCPUFieldPackInfo )
+waLBerla_execute_test( NAME GeneratedFieldPackInfoTest )
 endif()
diff --git a/tests/field/codegen/GeneratedFieldPackInfoTest.cpp b/tests/field/codegen/GeneratedFieldPackInfoTest.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2f8031064f6b6d1b733727f22726aab99057e390
--- /dev/null
+++ b/tests/field/codegen/GeneratedFieldPackInfoTest.cpp
@@ -0,0 +1,197 @@
+//======================================================================================================================
+//
+//  This file is part of waLBerla. waLBerla is free software: you can 
+//  redistribute it and/or modify it under the terms of the GNU General Public
+//  License as published by the Free Software Foundation, either version 3 of 
+//  the License, or (at your option) any later version.
+//  
+//  waLBerla is distributed in the hope that it will be useful, but WITHOUT 
+//  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 
+//  FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License 
+//  for more details.
+//  
+//  You should have received a copy of the GNU General Public License along
+//  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
+//
+//! \file GeneratedFieldPackInfoTest.cpp
+//! \ingroup field
+//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//! \author Helen Schottenhamml <helen.schottenhamml@fau.de>
+//! \brief Tests if a Field is correctly packed into buffers using generated pack info
+//
+//======================================================================================================================
+
+#include "field/AddToStorage.h"
+#include "field/GhostLayerField.h"
+
+#include "blockforest/Initialization.h"
+
+#include "core/debug/TestSubsystem.h"
+#include "core/Environment.h"
+
+#include "field/communication/PackInfo.h"
+#include "field/communication/UniformPullReductionPackInfo.h"
+
+#include <cstring>
+
+// include generated files
+#include "ScalarFieldCommunication.h"
+#include "ScalarFieldPullReduction.h"
+
+
+namespace walberla {
+
+void testScalarField( IBlock * block, BlockDataID fieldId )
+{
+   GhostLayerField<int,1> & field = *(block->getData<GhostLayerField<int,1> > (fieldId));
+   field.setWithGhostLayer( 0 );
+
+   WALBERLA_CHECK_EQUAL(field.xSize(), 2);
+   WALBERLA_CHECK_EQUAL(field.ySize(), 2);
+   WALBERLA_CHECK_EQUAL(field.zSize(), 2);
+
+   // initialize the bottom boundary
+   field(0,0,0) = 1;
+   field(0,1,0) = 2;
+   field(1,0,0) = 3;
+   field(1,1,0) = 4;
+
+   // -------------- Local Communication Test ----------------------
+
+   // communicate periodic from bottom to top
+   pystencils::ScalarFieldCommunication pi(fieldId);
+   pi.communicateLocal( block, block, stencil::B );
+
+   WALBERLA_CHECK_EQUAL ( field(0,0,+2), 1 );
+   WALBERLA_CHECK_EQUAL ( field(0,1,+2), 2 );
+   WALBERLA_CHECK_EQUAL ( field(1,0,+2), 3 );
+   WALBERLA_CHECK_EQUAL ( field(1,1,+2), 4 );
+
+   // -------------- Buffer Communication Test ---------------------
+
+   // Reset
+   field(0,0,2) = 0;
+   field(0,1,2) = 0;
+   field(1,0,2) = 0;
+   field(1,1,2) = 0;
+
+   mpi::GenericSendBuffer<> sendBuf;
+   pi.packData( block, stencil::B, sendBuf );
+
+   // Manually copy over the send to the receive buffer
+   mpi::GenericRecvBuffer<> recvBuf;
+   recvBuf.resize( sendBuf.size() );
+   memcpy( recvBuf.ptr(), sendBuf.ptr(), sendBuf.size()* sizeof(mpi::GenericSendBuffer<>::ElementType) );
+
+   pi.unpackData( block, stencil::T, recvBuf );
+
+   WALBERLA_CHECK_EQUAL ( field(0,0,+2), 1 );
+   WALBERLA_CHECK_EQUAL ( field(0,1,+2), 2 );
+   WALBERLA_CHECK_EQUAL ( field(1,0,+2), 3 );
+   WALBERLA_CHECK_EQUAL ( field(1,1,+2), 4 );
+}
+
+void testScalarFieldPullReduction( IBlock * block, BlockDataID fieldId )
+{
+   GhostLayerField<int,1> & field = *(block->getData<GhostLayerField<int,1> > (fieldId));
+   field.setWithGhostLayer( 0 );
+
+   WALBERLA_CHECK_EQUAL(field.xSize(), 2);
+   WALBERLA_CHECK_EQUAL(field.ySize(), 2);
+   WALBERLA_CHECK_EQUAL(field.zSize(), 2);
+
+   // initialize the bottom ghost layer cells
+   field(0,0,-1) = 1;
+   field(0,1,-1) = 2;
+   field(1,0,-1) = 3;
+   field(1,1,-1) = 4;
+
+   // initialize the top interior cells
+   field(0,0,1) = 1;
+   field(0,1,1) = 1;
+   field(1,0,1) = 1;
+   field(1,1,1) = 1;
+
+   // communicate periodic from bottom to top with uniform pull scheme
+   pystencils::ScalarFieldPullReduction pi1(fieldId);
+   pi1.communicateLocal( block, block, stencil::B );
+
+   // check values in top ghost layer
+   WALBERLA_CHECK_EQUAL ( field(0,0,2), 0 );
+   WALBERLA_CHECK_EQUAL ( field(0,1,2), 0 );
+   WALBERLA_CHECK_EQUAL ( field(1,0,2), 0 );
+   WALBERLA_CHECK_EQUAL ( field(1,1,2), 0 );
+
+   // check values in top interior cells
+   WALBERLA_CHECK_EQUAL ( field(0,0,1), 2 );
+   WALBERLA_CHECK_EQUAL ( field(0,1,1), 3 );
+   WALBERLA_CHECK_EQUAL ( field(1,0,1), 4 );
+   WALBERLA_CHECK_EQUAL ( field(1,1,1), 5 );
+
+   // communicate periodic from top to bottom with standard form to sync ghost layers
+   pystencils::ScalarFieldCommunication pi2 (fieldId);
+   pi2.communicateLocal( block, block, stencil::T );
+
+   // check values in bottom ghost layer
+   WALBERLA_CHECK_EQUAL ( field(0,0,-1), 2 );
+   WALBERLA_CHECK_EQUAL ( field(0,1,-1), 3 );
+   WALBERLA_CHECK_EQUAL ( field(1,0,-1), 4 );
+   WALBERLA_CHECK_EQUAL ( field(1,1,-1), 5 );
+
+   // check values in top interior cells
+   WALBERLA_CHECK_EQUAL ( field(0,0,1), 2 );
+   WALBERLA_CHECK_EQUAL ( field(0,1,1), 3 );
+   WALBERLA_CHECK_EQUAL ( field(1,0,1), 4 );
+   WALBERLA_CHECK_EQUAL ( field(1,1,1), 5 );
+
+}
+
+int main(int argc, char **argv)
+{
+   using blockforest::createUniformBlockGrid;
+
+   debug::enterTestMode();
+   Environment walberlaEnv(argc,argv);
+
+
+   // Create a BlockForest with 2x2x2 cells per block
+   uint_t processes = uint_c( MPIManager::instance()->numProcesses() );
+   auto blocks = createUniformBlockGrid(processes,1 ,1, //blocks
+                                        2,2,2,          //cells
+                                        1,              //dx
+                                        false,          //one block per process
+                                        true,true,true);//periodicity
+
+   // Create a Field with the same number of cells as the block
+   BlockDataID scalarFieldId = field::addToStorage<GhostLayerField<int,1> > ( blocks, "ScalarField" );
+
+   for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt ) // block loop
+      testScalarField( &(*blockIt), scalarFieldId );
+
+   // Create a BlockForest with 8x8x8 cells per block
+   blocks = createUniformBlockGrid(processes,1 ,1, //blocks
+                                   8,8,8,          //cells
+                                   1,              //dx
+                                   false,          //one block per process
+                                   true,true,true);//periodicity
+
+   // Create a Field with one quarter as many cells per dimension, i.e. a field with the same size as the one above
+   auto getSize = []( const shared_ptr< StructuredBlockStorage > &, IBlock * const ) {
+     return Vector3<uint_t>(2,2,2);
+   };
+   scalarFieldId = field::addToStorage<GhostLayerField<int,1> > ( blocks, "ScalarField", getSize );
+
+   for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt ) // block loop
+      testScalarField( &(*blockIt), scalarFieldId );
+
+   for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt ) // block loop
+      testScalarFieldPullReduction( &(*blockIt), scalarFieldId );
+
+   return 0;
+}
+} // namespace walberla
+
+int main( int argc, char* argv[] ) {
+   return walberla::main( argc, argv );
+}
\ No newline at end of file
diff --git a/tests/field/codegen/GeneratedFieldPackInfoTest.py b/tests/field/codegen/GeneratedFieldPackInfoTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..f63151b6e47c7225a929707a8723d567814859bd
--- /dev/null
+++ b/tests/field/codegen/GeneratedFieldPackInfoTest.py
@@ -0,0 +1,14 @@
+import operator as op
+import pystencils as ps
+from pystencils_walberla import CodeGeneration, generate_pack_info_for_field
+
+with CodeGeneration() as ctx:
+
+    layout = 'fzyx'
+
+    field = ps.fields("field: int32[3D]", layout=layout)
+
+    # communication
+    generate_pack_info_for_field(ctx, 'ScalarFieldCommunication', field, target='cpu')
+    generate_pack_info_for_field(ctx, 'ScalarFieldPullReduction', field, target='cpu', operator=op.add,
+                                 gl_to_inner=True)