diff --git a/.clang-tidy b/.clang-tidy
index 935443d1ec5ae2344604710b3070a1a4abd3812a..7127535c8e66a377978897492b26cb954201dc20 100644
--- a/.clang-tidy
+++ b/.clang-tidy
@@ -11,6 +11,7 @@ bugprone-*,
 
 misc-*,
 -misc-misplaced-const,
+-misc-no-recursion,
 -misc-non-private-member-variables-in-classes,
 
 modernize-*,
@@ -25,8 +26,11 @@ modernize-*,
 -modernize-redundant-void-arg,
 -modernize-use-trailing-return-type,
 -modernize-avoid-c-arrays,
+-modernize-concat-nested-namespaces,
+-modernize-use-nodiscard,
 
 mpi-*,
+-mpi-type-mismatch,
 
 openmp-*,
 -openmp-exception-escape,
@@ -43,21 +47,24 @@ readability-deleted-default,
 readability-isolate-declaration,
 readability-misleading-indentation,
 readability-misplaced-array-index,
+readability-non-const-parameter,
+readability-redundant-access-specifiers,
 readability-redundant-control-flow,
 readability-redundant-declaration,
 readability-redundant-function-ptr-dereference,
 readability-redundant-preprocessor,
 readability-redundant-smartptr-get,
 readability-redundant-string-cstr,
+readability-simplify-boolean-expr,
 readability-simplify-subscript-expr,
 readability-static-accessed-through-instance,
 readability-static-definition-in-anonymous-namespace,
 readability-string-compare,
-readability-uniqueptr-delete-release
+readability-uniqueptr-delete-release,
+readability-use-anyofallof
 
 '
 WarningsAsErrors: '*'
-HeaderFilterRegex: ''
-AnalyzeTemporaryDtors: false
+HeaderFilterRegex: '.*'
 ...
 
diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 1cda00fce54beca02ff95af8ae262a26c3142533..3ce52d2249ec97905f23ff9faa3eee23e332e5c9 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -7,7 +7,6 @@
 stages:
    - pretest
    - test
-   - merge_request
    - deploy
    - benchmark
 
@@ -33,10 +32,30 @@ stages:
       - mkdir $CI_PROJECT_DIR/build
       - cd $CI_PROJECT_DIR/build
       - if dpkg --compare-versions `ompi_info | head -2 | tail -1 | sed 's/[^0-9.]*\([0-9.]*\).*/\1/'` ge 1.10; then export MPIEXEC_PREFLAGS="--allow-run-as-root" ; fi
-      - cmake .. -DWALBERLA_BUFFER_DEBUG=$WALBERLA_BUFFER_DEBUG -DWALBERLA_BUILD_TESTS=ON -DWALBERLA_BUILD_BENCHMARKS=ON -DWALBERLA_BUILD_TUTORIALS=ON -DWALBERLA_BUILD_TOOLS=ON -DWALBERLA_BUILD_WITH_MPI=$WALBERLA_BUILD_WITH_MPI -DWALBERLA_BUILD_WITH_CUDA=$WALBERLA_BUILD_WITH_CUDA -DWALBERLA_BUILD_WITH_PYTHON=$WALBERLA_BUILD_WITH_PYTHON -DWALBERLA_BUILD_WITH_OPENMP=$WALBERLA_BUILD_WITH_OPENMP -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE -DMPIEXEC_PREFLAGS=$MPIEXEC_PREFLAGS -DWALBERLA_DOUBLE_ACCURACY=$WALBERLA_DOUBLE_ACCURACY -DWARNING_ERROR=ON -DWALBERLA_BUILD_WITH_METIS=$WALBERLA_BUILD_WITH_METIS -DWALBERLA_BUILD_WITH_PARMETIS=$WALBERLA_BUILD_WITH_PARMETIS -DWALBERLA_ENABLE_GUI=$WALBERLA_ENABLE_GUI -DWALBERLA_BUILD_WITH_CODEGEN=$WALBERLA_BUILD_WITH_CODEGEN
-      - cmake . -LAH
+      - cmake ..
+        -DWALBERLA_BUFFER_DEBUG=$WALBERLA_BUFFER_DEBUG
+        -DWALBERLA_BUILD_TESTS=ON
+        -DWALBERLA_BUILD_BENCHMARKS=ON
+        -DWALBERLA_BUILD_TUTORIALS=ON
+        -DWALBERLA_BUILD_TOOLS=ON
+        -DWALBERLA_BUILD_SHOWCASES=ON
+        -DWALBERLA_BUILD_WITH_MPI=$WALBERLA_BUILD_WITH_MPI
+        -DWALBERLA_BUILD_WITH_CUDA=$WALBERLA_BUILD_WITH_CUDA
+        -DWALBERLA_BUILD_WITH_PYTHON=$WALBERLA_BUILD_WITH_PYTHON
+        -DWALBERLA_BUILD_WITH_OPENMP=$WALBERLA_BUILD_WITH_OPENMP
+        -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE -DMPIEXEC_PREFLAGS=$MPIEXEC_PREFLAGS
+        -DWALBERLA_DOUBLE_ACCURACY=$WALBERLA_DOUBLE_ACCURACY
+        -DWARNING_ERROR=ON
+        -DWALBERLA_BUILD_WITH_METIS=$WALBERLA_BUILD_WITH_METIS
+        -DWALBERLA_BUILD_WITH_PARMETIS=$WALBERLA_BUILD_WITH_PARMETIS
+        -DWALBERLA_ENABLE_GUI=$WALBERLA_ENABLE_GUI
+        -DWALBERLA_BUILD_WITH_CODEGEN=$WALBERLA_BUILD_WITH_CODEGEN
+        -DWALBERLA_STL_BOUNDS_CHECKS=$WALBERLA_STL_BOUNDS_CHECKS
+      - cmake . -LA
       - make -j $NUM_BUILD_CORES -l $NUM_CORES
-      - ctest -LE $CTEST_EXCLUDE_LABELS -C $CMAKE_BUILD_TYPE --output-on-failure -j $NUM_CORES
+      - ctest -LE $CTEST_EXCLUDE_LABELS -C $CMAKE_BUILD_TYPE --output-on-failure -j $NUM_CORES -T Test
+   after_script:
+      - python3 cmake/ctest2junit.py build > report.xml
    tags:
       - docker
    variables:
@@ -51,6 +70,12 @@ stages:
       WALBERLA_BUILD_WITH_METIS: "ON"
       WALBERLA_BUILD_WITH_PARMETIS: "ON"
       WALBERLA_ENABLE_GUI: "OFF"
+   artifacts:
+      when: always
+      reports:
+         junit:
+            - report.xml
+            - python/report.xml
 
 
 ###############################################################################
@@ -61,202 +86,28 @@ stages:
 
 
 
-intel_18_serial:
-   extends: .build_template
-   image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:18
-   before_script:
-      - pip3 install lbmpy jinja2
-      - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
-      - cd ..
-   variables:
-      WALBERLA_BUILD_WITH_CUDA: "ON"
-      WALBERLA_BUILD_WITH_MPI: "OFF"
-      WALBERLA_BUILD_WITH_OPENMP: "OFF"
-      WALBERLA_BUILD_WITH_PARMETIS: "OFF"
-      WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   only:
-      variables:
-         - $ENABLE_NIGHTLY_BUILDS
-   
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
-   tags:
-      - cuda
-      - docker
-      - intel
-
-intel_18_mpionly:
-   extends: .build_template
-   image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:18
-   before_script:
-      - pip3 install lbmpy jinja2
-      - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
-      - cd ..
-   variables:
-      WALBERLA_BUILD_WITH_CUDA: "ON"
-      WALBERLA_BUILD_WITH_OPENMP: "OFF"
-      WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   only:
-      variables:
-         - $ENABLE_NIGHTLY_BUILDS
-   
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
-   tags:
-      - cuda
-      - docker
-      - intel
-
-intel_18_hybrid:
-   extends: .build_template
-   image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:18
-   before_script:
-      - pip3 install lbmpy jinja2
-      - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
-      - cd ..
-   variables:
-      WALBERLA_BUILD_WITH_CUDA: "ON"
-      WALBERLA_ENABLE_GUI: "ON"
-      WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
-   tags:
-      - cuda
-      - docker
-      - intel
-
-intel_18_serial_dbg:
-   extends: .build_template
-   image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:18
-   before_script:
-      - pip3 install lbmpy jinja2
-      - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
-      - cd ..
-   variables:
-      WALBERLA_BUILD_WITH_CUDA: "ON"
-      WALBERLA_BUILD_WITH_MPI: "OFF"
-      WALBERLA_BUILD_WITH_OPENMP: "OFF"
-      WALBERLA_BUILD_WITH_PARMETIS: "OFF"
-      CMAKE_BUILD_TYPE: "DebugOptimized"
-      WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
-   tags:
-      - cuda
-      - docker
-      - intel
-
-intel_18_mpionly_dbg:
-   extends: .build_template
-   image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:18
-   before_script:
-      - pip3 install lbmpy jinja2
-      - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
-      - cd ..
-   variables:
-      WALBERLA_BUILD_WITH_CUDA: "ON"
-      CMAKE_BUILD_TYPE: "DebugOptimized"
-      WALBERLA_BUILD_WITH_OPENMP: "OFF"
-      WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   only:
-      variables:
-         - $ENABLE_NIGHTLY_BUILDS
-   
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
-   tags:
-      - cuda
-      - docker
-      - intel
-
-intel_18_hybrid_dbg:
-   extends: .build_template
-   image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:18
-   before_script:
-      - pip3 install lbmpy jinja2
-      - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
-      - cd ..
-   variables:
-      WALBERLA_BUILD_WITH_CUDA: "ON"
-      CMAKE_BUILD_TYPE: "DebugOptimized"
-      WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
-   tags:
-      - cuda
-      - docker
-      - intel
-
-intel_18_hybrid_dbg_sp:
-   extends: .build_template
-   image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:18
-   variables:
-      WALBERLA_BUILD_WITH_CUDA: "ON"
-      CMAKE_BUILD_TYPE: "DebugOptimized"
-      WALBERLA_DOUBLE_ACCURACY: "OFF"
-      WALBERLA_BUILD_WITH_PARMETIS: "OFF"
-      WALBERLA_BUILD_WITH_METIS: "OFF"
-   only:
-      variables:
-         - $ENABLE_NIGHTLY_BUILDS
-   
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
-   tags:
-      - cuda
-      - docker
-      - intel
-
 intel_19_serial:
    extends: .build_template
    image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:19
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
       - intel
 
@@ -266,22 +117,20 @@ intel_19_mpionly:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
       - intel
 
@@ -291,17 +140,15 @@ intel_19_hybrid:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
+      - cuda
       - docker
       - intel
 
@@ -311,21 +158,19 @@ intel_19_serial_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
+      - cuda
       - docker
       - intel
 
@@ -335,19 +180,17 @@ intel_19_mpionly_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
+      - cuda
       - docker
       - intel
 
@@ -357,14 +200,16 @@ intel_19_hybrid_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
+      - cuda
       - docker
       - intel
 
@@ -372,16 +217,13 @@ intel_19_hybrid_dbg_sp:
    extends: .build_template
    image: i10git.cs.fau.de:5005/walberla/buildenvs/intel:19
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_DOUBLE_ACCURACY: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_METIS: "OFF"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
       - intel
 
@@ -391,23 +233,20 @@ gcc_7_serial:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
       WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - cuda
       - docker
@@ -418,21 +257,18 @@ gcc_7_mpionly:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
       WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - cuda
       - docker
@@ -443,16 +279,17 @@ gcc_7_hybrid:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
       WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
+   only:
+      variables:
+         - $ENABLE_NIGHTLY_BUILDS
+   
    tags:
       - cuda
       - docker
@@ -463,9 +300,9 @@ gcc_7_serial_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
       WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
@@ -474,10 +311,11 @@ gcc_7_serial_dbg:
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_ENABLE_GUI: "ON"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
+   only:
+      variables:
+         - $ENABLE_NIGHTLY_BUILDS
+   
    tags:
       - cuda
       - docker
@@ -488,22 +326,19 @@ gcc_7_mpionly_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
       WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - cuda
       - docker
@@ -514,17 +349,14 @@ gcc_7_hybrid_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
       WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
       - cuda
       - docker
@@ -542,10 +374,6 @@ gcc_7_hybrid_dbg_sp:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - cuda
       - docker
@@ -556,24 +384,22 @@ gcc_8_serial:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 gcc_8_mpionly:
@@ -582,22 +408,20 @@ gcc_8_mpionly:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 gcc_8_hybrid:
@@ -606,21 +430,19 @@ gcc_8_hybrid:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 gcc_8_serial_dbg:
@@ -629,25 +451,23 @@ gcc_8_serial_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 gcc_8_mpionly_dbg:
@@ -656,23 +476,21 @@ gcc_8_mpionly_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 gcc_8_hybrid_dbg:
@@ -681,29 +499,27 @@ gcc_8_hybrid_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 gcc_8_hybrid_dbg_sp:
    extends: .build_template
    image: i10git.cs.fau.de:5005/walberla/buildenvs/gcc:8
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_DOUBLE_ACCURACY: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
@@ -712,11 +528,8 @@ gcc_8_hybrid_dbg_sp:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 gcc_9_serial:
@@ -725,8 +538,7 @@ gcc_9_serial:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
    variables:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
@@ -734,14 +546,11 @@ gcc_9_serial:
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - docker
 
@@ -751,21 +560,17 @@ gcc_9_mpionly:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
    variables:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - docker
 
@@ -775,20 +580,16 @@ gcc_9_hybrid:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
    variables:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - docker
 
@@ -798,8 +599,7 @@ gcc_9_serial_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
    variables:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
@@ -808,14 +608,11 @@ gcc_9_serial_dbg:
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - docker
 
@@ -825,22 +622,18 @@ gcc_9_mpionly_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
    variables:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - docker
 
@@ -850,21 +643,17 @@ gcc_9_hybrid_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
    variables:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - docker
 
@@ -881,10 +670,6 @@ gcc_9_hybrid_dbg_sp:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - docker
 
@@ -894,8 +679,7 @@ gcc_10_serial:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
    variables:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
@@ -903,14 +687,11 @@ gcc_10_serial:
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - docker
 
@@ -920,21 +701,17 @@ gcc_10_mpionly:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
    variables:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - docker
 
@@ -945,12 +722,12 @@ gcc_10_hybrid:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
    variables:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
       - docker
 
@@ -960,8 +737,7 @@ gcc_10_serial_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
    variables:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
@@ -970,6 +746,7 @@ gcc_10_serial_dbg:
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
       - docker
 
@@ -979,18 +756,14 @@ gcc_10_mpionly_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
    variables:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
       - docker
 
@@ -1000,17 +773,13 @@ gcc_10_hybrid_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
    variables:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
       - docker
 
@@ -1023,10 +792,6 @@ gcc_10_hybrid_dbg_sp:
       WALBERLA_DOUBLE_ACCURACY: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_METIS: "OFF"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - docker
 
@@ -1036,23 +801,20 @@ clang_6.0_serial:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
       WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - cuda
       - docker
@@ -1063,21 +825,18 @@ clang_6.0_mpionly:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
       WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - cuda
       - docker
@@ -1088,20 +847,17 @@ clang_6.0_hybrid:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
       WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - cuda
       - docker
@@ -1112,9 +868,9 @@ clang_6.0_serial_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
       WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
@@ -1122,14 +878,11 @@ clang_6.0_serial_dbg:
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - cuda
       - docker
@@ -1140,22 +893,19 @@ clang_6.0_mpionly_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
       WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - cuda
       - docker
@@ -1166,17 +916,14 @@ clang_6.0_hybrid_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
       WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
       - cuda
       - docker
@@ -1194,10 +941,6 @@ clang_6.0_hybrid_dbg_sp:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - cuda
       - docker
@@ -1208,24 +951,22 @@ clang_7.0_serial:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_7.0_mpionly:
@@ -1234,22 +975,20 @@ clang_7.0_mpionly:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_7.0_hybrid:
@@ -1258,21 +997,19 @@ clang_7.0_hybrid:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_7.0_serial_dbg:
@@ -1281,25 +1018,23 @@ clang_7.0_serial_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_7.0_mpionly_dbg:
@@ -1308,23 +1043,21 @@ clang_7.0_mpionly_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_7.0_hybrid_dbg:
@@ -1333,29 +1066,27 @@ clang_7.0_hybrid_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_7.0_hybrid_dbg_sp:
    extends: .build_template
    image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:7.0
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_DOUBLE_ACCURACY: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
@@ -1364,11 +1095,8 @@ clang_7.0_hybrid_dbg_sp:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_8.0_serial:
@@ -1377,24 +1105,22 @@ clang_8.0_serial:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_8.0_mpionly:
@@ -1403,22 +1129,20 @@ clang_8.0_mpionly:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_8.0_hybrid:
@@ -1427,21 +1151,19 @@ clang_8.0_hybrid:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_8.0_serial_dbg:
@@ -1450,25 +1172,23 @@ clang_8.0_serial_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_8.0_mpionly_dbg:
@@ -1477,23 +1197,21 @@ clang_8.0_mpionly_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_8.0_hybrid_dbg:
@@ -1502,29 +1220,27 @@ clang_8.0_hybrid_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_8.0_hybrid_dbg_sp:
    extends: .build_template
    image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:8.0
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_DOUBLE_ACCURACY: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
@@ -1533,11 +1249,8 @@ clang_8.0_hybrid_dbg_sp:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_9.0_serial:
@@ -1546,24 +1259,22 @@ clang_9.0_serial:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_9.0_mpionly:
@@ -1572,22 +1283,20 @@ clang_9.0_mpionly:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_9.0_hybrid:
@@ -1596,21 +1305,19 @@ clang_9.0_hybrid:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_9.0_serial_dbg:
@@ -1619,25 +1326,23 @@ clang_9.0_serial_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_9.0_mpionly_dbg:
@@ -1646,23 +1351,21 @@ clang_9.0_mpionly_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_9.0_hybrid_dbg:
@@ -1671,29 +1374,27 @@ clang_9.0_hybrid_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_9.0_hybrid_dbg_sp:
    extends: .build_template
    image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:9.0
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_DOUBLE_ACCURACY: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
@@ -1702,11 +1403,8 @@ clang_9.0_hybrid_dbg_sp:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_10.0_serial:
@@ -1715,24 +1413,22 @@ clang_10.0_serial:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_10.0_mpionly:
@@ -1741,22 +1437,20 @@ clang_10.0_mpionly:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    only:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
+      - cuda
       - docker
 
 clang_10.0_hybrid:
@@ -1765,17 +1459,15 @@ clang_10.0_hybrid:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
+      - cuda
       - docker
 
 clang_10.0_serial_dbg:
@@ -1784,21 +1476,19 @@ clang_10.0_serial_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
+      - cuda
       - docker
 
 clang_10.0_mpionly_dbg:
@@ -1807,15 +1497,17 @@ clang_10.0_mpionly_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
+      - cuda
       - docker
 
 clang_10.0_hybrid_dbg:
@@ -1824,18 +1516,16 @@ clang_10.0_hybrid_dbg:
    before_script:
       - pip3 install lbmpy jinja2
       - cd python
-      - python3 -m unittest discover pystencils_walberla/
-      - python3 -m unittest discover lbmpy_walberla/
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
       - cd ..
+      - CC=gcc CXX=g++ pip3 install pycuda
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_BUILD_WITH_CODEGEN: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_PYTHON: "ON"
    tags:
+      - cuda
       - docker
 
 clang_10.0_hybrid_dbg_sp:
@@ -1843,12 +1533,13 @@ clang_10.0_hybrid_dbg_sp:
    image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:10.0
    stage: pretest
    variables:
-      WALBERLA_BUILD_WITH_CUDA: "OFF"
+      WALBERLA_BUILD_WITH_CUDA: "ON"
       CMAKE_BUILD_TYPE: "DebugOptimized"
       WALBERLA_DOUBLE_ACCURACY: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_METIS: "OFF"
    tags:
+      - cuda
       - docker
 
 
@@ -1865,10 +1556,6 @@ gcc_8_hybrid_dbg_noboost:
       WALBERLA_BUILD_WITH_PYTHON: "OFF"
    tags:
       - docker
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 
@@ -1878,9 +1565,9 @@ gcc_8_hybrid_dbg_noboost:
 ##                                                                           ##
 ###############################################################################
 
-gcc_9_stl_debug:
+gcc_10_stl_debug:
    extends: .build_template
-   image: i10git.cs.fau.de:5005/walberla/buildenvs/gcc:9
+   image: i10git.cs.fau.de:5005/walberla/buildenvs/gcc-debug-stl:10
    before_script:
       - rm -rf /opt/boost /usr/include/boost
    variables:
@@ -1896,9 +1583,7 @@ gcc_9_stl_debug:
       WALBERLA_BUILD_WITH_METIS: "OFF"
       WALBERLA_BUILD_WITH_PARMETIS: "OFF"
       WALBERLA_BUILD_WITH_PYTHON: "OFF"
-   only:
-      variables:
-         - $ENABLE_NIGHTLY_BUILDS
+      WALBERLA_STL_BOUNDS_CHECKS: "ON"
    tags:
       - docker
 
@@ -1918,12 +1603,8 @@ doc:
       - mkdir $CI_PROJECT_DIR/build
       - cd $CI_PROJECT_DIR/build
       - cmake ..
-      - cmake . -LAH
+      - cmake . -LA
       - make doc
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    tags:
       - docker
    artifacts:
@@ -1940,7 +1621,7 @@ doc:
 ###############################################################################
 
 clang-tidy:
-   image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:10.0
+   image: i10git.cs.fau.de:5005/walberla/buildenvs/clang:11.0
    script:
       - $CXX --version
       - clang-tidy -version
@@ -1948,13 +1629,9 @@ clang-tidy:
       - mkdir $CI_PROJECT_DIR/build
       - cd $CI_PROJECT_DIR/build
       - cmake .. -DCMAKE_EXPORT_COMPILE_COMMANDS=ON -DWALBERLA_BUFFER_DEBUG=ON -DWALBERLA_BUILD_TESTS=ON -DWALBERLA_BUILD_BENCHMARKS=ON -DWALBERLA_BUILD_TUTORIALS=ON -DWALBERLA_BUILD_TOOLS=ON -DWALBERLA_BUILD_WITH_MPI=ON -DWALBERLA_BUILD_WITH_OPENMP=ON -DCMAKE_BUILD_TYPE=Debug -DWALBERLA_BUILD_WITH_METIS=ON -DWALBERLA_BUILD_WITH_PARMETIS=ON -DWALBERLA_BUILD_WITH_OPENMESH=ON -DWALBERLA_DOUBLE_ACCURACY=ON
-      - cmake . -LAH
+      - cmake . -LA
       - utilities/filterCompileCommands.py compile_commands.json
       - run-clang-tidy.py -quiet | tee clang-tidy-output.txt
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
    artifacts:
       paths:
          - $CI_PROJECT_DIR/build/clang-tidy-output.txt
@@ -1966,7 +1643,7 @@ cppcheck:
    image: i10git.cs.fau.de:5005/walberla/buildenvs/cppcheck
    script:
       - cppcheck --version
-      - cppcheck . --max-configs=10 --enable=warning --enable=style --enable=performance --enable=portability -i src/gui/extern -i src/geometry/structured/extern -i sqlite3.c -i StackWalker.cpp -I src/ -I tests/ -I apps/ -D WALBERLA_BUILD_WITH_MPI -D WALBERLA_BUILD_WITH_METIS -D WALBERLA_BUILD_WITH_BOOST_THREAD -D WALBERLA_BUILD_WITH_PYTHON --xml 2> report.xml
+      - cppcheck . --max-configs=10 --enable=warning --enable=style --enable=performance --enable=portability -i src/gui/extern -i src/geometry/structured/extern -i sqlite3.c -i StackWalker.cpp -I src/ -I tests/ -I apps/ -D WALBERLA_BUILD_WITH_MPI -D WALBERLA_BUILD_WITH_METIS -D WALBERLA_BUILD_WITH_PYTHON --xml 2> report.xml
       - cppcheck-htmlreport --file=report.xml --report-dir=html_report --source-dir=.
    artifacts:
       untracked: true
@@ -1990,7 +1667,7 @@ coverage:
       - cd build
       - if dpkg --compare-versions `ompi_info | head -2 | tail -1 | sed 's/[^0-9.]*\([0-9.]*\).*/\1/'` ge 1.10; then export MPIEXEC_PREFLAGS="--allow-run-as-root" ; fi
       - cmake .. -DWALBERLA_BUILD_TESTS=ON -DWALBERLA_BUILD_BENCHMARKS=ON -DWALBERLA_BUILD_TUTORIALS=ON -DWALBERLA_BUILD_WITH_MPI=ON -DWALBERLA_BUILD_WITH_OPENMP=OFF -DCMAKE_BUILD_TYPE=Debug -DMPIEXEC_PREFLAGS=$MPIEXEC_PREFLAGS -DWALBERLA_BUILD_WITH_CODEGEN=OFF -DWALBERLA_BUILD_WITH_GCOV=ON
-      - cmake . -LAH
+      - cmake . -LA
       - make -j $NUM_BUILD_CORES -l $NUM_CORES
       - ctest -LE longrun --output-on-failure -j $NUM_CORES --timeout 3000
       - cd ..
@@ -2027,9 +1704,11 @@ coverage:
       - cmake --version
       - mkdir build
       - cd build
-      - cmake -LAH -DWALBERLA_BUILD_TESTS=ON -DWALBERLA_BUILD_BENCHMARKS=ON -DWALBERLA_BUILD_TUTORIALS=ON -DWALBERLA_BUILD_WITH_MPI=$WALBERLA_BUILD_WITH_MPI -DWALBERLA_BUILD_WITH_OPENMP=$WALBERLA_BUILD_WITH_OPENMP -DWALBERLA_DOUBLE_ACCURACY=$WALBERLA_DOUBLE_ACCURACY -DWARNING_ERROR=ON -G "$CMAKE_GENERATOR" ..
+      - cmake -LA -DWALBERLA_BUILD_TESTS=ON -DWALBERLA_BUILD_BENCHMARKS=ON -DWALBERLA_BUILD_TUTORIALS=ON -DWALBERLA_BUILD_WITH_MPI=$WALBERLA_BUILD_WITH_MPI -DWALBERLA_BUILD_WITH_OPENMP=$WALBERLA_BUILD_WITH_OPENMP -DWALBERLA_DOUBLE_ACCURACY=$WALBERLA_DOUBLE_ACCURACY -DWARNING_ERROR=ON -G "$CMAKE_GENERATOR" -DCMAKE_DISABLE_FIND_PACKAGE_Boost=TRUE ..
       - MSBuild.exe walberla.sln /property:Configuration=$BUILD_CONFIGURATION /verbosity:minimal /maxcpucount:4
-      - ctest -LE $CTEST_EXCLUDE_LABELS -C $BUILD_CONFIGURATION --output-on-failure -j 4
+      - ctest -LE $CTEST_EXCLUDE_LABELS -C $BUILD_CONFIGURATION --output-on-failure -j 4 -T Test
+   after_script:
+      - python3 cmake/ctest2junit.py build > report.xml
    variables:
       CMAKE_GENERATOR: "Visual Studio 15 2017 Win64"
       BUILD_CONFIGURATION: "Release"
@@ -2037,6 +1716,12 @@ coverage:
       WALBERLA_BUILD_WITH_MPI: "ON"
       WALBERLA_BUILD_WITH_OPENMP: "ON"
       WALBERLA_DOUBLE_ACCURACY: "ON"
+   artifacts:
+      when: always
+      reports:
+         junit:
+            - report.xml
+            - python/report.xml
 
 
 
@@ -2050,10 +1735,6 @@ msvc-14.1_Hybrid_dbg:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 msvc-14.1_Hybrid_sp_dbg:
@@ -2067,10 +1748,6 @@ msvc-14.1_Hybrid_sp_dbg:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 msvc-14.1_Hybrid:
@@ -2082,10 +1759,6 @@ msvc-14.1_Hybrid:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 msvc-14.1_serial_dbg:
@@ -2100,10 +1773,6 @@ msvc-14.1_serial_dbg:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 msvc-14.1_serial:
@@ -2117,10 +1786,6 @@ msvc-14.1_serial:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 msvc-14.1_mpionly_dbg:
@@ -2134,10 +1799,6 @@ msvc-14.1_mpionly_dbg:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 msvc-14.1_mpionly:
@@ -2150,10 +1811,6 @@ msvc-14.1_mpionly:
       variables:
          - $ENABLE_NIGHTLY_BUILDS
    
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 msvc-14.2_Hybrid_dbg:
@@ -2171,10 +1828,6 @@ msvc-14.2_Hybrid_sp_dbg:
       CMAKE_GENERATOR: "Visual Studio 16 2019"
       BUILD_CONFIGURATION: "DebugOptimized"
       WALBERLA_DOUBLE_ACCURACY: "OFF"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 msvc-14.2_Hybrid:
@@ -2182,10 +1835,6 @@ msvc-14.2_Hybrid:
    variables:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
       CMAKE_GENERATOR: "Visual Studio 16 2019"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 msvc-14.2_serial_dbg:
@@ -2196,10 +1845,6 @@ msvc-14.2_serial_dbg:
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       BUILD_CONFIGURATION: "DebugOptimized"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 msvc-14.2_serial:
@@ -2209,10 +1854,6 @@ msvc-14.2_serial:
       CMAKE_GENERATOR: "Visual Studio 16 2019"
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 msvc-14.2_mpionly_dbg:
@@ -2222,10 +1863,6 @@ msvc-14.2_mpionly_dbg:
       CMAKE_GENERATOR: "Visual Studio 16 2019"
       BUILD_CONFIGURATION: "DebugOptimized"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 msvc-14.2_mpionly:
@@ -2234,10 +1871,6 @@ msvc-14.2_mpionly:
       WALBERLA_BUILD_WITH_CUDA: "OFF"
       CMAKE_GENERATOR: "Visual Studio 16 2019"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 
 
@@ -2258,12 +1891,25 @@ msvc-14.2_mpionly:
       - mpirun --version
       - mkdir build
       - cd build
-      - cmake .. -DWALBERLA_BUILD_TESTS=ON -DWALBERLA_BUILD_BENCHMARKS=ON -DWALBERLA_BUILD_TUTORIALS=ON -DWALBERLA_BUILD_TOOLS=ON -DWALBERLA_BUILD_WITH_MPI=$WALBERLA_BUILD_WITH_MPI -DWALBERLA_BUILD_WITH_PYTHON=$WALBERLA_BUILD_WITH_PYTHON -DWALBERLA_BUILD_WITH_OPENMP=$WALBERLA_BUILD_WITH_OPENMP -DWALBERLA_BUILD_WITH_CUDA=$WALBERLA_BUILD_WITH_CUDA -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE -DWARNING_ERROR=ON
-      - cmake . -LAH
+      - cmake .. -DWALBERLA_BUILD_TESTS=ON -DWALBERLA_BUILD_BENCHMARKS=ON -DWALBERLA_BUILD_TUTORIALS=ON -DWALBERLA_BUILD_TOOLS=ON -DWALBERLA_BUILD_WITH_MPI=$WALBERLA_BUILD_WITH_MPI -DWALBERLA_BUILD_WITH_PYTHON=$WALBERLA_BUILD_WITH_PYTHON -DWALBERLA_BUILD_WITH_CODEGEN=$WALBERLA_BUILD_WITH_CODEGEN -DWALBERLA_BUILD_WITH_OPENMP=$WALBERLA_BUILD_WITH_OPENMP -DWALBERLA_BUILD_WITH_CUDA=$WALBERLA_BUILD_WITH_CUDA -DCMAKE_BUILD_TYPE=$CMAKE_BUILD_TYPE -DWARNING_ERROR=ON
+      - cmake . -LA
       - make -j $NUM_BUILD_CORES -l $NUM_CORES
-      - ctest -LE "$CTEST_EXCLUDE_LABELS|cuda" -C $CMAKE_BUILD_TYPE --output-on-failure -j $NUM_CORES
+      - ctest -LE "$CTEST_EXCLUDE_LABELS|cuda" -C $CMAKE_BUILD_TYPE --output-on-failure -j $NUM_CORES -T Test
+   before_script:
+      - pip3 install --user lbmpy jinja2 pytest-cov lxml
+      - cd python
+      - python3 -m pytest --junitxml=report.xml pystencils_walberla lbmpy_walberla
+      - cd ..
+   after_script:
+      - python3 cmake/ctest2junit.py build > report.xml
    tags:
       - mac
+   artifacts:
+      when: always
+      reports:
+         junit:
+            - report.xml
+            - python/report.xml
 
 mac_Serial_Dbg:
    <<: *mac_build_definition
@@ -2273,10 +1919,7 @@ mac_Serial_Dbg:
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PYTHON: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_CODEGEN: "ON"
 
 mac_Serial:
    <<: *mac_build_definition
@@ -2286,10 +1929,7 @@ mac_Serial:
       WALBERLA_BUILD_WITH_MPI: "OFF"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PYTHON: "ON"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
+      WALBERLA_BUILD_WITH_CODEGEN: "ON"
 
 mac_MpiOnly_Dbg:
    <<: *mac_build_definition
@@ -2299,6 +1939,7 @@ mac_MpiOnly_Dbg:
       WALBERLA_BUILD_WITH_MPI: "ON"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PYTHON: "ON"
+      WALBERLA_BUILD_WITH_CODEGEN: "ON"
       OMPI_MCA_btl: "self,tcp"
 
 mac_MpiOnly:
@@ -2309,11 +1950,8 @@ mac_MpiOnly:
       WALBERLA_BUILD_WITH_MPI: "ON"
       WALBERLA_BUILD_WITH_OPENMP: "OFF"
       WALBERLA_BUILD_WITH_PYTHON: "ON"
+      WALBERLA_BUILD_WITH_CODEGEN: "ON"
       OMPI_MCA_btl: "self,tcp"
-   stage: merge_request
-   when: manual
-   needs: []
-   allow_failure: false
 
 ###############################################################################
 ##                                                                           ##
@@ -2332,8 +1970,6 @@ mac_MpiOnly:
       - anaconda logout
    dependencies: []
    when: manual
-   needs: []
-   allow_failure: false
    only:
       - master@walberla/walberla
       - tags@walberla/walberla
@@ -2391,7 +2027,7 @@ conda-py36-linux:
       - mkdir $CI_PROJECT_DIR/build
       - cd $CI_PROJECT_DIR/build
       - cmake .. -DWALBERLA_BUFFER_DEBUG=OFF -DWALBERLA_BUILD_TESTS=OFF -DWALBERLA_BUILD_BENCHMARKS=ON -DWALBERLA_BUILD_TUTORIALS=OFF -DWALBERLA_BUILD_TOOLS=OFF -DWALBERLA_BUILD_WITH_MPI=ON -DWALBERLA_BUILD_WITH_CUDA=OFF -DWALBERLA_BUILD_WITH_PYTHON=OFF -DWALBERLA_BUILD_WITH_OPENMP=OFF -DCMAKE_BUILD_TYPE=RELEASE -DMPIEXEC_PREFLAGS=$MPIEXEC_PREFLAGS -DWALBERLA_DOUBLE_ACCURACY=ON -DWARNING_ERROR=ON -DWALBERLA_BUILD_WITH_METIS=OFF -DWALBERLA_BUILD_WITH_PARMETIS=OFF -DWALBERLA_OPTIMIZE_FOR_LOCALHOST=ON -DWALBERLA_BUILD_WITH_FASTMATH=ON -DWALBERLA_BUILD_WITH_LTO=ON
-      - cmake . -LAH
+      - cmake . -LA
       - cd apps/benchmarks/GranularGas
       - make -j 20
       - export PATH=$PATH:/usr/local/likwid/bin
@@ -2452,7 +2088,7 @@ benchmark_ClangBuildAnalyzer:
     - mkdir $CI_PROJECT_DIR/build
     - cd $CI_PROJECT_DIR/build
     - cmake .. -DWALBERLA_BUFFER_DEBUG=OFF -DWALBERLA_BUILD_TESTS=ON -DWALBERLA_BUILD_BENCHMARKS=ON -DWALBERLA_BUILD_TUTORIALS=ON -DWALBERLA_BUILD_TOOLS=OFF -DWALBERLA_BUILD_WITH_MPI=ON -DWALBERLA_BUILD_WITH_CUDA=OFF -DWALBERLA_BUILD_WITH_PYTHON=OFF -DWALBERLA_BUILD_WITH_OPENMP=OFF -DCMAKE_BUILD_TYPE=RELEASE -DMPIEXEC_PREFLAGS=$MPIEXEC_PREFLAGS -DWALBERLA_DOUBLE_ACCURACY=ON -DWARNING_ERROR=ON -DWALBERLA_BUILD_WITH_METIS=OFF -DWALBERLA_BUILD_WITH_PARMETIS=OFF -DWALBERLA_OPTIMIZE_FOR_LOCALHOST=ON -DWALBERLA_BUILD_WITH_FASTMATH=ON -DWALBERLA_BUILD_WITH_LTO=ON -DCMAKE_CXX_FLAGS=-ftime-trace -G Ninja
-    - cmake . -LAH
+    - cmake . -LA
     - ClangBuildAnalyzer --start .
     - ninja all
     - ClangBuildAnalyzer --stop . CBA
@@ -2462,4 +2098,4 @@ benchmark_ClangBuildAnalyzer:
     - docker-benchmark
   only:
      variables:
-        - $ENABLE_NIGHTLY_BUILDS
\ No newline at end of file
+        - $ENABLE_NIGHTLY_BUILDS
diff --git a/.gitmodules b/.gitmodules
new file mode 100644
index 0000000000000000000000000000000000000000..2c0cd5c01dd5a0faa4472178a0dc5f7782937f61
--- /dev/null
+++ b/.gitmodules
@@ -0,0 +1,3 @@
+[submodule "extern/pybind11"]
+	path = extern/pybind11
+	url = https://github.com/pybind/pybind11.git
diff --git a/.isort.cfg b/.isort.cfg
new file mode 100644
index 0000000000000000000000000000000000000000..bfe068484e9980b93f64a94cf82a9d9ce12fdcef
--- /dev/null
+++ b/.isort.cfg
@@ -0,0 +1,5 @@
+[settings]
+line_length=100
+balanced_wrapping=True
+multi_line_output=4
+known_third_party=sympy
diff --git a/AUTHORS.txt b/AUTHORS.txt
index fd6d6505b898fa66ff366a2b40b7bda45b0adc4e..2f956f1b169b71a921ce888d84a0d7f915891f51 100644
--- a/AUTHORS.txt
+++ b/AUTHORS.txt
@@ -13,7 +13,10 @@ Dominik Bartuschat
 Ehsan Fattahi
 Felix Winterhalter
 Florian Schornbaum
+Frederik Hennig
+Grigorii Drozdov
 Helen Schottenhamml
+Igor Ostanin
 Jan Götz
 Jan Hönig
 João Victor Tozatti Risso
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 7c695d2c5b60ab817cf30ceb467e469a5378bb37..bbcea297192340e81aa1dd92e684dfe39b5ad45a 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -2,6 +2,11 @@
 
 ## [Unreleased]
 
+- Python Coupling now build upon pybind11. Boost.Python is no longer supported
+  - lbm module dropped from python coupling due to deprecation for a long time
+  - geometry, postprocessing and timeloop dropped from python coupling due to its low usage
+  - PEP8-ification of Python API. This means all keyword arguments are now in snake_case and not in CamelCase as before.
+
 ## [4.1] - 2019-04-19
 ### Added
 - Galerkin coarsening for Multigrid
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 1abce07601403f398cf5c697ff2b85bcf22d5f21..e70d89fa2225f521cb9421388b181d1c66bdda1f 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -5,7 +5,7 @@
 ## Contents:
 ##   - definition of build options
 ##   - compiler variables ( c++ standard, warnings etc. )
-##   - Finding of service libraries. Required: boost, Optional: MPI, PE, METIS
+##   - Finding of service libraries. Required: none, Optional: Boost, MPI, FFTW3, METIS, OpenMesh, Python
 ##     the include paths are set, and the libraries are added to variable SERVICE_LIBS
 ##   - Subdirectory cmake lists are called
 ##       -> src/   this folder contains all modules, each module (that contains c or cpp files) is linked to a
@@ -75,8 +75,6 @@ option ( WALBERLA_BUILD_WITH_GCOV           "Enables gcov"
 option ( WALBERLA_BUILD_WITH_LTO            "Enable link time optimizations"                     )
 option ( WALBERLA_BUILD_WITH_OPENMP         "Enable OpenMP support"                              )
 option ( WALBERLA_BUILD_WITH_PYTHON         "Support for embedding Python"                       )
-option ( WALBERLA_BUILD_WITH_PYTHON_MODULE  "Build waLBerla python module"                       )
-option ( WALBERLA_BUILD_WITH_PYTHON_LBM     "Include LBM module into python module"          OFF )
 option ( WALBERLA_BUILD_WITH_CODEGEN        "Enable pystencils code generation"              OFF )
 
 
@@ -100,6 +98,8 @@ option ( WALBERLA_OPTIMIZE_FOR_LOCALHOST    "Enable compiler optimizations spcif
 
 option ( WALBERLA_LOG_SKIPPED               "Log skipped cmake targets"                      ON  )
 
+option ( WALBERLA_GIT_SUBMODULE_AUTO        "Check submodules during cmake run"               ON )
+
 # Installation Directory
 set ( CMAKE_INSTALL_PREFIX /usr/local/waLBerla CACHE STRING "The default installation directory."   )
 
@@ -136,7 +136,7 @@ endif()
 
 ############################################################################################################################
 
-set( CMAKE_CXX_STANDARD 14 )
+set( CMAKE_CXX_STANDARD 17 )
 set( CMAKE_CXX_STANDARD_REQUIRED ON )
 set( CMAKE_CXX_EXTENSIONS OFF )
 
@@ -170,10 +170,16 @@ if( CMAKE_CXX_COMPILER MATCHES "icpc" OR CMAKE_CXX_COMPILER_ARG1 MATCHES "icpc"
        SET(CMAKE_LINKER "${XILD}")
     ENDIF(XILD)
     MARK_AS_ADVANCED(XILD)
-    if( CMAKE_VERSION VERSION_LESS 3.6.0 )
-      set( CMAKE_CXX14_STANDARD_COMPILE_OPTION "-std=c++14" )
+
+    if( CMAKE_VERSION VERSION_LESS 3.11.0 )
+      set( CMAKE_CXX17_STANDARD_COMPILE_OPTION "-std=c++17" )
+      set( CMAKE_TRY_COMPILE_PLATFORM_VARIABLES CMAKE_CXX17_STANDARD_COMPILE_OPTION )
       add_flag ( CMAKE_CXX_FLAGS ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION} )
     endif()
+    if( CMAKE_CXX_COMPILER_VERSION VERSION_LESS "19.0.5" )
+      # std::filesystem uses ABI tags, which don't work 19.0.2 but do in 19.0.5
+      add_flag ( CMAKE_CXX_FLAGS "-D_GLIBCXX_USE_CXX11_ABI=0" )
+    endif()
 else()
     option ( WALBERLA_CXX_COMPILER_IS_INTEL "Use Intel compiler" OFF  )
 endif()
@@ -219,16 +225,21 @@ else()
 endif()
 mark_as_advanced ( WALBERLA_CXX_COMPILER_IS_CLANG )
 
+# Check for Cray compiler
 if( CMAKE_CXX_COMPILER_ID MATCHES Cray )
     option ( WALBERLA_CXX_COMPILER_IS_CRAY "Use Cray compiler" ON   )
-    if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS 8.4)
-        message( FATAL_ERROR "Insufficient Cray Compiler Environment version" )
-    endif()
 else()
     option ( WALBERLA_CXX_COMPILER_IS_CRAY "Use Cray compiler" OFF  )
 endif()
 mark_as_advanced ( WALBERLA_CXX_COMPILER_IS_CRAY )
 
+if( CMAKE_CXX_COMPILER MATCHES "pgc\\+\\+" OR CMAKE_CXX_COMPILER_ARG1 MATCHES "pgc\\+\\+" )
+    option ( WALBERLA_CXX_COMPILER_IS_PGI "Use PGI compiler" ON  )
+else()
+    option ( WALBERLA_CXX_COMPILER_IS_PGI "Use PGI compiler" OFF  )
+endif()
+mark_as_advanced ( WALBERLA_CXX_COMPILER_IS_PGI )
+
 # Check for MPI wrapper
 get_filename_component( CXX_COMPILER_WITHOUT_PATH ${CMAKE_CXX_COMPILER} NAME )
 if( CXX_COMPILER_WITHOUT_PATH MATCHES "mpi" OR CMAKE_CXX_COMPILER_ARG1 MATCHES "mpi" )
@@ -285,7 +296,8 @@ endif()
 
 # C++ language features for NEC compiler
 if( WALBERLA_CXX_COMPILER_IS_NEC )
-   set( CMAKE_CXX14_STANDARD_COMPILE_OPTION "-Kcpp14" )
+   set( CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION "-Kcpp${CMAKE_CXX_STANDARD}" )
+   set( CMAKE_TRY_COMPILE_PLATFORM_VARIABLES CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION )
    add_flag ( CMAKE_CXX_FLAGS "${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION} -Krtti -Kexceptions -size_t64 -Kgcc" )
    add_flag ( CMAKE_CXX_FLAGS "-D__BIG_ENDIAN -D__BYTE_ORDER=__BIG_ENDIAN" )
    add_flag ( CMAKE_CXX_FLAGS "-Tnoauto,used" )
@@ -314,8 +326,6 @@ endif()
 if( WALBERLA_CXX_COMPILER_IS_IBM )
    add_flag ( CMAKE_CXX_FLAGS "-qsuppress=1586-267" )  # 1586-267 (I) Inlining of specified subprogram failed due to the presence of a C++ exception handler
    add_flag ( CMAKE_CXX_FLAGS "-qsuppress=1586-266" )  # 1586-266 (I) Inlining of specified subprogram failed due to the presence of a global label
-   add_flag ( CMAKE_CXX_FLAGS "-qsuppress=1540-0724" ) # 1540-0724 (W) The non-type template argument "2147483648" of type "T" has wrapped [coming from boost/integer_traits.hpp]
-   add_flag ( CMAKE_CXX_FLAGS "-qsuppress=1540-0095" ) # 1540-0095 (W) The friend function declaration ... [coming from boost/mpl/map/aux_/map0.hpp]
    add_flag ( CMAKE_CXX_FLAGS "-qsuppress=1500-030" )  # 1500-030: (I) INFORMATION: [...] Additional optimization may be attained by recompiling and specifying MAXMEM option with a value greater than 8192.
    add_flag ( CMAKE_C_FLAGS "-qsuppress=1500-030" )    # 1500-030: (I) INFORMATION: [...] Additional optimization may be attained by recompiling and specifying MAXMEM option with a value greater than 8192.
 endif()
@@ -328,6 +338,21 @@ if( WALBERLA_CXX_COMPILER_IS_CRAY )
    add_flag ( CMAKE_CXX_FLAGS "-DSQLITE_HAVE_ISNAN" ) # SQLite will not work correctly with the -ffast-math option of GCC.
 endif()
 
+# Silences compiler and linker warnings and information with the PGI compiler
+if( WALBERLA_CXX_COMPILER_IS_PGI )
+   add_flag ( CMAKE_CXX_FLAGS "--display_error_number" )
+   add_flag ( CMAKE_C_FLAGS "--display_error_number" )
+   if( CMAKE_VERSION VERSION_LESS "3.19" )
+      #  https://github.com/Kitware/CMake/commit/52eee1938919deb59cc2b51d44f365f0d9a418e5
+      set( CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION "--c++${CMAKE_CXX_STANDARD}" )
+   endif()
+   add_flag ( CMAKE_CXX_FLAGS "--diag_suppress=1" )   # last line of file ends without a newline
+   add_flag ( CMAKE_CXX_FLAGS "--diag_suppress=111" ) # statement is unreachable
+   add_flag ( CMAKE_C_FLAGS "--diag_suppress=111" )   # statement is unreachable
+   add_flag ( CMAKE_C_FLAGS "--diag_suppress=550" )   # variable [...] was set but never used
+   add_flag ( CMAKE_C_FLAGS "--diag_suppress=191" )   # type qualifier is meaningless on cast type
+endif()
+
 # architecture optimization
 if( WALBERLA_OPTIMIZE_FOR_LOCALHOST )
    if( WALBERLA_CXX_COMPILER_IS_GNU OR WALBERLA_CXX_COMPILER_IS_INTEL OR WALBERLA_CXX_COMPILER_IS_CLANG )
@@ -367,12 +392,12 @@ endif()
 
 if ( WALBERLA_CXX_COMPILER_IS_CLANG )
     add_flag ( CMAKE_CXX_FLAGS "-Wall -Wconversion -Wshadow -Wno-c++11-extensions -Qunused-arguments" )
-    add_flag ( CMAKE_CXX_FLAGS "-D'_LIBCPP_EXTERN_TEMPLATE(...)='")
 endif ( )
 
 if( WALBERLA_CXX_COMPILER_IS_GNU OR WALBERLA_CXX_COMPILER_IS_INTEL OR WALBERLA_CXX_COMPILER_IS_CLANG )
     if ( WALBERLA_STL_BOUNDS_CHECKS )
         add_definitions ( "-D_GLIBCXX_DEBUG" )
+        add_definitions ( "-D_LIBCPP_DEBUG=1" )
     endif()
 endif()
 
@@ -433,25 +458,24 @@ endif()
 
 ############################################################################################################################
 ##
-##  Find optional C++ libraries
+##  Find newer C++ libraries, which may only be available in std::experimental on some compilers
 ##
 ############################################################################################################################
 
 try_compile( WALBERLA_USE_STD_FILESYSTEM "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/TestStdFilesystem.cpp"
-             COMPILE_DEFINITIONS -DWALBERLA_USE_STD_FILESYSTEM ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION} )
+             COMPILE_DEFINITIONS -DWALBERLA_USE_STD_FILESYSTEM )
 if( WALBERLA_USE_STD_FILESYSTEM )
    message( STATUS "Found std::filesystem")
 else()
    try_compile( WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/TestStdFilesystem.cpp"
-                COMPILE_DEFINITIONS -DWALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION} )
+                COMPILE_DEFINITIONS -DWALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM )
    if( WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM )
       message( STATUS "Found std::experimental::filesystem")
    endif()
    if( NOT WALBERLA_CXX_COMPILER_IS_MSVC AND NOT WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM )
       unset( WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM CACHE )
       try_compile( WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/TestStdFilesystem.cpp"
-                   COMPILE_DEFINITIONS -DWALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION}
-                   LINK_LIBRARIES stdc++fs )
+                   COMPILE_DEFINITIONS -DWALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM LINK_LIBRARIES stdc++fs )
       if( WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM )
          message( STATUS "Found std::experimental::filesystem in libstdc++fs")
       list ( APPEND SERVICE_LIBS -lstdc++fs )
@@ -460,47 +484,24 @@ else()
    if( NOT WALBERLA_CXX_COMPILER_IS_MSVC AND NOT WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM )
       unset( WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM CACHE )
       try_compile( WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/TestStdFilesystem.cpp"
-                   COMPILE_DEFINITIONS -DWALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION}
-                   LINK_LIBRARIES c++experimental )
+                   COMPILE_DEFINITIONS -DWALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM LINK_LIBRARIES c++experimental )
       if( WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM )
          message( STATUS "Found std::experimental::filesystem in libc++experimental")
          list ( APPEND SERVICE_LIBS -lc++experimental )
       endif()
    endif()
-endif()
-
-try_compile( WALBERLA_USE_STD_ANY "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/TestStdAny.cpp"
-             COMPILE_DEFINITIONS -DWALBERLA_USE_STD_ANY ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION} )
-if( WALBERLA_USE_STD_ANY )
-   message( STATUS "Found std::any")
-else()
-   try_compile( WALBERLA_USE_STD_EXPERIMENTAL_ANY "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/TestStdAny.cpp"
-                COMPILE_DEFINITIONS -DWALBERLA_USE_STD_EXPERIMENTAL_ANY ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION} )
-   if( WALBERLA_USE_STD_EXPERIMENTAL_ANY )
-      message( STATUS "Found std::experimental::any")
+   if( NOT WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM AND NOT WALBERLA_USE_STD_FILESYSTEM)
+       message(FATAL_ERROR "Neither std::filesystem nor std::experimental::filesystem are available")
    endif()
 endif()
 
-try_compile( WALBERLA_USE_STD_OPTIONAL "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/TestStdOptional.cpp"
-             COMPILE_DEFINITIONS -DWALBERLA_USE_STD_OPTIONAL ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION} )
-if( WALBERLA_USE_STD_OPTIONAL )
-   message( STATUS "Found std::optional")
-else()
-   try_compile( WALBERLA_USE_STD_EXPERIMENTAL_OPTIONAL "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/TestStdOptional.cpp"
-                COMPILE_DEFINITIONS -DWALBERLA_USE_STD_EXPERIMENTAL_OPTIONAL ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION} )
-   if( WALBERLA_USE_STD_EXPERIMENTAL_OPTIONAL )
-      message( STATUS "Found std::experimental::optional")
-   endif()
-endif()
-
-try_compile( WALBERLA_USE_STD_VARIANT "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/cmake/TestStdVariant.cpp"
-             COMPILE_DEFINITIONS -DWALBERLA_USE_STD_VARIANT ${CMAKE_CXX${CMAKE_CXX_STANDARD}_STANDARD_COMPILE_OPTION} )
-if( WALBERLA_USE_STD_VARIANT )
-   message( STATUS "Found std::variant")
+if (WALBERLA_BUILD_WITH_CUDA AND (CUDA_VERSION VERSION_LESS "11.0" OR CMAKE_VERSION VERSION_LESS 3.18.0))
+   # CUDA < 11 does not support C++17. std::experimental::any works with C++14, unlike std::any.
+   set(CMAKE_CUDA_STANDARD 14)
+   set(WALBERLA_USE_STD_EXPERIMENTAL_ANY 1)
 endif()
 
 
-
 ############################################################################################################################
 ##
 ##  Visual Studio Setup
@@ -556,14 +557,23 @@ endif ( )
 ##
 #############################################################################################################################
 if ( WALBERLA_BUILD_WITH_CODEGEN )
-    find_package( PythonInterp 3 QUIET REQUIRED)
+   find_package( PythonInterp 3 QUIET REQUIRED)
+   execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import lbmpy"
+         RESULT_VARIABLE LBMPY_FOUND )
+    if(NOT LBMPY_FOUND EQUAL 0)
+       message(FATAL_ERROR "WALBERLA_BUILD_WITH_CODEGEN activated but pystencils or lbmpy package not found.
+                            Please install lbmpy e.g.: 'pip3 install lbmpy'")
+    endif()
     execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "from pystencils.include import get_pystencils_include_path; print(get_pystencils_include_path())"
-                    RESULT_VARIABLE PYTHON_RET_CODE
                     OUTPUT_VARIABLE PYSTENCILS_INCLUDE_PATH)
-    if(NOT PYTHON_RET_CODE EQUAL 0)
-        message(FATAL_ERROR "WALBERLA_BUILD_WITH_CODEGEN activated and pystencils_walberla package not found")
-    endif()
     include_directories( ${PYSTENCILS_INCLUDE_PATH} )
+
+    execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import jinja2"
+          RESULT_VARIABLE JINJA2_FOUND )
+    if(NOT JINJA2_FOUND EQUAL 0)
+       message(FATAL_ERROR "WALBERLA_BUILD_WITH_CODEGEN activated and jinja2 package not found.
+       Please install jinja2 e.g.: 'pip3 install jinja2'")
+    endif()
 endif()
 ############################################################################################################################
 
@@ -577,53 +587,65 @@ endif()
 #############################################################################################################################
 if ( WALBERLA_BUILD_WITH_PYTHON )
 
-    set ( waLBerla_REQUIRED_MIN_PYTHON_VERSION "2.7")
-
-    find_package( PythonInterp 3 QUIET) # search for Python3 first
-    find_package( PythonInterp QUIET) # fallback to any Python version
-
+    find_package( PythonInterp 3.6 QUIET REQUIRED)
     find_package( PythonLibs QUIET REQUIRED)
 
-    if( PYTHONLIBS_VERSION_STRING VERSION_LESS ${waLBerla_REQUIRED_MIN_PYTHON_VERSION} )
-        message( FATAL_ERROR "Found old python library: ${PYTHONLIBS_VERSION_STRING} need at least ${waLBerla_REQUIRED_MIN_PYTHON_VERSION}" )
-    endif()
-
-    option( WALBERLA_USE_PYTHON_DEBUG_LIBRARY "Make use of the python debug library" OFF )
-
-    if( WALBERLA_USE_PYTHON_DEBUG_LIBRARY )
-      # you have to make sure this matches the settings you compiled boost with!
-      add_definitions( "-DBOOST_DEBUG_PYTHON" )
-    endif()
-
     if( NOT (PYTHON_LIBRARY AND PYTHON_INCLUDE_DIR ) )
         message( FATAL_ERROR "Couldn't find any python library" )
     endif()
 
-    SET( WALBERLA_BUILD_WITH_PYTHON 1 )
-    include_directories( ${PYTHON_INCLUDE_DIR} )
-    list ( APPEND SERVICE_LIBS ${PYTHON_LIBRARY} )
-
-    if( NOT WALBERLA_CXX_COMPILER_IS_MSVC )
-        list ( APPEND SERVICE_LIBS -lutil )
+    if(WALBERLA_CXX_COMPILER_IS_INTEL)
+        # Intel C++17 support introduced in 2.6.2 (https://github.com/pybind/pybind11/pull/2729)
+        set(PYBIND11_MINIMUM_VERSION "2.6.2")
+    else()
+        set(PYBIND11_MINIMUM_VERSION "2.6.0")
     endif()
 
-    if ( WALBERLA_BUILD_WITH_PYTHON_MODULE )
-        # a python module is a shared library - so everything has to be compiled to position independent code
-        # otherwise linking the static libs into the shared lib will result in errors
-        if( NOT WALBERLA_CXX_COMPILER_IS_MSVC )
-            add_flag ( CMAKE_CXX_FLAGS "-fPIC" )
-            add_flag ( CMAKE_C_FLAGS "-fPIC" )
+    execute_process(COMMAND ${PYTHON_EXECUTABLE} -c "import pybind11; print(pybind11._version.__version__)"
+                    OUTPUT_VARIABLE pybind11_VERSION ERROR_QUIET RESULT_VARIABLE pybind11_VERSION_RESULT)
+    string(STRIP "${pybind11_VERSION}" pybind11_VERSION)
+    if(pybind11_VERSION_RESULT EQUAL "0" AND pybind11_VERSION VERSION_GREATER_EQUAL "${PYBIND11_MINIMUM_VERSION}")
+        execute_process(COMMAND ${PYTHON_EXECUTABLE} -m pybind11 --cmakedir
+                        OUTPUT_VARIABLE PYBIND11_CMAKE_PATH)
+        string(STRIP "${PYBIND11_CMAKE_PATH}" PYBIND11_CMAKE_PATH)
+        find_package(pybind11 PATHS "${PYBIND11_CMAKE_PATH}" NO_DEFAULT_PATH REQUIRED)
+    else()
+        find_package(Git QUIET)
+        if(GIT_FOUND AND EXISTS "${PROJECT_SOURCE_DIR}/.git")
+           # Update submodules as needed
+           if(WALBERLA_GIT_SUBMODULE_AUTO)
+              message(STATUS "Submodule update")
+              execute_process(COMMAND ${GIT_EXECUTABLE} submodule update --init --recursive
+                    WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}
+                    RESULT_VARIABLE GIT_SUBMOD_RESULT)
+              if(NOT GIT_SUBMOD_RESULT EQUAL "0")
+                 message(FATAL_ERROR "git submodule update --init failed with ${GIT_SUBMOD_RESULT}, please checkout submodules")
+              endif()
+           endif()
         endif()
-    endif()
 
-    if( MSVC10 )
-        include(CMakeDependentOption)
-        CMAKE_DEPENDENT_OPTION( PYTHON_FIXED_HYPOT_REDEFINITION "fixed _hypot redefinition by python" OFF "WALBERLA_BUILD_WITH_PYTHON" OFF )
-        if( NOT PYTHON_FIXED_HYPOT_REDEFINITION )
-            message( WARNING "Make sure you modified your pyconfig.h that _hypot is not redefined -> see: http://connect.microsoft.com/VisualStudio/feedback/details/633988/warning-in-math-h-line-162-re-nonstandard-extensions-used" )
+        if(NOT EXISTS "${PROJECT_SOURCE_DIR}/extern/pybind11/CMakeLists.txt")
+            if(EXISTS "${PROJECT_SOURCE_DIR}/.git")
+                message(FATAL_ERROR "Please update git submodules or install pybind11 via pip.")
+            else()
+                message(FATAL_ERROR "Please install pybind11 via pip or download it to ${PROJECT_SOURCE_DIR}/extern/pybind11.")
+            endif()
+        endif()
+
+        add_subdirectory(extern/pybind11)
+
+        if(pybind11_VERSION VERSION_LESS "2.6.2")
+            # if pybind11 was installed into ${PYTHON_INCLUDE_DIR} (e.g. by pip), that will have a higher priority than the Git submodule unless we reorder the search path
+            # introduced in 2.6.0 (https://github.com/pybind/pybind11/issues/2709), fixed in 2.6.2 (https://github.com/pybind/pybind11/pull/2716)
+            set_property( TARGET pybind11::pybind11
+                          PROPERTY INTERFACE_INCLUDE_DIRECTORIES "${PROJECT_SOURCE_DIR}/extern/pybind11/include" "${PYTHON_INCLUDE_DIR}")
         endif()
     endif()
 
+    # a python module is a shared library - so everything has to be compiled to position independent code
+    # otherwise linking the static libs into the shared lib will result in errors
+    set(CMAKE_POSITION_INDEPENDENT_CODE ON)
+
 
     if(WALBERLA_BUILD_DOC)
       # Sphinx documentation
@@ -640,35 +662,14 @@ endif()
 ## BOOST Libraries
 ##
 #############################################################################################################################
-set ( waLBerla_REQUIRED_MIN_BOOST_VERSION "1.48")
 set ( Boost_NO_BOOST_CMAKE ON)
 
-if ( NOT WALBERLA_USE_STD_FILESYSTEM AND NOT WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM )
-  list ( APPEND waLBerla_REQUIRED_BOOST_COMPONENTS filesystem system )
-else()
-  list ( APPEND waLBerla_OPTIONAL_BOOST_COMPONENTS system )
-endif()
-
-if ( WALBERLA_BUILD_WITH_PYTHON )
-  if( WALBERLA_CXX_COMPILER_IS_MSVC )
-    get_filename_component(PYTHON_REQUIRED_LIB ${PYTHON_LIBRARY} NAME_WE)
-    list( APPEND waLBerla_REQUIRED_BOOST_COMPONENTS ${PYTHON_REQUIRED_LIB} )
-  elseif( WALBERLA_USE_STD_FILESYSTEM OR WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM )
-    list( APPEND waLBerla_REQUIRED_BOOST_COMPONENTS system )
-    list( REMOVE_ITEM waLBerla_OPTIONAL_BOOST_COMPONENTS system )
-  endif()
-endif()
-
 # This variable is necessary, if the CMAKE version used is not aware of a more recent boost version (keep this up to date!)
 set ( Boost_ADDITIONAL_VERSIONS
       "1.45" "1.45.0" "1.46" "1.46.0" "1.46.1" "1.47" "1.47.0" "1.48" "1.48.0" "1.49" "1.49.0"
       "1.50" "1.50.0" "1.51" "1.51.0" "1.52" "1.52.0" "1.53" "1.53.0" "1.54" "1.54.0" "1.55" "1.55.0"
       "1.56" "1.56.0" "1.57" "1.57.0" "1.58" "1.58.0" "1.59" "1.59.0" "1.60" "1.60.0" "1.61" "1.61.0" "1.62" "1.62.0" "1.63" "1.63.0"
-      "1.64.0" "1.65.0" "1.65.1" "1.66.0" "1.67.0" "1.68.0" "1.69.0" "1.70.0" "1.71.0" "1.72.0" "1.73.0")
-
-set ( Boost_USE_STATIC_LIBS    OFF CACHE BOOL "Use boost static libraries" )
-set ( Boost_USE_MULTITHREADED  OFF CACHE BOOL "Use boost multithreaded libraries" )
-set ( Boost_USE_STATIC_RUNTIME OFF CACHE BOOL "Use boost libraries statically linked to runtime libs" )
+      "1.64.0" "1.65.0" "1.65.1" "1.66.0" "1.67.0" "1.68.0" "1.69.0" "1.70.0" "1.71.0" "1.72.0" "1.73.0" "1.74.0")
 
 # if you defined BOOST_ROOT or BOOST_BASE in your environment use it here to find boost too
 if ( NOT BOOST_ROOT )
@@ -681,28 +682,7 @@ if ( NOT BOOST_ROOT )
    endforeach ( )
 endif ( )
 
-find_package ( Boost ${waLBerla_REQUIRED_MIN_BOOST_VERSION} COMPONENTS ${waLBerla_REQUIRED_BOOST_COMPONENTS} OPTIONAL_COMPONENTS ${waLBerla_OPTIONAL_BOOST_COMPONENTS} QUIET )
-
-if( NOT Boost_FOUND )
-   message ( WARNING
-      "The specified configuration of the BOOST libraries was not found on your system! Now trying some other configuration..." )
-   foreach ( Boost_USE_STATIC_LIBS ON OFF )
-      foreach ( Boost_USE_MULTITHREADED ON OFF )
-         find_package ( Boost ${waLBerla_REQUIRED_MIN_BOOST_VERSION} COMPONENTS ${waLBerla_REQUIRED_BOOST_COMPONENTS} OPTIONAL_COMPONENTS ${waLBerla_OPTIONAL_BOOST_COMPONENTS} QUIET )
-         if ( Boost_FOUND )
-            set ( Boost_USE_STATIC_LIBS   ${Boost_USE_STATIC_LIBS}   CACHE BOOL "Use boost static libraries"        FORCE )
-            set ( Boost_USE_MULTITHREADED ${Boost_USE_MULTITHREADED} CACHE BOOL "Use boost multithreaded libraries" FORCE )
-            set ( Boost_USE_MULTITHREADED_LIBRARY ${Boost_USE_MULTITHREADED} )
-            message ( STATUS "Working configuration of the BOOST libraries was found :o)!" )
-            message ( STATUS "Boost_USE_STATIC_LIBS and Boost_USE_MULTITHREADED was adapted accordingly." )
-            BREAK ( )
-         endif ( Boost_FOUND )
-      endforeach ( Boost_USE_MULTITHREADED )
-      if ( Boost_FOUND )
-         BREAK ( )
-      endif ( Boost_FOUND )
-   endforeach ( Boost_USE_STATIC_LIBS )
-endif ( NOT Boost_FOUND )
+find_package ( Boost )
 
 if ( Boost_FOUND )
    if(CMAKE_GENERATOR STREQUAL "Xcode")
@@ -711,64 +691,14 @@ if ( Boost_FOUND )
    else()
       include_directories ( SYSTEM ${Boost_INCLUDE_DIRS} )
    endif()
-   if( waLBerla_REQUIRED_BOOST_COMPONENTS )
-      link_directories ( ${Boost_LIBRARY_DIRS} )
-      list ( APPEND SERVICE_LIBS ${Boost_LIBRARIES} )
-   endif()
    add_definitions ( -DBOOST_ALL_NO_LIB ) # Disable Boost auto-linking (CMAKE does that for us...)
 
-   #fix for static lib usage: http://stackoverflow.com/questions/11812463/boost-python-link-errors-under-windows-msvc10
-   if( PYTHONLIBS_FOUND AND Boost_USE_STATIC_LIBS)
-      add_definitions( -DBOOST_PYTHON_STATIC_LIB )
-   endif()
-
-   #fix for strange link behaviour of boost to python: boost only links to 'pyhton*.lib' and not to the absolute path
-   if( WIN32 AND PYTHONLIBS_FOUND )
-      get_filename_component( PYTHON_LIBRARY_DIR ${PYTHON_INCLUDE_DIR} PATH )
-      link_directories( ${PYTHON_LIBRARY_DIR}/libs )
-      list( APPEND LINK_DIRS ${PYTHON_LIBRARY_DIR}/libs )
-   endif()
-
    set( WALBERLA_BUILD_WITH_BOOST TRUE CACHE INTERNAL "Build with Boost" )
 else( Boost_FOUND )
-   if( (WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM OR WALBERLA_USE_STD_FILESYSTEM) AND (WALBERLA_USE_STD_EXPERIMENTAL_ANY OR WALBERLA_USE_STD_ANY) AND (WALBERLA_USE_STD_EXPERIMENTAL_OPTIONAL OR WALBERLA_USE_STD_OPTIONAL) AND NOT WALBERLA_BUILD_WITH_PYTHON)
-      # we don't really need Boost
-      set( WALBERLA_BUILD_WITH_BOOST FALSE CACHE INTERNAL "Build with Boost" )
-   else()
-      # Search again, this time with the REQUIRED option. This will give a CMAKE error and a detailed error message for the user
-      find_package ( Boost ${waLBerla_REQUIRED_MIN_BOOST_VERSION} REQUIRED ${waLBerla_REQUIRED_BOOST_COMPONENTS} OPTIONAL_COMPONENTS ${waLBerla_OPTIONAL_BOOST_COMPONENTS} )
-      if( (WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM OR WALBERLA_USE_STD_FILESYSTEM) AND (WALBERLA_USE_STD_EXPERIMENTAL_ANY OR WALBERLA_USE_STD_ANY) AND (WALBERLA_USE_STD_EXPERIMENTAL_OPTIONAL OR WALBERLA_USE_STD_OPTIONAL) )
-         message(WARNING "If you set WALBERLA_BUILD_WITH_PYTHON=OFF, you can build without the Boost library.")
-      endif()
-   endif()
+   set( WALBERLA_BUILD_WITH_BOOST FALSE CACHE INTERNAL "Build with Boost" )
 endif( Boost_FOUND )
 
 
-# Check if Python3 found and look for according boost python library
-if ( WALBERLA_BUILD_WITH_PYTHON AND NOT WALBERLA_CXX_COMPILER_IS_MSVC)
-    SET(_boost_MULTITHREADED "")
-    if (Boost_USE_MULTITHREADED OR Boost_USE_MULTITHREADED_LIBRARY)
-        SET(_boost_MULTITHREADED "-mt")
-    endif()
-    if( PYTHON_LIBRARY MATCHES "python3" )
-        string(REPLACE "." ";" VERSION_LIST ${PYTHONLIBS_VERSION_STRING})
-        list(GET VERSION_LIST 0 PY_VER_MAJOR)
-        list(GET VERSION_LIST 1 PY_VER_MINOR)
-        find_library( BOOST_PYTHON_LIBRARY NAMES
-                boost_python${PY_VER_MAJOR}${PY_VER_MINOR}${_boost_MULTITHREADED}
-                boost_python-py${PY_VER_MAJOR}${PY_VER_MINOR}${_boost_MULTITHREADED}
-                boost_python${PY_VER_MAJOR}${_boost_MULTITHREADED}
-                boost_python${_boost_MULTITHREADED}
-                PATHS ${Boost_LIBRARY_DIRS} NO_DEFAULT_PATH )
-    else()
-        find_library( BOOST_PYTHON_LIBRARY NAMES boost_python${_boost_MULTITHREADED}
-                      PATHS ${Boost_LIBRARY_DIRS} NO_DEFAULT_PATH )
-    endif()
-    message(STATUS "Using Boost Python Library ${BOOST_PYTHON_LIBRARY}" )
-    list ( APPEND SERVICE_LIBS ${BOOST_PYTHON_LIBRARY} )
-endif()
-
-
 ############################################################################################################################
 
 
@@ -781,8 +711,14 @@ endif()
 ##
 ############################################################################################################################
 
-if ( NOT WIN32 )
-   add_flag( CMAKE_CXX_FLAGS "-pthread" )
+set( THREADS_PREFER_PTHREAD_FLAG TRUE )
+find_package(Threads)
+if ( Threads_FOUND )
+   if( CMAKE_USE_PTHREADS_INIT )
+      add_flag( CMAKE_CXX_FLAGS "-pthread" )
+   else()
+      add_flag( CMAKE_CXX_FLAGS "${CMAKE_THREAD_LIBS_INIT}" )
+   endif()
 endif()
 
 
@@ -899,10 +835,6 @@ if ( WALBERLA_ENABLE_GUI )
     INCLUDE( ${QT_USE_FILE} )
     list ( APPEND SERVICE_LIBS ${OPENGL_LIBRARIES} ${QT_LIBRARIES} )
 
-    # Workaround for Qt4 moc and newer boost versions - moc cannot parse BOOST_JOIN
-    # so additional defines are passed to the moc compiler that prevent the problematic header to be parsed
-    set( QT_MOC_EXECUTABLE ${QT_MOC_EXECUTABLE} -DBOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION -DBOOST_TT_HAS_OPERATOR_HPP_INCLUDED )
-
 endif(WALBERLA_ENABLE_GUI)
 
 ############################################################################################################################
@@ -1070,22 +1002,25 @@ endif()
 option ( WALBERLA_THREAD_SAFE_LOGGING "Enables/Disables thread-safe logging" ON )
 
 if ( WALBERLA_BUILD_WITH_OPENMP )
-    if ( WALBERLA_CXX_COMPILER_IS_INTEL AND "${CMAKE_CXX_COMPILER_VERSION}" VERSION_LESS "16.0.3" )
-       add_flag ( CMAKE_C_FLAGS   "-openmp" )
-       add_flag ( CMAKE_CXX_FLAGS "-openmp" )
-    elseif ( WALBERLA_CXX_COMPILER_IS_NEC )
-       add_flag ( CMAKE_C_FLAGS   "-Popenmp" )
-       add_flag ( CMAKE_CXX_FLAGS "-Popenmp" )
+    find_package( OpenMP )
+    if (OpenMP_FOUND)
+        add_flag ( CMAKE_C_FLAGS   "${OpenMP_C_FLAGS}" )
+        add_flag ( CMAKE_CXX_FLAGS "${OpenMP_CXX_FLAGS}" )
+        list ( APPEND SERVICE_LIBS ${OpenMP_CXX_LIBRARIES} )
     else()
-       find_package( OpenMP )
-       add_flag ( CMAKE_C_FLAGS   "${OpenMP_C_FLAGS}" )
-       add_flag ( CMAKE_CXX_FLAGS "${OpenMP_CXX_FLAGS}" )
-       list ( APPEND SERVICE_LIBS ${OpenMP_CXX_LIBRARIES} )
+        #workarounds
+        if ( WALBERLA_CXX_COMPILER_IS_NEC )
+            message( STATUS "Enabling OpenMP workaround for NEC")
+            add_flag ( CMAKE_C_FLAGS   "-Popenmp" )
+            add_flag ( CMAKE_CXX_FLAGS "-Popenmp" )
+        else()
+            message(FATAL_ERROR "Could NOT enable OpenMP")
+        endif()
     endif()
 else()
     if ( WALBERLA_CXX_COMPILER_IS_CRAY )
-       add_flag ( CMAKE_C_FLAGS   "-h noomp" )
-       add_flag ( CMAKE_CXX_FLAGS "-h noomp" )
+        add_flag ( CMAKE_C_FLAGS   "-h noomp" )
+        add_flag ( CMAKE_CXX_FLAGS "-h noomp" )
     endif()
 endif()
 ############################################################################################################################
@@ -1184,31 +1119,14 @@ endif()
 ##
 ############################################################################################################################
 if ( WALBERLA_BUILD_WITH_LTO  )
-
-   if( WALBERLA_CXX_COMPILER_IS_INTEL )
-      add_flag( CMAKE_CXX_FLAGS_RELEASE "-ip -ipo3" )
-      add_flag( CMAKE_C_FLAGS_RELEASE   "-ip -ipo3" )
-   endif()
-
-   if ( CMAKE_COMPILER_IS_GNUCXX )
-      add_flag ( CMAKE_C_FLAGS_RELEASE     "-flto=3" )
-      add_flag ( CMAKE_CXX_FLAGS_RELEASE   "-flto=3" )
-      add_flag ( CMAKE_EXE_LINKER_FLAGS    "-fuse-linker-plugin" )
-   endif ( )
-
-   if( WALBERLA_CXX_COMPILER_IS_MSVC )
-      add_flag ( CMAKE_CXX_FLAGS_RELEASE           "/GL"   )
-      add_flag ( CMAKE_EXE_LINKER_FLAGS_RELEASE    "/LTCG" )
-      add_flag ( CMAKE_SHARED_LINKER_FLAGS_RELEASE "/LTCG" )
-      add_flag ( CMAKE_MODULE_LINKER_FLAGS_RELEASE "/LTCG" )
-   endif ( )
-
-   if( WALBERLA_CXX_COMPILER_IS_IBM )
-      add_flag ( CMAKE_C_FLAGS_RELEASE     "-qipa" )
-      add_flag ( CMAKE_CXX_FLAGS_RELEASE   "-qipa" )
-      add_flag ( CMAKE_EXE_LINKER_FLAGS    "-qipa" )
-   endif( )
-
+    cmake_policy( SET CMP0069 NEW )
+    include( CheckIPOSupported )
+    check_ipo_supported( RESULT LTO_SUPPORTED LANGUAGES CXX )
+    if( LTO_SUPPORTED )
+       set( CMAKE_INTERPROCEDURAL_OPTIMIZATION TRUE )
+    else()
+       message( WARNING "Link-time optimization is not supported with this compiler" )
+    endif()
 endif ( )
 
 ############################################################################################################################
diff --git a/README.md b/README.md
index 40e176fb857656554a600748f73aa019389782f2..bca339ae96fdaff55538741d951e82e5fb6440a6 100644
--- a/README.md
+++ b/README.md
@@ -1,12 +1,12 @@
 # waLBerla
 
-waLBerla (widely applicable Lattice Boltzmann from Erlangen) is a massively 
-parallel framework for multi physics applications. Besides its original 
-objective, Lattice Boltzmann solvers for hydrodynamics, it now contains 
-modules for other applications like Multigrid and rigid body dynamics 
-as well. Great emphasis is placed on the interoperability between the modules 
-in particular the fluid-particle coupling. 
-It scales from laptops to current and future supercomputers while maintaining 
+waLBerla (widely applicable Lattice Boltzmann from Erlangen) is a massively
+parallel framework for multi physics applications. Besides its original
+objective, Lattice Boltzmann solvers for hydrodynamics, it now contains
+modules for other applications like Multigrid and rigid body dynamics
+as well. Great emphasis is placed on the interoperability between the modules
+in particular the fluid-particle coupling.
+It scales from laptops to current and future supercomputers while maintaining
 near-perfect efficiency.
 
 See https://www.walberla.net/ for more information and a showcase of applications.
@@ -19,8 +19,8 @@ is documented in [Sphinx](http://walberla.net/sphinx/index.html).
 
 ## Getting started
 
-The minimum requirements are a C++14-compliant compiler (e.g. GCC or Clang),
-the [Boost](http://www.boost.org) library and the [CMake](http://www.cmake.org)
+The minimum requirements are a C++17-compliant compiler (e.g. GCC or Clang)
+and the [CMake](http://www.cmake.org)
 build system. Furthermore, you need an MPI library (like
 [Open MPI](http://www.open-mpi.org)) if you want to make use of parallel
 processing capabilities. All of these dependencies are typically available in
@@ -47,15 +47,30 @@ Many thanks go to waLBerla's [contributors](AUTHORS.txt)
 
 If you use waLBerla in a publication, please cite the following articles:
 
-- C. Godenschwager, F. Schornbaum, M. Bauer, H. Köstler, and U. Rüde. A
-framework for hybrid parallel flow simulations with a trillion cells in complex
-geometries. In: Proceedings of the International Conference on High Performance
-Computing, Networking, Storage and Analysis, page 35. ACM, 2013.
-- M. Bauer, S. Eibl, C. Godenschwager, N. Kohl, M. Kuron, C. Rettinger,
-F. Schornbaum, C. Schwarzmeier, D. Thönnes, H. Köstler, and U. Rüde. waLBerla:
-A block-structured high-performance framework for multiphysics simulations. In:
-Computers & Mathematics with Applications, doi:10.1016/j.camwa.2020.01.007.
-Elsevier, 2020.
+Overview:
+  - M. Bauer et al, *waLBerla: A block-structured high-performance framework for
+    multiphysics simulations*. Computers & Mathematics with Applications, 2020.
+    https://doi.org/10.1016/j.camwa.2020.01.007.
+
+Grid Refinement:
+  - F. Schornbaum and U. Rüde, *Massively parallel algorithms for the lattice boltzmann
+    method on nonuniform grids*. SIAM Journal on Scientific Computing, 2016.
+    https://doi.org/10.1137/15M1035240
+
+LBM - Particle Coupling:
+  - C. Rettinger and U. Rüde, *A comparative study of fluid-particle coupling methods for
+    fully resolved lattice Boltzmann simulations*. Computers & Fluids, 2017.
+    https://doi.org/10.1016/j.compfluid.2017.05.033
+
+MESA-PD:
+  - S. Eibl and U. Rüde, *A Modular and Extensible Software Architecture for Particle Dynamics*.
+    Proceedings Of The 8Th International Conference On Discrete Element Methods.
+    https://mercurylab.co.uk/dem8/full-papers/#page-content
+
+Carbon Nanotubes:
+  - G. Drozdov et al, *Densification of single-walled carbon nanotube films:
+    Mesoscopic distinct element method simulations and experimental validation*.
+    Journal of Applied Physics, 2020. https://doi.org/10.1063/5.0025505
 
 ## License
 
diff --git a/apps/CMakeLists.txt b/apps/CMakeLists.txt
index 58953c4bbedfbf38d0f883edbb882d2f2880228d..178380b575ad967cfbb5af3181a41e17ddec9ada 100644
--- a/apps/CMakeLists.txt
+++ b/apps/CMakeLists.txt
@@ -32,6 +32,4 @@ endif()
 # Python module
 if ( WALBERLA_BUILD_WITH_PYTHON )
     add_subdirectory( pythonmodule )
-    # no else with "EXLUDE_FROM_ALL" here, since if WALBERLA_BUILD_WITH_PYTHON_MODULE is not activated
-    # waLBerla was build without -fPIC , so no linking into shared library is possible
 endif()
\ No newline at end of file
diff --git a/apps/benchmarks/AdaptiveMeshRefinementFluidParticleCoupling/AMRSedimentSettling.cpp b/apps/benchmarks/AdaptiveMeshRefinementFluidParticleCoupling/AMRSedimentSettling.cpp
index f8b5674dff39a25cdaf4854a8249d45dbefe988c..73e19a030e91a85a4dda5549d9a19c8078674886 100644
--- a/apps/benchmarks/AdaptiveMeshRefinementFluidParticleCoupling/AMRSedimentSettling.cpp
+++ b/apps/benchmarks/AdaptiveMeshRefinementFluidParticleCoupling/AMRSedimentSettling.cpp
@@ -90,25 +90,25 @@ using walberla::uint_t;
 //////////////
 
 // PDF field, flag field & body field
-typedef lbm::D3Q19< lbm::collision_model::TRT, false>  LatticeModel_T;
-typedef LatticeModel_T::Stencil          Stencil_T;
-typedef lbm::PdfField< LatticeModel_T > PdfField_T;
+using LatticeModel_T = lbm::D3Q19<lbm::collision_model::TRT, false>;
+using Stencil_T = LatticeModel_T::Stencil;
+using PdfField_T = lbm::PdfField<LatticeModel_T>;
 
-typedef walberla::uint8_t                 flag_t;
-typedef FlagField< flag_t >               FlagField_T;
-typedef GhostLayerField< pe::BodyID, 1 >  BodyField_T;
-typedef GhostLayerField< Vector3<real_t>, 1 >  VelocityField_T;
+using flag_t = walberla::uint8_t;
+using FlagField_T = FlagField<flag_t>;
+using BodyField_T = GhostLayerField<pe::BodyID, 1>;
+using VelocityField_T = GhostLayerField<Vector3<real_t>, 1>;
 
 const uint_t FieldGhostLayers = 4;
 
 // boundary handling
-typedef lbm::NoSlip< LatticeModel_T, flag_t > NoSlip_T;
+using NoSlip_T = lbm::NoSlip<LatticeModel_T, flag_t>;
 
-typedef pe_coupling::CurvedLinear< LatticeModel_T, FlagField_T > MO_T;
+using MO_T = pe_coupling::CurvedLinear<LatticeModel_T, FlagField_T>;
 
-typedef BoundaryHandling< FlagField_T, Stencil_T, NoSlip_T, MO_T > BoundaryHandling_T;
+using BoundaryHandling_T = BoundaryHandling<FlagField_T, Stencil_T, NoSlip_T, MO_T>;
 
-typedef std::tuple<pe::Sphere, pe::Ellipsoid, pe::Plane> BodyTypeTuple;
+using BodyTypeTuple = std::tuple<pe::Sphere, pe::Ellipsoid, pe::Plane>;
 
 ///////////
 // FLAGS //
@@ -135,8 +135,8 @@ template< typename LatticeModel_T, typename Filter_T >
 class VectorGradientRefinement
 {
 public:
-   typedef GhostLayerField< Vector3<real_t>, 1 >  VectorField_T;
-   typedef typename LatticeModel_T::Stencil       Stencil_T;
+   using VectorField_T = GhostLayerField<Vector3<real_t>, 1>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    VectorGradientRefinement( const ConstBlockDataID & fieldID, const Filter_T & filter,
                              const real_t upperLimit, const real_t lowerLimit, const uint_t maxLevel ) :
@@ -1504,7 +1504,7 @@ int main( int argc, char **argv )
    // add velocity field and utility
    BlockDataID velocityFieldID = field::addToStorage<VelocityField_T>( blocks, "velocity field", Vector3<real_t>(real_t(0)), field::zyxf, uint_t(2) );
 
-   typedef lbm::VelocityFieldWriter< PdfField_T, VelocityField_T > VelocityFieldWriter_T;
+   using VelocityFieldWriter_T = lbm::VelocityFieldWriter<PdfField_T, VelocityField_T>;
    BlockSweepWrapper< VelocityFieldWriter_T > velocityFieldWriter( blocks, VelocityFieldWriter_T( pdfFieldID, velocityFieldID ) );
 
 
@@ -1847,7 +1847,7 @@ int main( int argc, char **argv )
                                                  "Body Mapping", finestLevel );
 
    // add sweep for restoring PDFs in cells previously occupied by pe bodies
-   typedef pe_coupling::EquilibriumReconstructor< LatticeModel_T, BoundaryHandling_T > Reconstructor_T;
+   using Reconstructor_T = pe_coupling::EquilibriumReconstructor<LatticeModel_T, BoundaryHandling_T>;
    Reconstructor_T reconstructor( blocks, boundaryHandlingID, bodyFieldID );
    refinementTimestep->addPostStreamVoidFunction(lbm::refinement::SweepAsFunctorWrapper( pe_coupling::PDFReconstruction< LatticeModel_T, BoundaryHandling_T, Reconstructor_T > ( blocks, pdfFieldID,
                                                  boundaryHandlingID, bodyStorageID, globalBodyStorage, bodyFieldID, reconstructor, FormerMO_Flag, Fluid_Flag ), blocks ),
diff --git a/apps/benchmarks/AdaptiveMeshRefinementFluidParticleCoupling/AMRSettlingSphere.cpp b/apps/benchmarks/AdaptiveMeshRefinementFluidParticleCoupling/AMRSettlingSphere.cpp
index 0db5c807dfac7f573d2d894980d90e14112928a3..c13a7c93db4905f2f50342a79667613ec748771b 100644
--- a/apps/benchmarks/AdaptiveMeshRefinementFluidParticleCoupling/AMRSettlingSphere.cpp
+++ b/apps/benchmarks/AdaptiveMeshRefinementFluidParticleCoupling/AMRSettlingSphere.cpp
@@ -84,25 +84,25 @@ using walberla::uint_t;
 //////////////
 
 // PDF field, flag field & body field
-typedef lbm::D3Q19< lbm::collision_model::TRT, false >  LatticeModel_T;
-typedef LatticeModel_T::Stencil          Stencil_T;
-typedef lbm::PdfField< LatticeModel_T > PdfField_T;
+using LatticeModel_T = lbm::D3Q19<lbm::collision_model::TRT, false>;
+using Stencil_T = LatticeModel_T::Stencil;
+using PdfField_T = lbm::PdfField<LatticeModel_T>;
 
-typedef walberla::uint8_t                 flag_t;
-typedef FlagField< flag_t >               FlagField_T;
-typedef GhostLayerField< pe::BodyID, 1 >  BodyField_T;
-typedef GhostLayerField< Vector3<real_t>, 1 >  VelocityField_T;
+using flag_t = walberla::uint8_t;
+using FlagField_T = FlagField<flag_t>;
+using BodyField_T = GhostLayerField<pe::BodyID, 1>;
+using VelocityField_T = GhostLayerField<Vector3<real_t>, 1>;
 
 const uint_t FieldGhostLayers = 4;
 
 // boundary handling
-typedef lbm::NoSlip< LatticeModel_T, flag_t > NoSlip_T;
+using NoSlip_T = lbm::NoSlip<LatticeModel_T, flag_t>;
 
-typedef pe_coupling::CurvedLinear< LatticeModel_T, FlagField_T > MO_T;
+using MO_T = pe_coupling::CurvedLinear<LatticeModel_T, FlagField_T>;
 
-typedef BoundaryHandling< FlagField_T, Stencil_T, NoSlip_T, MO_T > BoundaryHandling_T;
+using BoundaryHandling_T = BoundaryHandling<FlagField_T, Stencil_T, NoSlip_T, MO_T>;
 
-typedef std::tuple<pe::Sphere, pe::Ellipsoid, pe::Plane> BodyTypeTuple;
+using BodyTypeTuple = std::tuple<pe::Sphere, pe::Ellipsoid, pe::Plane>;
 
 ///////////
 // FLAGS //
@@ -128,8 +128,8 @@ template< typename LatticeModel_T, typename Filter_T >
 class VectorGradientRefinement
 {
 public:
-   typedef GhostLayerField< Vector3<real_t>, 1 >  VectorField_T;
-   typedef typename LatticeModel_T::Stencil       Stencil_T;
+   using VectorField_T = GhostLayerField<Vector3<real_t>, 1>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    VectorGradientRefinement( const ConstBlockDataID & fieldID, const Filter_T & filter,
                              const real_t upperLimit, const real_t lowerLimit, const uint_t maxLevel ) :
@@ -994,7 +994,7 @@ int main( int argc, char **argv )
    // add velocity field and utility
    BlockDataID velocityFieldID = field::addToStorage<VelocityField_T>( blocks, "velocity field", Vector3<real_t>(real_t(0)), field::zyxf, uint_t(2) );
 
-   typedef lbm::VelocityFieldWriter< PdfField_T, VelocityField_T > VelocityFieldWriter_T;
+   using VelocityFieldWriter_T = lbm::VelocityFieldWriter<PdfField_T, VelocityField_T>;
    BlockSweepWrapper< VelocityFieldWriter_T > velocityFieldWriter( blocks, VelocityFieldWriter_T( pdfFieldID, velocityFieldID ) );
 
 
@@ -1177,7 +1177,7 @@ int main( int argc, char **argv )
                                                  "Body Mapping", finestLevel );
 
    // add sweep for restoring PDFs in cells previously occupied by pe bodies
-   typedef pe_coupling::EquilibriumReconstructor< LatticeModel_T, BoundaryHandling_T > Reconstructor_T;
+   using Reconstructor_T = pe_coupling::EquilibriumReconstructor<LatticeModel_T, BoundaryHandling_T>;
    Reconstructor_T reconstructor( blocks, boundaryHandlingID, bodyFieldID );
    refinementTimestep->addPostStreamVoidFunction(lbm::refinement::SweepAsFunctorWrapper( pe_coupling::PDFReconstruction< LatticeModel_T, BoundaryHandling_T, Reconstructor_T > ( blocks, pdfFieldID,
                                                                                                                                                                                  boundaryHandlingID, bodyStorageID, globalBodyStorage, bodyFieldID, reconstructor, FormerMO_Flag, Fluid_Flag ), blocks ),
diff --git a/apps/benchmarks/AdaptiveMeshRefinementFluidParticleCoupling/WorkloadEvaluation.cpp b/apps/benchmarks/AdaptiveMeshRefinementFluidParticleCoupling/WorkloadEvaluation.cpp
index 18d93204ff732f767ff0655935355b2a3f2992c8..01f0b3f99a4b998b53a0d9f431442db5916ce1d9 100644
--- a/apps/benchmarks/AdaptiveMeshRefinementFluidParticleCoupling/WorkloadEvaluation.cpp
+++ b/apps/benchmarks/AdaptiveMeshRefinementFluidParticleCoupling/WorkloadEvaluation.cpp
@@ -86,23 +86,23 @@ using namespace walberla;
 using walberla::uint_t;
 
 // PDF field, flag field & body field
-typedef lbm::D3Q19< lbm::collision_model::TRT, false>  LatticeModel_T;
+using LatticeModel_T = lbm::D3Q19<lbm::collision_model::TRT, false>;
 
-typedef LatticeModel_T::Stencil Stencil_T;
-typedef lbm::PdfField< LatticeModel_T > PdfField_T;
+using Stencil_T = LatticeModel_T::Stencil;
+using PdfField_T = lbm::PdfField<LatticeModel_T>;
 
-typedef walberla::uint8_t                 flag_t;
-typedef FlagField< flag_t >               FlagField_T;
-typedef GhostLayerField< pe::BodyID, 1 >  BodyField_T;
+using flag_t = walberla::uint8_t;
+using FlagField_T = FlagField<flag_t>;
+using BodyField_T = GhostLayerField<pe::BodyID, 1>;
 
 const uint_t FieldGhostLayers = 1;
 
 // boundary handling
-typedef pe_coupling::CurvedLinear< LatticeModel_T, FlagField_T > MO_CLI_T;
+using MO_CLI_T = pe_coupling::CurvedLinear<LatticeModel_T, FlagField_T>;
 
-typedef BoundaryHandling< FlagField_T, Stencil_T, MO_CLI_T > BoundaryHandling_T;
+using BoundaryHandling_T = BoundaryHandling<FlagField_T, Stencil_T, MO_CLI_T>;
 
-typedef std::tuple<pe::Sphere, pe::Ellipsoid, pe::Plane> BodyTypeTuple;
+using BodyTypeTuple = std::tuple<pe::Sphere, pe::Ellipsoid, pe::Plane>;
 
 ///////////
 // FLAGS //
@@ -754,7 +754,7 @@ int main( int argc, char **argv )
          << Sweep( pe_coupling::BodyMapping< LatticeModel_T, BoundaryHandling_T >( blocks, pdfFieldID, boundaryHandlingID, bodyStorageID, globalBodyStorage, bodyFieldID, MO_CLI_Flag, FormerMO_Flag, pe_coupling::selectRegularBodies ), "Body Mapping" );
 
    // sweep for restoring PDFs in cells previously occupied by pe bodies
-   typedef pe_coupling::EquilibriumReconstructor< LatticeModel_T, BoundaryHandling_T > Reconstructor_T;
+   using Reconstructor_T = pe_coupling::EquilibriumReconstructor<LatticeModel_T, BoundaryHandling_T>;
    Reconstructor_T reconstructor( blocks, boundaryHandlingID, bodyFieldID);
    timeloop.add()
          << Sweep( pe_coupling::PDFReconstruction< LatticeModel_T, BoundaryHandling_T, Reconstructor_T >
diff --git a/apps/benchmarks/CMakeLists.txt b/apps/benchmarks/CMakeLists.txt
index 01d57440109ea02878e19231b10a36e012d93d8c..7d5901c16064c4d3b103d5af59b40db3d9aa6403 100644
--- a/apps/benchmarks/CMakeLists.txt
+++ b/apps/benchmarks/CMakeLists.txt
@@ -1,12 +1,13 @@
 add_subdirectory( AdaptiveMeshRefinementFluidParticleCoupling )
 add_subdirectory( ComplexGeometry )
 add_subdirectory( DEM )
-add_subdirectory( FieldCommunication )
 add_subdirectory( MeshDistance )
 add_subdirectory( CouetteFlow )
 add_subdirectory( FluidParticleCoupling )
+add_subdirectory( FluidParticleCouplingWithLoadBalancing )
 add_subdirectory( ForcesOnSphereNearPlaneInShearFlow )
 add_subdirectory( GranularGas )
+add_subdirectory( IntegratorAccuracy )
 add_subdirectory( LennardJones )
 add_subdirectory( NonUniformGrid )
 add_subdirectory( MotionSingleHeavySphere )
@@ -14,10 +15,20 @@ add_subdirectory( PoiseuilleChannel )
 add_subdirectory( ProbeVsExtraMessage )
 add_subdirectory( SchaeferTurek )
 add_subdirectory( UniformGrid )
-if ( WALBERLA_BUILD_WITH_CODEGEN )
-add_subdirectory( UniformGridGenerated )
-add_subdirectory( PhaseFieldAllenCahn )
-endif()
-if ( WALBERLA_BUILD_WITH_CODEGEN AND WALBERLA_BUILD_WITH_CUDA )
-add_subdirectory( UniformGridGPU )
+
+if ( WALBERLA_BUILD_WITH_PYTHON )
+   add_subdirectory( FieldCommunication )
+
+   if ( WALBERLA_BUILD_WITH_CODEGEN )
+      add_subdirectory( UniformGridGenerated )
+      add_subdirectory( PhaseFieldAllenCahn )
+   endif()
+
+   if ( WALBERLA_BUILD_WITH_CODEGEN AND WALBERLA_BUILD_WITH_CUDA )
+      add_subdirectory( UniformGridGPU )
+   endif()
+
 endif()
+
+
+
diff --git a/apps/benchmarks/CouetteFlow/CouetteFlow.cpp b/apps/benchmarks/CouetteFlow/CouetteFlow.cpp
index 4bedb57423a9e7f3f9e0267b05ec3d3f4dbf7d7b..b313738d067f0f0ad6501f4e1776d3e0ed9f2bf9 100644
--- a/apps/benchmarks/CouetteFlow/CouetteFlow.cpp
+++ b/apps/benchmarks/CouetteFlow/CouetteFlow.cpp
@@ -118,21 +118,21 @@ using walberla::real_t;
 // TYPEDEFS //
 //////////////
 
-typedef lbm::D3Q15< lbm::collision_model::SRT,      false > D3Q15_SRT_INCOMP;
-typedef lbm::D3Q15< lbm::collision_model::SRT,      true  > D3Q15_SRT_COMP;
-typedef lbm::D3Q15< lbm::collision_model::TRT,      false > D3Q15_TRT_INCOMP;
-typedef lbm::D3Q15< lbm::collision_model::TRT,      true  > D3Q15_TRT_COMP;
-
-typedef lbm::D3Q19< lbm::collision_model::SRT,      false > D3Q19_SRT_INCOMP;
-typedef lbm::D3Q19< lbm::collision_model::SRT,      true  > D3Q19_SRT_COMP;
-typedef lbm::D3Q19< lbm::collision_model::TRT,      false > D3Q19_TRT_INCOMP;
-typedef lbm::D3Q19< lbm::collision_model::TRT,      true  > D3Q19_TRT_COMP;
-typedef lbm::D3Q19< lbm::collision_model::D3Q19MRT, false > D3Q19_MRT_INCOMP;
-
-typedef lbm::D3Q27< lbm::collision_model::SRT,      false > D3Q27_SRT_INCOMP;
-typedef lbm::D3Q27< lbm::collision_model::SRT,      true  > D3Q27_SRT_COMP;
-typedef lbm::D3Q27< lbm::collision_model::TRT,      false > D3Q27_TRT_INCOMP;
-typedef lbm::D3Q27< lbm::collision_model::TRT,      true  > D3Q27_TRT_COMP;
+using D3Q15_SRT_INCOMP = lbm::D3Q15<lbm::collision_model::SRT, false>;
+using D3Q15_SRT_COMP = lbm::D3Q15<lbm::collision_model::SRT, true>;
+using D3Q15_TRT_INCOMP = lbm::D3Q15<lbm::collision_model::TRT, false>;
+using D3Q15_TRT_COMP = lbm::D3Q15<lbm::collision_model::TRT, true>;
+
+using D3Q19_SRT_INCOMP = lbm::D3Q19<lbm::collision_model::SRT, false>;
+using D3Q19_SRT_COMP = lbm::D3Q19<lbm::collision_model::SRT, true>;
+using D3Q19_TRT_INCOMP = lbm::D3Q19<lbm::collision_model::TRT, false>;
+using D3Q19_TRT_COMP = lbm::D3Q19<lbm::collision_model::TRT, true>;
+using D3Q19_MRT_INCOMP = lbm::D3Q19<lbm::collision_model::D3Q19MRT, false>;
+
+using D3Q27_SRT_INCOMP = lbm::D3Q27<lbm::collision_model::SRT, false>;
+using D3Q27_SRT_COMP = lbm::D3Q27<lbm::collision_model::SRT, true>;
+using D3Q27_TRT_INCOMP = lbm::D3Q27<lbm::collision_model::TRT, false>;
+using D3Q27_TRT_COMP = lbm::D3Q27<lbm::collision_model::TRT, true>;
 
 template< typename LatticeModel_T >
 struct Types
@@ -370,10 +370,10 @@ class MyBoundaryHandling
 {
 public:
 
-   typedef lbm::NoSlip< LatticeModel_T, flag_t >    NoSlip_T;
-   typedef lbm::SimpleUBB< LatticeModel_T, flag_t > UBB_T;
+   using NoSlip_T = lbm::NoSlip<LatticeModel_T, flag_t>;
+   using UBB_T = lbm::SimpleUBB<LatticeModel_T, flag_t>;
 
-   typedef BoundaryHandling< FlagField_T, typename Types<LatticeModel_T>::Stencil_T, NoSlip_T, UBB_T > BoundaryHandling_T;
+   using BoundaryHandling_T = BoundaryHandling<FlagField_T, typename Types<LatticeModel_T>::Stencil_T, NoSlip_T, UBB_T>;
 
 
 
@@ -616,7 +616,7 @@ struct AddRefinementTimeStep
          }
          else
          {
-            typedef lbm::SplitSweep< LatticeModel_T, FlagField_T > Sweep_T;
+            using Sweep_T = lbm::SplitSweep<LatticeModel_T, FlagField_T>;
             auto mySweep = make_shared< Sweep_T >( pdfFieldId, flagFieldId, Fluid_Flag );
 
             addRefinementTimeStep< LatticeModel_T, Sweep_T >( timeloop, blocks, pdfFieldId, boundaryHandlingId, timingPool, levelwiseTimingPool,
diff --git a/apps/benchmarks/DEM/DEM.cpp b/apps/benchmarks/DEM/DEM.cpp
index a76f4993f8514030d92c52e6d9cb09573f7ffa8c..eb892fa4cf8f29a78a183262d7e4233fc8d139d4 100644
--- a/apps/benchmarks/DEM/DEM.cpp
+++ b/apps/benchmarks/DEM/DEM.cpp
@@ -48,7 +48,7 @@ int main( int argc, char** argv )
    using namespace walberla;
    using namespace walberla::pe;
 
-   typedef std::tuple<Sphere, Plane> BodyTuple ;
+   using BodyTuple = std::tuple<Sphere, Plane> ;
 
    walberla::MPIManager::instance()->initializeMPI( &argc, &argv );
 
diff --git a/apps/benchmarks/FieldCommunication/CMakeLists.txt b/apps/benchmarks/FieldCommunication/CMakeLists.txt
index 35a2f698a4514de3e9309cce56d977a25c470230..edb815612bdf3b4ccb520349b50bf2a29d60e209 100644
--- a/apps/benchmarks/FieldCommunication/CMakeLists.txt
+++ b/apps/benchmarks/FieldCommunication/CMakeLists.txt
@@ -4,4 +4,4 @@ waLBerla_link_files_to_builddir( "*.py" )
 
 
 waLBerla_add_executable ( NAME FieldCommunication
-                          DEPENDS blockforest core domain_decomposition field postprocessing sqlite )
+                          DEPENDS blockforest core domain_decomposition field postprocessing sqlite python_coupling )
diff --git a/apps/benchmarks/FieldCommunication/FieldCommunication.cpp b/apps/benchmarks/FieldCommunication/FieldCommunication.cpp
index fabde23e097a769713564842f3993baabe0a0fab..c9b3cc87a35b7ea56d8889e7d3caf18d9da352ec 100644
--- a/apps/benchmarks/FieldCommunication/FieldCommunication.cpp
+++ b/apps/benchmarks/FieldCommunication/FieldCommunication.cpp
@@ -36,7 +36,7 @@ class SingleMessageBufferedScheme
 public:
     typedef Stencil_T Stencil;
 
-    SingleMessageBufferedScheme( const weak_ptr_wrapper< StructuredBlockForest > & bf, const int tag = 17953 )
+    SingleMessageBufferedScheme( const weak_ptr< StructuredBlockForest > & bf, const int tag = 17953 )
             : blockForest_( bf ), tag_( tag ) {}
 
     inline void addDataToCommunicate( const shared_ptr< communication::UniformPackInfo > &packInfo )
@@ -67,7 +67,7 @@ public:
 
 private:
     std::vector< shared_ptr< UniformBufferedScheme< Stencil>> > schemes_;
-    weak_ptr_wrapper< StructuredBlockForest > blockForest_;
+    weak_ptr< StructuredBlockForest > blockForest_;
     int tag_;
 };
 
diff --git a/apps/benchmarks/FluidParticleCoupling/CMakeLists.txt b/apps/benchmarks/FluidParticleCoupling/CMakeLists.txt
index a9b6e43d80e939fa74e70b740cb4ee4ad2ff868f..898352998666621d6bdfec7f0f07f5c6b4de3724 100644
--- a/apps/benchmarks/FluidParticleCoupling/CMakeLists.txt
+++ b/apps/benchmarks/FluidParticleCoupling/CMakeLists.txt
@@ -38,6 +38,10 @@ if( WALBERLA_BUILD_WITH_CODEGEN )
          DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling mesa_pd
          postprocessing timeloop vtk FluidParticleCouplingGeneratedLBM)
 
+   waLBerla_add_executable(NAME MotionSettlingSphere FILES MotionSettlingSphere.cpp
+         DEPENDS blockforest boundary core domain_decomposition field lbm lbm_mesapd_coupling mesa_pd
+         postprocessing timeloop vtk FluidParticleCouplingGeneratedLBM)
+
 else()
 
 waLBerla_add_executable ( NAME SphereWallCollision FILES SphereWallCollision.cpp
diff --git a/apps/benchmarks/FluidParticleCoupling/DragForceSphere.cpp b/apps/benchmarks/FluidParticleCoupling/DragForceSphere.cpp
index a28651957a527509cc3575172614ff2242a6c869..37921bf9cf99629576040bca5c106fd9eaf84711 100644
--- a/apps/benchmarks/FluidParticleCoupling/DragForceSphere.cpp
+++ b/apps/benchmarks/FluidParticleCoupling/DragForceSphere.cpp
@@ -79,6 +79,11 @@
 
 #ifdef WALBERLA_BUILD_WITH_CODEGEN
 #include "GeneratedLBMWithForce.h"
+
+#define USE_TRTLIKE
+//#define USE_D3Q27TRTLIKE
+//#define USE_CUMULANTTRT
+//#define USE_CUMULANT
 #endif
 
 namespace drag_force_sphere_mem
@@ -529,17 +534,27 @@ int main( int argc, char **argv )
    real_t omegaBulk = (useSRT) ? lambda_e : lbm_mesapd_coupling::omegaBulkFromOmega(omega, bulkViscRateFactor);
 
    // add omega bulk field
-   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx, uint_t(0) );
+   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx);
 
    // create the lattice model
 #ifdef WALBERLA_BUILD_WITH_CODEGEN
+
+#if defined(USE_TRTLIKE) || defined(USE_D3Q27TRTLIKE)
    WALBERLA_LOG_INFO_ON_ROOT("Using generated TRT-like lattice model!");
    WALBERLA_LOG_INFO_ON_ROOT(" - magic number " << magicNumber);
    WALBERLA_LOG_INFO_ON_ROOT(" - omegaBulk = " << omegaBulk << ", bulk visc. = " << lbm_mesapd_coupling::bulkViscosityFromOmegaBulk(omegaBulk) << " (bvrf " << bulkViscRateFactor << ")");
    WALBERLA_LOG_INFO_ON_ROOT(" - lambda_e " << lambda_e << ", lambda_d " << lambda_d << ", omegaBulk " << omegaBulk );
    WALBERLA_LOG_INFO_ON_ROOT(" - use omega bulk adaption = " << useOmegaBulkAdaption << " (adaption layer size = " << adaptionLayerSize << ")");
-
    LatticeModel_T latticeModel = LatticeModel_T(omegaBulkFieldID, setup.extForce, lambda_d, lambda_e);
+#elif defined(USE_CUMULANTTRT)
+   WALBERLA_LOG_INFO_ON_ROOT("Using generated cumulant TRT lattice model!");
+   WALBERLA_LOG_INFO_ON_ROOT(" - magic number " << magicNumber);
+   WALBERLA_LOG_INFO_ON_ROOT(" - lambda_e " << lambda_e << ", lambda_d " << lambda_d );
+   LatticeModel_T latticeModel = LatticeModel_T(setup.extForce, lambda_d, lambda_e);
+#elif defined(USE_CUMULANT)
+   LatticeModel_T latticeModel = LatticeModel_T(setup.extForce, omega);
+#endif
+
 #else
    WALBERLA_LOG_INFO_ON_ROOT("Using waLBerla built-in MRT lattice model and ignoring omega bulk field since not supported!");
    WALBERLA_LOG_INFO_ON_ROOT(" - magic number " << magicNumber);
diff --git a/apps/benchmarks/FluidParticleCoupling/ForcesOnSphereNearPlane.cpp b/apps/benchmarks/FluidParticleCoupling/ForcesOnSphereNearPlane.cpp
index ecf14716ca36f464bf00c482e0a83b8c63c41856..ee3c0f1431a0c73d137c1bd07943729bab44ba8b 100644
--- a/apps/benchmarks/FluidParticleCoupling/ForcesOnSphereNearPlane.cpp
+++ b/apps/benchmarks/FluidParticleCoupling/ForcesOnSphereNearPlane.cpp
@@ -407,6 +407,7 @@ int main( int argc, char **argv )
    bool initializeVelocityProfile = false;
    bool useOmegaBulkAdaption = false;
    real_t adaptionLayerSize = real_t(2);
+   std::string boundaryCondition = "CLI"; // SBB, CLI
 
    real_t relativeChangeConvergenceEps = real_t(1e-5);
    real_t physicalCheckingFrequency = real_t(0.1);
@@ -420,6 +421,7 @@ int main( int argc, char **argv )
       if( std::strcmp( argv[i], "--timesteps" )                 == 0 ) { maximumNonDimTimesteps = real_c( std::atof( argv[++i] ) ); continue; }
       if( std::strcmp( argv[i], "--wallDistance" )              == 0 ) { normalizedWallDistance = real_c( std::atof( argv[++i] ) ); continue; }
       if( std::strcmp( argv[i], "--Re" )                        == 0 ) { ReynoldsNumberShear = real_c( std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--boundaryCondition" )         == 0 ) { boundaryCondition = argv[++i]; continue; }
       if( std::strcmp( argv[i], "--velocity" )                  == 0 ) { wallVelocity = real_c( std::atof( argv[++i] ) ); continue; }
       if( std::strcmp( argv[i], "--xOffset" )                   == 0 ) { xOffsetOfSpherePosition = real_c( std::atof( argv[++i] ) ); continue; }
       if( std::strcmp( argv[i], "--yOffset" )                   == 0 ) { yOffsetOfSpherePosition = real_c( std::atof( argv[++i] ) ); continue; }
@@ -438,6 +440,7 @@ int main( int argc, char **argv )
    WALBERLA_CHECK_GREATER_EQUAL(normalizedWallDistance, real_t(0.5));
    WALBERLA_CHECK_GREATER_EQUAL(ReynoldsNumberShear, real_t(0));
    WALBERLA_CHECK_GREATER_EQUAL(diameter, real_t(0));
+   WALBERLA_CHECK(boundaryCondition == "SBB" || boundaryCondition == "CLI");
 
    //////////////////////////
    // NUMERICAL PARAMETERS //
@@ -484,6 +487,7 @@ int main( int argc, char **argv )
    WALBERLA_LOG_INFO_ON_ROOT(" - use omega bulk adaption = " << useOmegaBulkAdaption << " (adaption layer size = " << adaptionLayerSize << ")");
    WALBERLA_LOG_INFO_ON_ROOT(" - sphere diameter = " << diameter << ", position = " << initialPosition << " ( xOffset = " << xOffsetOfSpherePosition << ", yOffset = " << yOffsetOfSpherePosition << " )");
    WALBERLA_LOG_INFO_ON_ROOT(" - base folder VTK = " << baseFolderVTK << ", base folder logging = " << baseFolderLogging );
+   WALBERLA_LOG_INFO_ON_ROOT(" - boundary condition = " << boundaryCondition );
 
    ///////////////////////////
    // BLOCK STRUCTURE SETUP //
@@ -562,7 +566,7 @@ int main( int argc, char **argv )
    ////////////////////////
 
    // add omega bulk field
-   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx, uint_t(0) );
+   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx);
 
    // create the lattice model
    real_t lambda_e = lbm::collision_model::TRT::lambda_e( omega );
@@ -594,11 +598,21 @@ int main( int argc, char **argv )
    lbm_mesapd_coupling::MovingParticleMappingKernel<BoundaryHandling_T> movingParticleMappingKernel(blocks, boundaryHandlingID, particleFieldID);
    lbm_mesapd_coupling::AverageHydrodynamicForceTorqueKernel averageHydrodynamicForceTorque;
 
-   // map sphere into the LBM simulation
-   ps->forEachParticle(false, lbm_mesapd_coupling::RegularParticlesSelector(), *accessor, movingParticleMappingKernel, *accessor, CLI_Flag);
+   if(boundaryCondition == "CLI")
+   {
+      // map sphere into the LBM simulation
+      ps->forEachParticle(false, lbm_mesapd_coupling::RegularParticlesSelector(), *accessor, movingParticleMappingKernel, *accessor, CLI_Flag);
+      // map planes
+      ps->forEachParticle(false, lbm_mesapd_coupling::GlobalParticlesSelector(), *accessor, movingParticleMappingKernel, *accessor, CLI_Flag);
+   }
+   else
+   {
+      // map sphere into the LBM simulation
+      ps->forEachParticle(false, lbm_mesapd_coupling::RegularParticlesSelector(), *accessor, movingParticleMappingKernel, *accessor, SBB_Flag);
+      // map planes
+      ps->forEachParticle(false, lbm_mesapd_coupling::GlobalParticlesSelector(), *accessor, movingParticleMappingKernel, *accessor, SBB_Flag);
+   }
 
-   // map planes
-   ps->forEachParticle(false, lbm_mesapd_coupling::GlobalParticlesSelector(), *accessor, movingParticleMappingKernel, *accessor, CLI_Flag);
 
    // initialize Couette velocity profile in whole domain
    if(initializeVelocityProfile)
@@ -661,13 +675,13 @@ int main( int argc, char **argv )
    timeloop.add() << Sweep( makeSharedSweep( lbmSweep ), "cell-wise LB sweep" );
 #endif
 
-
    // add force evaluation and logging
    real_t normalizationFactor = math::pi / real_t(8) * densityFluid * shearRate * shearRate * wallDistance * wallDistance * diameter * diameter ;
    std::string loggingFileName( baseFolderLogging + "/LoggingForcesNearPlane");
    loggingFileName += "_D" + std::to_string(uint_c(diameter));
    loggingFileName += "_Re" + std::to_string(uint_c(ReynoldsNumberShear));
    loggingFileName += "_WD" + std::to_string(uint_c(normalizedWallDistance * real_t(1000)));
+   loggingFileName += "_" + boundaryCondition;
    loggingFileName += "_bvrf" + std::to_string(uint_c(bulkViscRateFactor));
    if(useOmegaBulkAdaption) loggingFileName += "_uOBA" + std::to_string(uint_c(adaptionLayerSize));
    loggingFileName += "_mn" + std::to_string(magicNumber);
@@ -796,6 +810,7 @@ int main( int argc, char **argv )
    resultFileName += "_D" + std::to_string(uint_c(diameter));
    resultFileName += "_Re" + std::to_string(uint_c(ReynoldsNumberShear));
    resultFileName += "_WD" + std::to_string(uint_c(normalizedWallDistance * real_t(1000)));
+   resultFileName += "_" + boundaryCondition;
    resultFileName += "_bvrf" + std::to_string(uint_c(bulkViscRateFactor));
    if(useOmegaBulkAdaption) resultFileName += "_uOBA" + std::to_string(uint_c(adaptionLayerSize));
    resultFileName += "_mn" + std::to_string(magicNumber);
diff --git a/apps/benchmarks/FluidParticleCoupling/GeneratedLBM.py b/apps/benchmarks/FluidParticleCoupling/GeneratedLBM.py
index 7992bcdd92bbbbd99cd4a3ad9a948dbcd8eb7e8d..9772c5ba12edab54835a9a6ccc6dbab9c156443b 100644
--- a/apps/benchmarks/FluidParticleCoupling/GeneratedLBM.py
+++ b/apps/benchmarks/FluidParticleCoupling/GeneratedLBM.py
@@ -1,46 +1,140 @@
 import sympy as sp
+from sympy.core.cache import clear_cache
 import pystencils as ps
 from lbmpy.creationfunctions import create_lb_method_from_existing, create_lb_method
 from lbmpy_walberla import generate_lattice_model
 from pystencils_walberla import CodeGeneration
+from pystencils_walberla import get_vectorize_instruction_set
 
 from lbmpy.creationfunctions import create_lb_collision_rule
 from lbmpy.moments import is_even, get_order, MOMENT_SYMBOLS
 from lbmpy.stencils import get_stencil
 
+from collections import OrderedDict
 
 with CodeGeneration() as ctx:
 
-    omegaVisc = sp.Symbol("omega_visc")
-    omegaBulk = ps.fields("omega_bulk: [3D]", layout='fzyx')
-    omegaMagic = sp.Symbol("omega_magic")
-    stencil = get_stencil("D3Q19", 'walberla')
+    generatedMethod = "TRTlike"
+    #generatedMethod = "D3Q27TRTlike"
+    #generatedMethod = "cumulant"
 
-    x, y, z = MOMENT_SYMBOLS
-    one = sp.Rational(1, 1)
-    sq = x ** 2 + y ** 2 + z ** 2
-    moments = [
-        [one, x, y, z],  # [0, 3, 5, 7]
-        [sq - 1],  # [1]
-        [3 * sq ** 2 - 6 * sq + 1],  # [2]
-        [(3 * sq - 5) * x, (3 * sq - 5) * y, (3 * sq - 5) * z],  # [4, 6, 8]
-        [3 * x ** 2 - sq, y ** 2 - z ** 2, x * y, y * z, x * z],  # [9, 11, 13, 14, 15]
-        [(2 * sq - 3) * (3 * x ** 2 - sq), (2 * sq - 3) * (y ** 2 - z ** 2)],  # [10, 12]
-        [(y ** 2 - z ** 2) * x, (z ** 2 - x ** 2) * y, (x ** 2 - y ** 2) * z]  # [16, 17, 18]
-    ]
+    clear_cache()
 
-    # relaxation rate for first group of moments (1,x,y,z) is set to zero internally
-    relaxation_rates=[omegaBulk.center_vector, omegaBulk.center_vector, omegaMagic, omegaVisc, omegaVisc, omegaMagic]
+    cpu_vectorize_info = {'instruction_set': get_vectorize_instruction_set(ctx)}
 
-    methodWithForce = create_lb_method(stencil=stencil, method='mrt', maxwellian_moments=False,
-                                       nested_moments=moments, relaxation_rates=relaxation_rates)
+    if generatedMethod == "TRTlike":
+        omegaVisc = sp.Symbol("omega_visc")
+        omegaBulk = ps.fields("omega_bulk: [3D]", layout='fzyx')
+        omegaMagic = sp.Symbol("omega_magic")
+        stencil = get_stencil("D3Q19", 'walberla')
 
-    #print(methodWithForce.relaxation_rates)
-    #print(methodWithForce.moment_matrix)
+        x, y, z = MOMENT_SYMBOLS
+        one = sp.Rational(1, 1)
+        sq = x ** 2 + y ** 2 + z ** 2
+        moments = [
+            [one, x, y, z],  # [0, 3, 5, 7]
+            [sq - 1],  # [1]
+            [3 * sq ** 2 - 6 * sq + 1],  # [2]
+            [(3 * sq - 5) * x, (3 * sq - 5) * y, (3 * sq - 5) * z],  # [4, 6, 8]
+            [3 * x ** 2 - sq, y ** 2 - z ** 2, x * y, y * z, x * z],  # [9, 11, 13, 14, 15]
+            [(2 * sq - 3) * (3 * x ** 2 - sq), (2 * sq - 3) * (y ** 2 - z ** 2)],  # [10, 12]
+            [(y ** 2 - z ** 2) * x, (z ** 2 - x ** 2) * y, (x ** 2 - y ** 2) * z]  # [16, 17, 18]
+        ]
 
-    collision_rule = create_lb_collision_rule(lb_method=methodWithForce, optimization={'cse_global': True})
-    generate_lattice_model(ctx, 'GeneratedLBM', collision_rule, field_layout='fzyx')
+        # relaxation rate for first group of moments (1,x,y,z) is set to zero internally
+        relaxation_rates=[omegaBulk.center_vector, omegaBulk.center_vector, omegaMagic, omegaVisc, omegaVisc, omegaMagic]
 
+        methodWithForce = create_lb_method(stencil=stencil, method='mrt', maxwellian_moments=False,
+                                           nested_moments=moments, relaxation_rates=relaxation_rates)
+
+        #print(methodWithForce.relaxation_rates)
+        #print(methodWithForce.moment_matrix)
+        collision_rule = create_lb_collision_rule(lb_method=methodWithForce, optimization={'cse_global': True})
+        generate_lattice_model(ctx, 'GeneratedLBM', collision_rule, field_layout='fzyx', cpu_vectorize_info=cpu_vectorize_info)
+
+ 
+    if generatedMethod == "D3Q27TRTlike":
+
+        omegaVisc = sp.Symbol("omega_visc")
+        omegaBulk = ps.fields("omega_bulk: [3D]", layout='fzyx')
+        omegaMagic = sp.Symbol("omega_magic")
+        stencil = get_stencil("D3Q27", 'walberla')
+
+        relaxation_rates=[omegaVisc, omegaBulk.center_vector, omegaMagic, omegaVisc, omegaMagic, omegaVisc]
+
+        methodWithForce = create_lb_method(stencil=stencil, method='mrt', maxwellian_moments=False,weighted=True, compressible=False,
+                                           relaxation_rates=relaxation_rates)
+
+        collision_rule = create_lb_collision_rule(lb_method=methodWithForce, optimization={'cse_global': True})
+        generate_lattice_model(ctx, 'GeneratedLBM', collision_rule, field_layout='fzyx', cpu_vectorize_info=cpu_vectorize_info)
+
+    if generatedMethod == "cumulant":
+
+        x,y,z = MOMENT_SYMBOLS
+
+        cumulants = [0] * 27
+
+        cumulants[0] = sp.sympify(1) #000
+
+        cumulants[1] = x #100
+        cumulants[2] = y #010
+        cumulants[3] = z #001
+
+        cumulants[4] = x*y #110
+        cumulants[5] = x*z #101
+        cumulants[6] = y*z #011
+
+        cumulants[7] = x**2 - y**2 #200 - 020
+        cumulants[8] = x**2 - z**2 #200 - 002
+        cumulants[9] = x**2 + y**2 + z**2 #200 + 020 + 002
+
+        cumulants[10] = x*y**2 + x*z**2 #120 + 102
+        cumulants[11] = x**2*y + y*z**2 #210 + 012
+        cumulants[12] = x**2*z + y**2*z #201 + 021
+        cumulants[13] = x*y**2 - x*z**2 #120 - 102
+        cumulants[14] = x**2*y - y*z**2 #210 - 012
+        cumulants[15] = x**2*z - y**2*z #201 - 021
+
+        cumulants[16] = x*y*z #111
+
+        cumulants[17] = x**2*y**2 - 2*x**2*z**2 + y**2*z**2 # 220- 2*202 +022
+        cumulants[18] = x**2*y**2 + x**2*z**2 - 2*y**2*z**2 # 220 + 202 - 2*002
+        cumulants[19] = x**2*y**2 + x**2*z**2 + y**2*z**2 # 220 + 202 + 022
+
+        cumulants[20] = x**2*y*z # 211
+        cumulants[21] = x*y**2*z # 121
+        cumulants[22] = x*y*z**2 # 112
+
+        cumulants[23] = x**2*y**2*z # 221
+        cumulants[24] = x**2*y*z**2 # 212
+        cumulants[25] = x*y**2*z**2 # 122
+
+        cumulants[26] = x**2*y**2*z**2 # 222
+
+        def get_relaxation_rate(cumulant, omega):
+            if get_order(cumulant) <= 1:
+                return 0
+            elif get_order(cumulant) == 2 and cumulant != x**2 + y**2 + z**2:
+                return omega
+            else:
+                return 1
+
+        stencil = get_stencil("D3Q27", 'walberla')
+
+        omega = sp.Symbol("omega")
+        rr_dict = OrderedDict((c, get_relaxation_rate(c, omega))
+                              for c in cumulants)
+
+        from lbmpy.methods import create_with_continuous_maxwellian_eq_moments
+        my_method = create_with_continuous_maxwellian_eq_moments(stencil, rr_dict, cumulant=True, compressible=True,)
+
+        collision_rule = create_lb_collision_rule(lb_method=my_method,
+                                                  optimization={"cse_global": True,
+                                                                "cse_pdfs": False})
+
+        print(my_method.relaxation_rates)
+
+        generate_lattice_model(ctx, 'GeneratedLBM', collision_rule, field_layout='fzyx', cpu_vectorize_info=cpu_vectorize_info)
 
 
 
diff --git a/apps/benchmarks/FluidParticleCoupling/GeneratedLBMWithForce.py b/apps/benchmarks/FluidParticleCoupling/GeneratedLBMWithForce.py
index 9162d1cfeb55b6f850292ea3ce7fa6edac6c5aef..6f720252539ee4be59565902042128239b94611f 100644
--- a/apps/benchmarks/FluidParticleCoupling/GeneratedLBMWithForce.py
+++ b/apps/benchmarks/FluidParticleCoupling/GeneratedLBMWithForce.py
@@ -1,45 +1,215 @@
 import sympy as sp
+from sympy.core.cache import clear_cache
 import pystencils as ps
-from lbmpy.creationfunctions import create_lb_method_from_existing, create_lb_method
+from lbmpy.creationfunctions import create_lb_method_from_existing, create_lb_ast, create_lb_method
 from lbmpy_walberla import generate_lattice_model
 from pystencils_walberla import CodeGeneration
+from pystencils_walberla import get_vectorize_instruction_set
 
 from lbmpy.creationfunctions import create_lb_collision_rule
-from lbmpy.moments import is_even, get_order, MOMENT_SYMBOLS
+from lbmpy.moments import MOMENT_SYMBOLS, is_even, get_order
 from lbmpy.stencils import get_stencil
 from lbmpy.forcemodels import Luo
 
+from collections import OrderedDict
+
 with CodeGeneration() as ctx:
 
     forcing=(sp.symbols("fx"),0,0)
-    omegaVisc = sp.Symbol("omega_visc")
-    omegaBulk = ps.fields("omega_bulk: [3D]", layout='fzyx')
-    omegaMagic = sp.Symbol("omega_magic")
-    stencil = get_stencil("D3Q19", 'walberla')
-
     forcemodel=Luo(forcing)
 
-    x, y, z = MOMENT_SYMBOLS
-    one = sp.Rational(1, 1)
-    sq = x ** 2 + y ** 2 + z ** 2
-    moments = [
-        [one, x, y, z],  # [0, 3, 5, 7]
-        [sq - 1],  # [1]
-        [3 * sq ** 2 - 6 * sq + 1],  # [2]
-        [(3 * sq - 5) * x, (3 * sq - 5) * y, (3 * sq - 5) * z],  # [4, 6, 8]
-        [3 * x ** 2 - sq, y ** 2 - z ** 2, x * y, y * z, x * z],  # [9, 11, 13, 14, 15]
-        [(2 * sq - 3) * (3 * x ** 2 - sq), (2 * sq - 3) * (y ** 2 - z ** 2)],  # [10, 12]
-        [(y ** 2 - z ** 2) * x, (z ** 2 - x ** 2) * y, (x ** 2 - y ** 2) * z]  # [16, 17, 18]
-    ]
-
-    # relaxation rate for first group of moments (1,x,y,z) is set to zero internally
-    relaxation_rates=[omegaBulk.center_vector, omegaBulk.center_vector, omegaMagic, omegaVisc, omegaVisc, omegaMagic]
-
-    methodWithForce = create_lb_method(stencil=stencil, method='mrt', maxwellian_moments=False,
-                                       force_model=forcemodel, nested_moments=moments, relaxation_rates=relaxation_rates)
-
-    #print(methodWithForce.relaxation_rates)
-    #print(methodWithForce.moment_matrix)
-
-    collision_rule = create_lb_collision_rule(lb_method=methodWithForce, optimization={'cse_global': True})
-    generate_lattice_model(ctx, 'GeneratedLBMWithForce', collision_rule, field_layout='fzyx')
+    generatedMethod = "TRTlike"
+    #generatedMethod = "D3Q27TRTlike"
+    #generatedMethod = "cumulant"
+    #generatedMethod = "cumulantTRT"
+
+    print("Generating " + generatedMethod + " LBM method")
+
+    clear_cache()
+
+    cpu_vectorize_info = {'instruction_set': get_vectorize_instruction_set(ctx)}
+
+    if generatedMethod == "TRTlike":
+        omegaVisc = sp.Symbol("omega_visc")
+        omegaBulk = ps.fields("omega_bulk: [3D]", layout='fzyx')
+        omegaMagic = sp.Symbol("omega_magic")
+        stencil = get_stencil("D3Q19", 'walberla')
+
+        x, y, z = MOMENT_SYMBOLS
+        one = sp.Rational(1, 1)
+        sq = x ** 2 + y ** 2 + z ** 2
+        moments = [
+            [one, x, y, z],  # [0, 3, 5, 7]
+            [sq - 1],  # [1]
+            [3 * sq ** 2 - 6 * sq + 1],  # [2]
+            [(3 * sq - 5) * x, (3 * sq - 5) * y, (3 * sq - 5) * z],  # [4, 6, 8]
+            [3 * x ** 2 - sq, y ** 2 - z ** 2, x * y, y * z, x * z],  # [9, 11, 13, 14, 15]
+            [(2 * sq - 3) * (3 * x ** 2 - sq), (2 * sq - 3) * (y ** 2 - z ** 2)],  # [10, 12]
+            [(y ** 2 - z ** 2) * x, (z ** 2 - x ** 2) * y, (x ** 2 - y ** 2) * z]  # [16, 17, 18]
+        ]
+
+        # relaxation rate for first group of moments (1,x,y,z) is set to zero internally
+        relaxation_rates=[omegaBulk.center_vector, omegaBulk.center_vector, omegaMagic, omegaVisc, omegaVisc, omegaMagic]
+
+        methodWithForce = create_lb_method(stencil=stencil, method='mrt', maxwellian_moments=False,
+                                           force_model=forcemodel, nested_moments=moments, relaxation_rates=relaxation_rates)
+
+        #print(methodWithForce.relaxation_rates)
+        #print(methodWithForce.moment_matrix)
+        collision_rule = create_lb_collision_rule(lb_method=methodWithForce, optimization={'cse_global': True})
+        generate_lattice_model(ctx, 'GeneratedLBMWithForce', collision_rule, field_layout='fzyx', cpu_vectorize_info=cpu_vectorize_info)
+ 
+    if generatedMethod == "D3Q27TRTlike":
+
+        omegaVisc = sp.Symbol("omega_visc")
+        omegaBulk = ps.fields("omega_bulk: [3D]", layout='fzyx')
+        omegaMagic = sp.Symbol("omega_magic")
+        stencil = get_stencil("D3Q27", 'walberla')
+
+        relaxation_rates=[omegaVisc, omegaBulk.center_vector, omegaMagic, omegaVisc, omegaMagic, omegaVisc]
+
+        methodWithForce = create_lb_method(stencil=stencil, method='mrt', maxwellian_moments=False,weighted=True, compressible=False,
+                                           force_model=forcemodel, relaxation_rates=relaxation_rates)
+
+        collision_rule = create_lb_collision_rule(lb_method=methodWithForce, optimization={'cse_global': True})
+        generate_lattice_model(ctx, 'GeneratedLBMWithForce', collision_rule, field_layout='fzyx', cpu_vectorize_info=cpu_vectorize_info)
+
+    if generatedMethod == "cumulant":
+
+        x,y,z = MOMENT_SYMBOLS
+
+        cumulants = [0] * 27
+
+        cumulants[0] = sp.sympify(1) #000
+
+        cumulants[1] = x #100
+        cumulants[2] = y #010
+        cumulants[3] = z #001
+
+        cumulants[4] = x*y #110
+        cumulants[5] = x*z #101
+        cumulants[6] = y*z #011
+
+        cumulants[7] = x**2 - y**2 #200 - 020
+        cumulants[8] = x**2 - z**2 #200 - 002
+        cumulants[9] = x**2 + y**2 + z**2 #200 + 020 + 002
+
+        cumulants[10] = x*y**2 + x*z**2 #120 + 102
+        cumulants[11] = x**2*y + y*z**2 #210 + 012
+        cumulants[12] = x**2*z + y**2*z #201 + 021
+        cumulants[13] = x*y**2 - x*z**2 #120 - 102
+        cumulants[14] = x**2*y - y*z**2 #210 - 012
+        cumulants[15] = x**2*z - y**2*z #201 - 021
+
+        cumulants[16] = x*y*z #111
+
+        cumulants[17] = x**2*y**2 - 2*x**2*z**2 + y**2*z**2 # 220- 2*202 +022
+        cumulants[18] = x**2*y**2 + x**2*z**2 - 2*y**2*z**2 # 220 + 202 - 2*002
+        cumulants[19] = x**2*y**2 + x**2*z**2 + y**2*z**2 # 220 + 202 + 022
+
+        cumulants[20] = x**2*y*z # 211
+        cumulants[21] = x*y**2*z # 121
+        cumulants[22] = x*y*z**2 # 112
+
+        cumulants[23] = x**2*y**2*z # 221
+        cumulants[24] = x**2*y*z**2 # 212
+        cumulants[25] = x*y**2*z**2 # 122
+
+        cumulants[26] = x**2*y**2*z**2 # 222
+
+        def get_relaxation_rate(cumulant, omega):
+            if get_order(cumulant) <= 1:
+                return 0
+            elif get_order(cumulant) == 2 and cumulant != x**2 + y**2 + z**2:
+                return omega
+            else:
+                return 1
+
+        stencil = get_stencil("D3Q27", 'walberla')
+
+        omega = sp.Symbol("omega")
+        rr_dict = OrderedDict((c, get_relaxation_rate(c, omega))
+                              for c in cumulants)
+
+        from lbmpy.methods import create_with_continuous_maxwellian_eq_moments
+        my_method = create_with_continuous_maxwellian_eq_moments(stencil, rr_dict, cumulant=True, compressible=True, force_model=forcemodel)
+
+        collision_rule = create_lb_collision_rule(lb_method=my_method,
+                                                  optimization={"cse_global": True,
+                                                                "cse_pdfs": False})
+
+        print(my_method.relaxation_rates)
+
+        generate_lattice_model(ctx, 'GeneratedLBMWithForce', collision_rule, field_layout='fzyx', cpu_vectorize_info=cpu_vectorize_info)
+
+
+    if generatedMethod == "cumulantTRT":
+
+        x,y,z = MOMENT_SYMBOLS
+
+        cumulants = [0] * 27
+
+        cumulants[0] = sp.sympify(1) #000
+
+        cumulants[1] = x #100
+        cumulants[2] = y #010
+        cumulants[3] = z #001
+
+        cumulants[4] = x*y #110
+        cumulants[5] = x*z #101
+        cumulants[6] = y*z #011
+
+        cumulants[7] = x**2 - y**2 #200 - 020
+        cumulants[8] = x**2 - z**2 #200 - 002
+        cumulants[9] = x**2 + y**2 + z**2 #200 + 020 + 002
+
+        cumulants[10] = x*y**2 + x*z**2 #120 + 102
+        cumulants[11] = x**2*y + y*z**2 #210 + 012
+        cumulants[12] = x**2*z + y**2*z #201 + 021
+        cumulants[13] = x*y**2 - x*z**2 #120 - 102
+        cumulants[14] = x**2*y - y*z**2 #210 - 012
+        cumulants[15] = x**2*z - y**2*z #201 - 021
+
+        cumulants[16] = x*y*z #111
+
+        cumulants[17] = x**2*y**2 - 2*x**2*z**2 + y**2*z**2 # 220- 2*202 +022
+        cumulants[18] = x**2*y**2 + x**2*z**2 - 2*y**2*z**2 # 220 + 202 - 2*002
+        cumulants[19] = x**2*y**2 + x**2*z**2 + y**2*z**2 # 220 + 202 + 022
+
+        cumulants[20] = x**2*y*z # 211
+        cumulants[21] = x*y**2*z # 121
+        cumulants[22] = x*y*z**2 # 112
+
+        cumulants[23] = x**2*y**2*z # 221
+        cumulants[24] = x**2*y*z**2 # 212
+        cumulants[25] = x*y**2*z**2 # 122
+
+        cumulants[26] = x**2*y**2*z**2 # 222
+
+        def get_relaxation_rate(cumulant, omegaVisc, omegaMagic):
+            if get_order(cumulant) <= 1:
+                return 0
+            elif is_even(cumulant):
+                return omegaVisc
+            else:
+                return omegaMagic
+
+        stencil = get_stencil("D3Q27", 'walberla')
+
+        omegaVisc = sp.Symbol("omega_visc")
+        omegaMagic = sp.Symbol("omega_magic")
+
+        rr_dict = OrderedDict((c, get_relaxation_rate(c, omegaVisc, omegaMagic))
+                              for c in cumulants)
+
+        from lbmpy.methods import create_with_continuous_maxwellian_eq_moments
+        my_method = create_with_continuous_maxwellian_eq_moments(stencil, rr_dict, cumulant=True, compressible=True, force_model=forcemodel)
+
+        collision_rule = create_lb_collision_rule(lb_method=my_method,
+                                                  optimization={"cse_global": True,
+                                                                "cse_pdfs": False})
+
+        print(my_method.relaxation_rates)
+
+        generate_lattice_model(ctx, 'GeneratedLBMWithForce', collision_rule, field_layout='fzyx', cpu_vectorize_info=cpu_vectorize_info)
+
diff --git a/apps/benchmarks/FluidParticleCoupling/LubricationForceEvaluation.cpp b/apps/benchmarks/FluidParticleCoupling/LubricationForceEvaluation.cpp
index dcb7737fe5c8809802e22a3bd5802fd8321a2369..e17092473fdbc3524286da109ec2b93552c71dd5 100644
--- a/apps/benchmarks/FluidParticleCoupling/LubricationForceEvaluation.cpp
+++ b/apps/benchmarks/FluidParticleCoupling/LubricationForceEvaluation.cpp
@@ -52,6 +52,7 @@
 #include "lbm_mesapd_coupling/mapping/ParticleMapping.h"
 #include "lbm_mesapd_coupling/momentum_exchange_method/MovingParticleMapping.h"
 #include "lbm_mesapd_coupling/momentum_exchange_method/boundary/CurvedLinear.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/boundary/SimpleBB.h"
 #include "lbm_mesapd_coupling/utility/ParticleSelector.h"
 #include "lbm_mesapd_coupling/utility/ResetHydrodynamicForceTorqueKernel.h"
 #include "lbm_mesapd_coupling/utility/LubricationCorrectionKernel.h"
@@ -115,7 +116,8 @@ const uint_t FieldGhostLayers = 1;
 
 const FlagUID Fluid_Flag( "fluid" );
 const FlagUID FreeSlip_Flag( "free slip" );
-const FlagUID MO_Flag( "moving obstacle" );
+const FlagUID MO_SBB_Flag( "moving obstacle sbb" );
+const FlagUID MO_CLI_Flag( "moving obstacle cli" );
 const FlagUID FormerMO_Flag( "former moving obstacle" );
 
 /////////////////////////////////////
@@ -127,8 +129,9 @@ class MyBoundaryHandling
 public:
 
    using FreeSlip_T = lbm::FreeSlip< LatticeModel_T, FlagField_T>;
-   using MO_T = lbm_mesapd_coupling::CurvedLinear< LatticeModel_T, FlagField_T, ParticleAccessor_T >;
-   using Type = BoundaryHandling< FlagField_T, Stencil_T, FreeSlip_T, MO_T >;
+   using MO_SBB_T = lbm_mesapd_coupling::SimpleBB< LatticeModel_T, FlagField_T, ParticleAccessor_T >;
+   using MO_CLI_T = lbm_mesapd_coupling::CurvedLinear< LatticeModel_T, FlagField_T, ParticleAccessor_T >;
+   using Type = BoundaryHandling< FlagField_T, Stencil_T, FreeSlip_T, MO_SBB_T, MO_CLI_T >;
 
    MyBoundaryHandling( const BlockDataID & flagFieldID, const BlockDataID & pdfFieldID,
                        const BlockDataID & particleFieldID, const shared_ptr<ParticleAccessor_T>& ac) :
@@ -147,7 +150,8 @@ public:
 
       Type * handling = new Type( "moving obstacle boundary handling", flagField, fluid,
                                   FreeSlip_T( "FreeSlip", FreeSlip_Flag, pdfField, flagField, fluid ),
-                                  MO_T( "MO", MO_Flag, pdfField, flagField, particleField, ac_, fluid, *storage, *block ) );
+                                  MO_SBB_T( "SBB", MO_SBB_Flag, pdfField, flagField, particleField, ac_, fluid, *storage, *block ),
+                                  MO_CLI_T( "CLI", MO_CLI_Flag, pdfField, flagField, particleField, ac_, fluid, *storage, *block )  );
 
       const auto freeslip = flagField->getFlag( FreeSlip_Flag );
 
@@ -237,6 +241,7 @@ int main( int argc, char **argv )
    real_t magicNumber = real_t(3)/real_t(16);
    bool useOmegaBulkAdaption = false;
    real_t adaptionLayerSize = real_t(2);
+   bool useSBB = false;
 
    // 1: translation in normal direction -> normal Lubrication force
    // 2: translation in tangential direction -> tangential Lubrication force and torque
@@ -246,19 +251,20 @@ int main( int argc, char **argv )
 
    for( int i = 1; i < argc; ++i )
    {
-      if( std::strcmp( argv[i], "--sphWallTest" )               == 0 ) { sphSphTest = false; continue; }
-      if( std::strcmp( argv[i], "--noLogging" )                 == 0 ) { fileIO = false; continue; }
-      if( std::strcmp( argv[i], "--vtkIOFreq" )                 == 0 ) { vtkIOFreq = uint_c( std::atof( argv[++i] ) ); continue; }
-      if( std::strcmp( argv[i], "--setup" )                 == 0 ) { setup = uint_c( std::atof( argv[++i] ) ); continue; }
-      if( std::strcmp( argv[i], "--baseFolder" )                == 0 ) { baseFolder = argv[++i]; continue; }
-      if( std::strcmp( argv[i], "--diameter" )                  == 0 ) { radius = real_t(0.5)*real_c(std::atof(argv[++i])); continue; }
+      if( std::strcmp( argv[i], "--sphWallTest" )          == 0 ) { sphSphTest = false; continue; }
+      if( std::strcmp( argv[i], "--noLogging" )            == 0 ) { fileIO = false; continue; }
+      if( std::strcmp( argv[i], "--vtkIOFreq" )            == 0 ) { vtkIOFreq = uint_c( std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--setup" )                == 0 ) { setup = uint_c( std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--baseFolder" )           == 0 ) { baseFolder = argv[++i]; continue; }
+      if( std::strcmp( argv[i], "--diameter" )             == 0 ) { radius = real_t(0.5)*real_c(std::atof(argv[++i])); continue; }
       if( std::strcmp( argv[i], "--gapSize" )              == 0 ) { gapSize = real_c(std::atof(argv[++i])); continue; }
-      if( std::strcmp( argv[i], "--bulkViscRateFactor" )        == 0 ) { bulkViscRateFactor = real_c(std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--bulkViscRateFactor" )   == 0 ) { bulkViscRateFactor = real_c(std::atof( argv[++i] ) ); continue; }
       if( std::strcmp( argv[i], "--tau" )                  == 0 ) { tau = real_c(std::atof( argv[++i] ) ); continue; }
-      if( std::strcmp( argv[i], "--fileName" )                  == 0 ) { fileNameEnding = argv[++i]; continue; }
-      if( std::strcmp( argv[i], "--magicNumber" )               == 0 ) { magicNumber = real_c(std::atof(argv[++i])); continue; }
-      if( std::strcmp( argv[i], "--useOmegaBulkAdaption" )      == 0 ) { useOmegaBulkAdaption = true; continue; }
-      if( std::strcmp( argv[i], "--adaptionLayerSize" )         == 0 ) { adaptionLayerSize = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--fileName" )             == 0 ) { fileNameEnding = argv[++i]; continue; }
+      if( std::strcmp( argv[i], "--magicNumber" )          == 0 ) { magicNumber = real_c(std::atof(argv[++i])); continue; }
+      if( std::strcmp( argv[i], "--useOmegaBulkAdaption" ) == 0 ) { useOmegaBulkAdaption = true; continue; }
+      if( std::strcmp( argv[i], "--adaptionLayerSize" )    == 0 ) { adaptionLayerSize = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--useSBB" )               == 0 ) { useSBB = true; continue; }
       WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
    }
 
@@ -315,6 +321,7 @@ int main( int argc, char **argv )
          << " time steps   = " << timesteps << "\n"
          << " fStokes      = " << fStokes << "\n"
          << " setup        = " << setup << "\n"
+         << " useSBB       = " << useSBB << "\n"
          << "-------------------------------------------------------\n"
          << " domainSize = " << xSize << " x " << ySize << " x " << zSize  << "\n"
          << " blocks     = " << xBlocks << " x " << yBlocks << " x " << zBlocks  << "\n"
@@ -374,7 +381,7 @@ int main( int argc, char **argv )
          } else if (setup == 2)
          {
             // only tangential translational velocity
-            p.setLinearVelocity(Vector3<real_t>( real_t(0), velocity, real_t(0)));
+            p.setLinearVelocity(Vector3<real_t>( real_t(0), real_t(0), velocity));
             p.setAngularVelocity(Vector3<real_t>( real_t(0), real_t(0), real_t(0)));
          } else if (setup == 3)
          {
@@ -406,7 +413,7 @@ int main( int argc, char **argv )
          } else if (setup == 2)
          {
             // only tangential translational velocity
-            p.setLinearVelocity(Vector3<real_t>( real_t(0), -velocity, real_t(0)));
+            p.setLinearVelocity(Vector3<real_t>( real_t(0), real_t(0), -velocity));
             p.setAngularVelocity(Vector3<real_t>( real_t(0), real_t(0), real_t(0)));
          } else if (setup == 3)
          {
@@ -493,7 +500,7 @@ int main( int argc, char **argv )
    ////////////////////////
 
    // add omega bulk field
-   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx, uint_t(0) );
+   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx);
 
    // create the lattice model
    real_t lambda_e = lbm::collision_model::TRT::lambda_e( omega );
@@ -546,7 +553,13 @@ int main( int argc, char **argv )
    ///////////////
 
    // map particles into the LBM simulation
-   ps->forEachParticle(false, mesa_pd::kernel::SelectAll(), *accessor, movingParticleMappingKernel, *accessor, MO_Flag);
+   if(useSBB)
+   {
+      ps->forEachParticle(false, mesa_pd::kernel::SelectAll(), *accessor, movingParticleMappingKernel, *accessor, MO_SBB_Flag);
+   } else
+   {
+      ps->forEachParticle(false, mesa_pd::kernel::SelectAll(), *accessor, movingParticleMappingKernel, *accessor, MO_CLI_Flag);
+   }
 
    // create the timeloop
    SweepTimeloop timeloop( blocks->getBlockStorage(), timesteps );
@@ -637,7 +650,7 @@ int main( int argc, char **argv )
    real_t curTorqueNorm = real_t(0);
    real_t oldTorqueNorm = real_t(0);
 
-   real_t convergenceLimit = real_t(1e-4);
+   real_t convergenceLimit = real_t(1e-5);
 
    // time loop
    for (uint_t i = 1; i <= timesteps; ++i )
@@ -747,6 +760,7 @@ int main( int argc, char **argv )
       loggingFileName += "_bvrf" + std::to_string(uint_c(bulkViscRateFactor));
       loggingFileName += "_mn" + std::to_string(float(magicNumber));
       if( useOmegaBulkAdaption ) loggingFileName += "_uOBA" + std::to_string(uint_c(adaptionLayerSize));
+      if( useSBB ) loggingFileName += "_SBB";
       if( !fileNameEnding.empty()) loggingFileName += "_" + fileNameEnding;
       loggingFileName += ".txt";
 
diff --git a/apps/benchmarks/FluidParticleCoupling/MotionSettlingSphere.cpp b/apps/benchmarks/FluidParticleCoupling/MotionSettlingSphere.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..69f6a438e94b659c2f55d94373066770e3b3f8be
--- /dev/null
+++ b/apps/benchmarks/FluidParticleCoupling/MotionSettlingSphere.cpp
@@ -0,0 +1,1378 @@
+//======================================================================================================================
+//
+//  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 MotionSettlingSphere.cpp
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#include "boundary/all.h"
+
+#include "blockforest/all.h"
+
+#include "core/all.h"
+
+#include "domain_decomposition/all.h"
+
+#include "field/AddToStorage.h"
+#include "field/communication/PackInfo.h"
+#include "field/vtk/all.h"
+
+#include "lbm/boundary/all.h"
+#include "lbm/communication/PdfFieldPackInfo.h"
+#include "lbm/field/AddToStorage.h"
+#include "lbm/field/PdfField.h"
+#include "lbm/lattice_model/D3Q19.h"
+#include "lbm/sweeps/CellwiseSweep.h"
+#include "lbm/sweeps/SweepWrappers.h"
+#include "lbm/vtk/all.h"
+
+#include "lbm_mesapd_coupling/mapping/ParticleMapping.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/MovingParticleMapping.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/boundary/SimpleBB.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/boundary/CurvedLinear.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/reconstruction/Reconstructor.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/reconstruction/ExtrapolationDirectionFinder.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/reconstruction/PdfReconstructionManager.h"
+#include "lbm_mesapd_coupling/utility/AddForceOnParticlesKernel.h"
+#include "lbm_mesapd_coupling/utility/ParticleSelector.h"
+#include "lbm_mesapd_coupling/DataTypes.h"
+#include "lbm_mesapd_coupling/utility/AverageHydrodynamicForceTorqueKernel.h"
+#include "lbm_mesapd_coupling/utility/AddHydrodynamicInteractionKernel.h"
+#include "lbm_mesapd_coupling/utility/ResetHydrodynamicForceTorqueKernel.h"
+#include "lbm_mesapd_coupling/utility/OmegaBulkAdaption.h"
+#include "lbm_mesapd_coupling/utility/InspectionProbe.h"
+#include "lbm_mesapd_coupling/utility/InitializeHydrodynamicForceTorqueForAveragingKernel.h"
+
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/ParticleAccessorWithShape.h"
+#include "mesa_pd/data/ShapeStorage.h"
+#include "mesa_pd/data/DataTypes.h"
+#include "mesa_pd/data/shape/Sphere.h"
+#include "mesa_pd/domain/BlockForestDomain.h"
+#include "mesa_pd/domain/BlockForestDataHandling.h"
+#include "mesa_pd/kernel/DoubleCast.h"
+#include "mesa_pd/kernel/ParticleSelector.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
+#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/mpi/SyncNextNeighbors.h"
+#include "mesa_pd/mpi/SyncGhostOwners.h"
+#include "mesa_pd/mpi/ReduceProperty.h"
+#include "mesa_pd/mpi/notifications/ForceTorqueNotification.h"
+#include "mesa_pd/mpi/notifications/HydrodynamicForceTorqueNotification.h"
+#include "mesa_pd/vtk/ParticleVtkOutput.h"
+
+#include "stencil/D3Q27.h"
+
+#include "timeloop/SweepTimeloop.h"
+
+#include "vtk/BlockCellDataWriter.h"
+#include "vtk/Initialization.h"
+#include "vtk/VTKOutput.h"
+
+#include <functional>
+#include <memory>
+
+#ifdef WALBERLA_BUILD_WITH_CODEGEN
+#include "GeneratedLBM.h"
+#endif
+
+namespace motion_settling_sphere{
+
+///////////
+// USING //
+///////////
+
+using namespace walberla;
+using walberla::uint_t;
+
+//////////////
+// TYPEDEFS //
+//////////////
+
+using namespace walberla;
+using walberla::uint_t;
+
+#ifdef WALBERLA_BUILD_WITH_CODEGEN
+using LatticeModel_T = lbm::GeneratedLBM;
+#else
+using LatticeModel_T = lbm::D3Q19< lbm::collision_model::D3Q19MRT>;
+#endif
+
+using Stencil_T = LatticeModel_T::Stencil;
+using PdfField_T = lbm::PdfField<LatticeModel_T>;
+
+using flag_t = walberla::uint8_t;
+using FlagField_T = FlagField<flag_t>;
+
+using ScalarField_T = GhostLayerField< real_t, 1>;
+
+const uint_t FieldGhostLayers = 1;
+
+///////////
+// FLAGS //
+///////////
+
+const FlagUID Fluid_Flag   ( "fluid" );
+
+const FlagUID UBB_Flag     ( "velocity bounce back" );
+const FlagUID Outlet_Flag  ( "outlet" );
+
+const FlagUID MEM_BB_Flag   ( "moving obstacle BB" );
+const FlagUID MEM_CLI_Flag  ( "moving obstacle CLI" );
+const FlagUID FormerMEM_Flag( "former moving obstacle" );
+
+
+//////////////////////////////
+// Coupling algorithm enums //
+//////////////////////////////
+enum MEMVariant { BB, CLI, MR };
+
+MEMVariant to_MEMVariant( const std::string& s )
+{
+   if( s == "BB"  ) return MEMVariant::BB;
+   if( s == "CLI" ) return MEMVariant::CLI;
+   throw std::runtime_error("invalid conversion from text to MEMVariant");
+}
+
+std::string MEMVariant_to_string ( const MEMVariant& m )
+{
+   if( m == MEMVariant::BB  ) return "BB";
+   if( m == MEMVariant::CLI ) return "CLI";
+   throw std::runtime_error("invalid conversion from MEMVariant to string");
+}
+
+
+/////////////////////////////////////
+// BOUNDARY HANDLING CUSTOMIZATION //
+/////////////////////////////////////
+template <typename ParticleAccessor_T>
+class MyBoundaryHandling
+{
+public:
+
+   using UBB_T = lbm::SimpleUBB< LatticeModel_T, flag_t >;
+   using Outlet_T = lbm::SimplePressure< LatticeModel_T, flag_t >;
+   using MO_SBB_T = lbm_mesapd_coupling::SimpleBB< LatticeModel_T, FlagField_T, ParticleAccessor_T >;
+   using MO_CLI_T = lbm_mesapd_coupling::CurvedLinear< LatticeModel_T, FlagField_T, ParticleAccessor_T >;
+   using Type = BoundaryHandling< FlagField_T, Stencil_T, UBB_T, Outlet_T, MO_SBB_T, MO_CLI_T >;
+
+
+   MyBoundaryHandling( const BlockDataID & flagFieldID, const BlockDataID & pdfFieldID,
+                       const BlockDataID & particleFieldID, const shared_ptr<ParticleAccessor_T>& ac,
+                       Vector3<real_t> uInfty) :
+      flagFieldID_( flagFieldID ), pdfFieldID_( pdfFieldID ), particleFieldID_( particleFieldID ),
+      ac_( ac ), velocity_( uInfty )
+   {}
+
+   Type * operator()( IBlock* const block, const StructuredBlockStorage* const storage ) const
+   {
+      WALBERLA_ASSERT_NOT_NULLPTR( block );
+      WALBERLA_ASSERT_NOT_NULLPTR( storage );
+
+      FlagField_T * flagField = block->getData< FlagField_T >( flagFieldID_ );
+      PdfField_T *  pdfField  = block->getData< PdfField_T > ( pdfFieldID_ );
+      auto * particleField = block->getData< lbm_mesapd_coupling::ParticleField_T > ( particleFieldID_ );
+
+      const auto fluid = flagField->flagExists( Fluid_Flag ) ? flagField->getFlag( Fluid_Flag ) : flagField->registerFlag( Fluid_Flag );
+
+      Type * handling = new Type( "moving obstacle boundary handling", flagField, fluid,
+                                  UBB_T( "UBB", UBB_Flag, pdfField, velocity_),
+                                  Outlet_T( "Outlet", Outlet_Flag, pdfField, real_t(1) ),
+                                  MO_SBB_T (  "MEM_BB",  MEM_BB_Flag, pdfField, flagField, particleField, ac_, fluid, *storage, *block ),
+                                  MO_CLI_T( "MEM_CLI", MEM_CLI_Flag, pdfField, flagField, particleField, ac_, fluid, *storage, *block )  );
+
+      const auto ubb = flagField->getFlag( UBB_Flag );
+      const auto outlet = flagField->getFlag( Outlet_Flag );
+
+      CellInterval domainBB = storage->getDomainCellBB();
+
+      domainBB.xMin() -= cell_idx_c( FieldGhostLayers ); // -1
+      domainBB.xMax() += cell_idx_c( FieldGhostLayers ); // cellsX
+
+      domainBB.yMin() -= cell_idx_c( FieldGhostLayers ); // 0
+      domainBB.yMax() += cell_idx_c( FieldGhostLayers ); // cellsY+1
+
+      domainBB.zMin() -= cell_idx_c( FieldGhostLayers ); // 0
+      domainBB.zMax() += cell_idx_c( FieldGhostLayers ); // cellsZ+1
+
+      // BOTTOM
+      // -1........-1........-1
+      // cellsX+1..cellsY+1..-1
+      CellInterval bottom( domainBB.xMin(), domainBB.yMin(), domainBB.zMin(), domainBB.xMax(), domainBB.yMax(), domainBB.zMin() );
+      storage->transformGlobalToBlockLocalCellInterval( bottom, *block );
+      handling->forceBoundary( ubb, bottom );
+
+      // TOP
+      // -1........-1........cellsZ+1
+      // cellsX+1..cellsY+1..cellsZ+1
+      CellInterval top( domainBB.xMin(), domainBB.yMin(), domainBB.zMax(), domainBB.xMax(), domainBB.yMax(), domainBB.zMax() );
+      storage->transformGlobalToBlockLocalCellInterval( top, *block );
+      handling->forceBoundary( outlet, top );
+
+      handling->fillWithDomain( FieldGhostLayers );
+
+      return handling;
+   }
+
+private:
+   const BlockDataID flagFieldID_;
+   const BlockDataID pdfFieldID_;
+   const BlockDataID particleFieldID_;
+
+   shared_ptr<ParticleAccessor_T> ac_;
+   const Vector3<real_t> velocity_;
+
+}; // class MyBoundaryHandling
+
+/*
+ * Functionality for continuous logging of sphere properties during initial (resting) simulation
+ */
+template< typename ParticleAccessor_T>
+class RestingSphereForceEvaluator
+{
+public:
+   RestingSphereForceEvaluator( const shared_ptr< ParticleAccessor_T > & ac, walberla::id_t sphereUid,
+                                uint_t averageFrequency, const std::string & basefolder ) :
+         ac_( ac ), sphereUid_( sphereUid ), averageFrequency_( averageFrequency )
+   {
+      WALBERLA_ROOT_SECTION() {
+         // initially write header in file
+         std::ofstream file;
+         filename_ = basefolder;
+         filename_ += "/log_init.txt";
+         file.open(filename_.c_str());
+         file << "# f_z_current f_z_average f_x f_y\n";
+         file.close();
+      }
+   }
+
+   // evaluate the acting drag force on a fixed sphere
+   void operator()(const uint_t timestep)
+   {
+
+      // average the force over averageFrequency consecutive timesteps since there are small fluctuations in the force
+      auto currentForce = getForce();
+      currentAverage_ += currentForce[2];
+
+      WALBERLA_ROOT_SECTION()
+      {
+         std::ofstream file;
+         file.open( filename_.c_str(), std::ofstream::app );
+         file.precision(8);
+         file << timestep << "\t" << currentForce[2] << "\t" << dragForceNew_
+              << "\t" << currentForce[0] << "\t" << currentForce[1] << std::endl;
+         file.close();
+      }
+
+      if( timestep % averageFrequency_ != 0) return;
+
+      dragForceOld_ = dragForceNew_;
+      dragForceNew_ = currentAverage_ / real_c( averageFrequency_ );
+      currentAverage_ = real_t(0);
+
+   }
+
+   // Return the relative temporal change in the drag force
+   real_t getForceDiff() const
+   {
+      return std::fabs( ( dragForceNew_ - dragForceOld_ ) / dragForceNew_ );
+   }
+
+   real_t getDragForce() const
+   {
+      return dragForceNew_;
+   }
+
+private:
+
+   // Obtain the drag force acting on the sphere
+   Vector3<real_t> getForce()
+   {
+      Vector3<real_t> force(real_t( 0 ));
+      size_t idx = ac_->uidToIdx(sphereUid_);
+      if( idx != ac_->getInvalidIdx())
+      {
+         if(!mesa_pd::data::particle_flags::isSet( ac_->getFlags(idx), mesa_pd::data::particle_flags::GHOST))
+         {
+            force = ac_->getHydrodynamicForce(idx);
+         }
+      }
+
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( force[0], mpi::SUM );
+         mpi::allReduceInplace( force[1], mpi::SUM );
+         mpi::allReduceInplace( force[2], mpi::SUM );
+      }
+      return force;
+   }
+
+   shared_ptr< ParticleAccessor_T > ac_;
+   const walberla::id_t sphereUid_;
+
+   real_t currentAverage_ = real_t(0);
+   uint_t averageFrequency_;
+   std::string filename_;
+   real_t dragForceOld_ = real_t(0);
+   real_t dragForceNew_ = real_t(0);
+
+};
+
+/*
+ * Functionality for continuous logging of sphere properties during moving simulation
+ */
+template< typename ParticleAccessor_T>
+class MovingSpherePropertyEvaluator
+{
+public:
+   MovingSpherePropertyEvaluator( const shared_ptr< ParticleAccessor_T > & ac, walberla::id_t sphereUid, const std::string & basefolder,
+                                  const Vector3<real_t> & u_infty, const real_t Galileo, const real_t GalileoSim,
+                                  const real_t gravity, const real_t viscosity, const real_t diameter,
+                                  const real_t densityRatio ) :
+         ac_( ac ), sphereUid_( sphereUid ),
+         u_infty_( u_infty ), gravity_( gravity ), viscosity_( viscosity ),
+      diameter_( diameter ), densityRatio_( densityRatio )
+   {
+
+      WALBERLA_ROOT_SECTION()
+      {
+         std::ofstream fileSetup;
+         std::string filenameSetup = basefolder;
+         filenameSetup += "/setup.txt";
+         fileSetup.open( filenameSetup.c_str() );
+
+         fileSetup << "# setup parameters: \n";
+         fileSetup << "processes = " << MPIManager::instance()->numProcesses() << "\n";
+         fileSetup << "Galileo number (targeted) = " << Galileo << "\n";
+         fileSetup << "Galileo number (simulated) = " << GalileoSim << "\n";
+         fileSetup << "gravity = " << gravity << "\n";
+         fileSetup << "viscosity = " << viscosity << "\n";
+         fileSetup << "diameter = " << diameter << "\n";
+         fileSetup << "uInfty = " << u_infty_[2] << "\n";
+         real_t u_ref = std::sqrt( std::fabs(densityRatio - real_t(1)) * gravity * diameter );
+         fileSetup << "u_{ref} = " << u_ref << "\n";
+         fileSetup.close();
+      }
+
+      filenameRes_ = basefolder;
+      filenameRes_ += "/log_results.txt";
+      filenameAdd_ = basefolder;
+      filenameAdd_ += "/log_additional.txt";
+
+      WALBERLA_ROOT_SECTION()
+      {
+         // initially write header in file
+         std::ofstream fileRes;
+         fileRes.open( filenameRes_.c_str() );
+         // raw data
+         fileRes << "#\t x_p_x\t x_p_y\t x_p_z\t u_p_x\t u_p_y\t u_p_z\t omega_p_x\t omega_p_y\t omega_p_z\t u_{pr}_x\t u_{pr}_y\t u_{pr}_z\t ";
+         // processed data, 14 - 18
+         fileRes << "Re\t u_{pV}\t u_{pH}\t omega_{pV}\t omega_{pH}\t alpha\n";
+         fileRes.close();
+
+         std::ofstream fileAdd;
+         fileAdd.open( filenameAdd_.c_str() );
+         fileAdd << "# forceX forceY forceZ torqueX torqueY torqueZ\n";
+         fileAdd.close();
+      }
+   }
+
+   // evaluate and write the sphere properties
+   void operator()(const uint_t timestep)
+   {
+      Vector3<real_t> transVel( real_t(0) );
+      Vector3<real_t> angularVel( real_t(0) );
+      Vector3<real_t> pos( real_t(0) );
+
+      Vector3<real_t> force( real_t(0) );
+      Vector3<real_t> torque( real_t(0) );
+
+      size_t idx = ac_->uidToIdx(sphereUid_);
+      if( idx != ac_->getInvalidIdx())
+      {
+         if(!mesa_pd::data::particle_flags::isSet( ac_->getFlags(idx), mesa_pd::data::particle_flags::GHOST))
+         {
+            pos = ac_->getPosition(idx);
+            transVel = ac_->getLinearVelocity(idx);
+            angularVel = ac_->getAngularVelocity(idx);
+            force = ac_->getHydrodynamicForce(idx);
+            torque = ac_->getHydrodynamicTorque(idx);
+         }
+      }
+
+      std::vector<real_t> particlePos(3);
+      particlePos[0]=pos[0]; particlePos[1]=pos[1]; particlePos[2]=pos[2];
+
+      std::vector<real_t> particleTransVel(3);
+      particleTransVel[0]=transVel[0]; particleTransVel[1]=transVel[1]; particleTransVel[2]=transVel[2];
+
+      std::vector<real_t> particleAngularVel(3);
+      particleAngularVel[0]=angularVel[0]; particleAngularVel[1]=angularVel[1]; particleAngularVel[2]=angularVel[2];
+
+      std::vector<real_t> particleForce(3);
+      particleForce[0]=force[0]; particleForce[1]=force[1]; particleForce[2]=force[2];
+
+      std::vector<real_t> particleTorque(3);
+      particleTorque[0]=torque[0]; particleTorque[1]=torque[1]; particleTorque[2]=torque[2];
+
+      // reduce to root
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( particlePos, mpi::SUM );
+         mpi::reduceInplace( particleTransVel, mpi::SUM );
+         mpi::reduceInplace( particleAngularVel, mpi::SUM );
+         mpi::reduceInplace( particleForce, mpi::SUM );
+         mpi::reduceInplace( particleTorque, mpi::SUM );
+      }
+
+      particleHeight_ = particlePos[2];
+
+      //only root process has all the data
+      WALBERLA_ROOT_SECTION()
+      {
+
+         Vector3<real_t> u_p_r( particleTransVel[0] - u_infty_[0], particleTransVel[1] - u_infty_[1], particleTransVel[2] - u_infty_[2]);
+         real_t u_p_H = std::sqrt( u_p_r[0] * u_p_r[0] + u_p_r[1] * u_p_r[1]);
+         real_t u_p_V = u_p_r[2];
+
+         real_t omega_p_H = std::sqrt( particleAngularVel[0] * particleAngularVel[0] + particleAngularVel[1] * particleAngularVel[1] );
+         real_t omega_p_V = particleAngularVel[2];
+
+         real_t u_ref = std::sqrt( std::fabs(densityRatio_ - real_t(1)) * gravity_ * diameter_ );
+         real_t Reynolds = u_p_r.length() * diameter_ / viscosity_;
+
+         // results
+         std::ofstream fileRes;
+         fileRes.open( filenameRes_.c_str(), std::ofstream::app );
+         fileRes.precision(8);
+         fileRes << timestep
+                 << "\t" << particlePos[0] << "\t" << particlePos[1] << "\t" << particlePos[2]
+                 << "\t" << particleTransVel[0] << "\t" << particleTransVel[1] << "\t" << particleTransVel[2]
+                 << "\t" << particleAngularVel[0] << "\t" << particleAngularVel[1] << "\t" << particleAngularVel[2]
+                 << "\t" << u_p_r[0] << "\t" << u_p_r[1] << "\t" << u_p_r[2]
+                 << "\t" << Reynolds << "\t" << u_p_V/u_ref << "\t" << u_p_H/u_ref
+                 << "\t" << omega_p_V*(diameter_/u_ref) << "\t" << omega_p_H*(diameter_/u_ref)
+                 << "\t" << std::atan( u_p_H/std::fabs(u_p_V) )
+                 << std::endl;
+         fileRes.close();
+
+         // additional
+         std::ofstream fileAdd;
+         fileAdd.open( filenameAdd_.c_str(), std::ofstream::app );
+         fileAdd.precision(8);
+         fileAdd << timestep
+                 << "\t" << particleForce[0] << "\t" << particleForce[1] << "\t" << particleForce[2]
+                 << "\t" << particleTorque[0] << "\t" << particleTorque[1] << "\t" << particleTorque[2]
+                 << std::endl;
+         fileAdd.close();
+      }
+   }
+
+   real_t getParticleHeight() const
+   {
+      return particleHeight_;
+   }
+
+private:
+
+   shared_ptr< ParticleAccessor_T > ac_;
+   const walberla::id_t sphereUid_;
+
+   std::string filenameRes_;
+   std::string filenameAdd_;
+
+   Vector3<real_t> u_infty_;
+   real_t gravity_;
+   real_t viscosity_;
+   real_t diameter_;
+   real_t densityRatio_;
+
+   real_t particleHeight_;
+
+};
+
+/*
+ * Result evaluation for the written VTK files
+ *
+ * input: vel (fluid velocity), u_p (particle velocity), u_infty (inflow velocity)
+ *
+ * needed data:
+ * relative flow velocity: vel_r = vel - u_p
+ * relative particle velocity: u_p_r = u_p - u_infty
+ *
+ * magnitude of relative particle velocity in horizontal plane: u_p_H = sqrt( (u_p_r)_x^2 + (u_p_r)_y^2 )
+ * unit vector of particle motion in horizontal plane: e_p_H = ( (u_p_r)_x, (u_p_r)_y, 0) / u_p_H
+ * unit vector perpendicular to e_p_H and e_z:  e_p_Hz_perp = ( -(u_p_r)_y, (u_p_r)_x, 0) / u_p_H
+ * unit vector of particle motion: e_p_parallel = u_p_r / ||u_p_r||
+ * unit vector perpendicular to e_p_parallel and e_p_Hz_perp: e_p_perp = e_p_Hz_perp x e_p_parallel
+ * projected fluid velocities: vel_r_parallel = vel_r * (-e_p_parallel)
+ *                             vel_r_perp     = vel_r * e_p_perp
+ *                             vel_r_Hz_perp  = vel_r * e_p_Hz_perp
+ */
+template< typename ParticleAccessor_T>
+class VTKInfoLogger
+{
+public:
+
+   VTKInfoLogger( SweepTimeloop* timeloop, const shared_ptr< ParticleAccessor_T > & ac, walberla::id_t sphereUid,
+         const std::string & baseFolder, const Vector3<real_t>& u_infty ) :
+         timeloop_( timeloop ), ac_( ac ), sphereUid_( sphereUid ), baseFolder_( baseFolder ), u_infty_( u_infty )
+   { }
+
+   void operator()()
+   {
+      Vector3<real_t> u_p( real_t(0) );
+
+      size_t idx = ac_->uidToIdx(sphereUid_);
+      if( idx != ac_->getInvalidIdx())
+      {
+         if(!mesa_pd::data::particle_flags::isSet( ac_->getFlags(idx), mesa_pd::data::particle_flags::GHOST))
+         {
+            u_p = ac_->getLinearVelocity(idx);
+         }
+      }
+
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( u_p[0], mpi::SUM );
+         mpi::allReduceInplace( u_p[1], mpi::SUM );
+         mpi::allReduceInplace( u_p[2], mpi::SUM );
+
+      }
+
+      Vector3<real_t> u_p_r = u_p - u_infty_;
+      real_t u_p_r_sqr_mag = u_p_r.sqrLength();
+
+      real_t u_p_H = std::sqrt( u_p_r[0] * u_p_r[0] + u_p_r[1] * u_p_r[1]);
+
+      Vector3<real_t> e_p_H (u_p_r[0], u_p_r[1], real_t(0));
+      e_p_H /= u_p_H;
+      Vector3<real_t> e_p_Hz_perp (-u_p_r[1], u_p_r[0], real_t(0));
+      e_p_Hz_perp /= u_p_H;
+
+      Vector3<real_t> e_p_parallel = u_p_r / u_p_r.length();
+      Vector3<real_t> e_p_perp     = e_p_Hz_perp%e_p_parallel;
+
+      WALBERLA_ROOT_SECTION()
+      {
+         std::ofstream file;
+
+         std::string filename(baseFolder_);
+         filename += "/log_vtk/log_vtk_";
+         filename += std::to_string( timeloop_->getCurrentTimeStep() );
+         filename += ".txt";
+
+         file.open( filename.c_str() );
+         file.precision(8);
+
+         file << "u_p = "           << u_p << "\n";
+         file << "u_p_r_2 = "       << u_p_r_sqr_mag << "\n\n";
+
+         file << "e_{pH} = "        << e_p_H        << "\n";
+         file << "e_{pparallel} = " << e_p_parallel << "\n";
+         file << "e_{pperp} =  "    << e_p_perp     << "\n";
+         file << "e_{pHzperp} = "   << e_p_Hz_perp  << "\n";
+
+         file.close();
+      }
+   }
+
+private:
+
+   SweepTimeloop* timeloop_;
+
+   shared_ptr< ParticleAccessor_T > ac_;
+   const walberla::id_t sphereUid_;
+
+   std::string baseFolder_;
+   Vector3<real_t> u_infty_;
+
+};
+
+
+/*
+ * This extensive, physical test case simulates a single, heavy sphere in ambient fluid flow.
+ * It is based on the benchmark proposed in
+ * Uhlman, Dusek - "The motion of a single heavy sphere in ambient fluid: A benchmark for interface-resolved
+ *                  particulate flow simulations with significant relative velocities" IJMF (2014),
+ *                  doi: 10.1016/j.ijmultiphaseflow.2013.10.010
+ * Results for LBM done with waLBerla are published in
+ * Rettinger, Ruede - "A comparative study of fluid-particle coupling methods for fully resolved
+ *                     lattice Boltzmann simulations" CaF (2017),
+ *                     doi: 10.1016/j.compfluid.2017.05.033
+ * The setup and the benchmark itself are explained in detail in these two papers.
+ * Several logging files are written to evaluate the benchmark quantities.
+ *
+ * This case is the same as can be found in apps/benchmarks/MotionSingleHeavySphere
+ * but using the lbm_mesapd_coupling, instead of the pe_coupling.
+ * Compared to this previous work, significantly different outcomes can be observed when using the CLI boundary condition.
+ * This can be traced back to the distinct way of force averaging.
+ * The force averaging applied in the previous study (two LBM steps, then average) introduces a error in the Galilean invariance.
+ * Surprisingly, this error can result in better agreement to the reference solutions.
+ *
+ */
+int main( int argc, char **argv )
+{
+   debug::enterTestMode();
+
+   mpi::Environment env( argc, argv );
+
+   bool noViscosityIterations  = false;
+   bool vtkIOInit = false;
+   bool longDom   = false;
+   bool broadDom   = false;
+   bool useOmegaBulkAdaption = false;
+   real_t bulkViscRateFactor = real_t(1);
+   uint_t averageForceTorqueOverTwoTimeStepsMode = 1; // 0: no, 1: running, 2: 2LBM
+   bool conserveMomentum = false;
+   bool useDiffusiveScaling = true;
+   bool useVelocityVerlet   = true;
+   bool initFluidVelocity = true;
+   bool vtkOutputGhostlayers = false;
+
+   MEMVariant memVariant = MEMVariant::BB;
+   std::string reconstructorType = "Grad"; // Eq, EAN, Ext, Grad
+
+   real_t diameter = real_t(18);
+   uint_t XBlocks = uint_t(4);
+   uint_t YBlocks = uint_t(4);
+
+   real_t viscosity = real_t(0.01); // used in diffusive scaling
+   real_t uIn = real_t(0.02); // used for acoustic scaling
+
+   /*
+    * 0: custom
+    * 1: Ga = 144
+    * 2: Ga = 178.46
+    * 3: Ga = 190
+    * 4: Ga = 250
+    */
+   uint_t simulationCase = 1;
+   real_t Galileo = real_t(1);
+   real_t Re_target = real_t(1);
+   real_t timestepsNonDim = real_t(1);
+
+
+   real_t vtkWriteSpacingNonDim = real_t(3); // write every 3 non-dim timesteps
+   uint_t vtkNumOutputFiles = 10; // write only 10 vtk files: the 10 final ones
+
+   std::string folderEnding = "";
+
+   ////////////////////////////
+   // COMMAND LINE ARGUMENTS //
+   ////////////////////////////
+
+   for( int i = 1; i < argc; ++i )
+   {
+      if( std::strcmp( argv[i], "--case"   ) == 0 ) {simulationCase = uint_c( std::atof( argv[++i] ) ); continue;}
+      if( std::strcmp( argv[i], "--Ga"  ) == 0 ){Galileo = real_c( std::atof( argv[++i] ) ); continue;}
+      if( std::strcmp( argv[i], "--Re"  ) == 0 ){Re_target = real_c( std::atof( argv[++i] ) ); continue;}
+      if( std::strcmp( argv[i], "--timestepsNonDim"  ) == 0 ){timestepsNonDim = real_c( std::atof( argv[++i] ) ); continue;}
+      if( std::strcmp( argv[i], "--diameter"  ) == 0 ){diameter = real_c( std::atof( argv[++i] ) ); continue;}
+      if( std::strcmp( argv[i], "--noViscosityIterations" ) == 0 ){noViscosityIterations = true; continue;}
+      if( std::strcmp( argv[i], "--viscosity"  ) == 0 ){viscosity = real_c( std::atof( argv[++i] ) ); continue;}
+      if( std::strcmp( argv[i], "--uIn"  ) == 0 ){uIn = real_c( std::atof( argv[++i] ) ); continue;}
+      if( std::strcmp( argv[i], "--vtkIOInit" ) == 0 ){vtkIOInit = true; continue;}
+      if( std::strcmp( argv[i], "--longDom"   ) == 0 ){longDom = true; continue;}
+      if( std::strcmp( argv[i], "--broadDom"   ) == 0 ){broadDom = true; continue;}
+      if( std::strcmp( argv[i], "--variant"    ) == 0 ){memVariant = to_MEMVariant( argv[++i] ); continue;}
+      if( std::strcmp( argv[i], "--useOmegaBulkAdaption"   ) == 0 ){useOmegaBulkAdaption = true; continue;}
+      if( std::strcmp( argv[i], "--bvrf"   ) == 0 ){bulkViscRateFactor = real_c( std::atof( argv[++i] ) ); continue;}
+      if( std::strcmp( argv[i], "--forceTorqueAverageMode"   ) == 0 ){averageForceTorqueOverTwoTimeStepsMode = uint_c( std::atof( argv[++i] ) ); continue;}
+      if( std::strcmp( argv[i], "--conserveMomentum"   ) == 0 ){conserveMomentum = true; continue;}
+      if( std::strcmp( argv[i], "--useDiffusiveScaling"   ) == 0 ){useDiffusiveScaling = true; continue;}
+      if( std::strcmp( argv[i], "--XBlocks"   ) == 0 ){XBlocks = uint_c( std::atof( argv[++i] ) ); continue;}
+      if( std::strcmp( argv[i], "--YBlocks"   ) == 0 ){YBlocks = uint_c( std::atof( argv[++i] ) ); continue;}
+      if( std::strcmp( argv[i], "--folderEnding"   ) == 0 ){folderEnding = argv[++i]; continue;}
+      if( std::strcmp( argv[i], "--reconstructorType" ) == 0 ) {reconstructorType = argv[++i]; continue;}
+      if( std::strcmp( argv[i], "--useEuler" )   == 0 ) {useVelocityVerlet = false; continue;}
+      if( std::strcmp( argv[i], "--noFluidVelocityInit" )   == 0 ) {initFluidVelocity = false; continue;}
+      if( std::strcmp( argv[i], "--vtkOutputGhostlayers" )   == 0 ) {vtkOutputGhostlayers = true; continue;}
+      if( std::strcmp( argv[i], "--vtkNumOutputFiles"   ) == 0 ){vtkNumOutputFiles = uint_c( std::atof( argv[++i] ) ); continue;}
+      if( std::strcmp( argv[i], "--vtkWriteSpacingNonDim"  ) == 0 ){vtkWriteSpacingNonDim = real_c( std::atof( argv[++i] ) ); continue;}
+      WALBERLA_ABORT("command line argument unknown: " << argv[i] );
+   }
+
+   ///////////////////////////
+   // SIMULATION PROPERTIES //
+   ///////////////////////////
+
+   const real_t radius = real_t(0.5) * diameter;
+   uint_t xlength = uint_c( diameter * real_t(5.34) );
+   uint_t ylength = xlength;
+   uint_t zlength = uint_c( diameter * real_t(16) );
+
+   if(broadDom)
+   {
+      xlength *= uint_t(2);
+      ylength *= uint_t(2);
+   }
+
+   if(longDom)
+   {
+      zlength *= uint_t(3);
+   }
+
+
+   // estimate Reynolds number (i.e. inflow velocity) based on Galileo number
+   // values are taken from the original simulation of Uhlmann, Dusek
+   switch( simulationCase )
+   {
+   case 0:
+      WALBERLA_LOG_INFO_ON_ROOT("Using custom simulation case -> you have to provide Ga, Re, and time steps via command line arguments!");
+      break;
+   case 1:
+      Galileo = real_t(144);
+      Re_target = real_t(185.08);
+      timestepsNonDim = real_t(100);
+      break;
+   case 2:
+      Galileo = real_t(178.46);
+      Re_target = real_t(243.01);
+      timestepsNonDim = real_t(250);
+      break;
+   case 3:
+      Galileo = real_t(190);
+      Re_target = real_t(262.71);
+      timestepsNonDim = real_t(250);
+      break;
+   case 4:
+      Galileo = real_t(250);
+      Re_target = real_t(365.10);
+      timestepsNonDim = real_t(510);
+      break;
+   default:
+      WALBERLA_ABORT("Simulation case is not supported!");
+   }
+
+   if( useDiffusiveScaling)
+   {
+      // estimate fitting inflow velocity (diffusive scaling, viscosity is fixed)
+      // attention: might lead to large velocities for small diameters
+      uIn = Re_target * viscosity / diameter;
+   } else {
+      // estimate fitting viscosity (acoustic scaling: velocity is fixed)
+      // note that this is different to the approach taking in the paper, where an diffusive scaling with visc = 0.01 is taken
+      // attention: might lead to small viscosities for small diameters
+      viscosity = uIn * diameter / Re_target;
+   }
+
+
+   real_t omega = lbm::collision_model::omegaFromViscosity(viscosity);
+   real_t omegaBulk = lbm_mesapd_coupling::omegaBulkFromOmega(omega, bulkViscRateFactor);
+
+   Vector3<real_t> uInfty = Vector3<real_t>( real_t(0), real_t(0), uIn );
+
+   const real_t densityRatio = real_t(1.5);
+
+   const uint_t averageFrequency = uint_c( ( ( uint_c(Galileo) >= 200) ? real_t(500) : real_t(2) ) * diameter / uIn ); // for initial simulation
+   const real_t convergenceLimit = real_t(1e-4);
+   const real_t convergenceLimitGalileo = real_t(1e-4);
+   const real_t dx = real_t(1);
+   const real_t magicNumberTRT = lbm::collision_model::TRT::threeSixteenth;
+
+   const uint_t timestepsInit = uint_c( ( ( uint_c(Galileo) >= 200) ? real_t(3000) : real_t(100) ) * diameter / uIn ); // maximum number of time steps for the initial simulation
+   const uint_t writeFrequencyInit = uint_t(1000); // vtk write frequency init
+
+   ///////////////////////////
+   // BLOCK STRUCTURE SETUP //
+   ///////////////////////////
+   const int numProcs = MPIManager::instance()->numProcesses();
+   uint_t ZBlocks = uint_c(numProcs) / ( XBlocks * YBlocks );
+   const uint_t XCells = xlength / XBlocks;
+   const uint_t YCells = ylength / YBlocks;
+   const uint_t ZCells = zlength / ZBlocks;
+
+   if( (xlength != XCells * XBlocks) || (ylength != YCells * YBlocks) || (zlength != ZCells * ZBlocks) )
+   {
+      WALBERLA_ABORT("Domain partitioning does not fit to total domain size!");
+   }
+
+   WALBERLA_LOG_INFO_ON_ROOT("Motion settling sphere simulation of case " << simulationCase << " -> Ga = " << Galileo);
+   WALBERLA_LOG_INFO_ON_ROOT("Diameter: " << diameter);
+   WALBERLA_LOG_INFO_ON_ROOT("Domain: " << xlength << " x " << ylength << " x " << zlength);
+   WALBERLA_LOG_INFO_ON_ROOT("Processes: " << XBlocks << " x " << YBlocks << " x " << ZBlocks);
+   WALBERLA_LOG_INFO_ON_ROOT("Subdomains: " << XCells << " x " << YCells << " x " << ZCells);
+   WALBERLA_LOG_INFO_ON_ROOT("Tau: " << real_t(1)/omega);
+   WALBERLA_LOG_INFO_ON_ROOT("Viscosity: " << viscosity);
+   WALBERLA_LOG_INFO_ON_ROOT("uIn: " << uIn);
+   WALBERLA_LOG_INFO_ON_ROOT("Re_infty: " << uIn * diameter / viscosity);
+
+   auto blocks = blockforest::createUniformBlockGrid( XBlocks, YBlocks, ZBlocks, XCells, YCells, ZCells, dx, true,
+                                                      true, true, false );
+
+   //////////////////
+   // RPD COUPLING //
+   //////////////////
+
+   auto rpdDomain = std::make_shared<mesa_pd::domain::BlockForestDomain>(blocks->getBlockForestPointer());
+
+   //init data structures
+   auto ps = walberla::make_shared<mesa_pd::data::ParticleStorage>(1);
+   auto ss = walberla::make_shared<mesa_pd::data::ShapeStorage>();
+   using ParticleAccessor_T = mesa_pd::data::ParticleAccessorWithShape;
+   auto accessor = walberla::make_shared<ParticleAccessor_T >(ps, ss);
+
+
+
+   real_t xParticle = real_t(0);
+   real_t yParticle = real_t(0);
+   real_t zParticle = real_t(0);
+
+   // root determines particle position, then broadcasts it
+   WALBERLA_ROOT_SECTION()
+   {
+      if( simulationCase == 1 )
+      {
+         xParticle = real_c( xlength ) * real_t(0.5);
+         yParticle = real_c( ylength ) * real_t(0.5);
+      }
+      else if( simulationCase == 4 )
+      {
+         // add random perturbance for chaotic regime
+         walberla::math::seedRandomGenerator( std::mt19937::result_type(std::time(nullptr)) );
+         xParticle = real_c( xlength ) * real_t(0.5) + walberla::math::realRandom( real_t(-0.5), real_t(0.5) );
+         yParticle = real_c( ylength ) * real_t(0.5) + walberla::math::realRandom( real_t(-0.5), real_t(0.5) );
+
+      }
+      else
+      {
+         //add small perturbance to sphere position to break stability due to numerical symmetry
+         real_t perturbance = real_t(0.35);
+
+         xParticle = real_c( xlength ) * real_t(0.5) + perturbance;
+         yParticle = real_c( ylength ) * real_t(0.5);
+      }
+
+      zParticle = (longDom) ? ( diameter * real_t(16) + real_c( xlength ) ) : real_c( xlength );
+   }
+
+   // broadcast to other ranks
+   WALBERLA_MPI_SECTION()
+   {
+      mpi::broadcastObject( xParticle );
+      mpi::broadcastObject( yParticle );
+      mpi::broadcastObject( zParticle );
+   }
+
+   // add sphere
+   Vector3<real_t> position( xParticle, yParticle, zParticle );
+   auto sphereShape = ss->create<mesa_pd::data::Sphere>( radius );
+   ss->shapes[sphereShape]->updateMassAndInertia(densityRatio);
+
+   walberla::id_t sphereUid = 0;
+   if (rpdDomain->isContainedInProcessSubdomain( uint_c(mpi::MPIManager::instance()->rank()), position ))
+   {
+      mesa_pd::data::Particle&& p = *ps->create();
+      p.setPosition(position);
+      p.setInteractionRadius(radius);
+      p.setOwner(mpi::MPIManager::instance()->rank());
+      p.setShapeID(sphereShape);
+      sphereUid = p.getUid();
+   }
+   mpi::allReduceInplace(sphereUid, mpi::SUM);
+
+   // set up synchronization procedure
+   const real_t overlap = real_t( 1.5 ) * dx;
+   std::function<void(void)> syncCall;
+   if( XBlocks <= uint_t(4) )
+   {
+      WALBERLA_LOG_INFO_ON_ROOT("Using next neighbor sync!")
+      syncCall = [&ps,&rpdDomain,overlap](){
+         mesa_pd::mpi::SyncNextNeighbors syncNextNeighborFunc;
+         syncNextNeighborFunc(*ps, *rpdDomain, overlap);
+      };
+      syncCall();
+   }
+   else
+   {
+      WALBERLA_LOG_INFO_ON_ROOT("Using ghost owner sync!")
+      syncCall = [&ps,&rpdDomain,overlap](){
+         mesa_pd::mpi::SyncGhostOwners syncGhostOwnersFunc;
+         syncGhostOwnersFunc(*ps, *rpdDomain, overlap);
+      };
+      for(uint_t i = 0; i < uint_c(XBlocks/2); ++i) syncCall();
+   }
+
+
+   WALBERLA_LOG_INFO_ON_ROOT("Initial sphere position: " << position);
+
+   ///////////////////////
+   // ADD DATA TO BLOCKS //
+   ////////////////////////
+
+   // add omega bulk field
+   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx);
+
+
+   // create the lattice model
+   real_t lambda_e = lbm::collision_model::TRT::lambda_e( omega );
+   real_t lambda_d = lbm::collision_model::TRT::lambda_d( omega, magicNumberTRT );
+#ifdef WALBERLA_BUILD_WITH_CODEGEN
+   WALBERLA_LOG_INFO_ON_ROOT("Using generated TRT-like lattice model!");
+   LatticeModel_T latticeModel = LatticeModel_T(omegaBulkFieldID, lambda_d, lambda_e);
+#else
+   WALBERLA_LOG_INFO_ON_ROOT("Using waLBerla built-in TRT lattice model and ignoring omega bulk!");
+   LatticeModel_T latticeModel = LatticeModel_T( lbm::collision_model::TRT::constructWithMagicNumber( omega, magicNumberTRT ) );
+#endif
+
+   // add PDF field
+   // initial velocity in domain = inflow velocity
+   BlockDataID pdfFieldID = lbm::addPdfFieldToStorage< LatticeModel_T >( blocks, "pdf field", latticeModel, initFluidVelocity ? uInfty : Vector3<real_t>(0), real_t(1), uint_t(1), field::fzyx );
+
+   // add flag field
+   BlockDataID flagFieldID = field::addFlagFieldToStorage< FlagField_T >( blocks, "flag field" );
+
+   // add particle field
+   BlockDataID particleFieldID = field::addToStorage<lbm_mesapd_coupling::ParticleField_T>( blocks, "particle field", accessor->getInvalidUid(), field::fzyx, FieldGhostLayers );
+
+
+   // add boundary handling & initialize outer domain boundaries
+   using BoundaryHandling_T = MyBoundaryHandling<ParticleAccessor_T>::Type;
+   BlockDataID boundaryHandlingID = blocks->addStructuredBlockData< BoundaryHandling_T >(MyBoundaryHandling<ParticleAccessor_T>( flagFieldID, pdfFieldID, particleFieldID, accessor, uInfty), "boundary handling" );
+
+   // kernels
+   mesa_pd::mpi::ReduceProperty reduceProperty;
+
+   lbm_mesapd_coupling::AddHydrodynamicInteractionKernel addHydrodynamicInteraction;
+   lbm_mesapd_coupling::ResetHydrodynamicForceTorqueKernel resetHydrodynamicForceTorque;
+   lbm_mesapd_coupling::AverageHydrodynamicForceTorqueKernel averageHydrodynamicForceTorque;
+   lbm_mesapd_coupling::InitializeHydrodynamicForceTorqueForAveragingKernel initializeHydrodynamicForceTorqueForAveragingKernel;
+
+   lbm_mesapd_coupling::RegularParticlesSelector sphereSelector;
+
+   lbm_mesapd_coupling::MovingParticleMappingKernel<BoundaryHandling_T> movingParticleMappingKernel(blocks, boundaryHandlingID, particleFieldID);
+
+   // mapping of sphere required by MEM variants
+   // sets the correct flags
+   if( memVariant == MEMVariant::CLI )
+   {
+      ps->forEachParticle(false, sphereSelector, *accessor, movingParticleMappingKernel, *accessor, MEM_CLI_Flag);
+   }else {
+      ps->forEachParticle(false, sphereSelector, *accessor, movingParticleMappingKernel, *accessor, MEM_BB_Flag);
+   }
+
+   // base folder to store all logs and vtk output
+   std::string basefolder ("MSHS_");
+
+   basefolder += std::to_string( uint_c( Galileo ) );
+   basefolder += "_";
+   basefolder += std::to_string( uint_c( diameter ) );
+   basefolder += "_MEM_";
+   basefolder += MEMVariant_to_string( memVariant );
+   basefolder += "_bvrf";
+   basefolder += std::to_string(int(bulkViscRateFactor));
+   if( useOmegaBulkAdaption ) basefolder += "Adapt";
+
+   basefolder += "_" + reconstructorType;
+
+   if( longDom ) basefolder += "_longDom";
+   if( broadDom ) basefolder += "_broadDom";
+   if( conserveMomentum ) basefolder += "_conserveMomentum";
+   if( !folderEnding.empty() ) basefolder += "_" + folderEnding;
+
+   WALBERLA_LOG_INFO_ON_ROOT("Basefolder for simulation results: " << basefolder);
+
+   // create base directory if it does not yet exist
+   filesystem::path tpath( basefolder );
+   if( !filesystem::exists( tpath ) )
+      filesystem::create_directory( tpath );
+
+   // setup of the LBM communication for synchronizing the pdf field between neighboring blocks
+
+   blockforest::communication::UniformBufferedScheme< Stencil_T > optimizedPDFCommunicationScheme( blocks );
+   optimizedPDFCommunicationScheme.addPackInfo( make_shared< lbm::PdfFieldPackInfo< LatticeModel_T > >( pdfFieldID ) ); // optimized sync
+
+   blockforest::communication::UniformBufferedScheme< Stencil_T > fullPDFCommunicationScheme( blocks );
+   fullPDFCommunicationScheme.addPackInfo( make_shared< field::communication::PackInfo< PdfField_T > >( pdfFieldID ) ); // full sync
+
+   // omega bulk adaption
+   using OmegaBulkAdapter_T = lbm_mesapd_coupling::OmegaBulkAdapter<ParticleAccessor_T, decltype(sphereSelector)>;
+   real_t defaultOmegaBulk = lbm_mesapd_coupling::omegaBulkFromOmega(omega, real_t(1));
+   real_t adaptionLayerSize = real_t(2);
+   shared_ptr<OmegaBulkAdapter_T> omegaBulkAdapter = make_shared<OmegaBulkAdapter_T>(blocks, omegaBulkFieldID, accessor, defaultOmegaBulk, omegaBulk, adaptionLayerSize, sphereSelector);
+   for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt) {
+      (*omegaBulkAdapter)(blockIt.get());
+   }
+
+
+   //////////////////////////////////////
+   // TIME LOOP FOR INITIAL SIMULATION //
+   //////////////////////////////////////
+
+   // Initialization simulation: fixed sphere and simulate until convergence (of drag force)
+
+   // create the timeloop
+   SweepTimeloop timeloopInit( blocks->getBlockStorage(), timestepsInit );
+
+   // add LBM communication function and boundary handling sweep (does the hydro force calculations and the no-slip treatment)
+   auto bhSweep = BoundaryHandling_T::getBlockSweep( boundaryHandlingID );
+   timeloopInit.add() << BeforeFunction( optimizedPDFCommunicationScheme, "LBM Communication" )
+                  << Sweep(bhSweep, "Boundary Handling" );
+
+   // stream + collide LBM step
+#ifdef WALBERLA_BUILD_WITH_CODEGEN
+   auto lbmSweep = LatticeModel_T::Sweep( pdfFieldID );
+   timeloopInit.add() << Sweep( lbmSweep, "LB sweep" );
+#else
+   auto lbmSweep = lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldID, flagFieldID, Fluid_Flag );
+   timeloopInit.add() << Sweep( makeSharedSweep( lbmSweep ), "cell-wise LB sweep" );
+#endif
+
+   if( vtkIOInit )
+   {
+      auto pdfFieldVTKInit = vtk::createVTKOutput_BlockData( blocks, "fluid_field_init", writeFrequencyInit, 0, false, basefolder );
+
+      field::FlagFieldCellFilter< FlagField_T > fluidFilterInit( flagFieldID );
+      fluidFilterInit.addFlag( Fluid_Flag );
+      pdfFieldVTKInit->addCellInclusionFilter( fluidFilterInit );
+
+      pdfFieldVTKInit->addCellDataWriter( make_shared< lbm::VelocityVTKWriter< LatticeModel_T, float > >( pdfFieldID, "VelocityFromPDF" ) );
+      pdfFieldVTKInit->addCellDataWriter( make_shared< lbm::DensityVTKWriter < LatticeModel_T, float > >( pdfFieldID, "DensityFromPDF" ) );
+
+      timeloopInit.addFuncAfterTimeStep( vtk::writeFiles( pdfFieldVTKInit ), "VTK (fluid field data)" );
+   }
+
+   timeloopInit.addFuncAfterTimeStep( RemainingTimeLogger( timeloopInit.getNrOfTimeSteps(), real_t(30) ), "Remaining Time Logger" );
+
+   ////////////////////////////////
+   // EXECUTE INITIAL SIMULATION //
+   ////////////////////////////////
+
+   real_t gravity = real_t(1);
+   real_t GalileoSim = real_t(1);
+   real_t ReynoldsSim = real_t(1);
+   real_t u_ref = real_t(1);
+
+   WALBERLA_LOG_INFO_ON_ROOT("Starting initialization phase (sphere is kept fixed).");
+   WALBERLA_LOG_INFO_ON_ROOT("Iterating, and adapting the viscosity, until the targeted Galileo number is set.");
+
+   RestingSphereForceEvaluator<ParticleAccessor_T> forceEval( accessor, sphereUid, averageFrequency, basefolder );
+
+   while (true) {
+      WcTimingPool timeloopInitTiming;
+
+      WALBERLA_LOG_INFO_ON_ROOT("(Re-)Starting initial simulation.");
+      for( uint_t i = 1; i < timestepsInit; ++i ){
+
+         ps->forEachParticle(false, mesa_pd::kernel::SelectAll(), *accessor, resetHydrodynamicForceTorque, *accessor );
+
+         timeloopInit.singleStep( timeloopInitTiming );
+
+         reduceProperty.operator()<mesa_pd::HydrodynamicForceTorqueNotification>(*ps);
+         forceEval(timeloopInit.getCurrentTimeStep()+1);
+
+         // check if the relative change in the average drag force is below the specified convergence criterion
+         if (forceEval.getForceDiff() < convergenceLimit && i > 2 * std::max(averageFrequency, zlength) )
+         {
+            // conditions to break:
+            // - force diff sufficiently small
+            // - AND more time steps than twice the domain size in z direction to ensure new information due
+            //   to possible viscosity change has traveled twice through domain,
+            //   or twice the average frequency to have converged averages
+
+            WALBERLA_LOG_INFO_ON_ROOT("Drag force converged at time " << timeloopInit.getCurrentTimeStep());
+            break;
+         }
+         // if simulation gets unstable
+         if( std::isnan(forceEval.getDragForce()) )
+         {
+            WALBERLA_ABORT("NAN value detected in drag force during initial simulation, exiting....");
+         }
+      }
+      WALBERLA_LOG_INFO_ON_ROOT("Initial simulation has ended.")
+
+      //evaluate the gravitational force necessary to keep the sphere at a approximately fixed position
+      gravity = forceEval.getDragForce() / ( (densityRatio - real_t(1) ) * diameter * diameter * diameter * math::pi / real_t(6) );
+      GalileoSim = std::sqrt( ( densityRatio - real_t(1) ) * gravity * diameter * diameter * diameter ) / viscosity;
+      ReynoldsSim = uIn * diameter / viscosity;
+      u_ref = std::sqrt( std::fabs(densityRatio - real_t(1)) * gravity * diameter );
+
+      WALBERLA_LOG_INFO_ON_ROOT("Acting gravity = " << gravity );
+      WALBERLA_LOG_INFO_ON_ROOT("Simulated Galileo number = " << GalileoSim );
+      WALBERLA_LOG_INFO_ON_ROOT("Targeted Galileo number = " << Galileo );
+      WALBERLA_LOG_INFO_ON_ROOT("Reynolds number infty = " << ReynoldsSim );
+
+      if ( noViscosityIterations )
+      {
+         timeloopInitTiming.logResultOnRoot();
+         WALBERLA_LOG_INFO_ON_ROOT("Terminate iterations since viscosity should not be changed, flag \"--noViscosityIterations\"");
+         break;
+      }
+
+      // if simulated Galileo number is close enough to targeted Galileo number, stop the initial simulation
+      if( std::abs( GalileoSim - Galileo ) / Galileo < convergenceLimitGalileo )
+      {
+         timeloopInitTiming.logResultOnRoot();
+         WALBERLA_LOG_INFO_ON_ROOT("Iterations converged, simulated Galileo number is close enough to targeted one");
+         break;
+      }
+
+      // else update the simulation parameter accordingly and resume simulation
+      real_t diff = GalileoSim/Galileo;
+      viscosity = diff * viscosity;
+      omega = lbm::collision_model::omegaFromViscosity( viscosity );
+
+      lambda_e = lbm::collision_model::TRT::lambda_e( omega );
+      lambda_d = lbm::collision_model::TRT::lambda_d( omega, magicNumberTRT );
+
+      // also adapt omega bulk
+      omegaBulk = lbm_mesapd_coupling::omegaBulkFromOmega(omega, bulkViscRateFactor);
+      defaultOmegaBulk = lbm_mesapd_coupling::omegaBulkFromOmega(omega, real_t(1));
+      omegaBulkAdapter->setDefaultOmegaBulk(defaultOmegaBulk);
+      omegaBulkAdapter->setAdaptedOmegaBulk(omegaBulk);
+
+      // iterate all blocks with an iterator 'block' and change the collision model
+      for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )
+      {
+         // get the field data out of the block
+         auto pdf = blockIt->getData< PdfField_T > ( pdfFieldID );
+#ifdef WALBERLA_BUILD_WITH_CODEGEN
+         pdf->latticeModel().omega_magic_ = lambda_d;
+         pdf->latticeModel().omega_visc_ = lambda_e;
+#else
+         pdf->latticeModel().collisionModel().resetWithMagicNumber( omega, magicNumberTRT );
+#endif
+         (*omegaBulkAdapter)(blockIt.get());
+      }
+
+
+      WALBERLA_LOG_INFO_ON_ROOT("==> Adapting viscosity:");
+      WALBERLA_LOG_INFO_ON_ROOT("New viscosity = " << viscosity );
+      WALBERLA_LOG_INFO_ON_ROOT("New omega = " << omega );
+      WALBERLA_LOG_INFO_ON_ROOT("New omega bulk = " << omegaBulk );
+
+   }
+
+   if( averageForceTorqueOverTwoTimeStepsMode == 1 )
+   {
+      // maintain a good initial guess for averaging
+      ps->forEachParticle(false, mesa_pd::kernel::SelectLocal(), *accessor, initializeHydrodynamicForceTorqueForAveragingKernel, *accessor );
+   }
+   ps->forEachParticle(false, mesa_pd::kernel::SelectAll(), *accessor, resetHydrodynamicForceTorque, *accessor );
+
+   ///////////////
+   // TIME LOOP //
+   ///////////////
+
+   // actual simulation: freely moving sphere with acting gravity
+
+   // calculate the number of timesteps
+
+   real_t dtSim = (averageForceTorqueOverTwoTimeStepsMode == 2) ? real_t(2) : real_t(1);
+   const real_t t_ref = ( diameter / u_ref );
+   const uint_t timesteps = uint_c( timestepsNonDim * t_ref / dtSim );
+
+   // set vtk write frequency accordingly
+
+   const uint_t writeFrequency = uint_c( vtkWriteSpacingNonDim * t_ref ); // vtk write frequency
+   const uint_t initWriteCallsToSkip = uint_c(std::max( 0, int( timesteps - ( vtkNumOutputFiles - uint_t(1) ) * writeFrequency - uint_t(1) ) ) ); // write calls to be skipped
+
+   WALBERLA_LOG_INFO_ON_ROOT("Starting simulation timeloop with  " << timesteps << " timesteps!");
+   WALBERLA_LOG_INFO_ON_ROOT("Sphere is allowed to move freely under action of gravity");
+
+   SweepTimeloop timeloop( blocks->getBlockStorage(), uint_c(dtSim) * timesteps );
+
+
+   timeloop.addFuncBeforeTimeStep( RemainingTimeLogger( timeloop.getNrOfTimeSteps(), real_t(30) ), "Remaining Time Logger" );
+
+   // vtk output
+   auto pdfFieldVTK = vtk::createVTKOutput_BlockData( blocks, "fluid_field", writeFrequency, (vtkOutputGhostlayers) ? FieldGhostLayers : 0, false, basefolder );
+   pdfFieldVTK->setInitialWriteCallsToSkip( initWriteCallsToSkip );
+
+   pdfFieldVTK->addBeforeFunction( fullPDFCommunicationScheme );
+
+   // function to output plane infos for vtk output
+   pdfFieldVTK->addBeforeFunction(VTKInfoLogger<ParticleAccessor_T>( &timeloop, accessor, sphereUid, basefolder, uInfty ));
+
+   // create folder for log_vtk files to not pollute the basefolder
+   filesystem::path tpath2( basefolder+"/log_vtk" );
+   if( !filesystem::exists( tpath2 ) )
+      filesystem::create_directory( tpath2 );
+
+
+   field::FlagFieldCellFilter< FlagField_T > fluidFilter( flagFieldID );
+   fluidFilter.addFlag( Fluid_Flag );
+   pdfFieldVTK->addCellInclusionFilter( fluidFilter );
+
+   pdfFieldVTK->addCellDataWriter( make_shared< lbm::VelocityVTKWriter< LatticeModel_T, float > >( pdfFieldID, "VelocityFromPDF" ) );
+   pdfFieldVTK->addCellDataWriter( make_shared< lbm::DensityVTKWriter < LatticeModel_T, float > >( pdfFieldID, "DensityFromPDF" ) );
+
+   timeloop.addFuncBeforeTimeStep( vtk::writeFiles( pdfFieldVTK ), "VTK (fluid field data)" );
+
+
+   // add LBM communication function and boundary handling sweep (does the hydro force calculations and the no-slip treatment)
+   timeloop.add() << BeforeFunction( optimizedPDFCommunicationScheme, "LBM Communication" )
+                  << Sweep(bhSweep, "Boundary Handling" );
+
+   // stream + collide LBM step
+#ifdef WALBERLA_BUILD_WITH_CODEGEN
+   timeloop.add() << Sweep( lbmSweep, "LB sweep" );
+#else
+   timeloop.add() << Sweep( makeSharedSweep( lbmSweep ), "cell-wise LB sweep" );
+#endif
+
+
+   SweepTimeloop timeloopAfterParticles( blocks->getBlockStorage(), timesteps );
+
+   // sweep for updating the pe body mapping into the LBM simulation
+   if( memVariant == MEMVariant::CLI )
+      timeloopAfterParticles.add() << Sweep( lbm_mesapd_coupling::makeMovingParticleMapping<PdfField_T, BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, MEM_CLI_Flag, FormerMEM_Flag, sphereSelector, conserveMomentum), "Particle Mapping" );
+   else
+      timeloopAfterParticles.add() << Sweep( lbm_mesapd_coupling::makeMovingParticleMapping<PdfField_T, BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, MEM_BB_Flag, FormerMEM_Flag, sphereSelector, conserveMomentum), "Particle Mapping" );
+
+   // sweep for restoring PDFs in cells previously occupied by particles
+   if( reconstructorType == "EAN")
+   {
+
+      auto sphereNormalExtrapolationDirectionFinder = make_shared<lbm_mesapd_coupling::SphereNormalExtrapolationDirectionFinder>(blocks);
+      auto equilibriumAndNonEquilibriumSphereNormalReconstructor = lbm_mesapd_coupling::makeEquilibriumAndNonEquilibriumReconstructor<BoundaryHandling_T>(blocks, boundaryHandlingID, sphereNormalExtrapolationDirectionFinder, uint_t(3), true);
+      auto reconstructionManager = lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMEM_Flag, Fluid_Flag, equilibriumAndNonEquilibriumSphereNormalReconstructor, conserveMomentum);
+
+      timeloopAfterParticles.add() << BeforeFunction( fullPDFCommunicationScheme, "PDF Communication" )
+                                   << Sweep( makeSharedSweep(reconstructionManager), "PDF Restore" );
+
+   } else if( reconstructorType == "Ext" )
+   {
+      auto sphereNormalExtrapolationDirectionFinder = make_shared<lbm_mesapd_coupling::SphereNormalExtrapolationDirectionFinder>(blocks);
+      auto extrapolationSphereNormalReconstructor = lbm_mesapd_coupling::makeExtrapolationReconstructor<BoundaryHandling_T,lbm_mesapd_coupling::SphereNormalExtrapolationDirectionFinder,true>(blocks, boundaryHandlingID, sphereNormalExtrapolationDirectionFinder, uint_t(3), true);
+
+      timeloopAfterParticles.add() << BeforeFunction( fullPDFCommunicationScheme, "PDF Communication" )
+                                   << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMEM_Flag, Fluid_Flag, extrapolationSphereNormalReconstructor, conserveMomentum) ), "PDF Restore" );
+   } else if( reconstructorType == "Grad")
+   {
+      auto gradReconstructor = lbm_mesapd_coupling::makeGradsMomentApproximationReconstructor<BoundaryHandling_T>(blocks, boundaryHandlingID, omega, false, true, true);
+
+      timeloopAfterParticles.add() << BeforeFunction( fullPDFCommunicationScheme, "PDF Communication" )
+                                   << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMEM_Flag, Fluid_Flag, gradReconstructor, conserveMomentum) ), "PDF Restore" );
+   } else if( reconstructorType == "Eq")
+   {
+      timeloopAfterParticles.add() << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMEM_Flag, Fluid_Flag, conserveMomentum) ), "PDF Restore" );
+   } else {
+      WALBERLA_ABORT("Unknown reconstructor type " << reconstructorType);
+   }
+
+   // update bulk omega in all cells to adapt to changed particle position
+   if( useOmegaBulkAdaption )
+   {
+      timeloopAfterParticles.add() << Sweep( makeSharedSweep(omegaBulkAdapter), "Omega Bulk Adapter");
+   }
+
+   Vector3<real_t> extForcesOnSphere( real_t(0), real_t(0), - gravity * ( densityRatio - real_t(1) ) * diameter * diameter * diameter * math::pi / real_t(6));
+   lbm_mesapd_coupling::AddForceOnParticlesKernel addGravitationalForce(extForcesOnSphere);
+
+
+   mesa_pd::kernel::VelocityVerletPreForceUpdate  vvIntegratorPreForce(dtSim);
+   mesa_pd::kernel::VelocityVerletPostForceUpdate vvIntegratorPostForce(dtSim);
+   mesa_pd::kernel::ExplicitEuler explicitEulerIntegrator(dtSim);
+
+   MovingSpherePropertyEvaluator<ParticleAccessor_T> movingSpherePropertyEvaluator( accessor, sphereUid, basefolder, uInfty, Galileo, GalileoSim, gravity, viscosity, diameter, densityRatio );
+
+
+   ////////////////////////
+   // EXECUTE SIMULATION //
+   ////////////////////////
+
+   WcTimingPool timeloopTiming;
+
+   const bool useOpenMP = false;
+
+   // time loop
+   for (uint_t i = 0; i < timesteps; ++i )
+   {
+      // perform a single simulation step -> this contains LBM and setting of the hydrodynamic interactions
+      timeloop.singleStep( timeloopTiming );
+
+      if( averageForceTorqueOverTwoTimeStepsMode == 2)
+      {
+         // store current force and torque in fOld and tOld
+         ps->forEachParticle(useOpenMP, sphereSelector, *accessor, initializeHydrodynamicForceTorqueForAveragingKernel, *accessor );
+
+         // reset f and t
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectAll(), *accessor, resetHydrodynamicForceTorque, *accessor );
+
+         timeloop.singleStep( timeloopTiming );
+
+
+         // f = (f+fOld)/2
+         ps->forEachParticle(useOpenMP, sphereSelector, *accessor, averageHydrodynamicForceTorque, *accessor );
+
+         reduceProperty.operator()<mesa_pd::HydrodynamicForceTorqueNotification>(*ps);
+      }
+      else
+      {
+         reduceProperty.operator()<mesa_pd::HydrodynamicForceTorqueNotification>(*ps);
+
+         if( averageForceTorqueOverTwoTimeStepsMode == 1 )
+         {
+            ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, averageHydrodynamicForceTorque, *accessor );
+         }
+      }
+
+
+      timeloopTiming["RPD"].start();
+
+      if( useVelocityVerlet)
+      {
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPreForce, *accessor);
+         syncCall();
+      }
+
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addHydrodynamicInteraction, *accessor );
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addGravitationalForce, *accessor );
+
+      reduceProperty.operator()<mesa_pd::ForceTorqueNotification>(*ps);
+
+      if( useVelocityVerlet ) {
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPostForce, *accessor);
+      }
+      else
+      {
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, explicitEulerIntegrator, *accessor);
+      }
+      syncCall();
+
+      timeloopTiming["RPD"].end();
+
+      // evaluation
+      timeloopTiming["Logging"].start();
+      movingSpherePropertyEvaluator((i+1)*uint_c(dtSim));
+      timeloopTiming["Logging"].end();
+
+      // reset after logging here
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectAll(), *accessor, resetHydrodynamicForceTorque, *accessor );
+
+      timeloopAfterParticles.singleStep( timeloopTiming );
+
+      if(movingSpherePropertyEvaluator.getParticleHeight() > real_c(zlength) - real_t(3) * diameter)
+      {
+         pdfFieldVTK->forceWrite(timeloop.getCurrentTimeStep());
+         WALBERLA_LOG_WARNING_ON_ROOT("Sphere is too close to outflow, stopping simulation");
+         break;
+      }
+
+   }
+
+   timeloopTiming.logResultOnRoot();
+
+   return EXIT_SUCCESS;
+}
+
+} // namespace motion_settling_sphere
+
+int main( int argc, char **argv ){
+   motion_settling_sphere::main(argc, argv);
+}
diff --git a/apps/benchmarks/FluidParticleCoupling/ObliqueWetCollision.cpp b/apps/benchmarks/FluidParticleCoupling/ObliqueWetCollision.cpp
index cac87d6c75e0fb971a8cfb5ed26901a333fef5fc..3bb0c57eef6b1138797cef785afe47869abb3f0f 100644
--- a/apps/benchmarks/FluidParticleCoupling/ObliqueWetCollision.cpp
+++ b/apps/benchmarks/FluidParticleCoupling/ObliqueWetCollision.cpp
@@ -815,7 +815,7 @@ int main( int argc, char **argv )
    ////////////////////////
 
    // add omega bulk field
-   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx, uint_t(0) );
+   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx);
 
    // create the lattice model
    real_t lambda_e = lbm::collision_model::TRT::lambda_e( omega );
diff --git a/apps/benchmarks/FluidParticleCoupling/SettlingSphereInBox.cpp b/apps/benchmarks/FluidParticleCoupling/SettlingSphereInBox.cpp
index f1d6ac21e643f97fc1485678217c29551aa05266..3f2f613b252ece2fc72ad5c3df257e3c2f2f5223 100644
--- a/apps/benchmarks/FluidParticleCoupling/SettlingSphereInBox.cpp
+++ b/apps/benchmarks/FluidParticleCoupling/SettlingSphereInBox.cpp
@@ -186,9 +186,10 @@ class SpherePropertyLogger
 public:
    SpherePropertyLogger( const shared_ptr< ParticleAccessor_T > & ac, walberla::id_t sphereUid,
          const std::string & fileName, bool fileIO,
-         real_t dx_SI, real_t dt_SI, real_t diameter, real_t gravitationalForceMag) :
+         real_t dx_SI, real_t dt_SI, real_t diameter, real_t gravitationalForceMag, real_t uref) :
       ac_( ac ), sphereUid_( sphereUid ), fileName_( fileName ), fileIO_(fileIO),
-      dx_SI_( dx_SI ), dt_SI_( dt_SI ), diameter_( diameter ), gravitationalForceMag_( gravitationalForceMag ),
+      dx_SI_( dx_SI ), dt_SI_( dt_SI ), diameter_( diameter ),
+      gravitationalForceMag_( gravitationalForceMag ), uref_(uref), tref_(diameter / uref),
       position_( real_t(0) ), maxVelocity_( real_t(0) )
    {
       if ( fileIO_ )
@@ -197,7 +198,7 @@ public:
          {
             std::ofstream file;
             file.open( fileName_.c_str() );
-            file << "#\t t\t posZ\t gapZ/D\t velZ\t velZ_SI\t fZ\t fZ/fGravi\n";
+            file << "#\t t\t t/tref\t posZ\t gapZ/D\t velZ\t velZ_SI\t velZ/uref\t fZ\t fZ/fGravi\n";
             file.close();
          }
       }
@@ -264,10 +265,9 @@ private:
          auto velocity_SI = velocity * dx_SI_ / dt_SI_;
          auto normalizedHydForce = hydForce / gravitationalForceMag_;
 
-
-         file << timestep << "\t" << real_c(timestep) * dt_SI_ << "\t"
+         file << timestep << "\t" << real_c(timestep) * dt_SI_ << "\t" << real_c(timestep) / tref_
               << "\t" << position[2] << "\t" << scaledPosition[2] - real_t(0.5)
-              << "\t" << velocity[2] << "\t" << velocity_SI[2]
+              << "\t" << velocity[2] << "\t" << velocity_SI[2] << "\t" << velocity[2] / uref_
               << "\t" << hydForce[2] << "\t" << normalizedHydForce[2]
               << "\n";
          file.close();
@@ -278,7 +278,7 @@ private:
    const walberla::id_t sphereUid_;
    std::string fileName_;
    bool fileIO_;
-   real_t dx_SI_, dt_SI_, diameter_, gravitationalForceMag_;
+   real_t dx_SI_, dt_SI_, diameter_, gravitationalForceMag_, uref_, tref_;
 
    real_t position_;
    real_t maxVelocity_;
@@ -385,6 +385,8 @@ int main( int argc, char **argv )
    real_t adaptionLayerSize = real_t(2);
    bool useLubricationCorrection = true;
 
+   bool useGalileoParameterization = false;
+
    for( int i = 1; i < argc; ++i )
    {
       if( std::strcmp( argv[i], "--shortrun" )             == 0 ) { shortrun = true; continue; }
@@ -406,6 +408,7 @@ int main( int argc, char **argv )
       if( std::strcmp( argv[i], "--useOmegaBulkAdaption" ) == 0 ) { useOmegaBulkAdaption = true; continue; }
       if( std::strcmp( argv[i], "--adaptionLayerSize" )    == 0 ) { adaptionLayerSize = real_c(std::atof( argv[++i] )); continue; }
       if( std::strcmp( argv[i], "--noLubricationCorrection" ) == 0 ) { useLubricationCorrection = false; continue; }
+      if( std::strcmp( argv[i], "--useGalileoParameterization" ) == 0 ) { useGalileoParameterization = true; continue; }
       WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
    }
 
@@ -433,6 +436,9 @@ int main( int argc, char **argv )
    real_t densityFluid_SI;
    real_t dynamicViscosityFluid_SI;
    real_t expectedSettlingVelocity_SI;
+
+   // expected velocity given as uMax in experiments of ten Cate (Table 2, E1-E4), with uInfty from Table 1
+   // > slightly different Re than in ten Cate's Table 1
    switch( fluidType )
    {
       case 1:
@@ -494,8 +500,9 @@ int main( int argc, char **argv )
    const real_t diameter = diameter_SI / dx_SI;
    const real_t sphereVolume = math::pi / real_t(6) * diameter * diameter * diameter;
 
-   //const real_t dt_SI = characteristicVelocity / ug_SI * dx_SI; // this uses Ga for parameterization
-   const real_t dt_SI = characteristicVelocity / expectedSettlingVelocity_SI * dx_SI; // this uses Re for parameterization (only possible since settling velocity is known)
+
+   const real_t dt_SI = (useGalileoParameterization) ? characteristicVelocity / ug_SI * dx_SI : // this uses Ga for parameterization, where ug is the characteristic velocity
+                                                       characteristicVelocity / expectedSettlingVelocity_SI * dx_SI; // this uses Re for parameterization (only possible since settling velocity is known)
 
    const real_t viscosity =  kinematicViscosityFluid_SI * dt_SI / ( dx_SI * dx_SI );
    const real_t omega = lbm::collision_model::omegaFromViscosity(viscosity);
@@ -602,7 +609,7 @@ int main( int argc, char **argv )
    ////////////////////////
 
    // add omega bulk field
-   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx, uint_t(0) );
+   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx);
 
    // create the lattice model
    real_t lambda_e = lbm::collision_model::TRT::lambda_e( omega );
@@ -779,17 +786,19 @@ int main( int argc, char **argv )
    // evaluation functionality
    std::string loggingFileName( baseFolder + "/LoggingSettlingSphere_");
    loggingFileName += std::to_string(fluidType);
+   loggingFileName += "_res" + std::to_string(numberOfCellsInHorizontalDirection);
    loggingFileName += "_recon" + reconstructorType;
    loggingFileName += "_bvrf" + std::to_string(uint_c(bulkViscRateFactor));
    loggingFileName += "_mn" + std::to_string(float(magicNumber));
    if( useOmegaBulkAdaption ) loggingFileName += "_uOBA" + std::to_string(uint_c(adaptionLayerSize));
+   if( useGalileoParameterization ) loggingFileName += "_Ga";
    if( !fileNameEnding.empty()) loggingFileName += "_" + fileNameEnding;
    loggingFileName += ".txt";
    if( fileIO  )
    {
       WALBERLA_LOG_INFO_ON_ROOT(" - writing logging output to file \"" << loggingFileName << "\"");
    }
-   SpherePropertyLogger<ParticleAccessor_T> logger( accessor, sphereUid, loggingFileName, fileIO, dx_SI, dt_SI, diameter, -gravitationalForce[2] );
+   SpherePropertyLogger<ParticleAccessor_T> logger( accessor, sphereUid, loggingFileName, fileIO, dx_SI, dt_SI, diameter, -gravitationalForce[2], characteristicVelocity );
 
 
    ////////////////////////
@@ -832,7 +841,7 @@ int main( int argc, char **argv )
             syncCall();
          }
 
-         ps->forEachParticle(useOpenMP, sphereSelector, *accessor, addHydrodynamicInteraction, *accessor );
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addHydrodynamicInteraction, *accessor );
          ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addGravitationalForce, *accessor );
 
          // lubrication correction
diff --git a/apps/benchmarks/FluidParticleCoupling/SphereMovingWithPrescribedVelocity.cpp b/apps/benchmarks/FluidParticleCoupling/SphereMovingWithPrescribedVelocity.cpp
index 1be5bf12295ea88fc2ff02a03bb1fd0517398058..9c963fa9dcbc0580af7ce7476d92c0884bbaec27 100644
--- a/apps/benchmarks/FluidParticleCoupling/SphereMovingWithPrescribedVelocity.cpp
+++ b/apps/benchmarks/FluidParticleCoupling/SphereMovingWithPrescribedVelocity.cpp
@@ -129,7 +129,8 @@ const uint_t FieldGhostLayers = 1;
 
 const FlagUID Fluid_Flag( "fluid" );
 const FlagUID NoSlip_Flag( "no slip" );
-const FlagUID MO_Flag( "moving obstacle" );
+const FlagUID MO_SBB_Flag( "moving obstacle sbb" );
+const FlagUID MO_CLI_Flag( "moving obstacle cli" );
 const FlagUID FormerMO_Flag( "former moving obstacle" );
 
 /////////////////////////////////////
@@ -141,8 +142,9 @@ class MyBoundaryHandling
 public:
 
    using NoSlip_T = lbm::NoSlip< LatticeModel_T, flag_t >;
-   using MO_T = lbm_mesapd_coupling::CurvedLinear< LatticeModel_T, FlagField_T, ParticleAccessor_T >;
-   using Type = BoundaryHandling< FlagField_T, Stencil_T, NoSlip_T, MO_T >;
+   using MO_SBB_T = lbm_mesapd_coupling::SimpleBB< LatticeModel_T, FlagField_T, ParticleAccessor_T >;
+   using MO_CLI_T = lbm_mesapd_coupling::CurvedLinear< LatticeModel_T, FlagField_T, ParticleAccessor_T >;
+   using Type = BoundaryHandling< FlagField_T, Stencil_T, NoSlip_T, MO_SBB_T, MO_CLI_T >;
 
    MyBoundaryHandling( const BlockDataID & flagFieldID, const BlockDataID & pdfFieldID,
                        const BlockDataID & particleFieldID, const shared_ptr<ParticleAccessor_T>& ac) :
@@ -161,7 +163,8 @@ public:
 
       Type * handling = new Type( "moving obstacle boundary handling", flagField, fluid,
                                   NoSlip_T( "NoSlip", NoSlip_Flag, pdfField ),
-                                  MO_T( "MO", MO_Flag, pdfField, flagField, particleField, ac_, fluid, *storage, *block ) );
+                                  MO_SBB_T( "SBB", MO_SBB_Flag, pdfField, flagField, particleField, ac_, fluid, *storage, *block ),
+                                  MO_CLI_T( "CLI", MO_CLI_Flag, pdfField, flagField, particleField, ac_, fluid, *storage, *block )  );
 
       handling->fillWithDomain( FieldGhostLayers );
 
@@ -192,7 +195,7 @@ public:
          const std::string & fileName, bool fileIO,
          real_t diameter, real_t velocity, real_t forceMag) :
       ac_( ac ), sphereUid_( sphereUid ), fileName_( fileName ), fileIO_(fileIO),
-      diameter_( diameter ), velocity_(velocity), forceMag_( forceMag ),
+      diameter_( diameter ), velocityRef_(velocity), forceMag_( forceMag ),
       position_( real_t(0) )
    {
       if ( fileIO_ )
@@ -201,7 +204,7 @@ public:
          {
             std::ofstream file;
             file.open( fileName_.c_str() );
-            file << "#\t t\t posZ\t posZ/D\t fZ\t fZ/fSt\n";
+            file << "#\t t\t posZ\t posZ/D\t velZ\t velZ/velRef\t fZ\t fZ/fSt\n";
             file.close();
          }
       }
@@ -210,6 +213,7 @@ public:
    void operator()()
    {
       real_t pos(real_t(0));
+      real_t vel(real_t(0));
       real_t hydForce(real_t(0));
 
       size_t idx = ac_->uidToIdx(sphereUid_);
@@ -218,6 +222,7 @@ public:
          if(!mesa_pd::data::particle_flags::isSet( ac_->getFlags(idx), mesa_pd::data::particle_flags::GHOST))
          {
             pos = ac_->getPosition(idx)[2];
+            vel = ac_->getLinearVelocity(idx)[2];
             hydForce = ac_->getHydrodynamicForce(idx)[2];
          }
       }
@@ -225,10 +230,12 @@ public:
       WALBERLA_MPI_SECTION()
       {
          mpi::allReduceInplace( pos, mpi::SUM );
+         mpi::allReduceInplace( vel, mpi::SUM );
          mpi::allReduceInplace( hydForce, mpi::SUM );
       }
 
       position_ = pos;
+      velocity_ = vel;
       hydForce_ = hydForce;
 
    }
@@ -238,7 +245,12 @@ public:
       return position_;
    }
 
-   void writeToFile( const uint_t timestep, real_t density1, real_t density2, real_t totalMass )
+   real_t getForce() const
+   {
+      return hydForce_;
+   }
+
+   void writeToFile( const uint_t timestep, real_t density1, real_t density2, real_t totalMass, uint_t countSolidCells, uint_t countFluidSolidLinks, Vector3<real_t> fluidVelocity )
    {
       WALBERLA_ROOT_SECTION()
       {
@@ -249,10 +261,13 @@ public:
          auto normalizedHydForce = hydForce_ / std::abs(forceMag_);
 
          file << std::setprecision(10)
-              << timestep << "\t" << real_c(timestep) / (diameter_ / velocity_) << "\t"
+              << timestep << "\t" << real_c(timestep) / (diameter_ / velocityRef_) << "\t"
               << "\t" << position_ << "\t" << scaledPosition
+              << "\t" << velocity_ << "\t" << velocity_ / velocityRef_
               << "\t" << hydForce_ << "\t" << normalizedHydForce
               << "\t" << density1 << "\t" << density2 << "\t" << totalMass
+              << "\t" << countSolidCells << "\t" << countFluidSolidLinks
+              << "\t" << fluidVelocity[0] << "\t" << fluidVelocity[1] << "\t" << fluidVelocity[2]
               << "\n";
          file.close();
       }
@@ -263,19 +278,20 @@ private:
    const walberla::id_t sphereUid_;
    std::string fileName_;
    bool fileIO_;
-   real_t diameter_, velocity_, forceMag_;
+   real_t diameter_, velocityRef_, forceMag_;
 
-   real_t position_, hydForce_;
+   real_t position_, velocity_, hydForce_;
 };
 
 
-void createPlaneSetup(const shared_ptr<mesa_pd::data::ParticleStorage> & ps, const shared_ptr<mesa_pd::data::ShapeStorage> & ss, const math::AABB & simulationDomain)
+void createPlaneSetup(const shared_ptr<mesa_pd::data::ParticleStorage> & ps, const shared_ptr<mesa_pd::data::ShapeStorage> & ss, const math::AABB & simulationDomain, real_t velocity = real_t(0))
 {
    mesa_pd::data::Particle p2 = *ps->create(true);
    p2.setPosition(simulationDomain.minCorner());
    p2.setShapeID(ss->create<mesa_pd::data::HalfSpace>( Vector3<real_t>(1,0,0) ));
    p2.setOwner(mpi::MPIManager::instance()->rank());
    p2.setType(0);
+   p2.setLinearVelocity(Vector3<real_t>(real_t(0),real_t(0),velocity));
    mesa_pd::data::particle_flags::set(p2.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
    mesa_pd::data::particle_flags::set(p2.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
 
@@ -284,6 +300,7 @@ void createPlaneSetup(const shared_ptr<mesa_pd::data::ParticleStorage> & ps, con
    p3.setShapeID(ss->create<mesa_pd::data::HalfSpace>( Vector3<real_t>(-1,0,0) ));
    p3.setOwner(mpi::MPIManager::instance()->rank());
    p3.setType(0);
+   p3.setLinearVelocity(Vector3<real_t>(real_t(0),real_t(0),velocity));
    mesa_pd::data::particle_flags::set(p3.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
    mesa_pd::data::particle_flags::set(p3.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
 
@@ -292,6 +309,7 @@ void createPlaneSetup(const shared_ptr<mesa_pd::data::ParticleStorage> & ps, con
    p4.setShapeID(ss->create<mesa_pd::data::HalfSpace>( Vector3<real_t>(0,1,0) ));
    p4.setOwner(mpi::MPIManager::instance()->rank());
    p4.setType(0);
+   p4.setLinearVelocity(Vector3<real_t>(real_t(0),real_t(0),velocity));
    mesa_pd::data::particle_flags::set(p4.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
    mesa_pd::data::particle_flags::set(p4.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
 
@@ -300,6 +318,7 @@ void createPlaneSetup(const shared_ptr<mesa_pd::data::ParticleStorage> & ps, con
    p5.setShapeID(ss->create<mesa_pd::data::HalfSpace>( Vector3<real_t>(0,-1,0) ));
    p5.setOwner(mpi::MPIManager::instance()->rank());
    p5.setType(0);
+   p5.setLinearVelocity(Vector3<real_t>(real_t(0),real_t(0),velocity));
    mesa_pd::data::particle_flags::set(p5.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
    mesa_pd::data::particle_flags::set(p5.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
 
@@ -324,6 +343,27 @@ real_t getDensityAtPosition(const shared_ptr<StructuredBlockStorage> & blocks, B
    return density;
 }
 
+template< typename VelocityInterpolator_T>
+Vector3<real_t> getVelocityAtPosition(const shared_ptr<StructuredBlockStorage> & blocks, BlockDataID velocityInterpolatorID, Vector3<real_t> position)
+{
+
+   Vector3<real_t> vel(real_t(0));
+   for( auto & block: *blocks)
+   {
+      if(block.getAABB().contains(position))
+      {
+         auto velocityInterpolator = block.getData<VelocityInterpolator_T>(velocityInterpolatorID);
+         velocityInterpolator->get(position, &vel);
+      }
+
+   }
+   mpi::reduceInplace(vel[0], mpi::SUM);
+   mpi::reduceInplace(vel[1], mpi::SUM);
+   mpi::reduceInplace(vel[2], mpi::SUM);
+
+   return vel;
+}
+
 template< typename BoundaryHandling_T>
 real_t getAverageDensityInSystem(const shared_ptr<StructuredBlockStorage> & blocks, BlockDataID pdfFieldID, BlockDataID boundaryHandlingID)
 {
@@ -348,6 +388,41 @@ real_t getAverageDensityInSystem(const shared_ptr<StructuredBlockStorage> & bloc
    return totalMass/real_c(count);
 }
 
+template< typename BoundaryHandling_T>
+void evaluateMapping(const shared_ptr<StructuredBlockStorage> & blocks, BlockDataID boundaryHandlingID,
+                     uint_t & countSolidCells, uint_t & countFluidSolidLinks)
+{
+   countSolidCells = uint_t(0);
+   countFluidSolidLinks = uint_t(0);
+   for( auto & block: *blocks)
+   {
+      auto boundaryHandling = block.getData<BoundaryHandling_T>(boundaryHandlingID);
+      auto flagField = boundaryHandling->getFlagField();
+      //auto innerDomain = flagField->xyzSize();
+      WALBERLA_FOR_ALL_CELLS_XYZ(flagField,
+                                 if(!boundaryHandling->isDomain(x,y,z) )
+                                 {
+                                    ++countSolidCells;
+                                 } else
+                                 {
+                                    for( auto neighborDir = stencil::D3Q27::beginNoCenter(); neighborDir != stencil::D3Q27::end(); ++neighborDir )
+                                    {
+                                       Cell neighbor( x + neighborDir.cx(), y + neighborDir.cy(), z + neighborDir.cz() );
+
+                                       // check if neighbor cell is a solid cell
+                                       if( !boundaryHandling->isDomain( neighbor ) )
+                                       {
+                                          ++countFluidSolidLinks;
+                                       }
+                                    }
+                                 }
+      );
+   }
+
+   mpi::reduceInplace(countSolidCells, mpi::SUM);
+   mpi::reduceInplace(countFluidSolidLinks, mpi::SUM);
+}
+
 //////////
 // MAIN //
 //////////
@@ -389,13 +464,15 @@ int main( int argc, char **argv )
    std::string baseFolder = "vtk_out_MovingWithPrescribedVelocity";
    std::string fileNameEnding = "";
    bool logDensity = true;
+   bool logMapping= true;
+   bool logFluidVelocity = true;
    bool vtkOutputAtEnd = true;
 
    //numerical parameters
-   bool averageForceTorqueOverTwoTimeSteps = true;
    bool conserveMomentum = false;
-   uint_t numRPDSubCycles = uint_t(1);
    std::string reconstructorType = "Grad"; // Eq, EAN, Ext, Grad
+   std::string forceTorqueAveragingType = "runningAvg"; // noAvg, runningAvg, twoLBM,
+   std::string boundaryCondition = "CLI"; // SBB, CLI
    real_t bulkViscRateFactor = real_t(1);
    real_t magicNumber = real_t(0.1875);
    bool useOmegaBulkAdaption = false;
@@ -409,21 +486,27 @@ int main( int argc, char **argv )
    real_t velocity = real_t(0.02);
    real_t Re = real_t(164);
    real_t diameter = real_t(20);
-   real_t numberOfPasses = real_t(3);
+   real_t numberOfTimeStepsNonDim = real_t(20);
    real_t domainWidthNonDim = real_t(4);
    real_t domainHeightNonDim = real_t(8);
+   real_t accelerationFactor = real_t(3);
    bool usePeriodicSetup = false;
    bool artificiallyAccelerateSphere = false;
+   bool fixedSphere = false;
+
+   bool useGalileanInvariantSetup = false;
 
    for( int i = 1; i < argc; ++i )
    {
       if( std::strcmp( argv[i], "--funcTest" )             == 0 ) { funcTest = true; continue; }
       if( std::strcmp( argv[i], "--noLogging" )            == 0 ) { fileIO = false; continue; }
       if( std::strcmp( argv[i], "--noVtkOutputAtEnd" )     == 0 ) { vtkOutputAtEnd = false; continue; }
-      if( std::strcmp( argv[i], "--logDensity" )           == 0 ) { logDensity = true; continue; }
+      if( std::strcmp( argv[i], "--noLogDensity" )         == 0 ) { logDensity = false; continue; }
+      if( std::strcmp( argv[i], "--noLogMapping" )         == 0 ) { logMapping = false; continue; }
+      if( std::strcmp( argv[i], "--noLogFluidVelocity" )   == 0 ) { logFluidVelocity = false; continue; }
       if( std::strcmp( argv[i], "--vtkIOFreq" )            == 0 ) { vtkIOFreq = uint_c( std::atof( argv[++i] ) ); continue; }
-      if( std::strcmp( argv[i], "--numRPDSubCycles" )      == 0 ) { numRPDSubCycles = uint_c( std::atof( argv[++i] ) ); continue; }
-      if( std::strcmp( argv[i], "--noForceAveraging" )     == 0 ) { averageForceTorqueOverTwoTimeSteps = false; continue; }
+      if( std::strcmp( argv[i], "--averagingType" )        == 0 ) { forceTorqueAveragingType = argv[++i]; continue; }
+      if( std::strcmp( argv[i], "--boundaryCondition" )    == 0 ) { boundaryCondition = argv[++i]; continue; }
       if( std::strcmp( argv[i], "--conserveMomentum" )     == 0 ) { conserveMomentum = true; continue; }
       if( std::strcmp( argv[i], "--baseFolder" )           == 0 ) { baseFolder = argv[++i]; continue; }
       if( std::strcmp( argv[i], "--reconstructorType" )    == 0 ) { reconstructorType = argv[++i]; continue; }
@@ -437,16 +520,24 @@ int main( int argc, char **argv )
       if( std::strcmp( argv[i], "--fileName" )             == 0 ) { fileNameEnding = argv[++i]; continue; }
       if( std::strcmp( argv[i], "--useOmegaBulkAdaption" ) == 0 ) { useOmegaBulkAdaption = true; continue; }
       if( std::strcmp( argv[i], "--adaptionLayerSize" )    == 0 ) { adaptionLayerSize = real_c(std::atof( argv[++i] )); continue; }
-      if( std::strcmp( argv[i], "--numberOfPasses" )       == 0 ) { numberOfPasses = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--numberOfTimeStepsNonDim" )       == 0 ) { numberOfTimeStepsNonDim = real_c(std::atof( argv[++i] )); continue; }
       if( std::strcmp( argv[i], "--initialSpherePosition" )== 0 ) { initialSpherePosition = real_c(std::atof( argv[++i] )); continue; }
       if( std::strcmp( argv[i], "--usePeriodicSetup" )     == 0 ) { usePeriodicSetup = true; continue; }
       if( std::strcmp( argv[i], "--artificiallyAccelerateSphere" )     == 0 ) { artificiallyAccelerateSphere = true; continue; }
+      if( std::strcmp( argv[i], "--accelerationFactor" )   == 0 ) { accelerationFactor = real_c(std::atof( argv[++i] )); continue; }
       if( std::strcmp( argv[i], "--blocksInX" )            == 0 ) { blocksInX = uint_c( std::atof( argv[++i] ) ); continue; }
       if( std::strcmp( argv[i], "--blocksInY" )            == 0 ) { blocksInY = uint_c( std::atof( argv[++i] ) ); continue; }
       if( std::strcmp( argv[i], "--blocksInZ" )            == 0 ) { blocksInZ = uint_c( std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--useGalileanInvariantSetup" ) == 0 ) { useGalileanInvariantSetup = true; continue; }
+      if( std::strcmp( argv[i], "--fixedSphere" ) == 0 ) { fixedSphere = true; continue; }
       WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
    }
 
+   if(forceTorqueAveragingType != "noAvg" && forceTorqueAveragingType != "runningAvg" && forceTorqueAveragingType != "twoLBM")
+   {
+      WALBERLA_ABORT("Averaging type not implemented: " << forceTorqueAveragingType);
+   }
+
    if( funcTest )
    {
       walberla::logging::Logging::instance()->setLogLevel(logging::Logging::LogLevel::WARNING);
@@ -468,24 +559,33 @@ int main( int argc, char **argv )
    const real_t omega = lbm::collision_model::omegaFromViscosity(viscosity);
    const real_t relaxationTime = real_t(1) / omega;
    const real_t omegaBulk = lbm_mesapd_coupling::omegaBulkFromOmega(omega, bulkViscRateFactor);
+   const real_t tref = diameter / velocity;
 
    const Vector3<uint_t> domainSize( uint_c(domainWidthNonDim * diameter), uint_c(domainWidthNonDim * diameter), uint_c(domainHeightNonDim * diameter) );
 
    WALBERLA_LOG_INFO_ON_ROOT("Setup (in simulation, i.e. lattice, units):");
    WALBERLA_LOG_INFO_ON_ROOT(" - domain size = " << domainSize);
    WALBERLA_LOG_INFO_ON_ROOT(" - Re = " << Re);
+   WALBERLA_LOG_INFO_ON_ROOT(" - tref = " << tref);
+   WALBERLA_LOG_INFO_ON_ROOT(" - acceleration factor (a_acc) = " << accelerationFactor);
    WALBERLA_LOG_INFO_ON_ROOT(" - sphere: diameter = " << diameter << ", constant velocity = " << velocity );
-   WALBERLA_LOG_INFO_ON_ROOT(" - relaxation time (tau) = " << relaxationTime << ", omega = " << omega << " kin. visc = " << viscosity );
+   WALBERLA_LOG_INFO_ON_ROOT(" - relaxation time (tau) = " << relaxationTime << ", omega = " << omega << ", kin. visc = " << viscosity );
    WALBERLA_LOG_INFO_ON_ROOT(" - magic number " << magicNumber);
    WALBERLA_LOG_INFO_ON_ROOT(" - omegaBulk = " << omegaBulk << ", bulk visc. = " << lbm_mesapd_coupling::bulkViscosityFromOmegaBulk(omegaBulk) << " (bvrf " << bulkViscRateFactor << ")");
    WALBERLA_LOG_INFO_ON_ROOT(" - use omega bulk adaption = " << useOmegaBulkAdaption << " (adaption layer size = " << adaptionLayerSize << ")");
    WALBERLA_LOG_INFO_ON_ROOT(" - reconstructor type = " << reconstructorType );
 
-   if( vtkIOFreq > 0 )
-   {
-      WALBERLA_LOG_INFO_ON_ROOT(" - writing vtk files to folder \"" << baseFolder << "\" with frequency " << vtkIOFreq);
+   if( vtkIOFreq > 0 ) WALBERLA_LOG_INFO_ON_ROOT(" - writing vtk files to folder \"" << baseFolder << "\" with frequency " << vtkIOFreq);
+   if( useGalileanInvariantSetup ) {
+      WALBERLA_LOG_INFO_ON_ROOT("ATTENTION: Using Galilean Invariant Setup = Moving side walls, fixed sphere");
+      artificiallyAccelerateSphere = false;
+      usePeriodicSetup = false;
+      conserveMomentum = false;
+      fixedSphere = true;
+
    }
 
+
    ///////////////////////////
    // BLOCK STRUCTURE SETUP //
    ///////////////////////////
@@ -530,6 +630,7 @@ int main( int argc, char **argv )
 
    // bounding planes
    if(!usePeriodicSetup) createPlaneSetup(ps,ss,blocks->getDomain());
+   if(useGalileanInvariantSetup) createPlaneSetup(ps,ss,blocks->getDomain(),-velocity);
 
    // create sphere and store Uid
    Vector3<real_t> initialPosition( real_t(0.5) * real_c(domainSize[0]), real_t(0.5) * real_c(domainSize[1]), initialSpherePosition * real_c(domainSize[2]));
@@ -544,7 +645,7 @@ int main( int argc, char **argv )
       p.setInteractionRadius(diameter * real_t(0.5));
       p.setOwner(mpi::MPIManager::instance()->rank());
       p.setShapeID(sphereShape);
-      p.setLinearVelocity(Vector3<real_t>(0.0,0.0,velocity));
+      if(!useGalileanInvariantSetup && !artificiallyAccelerateSphere) p.setLinearVelocity(Vector3<real_t>(real_t(0),real_t(0),velocity));
       sphereUid = p.getUid();
    }
    mpi::allReduceInplace(sphereUid, mpi::SUM);
@@ -554,7 +655,7 @@ int main( int argc, char **argv )
    ////////////////////////
 
    // add omega bulk field
-   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx, uint_t(0) );
+   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx );
 
    // create the lattice model
    real_t lambda_e = lbm::collision_model::TRT::lambda_e( omega );
@@ -569,7 +670,8 @@ int main( int argc, char **argv )
 
    // add PDF field
    BlockDataID pdfFieldID = lbm::addPdfFieldToStorage< LatticeModel_T >( blocks, "pdf field (fzyx)", latticeModel,
-                                                                         Vector3< real_t >( real_t(0) ), real_t(1),
+                                                                         useGalileanInvariantSetup ? Vector3<real_t>(real_t(0), real_t(0), -velocity) : Vector3< real_t >( real_t(0)),
+                                                                         real_t(1),
                                                                          uint_t(1), field::fzyx );
    // add flag field
    BlockDataID flagFieldID = field::addFlagFieldToStorage<FlagField_T>( blocks, "flag field" );
@@ -584,9 +686,13 @@ int main( int argc, char **argv )
    // interpolation functionality
    using DensityAdaptor_T = typename lbm::Adaptor< LatticeModel_T >::Density;
    BlockDataID densityAdaptorID = field::addFieldAdaptor<  DensityAdaptor_T >( blocks, pdfFieldID, "density adaptor" );
+   using VelocityAdaptor_T = typename lbm::Adaptor< LatticeModel_T >::VelocityVector;
+   BlockDataID velocityAdaptorID = field::addFieldAdaptor<  VelocityAdaptor_T >( blocks, pdfFieldID, "velocity adaptor" );
 
    using DensityInterpolator_T = typename field::TrilinearFieldInterpolator<DensityAdaptor_T, FlagField_T>;
    BlockDataID densityInterpolatorID = field::addFieldInterpolator< DensityInterpolator_T, FlagField_T >( blocks, densityAdaptorID, flagFieldID, Fluid_Flag );
+   using VelocityInterpolator_T = typename field::TrilinearFieldInterpolator<VelocityAdaptor_T, FlagField_T>;
+   BlockDataID velocityInterpolatorID = field::addFieldInterpolator< VelocityInterpolator_T, FlagField_T >( blocks, velocityAdaptorID, flagFieldID, Fluid_Flag );
 
    // set up RPD functionality
    std::function<void(void)> syncCall = [ps,rpdDomain, adaptionLayerSize ](){
@@ -597,7 +703,6 @@ int main( int argc, char **argv )
 
    syncCall();
 
-   mesa_pd::kernel::ExplicitEuler explEulerIntegrator(real_t(1)/real_t(numRPDSubCycles));
 
    mesa_pd::mpi::ReduceProperty reduceProperty;
 
@@ -615,10 +720,18 @@ int main( int argc, char **argv )
    ///////////////
 
    // map planes into the LBM simulation -> act as no-slip boundaries
-   ps->forEachParticle(false, lbm_mesapd_coupling::GlobalParticlesSelector(), *accessor, particleMappingKernel, *accessor, NoSlip_Flag);
+   //ps->forEachParticle(false, lbm_mesapd_coupling::GlobalParticlesSelector(), *accessor, particleMappingKernel, *accessor, NoSlip_Flag);
 
    // map particles into the LBM simulation
-   ps->forEachParticle(false, sphereSelector, *accessor, movingParticleMappingKernel, *accessor, MO_Flag);
+   if(boundaryCondition == "SBB")
+   {
+      ps->forEachParticle(false, mesa_pd::kernel::SelectAll(), *accessor, movingParticleMappingKernel, *accessor, MO_SBB_Flag);
+   } else if(boundaryCondition == "CLI")
+   {
+      ps->forEachParticle(false, mesa_pd::kernel::SelectAll(), *accessor, movingParticleMappingKernel, *accessor, MO_CLI_Flag);
+   } else{
+      WALBERLA_ABORT("Unknown boundary condition " << boundaryCondition);
+   }
 
 
    // setup of the LBM communication for synchronizing the pdf field between neighboring blocks
@@ -631,7 +744,10 @@ int main( int argc, char **argv )
 
    // create the timeloop
 
-   const uint_t timesteps = uint_c(numberOfPasses * real_c(domainSize[2]) / velocity);
+   real_t dtSim = real_t(1);
+   if (forceTorqueAveragingType == "twoLBM") dtSim = real_t(2);
+
+   const uint_t timesteps = uint_t( numberOfTimeStepsNonDim * tref);
    WALBERLA_LOG_INFO_ON_ROOT("Running for " << timesteps << " time steps");
    SweepTimeloop timeloop( blocks->getBlockStorage(), timesteps );
 
@@ -645,6 +761,7 @@ int main( int argc, char **argv )
       auto particleVtkOutput = make_shared<mesa_pd::vtk::ParticleVtkOutput>(ps);
       particleVtkOutput->addOutput<mesa_pd::data::SelectParticleOwner>("owner");
       particleVtkOutput->addOutput<mesa_pd::data::SelectParticleLinearVelocity>("velocity");
+      particleVtkOutput->setParticleSelector( [sphereShape](const mesa_pd::data::ParticleStorage::iterator& pIt) {return pIt->getShapeID() == sphereShape;} ); //limit output to sphere
       auto particleVtkWriter = vtk::createVTKOutput_PointData(particleVtkOutput, "Particles", vtkIOFreq, baseFolder, "simulation_step");
       timeloop.addFuncBeforeTimeStep( vtk::writeFiles( particleVtkWriter ), "VTK (sphere data)" );
 
@@ -667,37 +784,86 @@ int main( int argc, char **argv )
 
    }
 
+   // add LBM communication function and boundary handling sweep (does the hydro force calculations and the no-slip treatment)
+   auto bhSweep = BoundaryHandling_T::getBlockSweep( boundaryHandlingID );
+   timeloop.add() << BeforeFunction( optimizedPDFCommunicationScheme, "LBM Communication" )
+                  << Sweep(bhSweep, "Boundary Handling" );
+
+   // stream + collide LBM step
+#ifdef WALBERLA_BUILD_WITH_CODEGEN
+   auto lbmSweep = LatticeModel_T::Sweep( pdfFieldID );
+   //timeloop.add() << Sweep( [&lbmSweep](IBlock * const block){lbmSweep.streamCollide(block);}, "LB sweep" );
+   timeloop.add() << Sweep(lbmSweep, "LB sweep" );
+#else
+   auto lbmSweep = lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldID, flagFieldID, Fluid_Flag );
+   timeloop.add() << Sweep( makeSharedSweep( lbmSweep ), "cell-wise LB sweep" );
+#endif
+
+
+   SweepTimeloop timeloopAfterParticles( blocks->getBlockStorage(), timesteps );
+
    // sweep for updating the particle mapping into the LBM simulation
-   timeloop.add() << Sweep( lbm_mesapd_coupling::makeMovingParticleMapping<PdfField_T, BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, MO_Flag, FormerMO_Flag, sphereSelector, conserveMomentum), "Particle Mapping" );
+   if(boundaryCondition == "SBB")
+   {
+      timeloopAfterParticles.add() << Sweep( lbm_mesapd_coupling::makeMovingParticleMapping<PdfField_T, BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, MO_SBB_Flag, FormerMO_Flag, sphereSelector, conserveMomentum), "Particle Mapping" );
+
+   } else if(boundaryCondition == "CLI")
+   {
+      timeloopAfterParticles.add() << Sweep( lbm_mesapd_coupling::makeMovingParticleMapping<PdfField_T, BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, MO_CLI_Flag, FormerMO_Flag, sphereSelector, conserveMomentum), "Particle Mapping" );
+   } else{
+      WALBERLA_ABORT("Unknown boundary condition " << boundaryCondition);
+   }
 
+   bool useValuesFromGhostLayerForReconstruction = true; // true: full sync needed
    // sweep for restoring PDFs in cells previously occupied by particles
    if( reconstructorType == "EAN")
    {
 
-      auto sphereNormalExtrapolationDirectionFinder = make_shared<lbm_mesapd_coupling::SphereNormalExtrapolationDirectionFinder>(blocks);
-      auto equilibriumAndNonEquilibriumSphereNormalReconstructor = lbm_mesapd_coupling::makeEquilibriumAndNonEquilibriumReconstructor<BoundaryHandling_T>(blocks, boundaryHandlingID, sphereNormalExtrapolationDirectionFinder, uint_t(3), true);
+      //auto sphereNormalExtrapolationDirectionFinder = make_shared<lbm_mesapd_coupling::SphereNormalExtrapolationDirectionFinder>(blocks);
+      auto sphereNormalExtrapolationDirectionFinder = make_shared<lbm_mesapd_coupling::FlagFieldNormalExtrapolationDirectionFinder<BoundaryHandling_T> >(blocks,boundaryHandlingID);
+      auto equilibriumAndNonEquilibriumSphereNormalReconstructor = lbm_mesapd_coupling::makeEquilibriumAndNonEquilibriumReconstructor<BoundaryHandling_T>(blocks, boundaryHandlingID, sphereNormalExtrapolationDirectionFinder, uint_t(3), useValuesFromGhostLayerForReconstruction);
       auto reconstructionManager = lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMO_Flag, Fluid_Flag, equilibriumAndNonEquilibriumSphereNormalReconstructor, conserveMomentum);
 
-      timeloop.add() << BeforeFunction( fullPDFCommunicationScheme, "PDF Communication" )
-                     << Sweep( makeSharedSweep(reconstructionManager), "PDF Restore" );
+      timeloopAfterParticles.add() << BeforeFunction( fullPDFCommunicationScheme, "PDF Communication" )
+                                   << Sweep( makeSharedSweep(reconstructionManager), "PDF Restore" );
 
    } else if( reconstructorType == "Grad")
    {
       bool recomputeDensity = false;
-      auto gradReconstructor = lbm_mesapd_coupling::makeGradsMomentApproximationReconstructor<BoundaryHandling_T>(blocks, boundaryHandlingID, omega, recomputeDensity, true, true);
+      bool useCentralDifferences = true;
+      auto gradReconstructor = lbm_mesapd_coupling::makeGradsMomentApproximationReconstructor<BoundaryHandling_T>(blocks, boundaryHandlingID, omega, recomputeDensity, useCentralDifferences, useValuesFromGhostLayerForReconstruction);
+
+      timeloopAfterParticles.add() << BeforeFunction( fullPDFCommunicationScheme, "PDF Communication" )
+                                   << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMO_Flag, Fluid_Flag, gradReconstructor, conserveMomentum) ), "PDF Restore" );
+   } else if( reconstructorType == "GradDen")
+   {
+      bool recomputeDensity = true;
+      bool useCentralDifferences = true;
+      auto gradReconstructor = lbm_mesapd_coupling::makeGradsMomentApproximationReconstructor<BoundaryHandling_T>(blocks, boundaryHandlingID, omega, recomputeDensity, useCentralDifferences, useValuesFromGhostLayerForReconstruction);
 
-      timeloop.add() << BeforeFunction( fullPDFCommunicationScheme, "PDF Communication" )
-                     << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMO_Flag, Fluid_Flag, gradReconstructor, conserveMomentum) ), "PDF Restore" );
+      timeloopAfterParticles.add() << BeforeFunction( fullPDFCommunicationScheme, "PDF Communication" )
+                                   << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMO_Flag, Fluid_Flag, gradReconstructor, conserveMomentum) ), "PDF Restore" );
    } else if( reconstructorType == "Eq")
    {
-      timeloop.add() << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMO_Flag, Fluid_Flag, conserveMomentum) ), "PDF Restore" );
+      auto eqReconstructor = lbm_mesapd_coupling::makeEquilibriumReconstructor<BoundaryHandling_T>(blocks, boundaryHandlingID, useValuesFromGhostLayerForReconstruction);
+      timeloopAfterParticles.add() << BeforeFunction( fullPDFCommunicationScheme, "PDF Communication" )
+                                   << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMO_Flag, Fluid_Flag, eqReconstructor, conserveMomentum) ), "PDF Restore" );
    } else if( reconstructorType == "Ext")
    {
-      auto sphereNormalExtrapolationDirectionFinder = make_shared<lbm_mesapd_coupling::SphereNormalExtrapolationDirectionFinder>(blocks);
-      auto extrapolationReconstructor = lbm_mesapd_coupling::makeExtrapolationReconstructor<BoundaryHandling_T, lbm_mesapd_coupling::SphereNormalExtrapolationDirectionFinder, true>(blocks, boundaryHandlingID, sphereNormalExtrapolationDirectionFinder, uint_t(3), true);
-      timeloop.add() << BeforeFunction( fullPDFCommunicationScheme, "LBM Communication" )
-                     << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMO_Flag, Fluid_Flag, extrapolationReconstructor, conserveMomentum)), "PDF Restore" );
-   } else {
+      // gets easily unstable
+      //auto sphereNormalExtrapolationDirectionFinder = make_shared<lbm_mesapd_coupling::SphereNormalExtrapolationDirectionFinder>(blocks);
+      auto sphereNormalExtrapolationDirectionFinder = make_shared<lbm_mesapd_coupling::FlagFieldNormalExtrapolationDirectionFinder<BoundaryHandling_T> >(blocks,boundaryHandlingID);
+      auto extrapolationReconstructor = lbm_mesapd_coupling::makeExtrapolationReconstructor<BoundaryHandling_T, lbm_mesapd_coupling::FlagFieldNormalExtrapolationDirectionFinder<BoundaryHandling_T>, false>(blocks, boundaryHandlingID, sphereNormalExtrapolationDirectionFinder, uint_t(3), useValuesFromGhostLayerForReconstruction);
+      timeloopAfterParticles.add() << BeforeFunction( fullPDFCommunicationScheme, "LBM Communication" )
+                                   << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMO_Flag, Fluid_Flag, extrapolationReconstructor, conserveMomentum)), "PDF Restore" );
+   } else if( reconstructorType == "ExtNS")
+   {
+      //auto sphereNormalExtrapolationDirectionFinder = make_shared<lbm_mesapd_coupling::SphereNormalExtrapolationDirectionFinder>(blocks);
+      auto sphereNormalExtrapolationDirectionFinder = make_shared<lbm_mesapd_coupling::FlagFieldNormalExtrapolationDirectionFinder<BoundaryHandling_T> >(blocks,boundaryHandlingID);
+      auto extrapolationReconstructor = lbm_mesapd_coupling::makeExtrapolationReconstructor<BoundaryHandling_T, lbm_mesapd_coupling::FlagFieldNormalExtrapolationDirectionFinder<BoundaryHandling_T>, true>(blocks, boundaryHandlingID, sphereNormalExtrapolationDirectionFinder, uint_t(3), useValuesFromGhostLayerForReconstruction);
+      timeloopAfterParticles.add() << BeforeFunction( fullPDFCommunicationScheme, "LBM Communication" )
+                                   << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMO_Flag, Fluid_Flag, extrapolationReconstructor, conserveMomentum)), "PDF Restore" );
+   }else {
       WALBERLA_ABORT("Unknown reconstructor type " << reconstructorType);
    }
 
@@ -707,35 +873,37 @@ int main( int argc, char **argv )
       using OmegaBulkAdapter_T = lbm_mesapd_coupling::OmegaBulkAdapter<ParticleAccessor_T, decltype(sphereSelector)>;
       real_t defaultOmegaBulk = lbm_mesapd_coupling::omegaBulkFromOmega(omega, real_t(1));
       shared_ptr<OmegaBulkAdapter_T> omegaBulkAdapter = make_shared<OmegaBulkAdapter_T>(blocks, omegaBulkFieldID, accessor, defaultOmegaBulk, omegaBulk, adaptionLayerSize, sphereSelector);
-      timeloop.add() << Sweep( makeSharedSweep(omegaBulkAdapter), "Omega Bulk Adapter");
+      timeloopAfterParticles.add() << Sweep( makeSharedSweep(omegaBulkAdapter), "Omega Bulk Adapter");
+      // initally adapt
+      for (auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt) {
+         (*omegaBulkAdapter)(blockIt.get());
+      }
    }
 
-   // add LBM communication function and boundary handling sweep (does the hydro force calculations and the no-slip treatment)
-   auto bhSweep = BoundaryHandling_T::getBlockSweep( boundaryHandlingID );
-   timeloop.add() << BeforeFunction( optimizedPDFCommunicationScheme, "LBM Communication" )
-                  << Sweep(bhSweep, "Boundary Handling" );
-
-   // stream + collide LBM step
-#ifdef WALBERLA_BUILD_WITH_CODEGEN
-   auto lbmSweep = LatticeModel_T::Sweep( pdfFieldID );
-   timeloop.add() << Sweep( lbmSweep, "LB sweep" );
-#else
-   auto lbmSweep = lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldID, flagFieldID, Fluid_Flag );
-   timeloop.add() << Sweep( makeSharedSweep( lbmSweep ), "cell-wise LB sweep" );
-#endif
-
    // evaluation functionality
-   std::string loggingFileName( baseFolder + "/LoggingPrescribedMovingSphere");
-   loggingFileName += "_Re" + std::to_string(uint_c(Re));
-   loggingFileName += "_D" + std::to_string(uint_c(diameter));
-   loggingFileName += "_vel" + std::to_string(float(velocity));
-   loggingFileName += "_recon" + reconstructorType;
-   loggingFileName += "_bvrf" + std::to_string(uint_c(bulkViscRateFactor));
-   loggingFileName += "_mn" + std::to_string(float(magicNumber));
-   if( useOmegaBulkAdaption ) loggingFileName += "_uOBA" + std::to_string(uint_c(adaptionLayerSize));
-   if( conserveMomentum ) loggingFileName += "_conserveMomentum";
-   if( artificiallyAccelerateSphere ) loggingFileName += "_acc";
-   if( !fileNameEnding.empty()) loggingFileName += "_" + fileNameEnding;
+   std::string fileNamePostFix("");
+   fileNamePostFix += "_Re" + std::to_string(uint_c(Re));
+   fileNamePostFix += "_D" + std::to_string(uint_c(diameter));
+   fileNamePostFix += "_vel" + std::to_string(float(velocity));
+   fileNamePostFix += "_" + boundaryCondition;
+   fileNamePostFix += "_recon" + reconstructorType;
+   fileNamePostFix += "_" + forceTorqueAveragingType;
+   fileNamePostFix += "_bvrf" + std::to_string(uint_c(bulkViscRateFactor));
+   //fileNamePostFix += "_mn" + std::to_string(float(magicNumber));
+   if( useOmegaBulkAdaption ) fileNamePostFix += "_uOBA" + std::to_string(uint_c(adaptionLayerSize));
+
+   if( useGalileanInvariantSetup )
+   {
+      fileNamePostFix += "_galileanInvariant";
+   }
+   else{
+      if( conserveMomentum ) fileNamePostFix += "_conserveMomentum";
+      if( artificiallyAccelerateSphere ) fileNamePostFix += "_acc";
+      if( fixedSphere ) fileNamePostFix += "_fixed";
+   }
+   if( !fileNameEnding.empty()) fileNamePostFix += "_" + fileNameEnding;
+   
+   std::string loggingFileName( baseFolder + "/Log" + fileNamePostFix);
    loggingFileName += ".txt";
 
    if( fileIO  )
@@ -753,43 +921,41 @@ int main( int argc, char **argv )
 
    const bool useOpenMP = false;
 
-   const real_t densityRatio = real_t(7800) / real_t(935);
-   const real_t responseTime = densityRatio * diameter * diameter / ( real_t(18) * viscosity );
-   WALBERLA_LOG_INFO_ON_ROOT(" - response time " << responseTime);
-   const real_t accelerationFactor = real_t(1) / (real_t(0.1) * responseTime);
+   mesa_pd::kernel::ExplicitEuler explEulerIntegrator(dtSim);
+   lbm_mesapd_coupling::InitializeHydrodynamicForceTorqueForAveragingKernel initializeHydrodynamicForceTorqueForAveragingKernel;
 
    // time loop
-   for (uint_t i = 0; i < timesteps; ++i )
+   for (uint_t i = 0; i < uint_c(real_c(timesteps)/dtSim); ++i )
    {
       // perform a single simulation step -> this contains LBM and setting of the hydrodynamic interactions
       timeloop.singleStep( timeloopTiming );
 
-      timeloopTiming["RPD"].start();
-      
-      reduceProperty.operator()<mesa_pd::HydrodynamicForceTorqueNotification>(*ps);
-
-      if( averageForceTorqueOverTwoTimeSteps )
+      if( forceTorqueAveragingType == "twoLBM")
       {
-         if( i == 0 )
-         {
-            lbm_mesapd_coupling::InitializeHydrodynamicForceTorqueForAveragingKernel initializeHydrodynamicForceTorqueForAveragingKernel;
-            ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, initializeHydrodynamicForceTorqueForAveragingKernel, *accessor );
-         }
-         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, averageHydrodynamicForceTorque, *accessor );
-      }
+         // store current force and torque in fOld and tOld
+         ps->forEachParticle(useOpenMP, sphereSelector, *accessor, initializeHydrodynamicForceTorqueForAveragingKernel, *accessor );
 
-      reduceProperty.operator()<mesa_pd::ForceTorqueNotification>(*ps);
-         
-      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, explEulerIntegrator, *accessor);
-      syncCall();
+         // reset f and t
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectAll(), *accessor, resetHydrodynamicForceTorque, *accessor );
 
-      timeloopTiming["RPD"].end();
+         timeloop.singleStep( timeloopTiming );
 
-      if( artificiallyAccelerateSphere )
+         // f = (f+fOld)/2
+         ps->forEachParticle(useOpenMP, sphereSelector, *accessor, averageHydrodynamicForceTorque, *accessor );
+
+         reduceProperty.operator()<mesa_pd::HydrodynamicForceTorqueNotification>(*ps);
+      } else
       {
-         // accelerating after reading from a checkpoint file does not make sense, as current actual runtime is not known
-         real_t newSphereVel = velocity * (std::exp(-accelerationFactor * real_t(i) ) - real_t(1));
-         ps->forEachParticle(useOpenMP, sphereSelector, *accessor, [newSphereVel](const size_t idx, ParticleAccessor_T& ac){ ac.setLinearVelocity(idx, Vector3<real_t>(real_t(0), real_t(0), newSphereVel));}, *accessor);
+         reduceProperty.operator()<mesa_pd::HydrodynamicForceTorqueNotification>(*ps);
+
+         if( forceTorqueAveragingType == "runningAvg" )
+         {
+            if( i == 0 )
+            {
+               ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, initializeHydrodynamicForceTorqueForAveragingKernel, *accessor );
+            }
+            ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, averageHydrodynamicForceTorque, *accessor );
+         }
       }
 
       // evaluation
@@ -797,28 +963,68 @@ int main( int argc, char **argv )
 
       logger();
 
+      if( std::isnan(logger.getForce()) )
+      {
+         WALBERLA_ABORT("Nan found in force - aborting");
+      }
+
       real_t density1 = real_t(0);
       real_t density2 = real_t(0);
       real_t averageDensity = real_t(0);
+      auto movingProbePosition = Vector3<real_t>(real_c(domainSize[0])*real_t(0.75), real_c(domainSize[1])*real_t(0.5), logger.getPosition());
       if(logDensity)
       {
          density1 = getDensityAtPosition<DensityInterpolator_T >(blocks, densityInterpolatorID, Vector3<real_t>(real_c(domainSize[0])*real_t(0.25), real_c(domainSize[1])*real_t(0.5), real_c(domainSize[2])*real_t(0.5)));
-         density2 = getDensityAtPosition<DensityInterpolator_T >(blocks, densityInterpolatorID, Vector3<real_t>(real_c(domainSize[0])*real_t(0.75), real_c(domainSize[1])*real_t(0.5), logger.getPosition()));
+         density2 = getDensityAtPosition<DensityInterpolator_T >(blocks, densityInterpolatorID, movingProbePosition);
          averageDensity = getAverageDensityInSystem<BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID);
       }
-      logger.writeToFile(i, density1, density2, averageDensity);
+
+      Vector3<real_t> velocityAtProbePos(real_t(0));
+      if(logFluidVelocity)
+      {
+         velocityAtProbePos = getVelocityAtPosition<VelocityInterpolator_T >(blocks, velocityInterpolatorID, movingProbePosition);
+      }
+
+      uint_t countSolidCells = 0;
+      uint_t countFluidSolidLinks = 0;
+      if(logMapping) evaluateMapping<BoundaryHandling_T>(blocks, boundaryHandlingID, countSolidCells, countFluidSolidLinks);
+
+      logger.writeToFile(i*uint_c(dtSim), density1, density2, averageDensity, countSolidCells, countFluidSolidLinks, velocityAtProbePos);
       timeloopTiming["Logging"].end();
 
+
       // reset after logging here
       ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectAll(), *accessor, resetHydrodynamicForceTorque, *accessor );
 
+
+      if(!fixedSphere)
+      {
+         timeloopTiming["RPD"].start();
+
+         if( artificiallyAccelerateSphere )
+         {
+            real_t newSphereVel = - velocity * (std::exp(- real_t(i) * dtSim * accelerationFactor/ tref ) - real_t(1));
+            ps->forEachParticle(useOpenMP, sphereSelector, *accessor, [newSphereVel](const size_t idx, ParticleAccessor_T& ac){ ac.setLinearVelocity(idx, Vector3<real_t>(real_t(0), real_t(0), newSphereVel));}, *accessor);
+         }
+
+         // there is no force acting on sphere
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, explEulerIntegrator, *accessor);
+         syncCall();
+
+         timeloopTiming["RPD"].end();
+
+         // update mapping, restore PDFs
+         timeloopAfterParticles.singleStep( timeloopTiming );
+      }
+
+
    }
 
    timeloopTiming.logResultOnRoot();
 
    if(vtkOutputAtEnd)
    {
-      std::string vtkFileName = "fluid_field";
+      std::string vtkFileName = "fluid_field_final"+fileNamePostFix;
       vtkFileName += "_bvrf" + std::to_string(uint_c(bulkViscRateFactor));
       if( useOmegaBulkAdaption ) vtkFileName += "_uOBA" + std::to_string(uint_c(adaptionLayerSize));
 
@@ -843,6 +1049,13 @@ int main( int argc, char **argv )
       pdfFieldVTK->addCellDataWriter( make_shared< lbm::DensityVTKWriter < LatticeModel_T, float > >( pdfFieldID, "DensityFromPDF" ) );
 
       vtk::writeFiles(pdfFieldVTK)();
+
+      auto particleVtkOutput = make_shared<mesa_pd::vtk::ParticleVtkOutput>(ps);
+      particleVtkOutput->addOutput<mesa_pd::data::SelectParticleLinearVelocity>("velocity");
+      particleVtkOutput->setParticleSelector( [sphereShape](const mesa_pd::data::ParticleStorage::iterator& pIt) {return pIt->getShapeID() == sphereShape;} ); //limit output to sphere
+      auto particleVtkWriter = vtk::createVTKOutput_PointData(particleVtkOutput, "Particles_final"+fileNamePostFix, uint_t(1), baseFolder);
+
+      vtk::writeFiles(particleVtkWriter)();
    }
 
 
diff --git a/apps/benchmarks/FluidParticleCoupling/SphereWallCollision.cpp b/apps/benchmarks/FluidParticleCoupling/SphereWallCollision.cpp
index cd84f67e4d0ad40ba6813cfc6716b195d0475558..1ce306d76a368929dce44e0e52fb73e7753bab7f 100644
--- a/apps/benchmarks/FluidParticleCoupling/SphereWallCollision.cpp
+++ b/apps/benchmarks/FluidParticleCoupling/SphereWallCollision.cpp
@@ -132,6 +132,10 @@ using ScalarField_T = GhostLayerField< real_t, 1>;
 
 const uint_t FieldGhostLayers = 1;
 
+#define USE_TRTLIKE
+//#define USE_D3Q27TRTLIKE
+//#define USE_CUMULANT
+
 ///////////
 // FLAGS //
 ///////////
@@ -758,10 +762,12 @@ int main( int argc, char **argv )
                                     + "_mn" + std::to_string(magicNumber);
    if(useOmegaBulkAdaption) checkPointFileName +="_omegaBulkAdaption";
    if(applyOutflowBCAtTop) checkPointFileName +="_outflowBC";
-#ifdef USE_TRT_LIKE_LATTICE_MODEL
-   checkPointFileName +="_TRTlike";
-#else
-   checkPointFileName += "_other";
+#if defined(USE_TRTLIKE)
+   checkPointFileName += "_trtlike";
+#elif defined(USE_CUMULANT)
+   checkPointFileName += "_cumulant";
+#elif defined(USE_D3Q27TRTLIKE)
+   checkPointFileName += "_d3q27trtlike";
 #endif
 
    WALBERLA_LOG_INFO_ON_ROOT("Checkpointing:");
@@ -852,10 +858,10 @@ int main( int argc, char **argv )
    {
       WALBERLA_LOG_INFO_ON_ROOT( "Initializing particles from checkpointing file!" );
       particleStorageID = blocks->loadBlockData( checkPointFileName + "_mesa.txt", mesa_pd::domain::createBlockForestDataHandling(ps), "Particle Storage" );
-   } else {
-      particleStorageID = blocks->addBlockData(mesa_pd::domain::createBlockForestDataHandling(ps), "Particle Storage");
       mesa_pd::mpi::ClearNextNeighborSync CNNS;
       CNNS(*accessor);
+   } else {
+      particleStorageID = blocks->addBlockData(mesa_pd::domain::createBlockForestDataHandling(ps), "Particle Storage");
    }
 
    // bounding planes
@@ -901,14 +907,19 @@ int main( int argc, char **argv )
    ////////////////////////
 
    // add omega bulk field
-   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx, uint_t(0) );
+   BlockDataID omegaBulkFieldID = field::addToStorage<ScalarField_T>( blocks, "omega bulk field", omegaBulk, field::fzyx);
 
    // create the lattice model
    real_t lambda_e = lbm::collision_model::TRT::lambda_e( omega );
    real_t lambda_d = lbm::collision_model::TRT::lambda_d( omega, magicNumber );
 #ifdef WALBERLA_BUILD_WITH_CODEGEN
+#ifdef USE_CUMULANT
+   WALBERLA_LOG_INFO_ON_ROOT("Using generated cumulant lattice model!");
+   LatticeModel_T latticeModel = LatticeModel_T(omega);
+#elif defined(USE_TRTLIKE) || defined(USE_D3Q27TRTLIKE)
    WALBERLA_LOG_INFO_ON_ROOT("Using generated TRT-like lattice model!");
    LatticeModel_T latticeModel = LatticeModel_T(omegaBulkFieldID, lambda_d, lambda_e);
+#endif
 #else
    WALBERLA_LOG_INFO_ON_ROOT("Using waLBerla built-in MRT lattice model and ignoring omega bulk field since not supported!");
    LatticeModel_T latticeModel = LatticeModel_T(lbm::collision_model::D3Q19MRT( omegaBulk, omegaBulk, lambda_d, lambda_e, lambda_e, lambda_d ));
@@ -1117,11 +1128,7 @@ int main( int argc, char **argv )
    }else if( reconstructorType == "Ext")
    {
       auto sphereNormalExtrapolationDirectionFinder = make_shared<lbm_mesapd_coupling::SphereNormalExtrapolationDirectionFinder>(blocks);
-#ifdef USE_TRT_LIKE_LATTICE_MODEL
-      auto extrapolationReconstructor = lbm_mesapd_coupling::makeExtrapolationReconstructor<BoundaryHandling_T, lbm_mesapd_coupling::SphereNormalExtrapolationDirectionFinder, true>(blocks, boundaryHandlingID, sphereNormalExtrapolationDirectionFinder, uint_t(3), true);
-#else
       auto extrapolationReconstructor = lbm_mesapd_coupling::makeExtrapolationReconstructor<BoundaryHandling_T, lbm_mesapd_coupling::SphereNormalExtrapolationDirectionFinder, false>(blocks, boundaryHandlingID, sphereNormalExtrapolationDirectionFinder, uint_t(3), true);
-#endif
       timeloopAfterParticles.add() << BeforeFunction( fullPDFCommunicationScheme, "LBM Communication" )
                                    << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMO_Flag, Fluid_Flag, extrapolationReconstructor, conserveMomentum)), "PDF Restore" );
    } else
@@ -1308,9 +1315,8 @@ int main( int argc, char **argv )
          }
          else
          {
-            lbm_mesapd_coupling::RegularParticlesSelector sphereSelector;
             lbm_mesapd_coupling::AddHydrodynamicInteractionKernel addHydrodynamicInteraction;
-            ps->forEachParticle(useOpenMP, sphereSelector, *accessor, addHydrodynamicInteraction, *accessor );
+            ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addHydrodynamicInteraction, *accessor );
          }
 
          hydForce = getForce(sphereUid,*accessor) - lubForce - collisionForce;
diff --git a/apps/benchmarks/FluidParticleCouplingWithLoadBalancing/CMakeLists.txt b/apps/benchmarks/FluidParticleCouplingWithLoadBalancing/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4742f47b6648f374935d7d48f35d8ec3983c39f3
--- /dev/null
+++ b/apps/benchmarks/FluidParticleCouplingWithLoadBalancing/CMakeLists.txt
@@ -0,0 +1,3 @@
+waLBerla_add_executable( NAME FluidParticleWorkloadEvaluation FILES FluidParticleWorkloadEvaluation.cpp DEPENDS blockforest boundary core field lbm lbm_mesapd_coupling mesa_pd postprocessing timeloop vtk )
+
+waLBerla_add_executable( NAME FluidParticleWorkloadDistribution FILES FluidParticleWorkloadDistribution.cpp DEPENDS blockforest boundary core field lbm lbm_mesapd_coupling mesa_pd postprocessing timeloop vtk )
diff --git a/apps/benchmarks/FluidParticleCouplingWithLoadBalancing/FluidParticleWorkloadDistribution.cpp b/apps/benchmarks/FluidParticleCouplingWithLoadBalancing/FluidParticleWorkloadDistribution.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9f93af67b37cfec23a3543948c70da55c388542b
--- /dev/null
+++ b/apps/benchmarks/FluidParticleCouplingWithLoadBalancing/FluidParticleWorkloadDistribution.cpp
@@ -0,0 +1,1730 @@
+//======================================================================================================================
+//
+//  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 FluidParticleWorkloadDistribution.cpp
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#include "blockforest/Initialization.h"
+#include "blockforest/communication/UniformBufferedScheme.h"
+#include "blockforest/loadbalancing/all.h"
+#include "blockforest/AABBRefinementSelection.h"
+
+#include "boundary/all.h"
+
+#include "core/DataTypes.h"
+#include "core/Environment.h"
+#include "core/debug/Debug.h"
+#include "core/debug/TestSubsystem.h"
+#include "core/math/all.h"
+#include "core/timing/RemainingTimeLogger.h"
+#include "core/mpi/Broadcast.h"
+
+#include "domain_decomposition/SharedSweep.h"
+#include "domain_decomposition/BlockSweepWrapper.h"
+
+#include "field/AddToStorage.h"
+#include "field/StabilityChecker.h"
+#include "field/communication/PackInfo.h"
+
+#include "lbm/boundary/all.h"
+#include "lbm/communication/PdfFieldPackInfo.h"
+#include "lbm/field/AddToStorage.h"
+#include "lbm/field/PdfField.h"
+#include "lbm/field/VelocityFieldWriter.h"
+#include "lbm/lattice_model/D3Q19.h"
+#include "lbm/sweeps/CellwiseSweep.h"
+
+#include "lbm_mesapd_coupling/amr/BlockInfo.h"
+#include "lbm_mesapd_coupling/amr/InfoCollection.h"
+#include "lbm_mesapd_coupling/amr/weight_assignment/WeightEvaluationFunctions.h"
+#include "lbm_mesapd_coupling/amr/weight_assignment/WeightAssignmentFunctor.h"
+#include "lbm_mesapd_coupling/amr/weight_assignment/MetisAssignmentFunctor.h"
+#include "lbm_mesapd_coupling/mapping/ParticleMapping.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/MovingParticleMapping.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/boundary/CurvedLinear.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/reconstruction/Reconstructor.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/reconstruction/PdfReconstructionManager.h"
+#include "lbm_mesapd_coupling/utility/AddForceOnParticlesKernel.h"
+#include "lbm_mesapd_coupling/utility/ParticleSelector.h"
+#include "lbm_mesapd_coupling/DataTypes.h"
+#include "lbm_mesapd_coupling/utility/AverageHydrodynamicForceTorqueKernel.h"
+#include "lbm_mesapd_coupling/utility/AddHydrodynamicInteractionKernel.h"
+#include "lbm_mesapd_coupling/utility/ResetHydrodynamicForceTorqueKernel.h"
+#include "lbm_mesapd_coupling/utility/LubricationCorrectionKernel.h"
+#include "lbm_mesapd_coupling/utility/InitializeHydrodynamicForceTorqueForAveragingKernel.h"
+#include "lbm_mesapd_coupling/utility/InspectionProbe.h"
+
+#include "mesa_pd/collision_detection/AnalyticContactDetection.h"
+#include "mesa_pd/data/ParticleAccessorWithShape.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/ShapeStorage.h"
+#include "mesa_pd/data/DataTypes.h"
+#include "mesa_pd/data/shape/HalfSpace.h"
+#include "mesa_pd/data/shape/Sphere.h"
+#include "mesa_pd/domain/BlockForestDomain.h"
+#include "mesa_pd/domain/BlockForestDataHandling.h"
+#include "mesa_pd/kernel/AssocToBlock.h"
+#include "mesa_pd/kernel/DoubleCast.h"
+#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/LinearSpringDashpot.h"
+#include "mesa_pd/kernel/ParticleSelector.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
+#include "mesa_pd/mpi/ClearNextNeighborSync.h"
+#include "mesa_pd/mpi/SyncNextNeighbors.h"
+#include "mesa_pd/mpi/SyncNextNeighborsBlockForest.h"
+#include "mesa_pd/mpi/ReduceProperty.h"
+#include "mesa_pd/mpi/ReduceContactHistory.h"
+#include "mesa_pd/mpi/ContactFilter.h"
+#include "mesa_pd/mpi/notifications/ForceTorqueNotification.h"
+#include "mesa_pd/mpi/notifications/HydrodynamicForceTorqueNotification.h"
+#include "mesa_pd/vtk/ParticleVtkOutput.h"
+
+#include "timeloop/SweepTimeloop.h"
+
+#include "vtk/all.h"
+#include "field/vtk/all.h"
+#include "lbm/vtk/all.h"
+
+#include "Utility.h"
+
+namespace fluid_particle_workload_distribution
+{
+
+///////////
+// USING //
+///////////
+
+using namespace walberla;
+using walberla::uint_t;
+
+using LatticeModel_T = lbm::D3Q19< lbm::collision_model::TRT, false >;
+using Stencil_T = LatticeModel_T::Stencil;
+using PdfField_T = lbm::PdfField<LatticeModel_T>;
+using flag_t = walberla::uint8_t;
+using FlagField_T = FlagField<flag_t>;
+
+using ParticleField_T = lbm_mesapd_coupling::ParticleField_T;
+using ParticleAccessor_T = mesa_pd::data::ParticleAccessorWithShape;
+
+const uint_t FieldGhostLayers = 1;
+
+using NoSlip_T = lbm::NoSlip<LatticeModel_T, flag_t> ;
+using MO_T = lbm_mesapd_coupling::CurvedLinear< LatticeModel_T, FlagField_T, ParticleAccessor_T >;
+using BoundaryHandling_T = BoundaryHandling< FlagField_T, Stencil_T, NoSlip_T, MO_T >;
+
+
+///////////
+// FLAGS //
+///////////
+
+const FlagUID Fluid_Flag( "fluid" );
+const FlagUID NoSlip_Flag( "no slip" );
+const FlagUID MO_Flag( "moving obstacle" );
+const FlagUID FormerMO_Flag( "former moving obstacle" );
+
+
+///////////////////////////////////
+// LOAD BALANCING FUNCTIONALITY //
+//////////////////////////////////
+
+
+
+/////////////////////
+// BLOCK STRUCTURE //
+/////////////////////
+
+static void workloadAndMemoryAssignment( SetupBlockForest& forest )
+{
+   for (auto &block : forest) {
+      block.setWorkload( numeric_cast< workload_t >( uint_t(1) << block.getLevel() ) );
+      block.setMemory( numeric_cast< memory_t >(1) );
+   }
+}
+
+static shared_ptr< StructuredBlockForest > createBlockStructure( const AABB & domainAABB, Vector3<uint_t> blockSizeInCells,
+                                                                 bool useBox, const std::string & loadDistributionStrategy,
+                                                                 bool keepGlobalBlockInformation = false )
+{
+   SetupBlockForest sforest;
+
+   Vector3<uint_t> numberOfBlocksPerDirection( uint_c(domainAABB.size(0)) / blockSizeInCells[0],
+                                               uint_c(domainAABB.size(1)) / blockSizeInCells[1],
+                                               uint_c(domainAABB.size(2)) / blockSizeInCells[2] );
+
+   for(uint_t i = 0; i < 3; ++i )
+   {
+      WALBERLA_CHECK_EQUAL( numberOfBlocksPerDirection[i] * blockSizeInCells[i], uint_c(domainAABB.size(i)),
+                            "Domain can not be decomposed in direction " << i << " into fine blocks of size " << blockSizeInCells[i] );
+   }
+
+   sforest.addWorkloadMemorySUIDAssignmentFunction( workloadAndMemoryAssignment );
+
+   Vector3<bool> periodicity( true, true, false);
+   if( useBox )
+   {
+      periodicity[0] = false;
+      periodicity[1] = false;
+   }
+   sforest.init( domainAABB,
+                 numberOfBlocksPerDirection[0], numberOfBlocksPerDirection[1], numberOfBlocksPerDirection[2],
+                 periodicity[0], periodicity[1], periodicity[2]);
+
+   // calculate process distribution
+   const memory_t memoryLimit = math::Limits< memory_t >::inf();
+
+   if( loadDistributionStrategy == "Hilbert" )
+   {
+      bool useHilbert = true;
+      sforest.balanceLoad( blockforest::StaticLevelwiseCurveBalance(useHilbert), uint_c( MPIManager::instance()->numProcesses() ), real_t(0), memoryLimit, true );
+   } else if ( loadDistributionStrategy == "Morton" )
+   {
+      bool useHilbert = false;
+      sforest.balanceLoad( blockforest::StaticLevelwiseCurveBalance(useHilbert), uint_c( MPIManager::instance()->numProcesses() ), real_t(0), memoryLimit, true );
+   } else if ( loadDistributionStrategy == "ParMetis" )
+   {
+      blockforest::StaticLevelwiseParMetis::Algorithm algorithm = blockforest::StaticLevelwiseParMetis::Algorithm::PARMETIS_PART_GEOM_KWAY;
+      blockforest::StaticLevelwiseParMetis staticParMetis(algorithm);
+      sforest.balanceLoad( staticParMetis, uint_c( MPIManager::instance()->numProcesses() ), real_t(0), memoryLimit, true );
+   } else if (loadDistributionStrategy == "Diffusive" )
+   {
+      // also use Hilbert curve here
+      bool useHilbert = true;
+      sforest.balanceLoad( blockforest::StaticLevelwiseCurveBalance(useHilbert), uint_c( MPIManager::instance()->numProcesses() ), real_t(0), memoryLimit, true );
+   } else
+   {
+      WALBERLA_ABORT("Load distribution strategy \"" << loadDistributionStrategy << "\t not implemented! - Aborting" );
+   }
+
+   WALBERLA_LOG_INFO_ON_ROOT( sforest );
+
+
+   // create StructuredBlockForest (encapsulates a newly created BlockForest)
+   shared_ptr< StructuredBlockForest > sbf =
+         make_shared< StructuredBlockForest >( make_shared< BlockForest >( uint_c( MPIManager::instance()->rank() ), sforest, keepGlobalBlockInformation ),
+                                               blockSizeInCells[0], blockSizeInCells[1], blockSizeInCells[2]);
+   sbf->createCellBoundingBoxes();
+
+   return sbf;
+}
+
+/////////////////////////////////////
+// BOUNDARY HANDLING CUSTOMIZATION //
+/////////////////////////////////////
+class MyBoundaryHandling : public blockforest::AlwaysInitializeBlockDataHandling< BoundaryHandling_T >
+{
+public:
+   MyBoundaryHandling( const weak_ptr< StructuredBlockStorage > & blocks,
+                       const BlockDataID & flagFieldID, const BlockDataID & pdfFieldID,
+                       const BlockDataID & particleFieldID, const shared_ptr<ParticleAccessor_T>& ac ) :
+         blocks_( blocks ), flagFieldID_( flagFieldID ), pdfFieldID_( pdfFieldID ), particleFieldID_( particleFieldID ), ac_( ac )
+   {}
+
+   BoundaryHandling_T * initialize( IBlock * const block ) override;
+
+private:
+
+   weak_ptr< StructuredBlockStorage > blocks_;
+
+   const BlockDataID flagFieldID_;
+   const BlockDataID pdfFieldID_;
+   const BlockDataID particleFieldID_;
+   shared_ptr<ParticleAccessor_T> ac_;
+
+}; // class MyBoundaryHandling
+
+BoundaryHandling_T * MyBoundaryHandling::initialize( IBlock * const block )
+{
+   WALBERLA_ASSERT_NOT_NULLPTR( block );
+
+   auto * flagField = block->getData< FlagField_T >( flagFieldID_ );
+   auto *  pdfField = block->getData< PdfField_T > ( pdfFieldID_ );
+   auto* particleField = block->getData<lbm_mesapd_coupling::ParticleField_T>(particleFieldID_);
+
+   const auto fluid = flagField->flagExists( Fluid_Flag ) ? flagField->getFlag( Fluid_Flag ) : flagField->registerFlag( Fluid_Flag );
+
+   auto blocksPtr = blocks_.lock();
+   WALBERLA_CHECK_NOT_NULLPTR( blocksPtr );
+
+   BoundaryHandling_T * handling = new BoundaryHandling_T( "moving obstacle boundary handling", flagField, fluid,
+                                                           NoSlip_T( "NoSlip", NoSlip_Flag, pdfField ),
+                                                           MO_T( "MO", MO_Flag, pdfField, flagField, particleField, ac_, fluid, *blocksPtr, *block ),
+                                                           BoundaryHandling_T::Mode::ENTIRE_FIELD_TRAVERSAL);
+
+   handling->fillWithDomain( FieldGhostLayers );
+
+   return handling;
+}
+
+void clearBoundaryHandling( BlockForest & forest, const BlockDataID & boundaryHandlingID ) {
+   for( auto blockIt = forest.begin(); blockIt != forest.end(); ++blockIt )
+   {
+      BoundaryHandling_T * boundaryHandling = blockIt->getData<BoundaryHandling_T>(boundaryHandlingID);
+      boundaryHandling->clear( FieldGhostLayers );
+   }
+}
+
+void recreateBoundaryHandling( BlockForest & forest, const BlockDataID & boundaryHandlingID ) {
+   for( auto blockIt = forest.begin(); blockIt != forest.end(); ++blockIt )
+   {
+      BoundaryHandling_T * boundaryHandling = blockIt->getData<BoundaryHandling_T>(boundaryHandlingID);
+      boundaryHandling->fillWithDomain( FieldGhostLayers );
+   }
+}
+
+void clearParticleField( BlockForest & forest, const BlockDataID & particleFieldID, const ParticleAccessor_T & accessor ) {
+   for (auto blockIt = forest.begin(); blockIt != forest.end(); ++blockIt) {
+      ParticleField_T *particleField = blockIt->getData<ParticleField_T>(particleFieldID);
+      particleField->setWithGhostLayer(accessor.getInvalidUid());
+   }
+}
+//*******************************************************************************************************************
+
+void evaluateTimers(WcTimingPool & timingPool,
+                    const std::vector<std::vector<std::string> > & timerKeys,
+                    std::vector<real_t> & timings )
+{
+
+   for (auto & timingsIt : timings)
+   {
+      timingsIt = real_t(0);
+   }
+
+   timingPool.unifyRegisteredTimersAcrossProcesses();
+
+   for (auto i = uint_t(0); i < timerKeys.size(); ++i )
+   {
+      auto keys = timerKeys[i];
+      for (const auto &timerName : keys)
+      {
+         if(timingPool.timerExists(timerName))
+         {
+            timings[i] += real_c(timingPool[timerName].total());
+         }
+      }
+
+   }
+}
+
+uint_t evaluateEdgeCut(BlockForest & forest)
+{
+
+   //note: only works for edges in uniform grids
+
+   auto edgecut = uint_t(0); // = edge weights between processes
+
+   for( auto blockIt = forest.begin(); blockIt != forest.end(); ++blockIt )
+   {
+      auto * block = static_cast<blockforest::Block*> (&(*blockIt));
+
+      real_t blockVolume = block->getAABB().volume();
+      real_t approximateEdgeLength = std::cbrt( blockVolume );
+
+      uint_t faceNeighborWeight = uint_c(approximateEdgeLength * approximateEdgeLength ); //common face
+      uint_t edgeNeighborWeight = uint_c(approximateEdgeLength); //common edge
+      uint_t cornerNeighborWeight = uint_c( 1 ); //common corner
+
+
+      for( const uint_t idx : blockforest::getFaceNeighborhoodSectionIndices() )
+      {
+         for (auto nb = uint_t(0); nb < block->getNeighborhoodSectionSize(idx); ++nb)
+         {
+            if( block->neighborExistsRemotely(idx,nb) ) edgecut += faceNeighborWeight;
+         }
+      }
+
+      for( const uint_t idx : blockforest::getEdgeNeighborhoodSectionIndices() )
+      {
+         for (auto nb = uint_t(0); nb < block->getNeighborhoodSectionSize(idx); ++nb)
+         {
+            if( block->neighborExistsRemotely(idx,nb) ) edgecut += edgeNeighborWeight;
+         }
+      }
+
+      for( const uint_t idx : blockforest::getCornerNeighborhoodSectionIndices() )
+      {
+         for (auto nb = uint_t(0); nb < block->getNeighborhoodSectionSize(idx); ++nb)
+         {
+            if( block->neighborExistsRemotely(idx,nb) ) edgecut += cornerNeighborWeight;
+         }
+      }
+   }
+   return edgecut;
+}
+
+
+void evaluateTotalSimulationTimePassed(WcTimingPool & timeloopTimingPool, const std::string & simulationString, const std::string & loadbalancingString,
+                                       double & totalSimTime, double & totalLBTime)
+{
+   shared_ptr< WcTimingPool> reduced = timeloopTimingPool.getReduced(timing::REDUCE_TOTAL, 0);
+
+   double totalTime = 0.0;
+   WALBERLA_ROOT_SECTION(){
+      totalTime = (*reduced)[simulationString].total();
+   }
+   totalSimTime = totalTime;
+
+   double lbTime = 0.0;
+   WALBERLA_ROOT_SECTION(){
+      lbTime = (*reduced)[loadbalancingString].total();
+   }
+   totalLBTime = lbTime;
+}
+
+void createPlane( const shared_ptr<mesa_pd::data::ParticleStorage> & ps, const shared_ptr<mesa_pd::data::ShapeStorage> & ss,
+                  const Vector3<real_t> position, const Vector3<real_t> normal)
+{
+   mesa_pd::data::Particle&& p0 = *ps->create(true);
+   p0.setPosition(position);
+   p0.setShapeID(ss->create<mesa_pd::data::HalfSpace>( normal.getNormalized() ));
+   p0.setOwner(mpi::MPIManager::instance()->rank());
+   p0.setType(0);
+   mesa_pd::data::particle_flags::set(p0.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
+   mesa_pd::data::particle_flags::set(p0.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
+   WALBERLA_LOG_INFO_ON_ROOT("Created plane at position " << position << " with normal " << normal.getNormalized  ());
+}
+
+void createBasicPlaneSetup(const shared_ptr<mesa_pd::data::ParticleStorage> & ps, const shared_ptr<mesa_pd::data::ShapeStorage> & ss, const math::AABB & simulationDomain)
+{
+   createPlane(ps, ss, simulationDomain.minCorner(), Vector3<real_t>(0,0, 1));
+   //createPlane(ps, ss, Vector3<real_t>(simulationDomain.xMax() * 0.5, simulationDomain.yMax() * 0.5, simulationDomain.zMin() + std::max(simulationDomain.xMax(), simulationDomain.yMax()) * 0.5 ), Vector3<real_t>(0,1, 1));
+   createPlane(ps, ss, simulationDomain.maxCorner(), Vector3<real_t>(0,0,-1));
+}
+
+void addBoxPlaneSetup(const shared_ptr<mesa_pd::data::ParticleStorage> & ps, const shared_ptr<mesa_pd::data::ShapeStorage> & ss, const math::AABB & simulationDomain)
+{
+   // add bounding planes (four horizontal directions)
+   createPlane(ps, ss, simulationDomain.minCorner(), Vector3<real_t>( 1, 0,0));
+   createPlane(ps, ss, simulationDomain.maxCorner(), Vector3<real_t>(-1, 0,0));
+   createPlane(ps, ss, simulationDomain.minCorner(), Vector3<real_t>( 0, 1,0));
+   createPlane(ps, ss, simulationDomain.maxCorner(), Vector3<real_t>( 0,-1,0));
+}
+
+void addHopperPlaneSetup(const shared_ptr<mesa_pd::data::ParticleStorage> & ps, const shared_ptr<mesa_pd::data::ShapeStorage> & ss, const math::AABB & simulationDomain,
+                         real_t hopperRelHeight, real_t hopperRelOpening)
+{
+   //hopper planes
+   real_t xMax = simulationDomain.xMax();
+   real_t yMax = simulationDomain.yMax();
+   real_t zMax = simulationDomain.zMax();
+   Vector3<real_t> p1(0,0,hopperRelHeight*zMax);
+   Vector3<real_t> n1(p1[2],0,hopperRelOpening*xMax-p1[0]);
+   Vector3<real_t> n2(0,p1[2],hopperRelOpening*yMax-p1[0]);
+   createPlane(ps, ss, p1, n1);
+   createPlane(ps, ss, p1, n2);
+
+   Vector3<real_t> p2(xMax,yMax,hopperRelHeight*zMax);
+   Vector3<real_t> n3(-p2[2],0,-((real_t(1)-hopperRelOpening)*xMax-p2[0]));
+   Vector3<real_t> n4(0,-p2[2],-((real_t(1)-hopperRelOpening)*yMax-p2[1]));
+   createPlane(ps, ss, p2, n3);
+   createPlane(ps, ss, p2, n4);
+}
+
+void evaluateParticleSimulation(const shared_ptr<mesa_pd::data::ParticleStorage> & ps, const shared_ptr<mesa_pd::data::ShapeStorage> & ss,
+                                uint_t numParticles, uint_t numGlobalParticles)
+{
+   auto numShapes = ss->shapes.size();
+   uint_t numLocalParticles = 0;
+   uint_t numGhostParticles = 0;
+   uint_t numGlobalParticlesOfRank = 0;
+   for(auto p = ps->begin(); p != ps->end(); ++p)
+   {
+      using namespace walberla::mesa_pd::data::particle_flags;
+      if (isSet(p->getFlags(), GHOST))
+      {
+         ++numGhostParticles;
+      } else if (isSet(p->getFlags(), GLOBAL))
+      {
+         ++numGlobalParticlesOfRank;
+      } else
+      {
+         ++numLocalParticles;
+      }
+      if(p->getShapeID() >= numShapes)
+      {
+         WALBERLA_LOG_INFO("Found invalid shape id " << *p);
+      }
+   }
+   //WALBERLA_LOG_INFO(numShapes << " " << numLocalParticles << " " << numGhostParticles);
+
+   if(numGlobalParticlesOfRank != numGlobalParticles)
+   {
+      WALBERLA_LOG_INFO("Number of global particles has changed to " << numGlobalParticlesOfRank);
+   }
+
+   mpi::reduceInplace(numLocalParticles, mpi::SUM);
+   if(numLocalParticles != numParticles)
+   {
+      WALBERLA_LOG_INFO_ON_ROOT("Number of particles has changed to " << numLocalParticles);
+   }
+}
+
+bool isnan(Vector3<real_t> vec)
+{
+   return std::isnan(vec[0]) || std::isnan(vec[1]) || std::isnan(vec[2]);
+}
+
+void checkParticleProperties(const shared_ptr<mesa_pd::data::ParticleStorage> & ps)
+{
+   for(auto p = ps->begin(); p != ps->end(); ++p)
+   {
+      if(isnan(p->getHydrodynamicForce())) WALBERLA_LOG_INFO("Found nan in hyd Force " << *p);
+      if(isnan(p->getOldHydrodynamicForce())) WALBERLA_LOG_INFO("Found nan in old hyd Force " << *p);
+      if(isnan(p->getForce())) WALBERLA_LOG_INFO("Found nan in Force " << *p);
+      if(isnan(p->getOldForce())) WALBERLA_LOG_INFO("Found nan in Force " << *p);
+   }
+}
+
+template< typename Probe_T>
+void checkMapping(const shared_ptr< StructuredBlockStorage > & blocks, const BlockDataID pdfFieldID,
+                  const BlockDataID boundaryHandlingID, Probe_T & probe )
+{
+
+   for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+   {
+      auto * pdfField = blockIt->getData< PdfField_T >( pdfFieldID );
+      auto * boundaryHandling = blockIt->getData< BoundaryHandling_T >( boundaryHandlingID );
+
+      WALBERLA_FOR_ALL_CELLS_XYZ(pdfField,
+          if (boundaryHandling->isDomain(x, y, z))
+          {
+             uint_t f = uint_t(0);
+             //for( uint_t f = uint_t(0); f < PdfField_T::F_SIZE; ++f )
+             //{
+                if( !walberla::field::internal::stabilityCheckerIsFinite( pdfField->get( x, y, z, cell_idx_c(f) ) ) )
+                {
+
+                   Vector3< real_t > center;
+                   blocks->getBlockLocalCellCenter( *blockIt, Cell(x,y,z), center );
+
+                   Cell gCell(x,y,z);
+                   blocks->transformBlockLocalToGlobalCell( gCell, *blockIt);
+
+                   WALBERLA_LOG_INFO("Instability found in block local cell( " << x << ", " << y << ", " << z << " ) at index " << f
+                                         << " = global cell ( " << gCell.x() << ", " << gCell.y() << ", " << gCell.z() << " ) with cell center ( " << center[0] << ", " << center[1] << ", " << center[2] << " )");
+
+                   probe.setPosition(center);
+                   real_t rho;
+                   Vector3<real_t> velocity;
+                   probe(rho, velocity);
+
+                }
+             //}
+          }
+          );
+
+   }
+}
+
+//*******************************************************************************************************************
+/*!\brief Simulation of settling particles inside a rectangular column filled with viscous fluid
+ *
+ * This application is used in the paper
+ *  Rettinger, Ruede - "Dynamic Load Balancing Techniques for Particulate Flow Simulations", 2019, Computation
+ * in Section 4 to apply the load estimator and to evaluate different load distribution strategies.
+ * It has here been adapted to the new mesapd and its coupling.
+ * More infos can be found in the PhD thesis by Christoph Rettinger.
+ *
+ * It, however, features several different command line arguments that can be used to tweak the simulation.
+ * The setup can be horizontally period, a box or a hopper geometry (configurable, as in the paper).
+ * The size, resolution and used blocks for the domain partitioning can be changed.
+ * Initially, all particles are pushed upwards to obtain a dense packing at the top plane.
+ *
+ * Most importantly, the load balancing can be modified:
+ *  - load estimation strategies:
+ *    - pure LBM = number of cells per block = constant workload per block
+ *    - coupling based load estimator = use fitted function from Sec. 3 of paper
+ *  - load distribution strategies:
+ *    - space-filling curves: Hilbert and Morton
+ *    - ParMETIS (and several algorithms and parameters)
+ *    - diffusive (and options)
+ *  - load balancing frequency
+ */
+//*******************************************************************************************************************
+int main( int argc, char **argv )
+{
+   debug::enterTestMode();
+
+   mpi::Environment env( argc, argv );
+
+   MPIManager::instance()->useWorldComm();
+
+   ///////////////////
+   // Customization //
+   ///////////////////
+
+   // simulation control
+   bool shortRun = false;
+   bool funcTest = false;
+   bool fileIO = true;
+   bool checkSimulation = false;
+   uint_t vtkWriteFreqDD = 0; //domain decomposition
+   uint_t vtkWriteFreqPa = 0; //particles
+   uint_t vtkWriteFreqFl = 0; //fluid
+   uint_t vtkWriteFreq = 0; //general
+   uint_t vtkWriteFreqInit = 0; //initial (particle-only) simulation
+   std::string baseFolder = "vtk_out_WorkloadDistribution"; // folder for vtk and file output
+   bool useProgressLogging = false;
+
+   // physical setup
+   auto GalileoNumber = real_t(50);
+   auto densityRatio = real_t(1.5);
+   auto diameter = real_t(15);
+   auto solidVolumeFraction = real_t(0.1);
+   auto blockSize = uint_t(32);
+   auto XBlocks = uint_t(12);
+   auto YBlocks = uint_t(12);
+   auto ZBlocks = uint_t(16);
+   bool useBox = false;
+   bool useHopper = false;
+   auto hopperRelHeight = real_t(0.5); // for hopper setup
+   auto hopperRelOpening = real_t(0.3); // for hopper setup
+
+   auto timesteps = uint_t(80000);
+
+   //numerical parameters
+   auto loadBalancingCheckFrequency = uint_t(100);
+   auto numRPDSubCycles = uint_t(10);
+   bool useBlockForestSync = false;
+
+   // load balancing
+   std::string loadEvaluationStrategy = "LBM"; //LBM, Fit
+   std::string loadDistributionStrategy = "Hilbert"; //Morton, Hilbert, ParMetis, Diffusive
+   real_t blockBaseWeight = real_t(1);
+
+   auto parMetis_ipc2redist = real_t(1000);
+   auto parMetisTolerance = real_t(-1);
+   std::string parMetisAlgorithmString = "ADAPTIVE_REPART";
+
+   auto diffusionFlowIterations = uint_t(15);
+   auto diffusionMaxIterations = uint_t(20);
+
+   bool useNoSlipForPlanes = false;
+
+
+   for( int i = 1; i < argc; ++i )
+   {
+      if( std::strcmp( argv[i], "--shortRun" )                 == 0 ) { shortRun = true; continue; }
+      if( std::strcmp( argv[i], "--funcTest" )                 == 0 ) { funcTest = true; continue; }
+      if( std::strcmp( argv[i], "--fileIO" )                   == 0 ) { fileIO = true; continue; }
+      if( std::strcmp( argv[i], "--useProgressLogging" )       == 0 ) { useProgressLogging = true; continue; }
+      if( std::strcmp( argv[i], "--checkSimulation" )          == 0 ) { checkSimulation = true; continue; }
+      if( std::strcmp( argv[i], "--vtkWriteFreqDD" )           == 0 ) { vtkWriteFreqDD = uint_c( std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--vtkWriteFreqPa" )           == 0 ) { vtkWriteFreqPa = uint_c( std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--vtkWriteFreqFl" )           == 0 ) { vtkWriteFreqFl = uint_c( std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--vtkWriteFreq" )             == 0 ) { vtkWriteFreq = uint_c( std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--vtkWriteFreqInit" )         == 0 ) { vtkWriteFreqInit = uint_c( std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--baseFolder" )               == 0 ) { baseFolder = argv[++i]; continue; }
+      if( std::strcmp( argv[i], "--densityRatio" )             == 0 ) { densityRatio = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--Ga" )                       == 0 ) { GalileoNumber = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--diameter" )                 == 0 ) { diameter = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--blockSize" )                == 0 ) { blockSize = uint_c(std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--XBlocks" )                  == 0 ) { XBlocks = uint_c(std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--YBlocks" )                  == 0 ) { YBlocks = uint_c(std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--ZBlocks" )                  == 0 ) { ZBlocks = uint_c(std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--useBox" )                   == 0 ) { useBox = true; continue; }
+      if( std::strcmp( argv[i], "--useHopper" )                == 0 ) { useHopper = true; continue; }
+      if( std::strcmp( argv[i], "--hopperHeight" )             == 0 ) { hopperRelHeight = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--hopperOpening" )            == 0 ) { hopperRelOpening = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--timesteps" )                == 0 ) { timesteps = uint_c(std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--numRPDSubCycles" )          == 0 ) { numRPDSubCycles = uint_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--useBlockForestSync" )       == 0 ) { useBlockForestSync = true; continue; }
+      if( std::strcmp( argv[i], "--useNoSlipForPlanes" )       == 0 ) { useNoSlipForPlanes = true; continue; }
+      if( std::strcmp( argv[i], "--blockBaseWeight" )          == 0 ) { blockBaseWeight = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--loadBalancingCheckFrequency" ) == 0 ) { loadBalancingCheckFrequency = uint_c( std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--loadEvaluationStrategy" )   == 0 ) { loadEvaluationStrategy = argv[++i]; continue; }
+      if( std::strcmp( argv[i], "--loadDistributionStrategy" ) == 0 ) { loadDistributionStrategy = argv[++i]; continue; }
+      if( std::strcmp( argv[i], "--ipc2redist" )               == 0 ) { parMetis_ipc2redist = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--parMetisTolerance" )        == 0 ) { parMetisTolerance = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--parMetisAlgorithm" )        == 0 ) { parMetisAlgorithmString = argv[++i]; continue; }
+      if( std::strcmp( argv[i], "--diffusionFlowIterations" )  == 0 ) { diffusionFlowIterations = uint_c(std::atof(argv[++i])); continue; }
+      if( std::strcmp( argv[i], "--diffusionMaxIterations" )   == 0 ) { diffusionMaxIterations = uint_c(std::atof(argv[++i])); continue; }
+      WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
+   }
+
+   if( fileIO )
+   {
+      WALBERLA_ROOT_SECTION(){
+         // create base directory if it does not yet exist
+         filesystem::path tpath( baseFolder );
+         if( !filesystem::exists( tpath ) )
+            filesystem::create_directory( tpath );
+      }
+      WALBERLA_MPI_BARRIER();
+   }
+
+   if(useProgressLogging) logging::Logging::instance()->includeLoggingToFile(baseFolder + "/progress_logging.txt");
+
+   if( loadEvaluationStrategy != "LBM" && loadEvaluationStrategy != "Fit" && loadEvaluationStrategy != "FitMulti")
+   {
+      WALBERLA_ABORT("Invalid load evaluation strategy: " << loadEvaluationStrategy);
+   }
+
+   if( vtkWriteFreq != 0 )
+   {
+      vtkWriteFreqDD = vtkWriteFreq;
+      vtkWriteFreqPa = vtkWriteFreq;
+      vtkWriteFreqFl = vtkWriteFreq;
+   }
+
+   if( diameter > real_c(blockSize) )
+   {
+      WALBERLA_LOG_WARNING("Particle Synchronization might not work since bodies are large compared to block size!");
+   }
+
+   if( useHopper )
+   {
+      WALBERLA_CHECK(hopperRelHeight >= real_t(0) && hopperRelHeight <= real_t(1), "Invalid relative hopper height of " << hopperRelHeight);
+      WALBERLA_CHECK(hopperRelOpening >= real_t(0) && hopperRelOpening <= real_t(1), "Invalid relative hopper opening of " << hopperRelOpening);
+   }
+
+
+   //////////////////////////
+   // NUMERICAL PARAMETERS //
+   //////////////////////////
+
+   const Vector3<uint_t> domainSize( XBlocks * blockSize, YBlocks * blockSize, ZBlocks * blockSize );
+   const auto domainVolume = real_t(domainSize[0] * domainSize[1] * domainSize[2]);
+   const real_t sphereVolume = math::pi / real_t(6) * diameter * diameter * diameter;
+   const uint_t numberOfSediments = uint_c(std::ceil(solidVolumeFraction * domainVolume / sphereVolume));
+
+   real_t expectedSedimentVolumeFraction = (useBox||useHopper) ? real_t(0.45) : real_t(0.52);
+   const real_t expectedSedimentedVolume = real_t(1)/expectedSedimentVolumeFraction * real_c(numberOfSediments) * sphereVolume;
+   const real_t expectedSedimentedHeight = std::max(diameter, expectedSedimentedVolume / real_c(domainSize[0] * domainSize[1]));
+
+   const auto uRef = real_t(0.02);
+   const real_t xRef = diameter;
+   const real_t tRef = xRef / uRef;
+
+   const real_t gravitationalAcceleration = uRef * uRef / ( (densityRatio-real_t(1)) * diameter );
+   const real_t viscosity = uRef * diameter / GalileoNumber;
+   const real_t omega = lbm::collision_model::omegaFromViscosity(viscosity);
+   const real_t tau = real_t(1) / omega;
+
+   const auto dx = real_t(1);
+   const real_t overlap = real_t( 1.5 ) * dx;
+
+   timesteps = funcTest ? 1 : ( shortRun ? uint_t(100) : timesteps );
+
+   WALBERLA_LOG_INFO_ON_ROOT("Setup (in simulation, i.e. lattice, units):");
+   WALBERLA_LOG_INFO_ON_ROOT(" - domain size = " << domainSize);
+   WALBERLA_LOG_INFO_ON_ROOT(" - sediment diameter = " << diameter );
+   WALBERLA_LOG_INFO_ON_ROOT(" - Galileo number = " << GalileoNumber );
+   WALBERLA_LOG_INFO_ON_ROOT(" - number of sediments: " << numberOfSediments);
+   WALBERLA_LOG_INFO_ON_ROOT(" - densityRatio = " << densityRatio );
+   WALBERLA_LOG_INFO_ON_ROOT(" - fluid: relaxation time (tau) = " << tau << ", kin. visc = " << viscosity );
+   WALBERLA_LOG_INFO_ON_ROOT(" - gravitational acceleration = " << gravitationalAcceleration );
+   WALBERLA_LOG_INFO_ON_ROOT(" - reference values: x = " << xRef << ", t = " << tRef << ", vel = " << uRef);
+   WALBERLA_LOG_INFO_ON_ROOT(" - omega: " << omega);
+   if( vtkWriteFreqDD > 0 )
+   {
+      WALBERLA_LOG_INFO_ON_ROOT(" - writing vtk files of domain decomposition to folder \"" << baseFolder << "\" with frequency " << vtkWriteFreqDD);
+   }
+   if( vtkWriteFreqPa > 0 )
+   {
+      WALBERLA_LOG_INFO_ON_ROOT(" - writing vtk files of bodies data to folder \"" << baseFolder << "\" with frequency " << vtkWriteFreqPa);
+   }
+   if( vtkWriteFreqFl > 0 )
+   {
+      WALBERLA_LOG_INFO_ON_ROOT(" - writing vtk files of fluid data to folder \"" << baseFolder << "\" with frequency " << vtkWriteFreqFl);
+   }
+   if( useBox )
+   {
+      WALBERLA_LOG_INFO_ON_ROOT(" - using box setup");
+   }
+   else if ( useHopper )
+   {
+      WALBERLA_LOG_INFO_ON_ROOT(" - using hopper setup");
+   }
+   else
+   {
+      WALBERLA_LOG_INFO_ON_ROOT(" - using horizontally periodic domain");
+   }
+
+   WALBERLA_LOG_INFO_ON_ROOT(" - refinement / load balancing check frequency: " << loadBalancingCheckFrequency);
+   WALBERLA_LOG_INFO_ON_ROOT(" - load evaluation strategy: " << loadEvaluationStrategy);
+   WALBERLA_LOG_INFO_ON_ROOT(" - load distribution strategy: " << loadDistributionStrategy);
+
+   ///////////////////////////
+   // BLOCK STRUCTURE SETUP //
+   ///////////////////////////
+
+   Vector3<uint_t> blockSizeInCells( blockSize );
+
+   AABB simulationDomain( real_t(0), real_t(0), real_t(0), real_c(domainSize[0]), real_c(domainSize[1]), real_c(domainSize[2]) );
+   AABB sedimentDomain( real_t(0), real_t(0), real_c(domainSize[2]) - expectedSedimentedHeight, real_c(domainSize[0]), real_c(domainSize[1]), real_c(domainSize[2]) );
+
+   auto blocks = createBlockStructure( simulationDomain, blockSizeInCells, (useBox||useHopper), loadDistributionStrategy );
+
+   //write initial domain decomposition to file
+   if( vtkWriteFreqDD > 0 )
+   {
+      vtk::writeDomainDecomposition( blocks, "initial_domain_decomposition", baseFolder );
+   }
+
+   /////////
+   // RPD //
+   /////////
+
+   const real_t restitutionCoeff = real_t(0.97);
+   const real_t frictionCoeffStatic = real_t(0.8);
+   const real_t frictionCoeffDynamic = real_t(0.15);
+   const real_t collisionTime = real_t(4) * diameter; // from my paper
+   const real_t poissonsRatio = real_t(0.22);
+   const real_t kappa = real_t(2) * ( real_t(1) - poissonsRatio ) / ( real_t(2) - poissonsRatio ) ;
+
+   auto rpdDomain = std::make_shared<mesa_pd::domain::BlockForestDomain>(blocks->getBlockForestPointer());
+
+   //init data structures
+   auto ps = walberla::make_shared<mesa_pd::data::ParticleStorage>(1);
+   blocks->addBlockData(mesa_pd::domain::createBlockForestDataHandling(ps), "Particle Storage"); // returned ID is not used, but ps has to be known to blockforest
+   auto ss = walberla::make_shared<mesa_pd::data::ShapeStorage>();
+   auto accessor = walberla::make_shared<ParticleAccessor_T >(ps, ss);
+
+   real_t timeStepSizeRPD = real_t(1)/real_t(numRPDSubCycles);
+   mesa_pd::kernel::VelocityVerletPreForceUpdate  vvIntegratorPreForce(timeStepSizeRPD);
+   mesa_pd::kernel::VelocityVerletPostForceUpdate vvIntegratorPostForce(timeStepSizeRPD);
+
+   // types: 0 = wall, 1: sphere
+   mesa_pd::kernel::LinearSpringDashpot collisionResponse(2);
+   collisionResponse.setFrictionCoefficientDynamic(0,1,frictionCoeffDynamic);
+   collisionResponse.setFrictionCoefficientDynamic(1,1,frictionCoeffDynamic);
+   collisionResponse.setFrictionCoefficientStatic(0,1,frictionCoeffStatic);
+   collisionResponse.setFrictionCoefficientStatic(1,1,frictionCoeffStatic);
+
+   const real_t particleMass = densityRatio * sphereVolume;
+   const real_t effMass_sphereWall = particleMass;
+   const real_t effMass_sphereSphere = particleMass * particleMass / ( real_t(2) * particleMass );
+   collisionResponse.setStiffnessAndDamping(0,1,restitutionCoeff,collisionTime,kappa,effMass_sphereWall);
+   collisionResponse.setStiffnessAndDamping(1,1,restitutionCoeff,collisionTime,kappa,effMass_sphereSphere);
+
+   mesa_pd::mpi::ReduceProperty reduceProperty;
+   mesa_pd::mpi::ReduceContactHistory reduceAndSwapContactHistory;
+   mesa_pd::mpi::SyncNextNeighbors syncNextNeighborFunc;
+   mesa_pd::kernel::AssocToBlock associateToBlock(blocks->getBlockForestPointer());
+   mesa_pd::mpi::SyncNextNeighborsBlockForest syncNextNeighborBlockForestFunc;
+
+
+   // create bounding planes
+
+   createBasicPlaneSetup(ps, ss, simulationDomain);
+   if(useBox || useHopper) addBoxPlaneSetup(ps, ss, simulationDomain);
+   if(useHopper) addHopperPlaneSetup(ps, ss, simulationDomain, hopperRelHeight, hopperRelOpening);
+
+   auto numGlobalParticles = ps->size();
+   WALBERLA_LOG_INFO_ON_ROOT("Created " << numGlobalParticles << " global particles");
+
+   // add the sediments
+
+   WALBERLA_LOG_INFO_ON_ROOT("Starting creation of sediments");
+
+   AABB sedimentGenerationDomain( real_t(0), real_t(0), real_t(0.5)*real_c(domainSize[2]), real_c(domainSize[0]), real_c(domainSize[1]), real_c(domainSize[2]) );
+
+   auto xParticle = real_t(0);
+   auto yParticle = real_t(0);
+   auto zParticle = real_t(0);
+
+   auto rank = mpi::MPIManager::instance()->rank();
+
+   auto sphereShape = ss->create<mesa_pd::data::Sphere>( diameter * real_t(0.5) );
+   ss->shapes[sphereShape]->updateMassAndInertia(densityRatio);
+
+   std::mt19937 randomGenerator (static_cast<unsigned int>(2610)); // fixed seed: quasi-random and reproducable
+
+   for( uint_t nSed = 0; nSed < numberOfSediments; ++nSed )
+   {
+
+      WALBERLA_ROOT_SECTION()
+      {
+         xParticle = math::realRandom<real_t>(sedimentGenerationDomain.xMin(), sedimentGenerationDomain.xMax(),randomGenerator);
+         yParticle = math::realRandom<real_t>(sedimentGenerationDomain.yMin(), sedimentGenerationDomain.yMax(),randomGenerator);
+         zParticle = math::realRandom<real_t>(sedimentGenerationDomain.zMin(), sedimentGenerationDomain.zMax(),randomGenerator);
+      }
+
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::broadcastObject( xParticle );
+         mpi::broadcastObject( yParticle );
+         mpi::broadcastObject( zParticle );
+      }
+
+      auto position = Vector3<real_t>( xParticle, yParticle, zParticle );
+
+      if (!rpdDomain->isContainedInProcessSubdomain(uint_c(rank), position)) continue;
+      auto p = ps->create();
+      p->setPosition(position);
+      p->setInteractionRadius(diameter * real_t(0.5));
+      p->setShapeID(sphereShape);
+      p->setType(1);
+      p->setOwner(rank);
+
+   }
+
+   if(useBlockForestSync)
+   {
+      ps->forEachParticle(false, mesa_pd::kernel::SelectLocal(), *accessor, associateToBlock, *accessor);
+      syncNextNeighborBlockForestFunc(*ps, blocks->getBlockForestPointer(), rpdDomain, overlap);
+
+   } else
+   {
+      syncNextNeighborFunc(*ps, *rpdDomain, overlap);
+   }
+
+
+   // Carry out particle-only simulation to obtain dense packing at top plane
+   // consists of three phases:
+   // 1: carefully resolve initial overlaps due to random generation, no gravity
+   // 2: apply low gravity in positive z-direction to create packing until convergence or targeted packing height reached
+   // 3: carry out a few time steps with gravity in negative direction to relay the system towards the real setup
+
+   const bool useOpenMP = false;
+   const real_t dt_RPD_Init = real_t(1);
+   const auto particleSimStepsPhase1 = uint_t(1000);
+   const auto maxParticleSimStepsPhase2 = (shortRun) ? uint_t(10) : uint_t(200000);
+   const auto particleSimStepsPhase3 = uint_t(std::sqrt(real_t(2)/std::fabs(gravitationalAcceleration)));
+
+   uint_t maxInitialParticleSimSteps = particleSimStepsPhase1 + maxParticleSimStepsPhase2 + particleSimStepsPhase3;
+
+   auto particleVtkOutput = make_shared<mesa_pd::vtk::ParticleVtkOutput>(ps);
+   particleVtkOutput->addOutput<mesa_pd::data::SelectParticleLinearVelocity>("velocity");
+   particleVtkOutput->addOutput<mesa_pd::data::SelectParticleOwner>("owner");
+   particleVtkOutput->setParticleSelector( [sphereShape](const mesa_pd::data::ParticleStorage::iterator& pIt) {return !mesa_pd::data::particle_flags::isSet(pIt->getFlags(), mesa_pd::data::particle_flags::GHOST) && pIt->getShapeID() == sphereShape;} ); //limit output to local sphere
+   auto particleVtkWriterInit = vtk::createVTKOutput_PointData(particleVtkOutput, "Particles_init", 1, baseFolder, "simulation_step");
+
+   real_t gravitationalAccelerationGeneration = gravitationalAcceleration;
+   auto oldMinParticlePosition = real_t(0);
+   real_t phase2ConvergenceLimit = std::fabs(gravitationalAccelerationGeneration);
+   real_t heightConvergenceThreshold = sedimentDomain.zMin();
+
+   uint_t beginOfPhase3SimStep = uint_t(0);
+
+   uint_t currentPhase = 1;
+
+   for(auto pet = uint_t(0); pet <= maxInitialParticleSimSteps; ++pet )
+   {
+
+      real_t maxPenetrationDepth = 0;
+      ps->forEachParticlePairHalf(useOpenMP, mesa_pd::kernel::ExcludeInfiniteInfinite(), *accessor,
+                                  [&collisionResponse, &rpdDomain, &maxPenetrationDepth, dt_RPD_Init]
+                                        (const size_t idx1, const size_t idx2, auto& ac)
+                                  {
+                                     mesa_pd::collision_detection::AnalyticContactDetection acd;
+                                     mesa_pd::kernel::DoubleCast double_cast;
+                                     mesa_pd::mpi::ContactFilter contact_filter;
+                                     if (double_cast(idx1, idx2, ac, acd, ac ))
+                                     {
+                                        if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), *rpdDomain))
+                                        {
+                                           maxPenetrationDepth = std::max(maxPenetrationDepth, std::abs(acd.getPenetrationDepth()));
+                                           collisionResponse(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(),
+                                                             acd.getContactNormal(), acd.getPenetrationDepth(), dt_RPD_Init);
+                                        }
+                                     }
+                                  },
+                                  *accessor );
+
+      reduceAndSwapContactHistory(*ps);
+
+      mpi::allReduceInplace(maxPenetrationDepth, mpi::MAX);
+
+      reduceProperty.operator()<mesa_pd::ForceTorqueNotification>(*ps);
+
+      ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, mesa_pd::kernel::ExplicitEuler(dt_RPD_Init), *accessor);
+      if(useBlockForestSync)
+      {
+         syncNextNeighborBlockForestFunc(*ps, blocks->getBlockForestPointer(), rpdDomain, overlap);
+         ps->forEachParticle(false, mesa_pd::kernel::SelectLocal(), *accessor, associateToBlock, *accessor);
+
+      } else
+      {
+         syncNextNeighborFunc(*ps, *rpdDomain, overlap);
+      }
+
+      if( vtkWriteFreqInit > uint_t(0) && pet % vtkWriteFreqInit == uint_t(0) )
+      {
+         particleVtkWriterInit->write();
+      }
+
+
+      if(currentPhase == 1)
+      {
+         // damp velocites to avoid too large ones
+         ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor,
+                              [](const size_t idx, ParticleAccessor_T& ac){
+                                 ac.setLinearVelocity(idx, ac.getLinearVelocity(idx) * real_t(0.5));
+                                 ac.setAngularVelocity(idx, ac.getAngularVelocity(idx) * real_t(0.5));
+                              }, *accessor);
+
+         if(pet > particleSimStepsPhase1)
+         {
+            WALBERLA_LOG_INFO_ON_ROOT("Starting phase 2 of initial particle simulation, with height threshold = " << heightConvergenceThreshold);
+            currentPhase = 2;
+
+            ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor,
+                                 [](const size_t idx, ParticleAccessor_T& ac){
+                                    ac.setLinearVelocity(idx, Vector3<real_t>(0.0));
+                                    ac.setAngularVelocity(idx, Vector3<real_t>(0.0));
+                                 }, *accessor);
+         }
+      } else if(currentPhase == 2)
+      {
+
+         Vector3<real_t> gravitationalForce( real_t(0), real_t(0), (densityRatio - real_t(1)) * gravitationalAccelerationGeneration * sphereVolume );
+         lbm_mesapd_coupling::AddForceOnParticlesKernel addGravitationalForce(gravitationalForce);
+         ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addGravitationalForce, *accessor );
+
+
+         real_t minParticlePosition = sedimentGenerationDomain.zMax();
+         ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor,
+                              [&minParticlePosition](const size_t idx, ParticleAccessor_T& ac){
+                                 minParticlePosition = std::min(ac.getPosition(idx)[2], minParticlePosition);
+                              }, *accessor);
+
+         WALBERLA_MPI_SECTION()
+         {
+            mpi::allReduceInplace(minParticlePosition, mpi::MIN);
+         }
+
+         WALBERLA_ROOT_SECTION()
+         {
+            if( pet % 100 == 0)
+            {
+               WALBERLA_LOG_INFO("[" << pet << "] Min position of all particles = " << minParticlePosition << " with goal height " << heightConvergenceThreshold);
+            }
+         }
+
+         if( minParticlePosition > heightConvergenceThreshold ) currentPhase = 3;
+
+         if( pet % 500 == 0)
+         {
+            if( std::fabs(minParticlePosition - oldMinParticlePosition) / minParticlePosition  < phase2ConvergenceLimit ) currentPhase = 3;
+            oldMinParticlePosition = minParticlePosition;
+         }
+
+         if( currentPhase == 3)
+         {
+            WALBERLA_LOG_INFO_ON_ROOT("Starting phase 3 of initial particle simulation");
+            beginOfPhase3SimStep = pet;
+         }
+
+      } else if(currentPhase == 3)
+      {
+         Vector3<real_t> gravitationalForce( real_t(0), real_t(0), -(densityRatio - real_t(1)) * gravitationalAccelerationGeneration * sphereVolume );
+         lbm_mesapd_coupling::AddForceOnParticlesKernel addGravitationalForce(gravitationalForce);
+         ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addGravitationalForce, *accessor );
+
+         if(pet - beginOfPhase3SimStep > particleSimStepsPhase3)
+         {
+            Vector3<real_t> initialParticleVelocity(real_t(0));
+            WALBERLA_LOG_INFO_ON_ROOT("Setting initial velocity " << initialParticleVelocity << " of all particles");
+            // reset velocities to avoid too large ones
+            ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor,
+                                 [initialParticleVelocity](const size_t idx, ParticleAccessor_T& ac){
+                                    ac.setLinearVelocity(idx, initialParticleVelocity);
+                                    ac.setAngularVelocity(idx, Vector3<real_t>(0));
+                                 }, *accessor);
+            break;
+         }
+      }
+   }
+   WALBERLA_LOG_INFO_ON_ROOT("Sediment layer creation done!");
+
+   ///////////////////////
+   // ADD DATA TO BLOCKS //
+   ////////////////////////
+
+   // create the lattice model
+   LatticeModel_T latticeModel = LatticeModel_T( lbm::collision_model::TRT::constructWithMagicNumber( omega, lbm::collision_model::TRT::threeSixteenth ) );
+
+   // add PDF field
+   BlockDataID pdfFieldID = lbm::addPdfFieldToStorage< LatticeModel_T >( blocks, "pdf field (zyxf)", latticeModel,
+                                                                         Vector3< real_t >( real_t(0) ), real_t(1),
+                                                                         FieldGhostLayers, field::zyxf );
+   // add flag field
+   BlockDataID flagFieldID = field::addFlagFieldToStorage<FlagField_T>( blocks, "flag field", FieldGhostLayers );
+
+   // add particle field
+   BlockDataID particleFieldID = field::addToStorage<lbm_mesapd_coupling::ParticleField_T>( blocks, "particle field", accessor->getInvalidUid(), field::zyxf, FieldGhostLayers );
+
+   // add boundary handling & initialize outer domain boundaries
+   BlockDataID boundaryHandlingID = blocks->addBlockData( make_shared< MyBoundaryHandling >( blocks, flagFieldID, pdfFieldID, particleFieldID, accessor ), "boundary handling" );
+
+   Vector3<real_t> gravitationalForce( real_t(0), real_t(0), -(densityRatio - real_t(1)) * gravitationalAcceleration * sphereVolume );
+   lbm_mesapd_coupling::AddForceOnParticlesKernel addGravitationalForce(gravitationalForce);
+   lbm_mesapd_coupling::ResetHydrodynamicForceTorqueKernel resetHydrodynamicForceTorque;
+   lbm_mesapd_coupling::AverageHydrodynamicForceTorqueKernel averageHydrodynamicForceTorque;
+   lbm_mesapd_coupling::LubricationCorrectionKernel lubricationCorrectionKernel(viscosity, [](real_t r){return (real_t(0.001 + real_t(0.00007)*r))*r;});
+   lbm_mesapd_coupling::ParticleMappingKernel<BoundaryHandling_T> particleMappingKernel(blocks, boundaryHandlingID);
+   lbm_mesapd_coupling::MovingParticleMappingKernel<BoundaryHandling_T> movingParticleMappingKernel(blocks, boundaryHandlingID, particleFieldID);
+   lbm_mesapd_coupling::ParticleMappingKernel<BoundaryHandling_T> fixedParticleMappingKernel(blocks, boundaryHandlingID);
+   lbm_mesapd_coupling::InitializeHydrodynamicForceTorqueForAveragingKernel initializeHydrodynamicForceTorqueForAveragingKernel;
+
+   WALBERLA_LOG_INFO_ON_ROOT(" - Lubrication correction:");
+   WALBERLA_LOG_INFO_ON_ROOT("   - normal cut off distance = " << lubricationCorrectionKernel.getNormalCutOffDistance());
+   WALBERLA_LOG_INFO_ON_ROOT("   - tangential translational cut off distance = " << lubricationCorrectionKernel.getTangentialTranslationalCutOffDistance());
+   WALBERLA_LOG_INFO_ON_ROOT("   - tangential rotational cut off distance = " << lubricationCorrectionKernel.getTangentialRotationalCutOffDistance());
+   const real_t maximumLubricationCutOffDistance = std::max(lubricationCorrectionKernel.getNormalCutOffDistance(), std::max(lubricationCorrectionKernel.getTangentialRotationalCutOffDistance(), lubricationCorrectionKernel.getTangentialTranslationalCutOffDistance()));
+
+   std::function<void(void)> particleMappingCall = [ps, accessor, &movingParticleMappingKernel, &fixedParticleMappingKernel, useNoSlipForPlanes]()
+   {
+      // map planes into the LBM simulation -> act as no-slip boundaries
+      if(useNoSlipForPlanes)
+      {
+         ps->forEachParticle(false, lbm_mesapd_coupling::GlobalParticlesSelector(), *accessor, fixedParticleMappingKernel, *accessor, NoSlip_Flag);
+      }
+      else {
+         ps->forEachParticle(false, lbm_mesapd_coupling::GlobalParticlesSelector(), *accessor, movingParticleMappingKernel, *accessor, MO_Flag);
+      }
+      // map particles into the LBM simulation
+      ps->forEachParticle(false, lbm_mesapd_coupling::RegularParticlesSelector(), *accessor, movingParticleMappingKernel, *accessor, MO_Flag);
+   };
+
+   particleMappingCall();
+
+
+   //////////////////////////
+   // LOAD BALANCING UTILs //
+   //////////////////////////
+
+   auto & blockforest = blocks->getBlockForest();
+   blockforest.recalculateBlockLevelsInRefresh( false ); // = only load balancing, no refinement checking
+   blockforest.alwaysRebalanceInRefresh( true ); //load balancing every time refresh is triggered
+   blockforest.reevaluateMinTargetLevelsAfterForcedRefinement( false );
+   blockforest.allowRefreshChangingDepth( false );
+   blockforest.allowMultipleRefreshCycles( false ); // otherwise info collections are invalid
+
+   shared_ptr<lbm_mesapd_coupling::amr::InfoCollection> couplingInfoCollection = walberla::make_shared<lbm_mesapd_coupling::amr::InfoCollection>();
+   uint_t numberOfProcesses = uint_c(MPIManager::instance()->numProcesses());
+
+   if( loadDistributionStrategy == "Hilbert" || loadDistributionStrategy == "Morton")
+   {
+
+      bool curveAllGather = true;
+      bool balanceLevelwise = true;
+
+      if( loadDistributionStrategy == "Hilbert")
+      {
+         bool useHilbert = true;
+         blockforest.setRefreshPhantomBlockMigrationPreparationFunction( blockforest::DynamicCurveBalance< blockforest::PODPhantomWeight<real_t> >( useHilbert, curveAllGather, balanceLevelwise ) );
+      }
+      else if (loadDistributionStrategy == "Morton" )
+      {
+         bool useHilbert = false;
+         blockforest.setRefreshPhantomBlockMigrationPreparationFunction( blockforest::DynamicCurveBalance< blockforest::PODPhantomWeight<real_t> >( useHilbert, curveAllGather, balanceLevelwise ) );
+      }
+
+      if( loadEvaluationStrategy == "Fit" )
+      {
+         lbm_mesapd_coupling::amr::WeightEvaluationFunctor weightEvaluationFunctor(couplingInfoCollection, lbm_mesapd_coupling::amr::fittedTotalWeightEvaluationFunction);
+         lbm_mesapd_coupling::amr::WeightAssignmentFunctor weightAssignmentFunctor(weightEvaluationFunctor);
+         weightAssignmentFunctor.setBlockBaseWeight(blockBaseWeight);
+         blockforest.setRefreshPhantomBlockDataAssignmentFunction(weightAssignmentFunctor);
+      }
+      else if( loadEvaluationStrategy == "LBM" )
+      {
+         lbm_mesapd_coupling::amr::WeightAssignmentFunctor weightAssignmentFunctor(lbm_mesapd_coupling::amr::defaultWeightEvaluationFunction);
+         weightAssignmentFunctor.setBlockBaseWeight(blockBaseWeight);
+         blockforest.setRefreshPhantomBlockDataAssignmentFunction(weightAssignmentFunctor);
+      }
+      else
+      {
+         WALBERLA_ABORT("Invalid load evaluation strategy: " << loadEvaluationStrategy);
+      }
+
+      blockforest.setRefreshPhantomBlockDataPackFunction(blockforest::PODPhantomWeightPackUnpack<real_t>());
+      blockforest.setRefreshPhantomBlockDataUnpackFunction(blockforest::PODPhantomWeightPackUnpack<real_t>());
+
+   }
+   else if( loadDistributionStrategy == "ParMetis")
+   {
+
+#ifndef WALBERLA_BUILD_WITH_PARMETIS
+      WALBERLA_ABORT( "You are trying to use ParMetis functionality but waLBerla is not configured to use it. Set 'WALBERLA_BUILD_WITH_PARMETIS' to 'ON' in your CMake cache to build against an installed version of ParMetis!" );
+#endif
+
+      blockforest::DynamicParMetis::Algorithm parMetisAlgorithm = blockforest::DynamicParMetis::stringToAlgorithm(parMetisAlgorithmString);
+      blockforest::DynamicParMetis::WeightsToUse parMetisWeightsToUse = blockforest::DynamicParMetis::WeightsToUse::PARMETIS_BOTH_WEIGHTS;
+      blockforest::DynamicParMetis::EdgeSource parMetisEdgeSource = blockforest::DynamicParMetis::EdgeSource::PARMETIS_EDGES_FROM_EDGE_WEIGHTS;
+
+      blockforest::DynamicParMetis dynamicParMetis(parMetisAlgorithm, parMetisWeightsToUse, parMetisEdgeSource);
+      dynamicParMetis.setipc2redist(parMetis_ipc2redist);
+
+      auto numberOfBlocks = XBlocks * YBlocks * ZBlocks;
+
+      real_t loadImbalanceTolerance = (parMetisTolerance < real_t(1)) ? std::max(real_t(1.05), real_t(1) + real_t(1) / ( real_c(numberOfBlocks) / real_c(numberOfProcesses) ) ) : parMetisTolerance;
+      dynamicParMetis.setImbalanceTolerance(double(loadImbalanceTolerance), 0);
+
+      WALBERLA_LOG_INFO_ON_ROOT(" - ParMetis configuration: ");
+      WALBERLA_LOG_INFO_ON_ROOT("   - algorithm = " << dynamicParMetis.algorithmToString() );
+      WALBERLA_LOG_INFO_ON_ROOT("   - weights to use = " << dynamicParMetis.weightsToUseToString() );
+      WALBERLA_LOG_INFO_ON_ROOT("   - edge source = " << dynamicParMetis.edgeSourceToString() );
+      WALBERLA_LOG_INFO_ON_ROOT("   - ipc2redist parameter = " << dynamicParMetis.getipc2redist() );
+      WALBERLA_LOG_INFO_ON_ROOT("   - imbalance tolerance = " << dynamicParMetis.getImbalanceTolerance() );
+
+      blockforest.setRefreshPhantomBlockDataPackFunction(blockforest::DynamicParMetisBlockInfoPackUnpack());
+      blockforest.setRefreshPhantomBlockDataUnpackFunction(blockforest::DynamicParMetisBlockInfoPackUnpack());
+      blockforest.setRefreshPhantomBlockMigrationPreparationFunction( dynamicParMetis );
+
+      if( loadEvaluationStrategy == "Fit" )
+      {
+         lbm_mesapd_coupling::amr::WeightEvaluationFunctor weightEvaluationFunctor(couplingInfoCollection, lbm_mesapd_coupling::amr::fittedTotalWeightEvaluationFunction);
+         lbm_mesapd_coupling::amr::MetisAssignmentFunctor weightAssignmentFunctor(weightEvaluationFunctor); //attention: special METIS assignment functor!
+         weightAssignmentFunctor.setBlockBaseWeight(blockBaseWeight);
+         real_t weightMultiplicator = real_t(1000); // values from predictor are in range [0-5] which is too coarse when cast to int as done in parmetis
+         weightAssignmentFunctor.setWeightMultiplicator(weightMultiplicator);
+         blockforest.setRefreshPhantomBlockDataAssignmentFunction(weightAssignmentFunctor);
+      }
+      else if( loadEvaluationStrategy == "LBM" )
+      {
+         lbm_mesapd_coupling::amr::MetisAssignmentFunctor weightAssignmentFunctor(lbm_mesapd_coupling::amr::defaultWeightEvaluationFunction);
+         weightAssignmentFunctor.setBlockBaseWeight(blockBaseWeight);
+         blockforest.setRefreshPhantomBlockDataAssignmentFunction(weightAssignmentFunctor);
+      }
+      else
+      {
+         WALBERLA_ABORT("Invalid load evaluation strategy: " << loadEvaluationStrategy);
+      }
+
+   }
+   else if( loadDistributionStrategy == "Diffusive")
+   {
+      using DB_T = blockforest::DynamicDiffusionBalance< blockforest::PODPhantomWeight<real_t> >;
+      DB_T dynamicDiffusion(diffusionMaxIterations, diffusionFlowIterations );
+      dynamicDiffusion.setMode(DB_T::Mode::DIFFUSION_PUSH);
+
+      WALBERLA_LOG_INFO_ON_ROOT(" - Dynamic diffusion configuration: ");
+      WALBERLA_LOG_INFO_ON_ROOT("   - max iterations = " << dynamicDiffusion.getMaxIterations() );
+      WALBERLA_LOG_INFO_ON_ROOT("   - flow iterations = " << dynamicDiffusion.getFlowIterations());
+
+      blockforest.setRefreshPhantomBlockDataPackFunction(blockforest::PODPhantomWeightPackUnpack<real_t>());
+      blockforest.setRefreshPhantomBlockDataUnpackFunction(blockforest::PODPhantomWeightPackUnpack<real_t>());
+      blockforest.setRefreshPhantomBlockMigrationPreparationFunction( dynamicDiffusion );
+
+      if( loadEvaluationStrategy == "Fit" )
+      {
+         lbm_mesapd_coupling::amr::WeightEvaluationFunctor weightEvaluationFunctor(couplingInfoCollection, lbm_mesapd_coupling::amr::fittedTotalWeightEvaluationFunction);
+         lbm_mesapd_coupling::amr::WeightAssignmentFunctor weightAssignmentFunctor(weightEvaluationFunctor);
+         weightAssignmentFunctor.setBlockBaseWeight(blockBaseWeight);
+         blockforest.setRefreshPhantomBlockDataAssignmentFunction(weightAssignmentFunctor);
+      }
+      else if( loadEvaluationStrategy == "LBM" )
+      {
+         lbm_mesapd_coupling::amr::WeightAssignmentFunctor weightAssignmentFunctor(lbm_mesapd_coupling::amr::defaultWeightEvaluationFunction);
+         weightAssignmentFunctor.setBlockBaseWeight(blockBaseWeight);
+         blockforest.setRefreshPhantomBlockDataAssignmentFunction(weightAssignmentFunctor);
+      }
+      else
+      {
+         WALBERLA_ABORT("Invalid load evaluation strategy: " << loadEvaluationStrategy);
+      }
+
+   } else
+   {
+      WALBERLA_ABORT("Load distribution strategy \"" << loadDistributionStrategy << "\t not implemented! - Aborting" );
+   }
+
+   lbm_mesapd_coupling::amr::BlockInfo emptyExampleBlock(blockSize*blockSize*blockSize, 0, 0, 0, 0, numRPDSubCycles);
+   WALBERLA_LOG_INFO_ON_ROOT("An example empty block has the weight: " << lbm_mesapd_coupling::amr::fittedTotalWeightEvaluationFunction(emptyExampleBlock));
+
+
+   ///////////////
+   // TIME LOOP //
+   ///////////////
+
+   // create the timeloop
+   SweepTimeloop timeloop( blocks->getBlockStorage(), timesteps );
+
+   timeloop.addFuncBeforeTimeStep( RemainingTimeLogger( timeloop.getNrOfTimeSteps() ), "Remaining Time Logger" );
+
+   if( vtkWriteFreqPa != uint_t(0) ) {
+      auto particleVtkWriter = vtk::createVTKOutput_PointData(particleVtkOutput, "Particles", vtkWriteFreqPa, baseFolder, "simulation_step");
+      timeloop.addFuncBeforeTimeStep( vtk::writeFiles( particleVtkWriter ), "VTK (sphere data)" );
+   }
+
+   if( vtkWriteFreqFl != uint_t(0) ) {
+
+      // pdf field
+      auto pdfFieldVTK = vtk::createVTKOutput_BlockData(blocks, "fluid_field", vtkWriteFreqFl, 0, false, baseFolder);
+
+      field::FlagFieldCellFilter<FlagField_T> fluidFilter(flagFieldID);
+      fluidFilter.addFlag(Fluid_Flag);
+      pdfFieldVTK->addCellInclusionFilter(fluidFilter);
+
+      pdfFieldVTK->addCellDataWriter(
+            make_shared<lbm::VelocityVTKWriter<LatticeModel_T, float> >(pdfFieldID, "VelocityFromPDF"));
+      pdfFieldVTK->addCellDataWriter(
+            make_shared<lbm::DensityVTKWriter<LatticeModel_T, float> >(pdfFieldID, "DensityFromPDF"));
+
+      timeloop.addFuncBeforeTimeStep(vtk::writeFiles(pdfFieldVTK), "VTK (fluid field data)");
+   }
+
+   if( vtkWriteFreqDD != uint_t(0) ) {
+      auto domainDecompVTK = vtk::createVTKOutput_DomainDecomposition(blocks, "domain_decomposition", vtkWriteFreqDD, baseFolder );
+      timeloop.addFuncBeforeTimeStep( vtk::writeFiles(domainDecompVTK), "VTK (domain decomposition)");
+   }
+
+
+   // setup of the LBM communication for synchronizing the pdf field between neighboring blocks
+   blockforest::communication::UniformBufferedScheme< Stencil_T > optimizedPDFCommunicationScheme( blocks );
+   optimizedPDFCommunicationScheme.addPackInfo( make_shared< lbm::PdfFieldPackInfo< LatticeModel_T > >( pdfFieldID ) ); // optimized sync
+
+   auto sweep = lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldID, flagFieldID, Fluid_Flag );
+
+   // add LBM communication function and boundary handling sweep (does the hydro force calculations and the no-slip treatment)
+   timeloop.add() << BeforeFunction( optimizedPDFCommunicationScheme, "LBM Communication" )
+                  << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" );
+
+   // streaming & collide
+   timeloop.add() << Sweep( makeSharedSweep(sweep), "Stream&Collide" );
+
+
+   SweepTimeloop timeloopAfterParticles( blocks->getBlockStorage(), timesteps );
+
+   // sweep for updating the particle mapping into the LBM simulation
+   bool conserveMomentum = false;
+   timeloopAfterParticles.add() << Sweep( lbm_mesapd_coupling::makeMovingParticleMapping<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, MO_Flag, FormerMO_Flag,
+                                                                                                                        lbm_mesapd_coupling::RegularParticlesSelector(), conserveMomentum), "Particle Mapping" );
+
+   bool recomputeTargetDensity = false;
+   auto gradReconstructor = lbm_mesapd_coupling::makeGradsMomentApproximationReconstructor<BoundaryHandling_T>(blocks, boundaryHandlingID, omega, recomputeTargetDensity,true);
+   blockforest::communication::UniformBufferedScheme< Stencil_T > fullPDFCommunicationScheme( blocks );
+   fullPDFCommunicationScheme.addPackInfo( make_shared< field::communication::PackInfo< PdfField_T > >( pdfFieldID ) ); // full sync
+   timeloopAfterParticles.add() << BeforeFunction( fullPDFCommunicationScheme, "PDF Communication" )
+                                << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMO_Flag, Fluid_Flag,
+                                                                                                                                           gradReconstructor, conserveMomentum) ), "PDF Restore" );
+
+   ////////////////////////
+   // EXECUTE SIMULATION //
+   ////////////////////////
+
+
+   uint_t loadEvaluationFrequency = loadBalancingCheckFrequency;
+
+   // file for simulation infos
+   std::string infoFileName( baseFolder + "/simulation_info.txt");
+   WALBERLA_ROOT_SECTION()
+   {
+      std::ofstream file;
+      file.open( infoFileName.c_str(), std::fstream::out | std::fstream::trunc );
+      file << "#i\t t\t tSim\t tLoadBal\t numProcs\t blocks (min/max/sum)\n";
+      file.close();
+   }
+
+   // process local timing measurements and predicted loads
+   std::string processLocalFiles(baseFolder + "/processLocalFiles");
+   WALBERLA_ROOT_SECTION()
+   {
+      filesystem::path tpath( processLocalFiles );
+      if( !filesystem::exists( tpath ) )
+         filesystem::create_directory( tpath );
+   }
+   std::string measurementFileProcessName(processLocalFiles + "/measurements_" + std::to_string(MPIManager::instance()->rank()) + ".txt");
+   {
+      std::ofstream file;
+      file.open( measurementFileProcessName.c_str(), std::fstream::out | std::fstream::trunc );
+      file << "#i\t t\t mTotSim\t mLB\t mLBM\t mBH\t mCoup1\t mCoup2\t mRPD\t cLBM\t cRB\t numBlocks\n";
+      file.close();
+   }
+
+   std::string predictionFileProcessName(processLocalFiles + "/predictions_" + std::to_string(MPIManager::instance()->rank()) + ".txt");
+   {
+      std::ofstream file;
+      file.open( predictionFileProcessName.c_str(), std::fstream::out | std::fstream::trunc );
+      file << "#i\t t\t wlLBM\t wlBH\t wlCoup1\t wlCoup2\t wlRPD\t edgecut\t numBlocks\n";
+      file.close();
+   }
+
+   std::vector< std::vector<std::string> > timerKeys;
+   std::vector<std::string> LBMTimer;
+   LBMTimer.emplace_back("Stream&Collide");
+   timerKeys.push_back(LBMTimer);
+
+   std::vector<std::string> bhTimer;
+   bhTimer.emplace_back("Boundary Handling");
+   timerKeys.push_back(bhTimer);
+
+   std::vector<std::string> couplingTimer1;
+   couplingTimer1.emplace_back("Particle Mapping");
+   std::vector<std::string> couplingTimer2;
+   couplingTimer2.emplace_back("PDF Restore");
+   timerKeys.push_back(couplingTimer1);
+   timerKeys.push_back(couplingTimer2);
+
+   std::vector<std::string> rpdTimer;
+   rpdTimer.emplace_back("RPD Force");
+   rpdTimer.emplace_back("RPD VV1");
+   rpdTimer.emplace_back("RPD VV2");
+   rpdTimer.emplace_back("RPD Lub");
+   rpdTimer.emplace_back("RPD Collision");
+   timerKeys.push_back(rpdTimer);
+
+   std::vector<std::string> LBMCommTimer;
+   LBMCommTimer.emplace_back("LBM Communication");
+   LBMCommTimer.emplace_back("PDF Communication");
+   timerKeys.push_back(LBMCommTimer);
+
+
+   std::vector<std::string> rpdCommTimer;
+   rpdCommTimer.emplace_back("Reduce Force Torque");
+   rpdCommTimer.emplace_back("Reduce Hyd Force Torque");
+   rpdCommTimer.emplace_back("Reduce Contact History");
+   rpdCommTimer.emplace_back("Sync");
+   timerKeys.push_back(rpdCommTimer);
+
+   std::vector<real_t> timings(timerKeys.size());
+
+   double oldmTotSim = 0.0;
+   double oldmLB = 0.0;
+
+   auto measurementFileCounter = uint_t(0);
+   auto predictionFileCounter = uint_t(0);
+
+   std::string loadEvaluationStep("load evaluation");
+   std::string loadBalancingStep("load balancing");
+   std::string simulationStep("simulation");
+
+   WcTimingPool timeloopTiming;
+   WcTimingPool simulationTiming;
+
+   lbm_mesapd_coupling::InspectionProbe<PdfField_T,BoundaryHandling_T,ParticleAccessor_T> probeForNaNs( Vector3<real_t>(0, 0, 0),
+                                                                                                  blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor,
+                                                                                                  true, true, "" );
+
+   // time loop
+   for (uint_t i = 0; i < timesteps; ++i )
+   {
+
+      // evaluate measurements (note: reflect simulation behavior BEFORE the evaluation)
+      if( loadEvaluationFrequency > 0 && i % loadEvaluationFrequency == 0 && i > 0 && fileIO)
+      {
+
+         simulationTiming[loadEvaluationStep].start();
+
+         // write process local timing measurements to files (per process, per load balancing step)
+         {
+            auto mTotSim = simulationTiming[simulationStep].total();
+            auto mLB = simulationTiming[loadBalancingStep].total();
+
+            evaluateTimers(timeloopTiming, timerKeys, timings);
+
+            auto & forest = blocks->getBlockForest();
+            uint_t numBlocks = forest.getNumberOfBlocks();
+
+            // write to process local file
+            std::ofstream file;
+            file.open( measurementFileProcessName.c_str(), std::ofstream::app  );
+            file << measurementFileCounter << "\t " << real_c(i) / tRef << "\t"
+                 << mTotSim - oldmTotSim << "\t" << mLB - oldmLB << "\t";
+            for (real_t timing : timings) {
+               file << "\t" << timing;
+            }
+            file << "\t" << numBlocks << "\n";
+            file.close();
+
+            oldmTotSim = mTotSim;
+            oldmLB = mLB;
+            measurementFileCounter++;
+
+            // reset timer to have measurement from evaluation to evaluation point
+            timeloopTiming.clear();
+
+         }
+
+         // evaluate general simulation infos (on root)
+         {
+            double totalTimeToCurrentTimestep(0.0);
+            double totalLBTimeToCurrentTimestep(0.0);
+            evaluateTotalSimulationTimePassed(simulationTiming, simulationStep, loadBalancingStep,
+                                              totalTimeToCurrentTimestep, totalLBTimeToCurrentTimestep);
+            math::DistributedSample numberOfBlocks;
+
+            auto & forest = blocks->getBlockForest();
+            uint_t numBlocks = forest.getNumberOfBlocks();
+            numberOfBlocks.castToRealAndInsert(numBlocks);
+            numberOfBlocks.mpiGatherRoot();
+
+            WALBERLA_ROOT_SECTION()
+            {
+               std::ofstream file;
+               file.open( infoFileName.c_str(), std::ofstream::app  );
+               file << i << "\t " << real_c(i) / tRef << "\t"
+                    << totalTimeToCurrentTimestep << "\t " << totalLBTimeToCurrentTimestep << "\t "
+                    << numberOfProcesses << "\t ";
+               file << uint_c(numberOfBlocks.min()) << "\t ";
+               file << uint_c(numberOfBlocks.max()) << "\t ";
+               file << uint_c(numberOfBlocks.sum()) << "\n ";
+               file.close();
+            }
+         }
+
+         simulationTiming[loadEvaluationStep].end();
+
+      }
+
+      if( loadBalancingCheckFrequency != 0 && i % loadBalancingCheckFrequency == 0)
+      {
+
+         if(useProgressLogging) walberla::logging::Logging::instance()->setFileLogLevel(logging::Logging::LogLevel::PROGRESS);
+
+         simulationTiming[loadBalancingStep].start();
+
+         if( loadEvaluationStrategy != "LBM" ) {
+
+            WALBERLA_LOG_INFO_ON_ROOT("Checking for load balancing...");
+
+            // update info collections for the particle presence based check and the load balancing:
+            auto &forest = blocks->getBlockForest();
+            lbm_mesapd_coupling::amr::updateAndSyncInfoCollection<BoundaryHandling_T,ParticleAccessor_T >(forest, boundaryHandlingID, *accessor, numRPDSubCycles, *couplingInfoCollection);
+
+            if(useProgressLogging)  WALBERLA_LOG_INFO("Created info collection with size " << couplingInfoCollection->size());
+
+            auto numBlocksBefore = forest.getNumberOfBlocks();
+            if(useProgressLogging)  WALBERLA_LOG_INFO("Number of blocks before refresh " << numBlocksBefore);
+
+            mesa_pd::mpi::ClearNextNeighborSync CNNS;
+            CNNS(*accessor);
+
+            if(useProgressLogging) WALBERLA_LOG_INFO_ON_ROOT("Cleared particle sync and starting refresh");
+
+            blocks->refresh();
+
+            auto numBlocksAfter = forest.getNumberOfBlocks();
+            if(useProgressLogging)  WALBERLA_LOG_INFO("Number of blocks after refresh " << numBlocksAfter);
+
+            if(useProgressLogging) WALBERLA_LOG_INFO_ON_ROOT("Refresh finished, recreating all datastructures");
+
+            //WALBERLA_LOG_INFO(rank << ", " << numBlocksBefore << " -> " << numBlocksAfter);
+            rpdDomain->refresh();
+            if(useBlockForestSync)
+            {
+               ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, associateToBlock, *accessor);
+               syncNextNeighborBlockForestFunc(*ps, blocks->getBlockForestPointer(), rpdDomain, overlap);
+
+            } else
+            {
+               syncNextNeighborFunc(*ps, *rpdDomain, overlap);
+            }
+
+            clearBoundaryHandling(forest, boundaryHandlingID);
+            clearParticleField(forest, particleFieldID, *accessor);
+
+            recreateBoundaryHandling(forest, boundaryHandlingID);
+
+            //NOTE: order in mapping is important: first all global/fixed particles,
+            // only then moving ones, which do not overwrite the mapping of the global/fixed ones
+            particleMappingCall();
+
+         }
+         simulationTiming[loadBalancingStep].end();
+
+         if(useProgressLogging) walberla::logging::Logging::instance()->setFileLogLevel(logging::Logging::LogLevel::INFO);
+
+      }
+
+      if(checkSimulation)
+      {
+         WALBERLA_LOG_INFO_ON_ROOT("Checking time step " << i );
+         evaluateParticleSimulation(ps, ss, numberOfSediments, numGlobalParticles);
+         checkMapping(blocks, pdfFieldID, boundaryHandlingID, probeForNaNs);
+      }
+
+      // evaluate predictions (note: reflect the predictions for all upcoming simulations, thus the corresponding measurements have to be taken afterwards)
+      if( loadEvaluationFrequency > 0 && i % loadEvaluationFrequency == 0 && fileIO)
+      {
+
+         simulationTiming[loadEvaluationStep].start();
+
+         // write process local load predictions to files (per process, per load balancing step)
+         {
+
+            auto wlLBM = real_t(0);
+            auto wlBH = real_t(0);
+            auto wlCoup1 = real_t(0);
+            auto wlCoup2 = real_t(0);
+            auto wlRPD = real_t(0);
+
+            auto & forest = blocks->getBlockForest();
+            lbm_mesapd_coupling::amr::updateAndSyncInfoCollection<BoundaryHandling_T,ParticleAccessor_T >(forest, boundaryHandlingID, *accessor, numRPDSubCycles, *couplingInfoCollection);
+
+            for( auto blockIt = forest.begin(); blockIt != forest.end(); ++blockIt ) {
+               auto * block = static_cast<blockforest::Block *> (&(*blockIt));
+               const auto &blockID = block->getId();
+               auto infoIt = couplingInfoCollection->find(blockID);
+               auto blockInfo = infoIt->second;
+
+               wlLBM   += lbm_mesapd_coupling::amr::fittedLBMWeightEvaluationFunction(blockInfo);
+               wlBH    += lbm_mesapd_coupling::amr::fittedBHWeightEvaluationFunction(blockInfo);
+               wlCoup1 += lbm_mesapd_coupling::amr::fittedCoup1WeightEvaluationFunction(blockInfo);
+               wlCoup2 += lbm_mesapd_coupling::amr::fittedCoup2WeightEvaluationFunction(blockInfo);
+               wlRPD   += lbm_mesapd_coupling::amr::fittedRPDWeightEvaluationFunction(blockInfo);
+
+            }
+
+            // note: we count the edge weight doubled here in total (to and from the other process). ParMetis only counts one direction.
+            uint_t edgecut = evaluateEdgeCut(forest);
+            uint_t numBlocks = forest.getNumberOfBlocks();
+
+            std::ofstream file;
+            file.open( predictionFileProcessName.c_str(), std::ofstream::app  );
+            file << predictionFileCounter << "\t " << real_c(i) / tRef << "\t"
+                 << wlLBM << "\t" << wlBH << "\t" << wlCoup1 << "\t" << wlCoup2 << "\t" << wlRPD << "\t"
+                 << edgecut << "\t" << numBlocks << "\n";
+            file.close();
+
+            predictionFileCounter++;;
+         }
+
+         simulationTiming[loadEvaluationStep].end();
+
+      }
+
+      simulationTiming[simulationStep].start();
+
+      // perform a single simulation step
+
+      timeloop.singleStep( timeloopTiming );
+
+      timeloopTiming["Reduce Hyd Force Torque"].start();
+      reduceProperty.operator()<mesa_pd::HydrodynamicForceTorqueNotification>(*ps);
+      timeloopTiming["Reduce Hyd Force Torque"].end();
+
+      timeloopTiming["RPD Force"].start();
+      if( i == 0 )
+      {
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, initializeHydrodynamicForceTorqueForAveragingKernel, *accessor );
+      }
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, averageHydrodynamicForceTorque, *accessor );
+      timeloopTiming["RPD Force"].end();
+
+      if(checkSimulation)
+      {
+         checkParticleProperties(ps);
+      }
+
+      for(auto subCycle = uint_t(0); subCycle < numRPDSubCycles; ++subCycle )
+      {
+
+         timeloopTiming["RPD VV1"].start();
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPreForce, *accessor);
+         timeloopTiming["RPD VV1"].end();
+
+         timeloopTiming["Sync"].start();
+         if(useBlockForestSync)
+         {
+            syncNextNeighborBlockForestFunc(*ps, blocks->getBlockForestPointer(), rpdDomain, overlap);
+            ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, associateToBlock, *accessor);
+         } else
+         {
+            syncNextNeighborFunc(*ps, *rpdDomain, overlap);
+         }
+         timeloopTiming["Sync"].end();
+
+         // lubrication correction
+         timeloopTiming["RPD Lub"].start();
+         ps->forEachParticlePairHalf(useOpenMP, mesa_pd::kernel::ExcludeInfiniteInfinite(), *accessor,
+                                     [&lubricationCorrectionKernel,maximumLubricationCutOffDistance, &rpdDomain]
+                                           (const size_t idx1, const size_t idx2, auto& ac)
+                                     {
+                                        mesa_pd::collision_detection::AnalyticContactDetection acd;
+                                        acd.getContactThreshold() = maximumLubricationCutOffDistance;
+                                        mesa_pd::kernel::DoubleCast double_cast;
+                                        mesa_pd::mpi::ContactFilter contact_filter;
+                                        if (double_cast(idx1, idx2, ac, acd, ac ))
+                                        {
+                                           if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), *rpdDomain))
+                                           {
+                                              double_cast(acd.getIdx1(), acd.getIdx2(), ac, lubricationCorrectionKernel, ac, acd.getContactNormal(), acd.getPenetrationDepth());
+                                           }
+                                        }
+                                     },
+                                     *accessor );
+         timeloopTiming["RPD Lub"].end();
+
+         // collision response
+         timeloopTiming["RPD Collision"].start();
+         ps->forEachParticlePairHalf(useOpenMP, mesa_pd::kernel::ExcludeInfiniteInfinite(), *accessor,
+                                     [&collisionResponse, &rpdDomain, timeStepSizeRPD]
+                                           (const size_t idx1, const size_t idx2, auto& ac)
+                                     {
+                                        mesa_pd::collision_detection::AnalyticContactDetection acd;
+                                        mesa_pd::kernel::DoubleCast double_cast;
+                                        mesa_pd::mpi::ContactFilter contact_filter;
+                                        if (double_cast(idx1, idx2, ac, acd, ac ))
+                                        {
+                                           if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), *rpdDomain))
+                                           {
+                                              collisionResponse(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), acd.getContactNormal(), acd.getPenetrationDepth(), timeStepSizeRPD);
+                                           }
+                                        }
+                                     },
+                                     *accessor );
+
+         timeloopTiming["RPD Collision"].end();
+
+         timeloopTiming["Reduce Contact History"].start();
+         reduceAndSwapContactHistory(*ps);
+         timeloopTiming["Reduce Contact History"].end();
+
+         timeloopTiming["RPD Force"].start();
+         lbm_mesapd_coupling::AddHydrodynamicInteractionKernel addHydrodynamicInteraction;
+         ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addHydrodynamicInteraction, *accessor );
+         ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addGravitationalForce, *accessor );
+         timeloopTiming["RPD Force"].end();
+
+         timeloopTiming["Reduce Force Torque"].start();
+         reduceProperty.operator()<mesa_pd::ForceTorqueNotification>(*ps);
+         timeloopTiming["Reduce Force Torque"].end();
+
+         // integration
+         timeloopTiming["RPD VV2"].start();
+         ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPostForce, *accessor);
+         timeloopTiming["RPD VV2"].end();
+
+         timeloopTiming["Sync"].start();
+         if(useBlockForestSync)
+         {
+            syncNextNeighborBlockForestFunc(*ps, blocks->getBlockForestPointer(), rpdDomain, overlap);
+         } else
+         {
+            syncNextNeighborFunc(*ps, *rpdDomain, overlap);
+         }
+         timeloopTiming["Sync"].end();
+
+
+      }
+
+      timeloopTiming["RPD Force"].start();
+      ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectAll(), *accessor, resetHydrodynamicForceTorque, *accessor );
+      timeloopTiming["RPD Force"].end();
+
+      // update particle mapping
+      timeloopAfterParticles.singleStep(timeloopTiming);
+
+      simulationTiming[simulationStep].end();
+
+   }
+
+   simulationTiming.logResultOnRoot();
+
+
+   return EXIT_SUCCESS;
+}
+
+} // namespace fluid_particle_workload_distribution
+
+int main( int argc, char **argv ){
+   fluid_particle_workload_distribution::main(argc, argv);
+}
diff --git a/apps/benchmarks/FluidParticleCouplingWithLoadBalancing/FluidParticleWorkloadEvaluation.cpp b/apps/benchmarks/FluidParticleCouplingWithLoadBalancing/FluidParticleWorkloadEvaluation.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..062e3f9c95f25baa5ddadd3b6511d9695cb5e457
--- /dev/null
+++ b/apps/benchmarks/FluidParticleCouplingWithLoadBalancing/FluidParticleWorkloadEvaluation.cpp
@@ -0,0 +1,956 @@
+//======================================================================================================================
+//
+//  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 FluidParticleWorkLoadEvaluation.cpp
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#include "blockforest/Initialization.h"
+#include "blockforest/communication/UniformBufferedScheme.h"
+
+#include "boundary/all.h"
+
+#include "core/DataTypes.h"
+#include "core/Environment.h"
+#include "core/debug/Debug.h"
+#include "core/debug/TestSubsystem.h"
+#include "core/logging/Logging.h"
+#include "core/math/all.h"
+#include "core/timing/RemainingTimeLogger.h"
+#include "core/mpi/MPIManager.h"
+#include "core/mpi/Reduce.h"
+#include "core/mpi/Broadcast.h"
+
+#include "domain_decomposition/SharedSweep.h"
+
+#include "field/AddToStorage.h"
+#include "field/communication/PackInfo.h"
+
+#include "lbm/communication/PdfFieldPackInfo.h"
+#include "lbm/field/AddToStorage.h"
+#include "lbm/field/PdfField.h"
+#include "lbm/lattice_model/D3Q19.h"
+#include "lbm/lattice_model/ForceModel.h"
+#include "lbm/sweeps/CellwiseSweep.h"
+#include "lbm/sweeps/SweepWrappers.h"
+#include "lbm/BlockForestEvaluation.h"
+
+#include "lbm_mesapd_coupling/mapping/ParticleMapping.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/MovingParticleMapping.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/boundary/CurvedLinear.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/reconstruction/Reconstructor.h"
+#include "lbm_mesapd_coupling/momentum_exchange_method/reconstruction/PdfReconstructionManager.h"
+#include "lbm_mesapd_coupling/utility/AddForceOnParticlesKernel.h"
+#include "lbm_mesapd_coupling/utility/ParticleSelector.h"
+#include "lbm_mesapd_coupling/DataTypes.h"
+#include "lbm_mesapd_coupling/utility/AverageHydrodynamicForceTorqueKernel.h"
+#include "lbm_mesapd_coupling/utility/AddHydrodynamicInteractionKernel.h"
+#include "lbm_mesapd_coupling/utility/ResetHydrodynamicForceTorqueKernel.h"
+#include "lbm_mesapd_coupling/utility/LubricationCorrectionKernel.h"
+#include "lbm_mesapd_coupling/utility/InitializeHydrodynamicForceTorqueForAveragingKernel.h"
+
+#include "mesa_pd/collision_detection/AnalyticContactDetection.h"
+#include "mesa_pd/data/ParticleAccessorWithShape.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/ShapeStorage.h"
+#include "mesa_pd/data/DataTypes.h"
+#include "mesa_pd/data/shape/HalfSpace.h"
+#include "mesa_pd/data/shape/Sphere.h"
+#include "mesa_pd/domain/BlockForestDomain.h"
+#include "mesa_pd/domain/BlockForestDataHandling.h"
+#include "mesa_pd/kernel/DoubleCast.h"
+#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/LinearSpringDashpot.h"
+#include "mesa_pd/kernel/ParticleSelector.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
+#include "mesa_pd/mpi/SyncNextNeighbors.h"
+#include "mesa_pd/mpi/ReduceProperty.h"
+#include "mesa_pd/mpi/ReduceContactHistory.h"
+#include "mesa_pd/mpi/ContactFilter.h"
+#include "mesa_pd/mpi/notifications/ForceTorqueNotification.h"
+#include "mesa_pd/mpi/notifications/HydrodynamicForceTorqueNotification.h"
+#include "mesa_pd/vtk/ParticleVtkOutput.h"
+
+#include "timeloop/SweepTimeloop.h"
+
+#include "vtk/all.h"
+#include "field/vtk/all.h"
+#include "lbm/vtk/all.h"
+
+#include <vector>
+#include <iostream>
+
+namespace fluid_particle_workload_evaluation
+{
+
+///////////
+// USING //
+///////////
+
+using namespace walberla;
+using walberla::uint_t;
+
+using LatticeModel_T = lbm::D3Q19< lbm::collision_model::TRT, false >;
+using Stencil_T = LatticeModel_T::Stencil;
+using PdfField_T = lbm::PdfField<LatticeModel_T>;
+using flag_t = walberla::uint8_t;
+using FlagField_T = FlagField<flag_t>;
+
+const uint_t FieldGhostLayers = 1;
+
+///////////
+// FLAGS //
+///////////
+
+const FlagUID Fluid_Flag   ( "fluid" );
+const FlagUID MO_Flag  ( "moving obstacle" );
+const FlagUID FormerMO_Flag( "former moving obstacle" );
+
+
+/////////////////////////////////////
+// BOUNDARY HANDLING CUSTOMIZATION //
+/////////////////////////////////////
+template <typename ParticleAccessor_T>
+class MyBoundaryHandling
+{
+public:
+
+   using MO_T = lbm_mesapd_coupling::CurvedLinear< LatticeModel_T, FlagField_T, ParticleAccessor_T >;
+   using Type = BoundaryHandling< FlagField_T, Stencil_T, MO_T >;
+
+   MyBoundaryHandling( const BlockDataID & flagFieldID, const BlockDataID & pdfFieldID,
+                       const BlockDataID & particleFieldID, const shared_ptr<ParticleAccessor_T>& ac,
+                       bool useEntireFieldTraversal) :
+         flagFieldID_( flagFieldID ), pdfFieldID_( pdfFieldID ), particleFieldID_( particleFieldID ), ac_( ac ), useEntireFieldTraversal_(useEntireFieldTraversal) {}
+
+   Type * operator()( IBlock* const block, const StructuredBlockStorage* const storage ) const
+   {
+      WALBERLA_ASSERT_NOT_NULLPTR( block );
+      WALBERLA_ASSERT_NOT_NULLPTR( storage );
+
+      auto * flagField     = block->getData< FlagField_T >( flagFieldID_ );
+      auto *  pdfField     = block->getData< PdfField_T > ( pdfFieldID_ );
+      auto * particleField = block->getData< lbm_mesapd_coupling::ParticleField_T > ( particleFieldID_ );
+
+      const auto fluid = flagField->flagExists( Fluid_Flag ) ? flagField->getFlag( Fluid_Flag ) : flagField->registerFlag( Fluid_Flag );
+
+      typename Type::Mode mode = (useEntireFieldTraversal_) ? Type::Mode::ENTIRE_FIELD_TRAVERSAL : Type::Mode::OPTIMIZED_SPARSE_TRAVERSAL ;
+
+      Type * handling = new Type( "moving obstacle boundary handling", flagField, fluid,
+                                  MO_T( "MO", MO_Flag, pdfField, flagField, particleField, ac_, fluid, *storage, *block ),
+                                  mode);
+
+      handling->fillWithDomain( FieldGhostLayers );
+
+      return handling;
+   }
+
+private:
+
+   const BlockDataID flagFieldID_;
+   const BlockDataID pdfFieldID_;
+   const BlockDataID particleFieldID_;
+
+   shared_ptr<ParticleAccessor_T> ac_;
+
+   bool useEntireFieldTraversal_;
+};
+
+template< typename BoundaryHandling_T >
+void evaluateFluidQuantities(const shared_ptr< StructuredBlockStorage > & blocks, const BlockDataID boundaryHandlingID,
+                             uint_t & numCells, uint_t & numFluidCells, uint_t & numNBCells )
+{
+
+   for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt)
+   {
+      auto * boundaryHandling = blockIt->getData< BoundaryHandling_T >( boundaryHandlingID );
+      auto xyzSize = boundaryHandling->getFlagField()->xyzSize();
+      numCells += xyzSize.numCells();
+
+      for(auto z = cell_idx_t(xyzSize.zMin()); z <= cell_idx_t(xyzSize.zMax()); ++z ){
+         for(auto y = cell_idx_t(xyzSize.yMin()); y <= cell_idx_t(xyzSize.yMax()); ++y ){
+            for(auto x = cell_idx_t(xyzSize.xMin()); x <= cell_idx_t(xyzSize.xMax()); ++x ) {
+               if (boundaryHandling->isDomain(x, y, z)) {
+                  ++numFluidCells;
+               }
+               if (boundaryHandling->isNearBoundary(x, y, z)) {
+                  ++numNBCells;
+               }
+            }
+         }
+      }
+   }
+}
+
+void evaluateRPDQuantities( const shared_ptr< mesa_pd::data::ParticleStorage > & ps,
+                            uint_t & numLocalParticles, uint_t & numGhostParticles)
+{
+
+   for (auto pIt = ps->begin(); pIt != ps->end(); ++pIt)
+   {
+      using namespace walberla::mesa_pd::data::particle_flags;
+      if (isSet(pIt->getFlags(), GHOST))
+      {
+         ++numGhostParticles;
+      } else
+      {
+         //note: global particles are included here
+         // use if (!isSet(pIt->getFlags(), GLOBAL)) if should be excluded
+         ++numLocalParticles;
+      }
+   }
+}
+
+void evaluateTimers(WcTimingPool & timingPool,
+                    const std::vector<std::vector<std::string> > & timerKeys,
+                    std::vector<double> & timings )
+{
+
+   for (auto & timingsIt : timings)
+   {
+      timingsIt = 0.0;
+   }
+
+   timingPool.unifyRegisteredTimersAcrossProcesses();
+
+   double scalingFactor = 1000.0; // milliseconds
+
+   for (auto i = uint_t(0); i < timerKeys.size(); ++i )
+   {
+      auto keys = timerKeys[i];
+      for (const auto &timerName : keys)
+      {
+         if(timingPool.timerExists(timerName))
+         {
+            timings[i] += timingPool[timerName].total() * scalingFactor;
+         }
+      }
+
+   }
+}
+
+
+//*******************************************************************************************************************
+/*! Application to evaluate the workload (time measurements) for a fluid-particle simulation
+ *
+ * This application is used in the paper
+ *  Rettinger, Ruede - "Dynamic Load Balancing Techniques for Particulate Flow Simulations", 2019, Computation
+ * in Section 3 to develop and calibrate the workload estimator.
+ * The setup features settling particle inside a horizontally periodic box.
+ * A comprehensive description is given in Sec. 3.3 of the paper.
+ * It uses 4 x 4 x 5 blocks for domain partitioning.
+ * For each block ( = each process), the block local quantities are evaluated as well as the timing infos of
+ * the fluid-particle interaction algorithm. Those infos are then written to files that can be used later on
+ * for function fitting to obtain a workload estimator.
+ *
+ * NOTE: Since this estimator relies on timing measurements, this evaluation procedure should be carried out everytime
+ * a different implementation, hardware or algorithm is used.
+ *
+ */
+//*******************************************************************************************************************
+int main( int argc, char **argv )
+{
+   debug::enterTestMode();
+
+   mpi::Environment env( argc, argv );
+
+   auto solidVolumeFraction = real_t(0.2);
+
+   // LBM / numerical parameters
+   auto blockSize  = uint_t(32);
+   auto uSettling = real_t(0.1); // characteristic settling velocity
+   auto diameter = real_t(10);
+
+   auto Ga = real_t(30); //Galileo number
+   auto numRPDSubCycles = uint_t(10);
+
+   auto vtkIOFreq = uint_t(0);
+   auto timestepsNonDim = real_t(2.5);
+   auto numSamples = uint_t(2000);
+   std::string baseFolder = "workload_files"; // folder for vtk and file output
+
+   bool noFileOutput = false;
+   bool useEntireFieldTraversal = true;
+   bool useFusedStreamCollide = false;
+
+   auto XBlocks = uint_t(4);
+   auto YBlocks = uint_t(4);
+   auto ZBlocks = uint_t(5);
+
+   real_t topWallOffsetFactor = real_t(1.05);
+
+   bool vtkDuringInit = false;
+
+   for( int i = 1; i < argc; ++i )
+   {
+      if( std::strcmp( argv[i], "--vtkIOFreq"               ) == 0 ) { vtkIOFreq = uint_c( std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--noFileOutput"            ) == 0 ) { noFileOutput = true; continue; }
+      if( std::strcmp( argv[i], "--vtkDuringInit"           ) == 0 ) { vtkDuringInit = true; continue; }
+      if( std::strcmp( argv[i], "--basefolder"              ) == 0 ) { baseFolder = argv[++i]; continue; }
+      if( std::strcmp( argv[i], "--solidVolumeFraction"     ) == 0 ) { solidVolumeFraction = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--diameter"                ) == 0 ) { diameter = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--blockSize"               ) == 0 ) { blockSize = uint_c(std::atof( argv[++i]) ); continue; }
+      if( std::strcmp( argv[i], "--XBlocks"                 ) == 0 ) { XBlocks = uint_c(std::atof( argv[++i]) ); continue; }
+      if( std::strcmp( argv[i], "--YBlocks"                 ) == 0 ) { YBlocks = uint_c(std::atof( argv[++i]) ); continue; }
+      if( std::strcmp( argv[i], "--ZBlocks"                 ) == 0 ) { ZBlocks = uint_c(std::atof( argv[++i]) ); continue; }
+      if( std::strcmp( argv[i], "--uSettling"               ) == 0 ) { uSettling = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--topWallOffsetFactor"     ) == 0 ) { topWallOffsetFactor = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--Ga"                      ) == 0 ) { Ga = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--timestepsNonDim"         ) == 0 ) { timestepsNonDim = real_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--numRPDSubCycles"         ) == 0 ) { numRPDSubCycles = uint_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--useEntireFieldTraversal" ) == 0 ) { useEntireFieldTraversal = true; continue; }
+      if( std::strcmp( argv[i], "--numSamples"              ) == 0 ) { numSamples = uint_c(std::atof( argv[++i] )); continue; }
+      if( std::strcmp( argv[i], "--useFusedStreamCollide"   ) == 0 ) { useFusedStreamCollide = true; continue; }
+      WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
+   }
+
+   WALBERLA_CHECK(diameter > real_t(1));
+   WALBERLA_CHECK(uSettling > real_t(0));
+   WALBERLA_CHECK(Ga > real_t(0));
+   WALBERLA_CHECK(solidVolumeFraction > real_t(0));
+   WALBERLA_CHECK(solidVolumeFraction < real_t(0.65));
+
+   ///////////////////////////
+   // SIMULATION PROPERTIES //
+   ///////////////////////////
+
+
+   if( MPIManager::instance()->numProcesses() != int(XBlocks * YBlocks * ZBlocks) )
+   {
+      WALBERLA_LOG_WARNING_ON_ROOT("WARNING! You have specified less or more processes than number of blocks -> the time measurements are no longer blockwise!")
+   }
+
+   if( diameter > real_c(blockSize) )
+   {
+      WALBERLA_LOG_WARNING_ON_ROOT("The bodies might be too large to work with the currently used synchronization!");
+   }
+
+   WALBERLA_LOG_INFO_ON_ROOT("Using setup with sedimenting particles -> creating two planes and applying gravitational force")
+
+   const uint_t XCells = blockSize * XBlocks;
+   const uint_t YCells = blockSize * YBlocks;
+   const uint_t ZCells = blockSize * ZBlocks;
+
+   const real_t topWallOffset =  topWallOffsetFactor * real_t(blockSize); // move the top wall downwards to take away a certain portion of the overall domain
+
+   // determine number of spheres to generate, if necessary scale diameter a bit to reach desired solid volume fraction
+   real_t domainHeight = real_c(ZCells) - topWallOffset;
+   real_t fluidVolume =  real_c( XCells * YCells ) * domainHeight;
+   real_t solidVolume = solidVolumeFraction * fluidVolume;
+   uint_t numberOfParticles = uint_c(std::ceil(solidVolume / ( math::pi / real_t(6) * diameter * diameter * diameter )));
+   diameter = std::cbrt( solidVolume / ( real_c(numberOfParticles) * math::pi / real_t(6) ) );
+
+   auto densityRatio = real_t(2.5);
+
+   real_t viscosity = uSettling * diameter / Ga;
+   const real_t omega = lbm::collision_model::omegaFromViscosity(viscosity);
+
+   const real_t gravitationalAcceleration = uSettling * uSettling / ( (densityRatio-real_t(1)) * diameter );
+
+   real_t tref = diameter / uSettling;
+   real_t Tref = domainHeight / uSettling;
+
+   uint_t timesteps = uint_c(timestepsNonDim * Tref);
+
+   const real_t dx = real_c(1);
+   WALBERLA_LOG_INFO_ON_ROOT("viscosity = " << viscosity);
+   WALBERLA_LOG_INFO_ON_ROOT("tau = " << real_t(1)/omega);
+   WALBERLA_LOG_INFO_ON_ROOT("diameter = " << diameter);
+   WALBERLA_LOG_INFO_ON_ROOT("solid volume fraction = " << solidVolumeFraction);
+   WALBERLA_LOG_INFO_ON_ROOT("domain size (in cells) = " << XCells << " x " << YCells << " x " << ZCells);
+   WALBERLA_LOG_INFO_ON_ROOT("number of bodies = " << numberOfParticles);
+   WALBERLA_LOG_INFO_ON_ROOT("gravitational acceleration = " << gravitationalAcceleration);
+   WALBERLA_LOG_INFO_ON_ROOT("Ga = " << Ga);
+   WALBERLA_LOG_INFO_ON_ROOT("uSettling = " << uSettling);
+   WALBERLA_LOG_INFO_ON_ROOT("tref = " << tref);
+   WALBERLA_LOG_INFO_ON_ROOT("Tref = " << Tref);
+   WALBERLA_LOG_INFO_ON_ROOT("timesteps = " << timesteps);
+   WALBERLA_LOG_INFO_ON_ROOT("number of workload samples = " << numSamples);
+
+   // create folder to store logging files
+   WALBERLA_ROOT_SECTION()
+   {
+      walberla::filesystem::path path1( baseFolder );
+      if( !walberla::filesystem::exists( path1 ) )
+         walberla::filesystem::create_directory( path1 );
+   }
+
+
+   ///////////////////////////
+   // BLOCK STRUCTURE SETUP //
+   ///////////////////////////
+
+   Vector3<bool> periodicity( true );
+   periodicity[2] = false;
+
+   // create domain
+   shared_ptr< StructuredBlockForest > blocks = blockforest::createUniformBlockGrid( XBlocks, YBlocks, ZBlocks, blockSize, blockSize, blockSize, dx,
+                                                    0, false, false, //one block per process!
+                                                    periodicity[0], periodicity[1], periodicity[2], //periodicity
+                                                    false );
+
+   /////////
+   // RPD //
+   /////////
+
+   const real_t restitutionCoeff = real_t(0.97);
+   const real_t frictionCoeffStatic = real_t(0.8);
+   const real_t frictionCoeffDynamic = real_t(0.15);
+   const real_t collisionTime = real_t(4) * diameter; // from my paper
+   const real_t poissonsRatio = real_t(0.22);
+   const real_t kappa = real_t(2) * ( real_t(1) - poissonsRatio ) / ( real_t(2) - poissonsRatio ) ;
+
+   auto rpdDomain = std::make_shared<mesa_pd::domain::BlockForestDomain>(blocks->getBlockForestPointer());
+
+   //init data structures
+   auto ps = walberla::make_shared<mesa_pd::data::ParticleStorage>(1);
+   auto ss = walberla::make_shared<mesa_pd::data::ShapeStorage>();
+   using ParticleAccessor_T = mesa_pd::data::ParticleAccessorWithShape;
+   auto accessor = walberla::make_shared<ParticleAccessor_T >(ps, ss);
+
+   real_t timeStepSizeRPD = real_t(1)/real_t(numRPDSubCycles);
+   mesa_pd::kernel::VelocityVerletPreForceUpdate  vvIntegratorPreForce(timeStepSizeRPD);
+   mesa_pd::kernel::VelocityVerletPostForceUpdate vvIntegratorPostForce(timeStepSizeRPD);
+
+   // types: 0 = wall, 1: sphere
+   mesa_pd::kernel::LinearSpringDashpot collisionResponse(2);
+   collisionResponse.setFrictionCoefficientDynamic(0,1,frictionCoeffDynamic);
+   collisionResponse.setFrictionCoefficientDynamic(1,1,frictionCoeffDynamic);
+   collisionResponse.setFrictionCoefficientStatic(0,1,frictionCoeffStatic);
+   collisionResponse.setFrictionCoefficientStatic(1,1,frictionCoeffStatic);
+
+   const real_t sphereVolume = math::pi / real_t(6) * diameter * diameter * diameter;
+   const real_t particleMass = densityRatio * sphereVolume;
+   const real_t effMass_sphereWall = particleMass;
+   const real_t effMass_sphereSphere = particleMass * particleMass / ( real_t(2) * particleMass );
+   collisionResponse.setStiffnessAndDamping(0,1,restitutionCoeff,collisionTime,kappa,effMass_sphereWall);
+   collisionResponse.setStiffnessAndDamping(1,1,restitutionCoeff,collisionTime,kappa,effMass_sphereSphere);
+
+   mesa_pd::mpi::ReduceProperty reduceProperty;
+   mesa_pd::mpi::ReduceContactHistory reduceAndSwapContactHistory;
+
+   //////////////
+   // COUPLING //
+   //////////////
+
+   // connect to pe
+   const real_t overlap = real_c( 1.5 ) * dx;
+
+   std::function<void(void)> syncCall = [&ps,&rpdDomain,overlap](){
+      mesa_pd::mpi::SyncNextNeighbors syncNextNeighborFunc;
+      syncNextNeighborFunc(*ps, *rpdDomain, overlap);
+   };
+
+   auto generationDomain = AABB( real_t(0), real_t(0), real_t(0), real_c(XCells), real_c(YCells), real_c(ZCells) - topWallOffset);
+
+   // create plane at top and bottom
+
+   mesa_pd::data::Particle&& p0 = *ps->create(true);
+   p0.setPosition(generationDomain.minCorner());
+   p0.setShapeID(ss->create<mesa_pd::data::HalfSpace>( Vector3<real_t>(0,0,1) ));
+   p0.setOwner(mpi::MPIManager::instance()->rank());
+   p0.setType(0);
+   mesa_pd::data::particle_flags::set(p0.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
+   mesa_pd::data::particle_flags::set(p0.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
+
+   mesa_pd::data::Particle&& p1 = *ps->create(true);
+   p1.setPosition(generationDomain.maxCorner());
+   p1.setShapeID(ss->create<mesa_pd::data::HalfSpace>( Vector3<real_t>(0,0,-1) ));
+   p1.setOwner(mpi::MPIManager::instance()->rank());
+   p1.setType(0);
+   mesa_pd::data::particle_flags::set(p1.getFlagsRef(), mesa_pd::data::particle_flags::INFINITE);
+   mesa_pd::data::particle_flags::set(p1.getFlagsRef(), mesa_pd::data::particle_flags::FIXED);
+
+   auto sphereShape = ss->create<mesa_pd::data::Sphere>( diameter * real_t(0.5) );
+   ss->shapes[sphereShape]->updateMassAndInertia(densityRatio);
+
+   auto xParticle = real_t(0);
+   auto yParticle = real_t(0);
+   auto zParticle = real_t(0);
+
+   auto rank = mpi::MPIManager::instance()->rank();
+
+   for( uint_t nPart = 0; nPart < numberOfParticles; ++nPart )
+   {
+
+      WALBERLA_ROOT_SECTION()
+      {
+         xParticle = math::realRandom<real_t>(generationDomain.xMin(), generationDomain.xMax());
+         yParticle = math::realRandom<real_t>(generationDomain.yMin(), generationDomain.yMax());
+         zParticle = math::realRandom<real_t>(generationDomain.zMin(), generationDomain.zMax());
+
+      }
+
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::broadcastObject( xParticle );
+         mpi::broadcastObject( yParticle );
+         mpi::broadcastObject( zParticle );
+      }
+
+      auto position = Vector3<real_t>( xParticle, yParticle, zParticle );
+      //WALBERLA_LOG_INFO_ON_ROOT(position);
+
+      if (!rpdDomain->isContainedInProcessSubdomain(uint_c(rank), position)) continue;
+      auto p                       = ps->create();
+      p->setPosition(position);
+      p->setInteractionRadius(diameter * real_t(0.5));
+      p->setShapeID(sphereShape);
+      p->setType(1);
+      p->setOwner(rank);
+   }
+
+   syncCall();
+
+   // resolve possible overlaps of the particles due to the random initialization via a particle-only simulation
+
+   const bool useOpenMP = false;
+   const real_t dt_RPD_Init = real_t(1);
+   const auto initialParticleSimSteps = uint_t(20000);
+   const real_t overlapLimit = real_t(0.001) * diameter;
+
+   auto particleVtkOutput = make_shared<mesa_pd::vtk::ParticleVtkOutput>(ps);
+   particleVtkOutput->addOutput<mesa_pd::data::SelectParticleLinearVelocity>("velocity");
+   particleVtkOutput->setParticleSelector( [sphereShape](const mesa_pd::data::ParticleStorage::iterator& pIt) {return !mesa_pd::data::particle_flags::isSet(pIt->getFlags(), mesa_pd::data::particle_flags::GHOST) && pIt->getShapeID() == sphereShape;} ); //limit output to local sphere
+   auto particleVtkWriterInit = vtk::createVTKOutput_PointData(particleVtkOutput, "Particles_init", 1, baseFolder, "simulation_step");
+
+   for(auto pet = uint_t(0); pet <= initialParticleSimSteps; ++pet )
+   {
+
+
+      real_t maxPenetrationDepth = 0;
+      ps->forEachParticlePairHalf(useOpenMP, mesa_pd::kernel::ExcludeInfiniteInfinite(), *accessor,
+                                  [&collisionResponse, &rpdDomain, &maxPenetrationDepth, dt_RPD_Init]
+                                        (const size_t idx1, const size_t idx2, auto& ac)
+                                  {
+                                     mesa_pd::collision_detection::AnalyticContactDetection acd;
+                                     mesa_pd::kernel::DoubleCast double_cast;
+                                     mesa_pd::mpi::ContactFilter contact_filter;
+                                     if (double_cast(idx1, idx2, ac, acd, ac ))
+                                     {
+                                        if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), *rpdDomain))
+                                        {
+                                           maxPenetrationDepth = std::max(maxPenetrationDepth, std::abs(acd.getPenetrationDepth()));
+                                           collisionResponse(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(),
+                                                 acd.getContactNormal(), acd.getPenetrationDepth(), dt_RPD_Init);
+                                        }
+                                     }
+                                  },
+                                  *accessor );
+
+      reduceAndSwapContactHistory(*ps);
+
+      mpi::allReduceInplace(maxPenetrationDepth, mpi::MAX);
+
+      reduceProperty.operator()<mesa_pd::ForceTorqueNotification>(*ps);
+
+      ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, mesa_pd::kernel::ExplicitEuler(dt_RPD_Init), *accessor);
+      syncCall();
+
+      if( pet % uint_t(20) == uint_t(0) )
+      {
+         if(vtkDuringInit)
+         {
+            particleVtkWriterInit->write();
+         }
+         WALBERLA_LOG_INFO_ON_ROOT(pet << " - current max overlap = " << maxPenetrationDepth / diameter * real_t(100) << "%");
+      }
+
+      if(maxPenetrationDepth < overlapLimit) break;
+
+      // reset velocites to avoid too large ones
+
+      ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor,
+            [](const size_t idx, ParticleAccessor_T& ac){
+               ac.setLinearVelocity(idx, ac.getLinearVelocity(idx) * real_t(0.5));
+               ac.setAngularVelocity(idx, ac.getAngularVelocity(idx) * real_t(0.5));
+               }, *accessor);
+
+
+   }
+
+
+   // reset all velocities to zero
+   Vector3<real_t> initialSphereVelocity(real_t(0));
+
+   ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectAll(), *accessor, [](const size_t idx, ParticleAccessor_T& ac){
+      ac.getNewContactHistoryRef(idx).clear();
+      ac.getOldContactHistoryRef(idx).clear();
+   }, *accessor);
+
+   WALBERLA_LOG_INFO_ON_ROOT("Setting initial velocity " << initialSphereVelocity << " of all spheres");
+   ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor,
+                        [initialSphereVelocity](const size_t idx, ParticleAccessor_T& ac){
+                           ac.setLinearVelocity(idx, initialSphereVelocity);
+                           ac.setAngularVelocity(idx, Vector3<real_t>(real_t(0)));
+                        }, *accessor);
+
+   syncCall();
+
+   ///////////////////////
+   // ADD DATA TO BLOCKS //
+   ////////////////////////
+
+   // create the lattice model
+   LatticeModel_T latticeModel = LatticeModel_T( lbm::collision_model::TRT::constructWithMagicNumber( omega ) );
+
+   // add PDF field
+   BlockDataID pdfFieldID = lbm::addPdfFieldToStorage< LatticeModel_T >( blocks, "pdf field (zyxf)", latticeModel,
+                                                                         Vector3< real_t >( real_t(0) ), real_t(1),
+                                                                         uint_t(1), field::zyxf );
+
+   // add flag field
+   BlockDataID flagFieldID = field::addFlagFieldToStorage<FlagField_T>( blocks, "flag field" );
+
+   // add particle field
+   BlockDataID particleFieldID = field::addToStorage<lbm_mesapd_coupling::ParticleField_T>( blocks, "particle field", accessor->getInvalidUid(), field::zyxf, FieldGhostLayers );
+
+   // add boundary handling & initialize outer domain boundaries
+   using BoundaryHandling_T = MyBoundaryHandling<ParticleAccessor_T>::Type;
+   BlockDataID boundaryHandlingID = blocks->addStructuredBlockData< BoundaryHandling_T >(MyBoundaryHandling<ParticleAccessor_T>( flagFieldID, pdfFieldID, particleFieldID, accessor, useEntireFieldTraversal), "boundary handling" );
+
+   Vector3<real_t> gravitationalForce( real_t(0), real_t(0), -(densityRatio - real_t(1)) * gravitationalAcceleration * sphereVolume );
+   lbm_mesapd_coupling::AddForceOnParticlesKernel addGravitationalForce(gravitationalForce);
+   lbm_mesapd_coupling::ResetHydrodynamicForceTorqueKernel resetHydrodynamicForceTorque;
+   lbm_mesapd_coupling::AverageHydrodynamicForceTorqueKernel averageHydrodynamicForceTorque;
+   lbm_mesapd_coupling::LubricationCorrectionKernel lubricationCorrectionKernel(viscosity, [](real_t r){return (real_t(0.001 + real_t(0.00007)*r))*r;});
+   lbm_mesapd_coupling::ParticleMappingKernel<BoundaryHandling_T> particleMappingKernel(blocks, boundaryHandlingID);
+   lbm_mesapd_coupling::MovingParticleMappingKernel<BoundaryHandling_T> movingParticleMappingKernel(blocks, boundaryHandlingID, particleFieldID);
+
+   WALBERLA_LOG_INFO_ON_ROOT(" - Lubrication correction:");
+   WALBERLA_LOG_INFO_ON_ROOT("   - normal cut off distance = " << lubricationCorrectionKernel.getNormalCutOffDistance());
+   WALBERLA_LOG_INFO_ON_ROOT("   - tangential translational cut off distance = " << lubricationCorrectionKernel.getTangentialTranslationalCutOffDistance());
+   WALBERLA_LOG_INFO_ON_ROOT("   - tangential rotational cut off distance = " << lubricationCorrectionKernel.getTangentialRotationalCutOffDistance());
+   const real_t maximumLubricationCutOffDistance = std::max(lubricationCorrectionKernel.getNormalCutOffDistance(), std::max(lubricationCorrectionKernel.getTangentialRotationalCutOffDistance(), lubricationCorrectionKernel.getTangentialTranslationalCutOffDistance()));
+
+
+   // map planes into the LBM simulation -> act as no-slip boundaries
+   ps->forEachParticle(false, lbm_mesapd_coupling::GlobalParticlesSelector(), *accessor, movingParticleMappingKernel, *accessor, MO_Flag);
+
+   // map particles into the LBM simulation
+   ps->forEachParticle(false, lbm_mesapd_coupling::RegularParticlesSelector(), *accessor, movingParticleMappingKernel, *accessor, MO_Flag);
+
+   lbm::BlockForestEvaluation<FlagField_T> bfEval(blocks, flagFieldID, Fluid_Flag);
+
+   WALBERLA_LOG_INFO_ON_ROOT(bfEval.loggingString());
+
+   ///////////////
+   // TIME LOOP //
+   ///////////////
+
+   // create the timeloop
+   SweepTimeloop timeloop( blocks->getBlockStorage(), timesteps );
+
+   timeloop.addFuncBeforeTimeStep( RemainingTimeLogger( timeloop.getNrOfTimeSteps() ), "Remaining Time Logger" );
+
+   if( vtkIOFreq != uint_t(0) )
+   {
+      auto particleVtkWriter = vtk::createVTKOutput_PointData(particleVtkOutput, "Particles", vtkIOFreq, baseFolder, "simulation_step");
+      timeloop.addFuncBeforeTimeStep( vtk::writeFiles( particleVtkWriter ), "VTK (sphere data)" );
+
+      // flag field
+      auto flagFieldVTK = vtk::createVTKOutput_BlockData( blocks, "flag_field", vtkIOFreq, 1, false, baseFolder );
+      flagFieldVTK->addCellDataWriter( make_shared< field::VTKWriter< FlagField_T > >( flagFieldID, "FlagField" ) );
+      timeloop.addFuncBeforeTimeStep( vtk::writeFiles( flagFieldVTK ), "VTK (flag field data)" );
+
+      // pdf field
+      auto pdfFieldVTK = vtk::createVTKOutput_BlockData( blocks, "fluid_field", vtkIOFreq, 0, false, baseFolder );
+
+      field::FlagFieldCellFilter< FlagField_T > fluidFilter( flagFieldID );
+      fluidFilter.addFlag( Fluid_Flag );
+      pdfFieldVTK->addCellInclusionFilter( fluidFilter );
+
+      pdfFieldVTK->addCellDataWriter( make_shared< lbm::VelocityVTKWriter< LatticeModel_T, float > >( pdfFieldID, "VelocityFromPDF" ) );
+      pdfFieldVTK->addCellDataWriter( make_shared< lbm::DensityVTKWriter < LatticeModel_T, float > >( pdfFieldID, "DensityFromPDF" ) );
+
+      timeloop.addFuncBeforeTimeStep( vtk::writeFiles( pdfFieldVTK ), "VTK (fluid field data)" );
+
+      auto domainDecompVTK = vtk::createVTKOutput_DomainDecomposition(blocks, "domain_decomposition", vtkIOFreq, baseFolder );
+      timeloop.addFuncBeforeTimeStep( vtk::writeFiles(domainDecompVTK), "VTK (domain decomposition)");
+   }
+
+
+   // setup of the LBM communication for synchronizing the pdf field between neighboring blocks
+   blockforest::communication::UniformBufferedScheme< Stencil_T > optimizedPDFCommunicationScheme( blocks );
+   optimizedPDFCommunicationScheme.addPackInfo( make_shared< lbm::PdfFieldPackInfo< LatticeModel_T > >( pdfFieldID ) ); // optimized sync
+
+   auto sweep = lbm::makeCellwiseSweep< LatticeModel_T, FlagField_T >( pdfFieldID, flagFieldID, Fluid_Flag );
+
+   if( !useFusedStreamCollide )
+   {
+      // Collide
+      timeloop.add() << Sweep( makeCollideSweep(sweep), "Collide" );
+   }
+
+   // add LBM communication function and boundary handling sweep (does the hydro force calculations and the no-slip treatment)
+   timeloop.add() << BeforeFunction( optimizedPDFCommunicationScheme, "LBM Communication" )
+                  << Sweep( BoundaryHandling_T::getBlockSweep( boundaryHandlingID ), "Boundary Handling" );
+
+   if( useFusedStreamCollide )
+   {
+      // streaming & collide
+      timeloop.add() << Sweep( makeSharedSweep(sweep), "Stream&Collide" );
+   } else
+   {
+      // streaming
+      timeloop.add() << Sweep( makeStreamSweep(sweep), "Stream" );
+
+   }
+
+   SweepTimeloop timeloopAfterParticles( blocks->getBlockStorage(), timesteps );
+
+   bool conserveMomentum = false;
+   // sweep for updating the particle mapping into the LBM simulation
+   timeloopAfterParticles.add() << Sweep( lbm_mesapd_coupling::makeMovingParticleMapping<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID,boundaryHandlingID, particleFieldID, accessor, MO_Flag, FormerMO_Flag,
+         lbm_mesapd_coupling::RegularParticlesSelector(), conserveMomentum), "Particle Mapping" );
+
+   bool recomputeTargetDensity = false;
+   auto gradReconstructor = lbm_mesapd_coupling::makeGradsMomentApproximationReconstructor<BoundaryHandling_T>(blocks, boundaryHandlingID, omega, recomputeTargetDensity,true);
+
+
+   blockforest::communication::UniformBufferedScheme< Stencil_T > fullPDFCommunicationScheme( blocks );
+   fullPDFCommunicationScheme.addPackInfo( make_shared< field::communication::PackInfo< PdfField_T > >( pdfFieldID ) ); // full sync
+
+   timeloopAfterParticles.add() << BeforeFunction( fullPDFCommunicationScheme, "PDF Communication" )
+                                << Sweep( makeSharedSweep(lbm_mesapd_coupling::makePdfReconstructionManager<PdfField_T,BoundaryHandling_T>(blocks, pdfFieldID, boundaryHandlingID, particleFieldID, accessor, FormerMO_Flag, Fluid_Flag,
+                                      gradReconstructor, conserveMomentum) ), "PDF Restore" );
+
+
+   ////////////////////////
+   // EXECUTE SIMULATION //
+   ////////////////////////
+
+   WcTimingPool timeloopTiming;
+
+   std::vector< std::vector<std::string> > timerKeys;
+   std::vector<std::string> LBMTimer;
+   LBMTimer.emplace_back("Stream&Collide");
+   LBMTimer.emplace_back("Stream");
+   LBMTimer.emplace_back("Collide");
+   timerKeys.push_back(LBMTimer);
+
+   std::vector<std::string> bhTimer;
+   bhTimer.emplace_back("Boundary Handling");
+   timerKeys.push_back(bhTimer);
+
+   std::vector<std::string> couplingTimer1;
+   couplingTimer1.emplace_back("Particle Mapping");
+   std::vector<std::string> couplingTimer2;
+   couplingTimer2.emplace_back("PDF Restore");
+   timerKeys.push_back(couplingTimer1);
+   timerKeys.push_back(couplingTimer2);
+
+   std::vector<std::string> rpdTimer;
+   rpdTimer.emplace_back("RPD Force");
+   rpdTimer.emplace_back("RPD VV1");
+   rpdTimer.emplace_back("RPD VV2");
+   rpdTimer.emplace_back("RPD Lub");
+   rpdTimer.emplace_back("RPD Collision");
+   timerKeys.push_back(rpdTimer);
+
+   uint_t numCells = uint_t(0);
+   uint_t numFluidCells = uint_t(0);
+   uint_t numNBCells = uint_t(0);
+   uint_t numLocalParticles = uint_t(0);
+   uint_t numGhostParticles = uint_t(0);
+   uint_t numContacts = uint_t(0);
+
+   std::vector<double> timings(timerKeys.size());
+
+   // every rank writes its own file -> numProcesses number of samples!
+   int myRank = MPIManager::instance()->rank();
+
+   std::string logFileName = baseFolder + "/load";
+   logFileName += "_settling";
+   logFileName += "_spheres";
+   logFileName += "_d" + std::to_string(int_c(std::ceil(diameter)));
+   logFileName += "_bs" + std::to_string(blockSize);
+   logFileName += "_" + std::to_string(myRank) + ".txt";
+
+
+   std::ofstream file;
+
+   if(!noFileOutput)
+   {
+      WALBERLA_LOG_INFO_ON_ROOT("Writing load info to file " << logFileName);
+      file.open( logFileName.c_str());
+      file << "# svf = " << solidVolumeFraction << ", d = " << diameter << ", domain = " << XCells << "x" << YCells << "x" << ZCells << "\n";
+   }
+
+
+   auto timeStepOfFirstTiming = uint_t(50);
+
+   if( timesteps - timeStepOfFirstTiming < numSamples )
+   {
+      WALBERLA_LOG_WARNING_ON_ROOT("Less actual time steps than number of required samples!");
+   }
+
+   uint_t nSample( 0 ); // number of current sample
+   real_t samplingFrequency = real_c(timesteps - timeStepOfFirstTiming) / real_c(numSamples);
+
+   // time loop
+   for (uint_t i = 0; i < timesteps; ++i )
+   {
+      // perform a single simulation step
+
+      timeloop.singleStep( timeloopTiming );
+
+      reduceProperty.operator()<mesa_pd::HydrodynamicForceTorqueNotification>(*ps);
+
+      timeloopTiming["RPD Force"].start();
+      if( i == 0 )
+      {
+         lbm_mesapd_coupling::InitializeHydrodynamicForceTorqueForAveragingKernel initializeHydrodynamicForceTorqueForAveragingKernel;
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, initializeHydrodynamicForceTorqueForAveragingKernel, *accessor );
+      }
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, averageHydrodynamicForceTorque, *accessor );
+      timeloopTiming["RPD Force"].end();
+
+      for(auto subCycle = uint_t(0); subCycle < numRPDSubCycles; ++subCycle )
+      {
+
+         timeloopTiming["RPD VV1"].start();
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPreForce, *accessor);
+         timeloopTiming["RPD VV1"].end();
+
+         syncCall();
+
+         // lubrication correction
+         timeloopTiming["RPD Lub"].start();
+         ps->forEachParticlePairHalf(useOpenMP, mesa_pd::kernel::ExcludeInfiniteInfinite(), *accessor,
+                                     [&lubricationCorrectionKernel,maximumLubricationCutOffDistance, &rpdDomain,&numContacts]
+                                     (const size_t idx1, const size_t idx2, auto& ac)
+                                     {
+                                        mesa_pd::collision_detection::AnalyticContactDetection acd;
+                                        acd.getContactThreshold() = maximumLubricationCutOffDistance;
+                                        mesa_pd::kernel::DoubleCast double_cast;
+                                        mesa_pd::mpi::ContactFilter contact_filter;
+                                        if (double_cast(idx1, idx2, ac, acd, ac ))
+                                        {
+                                           if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), *rpdDomain))
+                                           {
+                                              double_cast(acd.getIdx1(), acd.getIdx2(), ac, lubricationCorrectionKernel, ac, acd.getContactNormal(), acd.getPenetrationDepth());
+                                              ++numContacts; // this then also includes all actual contacts since there the contact threshold is smaller
+                                           }
+                                        }
+                                     },
+                                     *accessor );
+         timeloopTiming["RPD Lub"].end();
+
+         // collision response
+         timeloopTiming["RPD Collision"].start();
+         ps->forEachParticlePairHalf(useOpenMP, mesa_pd::kernel::ExcludeInfiniteInfinite(), *accessor,
+                                     [&collisionResponse, &rpdDomain, timeStepSizeRPD]
+                                           (const size_t idx1, const size_t idx2, auto& ac)
+                                     {
+                                        mesa_pd::collision_detection::AnalyticContactDetection acd;
+                                        mesa_pd::kernel::DoubleCast double_cast;
+                                        mesa_pd::mpi::ContactFilter contact_filter;
+                                        if (double_cast(idx1, idx2, ac, acd, ac ))
+                                        {
+                                           if (contact_filter(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), *rpdDomain))
+                                           {
+                                              collisionResponse(acd.getIdx1(), acd.getIdx2(), ac, acd.getContactPoint(), acd.getContactNormal(), acd.getPenetrationDepth(), timeStepSizeRPD);
+                                           }
+                                        }
+                                     },
+                                     *accessor );
+
+         timeloopTiming["RPD Collision"].end();
+
+         reduceAndSwapContactHistory(*ps);
+
+         timeloopTiming["RPD Force"].start();
+         lbm_mesapd_coupling::AddHydrodynamicInteractionKernel addHydrodynamicInteraction;
+         ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addHydrodynamicInteraction, *accessor );
+         ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addGravitationalForce, *accessor );
+         timeloopTiming["RPD Force"].end();
+
+         reduceProperty.operator()<mesa_pd::ForceTorqueNotification>(*ps);
+
+         // integration
+         timeloopTiming["RPD VV2"].start();
+         ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPostForce, *accessor);
+         timeloopTiming["RPD VV2"].end();
+
+         syncCall();
+
+
+      }
+
+      timeloopTiming["RPD Force"].start();
+      ps->forEachParticle( useOpenMP, mesa_pd::kernel::SelectAll(), *accessor, resetHydrodynamicForceTorque, *accessor );
+      timeloopTiming["RPD Force"].end();
+
+
+      // update particle mapping
+      timeloopAfterParticles.singleStep(timeloopTiming);
+
+      //WALBERLA_LOG_INFO_ON_ROOT(timeloopTiming);
+
+      // check if current time step should be included in sample
+      if( i >= uint_c( samplingFrequency * real_c(nSample) ) + timeStepOfFirstTiming )
+      {
+         // include -> evaluate all timers and quantities
+
+         evaluateFluidQuantities<BoundaryHandling_T>(blocks, boundaryHandlingID, numCells, numFluidCells, numNBCells);
+         evaluateRPDQuantities(ps, numLocalParticles, numGhostParticles);
+
+         evaluateTimers(timeloopTiming, timerKeys, timings);
+
+         if(!noFileOutput)
+         {
+            auto totalTime = std::accumulate(timings.begin(), timings.end(), 0.0 );
+
+            file << timeloop.getCurrentTimeStep() << " " << real_c(timeloop.getCurrentTimeStep()) / Tref << " "
+                 << numCells << " " << numFluidCells << " " << numNBCells << " "
+                 << numLocalParticles << " " << numGhostParticles << " "
+                 << real_c(numContacts) / real_c(numRPDSubCycles) << " " << numRPDSubCycles;
+            for (auto timing : timings) {
+               file << " " << timing;
+            }
+            file << " " << totalTime << "\n";
+         }
+
+         ++nSample;
+      }
+
+      numCells = uint_t(0);
+      numFluidCells = uint_t(0);
+      numNBCells = uint_t(0);
+      numLocalParticles = uint_t(0);
+      numGhostParticles = uint_t(0);
+      numContacts = uint_t(0);
+
+      // reset timers to always include only a single time step in them
+      timeloopTiming.clear();
+   }
+
+   if(!noFileOutput) {
+      file.close();
+   }
+
+   WALBERLA_LOG_INFO_ON_ROOT("Simulation finished!");
+
+   return 0;
+
+}
+
+}
+
+int main( int argc, char **argv ){
+   fluid_particle_workload_evaluation::main(argc, argv);
+}
diff --git a/apps/benchmarks/FluidParticleCouplingWithLoadBalancing/Utility.h b/apps/benchmarks/FluidParticleCouplingWithLoadBalancing/Utility.h
new file mode 100644
index 0000000000000000000000000000000000000000..f9e4c05f1273a463486ef7b18647d31020d5bfda
--- /dev/null
+++ b/apps/benchmarks/FluidParticleCouplingWithLoadBalancing/Utility.h
@@ -0,0 +1,67 @@
+#pragma once
+
+#include "lbm_mesapd_coupling/amr/BlockInfo.h"
+
+namespace walberla {
+namespace lbm_mesapd_coupling  {
+namespace amr {
+
+/*
+ * Result from the workload evaluation as described in
+ *  Rettinger, Ruede - "Dynamic Load Balancing Techniques for Particulate Flow Simulations", 2019, Computation
+ */
+real_t fittedLBMWeightEvaluationFunction(const BlockInfo& blockInfo)
+{
+   uint_t Ce = blockInfo.numberOfCells;
+   uint_t F  = blockInfo.numberOfFluidCells;
+   real_t weight = real_t(7.597476065046571e-06) * real_c(Ce) + real_t(8.95723566283202e-05) * real_c(F) + real_t(-0.1526111388616016);
+   return std::max(weight,real_t(0));
+}
+real_t fittedBHWeightEvaluationFunction(const BlockInfo& blockInfo)
+{
+   uint_t Ce = blockInfo.numberOfCells;
+   uint_t NB = blockInfo.numberOfNearBoundaryCells;
+   real_t weight = real_t(1.3067711379655123e-07) * real_c(Ce) + real_t(0.0007289549127205142) * real_c(NB) + real_t(-0.1575698071795788);
+   return std::max(weight,real_t(0));
+}
+real_t fittedRPDWeightEvaluationFunction(const BlockInfo& blockInfo)
+{
+   uint_t Pl = blockInfo.numberOfLocalParticles;
+   uint_t Pg = blockInfo.numberOfGhostParticles;
+   uint_t Sc = blockInfo.numberOfRPDSubCycles;
+   real_t cPlPg2 = real_t(2.402288635599054e-05);
+   real_t cPl    = real_t(0.00040932622363097144);
+   real_t cPg    = real_t(0.0007268941363125683);
+   real_t c      = real_t(2.01883028312316e-19);
+   real_t weight = real_c(Sc) * ( cPlPg2 * real_c(Pl+Pg) * real_c(Pl+Pg) + cPl * real_c(Pl) + cPg * real_c(Pg) + c );
+   return std::max(weight,real_t(0));
+}
+real_t fittedCoup1WeightEvaluationFunction(const BlockInfo& blockInfo)
+{
+   uint_t Ce = blockInfo.numberOfCells;
+   uint_t F  = blockInfo.numberOfFluidCells;
+   uint_t Pl = blockInfo.numberOfLocalParticles;
+   uint_t Pg = blockInfo.numberOfGhostParticles;
+   real_t weight = real_t(5.610203409278647e-06) * real_c(Ce) + real_t(-7.257635845636656e-07) * real_c(F) + real_t(0.02049703546054693) * real_c(Pl) + real_t(0.04248208493809902) * real_c(Pg) + real_t(-0.26609470510074784);
+   return std::max(weight,real_t(0));
+}
+real_t fittedCoup2WeightEvaluationFunction(const BlockInfo& blockInfo)
+{
+   uint_t Ce = blockInfo.numberOfCells;
+   uint_t F  = blockInfo.numberOfFluidCells;
+   uint_t Pl = blockInfo.numberOfLocalParticles;
+   uint_t Pg = blockInfo.numberOfGhostParticles;
+   real_t weight = real_t(7.198479654682179e-06) * real_c(Ce) + real_t(1.178247475854302e-06) * real_c(F) + real_t(-0.0026401549115124628) * real_c(Pl) + real_t(0.008459646786179298) * real_c(Pg) + real_t(-0.001077320113275954);
+   return std::max(weight,real_t(0));
+}
+real_t fittedTotalWeightEvaluationFunction(const BlockInfo& blockInfo)
+{
+   return fittedLBMWeightEvaluationFunction(blockInfo) + fittedBHWeightEvaluationFunction(blockInfo) +
+          fittedRPDWeightEvaluationFunction(blockInfo) + fittedCoup1WeightEvaluationFunction(blockInfo) +
+          fittedCoup2WeightEvaluationFunction(blockInfo);
+}
+
+} //namespace amr
+} //namespace lbm_mesapd_coupling
+} //namespace walberla
+
diff --git a/apps/benchmarks/ForcesOnSphereNearPlaneInShearFlow/ForcesOnSphereNearPlaneInShearFlow.cpp b/apps/benchmarks/ForcesOnSphereNearPlaneInShearFlow/ForcesOnSphereNearPlaneInShearFlow.cpp
index 7e5c37e2e6d0f56a765b987f774bb721df67925f..a01b5210f10c3dfecc54980e3f7861f0fd278f76 100644
--- a/apps/benchmarks/ForcesOnSphereNearPlaneInShearFlow/ForcesOnSphereNearPlaneInShearFlow.cpp
+++ b/apps/benchmarks/ForcesOnSphereNearPlaneInShearFlow/ForcesOnSphereNearPlaneInShearFlow.cpp
@@ -81,23 +81,23 @@ using walberla::uint_t;
 //////////////
 
 // PDF field, flag field & body field
-typedef lbm::D3Q19< lbm::collision_model::TRT, false >  LatticeModel_T;
+using LatticeModel_T = lbm::D3Q19<lbm::collision_model::TRT, false>;
 using Stencil_T = LatticeModel_T::Stencil;
 using PdfField_T = lbm::PdfField<LatticeModel_T>;
 
 using flag_t = walberla::uint8_t;
 using FlagField_T = FlagField<flag_t>;
-typedef GhostLayerField< pe::BodyID, 1 >  BodyField_T;
+using BodyField_T = GhostLayerField<pe::BodyID, 1>;
 
 const uint_t FieldGhostLayers = 4;
 
 // boundary handling
-typedef pe_coupling::SimpleBB< LatticeModel_T, FlagField_T > MO_SBB_T;
-typedef pe_coupling::CurvedLinear< LatticeModel_T, FlagField_T > MO_CLI_T;
+using MO_SBB_T = pe_coupling::SimpleBB<LatticeModel_T, FlagField_T>;
+using MO_CLI_T = pe_coupling::CurvedLinear<LatticeModel_T, FlagField_T>;
 
-typedef BoundaryHandling< FlagField_T, Stencil_T, MO_SBB_T, MO_CLI_T > BoundaryHandling_T;
+using BoundaryHandling_T = BoundaryHandling<FlagField_T, Stencil_T, MO_SBB_T, MO_CLI_T>;
 
-typedef std::tuple< pe::Sphere, pe::Plane > BodyTypeTuple;
+using BodyTypeTuple = std::tuple<pe::Sphere, pe::Plane>;
 
 ///////////
 // FLAGS //
diff --git a/apps/benchmarks/IntegratorAccuracy/CMakeLists.txt b/apps/benchmarks/IntegratorAccuracy/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8e95e831cc1fb275731b12bc14460cf64eedc729
--- /dev/null
+++ b/apps/benchmarks/IntegratorAccuracy/CMakeLists.txt
@@ -0,0 +1,4 @@
+waLBerla_link_files_to_builddir( "*.ipynb" )
+
+waLBerla_add_executable ( NAME IntegratorAccuracy 
+                          DEPENDS core mesa_pd )
diff --git a/apps/benchmarks/IntegratorAccuracy/IntegratorAccuracy.cpp b/apps/benchmarks/IntegratorAccuracy/IntegratorAccuracy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..af2dc8a3aaa54f805822eabcbd4139fb49d3cdf7
--- /dev/null
+++ b/apps/benchmarks/IntegratorAccuracy/IntegratorAccuracy.cpp
@@ -0,0 +1,274 @@
+//======================================================================================================================
+//
+//  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   IntegratorAccuracy.cpp
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include "mesa_pd/data/ParticleAccessor.h"
+
+#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
+
+#include "core/Environment.h"
+#include "core/math/all.h"
+
+#include <iostream>
+
+namespace walberla {
+
+using namespace walberla::mesa_pd;
+
+class SingleParticleAccessorWithShape : public data::SingleParticleAccessor
+{
+public:
+   void setInvMass(const size_t /*p_idx*/, const real_t &val)
+   { invMass_ = val; }
+
+   const auto &getInvMass(const size_t /*p_idx*/) const
+   { return invMass_; }
+
+   void setInvInertiaBF(const size_t /*p_idx*/, const Mat3 &val)
+   { invInertiaBF_ = val; }
+
+   const auto &getInvInertiaBF(const size_t /*p_idx*/) const
+   { return invInertiaBF_; }
+
+private:
+   real_t invMass_;
+   Mat3 invInertiaBF_;
+};
+
+struct Oscillator
+{
+   real_t amplitude = 1.5_r;
+   real_t k = 0.1_r;
+   real_t damping = 0_r;
+   real_t mass = 0.9_r;
+   real_t dt = 0.2_r;
+   real_t phaseFraction = 0_r;
+   real_t periods = 10_r;
+   real_t phase = phaseFraction * math::pi;
+   real_t dampingRatio = damping / (2_r * std::sqrt(mass * k));
+   real_t omega = std::sqrt(k / mass) * std::sqrt(1_r - dampingRatio * dampingRatio);
+   real_t decay = std::sqrt(k / mass) * dampingRatio;
+   real_t durationOnePeriod = 2_r * math::pi / omega;
+   uint_t timeSteps = uint_c(periods * durationOnePeriod / dt);
+
+   Oscillator(int argc, char **argv)
+   {
+      for (int i = 1; i < argc; ++i)
+      {
+         if (std::strcmp(argv[i], "--dt") == 0)
+         {
+            dt = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         if (std::strcmp(argv[i], "--amplitude") == 0)
+         {
+            amplitude = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         if (std::strcmp(argv[i], "--k") == 0)
+         {
+            k = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         if (std::strcmp(argv[i], "--damping") == 0)
+         {
+            damping = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         if (std::strcmp(argv[i], "--mass") == 0)
+         {
+            mass = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         if (std::strcmp(argv[i], "--phaseFraction") == 0)
+         {
+            phaseFraction = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         if (std::strcmp(argv[i], "--periods") == 0)
+         {
+            periods = real_c(std::atof(argv[++i]));
+            continue;
+         }
+         WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
+      }
+
+      update();
+   }
+
+   void update()
+   {
+      phase = phaseFraction * math::pi;
+      dampingRatio = damping / (2_r * std::sqrt(mass * k));
+      omega = std::sqrt(k / mass) * std::sqrt(1_r - dampingRatio * dampingRatio);
+      decay = std::sqrt(k / mass) * dampingRatio;
+      durationOnePeriod = 2_r * math::pi / omega;
+      timeSteps = uint_c(periods * durationOnePeriod / dt);
+   }
+
+   Vec3 getForce(const Vec3 &pos, const Vec3 &vel) const
+   {
+      return -k * pos - damping * vel;
+   }
+
+   real_t getEnergy(const real_t &pos, const real_t &vel) const
+   {
+      return 0.5_r * mass * vel * vel + 0.5_r * k * pos * pos;
+   }
+
+   real_t analyticalPos(real_t t) const
+   {
+      return amplitude * std::exp(-decay * t) * std::cos(omega * t + phase);
+   }
+
+   real_t analyticalVel(real_t t) const
+   {
+      return -decay * amplitude * std::exp(-decay * t) * std::cos(omega * t + phase)
+             -amplitude * std::exp(-decay * t) * omega * std::sin(omega * t + phase);
+   }
+};
+
+struct ExplicitEuler
+{
+   ExplicitEuler(real_t dt) : integrator(dt) {}
+   void operator()(SingleParticleAccessorWithShape& particle,
+                   const Oscillator& osc)
+   {
+      particle.setForce(0, osc.getForce(particle.getPosition(0),
+                                           particle.getLinearVelocity(0)));
+      integrator(0, particle);
+   }
+   kernel::ExplicitEuler integrator;
+};
+
+struct SemiImplicitEuler
+{
+   SemiImplicitEuler(real_t dt) : integrator(dt) {}
+   void operator()(SingleParticleAccessorWithShape& particle,
+                   const Oscillator& osc)
+   {
+      particle.setForce(0, osc.getForce(particle.getPosition(0),
+                                           particle.getLinearVelocity(0)));
+      integrator(0, particle);
+   }
+   kernel::SemiImplicitEuler integrator;
+};
+
+struct VelocityVerlet
+{
+   VelocityVerlet(real_t dt) : preVV(dt), postVV(dt) {}
+   void operator()(SingleParticleAccessorWithShape& particle,
+                   const Oscillator& osc)
+   {
+      preVV(0, particle);
+      particle.setForce(0, osc.getForce(particle.getPosition(0),
+                                           particle.getLinearVelocity(0)));
+      postVV(0, particle);
+   }
+   kernel::VelocityVerletPreForceUpdate preVV;
+   kernel::VelocityVerletPostForceUpdate postVV;
+};
+
+struct AccuracyResult
+{
+   real_t maxPosDeviation;
+   real_t maxVelDeviation;
+   real_t maxEneDeviation;
+};
+
+template <typename Integrator>
+AccuracyResult checkIntegrator(const Oscillator& osc)
+{
+   //init data structures
+   SingleParticleAccessorWithShape particle;
+
+   //first dummy argument is needed to fulfill accessor interface
+   particle.setPosition(0, Vec3(0, 0, osc.analyticalPos(0_r)));
+   particle.setLinearVelocity(0, Vec3(0, 0, osc.analyticalVel(0_r)));
+   particle.setInvMass(0, 1_r / osc.mass);
+   particle.setForce(0, osc.getForce(Vec3(0, 0, osc.analyticalPos(real_t(0))),
+                                        Vec3(0, 0, osc.analyticalVel(real_t(0)))));
+   particle.setOldForce(0, osc.getForce(Vec3(0, 0, osc.analyticalPos(-osc.dt)),
+                                           Vec3(0, 0, osc.analyticalVel(-osc.dt))));
+
+   // explicit euler
+   Integrator integrator(osc.dt);
+
+   real_t maxPosDeviation = 0_r;
+   real_t maxVelDeviation = 0_r;
+   real_t maxEneDeviation = 0_r;
+
+   for (auto i = uint_t(0); i <= osc.timeSteps; ++i)
+   {
+      real_t refPos = osc.analyticalPos(real_c(i) * osc.dt);
+      real_t refVel = osc.analyticalVel(real_c(i) * osc.dt);
+      real_t refEne = osc.getEnergy(refPos, refVel);
+
+      maxPosDeviation = std::max(maxPosDeviation, std::abs(particle.getPosition(0)[2] - refPos));
+      maxVelDeviation = std::max(maxVelDeviation, std::abs(particle.getLinearVelocity(0)[2] - refVel));
+      maxEneDeviation = std::max(maxEneDeviation, std::abs(osc.getEnergy(particle.getPosition(0)[2], particle.getLinearVelocity(0)[2]) - refEne));
+
+      std::cout << real_t(i) * osc.dt << " "
+                << refPos << " "
+                << refVel << " "
+                << refEne << " "
+                << particle.getPosition(0)[2] << " "
+                << particle.getLinearVelocity(0)[2] << " "
+                << osc.getEnergy(particle.getPosition(0)[2], particle.getLinearVelocity(0)[2]) << " "
+                << maxPosDeviation << " "
+                << maxVelDeviation << std::endl;
+
+      integrator(particle, osc);
+   }
+
+   return {maxPosDeviation, maxVelDeviation, maxEneDeviation};
+}
+
+int main(int argc, char **argv)
+{
+   mpi::Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   mpi::MPIManager::instance()->useWorldComm();
+
+   Oscillator osc(argc, argv);
+
+   AccuracyResult res;
+//   res = walberla::checkIntegrator<walberla::ExplicitEuler>(osc);
+//   res = walberla::checkIntegrator<walberla::SemiImplicitEuler>(osc);
+   res = walberla::checkIntegrator<walberla::VelocityVerlet>(osc);
+
+   return EXIT_SUCCESS;
+}
+
+} //namespace walberla
+
+
+/*
+ * Simulates a harmonic oscillator to test the accuracy of the integrators.
+ * Playground for integrator analysis. The corresponding unit test is located at
+ * tests/mesa_pd/kernel/IntegratorAccuracy.cpp
+ */
+int main(int argc, char **argv)
+{
+   return walberla::main(argc, argv);
+}
+
diff --git a/apps/benchmarks/IntegratorAccuracy/IntegratorAccuracy.ipynb b/apps/benchmarks/IntegratorAccuracy/IntegratorAccuracy.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..f0cc8feef715c2af5e8e6192175d4cec00f09b44
--- /dev/null
+++ b/apps/benchmarks/IntegratorAccuracy/IntegratorAccuracy.ipynb
@@ -0,0 +1,156 @@
+{
+ "cells": [
+  {
+   "cell_type": "markdown",
+   "metadata": {},
+   "source": [
+    "# error analysis of integration kernels"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import matplotlib.pyplot as plt\n",
+    "import numpy as np"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "integrator = 'euler'\n",
+    "data01 = np.loadtxt(f'{integrator}01.txt').transpose()\n",
+    "data02 = np.loadtxt(f'{integrator}02.txt').transpose()\n",
+    "data04 = np.loadtxt(f'{integrator}04.txt').transpose()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.plot(data01[0], data01[7], label='dt = 0.1')\n",
+    "plt.plot(data02[0], data02[7], label='dt = 0.2')\n",
+    "plt.plot(data04[0], data04[7], label='dt = 0.4')\n",
+    "plt.xlabel('time')\n",
+    "plt.ylabel('max pos error')\n",
+    "plt.legend()\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.plot(data01[0], data01[8], label='dt = 0.1')\n",
+    "plt.plot(data02[0], data02[8], label='dt = 0.2')\n",
+    "plt.plot(data04[0], data04[8], label='dt = 0.4')\n",
+    "plt.xlabel('time')\n",
+    "plt.ylabel('max vel error')\n",
+    "plt.legend()\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "final_errors = np.ones(3)\n",
+    "final_errors[0] = np.abs(data01[7][-1])\n",
+    "final_errors[1] = np.abs(data02[7][-1])\n",
+    "final_errors[2] = np.abs(data04[7][-1])\n",
+    "\n",
+    "plt.bar(np.arange(3), final_errors)\n",
+    "plt.show()\n",
+    "\n",
+    "print(final_errors[2] / final_errors[1])\n",
+    "print(final_errors[1] / final_errors[0])"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.plot(data01[0], data01[1], label='analytic')\n",
+    "plt.plot(data01[0], data01[4], label='dt = 0.1')\n",
+    "plt.plot(data02[0], data02[4], label='dt = 0.2')\n",
+    "plt.plot(data04[0], data04[4], label='dt = 0.4')\n",
+    "plt.xlabel('time')\n",
+    "plt.ylabel('pos')\n",
+    "plt.legend()\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.plot(data01[0], data01[2], label='analytic')\n",
+    "plt.plot(data01[0], data01[5], label='dt = 0.1')\n",
+    "plt.plot(data02[0], data02[5], label='dt = 0.2')\n",
+    "plt.plot(data04[0], data04[5], label='dt = 0.4')\n",
+    "plt.xlabel('time')\n",
+    "plt.ylabel('vel')\n",
+    "plt.legend()\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "plt.plot(data01[0], data01[6] / data01[3][0], label='dt = 0.1')\n",
+    "plt.plot(data02[0], data02[6] / data01[3][0], label='dt = 0.2')\n",
+    "plt.plot(data04[0], data04[6] / data01[3][0], label='dt = 0.4')\n",
+    "plt.xlabel('time')\n",
+    "plt.ylabel('relative energy')\n",
+    "plt.legend()\n",
+    "plt.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.7.4"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 2
+}
diff --git a/apps/benchmarks/MotionSingleHeavySphere/MotionSingleHeavySphere.cpp b/apps/benchmarks/MotionSingleHeavySphere/MotionSingleHeavySphere.cpp
index efeb8186e0f7a7659f58f17f50c0368659a46b58..293451af236c7c2605968c10cbac6333367911f9 100644
--- a/apps/benchmarks/MotionSingleHeavySphere/MotionSingleHeavySphere.cpp
+++ b/apps/benchmarks/MotionSingleHeavySphere/MotionSingleHeavySphere.cpp
@@ -76,29 +76,29 @@ using walberla::uint_t;
 //////////////
 
 // PDF field, flag field & body field
-typedef lbm::D3Q19< lbm::collision_model::TRT, false >  LatticeModel_T;
+using LatticeModel_T = lbm::D3Q19<lbm::collision_model::TRT, false>;
 
 using Stencil_T = LatticeModel_T::Stencil;
 using PdfField_T = lbm::PdfField<LatticeModel_T>;
 
 using flag_t = walberla::uint8_t;
 using FlagField_T = FlagField<flag_t>;
-typedef GhostLayerField< pe::BodyID, 1 >  BodyField_T;
+using BodyField_T = GhostLayerField<pe::BodyID, 1>;
 
-typedef std::pair< pe::BodyID, real_t >                              BodyAndVolumeFraction_T;
-typedef GhostLayerField< std::vector< BodyAndVolumeFraction_T >, 1 > BodyAndVolumeFractionField_T;
+using BodyAndVolumeFraction_T = std::pair<pe::BodyID, real_t>;
+using BodyAndVolumeFractionField_T = GhostLayerField<std::vector<BodyAndVolumeFraction_T>, 1>;
 
 const uint_t FieldGhostLayers = 1;
 
 // boundary handling
-typedef lbm::SimpleUBB< LatticeModel_T, flag_t >           UBB_T;
-typedef lbm::SimplePressure< LatticeModel_T, flag_t >      Outlet_T;
+using UBB_T = lbm::SimpleUBB<LatticeModel_T, flag_t>;
+using Outlet_T = lbm::SimplePressure<LatticeModel_T, flag_t>;
 
-typedef pe_coupling::SimpleBB< LatticeModel_T, FlagField_T >        MEM_BB_T;
-typedef pe_coupling::CurvedLinear< LatticeModel_T, FlagField_T >    MEM_CLI_T;
-typedef pe_coupling::CurvedQuadratic< LatticeModel_T, FlagField_T > MEM_MR_T;
+using MEM_BB_T = pe_coupling::SimpleBB<LatticeModel_T, FlagField_T>;
+using MEM_CLI_T = pe_coupling::CurvedLinear<LatticeModel_T, FlagField_T>;
+using MEM_MR_T = pe_coupling::CurvedQuadratic<LatticeModel_T, FlagField_T>;
 
-typedef BoundaryHandling< FlagField_T, Stencil_T, UBB_T, Outlet_T, MEM_BB_T, MEM_CLI_T, MEM_MR_T > BoundaryHandling_T;
+using BoundaryHandling_T = BoundaryHandling<FlagField_T, Stencil_T, UBB_T, Outlet_T, MEM_BB_T, MEM_CLI_T, MEM_MR_T>;
 
 using BodyTypeTuple = std::tuple<pe::Sphere>;
 
@@ -1236,7 +1236,7 @@ int main( int argc, char **argv )
       // reconstruct missing PDFs
       using ExtrapolationFinder_T = pe_coupling::SphereNormalExtrapolationDirectionFinder;
       ExtrapolationFinder_T extrapolationFinder( blocks, bodyFieldID );
-      typedef pe_coupling::ExtrapolationReconstructor< LatticeModel_T, BoundaryHandling_T, ExtrapolationFinder_T > Reconstructor_T;
+      using Reconstructor_T = pe_coupling::ExtrapolationReconstructor<LatticeModel_T, BoundaryHandling_T, ExtrapolationFinder_T>;
       Reconstructor_T reconstructor( blocks, boundaryHandlingID, bodyFieldID, extrapolationFinder, true );
       timeloop.add() << Sweep( pe_coupling::PDFReconstruction< LatticeModel_T, BoundaryHandling_T, Reconstructor_T >
                                ( blocks, pdfFieldID, boundaryHandlingID, bodyStorageID, globalBodyStorage, bodyFieldID, reconstructor, FormerMEM_Flag, Fluid_Flag ), "PDF Restore" );
diff --git a/apps/benchmarks/NonUniformGrid/NonUniformGrid.cpp b/apps/benchmarks/NonUniformGrid/NonUniformGrid.cpp
index c5f1df93e544ed8a16f5ae802caa21268fc438a7..817097ccab6f4a08d1eb0bd80b169d1dca326111 100644
--- a/apps/benchmarks/NonUniformGrid/NonUniformGrid.cpp
+++ b/apps/benchmarks/NonUniformGrid/NonUniformGrid.cpp
@@ -97,11 +97,11 @@ using walberla::real_t;
 // TYPEDEFS //
 //////////////
 
-typedef lbm::D3Q19< lbm::collision_model::SRT,      false > D3Q19_SRT_INCOMP;
-typedef lbm::D3Q19< lbm::collision_model::SRT,      true  > D3Q19_SRT_COMP;
-typedef lbm::D3Q19< lbm::collision_model::TRT,      false > D3Q19_TRT_INCOMP;
-typedef lbm::D3Q19< lbm::collision_model::TRT,      true  > D3Q19_TRT_COMP;
-typedef lbm::D3Q19< lbm::collision_model::D3Q19MRT, false > D3Q19_MRT_INCOMP;
+using D3Q19_SRT_INCOMP = lbm::D3Q19<lbm::collision_model::SRT, false>;
+using D3Q19_SRT_COMP = lbm::D3Q19<lbm::collision_model::SRT, true>;
+using D3Q19_TRT_INCOMP = lbm::D3Q19<lbm::collision_model::TRT, false>;
+using D3Q19_TRT_COMP = lbm::D3Q19<lbm::collision_model::TRT, true>;
+using D3Q19_MRT_INCOMP = lbm::D3Q19<lbm::collision_model::D3Q19MRT, false>;
 
 template< typename LatticeModel_T >
 struct Types
@@ -308,7 +308,7 @@ void createSetupBlockForest( blockforest::SetupBlockForest & sforest, const Conf
    const memory_t memoryLimit = configBlock.getParameter< memory_t >( "memoryLimit", numeric_cast< memory_t >(256) );
 
    sforest.balanceLoad( blockforest::StaticLevelwiseCurveBalance(true), numberOfProcesses, bufferProcesses, memoryLimit, true,
-                        (bufferProcesses != uint_t(0)) ? true : false );
+                        bufferProcesses != uint_t(0) );
 
    WALBERLA_LOG_INFO_ON_ROOT( "SetupBlockForest created successfully:\n" << sforest );
 }
@@ -437,10 +437,10 @@ void ReGrid::operator()( std::vector< std::pair< const Block *, uint_t > > & min
 template< typename LatticeModel_T >
 struct MyBoundaryTypes
 {
-   typedef lbm::NoSlip< LatticeModel_T, flag_t >    NoSlip_T;
-   typedef lbm::SimpleUBB< LatticeModel_T, flag_t > UBB_T;
+   using NoSlip_T = lbm::NoSlip<LatticeModel_T, flag_t>;
+   using UBB_T = lbm::SimpleUBB<LatticeModel_T, flag_t>;
 
-   typedef BoundaryHandling< FlagField_T, typename Types<LatticeModel_T>::Stencil_T, NoSlip_T, UBB_T > BoundaryHandling_T;
+   using BoundaryHandling_T = BoundaryHandling<FlagField_T, typename Types<LatticeModel_T>::Stencil_T, NoSlip_T, UBB_T>;
 };  
 
 template< typename LatticeModel_T >
@@ -632,7 +632,7 @@ struct AddRefinementTimeStep
          }
          else
          {
-            typedef lbm::SplitSweep< LatticeModel_T, FlagField_T > Sweep_T;
+            using Sweep_T = lbm::SplitSweep<LatticeModel_T, FlagField_T>;
             auto mySweep = make_shared< Sweep_T >( pdfFieldId, flagFieldId, Fluid_Flag );
 
             addRefinementTimeStep< LatticeModel_T, Sweep_T >( timeloop, blocks, pdfFieldId, boundaryHandlingId, timingPool, levelwiseTimingPool,
diff --git a/apps/benchmarks/PhaseFieldAllenCahn/CMakeLists.txt b/apps/benchmarks/PhaseFieldAllenCahn/CMakeLists.txt
index 629c9ec0a609c7312027d037f5504fc3a4d3ecb6..951f04a225d870d92e693c99d3ea3406bd5bc3f7 100644
--- a/apps/benchmarks/PhaseFieldAllenCahn/CMakeLists.txt
+++ b/apps/benchmarks/PhaseFieldAllenCahn/CMakeLists.txt
@@ -16,6 +16,7 @@ if (WALBERLA_BUILD_WITH_CUDA)
     waLBerla_add_executable(NAME benchmark_multiphase
             FILES benchmark_multiphase.cpp InitializerFunctions.cpp multiphase_codegen.py
             DEPENDS blockforest core cuda field postprocessing lbm geometry timeloop gui BenchmarkPhaseFieldCodeGenGPU)
+    set_target_properties(benchmark_multiphase PROPERTIES CXX_VISIBILITY_PRESET hidden)
 else ()
     waLBerla_generate_target_from_python(NAME BenchmarkPhaseFieldCodeGenCPU
             FILE multiphase_codegen.py
@@ -31,5 +32,6 @@ else ()
     waLBerla_add_executable(NAME benchmark_multiphase
             FILES benchmark_multiphase.cpp InitializerFunctions.cpp multiphase_codegen.py
             DEPENDS blockforest core field postprocessing lbm geometry timeloop gui BenchmarkPhaseFieldCodeGenCPU)
+    set_target_properties(benchmark_multiphase PROPERTIES CXX_VISIBILITY_PRESET hidden)
 endif (WALBERLA_BUILD_WITH_CUDA)
 
diff --git a/apps/benchmarks/PhaseFieldAllenCahn/InitializerFunctions.cpp b/apps/benchmarks/PhaseFieldAllenCahn/InitializerFunctions.cpp
index 45e91e25a4e7190e16ef0fe53d78d6ed9d6cde89..5f7e5b2f634b71d0ce63571bdab1eabf24afd49f 100644
--- a/apps/benchmarks/PhaseFieldAllenCahn/InitializerFunctions.cpp
+++ b/apps/benchmarks/PhaseFieldAllenCahn/InitializerFunctions.cpp
@@ -25,8 +25,6 @@
 #include "field/communication/PackInfo.h"
 #include "field/vtk/VTKWriter.h"
 
-#include "python_coupling/DictWrapper.h"
-
 namespace walberla
 {
 using PhaseField_T = GhostLayerField< real_t, 1 >;
diff --git a/apps/benchmarks/PhaseFieldAllenCahn/InitializerFunctions.h b/apps/benchmarks/PhaseFieldAllenCahn/InitializerFunctions.h
index 4ba7896e77a3a901ab8b221e88e33aff1036d649..f30d41e17ef19947f831b59ab3e7fb05b49df384 100644
--- a/apps/benchmarks/PhaseFieldAllenCahn/InitializerFunctions.h
+++ b/apps/benchmarks/PhaseFieldAllenCahn/InitializerFunctions.h
@@ -18,15 +18,12 @@
 //
 //======================================================================================================================
 
-#include "core/Environment.h"
-#include "core/logging/Initialization.h"
 #include "core/math/Constants.h"
 
 #include "field/FlagField.h"
 #include "field/communication/PackInfo.h"
 #include "field/vtk/VTKWriter.h"
 
-#include "python_coupling/DictWrapper.h"
 #pragma once
 
 namespace walberla
diff --git a/apps/benchmarks/PhaseFieldAllenCahn/benchmark_multiphase.cpp b/apps/benchmarks/PhaseFieldAllenCahn/benchmark_multiphase.cpp
index 944ca0a0b2febc784c95128cedb4ba2386c20193..3513c15f2f13250e9462c84af59b3ef3462e21a9 100644
--- a/apps/benchmarks/PhaseFieldAllenCahn/benchmark_multiphase.cpp
+++ b/apps/benchmarks/PhaseFieldAllenCahn/benchmark_multiphase.cpp
@@ -27,8 +27,6 @@
 
 #include "field/AddToStorage.h"
 #include "field/FlagField.h"
-#include "field/communication/PackInfo.h"
-#include "field/python/Exports.h"
 #include "field/vtk/VTKWriter.h"
 
 #include "geometry/InitBoundaryHandling.h"
diff --git a/apps/benchmarks/PhaseFieldAllenCahn/multiphase_codegen.py b/apps/benchmarks/PhaseFieldAllenCahn/multiphase_codegen.py
index 5fd50cf5735c84d404005020d7711ea9aa74f3ab..a751f35a43bab045b19e3ec94253f6930aacb839 100644
--- a/apps/benchmarks/PhaseFieldAllenCahn/multiphase_codegen.py
+++ b/apps/benchmarks/PhaseFieldAllenCahn/multiphase_codegen.py
@@ -122,8 +122,9 @@ hydro_LB_step = get_collision_assignments_hydro(lb_method=method_hydro,
                                                 density=density,
                                                 velocity_input=u,
                                                 force=force_g,
-                                                optimization={"symbolic_field": g,
-                                                              "symbolic_temporary_field": g_tmp},
+                                                sub_iterations=1,
+                                                symbolic_fields={"symbolic_field": g,
+                                                                 "symbolic_temporary_field": g_tmp},
                                                 kernel_type='collide_stream_push')
 
 # streaming of the hydrodynamic distribution
@@ -136,7 +137,7 @@ stream_hydro = create_lb_update_rule(stencil=stencil_hydro,
 # GENERATE SWEEPS #
 ###################
 
-cpu_vec = {'instruction_set': 'sse', 'assume_inner_stride_one': True, 'nontemporal': True}
+cpu_vec = {'assume_inner_stride_one': True, 'nontemporal': True}
 
 vp = [('int32_t', 'cudaBlockSize0'),
       ('int32_t', 'cudaBlockSize1')]
@@ -152,7 +153,7 @@ info_header = f"""
 """
 
 with CodeGeneration() as ctx:
-    if ctx.cuda is False:
+    if not ctx.cuda:
         if not ctx.optimize_for_localhost:
             cpu_vec = {'instruction_set': None}
 
diff --git a/apps/benchmarks/PhaseFieldAllenCahn/profiling.py b/apps/benchmarks/PhaseFieldAllenCahn/profiling.py
index 71cdba3f358ff07ac76f0bcfd7550762de7e5be7..f8e6bed300cb1ce5e04d1ae46915bf926d90c809 100644
--- a/apps/benchmarks/PhaseFieldAllenCahn/profiling.py
+++ b/apps/benchmarks/PhaseFieldAllenCahn/profiling.py
@@ -27,8 +27,6 @@ class Scenario:
         self.scenario = 1  # 1 rising bubble, 2 RTI
         self.config_dict = self.config()
 
-        self.csv_file = "benchmark.csv"
-
     @wlb.member_callback
     def config(self):
         return {
diff --git a/apps/benchmarks/PoiseuilleChannel/PoiseuilleChannel.cpp b/apps/benchmarks/PoiseuilleChannel/PoiseuilleChannel.cpp
index b72efbdcbfb90a2502523e3fff73122f36f2f65c..9542fad39b7963d76d4c6a7f1a5280ea0ef2dd04 100644
--- a/apps/benchmarks/PoiseuilleChannel/PoiseuilleChannel.cpp
+++ b/apps/benchmarks/PoiseuilleChannel/PoiseuilleChannel.cpp
@@ -116,15 +116,15 @@ using walberla::real_t;
 // TYPEDEFS //
 //////////////
 
-typedef lbm::D3Q19< lbm::collision_model::SRT, false, lbm::force_model::SimpleConstant > D3Q19_SRT_INCOMP;
-typedef lbm::D3Q19< lbm::collision_model::SRT, true,  lbm::force_model::SimpleConstant > D3Q19_SRT_COMP;
-typedef lbm::D3Q19< lbm::collision_model::TRT, false, lbm::force_model::SimpleConstant > D3Q19_TRT_INCOMP;
+using D3Q19_SRT_INCOMP = lbm::D3Q19<lbm::collision_model::SRT, false, lbm::force_model::SimpleConstant>;
+using D3Q19_SRT_COMP = lbm::D3Q19<lbm::collision_model::SRT, true, lbm::force_model::SimpleConstant>;
+using D3Q19_TRT_INCOMP = lbm::D3Q19<lbm::collision_model::TRT, false, lbm::force_model::SimpleConstant>;
 //typedef lbm::D3Q19< lbm::collision_model::TRT, true,  lbm::force_model::SimpleConstant > D3Q19_TRT_COMP;
 
-typedef lbm::D3Q27< lbm::collision_model::SRT, false, lbm::force_model::SimpleConstant > D3Q27_SRT_INCOMP;
-typedef lbm::D3Q27< lbm::collision_model::SRT, true,  lbm::force_model::SimpleConstant > D3Q27_SRT_COMP;
-typedef lbm::D3Q27< lbm::collision_model::TRT, false, lbm::force_model::SimpleConstant > D3Q27_TRT_INCOMP;
-typedef lbm::D3Q27< lbm::collision_model::TRT, true,  lbm::force_model::SimpleConstant > D3Q27_TRT_COMP;
+using D3Q27_SRT_INCOMP = lbm::D3Q27<lbm::collision_model::SRT, false, lbm::force_model::SimpleConstant>;
+using D3Q27_SRT_COMP = lbm::D3Q27<lbm::collision_model::SRT, true, lbm::force_model::SimpleConstant>;
+using D3Q27_TRT_INCOMP = lbm::D3Q27<lbm::collision_model::TRT, false, lbm::force_model::SimpleConstant>;
+using D3Q27_TRT_COMP = lbm::D3Q27<lbm::collision_model::TRT, true, lbm::force_model::SimpleConstant>;
 
 template< typename LatticeModel_T >
 struct Types
@@ -265,9 +265,7 @@ private:
       {
          const auto d = block.getAABB().sqSignedDistance( forest.getDomain().center() );
          const real_t r = setup_.radius_L + bufferDistance_;
-         if( d > (r * r) )
-            return true;
-         return false;
+         return d > (r * r);
       }
 
       return false;
@@ -455,10 +453,10 @@ class MyBoundaryHandling
 {
 public:
 
-   typedef lbm::NoSlip< LatticeModel_T, flag_t >      NoSlip_T;
-   typedef lbm::Curved< LatticeModel_T, FlagField_T > Curved_T;
+   using NoSlip_T = lbm::NoSlip<LatticeModel_T, flag_t>;
+   using Curved_T = lbm::Curved<LatticeModel_T, FlagField_T>;
 
-   typedef BoundaryHandling< FlagField_T, typename Types<LatticeModel_T>::Stencil_T, NoSlip_T, Curved_T > BoundaryHandling_T;
+   using BoundaryHandling_T = BoundaryHandling<FlagField_T, typename Types<LatticeModel_T>::Stencil_T, NoSlip_T, Curved_T>;
 
 
 
diff --git a/apps/benchmarks/SchaeferTurek/SchaeferTurek.cpp b/apps/benchmarks/SchaeferTurek/SchaeferTurek.cpp
index 50c3a9244da51ae3fa5dfec97d5b928ca8f56ced..cdbd64fbe079aea41ae4a75927230b77324431c0 100644
--- a/apps/benchmarks/SchaeferTurek/SchaeferTurek.cpp
+++ b/apps/benchmarks/SchaeferTurek/SchaeferTurek.cpp
@@ -133,26 +133,26 @@ using walberla::real_t;
 // TYPEDEFS //
 //////////////
 
-typedef lbm::D2Q9< lbm::collision_model::SRT,       false > D2Q9_SRT_INCOMP;
-typedef lbm::D2Q9< lbm::collision_model::SRT,       true  > D2Q9_SRT_COMP;
-typedef lbm::D2Q9< lbm::collision_model::TRT,       false > D2Q9_TRT_INCOMP;
-typedef lbm::D2Q9< lbm::collision_model::TRT,       true  > D2Q9_TRT_COMP;
-
-typedef lbm::D3Q15< lbm::collision_model::SRT,      false > D3Q15_SRT_INCOMP;
-typedef lbm::D3Q15< lbm::collision_model::SRT,      true  > D3Q15_SRT_COMP;
-typedef lbm::D3Q15< lbm::collision_model::TRT,      false > D3Q15_TRT_INCOMP;
-typedef lbm::D3Q15< lbm::collision_model::TRT,      true  > D3Q15_TRT_COMP;
-
-typedef lbm::D3Q19< lbm::collision_model::SRT,      false > D3Q19_SRT_INCOMP;
-typedef lbm::D3Q19< lbm::collision_model::SRT,      true  > D3Q19_SRT_COMP;
-typedef lbm::D3Q19< lbm::collision_model::TRT,      false > D3Q19_TRT_INCOMP;
-typedef lbm::D3Q19< lbm::collision_model::TRT,      true  > D3Q19_TRT_COMP;
-typedef lbm::D3Q19< lbm::collision_model::D3Q19MRT, false > D3Q19_MRT_INCOMP;
-
-typedef lbm::D3Q27< lbm::collision_model::SRT,      false > D3Q27_SRT_INCOMP;
-typedef lbm::D3Q27< lbm::collision_model::SRT,      true  > D3Q27_SRT_COMP;
-typedef lbm::D3Q27< lbm::collision_model::TRT,      false > D3Q27_TRT_INCOMP;
-typedef lbm::D3Q27< lbm::collision_model::TRT,      true  > D3Q27_TRT_COMP;
+using D2Q9_SRT_INCOMP = lbm::D2Q9<lbm::collision_model::SRT, false>;
+using D2Q9_SRT_COMP = lbm::D2Q9<lbm::collision_model::SRT, true>;
+using D2Q9_TRT_INCOMP = lbm::D2Q9<lbm::collision_model::TRT, false>;
+using D2Q9_TRT_COMP = lbm::D2Q9<lbm::collision_model::TRT, true>;
+
+using D3Q15_SRT_INCOMP = lbm::D3Q15<lbm::collision_model::SRT, false>;
+using D3Q15_SRT_COMP = lbm::D3Q15<lbm::collision_model::SRT, true>;
+using D3Q15_TRT_INCOMP = lbm::D3Q15<lbm::collision_model::TRT, false>;
+using D3Q15_TRT_COMP = lbm::D3Q15<lbm::collision_model::TRT, true>;
+
+using D3Q19_SRT_INCOMP = lbm::D3Q19<lbm::collision_model::SRT, false>;
+using D3Q19_SRT_COMP = lbm::D3Q19<lbm::collision_model::SRT, true>;
+using D3Q19_TRT_INCOMP = lbm::D3Q19<lbm::collision_model::TRT, false>;
+using D3Q19_TRT_COMP = lbm::D3Q19<lbm::collision_model::TRT, true>;
+using D3Q19_MRT_INCOMP = lbm::D3Q19<lbm::collision_model::D3Q19MRT, false>;
+
+using D3Q27_SRT_INCOMP = lbm::D3Q27<lbm::collision_model::SRT, false>;
+using D3Q27_SRT_COMP = lbm::D3Q27<lbm::collision_model::SRT, true>;
+using D3Q27_TRT_INCOMP = lbm::D3Q27<lbm::collision_model::TRT, false>;
+using D3Q27_TRT_COMP = lbm::D3Q27<lbm::collision_model::TRT, true>;
 
 template< typename LatticeModel_T >
 struct Types
@@ -776,16 +776,15 @@ private:
 template< typename LatticeModel_T >
 struct MyBoundaryTypes
 {
-   typedef lbm::NoSlip< LatticeModel_T, flag_t >                                  NoSlip_T;
-   typedef lbm::NoSlip< LatticeModel_T, flag_t >                                  Obstacle_T;
-   typedef lbm::Curved< LatticeModel_T, FlagField_T >                             Curved_T;
-   typedef lbm::DynamicUBB< LatticeModel_T, flag_t,
-                            SinusInflowVelocity<Is2D< LatticeModel_T >::value> >  DynamicUBB_T;
-   typedef lbm::Outlet< LatticeModel_T, FlagField_T, 2, 1 >                       Outlet21_T;
-   typedef lbm::Outlet< LatticeModel_T, FlagField_T, 4, 3 >                       Outlet43_T;
-   typedef lbm::SimplePressure< LatticeModel_T, flag_t >                          PressureOutlet_T;
-
-   typedef BoundaryHandling< FlagField_T, typename Types<LatticeModel_T>::Stencil_T, NoSlip_T, Obstacle_T, Curved_T, DynamicUBB_T, Outlet21_T, Outlet43_T, PressureOutlet_T > BoundaryHandling_T;
+   using NoSlip_T = lbm::NoSlip<LatticeModel_T, flag_t>;
+   using Obstacle_T = lbm::NoSlip<LatticeModel_T, flag_t>;
+   using Curved_T = lbm::Curved<LatticeModel_T, FlagField_T>;
+   using DynamicUBB_T = lbm::DynamicUBB<LatticeModel_T, flag_t, SinusInflowVelocity<Is2D<LatticeModel_T>::value>>;
+   using Outlet21_T = lbm::Outlet<LatticeModel_T, FlagField_T, 2, 1>;
+   using Outlet43_T = lbm::Outlet<LatticeModel_T, FlagField_T, 4, 3>;
+   using PressureOutlet_T = lbm::SimplePressure<LatticeModel_T, flag_t>;
+
+   using BoundaryHandling_T = BoundaryHandling<FlagField_T, typename Types<LatticeModel_T>::Stencil_T, NoSlip_T, Obstacle_T, Curved_T, DynamicUBB_T, Outlet21_T, Outlet43_T, PressureOutlet_T>;
 };
 
 
@@ -1318,9 +1317,9 @@ public:
                     "cD (real area) [5], cL (real area) [6], cD (discrete area) [7], cL (discrete area) [8], "
                     "pressure difference (in lattice units) [9], pressure difference (in Pa) [10], vortex velocity (in lattice units) [11], "
                     "Strouhal number (real D) [12], Strouhal number (discrete D) [13]" << std::endl;
-            if( setup_.evaluatePressure == false )
+            if( !setup_.evaluatePressure )
                file << "# ATTENTION: pressure was not evaluated, pressure difference is set to zero!" << std::endl;
-            if( setup_.evaluateStrouhal == false )
+            if( !setup_.evaluateStrouhal )
                file << "# ATTENTION: vortex velocities were not evaluated, Strouhal number is set to zero!" << std::endl;
             file.close();
          }
@@ -2285,7 +2284,7 @@ struct AddRefinementTimeStep
          }
          else
          {
-            typedef lbm::SplitSweep< LatticeModel_T, FlagField_T > Sweep_T;
+            using Sweep_T = lbm::SplitSweep<LatticeModel_T, FlagField_T>;
             auto mySweep = make_shared< Sweep_T >( pdfFieldId, flagFieldId, Fluid_Flag );
 
             addRefinementTimeStep< LatticeModel_T, Sweep_T >( timeloop, blocks, pdfFieldId, boundaryHandlingId, timingPool, levelwiseTimingPool,
@@ -2365,10 +2364,10 @@ void run( const shared_ptr< Config > & config, const LatticeModel_T & latticeMod
    
    // add velocity field + initialize velocity field writer (only used for simulations with an adaptive block structure)
 
-   typedef field::GhostLayerField< Vector3<real_t>, 1 > VelocityField_T;
+   using VelocityField_T = field::GhostLayerField<Vector3<real_t>, 1>;
    BlockDataID velocityFieldId = field::addToStorage< VelocityField_T >( blocks, "velocity", Vector3<real_t>(0), field::zyxf, FieldGhostLayers, true, None, Empty );
 
-   typedef lbm::VelocityFieldWriter< typename Types<LatticeModel_T>::PdfField_T, VelocityField_T > VelocityFieldWriter_T;
+   using VelocityFieldWriter_T = lbm::VelocityFieldWriter<typename Types<LatticeModel_T>::PdfField_T, VelocityField_T>;
    BlockSweepWrapper< VelocityFieldWriter_T > velocityFieldWriter( blocks, VelocityFieldWriter_T( pdfFieldId, velocityFieldId ), None, Empty );
    velocityFieldWriter();
 
diff --git a/apps/benchmarks/UniformGrid/UniformGrid.cpp b/apps/benchmarks/UniformGrid/UniformGrid.cpp
index d5d355e491812c639ac1b4fca5a982622c19af5e..0a947d20f060c3f8f93ee92d45cbe4298143b635 100644
--- a/apps/benchmarks/UniformGrid/UniformGrid.cpp
+++ b/apps/benchmarks/UniformGrid/UniformGrid.cpp
@@ -101,12 +101,12 @@ using walberla::real_t;
 // TYPEDEFS //
 //////////////
 
-typedef lbm::D3Q19< lbm::collision_model::SRT,           false > D3Q19_SRT_INCOMP;
-typedef lbm::D3Q19< lbm::collision_model::SRT,           true  > D3Q19_SRT_COMP;
-typedef lbm::D3Q19< lbm::collision_model::TRT,           false > D3Q19_TRT_INCOMP;
-typedef lbm::D3Q19< lbm::collision_model::TRT,           true  > D3Q19_TRT_COMP;
-typedef lbm::D3Q19< lbm::collision_model::D3Q19MRT,      false > D3Q19_MRT_INCOMP;
-typedef lbm::D3Q27< lbm::collision_model::D3Q27Cumulant, true  > D3Q27_CUMULANT_COMP;
+using D3Q19_SRT_INCOMP = lbm::D3Q19<lbm::collision_model::SRT, false>;
+using D3Q19_SRT_COMP = lbm::D3Q19<lbm::collision_model::SRT, true>;
+using D3Q19_TRT_INCOMP = lbm::D3Q19<lbm::collision_model::TRT, false>;
+using D3Q19_TRT_COMP = lbm::D3Q19<lbm::collision_model::TRT, true>;
+using D3Q19_MRT_INCOMP = lbm::D3Q19<lbm::collision_model::D3Q19MRT, false>;
+using D3Q27_CUMULANT_COMP = lbm::D3Q27<lbm::collision_model::D3Q27Cumulant, true>;
 
 
 template< typename LatticeModel_T >
@@ -347,10 +347,10 @@ class MyBoundaryHandling
 {
 public:
 
-   typedef lbm::NoSlip< LatticeModel_T, flag_t >    NoSlip_T;
-   typedef lbm::SimpleUBB< LatticeModel_T, flag_t > UBB_T;
+   using NoSlip_T = lbm::NoSlip<LatticeModel_T, flag_t>;
+   using UBB_T = lbm::SimpleUBB<LatticeModel_T, flag_t>;
 
-   typedef BoundaryHandling< FlagField_T, typename Types<LatticeModel_T>::Stencil_T, NoSlip_T, UBB_T > BoundaryHandling_T;
+   using BoundaryHandling_T = BoundaryHandling<FlagField_T, typename Types<LatticeModel_T>::Stencil_T, NoSlip_T, UBB_T>;
 
 
 
@@ -565,7 +565,7 @@ struct AddLB
             }
             else
             {
-               typedef lbm::SplitSweep< LatticeModel_T, FlagField_T > Sweep_T;
+               using Sweep_T = lbm::SplitSweep<LatticeModel_T, FlagField_T>;
                auto sweep = make_shared< Sweep_T >( pdfFieldId, flagFieldId, Fluid_Flag );
 
                timeloop.add() << Sweep( lbm::CollideSweep< Sweep_T >( sweep ), "split LB sweep (collide)" );
diff --git a/apps/benchmarks/UniformGridGPU/CMakeLists.txt b/apps/benchmarks/UniformGridGPU/CMakeLists.txt
index 4a639063329796c8d7e4732344e49e4316d8244e..29755644d22f0542ac29b50e848f20dd6a2c9974 100644
--- a/apps/benchmarks/UniformGridGPU/CMakeLists.txt
+++ b/apps/benchmarks/UniformGridGPU/CMakeLists.txt
@@ -1,6 +1,7 @@
 
 waLBerla_link_files_to_builddir( "*.prm" )
-#waLBerla_link_files_to_builddir( "simulation_setup" )
+waLBerla_link_files_to_builddir( "*.py" )
+waLBerla_link_files_to_builddir( "simulation_setup" )
 
 
 foreach (config srt trt mrt smagorinsky entropic smagorinsky_noopt entropic_kbc_n4
@@ -25,6 +26,7 @@ foreach (config srt trt mrt smagorinsky entropic smagorinsky_noopt entropic_kbc_
     waLBerla_add_executable(NAME UniformGridBenchmarkGPU_${config}
           FILES UniformGridGPU.cpp
           DEPENDS blockforest boundary core cuda domain_decomposition field geometry timeloop vtk gui UniformGridGPUGenerated_${config})
+    set_target_properties( UniformGridBenchmarkGPU_${config} PROPERTIES CXX_VISIBILITY_PRESET hidden)
 endforeach ()
 
 
@@ -46,4 +48,5 @@ foreach (config srt trt mrt smagorinsky entropic)
     waLBerla_add_executable(NAME UniformGridBenchmarkGPU_AA_${config}
           FILES UniformGridGPU_AA.cpp
           DEPENDS blockforest boundary core cuda domain_decomposition field geometry timeloop vtk gui UniformGridGPUGenerated_AA_${config})
+    set_target_properties( UniformGridBenchmarkGPU_AA_${config} PROPERTIES CXX_VISIBILITY_PRESET hidden)
 endforeach ()
diff --git a/apps/benchmarks/UniformGridGPU/UniformGridGPU.py b/apps/benchmarks/UniformGridGPU/UniformGridGPU.py
index c1371278a713e88e186b28346c4c314813dd6347..20ea3f0f702d4bda7eaf6bf92e9fbcbafab189a0 100644
--- a/apps/benchmarks/UniformGridGPU/UniformGridGPU.py
+++ b/apps/benchmarks/UniformGridGPU/UniformGridGPU.py
@@ -69,11 +69,10 @@ options_dict = {
         'relaxation_rate': omega,
     },
     'cumulant': {
+        'method': 'cumulant',
         'stencil': 'D3Q19',
         'compressible': True,
-        'method': 'mrt',
-        'cumulant': True,
-        'relaxation_rates': [0, omega, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+        'relaxation_rate': omega,
     },
 }
 
@@ -158,7 +157,7 @@ with CodeGeneration() as ctx:
 
     # getter & setter
     setter_assignments = macroscopic_values_setter(lb_method, velocity=velocity_field.center_vector,
-                                                   pdfs=pdfs.center_vector, density=1)
+                                                   pdfs=pdfs.center_vector, density=1.0)
     getter_assignments = macroscopic_values_getter(lb_method, velocity=velocity_field.center_vector,
                                                    pdfs=pdfs.center_vector, density=None)
     generate_sweep(ctx, 'UniformGridGPU_MacroSetter', setter_assignments)
diff --git a/apps/benchmarks/UniformGridGPU/UniformGridGPU_AA.py b/apps/benchmarks/UniformGridGPU/UniformGridGPU_AA.py
index 1fb6261d34a92936a1b3f277898b78b30602ba02..c7e6341ae6d9a125e09427dec23f044e212709f8 100644
--- a/apps/benchmarks/UniformGridGPU/UniformGridGPU_AA.py
+++ b/apps/benchmarks/UniformGridGPU/UniformGridGPU_AA.py
@@ -106,7 +106,7 @@ with CodeGeneration() as ctx:
 
     # getter & setter
     setter_assignments = macroscopic_values_setter(lb_method, velocity=velocity_field.center_vector,
-                                                   pdfs=pdfs.center_vector, density=1)
+                                                   pdfs=pdfs.center_vector, density=1.0)
     getter_assignments = macroscopic_values_getter(lb_method, velocity=velocity_field.center_vector,
                                                    pdfs=pdfs.center_vector, density=None)
     generate_sweep(ctx, 'UniformGridGPU_AA_MacroSetter', setter_assignments)
diff --git a/apps/benchmarks/UniformGridGPU/UniformGridGPU_Communication.h b/apps/benchmarks/UniformGridGPU/UniformGridGPU_Communication.h
index db0ec86e6cc1f58c4548a57023ac8ce49d69478f..aadf51331d507ef9bb12f3fc010606f18b599491 100644
--- a/apps/benchmarks/UniformGridGPU/UniformGridGPU_Communication.h
+++ b/apps/benchmarks/UniformGridGPU/UniformGridGPU_Communication.h
@@ -30,7 +30,7 @@ template<typename StencilType, typename GPUFieldType >
 class UniformGridGPU_Communication
 {
 public:
-    explicit UniformGridGPU_Communication(weak_ptr_wrapper<StructuredBlockForest> bf, const BlockDataID & bdId,
+    explicit UniformGridGPU_Communication(weak_ptr<StructuredBlockForest> bf, const BlockDataID & bdId,
                                           CommunicationSchemeType commSchemeType, bool cudaEnabledMPI = false)
         : _commSchemeType(commSchemeType), _cpuCommunicationScheme(nullptr), _gpuPackInfo(nullptr),
           _gpuCommunicationScheme(nullptr), _directScheme(nullptr)
diff --git a/apps/benchmarks/UniformGridGenerated/CMakeLists.txt b/apps/benchmarks/UniformGridGenerated/CMakeLists.txt
index ee60a84f5bfc253282e9440a1376f0ef7a43bb94..f964f242a7967c4440643aee9ed7769d18c7ebbd 100644
--- a/apps/benchmarks/UniformGridGenerated/CMakeLists.txt
+++ b/apps/benchmarks/UniformGridGenerated/CMakeLists.txt
@@ -15,6 +15,7 @@ foreach(config trt smagorinsky mrt entropic_kbc_n4 cumulant )
     waLBerla_add_executable ( NAME UniformGridBenchmarkGenerated_${config}
           FILES UniformGridGenerated.cpp
           DEPENDS blockforest boundary core domain_decomposition field geometry timeloop vtk gui
-          UniformGridGenerated_${config})
+          UniformGridGenerated_${config} python_coupling)
+    set_target_properties(UniformGridBenchmarkGenerated_${config} PROPERTIES CXX_VISIBILITY_PRESET hidden)
 
 endforeach()
diff --git a/apps/benchmarks/UniformGridGenerated/UniformGridGenerated.py b/apps/benchmarks/UniformGridGenerated/UniformGridGenerated.py
index b9ca24ba16f406ef37e5f51349068878295edbc3..d42452222bf78d4fd2d9b06aa21e3fa8f69bfa13 100644
--- a/apps/benchmarks/UniformGridGenerated/UniformGridGenerated.py
+++ b/apps/benchmarks/UniformGridGenerated/UniformGridGenerated.py
@@ -26,7 +26,7 @@ options_dict = {
     'mrt': {
         'method': 'mrt',
         'stencil': 'D3Q19',
-        'relaxation_rates': [0, omega, 1.3, 1.4, omega, 1.2, 1.1, 1.15, 1.234, 1.4235, 1.242, 1.2567, 0.9, 0.7],
+        'relaxation_rates': [0.0, omega, 1.3, 1.4, omega, 1.2, 1.1, 1.15, 1.234, 1.4235, 1.242, 1.2567, 0.9, 0.7],
     },
     'mrt_full': {
         'method': 'mrt',
@@ -55,11 +55,10 @@ options_dict = {
         'relaxation_rate': omega,
     },
     'cumulant': {
+        'method': 'cumulant',
         'stencil': 'D3Q19',
         'compressible': True,
-        'method': 'mrt',
-        'cumulant': True,
-        'relaxation_rates': [0, omega, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+        'relaxation_rate': omega,
     },
 }
 
@@ -172,7 +171,7 @@ with CodeGeneration() as ctx:
                    cpu_openmp=openmp_enabled, ghost_layers=1)
 
     setter_assignments = macroscopic_values_setter(update_rule_two_field.method, velocity=velocity_field.center_vector,
-                                                   pdfs=pdfs.center_vector, density=1)
+                                                   pdfs=pdfs.center_vector, density=1.0)
     getter_assignments = macroscopic_values_getter(update_rule_two_field.method, velocity=velocity_field.center_vector,
                                                    pdfs=pdfs.center_vector, density=None)
     generate_sweep(ctx, 'GenMacroSetter', setter_assignments, cpu_openmp=openmp_enabled)
diff --git a/apps/pythonmodule/CMakeLists.txt b/apps/pythonmodule/CMakeLists.txt
index 820784e2b07eff267fd275b8ae36dd60341f43c3..38290ffa55a94e9e4effa3502dcfde4d328e2622 100644
--- a/apps/pythonmodule/CMakeLists.txt
+++ b/apps/pythonmodule/CMakeLists.txt
@@ -1,43 +1,34 @@
 # waLBerla Python module
 
 
-if ( WALBERLA_BUILD_WITH_PYTHON_MODULE )
-
-    set(PYTHON_MODULE_DEPENDENCIES blockforest boundary domain_decomposition core field geometry lbm postprocessing python_coupling timeloop vtk)
-    if (WALBERLA_BUILD_WITH_CUDA)
-        set(PYTHON_MODULE_DEPENDENCIES ${PYTHON_MODULE_DEPENDENCIES} cuda)
-    endif()
-
-    if (WALBERLA_BUILD_WITH_OPENMESH)
-        set(PYTHON_MODULE_DEPENDENCIES ${PYTHON_MODULE_DEPENDENCIES} mesh pe)
+if ( WALBERLA_BUILD_WITH_PYTHON )
+    if ( WALBERLA_BUILD_WITH_CUDA )
+        set(PYTHON_MODULE_DEPENDENCIES blockforest boundary domain_decomposition core field python_coupling timeloop vtk cuda)
+    else()
+        set(PYTHON_MODULE_DEPENDENCIES blockforest boundary domain_decomposition core field python_coupling timeloop vtk)
     endif()
 
     if( WALBERLA_CXX_COMPILER_IS_MSVC )
-       set ( pythonModules ${PYTHON_MODULE_DEPENDENCIES})
+        set ( pythonModules ${PYTHON_MODULE_DEPENDENCIES})
     elseif( APPLE )
-       set ( pythonModules "-Wl,-force_load" ${PYTHON_MODULE_DEPENDENCIES})
+        set ( pythonModules "-Wl,-force_load" ${PYTHON_MODULE_DEPENDENCIES})
     else()
-       set ( pythonModules "-Wl,-whole-archive" ${PYTHON_MODULE_DEPENDENCIES}  "-Wl,-no-whole-archive" )
+        set ( pythonModules "-Wl,-whole-archive" ${PYTHON_MODULE_DEPENDENCIES}  "-Wl,-no-whole-archive" )
     endif()
 
-    if( WALBERLA_BUILD_WITH_PYTHON_LBM )
-        add_library( walberla_cpp SHARED PythonModuleWithLbmModule.cpp )
-    else()
-        add_library( walberla_cpp SHARED PythonModule.cpp )
-    endif()
+    add_library( walberla_cpp SHARED PythonModule.cpp )
 
-    target_link_libraries( walberla_cpp ${WALBERLA_LINK_LIBRARIES_KEYWORD} ${pythonModules} ${SERVICE_LIBS}  )
-    set_target_properties( walberla_cpp PROPERTIES PREFIX "")
-    if ( APPLE )
-       set_target_properties( walberla_cpp PROPERTIES SUFFIX ".so")
-    endif()
+    target_link_libraries( walberla_cpp ${WALBERLA_LINK_LIBRARIES_KEYWORD} ${pythonModules} ${SERVICE_LIBS} )
+
+    set_target_properties(
+            walberla_cpp PROPERTIES PREFIX "${PYTHON_MODULE_PREFIX}"
+            SUFFIX "${PYTHON_MODULE_EXTENSION}"
+    )
     set_target_properties( walberla_cpp PROPERTIES MACOSX_RPATH TRUE )
+    set_target_properties( walberla_cpp PROPERTIES CXX_VISIBILITY_PRESET hidden)
 
     configure_file( ${CMAKE_CURRENT_SOURCE_DIR}/setup.py  ${CMAKE_CURRENT_BINARY_DIR}/setup.py )
 
     add_custom_target( pythonModule        ALL ${PYTHON_EXECUTABLE} setup.py build   DEPENDS walberla_cpp )
-    add_custom_target( pythonModuleInstall     ${PYTHON_EXECUTABLE} setup.py install DEPENDS walberla_cpp )
-
-    add_test( NAME PythonModuleTest
-              COMMAND ${PYTHON_EXECUTABLE} -m unittest discover -v -s ${walberla_SOURCE_DIR}/python/waLBerla_tests )
+    add_custom_target( pythonModuleInstall     ${PYTHON_EXECUTABLE} setup.py install --user DEPENDS walberla_cpp )
 endif()
diff --git a/apps/pythonmodule/PythonModule.cpp b/apps/pythonmodule/PythonModule.cpp
index 43afcff724a02aca9babb38e7eabc7db56e820bf..3059e3f059e2fa110dda1681859693cb6616fc44 100644
--- a/apps/pythonmodule/PythonModule.cpp
+++ b/apps/pythonmodule/PythonModule.cpp
@@ -15,125 +15,76 @@
 //
 //! \file PythonModule.cpp
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
-#include "python_coupling/PythonWrapper.h"
-#include "waLBerlaDefinitions.h"
-#include "blockforest/python/Exports.h"
 #include "field/GhostLayerField.h"
-#include "field/python/Exports.h"
-#include "mesh/python/Exports.h"
-#include "geometry/python/Exports.h"
-#include "postprocessing/python/Exports.h"
+
 #include "python_coupling/Manager.h"
+#include "python_coupling/export/BlockForestExport.h"
+#include "python_coupling/export/FieldExports.h"
+#include "python_coupling/export/VTKExport.h"
 #include "python_coupling/helper/ModuleInit.h"
+
 #include "stencil/all.h"
-#include "timeloop/python/Exports.h"
-#include "vtk/python/Exports.h"
 
 #ifdef WALBERLA_BUILD_WITH_CUDA
-#include "cuda/python/Exports.h"
+ #include "python_coupling/export/CUDAExport.h"
 #endif
 
-#include <boost/mpl/vector.hpp>
-#include <boost/mpl/insert_range.hpp>
-
-
 
-namespace bmpl = boost::mpl;
 using namespace walberla;
 
-typedef bmpl::vector<
-            Field<walberla::real_t,1>,
-            Field<walberla::real_t,2>,
-            Field<walberla::real_t,3>,
-            Field<walberla::real_t,4>,
-            Field<walberla::real_t,5>,
-            Field<walberla::real_t,6>,
-            Field<walberla::real_t,9>,
-            Field<walberla::real_t,15>,
-            Field<walberla::real_t,19>,
-            Field<walberla::real_t,27>,
-
-            Field<walberla::int8_t,1>,
-            Field<walberla::int16_t,1>,
-            Field<walberla::int32_t,1>,
-
-            Field<walberla::int64_t,1>,
-            Field<walberla::int64_t,2>,
-            Field<walberla::int64_t,3>,
-            Field<walberla::int64_t,4>,
-
-            Field<walberla::uint8_t,1>,
-            Field<walberla::uint16_t,1>,
-            Field<walberla::uint32_t,1>
-      > FieldTypes;
-
 
-typedef bmpl::vector<
-                      GhostLayerField<walberla::real_t,1>,
-                      GhostLayerField<walberla::real_t,3>
-                    >  FieldTypesForMeshGeneration;
-
-
-typedef bmpl::vector< FlagField<walberla::uint8_t>,
-                      FlagField<walberla::uint16_t> > FlagFieldTypes;
-
-typedef bmpl::vector< stencil::D2Q5,
-                      stencil::D2Q9,
-                      stencil::D3Q7,
-                      stencil::D3Q19,
-                      stencil::D3Q27 > Stencils;
-
-typedef GhostLayerField<walberla::real_t,3> VecField_T;
-
-
-using namespace walberla;
+#define FIELD_TYPES \
+   Field<walberla::real_t,1>,\
+   Field<walberla::real_t,2>,\
+   Field<walberla::real_t,3>,\
+   Field<walberla::real_t,4>,\
+   Field<walberla::real_t,9>,\
+   Field<walberla::real_t,15>,\
+   Field<walberla::real_t,19>,\
+   Field<walberla::real_t,27>,\
+   Field<walberla::int8_t,1>,\
+   Field<walberla::int32_t,1>,\
+   Field<walberla::int64_t,1>,\
+   Field<walberla::int64_t,2>,\
+   Field<walberla::int64_t,3>,\
+   Field<walberla::uint8_t,1>,\
+   Field<walberla::uint16_t,1>,\
+   Field<walberla::uint32_t,1>
+
+#define GPU_FIELD_TYPES \
+   GPUField<real_t>,\
+   GPUField<int8_t>,\
+   GPUField<int32_t>,\
+   GPUField<int64_t>,\
+   GPUField<uint8_t>,\
+   GPUField<uint64_t>
 
 struct InitObject
 {
    InitObject()
    {
-      namespace bmpl = boost::mpl;
-
       auto pythonManager = python_coupling::Manager::instance();
-
       // Field
-      pythonManager->addExporterFunction( field::exportModuleToPython<FieldTypes> );
-      pythonManager->addExporterFunction( field::exportGatherFunctions<FieldTypes> );
-      pythonManager->addBlockDataConversion<FieldTypes>() ;
-
+      pythonManager->addExporterFunction( field::exportModuleToPython<FIELD_TYPES> );
+      pythonManager->addExporterFunction( field::exportGatherFunctions<FIELD_TYPES> );
+      pythonManager->addBlockDataConversion<FIELD_TYPES>();
       // Blockforest
-      pythonManager->addExporterFunction( blockforest::exportModuleToPython<Stencils> );
-
-      // Geometry
-      pythonManager->addExporterFunction( geometry::exportModuleToPython );
-
+      pythonManager->addExporterFunction(blockforest::exportModuleToPython<stencil::D2Q5, stencil::D2Q9, stencil::D3Q7, stencil::D3Q19, stencil::D3Q27>);
       // VTK
       pythonManager->addExporterFunction( vtk::exportModuleToPython );
-
-      // Postprocessing
-      pythonManager->addExporterFunction( postprocessing::exportModuleToPython<FieldTypesForMeshGeneration, FlagFieldTypes> );
-
-      // Timeloop
-      pythonManager->addExporterFunction( timeloop::exportModuleToPython );
-
-#ifdef WALBERLA_BUILD_WITH_OPENMESH
-      pythonManager->addExporterFunction( mesh::exportModuleToPython<FlagFieldTypes> );
-#endif
-
-#ifdef WALBERLA_BUILD_WITH_CUDA
-      using walberla::cuda::GPUField;
-      typedef bmpl::vector<GPUField<double>, GPUField<float>,
-                           GPUField<int8_t>,  GPUField<int16_t>,  GPUField<int32_t>, GPUField<int64_t>,
-                           GPUField<uint8_t>, GPUField<uint16_t>, GPUField<uint32_t>,GPUField<uint64_t> > GPUFields;
-
-      pythonManager->addExporterFunction( cuda::exportModuleToPython<GPUFields, FieldTypes> );
-      pythonManager->addBlockDataConversion<GPUFields>();
-#endif
-
+      #ifdef WALBERLA_BUILD_WITH_CUDA
+            using walberla::cuda::GPUField;
+
+            pythonManager->addExporterFunction( cuda::exportModuleToPython<GPU_FIELD_TYPES> );
+            pythonManager->addExporterFunction( cuda::exportCopyFunctionsToPython<FIELD_TYPES> );
+            pythonManager->addBlockDataConversion<GPU_FIELD_TYPES>();
+      #endif
+      //
       python_coupling::initWalberlaForPythonModule();
+
    }
 };
-InitObject globalInitObject;
-
+InitObject globalInitObject;
\ No newline at end of file
diff --git a/apps/pythonmodule/PythonModuleWithLbmModule.cpp b/apps/pythonmodule/PythonModuleWithLbmModule.cpp
deleted file mode 100644
index 03301a7233b59228c4f58758738baf62e5454148..0000000000000000000000000000000000000000
--- a/apps/pythonmodule/PythonModuleWithLbmModule.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-//======================================================================================================================
-//
-//  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 PythonModule.cpp
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-#include "python_coupling/PythonWrapper.h"
-#include "waLBerlaDefinitions.h"
-#include "blockforest/python/Exports.h"
-#include "field/GhostLayerField.h"
-#include "field/python/Exports.h"
-#include "geometry/python/Exports.h"
-#include "lbm/all.h"
-#include "lbm/lattice_model/ForceModel.h"
-#include "lbm/python/Exports.h"
-#include "postprocessing/python/Exports.h"
-#include "python_coupling/Manager.h"
-#include "python_coupling/helper/ModuleInit.h"
-#include "stencil/all.h"
-#include "timeloop/python/Exports.h"
-#include "vtk/python/Exports.h"
-
-#include <boost/mpl/vector.hpp>
-#include <boost/mpl/insert_range.hpp>
-
-
-namespace bmpl = boost::mpl;
-using namespace walberla;
-
-typedef bmpl::vector<
-            Field<walberla::real_t,1>,
-            Field<walberla::real_t,2>,
-            Field<walberla::real_t,3>,
-            Field<walberla::real_t,4>,
-            Field<walberla::real_t,5>,
-            Field<walberla::real_t,9>,
-            Field<walberla::real_t,19>,
-            Field<walberla::real_t,27>,
-
-            Field<walberla::int8_t,1>,
-            Field<walberla::int16_t,1>,
-            Field<walberla::int32_t,1>,
-
-            Field<walberla::int64_t,1>,
-            Field<walberla::int64_t,2>,
-            Field<walberla::int64_t,3>,
-            Field<walberla::int64_t,4>,
-            Field<walberla::int64_t,5>,
-
-            Field<walberla::uint8_t,1>,
-            Field<walberla::uint16_t,1>,
-            Field<walberla::uint32_t,1>
-      > FieldTypes;
-
-
-typedef bmpl::vector<
-                      GhostLayerField<walberla::real_t,1>,
-                      GhostLayerField<walberla::real_t,3>
-                    >  FieldTypesForMeshGeneration;
-
-
-typedef bmpl::vector< FlagField<walberla::uint8_t>,
-                      FlagField<walberla::uint16_t> > FlagFieldTypes;
-
-typedef bmpl::vector< stencil::D2Q5,
-                      stencil::D2Q9,
-                      stencil::D3Q7,
-                      stencil::D3Q19,
-                      stencil::D3Q27 > Stencils;
-
-typedef lbm::collision_model::SRTField< GhostLayerField<walberla::real_t,1> > SRTField_T;
-
-typedef GhostLayerField<walberla::real_t,3> VecField_T;
-
-
-// -------------------------    D2Q9 -----------------------------------------------------------------------------
-
-typedef bmpl::list< lbm::D2Q9  < lbm::collision_model::SRT, false, lbm::force_model::None, 2>,
-                    lbm::D2Q9  < lbm::collision_model::SRT, false, lbm::force_model::SimpleConstant, 2>,
-                    lbm::D2Q9  < lbm::collision_model::SRT, false, lbm::force_model::GuoConstant, 2>,
-                    lbm::D2Q9  < lbm::collision_model::SRT, false, lbm::force_model::LuoConstant, 2>,
-                    lbm::D2Q9  < lbm::collision_model::SRT, true, lbm::force_model::None, 2>,
-                    lbm::D2Q9  < lbm::collision_model::SRT, true, lbm::force_model::SimpleConstant, 2>,
-                    lbm::D2Q9  < lbm::collision_model::SRT, true, lbm::force_model::GuoConstant, 2>,
-                    lbm::D2Q9  < lbm::collision_model::SRT, false, lbm::force_model::GuoField<VecField_T>, 2>,
-                    lbm::D2Q9  < lbm::collision_model::SRT, true, lbm::force_model::LuoConstant, 2>,
-                    lbm::D2Q9  < lbm::collision_model::SRT, true, lbm::force_model::None, 1>,
-                    lbm::D2Q9  < lbm::collision_model::SRT, false, lbm::force_model::None, 1>
-> LM_D2Q19_SRT;
-
-typedef bmpl::list<   lbm::D2Q9  < lbm::collision_model::TRT, false, lbm::force_model::None, 2>,
-                      lbm::D2Q9  < lbm::collision_model::TRT, false, lbm::force_model::SimpleConstant, 2>,
-                      lbm::D2Q9  < lbm::collision_model::TRT, false, lbm::force_model::GuoConstant, 2>,
-                      lbm::D2Q9  < lbm::collision_model::TRT, false, lbm::force_model::LuoConstant, 2>,
-
-                      lbm::D2Q9  < lbm::collision_model::TRT, true, lbm::force_model::None, 2>,
-                      lbm::D2Q9  < lbm::collision_model::TRT, true, lbm::force_model::SimpleConstant, 2>,
-                      lbm::D2Q9  < lbm::collision_model::TRT, true, lbm::force_model::GuoConstant, 2>,
-                      lbm::D2Q9  < lbm::collision_model::TRT, true, lbm::force_model::LuoConstant, 2>,
-                      lbm::D2Q9  < lbm::collision_model::TRT, true, lbm::force_model::None, 1>,
-                      lbm::D2Q9  < lbm::collision_model::TRT, false, lbm::force_model::None, 1>
-> LM_D2Q19_TRT;
-
-typedef lbm::collision_model::SRTField<GhostLayerField<real_t,1> > SRTFieldCollisionModel;
-typedef bmpl::list<   lbm::D2Q9  < SRTFieldCollisionModel, false, lbm::force_model::None, 2>,
-                      lbm::D2Q9  < SRTFieldCollisionModel, false, lbm::force_model::SimpleConstant, 2>,
-                      lbm::D2Q9  < SRTFieldCollisionModel, false, lbm::force_model::GuoConstant, 2>,
-                      lbm::D2Q9  < SRTFieldCollisionModel, false, lbm::force_model::LuoConstant, 2>,
-
-                      lbm::D2Q9  < SRTFieldCollisionModel, true, lbm::force_model::None, 2>,
-                      lbm::D2Q9  < SRTFieldCollisionModel, true, lbm::force_model::SimpleConstant, 2>,
-                      lbm::D2Q9  < SRTFieldCollisionModel, true, lbm::force_model::GuoConstant, 2>,
-                      lbm::D2Q9  < SRTFieldCollisionModel, true, lbm::force_model::LuoConstant, 2>
-> LM_D2Q9_SRTField;
-
-// -------------------------    D3Q19 ----------------------------------------------------------------------------
-
-
-typedef bmpl::list<   lbm::D3Q19  < lbm::collision_model::SRT, false, lbm::force_model::None, 2>,
-                      lbm::D3Q19  < lbm::collision_model::SRT, false, lbm::force_model::SimpleConstant, 2>,
-                      lbm::D3Q19  < lbm::collision_model::SRT, false, lbm::force_model::GuoConstant, 2>,
-                      lbm::D3Q19  < lbm::collision_model::SRT, false, lbm::force_model::LuoConstant, 2>,
-
-                      lbm::D3Q19  < lbm::collision_model::SRT, true, lbm::force_model::None, 2>,
-                      lbm::D3Q19  < lbm::collision_model::SRT, true, lbm::force_model::SimpleConstant, 2>,
-                      lbm::D3Q19  < lbm::collision_model::SRT, true, lbm::force_model::GuoConstant, 2>,
-                      lbm::D3Q19  < lbm::collision_model::SRT, true, lbm::force_model::LuoConstant, 2>
-> LM_D3Q19_SRT;
-
-typedef bmpl::list<   lbm::D3Q19  < lbm::collision_model::TRT, false, lbm::force_model::None, 2>,
-                      lbm::D3Q19  < lbm::collision_model::TRT, false, lbm::force_model::SimpleConstant, 2>,
-                      lbm::D3Q19  < lbm::collision_model::TRT, false, lbm::force_model::GuoConstant, 2>,
-                      lbm::D3Q19  < lbm::collision_model::TRT, false, lbm::force_model::LuoConstant, 2>,
-
-                      lbm::D3Q19  < lbm::collision_model::TRT, true, lbm::force_model::None, 2>,
-                      lbm::D3Q19  < lbm::collision_model::TRT, true, lbm::force_model::SimpleConstant, 2>,
-                      lbm::D3Q19  < lbm::collision_model::TRT, true, lbm::force_model::GuoConstant, 2>,
-                      lbm::D3Q19  < lbm::collision_model::TRT, true, lbm::force_model::LuoConstant, 2>
-> LM_D3Q19_TRT;
-
-
-typedef bmpl::list<lbm::D3Q19 < lbm::collision_model::D3Q19MRT, false, lbm::force_model::None, 2>,
-                   lbm::D3Q19 < lbm::collision_model::D3Q19MRT, false, lbm::force_model::SimpleConstant, 2>,
-                   lbm::D3Q19 < lbm::collision_model::D3Q19MRT, false, lbm::force_model::GuoConstant, 2>,
-                   lbm::D3Q19 < lbm::collision_model::D3Q19MRT, false, lbm::force_model::LuoConstant, 2>,
-
-                   lbm::D3Q27 < lbm::collision_model::D3Q27Cumulant, true, lbm::force_model::None, 2>
-
-> LM_D3Q19_Extra;
-
-
-
-typedef lbm::collision_model::SRTField<GhostLayerField<real_t,1> > SRTFieldCollisionModel;
-typedef bmpl::list<   lbm::D3Q19  < SRTFieldCollisionModel, false, lbm::force_model::None, 2>,
-                      lbm::D3Q19  < SRTFieldCollisionModel, false, lbm::force_model::SimpleConstant, 2>,
-                      lbm::D3Q19  < SRTFieldCollisionModel, false, lbm::force_model::GuoConstant, 2>,
-                      lbm::D3Q19  < SRTFieldCollisionModel, false, lbm::force_model::LuoConstant, 2>,
-
-                      lbm::D3Q19  < SRTFieldCollisionModel, true, lbm::force_model::None, 2>,
-                      lbm::D3Q19  < SRTFieldCollisionModel, true, lbm::force_model::SimpleConstant, 2>,
-                      lbm::D3Q19  < SRTFieldCollisionModel, true, lbm::force_model::GuoConstant, 2>,
-                      lbm::D3Q19  < SRTFieldCollisionModel, true, lbm::force_model::LuoConstant, 2>
-> LM_D3Q19_SRTField;
-
-
-typedef bmpl::insert_range< LM_D2Q19_SRT, bmpl::end< LM_D2Q19_SRT >::type, LM_D2Q19_TRT>     ::type D2Q9_SRT_TRT;
-typedef bmpl::insert_range< D2Q9_SRT_TRT, bmpl::end< D2Q9_SRT_TRT >::type, LM_D2Q9_SRTField>::type LatticeModels2D;
-
-
-typedef bmpl::insert_range< LM_D3Q19_SRT,  bmpl::end< LM_D3Q19_SRT > ::type, LM_D3Q19_TRT>::type D3Q19_SRT_TRT;
-typedef bmpl::insert_range< D3Q19_SRT_TRT, bmpl::end< D3Q19_SRT_TRT >::type, LM_D3Q19_Extra>::type D3Q19_SRT_TRT_MRT;
-typedef bmpl::insert_range< D3Q19_SRT_TRT_MRT, bmpl::end< D3Q19_SRT_TRT_MRT >::type, LM_D3Q19_SRTField>::type LatticeModels3D;
-
-
-typedef bmpl::insert_range< LatticeModels2D, bmpl::end< LatticeModels2D >::type, LatticeModels3D>::type LatticeModels;
-
-typedef typename lbm::AdaptorsFromLatticeModels<LatticeModels>::type LBMAdaptors;
-
-
-
-using namespace walberla;
-
-struct InitObject
-{
-   InitObject()
-   {
-      namespace bmpl = boost::mpl;
-
-      auto pythonManager = python_coupling::Manager::instance();
-
-      // Field
-      pythonManager->addExporterFunction( field::exportModuleToPython<FieldTypes> );
-      pythonManager->addExporterFunction( field::exportGatherFunctions<bmpl::joint_view<FieldTypes,LBMAdaptors>::type > );
-      pythonManager->addBlockDataConversion< bmpl::joint_view<FieldTypes,LBMAdaptors>::type >() ;
-
-      // Blockforest
-      pythonManager->addExporterFunction( blockforest::exportModuleToPython<Stencils> );
-
-      // Geometry
-      pythonManager->addExporterFunction( geometry::exportModuleToPython );
-
-      // VTK
-      pythonManager->addExporterFunction( vtk::exportModuleToPython );
-
-      // LBM
-      typedef bmpl::vector< FlagField<walberla::uint8_t> > FlagFieldTypesForLBM;
-      pythonManager->addExporterFunction( lbm::exportBasic<LatticeModels, FlagFieldTypesForLBM> );
-      pythonManager->addExporterFunction( lbm::exportBoundary<LatticeModels, FlagFieldTypesForLBM> );
-      pythonManager->addExporterFunction( lbm::exportSweeps<LatticeModels, FlagFieldTypesForLBM> );
-
-      // Postprocessing
-      pythonManager->addExporterFunction( postprocessing::exportModuleToPython<FieldTypesForMeshGeneration, FlagFieldTypes> );
-
-      // Timeloop
-      pythonManager->addExporterFunction( timeloop::exportModuleToPython );
-
-      python_coupling::initWalberlaForPythonModule();
-   }
-};
-InitObject globalInitObject;
-
diff --git a/apps/pythonmodule/setup.py b/apps/pythonmodule/setup.py
index 5929b0fb9b78ef9d6984737ed165ac0f371829b1..066669c75768fdda310cb91d45c36bd96bbaf4be 100644
--- a/apps/pythonmodule/setup.py
+++ b/apps/pythonmodule/setup.py
@@ -1,28 +1,30 @@
-from distutils.core import setup
-import shutil
-from os.path import exists, join
-import platform
 import sys
+import platform
+from os.path import exists, join
+import shutil
+
+from setuptools import setup
 
 # The following variables are configure by CMake
 walberla_source_dir = "${walberla_SOURCE_DIR}"
 walberla_binary_dir = "${CMAKE_CURRENT_BINARY_DIR}"
+suffix = "${PYTHON_MODULE_EXTENSION}"
+prefix = "${PYTHON_MODULE_PREFIX}"
+walberla_module_file_name = prefix + "walberla_cpp" + suffix
+
+version = "${WALBERLA_VERSION}"
 
 if platform.system() == 'Windows':
-    extension = ('dll', 'pyd')
     configuration = 'Release'
 else:
-    extension = ('so', 'so')
     configuration = ''
 
 
 def collectFiles():
-    src_shared_lib = join(walberla_binary_dir, configuration, 'walberla_cpp.' + extension[0])
-    dst_shared_lib = join(walberla_binary_dir, 'waLBerla', 'walberla_cpp.' + extension[1])
+    src_shared_lib = join(walberla_binary_dir, configuration, walberla_module_file_name)
+    dst_shared_lib = join(walberla_binary_dir, 'waLBerla', walberla_module_file_name)
     # copy everything inplace
 
-    print(src_shared_lib)
-
     if not exists(src_shared_lib):
         print("Python Module was not built yet - run 'make walberla_cpp'")
         exit(1)
@@ -43,17 +45,19 @@ packages = ['waLBerla',
             'waLBerla.tools.report',
             'waLBerla.tools.sqlitedb',
             'waLBerla.tools.lbm_unitconversion',
-            'waLBerla.tools.jobscripts']
+            'waLBerla.tools.jobscripts',
+            'waLBerla.tools.config']
 
 collectFiles()
 
+
 setup(name='waLBerla',
-      version='1.0',
-      author='Martin Bauer',
-      author_email='martin.bauer@fau.de',
+      version=str(version),
+      author='Markus Holzer',
+      author_email='markus.holzer@fau.de',
       url='http://www.walberla.net',
       packages=packages,
-      package_data={'': ['walberla_cpp.' + extension[1]]}
+      package_data={'': [walberla_module_file_name]}
       )
 
 if sys.argv[1] == 'build':
diff --git a/apps/showcases/BidisperseFluidizedBed/BidisperseFluidizedBedDPM.cpp b/apps/showcases/BidisperseFluidizedBed/BidisperseFluidizedBedDPM.cpp
index d3e6fe63a866c8df9d30a9c5ea23d8522d868a9f..dad90bc62a911a87738db60f5ffa799046599aa3 100644
--- a/apps/showcases/BidisperseFluidizedBed/BidisperseFluidizedBedDPM.cpp
+++ b/apps/showcases/BidisperseFluidizedBed/BidisperseFluidizedBedDPM.cpp
@@ -76,12 +76,12 @@ const uint_t FieldGhostLayers( 1 );
 //#define OutletBC
 
 // PDF field, flag field & body field
-typedef GhostLayerField< Matrix3<real_t>, 1 >                          TensorField_T;
-typedef GhostLayerField< Vector3<real_t>, 1 >                          Vec3Field_T;
-typedef GhostLayerField< real_t, 1 >                                   ScalarField_T;
+using TensorField_T = GhostLayerField<Matrix3<real_t>, 1>;
+using Vec3Field_T = GhostLayerField<Vector3<real_t>, 1>;
+using ScalarField_T = GhostLayerField<real_t, 1>;
 using ForceModel_T = lbm::force_model::GuoField<Vec3Field_T>;
 
-typedef lbm::D3Q19< lbm::collision_model::SRTField<ScalarField_T>, false, ForceModel_T >   LatticeModel_T;
+using LatticeModel_T = lbm::D3Q19<lbm::collision_model::SRTField<ScalarField_T>, false, ForceModel_T>;
 using Stencil_T = LatticeModel_T::Stencil;
 using PdfField_T = lbm::PdfField<LatticeModel_T>;
 
@@ -89,18 +89,18 @@ using flag_t = walberla::uint8_t;
 using FlagField_T = FlagField<flag_t>;
 
 // boundary handling
-typedef lbm::NoSlip< LatticeModel_T, flag_t >                          NoSlip_T;
-typedef lbm::SimpleUBB< LatticeModel_T, flag_t >                       Inflow_T;
+using NoSlip_T = lbm::NoSlip<LatticeModel_T, flag_t>;
+using Inflow_T = lbm::SimpleUBB<LatticeModel_T, flag_t>;
 
 #ifdef OutletBC
 typedef lbm::Outlet< LatticeModel_T, FlagField_T >                     Outflow_T;
 #else
-typedef lbm::SimplePressure< LatticeModel_T, flag_t >                  Outflow_T;
+using Outflow_T = lbm::SimplePressure<LatticeModel_T, flag_t>;
 #endif
 
-typedef BoundaryHandling<FlagField_T, Stencil_T, NoSlip_T, Inflow_T, Outflow_T> BoundaryHandling_T;
+using BoundaryHandling_T = BoundaryHandling<FlagField_T, Stencil_T, NoSlip_T, Inflow_T, Outflow_T>;
 
-typedef std::tuple<pe::Plane, pe::Sphere> BodyTypeTuple ;
+using BodyTypeTuple = std::tuple<pe::Plane, pe::Sphere> ;
 
 ///////////
 // FLAGS //
@@ -1191,7 +1191,7 @@ int main( int argc, char **argv ) {
    if( dpm == DPMethod::GNS ) {
       if (interpol == Interpolation::INearestNeighbor) {
          if (dist == Distribution::DNearestNeighbor) {
-            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor> IFE_T;
+            using IFE_T = pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor>;
             shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
                                                                      flagFieldID, Fluid_Flag,
                                                                      velocityFieldID, svfFieldID,
@@ -1199,7 +1199,7 @@ int main( int argc, char **argv ) {
                                                                      viscosity);
             dragAndPressureForceEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr);
          } else if (dist == Distribution::DKernel) {
-            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor> IFE_T;
+            using IFE_T = pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor>;
             shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
                                                                      flagFieldID, Fluid_Flag,
                                                                      velocityFieldID, svfFieldID,
@@ -1209,7 +1209,7 @@ int main( int argc, char **argv ) {
          }
       } else if (interpol == Interpolation::IKernel) {
          if (dist == Distribution::DNearestNeighbor) {
-            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor> IFE_T;
+            using IFE_T = pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor>;
             shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
                                                                      flagFieldID, Fluid_Flag,
                                                                      velocityFieldID, svfFieldID,
@@ -1217,7 +1217,7 @@ int main( int argc, char **argv ) {
                                                                      viscosity);
             dragAndPressureForceEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr);
          } else if (dist == Distribution::DKernel) {
-            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor> IFE_T;
+            using IFE_T = pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor>;
             shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
                                                                      flagFieldID, Fluid_Flag,
                                                                      velocityFieldID, svfFieldID,
@@ -1227,7 +1227,7 @@ int main( int argc, char **argv ) {
          }
       } else if (interpol == Interpolation::ITrilinear) {
          if (dist == Distribution::DNearestNeighbor) {
-            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor> IFE_T;
+            using IFE_T = pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor>;
             shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
                                                                      flagFieldID, Fluid_Flag,
                                                                      velocityFieldID, svfFieldID,
@@ -1235,7 +1235,7 @@ int main( int argc, char **argv ) {
                                                                      viscosity);
             dragAndPressureForceEvaluationFunction = std::bind(&IFE_T::operator(), forceEvaluatorPtr);
          } else if (dist == Distribution::DKernel) {
-            typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor> IFE_T;
+            using IFE_T = pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor>;
             shared_ptr<IFE_T> forceEvaluatorPtr = make_shared<IFE_T>(blocks, dragForceFieldID, bodyStorageID,
                                                                      flagFieldID, Fluid_Flag,
                                                                      velocityFieldID, svfFieldID,
@@ -1257,7 +1257,7 @@ int main( int argc, char **argv ) {
    {
       if( dist == Distribution::DNearestNeighbor )
       {
-         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::LiftForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
                                                                         velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
                                                                         viscosity );
@@ -1265,7 +1265,7 @@ int main( int argc, char **argv ) {
       }
       else if( dist == Distribution::DKernel )
       {
-         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor > IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::LiftForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
                                                                         velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
                                                                         viscosity );
@@ -1276,7 +1276,7 @@ int main( int argc, char **argv ) {
    {
       if( dist == Distribution::DNearestNeighbor )
       {
-         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::LiftForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
                                                                         velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
                                                                         viscosity );
@@ -1284,7 +1284,7 @@ int main( int argc, char **argv ) {
       }
       else if( dist == Distribution::DKernel )
       {
-         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor > IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::LiftForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
                                                                         velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
                                                                         viscosity );
@@ -1295,7 +1295,7 @@ int main( int argc, char **argv ) {
    {
       if( dist == Distribution::DNearestNeighbor )
       {
-         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::LiftForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
                                                                         velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
                                                                         viscosity );
@@ -1303,7 +1303,7 @@ int main( int argc, char **argv ) {
       }
       else if( dist == Distribution::DKernel )
       {
-         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor > IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::LiftForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
                                                                         velocityFieldID, velocityCurlFieldID, liftCorrelationFunction,
                                                                         viscosity );
@@ -1317,7 +1317,7 @@ int main( int argc, char **argv ) {
    {
       if( dist == Distribution::DNearestNeighbor )
       {
-         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
                                                                         timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
                                                                         bodyVelocityTimeDerivativeEvaluator );
@@ -1325,7 +1325,7 @@ int main( int argc, char **argv ) {
       }
       else if( dist == Distribution::DKernel )
       {
-         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor > IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
                                                                         timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
                                                                         bodyVelocityTimeDerivativeEvaluator );
@@ -1336,7 +1336,7 @@ int main( int argc, char **argv ) {
    {
       if( dist == Distribution::DNearestNeighbor )
       {
-         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
                                                                         timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
                                                                         bodyVelocityTimeDerivativeEvaluator );
@@ -1344,7 +1344,7 @@ int main( int argc, char **argv ) {
       }
       else if( dist == Distribution::DKernel )
       {
-         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor > IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
                                                                         timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
                                                                         bodyVelocityTimeDerivativeEvaluator );
@@ -1355,7 +1355,7 @@ int main( int argc, char **argv ) {
    {
       if( dist == Distribution::DNearestNeighbor )
       {
-         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor > IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::NearestNeighborDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
                                                                         timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
                                                                         bodyVelocityTimeDerivativeEvaluator );
@@ -1363,7 +1363,7 @@ int main( int argc, char **argv ) {
       }
       else if( dist == Distribution::DKernel )
       {
-         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator< FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor > IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<FlagField_T, field::TrilinearFieldInterpolator, field::KernelDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T > ( blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag,
                                                                         timeDerivativeVelocityFieldID, addedMassCorrelationFunction,
                                                                         bodyVelocityTimeDerivativeEvaluator );
diff --git a/apps/showcases/CMakeLists.txt b/apps/showcases/CMakeLists.txt
index cfdbf95914d9d7f94bd7070485a75b113002f1b0..30592ee71e3c0bc0c6f6066170c779fc2f010a2e 100644
--- a/apps/showcases/CMakeLists.txt
+++ b/apps/showcases/CMakeLists.txt
@@ -3,6 +3,6 @@ add_subdirectory( CombinedResolvedUnresolved )
 add_subdirectory( HeatConduction )
 add_subdirectory( Mixer )
 add_subdirectory( PegIntoSphereBed )
-if ( WALBERLA_BUILD_WITH_CODEGEN)
+if ( WALBERLA_BUILD_WITH_CODEGEN AND WALBERLA_BUILD_WITH_PYTHON )
 add_subdirectory( PhaseFieldAllenCahn )
 endif()
diff --git a/apps/showcases/CombinedResolvedUnresolved/CombinedResolvedUnresolved.cpp b/apps/showcases/CombinedResolvedUnresolved/CombinedResolvedUnresolved.cpp
index 8b0779629654e8e34cb856cf81bcd155ffef87f7..5e1074ad008cd0a14f82fd6d3defd133063d4456 100644
--- a/apps/showcases/CombinedResolvedUnresolved/CombinedResolvedUnresolved.cpp
+++ b/apps/showcases/CombinedResolvedUnresolved/CombinedResolvedUnresolved.cpp
@@ -81,13 +81,13 @@ using walberla::uint_t;
 
 // PDF field, flag field & body field
 
-typedef GhostLayerField< pe::BodyID, 1 > BodyField_T;
-typedef GhostLayerField< Matrix3< real_t >, 1 > TensorField_T;
-typedef GhostLayerField< Vector3< real_t >, 1 > Vec3Field_T;
-typedef GhostLayerField< real_t, 1 > ScalarField_T;
+using BodyField_T = GhostLayerField<pe::BodyID, 1>;
+using TensorField_T = GhostLayerField<Matrix3<real_t>, 1>;
+using Vec3Field_T = GhostLayerField<Vector3<real_t>, 1>;
+using ScalarField_T = GhostLayerField<real_t, 1>;
 using ForceModel_T = lbm::force_model::GuoField< Vec3Field_T >;
 
-typedef lbm::D3Q19< lbm::collision_model::SRTField< ScalarField_T >, false, ForceModel_T > LatticeModel_T;
+using LatticeModel_T = lbm::D3Q19<lbm::collision_model::SRTField<ScalarField_T>, false, ForceModel_T>;
 using Stencil_T  = LatticeModel_T::Stencil;
 using PdfField_T = lbm::PdfField< LatticeModel_T >;
 
@@ -97,13 +97,13 @@ using FlagField_T = FlagField< flag_t >;
 const uint_t FieldGhostLayers = 1;
 
 // boundary handling
-typedef lbm::NoSlip< LatticeModel_T, flag_t > NoSlip_T;
+using NoSlip_T = lbm::NoSlip<LatticeModel_T, flag_t>;
 
-typedef pe_coupling::CurvedLinear< LatticeModel_T, FlagField_T > MO_T;
+using MO_T = pe_coupling::CurvedLinear<LatticeModel_T, FlagField_T>;
 
-typedef BoundaryHandling< FlagField_T, Stencil_T, NoSlip_T, MO_T > BoundaryHandling_T;
+using BoundaryHandling_T = BoundaryHandling<FlagField_T, Stencil_T, NoSlip_T, MO_T>;
 
-typedef std::tuple< pe::Sphere, pe::Plane > BodyTypeTuple;
+using BodyTypeTuple = std::tuple<pe::Sphere, pe::Plane>;
 
 ///////////
 // FLAGS //
@@ -265,10 +265,7 @@ bool selectDPMBodies(pe::BodyID bodyID)
 {
    pe::SphereID sphere = static_cast< pe::SphereID >(bodyID);
    real_t radius       = sphere->getRadius();
-   if (radius <= real_t(0.5))
-      return true;
-   else
-      return false;
+   return radius <= real_t(0.5);
 }
 
 bool selectMEMBodies(pe::BodyID bodyID) { return !selectDPMBodies(bodyID); }
@@ -546,7 +543,7 @@ int main(int argc, char** argv)
    // create pe bodies
 
    // bounding planes (global)
-   const auto planeMaterial = pe::createMaterial("planeMaterial", 1000.0, 0.8, 0.1, 0.05, 0.2, 80, 100, 10, 11);
+   const auto planeMaterial = pe::createMaterial("planeMaterial", 1000_r, 0.8_r, 0.1_r, 0.05_r, 0.2_r, 80_r, 100_r, 10_r, 11_r);
    pe::createPlane(*globalBodyStorage, 0, Vector3< real_t >(1, 0, 0), Vector3< real_t >(0, 0, 0), planeMaterial);
    pe::createPlane(*globalBodyStorage, 0, Vector3< real_t >(-1, 0, 0), Vector3< real_t >(real_c(domainSize[0]), 0, 0),
                    planeMaterial);
@@ -567,8 +564,8 @@ int main(int argc, char** argv)
    // add DPM spheres
    const auto peMaterial_DPM = createSphereMaterial("peMaterial_DPM", diameter_DPM, densityRatio_DPM);
    createSphereLattice(*blocks, *globalBodyStorage, bodyStorageID,
-                       AABB(real_t(0), real_t(0), real_t(1.0 * real_t(domainSize[2]) / 8.0), real_t(domainSize[0]),
-                            real_t(domainSize[1]), real_t(2.0 * real_t(domainSize[2]) / 8.0)),
+                       AABB(real_t(0), real_t(0), real_t(1.0 * real_t(domainSize[2]) / 8.0_r), real_t(domainSize[0]),
+                            real_t(domainSize[1]), real_t(2.0 * real_t(domainSize[2]) / 8.0_r)),
                        diameter_DPM, real_t(0.15), peMaterial_DPM, real_t(0));
 
    syncCall();
@@ -801,9 +798,7 @@ int main(int argc, char** argv)
    {
       if (dist == Distribution::DNearestNeighbor)
       {
-         typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<
-            FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor >
-            IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >(
             blocks, dragForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID, svfFieldID,
             pressureGradientFieldID, dragCorrelationFunction, viscosity, selectDPMBodies);
@@ -811,9 +806,7 @@ int main(int argc, char** argv)
       }
       else if (dist == Distribution::DKernel)
       {
-         typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<
-            FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor >
-            IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >(
             blocks, dragForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID, svfFieldID,
             pressureGradientFieldID, dragCorrelationFunction, viscosity, selectDPMBodies);
@@ -824,9 +817,7 @@ int main(int argc, char** argv)
    {
       if (dist == Distribution::DNearestNeighbor)
       {
-         typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<
-            FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor >
-            IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >(
             blocks, dragForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID, svfFieldID,
             pressureGradientFieldID, dragCorrelationFunction, viscosity, selectDPMBodies);
@@ -834,9 +825,7 @@ int main(int argc, char** argv)
       }
       else if (dist == Distribution::DKernel)
       {
-         typedef pe_coupling::discrete_particle_methods::InteractionForceEvaluator<
-            FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor >
-            IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::InteractionForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >(
             blocks, dragForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID, svfFieldID,
             pressureGradientFieldID, dragCorrelationFunction, viscosity, selectDPMBodies);
@@ -850,9 +839,7 @@ int main(int argc, char** argv)
    {
       if (dist == Distribution::DNearestNeighbor)
       {
-         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator<
-            FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor >
-            IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::LiftForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr =
             make_shared< IFE_T >(blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID,
                                  velocityCurlFieldID, liftCorrelationFunction, viscosity, selectDPMBodies);
@@ -860,9 +847,7 @@ int main(int argc, char** argv)
       }
       else if (dist == Distribution::DKernel)
       {
-         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator<
-            FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor >
-            IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::LiftForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr =
             make_shared< IFE_T >(blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID,
                                  velocityCurlFieldID, liftCorrelationFunction, viscosity, selectDPMBodies);
@@ -873,9 +858,7 @@ int main(int argc, char** argv)
    {
       if (dist == Distribution::DNearestNeighbor)
       {
-         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator<
-            FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor >
-            IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::LiftForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr =
             make_shared< IFE_T >(blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID,
                                  velocityCurlFieldID, liftCorrelationFunction, viscosity, selectDPMBodies);
@@ -883,9 +866,7 @@ int main(int argc, char** argv)
       }
       else if (dist == Distribution::DKernel)
       {
-         typedef pe_coupling::discrete_particle_methods::LiftForceEvaluator<
-            FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor >
-            IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::LiftForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr =
             make_shared< IFE_T >(blocks, liftForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, velocityFieldID,
                                  velocityCurlFieldID, liftCorrelationFunction, viscosity, selectDPMBodies);
@@ -899,9 +880,7 @@ int main(int argc, char** argv)
    {
       if (dist == Distribution::DNearestNeighbor)
       {
-         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<
-            FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor >
-            IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::NearestNeighborDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >(
             blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, timeDerivativeVelocityFieldID,
             addedMassCorrelationFunction, bodyVelocityTimeDerivativeEvaluator, selectDPMBodies);
@@ -909,9 +888,7 @@ int main(int argc, char** argv)
       }
       else if (dist == Distribution::DKernel)
       {
-         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<
-            FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor >
-            IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<FlagField_T, field::NearestNeighborFieldInterpolator, field::KernelDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >(
             blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, timeDerivativeVelocityFieldID,
             addedMassCorrelationFunction, bodyVelocityTimeDerivativeEvaluator, selectDPMBodies);
@@ -922,9 +899,7 @@ int main(int argc, char** argv)
    {
       if (dist == Distribution::DNearestNeighbor)
       {
-         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<
-            FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor >
-            IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::NearestNeighborDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >(
             blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, timeDerivativeVelocityFieldID,
             addedMassCorrelationFunction, bodyVelocityTimeDerivativeEvaluator, selectDPMBodies);
@@ -932,9 +907,7 @@ int main(int argc, char** argv)
       }
       else if (dist == Distribution::DKernel)
       {
-         typedef pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<
-            FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor >
-            IFE_T;
+         using IFE_T = pe_coupling::discrete_particle_methods::AddedMassForceEvaluator<FlagField_T, field::KernelFieldInterpolator, field::KernelDistributor>;
          shared_ptr< IFE_T > forceEvaluatorPtr = make_shared< IFE_T >(
             blocks, amForceFieldID, bodyStorageID, flagFieldID, Fluid_Flag, timeDerivativeVelocityFieldID,
             addedMassCorrelationFunction, bodyVelocityTimeDerivativeEvaluator, selectDPMBodies);
@@ -1219,9 +1192,7 @@ int main(int argc, char** argv)
 
       // sweep for restoring PDFs in cells previously occupied by pe bodies
       pe_coupling::SphereNormalExtrapolationDirectionFinder extrapolationFinder(blocks, bodyFieldID);
-      typedef pe_coupling::ExtrapolationReconstructor< LatticeModel_T, BoundaryHandling_T,
-                                                       pe_coupling::SphereNormalExtrapolationDirectionFinder >
-         Reconstructor_T;
+      using Reconstructor_T = pe_coupling::ExtrapolationReconstructor<LatticeModel_T, BoundaryHandling_T, pe_coupling::SphereNormalExtrapolationDirectionFinder>;
       Reconstructor_T reconstructor(blocks, boundaryHandlingID, bodyFieldID, extrapolationFinder, true);
 
       timeloop.add() << Sweep(pe_coupling::PDFReconstruction< LatticeModel_T, BoundaryHandling_T, Reconstructor_T >(
diff --git a/apps/showcases/HeatConduction/ContactDetection.h b/apps/showcases/HeatConduction/ContactDetection.h
index c5ed90565ea564ef350e1ff5a1931010bb22641a..9c812ce478ce5b14b1082d649dda583635dc7817 100644
--- a/apps/showcases/HeatConduction/ContactDetection.h
+++ b/apps/showcases/HeatConduction/ContactDetection.h
@@ -83,7 +83,7 @@ inline bool ContactDetection::operator()( const size_t idx1,
 template <typename Accessor>
 inline bool ContactDetection::operator()( const size_t idx1,
                                           const size_t idx2,
-                                          const data::Sphere& s,
+                                          const data::Sphere& /*s*/,
                                           const data::HalfSpace& p,
                                           Accessor& ac )
 {
diff --git a/apps/showcases/Mixer/Mixer.cpp b/apps/showcases/Mixer/Mixer.cpp
index 75016f8bd5dca74e64e898c2145c0739ffe03b0e..034156452a6c0fa72eb3bbf5ec2c48c7f5e79bfb 100644
--- a/apps/showcases/Mixer/Mixer.cpp
+++ b/apps/showcases/Mixer/Mixer.cpp
@@ -261,7 +261,7 @@ int main( int argc, char ** argv )
    Vec3 origin = simulationDomain.center();
    Vec3 dp;
 
-   auto mixingBlade = ss->create<data::Box>( Vec3(0.009,simulationDomain.ySize(),simulationDomain.zSize()) );
+   auto mixingBlade = ss->create<data::Box>( Vec3(0.009_r, simulationDomain.ySize(), simulationDomain.zSize()) );
    ss->shapes[mixingBlade]->updateMassAndInertia(std::numeric_limits<real_t>::infinity());
    auto box0                       = ps->create();
    box0->getPositionRef()          = Vec3(simulationDomain.xSize() * real_t(0.5),0.0,0.0);
@@ -316,10 +316,10 @@ int main( int argc, char ** argv )
    data::particle_flags::set(box3->getFlagsRef(), data::particle_flags::INFINITE);
    data::particle_flags::set(box3->getFlagsRef(), data::particle_flags::FIXED);
    data::particle_flags::set(box3->getFlagsRef(), data::particle_flags::NON_COMMUNICATING);
-   box3->getRotationRef().rotate(Vec3(0,1,0), math::pi * 0.15 );
+   box3->getRotationRef().rotate(Vec3(0,1,0), math::pi * 0.15_r );
 //   box3->getRotationRef().rotate(Vec3(0,0,1), -math::pi * 0.15 );
    dp = ( box3->getPosition() - origin );
-   dRot = Rot3(Vec3(real_t(0), real_t(0), math::pi * 1.5));
+   dRot = Rot3(Vec3(real_t(0), real_t(0), math::pi * 1.5_r));
    box3->setPosition( origin + dRot.getMatrix() * dp );
    box3->getRotationRef().rotate(dRot);
 
diff --git a/apps/showcases/PhaseFieldAllenCahn/CMakeLists.txt b/apps/showcases/PhaseFieldAllenCahn/CMakeLists.txt
index ed6d22966d3b63e776d5a1a02642cf1f4cf7bd07..9084c99a814c65c5ab6fefe54b1ad5ff0213282c 100644
--- a/apps/showcases/PhaseFieldAllenCahn/CMakeLists.txt
+++ b/apps/showcases/PhaseFieldAllenCahn/CMakeLists.txt
@@ -1,6 +1,4 @@
-if ( NOT WALBERLA_BUILD_WITH_CUDA)
 add_subdirectory( CPU )
-endif()
 
 if ( WALBERLA_BUILD_WITH_CUDA)
 add_subdirectory( GPU )
diff --git a/apps/showcases/PhaseFieldAllenCahn/CPU/CMakeLists.txt b/apps/showcases/PhaseFieldAllenCahn/CPU/CMakeLists.txt
index b155b2f5b06877094acf351dfe8cad8fc6d8eac0..adacc326397db495352f958afa90fb26ae63e5d4 100644
--- a/apps/showcases/PhaseFieldAllenCahn/CPU/CMakeLists.txt
+++ b/apps/showcases/PhaseFieldAllenCahn/CPU/CMakeLists.txt
@@ -13,9 +13,8 @@ waLBerla_generate_target_from_python(NAME PhaseFieldCodeGenCPU
         stream_hydro.cpp stream_hydro.h
         GenDefines.h)
 
-waLBerla_add_executable(NAME multiphase
+waLBerla_add_executable(NAME multiphaseCPU
         FILES multiphase.cpp PythonExports.cpp InitializerFunctions.cpp contact.cpp CalculateNormals.cpp multiphase_codegen.py
         DEPENDS blockforest core field postprocessing lbm geometry timeloop gui PhaseFieldCodeGenCPU)
 
-
-
+set_target_properties(multiphaseCPU PROPERTIES CXX_VISIBILITY_PRESET hidden)
diff --git a/apps/showcases/PhaseFieldAllenCahn/CPU/PythonExports.cpp b/apps/showcases/PhaseFieldAllenCahn/CPU/PythonExports.cpp
index 40bb8c52bf9a4b320b3f5300c0714ef8bda06e43..4b73da880ffb039fc8125f289019bc827ff9a70d 100644
--- a/apps/showcases/PhaseFieldAllenCahn/CPU/PythonExports.cpp
+++ b/apps/showcases/PhaseFieldAllenCahn/CPU/PythonExports.cpp
@@ -20,68 +20,28 @@
 #include "waLBerlaDefinitions.h"
 #ifdef WALBERLA_BUILD_WITH_PYTHON
 
-#include "python_coupling/Manager.h"
+#   include "core/math/Constants.h"
 
-#include "core/math/Constants.h"
-
-#include "blockforest/python/Exports.h"
-
-#include "field/communication/PackInfo.h"
-#include "field/python/Exports.h"
-
-#include "geometry/python/Exports.h"
-
-#include "postprocessing/python/Exports.h"
-
-#include "timeloop/python/Exports.h"
+#   include "field/communication/PackInfo.h"
 
+#   include "python_coupling/Manager.h"
+#   include "python_coupling/export/BlockForestExport.h"
+#   include "python_coupling/export/FieldExports.h"
 
 namespace walberla {
     using flag_t = uint8_t;
     // clang-format off
     void exportDataStructuresToPython() {
 
-        namespace bmpl = boost::mpl;
-
         auto pythonManager = python_coupling::Manager::instance();
 
-        typedef bmpl::vector<
-                Field<walberla::real_t, 1>,
-                Field<walberla::real_t, 3>,
-                Field<walberla::real_t, 9>,
-                Field<walberla::real_t, 19>,
-                Field<walberla::real_t, 27>>
-                FieldTypes;
-
-        typedef bmpl::vector<stencil::D2Q9,
-                stencil::D3Q19,
-                stencil::D3Q27>
-                Stencils;
-
-        typedef bmpl::vector<
-                GhostLayerField<real_t,1>,
-                GhostLayerField<real_t,3>>
-                RealFieldTypes;
-
-        typedef bmpl::vector<
-                FlagField<flag_t>>
-                FlagFieldTypes;
         // Field
-        pythonManager->addExporterFunction(field::exportModuleToPython<FieldTypes>);
-        pythonManager->addExporterFunction(field::exportGatherFunctions<FieldTypes>);
-        pythonManager->addBlockDataConversion<FieldTypes>();
+        pythonManager->addExporterFunction(field::exportModuleToPython<Field<walberla::real_t, 1>, Field<walberla::real_t, 3>, Field<walberla::real_t, 9>, Field<walberla::real_t, 19>, Field<walberla::real_t, 27>>);
+        pythonManager->addExporterFunction(field::exportGatherFunctions<Field<walberla::real_t, 1>, Field<walberla::real_t, 3>, Field<walberla::real_t, 9>, Field<walberla::real_t, 19>, Field<walberla::real_t, 27>>);
+        pythonManager->addBlockDataConversion<Field<walberla::real_t, 1>, Field<walberla::real_t, 3>, Field<walberla::real_t, 9>, Field<walberla::real_t, 19>, Field<walberla::real_t, 27>>();
 
         // Blockforest
-        pythonManager->addExporterFunction(blockforest::exportModuleToPython<Stencils>);
-
-        // Timeloop
-        pythonManager->addExporterFunction(timeloop::exportModuleToPython);
-
-        // Postprocessing
-        pythonManager->addExporterFunction( postprocessing::exportModuleToPython<RealFieldTypes, FlagFieldTypes> );
-
-        // Geometry
-        pythonManager->addExporterFunction( geometry::exportModuleToPython );
+        pythonManager->addExporterFunction(blockforest::exportModuleToPython<stencil::D2Q9, stencil::D3Q19, stencil::D3Q27>);
     }
    // clang-format on
 }
diff --git a/apps/showcases/PhaseFieldAllenCahn/CPU/contact.cpp b/apps/showcases/PhaseFieldAllenCahn/CPU/contact.cpp
index 4e87ab28f4c6e5c011d2e78dda90efc08f0e6c94..8253e1d4cd26c071e4c1abd775d8aee82e27ce26 100644
--- a/apps/showcases/PhaseFieldAllenCahn/CPU/contact.cpp
+++ b/apps/showcases/PhaseFieldAllenCahn/CPU/contact.cpp
@@ -36,7 +36,7 @@ namespace lbm
 
 namespace internal_boundary_contact
 {
-static FUNC_PREFIX void contact_angle_treatment(uint8_t* RESTRICT const _data_indexVector, double* RESTRICT _data_phase,
+static FUNC_PREFIX void contact_angle_treatment(uint8_t* WALBERLA_RESTRICT const _data_indexVector, double* WALBERLA_RESTRICT _data_phase,
                                                 int64_t const _stride_phase_0, int64_t const _stride_phase_1,
                                                 int64_t const _stride_phase_2, int64_t indexVectorSize, double alpha)
 {
@@ -58,8 +58,8 @@ static FUNC_PREFIX void contact_angle_treatment(uint8_t* RESTRICT const _data_in
       const double a = cos(alpha);
       const double W = 5;
 
-      double* RESTRICT _phase_wall     = _data_phase + _stride_phase_1 * y + _stride_phase_2 * z;
-      double* RESTRICT _phase_interior = _data_phase + _stride_phase_1 * y1 + _stride_phase_2 * z1;
+      double* WALBERLA_RESTRICT _phase_wall     = _data_phase + _stride_phase_1 * y + _stride_phase_2 * z;
+      double* WALBERLA_RESTRICT _phase_interior = _data_phase + _stride_phase_1 * y1 + _stride_phase_2 * z1;
       if (h < 0.001) { _phase_wall[_stride_phase_0 * x] = 1.0; }
       else if (a > 1e-8 || a < -1e-8)
       {
@@ -98,7 +98,7 @@ void contact::run(IBlock* block, IndexVectors::Type type)
    auto& alpha     = this->alpha_;
 
    WALBERLA_ASSERT_GREATER_EQUAL(0, -int_c(phaseField->nrOfGhostLayers()))
-   double* RESTRICT _data_phase = phaseField->dataAt(0, 0, 0, 0);
+   double* WALBERLA_RESTRICT _data_phase = phaseField->dataAt(0, 0, 0, 0);
    const auto _stride_pdfs_0    = int64_t(phaseField->xStride());
    const auto _stride_pdfs_1    = int64_t(phaseField->yStride());
    const auto _stride_pdfs_2    = int64_t(phaseField->zStride());
diff --git a/apps/showcases/PhaseFieldAllenCahn/CPU/contact.h b/apps/showcases/PhaseFieldAllenCahn/CPU/contact.h
index c534195169519a8634fad6fd0dc00b836b1b0634..dbaf4a560b3a717c96b34ae811a58f048776eff8 100644
--- a/apps/showcases/PhaseFieldAllenCahn/CPU/contact.h
+++ b/apps/showcases/PhaseFieldAllenCahn/CPU/contact.h
@@ -31,14 +31,6 @@
 #include <set>
 #include <vector>
 
-#ifdef __GNUC__
-#   define RESTRICT __restrict__
-#elif _MSC_VER
-#   define RESTRICT __restrict
-#else
-#   define RESTRICT
-#endif
-
 namespace walberla
 {
 namespace lbm
diff --git a/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase.cpp b/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase.cpp
index cf1eff4d3d60b7d43f11d8070dcd7e00c4a5bca0..00f5fae4d7caad2a9540aade589e05ea9aa06cc3 100644
--- a/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase.cpp
+++ b/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase.cpp
@@ -28,13 +28,13 @@
 #include "field/AddToStorage.h"
 #include "field/FlagField.h"
 #include "field/communication/PackInfo.h"
-#include "field/python/Exports.h"
 #include "field/vtk/VTKWriter.h"
 
 #include "geometry/InitBoundaryHandling.h"
 
 #include "python_coupling/CreateConfig.h"
 #include "python_coupling/PythonCallback.h"
+#include "python_coupling/export/FieldExports.h"
 
 #include "timeloop/SweepTimeloop.h"
 
@@ -292,8 +292,8 @@ int main(int argc, char** argv)
                python_coupling::PythonCallback callback("at_end_of_time_step");
                if (callback.isCallable())
                {
-                  callback.data().exposePtr("blocks", blocks);
-                  callback.data().exposePtr("time_loop", timeLoop);
+                  callback.data().exposeValue("blocks", blocks);
+                  callback.data().exposeValue( "timeStep", timeLoop->getCurrentTimeStep());
                   callback();
                }
             }
diff --git a/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase_RTI_3D.py b/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase_RTI_3D.py
index 54f656cf95344c58b5b201253fe60d0fc7760431..1ef86f585a2ad8e90ae0400321284431eaa71b04 100755
--- a/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase_RTI_3D.py
+++ b/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase_RTI_3D.py
@@ -13,7 +13,7 @@ class Scenario:
         self.dbWriteFrequency = 200
 
         # simulation parameters
-        self.timesteps = 6001
+        self.timesteps = 27001
 
         self.cells = (64, 32, 64)
         self.blocks = (1, 8, 1)
@@ -77,8 +77,8 @@ class Scenario:
         }
 
     @wlb.member_callback
-    def at_end_of_time_step(self, blocks, time_loop):
-        t = time_loop.getCurrentTimeStep()
+    def at_end_of_time_step(self, blocks, **kwargs):
+        t = kwargs['timeStep']
         ny = self.size[1]
         l0 = self.size[0]
         if t % self.dbWriteFrequency == 0:
@@ -88,22 +88,22 @@ class Scenario:
             mass = -100
             spike_data = wlb.field.gather(blocks, 'phase', makeSlice[self.size[0] // 2, :, self.size[2] // 2])
             if spike_data:
-                spike_field = np.asarray(spike_data.buffer()).squeeze()
+                spike_field = np.asarray(spike_data).squeeze()
                 location_of_spike = (np.argmax(spike_field > 0.5) - ny // 2) / l0
 
             bubble_data = wlb.field.gather(blocks, 'phase', makeSlice[0, :, 0])
             if bubble_data:
-                bubble_field = np.asarray(bubble_data.buffer()).squeeze()
+                bubble_field = np.asarray(bubble_data).squeeze()
                 location_of_bubble = (np.argmax(bubble_field > 0.5) - ny // 2) / l0
 
             saddle_data = wlb.field.gather(blocks, 'phase', makeSlice[0, :, self.size[2] // 2])
             if saddle_data:
-                saddle_field = np.asarray(saddle_data.buffer()).squeeze()
+                saddle_field = np.asarray(saddle_data).squeeze()
                 location_of_saddle = (np.argmax(saddle_field > 0.5) - ny // 2) / l0
 
             phase = wlb.field.gather(blocks, 'phase', makeSlice[:, :, :])
             if phase:
-                phase_field = np.asarray(phase.buffer()).squeeze()
+                phase_field = np.asarray(phase).squeeze()
                 mass = np.sum(phase_field)
 
             self.write_result_to_database(t, location_of_spike, location_of_bubble, location_of_saddle, mass)
diff --git a/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase_codegen.py b/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase_codegen.py
index 42e990dc30f32c487c75f021ca4f21ec993cf202..28f40c5e4a68f477fcd2d8137fa270ca07f0f577 100644
--- a/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase_codegen.py
+++ b/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase_codegen.py
@@ -17,6 +17,7 @@ from lbmpy.phasefield_allen_cahn.force_model import MultiphaseForceModel
 
 import numpy as np
 import sympy as sp
+import pystencils as ps
 
 stencil_phase = get_stencil("D3Q19")
 stencil_hydro = get_stencil("D3Q27")
@@ -125,8 +126,9 @@ phase_field_LB_step = create_lb_update_rule(lb_method=method_phase,
                                             kernel_type='stream_pull_collide')
 
 phase_field_LB_step.set_main_assignments_from_dict({**phase_field_LB_step.main_assignments_dict, **{C.center: sum_h}})
+subexp = [ps.Assignment(a.lhs, float(a.rhs)) if a.rhs == 0 else a for a in phase_field_LB_step.subexpressions]
 phase_field_LB_step = AssignmentCollection(main_assignments=phase_field_LB_step.main_assignments,
-                                           subexpressions=phase_field_LB_step.subexpressions)
+                                           subexpressions=subexp)
 phase_field_LB_step = sympy_cse(phase_field_LB_step)
 
 # ---------------------------------------------------------------------------------------------------------
@@ -134,8 +136,9 @@ hydro_LB_step = get_collision_assignments_hydro(lb_method=method_hydro,
                                                 density=density,
                                                 velocity_input=u,
                                                 force=force_g,
-                                                optimization={"symbolic_field": g,
-                                                              "symbolic_temporary_field": g_tmp},
+                                                sub_iterations=2,
+                                                symbolic_fields={"symbolic_field": g,
+                                                                 "symbolic_temporary_field": g_tmp},
                                                 kernel_type='collide_only')
 
 hydro_LB_step.set_sub_expressions_from_dict({**{relaxation_rate: relaxation_rate_cutoff},
@@ -149,7 +152,7 @@ stream_hydro = create_lb_update_rule(stencil=stencil_hydro,
 ###################
 # GENERATE SWEEPS #
 ###################
-cpu_vec = {'instruction_set': 'sse', 'assume_inner_stride_one': True, 'nontemporal': True}
+cpu_vec = {'assume_inner_stride_one': True, 'nontemporal': True}
 
 vp = [('int32_t', 'cudaBlockSize0'),
       ('int32_t', 'cudaBlockSize1')]
@@ -163,24 +166,24 @@ with CodeGeneration() as ctx:
     if not ctx.optimize_for_localhost:
         cpu_vec = {'instruction_set': None}
 
-    generate_sweep(ctx, 'initialize_phase_field_distributions', h_updates)
-    generate_sweep(ctx, 'initialize_velocity_based_distributions', g_updates)
+    generate_sweep(ctx, 'initialize_phase_field_distributions', h_updates, target='cpu')
+    generate_sweep(ctx, 'initialize_velocity_based_distributions', g_updates, target='cpu')
 
     generate_sweep(ctx, 'phase_field_LB_step', phase_field_LB_step,
                    field_swaps=[(h, h_tmp)],
                    inner_outer_split=True,
-                   cpu_vectorize_info=cpu_vec)
-    generate_boundary(ctx, 'phase_field_LB_NoSlip', NoSlip(), method_phase)
+                   cpu_vectorize_info=cpu_vec, target='cpu')
+    generate_boundary(ctx, 'phase_field_LB_NoSlip', NoSlip(), method_phase, target='cpu')
 
     generate_sweep(ctx, 'hydro_LB_step', hydro_LB_step,
                    inner_outer_split=True,
-                   cpu_vectorize_info=cpu_vec)
-    generate_boundary(ctx, 'hydro_LB_NoSlip', NoSlip(), method_hydro)
+                   cpu_vectorize_info=cpu_vec, target='cpu')
+    generate_boundary(ctx, 'hydro_LB_NoSlip', NoSlip(), method_hydro, target='cpu')
 
     generate_sweep(ctx, 'stream_hydro', stream_hydro,
                    field_swaps=[(g, g_tmp)],
                    inner_outer_split=True,
-                   cpu_vectorize_info=cpu_vec)
+                   cpu_vectorize_info=cpu_vec, target='cpu')
 
     ctx.write_file("GenDefines.h", info_header)
 
diff --git a/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase_rising_bubble.py b/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase_rising_bubble.py
index 9a8948868454021a8811f6d4ed5aa2662b88418f..d424d7c21c5255ec85ed28e493f54176d12ff4be 100755
--- a/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase_rising_bubble.py
+++ b/apps/showcases/PhaseFieldAllenCahn/CPU/multiphase_rising_bubble.py
@@ -91,12 +91,12 @@ class Scenario:
         }
 
     @wlb.member_callback
-    def at_end_of_time_step(self, blocks, time_loop):
-        t = time_loop.getCurrentTimeStep()
+    def at_end_of_time_step(self, blocks, **kwargs):
+        t = kwargs['timeStep']
         if t % self.dbWriteFrequency == 0:
             wlb_field = wlb.field.gather(blocks, 'phase', makeSlice[:, :, self.size[2] // 2])
             if wlb_field:
-                phase_field = np.asarray(wlb_field.buffer()).squeeze()
+                phase_field = np.asarray(wlb_field).squeeze()
 
                 location_of_gas = np.where(phase_field < 0.5)
                 cov = np.cov(location_of_gas)
@@ -107,7 +107,9 @@ class Scenario:
                 self.yPositions.append(center_of_mass[1])
                 if len(self.yPositions) > 1:
                     speed = self.yPositions[-1] - self.yPositions[-2]
-                    self.write_result_to_database(t, speed, axis_of_the_bubble, center_of_mass)
+                else:
+                    speed = 0
+                self.write_result_to_database(t, speed, axis_of_the_bubble, center_of_mass)
 
                 self.counter += 1
 
diff --git a/apps/showcases/PhaseFieldAllenCahn/GPU/CMakeLists.txt b/apps/showcases/PhaseFieldAllenCahn/GPU/CMakeLists.txt
index e2982ff9355663358789d257de9d679c3264e098..51c6922032a44744f513890ac6642185930ba3af 100644
--- a/apps/showcases/PhaseFieldAllenCahn/GPU/CMakeLists.txt
+++ b/apps/showcases/PhaseFieldAllenCahn/GPU/CMakeLists.txt
@@ -16,9 +16,8 @@ waLBerla_generate_target_from_python(NAME PhaseFieldCodeGenGPU
         PackInfo_velocity_based_distributions.cu PackInfo_velocity_based_distributions.h
         GenDefines.h)
 
-waLBerla_add_executable(NAME multiphase
+waLBerla_add_executable(NAME multiphaseGPU
         FILES multiphase.cpp PythonExports.cpp InitializerFunctions.cpp CalculateNormals.cpp contact.cu multiphase_codegen.py
         DEPENDS blockforest core cuda field postprocessing lbm geometry timeloop gui PhaseFieldCodeGenGPU)
 
-
-
+set_target_properties(multiphaseGPU PROPERTIES CXX_VISIBILITY_PRESET hidden)
diff --git a/apps/showcases/PhaseFieldAllenCahn/GPU/PythonExports.cpp b/apps/showcases/PhaseFieldAllenCahn/GPU/PythonExports.cpp
index 40bb8c52bf9a4b320b3f5300c0714ef8bda06e43..4b73da880ffb039fc8125f289019bc827ff9a70d 100644
--- a/apps/showcases/PhaseFieldAllenCahn/GPU/PythonExports.cpp
+++ b/apps/showcases/PhaseFieldAllenCahn/GPU/PythonExports.cpp
@@ -20,68 +20,28 @@
 #include "waLBerlaDefinitions.h"
 #ifdef WALBERLA_BUILD_WITH_PYTHON
 
-#include "python_coupling/Manager.h"
+#   include "core/math/Constants.h"
 
-#include "core/math/Constants.h"
-
-#include "blockforest/python/Exports.h"
-
-#include "field/communication/PackInfo.h"
-#include "field/python/Exports.h"
-
-#include "geometry/python/Exports.h"
-
-#include "postprocessing/python/Exports.h"
-
-#include "timeloop/python/Exports.h"
+#   include "field/communication/PackInfo.h"
 
+#   include "python_coupling/Manager.h"
+#   include "python_coupling/export/BlockForestExport.h"
+#   include "python_coupling/export/FieldExports.h"
 
 namespace walberla {
     using flag_t = uint8_t;
     // clang-format off
     void exportDataStructuresToPython() {
 
-        namespace bmpl = boost::mpl;
-
         auto pythonManager = python_coupling::Manager::instance();
 
-        typedef bmpl::vector<
-                Field<walberla::real_t, 1>,
-                Field<walberla::real_t, 3>,
-                Field<walberla::real_t, 9>,
-                Field<walberla::real_t, 19>,
-                Field<walberla::real_t, 27>>
-                FieldTypes;
-
-        typedef bmpl::vector<stencil::D2Q9,
-                stencil::D3Q19,
-                stencil::D3Q27>
-                Stencils;
-
-        typedef bmpl::vector<
-                GhostLayerField<real_t,1>,
-                GhostLayerField<real_t,3>>
-                RealFieldTypes;
-
-        typedef bmpl::vector<
-                FlagField<flag_t>>
-                FlagFieldTypes;
         // Field
-        pythonManager->addExporterFunction(field::exportModuleToPython<FieldTypes>);
-        pythonManager->addExporterFunction(field::exportGatherFunctions<FieldTypes>);
-        pythonManager->addBlockDataConversion<FieldTypes>();
+        pythonManager->addExporterFunction(field::exportModuleToPython<Field<walberla::real_t, 1>, Field<walberla::real_t, 3>, Field<walberla::real_t, 9>, Field<walberla::real_t, 19>, Field<walberla::real_t, 27>>);
+        pythonManager->addExporterFunction(field::exportGatherFunctions<Field<walberla::real_t, 1>, Field<walberla::real_t, 3>, Field<walberla::real_t, 9>, Field<walberla::real_t, 19>, Field<walberla::real_t, 27>>);
+        pythonManager->addBlockDataConversion<Field<walberla::real_t, 1>, Field<walberla::real_t, 3>, Field<walberla::real_t, 9>, Field<walberla::real_t, 19>, Field<walberla::real_t, 27>>();
 
         // Blockforest
-        pythonManager->addExporterFunction(blockforest::exportModuleToPython<Stencils>);
-
-        // Timeloop
-        pythonManager->addExporterFunction(timeloop::exportModuleToPython);
-
-        // Postprocessing
-        pythonManager->addExporterFunction( postprocessing::exportModuleToPython<RealFieldTypes, FlagFieldTypes> );
-
-        // Geometry
-        pythonManager->addExporterFunction( geometry::exportModuleToPython );
+        pythonManager->addExporterFunction(blockforest::exportModuleToPython<stencil::D2Q9, stencil::D3Q19, stencil::D3Q27>);
     }
    // clang-format on
 }
diff --git a/apps/showcases/PhaseFieldAllenCahn/GPU/contact.cu b/apps/showcases/PhaseFieldAllenCahn/GPU/contact.cu
index 6a42483ba1376328e71bff03d8bb234dbfb0474d..48d26d1c5d7ec8e9353e48cd9a81731179ce4de4 100644
--- a/apps/showcases/PhaseFieldAllenCahn/GPU/contact.cu
+++ b/apps/showcases/PhaseFieldAllenCahn/GPU/contact.cu
@@ -41,25 +41,25 @@ namespace lbm
 
 namespace internal_boundary_contact
 {
-static FUNC_PREFIX void contact_angle_treatment(uint8_t* RESTRICT const _data_indexVector, double* RESTRICT _data_phase,
+static FUNC_PREFIX void contact_angle_treatment(uint8_t* WALBERLA_RESTRICT const _data_indexVector, double* WALBERLA_RESTRICT _data_phase,
                                                 int64_t const _stride_phase_0, int64_t const _stride_phase_1,
                                                 int64_t const _stride_phase_2, int64_t indexVectorSize, double alpha)
 {
    if (blockDim.x * blockIdx.x + threadIdx.x < indexVectorSize)
    {
-      uint8_t* RESTRICT _data_indexVector_10 = _data_indexVector;
+      uint8_t* WALBERLA_RESTRICT _data_indexVector_10 = _data_indexVector;
       const int32_t x = *((int32_t*) (&_data_indexVector_10[24 * blockDim.x * blockIdx.x + 24 * threadIdx.x]));
-      uint8_t* RESTRICT _data_indexVector_14 = _data_indexVector + 4;
+      uint8_t* WALBERLA_RESTRICT _data_indexVector_14 = _data_indexVector + 4;
       const int32_t y = *((int32_t*) (&_data_indexVector_14[24 * blockDim.x * blockIdx.x + 24 * threadIdx.x]));
-      uint8_t* RESTRICT _data_indexVector_18 = _data_indexVector + 8;
+      uint8_t* WALBERLA_RESTRICT _data_indexVector_18 = _data_indexVector + 8;
       const int32_t z = *((int32_t*) (&_data_indexVector_18[24 * blockDim.x * blockIdx.x + 24 * threadIdx.x]));
-      uint8_t* RESTRICT _data_indexVector_112 = _data_indexVector + 12;
+      uint8_t* WALBERLA_RESTRICT _data_indexVector_112 = _data_indexVector + 12;
       const int32_t nx = *((int32_t*) (&_data_indexVector_112[24 * blockDim.x * blockIdx.x + 24 * threadIdx.x]));
       const int32_t x1 = x + nx;
-      uint8_t* RESTRICT _data_indexVector_116 = _data_indexVector + 16;
+      uint8_t* WALBERLA_RESTRICT _data_indexVector_116 = _data_indexVector + 16;
       const int32_t ny = *((int32_t*) (&_data_indexVector_116[24 * blockDim.x * blockIdx.x + 24 * threadIdx.x]));
       const int32_t y1 = y + ny;
-      uint8_t* RESTRICT _data_indexVector_200 = _data_indexVector + 20;
+      uint8_t* WALBERLA_RESTRICT _data_indexVector_200 = _data_indexVector + 20;
       const int32_t nz = *((int32_t*) (&_data_indexVector_200[24 * blockDim.x * blockIdx.x + 24 * threadIdx.x]));
       const int32_t z1 = z + nz;
 
@@ -67,8 +67,8 @@ static FUNC_PREFIX void contact_angle_treatment(uint8_t* RESTRICT const _data_in
       const double a = cos(alpha);
       const double W = 5;
 
-      double* RESTRICT _phase_wall     = _data_phase + _stride_phase_1 * y + _stride_phase_2 * z;
-      double* RESTRICT _phase_interior = _data_phase + _stride_phase_1 * y1 + _stride_phase_2 * z1;
+      double* WALBERLA_RESTRICT _phase_wall     = _data_phase + _stride_phase_1 * y + _stride_phase_2 * z;
+      double* WALBERLA_RESTRICT _phase_interior = _data_phase + _stride_phase_1 * y1 + _stride_phase_2 * z1;
       if (h < 0.001) { _phase_wall[_stride_phase_0 * x] = 1.0; }
       else if (a > 1e-8 || a < -1e-8)
       {
@@ -107,7 +107,7 @@ void contact::run(IBlock* block, IndexVectors::Type type, cudaStream_t stream)
 
    auto& alpha = this->alpha_;
    WALBERLA_ASSERT_GREATER_EQUAL(0, -int_c(phaseField->nrOfGhostLayers()))
-   double* RESTRICT _data_phase = phaseField->dataAt(0, 0, 0, 0);
+   double* WALBERLA_RESTRICT _data_phase = phaseField->dataAt(0, 0, 0, 0);
    const auto _stride_pdfs_0    = int64_t(phaseField->xStride());
    const auto _stride_pdfs_1    = int64_t(phaseField->yStride());
    const auto _stride_pdfs_2    = int64_t(phaseField->zStride());
diff --git a/apps/showcases/PhaseFieldAllenCahn/GPU/contact.h b/apps/showcases/PhaseFieldAllenCahn/GPU/contact.h
index 2ce5deefbe2f41df6ed295d9a8e9a44e863f6756..f2cd84cd0c67577ab7a40d8ac720fda46f6e7fe3 100644
--- a/apps/showcases/PhaseFieldAllenCahn/GPU/contact.h
+++ b/apps/showcases/PhaseFieldAllenCahn/GPU/contact.h
@@ -32,14 +32,6 @@
 #include <set>
 #include <vector>
 
-#ifdef __GNUC__
-#   define RESTRICT __restrict__
-#elif _MSC_VER
-#   define RESTRICT __restrict
-#else
-#   define RESTRICT
-#endif
-
 namespace walberla
 {
 namespace lbm
diff --git a/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase.cpp b/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase.cpp
index a0029b5408f093cfe5f581ce76102c00cc75d45c..2080ce38cf767c26b2e83ca36d64de5b5ec77cef 100644
--- a/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase.cpp
+++ b/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase.cpp
@@ -34,13 +34,13 @@
 #include "field/AddToStorage.h"
 #include "field/FlagField.h"
 #include "field/communication/PackInfo.h"
-#include "field/python/Exports.h"
 #include "field/vtk/VTKWriter.h"
 
 #include "geometry/InitBoundaryHandling.h"
 
 #include "python_coupling/CreateConfig.h"
 #include "python_coupling/PythonCallback.h"
+#include "python_coupling/export/FieldExports.h"
 
 #include "timeloop/SweepTimeloop.h"
 
@@ -319,8 +319,8 @@ int main(int argc, char** argv)
                python_coupling::PythonCallback callback("at_end_of_time_step");
                if (callback.isCallable())
                {
-                  callback.data().exposePtr("blocks", blocks);
-                  callback.data().exposePtr("time_loop", timeLoop);
+                  callback.data().exposeValue("blocks", blocks);
+                  callback.data().exposeValue( "timeStep", timeLoop->getCurrentTimeStep());
                   callback();
                }
             }
diff --git a/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase_RTI_3D.py b/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase_RTI_3D.py
index d862b1a4844c710528175b8c50e87c6880a1b733..c9798504de79d23c5bbaaa686b209c917d8a63f5 100755
--- a/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase_RTI_3D.py
+++ b/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase_RTI_3D.py
@@ -77,8 +77,8 @@ class Scenario:
         }
 
     @wlb.member_callback
-    def at_end_of_time_step(self, blocks, time_loop):
-        t = time_loop.getCurrentTimeStep()
+    def at_end_of_time_step(self, blocks, **kwargs):
+        t = kwargs['timeStep']
         ny = self.size[1]
         l0 = self.size[0]
         if t % self.dbWriteFrequency == 0:
@@ -88,22 +88,22 @@ class Scenario:
             mass = -100
             spike_data = wlb.field.gather(blocks, 'phase', makeSlice[self.size[0] // 2, :, self.size[2] // 2])
             if spike_data:
-                spike_field = np.asarray(spike_data.buffer()).squeeze()
+                spike_field = np.asarray(spike_data).squeeze()
                 location_of_spike = (np.argmax(spike_field > 0.5) - ny // 2) / l0
 
             bubble_data = wlb.field.gather(blocks, 'phase', makeSlice[0, :, 0])
             if bubble_data:
-                bubble_field = np.asarray(bubble_data.buffer()).squeeze()
+                bubble_field = np.asarray(bubble_data).squeeze()
                 location_of_bubble = (np.argmax(bubble_field > 0.5) - ny // 2) / l0
 
             saddle_data = wlb.field.gather(blocks, 'phase', makeSlice[0, :, self.size[2] // 2])
             if saddle_data:
-                saddle_field = np.asarray(saddle_data.buffer()).squeeze()
+                saddle_field = np.asarray(saddle_data).squeeze()
                 location_of_saddle = (np.argmax(saddle_field > 0.5) - ny // 2) / l0
 
             phase = wlb.field.gather(blocks, 'phase', makeSlice[:, :, :])
             if phase:
-                phase_field = np.asarray(phase.buffer()).squeeze()
+                phase_field = np.asarray(phase).squeeze()
                 mass = np.sum(phase_field)
 
             self.write_result_to_database(t, location_of_spike, location_of_bubble, location_of_saddle, mass)
diff --git a/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase_codegen.py b/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase_codegen.py
index 16bdcfb5adfefe261e024ce29f32c18d01f4984d..48a19621db6f0e62be8c6bc21f6ecc4c65faa90a 100644
--- a/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase_codegen.py
+++ b/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase_codegen.py
@@ -133,8 +133,9 @@ hydro_LB_step = get_collision_assignments_hydro(lb_method=method_hydro,
                                                 density=density,
                                                 velocity_input=u,
                                                 force=force_g,
-                                                optimization={"symbolic_field": g,
-                                                              "symbolic_temporary_field": g_tmp},
+                                                sub_iterations=2,
+                                                symbolic_fields={"symbolic_field": g,
+                                                                 "symbolic_temporary_field": g_tmp},
                                                 kernel_type='collide_only')
 
 hydro_LB_step.set_sub_expressions_from_dict({**{relaxation_rate: relaxation_rate_cutoff},
diff --git a/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase_rising_bubble.py b/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase_rising_bubble.py
index 4294fc7b82519002f765a34f1a7eb81b8b42718b..9f5f08323daf8ee885fd35d510260eda43512daf 100755
--- a/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase_rising_bubble.py
+++ b/apps/showcases/PhaseFieldAllenCahn/GPU/multiphase_rising_bubble.py
@@ -91,12 +91,12 @@ class Scenario:
         }
 
     @wlb.member_callback
-    def at_end_of_time_step(self, blocks, time_loop):
-        t = time_loop.getCurrentTimeStep()
+    def at_end_of_time_step(self, blocks, **kwargs):
+        t = kwargs['timeStep']
         if t % self.dbWriteFrequency == 0:
             wlb_field = wlb.field.gather(blocks, 'phase', makeSlice[:, :, self.size[2] // 2])
             if wlb_field:
-                phase_field = np.asarray(wlb_field.buffer()).squeeze()
+                phase_field = np.asarray(wlb_field).squeeze()
 
                 location_of_gas = np.where(phase_field < 0.5)
                 cov = np.cov(location_of_gas)
@@ -107,7 +107,9 @@ class Scenario:
                 self.yPositions.append(center_of_mass[1])
                 if len(self.yPositions) > 1:
                     speed = self.yPositions[-1] - self.yPositions[-2]
-                    self.write_result_to_database(t, speed, axis_of_the_bubble, center_of_mass)
+                else:
+                    speed = 0
+                self.write_result_to_database(t, speed, axis_of_the_bubble, center_of_mass)
 
                 self.counter += 1
 
diff --git a/apps/tutorials/basics/03_GameOfLife.cpp b/apps/tutorials/basics/03_GameOfLife.cpp
index 92cc1146bdedf42b85c5481c0bc6166a401701cb..868e21bcd7f8c5e62d49f984958fadb96a16221c 100644
--- a/apps/tutorials/basics/03_GameOfLife.cpp
+++ b/apps/tutorials/basics/03_GameOfLife.cpp
@@ -186,7 +186,7 @@ int main( int argc, char ** argv )
             false,                                                                   // one block per process? - "false" means all blocks to one process
             false, false, false );                                                   // no periodicity
 
-   typedef GhostLayerField<real_t,1> ScalarField;
+   using ScalarField = GhostLayerField<real_t, 1>;
    BlockDataID fieldID = field::addToStorage<ScalarField>( blocks,      // block storage
                                                            "My Field",  // name
                                                            real_c(0),   // initial value
diff --git a/apps/tutorials/codegen/03_AdvancedLBMCodegen.cpp b/apps/tutorials/codegen/03_AdvancedLBMCodegen.cpp
index 1f24dfffe59f267ae39d80bcdaeddb71a8db73de..b6ff21d73c085938ea896d3432b0be17cf4164d7 100644
--- a/apps/tutorials/codegen/03_AdvancedLBMCodegen.cpp
+++ b/apps/tutorials/codegen/03_AdvancedLBMCodegen.cpp
@@ -73,7 +73,7 @@ typedef FlagField< flag_t > FlagField_T;
 typedef lbm::CumulantMRTNoSlip NoSlip_T;
 
 #if defined(WALBERLA_BUILD_WITH_CUDA)
-typedef cuda::GPUField< double > GPUField;
+typedef cuda::GPUField< real_t > GPUField;
 #endif
 
 //////////////////////////////////////////
@@ -247,4 +247,4 @@ int main(int argc, char** argv)
 
 } // namespace walberla
 
-int main(int argc, char** argv) { return walberla::main(argc, argv); }
\ No newline at end of file
+int main(int argc, char** argv) { return walberla::main(argc, argv); }
diff --git a/apps/tutorials/codegen/03_AdvancedLBMCodegen.py b/apps/tutorials/codegen/03_AdvancedLBMCodegen.py
index a397909805f5fb18f1f1d3a9235717219d7bdc7d..65a0602f2a4ff2916c87661a38bc5b90c262cec6 100644
--- a/apps/tutorials/codegen/03_AdvancedLBMCodegen.py
+++ b/apps/tutorials/codegen/03_AdvancedLBMCodegen.py
@@ -36,9 +36,8 @@ optimization = {'cse_global': True,
 #   ==================
 
 lbm_params = {'stencil': stencil,
-              'method': 'mrt_raw',
-              'relaxation_rates': [0, 0, 0, omega, omega, omega, 1, 1, 1],
-              'cumulant': True,
+              'method': 'cumulant',
+              'relaxation_rate': omega,
               'compressible': True}
 
 lbm_update_rule = create_lb_update_rule(optimization=optimization,
diff --git a/apps/tutorials/lbm/01_BasicLBM.cpp b/apps/tutorials/lbm/01_BasicLBM.cpp
index 1380a1f10e052d8f09032cda1e943698a193d668..ccfd0632d29ed34e7f43bb724500decb4f3145ff 100644
--- a/apps/tutorials/lbm/01_BasicLBM.cpp
+++ b/apps/tutorials/lbm/01_BasicLBM.cpp
@@ -69,7 +69,7 @@ int main( int argc, char ** argv )
 
    auto boundariesConfig = walberlaEnv.config()->getOneBlock( "Boundaries" );
 
-   typedef lbm::DefaultBoundaryHandlingFactory< LatticeModel_T, FlagField_T > BHFactory;
+   using BHFactory = lbm::DefaultBoundaryHandlingFactory<LatticeModel_T, FlagField_T>;
 
    BlockDataID boundaryHandlingId = BHFactory::addBoundaryHandlingToStorage( blocks, "boundary handling", flagFieldId, pdfFieldId, fluidFlagUID,
                                                                              boundariesConfig.getParameter< Vector3<real_t> >( "velocity0", Vector3<real_t>() ),
diff --git a/apps/tutorials/lbm/02_BasicLBM_ExemplaryExtensions.cpp b/apps/tutorials/lbm/02_BasicLBM_ExemplaryExtensions.cpp
index 3f75b9bf607e014c9d63744e5a3d8b5981408b7c..1c491b90cd3266361401b3a33795985c4336d536 100644
--- a/apps/tutorials/lbm/02_BasicLBM_ExemplaryExtensions.cpp
+++ b/apps/tutorials/lbm/02_BasicLBM_ExemplaryExtensions.cpp
@@ -32,7 +32,7 @@
 
 namespace walberla {
 
-typedef lbm::D2Q9< lbm::collision_model::SRT, false, lbm::force_model::SimpleConstant >  LatticeModel_T;
+using LatticeModel_T = lbm::D2Q9<lbm::collision_model::SRT, false, lbm::force_model::SimpleConstant>;
 
 using Stencil_T = LatticeModel_T::Stencil;
 using CommunicationStencil_T = LatticeModel_T::CommunicationStencil;
@@ -78,7 +78,7 @@ public:
          return;
 
       // data type for storing a position together with a floating point value
-      typedef std::pair< Cell, real_t > PosValuePair;
+      using PosValuePair = std::pair<Cell, real_t>;
 
       // variables for storing the process local minimum/maximum velocity & density
       PosValuePair maxVelocity = std::pair< Cell, real_t >( Cell(), real_c(0) );
@@ -582,7 +582,7 @@ int main( int argc, char ** argv )
 
    auto boundariesConfig = walberlaEnv.config()->getOneBlock( "Boundaries" );
 
-   typedef lbm::DefaultBoundaryHandlingFactory< LatticeModel_T, FlagField_T > BHFactory;
+   using BHFactory = lbm::DefaultBoundaryHandlingFactory<LatticeModel_T, FlagField_T>;
    BlockDataID boundaryHandlingId = BHFactory::addBoundaryHandlingToStorage( blocks, "boundary handling", flagFieldId, pdfFieldId, fluidFlagUID,
                                                                              boundariesConfig.getParameter< Vector3<real_t> >( "velocity0", Vector3<real_t>() ),
                                                                              boundariesConfig.getParameter< Vector3<real_t> >( "velocity1", Vector3<real_t>() ),
diff --git a/apps/tutorials/lbm/03_LBLidDrivenCavity.cpp b/apps/tutorials/lbm/03_LBLidDrivenCavity.cpp
index 7aa2e29bc21be81d2d2877dcea2abb3af21b5662..95f9ef95e8dc4551bcfe508fea5a8bffea6f7371 100644
--- a/apps/tutorials/lbm/03_LBLidDrivenCavity.cpp
+++ b/apps/tutorials/lbm/03_LBLidDrivenCavity.cpp
@@ -75,11 +75,11 @@ using FlagField_T = FlagField<flag_t>; // the flag field: used for marking cells
                                           // (also used for distinguishing between different boundary conditions
                                           //  -> every boundary condition possesses its own, unique flag)
 
-typedef lbm::NoSlip< LatticeModel_T, flag_t >     NoSlip_T; // no slip boundary condition
-typedef lbm::SimpleUBB< LatticeModel_T, flag_t >  UBB_T;    // velocity bounce back boundary condition that internally works with one ...
+using NoSlip_T = lbm::NoSlip<LatticeModel_T, flag_t>; // no slip boundary condition
+using UBB_T = lbm::SimpleUBB<LatticeModel_T, flag_t>;    // velocity bounce back boundary condition that internally works with one ...
                                                             // ... constant velocity that must be set during the setup phase
 
-typedef BoundaryHandling< FlagField_T, Stencil_T, NoSlip_T, UBB_T > BoundaryHandling_T; // the boundary handling, includes a collection of all boundary conditions
+using BoundaryHandling_T = BoundaryHandling<FlagField_T, Stencil_T, NoSlip_T, UBB_T>; // the boundary handling, includes a collection of all boundary conditions
 
 ///////////
 // FLAGS //
diff --git a/apps/tutorials/pde/01_SolvingPDE.cpp b/apps/tutorials/pde/01_SolvingPDE.cpp
index 4b1e7d92f5edfaf572085862668ea085e6c22d96..3091c7597b6d8ef1ba8618eea6350f759d1f5a55 100644
--- a/apps/tutorials/pde/01_SolvingPDE.cpp
+++ b/apps/tutorials/pde/01_SolvingPDE.cpp
@@ -39,7 +39,7 @@
 
 namespace walberla {
 
-typedef GhostLayerField<real_t,1> ScalarField;
+using ScalarField = GhostLayerField<real_t, 1>;
 using Stencil_T = stencil::D2Q5;
 
 
diff --git a/apps/tutorials/pde/02_HeatEquation.cpp b/apps/tutorials/pde/02_HeatEquation.cpp
index dea4433808530f6d64fb25e5d881b214ca09ce6d..54a13fd9dd7151908183c81e1bcc21b573769d1f 100644
--- a/apps/tutorials/pde/02_HeatEquation.cpp
+++ b/apps/tutorials/pde/02_HeatEquation.cpp
@@ -43,7 +43,7 @@
 namespace walberla {
 
 
-typedef GhostLayerField<real_t,1> ScalarField;
+using ScalarField = GhostLayerField<real_t, 1>;
 using Stencil_T = stencil::D2Q5;
 
 
diff --git a/apps/tutorials/pde/03_HeatEquation_Extensions.cpp b/apps/tutorials/pde/03_HeatEquation_Extensions.cpp
index 9f65e2b125fddd3b50ddfb468cbeb05c07f342b0..a1b42906a8a13107516921c168e76dc903e9996d 100644
--- a/apps/tutorials/pde/03_HeatEquation_Extensions.cpp
+++ b/apps/tutorials/pde/03_HeatEquation_Extensions.cpp
@@ -44,7 +44,7 @@
 namespace walberla {
 
 
-typedef GhostLayerField<real_t,1> ScalarField;
+using ScalarField = GhostLayerField<real_t, 1>;
 using Stencil_T = stencil::D2Q5;
 
 
diff --git a/apps/tutorials/pe/01_ConfinedGas.cpp b/apps/tutorials/pe/01_ConfinedGas.cpp
index 6e0d1f7ab989f149270fdd38abe29ce9b5ffb047..df137021bdd824eddbd025cf34a0945838d7a029 100644
--- a/apps/tutorials/pe/01_ConfinedGas.cpp
+++ b/apps/tutorials/pe/01_ConfinedGas.cpp
@@ -35,7 +35,7 @@ namespace walberla {
 using namespace walberla::pe;
 
 //! [BodyTypeTuple]
-typedef std::tuple<Sphere, Plane> BodyTypeTuple ;
+using BodyTypeTuple = std::tuple<Sphere, Plane> ;
 //! [BodyTypeTuple]
 
 int main( int argc, char ** argv )
diff --git a/apps/tutorials/pe/02_ConfinedGasExtended.cpp b/apps/tutorials/pe/02_ConfinedGasExtended.cpp
index 69629eba1e82294ecaff18e5f25c540df05e5c91..4e08411755475040ba6bbf8ad2404f8cec568c18 100644
--- a/apps/tutorials/pe/02_ConfinedGasExtended.cpp
+++ b/apps/tutorials/pe/02_ConfinedGasExtended.cpp
@@ -43,7 +43,7 @@ using namespace walberla::pe;
 using namespace walberla::timing;
 using namespace walberla::pe::raytracing;
 
-typedef std::tuple<Sphere, Plane> BodyTuple ;
+using BodyTuple = std::tuple<Sphere, Plane> ;
 
 int main( int argc, char ** argv )
 {
diff --git a/cmake/FindBoost.cmake b/cmake/FindBoost.cmake
deleted file mode 100644
index 4b16cf6727dcc95c480cb1bd9f616fa61f45ca02..0000000000000000000000000000000000000000
--- a/cmake/FindBoost.cmake
+++ /dev/null
@@ -1,2213 +0,0 @@
-# Distributed under the OSI-approved BSD 3-Clause License.  See accompanying
-# file Copyright.txt or https://cmake.org/licensing for details.
-
-#[=======================================================================[.rst:
-FindBoost
----------
-
-Find Boost include dirs and libraries
-
-Use this module by invoking find_package with the form::
-
-  find_package(Boost
-    [version] [EXACT]      # Minimum or EXACT version e.g. 1.67.0
-    [REQUIRED]             # Fail with error if Boost is not found
-    [COMPONENTS <libs>...] # Boost libraries by their canonical name
-                           # e.g. "date_time" for "libboost_date_time"
-    [OPTIONAL_COMPONENTS <libs>...]
-                           # Optional Boost libraries by their canonical name)
-    )                      # e.g. "date_time" for "libboost_date_time"
-
-This module finds headers and requested component libraries OR a CMake
-package configuration file provided by a "Boost CMake" build.  For the
-latter case skip to the "Boost CMake" section below.  For the former
-case results are reported in variables::
-
-  Boost_FOUND            - True if headers and requested libraries were found
-  Boost_INCLUDE_DIRS     - Boost include directories
-  Boost_LIBRARY_DIRS     - Link directories for Boost libraries
-  Boost_LIBRARIES        - Boost component libraries to be linked
-  Boost_<C>_FOUND        - True if component <C> was found (<C> is upper-case)
-  Boost_<C>_LIBRARY      - Libraries to link for component <C> (may include
-                           target_link_libraries debug/optimized keywords)
-  Boost_VERSION          - BOOST_VERSION value from boost/version.hpp
-  Boost_LIB_VERSION      - Version string appended to library filenames
-  Boost_MAJOR_VERSION    - Boost major version number (X in X.y.z)
-  Boost_MINOR_VERSION    - Boost minor version number (Y in x.Y.z)
-  Boost_SUBMINOR_VERSION - Boost subminor version number (Z in x.y.Z)
-  Boost_VERSION_STRING   - Boost version number in x.y.z format
-  Boost_LIB_DIAGNOSTIC_DEFINITIONS (Windows)
-                         - Pass to add_definitions() to have diagnostic
-                           information about Boost's automatic linking
-                           displayed during compilation
-
-Note that Boost Python components require a Python version suffix
-(Boost 1.67 and later), e.g. ``python36`` or ``python27`` for the
-versions built against Python 3.6 and 2.7, respectively.  This also
-applies to additional components using Python including
-``mpi_python`` and ``numpy``.  Earlier Boost releases may use
-distribution-specific suffixes such as ``2``, ``3`` or ``2.7``.
-These may also be used as suffixes, but note that they are not
-portable.
-
-This module reads hints about search locations from variables::
-
-  BOOST_ROOT             - Preferred installation prefix
-   (or BOOSTROOT)
-  BOOST_INCLUDEDIR       - Preferred include directory e.g. <prefix>/include
-  BOOST_LIBRARYDIR       - Preferred library directory e.g. <prefix>/lib
-  Boost_NO_SYSTEM_PATHS  - Set to ON to disable searching in locations not
-                           specified by these hint variables. Default is OFF.
-  Boost_ADDITIONAL_VERSIONS
-                         - List of Boost versions not known to this module
-                           (Boost install locations may contain the version)
-
-and saves search results persistently in CMake cache entries::
-
-  Boost_INCLUDE_DIR         - Directory containing Boost headers
-  Boost_LIBRARY_DIR_RELEASE - Directory containing release Boost libraries
-  Boost_LIBRARY_DIR_DEBUG   - Directory containing debug Boost libraries
-  Boost_<C>_LIBRARY_DEBUG   - Component <C> library debug variant
-  Boost_<C>_LIBRARY_RELEASE - Component <C> library release variant
-
-The following :prop_tgt:`IMPORTED` targets are also defined::
-
-  Boost::boost                  - Target for header-only dependencies
-                                  (Boost include directory)
-  Boost::<C>                    - Target for specific component dependency
-                                  (shared or static library); <C> is lower-
-                                  case
-  Boost::diagnostic_definitions - interface target to enable diagnostic
-                                  information about Boost's automatic linking
-                                  during compilation (adds BOOST_LIB_DIAGNOSTIC)
-  Boost::disable_autolinking    - interface target to disable automatic
-                                  linking with MSVC (adds BOOST_ALL_NO_LIB)
-  Boost::dynamic_linking        - interface target to enable dynamic linking
-                                  linking with MSVC (adds BOOST_ALL_DYN_LINK)
-
-Implicit dependencies such as Boost::filesystem requiring
-Boost::system will be automatically detected and satisfied, even
-if system is not specified when using find_package and if
-Boost::system is not added to target_link_libraries.  If using
-Boost::thread, then Threads::Threads will also be added automatically.
-
-It is important to note that the imported targets behave differently
-than variables created by this module: multiple calls to
-find_package(Boost) in the same directory or sub-directories with
-different options (e.g. static or shared) will not override the
-values of the targets created by the first call.
-
-Users may set these hints or results as cache entries.  Projects
-should not read these entries directly but instead use the above
-result variables.  Note that some hint names start in upper-case
-"BOOST".  One may specify these as environment variables if they are
-not specified as CMake variables or cache entries.
-
-This module first searches for the Boost header files using the above
-hint variables (excluding BOOST_LIBRARYDIR) and saves the result in
-Boost_INCLUDE_DIR.  Then it searches for requested component libraries
-using the above hints (excluding BOOST_INCLUDEDIR and
-Boost_ADDITIONAL_VERSIONS), "lib" directories near Boost_INCLUDE_DIR,
-and the library name configuration settings below.  It saves the
-library directories in Boost_LIBRARY_DIR_DEBUG and
-Boost_LIBRARY_DIR_RELEASE and individual library
-locations in Boost_<C>_LIBRARY_DEBUG and Boost_<C>_LIBRARY_RELEASE.
-When one changes settings used by previous searches in the same build
-tree (excluding environment variables) this module discards previous
-search results affected by the changes and searches again.
-
-Boost libraries come in many variants encoded in their file name.
-Users or projects may tell this module which variant to find by
-setting variables::
-
-  Boost_USE_DEBUG_LIBS     - Set to ON or OFF to specify whether to search
-                             and use the debug libraries.  Default is ON.
-  Boost_USE_RELEASE_LIBS   - Set to ON or OFF to specify whether to search
-                             and use the release libraries.  Default is ON.
-  Boost_USE_MULTITHREADED  - Set to OFF to use the non-multithreaded
-                             libraries ('mt' tag).  Default is ON.
-  Boost_USE_STATIC_LIBS    - Set to ON to force the use of the static
-                             libraries.  Default is OFF.
-  Boost_USE_STATIC_RUNTIME - Set to ON or OFF to specify whether to use
-                             libraries linked statically to the C++ runtime
-                             ('s' tag).  Default is platform dependent.
-  Boost_USE_DEBUG_RUNTIME  - Set to ON or OFF to specify whether to use
-                             libraries linked to the MS debug C++ runtime
-                             ('g' tag).  Default is ON.
-  Boost_USE_DEBUG_PYTHON   - Set to ON to use libraries compiled with a
-                             debug Python build ('y' tag). Default is OFF.
-  Boost_USE_STLPORT        - Set to ON to use libraries compiled with
-                             STLPort ('p' tag).  Default is OFF.
-  Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS
-                           - Set to ON to use libraries compiled with
-                             STLPort deprecated "native iostreams"
-                             ('n' tag).  Default is OFF.
-  Boost_COMPILER           - Set to the compiler-specific library suffix
-                             (e.g. "-gcc43").  Default is auto-computed
-                             for the C++ compiler in use.  A list may be
-                             used if multiple compatible suffixes should
-                             be tested for, in decreasing order of
-                             preference.
-  Boost_ARCHITECTURE       - Set to the architecture-specific library suffix
-                             (e.g. "-x64").  Default is auto-computed for the
-                             C++ compiler in use.
-  Boost_THREADAPI          - Suffix for "thread" component library name,
-                             such as "pthread" or "win32".  Names with
-                             and without this suffix will both be tried.
-  Boost_NAMESPACE          - Alternate namespace used to build boost with
-                             e.g. if set to "myboost", will search for
-                             myboost_thread instead of boost_thread.
-
-Other variables one may set to control this module are::
-
-  Boost_DEBUG              - Set to ON to enable debug output from FindBoost.
-                             Please enable this before filing any bug report.
-  Boost_DETAILED_FAILURE_MSG
-                           - Set to ON to add detailed information to the
-                             failure message even when the REQUIRED option
-                             is not given to the find_package call.
-  Boost_REALPATH           - Set to ON to resolve symlinks for discovered
-                             libraries to assist with packaging.  For example,
-                             the "system" component library may be resolved to
-                             "/usr/lib/libboost_system.so.1.67.0" instead of
-                             "/usr/lib/libboost_system.so".  This does not
-                             affect linking and should not be enabled unless
-                             the user needs this information.
-  Boost_LIBRARY_DIR        - Default value for Boost_LIBRARY_DIR_RELEASE and
-                             Boost_LIBRARY_DIR_DEBUG.
-
-On Visual Studio and Borland compilers Boost headers request automatic
-linking to corresponding libraries.  This requires matching libraries
-to be linked explicitly or available in the link library search path.
-In this case setting Boost_USE_STATIC_LIBS to OFF may not achieve
-dynamic linking.  Boost automatic linking typically requests static
-libraries with a few exceptions (such as Boost.Python).  Use::
-
-  add_definitions(${Boost_LIB_DIAGNOSTIC_DEFINITIONS})
-
-to ask Boost to report information about automatic linking requests.
-
-Example to find Boost headers only::
-
-  find_package(Boost 1.36.0)
-  if(Boost_FOUND)
-    include_directories(${Boost_INCLUDE_DIRS})
-    add_executable(foo foo.cc)
-  endif()
-
-Example to find Boost libraries and use imported targets::
-
-  find_package(Boost 1.56 REQUIRED COMPONENTS
-               date_time filesystem iostreams)
-  add_executable(foo foo.cc)
-  target_link_libraries(foo Boost::date_time Boost::filesystem
-                            Boost::iostreams)
-
-Example to find Boost Python 3.6 libraries and use imported targets::
-
-  find_package(Boost 1.67 REQUIRED COMPONENTS
-               python36 numpy36)
-  add_executable(foo foo.cc)
-  target_link_libraries(foo Boost::python36 Boost::numpy36)
-
-Example to find Boost headers and some *static* (release only) libraries::
-
-  set(Boost_USE_STATIC_LIBS        ON)  # only find static libs
-  set(Boost_USE_DEBUG_LIBS         OFF) # ignore debug libs and
-  set(Boost_USE_RELEASE_LIBS       ON)  # only find release libs
-  set(Boost_USE_MULTITHREADED      ON)
-  set(Boost_USE_STATIC_RUNTIME    OFF)
-  find_package(Boost 1.66.0 COMPONENTS date_time filesystem system ...)
-  if(Boost_FOUND)
-    include_directories(${Boost_INCLUDE_DIRS})
-    add_executable(foo foo.cc)
-    target_link_libraries(foo ${Boost_LIBRARIES})
-  endif()
-
-Boost CMake
-^^^^^^^^^^^
-
-If Boost was built using the boost-cmake project it provides a package
-configuration file for use with find_package's Config mode.  This
-module looks for the package configuration file called
-BoostConfig.cmake or boost-config.cmake and stores the result in cache
-entry "Boost_DIR".  If found, the package configuration file is loaded
-and this module returns with no further action.  See documentation of
-the Boost CMake package configuration for details on what it provides.
-
-Set Boost_NO_BOOST_CMAKE to ON to disable the search for boost-cmake.
-#]=======================================================================]
-
-# Save project's policies
-cmake_policy(PUSH)
-cmake_policy(SET CMP0057 NEW) # if IN_LIST
-
-#-------------------------------------------------------------------------------
-# Before we go searching, check whether boost-cmake is available, unless the
-# user specifically asked NOT to search for boost-cmake.
-#
-# If Boost_DIR is set, this behaves as any find_package call would. If not,
-# it looks at BOOST_ROOT and BOOSTROOT to find Boost.
-#
-if (NOT Boost_NO_BOOST_CMAKE)
-  # If Boost_DIR is not set, look for BOOSTROOT and BOOST_ROOT as alternatives,
-  # since these are more conventional for Boost.
-  if ("$ENV{Boost_DIR}" STREQUAL "")
-    if (NOT "$ENV{BOOST_ROOT}" STREQUAL "")
-      set(ENV{Boost_DIR} $ENV{BOOST_ROOT})
-    elseif (NOT "$ENV{BOOSTROOT}" STREQUAL "")
-      set(ENV{Boost_DIR} $ENV{BOOSTROOT})
-    endif()
-  endif()
-
-  # Do the same find_package call but look specifically for the CMake version.
-  # Note that args are passed in the Boost_FIND_xxxxx variables, so there is no
-  # need to delegate them to this find_package call.
-  find_package(Boost QUIET NO_MODULE)
-  mark_as_advanced(Boost_DIR)
-
-  # If we found boost-cmake, then we're done.  Print out what we found.
-  # Otherwise let the rest of the module try to find it.
-  if (Boost_FOUND)
-    message(STATUS "Boost ${Boost_FIND_VERSION} found.")
-    if (Boost_FIND_COMPONENTS)
-      message(STATUS "Found Boost components:\n   ${Boost_FIND_COMPONENTS}")
-    endif()
-    # Restore project's policies
-    cmake_policy(POP)
-    return()
-  endif()
-endif()
-
-
-#-------------------------------------------------------------------------------
-#  FindBoost functions & macros
-#
-
-############################################
-#
-# Check the existence of the libraries.
-#
-############################################
-# This macro was taken directly from the FindQt4.cmake file that is included
-# with the CMake distribution. This is NOT my work. All work was done by the
-# original authors of the FindQt4.cmake file. Only minor modifications were
-# made to remove references to Qt and make this file more generally applicable
-# And ELSE/ENDIF pairs were removed for readability.
-#########################################################################
-
-macro(_Boost_ADJUST_LIB_VARS basename)
-  if(Boost_INCLUDE_DIR )
-    if(Boost_${basename}_LIBRARY_DEBUG AND Boost_${basename}_LIBRARY_RELEASE)
-      # if the generator is multi-config or if CMAKE_BUILD_TYPE is set for
-      # single-config generators, set optimized and debug libraries
-      get_property(_isMultiConfig GLOBAL PROPERTY GENERATOR_IS_MULTI_CONFIG)
-      if(_isMultiConfig OR CMAKE_BUILD_TYPE)
-        set(Boost_${basename}_LIBRARY optimized ${Boost_${basename}_LIBRARY_RELEASE} debug ${Boost_${basename}_LIBRARY_DEBUG})
-      else()
-        # For single-config generators where CMAKE_BUILD_TYPE has no value,
-        # just use the release libraries
-        set(Boost_${basename}_LIBRARY ${Boost_${basename}_LIBRARY_RELEASE} )
-      endif()
-      # FIXME: This probably should be set for both cases
-      set(Boost_${basename}_LIBRARIES optimized ${Boost_${basename}_LIBRARY_RELEASE} debug ${Boost_${basename}_LIBRARY_DEBUG})
-    endif()
-
-    # if only the release version was found, set the debug variable also to the release version
-    if(Boost_${basename}_LIBRARY_RELEASE AND NOT Boost_${basename}_LIBRARY_DEBUG)
-      set(Boost_${basename}_LIBRARY_DEBUG ${Boost_${basename}_LIBRARY_RELEASE})
-      set(Boost_${basename}_LIBRARY       ${Boost_${basename}_LIBRARY_RELEASE})
-      set(Boost_${basename}_LIBRARIES     ${Boost_${basename}_LIBRARY_RELEASE})
-    endif()
-
-    # if only the debug version was found, set the release variable also to the debug version
-    if(Boost_${basename}_LIBRARY_DEBUG AND NOT Boost_${basename}_LIBRARY_RELEASE)
-      set(Boost_${basename}_LIBRARY_RELEASE ${Boost_${basename}_LIBRARY_DEBUG})
-      set(Boost_${basename}_LIBRARY         ${Boost_${basename}_LIBRARY_DEBUG})
-      set(Boost_${basename}_LIBRARIES       ${Boost_${basename}_LIBRARY_DEBUG})
-    endif()
-
-    # If the debug & release library ends up being the same, omit the keywords
-    if("${Boost_${basename}_LIBRARY_RELEASE}" STREQUAL "${Boost_${basename}_LIBRARY_DEBUG}")
-      set(Boost_${basename}_LIBRARY   ${Boost_${basename}_LIBRARY_RELEASE} )
-      set(Boost_${basename}_LIBRARIES ${Boost_${basename}_LIBRARY_RELEASE} )
-    endif()
-
-    if(Boost_${basename}_LIBRARY AND Boost_${basename}_HEADER)
-      set(Boost_${basename}_FOUND ON)
-      if("x${basename}" STREQUAL "xTHREAD" AND NOT TARGET Threads::Threads)
-        string(APPEND Boost_ERROR_REASON_THREAD " (missing dependency: Threads)")
-        set(Boost_THREAD_FOUND OFF)
-      endif()
-    endif()
-
-  endif()
-  # Make variables changeable to the advanced user
-  mark_as_advanced(
-      Boost_${basename}_LIBRARY_RELEASE
-      Boost_${basename}_LIBRARY_DEBUG
-  )
-endmacro()
-
-# Detect changes in used variables.
-# Compares the current variable value with the last one.
-# In short form:
-# v != v_LAST                      -> CHANGED = 1
-# v is defined, v_LAST not         -> CHANGED = 1
-# v is not defined, but v_LAST is  -> CHANGED = 1
-# otherwise                        -> CHANGED = 0
-# CHANGED is returned in variable named ${changed_var}
-macro(_Boost_CHANGE_DETECT changed_var)
-  set(${changed_var} 0)
-  foreach(v ${ARGN})
-    if(DEFINED _Boost_COMPONENTS_SEARCHED)
-      if(${v})
-        if(_${v}_LAST)
-          string(COMPARE NOTEQUAL "${${v}}" "${_${v}_LAST}" _${v}_CHANGED)
-        else()
-          set(_${v}_CHANGED 1)
-        endif()
-      elseif(_${v}_LAST)
-        set(_${v}_CHANGED 1)
-      endif()
-      if(_${v}_CHANGED)
-        set(${changed_var} 1)
-      endif()
-    else()
-      set(_${v}_CHANGED 0)
-    endif()
-  endforeach()
-endmacro()
-
-#
-# Find the given library (var).
-# Use 'build_type' to support different lib paths for RELEASE or DEBUG builds
-#
-macro(_Boost_FIND_LIBRARY var build_type)
-
-  find_library(${var} ${ARGN})
-
-  if(${var})
-    # If this is the first library found then save Boost_LIBRARY_DIR_[RELEASE,DEBUG].
-    if(NOT Boost_LIBRARY_DIR_${build_type})
-      get_filename_component(_dir "${${var}}" PATH)
-      set(Boost_LIBRARY_DIR_${build_type} "${_dir}" CACHE PATH "Boost library directory ${build_type}" FORCE)
-    endif()
-  elseif(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT)
-    # Try component-specific hints but do not save Boost_LIBRARY_DIR_[RELEASE,DEBUG].
-    find_library(${var} HINTS ${_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT} ${ARGN})
-  endif()
-
-  # If Boost_LIBRARY_DIR_[RELEASE,DEBUG] is known then search only there.
-  if(Boost_LIBRARY_DIR_${build_type})
-    set(_boost_LIBRARY_SEARCH_DIRS_${build_type} ${Boost_LIBRARY_DIR_${build_type}} NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
-    if(Boost_DEBUG)
-      message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-        " Boost_LIBRARY_DIR_${build_type} = ${Boost_LIBRARY_DIR_${build_type}}"
-        " _boost_LIBRARY_SEARCH_DIRS_${build_type} = ${_boost_LIBRARY_SEARCH_DIRS_${build_type}}")
-    endif()
-  endif()
-endmacro()
-
-#-------------------------------------------------------------------------------
-
-# Convert CMAKE_CXX_COMPILER_VERSION to boost compiler suffix version.
-function(_Boost_COMPILER_DUMPVERSION _OUTPUT_VERSION _OUTPUT_VERSION_MAJOR _OUTPUT_VERSION_MINOR)
-  string(REGEX REPLACE "([0-9]+)\\.([0-9]+)(\\.[0-9]+)?" "\\1"
-    _boost_COMPILER_VERSION_MAJOR "${CMAKE_CXX_COMPILER_VERSION}")
-  string(REGEX REPLACE "([0-9]+)\\.([0-9]+)(\\.[0-9]+)?" "\\2"
-    _boost_COMPILER_VERSION_MINOR "${CMAKE_CXX_COMPILER_VERSION}")
-
-  set(_boost_COMPILER_VERSION "${_boost_COMPILER_VERSION_MAJOR}${_boost_COMPILER_VERSION_MINOR}")
-
-  set(${_OUTPUT_VERSION} ${_boost_COMPILER_VERSION} PARENT_SCOPE)
-  set(${_OUTPUT_VERSION_MAJOR} ${_boost_COMPILER_VERSION_MAJOR} PARENT_SCOPE)
-  set(${_OUTPUT_VERSION_MINOR} ${_boost_COMPILER_VERSION_MINOR} PARENT_SCOPE)
-endfunction()
-
-#
-# Take a list of libraries with "thread" in it
-# and prepend duplicates with "thread_${Boost_THREADAPI}"
-# at the front of the list
-#
-function(_Boost_PREPEND_LIST_WITH_THREADAPI _output)
-  set(_orig_libnames ${ARGN})
-  string(REPLACE "thread" "thread_${Boost_THREADAPI}" _threadapi_libnames "${_orig_libnames}")
-  set(${_output} ${_threadapi_libnames} ${_orig_libnames} PARENT_SCOPE)
-endfunction()
-
-#
-# If a library is found, replace its cache entry with its REALPATH
-#
-function(_Boost_SWAP_WITH_REALPATH _library _docstring)
-  if(${_library})
-    get_filename_component(_boost_filepathreal ${${_library}} REALPATH)
-    unset(${_library} CACHE)
-    set(${_library} ${_boost_filepathreal} CACHE FILEPATH "${_docstring}")
-  endif()
-endfunction()
-
-function(_Boost_CHECK_SPELLING _var)
-  if(${_var})
-    string(TOUPPER ${_var} _var_UC)
-    message(FATAL_ERROR "ERROR: ${_var} is not the correct spelling.  The proper spelling is ${_var_UC}.")
-  endif()
-endfunction()
-
-# Guesses Boost's compiler prefix used in built library names
-# Returns the guess by setting the variable pointed to by _ret
-function(_Boost_GUESS_COMPILER_PREFIX _ret)
-  if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xIntel")
-    if(WIN32)
-      set (_boost_COMPILER "-iw")
-    else()
-      set (_boost_COMPILER "-il")
-    endif()
-  elseif (GHSMULTI)
-    set(_boost_COMPILER "-ghs")
-  elseif("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC")
-    if(MSVC_TOOLSET_VERSION GREATER_EQUAL 141)
-      set(_boost_COMPILER "-vc141;-vc140")
-    elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 80)
-      set(_boost_COMPILER "-vc${MSVC_TOOLSET_VERSION}")
-    elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13.10)
-      set(_boost_COMPILER "-vc71")
-    elseif(NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS 13) # Good luck!
-      set(_boost_COMPILER "-vc7") # yes, this is correct
-    else() # VS 6.0 Good luck!
-      set(_boost_COMPILER "-vc6") # yes, this is correct
-    endif()
-  elseif (BORLAND)
-    set(_boost_COMPILER "-bcb")
-  elseif(CMAKE_CXX_COMPILER_ID STREQUAL "SunPro")
-    set(_boost_COMPILER "-sw")
-  elseif(CMAKE_CXX_COMPILER_ID STREQUAL "XL")
-    set(_boost_COMPILER "-xlc")
-  elseif (MINGW)
-    if(${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION} VERSION_LESS 1.34)
-        set(_boost_COMPILER "-mgw") # no GCC version encoding prior to 1.34
-    else()
-      _Boost_COMPILER_DUMPVERSION(_boost_COMPILER_VERSION _boost_COMPILER_VERSION_MAJOR _boost_COMPILER_VERSION_MINOR)
-      set(_boost_COMPILER "-mgw${_boost_COMPILER_VERSION}")
-    endif()
-  elseif (UNIX)
-    _Boost_COMPILER_DUMPVERSION(_boost_COMPILER_VERSION _boost_COMPILER_VERSION_MAJOR _boost_COMPILER_VERSION_MINOR)
-    if(NOT Boost_VERSION VERSION_LESS 106900)
-      # From GCC 5 and clang 4, versioning changes and minor becomes patch.
-      # For those compilers, patch is exclude from compiler tag in Boost 1.69+ library naming.
-      if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND _boost_COMPILER_VERSION_MAJOR VERSION_GREATER 4)
-        set(_boost_COMPILER_VERSION "${_boost_COMPILER_VERSION_MAJOR}")
-      elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND _boost_COMPILER_VERSION_MAJOR VERSION_GREATER 3)
-        set(_boost_COMPILER_VERSION "${_boost_COMPILER_VERSION_MAJOR}")
-      endif()
-    endif()
-
-    if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
-      if(${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION} VERSION_LESS 1.34)
-        set(_boost_COMPILER "-gcc") # no GCC version encoding prior to 1.34
-      else()
-        # Determine which version of GCC we have.
-        if(APPLE)
-          if(Boost_MINOR_VERSION)
-            if(${Boost_MINOR_VERSION} GREATER 35)
-              # In Boost 1.36.0 and newer, the mangled compiler name used
-              # on macOS/Darwin is "xgcc".
-              set(_boost_COMPILER "-xgcc${_boost_COMPILER_VERSION}")
-            else()
-              # In Boost <= 1.35.0, there is no mangled compiler name for
-              # the macOS/Darwin version of GCC.
-              set(_boost_COMPILER "")
-            endif()
-          else()
-            # We don't know the Boost version, so assume it's
-            # pre-1.36.0.
-            set(_boost_COMPILER "")
-          endif()
-        else()
-          set(_boost_COMPILER "-gcc${_boost_COMPILER_VERSION}")
-        endif()
-      endif()
-    elseif(CMAKE_CXX_COMPILER_ID STREQUAL "Clang")
-      # TODO: Find out any Boost version constraints vs clang support.
-      set(_boost_COMPILER "-clang${_boost_COMPILER_VERSION}")
-    endif()
-  else()
-    # TODO at least Boost_DEBUG here?
-    set(_boost_COMPILER "")
-  endif()
-  set(${_ret} ${_boost_COMPILER} PARENT_SCOPE)
-endfunction()
-
-#
-# Get component dependencies.  Requires the dependencies to have been
-# defined for the Boost release version.
-#
-# component - the component to check
-# _ret - list of library dependencies
-#
-function(_Boost_COMPONENT_DEPENDENCIES component _ret)
-  # Note: to add a new Boost release, run
-  #
-  #   % cmake -DBOOST_DIR=/path/to/boost/source -P Utilities/Scripts/BoostScanDeps.cmake
-  #
-  # The output may be added in a new block below.  If it's the same as
-  # the previous release, simply update the version range of the block
-  # for the previous release.  Also check if any new components have
-  # been added, and add any new components to
-  # _Boost_COMPONENT_HEADERS.
-  #
-  # This information was originally generated by running
-  # BoostScanDeps.cmake against every boost release to date supported
-  # by FindBoost:
-  #
-  #   % for version in /path/to/boost/sources/*
-  #     do
-  #       cmake -DBOOST_DIR=$version -P Utilities/Scripts/BoostScanDeps.cmake
-  #     done
-  #
-  # The output was then updated by search and replace with these regexes:
-  #
-  # - Strip message(STATUS) prefix dashes
-  #   s;^-- ;;
-  # - Indent
-  #   s;^set(;    set(;;
-  # - Add conditionals
-  #   s;Scanning /path/to/boost/sources/boost_\(.*\)_\(.*\)_\(.*);  elseif(NOT Boost_VERSION VERSION_LESS \10\20\3 AND Boost_VERSION VERSION_LESS xxxx);
-  #
-  # This results in the logic seen below, but will require the xxxx
-  # replacing with the following Boost release version (or the next
-  # minor version to be released, e.g. 1.59 was the latest at the time
-  # of writing, making 1.60 the next, so 106000 is the needed version
-  # number).  Identical consecutive releases were then merged together
-  # by updating the end range of the first block and removing the
-  # following redundant blocks.
-  #
-  # Running the script against all historical releases should be
-  # required only if the BoostScanDeps.cmake script logic is changed.
-  # The addition of a new release should only require it to be run
-  # against the new release.
-
-  # Handle Python version suffixes
-  if(component MATCHES "^(python|mpi_python|numpy)([0-9][0-9]?|[0-9]\\.[0-9])\$")
-    set(component "${CMAKE_MATCH_1}")
-    set(component_python_version "${CMAKE_MATCH_2}")
-  endif()
-
-  set(_Boost_IMPORTED_TARGETS TRUE)
-  if(Boost_VERSION AND Boost_VERSION VERSION_LESS 103300)
-    message(WARNING "Imported targets and dependency information not available for Boost version ${Boost_VERSION} (all versions older than 1.33)")
-    set(_Boost_IMPORTED_TARGETS FALSE)
-  elseif(NOT Boost_VERSION VERSION_LESS 103300 AND Boost_VERSION VERSION_LESS 103500)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex thread)
-    set(_Boost_REGEX_DEPENDENCIES thread)
-    set(_Boost_WAVE_DEPENDENCIES filesystem thread)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 103500 AND Boost_VERSION VERSION_LESS 103600)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system thread)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 103600 AND Boost_VERSION VERSION_LESS 103800)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system thread)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 103800 AND Boost_VERSION VERSION_LESS 104300)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_THREAD_DEPENDENCIES date_time)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system thread date_time)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 104300 AND Boost_VERSION VERSION_LESS 104400)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l random)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_THREAD_DEPENDENCIES date_time)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system thread date_time)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 104400 AND Boost_VERSION VERSION_LESS 104500)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l random serialization)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_THREAD_DEPENDENCIES date_time)
-    set(_Boost_WAVE_DEPENDENCIES serialization filesystem system thread date_time)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 104500 AND Boost_VERSION VERSION_LESS 104700)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l random)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_THREAD_DEPENDENCIES date_time)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread date_time)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 104700 AND Boost_VERSION VERSION_LESS 104800)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l random)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_THREAD_DEPENDENCIES date_time)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread date_time)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 104800 AND Boost_VERSION VERSION_LESS 105000)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l random)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_THREAD_DEPENDENCIES date_time)
-    set(_Boost_TIMER_DEPENDENCIES chrono system)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread date_time)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 105000 AND Boost_VERSION VERSION_LESS 105300)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l regex random)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_THREAD_DEPENDENCIES chrono system date_time)
-    set(_Boost_TIMER_DEPENDENCIES chrono system)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 105300 AND Boost_VERSION VERSION_LESS 105400)
-    set(_Boost_ATOMIC_DEPENDENCIES thread chrono system date_time)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l regex random)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic)
-    set(_Boost_TIMER_DEPENDENCIES chrono system)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 105400 AND Boost_VERSION VERSION_LESS 105500)
-    set(_Boost_ATOMIC_DEPENDENCIES thread chrono system date_time)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES log_setup date_time system filesystem thread regex chrono)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l regex random)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic)
-    set(_Boost_TIMER_DEPENDENCIES chrono system)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 105500 AND Boost_VERSION VERSION_LESS 105600)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_COROUTINE_DEPENDENCIES context system)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES log_setup date_time system filesystem thread regex chrono)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l regex random)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic)
-    set(_Boost_TIMER_DEPENDENCIES chrono system)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 105600 AND Boost_VERSION VERSION_LESS 105900)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_COROUTINE_DEPENDENCIES context system)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES log_setup date_time system filesystem thread regex chrono)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_RANDOM_DEPENDENCIES system)
-    set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic)
-    set(_Boost_TIMER_DEPENDENCIES chrono system)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 105900 AND Boost_VERSION VERSION_LESS 106000)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_COROUTINE_DEPENDENCIES context system)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES log_setup date_time system filesystem thread regex chrono atomic)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_RANDOM_DEPENDENCIES system)
-    set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic)
-    set(_Boost_TIMER_DEPENDENCIES chrono system)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 106000 AND Boost_VERSION VERSION_LESS 106100)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_COROUTINE_DEPENDENCIES context system)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_RANDOM_DEPENDENCIES system)
-    set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic)
-    set(_Boost_TIMER_DEPENDENCIES chrono system)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 106100 AND Boost_VERSION VERSION_LESS 106200)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time)
-    set(_Boost_COROUTINE_DEPENDENCIES context system)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_RANDOM_DEPENDENCIES system)
-    set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 106200 AND Boost_VERSION VERSION_LESS 106300)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time)
-    set(_Boost_COROUTINE_DEPENDENCIES context system)
-    set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_RANDOM_DEPENDENCIES system)
-    set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 106300 AND Boost_VERSION VERSION_LESS 106500)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time)
-    set(_Boost_COROUTINE_DEPENDENCIES context system)
-    set(_Boost_COROUTINE2_DEPENDENCIES context fiber thread chrono system date_time)
-    set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_RANDOM_DEPENDENCIES system)
-    set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 106500 AND Boost_VERSION VERSION_LESS 106700)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time)
-    set(_Boost_COROUTINE_DEPENDENCIES context system)
-    set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_NUMPY_DEPENDENCIES python${component_python_version})
-    set(_Boost_RANDOM_DEPENDENCIES system)
-    set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic)
-    set(_Boost_TIMER_DEPENDENCIES chrono system)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 106700 AND Boost_VERSION VERSION_LESS 106800)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time)
-    set(_Boost_COROUTINE_DEPENDENCIES context system)
-    set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_NUMPY_DEPENDENCIES python${component_python_version})
-    set(_Boost_RANDOM_DEPENDENCIES system)
-    set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic)
-    set(_Boost_TIMER_DEPENDENCIES chrono system)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 106800 AND Boost_VERSION VERSION_LESS 106900)
-    set(_Boost_CHRONO_DEPENDENCIES system)
-    set(_Boost_CONTEXT_DEPENDENCIES thread chrono system date_time)
-    set(_Boost_CONTRACT_DEPENDENCIES thread chrono system date_time)
-    set(_Boost_COROUTINE_DEPENDENCIES context system)
-    set(_Boost_FIBER_DEPENDENCIES context thread chrono system date_time)
-    set(_Boost_FILESYSTEM_DEPENDENCIES system)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES date_time log_setup system filesystem thread regex chrono atomic)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_NUMPY_DEPENDENCIES python${component_python_version})
-    set(_Boost_RANDOM_DEPENDENCIES system)
-    set(_Boost_THREAD_DEPENDENCIES chrono system date_time atomic)
-    set(_Boost_TIMER_DEPENDENCIES chrono system)
-    set(_Boost_WAVE_DEPENDENCIES filesystem system serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 106900 AND Boost_VERSION VERSION_LESS 107000)
-    set(_Boost_CONTRACT_DEPENDENCIES thread chrono date_time)
-    set(_Boost_COROUTINE_DEPENDENCIES context)
-    set(_Boost_FIBER_DEPENDENCIES context)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES date_time log_setup filesystem thread regex chrono atomic)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_NUMPY_DEPENDENCIES python${component_python_version})
-    set(_Boost_THREAD_DEPENDENCIES chrono date_time atomic)
-    set(_Boost_TIMER_DEPENDENCIES chrono system)
-    set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 107000 AND Boost_VERSION VERSION_LESS 107200)
-    set(_Boost_CONTRACT_DEPENDENCIES thread chrono date_time)
-    set(_Boost_COROUTINE_DEPENDENCIES context)
-    set(_Boost_FIBER_DEPENDENCIES context)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES date_time log_setup filesystem thread regex chrono atomic)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_NUMPY_DEPENDENCIES python${component_python_version})
-    set(_Boost_THREAD_DEPENDENCIES chrono date_time atomic)
-    set(_Boost_TIMER_DEPENDENCIES chrono)
-    set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  elseif(NOT Boost_VERSION VERSION_LESS 107200 AND Boost_VERSION VERSION_LESS 107300)
-    set(_Boost_CONTRACT_DEPENDENCIES thread chrono date_time)
-    set(_Boost_COROUTINE_DEPENDENCIES context)
-    set(_Boost_FIBER_DEPENDENCIES context)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES date_time log_setup filesystem thread regex chrono atomic)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l chrono atomic)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_NUMPY_DEPENDENCIES python${component_python_version})
-    set(_Boost_THREAD_DEPENDENCIES chrono date_time atomic)
-    set(_Boost_TIMER_DEPENDENCIES chrono)
-    set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-  else()
-    set(_Boost_CONTRACT_DEPENDENCIES thread chrono date_time)
-    set(_Boost_COROUTINE_DEPENDENCIES context)
-    set(_Boost_FIBER_DEPENDENCIES context)
-    set(_Boost_IOSTREAMS_DEPENDENCIES regex)
-    set(_Boost_LOG_DEPENDENCIES date_time log_setup filesystem thread regex chrono atomic)
-    set(_Boost_MATH_DEPENDENCIES math_c99 math_c99f math_c99l math_tr1 math_tr1f math_tr1l atomic)
-    set(_Boost_MPI_DEPENDENCIES serialization)
-    set(_Boost_MPI_PYTHON_DEPENDENCIES python${component_python_version} mpi serialization)
-    set(_Boost_NUMPY_DEPENDENCIES python${component_python_version})
-    set(_Boost_THREAD_DEPENDENCIES chrono date_time atomic)
-    set(_Boost_TIMER_DEPENDENCIES chrono)
-    set(_Boost_WAVE_DEPENDENCIES filesystem serialization thread chrono date_time atomic)
-    set(_Boost_WSERIALIZATION_DEPENDENCIES serialization)
-    if(Boost_VERSION VERSION_GREATER_EQUAL 107400)
-      message(WARNING "New Boost version may have incorrect or missing dependencies and imported targets")
-    endif()
-  endif()
-
-
-  string(TOUPPER ${component} uppercomponent)
-  set(${_ret} ${_Boost_${uppercomponent}_DEPENDENCIES} PARENT_SCOPE)
-  set(_Boost_IMPORTED_TARGETS ${_Boost_IMPORTED_TARGETS} PARENT_SCOPE)
-
-  string(REGEX REPLACE ";" " " _boost_DEPS_STRING "${_Boost_${uppercomponent}_DEPENDENCIES}")
-  if (NOT _boost_DEPS_STRING)
-    set(_boost_DEPS_STRING "(none)")
-  endif()
-  # message(STATUS "Dependencies for Boost::${component}: ${_boost_DEPS_STRING}")
-endfunction()
-
-#
-# Get component headers.  This is the primary header (or headers) for
-# a given component, and is used to check that the headers are present
-# as well as the library itself as an extra sanity check of the build
-# environment.
-#
-# component - the component to check
-# _hdrs
-#
-function(_Boost_COMPONENT_HEADERS component _hdrs)
-  # Handle Python version suffixes
-  if(component MATCHES "^(python|mpi_python|numpy)([0-9][0-9]?|[0-9]\\.[0-9])\$")
-    set(component "${CMAKE_MATCH_1}")
-    set(component_python_version "${CMAKE_MATCH_2}")
-  endif()
-
-  # Note: new boost components will require adding here.  The header
-  # must be present in all versions of Boost providing a library.
-  set(_Boost_ATOMIC_HEADERS              "boost/atomic.hpp")
-  set(_Boost_CHRONO_HEADERS              "boost/chrono.hpp")
-  set(_Boost_CONTAINER_HEADERS           "boost/container/container_fwd.hpp")
-  set(_Boost_CONTRACT_HEADERS            "boost/contract.hpp")
-  if(Boost_VERSION VERSION_LESS 106100)
-    set(_Boost_CONTEXT_HEADERS           "boost/context/all.hpp")
-  else()
-    set(_Boost_CONTEXT_HEADERS           "boost/context/detail/fcontext.hpp")
-  endif()
-  set(_Boost_COROUTINE_HEADERS           "boost/coroutine/all.hpp")
-  set(_Boost_DATE_TIME_HEADERS           "boost/date_time/date.hpp")
-  set(_Boost_EXCEPTION_HEADERS           "boost/exception/exception.hpp")
-  set(_Boost_FIBER_HEADERS               "boost/fiber/all.hpp")
-  set(_Boost_FILESYSTEM_HEADERS          "boost/filesystem/path.hpp")
-  set(_Boost_GRAPH_HEADERS               "boost/graph/adjacency_list.hpp")
-  set(_Boost_GRAPH_PARALLEL_HEADERS      "boost/graph/adjacency_list.hpp")
-  set(_Boost_IOSTREAMS_HEADERS           "boost/iostreams/stream.hpp")
-  set(_Boost_LOCALE_HEADERS              "boost/locale.hpp")
-  set(_Boost_LOG_HEADERS                 "boost/log/core.hpp")
-  set(_Boost_LOG_SETUP_HEADERS           "boost/log/detail/setup_config.hpp")
-  set(_Boost_MATH_HEADERS                "boost/math_fwd.hpp")
-  set(_Boost_MATH_C99_HEADERS            "boost/math/tr1.hpp")
-  set(_Boost_MATH_C99F_HEADERS           "boost/math/tr1.hpp")
-  set(_Boost_MATH_C99L_HEADERS           "boost/math/tr1.hpp")
-  set(_Boost_MATH_TR1_HEADERS            "boost/math/tr1.hpp")
-  set(_Boost_MATH_TR1F_HEADERS           "boost/math/tr1.hpp")
-  set(_Boost_MATH_TR1L_HEADERS           "boost/math/tr1.hpp")
-  set(_Boost_MPI_HEADERS                 "boost/mpi.hpp")
-  set(_Boost_MPI_PYTHON_HEADERS          "boost/mpi/python/config.hpp")
-  set(_Boost_NUMPY_HEADERS               "boost/python/numpy.hpp")
-  set(_Boost_PRG_EXEC_MONITOR_HEADERS    "boost/test/prg_exec_monitor.hpp")
-  set(_Boost_PROGRAM_OPTIONS_HEADERS     "boost/program_options.hpp")
-  set(_Boost_PYTHON_HEADERS              "boost/python.hpp")
-  set(_Boost_RANDOM_HEADERS              "boost/random.hpp")
-  set(_Boost_REGEX_HEADERS               "boost/regex.hpp")
-  set(_Boost_SERIALIZATION_HEADERS       "boost/serialization/serialization.hpp")
-  set(_Boost_SIGNALS_HEADERS             "boost/signals.hpp")
-  set(_Boost_STACKTRACE_ADDR2LINE_HEADERS "boost/stacktrace.hpp")
-  set(_Boost_STACKTRACE_BACKTRACE_HEADERS "boost/stacktrace.hpp")
-  set(_Boost_STACKTRACE_BASIC_HEADERS    "boost/stacktrace.hpp")
-  set(_Boost_STACKTRACE_NOOP_HEADERS     "boost/stacktrace.hpp")
-  set(_Boost_STACKTRACE_WINDBG_CACHED_HEADERS "boost/stacktrace.hpp")
-  set(_Boost_STACKTRACE_WINDBG_HEADERS   "boost/stacktrace.hpp")
-  set(_Boost_SYSTEM_HEADERS              "boost/system/config.hpp")
-  set(_Boost_TEST_EXEC_MONITOR_HEADERS   "boost/test/test_exec_monitor.hpp")
-  set(_Boost_THREAD_HEADERS              "boost/thread.hpp")
-  set(_Boost_TIMER_HEADERS               "boost/timer.hpp")
-  set(_Boost_TYPE_ERASURE_HEADERS        "boost/type_erasure/config.hpp")
-  set(_Boost_UNIT_TEST_FRAMEWORK_HEADERS "boost/test/framework.hpp")
-  set(_Boost_WAVE_HEADERS                "boost/wave.hpp")
-  set(_Boost_WSERIALIZATION_HEADERS      "boost/archive/text_wiarchive.hpp")
-  if(WIN32)
-    set(_Boost_BZIP2_HEADERS             "boost/iostreams/filter/bzip2.hpp")
-    set(_Boost_ZLIB_HEADERS              "boost/iostreams/filter/zlib.hpp")
-  endif()
-
-  string(TOUPPER ${component} uppercomponent)
-  set(${_hdrs} ${_Boost_${uppercomponent}_HEADERS} PARENT_SCOPE)
-
-  string(REGEX REPLACE ";" " " _boost_HDRS_STRING "${_Boost_${uppercomponent}_HEADERS}")
-  if (NOT _boost_HDRS_STRING)
-    set(_boost_HDRS_STRING "(none)")
-  endif()
-  # message(STATUS "Headers for Boost::${component}: ${_boost_HDRS_STRING}")
-endfunction()
-
-#
-# Determine if any missing dependencies require adding to the component list.
-#
-# Sets _Boost_${COMPONENT}_DEPENDENCIES for each required component,
-# plus _Boost_IMPORTED_TARGETS (TRUE if imported targets should be
-# defined; FALSE if dependency information is unavailable).
-#
-# componentvar - the component list variable name
-# extravar - the indirect dependency list variable name
-#
-#
-function(_Boost_MISSING_DEPENDENCIES componentvar extravar)
-  # _boost_unprocessed_components - list of components requiring processing
-  # _boost_processed_components - components already processed (or currently being processed)
-  # _boost_new_components - new components discovered for future processing
-  #
-  list(APPEND _boost_unprocessed_components ${${componentvar}})
-
-  while(_boost_unprocessed_components)
-    list(APPEND _boost_processed_components ${_boost_unprocessed_components})
-    foreach(component ${_boost_unprocessed_components})
-      string(TOUPPER ${component} uppercomponent)
-      set(${_ret} ${_Boost_${uppercomponent}_DEPENDENCIES} PARENT_SCOPE)
-      _Boost_COMPONENT_DEPENDENCIES("${component}" _Boost_${uppercomponent}_DEPENDENCIES)
-      set(_Boost_${uppercomponent}_DEPENDENCIES ${_Boost_${uppercomponent}_DEPENDENCIES} PARENT_SCOPE)
-      set(_Boost_IMPORTED_TARGETS ${_Boost_IMPORTED_TARGETS} PARENT_SCOPE)
-      foreach(componentdep ${_Boost_${uppercomponent}_DEPENDENCIES})
-        if (NOT ("${componentdep}" IN_LIST _boost_processed_components OR "${componentdep}" IN_LIST _boost_new_components))
-          list(APPEND _boost_new_components ${componentdep})
-        endif()
-      endforeach()
-    endforeach()
-    set(_boost_unprocessed_components ${_boost_new_components})
-    unset(_boost_new_components)
-  endwhile()
-  set(_boost_extra_components ${_boost_processed_components})
-  if(_boost_extra_components AND ${componentvar})
-    list(REMOVE_ITEM _boost_extra_components ${${componentvar}})
-  endif()
-  set(${componentvar} ${_boost_processed_components} PARENT_SCOPE)
-  set(${extravar} ${_boost_extra_components} PARENT_SCOPE)
-endfunction()
-
-#
-# Some boost libraries may require particular set of compler features.
-# The very first one was `boost::fiber` introduced in Boost 1.62.
-# One can check required compiler features of it in
-# `${Boost_ROOT}/libs/fiber/build/Jamfile.v2`.
-#
-function(_Boost_COMPILER_FEATURES component _ret)
-  # Boost >= 1.62 and < 1.67
-  if(NOT Boost_VERSION VERSION_LESS 106200 AND Boost_VERSION VERSION_LESS 106700)
-    set(_Boost_FIBER_COMPILER_FEATURES
-        cxx_alias_templates
-        cxx_auto_type
-        cxx_constexpr
-        cxx_defaulted_functions
-        cxx_final
-        cxx_lambdas
-        cxx_noexcept
-        cxx_nullptr
-        cxx_rvalue_references
-        cxx_thread_local
-        cxx_variadic_templates
-    )
-  endif()
-  string(TOUPPER ${component} uppercomponent)
-  set(${_ret} ${_Boost_${uppercomponent}_COMPILER_FEATURES} PARENT_SCOPE)
-endfunction()
-
-#
-# Update library search directory hint variable with paths used by prebuilt boost binaries.
-#
-# Prebuilt windows binaries (https://sourceforge.net/projects/boost/files/boost-binaries/)
-# have library directories named using MSVC compiler version and architecture.
-# This function would append corresponding directories if MSVC is a current compiler,
-# so having `BOOST_ROOT` would be enough to specify to find everything.
-#
-function(_Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS componentlibvar basedir)
-  if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC")
-    if(CMAKE_SIZEOF_VOID_P EQUAL 8)
-      set(_arch_suffix 64)
-    else()
-      set(_arch_suffix 32)
-    endif()
-    if(MSVC_TOOLSET_VERSION GREATER_EQUAL 141)
-      list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-14.1)
-      list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-14.0)
-    elseif(MSVC_TOOLSET_VERSION GREATER_EQUAL 80)
-      math(EXPR _toolset_major_version "${MSVC_TOOLSET_VERSION} / 10")
-      list(APPEND ${componentlibvar} ${basedir}/lib${_arch_suffix}-msvc-${_toolset_major_version}.0)
-    endif()
-    set(${componentlibvar} ${${componentlibvar}} PARENT_SCOPE)
-  endif()
-endfunction()
-
-#
-# End functions/macros
-#
-#-------------------------------------------------------------------------------
-
-#-------------------------------------------------------------------------------
-# main.
-#-------------------------------------------------------------------------------
-
-
-# If the user sets Boost_LIBRARY_DIR, use it as the default for both
-# configurations.
-if(NOT Boost_LIBRARY_DIR_RELEASE AND Boost_LIBRARY_DIR)
-  set(Boost_LIBRARY_DIR_RELEASE "${Boost_LIBRARY_DIR}")
-endif()
-if(NOT Boost_LIBRARY_DIR_DEBUG AND Boost_LIBRARY_DIR)
-  set(Boost_LIBRARY_DIR_DEBUG   "${Boost_LIBRARY_DIR}")
-endif()
-
-if(NOT DEFINED Boost_USE_DEBUG_LIBS)
-  set(Boost_USE_DEBUG_LIBS TRUE)
-endif()
-if(NOT DEFINED Boost_USE_RELEASE_LIBS)
-  set(Boost_USE_RELEASE_LIBS TRUE)
-endif()
-if(NOT DEFINED Boost_USE_MULTITHREADED)
-  set(Boost_USE_MULTITHREADED TRUE)
-endif()
-if(NOT DEFINED Boost_USE_DEBUG_RUNTIME)
-  set(Boost_USE_DEBUG_RUNTIME TRUE)
-endif()
-
-# Check the version of Boost against the requested version.
-if(Boost_FIND_VERSION AND NOT Boost_FIND_VERSION_MINOR)
-  message(SEND_ERROR "When requesting a specific version of Boost, you must provide at least the major and minor version numbers, e.g., 1.34")
-endif()
-
-if(Boost_FIND_VERSION_EXACT)
-  # The version may appear in a directory with or without the patch
-  # level, even when the patch level is non-zero.
-  set(_boost_TEST_VERSIONS
-    "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}.${Boost_FIND_VERSION_PATCH}"
-    "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}")
-else()
-  # The user has not requested an exact version.  Among known
-  # versions, find those that are acceptable to the user request.
-  #
-  # Note: When adding a new Boost release, also update the dependency
-  # information in _Boost_COMPONENT_DEPENDENCIES and
-  # _Boost_COMPONENT_HEADERS.  See the instructions at the top of
-  # _Boost_COMPONENT_DEPENDENCIES.
-  set(_Boost_KNOWN_VERSIONS ${Boost_ADDITIONAL_VERSIONS}
-    "1.70.0" "1.70" "1.69.0" "1.69"
-    "1.68.0" "1.68" "1.67.0" "1.67" "1.66.0" "1.66" "1.65.1" "1.65.0" "1.65"
-    "1.64.0" "1.64" "1.63.0" "1.63" "1.62.0" "1.62" "1.61.0" "1.61" "1.60.0" "1.60"
-    "1.59.0" "1.59" "1.58.0" "1.58" "1.57.0" "1.57" "1.56.0" "1.56" "1.55.0" "1.55"
-    "1.54.0" "1.54" "1.53.0" "1.53" "1.52.0" "1.52" "1.51.0" "1.51"
-    "1.50.0" "1.50" "1.49.0" "1.49" "1.48.0" "1.48" "1.47.0" "1.47" "1.46.1"
-    "1.46.0" "1.46" "1.45.0" "1.45" "1.44.0" "1.44" "1.43.0" "1.43" "1.42.0" "1.42"
-    "1.41.0" "1.41" "1.40.0" "1.40" "1.39.0" "1.39" "1.38.0" "1.38" "1.37.0" "1.37"
-    "1.36.1" "1.36.0" "1.36" "1.35.1" "1.35.0" "1.35" "1.34.1" "1.34.0"
-    "1.34" "1.33.1" "1.33.0" "1.33")
-
-  set(_boost_TEST_VERSIONS)
-  if(Boost_FIND_VERSION)
-    set(_Boost_FIND_VERSION_SHORT "${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}")
-    # Select acceptable versions.
-    foreach(version ${_Boost_KNOWN_VERSIONS})
-      if(NOT "${version}" VERSION_LESS "${Boost_FIND_VERSION}")
-        # This version is high enough.
-        list(APPEND _boost_TEST_VERSIONS "${version}")
-      elseif("${version}.99" VERSION_EQUAL "${_Boost_FIND_VERSION_SHORT}.99")
-        # This version is a short-form for the requested version with
-        # the patch level dropped.
-        list(APPEND _boost_TEST_VERSIONS "${version}")
-      endif()
-    endforeach()
-  else()
-    # Any version is acceptable.
-    set(_boost_TEST_VERSIONS "${_Boost_KNOWN_VERSIONS}")
-  endif()
-endif()
-
-# The reason that we failed to find Boost. This will be set to a
-# user-friendly message when we fail to find some necessary piece of
-# Boost.
-set(Boost_ERROR_REASON)
-
-if(Boost_DEBUG)
-  # Output some of their choices
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                 "_boost_TEST_VERSIONS = ${_boost_TEST_VERSIONS}")
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                 "Boost_USE_MULTITHREADED = ${Boost_USE_MULTITHREADED}")
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                 "Boost_USE_STATIC_LIBS = ${Boost_USE_STATIC_LIBS}")
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                 "Boost_USE_STATIC_RUNTIME = ${Boost_USE_STATIC_RUNTIME}")
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                 "Boost_ADDITIONAL_VERSIONS = ${Boost_ADDITIONAL_VERSIONS}")
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                 "Boost_NO_SYSTEM_PATHS = ${Boost_NO_SYSTEM_PATHS}")
-endif()
-
-# Supply Boost_LIB_DIAGNOSTIC_DEFINITIONS as a convenience target. It
-# will only contain any interface definitions on WIN32, but is created
-# on all platforms to keep end user code free from platform dependent
-# code.  Also provide convenience targets to disable autolinking and
-# enable dynamic linking.
-if(NOT TARGET Boost::diagnostic_definitions)
-  add_library(Boost::diagnostic_definitions INTERFACE IMPORTED)
-  add_library(Boost::disable_autolinking INTERFACE IMPORTED)
-  add_library(Boost::dynamic_linking INTERFACE IMPORTED)
-  set_target_properties(Boost::dynamic_linking PROPERTIES
-    INTERFACE_COMPILE_DEFINITIONS "BOOST_ALL_DYN_LINK")
-endif()
-if(WIN32)
-  # In windows, automatic linking is performed, so you do not have
-  # to specify the libraries.  If you are linking to a dynamic
-  # runtime, then you can choose to link to either a static or a
-  # dynamic Boost library, the default is to do a static link.  You
-  # can alter this for a specific library "whatever" by defining
-  # BOOST_WHATEVER_DYN_LINK to force Boost library "whatever" to be
-  # linked dynamically.  Alternatively you can force all Boost
-  # libraries to dynamic link by defining BOOST_ALL_DYN_LINK.
-
-  # This feature can be disabled for Boost library "whatever" by
-  # defining BOOST_WHATEVER_NO_LIB, or for all of Boost by defining
-  # BOOST_ALL_NO_LIB.
-
-  # If you want to observe which libraries are being linked against
-  # then defining BOOST_LIB_DIAGNOSTIC will cause the auto-linking
-  # code to emit a #pragma message each time a library is selected
-  # for linking.
-  set(Boost_LIB_DIAGNOSTIC_DEFINITIONS "-DBOOST_LIB_DIAGNOSTIC")
-  set_target_properties(Boost::diagnostic_definitions PROPERTIES
-    INTERFACE_COMPILE_DEFINITIONS "BOOST_LIB_DIAGNOSTIC")
-  set_target_properties(Boost::disable_autolinking PROPERTIES
-    INTERFACE_COMPILE_DEFINITIONS "BOOST_ALL_NO_LIB")
-endif()
-
-_Boost_CHECK_SPELLING(Boost_ROOT)
-_Boost_CHECK_SPELLING(Boost_LIBRARYDIR)
-_Boost_CHECK_SPELLING(Boost_INCLUDEDIR)
-
-# Collect environment variable inputs as hints.  Do not consider changes.
-foreach(v BOOSTROOT BOOST_ROOT BOOST_INCLUDEDIR BOOST_LIBRARYDIR)
-  set(_env $ENV{${v}})
-  if(_env)
-    file(TO_CMAKE_PATH "${_env}" _ENV_${v})
-  else()
-    set(_ENV_${v} "")
-  endif()
-endforeach()
-if(NOT _ENV_BOOST_ROOT AND _ENV_BOOSTROOT)
-  set(_ENV_BOOST_ROOT "${_ENV_BOOSTROOT}")
-endif()
-
-# Collect inputs and cached results.  Detect changes since the last run.
-if(NOT BOOST_ROOT AND BOOSTROOT)
-  set(BOOST_ROOT "${BOOSTROOT}")
-endif()
-set(_Boost_VARS_DIR
-  BOOST_ROOT
-  Boost_NO_SYSTEM_PATHS
-  )
-
-if(Boost_DEBUG)
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                 "Declared as CMake or Environmental Variables:")
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                 "  BOOST_ROOT = ${BOOST_ROOT}")
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                 "  BOOST_INCLUDEDIR = ${BOOST_INCLUDEDIR}")
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                 "  BOOST_LIBRARYDIR = ${BOOST_LIBRARYDIR}")
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                 "_boost_TEST_VERSIONS = ${_boost_TEST_VERSIONS}")
-endif()
-
-# ------------------------------------------------------------------------
-#  Search for Boost include DIR
-# ------------------------------------------------------------------------
-
-set(_Boost_VARS_INC BOOST_INCLUDEDIR Boost_INCLUDE_DIR Boost_ADDITIONAL_VERSIONS)
-_Boost_CHANGE_DETECT(_Boost_CHANGE_INCDIR ${_Boost_VARS_DIR} ${_Boost_VARS_INC})
-# Clear Boost_INCLUDE_DIR if it did not change but other input affecting the
-# location did.  We will find a new one based on the new inputs.
-if(_Boost_CHANGE_INCDIR AND NOT _Boost_INCLUDE_DIR_CHANGED)
-  unset(Boost_INCLUDE_DIR CACHE)
-endif()
-
-if(NOT Boost_INCLUDE_DIR)
-  set(_boost_INCLUDE_SEARCH_DIRS "")
-  if(BOOST_INCLUDEDIR)
-    list(APPEND _boost_INCLUDE_SEARCH_DIRS ${BOOST_INCLUDEDIR})
-  elseif(_ENV_BOOST_INCLUDEDIR)
-    list(APPEND _boost_INCLUDE_SEARCH_DIRS ${_ENV_BOOST_INCLUDEDIR})
-  endif()
-
-  if( BOOST_ROOT )
-    list(APPEND _boost_INCLUDE_SEARCH_DIRS ${BOOST_ROOT}/include ${BOOST_ROOT})
-  elseif( _ENV_BOOST_ROOT )
-    list(APPEND _boost_INCLUDE_SEARCH_DIRS ${_ENV_BOOST_ROOT}/include ${_ENV_BOOST_ROOT})
-  endif()
-
-  if( Boost_NO_SYSTEM_PATHS)
-    list(APPEND _boost_INCLUDE_SEARCH_DIRS NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH)
-  else()
-    if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC")
-      foreach(ver ${_boost_TEST_VERSIONS})
-        string(REPLACE "." "_" ver "${ver}")
-        list(APPEND _boost_INCLUDE_SEARCH_DIRS PATHS "C:/local/boost_${ver}")
-      endforeach()
-    endif()
-    list(APPEND _boost_INCLUDE_SEARCH_DIRS PATHS
-      C:/boost/include
-      C:/boost
-      /sw/local/include
-      )
-  endif()
-
-  # Try to find Boost by stepping backwards through the Boost versions
-  # we know about.
-  # Build a list of path suffixes for each version.
-  set(_boost_PATH_SUFFIXES)
-  foreach(_boost_VER ${_boost_TEST_VERSIONS})
-    # Add in a path suffix, based on the required version, ideally
-    # we could read this from version.hpp, but for that to work we'd
-    # need to know the include dir already
-    set(_boost_BOOSTIFIED_VERSION)
-
-    # Transform 1.35 => 1_35 and 1.36.0 => 1_36_0
-    if(_boost_VER MATCHES "([0-9]+)\\.([0-9]+)\\.([0-9]+)")
-        set(_boost_BOOSTIFIED_VERSION
-          "${CMAKE_MATCH_1}_${CMAKE_MATCH_2}_${CMAKE_MATCH_3}")
-    elseif(_boost_VER MATCHES "([0-9]+)\\.([0-9]+)")
-        set(_boost_BOOSTIFIED_VERSION
-          "${CMAKE_MATCH_1}_${CMAKE_MATCH_2}")
-    endif()
-
-    list(APPEND _boost_PATH_SUFFIXES
-      "boost-${_boost_BOOSTIFIED_VERSION}"
-      "boost_${_boost_BOOSTIFIED_VERSION}"
-      "boost/boost-${_boost_BOOSTIFIED_VERSION}"
-      "boost/boost_${_boost_BOOSTIFIED_VERSION}"
-      )
-
-  endforeach()
-
-  if(Boost_DEBUG)
-    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                   "Include debugging info:")
-    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                   "  _boost_INCLUDE_SEARCH_DIRS = ${_boost_INCLUDE_SEARCH_DIRS}")
-    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                   "  _boost_PATH_SUFFIXES = ${_boost_PATH_SUFFIXES}")
-  endif()
-
-  # Look for a standard boost header file.
-  find_path(Boost_INCLUDE_DIR
-    NAMES         boost/config.hpp
-    HINTS         ${_boost_INCLUDE_SEARCH_DIRS}
-    PATH_SUFFIXES ${_boost_PATH_SUFFIXES}
-    )
-endif()
-
-# ------------------------------------------------------------------------
-#  Extract version information from version.hpp
-# ------------------------------------------------------------------------
-
-# Set Boost_FOUND based only on header location and version.
-# It will be updated below for component libraries.
-if(Boost_INCLUDE_DIR)
-  if(Boost_DEBUG)
-    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                   "location of version.hpp: ${Boost_INCLUDE_DIR}/boost/version.hpp")
-  endif()
-
-  # Extract Boost_VERSION and Boost_LIB_VERSION from version.hpp
-  set(Boost_VERSION 0)
-  set(Boost_LIB_VERSION "")
-  file(STRINGS "${Boost_INCLUDE_DIR}/boost/version.hpp" _boost_VERSION_HPP_CONTENTS REGEX "#define BOOST_(LIB_)?VERSION ")
-  set(_Boost_VERSION_REGEX "([0-9]+)")
-  set(_Boost_LIB_VERSION_REGEX "\"([0-9_]+)\"")
-  foreach(v VERSION LIB_VERSION)
-    if("${_boost_VERSION_HPP_CONTENTS}" MATCHES "#define BOOST_${v} ${_Boost_${v}_REGEX}")
-      set(Boost_${v} "${CMAKE_MATCH_1}")
-    endif()
-  endforeach()
-  unset(_boost_VERSION_HPP_CONTENTS)
-
-  math(EXPR Boost_MAJOR_VERSION "${Boost_VERSION} / 100000")
-  math(EXPR Boost_MINOR_VERSION "${Boost_VERSION} / 100 % 1000")
-  math(EXPR Boost_SUBMINOR_VERSION "${Boost_VERSION} % 100")
-  set(Boost_VERSION_STRING "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}")
-
-  string(APPEND Boost_ERROR_REASON
-    "Boost version: ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}\nBoost include path: ${Boost_INCLUDE_DIR}")
-  if(Boost_DEBUG)
-    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                   "version.hpp reveals boost "
-                   "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}")
-  endif()
-
-  if(Boost_FIND_VERSION)
-    # Set Boost_FOUND based on requested version.
-    set(_Boost_VERSION "${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}")
-    if("${_Boost_VERSION}" VERSION_LESS "${Boost_FIND_VERSION}")
-      set(Boost_FOUND 0)
-      set(_Boost_VERSION_AGE "old")
-    elseif(Boost_FIND_VERSION_EXACT AND
-        NOT "${_Boost_VERSION}" VERSION_EQUAL "${Boost_FIND_VERSION}")
-      set(Boost_FOUND 0)
-      set(_Boost_VERSION_AGE "new")
-    else()
-      set(Boost_FOUND 1)
-    endif()
-    if(NOT Boost_FOUND)
-      # State that we found a version of Boost that is too new or too old.
-      string(APPEND Boost_ERROR_REASON
-        "\nDetected version of Boost is too ${_Boost_VERSION_AGE}. Requested version was ${Boost_FIND_VERSION_MAJOR}.${Boost_FIND_VERSION_MINOR}")
-      if (Boost_FIND_VERSION_PATCH)
-        string(APPEND Boost_ERROR_REASON
-          ".${Boost_FIND_VERSION_PATCH}")
-      endif ()
-      if (NOT Boost_FIND_VERSION_EXACT)
-        string(APPEND Boost_ERROR_REASON " (or newer)")
-      endif ()
-      string(APPEND Boost_ERROR_REASON ".")
-    endif ()
-  else()
-    # Caller will accept any Boost version.
-    set(Boost_FOUND 1)
-  endif()
-else()
-  set(Boost_FOUND 0)
-  string(APPEND Boost_ERROR_REASON
-    "Unable to find the Boost header files. Please set BOOST_ROOT to the root directory containing Boost or BOOST_INCLUDEDIR to the directory containing Boost's headers.")
-endif()
-
-# ------------------------------------------------------------------------
-#  Prefix initialization
-# ------------------------------------------------------------------------
-
-set(Boost_LIB_PREFIX "")
-if ( (GHSMULTI AND Boost_USE_STATIC_LIBS) OR
-    (WIN32 AND Boost_USE_STATIC_LIBS AND NOT CYGWIN) )
-  set(Boost_LIB_PREFIX "lib")
-endif()
-
-if ( NOT Boost_NAMESPACE )
-  set(Boost_NAMESPACE "boost")
-endif()
-
-if(Boost_DEBUG)
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-    "Boost_LIB_PREFIX = ${Boost_LIB_PREFIX}")
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-    "Boost_NAMESPACE = ${Boost_NAMESPACE}")
-endif()
-
-# ------------------------------------------------------------------------
-#  Suffix initialization and compiler suffix detection.
-# ------------------------------------------------------------------------
-
-set(_Boost_VARS_NAME
-  Boost_NAMESPACE
-  Boost_COMPILER
-  Boost_THREADAPI
-  Boost_USE_DEBUG_PYTHON
-  Boost_USE_MULTITHREADED
-  Boost_USE_STATIC_LIBS
-  Boost_USE_STATIC_RUNTIME
-  Boost_USE_STLPORT
-  Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS
-  )
-_Boost_CHANGE_DETECT(_Boost_CHANGE_LIBNAME ${_Boost_VARS_NAME})
-
-# Setting some more suffixes for the library
-if (Boost_COMPILER)
-  set(_boost_COMPILER ${Boost_COMPILER})
-  if(Boost_DEBUG)
-    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                   "using user-specified Boost_COMPILER = ${_boost_COMPILER}")
-  endif()
-else()
-  # Attempt to guess the compiler suffix
-  # NOTE: this is not perfect yet, if you experience any issues
-  # please report them and use the Boost_COMPILER variable
-  # to work around the problems.
-  _Boost_GUESS_COMPILER_PREFIX(_boost_COMPILER)
-  if(Boost_DEBUG)
-    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-      "guessed _boost_COMPILER = ${_boost_COMPILER}")
-  endif()
-endif()
-
-set (_boost_MULTITHREADED "-mt")
-if( NOT Boost_USE_MULTITHREADED )
-  set (_boost_MULTITHREADED "")
-endif()
-if(Boost_DEBUG)
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-    "_boost_MULTITHREADED = ${_boost_MULTITHREADED}")
-endif()
-
-#======================
-# Systematically build up the Boost ABI tag for the 'tagged' and 'versioned' layouts
-# http://boost.org/doc/libs/1_66_0/more/getting_started/windows.html#library-naming
-# http://boost.org/doc/libs/1_66_0/boost/config/auto_link.hpp
-# http://boost.org/doc/libs/1_66_0/tools/build/src/tools/common.jam
-# http://boost.org/doc/libs/1_66_0/boostcpp.jam
-set( _boost_RELEASE_ABI_TAG "-")
-set( _boost_DEBUG_ABI_TAG   "-")
-# Key       Use this library when:
-#  s        linking statically to the C++ standard library and
-#           compiler runtime support libraries.
-if(Boost_USE_STATIC_RUNTIME)
-  set( _boost_RELEASE_ABI_TAG "${_boost_RELEASE_ABI_TAG}s")
-  set( _boost_DEBUG_ABI_TAG   "${_boost_DEBUG_ABI_TAG}s")
-endif()
-#  g        using debug versions of the standard and runtime
-#           support libraries
-if(WIN32 AND Boost_USE_DEBUG_RUNTIME)
-  if("x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xMSVC"
-          OR "x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xClang"
-          OR "x${CMAKE_CXX_COMPILER_ID}" STREQUAL "xIntel")
-    string(APPEND _boost_DEBUG_ABI_TAG "g")
-  endif()
-endif()
-#  y        using special debug build of python
-if(Boost_USE_DEBUG_PYTHON)
-  string(APPEND _boost_DEBUG_ABI_TAG "y")
-endif()
-#  d        using a debug version of your code
-string(APPEND _boost_DEBUG_ABI_TAG "d")
-#  p        using the STLport standard library rather than the
-#           default one supplied with your compiler
-if(Boost_USE_STLPORT)
-  string(APPEND _boost_RELEASE_ABI_TAG "p")
-  string(APPEND _boost_DEBUG_ABI_TAG "p")
-endif()
-#  n        using the STLport deprecated "native iostreams" feature
-#           removed from the documentation in 1.43.0 but still present in
-#           boost/config/auto_link.hpp
-if(Boost_USE_STLPORT_DEPRECATED_NATIVE_IOSTREAMS)
-  string(APPEND _boost_RELEASE_ABI_TAG "n")
-  string(APPEND _boost_DEBUG_ABI_TAG "n")
-endif()
-
-#  -x86     Architecture and address model tag
-#           First character is the architecture, then word-size, either 32 or 64
-#           Only used in 'versioned' layout, added in Boost 1.66.0
-if(DEFINED Boost_ARCHITECTURE)
-  set(_boost_ARCHITECTURE_TAG "${Boost_ARCHITECTURE}")
-  if(Boost_DEBUG)
-    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-      "using user-specified Boost_ARCHITECTURE = ${_boost_ARCHITECTURE_TAG}")
-  endif()
-else()
-  set(_boost_ARCHITECTURE_TAG "")
-  # {CMAKE_CXX_COMPILER_ARCHITECTURE_ID} is not currently set for all compilers
-  if(NOT "x${CMAKE_CXX_COMPILER_ARCHITECTURE_ID}" STREQUAL "x" AND NOT Boost_VERSION VERSION_LESS 106600)
-    string(APPEND _boost_ARCHITECTURE_TAG "-")
-    # This needs to be kept in-sync with the section of CMakePlatformId.h.in
-    # inside 'defined(_WIN32) && defined(_MSC_VER)'
-    if(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "IA64")
-      string(APPEND _boost_ARCHITECTURE_TAG "i")
-    elseif(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "X86"
-              OR CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "x64")
-      string(APPEND _boost_ARCHITECTURE_TAG "x")
-    elseif(CMAKE_CXX_COMPILER_ARCHITECTURE_ID MATCHES "^ARM")
-      string(APPEND _boost_ARCHITECTURE_TAG "a")
-    elseif(CMAKE_CXX_COMPILER_ARCHITECTURE_ID STREQUAL "MIPS")
-      string(APPEND _boost_ARCHITECTURE_TAG "m")
-    endif()
-
-    if(CMAKE_SIZEOF_VOID_P EQUAL 8)
-      string(APPEND _boost_ARCHITECTURE_TAG "64")
-    else()
-      string(APPEND _boost_ARCHITECTURE_TAG "32")
-    endif()
-  endif()
-endif()
-
-if(Boost_DEBUG)
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-    "_boost_RELEASE_ABI_TAG = ${_boost_RELEASE_ABI_TAG}")
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-    "_boost_DEBUG_ABI_TAG = ${_boost_DEBUG_ABI_TAG}")
-endif()
-
-# ------------------------------------------------------------------------
-#  Begin finding boost libraries
-# ------------------------------------------------------------------------
-
-set(_Boost_VARS_LIB "")
-foreach(c DEBUG RELEASE)
-  set(_Boost_VARS_LIB_${c} BOOST_LIBRARYDIR Boost_LIBRARY_DIR_${c})
-  list(APPEND _Boost_VARS_LIB ${_Boost_VARS_LIB_${c}})
-  _Boost_CHANGE_DETECT(_Boost_CHANGE_LIBDIR_${c} ${_Boost_VARS_DIR} ${_Boost_VARS_LIB_${c}} Boost_INCLUDE_DIR)
-  # Clear Boost_LIBRARY_DIR_${c} if it did not change but other input affecting the
-  # location did.  We will find a new one based on the new inputs.
-  if(_Boost_CHANGE_LIBDIR_${c} AND NOT _Boost_LIBRARY_DIR_${c}_CHANGED)
-    unset(Boost_LIBRARY_DIR_${c} CACHE)
-  endif()
-
-  # If Boost_LIBRARY_DIR_[RELEASE,DEBUG] is set, prefer its value.
-  if(Boost_LIBRARY_DIR_${c})
-    set(_boost_LIBRARY_SEARCH_DIRS_${c} ${Boost_LIBRARY_DIR_${c}} NO_DEFAULT_PATH NO_CMAKE_FIND_ROOT_PATH)
-  else()
-    set(_boost_LIBRARY_SEARCH_DIRS_${c} "")
-    if(BOOST_LIBRARYDIR)
-      list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} ${BOOST_LIBRARYDIR})
-    elseif(_ENV_BOOST_LIBRARYDIR)
-      list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} ${_ENV_BOOST_LIBRARYDIR})
-    endif()
-
-    if(BOOST_ROOT)
-      list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} ${BOOST_ROOT}/lib ${BOOST_ROOT}/stage/lib)
-      _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "${BOOST_ROOT}")
-    elseif(_ENV_BOOST_ROOT)
-      list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} ${_ENV_BOOST_ROOT}/lib ${_ENV_BOOST_ROOT}/stage/lib)
-      _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "${_ENV_BOOST_ROOT}")
-    endif()
-
-    list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c}
-      ${Boost_INCLUDE_DIR}/lib
-      ${Boost_INCLUDE_DIR}/../lib
-      ${Boost_INCLUDE_DIR}/stage/lib
-      )
-    _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "${Boost_INCLUDE_DIR}/..")
-    _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "${Boost_INCLUDE_DIR}")
-    if( Boost_NO_SYSTEM_PATHS )
-      list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} NO_CMAKE_SYSTEM_PATH NO_SYSTEM_ENVIRONMENT_PATH)
-    else()
-      foreach(ver ${_boost_TEST_VERSIONS})
-        string(REPLACE "." "_" ver "${ver}")
-        _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "C:/local/boost_${ver}")
-      endforeach()
-      _Boost_UPDATE_WINDOWS_LIBRARY_SEARCH_DIRS_WITH_PREBUILT_PATHS(_boost_LIBRARY_SEARCH_DIRS_${c} "C:/boost")
-      list(APPEND _boost_LIBRARY_SEARCH_DIRS_${c} PATHS
-        C:/boost/lib
-        C:/boost
-        /sw/local/lib
-        )
-    endif()
-  endif()
-endforeach()
-
-if(Boost_DEBUG)
-  message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-    "_boost_LIBRARY_SEARCH_DIRS_RELEASE = ${_boost_LIBRARY_SEARCH_DIRS_RELEASE}"
-    "_boost_LIBRARY_SEARCH_DIRS_DEBUG   = ${_boost_LIBRARY_SEARCH_DIRS_DEBUG}")
-endif()
-
-# Support preference of static libs by adjusting CMAKE_FIND_LIBRARY_SUFFIXES
-if( Boost_USE_STATIC_LIBS )
-  set( _boost_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES ${CMAKE_FIND_LIBRARY_SUFFIXES})
-  if(WIN32)
-    list(INSERT CMAKE_FIND_LIBRARY_SUFFIXES 0 .lib .a)
-  else()
-    set(CMAKE_FIND_LIBRARY_SUFFIXES .a)
-  endif()
-endif()
-
-# We want to use the tag inline below without risking double dashes
-if(_boost_RELEASE_ABI_TAG)
-  if(${_boost_RELEASE_ABI_TAG} STREQUAL "-")
-    set(_boost_RELEASE_ABI_TAG "")
-  endif()
-endif()
-if(_boost_DEBUG_ABI_TAG)
-  if(${_boost_DEBUG_ABI_TAG} STREQUAL "-")
-    set(_boost_DEBUG_ABI_TAG "")
-  endif()
-endif()
-
-# The previous behavior of FindBoost when Boost_USE_STATIC_LIBS was enabled
-# on WIN32 was to:
-#  1. Search for static libs compiled against a SHARED C++ standard runtime library (use if found)
-#  2. Search for static libs compiled against a STATIC C++ standard runtime library (use if found)
-# We maintain this behavior since changing it could break people's builds.
-# To disable the ambiguous behavior, the user need only
-# set Boost_USE_STATIC_RUNTIME either ON or OFF.
-set(_boost_STATIC_RUNTIME_WORKAROUND false)
-if(WIN32 AND Boost_USE_STATIC_LIBS)
-  if(NOT DEFINED Boost_USE_STATIC_RUNTIME)
-    set(_boost_STATIC_RUNTIME_WORKAROUND TRUE)
-  endif()
-endif()
-
-# On versions < 1.35, remove the System library from the considered list
-# since it wasn't added until 1.35.
-if(Boost_VERSION AND Boost_FIND_COMPONENTS)
-   if(Boost_VERSION LESS 103500)
-     list(REMOVE_ITEM Boost_FIND_COMPONENTS system)
-   endif()
-endif()
-
-# Additional components may be required via component dependencies.
-# Add any missing components to the list.
-_Boost_MISSING_DEPENDENCIES(Boost_FIND_COMPONENTS _Boost_EXTRA_FIND_COMPONENTS)
-
-# If thread is required, get the thread libs as a dependency
-if("thread" IN_LIST Boost_FIND_COMPONENTS)
-  if(Boost_FIND_QUIETLY)
-    set(_Boost_find_quiet QUIET)
-  else()
-    set(_Boost_find_quiet "")
-  endif()
-  find_package(Threads ${_Boost_find_quiet})
-  unset(_Boost_find_quiet)
-endif()
-
-# If the user changed any of our control inputs flush previous results.
-if(_Boost_CHANGE_LIBDIR_DEBUG OR _Boost_CHANGE_LIBDIR_RELEASE OR _Boost_CHANGE_LIBNAME)
-  foreach(COMPONENT ${_Boost_COMPONENTS_SEARCHED})
-    string(TOUPPER ${COMPONENT} UPPERCOMPONENT)
-    foreach(c DEBUG RELEASE)
-      set(_var Boost_${UPPERCOMPONENT}_LIBRARY_${c})
-      unset(${_var} CACHE)
-      set(${_var} "${_var}-NOTFOUND")
-    endforeach()
-  endforeach()
-  set(_Boost_COMPONENTS_SEARCHED "")
-endif()
-
-foreach(COMPONENT ${Boost_FIND_COMPONENTS})
-  string(TOUPPER ${COMPONENT} UPPERCOMPONENT)
-
-  set( _boost_docstring_release "Boost ${COMPONENT} library (release)")
-  set( _boost_docstring_debug   "Boost ${COMPONENT} library (debug)")
-
-  # Compute component-specific hints.
-  set(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT "")
-  if(${COMPONENT} STREQUAL "mpi" OR ${COMPONENT} STREQUAL "mpi_python" OR
-     ${COMPONENT} STREQUAL "graph_parallel")
-    foreach(lib ${MPI_CXX_LIBRARIES} ${MPI_C_LIBRARIES})
-      if(IS_ABSOLUTE "${lib}")
-        get_filename_component(libdir "${lib}" PATH)
-        string(REPLACE "\\" "/" libdir "${libdir}")
-        list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT ${libdir})
-      endif()
-    endforeach()
-  endif()
-
-  # Handle Python version suffixes
-  unset(COMPONENT_PYTHON_VERSION_MAJOR)
-  unset(COMPONENT_PYTHON_VERSION_MINOR)
-  if(${COMPONENT} MATCHES "^(python|mpi_python|numpy)([0-9])\$")
-    set(COMPONENT_UNVERSIONED "${CMAKE_MATCH_1}")
-    set(COMPONENT_PYTHON_VERSION_MAJOR "${CMAKE_MATCH_2}")
-  elseif(${COMPONENT} MATCHES "^(python|mpi_python|numpy)([0-9])\\.?([0-9])\$")
-    set(COMPONENT_UNVERSIONED "${CMAKE_MATCH_1}")
-    set(COMPONENT_PYTHON_VERSION_MAJOR "${CMAKE_MATCH_2}")
-    set(COMPONENT_PYTHON_VERSION_MINOR "${CMAKE_MATCH_3}")
-  endif()
-
-  unset(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME)
-  if (COMPONENT_PYTHON_VERSION_MINOR)
-    # Boost >= 1.67
-    list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}")
-    # Debian/Ubuntu (Some versions omit the 2 and/or 3 from the suffix)
-    list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}${COMPONENT_PYTHON_VERSION_MAJOR}-py${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}")
-    list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-py${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}")
-    # Gentoo
-    list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}")
-    # RPMs
-    list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}-${COMPONENT_PYTHON_VERSION_MAJOR}${COMPONENT_PYTHON_VERSION_MINOR}")
-  endif()
-  if (COMPONENT_PYTHON_VERSION_MAJOR AND NOT COMPONENT_PYTHON_VERSION_MINOR)
-    # Boost < 1.67
-    list(APPEND _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME "${COMPONENT_UNVERSIONED}${COMPONENT_PYTHON_VERSION_MAJOR}")
-  endif()
-
-  # Consolidate and report component-specific hints.
-  if(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME)
-    list(REMOVE_DUPLICATES _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME)
-    if(Boost_DEBUG)
-      message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-        "Component-specific library search names for ${COMPONENT_NAME}: "
-        "${_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME}")
-    endif()
-  endif()
-  if(_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT)
-    list(REMOVE_DUPLICATES _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT)
-    if(Boost_DEBUG)
-      message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-        "Component-specific library search paths for ${COMPONENT}: "
-        "${_Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT}")
-    endif()
-  endif()
-
-  #
-  # Find headers
-  #
-  _Boost_COMPONENT_HEADERS("${COMPONENT}" Boost_${UPPERCOMPONENT}_HEADER_NAME)
-  # Look for a standard boost header file.
-  if(Boost_${UPPERCOMPONENT}_HEADER_NAME)
-    if(EXISTS "${Boost_INCLUDE_DIR}/${Boost_${UPPERCOMPONENT}_HEADER_NAME}")
-      set(Boost_${UPPERCOMPONENT}_HEADER ON)
-    else()
-      set(Boost_${UPPERCOMPONENT}_HEADER OFF)
-    endif()
-  else()
-    set(Boost_${UPPERCOMPONENT}_HEADER ON)
-    message(WARNING "No header defined for ${COMPONENT}; skipping header check")
-  endif()
-
-  #
-  # Find RELEASE libraries
-  #
-  unset(_boost_RELEASE_NAMES)
-  foreach(component IN LISTS _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME COMPONENT)
-    foreach(compiler IN LISTS _boost_COMPILER)
-      list(APPEND _boost_RELEASE_NAMES
-        ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION}
-        ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}${_boost_ARCHITECTURE_TAG}
-        ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG} )
-    endforeach()
-    list(APPEND _boost_RELEASE_NAMES
-      ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION}
-      ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}${_boost_ARCHITECTURE_TAG}
-      ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_ABI_TAG}
-      ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}
-      ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component} )
-    if(_boost_STATIC_RUNTIME_WORKAROUND)
-      set(_boost_RELEASE_STATIC_ABI_TAG "-s${_boost_RELEASE_ABI_TAG}")
-      foreach(compiler IN LISTS _boost_COMPILER)
-        list(APPEND _boost_RELEASE_NAMES
-          ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION}
-          ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}
-          ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} )
-      endforeach()
-      list(APPEND _boost_RELEASE_NAMES
-        ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION}
-        ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}
-        ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_RELEASE_STATIC_ABI_TAG} )
-    endif()
-  endforeach()
-  if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread")
-    _Boost_PREPEND_LIST_WITH_THREADAPI(_boost_RELEASE_NAMES ${_boost_RELEASE_NAMES})
-  endif()
-  if(Boost_DEBUG)
-    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                   "Searching for ${UPPERCOMPONENT}_LIBRARY_RELEASE: ${_boost_RELEASE_NAMES}")
-  endif()
-
-  # if Boost_LIBRARY_DIR_RELEASE is not defined,
-  # but Boost_LIBRARY_DIR_DEBUG is, look there first for RELEASE libs
-  if(NOT Boost_LIBRARY_DIR_RELEASE AND Boost_LIBRARY_DIR_DEBUG)
-    list(INSERT _boost_LIBRARY_SEARCH_DIRS_RELEASE 0 ${Boost_LIBRARY_DIR_DEBUG})
-  endif()
-
-  # Avoid passing backslashes to _Boost_FIND_LIBRARY due to macro re-parsing.
-  string(REPLACE "\\" "/" _boost_LIBRARY_SEARCH_DIRS_tmp "${_boost_LIBRARY_SEARCH_DIRS_RELEASE}")
-
-  if(Boost_USE_RELEASE_LIBS)
-    _Boost_FIND_LIBRARY(Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE RELEASE
-      NAMES ${_boost_RELEASE_NAMES}
-      HINTS ${_boost_LIBRARY_SEARCH_DIRS_tmp}
-      NAMES_PER_DIR
-      DOC "${_boost_docstring_release}"
-      )
-  endif()
-
-  #
-  # Find DEBUG libraries
-  #
-  unset(_boost_DEBUG_NAMES)
-  foreach(component IN LISTS _Boost_FIND_LIBRARY_HINTS_FOR_COMPONENT_NAME COMPONENT)
-    foreach(compiler IN LISTS _boost_COMPILER)
-      list(APPEND _boost_DEBUG_NAMES
-        ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION}
-        ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}${_boost_ARCHITECTURE_TAG}
-        ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG} )
-    endforeach()
-    list(APPEND _boost_DEBUG_NAMES
-      ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION}
-      ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}${_boost_ARCHITECTURE_TAG}
-      ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_ABI_TAG}
-      ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}
-      ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component} )
-    if(_boost_STATIC_RUNTIME_WORKAROUND)
-      set(_boost_DEBUG_STATIC_ABI_TAG "-s${_boost_DEBUG_ABI_TAG}")
-      foreach(compiler IN LISTS _boost_COMPILER)
-        list(APPEND _boost_DEBUG_NAMES
-          ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION}
-          ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}
-          ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${compiler}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} )
-      endforeach()
-      list(APPEND _boost_DEBUG_NAMES
-        ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}-${Boost_LIB_VERSION}
-        ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG}${_boost_ARCHITECTURE_TAG}
-        ${Boost_LIB_PREFIX}${Boost_NAMESPACE}_${component}${_boost_MULTITHREADED}${_boost_DEBUG_STATIC_ABI_TAG} )
-    endif()
-  endforeach()
-  if(Boost_THREADAPI AND ${COMPONENT} STREQUAL "thread")
-     _Boost_PREPEND_LIST_WITH_THREADAPI(_boost_DEBUG_NAMES ${_boost_DEBUG_NAMES})
-  endif()
-  if(Boost_DEBUG)
-    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] "
-                   "Searching for ${UPPERCOMPONENT}_LIBRARY_DEBUG: ${_boost_DEBUG_NAMES}")
-  endif()
-
-  # if Boost_LIBRARY_DIR_DEBUG is not defined,
-  # but Boost_LIBRARY_DIR_RELEASE is, look there first for DEBUG libs
-  if(NOT Boost_LIBRARY_DIR_DEBUG AND Boost_LIBRARY_DIR_RELEASE)
-    list(INSERT _boost_LIBRARY_SEARCH_DIRS_DEBUG 0 ${Boost_LIBRARY_DIR_RELEASE})
-  endif()
-
-  # Avoid passing backslashes to _Boost_FIND_LIBRARY due to macro re-parsing.
-  string(REPLACE "\\" "/" _boost_LIBRARY_SEARCH_DIRS_tmp "${_boost_LIBRARY_SEARCH_DIRS_DEBUG}")
-
-  if(Boost_USE_DEBUG_LIBS)
-    _Boost_FIND_LIBRARY(Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG DEBUG
-      NAMES ${_boost_DEBUG_NAMES}
-      HINTS ${_boost_LIBRARY_SEARCH_DIRS_tmp}
-      NAMES_PER_DIR
-      DOC "${_boost_docstring_debug}"
-      )
-  endif ()
-
-  if(Boost_REALPATH)
-    _Boost_SWAP_WITH_REALPATH(Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE "${_boost_docstring_release}")
-    _Boost_SWAP_WITH_REALPATH(Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG   "${_boost_docstring_debug}"  )
-  endif()
-
-  _Boost_ADJUST_LIB_VARS(${UPPERCOMPONENT})
-
-  # Check if component requires some compiler features
-  _Boost_COMPILER_FEATURES(${COMPONENT} _Boost_${UPPERCOMPONENT}_COMPILER_FEATURES)
-
-endforeach()
-
-# Restore the original find library ordering
-if( Boost_USE_STATIC_LIBS )
-  set(CMAKE_FIND_LIBRARY_SUFFIXES ${_boost_ORIG_CMAKE_FIND_LIBRARY_SUFFIXES})
-endif()
-
-# ------------------------------------------------------------------------
-#  End finding boost libraries
-# ------------------------------------------------------------------------
-
-set(Boost_INCLUDE_DIRS ${Boost_INCLUDE_DIR})
-set(Boost_LIBRARY_DIRS)
-if(Boost_LIBRARY_DIR_RELEASE)
-  list(APPEND Boost_LIBRARY_DIRS ${Boost_LIBRARY_DIR_RELEASE})
-endif()
-if(Boost_LIBRARY_DIR_DEBUG)
-  list(APPEND Boost_LIBRARY_DIRS ${Boost_LIBRARY_DIR_DEBUG})
-endif()
-if(Boost_LIBRARY_DIRS)
-  list(REMOVE_DUPLICATES Boost_LIBRARY_DIRS)
-endif()
-
-# The above setting of Boost_FOUND was based only on the header files.
-# Update it for the requested component libraries.
-if(Boost_FOUND)
-  # The headers were found.  Check for requested component libs.
-  set(_boost_CHECKED_COMPONENT FALSE)
-  set(_Boost_MISSING_COMPONENTS "")
-  foreach(COMPONENT ${Boost_FIND_COMPONENTS})
-    string(TOUPPER ${COMPONENT} UPPERCOMPONENT)
-    set(_boost_CHECKED_COMPONENT TRUE)
-    if(NOT Boost_${UPPERCOMPONENT}_FOUND AND Boost_FIND_REQUIRED_${COMPONENT})
-      list(APPEND _Boost_MISSING_COMPONENTS ${COMPONENT})
-    endif()
-  endforeach()
-  if(_Boost_MISSING_COMPONENTS AND _Boost_EXTRA_FIND_COMPONENTS)
-    # Optional indirect dependencies are not counted as missing.
-    list(REMOVE_ITEM _Boost_MISSING_COMPONENTS ${_Boost_EXTRA_FIND_COMPONENTS})
-  endif()
-
-  if(Boost_DEBUG)
-    message(STATUS "[ ${CMAKE_CURRENT_LIST_FILE}:${CMAKE_CURRENT_LIST_LINE} ] Boost_FOUND = ${Boost_FOUND}")
-  endif()
-
-  if (_Boost_MISSING_COMPONENTS)
-    set(Boost_FOUND 0)
-    # We were unable to find some libraries, so generate a sensible
-    # error message that lists the libraries we were unable to find.
-    string(APPEND Boost_ERROR_REASON
-      "\nCould not find the following")
-    if(Boost_USE_STATIC_LIBS)
-      string(APPEND Boost_ERROR_REASON " static")
-    endif()
-    string(APPEND Boost_ERROR_REASON
-      " Boost libraries:\n")
-    foreach(COMPONENT ${_Boost_MISSING_COMPONENTS})
-      string(TOUPPER ${COMPONENT} UPPERCOMPONENT)
-      string(APPEND Boost_ERROR_REASON
-        "        ${Boost_NAMESPACE}_${COMPONENT}${Boost_ERROR_REASON_${UPPERCOMPONENT}}\n")
-    endforeach()
-
-    list(LENGTH Boost_FIND_COMPONENTS Boost_NUM_COMPONENTS_WANTED)
-    list(LENGTH _Boost_MISSING_COMPONENTS Boost_NUM_MISSING_COMPONENTS)
-    if (${Boost_NUM_COMPONENTS_WANTED} EQUAL ${Boost_NUM_MISSING_COMPONENTS})
-      string(APPEND Boost_ERROR_REASON
-        "No Boost libraries were found. You may need to set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.")
-    else ()
-      string(APPEND Boost_ERROR_REASON
-        "Some (but not all) of the required Boost libraries were found. You may need to install these additional Boost libraries. Alternatively, set BOOST_LIBRARYDIR to the directory containing Boost libraries or BOOST_ROOT to the location of Boost.")
-    endif ()
-  endif ()
-
-  if( NOT Boost_LIBRARY_DIRS AND NOT _boost_CHECKED_COMPONENT )
-    # Compatibility Code for backwards compatibility with CMake
-    # 2.4's FindBoost module.
-
-    # Look for the boost library path.
-    # Note that the user may not have installed any libraries
-    # so it is quite possible the Boost_LIBRARY_DIRS may not exist.
-    set(_boost_LIB_DIR ${Boost_INCLUDE_DIR})
-
-    if("${_boost_LIB_DIR}" MATCHES "boost-[0-9]+")
-      get_filename_component(_boost_LIB_DIR ${_boost_LIB_DIR} PATH)
-    endif()
-
-    if("${_boost_LIB_DIR}" MATCHES "/include$")
-      # Strip off the trailing "/include" in the path.
-      get_filename_component(_boost_LIB_DIR ${_boost_LIB_DIR} PATH)
-    endif()
-
-    if(EXISTS "${_boost_LIB_DIR}/lib")
-      string(APPEND _boost_LIB_DIR /lib)
-    elseif(EXISTS "${_boost_LIB_DIR}/stage/lib")
-      string(APPEND _boost_LIB_DIR "/stage/lib")
-    else()
-      set(_boost_LIB_DIR "")
-    endif()
-
-    if(_boost_LIB_DIR AND EXISTS "${_boost_LIB_DIR}")
-      set(Boost_LIBRARY_DIRS ${_boost_LIB_DIR})
-    endif()
-
-  endif()
-else()
-  # Boost headers were not found so no components were found.
-  foreach(COMPONENT ${Boost_FIND_COMPONENTS})
-    string(TOUPPER ${COMPONENT} UPPERCOMPONENT)
-    set(Boost_${UPPERCOMPONENT}_FOUND 0)
-  endforeach()
-endif()
-
-# ------------------------------------------------------------------------
-#  Add imported targets
-# ------------------------------------------------------------------------
-
-if(Boost_FOUND)
-  # For header-only libraries
-  if(NOT TARGET Boost::boost)
-    add_library(Boost::boost INTERFACE IMPORTED)
-    if(Boost_INCLUDE_DIRS)
-      set_target_properties(Boost::boost PROPERTIES
-        INTERFACE_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}")
-    endif()
-  endif()
-
-  foreach(COMPONENT ${Boost_FIND_COMPONENTS})
-    if(_Boost_IMPORTED_TARGETS AND NOT TARGET Boost::${COMPONENT})
-      string(TOUPPER ${COMPONENT} UPPERCOMPONENT)
-      if(Boost_${UPPERCOMPONENT}_FOUND)
-        if(Boost_USE_STATIC_LIBS)
-          add_library(Boost::${COMPONENT} STATIC IMPORTED)
-        else()
-          # Even if Boost_USE_STATIC_LIBS is OFF, we might have static
-          # libraries as a result.
-          add_library(Boost::${COMPONENT} UNKNOWN IMPORTED)
-        endif()
-        if(Boost_INCLUDE_DIRS)
-          set_target_properties(Boost::${COMPONENT} PROPERTIES
-            INTERFACE_INCLUDE_DIRECTORIES "${Boost_INCLUDE_DIRS}")
-        endif()
-        if(EXISTS "${Boost_${UPPERCOMPONENT}_LIBRARY}")
-          set_target_properties(Boost::${COMPONENT} PROPERTIES
-            IMPORTED_LINK_INTERFACE_LANGUAGES "CXX"
-            IMPORTED_LOCATION "${Boost_${UPPERCOMPONENT}_LIBRARY}")
-        endif()
-        if(EXISTS "${Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE}")
-          set_property(TARGET Boost::${COMPONENT} APPEND PROPERTY
-            IMPORTED_CONFIGURATIONS RELEASE)
-          set_target_properties(Boost::${COMPONENT} PROPERTIES
-            IMPORTED_LINK_INTERFACE_LANGUAGES_RELEASE "CXX"
-            IMPORTED_LOCATION_RELEASE "${Boost_${UPPERCOMPONENT}_LIBRARY_RELEASE}")
-        endif()
-        if(EXISTS "${Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG}")
-          set_property(TARGET Boost::${COMPONENT} APPEND PROPERTY
-            IMPORTED_CONFIGURATIONS DEBUG)
-          set_target_properties(Boost::${COMPONENT} PROPERTIES
-            IMPORTED_LINK_INTERFACE_LANGUAGES_DEBUG "CXX"
-            IMPORTED_LOCATION_DEBUG "${Boost_${UPPERCOMPONENT}_LIBRARY_DEBUG}")
-        endif()
-        if(_Boost_${UPPERCOMPONENT}_DEPENDENCIES)
-          unset(_Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES)
-          foreach(dep ${_Boost_${UPPERCOMPONENT}_DEPENDENCIES})
-            list(APPEND _Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES Boost::${dep})
-          endforeach()
-          if(COMPONENT STREQUAL "thread")
-            list(APPEND _Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES Threads::Threads)
-          endif()
-          set_target_properties(Boost::${COMPONENT} PROPERTIES
-            INTERFACE_LINK_LIBRARIES "${_Boost_${UPPERCOMPONENT}_TARGET_DEPENDENCIES}")
-        endif()
-        if(_Boost_${UPPERCOMPONENT}_COMPILER_FEATURES)
-          set_target_properties(Boost::${COMPONENT} PROPERTIES
-            INTERFACE_COMPILE_FEATURES "${_Boost_${UPPERCOMPONENT}_COMPILER_FEATURES}")
-        endif()
-      endif()
-    endif()
-  endforeach()
-endif()
-
-# ------------------------------------------------------------------------
-#  Notification to end user about what was found
-# ------------------------------------------------------------------------
-
-set(Boost_LIBRARIES "")
-if(Boost_FOUND)
-  if(NOT Boost_FIND_QUIETLY)
-    message(STATUS "Boost version: ${Boost_MAJOR_VERSION}.${Boost_MINOR_VERSION}.${Boost_SUBMINOR_VERSION}")
-    if(Boost_FIND_COMPONENTS)
-      message(STATUS "Found the following Boost libraries:")
-    endif()
-  endif()
-  foreach( COMPONENT  ${Boost_FIND_COMPONENTS} )
-    string( TOUPPER ${COMPONENT} UPPERCOMPONENT )
-    if( Boost_${UPPERCOMPONENT}_FOUND )
-      if(NOT Boost_FIND_QUIETLY)
-        message (STATUS "  ${COMPONENT}")
-      endif()
-      list(APPEND Boost_LIBRARIES ${Boost_${UPPERCOMPONENT}_LIBRARY})
-      if(COMPONENT STREQUAL "thread")
-        list(APPEND Boost_LIBRARIES ${CMAKE_THREAD_LIBS_INIT})
-      endif()
-    endif()
-  endforeach()
-else()
-  if(Boost_FIND_REQUIRED)
-    message(SEND_ERROR "Unable to find the requested Boost libraries.\n${Boost_ERROR_REASON}")
-  else()
-    if(NOT Boost_FIND_QUIETLY)
-      # we opt not to automatically output Boost_ERROR_REASON here as
-      # it could be quite lengthy and somewhat imposing in its requests
-      # Since Boost is not always a required dependency we'll leave this
-      # up to the end-user.
-      if(Boost_DEBUG OR Boost_DETAILED_FAILURE_MSG)
-        message(STATUS "Could NOT find Boost\n${Boost_ERROR_REASON}")
-      else()
-        message(STATUS "Could NOT find Boost")
-      endif()
-    endif()
-  endif()
-endif()
-
-# Configure display of cache entries in GUI.
-foreach(v BOOSTROOT BOOST_ROOT ${_Boost_VARS_INC} ${_Boost_VARS_LIB})
-  get_property(_type CACHE ${v} PROPERTY TYPE)
-  if(_type)
-    set_property(CACHE ${v} PROPERTY ADVANCED 1)
-    if("x${_type}" STREQUAL "xUNINITIALIZED")
-      if("x${v}" STREQUAL "xBoost_ADDITIONAL_VERSIONS")
-        set_property(CACHE ${v} PROPERTY TYPE STRING)
-      else()
-        set_property(CACHE ${v} PROPERTY TYPE PATH)
-      endif()
-    endif()
-  endif()
-endforeach()
-
-# Record last used values of input variables so we can
-# detect on the next run if the user changed them.
-foreach(v
-    ${_Boost_VARS_INC} ${_Boost_VARS_LIB}
-    ${_Boost_VARS_DIR} ${_Boost_VARS_NAME}
-    )
-  if(DEFINED ${v})
-    set(_${v}_LAST "${${v}}" CACHE INTERNAL "Last used ${v} value.")
-  else()
-    unset(_${v}_LAST CACHE)
-  endif()
-endforeach()
-
-# Maintain a persistent list of components requested anywhere since
-# the last flush.
-set(_Boost_COMPONENTS_SEARCHED "${_Boost_COMPONENTS_SEARCHED}")
-list(APPEND _Boost_COMPONENTS_SEARCHED ${Boost_FIND_COMPONENTS})
-list(REMOVE_DUPLICATES _Boost_COMPONENTS_SEARCHED)
-list(SORT _Boost_COMPONENTS_SEARCHED)
-set(_Boost_COMPONENTS_SEARCHED "${_Boost_COMPONENTS_SEARCHED}"
-  CACHE INTERNAL "Components requested for this build tree.")
-
-# Restore project's policies
-cmake_policy(POP)
diff --git a/cmake/TestStdAny.cpp b/cmake/TestStdAny.cpp
deleted file mode 100644
index 6bcc880a6134bf1c0c538f6a32e92f87f6d1e8ea..0000000000000000000000000000000000000000
--- a/cmake/TestStdAny.cpp
+++ /dev/null
@@ -1,17 +0,0 @@
-#include <iostream>
-#if defined(WALBERLA_USE_STD_ANY)
-#include <any>
-#elif defined(WALBERLA_USE_STD_EXPERIMENTAL_ANY)
-#include <experimental/any>
-#endif
-
-int main() {
-#if defined(WALBERLA_USE_STD_ANY)
-   auto a = std::any(42);
-   std::cout << std::any_cast<int>(a) << std::endl;
-#elif defined(WALBERLA_USE_STD_EXPERIMENTAL_ANY)
-   auto a = std::experimental::any(42);
-   std::cout << std::experimental::any_cast<int>(a) << std::endl;
-#endif
-   return 0;
-}
diff --git a/cmake/TestStdOptional.cpp b/cmake/TestStdOptional.cpp
deleted file mode 100644
index fc3a148c128e534eb37fc635c697d1e88961a6db..0000000000000000000000000000000000000000
--- a/cmake/TestStdOptional.cpp
+++ /dev/null
@@ -1,19 +0,0 @@
-#include <iostream>
-#if defined(WALBERLA_USE_STD_OPTIONAL)
-#include <optional>
-#elif defined(WALBERLA_USE_STD_EXPERIMENTAL_OPTIONAL)
-#include <experimental/optional>
-#endif
-
-int main() {
-#if defined(WALBERLA_USE_STD_OPTIONAL)
-   auto a = std::optional<int>();
-   auto b = std::optional<int>(42);
-#elif defined(WALBERLA_USE_STD_EXPERIMENTAL_OPTIONAL)
-   auto a = std::experimental::optional<int>();
-   auto b = std::experimental::optional<int>(42);
-#endif
-   if (b)
-      std::cout << a.value_or(b.value()) << std::endl;
-   return 0;
-}
diff --git a/cmake/TestStdVariant.cpp b/cmake/TestStdVariant.cpp
deleted file mode 100644
index be98d121ffcfbc96d73948fee5ed67e0f75e8256..0000000000000000000000000000000000000000
--- a/cmake/TestStdVariant.cpp
+++ /dev/null
@@ -1,7 +0,0 @@
-#include <iostream>
-#include <variant>
-
-int main() {
-   std::variant<int,float> a;
-   return 0;
-}
diff --git a/cmake/ctest2junit.py b/cmake/ctest2junit.py
new file mode 100755
index 0000000000000000000000000000000000000000..d1e5c77b7623541636c58f4c2487d8e886c0da07
--- /dev/null
+++ b/cmake/ctest2junit.py
@@ -0,0 +1,158 @@
+#!/usr/bin/env python3
+# Adapted from https://github.com/scikit-build/scikit-ci-addons/tree/master/anyci
+# under Apache 2.0 license.
+
+import os
+import sys
+
+from lxml import etree
+
+
+def process(build_dir):
+
+    tag_file = build_dir + "/Testing/TAG"
+    if not os.path.exists(tag_file):
+        raise RuntimeError("Missing tag file %s" % tag_file)
+
+    with open(build_dir + "/Testing/TAG", 'r') as tag:
+        dir_name = tag.readline().strip()
+
+    test_xml = build_dir + "/Testing/" + dir_name + "/Test.xml"
+    if not os.path.exists(test_xml):
+        raise RuntimeError("Couldn't find %s" % test_xml)
+
+    xsl = """<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
+<xsl:output method="xml" indent="yes"/>
+
+    <xsl:template match="/Site">
+        <testsuite>
+            <xsl:variable name="BuildName"><xsl:value-of select="@BuildName"/></xsl:variable>
+            <xsl:variable name="BuildStamp"><xsl:value-of select="@BuildStamp"/></xsl:variable>
+            <xsl:variable name="Name"><xsl:value-of select="@Name"/></xsl:variable>
+            <xsl:variable name="Generator"><xsl:value-of select="@Generator"/></xsl:variable>
+            <xsl:variable name="CompilerName"><xsl:value-of select="@CompilerName"/></xsl:variable>
+            <xsl:variable name="OSName"><xsl:value-of select="@OSName"/></xsl:variable>
+            <xsl:variable name="Hostname"><xsl:value-of select="@Hostname"/></xsl:variable>
+            <xsl:variable name="OSRelease"><xsl:value-of select="@OSRelease"/></xsl:variable>
+            <xsl:variable name="OSVersion"><xsl:value-of select="@OSVersion"/></xsl:variable>
+            <xsl:variable name="OSPlatform"><xsl:value-of select="@OSPlatform"/></xsl:variable>
+            <xsl:variable name="Is64Bits"><xsl:value-of select="@Is64Bits"/></xsl:variable>
+            <xsl:variable name="VendorString"><xsl:value-of select="@VendorString"/></xsl:variable>
+            <xsl:variable name="VendorID"><xsl:value-of select="@VendorID"/></xsl:variable>
+            <xsl:variable name="FamilyID"><xsl:value-of select="@FamilyID"/></xsl:variable>
+            <xsl:variable name="ModelID"><xsl:value-of select="@ModelID"/></xsl:variable>
+            <xsl:variable name="ProcessorCacheSize"><xsl:value-of select="@ProcessorCacheSize"/></xsl:variable>
+            <xsl:variable name="NumberOfLogicalCPU"><xsl:value-of select="@NumberOfLogicalCPU"/></xsl:variable>
+            <xsl:variable name="NumberOfPhysicalCPU"><xsl:value-of select="@NumberOfPhysicalCPU"/></xsl:variable>
+            <xsl:variable name="TotalVirtualMemory"><xsl:value-of select="@TotalVirtualMemory"/></xsl:variable>
+            <xsl:variable name="TotalPhysicalMemory"><xsl:value-of select="@TotalPhysicalMemory"/></xsl:variable>
+            <xsl:variable name="LogicalProcessorsPerPhysical">
+                <xsl:value-of select="@LogicalProcessorsPerPhysical"/>
+            </xsl:variable>
+            <xsl:variable name="ProcessorClockFrequency">
+                <xsl:value-of select="@ProcessorClockFrequency"/>
+            </xsl:variable>
+            <properties>
+                <property name="BuildName" value="{$BuildName}" />
+                <property name="BuildStamp" value="{$BuildStamp}" />
+                <property name="Name" value="{$Name}" />
+                <property name="Generator" value="{$Generator}" />
+                <property name="CompilerName" value="{$CompilerName}" />
+                <property name="OSName" value="{$OSName}" />
+                <property name="Hostname" value="{$Hostname}" />
+                <property name="OSRelease" value="{$OSRelease}" />
+                <property name="OSVersion" value="{$OSVersion}" />
+                <property name="OSPlatform" value="{$OSPlatform}" />
+                <property name="Is64Bits" value="{$Is64Bits}" />
+                <property name="VendorString" value="{$VendorString}" />
+                <property name="VendorID" value="{$VendorID}" />
+                <property name="FamilyID" value="{$FamilyID}" />
+                <property name="ModelID" value="{$ModelID}" />
+                <property name="ProcessorCacheSize" value="{$ProcessorCacheSize}" />
+                <property name="NumberOfLogicalCPU" value="{$NumberOfLogicalCPU}" />
+                <property name="NumberOfPhysicalCPU" value="{$NumberOfPhysicalCPU}" />
+                <property name="TotalVirtualMemory" value="{$TotalVirtualMemory}" />
+                <property name="TotalPhysicalMemory" value="{$TotalPhysicalMemory}" />
+                <property name="LogicalProcessorsPerPhysical" value="{$LogicalProcessorsPerPhysical}" />
+                <property name="ProcessorClockFrequency" value="{$ProcessorClockFrequency}" />
+            </properties>
+            <xsl:apply-templates select="Testing/Test"/>
+
+            <system-out>
+                BuildName: <xsl:value-of select="$BuildName" />
+                BuildStamp: <xsl:value-of select="$BuildStamp" />
+                Name: <xsl:value-of select="$Name" />
+                Generator: <xsl:value-of select="$Generator" />
+                CompilerName: <xsl:value-of select="$CompilerName" />
+                OSName: <xsl:value-of select="$OSName" />
+                Hostname: <xsl:value-of select="$Hostname" />
+                OSRelease: <xsl:value-of select="$OSRelease" />
+                OSVersion: <xsl:value-of select="$OSVersion" />
+                OSPlatform: <xsl:value-of select="$OSPlatform" />
+                Is64Bits: <xsl:value-of select="$Is64Bits" />
+                VendorString: <xsl:value-of select="$VendorString" />
+                VendorID: <xsl:value-of select="$VendorID" />
+                FamilyID: <xsl:value-of select="$FamilyID" />
+                ModelID: <xsl:value-of select="$ModelID" />
+                ProcessorCacheSize: <xsl:value-of select="$ProcessorCacheSize" />
+                NumberOfLogicalCPU: <xsl:value-of select="$NumberOfLogicalCPU" />
+                NumberOfPhysicalCPU: <xsl:value-of select="$NumberOfPhysicalCPU" />
+                TotalVirtualMemory: <xsl:value-of select="$TotalVirtualMemory" />
+                TotalPhysicalMemory: <xsl:value-of select="$TotalPhysicalMemory" />
+                LogicalProcessorsPerPhysical: <xsl:value-of select="$LogicalProcessorsPerPhysical" />
+                ProcessorClockFrequency: <xsl:value-of select="$ProcessorClockFrequency" />
+            </system-out>
+        </testsuite>
+    </xsl:template>
+
+    <xsl:template match="Testing/Test">
+        <xsl:variable name="testcasename"><xsl:value-of select= "Name"/></xsl:variable>
+        <xsl:variable name="testsuitename"><xsl:value-of select= "Path"/></xsl:variable>
+        <xsl:variable name="exectime">
+            <xsl:for-each select="Results/NamedMeasurement">
+                <xsl:if test="@name = 'Execution Time'">
+                    <xsl:value-of select="Value"/>
+                </xsl:if>
+            </xsl:for-each>
+        </xsl:variable>
+
+        <testcase name="{$testcasename}" classname="{$testsuitename}" time="{$exectime}">
+            <xsl:if test="@Status = 'passed'">
+            </xsl:if>
+            <xsl:if test="@Status = 'failed'">
+                <xsl:variable name="failtype">
+                    <xsl:for-each select="Results/NamedMeasurement">
+                        <xsl:if test="@name = 'Exit Code'">
+                            <xsl:value-of select="Value/text()"/>
+                        </xsl:if>
+                    </xsl:for-each>
+                </xsl:variable>
+                <xsl:variable name="failcode">
+                    <xsl:for-each select="Results/NamedMeasurement">
+                        <xsl:if test="@name = 'Exit Value'">
+                            <xsl:value-of select="Value/text()"/>
+                        </xsl:if>
+                    </xsl:for-each>
+                </xsl:variable>
+                <error message="{$failtype} ({$failcode})">
+                    <xsl:value-of select="Results/Measurement/Value/text()" />
+                </error>
+            </xsl:if>
+            <xsl:if test="@Status = 'notrun'">
+                <skipped><xsl:value-of select="Results/Measurement/Value/text()" /></skipped>
+            </xsl:if>
+        </testcase>
+    </xsl:template>
+
+</xsl:stylesheet>
+"""
+
+    xml_doc = etree.parse(test_xml)
+    xslt_root = etree.XML(xsl)
+    transform = etree.XSLT(xslt_root)
+    result_tree = transform(xml_doc)
+    return result_tree
+
+
+if __name__ == "__main__":
+    print(process(sys.argv[1]))
diff --git a/cmake/waLBerlaFunctions.cmake b/cmake/waLBerlaFunctions.cmake
index a5822aa849337b6550dc5d546899d85bd2978d9e..2974ca22d9bd24988558c33cd5b07b59a58432c2 100644
--- a/cmake/waLBerlaFunctions.cmake
+++ b/cmake/waLBerlaFunctions.cmake
@@ -99,7 +99,7 @@ function ( waLBerla_add_module )
 
     waLBerla_register_dependency ( ${moduleName} ${ARG_DEPENDS} )
 
-    set_property( TARGET ${moduleName} PROPERTY CXX_STANDARD 14 )
+    set_property( TARGET ${moduleLibraryName} PROPERTY CXX_STANDARD ${CMAKE_CXX_STANDARD} )
 
     # This property is needed for visual studio to group modules together
     if( WALBERLA_GROUP_PROJECTS )
@@ -204,7 +204,7 @@ function ( waLBerla_add_executable )
 
     target_link_modules  ( ${ARG_NAME} ${ARG_DEPENDS}  )
     target_link_libraries( ${ARG_NAME} ${WALBERLA_LINK_LIBRARIES_KEYWORD} ${SERVICE_LIBS} )
-    set_property( TARGET ${ARG_NAME} PROPERTY CXX_STANDARD 14 )
+    set_property( TARGET ${ARG_NAME} PROPERTY CXX_STANDARD ${CMAKE_CXX_STANDARD} )
 
     if( WALBERLA_GROUP_PROJECTS )
         if( NOT ARG_GROUP )
diff --git a/doc/doxygen.in b/doc/doxygen.in
index a98ddcec124a1f973381240d1d5dcbaa5bf91b77..ca467e074e41bf329a27a391bf78c3d5b534f004 100644
--- a/doc/doxygen.in
+++ b/doc/doxygen.in
@@ -2093,7 +2093,7 @@ HIDE_UNDOC_RELATIONS   = YES
 # set to NO
 # The default value is: NO.
 
-HAVE_DOT               = @DOXYGEN_DOT_FOUND@
+HAVE_DOT               = NO
 
 # The DOT_NUM_THREADS specifies the number of dot invocations doxygen is allowed
 # to run in parallel. When set to 0 doxygen will base this on the number of
diff --git a/doc/setup.dox b/doc/setup.dox
index 5368c1e5e1d042724735b18d0c0453790caabc29..ab9ba0588aa13b53cb631ed84d226f2c3a03d520 100644
--- a/doc/setup.dox
+++ b/doc/setup.dox
@@ -22,12 +22,6 @@ If you are not familiar with the git version control system, this
 To compile waLBerla with all modules and features a few third-party libraries
 are required. 
 
-\subsubsection libs_boost Boost
-The boost library is the only required third-party library. It is installed on
-most Linux system by default and should be detected by cmake. 
-If boost resides at a non-standard location, you have to set the environment variable
-`BOOST_ROOT` in order to have it detected by cmake.
-
 \subsubsection libs_mpi   MPI (optional)
 The message passing interface (MPI) library is required if you want to run waLBerla
 with distributed memory parallelization. If MPI is not installed on your system,
@@ -73,8 +67,7 @@ WALBERLA_BUILD_WITH_MPI            |    ON    | Since one main goal of waLBerla
 WALBERLA_BUILD_WITH_OPENMP         |    OFF   | Enables/Disables OpenMP support for thread-parallel computation. Can also be combined with MPI for hybrid simulations!
 WALBERLA_BUILD_TESTS		       |	OFF   | If enabled, all tests are built when running make in the root build folder. But you can always go to a specific directory in your test folder and manually run make.
 WALBERLA_BUILD_BENCHMARKS          |	ON    | Enables/Disables the automatic build of all benchmarks located in "apps/benchmarks".  
-WALBERLA_BUILD_WITH_PYTHON         |	OFF   | Enables Python Support inside waLBerla (embedded Python). Then you can use Python scripts as configuration files and start an embedded python interpreter that can access waLBerla data structures. 
-WALBERLA_BUILD_WITH_PYTHON_MODULE  |	OFF   | This builds a shared library (and python module) walberla.so in "apps/pythonmodule" so that you can use walberla from python.  
+WALBERLA_BUILD_WITH_PYTHON         |	OFF   | Enables Python Support inside waLBerla (embedded Python). Then you can use Python scripts as configuration files and start an embedded python interpreter that can access waLBerla data structures. This builds a shared library (and python module) walberla_cpp.so in "apps/pythonmodule" so that you can use walberla from python.
 
 For a list of all switches, see CMakeLists.txt in the root source folder.
 
@@ -87,40 +80,13 @@ e.g. geometry setup, post-processing or steering. This mode can be enabled by th
 
 While in this first option the driving code is C++, the second option exports most of waLBerlas functionality as a
 Python module that can be imported from a regular Python script. Thus the application itself can be written in Python.
-To build and install the waLBerla Python module enable WALBERLA_BUILD_WITH_PYTHON and WALBERLA_BUILD_WITH_PYTHON_MODULE.
+To build and install the waLBerla Python module enable WALBERLA_BUILD_WITH_PYTHON.
 Then run 'make pythonModule' to build and 'make pythonModuleInstall' to install the module in your current environment.
+The python coupling is build up on <a target="_blank" href="https://pybind11.readthedocs.io/en/stable/index.html">pybind11</a> entirely. pybind11
+provides a lightweight header only solution for that. Thus there is no need to install additional software since pybind11
+is shipped with waLBerla as a submodule. The integration happens automatically when waLBerla is build with python.
 
-The third option is code generation using 'pystencils' and 'lbmpy' and is covered later.
-
-\subsection python_boost Installing Python dependencies.
-
-Both variants described above introduce dependencies on the Python and the boost::python library. We support building
-against Python in version 3.3 and above. On Ubuntu install 'python3-dev' and 'libboost-python-dev'. Then CMake
-automatically finds the right libraries.
-
-On clusters the setup process can be more complicated. Usually boost has to be compiled manually there. One very common
-problem then is to not link boost::python against the same python lib as waLBerla is linked against. For example when boost
-python is built against Python2 and waLBerla linked against Python3, linker errors occur. You can test what your
-boost python was linked against with 'grep -rl "PyString_Type"  libbboost_python'. If something is found, it was built
-against Python2, if not, against Python3.
-
-To build boost correctly against a certain Python version do:
-\verbatim
-./bootstrap.sh --with-python=/path/to/python3 --with-python-root=/base/dir/with/lib/and/include/ --with-python-version=3.Xm
-\endverbatim
-
-To be on the safe side then edit the project-config.jam and adapt the following line (here for example python3.6m)
-by entering the location of the interpreter, include path and lib path:
-\verbatim
-using python : 3.6 : /path/to/python3 : /python/root/include/python3.6m/ : /python/root/lib/ ;
-\endverbatim
-
-Then build with
-\verbatim
-./b2 -j $NUM_PROCS toolset=$TOOLSET install --prefix=$PREFIX
-\endverbatim
-
-Don't forget to set BOOST_ROOT such that CMake finds your custom boost library.
+The third option is code generation using <a target="_blank" href="https://pycodegen.pages.i10git.cs.fau.de/pystencils/">pystencils</a> and <a href="https://pycodegen.pages.i10git.cs.fau.de/lbmpy" target="_blank">lbmpy</a> and is covered in the tutorial section of waLBerla.
 
 \section ide_setup Setup of Integrated Development Environments
 
diff --git a/extern/pybind11 b/extern/pybind11
new file mode 160000
index 0000000000000000000000000000000000000000..8de7772cc72daca8e947b79b83fea46214931604
--- /dev/null
+++ b/extern/pybind11
@@ -0,0 +1 @@
+Subproject commit 8de7772cc72daca8e947b79b83fea46214931604
diff --git a/python/lbmpy_walberla/__init__.py b/python/lbmpy_walberla/__init__.py
index f7b022ab058e6a83a05aafa5fa586fb59d8551fd..1a7b136a7935d13be6c47d8a101e9efc4c631f56 100644
--- a/python/lbmpy_walberla/__init__.py
+++ b/python/lbmpy_walberla/__init__.py
@@ -1,4 +1,5 @@
 from .boundary import generate_boundary
 from .walberla_lbm_generation import RefinementScaling, generate_lattice_model
+from .packinfo import generate_lb_pack_info
 
-__all__ = ['generate_lattice_model', 'RefinementScaling', 'generate_boundary']
+__all__ = ['generate_lattice_model', 'RefinementScaling', 'generate_boundary', 'generate_lb_pack_info']
diff --git a/python/lbmpy_walberla/additional_data_handler.py b/python/lbmpy_walberla/additional_data_handler.py
new file mode 100644
index 0000000000000000000000000000000000000000..804c4953e2bd31f8b2cc905c62663e99bf26476b
--- /dev/null
+++ b/python/lbmpy_walberla/additional_data_handler.py
@@ -0,0 +1,127 @@
+from pystencils.stencil import inverse_direction
+
+from lbmpy.advanced_streaming import AccessPdfValues, numeric_offsets, numeric_index
+from lbmpy.boundaries import ExtrapolationOutflow, UBB
+
+from pystencils_walberla.additional_data_handler import AdditionalDataHandler
+
+
+class UBBAdditionalDataHandler(AdditionalDataHandler):
+    def __init__(self, stencil, boundary_object):
+        assert isinstance(boundary_object, UBB)
+        self._boundary_object = boundary_object
+        super(UBBAdditionalDataHandler, self).__init__(stencil=stencil)
+
+    @property
+    def constructor_arguments(self):
+        return ", std::function<Vector3<real_t>(const Cell &, const shared_ptr<StructuredBlockForest>&, IBlock&)>& " \
+               "velocityCallback "
+
+    @property
+    def initialiser_list(self):
+        return "elementInitaliser(velocityCallback),"
+
+    @property
+    def additional_arguments_for_fill_function(self):
+        return "blocks, "
+
+    @property
+    def additional_parameters_for_fill_function(self):
+        return " const shared_ptr<StructuredBlockForest> &blocks, "
+
+    def data_initialisation(self, direction):
+        init_list = ["Vector3<real_t> InitialisatonAdditionalData = elementInitaliser(Cell(it.x(), it.y(), it.z()), "
+                     "blocks, *block);", "element.vel_0 = InitialisatonAdditionalData[0];",
+                     "element.vel_1 = InitialisatonAdditionalData[1];"]
+        if self._dim == 3:
+            init_list.append("element.vel_2 = InitialisatonAdditionalData[2];")
+
+        return "\n".join(init_list)
+
+    @property
+    def additional_member_variable(self):
+        return "std::function<Vector3<real_t>(const Cell &, const shared_ptr<StructuredBlockForest>&, IBlock&)> " \
+               "elementInitaliser; "
+
+
+class OutflowAdditionalDataHandler(AdditionalDataHandler):
+    def __init__(self, stencil, boundary_object, target='cpu', field_name='pdfs'):
+        assert isinstance(boundary_object, ExtrapolationOutflow)
+        self._boundary_object = boundary_object
+        self._stencil = boundary_object.stencil
+        self._lb_method = boundary_object.lb_method
+        self._normal_direction = boundary_object.normal_direction
+        self._field_name = field_name
+        self._target = target
+        super(OutflowAdditionalDataHandler, self).__init__(stencil=stencil)
+
+        assert sum([a != 0 for a in self._normal_direction]) == 1,\
+            "The outflow boundary is only implemented for straight walls at the moment."
+
+    @property
+    def constructor_arguments(self):
+        return f", BlockDataID {self._field_name}CPUID_" if self._target == 'gpu' else ""
+
+    @property
+    def initialiser_list(self):
+        return f"{self._field_name}CPUID({self._field_name}CPUID_)," if self._target == 'gpu' else ""
+
+    @property
+    def additional_field_data(self):
+        identifier = "CPU" if self._target == "gpu" else ""
+        return f"auto {self._field_name} = block->getData< field::GhostLayerField<double, " \
+               f"{len(self._stencil)}> >({self._field_name}{identifier}ID); "
+
+    def data_initialisation(self, direction_index):
+        pdf_acc = AccessPdfValues(self._boundary_object.stencil,
+                                  streaming_pattern=self._boundary_object.streaming_pattern,
+                                  timestep=self._boundary_object.zeroth_timestep,
+                                  streaming_dir='out')
+
+        init_list = []
+        for key, value in self.get_init_dict(pdf_acc, direction_index).items():
+            init_list.append(f"element.{key} = {self._field_name}->get({value});")
+
+        return "\n".join(init_list)
+
+    @property
+    def additional_member_variable(self):
+        return f"BlockDataID {self._field_name}CPUID;"
+
+    @property
+    def stencil_info(self):
+        stencil_info = []
+        for i, d in enumerate(self._stencil):
+            if any([a != 0 and b != 0 and a == b for a, b in zip(self._normal_direction, d)]):
+                direction = d if self._dim == 3 else d + (0,)
+                stencil_info.append((i, direction, ", ".join([str(e) for e in direction])))
+        return stencil_info
+
+    def get_init_dict(self, pdf_accessor, direction_index):
+        """The Extrapolation Outflow boundary needs additional data. This function provides a list of all values
+        which have to be initialised"""
+        position = ["it.x()", "it.y()", "it.z()"]
+        direction = self._stencil[direction_index]
+        inv_dir = self._stencil.index(inverse_direction(direction))
+
+        tangential_offset = tuple(offset - normal for offset, normal in zip(direction, self._normal_direction))
+
+        result = {}
+        pos = []
+        offsets = numeric_offsets(pdf_accessor.accs[inv_dir])
+        for p, o, t in zip(position, offsets, tangential_offset):
+            pos.append(p + f" + cell_idx_c({str(o + t)})")
+        if self._dim == 2:
+            pos.append("0")
+        pos.append(str(numeric_index(pdf_accessor.accs[inv_dir])[0]))
+        result[f'pdf'] = ', '.join(pos)
+
+        pos = []
+        for p, o, t in zip(position, offsets, tangential_offset):
+            pos.append(p + f" + cell_idx_c({str(o + t)})")
+        if self._dim == 2:
+            pos.append("0")
+        pos.append(str(numeric_index(pdf_accessor.accs[inv_dir])[0]))
+        result[f'pdf_nd'] = ', '.join(pos)
+
+        return result
diff --git a/python/lbmpy_walberla/boundary.py b/python/lbmpy_walberla/boundary.py
index ba1d697b968805548d084dac248d480e6ab76aa3..b0dafb7b3b9835073101acfa33a611c71c62095d 100644
--- a/python/lbmpy_walberla/boundary.py
+++ b/python/lbmpy_walberla/boundary.py
@@ -1,70 +1,44 @@
-import numpy as np
-from jinja2 import Environment, PackageLoader, StrictUndefined
-
+import pystencils_walberla.boundary
 from lbmpy.boundaries.boundaryhandling import create_lattice_boltzmann_boundary_kernel
-from pystencils import Field, FieldType
-from pystencils.boundaries.createindexlist import numpy_data_type_for_boundary_object
-from pystencils.data_types import TypedSymbol, create_type
-from pystencils_walberla.boundary import struct_from_numpy_dtype
-from pystencils_walberla.codegen import default_create_kernel_parameters, KernelInfo
-from pystencils_walberla.jinja_filters import add_pystencils_filters_to_jinja_env
-
-
-def generate_boundary(generation_context, class_name, boundary_object, lb_method, **create_kernel_params):
-    struct_name = "IndexInfo"
-    boundary_object.name = class_name
-
-    create_kernel_params = default_create_kernel_parameters(generation_context, create_kernel_params)
-    target = create_kernel_params['target']
-
-    index_struct_dtype = numpy_data_type_for_boundary_object(boundary_object, lb_method.dim)
-
-    pdf_field = Field.create_generic('pdfs', lb_method.dim,
-                                     np.float64 if generation_context.double_accuracy else np.float32,
-                                     index_dimensions=1, layout='fzyx', index_shape=[len(lb_method.stencil)])
-
-    index_field = Field('indexVector', FieldType.INDEXED, index_struct_dtype, layout=[0],
-                        shape=(TypedSymbol("indexVectorSize", create_type(np.int64)), 1), strides=(1, 1))
-
-    kernel = create_lattice_boltzmann_boundary_kernel(pdf_field, index_field, lb_method, boundary_object, target=target,
-                                                      openmp=generation_context.openmp)
-    kernel.function_name = "boundary_" + boundary_object.name
-    kernel.assumed_inner_stride_one = False
-
-    # waLBerla is a 3D framework. Therefore, a zero for the z index has to be added if we work in 2D
-    if lb_method.dim == 2:
-        stencil = ()
-        for d in lb_method.stencil:
-            d = d + (0,)
-            stencil = stencil + (d,)
-    else:
-        stencil = lb_method.stencil
-
-    stencil_info = [(i, d, ", ".join([str(e) for e in d])) for i, d in enumerate(stencil)]
-    inv_dirs = []
-    for direction in stencil:
-        inverse_dir = tuple([-i for i in direction])
-        inv_dirs.append(stencil.index(inverse_dir))
-
-    context = {
-        'class_name': boundary_object.name,
-        'StructName': struct_name,
-        'StructDeclaration': struct_from_numpy_dtype(struct_name, index_struct_dtype),
-        'kernel': KernelInfo(kernel),
-        'stencil_info': stencil_info,
-        'inverse_directions': inv_dirs,
-        'dim': lb_method.dim,
-        'target': target,
-        'namespace': 'lbm',
-        'inner_or_boundary': boundary_object.inner_or_boundary
-    }
-
-    env = Environment(loader=PackageLoader('pystencils_walberla'), undefined=StrictUndefined)
-    add_pystencils_filters_to_jinja_env(env)
-
-    header = env.get_template('Boundary.tmpl.h').render(**context)
-    source = env.get_template('Boundary.tmpl.cpp').render(**context)
-
-    source_extension = "cpp" if create_kernel_params.get("target", "cpu") == "cpu" else "cu"
-    generation_context.write_file("{}.h".format(class_name), header)
-    generation_context.write_file("{}.{}".format(class_name, source_extension), source)
+from lbmpy.advanced_streaming import Timestep, is_inplace
+
+
+def generate_boundary(generation_context,
+                      class_name,
+                      boundary_object,
+                      lb_method,
+                      field_name='pdfs',
+                      streaming_pattern='pull',
+                      always_generate_separate_classes=False,
+                      additional_data_handler=None,
+                      **create_kernel_params):
+    def boundary_creation_function(field, index_field, stencil, boundary_functor, target='cpu', openmp=True, **kwargs):
+        pargs = (field, index_field, lb_method, boundary_functor)
+        kwargs = {'target': target, **kwargs}
+        if is_inplace(streaming_pattern) or always_generate_separate_classes:
+            return {
+                'EvenSweep': create_lattice_boltzmann_boundary_kernel(*pargs,
+                                                                      streaming_pattern=streaming_pattern,
+                                                                      prev_timestep=Timestep.EVEN,
+                                                                      **kwargs),
+                'OddSweep': create_lattice_boltzmann_boundary_kernel(*pargs,
+                                                                     streaming_pattern=streaming_pattern,
+                                                                     prev_timestep=Timestep.ODD,
+                                                                     **kwargs)
+            }
+        else:
+            return create_lattice_boltzmann_boundary_kernel(*pargs,
+                                                            streaming_pattern=streaming_pattern,
+                                                            prev_timestep=Timestep.BOTH,
+                                                            **kwargs)
+
+    pystencils_walberla.boundary.generate_boundary(generation_context,
+                                                   class_name,
+                                                   boundary_object,
+                                                   field_name=field_name,
+                                                   neighbor_stencil=lb_method.stencil,
+                                                   index_shape=[len(lb_method.stencil)],
+                                                   kernel_creation_function=boundary_creation_function,
+                                                   namespace='lbm',
+                                                   additional_data_handler=additional_data_handler,
+                                                   **create_kernel_params)
diff --git a/python/lbmpy_walberla/packinfo.py b/python/lbmpy_walberla/packinfo.py
new file mode 100644
index 0000000000000000000000000000000000000000..d6a0ab23c6d1a01dc5f0ba9c866781eba4cf2dba
--- /dev/null
+++ b/python/lbmpy_walberla/packinfo.py
@@ -0,0 +1,85 @@
+from collections import defaultdict
+from lbmpy.stencils import get_stencil
+from lbmpy.advanced_streaming.utility import Timestep, get_accessor, get_timesteps
+from lbmpy.advanced_streaming.communication import _extend_dir
+from pystencils.stencil import inverse_direction
+from pystencils_walberla.codegen import comm_directions, generate_pack_info
+from pystencils import Assignment, Field
+
+
+def generate_lb_pack_info(generation_context,
+                          class_name_prefix: str,
+                          stencil,
+                          pdf_field,
+                          streaming_pattern='pull',
+                          lb_collision_rule=None,
+                          always_generate_separate_classes=False,
+                          namespace='lbm',
+                          **create_kernel_params):
+    """Generates waLBerla MPI PackInfos for an LBM kernel, based on a given method
+    and streaming pattern. For in-place streaming patterns, two PackInfos are generated;
+    one for the even and another for the odd time steps.
+
+    Args:
+        generation_context: see documentation of `generate_sweep`
+        class_name_prefix: Prefix of the desired class name which will be extended with
+                           'Even' or 'Odd' for in-place kernels
+        stencil: The tuple of directions specifying the employed LB stencil.
+        pdf_field: pdf field for which the pack info is created
+        streaming_pattern: The employed streaming pattern.
+        lb_collision_rule: Optional. The collision rule defining the lattice boltzmann kernel, as returned
+                           by `create_lb_collision_rule`. If specified, it will be scanned for non-local
+                           accesses to other fields other than the PDF fields (as might be required for
+                           computing gradients in coupled simulations), whose communication will then
+                           be included in the PackInfo.
+        always_generate_separate_classes: If True, generate a pair of Even/Odd PackInfos even for a two-
+                                          fields kernel (i.e. the pull/push patterns). Otherwise, for two-fields
+                                          kernels, only one PackInfo class will be generated without a
+                                          suffix to its name.
+        namespace: inner namespace of the generated class
+        **create_kernel_params: remaining keyword arguments are passed to `pystencils.create_kernel`
+    """
+    timesteps = [Timestep.EVEN, Timestep.ODD] \
+        if always_generate_separate_classes \
+        else get_timesteps(streaming_pattern)
+
+    common_spec = defaultdict(set)
+
+    if lb_collision_rule is not None:
+        assignments = lb_collision_rule.all_assignments
+        reads = set()
+        for a in assignments:
+            if not isinstance(a, Assignment):
+                continue
+            reads.update(a.rhs.atoms(Field.Access))
+        for fa in reads:
+            assert all(abs(e) <= 1 for e in fa.offsets)
+            if all(offset == 0 for offset in fa.offsets):
+                continue
+            comm_direction = inverse_direction(fa.offsets)
+            for comm_dir in comm_directions(comm_direction):
+                common_spec[(comm_dir,)].add(fa.field.center(*fa.index))
+
+    full_stencil = get_stencil('D3Q27') if len(stencil[0]) == 3 else get_stencil('D2Q9')
+
+    for t in timesteps:
+        spec = common_spec.copy()
+        write_accesses = get_accessor(streaming_pattern, t).write(pdf_field, stencil)
+        for comm_dir in full_stencil:
+            if all(d == 0 for d in comm_dir):
+                continue
+
+            for streaming_dir in set(_extend_dir(comm_dir)) & set(stencil):
+                d = stencil.index(streaming_dir)
+                fa = write_accesses[d]
+                spec[(comm_dir,)].add(fa)
+
+        if t == Timestep.EVEN:
+            class_name_suffix = 'Even'
+        elif t == Timestep.ODD:
+            class_name_suffix = 'Odd'
+        else:
+            class_name_suffix = ''
+
+        class_name = class_name_prefix + class_name_suffix
+        generate_pack_info(generation_context, class_name, spec, namespace=namespace, **create_kernel_params)
diff --git a/python/lbmpy_walberla/tests/test_walberla_codegen.py b/python/lbmpy_walberla/tests/test_walberla_codegen.py
index 391743468a66994f9150eab066263e880b95ae3c..e38880acadeae832ae377a24d366e6e263ae34b8 100644
--- a/python/lbmpy_walberla/tests/test_walberla_codegen.py
+++ b/python/lbmpy_walberla/tests/test_walberla_codegen.py
@@ -85,7 +85,7 @@ class WalberlaLbmpyCodegenTest(unittest.TestCase):
             collision_rule = create_lb_collision_rule(
                 stencil='D3Q19', compressible=True, fluctuating={'seed': 0, 'temperature': 1e-6},
                 method='mrt', relaxation_rates=[omega_shear] * 19,
-                force_model='guo', force=force_field.center_vector,
+                force_model='schiller', force=force_field.center_vector,
                 optimization={'cse_global': False}
             )
             generate_lattice_model(ctx, 'FluctuatingMRT', collision_rule)
diff --git a/python/lbmpy_walberla/walberla_lbm_generation.py b/python/lbmpy_walberla/walberla_lbm_generation.py
index f5de71ecae30208adde8c4ff959ebf245f4b6524..1f16981fc1783df9cd66c8c3ea0e78437d4993e9 100644
--- a/python/lbmpy_walberla/walberla_lbm_generation.py
+++ b/python/lbmpy_walberla/walberla_lbm_generation.py
@@ -6,7 +6,6 @@ from jinja2 import Environment, PackageLoader, StrictUndefined, Template
 from sympy.tensor import IndexedBase
 
 import pystencils as ps
-# from lbmpy.creationfunctions import create_lb_update_rule, update_with_default_parameters
 from lbmpy.fieldaccess import CollideOnlyInplaceAccessor, StreamPullTwoFieldsAccessor
 from lbmpy.relaxationrates import relaxation_rate_scaling
 from lbmpy.stencils import get_stencil
diff --git a/python/mesa_pd.py b/python/mesa_pd.py
index cc8b5309131ae2e90b3cd32a04bd77599fef9256..16d089ceecf24094e773f69c6e799258fe54f7f2 100755
--- a/python/mesa_pd.py
+++ b/python/mesa_pd.py
@@ -63,6 +63,10 @@ if __name__ == '__main__':
     ps.add_property("oldHydrodynamicTorque", "walberla::mesa_pd::Vec3", defValue="real_t(0)",
                     syncMode="ON_OWNERSHIP_CHANGE")
 
+    # properties for VBond model
+    ps.add_property("clusterID", "int64_t", defValue="-1", syncMode="ON_GHOST_CREATION")
+    ps.add_property("segmentID", "int64_t", defValue="-1", syncMode="ON_GHOST_CREATION")
+
     ch = mpd.add(data.ContactHistory())
     ch.add_property("tangentialSpringDisplacement", "walberla::mesa_pd::Vec3", defValue="real_t(0)")
     ch.add_property("isSticking", "bool", defValue="false")
@@ -103,6 +107,8 @@ if __name__ == '__main__':
     mpd.add(kernel.InsertParticleIntoSparseLinkedCells())
     mpd.add(kernel.LinearSpringDashpot())
     mpd.add(kernel.NonLinearSpringDashpot())
+    mpd.add(kernel.PFCDamping())
+    mpd.add(kernel.SemiImplicitEuler())
     mpd.add(kernel.SingleCast(ps))
     mpd.add(kernel.SpringDashpot())
     mpd.add(kernel.SpringDashpotSpring())
diff --git a/python/mesa_pd/kernel/PFCDamping.py b/python/mesa_pd/kernel/PFCDamping.py
new file mode 100644
index 0000000000000000000000000000000000000000..c3faf5e9829f80652bcf93e02cb7357ed51e5636
--- /dev/null
+++ b/python/mesa_pd/kernel/PFCDamping.py
@@ -0,0 +1,30 @@
+# -*- coding: utf-8 -*-
+
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
+
+
+class PFCDamping:
+    def __init__(self, integrate_rotation=True):
+        self.context = {'bIntegrateRotation': integrate_rotation,
+                        'interface': [
+                            create_access("linearVelocity", "walberla::mesa_pd::Vec3", access="gs"),
+                            create_access("force", "walberla::mesa_pd::Vec3", access="gs"),
+                            create_access("flags", "walberla::mesa_pd::data::particle_flags::FlagT", access="g")
+                        ]
+                        }
+
+        if integrate_rotation:
+            self.context['interface'].append(create_access("angularVelocity", "walberla::mesa_pd::Vec3", access="gs"))
+            self.context['interface'].append(create_access("torque", "walberla::mesa_pd::Vec3", access="gs"))
+
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        generate_file(module['module_path'], 'kernel/PFCDamping.templ.h', ctx)
+
+        ctx["InterfaceTestName"] = "PFCDampingInterfaceCheck"
+        ctx["KernelInclude"] = "kernel/PFCDamping.h"
+        ctx["ExplicitInstantiation"] = \
+            "template void kernel::PFCDamping::operator()(const size_t p_idx1, Accessor& ac) const;"
+        generate_file(module['test_path'], 'tests/CheckInterface.templ.cpp', ctx,
+                      'kernel/interfaces/PFCDampingInterfaceCheck.cpp')
diff --git a/python/mesa_pd/kernel/SemiImplicitEuler.py b/python/mesa_pd/kernel/SemiImplicitEuler.py
new file mode 100644
index 0000000000000000000000000000000000000000..f5b6632a1f57d7dba5631896a3bd26b5a5cce177
--- /dev/null
+++ b/python/mesa_pd/kernel/SemiImplicitEuler.py
@@ -0,0 +1,34 @@
+# -*- coding: utf-8 -*-
+
+from mesa_pd.accessor import create_access
+from mesa_pd.utility import generate_file
+
+
+class SemiImplicitEuler:
+    def __init__(self, integrate_rotation=True):
+        self.context = {'bIntegrateRotation': integrate_rotation,
+                        'interface': [
+                            create_access("position", "walberla::mesa_pd::Vec3", access="gs"),
+                            create_access("linearVelocity", "walberla::mesa_pd::Vec3", access="gs"),
+                            create_access("invMass", "walberla::real_t", access="g"),
+                            create_access("force", "walberla::mesa_pd::Vec3", access="gs"),
+                            create_access("flags", "walberla::mesa_pd::data::particle_flags::FlagT", access="g")
+                        ]
+                        }
+
+        if integrate_rotation:
+            self.context['interface'].append(create_access("rotation", "walberla::mesa_pd::Rot3", access="gs"))
+            self.context['interface'].append(create_access("angularVelocity", "walberla::mesa_pd::Vec3", access="gs"))
+            self.context['interface'].append(create_access("invInertiaBF", "walberla::mesa_pd::Mat3", access="g"))
+            self.context['interface'].append(create_access("torque", "walberla::mesa_pd::Vec3", access="gs"))
+
+    def generate(self, module):
+        ctx = {'module': module, **self.context}
+        generate_file(module['module_path'], 'kernel/SemiImplicitEuler.templ.h', ctx)
+
+        ctx["InterfaceTestName"] = "SemiImplicitEulerInterfaceCheck"
+        ctx["KernelInclude"] = "kernel/SemiImplicitEuler.h"
+        ctx["SemiImplicitEulerInstantiation"] = \
+            "template void kernel::SemiImplicitEuler::operator()(const size_t p_idx1, Accessor& ac) const;"
+        generate_file(module['test_path'], 'tests/CheckInterface.templ.cpp', ctx,
+                      'kernel/interfaces/SemiImplicitEulerInterfaceCheck.cpp')
diff --git a/python/mesa_pd/kernel/__init__.py b/python/mesa_pd/kernel/__init__.py
index 317646d1e961b70c81c12e877161b353d9840bd8..fda6d3c46645040607b4c5e14d0509967e4ccea6 100644
--- a/python/mesa_pd/kernel/__init__.py
+++ b/python/mesa_pd/kernel/__init__.py
@@ -12,6 +12,8 @@ from .InsertParticleIntoLinkedCells import InsertParticleIntoLinkedCells
 from .InsertParticleIntoSparseLinkedCells import InsertParticleIntoSparseLinkedCells
 from .LinearSpringDashpot import LinearSpringDashpot
 from .NonLinearSpringDashpot import NonLinearSpringDashpot
+from .PFCDamping import PFCDamping
+from .SemiImplicitEuler import SemiImplicitEuler
 from .SingleCast import SingleCast
 from .SpringDashpot import SpringDashpot
 from .SpringDashpotSpring import SpringDashpotSpring
@@ -31,6 +33,8 @@ __all__ = ['DoubleCast',
            'InsertParticleIntoSparseLinkedCells',
            'LinearSpringDashpot',
            'NonLinearSpringDashpot',
+           'PFCDamping',
+           'SemiImplicitEuler',
            'SingleCast',
            'SpringDashpot',
            'SpringDashpotSpring',
diff --git a/python/mesa_pd/templates/common/ParticleFunctions.templ.h b/python/mesa_pd/templates/common/ParticleFunctions.templ.h
index 1ed2d53db7188722ef1ad457105c9e32202576bd..807a1291949b644f6f17b637d008c19688cd9467 100644
--- a/python/mesa_pd/templates/common/ParticleFunctions.templ.h
+++ b/python/mesa_pd/templates/common/ParticleFunctions.templ.h
@@ -13,7 +13,7 @@
 //  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 ParticleFunctions.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/data/ContactAccessor.templ.h b/python/mesa_pd/templates/data/ContactAccessor.templ.h
index b51f5de7fd5f4f21d7f58f504da39043a652aeb0..86666fcc356720ca0f3d9f8d9efc305561b04bef 100644
--- a/python/mesa_pd/templates/data/ContactAccessor.templ.h
+++ b/python/mesa_pd/templates/data/ContactAccessor.templ.h
@@ -13,7 +13,7 @@
 //  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 ContactAccessor.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/data/ContactHistory.templ.h b/python/mesa_pd/templates/data/ContactHistory.templ.h
index b455a2fa98c43f8132ac4201dc70cbeb5655db1b..1ab195d65ad122826a872aab9b5d854d65435a94 100644
--- a/python/mesa_pd/templates/data/ContactHistory.templ.h
+++ b/python/mesa_pd/templates/data/ContactHistory.templ.h
@@ -13,7 +13,7 @@
 //  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 ContactHistory.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/data/ContactStorage.templ.h b/python/mesa_pd/templates/data/ContactStorage.templ.h
index 3731188bb421575cd41242f71186c891b5bd365f..a609ebb071197de77e4922fe8fce7ce16cce4b2e 100644
--- a/python/mesa_pd/templates/data/ContactStorage.templ.h
+++ b/python/mesa_pd/templates/data/ContactStorage.templ.h
@@ -13,7 +13,7 @@
 //  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 ContactStorage.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
diff --git a/python/mesa_pd/templates/data/LinkedCells.templ.h b/python/mesa_pd/templates/data/LinkedCells.templ.h
index 713e2d073810c76029212d19010e7da87c344d00..fdf8aa39e0c1b605d43099449642ade938ddd8bc 100644
--- a/python/mesa_pd/templates/data/LinkedCells.templ.h
+++ b/python/mesa_pd/templates/data/LinkedCells.templ.h
@@ -13,7 +13,7 @@
 //  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 LinkedCells.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/data/ParticleAccessor.templ.h b/python/mesa_pd/templates/data/ParticleAccessor.templ.h
index 7372641f421672b9da01482024ba029d8f75b3ba..701d802d05005c2c02816ef2f7f5818ac28673ba 100644
--- a/python/mesa_pd/templates/data/ParticleAccessor.templ.h
+++ b/python/mesa_pd/templates/data/ParticleAccessor.templ.h
@@ -13,7 +13,7 @@
 //  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 ParticleAccessor.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/data/ParticleStorage.templ.h b/python/mesa_pd/templates/data/ParticleStorage.templ.h
index 7f8d00bb3b5884e17dbfc57a4cf20596dbb373fe..37cbd40aec915977d309565a26e8027e3f60f058 100644
--- a/python/mesa_pd/templates/data/ParticleStorage.templ.h
+++ b/python/mesa_pd/templates/data/ParticleStorage.templ.h
@@ -13,7 +13,7 @@
 //  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 ParticleStorage.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/data/ShapeStorage.templ.h b/python/mesa_pd/templates/data/ShapeStorage.templ.h
index 772628ea6ae98307622d83a2c373f6457316d287..22111a7a333d65a4ee466735b97c686b10e87df6 100644
--- a/python/mesa_pd/templates/data/ShapeStorage.templ.h
+++ b/python/mesa_pd/templates/data/ShapeStorage.templ.h
@@ -13,7 +13,7 @@
 //  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 GeometryStorage.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/data/SparseLinkedCells.templ.h b/python/mesa_pd/templates/data/SparseLinkedCells.templ.h
index 139dbb0a4b0e989ba5f2e513b0e0a4637255acdc..145ce87c641c2845e35fc6698e47edf26c80cca0 100644
--- a/python/mesa_pd/templates/data/SparseLinkedCells.templ.h
+++ b/python/mesa_pd/templates/data/SparseLinkedCells.templ.h
@@ -13,7 +13,7 @@
 //  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 SparseLinkedCells.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/kernel/DetectAndStoreContacts.templ.h b/python/mesa_pd/templates/kernel/DetectAndStoreContacts.templ.h
index 85369e566f6a6df5f3bdd4dff6ddf4abb201b8a6..fa3237fb6d4b0e583ca14b16a071370afbe6a8a6 100644
--- a/python/mesa_pd/templates/kernel/DetectAndStoreContacts.templ.h
+++ b/python/mesa_pd/templates/kernel/DetectAndStoreContacts.templ.h
@@ -13,7 +13,7 @@
 //  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 DetectAndStoreContacts.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
diff --git a/python/mesa_pd/templates/kernel/DoubleCast.templ.h b/python/mesa_pd/templates/kernel/DoubleCast.templ.h
index adc3caf3cba7d2f588d04afb07cd7f924d8a7e41..ab745eff7b924daa266dc1102c1c69151dfa9205 100644
--- a/python/mesa_pd/templates/kernel/DoubleCast.templ.h
+++ b/python/mesa_pd/templates/kernel/DoubleCast.templ.h
@@ -13,7 +13,7 @@
 //  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 SingleCast.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/kernel/ExplicitEuler.templ.h b/python/mesa_pd/templates/kernel/ExplicitEuler.templ.h
index e3916291b679d64f6559e0b35d4b651690f75dc7..d6893ffbe2ae8f6845963812fbdfda1af694a6bf 100644
--- a/python/mesa_pd/templates/kernel/ExplicitEuler.templ.h
+++ b/python/mesa_pd/templates/kernel/ExplicitEuler.templ.h
@@ -13,7 +13,7 @@
 //  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 ExplicitEuler.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -34,7 +34,12 @@ namespace mesa_pd {
 namespace kernel {
 
 /**
- * Kernel which explicitly integrates all particles in time.
+ * Explicit Euler integration.
+ * Uses the 0.5at^2 extension for position integration.
+ * Boils down to using v_{t+0.5dt} for position integration.
+ * This version exhibits increased stability compared to standard explicit euler.
+ *
+ * Wachs, A. Particle-scale computational approaches to model dry and saturated granular flows of non-Brownian, non-cohesive, and non-spherical rigid bodies. Acta Mech 230, 1919–1980 (2019). https://doi.org/10.1007/s00707-019-02389-9
  *
  * This kernel requires the following particle accessor interface
  * \code
@@ -75,15 +80,19 @@ inline void ExplicitEuler::operator()(const size_t idx,
 
    if (!data::particle_flags::isSet( ac.getFlags(idx), data::particle_flags::FIXED))
    {
-      ac.setPosition      (idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ * dt_ + ac.getLinearVelocity(idx) * dt_ + ac.getPosition(idx));
-      ac.setLinearVelocity(idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ + ac.getLinearVelocity(idx));
+      ac.setPosition      (idx, 0.5_r * ac.getInvMass(idx) * ac.getForce(idx) * dt_ * dt_ +
+                                ac.getLinearVelocity(idx) * dt_ +
+                                ac.getPosition(idx));
+      ac.setLinearVelocity(idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ +
+                                ac.getLinearVelocity(idx));
 
       {%- if bIntegrateRotation %}
       const Vec3 wdot = math::transformMatrixRART(ac.getRotation(idx).getMatrix(),
                                                   ac.getInvInertiaBF(idx)) * ac.getTorque(idx);
 
       // Calculating the rotation angle
-      const Vec3 phi( ac.getAngularVelocity(idx) * dt_ + wdot * dt_ * dt_);
+      const Vec3 phi( 0.5_r * wdot * dt_ * dt_ +
+                      ac.getAngularVelocity(idx) * dt_ );
 
       // Calculating the new orientation
       auto rotation = ac.getRotation(idx);
diff --git a/python/mesa_pd/templates/kernel/ForceLJ.templ.h b/python/mesa_pd/templates/kernel/ForceLJ.templ.h
index 4242a0658d0fa2e8e4007f78349616deca5d0ab8..fed2360e55d34098d818b0a67cb0ac0b466f9562 100644
--- a/python/mesa_pd/templates/kernel/ForceLJ.templ.h
+++ b/python/mesa_pd/templates/kernel/ForceLJ.templ.h
@@ -13,7 +13,7 @@
 //  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 ForceLJ.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/kernel/HCSITSRelaxationStep.templ.h b/python/mesa_pd/templates/kernel/HCSITSRelaxationStep.templ.h
index 1523a57b348524dba5dd3ce1edba892cb19d912c..e18d0df56636e10c316682b575c8576a706f155f 100644
--- a/python/mesa_pd/templates/kernel/HCSITSRelaxationStep.templ.h
+++ b/python/mesa_pd/templates/kernel/HCSITSRelaxationStep.templ.h
@@ -13,7 +13,7 @@
 //  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 HCSITSRelaxationStep.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
diff --git a/python/mesa_pd/templates/kernel/HeatConduction.templ.h b/python/mesa_pd/templates/kernel/HeatConduction.templ.h
index ef12443d9e070444ef050ec77df4c8bc5b2a95ba..03576a7f425a1b02078362c79047002836587927 100644
--- a/python/mesa_pd/templates/kernel/HeatConduction.templ.h
+++ b/python/mesa_pd/templates/kernel/HeatConduction.templ.h
@@ -13,7 +13,7 @@
 //  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 HeatConduction.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/kernel/InitContactsForHCSITS.templ.h b/python/mesa_pd/templates/kernel/InitContactsForHCSITS.templ.h
index eb48c9a5199d5b54d50f2a52d14ac68f63c74a34..e78443ff6f8b6b5e566bacc1b32beb9ff447f55b 100644
--- a/python/mesa_pd/templates/kernel/InitContactsForHCSITS.templ.h
+++ b/python/mesa_pd/templates/kernel/InitContactsForHCSITS.templ.h
@@ -13,7 +13,7 @@
 //  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 InitContactsForHCSITS.h
+//! \file
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/kernel/InitParticlesForHCSITS.templ.h b/python/mesa_pd/templates/kernel/InitParticlesForHCSITS.templ.h
index 1ef8698a49004622e5ddda143d07b70bc53edce3..e218cb1d5e285b2fa55f20947e90533b52443280 100644
--- a/python/mesa_pd/templates/kernel/InitParticlesForHCSITS.templ.h
+++ b/python/mesa_pd/templates/kernel/InitParticlesForHCSITS.templ.h
@@ -13,7 +13,7 @@
 //  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 InitParticlesForHCSITS.h
+//! \file
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/kernel/InsertParticleIntoLinkedCells.templ.h b/python/mesa_pd/templates/kernel/InsertParticleIntoLinkedCells.templ.h
index d80b9cfd81cde268f01336e326f85a851fd518e4..96bcd932df6bdcc44b2ae034f104f6ab59c2f7a1 100644
--- a/python/mesa_pd/templates/kernel/InsertParticleIntoLinkedCells.templ.h
+++ b/python/mesa_pd/templates/kernel/InsertParticleIntoLinkedCells.templ.h
@@ -13,7 +13,7 @@
 //  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 InsertParticleIntoLinkedCells.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/kernel/InsertParticleIntoSparseLinkedCells.templ.h b/python/mesa_pd/templates/kernel/InsertParticleIntoSparseLinkedCells.templ.h
index ca9f6ba0121463368feb787547d73603f9b29513..ff10b7b6d3de00bca08b0a99ae90d996885d0284 100644
--- a/python/mesa_pd/templates/kernel/InsertParticleIntoSparseLinkedCells.templ.h
+++ b/python/mesa_pd/templates/kernel/InsertParticleIntoSparseLinkedCells.templ.h
@@ -13,7 +13,7 @@
 //  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 InsertParticleIntoSparseLinkedCells.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/kernel/IntegrateParticlesHCSITS.templ.h b/python/mesa_pd/templates/kernel/IntegrateParticlesHCSITS.templ.h
index 068b21e14f59d2c9a341a7f4899656dad054ec34..a1a225ec9baff248f521335c1f142c46a8620f0e 100644
--- a/python/mesa_pd/templates/kernel/IntegrateParticlesHCSITS.templ.h
+++ b/python/mesa_pd/templates/kernel/IntegrateParticlesHCSITS.templ.h
@@ -13,7 +13,7 @@
 //  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 IntegrateParticlesHCSITS.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
diff --git a/python/mesa_pd/templates/kernel/LinearSpringDashpot.templ.h b/python/mesa_pd/templates/kernel/LinearSpringDashpot.templ.h
index 2613d241462169d9e72b076cbe0d4ed16c5966b0..2696e0f3430c92e8c9c3a337a4cbadb775b0ecaf 100644
--- a/python/mesa_pd/templates/kernel/LinearSpringDashpot.templ.h
+++ b/python/mesa_pd/templates/kernel/LinearSpringDashpot.templ.h
@@ -13,7 +13,7 @@
 //  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 LinearSpringDashpot.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
diff --git a/python/mesa_pd/templates/kernel/NonLinearSpringDashpot.templ.h b/python/mesa_pd/templates/kernel/NonLinearSpringDashpot.templ.h
index 45318c7808fb70d55c16fcb37a110ef122dd4883..3cc2fbd7f932392c7dfd289d98e31ac51b8fcc80 100644
--- a/python/mesa_pd/templates/kernel/NonLinearSpringDashpot.templ.h
+++ b/python/mesa_pd/templates/kernel/NonLinearSpringDashpot.templ.h
@@ -13,7 +13,7 @@
 //  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 NonLinearSpringDashpot.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
diff --git a/python/mesa_pd/templates/kernel/PFCDamping.templ.h b/python/mesa_pd/templates/kernel/PFCDamping.templ.h
new file mode 100644
index 0000000000000000000000000000000000000000..cab03afac8ff4854f5fd239a1bb2f706157cdba9
--- /dev/null
+++ b/python/mesa_pd/templates/kernel/PFCDamping.templ.h
@@ -0,0 +1,90 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov, <drozd013@umn.edu>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/IAccessor.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace kernel {
+
+/**
+ * PFC style damping
+ *
+ * This kernel requires the following particle accessor interface
+ * \code
+   {%- for prop in interface %}
+   {%- if 'g' in prop.access %}
+ * const {{prop.type}}& get{{prop.name | capFirst}}(const size_t p_idx) const;
+   {%- endif %}
+   {%- if 's' in prop.access %}
+ * void set{{prop.name | capFirst}}(const size_t p_idx, const {{prop.type}}& v);
+   {%- endif %}
+   {%- if 'r' in prop.access %}
+ * {{prop.type}}& get{{prop.name | capFirst}}Ref(const size_t p_idx);
+   {%- endif %}
+ *
+   {%- endfor %}
+ * \endcode
+ *
+ * \ingroup mesa_pd_kernel
+ */
+class PFCDamping
+{
+public:
+   PFCDamping(const real_t alpha) : alpha_(alpha) {}
+
+   template <typename Accessor>
+   void operator()(const size_t p_idx, Accessor& ac) const;
+private:
+   real_t alpha_ = 0_r;
+};
+
+template <typename Accessor>
+inline void PFCDamping::operator()(const size_t p_idx,
+                                   Accessor& ac) const
+{
+   static_assert(std::is_base_of<data::IAccessor, Accessor>::value, "please provide a valid accessor");
+
+   Vec3 damp_F(0,0,0);
+   Vec3 damp_M(0,0,0);
+
+   for (size_t i = 0; i < 3; i++)
+   {
+      damp_F[i] = - alpha_ * std::fabs( ac.getForce(p_idx)[i] ) * math::sign( ac.getLinearVelocity(p_idx)[i] );
+      damp_M[i] = - alpha_ * std::fabs( ac.getTorque(p_idx)[i] ) * math::sign( ac.getAngularVelocity(p_idx)[i] );
+   }
+
+   ac.setForce (p_idx, ac.getForce(p_idx)  + damp_F);
+   ac.setTorque(p_idx, ac.getTorque(p_idx) + damp_M);
+}
+
+} //namespace kernel
+} //namespace mesa_pd
+} //namespace walberla
diff --git a/python/mesa_pd/templates/kernel/SemiImplicitEuler.templ.h b/python/mesa_pd/templates/kernel/SemiImplicitEuler.templ.h
new file mode 100644
index 0000000000000000000000000000000000000000..edc393536385bbd471d153215976eae2dc07affb
--- /dev/null
+++ b/python/mesa_pd/templates/kernel/SemiImplicitEuler.templ.h
@@ -0,0 +1,109 @@
+//======================================================================================================================
+//
+//  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
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/IAccessor.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace kernel {
+
+/**
+ * Semi-implicit Euler integration for position and velocity.
+ *
+ * This kernel requires the following particle accessor interface
+ * \code
+   {%- for prop in interface %}
+   {%- if 'g' in prop.access %}
+ * const {{prop.type}}& get{{prop.name | capFirst}}(const size_t p_idx) const;
+   {%- endif %}
+   {%- if 's' in prop.access %}
+ * void set{{prop.name | capFirst}}(const size_t p_idx, const {{prop.type}}& v);
+   {%- endif %}
+   {%- if 'r' in prop.access %}
+ * {{prop.type}}& get{{prop.name | capFirst}}Ref(const size_t p_idx);
+   {%- endif %}
+ *
+   {%- endfor %}
+ * \endcode
+ *
+ * \pre  All forces and torques acting on the particles have to be set.
+ * \post All forces and torques are reset to 0.
+ * \ingroup mesa_pd_kernel
+ */
+class SemiImplicitEuler
+{
+public:
+   explicit SemiImplicitEuler(const real_t dt) : dt_(dt) {}
+
+   template <typename Accessor>
+   void operator()(const size_t i, Accessor& ac) const;
+private:
+   real_t dt_ = real_t(0.0);
+};
+
+template <typename Accessor>
+inline void SemiImplicitEuler::operator()(const size_t idx,
+                                          Accessor& ac) const
+{
+   static_assert(std::is_base_of<data::IAccessor, Accessor>::value, "please provide a valid accessor");
+
+   if (!data::particle_flags::isSet( ac.getFlags(idx), data::particle_flags::FIXED))
+   {
+      ac.setLinearVelocity( idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ +
+                                 ac.getLinearVelocity(idx));
+      ac.setPosition      ( idx, ac.getLinearVelocity(idx) * dt_ +
+                                 ac.getPosition(idx));
+
+      {%- if bIntegrateRotation %}
+      const Vec3 wdot = math::transformMatrixRART(ac.getRotation(idx).getMatrix(),
+                                                  ac.getInvInertiaBF(idx)) * ac.getTorque(idx);
+
+      ac.setAngularVelocity(idx, wdot * dt_ +
+                                 ac.getAngularVelocity(idx));
+
+      // Calculating the rotation angle
+      const Vec3 phi( ac.getAngularVelocity(idx) * dt_ );
+
+      // Calculating the new orientation
+      auto rotation = ac.getRotation(idx);
+      rotation.rotate( phi );
+      ac.setRotation(idx, rotation);
+
+      {%- endif %}
+   }
+
+   ac.setForce (idx, Vec3(real_t(0), real_t(0), real_t(0)));
+   {%- if bIntegrateRotation %}
+   ac.setTorque(idx, Vec3(real_t(0), real_t(0), real_t(0)));
+   {%- endif %}
+}
+
+} //namespace kernel
+} //namespace mesa_pd
+} //namespace walberla
diff --git a/python/mesa_pd/templates/kernel/SingleCast.templ.h b/python/mesa_pd/templates/kernel/SingleCast.templ.h
index eda737d630ae76a19dd8fb660c3de7330e408f00..878554980abc031518a64a04908df6e98a45c877 100644
--- a/python/mesa_pd/templates/kernel/SingleCast.templ.h
+++ b/python/mesa_pd/templates/kernel/SingleCast.templ.h
@@ -13,7 +13,7 @@
 //  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 SingleCast.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/kernel/SpringDashpot.templ.h b/python/mesa_pd/templates/kernel/SpringDashpot.templ.h
index 93df9f865d5ca019e0c62c189bc498994f1e981e..29c8a10767cb4c22269cba173887145c1d64b496 100644
--- a/python/mesa_pd/templates/kernel/SpringDashpot.templ.h
+++ b/python/mesa_pd/templates/kernel/SpringDashpot.templ.h
@@ -13,7 +13,7 @@
 //  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 SpringDashpot.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/kernel/SpringDashpotSpring.templ.h b/python/mesa_pd/templates/kernel/SpringDashpotSpring.templ.h
index 78d3d4863175141fc76c657828c9c448804671bd..873dc71062f35daeb1c79630b8487189ce0c687c 100644
--- a/python/mesa_pd/templates/kernel/SpringDashpotSpring.templ.h
+++ b/python/mesa_pd/templates/kernel/SpringDashpotSpring.templ.h
@@ -13,7 +13,7 @@
 //  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 SpringDashpotSpring.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/kernel/TemperatureIntegration.templ.h b/python/mesa_pd/templates/kernel/TemperatureIntegration.templ.h
index 2bdd7f362e6412830c55f78b2f233505ddfc472e..f6041bf3f7c0209f72c061eac009f9ba6219ba2f 100644
--- a/python/mesa_pd/templates/kernel/TemperatureIntegration.templ.h
+++ b/python/mesa_pd/templates/kernel/TemperatureIntegration.templ.h
@@ -13,7 +13,7 @@
 //  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 TemperatureIntegration.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/kernel/VelocityVerlet.templ.h b/python/mesa_pd/templates/kernel/VelocityVerlet.templ.h
index 148ac8c8d96d71d4cb7a0af97805689236d46a35..919a13b2a689fcf5404411d8823c4f1f8ec12e67 100644
--- a/python/mesa_pd/templates/kernel/VelocityVerlet.templ.h
+++ b/python/mesa_pd/templates/kernel/VelocityVerlet.templ.h
@@ -13,7 +13,7 @@
 //  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 VelocityVerlet.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -41,6 +41,8 @@ namespace kernel {
  * integration is only complete when both functions are called. The integration
  * is symplectic.
  *
+ * Wachs, A. Particle-scale computational approaches to model dry and saturated granular flows of non-Brownian, non-cohesive, and non-spherical rigid bodies. Acta Mech 230, 1919–1980 (2019). https://doi.org/10.1007/s00707-019-02389-9
+ *
  * This kernel requires the following particle accessor interface
  * \code
    {%- for prop in interface %}
diff --git a/python/mesa_pd/templates/mpi/BroadcastProperty.templ.h b/python/mesa_pd/templates/mpi/BroadcastProperty.templ.h
index 236c0a65cc59beaf36375c5d659a88ae318ea954..a9f3b3310dfa7b4412c86d938fc6db6a8bd834e2 100644
--- a/python/mesa_pd/templates/mpi/BroadcastProperty.templ.h
+++ b/python/mesa_pd/templates/mpi/BroadcastProperty.templ.h
@@ -13,7 +13,7 @@
 //  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 BroadcastProperty.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/ClearGhostOwnerSync.templ.h b/python/mesa_pd/templates/mpi/ClearGhostOwnerSync.templ.h
index 4194abe69f8972f0f001d1040c713450e96cac18..cd00a12a8a7fb498f4e043acc2cccfc2c45fd9f7 100644
--- a/python/mesa_pd/templates/mpi/ClearGhostOwnerSync.templ.h
+++ b/python/mesa_pd/templates/mpi/ClearGhostOwnerSync.templ.h
@@ -13,7 +13,7 @@
 //  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 ClearGhostOwnerSync.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/ClearNextNeighborSync.templ.h b/python/mesa_pd/templates/mpi/ClearNextNeighborSync.templ.h
index df3505f471027ad54cdb0f563854dde5219ff43b..c5c7d117cde2c6da1a5ab1e6efe8f20cfc998473 100644
--- a/python/mesa_pd/templates/mpi/ClearNextNeighborSync.templ.h
+++ b/python/mesa_pd/templates/mpi/ClearNextNeighborSync.templ.h
@@ -13,7 +13,7 @@
 //  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 ClearNextNeighborSync.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/ReduceContactHistory.templ.h b/python/mesa_pd/templates/mpi/ReduceContactHistory.templ.h
index 77aabba48ac98ffbf9abb61d1144bcea348bfb86..840e0bc1ad125405dc2a48a5420bf564b5e6157b 100644
--- a/python/mesa_pd/templates/mpi/ReduceContactHistory.templ.h
+++ b/python/mesa_pd/templates/mpi/ReduceContactHistory.templ.h
@@ -13,7 +13,7 @@
 //  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 ReduceContactHistory.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/ReduceProperty.templ.h b/python/mesa_pd/templates/mpi/ReduceProperty.templ.h
index 60abb8985584bfd5e80b96d0c3c1d950b722551c..970c0127c26daee627804ac11b28b754e3519dbc 100644
--- a/python/mesa_pd/templates/mpi/ReduceProperty.templ.h
+++ b/python/mesa_pd/templates/mpi/ReduceProperty.templ.h
@@ -13,7 +13,7 @@
 //  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 SyncProperty.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/ShapePackUnpack.templ.h b/python/mesa_pd/templates/mpi/ShapePackUnpack.templ.h
index 2031f6659bd1eec159b0e60fe321a8c52371d836..6ea9d158e4b8027987c9addfe5bacd203ffa586d 100644
--- a/python/mesa_pd/templates/mpi/ShapePackUnpack.templ.h
+++ b/python/mesa_pd/templates/mpi/ShapePackUnpack.templ.h
@@ -13,7 +13,7 @@
 //  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 ShapePackUnpack.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/SyncGhostOwners.templ.cpp b/python/mesa_pd/templates/mpi/SyncGhostOwners.templ.cpp
index 1dca2429de6f345715e0cd81ea90685bba0658dd..7336ec1e8699c5a738062d5e0689e8d13556c17e 100644
--- a/python/mesa_pd/templates/mpi/SyncGhostOwners.templ.cpp
+++ b/python/mesa_pd/templates/mpi/SyncGhostOwners.templ.cpp
@@ -13,7 +13,7 @@
 //  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 SyncGhostOwners.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/SyncGhostOwners.templ.h b/python/mesa_pd/templates/mpi/SyncGhostOwners.templ.h
index 0be135507c793080a5b7b8db30415839d59f2ae7..179253fb084c9d6f273d0ba0d975c04a259d716e 100644
--- a/python/mesa_pd/templates/mpi/SyncGhostOwners.templ.h
+++ b/python/mesa_pd/templates/mpi/SyncGhostOwners.templ.h
@@ -13,7 +13,7 @@
 //  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 SyncGhostOwners.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/SyncNextNeighbors.templ.cpp b/python/mesa_pd/templates/mpi/SyncNextNeighbors.templ.cpp
index f631bb0e7b49e814556f93664521d7fb5f5c30de..3dd7ca3c40988c74193df432df99dcb43498fbc7 100644
--- a/python/mesa_pd/templates/mpi/SyncNextNeighbors.templ.cpp
+++ b/python/mesa_pd/templates/mpi/SyncNextNeighbors.templ.cpp
@@ -13,7 +13,7 @@
 //  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 SyncNextNeighbors.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/SyncNextNeighbors.templ.h b/python/mesa_pd/templates/mpi/SyncNextNeighbors.templ.h
index 7a9dc110bc1e51bdfc4f052c08840cd9b077796c..bea705262fe22736950b6b05d048c6d6dd6a2cf3 100644
--- a/python/mesa_pd/templates/mpi/SyncNextNeighbors.templ.h
+++ b/python/mesa_pd/templates/mpi/SyncNextNeighbors.templ.h
@@ -13,7 +13,7 @@
 //  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 SyncNextNeighbors.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.cpp b/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.cpp
index ce8c9100d2615ac8660ff3a1040c259b36fc02bd..200a918d972d75414c6e4e567a5539430e87aa75 100644
--- a/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.cpp
+++ b/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.cpp
@@ -13,7 +13,7 @@
 //  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 SyncNextNeighborsNoGhosts.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.h b/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.h
index 8030c09e76f1362a2e5e4d9f16531c8525ef2046..22fb55829ace289dcc8b02a21f920672e61d9474 100644
--- a/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.h
+++ b/python/mesa_pd/templates/mpi/SyncNextNeighborsNoGhosts.templ.h
@@ -13,7 +13,7 @@
 //  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 SyncNextNeighborsNoGhosts.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/notifications/ContactHistoryNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/ContactHistoryNotification.templ.h
index 2ffca67a2aeecd4d4768ad6c4685bf426652afa7..0b7310f229624c9769425578ef6757a2e0d5f2d8 100644
--- a/python/mesa_pd/templates/mpi/notifications/ContactHistoryNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ContactHistoryNotification.templ.h
@@ -13,7 +13,7 @@
 //  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 ContactHistoryNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/notifications/NewGhostParticleNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/NewGhostParticleNotification.templ.h
index 7d8a8ce43f68a178090f062a8966e695166832ca..27bc904f43f95b91a9e599946e9317fd6270110e 100644
--- a/python/mesa_pd/templates/mpi/notifications/NewGhostParticleNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/NewGhostParticleNotification.templ.h
@@ -13,7 +13,7 @@
 //  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 NewGhostParticleNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/notifications/ParseMessage.templ.h b/python/mesa_pd/templates/mpi/notifications/ParseMessage.templ.h
index e1dce5b88e07ab9bc112d1812d7ca0fc245c5bf5..ce4aca6e321ccfb70fca0b2f2c6f469f56d37aef 100644
--- a/python/mesa_pd/templates/mpi/notifications/ParseMessage.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ParseMessage.templ.h
@@ -13,7 +13,7 @@
 //  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 ParseMessage.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \brief Parsing of messages
 //
diff --git a/python/mesa_pd/templates/mpi/notifications/ParticleCopyNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/ParticleCopyNotification.templ.h
index e2414008e6359bb5eefa3abe56aae6c1a29fcf79..a8c797f4c418a6f15030b8a564bc71692748721e 100644
--- a/python/mesa_pd/templates/mpi/notifications/ParticleCopyNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ParticleCopyNotification.templ.h
@@ -13,7 +13,7 @@
 //  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 ParticleCopyNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/notifications/ParticleGhostCopyNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/ParticleGhostCopyNotification.templ.h
index 52dd0a7806bcfe2b8194b8b1f636e26ea64c7f29..5e6b7aa4f85ae811fe08dbd5fecc372ef8e224ef 100644
--- a/python/mesa_pd/templates/mpi/notifications/ParticleGhostCopyNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ParticleGhostCopyNotification.templ.h
@@ -13,7 +13,7 @@
 //  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 ParticleGhostCopyNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/notifications/ParticleMigrationNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/ParticleMigrationNotification.templ.h
index f478bcc9c8985b699cb963ab3a01c9f181e994b0..26f50b3dc869895b46806ec502dabfcfd0ec562a 100644
--- a/python/mesa_pd/templates/mpi/notifications/ParticleMigrationNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ParticleMigrationNotification.templ.h
@@ -13,7 +13,7 @@
 //  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 ParticleMigrationNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/notifications/ParticleRemoteMigrationNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/ParticleRemoteMigrationNotification.templ.h
index 3a1cbceeb693d2dddb7b04f9391e22867769e7c8..966759bdd528731a1707a84ff04b744c6839d3d2 100644
--- a/python/mesa_pd/templates/mpi/notifications/ParticleRemoteMigrationNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ParticleRemoteMigrationNotification.templ.h
@@ -13,7 +13,7 @@
 //  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 ParticleRemoteMigrationNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/notifications/ParticleRemovalInformationNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/ParticleRemovalInformationNotification.templ.h
index 15f10d48cbb415941dc1685595509265d291852f..c3386d7e27407a0a8f432b363e895b953ef32359 100644
--- a/python/mesa_pd/templates/mpi/notifications/ParticleRemovalInformationNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ParticleRemovalInformationNotification.templ.h
@@ -13,7 +13,7 @@
 //  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 ParticleRemovalInformationNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/notifications/ParticleRemovalNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/ParticleRemovalNotification.templ.h
index 9b3e56b77451dda7e998c1d1942dbec71d1733fb..798f8b750508141eaeec7b586c294a75987df007 100644
--- a/python/mesa_pd/templates/mpi/notifications/ParticleRemovalNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ParticleRemovalNotification.templ.h
@@ -13,7 +13,7 @@
 //  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 ParticleRemovalNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/notifications/ParticleUpdateNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/ParticleUpdateNotification.templ.h
index 507eeda47fd17ce0eafff79964b5a1274f4aa821..282db85d48c5eb2a47d8936474bd268cdd46736c 100644
--- a/python/mesa_pd/templates/mpi/notifications/ParticleUpdateNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/ParticleUpdateNotification.templ.h
@@ -13,7 +13,7 @@
 //  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 ParticleUpdateNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/mpi/notifications/PropertyNotification.templ.h b/python/mesa_pd/templates/mpi/notifications/PropertyNotification.templ.h
index 701f21ea910476a3ac9e9449be91a6a9c5b4ed46..1a759ce31a67b26e6da8e15276919df7471ffba9 100644
--- a/python/mesa_pd/templates/mpi/notifications/PropertyNotification.templ.h
+++ b/python/mesa_pd/templates/mpi/notifications/PropertyNotification.templ.h
@@ -13,7 +13,7 @@
 //  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 {{name}}.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/mesa_pd/templates/tests/CheckInterface.templ.cpp b/python/mesa_pd/templates/tests/CheckInterface.templ.cpp
index 6bbd8f4f37d9c619ee24d207034992ecbb9f1ad1..817001e2d9be55b5fbca0927231f2bd38b174359 100644
--- a/python/mesa_pd/templates/tests/CheckInterface.templ.cpp
+++ b/python/mesa_pd/templates/tests/CheckInterface.templ.cpp
@@ -13,7 +13,7 @@
 //  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 {{InterfaceTestName}}.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/python/pystencils_walberla/additional_data_handler.py b/python/pystencils_walberla/additional_data_handler.py
new file mode 100644
index 0000000000000000000000000000000000000000..7abe7d5aa0365622fc3b870787c64810c52159e4
--- /dev/null
+++ b/python/pystencils_walberla/additional_data_handler.py
@@ -0,0 +1,55 @@
+from pystencils.stencil import inverse_direction
+
+
+class AdditionalDataHandler:
+    """Base class that defines how to handle boundary conditions holding additional data."""
+
+    def __init__(self, stencil):
+        self._dim = len(stencil[0])
+
+        # waLBerla is a 3D framework. Therefore, a zero for the z index has to be added if we work in 2D
+        if self._dim == 2:
+            self._walberla_stencil = ()
+            for d in stencil:
+                d = d + (0,)
+                self._walberla_stencil = self._walberla_stencil + (d,)
+        else:
+            self._walberla_stencil = stencil
+
+    @property
+    def constructor_arguments(self):
+        return ""
+
+    @property
+    def initialiser_list(self):
+        return ""
+
+    @property
+    def additional_arguments_for_fill_function(self):
+        return ""
+
+    @property
+    def additional_parameters_for_fill_function(self):
+        return ""
+
+    @property
+    def additional_field_data(self):
+        return ""
+
+    def data_initialisation(self, direction_index):
+        return ""
+
+    @property
+    def additional_member_variable(self):
+        return ""
+
+    @property
+    def stencil_info(self):
+        return [(i, d, ", ".join([str(e) for e in d])) for i, d in enumerate(self._walberla_stencil)]
+
+    @property
+    def inverse_directions(self):
+        inv_dirs = []
+        for direction in self._walberla_stencil:
+            inv_dirs.append(self._walberla_stencil.index(inverse_direction(direction)))
+        return inv_dirs
diff --git a/python/pystencils_walberla/boundary.py b/python/pystencils_walberla/boundary.py
index 1984fbfc52341645ad691a901ef90abdc57e5742..0d251236c0f9400bc2aea424e775aa1eb6c7e5a2 100644
--- a/python/pystencils_walberla/boundary.py
+++ b/python/pystencils_walberla/boundary.py
@@ -1,62 +1,94 @@
+from collections import OrderedDict
 import numpy as np
 from jinja2 import Environment, PackageLoader, StrictUndefined
-
 from pystencils import Field, FieldType
 from pystencils.boundaries.boundaryhandling import create_boundary_kernel
 from pystencils.boundaries.createindexlist import (
     boundary_index_array_coordinate_names, direction_member_name,
     numpy_data_type_for_boundary_object)
 from pystencils.data_types import TypedSymbol, create_type
-from pystencils_walberla.codegen import KernelInfo
+from pystencils_walberla.codegen import KernelInfo, default_create_kernel_parameters
 from pystencils_walberla.jinja_filters import add_pystencils_filters_to_jinja_env
+from pystencils_walberla.additional_data_handler import AdditionalDataHandler
 
 
-def generate_staggered_boundary(generation_context, class_name, boundary_object,
-                                dim, neighbor_stencil, index_shape, target='cpu'):
+def generate_boundary(generation_context,
+                      class_name,
+                      boundary_object,
+                      field_name,
+                      neighbor_stencil,
+                      index_shape,
+                      field_type=FieldType.GENERIC,
+                      kernel_creation_function=None,
+                      target='cpu',
+                      namespace='pystencils',
+                      additional_data_handler=None,
+                      **create_kernel_params):
+
+    if boundary_object.additional_data and additional_data_handler is None:
+        raise ValueError("Boundary object has additional data but you have not provided an AdditionalDataHandler.")
+
     struct_name = "IndexInfo"
     boundary_object.name = class_name
+    dim = len(neighbor_stencil[0])
+
+    create_kernel_params = default_create_kernel_parameters(generation_context, create_kernel_params)
+    create_kernel_params["target"] = target
+    del create_kernel_params["cpu_vectorize_info"]
 
+    if not create_kernel_params["data_type"]:
+        create_kernel_params["data_type"] = 'double' if generation_context.double_accuracy else 'float32'
     index_struct_dtype = numpy_data_type_for_boundary_object(boundary_object, dim)
 
-    staggered_field = Field.create_generic('field', dim,
-                                           np.float64 if generation_context.double_accuracy else np.float32,
-                                           index_dimensions=len(index_shape), layout='c', index_shape=index_shape,
-                                           field_type=FieldType.STAGGERED)
+    field = Field.create_generic(field_name, dim,
+                                 np.float64 if generation_context.double_accuracy else np.float32,
+                                 index_dimensions=len(index_shape), layout='fzyx', index_shape=index_shape,
+                                 field_type=field_type)
 
     index_field = Field('indexVector', FieldType.INDEXED, index_struct_dtype, layout=[0],
                         shape=(TypedSymbol("indexVectorSize", create_type(np.int64)), 1), strides=(1, 1))
 
-    kernel = create_boundary_kernel(staggered_field, index_field, neighbor_stencil, boundary_object, target=target,
-                                    openmp=generation_context.openmp)
-    kernel.function_name = "boundary_" + boundary_object.name
-    kernel.assumed_inner_stride_one = False
-
-    # waLBerla is a 3D framework. Therefore, a zero for the z index has to be added if we work in 2D
-    if dim == 2:
-        stencil = ()
-        for d in neighbor_stencil:
-            d = d + (0,)
-            stencil = stencil + (d,)
+    if not kernel_creation_function:
+        kernel_creation_function = create_boundary_kernel
+
+    kernels = kernel_creation_function(field, index_field, neighbor_stencil, boundary_object, **create_kernel_params)
+    if isinstance(kernels, dict):
+        sweep_to_kernel_info_dict = OrderedDict()
+        dummy_kernel_info = None
+        for sweep_class, sweep_kernel in kernels.items():
+            sweep_kernel.function_name = "boundary_" + boundary_object.name + '_' + sweep_class
+            sweep_kernel.assumed_inner_stride_one = False
+            kernel_info = KernelInfo(sweep_kernel)
+            sweep_to_kernel_info_dict[sweep_class] = kernel_info
+            if dummy_kernel_info is None:
+                dummy_kernel_info = kernel_info
+            # elif not dummy_kernel_info.has_same_interface(kernel_info):
+            #     raise ValueError("Multiple boundary sweeps must have the same kernel interface!")
+        multi_sweep = True
     else:
-        stencil = neighbor_stencil
+        multi_sweep = False
+        kernel = kernels
+        kernel.function_name = "boundary_" + boundary_object.name
+        kernel.assumed_inner_stride_one = False
+        kernel_info = KernelInfo(kernel)
+        sweep_to_kernel_info_dict = {'': kernel_info}
+        dummy_kernel_info = kernel_info
 
-    stencil_info = [(i, d, ", ".join([str(e) for e in d])) for i, d in enumerate(stencil)]
-    inv_dirs = []
-    for direction in stencil:
-        inverse_dir = tuple([-i for i in direction])
-        inv_dirs.append(stencil.index(inverse_dir))
+    if additional_data_handler is None:
+        additional_data_handler = AdditionalDataHandler(stencil=neighbor_stencil)
 
     context = {
         'class_name': boundary_object.name,
+        'sweep_classes': sweep_to_kernel_info_dict,
+        'multi_sweep': multi_sweep,
+        'dummy_kernel_info': dummy_kernel_info,
         'StructName': struct_name,
         'StructDeclaration': struct_from_numpy_dtype(struct_name, index_struct_dtype),
-        'kernel': KernelInfo(kernel),
-        'stencil_info': stencil_info,
-        'inverse_directions': inv_dirs,
         'dim': dim,
         'target': target,
-        'namespace': 'pystencils',
-        'inner_or_boundary': boundary_object.inner_or_boundary
+        'namespace': namespace,
+        'inner_or_boundary': boundary_object.inner_or_boundary,
+        'additional_data_handler': additional_data_handler
     }
 
     env = Environment(loader=PackageLoader('pystencils_walberla'), undefined=StrictUndefined)
@@ -66,87 +98,42 @@ def generate_staggered_boundary(generation_context, class_name, boundary_object,
     source = env.get_template('Boundary.tmpl.cpp').render(**context)
 
     source_extension = "cpp" if target == "cpu" else "cu"
-    generation_context.write_file("{}.h".format(class_name), header)
-    generation_context.write_file("{}.{}".format(class_name, source_extension), source)
+    generation_context.write_file(f"{class_name}.h", header)
+    generation_context.write_file(f"{class_name}.{source_extension}", source)
 
 
-def generate_staggered_flux_boundary(generation_context, class_name, boundary_object,
-                                     dim, neighbor_stencil, index_shape, target='cpu'):
-    struct_name = "IndexInfo"
-    boundary_object.name = class_name
-
-    index_struct_dtype = numpy_data_type_for_boundary_object(boundary_object, dim)
-
-    staggered_field = Field.create_generic('flux', dim,
-                                           np.float64 if generation_context.double_accuracy else np.float32,
-                                           index_dimensions=len(index_shape), layout='c', index_shape=index_shape,
-                                           field_type=FieldType.STAGGERED_FLUX)
-
-    index_field = Field('indexVector', FieldType.INDEXED, index_struct_dtype, layout=[0],
-                        shape=(TypedSymbol("indexVectorSize", create_type(np.int64)), 1), strides=(1, 1))
-
-    kernel = create_boundary_kernel(staggered_field, index_field, neighbor_stencil, boundary_object, target=target,
-                                    openmp=generation_context.openmp)
-    kernel.function_name = "boundary_" + boundary_object.name
-    kernel.assumed_inner_stride_one = False
-
-    # waLBerla is a 3D framework. Therefore, a zero for the z index has to be added if we work in 2D
-    if dim == 2:
-        stencil = ()
-        for d in neighbor_stencil:
-            d = d + (0,)
-            stencil = stencil + (d,)
-    else:
-        stencil = neighbor_stencil
-
-    stencil_info = [(i, d, ", ".join([str(e) for e in d])) for i, d in enumerate(stencil)]
-    inv_dirs = []
-    for direction in stencil:
-        inverse_dir = tuple([-i for i in direction])
-        inv_dirs.append(stencil.index(inverse_dir))
-
-    context = {
-        'class_name': boundary_object.name,
-        'StructName': struct_name,
-        'StructDeclaration': struct_from_numpy_dtype(struct_name, index_struct_dtype),
-        'kernel': KernelInfo(kernel),
-        'stencil_info': stencil_info,
-        'inverse_directions': inv_dirs,
-        'dim': dim,
-        'target': target,
-        'namespace': 'pystencils',
-        'inner_or_boundary': boundary_object.inner_or_boundary
-    }
-
-    env = Environment(loader=PackageLoader('pystencils_walberla'), undefined=StrictUndefined)
-    add_pystencils_filters_to_jinja_env(env)
+def generate_staggered_boundary(generation_context, class_name, boundary_object,
+                                dim, neighbor_stencil, index_shape, target='cpu', **kwargs):
+    assert dim == len(neighbor_stencil[0])
+    generate_boundary(generation_context, class_name, boundary_object, 'field', neighbor_stencil, index_shape,
+                      FieldType.STAGGERED, target=target, **kwargs)
 
-    header = env.get_template('Boundary.tmpl.h').render(**context)
-    source = env.get_template('Boundary.tmpl.cpp').render(**context)
 
-    source_extension = "cpp" if target == "cpu" else "cu"
-    generation_context.write_file("{}.h".format(class_name), header)
-    generation_context.write_file("{}.{}".format(class_name, source_extension), source)
+def generate_staggered_flux_boundary(generation_context, class_name, boundary_object,
+                                     dim, neighbor_stencil, index_shape, target='cpu', **kwargs):
+    assert dim == len(neighbor_stencil[0])
+    generate_boundary(generation_context, class_name, boundary_object, 'flux', neighbor_stencil, index_shape,
+                      FieldType.STAGGERED_FLUX, target=target, **kwargs)
 
 
 def struct_from_numpy_dtype(struct_name, numpy_dtype):
-    result = "struct %s { \n" % (struct_name,)
+    result = f"struct {struct_name} {{ \n"
 
     equality_compare = []
     constructor_params = []
     constructor_initializer_list = []
     for name, (sub_type, offset) in numpy_dtype.fields.items():
         pystencils_type = create_type(sub_type)
-        result += "    %s %s;\n" % (pystencils_type, name)
+        result += f"    {pystencils_type} {name};\n"
         if name in boundary_index_array_coordinate_names or name == direction_member_name:
-            constructor_params.append("%s %s_" % (pystencils_type, name))
-            constructor_initializer_list.append("%s(%s_)" % (name, name))
+            constructor_params.append(f"{pystencils_type} {name}_")
+            constructor_initializer_list.append(f"{name}({name}_)")
         else:
-            constructor_initializer_list.append("%s()" % name)
+            constructor_initializer_list.append(f"{name}()")
         if pystencils_type.is_float():
-            equality_compare.append("floatIsEqual(%s, o.%s)" % (name, name))
+            equality_compare.append(f"floatIsEqual({name}, o.{name})")
         else:
-            equality_compare.append("%s == o.%s" % (name, name))
+            equality_compare.append(f"{name} == o.{name}")
 
     result += "    %s(%s) : %s {}\n" % \
               (struct_name, ", ".join(constructor_params), ", ".join(constructor_initializer_list))
diff --git a/python/pystencils_walberla/codegen.py b/python/pystencils_walberla/codegen.py
index 37013cba6203cdd9a45da7b044361dc71fa457d1..44de5c3fb878cf1b34c7c311ce1cdc1da8daae8a 100644
--- a/python/pystencils_walberla/codegen.py
+++ b/python/pystencils_walberla/codegen.py
@@ -20,7 +20,7 @@ __all__ = ['generate_sweep', 'generate_pack_info', 'generate_pack_info_for_field
 
 def generate_sweep(generation_context, class_name, assignments,
                    namespace='pystencils', field_swaps=(), staggered=False, varying_parameters=(),
-                   inner_outer_split=False,
+                   inner_outer_split=False, ghost_layers_to_include=0,
                    **create_kernel_params):
     """Generates a waLBerla sweep from a pystencils representation.
 
@@ -44,6 +44,8 @@ def generate_sweep(generation_context, class_name, assignments,
                             the C++ class constructor even if the kernel does not need them.
         inner_outer_split: if True generate a sweep that supports separate iteration over inner and outer regions
                            to allow for communication hiding.
+        ghost_layers_to_include: determines how many ghost layers should be included for the Sweep.
+                                 This is relevant if a setter kernel should also set correct values to the ghost layers.
         **create_kernel_params: remaining keyword arguments are passed to `pystencils.create_kernel`
     """
     create_kernel_params = default_create_kernel_parameters(generation_context, create_kernel_params)
@@ -71,31 +73,22 @@ def generate_sweep(generation_context, class_name, assignments,
     env = Environment(loader=PackageLoader('pystencils_walberla'), undefined=StrictUndefined)
     add_pystencils_filters_to_jinja_env(env)
 
-    if inner_outer_split is False:
-        jinja_context = {
-            'kernel': KernelInfo(ast, temporary_fields, field_swaps, varying_parameters),
-            'namespace': namespace,
-            'class_name': class_name,
-            'target': create_kernel_params.get("target", "cpu"),
-            'headers': get_headers(ast),
-        }
-        header = env.get_template("Sweep.tmpl.h").render(**jinja_context)
-        source = env.get_template("Sweep.tmpl.cpp").render(**jinja_context)
-    else:
-        main_kernel_info = KernelInfo(ast, temporary_fields, field_swaps, varying_parameters)
-        representative_field = {p.field_name for p in main_kernel_info.parameters if p.is_field_parameter}
-        representative_field = sorted(representative_field)[0]
-
-        jinja_context = {
-            'kernel': main_kernel_info,
-            'namespace': namespace,
-            'class_name': class_name,
-            'target': create_kernel_params.get("target", "cpu"),
-            'field': representative_field,
-            'headers': get_headers(ast),
-        }
-        header = env.get_template("SweepInnerOuter.tmpl.h").render(**jinja_context)
-        source = env.get_template("SweepInnerOuter.tmpl.cpp").render(**jinja_context)
+    main_kernel_info = KernelInfo(ast, temporary_fields, field_swaps, varying_parameters)
+    representative_field = {p.field_name for p in main_kernel_info.parameters if p.is_field_parameter}
+    representative_field = sorted(representative_field)[0]
+
+    jinja_context = {
+        'kernel': main_kernel_info,
+        'namespace': namespace,
+        'class_name': class_name,
+        'target': create_kernel_params.get("target", "cpu"),
+        'field': representative_field,
+        'headers': get_headers(ast),
+        'ghost_layers_to_include': ghost_layers_to_include,
+        'inner_outer_split': inner_outer_split
+    }
+    header = env.get_template("Sweep.tmpl.h").render(**jinja_context)
+    source = env.get_template("Sweep.tmpl.cpp").render(**jinja_context)
 
     source_extension = "cpp" if create_kernel_params.get("target", "cpu") == "cpu" else "cu"
     generation_context.write_file("{}.h".format(class_name), header)
@@ -132,7 +125,7 @@ def generate_pack_info_from_kernel(generation_context, class_name: str, assignme
         class_name: name of the generated class
         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:
+        kind: can either be pull or push
         **create_kernel_params: remaining keyword arguments are passed to `pystencils.create_kernel`
     """
     assert kind in ('push', 'pull')
@@ -240,7 +233,6 @@ def generate_pack_info(generation_context, class_name: str,
         pack_kernels[direction_strings] = KernelInfo(pack_ast)
         unpack_kernels[direction_strings] = KernelInfo(unpack_ast)
         elements_per_cell[direction_strings] = len(terms)
-
     fused_kernel = create_kernel([Assignment(buffer.center, t) for t in all_accesses], **create_kernel_params)
     fused_kernel.assumed_inner_stride_one = create_kernel_params['cpu_vectorize_info']['assume_inner_stride_one']
 
@@ -333,6 +325,12 @@ class KernelInfo:
         self.varying_parameters = tuple(varying_parameters)
         self.parameters = ast.get_parameters()  # cache parameters here
 
+    def has_same_interface(self, other):
+        return self.temporary_fields == other.temporary_fields \
+            and self.field_swaps == other.field_swaps \
+            and self.varying_parameters == other.varying_parameters \
+            and self.parameters == other.parameters
+
 
 def get_vectorize_instruction_set(generation_context):
     if generation_context.optimize_for_localhost:
diff --git a/python/pystencils_walberla/jinja_filters.py b/python/pystencils_walberla/jinja_filters.py
index 60903be9e73a3ce92a83b1ab1e3968c791b27e3b..7158f81685b5382f7516299af574fd0d374be47a 100644
--- a/python/pystencils_walberla/jinja_filters.py
+++ b/python/pystencils_walberla/jinja_filters.py
@@ -338,6 +338,26 @@ def generate_constructor_parameters(kernel_info, parameters_to_ignore=None):
     return ", ".join(parameter_list + varying_parameters)
 
 
+def generate_constructor_call_arguments(kernel_info, parameters_to_ignore=None):
+    if parameters_to_ignore is None:
+        parameters_to_ignore = []
+
+    varying_parameters = []
+    if hasattr(kernel_info, 'varying_parameters'):
+        varying_parameters = kernel_info.varying_parameters
+    varying_parameter_names = tuple(e[1] for e in varying_parameters)
+    parameters_to_ignore += kernel_info.temporary_fields + varying_parameter_names
+
+    parameter_list = []
+    for param in kernel_info.parameters:
+        if param.is_field_pointer and param.field_name not in parameters_to_ignore:
+            parameter_list.append("%sID" % (param.field_name, ))
+        elif not param.is_field_parameter and param.symbol.name not in parameters_to_ignore:
+            parameter_list.append(f'{param.symbol.name}_')
+    varying_parameters = ["%s_" % e for e in varying_parameter_names]
+    return ", ".join(parameter_list + varying_parameters)
+
+
 @jinja2.contextfilter
 def generate_members(ctx, kernel_info, parameters_to_ignore=(), only_fields=False):
     ast = kernel_info.ast
@@ -382,14 +402,25 @@ def generate_destructor(kernel_info, class_name):
         return temporary_constructor.format(contents=contents, class_name=class_name)
 
 
+@jinja2.contextfilter
+def nested_class_method_definition_prefix(ctx, nested_class_name):
+    outer_class = ctx['class_name']
+    if len(nested_class_name) == 0:
+        return outer_class
+    else:
+        return outer_class + '::' + nested_class_name
+
+
 def add_pystencils_filters_to_jinja_env(jinja_env):
     jinja_env.filters['generate_definition'] = generate_definition
     jinja_env.filters['generate_declaration'] = generate_declaration
     jinja_env.filters['generate_members'] = generate_members
     jinja_env.filters['generate_constructor_parameters'] = generate_constructor_parameters
     jinja_env.filters['generate_constructor_initializer_list'] = generate_constructor_initializer_list
+    jinja_env.filters['generate_constructor_call_arguments'] = generate_constructor_call_arguments
     jinja_env.filters['generate_call'] = generate_call
     jinja_env.filters['generate_block_data_to_field_extraction'] = generate_block_data_to_field_extraction
     jinja_env.filters['generate_swaps'] = generate_swaps
     jinja_env.filters['generate_refs_for_kernel_parameters'] = generate_refs_for_kernel_parameters
     jinja_env.filters['generate_destructor'] = generate_destructor
+    jinja_env.filters['nested_class_method_definition_prefix'] = nested_class_method_definition_prefix
diff --git a/python/pystencils_walberla/templates/Boundary.tmpl.cpp b/python/pystencils_walberla/templates/Boundary.tmpl.cpp
index 33f799107424bc94b9e7db4cd3a1ab1dcb827754..ef1ec3b7b860f3ac0fd4b290e1e5fe33bd7f772e 100644
--- a/python/pystencils_walberla/templates/Boundary.tmpl.cpp
+++ b/python/pystencils_walberla/templates/Boundary.tmpl.cpp
@@ -51,7 +51,11 @@ namespace {{namespace}} {
 #endif
 
 
-{{kernel|generate_definition}}
+{% for sweep_class, sweep_kernel in sweep_classes.items() %}
+
+{{sweep_kernel|generate_definition}}
+
+{% endfor %}
 
 #ifdef __GNUC__
 #pragma GCC diagnostic pop
@@ -61,8 +65,10 @@ namespace {{namespace}} {
 #pragma pop
 #endif
 
+{% for sweep_class, sweep_kernel in sweep_classes.items() %}
+
 
-void {{class_name}}::run( IBlock * block, IndexVectors::Type type {% if target == 'gpu'%}, cudaStream_t stream {%endif%})
+void {{sweep_class|nested_class_method_definition_prefix}}::run( IBlock * block, IndexVectors::Type type {% if target == 'gpu'%}, cudaStream_t stream {%endif%})
 {
     auto * indexVectors = block->getData<IndexVectors>(indexVectorID);
     int64_t indexVectorSize = int64_c( indexVectors->indexVector(type).size() );
@@ -77,25 +83,27 @@ void {{class_name}}::run( IBlock * block, IndexVectors::Type type {% if target =
 
     uint8_t * _data_indexVector = reinterpret_cast<uint8_t*>(pointer);
 
-    {{kernel|generate_block_data_to_field_extraction(['indexVector', 'indexVectorSize'])|indent(4)}}
-    {{kernel|generate_call(spatial_shape_symbols=['indexVectorSize'], stream='stream')|indent(4)}}
+    {{sweep_kernel|generate_block_data_to_field_extraction(['indexVector', 'indexVectorSize'])|indent(4)}}
+    {{sweep_kernel|generate_refs_for_kernel_parameters(prefix='', parameters_to_ignore=['indexVectorSize'], ignore_fields=True)|indent(4) }}
+    {{sweep_kernel|generate_call(spatial_shape_symbols=['indexVectorSize'], stream='stream')|indent(4)}}
 }
 
-void {{class_name}}::operator() ( IBlock * block{% if target == 'gpu'%}, cudaStream_t stream {%endif%} )
+void {{sweep_class|nested_class_method_definition_prefix}}::operator() ( IBlock * block{% if target == 'gpu'%}, cudaStream_t stream {%endif%} )
 {
     run( block, IndexVectors::ALL{% if target == 'gpu'%}, stream {%endif%});
 }
 
-void {{class_name}}::inner( IBlock * block{% if target == 'gpu'%}, cudaStream_t stream {%endif%} )
+void {{sweep_class|nested_class_method_definition_prefix}}::inner( IBlock * block{% if target == 'gpu'%}, cudaStream_t stream {%endif%} )
 {
     run( block, IndexVectors::INNER{% if target == 'gpu'%}, stream {%endif%} );
 }
 
-void {{class_name}}::outer( IBlock * block{% if target == 'gpu'%}, cudaStream_t stream {%endif%} )
+void {{sweep_class|nested_class_method_definition_prefix}}::outer( IBlock * block{% if target == 'gpu'%}, cudaStream_t stream {%endif%} )
 {
     run( block, IndexVectors::OUTER{% if target == 'gpu'%}, stream {%endif%} );
 }
 
+{% endfor %}
 
 } // namespace {{namespace}}
 } // namespace walberla
diff --git a/python/pystencils_walberla/templates/Boundary.tmpl.h b/python/pystencils_walberla/templates/Boundary.tmpl.h
index c856adceaa0a5b1e0cbc6470da5ee52c3dfee622..c0a39c32b0ae8c08202ae7f0641f03db0ad4e592 100644
--- a/python/pystencils_walberla/templates/Boundary.tmpl.h
+++ b/python/pystencils_walberla/templates/Boundary.tmpl.h
@@ -17,13 +17,13 @@
 //! \\author pystencils
 //======================================================================================================================
 
-
 #include "core/DataTypes.h"
 
 {% if target is equalto 'cpu' -%}
 #include "field/GhostLayerField.h"
 {%- elif target is equalto 'gpu' -%}
 #include "cuda/GPUField.h"
+#include "cuda/FieldCopy.h"
 {%- endif %}
 #include "domain_decomposition/BlockDataID.h"
 #include "domain_decomposition/IBlock.h"
@@ -64,7 +64,7 @@ public:
             NUM_TYPES = 3
         };
 
-        IndexVectors() : cpuVectors_(NUM_TYPES)  {}
+        IndexVectors() = default;
         bool operator==(IndexVectors & other) { return other.cpuVectors_ == cpuVectors_; }
 
         {% if target == 'gpu' -%}
@@ -72,14 +72,14 @@ public:
             for( auto & gpuVec: gpuVectors_)
                 cudaFree( gpuVec );
         }
-        {% endif %}
+        {% endif -%}
 
         CpuIndexVector & indexVector(Type t) { return cpuVectors_[t]; }
         {{StructName}} * pointerCpu(Type t)  { return &(cpuVectors_[t][0]); }
 
         {% if target == 'gpu' -%}
         {{StructName}} * pointerGpu(Type t)  { return gpuVectors_[t]; }
-        {% endif %}
+        {% endif -%}
 
         void syncGPU()
         {
@@ -100,39 +100,66 @@ public:
         }
 
     private:
-        std::vector<CpuIndexVector> cpuVectors_;
+        std::vector<CpuIndexVector> cpuVectors_{NUM_TYPES};
 
         {% if target == 'gpu' -%}
         using GpuIndexVector = {{StructName}} *;
         std::vector<GpuIndexVector> gpuVectors_;
-        {% endif %}
+        {%- endif %}
+    };
+
+    {% if multi_sweep %}
+    {% for sweep_class_name, sweep_kernel_info in sweep_classes.items() %}
+    class {{sweep_class_name}}
+    {
+        public:
+            {{sweep_class_name}} ( {{sweep_kernel_info|generate_constructor_parameters(['indexVectorSize'])}} )
+                : {{ sweep_kernel_info|generate_constructor_initializer_list(['indexVectorSize']) }} {};
+
+            void operator() ( IBlock * block {% if target == 'gpu'%}, cudaStream_t stream = 0 {%endif%});
+            void inner( IBlock * block {% if target == 'gpu'%}, cudaStream_t stream = 0 {%endif%});
+            void outer( IBlock * block {% if target == 'gpu'%}, cudaStream_t stream = 0 {%endif%});
+        
+        private:
+            void run( IBlock * block, IndexVectors::Type type{% if target == 'gpu'%}, cudaStream_t stream = 0 {%endif%});
+
+            {{sweep_kernel_info|generate_members(['indexVectorSize'])|indent(12)}}
     };
 
+    {{sweep_class_name}} get{{sweep_class_name}} ()
+    {
+        return {{sweep_class_name}} ( {{sweep_kernel_info|generate_constructor_call_arguments(['indexVectorSize'])|indent(12)}} );
+    }
+    {% endfor %}
+    {% endif %}
 
     {{class_name}}( const shared_ptr<StructuredBlockForest> & blocks,
-                   {{kernel|generate_constructor_parameters(['indexVector', 'indexVectorSize'])}} )
-        : {{ kernel|generate_constructor_initializer_list(['indexVector', 'indexVectorSize']) }}
+                   {{dummy_kernel_info|generate_constructor_parameters(['indexVector', 'indexVectorSize'])}}{{additional_data_handler.constructor_arguments}})
+        :{{additional_data_handler.initialiser_list}} {{ dummy_kernel_info|generate_constructor_initializer_list(['indexVector', 'indexVectorSize']) }}
     {
         auto createIdxVector = []( IBlock * const , StructuredBlockStorage * const ) { return new IndexVectors(); };
         indexVectorID = blocks->addStructuredBlockData< IndexVectors >( createIdxVector, "IndexField_{{class_name}}");
     };
 
+    {% if not multi_sweep %}
+
     void operator() ( IBlock * block {% if target == 'gpu'%}, cudaStream_t stream = 0 {%endif%});
     void inner( IBlock * block {% if target == 'gpu'%}, cudaStream_t stream = 0 {%endif%});
     void outer( IBlock * block {% if target == 'gpu'%}, cudaStream_t stream = 0 {%endif%});
 
+    {% endif %}
 
     template<typename FlagField_T>
     void fillFromFlagField( const shared_ptr<StructuredBlockForest> & blocks, ConstBlockDataID flagFieldID,
                             FlagUID boundaryFlagUID, FlagUID domainFlagUID)
     {
         for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )
-            fillFromFlagField<FlagField_T>( &*blockIt, flagFieldID, boundaryFlagUID, domainFlagUID );
+            fillFromFlagField<FlagField_T>({{additional_data_handler.additional_arguments_for_fill_function}}&*blockIt, flagFieldID, boundaryFlagUID, domainFlagUID );
     }
 
 
     template<typename FlagField_T>
-    void fillFromFlagField( IBlock * block, ConstBlockDataID flagFieldID,
+    void fillFromFlagField({{additional_data_handler.additional_parameters_for_fill_function}}IBlock * block, ConstBlockDataID flagFieldID,
                             FlagUID boundaryFlagUID, FlagUID domainFlagUID )
     {
         auto * indexVectors = block->getData< IndexVectors > ( indexVectorID );
@@ -140,8 +167,8 @@ public:
         auto & indexVectorInner = indexVectors->indexVector(IndexVectors::INNER);
         auto & indexVectorOuter = indexVectors->indexVector(IndexVectors::OUTER);
 
-
         auto * flagField = block->getData< FlagField_T > ( flagFieldID );
+        {{additional_data_handler.additional_field_data|indent(4)}}
 
         if( !(flagField->flagExists(boundaryFlagUID) && flagField->flagExists(domainFlagUID) ))
             return;
@@ -152,7 +179,6 @@ public:
         auto inner = flagField->xyzSize();
         inner.expand( cell_idx_t(-1) );
 
-
         indexVectorAll.clear();
         indexVectorInner.clear();
         indexVectorOuter.clear();
@@ -161,15 +187,15 @@ public:
         {
             if( ! isFlagSet(it, domainFlag) )
                 continue;
-
-            {%- for dirIdx, dirVec, offset in stencil_info %}
+            {%- for dirIdx, dirVec, offset in additional_data_handler.stencil_info %}
             if ( isFlagSet( it.neighbor({{offset}} {%if dim == 3%}, 0 {%endif %}), boundaryFlag ) )
             {
                 {% if inner_or_boundary -%}
                 auto element = {{StructName}}(it.x(), it.y(), {%if dim == 3%} it.z(), {%endif %} {{dirIdx}} );
                 {% else -%}
-                auto element = {{StructName}}(it.x() + cell_idx_c({{dirVec[0]}}), it.y() + cell_idx_c({{dirVec[1]}}), {%if dim == 3%} it.z() + cell_idx_c({{dirVec[2]}}), {%endif %} {{inverse_directions[dirIdx]}} );
+                auto element = {{StructName}}(it.x() + cell_idx_c({{dirVec[0]}}), it.y() + cell_idx_c({{dirVec[1]}}), {%if dim == 3%} it.z() + cell_idx_c({{dirVec[2]}}), {%endif %} {{additional_data_handler.inverse_directions[dirIdx]}} );
                 {% endif -%}
+                {{additional_data_handler.data_initialisation(dirIdx)|indent(16)}}
                 indexVectorAll.push_back( element );
                 if( inner.contains( it.x(), it.y(), it.z() ) )
                     indexVectorInner.push_back( element );
@@ -178,16 +204,18 @@ public:
             }
             {% endfor %}
         }
-
         indexVectors->syncGPU();
     }
 
 private:
+    {% if not multi_sweep %}
     void run( IBlock * block, IndexVectors::Type type{% if target == 'gpu'%}, cudaStream_t stream = 0 {%endif%});
+    {% endif %}
 
     BlockDataID indexVectorID;
+    {{additional_data_handler.additional_member_variable|indent(4)}}
 public:
-    {{kernel|generate_members(('indexVector', 'indexVectorSize'))|indent(4)}}
+    {{dummy_kernel_info|generate_members(('indexVector', 'indexVectorSize'))|indent(4)}}
 };
 
 
diff --git a/python/pystencils_walberla/templates/Sweep.tmpl.cpp b/python/pystencils_walberla/templates/Sweep.tmpl.cpp
index b3b12c6b39f93c71fc8a3a9fd3b4430e6a1cbb16..9711be8cde6d7a94475c8172cb858c03631f60d4 100644
--- a/python/pystencils_walberla/templates/Sweep.tmpl.cpp
+++ b/python/pystencils_walberla/templates/Sweep.tmpl.cpp
@@ -59,7 +59,7 @@ void {{class_name}}::operator()( IBlock * block{%if target is equalto 'gpu'%} ,
 {
     {{kernel|generate_block_data_to_field_extraction|indent(4)}}
     {{kernel|generate_refs_for_kernel_parameters(prefix='this->', ignore_fields=True)|indent(4) }}
-    {{kernel|generate_call(stream='stream')|indent(4)}}
+    {{kernel|generate_call(ghost_layers_to_include=ghost_layers_to_include, stream='stream')|indent(4)}}
     {{kernel|generate_swaps|indent(4)}}
 }
 
@@ -83,6 +83,69 @@ void {{class_name}}::runOnCellInterval( const shared_ptr<StructuredBlockStorage>
     {{kernel|generate_swaps|indent(4)}}
 }
 
+{%if inner_outer_split%}
+void {{class_name}}::inner( IBlock * block{%if target is equalto 'gpu'%} , cudaStream_t stream{% endif %} )
+{
+    {{kernel|generate_block_data_to_field_extraction|indent(4)}}
+
+    CellInterval inner = {{field}}->xyzSize();
+    inner.expand(Cell(-outerWidth_[0], -outerWidth_[1], -outerWidth_[2]));
+
+    {{kernel|generate_refs_for_kernel_parameters(prefix='this->', ignore_fields=True)|indent(4) }}
+    {{kernel|generate_call(stream='stream', cell_interval='inner')|indent(4)}}
+}
+
+
+void {{class_name}}::outer( IBlock * block{%if target is equalto 'gpu'%} , cudaStream_t stream {% endif %} )
+{
+    {{kernel|generate_block_data_to_field_extraction|indent(4)}}
+
+    if( layers_.size() == 0 )
+    {
+        CellInterval ci;
+
+        {{field}}->getSliceBeforeGhostLayer(stencil::T, ci, outerWidth_[2], false);
+        layers_.push_back(ci);
+        {{field}}->getSliceBeforeGhostLayer(stencil::B, ci, outerWidth_[2], false);
+        layers_.push_back(ci);
+
+        {{field}}->getSliceBeforeGhostLayer(stencil::N, ci, outerWidth_[1], false);
+        ci.expand(Cell(0, 0, -outerWidth_[2]));
+        layers_.push_back(ci);
+        {{field}}->getSliceBeforeGhostLayer(stencil::S, ci, outerWidth_[1], false);
+        ci.expand(Cell(0, 0, -outerWidth_[2]));
+        layers_.push_back(ci);
+
+        {{field}}->getSliceBeforeGhostLayer(stencil::E, ci, outerWidth_[0], false);
+        ci.expand(Cell(0, -outerWidth_[1], -outerWidth_[2]));
+        layers_.push_back(ci);
+        {{field}}->getSliceBeforeGhostLayer(stencil::W, ci, outerWidth_[0], false);
+        ci.expand(Cell(0, -outerWidth_[1], -outerWidth_[2]));
+        layers_.push_back(ci);
+    }
+
+    {%if target is equalto 'gpu'%}
+    {
+        auto parallelSection_ = parallelStreams_.parallelSection( stream );
+        for( auto & ci: layers_ )
+        {
+            parallelSection_.run([&]( auto s ) {
+                {{kernel|generate_refs_for_kernel_parameters(prefix='this->', ignore_fields=True)|indent(4) }}
+                {{kernel|generate_call(stream='s', cell_interval='ci')|indent(16)}}
+            });
+        }
+    }
+    {% else %}
+    for( auto & ci: layers_ )
+    {
+        {{kernel|generate_refs_for_kernel_parameters(prefix='this->', ignore_fields=True)|indent(8) }}
+        {{kernel|generate_call(cell_interval='ci')|indent(8)}}
+    }
+    {% endif %}
+
+    {{kernel|generate_swaps|indent(4)}}
+}
+{% endif %}
 
 } // namespace {{namespace}}
 } // namespace walberla
diff --git a/python/pystencils_walberla/templates/Sweep.tmpl.h b/python/pystencils_walberla/templates/Sweep.tmpl.h
index b4db0f35bd6fc9ad5046c83bd99eca004291cc6f..9f0711a1e807553b2ca9cea2fa5dc5790c9c91af 100644
--- a/python/pystencils_walberla/templates/Sweep.tmpl.h
+++ b/python/pystencils_walberla/templates/Sweep.tmpl.h
@@ -24,6 +24,9 @@
 #include "field/GhostLayerField.h"
 {%- elif target is equalto 'gpu' -%}
 #include "cuda/GPUField.h"
+{% if inner_outer_split -%}
+#include "cuda/ParallelStreams.h"
+{%- endif %}
 {%- endif %}
 #include "field/SwapableCompare.h"
 #include "domain_decomposition/BlockDataID.h"
@@ -42,6 +45,7 @@
 #if ( defined WALBERLA_CXX_COMPILER_IS_GNU ) || ( defined WALBERLA_CXX_COMPILER_IS_CLANG )
 #   pragma GCC diagnostic push
 #   pragma GCC diagnostic ignored "-Wunused-parameter"
+#   pragma GCC diagnostic ignored "-Wreorder"
 #endif
 
 namespace walberla {
@@ -51,8 +55,8 @@ namespace {{namespace}} {
 class {{class_name}}
 {
 public:
-    {{class_name}}( {{kernel|generate_constructor_parameters}})
-        : {{ kernel|generate_constructor_initializer_list }}
+    {{class_name}}( {{kernel|generate_constructor_parameters}} {%if inner_outer_split%}, const Cell & outerWidth=Cell(1, 1, 1){% endif %})
+        : {{ kernel|generate_constructor_initializer_list }}{%if inner_outer_split%}{% if kernel|generate_constructor_initializer_list|length %},{% endif %} outerWidth_(outerWidth){% endif %}
     {};
 
     {{ kernel| generate_destructor(class_name) |indent(4) }}
@@ -79,7 +83,29 @@ public:
         };
     }
 
+{% if inner_outer_split %}
+
+    void inner( IBlock * block{%if target is equalto 'gpu'%} , cudaStream_t stream = nullptr{% endif %} );
+    void outer( IBlock * block{%if target is equalto 'gpu'%} , cudaStream_t stream = nullptr{% endif %} );
+
+    void setOuterPriority(int priority ) {
+       {%if target is equalto 'gpu'%}
+       parallelStreams_.setStreamPriority(priority);
+       {%endif%}
+    }
+    {{kernel|generate_members|indent(4)}}
+
+private:
+    {%if target is equalto 'gpu'%}
+    cuda::ParallelStreams parallelStreams_;
+    {% endif %}
+
+    Cell outerWidth_;
+    std::vector<CellInterval> layers_;
+
+{%- else -%}
     {{ kernel|generate_members|indent(4) }}
+{% endif -%}
 
 };
 
diff --git a/python/pystencils_walberla/templates/SweepInnerOuter.tmpl.cpp b/python/pystencils_walberla/templates/SweepInnerOuter.tmpl.cpp
deleted file mode 100644
index 6aec87cacbf3e1689ad07659f50e2b7d9c8206b1..0000000000000000000000000000000000000000
--- a/python/pystencils_walberla/templates/SweepInnerOuter.tmpl.cpp
+++ /dev/null
@@ -1,146 +0,0 @@
-//======================================================================================================================
-//
-//  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 {{class_name}}.cpp
-//! \\ingroup lbm
-//! \\author lbmpy
-//======================================================================================================================
-
-#include <cmath>
-
-#include "core/DataTypes.h"
-#include "core/Macros.h"
-#include "{{class_name}}.h"
-
-
-{% if target is equalto 'cpu' -%}
-#define FUNC_PREFIX
-{%- elif target is equalto 'gpu' -%}
-#define FUNC_PREFIX __global__
-{%- endif %}
-
-#if ( defined WALBERLA_CXX_COMPILER_IS_GNU ) || ( defined WALBERLA_CXX_COMPILER_IS_CLANG )
-#   pragma GCC diagnostic push
-#   pragma GCC diagnostic ignored "-Wfloat-equal"
-#   pragma GCC diagnostic ignored "-Wshadow"
-#   pragma GCC diagnostic ignored "-Wconversion"
-#endif
-
-using namespace std;
-
-namespace walberla {
-namespace {{namespace}} {
-
-{{kernel|generate_definition(target)}}
-
-void {{class_name}}::operator() ( IBlock * block{%if target is equalto 'gpu'%} , cudaStream_t stream{% endif %} )
-{
-    {{kernel|generate_block_data_to_field_extraction|indent(4)}}
-    {{kernel|generate_refs_for_kernel_parameters(prefix='this->', ignore_fields=True)|indent(4) }}
-    {{kernel|generate_call(stream='stream')|indent(4)}}
-    {{kernel|generate_swaps|indent(4)}}
-}
-
-
-void {{class_name}}::runOnCellInterval( const shared_ptr<StructuredBlockStorage> & blocks,
-                                        const CellInterval & globalCellInterval,
-                                        cell_idx_t ghostLayers,
-                                        IBlock * block{%if target is equalto 'gpu'%} , cudaStream_t stream{% endif %} )
-{
-    CellInterval ci = globalCellInterval;
-    CellInterval blockBB = blocks->getBlockCellBB( *block);
-    blockBB.expand( ghostLayers );
-    ci.intersect( blockBB );
-    blocks->transformGlobalToBlockLocalCellInterval( ci, *block );
-    if( ci.empty() )
-        return;
-
-    {{kernel|generate_block_data_to_field_extraction|indent(4)}}
-    {{kernel|generate_refs_for_kernel_parameters(prefix='this->', ignore_fields=True)|indent(4) }}
-    {{kernel|generate_call(stream='stream', cell_interval='ci')|indent(4)}}
-    {{kernel|generate_swaps|indent(4)}}
-}
-
-
-void {{class_name}}::inner( IBlock * block{%if target is equalto 'gpu'%} , cudaStream_t stream{% endif %} )
-{
-    {{kernel|generate_block_data_to_field_extraction|indent(4)}}
-
-    CellInterval inner = {{field}}->xyzSize();
-    inner.expand(Cell(-outerWidth_[0], -outerWidth_[1], -outerWidth_[2]));
-
-    {{kernel|generate_refs_for_kernel_parameters(prefix='this->', ignore_fields=True)|indent(4) }}
-    {{kernel|generate_call(stream='stream', cell_interval='inner')|indent(4)}}
-}
-
-
-void {{class_name}}::outer( IBlock * block{%if target is equalto 'gpu'%} , cudaStream_t stream {% endif %} )
-{
-    {{kernel|generate_block_data_to_field_extraction|indent(4)}}
-
-    if( layers_.size() == 0 )
-    {
-        CellInterval ci;
-
-        {{field}}->getSliceBeforeGhostLayer(stencil::T, ci, outerWidth_[2], false);
-        layers_.push_back(ci);
-        {{field}}->getSliceBeforeGhostLayer(stencil::B, ci, outerWidth_[2], false);
-        layers_.push_back(ci);
-
-        {{field}}->getSliceBeforeGhostLayer(stencil::N, ci, outerWidth_[1], false);
-        ci.expand(Cell(0, 0, -outerWidth_[2]));
-        layers_.push_back(ci);
-        {{field}}->getSliceBeforeGhostLayer(stencil::S, ci, outerWidth_[1], false);
-        ci.expand(Cell(0, 0, -outerWidth_[2]));
-        layers_.push_back(ci);
-
-        {{field}}->getSliceBeforeGhostLayer(stencil::E, ci, outerWidth_[0], false);
-        ci.expand(Cell(0, -outerWidth_[1], -outerWidth_[2]));
-        layers_.push_back(ci);
-        {{field}}->getSliceBeforeGhostLayer(stencil::W, ci, outerWidth_[0], false);
-        ci.expand(Cell(0, -outerWidth_[1], -outerWidth_[2]));
-        layers_.push_back(ci);
-    }
-
-    {%if target is equalto 'gpu'%}
-    {
-        auto parallelSection_ = parallelStreams_.parallelSection( stream );
-        for( auto & ci: layers_ )
-        {
-            parallelSection_.run([&]( auto s ) {
-                {{kernel|generate_refs_for_kernel_parameters(prefix='this->', ignore_fields=True)|indent(4) }}
-                {{kernel|generate_call(stream='s', cell_interval='ci')|indent(16)}}
-            });
-        }
-    }
-    {% else %}
-    for( auto & ci: layers_ )
-    {
-        {{kernel|generate_refs_for_kernel_parameters(prefix='this->', ignore_fields=True)|indent(8) }}
-        {{kernel|generate_call(cell_interval='ci')|indent(8)}}
-    }
-    {% endif %}
-
-    {{kernel|generate_swaps|indent(4)}}
-}
-
-
-} // namespace {{namespace}}
-} // namespace walberla
-
-
-#if ( defined WALBERLA_CXX_COMPILER_IS_GNU ) || ( defined WALBERLA_CXX_COMPILER_IS_CLANG )
-#   pragma GCC diagnostic pop
-#endif
diff --git a/python/pystencils_walberla/templates/SweepInnerOuter.tmpl.h b/python/pystencils_walberla/templates/SweepInnerOuter.tmpl.h
deleted file mode 100644
index 6f6094d0e08dfe00e7873cfb204f636c81c0a04a..0000000000000000000000000000000000000000
--- a/python/pystencils_walberla/templates/SweepInnerOuter.tmpl.h
+++ /dev/null
@@ -1,113 +0,0 @@
-//======================================================================================================================
-//
-//  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 {{class_name}}.h
-//! \\author pystencils
-//======================================================================================================================
-
-#pragma once
-#include "core/DataTypes.h"
-
-{% if target is equalto 'cpu' -%}
-#include "field/GhostLayerField.h"
-{%- elif target is equalto 'gpu' -%}
-#include "cuda/GPUField.h"
-#include "cuda/ParallelStreams.h"
-{%- endif %}
-#include "field/SwapableCompare.h"
-#include "domain_decomposition/BlockDataID.h"
-#include "domain_decomposition/IBlock.h"
-#include "domain_decomposition/StructuredBlockStorage.h"
-#include <set>
-
-#ifdef __GNUC__
-#define RESTRICT __restrict__
-#elif _MSC_VER
-#define RESTRICT __restrict
-#else
-#define RESTRICT
-#endif
-
-#if ( defined WALBERLA_CXX_COMPILER_IS_GNU ) || ( defined WALBERLA_CXX_COMPILER_IS_CLANG )
-#   pragma GCC diagnostic push
-#   pragma GCC diagnostic ignored "-Wunused-parameter"
-#   pragma GCC diagnostic ignored "-Wreorder"
-#endif
-
-namespace walberla {
-namespace {{namespace}} {
-
-
-class {{class_name}}
-{
-public:
-    {{class_name}}( {{kernel|generate_constructor_parameters}}, const Cell & outerWidth=Cell(1, 1, 1))
-        : {{ kernel|generate_constructor_initializer_list }}{% if kernel|generate_constructor_initializer_list|length %},{% endif %} outerWidth_(outerWidth)
-    {};
-
-    {{ kernel| generate_destructor(class_name) |indent(4) }}
-
-
-    void operator() ( IBlock * block{%if target is equalto 'gpu'%} , cudaStream_t stream = nullptr{% endif %} );
-
-    void runOnCellInterval(const shared_ptr<StructuredBlockStorage> & blocks,
-                           const CellInterval & globalCellInterval, cell_idx_t ghostLayers, IBlock * block
-                           {%if target is equalto 'gpu'%} , cudaStream_t stream = nullptr{% endif %});
-
-
-
-    static std::function<void (IBlock*)> getSweep(const shared_ptr<{{class_name}}> & kernel) {
-        return [kernel](IBlock * b) { (*kernel)(b); };
-    }
-
-    static std::function<void (IBlock*{%if target is equalto 'gpu'%} , cudaStream_t {% endif %})>
-            getSweepOnCellInterval(const shared_ptr<{{class_name}}> & kernel,
-                                   const shared_ptr<StructuredBlockStorage> & blocks,
-                                   const CellInterval & globalCellInterval,
-                                   cell_idx_t ghostLayers=1 )
-    {
-        return [kernel, blocks, globalCellInterval, ghostLayers] (IBlock * b{%if target is equalto 'gpu'%} , cudaStream_t stream = nullptr{% endif %}) {
-            kernel->runOnCellInterval(blocks, globalCellInterval, ghostLayers, b{%if target is equalto 'gpu'%}, stream {% endif %});
-        };
-    }
-
-
-    void inner( IBlock * block{%if target is equalto 'gpu'%} , cudaStream_t stream = nullptr{% endif %} );
-    void outer( IBlock * block{%if target is equalto 'gpu'%} , cudaStream_t stream = nullptr{% endif %} );
-
-    void setOuterPriority(int priority ) {
-        {%if target is equalto 'gpu'%}
-        parallelStreams_.setStreamPriority(priority);
-        {%endif%}
-    }
-    {{kernel|generate_members|indent(4)}}
-
-private:
-    {%if target is equalto 'gpu'%}
-    cuda::ParallelStreams parallelStreams_;
-    {% endif %}
-
-    Cell outerWidth_;
-    std::vector<CellInterval> layers_;
-};
-
-
-} // namespace {{namespace}}
-} // namespace walberla
-
-
-#if ( defined WALBERLA_CXX_COMPILER_IS_GNU ) || ( defined WALBERLA_CXX_COMPILER_IS_CLANG )
-#   pragma GCC diagnostic pop
-#endif
diff --git a/python/waLBerla/__init__.py b/python/waLBerla/__init__.py
index db838d8351dccaa400c450f1bf35d7d3daef60da..6e1e3ebabb56ba1e2f4307ef4c30876d921c3217 100644
--- a/python/waLBerla/__init__.py
+++ b/python/waLBerla/__init__.py
@@ -4,14 +4,13 @@ from .callbacks import memberCallback as member_callback  # noqa:F401
 
 import sys
 
+
 try:
     from .walberla_cpp import *  # noqa: F403
-
     cpp_available = True
 except ImportError:
     try:
         from walberla_cpp import *  # noqa: F403
-
         cpp_available = True
     except ImportError:
         cpp_available = False
@@ -41,26 +40,14 @@ if cpp_available:
         sys.modules[__name__ + '.field'] = field  # noqa: F405
         # extend the C++ module with some python functions
         from .field_extension import extend as extend_field
-
         extend_field(field)  # noqa: F405
+
     if 'cuda' in globals():
         sys.modules[__name__ + '.cuda'] = cuda  # noqa: F405
         from .cuda_extension import extend as extend_cuda
-
         extend_cuda(cuda)  # noqa: F405
-    if 'geometry' in globals():
-        sys.modules[__name__ + '.geometry'] = geometry  # noqa: F405
-    if 'lbm' in globals():
-        sys.modules[__name__ + '.lbm'] = lbm  # noqa: F405
-    if 'postprocessing' in globals():
-        sys.modules[__name__ + 'postprocessing'] = postprocessing  # noqa: F405
     if 'mpi' in globals():
         sys.modules[__name__ + '.mpi'] = mpi  # noqa: F405
-    if 'timeloop' in globals():
-        sys.modules[__name__ + '.timeloop'] = timeloop  # noqa: F405
-        from .timeloop_extension import extend as extend_timeloop
-
-        extend_timeloop(timeloop)  # noqa: F405
 else:
     class Dummy:
         pass
diff --git a/python/waLBerla/callbacks.py b/python/waLBerla/callbacks.py
index 8b24c6e0ec7c1941355881a9613bf9e95f1c244f..07f2ebc0f35ae7cda48f3958531b1f0fb11f48ac 100644
--- a/python/waLBerla/callbacks.py
+++ b/python/waLBerla/callbacks.py
@@ -45,7 +45,6 @@ To register a certain python function as callback it has to be set as attribute
 """
 
 from __future__ import print_function, absolute_import, division, unicode_literals
-import os
 from functools import partial
 
 
@@ -54,10 +53,10 @@ from functools import partial
 class callback:
     """Decorator class to mark a Python function as waLBerla callback"""
 
-    def __init__(self, callbackFunction):
-        if not type(callbackFunction) is str:
+    def __init__(self, callback_function):
+        if not type(callback_function) is str:
             raise Exception("waLBerla callback: Name of function has to be a string")
-        self.callbackFunction = callbackFunction
+        self.callbackFunction = callback_function
 
     def __call__(self, f):
         try:
@@ -94,7 +93,6 @@ class ScenarioManager:
            Activation means to register the _configLoopCallback as 'config' waLBerla callback function
            which is called when a new scenario is expected.
            When config is called again the calbacks of the next scenario are activated.
-
     """
 
     def __init__(self):
@@ -110,19 +108,19 @@ class ScenarioManager:
         """Activates this scenario manager instance"""
         try:
             import walberla_cpp
-            boundFunc = self._configLoopCallback
-            setattr(walberla_cpp.callbacks, "config", boundFunc)
+            bound_func = self._configLoopCallback
+            setattr(walberla_cpp.callbacks, "config", bound_func)
         except ImportError:
             pass
 
-    def restrictScenarios(self, startScenario=0):
+    def restrictScenarios(self, start_scenario=0):
         """Simulates not all scenarios registered at this manager, but skips the first startScenario-1 scenarios"""
-        self._startScenario = startScenario
+        self._startScenario = start_scenario
 
     def _configLoopCallback(self, *args, **kwargs):
-        def findCallbacks(classType):
+        def findCallbacks(class_type):
             res = dict()
-            for key, value in classType.__dict__.items():
+            for key, value in class_type.__dict__.items():
                 if hasattr(value, "waLBerla_callback_member"):
                     res[key] = value
             return res
@@ -145,24 +143,14 @@ class ScenarioManager:
 
         try:
             import walberla_cpp
-            if 'WALBERLA_SCENARIO_IDX' in os.environ:
-                scenario_idx = int(os.environ['WALBERLA_SCENARIO_IDX'])
-                try:
-                    scenario = self._scenarios[scenario_idx]
-                except IndexError:
-                    walberla_cpp.log_info_on_root("Scenario does not exists - all scenarios simulated?")
-                    exit(1)
-                walberla_cpp.log_info_on_root("Simulating Scenario %d of %d :" % (scenario_idx, len(self._scenarios)))
-                yield get_config_from_scenario(scenario)
-            else:
-                for idx, scenario in enumerate(self._scenarios):
-                    if idx < self._startScenario:
-                        continue  # Skip over all scenarios with id < startScenario
-                    cfg = None
-                    while cfg is None:
-                        cfg = get_config_from_scenario(scenario)
-                    walberla_cpp.log_info_on_root("Simulating Scenario %d of %d :" % (idx, len(self._scenarios)))
-                    yield cfg
+            for idx, scenario in enumerate(self._scenarios):
+                if idx < self._startScenario:
+                    continue  # Skip over all scenarios with id < startScenario
+                cfg = None
+                while cfg is None:
+                    cfg = get_config_from_scenario(scenario)
+                walberla_cpp.log_info_on_root("Simulating Scenario %d of %d :" % (idx + 1, len(self._scenarios)))
+                yield cfg
 
         except ImportError:
             pass
diff --git a/python/waLBerla/core_extension.py b/python/waLBerla/core_extension.py
index f06fefa68ff813a3bb5b0b3579fb4c9e7eba5cf1..be80be1307f0175cbd35494f242a192154cf9e57 100644
--- a/python/waLBerla/core_extension.py
+++ b/python/waLBerla/core_extension.py
@@ -12,7 +12,7 @@ class SliceMaker(object):
 makeSlice = SliceMaker()
 
 
-def normalizeSlice(slices, sizes):
+def normalize_slice(slices, sizes):
     """Converts slices with floating point entries to integer slices"""
     assert (len(slices) == len(sizes))
 
@@ -29,52 +29,52 @@ def normalizeSlice(slices, sizes):
         assert (type(s) is slice)
 
         if s.start is None:
-            newStart = 0
+            new_start = 0
         elif type(s.start) is float:
-            newStart = int(s.start * size)
+            new_start = int(s.start * size)
         else:
-            newStart = s.start
+            new_start = s.start
 
         if s.stop is None:
-            newStop = size
+            new_stop = size
         elif type(s.stop) is float:
-            newStop = int(s.stop * size)
+            new_stop = int(s.stop * size)
         else:
-            newStop = s.stop
+            new_stop = s.stop
 
-        result.append(slice(newStart, newStop, s.step))
+        result.append(slice(new_start, new_stop, s.step))
 
     return tuple(result)
 
 
-def sliceToCellInterval(s):
-    newMin = [0, 0, 0]
-    newMax = [0, 0, 0]
+def slice_to_cell_interval(s):
+    new_min = [0, 0, 0]
+    new_max = [0, 0, 0]
     for i in range(3):
         if type(s[i]) is int:
-            newMin[i] = s[i]
-            newMax[i] = s[i]
+            new_min[i] = s[i]
+            new_max[i] = s[i]
         else:
-            newMin[i] = s[i].start
-            newMax[i] = s[i].stop - 1
-    return walberla_cpp.CellInterval(newMin, newMax)
+            new_min[i] = s[i].start
+            new_max[i] = s[i].stop - 1
+    return walberla_cpp.CellInterval(new_min[0], new_min[1], new_min[2], new_max[0], new_max[1], new_max[2])
 
 
-def cellIntervalToSlice(cellInterval, collapseExtentOne=True):
-    if not hasattr(collapseExtentOne, '__len__'):
-        collapseExtentOne = (collapseExtentOne, collapseExtentOne, collapseExtentOne)
+def cell_interval_to_slice(cell_interval, collapse_extent_one=True):
+    if not hasattr(collapse_extent_one, '__len__'):
+        collapse_extent_one = (collapse_extent_one, collapse_extent_one, collapse_extent_one)
 
     slices = []
-    for i, collapseInfo in enumerate(collapseExtentOne):
-        if collapseInfo and cellInterval.min[i] == cellInterval.max[i]:
-            slices.append(cellInterval.min[i])
+    for i, collapseInfo in enumerate(collapse_extent_one):
+        if collapseInfo and cell_interval.min[i] == cell_interval.max[i]:
+            slices.append(cell_interval.min[i])
         else:
-            slices.append(slice(cellInterval.min[i], cellInterval.max[i] + 1, None))
+            slices.append(slice(cell_interval.min[i], cell_interval.max[i] + 1, None))
     return tuple(slices)
 
 
-def extend(coreModule):
-    coreModule.makeSlice = SliceMaker()
-    coreModule.normalizeSlice = normalizeSlice
-    coreModule.CellInterval.fromSlice = staticmethod(sliceToCellInterval)
-    coreModule.CellInterval.toSlice = cellIntervalToSlice
+def extend(core_module):
+    core_module.makeSlice = SliceMaker()
+    core_module.normalizeSlice = normalize_slice
+    core_module.CellInterval.fromSlice = staticmethod(slice_to_cell_interval)
+    core_module.CellInterval.toSlice = cell_interval_to_slice
diff --git a/python/waLBerla/cuda_extension.py b/python/waLBerla/cuda_extension.py
index 0cbb96163ecba2ba05dfd2575228ef30089e99f5..c7e74a528fff3605d05096908fd679afd74595e9 100644
--- a/python/waLBerla/cuda_extension.py
+++ b/python/waLBerla/cuda_extension.py
@@ -1,26 +1,26 @@
 from pycuda.gpuarray import GPUArray
 import numpy as np
-from .field_extension import normalizeGhostlayerInfo
+from .field_extension import normalize_ghostlayer_info
 
 
-def toGpuArray(f, withGhostLayers=True):
+def to_gpu_array(f, with_ghost_layers=True):
     """Converts a waLBerla GPUField to a pycuda GPUArray"""
     if not f:
         return None
     dtype = np.dtype(f.dtypeStr)
     strides = [dtype.itemsize * a for a in f.strides]
     res = GPUArray(f.sizeWithGhostLayers, dtype, gpudata=f.ptr, strides=strides)
-    if withGhostLayers is True:
+    if with_ghost_layers is True:
         return res
 
-    ghostLayers = normalizeGhostlayerInfo(f, withGhostLayers)
-    glCutoff = [f.nrOfGhostLayers - gl for gl in ghostLayers]
-    res = res[glCutoff[0]:-glCutoff[0] if glCutoff[0] > 0 else None,
-              glCutoff[1]:-glCutoff[1] if glCutoff[1] > 0 else None,
-              glCutoff[2]:-glCutoff[2] if glCutoff[2] > 0 else None,
+    ghost_layers = normalize_ghostlayer_info(f, with_ghost_layers)
+    cutoff = [f.nrOfGhostLayers - gl for gl in ghost_layers]
+    res = res[cutoff[0]:-cutoff[0] if cutoff[0] > 0 else None,
+              cutoff[1]:-cutoff[1] if cutoff[1] > 0 else None,
+              cutoff[2]:-cutoff[2] if cutoff[2] > 0 else None,
               :]
     return res
 
 
-def extend(cppCudaModule):
-    cppCudaModule.toGpuArray = toGpuArray
+def extend(cpp_cuda_module):
+    cpp_cuda_module.toGpuArray = to_gpu_array
diff --git a/python/waLBerla/field_extension.py b/python/waLBerla/field_extension.py
index 7e08aa730731f52da8eb35d9e445136a97b69f58..61a14f75103dec299763b3658a81ec3ccdce9b5e 100644
--- a/python/waLBerla/field_extension.py
+++ b/python/waLBerla/field_extension.py
@@ -8,11 +8,15 @@ import numpy
 
 # ----------------------------- Python functions to extend the C++ field module ---------------------------------
 
-def normalizeGhostlayerInfo(field, withGhostLayers):
+def normalize_ghostlayer_info(field, with_ghost_layers):
     """Takes one ghost layer parameter and returns an integer:
-        True -> all ghost layers, False->no ghost layers"""
+        True -> all ghost layers, False->no ghost layers
+        Args:
+            field: waLberl field object
+            with_ghost_layers: see numpy_array_from_walberla_field
+    """
 
-    def normalizeComponent(gl):
+    def normalize_component(gl):
         if gl is False:
             return 0
         if gl is True:
@@ -21,17 +25,17 @@ def normalizeGhostlayerInfo(field, withGhostLayers):
             raise ValueError("Field only has %d ghost layers (requested %d)" % (field.nrOfGhostLayers, gl))
         return gl
 
-    if hasattr(withGhostLayers, "__len__") and len(withGhostLayers) == 3:
-        ghostLayers = [normalizeComponent(gl) for gl in withGhostLayers]
+    if hasattr(with_ghost_layers, "__len__") and len(with_ghost_layers) == 3:
+        ghost_layers = [normalize_component(gl) for gl in with_ghost_layers]
     else:
-        ghostLayers = [normalizeComponent(withGhostLayers)] * 3
-    return ghostLayers
+        ghost_layers = [normalize_component(with_ghost_layers)] * 3
+    return ghost_layers
 
 
-def npArrayFromWaLBerlaField(field, withGhostLayers=False):
+def numpy_array_from_walberla_field(field, with_ghost_layers=False):
     """ Creates a numpy array view on the waLBerla field data
         @field: the waLBerla field
-        @withGhostLayers: Possible values:
+        @with_ghost_layers: Possible values:
                             1. Boolean: False: no ghost layers included
                                         True:  all ghost layers included
                             2. Integer: number of ghost layers to include
@@ -41,55 +45,58 @@ def npArrayFromWaLBerlaField(field, withGhostLayers=False):
     if not field:
         return None
 
-    ghostLayers = normalizeGhostlayerInfo(field, withGhostLayers)
-
-    if not hasattr(field, 'buffer'):  # Field adaptor -> create field with adapted values
-        field = field.copyToField()
+    if hasattr(field, 'nrOfGhostLayers'):
+        field_gl = field.nrOfGhostLayers
+    else:
+        field_gl = 0
+    ghost_layers = normalize_ghostlayer_info(field, with_ghost_layers)
 
-    if ghostLayers[0] == 0 and ghostLayers[1] == 0 and ghostLayers[2] == 0:
-        return numpy.asarray(field.buffer(False))
+    if ghost_layers[0] == field_gl and ghost_layers[1] == field_gl and ghost_layers[2] == field_gl:
+        return numpy.asarray(field)
     else:
-        result = numpy.asarray(field.buffer(True))
-        glCutoff = [field.nrOfGhostLayers - gl for gl in ghostLayers]
-        view = result[glCutoff[0]:-glCutoff[0] if glCutoff[0] > 0 else None,
-                      glCutoff[1]:-glCutoff[1] if glCutoff[1] > 0 else None,
-                      glCutoff[2]:-glCutoff[2] if glCutoff[2] > 0 else None,
-                      :]
+        result = numpy.asarray(field)
+        cutoff = [abs(gl - field.nrOfGhostLayers) for gl in ghost_layers]
+        if len(result.shape) == 4:
+            view = result[cutoff[0]:-cutoff[0] if cutoff[0] > 0 else None,
+                          cutoff[1]:-cutoff[1] if cutoff[1] > 0 else None,
+                          cutoff[2]:-cutoff[2] if cutoff[2] > 0 else None,
+                          :]
+        else:
+            view = result[cutoff[0]:-cutoff[0] if cutoff[0] > 0 else None,
+                          cutoff[1]:-cutoff[1] if cutoff[1] > 0 else None,
+                          cutoff[2]:-cutoff[2] if cutoff[2] > 0 else None]
         return view
 
 
-def arrayFromWaLBerlaAdaptor(field, withGhostLayers=False):
-    return npArrayFromWaLBerlaField(field.copyToField(), withGhostLayers)
-
-
-def copyArrayToField(dstField, srcArray, slice=[slice(None, None, None)] * 3, withGhostLayers=False):
+def copy_array_to_field(dst_field, src_array, idx=None, with_ghost_layers=False):
     """ Copies a numpy array into (part of) a waLBerla field
 
     Usually no copying has to take place between waLBerla fields and numpy arrays, since an array view can be
     constructed on a field that uses the same memory.
     When running certain numpy operations that cannot be done in-place,however, the data has to be copied back.
 
-    @param dstField: waLBerla field, where the data is copied to
-    @param srcArray: numpy array where to copy from
-    @param slice:    the numpy array is allowed to be smaller than the field. In this case the target region
+    @param dst_field: waLBerla field, where the data is copied to
+    @param src_array: numpy array where to copy from
+    @param idx:      the numpy array is allowed to be smaller than the field. In this case the target region
                      has to be specified via this 3 dimensional slice
-    @param withGhostLayers: if true the ghost layers of the field are considered as well
+    @param with_ghost_layers: if true the ghost layers of the field are considered as well
     """
-    dstAsArray = npArrayFromWaLBerlaField(dstField, withGhostLayers)
-    numpy.copyto(dstAsArray[slice], srcArray)
+    if idx is None:
+        idx = [slice(None, None, None)] * 3
+    dst_as_array = numpy_array_from_walberla_field(dst_field, with_ghost_layers)
+    numpy.copyto(dst_as_array[idx], src_array)
 
 
-def extend(cppFieldModule):
-    def gatherField(blocks, blockDataName, sliceObj, allGather=False):
-        field = cppFieldModule.gather(blocks, blockDataName, sliceObj, targetRank=-1 if allGather else 0)
+def extend(cpp_field_module):
+    def gather_field(blocks, block_data_name, slice_obj, all_gather=False):
+        field = cpp_field_module.gather(blocks, block_data_name, slice_obj, targetRank=-1 if all_gather else 0)
         if field is not None:
-            field = npArrayFromWaLBerlaField(field)
+            field = numpy_array_from_walberla_field(field)
             field.flags.writeable = False
             return field
         else:
             return None
 
-    cppFieldModule.toArray = npArrayFromWaLBerlaField
-    cppFieldModule.adaptorToArray = arrayFromWaLBerlaAdaptor
-    cppFieldModule.copyArrayToField = copyArrayToField
-    cppFieldModule.gatherField = gatherField
+    cpp_field_module.toArray = numpy_array_from_walberla_field
+    cpp_field_module.copyArrayToField = copy_array_to_field
+    cpp_field_module.gatherField = gather_field
diff --git a/python/waLBerla/geometry_setup.py b/python/waLBerla/geometry_setup.py
deleted file mode 100644
index 32947a35b959fa298aa85ded898054d4375c2565..0000000000000000000000000000000000000000
--- a/python/waLBerla/geometry_setup.py
+++ /dev/null
@@ -1,148 +0,0 @@
-import numpy as np
-import scipy
-import scipy.ndimage
-
-try:
-    from .walberla_cpp import CellInterval, field
-except ImportError:
-    from walberla_cpp import CellInterval, field
-
-from .core_extension import normalizeSlice
-
-
-def setBoundaryFromArray(blocks, boundaryID, targetSlice, imageArr, boundaryConfig,
-                         resizeFunc=None, extrusionCoordinate=-1):
-    """Initializes Boundary Handling using an image
-    :param blocks: the block storage
-    :param boundaryID: block data name of boundary handling
-    :param targetSlice: slice of the domain where the image should be placed. If a 2D slice is given, the image is
-                        automatically extruded along the third coordinate. For 3D (or 1D) slices an extrusionCoordinate
-                        has to be given.
-                        Example: for targetSlice=[0.25:0.75,  0  , 0.25:0.75] the image is resized to half
-                        the x-z domain size, placed in the middle of the domain and extruded in y direction.
-    :param imageArr:    a 2D array used to set up the boundaries
-    :param boundaryConfig: dictionary mapping values of imageArr to boundary configurations,
-                           used as index array in forceBoundary()
-    :param resizeFunc: if the given slice does not match the shape of imageArr, the image array has to be resized.
-                       This can not be done automatically since interpolation would change the values, and the mapping
-                       given in boundaryConfig is incorrect. Thus the resize function has to be supplied by the caller.
-                       Here a function "resize(imgArr, newSize)" has to be passed, that returns a resized array of
-                       shape "newSize".
-    :param extrusionCoordinate: only necessary if 3D or 1D slices are given for targetSlice.
-                                See documentation of targetSlice.
-    """
-
-    if len(blocks) == 0:
-        return
-
-    nrOfGhostLayers = blocks[0][boundaryID].getFlagField().nrOfGhostLayers
-
-    imageArr = np.rot90(imageArr, 3)
-
-    sliceWithGhostLayers = 'g' in targetSlice
-    targetSlice = [s for s in targetSlice if s != 'g']
-    size = [s + 2 * nrOfGhostLayers if sliceWithGhostLayers else s for s in blocks.getDomainCellBB().size]
-    imageCellInterval = CellInterval.fromSlice(normalizeSlice(targetSlice, size))
-    if sliceWithGhostLayers:
-        imageCellInterval.shift(-nrOfGhostLayers, -nrOfGhostLayers, -nrOfGhostLayers)
-
-    # Automatic detection of extrusion coordinate
-    if extrusionCoordinate < 0 or extrusionCoordinate > 2:
-        possibleExtrusionCoordinate = np.array([0, 0, 0])
-        for i in range(3):
-            if imageCellInterval.min[i] == imageCellInterval.max[i]:
-                possibleExtrusionCoordinate[i] = 1
-                extrusionCoordinate = i
-        if sum(possibleExtrusionCoordinate) != 1:
-            raise ValueError("No valid extrusionCoordinate given - "
-                             "and extrusion coordinate could not be found automatically")
-    assert (extrusionCoordinate < 3 and extrusionCoordinate >= 0)
-
-    # Resize image
-    imageBounds = list(imageCellInterval.size)
-    del imageBounds[extrusionCoordinate]
-    if imageArr.shape != tuple(imageBounds):
-        if resizeFunc is None:
-            raise ValueError("The given image size does not match the target slice: "
-                             "resizing would be necessary but no resizeFunc was given.")
-        imageArr = resizeFunc(imageArr, imageBounds)
-
-    assert imageArr.shape == tuple(imageBounds)
-    assert imageArr.dtype.kind == 'i', "imageArr has to be of integer type"
-
-    unusedIdx = 0
-    while unusedIdx in boundaryConfig:
-        unusedIdx += 1
-
-    def make2Dfrom3DSlice(targetSlice, extrusionCoordinate):
-        l = list(targetSlice)
-        del l[extrusionCoordinate]
-        return l
-
-    for block in blocks:
-        blockCellInterval = blocks.getBlockCellBB(block)
-        blockCellInterval.expand(nrOfGhostLayers)
-        intersectionGlobalCoord = blockCellInterval.getIntersection(imageCellInterval)
-
-        if intersectionGlobalCoord.empty():
-            continue
-        intersectionLocalCoord = blocks.transformGlobalToLocal(block, intersectionGlobalCoord)
-
-        # Create a field with same size as block
-        blockCellBB = blocks.getBlockCellBB(block)
-        wlbIndexField = field.createField(list(blockCellBB.size), np.int32, ghostLayers=nrOfGhostLayers)
-        indexField = field.toArray(wlbIndexField, withGhostLayers=nrOfGhostLayers)[:, :, :, :]
-        indexField[:, :, :, :] = unusedIdx
-
-        # Copy image into this field
-        targetSlice = intersectionLocalCoord.getShifted(nrOfGhostLayers, nrOfGhostLayers, nrOfGhostLayers).toSlice()
-
-        minCoord = np.array(imageCellInterval.min)
-        imgTargetSlice = intersectionGlobalCoord.getShifted(*(-minCoord)).toSlice()
-        sliceInImage = make2Dfrom3DSlice(imgTargetSlice, extrusionCoordinate)
-        indexField[targetSlice + [0]] = imageArr[sliceInImage]
-
-        block[boundaryID].forceBoundary(wlbIndexField, boundaryConfig)
-
-
-def binaryResize(img, newSize):
-    """This can be used as resize function for setBoundaryFromArray for arrays with
-       zero and ones. After resizing the image is again binarized"""
-    img = scipy.misc.imresize(img, size=newSize)
-    img[img <= 254] = 0
-    img[img > 254] = 1
-    img = img.astype(np.int32)
-    return img
-
-
-def setBoundaryFromBlackAndWhiteImage(blocks, boundaryID, targetSlice, imagePath, boundaryConfig,
-                                      extrusionCoordinate=-1):
-    """Loads array from image file and calls setBoundaryFromArray.
-
-     :param imagePath: path to image file.
-
-     For the other parameters see documentation of setBoundaryFromArray.
-    """
-    imgArr = scipy.ndimage.imread(imagePath, flatten=True).astype(int)
-    setBoundaryFromArray(blocks, boundaryID, targetSlice, imgArr, {0: boundaryConfig},
-                         resizeFunc=binaryResize, extrusionCoordinate=extrusionCoordinate)
-
-
-def setFieldUsingFlagMask(blocks, targetField, targetValue, flagField, flagNames):
-    """
-    Sets all values of a target field to given value where a certain flag is set.
-
-    :param blocks:  the block structure
-    :param targetField: the field that is modified
-    :param targetValue: value that is written to all entries of cells where the given flag is set
-    :param flagField: the flag field
-    :param flagNames: list of flag names. If one of the flags is set, the target value is written
-    """
-    for b in blocks:
-        mask = 0
-        for flagName in flagNames:
-            mask |= b[flagField].flag(flagName)
-
-        targetArr = field.toArray(b[targetField], True)
-        flagArr = field.toArray(b[flagField], True)[:, :, :, 0]
-        targetArr[np.bitwise_and(flagArr, mask) > 0, :] = targetValue
diff --git a/python/waLBerla/plot.py b/python/waLBerla/plot.py
index e4b007455f06f731afc766918bdac3d8e12aea50..a492823aa032c5402d2a14f4d6239d7ad894f351 100644
--- a/python/waLBerla/plot.py
+++ b/python/waLBerla/plot.py
@@ -11,128 +11,147 @@ except ImportError:
 from matplotlib.pyplot import imshow, gcf, figure, plot, quiver
 
 
-def fieldShow(npField, **kwargs):
-    npField = np.rot90(npField, 3)
-    imshow(npField, origin='lower', **kwargs)
+def field_show(numpy_field, **kwargs):
+    numpy_field = np.rot90(numpy_field, 3)
+    imshow(numpy_field, origin='lower', **kwargs)
 
 
-def scalarField(blocks, name, sliceDef, fCoord=0, targetRank=0, **kwargs):
+def scalar_field(blocks, name, slice_definition, f_coordinate=0, target_rank=0, **kwargs):
     """Plots a 2D slice through the global domain as an image
 
-    :param blocks:      the blockstorage
-    :param name:        Name of the block data to be plotted. Has to be a scalar field
-    :param sliceDef:    a two dimensional slice through the domain. Can be created with waLBerla.makeSlice
-    :param fCoord:      value of the forth field coordinate (f)
-    :param targetRank:  rank that gathers and plots the data
-    :param kwargs:      further keyword arguments are passed to matplotlib.pyplot.imshow
+    :param blocks:              the blockforest
+    :param name:                Name of the block data to be plotted. Has to be a scalar field
+    :param slice_definition:    a two dimensional slice through the domain. Can be created with waLBerla.makeSlice
+    :param f_coordinate:        value of the forth field coordinate (f)
+    :param target_rank:         rank that gathers and plots the data
+    :param kwargs:              further keyword arguments are passed to matplotlib.pyplot.imshow
     """
-    f = walberla_cpp.field.gather(blocks, name, sliceDef, targetRank=targetRank)
+    f = walberla_cpp.field.gather(blocks, name, slice_definition, targetRank=target_rank)
     if f:
-        npField = np.asarray(f.buffer())[:, :, :, fCoord].squeeze()
-        npField = np.swapaxes(npField, 0, 1)
-        imshow(npField, origin='lower', **kwargs)
+        numpy_field = np.asarray(f).squeeze()
+        numpy_field = np.swapaxes(numpy_field, 0, 1)
+        imshow(numpy_field, origin='lower', **kwargs)
 
 
-def scalarFieldAnimation(blocks, name, sliceDef, runFunction, plotSetupFunction=lambda: None,
-                         plotUpdateFunction=lambda: None, fCoord=0, targetRank=0, interval=30, frames=180, **kwargs):
+def scalar_field_animation(blocks, name, slice_definition, run_function, plot_setup_function=lambda: None,
+                           plot_update_function=lambda: None, f_coordinate=0, target_rank=0,
+                           interval=30, frames=180, **kwargs):
     """Creates animation of 2D slices through the global domain
 
-    :param runFunction:        function without arguments which is run between frames (should move simulation forward)
-    :param plotSetupFunction:  function without arguments that is called after the plot was initially created.
-                               Can be used to configure plot (set title etc.)
-    :param plotUpdateFunction: function without arguments that is called when figure is updated
-    :param interval:           passed to matplotlib.animation.FuncAnimation: milliseconds between two frames
-    :param frames:             passed to :class:`matplotlib.animation.FuncAnimation` number of frames
+    :param blocks:               the blockforest
+    :param name:                 Name of the block data to be plotted. Has to be a scalar field
+    :param slice_definition:     a two dimensional slice through the domain. Can be created with waLBerla.makeSlice
+    :param run_function:         function without arguments which is run between frames (should move simulation forward)
+    :param plot_setup_function:  function without arguments that is called after the plot was initially created.
+                                 Can be used to configure plot (set title etc.)
+    :param plot_update_function: function without arguments that is called when figure is updated
+    :param f_coordinate:         value of the forth field coordinate (f)
+    :param target_rank:          rank that gathers and plots the data
+    :param interval:             passed to matplotlib.animation.FuncAnimation: milliseconds between two frames
+    :param frames:               passed to :class:`matplotlib.animation.FuncAnimation` number of frames
 
     for other params see :func:`scalarField`
     """
     fig = gcf()
-    f = walberla_cpp.field.gather(blocks, name, sliceDef, targetRank=targetRank)
+    f = walberla_cpp.field.gather(blocks, name, slice_definition, targetRank=target_rank)
     im = None
     if f:
-        npField = np.asarray(f.buffer())[:, :, :, fCoord].squeeze()
-        npField = np.swapaxes(npField, 0, 1)
-        im = imshow(npField, origin='lower', **kwargs)
-        plotSetupFunction()
+        numpy_field = np.asarray(f).squeeze()
+        numpy_field = np.swapaxes(numpy_field, 0, 1)
+        im = imshow(numpy_field, origin='lower', **kwargs)
+        plot_setup_function()
 
     def updatefig(*args):
-        runFunction()
-        f = walberla_cpp.field.gather(blocks, name, sliceDef, targetRank=targetRank)
-        if f:
-            npField = np.swapaxes(np.asarray(f.buffer()), 0, 1)
-            npField = npField[:, :, :, fCoord].squeeze()
-            im.set_array(npField)
-            plotUpdateFunction()
+        run_function()
+        gathered_field = walberla_cpp.field.gather(blocks, name, slice_definition, targetRank=target_rank)
+        if gathered_field:
+            n_field = np.swapaxes(np.asarray(gathered_field), 0, 1)
+            if n_field.shape == 4:
+                n_field = n_field[:, :, :, f_coordinate].squeeze()
+            else:
+                n_field = n_field.squeeze()
+            im.set_array(n_field)
+            plot_update_function()
             return im,
 
     return animation.FuncAnimation(fig, updatefig, interval=interval, frames=frames)
 
 
-def vectorField(blocks, name, sliceDef, xComponent=0, yComponent=1, targetRank=0, xStep=1, yStep=1, **kwargs):
+def vector_field(blocks, name, slice_definition, x_component=0, y_component=1, target_rank=0,
+                 x_step=1, y_step=1, **kwargs):
     """Plots a vector field slice using matplotlib quiver
 
-    :param blocks:     the blockstorage
-    :param name:       Name of the block data to be plotted. Has to be a scalar field
-    :param sliceDef:   a two dimensional slice through the domain. Can be created with waLBerla.makeSlice
-    :param xComponent: which component of the vector field (0,1 or 2)
-                       to take as the horizontal value for the quiver arrows
-    :param yComponent: which component of the vector field (0,1 or 2)
-                       to take as the vertical value for the quiver arrows
-    :param xStep:      take only every xStep's cell/arrow in x direction
-    :param yStep:      take only every yStep's cell/arrow in y direction
-    :param targetRank: rank that gathers and plots the data
-    :param kwargs:     further keyword arguments are passed to matplotlib.pyplot.quiver
+    :param blocks:           the blockforest
+    :param name:             Name of the block data to be plotted. Has to be a scalar field
+    :param slice_definition: a two dimensional slice through the domain. Can be created with waLBerla.makeSlice
+    :param x_component:      which component of the vector field (0,1 or 2)
+                             to take as the horizontal value for the quiver arrows
+    :param y_component:      which component of the vector field (0,1 or 2)
+                             to take as the vertical value for the quiver arrows
+    :param x_step:           take only every xStep's cell/arrow in x direction
+    :param y_step:           take only every yStep's cell/arrow in y direction
+    :param target_rank:      rank that gathers and plots the data
+    :param kwargs:           further keyword arguments are passed to matplotlib.pyplot.quiver
     """
-    f = walberla_cpp.field.gather(blocks, name, sliceDef, targetRank=targetRank)
+    f = walberla_cpp.field.gather(blocks, name, slice_definition, targetRank=target_rank)
     if f:
-        npField = np.swapaxes(np.asarray(f.buffer()), 0, 1)
-        xVel = npField[::xStep, ::yStep, :, xComponent].squeeze()
-        yVel = npField[::xStep, ::yStep, :, yComponent].squeeze()
-        quiver(xVel, yVel, **kwargs)
+        numpy_field = np.swapaxes(np.asarray(f), 0, 1)
+        x_vel = numpy_field[::x_step, ::y_step, :, x_component].squeeze()
+        y_vel = numpy_field[::x_step, ::y_step, :, y_component].squeeze()
+        quiver(x_vel, y_vel, **kwargs)
 
 
-def alongLine(blocks, name, sliceDef, fCoord=0, targetRank=0, **kwargs):
+def along_line(blocks, name, slice_definition, f_coordinate=0, target_rank=0, **kwargs):
     """Plot a field value along a one dimensional slice through the domain
 
-    :param blocks:      the blockstorage
-    :param name:        Name of the block data to be plotted. Has to be a scalar field
-    :param sliceDef:    a one dimensional slice through the domain. Can be created with :func:`waLBerla.makeSlice`
-    :param fCoord:      value of the forth field coordinate (f)
-    :param targetRank:  rank that gathers and plots the data
-    :param kwargs:      further keyword arguments are passed to :func:`matplotlib.pyplot.plot`
+    :param blocks:           the blockstorage
+    :param name:             Name of the block data to be plotted. Has to be a scalar field
+    :param slice_definition: a one dimensional slice through the domain. Can be created with :func:`waLBerla.makeSlice`
+    :param f_coordinate:     value of the forth field coordinate (f)
+    :param target_rank:      rank that gathers and plots the data
+    :param kwargs:           further keyword arguments are passed to :func:`matplotlib.pyplot.plot`
     """
-    f = walberla_cpp.field.gather(blocks, name, sliceDef, targetRank=targetRank)
+    f = walberla_cpp.field.gather(blocks, name, slice_definition, targetRank=target_rank)
     if f:
-        npField = np.asarray(f.buffer())
-        npField = npField[:, :, :, fCoord].squeeze()
+        npField = np.asarray(f)
+        npField = npField[:, :, :, f_coordinate].squeeze()
         plot(npField, **kwargs)
 
 
-def alongLineAnimation(blocks, name, sliceDef, runFunction, plotSetupFunction=lambda: None, fCoord=0, targetRank=0,
-                       interval=30, frames=180, **kwargs):
+def along_line_animation(blocks, name, slice_definition, run_function, plot_setup_function=lambda: None,
+                         f_coordinate=0, target_rank=0, interval=30, frames=180, **kwargs):
     """Animated version of :func:`alongLine`
 
-    For parameter documentation see :func:`scalarFieldAnimation` and :func:`alongLine`
+    :param blocks:              the blockforest
+    :param name:                Name of the block data to be plotted. Has to be a scalar field
+    :param slice_definition:    a two dimensional slice through the domain. Can be created with waLBerla.makeSlice
+    :param run_function:        function without arguments which is run between frames (should move simulation forward)
+    :param plot_setup_function: function without arguments that is called after the plot was initially created.
+                                Can be used to configure plot (set title etc.)
+    :param f_coordinate:        value of the forth field coordinate (f)
+    :param target_rank:         rank that gathers and plots the data
+    :param interval:            passed to matplotlib.animation.FuncAnimation: milliseconds between two frames
+    :param frames:              passed to :class:`matplotlib.animation.FuncAnimation` number of frames
 
     """
     fig = figure()
 
-    f = walberla_cpp.field.gather(blocks, name, sliceDef, targetRank=targetRank)
+    f = walberla_cpp.field.gather(blocks, name, slice_definition, targetRank=target_rank)
     line = None
     if f:
-        npField = np.asarray(f.buffer())
-        npField = npField[:, :, :, fCoord].squeeze()
-        line, = plot(npField, **kwargs)
-        plotSetupFunction()
+        numpy_field = np.asarray(f)
+        numpy_field = numpy_field[:, :, :, f_coordinate].squeeze()
+        line, = plot(numpy_field, **kwargs)
+        plot_setup_function()
 
     def updatefig(*args):
-        runFunction()
-        f = walberla_cpp.field.gather(blocks, name, sliceDef, targetRank=targetRank)
-        if f:
-            npField = np.asarray(f.buffer())
-            npField = npField[:, :, :, fCoord].squeeze()
-            fig.gca().set_ylim((np.min(npField), np.max(npField)))
-            line.set_ydata(npField)
+        run_function()
+        gathered_array = walberla_cpp.field.gather(blocks, name, slice_definition, targetRank=target_rank)
+        if gathered_array:
+            n_field = np.asarray(gathered_array)
+            n_field = n_field[:, :, :, f_coordinate].squeeze()
+            fig.gca().set_ylim((np.min(n_field), np.max(n_field)))
+            line.set_ydata(n_field)
             return line,
 
     return animation.FuncAnimation(fig, updatefig, interval=interval, frames=frames, blit=False)
diff --git a/python/waLBerla/timeloop_extension.py b/python/waLBerla/timeloop_extension.py
deleted file mode 100644
index 31b859ad15c31c7c91805480efb2986805e9e3fa..0000000000000000000000000000000000000000
--- a/python/waLBerla/timeloop_extension.py
+++ /dev/null
@@ -1,69 +0,0 @@
-try:
-    from . import walberla_cpp
-except ImportError:
-    import walberla_cpp
-
-
-def functorFromSweep(blocks, sweep):
-    """Makes a functor from a sweep, by iterating over all the blocks in the provided block storage"""
-
-    def functor():
-        for b in blocks:
-            sweep(b)
-
-    return functor
-
-
-class Timeloop(walberla_cpp.timeloop.ITimeloop):
-    def __init__(self, nrOfTimesteps):
-        super().__init__()
-        self._nrOfTimesteps = nrOfTimesteps
-        self._timestep = 0
-        self._functors = []
-        self._stopFlag = False
-
-    def run(self, timesteps=None):
-        if not timesteps:
-            timesteps = self._nrOfTimesteps
-        for t in range(timesteps):
-            self.singleStep()
-            if self._stopFlag:
-                break
-
-    def singleStep(self):
-        for func in self._functors:
-            func()
-        self._timestep += 1
-
-    def stop(self):
-        self._stopFlag = True
-
-    def synchronizedStop(self, stop=True):
-        # syncStop = wlb.mpi.allreduceInt(int(stop), wlb.mpi.LOGICAL_OR)
-        self._stopFlag = True
-
-    def setCurrentTimeStep(self, ts):
-        self._timestep = ts
-
-    def getCurrentTimeStep(self):
-        return self._timestep
-
-    def getNrOfTimeSteps(self):
-        return self._nrOfTimesteps
-
-    def __getFunctor(self, functor, blocks):
-        if blocks:  # assume that it is a sweep if blocks were given
-            return functorFromSweep(blocks, functor)
-        else:
-            return functor
-
-    def add(self, functor, blocks=None):
-        self._functors.append(self.__getFunctor(functor, blocks))
-        return len(self._functors) - 1
-
-    def replace(self, handle, functor, blocks=None):
-        self._functors[handle] = self.__getFunctor(functor, blocks)
-
-
-def extend(cppTimeloopModule):
-    cppTimeloopModule.Timeloop = Timeloop
diff --git a/python/waLBerla/tools/sqlitedb/insert.py b/python/waLBerla/tools/sqlitedb/insert.py
index 29fa37c16ca8dcf5b5847f31aebc4558f4860fb8..2e759af079990f3ddc42a93f2918a46218e67df9 100644
--- a/python/waLBerla/tools/sqlitedb/insert.py
+++ b/python/waLBerla/tools/sqlitedb/insert.py
@@ -13,16 +13,16 @@ def sequenceValuesToScalars(data):
        This is useful when using the dictionary with storeSingle(), since each entry gets a separate column
        after the sequences have been separated.
      """
-    keysToDelete = []
-    newValues = {}
+    keys_to_delete = []
+    new_values = {}
     for key, value in data.items():
         if type(value) in [list, tuple]:
-            keysToDelete.append(key)
+            keys_to_delete.append(key)
             for i in range(len(value)):
-                newValues["%s_%d" % (key, i)] = value[i]
-    for k in keysToDelete:
+                new_values["%s_%d" % (key, i)] = value[i]
+    for k in keys_to_delete:
         del data[k]
-    data.update(newValues)
+    data.update(new_values)
 
 
 def storeSingle(data, tableName, dbFile="database.sqlite", runId=None):
@@ -40,9 +40,9 @@ def storeSingle(data, tableName, dbFile="database.sqlite", runId=None):
     if runId:
         data['runId'] = runId
 
-    keyString = ",".join(data.keys())
-    valueString = ",".join(["?" for e in data.values()])
-    query = "INSERT INTO %s ( %s ) VALUES ( %s )" % (tableName, keyString, valueString)
+    key_string = ",".join(data.keys())
+    value_string = ",".join(["?" for e in data.values()])
+    query = "INSERT INTO %s ( %s ) VALUES ( %s )" % (tableName, key_string, value_string)
 
     conn = sqlite3.connect(dbFile)
     c = conn.cursor()
@@ -76,9 +76,9 @@ def storeMultiple(data, tableName, dbFile="database.sqlite", runId=None):
     if runId is not None:
         data.update({'runId': [runId] * list_length})
 
-    keyString = ",".join(data.keys())
-    valueString = ",".join(["?" for e in data.values()])
-    query = "INSERT INTO %s ( %s ) VALUES ( %s )" % (tableName, keyString, valueString)
+    key_string = ",".join(data.keys())
+    value_string = ",".join(["?" for e in data.values()])
+    query = "INSERT INTO %s ( %s ) VALUES ( %s )" % (tableName, key_string, value_string)
 
     conn = sqlite3.connect(dbFile)
     c = conn.cursor()
@@ -93,32 +93,37 @@ def storeMultiple(data, tableName, dbFile="database.sqlite", runId=None):
     return lastrowid
 
 
-def checkAndUpdateSchema(data, tableName, dbFile="database.sqlite", referenceRuns=False):
+def checkAndUpdateSchema(data, tableName, dbFile="database.sqlite", referenceRuns=False, alter_table=False):
     """Alters a sqlite table in order to match the given data:
 
         * if table with given name does not exist yet, it is created
         * keys in the data dictionary correspond to columns
-        * columns are added if necessary, existing data has NULL in these new columns
+        * columns are added if necessary (only when alter_table is set to True),
+        * existing data has NULL in these new columns
 
     :param data:          see :func:`storeSingle` or :func:`storeMultiple`
+    :param tableName      name of the table which should be updated
+    :param dbFile         name of the sql file which should be written
     :param referenceRuns: if False the table gets an autoincrementing integer column 'runId'
                           if True, a normal column runId is created that points into
                           another table with runId as primary key
+    :param alter_table    If True the columns of the table will be altered.
+                          Should be called if new columns should be inserted.
      """
 
     def pythonToSqlType(python_type):
         if python_type is int:
-            return ("INTEGER")
+            return "INTEGER"
         elif python_type is bool:
-            return ("INTEGER")
+            return "INTEGER"
         elif python_type is float:
-            return ("DOUBLE")
+            return "DOUBLE"
         elif python_type is str:
-            return ("TEXT")
+            return "TEXT"
         elif python_type is list:
-            return (pythonToSqlType(type(value[0])))
+            return pythonToSqlType(type(value[0]))
         elif python_type is tuple:
-            return (pythonToSqlType(type(value[0])))
+            return pythonToSqlType(type(value[0]))
 
     if referenceRuns:
         names = ["runId"]
@@ -131,21 +136,22 @@ def checkAndUpdateSchema(data, tableName, dbFile="database.sqlite", referenceRun
         names.append(key)
         types.append(pythonToSqlType(type(value)))
 
-    columns = [("%s %s") % e for e in zip(names, types)]
+    columns = ["%s %s" % e for e in zip(names, types)]
 
-    createQuery = "CREATE TABLE IF NOT EXISTS %s ( %s );" % (tableName, ",".join(columns))
-    alterQueries = ["ALTER TABLE %s ADD COLUMN %s %s;" % (tableName, key, typ) for key, typ in zip(names, types)]
+    create_query = "CREATE TABLE IF NOT EXISTS %s ( %s );" % (tableName, ",".join(columns))
+    alter_queries = ["ALTER TABLE %s ADD COLUMN %s %s;" % (tableName, key, typ) for key, typ in zip(names, types)]
 
     conn = sqlite3.connect(dbFile)
     c = conn.cursor()
 
-    c.execute(createQuery)
-    for q in alterQueries:
-        try:
-            c.execute(q)
-        except sqlite3.OperationalError as e:
-            print(e)
-            pass
+    c.execute(create_query)
+    if alter_table:
+        for q in alter_queries:
+            try:
+                c.execute(q)
+            except sqlite3.OperationalError as e:
+                print(e)
+                pass
 
     conn.commit()
     conn.close()
diff --git a/python/waLBerla/tools/sqlitedb/merge.py b/python/waLBerla/tools/sqlitedb/merge.py
index 5628b1f39f6bf451586377ec80b7825ece29ffd8..b853e3272a8da88d61e44eb9e4a949d59bf1acf5 100755
--- a/python/waLBerla/tools/sqlitedb/merge.py
+++ b/python/waLBerla/tools/sqlitedb/merge.py
@@ -28,12 +28,12 @@ def mergeSqliteFiles(targetFile, fileToMerge):
     db = sqlite3.connect(targetFile)
     db.execute('ATTACH "' + fileToMerge + '" AS toMerge')
 
-    targetColumns = getColumnNames(db, "runs", "main")
-    toMergeColumns = getColumnNames(db, "runs", "toMerge")
+    target_columns = getColumnNames(db, "runs", "main")
+    to_merge_columns = getColumnNames(db, "runs", "toMerge")
 
-    columnsToCreate = [e for e in toMergeColumns if e not in targetColumns]
+    columns_to_create = [e for e in to_merge_columns if e not in target_columns]
 
-    for column in columnsToCreate:
+    for column in columns_to_create:
         print("Adding Column {} to run table of {} ".format(column[0], targetFile))
         db.execute("ALTER TABLE main.runs ADD COLUMN %s %s" % (column[0], column[1]))
 
@@ -41,12 +41,12 @@ def mergeSqliteFiles(targetFile, fileToMerge):
     # check if an entry with same date exists, if not add the run and the timing pool entries
     # to the targetTable
     c = db.cursor()
-    assert (toMergeColumns[0][0] == "runId")
-    columns = [e[0] for e in toMergeColumns]
-    columnString = ",".join(columns)
-    columnStringNoRunId = ",".join(columns[1:])
+    assert (to_merge_columns[0][0] == "runId")
+    columns = [e[0] for e in to_merge_columns]
+    column_string = ",".join(columns)
+    column_string_no_run_id = ",".join(columns[1:])
 
-    query = 'SELECT {} FROM toMerge.runs WHERE timestamp || " " || uuid NOT IN '.format(columnString, )
+    query = 'SELECT {} FROM toMerge.runs WHERE timestamp || " " || uuid NOT IN '.format(column_string, )
     query += '( SELECT timestamp || " " || uuid FROM main.runs )'
 
     # associated tables are tables that reference the runs table, having a first column of 'runId' which is a
@@ -80,7 +80,7 @@ def mergeSqliteFiles(targetFile, fileToMerge):
         # Build up insert statement for 'runs' table
         questionMarkList = ['?'] * (len(run) - 1)
         questionMarkString = ",".join(questionMarkList)
-        insertStatement = "INSERT INTO main.runs (%s) VALUES (%s);" % (columnStringNoRunId, questionMarkString)
+        insertStatement = "INSERT INTO main.runs (%s) VALUES (%s);" % (column_string_no_run_id, questionMarkString)
         # Execute the insert
         insertCursor = db.cursor()
         insertCursor.execute(insertStatement, run[1:])
diff --git a/python/waLBerla_docs/conf.py b/python/waLBerla_docs/conf.py
index e872f555a8ce3e71cde64e208cf44671225dcc94..b1e67ed6c6c97b58f901243d60b137e6f2927373 100644
--- a/python/waLBerla_docs/conf.py
+++ b/python/waLBerla_docs/conf.py
@@ -25,7 +25,7 @@ source_suffix = '.rst'
 master_doc = 'index'
 
 project = 'waLBerla'
-copyright = '2016, LSS waLBerla Team'
+copyright = '2020, LSS waLBerla Team'
 
 version = ''
 release = ''
@@ -36,7 +36,7 @@ pygments_style = 'sphinx'
 htmlhelp_basename = 'waLBerladoc'
 
 
-intersphinx_mapping = {'python': ('http://docs.python.org/3.5', None),
+intersphinx_mapping = {'python': ('http://docs.python.org/3.8', None),
                        'numpy': ('http://docs.scipy.org/doc/numpy/', None),
                        'scipy': ('http://docs.scipy.org/doc/scipy/reference/', None),
                        'matplotlib': ('http://matplotlib.sourceforge.net/', None)}
diff --git a/python/waLBerla_docs/index.rst b/python/waLBerla_docs/index.rst
index 78964d95e41cdd91a6f9df3a6d545d1b25eb6e4c..aa70a63decb26f2e932001512db7e86ee627558f 100644
--- a/python/waLBerla_docs/index.rst
+++ b/python/waLBerla_docs/index.rst
@@ -4,34 +4,23 @@ Documentation of waLBerla's Python interface
 Quick Start / Tutorials:
 ------------------------
 
-You can quickly try out waLBerla's Python interface in a hosted IPython notebook without
-having to install anything:
-
-http://demo.walberla.net
-
-This site contains interactive tutorials, illustrating how to set up a 2D lattice Boltzmann
-simulation with waLBerla.
-
-
-Installation with conda:
+This is the documentation of waLBerla`s Python coupling.
+It enables to use the core functionality of waLBerla from Python. A primary advantage of
+the Python coupling is that it allows having a view on waLBerla`s data as
+NumPy arrays. With this, it is easily possible to provide pre and postprocessing
+routines using powerful packages from Python. Furthermore, it is possible to set up entire
+simulations just from Python. This feature is primarily used in the code generation framework `pystencils <https://pycodegen.pages.i10git.cs.fau.de/pystencils/>`_
+within its `parallel data handling <https://pycodegen.pages.i10git.cs.fau.de/pystencils/notebooks/03_tutorial_datahandling.html>`_ which entirely builds upon waLBerla`s Python coupling.
+waLBerla`s Python bindings are built with `pybind11 <https://pybind11.readthedocs.io/en/stable/#>`_, a lightweight header-only package which is shipped as a submodule.
+Thus, there is no need to install any additional software.
+
+
+Installation:
 ------------------------
 
-To run waLBerla on your own machine the simplest way to get going is the installation via
-the `conda package manager <http://conda.pydata.org>`_::
-
-   conda install --channel lssfau walberla
-
-
-Run in docker:
---------------
-
-Docker is a lightweight virtualization solution. We provide a docker image that
-contains the same environment as hosted on http://demo.walberla.net.
-With this image you can run and develop waLBerla simulations on your own machine without having to manually
-install the dependencies. All you need is a running installation of  `Docker <www.docker.com>`_.
-Run the waLBerla image with the following command and navigate in your browser to http://localhost:8888 ::
+To install waLberla as Python package in your path run the following command in your build folder ::
 
-   docker run -it -p 8888:8888 walberla/runenv-ubuntu-python
+   make pythonModuleInstall
 
 
 API Documentation:
@@ -44,8 +33,6 @@ API Documentation:
    modules/blockforest
    modules/core
    modules/field
-   modules/geometry
-   modules/lbm
    modules/plot
    modules/tools
    
diff --git a/python/waLBerla_docs/ipython/ipython-tutorials/Tutorial 01 - Basic data structures.ipynb b/python/waLBerla_docs/ipython/ipython-tutorials/Tutorial 01 - Basic data structures.ipynb
index df203fccde788df875a7539b504fba071560c851..56093faa5f5a4e245fd13b224c8b89e6e98c0088 100644
--- a/python/waLBerla_docs/ipython/ipython-tutorials/Tutorial 01 - Basic data structures.ipynb	
+++ b/python/waLBerla_docs/ipython/ipython-tutorials/Tutorial 01 - Basic data structures.ipynb	
@@ -24,11 +24,21 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
+   "execution_count": 1,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[[0 0 0 0 0]\n",
+      " [0 0 0 0 0]\n",
+      " [0 0 0 0 0]\n",
+      " [0 0 0 0 0]\n",
+      " [0 0 0 0 0]]\n"
+     ]
+    }
+   ],
    "source": [
     "import numpy as np\n",
     "def makeGrid(shape):\n",
@@ -46,10 +56,8 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "execution_count": 2,
+   "metadata": {},
    "outputs": [],
    "source": [
     "ALIVE = 1\n",
@@ -86,11 +94,17 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
+   "execution_count": 3,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "[(-1, -1), (-1, 0), (-1, 1), (0, -1), (0, 1), (1, -1), (1, 0), (1, 1)]\n"
+     ]
+    }
+   ],
    "source": [
     "print(neighborhoodD2Q9)"
    ]
@@ -104,11 +118,34 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
+   "execution_count": 4,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Initial Setup:\n",
+      "[[0 0 0 0 0]\n",
+      " [0 0 0 0 0]\n",
+      " [0 1 1 1 0]\n",
+      " [0 0 0 0 0]\n",
+      " [0 0 0 0 0]]\n",
+      "After timestep 1: \n",
+      "[[0 0 0 0 0]\n",
+      " [0 0 1 0 0]\n",
+      " [0 0 1 0 0]\n",
+      " [0 0 1 0 0]\n",
+      " [0 0 0 0 0]]\n",
+      "After timestep 2: \n",
+      "[[0 0 0 0 0]\n",
+      " [0 0 0 0 0]\n",
+      " [0 1 1 1 0]\n",
+      " [0 0 0 0 0]\n",
+      " [0 0 0 0 0]]\n"
+     ]
+    }
+   ],
    "source": [
     "grid = makeGrid( [5,5] )\n",
     "grid[2,1:4] = ALIVE\n",
@@ -132,12 +169,24 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
+   "execution_count": 5,
    "metadata": {
-    "collapsed": false,
     "scrolled": false
    },
-   "outputs": [],
+   "outputs": [
+    {
+     "data": {
+      "image/png": "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\n",
+      "text/plain": [
+       "<Figure size 1080x864 with 1 Axes>"
+      ]
+     },
+     "metadata": {
+      "needs_background": "light"
+     },
+     "output_type": "display_data"
+    }
+   ],
    "source": [
     "from material.matplotlib_setup import *   # import matplotlib and configures it to play nicely with iPython notebook\n",
     "matplotlib.rcParams['image.cmap'] = 'Blues' # switch default colormap\n",
@@ -154,11 +203,26 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
+   "execution_count": 6,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<video controls width=\"80%\">\n",
+       " <source src=\"data:video/x-m4v;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQAAGlJtZGF0AAACcQYF//9t3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE2MCByMzAxMSBjZGU5YTkzIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAyMCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTAgcmVmPTIgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MToweDExMSBtZT1oZXggc3VibWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MSA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0tMiB0aHJlYWRzPTI0IGxvb2thaGVhZF90aHJlYWRzPTQgc2xpY2VkX3RocmVhZHM9MCBucj0wIGRlY2ltYXRlPTEgaW50ZXJsYWNlZD0wIGJsdXJheV9jb21wYXQ9MCBjb25zdHJhaW5lZF9pbnRyYT0wIGJmcmFtZXM9MCB3ZWlnaHRwPTAga2V5aW50PTI1MCBrZXlpbnRfbWluPTIgc2NlbmVjdXQ9NDAgaW50cmFfcmVmcmVzaD0wIHJjX2xvb2thaGVhZD00MCByYz1jcmYgbWJ0cmVlPTEgY3JmPTIzLjAgcWNvbXA9MC42MCBxcG1pbj0wIHFwbWF4PTY5IHFwc3RlcD00IGlwX3JhdGlvPTEuNDAgYXE9MToxLjAwAIAAAA/hZYiEBX///w9FAAE/zycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJyddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddf/4R8FoIgAMjdgACAUAAIAtTYAUERgAAgDgACACANHpYHzIq36zo0yT9Kmtra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tr/+H2CASABHf2wABANAAEAu5sAEkwy7TUENddddddddddddddddddddddddczBR2UgU0tddddddddddddddddddddddddddddddddddddddPrrrrrrrrrrrrrrrrrrrrrrrrrrp66666666666666666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp6666666666666666666666666/90hDUrBFwAIgviKY+if5AACAe8LwEkBxOwxHaS/8uv9s2VGGbFf+3/9RnkgFtzg0X1w09Y+FxdV/3ChZMDuhyQknln+BYf/9gk+QAD1BUAVblVKmGamQwbknqCOuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrvp66666666666666666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrp66666666666666666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrp66666666666666666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuv/EJwGGwRcABMTFOVhncstbnL/HBHoZITDETHEyPA8db72gCIf9UU9/ctKucufgACBYAAkzIHwJ/gDTACLTbCggt/jpk+GOcABBlIQhSkIc5CEOf/AAoD+BMEXbmpgyJThh/UO11111111111111111111111111111111PXXXXXXXT1111111111111111111111111309ddddddddddddddddddddddddddddddddddddddddPXXXXXXXXXXXXXXXXXXXXXXXXXXT111111111111111111111111111111111111111109ddddddddddddddddddddddddddPXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXT1111111111111111111111111109ddddddf//jgtBhwHAAIAYKBFMCj7Acx4GW/HAJnHOPJlra2tra2tra2tra2tra2tra2tra2tra//j4IFxeA4ABUA4LJpgCIB+CEFFVtMDO/Jgxrrrrrrrp666666666666666666666666666euuuuuuuKYNbP3QzQmQK6OA0tLS0tLS0tLS0tLS0tLS0tLS0tLS0sUwtEJy8KizVMP111111109ddddddddddddddddddddddddddPXXXXXXXMwZXqCuuuuuuuuuuuuuuuuuuuuuuunpnrrrrrrrp666666666666666666666666666euuuuuuunrrrrrrrrrrrrrrrrrrrrrrrp6euuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrr//iKM0KgggBmVM0T1e+AIwIrx64v1lFTikZUEr9943AAGgMOIQbN+djTxSn3/F+3s2Z8P/+wQf+MAAtAdYH2/Nahxq5sNQ3XXXXXXT1111111111111111111111109M9dddddddPXXXXXXXXXXXXXXXXXXXXXXXXX+QQ/4SBBAATJxDRflydFh+LRDKAAICRM5GgIOwCfiFqhGbr1Tu4YY5wcGVl9lZfUENddddddPXXXXXXXXXXXXXXXXXXXXXXXT09dddddddPXXXXXXXXXXXXXXXXXXXXXXXXXXT111111109dddddddddddddddddddddddPT111111109ddddddddddddddddddddddddddPXXXXXXXT1111111111111111111111109PXXXXXXXT1111111111111111111111111109ddddddfD/pgDDoMOAAkjEXHWmBsKW1uCByoy4Vy+XRaLXqa2Ewmtra2tra2tra2tra2tra2tra2tra2v8PH7BcTgAN0ZGxPmOjsxUZYVyzH4ZdLqYMa6666666euuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrri0adEV+8m7W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tanrrrrrrrp666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrp66666666666666666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuv//4QQggwAHww6QDN3IroPh29kG//nCEZSkOVB1J6ghrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp6666666666666666666666666/ieH/CQIIAKfEjHUZXP+BAe3YIWTX4n8qAhKv+C5pBtWX6tPLDD//+w99gXUG0Xdya2zovOtDGWaghrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrp66666666666666666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrp66666666666666666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp6666666666666666666666666///4IQ5gANAB7gBIERE4lEClpCpDT1DtdddddddddddddddddddddddddddddddddddddddPXXXXXXXXXXXXXXXXXXXXXXXXX2pAf/CQIoAFkXoR/K8RF76BHYxniI3skynCu8EYaCMrvAsW7g7xiD5wBKA9v4WcqalQn+DHOAAgylKUpSlKUpSlKABQH8CYIu3VTBkSnDD//qCOuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrp66666666666666666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp666666666666666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrwoGAf6BWCLAAQjsgrFkJrWNT2mERiSxEdU+3tbW18f7CMKfgAIG4U1uI4kBghVZh+xweAA2TNoTZXVmXxYQhDOHxwgwz59noQR/TRgKwJmQ//eNAYOi58wbDc5hAbA7g9ra2tra2tr48AjT0J8ABGJziL4kgcI61ZPGOYAAIA4AAgBgDxmW/oG0RwYIbtREXYIN/EkFVxwMFo1cmt+a2tra2tra+P7UgNDOAAgThDG73kgNEK7MPmILAIHq77v/zwNwnHTLSHAeeiKgvgQc8Ula9Stis90DST1TSSMw3ynf/w/QIDLDtbW1tbW18f+AKKCheAAkJiGIwzhZlLnBVSAAmJinOwR3LNaQQv2NfGFGp1h7DruA+AEKuQW2QiZMMKa8jf8P6I1tbW1tbXx/8YoKeAAgnGKXve8DTGdeaM9AxDd+jBFUU8KU36CwBVgCn6ogELH3/rn/UA+gnwX5EMj6cjvrPQxtm1tbXC3/9h4uAAhnK471lrv3vX/hqCGuuuuuuuuuuuuuuuuuuuuuuuuuuuuvRh//w8CDwyFYT6W1RJHPOL8AEoZawSXci3vghPNWGGoIa66666+EMP/CQIPkACUxwYD3SdzDQtHnXD9g5DAChLlI7SaQdbDDqCGuuuuuvtth/wkCCAC0NYQxT3E5//9/8DYgYcoGbJJLWpNF7//vGPRCu26omdQw1BCGuuuuuv8v/w+HgAHCFTIhtNKW6OKzQ3AIwc8CTr0G2BptbVXBhqHw111111/z/+Hw9AWwTEABVareALCMqVDv//4e/IAJgMS/PaGu4gL6hh1D9ddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeAAAC9EGaOAr4ArbigScAK5/dWIBcerfSqH4mA4AEX1JmPAAi+pM/LSfbT5aDMAAi+xMwCQG//iGC4YA4ABADBQIy3wgJSkCBZF4XZfJsILydf0EgohcQuIXELiFxC5v8eMMF0AHcxMIwhhAe7vn/8BEA+F2WHkVzJoNXcB4LelxaBXHAbOudc651zrnXHsFX/mvweHfOwV5/P5/P5/P53g8O+d8/n8/n8/n87wWcSHgHAAi+xMwcACL7EzAWCmZV6IOrx4AEX2JmEDvnYfz+fz+fz+fzvB4d875/P5/P5/P53g8O+d8/n8/n8/n87weHfO+fz+fz+fz+d4ND+fz+fz+fz+fzvnfP5/P5/P5/O+d8/n8/n8/n8/BYfz+fz+fz+fz+d4g753z+fz+fz+fz8Fh/P5/P5/P5/P53iDvnfP5/P5/P5/PwWH8/n8/n8/n8/neIO+d8/n8/n8/n8/BJxIeAcACL7EzBwAIvsTPgAI01J2nu4MQeH8/n8/n8/n8/neIO+d8/n8/n8/n8/BIdh4sUEHh/P5/P5/P5/P53iDvnfP5/P5/P5/PwWH8/n8/n8/n8/neIO+d8/n8/n8/n8/BYfz+fz+fz+fz+d4g753z+fz+fz+fz8Fh/P5/P5/P5/P53zvn8/n8/n8/nfO+fz+fz+fz+fgsPLn8/n8/n8/n8753z+fz+fz+fzvnfP5/P5/P5/PwaHfO+fz+fz+fz+d4PDvnfP5/P5/P5/O8FfMCDgIX6QdU7wgd87Dufz+fz+fz+d4PDvnfP5/P5/P5/O8Hh3zvn8/n8/n8/neDw753z+fz+fz+fzvB4d8exEyKsul3NptnXOudc651zrneDw3x/9AsDgDgAFwBwJLS3wFgEwZKX4XZfjFpFELiFxC4hcQuIXMPf/6BZAASiFFcuSCrelYYcAlhV/6eA+AmzgAIbu7u7u7u7u4AFA/glGefZHBJy4Yf/8AinLAn/Jr/hDm8Cd6S0I4rwAeSTrHW5n12Qj/6qdfANlAAAAURBmkAU8AcncCJffffffffcHl99999999weX33333333B5ffffffffcHl99999999weX33333333B5ffffffffcHl99999999waX333333333333333333333333BZgBBjPfcRffffffffcFnz33EX33333333BZ899xF99999999wWfPfcRffffffffcFnz33EX33333333BZ899xF99999999wWfPfcRffffffffcFiv777777777777777777777777gtvvvvvvvvvvvvvvvvvvvvvvvuDS++++++++4PL77777777gr8wIOCF4c+J3hC++++++++4PL77777777g8vvvvvvvvuDy++++++++4PL77777777g8vvvvvvvvvuA9/EwAHRvWSlz///hmAAEh0uTMHAAJDpcmYOAASHS5MwBb0AAAE2QZpgFPAHr333333333B5ffffffffaBc8Hl99999999weX33333333B5ffffffffcHl99999999weX33333333B5ffffffffcGl999999999999999999999999wWX33333333EX33333333BZffffffffcRffffffffcFl99999999xF99999999wWX33333333EX33333333BZffffffffcRffffffffcFl99999999xF99999999wWX33333333EX33333333BZffffffffffffffffffffffffcFt99999999999999999999999waX33333333B5ffffffffcFh2JhC++++++++4PL77777777g8vvvvvvvvuDy++++++++4PL77777777g8vvvvvvvvuAPWgAAAATxBmoAU8AevffffffffcHl99999999weX333333333B3fffffffffcHd999999999wd3333333333B3fffffffffcHd999999999wZ3333333333333333333333333BZffffffffcRffffffffcFl99999999xF99999999wWX33333333EX33333333BZffffffffcRffffffffcFl99999999xF99999999wWX33333333EX33333333BZffffffffcRffffffffcFl999999999999999999999999wW33333333333333333333333BrfffffffffcHd999999999wVneEL777777777g7vvvvvvvvvuDu+++++++++4O7777777777g7vvvvvvvvvuDu++++++++4A9aAAAABMkGaoBLwB6999999999weX33333333B5ffffffffcHl99999999weX33333333B5ffffffffcHl99999999weX33333333BpffffffffffffffffffffffffcFl99999999xF99999999wWX33333333EX33333333BZffffffffcRffffffffcFl99999999xF99999999wWX33333333EX33333333BZffffffffcRffffffffcFl99999999xF99999999wWX333333333333333333333333BbffffffffffffffffffffffcGt99999999weX33333333B5ffffffffcHl99999999weX33333333B5ffffffffcHl99999999oFTweX33333333AHrQAAAAxltb292AAAAbG12aGQAAAAAAAAAAAAAAAAAAAPoAAALuAABAAABAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACAAACQ3RyYWsAAABcdGtoZAAAAAMAAAAAAAAAAAAAAAEAAAAAAAALuAAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAEAAAAAAAAAAAAAAAAAAEAAAAAEOAAAA2AAAAAAACRlZHRzAAAAHGVsc3QAAAAAAAAAAQAAC7gAAAAAAAEAAAAAAbttZGlhAAAAIG1kaGQAAAAAAAAAAAAAAAAAAEAAAADAAFXEAAAAAAAtaGRscgAAAAAAAAAAdmlkZQAAAAAAAAAAAAAAAFZpZGVvSGFuZGxlcgAAAAFmbWluZgAAABR2bWhkAAAAAQAAAAAAAAAAAAAAJGRpbmYAAAAcZHJlZgAAAAAAAAABAAAADHVybCAAAAABAAABJnN0YmwAAACWc3RzZAAAAAAAAAABAAAAhmF2YzEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAEOANgAEgAAABIAAAAAAAAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAY//8AAAAwYXZjQwFCwB7/4QAYZ0LAHtsBEBt5eEAAAAMAQAAAAwEDxYu4AQAFaMqDyyAAAAAYc3R0cwAAAAAAAAABAAAABgAAIAAAAAAUc3RzcwAAAAAAAAABAAAAAQAAABxzdHNjAAAAAAAAAAEAAAABAAAABgAAAAEAAAAsc3RzegAAAAAAAAAAAAAABgAAEloAAAL4AAABSAAAAToAAAFAAAABNgAAABRzdGNvAAAAAAAAAAEAAAAwAAAAYnVkdGEAAABabWV0YQAAAAAAAAAhaGRscgAAAAAAAAAAbWRpcmFwcGwAAAAAAAAAAAAAAAAtaWxzdAAAACWpdG9vAAAAHWRhdGEAAAABAAAAAExhdmY1OC40NS4xMDA=\" type=\"video/mp4\">\n",
+       " Your browser does not support the video tag.\n",
+       "</video>"
+      ],
+      "text/plain": [
+       "<IPython.core.display.HTML object>"
+      ]
+     },
+     "execution_count": 6,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
     "ani = makeImshowAnimation(grid, gameOfLifeSweep, frames=6)\n",
     "displayAsHtmlVideo(ani, fps=2)"
@@ -173,14 +237,29 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
+   "execution_count": 7,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<video controls width=\"80%\">\n",
+       " <source src=\"data:video/x-m4v;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQABLEttZGF0AAACcgYF//9u3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE2MCByMzAxMSBjZGU5YTkzIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAyMCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTAgcmVmPTIgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MToweDExMSBtZT1oZXggc3VibWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MSA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0tMiB0aHJlYWRzPTI0IGxvb2thaGVhZF90aHJlYWRzPTQgc2xpY2VkX3RocmVhZHM9MCBucj0wIGRlY2ltYXRlPTEgaW50ZXJsYWNlZD0wIGJsdXJheV9jb21wYXQ9MCBjb25zdHJhaW5lZF9pbnRyYT0wIGJmcmFtZXM9MCB3ZWlnaHRwPTAga2V5aW50PTI1MCBrZXlpbnRfbWluPTE1IHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAY3GWIhA7yYoAAvBScnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJydddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddf/+E1hQMYACRjUmqjMEtyWKP6AZhHinGE/EyloXvzhBqaz1e/3yYYTojX7HTV3/GCpKBaq+eHzdW7hjD/lwAEYTuItBLDg9HnBBP4ZgAAgOAAQa1LfDAASAD1C8HUxFCbydXwwzS2gg7W1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1tbW1x84f6BYEYACICQ0GQgw8PpyrfYNzvXumGQ111111111111131zPHMcaWlpaWlpaWlpaWlpaWlpaWlpaWlpaX+If9AsBQA4BDHALlvgwACMCAS595N/mH/wWQAGUpREM/Z/AxjwwpIa+fSQkgTXuZghj47SPS0tLS0tLS0tLS0tLS0tLS0tLS0tLS0tLXT11111111111111zQTXpgnrrrrrrrrrrrrrrrrrrrrr+A//hoFEACKKcZCF+sc2Cg1xABGiBLLA+lhqTV/iH/oFkBwEKaCpb4OAEUEBbBlS/Eu8AzD/0CyAAmZjrocqRoQ1DDgSAIggX4F1+K+Enr8//w0UeaPgAUUpQkc6kL//+GNqYYUkQW18tkMYuE188EwQ1LXXXXXXXXXXXXXXXXXXXXXXXXT11111111111111109ddddddddddfEP/8FYIgOAAgAwEsviCAAIAyEgAaBqcsqnL8GVLKVL8WhYrb+prnD/+wVmAArijujOVExVwYwpknhmuZSrmeE8Kh+uuuuv4D/+CsF0ARTYMxj1q+DgiQYjlgF2WFm5qTTQ8AAA/+GiwBWEqDkPA+2HdV+A/Dgiw0aX6FMs0Nmn1+AYh/6BZA4CAj2XwrDIB5HgdY5fg9bwDb/6BZACb0iJEtXv3ZgLOsKZfn3/H/2CwFIqPiAHxh46JiDomIOM+DjPXXUt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111111111111///OFQxwAELEMiuLANGu8fYDX9DoB3q0wkYaNZ7QL8MdAL8ElhWKT2RR/LphmuuuuuuuuulpfhgAf4aBA7gOAATAYPIS274YE1hgvIMVLKKl+Oy1svzqQkVWH8P7BXwAG6MS8hcx2KRgwBGcGrueSFQQ11111111111111111111111111111111111109ddddddddddddfCDBD/hIMeBoISk6VOTAaeiV+h+wCUBg2OOaUCUt+lWPQMA3zIYVM4lHcP4MP//7BFDAAaL8JZemSV48vV+DGaWoZrrrrrrrrrrrpaWlrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp666666666666666euuuv//iAwXAuAAiq5og12ESGDuFbQCPD491//rDBdAAhTReYWM6qGWaa79EYd0wX11111111111111111111111111111111111111111111109ddddddddddddddPXXXXMwYUsz5PTBXU9ddddddddddddddddddddddddddddddddddddddddddddPXXXXXXXXXXXXXXT111109PT11111111111111111111111111111111111111111111109ddddddddddddddPXXXX+H+AWC4GAAGOrbkm1/w3YcBEA/DX3b/4Bv4LoAGRbOYVMnX+DDimZRGB3VHXbpgxrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp6666666666666/gkBh/CQYgAIWIZFcWAaNd4+wGvgaCEpOlTkwGnolfmABAGgHFygE91AYcA/wH1/i7vrX7FDFRdYtRPITWAMMbu6QIpCove/9h//sPf+AA0X4Sy9Mkjx5e76hmuuulta6666666666666666666666666666666666666666666666euuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrp66666666666666666666666666666666666666666666666666666euuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp6666666666666/9FlFi7DXAAaoiCHIZ3KHneGfgInAMT3QPN3tagI//3sOgG0nTViSmyacZfCYCJe8hwu3Ks+9eHphA71UPQK06oNT3+5CgBWowtwkoYvyv//8PD//YIIYADRXjWTpUs8WTq3DUN11111111111111111111111111111111111111111111111111109ddddddddddddddPXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXT1111111111111109ddddddddddddddddddddddddddddddddddddddddddddddddddddPXXXXXXXXXXXXXXT111111111111111111111111111111111111111111111111111109ddddddddddddddPXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXT1111111111111/9YyFxew1wAGtGJwRi64kJNcI/ouh3GUy1vEvaj77EhHeMYKYl/iF3eEGQs9qGEvlgY7ygXKkVPvFchTRgGpGDpFkZsL17vtRrjAUkTLVgQj+/veMDaNgEvv+XHUEe2Bh7NNgpZ2IR/jnAAQmd3d2dmZmd8P/gAUHcNRGl27hZKoYYfUN11111111111111111111111111111111111111111111111111109ddddddddddddd9PXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXT1111111111111109ddddddddddddddddddddddddddddddddddddddddddddddddddddPXXXXXXXXXXXXXXT111111111111111111111111111111111111111111111111111109ddddddddddddddPXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXT1111111111111//xqOFQxAATGxTFBnN5RYae8V8G2gDBLMJzRY807WBBbVhhOliKUvgD44RBZqCCTGv1YIIlHFgCaVumX/usQEDXsAWNVw8TG59h/29hPYsP4ABDVAZ0iC7+vuP/OUM11111111111111111111111111111111111111111111111111109ddddddddddddfLt/8JBbxAF1UIBtLQIlo/Kf0PyGoKGYnVZPu1hGGDzAZwexaq0E9UYuhEMPw//YehjwABACAAUAwwmdQvXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXT1111111111111109ddddddddddddddddddddddddddddddddddddddddddddddddddddPXXXXXXXXXXXXXXT111111111111111111111111111111111111111111111111111109ddddddddddddddPXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXT1111111111111//+KwqFuAAjGxTHAxD+UaCRTBT+QSsP5LCteSId4x7cxMItccwsVtVPUL11111111111111111111111111111111111111111111111111109ddddddddddddfGwvf/hIMeDbQBglmE5oseadviBP3CA/M6WyaUkT9HFQCeJk6VlxHf9Q1wgtNuXLAtGi/veZgJhhS+YX+TIZRn7YDD6eJgptq2uXzhH4QgCx/wAGD+CUVptkcPLVfDMAAPAAUBhRMgI+/lnb48AA4ADgKPywEj9e2CWCgpwLusJdYPMvT//gAwDQIJBMvYAAAhNbW1tbW1tbW1tY9QAEGUhyEMU5jGOY5/+AgfodAhtEmNwMC8UuKfDtbW1tbW1tbW1/8f6Ct/ASf5eLcAdCKs/YABQVKl2jf//0CDtcay1tbW1tbW1tbX/x/oK4SuHvEAH0Iqz9gAFBUqXaNxQ7W1tbW1tbW1tf/H90CHBH4e9wAeQyLfmAAQFVpdsy1tbW1tbW1tbX+GP4Q7JS1gAi7vXefLap76YZrrrrrrrrrrrrrr/jD6aCgWgCCDllGFNSltDYECCtQBwBFD4C7IAOBgj0uPdstQdBf9Drh7h+gn8GUYtnnnFu5hSHuDQA8AAQAwqAXD8IwxjmGKcQhJmNkMORBe1tbW1tbW1tclg4YOEumHAAO5EAECkzqXKcTv7hxIPrgNou04w/+HzEiY0gQsFE7q4AuwrxK0twnSSfgbgzAAEBAeQhZB1eAE0oAMEfewwhqn9YAwdQrCfarXujfB/yDhwrhSN47o3Tv8GHSDtLS0tLS0tLS/krN7DoKBgAB9LAAgFJnc8xTC7QNQgvH98SnjU9/6sYjOF2mRaxR//9ngZQpEfrAQJI7rK9JgBQ+XA4qLGe60fx7QcY4DeMvatDWTj/wf3APl/QT8AFyDlhLlOynajKQNwGKAAY+wY4g6v5A4KaUEK5j2xHCP8GHSDIoABG1tbW1tbW1tclNlEJOMumGgAEIS/e4E7nbESHKM+QbphteZpq4cxvBJ4Bxi8Zz4t1Ze4zAXxXqgACAOZsG1/94jbCBTzCaZZ03oahYAAgL0DULs/DL4UKCoJShdoVx88wSrguLJs1EPcWGHoyDsBimIpiFr/+3/aDYoABC1tbW1tbW1tfyRw9Q0KDHABQ/hOW+2OW4ABYfbDNjaD7mnH+94wNKEEL/IwxnRDX+EAQQKwla1namaxP3kQGxWRzAAZrkqBd9JRhhJevDBEln+8QSUYbVdKfuYOKHPDn/b+gn8M5OY7pZTYRrQ2BPAAEA4AEIocbP4CGzcVyYxsrzP4MOkGRQACFra2tra2tra1111111111111111311111111111111111111311111111131111111111311111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111111114AAAAKrQZo4HeAK+P5+BLwERmGH/9ArDAw1+MVwOvlh18vxiWol+ngQQxsCDAYMgUACtizEQjNPf/AITzDxhD/DQneAKAu7Ahx4FG5/8MDpDW4NTlgT1+WZEiuAypZtabKruA22CUUB1bUOra+HVtQ6tr/TG4D/+CvAcIpDZbgZMBA0OFXjx04WMc47pf1ARFnBSdREuiXcmiO6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\" type=\"video/mp4\">\n",
+       " Your browser does not support the video tag.\n",
+       "</video>"
+      ],
+      "text/plain": [
+       "<IPython.core.display.HTML object>"
+      ]
+     },
+     "execution_count": 7,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
-    "from scipy.misc import imread\n",
-    "grid = imread('material/GosperGliderGun.png',flatten=True).astype(int)\n",
+    "from imageio import imread\n",
+    "grid = imread('material/GosperGliderGun.png', as_gray=True).astype(int)\n",
     "grid[grid>0] = ALIVE  # values are from 0 to 255 - set everything nonzero to ALIVE\n",
     "ani = makeImshowAnimation(grid, gameOfLifeSweep, frames=6*15)\n",
     "displayAsHtmlVideo(ani, fps=15)"
@@ -190,65 +269,26 @@
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "## Step 2: Going parallel with ipyparallel and waLBerla"
+    "## Step 2: Demonstration with waLBerlas python bindings"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "waLBerla is parallelized using MPI (message passing interface). That means that multiple processes are started, possibly on different machines which all execute the same program and communicate by sending messages. In a normal Python environment we would start multiple Python interpreters all executing the same script using `mpirun`. The following command would run a script using 4 processes:\n",
+    "waLBerla is parallelized using MPI (message passing interface). That means that multiple processes are started, possibly on different machines which all execute the same program and communicate by sending messages. In a typical Python environment, we would start multiple Python interpreters all executing the same script using `mpirun`. The following command would run a script using four processes:\n",
     "\n",
     "\n",
     "``` mpirun -np 4 python3 my_waLBerla_script.py```\n",
     "\n",
-    "\n",
-    "\n",
-    "To make use of multiple processes in this IPython environment we use the [ipyparallel](http://ipyparallel.readthedocs.org/en/latest/intro.html) package. \n",
-    "Therefore you first have to [start an ipyparallel cluster here in the IPython Cluster tab](/tree/ipython-tutorials#ipyclusters). Otherwise the following code will not work.\n",
-    "\n",
-    "Now lets test our parallel environment. After importing and initializing *ipyparallel* we only have to \n",
-    "prefix a code cell with the IPython magic **px** and the code cell will be executed by each process of the cluster."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
-   "source": [
-    "from ipyparallel import Client\n",
-    "rc = Client()\n",
-    "numberOfProcesses = len(rc.ids)\n",
-    "print(\"There are %d processes in your ipyparallel cluster\" % ( numberOfProcesses,))"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
-   "source": [
-    "%%px\n",
-    "print(\"Hello parallel world\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "If above cells produce errors, go back to the IPCluster setup to make sure your cluster is running. If everything works you see the hello world message printed by each process in the cluster."
+    "Using multiple processes is not very convenient in an IPython environment. Thus we only demonstrate here with one process. However, this tutorial can be easily extended to multiple processes"
    ]
   },
   {
    "cell_type": "markdown",
    "metadata": {},
    "source": [
-    "Now lets implement the *Game of Life* algorithm in parallel using waLBerla. waLBerla divides the complete domain into blocks. These blocks can then be distributed to the participating processes. We will set up here a simple example where each process gets one block. waLBerla can put multiple blocks per process. This makes sense if the computational load varies for each block, then a process receives either few expensive blocks or many cheap blocks.\n",
+    "Now lets implement the *Game of Life* algorithm in using waLBerla. waLBerla divides the complete domain into blocks. These blocks can then be distributed to the participating processes. We will set up here a simple example where each process gets one block. waLBerla can put multiple blocks per process. This makes sense if the computational load varies for each block, then a process receives either few expensive blocks or many cheap blocks.\n",
     "\n",
     "While blocks are the basic unit of load balancing they also act as container for distributed data. In this *Game of Life* example we have to distribute our grid to the blocks e.g. each block internally stores only part of the complete domain. \n",
     "The local grids are extended by one ghost layer which are a shadow copy of the outermost layer of the neighboring block.\n",
@@ -261,65 +301,57 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
+   "execution_count": 8,
+   "metadata": {},
    "outputs": [],
    "source": [
-    "from scipy.misc import imread \n",
+    "from imageio import imread\n",
     "import os\n",
     "\n",
     "# Read the initial scenario\n",
-    "initialConfig = np.rot90( imread('material/GosperGliderGun.png',flatten=True).astype(int), 3 )\n",
-    "initialConfig[initialConfig>0] = ALIVE  # values are from 0 to 255 - set everything nonzero to ALIVE\n",
-    "\n",
-    "# and send it to all execution engines together with other local variables\n",
-    "rc[:].push( {'initialConfig'     : initialConfig,\n",
-    "             'numberOfProcesses' : numberOfProcesses,\n",
-    "             'ALIVE'             : ALIVE,\n",
-    "             'DEAD'              : DEAD, \n",
-    "             'neighborhoodD2Q9'  : neighborhoodD2Q9,\n",
-    "             'gameOfLifeSweep'   : gameOfLifeSweep,\n",
-    "             'cwd'               : os.getcwd()      } )\n",
-    "pass"
+    "initialConfig = np.rot90( imread('material/GosperGliderGun.png',as_gray=True).astype(int), 3 )\n",
+    "initialConfig[initialConfig>0] = ALIVE  # values are from 0 to 255 - set everything nonzero to ALIVE"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
+   "execution_count": 9,
+   "metadata": {},
+   "outputs": [
+    {
+     "name": "stdout",
+     "output_type": "stream",
+     "text": [
+      "Block on rank 0: with offset (0, 0, 0)\n"
+     ]
+    }
+   ],
    "source": [
-    "%%px\n",
+    "# %%px\n",
     "import sys\n",
     "import waLBerla as wlb\n",
     "import numpy as np\n",
     "import os\n",
     "\n",
-    "os.chdir( cwd ) # change working directory of all engines to working directory of notebook kernel\n",
+    "# For this tutorial we use only one process. The code can easaly run with more processes and mpirun as pyhton script\n",
+    "numberOfProcesses = 1\n",
     "\n",
     "domainSize = (initialConfig.shape[0], initialConfig.shape[1], 1)\n",
     "\n",
     "# We can either specify the detailed domain partitioning ...\n",
-    "blocks = wlb.createUniformBlockGrid(cellsPerBlock=(domainSize[0]//numberOfProcesses, domainSize[1], domainSize[2]),\n",
-    "                                    blocks=(numberOfProcesses,1,1), \n",
+    "blocks = wlb.createUniformBlockGrid(blocks=(numberOfProcesses, 1, 1),\n",
+    "                                    cellsPerBlock=(domainSize[0]//numberOfProcesses, domainSize[1], domainSize[2]),\n",
     "                                    periodic=(1,1,1))\n",
     "\n",
-    "# ... or let waLBerla do it automatically - \n",
-    "#     if the domainSize is not divisible by the block count the domain is slightly extended\n",
-    "blocks = wlb.createUniformBlockGrid(cells = domainSize, periodic=(1,1,1))\n",
     "\n",
     "# Now put one field (i.e. grid) on each block\n",
-    "wlb.field.addToStorage(blocks, name='PlayingField', type=np.int, ghostLayers=1)\n",
+    "wlb.field.addToStorage(blocks, name='PlayingField', dtype=np.int, ghostLayers=1)\n",
     "\n",
     "# Iterate over local blocks - in our setup we have exactly one block per process - but lets be general\n",
     "for block in blocks:\n",
-    "    offsetInGlobalDomain = blocks.transformLocalToGlobal(block, (0,0,0))\n",
+    "    offsetInGlobalDomain = blocks.transformLocalToGlobal(block, wlb.Cell(0,0,0))\n",
     "    myRank = wlb.mpi.rank()\n",
-    "    print(\"Block on rank %d: with offset %s\" % (myRank, offsetInGlobalDomain ))"
+    "    print(\"Block on rank %d: with offset %s\" % (myRank, offsetInGlobalDomain[:] ))"
    ]
   },
   {
@@ -331,13 +363,23 @@
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
+   "execution_count": 10,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "image/png": "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\n",
+      "text/plain": [
+       "<Figure size 1080x864 with 1 Axes>"
+      ]
+     },
+     "metadata": {
+      "needs_background": "light"
+     },
+     "output_type": "display_data"
+    }
+   ],
    "source": [
-    "%%px\n",
     "import waLBerla.plot as wlbPlt\n",
     "\n",
     "import matplotlib\n",
@@ -350,35 +392,49 @@
     "# Initialize our grid\n",
     "for block in blocks:\n",
     "    grid = wlb.field.toArray( block['PlayingField'] ).squeeze()\n",
-    "    offsetInGlobalDomain = blocks.transformLocalToGlobal(block, (0,0,0))\n",
+    "    offsetInGlobalDomain = blocks.transformLocalToGlobal(block, wlb.Cell(0,0,0))\n",
     "    blockSize = grid.shape[:2]\n",
     "    xBegin, xEnd = offsetInGlobalDomain[0], offsetInGlobalDomain[0] + grid.shape[0]\n",
     "    yBegin, yEnd = offsetInGlobalDomain[1], offsetInGlobalDomain[1] + grid.shape[1]\n",
     "    grid[:,:] = initialConfig[xBegin:xEnd, yBegin:yEnd]\n",
     "\n",
-    "wlbPlt.scalarField( blocks, 'PlayingField', wlb.makeSlice[:,:,0] )"
+    "wlbPlt.scalar_field( blocks, 'PlayingField', wlb.makeSlice[:,:,0] )"
    ]
   },
   {
    "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
+   "execution_count": 11,
+   "metadata": {},
+   "outputs": [
+    {
+     "data": {
+      "text/html": [
+       "<video controls width=\"80%\">\n",
+       " <source src=\"data:video/x-m4v;base64,AAAAIGZ0eXBpc29tAAACAGlzb21pc28yYXZjMW1wNDEAAAAIZnJlZQABOidtZGF0AAACcgYF//9u3EXpvebZSLeWLNgg2SPu73gyNjQgLSBjb3JlIDE2MCByMzAxMSBjZGU5YTkzIC0gSC4yNjQvTVBFRy00IEFWQyBjb2RlYyAtIENvcHlsZWZ0IDIwMDMtMjAyMCAtIGh0dHA6Ly93d3cudmlkZW9sYW4ub3JnL3gyNjQuaHRtbCAtIG9wdGlvbnM6IGNhYmFjPTAgcmVmPTIgZGVibG9jaz0xOjA6MCBhbmFseXNlPTB4MToweDExMSBtZT1oZXggc3VibWU9NyBwc3k9MSBwc3lfcmQ9MS4wMDowLjAwIG1peGVkX3JlZj0xIG1lX3JhbmdlPTE2IGNocm9tYV9tZT0xIHRyZWxsaXM9MSA4eDhkY3Q9MCBjcW09MCBkZWFkem9uZT0yMSwxMSBmYXN0X3Bza2lwPTEgY2hyb21hX3FwX29mZnNldD0tMiB0aHJlYWRzPTI0IGxvb2thaGVhZF90aHJlYWRzPTQgc2xpY2VkX3RocmVhZHM9MCBucj0wIGRlY2ltYXRlPTEgaW50ZXJsYWNlZD0wIGJsdXJheV9jb21wYXQ9MCBjb25zdHJhaW5lZF9pbnRyYT0wIGJmcmFtZXM9MCB3ZWlnaHRwPTAga2V5aW50PTI1MCBrZXlpbnRfbWluPTI1IHNjZW5lY3V0PTQwIGludHJhX3JlZnJlc2g9MCByY19sb29rYWhlYWQ9NDAgcmM9Y3JmIG1idHJlZT0xIGNyZj0yMy4wIHFjb21wPTAuNjAgcXBtaW49MCBxcG1heD02OSBxcHN0ZXA9NCBpcF9yYXRpbz0xLjQwIGFxPTE6MS4wMACAAAAXkGWIhAvyYoAAqcScnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJycnJydddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddf+uFWMXsNcABGPxxRmZUtRbuGf1QkOMehppXZgvn+AgZkRHlLaR3jZ4RRp2TCS3PvqSJANmyEAo+zOC8FRE2J9mMWf9wwMxP6AspOW+L/+82Y1yM6qQR3O/GZjkhSnc/hHD/LgAJkZhOhyDCd4w8yGGYAAIAwDEkS3KhgAMBynwErJVJS7LDBTS2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra2tra4+cP+H8ABILNCIgjz6RZPwdMNhrrrrrrrrrrrrrvrpxQBvvvvvvvvvvvvvvvvvvvvvv/iH/gsBQA4RCATLfEAE0QL59/+f/4LIAQKRj6LT0//+GVQ18+rJC9yMFN9999999999999999999999999QTOmCeuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrr+A//hoFEAHeyRjRl1e95WXwJARAYjuF2WWHJq/xD/0CwgHCCoZfA8TgSKwZKX4l3gGIf+gWQA5NMMVFh+HK/uBIkBU8Gpy9xHf5//ho78AA5tJtU+v+IUw18nZc5IE3dMEdS11111111111111111111111109dddddddddddddddPXXXXXXXXXXXxD//BWCQDgIDBbL4iABIYAARQXZZOy/JpNwuhsxkxnMZMZ/9X6fchkhnnD/+wVmAArlGaIx7Rfgw5J5IQpubzwqHa6666/gP/4KwXQEKNmetXwcQok5YH0duak00PDAP/QaPAFYvDiLA+2Hf4D4FBKDCRgwlLXwde8AxD/0CyBwQZrL4EgkAojwOmcvxHeAbR/6BZADKqSSWr3/AW4xL9viHD/2CwFIOAevJ45S8HD3wcPfA4jS5DiNLieuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrp666666666/+H/BYCKABEFuIpj6J4Nmt5YdY4f/oFZgavGgUOAxLol3iOiO+IBAP/grgOCYfZb4eAONAKEdEd22+v//YKzgAzCucun/77/z154daeoI6666/gMP/DQKIATaLGrIq7X/rAkEwNHDL490mbmqZofj//BXgBrRiVle7z5EOzMdfx9PMzG56SC2np66lrrrrrrr4h//grDwADdMmCZ8hQ53FgoLDBoBIeADhKA1TwdJ25gobjjrIbQp6w//sFYRAATyOIh1IY4iUhhwLJCLgE9PZZ5ERLmBC0897KdzUM11111111111109ddddddddddddf/8FXhUMQAEYaxxR2aUtRr+m/GzwRRpuRCavFbCGGIvjySTKQf/fAIQMSyiKbpS2HzXIUDrqq7Mjld/8whDxdcBvsLD//hfYsP4ADAcp0A1dKpMbZ6WmF66666666//+HBYCbgcECC0BpcMvpW7lZocfH/QLBHAcRF6Y/6pp8Xh/LB4OIiQti3tt5mC0UBiACs/gGAf6BUCAQkhFoSTIkmcVYq/h//grFnY4DoAQx99+oZr/xw/grBVxQABALgZYIEAxvi1nLGOdGXxaD4YAZYiECqByzIOWZwdIyDpGdr4jgEP4Kw1wOECsQBkIsNZRZ21PJkl3McRS1th8B//gr8HBAikMdQ9wkObZW7mbzQp6eoJaenrqWuuuuuuuniOjjTBQ6gprrrrrrrrrrrrp6666666666666+u7//CQU+SITMCoRnKCTeCMOwxe1j2kJSxN98RQynmg7zWt8NRReTP+PgGwWFwHAAJAMHARlsILhSAhIEeDw94kO7Xx5/Dw4bAAYpShI5+QvgYMXA5WDLYgHcYuCTueCYfrrrrrp5mCrKZgpFAxAZGAANf/h/hsFWABndNoXYhAUMkhlfxBBISNNiHX00/n//YbLgBgKZ5H6f/92Kuv1BX8fAP4bBVgARVc0Qf2MUasE/AwsCDw43xzp7DTTX44BD+CsuoDgI5YdLBhQWG5RR3EHnTyxizEOivFoafI59pr4n8P4bCHACOLRRleXnEFIS0M9nMLdf4f+w0F42g9/jrSsoO7EHdj54BD9BoMcAGZV0+zfDgfoAlXof3q38FwCH6G+AA2bI3lGN2jYxTGWcImyCVODx6/Fv4BiH+gWEwHEZQSWwhJMK+iQ7xV/AM//BZwAzuimrT/8R2Rie7t9PTBbUtdddddddeEwwD/grD2AAolINGhlFtMDdgwF4OKhOBqnh3goNxw72uEv/8FYsrMAB4RoUp2IxRV78GELMwIoAS0S0LP+BOL3nv+mFa666666666666euuuuuuuuuuuuuuunm4+mYLNUwV1111109PT1+YYB/wVguwAwDJb2u7/iXNFsT3bb+3/9grE4Azu/97/f17/UFPoGAf8FYJsAEK1rvv/ngwVi6C9jPO8a4538BwCH8FfgOBGKD5YMFTQyhxfOIOHThYy0xPRfOhYhzN88Ahj48ZwAGSKJib1a5ZGPvwOFChrFAo/9HnTzDRiHRX8H//grJwAwrm3tdoB2mpl+/9sFP0wCP6Ggm4Ay+73v9/L794SmgCYnvb+AYh4dAs4DhMMPlsIL/BKnB494k78AzD/hzgAUkpMTerXf//gwYa4HkwJYQ6T/5Tb+AbQ/wWcAZ3/97/f/T++30wV1111111110tLa111111111111109ddddddddddddddf4RD/gsDGDgAFAABAFIwlBFAkFIHmFL8Gzl9r4wt/4LDYADKclD7vFK4Y1RABiUtkODbl30w7XXXXX/CP/BYCDgCgmRgEFIAo3NYS2m80PgGAfDQLBXAFE0whUAfbDlqauXW8XICnlgxxRJoul3m026YLv/D/BWDLABzjNEMNxer25BEAREIQ0FmWTMt8M2m1fn//YbE4AFcozRGaUbwYyv68kOeFMENr8cA/4KwUcHCBSIMXB1QjtBLl+bl3OiPF9v/4K/ADFe3t6vHKGiP5/zQVg4ABEL0HJ13/4h/wWAkwcCApGBVgHhXL8u/ANvh4LI8gwBBNNs5mevHaS5c8u8zBPJV111111111111111111111111109ddddddddddddddczyRrXS1111110v//DgsBFH6QAV0kzPQsq5ZYdcXEPJBycR2bTb/CIYAH9gr4DhONktiAIiEC6Lpd//4f2CsSRmAApppGTYuiqHfyQ7zMmemCeultbWuuu1ta666666666666666666666666666euuuuuuuuuuuuv//+FQtwAG0RCFcrsULL6J/iKgaYNR4badLBmyUwABADTQALoSt58umF6666666666Wl/8AwDhEPCASMQRtrh/D+wV34Atzw5IUw/XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXT11111111111118ov/+Egt8kAF1KaAHgrG52w6TGEIYvxdwx69aIIZK8PcsMWvbP7//8P/+CL4ADSvEt9utmiX5swY6WoXrrrrrrrrrrrpaWlrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp6666666666666666euuv//iGC4FwAGPzSH5iOGsLCYwDgI9+v/9fBdABNy2KN+u78yDXbXumC+uuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrp6664pgwmt7+Kdtta+mC2pa66666666666666666666666666666666666666666666euuuuuuuuuuuuuuunrrrp6YMHTBbUtddddddddddddddddddddddddddddddddddddddddddddPXXXXXXXXXXXXXXXT111/h/gGC4FwAGbbfKfBjA6xXHerb/4Bv4LoAJPcxVk/3f8qYud1R126YL6666666666666666666666666666666666666666666666euuuuuuuuuuuuv5IrFH+EgxAAbSIRxEZiBJHD/woNr1s4TT2PrLCOgmoGbmHhNbUkEf74BI6BtbZKSxmW/xlxkAMVFHsGwplA9e9FAKvqeosc9fE44f/8Lw4YADStNRbdatNFzVlunS0wzXXXS2tddddddddddddddddddddddddddddddddddddddddddddddPXXXXXXXXXXXXXXXT11111111111111111111111111111111111111111111111111109dddddddddddddddPXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXT11111111111111109dddddddddddddddddddddddddddddddddddddddddddddddddddPXXXXXXXXXXXXXXXT11111111111111111111111111111111111111111111111111109ddddddddddddf+uEAHsNcABJELflgChiOU4C+4CDQMZisO6wG8HaHtNk6Ww/bGgjqNyLPHLn/rb+RjSzOQfMTGDMLd2p1WF5/8/8P/+CQvgANK8S326WaJfmvG2aWoXrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp6666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrp6666666666666666666666666666666666666666666666666666euuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp6666666666666666euuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrr/xXgKI1hjgAMP4yDe+1KEvXZ7WgzDWWSc4k3aARMnTJtc/n14AAgDgDXduyQFWAfgA9uKJwMlP+kQx4oKMWRmI6s8bx2VyAEQVd2RzAzCVxeqzTtbP+15AiemIYxP//sEsABERmZmRkRERnh/DAAodhoJb7Vhoqfhh9LTDNdddddddddddddddddddddddddddddddddddddddddddddddddddPXXXXXXXXXXXXXfXT11111111111111111111111111111111111111111111111111109dddddddddddddddPXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXT11111111111111109dddddddddddddddddddddddddddddddddddddddddddddddddddPXXXXXXXXXXXXXXXT11111111111111111111111111111111111111111111111111109ddddddddddddf//ECghCwACZEMroIzEvSxifIzERlOdSDKetXkZiIynP2GQtfeLBk4OSI7dBJAaynw/Db4JIcMAAj+CKkoazjaj/zqWoXrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp6666666666666/yL/4fCmhOoz1or6c/StcEhxRbKEP8IPXpG8PH//YIoY8AA0AHA4gmfqYVrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp6666666666666766euuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuuunrrrrrrrrrrrrrrrp6666666666666666666666666666666666666666666666666666euuuuuuuuuuuuuuunrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrrp6666666666666///jBCEsACILcRTNkTsxGIHTwj6Sf75dMJ11111111111111111111111111111111111111111111111111109ddddddddddddf7Cn/h8LYoh2ph+Lkj+7zhj5M2wmNmw4LADkaF4roUmcROgTHOCnlPUKcrD1+hj/CAZ4YADDuEp1vlmCXpr+YAAWAHBzyZAk/Xu+PAALACg4rLUsACGUszACAL1aCCKX/wAYBwSWoAAAg4aZPvvvvvvvvhPAAQ3Z2ZndmZ2d/+zgAYylmYAQBerQRh+vvvvvvvvv/8f8EvgAJxD8/QAKESlVQj3//8EHLTRvvvvvvvvv/8f8M+AAnEPy9AAoRKlVCONU9fffffffff/4/4JfAASjF7cgAQInKqpHr77777777/4Y/hDPfAJm79v9TC9dddddddddddddf8f6QQUCnwId7iHJ/1wfAppGoDgMoTIXZABwwi/USy5XxEP3IdQ/f+gn8NxHq0sp6sU7lsDQDwAKKkFw+CIYIxRCFM7yKX//ww6QVFANra2tra2tra5LBwwcJdMNAAd0gCCtnfYhjd/cOSH/gbTcmY/+USSY0gRYc3dXAVQpwUqkxeQSoG4zAAEAYWRSCDq8APSgDCNu1MWhffrAMOgznrsq9Jvv/UMoqjG7KcNxO9/0g2KAbW1tbW1tbW1/JWD2DQUCwAGRAEDtm69yENtB8Q3m+h18i//1QyCUW6RfmP//U8G7hq20Dvis1U9k4Ax44FMkr0o4NtrQ4hQbTty+Crkn/g/uH5f0E/AF2FeJWluE7ST4DcGYAAgIDyEKIOr+QcOFcKRvHdG6d/gw6QXFANra2tra2tra5KbKASfLphoABEFecwC/OdJyKf5GaQXKNb/hnL4S4AYI+blBCSCbXgvFXgA7rh9f3eT8Q5Ll0iSJnQ0OAAIBISAuH5j8c4CQyosoN2pzL/HxRZfyVN//Bh6aPgEIjoRnp/+380g2KANra2tra2tra/kjh6hoUGOACj0EjnpDO4ABZ9IYs+A+jEl+9ph4wZ6z+/juT33ECEOxmVhtjaxfvUGiplYAZav4bPMCYI5cMRBH64hYwLL2ShHDvPd4c/7f0EygCZW5CullJjCtDYXgACAQAEdA42fwEJm0rkxS5TlfwYdIKigDa2tra2tra2tdddddddddddddddd9ddddddddd9dddddddddd9ddddddddd9dddddddddd9dddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddddeAAAADAkGaOBfgCKOLDXABA7/+rv7MX7nx3oQH+6jWA14rwHAXzJkHAXzJkHAXzJgKhLDMNHgkcEx4I5TvBqficBEYQQSDDLIxQ/LXy/BmllNL9PAghhBbtgQYDBgigBlSREiWr3/7gEJ7wETHC9VAECZmAQQgqtX/VcGoQQoViISGAoS6Jd00/sREyQmSxMkOteY3AYf+CskByMjy2wdBQQDG+PdLeqa6gIiJOCtT6fcmk3T+heKAwULDt4haO8V2GACP1wd/nH/BUbHCH+gVBBKvTT4tgrjsfmCAYB/wVAqEVLFWKvTT/BMfAcEUlqZg4IpLUzBwRSWpneU2P/+CohFx/vjHfj2CmSvf9cuaH/+wVhaZTp7HY5Xyvo7C+fqAiZxiwOSrkOSrkHW+DreDI3/8OCwOcAJNGZkIZ67RvvCzc5oTY4XD+g19yXgBmrSVLtI7EOhEOvpk/i4KYGwQa8n+AiAETmC3/+CsEnAGd3/vf7+dVf87BTn5TQngH+hoLuADD+9XG+/vAzYC1EO9PmwHAIfxvgCdILGKgdio9afRYchwdVWin9VXNlWyw0X+Dj9wieCeA5EyJmBBVEo/exVMpVPqNjC//QLC4AYVzbbXa8AkBcUb3l+DLkwOCDCUEAnmWJXR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\" type=\"video/mp4\">\n",
+       " Your browser does not support the video tag.\n",
+       "</video>"
+      ],
+      "text/plain": [
+       "<IPython.core.display.HTML object>"
+      ]
+     },
+     "execution_count": 11,
+     "metadata": {},
+     "output_type": "execute_result"
+    }
+   ],
    "source": [
-    "%%px\n",
     "communication = wlb.createUniformBufferedScheme( blocks, 'D2Q9')\n",
     "communication.addDataToCommunicate( wlb.field.createPackInfo( blocks, 'PlayingField') )\n",
     "\n",
     "def runTimestep():\n",
     "    communication()\n",
     "    for block in blocks:\n",
-    "        grid = wlb.field.toArray( block['PlayingField'], withGhostLayers=True )[:,:,1,0]\n",
+    "        grid = wlb.field.toArray( block['PlayingField'], with_ghost_layers=True )[:, :, 1]\n",
     "        gameOfLifeSweep( grid )\n",
     "        \n",
     "\n",
-    "ani = wlbPlt.scalarFieldAnimation( blocks, 'PlayingField', wlb.makeSlice[:,:,0], runFunction=runTimestep, frames=100 )\n",
+    "ani = wlbPlt.scalar_field_animation( blocks, 'PlayingField', wlb.makeSlice[:,:,0], run_function=runTimestep, frames=100 )\n",
     "displayAsHtmlVideo( ani, fps=30, show=(wlb.mpi.rank()==0) )"
    ]
   }
@@ -399,9 +455,9 @@
    "name": "python",
    "nbconvert_exporter": "python",
    "pygments_lexer": "ipython3",
-   "version": "3.5.1"
+   "version": "3.8.2"
   }
  },
  "nbformat": 4,
- "nbformat_minor": 0
+ "nbformat_minor": 1
 }
diff --git a/python/waLBerla_docs/ipython/ipython-tutorials/Tutorial 02 - LBM.ipynb b/python/waLBerla_docs/ipython/ipython-tutorials/Tutorial 02 - LBM.ipynb
deleted file mode 100644
index 1ec91a5cc665deba87b010adde9c6a652f5ee034..0000000000000000000000000000000000000000
--- a/python/waLBerla_docs/ipython/ipython-tutorials/Tutorial 02 - LBM.ipynb	
+++ /dev/null
@@ -1,254 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "metadata": {
-    "collapsed": false
-   },
-   "source": [
-    "# waLBerla Tutorial 02: LBM Setup\n",
-    "\n",
-    "In this tutorial we set up a basic lattice Boltzmann simulation of a periodic channel.\n",
-    "\n",
-    "First we create a simple block structure, same as in the last \"Game of Life\" tutorial."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": true
-   },
-   "outputs": [],
-   "source": [
-    "from waLBerla import *\n",
-    "from material.matplotlib_setup import *\n",
-    "\n",
-    "blocks = createUniformBlockGrid( cells=(500,200,1), periodic=(1,0,1) )"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "For LBM we have the choice of different lattice models. A lattice model in waLBerla consists of:\n",
-    "- a stencil that defines the cell neighborhoods. In this tutorial we use a two dimensional D2Q9 stencil.\n",
-    "- a force model to use volume forces in the domain. For our setup we need a constant volume force to drive the channel and make use of the \"SimpleConstant\" model. \n",
-    "- a collision model defining the LBM collision operator. Her we use SRT (BGK), however the two relaxation time (TRT) model is also supported and for the D3Q19 model a MRT implementation is available as well\n",
-    "\n",
-    "With the lattice model a pdf field is created and added to all blocks. Additionally we create optional adaptors for velocity and density. These adaptors behave similar to fields, however no memory is required for them, the values are computed on-the-fly from the pdfs.\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": true
-   },
-   "outputs": [],
-   "source": [
-    "omega = 1.3\n",
-    "collisionModel =lbm.collisionModels.SRT(omega)\n",
-    "forceModel = lbm.forceModels.SimpleConstant( (1e-5,0,0) )\n",
-    "latticeModel = lbm.makeLatticeModel(\"D2Q9\", collisionModel, forceModel)\n",
-    "lbm.addPdfFieldToStorage(blocks, \"pdfs\", latticeModel, \n",
-    "                         velocityAdaptor=\"vel\", densityAdaptor=\"rho\", initialDensity=1.0)"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "To store geometry information a flag field is created and used as input to the LBM boundary handling:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
-   "source": [
-    "field.addFlagFieldToStorage( blocks, 'flags')\n",
-    "lbm.addBoundaryHandlingToStorage(blocks, 'boundary', 'pdfs', 'flags')"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Next we define the setup of our channel. At the northern and southern domain borders no-slip boundaries are required.\n",
-    "To set up the boundaries the distributed nature of the domain has to be taken into account. \n",
-    "When iterating over the blocks we only get the locally stored blocks. So this loop is automatically parallelized when using multiple MPI processes. For each block we first check if it is located at the border of the global domain, and if so, we set the border slice to no-slip. When defining the slice, the additional __'g'__ marker is used, to place the boundary in the ghost layer.\n"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
-   "source": [
-    "def setBoundariesChannel( blocks, boundaryHandlingID ):\n",
-    "    for block in blocks:\n",
-    "        b = block[ boundaryHandlingID ]\n",
-    "        if block.atDomainMinBorder[1]:\n",
-    "            b.forceBoundary('NoSlip',   makeSlice[ :, 0, :, 'g'])\n",
-    "        if block.atDomainMaxBorder[1]:\n",
-    "            b.forceBoundary('NoSlip',   makeSlice[ :,-1, :, 'g'])\n",
-    "        b.fillWithDomain()\n",
-    "setBoundariesChannel( blocks, 'boundary' )"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Finally a LBM sweep functor is created, making use of the already created pdf and flag field."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": true
-   },
-   "outputs": [],
-   "source": [
-    "sweep = lbm.makeCellwiseSweep(blocks, \"pdfs\", flagFieldID='flags', flagList=['fluid'])"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Since we have set our domain to be periodic in x-direction,the east and west boundary are taken care of by the ghost layer exchange. Here the communication is set up as explained in the previous tutorial. "
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": true
-   },
-   "outputs": [],
-   "source": [
-    "communication = createUniformBufferedScheme( blocks, 'D2Q9')\n",
-    "communication.addDataToCommunicate( field.createPackInfo( blocks, 'pdfs') )"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "One timestep consists of ghost layer communication, boundary handling and an LBM stream-collide step"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": true
-   },
-   "outputs": [],
-   "source": [
-    "def run( timesteps ):\n",
-    "    for t in range(timesteps):\n",
-    "        communication()\n",
-    "        for block in blocks: block['boundary']()\n",
-    "        for block in blocks: sweep.streamCollide( block )"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "To get a more interesting result we place an airfoil shaped object inside our channel. This geometry is loaded from an image placed in the 'material' subfolder. You can upload different images and simulate the flow around these objects:"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": true
-   },
-   "outputs": [],
-   "source": [
-    "from waLBerla.geometry_setup import *\n",
-    "setBoundaryFromBlackAndWhiteImage(blocks, \"boundary\", makeSlice[0.25:0.75, 0.3:0.6 ,0.5], \n",
-    "                                  \"material/wing.png\", \"NoSlip\")"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Next we create a video of the density distribution in the channel ( which is proportional to pressure in LBM). Before creating a frame for the video we let the simulation run for 10 timesteps, then we set the density inside the object to NaN to get a better looking plot"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": true
-   },
-   "outputs": [],
-   "source": [
-    "def setByMask( blocks, targetField, targetValue, flagField, flagNames ):\n",
-    "    for b in blocks:\n",
-    "        mask = 0\n",
-    "        for flagName in flagNames:\n",
-    "            mask |= b[flagField].flag(flagName)\n",
-    "\n",
-    "        targetArr = field.toArray( b[targetField], True )\n",
-    "        flagArr   = field.toArray( b[flagField]  , True )[:,:,:,0]\n",
-    "        targetArr[ np.bitwise_and( flagArr, mask ) > 0, : ] = targetValue\n",
-    "\n",
-    "\n",
-    "    \n",
-    "def visualizationStep():\n",
-    "    run(10)\n",
-    "    # Set pdfs to NaN in NoSlip cells to better see the obstacle in the visualization:\n",
-    "    setFieldUsingFlagMask(blocks, 'pdfs', np.NaN, 'flags', ['NoSlip'] )"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
-   "source": [
-    "import waLBerla.plot as wplt\n",
-    "\n",
-    "run(700) # run the simulation until flow field has stabilized - then no colormap rescaling is required\n",
-    "ani = wplt.scalarFieldAnimation( blocks, 'rho', makeSlice[:,:,0.5], visualizationStep, frames=300)\n",
-    "displayAsHtmlImage(ani)"
-   ]
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.5.1"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/python/waLBerla_docs/ipython/ipython-tutorials/Tutorial 03 - LBM with extensions.ipynb b/python/waLBerla_docs/ipython/ipython-tutorials/Tutorial 03 - LBM with extensions.ipynb
deleted file mode 100644
index e1787ea400b7a0ecc503d2cb4d7f1ac5cd8bca7f..0000000000000000000000000000000000000000
--- a/python/waLBerla_docs/ipython/ipython-tutorials/Tutorial 03 - LBM with extensions.ipynb	
+++ /dev/null
@@ -1,272 +0,0 @@
-{
- "cells": [
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "# waLBerla Tutorial 03: LBM Extensions\n",
-    "\n",
-    "\n",
-    "We start with the LBM simulation of an airfoil from last tutorial. In this tutorial we show how the boundary conditions can be set in a flexible way, by generating the airfoil geometry from an analytic description. Additionally we add some evaluation of force acting on the airfoil.\n",
-    "\n",
-    "## A) Programmatic Boundary setup"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
-   "source": [
-    "from waLBerla import *\n",
-    "import waLBerla.plot as wplt\n",
-    "from waLBerla.geometry_setup import *\n",
-    "import numpy as np\n",
-    "import itertools\n",
-    "from IPython import display\n",
-    "from material.matplotlib_setup import *"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "Instead of loading the airfoil from an image file, we use the [NACA analytical description](https://en.wikipedia.org/wiki/NACA_airfoil) to generate the geometry.  The following function is just an implementation of the NACA airfoil description from Wikipedia. Additionally the airfoil can be rotated afterwards. The resulting array has value 1 in cells overlapped by the airfoil and zero otherwise."
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": true
-   },
-   "outputs": [],
-   "source": [
-    "def makeNacaAirfoil( length, thickness=30, angle=0 ):\n",
-    "    import scipy\n",
-    "    import scipy.ndimage    \n",
-    "    def nacaAirfoil(x, thickness,chordLength):\n",
-    "        xOverC = x / chordLength\n",
-    "        y_t = 0\n",
-    "        coeffs = [ 0.2969, -0.1260, - 0.3516, 0.2843, -0.1015 ]\n",
-    "        for coeff, exponent in zip( coeffs, [ 0.5, 1,2,3,4 ] ):\n",
-    "            y_t += coeff * xOverC ** exponent\n",
-    "        y_t *= 5 * thickness/100 * chordLength\n",
-    "        return y_t\n",
-    "\n",
-    "    domain = np.zeros( (length, int(length*thickness/100) ) )\n",
-    "    it = np.nditer( domain, flags=['multi_index'], op_flags= ['readwrite'] )\n",
-    "    while not it.finished:\n",
-    "        x,y = it.multi_index\n",
-    "        y -= domain.shape[1]/2\n",
-    "        if abs(y) < nacaAirfoil( x, thickness, domain.shape[0] ):\n",
-    "            it[0] = 1\n",
-    "        it.iternext()\n",
-    "    domain = np.rot90( domain,1 )\n",
-    "    domain = scipy.ndimage.interpolation.rotate( domain, angle=-angle)\n",
-    "\n",
-    "    domain[ domain > 0.5 ] = 1\n",
-    "    domain[ domain <= 0.5 ] = 0\n",
-    "    domain = domain.astype( np.int32 )\n",
-    "    return domain"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
-   "source": [
-    "from ipywidgets import interactive\n",
-    "def showAirfoil( thickness=10, angle=20 ):\n",
-    "    wplt.style.use('ggplot')\n",
-    "    wplt.imshow( makeNacaAirfoil(300, thickness, angle) )\n",
-    "widgets = interactive(showAirfoil, thickness=(5,50,1), angle=(-45,45, 1))\n",
-    "display.display(widgets)"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
-   "source": [
-    "airfoilArr = makeNacaAirfoil( length=200, **widgets.kwargs )"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
-   "source": [
-    "def setBoundariesChannel( blocks, boundaryHandlingID ):\n",
-    "    for block in blocks:\n",
-    "        b = block[ boundaryHandlingID ]\n",
-    "        if block.atDomainMinBorder[1]:\n",
-    "            b.forceBoundary( 'NoSlip',   makeSlice[ :, 0, :, 'g'] )\n",
-    "        if block.atDomainMaxBorder[1]:\n",
-    "            b.forceBoundary( 'NoSlip',   makeSlice[ :,-1, :, 'g'] )\n",
-    "        b.fillWithDomain()\n",
-    "\n",
-    "\n",
-    "# Lattice Model Setup\n",
-    "omega = 1.7\n",
-    "domainSize = ( airfoilArr.shape[1]*2, airfoilArr.shape[0] * 2 )\n",
-    "blocks = createUniformBlockGrid( cells=(500,200,1), periodic=(1,0,1) )\n",
-    "collisionModel =lbm.collisionModels.SRT( omega )\n",
-    "forceModel = lbm.forceModels.SimpleConstant( (1e-5,0,0) )\n",
-    "latticeModel = lbm.makeLatticeModel( \"D2Q9\", collisionModel, forceModel )\n",
-    "lbm.addPdfFieldToStorage( blocks, \"pdfs\", latticeModel, velocityAdaptor=\"vel\", densityAdaptor=\"rho\", initialDensity=1.0 )\n",
-    "field.addFlagFieldToStorage( blocks, 'flags' )\n",
-    "lbm.addBoundaryHandlingToStorage( blocks, 'boundary', 'pdfs', 'flags' )\n",
-    "\n",
-    "# Boundary Setup\n",
-    "setBoundaryFromArray( blocks, 'boundary', makeSlice[0.3:0.7, 0.3:0.7 ,0.5], airfoilArr, { 1: 'NoSlip' }, resizeFunc=binaryResize )\n",
-    "setBoundariesChannel( blocks, 'boundary' )\n",
-    "\n",
-    "sweep = lbm.makeCellwiseSweep( blocks, \"pdfs\", flagFieldID='flags', flagList=['fluid'] )\n",
-    "\n",
-    "# Communication\n",
-    "communication = createUniformBufferedScheme( blocks, 'D3Q19')\n",
-    "communication.addDataToCommunicate( field.createPackInfo( blocks, 'pdfs') )\n",
-    "\n",
-    "def run( timesteps=10 ):\n",
-    "    for t in range(timesteps):\n",
-    "        communication()\n",
-    "        for block in blocks: block['boundary']()\n",
-    "        for block in blocks: sweep.streamCollide( block )"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
-   "source": [
-    "run(100)\n",
-    "setFieldUsingFlagMask(blocks, 'pdfs', np.NaN, 'flags', ['NoSlip'] )\n",
-    "ani = wplt.scalarFieldAnimation( blocks, 'rho', makeSlice[:,:,0.5], runFunction=run )\n",
-    "displayAsHtmlImage( ani )"
-   ]
-  },
-  {
-   "cell_type": "markdown",
-   "metadata": {},
-   "source": [
-    "## B) Evaluation"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
-   "source": [
-    "class ForceCalculationMasks:\n",
-    "    @staticmethod\n",
-    "    def addToBlock(block, blockStorage):\n",
-    "        pdfFieldArr  = field.toArray(block['pdfs'])\n",
-    "        flagFieldArr = field.toArray(block['flags'])[:,:,:,0]\n",
-    "        directions   = block['pdfs'].latticeModel.directions\n",
-    "        maskArr = np.zeros( pdfFieldArr.shape, dtype=bool )\n",
-    "        pdfDirectionArr = np.zeros( list(pdfFieldArr.shape) + [3] )\n",
-    "\n",
-    "        fluidFlag  =  block['flags'].flag(\"fluid\")\n",
-    "        noSlipFlag =  block['flags'].flag(\"NoSlip\")\n",
-    "\n",
-    "        innerPartOfDomain = itertools.product( range(2, maskArr.shape[0]-2),\n",
-    "                                               range(2, maskArr.shape[1]-2),\n",
-    "                                               range(0, maskArr.shape[2]  ) )\n",
-    "\n",
-    "        for x,y,z in innerPartOfDomain:\n",
-    "            if flagFieldArr[x,y,z] & fluidFlag:\n",
-    "                for dirIdx, dir in enumerate(directions):\n",
-    "                    nx, ny, nz = x+dir[0], y+dir[1], z+dir[2]\n",
-    "                    if flagFieldArr[nx,ny,nz] & noSlipFlag:\n",
-    "                        maskArr[x,y,z,dirIdx ] = True\n",
-    "                        pdfDirectionArr[x,y,z,:] = dir\n",
-    "        return ForceCalculationMasks( maskArr, pdfDirectionArr )\n",
-    "\n",
-    "    def __init__(self, maskArr, pdfDirectionArr):\n",
-    "        self._maskArr = maskArr\n",
-    "        self._pdfDirectionArr = pdfDirectionArr\n",
-    "\n",
-    "    def calculateForceOnBoundary(self, pdfField):\n",
-    "        force = np.array([ 0.0 ] * 3)\n",
-    "        pdfFieldArr = field.toArray( pdfField )\n",
-    "        for i in range(3):\n",
-    "            fArr = pdfFieldArr[ self._maskArr ] * self._pdfDirectionArr[self._maskArr,i]\n",
-    "            force[i] += np.sum( fArr )\n",
-    "        return force\n",
-    "\n",
-    "def calculateForceOnBoundary( blocks ):\n",
-    "    force = np.array( [ 0.0 ] * 3 )\n",
-    "    for block in blocks:\n",
-    "        force += block['ForceCalculation'].calculateForceOnBoundary(block['pdfs'])\n",
-    "    return np.array( mpi.reduceReal( force, mpi.SUM ) )\n",
-    "\n",
-    "blocks.addBlockData('ForceCalculation', ForceCalculationMasks.addToBlock)\n",
-    "pass"
-   ]
-  },
-  {
-   "cell_type": "code",
-   "execution_count": null,
-   "metadata": {
-    "collapsed": false
-   },
-   "outputs": [],
-   "source": [
-    "lifts = []\n",
-    "\n",
-    "for i in range(100):\n",
-    "    run( 10 )    \n",
-    "    plt.subplot(2,1,1)\n",
-    "    wplt.scalarField( blocks, 'rho', makeSlice[:,:,0.5] )\n",
-    "    plt.subplot(2,1,2)\n",
-    "    f = calculateForceOnBoundary( blocks )\n",
-    "    lifts.append( f[1] )\n",
-    "    wplt.plot( lifts, color='b' )\n",
-    "    wplt.ylim([0,0.15])\n",
-    "    wplt.xlim(0 , 100 )\n",
-    "    wplt.title(\"Lift\")\n",
-    "    display.display( wplt.gcf() )\n",
-    "    display.clear_output(wait=True) "
-   ]
-  }
- ],
- "metadata": {
-  "kernelspec": {
-   "display_name": "Python 3",
-   "language": "python",
-   "name": "python3"
-  },
-  "language_info": {
-   "codemirror_mode": {
-    "name": "ipython",
-    "version": 3
-   },
-   "file_extension": ".py",
-   "mimetype": "text/x-python",
-   "name": "python",
-   "nbconvert_exporter": "python",
-   "pygments_lexer": "ipython3",
-   "version": "3.5.1"
-  }
- },
- "nbformat": 4,
- "nbformat_minor": 0
-}
diff --git a/python/waLBerla_docs/modules/blockforest.rst b/python/waLBerla_docs/modules/blockforest.rst
index f2ce559e6728edf75e7e7aeb5e29217e1883a8b2..a9ef1d001235d20a0a5150784dbaa182f77d2ad0 100644
--- a/python/waLBerla_docs/modules/blockforest.rst
+++ b/python/waLBerla_docs/modules/blockforest.rst
@@ -8,21 +8,38 @@ Reference
 =========
 
 
-.. py:function:: createUniformBlockGrid(cells, cellsPerBlock, blocks, periodic=(0,0,0), dx=1.0, oneBlockPerProcess=True)
+.. py:function:: createUniformBlockGrid(blocks, cellsPerBlock, dx=1, oneBlockPerProcess=True, periodic=(0,0,0), keepGlobalBlockInformation=False)
    
-   Creates a new uniform StructuredBlockStorage. Similar to cpp function createUniformBlockGridFromConfig.
-   Specify either cells or (cellsPerBlock and blocks). 
-   
-   :param cells:              3-tuple with total numbers of cells in x,y,z direction. The returned BlockStorage may have
-                              more cells if the cell count in a dimension is not divisible by the number of processes.
-                              If this parameter is set, cellsPerBlock and blocks must not be set.
-   :param cellsPerBlock:      3-tuple with total number of cells per block in x,y,z direction.
-                              If this parameter is set, also blocks has to be set, but not cells
-   :param blocks:             3-tuple with total number of blocks in x,y,z direction.
-                              When using this parameter you also have to pass cellsPerBlock.
-   :param periodic:           Periodicity of the domain in x,y,z direction
-   :param dx:                 Side length of a single cell.
-   :param oneBlockPerProcess: If True, each process gets one block. If False, all blocks are put to one process.
-                              The second option makes only sense for debugging or testing.
+   Creates a new uniform StructuredBlockForest. Similar to cpp function blockforest::createUniformBlockGrid.
+   Specify blocks and cellsPerBlock.
+
+   :param blocks:                       3-tuple with total number of blocks in x,y,z direction.
+                                        When using this parameter you also have to pass cellsPerBlock.
+   :param cellsPerBlock:                3-tuple with total number of cells per block in x,y,z direction.
+                                        If this parameter is set, also blocks has to be set, but not cells
+   :param dx:                           Side length of a single cell.
+   :param oneBlockPerProcess:           If True, each process gets one block. If False, all blocks are put to one process.
+                                        The second option makes only sense for debugging or testing.
+   :param periodic:                     Periodicity of the domain in x,y,z direction
+   :param keepGlobalBlockInformation:   If true, each process keeps information about remote blocks (blocks that reside
+                                        on other processes). This information includes the process rank, the state, and
+                                        the axis-aligned bounding box of any block (local or remote). [false by default]
+
+.. py:function:: createUniformBlockGrid(cells, dx=1, oneBlockPerProcess=True, periodic=(0,0,0), keepGlobalBlockInformation=False)
+
+   Creates a new uniform StructuredBlockForest. Similar to cpp function blockforest::createUniformBlockGrid.
+   Specify only number of cells. The distribution on the processes is then calculated automatically.
+
+   :param cells :                       3-tuple with total number of cells in x,y,z direction.
+
+   :param dx:                           Side length of a single cell.
+   :param oneBlockPerProcess:           If True, each process gets one block. If False, all blocks are put to one process.
+                                        The second option makes only sense for debugging or testing.
+   :param periodic:                     Periodicity of the domain in x,y,z direction
+   :param keepGlobalBlockInformation:   If true, each process keeps information about remote blocks (blocks that reside
+                                        on other processes). This information includes the process rank, the state, and
+                                        the axis-aligned bounding box of any block (local or remote). [false by default]
+
+
 
    
\ No newline at end of file
diff --git a/python/waLBerla_docs/modules/core.rst b/python/waLBerla_docs/modules/core.rst
index c837f3a37ef15a50bd7c4dfad5743c4af3e115b8..ee8d985a3b8fe7f3a9582697c87c6afb519c7ac1 100644
--- a/python/waLBerla_docs/modules/core.rst
+++ b/python/waLBerla_docs/modules/core.rst
@@ -27,11 +27,6 @@ Block Structure
 
       Creates a CellInterval from a given Python slice e.g. ``CellInterval.fromSlice( makeSlice[0:2,0:1,0:4] )``
 
-   .. py:method:: __init__( minCell, maxCell )   
-
-      Construct a cell interval given the minimum and maximum coordinate (cell). 
-      The maxCell itself is included in the interval.
-   
    .. py:method:: __init__( xMin, yMin, zMin, xMax, yMax, zMax )
       
       Constructs a cell interval using 6 integers corresponding to begin and end of the 
@@ -186,13 +181,10 @@ Block Structure
   
   
 
-.. py:class:: StructuredBlockStorage
-   
-   StructuredBlockStorage represents a collection of blocks. It is an abstract class 
-   and can not be created directly. A concrete implementation like the blockforest can 
-   instantiate a StructuredBlockStorage. See blockforest.createUniformBlockGrid.
-   
+.. py:class:: StructuredBlockForest
    
+   StructuredBlockForest represents a collection of blocks. It can be created using the createUniformBlockGrid method.
+
    .. py:method:: getNumberOfLevels()
    .. py:method:: getDomain()
    
@@ -201,12 +193,15 @@ Block Structure
    .. py:method:: mapToPeriodicDomain( x,y,z )
    .. py:method:: mapToPeriodicDomain( point )
    .. py:method:: mapToPeriodicDomain( cell, level=0 )
-   
-   
+
    .. py:method:: getBlock( x,y,z )
    .. py:method:: containsGlobalBlockInformation( )
    .. py:method:: blocksOverlappedByAABB( point, aabb )
    .. py:method:: blocksContainedWithinAABB( point, aabb )
+
+   .. py:method:: addBlockData( name, blockdata)
+
+      Adds custom data to the blockforest. This can be a Python Class for example which is then callable on all blocks
    
    .. py:method:: blockExists( point )
    .. py:method:: blockExistsLocally( point )
diff --git a/python/waLBerla_docs/modules/field.rst b/python/waLBerla_docs/modules/field.rst
index 81a9be47e96f4ffc5320f1792d9eefa1cd8729c3..6357f6ea0b829f0df1a37ab7bcd4041cb6ddf744 100644
--- a/python/waLBerla_docs/modules/field.rst
+++ b/python/waLBerla_docs/modules/field.rst
@@ -18,7 +18,7 @@ For details have a look at the C++ documentation: :doxylink:class:`walberla::fie
 Fields and numpy
 ================
 
-waLBerla *fields* can be easily converted to an  :py:class:`numpy.ndarray` using Python's buffer protocol. This means
+waLBerla *fields* can be easily converted to an  :py:class:`numpy.ndarray` using the array functionality of pybind11. This means
 that a *numpy* array and a *field* can share the same memory such that fields can be manipulated using all the 
 power of *numpy*::
 
@@ -27,16 +27,15 @@ power of *numpy*::
    >>> field = waLBerla.field.createField( [3,3,3,1], float )
    >>> field[0,0,0,0]
    0.0
-   >>> npArr = numpy.asarray( field.buffer() )
-   >>> npArr = waLBerla.field.toArray( field ) # convenience function, same as above
+   >>> npArr = numpy.asarray( field )
+   >>> npArr = waLBerla.field.toArray( field, True ) # convenience function, same as above (True includes ghostlayers)
    >>> npArr[:] = 42.0
    >>> field[0,0,0,0]
    42.0
    
 A new *field* is created which is by default initialized with zero. Then a *numpy* array
 is created which shares the same data. After modifying the *numpy* array also the field
-values have changed. To view the field including ghost layers additional parameters to 
-:py:meth:`Field.buffer` are required.
+values have changed.
 
 A common source of error is to forget that some *numpy* functions create a copy of the data.
 The copy is of course not shared with the field anymore::
@@ -49,8 +48,8 @@ The copy is of course not shared with the field anymore::
 When during the array manipulation a copy was created the result has to be copied back into the
 field again. Here the function :py:func:`numpy.copyto` is helpful:::
    
-   >>> numpy.copyto( numpy.asarray( field.buffer() ), npArr )
-   >>> field = waLBerla.field.fromArray( npArr ) # convenience function, equivalent to above
+   >>> numpy.copyto( numpy.asarray( field ), npArr )
+   >>> field = waLBerla.field.toArray( field ) # convenience function, equivalent to above
    >>> field[0,0,0]
    5.0 
 
@@ -67,14 +66,15 @@ Classes
    - Exported from C++ class :doxylink:class:`walberla::field::Field`
    - To modify or access a field class, the most convenient way is to create a *numpy.ndarray* view on it.
    
-   .. py:method:: buffer ( withGhostLayers=False )
+   .. py:method:: __array__
          
-         The returned object implements the Python Buffer Protocol and can be used for example to
+         The returned object implements pybind11::array and can be used for example to
          create a *numpy.ndarray* that shares the same data::
          
-            numpy.asarray( field.buffer(withGhostLayers=True) )
-         
-         The optional parameter specifies if the ghost layers are part of the buffer.
+            numpy.asarray( field )
+
+         With this function all ghostlayers are included in the view on the array.
+         If the fourth dimension is one (this means only one value per cell). The returned numpy array has only 3 dimensions.
          
    
    .. py:method:: swapDataPointers ( otherField )
@@ -122,63 +122,6 @@ Classes
    .. py:attribute:: nrOfGhostLayers
       
       The number of ghostlayers at each border of the field.
-   
-
-.. py:class:: FlagField
-   
-   Subclass of :py:class:`GhostLayerField` where the value type is an unsigned integer and the
-   size of the f coordinate is fixed to one element. FlagFields provide additional management function
-   for storing multiple booleans per cell (encoded in bits). 
-   
-   
-   FlagFields are exported from C++ class :doxylink:class:`walberla::field::FlagField`
-   
-   .. py:method:: registerFlag( flagName, bitNr = None )
-   
-         Reserves the next free bit (if bitNr is None ) or the specified bit using the provided flag name.
-         Returns an integer where the reserved bit is set to one, all other bits are set to zero.
-                  
-   .. py:method:: flag( flagname )
-      
-         Returns an integer where the specified flag is set to one, all other flags are zero.
-      
-   .. py:method:: flagName( flag )
-         
-         Maps from integer where on bit is set to the name of the flag.
-      
-   .. py:attribute:: flags
-          
-          List with registered flag names.
-          
-
-
-.. py:class:: FieldAdaptor
-
-   A field adaptor is an object that emulates a GhostLayerField but does not store data itself.
-   Adaptors can only be created by C++ using :doxylink:class:`walberla::field::GhostLayerFieldAdaptor`.
-   
-   When accessing a cell of an adaptor, its value is computed on the fly based on one or multiple input fields.
-   A VelocityAdaptor, for example, computes the macroscopic velocity in a cell based on a field of particle distribution functions (PDFs).
-   Since adaptor do not hold data themselves they cannot be converted directly to numpy arrays ( see :py:meth:`copyToField` ).
-   Since this operation is expensive consider accessing only the required adaptor values using the getitem operator. 
-   
-   .. py:method:: copyToField ()
-   
-      Creates a field by computing the adaptor value for every cell (potentially expensive). 
-      Returns this temporary field. Modifications of this field
-      do not affect the adaptor or the adaptor base field. 
-   
-    
-   .. py:attribute:: size
-   
-         4-tuple with sizes of (x,y,z,f) coordinates not counting ghost layers 
-    
-   .. py:attribute:: sizeWithGhostLayer
-      
-      4-tuple with sizes of (x,y,z,f) coordinates including ghost layers
-    
-
-
 
 Free Functions
 --------------
@@ -187,52 +130,34 @@ Free Functions
 
    Creates a new GhostLayerField
    
-   :param size:        List of length 3 or 4 specifying x,y,z,f size of the field. 
-                       If list is of length 3 f-size is assumed to be 1
+   :param size:        List of length 4 specifying x,y,z,f size of the field.
    :param type:        Type of the field elements. Valid types are the python types as well as some numpy types:
                         - Integer types: int, numpy.int[8,16,32,64]
                         - Unsigned types: numpy.uint[8,16,32,64]
                         - Float types : float, numpy.float32, numpy.float64
+                        - Bool types : numpy.bool
                         
                        The type mapping is done via the C++ template trait ``walberla::python_coupling::isCppEqualToPythonType``
                        such that custom C++ types can be exported as well.
    :param ghostLayers: number of ghost layers of new field
-   :param layout:       Either array-of-structures ``field.zyxf``  or structure-of-arrays  ``field.fzyx``
-   
-              
-       
-             
+   :param layout:      Either array-of-structures ``field.zyxf``  or structure-of-arrays  ``field.fzyx``
 
-.. py:function:: createFlagField( size, nrOfBits=32, ghostLayers=1 )
-   
-   Creates a new FlagField
-   
-   :param size:        list of length 3 with x,y,z size of field
-   :param nrOfBits:    how many flags can be stored per cell. Allowed values are 8,16,32,64
-   :param ghostLayers: number of ghost layers of new field
 
 
-
-.. note:: The ValueError "Cannot create field of this (type,f-size) combination"
-          means that in C++ this specific choice of type and f-size was not exported to Python.
-          In C++ these are template parameters, so a separate field class has to be instantiated for each
-          combination. 
-
-
-
-
-.. py:function:: addToStorage( blocks, name, type, fSize=1, ghostLayers=1, layout=field.fzyx, initValue=None)
+.. py:function:: addToStorage( blocks, name, dtype, fSize=1, layout=field.fzyx, ghostLayers=1, initValue=0.0, alignment=0)
 
    Adds a GhostLayerField to the given blockStorage
    
-   :param blocks:    the structured blockstorage where the field should be added to
-   :param name:      name of block data, is used to retrieve the created field later on
-   :param initValue: initial value for all cells, if None the types are default initialized (for most types zero)
-   
-   The remaining parameter are the same as in  :py:func:`createField`
-    
-   
-   
+   :param blocks:       the structured blockstorage where the field should be added to
+   :param name:         name of block data, is used to retrieve the created field later on
+   :param dtype:        data type of the field
+   :param fSize:        number of values per cell
+   :param layout:       field.fzyx (SoA) or field.zyxf(AoS)
+   :param ghostLayers:  number of ghost layers of the field
+   :param initValue:    initial value for all cells, if None the types are default initialized (for most types zero)
+   :param alignment:    alignment in bytes of the field vector
+
+
 .. py:function:: gather( blocks, blockDataName, slice, targetRank=0 )
    
    Gathers part of the complete simulation domain (which is distributed to multiple processes)
diff --git a/python/waLBerla_docs/modules/geometry.rst b/python/waLBerla_docs/modules/geometry.rst
deleted file mode 100644
index 0a553af04e1b86821a839a32aa9a34aab3e1e671..0000000000000000000000000000000000000000
--- a/python/waLBerla_docs/modules/geometry.rst
+++ /dev/null
@@ -1,67 +0,0 @@
-***************
-Geometry module
-***************
-
-
-.. py:class:: TriangleMesh
-
-   Corresponds to C++ class :doxylink:class:`walberla::geometry::TriangleMesh`
-
-   .. py:attribute:: numTriangles
-   
-      Number of triangles.
-   
-   .. py:attribute:: numVertices
-   
-      Number of vertices.
-   
-   .. py:attribute:: numVertexNormals
-   
-      Number of vertex normals.
-   
-   .. py:method:: getAABB()
-   
-      Returns the axis aligned bounding box of the mesh.
-      
-   .. py:method:: volume()
-
-      Volume of the Mesh.
-
-   .. py:method:: scale(factor)
-
-      Scales the complete mesh by the given factor.
-
-   .. py:method:: scaleXYZ( factors )
-   
-      Scales the mesh by different factors in x,y,z direction.
-
-      :param factors: tuple or list with 3 entries corresponding to x,y,z factors
-
-   .. py:method:: exchangeAxes( xAxisId, yAxisId, zAxisId )
-
-      Permutes the coordinate order of each vertex. e.g. ``m.exchangeAxes(0,2,1)`` exchanges z and y axis.
-
-
-   .. py:method:: removeDuplicateVertices( tolerance = 1e-4)
-      
-      Merges vertices with a distance smaller than tolerance
-
-
-   .. py:method:: merge( other, offset=(0,0,0 ) )
-      
-      Merges another mesh into the current mesh. During the merging all vertices of the
-      other mesh are shifted by the given offset.
-
-
-   .. py:method:: save( filename )
-      
-      Saves the mesh to a file. The mesh format is deduced using the filename extension.
-      Supported formats are: obj,pov,off and vtp.
-
-   .. py:staticmethod:: load( filename, broadcast=True )
-
-      Loads mesh from a file. The mesh format is deduced using the filename extension.
-      Supported formats are obj, pov and off.
-   
-      :param broadcast: If True the mesh is read on the root system only and broadcasted
-                        to all other processes using MPI to reduce file system load.
diff --git a/python/waLBerla_docs/modules/lbm.rst b/python/waLBerla_docs/modules/lbm.rst
deleted file mode 100644
index 519462ef247cde66a7c4c0613b96d7d27446cf05..0000000000000000000000000000000000000000
--- a/python/waLBerla_docs/modules/lbm.rst
+++ /dev/null
@@ -1,261 +0,0 @@
-**********
-LBM module
-**********
-
-.. note:: This module is deprecated and about to be replaced by the native Python module *lbmpy*
-
-
-Creation Functions
-==================
-
-.. py:function:: makeLatticeModel( stencil, collisionModel, forceModel, compressible, equilibriumAccuracyOrder=2 )
-
-   Creates a new lattice model. A lattice model encapsulates all information about the lattice Boltzmann method.
-
-   :param stencil:                  a string describing the stencil in DxQy notation e.g. 'D2Q9', 'D3Q19', 'D3Q27'
-   :param collisionModel:           an instance of a collision model
-   :param forceModel:               an instance of a force model
-   :param compressible:             choose either a compressible or incompressible LBM scheme
-   :param equilibriumAccuracyOrder: order of the equilibrium distribution. Valid values are 1 and 2. If not sure use 2 here.
-
-
-   .. note ::
-      The collision model and force model object are copied into the lattice model object. Changes to
-      the passed force or collision model do not affect the state of the lattice model.
-      Similarly after a sweep was created with a lattice model, the sweep itself is not changed when the lattice model
-      it was created with was changed.
-
-
-
-.. py:function:: addPdfFieldToStorage( blocks, name, latticeModel, initialVelocity=(0,0,0), initialDensity=1.0, ghostlayers=1, layout=field.zyxf, densityAdaptor="", velocityAdaptor="" )
-                                       
-   Adds a PDFField to the provided blockstorage and optionally a density and velocity adaptor.
-   
-   :param blocks:           blockstorage where the pdf field should be added to
-   :param name:             block data id (string) of the new pdf field
-   :param latticeModel:     see :py:meth:`makeLatticeModel` . The lattice model is copied into the pdf field. 
-                            Later changes to the provided object do not affect the pdf field. To change parameters of 
-                            the lattice model later, one has to iterate over all blocks, get the pdf field and retrieve  
-                            a lattice model reference from it.
-   :param initialVelocity:  lattice velocity the field is initialized with
-   :param initialDensity:   density the field is initialized with
-   :param ghostlayers:      number of ghost layers, has to be at least one
-   :param layout:           memory layout of the field, ( see documentation of field module )
-   :param densityAdaptor:   if a nonempty string is passed a :py:class:`FieldAdaptor` for the density is created with a 
-                            blockdataID of the given name
-   :param velocityAdaptor:  if a nonempty string is passed a :py:class:`FieldAdaptor` for the velocity is created with a 
-                            blockdataID of the given name
-
-
-
-      
-.. py:function:: makeCellwiseSweep( blocks, pdfFieldID, flagFieldID="", flagList=[], velocityFieldID="" )
-
-   Creates a new LBM sweep.
-   
-   :param blocks:          block storage where pdf field ( and if used, the flag field ) are stored
-   :param pdfFieldID:      string identifier of the pdf field
-   :param flagFieldID:     string identifier of the flag field. If empty string is passed, the LBM sweep is executed on all cells. 
-   :param flagList:        Only necessary when a flagFieldID was specified. Pass a list of flag identifiers here,
-                           describing the flags where the sweep should be executed
-   :param velocityFieldID: optional velocity field ( field of fSize=3 and type=float) where the calculated velocity is written to.
-
-
-
-
-.. py:class:: PdfField( field.GhostLayerField )
-   
-   .. py:attribute:: latticeModel:
-   
-   
-   .. py:method:: setDensityAndVelocity( slice, velocity, density )
-   
-   .. py:method:: setToEquilibrium( slice, velocity, density )
-   
-   .. py:method:: getDensity( x,y,z )
-   
-   .. py:method:: getDensitySI ( x,y,z, rho_SI )   
-   
-   .. py:method:: getMomentumDensity ( x,y,z )   
-   
-   .. py:method:: getEquilibriumMomentumDensity ( x,y,z )   
-
-   .. py:method:: getVelocity ( x,y,z )   
-   
-   .. py:method:: getVelocitySI ( x,y,z. dx_SI, dt_SI )
-   
-   .. py:method:: getEquilibriumVelocity ( x,y,z )   
-
-   .. py:method:: getPressureTensor ( x,y,z )   
-   
-   
-
-
-Boundary Handling
-=================
-
-.. py:class:: BoundaryHandling
-
-   .. py:method:: isEmpty( x,y,z )
-   
-   .. py:method:: isNearBoundary( x,y,z )
-   
-   .. py:method:: isBoundary( x,y,z )
-   
-   .. py:method:: isDomain( x,y,z )
-   
-   .. py:method:: setDomain( x, y, z | slice )
-   
-   .. py:method:: forceDomain( x, y, z | slice )
-   
-   .. py:method:: fillWithDomain( x, y, z | slice | nrOfGhostLayersToInclude )
-   
-   .. py:method:: setBoundary( name, x, y, z | name, slice )
-   
-   .. py:method:: forceBoundary( name, x, y, z | name, slice )   
-    
-   .. py:method:: removeDomain( x, y, z | slice | nrOfGhostLayersToInclude )
-      
-   .. py:method:: removeBoundary( x, y, z | slice | nrOfGhostLayersToInclude )
-
-   .. py:method:: clear( x, y, z | slice | nrOfGhostLayersToInclude )
-      
-      
-Collision Models
-================
-
-.. py:class:: collisionModels.SRT
-
-   Single Relaxation Time (BGK) lattice model   
-   
-   .. py:method:: __init__( omega, level=0 )
-
-   
-   .. py:attribute:: omega:
-   
-         Relaxation parameter ( = 1/tau )
-   
-   .. py:attribute:: viscosity:
-   
-   .. py:attribute:: level:
-   
-   .. py:method:: reset( omega, level=0 )
-   
-         Sets a new relaxation parameter for the given level
-
-
-.. py:class:: collisionModels.SRTField( SRT )
-   
-   .. py:method:: __init__( omegaFieldID, level=0 )
-   
-      :param omegaFieldID:  this blockdata has to point to a floating point field of f-size=1 where for each cell
-                            a different omega value is stored. 
-                                  
-   
-
-.. py:class:: collisionModels.TRT
-   
-   .. py:method:: __init__( lambda_e, lambda_d, level=0 )
-   
-   .. staticmethod:: constructWithMagicNumber( omega, magicNumber=3.0/16.0 , level=0 )   
-   
-   .. py:attribute:: lambda_e:
-   
-   .. py:attribute:: lambda_d:
-   
-   .. py:attribute:: viscosity:
-
-   .. py:attribute:: level:
-
-   .. py:method:: reset( lambda_e, lambda_d, level=0 )
-   
-   .. py:method:: resetWithMagicNumber( omega, magicNumber=3.0/16.0 , level=0 )
-   
-
-.. py:class:: collisionModels.D3Q19MRT
-
-   .. py:method:: __init__( s1, s2, s4, s9, s10, s16, level=0 )
-
-   .. staticmethod:: constructTRTWithMagicNumber( omega, magicNumber=3.0/16.0 , level=0 )   
-   
-   .. staticmethod:: constructTRT( lambda_e, lambda_d, level=0 )
-   
-   .. staticmethod:: constructPanWithMagicNumber( omega, magicNumber=3.0/16.0 , level=0 )   
-   
-   .. staticmethod:: constructPan( lambda_e, lambda_d, level=0 )   
-   
-   .. py:attribute:: relaxationRates:
-   
-   .. py:attribute:: viscosity:
-
-   .. py:attribute:: level:
-   
-
-      
-Force Models
-============
-
-.. py:class:: forceModels.NoForce
-
-.. py:class:: forceModels.SimpleConstant
-
-   .. py:method:: __init__( force, level=0 )
-
-.. py:class:: forceModels.EDMField
-
-   .. py:method:: __init__( forceFieldID )
-
-.. py:class:: forceModels.LuoConstant
-
-   .. py:method:: __init__( force, level=0 )
-
-.. py:class:: forceModels.LuoField
-
-   .. py:method:: __init__( forceFieldID )
-
-.. py:class:: forceModels.GuoConstant
-
-   .. py:method:: __init__( force, level=0 )
-
-.. py:class:: forceModels.Correction
-
-   .. py:method:: __init__( previousMomentumDensityFieldID )
-   
-   
-Lattice Models
-==============
-
-.. py:class:: LatticeModel
-
-      Lattice models are created with the function :func:`makeLatticeModel` and encapsulate information about
-      stencil, collision operator and force model.
-      This information can be accessed through the following read-only attributes.
-
-      .. py:attribute:: collisionModel:
-
-            a *copy* of the collision model
-
-      .. py:attribute:: forceModel:
-
-            a *copy* of the force model
-
-      .. py:attribute:: compressible:
-
-            boolean signaling a compressible model
-
-      .. py:attribute:: stencilName:
-
-            a string describing the stencil in *DxQy* notation
-
-      .. py:attribute:: communicationStencilName:
-
-            name of stencil that should be used for communication. In most cases this is the same as stencilName
-
-      .. py:attribute:: directions:
-
-            a list of tuples containing the directions of the stencil. e.g. (0,0,0) for center, (1,0,0) for east etc.
-            For a DxQy stencil the list as y entries, the tuples are of length x.
-
-
-
-
diff --git a/python/waLBerla_tests/test_blockforest.py b/python/waLBerla_tests/test_blockforest.py
index 59e0057b237aaeb497b33e833430bfdbfeafb2b1..09a15815136861381a5a6b2a3c0409930a7164ee 100644
--- a/python/waLBerla_tests/test_blockforest.py
+++ b/python/waLBerla_tests/test_blockforest.py
@@ -1,36 +1,79 @@
 import unittest
-from waLBerla import field, createUniformBlockGrid
+import numpy as np
+from waLBerla import field, createUniformBlockGrid, AABB
 
 
 class BlockforestModuleTest(unittest.TestCase):
 
     def testMemoryManagement1(self):
         """Testing correct reference counting of block data"""
-        blocks = createUniformBlockGrid(cells=(2, 2, 2))
-        field.addToStorage(blocks, "TestField", float)
-        f = blocks[0]['TestField']
-        stridesBefore = f.strides
+        blocks = createUniformBlockGrid(blocks=(1, 1, 1), cellsPerBlock=(2, 2, 2))
+        field.addToStorage(blocks, "TestField", np.float64)
+        f = blocks[0]["TestField"]
+        strides_before = f.strides
         del blocks
         # create another block structure - this has triggered segfault
         # when previous blockstructure was already freed
-        blocks = createUniformBlockGrid(cells=(2, 2, 2))  # noqa: F841
+        blocks = createUniformBlockGrid(blocks=(1, 1, 1), cellsPerBlock=(2, 2, 2))  # noqa: F841
 
         # The first block structure must exist here, since we hold a reference to block data
         # if it would have been deleted already f.strides should lead to segfault or invalid values
-        self.assertEqual(stridesBefore, f.strides)
+        self.assertEqual(strides_before, f.strides)
 
     def testMemoryManagement2(self):
         """Testing correct reference counting of block data
            Holding only a numpy array pointing to a waLBerla field should still hold the blockstructure alive"""
-        blocks = createUniformBlockGrid(cells=(2, 2, 2))
-        field.addToStorage(blocks, "TestField", float)
-        npf = field.toArray(blocks[0]['TestField'])
-        npf[:, :, :, :] = 42.0
+        blocks = createUniformBlockGrid(blocks=(1, 1, 1), cellsPerBlock=(2, 2, 2))
+        field.addToStorage(blocks, "TestField", np.float64)
+        npf = field.toArray(blocks[0]["TestField"])
+        npf[:, :, :] = 42.0
         del blocks
         # create another block structure - this has triggered segfault
         # when previous blockstructure was already freed
-        blocks = createUniformBlockGrid(cells=(2, 2, 2))  # noqa: F841
-        self.assertEqual(npf[0, 0, 0, 0], 42.0)
+        blocks = createUniformBlockGrid(blocks=(1, 1, 1), cellsPerBlock=(2, 2, 2))  # noqa: F841
+        self.assertEqual(npf[0, 0, 0], 42.0)
+
+    def testMemoryManagement3(self):
+        """Same as testMemoryManagement2, but with iterators"""
+        blocks = createUniformBlockGrid(blocks=(1, 1, 1), cellsPerBlock=(2, 2, 2))
+        field.addToStorage(blocks, "TestField", np.float64)
+        for block in blocks:
+            for name in block.fieldNames:
+                if name == "TestField":
+                    f = block[name]
+                    npf = field.toArray(f)
+        npf[:, :, :] = 42.0
+        del blocks, block, name, f
+        blocks = createUniformBlockGrid(blocks=(1, 1, 1), cellsPerBlock=(2, 2, 2))  # noqa: F841
+        self.assertEqual(npf[0, 0, 0], 42.0)
+
+    def testExceptions(self):
+        """Check that the right exceptions are thrown when nonexistent or non-convertible fields are accessed"""
+        blocks = createUniformBlockGrid(blocks=(1, 1, 1), cellsPerBlock=(2, 2, 2))
+        with self.assertRaises(ValueError) as cm:
+            blocks[0]["cell bounding box"]
+        self.assertEqual(str(cm.exception), "This blockdata is not accessible from Python")
+        with self.assertRaises(IndexError) as cm:
+            blocks[0]["nonexistent"]
+        self.assertEqual(str(cm.exception), "No blockdata with the given name found")
+
+    def testGeneralFunctionality(self):
+        blocks = createUniformBlockGrid(blocks=(1, 1, 1), cellsPerBlock=(2, 2, 2))
+        self.assertEqual(blocks.getNumberOfLevels(), 1)
+
+        aabb = blocks.getDomain
+        aabb2 = AABB(1.0, 1.0, 1.0, 1.2, 1.2, 1.2)
+
+        self.assertEqual(aabb.min, (0.0, 0.0, 0.0))
+        self.assertEqual(aabb.max, (2.0, 2.0, 2.0))
+        self.assertEqual(aabb.size, (2.0, 2.0, 2.0))
+        self.assertEqual(aabb.empty, False)
+        self.assertEqual(aabb.volume, 8.0)
+        self.assertEqual(aabb.center, (1.0, 1.0, 1.0))
+        self.assertEqual(aabb.contains(aabb2), True)
+        self.assertEqual(aabb2.contains(aabb), False)
+        self.assertEqual(aabb2.contains((1.2, 1.2, 1.2)), False)
+        self.assertEqual(aabb2.contains((1.1, 1.1, 1.1)), True)
 
 
 if __name__ == '__main__':
diff --git a/python/waLBerla_tests/test_core.py b/python/waLBerla_tests/test_core.py
index 7d59da398675da8085ace556b7dacbdada294b4a..7a286876e2c10bfd151ad5a0d0b9542a911ccd00 100644
--- a/python/waLBerla_tests/test_core.py
+++ b/python/waLBerla_tests/test_core.py
@@ -6,7 +6,7 @@ class CoreTest(unittest.TestCase):
 
     def test_CellInterval(self):
         ci1 = wlb.CellInterval(0, 0, 0, 5, 5, 5)
-        ci2 = wlb.CellInterval([0] * 3, [5] * 3)
+        ci2 = wlb.CellInterval(0, 0, 0, 5, 5, 5)
         self.assertEqual(ci1, ci2, "Equality comparison of CellIntervals failed.")
         self.assertFalse(ci1 != ci2, "Inequality check for CellIntervals wrong ")
 
@@ -23,7 +23,7 @@ class CoreTest(unittest.TestCase):
 
     def test_AABB(self):
         aabb1 = wlb.AABB(0, 0, 0, 5, 5, 5)
-        aabb2 = wlb.AABB([0] * 3, [5] * 3)
+        aabb2 = wlb.AABB(0, 0, 0, 5, 5, 5)
         self.assertEqual(aabb1, aabb2)
 
 
diff --git a/python/waLBerla_tests/test_cuda_comm.py b/python/waLBerla_tests/test_cuda_comm.py
deleted file mode 100644
index 3ef61ee304c90561dba7114b1e72775ff8b26014..0000000000000000000000000000000000000000
--- a/python/waLBerla_tests/test_cuda_comm.py
+++ /dev/null
@@ -1,25 +0,0 @@
-from waLBerla import field, createUniformBlockGrid, createUniformBufferedScheme, cuda
-import numpy as np
-import pycuda.autoinit  # noqa: F401
-import pycuda.gpuarray as gpuArr
-# from pycuda import *
-from pystencils.field import createNumpyArrayWithLayout, getLayoutOfArray
-
-blocks = createUniformBlockGrid(cells=(1, 1, 1), periodic=(1, 1, 1))
-cuda.addGpuFieldToStorage(blocks, "gpuField", float, fSize=1, ghostLayers=1, layout=field.fzyx, usePitchedMem=False)
-
-gpuArr = cuda.toGpuArray(blocks[0]['gpuField'])  # noqa: F811
-
-testField = createNumpyArrayWithLayout(gpuArr.shape, getLayoutOfArray(gpuArr))
-testField[...] = 0
-testField[1, 1, 1, 0] = 1
-gpuArr.set(testField)
-
-scheme = createUniformBufferedScheme(blocks, "D3Q27")
-scheme.addDataToCommunicate(cuda.createPackInfo(blocks, "gpuField"))
-
-scheme()
-
-gpuArr = cuda.toGpuArray(blocks[0]['gpuField'])
-
-assert (np.allclose(np.ones([3, 3, 3, 1]), gpuArr.get()))
diff --git a/python/waLBerla_tests/test_field.py b/python/waLBerla_tests/test_field.py
index 9f984b3e01117448ba25f7c9d2ea358c8ef67e1c..04ab5afb43512619654425d04e40ca99c56f0238 100644
--- a/python/waLBerla_tests/test_field.py
+++ b/python/waLBerla_tests/test_field.py
@@ -1,59 +1,82 @@
 import unittest
+import numpy as np
+import waLBerla as wlb
 from waLBerla import field, createUniformBlockGrid
 
 
 class FieldModuleTest(unittest.TestCase):
 
     def testFieldAsBlockData(self):
-        blocks = createUniformBlockGrid(cells=(3, 2, 2), periodic=(1, 0, 0))
-        field.addToStorage(blocks, 'myField', float, fSize=3, ghostLayers=0, initValue=0.0)
-        myField = blocks[0]['myField']
-        self.assertEqual(myField[0, 0, 0, 0], 0)
-        myField[0, 0, 0, 0] = 42.0
-        self.assertEqual(myField[0, 0, 0, 0], 42.0)
+        blocks = createUniformBlockGrid(blocks=(1, 1, 1), cellsPerBlock=(3, 2, 2), periodic=(True, False, False))
+        field.addToStorage(blocks, 'myField', np.float64, fSize=3, ghostLayers=0, initValue=0.0)
+        my_field = wlb.field.toArray(blocks[0]['myField'])
+        self.assertEqual(my_field[0, 0, 0, 0], 0)
+        my_field[0, 0, 0, 0] = 42.0
+        self.assertEqual(my_field[0, 0, 0, 0], 42.0)
 
-        self.assertRaises(IndexError, myField.__getitem__, (3, 0, 0))
+        self.assertRaises(IndexError, my_field.__getitem__, (3, 0, 0))
 
     def testNumpyConversionWithoutGhostLayers(self):
-        f1 = field.createField([1, 2, 3, 4], float, 2, field.zyxf)
-        f2 = field.createField([1, 2, 3, 5], float, 4, field.zyxf)
-        f1np = field.toArray(f1)
-        f2np = field.toArray(f2)
-        self.assertEqual(f1np[0, 0, 0, 0], 0)
-        self.assertEqual(f1np.shape, (1, 2, 3, 4))
-        self.assertEqual(f2np.shape, (1, 2, 3, 5))
-
-        f1np[0, 0, 0, 0] = 1
-        f2np[0, 0, 0, 0] = 2
-        self.assertEqual(f1[0, 0, 0, 0], 1)
-        self.assertEqual(f2[0, 0, 0, 0], 2)
-
-    def testNumpyConversionWithGhostLayers(self):
-        f = field.createField([1, 2, 3, 1], float, 2, field.zyxf)
-        fnp = field.toArray(f, withGhostLayers=True)
-
-        self.assertEqual(fnp[0, 0, 0, 0], 0)
-        self.assertEqual(fnp.shape, (1 + 4, 2 + 4, 3 + 4, 1))
-        fnp[0, 0, 0, 0] = 42
-        self.assertEqual(f[-2, -2, -2, 0], 42)
+        blocks = createUniformBlockGrid(blocks=(1, 1, 1), cellsPerBlock=(1, 2, 3), periodic=(True, False, False))
+        field.addToStorage(blocks, 'f1', np.float64, fSize=2, ghostLayers=0, initValue=2.0)
+        field.addToStorage(blocks, 'f2', np.float64, fSize=3, ghostLayers=0, initValue=2.0)
+
+        f1np = field.toArray(blocks[0]['f1'])
+        f2np = field.toArray(blocks[0]['f2'])
+        self.assertEqual(f1np[0, 0, 0, 0], 2.0)
+        self.assertEqual(f1np.shape, (1, 2, 3, 2))
+        self.assertEqual(f2np.shape, (1, 2, 3, 3))
 
     def testGhostLayerExtraction(self):
-        size = [10, 5, 4]
+        size = (10, 5, 4)
         gl = 3
-        f = field.createField(size, float, ghostLayers=gl)
+        blocks = createUniformBlockGrid(blocks=(1, 1, 1), cellsPerBlock=size, periodic=(True, False, False))
+        field.addToStorage(blocks, 'f', np.float64, fSize=3, ghostLayers=gl, initValue=0.0)
+
+        f = blocks[0]['f']
 
-        view1 = field.toArray(f, withGhostLayers=True)
+        view1 = field.toArray(f, with_ghost_layers=True)
         self.assertEqual(view1[:, :, :, 0].shape, tuple([s + 2 * gl for s in size]))
 
-        view2 = field.toArray(f, withGhostLayers=False)
+        view2 = field.toArray(f, with_ghost_layers=False)
         self.assertEqual(view2[:, :, :, 0].shape, tuple(size))
 
-        view3 = field.toArray(f, withGhostLayers=2)
+        view3 = field.toArray(f, with_ghost_layers=2)
         self.assertEqual(view3[:, :, :, 0].shape, tuple([s + 2 * 2 for s in size]))
 
-        view4 = field.toArray(f, withGhostLayers=[2, False, True])
+        view4 = field.toArray(f, with_ghost_layers=[2, False, True])
         self.assertEqual(view4[:, :, :, 0].shape, tuple([size[0] + 2 * 2, size[1] + 2 * 0, size[2] + 2 * gl]))
 
+    def test_gather(self):
+        blocks = createUniformBlockGrid(blocks=(10, 4, 3), cellsPerBlock=(2, 2, 2),
+                                        periodic=(True, True, True), oneBlockPerProcess=False)
+        wlb.field.addToStorage(blocks, "test", dtype=np.int, fSize=3)
+
+        for block in blocks:
+            offset_in_global_domain = blocks.transformLocalToGlobal(block, wlb.Cell(0, 0, 0))[:]
+            f = wlb.field.toArray(block["test"])
+            s = f.shape
+            for i in range(0, s[0]):
+                for j in range(0, s[1]):
+                    for k in range(0, s[2]):
+                        f[i, j, k, 0] = i + offset_in_global_domain[0]
+                        f[i, j, k, 1] = j + offset_in_global_domain[1]
+                        f[i, j, k, 2] = k + offset_in_global_domain[2]
+
+        tp = tuple([slice(5, 15), slice(None, None), 0.5])
+        f = wlb.field.gather(blocks, "test", tp)
+
+        nparray = wlb.field.toArray(f)
+        self.assertEqual(nparray.shape, (11, 8, 1, 3))
+
+        s = nparray.shape
+        for i in range(0, s[0]):
+            for j in range(0, s[1]):
+                for k in range(0, s[2]):
+                    assert(nparray[i, j, k, 0] == i + 5)
+                    assert(nparray[i, j, k, 1] == j)
+                    assert(nparray[i, j, k, 2] == 2)
+
 
 if __name__ == '__main__':
     unittest.main()
diff --git a/python/waLBerla_tests/test_simpleLBM.py b/python/waLBerla_tests/test_simpleLBM.py
deleted file mode 100644
index defe348eb9a476dfea959b817c9059cad34ee539..0000000000000000000000000000000000000000
--- a/python/waLBerla_tests/test_simpleLBM.py
+++ /dev/null
@@ -1,198 +0,0 @@
-from waLBerla import makeSlice, field, mpi, lbm, createUniformBlockGrid, createUniformBufferedScheme
-from waLBerla.geometry_setup import setBoundaryFromBlackAndWhiteImage, setFieldUsingFlagMask
-import itertools
-
-import os
-import numpy as np
-import scipy
-
-imageFile = os.path.join(os.path.dirname(__file__), 'wing.png')
-
-
-def setBoundariesChannel(blocks, boundaryHandlingID):
-    for block in blocks:
-        b = block[boundaryHandlingID]
-        if block.atDomainMinBorder[1]:
-            b.forceBoundary('NoSlip', makeSlice[:, 0, :, 'g'])
-        if block.atDomainMaxBorder[1]:
-            b.forceBoundary('NoSlip', makeSlice[:, -1, :, 'g'])
-        b.fillWithDomain()
-
-
-class ForceCalculationMasks:
-    @staticmethod
-    def addToBlock(block, blockStorage):
-        pdfFieldArr = field.toArray(block['pdfs'])
-        flagFieldArr = field.toArray(block['flags'])[:, :, :, 0]
-        directions = block['pdfs'].latticeModel.directions
-        maskArr = np.zeros(pdfFieldArr.shape, dtype=bool)
-        pdfDirectionArr = np.zeros(list(pdfFieldArr.shape) + [3])
-
-        nearBoundaryFlag = block['flags'].flag("fluid")
-        noSlipFlag = block['flags'].flag("NoSlip")
-
-        innerPartOfDomain = itertools.product(range(2, maskArr.shape[0] - 2),
-                                              range(2, maskArr.shape[1] - 2),
-                                              range(maskArr.shape[2]))
-
-        for x, y, z in innerPartOfDomain:
-            if flagFieldArr[x, y, z] & nearBoundaryFlag:
-                for dirIdx, dir in enumerate(directions):
-                    nx, ny, nz = x + dir[0], y + dir[1], z + dir[2]
-                    if flagFieldArr[nx, ny, nz] & noSlipFlag:
-                        maskArr[x, y, z, dirIdx] = True
-                        pdfDirectionArr[x, y, z, :] = dir
-        return ForceCalculationMasks(maskArr, pdfDirectionArr)
-
-    def __init__(self, maskArr, pdfDirectionArr):
-        self._maskArr = maskArr
-        self._pdfDirectionArr = pdfDirectionArr
-
-    def calculateForceOnBoundary(self, pdfField):
-        force = np.array([0.0] * 3)
-        pdfFieldArr = field.toArray(pdfField)
-        for i in range(3):
-            fArr = pdfFieldArr[self._maskArr] * self._pdfDirectionArr[self._maskArr, i]
-            force[i] += np.sum(fArr)
-        return force
-
-
-def calculateForceOnBoundary(blocks):
-    force = np.array([0.0] * 3)
-    for block in blocks:
-        force += block['ForceCalculation'].calculateForceOnBoundary(block['pdfs'])
-    return np.array(mpi.reduceReal(force, mpi.SUM))
-
-
-def makeNacaAirfoilImage(domainSize, thicknessInPercent=30, angle=0):
-    def nacaAirfoil(x, thicknessInPercent, chordLength):
-        xOverC = x / chordLength
-        y_t = 0
-        coeffs = [0.2969, -0.1260, - 0.3516, 0.2843, -0.1015]
-        for coeff, exponent in zip(coeffs, [0.5, 1, 2, 3, 4]):
-            y_t += coeff * xOverC ** exponent
-        y_t *= 5 * thicknessInPercent / 100 * chordLength
-        return y_t
-
-    domain = np.zeros(domainSize)
-    it = np.nditer(domain, flags=['multi_index'], op_flags=['readwrite'])
-    while not it.finished:
-        x, y = it.multi_index
-        y -= domain.shape[1] / 2
-        if abs(y) < nacaAirfoil(x, thicknessInPercent, domain.shape[0]):
-            it[0] = 1
-        it.iternext()
-    domain = np.rot90(domain, 1)
-    domain = scipy.ndimage.interpolation.rotate(domain, angle=angle)
-
-    domain[domain > 0.5] = 1
-    domain[domain <= 0.5] = 0
-    domain = domain.astype(np.int32)
-    return domain
-
-
-img = makeNacaAirfoilImage([300, 300], 30, angle=-30)
-
-omega = 1.9
-blocks = createUniformBlockGrid(cells=(500, 200, 1), periodic=(1, 0, 1))
-
-collisionModel = lbm.collisionModels.SRT(omega)
-forceModel = lbm.forceModels.SimpleConstant((1e-5, 0, 0))
-latticeModel = lbm.makeLatticeModel("D2Q9", collisionModel, forceModel)
-lbm.addPdfFieldToStorage(blocks, "pdfs", latticeModel, velocityAdaptor="vel", densityAdaptor="rho", initialDensity=1.0)
-field.addFlagFieldToStorage(blocks, 'flags')
-lbm.addBoundaryHandlingToStorage(blocks, 'boundary', 'pdfs', 'flags')
-
-# setBoundaryFromArray( blocks, 'boundary', makeSlice[0.4:0.6, 0.4:0.55 ,0.5], img, { 1: 'NoSlip' } )
-setBoundaryFromBlackAndWhiteImage(blocks, "boundary", makeSlice[0.25:0.75, 0.3:0.6, 0.5], imageFile, "NoSlip")
-setBoundariesChannel(blocks, 'boundary')
-
-blocks.addBlockData('ForceCalculation', ForceCalculationMasks.addToBlock)
-
-sweep = lbm.makeCellwiseSweep(blocks, "pdfs", flagFieldID='flags', flagList=['fluid'])
-
-scheme = createUniformBufferedScheme(blocks, 'D3Q19')
-scheme.addDataToCommunicate(field.createPackInfo(blocks, 'pdfs'))
-
-
-def timestep():
-    scheme()
-    for block in blocks:
-        block['boundary']()
-    for block in blocks:
-        sweep.streamCollide(block)
-    return calculateForceOnBoundary(blocks)
-
-
-def run(timesteps):
-    for t in range(timesteps):
-        scheme()
-        for block in blocks:
-            block['boundary']()
-        for block in blocks:
-            sweep.streamCollide(block)
-
-
-def makeAnimation(blocks, interval=30, frames=180):
-    import matplotlib.pyplot as plt
-    import matplotlib.animation as animation
-
-    plt.style.use('ggplot')
-    NR_OF_TIMESTEPS_SHOWN = 600
-    lifts = []
-
-    fig = plt.gcf()
-    f = field.gather(blocks, 'rho', makeSlice[:, :, 0.5])
-    im = None
-
-    ymax = [0.05]
-    if f:
-        npField = field.toArray(f).squeeze()
-        npField = np.rot90(npField, 1)
-
-        plt.subplot(2, 1, 1)
-        plt.title("Lattice Density")
-        im = plt.imshow(npField)
-        plt.colorbar()
-
-        plt.subplot(2, 1, 2)
-        plt.title("Lift")
-        plt.ylim(0, ymax[0])
-        plt.xlim(0, NR_OF_TIMESTEPS_SHOWN)
-        liftPlot, = plt.plot(lifts)
-
-    def updatefig(*args):
-        force = timestep()
-        f = field.gather(blocks, 'rho', makeSlice[:, :, 0.5])
-        if f:
-            npField = field.toArray(f).squeeze()
-            npField = np.rot90(npField, 1)
-            im.set_array(npField)
-            im.autoscale()
-            if lifts and max(lifts) * 1.2 > ymax[0]:
-                ymax[0] = max(lifts) * 1.2
-                liftPlot.axes.set_ylim(0, ymax[0])
-
-            lifts.append(force[1])
-            nrOfSamples = len(lifts)
-            xMin = max(0, nrOfSamples - NR_OF_TIMESTEPS_SHOWN)
-            liftPlot.axes.set_xlim(xMin, xMin + NR_OF_TIMESTEPS_SHOWN)
-            liftPlot.set_data(np.arange(nrOfSamples), lifts)
-            return im, liftPlot
-
-    return animation.FuncAnimation(fig, updatefig, interval=interval, frames=frames, blit=False, repeat=False)
-
-
-showPlots = False
-
-if showPlots:
-    import waLBerla.plot as wplt
-
-    setFieldUsingFlagMask(blocks, 'pdfs', np.NaN, 'flags', ['NoSlip'])
-    run(1)
-    setFieldUsingFlagMask(blocks, 'pdfs', np.NaN, 'flags', ['NoSlip'])
-
-    ani = makeAnimation(blocks, frames=6000, )
-    wplt.show()
-else:
-    run(10)
diff --git a/python/waLBerla_tests/tools/test_lbm_unitconversion.py b/python/waLBerla_tests/tools/test_lbm_unitconversion.py
index 72d87aa3518fcb5c513b38b70c33daa3f8bd2103..7631b5776068a91e9de48e8d9b98ac108d2dd6d4 100644
--- a/python/waLBerla_tests/tools/test_lbm_unitconversion.py
+++ b/python/waLBerla_tests/tools/test_lbm_unitconversion.py
@@ -5,9 +5,9 @@ class UnitConversionTest(unittest.TestCase):
 
     def testExtractLatticeFactors(self):
         try:
-            import pint  # noqa: F401
+            import pint, sympy  # noqa: F401
         except ImportError:
-            print("Skipping unit conversion test since pint module not available")
+            print("Skipping unit conversion test since pint or sympy module not available")
             return
 
         from waLBerla.tools.lbm_unitconversion import extractLatticeFactors, computeLatticeFactors
@@ -28,9 +28,9 @@ class UnitConversionTest(unittest.TestCase):
 
     def testUnitConverter(self):
         try:
-            import pint  # noqa: F401
+            import pint, sympy  # noqa: F401
         except ImportError:
-            print("Skipping unit conversion test since pint module not available")
+            print("Skipping unit conversion test since pint or sympy module not available")
             return
 
         from waLBerla.tools.lbm_unitconversion import PintUnitConverter
diff --git a/python/waLBerla_tests/wing.png b/python/waLBerla_tests/wing.png
deleted file mode 100644
index f51549c70bf0f33840c03536577b4e8591eec70a..0000000000000000000000000000000000000000
Binary files a/python/waLBerla_tests/wing.png and /dev/null differ
diff --git a/src/blockforest/AABBRefinementSelection.h b/src/blockforest/AABBRefinementSelection.h
index fc454bedc4de777788d6b4a25ceb202bf950a373..45847dca596dc84415629404a8ac622684a3e8ec 100644
--- a/src/blockforest/AABBRefinementSelection.h
+++ b/src/blockforest/AABBRefinementSelection.h
@@ -40,7 +40,7 @@ class AABBRefinementSelection
 {
 public:
 
-   AABBRefinementSelection(){}
+   AABBRefinementSelection()= default;
 
    AABBRefinementSelection( const Config::BlockHandle & configBlock )
    {
@@ -74,12 +74,12 @@ public:
 
    void addAABB( const math::AABB & aabb, const uint_t level )
    {
-      aabbs_.push_back( std::make_pair( aabb, level ) );
+      aabbs_.emplace_back( aabb, level );
    }
 
    void addRegion( const math::AABB & region, const uint_t level )
    {
-      regions_.push_back( std::make_pair( region, level ) );
+      regions_.emplace_back( region, level );
    }
 
    // for static refinement
@@ -144,12 +144,12 @@ private:
       std::vector< std::pair< math::AABB, uint_t > > aabbs;
       for( auto region = regions_.begin(); region != regions_.end(); ++region )
       {
-         aabbs.push_back( std::make_pair( math::AABB( simulationDomain.xMin() + region->first.xMin() * simulationDomain.xSize(),
-                                                      simulationDomain.yMin() + region->first.yMin() * simulationDomain.ySize(),
-                                                      simulationDomain.zMin() + region->first.zMin() * simulationDomain.zSize(),
-                                                      simulationDomain.xMin() + region->first.xMax() * simulationDomain.xSize(),
-                                                      simulationDomain.yMin() + region->first.yMax() * simulationDomain.ySize(),
-                                                      simulationDomain.zMin() + region->first.zMax() * simulationDomain.zSize() ), region->second ) );
+         aabbs.emplace_back( math::AABB( simulationDomain.xMin() + region->first.xMin() * simulationDomain.xSize(),
+                                         simulationDomain.yMin() + region->first.yMin() * simulationDomain.ySize(),
+                                         simulationDomain.zMin() + region->first.zMin() * simulationDomain.zSize(),
+                                         simulationDomain.xMin() + region->first.xMax() * simulationDomain.xSize(),
+                                         simulationDomain.yMin() + region->first.yMax() * simulationDomain.ySize(),
+                                         simulationDomain.zMin() + region->first.zMax() * simulationDomain.zSize() ), region->second );
       }
       return aabbs;
    }
diff --git a/src/blockforest/Block.h b/src/blockforest/Block.h
index 39d958b7e8ecad45bd56d7113f1eebb8412fb41f..64c7dafa70efecb428807de242ddf165e4417023 100644
--- a/src/blockforest/Block.h
+++ b/src/blockforest/Block.h
@@ -79,14 +79,14 @@ public:
    Block( BlockForest & forest, const BlockID & id, const AABB & aabb, const uint_t level, mpi::RecvBuffer & buffer,
           const std::function< uint_t ( const uint_t ) > & processMapping = std::function< uint_t ( const uint_t ) >() );
 
-   virtual ~Block() {}
+   ~Block() override = default;
 
    void toBuffer( mpi::SendBuffer & buffer ) const;
 
    const BlockForest & getForest() const { return forest_; }
          BlockForest & getForest()       { return forest_; }
 
-   const BlockID & getId()      const { return id_; }
+   const BlockID & getId()      const override { return id_; }
          uint_t    getProcess() const;
          uint_t    getLevel()   const { return level_; }
 
@@ -135,7 +135,7 @@ public:
 
 protected:
 
-   bool equal( const IBlock* rhs ) const;
+   bool equal( const IBlock* rhs ) const override;
 
 private:
 
@@ -296,7 +296,7 @@ inline void Block::addNeighbor( const BlockID & id, const uint_t process, const
       WALBERLA_ASSERT( neighborhood_[i].getId() < id || id < neighborhood_[i].getId() );
 #endif
 
-   neighborhood_.push_back( NeighborBlock( forest_, id, process, state ) );
+   neighborhood_.emplace_back( forest_, id, process, state );
 }
 
 
@@ -366,7 +366,7 @@ inline void Block::setTargetLevel( const uint_t tl )
 
 } // namespace blockforest
 
-typedef blockforest::Block Block;
+using Block = blockforest::Block;
 
 } // namespace walberla
 
diff --git a/src/blockforest/BlockDataHandling.h b/src/blockforest/BlockDataHandling.h
index c947b3e032de0a01bf727a30e86d81ff23c035f5..7f56467c06b9eebf033753847020843a14a264c3 100644
--- a/src/blockforest/BlockDataHandling.h
+++ b/src/blockforest/BlockDataHandling.h
@@ -36,7 +36,7 @@ template< typename T >
 class BlockDataHandling : public domain_decomposition::BlockDataHandling<T>
 {
 public:
-   virtual ~BlockDataHandling() {}
+   ~BlockDataHandling() override = default;
 
    /// must be thread-safe !
    virtual void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child ) = 0;
@@ -60,19 +60,19 @@ template< typename T >
 class AlwaysInitializeBlockDataHandling : public BlockDataHandling<T>
 {
 public:
-   ~AlwaysInitializeBlockDataHandling() {}
+   ~AlwaysInitializeBlockDataHandling() override = default;
 
-   void serialize( IBlock * const, const BlockDataID &, mpi::SendBuffer & ) {}
-   void serializeCoarseToFine( Block * const, const BlockDataID &, mpi::SendBuffer &, const uint_t ) {}
-   void serializeFineToCoarse( Block * const, const BlockDataID &, mpi::SendBuffer & ) {}
+   void serialize( IBlock * const, const BlockDataID &, mpi::SendBuffer & ) override {}
+   void serializeCoarseToFine( Block * const, const BlockDataID &, mpi::SendBuffer &, const uint_t ) override {}
+   void serializeFineToCoarse( Block * const, const BlockDataID &, mpi::SendBuffer & ) override {}
 
-   T * deserialize( IBlock * const block ) { return this->initialize( block ); }
-   T * deserializeCoarseToFine( Block * const block ) { return this->initialize( block ); }
-   T * deserializeFineToCoarse( Block * const block ) { return this->initialize( block ); }
+   T * deserialize( IBlock * const block ) override { return this->initialize( block ); }
+   T * deserializeCoarseToFine( Block * const block ) override { return this->initialize( block ); }
+   T * deserializeFineToCoarse( Block * const block ) override { return this->initialize( block ); }
 
-   void deserialize( IBlock * const, const BlockDataID &, mpi::RecvBuffer & ) {}
-   void deserializeCoarseToFine( Block * const, const BlockDataID &, mpi::RecvBuffer & ) {}
-   void deserializeFineToCoarse( Block * const, const BlockDataID &, mpi::RecvBuffer &, const uint_t ) {}
+   void deserialize( IBlock * const, const BlockDataID &, mpi::RecvBuffer & ) override {}
+   void deserializeCoarseToFine( Block * const, const BlockDataID &, mpi::RecvBuffer & ) override {}
+   void deserializeFineToCoarse( Block * const, const BlockDataID &, mpi::RecvBuffer &, const uint_t ) override {}
 };
 
 
@@ -81,9 +81,9 @@ template< typename T >
 class AlwaysCreateBlockDataHandling : public AlwaysInitializeBlockDataHandling<T>
 {
 public:
-   ~AlwaysCreateBlockDataHandling() {}
+   ~AlwaysCreateBlockDataHandling() override = default;
 
-   T * initialize( IBlock * const /*block*/ ) {return new T();}
+   T * initialize( IBlock * const /*block*/ ) override {return new T();}
 };
 
 
@@ -95,9 +95,9 @@ namespace internal {
 class BlockDataHandlingWrapper : public domain_decomposition::internal::BlockDataHandlingWrapper
 {
 public:
-   typedef domain_decomposition::internal::BlockData BlockData;
+   using BlockData = domain_decomposition::internal::BlockData;
 
-   virtual ~BlockDataHandlingWrapper() {}
+   ~BlockDataHandlingWrapper() override = default;
    
    virtual void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child ) = 0;
    virtual void serializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) = 0;
@@ -115,70 +115,70 @@ template< typename T >
 class BlockDataHandlingHelper : public BlockDataHandlingWrapper
 {
 public:
-   typedef domain_decomposition::internal::BlockData BlockData;
+   using BlockData = domain_decomposition::internal::BlockData;
 
    BlockDataHandlingHelper( const shared_ptr< BlockDataHandling<T> > & dataHandling ) : dataHandling_( dataHandling ) {}
-   ~BlockDataHandlingHelper() {}
+   ~BlockDataHandlingHelper() override = default;
    
-   BlockData * initialize( IBlock * const block )
+   BlockData * initialize( IBlock * const block ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       T * ptr = dataHandling_->initialize( block );
-      return ptr ? new BlockData( ptr ) : NULL;
+      return ptr ? new BlockData( ptr ) : nullptr;
    }
    
-   void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer )
+   void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       dataHandling_->serialize( block, id, buffer );
    }
    
-   void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child )
+   void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       dataHandling_->serializeCoarseToFine( block, id, buffer, child );
    }
    
-   void serializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer )
+   void serializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       dataHandling_->serializeFineToCoarse( block, id, buffer );
    }
    
-   BlockData * deserialize( IBlock * const block )
+   BlockData * deserialize( IBlock * const block ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       T * ptr = dataHandling_->deserialize( block );
-      return ptr ? new BlockData( ptr ) : NULL;
+      return ptr ? new BlockData( ptr ) : nullptr;
    }
    
-   BlockData * deserializeCoarseToFine( Block * const block )
+   BlockData * deserializeCoarseToFine( Block * const block ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       T * ptr = dataHandling_->deserializeCoarseToFine( block );
-      return ptr ? new BlockData( ptr ) : NULL;
+      return ptr ? new BlockData( ptr ) : nullptr;
    }
    
-   BlockData * deserializeFineToCoarse( Block * const block )
+   BlockData * deserializeFineToCoarse( Block * const block ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       T * ptr = dataHandling_->deserializeFineToCoarse( block );
-      return ptr ? new BlockData( ptr ) : NULL;
+      return ptr ? new BlockData( ptr ) : nullptr;
    }
    
-   void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer )
+   void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       dataHandling_->deserialize( block, id, buffer );
    }
    
-   void deserializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer )
+   void deserializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       dataHandling_->deserializeCoarseToFine( block, id, buffer );
    }   
    
-   void deserializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer, const uint_t child )
+   void deserializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer, const uint_t child ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       dataHandling_->deserializeFineToCoarse( block, id, buffer, child );
diff --git a/src/blockforest/BlockForest.cpp b/src/blockforest/BlockForest.cpp
index 62affa8372cdddb8da498c7ee393dd98d266bd13..70b1fdaf4d4ef2521d81a1a977032376cddad65d 100644
--- a/src/blockforest/BlockForest.cpp
+++ b/src/blockforest/BlockForest.cpp
@@ -1661,7 +1661,7 @@ bool BlockForest::determineBlockTargetLevels( bool & additionalRefreshCycleRequi
             minTargetLevels[id] = minTargetLevel;
          }
       }
-#ifndef NDEBUF
+#ifndef NDEBUG
       else
       {
          WALBERLA_ASSERT_LESS_EQUAL( minTargetLevel, level + uint_t(1) );
diff --git a/src/blockforest/BlockForest.h b/src/blockforest/BlockForest.h
index b0731c1150a09ab1aa0d3fe944eebfca43d52243..0edd764afde9eaa965b544fd1aaa61b3b9c48f54 100644
--- a/src/blockforest/BlockForest.h
+++ b/src/blockforest/BlockForest.h
@@ -55,7 +55,6 @@ public:
    using RefreshCallbackFunction = std::function<void (BlockForest &, const PhantomBlockForest &)>;
 
    using SnapshotCreationFunction = std::function<void (std::vector<uint_t> &, std::vector<uint_t> &)>;
-   [[deprecated("typo: use SnapshotRestoreFunction")]] typedef std::function<uint_t (const uint_t)> SnapshotRestorenFunction;
    using SnapshotRestoreFunction = std::function<uint_t (const uint_t)>;
    using SnapshotRestoreCallbackFunction = std::function<void ()>;
 
@@ -179,13 +178,13 @@ public:
       bool getState( Set<SUID>& state, const BlockID& id ) const
          { const Node* node = getNode( id ); if( node ) { state = node->state_; return true; } return false; }
 
-      bool exists( const real_t x, const real_t y, const real_t z ) const { return getNode(x,y,z) != NULL; }
-      bool exists( const BlockID& id ) const { return getNode( id ) != NULL; }
+      bool exists( const real_t x, const real_t y, const real_t z ) const { return getNode(x,y,z) != nullptr; }
+      bool exists( const BlockID& id ) const { return getNode( id ) != nullptr; }
 
       bool existsRemotely( const real_t x, const real_t y, const real_t z ) const
-         { const Node* node = getNode( x, y, z ); return ( node != NULL && node->process_ != forest_.getProcess() ); }
+         { const Node* node = getNode( x, y, z ); return ( node != nullptr && node->process_ != forest_.getProcess() ); }
       bool existsRemotely( const BlockID& id ) const
-         { const Node* node = getNode( id ); return ( node != NULL && node->process_ != forest_.getProcess() ); }
+         { const Node* node = getNode( id ); return ( node != nullptr && node->process_ != forest_.getProcess() ); }
 
       bool getId( BlockID& id, const real_t x, const real_t y, const real_t z ) const;
 
@@ -197,10 +196,10 @@ public:
       bool getRootBlockState( Set<SUID>& state, const uint_t x, const uint_t y, const uint_t z ) const
          { const Node* node = getRootNode(x,y,z); if( node ) { state = node->state_; return true; } return false; }
 
-      bool rootBlockExists( const uint_t x, const uint_t y, const uint_t z ) const { return getRootNode(x,y,z) != NULL; }
+      bool rootBlockExists( const uint_t x, const uint_t y, const uint_t z ) const { return getRootNode(x,y,z) != nullptr; }
 
       bool rootBlockExistsRemotely( const uint_t x, const uint_t y, const uint_t z ) const
-         { const Node* node = getRootNode(x,y,z); return ( node != NULL && node->process_ != forest_.getProcess() ); }
+         { const Node* node = getRootNode(x,y,z); return ( node != nullptr && node->process_ != forest_.getProcess() ); }
 
    private:
 
@@ -210,7 +209,7 @@ public:
       const Node * getRootNode( const uint_t x, const uint_t y, const uint_t z ) const {
          const uint_t index =  z * forest_.getYSize() * forest_.getXSize() + y * forest_.getXSize() + x;
          if( index >= nodes_.size() )
-            return NULL;
+            return nullptr;
          return nodes_[ index ].get();
       }
 
@@ -223,7 +222,7 @@ public:
    BlockForest( const uint_t process, const SetupBlockForest& forest, const bool keepGlobalBlockInformation = false );
    BlockForest( const uint_t process, const char* const filename, const bool broadcastFile = true, const bool keepGlobalBlockInformation = false );
 
-   ~BlockForest() {}
+   ~BlockForest() override = default;
 
    uint_t getProcess()        const { return process_; }
    uint_t getProcessIdBytes() const { return processIdBytes_; }
@@ -277,45 +276,45 @@ public:
    inline void getBlocks( std::vector< const Block* >& blocks, const uint_t level ) const;
    inline void getBlocks( std::vector<       Block* >& blocks, const uint_t level );
 
-   inline void getBlocksContainedWithinAABB( std::vector< const IBlock* >& blocks, const AABB& aabb ) const;
-   inline void getBlocksContainedWithinAABB( std::vector<       IBlock* >& blocks, const AABB& aabb );
+   inline void getBlocksContainedWithinAABB( std::vector< const IBlock* >& blocks, const AABB& aabb ) const override;
+   inline void getBlocksContainedWithinAABB( std::vector<       IBlock* >& blocks, const AABB& aabb ) override;
 
-   inline void getBlocksOverlappedByAABB( std::vector< const IBlock* >& blocks, const AABB& aabb ) const;
-   inline void getBlocksOverlappedByAABB( std::vector<       IBlock* >& blocks, const AABB& aabb );
+   inline void getBlocksOverlappedByAABB( std::vector< const IBlock* >& blocks, const AABB& aabb ) const override;
+   inline void getBlocksOverlappedByAABB( std::vector<       IBlock* >& blocks, const AABB& aabb ) override;
 
    using BlockStorage::getBlock;
 
-   inline const Block* getBlock( const IBlockID& id ) const;
-   inline       Block* getBlock( const IBlockID& id );
+   inline const Block* getBlock( const IBlockID& id ) const override;
+   inline       Block* getBlock( const IBlockID& id ) override;
 
-   inline const Block* getBlock( const real_t x, const real_t y, const real_t z ) const;
-   inline       Block* getBlock( const real_t x, const real_t y, const real_t z );
+   inline const Block* getBlock( const real_t x, const real_t y, const real_t z ) const override;
+   inline       Block* getBlock( const real_t x, const real_t y, const real_t z ) override;
 
    inline const Block* getRootBlock( const uint_t x, const uint_t y, const uint_t z ) const;
    inline       Block* getRootBlock( const uint_t x, const uint_t y, const uint_t z );
 
 
 
-   bool containsGlobalBlockInformation() const { return blockInformation_->active(); }
+   bool containsGlobalBlockInformation() const override { return blockInformation_->active(); }
 
-   inline void getAllBlocks( std::vector< shared_ptr< IBlockID > >& blocks ) const;
+   inline void getAllBlocks( std::vector< shared_ptr< IBlockID > >& blocks ) const override;
 
-   inline bool blockExists        ( const real_t x, const real_t y, const real_t z ) const;
-   inline bool blockExistsLocally ( const real_t x, const real_t y, const real_t z ) const;
-   inline bool blockExistsRemotely( const real_t x, const real_t y, const real_t z ) const;
+   inline bool blockExists        ( const real_t x, const real_t y, const real_t z ) const override;
+   inline bool blockExistsLocally ( const real_t x, const real_t y, const real_t z ) const override;
+   inline bool blockExistsRemotely( const real_t x, const real_t y, const real_t z ) const override;
 
-   inline bool blockExists        ( const IBlockID& id ) const;
-   inline bool blockExistsLocally ( const IBlockID& id ) const;
-   inline bool blockExistsRemotely( const IBlockID& id ) const;
+   inline bool blockExists        ( const IBlockID& id ) const override;
+   inline bool blockExistsLocally ( const IBlockID& id ) const override;
+   inline bool blockExistsRemotely( const IBlockID& id ) const override;
 
    inline bool rootBlockExists        ( const uint_t x, const uint_t y, const uint_t z ) const;
    inline bool rootBlockExistsLocally ( const uint_t x, const uint_t y, const uint_t z ) const;
    inline bool rootBlockExistsRemotely( const uint_t x, const uint_t y, const uint_t z ) const;
 
-   void getBlockID( IBlockID& id, const real_t x, const real_t y, const real_t z ) const;
-   void getAABB       ( AABB&      aabb,  const IBlockID& id ) const;
-   void getState      ( Set<SUID>& state, const IBlockID& id ) const;
-   void getProcessRank( uint_t&    rank,  const IBlockID& id ) const;
+   void getBlockID( IBlockID& id, const real_t x, const real_t y, const real_t z ) const override;
+   void getAABB       ( AABB&      aabb,  const IBlockID& id ) const override;
+   void getState      ( Set<SUID>& state, const IBlockID& id ) const override;
+   void getProcessRank( uint_t&    rank,  const IBlockID& id ) const override;
 
    void getRootBlockAABB       ( AABB&      aabb,  const uint_t x, const uint_t y, const uint_t z ) const;
    void getRootBlockState      ( Set<SUID>& state, const uint_t x, const uint_t y, const uint_t z ) const;
@@ -324,7 +323,7 @@ public:
    const BlockInformation & getBlockInformation() const { return *blockInformation_; }
 
 
-   inline uint_t getLevel( const IBlock& block ) const;
+   inline uint_t getLevel( const IBlock& block ) const override;
    inline uint_t getLevelFromBlockId( const BlockID& id ) const;
    inline uint_t getAABBFromBlockId( AABB& aabb, const BlockID& id ) const;
    inline AABB   getAABBFromBlockId( const BlockID& id ) const;
@@ -338,9 +337,9 @@ public:
    bool insertBuffersIntoProcessNetwork() const { return insertBuffersIntoProcessNetwork_; }
 
    const std::vector< uint_t > & getNeighborhood() const { return neighborhood_; }
-   const std::vector< uint_t > & getNeighboringProcesses() const { return getNeighborhood(); }
+   const std::vector< uint_t > & getNeighboringProcesses() const override { return getNeighborhood(); }
 
-   std::map< uint_t, std::vector< Vector3<real_t> > > getNeighboringProcessOffsets() const;
+   std::map< uint_t, std::vector< Vector3<real_t> > > getNeighboringProcessOffsets() const override;
 
 
    
@@ -484,7 +483,7 @@ public:
 
 protected:
 
-   bool equal( const BlockStorage* rhs ) const;
+   bool equal( const BlockStorage* rhs ) const override;
    
    void addBlockData( IBlock * const block, const BlockDataID & index, domain_decomposition::internal::BlockData * const data )
    { BlockStorage::addBlockData( block, index, data ); }
@@ -643,7 +642,7 @@ inline const Block* BlockForest::getBlock( const IBlockID& id ) const {
    if( it != blocks_.end() )
       return it->second.get();
 
-   return NULL;
+   return nullptr;
 }
 
 
@@ -657,7 +656,7 @@ inline Block* BlockForest::getBlock( const IBlockID& id ) {
    if( it != blocks_.end() )
       return it->second.get();
 
-   return NULL;
+   return nullptr;
 }
 
 
@@ -667,7 +666,7 @@ inline const Block* BlockForest::getBlock( const real_t x, const real_t y, const
    for( auto it = blocks_.begin(); it != blocks_.end(); ++it )
       if( it->second->getAABB().contains(x,y,z) ) return it->second.get();
 
-   return NULL;
+   return nullptr;
 }
 
 
@@ -677,7 +676,7 @@ inline Block* BlockForest::getBlock( const real_t x, const real_t y, const real_
    for( auto it = blocks_.begin(); it != blocks_.end(); ++it )
       if( it->second->getAABB().contains(x,y,z) ) return it->second.get();
 
-   return NULL;
+   return nullptr;
 }
 
 
@@ -721,14 +720,14 @@ inline bool BlockForest::blockExists( const real_t x, const real_t y, const real
    if( blockInformation_->active() )
       return blockInformation_->exists(x,y,z);
 
-   return getBlock(x,y,z) != NULL;
+   return getBlock(x,y,z) != nullptr;
 }
 
 
 
 inline bool BlockForest::blockExistsLocally( const real_t x, const real_t y, const real_t z ) const {
 
-   return getBlock(x,y,z) != NULL;
+   return getBlock(x,y,z) != nullptr;
 }
 
 
@@ -738,7 +737,7 @@ inline bool BlockForest::blockExistsRemotely( const real_t x, const real_t y, co
    if( blockInformation_->active() )
       return blockInformation_->existsRemotely(x,y,z);
 
-   return getBlock(x,y,z) == NULL;
+   return getBlock(x,y,z) == nullptr;
 }
 
 
@@ -750,14 +749,14 @@ inline bool BlockForest::blockExists( const IBlockID& id ) const {
    if( blockInformation_->active() )
       return blockInformation_->exists( *static_cast< const BlockID* >( &id ) );
 
-   return getBlock( id ) != NULL;
+   return getBlock( id ) != nullptr;
 }
 
 
 
 inline bool BlockForest::blockExistsLocally( const IBlockID& id ) const {
 
-   return getBlock( id ) != NULL;
+   return getBlock( id ) != nullptr;
 }
 
 
@@ -769,7 +768,7 @@ inline bool BlockForest::blockExistsRemotely( const IBlockID& id ) const {
    if( blockInformation_->active() )
       return blockInformation_->existsRemotely( *static_cast< const BlockID* >( &id ) );
 
-   return getBlock( id ) == NULL;
+   return getBlock( id ) == nullptr;
 }
 
 
@@ -779,14 +778,14 @@ inline bool BlockForest::rootBlockExists( const uint_t x, const uint_t y, const
    if( blockInformation_->active() )
       return blockInformation_->rootBlockExists(x,y,z);
 
-   return getRootBlock(x,y,z) != NULL;
+   return getRootBlock(x,y,z) != nullptr;
 }
 
 
 
 inline bool BlockForest::rootBlockExistsLocally( const uint_t x, const uint_t y, const uint_t z ) const {
 
-   return getRootBlock(x,y,z) != NULL;
+   return getRootBlock(x,y,z) != nullptr;
 }
 
 
@@ -796,7 +795,7 @@ inline bool BlockForest::rootBlockExistsRemotely( const uint_t x, const uint_t y
    if( blockInformation_->active() )
       return blockInformation_->rootBlockExistsRemotely(x,y,z);
 
-   return getRootBlock(x,y,z) == NULL;
+   return getRootBlock(x,y,z) == nullptr;
 }
 
 
diff --git a/src/blockforest/BlockID.cpp b/src/blockforest/BlockID.cpp
index 886567ed93a2812dd5c176ff800ea5c192f4e32d..865cfcb2ede67d3c8542fb649379b4508f05fcd0 100644
--- a/src/blockforest/BlockID.cpp
+++ b/src/blockforest/BlockID.cpp
@@ -135,11 +135,9 @@ void BlockID::toByteArray( std::vector< uint8_t >& array, const uint_t offset, c
 
 #else
 
-#ifdef WALBERLA_CXX_COMPILER_IS_MSVC
-namespace { char dummy; } // disable MSVC warning LNK4221: This object file does not define any previously
-                          // undefined public symbols, so it will not be used by any link operation that
-                          // consumes this library
-#endif
+namespace internal {
+char dummy; // silence linker warning about object file with no symbols
+}
 
 #endif
 
diff --git a/src/blockforest/BlockID.h b/src/blockforest/BlockID.h
index ff64a5b19b9746c5a2b0135f1069c29905813ff9..e62d8ff09d05de25810b4364ecba6aff9824ee06 100644
--- a/src/blockforest/BlockID.h
+++ b/src/blockforest/BlockID.h
@@ -283,18 +283,18 @@ public:
    void   removeBranchId() { WALBERLA_ASSERT_GREATER_EQUAL( getUsedBits(), uint_c(4) ); id_ >>= 3; }
    uint_t    getBranchId() const { WALBERLA_ASSERT_GREATER_EQUAL( getUsedBits(), uint_c(4) ); return id_ & uint_c(7); }
 
-   bool operator< ( const IBlockID& rhs ) const
+   bool operator< ( const IBlockID& rhs ) const override
       { WALBERLA_ASSERT_EQUAL( dynamic_cast< const BlockID* >( &rhs ), &rhs ); return id_ <  static_cast< const BlockID* >( &rhs )->id_; }
    bool operator> ( const IBlockID& rhs ) const
       { WALBERLA_ASSERT_EQUAL( dynamic_cast< const BlockID* >( &rhs ), &rhs ); return id_ >  static_cast< const BlockID* >( &rhs )->id_; }
-   bool operator==( const IBlockID& rhs ) const
+   bool operator==( const IBlockID& rhs ) const override
       { WALBERLA_ASSERT_EQUAL( dynamic_cast< const BlockID* >( &rhs ), &rhs ); return id_ == static_cast< const BlockID* >( &rhs )->id_; }
-   bool operator!=( const IBlockID& rhs ) const
+   bool operator!=( const IBlockID& rhs ) const override
       { WALBERLA_ASSERT_EQUAL( dynamic_cast< const BlockID* >( &rhs ), &rhs ); return id_ != static_cast< const BlockID* >( &rhs )->id_; }
 
-   inline IDType getID() const;
+   inline IDType getID() const override;
 
-   inline std::ostream& toStream( std::ostream& os ) const;
+   inline std::ostream& toStream( std::ostream& os ) const override;
 
    void toByteArray( std::vector< uint8_t >& array, const uint_t offset, const uint_t bytes ) const { uintToByteArray( id_, array, offset, bytes ); }
 
@@ -385,7 +385,7 @@ void BlockID::fromBuffer( Buffer_T& buffer ) {
 
 } // namespace blockforest
 
-typedef blockforest::BlockID BlockID;
+using BlockID = blockforest::BlockID;
 
 } // namespace walberla
 
diff --git a/src/blockforest/BlockReconstruction.h b/src/blockforest/BlockReconstruction.h
index 68a2aba2f49053d316e977abb77e07b7935d2a45..c41689b94ea3e717486676452257d020579fe59a 100644
--- a/src/blockforest/BlockReconstruction.h
+++ b/src/blockforest/BlockReconstruction.h
@@ -164,7 +164,7 @@ void BlockReconstruction::reconstructNeighborhood( BLOCK* block, const std::vect
                const NeighborhoodReconstructionBlock* neighbor = &(neighbors[i]);
                uint_t index = 0;
 
-               if( neighborhood.insert( neighbor ).second == true ) {
+               if( neighborhood.insert( neighbor ).second ) {
 
                   index = block->getNeighborhoodSize();
                   neighborhoodIndex[ neighbor ] = index;
diff --git a/src/blockforest/CMakeLists.txt b/src/blockforest/CMakeLists.txt
index 554388c3f7b89319e130ab66eae6089f679c41f2..84027d327ed552e6cb0c9b35aec657fa308f3074 100644
--- a/src/blockforest/CMakeLists.txt
+++ b/src/blockforest/CMakeLists.txt
@@ -5,4 +5,4 @@ mark_as_advanced( WALBERLA_BLOCKFOREST_PRIMITIVE_BLOCKID )
 
 configure_file ( CMakeDefs.in.h  CMakeDefs.h )
 
-waLBerla_add_module( DEPENDS communication core domain_decomposition python_coupling stencil )
+waLBerla_add_module( DEPENDS communication core domain_decomposition stencil )
diff --git a/src/blockforest/GlobalLoadBalancing.h b/src/blockforest/GlobalLoadBalancing.h
index 521af77de0c6c1fa8b186286e04e427110cf03e0..ba92b73f23d751e2592e253b3a5f001fba227365 100644
--- a/src/blockforest/GlobalLoadBalancing.h
+++ b/src/blockforest/GlobalLoadBalancing.h
@@ -55,9 +55,9 @@ public:
    class MetisConfiguration {
 
    public:
-      typedef std::function< memory_t ( const BLOCK* const, const BLOCK* const ) > CommunicationFunction;
+      using CommunicationFunction = std::function<memory_t (const BLOCK *const, const BLOCK *const)>;
 
-      MetisConfiguration( const bool _includeMetis = false, const bool _forceMetis = false, CommunicationFunction _communicationFunction = 0,
+      MetisConfiguration( const bool _includeMetis = false, const bool _forceMetis = false, CommunicationFunction _communicationFunction = nullptr,
                           const real_t _maxUbvec = real_c(1.5), const uint_t _iterations = uint_c(10) ) :
          includeMetis_( _includeMetis ), forceMetis_( _forceMetis ), communicationFunction_( _communicationFunction ),
          maxUbvec_( _maxUbvec ), iterations_( _iterations ) {}
@@ -103,13 +103,13 @@ public:
    template< typename BLOCK >
    static inline uint_t minimizeProcesses( const std::vector< BLOCK* >& blocks, const memory_t memoryLimit,
                                            const MetisConfiguration<BLOCK>& metisConfig,
-                                           const std::vector< workload_t >* processesWork = NULL,
-                                           const std::vector< memory_t >* processesMemory = NULL );
+                                           const std::vector< workload_t >* processesWork = nullptr,
+                                           const std::vector< memory_t >* processesMemory = nullptr );
    template< typename BLOCK >
    static inline uint_t maximizeMemoryUtilization( const std::vector< BLOCK* >& blocks, const memory_t memoryLimit,
                                                    const MetisConfiguration<BLOCK>& metisConfig,
-                                                   const std::vector< workload_t >* processesWork = NULL,
-                                                   const std::vector< memory_t >* processesMemory = NULL );
+                                                   const std::vector< workload_t >* processesWork = nullptr,
+                                                   const std::vector< memory_t >* processesMemory = nullptr );
    // optimize workload
 
    template< typename BLOCK >
@@ -130,7 +130,7 @@ private:
 
    template< typename BLOCK >
    static uint_t fixedWork( const std::vector< BLOCK* >& blocks, const workload_t workloadLimit, const memory_t memoryLimit,
-                            const std::vector< workload_t >* processesWork = NULL, const std::vector< memory_t >* processesMemory = NULL );
+                            const std::vector< workload_t >* processesWork = nullptr, const std::vector< memory_t >* processesMemory = nullptr );
 
 #ifdef WALBERLA_BUILD_WITH_METIS
 
@@ -490,7 +490,7 @@ inline uint_t GlobalLoadBalancing::minimizeProcesses( const std::vector< BLOCK*
    // minimize number of processes == do not care about the amount of workload that is assigned to a process,
    //                                 just put as many blocks as possible on any process
 
-   workload_t workloadLimit = workloadSum( blocks ) + ( ( processesWork == NULL ) ? static_cast< workload_t >(0) :
+   workload_t workloadLimit = workloadSum( blocks ) + ( ( processesWork == nullptr ) ? static_cast< workload_t >(0) :
                                                                                     math::kahanSummation( processesWork->begin(), processesWork->end() ) );
 
    uint_t numberOfProcesses = fixedWork( blocks, workloadLimit, memoryLimit, processesWork, processesMemory );
@@ -642,7 +642,7 @@ void GlobalLoadBalancing::prepareProcessReordering( const std::vector< BLOCK* >
          const BLOCK* const block = (*it).second;
 
          for( uint_t i = 0; i != block->getNeighborhoodSize(); ++i )
-            if( neighbors.insert( block->getNeighborTargetProcess(i) ).second == true )
+            if( neighbors.insert( block->getNeighborTargetProcess(i) ).second )
                processNeighbors[ uint_c(p) ].push_back( block->getNeighborTargetProcess(i) );
 
 //       for( uint_t n = 0; n != 26; ++n )
@@ -673,7 +673,7 @@ void GlobalLoadBalancing::reorderProcessesByBFS( std::vector< BLOCK* > & blocks,
       uint_t startIndex = numberOfProcesses;
       for( uint_t i = previousStartIndex; i < numberOfProcesses; ++i )
       {
-         if( !processed[i] && processNeighbors[i].size() != 0 )
+         if( !processed[i] && !processNeighbors[i].empty() )
          {
             startIndex = i;
             break;
@@ -741,8 +741,8 @@ uint_t GlobalLoadBalancing::fixedWork( const std::vector< BLOCK* >& blocks, cons
    WALBERLA_ASSERT_GREATER( memoryLimit  , static_cast< memory_t >(0)   );
 
    uint_t     processes = 0;
-   workload_t workload  = ( processesWork != NULL && processes < processesWork->size() ) ? (*processesWork)[processes] : static_cast< workload_t >(0);
-   memory_t   memory    = ( processesMemory != NULL && processes < processesMemory->size() ) ? (*processesMemory)[processes] : static_cast< memory_t >(0);
+   workload_t workload  = ( processesWork != nullptr && processes < processesWork->size() ) ? (*processesWork)[processes] : static_cast< workload_t >(0);
+   memory_t   memory    = ( processesMemory != nullptr && processes < processesMemory->size() ) ? (*processesMemory)[processes] : static_cast< memory_t >(0);
 
    for( uint_t i = 0; i != blocks.size(); ++i ) {
 
@@ -754,8 +754,8 @@ uint_t GlobalLoadBalancing::fixedWork( const std::vector< BLOCK* >& blocks, cons
 
          ++processes;
 
-         workload = ( processesWork != NULL && processes < processesWork->size() ) ? (*processesWork)[processes] : static_cast< workload_t >(0);
-         memory   = ( processesMemory != NULL && processes < processesMemory->size() ) ? (*processesMemory)[processes] : static_cast< memory_t >(0);
+         workload = ( processesWork != nullptr && processes < processesWork->size() ) ? (*processesWork)[processes] : static_cast< workload_t >(0);
+         memory   = ( processesMemory != nullptr && processes < processesMemory->size() ) ? (*processesMemory)[processes] : static_cast< memory_t >(0);
       }
 
       WALBERLA_ASSERT_LESS_EQUAL( blocks[i]->getWorkload() + workload, workloadLimit );
diff --git a/src/blockforest/Initialization.h b/src/blockforest/Initialization.h
index 3a87f28117e09f46333bce69c72c776f3da18027..aecdd20adc5c4d995e27998d3898cc6c188b3930 100644
--- a/src/blockforest/Initialization.h
+++ b/src/blockforest/Initialization.h
@@ -34,11 +34,11 @@ namespace blockforest {
 
 
 shared_ptr< StructuredBlockForest >  createUniformBlockGridFromConfig( const shared_ptr< Config > & config,
-                                                                       CellInterval * requestedDomainSize = NULL,
+                                                                       CellInterval * requestedDomainSize = nullptr,
                                                                        const bool keepGlobalBlockInformation = false );
 
 shared_ptr< StructuredBlockForest >  createUniformBlockGridFromConfig( const Config::BlockHandle & configBlock,
-                                                                       CellInterval * requestedDomainSize = NULL,
+                                                                       CellInterval * requestedDomainSize = nullptr,
                                                                        const bool keepGlobalBlockInformation = false );
 
 
diff --git a/src/blockforest/PhantomBlock.h b/src/blockforest/PhantomBlock.h
index 6afe73bec61d07270a04a6a23fc56acfc9fe739a..3872a72610f8ac4d72e095d48573d2c5f069b005 100644
--- a/src/blockforest/PhantomBlock.h
+++ b/src/blockforest/PhantomBlock.h
@@ -88,7 +88,7 @@ public:
    T getData() const { return walberla::any_cast<T>( data_ ); }
    
    bool hasData() const {
-#ifdef WALBELRLA_USE_STD_ANY 
+#ifndef WALBERLA_USE_STD_EXPERIMENTAL_ANY
       return data_.has_value();
 #else
       return !(data_.empty());
@@ -304,7 +304,7 @@ inline void PhantomBlock::addNeighbor( const BlockID & id, const uint_t process,
       WALBERLA_ASSERT( neighborhood_[i].getId() < id || id < neighborhood_[i].getId() );
 #endif
 
-   neighborhood_.push_back( NeighborBlock( phantomForest_, id, process, state ) );
+   neighborhood_.emplace_back( phantomForest_, id, process, state );
 }
 
 
@@ -374,6 +374,6 @@ inline const AABB & PhantomBlock::getNeighborAABB( const uint_t index ) const
 
 } // namespace blockforest
 
-typedef blockforest::PhantomBlock PhantomBlock;
+using PhantomBlock = blockforest::PhantomBlock;
 
 } // namespace walberla
diff --git a/src/blockforest/PhantomBlockForest.h b/src/blockforest/PhantomBlockForest.h
index 1c7285167da9295ab15394133195f812406e1626..92211a7fc7d70c6075e951f83405b0496a8a349b 100644
--- a/src/blockforest/PhantomBlockForest.h
+++ b/src/blockforest/PhantomBlockForest.h
@@ -42,23 +42,16 @@ class PhantomBlockForest
 {
 public:
 
-   typedef std::function< Set<SUID> ( const std::vector< std::pair< BlockID, Set<SUID> > > & source, const BlockID & destintation ) >
-           BlockStateDeterminationFunction;
+   using BlockStateDeterminationFunction = std::function<Set<SUID> (const std::vector<std::pair<BlockID, Set<SUID>>> &, const BlockID &)>;
 
-   typedef std::function< void ( std::vector< std::pair< const PhantomBlock *, walberla::any > > & blockData,
-                                   const PhantomBlockForest & phantomForest ) >
-           PhantomBlockDataAssignmentFunction;
+   using PhantomBlockDataAssignmentFunction = std::function<void (std::vector<std::pair<const PhantomBlock *, walberla::any>> &, const PhantomBlockForest &)>;
 
    /// \param iteration execution counter of this callback
    /// \return should the callback rerun after phantom block migration?
-   typedef std::function< bool ( std::vector< std::pair< const PhantomBlock *, uint_t > > & targetProcess,
-                                   std::set< uint_t > & processesToRecvFrom,
-                                   const PhantomBlockForest & phantomForest,
-                                   const uint_t iteration ) >
-           MigrationPreparationFunction; // = load balancing
+   using MigrationPreparationFunction = std::function<bool (std::vector<std::pair<const PhantomBlock *, uint_t>> &, std::set<uint_t> &, const PhantomBlockForest &, const uint_t)>; // = load balancing
 
-   typedef std::function< void ( mpi::SendBuffer & buffer, const PhantomBlock & block ) >                    PhantomBlockDataPackFunction;
-   typedef std::function< void ( mpi::RecvBuffer & buffer, const PhantomBlock & block, walberla::any & data ) > PhantomBlockDataUnpackFunction;
+   using PhantomBlockDataPackFunction = std::function<void (mpi::SendBuffer &, const PhantomBlock &)>;
+   using PhantomBlockDataUnpackFunction = std::function<void (mpi::RecvBuffer &, const PhantomBlock &, walberla::any &)>;
 
 
 
@@ -128,6 +121,6 @@ inline shared_ptr< PhantomBlock > PhantomBlockForest::getBlock( const BlockID &
 
 } // namespace blockforest
 
-typedef blockforest::PhantomBlockForest PhantomBlockForest;
+using PhantomBlockForest = blockforest::PhantomBlockForest;
 
 } // namespace walberla
diff --git a/src/blockforest/SetupBlock.cpp b/src/blockforest/SetupBlock.cpp
index 391012997f330f75d07c2853354db39f5d6cd625..5b822acc18c5e19f3672c7f25b134594942ff2d9 100644
--- a/src/blockforest/SetupBlock.cpp
+++ b/src/blockforest/SetupBlock.cpp
@@ -37,7 +37,7 @@ void SetupBlock::assembleNeighborhood() {
 
    for( uint_t n = 0; n != 26; ++n )
       for( uint_t i = 0; i != neighborhoodSection_[n].size(); ++i )
-         if( neighborhood.insert( neighborhoodSection_[n][i] ).second == true )
+         if( neighborhood.insert( neighborhoodSection_[n][i] ).second )
             neighborhood_.push_back( neighborhoodSection_[n][i] );
 }
 
diff --git a/src/blockforest/SetupBlock.h b/src/blockforest/SetupBlock.h
index bd391aac8dcd7e2332f525f7618f44ba61f43c12..64b7b89caaa5546dc841ea4be3f80632eded4522 100644
--- a/src/blockforest/SetupBlock.h
+++ b/src/blockforest/SetupBlock.h
@@ -83,7 +83,7 @@ public:
    inline       SetupBlock* getChild( const uint_t index )       { WALBERLA_ASSERT_LESS( index, children_.size() ); return children_[index]; }
    inline       void        setChild( const uint_t index, SetupBlock* const child );
 
-   bool hasFather()   const { return father_ != NULL; }
+   bool hasFather()   const { return father_ != nullptr; }
    bool hasChildren() const { return !children_.empty(); }
 
    const std::vector< SetupBlock* >& getNeighborhoodSection( const uint_t index ) const { WALBERLA_ASSERT_LESS( index, 26 ); return neighborhoodSection_[index]; }
@@ -168,7 +168,7 @@ inline void SetupBlock::setChild( const uint_t index, SetupBlock* const child )
    WALBERLA_ASSERT( children_.empty() || children_.size() == 8 );
 
    if( children_.empty() )
-      children_.resize( 8, NULL );
+      children_.resize( 8, nullptr );
 
    children_[index] = child;
 }
diff --git a/src/blockforest/SetupBlockForest.h b/src/blockforest/SetupBlockForest.h
index a9192cac2f3946d02bf79bfb2f32c0f2293fa882..17fd2685a75b864b1c6bd5ba689a38a06acdbc23 100644
--- a/src/blockforest/SetupBlockForest.h
+++ b/src/blockforest/SetupBlockForest.h
@@ -45,8 +45,7 @@ class SetupBlockForest : private NonCopyable {
 
 public:
 
-   typedef std::function< uint_t ( SetupBlockForest & forest, const uint_t numberOfProcesses, const memory_t perProcessMemoryLimit ) >
-          TargetProcessAssignmentFunction; // returns number of processes (may be lower than numberOfProcesses)
+   using TargetProcessAssignmentFunction = std::function<uint_t (SetupBlockForest &, const uint_t, const memory_t)>; // returns number of processes (may be lower than numberOfProcesses)
 
 
 
@@ -72,14 +71,14 @@ public:
 
    // Do not use a vector of bool's! Due to the implementation of this vector in the standard library, parallel access to a
    // vector of bool's - even on different elements - is not thread-safe!
-   typedef std::function< void ( std::vector<uint8_t>& excludeBlock, const RootBlockAABB& aabb ) > RootBlockExclusionFunction;
+   using RootBlockExclusionFunction = std::function<void (std::vector<uint8_t> &, const RootBlockAABB &)>;
 
-   typedef std::function< void ( SetupBlockForest& forest ) > RefinementSelectionFunction;
-   typedef std::function< void ( SetupBlockForest& forest ) > WorkloadMemorySUIDAssignmentFunction;
+   using RefinementSelectionFunction = std::function<void (SetupBlockForest &)>;
+   using WorkloadMemorySUIDAssignmentFunction = std::function<void (SetupBlockForest &)>;
 
-   typedef std::vector< std::pair< const SetupBlock*, const SetupBlock* > >               CommunicationPairs;
-   typedef std::vector< real_t >                                                          CommunicationWeights;
-   typedef std::function< void ( const CommunicationPairs &, CommunicationWeights & ) > CommunicationWeightFunction;
+   using CommunicationPairs = std::vector<std::pair<const SetupBlock *, const SetupBlock *>>;
+   using CommunicationWeights = std::vector<real_t>;
+   using CommunicationWeightFunction = std::function<void (const CommunicationPairs &, CommunicationWeights &)>;
 
    inline static void NullCommunicationWeightFunction( const CommunicationPairs &, CommunicationWeights & )
    {
@@ -98,7 +97,7 @@ public:
 
    public:
 
-      iterator( const iterator& it ) : forest_( it.forest_ ), block_( it.block_ ) {}
+      iterator( const iterator& it )  = default;
 
       iterator& operator++() { WALBERLA_ASSERT_NOT_NULLPTR( block_ ); block_ = forest_->getNextBlock( block_ ); return *this; } // prefix ++X
       iterator  operator++(int) { iterator it( *this ); operator++(); return it; };                                             // postfix X++
@@ -127,7 +126,7 @@ public:
    public:
 
       const_iterator( const       iterator& it ) : forest_( it.forest_ ), block_( it.block_ ) {}
-      const_iterator( const const_iterator& it ) : forest_( it.forest_ ), block_( it.block_ ) {}
+      const_iterator( const const_iterator& it )  = default;
 
       const_iterator& operator++() { WALBERLA_ASSERT_NOT_NULLPTR( block_ ); block_ = forest_->getNextBlock( block_ ); return *this; } // prefix ++X
       const_iterator  operator++(int) { const_iterator it( *this ); operator++(); return it; };                                       // postfix X++
@@ -183,10 +182,10 @@ public:
    uint_t getNumberOfBlocks( const uint_t level ) const;
 
    inline const_iterator begin() const;
-   inline const_iterator end()   const { return const_iterator( this, NULL ); }
+   inline const_iterator end()   const { return const_iterator( this, nullptr ); }
 
    inline iterator begin();
-   inline iterator end() { return iterator( this, NULL ); }
+   inline iterator end() { return iterator( this, nullptr ); }
 
    const SetupBlock* getFirstBlock() const;
          SetupBlock* getFirstBlock();
@@ -420,7 +419,7 @@ inline SetupBlockForest::~SetupBlockForest() {
 
    for( uint_t i = 0; i != forest_.size(); ++i )
    {
-      if( forest_[i] != NULL ) delete forest_[i];
+      if( forest_[i] != nullptr ) delete forest_[i];
    }
 }
 
@@ -430,7 +429,7 @@ inline SetupBlockForest::const_iterator SetupBlockForest::begin() const {
 
    const SetupBlock* block = getFirstBlock();
 
-   if( block == NULL )
+   if( block == nullptr )
       return end();
 
    return SetupBlockForest::const_iterator( this, block );
@@ -442,7 +441,7 @@ inline SetupBlockForest::iterator SetupBlockForest::begin() {
 
    SetupBlock* block = getFirstBlock();
 
-   if( block == NULL )
+   if( block == nullptr )
       return end();
 
    return SetupBlockForest::iterator( this, block );
@@ -467,10 +466,10 @@ inline SetupBlock* SetupBlockForest::getRootBlock( const uint_t x, const uint_t
 inline const SetupBlock* SetupBlockForest::getBlock( const real_t px, const real_t py, const real_t pz ) const {
 
    if( !domain_.contains( px, py, pz ) )
-      return NULL;
+      return nullptr;
 
    SetupBlock* block = forest_[ mapPointToTreeIndex( px, py, pz ) ];
-   if( block == NULL ) return NULL;
+   if( block == nullptr ) return nullptr;
 
    return mapPointToBlock( block, px, py, pz );
 }
@@ -480,10 +479,10 @@ inline const SetupBlock* SetupBlockForest::getBlock( const real_t px, const real
 inline SetupBlock* SetupBlockForest::getBlock( const real_t px, const real_t py, const real_t pz ) {
 
    if( !domain_.contains( px, py, pz ) )
-      return NULL;
+      return nullptr;
 
    SetupBlock* block = forest_[ mapPointToTreeIndex( px, py, pz ) ];
-   if( block == NULL ) return NULL;
+   if( block == nullptr ) return nullptr;
 
    return mapPointToBlock( block, px, py, pz );
 }
@@ -715,7 +714,7 @@ class RefinementSelectionFunctions
 {
 public:
 
-   typedef blockforest::SetupBlockForest::RefinementSelectionFunction RefinementSelectionFunction;
+   using RefinementSelectionFunction = blockforest::SetupBlockForest::RefinementSelectionFunction;
 
    void add( const RefinementSelectionFunction & function )
    {
diff --git a/src/blockforest/StructuredBlockForest.h b/src/blockforest/StructuredBlockForest.h
index ac021fe66acce0eb8a24fa662b625ad906170762..04b61a94fbc7b4b84ab60714db58a78932471a7a 100644
--- a/src/blockforest/StructuredBlockForest.h
+++ b/src/blockforest/StructuredBlockForest.h
@@ -50,31 +50,31 @@ public:
    using StructuredBlockStorage::blockExistsLocally;
    using StructuredBlockStorage::blockExistsRemotely;
 
-          bool blockExists        ( const Cell& cell, const uint_t level = 0 ) const;
-   inline bool blockExistsLocally ( const Cell& cell, const uint_t level = 0 ) const;
-   inline bool blockExistsRemotely( const Cell& cell, const uint_t level = 0 ) const;
+          bool blockExists        ( const Cell& cell, const uint_t level = 0 ) const override;
+   inline bool blockExistsLocally ( const Cell& cell, const uint_t level = 0 ) const override;
+   inline bool blockExistsRemotely( const Cell& cell, const uint_t level = 0 ) const override;
 
-   void getBlockID( IBlockID& id, const Cell& cell, const uint_t level = 0 ) const;
+   void getBlockID( IBlockID& id, const Cell& cell, const uint_t level = 0 ) const override;
 
-   inline uint_t getLevel( const IBlock& block ) const;
+   inline uint_t getLevel( const IBlock& block ) const override;
 
    using StructuredBlockStorage::getNumberOfXCells;
    using StructuredBlockStorage::getNumberOfYCells;
    using StructuredBlockStorage::getNumberOfZCells;
 
 #ifdef NDEBUG
-   uint_t getNumberOfXCells( const IBlock& /*block*/ ) const { return blockCells_[0]; }
-   uint_t getNumberOfYCells( const IBlock& /*block*/ ) const { return blockCells_[1]; }
-   uint_t getNumberOfZCells( const IBlock& /*block*/ ) const { return blockCells_[2]; }
+   uint_t getNumberOfXCells( const IBlock& /*block*/ ) const override { return blockCells_[0]; }
+   uint_t getNumberOfYCells( const IBlock& /*block*/ ) const override { return blockCells_[1]; }
+   uint_t getNumberOfZCells( const IBlock& /*block*/ ) const override { return blockCells_[2]; }
 #else
-   uint_t getNumberOfXCells( const IBlock& block ) const { WALBERLA_ASSERT_EQUAL( &(getBlockStorage()), &(block.getBlockStorage()) ); return blockCells_[0]; }
-   uint_t getNumberOfYCells( const IBlock& block ) const { WALBERLA_ASSERT_EQUAL( &(getBlockStorage()), &(block.getBlockStorage()) ); return blockCells_[1]; }
-   uint_t getNumberOfZCells( const IBlock& block ) const { WALBERLA_ASSERT_EQUAL( &(getBlockStorage()), &(block.getBlockStorage()) ); return blockCells_[2]; }
+   uint_t getNumberOfXCells( const IBlock& block ) const override { WALBERLA_ASSERT_EQUAL( &(getBlockStorage()), &(block.getBlockStorage()) ); return blockCells_[0]; }
+   uint_t getNumberOfYCells( const IBlock& block ) const override { WALBERLA_ASSERT_EQUAL( &(getBlockStorage()), &(block.getBlockStorage()) ); return blockCells_[1]; }
+   uint_t getNumberOfZCells( const IBlock& block ) const override { WALBERLA_ASSERT_EQUAL( &(getBlockStorage()), &(block.getBlockStorage()) ); return blockCells_[2]; }
 #endif
 
    using StructuredBlockStorage::getNumberOfCells;
 
-   inline uint_t getNumberOfCells( const IBlock& block, const uint_t index ) const;
+   inline uint_t getNumberOfCells( const IBlock& block, const uint_t index ) const override;
 
    // direct access to all member functions which are special to of BlockForest (-> for documentation of these functions see class BlockForest)
 
@@ -164,21 +164,21 @@ public:
 
 protected:
 
-   inline bool equal( const StructuredBlockStorage* rhs ) const;
+   inline bool equal( const StructuredBlockStorage* rhs ) const override;
 
    // helper class for 'StructuredBlockForest::addCellBoundingBoxesAsBlockData'
    class CellBoundingBoxHandling : public AlwaysInitializeBlockDataHandling< CellInterval >
    {
    public:
       CellBoundingBoxHandling( const StructuredBlockForest & forest ) : forest_( forest ) {}
-      CellInterval * initialize( IBlock * const block ) { return forest_.initializeCellBoundingBox( block ); }
+      CellInterval * initialize( IBlock * const block ) override { return forest_.initializeCellBoundingBox( block ); }
    private:
       const StructuredBlockForest & forest_;
    };
    friend class CellBoundingBoxHandling;
 
    //using StructuredBlockStorage::initializeCellBoundingBox;
-   inline BlockDataID addCellBoundingBoxesAsBlockData( const std::string & identifier );
+   inline BlockDataID addCellBoundingBoxesAsBlockData( const std::string & identifier ) override;
 
 private:
 
@@ -217,7 +217,7 @@ inline StructuredBlockForest::StructuredBlockForest( const shared_ptr< BlockFore
 
 inline bool StructuredBlockForest::blockExistsLocally( const Cell& cell, const uint_t level ) const {
 
-   return getBlock( cell, level ) != NULL;
+   return getBlock( cell, level ) != nullptr;
 }
 
 
diff --git a/src/blockforest/Types.h b/src/blockforest/Types.h
index 76ab3911327cb28608cb6c89b2a8e964a6791803..dc61b3551056bdb8e44ec15aa722494c5c81746c 100644
--- a/src/blockforest/Types.h
+++ b/src/blockforest/Types.h
@@ -33,8 +33,8 @@ namespace blockforest {
 
 // data structure specific types
 
-typedef real_t workload_t;
-typedef real_t memory_t;
+using workload_t = real_t;
+using memory_t = real_t;
 
 WALBERLA_STATIC_ASSERT( sizeof( workload_t ) == 4 || sizeof( workload_t ) == 8 );
 WALBERLA_STATIC_ASSERT( sizeof( memory_t   ) == 4 || sizeof( memory_t   ) == 8 );
diff --git a/src/blockforest/communication/NonUniformBufferedScheme.h b/src/blockforest/communication/NonUniformBufferedScheme.h
index 3e206f36d1aa4bfca2eebf14859dd22b68da72bd..caf91651c578ddc7da5bea5b8a67398e8cc590ee 100644
--- a/src/blockforest/communication/NonUniformBufferedScheme.h
+++ b/src/blockforest/communication/NonUniformBufferedScheme.h
@@ -55,12 +55,12 @@ public:
 
    enum INDEX { EQUAL_LEVEL = 0, COARSE_TO_FINE = 1, FINE_TO_COARSE = 2 };
 
-   typedef mpi::SendBuffer SendBuffer;
-   typedef mpi::RecvBuffer RecvBuffer;
+   using SendBuffer = mpi::SendBuffer;
+   using RecvBuffer = mpi::RecvBuffer;
 
-   typedef shared_ptr< blockforest::communication::NonUniformPackInfo > PackInfo;
-   typedef std::function<void ()>                            VoidFunction;
-   typedef std::function<void ( SendBuffer & buffer )> SendBufferFunction;
+   using PackInfo = shared_ptr<blockforest::communication::NonUniformPackInfo>;
+   using VoidFunction = std::function<void ()>;
+   using SendBufferFunction = std::function<void (SendBuffer &)>;
 
    //**Construction & Destruction***************************************************************************************
    /*! \name Construction & Destruction */
diff --git a/src/blockforest/communication/NonUniformPackInfo.h b/src/blockforest/communication/NonUniformPackInfo.h
index a653169a5d90f6515e74b96d062feea779d68301..0b32369c654e4ca9642d88f5d85763f880b7e55d 100644
--- a/src/blockforest/communication/NonUniformPackInfo.h
+++ b/src/blockforest/communication/NonUniformPackInfo.h
@@ -44,8 +44,8 @@ public:
    //**Construction & Destruction************************************************************
    /*! \name Construction & Destruction */
    //@{
-            NonUniformPackInfo() {}
-   virtual ~NonUniformPackInfo() {}
+            NonUniformPackInfo() = default;
+   virtual ~NonUniformPackInfo() = default;
    //@}
    //*******************************************************************************************************************
 
diff --git a/src/blockforest/communication/UniformBufferedScheme.h b/src/blockforest/communication/UniformBufferedScheme.h
index b9c07bbecdd767fcb0dbcd0198fd6eedbfeb1b54..8677b5f83afe7c4ba8bb047fbd95724bc7ce3ac3 100644
--- a/src/blockforest/communication/UniformBufferedScheme.h
+++ b/src/blockforest/communication/UniformBufferedScheme.h
@@ -32,7 +32,6 @@
 #include "core/Set.h"
 #include "core/debug/CheckFunctions.h"
 #include "core/debug/Debug.h"
-#include "core/WeakPtrWrapper.h"
 #include "core/mpi/MPIManager.h"
 #include "core/mpi/OpenMPBufferSystem.h"
 #include "core/selectable/IsSetSelected.h"
@@ -81,22 +80,22 @@ template< typename Stencil_T >
 class UniformBufferedScheme
 {
 public:
-   typedef Stencil_T Stencil;
-   typedef mpi::SendBuffer SendBuffer;
-   typedef mpi::RecvBuffer RecvBuffer;
+   using Stencil = Stencil_T;
+   using SendBuffer = mpi::SendBuffer;
+   using RecvBuffer = mpi::RecvBuffer;
 
-   typedef shared_ptr< walberla::communication::UniformPackInfo >  PackInfo;
+   using PackInfo = shared_ptr<walberla::communication::UniformPackInfo>;
 
-   typedef std::function<void ()>                            VoidFunction;
-   typedef std::function<void ( SendBuffer & buffer )> SendBufferFunction;
+   using VoidFunction = std::function<void ()>;
+   using SendBufferFunction = std::function<void (SendBuffer &)>;
 
-   typedef walberla::communication::UniformPackInfo CommunicationItemInfo;
+   using CommunicationItemInfo = walberla::communication::UniformPackInfo;
 
    //**Construction & Destruction***************************************************************************************
    /*! \name Construction & Destruction */
    //@{
 
-   explicit UniformBufferedScheme( weak_ptr_wrapper<StructuredBlockForest> bf,
+   explicit UniformBufferedScheme( weak_ptr<StructuredBlockForest> bf,
                                    const int tag = 778 ) // waLBerla = 119+97+76+66+101+114+108+97
       : blockForest_( bf ),
         localMode_( START ),
@@ -111,7 +110,7 @@ public:
       forestModificationStamp_ = forest->getBlockForest().getModificationStamp();
    }
 
-   UniformBufferedScheme( weak_ptr_wrapper<StructuredBlockForest> bf,
+   UniformBufferedScheme( weak_ptr<StructuredBlockForest> bf,
                           const Set<SUID> & requiredBlockSelectors,
                           const Set<SUID> & incompatibleBlockSelectors,
                           const int tag = 778 ) // waLBerla = 119+97+76+66+101+114+108+97
@@ -177,7 +176,7 @@ protected:
 
 
 
-   weak_ptr_wrapper<StructuredBlockForest> blockForest_;
+   weak_ptr<StructuredBlockForest> blockForest_;
    uint_t forestModificationStamp_;
 
    std::vector< PackInfo > packInfos_;
diff --git a/src/blockforest/communication/UniformDirectScheme.h b/src/blockforest/communication/UniformDirectScheme.h
index bf3a73344604c24728eb34264cfb9027c485c938..0d10c2f4c383281e3b81aecf8c1778f1572ea13f 100644
--- a/src/blockforest/communication/UniformDirectScheme.h
+++ b/src/blockforest/communication/UniformDirectScheme.h
@@ -24,7 +24,6 @@
 
 #include "blockforest/StructuredBlockForest.h"
 #include "core/Set.h"
-#include "core/WeakPtrWrapper.h"
 #include "core/mpi/Datatype.h"
 #include "core/mpi/MPIManager.h"
 #include "core/mpi/MPIWrapper.h"
@@ -49,14 +48,14 @@ template< typename Stencil_T >
 class UniformDirectScheme
 {
 public:
-   typedef Stencil_T Stencil;
-   typedef walberla::communication::UniformMPIDatatypeInfo UniformMPIDatatypeInfo;
-   typedef walberla::communication::UniformMPIDatatypeInfo CommunicationItemInfo;
+   using Stencil = Stencil_T;
+   using UniformMPIDatatypeInfo = walberla::communication::UniformMPIDatatypeInfo;
+   using CommunicationItemInfo = walberla::communication::UniformMPIDatatypeInfo;
 
    //**Construction & Destruction***************************************************************************************
    /*! \name Construction & Destruction */
    //@{
-   explicit UniformDirectScheme( const weak_ptr_wrapper<StructuredBlockForest> & bf,
+   explicit UniformDirectScheme( const weak_ptr<StructuredBlockForest> & bf,
                                  const shared_ptr<UniformMPIDatatypeInfo> & dataInfo = shared_ptr<UniformMPIDatatypeInfo>(),
                                  const int tag = 778 ) // waLBerla = 119+97+76+66+101+114+108+97
       : blockForest_( bf ),
@@ -70,7 +69,7 @@ public:
          dataInfos_.push_back( dataInfo );
    }
 
-   UniformDirectScheme( const weak_ptr_wrapper<StructuredBlockForest> & bf,
+   UniformDirectScheme( const weak_ptr<StructuredBlockForest> & bf,
                         const Set<SUID> & requiredBlockSelectors,
                         const Set<SUID> & incompatibleBlockSelectors,
                         const shared_ptr<UniformMPIDatatypeInfo> & dataInfo = shared_ptr<UniformMPIDatatypeInfo>(),
@@ -140,7 +139,7 @@ protected:
       }
    };
 
-   weak_ptr_wrapper<StructuredBlockForest> blockForest_;
+   weak_ptr<StructuredBlockForest> blockForest_;
 
    bool setupRequired_;          //< this is set in the beginning or when new communication item was added
    bool communicationRunning_;   //< this is true between startCommunication() and wait()
diff --git a/src/blockforest/loadbalancing/Cartesian.h b/src/blockforest/loadbalancing/Cartesian.h
index 261473a9dc76f486545d91691929e71c387ed91d..a147f1c9875902f0dc7b5c10ae91226b901e8e64 100644
--- a/src/blockforest/loadbalancing/Cartesian.h
+++ b/src/blockforest/loadbalancing/Cartesian.h
@@ -35,7 +35,7 @@ class CartesianDistribution
 public:
 
    CartesianDistribution( const uint_t numberOfXProcesses, const uint_t numberOfYProcesses, const uint_t numberOfZProcesses,
-                          std::vector< uint_t > * processIdMap = NULL ) :
+                          std::vector< uint_t > * processIdMap = nullptr ) :
       numberOfXProcesses_( numberOfXProcesses ), numberOfYProcesses_( numberOfYProcesses ), numberOfZProcesses_( numberOfZProcesses ),
       processIdMap_( processIdMap )
    {}
diff --git a/src/blockforest/loadbalancing/DynamicCurve.h b/src/blockforest/loadbalancing/DynamicCurve.h
index 30724c40f91a67afdcd8781be216a4a44037c0a1..8b3e5808ac2c23b4b0a2016e91b5c18bd438f52f 100644
--- a/src/blockforest/loadbalancing/DynamicCurve.h
+++ b/src/blockforest/loadbalancing/DynamicCurve.h
@@ -53,8 +53,8 @@ template< typename PhantomData_T, typename Value_T, bool weighted = true >
 class BlockIDSorter
 {
 public:
-   typedef typename PhantomData_T::weight_t weight_t;
-   typedef std::vector< std::vector< std::pair< BlockID, weight_t > > > Blocks_T;
+   using weight_t = typename PhantomData_T::weight_t;
+   using Blocks_T = std::vector<std::vector<std::pair<BlockID, weight_t>>>;
    BlockIDSorter( const Blocks_T & blocks ) : blocks_( blocks ) {}
    bool operator()( const Value_T & lhs, const Value_T & rhs ) const
    {
@@ -68,7 +68,7 @@ template< typename PhantomData_T, typename Value_T >
 class BlockIDSorter< PhantomData_T, Value_T, false >
 {
 public:
-   typedef std::vector< std::vector< BlockID > > Blocks_T;
+   using Blocks_T = std::vector<std::vector<BlockID>>;
    BlockIDSorter( const Blocks_T & blocks ) : blocks_( blocks ) {}
    bool operator()( const Value_T & lhs, const Value_T & rhs ) const
    {
@@ -101,10 +101,10 @@ class DynamicCurveBalance
 {
 public:
 
-   typedef typename PhantomData_T::weight_t weight_t;
-   typedef mpi::MPIRank pid_t;
-   typedef uint16_t idx_t; // limits the maximum number of blocks per process to 65536
-   typedef internal::Node< pid_t, idx_t > Node;
+   using weight_t = typename PhantomData_T::weight_t;
+   using pid_t = mpi::MPIRank;
+   using idx_t = uint16_t; // limits the maximum number of blocks per process to 65536
+   using Node = internal::Node<pid_t, idx_t>;
 
    DynamicCurveBalance( const bool hilbert = true, const bool allGather = true, const bool levelwise = true ) :
       hilbert_( hilbert ), allGather_( allGather ), levelwise_(levelwise)
diff --git a/src/blockforest/loadbalancing/DynamicParMetis.h b/src/blockforest/loadbalancing/DynamicParMetis.h
index fa5663db5961e89cb57f4150cca10902706379d3..f78a4c4c055c56a6047c7b834125ead1af1219d3 100644
--- a/src/blockforest/loadbalancing/DynamicParMetis.h
+++ b/src/blockforest/loadbalancing/DynamicParMetis.h
@@ -92,8 +92,8 @@ class DynamicParMetisBlockInfo
 {
 public:
 
-   typedef int64_t weight_t;
-   typedef int64_t vsize_t;
+   using weight_t = int64_t;
+   using vsize_t = int64_t;
 
    DynamicParMetisBlockInfo( const weight_t vertexWeight, const uint_t ncon = 1 )
       : vertexWeight_(ncon, vertexWeight), vertexSize_(1)
diff --git a/src/blockforest/loadbalancing/NoPhantomData.h b/src/blockforest/loadbalancing/NoPhantomData.h
index a1662855d50a9477be38ded3e7ac7edfd945d129..7de1f9aaf90ac6576f8c09e5184cfea34b1280c5 100644
--- a/src/blockforest/loadbalancing/NoPhantomData.h
+++ b/src/blockforest/loadbalancing/NoPhantomData.h
@@ -31,7 +31,7 @@ namespace blockforest {
 class NoPhantomData
 {
 public:
-   typedef uint8_t weight_t;
+   using weight_t = uint8_t;
    weight_t weight() const { return uint8_t(1); }
 };
 
diff --git a/src/blockforest/loadbalancing/PODPhantomData.h b/src/blockforest/loadbalancing/PODPhantomData.h
index 2be8c77cecf13525222529e9dfbc2d31dfd35cea..2b8bc23eb8b4f0b5f1b49b7e5a690235d9e7384c 100644
--- a/src/blockforest/loadbalancing/PODPhantomData.h
+++ b/src/blockforest/loadbalancing/PODPhantomData.h
@@ -35,7 +35,7 @@ class PODPhantomWeight
 {
 public:
 
-   typedef T weight_t;
+   using weight_t = T;
 
    PODPhantomWeight( const T _weight ) : weight_( _weight ) {}
 
diff --git a/src/blockforest/loadbalancing/StaticParMetis.h b/src/blockforest/loadbalancing/StaticParMetis.h
index 0e77baf45b72be5a6f6991fed3c8cc49891f94f9..305d10a6d2c3486efb95f8113b62d115436471d6 100644
--- a/src/blockforest/loadbalancing/StaticParMetis.h
+++ b/src/blockforest/loadbalancing/StaticParMetis.h
@@ -40,8 +40,8 @@ public:
    enum Algorithm    { PARMETIS_PART_GEOM_KWAY, PARMETIS_PART_KWAY };
    enum WeightsToUse { PARMETIS_NO_WEIGHTS = 0, PARMETIS_EDGE_WEIGHTS = 1, PARMETIS_VERTEX_WEIGHTS = 2, PARMETIS_BOTH_WEIGHTS = 3 };
 
-   typedef std::pair< const SetupBlock *, const SetupBlock * > BlockPair;
-   typedef std::function<void (const std::vector< BlockPair > & edges, std::vector< int64_t > & weights ) > CommWeightFunction;
+   using BlockPair = std::pair<const SetupBlock *, const SetupBlock *>;
+   using CommWeightFunction = std::function<void (const std::vector<BlockPair> &, std::vector<int64_t> &)>;
 
    StaticLevelwiseParMetis( const Algorithm algorithm = PARMETIS_PART_GEOM_KWAY )
       : algorithm_( algorithm ), weightsToUse_( PARMETIS_VERTEX_WEIGHTS ) {}
diff --git a/src/blockforest/python/CommunicationExport.impl.h b/src/blockforest/python/CommunicationExport.impl.h
deleted file mode 100644
index 09041944f0ba0cf342c4a80fcf42e9e23abd207f..0000000000000000000000000000000000000000
--- a/src/blockforest/python/CommunicationExport.impl.h
+++ /dev/null
@@ -1,235 +0,0 @@
-//======================================================================================================================
-//
-//  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 CommunicationExport.impl.h
-//! \ingroup blockforest
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-
-#include "blockforest/communication/UniformBufferedScheme.h"
-#include "blockforest/communication/UniformDirectScheme.h"
-#include "python_coupling/helper/MplHelpers.h"
-
-namespace walberla {
-namespace blockforest {
-
-namespace internal
-{
-   //===================================================================================================================
-   //
-   //  UniformBufferedScheme
-   //
-   //===================================================================================================================
-
-   /// for details see documentation in core/WeakPtrWrapper.h
-   /// the purpose of this class could also be solved by adding return_internal_reference to "createUniformDirectScheme"
-   /// however this is not easily possible since it returns not a reference but an boost::python::object
-   template<typename Stencil>
-   class UniformBufferedSchemeWrapper : public blockforest::communication::UniformBufferedScheme<Stencil>
-   {
-   public:
-      UniformBufferedSchemeWrapper( const shared_ptr<StructuredBlockForest> & bf, const int tag )
-         : blockforest::communication::UniformBufferedScheme<Stencil>( bf, tag),
-           blockforest_( bf)
-      {}
-   private:
-      shared_ptr< StructuredBlockForest > blockforest_;
-   };
-
-
-   struct UniformBufferedSchemeExporter
-   {
-      template<typename Stencil>
-      void operator() ( python_coupling::NonCopyableWrap<Stencil> )
-      {
-         using namespace boost::python;
-         typedef UniformBufferedSchemeWrapper<Stencil> UBS;
-
-         class_< UBS, shared_ptr<UBS>, boost::noncopyable >( "UniformBufferedScheme", no_init )
-                  .def( "__call__",             &UBS::operator()             )
-                  .def( "communicate",          &UBS::communicate            )
-                  .def( "startCommunication",   &UBS::startCommunication     )
-                  .def( "wait",                 &UBS::wait                   )
-                  .def( "addPackInfo",          &UBS::addPackInfo            )
-                  .def( "addDataToCommunicate", &UBS::addDataToCommunicate   )
-                  .def( "localMode",            &UBS::localMode              )
-                  .def( "setLocalMode",         &UBS::setLocalMode           )
-               ;
-      }
-   };
-
-   class UniformBufferedSchemeCreator
-   {
-   public:
-      UniformBufferedSchemeCreator( const shared_ptr<StructuredBlockForest> & bf,
-                                    const std::string & stencilName,
-                                    const int tag )
-         : blockforest_( bf), stencilName_( stencilName ), tag_( tag )
-      {}
-
-      template<typename Stencil>
-      void operator() ( python_coupling::NonCopyableWrap<Stencil> )
-      {
-
-         using namespace boost::python;
-         if ( std::string(Stencil::NAME) == stencilName_ ) {
-            result_ = object ( make_shared< UniformBufferedSchemeWrapper<Stencil> > ( blockforest_, tag_ ) );
-         }
-      }
-
-      boost::python::object getResult() { return result_; }
-   private:
-      shared_ptr<StructuredBlockForest> blockforest_;
-      std::string stencilName_;
-      const int tag_;
-      boost::python::object result_;
-   };
-
-
-   template<typename Stencils>
-   boost::python::object createUniformBufferedScheme( const shared_ptr<StructuredBlockForest> & bf,
-                                                      const std::string & stencil,
-                                                      const int tag )
-   {
-      UniformBufferedSchemeCreator creator( bf, stencil, tag );
-      python_coupling::for_each_noncopyable_type< Stencils >  ( std::ref(creator) );
-
-      if ( creator.getResult() == boost::python::object() )
-      {
-         PyErr_SetString( PyExc_RuntimeError, "Unknown stencil.");
-         throw boost::python::error_already_set();
-      }
-      return creator.getResult();
-   }
-
-   //===================================================================================================================
-   //
-   //  UniformDirectScheme
-   //
-   //===================================================================================================================
-
-   template<typename Stencil>
-   class UniformDirectSchemeWrapper : public blockforest::communication::UniformDirectScheme<Stencil>
-   {
-   public:
-      UniformDirectSchemeWrapper( const shared_ptr<StructuredBlockForest> & bf, const int tag )
-         : blockforest::communication::UniformDirectScheme<Stencil>( bf, shared_ptr<walberla::communication::UniformMPIDatatypeInfo>(), tag),
-           blockforest_( bf)
-      {}
-   private:
-      shared_ptr< StructuredBlockForest > blockforest_;
-   };
-
-   struct UniformDirectSchemeExporter
-   {
-      template<typename Stencil>
-      void operator() ( python_coupling::NonCopyableWrap<Stencil> )
-      {
-         using namespace boost::python;
-         typedef UniformDirectSchemeWrapper<Stencil> UDS;
-
-         class_< UDS, shared_ptr<UDS>, boost::noncopyable >( "UniformDirectScheme", no_init )
-                  .def( "__call__",             &UDS::operator()             )
-                  .def( "communicate",          &UDS::communicate            )
-                  .def( "startCommunication",   &UDS::startCommunication     )
-                  .def( "wait",                 &UDS::wait                   )
-                  .def( "addDataToCommunicate", &UDS::addDataToCommunicate   )
-               ;
-      }
-   };
-
-   class UniformDirectSchemeCreator
-   {
-   public:
-      UniformDirectSchemeCreator( const shared_ptr<StructuredBlockForest> & bf,
-                                    const std::string & stencilName,
-                                    const int tag )
-         : blockforest_( bf), stencilName_( stencilName ), tag_( tag )
-      {}
-
-      template<typename Stencil>
-      void operator() ( python_coupling::NonCopyableWrap<Stencil> )
-      {
-
-         using namespace boost::python;
-         if ( std::string(Stencil::NAME) == stencilName_ ) {
-            result_ = object ( make_shared< UniformDirectSchemeWrapper<Stencil> > ( blockforest_, tag_ ) );
-         }
-      }
-
-      boost::python::object getResult() { return result_; }
-   private:
-      shared_ptr<StructuredBlockForest> blockforest_;
-      std::string stencilName_;
-      const int tag_;
-      boost::python::object result_;
-   };
-
-
-   template<typename Stencils>
-   boost::python::object createUniformDirectScheme( const shared_ptr<StructuredBlockForest> & bf,
-                                                    const std::string & stencil, const int tag )
-   {
-      UniformDirectSchemeCreator creator( bf, stencil, tag );
-      python_coupling::for_each_noncopyable_type< Stencils >  ( std::ref(creator) );
-
-      if ( creator.getResult() == boost::python::object() )
-      {
-         PyErr_SetString( PyExc_RuntimeError, "Unknown stencil.");
-         throw boost::python::error_already_set();
-      }
-      return creator.getResult();
-   }
-
-}
-
-
-template<typename Stencils>
-void exportUniformBufferedScheme()
-{
-   using namespace boost::python;
-
-   enum_<LocalCommunicationMode>("LocalCommunicationMode")
-       .value("START", START)
-       .value("WAIT", WAIT)
-       .value("BUFFER", BUFFER)
-       .export_values();
-
-   python_coupling::for_each_noncopyable_type< Stencils >  ( internal::UniformBufferedSchemeExporter() );
-
-   def( "createUniformBufferedScheme", &internal::createUniformBufferedScheme<Stencils>,
-            ( ( arg("blockForest"), arg("stencilName"), arg("tag")=778  ) ) );
-
-}
-
-template<typename Stencils>
-void exportUniformDirectScheme()
-{
-   using namespace boost::python;
-
-   python_coupling::for_each_noncopyable_type< Stencils >  ( internal::UniformDirectSchemeExporter() );
-
-   def( "createUniformDirectScheme", &internal::createUniformDirectScheme<Stencils>,
-            ( ( arg("blockForest"), arg("stencilName"), arg("tag")=778  ) ) );
-}
-
-
-
-} // namespace blockforest
-} // namespace walberla
-
-
diff --git a/src/blockforest/python/Exports.cpp b/src/blockforest/python/Exports.cpp
deleted file mode 100644
index 08873381e321bf8525bf3a5f39916c17233d2532..0000000000000000000000000000000000000000
--- a/src/blockforest/python/Exports.cpp
+++ /dev/null
@@ -1,327 +0,0 @@
-//======================================================================================================================
-//
-//  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 PythonExports.cpp
-//! \ingroup domain_decomposition
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-// Do not reorder includes - the include order is important
-#include "python_coupling/PythonWrapper.h"
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-#include "blockforest/StructuredBlockForest.h"
-#include "blockforest/communication/UniformBufferedScheme.h"
-#include "blockforest/Initialization.h"
-#include "blockforest/SetupBlock.h"
-#include "blockforest/SetupBlockForest.h"
-#include "blockforest/loadbalancing/StaticCurve.h"
-
-#include "core/logging/Logging.h"
-#include "core/StringUtility.h"
-#include "domain_decomposition/StructuredBlockStorage.h"
-#include "python_coupling/Manager.h"
-#include "python_coupling/helper/ConfigFromDict.h"
-
-#include "stencil/D3Q7.h"
-#include "stencil/D3Q19.h"
-#include "stencil/D3Q27.h"
-
-#include <memory>
-
-#include <sstream>
-
-#ifdef _MSC_VER
-#  pragma warning(push)
-// disable warning boost/python/raw_function.hpp(55): warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data
-#  pragma warning( disable : 4267 )
-#endif //_MSC_VER
-#include <boost/python/raw_function.hpp>
-#ifdef _MSC_VER
-#  pragma warning(pop)
-#endif //_MSC_VER
-
-
-using namespace boost::python;
-
-
-namespace walberla {
-namespace blockforest {
-
-using walberla::blockforest::communication::UniformBufferedScheme;
-
-bool checkForThreeTuple( object obj ) //NOLINT
-{
-   if( ! extract<tuple> ( obj ).check() )
-      return false;
-
-   tuple t = extract<tuple> ( obj );
-   return len(t) == 3;
-}
-
-
-object python_createUniformBlockGrid(tuple args, dict kw) //NOLINT
-{
-   if( len(args) > 0 ) {
-      PyErr_SetString( PyExc_ValueError, "This function takes only keyword arguments" );
-      throw boost::python::error_already_set();
-   }
-
-   using boost::python::stl_input_iterator;
-
-   boost::python::list keys = kw.keys();
-   for( auto it = stl_input_iterator<std::string>( keys ); it != stl_input_iterator<std::string>(); ++it )
-   {
-      if ( *it != "cells" &&
-           *it != "cellsPerBlock" &&
-           *it != "blocks" &&
-           *it != "periodic" &&
-           *it != "dx" &&
-           *it != "oneBlockPerProcess"  )
-      {
-         PyErr_SetString( PyExc_ValueError, (std::string("Unknown Parameter: ") + (*it) ).c_str() );
-         throw boost::python::error_already_set();
-      }
-   }
-
-   if( kw.has_key("cells ") && ! checkForThreeTuple( kw["cells"] ) ) {
-      PyErr_SetString( PyExc_ValueError, "Parameter 'cells' has to be tuple of length 3, indicating cells in x,y,z direction" );
-      throw boost::python::error_already_set();
-   }
-   if( kw.has_key("cellsPerBlock ") && ! checkForThreeTuple( kw["cellsPerBlock"] ) ) {
-      PyErr_SetString( PyExc_ValueError, "Parameter 'cellsPerBlock' has to be tuple of length 3, indicating cells in x,y,z direction" );
-      throw boost::python::error_already_set();
-   }
-   if( kw.has_key("blocks ") && ! checkForThreeTuple( kw["blocks"] ) ) {
-      PyErr_SetString( PyExc_ValueError, "Parameter 'blocks' has to be tuple of length 3, indicating cells in x,y,z direction" );
-      throw boost::python::error_already_set();
-   }
-
-   bool keepGlobalBlockInformation = false;
-   if ( kw.has_key("keepGlobalBlockInformation") )
-   {
-      if ( extract<bool>( kw["keepGlobalBlockInformation"] ).check() )
-         keepGlobalBlockInformation = extract<bool>( kw["keepGlobalBlockInformation"] );
-      else
-      {
-         PyErr_SetString( PyExc_ValueError, "Parameter 'keepGlobalBlockInformation' has to be a boolean" );
-         throw boost::python::error_already_set();
-      }
-   }
-
-   shared_ptr<Config> cfg = python_coupling::configFromPythonDict( kw );
-
-   try {
-      shared_ptr< StructuredBlockForest > blocks = createUniformBlockGridFromConfig( cfg->getGlobalBlock(), nullptr, keepGlobalBlockInformation );
-      return object(blocks);
-   }
-   catch( std::exception & e)
-   {
-      PyErr_SetString( PyExc_ValueError, e.what() );
-      throw boost::python::error_already_set();
-   }
-
-}
-
-shared_ptr<StructuredBlockForest> createStructuredBlockForest( Vector3<uint_t> blocks,
-                                                               Vector3<uint_t> cellsPerBlock,
-                                                               Vector3<bool> periodic,
-                                                               object blockExclusionCallback = object(),
-                                                               object workloadMemoryCallback = object(),
-                                                               object refinementCallback = object(),
-                                                               const real_t dx = 1.0,
-                                                               memory_t processMemoryLimit = std::numeric_limits<memory_t>::max(),
-                                                               const bool keepGlobalBlockInformation = false)
-{
-   using namespace blockforest;
-   Vector3<real_t> bbMax;
-   for( uint_t i=0; i < 3; ++i )
-      bbMax[i] = real_c( blocks[i] * cellsPerBlock[i] ) * dx;
-   AABB domainAABB ( Vector3<real_t>(0),  bbMax );
-
-   SetupBlockForest sforest;
-
-   auto blockExclusionFunc = [&blockExclusionCallback] ( std::vector<walberla::uint8_t>& excludeBlock, const SetupBlockForest::RootBlockAABB& aabb ) -> void
-   {
-      for( uint_t i = 0; i != excludeBlock.size(); ++i )
-      {
-         AABB bb = aabb(i);
-         auto pythonReturnVal = blockExclusionCallback(bb);
-         if( ! extract<bool>( pythonReturnVal ).check() ) {
-            PyErr_SetString( PyExc_ValueError, "blockExclusionCallback has to return a boolean");
-            throw boost::python::error_already_set();
-         }
-
-         bool returnVal = extract<bool>(pythonReturnVal);
-         if ( returnVal )
-            excludeBlock[i] = 1;
-      }
-   };
-
-   auto workloadMemoryFunc = [&workloadMemoryCallback] ( SetupBlockForest & forest )-> void
-   {
-      std::vector< SetupBlock* > blockVector;
-      forest.getBlocks( blockVector );
-
-      for( uint_t i = 0; i != blockVector.size(); ++i ) {
-         blockVector[i]->setMemory( memory_t(1) );
-         blockVector[i]->setWorkload( workload_t(1) );
-         workloadMemoryCallback( boost::python::ptr(blockVector[i]) );
-      }
-   };
-
-   auto refinementFunc = [&refinementCallback] ( SetupBlockForest & forest )-> void
-   {
-      for( auto block = forest.begin(); block != forest.end(); ++block )
-      {
-         SetupBlock * sb = &(*block);
-         auto pythonRes = refinementCallback( boost::python::ptr(sb) );
-         if( ! extract<bool>( pythonRes ).check() ) {
-            PyErr_SetString( PyExc_ValueError, "refinementCallback has to return a boolean");
-            throw boost::python::error_already_set();
-         }
-         bool returnVal = extract<bool>( pythonRes );
-         if( returnVal )
-            block->setMarker( true );
-      }
-   };
-
-   if ( blockExclusionCallback ) {
-      if( !PyCallable_Check( blockExclusionCallback.ptr() ) ) {
-         PyErr_SetString( PyExc_ValueError, "blockExclusionCallback has to be callable");
-         throw boost::python::error_already_set();
-      }
-      sforest.addRootBlockExclusionFunction( blockExclusionFunc );
-   }
-
-   if ( workloadMemoryCallback ) {
-      if( !PyCallable_Check( workloadMemoryCallback.ptr() ) ) {
-         PyErr_SetString( PyExc_ValueError, "workloadMemoryCallback has to be callable");
-         throw boost::python::error_already_set();
-      }
-      sforest.addWorkloadMemorySUIDAssignmentFunction( workloadMemoryFunc );
-   }
-   else
-      sforest.addWorkloadMemorySUIDAssignmentFunction( uniformWorkloadAndMemoryAssignment );
-
-   if ( refinementCallback ) {
-      if( !PyCallable_Check( refinementCallback.ptr() ) ) {
-         PyErr_SetString( PyExc_ValueError, "refinementCallback has to be callable");
-         throw boost::python::error_already_set();
-      }
-      sforest.addRefinementSelectionFunction( refinementFunc );
-   }
-
-   sforest.init( domainAABB, blocks[0], blocks[1], blocks[2], periodic[0], periodic[1], periodic[2] );
-
-   // calculate process distribution
-   sforest.balanceLoad( blockforest::StaticLevelwiseCurveBalanceWeighted(),
-                        uint_c( MPIManager::instance()->numProcesses() ),
-                        real_t(0), processMemoryLimit );
-
-   if( !MPIManager::instance()->rankValid() )
-      MPIManager::instance()->useWorldComm();
-
-   // create StructuredBlockForest (encapsulates a newly created BlockForest)
-   auto bf = std::make_shared< BlockForest >( uint_c( MPIManager::instance()->rank() ), sforest, keepGlobalBlockInformation );
-
-   auto sbf = std::make_shared< StructuredBlockForest >( bf, cellsPerBlock[0], cellsPerBlock[1], cellsPerBlock[2] );
-   sbf->createCellBoundingBoxes();
-
-   return sbf;
-}
-
-object createUniformNeighborScheme(  const shared_ptr<StructuredBlockForest> & bf,
-                                     const std::string & stencil )
-{
-   if ( string_icompare(stencil, "D3Q7") == 0 )
-      return object ( make_shared< UniformBufferedScheme<stencil::D3Q7> > ( bf ) );
-   if ( string_icompare(stencil, "D3Q19") == 0 )
-      return object ( make_shared< UniformBufferedScheme<stencil::D3Q19> > ( bf ) );
-   if ( string_icompare(stencil, "D3Q27") == 0 )
-      return object ( make_shared< UniformBufferedScheme<stencil::D3Q27> > ( bf ) );
-   else {
-      PyErr_SetString( PyExc_RuntimeError, "Unknown stencil. Allowed values 'D3Q27', 'D3Q19', 'D3Q7'");
-      throw error_already_set();
-      return object();
-   }
-}
-
-template<typename Stencil>
-void exportUniformBufferedScheme()
-{
-   typedef UniformBufferedScheme<Stencil> UNS;
-
-   class_< UNS, shared_ptr<UNS>, boost::noncopyable >( "UniformBufferedScheme", no_init )
-            .def( "__call__",             &UNS::operator()             )
-            .def( "communicate",          &UNS::communicate            )
-            .def( "startCommunication",   &UNS::startCommunication     )
-            .def( "wait",                 &UNS::wait                   )
-            .def( "addPackInfo",          &UNS::addPackInfo            )
-            .def( "addDataToCommunicate", &UNS::addDataToCommunicate   )
-    ;
-
-}
-
-std::string printSetupBlock(const SetupBlock & b )
-{
-   std::stringstream out;
-   out <<  "SetupBlock at " << b.getAABB();
-   return out.str();
-}
-
-
-
-void exportBlockForest()
-{
-   class_< StructuredBlockForest, //NOLINT
-           shared_ptr<StructuredBlockForest>,
-           bases<StructuredBlockStorage>, boost::noncopyable > ( "StructuredBlockForest", no_init );
-
-   class_< SetupBlock, boost::noncopyable > ( "SetupBlock", no_init )
-            .add_property("level", &SetupBlock::getLevel)
-            .add_property("workload", &SetupBlock::getWorkload, &SetupBlock::setWorkload)
-            .add_property("memory",  &SetupBlock::getMemory, &SetupBlock::setMemory)
-            .add_property("aabb", make_function(&SetupBlock::getAABB, return_value_policy<copy_const_reference>()))
-            .def("__repr__", &printSetupBlock)
-            ;
-
-#ifdef _MSC_VER
-#  pragma warning(push)
-// disable warning boost/python/raw_function.hpp(55): warning C4267: 'argument' : conversion from 'size_t' to 'int', possible loss of data
-#  pragma warning( disable : 4267 )
-#endif //_MSC_VER
-   def( "createUniformBlockGrid", raw_function(python_createUniformBlockGrid) );
-#ifdef _MSC_VER
-#  pragma warning(pop)
-#endif //_MSC_VER
-
-   def( "createCustomBlockGrid", createStructuredBlockForest,
-               (arg("blocks"), arg("cellsPerBlock"), arg("periodic"),
-                arg("blockExclusionCallback") = object(),
-                arg("workloadMemoryCallback") = object(),
-                arg("refinementCallback") = object() ,
-                arg("dx") = 1.0,
-                arg("processMemoryLimit") = std::numeric_limits<memory_t>::max(),
-                arg("keepGlobalBlockInformation") = false ) );
-}
-
-} // namespace domain_decomposition
-} // namespace walberla
-
-
-#endif //WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/blockforest/python/Exports.h b/src/blockforest/python/Exports.h
deleted file mode 100644
index a910b0541d075fd3e93948824d91b41a12090ea4..0000000000000000000000000000000000000000
--- a/src/blockforest/python/Exports.h
+++ /dev/null
@@ -1,51 +0,0 @@
-//======================================================================================================================
-//
-//  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 PythonExports.h
-//! \ingroup blockforest
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include "waLBerlaDefinitions.h"
-
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-#include "blockforest/python/CommunicationExport.h"
-
-
-namespace walberla {
-namespace blockforest {
-
-
-   void exportBlockForest();
-
-
-   template<typename Stencils>
-   void exportModuleToPython()
-   {
-      exportBlockForest();
-      exportUniformBufferedScheme<Stencils>();
-      exportUniformDirectScheme<Stencils>();
-   }
-
-} // namespace blockforest
-} // namespace walberla
-
-
-#endif // WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/boundary/Boundary.h b/src/boundary/Boundary.h
index 8d83aea92e3db51f7a045495dd19615e0ad071f0..d7b3679f8aa9278751989182f6827b2a5fbfbcc4 100644
--- a/src/boundary/Boundary.h
+++ b/src/boundary/Boundary.h
@@ -36,7 +36,7 @@ namespace boundary {
 
 class BoundaryConfiguration {
 public:
-   virtual ~BoundaryConfiguration() {}
+   virtual ~BoundaryConfiguration() = default;
    static const BoundaryConfiguration& null()                { return *boundaryNullPtr; }
    static const shared_ptr<BoundaryConfiguration> nullPtr()  { return  boundaryNullPtr; }
 private:
diff --git a/src/boundary/BoundaryHandling.h b/src/boundary/BoundaryHandling.h
index 96f93c991b7dc80579317daf06940b03d7c98525..9aa5a34390fb71d7479d512130dca4d81dc982b6 100644
--- a/src/boundary/BoundaryHandling.h
+++ b/src/boundary/BoundaryHandling.h
@@ -66,7 +66,7 @@ using std::tuple_size;
 
 
 class BHUIDGenerator : public uid::IndexGenerator< BHUIDGenerator, uint_t >{};
-typedef UID< BHUIDGenerator > BoundaryHandlingUID;
+using BoundaryHandlingUID = UID<BHUIDGenerator>;
 
 
 
@@ -78,9 +78,9 @@ public:
    template< typename F, typename... T >
    friend class BoundaryHandlingCollection;
 
-   typedef FlagField_T                               FlagField;
-   typedef typename FlagField_T::flag_t              flag_t;
-   typedef typename FlagField_T::const_base_iterator ConstFlagFieldBaseIterator;
+   using FlagField = FlagField_T;
+   using flag_t = typename FlagField_T::flag_t;
+   using ConstFlagFieldBaseIterator = typename FlagField_T::const_base_iterator;
 
    enum Mode { OPTIMIZED_SPARSE_TRAVERSAL, ENTIRE_FIELD_TRAVERSAL };
 
@@ -414,7 +414,7 @@ private:
 
    template< typename Boundary_T, typename BoundariesTuple, int N = std::tuple_size<BoundariesTuple>::value - 1 >
    inline const typename std::enable_if<(N!=0), Boundary_T>::type & getBoundaryCondition( const BoundaryUID & uid, const BoundariesTuple & boundaryConditions,
-                                                   typename std::enable_if< std::is_same< Boundary_T, typename std::tuple_element<N, BoundariesTuple>::type >::value >::type* /*dummy*/ = 0 ) const
+                                                   typename std::enable_if< std::is_same< Boundary_T, typename std::tuple_element<N, BoundariesTuple>::type >::value >::type* /*dummy*/ = nullptr ) const
    {
       if( uid == std::get<N>( boundaryConditions ).getUID() )
          return std::get<N>( boundaryConditions );
@@ -424,7 +424,7 @@ private:
 
    template< typename Boundary_T, typename BoundariesTuple, int N = std::tuple_size<BoundariesTuple>::value - 1 >
    inline const typename std::enable_if<(N==0), Boundary_T>::type & getBoundaryCondition( const BoundaryUID & uid, const BoundariesTuple & boundaryConditions,
-                                                   typename std::enable_if< std::is_same< Boundary_T, typename std::tuple_element<N, BoundariesTuple>::type >::value >::type* /*dummy*/ = 0 ) const
+                                                   typename std::enable_if< std::is_same< Boundary_T, typename std::tuple_element<N, BoundariesTuple>::type >::value >::type* /*dummy*/ = nullptr ) const
    {
       if( uid == std::get<N>( boundaryConditions ).getUID() )
          return std::get<N>( boundaryConditions );
@@ -439,8 +439,8 @@ private:
    template< typename Boundary_T, typename BoundariesTuple, int N = std::tuple_size<BoundariesTuple>::value - 1 >
    inline const typename std::enable_if<(N!=0), Boundary_T>::type & getBoundaryCondition( const BoundaryUID & uid, const BoundariesTuple & boundaryConditions,
                                                    typename std::enable_if< std::is_same< typename std::is_same< Boundary_T, typename std::tuple_element<N, BoundariesTuple>::type >::type,
-                                                                                              std::false_type >::value >::type* /*dummy*/ = 0,
-                                                   typename std::enable_if< (N>0) >::type* /*dummy*/ = 0 ) const
+                                                                                              std::false_type >::value >::type* /*dummy*/ = nullptr,
+                                                   typename std::enable_if< (N>0) >::type* /*dummy*/ = nullptr ) const
    {
       return getBoundaryCondition< Boundary_T, BoundariesTuple, N-1 >( uid, boundaryConditions );
    }
@@ -490,7 +490,7 @@ private:
    template< typename Boundary_T, typename BoundariesTuple, int N = std::tuple_size<BoundariesTuple>::value - 1 >
    inline const typename std::enable_if<(N==0), Boundary_T>::type & getBoundaryCondition_TypeExists( const BoundaryUID & uid, const BoundariesTuple & /*boundaryConditions*/,
                                                               typename std::enable_if< std::is_same< typename std::is_same< Boundary_T, typename std::tuple_element<0, BoundariesTuple>::type >::type,
-                                                                                                         std::false_type >::value >::type* /*dummy*/ = 0 ) const
+                                                                                                         std::false_type >::value >::type* /*dummy*/ = nullptr ) const
    {
       WALBERLA_ABORT( "The requested boundary condition " << uid.getIdentifier() << " is not part of this boundary handling." );
 
@@ -664,7 +664,7 @@ private:
    std::vector< std::vector< std::vector< std::pair< Cell, stencil::Direction > > > > cellDirectionPairs_; // 1st vector: numberOfGhostLayersToInclude
                                                                                                            // 2nd vector: boundary condition index
                                                                                                            // 3rd vector: vector of cell<->direction pairs
-   typedef std::tuple<Boundaries...> Tuple;
+   using Tuple = std::tuple<Boundaries...>;
    Tuple boundaryConditions_;
    bool  threadSafeBCs_;
 
@@ -2230,7 +2230,7 @@ inline void BoundaryHandling< FlagField_T, Stencil, Boundaries... >::operator()(
 
    WALBERLA_ASSERT( checkConsistency( localCells ) );
 
-   #ifdef _OPENMP
+   #if defined(_OPENMP) && !(defined(_MSC_VER) && _MSC_VER < 1925)
    const int zMin = int_c( localCells.zMin() );
    const int zMax = int_c( localCells.zMax() );
    #pragma omp parallel for schedule(static) if(threadSafeBCs_)
diff --git a/src/boundary/BoundaryHandlingCollection.h b/src/boundary/BoundaryHandlingCollection.h
index 61320c1788d2a7725e08cae6faa4fc24481557d1..7b94d4a0e2c47b12724162ce89c007c850de964d 100644
--- a/src/boundary/BoundaryHandlingCollection.h
+++ b/src/boundary/BoundaryHandlingCollection.h
@@ -49,7 +49,7 @@ namespace boundary {
 
 
 class BHCUIDGenerator : public uid::IndexGenerator< BHCUIDGenerator, uint_t >{};
-typedef UID< BHCUIDGenerator > BoundaryHandlingCollectionUID;
+using BoundaryHandlingCollectionUID = UID<BHCUIDGenerator>;
 
 
 
@@ -58,9 +58,9 @@ class BoundaryHandlingCollection
 {
 public:
 
-   typedef FlagField_T                               FlagField;
-   typedef typename FlagField_T::flag_t              flag_t;
-   typedef typename FlagField_T::const_base_iterator ConstFlagFieldBaseIterator;
+   using FlagField = FlagField_T;
+   using flag_t = typename FlagField_T::flag_t;
+   using ConstFlagFieldBaseIterator = typename FlagField_T::const_base_iterator;
 
 
 
@@ -534,7 +534,7 @@ private:
 
    const CellInterval outerBB_;
 
-   typedef std::tuple<Handlers...> Tuple;
+   using Tuple = std::tuple<Handlers...>;
    Tuple boundaryHandlers_;
 
 }; // class BoundaryHandlingCollection
diff --git a/src/boundary/BoundaryUID.h b/src/boundary/BoundaryUID.h
index 3df48a8c8b0ae31c9f109c376b429d55bf2e9367..0f3a86bb125faffce711309f137cc1a9878c6036 100644
--- a/src/boundary/BoundaryUID.h
+++ b/src/boundary/BoundaryUID.h
@@ -29,7 +29,7 @@ namespace boundary {
 
 
 class BUIDGenerator : public uid::IndexGenerator< BUIDGenerator, size_t >{};
-typedef UID< BUIDGenerator > BoundaryUID;
+using BoundaryUID = UID<BUIDGenerator>;
 
 
 } // namespace boundary
diff --git a/src/boundary/communication/HandlingPackInfo.h b/src/boundary/communication/HandlingPackInfo.h
index d730bf2adbf07b05f8460452c167c7074e3af3b1..460ec60e4ec4945154a4d8b0ec3d797a18f486bd 100644
--- a/src/boundary/communication/HandlingPackInfo.h
+++ b/src/boundary/communication/HandlingPackInfo.h
@@ -39,18 +39,18 @@ public:
    HandlingPackInfo( const BlockDataID & bdId, const bool assumeIdenticalFlagMapping = true, const uint_t numberOfLayers = 0 ) :
       bdId_( bdId ), numberOfLayers_( numberOfLayers ), assumeIdenticalFlagMapping_( assumeIdenticalFlagMapping ) {}
 
-   ~HandlingPackInfo() {}
+   ~HandlingPackInfo() override = default;
 
-   bool constantDataExchange() const { return false; }
-   bool threadsafeReceiving()  const { return false; }
+   bool constantDataExchange() const override { return false; }
+   bool threadsafeReceiving()  const override { return false; }
 
-   void unpackData( IBlock * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer );
+   void unpackData( IBlock * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer ) override;
 
-   void communicateLocal( const IBlock * sender, IBlock * receiver, stencil::Direction dir );
+   void communicateLocal( const IBlock * sender, IBlock * receiver, stencil::Direction dir ) override;
 
 protected:
 
-   void packDataImpl( const IBlock * sender, stencil::Direction dir, mpi::SendBuffer & buffer ) const;
+   void packDataImpl( const IBlock * sender, stencil::Direction dir, mpi::SendBuffer & buffer ) const override;
 
 
 
diff --git a/src/boundary/python/Exports.impl.h b/src/boundary/python/Exports.impl.h
deleted file mode 100644
index e6b5d85cb5748d6f6097048b4e4b846626c0a9f4..0000000000000000000000000000000000000000
--- a/src/boundary/python/Exports.impl.h
+++ /dev/null
@@ -1,232 +0,0 @@
-//======================================================================================================================
-//
-//  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 Exports.impl.h
-//! \ingroup boundary
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#include "python_coupling/Manager.h"
-#include "python_coupling/helper/SliceToCellInterval.h"
-#include "python_coupling/helper/ConfigFromDict.h"
-#include "boundary/Boundary.h"
-#include "field/FlagField.h"
-
-
-namespace walberla {
-namespace boundary {
-
-   namespace internal
-   {
-      using python_coupling::localPythonSliceToCellInterval;
-
-      template<typename BH>
-      shared_ptr<BoundaryConfiguration> boundaryConfFromDict( BH & h, const BoundaryUID & uid, boost::python::dict d ) {
-         shared_ptr<Config> cfg = python_coupling::configFromPythonDict( d );
-         return h.createBoundaryConfiguration( uid, cfg->getGlobalBlock() );
-      }
-      template<typename BH>
-      void BH_setBoundary1( BH & h, const std::string & name, cell_idx_t x, cell_idx_t y , cell_idx_t z, boost::python::dict conf ) {
-         h.setBoundary( name,x,y,z,  *boundaryConfFromDict( h, name, conf) );
-      }
-      template<typename BH>
-      void BH_setBoundary2( BH & h, const std::string & name, const boost::python::tuple & index, boost::python::dict conf ) {
-         h.setBoundary( name,
-                        localPythonSliceToCellInterval( *h.getFlagField() , index ),
-                        *boundaryConfFromDict( h, name, conf) );
-      }
-      template<typename BH>
-      void BH_forceBoundary1( BH & h, const std::string & name, cell_idx_t x, cell_idx_t y , cell_idx_t z, boost::python::dict conf ) {
-         h.forceBoundary( name,x,y,z,  *boundaryConfFromDict( h, name, conf) );
-      }
-      template<typename BH>
-      void BH_forceBoundary2( BH & h, const std::string & name, const boost::python::tuple & index, boost::python::dict conf )  {
-         h.forceBoundary( name,
-                        localPythonSliceToCellInterval( *h.getFlagField() , index ),
-                        *boundaryConfFromDict( h, name, conf) );
-      }
-
-      template<typename BH>
-      void BH_forceBoundary3( BH & h, const GhostLayerField<int,1> & indexField , boost::python::dict boundaryInfo )
-      {
-         using namespace boost::python;
-         list keys = boundaryInfo.keys();
-
-         std::map<int, FlagUID > flagUIDs;
-         std::map<int, shared_ptr<BoundaryConfiguration> > boundaryConfigs;
-
-         for (int i = 0; i < len( keys ); ++i)
-         {
-            int key =  extract<int>( keys[i] );
-            extract<std::string> extracted_str_val  ( boundaryInfo[key] );
-            extract<dict       > extracted_dict_val ( boundaryInfo[key] );
-
-            if ( extracted_str_val.check() )
-            {
-               std::string boundaryName = extracted_str_val;
-               flagUIDs[key] = FlagUID ( boundaryName );
-            }
-            else if ( extracted_dict_val.check() )
-            {
-               dict info = extracted_dict_val;
-               std::string boundaryName = extract<std::string>( info["name"] );
-
-               dict configDict = extract<dict>( info["config"] );
-
-               flagUIDs[key] = FlagUID ( boundaryName );
-               boundaryConfigs[key] = boundaryConfFromDict( h, boundaryName, configDict);
-            }
-            else {
-               PyErr_SetString( PyExc_ValueError, "Invalid parameter");
-               throw error_already_set();
-            }
-         }
-
-         if ( indexField.xyzSize() != h.getFlagField()->xyzSize() || indexField.nrOfGhostLayers() > h.getFlagField()->nrOfGhostLayers() ) {
-            PyErr_SetString( PyExc_ValueError, "Index field has to have same size as flag field");
-            throw error_already_set();
-         }
-
-         // iterate over flag field
-         cell_idx_t gl = cell_idx_c( indexField.nrOfGhostLayers() );
-         for( cell_idx_t z = -gl; z < cell_idx_c( indexField.zSize() ) + gl; ++z )
-            for( cell_idx_t y = -gl; y < cell_idx_c( indexField.ySize() ) + gl; ++y )
-               for( cell_idx_t x = -gl; x < cell_idx_c( indexField.xSize() ) + gl; ++x )
-               {
-                  int index = indexField(x,y,z);
-                  if ( flagUIDs.find( index ) != flagUIDs.end() )
-                  {
-                     if ( boundaryConfigs.find( index ) != boundaryConfigs.end()  )
-                        h.forceBoundary( flagUIDs[index],x,y,z, * boundaryConfigs[index] );
-                     else
-                        h.forceBoundary( flagUIDs[index],x,y,z );
-                  }
-               }
-      }
-
-      template<typename BH>
-      void BH_setDomainSlice( BH & h, const boost::python::tuple & index ) {
-         h.setDomain( localPythonSliceToCellInterval( *h.getFlagField() , index ) );
-      }
-
-      template<typename BH>
-      void BH_forceDomainSlice( BH & h, const boost::python::tuple & index ) {
-         h.forceDomain( localPythonSliceToCellInterval( *h.getFlagField() , index ) );
-      }
-
-      template<typename BH>
-      void BH_fillDomainSlice( BH & h, const boost::python::tuple & index ) {
-         h.fillWithDomain( localPythonSliceToCellInterval( *h.getFlagField() , index ) );
-      }
-      template<typename BH>
-      void BH_removeDomainSlice( BH & h, const boost::python::tuple & index ) {
-         h.removeDomain( localPythonSliceToCellInterval( *h.getFlagField() , index ) );
-      }
-      template<typename BH>
-      void BH_removeBoundarySlice( BH & h, const boost::python::tuple & index ) {
-         h.removeBoundary( localPythonSliceToCellInterval( *h.getFlagField() , index ) );
-      }
-      template<typename BH>
-      void BH_clearSlice( BH & h, const boost::python::tuple & index ) {
-         h.clear( localPythonSliceToCellInterval( *h.getFlagField() , index ) );
-      }
-
-
-
-      struct BoundaryHandlingExporter
-      {
-         template< typename BH>
-         void operator() ( python_coupling::NonCopyableWrap<BH> )
-         {
-            using namespace boost::python;
-            void ( BH::*p_exe1 )( uint_t )= &BH::operator();
-
-            bool ( BH::*p_isEmpty        ) ( cell_idx_t, cell_idx_t, cell_idx_t ) const = &BH::isEmpty       ;
-            bool ( BH::*p_isNearBoundary ) ( cell_idx_t, cell_idx_t, cell_idx_t ) const = &BH::isNearBoundary;
-            bool ( BH::*p_isBoundary     ) ( cell_idx_t, cell_idx_t, cell_idx_t ) const = &BH::isBoundary    ;
-            bool ( BH::*p_isDomain       ) ( cell_idx_t, cell_idx_t, cell_idx_t ) const = &BH::isDomain      ;
-
-            void ( BH::*p_setDomain      ) ( cell_idx_t, cell_idx_t, cell_idx_t )= &BH::setDomain  ;
-            void ( BH::*p_forceDomain    ) ( cell_idx_t, cell_idx_t, cell_idx_t )= &BH::forceDomain;
-            void ( BH::*p_fillWithDomain1) ( const uint_t )                      = &BH::fillWithDomain;
-            void ( BH::*p_fillWithDomain2) ( cell_idx_t, cell_idx_t, cell_idx_t )= &BH::fillWithDomain;
-
-            void ( BH::*p_removeDomain1 ) ( const uint_t )                      = &BH::removeDomain;
-            void ( BH::*p_removeDomain2 ) ( cell_idx_t, cell_idx_t, cell_idx_t )= &BH::removeDomain;
-
-            void ( BH::*p_removeBoundary1 ) ( const uint_t )                      = &BH::removeBoundary;
-            void ( BH::*p_removeBoundary2 ) ( cell_idx_t, cell_idx_t, cell_idx_t )= &BH::removeBoundary;
-
-            void ( BH::*p_clear1 ) ( const uint_t )                      = &BH::clear;
-            void ( BH::*p_clear2 ) ( cell_idx_t, cell_idx_t, cell_idx_t )= &BH::clear;
-
-            typename BH::FlagField * ( BH::*p_getFlagField) () = &BH::getFlagField;
-
-            typename BH::flag_t ( BH::*p_getNearBoundaryFlag ) () const  = &BH::getNearBoundaryFlag;
-            typename BH::flag_t ( BH::*p_getBoundaryMask )     () const  = &BH::getBoundaryMask;
-            typename BH::flag_t ( BH::*p_getDomainMask )       () const  = &BH::getDomainMask;
-
-            class_< BH, boost::noncopyable > ( "BoundaryHandling", no_init )
-               .def( "__call__",        p_exe1,                    ( arg("numberOfGhostLayersToInclude")=0 )  )
-               .def( "isEmpty"       ,  p_isEmpty,                 ( arg("x"), arg("y"), arg("z") ) )
-               .def( "isNearBoundary",  p_isNearBoundary,          ( arg("x"), arg("y"), arg("z") ) )
-               .def( "isBoundary"    ,  p_isBoundary,              ( arg("x"), arg("y"), arg("z") ) )
-               .def( "isDomain"      ,  p_isDomain,                ( arg("x"), arg("y"), arg("z") ) )
-               .def( "setDomain"     ,  p_setDomain,               ( arg("x"), arg("y"), arg("z") ) )
-               .def( "setDomain"     ,  BH_forceDomainSlice<BH>,   ( arg("slice") ) )
-               .def( "forceDomain"   ,  p_forceDomain,             ( arg("x"), arg("y"), arg("z") ) )
-               .def( "forceDomain"   ,  BH_forceDomainSlice<BH>,   ( arg("slice") ) )
-               .def( "fillWithDomain",  p_fillWithDomain1,         ( arg("numberOfGhostLayersToInclude")=0 ) )
-               .def( "fillWithDomain",  p_fillWithDomain2,         ( arg("x"), arg("y"), arg("z") ) )
-               .def( "fillWithDomain",  BH_fillDomainSlice<BH>,    ( arg("slice") ) )
-               .def( "setBoundary",     &BH_setBoundary1<BH>,      ( arg("name"), arg("x"), arg("y"), arg("z"), arg("boundaryParams")=dict() ) )
-               .def( "setBoundary",     &BH_setBoundary2<BH>,      ( arg("name"), arg("slice"), arg("boundaryParams")=dict() ) )
-               .def( "forceBoundary",   &BH_forceBoundary1<BH>,    ( arg("name"), arg("x"), arg("y"), arg("z"), arg("boundaryParams")=dict() ) )
-               .def( "forceBoundary",   &BH_forceBoundary2<BH>,    ( arg("name"), arg("slice"), arg("boundaryParams")=dict() ) )
-               .def( "forceBoundary",   &BH_forceBoundary3<BH>,    ( arg("indexField"), arg("boundaryInfo") ) )
-               .def( "removeDomain",    p_removeDomain1,           ( arg("numberOfGhostLayersToInclude")=0 ) )
-               .def( "removeDomain",    p_removeDomain2,           ( arg("x"), arg("y"), arg("z") ) )
-               .def( "removeDomain",    BH_removeDomainSlice<BH>,  ( arg("slice") ) )
-               .def( "removeBoundary",  p_removeBoundary1,         ( arg("numberOfGhostLayersToInclude")=0 ) )
-               .def( "removeBoundary",  p_removeBoundary2,         ( arg("x"), arg("y"), arg("z") ) )
-               .def( "removeBoundary",  BH_removeBoundarySlice<BH>,( arg("slice") ) )
-               .def( "clear",           p_clear1,                  ( arg("numberOfGhostLayersToInclude")=0 ) )
-               .def( "clear",           p_clear2,                  ( arg("x"), arg("y"), arg("z") ) )
-               .def( "clear",           BH_clearSlice<BH>,         ( arg("slice") ) )
-               .def( "getFlagField",    p_getFlagField,              return_internal_reference<>() )
-               .def( "getNearBoundaryFlag", p_getNearBoundaryFlag )
-               .def( "getBoundaryMask",     p_getBoundaryMask     )
-               .def( "getDomainMask",       p_getDomainMask       )
-            ;
-         }
-      };
-   } // namespace internal
-
-   template<typename BoundaryHandlings>
-   void exportModuleToPython()
-   {
-      python_coupling::for_each_noncopyable_type< BoundaryHandlings > ( internal::BoundaryHandlingExporter() );
-
-      auto pythonManager = python_coupling::Manager::instance();
-      pythonManager->addBlockDataConversion< BoundaryHandlings> ();
-
-   }
-
-
-} // namespace boundary
-} // namespace walberla
-
-
diff --git a/src/communication/ReducePackInfo.h b/src/communication/ReducePackInfo.h
index d353d6f37308041feb4b59dd9f3275a4f071ccc7..141dd98283f76c110cf93b12c45fa30e74b89725 100644
--- a/src/communication/ReducePackInfo.h
+++ b/src/communication/ReducePackInfo.h
@@ -42,7 +42,7 @@ class ReducePackInfo
 {
 public:
    ReducePackInfo( ) : size_(0u) {}
-   virtual ~ReducePackInfo() {}
+   virtual ~ReducePackInfo() = default;
 
    size_t getSize() const { return size_; }
 
diff --git a/src/communication/UniformMPIDatatypeInfo.h b/src/communication/UniformMPIDatatypeInfo.h
index 33e44a8732ce1c241aa4b88d4594134d9d27c0d9..8f8e0d6c46fb45f584023bc2160c6f1ab54aec10 100644
--- a/src/communication/UniformMPIDatatypeInfo.h
+++ b/src/communication/UniformMPIDatatypeInfo.h
@@ -46,6 +46,14 @@ namespace communication {
    {
    public:
 
+      //**Construction & Destruction************************************************************************************
+      /*! \name Construction & Destruction */
+      //@{
+               UniformMPIDatatypeInfo() = default;
+      virtual ~UniformMPIDatatypeInfo() = default;
+      //@}
+      //****************************************************************************************************************
+
       /*************************************************************************************************************//**
       * Return the MPI data type that should be used for sending to neighbor in  specified direction
       *****************************************************************************************************************/
diff --git a/src/communication/UniformPackInfo.h b/src/communication/UniformPackInfo.h
index d2737a19cc40439848107a853fb99bcada8c3172..5ec6db29d32dff36713ab903498048b450f748f2 100644
--- a/src/communication/UniformPackInfo.h
+++ b/src/communication/UniformPackInfo.h
@@ -57,8 +57,8 @@ public:
    //**Construction & Destruction***************************************************************************************
    /*! \name Construction & Destruction */
    //@{
-            UniformPackInfo() {}
-   virtual ~UniformPackInfo() {}
+            UniformPackInfo() = default;
+   virtual ~UniformPackInfo() = default;
    //@}
    //*******************************************************************************************************************
 
diff --git a/src/core/Abort.h b/src/core/Abort.h
index 8c52f6783d8539613492498be0bd66ae7858b1a6..ee77794f5c5ab634c451f8f14bde1bdcaa5893d5 100644
--- a/src/core/Abort.h
+++ b/src/core/Abort.h
@@ -40,7 +40,7 @@ class Abort : public singleton::Singleton<Abort>
 
 public:
 
-   typedef std::function<void ( const std::string & message, const std::string & callerPath, const int line, bool withDebugInfo  )> AbortFunction;
+   using AbortFunction = std::function<void (const std::string &, const std::string &, const int, bool)>;
 
    void resetAbortFunction( const AbortFunction & function = AbortFunction() ) { abortFunction_ = function; }
 
@@ -52,7 +52,7 @@ public:
 
 private:
 
-   Abort() {}
+   Abort() = default;
 
    AbortFunction abortFunction_;
 };
diff --git a/src/core/AllSet.h b/src/core/AllSet.h
index 74842fd8359bc0a64bebcbcb764ed3a473764f55..a2c79581b0eddab97af45c967c832cc793bf9502 100644
--- a/src/core/AllSet.h
+++ b/src/core/AllSet.h
@@ -94,7 +94,7 @@ public:
    friend inline bool operator==( const AllSet& a, const AllSet& b ) { return  setIsEqual(a,b); } ///< compares the content of two sets
    friend inline bool operator!=( const AllSet& a, const AllSet& b ) { return !setIsEqual(a,b); } ///< compares the content of two sets
 
-   typedef typename std::set<T>::const_iterator ConstIter;
+   using ConstIter = typename std::set<T>::const_iterator;
 
 
 
diff --git a/src/core/Any.h b/src/core/Any.h
index ac2f9dbb3f88be5fd0155ccd138dea9f33f74a94..a7bdfaea4837fcbcbe55e7642412f54f0ab9e3f9 100644
--- a/src/core/Any.h
+++ b/src/core/Any.h
@@ -21,29 +21,25 @@
 
 #pragma once
 
+#include "waLBerlaDefinitions.h"
 
-#if defined(WALBERLA_USE_STD_ANY)
+#ifndef WALBERLA_USE_STD_EXPERIMENTAL_ANY
 #include <any>
-#elif defined(WALBERLA_USE_STD_EXPERIMENTAL_ANY)
+#else
 #undef _LIBCPP_WARN_ON_DEPRECATED_EXPERIMENTAL_HEADER
 #include <experimental/any>
-#else
-#include <boost/any.hpp>
 #endif
 
 
 
 namespace walberla {
 
-#if defined(WALBERLA_USE_STD_ANY)
+#ifndef WALBERLA_USE_STD_EXPERIMENTAL_ANY
 using std::any;
 using std::any_cast;
-#elif defined(WALBERLA_USE_STD_EXPERIMENTAL_ANY)
+#else
 using std::experimental::any;
 using std::experimental::any_cast;
-#else
-using boost::any;
-using boost::any_cast;
 #endif
 
 }
diff --git a/src/core/Array.h b/src/core/Array.h
index 1c5f8c2b22b6261b226367fe5927284f365fe63d..42ab4efe51118b5edecb211b9066299466725ebc 100644
--- a/src/core/Array.h
+++ b/src/core/Array.h
@@ -41,12 +41,12 @@ class Array {
 
 public:
 
-   inline Array() : array_( NULL ), size_( uint_c(0) ) {}
+   inline Array() : array_( nullptr ), size_( uint_c(0) ) {}
    inline Array( const uint_t n, const T& t = T() );
    inline Array( const std::vector<T>& vector );
    inline Array( const Array& array );
 
-   ~Array() { if( array_ != NULL ) delete[] array_; }
+   ~Array() { if( array_ != nullptr ) delete[] array_; }
 
    uint_t size() const { return size_; }
    bool  empty() const { return size_ == 0; }
diff --git a/src/core/ConcatIterator.h b/src/core/ConcatIterator.h
index b21ce3705b9c10b339cf8295918aee50a4948a73..9b0ad071d38a4d7d3c1e902f19cd8cf5be10e6ef 100644
--- a/src/core/ConcatIterator.h
+++ b/src/core/ConcatIterator.h
@@ -27,9 +27,15 @@
 namespace walberla {
 
 template< typename T >
-class ConcatIterator : public std::iterator< std::input_iterator_tag, typename T::value_type, typename T::difference_type, typename T::pointer, typename T::reference >
+class ConcatIterator
 {
 public:
+    using iterator_category = std::input_iterator_tag;
+    using value_type = typename T::value_type;
+    using difference_type = typename T::difference_type;
+    using pointer = typename T::pointer;
+    using reference = typename T::reference;
+
     /// default constructed iterator points to end()
     ConcatIterator( )
         : ended_( true )
@@ -56,14 +62,7 @@ public:
     {
         if (ended_ || rhs.ended_)
         {
-           if (ended_ == rhs.ended_)
-           {
-              return true;
-           }
-           else
-           {
-              return false;
-           }
+           return ended_ == rhs.ended_;
         }
 
         return it_ == rhs.it_;
diff --git a/src/core/DataTypes.h b/src/core/DataTypes.h
index 28fdf80c767dd5318e630c9f153337e33b226351..8cb35637b67fe63c28846454955750b769574518 100644
--- a/src/core/DataTypes.h
+++ b/src/core/DataTypes.h
@@ -86,10 +86,10 @@ inline S numeric_cast( T t ) {
 
 
 // fixed size signed integral types
-typedef std::int8_t   int8_t;    ///<  8 bit signed integer
-typedef std::int16_t  int16_t;   ///< 16 bit signed integer
-typedef std::int32_t  int32_t;   ///< 32 bit signed integer
-typedef std::int64_t  int64_t;   ///< 64 bit signed integer
+using int8_t = std::int8_t;    ///<  8 bit signed integer
+using int16_t = std::int16_t;   ///< 16 bit signed integer
+using int32_t = std::int32_t;   ///< 32 bit signed integer
+using int64_t = std::int64_t;   ///< 64 bit signed integer
 
 template< typename T > inline int8_t   int8_c( T t ) { return numeric_cast< int8_t  >(t); } ///< cast to type int8_t  using "int8_c(x)"
 template< typename T > inline int16_t int16_c( T t ) { return numeric_cast< int16_t >(t); } ///< cast to type int16_t using "int16_c(x)"
@@ -100,12 +100,12 @@ template< typename T > inline int64_t int64_c( T t ) { return numeric_cast< int6
 
 // fixed size unsigned integral types
 
-typedef std::uint8_t  uint8_t;    ///<  8 bit unsigned integer
-typedef std::uint16_t uint16_t;   ///< 16 bit unsigned integer
-typedef std::uint32_t uint32_t;   ///< 32 bit unsigned integer
-typedef std::uint64_t uint64_t;   ///< 64 bit unsigned integer
-typedef uint8_t byte_t;
-typedef uint64_t id_t;            //sid datatype for pe
+using uint8_t = std::uint8_t;    ///<  8 bit unsigned integer
+using uint16_t = std::uint16_t;   ///< 16 bit unsigned integer
+using uint32_t = std::uint32_t;   ///< 32 bit unsigned integer
+using uint64_t = std::uint64_t;   ///< 64 bit unsigned integer
+using byte_t = uint8_t;
+using id_t = uint64_t;            //sid datatype for pe
 
 template< typename T > inline uint8_t   uint8_c( T t ) { return numeric_cast< uint8_t  >(t); } ///< cast to type uint8_t  using "uint8_c(x)"
 template< typename T > inline uint16_t uint16_c( T t ) { return numeric_cast< uint16_t >(t); } ///< cast to type uint16_t using "uint16_c(x)"
@@ -127,7 +127,7 @@ inline void static_assert_int_t() {
 
 // unsigned integral type
 
-typedef size_t uint_t;
+using uint_t = size_t;
 
 static_assert( std::numeric_limits<uint_t>::is_specialized &&
                std::numeric_limits<uint_t>::is_integer &&
@@ -144,7 +144,7 @@ inline void static_assert_uint_t() {
 
 // data structure specific data types
 
-typedef int cell_idx_t;
+using cell_idx_t = int;
 //typedef int64_t cell_idx_t;
 
 WALBERLA_STATIC_ASSERT( std::numeric_limits<cell_idx_t>::is_specialized &&
@@ -158,13 +158,13 @@ template< typename T > inline cell_idx_t cell_idx_c( T t ) { return numeric_cast
 // floating point type
 
 #ifdef WALBERLA_DOUBLE_ACCURACY
-typedef double real_t;
+using real_t = double;
 #else
 typedef float  real_t;
 #endif
 
-inline real_t operator"" _r( long double t ) { return static_cast< real_t >(t); }
-inline real_t operator"" _r( unsigned long long int t ) { return static_cast< real_t >(t); }
+inline constexpr real_t operator"" _r( long double t ) { return static_cast< real_t >(t); }
+inline constexpr real_t operator"" _r( unsigned long long int t ) { return static_cast< real_t >(t); }
 template< typename T > inline real_t real_c  ( T t ) { return numeric_cast< real_t >(t); } ///< cast to type real_t using "real_c(x)"
 template< typename T > inline double double_c( T t ) { return numeric_cast< double >(t); } ///< cast to type double
 template< typename T > inline float  float_c ( T t ) { return numeric_cast< float > (t); } ///< cast to type float
diff --git a/src/core/Environment.cpp b/src/core/Environment.cpp
index 015687ce3bba28e3ffc393d6df1e3b788621f49d..684f6f8e22dcdb50106591111fc1b0ce2591345d 100644
--- a/src/core/Environment.cpp
+++ b/src/core/Environment.cpp
@@ -82,7 +82,7 @@ void configureGlobalState( const shared_ptr<Config> & config ) {
       std::string suids = config->getParameter< std::string >( "GlobalState" );
 
       std::vector< std::string > states = string_split( suids, ", \t" );
-      states.erase( std::remove_if( states.begin(), states.end(), std::bind( &std::string::empty, std::placeholders::_1 ) ), states.end() );
+      states.erase( std::remove_if( states.begin(), states.end(), [](auto &s){ return s.empty(); } ), states.end() );
 
       Set<SUID> state;
       for( auto it = states.begin(); it != states.end(); ++it )
diff --git a/src/core/Filesystem.h b/src/core/Filesystem.h
index 24e14a8683a99b3798d732e5df4a5ae19f729b4e..beaf77ad00e28335681dff0a85df599709a0f59d 100644
--- a/src/core/Filesystem.h
+++ b/src/core/Filesystem.h
@@ -21,14 +21,13 @@
 
 #pragma once
 
+#include "waLBerlaDefinitions.h"
 
-#if defined(WALBERLA_USE_STD_FILESYSTEM)
+#ifndef WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM
 #include <filesystem>
-#elif defined(WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM)
+#else
 #define _LIBCPP_NO_EXPERIMENTAL_DEPRECATION_WARNING_FILESYSTEM
 #include <experimental/filesystem>
-#else
-#include <boost/filesystem.hpp>
 #endif
 
 
@@ -36,12 +35,10 @@
 namespace walberla {
 namespace filesystem {
 
-#if defined(WALBERLA_USE_STD_FILESYSTEM)
+#ifndef WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM
 using namespace std::filesystem;
-#elif defined(WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM)
-using namespace std::experimental::filesystem;
 #else
-using namespace boost::filesystem;
+using namespace std::experimental::filesystem;
 #endif
 
 }
diff --git a/src/core/NonCopyable.h b/src/core/NonCopyable.h
index c01b4faf3dc4edd763300a81cae7efc11b2f4a5c..59b7773d23cd77c8daed1e76928d85567af0595d 100644
--- a/src/core/NonCopyable.h
+++ b/src/core/NonCopyable.h
@@ -36,8 +36,8 @@ class NonCopyable {
 
 protected:
 
-    NonCopyable(){} // no object of type 'NonCopyable' can be created!
-   ~NonCopyable(){}
+    NonCopyable()= default; // no object of type 'NonCopyable' can be created!
+   ~NonCopyable()= default;
 
 private:
 
diff --git a/src/core/NonCreateable.h b/src/core/NonCreateable.h
index 42dc0057b102454f492bb2ec8918320a2227495b..fb291a3a93afcf49ae1a9b9225cf42c8ba3ccea1 100644
--- a/src/core/NonCreateable.h
+++ b/src/core/NonCreateable.h
@@ -35,34 +35,8 @@ namespace walberla{
 
 class NonCreateable {
 
-#ifndef _MSC_VER
-
-   // non-MSVC-Build
-
-private:
-
-    NonCreateable();
-   ~NonCreateable();
-
-   NonCreateable(const NonCreateable&);
-   NonCreateable& operator=(const NonCreateable&);
-
-#else
-
-   // MSVC-Build (eliminating warning C4624)
-
-private:
-
-   NonCreateable();
-   NonCreateable(const NonCreateable&);
-
-   NonCreateable& operator=(const NonCreateable&);
-
-protected:
-
-   ~NonCreateable();
-
-#endif
+public:
+   NonCreateable() = delete;
 
 };
 
diff --git a/src/core/OpenMP.h b/src/core/OpenMP.h
index 67d32589a2c3676361e9605ee06cdee385dcdebd..b8ae3d6077ea8e5467fa84deecc959f5be80bb4c 100644
--- a/src/core/OpenMP.h
+++ b/src/core/OpenMP.h
@@ -48,12 +48,12 @@ namespace walberla {
 #define WALBERLA_OPENMP_FUNCTION_ERROR WALBERLA_ABORT( "Invalid OpenMP function call! In case of compiling without OpenMP, OpenMP functions are not available and shouldn't be called!" );
 
 /* schedule kind constants */
-typedef enum omp_sched_t {
+using omp_sched_t = enum omp_sched_t {
 omp_sched_static  = 1,
 omp_sched_dynamic = 2,
 omp_sched_guided  = 3,
 omp_sched_auto    = 4
-} omp_sched_t;
+};
 
 /* set API functions */
 inline void    omp_set_num_threads (int) { WALBERLA_OPENMP_FUNCTION_ERROR }
@@ -81,9 +81,9 @@ inline void    omp_get_schedule            (omp_sched_t *, int *) { WALBERLA_OPE
 inline int     omp_get_max_task_priority   (void) { WALBERLA_OPENMP_FUNCTION_ERROR }
 
 /* lock API functions */
-typedef struct omp_lock_t {
+using omp_lock_t = struct omp_lock_t {
     void * _lk;
-} omp_lock_t;
+};
 
 inline void    omp_init_lock    (omp_lock_t *) { WALBERLA_OPENMP_FUNCTION_ERROR }
 inline void    omp_set_lock     (omp_lock_t *) { WALBERLA_OPENMP_FUNCTION_ERROR }
@@ -92,9 +92,9 @@ inline void    omp_destroy_lock (omp_lock_t *) { WALBERLA_OPENMP_FUNCTION_ERROR
 inline int     omp_test_lock    (omp_lock_t *) { WALBERLA_OPENMP_FUNCTION_ERROR }
 
 /* nested lock API functions */
-typedef struct omp_nest_lock_t {
+using omp_nest_lock_t = struct omp_nest_lock_t {
     void * _lk;
-} omp_nest_lock_t;
+};
 
 inline void    omp_init_nest_lock    (omp_nest_lock_t *) { WALBERLA_OPENMP_FUNCTION_ERROR }
 inline void    omp_set_nest_lock     (omp_nest_lock_t *) { WALBERLA_OPENMP_FUNCTION_ERROR }
@@ -103,7 +103,7 @@ inline void    omp_destroy_nest_lock (omp_nest_lock_t *) { WALBERLA_OPENMP_FUNCT
 inline int     omp_test_nest_lock    (omp_nest_lock_t *) { WALBERLA_OPENMP_FUNCTION_ERROR }
 
 /* lock hint type for dynamic user lock */
-typedef enum omp_lock_hint_t {
+using omp_lock_hint_t = enum omp_lock_hint_t {
     omp_lock_hint_none           = 0,
     omp_lock_hint_uncontended    = 1,
     omp_lock_hint_contended      = (1<<1 ),
@@ -112,7 +112,7 @@ typedef enum omp_lock_hint_t {
     kmp_lock_hint_hle            = (1<<16),
     kmp_lock_hint_rtm            = (1<<17),
     kmp_lock_hint_adaptive       = (1<<18)
-} omp_lock_hint_t;
+};
 
 /* hinted lock initializers */
 inline void omp_init_lock_with_hint(omp_lock_t *, omp_lock_hint_t) { WALBERLA_OPENMP_FUNCTION_ERROR }
@@ -144,13 +144,13 @@ inline int    omp_target_associate_ptr(void *, void *, size_t, size_t, int) { WA
 inline int    omp_target_disassociate_ptr(void *, int) { WALBERLA_OPENMP_FUNCTION_ERROR }
 
 /* OpenMP 4.0 affinity API */
-typedef enum omp_proc_bind_t {
+using omp_proc_bind_t = enum omp_proc_bind_t {
     omp_proc_bind_false = 0,
     omp_proc_bind_true = 1,
     omp_proc_bind_master = 2,
     omp_proc_bind_close = 3,
     omp_proc_bind_spread = 4
-} omp_proc_bind_t;
+};
 
 inline omp_proc_bind_t omp_get_proc_bind (void) { WALBERLA_OPENMP_FUNCTION_ERROR }
 
diff --git a/src/core/Optional.h b/src/core/Optional.h
index 60883fd3d63a7366f40119aff89eddc351045db6..5c31a1ec007286e02a8f413838fd3ad7dbd35241 100644
--- a/src/core/Optional.h
+++ b/src/core/Optional.h
@@ -21,29 +21,15 @@
 
 #pragma once
 
+#include "waLBerlaDefinitions.h"
 
-#if defined(WALBERLA_USE_STD_OPTIONAL)
 #include <optional>
-#elif defined(WALBERLA_USE_STD_EXPERIMENTAL_OPTIONAL)
-#undef _LIBCPP_WARN_ON_DEPRECATED_EXPERIMENTAL_HEADER
-#include <experimental/optional>
-#else
-#include <boost/optional.hpp>
-#endif
 
 
 
 namespace walberla {
 
-#if defined(WALBERLA_USE_STD_OPTIONAL)
 using std::optional;
 using std::nullopt;
-#elif defined(WALBERLA_USE_STD_EXPERIMENTAL_OPTIONAL)
-using std::experimental::optional;
-using std::experimental::nullopt;
-#else
-using boost::optional;
-const boost::none_t nullopt = boost::none;
-#endif
 
 }
diff --git a/src/core/Sanitizer.h b/src/core/Sanitizer.h
index 8ccb5f7b868a053643d0518bac63c020a330a49c..86ff575167a338a45514216eaba3fb0fba25463b 100644
--- a/src/core/Sanitizer.h
+++ b/src/core/Sanitizer.h
@@ -21,16 +21,15 @@
 
 #pragma once
 
-#if (( defined WALBERLA_CXX_COMPILER_IS_CLANG ) && ( __clang_major__ >=4 )  )  \
- || (( defined WALBERLA_CXX_COMPILER_IS_GNU )   && ( __GNUC__ >= 5 ) )
+#if defined(WALBERLA_CXX_COMPILER_IS_CLANG) || defined(WALBERLA_CXX_COMPILER_IS_GNU)
 # define ATTRIBUTE_NO_SANITIZE_ADDRESS __attribute__((no_sanitize_address))
 #else
 # define ATTRIBUTE_NO_SANITIZE_ADDRESS
 #endif
 
-#if ( ( defined WALBERLA_CXX_COMPILER_IS_GNU ) && ( __GNUC__ >= 5 ) )
+#if defined(WALBERLA_CXX_COMPILER_IS_GNU)
 # define ATTRIBUTE_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize_undefined))
-#elif (( defined WALBERLA_CXX_COMPILER_IS_CLANG ) && ( __clang_major__ >= 4 ) )
+#elif defined(WALBERLA_CXX_COMPILER_IS_CLANG)
 # define ATTRIBUTE_NO_SANITIZE_UNDEFINED __attribute__((no_sanitize("undefined")))
 #else
 # define ATTRIBUTE_NO_SANITIZE_UNDEFINED
diff --git a/src/core/Set.h b/src/core/Set.h
index 36bed9a616d7110d5706d1ec4a320cff62fefbef..cbc0d5c1196404f4fb71d72492c40a7a326b03c2 100644
--- a/src/core/Set.h
+++ b/src/core/Set.h
@@ -85,9 +85,9 @@ template< typename T >
 class Set {
 
 public:
-   typedef typename std::set<T>::value_type     value_type;
-   typedef typename std::set<T>::const_iterator const_iterator;
-   typedef typename std::set<T>::iterator       iterator;
+   using value_type = typename std::set<T>::value_type;
+   using const_iterator = typename std::set<T>::const_iterator;
+   using iterator = typename std::set<T>::iterator;
 
    friend inline Set<T> operator&( const Set& a, const Set& b ) { return setIntersection(a,b); } ///< intersection
    friend inline Set<T> operator+( const Set& a, const Set& b ) { return setUnion       (a,b); } ///< union
@@ -96,12 +96,12 @@ public:
    friend inline bool operator==( const Set& a, const Set& b ) { return  setIsEqual(a,b); } ///< compares the content of two sets
    friend inline bool operator!=( const Set& a, const Set& b ) { return !setIsEqual(a,b); } ///< compares the content of two sets
 
-   inline Set() {}
+   inline Set() = default;
    inline Set( const T& element ) { set_.insert( element ); }
 
-   inline virtual ~Set() {}
+   inline virtual ~Set() = default;
 
-   static const Set<T>& emptySet() { static Set set; return set; }
+   static const Set<T> emptySet() { return {}; }
 
    inline std::pair<iterator,bool> insert( const T& element )                    { return set_.insert( element ); }
    inline iterator                 insert( iterator position, const T& element ) { return set_.insert( position, element ); }
diff --git a/src/core/Sleep.cpp b/src/core/Sleep.cpp
index ae26ea9cfa1a738fe4279a491a76cf818d2ee913..8706a72ecfa210734663477cc48bac425a7e9089 100644
--- a/src/core/Sleep.cpp
+++ b/src/core/Sleep.cpp
@@ -22,36 +22,13 @@
 #include "Sleep.h"
 #include "waLBerlaDefinitions.h"
 
-#ifdef WALBERLA_CXX_COMPILER_IS_MSVC
-
-#include <windows.h>
+#include <thread>
 
 namespace walberla {
 
-   void sleep( uint_t seconds )
-   {
-      ::Sleep( static_cast<DWORD>( uint_t(1000) * seconds ) );
-   }
-
+void sleep( uint_t seconds )
+{
+   std::this_thread::sleep_for(std::chrono::seconds(static_cast<int>(seconds)));
 }
 
-#else
-
-#ifdef WALBERLA_CXX_COMPILER_IS_IBM
-#ifndef _POSIX_SOURCE
-#define _POSIX_SOURCE
-#endif
-#endif
-
-#include <unistd.h>
-
-namespace walberla {
-
-   void sleep( uint_t seconds )
-   {
-      ::sleep( static_cast<unsigned int>(seconds) );
-   }
-
 }
-
-#endif
\ No newline at end of file
diff --git a/src/core/StringUtility.impl.h b/src/core/StringUtility.impl.h
index ddc5f748275dc04e3500793e83758f658d58aa92..730bf592867b6d2b7abbf134300431b3de25bfbf 100644
--- a/src/core/StringUtility.impl.h
+++ b/src/core/StringUtility.impl.h
@@ -104,7 +104,7 @@ inline std::vector<std::string> string_split(std::string s, const std::string &d
          if (*it == d) {   // current character in s is a delimiter
             sub_end = it;
             if (sub_begin < sub_end) { // make sure that the substring is not empty
-               substrings.push_back(std::string(sub_begin, sub_end));
+               substrings.emplace_back(sub_begin, sub_end);
             }
             sub_begin = ++sub_end;
             continue;
@@ -114,7 +114,7 @@ inline std::vector<std::string> string_split(std::string s, const std::string &d
 
    // add substring from last delimiter to the end of s
    if (sub_begin < s.end()) {
-      substrings.push_back(std::string(sub_begin, s.end()));
+      substrings.emplace_back(sub_begin, s.end());
    }
 
    return substrings;
diff --git a/src/core/Template.h b/src/core/Template.h
deleted file mode 100644
index 861c7bebde4811d9d4053445fad2932af7490b25..0000000000000000000000000000000000000000
--- a/src/core/Template.h
+++ /dev/null
@@ -1,65 +0,0 @@
-//======================================================================================================================
-//
-//  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 Template.h
-//! \ingroup core
-//! \author Klaus Iglberger
-//! \author Sebastian Eibl <sebastian.eibl@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-
-//*************************************************************************************************
-/*! \cond internal */
-/*!\brief Compiler specific patch for nested template disambiguation.
- * \ingroup util
- *
- * The WALBERLA_TEMPLATE is a patch for the Microsoft Visual C++ compiler that does not correctly
- * parse definitions of nested templates of the following form:
-
-   \code
-   template< typename T >
-   class Alloc {
-    public:
-      ...
-      template< typename Other >
-      class rebind {
-       public:
-         typedef Alloc<Other> other;
-      };
-      ...
-   };
-
-   typedef Alloc<int>  AI;
-   typedef AI::template rebind<double>::other  Other;  // Compilation error with Visual C++
-   \endcode
-
- * In order to circumvent this compilation error, the WALBERLA_TEMPLATE macro should be used instead
- * the \a template keyword:
-
-   \code
-   ...
-   typedef AI::WALBERLA_TEMPLATE rebind<double>::other  Other;  // No compilation errors
-   \endcode
- */
-#if defined(_MSC_VER)
-#  define WALBERLA_TEMPLATE
-#else
-#  define WALBERLA_TEMPLATE template
-#endif
-/*! \endcond */
-//*************************************************************************************************
diff --git a/src/core/Variant.h b/src/core/Variant.h
index 7fe6d195afcacf49b0c1dea05d14edc61d0b408d..2be3f0a6c09e562dd48ef54b460dc08d786683d6 100644
--- a/src/core/Variant.h
+++ b/src/core/Variant.h
@@ -22,37 +22,17 @@
 #pragma once
 
 
-#if defined(WALBERLA_USE_STD_VARIANT)
 #include <variant>
-#else
-#include <boost/variant.hpp>
-#endif
 
 
 
 namespace walberla
 {
 
-#if defined(WALBERLA_USE_STD_VARIANT)
 using std::variant;
 using std::visit;
 using std::get;
 using std::holds_alternative;
 using std::bad_variant_access;
-#else
-using boost::variant;
-using boost::get;
-template <class T, class... Types>
-constexpr bool holds_alternative( const boost::variant<Types...>& v ) noexcept
-{
-   return v.type() == typeid( T );
-}
-using bad_variant_access = boost::bad_get;
-template<typename Visitor, typename... Variant>
-decltype( auto ) visit( Visitor&& visitor, Variant&& ... variant )
-{
-   return boost::apply_visitor( visitor, variant... );
-}
-#endif
 
 }
diff --git a/src/core/VectorTrait.h b/src/core/VectorTrait.h
index b050d159ee2a9a39670e715f5ebdccd7a95d42aa..82d60ef0bdd1ddf51193c9b7840d097d8fcfd99f 100644
--- a/src/core/VectorTrait.h
+++ b/src/core/VectorTrait.h
@@ -38,7 +38,7 @@ namespace walberla {
 template< typename T, class Enable = void >
 struct VectorTrait
 {
-   typedef void OutputType;
+   using OutputType = void;
 
    static const uint_t F_SIZE = 0u;
 };
@@ -46,7 +46,7 @@ struct VectorTrait
 template< typename T >
 struct VectorTrait<T, typename std::enable_if<std::is_arithmetic<T>::value>::type>
 {
-   typedef T OutputType;
+   using OutputType = T;
 
    static const uint_t F_SIZE = 1u;
    static T get   ( T   value, uint_t /*f*/ )        { return value; }
diff --git a/src/core/WeakPtrWrapper.h b/src/core/WeakPtrWrapper.h
deleted file mode 100644
index 2060422900f4970b0cd434c4f2d43856489275fa..0000000000000000000000000000000000000000
--- a/src/core/WeakPtrWrapper.h
+++ /dev/null
@@ -1,100 +0,0 @@
-//======================================================================================================================
-//
-//  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 WeakPtrWrapper.h
-//! \ingroup core
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include "waLBerlaDefinitions.h"
-#include "core/DataTypes.h"
-
-
-/*
-   What is this good for?
-
-      Memory Management of basic waLBerla objects: Use BlockStorage and CommunicationSchemes as example:
-          - a block storage is held as a shared_ptr
-          - create a communication scheme: internally the communication scheme
-            only holds a weak pointer to the block storage i.e. communication does not "own" the block storage.
-            If the communication would own the block storage and would internally hold a shared_ptr instead of a
-            weak_ptr, cycles could be created e.g. when a communication object is passed to the block storage,
-            which happens e.g. when registering communication functors at the block storage itself
-            -> weak_ptr is necessary to prevent cycles ( another way would be not to store a pointer to
-            block storage at all which is hardly practicable  )
-         - trying to communicate after a block storage was deleted leads to an error, which is reasonable
-
-      The problem occurs when exporting this to Python:
-         - Due to a known and probably never fixed bug, one cannot created weak pointers from shared pointers
-           received from Python functions.  Details here:
-              - http://stackoverflow.com/questions/8233252/boostpython-and-weak-ptr-stuff-disappearing
-              - https://svn.boost.org/trac/boost/ticket/3673
-         - What works are plain old C pointers -> when compiling with Python plain C pointers should be used instead
-           of the checked weak pointers -> this is the reason for this wrapper class
-         - Special attention is necessary when exporting this construct to Python since a block forest may only be
-           deleted when no communication object points to it. This is ensured in this case via
-           UniformBufferedSchemeWrapper
- */
-
-
-
-
-
-
-
-namespace walberla {
-
-
-#ifndef WALBERLA_BUILD_WITH_PYTHON
-
-
-
-template<typename T>
-class weak_ptr_wrapper : public weak_ptr<T>
-{
-public:
-   weak_ptr_wrapper() {}
-   weak_ptr_wrapper( const weak_ptr  <T> & r ) : weak_ptr<T>(r) {}
-   weak_ptr_wrapper( const shared_ptr<T> & r ) : weak_ptr<T>(r) {}
-};
-
-
-# else
-
-   // Due to a bug in boost::python weak_ptr cannnot be used:
-   // http://stackoverflow.com/questions/8233252/boostpython-and-weak-ptr-stuff-disappearing
-   template<typename T>
-   class weak_ptr_wrapper
-   {
-   public:
-      weak_ptr_wrapper( const shared_ptr<T> & sp )
-         : rawPtr_ ( sp.get() )
-      {}
-
-      T * lock() { return rawPtr_; }
-
-   protected:
-      T * rawPtr_;
-   };
-
-
-#endif // WALBERLA_BUILD_WITH_PYTHON
-
-} // namespace walberla
-
-
diff --git a/src/core/cell/Cell.h b/src/core/cell/Cell.h
index 8fc8ef14e29f216f173604b667807801b49e4f83..3df78ce5b147c26650cf8caf799a4bb342e34f4c 100644
--- a/src/core/cell/Cell.h
+++ b/src/core/cell/Cell.h
@@ -47,7 +47,7 @@ public:
 
    /*! \name Constructors */
    //@{
-   Cell() {}
+   Cell() = default;
    inline Cell( const cell_idx_t _x, const cell_idx_t _y, const cell_idx_t _z ) { cell[0] = _x; cell[1] = _y; cell[2] = _z; }
  //inline Cell( const int        _x, const int        _y, const int        _z );
    inline Cell( const uint_t     _x, const uint_t     _y, const uint_t     _z );
diff --git a/src/core/cell/CellArray.h b/src/core/cell/CellArray.h
index 3f26ee61c80a9a1ee160fdffb91ee3cfe13ddbf4..732b2b85337f0a8051170aa678cff05b285c9535 100644
--- a/src/core/cell/CellArray.h
+++ b/src/core/cell/CellArray.h
@@ -41,7 +41,7 @@ public:
 
    CellArray( const CellVector& cells )
    {
-      this->array_ = cells.size() == 0 ? NULL : new Cell[ cells.size() ];
+      this->array_ = cells.empty() ? nullptr : new Cell[ cells.size() ];
       this->size_  = cells.size();
 
       for( uint_t i = 0; i != this->size_; ++i )
@@ -50,7 +50,7 @@ public:
 
    CellArray( const CellSet& cells )
    {
-      this->array_ = cells.size() == 0 ? NULL : new Cell[ cells.size() ];
+      this->array_ = cells.empty() ? nullptr : new Cell[ cells.size() ];
       this->size_  = cells.size();
 
       uint_t i = 0;
diff --git a/src/core/cell/CellInterval.cpp b/src/core/cell/CellInterval.cpp
index 83ab0e33df531b98fc0505d05213e31ed3066457..c4df1ea31a656338691e4752ca8829150461eda7 100644
--- a/src/core/cell/CellInterval.cpp
+++ b/src/core/cell/CellInterval.cpp
@@ -49,13 +49,10 @@ bool CellInterval::overlaps( const CellVector& cellVector ) const
    if( empty() )
       return false;
 
-   for( const Cell & cell : cellVector )
-   {
-      if( this->contains( cell ) )
-         return true;
-   }
-
-   return false;
+   return std::any_of(cellVector.begin(),
+                      cellVector.end(),
+                      [&](const Cell & cell)
+                      {return contains( cell );});
 }
 
 
diff --git a/src/core/cell/CellInterval.h b/src/core/cell/CellInterval.h
index 10dfb0d9b1bbc375aeac80e08118b61af8c8cfd4..4befe87c1a85298e475636776bdd7eafd731e2b0 100644
--- a/src/core/cell/CellInterval.h
+++ b/src/core/cell/CellInterval.h
@@ -50,7 +50,7 @@ class CellIntervalIterator;
 class CellInterval {
 
 public:
-   typedef CellIntervalIterator const_iterator;
+   using const_iterator = CellIntervalIterator;
 
    CellInterval() : min_( cell_idx_c(0), cell_idx_c(0), cell_idx_c(0) ), max_( cell_idx_c(-1), cell_idx_c(-1), cell_idx_c(-1) ) {}
    CellInterval( const Cell& _min, const Cell& _max ) : min_( _min ), max_( _max ) {}
@@ -132,11 +132,11 @@ inline bool CellInterval::positiveIndicesOnly() const {
 class CellIntervalIterator
 {
 public:
-   typedef std::bidirectional_iterator_tag iterator_category;
-   typedef Cell                            value_type;
-   typedef ptrdiff_t                       difference_type;
-   typedef Cell*                           pointer;
-   typedef Cell&                           reference;
+   using iterator_category = std::bidirectional_iterator_tag;
+   using value_type = Cell;
+   using difference_type = ptrdiff_t;
+   using pointer = Cell *;
+   using reference = Cell &;
 
    CellIntervalIterator( const CellInterval & ci, const Cell & cell ) : ci_(ci), cell_( cell ) { }
 
@@ -236,11 +236,8 @@ inline bool CellInterval::overlaps( const CellInterval& other ) const
    if( empty() || other.empty() )
       return false;
 
-   if( other.min_.x() > max_.x() || other.min_.y() > max_.y() || other.min_.z() > max_.z() ||
-       other.max_.x() < min_.x() || other.max_.y() < min_.y() || other.max_.z() < min_.z() )
-      return false;
-   else
-      return true;
+   return !(other.min_.x() > max_.x() || other.min_.y() > max_.y() || other.min_.z() > max_.z() ||
+       other.max_.x() < min_.x() || other.max_.y() < min_.y() || other.max_.z() < min_.z());
 }
 
 
diff --git a/src/core/cell/CellVector.h b/src/core/cell/CellVector.h
index 0d9fe9f810175cd12b76d4179db347b0295e6e73..c05474400a39ba7b59ffd0268649eb12e3abc2eb 100644
--- a/src/core/cell/CellVector.h
+++ b/src/core/cell/CellVector.h
@@ -45,18 +45,18 @@ public:
 
    /*! \name Standard container typedefs */
    //@{
-   typedef std::vector<Cell>::iterator        iterator;
-   typedef std::vector<Cell>::const_iterator  const_iterator;
-   typedef std::vector<Cell>::size_type       size_type;
-   typedef std::vector<Cell>::reference       reference;
-   typedef std::vector<Cell>::const_reference const_reference;
-   typedef std::vector<Cell>::difference_type difference_type;
-   typedef Cell                               value_type;
+   using iterator = std::vector<Cell>::iterator;
+   using const_iterator = std::vector<Cell>::const_iterator;
+   using size_type = std::vector<Cell>::size_type;
+   using reference = std::vector<Cell>::reference;
+   using const_reference = std::vector<Cell>::const_reference;
+   using difference_type = std::vector<Cell>::difference_type;
+   using value_type = Cell;
    //@}
 
    /*! \name Constructors */
    //@{
-   CellVector() {}
+   CellVector() = default;
    CellVector(size_type n, const Cell & value = Cell()) : cells_(n, value) {}
    template <class InputIterator>
       CellVector(InputIterator first, InputIterator last) : cells_(first, last) { }
@@ -140,14 +140,14 @@ std::ostream & operator<<( std::ostream & os, const CellVector & cells );
 
 inline void CellVector::push_back( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z ) {
 
-   cells_.push_back( Cell(x, y, z) );
+   cells_.emplace_back(x, y, z );
 }
 
 
 
 inline void CellVector::push_back( const uint_t x, const uint_t y, const uint_t z ) {
 
-   cells_.push_back( Cell(x, y, z) );
+   cells_.emplace_back(x, y, z );
 }
 
 
diff --git a/src/core/config/Config.h b/src/core/config/Config.h
index 9837b870c7f0151aad400f509958c69d5290f171..51d16fdeee366dcbe69ebce182e217fa9113e8ce 100644
--- a/src/core/config/Config.h
+++ b/src/core/config/Config.h
@@ -88,16 +88,16 @@ public:
 
 private:
    //**Type definitions****************************************************************************
-   typedef std::string                                  Key;                  //!< Parameter key.
-   typedef std::string                                  Value;                //!< Parameter value.
-   typedef std::map<Key,Value, CaseInsensitiveCompare>  Map;                  //!< Parameter map.
-   typedef std::list<Block>                             List;                 //!< List for parameter blocks.
-   typedef std::map<Value,Value>                        ValueReplacementMap;  //!< Parameter value replacement map
-
-   typedef std::stringstream::pos_type         sstreamPos;  //!< Stream position.
-   typedef std::pair<sstreamPos,unsigned int>  Pair;        //!< Pair consisting of a stream position
+   using Key = std::string;                  //!< Parameter key.
+   using Value = std::string;                //!< Parameter value.
+   using Map = std::map<Key, Value, CaseInsensitiveCompare>;                  //!< Parameter map.
+   using List = std::list<Block>;                 //!< List for parameter blocks.
+   using ValueReplacementMap = std::map<Value, Value>;  //!< Parameter value replacement map
+
+   using sstreamPos = std::stringstream::pos_type;  //!< Stream position.
+   using Pair = std::pair<sstreamPos, unsigned int>;        //!< Pair consisting of a stream position
                                                             //!< and a line number.
-   typedef std::vector<Pair>                   LineVector;  //!< Vector for Pair.
+   using LineVector = std::vector<Pair>;  //!< Vector for Pair.
    //*******************************************************************************************************************
 
    //**Member constants*************************************************************************************************
@@ -106,11 +106,11 @@ private:
 
 public:
    //**Type definitions****************************************************************************
-   typedef std::vector<BlockHandle>  Blocks;          //!< Container for block handles.
-   typedef List::size_type           size_type;       //!< Size type for a Block count.
+   using Blocks = std::vector<BlockHandle>;          //!< Container for block handles.
+   using size_type = List::size_type;       //!< Size type for a Block count.
 
-   typedef Map::iterator             iterator;        //!< Iterator over the contained parameters.
-   typedef Map::const_iterator       const_iterator;  //!< Constant iterator over the contained parameters.
+   using iterator = Map::iterator;        //!< Iterator over the contained parameters.
+   using const_iterator = Map::const_iterator;  //!< Constant iterator over the contained parameters.
    //*******************************************************************************************************************
 
    //**Error codes******************************************************************************************************
@@ -280,7 +280,7 @@ public:
       template< typename T >
       inline Parameter<T> getParameter( const std::string & key, const T& defaultValue ) const;
 
-      bool isValid() const { return block_ != NULL; }
+      bool isValid() const { return block_ != nullptr; }
       operator bool() const { return isValid(); }
 
       inline const_iterator begin() const;
@@ -1058,7 +1058,7 @@ inline Config::const_iterator Config::Block::end() const
 // an error until the BlockHandle object is bound to a block.
  */
 inline Config::BlockHandle::BlockHandle()
-   :block_(0)
+   :block_(nullptr)
 {}
 //**********************************************************************************************************************
 
diff --git a/src/core/config/Iterator.h b/src/core/config/Iterator.h
index 3b9759426f1a70bbe30a0219ff960d0a9ae0e795..9796f59bfc16b5179240093ef29e018819dac1ad 100644
--- a/src/core/config/Iterator.h
+++ b/src/core/config/Iterator.h
@@ -29,7 +29,7 @@ namespace config {
 
    struct ConfigGenerator
    {
-      virtual ~ConfigGenerator() {}
+      virtual ~ConfigGenerator() = default;
       virtual shared_ptr<Config> next() = 0;
    };
 
@@ -37,7 +37,7 @@ namespace config {
    class Iterator
    {
    public:
-      Iterator() {}
+      Iterator() = default;
       Iterator( const shared_ptr<ConfigGenerator> & configGenerator )
           : generator_ ( configGenerator )
        {
diff --git a/src/core/debug/CheckFunctions.h b/src/core/debug/CheckFunctions.h
index 04444255cbec2bcf722ccee3d9f8788a6abb7b9a..4431b654daca92342bf6dfe1cee1b26cbde43729 100644
--- a/src/core/debug/CheckFunctions.h
+++ b/src/core/debug/CheckFunctions.h
@@ -212,7 +212,7 @@ namespace check_functions_detail {
 
 struct ExitHandler
 {
-   ExitHandler() {};
+   ExitHandler() = default;
    ExitHandler( const std::string & message ) : message_( message ) {}
    void operator()( const std::string & checkErrorMessage );
 private:
diff --git a/src/core/debug/CheckFunctions.impl.h b/src/core/debug/CheckFunctions.impl.h
index 023d5f7d3fa48d2920d7ba26444453931502572c..e4615bc0e1aaeca998385b5b8352bd00d05efa3a 100644
--- a/src/core/debug/CheckFunctions.impl.h
+++ b/src/core/debug/CheckFunctions.impl.h
@@ -37,7 +37,7 @@ namespace check_functions_detail {
 template< typename T >
 inline bool check_nullptr( T * p )
 {
-   return p == 0;
+   return p == nullptr;
 }
 
 template< typename T >
@@ -49,7 +49,7 @@ inline bool check_nullptr( const shared_ptr<T> & p )
 template< typename T >
 inline bool check_not_nullptr( T * p )
 {
-   return p != 0;
+   return p != nullptr;
 }
 
 template< typename T >
@@ -61,14 +61,14 @@ inline bool check_not_nullptr( const shared_ptr<T> & p )
 template< typename T, typename U >
 inline bool check_equal( const T & lhs, const U & rhs )
 {
-   typedef std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value> truth_type;
+   using truth_type = std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value>;
    return check_equal( lhs, rhs, truth_type() );
 }
 
 template< typename T, typename U >
 inline bool check_equal( const T & lhs, const U & rhs, const std::true_type & )
 {
-   typedef typename math::MathTrait<T,U>::High HighType;
+   using HighType = typename math::MathTrait<T, U>::High;
    return static_cast<HighType>(lhs) == static_cast<HighType>(rhs);
 }
 
@@ -81,14 +81,14 @@ inline bool check_equal( const T & lhs, const U & rhs, const std::false_type & )
 template< typename T, typename U >
 inline bool check_unequal( const T & lhs, const U & rhs )
 {
-   typedef std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value> truth_type;
+   using truth_type = std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value>;
    return check_unequal( lhs, rhs, truth_type() );
 }
 
 template< typename T, typename U >
 inline bool check_unequal( const T & lhs, const U & rhs, const std::true_type & )
 {
-   typedef typename math::MathTrait<T,U>::High HighType;
+   using HighType = typename math::MathTrait<T, U>::High;
    return static_cast<HighType>(lhs) != static_cast<HighType>(rhs);
 }
 
@@ -104,7 +104,7 @@ inline bool check_float_equal( const T & lhs, const U & rhs )
    static_assert( std::is_floating_point<T>::value,  "First operand type T is not a floating point type!");
    static_assert( std::is_floating_point<U>::value, "Second operand type U is not a floating point type!");
 
-   typedef typename math::MathTrait<T,U>::Low LowType;
+   using LowType = typename math::MathTrait<T, U>::Low;
 
    LowType low_lhs = static_cast<LowType>( lhs );
    LowType low_rhs = static_cast<LowType>( rhs );
@@ -118,7 +118,7 @@ inline bool check_float_unequal( const T & lhs, const U & rhs )
    static_assert( std::is_floating_point<T>::value,  "First operand type T is not a floating point type!");
    static_assert( std::is_floating_point<U>::value, "Second operand type U is not a floating point type!");
 
-   typedef typename math::MathTrait<T,U>::Low LowType;
+   using LowType = typename math::MathTrait<T, U>::Low;
 
    LowType low_lhs = static_cast<LowType>( lhs );
    LowType low_rhs = static_cast<LowType>( rhs );
@@ -133,7 +133,7 @@ inline bool check_float_equal_eps( const T & lhs, const U & rhs,
    static_assert( std::is_floating_point<T>::value,  "First operand type T is not a floating point type!");
    static_assert( std::is_floating_point<U>::value, "Second operand type U is not a floating point type!");
 
-   typedef typename math::MathTrait<T,U>::Low LowType;
+   using LowType = typename math::MathTrait<T, U>::Low;
 
    LowType low_lhs = static_cast<LowType>( lhs );
    LowType low_rhs = static_cast<LowType>( rhs );
@@ -148,7 +148,7 @@ inline bool check_float_unequal_eps( const T & lhs, const U & rhs,
    static_assert( std::is_floating_point<T>::value,  "First operand type T is not a floating point type!");
    static_assert( std::is_floating_point<U>::value, "Second operand type U is not a floating point type!");
 
-   typedef typename math::MathTrait<T,U>::Low LowType;
+   using LowType = typename math::MathTrait<T, U>::Low;
 
    LowType low_lhs = static_cast<LowType>( lhs );
    LowType low_rhs = static_cast<LowType>( rhs );
@@ -159,14 +159,14 @@ inline bool check_float_unequal_eps( const T & lhs, const U & rhs,
 template< typename T, typename U >
 inline bool check_identical( const T & lhs, const U & rhs )
 {
-   typedef std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value> truth_type;
+   using truth_type = std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value>;
    return check_identical( lhs, rhs, truth_type() );
 }
 
 template< typename T, typename U >
 inline bool check_identical( const T & lhs, const U & rhs, const std::true_type & )
 {
-   typedef typename math::MathTrait<T,U>::High HighType;
+   using HighType = typename math::MathTrait<T, U>::High;
    return isIdentical( static_cast<HighType>(lhs), static_cast<HighType>(rhs) );
 }
 
@@ -179,14 +179,14 @@ inline bool check_identical( const T & lhs, const U & rhs, const std::false_type
 template< typename T, typename U >
 inline bool check_not_identical( const T & lhs, const U & rhs )
 {
-   typedef std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value> truth_type;
+   using truth_type = std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value>;
    return check_not_identical( lhs, rhs, truth_type() );
 }
 
 template< typename T, typename U >
 inline bool check_not_identical( const T & lhs, const U & rhs, const std::true_type & )
 {
-   typedef typename math::MathTrait<T,U>::High HighType;
+   using HighType = typename math::MathTrait<T, U>::High;
    return !isIdentical( static_cast<HighType>(lhs), static_cast<HighType>(rhs) );
 }
 
@@ -199,14 +199,14 @@ inline bool check_not_identical( const T & lhs, const U & rhs, const std::false_
 template< typename T, typename U >
 inline bool check_less( const T & lhs, const U & rhs )
 {
-   typedef std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value> truth_type;
+   using truth_type = std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value>;
    return check_less( lhs, rhs, truth_type() );
 }
 
 template< typename T, typename U >
 inline bool check_less( const T & lhs, const U & rhs, const std::true_type & )
 {
-   typedef typename math::MathTrait<T,U>::High HighType;
+   using HighType = typename math::MathTrait<T, U>::High;
    return static_cast<HighType>(lhs) < static_cast<HighType>(rhs);
 }
 
@@ -219,14 +219,14 @@ inline bool check_less( const T & lhs, const U & rhs, const std::false_type & )
 template< typename T, typename U >
 inline bool check_greater( const T & lhs, const U & rhs )
 {
-   typedef std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value> truth_type;
+   using truth_type = std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value>;
    return check_greater( lhs, rhs, truth_type() );
 }
 
 template< typename T, typename U >
 inline bool check_greater( const T & lhs, const U & rhs, const std::true_type & )
 {
-   typedef typename math::MathTrait<T,U>::High HighType;
+   using HighType = typename math::MathTrait<T, U>::High;
    return static_cast<HighType>(lhs) > static_cast<HighType>(rhs);
 }
 
@@ -239,14 +239,14 @@ inline bool check_greater( const T & lhs, const U & rhs, const std::false_type &
 template< typename T, typename U >
 inline bool check_less_equal( const T & lhs, const U & rhs )
 {
-   typedef std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value> truth_type;
+   using truth_type = std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value>;
    return check_less_equal( lhs, rhs, truth_type() );
 }
 
 template< typename T, typename U >
 inline bool check_less_equal( const T & lhs, const U & rhs, const std::true_type & )
 {
-   typedef typename math::MathTrait<T,U>::High HighType;
+   using HighType = typename math::MathTrait<T, U>::High;
    return static_cast<HighType>(lhs) <= static_cast<HighType>(rhs);
 }
 
@@ -259,14 +259,14 @@ inline bool check_less_equal( const T & lhs, const U & rhs, const std::false_typ
 template< typename T, typename U >
 inline bool check_greater_equal( const T & lhs, const U & rhs )
 {
-   typedef std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value> truth_type;
+   using truth_type = std::integral_constant<bool, std::is_arithmetic<T>::value && std::is_arithmetic<U>::value>;
    return check_greater_equal( lhs, rhs, truth_type() );
 }
 
 template< typename T, typename U >
 inline bool check_greater_equal( const T & lhs, const U & rhs, const std::true_type & )
 {
-   typedef typename math::MathTrait<T,U>::High HighType;
+   using HighType = typename math::MathTrait<T, U>::High;
    return static_cast<HighType>(lhs) >= static_cast<HighType>(rhs);
 }
 
diff --git a/src/core/debug/demangle.h b/src/core/debug/demangle.h
index 2e317436e6d9bace3045ef40a79aa4fdd0f88a85..55334b5509744c9cbe40d772a569c16c91182b85 100644
--- a/src/core/debug/demangle.h
+++ b/src/core/debug/demangle.h
@@ -42,7 +42,7 @@ inline std::string demangle( const std::string & name )
 #ifdef HAVE_CXXABI_H
    int status = 0;
    std::size_t size = 0;
-   const char * demangled = abi::__cxa_demangle( name.c_str(), NULL, &size, &status );
+   const char * demangled = abi::__cxa_demangle( name.c_str(), nullptr, &size, &status );
    if( demangled == nullptr )
    {
       return name;
diff --git a/src/core/grid_generator/HCPIterator.cpp b/src/core/grid_generator/HCPIterator.cpp
index 6be206c66dffd3a5c3f346fc1e5d2489a182524d..7d136ef4250ca823fd95ec11d913a85c1577d60b 100644
--- a/src/core/grid_generator/HCPIterator.cpp
+++ b/src/core/grid_generator/HCPIterator.cpp
@@ -168,14 +168,7 @@ bool HCPIterator::operator==(const HCPIterator &rhs) const
 {
    if (ended_ || rhs.ended_)
    {
-      if (ended_ == rhs.ended_)
-      {
-         return true;
-      }
-      else
-      {
-         return false;
-      }
+      return ended_ == rhs.ended_;
    }
 
 //   WALBERLA_ASSERT_FLOAT_EQUAL(aabb_, rhs.aabb_, "Comparing iterators for different starting configurations!");
diff --git a/src/core/grid_generator/HCPIterator.h b/src/core/grid_generator/HCPIterator.h
index dc84a29c61176ff55edd8e7ae53e3b8795ec8915..dfc806d116a860e15c00a3c04a37ba30b607bc8a 100644
--- a/src/core/grid_generator/HCPIterator.h
+++ b/src/core/grid_generator/HCPIterator.h
@@ -33,9 +33,15 @@ namespace grid_generator {
 ///
 /// Usage:
 /// \code for (auto it = HCPIterator::begin(...); it != HCPIterator::end(); ++it) \endcode
-class HCPIterator : public std::iterator< std::forward_iterator_tag, Vector3<real_t> >
+class HCPIterator
 {
 public:
+    using iterator_category = std::forward_iterator_tag;
+    using value_type = Vector3<real_t>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = Vector3<real_t>*;
+    using reference = Vector3<real_t>&;
+
    /**
     * @brief begin iterator
     * @param domain volume were lattice points will be returned
diff --git a/src/core/grid_generator/SCIterator.cpp b/src/core/grid_generator/SCIterator.cpp
index 6c4399a6489ab85d22d647f3e84516685d07e680..254f97214cdb476bd3b2b882281fe44719a1df91 100644
--- a/src/core/grid_generator/SCIterator.cpp
+++ b/src/core/grid_generator/SCIterator.cpp
@@ -115,14 +115,7 @@ bool SCIterator::operator==(const SCIterator &rhs) const
 {
    if (ended_ || rhs.ended_)
    {
-      if (ended_ == rhs.ended_)
-      {
-         return true;
-      }
-      else
-      {
-         return false;
-      }
+      return ended_ == rhs.ended_;
    }
 
 //   WALBERLA_ASSERT_FLOAT_EQUAL(aabb_, rhs.aabb_, "Comparing iterators for different starting configurations!");
diff --git a/src/core/grid_generator/SCIterator.h b/src/core/grid_generator/SCIterator.h
index 4946f8dbd3b4d5d55fb857773e4d9c1c141e0743..7068bd3de13504a745b3f24ca7ccc58ec3be2ad5 100644
--- a/src/core/grid_generator/SCIterator.h
+++ b/src/core/grid_generator/SCIterator.h
@@ -46,9 +46,15 @@ namespace grid_generator {
 /// \endcode
 /// Usage:
 /// \code for (auto it = SCIterator::begin(...); it != SCIterator::end(); ++it) \endcode
-class SCIterator : public std::iterator< std::forward_iterator_tag, Vector3<real_t> >
+class SCIterator
 {
 public:
+    using iterator_category = std::forward_iterator_tag;
+    using value_type = Vector3<real_t>;
+    using difference_type = std::ptrdiff_t;
+    using pointer = Vector3<real_t>*;
+    using reference = Vector3<real_t>&;
+
    /**
     * @brief begin iterator
     * @param domain volume were lattice points will be returned
diff --git a/src/core/logging/Logging.h b/src/core/logging/Logging.h
index 1a43778516e837e874fed0dda040c25508b93c06..8d174df51acee3b451cf17a211b1f29c5325ca78 100644
--- a/src/core/logging/Logging.h
+++ b/src/core/logging/Logging.h
@@ -82,7 +82,7 @@ public:
    class CustomStamp
    {
    public:
-      virtual ~CustomStamp() {}
+      virtual ~CustomStamp() = default;
       virtual std::string stamp() = 0;
       virtual uint_t      maxStampWidth() = 0;
    };
diff --git a/src/core/math/AABBFwd.h b/src/core/math/AABBFwd.h
index 9a7593b67bb0ef469a3753a786282aa7e64045cd..3a51fbd441b3b6addfb9f09b10d2aec02c0ae5e5 100644
--- a/src/core/math/AABBFwd.h
+++ b/src/core/math/AABBFwd.h
@@ -30,7 +30,7 @@ namespace math {
 template< typename T >
 class GenericAABB;
 
-typedef GenericAABB< real_t > AABB; /// see class GenericAABB
+using AABB = GenericAABB<real_t>; /// see class GenericAABB
 
 } // namespace math
 
diff --git a/src/core/math/Angles.h b/src/core/math/Angles.h
new file mode 100644
index 0000000000000000000000000000000000000000..3294d892b299db83b8455d05226060b850287c82
--- /dev/null
+++ b/src/core/math/Angles.h
@@ -0,0 +1,34 @@
+//======================================================================================================================
+//
+//  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
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "Constants.h"
+
+namespace walberla
+{
+namespace math
+{
+///Converts a degrees angle to radians.
+constexpr real_t degToRad(real_t deg) {return deg * (pi / real_t(180));}
+///Converts a radians angle to degrees.
+constexpr real_t radToDeg(real_t rad) {return rad * (real_t(180) / pi);}
+} // namespace math
+} // namespace walberla
diff --git a/src/core/math/Constants.h b/src/core/math/Constants.h
index 79e97e97ed716524712878b36566379a01d36c24..5487e14cf34f651a9b1065520d182fae3b4e9c19 100644
--- a/src/core/math/Constants.h
+++ b/src/core/math/Constants.h
@@ -30,29 +30,25 @@
 #include <cmath>
 #include <core/DataTypes.h>
 
-// Disable false warnings in GCC 5
-#if (defined __GNUC__) && (__GNUC__ == 5) && (__GNUC_MINOR__ == 1)
-#   pragma GCC diagnostic push
-#   pragma GCC diagnostic ignored "-Wunused-variable"
-#endif
-
 namespace walberla
 {
 namespace math
 {
-constexpr real_t e                = real_t(2.7182818284590452354);  /* e */
-constexpr real_t log2_e           = real_t(1.4426950408889634074);  /* log_2 e */
-constexpr real_t log10_e          = real_t(0.43429448190325182765); /* log_10 e */
-constexpr real_t ln_two           = real_t(0.69314718055994530942); /* log_e 2 */
-constexpr real_t ln_ten           = real_t(2.30258509299404568402); /* log_e 10 */
-constexpr real_t pi               = real_t(3.14159265358979323846); /* pi */
-constexpr real_t half_pi          = real_t(1.57079632679489661923); /* pi/2 */
-constexpr real_t fourth_pi        = real_t(0.78539816339744830962); /* pi/4 */
-constexpr real_t one_div_pi       = real_t(0.31830988618379067154); /* 1/pi */
-constexpr real_t two_div_pi       = real_t(0.63661977236758134308); /* 2/pi */
-constexpr real_t two_div_root_pi  = real_t(1.12837916709551257390); /* 2/sqrt(pi) */
-constexpr real_t root_two         = real_t(1.41421356237309504880); /* sqrt(2) */
-constexpr real_t one_div_root_two = real_t(0.70710678118654752440); /* 1/sqrt(2) */
+constexpr real_t e                  = real_t(2.7182818284590452354);  /* e */
+constexpr real_t log2_e             = real_t(1.4426950408889634074);  /* log_2 e */
+constexpr real_t log10_e            = real_t(0.43429448190325182765); /* log_10 e */
+constexpr real_t ln_two             = real_t(0.69314718055994530942); /* log_e 2 */
+constexpr real_t ln_ten             = real_t(2.30258509299404568402); /* log_e 10 */
+constexpr real_t pi                 = real_t(3.14159265358979323846); /* pi */
+constexpr real_t half_pi            = real_t(1.57079632679489661923); /* pi/2 */
+constexpr real_t fourth_pi          = real_t(0.78539816339744830962); /* pi/4 */
+constexpr real_t one_div_pi         = real_t(0.31830988618379067154); /* 1/pi */
+constexpr real_t two_div_pi         = real_t(0.63661977236758134308); /* 2/pi */
+constexpr real_t two_div_root_pi    = real_t(1.12837916709551257390); /* 2/sqrt(pi) */
+constexpr real_t root_two           = real_t(1.41421356237309504880); /* sqrt(2) */
+constexpr real_t one_div_root_two   = real_t(0.70710678118654752440); /* 1/sqrt(2) */
+constexpr real_t root_three         = real_t(1.73205080757); /* sqrt(3) */
+constexpr real_t one_div_root_three = real_t(0.57735026919); /* 1/sqrt(3) */
 
 } // namespace math
 } // namespace walberla
\ No newline at end of file
diff --git a/src/core/math/GenericAABB.h b/src/core/math/GenericAABB.h
index a6e18882af3e2df37e7f9fb00983d360ed3d6d14..5d326b5af973b66db1c7628650cfa77e6c5d9433 100644
--- a/src/core/math/GenericAABB.h
+++ b/src/core/math/GenericAABB.h
@@ -48,8 +48,8 @@ class GenericAABB
 
 public:
    // Typedefs
-   typedef T            value_type;  /// scalar data type
-   typedef Vector3< T > vector_type; /// data type for three dimensional vectors
+   using value_type = T;  /// scalar data type
+   using vector_type = Vector3<T>; /// data type for three dimensional vectors
 
    // Constructors
    inline GenericAABB();
diff --git a/src/core/math/MathTrait.h b/src/core/math/MathTrait.h
index 3f333228957bd553f13d0bf9ad8ae60f985a454e..492badea48f77837f90b1dfe0c88a542cf820748 100644
--- a/src/core/math/MathTrait.h
+++ b/src/core/math/MathTrait.h
@@ -195,19 +195,19 @@ struct INVALID_NUMERICAL_TYPE;
 template< typename T1, typename T2 >
 struct MathTrait
 {
-   typedef T1   HighType;
-   typedef T2   LowType;
-   typedef T1   High;
-   typedef T2   Low;
+   using HighType = T1;
+   using LowType = T2;
+   using High = T1;
+   using Low = T2;
 };
 
 template< typename T>
 struct MathTrait< T, T >
 {
-   typedef T   HighType;
-   typedef T   LowType;
-   typedef T   High;
-   typedef T   Low;
+   using HighType = T;
+   using LowType = T;
+   using High = T;
+   using Low = T;
 };
 
 
diff --git a/src/core/math/Matrix2.h b/src/core/math/Matrix2.h
index e3d8893333b3e8ba27f59b3565d551379b8fd774..7132b05d06b6b524024587c40bb1f1dd85a12566 100644
--- a/src/core/math/Matrix2.h
+++ b/src/core/math/Matrix2.h
@@ -87,16 +87,16 @@ private:
 
 public:
    //**Constructors*****************************************************************************************************
-   explicit inline Matrix2();
-   explicit inline Matrix2( Type init );
-   explicit inline Matrix2( Type xx, Type xy, Type yx, Type yy );
-   explicit inline Matrix2( const Type* init );
+   explicit inline constexpr Matrix2();
+   explicit inline constexpr Matrix2( Type init );
+   explicit inline constexpr Matrix2( Type xx, Type xy, Type yx, Type yy );
+   explicit inline constexpr Matrix2( const Type* init );
 
 
-   inline Matrix2( const Matrix2& m );
+   inline constexpr Matrix2( const Matrix2& m );
 
    template< typename Other >
-   inline Matrix2( const Matrix2<Other>& m );
+   inline constexpr Matrix2( const Matrix2<Other>& m );
    //*******************************************************************************************************************
 
    //**Destructor*******************************************************************************************************
@@ -189,7 +189,7 @@ private:
 // with 0.
 */
 template< typename Type >
-inline Matrix2<Type>::Matrix2()
+inline constexpr Matrix2<Type>::Matrix2()
 {
    v_[0] = v_[3] = Type(1);
    v_[1] = v_[2] = Type(0);
@@ -204,7 +204,7 @@ inline Matrix2<Type>::Matrix2()
 // \param init Initial value for all matrix elements.
 */
 template< typename Type >
-inline Matrix2<Type>::Matrix2( Type init )
+inline constexpr Matrix2<Type>::Matrix2( Type init )
 {
    v_[0] = v_[1] = v_[2] = v_[3] = init;
 }
@@ -221,8 +221,8 @@ inline Matrix2<Type>::Matrix2( Type init )
 // \param yy The initial value for the yy-component.
 */
 template< typename Type >
-inline Matrix2<Type>::Matrix2( Type xx, Type xy,
-                               Type yx, Type yy  )
+inline constexpr Matrix2<Type>::Matrix2( Type xx, Type xy,
+                                         Type yx, Type yy  )
 {
    v_[0] = xx; v_[1] = xy;
    v_[2] = yx; v_[3] = yy;
@@ -239,7 +239,7 @@ inline Matrix2<Type>::Matrix2( Type xx, Type xy,
 // The array is assumed to have at least nine valid elements.
 */
 template< typename Type >
-inline Matrix2<Type>::Matrix2( const Type* init )
+inline constexpr Matrix2<Type>::Matrix2( const Type* init )
 {
    v_[0] = init[0];
    v_[1] = init[1];
@@ -258,7 +258,7 @@ inline Matrix2<Type>::Matrix2( const Type* init )
 // The copy constructor is explicitly defined in order to enable/facilitate NRV optimization.
 */
 template< typename Type >
-inline Matrix2<Type>::Matrix2( const Matrix2& m )
+inline constexpr Matrix2<Type>::Matrix2( const Matrix2& m )
 {
    v_[0] = m.v_[0];
    v_[1] = m.v_[1];
@@ -276,7 +276,7 @@ inline Matrix2<Type>::Matrix2( const Matrix2& m )
 */
 template< typename Type >
 template< typename Other >
-inline Matrix2<Type>::Matrix2( const Matrix2<Other>& m )
+inline constexpr Matrix2<Type>::Matrix2( const Matrix2<Other>& m )
 {
    v_[0] = m.v_[0];
    v_[1] = m.v_[1];
@@ -375,12 +375,10 @@ inline bool Matrix2<Type>::operator==( const Matrix2<Other>& rhs ) const
 {
    // In order to compare the vector and the scalar value, the data values of the lower-order
    // data type are converted to the higher-order data type.
-   if( !equal( v_[0], rhs.v_[0] ) ||
-       !equal( v_[1], rhs.v_[1] ) ||
-       !equal( v_[2], rhs.v_[2] ) ||
-       !equal( v_[3], rhs.v_[3] ) )
-      return false;
-   else return true;
+   return equal( v_[0], rhs.v_[0] ) &&
+          equal( v_[1], rhs.v_[1] ) &&
+          equal( v_[2], rhs.v_[2] ) &&
+          equal( v_[3], rhs.v_[3] );
 }
 //**********************************************************************************************************************
 
@@ -398,12 +396,7 @@ inline bool Matrix2<Type>::operator!=( const Matrix2<Other>& rhs ) const
 {
    // In order to compare the vector and the scalar value, the data values of the lower-order
    // data type are converted to the higher-order data type.
-   if( !equal( v_[0], rhs.v_[0] ) ||
-       !equal( v_[1], rhs.v_[1] ) ||
-       !equal( v_[2], rhs.v_[2] ) ||
-       !equal( v_[3], rhs.v_[3] ) )
-      return true;
-   else return false;
+   return !(*this == rhs);
 }
 //**********************************************************************************************************************
 
@@ -781,9 +774,7 @@ inline const Matrix2<Type> Matrix2<Type>::getInverse() const
 template< typename Type >
 inline bool Matrix2<Type>::isSingular() const
 {
-   if( equal( getDeterminant(), Type(0) ) )
-      return true;
-   else return false;
+   return equal( getDeterminant(), Type(0) );
 }
 //**********************************************************************************************************************
 
@@ -875,11 +866,8 @@ std::ostream& operator<<( std::ostream& os, const Matrix2<Type>& m )
 template< typename Type >
 inline bool isnan( const Matrix2<Type>& m )
 {
-   if( math::isnan( m[0] ) || math::isnan( m[1] )||
-       math::isnan( m[2] ) || math::isnan( m[3] )  )
-      return true;
-   else
-      return false;
+   return math::isnan( m[0] ) || math::isnan( m[1] )||
+       math::isnan( m[2] ) || math::isnan( m[3] );
 }
 //**********************************************************************************************************************
 
@@ -999,7 +987,7 @@ namespace walberla {
 template<typename T>
 struct VectorTrait< Matrix2<T> >
 {
-   typedef T OutputType;
+   using OutputType = T;
 
    static const uint_t F_SIZE =  4u;
    static T    get( const Matrix2<T> & v, uint_t f )       { return v[f]; }
diff --git a/src/core/math/Matrix3.h b/src/core/math/Matrix3.h
index b206d484dfcc17ce47a11b667f75b2cfc176b1f4..d6020cd539992e0f5863eb8e25a553da40dbe6a1 100644
--- a/src/core/math/Matrix3.h
+++ b/src/core/math/Matrix3.h
@@ -95,11 +95,11 @@ private:
 
 public:
    //**Constructors*****************************************************************************************************
-   explicit inline Matrix3() = default;
-   explicit inline Matrix3( Type init );
-   explicit inline Matrix3( const Vector3<Type>& a, const Vector3<Type>& b, const Vector3<Type>& c );
-   explicit inline Matrix3( Type xx, Type xy, Type xz, Type yx, Type yy, Type yz, Type zx, Type zy, Type zz );
-   explicit inline Matrix3( const Type* init );
+   explicit inline constexpr Matrix3() = default;
+   explicit inline constexpr Matrix3( Type init );
+   explicit inline constexpr Matrix3( const Vector3<Type>& a, const Vector3<Type>& b, const Vector3<Type>& c );
+   explicit inline constexpr Matrix3( Type xx, Type xy, Type xz, Type yx, Type yy, Type yz, Type zx, Type zy, Type zz );
+   explicit inline constexpr Matrix3( const Type* init );
 
    template< typename Axis, typename Angle >
    explicit Matrix3( Vector3<Axis> axis, Angle angle );
@@ -274,7 +274,7 @@ static_assert( std::is_trivially_copyable<Matrix3<real_t>>::value, "Matrix3<real
 // \param init Initial value for all matrix elements.
 */
 template< typename Type >
-inline Matrix3<Type>::Matrix3( Type init )
+inline constexpr Matrix3<Type>::Matrix3( Type init )
 {
    v_[0] = v_[1] = v_[2] = v_[3] = v_[4] = v_[5] = v_[6] = v_[7] = v_[8] = init;
 }
@@ -289,7 +289,7 @@ inline Matrix3<Type>::Matrix3( Type init )
 // \param c The third column of the matrix.
 //**********************************************************************************************************************
 template< typename Type >
-inline Matrix3<Type>::Matrix3( const Vector3<Type>& a, const Vector3<Type>& b, const Vector3<Type>& c )
+inline constexpr Matrix3<Type>::Matrix3( const Vector3<Type>& a, const Vector3<Type>& b, const Vector3<Type>& c )
 {
    v_[0] = a[0]; v_[1] = b[0]; v_[2] = c[0];
    v_[3] = a[1]; v_[4] = b[1]; v_[5] = c[1];
@@ -313,9 +313,9 @@ inline Matrix3<Type>::Matrix3( const Vector3<Type>& a, const Vector3<Type>& b, c
 // \param zz The initial value for the zz-component.
 */
 template< typename Type >
-inline Matrix3<Type>::Matrix3( Type xx, Type xy, Type xz,
-                               Type yx, Type yy, Type yz,
-                               Type zx, Type zy, Type zz )
+inline constexpr Matrix3<Type>::Matrix3( Type xx, Type xy, Type xz,
+                                         Type yx, Type yy, Type yz,
+                                         Type zx, Type zy, Type zz )
 {
    v_[0] = xx; v_[1] = xy; v_[2] = xz;
    v_[3] = yx; v_[4] = yy; v_[5] = yz;
@@ -333,7 +333,7 @@ inline Matrix3<Type>::Matrix3( Type xx, Type xy, Type xz,
 // The array is assumed to have at least nine valid elements.
 */
 template< typename Type >
-inline Matrix3<Type>::Matrix3( const Type* init )
+inline constexpr Matrix3<Type>::Matrix3( const Type* init )
 {
    v_[0] = init[0];
    v_[1] = init[1];
@@ -525,17 +525,15 @@ inline bool Matrix3<Type>::operator==( const Matrix3<Other>& rhs ) const
 {
    // In order to compare the vector and the scalar value, the data values of the lower-order
    // data type are converted to the higher-order data type.
-   if( !equal( v_[0], rhs.v_[0] ) ||
-       !equal( v_[1], rhs.v_[1] ) ||
-       !equal( v_[2], rhs.v_[2] ) ||
-       !equal( v_[3], rhs.v_[3] ) ||
-       !equal( v_[4], rhs.v_[4] ) ||
-       !equal( v_[5], rhs.v_[5] ) ||
-       !equal( v_[6], rhs.v_[6] ) ||
-       !equal( v_[7], rhs.v_[7] ) ||
-       !equal( v_[8], rhs.v_[8] ) )
-      return false;
-   else return true;
+   return equal( v_[0], rhs.v_[0] ) &&
+          equal( v_[1], rhs.v_[1] ) &&
+          equal( v_[2], rhs.v_[2] ) &&
+          equal( v_[3], rhs.v_[3] ) &&
+          equal( v_[4], rhs.v_[4] ) &&
+          equal( v_[5], rhs.v_[5] ) &&
+          equal( v_[6], rhs.v_[6] ) &&
+          equal( v_[7], rhs.v_[7] ) &&
+          equal( v_[8], rhs.v_[8] );
 }
 //**********************************************************************************************************************
 
@@ -553,17 +551,7 @@ inline bool Matrix3<Type>::operator!=( const Matrix3<Other>& rhs ) const
 {
    // In order to compare the vector and the scalar value, the data values of the lower-order
    // data type are converted to the higher-order data type.
-   if( !equal( v_[0], rhs.v_[0] ) ||
-       !equal( v_[1], rhs.v_[1] ) ||
-       !equal( v_[2], rhs.v_[2] ) ||
-       !equal( v_[3], rhs.v_[3] ) ||
-       !equal( v_[4], rhs.v_[4] ) ||
-       !equal( v_[5], rhs.v_[5] ) ||
-       !equal( v_[6], rhs.v_[6] ) ||
-       !equal( v_[7], rhs.v_[7] ) ||
-       !equal( v_[8], rhs.v_[8] ) )
-      return true;
-   else return false;
+   return !(*this == rhs);
 }
 //**********************************************************************************************************************
 
@@ -1129,9 +1117,7 @@ inline const Matrix3<HIGH> Matrix3<Type>::diagRotate( const Matrix3<Other>& m )
 template< typename Type >
 inline bool Matrix3<Type>::isSingular() const
 {
-   if( equal( getDeterminant(), Type(0) ) )
-      return true;
-   else return false;
+   return equal( getDeterminant(), Type(0) );
 }
 //**********************************************************************************************************************
 
@@ -1145,9 +1131,7 @@ inline bool Matrix3<Type>::isSingular() const
 template< typename Type >
 inline bool Matrix3<Type>::isSymmetric() const
 {
-   if( !equal( v_[1], v_[3] ) || !equal( v_[2], v_[6] ) || !equal( v_[5], v_[7] ) )
-      return false;
-   else return true;
+   return equal( v_[1], v_[3] ) && equal( v_[2], v_[6] ) && equal( v_[5], v_[7] );
 }
 //**********************************************************************************************************************
 
@@ -1160,7 +1144,7 @@ inline bool Matrix3<Type>::isSymmetric() const
 template< typename Type >
 inline bool Matrix3<Type>::isZero() const
 {
-   if( equal( v_[0], Type(0) ) &&
+   return equal( v_[0], Type(0) ) &&
        equal( v_[1], Type(0) ) &&
        equal( v_[2], Type(0) ) &&
        equal( v_[3], Type(0) ) &&
@@ -1168,9 +1152,7 @@ inline bool Matrix3<Type>::isZero() const
        equal( v_[5], Type(0) ) &&
        equal( v_[6], Type(0) ) &&
        equal( v_[7], Type(0) ) &&
-       equal( v_[8], Type(0) ) )
-      return true;
-   else return false;
+       equal( v_[8], Type(0) );
 }
 //**********************************************************************************************************************
 
@@ -1595,11 +1577,9 @@ std::ostream& operator<<( std::ostream& os, const Matrix3<Type>& m )
 template< typename Type >
 inline bool isnan( const Matrix3<Type>& m )
 {
-   if( math::isnan( m[0] ) || math::isnan( m[1] ) || math::isnan( m[2] ) ||
+   return math::isnan( m[0] ) || math::isnan( m[1] ) || math::isnan( m[2] ) ||
        math::isnan( m[3] ) || math::isnan( m[4] ) || math::isnan( m[5] ) ||
-       math::isnan( m[6] ) || math::isnan( m[7] ) || math::isnan( m[8] ) )
-      return true;
-   else return false;
+       math::isnan( m[6] ) || math::isnan( m[7] ) || math::isnan( m[8] );
 }
 //**********************************************************************************************************************
 
@@ -1658,11 +1638,9 @@ inline const Matrix3<Type> fabs( const Matrix3<Type>& m )
 template< typename Type >
 inline bool isinf( const Matrix3<Type>& m )
 {
-   if( math::isinf( m[0] ) || math::isinf( m[1] ) || math::isinf( m[2] ) ||
+   return math::isinf( m[0] ) || math::isinf( m[1] ) || math::isinf( m[2] ) ||
        math::isinf( m[3] ) || math::isinf( m[4] ) || math::isinf( m[5] ) ||
-       math::isinf( m[6] ) || math::isinf( m[7] ) || math::isinf( m[8] ) )
-      return true;
-   else return false;
+       math::isinf( m[6] ) || math::isinf( m[7] ) || math::isinf( m[8] );
 }
 //**********************************************************************************************************************
 
@@ -1716,7 +1694,7 @@ namespace walberla {
 template<typename T>
 struct VectorTrait< Matrix3<T> >
 {
-   typedef T OutputType;
+   using OutputType = T;
 
    static const uint_t F_SIZE =  9u;
    static T    get( const Matrix3<T> & v, uint_t f )       { return v[f]; }
diff --git a/src/core/math/MatrixMxN.h b/src/core/math/MatrixMxN.h
index 70854e2b6f029cfc29b009587cdd8681d024b1a3..380baeb326b4e8f6841575317c3bf9b61752d352 100644
--- a/src/core/math/MatrixMxN.h
+++ b/src/core/math/MatrixMxN.h
@@ -96,22 +96,22 @@ class MatrixMxN
 
 public:
    //**Type definitions****************************************************************************
-   typedef MatrixMxN<Type>   This;           //!< Type of this MatrixMxN instance.
-   typedef This              ResultType;     //!< Result type for expression template evaluations.
-   typedef Type              ElementType;    //!< Type of the matrix elements.
-   typedef const MatrixMxN&  CompositeType;  //!< Data type for composite expression templates.
+   using This = MatrixMxN<Type>;           //!< Type of this MatrixMxN instance.
+   using ResultType = This;     //!< Result type for expression template evaluations.
+   using ElementType = Type;    //!< Type of the matrix elements.
+   using CompositeType = const MatrixMxN &;  //!< Data type for composite expression templates.
    //**********************************************************************************************
 
    //**Constructors********************************************************************************
    /*!\name Constructors */
    //@{
-                           explicit inline MatrixMxN();
-                           explicit inline MatrixMxN( size_t m, size_t n );
-                           explicit inline MatrixMxN( size_t m, size_t n, Type init );
-                                    inline MatrixMxN( const MatrixMxN& m );
+                           explicit inline constexpr MatrixMxN();
+                           explicit inline constexpr MatrixMxN( size_t m, size_t n );
+                           explicit inline constexpr MatrixMxN( size_t m, size_t n, Type init );
+                                    inline constexpr MatrixMxN( const MatrixMxN& m );
 
    template< typename Other, size_t M, size_t N >
-   inline MatrixMxN( const Other (&rhs)[M][N] );
+   inline constexpr MatrixMxN( const Other (&rhs)[M][N] );
    //@}
    //**********************************************************************************************
 
@@ -199,11 +199,11 @@ private:
 /*!\brief The default constructor for MatrixMxN.
  */
 template< typename Type >  // Data type of the matrix
-inline MatrixMxN<Type>::MatrixMxN()
+inline constexpr MatrixMxN<Type>::MatrixMxN()
    : m_       ( 0 )  // The current number of rows of the matrix
    , n_       ( 0 )  // The current number of columns of the matrix
    , capacity_( 0 )  // The maximum capacity of the matrix
-   , v_       ( 0 )  // The matrix elements
+   , v_       ( nullptr )  // The matrix elements
 {}
 //*************************************************************************************************
 
@@ -218,7 +218,7 @@ inline MatrixMxN<Type>::MatrixMxN()
  *          element initialization is performed!
  */
 template< typename Type >  // Data type of the matrix
-inline MatrixMxN<Type>::MatrixMxN( size_t m, size_t n )
+inline constexpr MatrixMxN<Type>::MatrixMxN( size_t m, size_t n )
    : m_       ( m )                    // The current number of rows of the matrix
    , n_       ( n )                    // The current number of columns of the matrix
    , capacity_( m*n )                  // The maximum capacity of the matrix
@@ -237,7 +237,7 @@ inline MatrixMxN<Type>::MatrixMxN( size_t m, size_t n )
  * All matrix elements are initialized with the specified value.
  */
 template< typename Type >  // Data type of the matrix
-inline MatrixMxN<Type>::MatrixMxN( size_t m, size_t n, Type init )
+inline constexpr MatrixMxN<Type>::MatrixMxN( size_t m, size_t n, Type init )
    : m_       ( m )                    // The current number of rows of the matrix
    , n_       ( n )                    // The current number of columns of the matrix
    , capacity_( m*n )                  // The maximum capacity of the matrix
@@ -258,7 +258,7 @@ inline MatrixMxN<Type>::MatrixMxN( size_t m, size_t n, Type init )
  * and in order to enable/facilitate NRV optimization.
  */
 template< typename Type >  // Data type of the matrix
-inline MatrixMxN<Type>::MatrixMxN( const MatrixMxN& m )
+inline constexpr MatrixMxN<Type>::MatrixMxN( const MatrixMxN& m )
    : m_       ( m.m_  )                // The current number of rows of the matrix
    , n_       ( m.n_  )                // The current number of columns of the matrix
    , capacity_( m_*n_ )                // The maximum capacity of the matrix
@@ -292,7 +292,7 @@ template< typename Type >  // Data type of the matrix
 template< typename Other   // Data type of the initialization array
         , size_t M         // Number of rows of the initialization array
         , size_t N >       // Number of columns of the initialization array
-inline MatrixMxN<Type>::MatrixMxN( const Other (&rhs)[M][N] )
+inline constexpr MatrixMxN<Type>::MatrixMxN( const Other (&rhs)[M][N] )
    : m_       ( M )              // The current number of rows of the matrix
    , n_       ( N )              // The current number of columns of the matrix
    , capacity_( M*N )            // The maximum capacity of the matrix
diff --git a/src/core/math/Parser.cpp b/src/core/math/Parser.cpp
index b3a4575677d319efd1cd2d198cbcedd228bb561e..7c39e5e9da6402e5981cabbf9e2685ee58988084 100644
--- a/src/core/math/Parser.cpp
+++ b/src/core/math/Parser.cpp
@@ -31,9 +31,7 @@
 #   pragma warning( disable : 4706 )
 #elif ( defined WALBERLA_CXX_COMPILER_IS_GNU ) || ( defined WALBERLA_CXX_COMPILER_IS_CLANG )
 #   pragma GCC diagnostic push
-#   if !( ( __clang_major__ == 3 ) && ( __clang_minor__ <= 4 ) )
-#     pragma GCC diagnostic ignored "-Wpragmas"
-#   endif
+#   pragma GCC diagnostic ignored "-Wpragmas"
 #   pragma GCC diagnostic ignored "-Wsign-conversion"
 #   pragma GCC diagnostic ignored "-Wconversion"
 #   pragma GCC diagnostic ignored "-Wshorten-64-to-32"
diff --git a/src/core/math/Plane.h b/src/core/math/Plane.h
index bab0ac1e842c10583404e7cc312390474a43cb19..219a13c3919a1081b45b3577901a6616ebfa5311 100644
--- a/src/core/math/Plane.h
+++ b/src/core/math/Plane.h
@@ -33,7 +33,7 @@ namespace math {
 class Plane
 {
 public:
-   typedef Vector3<real_t> Vec3Real;
+   using Vec3Real = Vector3<real_t>;
 
    inline Plane();
    inline Plane( const Vec3Real & origin, const Vec3Real & _normal );
diff --git a/src/core/math/Quaternion.h b/src/core/math/Quaternion.h
index 90fd652de353026cab2612aed1be1570d754ea66..417e9b7937c1e1cb372cfa18495514c00a526090 100644
--- a/src/core/math/Quaternion.h
+++ b/src/core/math/Quaternion.h
@@ -108,7 +108,7 @@ class Quaternion
 
 public:
    //**Type definitions****************************************************************************
-   typedef Type  ElementType;  //!< Type of the quaternion elements.
+   using ElementType = Type;  //!< Type of the quaternion elements.
    //**********************************************************************************************
 
    //**Constructors********************************************************************************
@@ -659,7 +659,7 @@ template< typename Other>   // Data type of the vector
 inline const Vector3< typename MathTrait<Type,Other>::MultType >
    Quaternion<Type>::rotate( const Vector3<Other>& v ) const
 {
-   typedef typename MathTrait<Type,Other>::MultType  MT;
+   using MT = typename MathTrait<Type, Other>::MultType;
 
    // Multiplication in two steps
    const MT w( v_[1]*v[0] + v_[2]*v[1] + v_[3]*v[2] );
@@ -701,7 +701,7 @@ template< typename Other >  // Data type of the matrix
 inline const Matrix3< typename MathTrait<Type,Other>::MultType >
    Quaternion<Type>::rotate( const Matrix3<Other>& m ) const
 {
-   typedef typename MathTrait<Type,Other>::MultType  MT;
+   using MT = typename MathTrait<Type, Other>::MultType;
    const Matrix3<MT> R( this->toRotationMatrix() );
    return R.rotate( m );
 }
@@ -725,7 +725,7 @@ template< typename Other >  // Data type of the diagonal matrix
 inline const Matrix3< typename MathTrait<Type,Other>::MultType >
    Quaternion<Type>::diagRotate( const Matrix3<Other>& m ) const
 {
-   typedef typename MathTrait<Type,Other>::MultType  MT;
+   using MT = typename MathTrait<Type, Other>::MultType;
    const Matrix3<MT> R( this->toRotationMatrix() );
    return R.diagRotate( m );
 }
@@ -743,7 +743,7 @@ template< typename Other >  // Data type of the axis
 inline typename MathTrait<Type,Other>::HighType
    Quaternion<Type>::calcAngle( const Vector3<Other>& axis ) const
 {
-   typedef typename MathTrait<Type,Other>::HighType  High;
+   using High = typename MathTrait<Type, Other>::HighType;
 
    const Vector3<High> u( v_[1], v_[2], v_[3] );
    const High y  ( u.length() );
@@ -816,12 +816,10 @@ inline bool operator==( const Quaternion<T1>& lhs, const Quaternion<T2>& rhs )
 {
    // In order to compare the two quaternions, the data values of the lower-order data
    // type are converted to the higher-order data type within the equal function.
-   if( !equal( lhs[0], rhs[0] ) ||
-       !equal( lhs[1], rhs[1] ) ||
-       !equal( lhs[2], rhs[2] ) ||
-       !equal( lhs[2], rhs[2] ) )
-      return false;
-   else return true;
+   return equal( lhs[0], rhs[0] ) &&
+          equal( lhs[1], rhs[1] ) &&
+          equal( lhs[2], rhs[2] ) &&
+          equal( lhs[2], rhs[2] );
 }
 //*************************************************************************************************
 
@@ -911,9 +909,7 @@ std::istream& operator>>( std::istream& is, Quaternion<Type>& q )
 template< typename Type >  // Data type of the quaternion
 inline bool isnan( const Quaternion<Type>& q )
 {
-   if( isnan( q[0] ) || isnan( q[1] ) || isnan( q[2] ) || isnan( q[3] ) )
-      return true;
-   else return false;
+   return isnan( q[0] ) || isnan( q[1] ) || isnan( q[2] ) || isnan( q[3] );
 }
 //*************************************************************************************************
 
@@ -1061,7 +1057,7 @@ template< typename T1    // Data type of the left-hand side quaternion
 inline const Quaternion< typename MathTrait<T1,T2>::MultType >
    operator*( const Quaternion<T1>& lhs, const Quaternion<T2>& rhs )
 {
-   typedef typename MathTrait<T1,T2>::MultType  MT;
+   using MT = typename MathTrait<T1, T2>::MultType;
 
    const MT r( lhs[0]*rhs[0] - lhs[1]*rhs[1] - lhs[2]*rhs[2] - lhs[3]*rhs[3] );
    const MT i( lhs[0]*rhs[1] + lhs[1]*rhs[0] + lhs[2]*rhs[3] - lhs[3]*rhs[2] );
@@ -1141,7 +1137,7 @@ using math::Quaternion;
 template<typename T>
 struct VectorTrait< Quaternion<T> >
 {
-   typedef T OutputType;
+   using OutputType = T;
 
    static const uint_t F_SIZE =  4u;
    static T    get( const Quaternion<T> & v, uint_t f )       { return v[f]; }
diff --git a/src/core/math/SqrtTrait.h b/src/core/math/SqrtTrait.h
index 372eebae18895f87acfad618f28211ce73f12bc3..5712e65a07db9a7cdd816162f8b7774b2516bf2f 100644
--- a/src/core/math/SqrtTrait.h
+++ b/src/core/math/SqrtTrait.h
@@ -64,7 +64,7 @@ namespace math {
 template< typename T >
 struct SqrtTrait
 {
-   typedef double Type;  //!< Return type of std::sqrt for integral and double arguments.
+   using Type = double;  //!< Return type of std::sqrt for integral and double arguments.
 };
 //**********************************************************************************************************************
 
@@ -77,7 +77,7 @@ struct SqrtTrait
 template<>
 struct SqrtTrait<float>
 {
-   typedef float Type;  //!< Return type of std::sqrt for float arguments.
+   using Type = float;  //!< Return type of std::sqrt for float arguments.
 };
 /*! \endcond */
 //**********************************************************************************************************************
@@ -91,7 +91,7 @@ struct SqrtTrait<float>
 template<>
 struct SqrtTrait<long double>
 {
-   typedef long double Type;  //!< Return type of std::sqrt for long double arguments.
+   using Type = long double;  //!< Return type of std::sqrt for long double arguments.
 };
 /*! \endcond */
 //**********************************************************************************************************************
diff --git a/src/core/math/Uint.cpp b/src/core/math/Uint.cpp
index 38f8003e746a3534fcb312a408bbb51b5f0fe6fb..347f639b88802d1c2582f6985fe5520ec417e000 100644
--- a/src/core/math/Uint.cpp
+++ b/src/core/math/Uint.cpp
@@ -49,11 +49,5 @@ template<> uint_t uintMSBPosition< uint64_t >( uint64_t value ) { // for the doc
    return ( i != 0 ) ? (8 + msbLookupTable[i]) : msbLookupTable[value];
 }
 
-#ifndef WALBERLA_CXX_COMPILER_IS_MSVC
-
-const uint_t int_ld<1>::exp;
-
-#endif
-
 } // namespace math
 } // namespace walberla
diff --git a/src/core/math/Uint.h b/src/core/math/Uint.h
index 5a76bc8fa8f52b30c4e52efc16ccc3f8d25c3381..c5fc28d833f3d78cd360cd4f10d214277898943f 100644
--- a/src/core/math/Uint.h
+++ b/src/core/math/Uint.h
@@ -194,10 +194,10 @@ template<> inline uint_t uintMSBPosition< uint8_t >( uint8_t value ) {
 }
 
 template< uint_t size > struct uintFromBitWidth;
-template<> struct uintFromBitWidth<  8 > { typedef uint8_t  type; };
-template<> struct uintFromBitWidth< 16 > { typedef uint16_t type; };
-template<> struct uintFromBitWidth< 32 > { typedef uint32_t type; };
-template<> struct uintFromBitWidth< 64 > { typedef uint64_t type; };
+template<> struct uintFromBitWidth<  8 > { using type = uint8_t; };
+template<> struct uintFromBitWidth< 16 > { using type = uint16_t; };
+template<> struct uintFromBitWidth< 32 > { using type = uint32_t; };
+template<> struct uintFromBitWidth< 64 > { using type = uint64_t; };
 
 constexpr uint_t leastUnsignedIntegerBitWidth( uint_t width )
 {
@@ -218,12 +218,12 @@ constexpr uint_t leastUnsignedIntegerBitWidth( uint_t width )
 template< uint_t minSize >
 struct leastUnsignedInteger
 {
-   typedef typename uintFromBitWidth< leastUnsignedIntegerBitWidth( minSize ) >::type type;
+   using type = typename uintFromBitWidth<leastUnsignedIntegerBitWidth(minSize)>::type;
 };
 
 /// \cond internal
-static const uint_t UINT_BITS  = static_cast< uint_t >( std::numeric_limits< uint_t >::digits );
-static const uint_t UINT_BYTES = static_cast< uint_t >( std::numeric_limits< uint_t >::digits ) >> 3;
+static constexpr uint_t UINT_BITS  = static_cast< uint_t >( std::numeric_limits< uint_t >::digits );
+static constexpr uint_t UINT_BYTES = static_cast< uint_t >( std::numeric_limits< uint_t >::digits ) >> 3;
 
 static_assert( !(UINT_BITS & (UINT_BITS - 1)), "Type \"uint_t\" must consist of 2^x Bits!" ); // power of two
 
@@ -231,18 +231,17 @@ template< int N >
 struct int_ld
 {
    static_assert( N >= 1 && !(N & (N - 1)), "Calculating log_2(N) -> \"N\" must be a power of two!" );
-   static const uint_t exp = 1 + int_ld< (N >> 1) >::exp;
+   static constexpr uint_t exp = 1 + int_ld< (N >> 1) >::exp;
+   static_assert( exp > 0 );
 };
 
-template< int N > const uint_t int_ld<N>::exp;
-
 template<>
 struct int_ld<1>
 {
-   static const uint_t exp = 0;
+   static constexpr uint_t exp = 0;
 };
 
-static const uint_t UINT_BITS_LD = int_ld< std::numeric_limits< uint_t >::digits >::exp;
+static constexpr uint_t UINT_BITS_LD = int_ld< std::numeric_limits< uint_t >::digits >::exp;
 /// \endcond
 
 } // namespace math
diff --git a/src/core/math/Utility.h b/src/core/math/Utility.h
index b71ea90e07522396b798cef116fc6af8e76d5efb..87300282bca8c9f7b5949796407c95fea5bdabe6 100644
--- a/src/core/math/Utility.h
+++ b/src/core/math/Utility.h
@@ -346,7 +346,7 @@ inline bool equal_backend<long double>( long double a, long double b )
 template< typename T1, typename T2 >
 inline bool equal( T1 a, T2 b )
 {
-   typedef typename MathTrait<T1,T2>::High High;
+   using High = typename MathTrait<T1, T2>::High;
    return equal_backend<High>( a, b );
 }
 //**********************************************************************************************************************
diff --git a/src/core/math/Vector2.h b/src/core/math/Vector2.h
index b0decf8ec0a2953fc2a2c3ab3a9e8c619de99e60..00bfabe88bfbf785b2ed3789258358cfc2912a7e 100644
--- a/src/core/math/Vector2.h
+++ b/src/core/math/Vector2.h
@@ -94,10 +94,10 @@ private:
 
 public:
    //**Type definitions****************************************************************************
-   typedef typename SqrtTrait<Type>::Type Length;  //!< Vector length return type.
+   using Length = typename SqrtTrait<Type>::Type;  //!< Vector length return type.
                                                    /*!< Return type of the Vector2<Type>::length
                                                         function. */
-   typedef Type value_type;
+   using value_type = Type;
    //*******************************************************************************************************************
 
    //**Constructors*****************************************************************************************************
@@ -322,9 +322,7 @@ inline bool Vector2<Type>::operator==( Other rhs ) const
 {
    // In order to compare the vector and the scalar value, the data values of the lower-order
    // data type are converted to the higher-order data type within the equal function.
-   if( !equal( v_[0], rhs ) || !equal( v_[1], rhs ) )
-      return false;
-   else return true;
+   return equal( v_[0], rhs ) && equal( v_[1], rhs );
 }
 //**********************************************************************************************************************
 
@@ -342,9 +340,7 @@ inline bool Vector2<Type>::operator==( const Vector2<Other>& rhs ) const
 {
    // In order to compare the two vectors, the data values of the lower-order data
    // type are converted to the higher-order data type within the equal function.
-   if( !equal( v_[0], rhs.v_[0] ) || !equal( v_[1], rhs.v_[1] ) )
-      return false;
-   else return true;
+   return equal( v_[0], rhs.v_[0] ) && equal( v_[1], rhs.v_[1] );
 }
 //**********************************************************************************************************************
 
@@ -365,9 +361,7 @@ inline bool Vector2<Type>::operator!=( Other rhs ) const
 {
    // In order to compare the vector and the scalar value, the data values of the lower-order
    // data type are converted to the higher-order data type within the equal function.
-   if( !equal( v_[0], rhs ) || !equal( v_[1], rhs )  )
-      return true;
-   else return false;
+   return !(*this == rhs);
 }
 //**********************************************************************************************************************
 
@@ -385,9 +379,7 @@ inline bool Vector2<Type>::operator!=( const Vector2<Other>& rhs ) const
 {
    // In order to compare the two vectors, the data values of the lower-order data
    // type are converted to the higher-order data type within the equal function.
-   if( !equal( v_[0], rhs.v_[0] ) || !equal( v_[1], rhs.v_[1] ) )
-      return true;
-   else return false;
+   return !(*this == rhs);
 }
 //**********************************************************************************************************************
 
@@ -1457,10 +1449,7 @@ inline std::istream& operator>>( std::istream& is, Vector2<bool>& v )
 template< typename Type >
 inline bool isnan( const Vector2<Type>& v )
 {
-   if( walberla::math::isnan( v[0] ) || walberla::math::isnan( v[1] ) )
-      return true;
-   else
-      return false;
+   return walberla::math::isnan( v[0] ) || walberla::math::isnan( v[1] );
 }
 //**********************************************************************************************************************
 
@@ -1681,7 +1670,7 @@ namespace walberla {
    template<typename T>
    struct VectorTrait< Vector2<T> >
    {
-      typedef T OutputType;
+      using OutputType = T;
 
       static const uint_t F_SIZE =  2u;
       static   T  get( const Vector2<T> & v, uint_t f )       { return v[f]; }
diff --git a/src/core/math/Vector3.h b/src/core/math/Vector3.h
index 7bc9e87af1f534aa694f1c8302dd4b9124b15a2b..c47fd4a66986e94b6bd361ec2eda9bdadb9a98df 100644
--- a/src/core/math/Vector3.h
+++ b/src/core/math/Vector3.h
@@ -94,22 +94,22 @@ private:
 
 public:
    //**Type definitions****************************************************************************
-   typedef typename SqrtTrait<Type>::Type Length;  //!< Vector length return type.
+   using Length = typename SqrtTrait<Type>::Type;  //!< Vector length return type.
                                                    /*!< Return type of the Vector3<Type>::length
                                                         function. */
-   typedef Type value_type;
+   using value_type = Type;
    //*******************************************************************************************************************
 
    //**Constructors*****************************************************************************************************
-                              explicit inline Vector3() = default;
-                              explicit inline Vector3( Type init );
-   template< typename Other > explicit inline Vector3( Other init );
-                              explicit inline Vector3( Type x, Type y, Type z );
-                              explicit inline Vector3( const Type* init );
-                                       inline Vector3( const Vector3& v ) = default;
+                              explicit inline constexpr Vector3() = default;
+                              explicit inline constexpr Vector3( Type init );
+   template< typename Other > explicit inline constexpr Vector3( Other init );
+                              explicit inline constexpr Vector3( Type x, Type y, Type z );
+                              explicit inline constexpr Vector3( const Type* init );
+                                       inline constexpr Vector3( const Vector3& v ) = default;
 
    template< typename Other >
-   inline Vector3( const Vector3<Other>& v );
+   inline constexpr Vector3( const Vector3<Other>& v );
    //*******************************************************************************************************************
 
    //**Destructor*******************************************************************************************************
@@ -208,7 +208,7 @@ Vector3<T> & normalize( Vector3<T> & v );
 // \param init Initial value for all vector elements.
 */
 template< typename Type >
-inline Vector3<Type>::Vector3( Type init )
+inline constexpr Vector3<Type>::Vector3( Type init )
 {
    v_[0] = v_[1] = v_[2] = init;
 }
@@ -223,7 +223,7 @@ inline Vector3<Type>::Vector3( Type init )
 */
 template< typename Type >
 template< typename Other >
-inline Vector3<Type>::Vector3( Other init )
+inline constexpr Vector3<Type>::Vector3( Other init )
 {
    static_assert( std::is_arithmetic<Other>::value, "Vector3 only accepts arithmetic data types in Vector3( Other init )");
 
@@ -241,7 +241,7 @@ inline Vector3<Type>::Vector3( Other init )
 // \param z The initial value for the z-component.
 */
 template< typename Type >
-inline Vector3<Type>::Vector3( Type x, Type y, Type z )
+inline constexpr Vector3<Type>::Vector3( Type x, Type y, Type z )
 {
    v_[0] = x;
    v_[1] = y;
@@ -259,7 +259,7 @@ inline Vector3<Type>::Vector3( Type x, Type y, Type z )
 // The array is assumed to have at least three valid elements.
 */
 template< typename Type >
-inline Vector3<Type>::Vector3( const Type* init )
+inline constexpr Vector3<Type>::Vector3( const Type* init )
 {
    v_[0] = init[0];
    v_[1] = init[1];
@@ -276,7 +276,7 @@ inline Vector3<Type>::Vector3( const Type* init )
 */
 template< typename Type >
 template< typename Other >
-inline Vector3<Type>::Vector3( const Vector3<Other>& v )
+inline constexpr Vector3<Type>::Vector3( const Vector3<Other>& v )
 {
    v_[0] = numeric_cast<Type>( v.v_[0] );
    v_[1] = numeric_cast<Type>( v.v_[1] );
@@ -333,9 +333,7 @@ inline bool Vector3<Type>::operator==( Other rhs ) const
 {
    // In order to compare the vector and the scalar value, the data values of the lower-order
    // data type are converted to the higher-order data type within the equal function.
-   if( !equal( v_[0], rhs ) || !equal( v_[1], rhs ) || !equal( v_[2], rhs ) )
-      return false;
-   else return true;
+   return equal( v_[0], rhs ) && equal( v_[1], rhs ) && equal( v_[2], rhs );
 }
 //**********************************************************************************************************************
 
@@ -353,9 +351,7 @@ inline bool Vector3<Type>::operator==( const Vector3<Other>& rhs ) const
 {
    // In order to compare the two vectors, the data values of the lower-order data
    // type are converted to the higher-order data type within the equal function.
-   if( !equal( v_[0], rhs.v_[0] ) || !equal( v_[1], rhs.v_[1] ) || !equal( v_[2], rhs.v_[2] ) )
-      return false;
-   else return true;
+   return equal( v_[0], rhs.v_[0] ) && equal( v_[1], rhs.v_[1] ) && equal( v_[2], rhs.v_[2] );
 }
 //**********************************************************************************************************************
 
@@ -376,9 +372,7 @@ inline bool Vector3<Type>::operator!=( Other rhs ) const
 {
    // In order to compare the vector and the scalar value, the data values of the lower-order
    // data type are converted to the higher-order data type within the equal function.
-   if( !equal( v_[0], rhs ) || !equal( v_[1], rhs ) || !equal( v_[2], rhs ) )
-      return true;
-   else return false;
+   return !(*this == rhs);
 }
 //**********************************************************************************************************************
 
@@ -396,9 +390,7 @@ inline bool Vector3<Type>::operator!=( const Vector3<Other>& rhs ) const
 {
    // In order to compare the two vectors, the data values of the lower-order data
    // type are converted to the higher-order data type within the equal function.
-   if( !equal( v_[0], rhs.v_[0] ) || !equal( v_[1], rhs.v_[1] ) || !equal( v_[2], rhs.v_[2] ) )
-      return true;
-   else return false;
+   return !(*this == rhs);
 }
 //**********************************************************************************************************************
 
@@ -1934,7 +1926,7 @@ namespace walberla {
 template<typename T>
 struct VectorTrait< Vector3<T> >
 {
-   typedef T OutputType;
+   using OutputType = T;
 
    static const uint_t F_SIZE =  3u;
    static T    get( const Vector3<T> & v, uint_t f )       { return v[f]; }
diff --git a/src/core/math/equation_system/EquationSystem.h b/src/core/math/equation_system/EquationSystem.h
index e78df0345321a0e51a372941b0a96b46bfd88e29..82d17a0f79a552aa4d2b1cf13c575484909baf1e 100644
--- a/src/core/math/equation_system/EquationSystem.h
+++ b/src/core/math/equation_system/EquationSystem.h
@@ -67,16 +67,16 @@ namespace math {
       // forward declaration of EquationParser class
       friend class EquationParser;
 
-      typedef boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS> EqGraph;
+      using EqGraph = boost::adjacency_list<boost::vecS, boost::vecS, boost::undirectedS>;
 
-      typedef std::map<std::string, EqGraph::vertex_descriptor> EqVertexMap;
-      typedef std::map<std::string, EqGraph::vertex_descriptor> VarVertexMap;
+      using EqVertexMap = std::map<std::string, EqGraph::vertex_descriptor>;
+      using VarVertexMap = std::map<std::string, EqGraph::vertex_descriptor>;
 
-      typedef std::map<std::string, EqGraph::vertex_descriptor>::const_iterator EqVertexMapIt;
-      typedef std::map<std::string, EqGraph::vertex_descriptor>::const_iterator VarVertexMapIt;
+      using EqVertexMapIt = std::map<std::string, EqGraph::vertex_descriptor>::const_iterator;
+      using VarVertexMapIt = std::map<std::string, EqGraph::vertex_descriptor>::const_iterator;
 
-      typedef std::map<std::string, EquationPtr>                 EqMap;
-      typedef std::map<std::string, EquationPtr>::const_iterator EqMapIt;
+      using EqMap = std::map<std::string, EquationPtr>;
+      using EqMapIt = std::map<std::string, EquationPtr>::const_iterator;
 
       EqMap       eqMap_;
       EqGraph     eqGraph_;
@@ -121,7 +121,7 @@ namespace math {
 
    };
 
-   typedef shared_ptr< EquationSystem > EquationSystemPtr;
+   using EquationSystemPtr = shared_ptr<EquationSystem>;
 
 } // namespace math
 } // namespace walberla
diff --git a/src/core/math/equation_system/FwdEquation.h b/src/core/math/equation_system/FwdEquation.h
index 37787456b7eb1daa3ab131cb30112bd9804e483e..21fbb523cd241c52d51f60fb6fd3f13c0ca136cf 100644
--- a/src/core/math/equation_system/FwdEquation.h
+++ b/src/core/math/equation_system/FwdEquation.h
@@ -41,13 +41,13 @@ namespace math {
    };
 
    class Node;
-   typedef std::shared_ptr<Node> NodePtr;
+   using NodePtr = std::shared_ptr<Node>;
 
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    // EQUATION
    /////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
    class Equation;
-   typedef std::shared_ptr< Equation > EquationPtr;
+   using EquationPtr = std::shared_ptr<Equation>;
 
 } // namespace math
 } // namespace walberla
diff --git a/src/core/math/equation_system/FwdVariable.h b/src/core/math/equation_system/FwdVariable.h
index 76715f973efe5b97d47fe7762abc8747f14e760b..bb9e8edf550f014eb01aa904eee6aa1c697c9727 100644
--- a/src/core/math/equation_system/FwdVariable.h
+++ b/src/core/math/equation_system/FwdVariable.h
@@ -33,10 +33,10 @@ namespace math {
 
    class Var;
 
-   typedef std::shared_ptr<Var> VarPtr;
+   using VarPtr = std::shared_ptr<Var>;
 
-   typedef std::map<std::string, VarPtr>                 VarMap;
-   typedef std::map<std::string, VarPtr>::const_iterator VarMapIt;
+   using VarMap = std::map<std::string, VarPtr>;
+   using VarMapIt = std::map<std::string, VarPtr>::const_iterator;
 
 } // namespace math
 } // namespace walberla
diff --git a/src/core/math/equation_system/Operator.h b/src/core/math/equation_system/Operator.h
index b0dcd809ba495921df10d12b6068827162746045..e9a6a75e1ed50196b285212e1b5bed3ddfec4649 100644
--- a/src/core/math/equation_system/Operator.h
+++ b/src/core/math/equation_system/Operator.h
@@ -42,7 +42,7 @@ namespace math {
       OpType( const char& sign, const std::string& n, const unsigned int strength ) :
          sign_(sign), name_(n), strength_(strength) {}
 
-      virtual ~OpType() {}
+      virtual ~OpType() = default;
 
    private:
       OpType& operator=( const OpType& ){ return *this; }
@@ -68,56 +68,56 @@ namespace math {
    public:
       OpNo( const char& sign, const std::string& name, const unsigned int strength ) :
          OpType( sign, name, strength ) {}
-      double operator() ( const double &, const double & ) { WALBERLA_ABORT( "NO OPERATION" ); return 0; }
+      double operator() ( const double &, const double & ) override { WALBERLA_ABORT( "NO OPERATION" ); return 0; }
    };
 
    class OpPlus : public OpType{
    public:
       OpPlus( const char& sign, const std::string& name, const unsigned int strength ) :
          OpType( sign, name, strength ) {};
-      double operator() ( const double & a, const double & b ) { return a + b; }
+      double operator() ( const double & a, const double & b ) override { return a + b; }
    };
 
    class OpMinus : public OpType{
    public:
       OpMinus( const char& sign, const std::string& name, const unsigned int strength ) :
          OpType( sign, name, strength ) {}
-      double operator() ( const double & a, const double & b ) { return a - b; }
+      double operator() ( const double & a, const double & b ) override { return a - b; }
    };
 
    class OpMult : public OpType{
    public:
       OpMult( const char& sign, const std::string& name, const unsigned int strength ) :
          OpType( sign, name, strength ) {}
-      double operator() ( const double & a, const double & b ) { return a * b; }
+      double operator() ( const double & a, const double & b ) override { return a * b; }
    };
 
    class OpDiv : public OpType{
    public:
       OpDiv( const char& sign, const std::string& name, const unsigned int strength ) :
          OpType( sign, name, strength ) {}
-      double operator() ( const double & a, const double & b ) { return a / b; }
+      double operator() ( const double & a, const double & b ) override { return a / b; }
    };
 
    class OpProd : public OpType{
    public:
       OpProd( const char& sign, const std::string& name, const unsigned int strength ) :
          OpType( sign, name, strength ) {}
-      double operator() ( const double & a, const double & b ) { return pow( a, b ); }
+      double operator() ( const double & a, const double & b ) override { return pow( a, b ); }
    };
 
    class OpRoot : public OpType{
    public:
       OpRoot( const char& sign, const std::string& name, const unsigned int strength ) :
          OpType( sign, name, strength ) {}
-      double operator() ( const double & a, const double & b ) { return pow( a, 1/b ); }
+      double operator() ( const double & a, const double & b ) override { return pow( a, 1/b ); }
    };
 
    class OpLog : public OpType{
    public:
       OpLog( const char& sign, const std::string& name, const unsigned int strength ) :
          OpType( sign, name, strength ) {}
-      double operator() ( const double & a, const double & b ) { return log10(a) / log10(b); }
+      double operator() ( const double & a, const double & b ) override { return log10(a) / log10(b); }
    };
 
 
diff --git a/src/core/math/extern/exprtk.h b/src/core/math/extern/exprtk.h
index 4c88bbdf6b501ccd95726cab45b0000597e49e49..afa862bf11b3a28de2c44bd6145cd09842137f50 100644
--- a/src/core/math/extern/exprtk.h
+++ b/src/core/math/extern/exprtk.h
@@ -2,7 +2,7 @@
  ******************************************************************
  *           C++ Mathematical Expression Toolkit Library          *
  *                                                                *
- * Author: Arash Partow (1999-2019)                               *
+ * Author: Arash Partow (1999-2021)                               *
  * URL: http://www.partow.net/programming/exprtk/index.html       *
  *                                                                *
  * Copyright notice:                                              *
@@ -35,6 +35,7 @@
 
 
 #include <algorithm>
+#include <cassert>
 #include <cctype>
 #include <cmath>
 #include <complex>
@@ -83,12 +84,14 @@ namespace exprtk
 
    namespace details
    {
-      typedef unsigned char     uchar_t;
-      typedef char               char_t;
-      typedef uchar_t*        uchar_ptr;
-      typedef char_t*          char_ptr;
-      typedef uchar_t const* uchar_cptr;
-      typedef char_t const*   char_cptr;
+      typedef unsigned char            uchar_t;
+      typedef char                      char_t;
+      typedef uchar_t*               uchar_ptr;
+      typedef char_t*                 char_ptr;
+      typedef uchar_t const*        uchar_cptr;
+      typedef char_t const*          char_cptr;
+      typedef unsigned long long int _uint64_t;
+      typedef long long int           _int64_t;
 
       inline bool is_whitespace(const char_t c)
       {
@@ -162,6 +165,12 @@ namespace exprtk
                 ('\'' != c);
       }
 
+      inline bool is_valid_string_char(const char_t c)
+      {
+         return std::isprint(static_cast<unsigned char>(c)) ||
+                is_whitespace(c);
+      }
+
       #ifndef exprtk_disable_caseinsensitivity
       inline void case_normalise(std::string& s)
       {
@@ -308,31 +317,30 @@ namespace exprtk
       }
 
       template <typename Iterator>
-      inline void parse_hex(Iterator& itr, Iterator end, std::string::value_type& result)
+      inline bool parse_hex(Iterator& itr, Iterator end,
+                            std::string::value_type& result)
       {
          if (
-              (end !=  (itr    )) &&
-              (end !=  (itr + 1)) &&
-              (end !=  (itr + 2)) &&
-              (end !=  (itr + 3)) &&
-              ('0' == *(itr    )) &&
-              (
-                ('x' == *(itr + 1)) ||
-                ('X' == *(itr + 1))
-              ) &&
-              (is_hex_digit(*(itr + 2))) &&
-              (is_hex_digit(*(itr + 3)))
+              (end ==  (itr    ))               ||
+              (end ==  (itr + 1))               ||
+              (end ==  (itr + 2))               ||
+              (end ==  (itr + 3))               ||
+              ('0' != *(itr    ))               ||
+              ('X' != std::toupper(*(itr + 1))) ||
+              (!is_hex_digit(*(itr + 2)))       ||
+              (!is_hex_digit(*(itr + 3)))
             )
          {
-            result = hex_to_bin(static_cast<uchar_t>(*(itr + 2))) << 4 |
-                     hex_to_bin(static_cast<uchar_t>(*(itr + 3))) ;
-            itr += 3;
+            return false;
          }
-         else
-            result = '\0';
+
+         result = hex_to_bin(static_cast<uchar_t>(*(itr + 2))) << 4 |
+                  hex_to_bin(static_cast<uchar_t>(*(itr + 3))) ;
+
+         return true;
       }
 
-      inline void cleanup_escapes(std::string& s)
+      inline bool cleanup_escapes(std::string& s)
       {
          typedef std::string::iterator str_itr_t;
 
@@ -346,36 +354,41 @@ namespace exprtk
          {
             if ('\\' == (*itr1))
             {
-               ++removal_count;
-
                if (end == ++itr1)
-                  break;
-               else if ('\\' != (*itr1))
                {
-                  switch (*itr1)
-                  {
-                     case 'n' : (*itr1) = '\n'; break;
-                     case 'r' : (*itr1) = '\r'; break;
-                     case 't' : (*itr1) = '\t'; break;
-                     case '0' : parse_hex(itr1, end, (*itr1));
-                                removal_count += 3;
-                                break;
-                  }
-
-                  continue;
+                  return false;
                }
+               else if (parse_hex(itr1, end, *itr2))
+               {
+                  itr1+= 4;
+                  itr2+= 1;
+                  removal_count +=4;
+               }
+               else if ('a' == (*itr1)) { (*itr2++) = '\a'; ++itr1; ++removal_count; }
+               else if ('b' == (*itr1)) { (*itr2++) = '\b'; ++itr1; ++removal_count; }
+               else if ('f' == (*itr1)) { (*itr2++) = '\f'; ++itr1; ++removal_count; }
+               else if ('n' == (*itr1)) { (*itr2++) = '\n'; ++itr1; ++removal_count; }
+               else if ('r' == (*itr1)) { (*itr2++) = '\r'; ++itr1; ++removal_count; }
+               else if ('t' == (*itr1)) { (*itr2++) = '\t'; ++itr1; ++removal_count; }
+               else if ('v' == (*itr1)) { (*itr2++) = '\v'; ++itr1; ++removal_count; }
+               else if ('0' == (*itr1)) { (*itr2++) = '\0'; ++itr1; ++removal_count; }
+               else
+               {
+                  (*itr2++) = (*itr1++);
+                  ++removal_count;
+               }
+               continue;
             }
-
-            if (itr1 != itr2)
-            {
-               (*itr2) = (*itr1);
-            }
-
-            ++itr1;
-            ++itr2;
+            else
+               (*itr2++) = (*itr1++);
          }
 
+         if ((removal_count > s.size()) || (0 == removal_count))
+            return false;
+
          s.resize(s.size() - removal_count);
+
+         return true;
       }
 
       class build_string
@@ -643,23 +656,19 @@ namespace exprtk
       inline bool wc_match(const std::string& wild_card,
                            const std::string& str)
       {
-         return match_impl<char_cptr,cs_match>(wild_card.data(),
-                                               wild_card.data() + wild_card.size(),
-                                               str.data(),
-                                               str.data() + str.size(),
-                                               '*',
-                                               '?');
+         return match_impl<char_cptr,cs_match>(
+                   wild_card.data(), wild_card.data() + wild_card.size(),
+                   str.data(), str.data() + str.size(),
+                   '*', '?');
       }
 
       inline bool wc_imatch(const std::string& wild_card,
                             const std::string& str)
       {
-         return match_impl<char_cptr,cis_match>(wild_card.data(),
-                                                wild_card.data() + wild_card.size(),
-                                                str.data(),
-                                                str.data() + str.size(),
-                                                '*',
-                                                '?');
+         return match_impl<char_cptr,cis_match>(
+                   wild_card.data(), wild_card.data() + wild_card.size(),
+                   str.data(), str.data() + str.size(),
+                   '*', '?');
       }
 
       inline bool sequence_match(const std::string& pattern,
@@ -773,15 +782,15 @@ namespace exprtk
             };
 
             #define exprtk_register_real_type_tag(T)             \
-            template<> struct number_type<T>                     \
+            template <> struct number_type<T>                    \
             { typedef real_type_tag type; number_type() {} };    \
 
             #define exprtk_register_complex_type_tag(T)          \
-            template<> struct number_type<std::complex<T> >      \
+            template <> struct number_type<std::complex<T> >     \
             { typedef complex_type_tag type; number_type() {} }; \
 
             #define exprtk_register_int_type_tag(T)              \
-            template<> struct number_type<T>                     \
+            template <> struct number_type<T>                    \
             { typedef int_type_tag type; number_type() {} };     \
 
             exprtk_register_real_type_tag(double     )
@@ -792,12 +801,12 @@ namespace exprtk
             exprtk_register_complex_type_tag(long double)
             exprtk_register_complex_type_tag(float      )
 
-            exprtk_register_int_type_tag(short                 )
-            exprtk_register_int_type_tag(int                   )
-            exprtk_register_int_type_tag(long long int         )
-            exprtk_register_int_type_tag(unsigned short        )
-            exprtk_register_int_type_tag(unsigned int          )
-            exprtk_register_int_type_tag(unsigned long long int)
+            exprtk_register_int_type_tag(short         )
+            exprtk_register_int_type_tag(int           )
+            exprtk_register_int_type_tag(_int64_t      )
+            exprtk_register_int_type_tag(unsigned short)
+            exprtk_register_int_type_tag(unsigned int  )
+            exprtk_register_int_type_tag(_uint64_t     )
 
             #undef exprtk_register_real_type_tag
             #undef exprtk_register_int_type_tag
@@ -827,7 +836,7 @@ namespace exprtk
             {
                static inline long double value()
                {
-                  const long double epsilon = (long double)(0.000000000001);
+                  const long double epsilon = static_cast<long double>(0.000000000001);
                   return epsilon;
                }
             };
@@ -845,9 +854,9 @@ namespace exprtk
             }
 
             template <typename T>
-            inline long long int to_int64_impl(const T v, real_type_tag)
+            inline _int64_t to_int64_impl(const T v, real_type_tag)
             {
-               return static_cast<long long int>(v);
+               return static_cast<_int64_t>(v);
             }
 
             template <typename T>
@@ -1028,7 +1037,7 @@ namespace exprtk
             template <typename T>
             inline T roundn_impl(const T v0, const T v1, real_type_tag)
             {
-               const int index = std::max<int>(0, std::min<int>(pow10_size - 1, (int)std::floor(v1)));
+               const int index = std::max<int>(0, std::min<int>(pow10_size - 1, static_cast<int>(std::floor(v1))));
                const T p10 = T(pow10[index]);
 
                if (v0 < T(0))
@@ -1372,10 +1381,10 @@ namespace exprtk
          template <typename Type>
          struct numeric_info { enum { length = 0, size = 32, bound_length = 0, min_exp = 0, max_exp = 0 }; };
 
-         template<> struct numeric_info<int>         { enum { length = 10, size = 16, bound_length = 9}; };
-         template<> struct numeric_info<float>       { enum { min_exp =  -38, max_exp =  +38}; };
-         template<> struct numeric_info<double>      { enum { min_exp = -308, max_exp = +308}; };
-         template<> struct numeric_info<long double> { enum { min_exp = -308, max_exp = +308}; };
+         template <> struct numeric_info<int>         { enum { length = 10, size = 16, bound_length = 9}; };
+         template <> struct numeric_info<float>       { enum { min_exp =  -38, max_exp =  +38}; };
+         template <> struct numeric_info<double>      { enum { min_exp = -308, max_exp = +308}; };
+         template <> struct numeric_info<long double> { enum { min_exp = -308, max_exp = +308}; };
 
          template <typename T>
          inline int to_int32(const T v)
@@ -1385,7 +1394,7 @@ namespace exprtk
          }
 
          template <typename T>
-         inline long long int to_int64(const T v)
+         inline _int64_t to_int64(const T v)
          {
             const typename details::number_type<T>::type num_type;
             return to_int64_impl(v, num_type);
@@ -1827,6 +1836,13 @@ namespace exprtk
          return true;
       }
 
+      template <typename T>
+      inline bool valid_exponent(const int exponent, numeric::details::real_type_tag)
+      {
+         return (std::numeric_limits<T>::min_exponent10 <= exponent) &&
+                (exponent <= std::numeric_limits<T>::max_exponent10) ;
+      }
+
       template <typename Iterator, typename T>
       inline bool string_to_real(Iterator& itr_external, const Iterator end, T& t, numeric::details::real_type_tag)
       {
@@ -1867,12 +1883,12 @@ namespace exprtk
 
             while ((end != itr) && (zero == (*itr))) ++itr;
 
-            unsigned int digit;
-
             while (end != itr)
             {
                // Note: For 'physical' superscalar architectures it
                // is advised that the following loop be: 4xPD1 and 1xPD2
+               unsigned int digit;
+
                #ifdef exprtk_enable_superscalar
                parse_digit_1(d)
                parse_digit_1(d)
@@ -1892,11 +1908,12 @@ namespace exprtk
             if ('.' == (*itr))
             {
                const Iterator curr = ++itr;
-               unsigned int digit;
                T tmp_d = T(0);
 
                while (end != itr)
                {
+                  unsigned int digit;
+
                   #ifdef exprtk_enable_superscalar
                   parse_digit_1(tmp_d)
                   parse_digit_1(tmp_d)
@@ -1910,7 +1927,13 @@ namespace exprtk
                if (curr != itr)
                {
                   instate = true;
-                  d += compute_pow10(tmp_d,static_cast<int>(-std::distance(curr,itr)));
+
+                  const int exponent = static_cast<int>(-std::distance(curr, itr));
+
+                  if (!valid_exponent<T>(exponent, numeric::details::real_type_tag()))
+                     return false;
+
+                  d += compute_pow10(tmp_d, exponent);
                }
 
                #undef parse_digit_1
@@ -1981,6 +2004,8 @@ namespace exprtk
 
          if ((end != itr) || (!instate))
             return false;
+         else if (!valid_exponent<T>(exponent, numeric::details::real_type_tag()))
+            return false;
          else if (exponent)
             d = compute_pow10(d,exponent);
 
@@ -2019,6 +2044,50 @@ namespace exprtk
 
    } // namespace details
 
+   struct loop_runtime_check
+   {
+      enum loop_types
+      {
+         e_invalid           = 0,
+         e_for_loop          = 1,
+         e_while_loop        = 2,
+         e_repeat_until_loop = 4,
+         e_all_loops         = 7
+      };
+
+      enum violation_type
+      {
+          e_unknown         = 0,
+          e_iteration_count = 1,
+          e_timeout         = 2
+      };
+
+      loop_types loop_set;
+
+      loop_runtime_check()
+      : loop_set(e_invalid),
+        max_loop_iterations(0)
+      {}
+
+      details::_uint64_t max_loop_iterations;
+
+      struct violation_context
+      {
+         loop_types loop;
+         violation_type violation;
+         details::_uint64_t iteration_count;
+      };
+
+      virtual void handle_runtime_violation(const violation_context&)
+      {
+         throw std::runtime_error("ExprTk Loop run-time violation.");
+      }
+
+      virtual ~loop_runtime_check() {}
+   };
+
+   typedef loop_runtime_check* loop_runtime_check_ptr;
+
    namespace lexer
    {
       struct token
@@ -2199,7 +2268,7 @@ namespace exprtk
 
          typedef token token_t;
          typedef std::vector<token_t> token_list_t;
-         typedef std::vector<token_t>::iterator token_list_itr_t;
+         typedef token_list_t::iterator token_list_itr_t;
          typedef details::char_t char_t;
 
          generator()
@@ -2334,9 +2403,9 @@ namespace exprtk
             if (finished())
                return "";
             else if (token_list_.begin() != token_itr_)
-               return std::string(base_itr_ + (token_itr_ - 1)->position,s_end_);
+               return std::string(base_itr_ + (token_itr_ - 1)->position, s_end_);
             else
-               return std::string(base_itr_ + token_itr_->position,s_end_);
+               return std::string(base_itr_ + token_itr_->position, s_end_);
          }
 
       private:
@@ -2346,9 +2415,9 @@ namespace exprtk
             return (s_end_ == itr);
          }
 
+         #ifndef exprtk_disable_comments
          inline bool is_comment_start(details::char_cptr itr)
          {
-            #ifndef exprtk_disable_comments
             const char_t c0 = *(itr + 0);
             const char_t c1 = *(itr + 1);
 
@@ -2359,9 +2428,14 @@ namespace exprtk
                if (('/' == c0) && ('/' == c1)) return true;
                if (('/' == c0) && ('*' == c1)) return true;
             }
-            #endif
             return false;
          }
+         #else
+         inline bool is_comment_start(details::char_cptr)
+         {
+            return false;
+         }
+         #endif
 
          inline void skip_whitespace()
          {
@@ -2637,6 +2711,7 @@ namespace exprtk
                   {
                      t.set_error(token::e_err_number, initial_itr, s_itr_, base_itr_);
                      token_list_.push_back(t);
+
                      return;
                   }
 
@@ -2715,7 +2790,10 @@ namespace exprtk
             // $fdd(x,x,x) = at least 11 chars
             if (std::distance(s_itr_,s_end_) < 11)
             {
-               t.set_error(token::e_err_sfunc, initial_itr, s_itr_, base_itr_);
+               t.set_error(
+                  token::e_err_sfunc,
+                  initial_itr, std::min(initial_itr + 11, s_end_),
+                  base_itr_);
                token_list_.push_back(t);
 
                return;
@@ -2728,7 +2806,10 @@ namespace exprtk
                    (details::is_digit(*(s_itr_ + 3))))
                )
             {
-               t.set_error(token::e_err_sfunc, initial_itr, s_itr_, base_itr_);
+               t.set_error(
+                  token::e_err_sfunc,
+                  initial_itr, std::min(initial_itr + 4, s_end_),
+                  base_itr_);
                token_list_.push_back(t);
 
                return;
@@ -2752,6 +2833,7 @@ namespace exprtk
             {
                t.set_error(token::e_err_string, s_itr_, s_end_, base_itr_);
                token_list_.push_back(t);
+
                return;
             }
 
@@ -2762,7 +2844,14 @@ namespace exprtk
 
             while (!is_end(s_itr_))
             {
-               if (!escaped && ('\\' == *s_itr_))
+               if (!details::is_valid_string_char(*s_itr_))
+               {
+                  t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_);
+                  token_list_.push_back(t);
+
+                  return;
+               }
+               else if (!escaped && ('\\' == *s_itr_))
                {
                   escaped_found = true;
                   escaped = true;
@@ -2777,28 +2866,17 @@ namespace exprtk
                }
                else if (escaped)
                {
-                  if (!is_end(s_itr_) && ('0' == *(s_itr_)))
+                  if (
+                       !is_end(s_itr_) && ('0' == *(s_itr_)) &&
+                       ((s_itr_ + 4) <= s_end_)
+                     )
                   {
-                     /*
-                        Note: The following 'awkward' conditional is
-                              due to various broken msvc compilers.
-                     */
-                     #if defined(_MSC_VER) && (_MSC_VER == 1600)
-                     const bool within_range = !is_end(s_itr_ + 2) &&
-                                               !is_end(s_itr_ + 3) ;
-                     #else
-                     const bool within_range = !is_end(s_itr_ + 1) &&
-                                               !is_end(s_itr_ + 2) &&
-                                               !is_end(s_itr_ + 3) ;
-                     #endif
+                     const bool x_seperator = ('X' == std::toupper(*(s_itr_ + 1)));
 
-                     const bool x_seperator  = ('x' == *(s_itr_ + 1)) ||
-                                               ('X' == *(s_itr_ + 1)) ;
+                     const bool both_digits = details::is_hex_digit(*(s_itr_ + 2)) &&
+                                              details::is_hex_digit(*(s_itr_ + 3)) ;
 
-                     const bool both_digits  = details::is_hex_digit(*(s_itr_ + 2)) &&
-                                               details::is_hex_digit(*(s_itr_ + 3)) ;
-
-                     if (!within_range || !x_seperator || !both_digits)
+                     if (!(x_seperator && both_digits))
                      {
                         t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_);
                         token_list_.push_back(t);
@@ -2829,7 +2907,13 @@ namespace exprtk
             {
                std::string parsed_string(initial_itr,s_itr_);
 
-               details::cleanup_escapes(parsed_string);
+               if (!details::cleanup_escapes(parsed_string))
+               {
+                  t.set_error(token::e_err_string, initial_itr, s_itr_, base_itr_);
+                  token_list_.push_back(t);
+
+                  return;
+               }
 
                t.set_string(
                     parsed_string,
@@ -2845,10 +2929,10 @@ namespace exprtk
 
       private:
 
-         token_list_t     token_list_;
-         token_list_itr_t token_itr_;
-         token_list_itr_t store_token_itr_;
-         token_t eof_token_;
+         token_list_t       token_list_;
+         token_list_itr_t   token_itr_;
+         token_list_itr_t   store_token_itr_;
+         token_t            eof_token_;
          details::char_cptr base_itr_;
          details::char_cptr s_itr_;
          details::char_cptr s_end_;
@@ -3018,6 +3102,10 @@ namespace exprtk
 
             std::size_t changes = 0;
 
+            typedef std::pair<std::size_t, token> insert_t;
+            std::vector<insert_t> insert_list;
+            insert_list.reserve(10000);
+
             for (std::size_t i = 0; i < (g.token_list_.size() - stride_ + 1); ++i)
             {
                int insert_index = -1;
@@ -3041,17 +3129,36 @@ namespace exprtk
                            break;
                }
 
-               typedef std::iterator_traits<generator::token_list_t::iterator>::difference_type diff_t;
-
                if ((insert_index >= 0) && (insert_index <= (static_cast<int>(stride_) + 1)))
                {
-                  g.token_list_.insert(
-                     g.token_list_.begin() + static_cast<diff_t>(i + static_cast<std::size_t>(insert_index)), t);
-
+                  insert_list.push_back(insert_t(i, t));
                   changes++;
                }
             }
 
+            if (!insert_list.empty())
+            {
+               generator::token_list_t token_list;
+
+               std::size_t insert_index = 0;
+
+               for (std::size_t i = 0; i < g.token_list_.size(); ++i)
+               {
+                  token_list.push_back(g.token_list_[i]);
+
+                  if (
+                       (insert_index < insert_list.size()) &&
+                       (insert_list[insert_index].first == i)
+                     )
+                  {
+                     token_list.push_back(insert_list[insert_index].second);
+                     insert_index++;
+                  }
+               }
+
+               std::swap(g.token_list_,token_list);
+            }
+
             return changes;
          }
 
@@ -3086,7 +3193,7 @@ namespace exprtk
       {
       public:
 
-         token_joiner(const std::size_t& stride)
+         explicit token_joiner(const std::size_t& stride)
          : stride_(stride)
          {}
 
@@ -3110,59 +3217,82 @@ namespace exprtk
 
          inline std::size_t process_stride_2(generator& g)
          {
-            typedef std::iterator_traits<generator::token_list_t::iterator>::difference_type diff_t;
-
             if (g.token_list_.size() < 2)
                return 0;
 
             std::size_t changes = 0;
 
+            generator::token_list_t token_list;
+            token_list.reserve(10000);
+
             for (int i = 0;  i < static_cast<int>(g.token_list_.size() - 1); ++i)
             {
                token t;
 
-               while (join(g[i], g[i + 1], t))
+               for ( ; ; )
                {
-                  g.token_list_[i] = t;
+                  if (!join(g[i], g[i + 1], t))
+                  {
+                     token_list.push_back(g[i]);
+                     break;
+                  }
 
-                  g.token_list_.erase(g.token_list_.begin() + static_cast<diff_t>(i + 1));
+                  token_list.push_back(t);
 
                   ++changes;
 
-                  if (static_cast<std::size_t>(i + 1) >= g.token_list_.size())
+                  i+=2;
+
+                  if (static_cast<std::size_t>(i) >= g.token_list_.size())
                      break;
                }
             }
 
+            token_list.push_back(g.token_list_.back());
+
+            std::swap(token_list, g.token_list_);
+
             return changes;
          }
 
          inline std::size_t process_stride_3(generator& g)
          {
-            typedef std::iterator_traits<generator::token_list_t::iterator>::difference_type diff_t;
-
             if (g.token_list_.size() < 3)
                return 0;
 
             std::size_t changes = 0;
 
+            generator::token_list_t token_list;
+            token_list.reserve(10000);
+
             for (int i = 0;  i < static_cast<int>(g.token_list_.size() - 2); ++i)
             {
                token t;
 
-               while (join(g[i], g[i + 1], g[i + 2], t))
+               for ( ; ; )
                {
-                  g.token_list_[i] = t;
+                  if (!join(g[i], g[i + 1], g[i + 2], t))
+                  {
+                     token_list.push_back(g[i]);
+                     break;
+                  }
+
+                  token_list.push_back(t);
 
-                  g.token_list_.erase(g.token_list_.begin() + static_cast<diff_t>(i + 1),
-                                      g.token_list_.begin() + static_cast<diff_t>(i + 3));
                   ++changes;
 
-                  if (static_cast<std::size_t>(i + 2) >= g.token_list_.size())
+                  i+=3;
+
+                  if (static_cast<std::size_t>(i) >= g.token_list_.size())
                      break;
                }
             }
 
+            token_list.push_back(*(g.token_list_.begin() + g.token_list_.size() - 2));
+            token_list.push_back(*(g.token_list_.begin() + g.token_list_.size() - 1));
+
+            std::swap(token_list, g.token_list_);
+
             return changes;
          }
 
@@ -3172,11 +3302,11 @@ namespace exprtk
       namespace helper
       {
 
-         inline void dump(lexer::generator& generator)
+         inline void dump(const lexer::generator& generator)
          {
             for (std::size_t i = 0; i < generator.size(); ++i)
             {
-               lexer::token t = generator[i];
+               const lexer::token& t = generator[i];
                printf("Token[%02d] @ %03d  %6s  -->  '%s'\n",
                       static_cast<int>(i),
                       static_cast<int>(t.position),
@@ -3251,7 +3381,7 @@ namespace exprtk
          {
          public:
 
-            operator_joiner(const std::size_t& stride)
+            explicit operator_joiner(const std::size_t& stride)
             : token_joiner(stride)
             {}
 
@@ -3649,10 +3779,11 @@ namespace exprtk
             sequence_validator()
             : lexer::token_scanner(2)
             {
-               add_invalid(lexer::token::e_number ,lexer::token::e_number );
-               add_invalid(lexer::token::e_string ,lexer::token::e_string );
-               add_invalid(lexer::token::e_number ,lexer::token::e_string );
-               add_invalid(lexer::token::e_string ,lexer::token::e_number );
+               add_invalid(lexer::token::e_number, lexer::token::e_number);
+               add_invalid(lexer::token::e_string, lexer::token::e_string);
+               add_invalid(lexer::token::e_number, lexer::token::e_string);
+               add_invalid(lexer::token::e_string, lexer::token::e_number);
+
                add_invalid_set1(lexer::token::e_assign );
                add_invalid_set1(lexer::token::e_shr    );
                add_invalid_set1(lexer::token::e_shl    );
@@ -3726,21 +3857,21 @@ namespace exprtk
 
             void add_invalid_set1(lexer::token::token_type t)
             {
-               add_invalid(t,lexer::token::e_assign);
-               add_invalid(t,lexer::token::e_shr   );
-               add_invalid(t,lexer::token::e_shl   );
-               add_invalid(t,lexer::token::e_lte   );
-               add_invalid(t,lexer::token::e_ne    );
-               add_invalid(t,lexer::token::e_gte   );
-               add_invalid(t,lexer::token::e_lt    );
-               add_invalid(t,lexer::token::e_gt    );
-               add_invalid(t,lexer::token::e_eq    );
-               add_invalid(t,lexer::token::e_comma );
-               add_invalid(t,lexer::token::e_div   );
-               add_invalid(t,lexer::token::e_mul   );
-               add_invalid(t,lexer::token::e_mod   );
-               add_invalid(t,lexer::token::e_pow   );
-               add_invalid(t,lexer::token::e_colon );
+               add_invalid(t, lexer::token::e_assign);
+               add_invalid(t, lexer::token::e_shr   );
+               add_invalid(t, lexer::token::e_shl   );
+               add_invalid(t, lexer::token::e_lte   );
+               add_invalid(t, lexer::token::e_ne    );
+               add_invalid(t, lexer::token::e_gte   );
+               add_invalid(t, lexer::token::e_lt    );
+               add_invalid(t, lexer::token::e_gt    );
+               add_invalid(t, lexer::token::e_eq    );
+               add_invalid(t, lexer::token::e_comma );
+               add_invalid(t, lexer::token::e_div   );
+               add_invalid(t, lexer::token::e_mul   );
+               add_invalid(t, lexer::token::e_mod   );
+               add_invalid(t, lexer::token::e_pow   );
+               add_invalid(t, lexer::token::e_colon );
             }
 
             bool invalid_bracket_check(lexer::token::token_type base, lexer::token::token_type t)
@@ -3750,7 +3881,7 @@ namespace exprtk
                   switch (t)
                   {
                      case lexer::token::e_assign : return (']' != base);
-                     case lexer::token::e_string : return true;
+                     case lexer::token::e_string : return (')' != base);
                      default                     : return false;
                   }
                }
@@ -3821,23 +3952,23 @@ namespace exprtk
             sequence_validator_3tokens()
             : lexer::token_scanner(3)
             {
-               add_invalid(lexer::token::e_number , lexer::token::e_number , lexer::token::e_number);
-               add_invalid(lexer::token::e_string , lexer::token::e_string , lexer::token::e_string);
-               add_invalid(lexer::token::e_comma  , lexer::token::e_comma  , lexer::token::e_comma );
+               add_invalid(lexer::token::e_number, lexer::token::e_number, lexer::token::e_number);
+               add_invalid(lexer::token::e_string, lexer::token::e_string, lexer::token::e_string);
+               add_invalid(lexer::token::e_comma , lexer::token::e_comma , lexer::token::e_comma );
 
-               add_invalid(lexer::token::e_add ,lexer::token::e_add , lexer::token::e_add);
-               add_invalid(lexer::token::e_sub ,lexer::token::e_sub , lexer::token::e_sub);
-               add_invalid(lexer::token::e_div ,lexer::token::e_div , lexer::token::e_div);
-               add_invalid(lexer::token::e_mul ,lexer::token::e_mul , lexer::token::e_mul);
-               add_invalid(lexer::token::e_mod ,lexer::token::e_mod , lexer::token::e_mod);
-               add_invalid(lexer::token::e_pow ,lexer::token::e_pow , lexer::token::e_pow);
+               add_invalid(lexer::token::e_add   , lexer::token::e_add   , lexer::token::e_add   );
+               add_invalid(lexer::token::e_sub   , lexer::token::e_sub   , lexer::token::e_sub   );
+               add_invalid(lexer::token::e_div   , lexer::token::e_div   , lexer::token::e_div   );
+               add_invalid(lexer::token::e_mul   , lexer::token::e_mul   , lexer::token::e_mul   );
+               add_invalid(lexer::token::e_mod   , lexer::token::e_mod   , lexer::token::e_mod   );
+               add_invalid(lexer::token::e_pow   , lexer::token::e_pow   , lexer::token::e_pow   );
 
-               add_invalid(lexer::token::e_add ,lexer::token::e_sub , lexer::token::e_add);
-               add_invalid(lexer::token::e_sub ,lexer::token::e_add , lexer::token::e_sub);
-               add_invalid(lexer::token::e_div ,lexer::token::e_mul , lexer::token::e_div);
-               add_invalid(lexer::token::e_mul ,lexer::token::e_div , lexer::token::e_mul);
-               add_invalid(lexer::token::e_mod ,lexer::token::e_pow , lexer::token::e_mod);
-               add_invalid(lexer::token::e_pow ,lexer::token::e_mod , lexer::token::e_pow);
+               add_invalid(lexer::token::e_add   , lexer::token::e_sub   , lexer::token::e_add   );
+               add_invalid(lexer::token::e_sub   , lexer::token::e_add   , lexer::token::e_sub   );
+               add_invalid(lexer::token::e_div   , lexer::token::e_mul   , lexer::token::e_div   );
+               add_invalid(lexer::token::e_mul   , lexer::token::e_div   , lexer::token::e_mul   );
+               add_invalid(lexer::token::e_mod   , lexer::token::e_pow   , lexer::token::e_mod   );
+               add_invalid(lexer::token::e_pow   , lexer::token::e_mod   , lexer::token::e_pow   );
             }
 
             bool result()
@@ -4231,14 +4362,14 @@ namespace exprtk
    inline vector_view<T> make_vector_view(T* data,
                                           const std::size_t size, const std::size_t offset = 0)
    {
-      return vector_view<T>(data + offset,size);
+      return vector_view<T>(data + offset, size);
    }
 
    template <typename T>
    inline vector_view<T> make_vector_view(std::vector<T>& v,
                                           const std::size_t size, const std::size_t offset = 0)
    {
-      return vector_view<T>(v.data() + offset,size);
+      return vector_view<T>(v.data() + offset, size);
    }
 
    template <typename T> class results_context;
@@ -4273,7 +4404,7 @@ namespace exprtk
       {
       public:
 
-         parameter_list(std::vector<type_store>& pl)
+         explicit parameter_list(std::vector<type_store>& pl)
          : parameter_list_(pl)
          {}
 
@@ -4330,12 +4461,12 @@ namespace exprtk
          typedef type_store<T> type_store_t;
          typedef ViewType      value_t;
 
-         type_view(type_store_t& ts)
+         explicit type_view(type_store_t& ts)
          : ts_(ts),
            data_(reinterpret_cast<value_t*>(ts_.data))
          {}
 
-         type_view(const type_store_t& ts)
+         explicit type_view(const type_store_t& ts)
          : ts_(const_cast<type_store_t&>(ts)),
            data_(reinterpret_cast<value_t*>(ts_.data))
          {}
@@ -4380,11 +4511,11 @@ namespace exprtk
          typedef type_store<T> type_store_t;
          typedef T value_t;
 
-         scalar_view(type_store_t& ts)
+         explicit scalar_view(type_store_t& ts)
          : v_(*reinterpret_cast<value_t*>(ts.data))
          {}
 
-         scalar_view(const type_store_t& ts)
+         explicit scalar_view(const type_store_t& ts)
          : v_(*reinterpret_cast<value_t*>(const_cast<type_store_t&>(ts).data))
          {}
 
@@ -4572,27 +4703,33 @@ namespace exprtk
       {
          switch (opr)
          {
-            case e_add    : return  "+";
-            case e_sub    : return  "-";
-            case e_mul    : return  "*";
-            case e_div    : return  "/";
-            case e_mod    : return  "%";
-            case e_pow    : return  "^";
-            case e_assign : return ":=";
-            case e_addass : return "+=";
-            case e_subass : return "-=";
-            case e_mulass : return "*=";
-            case e_divass : return "/=";
-            case e_modass : return "%=";
-            case e_lt     : return  "<";
-            case e_lte    : return "<=";
-            case e_eq     : return "==";
-            case e_equal  : return  "=";
-            case e_ne     : return "!=";
-            case e_nequal : return "<>";
-            case e_gte    : return ">=";
-            case e_gt     : return  ">";
-            default       : return"N/A";
+            case e_add    : return  "+"  ;
+            case e_sub    : return  "-"  ;
+            case e_mul    : return  "*"  ;
+            case e_div    : return  "/"  ;
+            case e_mod    : return  "%"  ;
+            case e_pow    : return  "^"  ;
+            case e_assign : return ":="  ;
+            case e_addass : return "+="  ;
+            case e_subass : return "-="  ;
+            case e_mulass : return "*="  ;
+            case e_divass : return "/="  ;
+            case e_modass : return "%="  ;
+            case e_lt     : return  "<"  ;
+            case e_lte    : return "<="  ;
+            case e_eq     : return "=="  ;
+            case e_equal  : return  "="  ;
+            case e_ne     : return "!="  ;
+            case e_nequal : return "<>"  ;
+            case e_gte    : return ">="  ;
+            case e_gt     : return  ">"  ;
+            case e_and    : return "and" ;
+            case e_or     : return "or"  ;
+            case e_xor    : return "xor" ;
+            case e_nand   : return "nand";
+            case e_nor    : return "nor" ;
+            case e_xnor   : return "xnor";
+            default       : return "N/A" ;
          }
       }
 
@@ -4617,8 +4754,8 @@ namespace exprtk
 
          struct details
          {
-            details(const std::size_t& vsize,
-                    const unsigned int loop_batch_size = global_loop_batch_size)
+            explicit details(const std::size_t& vsize,
+                             const unsigned int loop_batch_size = global_loop_batch_size)
             : batch_size(loop_batch_size   ),
               remainder (vsize % batch_size),
               upper_bound(static_cast<int>(vsize - (remainder ? loop_batch_size : 0)))
@@ -4665,7 +4802,7 @@ namespace exprtk
               destruct (true)
             {}
 
-            control_block(const std::size_t& dsize)
+            explicit control_block(const std::size_t& dsize)
             : ref_count(1    ),
               size     (dsize),
               data     (0    ),
@@ -4732,7 +4869,7 @@ namespace exprtk
             {
                destruct = true;
                data     = new T[size];
-               std::fill_n(data,size,T(0));
+               std::fill_n(data, size, T(0));
                dump_ptr("control_block::create_data() - data",data,size);
             }
          };
@@ -4743,8 +4880,8 @@ namespace exprtk
          : control_block_(control_block::create(0))
          {}
 
-         vec_data_store(const std::size_t& size)
-         : control_block_(control_block::create(size,(data_t)(0),true))
+         explicit vec_data_store(const std::size_t& size)
+         : control_block_(control_block::create(size,reinterpret_cast<data_t>(0),true))
          {}
 
          vec_data_store(const std::size_t& size, data_t data, bool dstrct = false)
@@ -4829,7 +4966,7 @@ namespace exprtk
 
          static inline void match_sizes(type& vds0, type& vds1)
          {
-            std::size_t size = min_size(vds0.control_block_,vds1.control_block_);
+            const std::size_t size = min_size(vds0.control_block_,vds1.control_block_);
             vds0.control_block_->size = size;
             vds1.control_block_->size = size;
          }
@@ -4993,58 +5130,77 @@ namespace exprtk
          template <typename T>
          inline T process(const operator_type operation, const T arg0, const T arg1)
          {
-            return exprtk::details::numeric::details::process_impl(operation,arg0,arg1);
+            return exprtk::details::numeric::details::process_impl(operation, arg0, arg1);
          }
       }
 
+      template <typename Node>
+      struct node_collector_interface
+      {
+         typedef Node* node_ptr_t;
+         typedef Node** node_pp_t;
+         typedef std::vector<node_pp_t> noderef_list_t;
+
+         virtual ~node_collector_interface() {}
+
+         virtual void collect_nodes(noderef_list_t&) {}
+      };
+
+      template <typename Node>
+      struct node_depth_base;
+
       template <typename T>
-      class expression_node
+      class expression_node : public node_collector_interface<expression_node<T> >,
+                              public node_depth_base<expression_node<T> >
       {
       public:
 
          enum node_type
          {
-            e_none         , e_null         , e_constant     , e_unary        ,
-            e_binary       , e_binary_ext   , e_trinary      , e_quaternary   ,
-            e_vararg       , e_conditional  , e_while        , e_repeat       ,
-            e_for          , e_switch       , e_mswitch      , e_return       ,
-            e_retenv       , e_variable     , e_stringvar    , e_stringconst  ,
-            e_stringvarrng , e_cstringvarrng, e_strgenrange  , e_strconcat    ,
-            e_stringvarsize, e_strswap      , e_stringsize   , e_stringvararg ,
-            e_function     , e_vafunction   , e_genfunction  , e_strfunction  ,
-            e_strcondition , e_strccondition, e_add          , e_sub          ,
-            e_mul          , e_div          , e_mod          , e_pow          ,
-            e_lt           , e_lte          , e_gt           , e_gte          ,
-            e_eq           , e_ne           , e_and          , e_nand         ,
-            e_or           , e_nor          , e_xor          , e_xnor         ,
-            e_in           , e_like         , e_ilike        , e_inranges     ,
-            e_ipow         , e_ipowinv      , e_abs          , e_acos         ,
-            e_acosh        , e_asin         , e_asinh        , e_atan         ,
-            e_atanh        , e_ceil         , e_cos          , e_cosh         ,
-            e_exp          , e_expm1        , e_floor        , e_log          ,
-            e_log10        , e_log2         , e_log1p        , e_neg          ,
-            e_pos          , e_round        , e_sin          , e_sinc         ,
-            e_sinh         , e_sqrt         , e_tan          , e_tanh         ,
-            e_cot          , e_sec          , e_csc          , e_r2d          ,
-            e_d2r          , e_d2g          , e_g2d          , e_notl         ,
-            e_sgn          , e_erf          , e_erfc         , e_ncdf         ,
-            e_frac         , e_trunc        , e_uvouv        , e_vov          ,
-            e_cov          , e_voc          , e_vob          , e_bov          ,
-            e_cob          , e_boc          , e_vovov        , e_vovoc        ,
-            e_vocov        , e_covov        , e_covoc        , e_vovovov      ,
-            e_vovovoc      , e_vovocov      , e_vocovov      , e_covovov      ,
-            e_covocov      , e_vocovoc      , e_covovoc      , e_vococov      ,
-            e_sf3ext       , e_sf4ext       , e_nulleq       , e_strass       ,
-            e_vector       , e_vecelem      , e_rbvecelem    , e_rbveccelem   ,
-            e_vecdefass    , e_vecvalass    , e_vecvecass    , e_vecopvalass  ,
-            e_vecopvecass  , e_vecfunc      , e_vecvecswap   , e_vecvecineq   ,
-            e_vecvalineq   , e_valvecineq   , e_vecvecarith  , e_vecvalarith  ,
-            e_valvecarith  , e_vecunaryop   , e_break        , e_continue     ,
+            e_none          , e_null          , e_constant    , e_unary        ,
+            e_binary        , e_binary_ext    , e_trinary     , e_quaternary   ,
+            e_vararg        , e_conditional   , e_while       , e_repeat       ,
+            e_for           , e_switch        , e_mswitch     , e_return       ,
+            e_retenv        , e_variable      , e_stringvar   , e_stringconst  ,
+            e_stringvarrng  , e_cstringvarrng , e_strgenrange , e_strconcat    ,
+            e_stringvarsize , e_strswap       , e_stringsize  , e_stringvararg ,
+            e_function      , e_vafunction    , e_genfunction , e_strfunction  ,
+            e_strcondition  , e_strccondition , e_add         , e_sub          ,
+            e_mul           , e_div           , e_mod         , e_pow          ,
+            e_lt            , e_lte           , e_gt          , e_gte          ,
+            e_eq            , e_ne            , e_and         , e_nand         ,
+            e_or            , e_nor           , e_xor         , e_xnor         ,
+            e_in            , e_like          , e_ilike       , e_inranges     ,
+            e_ipow          , e_ipowinv       , e_abs         , e_acos         ,
+            e_acosh         , e_asin          , e_asinh       , e_atan         ,
+            e_atanh         , e_ceil          , e_cos         , e_cosh         ,
+            e_exp           , e_expm1         , e_floor       , e_log          ,
+            e_log10         , e_log2          , e_log1p       , e_neg          ,
+            e_pos           , e_round         , e_sin         , e_sinc         ,
+            e_sinh          , e_sqrt          , e_tan         , e_tanh         ,
+            e_cot           , e_sec           , e_csc         , e_r2d          ,
+            e_d2r           , e_d2g           , e_g2d         , e_notl         ,
+            e_sgn           , e_erf           , e_erfc        , e_ncdf         ,
+            e_frac          , e_trunc         , e_uvouv       , e_vov          ,
+            e_cov           , e_voc           , e_vob         , e_bov          ,
+            e_cob           , e_boc           , e_vovov       , e_vovoc        ,
+            e_vocov         , e_covov         , e_covoc       , e_vovovov      ,
+            e_vovovoc       , e_vovocov       , e_vocovov     , e_covovov      ,
+            e_covocov       , e_vocovoc       , e_covovoc     , e_vococov      ,
+            e_sf3ext        , e_sf4ext        , e_nulleq      , e_strass       ,
+            e_vector        , e_vecelem       , e_rbvecelem   , e_rbveccelem   ,
+            e_vecdefass     , e_vecvalass     , e_vecvecass   , e_vecopvalass  ,
+            e_vecopvecass   , e_vecfunc       , e_vecvecswap  , e_vecvecineq   ,
+            e_vecvalineq    , e_valvecineq    , e_vecvecarith , e_vecvalarith  ,
+            e_valvecarith   , e_vecunaryop    , e_break       , e_continue     ,
             e_swap
          };
 
          typedef T value_type;
          typedef expression_node<T>* expression_ptr;
+         typedef node_collector_interface<expression_node<T> > nci_t;
+         typedef typename nci_t::noderef_list_t noderef_list_t;
+         typedef node_depth_base<expression_node<T> > ndb_t;
 
          virtual ~expression_node()
          {}
@@ -5095,12 +5251,24 @@ namespace exprtk
          return std::not_equal_to<T>()(T(0),node->value());
       }
 
+      template <typename T>
+      inline bool is_true(const std::pair<expression_node<T>*,bool>& node)
+      {
+         return std::not_equal_to<T>()(T(0),node.first->value());
+      }
+
       template <typename T>
       inline bool is_false(const expression_node<T>* node)
       {
          return std::equal_to<T>()(T(0),node->value());
       }
 
+      template <typename T>
+      inline bool is_false(const std::pair<expression_node<T>*,bool>& node)
+      {
+         return std::equal_to<T>()(T(0),node.first->value());
+      }
+
       template <typename T>
       inline bool is_unary_node(const expression_node<T>* node)
       {
@@ -5243,7 +5411,8 @@ namespace exprtk
       template <typename T>
       inline bool branch_deletable(expression_node<T>* node)
       {
-         return !is_variable_node(node) &&
+         return (0 != node)             &&
+                !is_variable_node(node) &&
                 !is_string_node  (node) ;
       }
 
@@ -5301,6 +5470,76 @@ namespace exprtk
          return true;
       }
 
+      template <typename Node>
+      class node_collection_destructor
+      {
+      public:
+
+         typedef node_collector_interface<Node> nci_t;
+
+         typedef typename nci_t::node_ptr_t     node_ptr_t;
+         typedef typename nci_t::node_pp_t      node_pp_t;
+         typedef typename nci_t::noderef_list_t noderef_list_t;
+
+         static void delete_nodes(node_ptr_t& root)
+         {
+            std::vector<node_pp_t> node_delete_list;
+            node_delete_list.reserve(1000);
+
+            collect_nodes(root, node_delete_list);
+
+            for (std::size_t i = 0; i < node_delete_list.size(); ++i)
+            {
+               node_ptr_t& node = *node_delete_list[i];
+               exprtk_debug(("ncd::delete_nodes() - deleting: %p\n", static_cast<void*>(node)));
+               delete node;
+               node = reinterpret_cast<node_ptr_t>(0);
+            }
+         }
+
+      private:
+
+         static void collect_nodes(node_ptr_t& root, noderef_list_t& node_delete_list)
+         {
+            std::deque<node_ptr_t> node_list;
+            node_list.push_back(root);
+            node_delete_list.push_back(&root);
+
+            noderef_list_t child_node_delete_list;
+            child_node_delete_list.reserve(1000);
+
+            while (!node_list.empty())
+            {
+               node_list.front()->collect_nodes(child_node_delete_list);
+
+               if (!child_node_delete_list.empty())
+               {
+                  for (std::size_t i = 0; i < child_node_delete_list.size(); ++i)
+                  {
+                     node_pp_t& node = child_node_delete_list[i];
+
+                     if (0 == (*node))
+                     {
+                        exprtk_debug(("ncd::collect_nodes() - null node encountered.\n"));
+                     }
+
+                     node_list.push_back(*node);
+                  }
+
+                  node_delete_list.insert(
+                     node_delete_list.end(),
+                     child_node_delete_list.begin(), child_node_delete_list.end());
+
+                  child_node_delete_list.clear();
+               }
+
+               node_list.pop_front();
+            }
+
+            std::reverse(node_delete_list.begin(), node_delete_list.end());
+         }
+      };
+
       template <typename NodeAllocator, typename T, std::size_t N>
       inline void free_all_nodes(NodeAllocator& node_allocator, expression_node<T>* (&b)[N])
       {
@@ -5325,28 +5564,239 @@ namespace exprtk
       }
 
       template <typename NodeAllocator, typename T>
-      inline void free_node(NodeAllocator& node_allocator, expression_node<T>*& node, const bool force_delete = false)
+      inline void free_node(NodeAllocator&, expression_node<T>*& node)
       {
-         if (0 != node)
+         if ((0 == node) || is_variable_node(node) || is_string_node(node))
          {
-            if (
-                 (is_variable_node(node) || is_string_node(node)) ||
-                 force_delete
-               )
-               return;
-
-            node_allocator.free(node);
-            node = reinterpret_cast<expression_node<T>*>(0);
+            return;
          }
+
+         node_collection_destructor<expression_node<T> >
+            ::delete_nodes(node);
       }
 
       template <typename T>
       inline void destroy_node(expression_node<T>*& node)
       {
-         delete node;
-         node = reinterpret_cast<expression_node<T>*>(0);
+         if (0 != node)
+         {
+            node_collection_destructor<expression_node<T> >
+               ::delete_nodes(node);
+         }
       }
 
+      template <typename Node>
+      struct node_depth_base
+      {
+         node_depth_base()
+         : depth_set(false),
+           depth(0)
+         {}
+
+         virtual ~node_depth_base() {}
+
+         virtual std::size_t node_depth() const { return 1; }
+
+         std::size_t compute_node_depth(const Node* const& node) const
+         {
+            if (!depth_set)
+            {
+               depth = 1 + (node ? node->node_depth() : 0);
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         std::size_t compute_node_depth(const std::pair<Node*,bool>& branch) const
+         {
+            if (!depth_set)
+            {
+               depth = 1 + (branch.first ? branch.first->node_depth() : 0);
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         template <std::size_t N>
+         std::size_t compute_node_depth(const std::pair<Node*,bool> (&branch)[N]) const
+         {
+            if (!depth_set)
+            {
+               depth = 0;
+               for (std::size_t i = 0; i < N; ++i)
+               {
+                  if (branch[i].first)
+                  {
+                     depth = std::max(depth,branch[i].first->node_depth());
+                  }
+               }
+               depth += 1;
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         template <typename BranchType>
+         std::size_t compute_node_depth(const BranchType& n0, const BranchType& n1) const
+         {
+            if (!depth_set)
+            {
+               depth = 1 + std::max(compute_node_depth(n0), compute_node_depth(n1));
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         template <typename BranchType>
+         std::size_t compute_node_depth(const BranchType& n0, const BranchType& n1,
+                                        const BranchType& n2) const
+         {
+            if (!depth_set)
+            {
+               depth = 1 + std::max(
+                              std::max(compute_node_depth(n0), compute_node_depth(n1)),
+                              compute_node_depth(n2));
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         template <typename BranchType>
+         std::size_t compute_node_depth(const BranchType& n0, const BranchType& n1,
+                                        const BranchType& n2, const BranchType& n3) const
+         {
+            if (!depth_set)
+            {
+               depth = 1 + std::max(
+                           std::max(compute_node_depth(n0), compute_node_depth(n1)),
+                           std::max(compute_node_depth(n2), compute_node_depth(n3)));
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         std::size_t compute_node_depth(const Sequence<Node*, Allocator>& branch_list) const
+         {
+            if (!depth_set)
+            {
+               for (std::size_t i = 0; i < branch_list.size(); ++i)
+               {
+                  if (branch_list[i])
+                  {
+                     depth = std::max(depth, compute_node_depth(branch_list[i]));
+                  }
+               }
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence>
+         std::size_t compute_node_depth(const Sequence<std::pair<Node*,bool>,Allocator>& branch_list) const
+         {
+            if (!depth_set)
+            {
+               for (std::size_t i = 0; i < branch_list.size(); ++i)
+               {
+                  if (branch_list[i].first)
+                  {
+                     depth = std::max(depth, compute_node_depth(branch_list[i].first));
+                  }
+               }
+               depth_set = true;
+            }
+
+            return depth;
+         }
+
+         mutable bool depth_set;
+         mutable std::size_t depth;
+
+         template <typename NodeSequence>
+         void collect(Node*const& node,
+                      const bool deletable,
+                      NodeSequence& delete_node_list) const
+         {
+            if ((0 != node) && deletable)
+            {
+               delete_node_list.push_back(const_cast<Node**>(&node));
+            }
+         }
+
+         template <typename NodeSequence>
+         void collect(const std::pair<Node*, bool>& branch,
+                      NodeSequence& delete_node_list) const
+         {
+            collect(branch.first, branch.second, delete_node_list);
+         }
+
+         template <typename NodeSequence>
+         void collect(Node*& node,
+                      NodeSequence& delete_node_list) const
+         {
+            collect(node, branch_deletable(node), delete_node_list);
+         }
+
+         template <std::size_t N, typename NodeSequence>
+         void collect(const std::pair<Node*, bool>(&branch)[N],
+                      NodeSequence& delete_node_list) const
+         {
+            for (std::size_t i = 0; i < N; ++i)
+            {
+               collect(branch[i].first, branch[i].second, delete_node_list);
+            }
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence,
+                   typename NodeSequence>
+         void collect(const Sequence<std::pair<Node*, bool>, Allocator>& branch,
+                      NodeSequence& delete_node_list) const
+         {
+            for (std::size_t i = 0; i < branch.size(); ++i)
+            {
+               collect(branch[i].first, branch[i].second, delete_node_list);
+            }
+         }
+
+         template <typename Allocator,
+                   template <typename, typename> class Sequence,
+                   typename NodeSequence>
+         void collect(const Sequence<Node*, Allocator>& branch_list,
+                      NodeSequence& delete_node_list) const
+         {
+            for (std::size_t i = 0; i < branch_list.size(); ++i)
+            {
+               collect(branch_list[i], branch_deletable(branch_list[i]), delete_node_list);
+            }
+         }
+
+         template <typename Boolean,
+                   typename AllocatorT,
+                   typename AllocatorB,
+                   template <typename, typename> class Sequence,
+                   typename NodeSequence>
+         void collect(const Sequence<Node*, AllocatorT>& branch_list,
+                      const Sequence<Boolean, AllocatorB>& branch_deletable_list,
+                      NodeSequence& delete_node_list) const
+         {
+            for (std::size_t i = 0; i < branch_list.size(); ++i)
+            {
+               collect(branch_list[i], branch_deletable_list[i], delete_node_list);
+            }
+         }
+      };
+
       template <typename Type>
       class vector_holder
       {
@@ -5560,30 +6010,70 @@ namespace exprtk
          }
       };
 
+      template <typename T, std::size_t N>
+      inline void construct_branch_pair(std::pair<expression_node<T>*,bool> (&branch)[N],
+                                        expression_node<T>* b,
+                                        const std::size_t& index)
+      {
+         if (b && (index < N))
+         {
+            branch[index] = std::make_pair(b,branch_deletable(b));
+         }
+      }
+
+      template <typename T>
+      inline void construct_branch_pair(std::pair<expression_node<T>*,bool>& branch, expression_node<T>* b)
+      {
+         if (b)
+         {
+            branch = std::make_pair(b,branch_deletable(b));
+         }
+      }
+
+      template <std::size_t N, typename T>
+      inline void init_branches(std::pair<expression_node<T>*,bool> (&branch)[N],
+                                expression_node<T>* b0,
+                                expression_node<T>* b1 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b2 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b3 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b4 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b5 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b6 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b7 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b8 = reinterpret_cast<expression_node<T>*>(0),
+                                expression_node<T>* b9 = reinterpret_cast<expression_node<T>*>(0))
+      {
+         construct_branch_pair(branch, b0, 0);
+         construct_branch_pair(branch, b1, 1);
+         construct_branch_pair(branch, b2, 2);
+         construct_branch_pair(branch, b3, 3);
+         construct_branch_pair(branch, b4, 4);
+         construct_branch_pair(branch, b5, 5);
+         construct_branch_pair(branch, b6, 6);
+         construct_branch_pair(branch, b7, 7);
+         construct_branch_pair(branch, b8, 8);
+         construct_branch_pair(branch, b9, 9);
+      }
+
       template <typename T>
       class null_eq_node : public expression_node<T>
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-         null_eq_node(expression_ptr brnch, const bool equality = true)
-         : branch_(brnch),
-           branch_deletable_(branch_deletable(branch_)),
-           equality_(equality)
-         {}
-
-        ~null_eq_node()
+         explicit null_eq_node(expression_ptr branch, const bool equality = true)
+         : equality_(equality)
          {
-            if (branch_ && branch_deletable_)
-            {
-               destroy_node(branch_);
-            }
+            construct_branch_pair(branch_, branch);
          }
 
          inline T value() const
          {
-            const T v = branch_->value();
+            assert(branch_.first);
+
+            const T v = branch_.first->value();
             const bool result = details::numeric::is_nan(v);
 
             if (result)
@@ -5604,14 +6094,23 @@ namespace exprtk
 
          inline expression_node<T>* branch(const std::size_t&) const
          {
-            return branch_;
+            return branch_.first;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(branch_,node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
 
-         expression_ptr branch_;
-         const bool branch_deletable_;
          bool equality_;
+         branch_t branch_;
       };
 
       template <typename T>
@@ -5759,25 +6258,19 @@ namespace exprtk
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-         unary_node(const operator_type& opr,
-                    expression_ptr brnch)
-         : operation_(opr),
-           branch_(brnch),
-           branch_deletable_(branch_deletable(branch_))
-         {}
-
-        ~unary_node()
+         unary_node(const operator_type& opr, expression_ptr branch)
+         : operation_(opr)
          {
-            if (branch_ && branch_deletable_)
-            {
-               destroy_node(branch_);
-            }
+            construct_branch_pair(branch_,branch);
          }
 
          inline T value() const
          {
-            const T arg = branch_->value();
+            assert(branch_.first);
+
+            const T arg = branch_.first->value();
 
             return numeric::process<T>(operation_,arg);
          }
@@ -5794,94 +6287,28 @@ namespace exprtk
 
          inline expression_node<T>* branch(const std::size_t&) const
          {
-            return branch_;
+            return branch_.first;
          }
 
          inline void release()
          {
-            branch_deletable_ = false;
+            branch_.second = false;
          }
 
-      protected:
-
-         operator_type operation_;
-         expression_ptr branch_;
-         bool branch_deletable_;
-      };
-
-      template <typename T, std::size_t D, bool B>
-      struct construct_branch_pair
-      {
-         template <std::size_t N>
-         static inline void process(std::pair<expression_node<T>*,bool> (&)[N], expression_node<T>*)
-         {}
-      };
-
-      template <typename T, std::size_t D>
-      struct construct_branch_pair<T,D,true>
-      {
-         template <std::size_t N>
-         static inline void process(std::pair<expression_node<T>*,bool> (&branch)[N], expression_node<T>* b)
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
          {
-            if (b)
-            {
-               branch[D] = std::make_pair(b,branch_deletable(b));
-            }
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
          }
-      };
 
-      template <std::size_t N, typename T>
-      inline void init_branches(std::pair<expression_node<T>*,bool> (&branch)[N],
-                                expression_node<T>* b0,
-                                expression_node<T>* b1 = reinterpret_cast<expression_node<T>*>(0),
-                                expression_node<T>* b2 = reinterpret_cast<expression_node<T>*>(0),
-                                expression_node<T>* b3 = reinterpret_cast<expression_node<T>*>(0),
-                                expression_node<T>* b4 = reinterpret_cast<expression_node<T>*>(0),
-                                expression_node<T>* b5 = reinterpret_cast<expression_node<T>*>(0),
-                                expression_node<T>* b6 = reinterpret_cast<expression_node<T>*>(0),
-                                expression_node<T>* b7 = reinterpret_cast<expression_node<T>*>(0),
-                                expression_node<T>* b8 = reinterpret_cast<expression_node<T>*>(0),
-                                expression_node<T>* b9 = reinterpret_cast<expression_node<T>*>(0))
-      {
-         construct_branch_pair<T,0,(N > 0)>::process(branch,b0);
-         construct_branch_pair<T,1,(N > 1)>::process(branch,b1);
-         construct_branch_pair<T,2,(N > 2)>::process(branch,b2);
-         construct_branch_pair<T,3,(N > 3)>::process(branch,b3);
-         construct_branch_pair<T,4,(N > 4)>::process(branch,b4);
-         construct_branch_pair<T,5,(N > 5)>::process(branch,b5);
-         construct_branch_pair<T,6,(N > 6)>::process(branch,b6);
-         construct_branch_pair<T,7,(N > 7)>::process(branch,b7);
-         construct_branch_pair<T,8,(N > 8)>::process(branch,b8);
-         construct_branch_pair<T,9,(N > 9)>::process(branch,b9);
-      }
-
-      struct cleanup_branches
-      {
-         template <typename T, std::size_t N>
-         static inline void execute(std::pair<expression_node<T>*,bool> (&branch)[N])
+         std::size_t node_depth() const
          {
-            for (std::size_t i = 0; i < N; ++i)
-            {
-               if (branch[i].first && branch[i].second)
-               {
-                  destroy_node(branch[i].first);
-               }
-            }
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
-         template <typename T,
-                   typename Allocator,
-                   template <typename, typename> class Sequence>
-         static inline void execute(Sequence<std::pair<expression_node<T>*,bool>,Allocator>& branch)
-         {
-            for (std::size_t i = 0; i < branch.size(); ++i)
-            {
-               if (branch[i].first && branch[i].second)
-               {
-                  destroy_node(branch[i].first);
-               }
-            }
-         }
+      protected:
+
+         operator_type operation_;
+         branch_t branch_;
       };
 
       template <typename T>
@@ -5900,13 +6327,11 @@ namespace exprtk
             init_branches<2>(branch_, branch0, branch1);
          }
 
-        ~binary_node()
-         {
-            cleanup_branches::execute<T,2>(branch_);
-         }
-
          inline T value() const
          {
+            assert(branch_[0].first);
+            assert(branch_[1].first);
+
             const T arg0 = branch_[0].first->value();
             const T arg1 = branch_[1].first->value();
 
@@ -5933,6 +6358,16 @@ namespace exprtk
                return reinterpret_cast<expression_ptr>(0);
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::template compute_node_depth<2>(branch_);
+         }
+
       protected:
 
          operator_type operation_;
@@ -5952,13 +6387,11 @@ namespace exprtk
             init_branches<2>(branch_, branch0, branch1);
          }
 
-        ~binary_ext_node()
-         {
-            cleanup_branches::execute<T,2>(branch_);
-         }
-
          inline T value() const
          {
+            assert(branch_[0].first);
+            assert(branch_[1].first);
+
             const T arg0 = branch_[0].first->value();
             const T arg1 = branch_[1].first->value();
 
@@ -5985,6 +6418,16 @@ namespace exprtk
                return reinterpret_cast<expression_ptr>(0);
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::template compute_node_depth<2>(branch_);
+         }
+
       protected:
 
          branch_t branch_[2];
@@ -6007,13 +6450,12 @@ namespace exprtk
             init_branches<3>(branch_, branch0, branch1, branch2);
          }
 
-        ~trinary_node()
-         {
-            cleanup_branches::execute<T,3>(branch_);
-         }
-
          inline T value() const
          {
+            assert(branch_[0].first);
+            assert(branch_[1].first);
+            assert(branch_[2].first);
+
             const T arg0 = branch_[0].first->value();
             const T arg1 = branch_[1].first->value();
             const T arg2 = branch_[2].first->value();
@@ -6039,6 +6481,16 @@ namespace exprtk
             return expression_node<T>::e_trinary;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::template compute_node_depth<3>(branch_);
+         }
+
       protected:
 
          operator_type operation_;
@@ -6063,11 +6515,6 @@ namespace exprtk
             init_branches<4>(branch_, branch0, branch1, branch2, branch3);
          }
 
-        ~quaternary_node()
-         {
-            cleanup_branches::execute<T,4>(branch_);
-         }
-
          inline T value() const
          {
             return std::numeric_limits<T>::quiet_NaN();
@@ -6078,6 +6525,16 @@ namespace exprtk
             return expression_node<T>::e_quaternary;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::template compute_node_depth<4>(branch_);
+         }
+
       protected:
 
          operator_type operation_;
@@ -6090,42 +6547,27 @@ namespace exprtk
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-         conditional_node(expression_ptr test,
+         conditional_node(expression_ptr condition,
                           expression_ptr consequent,
                           expression_ptr alternative)
-         : test_(test),
-           consequent_(consequent),
-           alternative_(alternative),
-           test_deletable_(branch_deletable(test_)),
-           consequent_deletable_(branch_deletable(consequent_)),
-           alternative_deletable_(branch_deletable(alternative_))
-         {}
-
-        ~conditional_node()
          {
-            if (test_ && test_deletable_)
-            {
-               destroy_node(test_);
-            }
-
-            if (consequent_ && consequent_deletable_ )
-            {
-               destroy_node(consequent_);
-            }
-
-            if (alternative_ && alternative_deletable_)
-            {
-               destroy_node(alternative_);
-            }
+            construct_branch_pair(condition_  , condition  );
+            construct_branch_pair(consequent_ , consequent );
+            construct_branch_pair(alternative_, alternative);
          }
 
          inline T value() const
          {
-            if (is_true(test_))
-               return consequent_->value();
+            assert(condition_  .first);
+            assert(consequent_ .first);
+            assert(alternative_.first);
+
+            if (is_true(condition_))
+               return consequent_.first->value();
             else
-               return alternative_->value();
+               return alternative_.first->value();
          }
 
          inline typename expression_node<T>::node_type type() const
@@ -6133,14 +6575,24 @@ namespace exprtk
             return expression_node<T>::e_conditional;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(condition_  , node_delete_list);
+            expression_node<T>::ndb_t::collect(consequent_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(alternative_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth
+               (condition_, consequent_, alternative_);
+         }
+
       private:
 
-         expression_ptr test_;
-         expression_ptr consequent_;
-         expression_ptr alternative_;
-         const bool test_deletable_;
-         const bool consequent_deletable_;
-         const bool alternative_deletable_;
+         branch_t condition_;
+         branch_t consequent_;
+         branch_t alternative_;
       };
 
       template <typename T>
@@ -6150,32 +6602,22 @@ namespace exprtk
 
          // Consequent only conditional statement node
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-         cons_conditional_node(expression_ptr test,
+         cons_conditional_node(expression_ptr condition,
                                expression_ptr consequent)
-         : test_(test),
-           consequent_(consequent),
-           test_deletable_(branch_deletable(test_)),
-           consequent_deletable_(branch_deletable(consequent_))
-         {}
-
-        ~cons_conditional_node()
          {
-            if (test_ && test_deletable_)
-            {
-               destroy_node(test_);
-            }
-
-            if (consequent_ && consequent_deletable_)
-            {
-               destroy_node(consequent_);
-            }
+            construct_branch_pair(condition_ , condition );
+            construct_branch_pair(consequent_, consequent);
          }
 
          inline T value() const
          {
-            if (is_true(test_))
-               return consequent_->value();
+            assert(condition_ .first);
+            assert(consequent_.first);
+
+            if (is_true(condition_))
+               return consequent_.first->value();
             else
                return std::numeric_limits<T>::quiet_NaN();
          }
@@ -6185,12 +6627,22 @@ namespace exprtk
             return expression_node<T>::e_conditional;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(condition_ , node_delete_list);
+            expression_node<T>::ndb_t::collect(consequent_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::
+               compute_node_depth(condition_, consequent_);
+         }
+
       private:
 
-         expression_ptr test_;
-         expression_ptr consequent_;
-         const bool test_deletable_;
-         const bool consequent_deletable_;
+         branch_t condition_;
+         branch_t consequent_;
       };
 
       #ifndef exprtk_disable_break_continue
@@ -6199,7 +6651,7 @@ namespace exprtk
       {
       public:
 
-         break_exception(const T& v)
+         explicit break_exception(const T& v)
          : value(v)
          {}
 
@@ -6215,23 +6667,16 @@ namespace exprtk
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
          break_node(expression_ptr ret = expression_ptr(0))
-         : return_(ret),
-           return_deletable_(branch_deletable(return_))
-         {}
-
-        ~break_node()
          {
-            if (return_deletable_)
-            {
-               destroy_node(return_);
-            }
+            construct_branch_pair(return_, ret);
          }
 
          inline T value() const
          {
-            throw break_exception<T>(return_ ? return_->value() : std::numeric_limits<T>::quiet_NaN());
+            throw break_exception<T>(return_.first ? return_.first->value() : std::numeric_limits<T>::quiet_NaN());
             #ifndef _MSC_VER
             return std::numeric_limits<T>::quiet_NaN();
             #endif
@@ -6242,10 +6687,19 @@ namespace exprtk
             return expression_node<T>::e_break;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(return_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(return_);
+         }
+
       private:
 
-         expression_ptr return_;
-         const bool return_deletable_;
+         branch_t return_;
       };
 
       template <typename T>
@@ -6268,40 +6722,90 @@ namespace exprtk
       };
       #endif
 
-      template <typename T>
-      class while_loop_node : public expression_node<T>
+      #ifdef exprtk_enable_runtime_checks
+      struct loop_runtime_checker
       {
-      public:
-
-         typedef expression_node<T>* expression_ptr;
-
-         while_loop_node(expression_ptr condition, expression_ptr loop_body)
-         : condition_(condition),
-           loop_body_(loop_body),
-           condition_deletable_(branch_deletable(condition_)),
-           loop_body_deletable_(branch_deletable(loop_body_))
+         loop_runtime_checker(loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0),
+                              loop_runtime_check::loop_types lp_typ = loop_runtime_check::e_invalid)
+         : iteration_count_(0),
+           loop_runtime_check_(loop_rt_chk),
+           loop_type(lp_typ)
          {}
 
-        ~while_loop_node()
+         inline void reset(const _uint64_t initial_value = 0) const
          {
-            if (condition_ && condition_deletable_)
-            {
-               destroy_node(condition_);
-            }
+            iteration_count_ = initial_value;
+         }
 
-            if (loop_body_ && loop_body_deletable_)
+         inline bool check() const
+         {
+            if (
+                 (0 == loop_runtime_check_) ||
+                 (++iteration_count_ <= loop_runtime_check_->max_loop_iterations)
+               )
             {
-               destroy_node(loop_body_);
+               return true;
             }
+
+            loop_runtime_check::violation_context ctxt;
+            ctxt.loop      = loop_type;
+            ctxt.violation = loop_runtime_check::e_iteration_count;
+
+            loop_runtime_check_->handle_runtime_violation(ctxt);
+
+            return false;
+         }
+
+         mutable _uint64_t iteration_count_;
+         mutable loop_runtime_check_ptr loop_runtime_check_;
+         loop_runtime_check::loop_types loop_type;
+      };
+      #else
+      struct loop_runtime_checker
+      {
+         loop_runtime_checker(loop_runtime_check_ptr, loop_runtime_check::loop_types)
+         {}
+
+         inline void reset(const _uint64_t = 0) const
+         {}
+
+         inline bool check() const
+         {
+            return true;
+         }
+      };
+      #endif
+
+      template <typename T>
+      class while_loop_node : public expression_node<T>,
+                              public loop_runtime_checker
+      {
+      public:
+
+         typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
+
+         while_loop_node(expression_ptr condition,
+                         expression_ptr loop_body,
+                         loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
+         : loop_runtime_checker(loop_rt_chk,loop_runtime_check::e_while_loop)
+         {
+            construct_branch_pair(condition_, condition);
+            construct_branch_pair(loop_body_, loop_body);
          }
 
          inline T value() const
          {
+            assert(condition_.first);
+            assert(loop_body_.first);
+
             T result = T(0);
 
-            while (is_true(condition_))
+            loop_runtime_checker::reset();
+
+            while (is_true(condition_) && loop_runtime_checker::check())
             {
-               result = loop_body_->value();
+               result = loop_body_.first->value();
             }
 
             return result;
@@ -6312,50 +6816,55 @@ namespace exprtk
             return expression_node<T>::e_while;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(condition_, node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(condition_, loop_body_);
+         }
+
       private:
 
-         expression_ptr condition_;
-         expression_ptr loop_body_;
-         const bool condition_deletable_;
-         const bool loop_body_deletable_;
+         branch_t condition_;
+         branch_t loop_body_;
       };
 
       template <typename T>
-      class repeat_until_loop_node : public expression_node<T>
+      class repeat_until_loop_node : public expression_node<T>,
+                                     public loop_runtime_checker
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-         repeat_until_loop_node(expression_ptr condition, expression_ptr loop_body)
-         : condition_(condition),
-           loop_body_(loop_body),
-           condition_deletable_(branch_deletable(condition_)),
-           loop_body_deletable_(branch_deletable(loop_body_))
-         {}
-
-        ~repeat_until_loop_node()
+         repeat_until_loop_node(expression_ptr condition,
+                                expression_ptr loop_body,
+                                loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
+         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_repeat_until_loop)
          {
-            if (condition_ && condition_deletable_)
-            {
-               destroy_node(condition_);
-            }
-
-            if (loop_body_ && loop_body_deletable_)
-            {
-               destroy_node(loop_body_);
-            }
+            construct_branch_pair(condition_, condition);
+            construct_branch_pair(loop_body_, loop_body);
          }
 
          inline T value() const
          {
+            assert(condition_.first);
+            assert(loop_body_.first);
+
             T result = T(0);
 
+            loop_runtime_checker::reset(1);
+
             do
             {
-               result = loop_body_->value();
+               result = loop_body_.first->value();
             }
-            while (is_false(condition_));
+            while (is_false(condition_.first) && loop_runtime_checker::check());
 
             return result;
          }
@@ -6365,78 +6874,70 @@ namespace exprtk
             return expression_node<T>::e_repeat;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(condition_, node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(condition_, loop_body_);
+         }
+
       private:
 
-         expression_ptr condition_;
-         expression_ptr loop_body_;
-         const bool condition_deletable_;
-         const bool loop_body_deletable_;
+         branch_t condition_;
+         branch_t loop_body_;
       };
 
       template <typename T>
-      class for_loop_node : public expression_node<T>
+      class for_loop_node : public expression_node<T>,
+                            public loop_runtime_checker
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
          for_loop_node(expression_ptr initialiser,
                        expression_ptr condition,
                        expression_ptr incrementor,
-                       expression_ptr loop_body)
-         : initialiser_(initialiser),
-           condition_  (condition  ),
-           incrementor_(incrementor),
-           loop_body_  (loop_body  ),
-           initialiser_deletable_(branch_deletable(initialiser_)),
-           condition_deletable_  (branch_deletable(condition_  )),
-           incrementor_deletable_(branch_deletable(incrementor_)),
-           loop_body_deletable_  (branch_deletable(loop_body_  ))
-         {}
-
-        ~for_loop_node()
+                       expression_ptr loop_body,
+                       loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
+         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_for_loop)
          {
-            if (initialiser_ && initialiser_deletable_)
-            {
-               destroy_node(initialiser_);
-            }
-
-            if (condition_ && condition_deletable_)
-            {
-               destroy_node(condition_);
-            }
-
-            if (incrementor_ && incrementor_deletable_)
-            {
-               destroy_node(incrementor_);
-            }
-
-            if (loop_body_ && loop_body_deletable_)
-            {
-               destroy_node(loop_body_);
-            }
+            construct_branch_pair(initialiser_, initialiser);
+            construct_branch_pair(condition_  , condition  );
+            construct_branch_pair(incrementor_, incrementor);
+            construct_branch_pair(loop_body_  , loop_body  );
          }
 
          inline T value() const
          {
+            assert(condition_.first);
+            assert(loop_body_.first);
+
             T result = T(0);
 
-            if (initialiser_)
-               initialiser_->value();
+            loop_runtime_checker::reset();
+
+            if (initialiser_.first)
+               initialiser_.first->value();
 
-            if (incrementor_)
+            if (incrementor_.first)
             {
-               while (is_true(condition_))
+               while (is_true(condition_) && loop_runtime_checker::check())
                {
-                  result = loop_body_->value();
-                  incrementor_->value();
+                  result = loop_body_.first->value();
+                  incrementor_.first->value();
                }
             }
             else
             {
-               while (is_true(condition_))
+               while (is_true(condition_) && loop_runtime_checker::check())
                {
-                  result = loop_body_->value();
+                  result = loop_body_.first->value();
                }
             }
 
@@ -6448,55 +6949,61 @@ namespace exprtk
             return expression_node<T>::e_for;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(initialiser_, node_delete_list);
+            expression_node<T>::ndb_t::collect(condition_  , node_delete_list);
+            expression_node<T>::ndb_t::collect(incrementor_, node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_  , node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth
+               (initialiser_, condition_, incrementor_, loop_body_);
+         }
+
       private:
 
-         expression_ptr initialiser_      ;
-         expression_ptr condition_        ;
-         expression_ptr incrementor_      ;
-         expression_ptr loop_body_        ;
-         const bool initialiser_deletable_;
-         const bool condition_deletable_  ;
-         const bool incrementor_deletable_;
-         const bool loop_body_deletable_  ;
+         branch_t initialiser_;
+         branch_t condition_  ;
+         branch_t incrementor_;
+         branch_t loop_body_  ;
       };
 
       #ifndef exprtk_disable_break_continue
       template <typename T>
-      class while_loop_bc_node : public expression_node<T>
+      class while_loop_bc_node : public expression_node<T>,
+                                 public loop_runtime_checker
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-         while_loop_bc_node(expression_ptr condition, expression_ptr loop_body)
-         : condition_(condition),
-           loop_body_(loop_body),
-           condition_deletable_(branch_deletable(condition_)),
-           loop_body_deletable_(branch_deletable(loop_body_))
-         {}
-
-        ~while_loop_bc_node()
+         while_loop_bc_node(expression_ptr condition,
+                            expression_ptr loop_body,
+                            loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
+         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_while_loop)
          {
-            if (condition_ && condition_deletable_)
-            {
-               destroy_node(condition_);
-            }
-
-            if (loop_body_ && loop_body_deletable_)
-            {
-               destroy_node(loop_body_);
-            }
+            construct_branch_pair(condition_, condition);
+            construct_branch_pair(loop_body_, loop_body);
          }
 
          inline T value() const
          {
+            assert(condition_.first);
+            assert(loop_body_.first);
+
             T result = T(0);
 
-            while (is_true(condition_))
+            loop_runtime_checker::reset();
+
+            while (is_true(condition_) && loop_runtime_checker::check())
             {
                try
                {
-                  result = loop_body_->value();
+                  result = loop_body_.first->value();
                }
                catch(const break_exception<T>& e)
                {
@@ -6514,50 +7021,55 @@ namespace exprtk
             return expression_node<T>::e_while;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(condition_, node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(condition_, loop_body_);
+         }
+
       private:
 
-         expression_ptr condition_;
-         expression_ptr loop_body_;
-         const bool condition_deletable_;
-         const bool loop_body_deletable_;
+         branch_t condition_;
+         branch_t loop_body_;
       };
 
       template <typename T>
-      class repeat_until_loop_bc_node : public expression_node<T>
+      class repeat_until_loop_bc_node : public expression_node<T>,
+                                        public loop_runtime_checker
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-         repeat_until_loop_bc_node(expression_ptr condition, expression_ptr loop_body)
-         : condition_(condition),
-           loop_body_(loop_body),
-           condition_deletable_(branch_deletable(condition_)),
-           loop_body_deletable_(branch_deletable(loop_body_))
-         {}
-
-        ~repeat_until_loop_bc_node()
+         repeat_until_loop_bc_node(expression_ptr condition,
+                                   expression_ptr loop_body,
+                                   loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
+         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_repeat_until_loop)
          {
-            if (condition_ && condition_deletable_)
-            {
-               destroy_node(condition_);
-            }
-
-            if (loop_body_ && loop_body_deletable_)
-            {
-               destroy_node(loop_body_);
-            }
+            construct_branch_pair(condition_, condition);
+            construct_branch_pair(loop_body_, loop_body);
          }
 
          inline T value() const
          {
+            assert(condition_.first);
+            assert(loop_body_.first);
+
             T result = T(0);
 
+            loop_runtime_checker::reset();
+
             do
             {
                try
                {
-                  result = loop_body_->value();
+                  result = loop_body_.first->value();
                }
                catch(const break_exception<T>& e)
                {
@@ -6566,7 +7078,7 @@ namespace exprtk
                catch(const continue_exception&)
                {}
             }
-            while (is_false(condition_));
+            while (is_false(condition_.first) && loop_runtime_checker::check());
 
             return result;
          }
@@ -6576,72 +7088,64 @@ namespace exprtk
             return expression_node<T>::e_repeat;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(condition_, node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(condition_, loop_body_);
+         }
+
       private:
 
-         expression_ptr condition_;
-         expression_ptr loop_body_;
-         const bool condition_deletable_;
-         const bool loop_body_deletable_;
+         branch_t condition_;
+         branch_t loop_body_;
       };
 
       template <typename T>
-      class for_loop_bc_node : public expression_node<T>
+      class for_loop_bc_node : public expression_node<T>,
+                               public loop_runtime_checker
       {
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
          for_loop_bc_node(expression_ptr initialiser,
-                       expression_ptr condition,
-                       expression_ptr incrementor,
-                       expression_ptr loop_body)
-         : initialiser_(initialiser),
-           condition_  (condition  ),
-           incrementor_(incrementor),
-           loop_body_  (loop_body  ),
-           initialiser_deletable_(branch_deletable(initialiser_)),
-           condition_deletable_  (branch_deletable(condition_  )),
-           incrementor_deletable_(branch_deletable(incrementor_)),
-           loop_body_deletable_  (branch_deletable(loop_body_  ))
-         {}
-
-        ~for_loop_bc_node()
+                          expression_ptr condition,
+                          expression_ptr incrementor,
+                          expression_ptr loop_body,
+                          loop_runtime_check_ptr loop_rt_chk = loop_runtime_check_ptr(0))
+         : loop_runtime_checker(loop_rt_chk, loop_runtime_check::e_for_loop)
          {
-            if (initialiser_ && initialiser_deletable_)
-            {
-               destroy_node(initialiser_);
-            }
-
-            if (condition_ && condition_deletable_)
-            {
-               destroy_node(condition_);
-            }
-
-            if (incrementor_ && incrementor_deletable_)
-            {
-               destroy_node(incrementor_);
-            }
-
-            if (loop_body_ && loop_body_deletable_)
-            {
-               destroy_node(loop_body_);
-            }
+            construct_branch_pair(initialiser_, initialiser);
+            construct_branch_pair(condition_  , condition  );
+            construct_branch_pair(incrementor_, incrementor);
+            construct_branch_pair(loop_body_  , loop_body  );
          }
 
          inline T value() const
          {
+            assert(condition_.first);
+            assert(loop_body_.first);
+
             T result = T(0);
 
-            if (initialiser_)
-               initialiser_->value();
+            loop_runtime_checker::reset();
 
-            if (incrementor_)
+            if (initialiser_.first)
+               initialiser_.first->value();
+
+            if (incrementor_.first)
             {
-               while (is_true(condition_))
+               while (is_true(condition_) && loop_runtime_checker::check())
                {
                   try
                   {
-                     result = loop_body_->value();
+                     result = loop_body_.first->value();
                   }
                   catch(const break_exception<T>& e)
                   {
@@ -6650,16 +7154,16 @@ namespace exprtk
                   catch(const continue_exception&)
                   {}
 
-                  incrementor_->value();
+                  incrementor_.first->value();
                }
             }
             else
             {
-               while (is_true(condition_))
+               while (is_true(condition_) && loop_runtime_checker::check())
                {
                   try
                   {
-                     result = loop_body_->value();
+                     result = loop_body_.first->value();
                   }
                   catch(const break_exception<T>& e)
                   {
@@ -6678,16 +7182,26 @@ namespace exprtk
             return expression_node<T>::e_for;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(initialiser_, node_delete_list);
+            expression_node<T>::ndb_t::collect(condition_  , node_delete_list);
+            expression_node<T>::ndb_t::collect(incrementor_, node_delete_list);
+            expression_node<T>::ndb_t::collect(loop_body_  , node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth
+               (initialiser_, condition_, incrementor_, loop_body_);
+         }
+
       private:
 
-         expression_ptr initialiser_;
-         expression_ptr condition_  ;
-         expression_ptr incrementor_;
-         expression_ptr loop_body_  ;
-         const bool initialiser_deletable_;
-         const bool condition_deletable_  ;
-         const bool incrementor_deletable_;
-         const bool loop_body_deletable_  ;
+         branch_t initialiser_;
+         branch_t condition_  ;
+         branch_t incrementor_;
+         branch_t loop_body_  ;
       };
       #endif
 
@@ -6697,44 +7211,31 @@ namespace exprtk
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
-         switch_node(const Sequence<expression_ptr,Allocator>& arg_list)
+         explicit switch_node(const Sequence<expression_ptr,Allocator>& arg_list)
          {
             if (1 != (arg_list.size() & 1))
                return;
 
             arg_list_.resize(arg_list.size());
-            delete_branch_.resize(arg_list.size());
 
             for (std::size_t i = 0; i < arg_list.size(); ++i)
             {
                if (arg_list[i])
                {
-                       arg_list_[i] = arg_list[i];
-                  delete_branch_[i] = static_cast<unsigned char>(branch_deletable(arg_list_[i]) ? 1 : 0);
+                  construct_branch_pair(arg_list_[i], arg_list[i]);
                }
                else
                {
                   arg_list_.clear();
-                  delete_branch_.clear();
                   return;
                }
             }
          }
 
-        ~switch_node()
-         {
-            for (std::size_t i = 0; i < arg_list_.size(); ++i)
-            {
-               if (arg_list_[i] && delete_branch_[i])
-               {
-                  destroy_node(arg_list_[i]);
-               }
-            }
-         }
-
          inline T value() const
          {
             if (!arg_list_.empty())
@@ -6743,8 +7244,8 @@ namespace exprtk
 
                for (std::size_t i = 0; i < upper_bound; i += 2)
                {
-                  expression_ptr condition  = arg_list_[i    ];
-                  expression_ptr consequent = arg_list_[i + 1];
+                  expression_ptr condition  = arg_list_[i    ].first;
+                  expression_ptr consequent = arg_list_[i + 1].first;
 
                   if (is_true(condition))
                   {
@@ -6752,7 +7253,7 @@ namespace exprtk
                   }
                }
 
-               return arg_list_[upper_bound]->value();
+               return arg_list_[upper_bound].first->value();
             }
             else
                return std::numeric_limits<T>::quiet_NaN();
@@ -6763,10 +7264,19 @@ namespace exprtk
             return expression_node<T>::e_switch;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(arg_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(arg_list_);
+         }
+
       protected:
 
-         std::vector<expression_ptr> arg_list_;
-         std::vector<unsigned char> delete_branch_;
+         std::vector<branch_t> arg_list_;
       };
 
       template <typename T, typename Switch_N>
@@ -6778,7 +7288,7 @@ namespace exprtk
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
-         switch_n_node(const Sequence<expression_ptr,Allocator>& arg_list)
+         explicit switch_n_node(const Sequence<expression_ptr,Allocator>& arg_list)
          : switch_node<T>(arg_list)
          {}
 
@@ -6794,44 +7304,31 @@ namespace exprtk
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
-         multi_switch_node(const Sequence<expression_ptr,Allocator>& arg_list)
+         explicit multi_switch_node(const Sequence<expression_ptr,Allocator>& arg_list)
          {
             if (0 != (arg_list.size() & 1))
                return;
 
             arg_list_.resize(arg_list.size());
-            delete_branch_.resize(arg_list.size());
 
             for (std::size_t i = 0; i < arg_list.size(); ++i)
             {
                if (arg_list[i])
                {
-                       arg_list_[i] = arg_list[i];
-                  delete_branch_[i] = static_cast<unsigned char>(branch_deletable(arg_list_[i]) ? 1 : 0);
+                  construct_branch_pair(arg_list_[i], arg_list[i]);
                }
                else
                {
                   arg_list_.clear();
-                  delete_branch_.clear();
                   return;
                }
             }
          }
 
-        ~multi_switch_node()
-         {
-            for (std::size_t i = 0; i < arg_list_.size(); ++i)
-            {
-               if (arg_list_[i] && delete_branch_[i])
-               {
-                  destroy_node(arg_list_[i]);
-               }
-            }
-         }
-
          inline T value() const
          {
             T result = T(0);
@@ -6845,8 +7342,8 @@ namespace exprtk
 
             for (std::size_t i = 0; i < upper_bound; i += 2)
             {
-               expression_ptr condition  = arg_list_[i    ];
-               expression_ptr consequent = arg_list_[i + 1];
+               expression_ptr condition  = arg_list_[i    ].first;
+               expression_ptr consequent = arg_list_[i + 1].first;
 
                if (is_true(condition))
                {
@@ -6862,10 +7359,19 @@ namespace exprtk
             return expression_node<T>::e_mswitch;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(arg_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(arg_list_);
+         }
+
       private:
 
-         std::vector<expression_ptr> arg_list_;
-         std::vector<unsigned char> delete_branch_;
+         std::vector<branch_t> arg_list_;
       };
 
       template <typename T>
@@ -6892,7 +7398,7 @@ namespace exprtk
          : value_(&null_value)
          {}
 
-         variable_node(T& v)
+         explicit variable_node(T& v)
          : value_(&v)
          {}
 
@@ -6981,30 +7487,26 @@ namespace exprtk
             }
          }
 
-         bool const_range()
+         bool const_range() const
          {
            return ( n0_c.first &&  n1_c.first) &&
                   (!n0_e.first && !n1_e.first);
          }
 
-         bool var_range()
+         bool var_range() const
          {
            return ( n0_e.first &&  n1_e.first) &&
                   (!n0_c.first && !n1_c.first);
          }
 
-         bool operator() (std::size_t& r0, std::size_t& r1, const std::size_t& size = std::numeric_limits<std::size_t>::max()) const
+         bool operator() (std::size_t& r0, std::size_t& r1,
+                          const std::size_t& size = std::numeric_limits<std::size_t>::max()) const
          {
             if (n0_c.first)
                r0 = n0_c.second;
             else if (n0_e.first)
             {
-               T r0_value = n0_e.second->value();
-
-               if (r0_value < 0)
-                  return false;
-               else
-                  r0 = static_cast<std::size_t>(details::numeric::to_int64(r0_value));
+               r0 = static_cast<std::size_t>(details::numeric::to_int64(n0_e.second->value()));
             }
             else
                return false;
@@ -7013,12 +7515,7 @@ namespace exprtk
                r1 = n1_c.second;
             else if (n1_e.first)
             {
-               T r1_value = n1_e.second->value();
-
-               if (r1_value < 0)
-                  return false;
-               else
-                  r1 = static_cast<std::size_t>(details::numeric::to_int64(r1_value));
+               r1 = static_cast<std::size_t>(details::numeric::to_int64(n1_e.second->value()));
             }
             else
                return false;
@@ -7034,7 +7531,11 @@ namespace exprtk
             cache.first  = r0;
             cache.second = r1;
 
+            #ifndef exprtk_enable_runtime_checks
             return (r0 <= r1);
+            #else
+            return range_runtime_check(r0, r1, size);
+            #endif
          }
 
          inline std::size_t const_size() const
@@ -7052,6 +7553,27 @@ namespace exprtk
          std::pair<bool,std::size_t        > n0_c;
          std::pair<bool,std::size_t        > n1_c;
          mutable cached_range_t             cache;
+
+         #ifdef exprtk_enable_runtime_checks
+         bool range_runtime_check(const std::size_t r0,
+                                  const std::size_t r1,
+                                  const std::size_t size) const
+         {
+            if ((r0 < 0) || (r0 >= size))
+            {
+               throw std::runtime_error("range error: (r0 < 0) || (r0 >= size)");
+               return false;
+            }
+
+            if ((r1 < 0) || (r1 >= size))
+            {
+               throw std::runtime_error("range error: (r1 < 0) || (r1 >= size)");
+               return false;
+            }
+
+            return (r0 <= r1);
+         }
+         #endif
       };
 
       template <typename T>
@@ -7115,7 +7637,7 @@ namespace exprtk
          typedef vector_node<T>*     vector_node_ptr;
          typedef vec_data_store<T>             vds_t;
 
-         vector_node(vector_holder_t* vh)
+         explicit vector_node(vector_holder_t* vh)
          : vector_holder_(vh),
            vds_((*vector_holder_).size(),(*vector_holder_)[0])
          {
@@ -7179,38 +7701,31 @@ namespace exprtk
       {
       public:
 
-         typedef expression_node<T>* expression_ptr;
-         typedef vector_holder<T>    vector_holder_t;
-         typedef vector_holder_t*    vector_holder_ptr;
+         typedef expression_node<T>*            expression_ptr;
+         typedef vector_holder<T>               vector_holder_t;
+         typedef vector_holder_t*               vector_holder_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
          vector_elem_node(expression_ptr index, vector_holder_ptr vec_holder)
-         : index_(index),
-           vec_holder_(vec_holder),
-           vector_base_((*vec_holder)[0]),
-           index_deletable_(branch_deletable(index_))
-         {}
-
-        ~vector_elem_node()
+         : vec_holder_(vec_holder),
+           vector_base_((*vec_holder)[0])
          {
-            if (index_ && index_deletable_)
-            {
-               destroy_node(index_);
-            }
+            construct_branch_pair(index_, index);
          }
 
          inline T value() const
          {
-            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_->value())));
+            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
          }
 
          inline T& ref()
          {
-            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_->value())));
+            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
          }
 
          inline const T& ref() const
          {
-            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_->value())));
+            return *(vector_base_ + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
          }
 
          inline typename expression_node<T>::node_type type() const
@@ -7223,12 +7738,21 @@ namespace exprtk
             return (*vec_holder_);
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(index_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(index_);
+         }
+
       private:
 
-         expression_ptr index_;
          vector_holder_ptr vec_holder_;
          T* vector_base_;
-         const bool index_deletable_;
+         branch_t index_;
       };
 
       template <typename T>
@@ -7237,41 +7761,33 @@ namespace exprtk
       {
       public:
 
-         typedef expression_node<T>* expression_ptr;
-         typedef vector_holder<T>    vector_holder_t;
-         typedef vector_holder_t*    vector_holder_ptr;
-         typedef vec_data_store<T>   vds_t;
+         typedef expression_node<T>*            expression_ptr;
+         typedef vector_holder<T>               vector_holder_t;
+         typedef vector_holder_t*               vector_holder_ptr;
+         typedef vec_data_store<T>              vds_t;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
          rebasevector_elem_node(expression_ptr index, vector_holder_ptr vec_holder)
-         : index_(index),
-           index_deletable_(branch_deletable(index_)),
-           vector_holder_(vec_holder),
+         : vector_holder_(vec_holder),
            vds_((*vector_holder_).size(),(*vector_holder_)[0])
          {
             vector_holder_->set_ref(&vds_.ref());
-         }
-
-        ~rebasevector_elem_node()
-         {
-            if (index_ && index_deletable_)
-            {
-               destroy_node(index_);
-            }
+            construct_branch_pair(index_, index);
          }
 
          inline T value() const
          {
-            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_->value())));
+            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
          }
 
          inline T& ref()
          {
-            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_->value())));
+            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
          }
 
          inline const T& ref() const
          {
-            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_->value())));
+            return *(vds_.data() + static_cast<std::size_t>(details::numeric::to_int64(index_.first->value())));
          }
 
          inline typename expression_node<T>::node_type type() const
@@ -7284,12 +7800,21 @@ namespace exprtk
             return (*vector_holder_);
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(index_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(index_);
+         }
+
       private:
 
-         expression_ptr index_;
-         const bool index_deletable_;
          vector_holder_ptr vector_holder_;
          vds_t             vds_;
+         branch_t          index_;
       };
 
       template <typename T>
@@ -7360,17 +7885,6 @@ namespace exprtk
            single_value_initialse_(single_value_initialse)
          {}
 
-        ~vector_assignment_node()
-         {
-            for (std::size_t i = 0; i < initialiser_list_.size(); ++i)
-            {
-               if (branch_deletable(initialiser_list_[i]))
-               {
-                  destroy_node(initialiser_list_[i]);
-               }
-            }
-         }
-
          inline T value() const
          {
             if (single_value_initialse_)
@@ -7406,6 +7920,16 @@ namespace exprtk
             return expression_node<T>::e_vecdefass;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(initialiser_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(initialiser_list_);
+         }
+
       private:
 
          vector_assignment_node<T>& operator=(const vector_assignment_node<T>&);
@@ -7527,6 +8051,9 @@ namespace exprtk
 
          inline T value() const
          {
+            assert(binary_node<T>::branch_[0].first);
+            assert(binary_node<T>::branch_[1].first);
+
             if (initialised_)
             {
                binary_node<T>::branch_[0].first->value();
@@ -7831,18 +8358,18 @@ namespace exprtk
       {
       public:
 
-         typedef expression_node <T>*  expression_ptr;
-         typedef stringvar_node  <T>* strvar_node_ptr;
-         typedef string_base_node<T>*    str_base_ptr;
-         typedef range_pack      <T>          range_t;
-         typedef range_t*                   range_ptr;
-         typedef range_interface<T>          irange_t;
-         typedef irange_t*                 irange_ptr;
+         typedef expression_node <T>*      expression_ptr;
+         typedef stringvar_node  <T>*     strvar_node_ptr;
+         typedef string_base_node<T>*        str_base_ptr;
+         typedef range_pack      <T>              range_t;
+         typedef range_t*                       range_ptr;
+         typedef range_interface<T>              irange_t;
+         typedef irange_t*                     irange_ptr;
+         typedef std::pair<expression_ptr,bool>  branch_t;
+
 
          generic_string_range_node(expression_ptr str_branch, const range_t& brange)
          : initialised_(false),
-           branch_(str_branch),
-           branch_deletable_(branch_deletable(branch_)),
            str_base_ptr_ (0),
            str_range_ptr_(0),
            base_range_(brange)
@@ -7852,14 +8379,16 @@ namespace exprtk
             range_.cache.first  = range_.n0_c.second;
             range_.cache.second = range_.n1_c.second;
 
-            if (is_generally_string_node(branch_))
+            construct_branch_pair(branch_, str_branch);
+
+            if (is_generally_string_node(branch_.first))
             {
-               str_base_ptr_ = dynamic_cast<str_base_ptr>(branch_);
+               str_base_ptr_ = dynamic_cast<str_base_ptr>(branch_.first);
 
                if (0 == str_base_ptr_)
                   return;
 
-               str_range_ptr_ = dynamic_cast<irange_ptr>(branch_);
+               str_range_ptr_ = dynamic_cast<irange_ptr>(branch_.first);
 
                if (0 == str_range_ptr_)
                   return;
@@ -7871,18 +8400,15 @@ namespace exprtk
         ~generic_string_range_node()
          {
             base_range_.free();
-
-            if (branch_ && branch_deletable_)
-            {
-               destroy_node(branch_);
-            }
          }
 
          inline T value() const
          {
             if (initialised_)
             {
-               branch_->value();
+               assert(branch_.first);
+
+               branch_.first->value();
 
                std::size_t str_r0 = 0;
                std::size_t str_r1 = 0;
@@ -7890,13 +8416,13 @@ namespace exprtk
                std::size_t r0 = 0;
                std::size_t r1 = 0;
 
-               range_t& range = str_range_ptr_->range_ref();
+               const range_t& range = str_range_ptr_->range_ref();
 
                const std::size_t base_str_size = str_base_ptr_->size();
 
                if (
-                    range      (str_r0,str_r1,base_str_size) &&
-                    base_range_(    r0,    r1,base_str_size)
+                    range      (str_r0, str_r1, base_str_size) &&
+                    base_range_(    r0,     r1, base_str_size)
                   )
                {
                   const std::size_t size = (r1 - r0) + 1;
@@ -7941,11 +8467,20 @@ namespace exprtk
             return expression_node<T>::e_strgenrange;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
       private:
 
          bool                initialised_;
-         expression_ptr           branch_;
-         const bool     branch_deletable_;
+         branch_t                 branch_;
          str_base_ptr       str_base_ptr_;
          irange_ptr        str_range_ptr_;
          mutable range_t      base_range_;
@@ -8019,6 +8554,9 @@ namespace exprtk
          {
             if (initialised_)
             {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
                binary_node<T>::branch_[0].first->value();
                binary_node<T>::branch_[1].first->value();
 
@@ -8028,12 +8566,12 @@ namespace exprtk
                std::size_t str1_r0 = 0;
                std::size_t str1_r1 = 0;
 
-               range_t& range0 = str0_range_ptr_->range_ref();
-               range_t& range1 = str1_range_ptr_->range_ref();
+               const range_t& range0 = str0_range_ptr_->range_ref();
+               const range_t& range1 = str1_range_ptr_->range_ref();
 
                if (
-                    range0(str0_r0,str0_r1,str0_base_ptr_->size()) &&
-                    range1(str1_r0,str1_r1,str1_base_ptr_->size())
+                    range0(str0_r0, str0_r1, str0_base_ptr_->size()) &&
+                    range1(str1_r0, str1_r1, str1_base_ptr_->size())
                   )
                {
                   const std::size_t size0 = (str0_r1 - str0_r0) + 1;
@@ -8129,10 +8667,13 @@ namespace exprtk
          {
             if (initialised_)
             {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
                binary_node<T>::branch_[0].first->value();
                binary_node<T>::branch_[1].first->value();
 
-               std::swap(str0_node_ptr_->ref(),str1_node_ptr_->ref());
+               std::swap(str0_node_ptr_->ref(), str1_node_ptr_->ref());
             }
 
             return std::numeric_limits<T>::quiet_NaN();
@@ -8236,6 +8777,9 @@ namespace exprtk
          {
             if (initialised_)
             {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
                binary_node<T>::branch_[0].first->value();
                binary_node<T>::branch_[1].first->value();
 
@@ -8245,12 +8789,12 @@ namespace exprtk
                std::size_t str1_r0 = 0;
                std::size_t str1_r1 = 0;
 
-               range_t& range0 = (*str0_range_ptr_);
-               range_t& range1 = (*str1_range_ptr_);
+               const range_t& range0 = (*str0_range_ptr_);
+               const range_t& range1 = (*str1_range_ptr_);
 
                if (
-                    range0(str0_r0,str0_r1,str0_base_ptr_->size()) &&
-                    range1(str1_r0,str1_r1,str1_base_ptr_->size())
+                    range0(str0_r0, str0_r1, str0_base_ptr_->size()) &&
+                    range1(str1_r0, str1_r1, str1_base_ptr_->size())
                   )
                {
                   const std::size_t size0    = range0.cache_size();
@@ -8288,8 +8832,8 @@ namespace exprtk
                   exprtk_disable_fallthrough_begin
                   switch (lud.remainder)
                   {
-                     #define case_stmt(N)                      \
-                     case N : { std::swap(s0[i],s1[i]); ++i; } \
+                     #define case_stmt(N)                       \
+                     case N : { std::swap(s0[i], s1[i]); ++i; } \
 
                      #ifndef exprtk_disable_superscalar_unroll
                      case_stmt(15) case_stmt(14)
@@ -8367,38 +8911,32 @@ namespace exprtk
       {
       public:
 
-         typedef expression_node <T>* expression_ptr;
-         typedef string_base_node<T>*   str_base_ptr;
+         typedef expression_node <T>*      expression_ptr;
+         typedef string_base_node<T>*        str_base_ptr;
+         typedef std::pair<expression_ptr,bool>  branch_t;
+
 
-         string_size_node(expression_ptr brnch)
-         : branch_(brnch),
-           branch_deletable_(branch_deletable(branch_)),
-           str_base_ptr_(0)
+         explicit string_size_node(expression_ptr branch)
+         : str_base_ptr_(0)
          {
-            if (is_generally_string_node(branch_))
+            construct_branch_pair(branch_, branch);
+
+            if (is_generally_string_node(branch_.first))
             {
-               str_base_ptr_ = dynamic_cast<str_base_ptr>(branch_);
+               str_base_ptr_ = dynamic_cast<str_base_ptr>(branch_.first);
 
                if (0 == str_base_ptr_)
                   return;
             }
          }
 
-        ~string_size_node()
-         {
-            if (branch_ && branch_deletable_)
-            {
-               destroy_node(branch_);
-            }
-         }
-
          inline T value() const
          {
             T result = std::numeric_limits<T>::quiet_NaN();
 
             if (str_base_ptr_)
             {
-               branch_->value();
+               branch_.first->value();
                result = T(str_base_ptr_->size());
             }
 
@@ -8410,11 +8948,20 @@ namespace exprtk
             return expression_node<T>::e_stringsize;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
       private:
 
-         expression_ptr           branch_;
-         const bool     branch_deletable_;
-         str_base_ptr       str_base_ptr_;
+         branch_t           branch_;
+         str_base_ptr str_base_ptr_;
       };
 
       struct asn_assignment
@@ -8486,12 +9033,15 @@ namespace exprtk
          {
             if (initialised_)
             {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
                binary_node<T>::branch_[1].first->value();
 
                std::size_t r0 = 0;
                std::size_t r1 = 0;
 
-               range_t& range = (*str1_range_ptr_);
+               const range_t& range = (*str1_range_ptr_);
 
                if (range(r0, r1, str1_base_ptr_->size()))
                {
@@ -8552,28 +9102,29 @@ namespace exprtk
       {
       public:
 
-         typedef expression_node <T>*  expression_ptr;
-         typedef stringvar_node  <T>* strvar_node_ptr;
-         typedef string_base_node<T>*    str_base_ptr;
-         typedef range_pack      <T>          range_t;
-         typedef range_t*                   range_ptr;
-         typedef range_interface<T>          irange_t;
-         typedef irange_t*                 irange_ptr;
+         typedef expression_node  <T>*   expression_ptr;
+         typedef stringvar_node   <T>*  strvar_node_ptr;
+         typedef string_range_node<T>* str_rng_node_ptr;
+         typedef string_base_node <T>*     str_base_ptr;
+         typedef range_pack       <T>           range_t;
+         typedef range_t*                     range_ptr;
+         typedef range_interface<T>            irange_t;
+         typedef irange_t*                   irange_ptr;
 
          assignment_string_range_node(const operator_type& opr,
                                       expression_ptr branch0,
                                       expression_ptr branch1)
          : binary_node<T>(opr, branch0, branch1),
            initialised_(false),
-           str0_base_ptr_ (0),
-           str1_base_ptr_ (0),
-           str0_node_ptr_ (0),
-           str0_range_ptr_(0),
-           str1_range_ptr_(0)
+           str0_base_ptr_     (0),
+           str1_base_ptr_     (0),
+           str0_rng_node_ptr_ (0),
+           str0_range_ptr_    (0),
+           str1_range_ptr_    (0)
          {
             if (is_string_range_node(binary_node<T>::branch_[0].first))
             {
-               str0_node_ptr_ = static_cast<strvar_node_ptr>(binary_node<T>::branch_[0].first);
+               str0_rng_node_ptr_ = static_cast<str_rng_node_ptr>(binary_node<T>::branch_[0].first);
 
                str0_base_ptr_ = dynamic_cast<str_base_ptr>(binary_node<T>::branch_[0].first);
 
@@ -8600,17 +9151,20 @@ namespace exprtk
                str1_range_ptr_ = &(range->range_ref());
             }
 
-            initialised_ = str0_base_ptr_  &&
-                           str1_base_ptr_  &&
-                           str0_node_ptr_  &&
-                           str0_range_ptr_ &&
-                           str1_range_ptr_ ;
+            initialised_ = str0_base_ptr_     &&
+                           str1_base_ptr_     &&
+                           str0_rng_node_ptr_ &&
+                           str0_range_ptr_    &&
+                           str1_range_ptr_    ;
          }
 
          inline T value() const
          {
             if (initialised_)
             {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
                binary_node<T>::branch_[0].first->value();
                binary_node<T>::branch_[1].first->value();
 
@@ -8620,15 +9174,15 @@ namespace exprtk
                std::size_t s1_r0 = 0;
                std::size_t s1_r1 = 0;
 
-               range_t& range0 = (*str0_range_ptr_);
-               range_t& range1 = (*str1_range_ptr_);
+               const range_t& range0 = (*str0_range_ptr_);
+               const range_t& range1 = (*str1_range_ptr_);
 
                if (
                     range0(s0_r0, s0_r1, str0_base_ptr_->size()) &&
                     range1(s1_r0, s1_r1, str1_base_ptr_->size())
                   )
                {
-                  std::size_t size = std::min((s0_r1 - s0_r0),(s1_r1 - s1_r0)) + 1;
+                  const std::size_t size = std::min((s0_r1 - s0_r0), (s1_r1 - s1_r0)) + 1;
 
                   std::copy(str1_base_ptr_->base() + s1_r0,
                             str1_base_ptr_->base() + s1_r0 + size,
@@ -8641,27 +9195,27 @@ namespace exprtk
 
          std::string str() const
          {
-            return str0_node_ptr_->str();
+            return str0_base_ptr_->str();
          }
 
          char_cptr base() const
          {
-           return str0_node_ptr_->base();
+            return str0_base_ptr_->base();
          }
 
          std::size_t size() const
          {
-            return str0_node_ptr_->size();
+            return str0_base_ptr_->size();
          }
 
          range_t& range_ref()
          {
-            return str0_node_ptr_->range_ref();
+            return str0_rng_node_ptr_->range_ref();
          }
 
          const range_t& range_ref() const
          {
-            return str0_node_ptr_->range_ref();
+            return str0_rng_node_ptr_->range_ref();
          }
 
          inline typename expression_node<T>::node_type type() const
@@ -8671,12 +9225,12 @@ namespace exprtk
 
       private:
 
-         bool            initialised_;
-         str_base_ptr    str0_base_ptr_;
-         str_base_ptr    str1_base_ptr_;
-         strvar_node_ptr str0_node_ptr_;
-         range_ptr       str0_range_ptr_;
-         range_ptr       str1_range_ptr_;
+         bool             initialised_;
+         str_base_ptr     str0_base_ptr_;
+         str_base_ptr     str1_base_ptr_;
+         str_rng_node_ptr str0_rng_node_ptr_;
+         range_ptr        str0_range_ptr_;
+         range_ptr        str1_range_ptr_;
       };
 
       template <typename T>
@@ -8693,16 +9247,16 @@ namespace exprtk
          typedef range_interface<T>         irange_t;
          typedef irange_t*                irange_ptr;
 
-         conditional_string_node(expression_ptr test,
+         conditional_string_node(expression_ptr condition,
                                  expression_ptr consequent,
                                  expression_ptr alternative)
-         : trinary_node<T>(details::e_default,consequent,alternative,test),
+         : trinary_node<T>(details::e_default,consequent,alternative,condition),
            initialised_(false),
            str0_base_ptr_ (0),
            str1_base_ptr_ (0),
            str0_range_ptr_(0),
            str1_range_ptr_(0),
-           test_              (test),
+           condition_    (condition),
            consequent_  (consequent),
            alternative_(alternative)
          {
@@ -8749,14 +9303,18 @@ namespace exprtk
          {
             if (initialised_)
             {
+               assert(condition_  );
+               assert(consequent_ );
+               assert(alternative_);
+
                std::size_t r0 = 0;
                std::size_t r1 = 0;
 
-               if (is_true(test_))
+               if (is_true(condition_))
                {
                   consequent_->value();
 
-                  range_t& range = str0_range_ptr_->range_ref();
+                  const range_t& range = str0_range_ptr_->range_ref();
 
                   if (range(r0, r1, str0_base_ptr_->size()))
                   {
@@ -8774,7 +9332,7 @@ namespace exprtk
                {
                   alternative_->value();
 
-                  range_t& range = str1_range_ptr_->range_ref();
+                  const range_t& range = str1_range_ptr_->range_ref();
 
                   if (range(r0, r1, str1_base_ptr_->size()))
                   {
@@ -8833,7 +9391,7 @@ namespace exprtk
          mutable range_t     range_;
          mutable std::string value_;
 
-         expression_ptr test_;
+         expression_ptr condition_;
          expression_ptr consequent_;
          expression_ptr alternative_;
       };
@@ -8852,13 +9410,13 @@ namespace exprtk
          typedef range_interface<T>         irange_t;
          typedef irange_t*                irange_ptr;
 
-         cons_conditional_str_node(expression_ptr test,
+         cons_conditional_str_node(expression_ptr condition,
                                    expression_ptr consequent)
-         : binary_node<T>(details::e_default, consequent, test),
+         : binary_node<T>(details::e_default, consequent, condition),
            initialised_(false),
            str0_base_ptr_ (0),
            str0_range_ptr_(0),
-           test_      (test),
+           condition_ (condition),
            consequent_(consequent)
          {
             range_.n0_c = std::make_pair<bool,std::size_t>(true,0);
@@ -8887,11 +9445,14 @@ namespace exprtk
          {
             if (initialised_)
             {
-               if (is_true(test_))
+               assert(condition_ );
+               assert(consequent_);
+
+               if (is_true(condition_))
                {
                   consequent_->value();
 
-                  range_t& range = str0_range_ptr_->range_ref();
+                  const range_t& range = str0_range_ptr_->range_ref();
 
                   std::size_t r0 = 0;
                   std::size_t r1 = 0;
@@ -8951,7 +9512,7 @@ namespace exprtk
          mutable range_t     range_;
          mutable std::string value_;
 
-         expression_ptr test_;
+         expression_ptr condition_;
          expression_ptr consequent_;
       };
 
@@ -8962,33 +9523,34 @@ namespace exprtk
       {
       public:
 
-         typedef expression_node <T>*  expression_ptr;
-         typedef string_base_node<T>*    str_base_ptr;
-         typedef range_pack      <T>          range_t;
-         typedef range_t*                   range_ptr;
-         typedef range_interface<T>          irange_t;
-         typedef irange_t*                 irange_ptr;
+         typedef expression_node <T>*     expression_ptr;
+         typedef string_base_node<T>*       str_base_ptr;
+         typedef range_pack      <T>             range_t;
+         typedef range_t*                      range_ptr;
+         typedef range_interface<T>             irange_t;
+         typedef irange_t*                    irange_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
-         str_vararg_node(const Sequence<expression_ptr,Allocator>& arg_list)
-         : final_node_(arg_list.back()),
-           final_deletable_(branch_deletable(final_node_)),
-           initialised_(false),
+         explicit str_vararg_node(const Sequence<expression_ptr,Allocator>& arg_list)
+         : initialised_(false),
            str_base_ptr_ (0),
            str_range_ptr_(0)
          {
-            if (0 == final_node_)
+            construct_branch_pair(final_node_, const_cast<expression_ptr>(arg_list.back()));
+
+            if (0 == final_node_.first)
                return;
-            else if (!is_generally_string_node(final_node_))
+            else if (!is_generally_string_node(final_node_.first))
                return;
 
-            str_base_ptr_ = dynamic_cast<str_base_ptr>(final_node_);
+            str_base_ptr_ = dynamic_cast<str_base_ptr>(final_node_.first);
 
             if (0 == str_base_ptr_)
                return;
 
-            str_range_ptr_ = dynamic_cast<irange_ptr>(final_node_);
+            str_range_ptr_ = dynamic_cast<irange_ptr>(final_node_.first);
 
             if (0 == str_range_ptr_)
                return;
@@ -9000,41 +9562,22 @@ namespace exprtk
                const std::size_t arg_list_size = arg_list.size() - 1;
 
                arg_list_.resize(arg_list_size);
-               delete_branch_.resize(arg_list_size);
 
                for (std::size_t i = 0; i < arg_list_size; ++i)
                {
                   if (arg_list[i])
                   {
-                          arg_list_[i] = arg_list[i];
-                     delete_branch_[i] = static_cast<unsigned char>(branch_deletable(arg_list_[i]) ? 1 : 0);
+                     construct_branch_pair(arg_list_[i], arg_list[i]);
                   }
                   else
                   {
-                     arg_list_     .clear();
-                     delete_branch_.clear();
+                     arg_list_.clear();
                      return;
                   }
                }
             }
          }
 
-        ~str_vararg_node()
-         {
-            if (final_node_ && final_deletable_)
-            {
-               destroy_node(final_node_);
-            }
-
-            for (std::size_t i = 0; i < arg_list_.size(); ++i)
-            {
-               if (arg_list_[i] && delete_branch_[i])
-               {
-                  destroy_node(arg_list_[i]);
-               }
-            }
-         }
-
          inline T value() const
          {
             if (!arg_list_.empty())
@@ -9042,7 +9585,7 @@ namespace exprtk
                VarArgFunction::process(arg_list_);
             }
 
-            final_node_->value();
+            final_node_.first->value();
 
             return std::numeric_limits<T>::quiet_NaN();
          }
@@ -9077,15 +9620,26 @@ namespace exprtk
             return expression_node<T>::e_stringvararg;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(final_node_, node_delete_list);
+            expression_node<T>::ndb_t::collect(arg_list_  , node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return std::max(
+               expression_node<T>::ndb_t::compute_node_depth(final_node_),
+               expression_node<T>::ndb_t::compute_node_depth(arg_list_  ));
+         }
+
       private:
 
-         expression_ptr final_node_;
-         bool           final_deletable_;
-         bool           initialised_;
-         str_base_ptr   str_base_ptr_;
-         irange_ptr     str_range_ptr_;
-         std::vector<expression_ptr> arg_list_;
-         std::vector<unsigned char> delete_branch_;
+         bool                  initialised_;
+         branch_t              final_node_;
+         str_base_ptr          str_base_ptr_;
+         irange_ptr            str_range_ptr_;
+         std::vector<branch_t> arg_list_;
       };
       #endif
 
@@ -9118,14 +9672,14 @@ namespace exprtk
       template <typename T>                              \
       struct sf##NN##_op : public sf_base<T>             \
       {                                                  \
-         typedef typename sf_base<T>::Type Type;         \
+         typedef typename sf_base<T>::Type const Type;   \
          static inline T process(Type x, Type y, Type z) \
          {                                               \
             return (OP0);                                \
          }                                               \
          static inline std::string id()                  \
          {                                               \
-            return OP1;                                  \
+            return (OP1);                                \
          }                                               \
       };                                                 \
 
@@ -9182,12 +9736,15 @@ namespace exprtk
       template <typename T>                                      \
       struct sf##NN##_op : public sf_base<T>                     \
       {                                                          \
-         typedef typename sf_base<T>::Type Type;                 \
+         typedef typename sf_base<T>::Type const Type;           \
          static inline T process(Type x, Type y, Type z, Type w) \
          {                                                       \
             return (OP0);                                        \
          }                                                       \
-         static inline std::string id() { return OP1; }          \
+         static inline std::string id()                          \
+         {                                                       \
+            return (OP1);                                        \
+         }                                                       \
       };                                                         \
 
       define_sfop4(48,(x + ((y + z) / w)),"t+((t+t)/t)")
@@ -9326,6 +9883,10 @@ namespace exprtk
 
          inline T value() const
          {
+            assert(trinary_node<T>::branch_[0].first);
+            assert(trinary_node<T>::branch_[1].first);
+            assert(trinary_node<T>::branch_[2].first);
+
             const T x = trinary_node<T>::branch_[0].first->value();
             const T y = trinary_node<T>::branch_[1].first->value();
             const T z = trinary_node<T>::branch_[2].first->value();
@@ -9351,6 +9912,11 @@ namespace exprtk
 
          inline T value() const
          {
+            assert(quaternary_node<T>::branch_[0].first);
+            assert(quaternary_node<T>::branch_[1].first);
+            assert(quaternary_node<T>::branch_[2].first);
+            assert(quaternary_node<T>::branch_[3].first);
+
             const T x = quaternary_node<T>::branch_[0].first->value();
             const T y = quaternary_node<T>::branch_[1].first->value();
             const T z = quaternary_node<T>::branch_[2].first->value();
@@ -9434,47 +10000,31 @@ namespace exprtk
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
-         vararg_node(const Sequence<expression_ptr,Allocator>& arg_list)
+         explicit vararg_node(const Sequence<expression_ptr,Allocator>& arg_list)
          {
-            arg_list_     .resize(arg_list.size());
-            delete_branch_.resize(arg_list.size());
+            arg_list_.resize(arg_list.size());
 
             for (std::size_t i = 0; i < arg_list.size(); ++i)
             {
                if (arg_list[i])
                {
-                       arg_list_[i] = arg_list[i];
-                  delete_branch_[i] = static_cast<unsigned char>(branch_deletable(arg_list_[i]) ? 1 : 0);
+                  construct_branch_pair(arg_list_[i],arg_list[i]);
                }
                else
                {
                   arg_list_.clear();
-                  delete_branch_.clear();
                   return;
                }
             }
          }
 
-        ~vararg_node()
-         {
-            for (std::size_t i = 0; i < arg_list_.size(); ++i)
-            {
-               if (arg_list_[i] && delete_branch_[i])
-               {
-                  destroy_node(arg_list_[i]);
-               }
-            }
-         }
-
          inline T value() const
          {
-            if (!arg_list_.empty())
-               return VarArgFunction::process(arg_list_);
-            else
-               return std::numeric_limits<T>::quiet_NaN();
+            return VarArgFunction::process(arg_list_);
          }
 
          inline typename expression_node<T>::node_type type() const
@@ -9482,10 +10032,19 @@ namespace exprtk
             return expression_node<T>::e_vararg;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(arg_list_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(arg_list_);
+         }
+
       private:
 
-         std::vector<expression_ptr> arg_list_;
-         std::vector<unsigned char> delete_branch_;
+         std::vector<branch_t> arg_list_;
       };
 
       template <typename T, typename VarArgFunction>
@@ -9497,7 +10056,7 @@ namespace exprtk
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
-         vararg_varnode(const Sequence<expression_ptr,Allocator>& arg_list)
+         explicit vararg_varnode(const Sequence<expression_ptr,Allocator>& arg_list)
          {
             arg_list_.resize(arg_list.size());
 
@@ -9540,33 +10099,29 @@ namespace exprtk
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
-         vectorize_node(const expression_ptr v)
-         : ivec_ptr_(0),
-           v_(v),
-           v_deletable_(branch_deletable(v_))
+         explicit vectorize_node(const expression_ptr v)
+         : ivec_ptr_(0)
          {
-            if (is_ivector_node(v))
+            construct_branch_pair(v_, v);
+
+            if (is_ivector_node(v_.first))
             {
-               ivec_ptr_ = dynamic_cast<vector_interface<T>*>(v);
+               ivec_ptr_ = dynamic_cast<vector_interface<T>*>(v_.first);
             }
             else
                ivec_ptr_ = 0;
          }
 
-        ~vectorize_node()
-         {
-            if (v_ && v_deletable_)
-            {
-               destroy_node(v_);
-            }
-         }
-
          inline T value() const
          {
             if (ivec_ptr_)
             {
-               v_->value();
+               assert(v_.first);
+
+               v_.first->value();
+
                return VecFunction::process(ivec_ptr_);
             }
             else
@@ -9578,11 +10133,20 @@ namespace exprtk
             return expression_node<T>::e_vecfunc;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(v_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(v_);
+         }
+
       private:
 
          vector_interface<T>* ivec_ptr_;
-         expression_ptr              v_;
-         const bool        v_deletable_;
+         branch_t                    v_;
       };
 
       template <typename T>
@@ -9608,6 +10172,8 @@ namespace exprtk
          {
             if (var_node_ptr_)
             {
+               assert(binary_node<T>::branch_[1].first);
+
                T& result = var_node_ptr_->ref();
 
                result = binary_node<T>::branch_[1].first->value();
@@ -9646,6 +10212,8 @@ namespace exprtk
          {
             if (vec_node_ptr_)
             {
+               assert(binary_node<T>::branch_[1].first);
+
                T& result = vec_node_ptr_->ref();
 
                result = binary_node<T>::branch_[1].first->value();
@@ -9684,6 +10252,8 @@ namespace exprtk
          {
             if (rbvec_node_ptr_)
             {
+               assert(binary_node<T>::branch_[1].first);
+
                T& result = rbvec_node_ptr_->ref();
 
                result = binary_node<T>::branch_[1].first->value();
@@ -9722,6 +10292,8 @@ namespace exprtk
          {
             if (rbvec_node_ptr_)
             {
+               assert(binary_node<T>::branch_[1].first);
+
                T& result = rbvec_node_ptr_->ref();
 
                result = binary_node<T>::branch_[1].first->value();
@@ -9764,6 +10336,8 @@ namespace exprtk
          {
             if (vec_node_ptr_)
             {
+               assert(binary_node<T>::branch_[1].first);
+
                const T v = binary_node<T>::branch_[1].first->value();
 
                T* vec = vds().data();
@@ -9773,8 +10347,8 @@ namespace exprtk
 
                while (vec < upper_bound)
                {
-                 #define exprtk_loop(N) \
-                  vec[N] = v;           \
+                  #define exprtk_loop(N) \
+                  vec[N] = v;            \
 
                   exprtk_loop( 0) exprtk_loop( 1)
                   exprtk_loop( 2) exprtk_loop( 3)
@@ -9909,6 +10483,8 @@ namespace exprtk
          {
             if (initialised_)
             {
+               assert(binary_node<T>::branch_[1].first);
+
                binary_node<T>::branch_[1].first->value();
 
                if (src_is_ivec_)
@@ -10030,6 +10606,8 @@ namespace exprtk
          {
             if (var_node_ptr_)
             {
+               assert(binary_node<T>::branch_[1].first);
+
                T& v = var_node_ptr_->ref();
                v = Operation::process(v,binary_node<T>::branch_[1].first->value());
 
@@ -10067,6 +10645,8 @@ namespace exprtk
          {
             if (vec_node_ptr_)
             {
+               assert(binary_node<T>::branch_[1].first);
+
                T& v = vec_node_ptr_->ref();
                   v = Operation::process(v,binary_node<T>::branch_[1].first->value());
 
@@ -10104,6 +10684,8 @@ namespace exprtk
          {
             if (rbvec_node_ptr_)
             {
+               assert(binary_node<T>::branch_[1].first);
+
                T& v = rbvec_node_ptr_->ref();
                   v = Operation::process(v,binary_node<T>::branch_[1].first->value());
 
@@ -10141,6 +10723,8 @@ namespace exprtk
          {
             if (rbvec_node_ptr_)
             {
+               assert(binary_node<T>::branch_[1].first);
+
                T& v = rbvec_node_ptr_->ref();
                   v = Operation::process(v,binary_node<T>::branch_[1].first->value());
 
@@ -10182,6 +10766,8 @@ namespace exprtk
          {
             if (vec_node_ptr_)
             {
+               assert(binary_node<T>::branch_[1].first);
+
                const T v = binary_node<T>::branch_[1].first->value();
 
                T* vec = vds().data();
@@ -10327,19 +10913,22 @@ namespace exprtk
          {
             if (initialised_)
             {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
                binary_node<T>::branch_[0].first->value();
                binary_node<T>::branch_[1].first->value();
 
-               T* vec0 = vec0_node_ptr_->vds().data();
-               T* vec1 = vec1_node_ptr_->vds().data();
+                     T* vec0 = vec0_node_ptr_->vds().data();
+               const T* vec1 = vec1_node_ptr_->vds().data();
 
                loop_unroll::details lud(size());
                const T* upper_bound = vec0 + lud.upper_bound;
 
                while (vec0 < upper_bound)
                {
-                  #define exprtk_loop(N)                         \
-                  vec0[N] = Operation::process(vec0[N],vec1[N]); \
+                  #define exprtk_loop(N)                          \
+                  vec0[N] = Operation::process(vec0[N], vec1[N]); \
 
                   exprtk_loop( 0) exprtk_loop( 1)
                   exprtk_loop( 2) exprtk_loop( 3)
@@ -10361,8 +10950,8 @@ namespace exprtk
                exprtk_disable_fallthrough_begin
                switch (lud.remainder)
                {
-                  #define case_stmt(N)                                             \
-                  case N : { vec0[i] = Operation::process(vec0[i],vec1[i]); ++i; } \
+                  #define case_stmt(N)                                              \
+                  case N : { vec0[i] = Operation::process(vec0[i], vec1[i]); ++i; } \
 
                   #ifndef exprtk_disable_superscalar_unroll
                   case_stmt(15) case_stmt(14)
@@ -10512,20 +11101,23 @@ namespace exprtk
          {
             if (initialised_)
             {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
                binary_node<T>::branch_[0].first->value();
                binary_node<T>::branch_[1].first->value();
 
-               T* vec0 = vec0_node_ptr_->vds().data();
-               T* vec1 = vec1_node_ptr_->vds().data();
-               T* vec2 = vds().data();
+               const T* vec0 = vec0_node_ptr_->vds().data();
+               const T* vec1 = vec1_node_ptr_->vds().data();
+                     T* vec2 = vds().data();
 
                loop_unroll::details lud(size());
                const T* upper_bound = vec2 + lud.upper_bound;
 
                while (vec2 < upper_bound)
                {
-                  #define exprtk_loop(N)                         \
-                  vec2[N] = Operation::process(vec0[N],vec1[N]); \
+                  #define exprtk_loop(N)                          \
+                  vec2[N] = Operation::process(vec0[N], vec1[N]); \
 
                   exprtk_loop( 0) exprtk_loop( 1)
                   exprtk_loop( 2) exprtk_loop( 3)
@@ -10548,8 +11140,8 @@ namespace exprtk
                exprtk_disable_fallthrough_begin
                switch (lud.remainder)
                {
-                  #define case_stmt(N)                                             \
-                  case N : { vec2[i] = Operation::process(vec0[i],vec1[i]); ++i; } \
+                  #define case_stmt(N)                                              \
+                  case N : { vec2[i] = Operation::process(vec0[i], vec1[i]); ++i; } \
 
                   #ifndef exprtk_disable_superscalar_unroll
                   case_stmt(15) case_stmt(14)
@@ -10671,19 +11263,22 @@ namespace exprtk
          {
             if (vec0_node_ptr_)
             {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
                            binary_node<T>::branch_[0].first->value();
                const T v = binary_node<T>::branch_[1].first->value();
 
-               T* vec0 = vec0_node_ptr_->vds().data();
-               T* vec1 = vds().data();
+               const T* vec0 = vec0_node_ptr_->vds().data();
+                     T* vec1 = vds().data();
 
                loop_unroll::details lud(size());
                const T* upper_bound = vec0 + lud.upper_bound;
 
                while (vec0 < upper_bound)
                {
-                  #define exprtk_loop(N)                   \
-                  vec1[N] = Operation::process(vec0[N],v); \
+                  #define exprtk_loop(N)                    \
+                  vec1[N] = Operation::process(vec0[N], v); \
 
                   exprtk_loop( 0) exprtk_loop( 1)
                   exprtk_loop( 2) exprtk_loop( 3)
@@ -10705,8 +11300,8 @@ namespace exprtk
                exprtk_disable_fallthrough_begin
                switch (lud.remainder)
                {
-                  #define case_stmt(N)                                       \
-                  case N : { vec1[i] = Operation::process(vec0[i],v); ++i; } \
+                  #define case_stmt(N)                                        \
+                  case N : { vec1[i] = Operation::process(vec0[i], v); ++i; } \
 
                   #ifndef exprtk_disable_superscalar_unroll
                   case_stmt(15) case_stmt(14)
@@ -10826,19 +11421,22 @@ namespace exprtk
          {
             if (vec1_node_ptr_)
             {
+               assert(binary_node<T>::branch_[0].first);
+               assert(binary_node<T>::branch_[1].first);
+
                const T v = binary_node<T>::branch_[0].first->value();
                            binary_node<T>::branch_[1].first->value();
 
-               T* vec0 = vds().data();
-               T* vec1 = vec1_node_ptr_->vds().data();
+                     T* vec0 = vds().data();
+               const T* vec1 = vec1_node_ptr_->vds().data();
 
                loop_unroll::details lud(size());
                const T* upper_bound = vec0 + lud.upper_bound;
 
                while (vec0 < upper_bound)
                {
-                  #define exprtk_loop(N)                   \
-                  vec0[N] = Operation::process(v,vec1[N]); \
+                  #define exprtk_loop(N)                    \
+                  vec0[N] = Operation::process(v, vec1[N]); \
 
                   exprtk_loop( 0) exprtk_loop( 1)
                   exprtk_loop( 2) exprtk_loop( 3)
@@ -10860,8 +11458,8 @@ namespace exprtk
                exprtk_disable_fallthrough_begin
                switch (lud.remainder)
                {
-                  #define case_stmt(N)                                       \
-                  case N : { vec0[i] = Operation::process(v,vec1[i]); ++i; } \
+                  #define case_stmt(N)                                        \
+                  case N : { vec0[i] = Operation::process(v, vec1[i]); ++i; } \
 
                   #ifndef exprtk_disable_superscalar_unroll
                   case_stmt(15) case_stmt(14)
@@ -10942,15 +11540,15 @@ namespace exprtk
          {
             bool vec0_is_ivec = false;
 
-            if (is_vector_node(unary_node<T>::branch_))
+            if (is_vector_node(unary_node<T>::branch_.first))
             {
-               vec0_node_ptr_ = static_cast<vector_node_ptr>(unary_node<T>::branch_);
+               vec0_node_ptr_ = static_cast<vector_node_ptr>(unary_node<T>::branch_.first);
             }
-            else if (is_ivector_node(unary_node<T>::branch_))
+            else if (is_ivector_node(unary_node<T>::branch_.first))
             {
                vector_interface<T>* vi = reinterpret_cast<vector_interface<T>*>(0);
 
-               if (0 != (vi = dynamic_cast<vector_interface<T>*>(unary_node<T>::branch_)))
+               if (0 != (vi = dynamic_cast<vector_interface<T>*>(unary_node<T>::branch_.first)))
                {
                   vec0_node_ptr_ = vi->vec();
                   vec0_is_ivec   = true;
@@ -10977,12 +11575,14 @@ namespace exprtk
 
          inline T value() const
          {
-            unary_node<T>::branch_->value();
+            assert(unary_node<T>::branch_.first);
+
+            unary_node<T>::branch_.first->value();
 
             if (vec0_node_ptr_)
             {
-               T* vec0 = vec0_node_ptr_->vds().data();
-               T* vec1 = vds().data();
+               const T* vec0 = vec0_node_ptr_->vds().data();
+                     T* vec1 = vds().data();
 
                loop_unroll::details lud(size());
                const T* upper_bound = vec0 + lud.upper_bound;
@@ -11090,6 +11690,9 @@ namespace exprtk
 
          inline T value() const
          {
+            assert(binary_node<T>::branch_[0].first);
+            assert(binary_node<T>::branch_[1].first);
+
             return (
                      std::not_equal_to<T>()
                         (T(0),binary_node<T>::branch_[0].first->value()) &&
@@ -11114,6 +11717,9 @@ namespace exprtk
 
          inline T value() const
          {
+            assert(binary_node<T>::branch_[0].first);
+            assert(binary_node<T>::branch_[1].first);
+
             return (
                      std::not_equal_to<T>()
                         (T(0),binary_node<T>::branch_[0].first->value()) ||
@@ -11133,16 +11739,11 @@ namespace exprtk
          typedef std::pair<expression_ptr,bool> branch_t;
          typedef IFunction ifunction;
 
-         function_N_node(ifunction* func)
+         explicit function_N_node(ifunction* func)
          : function_((N == func->param_count) ? func : reinterpret_cast<ifunction*>(0)),
            parameter_count_(func->param_count)
          {}
 
-        ~function_N_node()
-         {
-            cleanup_branches::execute<T,N>(branch_);
-         }
-
          template <std::size_t NumBranches>
          bool init_branches(expression_ptr (&b)[NumBranches])
          {
@@ -11409,6 +12010,16 @@ namespace exprtk
             return expression_node<T>::e_function;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::template compute_node_depth<N>(branch_);
+         }
+
       private:
 
          ifunction*  function_;
@@ -11424,7 +12035,7 @@ namespace exprtk
          typedef expression_node<T>* expression_ptr;
          typedef IFunction ifunction;
 
-         function_N_node(ifunction* func)
+         explicit function_N_node(ifunction* func)
          : function_((0 == func->param_count) ? func : reinterpret_cast<ifunction*>(0))
          {}
 
@@ -11466,17 +12077,6 @@ namespace exprtk
             value_list_.resize(arg_list.size(),std::numeric_limits<T>::quiet_NaN());
          }
 
-        ~vararg_function_node()
-         {
-            for (std::size_t i = 0; i < arg_list_.size(); ++i)
-            {
-               if (arg_list_[i] && !details::is_variable_node(arg_list_[i]))
-               {
-                  destroy_node(arg_list_[i]);
-               }
-            }
-         }
-
          inline bool operator <(const vararg_function_node<T,VarArgFunction>& fn) const
          {
             return this < (&fn);
@@ -11498,6 +12098,22 @@ namespace exprtk
             return expression_node<T>::e_vafunction;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            for (std::size_t i = 0; i < arg_list_.size(); ++i)
+            {
+               if (arg_list_[i] && !details::is_variable_node(arg_list_[i]))
+               {
+                  node_delete_list.push_back(&arg_list_[i]);
+               }
+            }
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(arg_list_);
+         }
+
       private:
 
          inline void populate_value_list() const
@@ -11533,15 +12149,23 @@ namespace exprtk
          typedef std::vector<type_store_t>         typestore_list_t;
          typedef std::vector<range_data_type_t>        range_list_t;
 
-         generic_function_node(const std::vector<expression_ptr>& arg_list,
-                               GenericFunction* func = (GenericFunction*)(0))
+         explicit generic_function_node(const std::vector<expression_ptr>& arg_list,
+                                        GenericFunction* func = reinterpret_cast<GenericFunction*>(0))
          : function_(func),
            arg_list_(arg_list)
          {}
 
          virtual ~generic_function_node()
+         {}
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
          {
-            cleanup_branches::execute(branch_);
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
          virtual bool init_branches()
@@ -11549,7 +12173,7 @@ namespace exprtk
             expr_as_vec1_store_.resize(arg_list_.size(),T(0)               );
             typestore_list_    .resize(arg_list_.size(),type_store_t()     );
             range_list_        .resize(arg_list_.size(),range_data_type_t());
-            branch_            .resize(arg_list_.size(),branch_t((expression_ptr)0,false));
+            branch_            .resize(arg_list_.size(),branch_t(reinterpret_cast<expression_ptr>(0),false));
 
             for (std::size_t i = 0; i < arg_list_.size(); ++i)
             {
@@ -11591,7 +12215,7 @@ namespace exprtk
                   if (0 == (ri = dynamic_cast<range_interface_t*>(arg_list_[i])))
                      return false;
 
-                  range_t& rp = ri->range_ref();
+                  const range_t& rp = ri->range_ref();
 
                   if (
                        rp.const_range() &&
@@ -11670,11 +12294,11 @@ namespace exprtk
 
                if (rdt.range)
                {
-                  range_t&    rp = (*rdt.range);
-                  std::size_t r0 = 0;
-                  std::size_t r1 = 0;
+                  const range_t& rp = (*rdt.range);
+                  std::size_t r0    = 0;
+                  std::size_t r1    = 0;
 
-                  if (rp(r0,r1,rdt.size))
+                  if (rp(r0, r1, rdt.size))
                   {
                      type_store_t& ts = typestore_list_[i];
 
@@ -11740,7 +12364,10 @@ namespace exprtk
                   typedef typename StringFunction::parameter_list_t parameter_list_t;
 
                   const T result = (*gen_function_t::function_)
-                                      (ret_string_, parameter_list_t(gen_function_t::typestore_list_));
+                                      (
+                                        ret_string_,
+                                        parameter_list_t(gen_function_t::typestore_list_)
+                                      );
 
                   range_.n1_c.second  = ret_string_.size() - 1;
                   range_.cache.second = range_.n1_c.second;
@@ -11812,8 +12439,11 @@ namespace exprtk
                {
                   typedef typename GenericFunction::parameter_list_t parameter_list_t;
 
-                  return (*gen_function_t::function_)(param_seq_index_,
-                                                      parameter_list_t(gen_function_t::typestore_list_));
+                  return (*gen_function_t::function_)
+                            (
+                              param_seq_index_,
+                              parameter_list_t(gen_function_t::typestore_list_)
+                            );
                }
             }
 
@@ -11854,9 +12484,12 @@ namespace exprtk
                {
                   typedef typename StringFunction::parameter_list_t parameter_list_t;
 
-                  const T result = (*str_function_t::function_)(param_seq_index_,
-                                                                str_function_t::ret_string_,
-                                                                parameter_list_t(str_function_t::typestore_list_));
+                  const T result = (*str_function_t::function_)
+                                      (
+                                        param_seq_index_,
+                                        str_function_t::ret_string_,
+                                        parameter_list_t(str_function_t::typestore_list_)
+                                      );
 
                   str_function_t::range_.n1_c.second  = str_function_t::ret_string_.size() - 1;
                   str_function_t::range_.cache.second = str_function_t::range_.n1_c.second;
@@ -11951,30 +12584,25 @@ namespace exprtk
 
          typedef expression_node<T>* expression_ptr;
          typedef results_context<T>  results_context_t;
+         typedef std::pair<expression_ptr,bool> branch_t;
 
          return_envelope_node(expression_ptr body, results_context_t& rc)
          : results_context_(&rc  ),
-           return_invoked_ (false),
-           body_           (body ),
-           body_deletable_ (branch_deletable(body_))
-         {}
-
-        ~return_envelope_node()
+           return_invoked_ (false)
          {
-            if (body_ && body_deletable_)
-            {
-               destroy_node(body_);
-            }
+            construct_branch_pair(body_, body);
          }
 
          inline T value() const
          {
+            assert(body_.first);
+
             try
             {
                return_invoked_ = false;
                results_context_->clear();
 
-               return body_->value();
+               return body_.first->value();
             }
             catch(const return_exception&)
             {
@@ -11993,12 +12621,21 @@ namespace exprtk
             return &return_invoked_;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(body_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(body_);
+         }
+
       private:
 
          results_context_t* results_context_;
-         mutable bool       return_invoked_;
-         expression_ptr     body_;
-         const bool         body_deletable_;
+         mutable bool        return_invoked_;
+         branch_t                      body_;
       };
       #endif
 
@@ -12345,11 +12982,23 @@ namespace exprtk
       }
 
       template <typename T>
-      inline T value(T* t)
+      inline T value(std::pair<details::expression_node<T>*,bool> n)
+      {
+         return n.first->value();
+      }
+
+      template <typename T>
+      inline T value(const T* t)
       {
          return (*t);
       }
 
+      template <typename T>
+      inline T value(const T& t)
+      {
+         return t;
+      }
+
       template <typename T>
       struct vararg_add_op : public opr_base<T>
       {
@@ -12597,16 +13246,16 @@ namespace exprtk
          static inline T process_4(const Sequence& arg_list)
          {
             return std::min<T>(
-                        std::min<T>(value(arg_list[0]),value(arg_list[1])),
-                        std::min<T>(value(arg_list[2]),value(arg_list[3])));
+                        std::min<T>(value(arg_list[0]), value(arg_list[1])),
+                        std::min<T>(value(arg_list[2]), value(arg_list[3])));
          }
 
          template <typename Sequence>
          static inline T process_5(const Sequence& arg_list)
          {
             return std::min<T>(
-                   std::min<T>(std::min<T>(value(arg_list[0]),value(arg_list[1])),
-                               std::min<T>(value(arg_list[2]),value(arg_list[3]))),
+                   std::min<T>(std::min<T>(value(arg_list[0]), value(arg_list[1])),
+                               std::min<T>(value(arg_list[2]), value(arg_list[3]))),
                                value(arg_list[4]));
          }
       };
@@ -12668,16 +13317,16 @@ namespace exprtk
          static inline T process_4(const Sequence& arg_list)
          {
             return std::max<T>(
-                        std::max<T>(value(arg_list[0]),value(arg_list[1])),
-                        std::max<T>(value(arg_list[2]),value(arg_list[3])));
+                        std::max<T>(value(arg_list[0]), value(arg_list[1])),
+                        std::max<T>(value(arg_list[2]), value(arg_list[3])));
          }
 
          template <typename Sequence>
          static inline T process_5(const Sequence& arg_list)
          {
             return std::max<T>(
-                   std::max<T>(std::max<T>(value(arg_list[0]),value(arg_list[1])),
-                               std::max<T>(value(arg_list[2]),value(arg_list[3]))),
+                   std::max<T>(std::max<T>(value(arg_list[0]), value(arg_list[1])),
+                               std::max<T>(value(arg_list[2]), value(arg_list[3]))),
                                value(arg_list[4]));
          }
       };
@@ -13181,7 +13830,7 @@ namespace exprtk
 
             for (std::size_t i = 1; i < vec_size; ++i)
             {
-               T v_i = vec[i];
+               const T v_i = vec[i];
 
                if (v_i < result)
                   result = v_i;
@@ -13205,7 +13854,7 @@ namespace exprtk
 
             for (std::size_t i = 1; i < vec_size; ++i)
             {
-               T v_i = vec[i];
+               const T v_i = vec[i];
 
                if (v_i > result)
                   result = v_i;
@@ -13296,8 +13945,8 @@ namespace exprtk
       {
       public:
 
-       virtual ~cob_base_node()
-       {}
+         virtual ~cob_base_node()
+         {}
 
          inline virtual operator_type operation() const
          {
@@ -13516,24 +14165,17 @@ namespace exprtk
       public:
 
          typedef expression_node<T>* expression_ptr;
+         typedef std::pair<expression_ptr,bool> branch_t;
          typedef Operation operation_t;
 
-         explicit unary_branch_node(expression_ptr brnch)
-         : branch_(brnch),
-           branch_deletable_(branch_deletable(branch_))
-         {}
-
-        ~unary_branch_node()
+         explicit unary_branch_node(expression_ptr branch)
          {
-            if (branch_ && branch_deletable_)
-            {
-               destroy_node(branch_);
-            }
+            construct_branch_pair(branch_, branch);
          }
 
          inline T value() const
          {
-            return Operation::process(branch_->value());
+            return Operation::process(branch_.first->value());
          }
 
          inline typename expression_node<T>::node_type type() const
@@ -13548,12 +14190,22 @@ namespace exprtk
 
          inline expression_node<T>* branch(const std::size_t&) const
          {
-            return branch_;
+            return branch_.first;
          }
 
          inline void release()
          {
-            branch_deletable_ = false;
+            branch_.second = false;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
@@ -13561,8 +14213,7 @@ namespace exprtk
          unary_branch_node(unary_branch_node<T,Operation>&);
          unary_branch_node<T,Operation>& operator=(unary_branch_node<T,Operation>&);
 
-         expression_ptr branch_;
-         bool           branch_deletable_;
+         branch_t branch_;
       };
 
       template <typename T> struct is_const                { enum {result = 0}; };
@@ -14392,7 +15043,7 @@ namespace exprtk
 
          inline T3 t3() const
          {
-            return t2_;
+            return t3_;
          }
 
          std::string type_id() const
@@ -14623,20 +15274,16 @@ namespace exprtk
          typedef Operation operation_t;
 
          // variable op constant node
-         explicit vob_node(const T& var, const expression_ptr brnch)
+         explicit vob_node(const T& var, const expression_ptr branch)
          : v_(var)
          {
-            init_branches<1>(branch_,brnch);
-         }
-
-        ~vob_node()
-         {
-            cleanup_branches::execute<T,1>(branch_);
+            construct_branch_pair(branch_, branch);
          }
 
          inline T value() const
          {
-            return Operation::process(v_,branch_[0].first->value());
+            assert(branch_.first);
+            return Operation::process(v_,branch_.first->value());
          }
 
          inline operator_type operation() const
@@ -14651,7 +15298,17 @@ namespace exprtk
 
          inline expression_node<T>* branch(const std::size_t&) const
          {
-            return branch_[0].first;
+            return branch_.first;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
@@ -14660,7 +15317,7 @@ namespace exprtk
          vob_node<T,Operation>& operator=(const vob_node<T,Operation>&);
 
          const T& v_;
-         branch_t branch_[1];
+         branch_t branch_;
       };
 
       template <typename T, typename Operation>
@@ -14673,20 +15330,16 @@ namespace exprtk
          typedef Operation operation_t;
 
          // variable op constant node
-         explicit bov_node(const expression_ptr brnch, const T& var)
+         explicit bov_node(const expression_ptr branch, const T& var)
          : v_(var)
          {
-            init_branches<1>(branch_,brnch);
-         }
-
-        ~bov_node()
-         {
-            cleanup_branches::execute<T,1>(branch_);
+            construct_branch_pair(branch_, branch);
          }
 
          inline T value() const
          {
-            return Operation::process(branch_[0].first->value(),v_);
+            assert(branch_.first);
+            return Operation::process(branch_.first->value(),v_);
          }
 
          inline operator_type operation() const
@@ -14701,7 +15354,17 @@ namespace exprtk
 
          inline expression_node<T>* branch(const std::size_t&) const
          {
-            return branch_[0].first;
+            return branch_.first;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
@@ -14710,7 +15373,7 @@ namespace exprtk
          bov_node<T,Operation>& operator=(const bov_node<T,Operation>&);
 
          const T& v_;
-         branch_t branch_[1];
+         branch_t branch_;
       };
 
       template <typename T, typename Operation>
@@ -14723,20 +15386,16 @@ namespace exprtk
          typedef Operation operation_t;
 
          // variable op constant node
-         explicit cob_node(const T const_var, const expression_ptr brnch)
+         explicit cob_node(const T const_var, const expression_ptr branch)
          : c_(const_var)
          {
-            init_branches<1>(branch_,brnch);
-         }
-
-        ~cob_node()
-         {
-            cleanup_branches::execute<T,1>(branch_);
+            construct_branch_pair(branch_, branch);
          }
 
          inline T value() const
          {
-            return Operation::process(c_,branch_[0].first->value());
+            assert(branch_.first);
+            return Operation::process(c_,branch_.first->value());
          }
 
          inline operator_type operation() const
@@ -14756,13 +15415,23 @@ namespace exprtk
 
          inline expression_node<T>* branch(const std::size_t&) const
          {
-            return branch_[0].first;
+            return branch_.first;
          }
 
          inline expression_node<T>* move_branch(const std::size_t&)
          {
-            branch_[0].second = false;
-            return branch_[0].first;
+            branch_.second = false;
+            return branch_.first;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
@@ -14771,7 +15440,7 @@ namespace exprtk
          cob_node<T,Operation>& operator=(const cob_node<T,Operation>&);
 
          const T  c_;
-         branch_t branch_[1];
+         branch_t branch_;
       };
 
       template <typename T, typename Operation>
@@ -14784,20 +15453,16 @@ namespace exprtk
          typedef Operation operation_t;
 
          // variable op constant node
-         explicit boc_node(const expression_ptr brnch, const T const_var)
+         explicit boc_node(const expression_ptr branch, const T const_var)
          : c_(const_var)
          {
-            init_branches<1>(branch_,brnch);
-         }
-
-        ~boc_node()
-         {
-            cleanup_branches::execute<T,1>(branch_);
+            construct_branch_pair(branch_, branch);
          }
 
          inline T value() const
          {
-            return Operation::process(branch_[0].first->value(),c_);
+            assert(branch_.first);
+            return Operation::process(branch_.first->value(),c_);
          }
 
          inline operator_type operation() const
@@ -14817,13 +15482,23 @@ namespace exprtk
 
          inline expression_node<T>* branch(const std::size_t&) const
          {
-            return branch_[0].first;
+            return branch_.first;
          }
 
          inline expression_node<T>* move_branch(const std::size_t&)
          {
-            branch_[0].second = false;
-            return branch_[0].first;
+            branch_.second = false;
+            return branch_.first;
+         }
+
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
          }
 
       private:
@@ -14832,7 +15507,7 @@ namespace exprtk
          boc_node<T,Operation>& operator=(const boc_node<T,Operation>&);
 
          const T  c_;
-         branch_t branch_[1];
+         branch_t branch_;
       };
 
       #ifndef exprtk_disable_string_capabilities
@@ -15158,8 +15833,8 @@ namespace exprtk
                std::size_t str1_r0 = 0;
                std::size_t str1_r1 = 0;
 
-               range_t& range0 = (*str0_range_ptr_);
-               range_t& range1 = (*str1_range_ptr_);
+               const range_t& range0 = (*str0_range_ptr_);
+               const range_t& range1 = (*str1_range_ptr_);
 
                if (
                     range0(str0_r0, str0_r1, str0_base_ptr_->size()) &&
@@ -15294,19 +15969,15 @@ namespace exprtk
          typedef std::pair<expression_ptr, bool> branch_t;
          typedef PowOp operation_t;
 
-         explicit bipow_node(expression_ptr brnch)
+         explicit bipow_node(expression_ptr branch)
          {
-            init_branches<1>(branch_, brnch);
-         }
-
-        ~bipow_node()
-         {
-            cleanup_branches::execute<T,1>(branch_);
+            construct_branch_pair(branch_, branch);
          }
 
          inline T value() const
          {
-            return PowOp::result(branch_[0].first->value());
+            assert(branch_.first);
+            return PowOp::result(branch_.first->value());
          }
 
          inline typename expression_node<T>::node_type type() const
@@ -15314,12 +15985,22 @@ namespace exprtk
             return expression_node<T>::e_ipow;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
       private:
 
          bipow_node(const bipow_node<T,PowOp>&);
          bipow_node<T,PowOp>& operator=(const bipow_node<T,PowOp>&);
 
-         branch_t branch_[1];
+         branch_t branch_;
       };
 
       template <typename T, typename PowOp>
@@ -15361,19 +16042,15 @@ namespace exprtk
          typedef std::pair<expression_ptr, bool> branch_t;
          typedef PowOp operation_t;
 
-         explicit bipowninv_node(expression_ptr brnch)
+         explicit bipowninv_node(expression_ptr branch)
          {
-            init_branches<1>(branch_, brnch);
-         }
-
-        ~bipowninv_node()
-         {
-            cleanup_branches::execute<T,1>(branch_);
+            construct_branch_pair(branch_, branch);
          }
 
          inline T value() const
          {
-            return (T(1) / PowOp::result(branch_[0].first->value()));
+            assert(branch_.first);
+            return (T(1) / PowOp::result(branch_.first->value()));
          }
 
          inline typename expression_node<T>::node_type type() const
@@ -15381,12 +16058,22 @@ namespace exprtk
             return expression_node<T>::e_ipowinv;
          }
 
+         void collect_nodes(typename expression_node<T>::noderef_list_t& node_delete_list)
+         {
+            expression_node<T>::ndb_t::template collect(branch_, node_delete_list);
+         }
+
+         std::size_t node_depth() const
+         {
+            return expression_node<T>::ndb_t::compute_node_depth(branch_);
+         }
+
       private:
 
          bipowninv_node(const bipowninv_node<T,PowOp>&);
          bipowninv_node<T,PowOp>& operator=(const bipowninv_node<T,PowOp>&);
 
-         branch_t branch_[1];
+         branch_t branch_;
       };
 
       template <typename T>
@@ -15535,37 +16222,55 @@ namespace exprtk
          template <typename ResultNode, typename OpType, typename ExprNode>
          inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[1])
          {
-            return allocate<ResultNode>(operation, branch[0]);
+            expression_node<typename ResultNode::value_type>* result =
+               allocate<ResultNode>(operation, branch[0]);
+            result->node_depth();
+            return result;
          }
 
          template <typename ResultNode, typename OpType, typename ExprNode>
          inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[2])
          {
-            return allocate<ResultNode>(operation, branch[0], branch[1]);
+            expression_node<typename ResultNode::value_type>* result =
+               allocate<ResultNode>(operation, branch[0], branch[1]);
+            result->node_depth();
+            return result;
          }
 
          template <typename ResultNode, typename OpType, typename ExprNode>
          inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[3])
          {
-            return allocate<ResultNode>(operation, branch[0], branch[1], branch[2]);
+            expression_node<typename ResultNode::value_type>* result =
+               allocate<ResultNode>(operation, branch[0], branch[1], branch[2]);
+            result->node_depth();
+            return result;
          }
 
          template <typename ResultNode, typename OpType, typename ExprNode>
          inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[4])
          {
-            return allocate<ResultNode>(operation, branch[0], branch[1], branch[2], branch[3]);
+            expression_node<typename ResultNode::value_type>* result =
+               allocate<ResultNode>(operation, branch[0], branch[1], branch[2], branch[3]);
+            result->node_depth();
+            return result;
          }
 
          template <typename ResultNode, typename OpType, typename ExprNode>
          inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[5])
          {
-            return allocate<ResultNode>(operation, branch[0],branch[1], branch[2], branch[3], branch[4]);
+            expression_node<typename ResultNode::value_type>* result =
+               allocate<ResultNode>(operation, branch[0],branch[1], branch[2], branch[3], branch[4]);
+            result->node_depth();
+            return result;
          }
 
          template <typename ResultNode, typename OpType, typename ExprNode>
          inline expression_node<typename ResultNode::value_type>* allocate(OpType& operation, ExprNode (&branch)[6])
          {
-            return allocate<ResultNode>(operation, branch[0], branch[1], branch[2], branch[3], branch[4], branch[5]);
+            expression_node<typename ResultNode::value_type>* result =
+               allocate<ResultNode>(operation, branch[0], branch[1], branch[2], branch[3], branch[4], branch[5]);
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type>
@@ -15580,89 +16285,128 @@ namespace exprtk
                    template <typename, typename> class Sequence>
          inline expression_node<typename node_type::value_type>* allocate(const Sequence<Type,Allocator>& seq) const
          {
-            return (new node_type(seq));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(seq));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type, typename T1>
          inline expression_node<typename node_type::value_type>* allocate(T1& t1) const
          {
-            return (new node_type(t1));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type, typename T1>
          inline expression_node<typename node_type::value_type>* allocate_c(const T1& t1) const
          {
-            return (new node_type(t1));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
                    typename T1, typename T2>
          inline expression_node<typename node_type::value_type>* allocate(const T1& t1, const T2& t2) const
          {
-            return (new node_type(t1, t2));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
                    typename T1, typename T2>
          inline expression_node<typename node_type::value_type>* allocate_cr(const T1& t1, T2& t2) const
          {
-            return (new node_type(t1, t2));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
                    typename T1, typename T2>
          inline expression_node<typename node_type::value_type>* allocate_rc(T1& t1, const T2& t2) const
          {
-            return (new node_type(t1, t2));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
                    typename T1, typename T2>
          inline expression_node<typename node_type::value_type>* allocate_rr(T1& t1, T2& t2) const
          {
-            return (new node_type(t1, t2));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
                    typename T1, typename T2>
          inline expression_node<typename node_type::value_type>* allocate_tt(T1 t1, T2 t2) const
          {
-            return (new node_type(t1, t2));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
                    typename T1, typename T2, typename T3>
          inline expression_node<typename node_type::value_type>* allocate_ttt(T1 t1, T2 t2, T3 t3) const
          {
-            return (new node_type(t1, t2, t3));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
                    typename T1, typename T2, typename T3, typename T4>
          inline expression_node<typename node_type::value_type>* allocate_tttt(T1 t1, T2 t2, T3 t3, T4 t4) const
          {
-            return (new node_type(t1, t2, t3, t4));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
                    typename T1, typename T2, typename T3>
          inline expression_node<typename node_type::value_type>* allocate_rrr(T1& t1, T2& t2, T3& t3) const
          {
-            return (new node_type(t1, t2, t3));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
                    typename T1, typename T2, typename T3, typename T4>
          inline expression_node<typename node_type::value_type>* allocate_rrrr(T1& t1, T2& t2, T3& t3, T4& t4) const
          {
-            return (new node_type(t1, t2, t3, t4));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
                    typename T1, typename T2, typename T3, typename T4, typename T5>
          inline expression_node<typename node_type::value_type>* allocate_rrrrr(T1& t1, T2& t2, T3& t3, T4& t4, T5& t5) const
          {
-            return (new node_type(t1, t2, t3, t4, t5));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
@@ -15670,7 +16414,10 @@ namespace exprtk
          inline expression_node<typename node_type::value_type>* allocate(const T1& t1, const T2& t2,
                                                                           const T3& t3) const
          {
-            return (new node_type(t1, t2, t3));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
@@ -15679,7 +16426,10 @@ namespace exprtk
          inline expression_node<typename node_type::value_type>* allocate(const T1& t1, const T2& t2,
                                                                           const T3& t3, const T4& t4) const
          {
-            return (new node_type(t1, t2, t3, t4));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
@@ -15689,7 +16439,10 @@ namespace exprtk
                                                                           const T3& t3, const T4& t4,
                                                                           const T5& t5) const
          {
-            return (new node_type(t1, t2, t3, t4, t5));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
@@ -15699,7 +16452,10 @@ namespace exprtk
                                                                           const T3& t3, const T4& t4,
                                                                           const T5& t5, const T6& t6) const
          {
-            return (new node_type(t1, t2, t3, t4, t5, t6));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
@@ -15711,7 +16467,10 @@ namespace exprtk
                                                                           const T5& t5, const T6& t6,
                                                                           const T7& t7) const
          {
-            return (new node_type(t1, t2, t3, t4, t5, t6, t7));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6, t7));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
@@ -15724,7 +16483,10 @@ namespace exprtk
                                                                           const T5& t5, const T6& t6,
                                                                           const T7& t7, const T8& t8) const
          {
-            return (new node_type(t1, t2, t3, t4, t5, t6, t7, t8));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6, t7, t8));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
@@ -15738,7 +16500,10 @@ namespace exprtk
                                                                           const T7& t7, const T8& t8,
                                                                           const T9& t9) const
          {
-            return (new node_type(t1, t2, t3, t4, t5, t6, t7, t8, t9));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6, t7, t8, t9));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
@@ -15753,14 +16518,20 @@ namespace exprtk
                                                                           const T7& t7, const  T8&  t8,
                                                                           const T9& t9, const T10& t10) const
          {
-            return (new node_type(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6, t7, t8, t9, t10));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
                    typename T1, typename T2, typename T3>
          inline expression_node<typename node_type::value_type>* allocate_type(T1 t1, T2 t2, T3 t3) const
          {
-            return (new node_type(t1, t2, t3));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
@@ -15769,7 +16540,10 @@ namespace exprtk
          inline expression_node<typename node_type::value_type>* allocate_type(T1 t1, T2 t2,
                                                                                T3 t3, T4 t4) const
          {
-            return (new node_type(t1, t2, t3, t4));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
@@ -15780,7 +16554,10 @@ namespace exprtk
                                                                                T3 t3, T4 t4,
                                                                                T5 t5) const
          {
-            return (new node_type(t1, t2, t3, t4, t5));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
@@ -15791,7 +16568,10 @@ namespace exprtk
                                                                                T3 t3, T4 t4,
                                                                                T5 t5, T6 t6) const
          {
-            return (new node_type(t1, t2, t3, t4, t5, t6));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6));
+            result->node_depth();
+            return result;
          }
 
          template <typename node_type,
@@ -15803,12 +16583,19 @@ namespace exprtk
                                                                                T5 t5, T6 t6,
                                                                                T7 t7) const
          {
-            return (new node_type(t1, t2, t3, t4, t5, t6, t7));
+            expression_node<typename node_type::value_type>*
+            result = (new node_type(t1, t2, t3, t4, t5, t6, t7));
+            result->node_depth();
+            return result;
          }
 
          template <typename T>
          void inline free(expression_node<T>*& e) const
          {
+            exprtk_debug(("node_allocator::free() - deleting expression_node "
+                          "type: %03d addr: %p\n",
+                          static_cast<int>(e->type()),
+                          reinterpret_cast<void*>(e)));
             delete e;
             e = 0;
          }
@@ -15971,83 +16758,84 @@ namespace exprtk
       virtual ~ifunction()
       {}
 
-      #define empty_method_body                      \
+      #define empty_method_body(N)                   \
       {                                              \
+         exprtk_debug(("ifunction::operator() - Operator(" #N ") has not been overridden\n")); \
          return std::numeric_limits<T>::quiet_NaN(); \
       }                                              \
 
       inline virtual T operator() ()
-      empty_method_body
+      empty_method_body(0)
 
-       inline virtual T operator() (const T&)
-      empty_method_body
+      inline virtual T operator() (const T&)
+      empty_method_body(1)
 
-       inline virtual T operator() (const T&,const T&)
-      empty_method_body
+      inline virtual T operator() (const T&,const T&)
+      empty_method_body(2)
 
-       inline virtual T operator() (const T&, const T&, const T&)
-      empty_method_body
+      inline virtual T operator() (const T&, const T&, const T&)
+      empty_method_body(3)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(4)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(5)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(6)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(7)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(8)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(9)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(10)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
-                                  const T&)
-      empty_method_body
+                                   const T&)
+      empty_method_body(11)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
                                    const T&, const T&)
-      empty_method_body
+      empty_method_body(12)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
                                    const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(13)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
                                    const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(14)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
                                    const T&, const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(15)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
                                    const T&, const T&, const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(16)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
                                    const T&, const T&, const T&, const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(17)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
                                    const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(18)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
                                    const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(19)
 
       inline virtual T operator() (const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&,
                                    const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&, const T&)
-      empty_method_body
+      empty_method_body(20)
 
       #undef empty_method_body
 
@@ -16064,7 +16852,7 @@ namespace exprtk
 
       inline virtual T operator() (const std::vector<T>&)
       {
-         exprtk_debug(("ivararg_function::operator() - Operator has not been overridden.\n"));
+         exprtk_debug(("ivararg_function::operator() - Operator has not been overridden\n"));
          return std::numeric_limits<T>::quiet_NaN();
       }
    };
@@ -16095,7 +16883,7 @@ namespace exprtk
 
       #define igeneric_function_empty_body(N)        \
       {                                              \
-         exprtk_debug(("igeneric_function::operator() - Operator has not been overridden. ["#N"]\n")); \
+         exprtk_debug(("igeneric_function::operator() - Operator(" #N ") has not been overridden\n")); \
          return std::numeric_limits<T>::quiet_NaN(); \
       }                                              \
 
@@ -16150,7 +16938,7 @@ namespace exprtk
        {
           using exprtk::ifunction<T>::operator();
 
-          freefunc00(ff00_functor ff) : exprtk::ifunction<T>(0), f(ff) {}
+          explicit freefunc00(ff00_functor ff) : exprtk::ifunction<T>(0), f(ff) {}
           inline T operator() ()
           { return f(); }
           ff00_functor f;
@@ -16160,7 +16948,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc01(ff01_functor ff) : exprtk::ifunction<T>(1), f(ff) {}
+         explicit freefunc01(ff01_functor ff) : exprtk::ifunction<T>(1), f(ff) {}
          inline T operator() (const T& v0)
          { return f(v0); }
          ff01_functor f;
@@ -16170,7 +16958,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc02(ff02_functor ff) : exprtk::ifunction<T>(2), f(ff) {}
+         explicit freefunc02(ff02_functor ff) : exprtk::ifunction<T>(2), f(ff) {}
          inline T operator() (const T& v0, const T& v1)
          { return f(v0, v1); }
          ff02_functor f;
@@ -16180,7 +16968,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc03(ff03_functor ff) : exprtk::ifunction<T>(3), f(ff) {}
+         explicit freefunc03(ff03_functor ff) : exprtk::ifunction<T>(3), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2)
          { return f(v0, v1, v2); }
          ff03_functor f;
@@ -16190,7 +16978,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc04(ff04_functor ff) : exprtk::ifunction<T>(4), f(ff) {}
+         explicit freefunc04(ff04_functor ff) : exprtk::ifunction<T>(4), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3)
          { return f(v0, v1, v2, v3); }
          ff04_functor f;
@@ -16200,7 +16988,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc05(ff05_functor ff) : exprtk::ifunction<T>(5), f(ff) {}
+         explicit freefunc05(ff05_functor ff) : exprtk::ifunction<T>(5), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4)
          { return f(v0, v1, v2, v3, v4); }
          ff05_functor f;
@@ -16210,7 +16998,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc06(ff06_functor ff) : exprtk::ifunction<T>(6), f(ff) {}
+         explicit freefunc06(ff06_functor ff) : exprtk::ifunction<T>(6), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4, const T& v5)
          { return f(v0, v1, v2, v3, v4, v5); }
          ff06_functor f;
@@ -16220,7 +17008,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc07(ff07_functor ff) : exprtk::ifunction<T>(7), f(ff) {}
+         explicit freefunc07(ff07_functor ff) : exprtk::ifunction<T>(7), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
                               const T& v5, const T& v6)
          { return f(v0, v1, v2, v3, v4, v5, v6); }
@@ -16231,7 +17019,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc08(ff08_functor ff) : exprtk::ifunction<T>(8), f(ff) {}
+         explicit freefunc08(ff08_functor ff) : exprtk::ifunction<T>(8), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
                               const T& v5, const T& v6, const T& v7)
          { return f(v0, v1, v2, v3, v4, v5, v6, v7); }
@@ -16242,7 +17030,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc09(ff09_functor ff) : exprtk::ifunction<T>(9), f(ff) {}
+         explicit freefunc09(ff09_functor ff) : exprtk::ifunction<T>(9), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
                               const T& v5, const T& v6, const T& v7, const T& v8)
          { return f(v0, v1, v2, v3, v4, v5, v6, v7, v8); }
@@ -16253,7 +17041,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc10(ff10_functor ff) : exprtk::ifunction<T>(10), f(ff) {}
+         explicit freefunc10(ff10_functor ff) : exprtk::ifunction<T>(10), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
                               const T& v5, const T& v6, const T& v7, const T& v8, const T& v9)
          { return f(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9); }
@@ -16264,7 +17052,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc11(ff11_functor ff) : exprtk::ifunction<T>(11), f(ff) {}
+         explicit freefunc11(ff11_functor ff) : exprtk::ifunction<T>(11), f(ff) {}
          inline T operator() (const T& v0, const T& v1, const T& v2, const T& v3, const T& v4,
                               const T& v5, const T& v6, const T& v7, const T& v8, const T& v9, const T& v10)
          { return f(v0, v1, v2, v3, v4, v5, v6, v7, v8, v9, v10); }
@@ -16275,7 +17063,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc12(ff12_functor ff) : exprtk::ifunction<T>(12), f(ff) {}
+         explicit freefunc12(ff12_functor ff) : exprtk::ifunction<T>(12), f(ff) {}
          inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04,
                               const T& v05, const T& v06, const T& v07, const T& v08, const T& v09,
                               const T& v10, const T& v11)
@@ -16287,7 +17075,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc13(ff13_functor ff) : exprtk::ifunction<T>(13), f(ff) {}
+         explicit freefunc13(ff13_functor ff) : exprtk::ifunction<T>(13), f(ff) {}
          inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04,
                               const T& v05, const T& v06, const T& v07, const T& v08, const T& v09,
                               const T& v10, const T& v11, const T& v12)
@@ -16299,7 +17087,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc14(ff14_functor ff) : exprtk::ifunction<T>(14), f(ff) {}
+         explicit freefunc14(ff14_functor ff) : exprtk::ifunction<T>(14), f(ff) {}
          inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04,
                               const T& v05, const T& v06, const T& v07, const T& v08, const T& v09,
                               const T& v10, const T& v11, const T& v12, const T& v13)
@@ -16311,7 +17099,7 @@ namespace exprtk
       {
          using exprtk::ifunction<T>::operator();
 
-         freefunc15(ff15_functor ff) : exprtk::ifunction<T>(15), f(ff) {}
+         explicit freefunc15(ff15_functor ff) : exprtk::ifunction<T>(15), f(ff) {}
          inline T operator() (const T& v00, const T& v01, const T& v02, const T& v03, const T& v04,
                               const T& v05, const T& v06, const T& v07, const T& v08, const T& v09,
                               const T& v10, const T& v11, const T& v12, const T& v13, const T& v14)
@@ -16348,6 +17136,27 @@ namespace exprtk
          : size(0)
          {}
 
+         struct deleter
+         {
+            #define exprtk_define_process(Type)                  \
+            static inline void process(std::pair<bool,Type*>& n) \
+            {                                                    \
+               delete n.second;                                  \
+            }                                                    \
+
+            exprtk_define_process(variable_node_t )
+            exprtk_define_process(vector_t        )
+            #ifndef exprtk_disable_string_capabilities
+            exprtk_define_process(stringvar_node_t)
+            #endif
+
+            #undef exprtk_define_process
+
+            template <typename DeleteType>
+            static inline void process(std::pair<bool,DeleteType*>&)
+            {}
+         };
+
          inline bool symbol_exists(const std::string& symbol_name) const
          {
             if (symbol_name.empty())
@@ -16583,16 +17392,6 @@ namespace exprtk
 
             if (map.end() != itr)
             {
-               struct deleter
-               {
-                  static inline void process(std::pair<bool,variable_node_t*>& n)  { delete n.second; }
-                  static inline void process(std::pair<bool,vector_t*>& n)         { delete n.second; }
-                  #ifndef exprtk_disable_string_capabilities
-                  static inline void process(std::pair<bool,stringvar_node_t*>& n) { delete n.second; }
-                  #endif
-                  static inline void process(std::pair<bool,function_t*>&)         {                  }
-               };
-
                if (delete_node)
                {
                   deleter::process((*itr).second);
@@ -16629,16 +17428,6 @@ namespace exprtk
 
          inline void clear(const bool delete_node = true)
          {
-            struct deleter
-            {
-               static inline void process(std::pair<bool,variable_node_t*>& n)  { delete n.second; }
-               static inline void process(std::pair<bool,vector_t*>& n)         { delete n.second; }
-               static inline void process(std::pair<bool,function_t*>&)         {                  }
-               #ifndef exprtk_disable_string_capabilities
-               static inline void process(std::pair<bool,stringvar_node_t*>& n) { delete n.second; }
-               #endif
-            };
-
             if (!map.empty())
             {
                if (delete_node)
@@ -16704,20 +17493,20 @@ namespace exprtk
          }
       };
 
-      typedef details::expression_node<T>* expression_ptr;
-      typedef typename details::variable_node<T> variable_t;
-      typedef typename details::vector_holder<T> vector_holder_t;
-      typedef variable_t* variable_ptr;
+      typedef details::expression_node<T>*        expression_ptr;
+      typedef typename details::variable_node<T>  variable_t;
+      typedef typename details::vector_holder<T>  vector_holder_t;
+      typedef variable_t*                         variable_ptr;
       #ifndef exprtk_disable_string_capabilities
       typedef typename details::stringvar_node<T> stringvar_t;
-      typedef stringvar_t* stringvar_ptr;
+      typedef stringvar_t*                        stringvar_ptr;
       #endif
-      typedef ifunction        <T> function_t;
-      typedef ivararg_function <T> vararg_function_t;
-      typedef igeneric_function<T> generic_function_t;
-      typedef function_t* function_ptr;
-      typedef vararg_function_t*  vararg_function_ptr;
-      typedef generic_function_t* generic_function_ptr;
+      typedef ifunction        <T>                function_t;
+      typedef ivararg_function <T>                vararg_function_t;
+      typedef igeneric_function<T>                generic_function_t;
+      typedef function_t*                         function_ptr;
+      typedef vararg_function_t*                  vararg_function_ptr;
+      typedef generic_function_t*                 generic_function_ptr;
 
       static const std::size_t lut_size = 256;
 
@@ -16726,16 +17515,16 @@ namespace exprtk
       {
          struct st_data
          {
-            type_store<typename details::variable_node<T>,T> variable_store;
+            type_store<variable_t        , T                 > variable_store;
+            type_store<function_t        , function_t        > function_store;
+            type_store<vararg_function_t , vararg_function_t > vararg_function_store;
+            type_store<generic_function_t, generic_function_t> generic_function_store;
+            type_store<generic_function_t, generic_function_t> string_function_store;
+            type_store<generic_function_t, generic_function_t> overload_function_store;
+            type_store<vector_holder_t   , vector_holder_t   > vector_store;
             #ifndef exprtk_disable_string_capabilities
-            type_store<typename details::stringvar_node<T>,std::string> stringvar_store;
+            type_store<stringvar_t       , std::string       > stringvar_store;
             #endif
-            type_store<ifunction<T>,ifunction<T> >                 function_store;
-            type_store<ivararg_function <T>,ivararg_function <T> > vararg_function_store;
-            type_store<igeneric_function<T>,igeneric_function<T> > generic_function_store;
-            type_store<igeneric_function<T>,igeneric_function<T> > string_function_store;
-            type_store<igeneric_function<T>,igeneric_function<T> > overload_function_store;
-            type_store<vector_holder_t,vector_holder_t>            vector_store;
 
             st_data()
             {
@@ -16785,7 +17574,7 @@ namespace exprtk
            data_(st_data::create())
          {}
 
-         control_block(st_data* data)
+         explicit control_block(st_data* data)
          : ref_count(1),
            data_(data)
          {}
@@ -17117,7 +17906,7 @@ namespace exprtk
          else if (symbol_exists(variable_name))
             return false;
          else
-            return local_data().variable_store.add(variable_name,t,is_constant);
+            return local_data().variable_store.add(variable_name, t, is_constant);
       }
 
       inline bool add_constant(const std::string& constant_name, const T& value)
@@ -17132,7 +17921,7 @@ namespace exprtk
          local_data().local_symbol_list_.push_back(value);
          T& t = local_data().local_symbol_list_.back();
 
-         return add_variable(constant_name,t,true);
+         return add_variable(constant_name, t, true);
       }
 
       #ifndef exprtk_disable_string_capabilities
@@ -17145,7 +17934,7 @@ namespace exprtk
          else if (symbol_exists(stringvar_name))
             return false;
          else
-            return local_data().stringvar_store.add(stringvar_name,s,is_constant);
+            return local_data().stringvar_store.add(stringvar_name, s, is_constant);
       }
       #endif
 
@@ -17181,30 +17970,22 @@ namespace exprtk
             return false;
          else if (symbol_exists(function_name))
             return false;
-         else if (
-                   (
-                     (generic_function_t::e_rtrn_scalar == function.rtrn_type) ||
-                     (generic_function_t::e_rtrn_string == function.rtrn_type)
-                   ) &&
-                   std::string::npos != function.parameter_sequence.find_first_not_of("STVZ*?|")
-                 )
-            return false;
-         else if (
-                   (generic_function_t::e_rtrn_overload  == function.rtrn_type) &&
-                   std::string::npos != function.parameter_sequence.find_first_not_of("STVZ*?|:")
-                 )
-            return false;
-
-         switch (function.rtrn_type)
+         else
          {
-            case generic_function_t::e_rtrn_scalar :
-               return local_data().generic_function_store.add(function_name,function);
+            switch (function.rtrn_type)
+            {
+               case generic_function_t::e_rtrn_scalar :
+                  return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ?
+                         local_data().generic_function_store.add(function_name,function) : false;
 
-            case generic_function_t::e_rtrn_string :
-               return local_data().string_function_store.add(function_name,function);
+               case generic_function_t::e_rtrn_string :
+                  return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ?
+                         local_data().string_function_store.add(function_name,function)  : false;
 
-            case generic_function_t::e_rtrn_overload :
-               return local_data().overload_function_store.add(function_name,function);
+               case generic_function_t::e_rtrn_overload :
+                  return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|:")) ?
+                         local_data().overload_function_store.add(function_name,function) : false;
+            }
          }
 
          return false;
@@ -17270,30 +18051,22 @@ namespace exprtk
             return false;
          else if (symbol_exists(function_name,false))
             return false;
-         else if (
-                   (
-                     (generic_function_t::e_rtrn_scalar == function.rtrn_type) ||
-                     (generic_function_t::e_rtrn_string == function.rtrn_type)
-                   ) &&
-                   std::string::npos != function.parameter_sequence.find_first_not_of("STV*?|")
-                 )
-            return false;
-         else if (
-                   generic_function_t::e_rtrn_overload &&
-                   std::string::npos != function.parameter_sequence.find_first_not_of("STV*?|:")
-                 )
-            return false;
-
-         switch (function.rtrn_type)
+         else
          {
-            case generic_function_t::e_rtrn_scalar :
-               return local_data().generic_function_store.add(function_name,function);
+            switch (function.rtrn_type)
+            {
+               case generic_function_t::e_rtrn_scalar :
+                  return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ?
+                         local_data().generic_function_store.add(function_name,function) : false;
 
-            case generic_function_t::e_rtrn_string :
-               return local_data().string_function_store.add(function_name,function);
+               case generic_function_t::e_rtrn_string :
+                  return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|")) ?
+                         local_data().string_function_store.add(function_name,function)  : false;
 
-            case generic_function_t::e_rtrn_overload :
-               return local_data().overload_function_store.add(function_name,function);
+               case generic_function_t::e_rtrn_overload :
+                  return (std::string::npos == function.parameter_sequence.find_first_not_of("STVZ*?|:")) ?
+                         local_data().overload_function_store.add(function_name,function) : false;
+            }
          }
 
          return false;
@@ -17323,7 +18096,7 @@ namespace exprtk
          else if (0 == v_size)
             return false;
          else
-            return local_data().vector_store.add(vector_name,v,v_size);
+            return local_data().vector_store.add(vector_name, v, v_size);
       }
 
       template <typename Allocator>
@@ -17686,7 +18459,7 @@ namespace exprtk
                     ('_' != symbol[i])
                   )
                {
-                  if (('.' == symbol[i]) && (i < (symbol.size() - 1)))
+                  if ((i < (symbol.size() - 1)) && ('.' == symbol[i]))
                      continue;
                   else
                      return false;
@@ -17712,7 +18485,7 @@ namespace exprtk
                     ('_' != symbol[i])
                   )
                {
-                  if (('.' == symbol[i]) && (i < (symbol.size() - 1)))
+                  if ((i < (symbol.size() - 1)) && ('.' == symbol[i]))
                      continue;
                   else
                      return false;
@@ -17794,7 +18567,7 @@ namespace exprtk
            return_invoked(&retinv_null)
          {}
 
-         control_block(expression_ptr e)
+         explicit control_block(expression_ptr e)
          : ref_count(1),
            expr     (e),
            results  (0),
@@ -17821,13 +18594,13 @@ namespace exprtk
                      case e_vecholder : delete reinterpret_cast<vector_holder_ptr>(local_data_list[i].pointer);
                                         break;
 
-                     case e_data      : delete (T*)(local_data_list[i].pointer);
+                     case e_data      : delete reinterpret_cast<T*>(local_data_list[i].pointer);
                                         break;
 
-                     case e_vecdata   : delete [] (T*)(local_data_list[i].pointer);
+                     case e_vecdata   : delete [] reinterpret_cast<T*>(local_data_list[i].pointer);
                                         break;
 
-                     case e_string    : delete (std::string*)(local_data_list[i].pointer);
+                     case e_string    : delete reinterpret_cast<std::string*>(local_data_list[i].pointer);
                                         break;
 
                      default          : break;
@@ -17887,7 +18660,7 @@ namespace exprtk
          control_block_->ref_count++;
       }
 
-      expression(const symbol_table<T>& symbol_table)
+      explicit expression(const symbol_table<T>& symbol_table)
       : control_block_(0)
       {
          set_expression(new details::null_node<T>());
@@ -17946,6 +18719,9 @@ namespace exprtk
 
       inline T value() const
       {
+         assert(control_block_      );
+         assert(control_block_->expr);
+
          return control_block_->expr->value();
       }
 
@@ -18103,7 +18879,7 @@ namespace exprtk
       }
 
       control_block* control_block_;
-      symtab_list_t      symbol_table_list_;
+      symtab_list_t  symbol_table_list_;
 
       friend class parser<T>;
       friend class expression_helper<T>;
@@ -18162,7 +18938,8 @@ namespace exprtk
          e_numeric = 4,
          e_symtab  = 5,
          e_lexer   = 6,
-         e_helper  = 7
+         e_helper  = 7,
+         e_parser  = 8
       };
 
       struct type
@@ -18220,6 +18997,7 @@ namespace exprtk
             case e_symtab  : return std::string("Symbol Error" );
             case e_lexer   : return std::string("Lexer Error"  );
             case e_helper  : return std::string("Helper Error" );
+            case e_parser  : return std::string("Parser Error" );
             default        : return std::string("Unknown Error");
          }
       }
@@ -18508,7 +19286,7 @@ namespace exprtk
          typedef variable_node_t*     variable_node_ptr;
          typedef parser<T>                     parser_t;
 
-         scope_element_manager(parser<T>& p)
+         explicit scope_element_manager(parser<T>& p)
          : parser_(p),
            input_param_cnt_(0)
          {}
@@ -18619,26 +19397,24 @@ namespace exprtk
 
          inline void free_element(scope_element& se)
          {
-            #ifdef exprtk_enable_debugging
             exprtk_debug(("free_element() - se[%s]\n", se.name.c_str()));
-            #endif
 
             switch (se.type)
             {
-               case scope_element::e_variable   : if (se.data    ) delete (T*) se.data;
-                                                  if (se.var_node) delete se.var_node;
+               case scope_element::e_variable   : delete reinterpret_cast<T*>(se.data);
+                                                  delete se.var_node;
                                                   break;
 
-               case scope_element::e_vector     : if (se.data    ) delete[] (T*) se.data;
-                                                  if (se.vec_node) delete se.vec_node;
+               case scope_element::e_vector     : delete[] reinterpret_cast<T*>(se.data);
+                                                  delete se.vec_node;
                                                   break;
 
-               case scope_element::e_vecelem    : if (se.var_node) delete se.var_node;
+               case scope_element::e_vecelem    : delete se.var_node;
                                                   break;
 
                #ifndef exprtk_disable_string_capabilities
-               case scope_element::e_string     : if (se.data    ) delete (std::string*) se.data;
-                                                  if (se.str_node) delete se.str_node;
+               case scope_element::e_string     : delete reinterpret_cast<std::string*>(se.data);
+                                                  delete se.str_node;
                                                   break;
                #endif
 
@@ -18705,7 +19481,7 @@ namespace exprtk
 
          typedef parser<T> parser_t;
 
-         scope_handler(parser<T>& p)
+         explicit scope_handler(parser<T>& p)
          : parser_(p)
          {
             parser_.state_.scope_depth++;
@@ -18736,6 +19512,45 @@ namespace exprtk
          parser_t& parser_;
       };
 
+      class stack_limit_handler
+      {
+      public:
+
+         typedef parser<T> parser_t;
+
+         explicit stack_limit_handler(parser<T>& p)
+            : parser_(p),
+              limit_exceeded_(false)
+         {
+            if (++parser_.state_.stack_depth > parser_.settings_.max_stack_depth_)
+            {
+               limit_exceeded_ = true;
+               parser_.set_error(
+                  make_error(parser_error::e_parser,
+                     "ERR000 - Current stack depth " + details::to_str(parser_.state_.stack_depth) +
+                     " exceeds maximum allowed stack depth of " + details::to_str(parser_.settings_.max_stack_depth_),
+                     exprtk_error_location));
+            }
+         }
+
+        ~stack_limit_handler()
+         {
+            parser_.state_.stack_depth--;
+         }
+
+         bool operator!()
+         {
+            return limit_exceeded_;
+         }
+
+      private:
+
+         stack_limit_handler& operator=(const stack_limit_handler&);
+
+         parser_t& parser_;
+         bool limit_exceeded_;
+      };
+
       struct symtab_store
       {
          symbol_table_list_t symtab_list_;
@@ -19179,11 +19994,13 @@ namespace exprtk
 
          void reset()
          {
-            parsing_return_stmt = false;
-            parsing_break_stmt  = false;
-            return_stmt_present = false;
-            side_effect_present = false;
-            scope_depth         = 0;
+            parsing_return_stmt     = false;
+            parsing_break_stmt      = false;
+            return_stmt_present     = false;
+            side_effect_present     = false;
+            scope_depth             = 0;
+            stack_depth             = 0;
+            parsing_loop_stmt_count = 0;
          }
 
          #ifndef exprtk_enable_debugging
@@ -19206,6 +20023,8 @@ namespace exprtk
          bool side_effect_present;
          bool type_check_enabled;
          std::size_t scope_depth;
+         std::size_t stack_depth;
+         std::size_t parsing_loop_stmt_count;
       };
 
    public:
@@ -19532,6 +20351,8 @@ namespace exprtk
                                                      e_strength_reduction;
 
          settings_store(const std::size_t compile_options = compile_all_opts)
+         : max_stack_depth_(400),
+           max_node_depth_(10000)
          {
            load_compile_options(compile_options);
          }
@@ -19736,6 +20557,15 @@ namespace exprtk
                                                            .find(assign_opr_to_string(assignment_operation));
          }
 
+         bool logic_disabled(const details::operator_type logic_operation) const
+         {
+            if (disabled_logic_set_.empty())
+               return false;
+            else
+               return disabled_logic_set_.end() != disabled_logic_set_
+                                                           .find(logic_opr_to_string(logic_operation));
+         }
+
          bool arithmetic_disabled(const details::operator_type arithmetic_operation) const
          {
             if (disabled_arithmetic_set_.empty())
@@ -19940,6 +20770,16 @@ namespace exprtk
             return (*this);
          }
 
+         void set_max_stack_depth(const std::size_t mx_stack_depth)
+         {
+            max_stack_depth_ = mx_stack_depth;
+         }
+
+         void set_max_node_depth(const std::size_t max_node_depth)
+         {
+            max_node_depth_ = max_node_depth;
+         }
+
       private:
 
          void load_compile_options(const std::size_t compile_options)
@@ -20002,6 +20842,21 @@ namespace exprtk
             }
          }
 
+         std::string logic_opr_to_string(details::operator_type opr) const
+         {
+            switch (opr)
+            {
+               case details::e_and  : return "and" ;
+               case details::e_or   : return "or"  ;
+               case details::e_xor  : return "xor" ;
+               case details::e_nand : return "nand";
+               case details::e_nor  : return "nor" ;
+               case details::e_xnor : return "xnor";
+               case details::e_notl : return "not" ;
+               default              : return ""    ;
+            }
+         }
+
          bool enable_replacer_;
          bool enable_joiner_;
          bool enable_numeric_check_;
@@ -20023,6 +20878,9 @@ namespace exprtk
          disabled_entity_set_t disabled_assignment_set_;
          disabled_entity_set_t disabled_inequality_set_;
 
+         std::size_t max_stack_depth_;
+         std::size_t max_node_depth_;
+
          friend class parser<T>;
       };
 
@@ -20042,7 +20900,8 @@ namespace exprtk
         #pragma warning(pop)
         #endif
         operator_joiner_2_(2),
-        operator_joiner_3_(3)
+        operator_joiner_3_(3),
+        loop_runtime_check_(0)
       {
          init_precompilation();
 
@@ -20080,8 +20939,8 @@ namespace exprtk
          if (settings_.replacer_enabled())
          {
             symbol_replacer_.clear();
-            symbol_replacer_.add_replace("true" ,"1",lexer::token::e_number);
-            symbol_replacer_.add_replace("false","0",lexer::token::e_number);
+            symbol_replacer_.add_replace("true" , "1", lexer::token::e_number);
+            symbol_replacer_.add_replace("false", "0", lexer::token::e_number);
             helper_assembly_.token_modifier_list.clear();
             helper_assembly_.register_modifier(&symbol_replacer_);
          }
@@ -20146,7 +21005,7 @@ namespace exprtk
          {
             set_error(
                make_error(parser_error::e_syntax,
-                          "ERR000 - Empty expression!",
+                          "ERR001 - Empty expression!",
                           exprtk_error_location));
 
             return false;
@@ -20162,7 +21021,7 @@ namespace exprtk
          {
             set_error(
                make_error(parser_error::e_syntax,
-                          "ERR001 - Empty expression!",
+                          "ERR002 - Empty expression!",
                           exprtk_error_location));
 
             return false;
@@ -20191,7 +21050,7 @@ namespace exprtk
                dec_.return_present_ = true;
 
                e = expression_generator_
-                     .return_envelope(e,results_context_,retinvk_ptr);
+                     .return_envelope(e, results_context_, retinvk_ptr);
             }
 
             expr.set_expression(e);
@@ -20209,7 +21068,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR002 - Invalid expression encountered",
+                             "ERR003 - Invalid expression encountered",
                              exprtk_error_location));
             }
 
@@ -20228,13 +21087,13 @@ namespace exprtk
 
       inline expression_t compile(const std::string& expression_string, symbol_table_t& symtab)
       {
-         expression_t expr;
+         expression_t expression;
 
-         expr.register_symbol_table(symtab);
+         expression.register_symbol_table(symtab);
 
-         compile(expression_string,expr);
+         compile(expression_string,expression);
 
-         return expr;
+         return expression;
       }
 
       void process_lexer_errors()
@@ -20243,7 +21102,7 @@ namespace exprtk
          {
             if (lexer()[i].is_error())
             {
-               std::string diagnostic = "ERR003 - ";
+               std::string diagnostic = "ERR004 - ";
 
                switch (lexer()[i].type)
                {
@@ -20311,7 +21170,7 @@ namespace exprtk
                      set_error(
                         make_error(parser_error::e_token,
                                    bracket_checker_ptr->error_token(),
-                                   "ERR004 - Mismatched brackets: '" + bracket_checker_ptr->error_token().value + "'",
+                                   "ERR005 - Mismatched brackets: '" + bracket_checker_ptr->error_token().value + "'",
                                    exprtk_error_location));
                   }
                   else if (0 != (numeric_checker_ptr = dynamic_cast<lexer::helper::numeric_checker*>(helper_assembly_.error_token_scanner)))
@@ -20323,7 +21182,7 @@ namespace exprtk
                         set_error(
                            make_error(parser_error::e_token,
                                       error_token,
-                                      "ERR005 - Invalid numeric token: '" + error_token.value + "'",
+                                      "ERR006 - Invalid numeric token: '" + error_token.value + "'",
                                       exprtk_error_location));
                      }
 
@@ -20341,7 +21200,7 @@ namespace exprtk
                         set_error(
                            make_error(parser_error::e_token,
                                       error_token.first,
-                                      "ERR006 - Invalid token sequence: '" +
+                                      "ERR007 - Invalid token sequence: '" +
                                       error_token.first.value  + "' and '" +
                                       error_token.second.value + "'",
                                       exprtk_error_location));
@@ -20361,7 +21220,7 @@ namespace exprtk
                         set_error(
                            make_error(parser_error::e_token,
                                       error_token.first,
-                                      "ERR007 - Invalid token sequence: '" +
+                                      "ERR008 - Invalid token sequence: '" +
                                       error_token.first.value  + "' and '" +
                                       error_token.second.value + "'",
                                       exprtk_error_location));
@@ -20455,6 +21314,16 @@ namespace exprtk
          unknown_symbol_resolver_ = &default_usr_;
       }
 
+      inline void register_loop_runtime_check(loop_runtime_check& lrtchk)
+      {
+         loop_runtime_check_ = &lrtchk;
+      }
+
+      inline void clear_loop_runtime_check()
+      {
+         loop_runtime_check_ = loop_runtime_check_ptr(0);
+      }
+
    private:
 
       inline bool valid_base_operation(const std::string& symbol) const
@@ -20498,6 +21367,11 @@ namespace exprtk
                settings_.function_enabled(symbol);
       }
 
+      bool is_invalid_logic_operation(const details::operator_type operation) const
+      {
+         return settings_.logic_disabled(operation);
+      }
+
       bool is_invalid_arithmetic_operation(const details::operator_type operation) const
       {
          return settings_.arithmetic_disabled(operation);
@@ -20517,13 +21391,17 @@ namespace exprtk
       inline void next_token()
       {
          const std::string ct_str = current_token().value;
+         const std::size_t ct_pos = current_token().position;
          parser_helper::next_token();
          const std::string depth(2 * state_.scope_depth,' ');
          exprtk_debug(("%s"
-                       "prev[%s] --> curr[%s]\n",
+                       "prev[%s | %04d] --> curr[%s | %04d]  stack_level: %3d\n",
                        depth.c_str(),
                        ct_str.c_str(),
-                       current_token().value.c_str()));
+                       static_cast<unsigned int>(ct_pos),
+                       current_token().value.c_str(),
+                       static_cast<unsigned int>(current_token().position),
+                       static_cast<unsigned int>(state_.stack_depth)));
       }
       #endif
 
@@ -20532,8 +21410,6 @@ namespace exprtk
          std::vector<expression_node_ptr> arg_list;
          std::vector<bool> side_effect_list;
 
-         expression_node_ptr result = error_node();
-
          scoped_vec_delete<expression_node_t> sdd((*this),arg_list);
 
          lexer::token begin_token;
@@ -20554,7 +21430,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR008 - Invalid expression encountered",
+                                "ERR009 - Invalid expression encountered",
                                 exprtk_error_location));
                }
 
@@ -20600,7 +21476,7 @@ namespace exprtk
             dec_.final_stmt_return_ = true;
          }
 
-         result = simplify(arg_list,side_effect_list);
+         const expression_node_ptr result = simplify(arg_list,side_effect_list);
 
          sdd.delete_ptr = (0 == result);
 
@@ -20646,6 +21522,13 @@ namespace exprtk
 
       inline expression_node_ptr parse_expression(precedence_level precedence = e_level00)
       {
+         stack_limit_handler slh(*this);
+
+         if (!slh)
+         {
+            return error_node();
+         }
+
          expression_node_ptr expression = parse_branch(precedence);
 
          if (0 == expression)
@@ -20663,25 +21546,25 @@ namespace exprtk
 
             switch (current_token().type)
             {
-               case token_t::e_assign : current_state.set(e_level00,e_level00,details::e_assign); break;
-               case token_t::e_addass : current_state.set(e_level00,e_level00,details::e_addass); break;
-               case token_t::e_subass : current_state.set(e_level00,e_level00,details::e_subass); break;
-               case token_t::e_mulass : current_state.set(e_level00,e_level00,details::e_mulass); break;
-               case token_t::e_divass : current_state.set(e_level00,e_level00,details::e_divass); break;
-               case token_t::e_modass : current_state.set(e_level00,e_level00,details::e_modass); break;
-               case token_t::e_swap   : current_state.set(e_level00,e_level00,details::e_swap  ); break;
-               case token_t::e_lt     : current_state.set(e_level05,e_level06,details::    e_lt); break;
-               case token_t::e_lte    : current_state.set(e_level05,e_level06,details::   e_lte); break;
-               case token_t::e_eq     : current_state.set(e_level05,e_level06,details::    e_eq); break;
-               case token_t::e_ne     : current_state.set(e_level05,e_level06,details::    e_ne); break;
-               case token_t::e_gte    : current_state.set(e_level05,e_level06,details::   e_gte); break;
-               case token_t::e_gt     : current_state.set(e_level05,e_level06,details::    e_gt); break;
-               case token_t::e_add    : current_state.set(e_level07,e_level08,details::   e_add); break;
-               case token_t::e_sub    : current_state.set(e_level07,e_level08,details::   e_sub); break;
-               case token_t::e_div    : current_state.set(e_level10,e_level11,details::   e_div); break;
-               case token_t::e_mul    : current_state.set(e_level10,e_level11,details::   e_mul); break;
-               case token_t::e_mod    : current_state.set(e_level10,e_level11,details::   e_mod); break;
-               case token_t::e_pow    : current_state.set(e_level12,e_level12,details::   e_pow); break;
+               case token_t::e_assign : current_state.set(e_level00,e_level00, details::e_assign); break;
+               case token_t::e_addass : current_state.set(e_level00,e_level00, details::e_addass); break;
+               case token_t::e_subass : current_state.set(e_level00,e_level00, details::e_subass); break;
+               case token_t::e_mulass : current_state.set(e_level00,e_level00, details::e_mulass); break;
+               case token_t::e_divass : current_state.set(e_level00,e_level00, details::e_divass); break;
+               case token_t::e_modass : current_state.set(e_level00,e_level00, details::e_modass); break;
+               case token_t::e_swap   : current_state.set(e_level00,e_level00, details::e_swap  ); break;
+               case token_t::e_lt     : current_state.set(e_level05,e_level06, details::    e_lt); break;
+               case token_t::e_lte    : current_state.set(e_level05,e_level06, details::   e_lte); break;
+               case token_t::e_eq     : current_state.set(e_level05,e_level06, details::    e_eq); break;
+               case token_t::e_ne     : current_state.set(e_level05,e_level06, details::    e_ne); break;
+               case token_t::e_gte    : current_state.set(e_level05,e_level06, details::   e_gte); break;
+               case token_t::e_gt     : current_state.set(e_level05,e_level06, details::    e_gt); break;
+               case token_t::e_add    : current_state.set(e_level07,e_level08, details::   e_add); break;
+               case token_t::e_sub    : current_state.set(e_level07,e_level08, details::   e_sub); break;
+               case token_t::e_div    : current_state.set(e_level10,e_level11, details::   e_div); break;
+               case token_t::e_mul    : current_state.set(e_level10,e_level11, details::   e_mul); break;
+               case token_t::e_mod    : current_state.set(e_level10,e_level11, details::   e_mod); break;
+               case token_t::e_pow    : current_state.set(e_level12,e_level12, details::   e_pow); break;
                default                : if (token_t::e_symbol == current_token().type)
                                         {
                                            static const std::string s_and   =   "and";
@@ -20784,14 +21667,26 @@ namespace exprtk
             expression_node_ptr right_branch   = error_node();
             expression_node_ptr new_expression = error_node();
 
-            if (is_invalid_arithmetic_operation(current_state.operation))
+            if (is_invalid_logic_operation(current_state.operation))
             {
                free_node(node_allocator_,expression);
 
                set_error(
                   make_error(parser_error::e_syntax,
                              prev_token,
-                             "ERR009 - Invalid arithmetic operation '" + details::to_str(current_state.operation) + "'",
+                             "ERR010 - Invalid or disabled logic operation '" + details::to_str(current_state.operation) + "'",
+                             exprtk_error_location));
+
+               return error_node();
+            }
+            else if (is_invalid_arithmetic_operation(current_state.operation))
+            {
+               free_node(node_allocator_,expression);
+
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             prev_token,
+                             "ERR011 - Invalid or disabled arithmetic operation '" + details::to_str(current_state.operation) + "'",
                              exprtk_error_location));
 
                return error_node();
@@ -20803,7 +21698,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              prev_token,
-                             "ERR010 - Invalid inequality operation '" + details::to_str(current_state.operation) + "'",
+                             "ERR012 - Invalid inequality operation '" + details::to_str(current_state.operation) + "'",
                              exprtk_error_location));
 
                return error_node();
@@ -20815,7 +21710,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              prev_token,
-                             "ERR011 - Invalid assignment operation '" + details::to_str(current_state.operation) + "'",
+                             "ERR013 - Invalid or disabled assignment operation '" + details::to_str(current_state.operation) + "'",
                              exprtk_error_location));
 
                return error_node();
@@ -20834,7 +21729,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 prev_token,
-                                "ERR012 - Return statements cannot be part of sub-expressions",
+                                "ERR014 - Return statements cannot be part of sub-expressions",
                                 exprtk_error_location));
 
                   return error_node();
@@ -20857,7 +21752,7 @@ namespace exprtk
                                 prev_token,
                                 !synthesis_error_.empty() ?
                                 synthesis_error_ :
-                                "ERR013 - General parsing error at token: '" + prev_token.value + "'",
+                                "ERR015 - General parsing error at token: '" + prev_token.value + "'",
                                 exprtk_error_location));
                }
 
@@ -20882,6 +21777,20 @@ namespace exprtk
             }
          }
 
+         if ((0 != expression) && (expression->node_depth() > settings_.max_node_depth_))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                  current_token(),
+                  "ERR016 - Expression depth of " + details::to_str(static_cast<int>(expression->node_depth())) +
+                  " exceeds maximum allowed expression depth of " + details::to_str(static_cast<int>(settings_.max_node_depth_)),
+                  exprtk_error_location));
+
+            free_node(node_allocator_,expression);
+
+            return error_node();
+         }
+
          return expression;
       }
 
@@ -20927,7 +21836,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR014 - Failed to find variable node in symbol table",
+                                "ERR017 - Failed to find variable node in symbol table",
                                 exprtk_error_location));
 
                   free_node(node_allocator_,node);
@@ -20945,6 +21854,31 @@ namespace exprtk
          return reinterpret_cast<expression_node_ptr>(0);
       }
 
+      struct scoped_expression_delete
+      {
+         scoped_expression_delete(parser<T>& pr, expression_node_ptr& expression)
+         : delete_ptr(true),
+           parser_(pr),
+           expression_(expression)
+         {}
+
+        ~scoped_expression_delete()
+         {
+            if (delete_ptr)
+            {
+               free_node(parser_.node_allocator_, expression_);
+            }
+         }
+
+         bool delete_ptr;
+         parser<T>& parser_;
+         expression_node_ptr& expression_;
+
+      private:
+
+         scoped_expression_delete& operator=(const scoped_expression_delete&);
+      };
+
       template <typename Type, std::size_t N>
       struct scoped_delete
       {
@@ -20968,7 +21902,7 @@ namespace exprtk
             {
                for (std::size_t i = 0; i < N; ++i)
                {
-                  free_node(parser_.node_allocator_,p_[i]);
+                  free_node(parser_.node_allocator_, p_[i]);
                }
             }
          }
@@ -21050,7 +21984,7 @@ namespace exprtk
 
       struct scoped_bool_negator
       {
-         scoped_bool_negator(bool& bb)
+         explicit scoped_bool_negator(bool& bb)
          : b(bb)
          { b = !b; }
 
@@ -21062,7 +21996,7 @@ namespace exprtk
 
       struct scoped_bool_or_restorer
       {
-         scoped_bool_or_restorer(bool& bb)
+         explicit scoped_bool_or_restorer(bool& bb)
          : b(bb),
            original_value_(bb)
          {}
@@ -21076,6 +22010,21 @@ namespace exprtk
          bool original_value_;
       };
 
+      struct scoped_inc_dec
+      {
+         explicit scoped_inc_dec(std::size_t& v)
+         : v_(v)
+         { ++v_; }
+
+        ~scoped_inc_dec()
+         {
+           assert(v_ > 0);
+           --v_;
+         }
+
+         std::size_t& v_;
+      };
+
       inline expression_node_ptr parse_function_invocation(ifunction<T>* function, const std::string& function_name)
       {
          expression_node_ptr func_node = reinterpret_cast<expression_node_ptr>(0);
@@ -21107,7 +22056,7 @@ namespace exprtk
                          set_error(
                             make_error(parser_error::e_syntax,
                                        current_token(),
-                                       "ERR015 - Invalid number of parameters for function: '" + function_name + "'",
+                                       "ERR018 - Invalid number of parameters for function: '" + function_name + "'",
                                        exprtk_error_location));
 
                          return error_node();
@@ -21121,7 +22070,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR016 - Failed to generate call to function: '" + function_name + "'",
+                          "ERR019 - Failed to generate call to function: '" + function_name + "'",
                           exprtk_error_location));
 
             return error_node();
@@ -21140,7 +22089,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR017 - Expecting ifunction '" + function_name + "' to have non-zero parameter count",
+                          "ERR020 - Expecting ifunction '" + function_name + "' to have non-zero parameter count",
                           exprtk_error_location));
 
             return error_node();
@@ -21163,7 +22112,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR018 - Expecting argument list for function: '" + function_name + "'",
+                          "ERR021 - Expecting argument list for function: '" + function_name + "'",
                           exprtk_error_location));
 
             return error_node();
@@ -21178,7 +22127,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR019 - Failed to parse argument " + details::to_str(i) + " for function: '" + function_name + "'",
+                             "ERR022 - Failed to parse argument " + details::to_str(i) + " for function: '" + function_name + "'",
                              exprtk_error_location));
 
                return error_node();
@@ -21190,7 +22139,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR020 - Invalid number of arguments for function: '" + function_name + "'",
+                                "ERR023 - Invalid number of arguments for function: '" + function_name + "'",
                                 exprtk_error_location));
 
                   return error_node();
@@ -21203,7 +22152,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR021 - Invalid number of arguments for function: '" + function_name + "'",
+                          "ERR024 - Invalid number of arguments for function: '" + function_name + "'",
                           exprtk_error_location));
 
             return error_node();
@@ -21211,7 +22160,7 @@ namespace exprtk
          else
             result = expression_generator_.function(function,branch);
 
-         sd.delete_ptr = false;
+         sd.delete_ptr = (0 == result);
 
          return result;
       }
@@ -21232,7 +22181,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR022 - Expecting '()' to proceed call to function: '" + function_name + "'",
+                          "ERR025 - Expecting '()' to proceed call to function: '" + function_name + "'",
                           exprtk_error_location));
 
             free_node(node_allocator_,result);
@@ -21257,7 +22206,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR023 - Expected a '(' at start of function call to '" + function_name  +
+                          "ERR026 - Expected a '(' at start of function call to '" + function_name  +
                           "', instead got: '" + current_token().value + "'",
                           exprtk_error_location));
 
@@ -21269,7 +22218,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR024 - Expected at least one input parameter for function call '" + function_name + "'",
+                          "ERR027 - Expected at least one input parameter for function call '" + function_name + "'",
                           exprtk_error_location));
 
             return 0;
@@ -21295,7 +22244,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR025 - Expected a ',' between function input parameters, instead got: '" + current_token().value + "'",
+                             "ERR028 - Expected a ',' between function input parameters, instead got: '" + current_token().value + "'",
                              exprtk_error_location));
 
                return 0;
@@ -21307,7 +22256,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR026 - Invalid number of input parameters passed to function '" + function_name  + "'",
+                          "ERR029 - Invalid number of input parameters passed to function '" + function_name  + "'",
                           exprtk_error_location));
 
             return 0;
@@ -21330,7 +22279,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           diagnostic_token,
-                          "ERR027 - No entry found for base operation: " + operation_name,
+                          "ERR030 - No entry found for base operation: " + operation_name,
                           exprtk_error_location));
 
             return error_node();
@@ -21377,7 +22326,7 @@ namespace exprtk
          set_error(
             make_error(parser_error::e_syntax,
                        diagnostic_token,
-                       "ERR028 - Invalid number of input parameters for call to function: '" + operation_name + "'",
+                       "ERR031 - Invalid number of input parameters for call to function: '" + operation_name + "'",
                        exprtk_error_location));
 
          return error_node();
@@ -21397,7 +22346,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR029 - Expected ',' between if-statement condition and consequent",
+                          "ERR032 - Expected ',' between if-statement condition and consequent",
                           exprtk_error_location));
             result = false;
          }
@@ -21406,7 +22355,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR030 - Failed to parse consequent for if-statement",
+                          "ERR033 - Failed to parse consequent for if-statement",
                           exprtk_error_location));
             result = false;
          }
@@ -21415,7 +22364,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR031 - Expected ',' between if-statement consequent and alternative",
+                          "ERR034 - Expected ',' between if-statement consequent and alternative",
                           exprtk_error_location));
             result = false;
          }
@@ -21424,7 +22373,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR032 - Failed to parse alternative for if-statement",
+                          "ERR035 - Failed to parse alternative for if-statement",
                           exprtk_error_location));
             result = false;
          }
@@ -21433,7 +22382,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR033 - Expected ')' at the end of if-statement",
+                          "ERR036 - Expected ')' at the end of if-statement",
                           exprtk_error_location));
             result = false;
          }
@@ -21449,13 +22398,13 @@ namespace exprtk
                if (consq_is_str && alter_is_str)
                {
                   return expression_generator_
-                           .conditional_string(condition,consequent,alternative);
+                           .conditional_string(condition, consequent, alternative);
                }
 
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR034 - Return types of ternary if-statement differ",
+                             "ERR037 - Return types of ternary if-statement differ",
                              exprtk_error_location));
 
                result = false;
@@ -21465,15 +22414,15 @@ namespace exprtk
 
          if (!result)
          {
-            free_node(node_allocator_,  condition);
-            free_node(node_allocator_, consequent);
-            free_node(node_allocator_,alternative);
+            free_node(node_allocator_, condition  );
+            free_node(node_allocator_, consequent );
+            free_node(node_allocator_, alternative);
 
             return error_node();
          }
          else
             return expression_generator_
-                      .conditional(condition,consequent,alternative);
+                      .conditional(condition, consequent, alternative);
       }
 
       inline expression_node_ptr parse_conditional_statement_02(expression_node_ptr condition)
@@ -21490,7 +22439,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR035 - Failed to parse body of consequent for if-statement",
+                             "ERR038 - Failed to parse body of consequent for if-statement",
                              exprtk_error_location));
 
                result = false;
@@ -21513,7 +22462,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR036 - Expected ';' at the end of the consequent for if-statement",
+                                "ERR039 - Expected ';' at the end of the consequent for if-statement",
                                 exprtk_error_location));
 
                   result = false;
@@ -21524,7 +22473,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR037 - Failed to parse body of consequent for if-statement",
+                             "ERR040 - Failed to parse body of consequent for if-statement",
                              exprtk_error_location));
 
                result = false;
@@ -21544,7 +22493,7 @@ namespace exprtk
                      set_error(
                         make_error(parser_error::e_syntax,
                                    current_token(),
-                                   "ERR038 - Failed to parse body of the 'else' for if-statement",
+                                   "ERR041 - Failed to parse body of the 'else' for if-statement",
                                    exprtk_error_location));
 
                      result = false;
@@ -21557,7 +22506,7 @@ namespace exprtk
                      set_error(
                         make_error(parser_error::e_syntax,
                                    current_token(),
-                                   "ERR039 - Failed to parse body of if-else statement",
+                                   "ERR042 - Failed to parse body of if-else statement",
                                    exprtk_error_location));
 
                      result = false;
@@ -21570,7 +22519,7 @@ namespace exprtk
                      set_error(
                         make_error(parser_error::e_syntax,
                                    current_token(),
-                                   "ERR040 - Expected ';' at the end of the 'else-if' for the if-statement",
+                                   "ERR043 - Expected ';' at the end of the 'else-if' for the if-statement",
                                    exprtk_error_location));
 
                      result = false;
@@ -21581,7 +22530,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR041 - Failed to parse body of the 'else' for if-statement",
+                                "ERR044 - Failed to parse body of the 'else' for if-statement",
                                 exprtk_error_location));
 
                   result = false;
@@ -21606,7 +22555,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR042 - Return types of ternary if-statement differ",
+                             "ERR045 - Return types of ternary if-statement differ",
                              exprtk_error_location));
 
                result = false;
@@ -21638,7 +22587,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR043 - Expected '(' at start of if-statement, instead got: '" + current_token().value + "'",
+                          "ERR046 - Expected '(' at start of if-statement, instead got: '" + current_token().value + "'",
                           exprtk_error_location));
 
             return error_node();
@@ -21648,7 +22597,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR044 - Failed to parse condition for if-statement",
+                          "ERR047 - Failed to parse condition for if-statement",
                           exprtk_error_location));
 
             return error_node();
@@ -21680,7 +22629,7 @@ namespace exprtk
          set_error(
             make_error(parser_error::e_syntax,
                        current_token(),
-                       "ERR045 - Invalid if-statement",
+                       "ERR048 - Invalid if-statement",
                        exprtk_error_location));
 
          free_node(node_allocator_,condition);
@@ -21701,7 +22650,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR046 - Encountered invalid condition branch for ternary if-statement",
+                          "ERR049 - Encountered invalid condition branch for ternary if-statement",
                           exprtk_error_location));
 
             return error_node();
@@ -21711,7 +22660,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR047 - Expected '?' after condition of ternary if-statement",
+                          "ERR050 - Expected '?' after condition of ternary if-statement",
                           exprtk_error_location));
 
             result = false;
@@ -21721,7 +22670,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR048 - Failed to parse consequent for ternary if-statement",
+                          "ERR051 - Failed to parse consequent for ternary if-statement",
                           exprtk_error_location));
 
             result = false;
@@ -21731,7 +22680,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR049 - Expected ':' between ternary if-statement consequent and alternative",
+                          "ERR052 - Expected ':' between ternary if-statement consequent and alternative",
                           exprtk_error_location));
 
             result = false;
@@ -21741,7 +22690,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR050 - Failed to parse alternative for ternary if-statement",
+                          "ERR053 - Failed to parse alternative for ternary if-statement",
                           exprtk_error_location));
 
             result = false;
@@ -21764,7 +22713,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR051 - Return types of ternary if-statement differ",
+                             "ERR054 - Return types of ternary if-statement differ",
                              exprtk_error_location));
 
                result = false;
@@ -21785,6 +22734,22 @@ namespace exprtk
                       .conditional(condition, consequent, alternative);
       }
 
+      inline expression_node_ptr parse_not_statement()
+      {
+         if (settings_.logic_disabled("not"))
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR055 - Invalid or disabled logic operation 'not'",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+
+         return parse_base_operation();
+      }
+
       inline expression_node_ptr parse_while_loop()
       {
          // Parse: [while][(][test expr][)][{][expression][}]
@@ -21801,7 +22766,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR052 - Expected '(' at start of while-loop condition statement",
+                          "ERR056 - Expected '(' at start of while-loop condition statement",
                           exprtk_error_location));
 
             return error_node();
@@ -21811,7 +22776,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR053 - Failed to parse condition for while-loop",
+                          "ERR057 - Failed to parse condition for while-loop",
                           exprtk_error_location));
 
             return error_node();
@@ -21821,7 +22786,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR054 - Expected ')' at end of while-loop condition statement",
+                          "ERR058 - Expected ')' at end of while-loop condition statement",
                           exprtk_error_location));
 
             result = false;
@@ -21831,12 +22796,14 @@ namespace exprtk
 
          if (result)
          {
+            scoped_inc_dec sid(state_.parsing_loop_stmt_count);
+
             if (0 == (branch = parse_multi_sequence("while-loop")))
             {
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR055 - Failed to parse body of while-loop"));
+                             "ERR059 - Failed to parse body of while-loop"));
                result = false;
             }
             else if (0 == (result_node = expression_generator_.while_loop(condition,
@@ -21846,7 +22813,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR056 - Failed to synthesize while-loop",
+                             "ERR060 - Failed to synthesize while-loop",
                              exprtk_error_location));
 
                result = false;
@@ -21894,6 +22861,8 @@ namespace exprtk
 
             scoped_bool_or_restorer sbr(state_.side_effect_present);
 
+            scoped_inc_dec sid(state_.parsing_loop_stmt_count);
+
             for ( ; ; )
             {
                state_.side_effect_present = false;
@@ -21922,7 +22891,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR057 - Expected '" + token_t::to_str(seperator) + "' in body of repeat until loop",
+                                "ERR061 - Expected '" + token_t::to_str(seperator) + "' in body of repeat until loop",
                                 exprtk_error_location));
 
                   return error_node();
@@ -21946,7 +22915,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR058 - Failed to parse body of repeat until loop",
+                             "ERR062 - Failed to parse body of repeat until loop",
                              exprtk_error_location));
 
                return error_node();
@@ -21960,7 +22929,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR059 - Expected '(' before condition statement of repeat until loop",
+                          "ERR063 - Expected '(' before condition statement of repeat until loop",
                           exprtk_error_location));
 
             free_node(node_allocator_,branch);
@@ -21974,7 +22943,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR060 - Failed to parse condition for repeat until loop",
+                          "ERR064 - Failed to parse condition for repeat until loop",
                           exprtk_error_location));
 
             free_node(node_allocator_,branch);
@@ -21986,7 +22955,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR061 - Expected ')' after condition of repeat until loop",
+                          "ERR065 - Expected ')' after condition of repeat until loop",
                           exprtk_error_location));
 
             free_node(node_allocator_,    branch);
@@ -22007,7 +22976,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR062 - Failed to synthesize repeat until loop",
+                          "ERR066 - Failed to synthesize repeat until loop",
                           exprtk_error_location));
 
             free_node(node_allocator_,condition);
@@ -22042,7 +23011,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR063 - Expected '(' at start of for-loop",
+                          "ERR067 - Expected '(' at start of for-loop",
                           exprtk_error_location));
 
             return error_node();
@@ -22062,7 +23031,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR064 - Expected a variable at the start of initialiser section of for-loop",
+                                "ERR068 - Expected a variable at the start of initialiser section of for-loop",
                                 exprtk_error_location));
 
                   return error_node();
@@ -22072,7 +23041,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR065 - Expected variable assignment of initialiser section of for-loop",
+                                "ERR069 - Expected variable assignment of initialiser section of for-loop",
                                 exprtk_error_location));
 
                   return error_node();
@@ -22087,7 +23056,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR066 - For-loop variable '" + loop_counter_symbol+ "' is being shadowed by a previous declaration",
+                                "ERR070 - For-loop variable '" + loop_counter_symbol+ "' is being shadowed by a previous declaration",
                                 exprtk_error_location));
 
                   return error_node();
@@ -22112,14 +23081,14 @@ namespace exprtk
                      nse.type      = scope_element::e_variable;
                      nse.depth     = state_.scope_depth;
                      nse.data      = new T(T(0));
-                     nse.var_node  = node_allocator_.allocate<variable_node_t>(*(T*)(nse.data));
+                     nse.var_node  = node_allocator_.allocate<variable_node_t>(*reinterpret_cast<T*>(nse.data));
 
                      if (!sem_.add_element(nse))
                      {
                         set_error(
                            make_error(parser_error::e_syntax,
                                       current_token(),
-                                      "ERR067 - Failed to add new local variable '" + loop_counter_symbol + "' to SEM",
+                                      "ERR071 - Failed to add new local variable '" + loop_counter_symbol + "' to SEM",
                                       exprtk_error_location));
 
                         sem_.free_element(nse);
@@ -22141,7 +23110,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR068 - Failed to parse initialiser of for-loop",
+                             "ERR072 - Failed to parse initialiser of for-loop",
                              exprtk_error_location));
 
                result = false;
@@ -22151,7 +23120,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR069 - Expected ';' after initialiser of for-loop",
+                             "ERR073 - Expected ';' after initialiser of for-loop",
                              exprtk_error_location));
 
                result = false;
@@ -22165,7 +23134,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR070 - Failed to parse condition of for-loop",
+                             "ERR074 - Failed to parse condition of for-loop",
                              exprtk_error_location));
 
                result = false;
@@ -22175,7 +23144,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR071 - Expected ';' after condition section of for-loop",
+                             "ERR075 - Expected ';' after condition section of for-loop",
                              exprtk_error_location));
 
                result = false;
@@ -22189,7 +23158,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR072 - Failed to parse incrementor of for-loop",
+                             "ERR076 - Failed to parse incrementor of for-loop",
                              exprtk_error_location));
 
                result = false;
@@ -22199,7 +23168,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR073 - Expected ')' after incrementor section of for-loop",
+                             "ERR077 - Expected ')' after incrementor section of for-loop",
                              exprtk_error_location));
 
                result = false;
@@ -22210,12 +23179,14 @@ namespace exprtk
          {
             brkcnt_list_.push_front(false);
 
+            scoped_inc_dec sid(state_.parsing_loop_stmt_count);
+
             if (0 == (loop_body = parse_multi_sequence("for-loop")))
             {
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR074 - Failed to parse body of for-loop",
+                             "ERR078 - Failed to parse body of for-loop",
                              exprtk_error_location));
 
                result = false;
@@ -22265,7 +23236,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR075 - Expected keyword 'switch'",
+                          "ERR079 - Expected keyword 'switch'",
                           exprtk_error_location));
 
             return error_node();
@@ -22280,85 +23251,100 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR076 - Expected '{' for call to switch statement",
+                          "ERR080 - Expected '{' for call to switch statement",
                           exprtk_error_location));
 
             return error_node();
          }
 
+         expression_node_ptr default_statement = error_node();
+
+         scoped_expression_delete defstmt_delete((*this), default_statement);
+
          for ( ; ; )
          {
-            if (!details::imatch("case",current_token().value))
+            if (details::imatch("case",current_token().value))
             {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR077 - Expected either a 'case' or 'default' statement",
-                             exprtk_error_location));
+               next_token();
 
-               return error_node();
-            }
+               expression_node_ptr condition = parse_expression();
 
-            next_token();
+               if (0 == condition)
+                  return error_node();
+               else if (!token_is(token_t::e_colon))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR081 - Expected ':' for case of switch statement",
+                                exprtk_error_location));
 
-            expression_node_ptr condition = parse_expression();
+                  free_node(node_allocator_, condition);
 
-            if (0 == condition)
-               return error_node();
-            else if (!token_is(token_t::e_colon))
-            {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR078 - Expected ':' for case of switch statement",
-                             exprtk_error_location));
+                  return error_node();
+               }
 
-               return error_node();
-            }
+               expression_node_ptr consequent = parse_expression();
 
-            expression_node_ptr consequent = parse_expression();
+               if (0 == consequent)
+               {
+                  free_node(node_allocator_, condition);
 
-            if (0 == consequent)
-               return error_node();
-            else if (!token_is(token_t::e_eof))
-            {
-               set_error(
-                  make_error(parser_error::e_syntax,
-                             current_token(),
-                             "ERR079 - Expected ';' at end of case for switch statement",
-                             exprtk_error_location));
+                  return error_node();
+               }
+               else if (!token_is(token_t::e_eof))
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR082 - Expected ';' at end of case for switch statement",
+                                exprtk_error_location));
 
-               return error_node();
-            }
+                  free_node(node_allocator_,  condition);
+                  free_node(node_allocator_, consequent);
+
+                  return error_node();
+               }
+
+               // Can we optimise away the case statement?
+               if (is_constant_node(condition) && is_false(condition))
+               {
+                  free_node(node_allocator_,  condition);
+                  free_node(node_allocator_, consequent);
+               }
+               else
+               {
+                  arg_list.push_back( condition);
+                  arg_list.push_back(consequent);
+               }
 
-            // Can we optimise away the case statement?
-            if (is_constant_node(condition) && is_false(condition))
-            {
-               free_node(node_allocator_,  condition);
-               free_node(node_allocator_, consequent);
             }
-            else
+            else if (details::imatch("default",current_token().value))
             {
-               arg_list.push_back( condition);
-               arg_list.push_back(consequent);
-            }
+               if (0 != default_statement)
+               {
+                  set_error(
+                     make_error(parser_error::e_syntax,
+                                current_token(),
+                                "ERR083 - Multiple default cases for switch statement",
+                                exprtk_error_location));
+
+                  return error_node();
+               }
 
-            if (details::imatch("default",current_token().value))
-            {
                next_token();
+
                if (!token_is(token_t::e_colon))
                {
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR080 - Expected ':' for default of switch statement",
+                                "ERR084 - Expected ':' for default of switch statement",
                                 exprtk_error_location));
 
                   return error_node();
                }
 
-               expression_node_ptr default_statement = error_node();
-
                if (token_is(token_t::e_lcrlbracket,prsrhlpr_t::e_hold))
                   default_statement = parse_multi_sequence("switch-default");
                else
@@ -22368,36 +23354,40 @@ namespace exprtk
                   return error_node();
                else if (!token_is(token_t::e_eof))
                {
-                  free_node(node_allocator_,default_statement);
-
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR081 - Expected ';' at end of default for switch statement",
+                                "ERR085 - Expected ';' at end of default for switch statement",
                                 exprtk_error_location));
 
                   return error_node();
                }
-
-               arg_list.push_back(default_statement);
+            }
+            else if (token_is(token_t::e_rcrlbracket))
                break;
+            else
+            {
+               set_error(
+                  make_error(parser_error::e_syntax,
+                             current_token(),
+                             "ERR086 - Expected '}' at end of switch statement",
+                             exprtk_error_location));
+
+               return error_node();
             }
          }
 
-         if (!token_is(token_t::e_rcrlbracket))
-         {
-            set_error(
-               make_error(parser_error::e_syntax,
-                          current_token(),
-                          "ERR082 - Expected '}' at end of switch statement",
-                          exprtk_error_location));
+         const bool default_statement_present = (0 != default_statement);
 
-            return error_node();
+         if (default_statement_present)
+         {
+            arg_list.push_back(default_statement);
          }
 
-         result = expression_generator_.switch_statement(arg_list);
+         result = expression_generator_.switch_statement(arg_list, (0 != default_statement));
 
          svd.delete_ptr = (0 == result);
+         defstmt_delete.delete_ptr = (0 == result);
 
          return result;
       }
@@ -22405,14 +23395,13 @@ namespace exprtk
       inline expression_node_ptr parse_multi_switch_statement()
       {
          std::vector<expression_node_ptr> arg_list;
-         expression_node_ptr result = error_node();
 
          if (!details::imatch(current_token().value,"[*]"))
          {
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR083 - Expected token '[*]'",
+                          "ERR087 - Expected token '[*]'",
                           exprtk_error_location));
 
             return error_node();
@@ -22427,7 +23416,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR084 - Expected '{' for call to [*] statement",
+                          "ERR088 - Expected '{' for call to [*] statement",
                           exprtk_error_location));
 
             return error_node();
@@ -22440,7 +23429,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR085 - Expected a 'case' statement for multi-switch",
+                             "ERR089 - Expected a 'case' statement for multi-switch",
                              exprtk_error_location));
 
                return error_node();
@@ -22458,7 +23447,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR086 - Expected ':' for case of [*] statement",
+                             "ERR090 - Expected ':' for case of [*] statement",
                              exprtk_error_location));
 
                return error_node();
@@ -22474,7 +23463,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR087 - Expected ';' at end of case for [*] statement",
+                             "ERR091 - Expected ';' at end of case for [*] statement",
                              exprtk_error_location));
 
                return error_node();
@@ -22503,13 +23492,13 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR088 - Expected '}' at end of [*] statement",
+                          "ERR092 - Expected '}' at end of [*] statement",
                           exprtk_error_location));
 
             return error_node();
          }
 
-         result = expression_generator_.multi_switch_statement(arg_list);
+         const expression_node_ptr result = expression_generator_.multi_switch_statement(arg_list);
 
          svd.delete_ptr = (0 == result);
 
@@ -22519,7 +23508,6 @@ namespace exprtk
       inline expression_node_ptr parse_vararg_function()
       {
          std::vector<expression_node_ptr> arg_list;
-         expression_node_ptr result = error_node();
 
          details::operator_type opt_type = details::e_default;
          const std::string symbol = current_token().value;
@@ -22545,7 +23533,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR089 - Unsupported vararg function: " + symbol,
+                          "ERR093 - Unsupported vararg function: " + symbol,
                           exprtk_error_location));
 
             return error_node();
@@ -22562,7 +23550,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR090 - Expected '(' for call to vararg function: " + symbol,
+                          "ERR094 - Expected '(' for call to vararg function: " + symbol,
                           exprtk_error_location));
 
             return error_node();
@@ -22584,14 +23572,14 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR091 - Expected ',' for call to vararg function: " + symbol,
+                             "ERR095 - Expected ',' for call to vararg function: " + symbol,
                              exprtk_error_location));
 
                return error_node();
             }
          }
 
-         result = expression_generator_.vararg_function(opt_type,arg_list);
+         const expression_node_ptr result = expression_generator_.vararg_function(opt_type,arg_list);
 
          sdd.delete_ptr = (0 == result);
          return result;
@@ -22605,7 +23593,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR092 - Expected '[' as start of string range definition",
+                          "ERR096 - Expected '[' as start of string range definition",
                           exprtk_error_location));
 
             free_node(node_allocator_,expression);
@@ -22633,10 +23621,11 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR093 - Failed to generate string range node",
+                          "ERR097 - Failed to generate string range node",
                           exprtk_error_location));
 
             free_node(node_allocator_,expression);
+            rp.free();
          }
 
          rp.clear();
@@ -22769,7 +23758,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR094 - Expected '" + token_t::to_str(close_bracket) + "' for call to multi-sequence" +
+                             "ERR098 - Expected '" + token_t::to_str(close_bracket) + "' for call to multi-sequence" +
                              ((!source.empty()) ? std::string(" section of " + source): ""),
                              exprtk_error_location));
 
@@ -22809,14 +23798,14 @@ namespace exprtk
             if (token_is(close_bracket))
                break;
 
-            bool is_next_close = peek_token_is(close_bracket);
+            const bool is_next_close = peek_token_is(close_bracket);
 
             if (!token_is(seperator) && is_next_close)
             {
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR095 - Expected '" + details::to_str(seperator) + "' for call to multi-sequence section of " + source,
+                             "ERR099 - Expected '" + details::to_str(seperator) + "' for call to multi-sequence section of " + source,
                              exprtk_error_location));
 
                return error_node();
@@ -22850,7 +23839,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR096 - Expected '[' for start of range",
+                          "ERR100 - Expected '[' for start of range",
                           exprtk_error_location));
 
             return false;
@@ -22871,7 +23860,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR097 - Failed parse begin section of range",
+                             "ERR101 - Failed parse begin section of range",
                              exprtk_error_location));
 
                return false;
@@ -22894,7 +23883,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR098 - Range lower bound less than zero! Constraint: r0 >= 0",
+                                "ERR102 - Range lower bound less than zero! Constraint: r0 >= 0",
                                 exprtk_error_location));
 
                   return false;
@@ -22911,7 +23900,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR099 - Expected ':' for break  in range",
+                             "ERR103 - Expected ':' for break  in range",
                              exprtk_error_location));
 
                rp.free();
@@ -22934,7 +23923,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR100 - Failed parse end section of range",
+                             "ERR104 - Failed parse end section of range",
                              exprtk_error_location));
 
                rp.free();
@@ -22959,9 +23948,11 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR101 - Range upper bound less than zero! Constraint: r1 >= 0",
+                                "ERR105 - Range upper bound less than zero! Constraint: r1 >= 0",
                                 exprtk_error_location));
 
+                  rp.free();
+
                   return false;
                }
             }
@@ -22976,7 +23967,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR102 - Expected ']' for start of range",
+                             "ERR106 - Expected ']' for start of range",
                              exprtk_error_location));
 
                rp.free();
@@ -22990,14 +23981,21 @@ namespace exprtk
             std::size_t r0 = 0;
             std::size_t r1 = 0;
 
-            const bool rp_result = rp(r0,r1);
+            bool rp_result = false;
+
+            try
+            {
+               rp_result = rp(r0, r1);
+            }
+            catch (std::runtime_error&)
+            {}
 
             if (!rp_result || (r0 > r1))
             {
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR103 - Invalid range, Constraint: r0 <= r1",
+                             "ERR107 - Invalid range, Constraint: r0 <= r1",
                              exprtk_error_location));
 
                return false;
@@ -23038,7 +24036,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR104 - Unknown string symbol",
+                             "ERR108 - Unknown string symbol",
                              exprtk_error_location));
 
                return error_node();
@@ -23132,6 +24130,7 @@ namespace exprtk
             if (!parse_range(rp))
             {
                free_node(node_allocator_,result);
+               rp.free();
 
                return error_node();
             }
@@ -23152,11 +24151,13 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR105 - Overflow in range for string: '" + const_str + "'[" +
+                             "ERR109 - Overflow in range for string: '" + const_str + "'[" +
                              (rp.n0_c.first ? details::to_str(static_cast<int>(rp.n0_c.second)) : "?") + ":" +
                              (rp.n1_c.first ? details::to_str(static_cast<int>(rp.n1_c.second)) : "?") + "]",
                              exprtk_error_location));
 
+               rp.free();
+
                return error_node();
             }
 
@@ -23196,7 +24197,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR106 - Symbol '" + symbol+ " not a vector",
+                             "ERR110 - Symbol '" + symbol+ " not a vector",
                              exprtk_error_location));
 
                return error_node();
@@ -23222,7 +24223,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR107 - Failed to parse index for vector: '" + symbol + "'",
+                          "ERR111 - Failed to parse index for vector: '" + symbol + "'",
                           exprtk_error_location));
 
             return error_node();
@@ -23232,7 +24233,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR108 - Expected ']' for index of vector: '" + symbol + "'",
+                          "ERR112 - Expected ']' for index of vector: '" + symbol + "'",
                           exprtk_error_location));
 
             free_node(node_allocator_,index_expr);
@@ -23251,7 +24252,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR109 - Index of " + details::to_str(index) + " out of range for "
+                             "ERR113 - Index of " + details::to_str(index) + " out of range for "
                              "vector '" + symbol + "' of size " + details::to_str(vec_size),
                              exprtk_error_location));
 
@@ -23261,7 +24262,7 @@ namespace exprtk
             }
          }
 
-         return expression_generator_.vector_element(symbol,vec,index_expr);
+         return expression_generator_.vector_element(symbol, vec, index_expr);
       }
 
       inline expression_node_ptr parse_vararg_function_call(ivararg_function<T>* vararg_function, const std::string& vararg_function_name)
@@ -23283,7 +24284,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR110 - Zero parameter call to vararg function: "
+                                "ERR114 - Zero parameter call to vararg function: "
                                 + vararg_function_name + " not allowed",
                                 exprtk_error_location));
 
@@ -23308,7 +24309,7 @@ namespace exprtk
                      set_error(
                         make_error(parser_error::e_syntax,
                                    current_token(),
-                                   "ERR111 - Expected ',' for call to vararg function: "
+                                   "ERR115 - Expected ',' for call to vararg function: "
                                    + vararg_function_name,
                                    exprtk_error_location));
 
@@ -23322,7 +24323,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR112 - Zero parameter call to vararg function: "
+                          "ERR116 - Zero parameter call to vararg function: "
                           + vararg_function_name + " not allowed",
                           exprtk_error_location));
 
@@ -23334,7 +24335,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR113 - Invalid number of parameters to call to vararg function: "
+                          "ERR117 - Invalid number of parameters to call to vararg function: "
                           + vararg_function_name + ", require at least "
                           + details::to_str(static_cast<int>(vararg_function->min_num_args())) + " parameters",
                           exprtk_error_location));
@@ -23346,7 +24347,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR114 - Invalid number of parameters to call to vararg function: "
+                          "ERR118 - Invalid number of parameters to call to vararg function: "
                           + vararg_function_name + ", require no more than "
                           + details::to_str(static_cast<int>(vararg_function->max_num_args())) + " parameters",
                           exprtk_error_location));
@@ -23393,11 +24394,6 @@ namespace exprtk
             parse_function_prototypes(func_prototypes);
          }
 
-         void set_default_return_type(const std::string& return_type)
-         {
-            default_return_type_ = return_type;
-         }
-
          bool verify(const std::string& param_seq, std::size_t& pseq_index)
          {
             if (function_definition_list_.empty())
@@ -23429,7 +24425,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 parser_.current_token(),
-                                "ERR115 - Failed parameter type check for function '" + function_name_ + "', "
+                                "ERR119 - Failed parameter type check for function '" + function_name_ + "', "
                                 "Expected '" + function_definition_list_[0].param_seq +
                                 "'  call set: '" + param_seq + "'",
                                 exprtk_error_location));
@@ -23451,7 +24447,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 parser_.current_token(),
-                                "ERR116 - Failed parameter type check for function '" + function_name_ + "', "
+                                "ERR120 - Failed parameter type check for function '" + function_name_ + "', "
                                 "Best match: '" + function_definition_list_[max_diff_index].param_seq +
                                 "'  call set: '" + param_seq + "'",
                                 exprtk_error_location));
@@ -23508,8 +24504,7 @@ namespace exprtk
                  if (*iter == delimiter)
                  {
                      result.push_back(std::string(current_begin, iter));
-                     ++iter;
-                     current_begin = iter;
+                     current_begin = ++iter;
                  }
                  else
                      ++iter;
@@ -23594,7 +24589,7 @@ namespace exprtk
                      set_error(
                         make_error(parser_error::e_syntax,
                                    parser_.current_token(),
-                                   "ERR117 - Invalid parameter sequence of '" + param_seq_list[i] +
+                                   "ERR121 - Invalid parameter sequence of '" + param_seq_list[i] +
                                    "' for function: " + function_name_,
                                    exprtk_error_location));
                   return;
@@ -23610,7 +24605,7 @@ namespace exprtk
                      set_error(
                         make_error(parser_error::e_syntax,
                                    parser_.current_token(),
-                                   "ERR118 - Function '" + function_name_ + "' has a parameter sequence conflict between " +
+                                   "ERR122 - Function '" + function_name_ + "' has a parameter sequence conflict between " +
                                    "pseq_idx[" + details::to_str(seq_itr->second) + "] and" +
                                    "pseq_idx[" + details::to_str(i) + "] " +
                                    "param seq: " + param_seq_list[i],
@@ -23649,7 +24644,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR119 - Type checker instantiation failure for generic function: " + function_name,
+                          "ERR123 - Type checker instantiation failure for generic function: " + function_name,
                           exprtk_error_location));
 
             return error_node();
@@ -23667,7 +24662,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR120 - Zero parameter call to generic function: "
+                                "ERR124 - Zero parameter call to generic function: "
                                 + function_name + " not allowed",
                                 exprtk_error_location));
 
@@ -23699,7 +24694,7 @@ namespace exprtk
                      set_error(
                         make_error(parser_error::e_syntax,
                                    current_token(),
-                                   "ERR121 - Expected ',' for call to generic function: " + function_name,
+                                   "ERR125 - Expected ',' for call to generic function: " + function_name,
                                    exprtk_error_location));
 
                      return error_node();
@@ -23716,7 +24711,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR122 - Zero parameter call to generic function: "
+                          "ERR126 - Zero parameter call to generic function: "
                           + function_name + " not allowed",
                           exprtk_error_location));
 
@@ -23733,7 +24728,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR123 - Invalid input parameter sequence for call to generic function: " + function_name,
+                          "ERR127 - Invalid input parameter sequence for call to generic function: " + function_name,
                           exprtk_error_location));
 
             return error_node();
@@ -23755,7 +24750,7 @@ namespace exprtk
 
       inline bool parse_igeneric_function_params(std::string& param_type_list,
                                                  std::vector<expression_node_ptr>& arg_list,
-                                                 const std::string function_name,
+                                                 const std::string& function_name,
                                                  igeneric_function<T>* function,
                                                  const type_checker& tc)
       {
@@ -23771,7 +24766,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR124 - Zero parameter call to generic function: "
+                                "ERR128 - Zero parameter call to generic function: "
                                 + function_name + " not allowed",
                                 exprtk_error_location));
 
@@ -23803,7 +24798,7 @@ namespace exprtk
                      set_error(
                         make_error(parser_error::e_syntax,
                                    current_token(),
-                                   "ERR125 - Expected ',' for call to string function: " + function_name,
+                                   "ERR129 - Expected ',' for call to string function: " + function_name,
                                    exprtk_error_location));
 
                      return false;
@@ -23850,7 +24845,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR126 - Invalid input parameter sequence for call to string function: " + function_name,
+                          "ERR130 - Invalid input parameter sequence for call to string function: " + function_name,
                           exprtk_error_location));
 
             return error_node();
@@ -23902,7 +24897,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR127 - Invalid input parameter sequence for call to overloaded function: " + function_name,
+                          "ERR131 - Invalid input parameter sequence for call to overloaded function: " + function_name,
                           exprtk_error_location));
 
             return error_node();
@@ -23933,7 +24928,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR128 - Invalid return type for call to overloaded function: " + function_name,
+                          "ERR132 - Invalid return type for call to overloaded function: " + function_name,
                           exprtk_error_location));
          }
 
@@ -23945,12 +24940,12 @@ namespace exprtk
       template <typename Type, std::size_t NumberOfParameters>
       struct parse_special_function_impl
       {
-         static inline expression_node_ptr process(parser<Type>& p,const details::operator_type opt_type, const std::string& sf_name)
+         static inline expression_node_ptr process(parser<Type>& p, const details::operator_type opt_type, const std::string& sf_name)
          {
             expression_node_ptr branch[NumberOfParameters];
-            expression_node_ptr result  = error_node();
+            expression_node_ptr result = error_node();
 
-            std::fill_n(branch,NumberOfParameters,reinterpret_cast<expression_node_ptr>(0));
+            std::fill_n(branch, NumberOfParameters, reinterpret_cast<expression_node_ptr>(0));
 
             scoped_delete<expression_node_t,NumberOfParameters> sd(p,branch);
 
@@ -23961,7 +24956,7 @@ namespace exprtk
                p.set_error(
                     make_error(parser_error::e_syntax,
                                p.current_token(),
-                               "ERR129 - Expected '(' for special function '" + sf_name + "'",
+                               "ERR133 - Expected '(' for special function '" + sf_name + "'",
                                exprtk_error_location));
 
                return error_node();
@@ -23982,7 +24977,7 @@ namespace exprtk
                      p.set_error(
                           make_error(parser_error::e_syntax,
                                      p.current_token(),
-                                     "ERR130 - Expected ',' before next parameter of special function '" + sf_name + "'",
+                                     "ERR134 - Expected ',' before next parameter of special function '" + sf_name + "'",
                                      exprtk_error_location));
 
                      return p.error_node();
@@ -23995,7 +24990,7 @@ namespace exprtk
                p.set_error(
                     make_error(parser_error::e_syntax,
                                p.current_token(),
-                               "ERR131 - Invalid number of parameters for special function '" + sf_name + "'",
+                               "ERR135 - Invalid number of parameters for special function '" + sf_name + "'",
                                exprtk_error_location));
 
                return p.error_node();
@@ -24022,7 +25017,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_token,
                           current_token(),
-                          "ERR132 - Invalid special function[1]: " + sf_name,
+                          "ERR136 - Invalid special function[1]: " + sf_name,
                           exprtk_error_location));
 
             return error_node();
@@ -24036,7 +25031,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_token,
                           current_token(),
-                          "ERR133 - Invalid special function[2]: " + sf_name,
+                          "ERR137 - Invalid special function[2]: " + sf_name,
                           exprtk_error_location));
 
             return error_node();
@@ -24068,7 +25063,17 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR134 - Break call within a break call is not allowed",
+                          "ERR138 - Invoking 'break' within a break call is not allowed",
+                          exprtk_error_location));
+
+            return error_node();
+         }
+         else if (0 == state_.parsing_loop_stmt_count)
+         {
+            set_error(
+               make_error(parser_error::e_syntax,
+                          current_token(),
+                          "ERR139 - Invalid use of 'break', allowed only in the scope of a loop",
                           exprtk_error_location));
 
             return error_node();
@@ -24091,7 +25096,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR135 - Failed to parse return expression for 'break' statement",
+                                "ERR140 - Failed to parse return expression for 'break' statement",
                                 exprtk_error_location));
 
                   return error_node();
@@ -24101,7 +25106,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR136 - Expected ']' at the completion of break's return expression",
+                                "ERR141 - Expected ']' at the completion of break's return expression",
                                 exprtk_error_location));
 
                   free_node(node_allocator_,return_expr);
@@ -24119,7 +25124,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR137 - Invalid use of 'break', allowed only in the scope of a loop",
+                          "ERR142 - Invalid use of 'break', allowed only in the scope of a loop",
                           exprtk_error_location));
          }
 
@@ -24128,25 +25133,25 @@ namespace exprtk
 
       inline expression_node_ptr parse_continue_statement()
       {
-         if (!brkcnt_list_.empty())
-         {
-            next_token();
-
-            brkcnt_list_.front() = true;
-            state_.activate_side_effect("parse_continue_statement()");
-
-            return node_allocator_.allocate<details::continue_node<T> >();
-         }
-         else
+         if (0 == state_.parsing_loop_stmt_count)
          {
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR138 - Invalid use of 'continue', allowed only in the scope of a loop",
+                          "ERR143 - Invalid use of 'continue', allowed only in the scope of a loop",
                           exprtk_error_location));
 
             return error_node();
          }
+         else
+         {
+            next_token();
+
+            brkcnt_list_.front() = true;
+            state_.activate_side_effect("parse_continue_statement()");
+
+            return node_allocator_.allocate<details::continue_node<T> >();
+         }
       }
       #endif
 
@@ -24159,7 +25164,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR139 - Expected '[' as part of vector size definition",
+                          "ERR144 - Expected '[' as part of vector size definition",
                           exprtk_error_location));
 
             return error_node();
@@ -24169,7 +25174,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR140 - Failed to determine size of vector '" + vec_name + "'",
+                          "ERR145 - Failed to determine size of vector '" + vec_name + "'",
                           exprtk_error_location));
 
             return error_node();
@@ -24181,13 +25186,13 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR141 - Expected a literal number as size of vector '" + vec_name + "'",
+                          "ERR146 - Expected a literal number as size of vector '" + vec_name + "'",
                           exprtk_error_location));
 
             return error_node();
          }
 
-         T vector_size = size_expr->value();
+         const T vector_size = size_expr->value();
 
          free_node(node_allocator_,size_expr);
 
@@ -24203,7 +25208,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR142 - Invalid vector size. Must be an integer in the range [0,2e9], size: " +
+                          "ERR147 - Invalid vector size. Must be an integer in the range [0,2e9], size: " +
                           details::to_str(details::numeric::to_int32(vector_size)),
                           exprtk_error_location));
 
@@ -24223,7 +25228,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR143 - Expected ']' as part of vector size definition",
+                          "ERR148 - Expected ']' as part of vector size definition",
                           exprtk_error_location));
 
             return error_node();
@@ -24235,7 +25240,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR144 - Expected ':=' as part of vector definition",
+                             "ERR149 - Expected ':=' as part of vector definition",
                              exprtk_error_location));
 
                return error_node();
@@ -24249,7 +25254,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR145 - Failed to parse single vector initialiser",
+                                "ERR150 - Failed to parse single vector initialiser",
                                 exprtk_error_location));
 
                   return error_node();
@@ -24262,7 +25267,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR146 - Expected ']' to close single value vector initialiser",
+                                "ERR151 - Expected ']' to close single value vector initialiser",
                                 exprtk_error_location));
 
                   return error_node();
@@ -24278,7 +25283,7 @@ namespace exprtk
                if (token_t::e_symbol == current_token().type)
                {
                   // Is it a locally defined vector?
-                  scope_element& se = sem_.get_active_element(current_token().value);
+                  const scope_element& se = sem_.get_active_element(current_token().value);
 
                   if (scope_element::e_vector == se.type)
                   {
@@ -24309,7 +25314,7 @@ namespace exprtk
                      set_error(
                         make_error(parser_error::e_syntax,
                                    current_token(),
-                                   "ERR147 - Expected '{' as part of vector initialiser list",
+                                   "ERR152 - Expected '{' as part of vector initialiser list",
                                    exprtk_error_location));
 
                      return error_node();
@@ -24329,7 +25334,7 @@ namespace exprtk
                      set_error(
                         make_error(parser_error::e_syntax,
                                    current_token(),
-                                   "ERR148 - Expected '{' as part of vector initialiser list",
+                                   "ERR153 - Expected '{' as part of vector initialiser list",
                                    exprtk_error_location));
 
                      return error_node();
@@ -24340,14 +25345,14 @@ namespace exprtk
                   if (token_is(token_t::e_rcrlbracket))
                      break;
 
-                  bool is_next_close = peek_token_is(token_t::e_rcrlbracket);
+                  const bool is_next_close = peek_token_is(token_t::e_rcrlbracket);
 
                   if (!token_is(token_t::e_comma) && is_next_close)
                   {
                      set_error(
                         make_error(parser_error::e_syntax,
                                    current_token(),
-                                   "ERR149 - Expected ',' between vector initialisers",
+                                   "ERR154 - Expected ',' between vector initialisers",
                                    exprtk_error_location));
 
                      return error_node();
@@ -24369,7 +25374,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR150 - Expected ';' at end of vector definition",
+                                "ERR155 - Expected ';' at end of vector definition",
                                 exprtk_error_location));
 
                   return error_node();
@@ -24381,7 +25386,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR151 - Initialiser list larger than the number of elements in the vector: '" + vec_name + "'",
+                             "ERR156 - Initialiser list larger than the number of elements in the vector: '" + vec_name + "'",
                              exprtk_error_location));
 
                return error_node();
@@ -24401,7 +25406,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR152 - Illegal redefinition of local vector: '" + vec_name + "'",
+                             "ERR157 - Illegal redefinition of local vector: '" + vec_name + "'",
                              exprtk_error_location));
 
                return error_node();
@@ -24428,14 +25433,14 @@ namespace exprtk
             nse.depth     = state_.scope_depth;
             nse.size      = vec_size;
             nse.data      = new T[vec_size];
-            nse.vec_node  = new typename scope_element::vector_holder_t((T*)(nse.data),nse.size);
+            nse.vec_node  = new typename scope_element::vector_holder_t(reinterpret_cast<T*>(nse.data),nse.size);
 
             if (!sem_.add_element(nse))
             {
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR153 - Failed to add new local vector '" + vec_name + "' to SEM",
+                             "ERR158 - Failed to add new local vector '" + vec_name + "' to SEM",
                              exprtk_error_location));
 
                sem_.free_element(nse);
@@ -24494,7 +25499,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR154 - Illegal redefinition of local variable: '" + str_name + "'",
+                             "ERR159 - Illegal redefinition of local variable: '" + str_name + "'",
                              exprtk_error_location));
 
                free_node(node_allocator_,initialisation_expression);
@@ -24519,14 +25524,14 @@ namespace exprtk
             nse.type      = scope_element::e_string;
             nse.depth     = state_.scope_depth;
             nse.data      = new std::string;
-            nse.str_node  = new stringvar_node_t(*(std::string*)(nse.data));
+            nse.str_node  = new stringvar_node_t(*reinterpret_cast<std::string*>(nse.data));
 
             if (!sem_.add_element(nse))
             {
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR155 - Failed to add new local string variable '" + str_name + "' to SEM",
+                             "ERR160 - Failed to add new local string variable '" + str_name + "' to SEM",
                              exprtk_error_location));
 
                free_node(node_allocator_,initialisation_expression);
@@ -24572,7 +25577,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR156 - Illegal variable definition",
+                          "ERR161 - Illegal variable definition",
                           exprtk_error_location));
 
             return error_node();
@@ -24593,7 +25598,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR157 - Expected a symbol for variable definition",
+                          "ERR162 - Expected a symbol for variable definition",
                           exprtk_error_location));
 
             return error_node();
@@ -24603,7 +25608,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR158 - Illegal redefinition of reserved keyword: '" + var_name + "'",
+                          "ERR163 - Illegal redefinition of reserved keyword: '" + var_name + "'",
                           exprtk_error_location));
 
             return error_node();
@@ -24613,7 +25618,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR159 - Illegal redefinition of variable '" + var_name + "'",
+                          "ERR164 - Illegal redefinition of variable '" + var_name + "'",
                           exprtk_error_location));
 
             return error_node();
@@ -24623,7 +25628,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR160 - Illegal redefinition of local variable: '" + var_name + "'",
+                          "ERR165 - Illegal redefinition of local variable: '" + var_name + "'",
                           exprtk_error_location));
 
             return error_node();
@@ -24643,7 +25648,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR161 - Failed to parse initialisation expression",
+                             "ERR166 - Failed to parse initialisation expression",
                              exprtk_error_location));
 
                return error_node();
@@ -24661,7 +25666,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR162 - Expected ';' after variable definition",
+                             "ERR167 - Expected ';' after variable definition",
                              exprtk_error_location));
 
                free_node(node_allocator_,initialisation_expression);
@@ -24689,7 +25694,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR163 - Illegal redefinition of local variable: '" + var_name + "'",
+                             "ERR168 - Illegal redefinition of local variable: '" + var_name + "'",
                              exprtk_error_location));
 
                free_node(node_allocator_, initialisation_expression);
@@ -24714,14 +25719,14 @@ namespace exprtk
             nse.type      = scope_element::e_variable;
             nse.depth     = state_.scope_depth;
             nse.data      = new T(T(0));
-            nse.var_node  = node_allocator_.allocate<variable_node_t>(*(T*)(nse.data));
+            nse.var_node  = node_allocator_.allocate<variable_node_t>(*reinterpret_cast<T*>(nse.data));
 
             if (!sem_.add_element(nse))
             {
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR164 - Failed to add new local variable '" + var_name + "' to SEM",
+                             "ERR169 - Failed to add new local variable '" + var_name + "' to SEM",
                              exprtk_error_location));
 
                free_node(node_allocator_, initialisation_expression);
@@ -24758,7 +25763,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR165 - Expected a '{}' for uninitialised var definition",
+                          "ERR170 - Expected a '{}' for uninitialised var definition",
                           exprtk_error_location));
 
             return error_node();
@@ -24768,7 +25773,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR166 - Expected ';' after uninitialised variable definition",
+                          "ERR171 - Expected ';' after uninitialised variable definition",
                           exprtk_error_location));
 
             return error_node();
@@ -24785,7 +25790,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR167 - Illegal redefinition of local variable: '" + var_name + "'",
+                             "ERR172 - Illegal redefinition of local variable: '" + var_name + "'",
                              exprtk_error_location));
 
                return error_node();
@@ -24808,14 +25813,14 @@ namespace exprtk
             nse.depth     = state_.scope_depth;
             nse.ip_index  = sem_.next_ip_index();
             nse.data      = new T(T(0));
-            nse.var_node  = node_allocator_.allocate<variable_node_t>(*(T*)(nse.data));
+            nse.var_node  = node_allocator_.allocate<variable_node_t>(*reinterpret_cast<T*>(nse.data));
 
             if (!sem_.add_element(nse))
             {
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR168 - Failed to add new local variable '" + var_name + "' to SEM",
+                             "ERR173 - Failed to add new local variable '" + var_name + "' to SEM",
                              exprtk_error_location));
 
                sem_.free_element(nse);
@@ -24848,7 +25853,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR169 - Expected '(' at start of swap statement",
+                          "ERR174 - Expected '(' at start of swap statement",
                           exprtk_error_location));
 
             return error_node();
@@ -24867,7 +25872,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR170 - Expected a symbol for variable or vector element definition",
+                          "ERR175 - Expected a symbol for variable or vector element definition",
                           exprtk_error_location));
 
             return error_node();
@@ -24879,7 +25884,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR171 - First parameter to swap is an invalid vector element: '" + var0_name + "'",
+                             "ERR176 - First parameter to swap is an invalid vector element: '" + var0_name + "'",
                              exprtk_error_location));
 
                return error_node();
@@ -24894,7 +25899,7 @@ namespace exprtk
                variable0 = symtab_store_.get_variable(var0_name);
             }
 
-            scope_element& se = sem_.get_element(var0_name);
+            const scope_element& se = sem_.get_element(var0_name);
 
             if (
                  (se.active)            &&
@@ -24912,7 +25917,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR172 - First parameter to swap is an invalid variable: '" + var0_name + "'",
+                             "ERR177 - First parameter to swap is an invalid variable: '" + var0_name + "'",
                              exprtk_error_location));
 
                return error_node();
@@ -24926,7 +25931,7 @@ namespace exprtk
             set_error(
                 make_error(parser_error::e_syntax,
                            current_token(),
-                           "ERR173 - Expected ',' between parameters to swap",
+                           "ERR178 - Expected ',' between parameters to swap",
                            exprtk_error_location));
 
             if (variable0_generated)
@@ -24944,7 +25949,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR174 - Expected a symbol for variable or vector element definition",
+                          "ERR179 - Expected a symbol for variable or vector element definition",
                           exprtk_error_location));
 
             if (variable0_generated)
@@ -24961,7 +25966,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR175 - Second parameter to swap is an invalid vector element: '" + var1_name + "'",
+                             "ERR180 - Second parameter to swap is an invalid vector element: '" + var1_name + "'",
                              exprtk_error_location));
 
                if (variable0_generated)
@@ -24981,7 +25986,7 @@ namespace exprtk
                variable1 = symtab_store_.get_variable(var1_name);
             }
 
-            scope_element& se = sem_.get_element(var1_name);
+            const scope_element& se = sem_.get_element(var1_name);
 
             if (
                  (se.active) &&
@@ -24999,7 +26004,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR176 - Second parameter to swap is an invalid variable: '" + var1_name + "'",
+                             "ERR181 - Second parameter to swap is an invalid variable: '" + var1_name + "'",
                              exprtk_error_location));
 
                if (variable0_generated)
@@ -25018,7 +26023,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR177 - Expected ')' at end of swap statement",
+                          "ERR182 - Expected ')' at end of swap statement",
                           exprtk_error_location));
 
             if (variable0_generated)
@@ -25075,7 +26080,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR178 - Return call within a return call is not allowed",
+                          "ERR183 - Return call within a return call is not allowed",
                           exprtk_error_location));
 
             return error_node();
@@ -25099,7 +26104,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR179 - Expected '[' at start of return statement",
+                          "ERR184 - Expected '[' at start of return statement",
                           exprtk_error_location));
 
             return error_node();
@@ -25122,7 +26127,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR180 - Expected ',' between values during call to return",
+                                "ERR185 - Expected ',' between values during call to return",
                                 exprtk_error_location));
 
                   return error_node();
@@ -25134,7 +26139,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR181 - Zero parameter return statement not allowed",
+                          "ERR186 - Zero parameter return statement not allowed",
                           exprtk_error_location));
 
             return error_node();
@@ -25149,7 +26154,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              prev_token,
-                             "ERR182 - Invalid ']' found during return call",
+                             "ERR187 - Invalid ']' found during return call",
                              exprtk_error_location));
 
                return error_node();
@@ -25202,7 +26207,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR183 - Invalid sequence of variable '"+ symbol + "' and bracket",
+                             "ERR188 - Invalid sequence of variable '"+ symbol + "' and bracket",
                              exprtk_error_location));
 
                return false;
@@ -25250,7 +26255,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR184 - Invalid sequence of brackets",
+                             "ERR189 - Invalid sequence of brackets",
                              exprtk_error_location));
 
                return false;
@@ -25347,7 +26352,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR185 - Failed to generate node for function: '" + symbol + "'",
+                                "ERR190 - Failed to generate node for function: '" + symbol + "'",
                                 exprtk_error_location));
 
                   return error_node();
@@ -25373,7 +26378,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR186 - Failed to generate node for vararg function: '" + symbol + "'",
+                                "ERR191 - Failed to generate node for vararg function: '" + symbol + "'",
                                 exprtk_error_location));
 
                   return error_node();
@@ -25399,7 +26404,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR187 - Failed to generate node for generic function: '" + symbol + "'",
+                                "ERR192 - Failed to generate node for generic function: '" + symbol + "'",
                                 exprtk_error_location));
 
                   return error_node();
@@ -25426,7 +26431,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR188 - Failed to generate node for string function: '" + symbol + "'",
+                                "ERR193 - Failed to generate node for string function: '" + symbol + "'",
                                 exprtk_error_location));
 
                   return error_node();
@@ -25452,7 +26457,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR189 - Failed to generate node for overload function: '" + symbol + "'",
+                                "ERR194 - Failed to generate node for overload function: '" + symbol + "'",
                                 exprtk_error_location));
 
                   return error_node();
@@ -25478,7 +26483,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_syntax,
                                 current_token(),
-                                "ERR190 - Invalid use of reserved symbol '" + symbol + "'",
+                                "ERR195 - Invalid use of reserved symbol '" + symbol + "'",
                                 exprtk_error_location));
 
                   return error_node();
@@ -25541,7 +26546,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_symtab,
                                 current_token(),
-                                "ERR191 - Failed to create variable: '" + symbol + "'" +
+                                "ERR196 - Failed to create variable: '" + symbol + "'" +
                                 (error_message.empty() ? "" : " - " + error_message),
                                 exprtk_error_location));
 
@@ -25561,7 +26566,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_symtab,
                                 current_token(),
-                                "ERR192 - Failed to resolve symbol: '" + symbol + "'" +
+                                "ERR197 - Failed to resolve symbol: '" + symbol + "'" +
                                 (error_message.empty() ? "" : " - " + error_message),
                                 exprtk_error_location));
                }
@@ -25573,7 +26578,7 @@ namespace exprtk
          set_error(
             make_error(parser_error::e_syntax,
                        current_token(),
-                       "ERR193 - Undefined symbol: '" + symbol + "'",
+                       "ERR198 - Undefined symbol: '" + symbol + "'",
                        exprtk_error_location));
 
          return error_node();
@@ -25592,11 +26597,16 @@ namespace exprtk
          static const std::string symbol_var      = "var"     ;
          static const std::string symbol_swap     = "swap"    ;
          static const std::string symbol_return   = "return"  ;
+         static const std::string symbol_not      = "not"     ;
 
          if (valid_vararg_operation(current_token().value))
          {
             return parse_vararg_function();
          }
+         else if (details::imatch(current_token().value, symbol_not))
+         {
+            return parse_not_statement();
+         }
          else if (valid_base_operation(current_token().value))
          {
             return parse_base_operation();
@@ -25680,7 +26690,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_symtab,
                           current_token(),
-                          "ERR194 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token().value,
+                          "ERR199 - Variable or function detected, yet symbol-table is invalid, Symbol: " + current_token().value,
                           exprtk_error_location));
 
             return error_node();
@@ -25689,6 +26699,13 @@ namespace exprtk
 
       inline expression_node_ptr parse_branch(precedence_level precedence = e_level00)
       {
+         stack_limit_handler slh(*this);
+
+         if (!slh)
+         {
+            return error_node();
+         }
+
          expression_node_ptr branch = error_node();
 
          if (token_t::e_number == current_token().type)
@@ -25704,7 +26721,7 @@ namespace exprtk
                   set_error(
                      make_error(parser_error::e_numeric,
                                 current_token(),
-                                "ERR195 - Failed generate node for scalar: '" + current_token().value + "'",
+                                "ERR200 - Failed generate node for scalar: '" + current_token().value + "'",
                                 exprtk_error_location));
 
                   return error_node();
@@ -25718,7 +26735,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_numeric,
                              current_token(),
-                             "ERR196 - Failed to convert '" + current_token().value + "' to a number",
+                             "ERR201 - Failed to convert '" + current_token().value + "' to a number",
                              exprtk_error_location));
 
                return error_node();
@@ -25745,7 +26762,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR197 - Expected ')' instead of: '" + current_token().value + "'",
+                             "ERR202 - Expected ')' instead of: '" + current_token().value + "'",
                              exprtk_error_location));
 
                free_node(node_allocator_,branch);
@@ -25770,7 +26787,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR198 - Expected ']' instead of: '" + current_token().value + "'",
+                             "ERR203 - Expected ']' instead of: '" + current_token().value + "'",
                              exprtk_error_location));
 
                free_node(node_allocator_,branch);
@@ -25795,7 +26812,7 @@ namespace exprtk
                set_error(
                   make_error(parser_error::e_syntax,
                              current_token(),
-                             "ERR199 - Expected '}' instead of: '" + current_token().value + "'",
+                             "ERR204 - Expected '}' instead of: '" + current_token().value + "'",
                              exprtk_error_location));
 
                free_node(node_allocator_,branch);
@@ -25822,7 +26839,16 @@ namespace exprtk
                   )
                )
             {
-               branch = expression_generator_(details::e_neg,branch);
+               expression_node_ptr result = expression_generator_(details::e_neg,branch);
+
+               if (0 == result)
+               {
+                  free_node(node_allocator_,branch);
+
+                  return error_node();
+               }
+               else
+                  branch = result;
             }
          }
          else if (token_t::e_add == current_token().type)
@@ -25835,7 +26861,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR200 - Premature end of expression[1]",
+                          "ERR205 - Premature end of expression[1]",
                           exprtk_error_location));
 
             return error_node();
@@ -25845,7 +26871,7 @@ namespace exprtk
             set_error(
                make_error(parser_error::e_syntax,
                           current_token(),
-                          "ERR201 - Premature end of expression[2]",
+                          "ERR206 - Premature end of expression[2]",
                           exprtk_error_location));
 
             return error_node();
@@ -26839,6 +27865,19 @@ namespace exprtk
          }
          #endif
 
+         inline loop_runtime_check_ptr get_loop_runtime_check(const loop_runtime_check::loop_types loop_type) const
+         {
+            if (
+                 parser_->loop_runtime_check_ &&
+                 (loop_type == (parser_->loop_runtime_check_->loop_set & loop_type))
+               )
+            {
+               return parser_->loop_runtime_check_;
+            }
+
+            return loop_runtime_check_ptr(0);
+         }
+
          inline expression_node_ptr while_loop(expression_node_ptr& condition,
                                                expression_node_ptr& branch,
                                                const bool brkcont = false) const
@@ -26864,10 +27903,20 @@ namespace exprtk
                return branch;
             }
             else if (!brkcont)
-               return node_allocator_->allocate<while_loop_node_t>(condition,branch);
+               return node_allocator_->allocate<while_loop_node_t>
+                        (
+                          condition,
+                          branch,
+                          get_loop_runtime_check(loop_runtime_check::e_while_loop)
+                        );
             #ifndef exprtk_disable_break_continue
             else
-               return node_allocator_->allocate<while_loop_bc_node_t>(condition,branch);
+               return node_allocator_->allocate<while_loop_bc_node_t>
+                        (
+                          condition,
+                          branch,
+                          get_loop_runtime_check(loop_runtime_check::e_while_loop)
+                        );
             #else
                return error_node();
             #endif
@@ -26901,10 +27950,20 @@ namespace exprtk
                return branch;
             }
             else if (!brkcont)
-               return node_allocator_->allocate<repeat_until_loop_node_t>(condition,branch);
+               return node_allocator_->allocate<repeat_until_loop_node_t>
+                        (
+                          condition,
+                          branch,
+                          get_loop_runtime_check(loop_runtime_check::e_repeat_until_loop)
+                        );
             #ifndef exprtk_disable_break_continue
             else
-               return node_allocator_->allocate<repeat_until_loop_bc_node_t>(condition,branch);
+               return node_allocator_->allocate<repeat_until_loop_bc_node_t>
+                        (
+                          condition,
+                          branch,
+                          get_loop_runtime_check(loop_runtime_check::e_repeat_until_loop)
+                        );
             #else
                return error_node();
             #endif
@@ -26947,7 +28006,8 @@ namespace exprtk
                                          initialiser,
                                          condition,
                                          incrementor,
-                                         loop_body
+                                         loop_body,
+                                         get_loop_runtime_check(loop_runtime_check::e_for_loop)
                                        );
 
             #ifndef exprtk_disable_break_continue
@@ -26957,7 +28017,8 @@ namespace exprtk
                                          initialiser,
                                          condition,
                                          incrementor,
-                                         loop_body
+                                         loop_body,
+                                         get_loop_runtime_check(loop_runtime_check::e_for_loop)
                                        );
             #else
             return error_node();
@@ -27038,10 +28099,10 @@ namespace exprtk
 
          struct switch_nodes
          {
-            typedef std::vector<expression_node_ptr> arg_list_t;
+            typedef std::vector<std::pair<expression_node_ptr,bool> > arg_list_t;
 
-            #define case_stmt(N)                                             \
-            if (is_true(arg[(2 * N)])) { return arg[(2 * N) + 1]->value(); } \
+            #define case_stmt(N)                                                         \
+            if (is_true(arg[(2 * N)].first)) { return arg[(2 * N) + 1].first->value(); } \
 
             struct switch_1
             {
@@ -27049,7 +28110,7 @@ namespace exprtk
                {
                   case_stmt(0)
 
-                  return arg.back()->value();
+                  return arg.back().first->value();
                }
             };
 
@@ -27059,7 +28120,7 @@ namespace exprtk
                {
                   case_stmt(0) case_stmt(1)
 
-                  return arg.back()->value();
+                  return arg.back().first->value();
                }
             };
 
@@ -27070,7 +28131,7 @@ namespace exprtk
                   case_stmt(0) case_stmt(1)
                   case_stmt(2)
 
-                  return arg.back()->value();
+                  return arg.back().first->value();
                }
             };
 
@@ -27081,7 +28142,7 @@ namespace exprtk
                   case_stmt(0) case_stmt(1)
                   case_stmt(2) case_stmt(3)
 
-                  return arg.back()->value();
+                  return arg.back().first->value();
                }
             };
 
@@ -27093,7 +28154,7 @@ namespace exprtk
                   case_stmt(2) case_stmt(3)
                   case_stmt(4)
 
-                  return arg.back()->value();
+                  return arg.back().first->value();
                }
             };
 
@@ -27105,7 +28166,7 @@ namespace exprtk
                   case_stmt(2) case_stmt(3)
                   case_stmt(4) case_stmt(5)
 
-                  return arg.back()->value();
+                  return arg.back().first->value();
                }
             };
 
@@ -27118,7 +28179,7 @@ namespace exprtk
                   case_stmt(4) case_stmt(5)
                   case_stmt(6)
 
-                  return arg.back()->value();
+                  return arg.back().first->value();
                }
             };
 
@@ -27127,14 +28188,13 @@ namespace exprtk
 
          template <typename Allocator,
                    template <typename, typename> class Sequence>
-         inline expression_node_ptr switch_statement(Sequence<expression_node_ptr,Allocator>& arg_list)
+         inline expression_node_ptr switch_statement(Sequence<expression_node_ptr,Allocator>& arg_list, const bool default_statement_present)
          {
             if (arg_list.empty())
                return error_node();
             else if (
-                      !all_nodes_valid(arg_list)   ||
-                      (arg_list.size() < 3)        ||
-                      ((arg_list.size() % 2) != 1)
+                      !all_nodes_valid(arg_list) ||
+                      (!default_statement_present && (arg_list.size() < 2))
                     )
             {
                details::free_all_nodes(*node_allocator_,arg_list);
@@ -27631,24 +28691,31 @@ namespace exprtk
                if (details::is_constant_node(result))
                   return result;
                else if (!all_nodes_valid(b))
+               {
+                  details::free_node(*node_allocator_,result);
+                  std::fill_n(b, N, reinterpret_cast<expression_node_ptr>(0));
+
                   return error_node();
+               }
                else if (N != f->param_count)
                {
-                  details::free_all_nodes(*node_allocator_,b);
+                  details::free_node(*node_allocator_,result);
+                  std::fill_n(b, N, reinterpret_cast<expression_node_ptr>(0));
 
                   return error_node();
                }
 
-               function_N_node_t* func_node_ptr = static_cast<function_N_node_t*>(result);
+               function_N_node_t* func_node_ptr = reinterpret_cast<function_N_node_t*>(result);
 
-               if (func_node_ptr->init_branches(b))
-                  return result;
-               else
+               if (!func_node_ptr->init_branches(b))
                {
-                  details::free_all_nodes(*node_allocator_,b);
+                  details::free_node(*node_allocator_,result);
+                  std::fill_n(b, N, reinterpret_cast<expression_node_ptr>(0));
 
                   return error_node();
                }
+
+               return result;
             }
          }
 
@@ -27872,7 +28939,7 @@ namespace exprtk
                   return node_allocator_->allocate<rebasevector_celem_node_t>(i,vector_base);
                }
 
-               scope_element& se = parser_->sem_.get_element(symbol,i);
+               const scope_element& se = parser_->sem_.get_element(symbol,i);
 
                if (se.index == i)
                {
@@ -28724,8 +29791,9 @@ namespace exprtk
                {
                   expression_node_ptr result = error_node();
 
-                  const bool synthesis_result = synthesize_sf4ext_expression::template compile_right<vtype>
-                                                  (expr_gen, v, operation, branch[1], result);
+                  const bool synthesis_result =
+                     synthesize_sf4ext_expression::template compile_right<vtype>
+                        (expr_gen, v, operation, branch[1], result);
 
                   if (synthesis_result)
                   {
@@ -28798,8 +29866,9 @@ namespace exprtk
                {
                   expression_node_ptr result = error_node();
 
-                  const bool synthesis_result = synthesize_sf4ext_expression::template compile_left<vtype>
-                                                   (expr_gen, v, operation, branch[0], result);
+                  const bool synthesis_result =
+                     synthesize_sf4ext_expression::template compile_left<vtype>
+                        (expr_gen, v, operation, branch[0], result);
 
                   if (synthesis_result)
                   {
@@ -28980,7 +30049,11 @@ namespace exprtk
                {
                   expression_node_ptr result = error_node();
 
-                  if (synthesize_sf4ext_expression::template compile_right<ctype>(expr_gen,c,operation,branch[1],result))
+                  const bool synthesis_result =
+                     synthesize_sf4ext_expression::template compile_right<ctype>
+                        (expr_gen, c, operation, branch[1], result);
+
+                  if (synthesis_result)
                   {
                      free_node(*expr_gen.node_allocator_,branch[1]);
 
@@ -29095,8 +30168,9 @@ namespace exprtk
                {
                   expression_node_ptr result = error_node();
 
-                  const bool synthesis_result = synthesize_sf4ext_expression::template compile_left<ctype>
-                                                   (expr_gen, c, operation, branch[0], result);
+                  const bool synthesis_result =
+                     synthesize_sf4ext_expression::template compile_left<ctype>
+                        (expr_gen, c, operation, branch[0], result);
 
                   if (synthesis_result)
                   {
@@ -30450,7 +31524,7 @@ namespace exprtk
 
                const bool synthesis_result =
                   synthesize_sf3ext_expression::template compile<ctype, vtype, ctype>
-                     (expr_gen, id(expr_gen, o0, o1), c0, v, c1,result);
+                     (expr_gen, id(expr_gen, o0, o1), c0, v, c1, result);
 
                if (synthesis_result)
                   return result;
@@ -30946,7 +32020,7 @@ namespace exprtk
 
                const bool synthesis_result =
                   synthesize_sf4ext_expression::template compile<T0, T1, T2, T3>
-                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, v3,result);
+                     (expr_gen, id(expr_gen, o0, o1, o2), v0, v1, v2, v3, result);
 
                if (synthesis_result)
                   return result;
@@ -34572,8 +35646,8 @@ namespace exprtk
          {
             const std::string  s0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->str  ();
                   std::string& s1 = static_cast<details::string_range_node<Type>*>      (branch[1])->ref  ();
-            range_t           rp0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->range();
-            range_t           rp1 = static_cast<details::string_range_node<Type>*>      (branch[1])->range();
+            const range_t     rp0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->range();
+            const range_t     rp1 = static_cast<details::string_range_node<Type>*>      (branch[1])->range();
 
             static_cast<details::const_string_range_node<Type>*>(branch[0])->range_ref().clear();
             static_cast<details::string_range_node<Type>*>      (branch[1])->range_ref().clear();
@@ -34586,9 +35660,9 @@ namespace exprtk
 
          inline expression_node_ptr synthesize_csrocs_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
          {
-            std::string       s0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->str  ();
+            const std::string s0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->str  ();
             const std::string s1 = static_cast<details::string_literal_node<Type>*>    (branch[1])->str  ();
-            range_t          rp0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->range();
+            const range_t    rp0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->range();
 
             static_cast<details::const_string_range_node<Type>*>(branch[0])->range_ref().clear();
 
@@ -34599,10 +35673,10 @@ namespace exprtk
 
          inline expression_node_ptr synthesize_csrocsr_expression(const details::operator_type& opr, expression_node_ptr (&branch)[2])
          {
-            std::string   s0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->str  ();
-            std::string   s1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->str  ();
-            range_t      rp0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->range();
-            range_t      rp1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->range();
+            const std::string s0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->str  ();
+            const std::string s1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->str  ();
+            const range_t    rp0 = static_cast<details::const_string_range_node<Type>*>(branch[0])->range();
+            const range_t    rp1 = static_cast<details::const_string_range_node<Type>*>(branch[1])->range();
 
             static_cast<details::const_string_range_node<Type>*>(branch[0])->range_ref().clear();
             static_cast<details::const_string_range_node<Type>*>(branch[1])->range_ref().clear();
@@ -34847,8 +35921,8 @@ namespace exprtk
 
             typedef typename details::null_eq_node<T> nulleq_node_t;
 
-            bool b0_null = details::is_null_node(branch[0]);
-            bool b1_null = details::is_null_node(branch[1]);
+            const bool b0_null = details::is_null_node(branch[0]);
+            const bool b1_null = details::is_null_node(branch[1]);
 
             if (b0_null && b1_null)
             {
@@ -34909,21 +35983,22 @@ namespace exprtk
             {
                return branch[0];
             }
-            else if (
-                      (details::e_lt    == operation) || (details::e_lte  == operation) ||
-                      (details::e_gt    == operation) || (details::e_gte  == operation) ||
-                      (details::e_and   == operation) || (details::e_nand == operation) ||
-                      (details::e_or    == operation) || (details::e_nor  == operation) ||
-                      (details::e_xor   == operation) || (details::e_xnor == operation) ||
-                      (details::e_in    == operation) || (details::e_like == operation) ||
-                      (details::e_ilike == operation)
-                    )
+
+            details::free_node(*node_allocator_, branch[0]);
+
+            if (
+                 (details::e_lt    == operation) || (details::e_lte  == operation) ||
+                 (details::e_gt    == operation) || (details::e_gte  == operation) ||
+                 (details::e_and   == operation) || (details::e_nand == operation) ||
+                 (details::e_or    == operation) || (details::e_nor  == operation) ||
+                 (details::e_xor   == operation) || (details::e_xnor == operation) ||
+                 (details::e_in    == operation) || (details::e_like == operation) ||
+                 (details::e_ilike == operation)
+               )
             {
                return node_allocator_->allocate_c<literal_node_t>(T(0));
             }
 
-            details::free_node(*node_allocator_,branch[0]);
-
             return node_allocator_->allocate<details::null_node<Type> >();
          }
 
@@ -34953,7 +36028,7 @@ namespace exprtk
 
                if (is_constant_foldable<N>(branch))
                {
-                  Type v = expression_point->value();
+                  const Type v = expression_point->value();
                   details::free_node(*node_allocator_,expression_point);
 
                   return node_allocator_->allocate<literal_node_t>(v);
@@ -35328,40 +36403,116 @@ namespace exprtk
       lexer::helper::sequence_validator         sequence_validator_;
       lexer::helper::sequence_validator_3tokens sequence_validator_3tkns_;
 
+      loop_runtime_check_ptr loop_runtime_check_;
+
       template <typename ParserType>
       friend void details::disable_type_checking(ParserType& p);
    };
 
+   namespace details
+   {
+      template <typename T>
+      struct collector_helper
+      {
+         typedef exprtk::symbol_table<T> symbol_table_t;
+         typedef exprtk::expression<T>     expression_t;
+         typedef exprtk::parser<T>             parser_t;
+         typedef typename parser_t::dependent_entity_collector::symbol_t symbol_t;
+         typedef typename parser_t::unknown_symbol_resolver usr_t;
+
+         struct resolve_as_vector : public parser_t::unknown_symbol_resolver
+         {
+            typedef exprtk::parser<T> parser_t;
+
+            resolve_as_vector()
+            : usr_t(usr_t::e_usrmode_extended)
+            {}
+
+            virtual bool process(const std::string& unknown_symbol,
+                                 symbol_table_t& symbol_table,
+                                 std::string&)
+            {
+               static T v[1];
+               symbol_table.add_vector(unknown_symbol,v);
+               return true;
+            }
+         };
+
+         static inline bool collection_pass(const std::string& expression_string,
+                                            std::set<std::string>& symbol_set,
+                                            const bool collect_variables,
+                                            const bool collect_functions,
+                                            const bool vector_pass,
+                                            symbol_table_t& ext_symbol_table)
+         {
+            symbol_table_t symbol_table;
+            expression_t   expression;
+            parser_t       parser;
+
+            resolve_as_vector vect_resolver;
+
+            expression.register_symbol_table(symbol_table    );
+            expression.register_symbol_table(ext_symbol_table);
+
+            if (vector_pass)
+               parser.enable_unknown_symbol_resolver(&vect_resolver);
+            else
+               parser.enable_unknown_symbol_resolver();
+
+            if (collect_variables)
+               parser.dec().collect_variables() = true;
+
+            if (collect_functions)
+               parser.dec().collect_functions() = true;
+
+            bool pass_result = false;
+
+            details::disable_type_checking(parser);
+
+            if (parser.compile(expression_string, expression))
+            {
+               pass_result = true;
+
+               std::deque<symbol_t> symb_list;
+               parser.dec().symbols(symb_list);
+
+               for (std::size_t i = 0; i < symb_list.size(); ++i)
+               {
+                  symbol_set.insert(symb_list[i].first);
+               }
+            }
+
+            return pass_result;
+         }
+      };
+   }
+
    template <typename Allocator,
              template <typename, typename> class Sequence>
-   inline bool collect_variables(const std::string& expr_str,
+   inline bool collect_variables(const std::string& expression,
                                  Sequence<std::string, Allocator>& symbol_list)
    {
       typedef double T;
-      typedef exprtk::symbol_table<T> symbol_table_t;
-      typedef exprtk::expression<T>     expression_t;
-      typedef exprtk::parser<T>             parser_t;
-      typedef parser_t::dependent_entity_collector::symbol_t symbol_t;
+      typedef details::collector_helper<T> collect_t;
 
-      symbol_table_t symbol_table;
-      expression_t   expression;
-      parser_t       parser;
+      collect_t::symbol_table_t null_symbol_table;
 
-      expression.register_symbol_table(symbol_table);
+      std::set<std::string> symbol_set;
 
-      parser.enable_unknown_symbol_resolver();
-      parser.dec().collect_variables() = true;
+      const bool variable_pass = collect_t::collection_pass
+                                    (expression, symbol_set, true, false, false, null_symbol_table);
+      const bool vector_pass   = collect_t::collection_pass
+                                    (expression, symbol_set, true, false,  true, null_symbol_table);
 
-      if (!parser.compile(expr_str, expression))
+      if (!variable_pass && !vector_pass)
          return false;
 
-      std::deque<symbol_t> symb_list;
+      std::set<std::string>::iterator itr = symbol_set.begin();
 
-      parser.dec().symbols(symb_list);
-
-      for (std::size_t i = 0; i < symb_list.size(); ++i)
+      while (symbol_set.end() != itr)
       {
-         symbol_list.push_back(symb_list[i].first);
+         symbol_list.push_back(*itr);
+         ++itr;
       }
 
       return true;
@@ -35370,37 +36521,28 @@ namespace exprtk
    template <typename T,
              typename Allocator,
              template <typename, typename> class Sequence>
-   inline bool collect_variables(const std::string& expr_str,
+   inline bool collect_variables(const std::string& expression,
                                  exprtk::symbol_table<T>& extrnl_symbol_table,
                                  Sequence<std::string, Allocator>& symbol_list)
    {
-      typedef exprtk::symbol_table<T> symbol_table_t;
-      typedef exprtk::expression<T>     expression_t;
-      typedef exprtk::parser<T>             parser_t;
-      typedef typename parser_t::dependent_entity_collector::symbol_t symbol_t;
-
-      symbol_table_t symbol_table;
-      expression_t   expression;
-      parser_t       parser;
-
-      expression.register_symbol_table(symbol_table);
-      expression.register_symbol_table(extrnl_symbol_table);
+      typedef details::collector_helper<T> collect_t;
 
-      parser.enable_unknown_symbol_resolver();
-      parser.dec().collect_variables() = true;
+      std::set<std::string> symbol_set;
 
-      details::disable_type_checking(parser);
+      const bool variable_pass = collect_t::collection_pass
+                                    (expression, symbol_set, true, false, false, extrnl_symbol_table);
+      const bool vector_pass   = collect_t::collection_pass
+                                    (expression, symbol_set, true, false,  true, extrnl_symbol_table);
 
-      if (!parser.compile(expr_str, expression))
+      if (!variable_pass && !vector_pass)
          return false;
 
-      std::deque<symbol_t> symb_list;
+      std::set<std::string>::iterator itr = symbol_set.begin();
 
-      parser.dec().symbols(symb_list);
-
-      for (std::size_t i = 0; i < symb_list.size(); ++i)
+      while (symbol_set.end() != itr)
       {
-         symbol_list.push_back(symb_list[i].first);
+         symbol_list.push_back(*itr);
+         ++itr;
       }
 
       return true;
@@ -35408,34 +36550,30 @@ namespace exprtk
 
    template <typename Allocator,
              template <typename, typename> class Sequence>
-   inline bool collect_functions(const std::string& expr_str,
+   inline bool collect_functions(const std::string& expression,
                                  Sequence<std::string, Allocator>& symbol_list)
    {
       typedef double T;
-      typedef exprtk::symbol_table<T> symbol_table_t;
-      typedef exprtk::expression<T>     expression_t;
-      typedef exprtk::parser<T>             parser_t;
-      typedef parser_t::dependent_entity_collector::symbol_t symbol_t;
+      typedef details::collector_helper<T> collect_t;
 
-      symbol_table_t symbol_table;
-      expression_t   expression;
-      parser_t       parser;
+      collect_t::symbol_table_t null_symbol_table;
 
-      expression.register_symbol_table(symbol_table);
+      std::set<std::string> symbol_set;
 
-      parser.enable_unknown_symbol_resolver();
-      parser.dec().collect_functions() = true;
+      const bool variable_pass = collect_t::collection_pass
+                                    (expression, symbol_set, false, true, false, null_symbol_table);
+      const bool vector_pass   = collect_t::collection_pass
+                                    (expression, symbol_set, false, true,  true, null_symbol_table);
 
-      if (!parser.compile(expr_str, expression))
+      if (!variable_pass && !vector_pass)
          return false;
 
-      std::deque<symbol_t> symb_list;
+      std::set<std::string>::iterator itr = symbol_set.begin();
 
-      parser.dec().symbols(symb_list);
-
-      for (std::size_t i = 0; i < symb_list.size(); ++i)
+      while (symbol_set.end() != itr)
       {
-         symbol_list.push_back(symb_list[i].first);
+         symbol_list.push_back(*itr);
+         ++itr;
       }
 
       return true;
@@ -35444,37 +36582,28 @@ namespace exprtk
    template <typename T,
              typename Allocator,
              template <typename, typename> class Sequence>
-   inline bool collect_functions(const std::string& expr_str,
+   inline bool collect_functions(const std::string& expression,
                                  exprtk::symbol_table<T>& extrnl_symbol_table,
                                  Sequence<std::string, Allocator>& symbol_list)
    {
-      typedef exprtk::symbol_table<T> symbol_table_t;
-      typedef exprtk::expression<T>     expression_t;
-      typedef exprtk::parser<T>             parser_t;
-      typedef typename parser_t::dependent_entity_collector::symbol_t symbol_t;
-
-      symbol_table_t symbol_table;
-      expression_t   expression;
-      parser_t       parser;
+      typedef details::collector_helper<T> collect_t;
 
-      expression.register_symbol_table(symbol_table);
-      expression.register_symbol_table(extrnl_symbol_table);
-
-      parser.enable_unknown_symbol_resolver();
-      parser.dec().collect_functions() = true;
+      std::set<std::string> symbol_set;
 
-      details::disable_type_checking(parser);
+      const bool variable_pass = collect_t::collection_pass
+                                    (expression, symbol_set, false, true, false, extrnl_symbol_table);
+      const bool vector_pass   = collect_t::collection_pass
+                                    (expression, symbol_set, false, true,  true, extrnl_symbol_table);
 
-      if (!parser.compile(expr_str, expression))
+      if (!variable_pass && !vector_pass)
          return false;
 
-      std::deque<symbol_t> symb_list;
+      std::set<std::string>::iterator itr = symbol_set.begin();
 
-      parser.dec().symbols(symb_list);
-
-      for (std::size_t i = 0; i < symb_list.size(); ++i)
+      while (symbol_set.end() != itr)
       {
-         symbol_list.push_back(symb_list[i].first);
+         symbol_list.push_back(*itr);
+         ++itr;
       }
 
       return true;
@@ -35520,8 +36649,8 @@ namespace exprtk
       if (var)
       {
          T& x = var->ref();
-         T  x_original = x;
-         T result = integrate(e,x,r0,r1,number_of_intervals);
+         const T x_original = x;
+         const T result = integrate(e, x, r0, r1, number_of_intervals);
          x = x_original;
 
          return result;
@@ -35611,8 +36740,8 @@ namespace exprtk
       if (var)
       {
          T& x = var->ref();
-         T x_original = x;
-         T result = derivative(e,x,h);
+         const T x_original = x;
+         const T result = derivative(e, x, h);
          x = x_original;
 
          return result;
@@ -35639,7 +36768,7 @@ namespace exprtk
       {
          T& x = var->ref();
          const T x_original = x;
-         const T result = second_derivative(e,x,h);
+         const T result = second_derivative(e, x, h);
          x = x_original;
 
          return result;
@@ -35666,7 +36795,7 @@ namespace exprtk
       {
          T& x = var->ref();
          const T x_original = x;
-         const T result = third_derivative(e,x,h);
+         const T result = third_derivative(e, x, h);
          x = x_original;
 
          return result;
@@ -35959,62 +37088,62 @@ namespace exprtk
 
       inline virtual T operator() (const T& x, const T& c1, const T& c0)
       {
-         poly_rtrn(1) poly_impl<T,1>::evaluate(x,c1,c0);
+         poly_rtrn(1) (poly_impl<T,1>::evaluate(x, c1, c0));
       }
 
       inline virtual T operator() (const T& x, const T& c2, const T& c1, const T& c0)
       {
-         poly_rtrn(2) poly_impl<T,2>::evaluate(x,c2,c1,c0);
+         poly_rtrn(2) (poly_impl<T,2>::evaluate(x, c2, c1, c0));
       }
 
       inline virtual T operator() (const T& x, const T& c3, const T& c2, const T& c1, const T& c0)
       {
-         poly_rtrn(3) poly_impl<T,3>::evaluate(x,c3,c2,c1,c0);
+         poly_rtrn(3) (poly_impl<T,3>::evaluate(x, c3, c2, c1, c0));
       }
 
       inline virtual T operator() (const T& x, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
       {
-         poly_rtrn(4) poly_impl<T,4>::evaluate(x,c4,c3,c2,c1,c0);
+         poly_rtrn(4) (poly_impl<T,4>::evaluate(x, c4, c3, c2, c1, c0));
       }
 
       inline virtual T operator() (const T& x, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
       {
-         poly_rtrn(5) poly_impl<T,5>::evaluate(x,c5,c4,c3,c2,c1,c0);
+         poly_rtrn(5) (poly_impl<T,5>::evaluate(x, c5, c4, c3, c2, c1, c0));
       }
 
       inline virtual T operator() (const T& x, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
       {
-         poly_rtrn(6) poly_impl<T,6>::evaluate(x,c6,c5,c4,c3,c2,c1,c0);
+         poly_rtrn(6) (poly_impl<T,6>::evaluate(x, c6, c5, c4, c3, c2, c1, c0));
       }
 
       inline virtual T operator() (const T& x, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
       {
-         poly_rtrn(7) poly_impl<T,7>::evaluate(x,c7,c6,c5,c4,c3,c2,c1,c0);
+         poly_rtrn(7) (poly_impl<T,7>::evaluate(x, c7, c6, c5, c4, c3, c2, c1, c0));
       }
 
       inline virtual T operator() (const T& x, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
       {
-         poly_rtrn(8) poly_impl<T,8>::evaluate(x,c8,c7,c6,c5,c4,c3,c2,c1,c0);
+         poly_rtrn(8) (poly_impl<T,8>::evaluate(x, c8, c7, c6, c5, c4, c3, c2, c1, c0));
       }
 
       inline virtual T operator() (const T& x, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
       {
-         poly_rtrn(9) poly_impl<T,9>::evaluate(x,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0);
+         poly_rtrn(9) (poly_impl<T,9>::evaluate(x, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0));
       }
 
       inline virtual T operator() (const T& x, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
       {
-         poly_rtrn(10) poly_impl<T,10>::evaluate(x,c10,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0);
+         poly_rtrn(10) (poly_impl<T,10>::evaluate(x, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0));
       }
 
       inline virtual T operator() (const T& x, const T& c11, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
       {
-         poly_rtrn(11) poly_impl<T,11>::evaluate(x,c11,c10,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0);
+         poly_rtrn(11) (poly_impl<T,11>::evaluate(x, c11, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0));
       }
 
       inline virtual T operator() (const T& x, const T& c12, const T& c11, const T& c10, const T& c9, const T& c8, const T& c7, const T& c6, const T& c5, const T& c4, const T& c3, const T& c2, const T& c1, const T& c0)
       {
-         poly_rtrn(12) poly_impl<T,12>::evaluate(x,c12,c11,c10,c9,c8,c7,c6,c5,c4,c3,c2,c1,c0);
+         poly_rtrn(12) (poly_impl<T,12>::evaluate(x, c12, c11, c10, c9, c8, c7, c6, c5, c4, c3, c2, c1, c0));
       }
 
       #undef poly_rtrn
@@ -36202,7 +37331,7 @@ namespace exprtk
 
             typedef typename expression_t::control_block::local_data_list_t ldl_t;
 
-            ldl_t ldl = expr.local_data_list();
+            const ldl_t ldl = expr.local_data_list();
 
             std::vector<std::size_t> index_list;
 
@@ -36371,8 +37500,16 @@ namespace exprtk
       template <typename BaseFuncType>
       struct scoped_bft
       {
-         scoped_bft(BaseFuncType& bft) : bft_(bft) { bft_.pre (); }
-        ~scoped_bft()                              { bft_.post(); }
+         explicit scoped_bft(BaseFuncType& bft)
+         : bft_(bft)
+         {
+            bft_.pre ();
+         }
+
+        ~scoped_bft()
+         {
+            bft_.post();
+         }
 
          BaseFuncType& bft_;
 
@@ -36472,7 +37609,7 @@ namespace exprtk
          typedef typename results_context_t::type_store_t type_t;
          typedef typename type_t::scalar_view scalar_t;
 
-         T result = e.value();
+         const T result = e.value();
 
          if (e.return_invoked())
          {
@@ -36566,6 +37703,11 @@ namespace exprtk
          return symbol_table_;
       }
 
+      inline const symbol_table_t& symbol_table() const
+      {
+         return symbol_table_;
+      }
+
       inline void add_auxiliary_symtab(symbol_table_t& symtab)
       {
          auxiliary_symtab_list_.push_back(&symtab);
@@ -36781,89 +37923,90 @@ namespace exprtk
    template <typename T>
    inline bool pgo_primer()
    {
-      static const std::string expression_list[]
-                                        = {
-                                             "(y + x)",
-                                             "2 * (y + x)",
-                                             "(2 * y + 2 * x)",
-                                             "(y + x / y) * (x - y / x)",
-                                             "x / ((x + y) * (x - y)) / y",
-                                             "1 - ((x * y) + (y / x)) - 3",
-                                             "sin(2 * x) + cos(pi / y)",
-                                             "1 - sin(2 * x) + cos(pi / y)",
-                                             "sqrt(1 - sin(2 * x) + cos(pi / y) / 3)",
-                                             "(x^2 / sin(2 * pi / y)) -x / 2",
-                                             "x + (cos(y - sin(2 / x * pi)) - sin(x - cos(2 * y / pi))) - y",
-                                             "clamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0)",
-                                             "iclamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0)",
-                                             "max(3.33, min(sqrt(1 - sin(2 * x) + cos(pi / y) / 3), 1.11))",
-                                             "if(avg(x,y) <= x + y, x - y, x * y) + 2 * pi / x",
-                                             "1.1x^1 + 2.2y^2 - 3.3x^3 + 4.4y^4 - 5.5x^5 + 6.6y^6 - 7.7x^27 + 8.8y^55",
-                                             "(yy + xx)",
-                                             "2 * (yy + xx)",
-                                             "(2 * yy + 2 * xx)",
-                                             "(yy + xx / yy) * (xx - yy / xx)",
-                                             "xx / ((xx + yy) * (xx - yy)) / yy",
-                                             "1 - ((xx * yy) + (yy / xx)) - 3",
-                                             "sin(2 * xx) + cos(pi / yy)",
-                                             "1 - sin(2 * xx) + cos(pi / yy)",
-                                             "sqrt(1 - sin(2 * xx) + cos(pi / yy) / 3)",
-                                             "(xx^2 / sin(2 * pi / yy)) -xx / 2",
-                                             "xx + (cos(yy - sin(2 / xx * pi)) - sin(xx - cos(2 * yy / pi))) - yy",
-                                             "clamp(-1.0, sin(2 * pi * xx) + cos(yy / 2 * pi), +1.0)",
-                                             "max(3.33, min(sqrt(1 - sin(2 * xx) + cos(pi / yy) / 3), 1.11))",
-                                             "if(avg(xx,yy) <= xx + yy, xx - yy, xx * yy) + 2 * pi / xx",
-                                             "1.1xx^1 + 2.2yy^2 - 3.3xx^3 + 4.4yy^4 - 5.5xx^5 + 6.6yy^6 - 7.7xx^27 + 8.8yy^55",
-                                             "(1.1*(2.2*(3.3*(4.4*(5.5*(6.6*(7.7*(8.8*(9.9+x)))))))))",
-                                             "(((((((((x+9.9)*8.8)*7.7)*6.6)*5.5)*4.4)*3.3)*2.2)*1.1)",
-                                             "(x + y) * z", "x + (y * z)", "(x + y) * 7", "x + (y * 7)",
-                                             "(x + 7) * y", "x + (7 * y)", "(7 + x) * y", "7 + (x * y)",
-                                             "(2 + x) * 3", "2 + (x * 3)", "(2 + 3) * x", "2 + (3 * x)",
-                                             "(x + 2) * 3", "x + (2 * 3)",
-                                             "(x + y) * (z / w)", "(x + y) * (z / 7)", "(x + y) * (7 / z)", "(x + 7) * (y / z)",
-                                             "(7 + x) * (y / z)", "(2 + x) * (y / z)", "(x + 2) * (y / 3)", "(2 + x) * (y / 3)",
-                                             "(x + 2) * (3 / y)", "x + (y * (z / w))", "x + (y * (z / 7))", "x + (y * (7 / z))",
-                                             "x + (7 * (y / z))", "7 + (x * (y / z))", "2 + (x * (3 / y))", "x + (2 * (y / 4))",
-                                             "2 + (x * (y / 3))", "x + (2 * (3 / y))",
-                                             "x + ((y * z) / w)", "x + ((y * z) / 7)", "x + ((y * 7) / z)", "x + ((7 * y) / z)",
-                                             "7 + ((y * z) / w)", "2 + ((x * 3) / y)", "x + ((2 * y) / 3)", "2 + ((x * y) / 3)",
-                                             "x + ((2 * 3) / y)", "(((x + y) * z) / w)",
-                                             "(((x + y) * z) / 7)", "(((x + y) * 7) / z)", "(((x + 7) * y) / z)", "(((7 + x) * y) / z)",
-                                             "(((2 + x) * 3) / y)", "(((x + 2) * y) / 3)", "(((2 + x) * y) / 3)", "(((x + 2) * 3) / y)",
-                                             "((x + (y * z)) / w)", "((x + (y * z)) / 7)", "((x + (y * 7)) / y)", "((x + (7 * y)) / z)",
-                                             "((7 + (x * y)) / z)", "((2 + (x * 3)) / y)", "((x + (2 * y)) / 3)", "((2 + (x * y)) / 3)",
-                                             "((x + (2 * 3)) / y)",
-                                             "(xx + yy) * zz", "xx + (yy * zz)",
-                                             "(xx + yy) * 7", "xx + (yy * 7)",
-                                             "(xx + 7) * yy", "xx + (7 * yy)",
-                                             "(7 + xx) * yy", "7 + (xx * yy)",
-                                             "(2 + x) * 3", "2 + (x * 3)",
-                                             "(2 + 3) * x", "2 + (3 * x)",
-                                             "(x + 2) * 3", "x + (2 * 3)",
-                                             "(xx + yy) * (zz / ww)", "(xx + yy) * (zz / 7)",
-                                             "(xx + yy) * (7 / zz)", "(xx + 7) * (yy / zz)",
-                                             "(7 + xx) * (yy / zz)", "(2 + xx) * (yy / zz)",
-                                             "(xx + 2) * (yy / 3)", "(2 + xx) * (yy / 3)",
-                                             "(xx + 2) * (3 / yy)", "xx + (yy * (zz / ww))",
-                                             "xx + (yy * (zz / 7))", "xx + (yy * (7 / zz))",
-                                             "xx + (7 * (yy / zz))", "7 + (xx * (yy / zz))",
-                                             "2 + (xx * (3 / yy))", "xx + (2 * (yy / 4))",
-                                             "2 + (xx * (yy / 3))", "xx + (2 * (3 / yy))",
-                                             "xx + ((yy * zz) / ww)", "xx + ((yy * zz) / 7)",
-                                             "xx + ((yy * 7) / zz)", "xx + ((7 * yy) / zz)",
-                                             "7 + ((yy * zz) / ww)", "2 + ((xx * 3) / yy)",
-                                             "xx + ((2 * yy) / 3)", "2 + ((xx * yy) / 3)",
-                                             "xx + ((2 * 3) / yy)", "(((xx + yy) * zz) / ww)",
-                                             "(((xx + yy) * zz) / 7)", "(((xx + yy) * 7) / zz)",
-                                             "(((xx + 7) * yy) / zz)", "(((7 + xx) * yy) / zz)",
-                                             "(((2 + xx) * 3) / yy)", "(((xx + 2) * yy) / 3)",
-                                             "(((2 + xx) * yy) / 3)", "(((xx + 2) * 3) / yy)",
-                                             "((xx + (yy * zz)) / ww)", "((xx + (yy * zz)) / 7)",
-                                             "((xx + (yy * 7)) / yy)", "((xx + (7 * yy)) / zz)",
-                                             "((7 + (xx * yy)) / zz)", "((2 + (xx * 3)) / yy)",
-                                             "((xx + (2 * yy)) / 3)", "((2 + (xx * yy)) / 3)",
-                                             "((xx + (2 * 3)) / yy)"
-                                          };
+      static const std::string expression_list[] =
+             {
+                "(y + x)",
+                "2 * (y + x)",
+                "(2 * y + 2 * x)",
+                "(y + x / y) * (x - y / x)",
+                "x / ((x + y) * (x - y)) / y",
+                "1 - ((x * y) + (y / x)) - 3",
+                "sin(2 * x) + cos(pi / y)",
+                "1 - sin(2 * x) + cos(pi / y)",
+                "sqrt(1 - sin(2 * x) + cos(pi / y) / 3)",
+                "(x^2 / sin(2 * pi / y)) -x / 2",
+                "x + (cos(y - sin(2 / x * pi)) - sin(x - cos(2 * y / pi))) - y",
+                "clamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0)",
+                "iclamp(-1.0, sin(2 * pi * x) + cos(y / 2 * pi), +1.0)",
+                "max(3.33, min(sqrt(1 - sin(2 * x) + cos(pi / y) / 3), 1.11))",
+                "if(avg(x,y) <= x + y, x - y, x * y) + 2 * pi / x",
+                "1.1x^1 + 2.2y^2 - 3.3x^3 + 4.4y^4 - 5.5x^5 + 6.6y^6 - 7.7x^27 + 8.8y^55",
+                "(yy + xx)",
+                "2 * (yy + xx)",
+                "(2 * yy + 2 * xx)",
+                "(yy + xx / yy) * (xx - yy / xx)",
+                "xx / ((xx + yy) * (xx - yy)) / yy",
+                "1 - ((xx * yy) + (yy / xx)) - 3",
+                "sin(2 * xx) + cos(pi / yy)",
+                "1 - sin(2 * xx) + cos(pi / yy)",
+                "sqrt(1 - sin(2 * xx) + cos(pi / yy) / 3)",
+                "(xx^2 / sin(2 * pi / yy)) -xx / 2",
+                "xx + (cos(yy - sin(2 / xx * pi)) - sin(xx - cos(2 * yy / pi))) - yy",
+                "clamp(-1.0, sin(2 * pi * xx) + cos(yy / 2 * pi), +1.0)",
+                "max(3.33, min(sqrt(1 - sin(2 * xx) + cos(pi / yy) / 3), 1.11))",
+                "if(avg(xx,yy) <= xx + yy, xx - yy, xx * yy) + 2 * pi / xx",
+                "1.1xx^1 + 2.2yy^2 - 3.3xx^3 + 4.4yy^4 - 5.5xx^5 + 6.6yy^6 - 7.7xx^27 + 8.8yy^55",
+                "(1.1*(2.2*(3.3*(4.4*(5.5*(6.6*(7.7*(8.8*(9.9+x)))))))))",
+                "(((((((((x+9.9)*8.8)*7.7)*6.6)*5.5)*4.4)*3.3)*2.2)*1.1)",
+                "(x + y) * z", "x + (y * z)", "(x + y) * 7", "x + (y * 7)",
+                "(x + 7) * y", "x + (7 * y)", "(7 + x) * y", "7 + (x * y)",
+                "(2 + x) * 3", "2 + (x * 3)", "(2 + 3) * x", "2 + (3 * x)",
+                "(x + 2) * 3", "x + (2 * 3)",
+                "(x + y) * (z / w)", "(x + y) * (z / 7)", "(x + y) * (7 / z)", "(x + 7) * (y / z)",
+                "(7 + x) * (y / z)", "(2 + x) * (y / z)", "(x + 2) * (y / 3)", "(2 + x) * (y / 3)",
+                "(x + 2) * (3 / y)", "x + (y * (z / w))", "x + (y * (z / 7))", "x + (y * (7 / z))",
+                "x + (7 * (y / z))", "7 + (x * (y / z))", "2 + (x * (3 / y))", "x + (2 * (y / 4))",
+                "2 + (x * (y / 3))", "x + (2 * (3 / y))",
+                "x + ((y * z) / w)", "x + ((y * z) / 7)", "x + ((y * 7) / z)", "x + ((7 * y) / z)",
+                "7 + ((y * z) / w)", "2 + ((x * 3) / y)", "x + ((2 * y) / 3)", "2 + ((x * y) / 3)",
+                "x + ((2 * 3) / y)", "(((x + y) * z) / w)",
+                "(((x + y) * z) / 7)", "(((x + y) * 7) / z)", "(((x + 7) * y) / z)", "(((7 + x) * y) / z)",
+                "(((2 + x) * 3) / y)", "(((x + 2) * y) / 3)", "(((2 + x) * y) / 3)", "(((x + 2) * 3) / y)",
+                "((x + (y * z)) / w)", "((x + (y * z)) / 7)", "((x + (y * 7)) / y)", "((x + (7 * y)) / z)",
+                "((7 + (x * y)) / z)", "((2 + (x * 3)) / y)", "((x + (2 * y)) / 3)", "((2 + (x * y)) / 3)",
+                "((x + (2 * 3)) / y)",
+                "(xx + yy) * zz", "xx + (yy * zz)",
+                "(xx + yy) * 7", "xx + (yy * 7)",
+                "(xx + 7) * yy", "xx + (7 * yy)",
+                "(7 + xx) * yy", "7 + (xx * yy)",
+                "(2 + x) * 3", "2 + (x * 3)",
+                "(2 + 3) * x", "2 + (3 * x)",
+                "(x + 2) * 3", "x + (2 * 3)",
+                "(xx + yy) * (zz / ww)", "(xx + yy) * (zz / 7)",
+                "(xx + yy) * (7 / zz)", "(xx + 7) * (yy / zz)",
+                "(7 + xx) * (yy / zz)", "(2 + xx) * (yy / zz)",
+                "(xx + 2) * (yy / 3)", "(2 + xx) * (yy / 3)",
+                "(xx + 2) * (3 / yy)", "xx + (yy * (zz / ww))",
+                "xx + (yy * (zz / 7))", "xx + (yy * (7 / zz))",
+                "xx + (7 * (yy / zz))", "7 + (xx * (yy / zz))",
+                "2 + (xx * (3 / yy))", "xx + (2 * (yy / 4))",
+                "2 + (xx * (yy / 3))", "xx + (2 * (3 / yy))",
+                "xx + ((yy * zz) / ww)", "xx + ((yy * zz) / 7)",
+                "xx + ((yy * 7) / zz)", "xx + ((7 * yy) / zz)",
+                "7 + ((yy * zz) / ww)", "2 + ((xx * 3) / yy)",
+                "xx + ((2 * yy) / 3)", "2 + ((xx * yy) / 3)",
+                "xx + ((2 * 3) / yy)", "(((xx + yy) * zz) / ww)",
+                "(((xx + yy) * zz) / 7)", "(((xx + yy) * 7) / zz)",
+                "(((xx + 7) * yy) / zz)", "(((7 + xx) * yy) / zz)",
+                "(((2 + xx) * 3) / yy)", "(((xx + 2) * yy) / 3)",
+                "(((2 + xx) * yy) / 3)", "(((xx + 2) * 3) / yy)",
+                "((xx + (yy * zz)) / ww)", "((xx + (yy * zz)) / 7)",
+                "((xx + (yy * 7)) / yy)", "((xx + (7 * yy)) / zz)",
+                "((7 + (xx * yy)) / zz)", "((2 + (xx * 3)) / yy)",
+                "((xx + (2 * yy)) / 3)", "((2 + (xx * yy)) / 3)",
+                "((xx + (2 * 3)) / yy)"
+             };
+
       static const std::size_t expression_list_size = sizeof(expression_list) / sizeof(std::string);
 
       T  x = T(0);
@@ -37048,14 +38191,14 @@ namespace exprtk
          {
             if (stop_time_.tv_sec >= start_time_.tv_sec)
             {
-               return 1000000LLU * static_cast<unsigned long long int>(stop_time_.tv_sec  - start_time_.tv_sec ) +
-                                   static_cast<unsigned long long int>(stop_time_.tv_usec - start_time_.tv_usec) ;
+               return 1000000LLU * static_cast<details::_uint64_t>(stop_time_.tv_sec  - start_time_.tv_sec ) +
+                                   static_cast<details::_uint64_t>(stop_time_.tv_usec - start_time_.tv_usec) ;
             }
             else
-               return std::numeric_limits<unsigned long long int>::max();
+               return std::numeric_limits<details::_uint64_t>::max();
          }
          else
-            return std::numeric_limits<unsigned long long int>::max();
+            return std::numeric_limits<details::_uint64_t>::max();
       }
 
       inline double time() const
@@ -38107,10 +39250,13 @@ namespace exprtk
             )
             return T(0);
 
-         std::size_t dist  = r1 - r0 + 1;
-         std::size_t shift = n % dist;
+         const std::size_t dist  = r1 - r0 + 1;
+         const std::size_t shift = n % dist;
 
-         std::rotate(vec.begin() + r0, vec.begin() + r0 + shift, vec.begin() + r1 + 1);
+         std::rotate(
+            vec.begin() + r0,
+            vec.begin() + r0 + shift,
+            vec.begin() + r1 + 1);
 
          return T(1);
       }
@@ -38158,7 +39304,10 @@ namespace exprtk
          std::size_t dist  = r1 - r0 + 1;
          std::size_t shift = (dist - (n % dist)) % dist;
 
-         std::rotate(vec.begin() + r0, vec.begin() + r0 + shift, vec.begin() + r1 + 1);
+         std::rotate(
+            vec.begin() + r0,
+            vec.begin() + r0 + shift,
+            vec.begin() + r1 + 1);
 
          return T(1);
       }
@@ -38203,12 +39352,15 @@ namespace exprtk
             )
             return T(0);
 
-         std::size_t dist  = r1 - r0 + 1;
+         const std::size_t dist  = r1 - r0 + 1;
 
          if (n > dist)
             return T(0);
 
-         std::rotate(vec.begin() + r0, vec.begin() + r0 + n, vec.begin() + r1 + 1);
+         std::rotate(
+            vec.begin() + r0,
+            vec.begin() + r0 + n,
+            vec.begin() + r1 + 1);
 
          for (std::size_t i = r1 - n + 1; i <= r1; ++i)
          {
@@ -38258,14 +39410,17 @@ namespace exprtk
             )
             return T(0);
 
-         std::size_t dist  = r1 - r0 + 1;
+         const std::size_t dist  = r1 - r0 + 1;
 
          if (n > dist)
             return T(0);
 
-         std::size_t shift = (dist - (n % dist)) % dist;
+         const std::size_t shift = (dist - (n % dist)) % dist;
 
-         std::rotate(vec.begin() + r0, vec.begin() + r0 + shift, vec.begin() + r1 + 1);
+         std::rotate(
+            vec.begin() + r0,
+            vec.begin() + r0 + shift,
+            vec.begin() + r1 + 1);
 
          for (std::size_t i = r0; i < r0 + n; ++i)
          {
@@ -38882,9 +40037,11 @@ namespace exprtk
    namespace information
    {
       static const char* library = "Mathematical Expression Toolkit";
-      static const char* version = "2.71828182845904523536028747135266249775724709369995957"
-                                   "4966967627724076630353547594571382178525166427427466391";
-      static const char* date    = "20190818";
+      static const char* version = "2.718281828459045235360287471352"
+                                   "66249775724709369995957496696762"
+                                   "77240766303535475945713821785251"
+                                   "66427427466391932003059921817413";
+      static const char* date    = "20210101";
 
       static inline std::string data()
       {
diff --git a/src/core/mpi/BufferDataTypeExtensions.h b/src/core/mpi/BufferDataTypeExtensions.h
index 9685d6ac2d7a14b7bd68e162e81c27f7254e02b7..0af75724692a1f9434d97f72eded6afed1e6d24e 100644
--- a/src/core/mpi/BufferDataTypeExtensions.h
+++ b/src/core/mpi/BufferDataTypeExtensions.h
@@ -28,7 +28,9 @@
 #include "core/Conversion.h"
 #include "core/DataTypes.h"
 #include "core/math/Uint.h"
+#if __cplusplus >= 201703L || defined(_MSC_VER)
 #include "core/Optional.h"
+#endif
 #include "core/RandomUUID.h"
 
 #include <array>
@@ -141,13 +143,6 @@ void sendContainer( GenericSendBuffer<T,G> & buf, const Cont & container )
 }
 
 
-#ifdef WALBERLA_CXX_COMPILER_IS_GNU
-#if __GNUC__ == 4 && __GNUC_MINOR__ == 9 || __GNUC__ == 6
-#pragma GCC push_options
-#pragma GCC optimize(2)
-#endif
-#endif
-
 template< typename T,    // Element type of RecvBuffer
           typename Cont> // Container
 void recvContainer( GenericRecvBuffer<T> & buf, Cont & container )
@@ -160,12 +155,6 @@ void recvContainer( GenericRecvBuffer<T> & buf, Cont & container )
       buf >> *it;
 }
 
-#ifdef WALBERLA_CXX_COMPILER_IS_GNU
-#if __GNUC__ == 4 && __GNUC_MINOR__ == 9 || __GNUC__ == 6
-#pragma GCC pop_options
-#endif
-#endif
-
 
 
 template< typename T,    // Element type of SendBuffer
@@ -277,7 +266,7 @@ template< typename T,    // Element type of SendBuffer
 GenericSendBuffer<T,G>& packBoolVectorWithoutSize(GenericSendBuffer<T,G> & buf, const std::vector<bool> & bools )
 {
    // Use an unsigned type at least as large as the SendBuffer base type as container for the bools
-   typedef typename math::leastUnsignedInteger< std::numeric_limits<T>::digits >::type ContainerType;
+   using ContainerType = typename math::leastUnsignedInteger<std::numeric_limits<T>::digits>::type;
    static const size_t NUM_BITS = std::numeric_limits<ContainerType>::digits;
 
    auto it = bools.begin();
@@ -298,7 +287,7 @@ template< typename T >    // Element type  of RecvBuffer
 GenericRecvBuffer<T>& unpackBoolVectorWithoutSize(GenericRecvBuffer<T> & buf, std::vector<bool> & bools, size_t size )
 {
    // Use an unsigned type at least as large as the RecvBuffer base type as container for the bools
-   typedef typename math::leastUnsignedInteger<std::numeric_limits<T>::digits>::type ContainerType;
+   using ContainerType = typename math::leastUnsignedInteger<std::numeric_limits<T>::digits>::type;
    static const size_t NUM_BITS = std::numeric_limits<ContainerType>::digits;
 
    bools.resize(size);
@@ -564,6 +553,7 @@ template<typename T, typename K, typename C, typename A>
 struct BufferSizeTrait< std::multimap<K,T,C,A> > { static const bool constantSize = false;  };
 
 
+#if __cplusplus >= 201703L || defined(_MSC_VER)
 // ---------------------------------------------------------------------------------------------------------------------
 // ------------------------------------------- optional Support --------------------------------------------------------
 // ---------------------------------------------------------------------------------------------------------------------
@@ -614,6 +604,7 @@ GenericRecvBuffer<T>& operator>>( GenericRecvBuffer<T> & buf, walberla::optional
 
    return buf;
 }
+#endif
 
 // ---------------------------------------------------------------------------------------------------------------------
 // --------------------------------------- RandomUUID Support ----------------------------------------------------------
diff --git a/src/core/mpi/BufferSystem.h b/src/core/mpi/BufferSystem.h
index 8be65ea4a355644a39381eb29944375010fbc4f5..f2b0cbf431741cf020eaadea1f0268e27c8b3429 100644
--- a/src/core/mpi/BufferSystem.h
+++ b/src/core/mpi/BufferSystem.h
@@ -118,7 +118,7 @@ public:
    explicit GenericBufferSystem( const MPI_Comm & communicator, int tag = 0 );
    GenericBufferSystem( const GenericBufferSystem & other );
    GenericBufferSystem & operator=( const GenericBufferSystem & other );
-   ~GenericBufferSystem() {}
+   ~GenericBufferSystem() = default;
    //@}
    //*******************************************************************************************************************
 
@@ -211,7 +211,7 @@ public:
    //* Rank Ranges     *************************************************************************************************
    /*! \name Rank Ranges  */
    //@{
-   typedef std::set<MPIRank> RankRange;
+   using RankRange = std::set<MPIRank>;
    static RankRange noRanks();
    static RankRange allRanks();
    static RankRange allRanksButRoot();
@@ -264,7 +264,7 @@ protected:
    int64_t numberOfReceives_ = 0; ///< number of communication partners during last receive
 };
 
-typedef GenericBufferSystem<RecvBuffer, SendBuffer> BufferSystem;
+using BufferSystem = GenericBufferSystem<RecvBuffer, SendBuffer>;
 
 } // namespace mpi
 } // namespace walberla
diff --git a/src/core/mpi/BufferSystemHelper.h b/src/core/mpi/BufferSystemHelper.h
index 661e463587263ec62d8aadbc1ba93d15660ca2ed..78b77c5ed6b3aec418c4057671f683f6c816c8e1 100644
--- a/src/core/mpi/BufferSystemHelper.h
+++ b/src/core/mpi/BufferSystemHelper.h
@@ -43,7 +43,7 @@ namespace internal {
          : communicator_( communicator), tag_(tag)
       {}
 
-      virtual ~AbstractCommunication() {}
+      virtual ~AbstractCommunication() = default;
 
 
       struct ReceiveInfo {
@@ -110,15 +110,15 @@ namespace internal {
       KnownSizeCommunication( const MPI_Comm & communicator, int tag = 0 )
            : AbstractCommunication<RecvBuffer_T, SendBuffer_T>( communicator, tag ), sending_(false), receiving_(false) {}
 
-      virtual ~KnownSizeCommunication() {}
+      ~KnownSizeCommunication() override = default;
 
-      virtual void send( MPIRank receiver, const SendBuffer_T & sendBuffer );
-      virtual void waitForSends();
+      void send( MPIRank receiver, const SendBuffer_T & sendBuffer ) override;
+      void waitForSends() override;
 
-      virtual void    scheduleReceives  ( std::map<MPIRank, ReceiveInfo> & recvInfos );
+      void    scheduleReceives  ( std::map<MPIRank, ReceiveInfo> & recvInfos ) override;
 
       /// size field of recvInfos is expected to be valid
-      virtual MPIRank waitForNextReceive( std::map<MPIRank, ReceiveInfo> & recvInfos );
+      MPIRank waitForNextReceive( std::map<MPIRank, ReceiveInfo> & recvInfos ) override;
 
    private:
       bool sending_;
@@ -138,15 +138,15 @@ namespace internal {
       UnknownSizeCommunication( const MPI_Comm & communicator, int tag = 0 )
            :  AbstractCommunication<RecvBuffer_T, SendBuffer_T>( communicator, tag ), sending_(false), receiving_(false) {}
 
-      virtual ~UnknownSizeCommunication() {}
+      ~UnknownSizeCommunication() override = default;
 
-      virtual void send( MPIRank receiver, const SendBuffer_T & sendBuffer );
-      virtual void waitForSends();
+      void send( MPIRank receiver, const SendBuffer_T & sendBuffer ) override;
+      void waitForSends() override;
 
-      virtual void scheduleReceives( std::map<MPIRank, ReceiveInfo> & recvInfos );
+      void scheduleReceives( std::map<MPIRank, ReceiveInfo> & recvInfos ) override;
 
       /// size field of recvInfos can be invalid, is filled in with the actual message size
-      virtual MPIRank waitForNextReceive( std::map<MPIRank, ReceiveInfo> & recvInfos );
+      MPIRank waitForNextReceive( std::map<MPIRank, ReceiveInfo> & recvInfos ) override;
 
    private:
       bool sending_;
@@ -170,15 +170,15 @@ namespace internal {
       UnknownSizeCommunicationIProbe( const MPI_Comm & communicator, int tag = 0 )
            :  AbstractCommunication<RecvBuffer_T, SendBuffer_T>( communicator, tag ), sending_(false), receiving_(false) {}
 
-      virtual ~UnknownSizeCommunicationIProbe() {}
+      ~UnknownSizeCommunicationIProbe() override = default;
 
-      virtual void send( MPIRank receiver, const SendBuffer_T & sendBuffer );
-      virtual void waitForSends();
+      void send( MPIRank receiver, const SendBuffer_T & sendBuffer ) override;
+      void waitForSends() override;
 
-      virtual void scheduleReceives( std::map<MPIRank, ReceiveInfo> & recvInfos );
+      void scheduleReceives( std::map<MPIRank, ReceiveInfo> & recvInfos ) override;
 
       /// size field of recvInfos can be invalid, is filled in with the actual message size
-      virtual MPIRank waitForNextReceive( std::map<MPIRank, ReceiveInfo> & recvInfos );
+      MPIRank waitForNextReceive( std::map<MPIRank, ReceiveInfo> & recvInfos ) override;
 
    private:
       bool sending_;
@@ -198,15 +198,15 @@ namespace internal {
       NoMPICommunication( const MPI_Comm & communicator, int tag = 0 )
          : AbstractCommunication<RecvBuffer_T, SendBuffer_T>( communicator, tag ), received_( false ) {}
 
-      virtual ~NoMPICommunication() {}
+      ~NoMPICommunication() override = default;
 
-      virtual void send( MPIRank receiver, const SendBuffer_T & sendBuffer );
-      virtual void waitForSends();
+      void send( MPIRank receiver, const SendBuffer_T & sendBuffer ) override;
+      void waitForSends() override;
 
-      virtual void scheduleReceives( std::map<MPIRank, ReceiveInfo> & recvInfos );
+      void scheduleReceives( std::map<MPIRank, ReceiveInfo> & recvInfos ) override;
 
       /// size field of recvInfos can be invalid, is filled in with the actual message size
-      virtual MPIRank waitForNextReceive( std::map<MPIRank, ReceiveInfo> & recvInfos );
+      MPIRank waitForNextReceive( std::map<MPIRank, ReceiveInfo> & recvInfos ) override;
 
    private:
       bool         received_;
diff --git a/src/core/mpi/Gather.h b/src/core/mpi/Gather.h
index 5cad161899035d4575dfc1c32b662fd4624c863d..976981f9fa0e8fe9374c4809e77fb656a23257cc 100644
--- a/src/core/mpi/Gather.h
+++ b/src/core/mpi/Gather.h
@@ -78,7 +78,7 @@ std::vector<T> gather( T value, int recvRank = 0, MPI_Comm comm = MPI_COMM_WORLD
       return result;
    }
 
-   MPI_Gather( &value, 1, MPITrait<T>::type(), NULL, 1, MPITrait<T>::type(),
+   MPI_Gather( &value, 1, MPITrait<T>::type(), nullptr, 1, MPITrait<T>::type(),
                recvRank, comm );
 
    return std::vector<T>();
diff --git a/src/core/mpi/Gatherv.h b/src/core/mpi/Gatherv.h
index 7ffda280a2b645e420608b123d3bd5a3880b604e..7a9de2652e45d552f59f5834c2f0b75fdea77232 100644
--- a/src/core/mpi/Gatherv.h
+++ b/src/core/mpi/Gatherv.h
@@ -90,9 +90,9 @@ std::vector<T> gatherv( const std::vector<T> & values, int recvRank = 0, MPI_Com
       if( values.empty() )
       {
          if( !result.empty() )
-            MPI_Gatherv( NULL, 0, MPITrait<T>::type(), &(result[0]), &(recvCounts[0]), &(displacements[0]), MPITrait<T>::type(), recvRank, comm );
+            MPI_Gatherv( nullptr, 0, MPITrait<T>::type(), &(result[0]), &(recvCounts[0]), &(displacements[0]), MPITrait<T>::type(), recvRank, comm );
          else
-            MPI_Gatherv( NULL, 0, MPITrait<T>::type(), NULL, &(recvCounts[0]), &(displacements[0]), MPITrait<T>::type(), recvRank, comm );
+            MPI_Gatherv( nullptr, 0, MPITrait<T>::type(), nullptr, &(recvCounts[0]), &(displacements[0]), MPITrait<T>::type(), recvRank, comm );
       }
       else
       {
@@ -109,11 +109,11 @@ std::vector<T> gatherv( const std::vector<T> & values, int recvRank = 0, MPI_Com
 
    if( values.empty() )
    {
-      MPI_Gatherv( NULL, 0, MPITrait<T>::type(), NULL, NULL, NULL, MPITrait<T>::type(), recvRank, comm );
+      MPI_Gatherv( nullptr, 0, MPITrait<T>::type(), nullptr, nullptr, nullptr, MPITrait<T>::type(), recvRank, comm );
    }
    else
    {
-      MPI_Gatherv( const_cast<T*>( &(values[0]) ), int_c( values.size() ), MPITrait<T>::type(), NULL, NULL, NULL, MPITrait<T>::type(),
+      MPI_Gatherv( const_cast<T*>( &(values[0]) ), int_c( values.size() ), MPITrait<T>::type(), nullptr, nullptr, nullptr, MPITrait<T>::type(),
                    recvRank, comm );
    }
 
@@ -171,7 +171,7 @@ std::vector<T> allGatherv( const std::vector<T> & values, MPI_Comm comm = MPI_CO
    {
       if( values.empty() )
       {
-         MPI_Allgatherv( NULL, 0, MPITrait<T>::type(), &(result[0]), &(recvCounts[0]), &(displacements[0]),
+         MPI_Allgatherv( nullptr, 0, MPITrait<T>::type(), &(result[0]), &(recvCounts[0]), &(displacements[0]),
                          MPITrait<T>::type(), comm );
       }
       else
diff --git a/src/core/mpi/MPIWrapper.h b/src/core/mpi/MPIWrapper.h
index 3cdc7d54626747d8a6d9010a2211e4ccfbbeb54d..cd250cb97bdf438e48e79c4010d75942e16ba010 100644
--- a/src/core/mpi/MPIWrapper.h
+++ b/src/core/mpi/MPIWrapper.h
@@ -61,7 +61,14 @@ namespace mpistubs {
 #pragma warning ( push, 1 )
 #endif
 
+#if ( defined WALBERLA_CXX_COMPILER_IS_GNU ) || ( defined WALBERLA_CXX_COMPILER_IS_CLANG )
+#   pragma GCC diagnostic push
+#   pragma GCC diagnostic ignored "-Wsign-conversion"
+#endif
 #include <mpi.h>
+#if ( defined WALBERLA_CXX_COMPILER_IS_GNU ) || ( defined WALBERLA_CXX_COMPILER_IS_CLANG )
+#   pragma GCC diagnostic pop
+#endif
 
 #ifdef _MSC_VER
 #pragma warning ( pop )
@@ -82,16 +89,16 @@ namespace walberla {
 namespace mpistubs {
 
 
-typedef int MPI_Comm;
-typedef int MPI_Datatype;
-typedef int MPI_Group;
-typedef int MPI_Op;
-typedef int MPI_Request;
-typedef int MPI_File;
-typedef int MPI_Offset;
-typedef int MPI_Info;
-typedef int MPI_Aint;
-typedef void (MPI_User_function) (void* a, void* b, int* len, MPI_Datatype*);
+using MPI_Comm = int;
+using MPI_Datatype = int;
+using MPI_Group = int;
+using MPI_Op = int;
+using MPI_Request = int;
+using MPI_File = int;
+using MPI_Offset = int;
+using MPI_Info = int;
+using MPI_Aint = int;
+using MPI_User_function = void (void *, void *, int *, MPI_Datatype *);
 
 struct MPI_Status
 {
@@ -170,10 +177,10 @@ const int MPI_REQUEST_NULL = 0;
 
 const int MPI_FILE_NULL = 0;
 
-static MPI_Status* MPI_STATUS_IGNORE   = NULL;
-static MPI_Status* MPI_STATUSES_IGNORE = NULL;
+static MPI_Status* MPI_STATUS_IGNORE   = nullptr;
+static MPI_Status* MPI_STATUSES_IGNORE = nullptr;
 
-namespace non_mpi_internal { inline void disableDefinedButNotUsedWarning() { MPI_STATUS_IGNORE = NULL; MPI_STATUSES_IGNORE = NULL; } }
+namespace non_mpi_internal { inline void disableDefinedButNotUsedWarning() { MPI_STATUS_IGNORE = nullptr; MPI_STATUSES_IGNORE = nullptr; } }
 
 const int MPI_MAX_OBJECT_NAME  = 255;
 const int MPI_MAX_ERROR_STRING = 255;
@@ -284,8 +291,8 @@ using namespace mpistubs;
 namespace walberla {
 
 namespace mpi {
-   typedef int MPIRank;
-   typedef int MPISize;
+   using MPIRank = int;
+   using MPISize = int;
    const MPIRank INVALID_RANK = -1;
    const MPISize INVALID_SIZE = -1;
 } // namespace MPI
diff --git a/src/core/mpi/OpenMPBufferSystem.h b/src/core/mpi/OpenMPBufferSystem.h
index 232f54f42a6dbe4037c4b2c8f2d7f02b98773bcb..660a5ccebe415e129f5965c3ef5a418731ef71df 100644
--- a/src/core/mpi/OpenMPBufferSystem.h
+++ b/src/core/mpi/OpenMPBufferSystem.h
@@ -100,7 +100,7 @@ private:
    void waitSerial();
 };
 
-typedef GenericOpenMPBufferSystem<RecvBuffer, SendBuffer> OpenMPBufferSystem;
+using OpenMPBufferSystem = GenericOpenMPBufferSystem<RecvBuffer, SendBuffer>;
 
 
 } // namespace mpi
diff --git a/src/core/mpi/RecvBuffer.h b/src/core/mpi/RecvBuffer.h
index c35b0c2aaf427e90f775fb814e3fe95f96027598..0b3df4abffd48550221cf321c94c4a5c6f95cb77 100644
--- a/src/core/mpi/RecvBuffer.h
+++ b/src/core/mpi/RecvBuffer.h
@@ -81,7 +81,7 @@ class GenericRecvBuffer
 {
 public:
    //**Type definitions*************************************************************************************************
-   typedef T  ElementType;  //!< Type of the receive buffer elements.
+   using ElementType = T;  //!< Type of the receive buffer elements.
    //*******************************************************************************************************************
 
    //**Constructors*****************************************************************************************************
@@ -173,7 +173,7 @@ private:
 };
 //**********************************************************************************************************************
 
-typedef GenericRecvBuffer<> RecvBuffer;
+using RecvBuffer = GenericRecvBuffer<>;
 
 
 
@@ -189,9 +189,9 @@ typedef GenericRecvBuffer<> RecvBuffer;
 template< typename T >  // Element type
 inline GenericRecvBuffer<T>::GenericRecvBuffer()
    : capacity_( 0    )  // Capacity of the receive buffer
-   , begin_   ( NULL )  // Pointer to the first element
-   , cur_     ( NULL )  // Pointer to the current element
-   , end_     ( NULL )  // Pointer to the last element
+   , begin_   ( nullptr )  // Pointer to the first element
+   , cur_     ( nullptr )  // Pointer to the current element
+   , end_     ( nullptr )  // Pointer to the last element
 {}
 //**********************************************************************************************************************
 
@@ -615,9 +615,9 @@ inline void GenericRecvBuffer<T>::reset()
 {
    delete [] begin_;
    capacity_ = 0;
-   begin_    = NULL;
-   cur_      = NULL;
-   end_      = NULL;
+   begin_    = nullptr;
+   cur_      = nullptr;
+   end_      = nullptr;
 }
 //**********************************************************************************************************************
 
diff --git a/src/core/mpi/Reduce.h b/src/core/mpi/Reduce.h
index f60502868477906d5981d85d013b055c43edd83d..c93e9443fa92c4f23e4975d59fbdf6fd78c36d58 100644
--- a/src/core/mpi/Reduce.h
+++ b/src/core/mpi/Reduce.h
@@ -93,7 +93,7 @@ void reduceInplace( T & value, Operation operation, int recvRank = 0, MPI_Comm c
    }
    else
    {
-      MPI_Reduce( &value, 0, 1, MPITrait<T>::type(), toMPI_Op(operation), recvRank, comm );
+      MPI_Reduce( &value, nullptr, 1, MPITrait<T>::type(), toMPI_Op(operation), recvRank, comm );
    }
 }
 
@@ -130,7 +130,7 @@ inline void reduceInplace( bool & value, Operation operation, int recvRank = 0,
    }
    else
    {
-      MPI_Reduce( &intValue, 0, 1, MPITrait<int>::type(), toMPI_Op(operation), recvRank, comm );
+      MPI_Reduce( &intValue, nullptr, 1, MPITrait<int>::type(), toMPI_Op(operation), recvRank, comm );
    }
 
    value = intValue != 0;
@@ -174,7 +174,7 @@ T reduce( const T value, Operation operation, int recvRank = 0, MPI_Comm comm =
    }
    else
    {
-      MPI_Reduce( const_cast<T*>( &value ), 0, 1, MPITrait<T>::type(), toMPI_Op(operation), recvRank, comm );
+      MPI_Reduce( const_cast<T*>( &value ), nullptr, 1, MPITrait<T>::type(), toMPI_Op(operation), recvRank, comm );
    }
 
    return result;
@@ -215,7 +215,7 @@ inline bool reduce( const bool value, Operation operation, int recvRank = 0, MPI
    }
    else
    {
-      MPI_Reduce( &intValue, 0, 1, MPITrait<int>::type(), toMPI_Op(operation), recvRank, comm );
+      MPI_Reduce( &intValue, nullptr, 1, MPITrait<int>::type(), toMPI_Op(operation), recvRank, comm );
    }
 
    return intResult != 0;
@@ -251,11 +251,11 @@ void reduceInplace( std::vector<T> & values, Operation operation, int recvRank =
 
    if( myRank == recvRank )
    {
-      MPI_Reduce( MPI_IN_PLACE, values.empty() ? 0 : &values[0], int_c( values.size() ), MPITrait<T>::type(), toMPI_Op(operation), recvRank, comm );
+      MPI_Reduce( MPI_IN_PLACE, values.empty() ? nullptr : &values[0], int_c( values.size() ), MPITrait<T>::type(), toMPI_Op(operation), recvRank, comm );
    }
    else
    {
-      MPI_Reduce( values.empty() ? 0 : &values[0], 0, int_c( values.size() ), MPITrait<T>::type(), toMPI_Op(operation), recvRank, comm );
+      MPI_Reduce( values.empty() ? nullptr : &values[0], nullptr, int_c( values.size() ), MPITrait<T>::type(), toMPI_Op(operation), recvRank, comm );
    }
 }
 
@@ -291,14 +291,14 @@ inline void reduceInplace( std::vector<bool> & values, Operation operation, int
 
    if( myRank == recvRank )
    {
-      MPI_Reduce( MPI_IN_PLACE, sendBuffer.empty() ? 0 : &sendBuffer[0], int_c( sendBuffer.size() ), MPITrait<uint8_t>::type(), toMPI_Op(operation), recvRank, comm );
+      MPI_Reduce( MPI_IN_PLACE, sendBuffer.empty() ? nullptr : &sendBuffer[0], int_c( sendBuffer.size() ), MPITrait<uint8_t>::type(), toMPI_Op(operation), recvRank, comm );
       size_t size = values.size();
       convert( sendBuffer, values );
       values.resize(size);
    }
    else
    {
-      MPI_Reduce( sendBuffer.empty() ? 0 : &sendBuffer[0], 0, int_c( sendBuffer.size() ), MPITrait<uint8_t>::type(), toMPI_Op(operation), recvRank, comm );
+      MPI_Reduce( sendBuffer.empty() ? nullptr : &sendBuffer[0], nullptr, int_c( sendBuffer.size() ), MPITrait<uint8_t>::type(), toMPI_Op(operation), recvRank, comm );
    }
 }
 
@@ -426,7 +426,7 @@ void allReduceInplace( std::vector<T> & values, Operation operation, MPI_Comm co
 
    WALBERLA_NON_MPI_SECTION() { return; }
 
-   MPI_Allreduce( MPI_IN_PLACE, values.empty() ? 0 : &values[0], int_c( values.size() ), MPITrait<T>::type(), toMPI_Op(operation), comm );
+   MPI_Allreduce( MPI_IN_PLACE, values.empty() ? nullptr : &values[0], int_c( values.size() ), MPITrait<T>::type(), toMPI_Op(operation), comm );
 }
 
 
@@ -451,7 +451,7 @@ inline void allReduceInplace( std::vector<bool> & bools, Operation operation, MP
    std::vector<uint8_t> sendBuffer;
 
    convert( bools, sendBuffer );
-   MPI_Allreduce( MPI_IN_PLACE, sendBuffer.empty() ? 0 : &sendBuffer[0], int_c( sendBuffer.size() ), MPITrait<uint8_t>::type(), toMPI_Op(operation), comm );
+   MPI_Allreduce( MPI_IN_PLACE, sendBuffer.empty() ? nullptr : &sendBuffer[0], int_c( sendBuffer.size() ), MPITrait<uint8_t>::type(), toMPI_Op(operation), comm );
    auto size = bools.size();
    convert(sendBuffer, bools);
    bools.resize(size);
diff --git a/src/core/mpi/SendBuffer.h b/src/core/mpi/SendBuffer.h
index 60d8e471f5ed9891749fd9d75d82c12ad309cbd2..3fac958a74fec25adb8cb4396845f8a184a4b7eb 100644
--- a/src/core/mpi/SendBuffer.h
+++ b/src/core/mpi/SendBuffer.h
@@ -87,7 +87,7 @@ public:
    {
    public:
       static_assert( std::is_fundamental<VT>::value, "only fundamental data types are allowed");
-      typedef VT value_type;
+      using value_type = VT;
 
       Ptr(GenericSendBuffer<T, G>& buffer, const std::ptrdiff_t offset, const size_t length)
          : buffer_(buffer), offset_(offset), length_(length) {}
@@ -102,7 +102,7 @@ public:
    };
 
    //**Type definitions*************************************************************************************************
-   typedef T  ElementType;  //!< Type of the receive buffer elements.
+   using ElementType = T;  //!< Type of the receive buffer elements.
    //*******************************************************************************************************************
 
    //**Constructors*****************************************************************************************************
@@ -216,7 +216,7 @@ private:
 };
 //**********************************************************************************************************************
 
-typedef GenericSendBuffer<> SendBuffer;
+using SendBuffer = GenericSendBuffer<>;
 
 
 //======================================================================================================================
diff --git a/src/core/selectable/SelectableObject.h b/src/core/selectable/SelectableObject.h
index 7f317c11526226ffbfc9ef90a28a238d5e0a4cf2..6a282536dfdd6f29d92806dfebdb003092246623 100644
--- a/src/core/selectable/SelectableObject.h
+++ b/src/core/selectable/SelectableObject.h
@@ -138,7 +138,7 @@ public:
 
 
 
-   virtual ~SelectableObject() {}
+   virtual ~SelectableObject() = default;
 
    void add( const T& object, const A& attributes, const std::string& identifier = std::string() );
 
@@ -221,7 +221,7 @@ size_t SelectableObject<T,A,S>::get( T& object, const S& selector ) const {
 
    select( index, selector );
 
-   if( index.size() >= 1 ) {
+   if( !index.empty() ) {
       WALBERLA_ASSERT_LESS( index[0], object_.size() );
       object = object_[ index[0] ];
    }
@@ -270,7 +270,7 @@ size_t SelectableObject<T,A,S>::get( T& object, std::string& identifier, const S
 
    select( index, selector );
 
-   if( index.size() >= 1 ) {
+   if( !index.empty() ) {
       WALBERLA_ASSERT_LESS( index[0], object_.size() );
       object = object_[ index[0] ];
       identifier = identifier_[ index[0] ];
@@ -325,7 +325,7 @@ const T* SelectableObject<T,A,S>::getUnique( const S& selector ) const {
       return &(object_[ index[0] ]);
    }
 
-   return NULL;
+   return nullptr;
 }
 
 
@@ -363,7 +363,7 @@ const T* SelectableObject<T,A,S>::getUnique( const S& selector, std::string & id
       return &(object_[ index[0] ]);
    }
 
-   return NULL;
+   return nullptr;
 }
 
 
diff --git a/src/core/selectable/SetSelectableObject.h b/src/core/selectable/SetSelectableObject.h
index 8c88a3f23b058ed5a02cc251654292166d9bf85f..e43740aef98f12da597710a7e5911c05dbc1b32c 100644
--- a/src/core/selectable/SetSelectableObject.h
+++ b/src/core/selectable/SetSelectableObject.h
@@ -49,14 +49,14 @@ class SetSelectableObject : public SelectableObject< T, SetSelectionPair<U>, Set
 {
 public:
 
-   SetSelectableObject() {}
+   SetSelectableObject() = default;
 
    SetSelectableObject( const T& object, const Set<U>& include, const Set<U>& exclude, const std::string& identifier = std::string() ) {
 
       add( object, include, exclude, identifier );
    }
 
-   virtual ~SetSelectableObject() {}
+   ~SetSelectableObject() override = default;
 
    void add( const T& object, const Set<U>& include, const Set<U>& exclude, const std::string& identifier = std::string() ) {
 
@@ -73,7 +73,7 @@ private:
    };
 
    // added inline qualifier to suppress unjustified MSVC warning C4505
-   virtual inline void select( std::vector< size_t >& index, const Set<U>& selector ) const;
+   inline void select( std::vector< size_t >& index, const Set<U>& selector ) const override;
 
 }; // SetSelectableObject
 
diff --git a/src/core/singleton/Singleton.h b/src/core/singleton/Singleton.h
index ead687d0c3443b73129b9397e24c644ac0f701a7..f1e5975df02a22e1a6db90e30975e6c81f43035b 100644
--- a/src/core/singleton/Singleton.h
+++ b/src/core/singleton/Singleton.h
@@ -70,14 +70,14 @@ protected:
    // In case a cyclic lifetime dependency is detected, a compilation error is created.
     */
    explicit Singleton()
-   {}
+   = default;
    //*******************************************************************************************************************
 
    //**Destructor*******************************************************************************************************
    /*!\brief Destructor for the Singleton class.
     */
    ~Singleton()
-   {}
+   = default;
    //*******************************************************************************************************************
 
 public:
diff --git a/src/core/timing/Timer.h b/src/core/timing/Timer.h
index f25602bfa04f417089f76ee63ed581a94c7ecd4d..15a9632656e55798888d0da9c6ed0a94651391c4 100644
--- a/src/core/timing/Timer.h
+++ b/src/core/timing/Timer.h
@@ -105,7 +105,7 @@ class Timer
    friend mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, Timer<TP2> & t );
 public:
    //**Type definitions*************************************************************************************************
-   typedef TP  TimingPolicy;  //!< Timing policy of the Timer.
+   using TimingPolicy = TP;  //!< Timing policy of the Timer.
    //*******************************************************************************************************************
 
    //**Constructor******************************************************************************************************
@@ -589,7 +589,7 @@ mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, Timer<TP
 
 } //namespace timing
 
-typedef timing::Timer<timing::CpuPolicy>  CpuTimer;
-typedef timing::Timer<timing::WcPolicy>    WcTimer;
+using CpuTimer = timing::Timer<timing::CpuPolicy>;
+using WcTimer = timing::Timer<timing::WcPolicy>;
 
 } // namespace walberla
diff --git a/src/core/timing/TimingNode.h b/src/core/timing/TimingNode.h
index acd33ecd213f922243d0b343a89b6a69675182d6..6682ab62600653b3b52dcb73da912e66b86d1a53 100644
--- a/src/core/timing/TimingNode.h
+++ b/src/core/timing/TimingNode.h
@@ -76,14 +76,14 @@ struct TimingNode
 
 template< typename TP >  // Timing policy
 TimingNode<TP>::TimingNode()
-   : last_(NULL)
+   : last_(nullptr)
 {
 
 }
 
 template< typename TP >  // Timing policy
 TimingNode<TP>::TimingNode(const TimingNode<TP>& tn)
-   : last_(NULL)
+   : last_(nullptr)
    , timer_(tn.timer_)
    , tree_(tn.tree_)
 {
@@ -301,11 +301,11 @@ void reduceInplace( TimingNode<TP>& tn, ReduceType rt = REDUCE_TOTAL, int target
 
    if( targetRank >= 0 )
    {
-      void * minTarget = targetRank == rank ? &minRed.front() : NULL;
-      void * maxTarget = targetRank == rank ? &maxRed.front() : NULL;
-      void * sumTarget = targetRank == rank ? &sumRed.front() : NULL;
-      void * sumSqTarget = targetRank == rank ? &sumSqRed.front() : NULL;
-      void * countTarget = targetRank == rank ? &countRed.front() : NULL;
+      void * minTarget = targetRank == rank ? &minRed.front() : nullptr;
+      void * maxTarget = targetRank == rank ? &maxRed.front() : nullptr;
+      void * sumTarget = targetRank == rank ? &sumRed.front() : nullptr;
+      void * sumSqTarget = targetRank == rank ? &sumSqRed.front() : nullptr;
+      void * countTarget = targetRank == rank ? &countRed.front() : nullptr;
 
       MPI_Reduce( &min.front(), minTarget,
                   int_c(min.size()), MPITrait<double>::type(), MPI_MIN, targetRank,MPI_COMM_WORLD );
@@ -493,7 +493,7 @@ void addRemainderNodes(timing::TimingNode<TP> &tn) {
 } /// namespace internal
 }
 
-typedef timing::TimingNode<timing::WcPolicy>  WcTimingNode;
-typedef timing::TimingNode<timing::CpuPolicy> CpuTimingNode;
+using WcTimingNode = timing::TimingNode<timing::WcPolicy>;
+using CpuTimingNode = timing::TimingNode<timing::CpuPolicy>;
 
 }
diff --git a/src/core/timing/TimingPool.h b/src/core/timing/TimingPool.h
index 5e8fd71049bb268317d7f84fc4ec47fb6e9ed750..5e41c14d783067b68f4fa76e3a789a0a6728c0bf 100644
--- a/src/core/timing/TimingPool.h
+++ b/src/core/timing/TimingPool.h
@@ -63,15 +63,15 @@ public:
    //**Construction & Destruction***************************************************************************************
    /*! \name Construction & Destruction */
    //@{
-   TimingPool() {}
+   TimingPool() = default;
    //@}
    //*******************************************************************************************************************
 
    //** Access to Timer Objects ****************************************************************************************
    /*! \name Access to Timer Objects */
    //@{
-   typedef typename std::map<std::string, Timer<TP> >::iterator       iterator;
-   typedef typename std::map<std::string, Timer<TP> >::const_iterator const_iterator;
+   using iterator = typename std::map<std::string, Timer<TP>>::iterator;
+   using const_iterator = typename std::map<std::string, Timer<TP>>::const_iterator;
 
          iterator begin()       { return timerMap_.begin(); }
    const_iterator begin() const { return timerMap_.begin(); }
@@ -248,6 +248,6 @@ namespace timing {
 //======================================================================================================================
 
 namespace walberla {
-   typedef timing::TimingPool<timing::WcPolicy>  WcTimingPool;
-   typedef timing::TimingPool<timing::CpuPolicy> CpuTimingPool;
+   using WcTimingPool = timing::TimingPool<timing::WcPolicy>;
+   using CpuTimingPool = timing::TimingPool<timing::CpuPolicy>;
 }
diff --git a/src/core/timing/TimingTree.h b/src/core/timing/TimingTree.h
index b4f4e736b77a2942e6d78d26ef28e74c5764a7e9..32ed44549b97482bf4a06604588f5a7d2311f336 100644
--- a/src/core/timing/TimingTree.h
+++ b/src/core/timing/TimingTree.h
@@ -1,15 +1,15 @@
 //======================================================================================================================
 //
-//  This file is part of waLBerla. waLBerla is free software: you can 
+//  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 
+//  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 
+//
+//  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/>.
 //
@@ -68,16 +68,19 @@ public:
    /// Resets the the timing hierarchy
    void reset();
 
-   //** Reduction ******************************************************************************************************
-   /*! \name Reduction */
-   //@{
    /// Collects all the timing data from different processes
-   TimingTree<TP> getReduced( ReduceType rt = REDUCE_TOTAL, int targetRank = 0 ) const;
+   ///
+   /// \param rt type of reduction used
+   /// \param targetRank
+   /// \param callSynchronize call synchronize() before doing the reduction
+   /// \return
+   ///
+   /// \attention Setting callSynchronize to false can lead to deadlocks during reduction if different ranks
+   ///            have different timing nodes! Setting it to true causes an additional communication.
+   TimingTree<TP> getReduced( ReduceType rt = REDUCE_TOTAL, int targetRank = 0, bool callSynchronize = true ) const;
 
    /// Adds entries which only exist on other processes. Has to be collectively called on all processes!
    void synchronize();
-   //@}
-   //*******************************************************************************************************************
 
    /// Returns the raw tree data structure
    const TimingNode<TP>& getRawData() const;
@@ -109,15 +112,15 @@ std::ostream& operator<<( std::ostream& os, const TimingTree<TP>& tt )
 
 template< typename TP >  // Timing policy
 TimingTree<TP>::TimingTree()
-   : current_(&root_)
+      : current_(&root_)
 {
 
 }
 
 template< typename TP >  // Timing policy
 TimingTree<TP>::TimingTree(const TimingTree<TP>& tt)
-   : root_(tt.root_)
-   , current_(&root_)
+      : root_(tt.root_)
+      , current_(&root_)
 {
    WALBERLA_ASSERT_EQUAL(tt.current_, &tt.root_, "Copying is only allowed for stopped TimingTrees!\nTimer still running: " << getCurrentTimerName() );
 }
@@ -133,8 +136,8 @@ TimingTree<TP>& TimingTree<TP>::operator=(const TimingTree<TP>& tt)
 template< typename TP >  // Timing policy
 void TimingTree<TP>::swap(TimingTree<TP>& tt)
 {
-    std::swap(current_, tt.current_);
-    std::swap(root_, tt.root_);
+   std::swap(current_, tt.current_);
+   std::swap(root_, tt.root_);
 }
 
 /// \param name timer name. '.' is not allowed in the timer name!
@@ -179,9 +182,7 @@ bool TimingTree<TP>::isTimerRunning(const std::string& name) const
 {
    WALBERLA_ASSERT_NOT_NULLPTR( current_->last_ );
    auto timerIt = current_->last_->tree_.find(name);
-   if ((timerIt == current_->last_->tree_.end()) || (&(timerIt->second) != current_))
-      return false;
-   return true;
+   return !((timerIt == current_->last_->tree_.end()) || (&(timerIt->second) != current_));
 }
 
 template< typename TP >  // Timing policy
@@ -191,9 +192,13 @@ void TimingTree<TP>::reset()
 }
 
 template< typename TP >  // Timing policy
-TimingTree<TP> TimingTree<TP>::getReduced( ReduceType rt, int targetRank ) const
+TimingTree<TP> TimingTree<TP>::getReduced( ReduceType rt, int targetRank, bool callSynchronize ) const
 {
    TimingTree<TP> tt(*this);
+   if (callSynchronize)
+   {
+      tt.synchronize();
+   }
    reduceInplace( tt.root_, rt, targetRank );
    return tt;
 }
@@ -253,6 +258,6 @@ TimingTree< TP > TimingTree< TP >::getCopyWithRemainder() const
 
 }
 
-typedef timing::TimingTree<timing::WcPolicy>  WcTimingTree;
-typedef timing::TimingTree<timing::CpuPolicy> CpuTimingTree;
+using WcTimingTree = timing::TimingTree<timing::WcPolicy>;
+using CpuTimingTree = timing::TimingTree<timing::CpuPolicy>;
 }
diff --git a/src/core/uid/SUID.h b/src/core/uid/SUID.h
index 329abb0fa0dd45f2bcc50adf665c224f6b8deb8f..21b48ca261242354e910e9746abd1989e6045d59 100644
--- a/src/core/uid/SUID.h
+++ b/src/core/uid/SUID.h
@@ -33,7 +33,7 @@ namespace uid {
 namespace suidgenerator {
 class S : public IndexGenerator< S, size_t >{}; ///< generator class for unified state/selection UIDs
 }
-typedef UID< suidgenerator::S > SUID;           ///< unified state/selection UID
+using SUID = UID<suidgenerator::S>;           ///< unified state/selection UID
 
 
 } // namespace uid
diff --git a/src/core/uid/UID.h b/src/core/uid/UID.h
index f90f7f9de7e8bf9cb5a65d1df5341e806f73d96e..89a270e4cbe23ae5c17819d20d3dc43f6aeba22e 100644
--- a/src/core/uid/UID.h
+++ b/src/core/uid/UID.h
@@ -74,7 +74,7 @@ template< typename T > class UID {
 
 public:
 
-   typedef typename T::uint_type uint_type;
+   using uint_type = typename T::uint_type;
 
 
             inline UID( const char* const  identifier, const bool newUid = false, const bool appendUIDtoIdentifier = false );
diff --git a/src/core/uid/UIDGenerators.h b/src/core/uid/UIDGenerators.h
index 0e913b16a100773e9f2aa149c55e2320a51684a4..4d4a333f9800ffdd88f32fa6e277f710ea21615d 100644
--- a/src/core/uid/UIDGenerators.h
+++ b/src/core/uid/UIDGenerators.h
@@ -87,8 +87,8 @@ template< typename T, typename UINT >
 class IndexGenerator {
 public:
 
-   typedef UINT uint_type;
-   typedef index_generated_tag generator_type;
+   using uint_type = UINT;
+   using generator_type = index_generated_tag;
 
    static uint_type generateUID() {
       static uint_type uid(0);
@@ -123,8 +123,8 @@ template< typename T, typename UINT >
 class StandardGenerator {
 public:
 
-   typedef UINT uint_type;
-   typedef standard_generated_tag generator_type;
+   using uint_type = UINT;
+   using generator_type = standard_generated_tag;
 
    static uint_type generateUID() {
       static uint_type uid(0);
@@ -216,8 +216,8 @@ template< typename T, typename UINT >
 class BitGenerator {
 public:
 
-   typedef UINT uint_type;
-   typedef bit_generated_tag generator_type;
+   using uint_type = UINT;
+   using generator_type = bit_generated_tag;
 
    static uint_type generateUID() {
       static uint_type uid(1);
@@ -262,8 +262,8 @@ template< typename T, typename UINT >
 class SingletonGenerator {
 public:
 
-   typedef UINT uint_type;
-   typedef singleton_generated_tag generator_type;
+   using uint_type = UINT;
+   using generator_type = singleton_generated_tag;
 
    static uint_type generateUID() {
       static uint_type uid(0);
diff --git a/src/cuda/CMakeLists.txt b/src/cuda/CMakeLists.txt
index b5174a3e52107edb4c2f1cf168604d096b41106d..46b22c43a0056afc407c98341384e8d22b5147f8 100644
--- a/src/cuda/CMakeLists.txt
+++ b/src/cuda/CMakeLists.txt
@@ -4,7 +4,7 @@
 #
 ###################################################################################################
 
-waLBerla_add_module( DEPENDS blockforest core communication domain_decomposition executiontree python_coupling field stencil lbm
+waLBerla_add_module( DEPENDS blockforest core communication domain_decomposition executiontree field stencil lbm
                      BUILD_ONLY_IF_FOUND CUDA )
 
 ###################################################################################################
\ No newline at end of file
diff --git a/src/cuda/communication/UniformGPUScheme.h b/src/cuda/communication/UniformGPUScheme.h
index 0dda96a480a154a4f400e7d1e425cc49eb477472..ba702c5740be3847f8e057af68d002f6fc62e21e 100644
--- a/src/cuda/communication/UniformGPUScheme.h
+++ b/src/cuda/communication/UniformGPUScheme.h
@@ -24,7 +24,6 @@
 #include "blockforest/StructuredBlockForest.h"
 #include "core/mpi/MPIWrapper.h"
 #include "core/mpi/BufferSystem.h"
-#include "core/WeakPtrWrapper.h"
 #include "domain_decomposition/IBlock.h"
 #include "stencil/Directions.h"
 
@@ -45,7 +44,7 @@ namespace communication {
    class UniformGPUScheme
    {
    public:
-       explicit UniformGPUScheme( weak_ptr_wrapper<StructuredBlockForest> bf,
+       explicit UniformGPUScheme( weak_ptr<StructuredBlockForest> bf,
                                   bool sendDirectlyFromGPU = false,
                                   const int tag = 5432 );
 
@@ -60,7 +59,7 @@ namespace communication {
    private:
        void setupCommunication();
 
-       weak_ptr_wrapper<StructuredBlockForest> blockForest_;
+       weak_ptr<StructuredBlockForest> blockForest_;
        uint_t forestModificationStamp_;
 
        bool setupBeforeNextCommunication_;
diff --git a/src/cuda/communication/UniformGPUScheme.impl.h b/src/cuda/communication/UniformGPUScheme.impl.h
index b39d9d054a7cf4fad71b328b8f5dbc54ca06a959..03b65f3b58981a1239152ddb1f444798e1f9a90e 100644
--- a/src/cuda/communication/UniformGPUScheme.impl.h
+++ b/src/cuda/communication/UniformGPUScheme.impl.h
@@ -27,7 +27,7 @@ namespace communication {
 
 
 template<typename Stencil>
-UniformGPUScheme<Stencil>::UniformGPUScheme( weak_ptr_wrapper <StructuredBlockForest> bf,
+UniformGPUScheme<Stencil>::UniformGPUScheme( weak_ptr <StructuredBlockForest> bf,
                                              bool sendDirectlyFromGPU,
                                              const int tag )
         : blockForest_( bf ),
diff --git a/src/cuda/python/Exports.impl.h b/src/cuda/python/Exports.impl.h
deleted file mode 100644
index fa1352db9dbed3d49847ff975ce5b2a3b4d421aa..0000000000000000000000000000000000000000
--- a/src/cuda/python/Exports.impl.h
+++ /dev/null
@@ -1,407 +0,0 @@
-//======================================================================================================================
-//
-//  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 FieldExport.cpp
-//! \ingroup cuda
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-// Do not reorder includes - the include order is important
-#include "python_coupling/PythonWrapper.h"
-
-#include "core/logging/Logging.h"
-#include "cuda/GPUField.h"
-#include "cuda/communication/GPUPackInfo.h"
-#include "cuda/AddGPUFieldToStorage.h"
-#include "cuda/FieldCopy.h"
-#include "cuda/GPUField.h"
-#include "field/communication/UniformMPIDatatypeInfo.h"
-#include "field/AddToStorage.h"
-#include "field/python/FieldExport.h"
-#include "python_coupling/helper/MplHelpers.h"
-#include "python_coupling/helper/BoostPythonHelpers.h"
-
-#include <type_traits>
-#include <iostream>
-
-namespace walberla {
-namespace cuda {
-
-
-
-namespace internal {
-
-   //===================================================================================================================
-   //
-   //  Field export
-   //
-   //===================================================================================================================
-
-   template<typename GpuField_T>
-   uint64_t gpufield_ptr(const GpuField_T & gpuField)
-   {
-      return reinterpret_cast<uint64_t>(gpuField.pitchedPtr().ptr);
-   }
-
-   template<typename GpuField_T>
-   std::string gpufield_dtypeStr(const GpuField_T & )
-   {
-      return std::string(field::internal::PythonFormatString<typename GpuField_T::value_type>::get());
-   }
-
-   struct GpuFieldExporter
-   {
-      template< typename GpuField_T>
-      void operator() ( python_coupling::NonCopyableWrap<GpuField_T> )
-      {
-         using namespace boost::python;
-
-         class_<GpuField_T, shared_ptr<GpuField_T>, boost::noncopyable>( "GpuField", no_init )
-            .add_property("layout",              &field::internal::field_layout            < GpuField_T > )
-            .add_property("size",                &field::internal::field_size              < GpuField_T > )
-            .add_property("sizeWithGhostLayers", &field::internal::field_sizeWithGhostLayer< GpuField_T > )
-            .add_property("allocSize",           &field::internal::field_allocSize         < GpuField_T > )
-            .add_property("strides",             &field::internal::field_strides           < GpuField_T > )
-            .add_property("offsets",             &field::internal::field_offsets           < GpuField_T > )
-            .add_property("ptr",                 &gpufield_ptr                             < GpuField_T > )
-            .add_property("dtypeStr",            &gpufield_dtypeStr                        < GpuField_T > )
-            .add_property("isPitchedMem",        &GpuField_T::isPitchedMem )
-            .def("swapDataPointers",             &field::internal::field_swapDataPointers  < GpuField_T > )
-            .add_property("nrOfGhostLayers",     &GpuField_T::nrOfGhostLayers )
-            .def("cloneUninitialized", &GpuField_T::cloneUninitialized, return_value_policy<manage_new_object>())
-            ;
-
-
-         using field::communication::PackInfo;
-         using communication::GPUPackInfo;
-         class_< GPUPackInfo<GpuField_T>,
-                 shared_ptr< GPUPackInfo<GpuField_T> >,
-                 bases<walberla::communication::UniformPackInfo>,
-                 boost::noncopyable >( "GpuFieldPackInfo", no_init );
-
-         using field::communication::UniformMPIDatatypeInfo;
-         class_< UniformMPIDatatypeInfo<GpuField_T>,
-                 shared_ptr< UniformMPIDatatypeInfo<GpuField_T> >,
-                 bases<walberla::communication::UniformMPIDatatypeInfo>,
-                 boost::noncopyable >( "GpuFieldMPIDataTypeInfo", no_init );
-
-      }
-   };
-
-
-   //===================================================================================================================
-   //
-   //  createField
-   //
-   //===================================================================================================================
-
-   class CreateFieldExporter
-   {
-   public:
-      CreateFieldExporter( uint_t xs, uint_t ys, uint_t zs, uint_t fs, uint_t gl,
-                           Layout layout, const boost::python::object & type, bool usePitchedMem,
-                           const shared_ptr<boost::python::object> & resultPointer )
-         : xs_( xs ), ys_(ys), zs_(zs), fs_(fs), gl_(gl),
-           layout_( layout),  type_( type ), usePitchedMem_( usePitchedMem ) , resultPointer_( resultPointer )
-      {}
-
-      template< typename GpuField_T>
-      void operator() ( python_coupling::NonCopyableWrap<GpuField_T> )
-      {
-         using namespace boost::python;
-         typedef typename GpuField_T::value_type T;
-         if( python_coupling::isCppEqualToPythonType<T>( (PyTypeObject *)type_.ptr() )  )
-         {
-            *resultPointer_ = object( make_shared< GPUField<T> >( xs_,ys_,zs_, fs_,  gl_, layout_, usePitchedMem_ )  );
-         }
-      }
-
-   private:
-      uint_t xs_;
-      uint_t ys_;
-      uint_t zs_;
-      uint_t fs_;
-      uint_t gl_;
-      Layout layout_;
-      boost::python::object type_;
-      bool usePitchedMem_;
-      shared_ptr<boost::python::object> resultPointer_;
-   };
-
-   template<typename GpuFields>
-   boost::python::object createPythonGpuField( boost::python::list size,
-                                               boost::python::object type,
-                                               uint_t ghostLayers,
-                                               Layout layout,
-                                               bool usePitchedMem)
-   {
-      using namespace boost::python;
-      uint_t xSize = extract<uint_t> ( size[0] );
-      uint_t ySize = extract<uint_t> ( size[1] );
-      uint_t zSize = extract<uint_t> ( size[2] );
-      uint_t sizeLen = uint_c( len( size ) );
-      uint_t fSize = 1;
-      if ( sizeLen == 4 )
-         fSize = extract<uint_t> ( size[3] );
-
-      if ( ! PyType_Check( type.ptr() ) ) {
-         PyErr_SetString( PyExc_RuntimeError, "Invalid 'type' parameter");
-         throw error_already_set();
-      }
-
-      auto result = make_shared<boost::python::object>();
-      CreateFieldExporter exporter( xSize,ySize, zSize, fSize, ghostLayers, layout, type, usePitchedMem, result );
-      python_coupling::for_each_noncopyable_type< GpuFields >( exporter );
-
-      if ( *result == object()  )
-      {
-         PyErr_SetString( PyExc_ValueError, "Cannot create field of this type");
-         throw error_already_set();
-      }
-      else {
-         return *result;
-      }
-   }
-
-
-   //===================================================================================================================
-   //
-   //  addToStorage
-   //
-   //===================================================================================================================
-
-   class AddToStorageExporter
-   {
-   public:
-      AddToStorageExporter( const shared_ptr<StructuredBlockStorage> & blocks,
-                           const std::string & name, uint_t fs, uint_t gl, Layout layout,
-                           const boost::python::object & type,
-                           bool usePitchedMem )
-         : blocks_( blocks ), name_( name ), fs_( fs ),
-           gl_(gl),layout_( layout),  type_( type ), usePitchedMem_(usePitchedMem), found_(false)
-      {}
-
-      template< typename GpuField_T>
-      void operator() ( python_coupling::NonCopyableWrap<GpuField_T> )
-      {
-         typedef typename GpuField_T::value_type T;
-         if( python_coupling::isCppEqualToPythonType<T>( (PyTypeObject *)type_.ptr() )  )
-         {
-            WALBERLA_ASSERT(!found_);
-            addGPUFieldToStorage<GPUField<T> >(blocks_, name_, fs_, layout_, gl_, usePitchedMem_);
-            found_ = true;
-         }
-      }
-
-      bool successful() const { return found_; }
-   private:
-      shared_ptr< StructuredBlockStorage > blocks_;
-      std::string name_;
-      uint_t fs_;
-      uint_t gl_;
-      Layout layout_;
-      boost::python::object type_;
-      bool usePitchedMem_;
-      bool found_;
-   };
-
-   template<typename GpuFields>
-   void addToStorage( const shared_ptr<StructuredBlockStorage> & blocks, const std::string & name,
-                      boost::python::object type, uint_t fs, uint_t gl, Layout layout, bool usePitchedMem )
-   {
-      using namespace boost::python;
-
-      if ( ! PyType_Check( type.ptr() ) ) {
-         PyErr_SetString( PyExc_RuntimeError, "Invalid 'type' parameter");
-         throw error_already_set();
-      }
-
-      auto result = make_shared<boost::python::object>();
-      AddToStorageExporter exporter( blocks, name, fs, gl, layout, type, usePitchedMem );
-      python_coupling::for_each_noncopyable_type<GpuFields>( std::ref(exporter) );
-
-      if ( ! exporter.successful() ) {
-         PyErr_SetString( PyExc_ValueError, "Adding Field failed.");
-         throw error_already_set();
-      }
-   }
-
-
-   //===================================================================================================================
-   //
-   //  createPackInfo Export
-   //
-   //===================================================================================================================
-
-   template< typename GPUField_T >
-   boost::python::object createGPUPackInfoToObject( BlockDataID bdId, uint_t numberOfGhostLayers )
-   {
-      using cuda::communication::GPUPackInfo;
-      if ( numberOfGhostLayers > 0  )
-         return boost::python::object( make_shared< GPUPackInfo<GPUField_T> >( bdId, numberOfGhostLayers ) );
-      else
-         return boost::python::object( make_shared< GPUPackInfo<GPUField_T> >( bdId ) );
-   }
-
-   FunctionExporterClass( createGPUPackInfoToObject, boost::python::object( BlockDataID, uint_t  ) );
-
-   template< typename GpuFields>
-   boost::python::object createPackInfo( const shared_ptr<StructuredBlockStorage> & bs,
-                                         const std::string & blockDataName, uint_t numberOfGhostLayers )
-   {
-      using cuda::communication::GPUPackInfo;
-
-      auto bdId = python_coupling::blockDataIDFromString( *bs, blockDataName );
-      if ( bs->begin() == bs->end() ) {
-         // if no blocks are on this field an arbitrary PackInfo can be returned
-         return createGPUPackInfoToObject< GPUField<real_t> > ( bdId, numberOfGhostLayers );
-      }
-
-      IBlock * firstBlock =  & ( * bs->begin() );
-      python_coupling::Dispatcher<GpuFields, Exporter_createGPUPackInfoToObject > dispatcher( firstBlock );
-      return dispatcher( bdId )( bdId, numberOfGhostLayers ) ;
-   }
-
-
-   //===================================================================================================================
-   //
-   //  createMPIDatatypeInfo
-   //
-   //===================================================================================================================
-
-
-   template< typename GpuField_T >
-   boost::python::object createMPIDatatypeInfoToObject( BlockDataID bdId, uint_t numberOfGhostLayers )
-   {
-      using field::communication::UniformMPIDatatypeInfo;
-      if ( numberOfGhostLayers > 0 )
-         return boost::python::object( make_shared< UniformMPIDatatypeInfo<GpuField_T> >( bdId, numberOfGhostLayers ) );
-      else
-         return boost::python::object( make_shared< UniformMPIDatatypeInfo<GpuField_T> >( bdId ) );
-   }
-
-   FunctionExporterClass( createMPIDatatypeInfoToObject, boost::python::object( BlockDataID, uint_t  ) );
-
-   template< typename GpuFields>
-   boost::python::object createMPIDatatypeInfo( const shared_ptr<StructuredBlockStorage> & bs,
-                                                const std::string & blockDataName,
-                                                uint_t numberOfGhostLayers)
-   {
-      auto bdId = python_coupling::blockDataIDFromString( *bs, blockDataName );
-      if ( bs->begin() == bs->end() ) {
-         // if no blocks are on this field an arbitrary MPIDatatypeInfo can be returned
-         return createMPIDatatypeInfoToObject< GPUField<real_t> > ( bdId, numberOfGhostLayers );
-      }
-
-      IBlock * firstBlock =  & ( * bs->begin() );
-      python_coupling::Dispatcher<GpuFields, Exporter_createMPIDatatypeInfoToObject > dispatcher( firstBlock );
-      return dispatcher( bdId )( bdId, numberOfGhostLayers );
-   }
-
-
-   //===================================================================================================================
-   //
-   //  fieldCopy
-   //
-   //===================================================================================================================
-
-   template<typename Field_T>
-   void copyFieldToGpuDispatch(const shared_ptr<StructuredBlockStorage> & bs,
-                               BlockDataID cpuFieldId, BlockDataID gpuFieldId, bool toGpu)
-   {
-      typedef cuda::GPUField<typename Field_T::value_type> GpuField;
-      if(toGpu)
-         cuda::fieldCpy<GpuField, Field_T>(bs, gpuFieldId, cpuFieldId);
-      else
-         cuda::fieldCpy<Field_T, GpuField>(bs, cpuFieldId, gpuFieldId);
-   }
-   FunctionExporterClass( copyFieldToGpuDispatch,
-                          void( const shared_ptr<StructuredBlockStorage> &, BlockDataID, BlockDataID, bool ) );
-
-   template< typename FieldTypes >
-   void transferFields( const shared_ptr<StructuredBlockStorage> & bs,
-                        const std::string & gpuFieldId, const std::string & cpuFieldId, bool toGpu)
-   {
-      if( bs->begin() == bs->end()) {
-         return;
-      };
-
-      auto dstBdId = python_coupling::blockDataIDFromString( *bs, gpuFieldId );
-      auto srcBdId = python_coupling::blockDataIDFromString( *bs, cpuFieldId );
-
-      IBlock * firstBlock =  & ( * bs->begin() );
-      python_coupling::Dispatcher<FieldTypes, Exporter_copyFieldToGpuDispatch> dispatcher( firstBlock );
-      dispatcher( srcBdId )( bs, srcBdId, dstBdId, toGpu );
-   }
-
-   template< typename FieldTypes>
-   void copyFieldToGpu(const shared_ptr<StructuredBlockStorage> & bs,
-                       const std::string & gpuFieldId, const std::string & cpuFieldId)
-   {
-      transferFields<FieldTypes>(bs, gpuFieldId, cpuFieldId, true);
-   }
-
-   template< typename FieldTypes>
-   void copyFieldToCpu(const shared_ptr<StructuredBlockStorage> & bs,
-                       const std::string & gpuFieldId, const std::string & cpuFieldId)
-   {
-      transferFields<FieldTypes>(bs, gpuFieldId, cpuFieldId, false);
-   }
-
-} // namespace internal
-
-
-
-
-template<typename GpuFields, typename CpuFields >
-void exportModuleToPython()
-{
-   python_coupling::ModuleScope fieldModule( "cuda" );
-
-   using namespace boost::python;
-
-   python_coupling::for_each_noncopyable_type<GpuFields>( internal::GpuFieldExporter() );
-
-   def( "createGpuField", &internal::createPythonGpuField<GpuFields>, ( ( arg("size")                     ),
-                                                                         ( arg("type")                     ),
-                                                                         ( arg("ghostLayers") = uint_t(1)  ),
-                                                                         ( arg("layout")      = field::zyxf),
-                                                                         ( arg("usePitchedMem") = true     )  ) );
-
-
-   def( "addGpuFieldToStorage",  &internal::addToStorage<GpuFields>, ( ( arg("blocks")                  ),
-                                                                        ( arg("name")                    ),
-                                                                        ( arg("type")                    ),
-                                                                        ( arg("fSize")       = 1         ),
-                                                                        ( arg("ghostLayers") = uint_t(1) ),
-                                                                        ( arg("layout")      = field::zyxf      ),
-                                                                        ( arg("usePitchedMem") = object()  ) ) );
-
-   def( "createMPIDatatypeInfo",&internal::createMPIDatatypeInfo<GpuFields>, ( arg("blocks"), arg("blockDataName"), arg("numberOfGhostLayers" ) =0 ) );
-   def( "createPackInfo",       &internal::createPackInfo<GpuFields>,        ( arg("blocks"), arg("blockDataName"), arg("numberOfGhostLayers" ) =0 ) );
-
-   def( "copyFieldToGpu", &internal::copyFieldToGpu<CpuFields>, (arg("blocks"), ("gpuFieldId"), ("cpuFieldId")));
-   def( "copyFieldToCpu", &internal::copyFieldToCpu<CpuFields>, (arg("blocks"), ("gpuFieldId"), ("cpuFieldId")));
-}
-
-
-
-
-
-} // namespace cuda
-} // namespace walberla
-
-
diff --git a/src/domain_decomposition/BlockDataHandling.h b/src/domain_decomposition/BlockDataHandling.h
index 4bd96dae090d6c09d5c43498d61343600803312b..0720eb572ffadafc2deb78b3733e4cf5ff225029 100644
--- a/src/domain_decomposition/BlockDataHandling.h
+++ b/src/domain_decomposition/BlockDataHandling.h
@@ -45,9 +45,9 @@ template< typename T >
 class BlockDataHandling
 {
 public:
-   typedef T value_type;
+   using value_type = T;
 
-   virtual ~BlockDataHandling() {}
+   virtual ~BlockDataHandling() = default;
    
    /// must be thread-safe !
    virtual T * initialize( IBlock * const block ) = 0;
@@ -66,11 +66,11 @@ template< typename T >
 class AlwaysInitializeBlockDataHandling : public BlockDataHandling<T>
 {
 public:
-   ~AlwaysInitializeBlockDataHandling() {}
+   ~AlwaysInitializeBlockDataHandling() override = default;
 
-   void serialize( IBlock * const, const BlockDataID &, mpi::SendBuffer & ) {}
-   T * deserialize( IBlock * const block ) { return this->initialize( block ); }
-   void deserialize( IBlock * const, const BlockDataID &, mpi::RecvBuffer & ) {}
+   void serialize( IBlock * const, const BlockDataID &, mpi::SendBuffer & ) override {}
+   T * deserialize( IBlock * const block ) override { return this->initialize( block ); }
+   void deserialize( IBlock * const, const BlockDataID &, mpi::RecvBuffer & ) override {}
 };
 
 
@@ -82,29 +82,29 @@ class BlockDataHandlingFunctionAdaptor : public BlockDataHandling<T>
 {
 public:
 
-   typedef std::function< T* ( IBlock * const block ) > Function;
+   using Function = std::function<T *(IBlock *const)>;
 
    BlockDataHandlingFunctionAdaptor( const Function & function ) : function_( function ) {}
 
-   ~BlockDataHandlingFunctionAdaptor() {}
+   ~BlockDataHandlingFunctionAdaptor() override = default;
 
-   T * initialize( IBlock * const block ) { return function_( block ); }
+   T * initialize( IBlock * const block ) override { return function_( block ); }
    
-   void serialize( IBlock * const, const BlockDataID &, mpi::SendBuffer & )
+   void serialize( IBlock * const, const BlockDataID &, mpi::SendBuffer & ) override
    {
       WALBERLA_ABORT( "You are trying to serialize a block data item for which only an initialization function was registered" );
 #ifdef __IBMCPP__
       return NULL; // never reached, helps to suppress a warning from the IBM compiler
 #endif
    }
-   T * deserialize( IBlock * const )
+   T * deserialize( IBlock * const ) override
    {
       WALBERLA_ABORT( "You are trying to deserialize a block data item for which only an initialization function was registered" );
 #ifdef __IBMCPP__
       return NULL; // never reached, helps to suppress a warning from the IBM compiler
 #endif
    }
-   void deserialize( IBlock * const, const BlockDataID &, mpi::RecvBuffer & )
+   void deserialize( IBlock * const, const BlockDataID &, mpi::RecvBuffer & ) override
    {
       WALBERLA_ABORT( "You are trying to deserialize a block data item for which only an initialization function was registered" );
    }
@@ -155,7 +155,7 @@ namespace internal {
 class BlockDataHandlingWrapper
 {
 public:
-   virtual ~BlockDataHandlingWrapper() {}
+   virtual ~BlockDataHandlingWrapper() = default;
 
    virtual BlockData * initialize( IBlock * const block ) = 0;
    
@@ -173,27 +173,27 @@ public:
 
    BlockDataHandlingHelper( const shared_ptr< BlockDataHandling<T> > & dataHandling ) : dataHandling_( dataHandling ) {}
   
-   BlockData * initialize( IBlock * const block )
+   BlockData * initialize( IBlock * const block ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       T * ptr = dataHandling_->initialize( block );
-      return ptr ? new BlockData( ptr ) : NULL;
+      return ptr ? new BlockData( ptr ) : nullptr;
    }
    
-   void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer )
+   void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       dataHandling_->serialize( block, id, buffer );
    }
    
-   BlockData * deserialize( IBlock * const block )
+   BlockData * deserialize( IBlock * const block ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       T * ptr = dataHandling_->deserialize( block );
-      return ptr ? new BlockData( ptr ) : NULL;
+      return ptr ? new BlockData( ptr ) : nullptr;
    }
    
-   void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer )
+   void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( block );
       dataHandling_->deserialize( block, id, buffer );
@@ -206,7 +206,7 @@ private:
 
 
 
-typedef selectable::SetSelectableObject< shared_ptr< BlockDataHandlingWrapper >, SUID > SelectableBlockDataHandlingWrapper;
+using SelectableBlockDataHandlingWrapper = selectable::SetSelectableObject<shared_ptr<BlockDataHandlingWrapper>, SUID>;
 
 
 
diff --git a/src/domain_decomposition/BlockStorage.h b/src/domain_decomposition/BlockStorage.h
index 34789a47b625f8db032975c470a8efdff312e51b..b59f7b30fe803a2c9abbe48c8856c9b4bbf2be20 100644
--- a/src/domain_decomposition/BlockStorage.h
+++ b/src/domain_decomposition/BlockStorage.h
@@ -73,17 +73,21 @@ class BlockStorage : private NonCopyable {
 
 public:
 
-   typedef std::map< IBlockID::IDType, IBlock* > BlockContainerType ;
+   using BlockContainerType = std::map<IBlockID::IDType, IBlock *> ;
 
    class const_iterator;
 
-   class iterator : public std::iterator <std::forward_iterator_tag, IBlock > {
+   class iterator {
       friend class const_iterator;
       friend class BlockStorage;
    public:
+      using iterator_category = std::forward_iterator_tag;
+      using value_type = IBlock;
+      using difference_type = std::ptrdiff_t;
+      using pointer = IBlock*;
+      using reference = IBlock&;
 
-      iterator( const iterator & it ) :
-         it_( it.it_ ), end_( it.end_ ), requiredSelectors_( it.requiredSelectors_ ), incompatibleSelectors_( it.incompatibleSelectors_ ) {}
+      iterator( const iterator & it )  = default;
 
       iterator & operator++()    { ++it_; checkStateAndAdapt(); return *this; }      // prefix ++X
       iterator   operator++(int) { iterator it( *this ); operator++(); return it; }; // postfix X++
@@ -128,8 +132,7 @@ public:
 
       const_iterator( const iterator & it ) :
          it_( it.it_ ), end_( it.end_ ), requiredSelectors_( it.requiredSelectors_ ), incompatibleSelectors_( it.incompatibleSelectors_ ) {}
-      const_iterator( const const_iterator & it ) :
-         it_( it.it_ ), end_( it.end_ ), requiredSelectors_( it.requiredSelectors_ ), incompatibleSelectors_( it.incompatibleSelectors_ ) {}
+      const_iterator( const const_iterator & it )  = default;
 
       const_iterator & operator++()    { ++it_; checkStateAndAdapt(); return *this; }            // prefix ++X
       const_iterator   operator++(int) { const_iterator it( *this ); operator++(); return it; }; // postfix X++
@@ -280,7 +283,7 @@ public:
     
      if( it != iBlocks_.end()) return it->second;
 
-     return NULL;
+     return nullptr;
    }
 
    //*******************************************************************************************************************
@@ -301,7 +304,7 @@ public:
     
      if( it != iBlocks_.end()) return it->second;
 
-     return NULL;
+     return nullptr;
    }
 
    //*******************************************************************************************************************
diff --git a/src/domain_decomposition/IBlock.h b/src/domain_decomposition/IBlock.h
index c25c55ee9a6f5e1224941ee51c3144ebd230adcc..06e48b6905194a71ed3970550ca65ee525876080 100644
--- a/src/domain_decomposition/IBlock.h
+++ b/src/domain_decomposition/IBlock.h
@@ -50,7 +50,7 @@ private:
 
    class DataBase {
    public:
-      virtual ~DataBase() {}
+      virtual ~DataBase() = default;
       virtual bool operator==( const DataBase& rhs ) const = 0;
       virtual bool isOfSameType( const DataBase& rhs ) const = 0;
    };
@@ -59,13 +59,13 @@ private:
    class Data : public DataBase {
    public:
       Data( T* data ) : data_( data ) {}
-      ~Data() { delete data_; }
-      bool operator==( const DataBase& rhs ) const {
+      ~Data() override { delete data_; }
+      bool operator==( const DataBase& rhs ) const override {
          const Data<T>* rhsData = dynamic_cast< const Data<T>* >( &rhs );
          return ( rhsData == &rhs ) && ( *data_ == *(rhsData->data_) ); // every object that is registered as block data
                                                                         // must be comparable with "==" !
       }
-      bool isOfSameType( const DataBase& rhs ) const { return dynamic_cast< const Data<T>* >( &rhs ) == &rhs; }
+      bool isOfSameType( const DataBase& rhs ) const override { return dynamic_cast< const Data<T>* >( &rhs ) == &rhs; }
    private:
       T* data_;
    };
@@ -207,7 +207,7 @@ class IBlock : private NonCopyable
 {
 public:
 
-   typedef internal::BlockData BlockData;
+   using BlockData = internal::BlockData;
 
    friend class           BlockStorage;
    friend class StructuredBlockStorage;
@@ -299,7 +299,7 @@ inline bool IBlock::isBlockDataAllocated( const ConstBlockDataID & index ) const
 
    WALBERLA_ASSERT_LESS( uint_t( index ), data_.size() );
 
-   return ( data_[index] != NULL );
+   return ( data_[index] != nullptr );
 }
 
 
@@ -314,8 +314,8 @@ inline const T* IBlock::getData( const ConstBlockDataID & index ) const {
 
    WALBERLA_ASSERT_LESS( uint_t( index ), data_.size() );
 
-   if( data_[index] == NULL )
-      return NULL;
+   if( data_[index] == nullptr )
+      return nullptr;
 
    return data_[index]->template get< T >();
 }
@@ -337,8 +337,8 @@ inline const T* IBlock::getData( const BlockDataID & index ) const {
 
    WALBERLA_ASSERT_LESS( uint_t( index ), data_.size() );
 
-   if( data_[index] == NULL )
-      return NULL;
+   if( data_[index] == nullptr )
+      return nullptr;
 
    return data_[index]->template get< T >();
 }
@@ -368,7 +368,7 @@ inline void IBlock::deleteData( const BlockDataID & index )
 {
    WALBERLA_ASSERT_LESS( uint_t( index ), data_.size() );
    delete data_[index];
-   data_[index] = NULL;
+   data_[index] = nullptr;
 }
 
 
@@ -445,9 +445,9 @@ inline bool IBlock::isDataOfSameType( const ConstBlockDataID & indexA, const Con
 inline void IBlock::addData( const BlockDataID & index, BlockData * const data )
 {
    if( data_.size() <= index )
-      data_.resize( index+1, NULL );
+      data_.resize( index+1, nullptr );
 
-   if( data != NULL ) {
+   if( data != nullptr ) {
       WALBERLA_ASSERT_NULLPTR( data_[index] );
       data_[index] = data;
    }
@@ -465,7 +465,7 @@ inline const T* IBlock::uncheckedFastGetData( const ConstBlockDataID & index ) c
 
    WALBERLA_ASSERT_LESS( uint_t( index ), data_.size() );
 
-   if( data_[index] == NULL )
+   if( data_[index] == nullptr )
       return NULL;
 
    return data_[index]->template uncheckedFastGet< T >();
@@ -485,8 +485,8 @@ inline const T* IBlock::uncheckedFastGetData( const BlockDataID & index ) const
 
    WALBERLA_ASSERT_LESS( uint_t( index ), data_.size() );
 
-   if( data_[index] == NULL )
-      return NULL;
+   if( data_[index] == nullptr )
+      return nullptr;
 
    return data_[index]->template uncheckedFastGet< T >();
 }
diff --git a/src/domain_decomposition/IBlockID.h b/src/domain_decomposition/IBlockID.h
index c30f83fdfb14b91bd270859ecb1de01783db7c23..5e1cb771db8d2b6a9d9116aa7e9019a6e1439ea1 100644
--- a/src/domain_decomposition/IBlockID.h
+++ b/src/domain_decomposition/IBlockID.h
@@ -44,9 +44,9 @@ class IBlockID {
 
 public:
    /// ID type which can be used as a key in a map
-   typedef uint64_t IDType;
+   using IDType = uint64_t;
 
-   virtual ~IBlockID() {}
+   virtual ~IBlockID() = default;
 
    virtual bool operator< ( const IBlockID& rhs ) const = 0;
    virtual bool operator==( const IBlockID& rhs ) const = 0;
diff --git a/src/domain_decomposition/StructuredBlockStorage.h b/src/domain_decomposition/StructuredBlockStorage.h
index 752107f47b4d421719d14898ff365f63141b932d..574634255f49d6f969c82a108c98f0dde7582132 100644
--- a/src/domain_decomposition/StructuredBlockStorage.h
+++ b/src/domain_decomposition/StructuredBlockStorage.h
@@ -121,8 +121,8 @@ public:
       internal::SelectableBlockDataHandlingWrapper dataHandling_;
    };
 
-   typedef BlockStorage::const_iterator const_iterator;
-   typedef BlockStorage::iterator             iterator;
+   using const_iterator = BlockStorage::const_iterator;
+   using iterator = BlockStorage::iterator;
 
    const BlockStorage& getBlockStorage() const { return *blockStorage_; }
          BlockStorage& getBlockStorage()       { return *blockStorage_; }
@@ -426,7 +426,7 @@ protected:
    StructuredBlockStorage( const shared_ptr<BlockStorage> & blockStorage,
                            const std::vector< uint_t > & xCells, const std::vector< uint_t > & yCells, const std::vector< uint_t > & zCells );
 
-   virtual ~StructuredBlockStorage() {} ///< Must not be made public! No one should be allowed to delete a variable of type 'StructuredBlockStorage*'
+   virtual ~StructuredBlockStorage() = default; ///< Must not be made public! No one should be allowed to delete a variable of type 'StructuredBlockStorage*'
 
    void resetCellDecomposition( const std::vector< uint_t > & xCells, const std::vector< uint_t > & yCells, const std::vector< uint_t > & zCells );
 
@@ -437,7 +437,7 @@ protected:
    {
    public:
       CellBoundingBoxHandling( const StructuredBlockStorage & storage ) : storage_( storage ) {}
-      CellInterval * initialize( IBlock * const block ) { return storage_.initializeCellBoundingBox( block ); }
+      CellInterval * initialize( IBlock * const block ) override { return storage_.initializeCellBoundingBox( block ); }
    private:
       const StructuredBlockStorage & storage_;
    };
@@ -693,10 +693,10 @@ inline const IBlock* StructuredBlockStorage::getBlock( const Cell& cell, const u
    getCellCenter( x, y, z, cell, level );
 
    const IBlock* block = blockStorage_->getBlock(x,y,z);
-   if( block == NULL )
-      return NULL;
+   if( block == nullptr )
+      return nullptr;
 
-   return ( getLevel( *block ) == level ) ? block : NULL;
+   return ( getLevel( *block ) == level ) ? block : nullptr;
 }
 
 
@@ -716,10 +716,10 @@ inline IBlock* StructuredBlockStorage::getBlock( const Cell& cell, const uint_t
    getCellCenter( x, y, z, cell, level );
 
    IBlock* block = blockStorage_->getBlock(x,y,z);
-   if( block == NULL )
-      return NULL;
+   if( block == nullptr )
+      return nullptr;
 
-   return ( getLevel( *block ) == level ) ? block : NULL;
+   return ( getLevel( *block ) == level ) ? block : nullptr;
 }
 
 
diff --git a/src/executiontree/ExecutionTree.h b/src/executiontree/ExecutionTree.h
index f9c838ec63cc7ff8b5f01e02d962d3b9c9d6e36f..b9d39d79835894d7d959135c8b8d7a2e1a1d397a 100644
--- a/src/executiontree/ExecutionTree.h
+++ b/src/executiontree/ExecutionTree.h
@@ -107,7 +107,7 @@ std::ostream &operator<<( std::ostream &os, const IFunctionNode &node );
 class IFunctionNode
 {
 public:
-   virtual ~IFunctionNode() {}
+   virtual ~IFunctionNode() = default;
    virtual void operator()() = 0;
    virtual std::string getName() const = 0;
    virtual std::deque< shared_ptr< IFunctionNode > > getChildren() const { return {}; }
@@ -122,7 +122,7 @@ public:
            const std::string &name,
            const TimingTreePtr & timingTree );
 
-   std::string getName() const override { return name_ != "" ? name_ : "Functor"; };
+   std::string getName() const override { return !name_.empty() ? name_ : "Functor"; };
    void operator() () override;
 
 private:
@@ -158,7 +158,7 @@ public:
 
    void push_back( const IFunctionNodePtr &fct ) { children_.push_back( fct ); }
    void push_front( const IFunctionNodePtr &fct ) { children_.push_front( fct ); }
-   std::string getName() const override { return name_ != "" ? name_ : "Sequence"; };
+   std::string getName() const override { return !name_.empty() ? name_ : "Sequence"; };
    std::deque< IFunctionNodePtr > getChildren() const override { return children_; };
 
 private:
diff --git a/src/fft/Fft.h b/src/fft/Fft.h
index 5e4372b1c0510c414e2ddf0545c9e3e9489cb2c3..e3219e46137071497d3697933d01c11ad1c02c38 100644
--- a/src/fft/Fft.h
+++ b/src/fft/Fft.h
@@ -35,7 +35,7 @@ class FourierTransform
       typedef std::unique_ptr<pfft_complex[], std::function<void(pfft_complex*)> > FFTComplex;
       typedef std::unique_ptr<std::remove_pointer<pfft_plan>::type, std::function<void(pfft_plan)> > FFTPlan;
 #else
-      typedef std::unique_ptr<double[], std::function<void(double*)> > FFTReal;
+      using FFTReal = std::unique_ptr<double [], std::function<void (double *)>>;
       typedef std::unique_ptr<fftw_complex[], std::function<void(fftw_complex*)> > FFTComplex;
       typedef std::unique_ptr<std::remove_pointer<fftw_plan>::type, std::function<void(fftw_plan)> > FFTPlan;
 #endif
diff --git a/src/field/AccuracyEvaluation.h b/src/field/AccuracyEvaluation.h
index 036803b431c280d1b611d9e381bd6f7002a9280b..77fd10c4d3d649789ba29fc1b43fb23b332c4b13 100644
--- a/src/field/AccuracyEvaluation.h
+++ b/src/field/AccuracyEvaluation.h
@@ -332,7 +332,7 @@ shared_ptr< AccuracyEvaluation< Field_T, SolutionFunction_T > > makeAccuracyEval
                                                                                         const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                                                                                         const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef AccuracyEvaluation< Field_T, SolutionFunction_T > AE_T;
+   using AE_T = AccuracyEvaluation<Field_T, SolutionFunction_T>;
    return shared_ptr< AE_T >( new AE_T( blocks, fieldId, solution, plotFrequency, logFrequency, requiredSelectors, incompatibleSelectors ) );
 }
 
@@ -345,7 +345,7 @@ makeAccuracyEvaluation( const weak_ptr< StructuredBlockStorage > & blocks,
                         const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                         const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef AccuracyEvaluation< Field_T, SolutionFunction_T, FlagFieldEvaluationFilter<FlagField_T> > AE_T;
+   using AE_T = AccuracyEvaluation<Field_T, SolutionFunction_T, FlagFieldEvaluationFilter<FlagField_T>>;
    return shared_ptr< AE_T >( new AE_T( blocks, fieldId, solution, FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, cellsToEvaluate ),
                                         plotFrequency, logFrequency, requiredSelectors, incompatibleSelectors ) );
 }
@@ -358,7 +358,7 @@ makeAccuracyEvaluation( const weak_ptr< StructuredBlockStorage > & blocks, const
                         const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                         const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef AccuracyEvaluation< Field_T, SolutionFunction_T, Filter_T > AE_T;
+   using AE_T = AccuracyEvaluation<Field_T, SolutionFunction_T, Filter_T>;
    return shared_ptr< AE_T >( new AE_T( blocks, fieldId, solution, filter, plotFrequency, logFrequency, requiredSelectors, incompatibleSelectors ) );
 }
 
@@ -419,7 +419,7 @@ shared_ptr< AccuracyEvaluation< Field_T, SolutionFunction_T > > makeAccuracyEval
                                                                                         const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_ACCURACY_EVALUATION_CONFIG_PARSER( config )
-   typedef AccuracyEvaluation< Field_T, SolutionFunction_T > AE_T;
+   using AE_T = AccuracyEvaluation<Field_T, SolutionFunction_T>;
    auto evaluation = shared_ptr< AE_T >( new AE_T( blocks, fieldId, solution, defaultPlotFrequency, defaultLogFrequency, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_ACCURACY_EVALUATION_SET_AND_RETURN()
 }
@@ -435,7 +435,7 @@ makeAccuracyEvaluation( const Config_T & config,
                         const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_ACCURACY_EVALUATION_CONFIG_PARSER( config )
-   typedef AccuracyEvaluation< Field_T, SolutionFunction_T, FlagFieldEvaluationFilter<FlagField_T> > AE_T;
+   using AE_T = AccuracyEvaluation<Field_T, SolutionFunction_T, FlagFieldEvaluationFilter<FlagField_T>>;
    auto evaluation = shared_ptr< AE_T >( new AE_T( blocks, fieldId, solution, FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, cellsToEvaluate ),
                                                    defaultPlotFrequency, defaultLogFrequency, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_ACCURACY_EVALUATION_SET_AND_RETURN()
@@ -451,7 +451,7 @@ makeAccuracyEvaluation( const Config_T & config,
                         const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_ACCURACY_EVALUATION_CONFIG_PARSER( config )
-   typedef AccuracyEvaluation< Field_T, SolutionFunction_T, Filter_T > AE_T;
+   using AE_T = AccuracyEvaluation<Field_T, SolutionFunction_T, Filter_T>;
    auto evaluation = shared_ptr< AE_T >( new AE_T( blocks, fieldId, solution, filter,
                                                    defaultPlotFrequency, defaultLogFrequency, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_ACCURACY_EVALUATION_SET_AND_RETURN()
diff --git a/src/field/AccuracyEvaluationLinePlot.h b/src/field/AccuracyEvaluationLinePlot.h
index 51a18da5151a90d93179a0ebcf80809840233860..a0c2218609ee7a1d4f3be1ed969e0f01923ca95d 100644
--- a/src/field/AccuracyEvaluationLinePlot.h
+++ b/src/field/AccuracyEvaluationLinePlot.h
@@ -356,7 +356,7 @@ void AccuracyEvaluationLinePlot< Field_T, SolutionFunction_T, Filter_T >::operat
 
    WALBERLA_ROOT_SECTION()
    {
-      typedef typename Field_T::value_type Value_T;
+      using Value_T = typename Field_T::value_type;
       
       std::vector< internal::AccuracyEvaluationPlotData<Value_T> > points;
       while( !buffer.isEmpty() )
@@ -398,7 +398,7 @@ shared_ptr< AccuracyEvaluationLinePlot< Field_T, SolutionFunction_T > > makeAccu
                                                                                                         const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                                                                                                         const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef AccuracyEvaluationLinePlot< Field_T, SolutionFunction_T > AE_T;
+   using AE_T = AccuracyEvaluationLinePlot<Field_T, SolutionFunction_T>;
    return shared_ptr< AE_T >( new AE_T( blocks, fieldId, solution, yAxis, requiredSelectors, incompatibleSelectors ) );
 }
 
@@ -411,7 +411,7 @@ makeAccuracyEvaluationLinePlot( const weak_ptr< StructuredBlockStorage > & block
                                 const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                                 const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef AccuracyEvaluationLinePlot< Field_T, SolutionFunction_T, FlagFieldEvaluationFilter<FlagField_T> > AE_T;
+   using AE_T = AccuracyEvaluationLinePlot<Field_T, SolutionFunction_T, FlagFieldEvaluationFilter<FlagField_T>>;
    return shared_ptr< AE_T >( new AE_T( blocks, fieldId, solution, FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, cellsToEvaluate ),
                                         yAxis, requiredSelectors, incompatibleSelectors ) );
 }
@@ -424,7 +424,7 @@ makeAccuracyEvaluationLinePlot( const weak_ptr< StructuredBlockStorage > & block
                                 const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                                 const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef AccuracyEvaluationLinePlot< Field_T, SolutionFunction_T, Filter_T > AE_T;
+   using AE_T = AccuracyEvaluationLinePlot<Field_T, SolutionFunction_T, Filter_T>;
    return shared_ptr< AE_T >( new AE_T( blocks, fieldId, solution, filter, yAxis, requiredSelectors, incompatibleSelectors ) );
 }
 
@@ -491,7 +491,7 @@ makeAccuracyEvaluationLinePlot( const Config_T & config,
                                 const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_ACCURACY_EVALUATION_LINE_PLOT_CONFIG_PARSER( config )
-   typedef AccuracyEvaluationLinePlot< Field_T, SolutionFunction_T > AE_T;
+   using AE_T = AccuracyEvaluationLinePlot<Field_T, SolutionFunction_T>;
    auto evaluation = shared_ptr< AE_T >( new AE_T( blocks, fieldId, solution, defaultYAxis, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_ACCURACY_EVALUATION_LINE_PLOT_SET_AND_RETURN()
 }
@@ -507,7 +507,7 @@ makeAccuracyEvaluationLinePlot( const Config_T & config,
                                 const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_ACCURACY_EVALUATION_LINE_PLOT_CONFIG_PARSER( config )
-   typedef AccuracyEvaluationLinePlot< Field_T, SolutionFunction_T, FlagFieldEvaluationFilter<FlagField_T> > AE_T;
+   using AE_T = AccuracyEvaluationLinePlot<Field_T, SolutionFunction_T, FlagFieldEvaluationFilter<FlagField_T>>;
    auto evaluation = shared_ptr< AE_T >( new AE_T( blocks, fieldId, solution, FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, cellsToEvaluate ),
                                                    defaultYAxis, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_ACCURACY_EVALUATION_LINE_PLOT_SET_AND_RETURN()
@@ -523,7 +523,7 @@ makeAccuracyEvaluationLinePlot( const Config_T & config,
                                 const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_ACCURACY_EVALUATION_LINE_PLOT_CONFIG_PARSER( config )
-   typedef AccuracyEvaluationLinePlot< Field_T, SolutionFunction_T, Filter_T > AE_T;
+   using AE_T = AccuracyEvaluationLinePlot<Field_T, SolutionFunction_T, Filter_T>;
    auto evaluation = shared_ptr< AE_T >( new AE_T( blocks, fieldId, solution, filter, defaultYAxis, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_ACCURACY_EVALUATION_LINE_PLOT_SET_AND_RETURN()
 }
@@ -642,7 +642,7 @@ makeAccuracyEvaluationLinePlotter( const shared_ptr< AccuracyEvaluationLinePlot_
                                    const std::string & filename = internal::accuracyEvaluationLinePlotterFilename,
                                    const std::string & fileExtension = internal::accuracyEvaluationLinePlotterExtension )
 {
-   typedef AccuracyEvaluationLinePlotter< AccuracyEvaluationLinePlot_T > AE_T;
+   using AE_T = AccuracyEvaluationLinePlotter<AccuracyEvaluationLinePlot_T>;
    return shared_ptr< AE_T >( new AE_T( plot, evaluationFrequency, filename, fileExtension ) );
 }
 
@@ -686,7 +686,7 @@ makeAccuracyEvaluationLinePlotter( const Config_T & config,
    std::string defaultFilename( internal::accuracyEvaluationLinePlotterFilename );
    std::string defaultFileExtension( internal::accuracyEvaluationLinePlotterExtension );
    internal::accuracyEvaluationLinePlotterConfigParser( config, configBlockName, defaultEvaluationFrequency, defaultFilename, defaultFileExtension );
-   typedef AccuracyEvaluationLinePlotter< AccuracyEvaluationLinePlot_T > AE_T;
+   using AE_T = AccuracyEvaluationLinePlotter<AccuracyEvaluationLinePlot_T>;
    return shared_ptr< AE_T >( new AE_T( plot, defaultEvaluationFrequency, defaultFilename, defaultFileExtension ) );
 }
 
diff --git a/src/field/EvaluationFilter.h b/src/field/EvaluationFilter.h
index b4ef168ef4980f02b619f2bc196e021efb482fe5..7c9ac2c1e8fd9c66e778bba5b85a362922bd6cd3 100644
--- a/src/field/EvaluationFilter.h
+++ b/src/field/EvaluationFilter.h
@@ -69,7 +69,7 @@ class FlagFieldEvaluationFilter
 public:
 
    FlagFieldEvaluationFilter( const ConstBlockDataID & flagFieldId, const Set< FlagUID > & cellsToEvaluate ) :
-      flagFieldId_( flagFieldId ), flagField_( NULL ), cellsToEvaluate_( cellsToEvaluate ) {}
+      flagFieldId_( flagFieldId ), flagField_( nullptr ), cellsToEvaluate_( cellsToEvaluate ) {}
 
    void operator()( const IBlock & block )
    {
diff --git a/src/field/Field.h b/src/field/Field.h
index 7b3103b802ea59f7d2350a4526d38bd0634736ea..a47fe47e7314011fe0bae2df37ca090facabb321 100644
--- a/src/field/Field.h
+++ b/src/field/Field.h
@@ -72,24 +72,21 @@ namespace field {
       //** Type Definitions  *******************************************************************************************
       /*! \name Type Definitions */
       //@{
-      typedef T                                    value_type;
+      using value_type = T;
 
-      typedef ForwardFieldIterator<T,fSize_>       iterator;
-      typedef ForwardFieldIterator<const T,fSize_> const_iterator;
+      using iterator = ForwardFieldIterator<T, fSize_>;
+      using const_iterator = ForwardFieldIterator<const T, fSize_>;
 
-      typedef ReverseFieldIterator<T,fSize_>       reverse_iterator;
-      typedef ReverseFieldIterator<const T,fSize_> const_reverse_iterator;
+      using reverse_iterator = ReverseFieldIterator<T, fSize_>;
+      using const_reverse_iterator = ReverseFieldIterator<const T, fSize_>;
 
-      typedef FieldIterator<T,fSize_ >             base_iterator;
-      typedef FieldIterator<const T,fSize_ >       const_base_iterator;
+      using base_iterator = FieldIterator<T, fSize_>;
+      using const_base_iterator = FieldIterator<const T, fSize_>;
 
-      typedef FieldPointer<Field<T,fSize_>, Field<T,fSize_>, T >             Ptr;
-      typedef FieldPointer<Field<T,fSize_>, const Field<T,fSize_>, const T > ConstPtr;
+      using Ptr = FieldPointer<Field<T, fSize_>, Field<T, fSize_>, T>;
+      using ConstPtr = FieldPointer<Field<T, fSize_>, const Field<T, fSize_>, const T>;
 
-      typedef typename std::conditional<VectorTrait<T>::F_SIZE!=0,
-                                        Field<typename VectorTrait<T>::OutputType, VectorTrait<T>::F_SIZE*fSize_>,
-                                        Field<T, fSize_>
-                                        >::type FlattenedField;
+      using FlattenedField = typename std::conditional<VectorTrait<T>::F_SIZE != 0, Field<typename VectorTrait<T>::OutputType, VectorTrait<T>::F_SIZE * fSize_>, Field<T, fSize_>>::type;
 
       static const uint_t F_SIZE = fSize_;
       //@}
@@ -283,8 +280,7 @@ namespace field {
       //** Monitoring  *************************************************************************************************
       /*! \name Monitoring */
       //@{
-      typedef std::function< void ( cell_idx_t x, cell_idx_t y,
-                                      cell_idx_t z, cell_idx_t f, const T & value) > MonitorFunction;
+      using MonitorFunction = std::function<void (cell_idx_t, cell_idx_t, cell_idx_t, cell_idx_t, const T &)>;
 
       void addMonitoringFunction( const MonitorFunction & func );
       //@}
diff --git a/src/field/Field.impl.h b/src/field/Field.impl.h
index 2ae44c77fd0bf6ad639741049d0263d2bea20813..a70bdf813d071293c4c20674ac307a4221db66c5 100644
--- a/src/field/Field.impl.h
+++ b/src/field/Field.impl.h
@@ -45,7 +45,7 @@ namespace field {
     *******************************************************************************************************************/
    template<typename T, uint_t fSize_>
    Field<T,fSize_>::Field( )
-       : values_( NULL ), valuesWithOffset_( NULL ),
+       : values_( nullptr ), valuesWithOffset_( nullptr ),
          xSize_(0), ySize_(0), zSize_(0),
          xAllocSize_(0), yAllocSize_(0), zAllocSize_(0), fAllocSize_(0)
    {
@@ -63,7 +63,7 @@ namespace field {
    template<typename T, uint_t fSize_>
    Field<T,fSize_>::Field( uint_t _xSize, uint_t _ySize, uint_t _zSize, const Layout & l,
                            const shared_ptr<FieldAllocator<T> > &alloc )
-       : values_( NULL ), valuesWithOffset_( NULL )
+       : values_( nullptr ), valuesWithOffset_( nullptr )
    {
       init(_xSize,_ySize,_zSize,l,alloc);
    }
@@ -82,7 +82,7 @@ namespace field {
    template<typename T, uint_t fSize_>
    Field<T,fSize_>::Field( uint_t _xSize, uint_t _ySize, uint_t _zSize, const T & initVal, const Layout & l,
                            const shared_ptr<FieldAllocator<T> > &alloc )
-          : values_( NULL ), valuesWithOffset_( NULL )
+          : values_( nullptr ), valuesWithOffset_( nullptr )
    {
       init(_xSize,_ySize,_zSize,l,alloc);
       set(initVal);
@@ -122,8 +122,8 @@ namespace field {
          return;
 
       allocator_->decrementReferenceCount( values_ );
-      values_ = NULL;
-      valuesWithOffset_ = NULL;
+      values_ = nullptr;
+      valuesWithOffset_ = nullptr;
       init( _xSize, _ySize, _zSize, layout_, allocator_ );
    }
 
@@ -314,23 +314,33 @@ namespace field {
       WALBERLA_ASSERT_NULLPTR( valuesWithOffset_ );
 
       // Automatically select allocator if none was given
-      if ( alloc == 0 )
+      if ( alloc == nullptr )
       {
+#ifdef __BIGGEST_ALIGNMENT__
+         const uint_t alignment = __BIGGEST_ALIGNMENT__;
+#elif defined(__AVX512F__)
+         const uint_t alignment = 64;
+#elif defined(__AVX__)
          const uint_t alignment = 32;
+#elif defined(__SSE__) || defined(_MSC_VER)
+         const uint_t alignment = 16;
+#else
+         const uint_t alignment = 64;
+#endif
 
          // aligned allocator only used (by default) if ...
          if ( l == fzyx                      && // ... we use a structure of arrays layout
               _xSize * sizeof(T) > alignment && // ... the inner coordinate is sufficiently large
               sizeof(T) < alignment          && // ... the stored data type is smaller than the alignment
               alignment % sizeof(T) == 0 )      // ... there is an integer number of elements fitting in one aligned line
-            alloc = make_shared<AllocateAligned<T,32> >();
+            alloc = make_shared<AllocateAligned<T,alignment> >();
          else
             alloc = make_shared<StdFieldAlloc<T> > ();
       }
 
       allocator_ = alloc;
       allocator_->setInnerGhostLayerSize( innerGhostLayerSizeForAlignedAlloc );
-      values_ = 0;
+      values_ = nullptr;
       xSize_ = _xSize;
       ySize_ = _ySize;
       zSize_ = _zSize;
diff --git a/src/field/FlagField.h b/src/field/FlagField.h
index 62f9deef5b9f071923e6c8f4a149f032c4131f6f..a2333770d5b1ae3b9b901b8248f572a41cb63520 100644
--- a/src/field/FlagField.h
+++ b/src/field/FlagField.h
@@ -86,21 +86,21 @@ public:
    //** Type Definitions  **********************************************************************************************
    /*! \name Type Definitions */
    //@{
-   typedef T flag_t;
+   using flag_t = T;
 
-   typedef typename GhostLayerField<T,1>::value_type             value_type;
+   using value_type = typename GhostLayerField<T, 1>::value_type;
 
-   typedef typename GhostLayerField<T,1>::iterator               iterator;
-   typedef typename GhostLayerField<T,1>::const_iterator         const_iterator;
+   using iterator = typename GhostLayerField<T, 1>::iterator;
+   using const_iterator = typename GhostLayerField<T, 1>::const_iterator;
 
-   typedef typename GhostLayerField<T,1>::reverse_iterator       reverse_iterator;
-   typedef typename GhostLayerField<T,1>::const_reverse_iterator const_reverse_iterator;
+   using reverse_iterator = typename GhostLayerField<T, 1>::reverse_iterator;
+   using const_reverse_iterator = typename GhostLayerField<T, 1>::const_reverse_iterator;
 
-   typedef typename Field<T,1>::base_iterator                    base_iterator;
-   typedef typename Field<T,1>::const_base_iterator              const_base_iterator;
+   using base_iterator = typename Field<T, 1>::base_iterator;
+   using const_base_iterator = typename Field<T, 1>::const_base_iterator;
 
-   typedef typename GhostLayerField<T,1>::Ptr                    Ptr;
-   typedef typename GhostLayerField<T,1>::ConstPtr               ConstPtr;
+   using Ptr = typename GhostLayerField<T, 1>::Ptr;
+   using ConstPtr = typename GhostLayerField<T, 1>::ConstPtr;
    //@}
    //*******************************************************************************************************************
 
@@ -111,7 +111,7 @@ public:
 
    FlagField( uint_t xSize, uint_t ySize, uint_t zSize, uint_t gl,
               const shared_ptr<FieldAllocator<T> > &alloc = make_shared<StdFieldAlloc<T> >());
-   virtual ~FlagField();
+   ~FlagField() override;
 
    inline FlagField<T> * clone()              const;
    inline FlagField<T> * cloneUninitialized() const;
@@ -123,7 +123,7 @@ public:
    //** Access Functions ***********************************************************************************************
    /*! \name Access Functions */
    //@{
-   typedef cell_idx_t idx;
+   using idx = cell_idx_t;
 
    void addMask    (idx x, idx y, idx z, flag_t m) { WALBERLA_ASSERT(isRegistered(m)); field::addMask( this->get(x,y,z), m ); }
    void addFlag    (idx x, idx y, idx z, flag_t f) { WALBERLA_ASSERT(isRegistered(f)); field::addFlag( this->get(x,y,z), f ); }
@@ -196,7 +196,7 @@ protected:
    /*! \name Shallow Copy */
    //@{
    FlagField(const FlagField<T> & other);
-   virtual Field<T,1> * cloneShallowCopyInternal()   const;
+   Field<T,1> * cloneShallowCopyInternal()   const override;
    //@}
    //*******************************************************************************************************************
 
diff --git a/src/field/FlagField.impl.h b/src/field/FlagField.impl.h
index bc2c5e950f824021e19f9c829270d476f45ef997..4e32e312cae0e4d23c697ec1f785ff34b4abff29 100644
--- a/src/field/FlagField.impl.h
+++ b/src/field/FlagField.impl.h
@@ -489,7 +489,7 @@ namespace field {
     template<class Sten, typename FieldPtrOrIterator>
     inline bool isFlagInNeighborhood(const FieldPtrOrIterator & i, typename FieldPtrOrIterator::value_type mask)
     {
-       typedef typename std::remove_const< typename FieldPtrOrIterator::value_type >::type T;
+       using T = typename std::remove_const<typename FieldPtrOrIterator::value_type>::type;
 
        static_assert( (std::is_same< T,uint8_t >::value ||
                        std::is_same< T,uint16_t>::value ||
@@ -512,7 +512,7 @@ namespace field {
     inline typename std::remove_const<typename FieldPtrOrIterator::value_type>::type
        getOredNeighborhood(const FieldPtrOrIterator & i)
     {
-       typedef typename std::remove_const<typename FieldPtrOrIterator::value_type>::type RetType;
+       using RetType = typename std::remove_const<typename FieldPtrOrIterator::value_type>::type;
 
        RetType flag = 0;
        for( auto d = Sten::beginNoCenter(); d != Sten::end(); ++d ) {
diff --git a/src/field/FlagUID.h b/src/field/FlagUID.h
index 3e97fac54a7ad5baed78f73b7725e072890063da..116cb21d0f6e9f222822b3559999e18c54d3dca9 100644
--- a/src/field/FlagUID.h
+++ b/src/field/FlagUID.h
@@ -30,7 +30,7 @@ namespace field {
 
 ///\cond internal
 class FlagUIDGenerator : public uid::IndexGenerator< FlagUIDGenerator, uint_t >{};
-typedef UID< FlagUIDGenerator > FlagUID;
+using FlagUID = UID<FlagUIDGenerator>;
 ///\endcond
 
 } // namespace field
diff --git a/src/field/GhostLayerField.h b/src/field/GhostLayerField.h
index 00d3b58d159d9a3252cd777e13317dac3e1816ea..becc54869fa8b4906632ee0c8dab58c8776ecef5 100644
--- a/src/field/GhostLayerField.h
+++ b/src/field/GhostLayerField.h
@@ -53,24 +53,21 @@ namespace field {
       //** Type Definitions  *******************************************************************************************
       /*! \name Type Definitions */
       //@{
-      typedef typename Field<T,fSize_>::value_type             value_type;
+      using value_type = typename Field<T, fSize_>::value_type;
 
-      typedef typename Field<T,fSize_>::iterator               iterator;
-      typedef typename Field<T,fSize_>::const_iterator         const_iterator;
+      using iterator = typename Field<T, fSize_>::iterator;
+      using const_iterator = typename Field<T, fSize_>::const_iterator;
 
-      typedef typename Field<T,fSize_>::reverse_iterator       reverse_iterator;
-      typedef typename Field<T,fSize_>::const_reverse_iterator const_reverse_iterator;
+      using reverse_iterator = typename Field<T, fSize_>::reverse_iterator;
+      using const_reverse_iterator = typename Field<T, fSize_>::const_reverse_iterator;
 
-      typedef typename Field<T,fSize_>::base_iterator          base_iterator;
-      typedef typename Field<T,fSize_>::const_base_iterator    const_base_iterator;
+      using base_iterator = typename Field<T, fSize_>::base_iterator;
+      using const_base_iterator = typename Field<T, fSize_>::const_base_iterator;
 
-      typedef typename Field<T,fSize_>::Ptr                    Ptr;
-      typedef typename Field<T,fSize_>::ConstPtr               ConstPtr;
+      using Ptr = typename Field<T, fSize_>::Ptr;
+      using ConstPtr = typename Field<T, fSize_>::ConstPtr;
 
-      typedef typename std::conditional<VectorTrait<T>::F_SIZE!=0,
-                                        GhostLayerField<typename VectorTrait<T>::OutputType, VectorTrait<T>::F_SIZE*fSize_>,
-                                        GhostLayerField<T, fSize_>
-                                        >::type FlattenedField;
+      using FlattenedField = typename std::conditional<VectorTrait<T>::F_SIZE != 0, GhostLayerField<typename VectorTrait<T>::OutputType, VectorTrait<T>::F_SIZE * fSize_>, GhostLayerField<T, fSize_>>::type;
       //@}
       //****************************************************************************************************************
 
@@ -90,8 +87,7 @@ namespace field {
                        const std::vector<T> & fValues, const Layout & layout = zyxf,
                        const shared_ptr<FieldAllocator<T> > &alloc = shared_ptr<FieldAllocator<T> >() );
 
-      virtual ~GhostLayerField() = default;
-
+      ~GhostLayerField() override = default;
 
 
       void init( uint_t xSizeWithoutGhostLayer,
@@ -102,7 +98,7 @@ namespace field {
                  const shared_ptr<FieldAllocator<T> > &alloc = shared_ptr<FieldAllocator<T> >() );
 
 
-      virtual void resize( uint_t xSize, uint_t ySize, uint_t zSize );
+      void resize( uint_t xSize, uint_t ySize, uint_t zSize ) override;
               void resize( uint_t xSize, uint_t ySize, uint_t zSize, uint_t gl );
 
       using Field<T,fSize_>::resize;
@@ -200,8 +196,8 @@ namespace field {
       /*! \name Slicing */
       //@{
       GhostLayerField<T,fSize_> * getSlicedField( const CellInterval & interval ) const;
-      virtual void slice           ( const CellInterval & interval );
-      virtual void shiftCoordinates( cell_idx_t cx, cell_idx_t cy, cell_idx_t cz );
+      void slice           ( const CellInterval & interval ) override;
+      void shiftCoordinates( cell_idx_t cx, cell_idx_t cy, cell_idx_t cz ) override;
       //@}
       //****************************************************************************************************************
 
@@ -214,8 +210,8 @@ namespace field {
       //** Shallow Copy ************************************************************************************************
       /*! \name Shallow Copy */
       //@{
-      virtual Field<T,fSize_> * cloneShallowCopyInternal()   const;
-      virtual typename Field<T,fSize_>::FlattenedField * flattenedShallowCopyInternal() const;
+      Field<T,fSize_> * cloneShallowCopyInternal()   const override;
+      typename Field<T,fSize_>::FlattenedField * flattenedShallowCopyInternal() const override;
       GhostLayerField(const GhostLayerField<T,fSize_> & other);
       template <typename T2, uint_t fSize2>
       GhostLayerField(const GhostLayerField<T2, fSize2> & other);
diff --git a/src/field/GhostLayerField.impl.h b/src/field/GhostLayerField.impl.h
index 5a8c6cc7ab2566bf02fb78866c34eeef2bc9a602..8e122c36a2e9665c4c3b3b93e78cd066625bdac6 100644
--- a/src/field/GhostLayerField.impl.h
+++ b/src/field/GhostLayerField.impl.h
@@ -369,15 +369,12 @@ namespace field {
    template<typename T, uint_t fSize_>
    bool GhostLayerField<T,fSize_>::isInInnerPart( const Cell & cell ) const
    {
-      if ( cell[0] < 0 ||
+      return !(cell[0] < 0 ||
            cell[1] < 0 ||
            cell[2] < 0 ||
            cell[0] >= cell_idx_c( this->xSize() ) ||
            cell[1] >= cell_idx_c( this->ySize() ) ||
-           cell[2] >= cell_idx_c( this->zSize() ) )
-         return false;
-
-      return true;
+           cell[2] >= cell_idx_c( this->zSize() ));
    }
 
    //*******************************************************************************************************************
diff --git a/src/field/MassEvaluation.h b/src/field/MassEvaluation.h
index a6aebf96f5200af1b5652826144fb83bfd0f47d9..0b9ec26a42962a5d34019ec850539bac9ed88eaf 100644
--- a/src/field/MassEvaluation.h
+++ b/src/field/MassEvaluation.h
@@ -297,7 +297,7 @@ shared_ptr< MassEvaluation< DensityField_T > > makeMassEvaluation( const weak_pt
                                                                    const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                                                                    const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef MassEvaluation< DensityField_T > ME_T;
+   using ME_T = MassEvaluation<DensityField_T>;
    return shared_ptr< ME_T >( new ME_T( blocks, fieldId, plotFrequency, logFrequency, filename, requiredSelectors, incompatibleSelectors ) );
 }
 
@@ -310,7 +310,7 @@ makeMassEvaluation( const weak_ptr< StructuredBlockStorage > & blocks,
                     const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                     const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef MassEvaluation< DensityField_T, FlagFieldEvaluationFilter<FlagField_T> > ME_T;
+   using ME_T = MassEvaluation<DensityField_T, FlagFieldEvaluationFilter<FlagField_T>>;
    return shared_ptr< ME_T >( new ME_T( blocks, fieldId, FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, cellsToEvaluate ),
                                         plotFrequency, logFrequency, filename, requiredSelectors, incompatibleSelectors ) );
 }
@@ -323,7 +323,7 @@ shared_ptr< MassEvaluation< DensityField_T, Filter_T > > makeMassEvaluation( con
                                                                              const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                                                                              const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef MassEvaluation< DensityField_T, Filter_T > ME_T;
+   using ME_T = MassEvaluation<DensityField_T, Filter_T>;
    return shared_ptr< ME_T >( new ME_T( blocks, fieldId, filter, plotFrequency, logFrequency, filename, requiredSelectors, incompatibleSelectors ) );
 }
 
@@ -385,7 +385,7 @@ shared_ptr< MassEvaluation< DensityField_T > > makeMassEvaluation( const Config_
                                                                    const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_MASS_EVALUATION_CONFIG_PARSER( config )
-   typedef MassEvaluation< DensityField_T > ME_T;
+   using ME_T = MassEvaluation<DensityField_T>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, defaultPlotFrequency, defaultLogFrequency, defaultFilename,
                                                    requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_MASS_EVALUATION_SET_AND_RETURN()
@@ -401,7 +401,7 @@ makeMassEvaluation( const Config_T & config,
                     const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_MASS_EVALUATION_CONFIG_PARSER( config )
-   typedef MassEvaluation< DensityField_T, FlagFieldEvaluationFilter<FlagField_T> > ME_T;
+   using ME_T = MassEvaluation<DensityField_T, FlagFieldEvaluationFilter<FlagField_T>>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, cellsToEvaluate ),
                                                    defaultPlotFrequency, defaultLogFrequency, defaultFilename, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_MASS_EVALUATION_SET_AND_RETURN()
@@ -416,7 +416,7 @@ shared_ptr< MassEvaluation< DensityField_T, Filter_T > > makeMassEvaluation( con
                                                                              const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_MASS_EVALUATION_CONFIG_PARSER( config )
-   typedef MassEvaluation< DensityField_T, Filter_T > ME_T;
+   using ME_T = MassEvaluation<DensityField_T, Filter_T>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, filter, defaultPlotFrequency, defaultLogFrequency, defaultFilename,
                                                    requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_MASS_EVALUATION_SET_AND_RETURN()
diff --git a/src/field/StabilityChecker.h b/src/field/StabilityChecker.h
index 8a6e80b281a84abc9889045d72e20ffe5f15b6f7..fbbc530e74564421f2053c3404a6c6352a99938c 100644
--- a/src/field/StabilityChecker.h
+++ b/src/field/StabilityChecker.h
@@ -138,7 +138,7 @@ class StabilityChecker
 {
 private:
 
-   typedef std::map< const IBlock *, std::map< Cell, std::set< cell_idx_t > > > BlockCellsMap;
+   using BlockCellsMap = std::map<const IBlock *, std::map<Cell, std::set<cell_idx_t>>>;
 
    ////////////////////////////////
    //  VTK Output Helper Classes //
@@ -184,9 +184,9 @@ private:
 
    protected:
 
-      void configure() {}
+      void configure() override {}
 
-      uint8_t evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f )
+      uint8_t evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f ) override
       {
          WALBERLA_ASSERT( map_.find( this->block_ ) != map_.end() );
          WALBERLA_ASSERT( map_[ this->block_ ].find( Cell(x,y,z) ) != map_[ this->block_ ].end() );
@@ -205,8 +205,8 @@ private:
    public:
       LocalCoordVTKWriter( const std::string & id ) : vtk::BlockCellDataWriter< cell_idx_t, 3 >( id ) {}
    protected:
-      void configure() {}
-      cell_idx_t evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f )
+      void configure() override {}
+      cell_idx_t evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f ) override
       {
          return ( f == cell_idx_t(0) ) ? x : ( ( f == cell_idx_t(1) ) ? y : z );
       }
@@ -218,8 +218,8 @@ private:
    public:
       GlobalCoordVTKWriter( const std::string & id ) : vtk::BlockCellDataWriter< cell_idx_t, 3 >( id ) {}
    protected:
-      void configure() {}
-      cell_idx_t evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f )
+      void configure() override {}
+      cell_idx_t evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f ) override
       {
          Cell cell(x,y,z);
          this->blockStorage_->transformBlockLocalToGlobalCell( cell, *(this->block_) );
@@ -510,7 +510,7 @@ shared_ptr< StabilityChecker< Field_T > > makeStabilityChecker( const weak_ptr<
                                                                 const Set<SUID> & requiredSelectors     = Set<SUID>::emptySet(),
                                                                 const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef StabilityChecker< Field_T > SC_T;
+   using SC_T = StabilityChecker<Field_T>;
    return shared_ptr< SC_T >( new SC_T( blocks, fieldId, checkFrequency, outputToStream, outputVTK, requiredSelectors, incompatibleSelectors ) );
 }
 
@@ -523,7 +523,7 @@ makeStabilityChecker( const weak_ptr< StructuredBlockStorage > & blocks,
                       const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                       const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef StabilityChecker< Field_T, FlagFieldEvaluationFilter<FlagField_T> > SC_T;
+   using SC_T = StabilityChecker<Field_T, FlagFieldEvaluationFilter<FlagField_T>>;
    return shared_ptr< SC_T >( new SC_T( blocks, fieldId, FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, cellsToEvaluate ),
                                         checkFrequency, outputToStream, outputVTK, requiredSelectors, incompatibleSelectors ) );
 }
@@ -536,7 +536,7 @@ makeStabilityChecker( const weak_ptr< StructuredBlockStorage > & blocks, const C
                       const Set<SUID> & requiredSelectors     = Set<SUID>::emptySet(),
                       const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef StabilityChecker< Field_T, Filter_T > SC_T;
+   using SC_T = StabilityChecker<Field_T, Filter_T>;
    return shared_ptr< SC_T >( new SC_T( blocks, fieldId, filter, checkFrequency, outputToStream, outputVTK, requiredSelectors, incompatibleSelectors ) );
 }
 
@@ -618,7 +618,7 @@ shared_ptr< StabilityChecker< Field_T > > makeStabilityChecker( const Config_T &
                                                                 const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_STABILITY_CHECKER_CONFIG_PARSER( config )
-   typedef StabilityChecker< Field_T > SC_T;
+   using SC_T = StabilityChecker<Field_T>;
    auto checker = shared_ptr< SC_T >( new SC_T( blocks, fieldId, defaultCheckFrequency, defaultOutputToStream, defaultOutputVTK, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_STABILITY_CHECKER_SET_AND_RETURN()
 }
@@ -633,7 +633,7 @@ makeStabilityChecker( const Config_T & config,
                       const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_STABILITY_CHECKER_CONFIG_PARSER( config )
-   typedef StabilityChecker< Field_T, FlagFieldEvaluationFilter<FlagField_T> > SC_T;
+   using SC_T = StabilityChecker<Field_T, FlagFieldEvaluationFilter<FlagField_T>>;
    auto checker = shared_ptr< SC_T >( new SC_T( blocks, fieldId, FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, cellsToEvaluate ),
                                                 defaultCheckFrequency, defaultOutputToStream, defaultOutputVTK, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_STABILITY_CHECKER_SET_AND_RETURN()
@@ -648,7 +648,7 @@ makeStabilityChecker( const Config_T & config,
                       const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_STABILITY_CHECKER_CONFIG_PARSER( config )
-   typedef StabilityChecker< Field_T, Filter_T > SC_T;
+   using SC_T = StabilityChecker<Field_T, Filter_T>;
    auto checker = shared_ptr< SC_T >( new SC_T( blocks, fieldId, filter, defaultCheckFrequency, defaultOutputToStream, defaultOutputVTK,
                                                 requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_STABILITY_CHECKER_SET_AND_RETURN()
diff --git a/src/field/SymmetryCheck.h b/src/field/SymmetryCheck.h
index 8aa914352e17008303e46ec13c45b4b9bfd638f9..ec9237b5460f475ecfedd9215ee6124c5774d2d8 100644
--- a/src/field/SymmetryCheck.h
+++ b/src/field/SymmetryCheck.h
@@ -41,7 +41,7 @@ namespace field {
    */
    //*******************************************************************************************************************
    template< typename Field_T >
-   bool isSymmetric( const Field_T * field, uint_t dimension, uint_t fCoord = 0, Cell * differingCell = NULL )
+   bool isSymmetric( const Field_T * field, uint_t dimension, uint_t fCoord = 0, Cell * differingCell = nullptr )
    {
       WALBERLA_ASSERT_LESS( dimension, 3 );
 
@@ -63,7 +63,7 @@ namespace field {
                auto value2 = field->get( compareCell, fCoord );
                if ( ! math::equal( value1, value2 ) )
                {
-                  if ( differingCell != NULL )
+                  if ( differingCell != nullptr )
                      *differingCell = currentCell;
 
                   return false;
diff --git a/src/field/Traits.h b/src/field/Traits.h
deleted file mode 100644
index bdde7f0c92ef2d1a25afa65a7a9f28d761b41f9d..0000000000000000000000000000000000000000
--- a/src/field/Traits.h
+++ /dev/null
@@ -1,61 +0,0 @@
-
-//======================================================================================================================
-//
-//  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 Traits.h
-//! \ingroup field
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-
-namespace walberla {
-namespace field {
-
-
-
-
-   template<typename Pair>
-   struct ToField
-   {
-       typedef Field< typename Pair::first, Pair::second::value > type;
-   };
-
-   template<typename TupleList>
-   struct ToFieldList
-   {
-      typedef mpl::transform< TupleList, ToField<mpl::_1> >::type type;
-   };
-
-   template<typename Pair>
-   struct ToGhostLayerField
-   {
-      typedef GhostLayerField< typename Pair::first, Pair::second::value > type;
-   };
-
-   template<typename TupleList>
-   struct ToGhostLayerFieldList
-   {
-      typedef mpl::transform< TupleList, ToGhostLayerField<mpl::_1> >::type type;
-   };
-
-
-} // namespace field
-} // namespace walberla
-
-
-
diff --git a/src/field/VolumetricFlowRateEvaluation.h b/src/field/VolumetricFlowRateEvaluation.h
index bbbcc01e0691de2aff1dbb3d86b41690171b2299..063bd1518ed2af8f79635311aa046d51566fc322 100644
--- a/src/field/VolumetricFlowRateEvaluation.h
+++ b/src/field/VolumetricFlowRateEvaluation.h
@@ -48,8 +48,8 @@ namespace field {
 
 namespace internal {
 
-typedef std::function< real_t () >  FlowRateSolution_T;
-typedef std::function< Vector3< real_t > ( const Vector3< real_t > & ) >  FlowRateVelocitySolution_T;
+using FlowRateSolution_T = std::function<real_t ()>;
+using FlowRateVelocitySolution_T = std::function<Vector3<real_t> (const Vector3<real_t> &)>;
 
 const std::string     volumetricFlowRateEvaluationFilename("flowrate.dat");
 const real_t          volumetricFlowRateEvaluationNormalization( real_t(1) );
@@ -150,8 +150,8 @@ class VolumetricFlowRateEvaluation
 {
 public:
 
-   typedef internal::FlowRateSolution_T          Solution_T;
-   typedef internal::FlowRateVelocitySolution_T  VelocitySolution_T;
+   using Solution_T = internal::FlowRateSolution_T;
+   using VelocitySolution_T = internal::FlowRateVelocitySolution_T;
 
    VolumetricFlowRateEvaluation( const weak_ptr< StructuredBlockStorage > & blocks, const ConstBlockDataID & fieldId,
                                  const Filter_T & filter,
@@ -491,7 +491,7 @@ shared_ptr< VolumetricFlowRateEvaluation< VelocityField_T > > makeVolumetricFlow
                                                                                                 const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                                                                                                 const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef VolumetricFlowRateEvaluation< VelocityField_T > FR_T;
+   using FR_T = VolumetricFlowRateEvaluation<VelocityField_T>;
    return shared_ptr< FR_T >( new FR_T( blocks, velocityFieldId, plotFrequency, logFrequency, solution, velocitySolution, requiredSelectors, incompatibleSelectors ) );
 }
 
@@ -505,7 +505,7 @@ makeVolumetricFlowRateEvaluation( const weak_ptr< StructuredBlockStorage > & blo
                                   const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                                   const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef VolumetricFlowRateEvaluation< VelocityField_T, FlagFieldEvaluationFilter<FlagField_T> > FR_T;
+   using FR_T = VolumetricFlowRateEvaluation<VelocityField_T, FlagFieldEvaluationFilter<FlagField_T>>;
    return shared_ptr< FR_T >( new FR_T( blocks, velocityFieldId, FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, cellsToEvaluate ),
                                         plotFrequency, logFrequency, solution, velocitySolution, requiredSelectors, incompatibleSelectors ) );
 }
@@ -519,7 +519,7 @@ makeVolumetricFlowRateEvaluation( const weak_ptr< StructuredBlockStorage > & blo
                                   const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                                   const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef VolumetricFlowRateEvaluation< VelocityField_T, Filter_T > FR_T;
+   using FR_T = VolumetricFlowRateEvaluation<VelocityField_T, Filter_T>;
    return shared_ptr< FR_T >( new FR_T( blocks, velocityFieldId, filter, plotFrequency, logFrequency, solution, velocitySolution, requiredSelectors, incompatibleSelectors ) );
 }
 
@@ -596,7 +596,7 @@ shared_ptr< VolumetricFlowRateEvaluation< VelocityField_T > > makeVolumetricFlow
                                                                                                 const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_VOLUMETRIC_FLOW_RATE_EVALUATION_CONFIG_PARSER( config )
-   typedef VolumetricFlowRateEvaluation< VelocityField_T > FR_T;
+   using FR_T = VolumetricFlowRateEvaluation<VelocityField_T>;
    auto evaluation = shared_ptr< FR_T >( new FR_T( blocks, velocityFieldId, defaultPlotFrequency, defaultLogFrequency,
                                                    solution, velocitySolution, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_VOLUMETRIC_FLOW_RATE_EVALUATION_SET_AND_RETURN()
@@ -614,7 +614,7 @@ makeVolumetricFlowRateEvaluation( const Config_T & config,
                                   const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_VOLUMETRIC_FLOW_RATE_EVALUATION_CONFIG_PARSER( config )
-   typedef VolumetricFlowRateEvaluation< VelocityField_T, FlagFieldEvaluationFilter<FlagField_T> > FR_T;
+   using FR_T = VolumetricFlowRateEvaluation<VelocityField_T, FlagFieldEvaluationFilter<FlagField_T>>;
    auto evaluation = shared_ptr< FR_T >( new FR_T( blocks, velocityFieldId, FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, cellsToEvaluate ),
                                                    defaultPlotFrequency, defaultLogFrequency, solution, velocitySolution, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_VOLUMETRIC_FLOW_RATE_EVALUATION_SET_AND_RETURN()
@@ -632,7 +632,7 @@ makeVolumetricFlowRateEvaluation( const Config_T & config,
                                   const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_FIELD_MAKE_VOLUMETRIC_FLOW_RATE_EVALUATION_CONFIG_PARSER( config )
-   typedef VolumetricFlowRateEvaluation< VelocityField_T, Filter_T > FR_T;
+   using FR_T = VolumetricFlowRateEvaluation<VelocityField_T, Filter_T>;
    auto evaluation = shared_ptr< FR_T >( new FR_T( blocks, velocityFieldId, filter, defaultPlotFrequency, defaultLogFrequency,
                                                    solution, velocitySolution, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_VOLUMETRIC_FLOW_RATE_EVALUATION_SET_AND_RETURN()
diff --git a/src/field/adaptors/AdaptorCreators.h b/src/field/adaptors/AdaptorCreators.h
index 1c410ca91246600c385f5ba0a6691b7a67474462..b6907b2c20b761516d8529819b19e038eef0cd72 100644
--- a/src/field/adaptors/AdaptorCreators.h
+++ b/src/field/adaptors/AdaptorCreators.h
@@ -38,14 +38,14 @@ class AdaptorHandling : public blockforest::AlwaysInitializeBlockDataHandling< A
 {
 public:
 
-   typedef typename Adaptor_T::functor_t AdaptionFunction_T;
+   using AdaptionFunction_T = typename Adaptor_T::functor_t;
 
    AdaptorHandling( const ConstBlockDataID & adaptedFieldId, const AdaptionFunction_T & function ) :
       adaptedFieldId_( adaptedFieldId ), function_( function ) {}
 
-   Adaptor_T * initialize( IBlock * const block )
+   Adaptor_T * initialize( IBlock * const block ) override
    {
-      typedef typename Adaptor_T::basefield_t AdaptedField_T;
+      using AdaptedField_T = typename Adaptor_T::basefield_t;
       const AdaptedField_T * adaptedField = block->getData< AdaptedField_T >( adaptedFieldId_ );
       return new Adaptor_T( *adaptedField, function_ );
    }
diff --git a/src/field/adaptors/AdaptorIterator.h b/src/field/adaptors/AdaptorIterator.h
index b9504ef310f73be92f3b91acb6aee16ded907c97..e2da36e2e3a0545c85bdd4397f597445f3d32458 100644
--- a/src/field/adaptors/AdaptorIterator.h
+++ b/src/field/adaptors/AdaptorIterator.h
@@ -43,9 +43,9 @@ template < typename AdaptedIterator, typename FieldAdaptor>
 class AdaptorIterator
 {
 public:
-   typedef typename FieldAdaptor::value_type T;
-   typedef typename FieldAdaptor::functor_t  Functor;
-   typedef AdaptorIterator<AdaptedIterator, FieldAdaptor > OwnType;
+   using T = typename FieldAdaptor::value_type;
+   using Functor = typename FieldAdaptor::functor_t;
+   using OwnType = AdaptorIterator<AdaptedIterator, FieldAdaptor>;
 
    AdaptorIterator( const AdaptedIterator & baseIterator, const FieldAdaptor * adaptedField )
       : base_( baseIterator ), adaptedField_( adaptedField), functor_( adaptedField->getFunctor() )
diff --git a/src/field/adaptors/ComponentExtractionAdaptor.h b/src/field/adaptors/ComponentExtractionAdaptor.h
index bfd60854c1e38885fbfcec755f74ef3c70e3bec5..12451fcc647b6259e800116f093cb3d86266229e 100644
--- a/src/field/adaptors/ComponentExtractionAdaptor.h
+++ b/src/field/adaptors/ComponentExtractionAdaptor.h
@@ -32,12 +32,12 @@ namespace field {
    class ComponentExtractionFunction
    {
    public:
-      typedef          Field_T                      basefield_t;
-      typedef typename Field_T::const_base_iterator basefield_iterator;
+      using basefield_t = Field_T;
+      using basefield_iterator = typename Field_T::const_base_iterator;
 
-      typedef VectorTrait<typename basefield_t::value_type > OutputTrait;
+      using OutputTrait = VectorTrait<typename basefield_t::value_type>;
 
-      typedef typename OutputTrait::OutputType          value_type;
+      using value_type = typename OutputTrait::OutputType;
 
       static const uint_t F_SIZE = 1;
 
@@ -61,8 +61,8 @@ namespace field {
    class ComponentExtractionAdaptor : public GhostLayerFieldAdaptor< ComponentExtractionFunction<field_t,component,vectorComponent>, 0 >
    {
    public:
-      typedef ComponentExtractionFunction<field_t,component,vectorComponent> Func;
-      typedef GhostLayerFieldAdaptor< Func, 0 > baseclass;
+      using Func = ComponentExtractionFunction<field_t, component, vectorComponent>;
+      using baseclass = GhostLayerFieldAdaptor<Func, 0>;
 
       ComponentExtractionAdaptor( const field_t & field, const Func & func = Func() )
          : baseclass( field, func)
diff --git a/src/field/adaptors/GhostLayerFieldAdaptor.h b/src/field/adaptors/GhostLayerFieldAdaptor.h
index f26f87b0ab8d55a7c0d6426ab3e61f06e9b3eda7..13ac8f0e289b6bf5300ac70647816a44dbb034f1 100644
--- a/src/field/adaptors/GhostLayerFieldAdaptor.h
+++ b/src/field/adaptors/GhostLayerFieldAdaptor.h
@@ -85,23 +85,23 @@ public:
    //** Type Definitions  **********************************************************************************************
    /*! \name Type Definitions */
    //@{
-   typedef Functor                       functor_t;
-   typedef typename Functor::basefield_t basefield_t;
-   typedef typename Functor::value_type  value_type;
-   typedef typename Functor::value_type  T;
+   using functor_t = Functor;
+   using basefield_t = typename Functor::basefield_t;
+   using value_type = typename Functor::value_type;
+   using T = typename Functor::value_type;
 
    static const uint_t F_SIZE = Functor::F_SIZE;
 
-   typedef typename basefield_t::const_base_iterator     adapted_base_iterator;
-   typedef typename basefield_t::const_iterator          adapted_iterator;
-   typedef typename basefield_t::const_reverse_iterator  adapted_reverse_iterator;
+   using adapted_base_iterator = typename basefield_t::const_base_iterator;
+   using adapted_iterator = typename basefield_t::const_iterator;
+   using adapted_reverse_iterator = typename basefield_t::const_reverse_iterator;
 
-   typedef GhostLayerFieldAdaptor<Functor,glDecrease> OwnType;
-   typedef AdaptorIterator<adapted_base_iterator,   OwnType > const_base_iterator;
-   typedef AdaptorIterator<adapted_iterator,        OwnType > const_iterator;
-   typedef AdaptorIterator<adapted_reverse_iterator,OwnType > const_reverse_iterator;
+   using OwnType = GhostLayerFieldAdaptor<Functor, glDecrease>;
+   using const_base_iterator = AdaptorIterator<adapted_base_iterator, OwnType>;
+   using const_iterator = AdaptorIterator<adapted_iterator, OwnType>;
+   using const_reverse_iterator = AdaptorIterator<adapted_reverse_iterator, OwnType>;
 
-   typedef FieldPointer<OwnType, const OwnType, const T > ConstPtr;
+   using ConstPtr = FieldPointer<OwnType, const OwnType, const T>;
    //@}
    //*******************************************************************************************************************
 
@@ -112,7 +112,7 @@ public:
 
    // since Field/GhostLayerField is polymorphic, this class also has to be polymorphic
    // example: dynamic_cast< field_t > would fail if field_t = GhostLayerFieldAdaptor
-   virtual ~GhostLayerFieldAdaptor() {}
+   virtual ~GhostLayerFieldAdaptor() = default;
 
 
    const functor_t   & getFunctor()     const { return functor_; }
diff --git a/src/field/adaptors/VectorFieldAccessor.h b/src/field/adaptors/VectorFieldAccessor.h
index e41f4684a13d264d3d193599e6b8f01b11b9549c..2ba6ca1a28969687caab67540456b5c615d7cbab 100644
--- a/src/field/adaptors/VectorFieldAccessor.h
+++ b/src/field/adaptors/VectorFieldAccessor.h
@@ -36,7 +36,7 @@ namespace field {
       static_assert( VectorField_T::F_SIZE == 3, "Only valid for Fields with 3 components (F_SIZE==3)" );
       static_assert( std::is_same< typename VectorField_T::value_type, real_t >::value, "Only works for real valued fields" );
 
-      typedef Vector3<real_t> vector_or_constRefVector;
+      using vector_or_constRefVector = Vector3<real_t>;
 
       static vector_or_constRefVector get( const VectorField_T * f, cell_idx_t x, cell_idx_t y, cell_idx_t z )
       {
@@ -56,7 +56,7 @@ namespace field {
                               typename std::enable_if< std::is_same< typename VectorField_T::value_type,
                                                                            Vector3<real_t> >::value >::type >
    {
-       typedef const Vector3<real_t> & vector_or_constRefVector;
+       using vector_or_constRefVector = const Vector3<real_t> &;
 
        static vector_or_constRefVector get( const VectorField_T * f, cell_idx_t x, cell_idx_t y, cell_idx_t z )
        {
diff --git a/src/field/allocation/FieldAllocator.h b/src/field/allocation/FieldAllocator.h
index f78771e15b2d1df7e0b61a7c2d9f29d44e7a9add..781cda4bc2b8f8547f94473b561d475576f1bb2d 100644
--- a/src/field/allocation/FieldAllocator.h
+++ b/src/field/allocation/FieldAllocator.h
@@ -238,8 +238,8 @@ namespace field {
 
       protected:
 
-         virtual T * allocateMemory (  uint_t size0, uint_t size1, uint_t size2, uint_t size3,
-                                       uint_t & allocSize1, uint_t & allocSize2, uint_t & allocSize3)
+         T * allocateMemory (  uint_t size0, uint_t size1, uint_t size2, uint_t size3,
+                                       uint_t & allocSize1, uint_t & allocSize2, uint_t & allocSize3) override
          {
             allocSize1=size1;
             allocSize2=size2;
@@ -254,7 +254,7 @@ namespace field {
             return allocateMemory ( size0 * allocSize1 * allocSize2 * allocSize3 );
          }
 
-         virtual T * allocateMemory (  uint_t size )
+         T * allocateMemory (  uint_t size ) override
          {
             void * ptr = aligned_malloc_with_offset(size * sizeof(T) + alignment, alignment, offset_ % alignment );
             if(!ptr)
@@ -274,11 +274,11 @@ namespace field {
             return ret;
          }
 
-         virtual void setInnerGhostLayerSize( uint_t innerGhostLayerSize ) {
+         void setInnerGhostLayerSize( uint_t innerGhostLayerSize ) override {
             offset_ = sizeof(T) * innerGhostLayerSize;
          }
 
-         virtual void deallocate(T *& values )
+         void deallocate(T *& values ) override
          {
             WALBERLA_ASSERT ( nrOfElements_.find(values) != nrOfElements_.end() );
 
@@ -328,8 +328,8 @@ namespace field {
    class StdFieldAlloc : public FieldAllocator<T>
    {
       public:
-         virtual T * allocateMemory (  uint_t size0, uint_t size1, uint_t size2, uint_t size3,
-                                       uint_t & allocSize1, uint_t & allocSize2, uint_t & allocSize3)
+         T * allocateMemory (  uint_t size0, uint_t size1, uint_t size2, uint_t size3,
+                                       uint_t & allocSize1, uint_t & allocSize2, uint_t & allocSize3) override
          {
             allocSize1=size1;
             allocSize2=size2;
@@ -337,14 +337,14 @@ namespace field {
             return new T[size0*size1*size2*size3];
          }
 
-         virtual T * allocateMemory ( uint_t size )
+         T * allocateMemory ( uint_t size ) override
          {
             return new T[size];
          }
 
-         virtual void deallocate(T *& values) {
+         void deallocate(T *& values) override {
             delete[] values;
-            values = 0;
+            values = nullptr;
          }
    };
 
diff --git a/src/field/blockforest/BlockDataHandling.h b/src/field/blockforest/BlockDataHandling.h
index 98252f6c076dbb17e3140238dbc12e4298766b70..2939352343ca8b3b6d3ed2b98bfa111f9b9e75f5 100644
--- a/src/field/blockforest/BlockDataHandling.h
+++ b/src/field/blockforest/BlockDataHandling.h
@@ -42,14 +42,14 @@ class BlockDataHandling : public blockforest::BlockDataHandling< Field_T >
 {
 public:
 
-   typedef typename Field_T::value_type Value_T;
-   typedef std::function< void ( Field_T * field, IBlock * const block ) > InitializationFunction_T;
+   using Value_T = typename Field_T::value_type;
+   using InitializationFunction_T = std::function<void (Field_T *, IBlock *const)>;
 
-   virtual ~BlockDataHandling() = default;
+   ~BlockDataHandling() override = default;
 
    void addInitializationFunction( const InitializationFunction_T & initFunction ) { initFunction_ = initFunction; }
 
-   Field_T * initialize( IBlock * const block )
+   Field_T * initialize( IBlock * const block ) override
    {
       Field_T * field = allocate( block );
       
@@ -59,20 +59,20 @@ public:
       return field;
    }
 
-   inline void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer );
+   inline void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override;
 
-   void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child );
-   void serializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer );
+   void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child ) override;
+   void serializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override;
 
-   Field_T * deserialize( IBlock * const block ) { return reallocate( block ); }
+   Field_T * deserialize( IBlock * const block ) override { return reallocate( block ); }
 
-   Field_T * deserializeCoarseToFine( Block * const block ) { return reallocate( block ); }
-   Field_T * deserializeFineToCoarse( Block * const block ) { return reallocate( block ); }   
+   Field_T * deserializeCoarseToFine( Block * const block ) override { return reallocate( block ); }
+   Field_T * deserializeFineToCoarse( Block * const block ) override { return reallocate( block ); }   
    
-   void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer );
+   void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override;
 
-   void deserializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer );
-   void deserializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer, const uint_t child );
+   void deserializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override;
+   void deserializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer, const uint_t child ) override;
 
 protected:
 
@@ -398,7 +398,7 @@ class DefaultBlockDataHandling : public BlockDataHandling< GhostLayerField_T >
 {
 public:
 
-   typedef typename GhostLayerField_T::value_type Value_T;
+   using Value_T = typename GhostLayerField_T::value_type;
 
    DefaultBlockDataHandling( const weak_ptr< StructuredBlockStorage > & blocks,
                              const std::function< Vector3< uint_t > ( const shared_ptr< StructuredBlockStorage > &, IBlock * const ) > calculateSize = internal::defaultSize ) :
@@ -421,7 +421,7 @@ public:
 
 protected:
 
-   GhostLayerField_T * allocate( IBlock * const block )
+   GhostLayerField_T * allocate( IBlock * const block ) override
    {
       auto blocks = blocks_.lock();
       WALBERLA_CHECK_NOT_NULLPTR( blocks, "Trying to access 'DefaultBlockDataHandling' for a block storage object that doesn't exist anymore" );
@@ -430,7 +430,7 @@ protected:
                                                       nrOfGhostLayers_, initValue_, layout_ );
    }
 
-   GhostLayerField_T * reallocate( IBlock * const block )
+   GhostLayerField_T * reallocate( IBlock * const block ) override
    {
       auto blocks = blocks_.lock();
       WALBERLA_CHECK_NOT_NULLPTR( blocks, "Trying to access 'DefaultBlockDataHandling' for a block storage object that doesn't exist anymore" );
@@ -460,8 +460,8 @@ class AlwaysInitializeBlockDataHandling : public blockforest::AlwaysInitializeBl
 {
 public:
 
-   typedef typename GhostLayerField_T::value_type Value_T;
-   typedef std::function< void ( GhostLayerField_T * field, IBlock * const block ) > InitializationFunction_T;
+   using Value_T = typename GhostLayerField_T::value_type;
+   using InitializationFunction_T = std::function<void (GhostLayerField_T *, IBlock *const)>;
 
    AlwaysInitializeBlockDataHandling( const weak_ptr< StructuredBlockStorage > & blocks,
                                       const std::function< Vector3< uint_t > ( const shared_ptr< StructuredBlockStorage > &, IBlock * const ) > calculateSize = internal::defaultSize ) :
@@ -484,7 +484,7 @@ public:
 
    void addInitializationFunction( const InitializationFunction_T & initFunction ) { initFunction_ = initFunction; }
 
-   GhostLayerField_T * initialize( IBlock * const block )
+   GhostLayerField_T * initialize( IBlock * const block ) override
    {
       auto blocks = blocks_.lock();
       WALBERLA_CHECK_NOT_NULLPTR( blocks, "Trying to access 'AlwaysInitializeBlockDataHandling' for a block storage object that doesn't exist anymore" );
@@ -524,7 +524,7 @@ public:
       fieldToClone_( fieldToClone )
    {}
 
-   Field_T * initialize( IBlock * const block )
+   Field_T * initialize( IBlock * const block ) override
    {
       const Field_T * toClone = block->template getData< Field_T >( fieldToClone_ );
       return toClone->clone();
@@ -550,7 +550,7 @@ public:
       fieldToClone_( fieldToClone )
    {}
 
-   typename Field_T::FlattenedField * initialize( IBlock * const block )
+   typename Field_T::FlattenedField * initialize( IBlock * const block ) override
    {
       const Field_T * toClone = block->template getData< Field_T >( fieldToClone_ );
       return toClone->flattenedShallowCopy();
diff --git a/src/field/blockforest/GradientRefinement.h b/src/field/blockforest/GradientRefinement.h
index b05b8d1dfa50400bee199f8703c4772a28afde0e..fb7f8b46634d0e7d2467ab95ca63538fb5b4ed42 100644
--- a/src/field/blockforest/GradientRefinement.h
+++ b/src/field/blockforest/GradientRefinement.h
@@ -81,7 +81,7 @@ void GradientRefinement< VectorField_T, Filter_T, Pseudo2D >::operator()( std::v
       const Block * const block = it->first;
       const VectorField_T * u = block->template getData< VectorField_T >( fieldId_ );
 
-      if( u == NULL )
+      if( u == nullptr )
       {
          it->second = uint_t(0);
          continue;
diff --git a/src/field/communication/MPIDatatypes.impl.h b/src/field/communication/MPIDatatypes.impl.h
index 1bb4bf3a6bf5c8ff35d335dc20d2365c04de931e..a21ac75bd6a150fb14bae781bd3cdb5d9a66f22d 100644
--- a/src/field/communication/MPIDatatypes.impl.h
+++ b/src/field/communication/MPIDatatypes.impl.h
@@ -33,7 +33,7 @@ MPI_Datatype mpiDatatypeSlice( const Field_T & field,
                                const cell_idx_t xBeg, const cell_idx_t yBeg, const cell_idx_t zBeg, const cell_idx_t fBeg,
                                const cell_idx_t xEnd, const cell_idx_t yEnd, const cell_idx_t zEnd, const cell_idx_t fEnd )
 {
-   typedef typename Field_T::value_type T;
+   using T = typename Field_T::value_type;
    int sizes[4];
    int subsizes[4];
    int starts[4];
@@ -133,7 +133,7 @@ MPI_Datatype mpiDatatypeSliceXYZ( const Field_T & field, const CellInterval & in
 template<typename Field_T>
 MPI_Datatype mpiDatatypeSliceXYZ( const Field_T & field, const CellInterval & interval, const std::set<cell_idx_t> & fs )
 {
-   typedef typename Field_T::value_type T;
+   using T = typename Field_T::value_type;
 
    MPI_Datatype newType = MPI_DATATYPE_NULL;
 
diff --git a/src/field/communication/PackInfo.h b/src/field/communication/PackInfo.h
index 0c74f604f40483ce3ec60e6eaeee090ebaf2b323..108860eb3af81bf8c6fa74786fadd58b3b7d2150 100644
--- a/src/field/communication/PackInfo.h
+++ b/src/field/communication/PackInfo.h
@@ -42,7 +42,7 @@ template<typename GhostLayerField_T>
 class PackInfo : public walberla::communication::UniformPackInfo
 {
 public:
-   typedef typename GhostLayerField_T::value_type T;
+   using T = typename GhostLayerField_T::value_type;
 
    PackInfo( const BlockDataID & bdId ) : bdId_( bdId ), communicateAllGhostLayers_( true ),
                                           numberOfGhostLayers_( 0 ) {}
@@ -50,17 +50,17 @@ public:
    PackInfo( const BlockDataID & bdId, const uint_t numberOfGhostLayers ) : bdId_( bdId ),
       communicateAllGhostLayers_( false ), numberOfGhostLayers_(  numberOfGhostLayers ) {}
 
-   virtual ~PackInfo() {}
+   ~PackInfo() override = default;
 
-   bool constantDataExchange() const { return mpi::BufferSizeTrait<T>::constantSize; }
-   bool threadsafeReceiving()  const { return true; }
+   bool constantDataExchange() const override { return mpi::BufferSizeTrait<T>::constantSize; }
+   bool threadsafeReceiving()  const override { return true; }
 
-   void unpackData(IBlock * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer);
+   void unpackData(IBlock * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer) override;
 
-   void communicateLocal(const IBlock * sender, IBlock * receiver, stencil::Direction dir);
+   void communicateLocal(const IBlock * sender, IBlock * receiver, stencil::Direction dir) override;
 
 protected:
-   void packDataImpl(const IBlock * sender, stencil::Direction dir, mpi::SendBuffer & outBuffer) const;
+   void packDataImpl(const IBlock * sender, stencil::Direction dir, mpi::SendBuffer & outBuffer) const override;
 
    uint_t numberOfGhostLayersToCommunicate( const GhostLayerField_T * const field ) const;
 
diff --git a/src/field/communication/ReducePackInfo.h b/src/field/communication/ReducePackInfo.h
index 81a1426127275c3b186972b715a8f04cd4841b7d..532b437246bedb88e2ba13f80605432888f08d44 100644
--- a/src/field/communication/ReducePackInfo.h
+++ b/src/field/communication/ReducePackInfo.h
@@ -42,7 +42,7 @@ template< template<typename> class ReduceOperation, typename GhostLayerField_T >
 class ReducePackInfo : public walberla::communication::ReducePackInfo
 {
 public:
-   typedef typename GhostLayerField_T::value_type T;
+   using T = typename GhostLayerField_T::value_type;
 
    ReducePackInfo( const BlockDataID & bdId, T init )
         : bdId_(bdId),
@@ -50,19 +50,19 @@ public:
           op_()
    {}
 
-   virtual ~ReducePackInfo() {}
+   ~ReducePackInfo() override = default;
 
    bool constantDataExchange() const { return false; }
    bool threadsafeReceiving()  const { return false; }
 
-   virtual void safeCommunicateLocal( const IBlock * sender, IBlock * receiver, stencil::Direction dir );
-   virtual void packData            ( const IBlock * sender,   stencil::Direction dir, mpi::SendBuffer & outBuffer );
-   virtual void safeUnpackData      (       IBlock * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer    );
+   void safeCommunicateLocal( const IBlock * sender, IBlock * receiver, stencil::Direction dir ) override;
+   void packData            ( const IBlock * sender,   stencil::Direction dir, mpi::SendBuffer & outBuffer ) override;
+   void safeUnpackData      (       IBlock * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer    ) override;
 
    size_t getDataSize() const { return getSize() * sizeof(T); }
 
 protected:
-   virtual size_t initData( IBlock * receiver, stencil::Direction dir );
+   size_t initData( IBlock * receiver, stencil::Direction dir ) override;
 
    const BlockDataID        bdId_;
    const T                  init_;
diff --git a/src/field/communication/StencilRestrictedMPIDatatypeInfo.h b/src/field/communication/StencilRestrictedMPIDatatypeInfo.h
index 1150c76ff004fd3b5e3d932737708969da1d8b1b..64421ee77b26bbb15a7125854462f315acfabbd8 100644
--- a/src/field/communication/StencilRestrictedMPIDatatypeInfo.h
+++ b/src/field/communication/StencilRestrictedMPIDatatypeInfo.h
@@ -39,26 +39,26 @@ class StencilRestrictedMPIDatatypeInfo : public walberla::communication::Uniform
 public:
     StencilRestrictedMPIDatatypeInfo( BlockDataID fieldID ) : fieldID_( fieldID ) {}
 
-    virtual ~StencilRestrictedMPIDatatypeInfo() {}
+    ~StencilRestrictedMPIDatatypeInfo() override = default;
 
-    virtual shared_ptr<mpi::Datatype> getSendDatatype ( IBlock * block, const stencil::Direction dir )
+    shared_ptr<mpi::Datatype> getSendDatatype ( IBlock * block, const stencil::Direction dir ) override
     {
         return make_shared<mpi::Datatype>( field::communication::mpiDatatypeSliceBeforeGhostlayerXYZ(
                 *getField( block ), dir, uint_t( 1 ), getOptimizedCommunicationIndices( dir ), false ) );
     }
 
-    virtual shared_ptr<mpi::Datatype> getRecvDatatype ( IBlock * block, const stencil::Direction dir )
+    shared_ptr<mpi::Datatype> getRecvDatatype ( IBlock * block, const stencil::Direction dir ) override
     {
         return make_shared<mpi::Datatype>( field::communication::mpiDatatypeGhostLayerOnlyXYZ(
                 *getField( block ), dir, false, getOptimizedCommunicationIndices( stencil::inverseDir[dir] ) ) );
     }
 
-    virtual void * getSendPointer( IBlock * block, const stencil::Direction )
+    void * getSendPointer( IBlock * block, const stencil::Direction ) override
     {
         return getField(block)->data();
     }
 
-    virtual void * getRecvPointer( IBlock * block, const stencil::Direction )
+    void * getRecvPointer( IBlock * block, const stencil::Direction ) override
     {
         return getField(block)->data();
     }
diff --git a/src/field/communication/StencilRestrictedPackInfo.h b/src/field/communication/StencilRestrictedPackInfo.h
index 72e40584519dde9d15729fb15067ec8278930fc5..45527cadf5e11b66f0a70b7c88127af11cd6498a 100644
--- a/src/field/communication/StencilRestrictedPackInfo.h
+++ b/src/field/communication/StencilRestrictedPackInfo.h
@@ -47,18 +47,18 @@ class StencilRestrictedPackInfo : public walberla::communication::UniformPackInf
 {
 public:
    StencilRestrictedPackInfo( const BlockDataID & fieldId ) : fieldId_( fieldId ) {}
-   virtual ~StencilRestrictedPackInfo() {}
+   ~StencilRestrictedPackInfo() override = default;
 
-   bool constantDataExchange() const { return true; }
-   bool threadsafeReceiving()  const { return true; }
+   bool constantDataExchange() const override { return true; }
+   bool threadsafeReceiving()  const override { return true; }
 
-   void unpackData( IBlock * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer );
+   void unpackData( IBlock * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer ) override;
 
-   void communicateLocal( const IBlock * sender, IBlock * receiver, stencil::Direction dir );
+   void communicateLocal( const IBlock * sender, IBlock * receiver, stencil::Direction dir ) override;
 
 protected:
 
-   void packDataImpl( const IBlock * sender, stencil::Direction dir, mpi::SendBuffer & outBuffer ) const;
+   void packDataImpl( const IBlock * sender, stencil::Direction dir, mpi::SendBuffer & outBuffer ) const override;
 
    const BlockDataID fieldId_;
 
diff --git a/src/field/communication/UniformMPIDatatypeInfo.h b/src/field/communication/UniformMPIDatatypeInfo.h
index 4734344c1fb680b1d5f7fb50b73d3eae1bd1c2f7..e7042ab57395e3954b98cd8a2ae3dca66055bab8 100644
--- a/src/field/communication/UniformMPIDatatypeInfo.h
+++ b/src/field/communication/UniformMPIDatatypeInfo.h
@@ -39,26 +39,26 @@ public:
    UniformMPIDatatypeInfo( BlockDataID blockDataID, const uint_t numberOfGhostLayers ) : blockDataID_( blockDataID ),
       communicateAllGhostLayers_( false ), numberOfGhostLayers_( numberOfGhostLayers ) {}
 
-   virtual ~UniformMPIDatatypeInfo() {}
+   ~UniformMPIDatatypeInfo() override = default;
 
-   virtual shared_ptr<mpi::Datatype> getSendDatatype ( IBlock * block, const stencil::Direction dir )
+   shared_ptr<mpi::Datatype> getSendDatatype ( IBlock * block, const stencil::Direction dir ) override
    {
       auto numGl = numberOfGhostLayersToCommunicate( block );
       return make_shared<mpi::Datatype>( mpiDatatypeSliceBeforeGhostlayer( *getField( block ), dir, numGl ) );
    }
 
-   virtual shared_ptr<mpi::Datatype> getRecvDatatype ( IBlock * block, const stencil::Direction dir )
+   shared_ptr<mpi::Datatype> getRecvDatatype ( IBlock * block, const stencil::Direction dir ) override
    {
       auto numGl = numberOfGhostLayersToCommunicate( block );
       return make_shared<mpi::Datatype>( mpiDatatypeGhostLayerOnly( *getField(block), numGl, dir ) );
    }
 
-   virtual void * getSendPointer( IBlock * block, const stencil::Direction )
+   void * getSendPointer( IBlock * block, const stencil::Direction ) override
    {
       return getField(block)->data();
    }
 
-   virtual void * getRecvPointer( IBlock * block, const stencil::Direction )
+   void * getRecvPointer( IBlock * block, const stencil::Direction ) override
    {
       return getField(block)->data();
    }
diff --git a/src/field/communication/UniformPullReductionPackInfo.h b/src/field/communication/UniformPullReductionPackInfo.h
index 71ceb7e8e6fba381d5623f80e3ca6f9bb2b8ae47..aa7ffe1a906c81733f0c8b36f53532637b2f2993 100644
--- a/src/field/communication/UniformPullReductionPackInfo.h
+++ b/src/field/communication/UniformPullReductionPackInfo.h
@@ -49,7 +49,7 @@ template< template<typename> class ReduceOperation, typename GhostLayerField_T >
 class UniformPullReductionPackInfo : public walberla::communication::UniformPackInfo
 {
 public:
-   typedef typename GhostLayerField_T::value_type T;
+   using T = typename GhostLayerField_T::value_type;
 
    UniformPullReductionPackInfo( const BlockDataID & bdID ) : bdID_( bdID ), communicateAllGhostLayers_( true ),
                                  numberOfGhostLayers_( 0 ) {}
@@ -57,17 +57,17 @@ public:
    UniformPullReductionPackInfo( const BlockDataID & bdID, const uint_t numberOfGhostLayers ) : bdID_( bdID ),
                                  communicateAllGhostLayers_( false ), numberOfGhostLayers_(  numberOfGhostLayers ) {}
 
-   virtual ~UniformPullReductionPackInfo() {}
+   ~UniformPullReductionPackInfo() override = default;
 
-   bool constantDataExchange() const { return mpi::BufferSizeTrait<T>::constantSize; }
-   bool threadsafeReceiving()  const { return true; }
+   bool constantDataExchange() const override { return mpi::BufferSizeTrait<T>::constantSize; }
+   bool threadsafeReceiving()  const override { return true; }
 
-   void unpackData(IBlock * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer);
+   void unpackData(IBlock * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer) override;
 
-   void communicateLocal(const IBlock * sender, IBlock * receiver, stencil::Direction dir);
+   void communicateLocal(const IBlock * sender, IBlock * receiver, stencil::Direction dir) override;
 
 protected:
-   void packDataImpl(const IBlock * sender, stencil::Direction dir, mpi::SendBuffer & outBuffer) const;
+   void packDataImpl(const IBlock * sender, stencil::Direction dir, mpi::SendBuffer & outBuffer) const override;
 
    uint_t numberOfGhostLayersToCommunicate( const GhostLayerField_T * const field ) const;
 
diff --git a/src/field/distributors/DistributorCreators.h b/src/field/distributors/DistributorCreators.h
index e87b907cebac9b1aabf92dba0be34f730e16fffb..2ff45e304a5c68a2997cb8f654fb49096a361c99 100644
--- a/src/field/distributors/DistributorCreators.h
+++ b/src/field/distributors/DistributorCreators.h
@@ -65,10 +65,10 @@ public:
    blockStorage_( blockStorage ), distributionDestinationFieldID_( distributionDestinationFieldID ), flagFieldID_( flagFieldID ), cellsToEvaluate_( cellsToEvaluate )
    {}
 
-   Distributor_T * initialize( IBlock * const block )
+   Distributor_T * initialize( IBlock * const block ) override
    {
-      typedef typename Distributor_T::BaseField_T DistributionDestinationField_T;
-      typedef typename FlagField_T::flag_t flag_t;
+      using DistributionDestinationField_T = typename Distributor_T::BaseField_T;
+      using flag_t = typename FlagField_T::flag_t;
       DistributionDestinationField_T * distributionDestinationField = block->getData< DistributionDestinationField_T >( distributionDestinationFieldID_ );
       const FlagField_T * flagField = block->getData< FlagField_T >( flagFieldID_ );
 
diff --git a/src/field/distributors/KernelDistributor.h b/src/field/distributors/KernelDistributor.h
index 1aed2f2ea3c801998114723c8a8ac127d708ac33..712d9a7b7e32ae96b0d259873d102166675b22b3 100644
--- a/src/field/distributors/KernelDistributor.h
+++ b/src/field/distributors/KernelDistributor.h
@@ -50,9 +50,9 @@ public:
 
    static const uint_t F_SIZE = Field_T::F_SIZE;
 
-   typedef Field_T                                BaseField_T;
-   typedef typename FlagField_T::flag_t           flag_t;
-   typedef KernelDistributor<Field_T,FlagField_T> OwnType;
+   using BaseField_T = Field_T;
+   using flag_t = typename FlagField_T::flag_t;
+   using OwnType = KernelDistributor<Field_T, FlagField_T>;
 
    KernelDistributor( const weak_ptr<StructuredBlockStorage> & blockStorage, const IBlock & block,
                       BaseField_T & baseField, const FlagField_T & flagField,
diff --git a/src/field/distributors/NearestNeighborDistributor.h b/src/field/distributors/NearestNeighborDistributor.h
index 6aa4ba2958864e0be6de91c487566b787f5eaaf9..932f443b3e30af9ab6a2bc4f6c6f3f585e485473 100644
--- a/src/field/distributors/NearestNeighborDistributor.h
+++ b/src/field/distributors/NearestNeighborDistributor.h
@@ -49,9 +49,9 @@ public:
 
    static const uint_t F_SIZE = Field_T::F_SIZE;
 
-   typedef Field_T                                         BaseField_T;
-   typedef typename FlagField_T::flag_t                    flag_t;
-   typedef NearestNeighborDistributor<Field_T,FlagField_T> OwnType;
+   using BaseField_T = Field_T;
+   using flag_t = typename FlagField_T::flag_t;
+   using OwnType = NearestNeighborDistributor<Field_T, FlagField_T>;
 
    NearestNeighborDistributor( const weak_ptr<StructuredBlockStorage> & blockStorage, const IBlock & block,
                                BaseField_T & baseField, const FlagField_T & flagField,
diff --git a/src/field/interpolators/FieldInterpolatorCreators.h b/src/field/interpolators/FieldInterpolatorCreators.h
index 560df7879d03c5b4180b39094bf0f991614a7299..c4bcfafc3683886e57bf167ae5fcf7cf912d57da 100644
--- a/src/field/interpolators/FieldInterpolatorCreators.h
+++ b/src/field/interpolators/FieldInterpolatorCreators.h
@@ -64,10 +64,10 @@ public:
    blockStorage_( blockStorage ), interpolatedFieldID_( interpolatedFieldID ), flagFieldID_( flagFieldID ), cellsToEvaluate_( cellsToEvaluate )
    {}
 
-   Interpolator_T * initialize( IBlock * const block )
+   Interpolator_T * initialize( IBlock * const block ) override
    {
-      typedef typename Interpolator_T::BaseField_T InterpolatedField_T;
-      typedef typename FlagField_T::flag_t flag_t;
+      using InterpolatedField_T = typename Interpolator_T::BaseField_T;
+      using flag_t = typename FlagField_T::flag_t;
       const InterpolatedField_T * interpolatedField = block->getData< InterpolatedField_T >( interpolatedFieldID_ );
       const FlagField_T * flagField = block->getData< FlagField_T >( flagFieldID_ );
 
diff --git a/src/field/interpolators/KernelFieldInterpolator.h b/src/field/interpolators/KernelFieldInterpolator.h
index 50dfde0fc9a370fb1e4b8af95b37ccfe3bae9d4d..0e59fabf21dfd4899e62ca29961811a0e99e59d9 100644
--- a/src/field/interpolators/KernelFieldInterpolator.h
+++ b/src/field/interpolators/KernelFieldInterpolator.h
@@ -92,9 +92,9 @@ public:
 
    static const uint_t F_SIZE = Field_T::F_SIZE;
 
-   typedef Field_T                                      BaseField_T;
-   typedef typename FlagField_T::flag_t                 flag_t;
-   typedef KernelFieldInterpolator<Field_T,FlagField_T> OwnType;
+   using BaseField_T = Field_T;
+   using flag_t = typename FlagField_T::flag_t;
+   using OwnType = KernelFieldInterpolator<Field_T, FlagField_T>;
 
    KernelFieldInterpolator( const weak_ptr<StructuredBlockStorage> & blockStorage, const IBlock & block,
                             const BaseField_T & baseField, const FlagField_T & flagField,
diff --git a/src/field/interpolators/NearestNeighborFieldInterpolator.h b/src/field/interpolators/NearestNeighborFieldInterpolator.h
index 975c55edb4c52d0ea197391a88b271588bbf7729..b5b5cba7f65a3e06517356933d157108ba81e90c 100644
--- a/src/field/interpolators/NearestNeighborFieldInterpolator.h
+++ b/src/field/interpolators/NearestNeighborFieldInterpolator.h
@@ -46,9 +46,9 @@ public:
 
    static const uint_t F_SIZE = Field_T::F_SIZE;
 
-   typedef Field_T                                               BaseField_T;
-   typedef typename FlagField_T::flag_t                          flag_t;
-   typedef NearestNeighborFieldInterpolator<Field_T,FlagField_T> OwnType;
+   using BaseField_T = Field_T;
+   using flag_t = typename FlagField_T::flag_t;
+   using OwnType = NearestNeighborFieldInterpolator<Field_T, FlagField_T>;
 
    NearestNeighborFieldInterpolator( const weak_ptr<StructuredBlockStorage> & blockStorage, const IBlock & block,
                                      const BaseField_T & baseField, const FlagField_T & flagField,
diff --git a/src/field/interpolators/NearestNeighborInterpolator.h b/src/field/interpolators/NearestNeighborInterpolator.h
index 0a609dc68ae89036cfc28a006829ce0dc618bb88..b76c61fa5f8d711cf0a1b17e6ec9a94a93039a20 100644
--- a/src/field/interpolators/NearestNeighborInterpolator.h
+++ b/src/field/interpolators/NearestNeighborInterpolator.h
@@ -53,7 +53,7 @@ namespace field {
    public:
 
       static const uint_t F_SIZE = GlField::F_SIZE;
-      typedef  typename GlField::value_type value_type;
+      using value_type = typename GlField::value_type;
 
       NearestNeighborInterpolator( const GlField & fieldToInterpolate )
          : field_( fieldToInterpolate )
diff --git a/src/field/interpolators/TrilinearFieldInterpolator.h b/src/field/interpolators/TrilinearFieldInterpolator.h
index bdac4ff1a6d752b6d5d898139de421fb5793f43b..8eb24a4cf0877e0692f6e963fc8a01f5a47db9b4 100644
--- a/src/field/interpolators/TrilinearFieldInterpolator.h
+++ b/src/field/interpolators/TrilinearFieldInterpolator.h
@@ -48,9 +48,9 @@ public:
 
    static const uint_t F_SIZE = Field_T::F_SIZE;
 
-   typedef Field_T                                         BaseField_T;
-   typedef typename FlagField_T::flag_t                    flag_t;
-   typedef TrilinearFieldInterpolator<Field_T,FlagField_T> OwnType;
+   using BaseField_T = Field_T;
+   using flag_t = typename FlagField_T::flag_t;
+   using OwnType = TrilinearFieldInterpolator<Field_T, FlagField_T>;
 
    TrilinearFieldInterpolator( const weak_ptr<StructuredBlockStorage> & blockStorage, const IBlock & block,
                                const BaseField_T & baseField, const FlagField_T & flagField,
diff --git a/src/field/iterators/FieldIterator.h b/src/field/iterators/FieldIterator.h
index 0136ce72728aee1fe8e4495925a37b166a8b50ce..7557fea2af83b9c6b880dab2abc73a9eb9ec07f7 100644
--- a/src/field/iterators/FieldIterator.h
+++ b/src/field/iterators/FieldIterator.h
@@ -66,11 +66,16 @@ namespace field {
     */
    //*******************************************************************************************************************
    template <typename T, uint_t fieldFSize>
-   class FieldIterator : public std::iterator <std::forward_iterator_tag,T>
+   class FieldIterator
    {
    public:
-      typedef Field<typename std::remove_const<T>::type, fieldFSize> FieldType;
-      typedef T value_type;
+      using iterator_category = std::forward_iterator_tag;
+      using value_type = T;
+      using difference_type = std::ptrdiff_t;
+      using pointer = T*;
+      using reference = T&;
+
+      using FieldType = Field<typename std::remove_const<T>::type, fieldFSize>;
 
       static const uint_t F_SIZE = fieldFSize;
 
@@ -214,8 +219,8 @@ namespace field {
    class ForwardFieldIterator : public FieldIterator<T,fieldFSize>
    {
    public:
-      typedef FieldIterator<T,fieldFSize> Parent;
-      typedef Field<typename std::remove_const<T>::type, fieldFSize> FieldType;
+      using Parent = FieldIterator<T, fieldFSize>;
+      using FieldType = Field<typename std::remove_const<T>::type, fieldFSize>;
 
       //**Constructor/Destructor****************************************************************************************
       /*!\name Constructor/Destructor */
@@ -257,8 +262,8 @@ namespace field {
    class ReverseFieldIterator : public FieldIterator<T,fieldFSize>
    {
    public:
-       typedef FieldIterator<T,fieldFSize> Parent;
-       typedef Field<typename std::remove_const<T>::type, fieldFSize> FieldType;
+       using Parent = FieldIterator<T, fieldFSize>;
+       using FieldType = Field<typename std::remove_const<T>::type, fieldFSize>;
 
       //**Constructor/Destructor****************************************************************************************
       /*!\name Constructor/Destructor */
diff --git a/src/field/iterators/FieldIterator.impl.h b/src/field/iterators/FieldIterator.impl.h
index 0a132ae01aee0d0615c4655c6f13cc7f5b25a3bf..d2184e9a8201fbd12a6ca90e6aac9e016ec8ec83 100644
--- a/src/field/iterators/FieldIterator.impl.h
+++ b/src/field/iterators/FieldIterator.impl.h
@@ -41,13 +41,13 @@ FieldIterator<T,fs>::FieldIterator( const typename FieldIterator<T,fs>::FieldTyp
 {
    if ( field->xyzSize().empty() )
    {
-      linePtr_ = NULL;
-      lineEnd_ = NULL;
-      f_       = NULL;
+      linePtr_ = nullptr;
+      lineEnd_ = nullptr;
+      f_       = nullptr;
       return;
    }
 
-   typedef typename std::remove_const<T>::type NonConstT;
+   using NonConstT = typename std::remove_const<T>::type;
 
    cur_[0] = cur_[1] = cur_[2] = 0;
    if( f_->layout() == fzyx )
@@ -114,7 +114,7 @@ FieldIterator<T,fs>::FieldIterator( const typename FieldIterator<T,fs>::FieldTyp
  **********************************************************************************************************************/
 template <typename T, uint_t fs>
 FieldIterator<T,fs>::FieldIterator()
-   : linePtr_(NULL), lineEnd_(NULL), f_(NULL)
+   : linePtr_(nullptr), lineEnd_(nullptr), f_(nullptr)
 {
 }
 
@@ -228,7 +228,7 @@ inline void FieldIterator<T,fs>::incrementLine()
          if(cur_[0] == cell_idx_c(sizes_[0]) )
          {
             // iterator at end
-            linePtr_ = NULL;
+            linePtr_ = nullptr;
             return;
          }
       }
@@ -264,7 +264,7 @@ inline void FieldIterator<T,fs>::decrementLine()
          if(cur_[0] < 0 )
          {
             // iterator at end
-            linePtr_ = NULL;
+            linePtr_ = nullptr;
             return;
          }
       }
diff --git a/src/field/iterators/FieldNeighborPointer.h b/src/field/iterators/FieldNeighborPointer.h
index 9d483d1e90456e33ea224fce0c8775fd11cb2e6f..4ab5a13c04c618d54f24f82aef8b30de0a273373 100644
--- a/src/field/iterators/FieldNeighborPointer.h
+++ b/src/field/iterators/FieldNeighborPointer.h
@@ -61,7 +61,7 @@ namespace field {
    {
 
    public:
-      typedef Value_T value_type;
+      using value_type = Value_T;
       static const uint_t F_SIZE = Field_T::F_SIZE;
 
       FieldNeighborPointer( FieldMember & field, cell_idx_t _x, cell_idx_t _y, cell_idx_t _z, cell_idx_t _f = 0 )
diff --git a/src/field/iterators/FieldPointer.h b/src/field/iterators/FieldPointer.h
index ba027faadb30672769b055dbe66dd5e52d570c50..b36a19af5d57181215a05e3a856b181d8554d13b 100644
--- a/src/field/iterators/FieldPointer.h
+++ b/src/field/iterators/FieldPointer.h
@@ -63,7 +63,7 @@ namespace field {
    class FieldPointer
    {
    public:
-      typedef Value_T value_type;
+      using value_type = Value_T;
       static const uint_t F_SIZE = Field_T::F_SIZE;
 
       FieldPointer( FieldMember & field, cell_idx_t _x, cell_idx_t _y, cell_idx_t _z, cell_idx_t _f = 0 )
diff --git a/src/field/iterators/IteratorMacros.h b/src/field/iterators/IteratorMacros.h
index 08e8d5d9786ccb9fb5705f005b101567a3b1f87b..ed5b98711891b1f35ee3bbeef15520bf039bf7a0 100644
--- a/src/field/iterators/IteratorMacros.h
+++ b/src/field/iterators/IteratorMacros.h
@@ -149,7 +149,7 @@
 
 #ifdef _OPENMP
 
-#ifdef WALBERLA_CXX_COMPILER_IS_MSVC
+#if (defined(_MSC_VER) && _MSC_VER < 1926)
 
 #define WALBERLA_FOR_ALL_CELLS_XYZ_OMP( field, omp, CODE ) \
    { WALBERLA_ASSERT_NOT_NULLPTR_1( (field) ); \
@@ -875,7 +875,7 @@
       } \
    } }
 
-#else // == not WALBERLA_CXX_COMPILER_IS_MSVC
+#else // == MSVC >= 2019 16.6 or not MSVC
 
 #define WALBERLA_FOR_ALL_CELLS_XYZ_OMP( field, omp, CODE ) \
    { WALBERLA_ASSERT_NOT_NULLPTR_1( (field) ); \
diff --git a/src/field/python/CommunicationExport.impl.h b/src/field/python/CommunicationExport.impl.h
deleted file mode 100644
index 15066bcb0320a2ea7bf630b8848c971dddc5c464..0000000000000000000000000000000000000000
--- a/src/field/python/CommunicationExport.impl.h
+++ /dev/null
@@ -1,260 +0,0 @@
-//======================================================================================================================
-//
-//  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 CommunicationExport.impl.h
-//! \ingroup field
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#include "python_coupling/PythonWrapper.h"
-
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-
-#include "field/communication/PackInfo.h"
-#include "field/communication/StencilRestrictedPackInfo.h"
-#include "field/communication/UniformMPIDatatypeInfo.h"
-
-#include "python_coupling/helper/MplHelpers.h"
-#include "python_coupling/helper/BoostPythonHelpers.h"
-#include "python_coupling/helper/MplHelpers.h"
-
-#include "stencil/D2Q9.h"
-#include "stencil/D3Q7.h"
-#include "stencil/D3Q15.h"
-#include "stencil/D3Q19.h"
-#include "stencil/D3Q27.h"
-
-
-namespace walberla {
-namespace field {
-
-
-namespace internal {
-
-   //===================================================================================================================
-   //
-   //  createStencilRestrictedPackInfo Export
-   //
-   //===================================================================================================================
-
-   template< typename FieldType >
-   typename std::enable_if<FieldType::F_SIZE == 27, boost::python::object>::type
-   createStencilRestrictedPackInfoObject( BlockDataID bdId )
-   {
-      typedef GhostLayerField<typename FieldType::value_type, 27> GlField_T;
-      using field::communication::StencilRestrictedPackInfo;
-      return boost::python::object( make_shared< StencilRestrictedPackInfo<GlField_T, stencil::D3Q27> >( bdId) );
-   }
-
-   template< typename FieldType >
-   typename std::enable_if<FieldType::F_SIZE == 19, boost::python::object>::type
-   createStencilRestrictedPackInfoObject( BlockDataID bdId )
-   {
-      typedef GhostLayerField<typename FieldType::value_type, 19> GlField_T;
-      using field::communication::StencilRestrictedPackInfo;
-      return boost::python::object( make_shared< StencilRestrictedPackInfo<GlField_T, stencil::D3Q19> >( bdId) );
-   }
-
-   template< typename FieldType >
-   typename std::enable_if<FieldType::F_SIZE == 15, boost::python::object>::type
-   createStencilRestrictedPackInfoObject( BlockDataID bdId )
-   {
-      typedef GhostLayerField<typename FieldType::value_type, 15> GlField_T;
-      using field::communication::StencilRestrictedPackInfo;
-      return boost::python::object( make_shared< StencilRestrictedPackInfo<GlField_T, stencil::D3Q15> >( bdId) );
-   }
-
-   template< typename FieldType >
-   typename std::enable_if<FieldType::F_SIZE == 7, boost::python::object>::type
-   createStencilRestrictedPackInfoObject( BlockDataID bdId )
-   {
-      typedef GhostLayerField<typename FieldType::value_type, 7> GlField_T;
-      using field::communication::StencilRestrictedPackInfo;
-      return boost::python::object( make_shared< StencilRestrictedPackInfo<GlField_T, stencil::D3Q7> >( bdId) );
-   }
-
-   template< typename FieldType >
-   typename std::enable_if<FieldType::F_SIZE == 9, boost::python::object>::type
-   createStencilRestrictedPackInfoObject( BlockDataID bdId )
-   {
-      typedef GhostLayerField<typename FieldType::value_type, 9> GlField_T;
-      using field::communication::StencilRestrictedPackInfo;
-      return boost::python::object( make_shared< StencilRestrictedPackInfo<GlField_T, stencil::D2Q9> >( bdId) );
-   }
-
-   template< typename FieldType >
-   typename std::enable_if<!(FieldType::F_SIZE == 9  ||
-                             FieldType::F_SIZE == 7  ||
-                             FieldType::F_SIZE == 15 ||
-                             FieldType::F_SIZE == 19 ||
-                             FieldType::F_SIZE == 27), boost::python::object>::type
-   createStencilRestrictedPackInfoObject( BlockDataID )
-   {
-      PyErr_SetString( PyExc_ValueError, "This works only for fields with fSize in 7, 9, 15, 19 or 27" );
-      throw boost::python::error_already_set();
-   }
-
-   FunctionExporterClass( createStencilRestrictedPackInfoObject, boost::python::object( BlockDataID ) );
-
-   template< typename FieldVector>
-   boost::python::object createStencilRestrictedPackInfo( const shared_ptr<StructuredBlockStorage> & bs,
-                                                          const std::string & blockDataName )
-   {
-      auto bdId = python_coupling::blockDataIDFromString( *bs, blockDataName );
-      if ( bs->begin() == bs->end() ) {
-         // if no blocks are on this field an arbitrary PackInfo can be returned
-         return createStencilRestrictedPackInfoObject< GhostLayerField<real_t,1> > ( bdId );
-      }
-
-      IBlock * firstBlock =  & ( * bs->begin() );
-      python_coupling::Dispatcher<FieldVector, Exporter_createStencilRestrictedPackInfoObject > dispatcher( firstBlock );
-      return dispatcher( bdId )( bdId ) ;
-   }
-
-   //===================================================================================================================
-   //
-   //  createPackInfo Export
-   //
-   //===================================================================================================================
-
-   template< typename FieldType >
-   boost::python::object createPackInfoToObject( BlockDataID bdId, uint_t numberOfGhostLayers )
-   {
-      typedef typename FieldType::value_type T;
-      const uint_t F_SIZE = FieldType::F_SIZE;
-      typedef GhostLayerField<T,F_SIZE> GlField_T;
-      if ( numberOfGhostLayers > 0  )
-         return boost::python::object( make_shared< field::communication::PackInfo<GlField_T> >( bdId, numberOfGhostLayers ) );
-      else
-         return boost::python::object( make_shared< field::communication::PackInfo<GlField_T> >( bdId ) );
-   }
-
-   FunctionExporterClass( createPackInfoToObject, boost::python::object( BlockDataID, uint_t  ) );
-
-   template< typename FieldVector>
-   boost::python::object createPackInfo( const shared_ptr<StructuredBlockStorage> & bs,
-                                         const std::string & blockDataName, uint_t numberOfGhostLayers )
-   {
-      auto bdId = python_coupling::blockDataIDFromString( *bs, blockDataName );
-      if ( bs->begin() == bs->end() ) {
-         // if no blocks are on this field an arbitrary PackInfo can be returned
-         return createPackInfoToObject< GhostLayerField<real_t,1> > ( bdId, numberOfGhostLayers );
-      }
-
-      IBlock * firstBlock =  & ( * bs->begin() );
-      python_coupling::Dispatcher<FieldVector, Exporter_createPackInfoToObject > dispatcher( firstBlock );
-      return dispatcher( bdId )( bdId, numberOfGhostLayers ) ;
-   }
-
-
-   //===================================================================================================================
-   //
-   //  createMPIDatatypeInfo
-   //
-   //===================================================================================================================
-
-
-   template< typename FieldType >
-   boost::python::object createMPIDatatypeInfoToObject( BlockDataID bdId, uint_t numberOfGhostLayers )
-   {
-      typedef typename FieldType::value_type T;
-      const uint_t F_SIZE = FieldType::F_SIZE;
-      typedef GhostLayerField<T,F_SIZE> GlField_T;
-      using field::communication::UniformMPIDatatypeInfo;
-
-      if ( numberOfGhostLayers > 0 )
-         return boost::python::object( make_shared< UniformMPIDatatypeInfo<GlField_T> >( bdId, numberOfGhostLayers ) );
-      else
-         return boost::python::object( make_shared< UniformMPIDatatypeInfo<GlField_T> >( bdId ) );
-   }
-
-   FunctionExporterClass( createMPIDatatypeInfoToObject, boost::python::object( BlockDataID, uint_t  ) );
-
-   template< typename FieldVector>
-   boost::python::object createMPIDatatypeInfo( const shared_ptr<StructuredBlockStorage> & bs,
-                                                const std::string & blockDataName,
-                                                uint_t numberOfGhostLayers)
-   {
-      auto bdId = python_coupling::blockDataIDFromString( *bs, blockDataName );
-      if ( bs->begin() == bs->end() ) {
-         // if no blocks are on this field an arbitrary MPIDatatypeInfo can be returned
-         return createMPIDatatypeInfoToObject< GhostLayerField<real_t,1> > ( bdId, numberOfGhostLayers );
-      }
-
-      IBlock * firstBlock =  & ( * bs->begin() );
-      python_coupling::Dispatcher<FieldVector, Exporter_createMPIDatatypeInfoToObject > dispatcher( firstBlock );
-      return dispatcher( bdId )( bdId, numberOfGhostLayers );
-   }
-
-   template< typename T>
-   void exportStencilRestrictedPackInfo()
-   {
-      using field::communication::StencilRestrictedPackInfo;
-      using namespace boost::python;
-
-      {
-         typedef StencilRestrictedPackInfo<GhostLayerField<T, 9>, stencil::D2Q9> Pi;
-         class_< Pi, shared_ptr<Pi>, bases<walberla::communication::UniformPackInfo>,  boost::noncopyable >( "StencilRestrictedPackInfo", no_init );
-      }
-      {
-         typedef StencilRestrictedPackInfo<GhostLayerField<T, 7>, stencil::D3Q7> Pi;
-         class_< Pi, shared_ptr<Pi>, bases<walberla::communication::UniformPackInfo>,  boost::noncopyable >( "StencilRestrictedPackInfo", no_init );
-      }
-      {
-         typedef StencilRestrictedPackInfo<GhostLayerField<T, 15>, stencil::D3Q15> Pi;
-         class_< Pi, shared_ptr<Pi>, bases<walberla::communication::UniformPackInfo>,  boost::noncopyable >( "StencilRestrictedPackInfo", no_init );
-      }
-      {
-         typedef StencilRestrictedPackInfo<GhostLayerField<T, 19>, stencil::D3Q19> Pi;
-         class_< Pi, shared_ptr<Pi>, bases<walberla::communication::UniformPackInfo>,  boost::noncopyable >( "StencilRestrictedPackInfo", no_init );
-      }
-      {
-         typedef StencilRestrictedPackInfo<GhostLayerField<T, 27>, stencil::D3Q27> Pi;
-         class_< Pi, shared_ptr<Pi>, bases<walberla::communication::UniformPackInfo>,  boost::noncopyable >( "StencilRestrictedPackInfo", no_init );
-      }
-
-   }
-
-} // namespace internal
-
-
-
-
-
-template<typename FieldTypes>
-void exportCommunicationClasses()
-{
-   using namespace boost::python;
-
-   internal::exportStencilRestrictedPackInfo<float>();
-   internal::exportStencilRestrictedPackInfo<double>();
-
-   def( "createMPIDatatypeInfo",&internal::createMPIDatatypeInfo<FieldTypes>, ( arg("blocks"), arg("blockDataName"), arg("numberOfGhostLayers" ) =0 ) );
-   def( "createPackInfo",       &internal::createPackInfo<FieldTypes>,        ( arg("blocks"), arg("blockDataName"), arg("numberOfGhostLayers" ) =0 ) );
-   def( "createStencilRestrictedPackInfo", &internal::createStencilRestrictedPackInfo<FieldTypes>,
-        (arg("blocks"), arg("blockDataName") ));
-}
-
-
-} // namespace moduleName
-} // namespace walberla
-
-
-
-
-#endif // WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/field/python/FieldExport.h b/src/field/python/FieldExport.h
deleted file mode 100644
index e7bdedb4f2548294f5a67293b3f6d94e60be76b6..0000000000000000000000000000000000000000
--- a/src/field/python/FieldExport.h
+++ /dev/null
@@ -1,65 +0,0 @@
-//======================================================================================================================
-//
-//  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 FieldExport.h
-//! \ingroup field
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-
-#include <string>
-
-namespace walberla {
-namespace field {
-
-
-   //*******************************************************************************************************************
-   /*! Exports all Fields given in the Sequence
-   *
-   * Put only Fields in the sequence! The corresponding GhostLayerFields and FlagFields are exported automatically
-   *
-   * \warning Make sure that the same adaptor type is exported only once!
-   */
-   //*******************************************************************************************************************
-   template<typename FieldTypes >
-   void exportFields();
-
-
-
-   //*******************************************************************************************************************
-   /*! Exports all GhostLayerFieldAdaptors given in the Sequence
-   *
-   * \warning Make sure that the same adaptor type is exported only once!
-   */
-   //*******************************************************************************************************************
-   template<typename AdaptorTypes>
-   void exportGhostLayerFieldAdaptors();
-
-   template<typename AdaptorType>
-   void exportGhostLayerFieldAdaptor();
-
-
-} // namespace field
-} // namespace walberla
-
-#include "FieldExport.impl.h"
-
-
-#endif //WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/field/python/FieldExport.impl.h b/src/field/python/FieldExport.impl.h
deleted file mode 100644
index 83e3b42397f407933dbb711faaca6e2bfa4c9a44..0000000000000000000000000000000000000000
--- a/src/field/python/FieldExport.impl.h
+++ /dev/null
@@ -1,1396 +0,0 @@
-//======================================================================================================================
-//
-//  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 FieldExport.cpp
-//! \ingroup field
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-// Do not reorder includes - the include order is important
-#include "python_coupling/PythonWrapper.h"
-
-
-#include "core/logging/Logging.h"
-#include "core/VectorTrait.h"
-#include "field/Field.h"
-#include "field/GhostLayerField.h"
-#include "field/FlagField.h"
-#include "field/communication/PackInfo.h"
-#include "field/communication/UniformMPIDatatypeInfo.h"
-
-#include "field/AddToStorage.h"
-#include "field/python/GatherExport.h"
-#include "field/vtk/VTKWriter.h"
-#include "field/vtk/FlagFieldMapping.h"
-
-#include "python_coupling/helper/MplHelpers.h"
-#include "python_coupling/helper/BoostPythonHelpers.h"
-
-#include <boost/mpl/vector.hpp>
-
-#include <iostream>
-#include <type_traits>
-
-namespace walberla {
-namespace field {
-
-
-
-namespace internal {
-
-   //===================================================================================================================
-   //
-   //  Buffer Protocol for Fields
-   //
-   //===================================================================================================================
-
-
-
-   /* This section implements the Python buffer protocol for walberla::Field's
-    *
-    *  - Why? The buffer protocol enables other Python types to use the memory belonging to a field.
-    *         One can for example construct a numpy.array that operates on the field data
-    *  - How? a = numpy.asarray( myWalberlaField.buffer() )
-    *         creates a numpy array which uses the field data in a read-write way! no data is copied
-    *  - Python buffer protocol: http://docs.python.org/dev/c-api/buffer.html
-    *  - Why so complicated?
-    *       boost::python does not yet (as in version 1.55) support the buffer protocol
-    *       so everything has to be written in the native Python C interface.
-    *       In order to export the Field with boost python and keep the native C interface part as
-    *       small as possible, a new Type called FieldBuffer is introduced which is exported in the
-    *       C interface. This type can only be created using the buffer() function of the field.
-    *
-    *
-    *  - Lifetime issues:
-    *       0) f   = walberla.create_field( ( 5,3,2) )
-    *       1) buf = f.buffer():
-    *             - creates a FieldBuffer object which has as only member the Field
-    *             - f is not deallocated as long as 'buf' exists
-    *       2) a = numpy.asarray( buf )
-    *             - calls 'fieldbuffer_get' which creates a Py_buffer,
-    *                extracts the information, then immediately calls 'fieldbuffer_release'
-    *                to clean up the Py_buffer.
-    *             - buf still exists (   fieldbuffer_release only cleans up Py_buffer not
-    *                the FieldBuffer object 'buf' )
-    *       3) del f
-    *          del b
-    *             - the buffer object b is not deallocated since a still has a reference to it
-    *             - since b is not deleted f is not deleted since b has still a reference to it
-    *               i.e   a -> b -> f
-    *       4) when a is deleted everything can be cleaned up, which means that fieldbuffer_dealloc
-    *          is called, which decrements the reference count for f, so that f can be deallocated if
-    *          not used otherwise
-    *
-    */
-   template<class T> struct PythonFormatString                    { inline static char * get() { static char value [] = "B"; return value; } };
-
-   template<>        struct PythonFormatString<double>            { inline static char * get() { static char value [] = "d"; return value; } };
-   template<>        struct PythonFormatString<float>             { inline static char * get() { static char value [] = "f"; return value; } };
-   template<>        struct PythonFormatString<unsigned short>    { inline static char * get() { static char value [] = "H"; return value; } };
-   template<>        struct PythonFormatString<int>               { inline static char * get() { static char value [] = "i"; return value; } };
-   template<>        struct PythonFormatString<unsigned int>      { inline static char * get() { static char value [] = "I"; return value; } };
-   template<>        struct PythonFormatString<long>              { inline static char * get() { static char value [] = "l"; return value; } };
-   template<>        struct PythonFormatString<unsigned long>     { inline static char * get() { static char value [] = "L"; return value; } };
-   template<>        struct PythonFormatString<long long>         { inline static char * get() { static char value [] = "q"; return value; } };
-   template<>        struct PythonFormatString<unsigned long long>{ inline static char * get() { static char value [] = "Q"; return value; } };
-
-
-   typedef struct {
-       PyObject_HEAD
-       PyObject * field;
-       PyObject * fieldAlloc;
-       void * fieldData;
-   } FieldBufferPyObject;
-
-
-   template<typename T, uint_t fs>
-   struct FieldBufferGetDispatch
-   {
-        static int get( FieldBufferPyObject * exporter, Py_buffer * view, int flags, bool withGhostLayer )
-        {
-           namespace bp = boost::python;
-
-           bp::object fieldObject ( bp::handle<>(bp::borrowed( exporter->field ) ) );
-           Field<T,fs> * field = bp::extract< Field<T,fs> * > ( fieldObject );
-
-           bp::object fieldAllocObject ( bp::handle<>(bp::borrowed( exporter->fieldAlloc ) ) );
-           FieldAllocator<T> * fieldAlloc = bp::extract< FieldAllocator<T> * > ( fieldAllocObject );
-           fieldAlloc->incrementReferenceCount( field->data() );
-
-           view->obj = (PyObject*) exporter;
-           Py_INCREF( view->obj );
-
-           uint_t size[3];
-           auto glField = dynamic_cast<GhostLayerField<T,fs> * >( field );
-           if ( glField && withGhostLayer )
-           {
-              size[0] = glField->xSizeWithGhostLayer();
-              size[1] = glField->ySizeWithGhostLayer();
-              size[2] = glField->zSizeWithGhostLayer();
-              cell_idx_t gl = cell_idx_c( glField->nrOfGhostLayers() );
-              view->buf = &( field->get( -gl, -gl, -gl,0 ) );
-           }
-           else
-           {
-              size[0] = field->xSize();
-              size[1] = field->ySize();
-              size[2] = field->zSize();
-              view->buf = & ( field->get(0,0,0,0) ) ;
-           }
-
-
-           // Mandatory
-           view->len = Py_ssize_t( size[0] * size[1] * size[2] * fs * sizeof(T) );
-           view->itemsize = sizeof( T );
-
-           view->ndim = 4;
-
-           view->format = NULL;
-           if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
-              view->format = PythonFormatString<T>::get();
-
-           view->shape=NULL;
-           if ((flags & PyBUF_ND) == PyBUF_ND)
-           {
-              view->shape = new Py_ssize_t[4];
-              view->shape[0u] = Py_ssize_t( size[0u] );
-              view->shape[1u] = Py_ssize_t( size[1u] );
-              view->shape[2u] = Py_ssize_t( size[2u] );
-              view->shape[3u] = Py_ssize_t( field->fSize() );
-           }
-
-           view->strides = NULL;
-           if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES)
-           {
-              view->strides = new Py_ssize_t[4u];
-              view->strides[0u] = Py_ssize_t( uint_c( field->xStride() ) * sizeof(T) );
-              view->strides[1u] = Py_ssize_t( uint_c( field->yStride() ) * sizeof(T) );
-              view->strides[2u] = Py_ssize_t( uint_c( field->zStride() ) * sizeof(T) );
-              view->strides[3u] = Py_ssize_t( uint_c( field->fStride() ) * sizeof(T) );
-           }
-
-           view->suboffsets = NULL;
-           view->internal = NULL;
-           view->readonly = false;
-
-           // We don't need the field any more. For freeing only the allocator and the field data is necessary
-           Py_DECREF( exporter->field );
-           exporter->field = NULL;
-
-           return 0;
-        }
-    };
-
-    template<typename VectorType>
-    struct FieldBufferGetDispatch<VectorType,1>
-    {
-        static int get( FieldBufferPyObject * exporter, Py_buffer * view, int flags, bool withGhostLayer )
-        {
-           namespace bp = boost::python;
-
-           typedef VectorTrait<VectorType> VecTrait;
-           typedef typename VecTrait::OutputType ElementType;
-
-           static_assert( sizeof(VectorType) == VecTrait::F_SIZE*sizeof(ElementType),
-                          "Creating Python Memory View works only for vectors types that hold their elements consecutively in memory" );
-
-           bp::object fieldObject ( bp::handle<>(bp::borrowed( exporter->field ) ) );
-           Field<VectorType,1> * field = bp::extract< Field<VectorType,1> * > ( fieldObject );
-
-           bp::object fieldAllocObject ( bp::handle<>(bp::borrowed( exporter->fieldAlloc ) ) );
-           FieldAllocator<VectorType> * fieldAlloc = bp::extract< FieldAllocator<VectorType> * > ( fieldAllocObject );
-           fieldAlloc->incrementReferenceCount( field->data() );
-
-           view->obj = (PyObject*) exporter;
-           Py_INCREF( view->obj );
-
-           uint_t size[3];
-           auto glField = dynamic_cast<GhostLayerField<VectorType,1> * >( field );
-           if ( glField && withGhostLayer )
-           {
-              size[0] = glField->xSizeWithGhostLayer();
-              size[1] = glField->ySizeWithGhostLayer();
-              size[2] = glField->zSizeWithGhostLayer();
-              cell_idx_t gl = cell_idx_c( glField->nrOfGhostLayers() );
-              view->buf = &( field->get( -gl, -gl, -gl,0 ) );
-           }
-           else
-           {
-              size[0] = field->xSize();
-              size[1] = field->ySize();
-              size[2] = field->zSize();
-              view->buf = & ( field->get(0,0,0,0) ) ;
-           }
-
-
-           // Mandatory
-           view->len = Py_ssize_t( size[0] * size[1] * size[2] * VecTrait::F_SIZE * sizeof(ElementType) );
-           view->itemsize = sizeof( ElementType );
-
-           view->ndim = 4;
-
-           view->format = NULL;
-           if ((flags & PyBUF_FORMAT) == PyBUF_FORMAT)
-              view->format = PythonFormatString<ElementType>::get();
-
-           view->shape=NULL;
-           if ((flags & PyBUF_ND) == PyBUF_ND)
-           {
-              view->shape = new Py_ssize_t[4];
-              view->shape[0u] = Py_ssize_t( size[0u] );
-              view->shape[1u] = Py_ssize_t( size[1u] );
-              view->shape[2u] = Py_ssize_t( size[2u] );
-              view->shape[3u] = Py_ssize_t( VecTrait::F_SIZE );
-           }
-
-           view->strides = NULL;
-           if ((flags & PyBUF_STRIDES) == PyBUF_STRIDES)
-           {
-              view->strides = new Py_ssize_t[4u];
-              view->strides[0u] = Py_ssize_t( uint_c( field->xStride() ) * VecTrait::F_SIZE * sizeof(ElementType) );
-              view->strides[1u] = Py_ssize_t( uint_c( field->yStride() ) * VecTrait::F_SIZE * sizeof(ElementType) );
-              view->strides[2u] = Py_ssize_t( uint_c( field->zStride() ) * VecTrait::F_SIZE * sizeof(ElementType) );
-              view->strides[3u] = Py_ssize_t( sizeof(ElementType) );
-           }
-
-           view->suboffsets = NULL;
-           view->internal = NULL;
-           view->readonly = false;
-
-           // We don't need the field any more. For freeing only the allocator and the field data is necessary
-           Py_DECREF( exporter->field );
-           exporter->field = NULL;
-
-           return 0;
-        }
-    };
-
-   template<typename T, uint_t fs>
-   int fieldbuffer_get_withGl ( FieldBufferPyObject * exporter, Py_buffer * view, int flags )
-   {
-      return FieldBufferGetDispatch<T,fs>::get( exporter, view, flags, true );
-   }
-
-   template<typename T, uint_t fs>
-   int fieldbuffer_get ( FieldBufferPyObject * exporter, Py_buffer * view, int flags )
-   {
-      return FieldBufferGetDispatch<T,fs>::get( exporter, view, flags, false );
-   }
-
-
-   template<typename T, uint_t fs>
-   int fieldbuffer_release ( PyObject * /*exporter*/, Py_buffer * view )
-   {
-      delete [] view->strides;
-      delete [] view->shape;
-      //std::cout << "Releasing Field Buffer " << std::endl;
-      return 0;
-   }
-
-
-   template<typename T, uint_t fs>
-   static void fieldbuffer_dealloc( FieldBufferPyObject * exporter )
-   {
-      namespace bp = boost::python;
-      bp::object fieldAllocObject ( bp::handle<>(bp::borrowed( exporter->fieldAlloc ) ) );
-      FieldAllocator<T> * fieldAlloc = bp::extract< FieldAllocator<T> * > ( fieldAllocObject );
-
-      fieldAlloc->decrementReferenceCount( (T*) exporter->fieldData );
-
-      Py_DECREF( exporter->fieldAlloc );
-
-
-      //std::cout << "Dealloc Fieldbuffer " << (void*) exporter << std::endl;
-      Py_TYPE(exporter)->tp_free ((PyObject*) exporter );
-   }
-
-
-   template<typename T, uint_t fs>
-   Py_ssize_t fieldbuffer_getbuffer(FieldBufferPyObject *, Py_ssize_t , const void **)
-   {
-      WALBERLA_CHECK(false, "fieldbuffer_getbuffer is part of the old buffer interface and should never be used");
-      return Py_ssize_t(0);// prevent compiler warning
-   }
-
-
-   template<typename T, uint_t fs>
-   Py_ssize_t fieldbuffer_getsegcount(FieldBufferPyObject *, Py_ssize_t *)
-   {
-      WALBERLA_CHECK(false, "fieldbuffer_getsegcount is part of the old buffer interface and should never be used");
-      return Py_ssize_t(0);// prevent compiler warning
-   }
-
-
-   template<typename T, uint_t fs>
-   Py_ssize_t fieldbuffer_getbuffer_withGl(FieldBufferPyObject *, Py_ssize_t , const void **)
-   {
-      WALBERLA_CHECK(false, "fieldbuffer_getbuffer is part of the old buffer interface and should never be used");
-      return Py_ssize_t(0);// prevent compiler warning
-   }
-
-
-   template<typename T, uint_t fs>
-   Py_ssize_t fieldbuffer_getsegcount_withGl(FieldBufferPyObject *, Py_ssize_t *)
-   {
-      WALBERLA_CHECK(false, "fieldbuffer_getsegcount is part of the old buffer interface and should never be used");
-      return Py_ssize_t(0);// prevent compiler warning
-   }
-
-
-
-   #ifdef WALBERLA_CXX_COMPILER_IS_GNU
-   #pragma GCC diagnostic push
-   #pragma GCC diagnostic ignored "-Wmissing-field-initializers"
-   #endif
-
-
-   template<typename T, uint_t fs>
-   struct FieldBufferType
-   {
-      static PyBufferProcs bufferProcs;
-      static PyTypeObject  value;
-   };
-
-   template<typename T, uint_t fs>
-   PyBufferProcs FieldBufferType<T,fs>::bufferProcs = {
-#if PY_MAJOR_VERSION < 3
-       (readbufferproc)   fieldbuffer_getbuffer  <T,fs>, /* bf_getreadbuffer */
-       (writebufferproc)  fieldbuffer_getbuffer  <T,fs>, /* bf_getwritebuffer */
-       (segcountproc)     fieldbuffer_getsegcount<T,fs>, /* bf_getsegcount */
-       (charbufferproc)   fieldbuffer_getbuffer  <T,fs>,  /* bf_getcharbuffer */
-#endif
-       (getbufferproc)    fieldbuffer_get    <T,fs>, /* bf_getbuffer */
-       (releasebufferproc)fieldbuffer_release<T,fs>, /* bf_releasebuffer */
-   };
-
-   template<typename T, uint_t fs>
-   PyTypeObject FieldBufferType<T,fs>::value = {
-       PyVarObject_HEAD_INIT(NULL, 0)
-       "walberla_cpp.FieldBuffer",              /* tp_name */
-       sizeof(FieldBufferPyObject),             /* tp_basicsize */
-       0,                                       /* tp_itemsize */
-       (destructor)fieldbuffer_dealloc<T,fs>,   /* tp_dealloc */
-       0,                                       /* tp_print */
-       0,                                       /* tp_getattr */
-       0,                                       /* tp_setattr */
-       0,                                       /* tp_reserved */
-       0,                                       /* tp_repr */
-       0,                                       /* tp_as_number */
-       0,                                       /* tp_as_sequence */
-       0,                                       /* tp_as_mapping */
-       0,                                       /* tp_hash  */
-       0,                                       /* tp_call */
-       0,                                       /* tp_str */
-       0,                                       /* tp_getattro */
-       0,                                       /* tp_setattro */
-       &FieldBufferType<T,fs>::bufferProcs,     /* tp_as_buffer */
-#if PY_MAJOR_VERSION >= 3
-       Py_TPFLAGS_DEFAULT,                      /* tp_flags */
-#else
-       Py_TPFLAGS_DEFAULT |
-          Py_TPFLAGS_HAVE_NEWBUFFER,            /* tp_flags */
-#endif
-       "FieldBuffer Objects",                   /* tp_doc */
-   };
-
-
-
-   template<typename T, uint_t fs>
-   struct FieldBufferTypeGl
-   {
-      static PyBufferProcs bufferProcs;
-      static PyTypeObject  value;
-   };
-
-   template<typename T, uint_t fs>
-   PyBufferProcs FieldBufferTypeGl<T,fs>::bufferProcs = {
-#if PY_MAJOR_VERSION < 3
-       (readbufferproc)   fieldbuffer_getbuffer_withGl  <T,fs>, /* bf_getreadbuffer */
-       (writebufferproc)  fieldbuffer_getbuffer_withGl  <T,fs>, /* bf_getwritebuffer */
-       (segcountproc)     fieldbuffer_getsegcount_withGl<T,fs>, /* bf_getsegcount */
-       (charbufferproc)   fieldbuffer_getbuffer_withGl  <T,fs>,  /* bf_getcharbuffer */
-#endif
-       (getbufferproc)    fieldbuffer_get_withGl<T,fs>, /* bf_getbuffer */
-       (releasebufferproc)fieldbuffer_release   <T,fs>, /* bf_releasebuffer */
-   };
-
-   template<typename T, uint_t fs>
-   PyTypeObject FieldBufferTypeGl<T,fs>::value = {
-       PyVarObject_HEAD_INIT(NULL, 0)
-       "walberla_cpp.FieldBufferGl",            /* tp_name */
-       sizeof(FieldBufferPyObject),             /* tp_basicsize */
-       0,                                       /* tp_itemsize */
-       (destructor)fieldbuffer_dealloc<T,fs>,   /* tp_dealloc */
-       0,                                       /* tp_print */
-       0,                                       /* tp_getattr */
-       0,                                       /* tp_setattr */
-       0,                                       /* tp_reserved */
-       0,                                       /* tp_repr */
-       0,                                       /* tp_as_number */
-       0,                                       /* tp_as_sequence */
-       0,                                       /* tp_as_mapping */
-       0,                                       /* tp_hash  */
-       0,                                       /* tp_call */
-       0,                                       /* tp_str */
-       0,                                       /* tp_getattro */
-       0,                                       /* tp_setattro */
-       &FieldBufferTypeGl<T,fs>::bufferProcs,   /* tp_as_buffer */
-#if PY_MAJOR_VERSION >= 3
-       Py_TPFLAGS_DEFAULT,                      /* tp_flags */
-#else
-       Py_TPFLAGS_DEFAULT |
-          Py_TPFLAGS_HAVE_NEWBUFFER,            /* tp_flags */
-#endif
-       "FieldBufferGl Objects",                 /* tp_doc */
-   };
-
-
-
-   #ifdef WALBERLA_CXX_COMPILER_IS_GNU
-   #pragma GCC diagnostic pop
-   #endif
-
-   // this will become the buffer() function of the field which creates the
-   // FieldBuffer object, so this function is between field (exported in boost::python) and
-   // FieldBuffer which is exported in native Python C API
-   template<typename T, uint_t fs>
-   boost::python::object field_getBufferInterface( boost::python::object field, bool withGhostLayers )
-   {
-      namespace bp = boost::python;
-
-      FieldBufferPyObject *obj;
-      if ( withGhostLayers )
-         obj = (FieldBufferPyObject*) PyObject_CallObject((PyObject *) & FieldBufferTypeGl<T,fs>::value, NULL );
-      else
-         obj = (FieldBufferPyObject*) PyObject_CallObject((PyObject *) & FieldBufferType<T,fs>::value, NULL );
-
-
-      Field<T,fs> * fieldPtr = bp::extract< Field<T,fs> * > ( field );
-      bp::object fieldPtrObject( fieldPtr->getAllocator() );
-
-      obj->field      = field.ptr();
-      obj->fieldAlloc = fieldPtrObject.ptr();
-      obj->fieldData  = (void*) ( fieldPtr->data() );
-      Py_INCREF( obj->field      );
-      Py_INCREF( obj->fieldAlloc );
-
-      return bp::object ( bp::handle<>( (PyObject*) obj ) );
-   }
-
-
-   //===================================================================================================================
-   //
-   //  Aligned Allocation
-   //
-   //===================================================================================================================
-
-   template<typename T>
-   shared_ptr<field::FieldAllocator<T> > getAllocator(uint_t alignment)
-   {
-      if( alignment == 0 )
-         return shared_ptr<field::FieldAllocator<T> >(); // leave to default - auto-detection of alignment
-      else if ( alignment == 16 )
-         return make_shared< field::AllocateAligned<T, 16> >();
-      else if ( alignment == 32 )
-         return make_shared< field::AllocateAligned<T, 32> >();
-      else if ( alignment == 64 )
-         return make_shared< field::AllocateAligned<T, 64> >();
-      else if ( alignment == 128 )
-         return make_shared< field::AllocateAligned<T, 128> >();
-      else {
-         PyErr_SetString( PyExc_ValueError, "Alignment parameter has to be one of 0, 16, 32, 64, 128." );
-         throw boost::python::error_already_set();
-         return shared_ptr<field::FieldAllocator<T> >();
-      }
-   }
-
-   template< typename GhostLayerField_T >
-   class GhostLayerFieldDataHandling : public field::BlockDataHandling< GhostLayerField_T >
-   {
-   public:
-      typedef typename GhostLayerField_T::value_type Value_T;
-
-      GhostLayerFieldDataHandling( const weak_ptr<StructuredBlockStorage> &blocks, const uint_t nrOfGhostLayers,
-                                   const Value_T &initValue, const Layout layout, uint_t alignment = 0 ) :
-              blocks_( blocks ), nrOfGhostLayers_( nrOfGhostLayers ), initValue_( initValue ), layout_( layout ),
-              alignment_( alignment ) {}
-
-      GhostLayerField_T * allocate( IBlock * const block )
-      {
-         auto blocks = blocks_.lock();
-         WALBERLA_CHECK_NOT_NULLPTR( blocks, "Trying to access 'AlwaysInitializeBlockDataHandling' for a block "
-                                             "storage object that doesn't exist anymore" );
-         GhostLayerField_T * field = new GhostLayerField_T ( blocks->getNumberOfXCells( *block ),
-                                                             blocks->getNumberOfYCells( *block ),
-                                                             blocks->getNumberOfZCells( *block ),
-                                                             nrOfGhostLayers_, initValue_, layout_,
-                                                             getAllocator<Value_T>(alignment_) );
-         return field;
-      }
-
-      GhostLayerField_T * reallocate( IBlock * const block )
-      {
-         return allocate(block);
-      }
-
-   private:
-      weak_ptr< StructuredBlockStorage > blocks_;
-
-      uint_t  nrOfGhostLayers_;
-      Value_T initValue_;
-      Layout  layout_;
-      uint_t alignment_;
-   };
-
-
-   //===================================================================================================================
-   //
-   //  Field functions redefined for easier export
-   //
-   //===================================================================================================================
-
-
-   static inline Cell tupleToCell( boost::python::tuple & tuple  )
-   {
-      using boost::python::extract;
-      return Cell (  extract<cell_idx_t>( tuple[0] ),
-                     extract<cell_idx_t>( tuple[1] ),
-                     extract<cell_idx_t>( tuple[2] ) );
-   }
-
-   template<typename Field_T>
-   void field_setCellXYZ( Field_T & field, boost::python::tuple args, const typename Field_T::value_type & value )
-   {
-      using namespace boost::python;
-
-      if ( len(args) < 3 || len(args) > 4 )
-      {
-         PyErr_SetString( PyExc_RuntimeError, "3 or 4 indices required");
-         throw error_already_set();
-      }
-
-      cell_idx_t f = 0;
-      if ( len(args) == 4 )
-         f = extract<cell_idx_t> ( args[3] );
-
-      Cell cell = tupleToCell(args);
-      if ( ! field.coordinatesValid( cell[0], cell[1], cell[2], f ) )
-      {
-         PyErr_SetString( PyExc_IndexError, "Field indices out of bounds");
-         throw error_already_set();
-      }
-      field(cell, f) = value;
-   }
-
-   template<typename Field_T>
-   typename Field_T::value_type field_getCellXYZ( Field_T & field, boost::python::tuple args )
-   {
-      using namespace boost::python;
-      if ( len(args) < 3 || len(args) > 4 )
-      {
-         PyErr_SetString( PyExc_RuntimeError, "3 or 4 indices required");
-         throw error_already_set();
-      }
-
-      cell_idx_t f = 0;
-      if ( len(args) == 4 )
-         f = extract<cell_idx_t> ( args[3] );
-
-      Cell cell = tupleToCell(args);
-      if ( ! field.coordinatesValid( cell[0], cell[1], cell[2], f ) )
-      {
-         PyErr_SetString( PyExc_IndexError, "Field indices out of bounds");
-         throw error_already_set();
-      }
-
-      return field( cell, f );
-   }
-
-
-   template<typename Field_T>
-   boost::python::object field_size( const Field_T & field ) {
-      return boost::python::make_tuple( field.xSize(), field.ySize(), field.zSize(), field.fSize() );
-   }
-
-   template<typename GlField_T>
-   boost::python::object field_sizeWithGhostLayer( const GlField_T & field ) {
-      return boost::python::make_tuple( field.xSizeWithGhostLayer(), field.ySizeWithGhostLayer(), field.zSizeWithGhostLayer(), field.fSize() );
-   }
-
-
-   template<typename Field_T>
-   boost::python::object field_allocSize( const Field_T & field ) {
-      return boost::python::make_tuple( field.xAllocSize(), field.yAllocSize(),
-                                        field.zAllocSize(), field.fAllocSize() );
-   }
-
-
-   template<typename Field_T>
-   boost::python::object field_strides( const Field_T & field ) {
-      return boost::python::make_tuple( field.xStride(), field.yStride(),
-                                        field.zStride(), field.fStride() );
-   }
-
-   template<typename Field_T>
-   boost::python::object field_offsets( const Field_T & field ) {
-      return boost::python::make_tuple( field.xOff(), field.yOff(), field.zOff() );
-   }
-
-
-   template<typename Field_T>
-   boost::python::object field_layout( const Field_T & f ) {
-      if ( f.layout() == field::fzyx ) return boost::python::object( "fzyx" );
-      if ( f.layout() == field::zyxf ) return boost::python::object( "zyxf" );
-
-      return boost::python::object();
-   }
-
-
-   template<typename Field_T>
-   void field_swapDataPointers( Field_T & f1, Field_T & f2 )
-   {
-      if ( ! f1.hasSameAllocSize(f2 ) ||
-           ! f1.hasSameSize( f2)      ||
-             f1.layout() != f2.layout() )
-      {
-         PyErr_SetString( PyExc_ValueError, "The data of fields with different sizes or layout cannot be swapped");
-         throw boost::python::error_already_set();
-      }
-      f1.swapDataPointers( f2 );
-   }
-
-
-   template<typename T>
-   T  FF_getFlag( const FlagField<T> & ff,  const std::string & flag ) {
-      if ( ! ff.flagExists(flag) )
-      {
-         PyErr_SetString( PyExc_ValueError, "No such flag");
-         throw boost::python::error_already_set();
-      }
-      return ff.getFlag( flag );
-   }
-
-   template<typename T>
-   boost::python::object FF_registeredFlags( const FlagField<T> & ff )
-   {
-      std::vector<FlagUID> flags;
-      ff.getAllRegisteredFlags( flags );
-      boost::python::list result;
-
-      for( auto i = flags.begin(); i != flags.end(); ++i )
-         result.append( i->toString() );
-      boost::python::object objectResult = result;
-      return objectResult;
-   }
-
-
-   template<typename T>
-   boost::python::object FF_flagMap( const FlagField<T> & ff )
-   {
-      std::vector<FlagUID> flags;
-      ff.getAllRegisteredFlags( flags );
-      boost::python::dict result;
-
-      for( auto i = flags.begin(); i != flags.end(); ++i )
-         result[ i->toString() ] = ff.getFlag( *i );
-      boost::python::object objectResult = result;
-      return objectResult;
-   }
-
-   template<typename T>
-   boost::python::object FF_registerFlag( FlagField<T> & ff, const std::string & flag, boost::python::object bitNr )
-   {
-      using namespace boost::python;
-
-      try {
-
-         if ( bitNr == object() )
-            return object( ff.registerFlag( FlagUID(flag) ) );
-         else
-         {
-            if ( extract<uint_t>(bitNr ).check() ) {
-               uint_t bit = extract<uint_t>(bitNr);
-               return object( ff.registerFlag( flag, bit ) );
-            }
-            else {
-               PyErr_SetString( PyExc_ValueError, "Parameter bitNr has to be a positive integer");
-               throw boost::python::error_already_set();
-            }
-         }
-      }
-      catch ( std::runtime_error & e ) {
-         PyErr_SetString( PyExc_ValueError, e.what() );
-         throw boost::python::error_already_set();
-      }
-
-   }
-
-   template<typename T>
-   std::string FF_getFlagName( const FlagField<T> & ff, T flag )
-   {
-      try {
-         return ff.getFlagUID( flag ).getIdentifier();
-      }
-      catch ( std::runtime_error & e ) {
-         PyErr_SetString( PyExc_ValueError, e.what() );
-         throw boost::python::error_already_set();
-      }
-   }
-
-
-   template<typename Field_T>
-   boost::python::object copyAdaptorToField( const Field_T & f )
-   {
-      typedef GhostLayerField<typename Field_T::value_type, Field_T::F_SIZE> ResField;
-      auto res = make_shared< ResField > ( f.xSize(), f.ySize(), f.zSize(), f.nrOfGhostLayers() );
-
-      auto srcIt = f.beginWithGhostLayerXYZ();
-      auto dstIt = res->beginWithGhostLayerXYZ();
-      while ( srcIt != f.end() )
-      {
-         for( cell_idx_t fCoord = 0; fCoord < cell_idx_c(Field_T::F_SIZE); ++fCoord )
-            dstIt.getF( fCoord ) = srcIt.getF( fCoord );
-
-         ++srcIt;
-         ++dstIt;
-      }
-      return boost::python::object( res );
-   }
-
-
-
-   //===================================================================================================================
-   //
-   //  Field export
-   //
-   //===================================================================================================================
-
-   template<typename T>
-   void exportFlagFieldIfUnsigned( typename std::enable_if<std::is_unsigned<T>::value >::type* = 0 )
-   {
-      using namespace boost::python;
-
-      class_< FlagField<T> ,
-              shared_ptr<FlagField<T> >,
-              bases<GhostLayerField<T,1> >,
-              boost::noncopyable > ( "FlagField", no_init )
-          .def         ( "registerFlag", &FF_registerFlag<T>   ,  ( arg("flagName"),  arg("bitNr") = object() ) )
-          .def         ( "flag",         &FF_getFlag<T>         )
-          .def         ( "flagName",     &FF_getFlagName<T>     )
-          .add_property( "flags",        &FF_registeredFlags<T> )
-          .add_property( "flagMap",      &FF_flagMap<T>         )
-          ;
-
-   }
-   template<typename T>
-   void exportFlagFieldIfUnsigned( typename std::enable_if< ! std::is_unsigned<T>::value >::type* = 0 )  {}
-
-
-   struct FieldExporter
-   {
-      template< typename FieldType>
-      void operator() ( python_coupling::NonCopyableWrap<FieldType> )
-      {
-         typedef typename FieldType::value_type T;
-         const uint_t F_SIZE = FieldType::F_SIZE;
-         typedef GhostLayerField<T,F_SIZE> GlField_T;
-         typedef Field<T,F_SIZE> Field_T;
-
-         using namespace boost::python;
-
-         class_<Field_T, shared_ptr<Field_T>, boost::noncopyable>( "Field", no_init )
-            .add_property("layout",    &field_layout          < Field_T > )
-            .add_property("size",      &field_size            < Field_T > )
-            .add_property("allocSize", &field_allocSize       < Field_T > )
-            .add_property("strides",   &field_strides         < Field_T > )
-            .add_property("offsets",   &field_offsets         < Field_T > )
-            .def("clone",              &Field_T::clone             , return_value_policy<manage_new_object>())
-            .def("cloneUninitialized", &Field_T::cloneUninitialized, return_value_policy<manage_new_object>())
-            .def("swapDataPointers",   &field_swapDataPointers< Field_T > )
-            .def("__getitem__",        &field_getCellXYZ      < Field_T > )
-            .def("__setitem__",        &field_setCellXYZ      < Field_T > )
-            .def("buffer",             &field_getBufferInterface<T,F_SIZE>, ( arg("withGhostLayers") = false ) );
-            ;
-
-         class_< GlField_T , shared_ptr<GlField_T>, bases<Field_T>,  boost::noncopyable > ( "GhostLayerField", no_init )
-            .add_property("sizeWithGhostLayer", &field_sizeWithGhostLayer<GlField_T> )
-            .add_property("nrOfGhostLayers",    &GlField_T::nrOfGhostLayers     )
-         ;
-
-         if ( F_SIZE == 1 )
-            exportFlagFieldIfUnsigned<T>();
-
-
-         // Field Buffer
-         FieldBufferType<T,F_SIZE>::value.tp_new = PyType_GenericNew;
-         if ( PyType_Ready(& FieldBufferType<T,F_SIZE>::value ) < 0 )
-             return;
-
-         Py_INCREF( (& FieldBufferType<T,F_SIZE>::value ) );
-         PyModule_AddObject( boost::python::scope().ptr(), "FieldBuffer", (PyObject *)&FieldBufferType<T,F_SIZE>::value );
-
-         // Field Buffer with ghost layer
-         FieldBufferTypeGl<T,F_SIZE>::value.tp_new = PyType_GenericNew;
-         if ( PyType_Ready(& FieldBufferTypeGl<T,F_SIZE>::value ) < 0 )
-             return;
-
-         Py_INCREF( (& FieldBufferTypeGl<T,F_SIZE>::value ) );
-         PyModule_AddObject( boost::python::scope().ptr(), "FieldBufferGl", (PyObject *)&FieldBufferTypeGl<T,F_SIZE>::value );
-
-
-         // Field Buffer type
-
-         if (  python_coupling::isTypeRegisteredInBoostPython< FieldAllocator<T> >() == false )
-         {
-            class_< FieldAllocator<T>, shared_ptr<FieldAllocator<T> >, boost::noncopyable> ( "FieldAllocator", no_init )
-                  .def( "incrementReferenceCount", &FieldAllocator<T>::incrementReferenceCount  )
-                  .def( "decrementReferenceCount", &FieldAllocator<T>::decrementReferenceCount  );
-         }
-
-         using field::communication::PackInfo;
-         class_< PackInfo<GlField_T>,
-                 shared_ptr< PackInfo<GlField_T> >,
-                 bases<walberla::communication::UniformPackInfo>,
-                 boost::noncopyable >( "FieldPackInfo", no_init );
-
-
-         using field::communication::UniformMPIDatatypeInfo;
-         class_< UniformMPIDatatypeInfo<GlField_T>,
-                 shared_ptr< UniformMPIDatatypeInfo<GlField_T> >,
-                 bases<walberla::communication::UniformMPIDatatypeInfo>,
-                 boost::noncopyable >( "FieldMPIDataTypeInfo", no_init );
-
-      }
-   };
-
-
-   struct GhostLayerFieldAdaptorExporter
-   {
-      GhostLayerFieldAdaptorExporter(  const std::string & name )
-         : name_ ( name )
-      {}
-
-      template< typename Adaptor>
-      void operator()( python_coupling::NonCopyableWrap<Adaptor> )
-      {
-         using namespace boost::python;
-
-         class_< Adaptor, shared_ptr<Adaptor>, boost::noncopyable> ( name_.c_str(), no_init )
-               .add_property("size",                &field_size              <Adaptor > )
-               .add_property("sizeWithGhostLayer",  &field_sizeWithGhostLayer<Adaptor> )
-               .add_property("nrOfGhostLayers",     &Adaptor::nrOfGhostLayers          )
-               .def("__getitem__",                  &field_getCellXYZ        <Adaptor> )
-               .def("copyToField",                  &copyAdaptorToField      <Adaptor> );
-      }
-
-      std::string name_;
-   };
-
-   //===================================================================================================================
-   //
-   //  createField
-   //
-   //===================================================================================================================
-
-
-   class CreateFieldExporter
-   {
-   public:
-      CreateFieldExporter( uint_t xs, uint_t ys, uint_t zs, uint_t fs, uint_t gl,
-                           Layout layout, const boost::python::object & type, uint_t alignment,
-                           const shared_ptr<boost::python::object> & resultPointer  )
-         : xs_( xs ), ys_(ys), zs_(zs), fs_(fs), gl_(gl),
-           layout_( layout),  type_( type ), alignment_(alignment), resultPointer_( resultPointer )
-      {}
-
-      template< typename FieldType>
-      void operator() ( python_coupling::NonCopyableWrap<FieldType> )
-      {
-         using namespace boost::python;
-         typedef typename FieldType::value_type T;
-         const uint_t F_SIZE = FieldType::F_SIZE;
-
-         if( F_SIZE != fs_ )
-            return;
-
-         if( python_coupling::isCppEqualToPythonType<T>( (PyTypeObject *)type_.ptr() )  )
-         {
-            T initVal = T(); //extract<T> ( initValue_ );
-            *resultPointer_ = object( make_shared< GhostLayerField<T,F_SIZE> >( xs_,ys_,zs_, gl_, initVal, layout_,
-                                                                                getAllocator<T>(alignment_)));
-         }
-      }
-
-   private:
-      uint_t xs_;
-      uint_t ys_;
-      uint_t zs_;
-      uint_t fs_;
-      uint_t gl_;
-      Layout layout_;
-      boost::python::object type_;
-      uint_t alignment_;
-      shared_ptr<boost::python::object> resultPointer_;
-   };
-
-   template<typename FieldTypes>
-   boost::python::object createPythonField( boost::python::list size,
-                                            boost::python::object type,
-                                            uint_t ghostLayers,
-                                            Layout layout,
-                                            uint_t alignment)
-   {
-      using namespace boost::python;
-      uint_t xSize = extract<uint_t> ( size[0] );
-      uint_t ySize = extract<uint_t> ( size[1] );
-      uint_t zSize = extract<uint_t> ( size[2] );
-      uint_t sizeLen = uint_c( len( size ) );
-      uint_t fSize = 1;
-      if ( sizeLen == 4 )
-         fSize = extract<uint_t> ( size[3] );
-
-      if ( ! PyType_Check( type.ptr() ) ) {
-         PyErr_SetString( PyExc_RuntimeError, "Invalid 'type' parameter");
-         throw error_already_set();
-      }
-
-      auto result = make_shared<boost::python::object>();
-      CreateFieldExporter exporter( xSize,ySize, zSize, fSize, ghostLayers, layout, type, alignment, result );
-      python_coupling::for_each_noncopyable_type< FieldTypes >  ( exporter );
-
-      if ( *result == object()  )
-      {
-         PyErr_SetString( PyExc_ValueError, "Cannot create field of this (type,f-size) combination");
-         throw error_already_set();
-      }
-      else {
-         return *result;
-      }
-   }
-
-   //===================================================================================================================
-   //
-   //  createFlagField
-   //
-   //===================================================================================================================
-
-
-   inline boost::python::object createPythonFlagField( boost::python::list size, uint_t nrOfBits, uint_t ghostLayers )
-   {
-      using namespace boost::python;
-
-      uint_t sizeLen = uint_c( len( size ) );
-      if ( sizeLen != 3 ) {
-         PyErr_SetString( PyExc_ValueError, "Size parameter has to be a list of length 3");
-         throw error_already_set();
-      }
-      uint_t xSize = extract<uint_t> ( size[0] );
-      uint_t ySize = extract<uint_t> ( size[1] );
-      uint_t zSize = extract<uint_t> ( size[2] );
-
-      if( nrOfBits == 8 )        return object( make_shared< FlagField< uint8_t > >( xSize, ySize, zSize, ghostLayers )  );
-      else if ( nrOfBits == 16 ) return object( make_shared< FlagField< uint16_t> >( xSize, ySize, zSize, ghostLayers )  );
-      else if ( nrOfBits == 32 ) return object( make_shared< FlagField< uint32_t> >( xSize, ySize, zSize, ghostLayers )  );
-      else if ( nrOfBits == 64 ) return object( make_shared< FlagField< uint64_t> >( xSize, ySize, zSize, ghostLayers )  );
-      else
-      {
-         PyErr_SetString( PyExc_ValueError, "Allowed values for number of bits are: 8,16,32,64");
-         throw error_already_set();
-      }
-   }
-
-
-   //===================================================================================================================
-   //
-   //  addToStorage
-   //
-   //===================================================================================================================
-
-   class AddToStorageExporter
-   {
-   public:
-      AddToStorageExporter(const shared_ptr<StructuredBlockStorage> & blocks,
-                           const std::string & name, uint_t fs, uint_t gl, Layout layout,
-                           const boost::python::object & type,
-                           const boost::python::object & initObj,
-                           uint_t alignment )
-         : blocks_( blocks ), name_( name ), fs_( fs ),
-           gl_(gl),layout_( layout),  type_( type ), initObj_( initObj), alignment_(alignment), found_(false)
-      {}
-
-      template< typename FieldType>
-      void operator() ( python_coupling::NonCopyableWrap<FieldType> )
-      {
-         using namespace boost::python;
-         typedef typename FieldType::value_type T;
-         const uint_t F_SIZE = FieldType::F_SIZE;
-
-         if( F_SIZE != fs_ )
-            return;
-
-         if( !found_ && python_coupling::isCppEqualToPythonType<T>( (PyTypeObject *)type_.ptr() )  )
-         {
-            typedef internal::GhostLayerFieldDataHandling< GhostLayerField<T,F_SIZE > > DataHandling;
-            if ( initObj_ == object() ) {
-               auto dataHandling = walberla::make_shared< DataHandling >( blocks_, gl_, T(), layout_, alignment_ );
-               blocks_->addBlockData( dataHandling, name_ );
-            }
-            else {
-               auto dataHandling = walberla::make_shared< DataHandling >( blocks_, gl_, extract<T>(initObj_), layout_, alignment_ );
-               blocks_->addBlockData( dataHandling, name_ );
-            }
-            found_ = true;
-         }
-      }
-
-      bool successful() const { return found_; }
-   private:
-      shared_ptr< StructuredBlockStorage > blocks_;
-      std::string name_;
-      uint_t fs_;
-      uint_t gl_;
-      Layout layout_;
-      boost::python::object type_;
-      boost::python::object initObj_;
-      uint_t alignment_;
-      bool found_;
-   };
-
-   template<typename FieldTypes>
-   void addToStorage( const shared_ptr<StructuredBlockStorage> & blocks, const std::string & name,
-                      boost::python::object type, uint_t fs, uint_t gl, Layout layout, boost::python::object initValue,
-                      uint_t alignment)
-   {
-      using namespace boost::python;
-
-      if ( ! PyType_Check( type.ptr() ) ) {
-         PyErr_SetString( PyExc_RuntimeError, "Invalid 'type' parameter");
-         throw error_already_set();
-      }
-
-      auto result = make_shared<boost::python::object>();
-      AddToStorageExporter exporter( blocks, name, fs, gl, layout, type, initValue, alignment );
-      python_coupling::for_each_noncopyable_type< FieldTypes >  ( std::ref(exporter) );
-
-      if ( ! exporter.successful() ) {
-         PyErr_SetString( PyExc_ValueError, "Adding Field failed.");
-         throw error_already_set();
-      }
-   }
-
-
-   inline void addFlagFieldToStorage( const shared_ptr<StructuredBlockStorage> & blocks, const std::string & name,
-                              uint_t nrOfBits, uint_t gl  )
-   {
-      if( nrOfBits == 8 )        field::addFlagFieldToStorage< FlagField<uint8_t>  > ( blocks, name, gl );
-      else if ( nrOfBits == 16 ) field::addFlagFieldToStorage< FlagField<uint16_t> > ( blocks, name, gl );
-      else if ( nrOfBits == 32 ) field::addFlagFieldToStorage< FlagField<uint32_t> > ( blocks, name, gl );
-      else if ( nrOfBits == 64 ) field::addFlagFieldToStorage< FlagField<uint64_t> > ( blocks, name, gl );
-      else
-      {
-         PyErr_SetString( PyExc_ValueError, "Allowed values for number of bits are: 8,16,32,64");
-         throw boost::python::error_already_set();
-      }
-   }
-
-   //===================================================================================================================
-   //
-   //  createVTKWriter
-   //
-   //===================================================================================================================
-
-   class CreateVTKWriterExporter
-   {
-   public:
-       CreateVTKWriterExporter( const shared_ptr<StructuredBlockStorage> & blocks,
-                                ConstBlockDataID fieldId, const std::string & vtkName)
-         : blocks_( blocks ), fieldId_(fieldId), vtkName_( vtkName )
-      {}
-
-      template< typename FieldType>
-      void operator() ( python_coupling::NonCopyableWrap<FieldType> )
-      {
-         using namespace boost::python;
-
-         IBlock * firstBlock =  & ( * blocks_->begin() );
-         if( firstBlock->isDataClassOrSubclassOf<FieldType>(fieldId_) )
-            writer_ = shared_ptr<field::VTKWriter<FieldType> >( new field::VTKWriter<FieldType>(fieldId_, vtkName_));
-      }
-
-      shared_ptr< vtk::BlockCellDataWriterInterface > getCreatedWriter() {
-         return writer_;
-      }
-
-   private:
-      shared_ptr< vtk::BlockCellDataWriterInterface > writer_;
-      shared_ptr< StructuredBlockStorage > blocks_;
-      ConstBlockDataID fieldId_;
-      std::string vtkName_;
-   };
-
-
-   template<typename FieldTypes>
-   inline shared_ptr<vtk::BlockCellDataWriterInterface> createVTKWriter(const shared_ptr<StructuredBlockStorage> & blocks,
-                                                                        const std::string & name,
-                                                                        const std::string & nameInVtkOutput = "")
-   {
-      std::string vtkName = nameInVtkOutput;
-      if( vtkName.size() == 0)
-         vtkName = name;
-
-      if ( blocks->begin() == blocks->end() )
-         return shared_ptr<vtk::BlockCellDataWriterInterface>();
-      auto fieldID = python_coupling::blockDataIDFromString( *blocks, name );
-
-      CreateVTKWriterExporter exporter(blocks, fieldID, vtkName);
-      python_coupling::for_each_noncopyable_type< FieldTypes >  ( std::ref(exporter) );
-      if ( ! exporter.getCreatedWriter() ) {
-         PyErr_SetString( PyExc_ValueError, "Failed to create writer");
-         throw boost::python::error_already_set();
-      }
-      else {
-         return exporter.getCreatedWriter();
-      }
-   }
-
-   //===================================================================================================================
-   //
-   //  createFlagFieldVTKWriter
-   //
-   //===================================================================================================================
-
-   class CreateFlagFieldVTKWriterExporter
-   {
-   public:
-      CreateFlagFieldVTKWriterExporter( const shared_ptr<StructuredBlockStorage> & blocks,
-                                        ConstBlockDataID fieldId, const std::string & vtkName,
-                                        boost::python::dict flagMapping)
-         : blocks_( blocks ), fieldId_(fieldId), vtkName_( vtkName ), flagMapping_( flagMapping )
-      {}
-
-      template< typename FieldType>
-      void operator() ( python_coupling::NonCopyableWrap<FieldType> )
-      {
-         using namespace boost::python;
-
-         IBlock * firstBlock =  & ( * blocks_->begin() );
-         if( firstBlock->isDataClassOrSubclassOf<FieldType>(fieldId_) )
-         {
-            typedef typename FieldType::flag_t flag_t;
-            typedef field::FlagFieldMapping<FieldType, flag_t> FFMapping;
-            auto uncastedWriter = shared_ptr<FFMapping >( new FFMapping(fieldId_, vtkName_));
-            writer_ = uncastedWriter;
-            auto keys = flagMapping_.keys();
-
-            for( int i=0; i < len(keys); ++i ) {
-               uncastedWriter->addMapping(FlagUID(extract<std::string>(keys[i])),
-                                          extract<flag_t>(flagMapping_[keys[i]]) );
-            }
-         }
-
-      }
-
-      shared_ptr< vtk::BlockCellDataWriterInterface > getCreatedWriter() {
-         return writer_;
-      }
-
-   private:
-      shared_ptr< vtk::BlockCellDataWriterInterface > writer_;
-      shared_ptr< StructuredBlockStorage > blocks_;
-      ConstBlockDataID fieldId_;
-      std::string vtkName_;
-      boost::python::dict flagMapping_;
-   };
-
-
-   template<typename FieldTypes>
-   inline shared_ptr<vtk::BlockCellDataWriterInterface> createFlagFieldVTKWriter(const shared_ptr<StructuredBlockStorage> & blocks,
-                                                                                 const std::string & name,
-                                                                                 boost::python::dict flagMapping,
-                                                                                 const std::string & nameInVtkOutput = "" )
-   {
-      std::string vtkName = nameInVtkOutput;
-      if( vtkName.size() == 0)
-         vtkName = name;
-
-      if ( blocks->begin() == blocks->end() )
-         return shared_ptr<vtk::BlockCellDataWriterInterface>();
-      auto fieldID = python_coupling::blockDataIDFromString( *blocks, name );
-
-      CreateFlagFieldVTKWriterExporter exporter(blocks, fieldID, vtkName, flagMapping);
-      python_coupling::for_each_noncopyable_type< FieldTypes >  ( std::ref(exporter) );
-      if ( ! exporter.getCreatedWriter() ) {
-         PyErr_SetString( PyExc_ValueError, "Failed to create writer");
-         throw boost::python::error_already_set();
-      }
-      else {
-         return exporter.getCreatedWriter();
-      }
-   }
-
-
-   //===================================================================================================================
-   //
-   //  createBinarizationFieldWriter
-   //
-   //===================================================================================================================
-
-   class CreateBinarizationVTKWriterExporter
-   {
-   public:
-      CreateBinarizationVTKWriterExporter( const shared_ptr<StructuredBlockStorage> & blocks,
-                                           ConstBlockDataID fieldId, const std::string & vtkName, uint_t mask )
-         : blocks_( blocks ), fieldId_(fieldId), vtkName_( vtkName ), mask_(mask)
-      {}
-
-      template< typename FieldType>
-      void operator() ( python_coupling::NonCopyableWrap<FieldType> )
-      {
-         using namespace boost::python;
-
-         IBlock * firstBlock =  & ( * blocks_->begin() );
-         if( firstBlock->isDataClassOrSubclassOf<FieldType>(fieldId_) )
-         {
-            typedef field::BinarizationFieldWriter<FieldType> Writer;
-            writer_ = shared_ptr<Writer>( new Writer(fieldId_, vtkName_,
-                                                     static_cast<typename FieldType::value_type>(mask_) ));
-         }
-      }
-
-      shared_ptr< vtk::BlockCellDataWriterInterface > getCreatedWriter() {
-         return writer_;
-      }
-
-   private:
-      shared_ptr< vtk::BlockCellDataWriterInterface > writer_;
-      shared_ptr< StructuredBlockStorage > blocks_;
-      ConstBlockDataID fieldId_;
-      std::string vtkName_;
-      uint_t mask_;
-   };
-
-
-   template<typename FieldTypes>
-   inline shared_ptr<vtk::BlockCellDataWriterInterface> createBinarizationVTKWriter(const shared_ptr<StructuredBlockStorage> & blocks,
-                                                                                    const std::string & name,
-                                                                                    uint_t mask,
-                                                                                    const std::string & nameInVtkOutput = "" )
-   {
-      std::string vtkName = nameInVtkOutput;
-      if( vtkName.size() == 0)
-         vtkName = name;
-
-      if ( blocks->begin() == blocks->end() )
-         return shared_ptr<vtk::BlockCellDataWriterInterface>();
-      auto fieldID = python_coupling::blockDataIDFromString( *blocks, name );
-
-      CreateBinarizationVTKWriterExporter exporter(blocks, fieldID, vtkName, mask);
-      python_coupling::for_each_noncopyable_type< FieldTypes >  ( std::ref(exporter) );
-      if ( ! exporter.getCreatedWriter() ) {
-         PyErr_SetString( PyExc_ValueError, "Failed to create writer");
-         throw boost::python::error_already_set();
-      }
-      else {
-         return exporter.getCreatedWriter();
-      }
-   }
-
-
-} // namespace internal
-
-
-
-
-template<typename FieldTypes >
-void exportFields()
-{
-   using namespace boost::python;
-
-   enum_<Layout>("Layout")
-       .value("fzyx", fzyx)
-       .value("zyxf", zyxf)
-       .export_values();
-
-   python_coupling::for_each_noncopyable_type< FieldTypes > ( internal::FieldExporter() );
-
-   def( "createField", &internal::createPythonField<FieldTypes>, ( ( arg("size")                    ),
-                                                                   ( arg("type")                    ),
-                                                                   ( arg("ghostLayers") = uint_t(1) ),
-                                                                   ( arg("layout")      = zyxf      ),
-                                                                   ( arg("alignment")   = 0         )) );
-
-   def( "createFlagField", &internal::createPythonFlagField, ( ( arg("size")                      ),
-                                                               ( arg("nrOfBits")    = uint_t(32)  ),
-                                                               ( arg("ghostLayers") = uint_t(1)   )  ) );
-
-   def( "addToStorage",    &internal::addToStorage<FieldTypes>, ( ( arg("blocks")                  ),
-                                                                  ( arg("name")                    ),
-                                                                  ( arg("type")                    ),
-                                                                  ( arg("fSize")       = 1         ),
-                                                                  ( arg("ghostLayers") = uint_t(1) ),
-                                                                  ( arg("layout")      = zyxf      ),
-                                                                  ( arg("initValue")   = object()  ),
-                                                                  ( arg("alignment")   = 0         ) ) );
-
-   def( "addFlagFieldToStorage",&internal::addFlagFieldToStorage, ( ( arg("blocks")                  ),
-                                                                    ( arg("name")                    ),
-                                                                    ( arg("nrOfBits")=8              ),
-                                                                    ( arg("ghostLayers") = uint_t(1) ) ) );
-
-   def( "createVTKWriter", &internal::createVTKWriter<FieldTypes>, ( arg("blocks"), arg("name"), arg("vtkName")="" ));
-
-
-   typedef boost::mpl::vector<
-           FlagField<uint8_t>,
-           FlagField<uint16_t>,
-           FlagField<uint32_t>,
-           FlagField<uint64_t> > FlagFields;
-
-   def( "createFlagFieldVTKWriter", &internal::createFlagFieldVTKWriter<FlagFields>,
-                                   ( arg("blocks"), arg("name"), arg("flagMapping"), arg("vtkName")="" ));
-
-
-   typedef boost::mpl::vector<
-           Field<uint8_t,1 >,
-           Field<uint16_t, 1>,
-           Field<uint32_t, 1>,
-           Field<uint64_t, 1> > UintFields;
-
-   def( "createBinarizationVTKWriter", &internal::createBinarizationVTKWriter<UintFields>,
-        ( arg("blocks"), arg("name"), arg("mask"), arg("vtkName")="" ));
-}
-
-
-template<typename AdaptorTypes>
-void exportGhostLayerFieldAdaptors()
-{
-   python_coupling::for_each_noncopyable_type< AdaptorTypes > ( internal::GhostLayerFieldAdaptorExporter("FieldAdaptor") );
-}
-
-template<typename AdaptorType>
-void exportGhostLayerFieldAdaptor()
-{
-   typedef boost::mpl::vector< AdaptorType > AdaptorTypes;
-   exportGhostLayerFieldAdaptors<AdaptorTypes>( );
-}
-
-
-
-
-} // namespace field
-} // namespace walberla
-
-
diff --git a/src/field/python/GatherExport.h b/src/field/python/GatherExport.h
deleted file mode 100644
index 1105caa000f61bf0a3e7454ff725ee8420ecd38c..0000000000000000000000000000000000000000
--- a/src/field/python/GatherExport.h
+++ /dev/null
@@ -1,38 +0,0 @@
-//======================================================================================================================
-//
-//  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 GatherExport.h
-//! \ingroup field
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-
-namespace walberla {
-namespace field {
-
-
-template<typename FieldTypes >
-void exportGatherFunctions();
-
-
-
-} // namespace field
-} // namespace walberla
-
-
-#include "GatherExport.impl.h"
diff --git a/src/field/python/GatherExport.impl.h b/src/field/python/GatherExport.impl.h
deleted file mode 100644
index 42f66565c21de0d708f2f00f3f916cf770e84ce1..0000000000000000000000000000000000000000
--- a/src/field/python/GatherExport.impl.h
+++ /dev/null
@@ -1,109 +0,0 @@
-//======================================================================================================================
-//
-//  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 GatherExport.impl.h
-//! \ingroup field
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#include "field/Gather.h"
-#include "python_coupling/helper/MplHelpers.h"
-#include "python_coupling/helper/BlockStorageExportHelpers.h"
-#include "python_coupling/helper/ModuleScope.h"
-#include "python_coupling/helper/SliceToCellInterval.h"
-
-
-namespace walberla {
-namespace field {
-
-
-namespace internal {
-
-   //===================================================================================================================
-   //
-   //  Gather
-   //
-   //===================================================================================================================
-
-
-   template<typename Field_T>
-   boost::python::object gatherToObject( const shared_ptr<StructuredBlockStorage> & blocks, BlockDataID fieldID,
-                                         CellInterval boundingBox = CellInterval(), int targetRank = 0 )
-   {
-      typedef Field< typename Field_T::value_type, Field_T::F_SIZE > ResultField;
-      auto result = make_shared< ResultField > ( 0,0,0 );
-      field::gather< Field_T, ResultField > ( *result, blocks, fieldID, boundingBox, targetRank, MPI_COMM_WORLD );
-
-      if ( MPIManager::instance()->worldRank() == targetRank )
-         return boost::python::object(result);
-      else
-         return boost::python::object();
-   }
-
-   FunctionExporterClass( gatherToObject,
-                          boost::python::object( const shared_ptr<StructuredBlockStorage> &,
-                                                 BlockDataID, CellInterval,int ) );
-
-   template<typename FieldTypes>
-   static boost::python::object gatherWrapper (  const shared_ptr<StructuredBlockStorage> & blocks, const std::string & blockDataStr,
-                                                 const boost::python::tuple & slice,  int targetRank = 0 )
-   {
-      using namespace boost::python;
-
-      auto fieldID = python_coupling::blockDataIDFromString( *blocks, blockDataStr );
-      CellInterval boundingBox = python_coupling::globalPythonSliceToCellInterval( blocks, slice );
-
-      if ( blocks->begin() == blocks->end() ) {
-         // if no blocks are on this process the field::gather function can be called with any type
-         // however we have to call it, otherwise a deadlock occurs
-         gatherToObject< Field<real_t,1> > ( blocks, fieldID, boundingBox, targetRank );
-         return object();
-      }
-
-      IBlock * firstBlock =  & ( * blocks->begin() );
-      python_coupling::Dispatcher<FieldTypes, Exporter_gatherToObject > dispatcher( firstBlock );
-      auto func = dispatcher( fieldID );
-      if ( !func )
-      {
-         PyErr_SetString( PyExc_RuntimeError, "This function cannot handle this type of block data.");
-         throw error_already_set();
-      }
-      else
-      {
-         return func( blocks, fieldID, boundingBox, targetRank) ;
-      }
-   }
-
-} // namespace internal
-
-
-
-template<typename FieldTypes >
-void exportGatherFunctions()
-{
-   using namespace boost::python;
-   python_coupling::ModuleScope fieldModule( "field" );
-
-   def( "gather",  &internal::gatherWrapper<FieldTypes>,  ( arg("blocks"), arg("blockDataName"), arg("slice"), arg("targetRank") = 0 ) );
-}
-
-
-
-
-} // namespace moduleName
-} // namespace walberla
-
-
diff --git a/src/field/refinement/PackInfo.h b/src/field/refinement/PackInfo.h
index bfc485289321f556afacd3639c4d3de3b4ff406a..bc4f683440109031d0dd9d53bc0ed6167430af66 100644
--- a/src/field/refinement/PackInfo.h
+++ b/src/field/refinement/PackInfo.h
@@ -39,25 +39,25 @@ class PackInfo : public blockforest::communication::NonUniformPackInfo
 public:
 
    PackInfo( const BlockDataID & fieldId ) : fieldId_( fieldId ) {}
-   virtual ~PackInfo() {}
+   ~PackInfo() override = default;
 
-   bool constantDataExchange() const { return true; }
-   bool threadsafeReceiving()  const { return true; }
+   bool constantDataExchange() const override { return true; }
+   bool threadsafeReceiving()  const override { return true; }
 
-   void       unpackDataEqualLevel( Block * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer );
-   void communicateLocalEqualLevel( const Block * sender, Block * receiver, stencil::Direction dir );
+   void       unpackDataEqualLevel( Block * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer ) override;
+   void communicateLocalEqualLevel( const Block * sender, Block * receiver, stencil::Direction dir ) override;
 
-   void       unpackDataCoarseToFine( Block * fineReceiver, const BlockID & coarseSender, stencil::Direction dir, mpi::RecvBuffer & buffer );
-   void communicateLocalCoarseToFine( const Block * coarseSender, Block * fineReceiver, stencil::Direction dir );
+   void       unpackDataCoarseToFine( Block * fineReceiver, const BlockID & coarseSender, stencil::Direction dir, mpi::RecvBuffer & buffer ) override;
+   void communicateLocalCoarseToFine( const Block * coarseSender, Block * fineReceiver, stencil::Direction dir ) override;
 
-   void       unpackDataFineToCoarse( Block * coarseReceiver, const BlockID & fineSender, stencil::Direction dir, mpi::RecvBuffer & buffer );
-   void communicateLocalFineToCoarse( const Block * fineSender, Block * coarseReceiver, stencil::Direction dir );
+   void       unpackDataFineToCoarse( Block * coarseReceiver, const BlockID & fineSender, stencil::Direction dir, mpi::RecvBuffer & buffer ) override;
+   void communicateLocalFineToCoarse( const Block * fineSender, Block * coarseReceiver, stencil::Direction dir ) override;
 
 protected:
 
-   void packDataEqualLevelImpl( const Block * sender, stencil::Direction dir, mpi::SendBuffer & buffer ) const;
-   void packDataCoarseToFineImpl( const Block * coarseSender, const BlockID &   fineReceiver, stencil::Direction dir, mpi::SendBuffer & buffer ) const;
-   void packDataFineToCoarseImpl( const Block *   fineSender, const BlockID & coarseReceiver, stencil::Direction dir, mpi::SendBuffer & buffer ) const;
+   void packDataEqualLevelImpl( const Block * sender, stencil::Direction dir, mpi::SendBuffer & buffer ) const override;
+   void packDataCoarseToFineImpl( const Block * coarseSender, const BlockID &   fineReceiver, stencil::Direction dir, mpi::SendBuffer & buffer ) const override;
+   void packDataFineToCoarseImpl( const Block *   fineSender, const BlockID & coarseReceiver, stencil::Direction dir, mpi::SendBuffer & buffer ) const override;
 
    ///////////////////////////////////////////////////////////////////////
    // Helper functions for determining packing/unpacking cell intervals //
diff --git a/src/field/vtk/FlagFieldCellFilter.h b/src/field/vtk/FlagFieldCellFilter.h
index 708c5234ee6a446fbfa273a1c13992009b8a6de4..d82c3a13e82cbab5851f7572ef44c02a9bb186f5 100644
--- a/src/field/vtk/FlagFieldCellFilter.h
+++ b/src/field/vtk/FlagFieldCellFilter.h
@@ -35,7 +35,7 @@ namespace field {
 template< typename FlagField_T >
 class FlagFieldCellFilter {
 private:
-   typedef typename FlagField_T::flag_t flag_t;
+   using flag_t = typename FlagField_T::flag_t;
 public:
 
    FlagFieldCellFilter( const ConstBlockDataID flags ) : flagField_( flags ) {}
diff --git a/src/field/vtk/FlagFieldMapping.h b/src/field/vtk/FlagFieldMapping.h
index 8b25ce58e6dea759214898243d717ce5ab97fde8..f8cb572b5682f4faaed44832758f45efb5886fa8 100644
--- a/src/field/vtk/FlagFieldMapping.h
+++ b/src/field/vtk/FlagFieldMapping.h
@@ -34,14 +34,14 @@ template< typename FlagField_T, typename T >
 class FlagFieldMapping : public vtk::BlockCellDataWriter<T,1>
 {
 private:
-   typedef typename FlagField_T::flag_t flag_t;
+   using flag_t = typename FlagField_T::flag_t;
 public:
 
    FlagFieldMapping( const ConstBlockDataID flagId, const std::string& id ) :
       vtk::BlockCellDataWriter<T,1>( id ), flagId_( flagId ), flagField_( NULL ) {}
 
    FlagFieldMapping( const ConstBlockDataID flagId, const std::string& id, const std::map< FlagUID, T > mapping ) :
-      vtk::BlockCellDataWriter<T,1>( id ), flagId_( flagId ), flagField_( NULL ), mapping_( mapping ) {}
+      vtk::BlockCellDataWriter<T,1>( id ), flagId_( flagId ), flagField_( nullptr ), mapping_( mapping ) {}
 
    void addMapping( const FlagUID& flag, const T& value )
    {
@@ -51,7 +51,7 @@ public:
 
 protected:
 
-   void configure()
+   void configure() override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( this->block_ );
       flagField_ = this->block_->template getData< FlagField_T >( flagId_ );
@@ -64,7 +64,7 @@ protected:
       }
    }
 
-   T evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t /*f*/ )
+   T evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t /*f*/ ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( flagField_ );
       T result = 0;
@@ -86,7 +86,7 @@ protected:
 template<typename FieldType, typename TargetType=uint8_t>
 class BinarizationFieldWriter : public vtk::BlockCellDataWriter<TargetType,1>
 {
-   typedef typename FieldType::value_type SrcType;
+   using SrcType = typename FieldType::value_type;
 
 public:
    BinarizationFieldWriter( const ConstBlockDataID fieldID, const std::string& id, SrcType mask) :
diff --git a/src/field/vtk/VTKWriter.h b/src/field/vtk/VTKWriter.h
index c83033fe81a06a54c7a0874ceb8581fc07b6f353..513b920e1b1334cf19c3816cba394496b750e2d1 100644
--- a/src/field/vtk/VTKWriter.h
+++ b/src/field/vtk/VTKWriter.h
@@ -103,21 +103,21 @@ class VTKWriter : public vtk::BlockCellDataWriter< OutputType,
                                                    VectorTrait<typename Field_T::value_type>::F_SIZE * Field_T::F_SIZE >
 {
 public:
-   typedef VectorTrait<typename Field_T::value_type > OutputTrait;
+   using OutputTrait = VectorTrait<typename Field_T::value_type>;
 
-   typedef vtk::BlockCellDataWriter<OutputType, OutputTrait::F_SIZE * Field_T::F_SIZE> base_t;
+   using base_t = vtk::BlockCellDataWriter<OutputType, OutputTrait::F_SIZE * Field_T::F_SIZE>;
 
    VTKWriter( const ConstBlockDataID bdid, const std::string& id ) :
-      base_t( id ), bdid_( bdid ), field_( NULL ) {}
+      base_t( id ), bdid_( bdid ), field_( nullptr ) {}
 
 protected:
 
-   void configure() {
+   void configure() override {
       WALBERLA_ASSERT_NOT_NULLPTR( this->block_ );
       field_ = this->block_->template getData< Field_T >( bdid_ );
    }
 
-   OutputType evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f )
+   OutputType evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( field_ );
 
@@ -192,7 +192,7 @@ inline vtk::VTKOutput::Write createVTKOutput  ( const ConstBlockDataID & fieldId
                                                 const Set<SUID>& incompatibleStates = Set<SUID>::emptySet(),
                                                 bool useMPIIO = true, const uint_t initialExecutionCount = 0 )
 {
-   typedef typename VectorTrait<typename Field_T::value_type >::OutputType OutputType;
+   using OutputType = typename VectorTrait<typename Field_T::value_type>::OutputType;
 
    return createVTKOutput<Field_T, OutputType> (
                fieldId, blocks, identifier, writeFrequency, ghostLayers, forcePVTU, baseFolder,executionFolder,
@@ -247,7 +247,7 @@ inline vtk::VTKOutput::Write createScalingVTKOutput  ( const ConstBlockDataID &
                                                        const Set<SUID>& incompatibleStates = Set<SUID>::emptySet(),
                                                        bool useMPIIO = true, const uint_t initialExecutionCount = 0 )
 {
-   typedef typename VectorTrait<typename Field_T::value_type >::OutputType OutputType;
+   using OutputType = typename VectorTrait<typename Field_T::value_type>::OutputType;
 
    return createScalingVTKOutput<Field_T, OutputType> (
                fieldId, blocks, identifier, writeFrequency, factor, ghostLayers, forcePVTU, baseFolder,executionFolder,
diff --git a/src/gather/CellGatherPackInfo.h b/src/gather/CellGatherPackInfo.h
index d4cee6c5dff688bdadb1cf2726c02713be98c356..e12e5ab6f205f5ca11f3762da6a471366185e144 100644
--- a/src/gather/CellGatherPackInfo.h
+++ b/src/gather/CellGatherPackInfo.h
@@ -79,12 +79,12 @@ public:
    /*! \name Packing Interface  */
    //@{
 
-   virtual void packData  ( const IBlock * sender,
-                            mpi::SendBuffer & outBuffer );
+   void packData  ( const IBlock * sender,
+                            mpi::SendBuffer & outBuffer ) override;
 
-   virtual void unpackData( mpi::RecvBuffer & buffer );
+   void unpackData( mpi::RecvBuffer & buffer ) override;
 
-   virtual void gatherFinished();
+   void gatherFinished() override;
    //@}
    //*******************************************************************************************************************
 
@@ -124,7 +124,7 @@ protected:
    shared_ptr<DataProcessor> dataProcessor_;
 
    /// Helper class for sorting the receivedData array according to t-value
-   struct Compare : public std::binary_function<std::vector<real_t> , std::vector<real_t> ,bool>
+   struct Compare : public std::function<bool(std::vector<real_t> , std::vector<real_t>)>
    {
       inline bool operator()(const std::vector<real_t> & v1, const std::vector<real_t> & v2) const {
          return v1[0] < v2[0];
diff --git a/src/gather/CurveGatherPackInfo.h b/src/gather/CurveGatherPackInfo.h
index f23b18ae6abc04845bedef1fb39e748250fd5243..6bdd3bab099c0a7cb4c9c5c5ccf94b6e0e1ff1a8 100644
--- a/src/gather/CurveGatherPackInfo.h
+++ b/src/gather/CurveGatherPackInfo.h
@@ -106,7 +106,7 @@ class CurveGatherPackInfo : public GatherPackInfo
                            const shared_ptr<DataProcessor> & dp);
 
 
-      virtual ~CurveGatherPackInfo() {}
+      ~CurveGatherPackInfo() override {}
 
       //@}
       //****************************************************************************************************************
@@ -117,13 +117,13 @@ class CurveGatherPackInfo : public GatherPackInfo
       /*! \name Packing Interface  */
       //@{
 
-      virtual void packData  ( const IBlock * sender,
-                               mpi::SendBuffer & outBuffer );
+      void packData  ( const IBlock * sender,
+                               mpi::SendBuffer & outBuffer ) override;
 
-      virtual void unpackData( mpi::RecvBuffer & buffer );
+      void unpackData( mpi::RecvBuffer & buffer ) override;
 
 
-      virtual void gatherFinished();
+      void gatherFinished() override;
       //@}
       //****************************************************************************************************************
 
@@ -193,7 +193,7 @@ class CurveGatherPackInfo : public GatherPackInfo
 
 
       /// Helper class for sorting the receivedData array according to t-value
-      struct Compare : public std::binary_function<std::vector<real_t> , std::vector<real_t> ,bool>
+      struct Compare : public std::function<bool(std::vector<real_t> , std::vector<real_t>)>
       {
          inline bool operator()(const std::vector<real_t> & v1, const std::vector<real_t> & v2) const {
             return v1[0] < v2[0];
diff --git a/src/gather/FileGatherScheme.h b/src/gather/FileGatherScheme.h
index b7409573f706fe66d02dbe5e829daf28159176b4..7a48b0f10cc831bdc670139620a7d327d6f5425c 100644
--- a/src/gather/FileGatherScheme.h
+++ b/src/gather/FileGatherScheme.h
@@ -106,7 +106,7 @@ namespace gather
 
          domain_decomposition::BlockStorage  & blocks_;
 
-         typedef std::vector<shared_ptr<GatherPackInfo> > PackInfoVector;
+         using PackInfoVector = std::vector<shared_ptr<GatherPackInfo>>;
          PackInfoVector  packInfos_; ///< all registered PackInfos
 
          /// To generated unique filenames for every FileCollectorScheme
diff --git a/src/gather/GatherPackInfo.h b/src/gather/GatherPackInfo.h
index a4688bc0e75f42aa713ff828f62cd8e5343d99ad..640866581544b22253292c5db253c9d2790ea74c 100644
--- a/src/gather/GatherPackInfo.h
+++ b/src/gather/GatherPackInfo.h
@@ -38,7 +38,7 @@ namespace gather {
    class GatherPackInfo
    {
       public:
-         virtual ~GatherPackInfo() {}
+         virtual ~GatherPackInfo() = default;
 
          /**********************************************************************************************************//**
          * Packs all data to be gathered into the given buffer
diff --git a/src/gather/GnuPlotGraphWriter.h b/src/gather/GnuPlotGraphWriter.h
index c8a70fbfebf1f1f42a9bc8cb1d28a4769877b4f8..48d09f61a13a87da7e22564fde56559cc4795f87 100644
--- a/src/gather/GnuPlotGraphWriter.h
+++ b/src/gather/GnuPlotGraphWriter.h
@@ -59,7 +59,7 @@ class GnuPlotGraphWriter : public DataProcessor
          ++callNr;
       }
 
-      virtual void process(const std::vector<std::vector<real_t> > & data)  { writeDataSet(data); }
+      void process(const std::vector<std::vector<real_t> > & data) override  { writeDataSet(data); }
 
    private:
       std::string filename_;
diff --git a/src/gather/MPIGatherScheme.h b/src/gather/MPIGatherScheme.h
index 3d06db3cabd5df2481ae986fa3f2a93ceff5d665..e9270289ba1a1b4ff4b5d445e91fd679cc450b01 100644
--- a/src/gather/MPIGatherScheme.h
+++ b/src/gather/MPIGatherScheme.h
@@ -102,7 +102,7 @@ class MPIGatherScheme
 
 
    private:
-      typedef std::vector<shared_ptr<GatherPackInfo> > PackInfoVector;
+      using PackInfoVector = std::vector<shared_ptr<GatherPackInfo>>;
 
       domain_decomposition::BlockStorage  & blocks_;
       PackInfoVector                        packInfos_;               ///< all registered PackInfos
diff --git a/src/geometry/GeometricalFunctions.cpp b/src/geometry/GeometricalFunctions.cpp
index e904312d97b483058ee6f8551142898b2c6ae3aa..3ae39d9ee041d1ccef4ca2bfed7e65ce52e8c389 100644
--- a/src/geometry/GeometricalFunctions.cpp
+++ b/src/geometry/GeometricalFunctions.cpp
@@ -64,6 +64,7 @@ namespace geometry {
 void getClosestLineBoxPoints( const Vector3<real_t>& p1, const Vector3<real_t>& p2, const Vector3<real_t>& c, const Matrix3<real_t>& R,
                               const Vector3<real_t>& side, Vector3<real_t>& lret, Vector3<real_t>& bret )
 {
+   WALBERLA_ABORT("This function produces incorrect results! See https://i10git.cs.fau.de/walberla/walberla/-/issues/25");
    using namespace walberla::math;
    //----- Note: All computations will be performed in the box-relative coordinate-system -----
 
diff --git a/src/geometry/InitBoundaryHandling.h b/src/geometry/InitBoundaryHandling.h
index 7be3b02710ad675f91a8906e67f34b28a0b3bd74..fe6817d3ead08c2b7bb144f9564dd8ea8d03809d 100644
--- a/src/geometry/InitBoundaryHandling.h
+++ b/src/geometry/InitBoundaryHandling.h
@@ -87,12 +87,12 @@ void initBoundaryHandling( StructuredBlockStorage & blocks, BlockDataID boundary
    using namespace geometry;
    using namespace initializer;
 
-   typedef BoundaryFromCellInterval  <BoundaryHandling>                 FromCellInterval;
-   typedef BoundaryFromDomainBorder  <BoundaryHandling>                 FromBorder;
-   typedef BoundaryFromVoxelFile     <BoundaryHandling>                 FromVoxelFile;
-   typedef BoundaryFromBody          <BoundaryHandling>                 FromBody;
-   typedef BoundaryFromImage         <BoundaryHandling, GrayScaleImage> FromGrayScaleImage;
-   typedef BoundaryFromImage         <BoundaryHandling, RGBAImage>      FromRGBAImage;
+   using FromCellInterval = BoundaryFromCellInterval<BoundaryHandling>;
+   using FromBorder = BoundaryFromDomainBorder<BoundaryHandling>;
+   using FromVoxelFile = BoundaryFromVoxelFile<BoundaryHandling>;
+   using FromBody = BoundaryFromBody<BoundaryHandling>;
+   using FromGrayScaleImage = BoundaryFromImage<BoundaryHandling, GrayScaleImage>;
+   using FromRGBAImage = BoundaryFromImage<BoundaryHandling, RGBAImage>;
 
    InitializationManager initManager( blocks.getBlockStorage() );
 
diff --git a/src/geometry/bodies/DynamicBody.h b/src/geometry/bodies/DynamicBody.h
index 8a0ecfd52ef852c5f875474a0f4acbc79b91192b..1ebd1670e4a5e05d261678c083083f59553e1b90 100644
--- a/src/geometry/bodies/DynamicBody.h
+++ b/src/geometry/bodies/DynamicBody.h
@@ -45,15 +45,15 @@ public:
       : body_(b)
    {}
    
-   virtual bool contains (const Vector3<real_t> & point ) const
+   bool contains (const Vector3<real_t> & point ) const override
    {
         return geometry::contains( body_, point );
    }
-   virtual FastOverlapResult fastOverlapCheck ( const Vector3<real_t> & cellMidpoint, const Vector3<real_t> & dx ) const
+   FastOverlapResult fastOverlapCheck ( const Vector3<real_t> & cellMidpoint, const Vector3<real_t> & dx ) const override
    {
       return geometry::fastOverlapCheck( body_, cellMidpoint, dx );
    }
-   virtual FastOverlapResult fastOverlapCheck ( const AABB & box ) const
+   FastOverlapResult fastOverlapCheck ( const AABB & box ) const override
    {
       return geometry::fastOverlapCheck( body_, box);
    }
diff --git a/src/geometry/containment_octree/BranchNode.h b/src/geometry/containment_octree/BranchNode.h
index fd5d950774a8486a20d561aad6bc3ed112e58bc8..2b160ad5b3641e5adcbcdba3c6c245bcbc2f89c8 100644
--- a/src/geometry/containment_octree/BranchNode.h
+++ b/src/geometry/containment_octree/BranchNode.h
@@ -36,12 +36,12 @@ template< typename ContainmentOctreeT >
 class BranchNode : public Node<ContainmentOctreeT>
 {
 public:
-   typedef typename Node<ContainmentOctreeT>::DistanceObject DistanceObject;
-   typedef typename Node<ContainmentOctreeT>::Scalar Scalar;
-   typedef typename Node<ContainmentOctreeT>::Point Point;
-   typedef typename Node<ContainmentOctreeT>::AABB AABB;
+   using DistanceObject = typename Node<ContainmentOctreeT>::DistanceObject;
+   using Scalar = typename Node<ContainmentOctreeT>::Scalar;
+   using Point = typename Node<ContainmentOctreeT>::Point;
+   using AABB = typename Node<ContainmentOctreeT>::AABB;
    
-   typedef typename Node<ContainmentOctreeT>::KahanAccumulator KahanAccumulator;
+   using KahanAccumulator = typename Node<ContainmentOctreeT>::KahanAccumulator;
    
    inline BranchNode( const shared_ptr<const DistanceObject> & distanceObject, const AABB & aabb, const Scalar epsilon,
                       const uint_t maxDepth, const Scalar minAABBVolume );
diff --git a/src/geometry/containment_octree/ContainmentOctree.h b/src/geometry/containment_octree/ContainmentOctree.h
index 96d091c006d8554a4af79a374e1c589ad7097813..d95fa55ceb59b1be675ded5b180e4434941cfd99 100644
--- a/src/geometry/containment_octree/ContainmentOctree.h
+++ b/src/geometry/containment_octree/ContainmentOctree.h
@@ -42,18 +42,18 @@ template< typename DistanceObjectT >
 class ContainmentOctree
 {
 public:
-   typedef typename DistanceObjectT::Scalar Scalar;
-   typedef typename DistanceObjectT::Point  Point;
-   typedef math::GenericAABB<Scalar>        AABB;
-
-   typedef math::KahanAccumulator<Scalar> KahanAccumulator;
-   typedef DistanceObjectT                DistanceObject;
-
-   typedef containment_octree::Node<ContainmentOctree>                  Node;
-   typedef containment_octree::InsideLeafNode<ContainmentOctree>        InsideLeafNode;
-   typedef containment_octree::OutsideLeafNode<ContainmentOctree>       OutsideLeafNode;
-   typedef containment_octree::IndeterminateLeafNode<ContainmentOctree> IndeterminateLeafNode;
-   typedef containment_octree::BranchNode<ContainmentOctree>            BranchNode;
+   using Scalar = typename DistanceObjectT::Scalar;
+   using Point = typename DistanceObjectT::Point;
+   using AABB = math::GenericAABB<Scalar>;
+
+   using KahanAccumulator = math::KahanAccumulator<Scalar>;
+   using DistanceObject = DistanceObjectT;
+
+   using Node = containment_octree::Node<ContainmentOctree>;
+   using InsideLeafNode = containment_octree::InsideLeafNode<ContainmentOctree>;
+   using OutsideLeafNode = containment_octree::OutsideLeafNode<ContainmentOctree>;
+   using IndeterminateLeafNode = containment_octree::IndeterminateLeafNode<ContainmentOctree>;
+   using BranchNode = containment_octree::BranchNode<ContainmentOctree>;
 
    inline ContainmentOctree( const shared_ptr<const DistanceObject> & distanceObject, const Scalar epsilon = Scalar(0),
                              const uint_t maxDepth = 6u, const Scalar minAABBVolume = Scalar(0) );
diff --git a/src/geometry/containment_octree/IndeterminateLeafNode.h b/src/geometry/containment_octree/IndeterminateLeafNode.h
index 349d18369b49d58380a9b68e6daa9e385506a1a4..382620f5767bbc746dbc7873448c42532a24aa46 100644
--- a/src/geometry/containment_octree/IndeterminateLeafNode.h
+++ b/src/geometry/containment_octree/IndeterminateLeafNode.h
@@ -34,17 +34,17 @@ class IndeterminateLeafNode : public LeafNode<ContainmentOctreeT>
 {
 public:
    using LeafNode<ContainmentOctreeT>::numNodes;
-   typedef typename LeafNode<ContainmentOctreeT>::DistanceObject DistanceObject;
-   typedef typename LeafNode<ContainmentOctreeT>::Scalar Scalar;
-   typedef typename LeafNode<ContainmentOctreeT>::Point Point;
-   typedef typename LeafNode<ContainmentOctreeT>::AABB AABB;
+   using DistanceObject = typename LeafNode<ContainmentOctreeT>::DistanceObject;
+   using Scalar = typename LeafNode<ContainmentOctreeT>::Scalar;
+   using Point = typename LeafNode<ContainmentOctreeT>::Point;
+   using AABB = typename LeafNode<ContainmentOctreeT>::AABB;
    
-   typedef typename LeafNode<ContainmentOctreeT>::KahanAccumulator KahanAccumulator;
+   using KahanAccumulator = typename LeafNode<ContainmentOctreeT>::KahanAccumulator;
    
    IndeterminateLeafNode( const shared_ptr<const DistanceObject> & distanceObject, const Scalar epsilon )
       :  distanceObject_( distanceObject ), sqEpsilon_( epsilon * epsilon ) { }
 
-   virtual ~IndeterminateLeafNode() {}
+   virtual ~IndeterminateLeafNode() = default;
 
    virtual bool contains( const Point & p ) const { return distanceObject_->sqSignedDistance(p) <= sqEpsilon_; }
 
diff --git a/src/geometry/containment_octree/InsideLeafNode.h b/src/geometry/containment_octree/InsideLeafNode.h
index f291a5c37b900b5b52dc18fe851d97e65c7e2ae9..0c46290f13148bcd3d2451301512d6ae5a62df0d 100644
--- a/src/geometry/containment_octree/InsideLeafNode.h
+++ b/src/geometry/containment_octree/InsideLeafNode.h
@@ -34,14 +34,14 @@ class InsideLeafNode : public LeafNode<ContainmentOctreeT>
 {
 public:
    using LeafNode<ContainmentOctreeT>::numNodes;
-   typedef typename LeafNode<ContainmentOctreeT>::DistanceObject DistanceObject;
-   typedef typename LeafNode<ContainmentOctreeT>::Scalar Scalar;
-   typedef typename LeafNode<ContainmentOctreeT>::Point Point;
-   typedef typename LeafNode<ContainmentOctreeT>::AABB AABB;
+   using DistanceObject = typename LeafNode<ContainmentOctreeT>::DistanceObject;
+   using Scalar = typename LeafNode<ContainmentOctreeT>::Scalar;
+   using Point = typename LeafNode<ContainmentOctreeT>::Point;
+   using AABB = typename LeafNode<ContainmentOctreeT>::AABB;
     
-   typedef typename LeafNode<ContainmentOctreeT>::KahanAccumulator KahanAccumulator;
+   using KahanAccumulator = typename LeafNode<ContainmentOctreeT>::KahanAccumulator;
 
-   virtual ~InsideLeafNode() {}
+   virtual ~InsideLeafNode() = default;
 
    virtual bool contains( const Point & /*p*/ ) const { return true; }
 
diff --git a/src/geometry/containment_octree/LeafNode.h b/src/geometry/containment_octree/LeafNode.h
index 57c1b6553db38ebee6b26fdc3f43de732d6fca50..177a352dc1f07300f09f6058df8755b98d57e96f 100644
--- a/src/geometry/containment_octree/LeafNode.h
+++ b/src/geometry/containment_octree/LeafNode.h
@@ -35,14 +35,14 @@ class LeafNode : public Node<ContainmentOctreeT>
 {
 public:
    using Node<ContainmentOctreeT>::numNodes;
-   typedef typename Node<ContainmentOctreeT>::DistanceObject DistanceObject;
-   typedef typename Node<ContainmentOctreeT>::Scalar Scalar;
-   typedef typename Node<ContainmentOctreeT>::Point Point;
-   typedef typename Node<ContainmentOctreeT>::AABB AABB;
+   using DistanceObject = typename Node<ContainmentOctreeT>::DistanceObject;
+   using Scalar = typename Node<ContainmentOctreeT>::Scalar;
+   using Point = typename Node<ContainmentOctreeT>::Point;
+   using AABB = typename Node<ContainmentOctreeT>::AABB;
    
-   typedef typename Node<ContainmentOctreeT>::KahanAccumulator KahanAccumulator;
+   using KahanAccumulator = typename Node<ContainmentOctreeT>::KahanAccumulator;
 
-   virtual ~LeafNode() {}
+   virtual ~LeafNode() = default;
 
    virtual uint_t height() const { return uint_t(0); }
    virtual uint_t numNodes() const { return uint_t(0); }
diff --git a/src/geometry/containment_octree/Node.h b/src/geometry/containment_octree/Node.h
index 0a0d03ce60301f60b606de8c9dfae1454a8abf38..ddc3e15ecd657d515d8898994d2d357359b13052 100644
--- a/src/geometry/containment_octree/Node.h
+++ b/src/geometry/containment_octree/Node.h
@@ -33,14 +33,14 @@ template< typename ContainmentOctreeT >
 class Node
 {
 public:
-   typedef typename ContainmentOctreeT::Point  Point;
-   typedef typename ContainmentOctreeT::Scalar Scalar;
-   typedef typename ContainmentOctreeT::AABB   AABB;
+   using Point = typename ContainmentOctreeT::Point;
+   using Scalar = typename ContainmentOctreeT::Scalar;
+   using AABB = typename ContainmentOctreeT::AABB;
 
-   typedef typename ContainmentOctreeT::DistanceObject   DistanceObject;
-   typedef typename ContainmentOctreeT::KahanAccumulator KahanAccumulator;
+   using DistanceObject = typename ContainmentOctreeT::DistanceObject;
+   using KahanAccumulator = typename ContainmentOctreeT::KahanAccumulator;
 
-   virtual ~Node() {}
+   virtual ~Node() = default;
    virtual bool contains( const Point & p ) const = 0;
    virtual uint_t height() const = 0;
    virtual uint_t numNodes() const = 0;
diff --git a/src/geometry/containment_octree/OutsideLeafNode.h b/src/geometry/containment_octree/OutsideLeafNode.h
index 2144b4e4df13d0c34c5a4b147402822e52401514..6328ce593bfa29ad81ec0414f816eadc7580296f 100644
--- a/src/geometry/containment_octree/OutsideLeafNode.h
+++ b/src/geometry/containment_octree/OutsideLeafNode.h
@@ -35,14 +35,14 @@ class OutsideLeafNode : public LeafNode<ContainmentOctreeT>
 public:
    using LeafNode<ContainmentOctreeT>::numNodes;
    
-   typedef typename LeafNode<ContainmentOctreeT>::DistanceObject DistanceObject;
-   typedef typename LeafNode<ContainmentOctreeT>::Scalar Scalar;
-   typedef typename LeafNode<ContainmentOctreeT>::Point Point;
-   typedef typename LeafNode<ContainmentOctreeT>::AABB AABB;
+   using DistanceObject = typename LeafNode<ContainmentOctreeT>::DistanceObject;
+   using Scalar = typename LeafNode<ContainmentOctreeT>::Scalar;
+   using Point = typename LeafNode<ContainmentOctreeT>::Point;
+   using AABB = typename LeafNode<ContainmentOctreeT>::AABB;
    
-   typedef typename LeafNode<ContainmentOctreeT>::KahanAccumulator KahanAccumulator;
+   using KahanAccumulator = typename LeafNode<ContainmentOctreeT>::KahanAccumulator;
       
-   virtual ~OutsideLeafNode() {}
+   virtual ~OutsideLeafNode() = default;
 
    virtual bool contains( const Point & /*p*/ ) const { return false; }
 
diff --git a/src/geometry/initializer/BoundaryFromBody.h b/src/geometry/initializer/BoundaryFromBody.h
index 0c5c696a909de92dd84cc33b94d2006285c31dac..1eb9e06b786a858cb6432ee168ed40b0c8501c4b 100644
--- a/src/geometry/initializer/BoundaryFromBody.h
+++ b/src/geometry/initializer/BoundaryFromBody.h
@@ -79,7 +79,7 @@ public:
    * The body parameters and the parameters for the boundary are read from configuration.
    *
    *****************************************************************************************************************/
-   virtual void init( BlockStorage & , const Config::BlockHandle & blockHandle ) { return init(blockHandle); }
+   void init( BlockStorage & , const Config::BlockHandle & blockHandle ) override { return init(blockHandle); }
    void init( const Config::BlockHandle & blockHandle );
 
 
diff --git a/src/geometry/initializer/BoundaryFromCellInterval.h b/src/geometry/initializer/BoundaryFromCellInterval.h
index 80355b49f8a99ea87aca183a38f29109d7e52892..f2245274c9d279a5c3b340595b53879f20822be2 100644
--- a/src/geometry/initializer/BoundaryFromCellInterval.h
+++ b/src/geometry/initializer/BoundaryFromCellInterval.h
@@ -84,7 +84,7 @@ public:
    * The cell interval and the parameters for the boundary are read from configuration.
    *
    *****************************************************************************************************************/
-   virtual void init( BlockStorage & , const Config::BlockHandle & blockHandle ) { return init(blockHandle); }
+   void init( BlockStorage & , const Config::BlockHandle & blockHandle ) override { return init(blockHandle); }
    void init( const Config::BlockHandle & blockHandle );
 
 
diff --git a/src/geometry/initializer/BoundaryFromDomainBorder.h b/src/geometry/initializer/BoundaryFromDomainBorder.h
index 9fa9937815e640880c2722cf3df1b58f1cad864c..9329a72ada1ca2c362f37ea7f8d77af5323c58db 100644
--- a/src/geometry/initializer/BoundaryFromDomainBorder.h
+++ b/src/geometry/initializer/BoundaryFromDomainBorder.h
@@ -80,7 +80,7 @@ public:
 
    void init ( const Config::BlockHandle & blockHandle );
 
-   virtual void init( BlockStorage & blockStorage, const Config::BlockHandle & blockHandle );
+   void init( BlockStorage & blockStorage, const Config::BlockHandle & blockHandle ) override;
 
 
    void init( FlagUID flagUID, stencil::Direction direction,
diff --git a/src/geometry/initializer/BoundaryFromImage.h b/src/geometry/initializer/BoundaryFromImage.h
index 9db375940f1f64febc7330f1365a941c69c0b367..466af7431b1aac361c72c202f1ddad3745440324 100644
--- a/src/geometry/initializer/BoundaryFromImage.h
+++ b/src/geometry/initializer/BoundaryFromImage.h
@@ -87,8 +87,8 @@ namespace initializer {
    class BoundaryFromImage : public Initializer
    {
    public:
-      typedef typename Image_T::pixel_t pixel_t;
-      typedef std::map<pixel_t, BoundarySetter<BoundaryHandling_T> > BoundarySetters;
+      using pixel_t = typename Image_T::pixel_t;
+      using BoundarySetters = std::map<pixel_t, BoundarySetter<BoundaryHandling_T>>;
 
 
       BoundaryFromImage( StructuredBlockStorage & blocks, BlockDataID handlerBlockDataID );
@@ -97,7 +97,7 @@ namespace initializer {
       * Initializes the scalar field using parameters of config block
       * for syntax see class documentation
       *****************************************************************************************************************/
-      virtual void init ( BlockStorage & , const Config::BlockHandle & block ) { init(block); }
+      void init ( BlockStorage & , const Config::BlockHandle & block ) override { init(block); }
       void init( const Config::BlockHandle & block );
 
 
diff --git a/src/geometry/initializer/BoundaryFromVoxelFile.h b/src/geometry/initializer/BoundaryFromVoxelFile.h
index 6bef2c8c3c2260b1b728fdce1158f08f3c838a9c..7987f77f9444a6e64f0c4e125dd0462279e3dc89 100644
--- a/src/geometry/initializer/BoundaryFromVoxelFile.h
+++ b/src/geometry/initializer/BoundaryFromVoxelFile.h
@@ -58,8 +58,8 @@ struct IBlockIDPtrCompare {
    bool operator()( const IBlockID * lhs, const IBlockID * rhs ) const;
 };
 
-typedef std::map<const IBlockID*, CellInterval, IBlockIDPtrCompare> CellIntervalMap;
-typedef std::map<const IBlockID*, std::pair<CellInterval, std::vector<uint8_t> >, IBlockIDPtrCompare> CellIntervalDataMap;
+using CellIntervalMap = std::map<const IBlockID *, CellInterval, IBlockIDPtrCompare>;
+using CellIntervalDataMap = std::map<const IBlockID *, std::pair<CellInterval, std::vector<uint8_t>>, IBlockIDPtrCompare>;
 
 
 //*******************************************************************************************************************
@@ -88,7 +88,7 @@ class BoundaryFromVoxelFile : public Initializer
 public:
    BoundaryFromVoxelFile( const StructuredBlockStorage & structuredBlockStorage, BlockDataID & boundaryHandlerID );
 
-   virtual void init( BlockStorage & blockStorage, const Config::BlockHandle & blockHandle );
+   void init( BlockStorage & blockStorage, const Config::BlockHandle & blockHandle ) override;
 
 
 protected:
diff --git a/src/geometry/initializer/BoundarySetterFlagFieldSpecialization.h b/src/geometry/initializer/BoundarySetterFlagFieldSpecialization.h
index 1ae6a04e217ea2533180775e914f38788c10b5b5..f731a37f02fd8b68bf9836c35a50e2079617319b 100644
--- a/src/geometry/initializer/BoundarySetterFlagFieldSpecialization.h
+++ b/src/geometry/initializer/BoundarySetterFlagFieldSpecialization.h
@@ -97,7 +97,7 @@ namespace initializer {
             ++blockHandleIt;
       }
 
-      if (boundaryConfigBlocks.size () > 0 ) {
+      if (!boundaryConfigBlocks.empty() ) {
          WALBERLA_ABORT_NO_DEBUG_INFO( "No boundary setup blocks are allowed when configuring a flag field"
                                                << blockHandle.getKey() );
       }
diff --git a/src/geometry/initializer/InitializationManager.h b/src/geometry/initializer/InitializationManager.h
index 7c9ac444b8ecd6000cf573bb9985386bcfb0be9a..81ed7d68cf4565810ba1d079c95b7a1366b0141a 100644
--- a/src/geometry/initializer/InitializationManager.h
+++ b/src/geometry/initializer/InitializationManager.h
@@ -44,7 +44,7 @@ namespace initializer {
 
    class Initializer;
    class InitializerUIDGenerator;
-   typedef uid::UID< InitializerUIDGenerator > InitializerUID;
+   using InitializerUID = uid::UID<InitializerUIDGenerator>;
 
 
    //*******************************************************************************************************************
diff --git a/src/geometry/initializer/Initializer.h b/src/geometry/initializer/Initializer.h
index a724baacf8377054f89b334466dac0e8e2c3fa6c..88336d0ec3d92bc2004d19bdf9f267dadcedc59d 100644
--- a/src/geometry/initializer/Initializer.h
+++ b/src/geometry/initializer/Initializer.h
@@ -51,7 +51,7 @@ namespace initializer {
    class Initializer
    {
    public:
-      virtual ~Initializer() { }
+      virtual ~Initializer() = default;
 
       virtual void init( domain_decomposition::BlockStorage & blockStorage, const Config::BlockHandle & blockHandle ) = 0;
    };
diff --git a/src/geometry/initializer/OverlapFieldFromBody.h b/src/geometry/initializer/OverlapFieldFromBody.h
index b12b9e2bc097b25a52fa20274c4a49fd96fab84f..0438b85e8c545ae3158aa676cc7e41d969ac378d 100644
--- a/src/geometry/initializer/OverlapFieldFromBody.h
+++ b/src/geometry/initializer/OverlapFieldFromBody.h
@@ -101,7 +101,7 @@ namespace initializer {
       * Interface implementation for Initializer - sets a body on a scalar field with options from configuration file
       *
       *****************************************************************************************************************/
-      virtual void init( BlockStorage & blockStorage, const Config::BlockHandle & blockHandle );
+      void init( BlockStorage & blockStorage, const Config::BlockHandle & blockHandle ) override;
 
 
 
diff --git a/src/geometry/initializer/ScalarFieldFromBody.h b/src/geometry/initializer/ScalarFieldFromBody.h
index ba8b72f9b407c505666121770bf30d1aea6f58d9..617097ad85d1e79b5c98046101a4c7f59425b0e7 100644
--- a/src/geometry/initializer/ScalarFieldFromBody.h
+++ b/src/geometry/initializer/ScalarFieldFromBody.h
@@ -77,7 +77,7 @@ namespace initializer {
    class ScalarFieldFromBody : public Initializer
    {
    public:
-      typedef typename Field_T::value_type Value_T;
+      using Value_T = typename Field_T::value_type;
       
       /*************************************************************************************************************//**
       * Constructor
@@ -97,7 +97,7 @@ namespace initializer {
       * Interface implementation for Initializer - sets a body on a scalar field with options from configuration file
       *
       *****************************************************************************************************************/
-      virtual void init( BlockStorage & blockStorage, const Config::BlockHandle & blockHandle );
+      void init( BlockStorage & blockStorage, const Config::BlockHandle & blockHandle ) override;
 
 
 
diff --git a/src/geometry/initializer/ScalarFieldFromGrayScaleImage.h b/src/geometry/initializer/ScalarFieldFromGrayScaleImage.h
index f570c0f118a6af7e9293c81fefe5463f1245a567..2664fba729025996ebf9255cb3854b35b92c7706 100644
--- a/src/geometry/initializer/ScalarFieldFromGrayScaleImage.h
+++ b/src/geometry/initializer/ScalarFieldFromGrayScaleImage.h
@@ -64,7 +64,7 @@ namespace initializer {
       * Initializes the scalar field using parameters of config block
       * for syntax see class documentation
       *****************************************************************************************************************/
-      virtual void init ( BlockStorage & blockStorage, const Config::BlockHandle & block );
+      void init ( BlockStorage & blockStorage, const Config::BlockHandle & block ) override;
 
 
       /*************************************************************************************************************//**
diff --git a/src/geometry/mesh/TriangleMesh.h b/src/geometry/mesh/TriangleMesh.h
index 12501c96ced95ca771de61ae485cdbbccdf98125..8f6817c41100d6609187250d51858746463c4ebd 100644
--- a/src/geometry/mesh/TriangleMesh.h
+++ b/src/geometry/mesh/TriangleMesh.h
@@ -54,12 +54,12 @@ namespace geometry {
    class TriangleMesh
    {
    public:
-      typedef uint32_t index_t;
+      using index_t = uint32_t;
       template<typename T> static index_t index_c( T x ) { return numeric_cast<index_t>(x); }
 
-      typedef Vector3<real_t> vertex_t;
-      typedef Vector3<real_t> normal_t;
-      typedef Vector3<float>  color_t;
+      using vertex_t = Vector3<real_t>;
+      using normal_t = Vector3<real_t>;
+      using color_t = Vector3<float>;
 
       //** Size Information  *******************************************************************************************
       /*! \name Size Information */
diff --git a/src/geometry/python/Exports.cpp b/src/geometry/python/Exports.cpp
deleted file mode 100644
index aa9f32f7b8635e3dd8df8535941dbac11b0755e4..0000000000000000000000000000000000000000
--- a/src/geometry/python/Exports.cpp
+++ /dev/null
@@ -1,103 +0,0 @@
-//======================================================================================================================
-//
-//  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 PythonExports.cpp
-//! \ingroup geometry
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-// Do not reorder includes - the include order is important
-#include "python_coupling/PythonWrapper.h"
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-#include "python_coupling/helper/ModuleScope.h"
-
-#include "core/math/AABB.h"
-#include "geometry/mesh/TriangleMesh.h"
-#include "geometry/mesh/TriangleMeshIO.h"
-
-
-
-using namespace boost::python;
-
-
-namespace walberla {
-namespace geometry {
-
-void triangleMesh_scaleReal( TriangleMesh & m, real_t scaleFactor )               { m.scale( scaleFactor ); }
-void triangleMesh_scaleVec ( TriangleMesh & m, const Vector3<real_t> & scaleVec ) { m.scale( scaleVec );    }
-
-
-shared_ptr<TriangleMesh> triangleMesh_load ( const std::string & filename, bool broadcast )
-{
-   auto mesh = make_shared<TriangleMesh>();
-
-   try
-   {
-      if( broadcast )
-         readAndBroadcastMesh( filename, *mesh );
-      else
-         readMesh( filename, *mesh );
-   }
-   catch( std::exception & e )
-   {
-      PyErr_SetString( PyExc_RuntimeError, e.what() );
-      throw error_already_set();
-   }
-   return mesh;
-}
-
-void triangleMesh_save( TriangleMesh & mesh, const std::string & filename )
-{
-   try
-   {
-      writeMesh( filename, mesh );
-   }
-   catch( std::exception & e )
-   {
-      PyErr_SetString( PyExc_RuntimeError, e.what() );
-      throw error_already_set();
-   }
-}
-
-
-
-void exportModuleToPython()
-{
-   python_coupling::ModuleScope fieldModule( "geometry" );
-
-   class_< TriangleMesh, shared_ptr<TriangleMesh>, boost::noncopyable > ( "TriangleMesh", no_init )
-            .add_property( "numTriangles",             &TriangleMesh::getNumTriangles )
-            .add_property( "numVertices" ,             &TriangleMesh::getNumVertices )
-            .add_property( "numVertexNormals",         &TriangleMesh::getNumNormals )
-            .def         ( "volume",                   &TriangleMesh::volume )
-            .def         ( "scale",                    &triangleMesh_scaleReal, arg("factor") )
-            .def         ( "exchangeAxes",             &TriangleMesh::exchangeAxes, ( arg("xAxisId"), arg("yAxisId"), arg("zAxisId") ) )
-            .def         ( "scaleXYZ",                 &triangleMesh_scaleVec, arg("factors") )
-            .def         ( "removeDuplicateVertices",  &TriangleMesh::removeDuplicateVertices, ( arg("tolerance") = 1e-4 ) )
-            .def         ( "merge",                    &TriangleMesh::merge, ( arg("other"), arg("offset") = Vector3<real_t>(0) ) )
-            .def         ( "getAABB",                  &TriangleMesh::getAABB )
-            .def         ( "save",                     &triangleMesh_save, arg("filename") )
-            .def         ( "load",                     &triangleMesh_load, ( arg("filename"), arg("broadcast") = true) ).staticmethod( "load" )
-   ;
-}
-
-
-} // namespace geometry
-} // namespace walberla
-
-
-#endif //WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/geometry/structured/BasicVoxelFileReader.impl.h b/src/geometry/structured/BasicVoxelFileReader.impl.h
index 4dbdc8ad87c823c31ba744e8f5303551534408be..9bebb8345764661b8b15957ea9e0c33fa21ccfd8 100644
--- a/src/geometry/structured/BasicVoxelFileReader.impl.h
+++ b/src/geometry/structured/BasicVoxelFileReader.impl.h
@@ -371,7 +371,7 @@ void BasicVoxelFileReader<T>::close()
 
    assert(!filestream_.is_open());
    assert(dataBegin_ == std::streampos());
-   assert(filename_ == "");
+   assert(filename_.empty());
    assert(xSize_ == 0);
    assert(ySize_ == 0);
    assert(zSize_ == 0);
diff --git a/src/geometry/structured/GrayScaleImage.h b/src/geometry/structured/GrayScaleImage.h
index a32c2254817ee600ff768b153ce8ba0f86c5df98..43e39359b3ce578e7114de1dd3b6f9e69db8879b 100644
--- a/src/geometry/structured/GrayScaleImage.h
+++ b/src/geometry/structured/GrayScaleImage.h
@@ -44,7 +44,7 @@ namespace geometry   {
    class GrayScaleImage
    {
    public:
-      typedef unsigned char pixel_t;
+      using pixel_t = unsigned char;
 
       GrayScaleImage( uint_t _width, uint_t _height );
       GrayScaleImage( const std::string & pngFilename );
@@ -70,7 +70,7 @@ namespace geometry   {
       static pixel_t pixelValueFromString( const std::string & str );
 
    protected:
-      GrayScaleImage() {}
+      GrayScaleImage() = default;
 
       uint_t size_[2];                   //< 0=width,  1=height
       std::vector<unsigned char> image_; //< raw pixels
diff --git a/src/geometry/structured/RGBAImage.h b/src/geometry/structured/RGBAImage.h
index 4c7be318897f98b9cd4b37c0b7c81a02e31cc51b..4e2871659e215f6e2e8ab60cb0637d23b861f9b8 100644
--- a/src/geometry/structured/RGBAImage.h
+++ b/src/geometry/structured/RGBAImage.h
@@ -99,7 +99,7 @@ namespace geometry   {
       static pixel_t pixelValueFromString( const std::string & str );
 
    protected:
-      RGBAImage() {}
+      RGBAImage() = default;
 
       uint_t size_[2];                   //< 0=width,  1=height
       std::vector<unsigned char> image_; //< raw pixels
diff --git a/src/geometry/structured/extern/lodepng.h b/src/geometry/structured/extern/lodepng.h
index 404ae1ec40fccf1c28295bd81abc9953c701f256..2d4b383cca999b3a9901160615e5ded51504d3ae 100644
--- a/src/geometry/structured/extern/lodepng.h
+++ b/src/geometry/structured/extern/lodepng.h
@@ -622,7 +622,7 @@ typedef struct LodePNGState
   unsigned error;
 #ifdef LODEPNG_COMPILE_CPP
   //For the lodepng::State subclass.
-  virtual ~LodePNGState(){}
+  virtual ~LodePNGState()= default;
 #endif
 } LodePNGState;
 
@@ -808,7 +808,7 @@ class State : public LodePNGState
   public:
     State();
     State(const State& other);
-    virtual ~State();
+    ~State() override;
     State& operator=(const State& other);
 };
 
diff --git a/src/gui/Gui.h b/src/gui/Gui.h
index 1332bb8dccda6574013f348140f941974ccd3ce8..799060b7a2f0c7021c9ec4dca5f8930d14074097 100644
--- a/src/gui/Gui.h
+++ b/src/gui/Gui.h
@@ -52,7 +52,7 @@ namespace gui {
 
       void registerPropertyTree( const shared_ptr<PropertyTree>& propertyTree );
 
-      typedef std::function< DisplayAdaptor * ( const IBlock &, ConstBlockDataID ) > DisplayAdaptorCreatorFunc;
+      using DisplayAdaptorCreatorFunc = std::function<DisplayAdaptor *(const IBlock &, ConstBlockDataID)>;
       void registerDisplayAdaptorCreator( const DisplayAdaptorCreatorFunc & creatorFunc );
 
       static void breakpoint( const std::string & comment, const std::string & file, int line );
diff --git a/src/gui/PropertyTree.h b/src/gui/PropertyTree.h
index f6a5ae179897f767c2b8fce2100c706a186a822a..30bb247e3ccbe156e0b87e452daf9c8d2f1fa3eb 100644
--- a/src/gui/PropertyTree.h
+++ b/src/gui/PropertyTree.h
@@ -52,7 +52,7 @@ namespace gui {
 #        ifdef WALBERLA_ENABLE_GUI
          typedef QStandardItem*  ItemID;
 #        else
-         typedef void * ItemID;
+         using ItemID = void *;
 #        endif
 
          PropertyTree();
@@ -139,12 +139,12 @@ namespace gui {
           *               to add children
           */
          template <typename T>
-         ItemID addItem(const std::string & name, const T & val, ItemID parent=0);
+         ItemID addItem(const std::string & name, const T & val, ItemID parent=nullptr);
 
          /**
           * Convenience method, behaves like addItem above with no value -> empty second column
           */
-         ItemID addItem(const std::string & name, ItemID parent=0);
+         ItemID addItem(const std::string & name, ItemID parent=nullptr);
 
          /**
           * Changes an existing item
diff --git a/src/gui/PropertyTree.impl.h b/src/gui/PropertyTree.impl.h
index a4c7053556fa0a4cf742dd3b8a5f509e06469509..f7d6dae8d38196abb7207d26ed28188787b9b28b 100644
--- a/src/gui/PropertyTree.impl.h
+++ b/src/gui/PropertyTree.impl.h
@@ -78,7 +78,7 @@ namespace gui  {
    template <typename T>
    PropertyTree::ItemID PropertyTree::addItem(const std::string &, const T &, ItemID)
    {
-      return 0;
+      return nullptr;
    }
 
    template <typename T>
diff --git a/src/lbm/MassEvaluation.h b/src/lbm/MassEvaluation.h
index 5a12f9c1329fb0f09bdcd81e92490919b5d7c274..9757137db90342d2fc4f34505dcefb064eb73432 100644
--- a/src/lbm/MassEvaluation.h
+++ b/src/lbm/MassEvaluation.h
@@ -96,7 +96,7 @@ shared_ptr< walberla::field::MassEvaluation< DensityField_T > > makeMassEvaluati
                                                                                     const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                                                                                     const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef walberla::field::MassEvaluation< DensityField_T > ME_T;
+   using ME_T = walberla::field::MassEvaluation<DensityField_T>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, plotFrequency, logFrequency, filename, requiredSelectors, incompatibleSelectors ) );
    evaluation->setDomainNormalization( internal::massEvaluationDomain( blocks, level ) );
    return evaluation;
@@ -111,7 +111,7 @@ makeMassEvaluation( const shared_ptr< StructuredBlockStorage > & blocks, const u
                     const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                     const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef walberla::field::MassEvaluation< DensityField_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T> > ME_T;
+   using ME_T = walberla::field::MassEvaluation<DensityField_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, walberla::field::FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, fluid ),
                                                    plotFrequency, logFrequency, filename, requiredSelectors, incompatibleSelectors ) );
    evaluation->setDomainNormalization( internal::massEvaluationDomain( blocks, level ) );
@@ -126,7 +126,7 @@ shared_ptr< walberla::field::MassEvaluation< DensityField_T, Filter_T > > makeMa
                                                                                               const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                                                                                               const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef walberla::field::MassEvaluation< DensityField_T, Filter_T > ME_T;
+   using ME_T = walberla::field::MassEvaluation<DensityField_T, Filter_T>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, filter, plotFrequency, logFrequency, filename, requiredSelectors, incompatibleSelectors ) );
    evaluation->setDomainNormalization( internal::massEvaluationDomain( blocks, level ) );
    return evaluation;
@@ -143,7 +143,7 @@ makeMassEvaluation( const shared_ptr< StructuredBlockStorage > & blocks, const u
                     const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                     const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef walberla::field::MassEvaluation< DensityField_T, field::DefaultEvaluationFilter, Pseudo2D > ME_T;
+   using ME_T = walberla::field::MassEvaluation<DensityField_T, field::DefaultEvaluationFilter, Pseudo2D>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, plotFrequency, logFrequency, filename, requiredSelectors, incompatibleSelectors ) );
    evaluation->setDomainNormalization( internal::massEvaluationDomain( blocks, level ) );
    return evaluation;
@@ -158,7 +158,7 @@ makeMassEvaluation( const shared_ptr< StructuredBlockStorage > & blocks, const u
                     const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                     const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef walberla::field::MassEvaluation< DensityField_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>, Pseudo2D > ME_T;
+   using ME_T = walberla::field::MassEvaluation<DensityField_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>, Pseudo2D>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, walberla::field::FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, fluid ),
                                                    plotFrequency, logFrequency, filename, requiredSelectors, incompatibleSelectors ) );
    evaluation->setDomainNormalization( internal::massEvaluationDomain( blocks, level ) );
@@ -174,7 +174,7 @@ makeMassEvaluation( const shared_ptr< StructuredBlockStorage > & blocks, const u
                     const Set<SUID> & requiredSelectors = Set<SUID>::emptySet(),
                     const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
-   typedef walberla::field::MassEvaluation< DensityField_T, Filter_T, Pseudo2D > ME_T;
+   using ME_T = walberla::field::MassEvaluation<DensityField_T, Filter_T, Pseudo2D>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, filter, plotFrequency, logFrequency, filename, requiredSelectors, incompatibleSelectors ) );
    evaluation->setDomainNormalization( internal::massEvaluationDomain( blocks, level ) );
    return evaluation;
@@ -202,7 +202,7 @@ shared_ptr< walberla::field::MassEvaluation< DensityField_T > > makeMassEvaluati
                                                                                     const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_LBM_MAKE_MASS_EVALUATION_CONFIG_PARSER( config )
-   typedef walberla::field::MassEvaluation< DensityField_T > ME_T;
+   using ME_T = walberla::field::MassEvaluation<DensityField_T>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, defaultPlotFrequency, defaultLogFrequency, defaultFilename, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_MASS_EVALUATION_SET_AND_RETURN()
 }
@@ -217,7 +217,7 @@ makeMassEvaluation( const Config_T & config,
                     const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_LBM_MAKE_MASS_EVALUATION_CONFIG_PARSER( config )
-   typedef walberla::field::MassEvaluation< DensityField_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T> > ME_T;
+   using ME_T = walberla::field::MassEvaluation<DensityField_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, walberla::field::FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, fluid ),
                                                    defaultPlotFrequency, defaultLogFrequency, defaultFilename, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_MASS_EVALUATION_SET_AND_RETURN()
@@ -232,7 +232,7 @@ shared_ptr< walberla::field::MassEvaluation< DensityField_T, Filter_T > > makeMa
                                                                                               const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_LBM_MAKE_MASS_EVALUATION_CONFIG_PARSER( config )
-   typedef walberla::field::MassEvaluation< DensityField_T, Filter_T > ME_T;
+   using ME_T = walberla::field::MassEvaluation<DensityField_T, Filter_T>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, filter, defaultPlotFrequency, defaultLogFrequency, defaultFilename, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_MASS_EVALUATION_SET_AND_RETURN()
 }
@@ -249,7 +249,7 @@ makeMassEvaluation( const Config_T & config,
                     const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_LBM_MAKE_MASS_EVALUATION_CONFIG_PARSER( config )
-   typedef walberla::field::MassEvaluation< DensityField_T, field::DefaultEvaluationFilter, Pseudo2D > ME_T;
+   using ME_T = walberla::field::MassEvaluation<DensityField_T, field::DefaultEvaluationFilter, Pseudo2D>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, defaultPlotFrequency, defaultLogFrequency, defaultFilename, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_MASS_EVALUATION_SET_AND_RETURN()
 }
@@ -264,7 +264,7 @@ makeMassEvaluation( const Config_T & config,
                     const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_LBM_MAKE_MASS_EVALUATION_CONFIG_PARSER( config )
-   typedef walberla::field::MassEvaluation< DensityField_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>, Pseudo2D > ME_T;
+   using ME_T = walberla::field::MassEvaluation<DensityField_T, walberla::field::FlagFieldEvaluationFilter<FlagField_T>, Pseudo2D>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, walberla::field::FlagFieldEvaluationFilter<FlagField_T>( flagFieldId, fluid ),
                                                    defaultPlotFrequency, defaultLogFrequency, defaultFilename, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_MASS_EVALUATION_SET_AND_RETURN()
@@ -280,7 +280,7 @@ makeMassEvaluation( const Config_T & config,
                     const Set<SUID> & incompatibleSelectors = Set<SUID>::emptySet() )
 {
    WALBERLA_LBM_MAKE_MASS_EVALUATION_CONFIG_PARSER( config )
-   typedef walberla::field::MassEvaluation< DensityField_T, Filter_T, Pseudo2D > ME_T;
+   using ME_T = walberla::field::MassEvaluation<DensityField_T, Filter_T, Pseudo2D>;
    auto evaluation = shared_ptr< ME_T >( new ME_T( blocks, fieldId, filter, defaultPlotFrequency, defaultLogFrequency, defaultFilename, requiredSelectors, incompatibleSelectors ) );
    WALBERLA_FIELD_MAKE_MASS_EVALUATION_SET_AND_RETURN()
 }
diff --git a/src/lbm/PerformanceLogger.h b/src/lbm/PerformanceLogger.h
index 956b4a7daf2eea37f13b2ca8f2d9e370b3c71abd..7e7f183b7b28d8d511ff18936ad25ff8c178cc55 100644
--- a/src/lbm/PerformanceLogger.h
+++ b/src/lbm/PerformanceLogger.h
@@ -97,7 +97,7 @@ PerformanceLogger<FlagField_T>::PerformanceLogger( const shared_ptr< StructuredB
                    const Set<SUID> & requiredSelectors /*= Set<SUID>::emptySet()*/,
                    const Set<SUID> & incompatibleSelectors /*= Set<SUID>::emptySet()*/ )
                    : performanceEvaluation_( blocks, flagFieldId, fluid, requiredSelectors, incompatibleSelectors ),
-                     interval_(interval), timestep_(1), refreshCellCountOnCall_(false), timeloop_(NULL)
+                     interval_(interval), timestep_(1), refreshCellCountOnCall_(false), timeloop_(nullptr)
 {
 }
 
diff --git a/src/lbm/blockforest/PostProcessing.h b/src/lbm/blockforest/PostProcessing.h
index 23644cddc5db68384cd1b99cb10be16ca90f9ad4..e0037f50605aac9ab5fb5dd5487c50b2c60096b5 100644
--- a/src/lbm/blockforest/PostProcessing.h
+++ b/src/lbm/blockforest/PostProcessing.h
@@ -36,7 +36,7 @@ namespace lbm {
 
 namespace internal
 {
-   typedef field::Field< uint8_t, 1 > MarkerField_T;
+   using MarkerField_T = field::Field<uint8_t, 1>;
 }
 
 
@@ -47,9 +47,9 @@ class PostProcessing
 {
 public:
 
-   typedef PdfField< LatticeModel_T > PdfField_T;
-   typedef typename LatticeModel_T::Stencil Stencil_T;
-   typedef typename NeighborsStencil<LatticeModel_T>::type NeighborsStencil_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
+   using NeighborsStencil_T = typename NeighborsStencil<LatticeModel_T>::type;
 
    PostProcessing( const BlockDataID & pdfFieldId, const BlockDataID & markerFieldId, const Filter_T & filter ) :
       pdfFieldId_( pdfFieldId ), markerFieldId_( markerFieldId ), filter_( filter )
@@ -316,7 +316,7 @@ class MarkerFieldGenerator
 {
 public:
 
-   typedef PdfField< LatticeModel_T > PdfField_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
 
    MarkerFieldGenerator( const BlockDataID & pdfFieldId, const BlockDataID & markerFieldId, const Filter_T & filter ) :
       pdfFieldId_( pdfFieldId ), markerFieldId_( markerFieldId ), filter_( filter )
@@ -371,30 +371,30 @@ class MarkerData : public blockforest::BlockDataHandling< internal::MarkerField_
 {
 public:
 
-   typedef PdfField< LatticeModel_T > PdfField_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
 
    MarkerData( const BlockDataID & pdfFieldId, const Filter_T & filter ) :
       pdfFieldId_( pdfFieldId ), filter_( filter )
    {}
 
-   virtual ~MarkerData() {}
+   ~MarkerData() override = default;
 
-   internal::MarkerField_T * initialize( IBlock * const ) { return allocate(); }
+   internal::MarkerField_T * initialize( IBlock * const ) override { return allocate(); }
 
-   void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer );
+   void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override;
 
-   void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child );
-   void serializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer );
+   void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child ) override;
+   void serializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override;
 
-   internal::MarkerField_T * deserialize( IBlock * const ) { return allocate(); }
+   internal::MarkerField_T * deserialize( IBlock * const ) override { return allocate(); }
 
-   internal::MarkerField_T * deserializeCoarseToFine( Block * const ) { return allocate(); }
-   internal::MarkerField_T * deserializeFineToCoarse( Block * const ) { return allocate(); }
+   internal::MarkerField_T * deserializeCoarseToFine( Block * const ) override { return allocate(); }
+   internal::MarkerField_T * deserializeFineToCoarse( Block * const ) override { return allocate(); }
    
-   void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer );
+   void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override;
 
-   void deserializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer );
-   void deserializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer, const uint_t child );   
+   void deserializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override;
+   void deserializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer, const uint_t child ) override;   
 
 protected:
 
diff --git a/src/lbm/boundary/Curved.h b/src/lbm/boundary/Curved.h
index d5f7aa08ff5480a1d58ba6a0fd11e22010150509..9275543895ba067d7d89b9b5659a0956b852d2d6 100644
--- a/src/lbm/boundary/Curved.h
+++ b/src/lbm/boundary/Curved.h
@@ -48,11 +48,11 @@ namespace lbm {
 template< typename LatticeModel_T, typename FlagField_T >
 class Curved : public Boundary< typename FlagField_T::flag_t >
 {
-   typedef PdfField< LatticeModel_T >        PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
-   typedef typename FlagField_T::flag_t      flag_t;
+   using PDFField = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
+   using flag_t = typename FlagField_T::flag_t;
 
-   typedef GhostLayerField< shared_ptr< std::array<real_t, Stencil::Size> >, 1 >  WeightField;
+   using WeightField = GhostLayerField<shared_ptr<std::array<real_t, Stencil::Size>>, 1>;
 
 public:
 
@@ -256,7 +256,7 @@ inline void Curved< LatticeModel_T, FlagField_T >::treatDirection( const cell_id
    }
    else
    {
-      WALBERLA_ASSERT( weights_->get( nx, ny, nz ).get() != NULL );
+      WALBERLA_ASSERT( weights_->get( nx, ny, nz ).get() != nullptr );
       WALBERLA_ASSERT_LESS( Stencil::invDirIdx(dir), weights_->get( nx, ny, nz )->size() );
       
       // linear multi reflection model without non-equilibirum
diff --git a/src/lbm/boundary/DiffusionDirichlet.h b/src/lbm/boundary/DiffusionDirichlet.h
index 4f07d41b809e0b375606b0dc13a30d4e24a0fe74..643027743f14683e830cc9015d633fa5e1b8997c 100644
--- a/src/lbm/boundary/DiffusionDirichlet.h
+++ b/src/lbm/boundary/DiffusionDirichlet.h
@@ -51,10 +51,10 @@ class DiffusionDirichlet : public Boundary<flag_t>
    static_assert( LatticeModel_T::compressible,                                                             "Only works with compressible models!" );
    //static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value), "Only works without additional forces!" );
 
-   typedef PdfField< LatticeModel_T >        PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PDFField = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
-   typedef GhostLayerField< real_t, 1 > ScalarField;
+   using ScalarField = GhostLayerField<real_t, 1>;
 
 public:
 
@@ -72,7 +72,7 @@ public:
 
       const real_t & val() const { return val_; }
 
-      virtual void val( real_t& _val, cell_idx_t, cell_idx_t, cell_idx_t ) const { _val = val(); }
+      void val( real_t& _val, cell_idx_t, cell_idx_t, cell_idx_t ) const override { _val = val(); }
 
       real_t & val(){ return val_; }
 
@@ -122,7 +122,7 @@ inline DiffusionDirichlet< LatticeModel_T, flag_t >::DiffusionDirichlet( const B
    Boundary<flag_t>( boundaryUID ), uid_( uid ), pdfField_( pdfField )
 {
    WALBERLA_ASSERT_NOT_NULLPTR( pdfField_ );
-   if (flagField != NULL)
+   if (flagField != nullptr)
       sclField_ = make_shared<ScalarField>( pdfField_->xSize(), pdfField_->ySize(), pdfField_->zSize(), flagField->nrOfGhostLayers(), field::zyxf );
    else
       sclField_ = make_shared<ScalarField>( pdfField_->xSize(), pdfField_->ySize(), pdfField_->zSize(), pdfField->nrOfGhostLayers(), field::zyxf );
diff --git a/src/lbm/boundary/DynamicUBB.h b/src/lbm/boundary/DynamicUBB.h
index 945cc36f3b895c2013ff9df7c68fd89ce99d6313..07aac83ceda73ecfc7ba6bca30938af327b50294 100644
--- a/src/lbm/boundary/DynamicUBB.h
+++ b/src/lbm/boundary/DynamicUBB.h
@@ -54,10 +54,10 @@ namespace lbm {
 template< typename LatticeModel_T, typename flag_t, typename VelocityFunctor_T, bool AdaptVelocityToExternalForce = false, bool StoreForce = false >
 class DynamicUBB : public Boundary<flag_t>
 {
-   typedef lbm::PdfField< LatticeModel_T >   PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PDFField = lbm::PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
-   typedef GhostLayerField< Vector3<real_t>, 1 > ForceField;
+   using ForceField = GhostLayerField<Vector3<real_t>, 1>;
 
 public:
 
diff --git a/src/lbm/boundary/FreeDiffusion.h b/src/lbm/boundary/FreeDiffusion.h
index d264eda6dbe3d2a13d502defd5df2a1ba04fa83d..4e7aab582535a91c994c9c055f3f9e05804d87dd 100644
--- a/src/lbm/boundary/FreeDiffusion.h
+++ b/src/lbm/boundary/FreeDiffusion.h
@@ -33,8 +33,8 @@ namespace lbm {
    template< typename LatticeModel_T, typename FlagField_T > class FreeDiffusion : public FreeSlip< LatticeModel_T, FlagField_T >
    {
    protected:
-      typedef typename FreeSlip< LatticeModel_T, FlagField_T >::flag_t    flag_t;
-      typedef typename FreeSlip< LatticeModel_T, FlagField_T >::PDFField  PDFField;
+      using flag_t = typename FreeSlip<LatticeModel_T, FlagField_T>::flag_t;
+      using PDFField = typename FreeSlip<LatticeModel_T, FlagField_T>::PDFField;
 
    public:
       FreeDiffusion( const BoundaryUID& boundaryUID, const FlagUID& uid, PDFField* const pdfField, const FlagField_T* const flagField, const flag_t domain )
diff --git a/src/lbm/boundary/FreeSlip.h b/src/lbm/boundary/FreeSlip.h
index 14d3cd0f15b1a53b0d9428e6ec1b818a6d5e5cb4..1f5a35be99f545273b271a6631c2be6e67e10e6f 100644
--- a/src/lbm/boundary/FreeSlip.h
+++ b/src/lbm/boundary/FreeSlip.h
@@ -46,9 +46,9 @@ template< typename LatticeModel_T, typename FlagField_T >
 class FreeSlip : public Boundary< typename FlagField_T::flag_t > {
 
 protected:
-   typedef PdfField< LatticeModel_T >        PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
-   typedef typename FlagField_T::flag_t      flag_t;
+   using PDFField = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
+   using flag_t = typename FlagField_T::flag_t;
 
 public:
 
diff --git a/src/lbm/boundary/NoDiffusion.h b/src/lbm/boundary/NoDiffusion.h
index f6bbec40b8b8e23c1d116a9031ad80458ebc2bb3..fb0e295ce14f685db37e6b18ee77c95a97315943 100644
--- a/src/lbm/boundary/NoDiffusion.h
+++ b/src/lbm/boundary/NoDiffusion.h
@@ -33,7 +33,7 @@ namespace lbm {
    template< typename LatticeModel_T, typename flag_t > class NoDiffusion : public NoSlip< LatticeModel_T, flag_t >
    {
    protected:
-      typedef typename NoSlip< LatticeModel_T, flag_t >::PDFField  PDFField;
+      using PDFField = typename NoSlip<LatticeModel_T, flag_t>::PDFField;
 
    public:
       NoDiffusion( const BoundaryUID& boundaryUID, const FlagUID& uid, PDFField* const pdfField )
diff --git a/src/lbm/boundary/NoSlip.h b/src/lbm/boundary/NoSlip.h
index dc769d345453d523e7f68902eac7cbec2e040dcd..3c52f58729e68ee8098b0d3aff2db8231b462275 100644
--- a/src/lbm/boundary/NoSlip.h
+++ b/src/lbm/boundary/NoSlip.h
@@ -47,10 +47,10 @@ class NoSlip : public Boundary<flag_t>
 {
 protected:
 
-   typedef PdfField< LatticeModel_T >        PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PDFField = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
-   typedef GhostLayerField< Vector3<real_t>, 1 > ForceField;
+   using ForceField = GhostLayerField<Vector3<real_t>, 1>;
 
 public:
 
diff --git a/src/lbm/boundary/Outlet.h b/src/lbm/boundary/Outlet.h
index da59ec47cd9b05e80c61c440af145a1a9119ba22..c512345b7afb2b86d1d5a536a25dc793af16bd35 100644
--- a/src/lbm/boundary/Outlet.h
+++ b/src/lbm/boundary/Outlet.h
@@ -47,9 +47,9 @@ template< typename LatticeModel_T, typename FlagField_T, int Numerator = 2, int
 class Outlet : public Boundary< typename FlagField_T::flag_t > {
 
 protected:
-   typedef PdfField< LatticeModel_T >        PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
-   typedef typename FlagField_T::flag_t      flag_t;
+   using PDFField = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
+   using flag_t = typename FlagField_T::flag_t;
 
 public:
 
diff --git a/src/lbm/boundary/ParserUBB.h b/src/lbm/boundary/ParserUBB.h
index 2ef68c194d80636f53a55567190aa62c17d9700b..98aae788e9fef206999376fcc26f038df4b94a67 100644
--- a/src/lbm/boundary/ParserUBB.h
+++ b/src/lbm/boundary/ParserUBB.h
@@ -50,10 +50,10 @@ namespace lbm {
 template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce = false, bool StoreForce = false >
 class ParserUBB : public Boundary<flag_t>
 {
-   typedef lbm::PdfField< LatticeModel_T >   PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PDFField = lbm::PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
-   typedef GhostLayerField< Vector3<real_t>, 1 > ForceField;
+   using ForceField = GhostLayerField<Vector3<real_t>, 1>;
 
 public:
 
@@ -108,8 +108,8 @@ public:
       return velocity != nullptr;
    }
 
-   typedef GhostLayerField< shared_ptr<Parser>, 1 > ParserField;
-   typedef GhostLayerField< Vector3<real_t>, 1 >    VelocityField;
+   using ParserField = GhostLayerField<shared_ptr<Parser>, 1>;
+   using VelocityField = GhostLayerField<Vector3<real_t>, 1>;
 
    static shared_ptr<Parser> createConfiguration( const Config::BlockHandle & config )
       { return make_shared<Parser>( config ); }
diff --git a/src/lbm/boundary/Pressure.h b/src/lbm/boundary/Pressure.h
index 8dafb7e46b84f95afd3585efe1338cd3fd51388e..bbb93f77099983e9cd2aee69411c5c2b0fabe131 100644
--- a/src/lbm/boundary/Pressure.h
+++ b/src/lbm/boundary/Pressure.h
@@ -48,10 +48,10 @@ namespace lbm {
 template< typename LatticeModel_T, typename flag_t >
 class Pressure : public Boundary<flag_t>
 {
-   typedef PdfField< LatticeModel_T >        PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PDFField = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
-   typedef GhostLayerField< real_t, 1 >  LatticeDensityField;
+   using LatticeDensityField = GhostLayerField<real_t, 1>;
 
 public:
 
diff --git a/src/lbm/boundary/SimpleDiffusionDirichlet.h b/src/lbm/boundary/SimpleDiffusionDirichlet.h
index 1aca569bb201764765954b6a1b9c2471a7322262..e3a6dde2fefc7a201b49ba05c8d6bfe9ce7f50cd 100644
--- a/src/lbm/boundary/SimpleDiffusionDirichlet.h
+++ b/src/lbm/boundary/SimpleDiffusionDirichlet.h
@@ -51,8 +51,8 @@ class SimpleDiffusionDirichlet : public Boundary<flag_t>
    static_assert( LatticeModel_T::compressible,                                                             "Only works with compressible models!" );
    //static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value), "Only works without additional forces!" );
 
-   typedef PdfField< LatticeModel_T >        PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PDFField = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
 public:
 
diff --git a/src/lbm/boundary/SimplePAB.h b/src/lbm/boundary/SimplePAB.h
index b4cd589cf04de8c5d5b448ea6ae2f31e16bcd182..f35429958cd172fe15a0dca4c9a3cb02bd5cc718 100644
--- a/src/lbm/boundary/SimplePAB.h
+++ b/src/lbm/boundary/SimplePAB.h
@@ -57,11 +57,11 @@ class SimplePAB : public Boundary<typename FlagFieldT::flag_t>
    static_assert( LatticeModel_T::compressible == false,                                                             "Only works with incompressible models!" );
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
 
-   typedef PdfField< LatticeModel_T >        PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
-   typedef typename FlagFieldT::flag_t       flag_t;
+   using PDFField = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
+   using flag_t = typename FlagFieldT::flag_t;
 
-   typedef Vector3<real_t> Vec3Real;
+   using Vec3Real = Vector3<real_t>;
 
 public:
 
diff --git a/src/lbm/boundary/SimplePressure.h b/src/lbm/boundary/SimplePressure.h
index d7b39045fa775c6de9712184237775241513a17c..543360d91cf473de727aa38e7e9be6c17db0e368 100644
--- a/src/lbm/boundary/SimplePressure.h
+++ b/src/lbm/boundary/SimplePressure.h
@@ -48,10 +48,10 @@ namespace lbm {
 template< typename LatticeModel_T, typename flag_t >
 class SimplePressure : public Boundary<flag_t>
 {
-   typedef PdfField< LatticeModel_T >        PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PDFField = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
-   typedef Vector3<real_t>  Vec3Real;
+   using Vec3Real = Vector3<real_t>;
 
 public:
 
diff --git a/src/lbm/boundary/SimpleUBB.h b/src/lbm/boundary/SimpleUBB.h
index ba07eedc41cd4da531372b3db8f2b7fd5f141f49..8c1c93af05c78372adb27f5b553baa5495372663 100644
--- a/src/lbm/boundary/SimpleUBB.h
+++ b/src/lbm/boundary/SimpleUBB.h
@@ -48,10 +48,10 @@ namespace lbm {
 template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce = false, bool StoreForce = false >
 class SimpleUBB : public Boundary<flag_t>
 {
-   typedef PdfField< LatticeModel_T >        PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PDFField = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
-   typedef GhostLayerField< Vector3<real_t>, 1 > ForceField;
+   using ForceField = GhostLayerField<Vector3<real_t>, 1>;
 
 public:
 
diff --git a/src/lbm/boundary/SimpleVelocityBoundary.h b/src/lbm/boundary/SimpleVelocityBoundary.h
index edb6a4d94afd242801ee6d66feb9a900c61945a8..1eda8237c1fb00a29912750f35848a2cb2a0de86 100644
--- a/src/lbm/boundary/SimpleVelocityBoundary.h
+++ b/src/lbm/boundary/SimpleVelocityBoundary.h
@@ -47,8 +47,8 @@ namespace lbm {
 template< typename LatticeModel_T, typename flag_t >
 class SimpleVelocityBoundary : public Boundary<flag_t>
 {
-   typedef PdfField< LatticeModel_T >        PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PDFField = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
 public:
 
diff --git a/src/lbm/boundary/UBB.h b/src/lbm/boundary/UBB.h
index 5ce5fcc06251cb3ad868520c523a26383c70ce8f..c866776d2cba47c20107a83b531bbb21c35c78e2 100644
--- a/src/lbm/boundary/UBB.h
+++ b/src/lbm/boundary/UBB.h
@@ -49,12 +49,12 @@ namespace lbm {
 template< typename LatticeModel_T, typename flag_t, bool AdaptVelocityToExternalForce = false, bool StoreForce = false >
 class UBB : public Boundary<flag_t>
 {
-   typedef PdfField< LatticeModel_T >        PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PDFField = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
-   typedef GhostLayerField< Vector3<real_t>, 1 >  VelField;
+   using VelField = GhostLayerField<Vector3<real_t>, 1>;
 
-   typedef GhostLayerField< Vector3<real_t>, 1 > ForceField;
+   using ForceField = GhostLayerField<Vector3<real_t>, 1>;
 
 public:
 
@@ -87,7 +87,7 @@ public:
 
 
 
-   inline UBB( const BoundaryUID & boundaryUID, const FlagUID & uid, PDFField* const pdfField, FlagField<flag_t> * const flagField = NULL );
+   inline UBB( const BoundaryUID & boundaryUID, const FlagUID & uid, PDFField* const pdfField, FlagField<flag_t> * const flagField = nullptr );
 
    void pushFlags( std::vector< FlagUID > & uids ) const { uids.push_back( uid_ ); }
 
@@ -146,7 +146,7 @@ inline UBB< LatticeModel_T, flag_t, AdaptVelocityToExternalForce, StoreForce >::
    Boundary<flag_t>( boundaryUID ), uid_( uid ), pdfField_( pdfField )
 {
    WALBERLA_ASSERT_NOT_NULLPTR( pdfField_ );
-   if (flagField != NULL)
+   if (flagField != nullptr)
       vel_ = make_shared<VelField>( pdfField_->xSize(), pdfField_->ySize(), pdfField_->zSize(), flagField->nrOfGhostLayers(), field::zyxf );
    else
       vel_ = make_shared<VelField>( pdfField_->xSize(), pdfField_->ySize(), pdfField_->zSize(), pdfField_->nrOfGhostLayers(), field::zyxf );
diff --git a/src/lbm/boundary/VelocityBoundary.h b/src/lbm/boundary/VelocityBoundary.h
index 8b470021b3575e755894f49397ff9cc0ddb945c3..c5be0e0a6370c9cd029dce73ac0df51b59c728bf 100644
--- a/src/lbm/boundary/VelocityBoundary.h
+++ b/src/lbm/boundary/VelocityBoundary.h
@@ -48,10 +48,10 @@ namespace lbm {
 template< typename LatticeModel_T, typename flag_t >
 class VelocityBoundary : public Boundary<flag_t>
 {
-   typedef PdfField< LatticeModel_T >        PDFField;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PDFField = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
-   typedef GhostLayerField< shared_ptr< std::vector<real_t> >, 1 >  VelField;
+   using VelField = GhostLayerField<shared_ptr<std::vector<real_t>>, 1>;
 
 public:
 
@@ -232,7 +232,7 @@ inline void VelocityBoundary< LatticeModel_T, flag_t >::treatDirection( const ce
    WALBERLA_ASSERT_EQUAL( mask & this->mask_, this->mask_ ); // only true if "this->mask_" only contains one single flag, which is the case for the
                                                              // current implementation of this boundary condition (VelocityBoundary)
 
-   WALBERLA_ASSERT( vel_->get( nx, ny, nz ).get() != NULL );
+   WALBERLA_ASSERT( vel_->get( nx, ny, nz ).get() != nullptr );
    WALBERLA_ASSERT_LESS( Stencil::invDirIdx(dir), vel_->get( nx, ny, nz )->size() );
 
    pdfField_->get( nx, ny, nz, Stencil::invDirIdx(dir) ) = (*(vel_->get( nx, ny, nz )))[ Stencil::invDirIdx(dir) ];
diff --git a/src/lbm/boundary/factories/DefaultBoundaryHandling.h b/src/lbm/boundary/factories/DefaultBoundaryHandling.h
index 328bd1aacddc3a5ae9835659b527c63a60ab2b2a..bdd2fb493326ca19af173aae9c1a4c769b97c319 100644
--- a/src/lbm/boundary/factories/DefaultBoundaryHandling.h
+++ b/src/lbm/boundary/factories/DefaultBoundaryHandling.h
@@ -71,16 +71,16 @@ template <typename LatticeModel, typename FlagFieldT >
 class DefaultBoundaryHandlingFactory
 {
 public:
-   typedef typename FlagFieldT::flag_t            flag_t;
-   typedef typename LatticeModel::Stencil         Stencil;
-   typedef NoSlip< LatticeModel, flag_t >         BcNoSlip;
-   typedef FreeSlip< LatticeModel, FlagFieldT >   BcFreeSlip;
-   typedef SimpleUBB< LatticeModel, flag_t >      BcSimpleUBB;
-   typedef SimplePressure< LatticeModel, flag_t > BcSimplePressure;
-   typedef Vector3<real_t>                        Velocity;
-   typedef PdfField< LatticeModel >               PdfFieldLM;
-
-   typedef walberla::boundary::BoundaryHandling< FlagFieldT, Stencil, BcNoSlip, BcFreeSlip, BcSimpleUBB, BcSimpleUBB, BcSimplePressure, BcSimplePressure > BoundaryHandling;
+   using flag_t = typename FlagFieldT::flag_t;
+   using Stencil = typename LatticeModel::Stencil;
+   using BcNoSlip = NoSlip<LatticeModel, flag_t>;
+   using BcFreeSlip = FreeSlip<LatticeModel, FlagFieldT>;
+   using BcSimpleUBB = SimpleUBB<LatticeModel, flag_t>;
+   using BcSimplePressure = SimplePressure<LatticeModel, flag_t>;
+   using Velocity = Vector3<real_t>;
+   using PdfFieldLM = PdfField<LatticeModel>;
+
+   using BoundaryHandling = walberla::boundary::BoundaryHandling<FlagFieldT, Stencil, BcNoSlip, BcFreeSlip, BcSimpleUBB, BcSimpleUBB, BcSimplePressure, BcSimplePressure>;
 
    static BlockDataID addBoundaryHandlingToStorage( const shared_ptr< StructuredBlockStorage > & bs, const std::string & identifier,
                                                     BlockDataID flagFieldID, BlockDataID pdfFieldID, const Set< FlagUID > & flagUIDSet,
diff --git a/src/lbm/boundary/factories/DefaultBoundaryHandlingCollection.h b/src/lbm/boundary/factories/DefaultBoundaryHandlingCollection.h
index 54aafd3345681bea2ea7776ae6decfd98a0361f3..49e77691a8781bbd980faadca3a0fd422bb497b5 100644
--- a/src/lbm/boundary/factories/DefaultBoundaryHandlingCollection.h
+++ b/src/lbm/boundary/factories/DefaultBoundaryHandlingCollection.h
@@ -37,11 +37,11 @@ template< typename LatticeModel_T, typename DiffusionLatticeModel_T, typename Fl
 class DefaultBoundaryHandlingCollectionFactory
 {
 private:
-   typedef typename DefaultBoundaryHandlingFactory         < LatticeModel_T,          FlagField_T >::BoundaryHandling    DefaultBoundaryHandling_T;
-   typedef typename DefaultDiffusionBoundaryHandlingFactory< DiffusionLatticeModel_T, FlagField_T >::BoundaryHandling_T  DefaultDiffusionBoundaryHandlingFactory_T;
+   using DefaultBoundaryHandling_T = typename DefaultBoundaryHandlingFactory<LatticeModel_T, FlagField_T>::BoundaryHandling;
+   using DefaultDiffusionBoundaryHandlingFactory_T = typename DefaultDiffusionBoundaryHandlingFactory<DiffusionLatticeModel_T, FlagField_T>::BoundaryHandling_T;
 
 public:
-   typedef BoundaryHandlingCollection< FlagField_T, DefaultBoundaryHandling_T &, DefaultDiffusionBoundaryHandlingFactory_T & > BoundaryHandlingCollection_T;
+   using BoundaryHandlingCollection_T = BoundaryHandlingCollection<FlagField_T, DefaultBoundaryHandling_T &, DefaultDiffusionBoundaryHandlingFactory_T &>;
 
 
 private:
diff --git a/src/lbm/boundary/factories/DefaultDiffusionBoundaryHandling.h b/src/lbm/boundary/factories/DefaultDiffusionBoundaryHandling.h
index e32559776caa7bf309cb6d723ecf0d7be605d086..99754f88f35bdee443837684147be01822dced28 100644
--- a/src/lbm/boundary/factories/DefaultDiffusionBoundaryHandling.h
+++ b/src/lbm/boundary/factories/DefaultDiffusionBoundaryHandling.h
@@ -44,19 +44,19 @@ template< typename LatticeModel_T, typename FlagField_T >
 class DefaultDiffusionBoundaryHandlingFactory
 {
 private:
-   typedef typename FlagField_T::flag_t flag_t;
+   using flag_t = typename FlagField_T::flag_t;
 
-   typedef typename LatticeModel_T::Stencil Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
 public:
-   typedef lbm::DiffusionDirichlet      < LatticeModel_T, flag_t      >  DiffusionDirichlet_T;
-   typedef lbm::SimpleDiffusionDirichlet< LatticeModel_T, flag_t      >  SimpleDiffusionDirichlet_T;
-   typedef lbm::NoDiffusion             < LatticeModel_T, flag_t      >  NoDiffusion_T;
-   typedef lbm::FreeDiffusion           < LatticeModel_T, FlagField_T >  FreeDiffusion_T;
+   using DiffusionDirichlet_T = lbm::DiffusionDirichlet<LatticeModel_T, flag_t>;
+   using SimpleDiffusionDirichlet_T = lbm::SimpleDiffusionDirichlet<LatticeModel_T, flag_t>;
+   using NoDiffusion_T = lbm::NoDiffusion<LatticeModel_T, flag_t>;
+   using FreeDiffusion_T = lbm::FreeDiffusion<LatticeModel_T, FlagField_T>;
 
 public:
-   typedef walberla::boundary::BoundaryHandling< FlagField_T, Stencil, DiffusionDirichlet_T, SimpleDiffusionDirichlet_T, NoDiffusion_T, FreeDiffusion_T, SimpleDiffusionDirichlet_T, SimpleDiffusionDirichlet_T > BoundaryHandling_T;
-   typedef BoundaryHandling_T BoundaryHandling;
+   using BoundaryHandling_T = walberla::boundary::BoundaryHandling<FlagField_T, Stencil, DiffusionDirichlet_T, SimpleDiffusionDirichlet_T, NoDiffusion_T, FreeDiffusion_T, SimpleDiffusionDirichlet_T, SimpleDiffusionDirichlet_T>;
+   using BoundaryHandling = BoundaryHandling_T;
 
    const static FlagUID& getDiffusionDirichletFlagUID()        { static FlagUID uid( "DiffusionDirichlet"        ); return uid; }
    const static FlagUID& getSimpleDiffusionDirichletFlagUID()  { static FlagUID uid( "SimpleDiffusionDirichlet"  ); return uid; }
@@ -87,7 +87,7 @@ private:
    static BoundaryHandling_T* createDefaultDiffusionBoundaryHandlingFactory( 
       IBlock* const block, const StructuredBlockStorage* const /*bs*/, const BlockDataID& flagFieldID, const Set< FlagUID > & domainFlagUIDs, const BlockDataID& pdfFieldID, const Set< FlagUID > & initFlagUIDs )
    {
-      typedef lbm::PdfField< LatticeModel_T > PDFField_T;
+      using PDFField_T = lbm::PdfField<LatticeModel_T>;
 
       WALBERLA_ASSERT_NOT_NULLPTR( block );
 
diff --git a/src/lbm/boundary/factories/ExtendedBoundaryHandlingFactory.h b/src/lbm/boundary/factories/ExtendedBoundaryHandlingFactory.h
index 118065cff9670fa67b3c4055b9199e56087930bc..730ceb008f7897c0d608119ad925940517d28421 100644
--- a/src/lbm/boundary/factories/ExtendedBoundaryHandlingFactory.h
+++ b/src/lbm/boundary/factories/ExtendedBoundaryHandlingFactory.h
@@ -72,19 +72,19 @@ template <typename LatticeModel, typename FlagFieldT >
 class ExtendedBoundaryHandlingFactory
 {
 public:
-   typedef typename FlagFieldT::flag_t            flag_t;
-   typedef typename LatticeModel::Stencil         Stencil;
-   typedef Vector3<real_t>                        Velocity;
-   typedef PdfField< LatticeModel >               PdfFieldLM;
-
-   typedef NoSlip< LatticeModel, flag_t >         BcNoSlip;
-   typedef FreeSlip< LatticeModel, FlagFieldT >   BcFreeSlip;
-   typedef Pressure< LatticeModel, flag_t >       BcPressure;
-   typedef ParserUBB<LatticeModel, flag_t>        BcUBB;
-   typedef Outlet<LatticeModel, FlagFieldT >      BcOutlet;
-   typedef Curved<LatticeModel, FlagFieldT >      BcCurved;
-
-   typedef walberla::boundary::BoundaryHandling< FlagFieldT, Stencil, BcNoSlip, BcFreeSlip, BcPressure, BcUBB, BcOutlet, BcCurved >  BoundaryHandling;
+   using flag_t = typename FlagFieldT::flag_t;
+   using Stencil = typename LatticeModel::Stencil;
+   using Velocity = Vector3<real_t>;
+   using PdfFieldLM = PdfField<LatticeModel>;
+
+   using BcNoSlip = NoSlip<LatticeModel, flag_t>;
+   using BcFreeSlip = FreeSlip<LatticeModel, FlagFieldT>;
+   using BcPressure = Pressure<LatticeModel, flag_t>;
+   using BcUBB = ParserUBB<LatticeModel, flag_t>;
+   using BcOutlet = Outlet<LatticeModel, FlagFieldT>;
+   using BcCurved = Curved<LatticeModel, FlagFieldT>;
+
+   using BoundaryHandling = walberla::boundary::BoundaryHandling<FlagFieldT, Stencil, BcNoSlip, BcFreeSlip, BcPressure, BcUBB, BcOutlet, BcCurved>;
 
    static BlockDataID addBoundaryHandlingToStorage( const shared_ptr< StructuredBlockStorage > & bs, const std::string & identifier,
                                                     BlockDataID flagFieldID, BlockDataID pdfFieldID, const Set< FlagUID > & flagUIDSet)
diff --git a/src/lbm/evaluations/Permeability.h b/src/lbm/evaluations/Permeability.h
index 997bda395c7f048c020f8773b801a45e18cd2aee..94e146f24b63d806fa90e2cc916608a91e0a9501 100644
--- a/src/lbm/evaluations/Permeability.h
+++ b/src/lbm/evaluations/Permeability.h
@@ -43,12 +43,12 @@ class Permeability
 {
 public:
 
-   typedef PdfField_T         PdfField;
-   typedef BoundaryHandling_T BoundaryHandling;
+   using PdfField = PdfField_T;
+   using BoundaryHandling = BoundaryHandling_T;
 
-   typedef typename PdfField_T::LatticeModel      LatticeModel;
-   typedef typename BoundaryHandling_T::FlagField FlagField;
-   typedef typename FlagField::flag_t             flag_t;
+   using LatticeModel = typename PdfField_T::LatticeModel;
+   using FlagField = typename BoundaryHandling_T::FlagField;
+   using flag_t = typename FlagField::flag_t;
 
    Permeability( real_t viscosity, const BlockDataID & pdfFieldId, const BlockDataID & boundaryHandlingId, const FlagUID & fluid,
                  const shared_ptr<blockforest::StructuredBlockForest> blocks );
diff --git a/src/lbm/field/Adaptors.h b/src/lbm/field/Adaptors.h
index 4bb36ca5cb6bb10d578ecbe4ec5f1f3f02c99733..f0c7ee17287c024639309af217d6aa16c44ffa20 100644
--- a/src/lbm/field/Adaptors.h
+++ b/src/lbm/field/Adaptors.h
@@ -37,9 +37,9 @@ template< typename LatticeModel_T >
 class DensityAdaptionFunction
 {
 public:
-   typedef PdfField< LatticeModel_T >                 basefield_t;
-   typedef typename basefield_t::const_base_iterator  basefield_iterator;
-   typedef real_t                                     value_type;
+   using basefield_t = PdfField<LatticeModel_T>;
+   using basefield_iterator = typename basefield_t::const_base_iterator;
+   using value_type = real_t;
 
    static const uint_t F_SIZE = 1u;
 
@@ -62,9 +62,9 @@ template< typename LatticeModel_T >
 class VelocityVectorAdaptionFunction
 {
 public:
-   typedef PdfField< LatticeModel_T >                 basefield_t;
-   typedef typename basefield_t::const_base_iterator  basefield_iterator;
-   typedef Vector3<real_t>                            value_type;
+   using basefield_t = PdfField<LatticeModel_T>;
+   using basefield_iterator = typename basefield_t::const_base_iterator;
+   using value_type = Vector3<real_t>;
 
    static const uint_t F_SIZE = 1u;
 
@@ -86,11 +86,11 @@ template< typename LatticeModel_T, bool stream = false >
 class MomentumDensityVectorAdaptionFunction
 {
 public:
-   typedef PdfField< LatticeModel_T >                 basefield_t;
-   typedef typename basefield_t::const_base_iterator  basefield_iterator;
-   typedef Vector3<real_t>                            value_type;
+   using basefield_t = PdfField<LatticeModel_T>;
+   using basefield_iterator = typename basefield_t::const_base_iterator;
+   using value_type = Vector3<real_t>;
 
-   typedef typename LatticeModel_T::Stencil           Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    static const uint_t F_SIZE = 1u;
 
@@ -114,9 +114,9 @@ template< typename LatticeModel_T >
 class VelocityAdaptionFunction
 {
 public:
-   typedef PdfField< LatticeModel_T >                 basefield_t;
-   typedef typename basefield_t::const_base_iterator  basefield_iterator;
-   typedef real_t                                     value_type;
+   using basefield_t = PdfField<LatticeModel_T>;
+   using basefield_iterator = typename basefield_t::const_base_iterator;
+   using value_type = real_t;
 
    static const uint_t F_SIZE = 3u;
 
@@ -142,11 +142,11 @@ template< typename LatticeModel_T, bool stream = false >
 class MomentumDensityAdaptionFunction
 {
 public:
-   typedef PdfField< LatticeModel_T >                 basefield_t;
-   typedef typename basefield_t::const_base_iterator  basefield_iterator;
-   typedef real_t                                     value_type;
+   using basefield_t = PdfField<LatticeModel_T>;
+   using basefield_iterator = typename basefield_t::const_base_iterator;
+   using value_type = real_t;
 
-   typedef typename LatticeModel_T::Stencil           Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    static const uint_t F_SIZE = 3u;
 
@@ -173,9 +173,9 @@ template< typename LatticeModel_T >
 class ShearRateAdaptionFunction
 {
 public:
-   typedef PdfField< LatticeModel_T >                 basefield_t;
-   typedef typename basefield_t::const_base_iterator  basefield_iterator;
-   typedef real_t                                     value_type;
+   using basefield_t = PdfField<LatticeModel_T>;
+   using basefield_iterator = typename basefield_t::const_base_iterator;
+   using value_type = real_t;
 
    static const uint_t F_SIZE = 1u;
 
@@ -199,15 +199,15 @@ public:
 template< typename LatticeModel_T >
 struct Adaptor
 {
-   typedef field::GhostLayerFieldAdaptor<DensityAdaptionFunction       <LatticeModel_T>, 0> Density;
-   typedef field::GhostLayerFieldAdaptor<VelocityVectorAdaptionFunction<LatticeModel_T>, 0> VelocityVector;
-   typedef field::GhostLayerFieldAdaptor<VelocityAdaptionFunction      <LatticeModel_T>, 0> Velocity;
-   typedef field::GhostLayerFieldAdaptor<ShearRateAdaptionFunction     <LatticeModel_T>, 0> ShearRate;
-
-   typedef field::GhostLayerFieldAdaptor<MomentumDensityVectorAdaptionFunction<LatticeModel_T, true >, 1> StreamMomentumDensityVector;
-   typedef field::GhostLayerFieldAdaptor<MomentumDensityAdaptionFunction      <LatticeModel_T, true >, 1> StreamMomentumDensity;
-   typedef field::GhostLayerFieldAdaptor<MomentumDensityVectorAdaptionFunction<LatticeModel_T, false>, 0> CollideMomentumDensityVector;
-   typedef field::GhostLayerFieldAdaptor<MomentumDensityAdaptionFunction      <LatticeModel_T, false>, 0> CollideMomentumDensity;
+   using Density = field::GhostLayerFieldAdaptor<DensityAdaptionFunction<LatticeModel_T>, 0>;
+   using VelocityVector = field::GhostLayerFieldAdaptor<VelocityVectorAdaptionFunction<LatticeModel_T>, 0>;
+   using Velocity = field::GhostLayerFieldAdaptor<VelocityAdaptionFunction<LatticeModel_T>, 0>;
+   using ShearRate = field::GhostLayerFieldAdaptor<ShearRateAdaptionFunction<LatticeModel_T>, 0>;
+
+   using StreamMomentumDensityVector = field::GhostLayerFieldAdaptor<MomentumDensityVectorAdaptionFunction<LatticeModel_T, true>, 1>;
+   using StreamMomentumDensity = field::GhostLayerFieldAdaptor<MomentumDensityAdaptionFunction<LatticeModel_T, true>, 1>;
+   using CollideMomentumDensityVector = field::GhostLayerFieldAdaptor<MomentumDensityVectorAdaptionFunction<LatticeModel_T, false>, 0>;
+   using CollideMomentumDensity = field::GhostLayerFieldAdaptor<MomentumDensityAdaptionFunction<LatticeModel_T, false>, 0>;
 };
 
 
diff --git a/src/lbm/field/AddToStorage.h b/src/lbm/field/AddToStorage.h
index 3cf80dc43b9783f76b90c003e1d7ccce6a35573a..cd3319a8b0d3305217523ed11ff54bbd0e82a9ee 100644
--- a/src/lbm/field/AddToStorage.h
+++ b/src/lbm/field/AddToStorage.h
@@ -40,8 +40,8 @@ class PdfFieldHandling : public field::BlockDataHandling< PdfField<LatticeModel_
 {
 public:
 
-   typedef PdfField<LatticeModel_T> PdfField_T;
-   typedef field::BlockDataHandling< PdfField_T, LatticeModel_T::Stencil::D == 2 > Base_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Base_T = field::BlockDataHandling<PdfField_T, LatticeModel_T::Stencil::D == 2>;
 
    PdfFieldHandling( const weak_ptr< StructuredBlockStorage > & blocks, const LatticeModel_T & latticeModel,
                      const bool _initialize, const Vector3<real_t> & initialVelocity, const real_t initialDensity,
@@ -50,37 +50,37 @@ public:
       initialize_( _initialize ), initialVelocity_( initialVelocity ), initialDensity_( initialDensity ),
       nrOfGhostLayers_( nrOfGhostLayers ), layout_( layout ) {}
 
-   inline void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer )
+   inline void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override
    {
       packLatticeModel( block, id, buffer );
       Base_T::serialize( block, id, buffer );
    }
 
-   void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child )
+   void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child ) override
    {
       packLatticeModel( block, id, buffer );
       Base_T::serializeCoarseToFine( block, id, buffer, child );
    }
 
-   void serializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer )
+   void serializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override
    {
       packLatticeModel( block, id, buffer );
       Base_T::serializeFineToCoarse( block, id, buffer );
    }
 
-   void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer )
+   void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override
    {
       unpackLatticeModel( block, id, buffer );
       Base_T::deserialize( block, id, buffer );
    }
 
-   void deserializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer )
+   void deserializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override
    {
       unpackLatticeModel( block, id, buffer );
       Base_T::deserializeCoarseToFine( block, id, buffer );
    }
 
-   void deserializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer, const uint_t child )
+   void deserializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer, const uint_t child ) override
    {
       unpackLatticeModel( block, id, buffer );
       Base_T::deserializeFineToCoarse( block, id, buffer, child );
@@ -88,12 +88,12 @@ public:
 
 protected:
 
-   PdfField<LatticeModel_T> * allocate( IBlock * const block )
+   PdfField<LatticeModel_T> * allocate( IBlock * const block ) override
    {
       return allocateDispatch( block, initialize_, initialDensity_ );
    }
 
-   PdfField<LatticeModel_T> * reallocate( IBlock * const block )
+   PdfField<LatticeModel_T> * reallocate( IBlock * const block ) override
    {
 #ifdef NDEBUG
       return allocateDispatch( block, false, initialDensity_ );
diff --git a/src/lbm/field/Density.h b/src/lbm/field/Density.h
index a7c346e13721c9215b67316d440415b3f482607b..3555e79815a1168a0fbed7165a652f6204ae9a0b 100644
--- a/src/lbm/field/Density.h
+++ b/src/lbm/field/Density.h
@@ -80,7 +80,7 @@ struct Density< LatticeModel_T, typename std::enable_if< LatticeModel_T::compres
 template< typename LatticeModel_T >
 struct Density< LatticeModel_T, typename std::enable_if< ! LatticeModel_T::compressible >::type >
 {
-   static_assert( LatticeModel_T::compressible == false, "Only works with incompressible models!" );
+   static_assert( !LatticeModel_T::compressible, "Only works with incompressible models!" );
 
    template< typename FieldPtrOrIterator >
    static inline real_t get( const LatticeModel_T & /*latticeModel*/, const FieldPtrOrIterator & it )
diff --git a/src/lbm/field/DensityAndMomentumDensity.h b/src/lbm/field/DensityAndMomentumDensity.h
index db732eb01596c5913f96d6c89f581009f68abd27..5a1d139b9140281b35a43ad1d59395c7e39fb799 100644
--- a/src/lbm/field/DensityAndMomentumDensity.h
+++ b/src/lbm/field/DensityAndMomentumDensity.h
@@ -112,7 +112,7 @@ real_t getDensityAndMomentumDensityD3Q19( Vector3< real_t > & momentumDensity, c
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q19 >::value), "This function works with D3Q19 stencils only!" );
 
    using namespace stencil;
-   typedef typename LatticeModel_T::Stencil Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    momentumDensity[0] = it[ Stencil::idx[E] ] + it[ Stencil::idx[NE] ] + it[ Stencil::idx[SE] ] + it[ Stencil::idx[TE] ] + it[ Stencil::idx[BE] ] ;
    momentumDensity[1] = it[ Stencil::idx[N] ] + it[ Stencil::idx[NW] ] + it[ Stencil::idx[TN] ] + it[ Stencil::idx[BN] ];
@@ -138,7 +138,7 @@ real_t getDensityAndMomentumDensityD3Q19( Vector3< real_t > & momentumDensity, c
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q19 >::value), "This function works with D3Q19 stencils only!" );
 
    using namespace stencil;
-   typedef typename LatticeModel_T::Stencil Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    const auto & xyz0 = pdf(x,y,z,0);
 
diff --git a/src/lbm/field/DensityVelocityCallback.h b/src/lbm/field/DensityVelocityCallback.h
index 3c7c69087c7a0b302be099642e4ed471ffc1be11..d7c76c5ca808b898659e6998dea85a7b94b1aa36 100644
--- a/src/lbm/field/DensityVelocityCallback.h
+++ b/src/lbm/field/DensityVelocityCallback.h
@@ -246,7 +246,7 @@ class AdvectionDiffusionDensityEquilibriumVelocityCalculation
 public:
 
    AdvectionDiffusionDensityEquilibriumVelocityCalculation( const ConstBlockDataID & velocityFieldId ) :
-      velocityFieldId_( velocityFieldId ), velocityField_( NULL ) {}
+      velocityFieldId_( velocityFieldId ), velocityField_( nullptr ) {}
 
    void operator()( IBlock & block )
    {
diff --git a/src/lbm/field/Equilibrium.h b/src/lbm/field/Equilibrium.h
index 8b6635431de998b1b13fb1eb26861d2c1a297b0f..1fcd480335763de54b7499403f16d1df25a37f42 100644
--- a/src/lbm/field/Equilibrium.h
+++ b/src/lbm/field/Equilibrium.h
@@ -210,7 +210,7 @@ struct Equilibrium< LatticeModel_T, typename std::enable_if< std::is_same< typen
    static_assert( LatticeModel_T::compressible == false,                                       "Only works with incompressible models!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    template< typename FieldPtrOrIterator >
    static void set( FieldPtrOrIterator & it,
@@ -359,7 +359,7 @@ struct Equilibrium< LatticeModel_T, typename std::enable_if< std::is_same< typen
    static_assert( LatticeModel_T::compressible,                                                "Only works with compressible models!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    template< typename FieldPtrOrIterator >
    static void set( FieldPtrOrIterator & it,
diff --git a/src/lbm/field/MomentumDensity.h b/src/lbm/field/MomentumDensity.h
index 782e82ef42f1d16e788245ebe548de3bb8ca94ed..0a79a0fdca59bf03437b4e8f96718588d31dba77 100644
--- a/src/lbm/field/MomentumDensity.h
+++ b/src/lbm/field/MomentumDensity.h
@@ -103,7 +103,7 @@ void getMomentumDensityD3Q19( Vector3< real_t > & momentumDensity, const FieldPt
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q19 >::value), "This function works with D3Q19 stencils only!" );
 
    using namespace stencil;
-   typedef typename LatticeModel_T::Stencil Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    momentumDensity[0] = it[ Stencil::idx[E] ] + it[ Stencil::idx[NE] ] + it[ Stencil::idx[SE] ] + it[ Stencil::idx[TE] ] + it[ Stencil::idx[BE] ] -
                         it[ Stencil::idx[W] ] - it[ Stencil::idx[NW] ] - it[ Stencil::idx[SW] ] - it[ Stencil::idx[TW] ] - it[ Stencil::idx[BW] ];
@@ -120,7 +120,7 @@ void getMomentumDensityD3Q19( Vector3< real_t > & momentumDensity, const PdfFiel
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q19 >::value), "This function works with D3Q19 stencils only!" );
 
    using namespace stencil;
-   typedef typename LatticeModel_T::Stencil Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    const auto & xyz0 = pdf(x,y,z,0);
 
diff --git a/src/lbm/field/PdfField.h b/src/lbm/field/PdfField.h
index 4fd11e3b9025f06c5052cc9060745bb79613666e..0536b7d1da12d42a799e959c5fb3bb9c6e078586 100644
--- a/src/lbm/field/PdfField.h
+++ b/src/lbm/field/PdfField.h
@@ -78,22 +78,22 @@ public:
    //** Type Definitions  **********************************************************************************************
    /*! \name Type Definitions */
    //@{
-   typedef LatticeModel_T                    LatticeModel;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using LatticeModel = LatticeModel_T;
+   using Stencil = typename LatticeModel_T::Stencil;
 
-   typedef typename GhostLayerField< real_t, Stencil::Size >::value_type             value_type;
+   using value_type = typename GhostLayerField<real_t, Stencil::Size>::value_type;
 
-   typedef typename GhostLayerField< real_t, Stencil::Size >::iterator               iterator;
-   typedef typename GhostLayerField< real_t, Stencil::Size >::const_iterator         const_iterator;
+   using iterator = typename GhostLayerField<real_t, Stencil::Size>::iterator;
+   using const_iterator = typename GhostLayerField<real_t, Stencil::Size>::const_iterator;
 
-   typedef typename GhostLayerField< real_t, Stencil::Size >::reverse_iterator       reverse_iterator;
-   typedef typename GhostLayerField< real_t, Stencil::Size >::const_reverse_iterator const_reverse_iterator;
+   using reverse_iterator = typename GhostLayerField<real_t, Stencil::Size>::reverse_iterator;
+   using const_reverse_iterator = typename GhostLayerField<real_t, Stencil::Size>::const_reverse_iterator;
 
-   typedef typename GhostLayerField< real_t, Stencil::Size >::base_iterator          base_iterator;
-   typedef typename GhostLayerField< real_t, Stencil::Size >::const_base_iterator    const_base_iterator;
+   using base_iterator = typename GhostLayerField<real_t, Stencil::Size>::base_iterator;
+   using const_base_iterator = typename GhostLayerField<real_t, Stencil::Size>::const_base_iterator;
 
-   typedef typename GhostLayerField< real_t, Stencil::Size >::Ptr                    Ptr;
-   typedef typename GhostLayerField< real_t, Stencil::Size >::ConstPtr               ConstPtr;
+   using Ptr = typename GhostLayerField<real_t, Stencil::Size>::Ptr;
+   using ConstPtr = typename GhostLayerField<real_t, Stencil::Size>::ConstPtr;
    //@}
    //*******************************************************************************************************************
 
@@ -104,8 +104,7 @@ public:
              const uint_t ghostLayers = uint_t(1), const field::Layout & _layout = field::zyxf,
              const shared_ptr< field::FieldAllocator<real_t> > & alloc = shared_ptr< field::FieldAllocator<real_t> >() );
 
-   virtual ~PdfField() = default;
-
+   ~PdfField() override = default;
 
 
    //inline bool operator==( const PdfField & rhs ) const; // TODO! -> ticket
@@ -282,7 +281,7 @@ protected:
    /*! \name Shallow Copy */
    //@{
    inline PdfField( const PdfField< LatticeModel_T > & other );
-   Field< real_t, Stencil::Size > * cloneShallowCopyInternal() const { return new PdfField< LatticeModel_T >( *this ); }
+   Field< real_t, Stencil::Size > * cloneShallowCopyInternal() const override { return new PdfField< LatticeModel_T >( *this ); }
    //@}
    //*******************************************************************************************************************
 
diff --git a/src/lbm/field/QCriterion.h b/src/lbm/field/QCriterion.h
index eff9b801523f5d5e696730306527f3239dd1507c..c681117781d1d6744f49703ccf4d3258da2bfeed 100644
--- a/src/lbm/field/QCriterion.h
+++ b/src/lbm/field/QCriterion.h
@@ -28,36 +28,40 @@
 // You should never use these functions directly, always refer to the member functions
 // of PdfField or the free functions that can be found in MacroscopicValueCalculation.h
 
-namespace walberla {
-namespace lbm {
-
-struct QCriterion {
+namespace walberla
+{
+namespace lbm
+{
+struct QCriterion
+{
    template< typename VelocityField_T, typename Filter_T >
-   static inline real_t get(const VelocityField_T & velocityField, const Filter_T & filter,
-           const cell_idx_t x, const cell_idx_t y, const cell_idx_t z,
-           real_t dx = real_t(1), real_t dy = real_t(1), real_t dz = real_t(1)) {
+   static inline real_t get(const VelocityField_T& velocityField, const Filter_T& filter, const cell_idx_t x,
+                            const cell_idx_t y, const cell_idx_t z, real_t dx = real_t(1), real_t dy = real_t(1),
+                            real_t dz = real_t(1))
+   {
       const auto one = cell_idx_t(1);
 
-      if(filter(x,y,z) && filter(x+one,y,z) && filter(x-one,y,z) && filter(x,y+one,z)
-         && filter(x,y-one,z) && filter(x,y,z+one) && filter(x,y,z-one)) {
-         const Vector3<real_t> xa = velocityField.get(x+one,y,z);
-         const Vector3<real_t> xb = velocityField.get(x-one,y,z);
-         const Vector3<real_t> ya = velocityField.get(x,y+one,z);
-         const Vector3<real_t> yb = velocityField.get(x,y-one,z);
-         const Vector3<real_t> za = velocityField.get(x,y,z+one);
-         const Vector3<real_t> zb = velocityField.get(x,y,z-one);
+      auto f(velocityField.flattenedShallowCopy());
+
+      if (filter(x, y, z) && filter(x + one, y, z) && filter(x - one, y, z) && filter(x, y + one, z) &&
+          filter(x, y - one, z) && filter(x, y, z + one) && filter(x, y, z - one))
+      {
+         Vector3< real_t > xa(f->get(x + one, y, z, 0), f->get(x + one, y, z, 1), f->get(x + one, y, z, 2));
+         Vector3< real_t > xb(f->get(x - one, y, z, 0), f->get(x - one, y, z, 1), f->get(x - one, y, z, 2));
+         Vector3< real_t > ya(f->get(x, y + one, z, 0), f->get(x, y + one, z, 1), f->get(x, y + one, z, 2));
+         Vector3< real_t > yb(f->get(x, y - one, z, 0), f->get(x, y - one, z, 1), f->get(x, y - one, z, 2));
+         Vector3< real_t > za(f->get(x, y, z + one, 0), f->get(x, y, z + one, 1), f->get(x, y, z + one, 2));
+         Vector3< real_t > zb(f->get(x, y, z - one, 0), f->get(x, y, z - one, 1), f->get(x, y, z - one, 2));
 
          return calculate(xa, xb, ya, yb, za, zb, dx, dy, dz);
       }
-
       return real_t(0);
    }
 
-   static inline real_t calculate(const Vector3<real_t> xa, const Vector3<real_t> xb,
-           const Vector3<real_t> ya, const Vector3<real_t> yb,
-           const Vector3<real_t> za, const Vector3<real_t> zb,
-           const real_t dx, const real_t dy, const real_t dz) {
-
+   static inline real_t calculate(const Vector3< real_t > xa, const Vector3< real_t > xb, const Vector3< real_t > ya,
+                                  const Vector3< real_t > yb, const Vector3< real_t > za, const Vector3< real_t > zb,
+                                  const real_t dx, const real_t dy, const real_t dz)
+   {
       const auto halfInvDx = real_t(0.5) / dx;
       const auto halfInvDy = real_t(0.5) / dy;
       const auto halfInvDz = real_t(0.5) / dz;
@@ -75,18 +79,17 @@ struct QCriterion {
       const real_t duzdz = (za[2] - zb[2]) * halfInvDz;
 
       // Q = 1/2 * (||W||² - ||S||²)
-      real_t sNormSq = duxdx*duxdx + duydy*duydy + duzdz*duzdz +
-              real_t(0.5)*(duxdy+duydx)*(duxdy+duydx) + real_t(0.5)*(duydz+duzdy)*(duydz+duzdy) +
-              real_t(0.5)*(duxdz+duzdx)*(duxdz+duzdx);
+      real_t sNormSq = duxdx * duxdx + duydy * duydy + duzdz * duzdz + real_t(0.5) * (duxdy + duydx) * (duxdy + duydx) +
+                       real_t(0.5) * (duydz + duzdy) * (duydz + duzdy) +
+                       real_t(0.5) * (duxdz + duzdx) * (duxdz + duzdx);
 
-      real_t omegaNormSq = real_t(0.5)*(duxdz-duzdx)*(duxdz-duzdx) +
-              real_t(0.5)*(duxdy-duydx)*(duxdy-duydx) +
-              real_t(0.5)*(duydz-duzdy)*(duydz-duzdy);
+      real_t omegaNormSq = real_t(0.5) * (duxdz - duzdx) * (duxdz - duzdx) +
+                           real_t(0.5) * (duxdy - duydx) * (duxdy - duydx) +
+                           real_t(0.5) * (duydz - duzdy) * (duydz - duzdy);
 
       return real_t(0.5) * (omegaNormSq - sNormSq);
    }
 };
 
-
 } // namespace lbm
 } // namespace walberla
diff --git a/src/lbm/field/ShearRate.h b/src/lbm/field/ShearRate.h
index 2352a9782cb9fd0c87bf231a64f0f11c8a285308..d98a169a9f7c27348218d2629f9c5ffa15aad0ad 100644
--- a/src/lbm/field/ShearRate.h
+++ b/src/lbm/field/ShearRate.h
@@ -89,8 +89,8 @@ namespace internal {
 template< typename LatticeModel_T >
 struct ShearRate
 {
-   typedef typename LatticeModel_T::Stencil Stencil;
-   typedef typename internal::ShearRelaxationParameter<typename LatticeModel_T::CollisionModel> ShearRelaxationParameter;
+   using Stencil = typename LatticeModel_T::Stencil;
+   using ShearRelaxationParameter = typename internal::ShearRelaxationParameter<typename LatticeModel_T::CollisionModel>;
 
    template< typename FieldPtrOrIterator >
    static inline real_t get( const LatticeModel_T & latticeModel, const FieldPtrOrIterator & it,
diff --git a/src/lbm/field/initializer/ExprSystemInitFunction.cpp b/src/lbm/field/initializer/ExprSystemInitFunction.cpp
index abb2f028c79a1f737d8fd954ba7904208eb2bcf1..bb3f5ebcd44d6adfaca910d1a67f9c930b43c149 100644
--- a/src/lbm/field/initializer/ExprSystemInitFunction.cpp
+++ b/src/lbm/field/initializer/ExprSystemInitFunction.cpp
@@ -27,9 +27,7 @@
 #   pragma warning( disable : 4706 )
 #elif ( defined WALBERLA_CXX_COMPILER_IS_GNU ) || ( defined WALBERLA_CXX_COMPILER_IS_CLANG )
 #   pragma GCC diagnostic push
-#   if !( ( __clang_major__ == 3 ) && ( __clang_minor__ <= 4 ) )
-#     pragma GCC diagnostic ignored "-Wpragmas"
-#   endif
+#   pragma GCC diagnostic ignored "-Wpragmas"
 #   pragma GCC diagnostic ignored "-Wsign-conversion"
 #   pragma GCC diagnostic ignored "-Wconversion"
 #   pragma GCC diagnostic ignored "-Wshorten-64-to-32"
diff --git a/src/lbm/field/initializer/ExprSystemInitFunction.h b/src/lbm/field/initializer/ExprSystemInitFunction.h
index 4d8557f7c1ebc4c68e8cc382c93b904744cad0c8..32d91f43f98fe9a632c83e6e2e2396c8b27999ba 100644
--- a/src/lbm/field/initializer/ExprSystemInitFunction.h
+++ b/src/lbm/field/initializer/ExprSystemInitFunction.h
@@ -43,9 +43,9 @@ class ExprSystemInitFunction
 {
 public:
 
-   typedef exprtk::symbol_table<real_t>      SymbolTable;
-   typedef exprtk::expression<real_t>        Expression;
-   typedef std::map<std::string, Expression> ExprSystem;
+   using SymbolTable = exprtk::symbol_table<real_t>;
+   using Expression = exprtk::expression<real_t>;
+   using ExprSystem = std::map<std::string, Expression>;
 
    ExprSystemInitFunction( const shared_ptr<StructuredBlockForest> & blocks );
    ~ExprSystemInitFunction();
diff --git a/src/lbm/field/initializer/PdfFieldInitializer.h b/src/lbm/field/initializer/PdfFieldInitializer.h
index 0558b1b5279af9b50293cc9516da13a2118cba12..5a718f8d77af614d9397967c9f159a3814f4587c 100644
--- a/src/lbm/field/initializer/PdfFieldInitializer.h
+++ b/src/lbm/field/initializer/PdfFieldInitializer.h
@@ -55,7 +55,7 @@ public:
 
 private:
 
-   typedef lbm::PdfField<LatticeModel_T> PdfField_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
 
    const BlockDataID                       pdfFieldId_;
    const shared_ptr<StructuredBlockForest> blocks_;
diff --git a/src/lbm/geometry/initializer/PoiseuilleInitializer.h b/src/lbm/geometry/initializer/PoiseuilleInitializer.h
index 4315bed9cc9a854d44bc78d556021301417ef1bf..fa6677a447ce3e5a6a253a6e93ce167bd02ab36b 100644
--- a/src/lbm/geometry/initializer/PoiseuilleInitializer.h
+++ b/src/lbm/geometry/initializer/PoiseuilleInitializer.h
@@ -82,7 +82,7 @@ namespace initializer {
          Z_AXIS=2,
          INVALID_AXIS=3
       };*/
-      typedef uint_t Axis;
+      using Axis = uint_t;
       static const uint_t X_AXIS;
       static const uint_t Y_AXIS;
       static const uint_t Z_AXIS;
@@ -93,7 +93,7 @@ namespace initializer {
                   field::FlagUID pressureFlag1, field::FlagUID pressureFlag2 );
 
 
-      virtual void init( BlockStorage & , const Config::BlockHandle & blockHandle )    {  init( blockHandle );   }
+      void init( BlockStorage & , const Config::BlockHandle & blockHandle ) override    {  init( blockHandle );   }
 
       void init( const Config::BlockHandle & blockHandle );
       void init( Scenario scenario, BoundaryType boundaryType, real_t pressureDiff, Axis flowAxis, Axis parabolaAxis = INVALID_AXIS );
diff --git a/src/lbm/lattice_model/CollisionModel.h b/src/lbm/lattice_model/CollisionModel.h
index df97cc3c5644a74a88d59ae37d0397d4b282eeac..bd521e042dedf772992dbf110d8751be30c95570 100644
--- a/src/lbm/lattice_model/CollisionModel.h
+++ b/src/lbm/lattice_model/CollisionModel.h
@@ -81,7 +81,7 @@ class SRT
 {
 public:
 
-   typedef SRT_tag tag;
+   using tag = SRT_tag;
    static const bool constant = true;
    
    SRT( const real_t _omega, const uint_t _level = uint_t(0) ) :
@@ -138,14 +138,14 @@ class SRTField
 {
 public:
 
-   typedef SRT_tag tag;
+   using tag = SRT_tag;
    static const bool constant = false;
 
    SRTField() :
       omegaFieldId_(), omegaField_( NULL ), level_( uint_t(0) ) {}
 
    SRTField( const BlockDataID & omegaFieldId, const uint_t _level = uint_t(0) ) :
-      omegaFieldId_( omegaFieldId ), omegaField_( NULL ), level_( _level ) {}
+      omegaFieldId_( omegaFieldId ), omegaField_( nullptr ), level_( _level ) {}
 
    void setFieldId( const BlockDataID & omegaFieldId, const uint_t _level = uint_t(0) )
    {
@@ -212,7 +212,7 @@ class TRT
 {
 public:
 
-   typedef TRT_tag tag;
+   using tag = TRT_tag;
    
    static const real_t threeSixteenth;
 
@@ -332,7 +332,7 @@ class D3Q19MRT
 {
 public:
 
-   typedef MRT_tag tag;
+   using tag = MRT_tag;
    
    static const real_t threeSixteenth;
    
@@ -581,7 +581,7 @@ class D3Q27Cumulant
 {
 public:
   
-   typedef Cumulant_tag tag;
+   using tag = Cumulant_tag;
 
    /// Initializes all omegas to one except omega1
    D3Q27Cumulant( const real_t _omega1, const uint_t _level = uint_t(0) ) :
diff --git a/src/lbm/lattice_model/D2Q9.h b/src/lbm/lattice_model/D2Q9.h
index a4080f3bc2a89ad6b201039e6308607f1216a848..831159c8fedf611f766ac10d2676c86b72162025 100644
--- a/src/lbm/lattice_model/D2Q9.h
+++ b/src/lbm/lattice_model/D2Q9.h
@@ -39,11 +39,11 @@ public:
 
    static_assert( ( ! std::is_same< CollisionModel_T, collision_model::D3Q19MRT >::value), "D3Q19MRT only works with D3Q19!" );
 
-   typedef typename LatticeModelBase< CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder >::CollisionModel  CollisionModel;
-   typedef typename LatticeModelBase< CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder >::ForceModel      ForceModel;
+   using CollisionModel = typename LatticeModelBase<CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder>::CollisionModel;
+   using ForceModel = typename LatticeModelBase<CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder>::ForceModel;
 
-   typedef stencil::D2Q9 Stencil;
-   typedef stencil::D2Q9 CommunicationStencil;
+   using Stencil = stencil::D2Q9;
+   using CommunicationStencil = stencil::D2Q9;
 
    static const char * NAME;
 
@@ -63,11 +63,11 @@ public:
       static_assert( (std::is_same< ForceModel_T, force_model::None >::value), "This constructor is only available if the force model is equal to force_model::None!" );
    }
 
-   virtual ~D2Q9() {}
+   ~D2Q9() override = default;
 
 protected:
 
-   virtual void config( IBlock & /*block*/, StructuredBlockStorage & /*sbs*/ ) {}
+   void config( IBlock & /*block*/, StructuredBlockStorage & /*sbs*/ ) override {}
 };
 
 template< typename CM, bool C, typename FM, int EAO > const char*  D2Q9<CM,C,FM,EAO>::NAME = "D2Q9";
diff --git a/src/lbm/lattice_model/D3Q15.h b/src/lbm/lattice_model/D3Q15.h
index e969749a4907fbb270350475c0d0a60bb4aa6cb9..42dc975fd76ab5d6d14d0ce6a8806aa3d9028e4b 100644
--- a/src/lbm/lattice_model/D3Q15.h
+++ b/src/lbm/lattice_model/D3Q15.h
@@ -38,11 +38,11 @@ class D3Q15 : public LatticeModelBase< CollisionModel_T, Compressible, ForceMode
 {
 public:
 
-   typedef typename LatticeModelBase< CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder >::CollisionModel  CollisionModel;
-   typedef typename LatticeModelBase< CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder >::ForceModel      ForceModel;
+   using CollisionModel = typename LatticeModelBase<CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder>::CollisionModel;
+   using ForceModel = typename LatticeModelBase<CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder>::ForceModel;
 
-   typedef stencil::D3Q15 Stencil;
-   typedef stencil::D3Q27 CommunicationStencil;
+   using Stencil = stencil::D3Q15;
+   using CommunicationStencil = stencil::D3Q27;
 
    static const char * NAME;
 
@@ -62,11 +62,11 @@ public:
       static_assert( (std::is_same< ForceModel_T, force_model::None >::value), "This constructor is only available if the force model is equal to force_model::None!" );
    }
 
-   virtual ~D3Q15() {}
+   ~D3Q15() override = default;
 
 protected:
 
-   virtual void config( IBlock & /*block*/, StructuredBlockStorage & /*sbs*/ ) {}
+   void config( IBlock & /*block*/, StructuredBlockStorage & /*sbs*/ ) override {}
 };
 
 template< typename CM, bool C, typename FM, int EAO > const char*  D3Q15<CM,C,FM,EAO>::NAME = "D3Q15";
diff --git a/src/lbm/lattice_model/D3Q19.h b/src/lbm/lattice_model/D3Q19.h
index e196e4ede7b1fefc1394b53ae8e9d6b55a05cc39..f6decf3cff344f9f00971e476a0a72d1b716ebc0 100644
--- a/src/lbm/lattice_model/D3Q19.h
+++ b/src/lbm/lattice_model/D3Q19.h
@@ -37,11 +37,11 @@ class D3Q19 : public LatticeModelBase< CollisionModel_T, Compressible, ForceMode
 {
 public:
 
-   typedef typename LatticeModelBase< CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder >::CollisionModel  CollisionModel;
-   typedef typename LatticeModelBase< CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder >::ForceModel      ForceModel;
+   using CollisionModel = typename LatticeModelBase<CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder>::CollisionModel;
+   using ForceModel = typename LatticeModelBase<CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder>::ForceModel;
 
-   typedef stencil::D3Q19 Stencil;
-   typedef stencil::D3Q19 CommunicationStencil;
+   using Stencil = stencil::D3Q19;
+   using CommunicationStencil = stencil::D3Q19;
 
    static const char * NAME;
 
@@ -61,11 +61,11 @@ public:
       static_assert( (std::is_same< ForceModel_T, force_model::None >::value), "This constructor is only available if the force model is equal to force_model::None!" );
    }
 
-   virtual ~D3Q19() {}
+   ~D3Q19() override = default;
 
 protected:
 
-   virtual void config( IBlock & /*block*/, StructuredBlockStorage & /*sbs*/ ) {}
+   void config( IBlock & /*block*/, StructuredBlockStorage & /*sbs*/ ) override {}
 };
 
 template< typename CM, bool C, typename FM, int EAO > const char*  D3Q19<CM,C,FM,EAO>::NAME = "D3Q19";
diff --git a/src/lbm/lattice_model/D3Q27.h b/src/lbm/lattice_model/D3Q27.h
index 72b5188f5d471069d3e42fece3cdf2b146bbc46e..e90c078c76b7507bc4d9b3732d93ff0a8fe012c8 100644
--- a/src/lbm/lattice_model/D3Q27.h
+++ b/src/lbm/lattice_model/D3Q27.h
@@ -39,11 +39,11 @@ public:
 
    static_assert( ( ! std::is_same< CollisionModel_T, collision_model::D3Q19MRT >::value), "D3Q19MRT only works with D3Q19!" );
 
-   typedef typename LatticeModelBase< CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder >::CollisionModel  CollisionModel;
-   typedef typename LatticeModelBase< CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder >::ForceModel      ForceModel;
+   using CollisionModel = typename LatticeModelBase<CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder>::CollisionModel;
+   using ForceModel = typename LatticeModelBase<CollisionModel_T, Compressible, ForceModel_T, EquilibriumAccuracyOrder>::ForceModel;
 
-   typedef stencil::D3Q27 Stencil;
-   typedef stencil::D3Q27 CommunicationStencil;
+   using Stencil = stencil::D3Q27;
+   using CommunicationStencil = stencil::D3Q27;
 
    static const char * NAME;
 
@@ -64,11 +64,11 @@ public:
       static_assert( (std::is_same< ForceModel_T, force_model::None >::value), "This constructor is only available if the force model is equal to force_model::None!" );
    }
 
-   virtual ~D3Q27() {}
+   ~D3Q27() override = default;
 
 protected:
 
-   virtual void config( IBlock & /*block*/, StructuredBlockStorage & /*sbs*/ ) {}
+   void config( IBlock & /*block*/, StructuredBlockStorage & /*sbs*/ ) override {}
 };
 
 template< typename CM, bool C, typename FM, int EAO > const char*  D3Q27<CM,C,FM,EAO>::NAME = "D3Q27";
diff --git a/src/lbm/lattice_model/EquilibriumDistribution.h b/src/lbm/lattice_model/EquilibriumDistribution.h
index 31daeb24681a6a0cc3f0391ca1946f85cb5273ea..48eab60f5a4c5f4f41fab494fd9ac09b427ecf70 100644
--- a/src/lbm/lattice_model/EquilibriumDistribution.h
+++ b/src/lbm/lattice_model/EquilibriumDistribution.h
@@ -71,7 +71,7 @@ public:
    static_assert( LatticeModel_T::compressible == false,         "Only works with incompressible models!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef typename LatticeModel_T::Stencil Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    static real_t get( const real_t cx, const real_t cy, const real_t cz, const real_t w,
                       const Vector3< real_t > & velocity = Vector3< real_t >( real_t(0.0) ), const real_t rho = real_t(1.0) )
@@ -127,7 +127,7 @@ public:
    static_assert( LatticeModel_T::compressible == false,         "Only works with incompressible models!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 1, "Only works for lattice models that require the equilibrium distribution to be order 1 accurate!" );
 
-   typedef typename LatticeModel_T::Stencil Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    static real_t get( const real_t cx, const real_t cy, const real_t cz, const real_t w,
                       const Vector3< real_t > & velocity = Vector3< real_t >( real_t(0.0) ), const real_t rho = real_t(1.0) )
@@ -176,7 +176,7 @@ public:
    static_assert( LatticeModel_T::compressible,                  "Only works with compressible models!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef typename LatticeModel_T::Stencil Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    static real_t get( const real_t cx, const real_t cy, const real_t cz, const real_t w,
                       const Vector3< real_t > & velocity = Vector3< real_t >( real_t(0.0) ), const real_t rho = real_t(1.0) )
@@ -231,7 +231,7 @@ public:
    static_assert( LatticeModel_T::compressible,                  "Only works with compressible models!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 1, "Only works for lattice models that require the equilibrium distribution to be order 1 accurate!" );
 
-   typedef typename LatticeModel_T::Stencil Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    static real_t get( const real_t cx, const real_t cy, const real_t cz, const real_t w,
                       const Vector3< real_t > & velocity = Vector3< real_t >( real_t(0.0) ), const real_t rho = real_t(1.0) )
diff --git a/src/lbm/lattice_model/ForceModel.h b/src/lbm/lattice_model/ForceModel.h
index 9b01b48b2d261810ce6e58f4efa37aceae2c2f3a..0c30441cae6c4bb32ebcb826c68909225e203863 100644
--- a/src/lbm/lattice_model/ForceModel.h
+++ b/src/lbm/lattice_model/ForceModel.h
@@ -143,8 +143,8 @@ class None
 {
 public:
 
-   typedef None_tag tag;
-   typedef NoDirectionIndependentTerms DirectionIndependentTerms_T;
+   using tag = None_tag;
+   using DirectionIndependentTerms_T = NoDirectionIndependentTerms;
 
    static const bool shiftMacVel = false;
    static const bool shiftEquVel = false;
@@ -180,8 +180,8 @@ class SimpleConstant
 {
 public:
 
-   typedef Simple_tag tag;
-   typedef NoDirectionIndependentTerms DirectionIndependentTerms_T;
+   using tag = Simple_tag;
+   using DirectionIndependentTerms_T = NoDirectionIndependentTerms;
 
    static const bool shiftMacVel = true;
    static const bool shiftEquVel = false;
@@ -277,9 +277,9 @@ private:
 
 public:
 
-   typedef EDM_tag tag;
-   typedef real_t DirectionIndependentTerms_T;
-   typedef ForceField_T ForceField;
+   using tag = EDM_tag;
+   using DirectionIndependentTerms_T = real_t;
+   using ForceField = ForceField_T;
 
    static const bool shiftMacVel = true;
    static const bool shiftEquVel = false;
@@ -337,8 +337,8 @@ class LuoConstant
 {
 public:
 
-   typedef Luo_tag tag;
-   typedef NoDirectionIndependentTerms DirectionIndependentTerms_T;
+   using tag = Luo_tag;
+   using DirectionIndependentTerms_T = NoDirectionIndependentTerms;
 
    static const bool shiftMacVel = true;
    static const bool shiftEquVel = false;
@@ -404,9 +404,9 @@ class LuoField
 {
 public:
 
-   typedef Luo_tag tag;
-   typedef NoDirectionIndependentTerms DirectionIndependentTerms_T;
-   typedef ForceField_T ForceField;
+   using tag = Luo_tag;
+   using DirectionIndependentTerms_T = NoDirectionIndependentTerms;
+   using ForceField = ForceField_T;
 
    static const bool shiftMacVel = true;
    static const bool shiftEquVel = false;
@@ -460,8 +460,8 @@ class GuoConstant
 {
 public:
 
-   typedef Guo_tag tag;
-   typedef Matrix3<real_t> DirectionIndependentTerms_T;
+   using tag = Guo_tag;
+   using DirectionIndependentTerms_T = Matrix3<real_t>;
 
    static const bool shiftMacVel = true;
    static const bool shiftEquVel = true;
@@ -553,9 +553,9 @@ class GuoField
 {
 public:
 
-   typedef Guo_tag tag;
-   typedef Matrix3<real_t> DirectionIndependentTerms_T;
-   typedef ForceField_T ForceField;
+   using tag = Guo_tag;
+   using DirectionIndependentTerms_T = Matrix3<real_t>;
+   using ForceField = ForceField_T;
 
    static const bool shiftMacVel = true;
    static const bool shiftEquVel = true;
@@ -563,7 +563,7 @@ public:
    static const bool constant = false;
 
    GuoField( const BlockDataID & forceFieldId ) :
-      forceFieldId_( forceFieldId ), forceField_( NULL ) {}
+      forceFieldId_( forceFieldId ), forceField_( nullptr ) {}
 
    void pack( mpi::SendBuffer & buffer ) const { buffer << forceFieldId_; }
    void unpack( mpi::RecvBuffer & buffer ) { buffer >> forceFieldId_; }
@@ -635,8 +635,8 @@ class Correction
 {
 public:
 
-   typedef Correction_tag tag;
-   typedef Vector3<real_t> DirectionIndependentTerms_T;
+   using tag = Correction_tag;
+   using DirectionIndependentTerms_T = Vector3<real_t>;
 
    static const bool shiftMacVel = false;
    static const bool shiftEquVel = false;
@@ -644,7 +644,7 @@ public:
    static const bool constant = true;
 
    Correction( const BlockDataID & previousRhoVelocityId ) :
-      force_( real_t(0) ), previousRhoVelocityId_( previousRhoVelocityId ), previousRhoVelocity_(NULL) {}
+      force_( real_t(0) ), previousRhoVelocityId_( previousRhoVelocityId ), previousRhoVelocity_(nullptr) {}
 
    void pack( mpi::SendBuffer & buffer ) const { buffer << force_ << previousRhoVelocityId_; }
    void unpack( mpi::RecvBuffer & buffer ) { buffer >> force_ >> previousRhoVelocityId_; }
diff --git a/src/lbm/lattice_model/LatticeModelBase.h b/src/lbm/lattice_model/LatticeModelBase.h
index 385e9abcccfd31d3ef11a64fc9ce464963ae49ed..3ded2ae7e4b3aecd09547c25265a4c1cfeed9678 100644
--- a/src/lbm/lattice_model/LatticeModelBase.h
+++ b/src/lbm/lattice_model/LatticeModelBase.h
@@ -71,8 +71,8 @@ class LatticeModelBase
 {
 public:
 
-   typedef CollisionModel_T CollisionModel;
-   typedef ForceModel_T     ForceModel;
+   using CollisionModel = CollisionModel_T;
+   using ForceModel = ForceModel_T;
 
    static const bool compressible = Compressible;
    static const int  equilibriumAccuracyOrder = EquilibriumAccuracyOrder;
@@ -82,7 +82,7 @@ public:
    LatticeModelBase( const CollisionModel_T & cm, const ForceModel_T & fm ) :
       collisionModel_( cm ), forceModel_( fm ) {}
 
-   virtual ~LatticeModelBase() {}
+   virtual ~LatticeModelBase() = default;
 
    virtual void pack( mpi::SendBuffer & buffer ) const
    {
diff --git a/src/lbm/lattice_model/NeighborsStencil.h b/src/lbm/lattice_model/NeighborsStencil.h
index 32badbbd354f5b87e97a865bc8f33b7ee438232b..823b45383483172ac1864f31cb47109f4ef0cad4 100644
--- a/src/lbm/lattice_model/NeighborsStencil.h
+++ b/src/lbm/lattice_model/NeighborsStencil.h
@@ -39,13 +39,13 @@ struct NeighborsStencil;
 template< typename LatticeModel_T >
 struct NeighborsStencil< LatticeModel_T, typename std::enable_if< LatticeModel_T::Stencil::D == 2 >::type >
 {
-   typedef stencil::D2Q9 type;
+   using type = stencil::D2Q9;
 };
 
 template< typename LatticeModel_T >
 struct NeighborsStencil< LatticeModel_T, typename std::enable_if< LatticeModel_T::Stencil::D == 3 >::type >
 {
-   typedef stencil::D3Q27 type;
+   using type = stencil::D3Q27;
 };
 
 
diff --git a/src/lbm/lattice_model/SmagorinskyLES.h b/src/lbm/lattice_model/SmagorinskyLES.h
index 169439e77c9d4341c86654f70756ee193797ec16..167f88e4fea0eb4b0d0b79ede397fbada33350ae 100644
--- a/src/lbm/lattice_model/SmagorinskyLES.h
+++ b/src/lbm/lattice_model/SmagorinskyLES.h
@@ -44,9 +44,9 @@ class SmagorinskyLES
 {
 public:
 
-   typedef lbm::PdfField< LatticeModel_T >   PdfField_T;
-   typedef GhostLayerField< real_t, 1 >      ScalarField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
+   using ScalarField_T = GhostLayerField<real_t, 1>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    SmagorinskyLES( weak_ptr< StructuredBlockStorage > blocks, const ConstBlockDataID & pdfFieldId, const BlockDataID & omegaFieldId,
                    const real_t & kinematicViscosity, const real_t & smagorinskyConstant,
diff --git a/src/lbm/python/ExportBasic.cpp b/src/lbm/python/ExportBasic.cpp
deleted file mode 100644
index b8af2324a500ffa185f31dbbc250e8f4b0a9877b..0000000000000000000000000000000000000000
--- a/src/lbm/python/ExportBasic.cpp
+++ /dev/null
@@ -1,244 +0,0 @@
-//======================================================================================================================
-//
-//  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 PythonExports.cpp
-//! \ingroup domain_decomposition
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-// Do not reorder includes - the include order is important
-#include "python_coupling/PythonWrapper.h"
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-#include "ExportBasic.h"
-#include "core/logging/Logging.h"
-#include "python_coupling/Manager.h"
-#include "python_coupling/helper/ModuleScope.h"
-
-#include "domain_decomposition/BlockDataID.h"
-
-#include "lbm/field/Adaptors.h"
-#include "lbm/lattice_model/CollisionModel.h"
-#include "lbm/lattice_model/D3Q19.h"
-#include "lbm/lattice_model/ForceModel.h"
-
-
-using namespace boost::python;
-
-
-namespace walberla {
-namespace lbm {
-
-
-namespace internal {
-
-   using namespace boost::python;
-
-
-   template<typename ForceModel_T> bool FM_getShiftMacVel( const ForceModel_T & ) { return ForceModel_T::shiftMacVel; }
-   template<typename ForceModel_T> bool FM_getShiftEquVel( const ForceModel_T & ) { return ForceModel_T::shiftEquVel; }
-   template<typename ForceModel_T> bool FM_getConstant   ( const ForceModel_T & ) { return ForceModel_T::constant;    }
-
-   template<typename ForceModel_T>
-   void addForceModelDefs( class_< ForceModel_T> & c )
-   {
-      c.add_property( "shiftMacVel",            FM_getShiftMacVel<ForceModel_T> )
-       .add_property( "shiftEquVel",            FM_getShiftEquVel<ForceModel_T> )
-       .add_property( "constant",               FM_getConstant   <ForceModel_T>)
-       .def( "setConstantBodyForceIfPossible", &ForceModel_T::setConstantBodyForceIfPossible )
-      ;
-   }
-
-
-   list getMRTRelaxationRates( const collision_model::D3Q19MRT & l )
-   {
-      list result;
-      for(uint_t i=0; i<19; ++i )
-         result.append( l.s(i) );
-      return result;
-   }
-
-   list getCumulantRelaxationRates( const collision_model::D3Q27Cumulant & l )
-   {
-      list result;
-      for(uint_t i=0; i<10; ++i )
-         result.append( l.omega(i) );
-      return result;
-   }
-}
-
-void exportForceModels()
-{
-   static bool alreadyExported = false;
-   if( alreadyExported )
-      return;
-   alreadyExported = true;
-
-   using namespace internal;
-   using namespace force_model;
-
-   python_coupling::ModuleScope scope("forceModels");
-
-   typedef GhostLayerField<Vector3<real_t>,1 > VecField;
-
-   auto export1 = class_< None >("NoForce" ); // "None" is a Python keyword -> renamed to NoForce
-   addForceModelDefs( export1 );
-
-   auto export2 = class_< SimpleConstant >("SimpleConstant",
-                  init<Vector3<real_t> , uint_t> ( (arg("force"), arg("level")=uint_t(0) ) ) );
-   const Vector3<real_t>& (SimpleConstant::*p_SimpleConstantForce)() const = &SimpleConstant::force;
-   export2.def("force", p_SimpleConstantForce, return_value_policy<copy_const_reference>());
-   addForceModelDefs( export2 );
-
-   auto export3 = class_< EDMField<VecField> > ("EDMField",
-                  init< BlockDataID> ( (arg("forceFieldID") )) );
-   addForceModelDefs( export3 );
-
-   auto export4 = class_< LuoConstant > ("LuoConstant",
-                   init<Vector3<real_t> , uint_t> ( (arg("force"), arg("level")=uint_t(0) ) ) );
-   const Vector3<real_t>& (LuoConstant::*p_LuoConstantForce)() const = &LuoConstant::force;
-   export4.def("force", p_LuoConstantForce, return_value_policy<copy_const_reference>() );
-   addForceModelDefs( export4 );
-
-   auto export5 = class_< LuoField<VecField> > ("LuoField",
-                  init< BlockDataID> ( (arg("forceFieldID") )) );
-   addForceModelDefs( export5 );
-
-   auto export6 = class_< GuoConstant > ("GuoConstant",
-                   init<Vector3<real_t> , uint_t> ( (arg("force"), arg("level")=uint_t(0) ) ) );
-   const Vector3<real_t>& (GuoConstant::*p_GuoConstantForce)() const = &GuoConstant::force;
-   export6.def("force", p_GuoConstantForce, return_value_policy<copy_const_reference>() );
-   addForceModelDefs( export6 );
-
-   auto export7 = class_< GuoField<VecField> > ("GuoField",
-                  init< BlockDataID> ( (arg("forceFieldID") )) );
-   addForceModelDefs( export7 );
-
-   auto export8 = class_< Correction<VecField> > ("Correction",
-                  init< BlockDataID> ( (arg("previousMomentumDensityFieldID") )) );
-   addForceModelDefs( export8 );
-}
-
-
-
-shared_ptr< collision_model::SRTField<GhostLayerField<real_t,1> > > createSRTFieldLatticeModel( const shared_ptr<StructuredBlockStorage> & bs, const std::string & blockDataName, uint_t level )
-{
-   auto blockDataID = python_coupling::blockDataIDFromString( *bs, blockDataName );
-   return make_shared< collision_model::SRTField<GhostLayerField<real_t,1> > >( blockDataID, level );
-}
-
-void exportCollisionModels()
-{
-   static bool alreadyExported = false;
-   if( alreadyExported )
-      return;
-   alreadyExported = true;
-
-   using namespace internal;
-
-   python_coupling::ModuleScope scope("collisionModels");
-
-   using collision_model::SRT;
-   using collision_model::SRTField;
-   using collision_model::TRT;
-   using collision_model::D3Q19MRT;
-   using collision_model::D3Q27Cumulant;
-
-   def( "levelDependentRelaxationParameter", collision_model::levelDependentRelaxationParameter, (arg("targetLevel"), arg("parameterLevel"), arg("level")) );
-   def( "viscosityFromOmega", collision_model::viscosityFromOmega, (arg("omega") ) );
-   def( "omegaFromViscosity", collision_model::omegaFromViscosity, (arg("viscosity") ) );
-
-   // SRT
-   {
-      real_t ( SRT::*ptr_omega     )() const = &SRT::omega;
-      real_t ( SRT::*ptr_viscosity )() const = &SRT::viscosity;
-
-      class_< SRT > ("SRT", init<real_t, uint_t>( ( arg("omega"), arg("level")=uint_t(0) )  ) )
-          .add_property("omega", ptr_omega)
-          .add_property("viscosity", ptr_viscosity )
-          .add_property("level", &SRT::level )
-          .def("reset", &SRT::reset, (arg("omega"), arg("level")=uint_t(0) ) )
-       ;
-   }
-
-   // SRTField
-   {
-      typedef SRTField<GhostLayerField<real_t,1> > SRTField_T;
-      class_< SRTField_T >( "SRTField", no_init )
-          .def( "__init__", make_constructor( &createSRTFieldLatticeModel) )
-       ;
-   }
-
-   // TRT
-   {
-      real_t ( TRT::*ptr_lambda_e )() const = &TRT::lambda_e;
-      real_t ( TRT::*ptr_lambda_d )() const = &TRT::lambda_d;
-      real_t ( TRT::*ptr_viscosity )() const = &TRT::viscosity;
-
-      class_< TRT> ( "TRT", init<real_t, real_t, uint_t>( (arg("lambda_e"), arg("lambda_d"), arg("level")=uint_t(0) ) ) )
-         .add_property("lambda_e", ptr_lambda_e )
-         .add_property("lambda_d", ptr_lambda_d )
-         .add_property("lambda_o", ptr_lambda_d )
-         .add_property("viscosity", ptr_viscosity )
-         .add_property("level", &TRT::level )
-         .def( "reset", &TRT::reset, ( arg("lambda_e"), arg("lambda_d"), arg("level")=uint_t(0) ) )
-         .def( "resetWithMagicNumber",     &TRT::resetWithMagicNumber,     (arg("omega"), arg("magicNumber") = TRT::threeSixteenth,arg("level")=uint_t(0) ) )
-         .def( "constructWithMagicNumber", &TRT::constructWithMagicNumber, (arg("omega"), arg("magicNumber") = TRT::threeSixteenth, arg("level")=uint_t(0) ) )
-         .staticmethod("constructWithMagicNumber")
-      ;
-   }
-
-   // MRT
-   {
-      real_t ( D3Q19MRT::*ptr_viscosity )() const = &D3Q19MRT::viscosity;
-
-      class_< D3Q19MRT >( "D3Q19MRT", init<real_t, real_t, real_t, real_t, real_t, real_t, uint_t>(
-                                         ( arg("s1"), arg("s2"), arg("s4"), arg("s9"), arg("s10"), arg("s16"), arg("level")=uint_t(0) ) ))
-          .add_property( "relaxationRates", getMRTRelaxationRates )
-          .add_property( "viscosity", ptr_viscosity )
-          .def( "constructTRT", &D3Q19MRT::constructTRT, (arg("lambda_e"), arg("lambda_d"), arg("level")=uint_t(0) ) )
-          .staticmethod("constructTRT")
-          .def( "constructTRTWithMagicNumber", &D3Q19MRT::constructTRTWithMagicNumber, (arg("omega"), arg("magicNumber")=D3Q19MRT::threeSixteenth, arg("level")=uint_t(0) ) )
-          .staticmethod("constructTRTWithMagicNumber")
-          .def( "constructPan", &D3Q19MRT::constructPan, (arg("lambda_e"), arg("lambda_d"), arg("level")=uint_t(0) ) )
-          .staticmethod("constructPan")
-          .def( "constructPanWithMagicNumber", &D3Q19MRT::constructPanWithMagicNumber, (arg("omega"), arg("magicNumber")=D3Q19MRT::threeSixteenth, arg("level")=uint_t(0) ) )
-          .staticmethod("constructPanWithMagicNumber")
-       ;
-   }
-
-   // Cumulant
-   {
-      real_t ( D3Q27Cumulant::*ptr_viscosity )() const = &D3Q27Cumulant::viscosity;
-
-      class_< D3Q27Cumulant >( "D3Q27Cumulant", init<real_t, real_t, real_t, real_t, real_t, real_t, real_t, real_t, real_t,real_t, uint_t>(
-                                         ( arg("omega1"), arg("omega2")=real_t(1), arg("omega3")=real_t(1), arg("omega4")=real_t(1), arg("omega5")=real_t(1), arg("omega6")=real_t(1), arg("omega7")=real_t(1), arg("omega8")=real_t(1), arg("omega9")=real_t(1), arg("omega10")=real_t(1), arg("level")=uint_t(0) ) ))
-          .add_property( "relaxationRates", getCumulantRelaxationRates )
-          .add_property( "viscosity", ptr_viscosity )
-       ;
-
-   }
-
-}
-
-
-
-} // namespace lbm
-} // namespace walberla
-
-
-#endif //WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/lbm/python/ExportBasic.h b/src/lbm/python/ExportBasic.h
deleted file mode 100644
index ba55ed144512db4003376bfa094d1afc7b954f21..0000000000000000000000000000000000000000
--- a/src/lbm/python/ExportBasic.h
+++ /dev/null
@@ -1,47 +0,0 @@
-//======================================================================================================================
-//
-//  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 ExportBasic.h
-//! \ingroup lbm
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include "waLBerlaDefinitions.h"
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-namespace walberla {
-namespace lbm {
-
-    void exportCollisionModels();
-    void exportForceModels();
-
-    template< typename LatticeModels > struct VelocityAdaptorsFromLatticeModels;
-    template< typename LatticeModels > struct DensityAdaptorsFromLatticeModels;
-    template< typename LatticeModels > struct AdaptorsFromLatticeModels;
-    template< typename LatticeModels > struct ExtendedBoundaryHandlingsFromLatticeModels;
-
-    template<typename LatticeModels, typename FlagFields>
-    void exportBasic();
-
-} // namespace lbm
-} // namespace walberla
-
-
-#include "ExportBasic.impl.h"
-
-#endif // WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/lbm/python/ExportBasic.impl.h b/src/lbm/python/ExportBasic.impl.h
deleted file mode 100644
index c0097f54eccce9d73217935797c41a37e9d3763e..0000000000000000000000000000000000000000
--- a/src/lbm/python/ExportBasic.impl.h
+++ /dev/null
@@ -1,520 +0,0 @@
-//======================================================================================================================
-//
-//  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 ExportBasic.impl.h
-//! \ingroup lbm
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#include "python_coupling/helper/MplHelpers.h"
-#include "python_coupling/helper/BoostPythonHelpers.h"
-#include "python_coupling/helper/ModuleScope.h"
-#include "python_coupling/helper/BlockStorageExportHelpers.h"
-
-#include "core/logging/Logging.h"
-
-#include "boundary/python/Exports.h"
-#include "field/adaptors/AdaptorCreators.h"
-#include "lbm/lattice_model/CollisionModel.h"
-#include "lbm/lattice_model/ForceModel.h"
-#include "lbm/sweeps/CellwiseSweep.h"
-#include "lbm/field/AddToStorage.h"
-#include "lbm/field/Adaptors.h"
-#include "lbm/lattice_model/D3Q19.h"
-#include "lbm/sweeps/SplitPureSweep.h"
-
-#include "field/python/FieldExport.h"
-
-#include <boost/mpl/transform.hpp>
-#include <boost/mpl/copy.hpp>
-
-
-namespace walberla {
-namespace lbm {
-
-
-namespace internal
-{
-   //===================================================================================================================
-   //
-   //  LatticeModel
-   //
-   //===================================================================================================================
-
-   class LatticeModelCreator
-   {
-   public:
-      LatticeModelCreator( bool compressible, uint_t eqOrder, const std::string & stencil,
-                           boost::python::object collisionModel, boost::python::object forceModel )
-         : compressible_( compressible ), equilibriumAccuracyOrder_( eqOrder ), stencil_( stencil ),
-           collisionModel_( collisionModel ), forceModel_( forceModel )
-      {}
-
-      template<typename LatticeModel_T>
-      void operator() ( python_coupling::NonCopyableWrap<LatticeModel_T> )
-      {
-         using namespace boost::python;
-         using namespace collision_model;
-
-         typedef typename LatticeModel_T::Stencil Stencil_T;
-
-         if ( compressible_              != LatticeModel_T::compressible             ) return;
-         if ( equilibriumAccuracyOrder_  != LatticeModel_T::equilibriumAccuracyOrder ) return;
-         if ( stencil_                   != Stencil_T::NAME                          ) return;
-
-         if ( ! extract< typename LatticeModel_T::CollisionModel>(collisionModel_).check() ) return;
-         if ( ! extract< typename LatticeModel_T::ForceModel    >(forceModel_    ).check() ) return;
-
-         result_ = object( LatticeModel_T( extract< typename LatticeModel_T::CollisionModel>(collisionModel_),
-                                           extract< typename LatticeModel_T::ForceModel    >(forceModel_    ) ) );
-      }
-
-      boost::python::object getResult() const { return result_; }
-
-   private:
-      bool compressible_;
-      uint_t equilibriumAccuracyOrder_;
-      std::string stencil_;
-      boost::python::object collisionModel_;
-      boost::python::object forceModel_;
-
-
-      boost::python::object result_;
-   };
-
-   struct LatticeModelExporter
-   {
-      template<typename LatticeModel_T>
-      static boost::python::list getDirections( LatticeModel_T & )
-      {
-         using namespace boost::python;
-         list directionList;
-         const int dimension = LatticeModel_T::Stencil::D;
-         if( dimension < 1 || dimension > 3) {
-            PyErr_SetString( PyExc_ValueError, "Only stencils with dimensions 1,2,3 supported");
-            throw error_already_set();
-         }
-         for( auto it = LatticeModel_T::Stencil::begin(); it != LatticeModel_T::Stencil::end(); ++it )
-         {
-            if(dimension == 1) {
-               directionList.append( make_tuple(it.cx() ) );
-            }
-            else if( dimension == 2) {
-               directionList.append( make_tuple(it.cx(), it.cy()) );
-            } else if (dimension == 3) {
-               directionList.append( make_tuple(it.cx(), it.cy(), it.cz() ) );
-            }
-         }
-         return directionList;
-      }
-      template<typename LatticeModel_T>
-      static bool isCompressible(LatticeModel_T &) {
-         return LatticeModel_T::compressible;
-      }
-      template<typename LatticeModel_T>
-      static int equilibriumAccuracyOrder(LatticeModel_T &) {
-         return LatticeModel_T::equilibriumAccuracyOrder;
-      }
-      template<typename LatticeModel_T>
-      static boost::python::str stencilName(LatticeModel_T &) {
-        return boost::python::str(LatticeModel_T::Stencil::NAME);
-      }
-      template<typename LatticeModel_T>
-      static boost::python::str communicationStencilName(LatticeModel_T &) {
-        return boost::python::str(LatticeModel_T::CommunicationStencil::NAME);
-      }
-
-      template<typename LatticeModel_T>
-      void operator()( python_coupling::NonCopyableWrap<LatticeModel_T>  )
-      {
-         using namespace boost::python;
-
-         typename LatticeModel_T::CollisionModel& ( LatticeModel_T::*p_collisionModel) () = &LatticeModel_T::collisionModel;
-         typename LatticeModel_T::ForceModel    & ( LatticeModel_T::*p_forceModel)     () = &LatticeModel_T::forceModel;
-
-         class_< LatticeModel_T > ( LatticeModel_T::NAME, no_init )
-                  .add_property( "collisionModel", make_function( p_collisionModel, return_internal_reference<>() ) )
-                  .add_property( "forceModel",     make_function( p_forceModel    , return_internal_reference<>() ) )
-                  .add_property( "compressible",            &LatticeModelExporter::isCompressible<LatticeModel_T>)
-                  .add_property( "equilibriumAccuracyOrder",&LatticeModelExporter::equilibriumAccuracyOrder<LatticeModel_T>)
-                  .add_property( "stencilName",             &LatticeModelExporter::stencilName<LatticeModel_T>)
-                  .add_property( "communicationStencilName",&LatticeModelExporter::communicationStencilName<LatticeModel_T>)
-                  .add_property( "directions",              &LatticeModelExporter::getDirections<LatticeModel_T> )
-         ;
-      }
-   };
-
-
-   template<typename LatticeModels>
-   boost::python::object makeLatticeModel( const std::string & stencil, boost::python::object collisionModel, boost::python::object forceModel,
-                                           bool compressible, uint_t equilibriumAccuracyOrder )
-   {
-      using namespace boost::python;
-
-      LatticeModelCreator creator( compressible, equilibriumAccuracyOrder, stencil, collisionModel, forceModel );
-      python_coupling::for_each_noncopyable_type<LatticeModels>( std::ref(creator) );
-
-      if ( creator.getResult() == object() )
-      {
-         PyErr_SetString( PyExc_ValueError, "No such LatticeModel available.");
-         throw error_already_set();
-      }
-
-      return creator.getResult();
-   }
-
-   //===================================================================================================================
-   //
-   //  PdfField
-   //
-   //===================================================================================================================
-
-   template<typename LatticeModel_T>
-   typename PdfField<LatticeModel_T>::iterator pythonSliceToFieldIterator( PdfField<LatticeModel_T> & field,
-                                                                           boost::python::tuple pyIndex )
-   {
-      using python_coupling::localPythonSliceToCellInterval;
-      return field.beginSliceXYZ( localPythonSliceToCellInterval(field, pyIndex) );
-   }
-
-   template<typename LatticeModel_T>
-   void pdfField_setDensityAndVelocity( PdfField<LatticeModel_T> & field, boost::python::tuple pyIndex,
-                                        const Vector3<real_t> & velocity, real_t rho )
-   {
-      typename PdfField<LatticeModel_T>::iterator beginIterator = pythonSliceToFieldIterator<LatticeModel_T>( field, pyIndex );
-      DensityAndVelocityRange< LatticeModel_T, typename  PdfField<LatticeModel_T>::iterator >::set( beginIterator, field.end(), field.latticeModel(), velocity, rho );
-   }
-
-   template<typename LatticeModel_T>
-   void pdfField_setToEquilibrium( PdfField<LatticeModel_T> & field, boost::python::tuple pyIndex,
-                                   const Vector3<real_t> & velocity, real_t rho )
-   {
-      typename PdfField<LatticeModel_T>::iterator beginIterator = pythonSliceToFieldIterator<LatticeModel_T>( field, pyIndex );
-      EquilibriumRange< LatticeModel_T, typename  PdfField<LatticeModel_T>::iterator >::set( beginIterator, field.end(), velocity, rho );
-   }
-
-   template<typename LatticeModel_T>
-   boost::python::list pdfField_getPressureTensor( PdfField<LatticeModel_T> & field, cell_idx_t x, cell_idx_t y, cell_idx_t z )
-   {
-      using namespace boost::python;
-
-      Matrix3<real_t> m = field.getPressureTensor( x,y,z );
-      list result;
-      for(uint_t i=0; i<3; ++i )
-      {
-         list row;
-         for(uint_t j=0; j<3; ++j)
-            row.append( m(i,j) );
-         result.append(row);
-      }
-      return result;
-   }
-
-
-   struct PdfFieldExporter
-   {
-      template<typename LatticeModel_T>
-      void operator()( python_coupling::NonCopyableWrap<LatticeModel_T>  )
-      {
-         using namespace boost::python;
-
-         typedef PdfField<LatticeModel_T> PdfField_T;
-         typedef GhostLayerField<real_t, LatticeModel_T::Stencil::Size >  Base;
-
-
-         LatticeModel_T & ( PdfField_T::*ptr_latticeModel ) ()  = &PdfField_T::latticeModel;
-
-         //real_t ( PdfField_T::*ptr_getShearRate                  )( cell_idx_t, cell_idx_t, cell_idx_t         ) const = &PdfField_T::getShearRate;
-
-         real_t ( PdfField_T::*ptr_getDensity                    )( cell_idx_t, cell_idx_t, cell_idx_t         ) const = &PdfField_T::getDensity;
-         real_t ( PdfField_T::*ptr_getDensitySI                  )( cell_idx_t, cell_idx_t, cell_idx_t, real_t ) const = &PdfField_T::getDensitySI;
-
-         Vector3<real_t> ( PdfField_T::*ptr_getMomentumDensity            )( cell_idx_t, cell_idx_t, cell_idx_t         ) const = &PdfField_T::getMomentumDensity;
-         Vector3<real_t> ( PdfField_T::*ptr_getEquilibriumMomentumDensity )( cell_idx_t, cell_idx_t, cell_idx_t         ) const = &PdfField_T::getEquilibriumMomentumDensity;
-         real_t          ( PdfField_T::*ptr_getShearRate                  )( cell_idx_t, cell_idx_t, cell_idx_t                 ) const  = &PdfField_T::getShearRate;
-         Vector3<real_t> ( PdfField_T::*ptr_getVelocity                   )( cell_idx_t, cell_idx_t, cell_idx_t                 ) const  = &PdfField_T::getVelocity;
-         Vector3<real_t> ( PdfField_T::*ptr_getVelocitySI                 )( cell_idx_t, cell_idx_t, cell_idx_t, real_t, real_t ) const  = &PdfField_T::getVelocitySI;
-         Vector3<real_t> ( PdfField_T::*ptr_getEquilibriumVelocity        )( cell_idx_t, cell_idx_t, cell_idx_t                 ) const  = &PdfField_T::getEquilibriumVelocity;
-
-         try
-         {
-            class_< PdfField_T, shared_ptr<PdfField_T>, bases< Base >, boost::noncopyable >("PdfField", no_init)
-               .add_property("latticeModel", make_function( ptr_latticeModel, return_internal_reference<>() ) )
-               .def( "setDensityAndVelocity", pdfField_setDensityAndVelocity<LatticeModel_T>, ( args("slice"), args("velocity"), args("density") ) )
-               .def( "setToEquilibrium",      pdfField_setToEquilibrium<LatticeModel_T>,      ( args("slice"), args("velocity"), args("density") ) )
-               .def( "getShearRate",                   ptr_getShearRate                           , ( arg("x"), arg("y"), arg("z")  ) )
-               .def( "getDensity",                     ptr_getDensity                             , ( arg("x"), arg("y"), arg("z") ) )
-               .def( "getDensitySI",                   ptr_getDensitySI                           , ( arg("x"), arg("y"), arg("z"), arg("rho_SI") ) )
-               .def( "getMomentumDensity"            , ptr_getMomentumDensity                     , ( arg("x"), arg("y"), arg("z") ) )
-               .def( "getEquilibriumMomentumDensity" , ptr_getEquilibriumMomentumDensity          , ( arg("x"), arg("y"), arg("z") ) )
-               .def( "getVelocity"                   , ptr_getVelocity                            , ( arg("x"), arg("y"), arg("z") ) )
-               .def( "getVelocitySI"                 , ptr_getVelocitySI                          , ( arg("x"), arg("y"), arg("z"), arg("dx_SI"), arg("dy_SI") ) )
-               .def( "getEquilibriumVelocity"        , ptr_getEquilibriumVelocity                 , ( arg("x"), arg("y"), arg("z") ) )
-               .def( "getPressureTensor",              pdfField_getPressureTensor<LatticeModel_T> , ( arg("x"), arg("y"), arg("z") ) )
-            ;
-         }
-         catch (...) {
-            WALBERLA_LOG_WARNING( "Exporting PDFField failed. Did you forget to export the corresponding GhostLayerField?" );
-         }
-      }
-   };
-
-   class AddPdfFieldToStorageExporter
-   {
-   public:
-      AddPdfFieldToStorageExporter( const shared_ptr<StructuredBlockStorage> & blocks,
-                           const std::string & name,
-                           boost::python::object latticeModel,
-                           const Vector3<real_t> & initialVelocity, real_t initialDensity,
-                           uint_t gl, field::Layout layout,
-                           const std::string & densityAdaptorName, const std::string & velocityAdaptorName,
-                           const std::string & shearRateAdaptorName )
-         : blocks_( blocks ), name_( name ), latticeModel_(latticeModel),
-           initialVelocity_( initialVelocity ), initialDensity_( initialDensity ),
-           gl_(gl),layout_( layout),
-           densityAdaptorName_( densityAdaptorName ), velocityAdaptorName_( velocityAdaptorName ),
-           shearRateAdaptorName_( shearRateAdaptorName),
-           found_(false)
-      {}
-
-      template< typename LatticeModel_T>
-      void operator() ( python_coupling::NonCopyableWrap<LatticeModel_T> )
-      {
-         using namespace boost::python;
-
-         if( ! extract<LatticeModel_T>( latticeModel_ ).check() )
-            return;
-
-         WALBERLA_ASSERT( !found_ );
-         BlockDataID pdfFieldID = addPdfFieldToStorage<LatticeModel_T>( blocks_, name_, extract<LatticeModel_T>( latticeModel_ ),
-                                                                        initialVelocity_, initialDensity_, gl_, layout_ );
-
-         if ( densityAdaptorName_.size() > 0 ) {
-            field::addFieldAdaptor< typename lbm::Adaptor<LatticeModel_T>::Density > ( blocks_, pdfFieldID, densityAdaptorName_ );
-         }
-         if ( velocityAdaptorName_.size() > 0 ) {
-            field::addFieldAdaptor< typename lbm::Adaptor<LatticeModel_T>::Velocity >( blocks_, pdfFieldID, velocityAdaptorName_ );
-         }
-         if ( shearRateAdaptorName_.size() > 0 ) {
-            field::addFieldAdaptor< typename lbm::Adaptor<LatticeModel_T>::ShearRate >( blocks_, pdfFieldID, shearRateAdaptorName_ );
-         }
-         found_ = true;
-      }
-
-      bool successful() { return found_; }
-
-   private:
-      shared_ptr< StructuredBlockStorage > blocks_;
-      std::string name_;
-      boost::python::object latticeModel_;
-      Vector3<real_t> initialVelocity_;
-
-      real_t initialDensity_;
-      uint_t gl_;
-      field::Layout layout_;
-
-      std::string densityAdaptorName_;
-      std::string velocityAdaptorName_;
-      std::string shearRateAdaptorName_;
-
-      bool found_;
-   };
-
-   template<typename LatticeModels>
-   void addPdfFieldToStorage( const shared_ptr<StructuredBlockStorage> & blocks,
-                              const std::string & identifier,
-                              boost::python::object latticeModel,
-                              const Vector3<real_t> & initialVelocity, real_t initialDensity,
-                              uint_t gl, field::Layout layout,
-                              const std::string & densityAdaptor, const std::string & velocityAdaptor,
-                              const std::string & shearRateAdaptor )
-   {
-      using namespace boost::python;
-
-      internal::AddPdfFieldToStorageExporter exporter( blocks, identifier, latticeModel, initialVelocity,
-                                                       initialDensity, gl, layout, densityAdaptor, velocityAdaptor, shearRateAdaptor );
-      python_coupling::for_each_noncopyable_type< LatticeModels >  ( std::ref(exporter) );
-      if ( ! exporter.successful() ) {
-         PyErr_SetString( PyExc_ValueError, "Adding Pdf Field failed.");
-         throw error_already_set();
-      }
-   }
-
-   struct AdaptorExporter
-   {
-      template< typename LatticeModel_T>
-      void operator() ( python_coupling::NonCopyableWrap<LatticeModel_T> )
-      {
-         field::exportGhostLayerFieldAdaptor< typename Adaptor< LatticeModel_T >::Density   > ( );
-         field::exportGhostLayerFieldAdaptor< typename Adaptor< LatticeModel_T >::Velocity  > ( );
-         field::exportGhostLayerFieldAdaptor< typename Adaptor< LatticeModel_T >::ShearRate > ( );
-      }
-   };
-
-    template< typename LatticeModel >
-    struct VelocityAdaptorFromLatticeModel
-    {
-        typedef typename lbm::Adaptor< LatticeModel>::Velocity type;
-    };
-
-    template< typename LatticeModel >
-    struct DensityAdaptorFromLatticeModel
-    {
-        typedef typename lbm::Adaptor< LatticeModel>::Density type;
-    };
-
-    template< typename LatticeModel >
-    struct ShearRateAdaptorFromLatticeModel
-    {
-        typedef typename lbm::Adaptor< LatticeModel>::ShearRate type;
-    };
-
-    class SweepWrapper
-    {
-    public:
-        SweepWrapper()
-        {}
-
-        SweepWrapper( const std::function<void(IBlock*) > & sweepToWrap )
-                : sweepToWrap_( sweepToWrap ) {}
-
-        void operator() ( IBlock * block )
-        {
-           if ( sweepToWrap_ )
-              sweepToWrap_( block );
-        }
-    protected:
-        std::function<void(IBlock*) > sweepToWrap_;
-    };
-
-
-    inline shared_ptr<SweepWrapper> makeSplitPureSweep(const shared_ptr<StructuredBlockStorage> & blocks,
-                                                       const std::string & pdfFieldIDStr)
-    {
-       if ( blocks->begin() == blocks->end() ) {
-          PyErr_SetString( PyExc_RuntimeError, "No blocks on this process" );
-          throw boost::python::error_already_set();
-       }
-
-       IBlock & firstBlock = *(  blocks->begin() );
-       BlockDataID pdfFieldID = python_coupling::blockDataIDFromString( firstBlock, pdfFieldIDStr );
-
-       typedef lbm::D3Q19< lbm::collision_model::SRT, true >  LM_SRT_Compressible;
-       typedef lbm::D3Q19< lbm::collision_model::SRT, false > LM_SRT_Incompressible;
-       typedef lbm::D3Q19< lbm::collision_model::TRT, true >  LM_TRT_Compressible;
-       typedef lbm::D3Q19< lbm::collision_model::TRT, false > LM_TRT_Incompressible;
-
-
-       if      (firstBlock.isDataOfType<PdfField<LM_SRT_Compressible>>(pdfFieldID)) {
-          return make_shared<SweepWrapper>(SplitPureSweep<LM_SRT_Compressible>(pdfFieldID));
-       }
-       else if (firstBlock.isDataOfType<PdfField<LM_SRT_Incompressible>>(pdfFieldID)) {
-          return make_shared<SweepWrapper>(SplitPureSweep<LM_SRT_Incompressible>(pdfFieldID));
-       }
-       else if (firstBlock.isDataOfType<PdfField<LM_TRT_Compressible>>(pdfFieldID)) {
-          return make_shared<SweepWrapper>(SplitPureSweep<LM_TRT_Compressible>(pdfFieldID));
-       }
-       else if (firstBlock.isDataOfType<PdfField<LM_TRT_Incompressible>>(pdfFieldID)) {
-          return make_shared<SweepWrapper>(SplitPureSweep<LM_TRT_Compressible>(pdfFieldID));
-       }
-       else {
-          PyErr_SetString( PyExc_RuntimeError, "No split-pure sweep available for this lattice model" );
-          throw boost::python::error_already_set();
-       }
-    }
-}
-
-template< typename LatticeModels >
-struct VelocityAdaptorsFromLatticeModels
-{
-   typedef typename boost::mpl::transform< LatticeModels, internal::VelocityAdaptorFromLatticeModel<boost::mpl::_1 > >::type type;
-};
-
-template< typename LatticeModels >
-struct DensityAdaptorsFromLatticeModels
-{
-   typedef typename boost::mpl::transform< LatticeModels, internal::DensityAdaptorFromLatticeModel<boost::mpl::_1> >::type type;
-};
-
-template< typename LatticeModels >
-struct ShearRateAdaptorsFromLatticeModels
-{
-   typedef typename boost::mpl::transform< LatticeModels, internal::ShearRateAdaptorFromLatticeModel<boost::mpl::_1> >::type type;
-};
-
-template< typename LatticeModels >
-struct AdaptorsFromLatticeModels
-{
-   typedef typename boost::mpl::copy< typename DensityAdaptorsFromLatticeModels<LatticeModels>::type,
-                                      boost::mpl::front_inserter<  typename VelocityAdaptorsFromLatticeModels<LatticeModels>::type  > >
-                                     ::type  tmp1;
-
-   typedef typename boost::mpl::copy< typename ShearRateAdaptorsFromLatticeModels<LatticeModels>::type,
-                                      boost::mpl::front_inserter<  tmp1  > >
-                                     ::type  type;
-};
-
-
-
-template<typename LatticeModels, typename FlagFields>
-void exportBasic()
-{
-   using namespace boost::python;
-
-   python_coupling::ModuleScope scope("lbm");
-
-   // Lattice Models
-   exportForceModels();
-   exportCollisionModels();
-   python_coupling::for_each_noncopyable_type<LatticeModels>( internal::LatticeModelExporter() );
-   python_coupling::for_each_noncopyable_type<LatticeModels>( internal::AdaptorExporter() );
-
-   def( "makeLatticeModel", internal::makeLatticeModel<LatticeModels>,
-        ( arg("stencil"), arg("collisionModel"),
-          arg("forceModel")= force_model::None(),
-          arg("compressible")=false, arg("equilibriumAccuracyOrder")=2)  );
-
-   // PdfField
-   python_coupling::for_each_noncopyable_type< LatticeModels > ( internal::PdfFieldExporter() );
-   def( "addPdfFieldToStorage", &internal::addPdfFieldToStorage<LatticeModels>,
-            (( arg("blocks")                            ),
-             ( arg("name")                              ),
-             ( arg("latticeModel")                      ),
-             ( arg("initialVelocity") = Vector3<real_t>() ),
-             ( arg("initialDensity")  = real_t(1)        ),
-             ( arg("ghostlayers")     = uint_t(1)        ),
-             ( arg("layout")          = field::zyxf      ),
-             ( arg("densityAdaptor")  = std::string()    ),
-             ( arg("velocityAdaptor") = std::string()    ),
-             ( arg("shearRateAdaptor")= std::string()    )
-             ) );
-
-   // Split Sweeps
-   def("makeSplitPureSweep", &internal::makeSplitPureSweep, (arg("blocks"), arg("pdfFieldId")));
-   class_<internal::SweepWrapper, shared_ptr<internal::SweepWrapper>, boost::noncopyable>("SplitPureSweep", no_init)
-           .def("__call__", &internal::SweepWrapper::operator());
-
-
-   auto pythonManager = python_coupling::Manager::instance();
-   pythonManager->addBlockDataConversion< typename AdaptorsFromLatticeModels                 < LatticeModels >::type > ();
-}
-
-
-
-} // namespace lbm
-} // namespace walberla
-
-
diff --git a/src/lbm/python/ExportBoundary.h b/src/lbm/python/ExportBoundary.h
deleted file mode 100644
index a6803a37472e24362be7927aeb251f8c27767193..0000000000000000000000000000000000000000
--- a/src/lbm/python/ExportBoundary.h
+++ /dev/null
@@ -1,39 +0,0 @@
-//======================================================================================================================
-//
-//  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 ExportBoundary.h
-//! \ingroup lbm
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include "waLBerlaDefinitions.h"
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-namespace walberla {
-namespace lbm {
-
-   template<typename LatticeModels, typename FlagFields>
-   void exportBoundary();
-
-} // namespace lbm
-} // namespace walberla
-
-
-#include "ExportBoundary.impl.h"
-
-#endif // WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/lbm/python/ExportBoundary.impl.h b/src/lbm/python/ExportBoundary.impl.h
deleted file mode 100644
index 06845e35a29f9b4d9b5635944145766ea6e5e9f2..0000000000000000000000000000000000000000
--- a/src/lbm/python/ExportBoundary.impl.h
+++ /dev/null
@@ -1,279 +0,0 @@
-//======================================================================================================================
-//
-//  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 ExportBoundary.impl.h
-//! \ingroup lbm
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#include "python_coupling/helper/MplHelpers.h"
-#include "python_coupling/helper/BoostPythonHelpers.h"
-#include "python_coupling/helper/ModuleScope.h"
-#include "python_coupling/helper/BlockStorageExportHelpers.h"
-#include "python_coupling/helper/ConfigFromDict.h"
-#include "python_coupling/helper/SliceToCellInterval.h"
-#include "python_coupling/Manager.h"
-
-#include "core/logging/Logging.h"
-
-#include "boundary/python/Exports.h"
-
-#include "lbm/boundary/factories/ExtendedBoundaryHandlingFactory.h"
-
-#include "field/python/FieldExport.h"
-#include "field/adaptors/AdaptorCreators.h"
-
-#include <boost/mpl/transform.hpp>
-#include <boost/mpl/copy.hpp>
-
-namespace walberla {
-namespace lbm {
-
-
-namespace internal
-{
-    using python_coupling::localPythonSliceToCellInterval;
-
-
-   template<typename BH>
-   shared_ptr<BoundaryConfiguration> boundaryConfFromDict( BH & h, const BoundaryUID & uid, boost::python::dict d ) {
-      shared_ptr<Config> cfg = python_coupling::configFromPythonDict( d );
-      return h.createBoundaryConfiguration( uid, cfg->getGlobalBlock() );
-   }
-   template<typename BH>
-   void BH_setBoundary1( BH & h, const std::string & name, cell_idx_t x, cell_idx_t y , cell_idx_t z, boost::python::dict conf ) {
-      h.setBoundary( name,x,y,z,  *boundaryConfFromDict( h, name, conf) );
-   }
-   template<typename BH>
-   void BH_setBoundary2( BH & h, const std::string & name, const boost::python::tuple & index, boost::python::dict conf ) {
-      h.setBoundary( name,
-                     localPythonSliceToCellInterval( *h.getFlagField() , index ),
-                     *boundaryConfFromDict( h, name, conf) );
-   }
-   template<typename BH>
-   void BH_forceBoundary1( BH & h, const std::string & name, cell_idx_t x, cell_idx_t y , cell_idx_t z, boost::python::dict conf ) {
-      h.forceBoundary( name,x,y,z,  *boundaryConfFromDict( h, name, conf) );
-   }
-   template<typename BH>
-   void BH_forceBoundary2( BH & h, const std::string & name, const boost::python::tuple & index, boost::python::dict conf )  {
-      h.forceBoundary( name,
-                     localPythonSliceToCellInterval( *h.getFlagField() , index ),
-                     *boundaryConfFromDict( h, name, conf) );
-   }
-
-   template<typename BH>
-   void BH_forceBoundary3( BH & h, const GhostLayerField<int,1> & indexField , boost::python::dict boundaryInfo )
-   {
-      using namespace boost::python;
-      list keys = boundaryInfo.keys();
-
-      std::map<int, FlagUID > flagUIDs;
-      std::map<int, shared_ptr<BoundaryConfiguration> > boundaryConfigs;
-
-      for (int i = 0; i < len( keys ); ++i)
-      {
-         int key =  extract<int>( keys[i] );
-         extract<std::string> extracted_str_val  ( boundaryInfo[key] );
-         extract<dict       > extracted_dict_val ( boundaryInfo[key] );
-
-         if ( extracted_str_val.check() )
-         {
-            std::string boundaryName = extracted_str_val;
-            flagUIDs[key] = FlagUID ( boundaryName );
-         }
-         else if ( extracted_dict_val.check() )
-         {
-            dict info = extracted_dict_val;
-            std::string boundaryName = extract<std::string>( info["name"] );
-
-            dict configDict = extract<dict>( info["config"] );
-
-            flagUIDs[key] = FlagUID ( boundaryName );
-            boundaryConfigs[key] = boundaryConfFromDict( h, boundaryName, configDict);
-         }
-         else {
-            PyErr_SetString( PyExc_ValueError, "Invalid parameter");
-            throw error_already_set();
-         }
-      }
-
-      // iterate over flag field
-      if ( indexField.xyzSize() != h.getFlagField()->xyzSize() || indexField.nrOfGhostLayers() > h.getFlagField()->nrOfGhostLayers() ) {
-         WALBERLA_LOG_DEVEL("indexField " << indexField.xyzSizeWithGhostLayer() );
-         WALBERLA_LOG_DEVEL("flagField  " << h.getFlagField()->xyzSizeWithGhostLayer() );
-         PyErr_SetString( PyExc_ValueError, "Index field has to have same size as flag field");
-         throw error_already_set();
-      }
-
-
-      cell_idx_t gl = cell_idx_c( indexField.nrOfGhostLayers() );
-      for( cell_idx_t z = -gl; z < cell_idx_c( indexField.zSizeWithGhostLayer() ); ++z )
-         for( cell_idx_t y = -gl; y < cell_idx_c( indexField.ySizeWithGhostLayer() ); ++y )
-            for( cell_idx_t x = -gl; x < cell_idx_c( indexField.xSizeWithGhostLayer() ); ++x )
-            {
-               int index = indexField(x,y,z);
-               if ( flagUIDs.find( index ) != flagUIDs.end() )
-               {
-                  if ( boundaryConfigs.find( index ) != boundaryConfigs.end()  )
-                     h.forceBoundary( flagUIDs[index],x,y,z, * boundaryConfigs[index] );
-                  else
-                     h.forceBoundary( flagUIDs[index],x,y,z );
-               }
-            }
-   }
-
-   template<typename BH>
-   void BH_setDomainSlice( BH & h, const boost::python::tuple & index ) {
-      h.setDomain( localPythonSliceToCellInterval( *h.getFlagField() , index ) );
-   }
-
-   template<typename BH>
-   void BH_forceDomainSlice( BH & h, const boost::python::tuple & index ) {
-      h.forceDomain( localPythonSliceToCellInterval( *h.getFlagField() , index ) );
-   }
-
-   template<typename BH>
-   void BH_fillDomainSlice( BH & h, const boost::python::tuple & index ) {
-      h.fillWithDomain( localPythonSliceToCellInterval( *h.getFlagField() , index ) );
-   }
-   template<typename BH>
-   void BH_removeDomainSlice( BH & h, const boost::python::tuple & index ) {
-      h.removeDomain( localPythonSliceToCellInterval( *h.getFlagField() , index ) );
-   }
-   template<typename BH>
-   void BH_removeBoundarySlice( BH & h, const boost::python::tuple & index ) {
-      h.removeBoundary( localPythonSliceToCellInterval( *h.getFlagField() , index ) );
-   }
-   template<typename BH>
-   void BH_clearSlice( BH & h, const boost::python::tuple & index ) {
-      h.clear( localPythonSliceToCellInterval( *h.getFlagField() , index ) );
-   }
-
-
-
-
-   class ExtendedBoundaryHandlingCreator
-   {
-   public:
-
-      ExtendedBoundaryHandlingCreator( const shared_ptr<StructuredBlockStorage> & blocks,
-                                       const std::string & name, BlockDataID pdfFieldID, BlockDataID flagFieldID )
-         : blocks_( blocks ), name_( name ),
-           pdfFieldID_( pdfFieldID ), flagFieldID_( flagFieldID ), found_ ( false )
-      {}
-
-
-      template<typename LatticeModel>
-      void operator()( python_coupling::NonCopyableWrap<LatticeModel>  )
-      {
-         using namespace boost::python;
-
-         IBlock & firstBlock = *(  blocks_->begin() );
-         if ( firstBlock.isDataClassOrSubclassOf< PdfField<LatticeModel> >( pdfFieldID_ ) )
-         {
-            WALBERLA_ASSERT( !found_ );
-            ExtendedBoundaryHandlingFactory<LatticeModel, FlagField<uint8_t> >::addBoundaryHandlingToStorage(
-                     blocks_, name_, flagFieldID_, pdfFieldID_, FlagUID("fluid") );
-            found_ = true;
-         }
-      }
-
-      bool successful() const { return found_; }
-
-   private:
-      shared_ptr<StructuredBlockStorage> blocks_;
-      std::string name_;
-      BlockDataID pdfFieldID_;
-      BlockDataID flagFieldID_;
-
-      bool found_;
-   };
-
-
-   template<typename LatticeModels>
-   void addBoundaryHandlingToStorage( const shared_ptr<StructuredBlockStorage> & bs,
-                                      const std::string & name,
-                                      const std::string & pdfFieldStringID,
-                                      const std::string & flagFieldStringID  )
-   {
-      using namespace boost::python;
-
-      if ( bs->begin() == bs->end() )
-         return;
-
-
-      IBlock & firstBlock = *(  bs->begin() );
-
-      BlockDataID pdfFieldID  = python_coupling::blockDataIDFromString( firstBlock, pdfFieldStringID  );
-      BlockDataID flagFieldID = python_coupling::blockDataIDFromString( firstBlock, flagFieldStringID );
-
-      if ( ! firstBlock.isDataClassOrSubclassOf< FlagField<uint8_t> >( flagFieldID ) )
-      {
-         PyErr_SetString( PyExc_ValueError, "Unknown FlagField type. Please provide a FlagField with 8 bit per cell");
-         throw error_already_set();
-      }
-
-      ExtendedBoundaryHandlingCreator creator( bs, name, pdfFieldID, flagFieldID );
-      python_coupling::for_each_noncopyable_type<LatticeModels>( std::ref( creator ) );
-
-      if ( ! creator.successful() )
-      {
-         PyErr_SetString( PyExc_ValueError, "No Boundary Handling found for this lattice model");
-         throw error_already_set();
-      }
-   }
-
-   template< typename LatticeModel >
-   struct ExtendedBoundaryHandlingFromLatticeModel
-   {
-      typedef typename ExtendedBoundaryHandlingFactory< LatticeModel, FlagField<uint8_t> >::BoundaryHandling type;
-   };
- }
-
-
-template< typename LatticeModels >
-struct ExtendedBoundaryHandlingsFromLatticeModels
-{
-   typedef typename boost::mpl::transform< LatticeModels, internal::ExtendedBoundaryHandlingFromLatticeModel<boost::mpl::_1> >::type type;
-};
-
-
-template<typename LatticeModels, typename FlagFields>
-void exportBoundary()
-{
-   using namespace boost::python;
-
-   python_coupling::ModuleScope scope("lbm");
-
-   // Extended Boundary Handling
-   boundary::exportModuleToPython< typename ExtendedBoundaryHandlingsFromLatticeModels< LatticeModels >::type >();
-
-   def ( "addBoundaryHandlingToStorage", &internal::addBoundaryHandlingToStorage<LatticeModels>,
-            ( arg("blocks"),
-              arg("name"),
-              arg("pdfField"),
-              arg("flagField") ) );
-
-   auto pythonManager = python_coupling::Manager::instance();
-   pythonManager->addBlockDataConversion< typename ExtendedBoundaryHandlingsFromLatticeModels< LatticeModels >::type > ();
-}
-
-
-
-} // namespace lbm
-} // namespace walberla
-
-
diff --git a/src/lbm/python/ExportSweeps.h b/src/lbm/python/ExportSweeps.h
deleted file mode 100644
index ba33e480306cc8e2134fcf775d315e8c1f5396cb..0000000000000000000000000000000000000000
--- a/src/lbm/python/ExportSweeps.h
+++ /dev/null
@@ -1,39 +0,0 @@
-//======================================================================================================================
-//
-//  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 ExportSweeps.h
-//! \ingroup lbm
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include "waLBerlaDefinitions.h"
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-namespace walberla {
-namespace lbm {
-
-   template<typename LatticeModels, typename FlagFields>
-   void exportSweeps();
-
-} // namespace lbm
-} // namespace walberla
-
-
-#include "ExportSweeps.impl.h"
-
-#endif // WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/lbm/python/ExportSweeps.impl.h b/src/lbm/python/ExportSweeps.impl.h
deleted file mode 100644
index 5f9440114375038cd2c75a17e82ef13dcb45ae83..0000000000000000000000000000000000000000
--- a/src/lbm/python/ExportSweeps.impl.h
+++ /dev/null
@@ -1,260 +0,0 @@
-//======================================================================================================================
-//
-//  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 ExportSweeps.impl.h
-//! \ingroup lbm
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#include "python_coupling/helper/MplHelpers.h"
-#include "python_coupling/helper/BoostPythonHelpers.h"
-#include "python_coupling/helper/ModuleScope.h"
-#include "python_coupling/helper/BlockStorageExportHelpers.h"
-
-#include "python_coupling/Manager.h"
-
-#include "core/logging/Logging.h"
-
-#include "lbm/lattice_model/CollisionModel.h"
-#include "lbm/sweeps/CellwiseSweep.h"
-
-#include "field/python/FieldExport.h"
-#include "field/adaptors/AdaptorCreators.h"
-
-namespace walberla {
-namespace lbm {
-
-
-namespace internal
-{
-   template< typename LM, typename Filter, typename In, typename Out >
-   void addSweepMemberFunctions( boost::python::class_< CellwiseSweep<LM,Filter,In,Out>, shared_ptr<CellwiseSweep<LM,Filter,In,Out> > > & c )
-   {
-      using namespace boost::python;
-
-      typedef CellwiseSweep<LM,Filter,In,Out> Sweep;
-      c.def( "stream",       &Sweep::stream,        (arg("block"),arg("numberOfGhostLayersToInclude")=uint_t(0) ) )
-       .def( "collide",      &Sweep::collide,       (arg("block"),arg("numberOfGhostLayersToInclude")=uint_t(0) ) )
-       .def("streamCollide", &Sweep::streamCollide, (arg("block"),arg("numberOfGhostLayersToInclude")=uint_t(0) ) )
-       .def("__call__",      &Sweep::operator(),    (arg("block"),arg("numberOfGhostLayersToInclude")=uint_t(0) ) )
-      ;
-   }
-
-   struct CellwiseSweepExporterWithoutFlagField
-   {
-      template<typename LatticeModel_T>
-      void operator()( python_coupling::NonCopyableWrap<LatticeModel_T>  )
-      {
-         using namespace boost::python;
-
-         typedef GhostLayerField< real_t, 3 > VelocityField_T;
-
-         typedef CellwiseSweep<LatticeModel_T,
-                  field::DefaultEvaluationFilter,
-                  DefaultDensityEquilibriumVelocityCalculation,
-                  DefaultDensityVelocityCallback
-                  > Sweep1;
-         typedef CellwiseSweep<LatticeModel_T,
-                  field::DefaultEvaluationFilter,
-                  DefaultDensityEquilibriumVelocityCalculation,
-                  VelocityCallback<VelocityField_T>
-                  > Sweep2;
-
-         auto sweep1 = class_< Sweep1, shared_ptr<Sweep1> > ( "CellwiseSweep", no_init );
-         auto sweep2 = class_< Sweep2, shared_ptr<Sweep2> > ( "CellwiseSweep", no_init );
-         addSweepMemberFunctions( sweep1 );
-         addSweepMemberFunctions( sweep2 );
-      }
-   };
-
-   struct CellwiseSweepExporterWithFlagField
-   {
-      template<typename LatticeModel_FlagField_Pair>
-      void operator()( python_coupling::NonCopyableWrap<LatticeModel_FlagField_Pair>  )
-      {
-         using namespace boost::python;
-
-         typedef typename LatticeModel_FlagField_Pair::first  LatticeModel_T;
-         typedef typename LatticeModel_FlagField_Pair::second FlagField_T;
-
-         typedef GhostLayerField< real_t,3> VelocityField_T;
-
-         typedef CellwiseSweep<LatticeModel_T,
-                  field::FlagFieldEvaluationFilter<FlagField_T>,
-                  DefaultDensityEquilibriumVelocityCalculation,
-                  DefaultDensityVelocityCallback
-                  > Sweep1;
-         typedef  CellwiseSweep<LatticeModel_T,
-                  field::FlagFieldEvaluationFilter<FlagField_T>,
-                  DefaultDensityEquilibriumVelocityCalculation,
-                  VelocityCallback<VelocityField_T>
-                  > Sweep2;
-
-         auto sweep1 = class_< Sweep1, shared_ptr<Sweep1> > ( "CellwiseSweep", no_init );
-         auto sweep2 = class_< Sweep2, shared_ptr<Sweep2> > ( "CellwiseSweep", no_init );
-         addSweepMemberFunctions( sweep1 );
-         addSweepMemberFunctions( sweep2 );
-      }
-   };
-
-
-   class CellwiseSweepCreator
-   {
-   public:
-
-      CellwiseSweepCreator( const shared_ptr<StructuredBlockStorage> & blocks, BlockDataID pdfFieldID,
-                            const std::string & flagFieldStringID, const std::string & velocityFieldStringID,
-                            const Set< FlagUID > & cellsToEvaluate )
-         : blocks_( blocks ), pdfFieldID_( pdfFieldID ), flagFieldStringID_( flagFieldStringID ),
-           velocityFieldStringID_( velocityFieldStringID ), cellsToEvaluate_( cellsToEvaluate )
-      {}
-
-
-      template<typename LatticeModel_FlagField_Pair>
-      void operator()( python_coupling::NonCopyableWrap<LatticeModel_FlagField_Pair>  )
-      {
-         typedef GhostLayerField<real_t,3> VelocityField_T;
-
-         using namespace boost::python;
-         using python_coupling::blockDataIDFromString;
-
-
-         typedef typename LatticeModel_FlagField_Pair::first  LatticeModel_T;
-         typedef typename LatticeModel_FlagField_Pair::second FlagField_T;
-
-         if ( blocks_->begin() == blocks_->end() )
-            return;
-
-         IBlock & firstBlock = *(  blocks_->begin() );
-
-         if( ! firstBlock.isDataClassOrSubclassOf<PdfField<LatticeModel_T> >(pdfFieldID_) )
-            return;
-
-         if( flagFieldStringID_.size() > 0 )
-         {
-            // Use FlagFilter
-            BlockDataID flagFieldID = blockDataIDFromString( firstBlock, flagFieldStringID_ );
-            if ( ! firstBlock.isDataClassOrSubclassOf< FlagField_T>( flagFieldID ) )
-               return;
-
-            if ( velocityFieldStringID_.size() > 0 )
-            {
-               BlockDataID velocityFieldID = blockDataIDFromString( firstBlock, velocityFieldStringID_ );
-               result_ = object( makeCellwiseSweep<LatticeModel_T, FlagField_T, VelocityField_T >( pdfFieldID_, flagFieldID,
-                                                                                                   cellsToEvaluate_, velocityFieldID ) );
-            }
-            else
-            {
-               result_ = object( makeCellwiseSweep<LatticeModel_T, FlagField_T>( pdfFieldID_, flagFieldID, cellsToEvaluate_ ) );
-            }
-         }
-         else
-         {
-            if ( velocityFieldStringID_.size() > 0 )
-            {
-               BlockDataID velocityFieldID = blockDataIDFromString( firstBlock, velocityFieldStringID_ );
-               result_ = object ( makeCellwiseSweep<LatticeModel_T,
-                                           field::DefaultEvaluationFilter,
-                                           DefaultDensityEquilibriumVelocityCalculation,
-                                           VelocityCallback<VelocityField_T>
-                                           >
-                                           ( pdfFieldID_, field::DefaultEvaluationFilter(),
-                                             DefaultDensityEquilibriumVelocityCalculation(),
-                                             VelocityCallback<VelocityField_T>( velocityFieldID ) ) );
-            }
-            else
-            {
-               result_ = object( makeCellwiseSweep<LatticeModel_T>( pdfFieldID_ ) );
-            }
-         }
-      }
-
-      boost::python::object getResult() const { return result_; }
-
-   private:
-      shared_ptr<StructuredBlockStorage> blocks_;
-      BlockDataID pdfFieldID_;
-      std::string flagFieldStringID_;
-      std::string velocityFieldStringID_;
-      Set< FlagUID >  cellsToEvaluate_;
-
-      boost::python::object result_;
-   };
-
-
-   template<typename LatticeModel_FlagField_Pairs>
-   boost::python::object makeCellwiseSweep( const shared_ptr<StructuredBlockStorage> & bs,
-                                            const std::string & pdfFieldStringID,
-                                            const std::string & flagFieldStringID, boost::python::list flagList,
-                                            const std::string & velocityFieldStringID )
-   {
-      using namespace boost::python;
-
-      if ( bs->begin() == bs->end() )
-         return object();
-      IBlock & firstBlock = *(  bs->begin() );
-
-      if( flagFieldStringID.size() > 0 || len(flagList) > 0 )
-      {
-         if ( !( flagFieldStringID.size() > 0 && len(flagList) > 0 ) )
-         {
-            PyErr_SetString( PyExc_ValueError, "Pass flagFieldID and flagList or none of them.");
-            throw error_already_set();
-         }
-      }
-
-      BlockDataID pdfFieldID = python_coupling::blockDataIDFromString( firstBlock, pdfFieldStringID );
-
-      auto flagUidSet = python_coupling::uidSetFromStringContainer< FlagUID >( flagList );
-
-      CellwiseSweepCreator creator( bs, pdfFieldID, flagFieldStringID, velocityFieldStringID, flagUidSet );
-      python_coupling::for_each_noncopyable_type<LatticeModel_FlagField_Pairs>( std::ref( creator ) );
-
-      if ( creator.getResult() == object() )
-      {
-         PyErr_SetString( PyExc_ValueError, "No Sweep available with these options.");
-         throw error_already_set();
-      }
-      return creator.getResult();
-   }
-
-}
-
-template<typename LatticeModels, typename FlagFields>
-void exportSweeps()
-{
-   using namespace boost::python;
-
-
-   python_coupling::ModuleScope scope("lbm");
-
-   // Cellwise Sweep
-   typedef python_coupling::combine_vectors<LatticeModels,FlagFields> LatticeModel_FlagField_Pairs;
-   python_coupling::for_each_noncopyable_type<LatticeModel_FlagField_Pairs>( internal::CellwiseSweepExporterWithFlagField() );
-   python_coupling::for_each_noncopyable_type<LatticeModels>               ( internal::CellwiseSweepExporterWithoutFlagField() );
-   def( "makeCellwiseSweep", internal::makeCellwiseSweep<LatticeModel_FlagField_Pairs>,
-                  ( arg("blocks"), arg("pdfFieldID"),
-                    arg("flagFieldID")=std::string(), arg("flagList")=boost::python::list(),
-                    arg("velocityFieldID")=std::string() ) );
-
-}
-
-
-
-} // namespace lbm
-} // namespace walberla
-
-
diff --git a/src/lbm/python/Exports.h b/src/lbm/python/Exports.h
deleted file mode 100644
index bbf41d3b25787ed9e1e140c713bcce17815213b1..0000000000000000000000000000000000000000
--- a/src/lbm/python/Exports.h
+++ /dev/null
@@ -1,49 +0,0 @@
-//======================================================================================================================
-//
-//  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 PythonExports.h
-//! \ingroup lbm
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include "waLBerlaDefinitions.h"
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-#include "ExportBasic.h"
-#include "ExportSweeps.h"
-#include "ExportBoundary.h"
-
-
-namespace walberla {
-namespace lbm {
-
-
-    template<typename LatticeModels, typename FlagFields>
-    void exportModuleToPython()
-    {
-       exportBasic<LatticeModels,FlagFields>();
-       exportSweeps<LatticeModels,FlagFields>();
-       exportBoundary<LatticeModels,FlagFields>();
-    }
-
-
-} // namespace lbm
-} // namespace walberla
-
-
-#endif // WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/lbm/refinement/CurlBasedLevelDetermination.h b/src/lbm/refinement/CurlBasedLevelDetermination.h
new file mode 100644
index 0000000000000000000000000000000000000000..7e9157c31c59c59bcd2f806285725cedc00aa493
--- /dev/null
+++ b/src/lbm/refinement/CurlBasedLevelDetermination.h
@@ -0,0 +1,178 @@
+//======================================================================================================================
+//
+//  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 VorticityBasedLevelDetermination.h
+//! \ingroup lbm
+//! \author Florian Schornbaum <florian.schornbaum@fau.de>
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//! \author Lukas Werner <lks.werner@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "blockforest/BlockForest.h"
+#include "core/math/Vector3.h"
+#include "domain_decomposition/BlockDataID.h"
+#include "field/GhostLayerField.h"
+
+#include <vector>
+
+namespace walberla {
+namespace lbm {
+namespace refinement {
+
+
+/*!\brief Level determination for refinement check based on local curl
+ *
+ * If (scaled) vorticity magnitude is below lowerLimit in all cells of a block, that block could be coarsened.
+ * If the (scaled) vorticity value is above the upperLimit for at least one cell, that block gets marked for refinement.
+ * Else, the block remains on the current level.
+ *
+ * The scaling originates from neglecting the actual mesh size on the block to obtain different vorticity values for
+ * different mesh sizes.
+ *
+ * Parametes upperLimit corresponds to sigma_c, coarsenFactor to c, lowerLimit to c*sigma_c, lengthScaleWeight to r.
+ */
+template< typename Filter_T >
+class CurlBasedLevelDetermination // used as a 'BlockForest::RefreshMinTargetLevelDeterminationFunction'
+{
+
+public:
+
+   using VectorField_T = GhostLayerField<Vector3<real_t>, 1>;
+
+   CurlBasedLevelDetermination(const ConstBlockDataID & fieldID, const StructuredBlockForest & structuredBlockForest,
+         const Filter_T & filter, const uint_t maxLevel,
+         const real_t upperLimit, const real_t lowerLimit, const real_t lengthScaleWeight = real_t(2)) :
+         fieldID_(fieldID), structuredBlockForest_(structuredBlockForest), filter_(filter), maxLevel_(maxLevel),
+         upperLimitSqr_(upperLimit*upperLimit), lowerLimitSqr_(lowerLimit*lowerLimit),
+         lengthScaleWeight_(lengthScaleWeight)
+   {
+      WALBERLA_CHECK_FLOAT_UNEQUAL(lengthScaleWeight_, real_t(0)); // else std::pow(x, y/0) is calculated further below
+   }
+
+   void operator()( std::vector< std::pair< const Block *, uint_t > > & minTargetLevels,
+                    std::vector< const Block * > & blocksAlreadyMarkedForRefinement,
+                    const BlockForest & forest );
+
+private:
+
+   ConstBlockDataID fieldID_;
+   const StructuredBlockForest & structuredBlockForest_;
+
+   Filter_T filter_;
+
+   uint_t maxLevel_;
+
+   real_t upperLimitSqr_;
+   real_t lowerLimitSqr_;
+
+   real_t lengthScaleWeight_;
+};
+
+template< typename Filter_T >
+void CurlBasedLevelDetermination< Filter_T >::operator()( std::vector< std::pair< const Block *,
+      uint_t > > & minTargetLevels, std::vector< const Block * > &, const BlockForest & forest) {
+
+   for(auto & minTargetLevel : minTargetLevels) {
+      const Block * const block = minTargetLevel.first;
+      const VectorField_T * u = block->template getData< VectorField_T >(fieldID_);
+
+      if(u == nullptr) {
+         minTargetLevel.second = uint_t(0);
+         continue;
+      }
+
+      WALBERLA_ASSERT_GREATER_EQUAL(u->nrOfGhostLayers(), uint_t(1));
+
+      CellInterval interval = u->xyzSize();
+      Cell expand(cell_idx_c(-1), cell_idx_c(-1), cell_idx_c(-1));
+      interval.expand(expand);
+
+      const auto one = cell_idx_t(1);
+
+      const real_t dx = structuredBlockForest_.dx(forest.getLevel(*block));
+      const real_t dy = structuredBlockForest_.dy(forest.getLevel(*block));
+      const real_t dz = structuredBlockForest_.dz(forest.getLevel(*block));
+
+      const auto halfInvDx = real_t(0.5) * real_t(1) / dx;
+      const auto halfInvDy = real_t(0.5) * real_t(1) / dy;
+      const auto halfInvDz = real_t(0.5) * real_t(1) / dz;
+
+      bool refine = false;
+      bool coarsen = true;
+
+      filter_(*block);
+
+      const cell_idx_t xSize = cell_idx_c(interval.xSize());
+      const cell_idx_t ySize = cell_idx_c(interval.ySize());
+      const cell_idx_t zSize = cell_idx_c(interval.zSize());
+
+      const real_t lengthScale = std::cbrt(dx*dy*dz);
+      const real_t weightedLengthScale = std::pow(lengthScale, (lengthScaleWeight_+1)/lengthScaleWeight_);
+      const real_t weightedLengthScaleSqr = weightedLengthScale*weightedLengthScale;
+
+      for (auto z = cell_idx_t(0); z < zSize; ++z) {
+         for (auto y = cell_idx_t(0); y < ySize; ++y) {
+            for (auto x = cell_idx_t(0); x < xSize; ++x) {
+               if (filter_(x,y,z) && filter_(x+one,y,z) && filter_(x-one,y,z) && filter_(x,y+one,z) && filter_(x,y-one,z)
+                  && filter_(x,y,z+one) && filter_(x,y,z-one)) {
+                  const Vector3< real_t > xa = u->get(x+one,y,z);
+                  const Vector3< real_t > xb = u->get(x-one,y,z);
+                  const Vector3< real_t > ya = u->get(x,y+one,z);
+                  const Vector3< real_t > yb = u->get(x,y-one,z);
+                  const Vector3< real_t > za = u->get(x,y,z+one);
+                  const Vector3< real_t > zb = u->get(x,y,z-one);
+
+                  const real_t duzdy = halfInvDy * (ya[2] - yb[2]);
+                  const real_t duydz = halfInvDz * (za[1] - zb[1]);
+                  const real_t duxdz = halfInvDz * (za[0] - zb[0]);
+                  const real_t duzdx = halfInvDx * (xa[2] - xb[2]);
+                  const real_t duydx = halfInvDx * (xa[1] - xb[1]);
+                  const real_t duxdy = halfInvDy * (ya[0] - yb[0]);
+
+                  const Vector3< real_t > curl( duzdy - duydz, duxdz - duzdx, duydx - duxdy );
+                  const auto curlSqr = curl.sqrLength();
+
+                  const auto curlSensorSqr = curlSqr * weightedLengthScaleSqr;
+
+                  if (curlSensorSqr > lowerLimitSqr_) {
+                     // curl is not small enough to coarsen, i.e. stay at least on the current level
+                     coarsen = false;
+                     if (curlSensorSqr > upperLimitSqr_) {
+                        // curl is big enough for refinement
+                        refine = true;
+                     }
+                  }
+               }
+            }
+         }
+      }
+
+      if (refine && block->getLevel() < maxLevel_) {
+         WALBERLA_ASSERT(!coarsen);
+         minTargetLevel.second = block->getLevel() + uint_t(1);
+      }
+      if (coarsen && block->getLevel() > uint_t(0)) {
+         WALBERLA_ASSERT(!refine);
+         minTargetLevel.second = block->getLevel() - uint_t(1);
+      }
+   }
+}
+
+} // namespace refinement
+} // namespace lbm
+} // namespace walberla
diff --git a/src/lbm/refinement/EqualLevelBorderStreamCorrection.h b/src/lbm/refinement/EqualLevelBorderStreamCorrection.h
index 2cc428b98c35add1263d4a988d72287cab414b46..957b99adc87e34f56437a9111d5373cdf8eec03f 100644
--- a/src/lbm/refinement/EqualLevelBorderStreamCorrection.h
+++ b/src/lbm/refinement/EqualLevelBorderStreamCorrection.h
@@ -47,12 +47,12 @@ struct EdgeCornerStencil;
 template< typename LatticeModel_T >
 struct EdgeCornerStencil< LatticeModel_T, typename std::enable_if< LatticeModel_T::Stencil::D == 2 >::type >
 {
-   typedef stencil::D2CornerStencil type;
+   using type = stencil::D2CornerStencil;
 };
 template< typename LatticeModel_T >
 struct EdgeCornerStencil< LatticeModel_T, typename std::enable_if< LatticeModel_T::Stencil::D == 3 >::type >
 {
-   typedef stencil::D3EdgeCornerStencil type;
+   using type = stencil::D3EdgeCornerStencil;
 };
 }
 
@@ -63,8 +63,8 @@ class EqualLevelBorderStreamCorrection
 {
 public:
 
-   typedef PdfField< LatticeModel_T > PdfField_T;
-   typedef typename internal::EdgeCornerStencil< LatticeModel_T >::type EdgeCornerStencil_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using EdgeCornerStencil_T = typename internal::EdgeCornerStencil<LatticeModel_T>::type;
 
    EqualLevelBorderStreamCorrection( const BlockDataID & pdfFieldId ) : pdfFieldId_( pdfFieldId ) {}
 
diff --git a/src/lbm/refinement/LinearExplosion.h b/src/lbm/refinement/LinearExplosion.h
index c70b7befe7296d10b6d9caec91ded7d1532b3d4f..a43f079239b9658234a3be7aaf0cd19f9422a6f4 100644
--- a/src/lbm/refinement/LinearExplosion.h
+++ b/src/lbm/refinement/LinearExplosion.h
@@ -67,12 +67,12 @@ class LinearExplosion
 {
 private:
 
-   typedef typename LatticeModel_T::Stencil Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
-   typedef PdfField< LatticeModel_T > PdfField_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
 
-   typedef field::GhostLayerField< typename PdfField_T::value_type, PdfField_T::F_SIZE > CoarseField;
-   typedef field::GhostLayerField< bool, 1 > BoolField;
+   using CoarseField = field::GhostLayerField<typename PdfField_T::value_type, PdfField_T::F_SIZE>;
+   using BoolField = field::GhostLayerField<bool, 1>;
 
 public:
 
diff --git a/src/lbm/refinement/PdfFieldPackInfo.h b/src/lbm/refinement/PdfFieldPackInfo.h
index 6fbfb92216ba567974b4dbd717be27dc3e3a403d..1c1202e4f7f1f4aa499b63af2d9983fdcdb04e71 100644
--- a/src/lbm/refinement/PdfFieldPackInfo.h
+++ b/src/lbm/refinement/PdfFieldPackInfo.h
@@ -44,8 +44,8 @@ class PdfFieldPackInfo : public TimeStepPdfPackInfo
 {
 public:
 
-   typedef PdfField<LatticeModel_T>          PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
 #ifdef NDEBUG   
    PdfFieldPackInfo( const BlockDataID & pdfFieldId, const bool _optimizedEqualLevelCommunication = true,
@@ -60,31 +60,31 @@ public:
       equalLevelCells_( equalLevelCells() ) {}
 #endif
 
-   virtual ~PdfFieldPackInfo() {}
+   ~PdfFieldPackInfo() override = default;
 
-   bool optimizedEqualLevelCommunication() const { return optimizedEqualLevelCommunication_; }
-   void optimizeEqualLevelCommunication( const bool value = true ) { optimizedEqualLevelCommunication_ = value; }
+   bool optimizedEqualLevelCommunication() const override { return optimizedEqualLevelCommunication_; }
+   void optimizeEqualLevelCommunication( const bool value = true ) override { optimizedEqualLevelCommunication_ = value; }
    
-   bool optimizedForLinearExplosion() const { return optimizedForLinearExplosion_; }
-   void optimizeForLinearExplosion( const bool value = true ) { optimizedForLinearExplosion_ = value; }
+   bool optimizedForLinearExplosion() const override { return optimizedForLinearExplosion_; }
+   void optimizeForLinearExplosion( const bool value = true ) override { optimizedForLinearExplosion_ = value; }
    
-   bool constantDataExchange() const { return true; }
-   bool threadsafeReceiving()  const { return true; }
+   bool constantDataExchange() const override { return true; }
+   bool threadsafeReceiving()  const override { return true; }
 
-   void       unpackDataEqualLevel( Block * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer );
-   void communicateLocalEqualLevel( const Block * sender, Block * receiver, stencil::Direction dir );
+   void       unpackDataEqualLevel( Block * receiver, stencil::Direction dir, mpi::RecvBuffer & buffer ) override;
+   void communicateLocalEqualLevel( const Block * sender, Block * receiver, stencil::Direction dir ) override;
 
-   void       unpackDataCoarseToFine( Block * fineReceiver, const BlockID & coarseSender, stencil::Direction dir, mpi::RecvBuffer & buffer );
-   void communicateLocalCoarseToFine( const Block * coarseSender, Block * fineReceiver, stencil::Direction dir );
+   void       unpackDataCoarseToFine( Block * fineReceiver, const BlockID & coarseSender, stencil::Direction dir, mpi::RecvBuffer & buffer ) override;
+   void communicateLocalCoarseToFine( const Block * coarseSender, Block * fineReceiver, stencil::Direction dir ) override;
 
-   void       unpackDataFineToCoarse( Block * coarseReceiver, const BlockID & fineSender, stencil::Direction dir, mpi::RecvBuffer & buffer );
-   void communicateLocalFineToCoarse( const Block * fineSender, Block * coarseReceiver, stencil::Direction dir );
+   void       unpackDataFineToCoarse( Block * coarseReceiver, const BlockID & fineSender, stencil::Direction dir, mpi::RecvBuffer & buffer ) override;
+   void communicateLocalFineToCoarse( const Block * fineSender, Block * coarseReceiver, stencil::Direction dir ) override;
 
 protected:
 
-   void packDataEqualLevelImpl( const Block * sender, stencil::Direction dir, mpi::SendBuffer & buffer ) const;
-   void packDataCoarseToFineImpl( const Block * coarseSender, const BlockID &   fineReceiver, stencil::Direction dir, mpi::SendBuffer & buffer ) const;
-   void packDataFineToCoarseImpl( const Block *   fineSender, const BlockID & coarseReceiver, stencil::Direction dir, mpi::SendBuffer & buffer ) const;
+   void packDataEqualLevelImpl( const Block * sender, stencil::Direction dir, mpi::SendBuffer & buffer ) const override;
+   void packDataCoarseToFineImpl( const Block * coarseSender, const BlockID &   fineReceiver, stencil::Direction dir, mpi::SendBuffer & buffer ) const override;
+   void packDataFineToCoarseImpl( const Block *   fineSender, const BlockID & coarseReceiver, stencil::Direction dir, mpi::SendBuffer & buffer ) const override;
 
    ///////////////////////////////////////////////////////////////////////
    // Helper functions for determining packing/unpacking cell intervals //
@@ -1090,18 +1090,18 @@ inline std::vector< CellInterval > PdfFieldPackInfo< LatticeModel_T, BoundaryHan
 
       WALBERLA_ASSERT_EQUAL( interval.xSize(), equalLevelCells_ );
 
-      intervals.push_back( CellInterval( interval.xMin(), interval.yMin(), interval.zMax(),
-                                         interval.xMax(), interval.yMax(), interval.zMax() ) );
-      intervals.push_back( CellInterval( interval.xMin(), interval.yMin(), interval.zMin() + cell_idx_t(1),
-                                         interval.xMax(), interval.yMin(), interval.zMax() - cell_idx_t(1) ) );
-      intervals.push_back( CellInterval( interval.xMin(), interval.yMax(), interval.zMin() + cell_idx_t(1),
-                                         interval.xMax(), interval.yMax(), interval.zMax() - cell_idx_t(1) ) );
-      intervals.push_back( CellInterval( interval.xMin(), interval.yMin(), interval.zMin(),
-                                         interval.xMax(), interval.yMax(), interval.zMin() ) );
+      intervals.emplace_back( interval.xMin(), interval.yMin(), interval.zMax(),
+                                         interval.xMax(), interval.yMax(), interval.zMax() );
+      intervals.emplace_back( interval.xMin(), interval.yMin(), interval.zMin() + cell_idx_t(1),
+                                         interval.xMax(), interval.yMin(), interval.zMax() - cell_idx_t(1) );
+      intervals.emplace_back( interval.xMin(), interval.yMax(), interval.zMin() + cell_idx_t(1),
+                                         interval.xMax(), interval.yMax(), interval.zMax() - cell_idx_t(1) );
+      intervals.emplace_back( interval.xMin(), interval.yMin(), interval.zMin(),
+                                         interval.xMax(), interval.yMax(), interval.zMin() );
 
       const cell_idx_t x = ( stencil::cx[dir] > 0 ) ? interval.xMax() : interval.xMin();
-      intervals.push_back( CellInterval( x, interval.yMin() + cell_idx_t(1), interval.zMin() + cell_idx_t(1),
-                                         x, interval.yMax() - cell_idx_t(1), interval.zMax() - cell_idx_t(1) ) );
+      intervals.emplace_back( x, interval.yMin() + cell_idx_t(1), interval.zMin() + cell_idx_t(1),
+                                         x, interval.yMax() - cell_idx_t(1), interval.zMax() - cell_idx_t(1) );
    }
    else if( stencil::cy[dir] != 0 )
    {
@@ -1110,18 +1110,18 @@ inline std::vector< CellInterval > PdfFieldPackInfo< LatticeModel_T, BoundaryHan
 
       WALBERLA_ASSERT_EQUAL( interval.ySize(), equalLevelCells_ );
 
-      intervals.push_back( CellInterval( interval.xMin(), interval.yMin(), interval.zMax(),
-                                         interval.xMax(), interval.yMax(), interval.zMax() ) );
-      intervals.push_back( CellInterval( interval.xMin(), interval.yMin(), interval.zMin() + cell_idx_t(1),
-                                         interval.xMin(), interval.yMax(), interval.zMax() - cell_idx_t(1) ) );
-      intervals.push_back( CellInterval( interval.xMax(), interval.yMin(), interval.zMin() + cell_idx_t(1),
-                                         interval.xMax(), interval.yMax(), interval.zMax() - cell_idx_t(1) ) );
-      intervals.push_back( CellInterval( interval.xMin(), interval.yMin(), interval.zMin(),
-                                         interval.xMax(), interval.yMax(), interval.zMin() ) );
+      intervals.emplace_back( interval.xMin(), interval.yMin(), interval.zMax(),
+                                         interval.xMax(), interval.yMax(), interval.zMax() );
+      intervals.emplace_back( interval.xMin(), interval.yMin(), interval.zMin() + cell_idx_t(1),
+                                         interval.xMin(), interval.yMax(), interval.zMax() - cell_idx_t(1) );
+      intervals.emplace_back( interval.xMax(), interval.yMin(), interval.zMin() + cell_idx_t(1),
+                                         interval.xMax(), interval.yMax(), interval.zMax() - cell_idx_t(1) );
+      intervals.emplace_back( interval.xMin(), interval.yMin(), interval.zMin(),
+                                         interval.xMax(), interval.yMax(), interval.zMin() );
 
       const cell_idx_t y = ( stencil::cy[dir] > 0 ) ? interval.yMax() : interval.yMin();
-      intervals.push_back( CellInterval( interval.xMin() + cell_idx_t(1), y, interval.zMin() + cell_idx_t(1),
-                                         interval.xMax() - cell_idx_t(1), y, interval.zMax() - cell_idx_t(1) ) );
+      intervals.emplace_back( interval.xMin() + cell_idx_t(1), y, interval.zMin() + cell_idx_t(1),
+                                         interval.xMax() - cell_idx_t(1), y, interval.zMax() - cell_idx_t(1) );
    }
    else
    {
@@ -1131,18 +1131,18 @@ inline std::vector< CellInterval > PdfFieldPackInfo< LatticeModel_T, BoundaryHan
 
       WALBERLA_ASSERT_EQUAL( interval.zSize(), equalLevelCells_ );
 
-      intervals.push_back( CellInterval( interval.xMin(), interval.yMax(),                 interval.zMin(),
-                                         interval.xMax(), interval.yMax(),                 interval.zMax() ) );
-      intervals.push_back( CellInterval( interval.xMin(), interval.yMin() + cell_idx_t(1), interval.zMin(),
-                                         interval.xMin(), interval.yMax() - cell_idx_t(1), interval.zMax() ) );
-      intervals.push_back( CellInterval( interval.xMax(), interval.yMin() + cell_idx_t(1), interval.zMin(),
-                                         interval.xMax(), interval.yMax() - cell_idx_t(1), interval.zMax() ) );
-      intervals.push_back( CellInterval( interval.xMin(), interval.yMin(),                 interval.zMin(),
-                                         interval.xMax(), interval.yMin(),                 interval.zMax() ) );
+      intervals.emplace_back( interval.xMin(), interval.yMax(),                 interval.zMin(),
+                                         interval.xMax(), interval.yMax(),                 interval.zMax() );
+      intervals.emplace_back( interval.xMin(), interval.yMin() + cell_idx_t(1), interval.zMin(),
+                                         interval.xMin(), interval.yMax() - cell_idx_t(1), interval.zMax() );
+      intervals.emplace_back( interval.xMax(), interval.yMin() + cell_idx_t(1), interval.zMin(),
+                                         interval.xMax(), interval.yMax() - cell_idx_t(1), interval.zMax() );
+      intervals.emplace_back( interval.xMin(), interval.yMin(),                 interval.zMin(),
+                                         interval.xMax(), interval.yMin(),                 interval.zMax() );
 
       const cell_idx_t z = ( stencil::cz[dir] > 0 ) ? interval.zMax() : interval.zMin();
-      intervals.push_back( CellInterval( interval.xMin() + cell_idx_t(1), interval.yMin() + cell_idx_t(1), z,
-                                         interval.xMax() - cell_idx_t(1), interval.yMax() - cell_idx_t(1), z ) );
+      intervals.emplace_back( interval.xMin() + cell_idx_t(1), interval.yMin() + cell_idx_t(1), z,
+                                         interval.xMax() - cell_idx_t(1), interval.yMax() - cell_idx_t(1), z );
    }
 
    return intervals;
diff --git a/src/lbm/refinement/TimeStep.h b/src/lbm/refinement/TimeStep.h
index 1be6a470d5402e21600831870524e80c563f6716..bafcdcc04363a620bf8323c4ee809f369bea5a5e 100644
--- a/src/lbm/refinement/TimeStep.h
+++ b/src/lbm/refinement/TimeStep.h
@@ -53,10 +53,10 @@ class TimeStep
 {
 public:
    
-   typedef typename NeighborsStencil<LatticeModel_T>::type CommunicationStencil_T;
+   using CommunicationStencil_T = typename NeighborsStencil<LatticeModel_T>::type;
 
-   typedef std::function< void ( const uint_t, const uint_t ) >            VoidFunction;  // parameters: level, execution count
-   typedef std::function< void ( IBlock *, const uint_t, const uint_t ) >  BlockFunction; // parameters: block, level, execution count
+   using VoidFunction = std::function<void (const uint_t, const uint_t)>;  // parameters: level, execution count
+   using BlockFunction = std::function<void (IBlock *, const uint_t, const uint_t)>; // parameters: block, level, execution count
    
 private:
 
@@ -110,7 +110,7 @@ private:
 
 public:
 
-   typedef typename LatticeModel_T::Stencil Stencil;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    static const uint_t StreamIncludedGhostLayers = 2;
 
@@ -397,14 +397,14 @@ void TimeStep< LatticeModel_T, Sweep_T, BoundaryHandling_T >::consistencyChecks(
          continue;
 
       auto *  pdfField = block->template getData< PdfField< LatticeModel_T > >( pdfFieldId );
-      if( pdfField == NULL )
+      if( pdfField == nullptr )
       {
          WALBERLA_ABORT( "Could not get the PDF field from block " << block->getId() << ". Check if it is allocated on "
                          "the block and if the LatticeModel matches!" );
       }
 
       auto * boundaryHandling = block->template getData< BoundaryHandling_T >( boundaryHandlingId );
-      if( boundaryHandling == NULL )
+      if( boundaryHandling == nullptr )
       {
          WALBERLA_ABORT( "Could not get the boundary handling from block " << block->getId() << ". Check if it is "
                          "allocated on the block and if its type matches!" );
@@ -595,7 +595,7 @@ template< typename LatticeModel_T, typename Sweep_T, typename BoundaryHandling_T
 inline void TimeStep< LatticeModel_T, Sweep_T, BoundaryHandling_T >::addPostCollideVoidFunction( const VoidFunction & function, const std::string & identifier )
 {
    VoidFunctionWrappper wrappedFunction( globalPostCollideVoidFunctions_, globalPostCollideVoidFunctions_.size() );
-   globalPostCollideVoidFunctions_.push_back( std::make_pair( function, identifier ) );
+   globalPostCollideVoidFunctions_.emplace_back( function, identifier );
    addFunction< VoidFunction >( postCollideVoidFunctions_, wrappedFunction, identifier );
 }
 
@@ -603,7 +603,7 @@ template< typename LatticeModel_T, typename Sweep_T, typename BoundaryHandling_T
 inline void TimeStep< LatticeModel_T, Sweep_T, BoundaryHandling_T >::addPostCollideBlockFunction( const BlockFunction & function, const std::string & identifier )
 {
    BlockFunctionWrappper wrappedFunction( globalPostCollideBlockFunctions_, globalPostCollideBlockFunctions_.size() );
-   globalPostCollideBlockFunctions_.push_back( std::make_pair( function, identifier ) );
+   globalPostCollideBlockFunctions_.emplace_back( function, identifier );
    addFunction< BlockFunction >( postCollideBlockFunctions_, wrappedFunction, identifier );
 }
 
@@ -623,7 +623,7 @@ template< typename LatticeModel_T, typename Sweep_T, typename BoundaryHandling_T
 inline void TimeStep< LatticeModel_T, Sweep_T, BoundaryHandling_T >::addPostBoundaryHandlingVoidFunction( const VoidFunction & function, const std::string & identifier )
 {
    VoidFunctionWrappper wrappedFunction( globalPostBoundaryHandlingVoidFunctions_, globalPostBoundaryHandlingVoidFunctions_.size() );
-   globalPostBoundaryHandlingVoidFunctions_.push_back( std::make_pair( function, identifier ) );
+   globalPostBoundaryHandlingVoidFunctions_.emplace_back( function, identifier );
    addFunction< VoidFunction >( postBoundaryHandlingVoidFunctions_, wrappedFunction, identifier );
 }
 
@@ -631,7 +631,7 @@ template< typename LatticeModel_T, typename Sweep_T, typename BoundaryHandling_T
 inline void TimeStep< LatticeModel_T, Sweep_T, BoundaryHandling_T >::addPostBoundaryHandlingBlockFunction( const BlockFunction & function, const std::string & identifier )
 {
    BlockFunctionWrappper wrappedFunction( globalPostBoundaryHandlingBlockFunctions_, globalPostBoundaryHandlingBlockFunctions_.size() );
-   globalPostBoundaryHandlingBlockFunctions_.push_back( std::make_pair( function, identifier ) );
+   globalPostBoundaryHandlingBlockFunctions_.emplace_back( function, identifier );
    addFunction< BlockFunction >( postBoundaryHandlingBlockFunctions_, wrappedFunction, identifier );
 }
 
@@ -651,7 +651,7 @@ template< typename LatticeModel_T, typename Sweep_T, typename BoundaryHandling_T
 inline void TimeStep< LatticeModel_T, Sweep_T, BoundaryHandling_T >::addPostStreamVoidFunction( const VoidFunction & function, const std::string & identifier )
 {
    VoidFunctionWrappper wrappedFunction( globalPostStreamVoidFunctions_, globalPostStreamVoidFunctions_.size() );
-   globalPostStreamVoidFunctions_.push_back( std::make_pair( function, identifier ) );
+   globalPostStreamVoidFunctions_.emplace_back( function, identifier );
    addFunction< VoidFunction >( postStreamVoidFunctions_, wrappedFunction, identifier );
 }
 
@@ -659,7 +659,7 @@ template< typename LatticeModel_T, typename Sweep_T, typename BoundaryHandling_T
 inline void TimeStep< LatticeModel_T, Sweep_T, BoundaryHandling_T >::addPostStreamBlockFunction( const BlockFunction & function, const std::string & identifier )
 {
    BlockFunctionWrappper wrappedFunction( globalPostStreamBlockFunctions_, globalPostStreamBlockFunctions_.size() );
-   globalPostStreamBlockFunctions_.push_back( std::make_pair( function, identifier ) );
+   globalPostStreamBlockFunctions_.emplace_back( function, identifier );
    addFunction< BlockFunction >( postStreamBlockFunctions_, wrappedFunction, identifier );
 }
 
@@ -1726,7 +1726,7 @@ makeTimeStep( weak_ptr< StructuredBlockForest > blocks, shared_ptr< Sweep_T > &
               const Set<SUID> & requiredBlockSelectors = Set<SUID>::emptySet(),
               const Set<SUID> & incompatibleBlockSelectors = Set<SUID>::emptySet() )
 {
-   typedef TimeStep< LatticeModel_T, Sweep_T, BoundaryHandling_T > TS_T;
+   using TS_T = TimeStep<LatticeModel_T, Sweep_T, BoundaryHandling_T>;
    return shared_ptr< TS_T >( new TS_T( blocks, sweep, pdfFieldId, boundaryHandlingId, requiredBlockSelectors, incompatibleBlockSelectors ) );
 }
 
@@ -1740,7 +1740,7 @@ makeTimeStep( weak_ptr< StructuredBlockForest > blocks, shared_ptr< Sweep_T > &
               const Set<SUID> & requiredBlockSelectors = Set<SUID>::emptySet(),
               const Set<SUID> & incompatibleBlockSelectors = Set<SUID>::emptySet() )
 {
-   typedef TimeStep< LatticeModel_T, Sweep_T, BoundaryHandling_T > TS_T;
+   using TS_T = TimeStep<LatticeModel_T, Sweep_T, BoundaryHandling_T>;
    return shared_ptr< TS_T >( new TS_T( blocks, sweep, pdfFieldId, boundaryHandlingId, pdfPackInfo, requiredBlockSelectors, incompatibleBlockSelectors ) );
 }
 
diff --git a/src/lbm/refinement/VorticityBasedLevelDetermination.h b/src/lbm/refinement/VorticityBasedLevelDetermination.h
index c3f4ee20d66f3840876c685a148af3d49d6b3156..2be753c0ce9be6f7c15010c1875ed1f8d69b516a 100644
--- a/src/lbm/refinement/VorticityBasedLevelDetermination.h
+++ b/src/lbm/refinement/VorticityBasedLevelDetermination.h
@@ -49,7 +49,7 @@ class VorticityBasedLevelDetermination // used as a 'BlockForest::RefreshMinTarg
 
 public:
 
-   typedef GhostLayerField< Vector3<real_t>, 1 >  VectorField_T;
+   using VectorField_T = GhostLayerField<Vector3<real_t>, 1>;
 
    VorticityBasedLevelDetermination( const ConstBlockDataID & fieldID, const Filter_T & filter,
                                      const real_t upperLimit, const real_t lowerLimit, const uint_t maxLevel ) :
diff --git a/src/lbm/refinement/all.h b/src/lbm/refinement/all.h
index a63a1ba388c87af4a16b639e81ed7acd5ed3c8ab..e55e8edb104a9b84facaf64f4d9a8e738be3d124 100644
--- a/src/lbm/refinement/all.h
+++ b/src/lbm/refinement/all.h
@@ -30,3 +30,4 @@
 #include "TimeStepPdfPackInfo.h"
 #include "TimeTracker.h"
 #include "VorticityBasedLevelDetermination.h"
+#include "CurlBasedLevelDetermination.h"
\ No newline at end of file
diff --git a/src/lbm/srt/SplitPureSweep.impl.h b/src/lbm/srt/SplitPureSweep.impl.h
index b193c8636ada13a602e0d112ce13e4e361c30610..e1cfe424b7abca3506f077e290e0389233cefe00 100644
--- a/src/lbm/srt/SplitPureSweep.impl.h
+++ b/src/lbm/srt/SplitPureSweep.impl.h
@@ -73,8 +73,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef typename SweepBase<LatticeModel_T>::PdfField_T  PdfField_T;
-   typedef typename LatticeModel_T::Stencil                Stencil;
+   using PdfField_T = typename SweepBase<LatticeModel_T>::PdfField_T;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    // block has NO dst pdf field
    SplitPureSweep( const BlockDataID & pdfField ) :
@@ -99,8 +99,8 @@ void SplitPureSweep< LatticeModel_T, typename std::enable_if< std::is_same< type
                                                                  >::type
    >::operator()( IBlock * const block )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
 
    this->getFields( block, src, dst );
 
@@ -431,8 +431,8 @@ void SplitPureSweep< LatticeModel_T, typename std::enable_if< std::is_same< type
                                                                  >::type
    >::stream( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
 
    this->getFields( block, src, dst );
 
@@ -768,8 +768,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef typename SweepBase<LatticeModel_T>::PdfField_T  PdfField_T;
-   typedef typename LatticeModel_T::Stencil                Stencil;
+   using PdfField_T = typename SweepBase<LatticeModel_T>::PdfField_T;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    // block has NO dst pdf field
    SplitPureSweep( const BlockDataID & pdfField ) :
@@ -794,8 +794,8 @@ void SplitPureSweep< LatticeModel_T, typename std::enable_if< std::is_same< type
                                                                  >::type
    >::operator()( IBlock * const block )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
 
    this->getFields( block, src, dst );
 
@@ -1130,8 +1130,8 @@ void SplitPureSweep< LatticeModel_T, typename std::enable_if< std::is_same< type
                                                                  >::type
    >::stream( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
 
    this->getFields( block, src, dst );
 
diff --git a/src/lbm/srt/SplitSweep.impl.h b/src/lbm/srt/SplitSweep.impl.h
index b7e630c2222f5c7ccd05954b277c5fcb9bd8c016..b5f6b68fbe6eb8eaa852bfb9b0e7264dde24bf98 100644
--- a/src/lbm/srt/SplitSweep.impl.h
+++ b/src/lbm/srt/SplitSweep.impl.h
@@ -67,8 +67,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef typename FlagFieldSweepBase<LatticeModel_T,FlagField_T>::PdfField_T  PdfField_T;
-   typedef typename LatticeModel_T::Stencil                                     Stencil;
+   using PdfField_T = typename FlagFieldSweepBase<LatticeModel_T, FlagField_T>::PdfField_T;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    // block has NO dst pdf field, lbm mask consists of multiple flags
    SplitSweep( const BlockDataID & pdfField, const ConstBlockDataID & flagField, const Set< FlagUID > & lbmMask ) :
@@ -93,9 +93,9 @@ void SplitSweep< LatticeModel_T, FlagField_T, typename std::enable_if< std::is_s
                                                                           >::type
    >::operator()( IBlock * const block )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
-   const FlagField_T * flagField( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
+   const FlagField_T * flagField( nullptr );
 
    auto lbm = this->getLbmMaskAndFields( block, src, dst, flagField );
 
@@ -493,9 +493,9 @@ void SplitSweep< LatticeModel_T, FlagField_T, typename std::enable_if< std::is_s
                                                                           >::type
    >::stream( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
-   const FlagField_T * flagField( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
+   const FlagField_T * flagField( nullptr );
 
    auto lbm = this->getLbmMaskAndFields( block, src, dst, flagField );
 
@@ -517,8 +517,8 @@ void SplitSweep< LatticeModel_T, FlagField_T, typename std::enable_if< std::is_s
 {
    WALBERLA_ASSERT_EQUAL( numberOfGhostLayersToInclude, uint_t(0) ); // the implementation right now doesn't support inclusion of ghost layers in collide step!
 
-   PdfField_T * src( NULL );
-   const FlagField_T * flagField( NULL );
+   PdfField_T * src( nullptr );
+   const FlagField_T * flagField( nullptr );
 
    auto lbm = this->getLbmMaskAndFields( block, src, flagField );
 
@@ -902,8 +902,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef typename FlagFieldSweepBase<LatticeModel_T,FlagField_T>::PdfField_T  PdfField_T;
-   typedef typename LatticeModel_T::Stencil                                     Stencil;
+   using PdfField_T = typename FlagFieldSweepBase<LatticeModel_T, FlagField_T>::PdfField_T;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    // block has NO dst pdf field, lbm mask consists of multiple flags
    SplitSweep( const BlockDataID & pdfField, const ConstBlockDataID & flagField, const Set< FlagUID > & lbmMask ) :
@@ -928,9 +928,9 @@ void SplitSweep< LatticeModel_T, FlagField_T, typename std::enable_if< std::is_s
                                                                           >::type
    >::operator()( IBlock * const block )                                              
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
-   const FlagField_T * flagField( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
+   const FlagField_T * flagField( nullptr );
 
    auto lbm = this->getLbmMaskAndFields( block, src, dst, flagField );
 
@@ -1333,9 +1333,9 @@ void SplitSweep< LatticeModel_T, FlagField_T, typename std::enable_if< std::is_s
                                                                           >::type
    >::stream( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
-   const FlagField_T * flagField( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
+   const FlagField_T * flagField( nullptr );
 
    auto lbm = this->getLbmMaskAndFields( block, src, dst, flagField );
 
@@ -1357,8 +1357,8 @@ void SplitSweep< LatticeModel_T, FlagField_T, typename std::enable_if< std::is_s
 {
    WALBERLA_ASSERT_EQUAL( numberOfGhostLayersToInclude, uint_t(0) ); // the implementation right now doesn't support inclusion of ghost layers in collide step!
 
-   PdfField_T * src( NULL );
-   const FlagField_T * flagField( NULL );
+   PdfField_T * src( nullptr );
+   const FlagField_T * flagField( nullptr );
 
    auto lbm = this->getLbmMaskAndFields( block, src, flagField );
 
diff --git a/src/lbm/srt/cell_operations/AdvectionDiffusionCellOperation.impl.h b/src/lbm/srt/cell_operations/AdvectionDiffusionCellOperation.impl.h
index 44883340ff6eaed38c053c6f9391c8171537e02d..f638cf3c9f870cf759465f5a1eed2dbf75edbdef 100644
--- a/src/lbm/srt/cell_operations/AdvectionDiffusionCellOperation.impl.h
+++ b/src/lbm/srt/cell_operations/AdvectionDiffusionCellOperation.impl.h
@@ -63,9 +63,9 @@ public:
    static_assert( LM_AdvDiff::compressible,                                                                      "Only works with compressible models!" );
    static_assert( (std::is_same< typename LM_AdvDiff::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
 
-   typedef PdfField< LM_AdvDiff >        AdvDiffPdfField_T;
-   typedef PdfField< LM_Hydro   >        HydroPdfField_T;
-   typedef typename LM_AdvDiff::Stencil  Stencil;
+   using AdvDiffPdfField_T = PdfField<LM_AdvDiff>;
+   using HydroPdfField_T = PdfField<LM_Hydro>;
+   using Stencil = typename LM_AdvDiff::Stencil;
 
    AdvectionDiffusionCellOperation() : omega_( real_t(0) ), advDiffLatticeModel_( NULL ), hydroLatticeModel_(NULL) {}
 
@@ -138,9 +138,9 @@ public:
    static_assert( LM_AdvDiff::compressible,                                                                      "Only works with compressible models!" );
    static_assert( (std::is_same< typename LM_AdvDiff::ForceModel::tag, force_model::Correction_tag >::value),  "Only works with correction force!" );
 
-   typedef PdfField< LM_AdvDiff >        AdvDiffPdfField_T;
-   typedef PdfField< LM_Hydro   >        HydroPdfField_T;
-   typedef typename LM_AdvDiff::Stencil  Stencil;
+   using AdvDiffPdfField_T = PdfField<LM_AdvDiff>;
+   using HydroPdfField_T = PdfField<LM_Hydro>;
+   using Stencil = typename LM_AdvDiff::Stencil;
 
    AdvectionDiffusionCellOperation() : omega_( real_t(0) ), advDiffLatticeModel_( NULL ), hydroLatticeModel_(NULL) {}
 
diff --git a/src/lbm/srt/cell_operations/DefaultCellOperation.impl.h b/src/lbm/srt/cell_operations/DefaultCellOperation.impl.h
index 7f1086e77cc77a89360e1ce8a5f87dcfb4054fee..0cf052e6b5ea7f04dbb5cbec8af475002d3b67d5 100644
--- a/src/lbm/srt/cell_operations/DefaultCellOperation.impl.h
+++ b/src/lbm/srt/cell_operations/DefaultCellOperation.impl.h
@@ -65,8 +65,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    DefaultCellOperation() : omega_( real_t(0) ), latticeModel_( NULL ) {}
 
@@ -136,8 +136,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::Guo_tag >::value),         "Only works with Guo constant force model !" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    DefaultCellOperation() : omega_( real_t(0) ), latticeModel_( NULL ) {}
 
@@ -230,8 +230,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    DefaultCellOperation() : omega_trm_( real_t(0) ), omega_w0_( real_t(0) ), omega_w1_( real_t(0) ), omega_w2_( real_t(0) ) {}
 
@@ -476,8 +476,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    DefaultCellOperation() : omega_trm_( real_t(0) ), omega_w0_( real_t(0) ), omega_w1_( real_t(0) ), omega_w2_( real_t(0) ) {}
 
diff --git a/src/lbm/sweeps/ActiveCellSweep.h b/src/lbm/sweeps/ActiveCellSweep.h
index e191b785d0cb5437bb4b6cfdbf64ac37345a748d..4874c36af4fc4c666aaef3f37610d8faeded25c6 100644
--- a/src/lbm/sweeps/ActiveCellSweep.h
+++ b/src/lbm/sweeps/ActiveCellSweep.h
@@ -35,7 +35,7 @@ class ActiveCellSweep : FlagFieldSweepBase< LatticeModel_T, FlagField_T >
 {
 public:
 
-   typedef typename FlagFieldSweepBase<LatticeModel_T,FlagField_T>::PdfField_T  PdfField_T;
+   using PdfField_T = typename FlagFieldSweepBase<LatticeModel_T, FlagField_T>::PdfField_T;
 
    // block has NO dst pdf field, lbm mask consists of multiple flags
    ActiveCellSweep( const CellOperation & op, const BlockDataID & pdfField, const ConstBlockDataID & flagField,
@@ -47,7 +47,7 @@ public:
                     const Set< FlagUID > & lbmMask, const bool _useIterators = false ) :
       FlagFieldSweepBase<LatticeModel_T,FlagField_T>( src, dst, flagField, lbmMask ), cellOperation_( op ), useIterators_( _useIterators ) {}
 
-   virtual ~ActiveCellSweep() {}
+   virtual ~ActiveCellSweep() = default;
 
    const CellOperation & getCellOperation() const { return cellOperation_; }
          CellOperation & getCellOperation()       { return cellOperation_; }
diff --git a/src/lbm/sweeps/CellwiseSweep.h b/src/lbm/sweeps/CellwiseSweep.h
index 2879f19ae2c56d5244706ec8c3cd9be5f6e852ae..e35e32f64b630a998edff5829c7d2f961b707618 100644
--- a/src/lbm/sweeps/CellwiseSweep.h
+++ b/src/lbm/sweeps/CellwiseSweep.h
@@ -410,7 +410,7 @@ shared_ptr< CellwiseSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, Densit
 makeCellwiseSweep( const BlockDataID & pdfFieldId, const Filter_T & filter,
                    const DensityVelocityIn_T & densityVelocityIn, const DensityVelocityOut_T & densityVelocityOut )
 {
-   typedef CellwiseSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T > CS_T;
+   using CS_T = CellwiseSweep<LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T>;
    return shared_ptr< CS_T >( new CS_T( pdfFieldId, filter, densityVelocityIn, densityVelocityOut ) );
 }
 
@@ -419,7 +419,7 @@ shared_ptr< CellwiseSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, Densit
 makeCellwiseSweep( const BlockDataID & src, const BlockDataID & dst, const Filter_T & filter,
                    const DensityVelocityIn_T & densityVelocityIn, const DensityVelocityOut_T & densityVelocityOut )
 {
-   typedef CellwiseSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T > CS_T;
+   using CS_T = CellwiseSweep<LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T>;
    return shared_ptr< CS_T >( new CS_T( src, dst, filter, densityVelocityIn, densityVelocityOut ) );
 }
 
@@ -520,7 +520,7 @@ shared_ptr< CellwiseSweep< LatticeModel_T, Filter_T, AdvectionDiffusionDensityEq
 makeCellwiseAdvectionDiffusionSweep( const BlockDataID & pdfFieldId, const ConstBlockDataID & velocityFieldId, const Filter_T & filter,
                                      const DensityVelocityOut_T & densityVelocityOut )
 {
-   typedef CellwiseSweep< LatticeModel_T, Filter_T, AdvectionDiffusionDensityEquilibriumVelocityCalculation<VelocityField_T>, DensityVelocityOut_T > CS_T;
+   using CS_T = CellwiseSweep<LatticeModel_T, Filter_T, AdvectionDiffusionDensityEquilibriumVelocityCalculation<VelocityField_T>, DensityVelocityOut_T>;
    return shared_ptr< CS_T >( new CS_T( pdfFieldId, filter,
                                         AdvectionDiffusionDensityEquilibriumVelocityCalculation<VelocityField_T>( velocityFieldId ), densityVelocityOut ) );
 }
@@ -530,7 +530,7 @@ shared_ptr< CellwiseSweep< LatticeModel_T, Filter_T, AdvectionDiffusionDensityEq
 makeCellwiseAdvectionDiffusionSweep( const BlockDataID & src, const BlockDataID & dst, const ConstBlockDataID & velocityFieldId,
                                      const Filter_T & filter, const DensityVelocityOut_T & densityVelocityOut )
 {
-   typedef CellwiseSweep< LatticeModel_T, Filter_T, AdvectionDiffusionDensityEquilibriumVelocityCalculation<VelocityField_T>, DensityVelocityOut_T > CS_T;
+   using CS_T = CellwiseSweep<LatticeModel_T, Filter_T, AdvectionDiffusionDensityEquilibriumVelocityCalculation<VelocityField_T>, DensityVelocityOut_T>;
    return shared_ptr< CS_T >( new CS_T( src, dst, filter,
                                         AdvectionDiffusionDensityEquilibriumVelocityCalculation<VelocityField_T>( velocityFieldId ), densityVelocityOut ) );
 }
diff --git a/src/lbm/sweeps/FlagFieldSweepBase.h b/src/lbm/sweeps/FlagFieldSweepBase.h
index 3df1dcf959d4b1ea4aecfc9ca3b560aea04e9ef5..edf5ed56be0593cb744d01536e2f82e143846c2c 100644
--- a/src/lbm/sweeps/FlagFieldSweepBase.h
+++ b/src/lbm/sweeps/FlagFieldSweepBase.h
@@ -35,8 +35,8 @@ class FlagFieldSweepBase : public SweepBase<LatticeModel_T>
 {
 public:
 
-   typedef typename SweepBase< LatticeModel_T >::PdfField_T PdfField_T;
-   typedef typename FlagField_T::flag_t                     flag_t;
+   using PdfField_T = typename SweepBase<LatticeModel_T>::PdfField_T;
+   using flag_t = typename FlagField_T::flag_t;
 
    // block has NO dst pdf field, lbm mask consists of multiple flags
    FlagFieldSweepBase( const BlockDataID & pdfField, const ConstBlockDataID & flagField, const Set< FlagUID > & lbmMask ) :
diff --git a/src/lbm/sweeps/StreamPull.h b/src/lbm/sweeps/StreamPull.h
index 136712f8bcd8cc09ceb3dc25db0d89bf1caf7fe5..fba95f580d56366b29111307bfd965038ff98fc9 100644
--- a/src/lbm/sweeps/StreamPull.h
+++ b/src/lbm/sweeps/StreamPull.h
@@ -62,8 +62,8 @@ struct StreamPullEverything
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q19 >::value == false), "There is a specialization for D3Q19!" );
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q27 >::value == false), "There is a specialization for D3Q27!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    static void execute( PdfField_T * src, PdfField_T * dst, const uint_t numberOfGhostLayersToInclude = uint_t(0) );
 };
@@ -114,8 +114,8 @@ struct StreamPullEverything< LatticeModel_T, typename std::enable_if< std::is_sa
 {
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D2Q9 >::value), "Only works with D2Q9!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    static void execute( PdfField_T * src, PdfField_T * dst, const uint_t numberOfGhostLayersToInclude = uint_t(0) );
 };
@@ -177,8 +177,8 @@ struct StreamPullEverything< LatticeModel_T, typename std::enable_if< std::is_sa
 {
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q19 >::value), "Only works with D3Q19!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    static void execute( PdfField_T * src, PdfField_T * dst, const uint_t numberOfGhostLayersToInclude = uint_t(0) );
 };
@@ -250,8 +250,8 @@ struct StreamPullEverything< LatticeModel_T, typename std::enable_if< std::is_sa
 {
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q27 >::value), "Only works with D3Q27!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    static void execute( PdfField_T * src, PdfField_T * dst, const uint_t numberOfGhostLayersToInclude = uint_t(0) );
 };
@@ -334,8 +334,8 @@ struct StreamPull
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q19 >::value == false), "There is a specialization for D3Q19!" );
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q27 >::value == false), "There is a specialization for D3Q27!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    template< typename Filter_T >
    static inline void execute( PdfField_T * src, PdfField_T * dst, IBlock * block, Filter_T & filter, const uint_t numberOfGhostLayersToInclude = uint_t(0) );
@@ -392,8 +392,8 @@ struct StreamPull< LatticeModel_T, typename std::enable_if< std::is_same< typena
 {
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D2Q9 >::value), "Only works with D2Q9!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    template< typename Filter_T >
    static void execute( PdfField_T * src, PdfField_T * dst, IBlock * block, Filter_T & filter, const uint_t numberOfGhostLayersToInclude = uint_t(0) );
@@ -464,8 +464,8 @@ struct StreamPull< LatticeModel_T, typename std::enable_if< std::is_same< typena
 {
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q19 >::value), "Only works with D3Q19!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    template< typename Filter_T >
    static void execute( PdfField_T * src, PdfField_T * dst, IBlock * block, Filter_T & filter, const uint_t numberOfGhostLayersToInclude = uint_t(0) );
@@ -546,8 +546,8 @@ struct StreamPull< LatticeModel_T, typename std::enable_if< std::is_same< typena
 {
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q27 >::value), "Only works with D3Q27!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    template< typename Filter_T >
    static void execute( PdfField_T * src, PdfField_T * dst, IBlock * block, Filter_T & filter, const uint_t numberOfGhostLayersToInclude = uint_t(0) );
diff --git a/src/lbm/sweeps/Streaming.h b/src/lbm/sweeps/Streaming.h
index f4d87fe4a4427591f7873d61d2a24932efe71902..b331e72f4b5cf9bf3b96b781be64d17ac5c70e0e 100644
--- a/src/lbm/sweeps/Streaming.h
+++ b/src/lbm/sweeps/Streaming.h
@@ -57,8 +57,8 @@ struct Stream< LatticeModel_T, FlagField_T, typename std::enable_if< ! std::is_s
 {
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q19 >::value == false), "There is a specialization for D3Q19!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    static void execute( PdfField_T * src, PdfField_T * dst, const FlagField_T * flagField, const typename FlagField_T::flag_t lbm,
                         const uint_t numberOfGhostLayersToInclude = uint_t(0) );
@@ -99,8 +99,8 @@ struct Stream< LatticeModel_T, FlagField_T, typename std::enable_if< std::is_sam
 {
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q19 >::value), "Only works with D3Q19!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    static void execute( PdfField_T * src, PdfField_T * dst, const FlagField_T * flagField, const typename FlagField_T::flag_t lbm,
                         const uint_t numberOfGhostLayersToInclude = uint_t(0) );
@@ -164,8 +164,8 @@ struct StreamEverything< LatticeModel_T, typename std::enable_if< ! std::is_same
 {
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q19 >::value == false), "There is a specialization for D3Q19!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    static void execute( PdfField_T * src, PdfField_T * dst, const uint_t numberOfGhostLayersToInclude = uint_t(0) );
 };
@@ -216,8 +216,8 @@ struct StreamEverything< LatticeModel_T, typename std::enable_if< std::is_same<
 {
    static_assert( (std::is_same< typename LatticeModel_T::Stencil, stencil::D3Q19 >::value), "Only works with D3Q19!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    static void execute( PdfField_T * src, PdfField_T * dst, const uint_t numberOfGhostLayersToInclude = uint_t(0) );
 };
diff --git a/src/lbm/sweeps/SweepBase.h b/src/lbm/sweeps/SweepBase.h
index d3389cc60e612975851b885922f2dfd91279abb0..f7dbddd12448eedea7a182da302a475a91dd905b 100644
--- a/src/lbm/sweeps/SweepBase.h
+++ b/src/lbm/sweeps/SweepBase.h
@@ -47,7 +47,7 @@ class SweepBase
 {
 public:
 
-   typedef PdfField< LatticeModel_T > PdfField_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
 
    // block has NO dst pdf field
    SweepBase( const BlockDataID & pdfField,
diff --git a/src/lbm/trt/SplitPureSweep.impl.h b/src/lbm/trt/SplitPureSweep.impl.h
index 695cebccf47b618a3d65c2fd4fd6cfea900c5bca..3594863a90d565a7722601bf99699ac760ba358b 100644
--- a/src/lbm/trt/SplitPureSweep.impl.h
+++ b/src/lbm/trt/SplitPureSweep.impl.h
@@ -73,8 +73,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef typename SweepBase<LatticeModel_T>::PdfField_T  PdfField_T;
-   typedef typename LatticeModel_T::Stencil                Stencil;
+   using PdfField_T = typename SweepBase<LatticeModel_T>::PdfField_T;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    // block has NO dst pdf field
    SplitPureSweep( const BlockDataID & pdfField ) :
@@ -98,8 +98,8 @@ void SplitPureSweep< LatticeModel_T, typename std::enable_if< std::is_same< type
                                                                 >::type
    >::operator()( IBlock * const block )                                       
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
 
    this->getFields( block, src, dst );
 
@@ -480,8 +480,8 @@ void SplitPureSweep< LatticeModel_T, typename std::enable_if< std::is_same< type
                                                               >::type
    >::stream( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
 
    this->getFields( block, src, dst );
 
@@ -866,8 +866,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef typename SweepBase<LatticeModel_T>::PdfField_T  PdfField_T;
-   typedef typename LatticeModel_T::Stencil                Stencil;
+   using PdfField_T = typename SweepBase<LatticeModel_T>::PdfField_T;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    // block has NO dst pdf field
    SplitPureSweep( const BlockDataID & pdfField ) :
@@ -891,8 +891,8 @@ void SplitPureSweep< LatticeModel_T, typename std::enable_if< std::is_same< type
                                                               >::type
    >::operator()( IBlock * const block )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
 
    this->getFields( block, src, dst );
 
@@ -1292,8 +1292,8 @@ void SplitPureSweep< LatticeModel_T, typename std::enable_if< std::is_same< type
                                                               >::type
    >::stream( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
 
    this->getFields( block, src, dst );
 
diff --git a/src/lbm/trt/SplitSweep.impl.h b/src/lbm/trt/SplitSweep.impl.h
index fadc10b487a0bd88f627e46fe72eb8f817c8575e..c46db20a7a356dcba14adb78023d99c7bd9a26cd 100644
--- a/src/lbm/trt/SplitSweep.impl.h
+++ b/src/lbm/trt/SplitSweep.impl.h
@@ -68,8 +68,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef typename FlagFieldSweepBase<LatticeModel_T,FlagField_T>::PdfField_T  PdfField_T;
-   typedef typename LatticeModel_T::Stencil                                     Stencil;
+   using PdfField_T = typename FlagFieldSweepBase<LatticeModel_T, FlagField_T>::PdfField_T;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    // block has NO dst pdf field, lbm mask consists of multiple flags
    SplitSweep( const BlockDataID & pdfField, const ConstBlockDataID & flagField, const Set< FlagUID > & lbmMask ) :
@@ -93,9 +93,9 @@ void SplitSweep< LatticeModel_T, FlagField_T, typename std::enable_if< std::is_s
                                                                        >::type
    >::operator()( IBlock * const block )                                               
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
-   const FlagField_T * flagField( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
+   const FlagField_T * flagField( nullptr );
 
    auto lbm = this->getLbmMaskAndFields( block, src, dst, flagField );
 
@@ -545,9 +545,9 @@ void SplitSweep< LatticeModel_T, FlagField_T, typename std::enable_if< std::is_s
                                                                        >::type
    >::stream( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
-   const FlagField_T * flagField( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
+   const FlagField_T * flagField( nullptr );
 
    auto lbm = this->getLbmMaskAndFields( block, src, dst, flagField );
 
@@ -568,8 +568,8 @@ void SplitSweep< LatticeModel_T, FlagField_T, typename std::enable_if< std::is_s
 {
    WALBERLA_ASSERT_EQUAL( numberOfGhostLayersToInclude, uint_t(0) ); // the implementation right now doesn't support inclusion of ghost layers in collide step!
 
-   PdfField_T * src( NULL );
-   const FlagField_T * flagField( NULL );
+   PdfField_T * src( nullptr );
+   const FlagField_T * flagField( nullptr );
 
    auto lbm = this->getLbmMaskAndFields( block, src, flagField );
 
@@ -1005,8 +1005,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef typename FlagFieldSweepBase<LatticeModel_T,FlagField_T>::PdfField_T  PdfField_T;
-   typedef typename LatticeModel_T::Stencil                                     Stencil;
+   using PdfField_T = typename FlagFieldSweepBase<LatticeModel_T, FlagField_T>::PdfField_T;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    // block has NO dst pdf field, lbm mask consists of multiple flags
    SplitSweep( const BlockDataID & pdfField, const ConstBlockDataID & flagField, const Set< FlagUID > & lbmMask ) :
@@ -1030,9 +1030,9 @@ void SplitSweep< LatticeModel_T, FlagField_T, typename std::enable_if< std::is_s
                                                                        >::type
    >::operator()( IBlock * const block )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
-   const FlagField_T * flagField( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
+   const FlagField_T * flagField( nullptr );
 
    auto lbm = this->getLbmMaskAndFields( block, src, dst, flagField );
 
@@ -1501,9 +1501,9 @@ void SplitSweep< LatticeModel_T, FlagField_T, typename std::enable_if< std::is_s
                                                                        >::type
    >::stream( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
-   const FlagField_T * flagField( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
+   const FlagField_T * flagField( nullptr );
 
    auto lbm = this->getLbmMaskAndFields( block, src, dst, flagField );
 
@@ -1524,8 +1524,8 @@ void SplitSweep< LatticeModel_T, FlagField_T, typename std::enable_if< std::is_s
 {
    WALBERLA_ASSERT_EQUAL( numberOfGhostLayersToInclude, uint_t(0) ); // the implementation right now doesn't support inclusion of ghost layers in collide step!
 
-   PdfField_T * src( NULL );
-   const FlagField_T * flagField( NULL );
+   PdfField_T * src( nullptr );
+   const FlagField_T * flagField( nullptr );
 
    auto lbm = this->getLbmMaskAndFields( block, src, flagField );
 
diff --git a/src/lbm/trt/cell_operations/DefaultCellOperation.impl.h b/src/lbm/trt/cell_operations/DefaultCellOperation.impl.h
index 0950f0cca8bcdf68c1775ee1058719f253b6c3e2..165e47f68f5637a1962e3f8d2b7a3ab9533cc42b 100644
--- a/src/lbm/trt/cell_operations/DefaultCellOperation.impl.h
+++ b/src/lbm/trt/cell_operations/DefaultCellOperation.impl.h
@@ -63,8 +63,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    DefaultCellOperation() : lambda_e_( real_t(0) ), lambda_d_( real_t(0) ), latticeModel_( NULL ) {}
 
@@ -179,8 +179,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    DefaultCellOperation() :
       lambda_e_( real_t(0) ), lambda_e_scaled_( real_t(0) ), lambda_d_scaled_( real_t(0) ),
@@ -431,8 +431,8 @@ public:
    static_assert( (std::is_same< typename LatticeModel_T::ForceModel::tag, force_model::None_tag >::value),        "Only works without additional forces!" );
    static_assert( LatticeModel_T::equilibriumAccuracyOrder == 2, "Only works for lattice models that require the equilibrium distribution to be order 2 accurate!" );
 
-   typedef PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    DefaultCellOperation() :
       lambda_e_( real_t(0) ), lambda_e_scaled_( real_t(0) ), lambda_d_scaled_( real_t(0) ),
diff --git a/src/lbm/vtk/Density.h b/src/lbm/vtk/Density.h
index 387086fe68a19346f04f8672cd3197646375c01f..f40bc63599f003a7a13cb2303d256651b336d6dd 100644
--- a/src/lbm/vtk/Density.h
+++ b/src/lbm/vtk/Density.h
@@ -36,16 +36,16 @@ class DensityVTKWriter : public vtk::BlockCellDataWriter< OutputType >
 {
 public:
 
-   typedef PdfField< LatticeModel_T > PdfField_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
 
    DensityVTKWriter( const ConstBlockDataID & pdf, const std::string & id ) :
-      vtk::BlockCellDataWriter< OutputType >( id ), bdid_( pdf ), pdf_( NULL ) {}
+      vtk::BlockCellDataWriter< OutputType >( id ), bdid_( pdf ), pdf_( nullptr ) {}
 
 protected:
 
-   void configure() { WALBERLA_ASSERT_NOT_NULLPTR( this->block_ ); pdf_ = this->block_->template getData< PdfField_T >( bdid_ ); }
+   void configure() override { WALBERLA_ASSERT_NOT_NULLPTR( this->block_ ); pdf_ = this->block_->template getData< PdfField_T >( bdid_ ); }
 
-   OutputType evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t /*f*/ )
+   OutputType evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t /*f*/ ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( pdf_ );
       return numeric_cast< OutputType >( pdf_->getDensity(x,y,z) );
@@ -63,7 +63,7 @@ class DensitySIVTKWriter : public vtk::BlockCellDataWriter< OutputType >
 {
 public:
 
-   typedef PdfField< LatticeModel_T > PdfField_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
 
    DensitySIVTKWriter( const ConstBlockDataID & pdf, const real_t rho_SI, const std::string & id ) :
       vtk::BlockCellDataWriter< OutputType >( id ), bdid_( pdf ), pdf_( NULL ), rho_SI_( rho_SI ) {}
diff --git a/src/lbm/vtk/NonEquilibrium.h b/src/lbm/vtk/NonEquilibrium.h
index c8699c43e794ffab50272495341af54459d93cca..4a756dba9651096f677b12627305b6e7f68b238b 100644
--- a/src/lbm/vtk/NonEquilibrium.h
+++ b/src/lbm/vtk/NonEquilibrium.h
@@ -41,17 +41,17 @@ class NonEqulibriumVTKWriter : public vtk::BlockCellDataWriter< OutputType, Latt
 {
 public:
 
-   typedef PdfField< LatticeModel_T >       PdfField_T;
-   typedef typename LatticeModel_T::Stencil Stencil;
+   using PdfField_T = PdfField<LatticeModel_T>;
+   using Stencil = typename LatticeModel_T::Stencil;
 
    NonEqulibriumVTKWriter( const ConstBlockDataID & pdf, const std::string & id ) :
-      vtk::BlockCellDataWriter< OutputType, Stencil::Size >( id ), bdid_( pdf ), pdf_( NULL ) {}
+      vtk::BlockCellDataWriter< OutputType, Stencil::Size >( id ), bdid_( pdf ), pdf_( nullptr ) {}
 
 protected:
 
-   void configure() { WALBERLA_ASSERT_NOT_NULLPTR( this->block_ ); pdf_ = this->block_->template getData< PdfField_T >( bdid_ ); }
+   void configure() override { WALBERLA_ASSERT_NOT_NULLPTR( this->block_ ); pdf_ = this->block_->template getData< PdfField_T >( bdid_ ); }
 
-   OutputType evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f )
+   OutputType evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( pdf_ );
 
diff --git a/src/lbm/vtk/PressureTensor.h b/src/lbm/vtk/PressureTensor.h
index da09d6664412ea537d72463d651a756e3cd895c0..60c3afe53ce06253e5d689f489f0ddf9d119b2c7 100644
--- a/src/lbm/vtk/PressureTensor.h
+++ b/src/lbm/vtk/PressureTensor.h
@@ -36,7 +36,7 @@ class PressureTensorVTKWriter : public vtk::BlockCellDataWriter< OutputType, 9 >
 {
 public:
 
-   typedef PdfField< LatticeModel_T > PdfField_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
 
    PressureTensorVTKWriter( const ConstBlockDataID & pdfFieldId, const std::string & id ) :
       vtk::BlockCellDataWriter< OutputType, 9 >( id ), bdid_( pdfFieldId ), pdf_( NULL ) {}
diff --git a/src/lbm/vtk/VTKOutput.h b/src/lbm/vtk/VTKOutput.h
index 1c00b11ff3f585e3727e218a80dce2555e2eb738..9e58cf06f49d214d9c50f3d0b9cd55c01d681fd4 100644
--- a/src/lbm/vtk/VTKOutput.h
+++ b/src/lbm/vtk/VTKOutput.h
@@ -79,8 +79,8 @@ class VTKOutput {
 
 public:
 
-   typedef typename FlagFieldT::flag_t flag_t;
-   typedef std::map<FlagUID, flag_t> FlagMap;
+   using flag_t = typename FlagFieldT::flag_t;
+   using FlagMap = std::map<FlagUID, flag_t>;
 
    VTKOutput( const ConstBlockDataID & pdfField, const ConstBlockDataID & flagField,
               const FlagUID & domainFlagUID, const vtk::VTKOutput::BeforeFunction & ghostLayerSyncFunction,
@@ -90,7 +90,7 @@ public:
               const FlagUID & domainFlagUID,
               const FlagMap & flagFieldMapping = FlagMap() );
 
-   virtual ~VTKOutput() {}
+   virtual ~VTKOutput() = default;
 
    virtual void operator()( std::vector< shared_ptr<vtk::BlockCellDataWriterInterface> > & writers,
                             std::map< std::string, vtk::VTKOutput::CellFilter > & filters,
@@ -196,7 +196,7 @@ void VTKOutput<LatticeModel, FlagFieldT>::addToTimeloop( Timeloop & timeloop, co
                                                          const FlagUID & domainFlagUID,
                                                          const FlagMap & flagFieldMapping )
 {
-   typedef PdfField< LatticeModel > PdfField_t;
+   using PdfField_t = PdfField<LatticeModel>;
 
    blockforest::communication::UniformBufferedScheme< stencil::D3Q27 > pdfGhostLayerSync( blocks );
    pdfGhostLayerSync.addPackInfo( make_shared< field::communication::PackInfo< PdfField_t > >( pdfField ) );
diff --git a/src/lbm/vtk/Velocity.h b/src/lbm/vtk/Velocity.h
index 6fb513745b11ca9fb7da05d9e0c0bef8de7deea0..8a31c07d2ec5cb4c73f354e2d27daf11b0ceb9ad 100644
--- a/src/lbm/vtk/Velocity.h
+++ b/src/lbm/vtk/Velocity.h
@@ -36,16 +36,16 @@ class VelocityVTKWriter : public vtk::BlockCellDataWriter< OutputType, 3 >
 {
 public:
 
-   typedef PdfField< LatticeModel_T > PdfField_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
 
    VelocityVTKWriter( const ConstBlockDataID & pdfFieldId, const std::string & id ) :
-      vtk::BlockCellDataWriter< OutputType, 3 >( id ), bdid_( pdfFieldId ), pdf_( NULL ) {}
+      vtk::BlockCellDataWriter< OutputType, 3 >( id ), bdid_( pdfFieldId ), pdf_( nullptr ) {}
 
 protected:
 
-   void configure() { WALBERLA_ASSERT_NOT_NULLPTR( this->block_ ); pdf_ = this->block_->template getData< PdfField_T >( bdid_ ); }
+   void configure() override { WALBERLA_ASSERT_NOT_NULLPTR( this->block_ ); pdf_ = this->block_->template getData< PdfField_T >( bdid_ ); }
 
-   OutputType evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f )
+   OutputType evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( pdf_ );
       return numeric_cast< OutputType >( (pdf_->getVelocity(x,y,z))[ uint_c(f) ] );
@@ -63,16 +63,16 @@ class VelocityMagnitudeVTKWriter : public vtk::BlockCellDataWriter< OutputType,
 {
 public:
 
-   typedef PdfField< LatticeModel_T > PdfField_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
 
    VelocityMagnitudeVTKWriter( const ConstBlockDataID & pdfFieldId, const std::string & id ) :
-      vtk::BlockCellDataWriter< OutputType, 1 >( id ), bdid_( pdfFieldId ), pdf_( NULL ) {}
+      vtk::BlockCellDataWriter< OutputType, 1 >( id ), bdid_( pdfFieldId ), pdf_( nullptr ) {}
 
 protected:
 
-   void configure() { WALBERLA_ASSERT_NOT_NULLPTR( this->block_ ); pdf_ = this->block_->template getData< PdfField_T >( bdid_ ); }
+   void configure() override { WALBERLA_ASSERT_NOT_NULLPTR( this->block_ ); pdf_ = this->block_->template getData< PdfField_T >( bdid_ ); }
 
-   OutputType evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t /*f*/ )
+   OutputType evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t /*f*/ ) override
    {
       WALBERLA_ASSERT_NOT_NULLPTR( pdf_ );
       return numeric_cast< OutputType >( pdf_->getVelocity(x,y,z).length() );
@@ -90,7 +90,7 @@ class VelocitySIVTKWriter : public vtk::BlockCellDataWriter< OutputType, 3 >
 {
 public:
 
-   typedef PdfField< LatticeModel_T > PdfField_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
 
    VelocitySIVTKWriter( const ConstBlockDataID & pdfFieldId, const real_t dx_SI, const real_t dt_SI, const std::string & id ) :
       vtk::BlockCellDataWriter< OutputType, 3 >( id ), bdid_( pdfFieldId ), pdf_( NULL ), dxDividedByDt_SI_( dx_SI / dt_SI ) {}
@@ -118,7 +118,7 @@ class VelocitySIMagnitudeVTKWriter : public vtk::BlockCellDataWriter< OutputType
 {
 public:
 
-   typedef PdfField< LatticeModel_T > PdfField_T;
+   using PdfField_T = PdfField<LatticeModel_T>;
 
    VelocitySIMagnitudeVTKWriter( const ConstBlockDataID & pdfFieldId, const real_t dx_SI, const real_t dt_SI, const std::string & id ) :
       vtk::BlockCellDataWriter< OutputType, 1 >( id ), bdid_( pdfFieldId ), pdf_( NULL ), dxDividedByDt_SI_( dx_SI / dt_SI ) {}
diff --git a/src/lbm_mesapd_coupling/amr/BlockInfo.h b/src/lbm_mesapd_coupling/amr/BlockInfo.h
new file mode 100644
index 0000000000000000000000000000000000000000..f36b491fb7ed60c93c9350fe14ab91e2769282a3
--- /dev/null
+++ b/src/lbm_mesapd_coupling/amr/BlockInfo.h
@@ -0,0 +1,84 @@
+//======================================================================================================================
+//
+//  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 BlockInfo.h
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//! \author Lukas Werner <lks.werner@fau.de>
+//! //
+//======================================================================================================================
+
+#pragma once
+
+#include <core/mpi/RecvBuffer.h>
+#include <core/mpi/SendBuffer.h>
+
+#include <ostream>
+
+namespace walberla {
+namespace lbm_mesapd_coupling {
+namespace amr {
+
+struct BlockInfo {
+   // lbm quantities
+   uint_t numberOfCells;
+   uint_t numberOfFluidCells;
+   uint_t numberOfNearBoundaryCells;
+   // rpd quantities
+   uint_t numberOfLocalParticles;
+   uint_t numberOfGhostParticles;
+   // coupling quantities
+   uint_t numberOfRPDSubCycles;
+
+   BlockInfo()
+         : numberOfCells(0), numberOfFluidCells(0), numberOfNearBoundaryCells(0),
+           numberOfLocalParticles(0), numberOfGhostParticles(0), numberOfRPDSubCycles(0) {}
+
+   BlockInfo(const uint_t numCells, const uint_t numFluidCells, const uint_t numNearBoundaryCells,
+             const uint_t numLocalParticles, const uint_t numGhostParticles, const uint_t numRPDSubCycles)
+         : numberOfCells(numCells), numberOfFluidCells(numFluidCells), numberOfNearBoundaryCells(numNearBoundaryCells),
+           numberOfLocalParticles(numLocalParticles), numberOfGhostParticles(numGhostParticles), numberOfRPDSubCycles(numRPDSubCycles) {}
+};
+
+
+inline
+std::ostream& operator<<( std::ostream& os, const BlockInfo& bi )
+{
+   os << bi.numberOfCells << " / " << bi.numberOfFluidCells << " / " << bi.numberOfNearBoundaryCells << " / "
+      << bi.numberOfLocalParticles << " / "<< bi.numberOfGhostParticles << " / " << bi.numberOfRPDSubCycles;
+   return os;
+}
+
+template< typename T,    // Element type of SendBuffer
+      typename G>    // Growth policy of SendBuffer
+mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, const BlockInfo& info )
+{
+   buf.addDebugMarker( "pca" );
+   buf << info.numberOfCells << info.numberOfFluidCells << info.numberOfNearBoundaryCells
+       << info.numberOfLocalParticles << info.numberOfGhostParticles << info.numberOfRPDSubCycles;
+   return buf;
+}
+
+template< typename T>    // Element type of SendBuffer
+mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, BlockInfo& info )
+{
+   buf.readDebugMarker( "pca" );
+   buf >> info.numberOfCells >> info.numberOfFluidCells >> info.numberOfNearBoundaryCells
+       >> info.numberOfLocalParticles >> info.numberOfGhostParticles >> info.numberOfRPDSubCycles;
+   return buf;
+}
+
+} // namespace amr
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/amr/InfoCollection.h b/src/lbm_mesapd_coupling/amr/InfoCollection.h
new file mode 100644
index 0000000000000000000000000000000000000000..f3e596eb74e2bdf07baa5b3dd4e97da5441d5994
--- /dev/null
+++ b/src/lbm_mesapd_coupling/amr/InfoCollection.h
@@ -0,0 +1,208 @@
+//======================================================================================================================
+//
+//  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 InfoCollection.h
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "BlockInfo.h"
+
+#include "blockforest/BlockForest.h"
+#include "blockforest/BlockID.h"
+#include "core/mpi/BufferSystem.h"
+
+#include "mesa_pd/data/Flags.h"
+
+#include <map>
+
+namespace walberla {
+namespace lbm_mesapd_coupling {
+namespace amr {
+
+using InfoCollection = std::map<blockforest::BlockID, BlockInfo>  ;
+using InfoCollectionPair = std::pair<blockforest::BlockID, BlockInfo>;
+
+template <typename BoundaryHandling_T, typename ParticleAccessor_T>
+void updateAndSyncInfoCollection(BlockForest& bf, const BlockDataID boundaryHandlingID,
+                                 const ParticleAccessor_T& accessor, const uint_t numberOfRPDSubCycles,
+                                 InfoCollection& ic ) {
+   ic.clear();
+
+   mpi::BufferSystem bs( MPIManager::instance()->comm(), 856 );
+
+   for (auto blockIt = bf.begin(); blockIt != bf.end(); ++blockIt)
+   {
+      auto * block = static_cast<blockforest::Block*> (&(*blockIt));
+
+      // evaluate LBM quantities
+      BoundaryHandling_T * boundaryHandling = blockIt->getData< BoundaryHandling_T >( boundaryHandlingID );
+      auto xyzSize = boundaryHandling->getFlagField()->xyzSize();
+      const uint_t numCells = xyzSize.numCells();
+      uint_t numFluidCells(0), numNearBoundaryCells(0);
+      for( auto cellIt = xyzSize.begin(); cellIt != xyzSize.end(); ++cellIt)
+      {
+         if( boundaryHandling->isDomain(*cellIt) )
+         {
+            ++numFluidCells;
+         }
+         if( boundaryHandling->isNearBoundary(*cellIt))
+         {
+            ++numNearBoundaryCells;
+         }
+      }
+
+      // evaluate MESAPD quantities
+      // count block local and (possible) ghost particles here
+      uint_t numLocalParticles = 0, numGhostParticles = 0;
+      for (size_t idx = 0; idx < accessor.size(); ++idx) {
+
+         using namespace walberla::mesa_pd::data::particle_flags;
+         if( isSet(accessor.getFlags(idx), GLOBAL) ) continue;
+
+         if ( block->getAABB().contains(accessor.getPosition(idx)) ) {
+            numLocalParticles++;
+         } else if (block->getAABB().sqDistance(accessor.getPosition(idx)) < accessor.getInteractionRadius(idx)*accessor.getInteractionRadius(idx)) {
+            numGhostParticles++;
+         }
+      }
+
+      BlockInfo blockInfo(numCells, numFluidCells, numNearBoundaryCells, numLocalParticles, numGhostParticles, numberOfRPDSubCycles);
+      InfoCollectionPair infoCollectionEntry(block->getId(), blockInfo);
+
+      ic.insert( infoCollectionEntry );
+
+      for( auto nb = uint_t(0); nb < block->getNeighborhoodSize(); ++nb )
+      {
+         bs.sendBuffer( block->getNeighborProcess(nb) ) << infoCollectionEntry;
+      }
+
+      //note: is it necessary to add child blocks already into the info collection?
+      // here, we still have full geometrical information and can probably determine number of fluid and near boundary cells more easily
+      // however, the interesting (and most costly) blocks are never refined and thus their child infos is never needed
+      // see pe/amr/InfoCollection.cpp for an example
+
+   }
+
+   // size of buffer is unknown and changes with each send
+   bs.setReceiverInfoFromSendBufferState(false, true);
+   bs.sendAll();
+
+   // info collection has to be distirbuted to neighboring processes such that later on when coarsening was applied,
+   // the weight of the coarsened block can be computed
+   for( auto recvIt = bs.begin(); recvIt != bs.end(); ++recvIt )
+   {
+      while( !recvIt.buffer().isEmpty() )
+      {
+         InfoCollectionPair val;
+         recvIt.buffer() >> val;
+         ic.insert(val);
+      }
+   }
+}
+
+void getBlockInfoFromInfoCollection( const PhantomBlock * block, const shared_ptr<InfoCollection>& ic,
+                                     BlockInfo & blockInfo )
+{
+   WALBERLA_ASSERT_NOT_NULLPTR(block);
+
+   if (block->sourceBlockIsLarger())
+   {
+      // block is a result of refinement -> BlockInfo object only available for the father block
+      // there should be no particles on the block (otherwise it would not have been refined)
+      // and refinement in LBM does not change the number of cells
+      // we assume that the number of fluid and near boundary cells also stays the same
+      // (ATTENTION: not true for blocks intersecting with a boundary!)
+      // -> we can use the information of the father block for weight assignment
+
+      auto infoIt = ic->find( block->getId().getFatherId() );
+      WALBERLA_CHECK_UNEQUAL( infoIt, ic->end(), "Father block with ID " << block->getId().getFatherId() << " not found in info collection!" );
+
+      // check the above mentioned assumptions
+      WALBERLA_ASSERT_EQUAL(infoIt->second.numberOfLocalParticles, uint_t(0));
+
+      blockInfo = infoIt->second;
+   }
+   else if (block->sourceBlockHasTheSameSize())
+   {
+      auto infoIt = ic->find( block->getId() );
+      WALBERLA_CHECK_UNEQUAL( infoIt, ic->end(), "Block with ID " << block->getId() << " not found in info collection!" );
+      blockInfo = infoIt->second;
+   }
+   else
+   {
+      // source block of block is smaller
+
+      // block is a result of coarsening -> BlockInfo object is available on all 8 child blocks
+      // there should be no particles on the block (otherwise it would not have been coarsened)
+      // and refinement in LBM does not change the number of cells
+      // we assume that the number of fluid and near boundary cells will be the average of all 8 child blocks
+      // -> we can use the information of the child blocks for weight assignment
+
+      blockforest::BlockID childIdForInit(block->getId(), 0);
+      auto childForInitIt = ic->find( childIdForInit );
+      WALBERLA_CHECK_UNEQUAL( childForInitIt, ic->end(), "Child block with ID " << childIdForInit << " not found in info collection!" );
+      BlockInfo combinedInfo = childForInitIt->second;
+      uint_t numFluidCells(0);
+      uint_t numNearBoundaryCells(0);
+      for (uint_t child = 0; child < 8; ++child)
+      {
+         blockforest::BlockID childId(block->getId(), child);
+         auto childIt = ic->find( childId );
+         WALBERLA_CHECK_UNEQUAL( childIt, ic->end(), "Child block with ID " << childId << " not found in info collection!" );
+         numFluidCells += childIt->second.numberOfFluidCells;
+         numNearBoundaryCells += childIt->second.numberOfNearBoundaryCells;
+
+         // check above mentioned assumptions
+         WALBERLA_ASSERT_EQUAL(childIt->second.numberOfLocalParticles, uint_t(0));
+      }
+      // total number of cells remains unchanged
+      combinedInfo.numberOfFluidCells = uint_c(numFluidCells / uint_t(8)); //average
+      combinedInfo.numberOfNearBoundaryCells = uint_c( numNearBoundaryCells / uint_t(8) ); //average
+      combinedInfo.numberOfLocalParticles = uint_t(0);
+      // number of rpd sub cycles stays the same
+
+      blockInfo = combinedInfo;
+   }
+}
+
+/*
+ * Provides mapping from phantom block to weight evaluation via info collection
+ */
+class WeightEvaluationFunctor
+{
+public:
+   WeightEvaluationFunctor(const shared_ptr<InfoCollection>& ic,
+                           const std::function<real_t(const BlockInfo&)> & weightEvaluationFct) :
+         ic_(ic), weightEvaluationFct_(weightEvaluationFct) {}
+
+   real_t operator()(const PhantomBlock * block)
+   {
+      BlockInfo blockInfo;
+      getBlockInfoFromInfoCollection(block, ic_, blockInfo);
+      return weightEvaluationFct_(blockInfo);
+   }
+
+private:
+   shared_ptr<InfoCollection> ic_;
+   std::function<real_t(const BlockInfo&)> weightEvaluationFct_;
+};
+
+
+} // namepace amr
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/amr/level_determination/ParticlePresenceLevelDetermination.h b/src/lbm_mesapd_coupling/amr/level_determination/ParticlePresenceLevelDetermination.h
new file mode 100644
index 0000000000000000000000000000000000000000..9ad15259e13970eb838ac15207e93b4e05581bd7
--- /dev/null
+++ b/src/lbm_mesapd_coupling/amr/level_determination/ParticlePresenceLevelDetermination.h
@@ -0,0 +1,101 @@
+//======================================================================================================================
+//
+//  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 ParticlePresenceLevelDetermination.h
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "blockforest/BlockForest.h"
+#include "lbm_mesapd_coupling/amr/InfoCollection.h"
+
+namespace walberla {
+namespace lbm_mesapd_coupling {
+
+/*
+ * Class to determine the minimum level a block can be.
+ * For coupled LBM-PE simulations the following rules apply:
+ *  - a moving particle will always remain on the finest block
+ *  - a moving particle is not allowed to extend into an area with a coarser block
+ *  - if no moving particle is present, the level can be as coarse as possible (restricted by the 2:1 rule)
+ * Therefore, if a particle, local or remote (due to particles that are larger than a block), is present on any of the
+ * neighboring blocks of a certain block, this block's target level is the finest level.
+ * This, together with a refinement checking frequency that depends on the maximum translational particle velocity,
+ * ensures the above given requirements.
+ */
+class ParticlePresenceLevelDetermination
+{
+public:
+
+   ParticlePresenceLevelDetermination( const shared_ptr<lbm_mesapd_coupling::InfoCollection> & infoCollection, uint_t finestLevel) :
+         infoCollection_( infoCollection ), finestLevel_( finestLevel)
+   {}
+
+   void operator()( std::vector< std::pair< const Block *, uint_t > > & minTargetLevels,
+                    std::vector< const Block * > &, const BlockForest & /*forest*/ ) {
+      for (auto &minTargetLevel : minTargetLevels) {
+         uint_t currentLevelOfBlock = minTargetLevel.first->getLevel();
+
+         const uint_t numberOfParticlesInDirectNeighborhood = getNumberOfLocalAndShadowParticlesInNeighborhood(minTargetLevel.first);
+
+         uint_t targetLevelOfBlock = currentLevelOfBlock; //keep everything as it is
+         if ( numberOfParticlesInDirectNeighborhood > uint_t(0) )
+         {
+            // set block to finest level if there are particles nearby
+            targetLevelOfBlock = finestLevel_;
+         }
+         else
+         {
+            // block could coarsen since there are no particles nearby
+            if( currentLevelOfBlock > uint_t(0) )
+               targetLevelOfBlock = currentLevelOfBlock - uint_t(1);
+         }
+
+         WALBERLA_CHECK_LESS_EQUAL(std::abs(int_c(targetLevelOfBlock) - int_c(currentLevelOfBlock)), uint_t(1), "Only level difference of maximum 1 allowed!");
+         minTargetLevel.second = targetLevelOfBlock;
+      }
+   }
+
+private:
+
+   uint_t getNumberOfLocalAndShadowParticlesInNeighborhood(const Block * block) {
+      auto numParticles = uint_t(0);
+
+      // add particles of current block
+      const auto infoIt = infoCollection_->find(block->getId());
+      WALBERLA_CHECK_UNEQUAL(infoIt, infoCollection_->end(), "Block with ID " << block->getId() << " not found in info collection!");
+
+      numParticles += infoIt->second.numberOfLocalParticles + infoIt->second.numberOfShadowParticles;
+
+      // add particles of all neighboring blocks
+      for(uint_t i = 0; i < block->getNeighborhoodSize(); ++i)
+      {
+         const BlockID &neighborBlockID = block->getNeighborId(i);
+         const auto infoItNeighbor = infoCollection_->find(neighborBlockID);
+         WALBERLA_CHECK_UNEQUAL(infoItNeighbor, infoCollection_->end(), "Neighbor block with ID " << neighborBlockID << " not found in info collection!");
+
+         numParticles += infoItNeighbor->second.numberOfLocalParticles + infoItNeighbor->second.numberOfShadowParticles;
+      }
+      return numParticles;
+   }
+
+   shared_ptr<lbm_mesapd_coupling::InfoCollection> infoCollection_;
+   uint_t finestLevel_;
+};
+
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/amr/weight_assignment/MetisAssignmentFunctor.cpp b/src/lbm_mesapd_coupling/amr/weight_assignment/MetisAssignmentFunctor.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5c33cb777237d24594f627892cd5d74467637e5a
--- /dev/null
+++ b/src/lbm_mesapd_coupling/amr/weight_assignment/MetisAssignmentFunctor.cpp
@@ -0,0 +1,97 @@
+//======================================================================================================================
+//
+//  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 MetisAssignmentFunctor.cpp
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#include "MetisAssignmentFunctor.h"
+
+namespace walberla {
+namespace lbm_mesapd_coupling {
+namespace amr {
+
+void MetisAssignmentFunctor::operator()( std::vector< std::pair< const PhantomBlock *, walberla::any > > & blockData,
+      const PhantomBlockForest & /*phantomBlockForest*/)
+{
+   for (auto &it : blockData) {
+      const PhantomBlock * block = it.first;
+      //only change of one level is supported!
+      WALBERLA_ASSERT_LESS( std::abs(int_c(block->getLevel()) - int_c(block->getSourceLevel())), 2 );
+
+      std::vector<int64_t> metisVertexWeights(weightEvaluationFctVector_.size());
+
+      for( auto con = uint_t(0); con < weightEvaluationFctVector_.size(); ++con )
+      {
+         real_t vertexWeight = std::max(weightEvaluationFctVector_[con](block), blockBaseWeight_);
+
+         int64_t metisVertexWeight = int64_c( weightMultiplicator_ * vertexWeight );
+
+         WALBERLA_ASSERT_GREATER(metisVertexWeight, int64_t(0));
+         metisVertexWeights[con] = metisVertexWeight;
+      }
+
+      blockforest::DynamicParMetisBlockInfo info( metisVertexWeights );
+
+      info.setVertexCoords(it.first->getAABB().center() );
+
+      real_t blockVolume = it.first->getAABB().volume();
+      real_t approximateEdgeLength = std::cbrt( blockVolume );
+
+      int64_t faceNeighborWeight = int64_c(approximateEdgeLength * approximateEdgeLength ); //common face
+      int64_t edgeNeighborWeight = int64_c( approximateEdgeLength ); //common edge
+      int64_t cornerNeighborWeight = int64_c( 1 ); //common corner
+
+      int64_t vertexSize = int64_c(blockVolume);
+      WALBERLA_ASSERT_GREATER(vertexSize, int64_t(0));
+      info.setVertexSize( vertexSize );
+
+      for( const uint_t idx : blockforest::getFaceNeighborhoodSectionIndices() )
+      {
+         for( auto nb = uint_t(0); nb < it.first->getNeighborhoodSectionSize(idx); ++nb )
+         {
+            auto neighborBlockID = it.first->getNeighborId(idx,nb);
+            info.setEdgeWeight(neighborBlockID, faceNeighborWeight );
+         }
+      }
+
+      for( const uint_t idx : blockforest::getEdgeNeighborhoodSectionIndices() )
+      {
+         for( auto nb = uint_t(0); nb < it.first->getNeighborhoodSectionSize(idx); ++nb )
+         {
+            auto neighborBlockID = it.first->getNeighborId(idx,nb);
+            info.setEdgeWeight(neighborBlockID, edgeNeighborWeight );
+         }
+      }
+
+      for( const uint_t idx : blockforest::getCornerNeighborhoodSectionIndices() )
+      {
+         for( auto nb = uint_t(0); nb < it.first->getNeighborhoodSectionSize(idx); ++nb )
+         {
+            auto neighborBlockID = it.first->getNeighborId(idx,nb);
+            info.setEdgeWeight(neighborBlockID, cornerNeighborWeight );
+         }
+      }
+
+      it.second = info;
+
+   }
+}
+
+
+} // namespace amr
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/lbm_mesapd_coupling/amr/weight_assignment/MetisAssignmentFunctor.h b/src/lbm_mesapd_coupling/amr/weight_assignment/MetisAssignmentFunctor.h
new file mode 100644
index 0000000000000000000000000000000000000000..962a6c975cbbe7ab0aa2fa3c569cae798dc90054
--- /dev/null
+++ b/src/lbm_mesapd_coupling/amr/weight_assignment/MetisAssignmentFunctor.h
@@ -0,0 +1,66 @@
+//======================================================================================================================
+//
+//  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 MetisAssignmentFunctor.h
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "blockforest/PhantomBlockForest.h"
+#include "blockforest/PhantomBlock.h"
+#include "blockforest/loadbalancing/DynamicParMetis.h"
+
+#include <functional>
+#include <vector>
+
+namespace walberla {
+namespace lbm_mesapd_coupling {
+namespace amr {
+
+class MetisAssignmentFunctor
+{
+public:
+
+   using WeightEvaluationFct = std::function<real_t(const PhantomBlock *)>;
+
+   explicit MetisAssignmentFunctor( const WeightEvaluationFct& weightEvaluationFct)
+         : weightEvaluationFctVector_(1, weightEvaluationFct) {}
+
+   explicit MetisAssignmentFunctor( const std::vector< WeightEvaluationFct > & weightEvaluationFctVector )
+         : weightEvaluationFctVector_(weightEvaluationFctVector) {}
+
+   void operator()( std::vector< std::pair< const PhantomBlock *, walberla::any > > & blockData, const PhantomBlockForest & phantomBlockForest);
+
+   uint_t getNumberOfConstrains() const { return weightEvaluationFctVector_.size(); }
+
+   inline void   setBlockBaseWeight( const real_t blockBaseWeight ){ blockBaseWeight_ = blockBaseWeight; }
+   inline real_t getBlockBaseWeight() const { return blockBaseWeight_; }
+
+   inline void setWeightMultiplicator( const real_t weightMultiplicator ){ weightMultiplicator_ = weightMultiplicator; }
+
+private:
+   std::vector< WeightEvaluationFct > weightEvaluationFctVector_;
+   real_t blockBaseWeight_ = real_t(1);
+   real_t weightMultiplicator_ = real_t(1);
+};
+
+
+
+} // namespace amr
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
+
diff --git a/src/lbm_mesapd_coupling/amr/weight_assignment/WeightAssignmentFunctor.h b/src/lbm_mesapd_coupling/amr/weight_assignment/WeightAssignmentFunctor.h
new file mode 100644
index 0000000000000000000000000000000000000000..0113d045a1d7a6b305f134d4045b9c49fd85457f
--- /dev/null
+++ b/src/lbm_mesapd_coupling/amr/weight_assignment/WeightAssignmentFunctor.h
@@ -0,0 +1,67 @@
+//======================================================================================================================
+//
+//  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 WeightAssignmentFunctor.h
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "blockforest/loadbalancing/PODPhantomData.h"
+#include "blockforest/PhantomBlockForest.h"
+#include "blockforest/PhantomBlock.h"
+
+#include <functional>
+
+namespace walberla {
+namespace lbm_mesapd_coupling  {
+namespace amr {
+
+
+
+class WeightAssignmentFunctor
+{
+public:
+   using PhantomBlockWeight = walberla::blockforest::PODPhantomWeight<double>;
+   using WeightEvaluationFct = std::function<real_t(const PhantomBlock *)>;
+
+   explicit WeightAssignmentFunctor( const WeightEvaluationFct & weightEvaluationFct ) :
+         weightEvaluationFct_(weightEvaluationFct) {}
+
+   void operator()( std::vector< std::pair< const PhantomBlock *, walberla::any > > & blockData, const PhantomBlockForest & )
+   {
+      for (auto &it : blockData) {
+         const PhantomBlock * block = it.first;
+         //only change of one level is supported!
+         WALBERLA_ASSERT_LESS( std::abs(int_c(block->getLevel()) - int_c(block->getSourceLevel())), 2 );
+
+         real_t blockWeight = std::max(weightEvaluationFct_(block), blockBaseWeight_);
+         it.second = PhantomBlockWeight( double_c( blockWeight ) );
+      }
+   }
+
+   inline void   setBlockBaseWeight( const real_t blockBaseWeight ) { blockBaseWeight_ = blockBaseWeight; }
+   inline real_t getBlockBaseWeight() const { return blockBaseWeight_; }
+
+private:
+   WeightEvaluationFct weightEvaluationFct_;
+   real_t blockBaseWeight_ = real_t(1);
+};
+
+
+} // namespace amr
+} // namespace lbm_mesapd_coupling
+} // namespace walberla
diff --git a/src/boundary/python/Exports.h b/src/lbm_mesapd_coupling/amr/weight_assignment/WeightEvaluationFunctions.h
similarity index 74%
rename from src/boundary/python/Exports.h
rename to src/lbm_mesapd_coupling/amr/weight_assignment/WeightEvaluationFunctions.h
index 2425f47889948c15c331ce815eea8ceca29f9806..6219f4f43f6c2212231b21afe68ce03c4251abc5 100644
--- a/src/boundary/python/Exports.h
+++ b/src/lbm_mesapd_coupling/amr/weight_assignment/WeightEvaluationFunctions.h
@@ -13,27 +13,24 @@
 //  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 PythonExports.h
-//! \ingroup lbm
-//! \author Martin Bauer <martin.bauer@fau.de>
+//! \file WeightEvaluationFunctions.h
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
 //======================================================================================================================
 
 #pragma once
 
-#include "waLBerlaDefinitions.h"
-#ifdef WALBERLA_BUILD_WITH_PYTHON
+#include "blockforest/PhantomBlock.h"
 
 namespace walberla {
-namespace lbm {
+namespace lbm_mesapd_coupling  {
+namespace amr {
 
-   template<typename BoundaryHandlings>
-   void exportModuleToPython();
+real_t defaultWeightEvaluationFunction(const PhantomBlock * /*block*/)
+{
+   return real_t(1);
+}
 
-} // namespace lbm
+} // namespace amr
+} // namespace lbm_mesapd_coupling
 } // namespace walberla
-
-
-#include "Exports.impl.h"
-
-#endif // WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/lbm_mesapd_coupling/momentum_exchange_method/MovingParticleMapping.h b/src/lbm_mesapd_coupling/momentum_exchange_method/MovingParticleMapping.h
index 29c161d4f0378d60e416e4dac2a3e887d2185ccd..88d3112369193368602fec3c9e9506bad89a2b7f 100644
--- a/src/lbm_mesapd_coupling/momentum_exchange_method/MovingParticleMapping.h
+++ b/src/lbm_mesapd_coupling/momentum_exchange_method/MovingParticleMapping.h
@@ -165,9 +165,9 @@ private:
                      }
 
                   }
-
-                  // cell is already an obstacle (maybe from another particle)
-                  if( isFlagSet( cellFlagPtr, obstacle ) ) {
+                  else if( isFlagSet( cellFlagPtr, obstacle ) )
+                  {
+                     // cell is already an obstacle (maybe from another particle)
                      auto formerParticleUid = (*particleField)(x, y, z);
                      auto formerParticleIdx = ac_->uidToIdx(formerParticleUid);
                      if(!isSet(ac_->getFlags(formerParticleIdx), mesa_pd::data::particle_flags::FIXED) )
@@ -179,10 +179,9 @@ private:
                         (*particleField)(x, y, z) = particleUid;
                      }
                   }
-
-                  // cell is a former obstacle cell (maybe from another particle that has moved away)
-                  if( isFlagSet( cellFlagPtr, formerObstacle ) )
+                  else if( isFlagSet( cellFlagPtr, formerObstacle ) )
                   {
+                     // cell is a former obstacle cell (maybe from another particle that has moved away)
                      boundaryHandling->setBoundary( obstacle, x, y, z );
                      removeFlag( cellFlagPtr, formerObstacle );
                      (*particleField)(x, y, z) = particleUid;
@@ -333,8 +332,12 @@ private:
             {
                if( singleCast(particleIdx, ac, containsPointFctr, ac, Vector3<real_t>(cx,cy,cz)) )
                {
-                  boundaryHandling->forceBoundary(obstacleFlag, x, y, z);
-                  (*particleField)(x,y,z) = ac.getUid(particleIdx);
+                  if(boundaryHandling->isDomain(x,y,z))
+                  {
+                     boundaryHandling->forceBoundary(obstacleFlag, x, y, z);
+                     (*particleField)(x,y,z) = ac.getUid(particleIdx);
+                  }
+                  // no else -> will not overwrite pre-existing boundary cells, e.g. of previously applied applied mapping (e.g. when first mapping global particles)
                }
                cx += dx;
             }
diff --git a/src/lbm_mesapd_coupling/utility/InspectionProbe.h b/src/lbm_mesapd_coupling/utility/InspectionProbe.h
index 4513cdfaf616982c2d11aa1d5253ce2d49459eab..b2644017f7d0d373ffc61352e404ffa20be119f8 100644
--- a/src/lbm_mesapd_coupling/utility/InspectionProbe.h
+++ b/src/lbm_mesapd_coupling/utility/InspectionProbe.h
@@ -74,6 +74,11 @@ public:
       }
    }
 
+   void setPosition(Vector3<real_t> probeLocation)
+   {
+      probeLocation_ = probeLocation;
+   }
+
 private:
 
    void printStatusOfCellSurrounding(Cell centerCell, const IBlock& block)
@@ -121,7 +126,7 @@ private:
 
    void printToScreen(bool isFluid, real_t rho, Vector3<real_t> velocity)
    {
-      if(printToScreen_) WALBERLA_LOG_INFO("Values in probe at position " << probeLocation_ << ": " << (isFluid?"F":"P") << ", rho = " << rho << ", velocity = " << velocity);
+      if(printToScreen_) WALBERLA_LOG_INFO("Values in probe at position " << probeLocation_ << " on rank " << mpi::MPIManager::instance()->rank() << ": " << (isFluid?"F":"P") << ", rho = " << rho << ", velocity = " << velocity);
    }
 
    void writeToFile(bool isFluid, real_t rho, Vector3<real_t> velocity)
diff --git a/src/lbm_mesapd_coupling/utility/ParticleSelector.h b/src/lbm_mesapd_coupling/utility/ParticleSelector.h
index 9baf0a4a1bb36d3938a6fcaefeeb0b10f480a1dd..a18a91af44075a41384b513093b6062a180bac92 100644
--- a/src/lbm_mesapd_coupling/utility/ParticleSelector.h
+++ b/src/lbm_mesapd_coupling/utility/ParticleSelector.h
@@ -24,6 +24,7 @@
 #include "mesa_pd/data/Flags.h"
 #include "mesa_pd/data/IAccessor.h"
 #include "mesa_pd/data/shape/Sphere.h"
+#include "mesa_pd/kernel/ParticleSelector.h"
 
 namespace walberla {
 namespace lbm_mesapd_coupling {
@@ -76,8 +77,9 @@ struct StokesNumberBasedSphereSelector
       static_assert(std::is_base_of<mesa_pd::data::IAccessor, ParticleAccessor_T>::value, "Provide a valid accessor as template");
 
       lbm_mesapd_coupling::RegularParticlesSelector regularParticlesSelector;
+      mesa_pd::kernel::SelectLocal localParticlesSelector;
 
-      if(regularParticlesSelector(particleIdx, ac))
+      if(regularParticlesSelector(particleIdx, ac) && localParticlesSelector(particleIdx, ac))
       {
          // check for non-fixed spheres
          if( ac.getShape(particleIdx)->getShapeType() == mesa_pd::data::Sphere::SHAPE_TYPE )
diff --git a/src/lbm_mesapd_coupling/utility/SubCyclingManager.cpp b/src/lbm_mesapd_coupling/utility/SubCyclingManager.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c9e09bed462bf18bb5e7db5bbe96491221cfbf45
--- /dev/null
+++ b/src/lbm_mesapd_coupling/utility/SubCyclingManager.cpp
@@ -0,0 +1,95 @@
+//======================================================================================================================
+//
+//  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 SubCyclingManager.cpp
+//! \author Lukas Werner <lks.werner@fau.de>
+//
+//======================================================================================================================
+
+
+#include "SubCyclingManager.h"
+
+#include <utility>
+
+namespace walberla {
+namespace lbm_mesapd_coupling {
+
+SubCyclingManager::SubCyclingManager(size_t numberOfSubCycles, shared_ptr<WcTimingPool> timingPool)
+   : numberOfSubCycles_(numberOfSubCycles), timingPool_(std::move(timingPool)), currentTimeStep_(0) {}
+
+void SubCyclingManager::operator()() {
+   executeBeforeFunctions();
+   for (size_t s = 0; s < numberOfSubCycles_; ++s) {
+      executeDuringFunctions();
+   }
+   executeAfterFunctions();
+   ++currentTimeStep_;
+}
+
+SubCyclingManager::FuncHandle
+SubCyclingManager::addFuncBeforeSubCycles(const VoidVoidFunc &f, const std::string &identifier) {
+   IdentifiedFunc idFunc(identifier, f);
+   beforeFunctions_.emplace_back(idFunc);
+   return beforeFunctions_.size() - 1;
+}
+
+SubCyclingManager::FuncHandle
+SubCyclingManager::addFuncDuringSubCycles(const VoidVoidFunc &f, const std::string &identifier) {
+   IdentifiedFunc idFunc(identifier, f);
+   duringFunctions_.emplace_back(idFunc);
+   return duringFunctions_.size() - 1;
+}
+
+SubCyclingManager::FuncHandle
+SubCyclingManager::addFuncAfterSubCycles(const VoidVoidFunc &f, const std::string &identifier) {
+   IdentifiedFunc idFunc(identifier, f);
+   afterFunctions_.emplace_back(idFunc);
+   return afterFunctions_.size() - 1;
+}
+
+inline void SubCyclingManager::executeBeforeFunctions() {
+   executeFunctions(beforeFunctions_);
+}
+
+inline void SubCyclingManager::executeDuringFunctions() {
+   executeFunctions(duringFunctions_);
+}
+
+inline void SubCyclingManager::executeAfterFunctions() {
+   executeFunctions(afterFunctions_);
+}
+
+inline void SubCyclingManager::executeFunctions(std::vector<IdentifiedFunc>& functions) {
+   for (const auto &f: functions) {
+      startTiming(f.first);
+      f.second();
+      stopTiming(f.first);
+   }
+}
+
+inline void SubCyclingManager::startTiming(const std::string & name){
+   if (timingPool_ != nullptr) {
+      (*timingPool_)[name].start();
+   }
+}
+
+inline void SubCyclingManager::stopTiming(const std::string & name){
+   if (timingPool_ != nullptr) {
+      (*timingPool_)[name].end();
+   }
+}
+
+}
+}
\ No newline at end of file
diff --git a/src/lbm_mesapd_coupling/utility/SubCyclingManager.h b/src/lbm_mesapd_coupling/utility/SubCyclingManager.h
new file mode 100644
index 0000000000000000000000000000000000000000..b46ed444b60757f36628ea1f996f9d3fb7a1517c
--- /dev/null
+++ b/src/lbm_mesapd_coupling/utility/SubCyclingManager.h
@@ -0,0 +1,88 @@
+//======================================================================================================================
+//
+//  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 SubCyclingManager.h
+//! \author Lukas Werner <lks.werner@fau.de>
+//
+//======================================================================================================================
+
+#include "core/DataTypes.h"
+#include "core/logging/Logging.h"
+#include "core/timing/TimingPool.h"
+
+namespace walberla {
+namespace lbm_mesapd_coupling {
+
+/*! \brief Handles the execution of subcycles in a timeloop to allow for finer grained time steps than the LBM ones.
+*
+* Supports registration of functions that are run before, during or after a subcycle.
+* The SubCyclingManager itself has to be added to a parent (LBM) time loop, and will execute the functions registered
+* for execution during the subcycling procedure numberOfSubCycles times.
+*/
+
+class SubCyclingManager {
+public:
+   using VoidVoidFunc = std::function<void ()>;
+
+   /*! \name Construction & Destruction */
+   //@{
+   explicit SubCyclingManager(size_t numberOfSubCycles, shared_ptr<WcTimingPool> timingPool = nullptr);
+
+   virtual ~SubCyclingManager() = default;
+   //@}
+
+   /*! \name Registration Functions */
+   //@{
+   using FuncHandle = size_t;
+
+   FuncHandle addFuncBeforeSubCycles(const VoidVoidFunc &f, const std::string &identifier = "Other");
+   FuncHandle addFuncDuringSubCycles(const VoidVoidFunc &f, const std::string &identifier = "Other");
+   FuncHandle addFuncAfterSubCycles(const VoidVoidFunc &f, const std::string &identifier = "Other");
+   //@}
+
+   /*! \name Execution Functions */
+   //@{
+   void operator()();
+   //@}
+
+   /*! \name Bookkeeping Functions */
+   //@{
+   uint_t getCurrentTimeStep() const        { return currentTimeStep_;   }
+   void setCurrentTimeStep(uint_t timestep) { currentTimeStep_ = timestep; }
+   //@}
+
+private:
+   using IdentifiedFunc = std::pair<std::string, VoidVoidFunc>;
+
+   inline void executeBeforeFunctions();
+   inline void executeDuringFunctions();
+   inline void executeAfterFunctions();
+
+   inline void executeFunctions(std::vector<IdentifiedFunc>& functions);
+
+   inline void startTiming(const std::string & name);
+   inline void stopTiming(const std::string & name);
+
+   size_t numberOfSubCycles_;
+   shared_ptr<WcTimingPool> timingPool_;
+   uint_t currentTimeStep_;
+
+   std::vector<IdentifiedFunc> beforeFunctions_;
+   std::vector<IdentifiedFunc> duringFunctions_;
+   std::vector<IdentifiedFunc> afterFunctions_;
+};
+
+}
+}
\ No newline at end of file
diff --git a/src/mesa_pd/collision_detection/AnalyticCollisionFunctions.h b/src/mesa_pd/collision_detection/AnalyticCollisionFunctions.h
index bcf3baf63b2d70d05a9992929cd3f52b4cac67af..b09f646ed3201990543ace423cadfd1a650b341c 100644
--- a/src/mesa_pd/collision_detection/AnalyticCollisionFunctions.h
+++ b/src/mesa_pd/collision_detection/AnalyticCollisionFunctions.h
@@ -13,7 +13,7 @@
 //  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 AnalyticCollisionFunctions.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/collision_detection/AnalyticContactDetection.h b/src/mesa_pd/collision_detection/AnalyticContactDetection.h
index 284cd1eb9470b155d8ae7bedd034070f09319182..36941efc1545ed8e58b75144c6af5d66de5348dc 100644
--- a/src/mesa_pd/collision_detection/AnalyticContactDetection.h
+++ b/src/mesa_pd/collision_detection/AnalyticContactDetection.h
@@ -13,7 +13,7 @@
 //  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 AnalyticContactDetection.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -46,7 +46,7 @@ namespace collision_detection {
 class AnalyticContactDetection
 {
 public:
-   virtual ~AnalyticContactDetection() {}
+   virtual ~AnalyticContactDetection() = default;
 
    size_t& getIdx1() {return idx1_;}
    size_t& getIdx2() {return idx2_;}
diff --git a/src/mesa_pd/collision_detection/BroadPhase.h b/src/mesa_pd/collision_detection/BroadPhase.h
index 4bad17b24d100f93c28e79b0934cb7e4db090ef2..f98658836b48704e1956aaf9aacfd9abbd7e9dcd 100644
--- a/src/mesa_pd/collision_detection/BroadPhase.h
+++ b/src/mesa_pd/collision_detection/BroadPhase.h
@@ -13,7 +13,7 @@
 //  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 BroadPhase.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -38,8 +38,7 @@ bool isInInteractionDistance(const size_t idx1, const size_t idx2, Accessor& ac)
    if (isSet(ac.getFlags(idx2), INFINITE)) return true;
    auto separationDist = ac.getInteractionRadius(idx1) + ac.getInteractionRadius(idx2);
    auto realDist2 = (ac.getPosition(idx1) - ac.getPosition(idx2)).sqrLength();
-   if (realDist2 < separationDist*separationDist) return true;
-   return false;
+   return realDist2 < separationDist*separationDist;
 }
 
 } //namespace collision_detection
diff --git a/src/mesa_pd/collision_detection/EPA.cpp b/src/mesa_pd/collision_detection/EPA.cpp
index 3fb4ced431d587865cd0e5a454979823c2d31988..defefd3a9dbeaac773a069e71a23744b465b1752 100644
--- a/src/mesa_pd/collision_detection/EPA.cpp
+++ b/src/mesa_pd/collision_detection/EPA.cpp
@@ -13,7 +13,7 @@
 //  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 EPA.cpp
+//! \file
 //! \author Tobias Scharpff
 //! \author Tobias Leemann
 //
@@ -362,11 +362,7 @@ bool EPA::doEPA( Support &geom1,
                      penetrationDepth = -supp_dist + real_t(2.0) * margin;
                      retNormal = -ctr;
                      
-                     if(penetrationDepth < contactThreshold){
-                        return true;
-                     }else{
-                        return false;
-                     }
+                     return penetrationDepth < contactThreshold;
                   } else {
                      //Reject sphere
                      removeSupportMargin(epaVolume, supportA, supportB);
@@ -500,12 +496,7 @@ bool EPA::doEPA( Support &geom1,
    std::cerr << "entryHeap[0]->getSqrDist()=" << entryHeap[0]->getSqrDist() << std::endl;*/
    //std::cout << "EPA penetration depth: " << penetrationDepth <<  std::endl;
 
-   if(penetrationDepth < contactThreshold) {
-      return true;
-   }
-
-   //no intersection found!
-   return false;
+   return penetrationDepth < contactThreshold;
 }
 //*************************************************************************************************
 
@@ -722,11 +713,7 @@ inline bool EPA::originInTetrahedron( const Vec3& p0, const Vec3& p1, const Vec3
       return false;
    }
    Vec3 normal3T = (p0 -p3) % (p1-p3);
-   if( (normal3T*p3 > real_t(0.0)) == (normal3T*p2 > real_t(0.0)) ) {
-      return false;
-   }
-
-   return true;
+   return (normal3T*p3 > real_t(0.0)) != (normal3T*p2 > real_t(0.0));
 }
 //*************************************************************************************************
 
diff --git a/src/mesa_pd/collision_detection/EPA.h b/src/mesa_pd/collision_detection/EPA.h
index 46d59a08ad592056d3e93246a0ba435bf57c3ca0..052f6cdf89d71733580f4fc013630b2f76a07e04 100644
--- a/src/mesa_pd/collision_detection/EPA.h
+++ b/src/mesa_pd/collision_detection/EPA.h
@@ -13,7 +13,7 @@
 //  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 EPA.h
+//! \file
 //! \author Tobias Scharpff
 //! \author Tobias Leemann
 //
@@ -48,9 +48,9 @@ private :
    class EPA_Triangle;
    class EPA_TriangleComp;
 
-   typedef std::vector<EPA_Triangle>  EPA_EntryBuffer;
-   typedef std::vector<EPA_Triangle*> EPA_EntryHeap;
-   typedef std::vector<EPA_Edge>      EPA_EdgeBuffer;
+   using EPA_EntryBuffer = std::vector<EPA_Triangle>;
+   using EPA_EntryHeap = std::vector<EPA_Triangle *>;
+   using EPA_EdgeBuffer = std::vector<EPA_Edge>;
    //**********************************************************************************************
 
 public:
diff --git a/src/mesa_pd/collision_detection/GJK.cpp b/src/mesa_pd/collision_detection/GJK.cpp
index d111caccdd28eeb7070607df748059c824a78b6f..002f3f78826cd63eaaa3113bec3d25f6389bb3cc 100644
--- a/src/mesa_pd/collision_detection/GJK.cpp
+++ b/src/mesa_pd/collision_detection/GJK.cpp
@@ -13,7 +13,7 @@
 //  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 GJK.cpp
+//! \file
 //! \author Tobias Scharpff
 //! \author Tobias Leemann
 //
diff --git a/src/mesa_pd/collision_detection/GJK.h b/src/mesa_pd/collision_detection/GJK.h
index 47b4247319d95229fbce258ab570092034524272..7ccff51287b6b184934d93bddc07d9ee8b0ff25b 100644
--- a/src/mesa_pd/collision_detection/GJK.h
+++ b/src/mesa_pd/collision_detection/GJK.h
@@ -13,7 +13,7 @@
 //  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 GJK.h
+//! \file
 //! \author Tobias Scharpff
 //! \author Tobias Leemann
 //
diff --git a/src/mesa_pd/collision_detection/GeneralContactDetection.h b/src/mesa_pd/collision_detection/GeneralContactDetection.h
index bf6d3fad69ca37ecd89bc56ed296a4ef66184a2b..9564916c02a97432793a09f5805bf9f9072f73ac 100644
--- a/src/mesa_pd/collision_detection/GeneralContactDetection.h
+++ b/src/mesa_pd/collision_detection/GeneralContactDetection.h
@@ -13,7 +13,7 @@
 //  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 GeneralContactDetection.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -199,13 +199,7 @@ bool GeneralContactDetection::collideGJKEPA(Support& geom0, Support& geom1)
    {
       EPA epa;
       epa.useSphereOptimization(false);
-      if (epa.doEPAmargin(geom0, geom1, gjk, contactNormal_, contactPoint_, penetrationDepth_, margin))
-      {
-         return true;
-      } else
-      {
-         return false;
-      }
+      return epa.doEPAmargin(geom0, geom1, gjk, contactNormal_, contactPoint_, penetrationDepth_, margin);
    } else
    {
       return false;
diff --git a/src/mesa_pd/collision_detection/Support.h b/src/mesa_pd/collision_detection/Support.h
index 10e64af5e770d067ca496471fa39580eb29269d2..e7ffebfd93de7987019241b0f67eadd9bd99837e 100644
--- a/src/mesa_pd/collision_detection/Support.h
+++ b/src/mesa_pd/collision_detection/Support.h
@@ -13,7 +13,7 @@
 //  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 Support.h
+//! \file
 //! \author Tobias Scharpff
 //! \author Tobias Leemann
 //
diff --git a/src/mesa_pd/common/AABBConversion.h b/src/mesa_pd/common/AABBConversion.h
index ade72f356b3039e5e32b5d47572d0f6cb4438179..33896f25f731c381b866adbde962f51bf490682d 100644
--- a/src/mesa_pd/common/AABBConversion.h
+++ b/src/mesa_pd/common/AABBConversion.h
@@ -13,7 +13,7 @@
 //  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 AABBConversion.h
+//! \file
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/common/Contains.h b/src/mesa_pd/common/Contains.h
index 5bd17b840416dacb37ce5fdda54102a7e88ab331..2b13cd351a59c5d4b58605b7e69380dfec657832 100644
--- a/src/mesa_pd/common/Contains.h
+++ b/src/mesa_pd/common/Contains.h
@@ -13,7 +13,7 @@
 //  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 Contains.h
+//! \file
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/common/ParticleFunctions.h b/src/mesa_pd/common/ParticleFunctions.h
index b546b36a7347e8e655062986415533158bf76105..bb1c89880afbce2932545b36709808b01db619b8 100644
--- a/src/mesa_pd/common/ParticleFunctions.h
+++ b/src/mesa_pd/common/ParticleFunctions.h
@@ -13,7 +13,7 @@
 //  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 ParticleFunctions.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/common/RayParticleIntersection.h b/src/mesa_pd/common/RayParticleIntersection.h
index 097fb384822061a0484292d4e67e07fb2aa45421..d1c9ce912dd325b931ecfa5e52021e0fe4ccbdc1 100644
--- a/src/mesa_pd/common/RayParticleIntersection.h
+++ b/src/mesa_pd/common/RayParticleIntersection.h
@@ -13,7 +13,7 @@
 //  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 RayParticleIntersection.h
+//! \file
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/ContactAccessor.h b/src/mesa_pd/data/ContactAccessor.h
index 470eb0b7c72670d141e58325b2a0106d8c097a64..a33d4e804c55b7544ded7b86a01c6352c12d8cef 100644
--- a/src/mesa_pd/data/ContactAccessor.h
+++ b/src/mesa_pd/data/ContactAccessor.h
@@ -13,7 +13,7 @@
 //  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 ContactAccessor.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -47,7 +47,7 @@ class ContactAccessor : public IContactAccessor
 {
 public:
    ContactAccessor(const std::shared_ptr<data::ContactStorage>& ps) : ps_(ps) {}
-   virtual ~ContactAccessor() = default;
+   ~ContactAccessor() override = default;
    const walberla::id_t& getUid(const size_t p_idx) const {return ps_->getUid(p_idx);}
    walberla::id_t& getUidRef(const size_t p_idx) {return ps_->getUidRef(p_idx);}
    void setUid(const size_t p_idx, const walberla::id_t& v) { ps_->setUid(p_idx, v);}
@@ -156,7 +156,7 @@ inline size_t ContactAccessor::find(const id_t& uid)
 class SingleContactAccessor : public IAccessor
 {
 public:
-   virtual ~SingleContactAccessor() = default;
+   ~SingleContactAccessor() override = default;
    const walberla::id_t& getUid(const size_t /*p_idx*/) const {return uid_;}
    void setUid(const size_t /*p_idx*/, const walberla::id_t& v) { uid_ = v;}
    walberla::id_t& getUidRef(const size_t /*p_idx*/) {return uid_;}
diff --git a/src/mesa_pd/data/ContactHistory.h b/src/mesa_pd/data/ContactHistory.h
index 2e8712209f3d0240588aed2241c82aaaa98ee6e1..48bc77b53a7594d61c193e791a8c49d2b6978c11 100644
--- a/src/mesa_pd/data/ContactHistory.h
+++ b/src/mesa_pd/data/ContactHistory.h
@@ -13,7 +13,7 @@
 //  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 ContactHistory.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/ContactStorage.h b/src/mesa_pd/data/ContactStorage.h
index 0de025bac7d673b400a0fc64d74a5d5cb724d629..67f84e6802f14a05c62395925e5b02af1da8f856 100644
--- a/src/mesa_pd/data/ContactStorage.h
+++ b/src/mesa_pd/data/ContactStorage.h
@@ -13,7 +13,7 @@
 //  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 ContactStorage.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
diff --git a/src/mesa_pd/data/DataTypes.h b/src/mesa_pd/data/DataTypes.h
index 49da2f96340f497aa6d44f8c607ef6977881c2e9..4c38e37267f822c9c62f102b88f0a5d0b9885623 100644
--- a/src/mesa_pd/data/DataTypes.h
+++ b/src/mesa_pd/data/DataTypes.h
@@ -13,7 +13,7 @@
 //  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 DataTypes.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/Flags.h b/src/mesa_pd/data/Flags.h
index 14d1593c2cc8c8d32c154c25dbcc60de8b86f33e..5d6f70bc58251c40425a6d23babce319d81d5b32 100644
--- a/src/mesa_pd/data/Flags.h
+++ b/src/mesa_pd/data/Flags.h
@@ -13,7 +13,7 @@
 //  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 Flags.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/IAccessor.h b/src/mesa_pd/data/IAccessor.h
index 2db039769f3ca65d626dd2b90d6ba7d47159e6cd..820541f3e7756938b86df97543186b86ac0dc819 100644
--- a/src/mesa_pd/data/IAccessor.h
+++ b/src/mesa_pd/data/IAccessor.h
@@ -13,7 +13,7 @@
 //  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 IAccessor.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/IContactAccessor.h b/src/mesa_pd/data/IContactAccessor.h
index 68382fa9b4d38492055050b148b016f0d7821e77..5cd7e89575e93218a2265160ec79c4e262e0b614 100644
--- a/src/mesa_pd/data/IContactAccessor.h
+++ b/src/mesa_pd/data/IContactAccessor.h
@@ -13,7 +13,7 @@
 //  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 IContactAccessor.h
+//! \file
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/LinkedCells.h b/src/mesa_pd/data/LinkedCells.h
index 3915558a90a696099dff12575bd055ca2def9a06..9ba699fd0da4a36afc1d76472b1c37c1917a3bde 100644
--- a/src/mesa_pd/data/LinkedCells.h
+++ b/src/mesa_pd/data/LinkedCells.h
@@ -13,7 +13,7 @@
 //  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 LinkedCells.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/ParticleAccessor.h b/src/mesa_pd/data/ParticleAccessor.h
index a00eb26c3bc6770288e31a978abaebdcc7f73f2e..62c61f6f7cf4ab0b8591deeb3661de4ac2a9ac8e 100644
--- a/src/mesa_pd/data/ParticleAccessor.h
+++ b/src/mesa_pd/data/ParticleAccessor.h
@@ -13,7 +13,7 @@
 //  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 ParticleAccessor.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -47,7 +47,7 @@ class ParticleAccessor : public IAccessor
 {
 public:
    ParticleAccessor(const std::shared_ptr<data::ParticleStorage>& ps) : ps_(ps) {}
-   virtual ~ParticleAccessor() = default;
+   ~ParticleAccessor() override = default;
    walberla::id_t const & getUid(const size_t p_idx) const {return ps_->getUid(p_idx);}
    walberla::id_t& getUidRef(const size_t p_idx) {return ps_->getUidRef(p_idx);}
    void setUid(const size_t p_idx, walberla::id_t const & v) { ps_->setUid(p_idx, v);}
@@ -164,6 +164,14 @@ public:
    walberla::mesa_pd::Vec3& getOldHydrodynamicTorqueRef(const size_t p_idx) {return ps_->getOldHydrodynamicTorqueRef(p_idx);}
    void setOldHydrodynamicTorque(const size_t p_idx, walberla::mesa_pd::Vec3 const & v) { ps_->setOldHydrodynamicTorque(p_idx, v);}
    
+   int64_t const & getClusterID(const size_t p_idx) const {return ps_->getClusterID(p_idx);}
+   int64_t& getClusterIDRef(const size_t p_idx) {return ps_->getClusterIDRef(p_idx);}
+   void setClusterID(const size_t p_idx, int64_t const & v) { ps_->setClusterID(p_idx, v);}
+   
+   int64_t const & getSegmentID(const size_t p_idx) const {return ps_->getSegmentID(p_idx);}
+   int64_t& getSegmentIDRef(const size_t p_idx) {return ps_->getSegmentIDRef(p_idx);}
+   void setSegmentID(const size_t p_idx, int64_t const & v) { ps_->setSegmentID(p_idx, v);}
+   
    std::unordered_set<walberla::mpi::MPIRank> const & getNeighborState(const size_t p_idx) const {return ps_->getNeighborState(p_idx);}
    std::unordered_set<walberla::mpi::MPIRank>& getNeighborStateRef(const size_t p_idx) {return ps_->getNeighborStateRef(p_idx);}
    void setNeighborState(const size_t p_idx, std::unordered_set<walberla::mpi::MPIRank> const & v) { ps_->setNeighborState(p_idx, v);}
@@ -212,7 +220,7 @@ inline size_t ParticleAccessor::find(const id_t& uid)
 class SingleParticleAccessor : public IAccessor
 {
 public:
-   virtual ~SingleParticleAccessor() = default;
+   ~SingleParticleAccessor() override = default;
    walberla::id_t const & getUid(const size_t /*p_idx*/) const {return uid_;}
    void setUid(const size_t /*p_idx*/, walberla::id_t const & v) { uid_ = v;}
    walberla::id_t& getUidRef(const size_t /*p_idx*/) {return uid_;}
@@ -329,6 +337,14 @@ public:
    void setOldHydrodynamicTorque(const size_t /*p_idx*/, walberla::mesa_pd::Vec3 const & v) { oldHydrodynamicTorque_ = v;}
    walberla::mesa_pd::Vec3& getOldHydrodynamicTorqueRef(const size_t /*p_idx*/) {return oldHydrodynamicTorque_;}
    
+   int64_t const & getClusterID(const size_t /*p_idx*/) const {return clusterID_;}
+   void setClusterID(const size_t /*p_idx*/, int64_t const & v) { clusterID_ = v;}
+   int64_t& getClusterIDRef(const size_t /*p_idx*/) {return clusterID_;}
+   
+   int64_t const & getSegmentID(const size_t /*p_idx*/) const {return segmentID_;}
+   void setSegmentID(const size_t /*p_idx*/, int64_t const & v) { segmentID_ = v;}
+   int64_t& getSegmentIDRef(const size_t /*p_idx*/) {return segmentID_;}
+   
    std::unordered_set<walberla::mpi::MPIRank> const & getNeighborState(const size_t /*p_idx*/) const {return neighborState_;}
    void setNeighborState(const size_t /*p_idx*/, std::unordered_set<walberla::mpi::MPIRank> const & v) { neighborState_ = v;}
    std::unordered_set<walberla::mpi::MPIRank>& getNeighborStateRef(const size_t /*p_idx*/) {return neighborState_;}
@@ -373,6 +389,8 @@ private:
    walberla::mesa_pd::Vec3 hydrodynamicTorque_;
    walberla::mesa_pd::Vec3 oldHydrodynamicForce_;
    walberla::mesa_pd::Vec3 oldHydrodynamicTorque_;
+   int64_t clusterID_;
+   int64_t segmentID_;
    std::unordered_set<walberla::mpi::MPIRank> neighborState_;
 };
 
diff --git a/src/mesa_pd/data/ParticleAccessorWithShape.h b/src/mesa_pd/data/ParticleAccessorWithShape.h
index 3773468fa554d4ca6b25838e21240c6a7c7f7880..7f3413c12bef6e82fa0782ed31351eacc38ebe7b 100644
--- a/src/mesa_pd/data/ParticleAccessorWithShape.h
+++ b/src/mesa_pd/data/ParticleAccessorWithShape.h
@@ -13,7 +13,7 @@
 //  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 ParticleAccessorWithShape.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/ParticleStorage.h b/src/mesa_pd/data/ParticleStorage.h
index f4f8de7e5afb83145bf35d244a5b139c1580a98a..81d628681345bacbc293eab25bbfc573bda53af2 100644
--- a/src/mesa_pd/data/ParticleStorage.h
+++ b/src/mesa_pd/data/ParticleStorage.h
@@ -13,7 +13,7 @@
 //  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 ParticleStorage.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -100,6 +100,8 @@ public:
       using hydrodynamicTorque_type = walberla::mesa_pd::Vec3;
       using oldHydrodynamicForce_type = walberla::mesa_pd::Vec3;
       using oldHydrodynamicTorque_type = walberla::mesa_pd::Vec3;
+      using clusterID_type = int64_t;
+      using segmentID_type = int64_t;
       using neighborState_type = std::unordered_set<walberla::mpi::MPIRank>;
 
       
@@ -219,6 +221,14 @@ public:
       oldHydrodynamicTorque_type& getOldHydrodynamicTorqueRef() {return storage_.getOldHydrodynamicTorqueRef(i_);}
       void setOldHydrodynamicTorque(oldHydrodynamicTorque_type const & v) { storage_.setOldHydrodynamicTorque(i_, v);}
       
+      clusterID_type const & getClusterID() const {return storage_.getClusterID(i_);}
+      clusterID_type& getClusterIDRef() {return storage_.getClusterIDRef(i_);}
+      void setClusterID(clusterID_type const & v) { storage_.setClusterID(i_, v);}
+      
+      segmentID_type const & getSegmentID() const {return storage_.getSegmentID(i_);}
+      segmentID_type& getSegmentIDRef() {return storage_.getSegmentIDRef(i_);}
+      void setSegmentID(segmentID_type const & v) { storage_.setSegmentID(i_, v);}
+      
       neighborState_type const & getNeighborState() const {return storage_.getNeighborState(i_);}
       neighborState_type& getNeighborStateRef() {return storage_.getNeighborStateRef(i_);}
       void setNeighborState(neighborState_type const & v) { storage_.setNeighborState(i_, v);}
@@ -312,6 +322,8 @@ public:
    using hydrodynamicTorque_type = walberla::mesa_pd::Vec3;
    using oldHydrodynamicForce_type = walberla::mesa_pd::Vec3;
    using oldHydrodynamicTorque_type = walberla::mesa_pd::Vec3;
+   using clusterID_type = int64_t;
+   using segmentID_type = int64_t;
    using neighborState_type = std::unordered_set<walberla::mpi::MPIRank>;
 
    
@@ -431,6 +443,14 @@ public:
    oldHydrodynamicTorque_type& getOldHydrodynamicTorqueRef(const size_t idx) {return oldHydrodynamicTorque_[idx];}
    void setOldHydrodynamicTorque(const size_t idx, oldHydrodynamicTorque_type const & v) { oldHydrodynamicTorque_[idx] = v; }
    
+   clusterID_type const & getClusterID(const size_t idx) const {return clusterID_[idx];}
+   clusterID_type& getClusterIDRef(const size_t idx) {return clusterID_[idx];}
+   void setClusterID(const size_t idx, clusterID_type const & v) { clusterID_[idx] = v; }
+   
+   segmentID_type const & getSegmentID(const size_t idx) const {return segmentID_[idx];}
+   segmentID_type& getSegmentIDRef(const size_t idx) {return segmentID_[idx];}
+   void setSegmentID(const size_t idx, segmentID_type const & v) { segmentID_[idx] = v; }
+   
    neighborState_type const & getNeighborState(const size_t idx) const {return neighborState_[idx];}
    neighborState_type& getNeighborStateRef(const size_t idx) {return neighborState_[idx];}
    void setNeighborState(const size_t idx, neighborState_type const & v) { neighborState_[idx] = v; }
@@ -555,6 +575,8 @@ public:
    std::vector<hydrodynamicTorque_type> hydrodynamicTorque_ {};
    std::vector<oldHydrodynamicForce_type> oldHydrodynamicForce_ {};
    std::vector<oldHydrodynamicTorque_type> oldHydrodynamicTorque_ {};
+   std::vector<clusterID_type> clusterID_ {};
+   std::vector<segmentID_type> segmentID_ {};
    std::vector<neighborState_type> neighborState_ {};
    std::unordered_map<uid_type, size_t> uidToIdx_;
    static_assert(std::is_same<uid_type, id_t>::value,
@@ -594,6 +616,8 @@ ParticleStorage::Particle& ParticleStorage::Particle::operator=(const ParticleSt
    getHydrodynamicTorqueRef() = rhs.getHydrodynamicTorque();
    getOldHydrodynamicForceRef() = rhs.getOldHydrodynamicForce();
    getOldHydrodynamicTorqueRef() = rhs.getOldHydrodynamicTorque();
+   getClusterIDRef() = rhs.getClusterID();
+   getSegmentIDRef() = rhs.getSegmentID();
    getNeighborStateRef() = rhs.getNeighborState();
    return *this;
 }
@@ -630,6 +654,8 @@ ParticleStorage::Particle& ParticleStorage::Particle::operator=(ParticleStorage:
    getHydrodynamicTorqueRef() = std::move(rhs.getHydrodynamicTorqueRef());
    getOldHydrodynamicForceRef() = std::move(rhs.getOldHydrodynamicForceRef());
    getOldHydrodynamicTorqueRef() = std::move(rhs.getOldHydrodynamicTorqueRef());
+   getClusterIDRef() = std::move(rhs.getClusterIDRef());
+   getSegmentIDRef() = std::move(rhs.getSegmentIDRef());
    getNeighborStateRef() = std::move(rhs.getNeighborStateRef());
    return *this;
 }
@@ -667,6 +693,8 @@ void swap(ParticleStorage::Particle lhs, ParticleStorage::Particle rhs)
    std::swap(lhs.getHydrodynamicTorqueRef(), rhs.getHydrodynamicTorqueRef());
    std::swap(lhs.getOldHydrodynamicForceRef(), rhs.getOldHydrodynamicForceRef());
    std::swap(lhs.getOldHydrodynamicTorqueRef(), rhs.getOldHydrodynamicTorqueRef());
+   std::swap(lhs.getClusterIDRef(), rhs.getClusterIDRef());
+   std::swap(lhs.getSegmentIDRef(), rhs.getSegmentIDRef());
    std::swap(lhs.getNeighborStateRef(), rhs.getNeighborStateRef());
 }
 
@@ -704,6 +732,8 @@ std::ostream& operator<<( std::ostream& os, const ParticleStorage::Particle& p )
          "hydrodynamicTorque  : " << p.getHydrodynamicTorque() << "\n" <<
          "oldHydrodynamicForce: " << p.getOldHydrodynamicForce() << "\n" <<
          "oldHydrodynamicTorque: " << p.getOldHydrodynamicTorque() << "\n" <<
+         "clusterID           : " << p.getClusterID() << "\n" <<
+         "segmentID           : " << p.getSegmentID() << "\n" <<
          "neighborState       : " << p.getNeighborState() << "\n" <<
          "================================" << std::endl;
    return os;
@@ -811,6 +841,8 @@ inline ParticleStorage::iterator ParticleStorage::create(const id_t& uid)
    hydrodynamicTorque_.emplace_back(real_t(0));
    oldHydrodynamicForce_.emplace_back(real_t(0));
    oldHydrodynamicTorque_.emplace_back(real_t(0));
+   clusterID_.emplace_back(-1);
+   segmentID_.emplace_back(-1);
    neighborState_.emplace_back();
    uid_.back() = uid;
    uidToIdx_[uid] = uid_.size() - 1;
@@ -873,6 +905,8 @@ inline ParticleStorage::iterator ParticleStorage::erase(iterator& it)
    hydrodynamicTorque_.pop_back();
    oldHydrodynamicForce_.pop_back();
    oldHydrodynamicTorque_.pop_back();
+   clusterID_.pop_back();
+   segmentID_.pop_back();
    neighborState_.pop_back();
    return it;
 }
@@ -922,6 +956,8 @@ inline void ParticleStorage::reserve(const size_t size)
    hydrodynamicTorque_.reserve(size);
    oldHydrodynamicForce_.reserve(size);
    oldHydrodynamicTorque_.reserve(size);
+   clusterID_.reserve(size);
+   segmentID_.reserve(size);
    neighborState_.reserve(size);
 }
 
@@ -956,6 +992,8 @@ inline void ParticleStorage::clear()
    hydrodynamicTorque_.clear();
    oldHydrodynamicForce_.clear();
    oldHydrodynamicTorque_.clear();
+   clusterID_.clear();
+   segmentID_.clear();
    neighborState_.clear();
    uidToIdx_.clear();
 }
@@ -991,6 +1029,8 @@ inline size_t ParticleStorage::size() const
    //WALBERLA_ASSERT_EQUAL( uid_.size(), hydrodynamicTorque.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), oldHydrodynamicForce.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), oldHydrodynamicTorque.size() );
+   //WALBERLA_ASSERT_EQUAL( uid_.size(), clusterID.size() );
+   //WALBERLA_ASSERT_EQUAL( uid_.size(), segmentID.size() );
    //WALBERLA_ASSERT_EQUAL( uid_.size(), neighborState.size() );
    return uid_.size();
 }
@@ -1419,6 +1459,24 @@ public:
    walberla::mesa_pd::Vec3 const & operator()(const data::Particle& p) const {return p.getOldHydrodynamicTorque();}
 };
 ///Predicate that selects a certain property from a Particle
+class SelectParticleClusterID
+{
+public:
+   using return_type = int64_t;
+   int64_t& operator()(data::Particle& p) const {return p.getClusterIDRef();}
+   int64_t& operator()(data::Particle&& p) const {return p.getClusterIDRef();}
+   int64_t const & operator()(const data::Particle& p) const {return p.getClusterID();}
+};
+///Predicate that selects a certain property from a Particle
+class SelectParticleSegmentID
+{
+public:
+   using return_type = int64_t;
+   int64_t& operator()(data::Particle& p) const {return p.getSegmentIDRef();}
+   int64_t& operator()(data::Particle&& p) const {return p.getSegmentIDRef();}
+   int64_t const & operator()(const data::Particle& p) const {return p.getSegmentID();}
+};
+///Predicate that selects a certain property from a Particle
 class SelectParticleNeighborState
 {
 public:
diff --git a/src/mesa_pd/data/STLOverloads.h b/src/mesa_pd/data/STLOverloads.h
index 7a929e54c79fdc9af2adaae9a9aabeb8477cd8c1..b72c4b3759a9c31ff95518c3f0fbd1ceaeece8f7 100644
--- a/src/mesa_pd/data/STLOverloads.h
+++ b/src/mesa_pd/data/STLOverloads.h
@@ -13,7 +13,7 @@
 //  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 STLOverloads.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/ShapeStorage.h b/src/mesa_pd/data/ShapeStorage.h
index 7fdb72a6a10580f6edaa590936a31c827ec62745..3b6ca96ef2a8424f741b0323fe2ce0ac242f2b39 100644
--- a/src/mesa_pd/data/ShapeStorage.h
+++ b/src/mesa_pd/data/ShapeStorage.h
@@ -13,7 +13,7 @@
 //  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 GeometryStorage.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/SparseLinkedCells.h b/src/mesa_pd/data/SparseLinkedCells.h
index 7185c2988b41d7b4b3f0fe78290d0c62a3d68dd7..2c184de476a67c8a02eaf19aae72540d625e2bd9 100644
--- a/src/mesa_pd/data/SparseLinkedCells.h
+++ b/src/mesa_pd/data/SparseLinkedCells.h
@@ -13,7 +13,7 @@
 //  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 SparseLinkedCells.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/shape/BaseShape.h b/src/mesa_pd/data/shape/BaseShape.h
index 20ab37d2e2fe296edcb3dcf1ac41b0412f57b99a..abd1eb5114a3907694c74aa4dea6e6a07936b9e2 100644
--- a/src/mesa_pd/data/shape/BaseShape.h
+++ b/src/mesa_pd/data/shape/BaseShape.h
@@ -13,7 +13,7 @@
 //  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 BaseShape.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/shape/Box.h b/src/mesa_pd/data/shape/Box.h
index cb67d2d6306c9f09d18aa3f26ae893d4754233e5..08f135e864766b822a1ecb93f41ae636f3addd77 100644
--- a/src/mesa_pd/data/shape/Box.h
+++ b/src/mesa_pd/data/shape/Box.h
@@ -13,7 +13,7 @@
 //  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 Box.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/shape/ConvexPolyhedron.h b/src/mesa_pd/data/shape/ConvexPolyhedron.h
index cad5c65863e3ad4f2fc7a1bace071454c787aecd..8bb9ec0b5394747fff6f2d70a672f6d8d481294c 100644
--- a/src/mesa_pd/data/shape/ConvexPolyhedron.h
+++ b/src/mesa_pd/data/shape/ConvexPolyhedron.h
@@ -13,7 +13,7 @@
 //  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 ConvexPolyhedron.h
+//! \file
 //! \author Lukas Werner
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/shape/CylindricalBoundary.h b/src/mesa_pd/data/shape/CylindricalBoundary.h
index 81a04d7e47daf073d6a09616f85369f209838d15..b7bb74051d503ef81ba6157e2636488f44fb1a56 100644
--- a/src/mesa_pd/data/shape/CylindricalBoundary.h
+++ b/src/mesa_pd/data/shape/CylindricalBoundary.h
@@ -13,7 +13,7 @@
 //  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 CylindricalBoundary.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/shape/Ellipsoid.h b/src/mesa_pd/data/shape/Ellipsoid.h
index 7101209cbc2d29e7d9f5fb394686545af0169662..e4b08ef902f8f60e011a70a49cd3110d03e28fa5 100644
--- a/src/mesa_pd/data/shape/Ellipsoid.h
+++ b/src/mesa_pd/data/shape/Ellipsoid.h
@@ -13,7 +13,7 @@
 //  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 Ellipsoid.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/shape/HalfSpace.h b/src/mesa_pd/data/shape/HalfSpace.h
index 87b53eaf9615a1861cbe6c4160a099af5e275a33..8145fc2c482fa33be0f1433c88a2e27ba06903b6 100644
--- a/src/mesa_pd/data/shape/HalfSpace.h
+++ b/src/mesa_pd/data/shape/HalfSpace.h
@@ -13,7 +13,7 @@
 //  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 HalfSpace.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/data/shape/Sphere.h b/src/mesa_pd/data/shape/Sphere.h
index 5192c58b156ff061d9c0f5dd74c14f9d86542635..01b2ca0f715b612180d5ae1041dd018199022f51 100644
--- a/src/mesa_pd/data/shape/Sphere.h
+++ b/src/mesa_pd/data/shape/Sphere.h
@@ -13,7 +13,7 @@
 //  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 Sphere.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/domain/BlockForestDataHandling.cpp b/src/mesa_pd/domain/BlockForestDataHandling.cpp
index 1a75c13849263038235113f8500dc17c698fd361..29316a656b8006c40b4da8cef8a2cc35419542fa 100644
--- a/src/mesa_pd/domain/BlockForestDataHandling.cpp
+++ b/src/mesa_pd/domain/BlockForestDataHandling.cpp
@@ -13,7 +13,7 @@
 //  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 BlockForestDataHandling.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/domain/BlockForestDataHandling.h b/src/mesa_pd/domain/BlockForestDataHandling.h
index 338c6a6495cc6d3ab944e0fa2f5547ae948b285d..7f0e4172e43f6061051821764c3113156727eeec 100644
--- a/src/mesa_pd/domain/BlockForestDataHandling.h
+++ b/src/mesa_pd/domain/BlockForestDataHandling.h
@@ -13,7 +13,7 @@
 //  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 BlockForestDataHandling.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -68,22 +68,22 @@ class BlockForestDataHandling: public blockforest::BlockDataHandling<internal::P
 {
 public:
    BlockForestDataHandling(const std::shared_ptr<data::ParticleStorage>& ps) : ps_(ps) {}
-   virtual ~BlockForestDataHandling() {}
+   ~BlockForestDataHandling() override = default;
 
-   virtual internal::ParticleDeleter* initialize( IBlock * const block ) override;
+   internal::ParticleDeleter* initialize( IBlock * const block ) override;
 
-   virtual void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override;
-   virtual internal::ParticleDeleter* deserialize( IBlock * const block ) override;
-   virtual void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override;
+   void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override;
+   internal::ParticleDeleter* deserialize( IBlock * const block ) override;
+   void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override;
 
-   virtual void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child ) override;
-   virtual void serializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override;
+   void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child ) override;
+   void serializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override;
 
-   virtual internal::ParticleDeleter* deserializeCoarseToFine( Block * const block ) override;
-   virtual internal::ParticleDeleter* deserializeFineToCoarse( Block * const block ) override;
+   internal::ParticleDeleter* deserializeCoarseToFine( Block * const block ) override;
+   internal::ParticleDeleter* deserializeFineToCoarse( Block * const block ) override;
 
-   virtual void deserializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override;
-   virtual void deserializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer, const uint_t child ) override;
+   void deserializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override;
+   void deserializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer, const uint_t child ) override;
 
 private:
    void deserializeImpl( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer );
diff --git a/src/mesa_pd/domain/BlockForestDomain.cpp b/src/mesa_pd/domain/BlockForestDomain.cpp
index cf530527087b6ac5cf8a487c10f29fec0d392285..2614b9d2992f178729e8ef3a5cd992c3015afae8 100644
--- a/src/mesa_pd/domain/BlockForestDomain.cpp
+++ b/src/mesa_pd/domain/BlockForestDomain.cpp
@@ -13,7 +13,7 @@
 //  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 BlockForestDomain.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -127,11 +127,10 @@ bool BlockForestDomain::isContainedInProcessSubdomain(const uint_t rank, const V
 bool   BlockForestDomain::isContainedInLocalSubdomain(const Vec3& pt,
                                                       const real_t& radius) const
 {
-   for (auto& aabb : localAABBs_)
-   {
-      if (isInsideAABB(pt, radius, aabb)) return true;
-   }
-   return false;
+   return std::any_of(localAABBs_.begin(),
+                      localAABBs_.end(),
+                      [&](auto& aabb)
+                      {return isInsideAABB(pt, radius, aabb);});
 }
 
 bool BlockForestDomain::isContainedInProcessSubdomain(const Vec3& pt, const real_t& radius) const
@@ -153,12 +152,11 @@ bool BlockForestDomain::isContainedInProcessSubdomain(const Vec3& pt, const real
    }
 
    //intersects one of the neighboring subdomains?
-   for (auto& subdomain : neighborSubdomains_)
-   {
-      if (sqDistancePointToAABB(pt, subdomain.aabb) < radius*radius) return false;
-   }
-
-   return true;
+   return std::none_of(neighborSubdomains_.begin(),
+                       neighborSubdomains_.end(),
+                       [&](const auto &subdomain) {
+                          return sqDistancePointToAABB(pt, subdomain.aabb) < radius * radius;
+                       });
 }
 
 int BlockForestDomain::findContainingProcessRank(const Vec3& pt) const
diff --git a/src/mesa_pd/domain/BlockForestDomain.h b/src/mesa_pd/domain/BlockForestDomain.h
index e4d01fbb9863317c249c4f20b0aad0d54a523de0..7df1f9b6827fda77c950acadfbf28bc69e2b91a5 100644
--- a/src/mesa_pd/domain/BlockForestDomain.h
+++ b/src/mesa_pd/domain/BlockForestDomain.h
@@ -13,7 +13,7 @@
 //  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 BlockForestDomain.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/domain/IDomain.h b/src/mesa_pd/domain/IDomain.h
index 532a10e0230baad03f8d0ec487b3bc033baff822..536aeb2ff8db34796b0a41d72fa633b0ee6d2dc6 100644
--- a/src/mesa_pd/domain/IDomain.h
+++ b/src/mesa_pd/domain/IDomain.h
@@ -13,7 +13,7 @@
 //  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 IDomain.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/domain/InfiniteDomain.h b/src/mesa_pd/domain/InfiniteDomain.h
index 3e98e8246f71b48a99d4c5db8fb6ac0565ccc711..591da890243262b958645ada984b690caac31722 100644
--- a/src/mesa_pd/domain/InfiniteDomain.h
+++ b/src/mesa_pd/domain/InfiniteDomain.h
@@ -13,29 +13,41 @@
 //  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 InfiniteDomain.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
 
 #pragma once
 
+#include <core/mpi/MPIManager.h>
 #include <mesa_pd/domain/IDomain.h>
 
 namespace walberla {
 namespace mesa_pd {
 namespace domain {
 
+/**
+ * Every process assumes the whole simulation space belongs to its subdomain.
+ */
 class InfiniteDomain : public IDomain
 {
 public:
-   bool   isContainedInProcessSubdomain(const uint_t /*rank*/, const Vec3& /*pt*/) const override {return true;}
-   int    findContainingProcessRank(const Vec3& /*pt*/) const override {return walberla::mpi::MPIManager::instance()->rank();}
+   ///Everything belongs to the calling process.
+   bool   isContainedInProcessSubdomain(const uint_t rank, const Vec3& /*pt*/) const override {return rank==rank_;}
+   ///Everything belongs to the calling process.
+   int    findContainingProcessRank(const Vec3& /*pt*/) const override {return static_cast<int>(rank_);}
+   ///Nothing to do here since domain is infinite.
    void   periodicallyMapToDomain(Vec3& /*pt*/) const override {}
+   ///If I own everything I do not have neighbors.
    std::vector<uint_t> getNeighborProcesses() const override {return {};}
+   ///Everything belongs to my subdomain.
    bool   intersectsWithProcessSubdomain(const uint_t rank, const Vec3& /*pt*/, const real_t& /*radius*/) const override
-   { return int_c(rank)==walberla::mpi::MPIManager::instance()->rank() ? true : false;}
+   { return rank==rank_;}
+   ///Nothing to do here.
    void   correctParticlePosition(Vec3& /*pt*/) const override {}
+private:
+   const uint_t rank_ = static_cast<uint_t>(MPIManager::instance()->rank());
 };
 
 } //namespace domain
diff --git a/src/mesa_pd/domain/InfoCollection.h b/src/mesa_pd/domain/InfoCollection.h
index 90df6dbf7444cee08ae06f86cd3e9d1241d0c8c1..dc6047c96fe106e30e181aab8819c37f0d41ff9a 100644
--- a/src/mesa_pd/domain/InfoCollection.h
+++ b/src/mesa_pd/domain/InfoCollection.h
@@ -13,7 +13,7 @@
 //  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 InfoCollection.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/AssocToBlock.h b/src/mesa_pd/kernel/AssocToBlock.h
index f25008abc8157e59da4c59d18cd28b02279ea26f..a9a77260d7ba08ce0ed3b78c4c12738f27b2108f 100644
--- a/src/mesa_pd/kernel/AssocToBlock.h
+++ b/src/mesa_pd/kernel/AssocToBlock.h
@@ -13,7 +13,7 @@
 //  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 AssocToBlock.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/DetectAndStoreContacts.h b/src/mesa_pd/kernel/DetectAndStoreContacts.h
index 5a7900e43ae41c1c7799b55c48a168f2dbeb42b9..7656703b9ffd300375853e77061cb20ee9a7c161 100644
--- a/src/mesa_pd/kernel/DetectAndStoreContacts.h
+++ b/src/mesa_pd/kernel/DetectAndStoreContacts.h
@@ -13,7 +13,7 @@
 //  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 DetectAndStoreContacts.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
diff --git a/src/mesa_pd/kernel/DoubleCast.h b/src/mesa_pd/kernel/DoubleCast.h
index 6f7851bfe4f86670954880daf5d87090de801f82..c306c989d589fb809bbe7d38d148bf631e2bf9e4 100644
--- a/src/mesa_pd/kernel/DoubleCast.h
+++ b/src/mesa_pd/kernel/DoubleCast.h
@@ -13,7 +13,7 @@
 //  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 SingleCast.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/ExplicitEuler.h b/src/mesa_pd/kernel/ExplicitEuler.h
index 8887219b19e206bf4e5b193b7090e3ae989886ce..bf1bfa6609731a66d48a05cd5ab9995c30b4994e 100644
--- a/src/mesa_pd/kernel/ExplicitEuler.h
+++ b/src/mesa_pd/kernel/ExplicitEuler.h
@@ -13,7 +13,7 @@
 //  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 ExplicitEuler.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -34,7 +34,12 @@ namespace mesa_pd {
 namespace kernel {
 
 /**
- * Kernel which explicitly integrates all particles in time.
+ * Explicit Euler integration.
+ * Uses the 0.5at^2 extension for position integration.
+ * Boils down to using v_{t+0.5dt} for position integration.
+ * This version exhibits increased stability compared to standard explicit euler.
+ *
+ * Wachs, A. Particle-scale computational approaches to model dry and saturated granular flows of non-Brownian, non-cohesive, and non-spherical rigid bodies. Acta Mech 230, 1919–1980 (2019). https://doi.org/10.1007/s00707-019-02389-9
  *
  * This kernel requires the following particle accessor interface
  * \code
@@ -87,13 +92,17 @@ inline void ExplicitEuler::operator()(const size_t idx,
 
    if (!data::particle_flags::isSet( ac.getFlags(idx), data::particle_flags::FIXED))
    {
-      ac.setPosition      (idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ * dt_ + ac.getLinearVelocity(idx) * dt_ + ac.getPosition(idx));
-      ac.setLinearVelocity(idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ + ac.getLinearVelocity(idx));
+      ac.setPosition      (idx, 0.5_r * ac.getInvMass(idx) * ac.getForce(idx) * dt_ * dt_ +
+                                ac.getLinearVelocity(idx) * dt_ +
+                                ac.getPosition(idx));
+      ac.setLinearVelocity(idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ +
+                                ac.getLinearVelocity(idx));
       const Vec3 wdot = math::transformMatrixRART(ac.getRotation(idx).getMatrix(),
                                                   ac.getInvInertiaBF(idx)) * ac.getTorque(idx);
 
       // Calculating the rotation angle
-      const Vec3 phi( ac.getAngularVelocity(idx) * dt_ + wdot * dt_ * dt_);
+      const Vec3 phi( 0.5_r * wdot * dt_ * dt_ +
+                      ac.getAngularVelocity(idx) * dt_ );
 
       // Calculating the new orientation
       auto rotation = ac.getRotation(idx);
diff --git a/src/mesa_pd/kernel/ExplicitEulerWithShape.h b/src/mesa_pd/kernel/ExplicitEulerWithShape.h
index d66d9a8aa7bddb762a2e5d4c61314e0e2699a16c..06ed0c23211f051dbc81fd481c749f454bc7f509 100644
--- a/src/mesa_pd/kernel/ExplicitEulerWithShape.h
+++ b/src/mesa_pd/kernel/ExplicitEulerWithShape.h
@@ -13,7 +13,7 @@
 //  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 ExplicitEuler.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/ForceLJ.h b/src/mesa_pd/kernel/ForceLJ.h
index b937372b96ffeaf09bca1f69480426050cd79cd3..483e586b70f383544f3847336524eec204747eb1 100644
--- a/src/mesa_pd/kernel/ForceLJ.h
+++ b/src/mesa_pd/kernel/ForceLJ.h
@@ -13,7 +13,7 @@
 //  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 ForceLJ.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/HCSITSRelaxationStep.h b/src/mesa_pd/kernel/HCSITSRelaxationStep.h
index 10d87699963103261cbe32cd411f2af46429739a..5d9ac3a762e244e450ef7d89e7ecd4c5467a9384 100644
--- a/src/mesa_pd/kernel/HCSITSRelaxationStep.h
+++ b/src/mesa_pd/kernel/HCSITSRelaxationStep.h
@@ -13,7 +13,7 @@
 //  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 HCSITSRelaxationStep.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
diff --git a/src/mesa_pd/kernel/HeatConduction.h b/src/mesa_pd/kernel/HeatConduction.h
index 57e60cbccb272452e51490cbc5cfd0a35c66840f..b474db018ac02f02dd6b0805e950314ae33b022c 100644
--- a/src/mesa_pd/kernel/HeatConduction.h
+++ b/src/mesa_pd/kernel/HeatConduction.h
@@ -13,7 +13,7 @@
 //  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 HeatConduction.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/InitContactsForHCSITS.h b/src/mesa_pd/kernel/InitContactsForHCSITS.h
index e2d14795204d7372f0b61032f3437653acbbd154..fae1131c7515abff21d1e0fd8b57c8bfe5688e2c 100644
--- a/src/mesa_pd/kernel/InitContactsForHCSITS.h
+++ b/src/mesa_pd/kernel/InitContactsForHCSITS.h
@@ -13,7 +13,7 @@
 //  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 InitContactsForHCSITS.h
+//! \file
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/InitParticlesForHCSITS.h b/src/mesa_pd/kernel/InitParticlesForHCSITS.h
index 5a72e4fadaee5c914b88c654ae40cf41a6955d0d..aba85f763f28e8a817e3a080c688ee608ea7adcc 100644
--- a/src/mesa_pd/kernel/InitParticlesForHCSITS.h
+++ b/src/mesa_pd/kernel/InitParticlesForHCSITS.h
@@ -13,7 +13,7 @@
 //  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 InitParticlesForHCSITS.h
+//! \file
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/InsertParticleIntoLinkedCells.h b/src/mesa_pd/kernel/InsertParticleIntoLinkedCells.h
index 6f693bce6f1c33f202fae455b9920258c176a364..a2cbd5c578c89d9da0937d2f86b617b4818d5013 100644
--- a/src/mesa_pd/kernel/InsertParticleIntoLinkedCells.h
+++ b/src/mesa_pd/kernel/InsertParticleIntoLinkedCells.h
@@ -13,7 +13,7 @@
 //  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 InsertParticleIntoLinkedCells.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/InsertParticleIntoSparseLinkedCells.h b/src/mesa_pd/kernel/InsertParticleIntoSparseLinkedCells.h
index a473a6fbbdbf6adf200ec208a7153cc20530f514..6975337c415013d365f7a90b6fe3d060f2a8cf48 100644
--- a/src/mesa_pd/kernel/InsertParticleIntoSparseLinkedCells.h
+++ b/src/mesa_pd/kernel/InsertParticleIntoSparseLinkedCells.h
@@ -13,7 +13,7 @@
 //  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 InsertParticleIntoSparseLinkedCells.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/IntegrateParticlesHCSITS.h b/src/mesa_pd/kernel/IntegrateParticlesHCSITS.h
index 7cc75be73f78cba51ba5d78b29e037624710ae42..0f00fc9a027c8c280c16ef87ee29b607caea1a01 100644
--- a/src/mesa_pd/kernel/IntegrateParticlesHCSITS.h
+++ b/src/mesa_pd/kernel/IntegrateParticlesHCSITS.h
@@ -13,7 +13,7 @@
 //  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 IntegrateParticlesHCSITS.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
diff --git a/src/mesa_pd/kernel/LinearSpringDashpot.h b/src/mesa_pd/kernel/LinearSpringDashpot.h
index acf15d8448af6b0212cc0e0422f2ceaf8848ecb1..3a57f56c0aabae532ada51e9e73873bf60360958 100644
--- a/src/mesa_pd/kernel/LinearSpringDashpot.h
+++ b/src/mesa_pd/kernel/LinearSpringDashpot.h
@@ -13,7 +13,7 @@
 //  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 LinearSpringDashpot.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
diff --git a/src/mesa_pd/kernel/NonLinearSpringDashpot.h b/src/mesa_pd/kernel/NonLinearSpringDashpot.h
index 987cff4ef993e3ba46530c2cb4fec3f0125b03d9..6486e0ec522d83349759c3ece70f9b9d58ba13f0 100644
--- a/src/mesa_pd/kernel/NonLinearSpringDashpot.h
+++ b/src/mesa_pd/kernel/NonLinearSpringDashpot.h
@@ -13,7 +13,7 @@
 //  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 NonLinearSpringDashpot.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
diff --git a/src/mesa_pd/kernel/PFCDamping.h b/src/mesa_pd/kernel/PFCDamping.h
new file mode 100644
index 0000000000000000000000000000000000000000..1442226e9784b4645e5007398d7892479bbdca1a
--- /dev/null
+++ b/src/mesa_pd/kernel/PFCDamping.h
@@ -0,0 +1,92 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov, <drozd013@umn.edu>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/IAccessor.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace kernel {
+
+/**
+ * PFC style damping
+ *
+ * This kernel requires the following particle accessor interface
+ * \code
+ * const walberla::mesa_pd::Vec3& getLinearVelocity(const size_t p_idx) const;
+ * void setLinearVelocity(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * const walberla::mesa_pd::Vec3& getForce(const size_t p_idx) const;
+ * void setForce(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * const walberla::mesa_pd::data::particle_flags::FlagT& getFlags(const size_t p_idx) const;
+ *
+ * const walberla::mesa_pd::Vec3& getAngularVelocity(const size_t p_idx) const;
+ * void setAngularVelocity(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * const walberla::mesa_pd::Vec3& getTorque(const size_t p_idx) const;
+ * void setTorque(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * \endcode
+ *
+ * \ingroup mesa_pd_kernel
+ */
+class PFCDamping
+{
+public:
+   PFCDamping(const real_t alpha) : alpha_(alpha) {}
+
+   template <typename Accessor>
+   void operator()(const size_t p_idx, Accessor& ac) const;
+private:
+   real_t alpha_ = 0_r;
+};
+
+template <typename Accessor>
+inline void PFCDamping::operator()(const size_t p_idx,
+                                   Accessor& ac) const
+{
+   static_assert(std::is_base_of<data::IAccessor, Accessor>::value, "please provide a valid accessor");
+
+   Vec3 damp_F(0,0,0);
+   Vec3 damp_M(0,0,0);
+
+   for (size_t i = 0; i < 3; i++)
+   {
+      damp_F[i] = - alpha_ * std::fabs( ac.getForce(p_idx)[i] ) * math::sign( ac.getLinearVelocity(p_idx)[i] );
+      damp_M[i] = - alpha_ * std::fabs( ac.getTorque(p_idx)[i] ) * math::sign( ac.getAngularVelocity(p_idx)[i] );
+   }
+
+   ac.setForce (p_idx, ac.getForce(p_idx)  + damp_F);
+   ac.setTorque(p_idx, ac.getTorque(p_idx) + damp_M);
+}
+
+} //namespace kernel
+} //namespace mesa_pd
+} //namespace walberla
\ No newline at end of file
diff --git a/src/mesa_pd/kernel/ParticleSelector.h b/src/mesa_pd/kernel/ParticleSelector.h
index 7a09913cac12de5a73df1c72034ad6de2a9a21e4..9ac4392f568aa8a78730b664fe9b8fc28583e3de 100644
--- a/src/mesa_pd/kernel/ParticleSelector.h
+++ b/src/mesa_pd/kernel/ParticleSelector.h
@@ -13,7 +13,7 @@
 //  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 ParticleSelector.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -59,8 +59,7 @@ public:
    bool operator()(const size_t idx, Accessor& ac) const
    {
       using namespace walberla::mesa_pd::data::particle_flags;
-      if (isSet(ac.getFlags(idx), GHOST)) return true;
-      return false;
+      return isSet(ac.getFlags(idx), GHOST);
    }
 };
 
@@ -71,8 +70,7 @@ public:
    bool operator()(const size_t idx, const size_t jdx, Accessor& ac) const
    {
       using namespace walberla::mesa_pd::data::particle_flags;
-      if (isSet(ac.getFlags(idx), INFINITE) && isSet(ac.getFlags(jdx), INFINITE)) return false;
-      return true;
+      return !(isSet(ac.getFlags(idx), INFINITE) && isSet(ac.getFlags(jdx), INFINITE));
    }
 };
 
diff --git a/src/mesa_pd/kernel/SemiImplicitEuler.h b/src/mesa_pd/kernel/SemiImplicitEuler.h
new file mode 100644
index 0000000000000000000000000000000000000000..13963d4f59c22f0a03d865eceac18e8b5183f3e2
--- /dev/null
+++ b/src/mesa_pd/kernel/SemiImplicitEuler.h
@@ -0,0 +1,115 @@
+//======================================================================================================================
+//
+//  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
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/IAccessor.h>
+
+namespace walberla {
+namespace mesa_pd {
+namespace kernel {
+
+/**
+ * Semi-implicit Euler integration for position and velocity.
+ *
+ * This kernel requires the following particle accessor interface
+ * \code
+ * const walberla::mesa_pd::Vec3& getPosition(const size_t p_idx) const;
+ * void setPosition(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * const walberla::mesa_pd::Vec3& getLinearVelocity(const size_t p_idx) const;
+ * void setLinearVelocity(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * const walberla::real_t& getInvMass(const size_t p_idx) const;
+ *
+ * const walberla::mesa_pd::Vec3& getForce(const size_t p_idx) const;
+ * void setForce(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * const walberla::mesa_pd::data::particle_flags::FlagT& getFlags(const size_t p_idx) const;
+ *
+ * const walberla::mesa_pd::Rot3& getRotation(const size_t p_idx) const;
+ * void setRotation(const size_t p_idx, const walberla::mesa_pd::Rot3& v);
+ *
+ * const walberla::mesa_pd::Vec3& getAngularVelocity(const size_t p_idx) const;
+ * void setAngularVelocity(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * const walberla::mesa_pd::Mat3& getInvInertiaBF(const size_t p_idx) const;
+ *
+ * const walberla::mesa_pd::Vec3& getTorque(const size_t p_idx) const;
+ * void setTorque(const size_t p_idx, const walberla::mesa_pd::Vec3& v);
+ *
+ * \endcode
+ *
+ * \pre  All forces and torques acting on the particles have to be set.
+ * \post All forces and torques are reset to 0.
+ * \ingroup mesa_pd_kernel
+ */
+class SemiImplicitEuler
+{
+public:
+   explicit SemiImplicitEuler(const real_t dt) : dt_(dt) {}
+
+   template <typename Accessor>
+   void operator()(const size_t i, Accessor& ac) const;
+private:
+   real_t dt_ = real_t(0.0);
+};
+
+template <typename Accessor>
+inline void SemiImplicitEuler::operator()(const size_t idx,
+                                          Accessor& ac) const
+{
+   static_assert(std::is_base_of<data::IAccessor, Accessor>::value, "please provide a valid accessor");
+
+   if (!data::particle_flags::isSet( ac.getFlags(idx), data::particle_flags::FIXED))
+   {
+      ac.setLinearVelocity( idx, ac.getInvMass(idx) * ac.getForce(idx) * dt_ +
+                                 ac.getLinearVelocity(idx));
+      ac.setPosition      ( idx, ac.getLinearVelocity(idx) * dt_ +
+                                 ac.getPosition(idx));
+      const Vec3 wdot = math::transformMatrixRART(ac.getRotation(idx).getMatrix(),
+                                                  ac.getInvInertiaBF(idx)) * ac.getTorque(idx);
+
+      ac.setAngularVelocity(idx, wdot * dt_ +
+                                 ac.getAngularVelocity(idx));
+
+      // Calculating the rotation angle
+      const Vec3 phi( ac.getAngularVelocity(idx) * dt_ );
+
+      // Calculating the new orientation
+      auto rotation = ac.getRotation(idx);
+      rotation.rotate( phi );
+      ac.setRotation(idx, rotation);
+   }
+
+   ac.setForce (idx, Vec3(real_t(0), real_t(0), real_t(0)));
+   ac.setTorque(idx, Vec3(real_t(0), real_t(0), real_t(0)));
+}
+
+} //namespace kernel
+} //namespace mesa_pd
+} //namespace walberla
\ No newline at end of file
diff --git a/src/mesa_pd/kernel/SingleCast.h b/src/mesa_pd/kernel/SingleCast.h
index b8ae7571732ba1247c7ccbca3acf3048103f5a7c..22ab3bde4d382c0f43453aee3de5f10197c965a7 100644
--- a/src/mesa_pd/kernel/SingleCast.h
+++ b/src/mesa_pd/kernel/SingleCast.h
@@ -13,7 +13,7 @@
 //  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 SingleCast.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/SpringDashpot.h b/src/mesa_pd/kernel/SpringDashpot.h
index 60957e0fa374d883232a65531434c519183df168..1388e85c86f4aecd1c78fbd82ecfe00596c3e5c0 100644
--- a/src/mesa_pd/kernel/SpringDashpot.h
+++ b/src/mesa_pd/kernel/SpringDashpot.h
@@ -13,7 +13,7 @@
 //  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 SpringDashpot.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/SpringDashpotSpring.h b/src/mesa_pd/kernel/SpringDashpotSpring.h
index f2f87671a4e9bf08c87f17100f3b47a482f385e3..0fbd2e684a2722abde85312be083eea7df5d6a12 100644
--- a/src/mesa_pd/kernel/SpringDashpotSpring.h
+++ b/src/mesa_pd/kernel/SpringDashpotSpring.h
@@ -13,7 +13,7 @@
 //  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 SpringDashpotSpring.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/TemperatureIntegration.h b/src/mesa_pd/kernel/TemperatureIntegration.h
index c90eb3ffcf3b65bb97e77a31d127eb43a02830a9..8f8a771cec0217ab8b4d096637e50c850cb8ba36 100644
--- a/src/mesa_pd/kernel/TemperatureIntegration.h
+++ b/src/mesa_pd/kernel/TemperatureIntegration.h
@@ -13,7 +13,7 @@
 //  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 TemperatureIntegration.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/kernel/VelocityVerlet.h b/src/mesa_pd/kernel/VelocityVerlet.h
index f029652c27e4c1e5e4ac6e81d27b083e59e3a7b6..1564842e494d092ea22a0cf3f6295ef1de515adf 100644
--- a/src/mesa_pd/kernel/VelocityVerlet.h
+++ b/src/mesa_pd/kernel/VelocityVerlet.h
@@ -13,7 +13,7 @@
 //  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 VelocityVerlet.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -41,6 +41,8 @@ namespace kernel {
  * integration is only complete when both functions are called. The integration
  * is symplectic.
  *
+ * Wachs, A. Particle-scale computational approaches to model dry and saturated granular flows of non-Brownian, non-cohesive, and non-spherical rigid bodies. Acta Mech 230, 1919–1980 (2019). https://doi.org/10.1007/s00707-019-02389-9
+ *
  * This kernel requires the following particle accessor interface
  * \code
  * const walberla::mesa_pd::Vec3& getPosition(const size_t p_idx) const;
diff --git a/src/mesa_pd/kernel/cnt/AnisotropicVDWContact.h b/src/mesa_pd/kernel/cnt/AnisotropicVDWContact.h
new file mode 100644
index 0000000000000000000000000000000000000000..df369216e4d5e808ab9c5567897ae13ac7c35f85
--- /dev/null
+++ b/src/mesa_pd/kernel/cnt/AnisotropicVDWContact.h
@@ -0,0 +1,386 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/common/ParticleFunctions.h>
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/IAccessor.h>
+
+#include <core/math/Angles.h>
+#include <core/math/Constants.h>
+#include <core/logging/Logging.h>
+
+#include <vector>
+
+namespace walberla {
+namespace mesa_pd {
+namespace kernel {
+namespace cnt {
+
+/**
+ * anisotropic vdW contact
+ */
+class AnisotropicVDWContact
+{
+public:
+   template<typename Accessor>
+   void operator()(const size_t p_idx1,
+                   const size_t p_idx2,
+                   Accessor &ac);
+
+   static constexpr real_t eps_ = 0.03733_r;
+   static constexpr real_t A_ = 0.0223_r;
+   static constexpr real_t B_ = 1.31_r;
+   static constexpr real_t alf_ = 9.5_r;
+   static constexpr real_t bet_ = 4.0_r;
+   static constexpr real_t Cg_ = 90_r;
+   static constexpr real_t del_ = -7.5_r;
+
+   static constexpr size_t M = 5;
+   /// Set of fitted constants, that determine the level of "smoothness" of vdW potential -
+   /// magnitude of shear forces between sliding CNTs
+   static constexpr std::array<real_t, M> C = { 0.35819_r, 0.03263_r, -0.00138_r, -0.00017_r, 0.00024_r };
+
+   /// vdW interaction radius w/r to inertial segment radius
+   static constexpr real_t CutoffFactor     = 4_r;
+   /// CNT radius
+   static constexpr real_t R_CNT = 6.78_r;
+   static constexpr real_t R_ = R_CNT;
+   static constexpr real_t r_cut_ = CutoffFactor * R_;
+
+   auto isParallel() const {return isParallel_;}
+   auto getLastEnergy() const {return energy_;}
+private:
+   real_t energy_; ///< total potential
+   bool isParallel_;
+};
+
+template<typename Accessor>
+inline
+void AnisotropicVDWContact::operator()(const size_t p_idx1,
+                                       const size_t p_idx2,
+                                       Accessor &ac)
+{
+   //===Adaptation of PFC5 vdW contact model implementation====
+
+   // Getting the orientations of segments
+   Vec3 b1 = ac.getRotation(p_idx1).getMatrix() * Vec3(1.0, 0.0, 0.0); ///< ball 1 axial direction
+   Vec3 b2 = ac.getRotation(p_idx2).getMatrix() * Vec3(1.0, 0.0, 0.0); ///< ball 2 axial direction
+
+
+   // Distance between segments
+
+   Vec3 n = ac.getPosition(p_idx2) - ac.getPosition(p_idx1); ///< contact normal
+   auto L = n.length();
+   n *= (1_r/L);
+
+
+   //WALBERLA_LOG_DEVEL( "Normal: n = " << n );
+   //WALBERLA_LOG_DEVEL( "Orientation of seg 2: b2 = " << b2 );
+   //WALBERLA_LOG_DEVEL( "Orientation of seg 1: b1 = " << b1 );
+   //WALBERLA_LOG_DEVEL( "Length of rad vect: L = " << L );
+
+
+   constexpr real_t TOL = 10e-8_r;
+   //---------------------
+   // NORMALS CALCULATION
+   //---------------------
+   // names convention:
+   // c1 - contact 1-2 normal
+   // b1 - ball 1 axial direction
+   // b2 - ball 2 axial direction
+   // b3 - neytral direction
+   // g - alighning torque direction
+   // d - neytral plane normal direction
+   // s - shear force direction
+
+   // angle gamma - angle between two axial directions
+   auto cos_gamma = b1 * b2;
+
+
+
+   // if the angle between two axal directions is blunt, then inverce b2
+   if (cos_gamma < 0_r)
+   {
+      b2 = -b2;
+      cos_gamma = -cos_gamma;
+   }
+   // check that cosine belongs [-1,1]
+   cos_gamma = std::min(1.0_r, cos_gamma);
+   cos_gamma = std::max(-1.0_r, cos_gamma);
+   isParallel_ = false;
+   if (L < 20_r && L > 16_r)
+   {
+      const auto gamma = acos(cos_gamma);
+      if (gamma < math::degToRad(10_r) || gamma > math::degToRad(170_r))
+         isParallel_ = true;
+   }
+   //WALBERLA_LOG_DEVEL( "cos_gamma: = " << cos_gamma );
+
+
+   // calculate functions of double argument
+   auto sin_gamma = std::sqrt(1.0_r - cos_gamma * cos_gamma);
+   auto cos_2gamma = cos_gamma * cos_gamma - sin_gamma * sin_gamma;
+   auto sin_2gamma = 2.0_r * sin_gamma * cos_gamma;
+
+   //WALBERLA_LOG_DEVEL( "sin_gamma: = " << sin_gamma );
+   //WALBERLA_LOG_DEVEL( "cos_2gamma: = " << cos_2gamma );
+   //WALBERLA_LOG_DEVEL( "sin_2gamma: = " << sin_2gamma );
+
+
+   // g - direction of the aligning torques - b1 X b2
+   Vec3 g(0.0, 0.0, 0.0);
+   if (sin_gamma > TOL)
+   {
+      g = b1 % b2;
+      g = g * (1.0_r / g.length());
+   }
+   //WALBERLA_LOG_DEVEL( "Aligning moment direction: g = " << g );
+
+   // b3 - vector defining the neutral plane ( plane of shear forces )
+   Vec3 b3 = b1 + b2;
+   b3 = b3 * (1.0_r / b3.length());
+   //WALBERLA_LOG_DEVEL( "Neutral plane defined by b3 = " << b3 );
+
+   // angle theta - angle between b3 and c1
+   auto cos_theta = b3 * n;
+   // check that cosine belongs [-1,1]
+   cos_theta = std::min(1.0_r, cos_theta);
+   cos_theta = std::max(-1.0_r, cos_theta);
+   //WALBERLA_LOG_DEVEL( "cos_theta: = " << cos_theta );
+
+   // calculation of shear force direction
+   Vec3 s(0.0, 0.0, 0.0);
+   Vec3 d(0.0, 0.0, 0.0);
+
+   if ((cos_theta > -1.0 + TOL) || (cos_theta < 1.0 - TOL))
+      d = n % b3;
+   s = n % d;
+   s = s * (1.0_r / s.length());
+
+   //WALBERLA_LOG_DEVEL( "Shear force direction: = " << s );
+   //--------------------------------
+   // NORMALS CALCULATION - END
+   //--------------------------------
+
+   // Fast calculation of trigonometric functions ( Chebyshev formulas )
+   real_t coss[M], sinn[M];
+   real_t sin_theta = std::sqrt(1.0_r - cos_theta * cos_theta);
+
+   coss[0] = cos_theta * cos_theta - sin_theta * sin_theta;
+   sinn[0] = 2.0_r * sin_theta * cos_theta;
+
+   for (size_t i = 0; i < M - 1; ++i)
+   {
+      coss[i + 1] = coss[i] * coss[0] - sinn[i] * sinn[0];
+      sinn[i + 1] = sinn[i] * coss[0] + sinn[0] * coss[i];
+   }
+
+   //WALBERLA_LOG_DEVEL( "coss: = " << coss[0] <<" "<< coss[1] <<" "<< coss[2] <<" "<< coss[3] <<" "<< coss[4]);
+   //WALBERLA_LOG_DEVEL( "sinn: = " << sinn[0] <<" "<< sinn[1] <<" "<< sinn[2] <<" "<< sinn[3] <<" "<< sinn[4]);
+
+   //WALBERLA_LOG_DEVEL( "C: = " << C[0] <<" "<< C[1] <<" "<< C[2] <<" "<< C[3] <<" "<< C[4]);
+
+   // Cutoff for theta adjustment
+   real_t W_th = 1_r;
+   real_t W_th_L = 0_r;
+   real_t W_th_LL = 0_r;
+
+   // Adjustment w/r to O
+   real_t TH = 1_r;
+   real_t TH_L = 0_r;
+   real_t TH_LL = 0_r;
+   real_t TH_O = 0_r;
+   real_t TH_OO = 0_r;
+
+   real_t sign = 1_r;
+   for (size_t i = 0; i < M; ++i)
+   {
+      TH = TH + W_th * C[i] * (sign + coss[i]);
+      TH_L = TH_L + W_th_L * C[i] * (sign + coss[i]);
+      TH_LL = TH_LL + W_th_LL * C[i] * (sign + coss[i]);
+      TH_O = TH_O - W_th * 2_r * real_t(i + 1) * C[i] * (sinn[i]);
+      TH_OO = TH_OO - W_th * 4_r * real_t(i + 1) * real_t(i + 1) * C[i] * (coss[i]);
+      sign *= -1_r;
+   }
+
+   //WALBERLA_LOG_DEVEL( "TH: = " << TH );
+   //WALBERLA_LOG_DEVEL( "TH_L: = " << TH_L );
+   //WALBERLA_LOG_DEVEL( "TH_LL: = " << TH_LL );
+   //WALBERLA_LOG_DEVEL( "TH_O: = " << TH_O );
+   //WALBERLA_LOG_DEVEL( "TH_OO: = " << TH_OO );
+
+
+   //------------------------------------------------------------------
+   // THIS BLOCK IMPLEMENTS IF THE DISTANCE L IS WITHIN WORKING RANGE
+   //------------------------------------------------------------------
+   if ((L < 2_r * r_cut_) && (L > 2_r * R_ * 1.2_r * TH))
+   {
+      //-----Constants that appear in the potential--------------------------
+      // This set of constants is described in our paper.
+      //---------------------------------------------------------------------
+
+      //-----Function D and its derivatives-----------------------
+      real_t D = L / (R_ * TH) - 2_r;
+      real_t D_L = 1_r / (R_ * TH) - (L * TH_L) / (R_ * TH * TH);
+      real_t D_O = -(L * TH_O) / (R_ * TH * TH);
+      real_t D_LL = -(TH_L) / (R_ * TH * TH);
+      D_LL = D_LL - ((R_ * TH * TH) * (TH_L + L * TH_LL)) / (R_ * R_ * TH * TH * TH * TH);
+      D_LL = D_LL + (2_r * R_ * L * TH * TH_L * TH_L) / (R_ * R_ * TH * TH * TH * TH);
+      real_t D_OO = -(R_ * L * TH_OO * TH * TH) / (R_ * R_ * TH * TH * TH * TH);
+      D_OO = D_OO + (2_r * R_ * L * TH * TH_O * TH_O) / (R_ * R_ * TH * TH * TH * TH);
+      //-----------------------------------------------------------
+
+      //WALBERLA_LOG_DEVEL( "D: = " << D );
+      //WALBERLA_LOG_DEVEL( "D_L: = " << D_L );
+      //WALBERLA_LOG_DEVEL( "D_LL: = " << D_LL );
+      //WALBERLA_LOG_DEVEL( "D_O: = " << D_O );
+      //WALBERLA_LOG_DEVEL( "D_OO: = " << D_OO );
+
+
+      //----Function Vc and its derivatives---------------------------------------
+      const real_t DpowAlpha0 = std::pow(D, -(alf_));
+      const real_t DpowAlpha1 = std::pow(D, -(alf_ + 1));
+      const real_t DpowAlpha2 = std::pow(D, -(alf_ + 2));
+      const real_t DpowBeta0 = std::pow(D, -(bet_));
+      const real_t DpowBeta1 = std::pow(D, -(bet_ + 1));
+      const real_t DpowBeta2 = std::pow(D, -(bet_ + 2));
+      real_t Vc = 4_r * eps_ * (A_ * DpowAlpha0 - B_ * DpowBeta0);
+      real_t Vc_L = 4_r * eps_ * (-alf_ * A_ * DpowAlpha1 + bet_ * B_ * DpowBeta1) * D_L;
+      real_t Vc_O = 4_r * eps_ * (-alf_ * A_ * DpowAlpha1 + bet_ * B_ * DpowBeta1) * D_O;
+      real_t Vc_LL = 4_r * eps_ * (alf_ * (alf_ + 1_r) * A_ * DpowAlpha2 - bet_ * (bet_ + 1_r) * B_ * DpowBeta2) * D_L;
+      Vc_LL = Vc_LL + 4_r * eps_ * (-alf_ * A_ * DpowAlpha1 + bet_ * B_ * DpowBeta1) * D_LL;
+      real_t Vc_OO = 4_r * eps_ * (alf_ * (alf_ + 1_r) * A_ * DpowAlpha2 - bet_ * (bet_ + 1_r) * B_ * DpowBeta2) * D_O;
+      Vc_OO = Vc_OO + 4_r * eps_ * (-alf_ * A_ * DpowAlpha1 + bet_ * B_ * DpowBeta1) * D_OO;
+      //--------------------------------------------------------------------------
+
+      //WALBERLA_LOG_DEVEL( "VC = " << Vc );
+      //WALBERLA_LOG_DEVEL( "VC_L = " << Vc_L );
+      //WALBERLA_LOG_DEVEL( "VC_LL = " << Vc_LL );
+
+      //WALBERLA_LOG_DEVEL( "VC_O = " << Vc_O );
+      //WALBERLA_LOG_DEVEL( "VC_OO = " << Vc_OO );
+
+
+      // Cutoff for u adjustment
+      real_t W_u = 1_r;
+      real_t W_u_L = 0_r;
+      real_t W_u_LL = 0_r;
+      WALBERLA_UNUSED(W_u_LL);
+
+      // Cubic cutoff function 3T->4T (hardcoded since we do not need to mess w these parameters)
+      constexpr auto Q1_ = -80.0_r;
+      constexpr auto Q2_ = 288.0_r;
+      constexpr auto Q3_ = -336.0_r;
+      constexpr auto Q4_ = 128.0_r;
+      const real_t rcut2inv = 1_r / (2.0_r * r_cut_);
+      real_t nd = L * rcut2inv;
+      if ((nd > 0.75_r) && (nd < 1.0_r))
+      {
+         W_u = Q1_ + Q2_ * nd + Q3_ * nd * nd + Q4_ * nd * nd * nd;
+         W_u_L = (Q2_ + 2.0_r * Q3_ * nd + 3.0_r * Q4_ * nd * nd) * rcut2inv;
+         W_u_LL = (2.0_r * Q3_ + 6.0_r * Q4_ * nd) * rcut2inv * rcut2inv;
+      }
+      //--------------------------------------------------------------------------
+
+      //WALBERLA_LOG_DEVEL( "W_u = " << W_u );
+      //WALBERLA_LOG_DEVEL( "W_u_L = " << W_u_L );
+      //WALBERLA_LOG_DEVEL( "W_u_LL = " << W_u_LL );
+
+
+      // Cutoff for gamma adjustment
+
+      real_t W_ga, W_ga_L, W_ga_LL;
+      if (L / R_ > 2.75_r)
+      {
+         W_ga = Cg_ * std::pow((L / R_), del_);
+         W_ga_L = ((del_ * Cg_) / R_) * std::pow((L / R_), del_ - 1_r);
+         W_ga_LL = ((del_ * (del_ - 1) * Cg_) / (R_ * R_)) * std::pow((L / R_), del_ - 2_r);
+      } else
+      {
+         W_ga = Cg_ * std::pow((2.75_r), del_);
+         W_ga_L = 0;
+         W_ga_LL = 0;
+      }
+
+      //WALBERLA_LOG_DEVEL( "W_ga = " << W_ga );
+      //WALBERLA_LOG_DEVEL( "W_ga_L = " << W_ga_L );
+      //WALBERLA_LOG_DEVEL( "W_ga_LL = " << W_ga_LL );
+
+
+      real_t GA = 1_r;
+      real_t GA_L = 0_r;
+      real_t GA_LL = 0_r;
+      WALBERLA_UNUSED(GA_LL);
+      real_t GA_G = 0_r;
+      real_t GA_GG = 0_r;
+      WALBERLA_UNUSED(GA_GG);
+      if (std::abs(sin_gamma) > TOL)
+      {
+         GA = 1_r + W_ga * (1_r - cos_2gamma);
+         GA_L = W_ga_L * (1_r - cos_2gamma);
+         GA_LL = W_ga_LL * (1_r - cos_2gamma);
+         GA_G = 2_r * W_ga * sin_2gamma;
+         GA_GG = 4_r * W_ga * cos_2gamma;
+      }
+
+      //----Forces and torque-----------------------
+      real_t FL = -GA_L * W_u * Vc - GA * W_u_L * Vc - GA * W_u * Vc_L;
+      real_t FO = -(1_r / L) * GA * W_u * Vc_O;
+      real_t MG = -GA_G * W_u * Vc;
+
+      //WALBERLA_LOG_DEVEL( "FL = " << FL );
+      //WALBERLA_LOG_DEVEL( "FO = " << FO );
+      //WALBERLA_LOG_DEVEL( "MG = " << MG );
+
+
+      Vec3 force = FL * n + FO * s;
+      Vec3 moment = MG * g;
+
+
+      //WALBERLA_LOG_DEVEL( "Contact force: = " << force );
+      //WALBERLA_LOG_DEVEL( "Contact moment: = " << moment );
+
+      addForceAtomic(p_idx1, ac, -force);
+      addForceAtomic(p_idx2, ac,  force);
+
+      addTorqueAtomic(p_idx1, ac, -moment);
+      addTorqueAtomic(p_idx2, ac,  moment);
+
+      // Potential energy
+      energy_ = GA * W_u * Vc;
+      // WALBERLA_LOG_DEVEL( "U_vdw = " << U );
+   } else if (L <= 2_r * R_ * 1.2_r * TH)
+   { // Small distance
+      //WALBERLA_LOG_DEVEL( "Small distance");
+      energy_ = 0_r;
+      real_t F = -1_r;
+      addForceAtomic(p_idx1, ac,  F * n);
+      addForceAtomic(p_idx2, ac, -F * n);
+   }
+}
+
+} //namespace cnt
+} //namespace kernel
+} //namespace mesa_pd
+} //namespace walberla
\ No newline at end of file
diff --git a/src/mesa_pd/kernel/cnt/IntegratedVDWContact.h b/src/mesa_pd/kernel/cnt/IntegratedVDWContact.h
new file mode 100644
index 0000000000000000000000000000000000000000..844887f15c5b472b12830f0e933709f4e69681b1
--- /dev/null
+++ b/src/mesa_pd/kernel/cnt/IntegratedVDWContact.h
@@ -0,0 +1,145 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/common/ParticleFunctions.h>
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/IAccessor.h>
+
+#include <core/math/Constants.h>
+#include <core/logging/Logging.h>
+
+#include <vector>
+
+namespace walberla {
+namespace mesa_pd {
+namespace kernel {
+namespace cnt {
+
+/**
+ * vdW Contact with integration
+ */
+class IntegratedVDWContact
+{
+public:
+   template<typename Accessor>
+   void operator()(const size_t p_idx1,
+                   const size_t p_idx2,
+                   Accessor &ac);
+
+   static constexpr real_t R_CNT = 6.78_r; ///< CNT radius
+   static constexpr real_t T = 2_r * R_CNT; ///< Height of a cylindrical segment
+   static constexpr real_t eps = 0.07124_r;
+   static constexpr real_t A = 0.0223_r;
+   static constexpr real_t B = 1.31_r;
+   static constexpr real_t alpha = 9.5_r;
+   static constexpr real_t beta = 4.0_r;
+
+   // vdW adhesion + linear repulsion potential.
+   const real_t r0 = R_CNT * (2_r + std::pow((alpha * A / (beta * B)), 1_r / (alpha - beta)));
+   const real_t u0 = 4_r * eps * (A / std::pow(r0 / R_CNT - 2_r, (alpha)) - B / std::pow(r0 / R_CNT - 2_r, (beta)));
+
+   auto getLastEnergy() const {return energy_;}
+private:
+   real_t energy_; ///< total potential
+};
+
+
+template<typename Accessor>
+inline
+void IntegratedVDWContact::operator()(const size_t p_idx1,
+                                      const size_t p_idx2,
+                                      Accessor &ac)
+{
+   constexpr real_t K_n = 200.0_r; // Good for fabrics modeling
+
+   // particle centers
+   Vec3 O1 = ac.getPosition(p_idx1);
+   Vec3 O2 = ac.getPosition(p_idx2);
+
+   // axial vectors
+   Vec3 b1 = ac.getRotation(p_idx1).getMatrix() * Vec3(1_r, 0_r, 0_r);
+   Vec3 b2 = ac.getRotation(p_idx2).getMatrix() * Vec3(1_r, 0_r, 0_r);
+
+   energy_ = 0_r;
+   Vec3 force12(0);  // Force 12
+   Vec3 force21(0);  // Force 21
+   Vec3 moment12(0); // Total torque 12
+   Vec3 moment21(0); // Total torque 21
+
+   constexpr int Np = 5; // Number of integration points over each axis
+   constexpr real_t Npinv = 1.0_r / real_t(Np);
+   for (int i = 0; i < Np; ++i) // integral dl1
+   {
+      for (int j = 0; j < Np; ++j) // integral dl2
+      {
+         // Levers
+         Vec3 l1 = (-0.5_r * T + (0.5_r + real_t(i)) * T * Npinv) * b1;
+         Vec3 l2 = (-0.5_r * T + (0.5_r + real_t(j)) * T * Npinv) * b2;
+
+         /// radius vector between dl1 and dl2
+         Vec3 R12 = (O2 + l2) - (O1 + l1);
+
+         real_t r12 = R12.length();
+         Vec3 n12 = R12 * (1_r / r12);
+
+         real_t dU = 0_r;
+         Vec3 dforce12(0);
+
+         if (r12 < r0)
+         {
+            // elastic interaction
+            dU = u0 + K_n * (r12 - r0) * (r12 - r0) * Npinv * Npinv;
+            dforce12 = n12 * K_n * (r12 - r0) * Npinv * Npinv;
+         } else
+         {
+            // vdW interaction
+            const real_t normDistance = r12 / R_CNT - 2_r;
+            const real_t powAlpha = std::pow(normDistance, alpha);
+            const real_t powBeta = std::pow(normDistance, beta);
+            dU = 4_r * eps * (A / powAlpha - B / powBeta) * Npinv * Npinv;
+            dforce12 = n12 * 4_r * eps / R_CNT * Npinv * Npinv *
+                       (-(alpha * A) / (powAlpha * normDistance) + (beta * B) / (powBeta * normDistance));
+         }
+
+         Vec3 dmoment12 = l2 % dforce12;
+         Vec3 dmoment21 = l1 % (-dforce12);
+
+         energy_ += dU;
+         force12 += dforce12;
+         force21 -= dforce12;
+         moment12 += dmoment12;
+         moment21 += dmoment21;
+      }
+   }
+
+   addForceAtomic(p_idx1, ac, force12);
+   addForceAtomic(p_idx2, ac, force21);
+   addTorqueAtomic(p_idx1, ac,  -moment21);
+   addTorqueAtomic(p_idx2, ac,  -moment12);
+}
+
+} //namespace cnt
+} //namespace kernel
+} //namespace mesa_pd
+} //namespace walberla
\ No newline at end of file
diff --git a/src/mesa_pd/kernel/cnt/IsotropicVDWContact.h b/src/mesa_pd/kernel/cnt/IsotropicVDWContact.h
new file mode 100644
index 0000000000000000000000000000000000000000..a92ecda09637c90da12722611665836875016328
--- /dev/null
+++ b/src/mesa_pd/kernel/cnt/IsotropicVDWContact.h
@@ -0,0 +1,106 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/common/ParticleFunctions.h>
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/IAccessor.h>
+
+#include <core/math/Constants.h>
+#include <core/logging/Logging.h>
+
+#include <vector>
+
+namespace walberla {
+namespace mesa_pd {
+namespace kernel {
+namespace cnt {
+
+/**
+ * Isotropic vdW contact model.
+ *
+ * implementation follows:
+ * I. Ostanin, R. Ballarini, D. Potyondy, T. Dumitrica, A distinct element method for large scale simulations of carbon nanotube assemblies, J. Mech. Phys. Solid. 61 (2013) 762-782.
+ * https://doi.org/10.1016/j.jmps.2012.10.016
+ */
+class IsotropicVDWContact
+{
+public:
+   template<typename Accessor>
+   void operator()(const size_t p_idx1,
+                   const size_t p_idx2,
+                   Accessor &ac);
+   static real_t equilibriumDistance();
+
+   static constexpr real_t eps = 0.07124_r;
+   static constexpr real_t A = 0.0223_r;
+   static constexpr real_t B = 1.31_r;
+   static constexpr real_t alpha = 9.5_r;
+   static constexpr real_t beta = 4.0_r;
+   static constexpr real_t r = 6.78_r;
+   static constexpr real_t Rinv = 1.0_r / r;
+   static constexpr real_t Dc = 0.4_r;
+
+   auto getLastForce() const {return F_;}
+   auto getLastEnergy() const {return U_;}
+private:
+   real_t F_ = 0_r; ///< resulting force from the last interaction
+   real_t U_ = 0_r; ///< resulting energy from the last interaction
+};
+
+template<typename Accessor>
+inline
+void IsotropicVDWContact::operator()(const size_t p_idx1,
+                                     const size_t p_idx2,
+                                     Accessor &ac)
+{
+   Vec3 n = ac.getPosition(p_idx2) - ac.getPosition(p_idx1); ///< contact normal
+   real_t L = n.length();
+   n *= (1_r/L);
+
+   real_t D = L * Rinv - 2_r;
+   F_ = 0.01_r; //default value
+   U_ = 0_r; //default value
+
+   if (D > Dc)
+   {
+      const auto pow_alpha = std::pow(D, alpha);
+      const auto pow_beta = std::pow(D, beta);
+      F_ = 4_r * eps * (-(alpha * A) / (pow_alpha * D) + (beta * B) / (pow_beta * D));
+      U_ = 4_r * eps * (A / pow_alpha - B / pow_beta);
+   }
+
+   Vec3 force = n * F_;
+   addForceAtomic(p_idx1, ac,  force);
+   addForceAtomic(p_idx2, ac, -force);
+}
+
+real_t IsotropicVDWContact::equilibriumDistance()
+{
+   return r * ( std::pow( (alpha*A)/(beta*B), 1_r/(alpha-beta)) + 2_r);
+}
+
+} //namespace cnt
+} //namespace kernel
+} //namespace mesa_pd
+} //namespace walberla
\ No newline at end of file
diff --git a/src/mesa_pd/kernel/cnt/Parameters.h b/src/mesa_pd/kernel/cnt/Parameters.h
new file mode 100644
index 0000000000000000000000000000000000000000..c265088bab02da9837599fbf333032b795bf2b0d
--- /dev/null
+++ b/src/mesa_pd/kernel/cnt/Parameters.h
@@ -0,0 +1,108 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/DataTypes.h"
+#include "core/math/Constants.h"
+
+namespace walberla {
+namespace mesa_pd {
+namespace kernel {
+namespace cnt {
+
+//====================CNT inertial and elastic parameters========================================================
+
+// Within our model, CNTs are represented with intersecting capsule primitives - cylinders with capped ends.
+// Capped ends are necessary for a continuous normal definition
+// Each capsule represents the inertial properties of a cylindrical segment.
+
+// For the units system used see PFC 5 implementation docs
+
+/// timestep is fixed to 20 fs.
+constexpr auto dT = 20_r;
+
+/// vdW interaction radius w/r to inertial segment radius
+constexpr auto CutoffFactor     = 4_r;
+
+// CNT lattice numbers (m,n) CNT
+constexpr auto mm = 10_r;
+constexpr auto nn = 10_r;
+
+/// CNT Young modulus in GPa based on atomistic simulations
+constexpr auto En = 1029_r;
+/// CNT shear modulus in GPa based on atomistic simulations
+constexpr auto Gs = 459_r;
+
+/// Equilibrium distance of a covalent C-C bond
+constexpr auto a_CC = 1.42_r;
+
+/// Equilibrium vdW separation of two CNT surfaces
+constexpr auto a_VD = 3.35_r;
+
+/// CNT radius
+constexpr auto R_CNT = 6.78_r; //( a_CC / (2 * PII)) * std::sqrt( 3.0 * ( mm * mm + nn * nn + mm * nn ) );
+
+/// external radius of an idealized hollow cylindrical shell
+constexpr auto R_1 = R_CNT + 0.5_r * a_VD;
+/// internal radius of an idealized hollow cylindrical shell
+constexpr auto R_2 = R_CNT - 0.5_r * a_VD;
+
+/// height of a cylindrical segment
+constexpr auto T = 2_r * R_CNT;
+
+/// linear density in atoms per A
+constexpr auto ro = 4_r * mm / ( math::root_three * a_CC );
+
+/// Atomic mass of Carbon in AMU
+constexpr auto M_C = 12.011_r;
+
+/// Mass of the repetative cell in AMU
+constexpr auto mass_T = ro * T * M_C * 104.397_r;
+
+/// Volume of a capsule
+double vol_capsule = (4_r/3_r) * math::pi * (R_CNT * R_CNT * R_CNT) + math::pi * R_CNT * R_CNT * T;
+
+/// Density of a capsule
+double dens_capsule = mass_T / vol_capsule;
+
+/// V-bond parameter
+constexpr auto knorm = (En * 0.006242_r) / T;
+/// V-bond parameter
+constexpr auto kshear = (Gs * 0.006242_r) / T;
+
+constexpr auto margin = 10.e-10_r;
+
+constexpr auto outer_radius     = CutoffFactor * R_CNT;
+// real_t inner_radius     = R_CNT; // Capsule
+constexpr auto inner_radius     = 1.5811_r * R_CNT; // Sphere
+
+/// volume of a sphere
+constexpr auto vol_sphere = (4_r/3_r) * math::pi * (inner_radius * inner_radius * inner_radius);
+
+/// density of a sphere
+constexpr auto dens_sphere = mass_T / vol_sphere;
+
+} //namespace cnt
+} //namespace kernel
+} //namespace mesa_pd
+} //namespace walberla
diff --git a/src/mesa_pd/kernel/cnt/VBondContact.h b/src/mesa_pd/kernel/cnt/VBondContact.h
new file mode 100644
index 0000000000000000000000000000000000000000..6bc5edf98e7bdb8c3adee076a712ea42bc15dccf
--- /dev/null
+++ b/src/mesa_pd/kernel/cnt/VBondContact.h
@@ -0,0 +1,155 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/common/ParticleFunctions.h>
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/IAccessor.h>
+
+#include <core/math/Constants.h>
+#include <core/logging/Logging.h>
+
+#include <vector>
+
+namespace walberla {
+namespace mesa_pd {
+namespace kernel {
+namespace cnt {
+
+/**
+ * VBond interaction model
+ *
+ * Implementation according to:
+ *
+ * Ostanin, I., Dumitrică, T., Eibl, S., and Rüde, U. (October 4, 2019).
+ * "Size-Independent Mechanical Response of Ultrathin Carbon Nanotube Films in Mesoscopic Distinct Element Method Simulations."
+ * ASME. J. Appl. Mech. December 2019; 86(12): 121006.
+ * https://doi.org/10.1115/1.4044413
+ */
+class VBondContact
+{
+public:
+   VBondContact( const Vector3<bool>& isPeriodic = Vector3<bool>{false, false, false},
+                 const Vector3<int64_t>& maxSegments = Vector3<real_t>{0, 0, 0} )
+   : isPeriodic_(isPeriodic)
+   , maxSegments_(maxSegments)
+   {}
+
+   template<typename Accessor>
+   void operator()(const size_t p_idx1,
+                   const size_t p_idx2,
+                   Accessor &ac);
+
+   static constexpr real_t S = 142.7_r; // Area of the bond,
+   static constexpr real_t E = 1029_r * 0.006242_r; // Conversion from GPa to eV/A^3
+   static constexpr real_t G = 459_r * 0.006242_r;
+   static constexpr real_t a = 2_r * 6.78_r; // (10,10) CNTS
+   static constexpr real_t J = 3480_r;
+   static constexpr real_t Jp = 2_r * J;
+
+   // Stiffnesses, equilibrium length etc
+   static constexpr real_t B1 = E * S / a;
+   static constexpr real_t B2 = 12_r * E * J / a;
+   static constexpr real_t B3 = -2_r * E * J / a - G * Jp / (2_r * a);
+   static constexpr real_t B4 = G * Jp / a;
+
+
+
+   /// Get tensile energy from last contact.
+   auto getLastTensileEnergy() const {return tensileEnergy;}
+   /// Get shear energy from last contact.
+   auto getLastShearEnergy() const {return shearEnergy;}
+   /// Get bending energy from last contact.
+   auto getLastBendingEnergy() const {return bendingEnergy;}
+   /// Get twisting energy from last contact.
+   auto getLastTwistingEnergy() const {return twistingEnergy;}
+private:
+   real_t tensileEnergy;
+   real_t shearEnergy;
+   real_t bendingEnergy;
+   real_t twistingEnergy;
+
+   Vector3<bool> isPeriodic_;
+   Vector3<int64_t> maxSegments_;
+};
+
+
+template<typename Accessor>
+inline
+void VBondContact::operator()(const size_t p_idx1,
+                              const size_t p_idx2,
+                              Accessor &ac)
+{
+   Vec3 ri = ac.getPosition(p_idx1);
+   Vec3 rj = ac.getPosition(p_idx2);
+
+   // Fix for the issue of segment's undefined sides
+   real_t sign = ac.getSegmentID(p_idx1) <= ac.getSegmentID(p_idx2) ? 1_r : -1_r;
+   if (((isPeriodic_[0]) && (std::abs(ac.getSegmentID(p_idx2) - ac.getSegmentID(p_idx1)) == maxSegments_[0] - 1)) ||
+       ((isPeriodic_[1]) && (std::abs(ac.getSegmentID(p_idx2) - ac.getSegmentID(p_idx1)) == maxSegments_[1] - 1)))
+      sign = -sign; // special case for periodic fibers
+
+   Vec3 ni1 = ac.getRotation(p_idx1).getMatrix() * Vec3(sign, 0_r, 0_r);
+   Vec3 ni2 = ac.getRotation(p_idx1).getMatrix() * Vec3(0_r, 1_r, 0_r);
+   Vec3 ni3 = ac.getRotation(p_idx1).getMatrix() * Vec3(0_r, 0_r, 1_r);
+
+   Vec3 nj1 = ac.getRotation(p_idx2).getMatrix() * Vec3(-sign, 0_r, 0_r);
+   Vec3 nj2 = ac.getRotation(p_idx2).getMatrix() * Vec3(0_r, 1_r, 0_r);
+   Vec3 nj3 = ac.getRotation(p_idx2).getMatrix() * Vec3(0_r, 0_r, 1_r);
+
+   // Vectors Dij and dij
+   Vec3 Dij = rj - ri;
+   real_t s = Dij.length();
+   Vec3 dij = Dij * (1_r / s);
+
+   real_t C = 1_r;
+
+   tensileEnergy  = 0.5_r * B1 * (s - a) * (s - a);
+   shearEnergy    = B2 * (0.5_r * (nj1 - ni1) * dij - 0.25_r * ni1 * nj1 + 0.75_r);
+   bendingEnergy  = (0.25_r * B2 + B3 + 0.5_r * B4) * (ni1 * nj1 + 1_r);
+   twistingEnergy = -0.5_r * B4 * (ni1 * nj1 + ni2 * nj2 + ni3 * nj3 - 1_r);
+
+   Vec3 rij = dij;
+
+   Vec3 Fij = C * B1 * (s - a) * rij + B2 / (2_r * s) * ((nj1 - ni1) - ((nj1 - ni1) * dij) * dij);
+   Vec3 Fji = -Fij;
+   Vec3 M_TB = C * B3 * (nj1 % ni1) - 0.5_r * B4 * (nj2 % ni2 + nj3 % ni3);
+   Vec3 Mij = -C * 0.5_r * B2 * (dij % ni1) + M_TB;
+   Vec3 Mji =  C * 0.5_r * B2 * (dij % nj1) - M_TB;
+
+   addForceAtomic(p_idx1, ac, Fij);
+   addForceAtomic(p_idx2, ac, Fji);
+   addTorqueAtomic(p_idx1, ac, Mij);
+   addTorqueAtomic(p_idx2, ac, Mji);
+}
+
+} //namespace cnt
+} //namespace kernel
+} //namespace mesa_pd
+} //namespace walberla
\ No newline at end of file
diff --git a/src/mesa_pd/kernel/cnt/ViscousDamping.h b/src/mesa_pd/kernel/cnt/ViscousDamping.h
new file mode 100644
index 0000000000000000000000000000000000000000..5165c0c8decc4ad05900836b889b0f0685810ff5
--- /dev/null
+++ b/src/mesa_pd/kernel/cnt/ViscousDamping.h
@@ -0,0 +1,77 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/common/ParticleFunctions.h>
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/IAccessor.h>
+
+#include <core/math/Constants.h>
+#include <core/logging/Logging.h>
+
+#include <vector>
+
+namespace walberla {
+namespace mesa_pd {
+namespace kernel {
+namespace cnt {
+
+class ViscousDamping
+{
+public:
+   ViscousDamping(real_t forceDampingFactor, real_t torqueDampingFactor)
+   : forceDampingFactor_(forceDampingFactor)
+   , torqueDampingFactor_(torqueDampingFactor)
+   {}
+
+   template<typename Accessor>
+   void operator()(const size_t p_idx1,
+                   const size_t p_idx2,
+                   Accessor &ac) const;
+
+   auto getForceDampingFactor() const {return forceDampingFactor_;}
+   auto getTorqueDampingFactor() const {return torqueDampingFactor_;}
+private:
+   const real_t forceDampingFactor_;
+   const real_t torqueDampingFactor_;
+};
+
+template<typename Accessor>
+inline void ViscousDamping::operator()(const size_t p_idx1,
+                                       const size_t p_idx2,
+                                       Accessor &ac) const
+{
+   Vec3 velDampingForce = (ac.getLinearVelocity(p_idx1) - ac.getLinearVelocity(p_idx2)) * forceDampingFactor_;
+   addForceAtomic(p_idx1, ac, -velDampingForce);
+   addForceAtomic(p_idx2, ac,  velDampingForce);
+
+   Vec3 angDampingTorque = (ac.getAngularVelocity(p_idx1) - ac.getAngularVelocity(p_idx2)) * torqueDampingFactor_;
+   addTorqueAtomic(p_idx1, ac, -angDampingTorque);
+   addTorqueAtomic(p_idx2, ac,  angDampingTorque);
+
+}
+
+} //namespace cnt
+} //namespace kernel
+} //namespace mesa_pd
+} //namespace walberla
\ No newline at end of file
diff --git a/src/mesa_pd/kernel/cnt/WallContact.h b/src/mesa_pd/kernel/cnt/WallContact.h
new file mode 100644
index 0000000000000000000000000000000000000000..5c221025a535399ff243a9995abc8be055cfe09e
--- /dev/null
+++ b/src/mesa_pd/kernel/cnt/WallContact.h
@@ -0,0 +1,113 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mesa_pd/common/ParticleFunctions.h>
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/IAccessor.h>
+
+#include <core/math/Constants.h>
+#include <core/logging/Logging.h>
+
+#include <vector>
+
+namespace walberla {
+namespace mesa_pd {
+namespace kernel {
+namespace cnt {
+
+/**
+ * Repulsive wall interaction kernel.
+ *
+ * Implementation follows:
+ * Wittmaack, Volkov, Zhigilei, "Phase transformation as the mechanism of mechanical deformation of vertically aligned CNT arrays" - Carbon, 2019
+ * https://doi.org/10.1016/j.carbon.2018.11.066
+ *
+ * The force is divided into three areas.
+ * ========================== z=0, position of the wall
+ * close to wall
+ * -------------------------- z=z1+r
+ * spline interpolation
+ * -------------------------- z=z2+r
+ * far away from wall
+ */
+class WallContact
+{
+public:
+   explicit WallContact(real_t zPos) : zPos_(zPos) {}
+
+   template<typename Accessor>
+   void operator()(const size_t p_idx1,
+                   Accessor &ac);
+
+   static constexpr real_t r = 6.78_r; ///< A
+   static constexpr real_t eps = 0.254e-3_r; ///< eV/amu
+   static constexpr real_t m = 2648.8_r; ///< amu
+   static constexpr real_t s = 3.6_r; ///< A
+   static constexpr real_t s12 = ((s * s) * (s * s) * (s * s)) * ((s * s) * (s * s) * (s * s));
+
+   static constexpr real_t z1 = 10_r; ///< A
+   static constexpr real_t z2 = 12_r; ///< A
+
+   auto getLastForce() const {return F_;}
+
+   void setZPos(const real_t& zPos) {zPos_ = zPos;}
+   auto getZPos() const {return zPos_;}
+
+private:
+   real_t zPos_ = 0_r;
+   real_t F_ = 0_r; ///< resulting force from the last interaction
+};
+
+template<typename Accessor>
+inline void WallContact::operator()(const size_t p_idx,
+                                    Accessor &ac)
+{
+   auto dz = ac.getPosition(p_idx)[2] - zPos_;
+   F_ = std::copysign(1_r, dz);
+   dz = std::abs(dz);
+
+   if (dz < r + z1)
+   {
+      //close to wall
+      const auto tmp = dz - r;
+      const auto pow = ((tmp * tmp) * (tmp * tmp) * (tmp * tmp)) * ((tmp * tmp) * (tmp * tmp) * (tmp * tmp)) * tmp;
+      F_ *= m * eps * s12 * 12_r / pow;
+   } else if (dz < r + z2)
+   {
+      //cubic spline interpolation
+      auto S = [](real_t x) { return (3_r * (12_r + r) * (14_r + r) - 6_r * (13_r + r) * x + 3_r * x * x) * 5e-14_r; };
+      F_ *= m * eps * s12 * S(dz);
+   } else
+   {
+      //far away from wall
+      F_ = 0_r;
+   }
+
+   addForceAtomic( p_idx, ac, Vec3(0_r, 0_r, F_) );
+}
+
+} //namespace cnt
+} //namespace kernel
+} //namespace mesa_pd
+} //namespace walberla
\ No newline at end of file
diff --git a/src/mesa_pd/mpi/BroadcastProperty.h b/src/mesa_pd/mpi/BroadcastProperty.h
index 27c4542cf29f2b2b8c08019bbb3a1611de1e02ca..5b3313eb4a533d59eaf36d5f06b5218e07b783d2 100644
--- a/src/mesa_pd/mpi/BroadcastProperty.h
+++ b/src/mesa_pd/mpi/BroadcastProperty.h
@@ -13,7 +13,7 @@
 //  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 BroadcastProperty.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/ClearGhostOwnerSync.h b/src/mesa_pd/mpi/ClearGhostOwnerSync.h
index aff6b8dcc290d382b79f6acb2ec53c11714fbf28..b8713e27acd9b296e199ba6fc4af48336781412f 100644
--- a/src/mesa_pd/mpi/ClearGhostOwnerSync.h
+++ b/src/mesa_pd/mpi/ClearGhostOwnerSync.h
@@ -13,7 +13,7 @@
 //  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 ClearGhostOwnerSync.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/ClearNextNeighborSync.h b/src/mesa_pd/mpi/ClearNextNeighborSync.h
index efadbe262089e5aae1bf9a07f092f36b69e024fe..b091b70bb0d4dcdf6952280aa21278ae2eee5e38 100644
--- a/src/mesa_pd/mpi/ClearNextNeighborSync.h
+++ b/src/mesa_pd/mpi/ClearNextNeighborSync.h
@@ -13,7 +13,7 @@
 //  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 ClearNextNeighborSync.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/ContactFilter.h b/src/mesa_pd/mpi/ContactFilter.h
index cfcada06b3677a7b02c145001f4abf5ef26bfd2c..3ba3bc9b5ffbdbc9a8a0bf5c1cc79b42efabf1fa 100644
--- a/src/mesa_pd/mpi/ContactFilter.h
+++ b/src/mesa_pd/mpi/ContactFilter.h
@@ -13,7 +13,7 @@
 //  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 ContactFilter.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/ReduceContactHistory.h b/src/mesa_pd/mpi/ReduceContactHistory.h
index 7d304f09ddbd25993d7c16edd4fa34113c4a7452..efd4e9bd85dfe65d2d38c9be5bb9fac4250ed652 100644
--- a/src/mesa_pd/mpi/ReduceContactHistory.h
+++ b/src/mesa_pd/mpi/ReduceContactHistory.h
@@ -13,7 +13,7 @@
 //  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 ReduceContactHistory.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/ReduceProperty.h b/src/mesa_pd/mpi/ReduceProperty.h
index 46736d9edd5006565e223a12ab7a69e577ccae27..8722179ee5295cea0541c1a116ebfc473d189e86 100644
--- a/src/mesa_pd/mpi/ReduceProperty.h
+++ b/src/mesa_pd/mpi/ReduceProperty.h
@@ -13,7 +13,7 @@
 //  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 SyncProperty.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/RemoveAndNotify.cpp b/src/mesa_pd/mpi/RemoveAndNotify.cpp
index c04a40f949d3149dfaa51f16ebed7d1335fa1c7e..d54a8c5cc54f9fd3d8a780aecc15906b7587d112 100644
--- a/src/mesa_pd/mpi/RemoveAndNotify.cpp
+++ b/src/mesa_pd/mpi/RemoveAndNotify.cpp
@@ -13,7 +13,7 @@
 //  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 RemoveAndNotify.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/RemoveAndNotify.h b/src/mesa_pd/mpi/RemoveAndNotify.h
index 90cf8d3f1d24ce6e165ca06bfa48d1767bebbce3..753de7195fc1aed4449b3a73ec59af4ef52925bd 100644
--- a/src/mesa_pd/mpi/RemoveAndNotify.h
+++ b/src/mesa_pd/mpi/RemoveAndNotify.h
@@ -13,7 +13,7 @@
 //  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 RemoveAndNotify.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/ShapePackUnpack.h b/src/mesa_pd/mpi/ShapePackUnpack.h
index dd90703559ec4da6660a8fe5e1706bf01242d5ed..4461369ece5d0ac4321eee6ac1a900bb5abc5b21 100644
--- a/src/mesa_pd/mpi/ShapePackUnpack.h
+++ b/src/mesa_pd/mpi/ShapePackUnpack.h
@@ -13,7 +13,7 @@
 //  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 ShapePackUnpack.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/SyncGhostOwners.cpp b/src/mesa_pd/mpi/SyncGhostOwners.cpp
index dd13a9fb2623fe2cba41fd8bcca1318176ec1757..e9eceaf88138b404d93d466283d45f0c7f4d9910 100644
--- a/src/mesa_pd/mpi/SyncGhostOwners.cpp
+++ b/src/mesa_pd/mpi/SyncGhostOwners.cpp
@@ -13,7 +13,7 @@
 //  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 SyncGhostOwners.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/SyncGhostOwners.h b/src/mesa_pd/mpi/SyncGhostOwners.h
index 02089e33c33cd6878731110cb4b349b6213a2788..98043c6a2d2d4a2af2d425cd31c1e46096353930 100644
--- a/src/mesa_pd/mpi/SyncGhostOwners.h
+++ b/src/mesa_pd/mpi/SyncGhostOwners.h
@@ -13,7 +13,7 @@
 //  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 SyncGhostOwners.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/SyncNextNeighbors.cpp b/src/mesa_pd/mpi/SyncNextNeighbors.cpp
index 52d922375c06dae902fef936d4db61b69edfebdf..44f3946e792828e8729dd2967427f07227b79bda 100644
--- a/src/mesa_pd/mpi/SyncNextNeighbors.cpp
+++ b/src/mesa_pd/mpi/SyncNextNeighbors.cpp
@@ -13,7 +13,7 @@
 //  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 SyncNextNeighbors.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/SyncNextNeighbors.h b/src/mesa_pd/mpi/SyncNextNeighbors.h
index 31e51f56b8324ba3dc99cb7cb01345a51c08afd7..e07d0c14d587497202f657be9b624bca7bf21f7a 100644
--- a/src/mesa_pd/mpi/SyncNextNeighbors.h
+++ b/src/mesa_pd/mpi/SyncNextNeighbors.h
@@ -13,7 +13,7 @@
 //  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 SyncNextNeighbors.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/SyncNextNeighborsBlockForest.cpp b/src/mesa_pd/mpi/SyncNextNeighborsBlockForest.cpp
index 34778d0e2ca5b458666498f296f8109ac72b4f50..f4adbf79fae95e17734d7184115f8a2eab988023 100644
--- a/src/mesa_pd/mpi/SyncNextNeighborsBlockForest.cpp
+++ b/src/mesa_pd/mpi/SyncNextNeighborsBlockForest.cpp
@@ -13,7 +13,7 @@
 //  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 SyncNextNeighborsBlockForest.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/SyncNextNeighborsBlockForest.h b/src/mesa_pd/mpi/SyncNextNeighborsBlockForest.h
index 8c04c3ceed47a17f5ec147722549ca4cd25f456d..060dfd5522f3499b50592f93b99209bc7f98d643 100644
--- a/src/mesa_pd/mpi/SyncNextNeighborsBlockForest.h
+++ b/src/mesa_pd/mpi/SyncNextNeighborsBlockForest.h
@@ -13,7 +13,7 @@
 //  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 SyncNextNeighborsBlockForest.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp b/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp
index 145b95cd8bea0732f1f8b507d7d9ed21bfd1c421..10e473b639d88670df53857efcb4087431b0ad22 100644
--- a/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp
+++ b/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp
@@ -13,7 +13,7 @@
 //  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 SyncNextNeighborsNoGhosts.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.h b/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.h
index a95bc567f5c8846f72262f457c9447b6f7f36f4e..d39bb8b2f3d8a7f6b76e8e22e7b03558f5978fda 100644
--- a/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.h
+++ b/src/mesa_pd/mpi/SyncNextNeighborsNoGhosts.h
@@ -13,7 +13,7 @@
 //  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 SyncNextNeighborsNoGhosts.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/ContactHistoryNotification.h b/src/mesa_pd/mpi/notifications/ContactHistoryNotification.h
index b177f313c61d1c522a116c83781109275f2c0c07..92bfe1dac82b233b84eda7add4d5be83cf786cf4 100644
--- a/src/mesa_pd/mpi/notifications/ContactHistoryNotification.h
+++ b/src/mesa_pd/mpi/notifications/ContactHistoryNotification.h
@@ -13,7 +13,7 @@
 //  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 ContactHistoryNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/ForceTorqueNotification.h b/src/mesa_pd/mpi/notifications/ForceTorqueNotification.h
index 2a41c9d4f34fa786c54fd75cf86b750aaf0abfdc..eb5c3198f2096acd9d3620a1352f2a7f9c9f84d0 100644
--- a/src/mesa_pd/mpi/notifications/ForceTorqueNotification.h
+++ b/src/mesa_pd/mpi/notifications/ForceTorqueNotification.h
@@ -13,7 +13,7 @@
 //  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 ForceTorqueNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/HeatFluxNotification.h b/src/mesa_pd/mpi/notifications/HeatFluxNotification.h
index a2ab05d07154697dc5c38d1ddf3a94767599a3db..5ed1d294c50d5f3db75cc99ae76537b3980f5416 100644
--- a/src/mesa_pd/mpi/notifications/HeatFluxNotification.h
+++ b/src/mesa_pd/mpi/notifications/HeatFluxNotification.h
@@ -13,7 +13,7 @@
 //  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 HeatFluxNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/HydrodynamicForceTorqueNotification.h b/src/mesa_pd/mpi/notifications/HydrodynamicForceTorqueNotification.h
index 6155351601b2c0cd16673c6d865eb041152a77c4..7d6ac15949c876eb574346b4ff1c77efd5a0c9d4 100644
--- a/src/mesa_pd/mpi/notifications/HydrodynamicForceTorqueNotification.h
+++ b/src/mesa_pd/mpi/notifications/HydrodynamicForceTorqueNotification.h
@@ -13,7 +13,7 @@
 //  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 HydrodynamicForceTorqueNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/NewGhostParticleNotification.h b/src/mesa_pd/mpi/notifications/NewGhostParticleNotification.h
index 3c929cee446dca9770755b6071d72142ae6de2c6..6a36909af49697b3f81d99032b2648db497c62ab 100644
--- a/src/mesa_pd/mpi/notifications/NewGhostParticleNotification.h
+++ b/src/mesa_pd/mpi/notifications/NewGhostParticleNotification.h
@@ -13,7 +13,7 @@
 //  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 NewGhostParticleNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/NotificationType.h b/src/mesa_pd/mpi/notifications/NotificationType.h
index 66cf5bf95a56f53dd8063f1ba017f8ac08f91639..1e27ec347eed4037b426ecb8ba16184ae2ed1701 100644
--- a/src/mesa_pd/mpi/notifications/NotificationType.h
+++ b/src/mesa_pd/mpi/notifications/NotificationType.h
@@ -13,7 +13,7 @@
 //  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 NotificationType.h
+//! \file
 //! \author Tobias Preclik
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \brief Header file for the notification types
diff --git a/src/mesa_pd/mpi/notifications/PackNotification.h b/src/mesa_pd/mpi/notifications/PackNotification.h
index fe3d5212b9f50f71eff3051905df792df6546732..8a0e2b9e38a1e5c8f614a6504b6787598873dd45 100644
--- a/src/mesa_pd/mpi/notifications/PackNotification.h
+++ b/src/mesa_pd/mpi/notifications/PackNotification.h
@@ -13,7 +13,7 @@
 //  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 PackNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/ParseMessage.h b/src/mesa_pd/mpi/notifications/ParseMessage.h
index caf0e0f890312b7566ffd92cd3194728fb09f095..32ae118a5f851631dde80e8c83422e7eec478652 100644
--- a/src/mesa_pd/mpi/notifications/ParseMessage.h
+++ b/src/mesa_pd/mpi/notifications/ParseMessage.h
@@ -13,7 +13,7 @@
 //  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 ParseMessage.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //! \brief Parsing of messages
 //
diff --git a/src/mesa_pd/mpi/notifications/ParticleCopyNotification.h b/src/mesa_pd/mpi/notifications/ParticleCopyNotification.h
index b01a4dbeb92514fbed8eebcbbc54c6ce92b2f89e..f0944ef6dc366541b335811870764b6473cc4a92 100644
--- a/src/mesa_pd/mpi/notifications/ParticleCopyNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleCopyNotification.h
@@ -13,7 +13,7 @@
 //  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 ParticleCopyNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -69,6 +69,8 @@ public:
       walberla::mesa_pd::Vec3 hydrodynamicTorque {real_t(0)};
       walberla::mesa_pd::Vec3 oldHydrodynamicForce {real_t(0)};
       walberla::mesa_pd::Vec3 oldHydrodynamicTorque {real_t(0)};
+      int64_t clusterID {-1};
+      int64_t segmentID {-1};
    };
 
    inline explicit ParticleCopyNotification( const data::Particle& particle ) : particle_(particle) {}
@@ -101,6 +103,8 @@ inline data::ParticleStorage::iterator createNewParticle(data::ParticleStorage&
    pIt->setHydrodynamicTorque(data.hydrodynamicTorque);
    pIt->setOldHydrodynamicForce(data.oldHydrodynamicForce);
    pIt->setOldHydrodynamicTorque(data.oldHydrodynamicTorque);
+   pIt->setClusterID(data.clusterID);
+   pIt->setSegmentID(data.segmentID);
    return pIt;
 }
 
@@ -148,6 +152,8 @@ mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, cons
    buf << obj.particle_.getHydrodynamicTorque();
    buf << obj.particle_.getOldHydrodynamicForce();
    buf << obj.particle_.getOldHydrodynamicTorque();
+   buf << obj.particle_.getClusterID();
+   buf << obj.particle_.getSegmentID();
    return buf;
 }
 
@@ -176,6 +182,8 @@ mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd:
    buf >> objparam.hydrodynamicTorque;
    buf >> objparam.oldHydrodynamicForce;
    buf >> objparam.oldHydrodynamicTorque;
+   buf >> objparam.clusterID;
+   buf >> objparam.segmentID;
    return buf;
 }
 
diff --git a/src/mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h b/src/mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h
index c4e1c2417b0ca1f02d1b7cbaabd193234085a605..b9e6aa9663c96aa4b24d74c513c66be63fcea6e3 100644
--- a/src/mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleGhostCopyNotification.h
@@ -13,7 +13,7 @@
 //  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 ParticleGhostCopyNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -62,6 +62,8 @@ public:
       uint_t type {0};
       std::map<walberla::id_t, walberla::mesa_pd::data::ContactHistory> oldContactHistory {};
       walberla::real_t temperature {real_t(0)};
+      int64_t clusterID {-1};
+      int64_t segmentID {-1};
    };
 
    inline explicit ParticleGhostCopyNotification( const data::Particle& particle ) : particle_(particle) {}
@@ -87,6 +89,8 @@ inline data::ParticleStorage::iterator createNewParticle(data::ParticleStorage&
    pIt->setType(data.type);
    pIt->setOldContactHistory(data.oldContactHistory);
    pIt->setTemperature(data.temperature);
+   pIt->setClusterID(data.clusterID);
+   pIt->setSegmentID(data.segmentID);
    return pIt;
 }
 
@@ -127,6 +131,8 @@ mpi::GenericSendBuffer<T,G>& operator<<( mpi::GenericSendBuffer<T,G> & buf, cons
    buf << obj.particle_.getType();
    buf << obj.particle_.getOldContactHistory();
    buf << obj.particle_.getTemperature();
+   buf << obj.particle_.getClusterID();
+   buf << obj.particle_.getSegmentID();
    return buf;
 }
 
@@ -148,6 +154,8 @@ mpi::GenericRecvBuffer<T>& operator>>( mpi::GenericRecvBuffer<T> & buf, mesa_pd:
    buf >> objparam.type;
    buf >> objparam.oldContactHistory;
    buf >> objparam.temperature;
+   buf >> objparam.clusterID;
+   buf >> objparam.segmentID;
    return buf;
 }
 
diff --git a/src/mesa_pd/mpi/notifications/ParticleMigrationNotification.h b/src/mesa_pd/mpi/notifications/ParticleMigrationNotification.h
index 836fa29ee8453dd1df578de69e6eccc2e00d2f31..847368ed4ac05817b88e4eed965827433a3ea672 100644
--- a/src/mesa_pd/mpi/notifications/ParticleMigrationNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleMigrationNotification.h
@@ -13,7 +13,7 @@
 //  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 ParticleMigrationNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/ParticleRemoteMigrationNotification.h b/src/mesa_pd/mpi/notifications/ParticleRemoteMigrationNotification.h
index dc406fa1d9c3cca530b800745b88fc1f7d59174e..6e08ba60727cf7251cbbdb75df9c83ba2199581f 100644
--- a/src/mesa_pd/mpi/notifications/ParticleRemoteMigrationNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleRemoteMigrationNotification.h
@@ -13,7 +13,7 @@
 //  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 ParticleRemoteMigrationNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/ParticleRemovalInformationNotification.h b/src/mesa_pd/mpi/notifications/ParticleRemovalInformationNotification.h
index aec2ccdfb7904b512f912fa9aa4fa383668c9a09..e0813d5641923f426b2292ac8d9753e0a4acd107 100644
--- a/src/mesa_pd/mpi/notifications/ParticleRemovalInformationNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleRemovalInformationNotification.h
@@ -13,7 +13,7 @@
 //  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 ParticleRemovalInformationNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/ParticleRemovalNotification.h b/src/mesa_pd/mpi/notifications/ParticleRemovalNotification.h
index 8740e43fd2fe88b20f934233417f00507878b2ad..7b674f4d8df0fb87b739c6cec1a43c8f46d29853 100644
--- a/src/mesa_pd/mpi/notifications/ParticleRemovalNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleRemovalNotification.h
@@ -13,7 +13,7 @@
 //  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 ParticleRemovalNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/ParticleUpdateNotification.h b/src/mesa_pd/mpi/notifications/ParticleUpdateNotification.h
index d845082000b48affcd17cbfecb77247a6d00662d..95d0d7e37acc742b4cff1fce9442398f9a3a3042 100644
--- a/src/mesa_pd/mpi/notifications/ParticleUpdateNotification.h
+++ b/src/mesa_pd/mpi/notifications/ParticleUpdateNotification.h
@@ -13,7 +13,7 @@
 //  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 ParticleUpdateNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/VelocityCorrectionNotification.h b/src/mesa_pd/mpi/notifications/VelocityCorrectionNotification.h
index dd4ca2a89d89251f35cca0dfef8750e452cf1b34..7f9347f918ca77ea079c12988e8d3d03f3254fea 100644
--- a/src/mesa_pd/mpi/notifications/VelocityCorrectionNotification.h
+++ b/src/mesa_pd/mpi/notifications/VelocityCorrectionNotification.h
@@ -13,7 +13,7 @@
 //  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 VelocityCorrectionNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/VelocityUpdateNotification.h b/src/mesa_pd/mpi/notifications/VelocityUpdateNotification.h
index 7f8c1e7c733df07629bbbc4fe3540c19c7dc97d4..f526d99c8f21c50f7b2b7ac224498d3f58d19125 100644
--- a/src/mesa_pd/mpi/notifications/VelocityUpdateNotification.h
+++ b/src/mesa_pd/mpi/notifications/VelocityUpdateNotification.h
@@ -13,7 +13,7 @@
 //  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 VelocityCorrectionNotification.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/mpi/notifications/reset.h b/src/mesa_pd/mpi/notifications/reset.h
index 24bab4533bacbfba9695fb4927136112da31b47e..ff7449e375470decda50b6458274559dfed775a3 100644
--- a/src/mesa_pd/mpi/notifications/reset.h
+++ b/src/mesa_pd/mpi/notifications/reset.h
@@ -13,7 +13,7 @@
 //  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 reset.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/sorting/HilbertCompareFunctor.cpp b/src/mesa_pd/sorting/HilbertCompareFunctor.cpp
index 2cf2a84661a4a5204db0f599c7dfe5c0d2fadcaa..e14de8d24673a399cb386faf7169db1e25ec94e0 100644
--- a/src/mesa_pd/sorting/HilbertCompareFunctor.cpp
+++ b/src/mesa_pd/sorting/HilbertCompareFunctor.cpp
@@ -13,7 +13,7 @@
 //  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 HilbertCompareFunctor.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/sorting/HilbertCompareFunctor.h b/src/mesa_pd/sorting/HilbertCompareFunctor.h
index 70e91665fba051bd14fc51d0679489e780c42b3d..35a7ca9c27bd816668c8c07e1f692bd3d968d139 100644
--- a/src/mesa_pd/sorting/HilbertCompareFunctor.h
+++ b/src/mesa_pd/sorting/HilbertCompareFunctor.h
@@ -13,7 +13,7 @@
 //  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 HilbertCompareFunctor.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/sorting/LinearizedCompareFunctor.cpp b/src/mesa_pd/sorting/LinearizedCompareFunctor.cpp
index 7588285a4e6663ec98fdb6ea4587979efdf2880f..bb9d0cb1411d98462655b0de2b9a93cd0c5823fd 100644
--- a/src/mesa_pd/sorting/LinearizedCompareFunctor.cpp
+++ b/src/mesa_pd/sorting/LinearizedCompareFunctor.cpp
@@ -13,7 +13,7 @@
 //  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 LinearizedCompareFunctor.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/sorting/LinearizedCompareFunctor.h b/src/mesa_pd/sorting/LinearizedCompareFunctor.h
index 9caf02bd04b0ca4447393acafc1b6f820f99d413..f08b7bec0182c63e32147a7aa3a1f77aab0e5f8a 100644
--- a/src/mesa_pd/sorting/LinearizedCompareFunctor.h
+++ b/src/mesa_pd/sorting/LinearizedCompareFunctor.h
@@ -13,7 +13,7 @@
 //  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 LinearizedCompareFunctor.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/vtk/ConvexPolyhedron/MeshParticleVTKOutput.h b/src/mesa_pd/vtk/ConvexPolyhedron/MeshParticleVTKOutput.h
index f39b7c5bc32a7125120b472de3832603e498409d..95127646d85a706f20d2b4fcade6191e95725e9b 100644
--- a/src/mesa_pd/vtk/ConvexPolyhedron/MeshParticleVTKOutput.h
+++ b/src/mesa_pd/vtk/ConvexPolyhedron/MeshParticleVTKOutput.h
@@ -13,7 +13,7 @@
 //  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 MeshVTKOutput.h
+//! \file
 //! \author Lukas Werner
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/DataSourceAdapters.h b/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/DataSourceAdapters.h
index 8eb8a03f4586c3455e2b2fd3e31216ba7e1ca782..2db947f389809739c1f6626122b10fe32d4a2f1a 100644
--- a/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/DataSourceAdapters.h
+++ b/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/DataSourceAdapters.h
@@ -13,7 +13,7 @@
 //  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 DataSourceAdapters.h
+//! \file
 //! \author Lukas Werner
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/FaceDataSource.h b/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/FaceDataSource.h
index 94479be44d587c2c8465112058c167a15d13865b..4ab4de70f5f4fe4650165e6cb3f6d386d91f6149 100644
--- a/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/FaceDataSource.h
+++ b/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/FaceDataSource.h
@@ -13,7 +13,7 @@
 //  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 ParticleFaceDataSource.h
+//! \file
 //! \author Lukas Werner
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/OutputSelectorFaceDataSource.h b/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/OutputSelectorFaceDataSource.h
index f1442527d9ee3fcb08515aedf7d402ff035b47ed..02c5fec6bc04b991db03342fc70bbf2fd6db6066 100644
--- a/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/OutputSelectorFaceDataSource.h
+++ b/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/OutputSelectorFaceDataSource.h
@@ -13,7 +13,7 @@
 //  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 ParticleOutputSelectorFaceDataSource.h
+//! \file
 //! \author Lukas Werner
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/OutputSelectorVertexDataSource.h b/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/OutputSelectorVertexDataSource.h
index 1f8a7fd05e41ed5b78a399571264a53cc3a16697..f569865a5e18717ba96397d23d72ec8b9e5504cf 100644
--- a/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/OutputSelectorVertexDataSource.h
+++ b/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/OutputSelectorVertexDataSource.h
@@ -13,7 +13,7 @@
 //  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 ParticleOutputSelectorVertexDataSource.h
+//! \file
 //! \author Lukas Werner
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/SurfaceVelocityVertexDataSource.h b/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/SurfaceVelocityVertexDataSource.h
index 03eec9d9ed4d5d7bbf17b24c86b55e44389afb7f..2b27499a2ce0f5c51d84dc2aec4f6de4bf346f6a 100644
--- a/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/SurfaceVelocityVertexDataSource.h
+++ b/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/SurfaceVelocityVertexDataSource.h
@@ -13,7 +13,7 @@
 //  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 ParticleOutputSelectorVertexDataSource.h
+//! \file
 //! \author Lukas Werner
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/VertexDataSource.h b/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/VertexDataSource.h
index bef238fa0de699c4da73597d6ddffe460bcf7add..41adedfd914abb90426df5c6e4a06fad86011265 100644
--- a/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/VertexDataSource.h
+++ b/src/mesa_pd/vtk/ConvexPolyhedron/data_sources/VertexDataSource.h
@@ -13,7 +13,7 @@
 //  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 ParticleVertexDataSource.h
+//! \file
 //! \author Lukas Werner
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/vtk/ConvexPolyhedron/tesselation/ConvexPolyhedronTesselation.h b/src/mesa_pd/vtk/ConvexPolyhedron/tesselation/ConvexPolyhedronTesselation.h
index 87bed413da182d220fed5ea869b29328e046d8c6..7aa9fa690cc427bbad011be0717aa9efe4256cfb 100644
--- a/src/mesa_pd/vtk/ConvexPolyhedron/tesselation/ConvexPolyhedronTesselation.h
+++ b/src/mesa_pd/vtk/ConvexPolyhedron/tesselation/ConvexPolyhedronTesselation.h
@@ -13,7 +13,7 @@
 //  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 ConvexPolyhedronTesselation.h
+//! \file
 //! \author Lukas Werner
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/vtk/OutputSelector.h b/src/mesa_pd/vtk/OutputSelector.h
index 177b38f8b4c49c25a422e53f3e2ccfd75797a9cd..03665d48ef76fb409a793981df8d3561050c6736 100644
--- a/src/mesa_pd/vtk/OutputSelector.h
+++ b/src/mesa_pd/vtk/OutputSelector.h
@@ -13,7 +13,7 @@
 //  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 OutputSelector.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -38,7 +38,7 @@ class IOutputSelector
 {
 public:
    IOutputSelector( char const * const ts, const uint_t c) : type_string(ts), components(c) {}
-   virtual ~IOutputSelector() {}
+   virtual ~IOutputSelector() = default;
    virtual void push( std::ostream& os , const data::Particle&& p, const uint_t component ) = 0;
    virtual void push( walberla::vtk::Base64Writer& b64, const data::Particle&& p, const uint_t component ) = 0;
 
diff --git a/src/mesa_pd/vtk/ParticleVtkOutput.cpp b/src/mesa_pd/vtk/ParticleVtkOutput.cpp
index 018670be9dd72965049fd3c0006fbe41a3cecd0f..6093eeb30cce7c8fafa526c0c8d52608ae7c41e7 100644
--- a/src/mesa_pd/vtk/ParticleVtkOutput.cpp
+++ b/src/mesa_pd/vtk/ParticleVtkOutput.cpp
@@ -13,7 +13,7 @@
 //  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 ParticleVtkOutput.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/vtk/ParticleVtkOutput.h b/src/mesa_pd/vtk/ParticleVtkOutput.h
index 5cbe88a6d546009b550aac368d836c56aa79f68e..dcd90fe378d2dc14f4c074e804c3583caf68c4c6 100644
--- a/src/mesa_pd/vtk/ParticleVtkOutput.h
+++ b/src/mesa_pd/vtk/ParticleVtkOutput.h
@@ -13,7 +13,7 @@
 //  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 ParticleVtkOutput.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/vtk/TensorGlyph.cpp b/src/mesa_pd/vtk/TensorGlyph.cpp
index 4f058f6677ac05e473bc470dbffcc0b626924b89..c886731d5a223aa22cd5ad66413a1d586f6f5540 100644
--- a/src/mesa_pd/vtk/TensorGlyph.cpp
+++ b/src/mesa_pd/vtk/TensorGlyph.cpp
@@ -13,7 +13,7 @@
 //  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 TensorGlyph.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/vtk/TensorGlyph.h b/src/mesa_pd/vtk/TensorGlyph.h
index d739f8b0070297ca702cddab2046bce93284e935..f77c19abd016577425bb2930d83d0fc91a739810 100644
--- a/src/mesa_pd/vtk/TensorGlyph.h
+++ b/src/mesa_pd/vtk/TensorGlyph.h
@@ -13,7 +13,7 @@
 //  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 TensorGlyph.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesa_pd/vtk/WriteOutput.h b/src/mesa_pd/vtk/WriteOutput.h
index b6016ea796622d6b87baad35a1b01bc1d02762e5..91ef8141fc8e6944f34f97d742cadc11a94e757b 100644
--- a/src/mesa_pd/vtk/WriteOutput.h
+++ b/src/mesa_pd/vtk/WriteOutput.h
@@ -13,7 +13,7 @@
 //  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 WriteOutput.h
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/src/mesh/pe/rigid_body/ConvexPolyhedron.cpp b/src/mesh/pe/rigid_body/ConvexPolyhedron.cpp
index ed500565e0281fed065d40c96126e85dfa0c6cb6..2b505e09bde0060b510ce2e6eb746665358558f1 100644
--- a/src/mesh/pe/rigid_body/ConvexPolyhedron.cpp
+++ b/src/mesh/pe/rigid_body/ConvexPolyhedron.cpp
@@ -297,16 +297,17 @@ bool ConvexPolyhedron::containsRelPointImpl( real_t px, real_t py, real_t pz ) c
    if( px * px + py * py + pz * pz > boundingSphereRadius_ * boundingSphereRadius_ )
       return false;
 
-   for(auto fh : mesh_.faces())
-   {
-      const TriangleMesh::Normal & n = mesh_.normal(fh); // Plane normal
-      const TriangleMesh::Point & pp = mesh_.point(mesh_.to_vertex_handle(mesh_.halfedge_handle(fh))); // Point on plane
-
-      if( n[0] * (px - pp[0]) + n[1] * (py - pp[1]) + n[2] * (pz - pp[2]) >= real_t(0) )
-         return false;
-   }
-
-   return true;
+   return std::none_of(mesh_.faces().begin(),
+                       mesh_.faces().end(),
+                       [&](auto fh)
+                       {
+                          //check if point is on positive side of the face
+                          const TriangleMesh::Normal &n = mesh_.normal(fh); // Plane normal
+                          const TriangleMesh::Point &pp = mesh_.point(
+                                mesh_.to_vertex_handle(mesh_.halfedge_handle(fh))); // Point on plane
+
+                          return (n[0] * (px - pp[0]) + n[1] * (py - pp[1]) + n[2] * (pz - pp[2]) >= real_t(0));
+                       });
 }
 //*************************************************************************************************
 
diff --git a/src/mesh/python/Exports.h b/src/mesh/python/Exports.h
deleted file mode 100644
index 0ae7cfd3abbb30a7aa057217bf980c0cdafe29e3..0000000000000000000000000000000000000000
--- a/src/mesh/python/Exports.h
+++ /dev/null
@@ -1,41 +0,0 @@
-//======================================================================================================================
-//
-//  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 Exports.h
-//! \ingroup mesh
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include "waLBerlaDefinitions.h"
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-namespace walberla {
-namespace mesh {
-
-
-   template<typename FlagFields>
-   void exportModuleToPython();
-
-
-} // namespace mesh
-} // namespace walberla
-
-
-#include "Exports.impl.h"
-
-#endif // WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/mesh/python/Exports.impl.h b/src/mesh/python/Exports.impl.h
deleted file mode 100644
index 4c68eb8505252f9420e6fa3e5df9de8e8ed1571f..0000000000000000000000000000000000000000
--- a/src/mesh/python/Exports.impl.h
+++ /dev/null
@@ -1,186 +0,0 @@
-//======================================================================================================================
-//
-//  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 Exports.impl.h
-//! \ingroup mesh
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-// Do not reorder includes - the include order is important
-#include "python_coupling/PythonWrapper.h"
-#include "python_coupling/helper/ModuleScope.h"
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-#ifdef WALBERLA_BUILD_WITH_OPENMESH
-
-#include "python_coupling/Manager.h"
-#include "python_coupling/helper/MplHelpers.h"
-#include "python_coupling/helper/BlockStorageExportHelpers.h"
-#include "python_coupling/helper/PythonIterableToStdVector.h"
-#include "python_coupling/helper/SharedPtrDeleter.h"
-
-#ifdef _MSC_VER
-#  pragma warning( push )
-#  pragma warning( disable : 4456 4459 )
-#endif
-
-#include <OpenMesh/Core/IO/MeshIO.hh>
-#include <OpenMesh/Core/Mesh/TriMesh_ArrayKernelT.hh>
-
-#ifdef _MSC_VER
-#  pragma warning( pop )
-#endif
-
-#include "mesh_common/MeshIO.h"
-#include "mesh_common/distance_octree/DistanceOctree.h"
-#include "mesh_common/DistanceComputations.h"
-#include "mesh_common/DistanceFunction.h"
-#include "mesh_common/MeshOperations.h"
-#include "mesh_common/TriangleMeshes.h"
-#include "mesh/boundary/BoundarySetup.h"
-#include "mesh_common/vtk/VTKMeshWriter.h"
-
-using namespace boost::python;
-
-
-namespace walberla {
-namespace mesh {
-
-typedef mesh::DistanceOctree<PythonTriangleMesh> Octree;
-typedef mesh::VTKMeshWriter<PythonTriangleMesh> MeshWriter;
-
-namespace internal
-{
-   void exp_readAndBroadcast(const std::string & fileName, PythonTriangleMesh & mesh )
-   {
-      mesh::readAndBroadcast(fileName, mesh);
-   }
-
-
-   shared_ptr<Octree> makeDistanceOctree(object meshObj, uint_t maxDepth, uint_t minNumTriangles)
-   {
-      auto mesh = python_coupling::createSharedPtrFromPythonObject<PythonTriangleMesh>(meshObj);
-      auto triangleDistance = make_shared<TriangleDistance<PythonTriangleMesh> > (mesh);
-      return make_shared< Octree >(triangleDistance, maxDepth, minNumTriangles );
-   }
-
-   shared_ptr<MeshWriter> makeVTKMeshWriter(object meshObj, const std::string & identifier, const uint_t writeFrequency, const std::string & baseFolder)
-   {
-      shared_ptr<PythonTriangleMesh> mesh = python_coupling::createSharedPtrFromPythonObject<PythonTriangleMesh>(meshObj);
-      return walberla::make_shared< MeshWriter >( mesh, identifier, writeFrequency, baseFolder );
-   }
-
-   // ----------------------------------- markFlagField ----------------------------------------------------------------
-
-
-   template<typename FlagField>
-   void markFlagFieldTmpl(const shared_ptr<Octree> & octree, const shared_ptr<StructuredBlockStorage> & bs, BlockDataID flagFieldID,
-                          const std::string & flagName, uint_t ghostLayers)
-   {
-      BoundarySetup setup( bs, makeMeshDistanceFunction( octree ), ghostLayers);
-      setup.setFlag<FlagField>(flagFieldID, flagName, BoundarySetup::INSIDE);
-   }
-
-   FunctionExporterClass( markFlagFieldTmpl,
-                          void ( const shared_ptr<Octree> &, const shared_ptr<StructuredBlockStorage> & ,
-                                 BlockDataID ,const std::string &, uint_t ) );
-
-   template<typename FlagFields>
-   void exp_markFlagField(const shared_ptr<Octree> & octree, const shared_ptr<StructuredBlockStorage> & bs,
-                          const std::string & blockDataStr, const std::string & flagName, uint_t ghostLayers)
-   {
-      if ( bs->begin() == bs->end() )
-         return;
-      IBlock * firstBlock =  & ( * bs->begin() );
-
-      auto fieldID = python_coupling::blockDataIDFromString( *bs, blockDataStr );
-      python_coupling::Dispatcher<FlagFields, Exporter_markFlagFieldTmpl > dispatcher( firstBlock );
-      dispatcher( fieldID )(octree, bs, fieldID, flagName, ghostLayers);
-   }
-}
-
-
-bool Octree_isAABBFullyInside(const Octree & octree, const AABB & aabb)
-{
-   for( auto corner: aabb.corners() )
-   {
-      const Octree::Point p ( numeric_cast<Octree::Scalar>(corner[0]),
-                              numeric_cast<Octree::Scalar>(corner[1]),
-                              numeric_cast<Octree::Scalar>(corner[2]) );
-      if( octree.sqSignedDistance(p) > 0 )
-         return false;
-   }
-   return true;
-}
-
-
-bool Octree_isAABBFullyOutside(const Octree & octree, const AABB & aabb)
-{
-   for( auto corner: aabb.corners() )
-   {
-      const Octree::Point p ( numeric_cast<Octree::Scalar>(corner[0]),
-                              numeric_cast<Octree::Scalar>(corner[1]),
-                              numeric_cast<Octree::Scalar>(corner[2]) );
-      if( octree.sqSignedDistance(p) < 0 )
-         return false;
-   }
-   return true;
-}
-
-
-template<typename FlagFields>
-void exportModuleToPython()
-{
-   python_coupling::ModuleScope fieldModule( "mesh" );
-
-   def( "readAndBroadcast", &internal::exp_readAndBroadcast, (arg("fileName"), arg("mesh")) );
-
-   def( "aabb", &mesh::computeAABB<PythonTriangleMesh>, (arg("mesh")) );
-   def( "translate", &mesh::translate<PythonTriangleMesh>, (arg("mesh"), arg("translationVector")) );
-   def( "scale", &mesh::scale<PythonTriangleMesh>, (arg("mesh"), arg("scaleFactors")) );
-   def( "volume", &mesh::computeVolume<PythonTriangleMesh>, (arg("mesh")) );
-   def( "surfaceArea", &mesh::computeSurfaceArea<PythonTriangleMesh>, (arg("mesh")) );
-
-   def( "markFlagField", &internal::exp_markFlagField<FlagFields> );
-
-   Octree::Scalar (Octree::*sqSignedDistance1)(const Octree::Point&) const= &Octree::sqSignedDistance;
-   Octree::Scalar (Octree::*sqDistance1)(const Octree::Point&) const= &Octree::sqDistance;
-
-   class_<Octree, shared_ptr<Octree> >("DistanceOctree", no_init)
-      .def("__init__", make_constructor(internal::makeDistanceOctree, default_call_policies() ,(arg("mesh"), arg("maxDepth")=20u, arg("minNumTriangles")=25u) ) )
-      .def("sqSignedDistance", sqSignedDistance1, (arg("p")))
-      .def("sqDistance", sqDistance1, (arg("p")))
-      .def("height", &Octree::height)
-      .def("writeVTKOutput", &Octree::writeVTKOutput, (arg("filestem")))
-      .def("isAABBfullyOutside", &Octree_isAABBFullyOutside)
-      .def("isAABBfullyInside", &Octree_isAABBFullyInside)
-   ;
-
-   class_<MeshWriter, shared_ptr<MeshWriter> >("VTKMeshWriter", no_init)
-      .def("__init__", make_constructor(internal::makeVTKMeshWriter, default_call_policies() ,(arg("mesh"), arg("identifier"), arg("writeFrequency")=1u, arg("baseFolder")="vtk_out") ) )
-      .def("__call__", &MeshWriter::operator())
-   ;
-
-}
-
-
-
-} // namespace mesh
-} // namespace walberla
-
-
-#endif //WALBERLA_BUILD_WITH_PYTHON
-#endif //WALBERLA_BUILD_WITH_OPENMESH
\ No newline at end of file
diff --git a/src/mesh_common/MeshOperations.h b/src/mesh_common/MeshOperations.h
index b870817508fe2e00186fc7b0f5dfea7e944e9b70..1694edf876107b953b7e6f02f9d20d843dc9f28c 100644
--- a/src/mesh_common/MeshOperations.h
+++ b/src/mesh_common/MeshOperations.h
@@ -49,6 +49,9 @@ void translate( MeshType & mesh, const Vector3< typename MeshType::Scalar > & of
 template< typename MeshType >
 void scale( MeshType & mesh, const Vector3< typename MeshType::Scalar > & scaleFactors );
 
+template< typename MeshType >
+void rotate( MeshType& mesh, Vector3<typename  MeshType::Scalar > axis, typename MeshType::Scalar angle, Vector3< typename MeshType::scalar> axis_foot);
+
 template< typename MeshType >
 typename MeshType::Scalar computeVolume( const MeshType & mesh );
 
@@ -182,6 +185,21 @@ void scale( MeshType & mesh, const Vector3< typename MeshType::Scalar > & scaleF
    }
 }
 
+template< typename MeshType >
+void rotate( MeshType& mesh, Vector3<typename  MeshType::Scalar > axis, typename MeshType::Scalar angle, Vector3< typename MeshType::Scalar> axis_foot)
+{
+    Matrix3< typename MeshType::Scalar > mat(axis, angle);
+
+    for( auto v_it = mesh.vertices_begin(); v_it != mesh.vertices_end(); ++v_it)
+    {
+        auto &p = mesh.point(*v_it);
+        p -= mesh::toOpenMesh(axis_foot);
+        p = mat*p;
+        p += mesh::toOpenMesh(axis_foot);
+    }
+}
+
+
 
 template< typename MeshType >
 typename MeshType::Scalar computeVolume( const MeshType & mesh )
diff --git a/src/pde/sweeps/Jacobi.h b/src/pde/sweeps/Jacobi.h
index 89f3e52779b30f78c621ed235eb27f349fd7ea8f..9f8b4e714d2f94be4038ffbe36d38d6ab6c3172c 100644
--- a/src/pde/sweeps/Jacobi.h
+++ b/src/pde/sweeps/Jacobi.h
@@ -55,10 +55,10 @@ public:
 template< typename Stencil_T >
 void Jacobi< Stencil_T >::operator()( IBlock * const block )
 {
-   Field_T * sf( NULL );
-   Field_T * df( NULL );
-   Field_T * ff( NULL );
-   StencilField_T * stencil( NULL );
+   Field_T * sf( nullptr );
+   Field_T * df( nullptr );
+   Field_T * ff( nullptr );
+   StencilField_T * stencil( nullptr );
    this->getFields( block, sf, df, ff, stencil );
 
    WALBERLA_ASSERT_GREATER_EQUAL( sf->nrOfGhostLayers(), 1 );
diff --git a/src/pde/sweeps/JacobiFixedStencil.h b/src/pde/sweeps/JacobiFixedStencil.h
index eb1367aae7f5f1a3f9dccce8202e70453e641bf7..4efc6fc5e8c9b7726ee5adae50c2207873648410 100644
--- a/src/pde/sweeps/JacobiFixedStencil.h
+++ b/src/pde/sweeps/JacobiFixedStencil.h
@@ -55,9 +55,9 @@ public:
 template< typename Stencil_T >
 void JacobiFixedStencil< Stencil_T >::operator()( IBlock * const block )
 {
-   Field_T * sf( NULL );
-   Field_T * df( NULL );
-   Field_T * ff( NULL );
+   Field_T * sf( nullptr );
+   Field_T * df( nullptr );
+   Field_T * ff( nullptr );
    this->getFields( block, sf, df, ff );
 
    WALBERLA_ASSERT_GREATER_EQUAL( sf->nrOfGhostLayers(), 1 );
diff --git a/src/pde/sweeps/RBGS.h b/src/pde/sweeps/RBGS.h
index 69f7245fac91a5e77ec5bdddb2b25fa92d45d253..e8d800aa5fcad9a6181ac4fa1f01b60115bbe4d1 100644
--- a/src/pde/sweeps/RBGS.h
+++ b/src/pde/sweeps/RBGS.h
@@ -77,9 +77,9 @@ void RBGS< Stencil_T >::update( IBlock * const block, const bool rb )
       WALBERLA_ASSERT( realIsIdentical( dir.length(), real_t(1) ) );
 #endif
 
-   Field_T * uf( NULL );
-   Field_T * ff( NULL );
-   StencilField_T * stencil( NULL );
+   Field_T * uf( nullptr );
+   Field_T * ff( nullptr );
+   StencilField_T * stencil( nullptr );
    this->getFields( block, uf, ff, stencil );
 
    WALBERLA_ASSERT_GREATER_EQUAL( uf->nrOfGhostLayers(), 1 );
diff --git a/src/pde/sweeps/RBGSFixedStencil.h b/src/pde/sweeps/RBGSFixedStencil.h
index 75196987f18a941a716661f95873c8c960276904..5b57cc351de611f8046f9b3edf82aab084b5f5be 100644
--- a/src/pde/sweeps/RBGSFixedStencil.h
+++ b/src/pde/sweeps/RBGSFixedStencil.h
@@ -76,8 +76,8 @@ void RBGSFixedStencil< Stencil_T >::update( IBlock * const block, const bool rb
       WALBERLA_ASSERT( realIsIdentical( dir.length(), real_t(1) ) );
 #endif
 
-   Field_T * uf( NULL );
-   Field_T * ff( NULL );
+   Field_T * uf( nullptr );
+   Field_T * ff( nullptr );
    this->getFields( block, uf, ff );
 
    WALBERLA_ASSERT_GREATER_EQUAL( uf->nrOfGhostLayers(), 1 );
diff --git a/src/pde/sweeps/SOR.h b/src/pde/sweeps/SOR.h
index 4997b40d6af610f7e995ae7229339dbb08e97b93..48a0b56e35561d06f5112fad685e12600da6a586 100644
--- a/src/pde/sweeps/SOR.h
+++ b/src/pde/sweeps/SOR.h
@@ -77,9 +77,9 @@ void SOR< Stencil_T >::update( IBlock * const block, const bool rb )
       WALBERLA_ASSERT( realIsIdentical( dir.length(), real_t(1) ) );
 #endif
 
-   Field_T * uf( NULL );
-   Field_T * ff( NULL );
-   StencilField_T * stencil( NULL );
+   Field_T * uf( nullptr );
+   Field_T * ff( nullptr );
+   StencilField_T * stencil( nullptr );
    this->getFields( block, uf, ff, stencil );
 
    WALBERLA_ASSERT_GREATER_EQUAL( uf->nrOfGhostLayers(), 1 );
diff --git a/src/pde/sweeps/SORFixedStencil.h b/src/pde/sweeps/SORFixedStencil.h
index d3983ab1861c856c388d7c2feb3601c654f0e58f..0e9387309fe0e7009aabd9b21d87347f9b0a9254 100644
--- a/src/pde/sweeps/SORFixedStencil.h
+++ b/src/pde/sweeps/SORFixedStencil.h
@@ -76,8 +76,8 @@ void SORFixedStencil< Stencil_T >::update( IBlock * const block, const bool rb )
       WALBERLA_ASSERT( realIsIdentical( dir.length(), real_t(1) ) );
 #endif
 
-   Field_T * uf( NULL );
-   Field_T * ff( NULL );
+   Field_T * uf( nullptr );
+   Field_T * ff( nullptr );
    this->getFields( block, uf, ff );
 
    WALBERLA_ASSERT_GREATER_EQUAL( uf->nrOfGhostLayers(), 1 );
diff --git a/src/pde/sweeps/SweepBase.h b/src/pde/sweeps/SweepBase.h
index 031a3ee5917697b56697ca723d1bdb79406cc900..e8fe9fb43509ef9af54080146f6fcc310955cc03 100644
--- a/src/pde/sweeps/SweepBase.h
+++ b/src/pde/sweeps/SweepBase.h
@@ -39,7 +39,7 @@ class SweepBase
 {
 public:
 
-   typedef GhostLayerField< real_t, 1 > Field_T;
+   using Field_T = GhostLayerField<real_t, 1>;
 
    // block has NO dst u field
    SweepBase( const BlockDataID & uFieldId, const BlockDataID & fFieldId ) :
diff --git a/src/pe/Materials.h b/src/pe/Materials.h
index d4d54b96ccc08740b6cbc6222f2d09da763cb819..15863462f6de1e896f2f034c605c11ae100e2179 100644
--- a/src/pe/Materials.h
+++ b/src/pe/Materials.h
@@ -95,7 +95,7 @@ class WALBERLA_PUBLIC Material
 {
 private:
    //**Type definitions****************************************************************************
-   typedef Materials::size_type  SizeType;  //!< Size type of the Material class.
+   using SizeType = Materials::size_type;  //!< Size type of the Material class.
    //**********************************************************************************************
 
 public:
diff --git a/src/pe/Types.h b/src/pe/Types.h
index d66c9df9235bbde5948fb5e32320b01e6f14cf55..645f167807a17bdc2862590ba739fc8eb83d5c1e 100644
--- a/src/pe/Types.h
+++ b/src/pe/Types.h
@@ -42,12 +42,12 @@ template<typename T> class MatrixMxN;
 }
 namespace pe{
 
-typedef math::Vector2<real_t> Vec2;
-typedef math::Vector3<real_t> Vec3;
-typedef math::Matrix2<real_t> Mat2;
-typedef math::Matrix3<real_t> Mat3;
-typedef math::Quaternion<real_t>  Quat;
-typedef math::MatrixMxN<real_t> MatN;
+using Vec2 = math::Vector2<real_t>;
+using Vec3 = math::Vector3<real_t>;
+using Mat2 = math::Matrix2<real_t>;
+using Mat3 = math::Matrix3<real_t>;
+using Quat = math::Quaternion<real_t>;
+using MatN = math::MatrixMxN<real_t>;
 
 using math::AABB;
 
@@ -77,67 +77,67 @@ class TriangleMesh;
 template <typename... BodyTypes>
 class Union;
 
-typedef RigidBody             BodyType;            //!< Type of the rigid bodies.
-typedef RigidBody*            BodyID;              //!< Handle for a rigid body.
-typedef const RigidBody*      ConstBodyID;         //!< Handle for a constant rigid body.
+using BodyType = RigidBody;            //!< Type of the rigid bodies.
+using BodyID = RigidBody *;              //!< Handle for a rigid body.
+using ConstBodyID = const RigidBody *;         //!< Handle for a constant rigid body.
 using   BodyPtr             = std::unique_ptr<RigidBody>;
 
-typedef GeomPrimitive*  GeomID;
-typedef const GeomPrimitive*  ConstGeomID;
+using GeomID = GeomPrimitive *;
+using ConstGeomID = const GeomPrimitive *;
 
-typedef Box                   BoxType;             //!< Type of the box geometric primitive.
-typedef Box*                  BoxID;               //!< Handle for a box primitive.
-typedef const Box*            ConstBoxID;          //!< Handle for a constant box primitive.
+using BoxType = Box;             //!< Type of the box geometric primitive.
+using BoxID = Box *;               //!< Handle for a box primitive.
+using ConstBoxID = const Box *;          //!< Handle for a constant box primitive.
 using   BoxPtr              = std::unique_ptr<Box>;
 
-typedef Capsule               CapsuleType;         //!< Type of the capsule geometric primitive.
-typedef Capsule*              CapsuleID;           //!< Handle for a capsule primitive.
-typedef const Capsule*        ConstCapsuleID;      //!< Handle for a constant capsule primitive.
+using CapsuleType = Capsule;         //!< Type of the capsule geometric primitive.
+using CapsuleID = Capsule *;           //!< Handle for a capsule primitive.
+using ConstCapsuleID = const Capsule *;      //!< Handle for a constant capsule primitive.
 using   CapsulePtr          = std::unique_ptr<Capsule>;
 
-typedef Cylinder              CylinderType;        //!< Type of the cylinder geometric primitive.
-typedef Cylinder*             CylinderID;          //!< Handle for a cylinder primitive.
-typedef const Cylinder*       ConstCylinderID;     //!< Handle for a constant cylinder primitive.
+using CylinderType = Cylinder;        //!< Type of the cylinder geometric primitive.
+using CylinderID = Cylinder *;          //!< Handle for a cylinder primitive.
+using ConstCylinderID = const Cylinder *;     //!< Handle for a constant cylinder primitive.
 using   CylinderPtr         = std::unique_ptr<Cylinder>;
 
-typedef CylindricalBoundary        CylindricalBoundaryType;        //!< Type of the cylindrical boundary geometric primitive.
-typedef CylindricalBoundary*       CylindricalBoundaryID;          //!< Handle for a cylindrical boundary primitive.
-typedef const CylindricalBoundary* ConstCylindricalBoundaryID;     //!< Handle for a constant cylindrical boundary primitive.
+using CylindricalBoundaryType = CylindricalBoundary;        //!< Type of the cylindrical boundary geometric primitive.
+using CylindricalBoundaryID = CylindricalBoundary *;          //!< Handle for a cylindrical boundary primitive.
+using ConstCylindricalBoundaryID = const CylindricalBoundary *;     //!< Handle for a constant cylindrical boundary primitive.
 using   CylindricalBoundaryPtr   = std::unique_ptr<CylindricalBoundary>;
 
-typedef Ellipsoid             EllipsoidType;       //!< Type of the ellipsoid geometric primitive.
-typedef Ellipsoid*            EllipsoidID;         //!< Handle for a ellipsoid primitive.
-typedef const Ellipsoid*      ConstEllipsoidID;    //!< Handle for a constant ellipsoid primitive.
+using EllipsoidType = Ellipsoid;       //!< Type of the ellipsoid geometric primitive.
+using EllipsoidID = Ellipsoid *;         //!< Handle for a ellipsoid primitive.
+using ConstEllipsoidID = const Ellipsoid *;    //!< Handle for a constant ellipsoid primitive.
 using   EllipsoidPtr        = std::unique_ptr<Ellipsoid>;
 
-typedef Plane                 PlaneType;           //!< Type of the plane geometric primitive.
-typedef Plane*                PlaneID;             //!< Handle for a plane primitive.
-typedef const Plane*          ConstPlaneID;        //!< Handle for a constant plane primitive.
+using PlaneType = Plane;           //!< Type of the plane geometric primitive.
+using PlaneID = Plane *;             //!< Handle for a plane primitive.
+using ConstPlaneID = const Plane *;        //!< Handle for a constant plane primitive.
 using   PlanePtr            = std::unique_ptr<Plane>;
 
-typedef Sphere                SphereType;          //!< Type of the sphere geometric primitive.
-typedef Sphere*               SphereID;            //!< Handle for a sphere primitive.
-typedef const Sphere*         ConstSphereID;       //!< Handle for a constant sphere primitive.
+using SphereType = Sphere;          //!< Type of the sphere geometric primitive.
+using SphereID = Sphere *;            //!< Handle for a sphere primitive.
+using ConstSphereID = const Sphere *;       //!< Handle for a constant sphere primitive.
 using   SpherePtr           = std::unique_ptr<Sphere>;
 
-typedef Squirmer              SquirmerType;        //!< Type of the squirmer geometric primitive.
-typedef Squirmer*             SquirmerID;          //!< Handle for a squirmer primitive.
-typedef const Squirmer*       ConstSquirmerID;     //!< Handle for a constant squirmer primitive.
+using SquirmerType = Squirmer;        //!< Type of the squirmer geometric primitive.
+using SquirmerID = Squirmer *;          //!< Handle for a squirmer primitive.
+using ConstSquirmerID = const Squirmer *;     //!< Handle for a constant squirmer primitive.
 using   SquirmerPtr         = std::unique_ptr<Squirmer>;
 
-typedef TriangleMesh          MeshType;             //!< Type of the triangle mesh geometric primitive.
-typedef TriangleMesh*         MeshID;               //!< Handle for a triangle mesh primitive.
-typedef const TriangleMesh*   ConstMeshID;          //!< Handle for a constant triangle mesh primitive.
+using MeshType = TriangleMesh;             //!< Type of the triangle mesh geometric primitive.
+using MeshID = TriangleMesh *;               //!< Handle for a triangle mesh primitive.
+using ConstMeshID = const TriangleMesh *;          //!< Handle for a constant triangle mesh primitive.
 using   TriangleMeshPtr     = std::unique_ptr<TriangleMesh>;
 
-typedef Contact               ContactType;         //!< Type of the contacts.
-typedef Contact*              ContactID;           //!< Handle for a contact.
-typedef const Contact*        ConstContactID;      //!< Handle for a constant contact.
+using ContactType = Contact;         //!< Type of the contacts.
+using ContactID = Contact *;           //!< Handle for a contact.
+using ConstContactID = const Contact *;      //!< Handle for a constant contact.
 
-typedef BodyManager*          ManagerID;           //!< Handle for a BodyManager.
-typedef const BodyManager*    ConstManagerID;      //!< Handle for a constant BodyManager.
+using ManagerID = BodyManager *;           //!< Handle for a BodyManager.
+using ConstManagerID = const BodyManager *;      //!< Handle for a constant BodyManager.
 
-typedef std::vector<Material>  Materials;          //!< Vector for materials.
+using Materials = std::vector<Material>;          //!< Vector for materials.
 
 
 //*************************************************************************************************
@@ -156,14 +156,14 @@ typedef std::vector<Material>  Materials;          //!< Vector for materials.
    MaterialID myMaterial = createMaterial( "myMaterial", real_c(2.54), real_c(0.8), real_c(0.1), real_c(0.05) );
    \endcode
  */
-typedef Materials::size_type  MaterialID;
+using MaterialID = Materials::size_type;
 //*************************************************************************************************
 
 ///Output data type of coarse collision detection module
-typedef std::vector<std::pair<BodyID, BodyID> > PossibleContacts;
+using PossibleContacts = std::vector<std::pair<BodyID, BodyID>>;
 
 ///Output data type of fine collision detection module
-typedef std::vector< Contact > Contacts;
+using Contacts = std::vector<Contact>;
 
 ///Class enum for BodyStorages
 /// \see Storage
@@ -187,7 +187,7 @@ struct StorageSelect
 };
 
 ///Container for local and shadow body storage
-typedef std::array<BodyStorage, 2> Storage;
+using Storage = std::array<BodyStorage, 2>;
 
 }  // namespace pe
 }  // namespace walberla
diff --git a/src/pe/amr/weight_assignment/MetisAssignmentFunctor.h b/src/pe/amr/weight_assignment/MetisAssignmentFunctor.h
index 4d0aeb411c46a77749f8a13226d9dac4824ca245..f0797401ed1eb68c002fe905722746661c755ced 100644
--- a/src/pe/amr/weight_assignment/MetisAssignmentFunctor.h
+++ b/src/pe/amr/weight_assignment/MetisAssignmentFunctor.h
@@ -40,9 +40,9 @@ class MetisAssignmentFunctor
 {
 public:
    ///Convenience typedef to be used as PhantomBlock weight in conjunction with this weight assignment functor.
-   typedef blockforest::DynamicParMetisBlockInfo           PhantomBlockWeight;
+   using PhantomBlockWeight = blockforest::DynamicParMetisBlockInfo;
    ///Convenience typdef for pack and unpack functions to be used in conjunction with PhantomBlockWeight.
-   typedef blockforest::DynamicParMetisBlockInfoPackUnpack PhantomBlockWeightPackUnpackFunctor;
+   using PhantomBlockWeightPackUnpackFunctor = blockforest::DynamicParMetisBlockInfoPackUnpack;
 
    MetisAssignmentFunctor( shared_ptr<blockforest::InfoCollection>& ic, const real_t baseWeight = real_t(10.0) ) : ic_(ic), baseWeight_(baseWeight) {}
 
diff --git a/src/pe/amr/weight_assignment/WeightAssignmentFunctor.h b/src/pe/amr/weight_assignment/WeightAssignmentFunctor.h
index 66f267af0510bce6cb60ce4d74fb30d5594d9e7c..069efae7e14d587208d50558e3f4d3180ca47d7b 100644
--- a/src/pe/amr/weight_assignment/WeightAssignmentFunctor.h
+++ b/src/pe/amr/weight_assignment/WeightAssignmentFunctor.h
@@ -38,9 +38,9 @@ class WeightAssignmentFunctor
 {
 public:
    ///Convenience typedef to be used as PhantomBlock weight in conjunction with this weight assignment functor.
-   typedef walberla::blockforest::PODPhantomWeight<double>           PhantomBlockWeight;
+   using PhantomBlockWeight = walberla::blockforest::PODPhantomWeight<double>;
    ///Convenience typdef for pack and unpack functions to be used in conjunction with PhantomBlockWeight.
-   typedef walberla::blockforest::PODPhantomWeightPackUnpack<double> PhantomBlockWeightPackUnpackFunctor;
+   using PhantomBlockWeightPackUnpackFunctor = walberla::blockforest::PODPhantomWeightPackUnpack<double>;
 
    WeightAssignmentFunctor( shared_ptr<blockforest::InfoCollection>& ic, const real_t baseWeight = real_t(10.0) ) : ic_(ic), baseWeight_(baseWeight) {}
 
diff --git a/src/pe/bg/IBG.h b/src/pe/bg/IBG.h
index 8c956b5675bb57fd2b02e701dedb39d7beb78d54..fa7ec0662a0a3699434a936de9580a454f1f0e94 100644
--- a/src/pe/bg/IBG.h
+++ b/src/pe/bg/IBG.h
@@ -29,7 +29,7 @@ namespace bg {
 class IBG : private NonCopyable
 {
 public:
-   virtual ~IBG() {};
+   virtual ~IBG() = default;
 };
 
 //*************************************************************************************************
diff --git a/src/pe/bg/SimpleBGDataHandling.h b/src/pe/bg/SimpleBGDataHandling.h
index 7b31e020ba98d5622486e35bbdba5d108e7c32e4..79535c82bf4cf537c3db9edf28f7c011c602de58 100644
--- a/src/pe/bg/SimpleBGDataHandling.h
+++ b/src/pe/bg/SimpleBGDataHandling.h
@@ -30,7 +30,7 @@ namespace bg {
 
 class SimpleBGDataHandling : public blockforest::AlwaysInitializeBlockDataHandling<SimpleBG>{
 public:
-    SimpleBG * initialize( IBlock * const /*block*/ ) {return new SimpleBG();}
+    SimpleBG * initialize( IBlock * const /*block*/ ) override {return new SimpleBG();}
 };
 
 inline
diff --git a/src/pe/ccd/HashGrids.h b/src/pe/ccd/HashGrids.h
index 24ecec304fea6ee52f02ae52b5c6c13500d10303..cbfe0471da80d0ced8bc6851b884b7cc34034ffa 100644
--- a/src/pe/ccd/HashGrids.h
+++ b/src/pe/ccd/HashGrids.h
@@ -103,7 +103,7 @@ public:
 private:
    //**Type definitions****************************************************************************
    //! Vector for storing (handles to) rigid bodies.
-   typedef std::vector<BodyID>  BodyVector;
+   using BodyVector = std::vector<BodyID>;
    //**********************************************************************************************
 
    //**********************************************************************************************
@@ -114,7 +114,7 @@ private:
     private:
       //**Type definitions*************************************************************************
       //! The signed integer type that is used for storing offsets to neighboring cells.
-      typedef long offset_t;
+      using offset_t = long;
       //*******************************************************************************************
 
       //*******************************************************************************************
@@ -139,7 +139,7 @@ private:
 
       //**Type definitions*************************************************************************
       //! Vector for storing pointers to (body-occupied) cells.
-      typedef std::vector<Cell*>  CellVector;
+      using CellVector = std::vector<Cell *>;
       //*******************************************************************************************
 
     public:
@@ -262,7 +262,7 @@ private:
    //! List for storing all the hash grids that are in use.
    /*! This data structure is used to represent the grid hierarchy. All hash grids are stored in
        ascending order by the size of their cells. */
-   typedef std::list<HashGrid*>  GridList;
+   using GridList = std::list<HashGrid *>;
    //**********************************************************************************************
 
 public:
@@ -277,7 +277,7 @@ public:
    //**Destructor**********************************************************************************
    /*!\name Destructor */
    //@{
-   ~HashGrids();
+   ~HashGrids() override;
    //@}
    //**********************************************************************************************
 
@@ -286,7 +286,7 @@ public:
    //@{
    inline void add   ( BodyID body );
           void remove( BodyID body );
-   inline int getObservedBodyCount() const { return observedBodyCount_ + static_cast<int> (globalStorage_.size()); }
+   inline int getObservedBodyCount() const override { return observedBodyCount_ + static_cast<int> (globalStorage_.size()); }
    //@}
    //**********************************************************************************************
 
@@ -294,13 +294,13 @@ public:
    /*!\name Utility functions */
    //@{
           void clear       ();
-          void reloadBodies();
+          void reloadBodies() override;
    //@}
    //**********************************************************************************************
 
    //**Implementation of ICCD interface ********************************************************
-   virtual PossibleContacts& generatePossibleContacts( WcTimingTree* tt = NULL );
-   void update(WcTimingTree* tt = NULL);
+   PossibleContacts& generatePossibleContacts( WcTimingTree* tt = nullptr ) override;
+   void update(WcTimingTree* tt = nullptr);
    
    bool active() const { return gridActive_; }
    
@@ -423,7 +423,7 @@ size_t HashGrids::HashGrid::process( BodyID** gridBodies, Contacts& contacts ) c
          Cell*       nbCell   = (*cell) + (*cell)->neighborOffset_[i];
          BodyVector* nbBodies = nbCell->bodies_;
 
-         if( nbBodies != NULL )
+         if( nbBodies != nullptr )
          {
             for( auto aIt = cellBodies->begin(); aIt < cellBodies->end(); ++aIt ) {
                auto endNeighbour = nbBodies->begin();
@@ -480,7 +480,7 @@ void HashGrids::HashGrid::processBodies( BodyID* bodies, size_t bodyCount, Conta
          Cell*       nbCell   = cell + cell->neighborOffset_[i];
          BodyVector* nbBodies = nbCell->bodies_;
 
-         if( nbBodies != NULL ) {
+         if( nbBodies != nullptr ) {
             auto endNeighbour = nbBodies->begin();
             if ((*aIt)->isFixed())
             {
@@ -521,7 +521,7 @@ BodyID HashGrids::HashGrid::getBodyIntersectionForBlockCell(const Vector3<int32_
                                                             std::function<bool (const BodyID body)> isBodyVisibleFunc) const {
    real_t t_local;
    Vec3 n_local;
-   BodyID body = NULL;
+   BodyID body = nullptr;
    
    raytracing::IntersectsFunctor intersectsFunc(ray, t_local, n_local);
    
@@ -577,7 +577,7 @@ BodyID HashGrids::HashGrid::getBodyIntersectionForBlockCell(const Vector3<int32_
       const Cell* nbCell = &centerCell + centerCell.neighborOffset_[neighborIndex];
       const BodyVector* nbBodies = nbCell->bodies_;
       
-      if (nbBodies != NULL) {
+      if (nbBodies != nullptr) {
          for (const BodyID& cellBody: *nbBodies) {
             if (cellBody->isRemote()) {
                continue;
@@ -618,8 +618,8 @@ BodyID HashGrids::HashGrid::getRayIntersectingBody(const raytracing::Ray& ray, c
                                                    std::function<bool (const BodyID body)> isBodyVisibleFunc) const {
    const real_t realMax = std::numeric_limits<real_t>::max();
    
-   BodyID body_local = NULL;
-   BodyID body_closest = NULL;
+   BodyID body_local = nullptr;
+   BodyID body_closest = nullptr;
    
    int32_t blockXCellCountMin = int32_c(blockAABB.xMin() * inverseCellSpan_) - 1;
    int32_t blockXCellCountMax = int32_c(std::ceil(blockAABB.xMax() * inverseCellSpan_)) + 1;
@@ -642,7 +642,7 @@ BodyID HashGrids::HashGrid::getRayIntersectingBody(const raytracing::Ray& ray, c
          firstPointCenteredInCell = firstPoint - firstPointNormal * (cellSpan_/real_t(2));
          tRayOriginToGrid = (ray.getOrigin() - firstPoint).length();
       } else {
-         return NULL;
+         return nullptr;
       }
    }
    
@@ -679,7 +679,7 @@ BodyID HashGrids::HashGrid::getRayIntersectingBody(const raytracing::Ray& ray, c
       body_local = getBodyIntersectionForBlockCell<BodyTuple>(currentCell, BLOCKCELL_NORMAL_INDETERMINATE, 0,
                                                               ray, t_closest, n_closest,
                                                               isBodyVisibleFunc);
-      if (body_local != NULL) {
+      if (body_local != nullptr) {
          body_closest = body_local;
       }
    }
@@ -749,7 +749,7 @@ BodyID HashGrids::HashGrid::getRayIntersectingBody(const raytracing::Ray& ray, c
       body_local = getBodyIntersectionForBlockCell<BodyTuple>(currentCell, blockCellNormalAxis, blockCellNormalDir,
                                                               ray, t_closest, n_closest,
                                                               isBodyVisibleFunc);
-      if (body_local != NULL) {
+      if (body_local != nullptr) {
          body_closest = body_local;
       }
    }
@@ -771,7 +771,7 @@ BodyID HashGrids::getClosestBodyIntersectingWithRay(const raytracing::Ray& ray,
                                                     std::function<bool (const BodyID body)> isBodyVisibleFunc) const {
    const real_t realMax = std::numeric_limits<real_t>::max();
 
-   BodyID body_closest = NULL;
+   BodyID body_closest = nullptr;
    real_t t_closest = realMax;
    Vec3 n_closest;
    
@@ -791,7 +791,7 @@ BodyID HashGrids::getClosestBodyIntersectingWithRay(const raytracing::Ray& ray,
    
    for(auto grid: gridList_) {
       body_local = grid->getRayIntersectingBody<BodyTuple>(ray, blockAABB, t_closest, n_closest, isBodyVisibleFunc);
-      if (body_local != NULL){
+      if (body_local != nullptr){
          body_closest = body_local;
       }
    }
diff --git a/src/pe/ccd/HashGridsBodyTrait.h b/src/pe/ccd/HashGridsBodyTrait.h
index 40ab2505ecaffd12710fe6116f87dd2fca404769..3e82abcecef147eada6364c0d0b5a42f4660150d 100644
--- a/src/pe/ccd/HashGridsBodyTrait.h
+++ b/src/pe/ccd/HashGridsBodyTrait.h
@@ -88,7 +88,7 @@ protected:
  * \param body The rigid body containing this bounding box.
  */
 inline HashGridsBodyTrait::HashGridsBodyTrait( )
-   : grid_  (0)  // Pointer to the hash grid this rigid body is currently assigned to
+   : grid_  (nullptr)  // Pointer to the hash grid this rigid body is currently assigned to
    , hash_  (0)  // Current hash value of this rigid body
    , cellId_(0)  // The body's index in the body container of the grid cell this rigid body is currently assigned to
 {}
diff --git a/src/pe/ccd/HashGridsDataHandling.h b/src/pe/ccd/HashGridsDataHandling.h
index 43aa61863d15d97bef36c1355721ae9b070698a3..7190343c398f5e7b1f78bc7c7f3ed0d2b33dca83 100644
--- a/src/pe/ccd/HashGridsDataHandling.h
+++ b/src/pe/ccd/HashGridsDataHandling.h
@@ -33,7 +33,7 @@ namespace ccd {
 class HashGridsDataHandling : public blockforest::AlwaysInitializeBlockDataHandling<HashGrids>{
 public:
    HashGridsDataHandling(const shared_ptr<BodyStorage>& globalStorage, const BlockDataID& storageID) : globalStorage_(globalStorage), storageID_(storageID) {}
-   HashGrids * initialize( IBlock * const block )
+   HashGrids * initialize( IBlock * const block ) override
    {
       Storage* storage = block->getData< Storage >( storageID_ );
       return new HashGrids(*globalStorage_, (*storage)[0], (*storage)[1]);
diff --git a/src/pe/ccd/ICCD.h b/src/pe/ccd/ICCD.h
index b47a301e4f4515b90dd7b53a756f6e34822b5106..8aeb8d50ac9ede85a2729ed29a6f865444c329d1 100644
--- a/src/pe/ccd/ICCD.h
+++ b/src/pe/ccd/ICCD.h
@@ -33,11 +33,11 @@ namespace ccd {
 
 class ICCD : private NonCopyable {
 public:
-   virtual ~ICCD() {}
+   virtual ~ICCD() = default;
 
    /// Generates a list of possible contact pairs.
    /// This list is also stored in the member variable contacts_ for reuse lateron.
-   virtual PossibleContacts& generatePossibleContacts( WcTimingTree* tt = NULL ) = 0;
+   virtual PossibleContacts& generatePossibleContacts( WcTimingTree* tt = nullptr ) = 0;
    PossibleContacts& getPossibleContacts() {return contacts_;}
 
    virtual void reloadBodies() {}
diff --git a/src/pe/ccd/SimpleCCD.h b/src/pe/ccd/SimpleCCD.h
index 42a54f5993cf18bb5a9ee2073eb940d919660cfd..f00a0faee690cba756b980b38f15e13b627c3d69 100644
--- a/src/pe/ccd/SimpleCCD.h
+++ b/src/pe/ccd/SimpleCCD.h
@@ -29,11 +29,11 @@ namespace ccd {
 class SimpleCCD : public ICCD{
 public:
    explicit SimpleCCD(BodyStorage& globalStorage, Storage& storage);
-   ~SimpleCCD();
+   ~SimpleCCD() override;
 
-   virtual PossibleContacts& generatePossibleContacts( WcTimingTree* tt = NULL );
+   PossibleContacts& generatePossibleContacts( WcTimingTree* tt = nullptr ) override;
 
-   int getObservedBodyCount() const;
+   int getObservedBodyCount() const override;
 private:
    //**Add/remove functions*********************************************************************
    /*!\name Add/remove functions */
diff --git a/src/pe/ccd/SimpleCCDDataHandling.h b/src/pe/ccd/SimpleCCDDataHandling.h
index e6eba77091c6ea9a72aa8f5659f82d76d5f04116..2cf7756b7f6e6bee7ab5cab79b287200ca5f360e 100644
--- a/src/pe/ccd/SimpleCCDDataHandling.h
+++ b/src/pe/ccd/SimpleCCDDataHandling.h
@@ -33,7 +33,7 @@ namespace ccd {
 class SimpleCCDDataHandling : public blockforest::AlwaysInitializeBlockDataHandling<SimpleCCD>{
 public:
    SimpleCCDDataHandling(const shared_ptr<BodyStorage>& globalStorage, const BlockDataID& storageID) : globalStorage_(globalStorage), storageID_(storageID) {}
-   SimpleCCD * initialize( IBlock * const block )
+   SimpleCCD * initialize( IBlock * const block ) override
    {
       Storage* storage = block->getData< Storage >( storageID_ );
       return new SimpleCCD(*globalStorage_, *storage);
diff --git a/src/pe/collision/EPA.cpp b/src/pe/collision/EPA.cpp
index 6e7eb97551070580c1686139876341868d5923d7..171dc85949828e61adb9df099c7141867b6a93fc 100644
--- a/src/pe/collision/EPA.cpp
+++ b/src/pe/collision/EPA.cpp
@@ -350,11 +350,7 @@ bool EPA::doEPA( GeomPrimitive &geom1, GeomPrimitive &geom2, const GJK& gjk, Vec
                      penetrationDepth = -supp_dist + real_t(2.0) * margin;
                      retNormal = -ctr;
                      
-                     if(penetrationDepth < contactThreshold){
-                        return true;
-                     }else{
-                        return false;
-                     }
+                     return penetrationDepth < contactThreshold;
                   } else {
                      //Reject sphere
                      removeSupportMargin(epaVolume, supportA, supportB);
@@ -488,12 +484,7 @@ bool EPA::doEPA( GeomPrimitive &geom1, GeomPrimitive &geom2, const GJK& gjk, Vec
    std::cerr << "entryHeap[0]->getSqrDist()=" << entryHeap[0]->getSqrDist() << std::endl;*/
    //std::cout << "EPA penetration depth: " << penetrationDepth <<  std::endl;
 
-   if(penetrationDepth < contactThreshold) {
-      return true;
-   }
-
-   //no intersection found!
-   return false;
+   return penetrationDepth < contactThreshold;
 }
 //*************************************************************************************************
 
@@ -705,11 +696,7 @@ inline bool EPA::originInTetrahedron( const Vec3& p0, const Vec3& p1, const Vec3
       return false;
    }
    Vec3 normal3T = (p0 -p3) % (p1-p3);
-   if( (normal3T*p3 > real_t(0.0)) == (normal3T*p2 > real_t(0.0)) ) {
-      return false;
-   }
-
-   return true;
+   return (normal3T*p3 > real_t(0.0)) != (normal3T*p2 > real_t(0.0));
 }
 //*************************************************************************************************
 
diff --git a/src/pe/collision/EPA.h b/src/pe/collision/EPA.h
index a318e7e578323e4237e51492b3ef250d7838427c..7bc5840d1311ceec2c21f2054f86ca25a7773a98 100644
--- a/src/pe/collision/EPA.h
+++ b/src/pe/collision/EPA.h
@@ -64,9 +64,9 @@ private :
    class EPA_Triangle;
    class EPA_TriangleComp;
 
-   typedef std::vector<EPA_Triangle>  EPA_EntryBuffer;
-   typedef std::vector<EPA_Triangle*> EPA_EntryHeap;
-   typedef std::vector<EPA_Edge>      EPA_EdgeBuffer;
+   using EPA_EntryBuffer = std::vector<EPA_Triangle>;
+   using EPA_EntryHeap = std::vector<EPA_Triangle *>;
+   using EPA_EdgeBuffer = std::vector<EPA_Edge>;
    //**********************************************************************************************
 
 public:
diff --git a/src/pe/cr/DEM.h b/src/pe/cr/DEM.h
index f716157ef9f3302bf05195a3a280f534f9c280b1..21e1e16cf1088a2a1c53dcc7ca891bb439a6017a 100644
--- a/src/pe/cr/DEM.h
+++ b/src/pe/cr/DEM.h
@@ -51,7 +51,7 @@ public:
             , domain_decomposition::BlockDataID storageID
             , domain_decomposition::BlockDataID ccdID
             , domain_decomposition::BlockDataID fcdID
-            , WcTimingTree*                     tt = NULL);
+            , WcTimingTree*                     tt = nullptr);
 
    /// forwards to timestep
    /// Convenience operator to make class a functor.
@@ -61,9 +61,9 @@ public:
 
    inline Integrator                getIntegrator()                const { return integrate_; }
    inline ContactResolver           getContactResolver()           const { return resolveContact_; }
-   virtual inline real_t            getMaximumPenetration()        const override { return maxPenetration_; }
-   virtual inline size_t            getNumberOfContacts()          const override { return numberOfContacts_; }
-   virtual inline size_t            getNumberOfContactsTreated()   const override { return numberOfContactsTreated_; }
+   inline real_t            getMaximumPenetration()        const override { return maxPenetration_; }
+   inline size_t            getNumberOfContacts()          const override { return numberOfContacts_; }
+   inline size_t            getNumberOfContactsTreated()   const override { return numberOfContactsTreated_; }
 private:
    Integrator                        integrate_;
    ContactResolver                   resolveContact_;
@@ -87,7 +87,7 @@ public:
        , domain_decomposition::BlockDataID storageID
        , domain_decomposition::BlockDataID ccdID
        , domain_decomposition::BlockDataID fcdID
-       , WcTimingTree*                     tt = NULL)
+       , WcTimingTree*                     tt = nullptr)
    : DEMSolver<IntegrateImplicitEuler, ResolveContactSpringDashpotHaffWerner>(
               IntegrateImplicitEuler(), ResolveContactSpringDashpotHaffWerner(),
               globalBodyStorage, blockStorage, storageID, ccdID, fcdID, tt )
diff --git a/src/pe/cr/DEM.impl.h b/src/pe/cr/DEM.impl.h
index a3560e50fd94ba9daf71b71032deaa4ad3a9ec58..792f59bb3cd8ca2d7d21a78af36f6a84a4e9038f 100644
--- a/src/pe/cr/DEM.impl.h
+++ b/src/pe/cr/DEM.impl.h
@@ -77,9 +77,9 @@ void DEMSolver<Integrator,ContactResolver>::timestep( real_t dt )
       ccd::ICCD* ccd = currentBlock.getData< ccd::ICCD >( ccdID_ );
       fcd::IFCD* fcd = currentBlock.getData< fcd::IFCD >( fcdID_ );
       ccd->generatePossibleContacts( tt_ );
-      if (tt_ != NULL) tt_->start("FCD");
+      if (tt_ != nullptr) tt_->start("FCD");
       Contacts& cont = fcd->generateContacts( ccd->getPossibleContacts() );
-      if (tt_ != NULL) tt_->stop("FCD");
+      if (tt_ != nullptr) tt_->stop("FCD");
 
       for (auto cIt = cont.begin(); cIt != cont.end(); ++cIt){
          const real_t overlap( -cIt->getDistance() );
@@ -100,9 +100,9 @@ void DEMSolver<Integrator,ContactResolver>::timestep( real_t dt )
 //   if (numContacts > 0)
 //      WALBERLA_LOG_DEVEL_ON_ROOT("#Contacts: " << numContacts << "." );
 
-   if (tt_ != NULL) tt_->start("ForceSync");
+   if (tt_ != nullptr) tt_->start("ForceSync");
    reduceForces( *blockStorage_, storageID_, *globalBodyStorage_);
-   if (tt_ != NULL) tt_->stop("ForceSync");
+   if (tt_ != nullptr) tt_->stop("ForceSync");
 
    for (auto it = blockStorage_->begin(); it != blockStorage_->end(); ++it)
    {
@@ -114,7 +114,7 @@ void DEMSolver<Integrator,ContactResolver>::timestep( real_t dt )
       // Updating the positions and velocities of all locally owned and global rigid bodies (but not shadow copies).
       WALBERLA_LOG_DETAIL( "Time integration starts...\n" );
 
-      if (tt_ != NULL) tt_->start("Integration");
+      if (tt_ != nullptr) tt_->start("Integration");
 
       for( auto bodyIt = localStorage.begin(); bodyIt != localStorage.end(); ++bodyIt )
       {
@@ -140,7 +140,7 @@ void DEMSolver<Integrator,ContactResolver>::timestep( real_t dt )
          bodyIt->resetForceAndTorque();
       }
 
-      if (tt_ != NULL) tt_->stop("Integration");
+      if (tt_ != nullptr) tt_->stop("Integration");
 
       // Reset forces of shadow copies
       for( auto& body : shadowStorage ) {
diff --git a/src/pe/cr/HCSITS.h b/src/pe/cr/HCSITS.h
index 742799f0833916273632ea816bfa9ff548c59427..93da3f0f2abc075eb768d3d2745c04117ea8eaf6 100644
--- a/src/pe/cr/HCSITS.h
+++ b/src/pe/cr/HCSITS.h
@@ -117,22 +117,22 @@ public:
                                                         domain_decomposition::BlockDataID   storageID,
                                                         domain_decomposition::BlockDataID   ccdID,
                                                         domain_decomposition::BlockDataID   fcdID,
-                                                        WcTimingTree* tt = NULL );
+                                                        WcTimingTree* tt = nullptr );
    //@}
    //**********************************************************************************************
    //**Destructor**********************************************************************************
    /*!\name Destructor */
    //@{
-   ~HardContactSemiImplicitTimesteppingSolvers();
+   ~HardContactSemiImplicitTimesteppingSolvers() override;
    //@}
    //**********************************************************************************************
 
    //**Get functions*******************************************************************************
    /*!\name Get functions */
    //@{
-   virtual inline real_t            getMaximumPenetration()        const override;
-   virtual inline size_t            getNumberOfContacts()          const override;
-   virtual inline size_t            getNumberOfContactsTreated()   const override;
+   inline real_t            getMaximumPenetration()        const override;
+   inline size_t            getNumberOfContacts()          const override;
+   inline size_t            getNumberOfContactsTreated()   const override;
    inline const std::map<IBlockID::IDType, ContactCache> getContactCache() const { return blockToContactCache_; }
    inline real_t                    getSpeedLimitFactor() const;
    inline size_t                    getMaxIterations() const { return maxIterations_; }
@@ -270,7 +270,7 @@ private:
 };
 //*************************************************************************************************
 
-typedef HardContactSemiImplicitTimesteppingSolvers HCSITS;
+using HCSITS = HardContactSemiImplicitTimesteppingSolvers;
 
 //=================================================================================================
 //
diff --git a/src/pe/cr/HCSITS.impl.h b/src/pe/cr/HCSITS.impl.h
index 7218b4580519cd623a74976c678a5786eba69184..25cccf846670b51e57300aa82a6fad088a242da9 100644
--- a/src/pe/cr/HCSITS.impl.h
+++ b/src/pe/cr/HCSITS.impl.h
@@ -157,7 +157,7 @@ inline void HardContactSemiImplicitTimesteppingSolvers::timestep( const real_t d
    numContactsTreated_ = 0;
    maximumPenetration_ = 0;
 
-   if (tt_ != NULL) tt_->start("Simulation Step");
+   if (tt_ != nullptr) tt_->start("Simulation Step");
 
    for (auto it = blockStorage_->begin(); it != blockStorage_->end(); ++it)
    {
@@ -176,13 +176,13 @@ inline void HardContactSemiImplicitTimesteppingSolvers::timestep( const real_t d
       // Detect all collisions
       WALBERLA_LOG_DETAIL( "Detecting contacts...");
 
-      if (tt_ != NULL) tt_->start("Collision Detection");
+      if (tt_ != nullptr) tt_->start("Collision Detection");
       ccd->generatePossibleContacts( tt_ );
-      if (tt_ != NULL) tt_->start("Fine");
+      if (tt_ != nullptr) tt_->start("Fine");
       Contacts& contacts = fcd->generateContacts( ccd->getPossibleContacts() );
-      if (tt_ != NULL) tt_->stop("Fine");
+      if (tt_ != nullptr) tt_->stop("Fine");
 
-      if (tt_ != NULL) tt_->start("Filtering");
+      if (tt_ != nullptr) tt_->start("Filtering");
       // Filter out contacts
       size_t numContacts( contacts.size() );
       size_t numContactsMasked( 0 );
@@ -204,10 +204,10 @@ inline void HardContactSemiImplicitTimesteppingSolvers::timestep( const real_t d
       numContactsTreated_ += numContactsMasked;
 
       //      WALBERLA_LOG_DEVEL("contact filtering: " << numContactsMasked << "/" << contacts.size());
-      if (tt_ != NULL) tt_->stop("Filtering");
-      if (tt_ != NULL) tt_->stop("Collision Detection");
+      if (tt_ != nullptr) tt_->stop("Filtering");
+      if (tt_ != nullptr) tt_->stop("Collision Detection");
 
-      if (tt_ != NULL) tt_->start("Collision Response Contact Caching");
+      if (tt_ != nullptr) tt_->start("Collision Response Contact Caching");
       // Cache contact properties
       contactCache.resize( numContactsMasked );
 
@@ -277,8 +277,8 @@ inline void HardContactSemiImplicitTimesteppingSolvers::timestep( const real_t d
          }
       }
 
-      if (tt_ != NULL) tt_->stop("Collision Response Contact Caching");
-      if (tt_ != NULL) tt_->start("Collision Response Body Caching");
+      if (tt_ != nullptr) tt_->stop("Collision Response Contact Caching");
+      if (tt_ != nullptr) tt_->start("Collision Response Body Caching");
 
       // Cache body properties (and time integrate v and w to the end of the time step by applying external forces, torques and accelerations)
       size_t numBodies( globalBodyStorage_->size() + localStorage.size() + shadowStorage.size() );
@@ -331,10 +331,10 @@ inline void HardContactSemiImplicitTimesteppingSolvers::timestep( const real_t d
 #endif
       }
 
-      if (tt_ != NULL) tt_->stop("Collision Response Body Caching");
+      if (tt_ != nullptr) tt_->stop("Collision Response Body Caching");
    }
 
-   if (blockStorage_->size() == 0)
+   if (blockStorage_->empty())
    {
       // create artificial block to handle global bodies even on processes where there are no blocks
       BodyCache&    bodyCache    = blockToBodyCache_[0];
@@ -357,7 +357,7 @@ inline void HardContactSemiImplicitTimesteppingSolvers::timestep( const real_t d
       }
    }
 
-   if (tt_ != NULL) tt_->start("Collision Response Resolution");
+   if (tt_ != nullptr) tt_->start("Collision Response Resolution");
    const real_t rp = relaxationParam_;
    relaxationParam_ = real_c(1); // must be set to 1.0 such that dv and dw caused by external forces and torques are not falsely altered
    synchronizeVelocities( );
@@ -372,7 +372,7 @@ inline void HardContactSemiImplicitTimesteppingSolvers::timestep( const real_t d
 
       iteration_ = it;
 
-      if (tt_ != NULL) tt_->start("Collision Response Solving");
+      if (tt_ != nullptr) tt_->start("Collision Response Solving");
       for (auto blkIt = blockStorage_->begin(); blkIt != blockStorage_->end(); ++blkIt)
       {
          IBlock& currentBlock = *blkIt;
@@ -410,7 +410,7 @@ inline void HardContactSemiImplicitTimesteppingSolvers::timestep( const real_t d
          }
       }
 
-      if (tt_ != NULL) tt_->stop("Collision Response Solving");
+      if (tt_ != nullptr) tt_->stop("Collision Response Solving");
 
       synchronizeVelocities( );
 
@@ -438,8 +438,8 @@ inline void HardContactSemiImplicitTimesteppingSolvers::timestep( const real_t d
 #endif
    }
 
-   if (tt_ != NULL) tt_->stop("Collision Response Resolution");
-   if (tt_ != NULL) tt_->start("Collision Response Integration");
+   if (tt_ != nullptr) tt_->stop("Collision Response Resolution");
+   if (tt_ != nullptr) tt_->start("Collision Response Integration");
 
    // WARNING: Even though bodyCache.dv_[j] and bodyCache.dw_[j] _should_ be exactly 0 at all times for
    // bodies with infinite mass/inertia, this is not the case if the simulation breaks.
@@ -504,12 +504,12 @@ inline void HardContactSemiImplicitTimesteppingSolvers::timestep( const real_t d
       // NOTE: We might still need shadow copy updates if the user sets velocities or positions. Thus we might have to split up synchronize() again. It doesn't break anything if we still communicate the shadow copy updates though.
    }
 
-   if (tt_ != NULL) tt_->stop("Collision Response Integration");
+   if (tt_ != nullptr) tt_->stop("Collision Response Integration");
 
    blockToBodyCache_.clear();
    blockToContactCache_.clear();
 
-   if (tt_ != NULL) tt_->stop("Simulation Step");
+   if (tt_ != nullptr) tt_->stop("Simulation Step");
 }
 //*************************************************************************************************
 
@@ -1506,8 +1506,8 @@ inline void HardContactSemiImplicitTimesteppingSolvers::synchronizeVelocities( )
    if ((mpi::MPIManager::instance()->numProcesses() <= 1) && (blockStorage_->size() <= 1))
       return;
 
-   if (tt_ != NULL) tt_->start("Velocity Sync");
-   if (tt_ != NULL) tt_->start("Velocity Sync Correction Assembling");
+   if (tt_ != nullptr) tt_->start("Velocity Sync");
+   if (tt_ != nullptr) tt_->start("Velocity Sync Correction Assembling");
 
    // Sending local force contributions of shadow copies to owner.
    WALBERLA_LOG_DETAIL( "Assembling of velocity correction message starts...\n" );
@@ -1558,12 +1558,12 @@ inline void HardContactSemiImplicitTimesteppingSolvers::synchronizeVelocities( )
       }
    }
 
-   if (tt_ != NULL) tt_->stop("Velocity Sync Correction Assembling");
+   if (tt_ != nullptr) tt_->stop("Velocity Sync Correction Assembling");
 
    //   for( ProcessIterator process = processstorage_.begin(); process != processstorage_.end(); ++process )
    //      sentVelocitiesSyncCorrections_.transfered( process->getSendBuffer().size() );
 
-   if (tt_ != NULL) tt_->start("Velocity Sync Correction Communicate");
+   if (tt_ != nullptr) tt_->start("Velocity Sync Correction Communicate");
 
    WALBERLA_LOG_DETAIL( "Communication of velocity correction message starts..." );
 
@@ -1573,12 +1573,12 @@ inline void HardContactSemiImplicitTimesteppingSolvers::synchronizeVelocities( )
    syncVelBS.setReceiverInfo(recvRanks, true);
    syncVelBS.sendAll();
 
-   if (tt_ != NULL) tt_->stop("Velocity Sync Correction Communicate");
+   if (tt_ != nullptr) tt_->stop("Velocity Sync Correction Communicate");
 
    //   for( ProcessIterator process = processstorage_.begin(); process != processstorage_.end(); ++process )
    //      receivedVelocitiesSyncCorrections_.transfered( process->getRecvBuffer().size() );
 
-   if (tt_ != NULL) tt_->start("Velocity Sync Correction Parsing");
+   if (tt_ != nullptr) tt_->start("Velocity Sync Correction Parsing");
 
    // Receiving force and torque contributions
    WALBERLA_LOG_DETAIL( "Parsing of velocity correction message starts...");
@@ -1595,7 +1595,7 @@ inline void HardContactSemiImplicitTimesteppingSolvers::synchronizeVelocities( )
          //         it.buffer() >> sender;
          it.buffer() >> receiver;
          auto blk = blockStorage_->getBlock(receiver);
-         WALBERLA_CHECK(blk != NULL, receiver << " not on this process!");
+         WALBERLA_CHECK(blk != nullptr, receiver << " not on this process!");
          IBlock& block = *blk;
          Storage* storage  = block.uncheckedFastGetData< Storage >( storageID_ );
          BodyStorage& localStorage  = (*storage)[0];
@@ -1606,9 +1606,9 @@ inline void HardContactSemiImplicitTimesteppingSolvers::synchronizeVelocities( )
       //      if (tt_ != NULL) tt_->stop("Inside Loop");
    }
 
-   if (tt_ != NULL) tt_->stop("Velocity Sync Correction Parsing");
+   if (tt_ != nullptr) tt_->stop("Velocity Sync Correction Parsing");
 
-   if (tt_ != NULL) tt_->start("Velocity Sync Update Assembling");
+   if (tt_ != nullptr) tt_->start("Velocity Sync Update Assembling");
    WALBERLA_LOG_DETAIL( "Assembling of velocity update message starts...");
 
    //==========================================================
@@ -1657,24 +1657,24 @@ inline void HardContactSemiImplicitTimesteppingSolvers::synchronizeVelocities( )
       }
    }
 
-   if (tt_ != NULL) tt_->stop("Velocity Sync Update Assembling");
+   if (tt_ != nullptr) tt_->stop("Velocity Sync Update Assembling");
 
    //   for( ProcessIterator process = processstorage_.begin(); process != processstorage_.end(); ++process )
    //      sentVelocitiesSyncUpdates_.transfered( process->getSendBuffer().size() );
 
-   if (tt_ != NULL) tt_->start("Velocity Sync Update Communincate");
+   if (tt_ != nullptr) tt_->start("Velocity Sync Update Communincate");
 
    WALBERLA_LOG_DETAIL( "Communication of velocity update message starts...");
 
    syncVelBS.setReceiverInfo(recvRanks, true);
    syncVelBS.sendAll();
 
-   if (tt_ != NULL) tt_->stop("Velocity Sync Update Communincate");
+   if (tt_ != nullptr) tt_->stop("Velocity Sync Update Communincate");
 
    //   for( ProcessIterator process = processstorage_.begin(); process != processstorage_.end(); ++process )
    //      receivedVelocitiesSyncUpdates_.transfered( process->getRecvBuffer().size() );
 
-   if (tt_ != NULL) tt_->start("Velocity Sync Update Processing");
+   if (tt_ != nullptr) tt_->start("Velocity Sync Update Processing");
 
    // Receiving velocity updates
    WALBERLA_LOG_DETAIL( "Parsing of velocity update message starts...");
@@ -1691,7 +1691,7 @@ inline void HardContactSemiImplicitTimesteppingSolvers::synchronizeVelocities( )
          //         it.buffer() >> sender;
          it.buffer() >> receiver;
          auto blk = blockStorage_->getBlock(receiver);
-         WALBERLA_CHECK(blk != NULL, receiver << " not on this process!");
+         WALBERLA_CHECK(blk != nullptr, receiver << " not on this process!");
          IBlock& block = *blk;
          Storage* storage  = block.uncheckedFastGetData< Storage >( storageID_ );
          //         BodyStorage& localStorage  = (*storage)[0];
@@ -1702,9 +1702,9 @@ inline void HardContactSemiImplicitTimesteppingSolvers::synchronizeVelocities( )
       //      if (tt_ != NULL) tt_->stop("Inside Loop");
    }
 
-   if (tt_ != NULL) tt_->stop("Velocity Sync Update Processing");
+   if (tt_ != nullptr) tt_->stop("Velocity Sync Update Processing");
 
-   if (tt_ != NULL) tt_->start("Velocity Sync Globals");
+   if (tt_ != nullptr) tt_->start("Velocity Sync Globals");
    /*
    {
       size_t i;
@@ -1743,8 +1743,8 @@ inline void HardContactSemiImplicitTimesteppingSolvers::synchronizeVelocities( )
       }
    }*/
 
-   if (tt_ != NULL) tt_->stop("Velocity Sync Globals");
-   if (tt_ != NULL) tt_->stop("Velocity Sync");
+   if (tt_ != nullptr) tt_->stop("Velocity Sync Globals");
+   if (tt_ != nullptr) tt_->stop("Velocity Sync");
 }
 //*************************************************************************************************
 
diff --git a/src/pe/cr/ICR.h b/src/pe/cr/ICR.h
index 73d1868d3be50e43269ce044052bc32fedcadacd..7c86a9fa0c21e666b6345988e62137d5c6947795 100644
--- a/src/pe/cr/ICR.h
+++ b/src/pe/cr/ICR.h
@@ -31,7 +31,7 @@ namespace cr {
 class ICR {
 public:
    ICR() : /*globalLinearDrag_(0),*/ globalLinearAcceleration_(0) {}
-   virtual ~ICR() {}
+   virtual ~ICR() = default;
 
    virtual void timestep( const real_t dt ) = 0;
 
diff --git a/src/pe/cr/PlainIntegrator.h b/src/pe/cr/PlainIntegrator.h
index 92abbbfb3746b59f08927fb9222b4b72bd00045f..c2284eff2e0c9400430eda5d6cb29be068e8a153 100644
--- a/src/pe/cr/PlainIntegrator.h
+++ b/src/pe/cr/PlainIntegrator.h
@@ -46,13 +46,13 @@ public:
                           const shared_ptr<BodyStorage>&      globalBodyStorage,
                           const shared_ptr<BlockStorage>&     blockStorage,
                           domain_decomposition::BlockDataID   storageID,
-                          WcTimingTree* tt = NULL );
+                          WcTimingTree* tt = nullptr );
 
    /// forwards to timestep
    /// Convenience operator to make class a functor.
    void operator()(const real_t dt) { timestep(dt); }
    /// Advances the simulation dt seconds.
-   void timestep( const real_t dt );
+   void timestep( const real_t dt ) override;
 private:
    const Integrator                  integrate_;
    shared_ptr<BodyStorage>           globalBodyStorage_;
@@ -67,7 +67,7 @@ public:
    PlainIntegrator(  const shared_ptr<BodyStorage>&    globalBodyStorage
                    , const shared_ptr<BlockStorage>&   blockStorage
                    , domain_decomposition::BlockDataID storageID
-                   , WcTimingTree*                     tt = NULL)
+                   , WcTimingTree*                     tt = nullptr)
    : PlainIntegratorSolver<IntegrateImplicitEuler>( IntegrateImplicitEuler(), globalBodyStorage, blockStorage,
                                                    storageID, tt )
    {
diff --git a/src/pe/cr/PlainIntegrator.impl.h b/src/pe/cr/PlainIntegrator.impl.h
index e2a45b2f8eda5405f858db551101d17f545e6537..cd071eb7f7e5986415f3dae5889dd8243c28d995 100644
--- a/src/pe/cr/PlainIntegrator.impl.h
+++ b/src/pe/cr/PlainIntegrator.impl.h
@@ -57,8 +57,8 @@ PlainIntegratorSolver<Integrator>::PlainIntegratorSolver( const Integrator & int
 template< typename Integrator >
 void PlainIntegratorSolver<Integrator>::timestep( const real_t dt )
 {
-   if (tt_!=NULL) tt_->start("PlainIntegrator");
-   if (tt_!=NULL) tt_->start("Integrate Bodies");
+   if (tt_!=nullptr) tt_->start("PlainIntegrator");
+   if (tt_!=nullptr) tt_->start("Integrate Bodies");
    for (auto it = blockStorage_->begin(); it != blockStorage_->end(); ++it){
       IBlock & currentBlock = *it;
       Storage * storage = currentBlock.getData< Storage >( storageID_ );
@@ -80,8 +80,8 @@ void PlainIntegratorSolver<Integrator>::timestep( const real_t dt )
          WALBERLA_ASSERT( bd->checkInvariants(), "Invalid body state detected" );
       }
    }
-   if (tt_!=NULL) tt_->stop("Integrate Bodies");
-   if (tt_!=NULL) tt_->stop("PlainIntegrator");
+   if (tt_!=nullptr) tt_->stop("Integrate Bodies");
+   if (tt_!=nullptr) tt_->stop("PlainIntegrator");
 }
 
 } // namespace cr
diff --git a/src/pe/fcd/GenericFCD.h b/src/pe/fcd/GenericFCD.h
index 27a311f3c3bdb48552103bed3c47c18f7d05e0d0..c054ae55ca2deb66bb4f3174c82e4f52f83c894e 100644
--- a/src/pe/fcd/GenericFCD.h
+++ b/src/pe/fcd/GenericFCD.h
@@ -36,7 +36,7 @@ namespace fcd {
 template <typename BodyTypeTuple, template <typename Container> class CollisionFunctor >
 class GenericFCD : public IFCD{
 public:
-   virtual Contacts& generateContacts(PossibleContacts& possibleContacts)
+   Contacts& generateContacts(PossibleContacts& possibleContacts) override
    {
       contacts_.clear();
       CollisionFunctor<decltype(contacts_)> func(contacts_);
diff --git a/src/pe/fcd/IFCD.h b/src/pe/fcd/IFCD.h
index 0fb86fc595ce0c4bfedb3096a4ddaf5b6a325c66..e34592b3df6320c97a0fa6104f438d5464fe2f2f 100644
--- a/src/pe/fcd/IFCD.h
+++ b/src/pe/fcd/IFCD.h
@@ -35,7 +35,7 @@ namespace fcd {
 
 class IFCD : private NonCopyable{
 public:
-   virtual ~IFCD() {};
+   virtual ~IFCD() = default;
 
    ///
    /// \brief generates a list of actual collisions
diff --git a/src/pe/fcd/SimpleFCD.h b/src/pe/fcd/SimpleFCD.h
index 6f38ec19067a54641867b2315048af3af2a0937f..0527459aab63d93d6c10660c14fb3e7223dbd452 100644
--- a/src/pe/fcd/SimpleFCD.h
+++ b/src/pe/fcd/SimpleFCD.h
@@ -32,7 +32,7 @@ namespace fcd {
 template <typename BodyTypeTuple>
 class SimpleFCD : public GenericFCD<BodyTypeTuple, AnalyticCollideFunctor>{
 public:
-   virtual ~SimpleFCD(){}
+   ~SimpleFCD() override = default;
 };
 
 }
diff --git a/src/pe/raytracing/Intersects.h b/src/pe/raytracing/Intersects.h
index 3746ce6d01ccb4f47b4e1d4584aacf610496dee3..ea7c6f8507b52b20e03d21a6d2400e0078f60ec1 100644
--- a/src/pe/raytracing/Intersects.h
+++ b/src/pe/raytracing/Intersects.h
@@ -45,7 +45,7 @@ inline bool intersects(const EllipsoidID ellipsoid, const Ray& ray, real_t& t, V
 
 inline bool intersects(const BodyID body, const Ray& ray, real_t& t, Vec3& n);
    
-inline bool intersects(const AABB& aabb, const Ray& ray, real_t& t, real_t padding = real_t(0.0), Vec3* n = NULL);
+inline bool intersects(const AABB& aabb, const Ray& ray, real_t& t, real_t padding = real_t(0.0), Vec3* n = nullptr);
 inline bool intersectsSphere(const Vec3& gpos, real_t radius, const Ray& ray, real_t& t0, real_t& t1);
 
 struct IntersectsFunctor
@@ -430,14 +430,14 @@ inline bool intersects(const AABB& aabb, const Ray& ray, real_t& t, real_t paddi
       tmax = tzmax;
    }
    
-   if (n != NULL) {
+   if (n != nullptr) {
       (*n)[0] = (*n)[1] = (*n)[2] = real_t(0);
    }
    real_t t_;
    if (tmin > 0) {
       // ray hit box from outside
       t_ = tmin;
-      if (n != NULL) {
+      if (n != nullptr) {
          (*n)[tminAxis] = real_t(1);
       }
    } else if (tmax < 0) {
@@ -447,12 +447,12 @@ inline bool intersects(const AABB& aabb, const Ray& ray, real_t& t, real_t paddi
    } else {
       // ray origin within box
       t_ = tmax;
-      if (n != NULL) {
+      if (n != nullptr) {
          (*n)[tmaxAxis] = real_t(1);
       }
    }
    
-   if (n != NULL) {
+   if (n != nullptr) {
       if (ray.getDirection() * (*n) > 0) {
          *n = -(*n);
       }
diff --git a/src/pe/raytracing/Lighting.h b/src/pe/raytracing/Lighting.h
index 451cfe3357ca80dad10613a79251ed75da366898..1d64cbc87aba6b71032b807a92d586ab0b277214 100644
--- a/src/pe/raytracing/Lighting.h
+++ b/src/pe/raytracing/Lighting.h
@@ -39,9 +39,7 @@ struct Lighting {
    
    /*!\brief Instantiation constructor for the Lighting struct.
     */
-   Lighting () {
-      
-   }
+   Lighting () = default;
    
    /*!\brief Instantiation constructor for the Lighting struct.
     * \param pointLightOrigin Origin of the point light.
diff --git a/src/pe/raytracing/Raytracer.cpp b/src/pe/raytracing/Raytracer.cpp
index 631a5dfd3384bba103c8b5cd2381acad40d0cd5d..344b8af9938271e05aa5af4f97fe10b6095642f4 100644
--- a/src/pe/raytracing/Raytracer.cpp
+++ b/src/pe/raytracing/Raytracer.cpp
@@ -31,7 +31,7 @@ namespace walberla {
 namespace pe {
 namespace raytracing {
    
-void BodyIntersectionInfo_Comparator_MPI_OP( BodyIntersectionInfo *in, BodyIntersectionInfo *inout, int *len, MPI_Datatype *dptr) {
+void BodyIntersectionInfo_Comparator_MPI_OP( BodyIntersectionInfo *in, BodyIntersectionInfo *inout, const int *len, MPI_Datatype *dptr) {
    WALBERLA_UNUSED(dptr);
    for (int i = 0; i < *len; ++i) {
       if (in->bodySystemID != 0 && inout->bodySystemID != 0) {
diff --git a/src/pe/raytracing/Raytracer.h b/src/pe/raytracing/Raytracer.h
index 39c42aa213a0af99570f9faccb64d2b5e33ee57c..f531527608950ec9a43ccac11a930e55bd6007b3 100644
--- a/src/pe/raytracing/Raytracer.h
+++ b/src/pe/raytracing/Raytracer.h
@@ -183,7 +183,7 @@ public:
    /*!\name Functions */
    //@{
    template <typename BodyTypeTuple>
-   void generateImage(const size_t timestep, WcTimingTree* tt = NULL );
+   void generateImage(const size_t timestep, WcTimingTree* tt = nullptr );
    
    void setupView_();
    void setupFilenameRankWidth_();
@@ -191,9 +191,9 @@ public:
    
 private:
    void localOutput(const std::vector<BodyIntersectionInfo>& intersectionsBuffer, size_t timestep,
-                    WcTimingTree* tt = NULL);
+                    WcTimingTree* tt = nullptr);
    void output(const std::vector<BodyIntersectionInfo>& intersectionsBuffer, size_t timestep,
-               WcTimingTree* tt = NULL);
+               WcTimingTree* tt = nullptr);
 
    std::string getOutputFilename(const std::string& base, size_t timestep, bool isGlobalImage) const;
    void writeImageToFile(const std::vector<BodyIntersectionInfo>& intersectionsBuffer,
@@ -201,9 +201,9 @@ private:
    void writeImageToFile(const std::vector<BodyIntersectionInfo>& intersectionsBuffer,
                          const std::string& fileName) const;
    
-   void syncImageUsingMPIReduce(std::vector<BodyIntersectionInfo>& intersectionsBuffer, WcTimingTree* tt = NULL);
+   void syncImageUsingMPIReduce(std::vector<BodyIntersectionInfo>& intersectionsBuffer, WcTimingTree* tt = nullptr);
    void syncImageUsingMPIGather(std::vector<BodyIntersectionInfo>& intersections,
-                                std::vector<BodyIntersectionInfo>& intersectionsBuffer, WcTimingTree* tt = NULL);
+                                std::vector<BodyIntersectionInfo>& intersectionsBuffer, WcTimingTree* tt = nullptr);
    
    inline bool isPlaneVisible(const PlaneID plane, const Ray& ray) const;
    inline size_t coordinateToArrayIndex(size_t x, size_t y) const;
@@ -502,7 +502,7 @@ inline void Raytracer::traceRayInHashGrids(const Ray& ray, BodyID& body_closest,
       const ccd::HashGrids* hashgrids = blockIt->uncheckedFastGetData<ccd::HashGrids>(ccdID_);
       BodyID body = hashgrids->getClosestBodyIntersectingWithRay<BodyTypeTuple>(ray, blockAABB, t, n,
                                                                                 isBodyVisibleFunc_);
-      if (body != NULL && t < t_closest) {
+      if (body != nullptr && t < t_closest) {
          t_closest = t;
          body_closest = body;
          n_closest = n;
@@ -519,7 +519,7 @@ inline void Raytracer::traceRayInHashGrids(const Ray& ray, BodyID& body_closest,
  */
 template <typename BodyTypeTuple>
 void Raytracer::generateImage(const size_t timestep, WcTimingTree* tt) {
-   if (tt != NULL) tt->start("Raytracing");
+   if (tt != nullptr) tt->start("Raytracing");
    const real_t realMax = std::numeric_limits<real_t>::max();
    
    std::vector<BodyIntersectionInfo> intersections;
@@ -529,25 +529,25 @@ void Raytracer::generateImage(const size_t timestep, WcTimingTree* tt) {
 
    if (raytracingAlgorithm_ == RAYTRACE_HASHGRIDS || raytracingAlgorithm_ == RAYTRACE_COMPARE_BOTH
       || raytracingAlgorithm_ == RAYTRACE_COMPARE_BOTH_STRICTLY) {
-      if (tt != NULL) tt->start("HashGrids Update");
+      if (tt != nullptr) tt->start("HashGrids Update");
       for (auto blockIt = forest_->begin(); blockIt != forest_->end(); ++blockIt) {
          ccd::HashGrids* hashgrids = blockIt->getData<ccd::HashGrids>(ccdID_);
          hashgrids->update();
       }
-      if (tt != NULL) tt->stop("HashGrids Update");
+      if (tt != nullptr) tt->stop("HashGrids Update");
    }
    
    real_t t, t_closest;
    Vec3 n;
    Vec3 n_closest;
-   BodyID body_closest = NULL;
+   BodyID body_closest = nullptr;
    Ray ray(cameraPosition_, Vec3(1,0,0));
    IntersectsFunctor func(ray, t, n);
    bool isErrorneousPixel = false;
    uint_t pixelErrors = 0;
    std::map<BodyID, std::unordered_set<BodyID>> correctToIncorrectBodyIDsMap;
    
-   if (tt != NULL) tt->start("Intersection Testing");
+   if (tt != nullptr) tt->start("Intersection Testing");
    for (size_t x = 0; x < pixelsHorizontal_*antiAliasFactor_; x++) {
       for (size_t y = 0; y < pixelsVertical_*antiAliasFactor_; y++) {
          Vec3 pixelLocation = viewingPlaneOrigin_ + u_*(real_c(x)+real_t(0.5))*pixelWidth_ + v_*(real_c(y)+real_t(0.5))*pixelHeight_;
@@ -556,7 +556,7 @@ void Raytracer::generateImage(const size_t timestep, WcTimingTree* tt) {
          
          n.reset();
          t_closest = realMax;
-         body_closest = NULL;
+         body_closest = nullptr;
          
          if (raytracingAlgorithm_ == RAYTRACE_HASHGRIDS) {
             traceRayInHashGrids<BodyTypeTuple>(ray, body_closest, t_closest, n_closest);
@@ -567,7 +567,7 @@ void Raytracer::generateImage(const size_t timestep, WcTimingTree* tt) {
             BodyID hashgrids_body_closest = body_closest;
             
             t_closest = realMax;
-            body_closest = NULL;
+            body_closest = nullptr;
             traceRayNaively<BodyTypeTuple>(ray, body_closest, t_closest, n_closest);
             
             if (body_closest != hashgrids_body_closest) {
@@ -583,7 +583,7 @@ void Raytracer::generateImage(const size_t timestep, WcTimingTree* tt) {
          intersectionInfo.imageX = uint32_t(x);
          intersectionInfo.imageY = uint32_t(y);
          
-         if (!realIsIdentical(t_closest, realMax) && body_closest != NULL) {
+         if (!realIsIdentical(t_closest, realMax) && body_closest != nullptr) {
             Color color = getColor(body_closest, ray, t_closest, n_closest);
             if (isErrorneousPixel) {
                color = Color(1,0,0);
@@ -606,7 +606,7 @@ void Raytracer::generateImage(const size_t timestep, WcTimingTree* tt) {
          }
       }
    }
-   if (tt != NULL) tt->stop("Intersection Testing");
+   if (tt != nullptr) tt->stop("Intersection Testing");
 
    if (raytracingAlgorithm_ == RAYTRACE_COMPARE_BOTH || raytracingAlgorithm_ == RAYTRACE_COMPARE_BOTH_STRICTLY) {
       if (pixelErrors > 0) {
@@ -615,7 +615,7 @@ void Raytracer::generateImage(const size_t timestep, WcTimingTree* tt) {
          std::stringstream ss;
          for (auto it: correctToIncorrectBodyIDsMap) {
             const BodyID correctBody = it.first;
-            if (it.first != NULL) {
+            if (it.first != nullptr) {
                ss << " correct body: " << correctBody->getID() << "(" << correctBody->getHash() << ")";
             } else {
                ss << " no body naively found";
@@ -623,7 +623,7 @@ void Raytracer::generateImage(const size_t timestep, WcTimingTree* tt) {
             ss << ", hashgrids found:";
             for (auto incorrectBody: it.second) {
                ss << " ";
-               if (incorrectBody != NULL) {
+               if (incorrectBody != nullptr) {
                   ss << incorrectBody->getID() << "(" << incorrectBody->getHash();
                } else {
                   ss << "NULL";
@@ -662,7 +662,7 @@ void Raytracer::generateImage(const size_t timestep, WcTimingTree* tt) {
    
    output(intersectionsBuffer, timestep, tt);
    
-   if (tt != NULL) tt->stop("Raytracing");
+   if (tt != nullptr) tt->stop("Raytracing");
 }
 
 /*!\brief Computes the color for a certain intersection.
diff --git a/src/pe/raytracing/ShadingParameters.h b/src/pe/raytracing/ShadingParameters.h
index 7e476527d1bd665c4017780f8858dbd39aff8876..c6a7eb16c23930a40292684ab6f8654699b8f214 100644
--- a/src/pe/raytracing/ShadingParameters.h
+++ b/src/pe/raytracing/ShadingParameters.h
@@ -36,9 +36,7 @@ struct ShadingParameters {
    
    /*!\brief Instantiation constructor for the Shading struct.
     */
-   ShadingParameters () {
-      
-   }
+   ShadingParameters () = default;
    
    /*!\brief Instantiation constructor for the Shading struct.
     * \param diffuseColor Primary color of the material.
diff --git a/src/pe/rigidbody/BodyIterators.h b/src/pe/rigidbody/BodyIterators.h
index 5f4ef40fc5bc9be40788c60af43cff52dea9972e..1948465f634c5aeea2bd22906e567965843843f1 100644
--- a/src/pe/rigidbody/BodyIterators.h
+++ b/src/pe/rigidbody/BodyIterators.h
@@ -34,10 +34,16 @@ class BodyIterator
 public:
 
    template< typename T >
-   class iterator : public std::iterator< std::input_iterator_tag, typename T::value_type, typename T::difference_type, typename T::pointer, typename T::reference >
+   class iterator
    {
       friend class BodyIterator;
    public:
+      using iterator_category = std::input_iterator_tag;
+      using value_type = typename T::value_type;
+      using difference_type = typename T::difference_type;
+      using pointer = typename T::pointer;
+      using reference = typename T::reference;
+
       iterator & operator++()    { ++it_; checkStateAndAdapt(); return *this; }      // prefix ++X
       iterator   operator++(int) { iterator it( *this ); operator++(); return it; }; // postfix X++
 
@@ -45,14 +51,7 @@ public:
       {
          if (ended_ || rhs.ended_)
          {
-            if (ended_ == rhs.ended_)
-            {
-               return true;
-            }
-            else
-            {
-               return false;
-            }
+            return ended_ == rhs.ended_;
          }
 
          //std::vector::iterator cannot be compared between different instances (assert!)
@@ -146,10 +145,16 @@ class LocalBodyIterator
 public:
 
    template< typename T >
-   class iterator : public std::iterator< std::input_iterator_tag, typename T::value_type, typename T::difference_type, typename T::pointer, typename T::reference >
+   class iterator
    {
       friend class LocalBodyIterator;
    public:
+      using iterator_category = std::input_iterator_tag;
+      using value_type = typename T::value_type;
+      using difference_type = typename T::difference_type;
+      using pointer = typename T::pointer;
+      using reference = typename T::reference;
+
       iterator & operator++()    { ++it_; checkStateAndAdapt(); return *this; }      // prefix ++X
       iterator   operator++(int) { iterator it( *this ); operator++(); return it; }; // postfix X++
 
@@ -157,14 +162,7 @@ public:
       {
          if (ended_ || rhs.ended_)
          {
-            if (ended_ == rhs.ended_)
-            {
-               return true;
-            }
-            else
-            {
-               return false;
-            }
+            return ended_ == rhs.ended_;
          }
 
          return it_ == rhs.it_;
@@ -236,10 +234,16 @@ class ShadowBodyIterator
 public:
 
    template< typename T >
-   class iterator : public std::iterator< std::input_iterator_tag, typename T::value_type, typename T::difference_type, typename T::pointer, typename T::reference >
+   class iterator
    {
       friend class ShadowBodyIterator;
    public:
+      using iterator_category = std::input_iterator_tag;
+      using value_type = typename T::value_type;
+      using difference_type = typename T::difference_type;
+      using pointer = typename T::pointer;
+      using reference = typename T::reference;
+
       iterator & operator++()    { ++it_; checkStateAndAdapt(); return *this; }      // prefix ++X
       iterator   operator++(int) { iterator it( *this ); operator++(); return it; }; // postfix X++
 
@@ -247,14 +251,7 @@ public:
       {
          if (ended_ || rhs.ended_)
          {
-            if (ended_ == rhs.ended_)
-            {
-               return true;
-            }
-            else
-            {
-               return false;
-            }
+            return ended_ == rhs.ended_;
          }
 
          return it_ == rhs.it_;
diff --git a/src/pe/rigidbody/Box.h b/src/pe/rigidbody/Box.h
index 266aead5d9d9f4e1dfb1a6ebce1dad29a2d31544..883739868f995502cf292bee6540e4d3f5136a58 100644
--- a/src/pe/rigidbody/Box.h
+++ b/src/pe/rigidbody/Box.h
@@ -74,7 +74,7 @@ public:
    //**Destructor**********************************************************************************
    /*!\name Destructor */
    //@{
-   virtual ~Box();
+   ~Box() override;
    //@}
    //**********************************************************************************************
 
@@ -83,7 +83,7 @@ public:
    /*!\name Get functions */
    //@{
    inline const Vec3& getLengths() const;
-   virtual inline real_t getVolume() const;
+   inline real_t getVolume() const override;
    //@}
    //**********************************************************************************************
 
@@ -119,15 +119,15 @@ public:
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   inline virtual Vec3 support( const Vec3& d ) const;
-   inline virtual Vec3 supportContactThreshold( const Vec3& d ) const;
+   inline Vec3 support( const Vec3& d ) const override;
+   inline Vec3 supportContactThreshold( const Vec3& d ) const override;
    //@}
    //**********************************************************************************************
 
    //**Output functions****************************************************************************
    /*!\name Output functions */
    //@{
-   virtual void print( std::ostream& os, const char* tab ) const;
+   void print( std::ostream& os, const char* tab ) const override;
    //@}
    //**********************************************************************************************
 
@@ -135,15 +135,15 @@ protected:
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   virtual bool containsRelPointImpl ( real_t px, real_t py, real_t pz ) const;
-   virtual bool isSurfaceRelPointImpl( real_t px, real_t py, real_t pz ) const;
+   bool containsRelPointImpl ( real_t px, real_t py, real_t pz ) const override;
+   bool isSurfaceRelPointImpl( real_t px, real_t py, real_t pz ) const override;
    //@}
    //**********************************************************************************************
 
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   virtual void calcBoundingBox();  // Calculation of the axis-aligned bounding box
+   void calcBoundingBox() override;  // Calculation of the axis-aligned bounding box
    //@}
    //**********************************************************************************************
 
diff --git a/src/pe/rigidbody/Capsule.h b/src/pe/rigidbody/Capsule.h
index 937aadcda652d09631d4ecb0702eda7a27acafcb..52311a2041fb06096bf89dea32bd9bb401865bd4 100644
--- a/src/pe/rigidbody/Capsule.h
+++ b/src/pe/rigidbody/Capsule.h
@@ -79,7 +79,7 @@ public:
    //**Destructor**********************************************************************************
    /*!\name Destructor */
    //@{
-   virtual ~Capsule();
+   ~Capsule() override;
    //@}
    //**********************************************************************************************
 
@@ -89,14 +89,14 @@ public:
    //@{
    inline real_t  getRadius() const;
    inline real_t  getLength() const;
-   inline real_t  getVolume() const;
+   inline real_t  getVolume() const override;
    //@}
    //**********************************************************************************************
 
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   inline virtual Vec3 support( const Vec3& d ) const;
+   inline Vec3 support( const Vec3& d ) const override;
    //@}
    //**********************************************************************************************
 
@@ -128,18 +128,18 @@ public:
    //**Output functions****************************************************************************
    /*!\name Output functions */
    //@{
-   virtual void print( std::ostream& os, const char* tab ) const;
+   void print( std::ostream& os, const char* tab ) const override;
    //@}
    //**********************************************************************************************
 
 protected:
-   virtual bool containsRelPointImpl ( real_t  px, real_t  py, real_t  pz ) const;
-   virtual bool isSurfaceRelPointImpl( real_t  px, real_t  py, real_t  pz ) const;
+   bool containsRelPointImpl ( real_t  px, real_t  py, real_t  pz ) const override;
+   bool isSurfaceRelPointImpl( real_t  px, real_t  py, real_t  pz ) const override;
 
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   virtual void calcBoundingBox();  // Calculation of the axis-aligned bounding box
+   void calcBoundingBox() override;  // Calculation of the axis-aligned bounding box
    //@}
    //**********************************************************************************************
 
diff --git a/src/pe/rigidbody/CylindricalBoundary.h b/src/pe/rigidbody/CylindricalBoundary.h
index c56582c20094d26ab85c5b00b6adff489b8b9f3c..0dc9312dc19187c34f51fe65e3675599b8407d59 100644
--- a/src/pe/rigidbody/CylindricalBoundary.h
+++ b/src/pe/rigidbody/CylindricalBoundary.h
@@ -62,7 +62,7 @@ public:
    //**Destructor**********************************************************************************
    /*!\name Destructor */
    //@{
-   virtual ~CylindricalBoundary();
+   ~CylindricalBoundary() override;
    //@}
    //**********************************************************************************************
 
@@ -85,7 +85,7 @@ public:
    //**Output functions****************************************************************************
    /*!\name Output functions */
    //@{
-   virtual void print( std::ostream& os, const char* tab ) const;
+   void print( std::ostream& os, const char* tab ) const override;
    //@}
    //**********************************************************************************************
 
@@ -93,10 +93,10 @@ protected:
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   virtual bool containsRelPointImpl ( real_t px, real_t py, real_t pz ) const;
-   virtual bool isSurfaceRelPointImpl( real_t px, real_t py, real_t pz ) const;
+   bool containsRelPointImpl ( real_t px, real_t py, real_t pz ) const override;
+   bool isSurfaceRelPointImpl( real_t px, real_t py, real_t pz ) const override;
 
-   virtual void calcBoundingBox();  // Calculation of the axis-aligned bounding box
+   void calcBoundingBox() override;  // Calculation of the axis-aligned bounding box
    //@}
    //**********************************************************************************************
 
diff --git a/src/pe/rigidbody/Ellipsoid.h b/src/pe/rigidbody/Ellipsoid.h
index 958c78ab31f474f99c5bdc310023fa0ca79edc31..e9a73305f0646a39fbaf82259f3967ba86b60332 100644
--- a/src/pe/rigidbody/Ellipsoid.h
+++ b/src/pe/rigidbody/Ellipsoid.h
@@ -77,7 +77,7 @@ public:
    //**Destructor**********************************************************************************
    /*!\name Destructor */
    //@{
-   virtual ~Ellipsoid();
+   ~Ellipsoid() override;
    //@}
    //**********************************************************************************************
    //**********************************************************************************************
@@ -87,7 +87,7 @@ public:
    /*!\name Get functions */
    //@{
    inline const Vec3& getSemiAxes() const;
-   virtual inline real_t getVolume()         const;
+   inline real_t getVolume()         const override;
    //@}
    //**********************************************************************************************
 
@@ -99,14 +99,14 @@ public:
    //**Output functions****************************************************************************
    /*!\name Output functions */
    //@{
-   virtual void print( std::ostream& os, const char* tab ) const;
+   void print( std::ostream& os, const char* tab ) const override;
    //@}
    //**********************************************************************************************
 
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   inline virtual Vec3 support( const Vec3& d ) const;
+   inline Vec3 support( const Vec3& d ) const override;
    //@}
    //**********************************************************************************************
 
@@ -124,15 +124,15 @@ protected:
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   virtual bool containsRelPointImpl ( real_t px, real_t py, real_t pz ) const;
-   virtual bool isSurfaceRelPointImpl( real_t px, real_t py, real_t pz ) const;
+   bool containsRelPointImpl ( real_t px, real_t py, real_t pz ) const override;
+   bool isSurfaceRelPointImpl( real_t px, real_t py, real_t pz ) const override;
    //@}
    //**********************************************************************************************
 
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   inline virtual void calcBoundingBox();  // Calculation of the axis-aligned bounding box
+   inline void calcBoundingBox() override;  // Calculation of the axis-aligned bounding box
    //@}
    //**********************************************************************************************
 
diff --git a/src/pe/rigidbody/GeomPrimitive.h b/src/pe/rigidbody/GeomPrimitive.h
index 2fa2f059d78d303c7df34748860eca66f070363a..359a7856f44fa73c1dbc8efd6ee746cbba59b203 100644
--- a/src/pe/rigidbody/GeomPrimitive.h
+++ b/src/pe/rigidbody/GeomPrimitive.h
@@ -68,7 +68,7 @@ protected:
    //**Destructor**********************************************************************************
    /*!\name Destructor */
    //@{
-   virtual ~GeomPrimitive() = 0;
+   ~GeomPrimitive() override = 0;
    //@}
    //**********************************************************************************************
 
diff --git a/src/pe/rigidbody/MPIRigidBodyTrait.h b/src/pe/rigidbody/MPIRigidBodyTrait.h
index 022bc2dbc7c8c1f670ac868ea06685a9009981c4..5e24baa2947156c93904054ae12d8bbc0b33a13b 100644
--- a/src/pe/rigidbody/MPIRigidBodyTrait.h
+++ b/src/pe/rigidbody/MPIRigidBodyTrait.h
@@ -47,15 +47,15 @@ class MPIRigidBodyTrait
 {
 protected:
    //**Type definitions****************************************************************************
-   typedef std::vector<Owner>             ShadowOwners;       //!< Vector for remote MPI processes the rigid body is contained in.
-   typedef std::set<BlockID>              BlockStates;        //!< Stores the information if neighbor block knows about object.
+   using ShadowOwners = std::vector<Owner>;       //!< Vector for remote MPI processes the rigid body is contained in.
+   using BlockStates = std::set<BlockID>;        //!< Stores the information if neighbor block knows about object.
    //**********************************************************************************************
 
 public:
    //**Type definitions****************************************************************************
-   typedef ShadowOwners::iterator        ShadowOwnersIterator;       //!< Iterator over the connected processes.
-   typedef ShadowOwners::const_iterator  ConstShadowOwnersIterator;  //!< ConstIterator over the connected processes.
-   typedef size_t                        SizeType;
+   using ShadowOwnersIterator = ShadowOwners::iterator;       //!< Iterator over the connected processes.
+   using ConstShadowOwnersIterator = ShadowOwners::const_iterator;  //!< ConstIterator over the connected processes.
+   using SizeType = size_t;
    //**********************************************************************************************
 
    //**Constructor*********************************************************************************
@@ -68,7 +68,7 @@ public:
    //**Destructor**********************************************************************************
    /*!\name Destructor */
    //@{
-   virtual ~MPIRigidBodyTrait() {};
+   virtual ~MPIRigidBodyTrait() = default;
    //@}
    //**********************************************************************************************
 
@@ -178,9 +178,7 @@ inline void MPIRigidBodyTrait::deregisterShadowOwner( const Owner& owner )
  */
 inline bool MPIRigidBodyTrait::isShadowOwnerRegistered( const Owner& owner ) const
 {
-   if( std::find( shadowOwners_.begin(), shadowOwners_.end(), owner ) == shadowOwners_.end() )
-      return false;
-   else return true;
+   return std::find( shadowOwners_.begin(), shadowOwners_.end(), owner ) != shadowOwners_.end();
 }
 //*************************************************************************************************
 
diff --git a/src/pe/rigidbody/Owner.cpp b/src/pe/rigidbody/Owner.cpp
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/src/pe/rigidbody/Plane.h b/src/pe/rigidbody/Plane.h
index 94368f79fcba13971c0541e1bc8f60cbc848b4bf..1e8bd0966ed922e06735c722384330918a5a3e08 100644
--- a/src/pe/rigidbody/Plane.h
+++ b/src/pe/rigidbody/Plane.h
@@ -79,7 +79,7 @@ public:
    //**Destructor**********************************************************************************
    /*!\name Destructor */
    //@{
-   virtual ~Plane();
+   ~Plane() override;
    //@}
    //**********************************************************************************************
 
@@ -87,7 +87,7 @@ public:
    //**Get functions*******************************************************************************
    /*!\name Get functions */
    //@{
-   virtual inline real_t getVolume()       const;
+   inline real_t getVolume()       const override;
    inline const Vec3&    getNormal()       const;
    inline real_t         getDisplacement() const;
    //@}
@@ -112,7 +112,7 @@ public:
    //**Output functions****************************************************************************
    /*!\name Output functions */
    //@{
-   virtual void print( std::ostream& os, const char* tab ) const;
+   void print( std::ostream& os, const char* tab ) const override;
    //@}
    //**********************************************************************************************
 
@@ -120,21 +120,21 @@ protected:
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   virtual void setPositionImpl       ( real_t px, real_t py, real_t pz );
-   virtual void setOrientationImpl    ( real_t r, real_t i, real_t j, real_t k );
-   virtual void translateImpl         ( real_t dx, real_t dy, real_t dz );
-   virtual void rotateImpl            ( const Quat& dq );
-   virtual void rotateAroundOriginImpl( const Quat& dq );
-   virtual void rotateAroundPointImpl ( const Vec3& point, const Quat& dq );
-   virtual bool containsRelPointImpl   ( real_t px, real_t py, real_t pz ) const;
-   virtual bool isSurfaceRelPointImpl  ( real_t px, real_t py, real_t pz ) const;
+   void setPositionImpl       ( real_t px, real_t py, real_t pz ) override;
+   void setOrientationImpl    ( real_t r, real_t i, real_t j, real_t k ) override;
+   void translateImpl         ( real_t dx, real_t dy, real_t dz ) override;
+   void rotateImpl            ( const Quat& dq ) override;
+   void rotateAroundOriginImpl( const Quat& dq ) override;
+   void rotateAroundPointImpl ( const Vec3& point, const Quat& dq ) override;
+   bool containsRelPointImpl   ( real_t px, real_t py, real_t pz ) const override;
+   bool isSurfaceRelPointImpl  ( real_t px, real_t py, real_t pz ) const override;
    //@}
    //**********************************************************************************************
 
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   virtual void calcBoundingBox();  // Calculation of the axis-aligned bounding box
+   void calcBoundingBox() override;  // Calculation of the axis-aligned bounding box
    //@}
    //**********************************************************************************************
 
diff --git a/src/pe/rigidbody/RigidBody.h b/src/pe/rigidbody/RigidBody.h
index 440abac642f2e9f018dcd59c519dd931bcc76f9a..eaecb02dabdb773b29f55f95a8695b9af7664f61 100644
--- a/src/pe/rigidbody/RigidBody.h
+++ b/src/pe/rigidbody/RigidBody.h
@@ -498,7 +498,7 @@ inline void RigidBody::calcMotion()
  */
 inline bool RigidBody::hasManager() const
 {
-   return manager_ != 0;
+   return manager_ != nullptr;
 }
 //*************************************************************************************************
 
diff --git a/src/pe/rigidbody/RigidBodyCastIterator.h b/src/pe/rigidbody/RigidBodyCastIterator.h
index 7241baa63ce3b6eac5bafb20be5571016f992526..3486472b4e501d577edd594b784fbf28d1061b3b 100644
--- a/src/pe/rigidbody/RigidBodyCastIterator.h
+++ b/src/pe/rigidbody/RigidBodyCastIterator.h
@@ -60,7 +60,7 @@ public:
    //**Constructors********************************************************************************
    /*!\name Constructors */
    //@{
-   inline RigidBodyCastIterator() {}
+   inline RigidBodyCastIterator() = default;
    explicit inline RigidBodyCastIterator( const typename ContainerType::iterator& begin, const typename ContainerType::iterator& end );
 
    RigidBodyCastIterator( const RigidBodyCastIterator<C>& it) = default;
@@ -207,7 +207,7 @@ public:
    //**Constructors********************************************************************************
    /*!\name Constructors */
    //@{
-   inline ConstRigidBodyCastIterator() {}
+   inline ConstRigidBodyCastIterator() = default;
    explicit inline ConstRigidBodyCastIterator( const typename ContainerType::const_iterator& begin,
                                                const typename ContainerType::const_iterator& end );
 
diff --git a/src/pe/rigidbody/RigidBodyIterator.h b/src/pe/rigidbody/RigidBodyIterator.h
index 8e2be67d2f299dde26ebb313b4a17e4c22642301..ffa60d2c69a948e037c99dd407d95490a178741f 100644
--- a/src/pe/rigidbody/RigidBodyIterator.h
+++ b/src/pe/rigidbody/RigidBodyIterator.h
@@ -58,7 +58,7 @@ public:
    //**Constructors********************************************************************************
    /*!\name Constructors */
    //@{
-   inline RigidBodyIterator() {}
+   inline RigidBodyIterator() = default;
    explicit inline RigidBodyIterator( const typename ContainerType::iterator& it ) : it_(it) {}
 
    RigidBodyIterator( const RigidBodyIterator& it) = default;
@@ -226,7 +226,7 @@ public:
    //**Constructors********************************************************************************
    /*!\name Constructors */
    //@{
-   inline ConstRigidBodyIterator() {}
+   inline ConstRigidBodyIterator() = default;
    inline ConstRigidBodyIterator( const RigidBodyIterator& it ) : it_(it.get()) {}
    explicit inline ConstRigidBodyIterator( const typename ContainerType::iterator& it ) : it_(it) {}
    explicit inline ConstRigidBodyIterator( const typename ContainerType::const_iterator& it ) : it_(it) {}
diff --git a/src/pe/rigidbody/Sphere.h b/src/pe/rigidbody/Sphere.h
index 669cf07bcb15805f80c45f7992a4c31c7233678d..a7400fdb7d93d899b6fa686c9e663cacf0a1062b 100644
--- a/src/pe/rigidbody/Sphere.h
+++ b/src/pe/rigidbody/Sphere.h
@@ -75,7 +75,7 @@ public:
    //**Destructor**********************************************************************************
    /*!\name Destructor */
    //@{
-   virtual ~Sphere();
+   ~Sphere() override;
    //@}
    //**********************************************************************************************
    //**********************************************************************************************
@@ -85,7 +85,7 @@ public:
    /*!\name Get functions */
    //@{
    inline real_t getRadius() const;
-   virtual inline real_t getVolume()         const;
+   inline real_t getVolume()         const override;
    //@}
    //**********************************************************************************************
 
@@ -107,14 +107,14 @@ public:
    //**Output functions****************************************************************************
    /*!\name Output functions */
    //@{
-   virtual void print( std::ostream& os, const char* tab ) const;
+   void print( std::ostream& os, const char* tab ) const override;
    //@}
    //**********************************************************************************************
 
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   inline virtual Vec3 support( const Vec3& d ) const;
+   inline Vec3 support( const Vec3& d ) const override;
    //@}
    //**********************************************************************************************
 
@@ -132,15 +132,15 @@ protected:
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   virtual bool containsRelPointImpl ( real_t px, real_t py, real_t pz ) const;
-   virtual bool isSurfaceRelPointImpl( real_t px, real_t py, real_t pz ) const;
+   bool containsRelPointImpl ( real_t px, real_t py, real_t pz ) const override;
+   bool isSurfaceRelPointImpl( real_t px, real_t py, real_t pz ) const override;
    //@}
    //**********************************************************************************************
 
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   inline virtual void calcBoundingBox();  // Calculation of the axis-aligned bounding box
+   inline void calcBoundingBox() override;  // Calculation of the axis-aligned bounding box
    //@}
    //**********************************************************************************************
 
diff --git a/src/pe/rigidbody/Squirmer.h b/src/pe/rigidbody/Squirmer.h
index 41a27c9a78474ff82a677d45bb4a5e4af65da3ff..69270d006f0ed64a296b6cadfa6ea6b9dea5f2f5 100644
--- a/src/pe/rigidbody/Squirmer.h
+++ b/src/pe/rigidbody/Squirmer.h
@@ -41,7 +41,7 @@ public:
    //**Destructor**********************************************************************************
    /*!\name Destructor */
    //@{
-   virtual ~Squirmer();
+   ~Squirmer() override;
    //@}
    //**********************************************************************************************
    //**********************************************************************************************
diff --git a/src/pe/rigidbody/StorageDataHandling.h b/src/pe/rigidbody/StorageDataHandling.h
index 12495a01b0b56bd80e1a26d062b9edbd9098c00c..4260f5278572206dddb39b36ce25148d28ad9446 100644
--- a/src/pe/rigidbody/StorageDataHandling.h
+++ b/src/pe/rigidbody/StorageDataHandling.h
@@ -40,32 +40,32 @@ namespace pe{
 template<typename BodyTuple>
 class StorageDataHandling : public blockforest::BlockDataHandling<Storage>{
 public:
-   virtual ~StorageDataHandling() {}
+   ~StorageDataHandling() override = default;
 
    /// must be thread-safe !
-   virtual Storage * initialize( IBlock * const block ) override;
+   Storage * initialize( IBlock * const block ) override;
 
    /// must be thread-safe !
-   virtual void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override;
+   void serialize( IBlock * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override;
    /// must be thread-safe !
-   virtual Storage * deserialize( IBlock * const block ) override;
+   Storage * deserialize( IBlock * const block ) override;
    /// must be thread-safe !
-   virtual void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override;
+   void deserialize( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override;
 
    /// must be thread-safe !
-   virtual void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child ) override;
+   void serializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer, const uint_t child ) override;
    /// must be thread-safe !
-   virtual void serializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override;
+   void serializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::SendBuffer & buffer ) override;
 
    /// must be thread-safe !
-   virtual Storage * deserializeCoarseToFine( Block * const block ) override;
+   Storage * deserializeCoarseToFine( Block * const block ) override;
    /// must be thread-safe !
-   virtual Storage * deserializeFineToCoarse( Block * const block ) override;
+   Storage * deserializeFineToCoarse( Block * const block ) override;
 
    /// must be thread-safe !
-   virtual void deserializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override;
+   void deserializeCoarseToFine( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer ) override;
    /// must be thread-safe !
-   virtual void deserializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer, const uint_t child ) override;
+   void deserializeFineToCoarse( Block * const block, const BlockDataID & id, mpi::RecvBuffer & buffer, const uint_t child ) override;
 
 private:
    void deserializeImpl( IBlock * const block, const BlockDataID & id, mpi::RecvBuffer & buffer );
diff --git a/src/pe/rigidbody/Union.h b/src/pe/rigidbody/Union.h
index e9337e069d82375ffcbc48b605cfb4b8c71fc461..31a224d2602df0e7339307a191f3c144faf67be9 100644
--- a/src/pe/rigidbody/Union.h
+++ b/src/pe/rigidbody/Union.h
@@ -87,7 +87,7 @@ public:
    //**Destructor**********************************************************************************
    /*!\name Destructor */
    //@{
-   virtual ~Union();
+   ~Union() override;
    //@}
    //**********************************************************************************************
    //**********************************************************************************************
@@ -121,23 +121,23 @@ public:
    //@}
    //**********************************************************************************************
 
-   virtual inline real_t getVolume()         const override;
+   inline real_t getVolume()         const override;
 
    //**Set functions*******************************************************************************
    /*!\name Set functions */
    //@{
-   virtual void setRemote( bool remote ) override;
+   void setRemote( bool remote ) override;
    //@}
    //**********************************************************************************************
 
-   virtual inline bool   hasSubBodies()      const override { return true; }
+   inline bool   hasSubBodies()      const override { return true; }
 
    //**Signal functions***************************************************************************
    /*!\name Signal functions */
    //@{
-   virtual void handleModification() override;
-   virtual void handleTranslation() override;
-   virtual void handleRotation() override;
+   void handleModification() override;
+   void handleTranslation() override;
+   void handleRotation() override;
    //@}
    //**********************************************************************************************
 
@@ -158,7 +158,7 @@ public:
    //**Output functions****************************************************************************
    /*!\name Output functions */
    //@{
-   virtual void print( std::ostream& os, const char* tab ) const override;
+   void print( std::ostream& os, const char* tab ) const override;
    //@}
    //**********************************************************************************************
 
@@ -166,21 +166,21 @@ protected:
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   virtual void setPositionImpl       ( real_t px, real_t py, real_t pz )         override;
-   virtual void setOrientationImpl    ( real_t r, real_t i, real_t j, real_t k )  override;
-   virtual void translateImpl         ( real_t dx, real_t dy, real_t dz )         override;
-   virtual void rotateImpl            ( const Quat& dq )                          override;
-   virtual void rotateAroundOriginImpl( const Quat& dq )                          override;
-   virtual void rotateAroundPointImpl ( const Vec3& point, const Quat& dq )       override;
-   virtual bool containsRelPointImpl   ( real_t px, real_t py, real_t pz ) const  override;
-   virtual bool isSurfaceRelPointImpl  ( real_t px, real_t py, real_t pz ) const  override;
+   void setPositionImpl       ( real_t px, real_t py, real_t pz )         override;
+   void setOrientationImpl    ( real_t r, real_t i, real_t j, real_t k )  override;
+   void translateImpl         ( real_t dx, real_t dy, real_t dz )         override;
+   void rotateImpl            ( const Quat& dq )                          override;
+   void rotateAroundOriginImpl( const Quat& dq )                          override;
+   void rotateAroundPointImpl ( const Vec3& point, const Quat& dq )       override;
+   bool containsRelPointImpl   ( real_t px, real_t py, real_t pz ) const  override;
+   bool isSurfaceRelPointImpl  ( real_t px, real_t py, real_t pz ) const  override;
    //@}
    //**********************************************************************************************
 
    //**Utility functions***************************************************************************
    /*!\name Utility functions */
    //@{
-   inline virtual void calcBoundingBox() override;  // Calculation of the axis-aligned bounding box
+   inline void calcBoundingBox() override;  // Calculation of the axis-aligned bounding box
    inline         void calcCenterOfMass(); // Compute mass and center of gravity
    inline         void calcInertia();      // Calculation of the moment of inertia
    //@}
diff --git a/src/pe/rigidbody/UnionFactory.h b/src/pe/rigidbody/UnionFactory.h
index 6ad1d934e5a8391c1c44f6e4f513d15342b2c677..04090a61dfa8e06d05a4f1275ae0bd4270ea28da 100644
--- a/src/pe/rigidbody/UnionFactory.h
+++ b/src/pe/rigidbody/UnionFactory.h
@@ -133,7 +133,7 @@ BoxID createBox( Union<BodyTypes...>* un,
       throw std::runtime_error("Box TypeID not initalized!");
 
    // union not on this process/block -> terminate creation
-   if (un == NULL)
+   if (un == nullptr)
       throw std::invalid_argument( "createBox: Union argument is NULL" );
 
    // main union not on this process/block -> terminate creation
@@ -159,7 +159,7 @@ BoxID createBox( Union<BodyTypes...>* un,
    std::unique_ptr<Box> box = std::make_unique<Box>(sid, uid, gpos, Quat(), lengths, material, global, communicating, infiniteMass);
    box->MPITrait.setOwner( un->MPITrait.getOwner() );
 
-   if (box != NULL)
+   if (box != nullptr)
    {
       // Logging the successful creation of the box
       WALBERLA_LOG_DETAIL(
@@ -199,7 +199,7 @@ CapsuleID createCapsule( Union<BodyTypes...>* un,
       throw std::runtime_error("Capsule TypeID not initalized!");
 
    // union not on this process/block -> terminate creation
-   if (un == NULL)
+   if (un == nullptr)
       throw std::invalid_argument( "createCapsule: Union argument is NULL" );
 
    // main union not on this process/block -> terminate creation
@@ -229,7 +229,7 @@ CapsuleID createCapsule( Union<BodyTypes...>* un,
    std::unique_ptr<Capsule> capsule = std::make_unique<Capsule>(sid, uid, gpos, Quat(), radius, length, material, global, communicating, infiniteMass);
    capsule->MPITrait.setOwner( un->MPITrait.getOwner() );
 
-   if (capsule != NULL)
+   if (capsule != nullptr)
    {
       WALBERLA_LOG_DETAIL("Created capsule " << capsule->getSystemID() << "\n" << *capsule);
    }
@@ -260,7 +260,7 @@ SphereID createSphere( Union<BodyTypes...>* un,
       throw std::runtime_error("Sphere TypeID not initalized!");
 
    // union not on this process/block -> terminate creation
-   if (un == NULL)
+   if (un == nullptr)
       throw std::invalid_argument( "createSphere: Union argument is NULL" );
 
    // main union not on this process/block -> terminate creation
@@ -287,7 +287,7 @@ SphereID createSphere( Union<BodyTypes...>* un,
    std::unique_ptr<Sphere> sphere = std::make_unique<Sphere>(sid, uid, gpos, Quat(), radius, material, global, communicating, infiniteMass);
    sphere->MPITrait.setOwner( un->MPITrait.getOwner() );
 
-   if (sphere != NULL)
+   if (sphere != nullptr)
    {
       // Logging the successful creation of the sphere
       WALBERLA_LOG_DETAIL(
diff --git a/src/pe/synchronization/SyncForces.h b/src/pe/synchronization/SyncForces.h
index b28fb837ab365264a78bef4a1812962f707967d3..a990719436cf811c15d6074534c7437bb0b4b805 100644
--- a/src/pe/synchronization/SyncForces.h
+++ b/src/pe/synchronization/SyncForces.h
@@ -110,7 +110,7 @@ void reduceForces( BlockStorage& blocks, BlockDataID storageID )
          it.buffer() >> sender;
          it.buffer() >> receiver;
          auto blk = blocks.getBlock(receiver);
-         WALBERLA_CHECK(blk != NULL, receiver << " not on this process!");
+         WALBERLA_CHECK(blk != nullptr, receiver << " not on this process!");
          IBlock& block = *blk;
          Storage* storage  = block.getData< Storage >( storageID );
          BodyStorage& localStorage  = (*storage)[0];
@@ -231,7 +231,7 @@ void distributeForces( BlockStorage& blocks, BlockDataID storageID )
          it.buffer() >> sender;
          it.buffer() >> receiver;
          auto blk = blocks.getBlock(receiver);
-         WALBERLA_CHECK(blk != NULL, receiver << " not on this process!");
+         WALBERLA_CHECK(blk != nullptr, receiver << " not on this process!");
          IBlock& block = *blk;
          Storage* storage  = block.getData< Storage >( storageID );
          BodyStorage& localStorage  = (*storage)[0];
diff --git a/src/pe/synchronization/SyncNextNeighbors.h b/src/pe/synchronization/SyncNextNeighbors.h
index 83ad74ac0a839491f1d8023696354b61fa7f3cba..4fd166011d6756241972612723d7f074a0c8d7db 100644
--- a/src/pe/synchronization/SyncNextNeighbors.h
+++ b/src/pe/synchronization/SyncNextNeighbors.h
@@ -250,10 +250,10 @@ void generateSynchonizationMessages(mpi::BufferSystem& bs, const Block& block, B
 }
 
 template <typename BodyTypeTuple>
-void syncNextNeighbors( BlockForest& forest, BlockDataID storageID, WcTimingTree* tt = NULL, const real_t dx = real_t(0), const bool syncNonCommunicatingBodies = false )
+void syncNextNeighbors( BlockForest& forest, BlockDataID storageID, WcTimingTree* tt = nullptr, const real_t dx = real_t(0), const bool syncNonCommunicatingBodies = false )
 {
-   if (tt != NULL) tt->start("Sync");
-   if (tt != NULL) tt->start("Assembling Body Synchronization");
+   if (tt != nullptr) tt->start("Sync");
+   if (tt != nullptr) tt->start("Assembling Body Synchronization");
    mpi::BufferSystem bs( mpi::MPIManager::instance()->comm() );
 
    for (auto it = forest.begin(); it != forest.end(); ++it)
@@ -274,13 +274,13 @@ void syncNextNeighbors( BlockForest& forest, BlockDataID storageID, WcTimingTree
       }
       generateSynchonizationMessages<BodyTypeTuple>(bs, *block, *localStorage, *shadowStorage, dx, syncNonCommunicatingBodies);
    }
-   if (tt != NULL) tt->stop("Assembling Body Synchronization");
+   if (tt != nullptr) tt->stop("Assembling Body Synchronization");
 
    // size of buffer is unknown and changes with each send
    bs.setReceiverInfoFromSendBufferState(false, true);
    bs.sendAll();
 
-   if (tt != NULL) tt->start("Parsing Body Synchronization");
+   if (tt != nullptr) tt->start("Parsing Body Synchronization");
    // Receiving the updates for the remote rigid bodies from the connected processes
    WALBERLA_LOG_DETAIL( "Parsing of body synchronization response starts..." );
    for( auto it = bs.begin(); it != bs.end(); ++it )
@@ -294,7 +294,7 @@ void syncNextNeighbors( BlockForest& forest, BlockDataID storageID, WcTimingTree
          it.buffer() >> sender;
          it.buffer() >> receiver;
          auto blk = forest.getBlock(receiver);
-         WALBERLA_CHECK(blk != NULL, receiver << " not on this process!");
+         WALBERLA_CHECK(blk != nullptr, receiver << " not on this process!");
          IBlock& block = *blk;
          Storage* storage  = block.getData< Storage >( storageID );
          BodyStorage& localStorage  = (*storage)[0];
@@ -303,8 +303,8 @@ void syncNextNeighbors( BlockForest& forest, BlockDataID storageID, WcTimingTree
       }
    }
    WALBERLA_LOG_DETAIL( "Parsing of body synchronization response ended." );
-   if (tt != NULL) tt->stop("Parsing Body Synchronization");
-   if (tt != NULL) tt->stop("Sync");
+   if (tt != nullptr) tt->stop("Parsing Body Synchronization");
+   if (tt != nullptr) tt->stop("Sync");
 }
 
 }  // namespace pe
diff --git a/src/pe/synchronization/SyncShadowOwners.h b/src/pe/synchronization/SyncShadowOwners.h
index 89716ed2877a300fb0d29dcb34680f59a031a851..32669e30349f6c4e72be0ffde47cce56d08b412f 100644
--- a/src/pe/synchronization/SyncShadowOwners.h
+++ b/src/pe/synchronization/SyncShadowOwners.h
@@ -193,7 +193,7 @@ void updateAndMigrate( BlockForest& forest, BlockDataID storageID, const bool sy
          it.buffer() >> sender;
          it.buffer() >> receiver;
          auto blk = forest.getBlock(receiver);
-         WALBERLA_CHECK(blk != NULL, receiver << " not on this process!");
+         WALBERLA_CHECK(blk != nullptr, receiver << " not on this process!");
          Block * block = dynamic_cast< Block * >( blk );
          Storage* storage  = block->getData< Storage >( storageID );
          BodyStorage& localStorage  = (*storage)[0];
@@ -360,7 +360,7 @@ void checkAndResolveOverlap( BlockForest& forest, BlockDataID storageID, const r
          it.buffer() >> sender;
          it.buffer() >> receiver;
          auto blk = forest.getBlock(receiver);
-         WALBERLA_CHECK(blk != NULL, receiver << " not on this process!");
+         WALBERLA_CHECK(blk != nullptr, receiver << " not on this process!");
          Block * block = dynamic_cast< Block * >( blk );
          Storage* storage  = block->getData< Storage >( storageID );
          BodyStorage& localStorage  = (*storage)[0];
@@ -372,25 +372,25 @@ void checkAndResolveOverlap( BlockForest& forest, BlockDataID storageID, const r
 }
 
 template <typename BodyTypeTuple>
-void syncShadowOwners( BlockForest& forest, BlockDataID storageID, WcTimingTree* tt = NULL, const real_t dx = real_t(0), const bool syncNonCommunicatingBodies = false )
+void syncShadowOwners( BlockForest& forest, BlockDataID storageID, WcTimingTree* tt = nullptr, const real_t dx = real_t(0), const bool syncNonCommunicatingBodies = false )
 {
-   if (tt != NULL) tt->start("Sync");
+   if (tt != nullptr) tt->start("Sync");
 
    //==========================================================
    // STEP1: Update & Migrate
    //==========================================================
-   if (tt != NULL) tt->start("Update&Migrate");
+   if (tt != nullptr) tt->start("Update&Migrate");
    updateAndMigrate<BodyTypeTuple>( forest, storageID, syncNonCommunicatingBodies);
-   if (tt != NULL) tt->stop("Update&Migrate");
+   if (tt != nullptr) tt->stop("Update&Migrate");
 
    //==========================================================
    // STEP2: Check & Resolve
    //==========================================================
-   if (tt != NULL) tt->start("Check&Resolve");
+   if (tt != nullptr) tt->start("Check&Resolve");
    checkAndResolveOverlap<BodyTypeTuple>( forest, storageID, dx, syncNonCommunicatingBodies);
-   if (tt != NULL) tt->stop("Check&Resolve");
+   if (tt != nullptr) tt->stop("Check&Resolve");
 
-   if (tt != NULL) tt->stop("Sync");
+   if (tt != nullptr) tt->stop("Sync");
 }
 
 }
diff --git a/src/pe/utility/BodyCast.h b/src/pe/utility/BodyCast.h
index ebd6574b7c0cf3d49d978f73474c38ae3f5dae77..f3f6e1ca82114feba8db8b0006a1009b64ef5175 100644
--- a/src/pe/utility/BodyCast.h
+++ b/src/pe/utility/BodyCast.h
@@ -39,7 +39,7 @@ public:
       static_assert(std::is_base_of<RigidBody, CastBodyType>::value, "only downcasting allowed!");
       if (CastBodyType::getStaticTypeID() == typeID)
       {
-         CastBodyType* bd = NULL;
+         CastBodyType* bd = nullptr;
          return func( static_cast<CastBodyType *>( bd ) );
       } else
       {
diff --git a/src/pe/vtk/BodyVtkOutput.h b/src/pe/vtk/BodyVtkOutput.h
index 3898f6f10b86f17cbba3e965fcf7e45dadad4561..4a91a88933d523f30ab7a79bd16c8061638114c1 100644
--- a/src/pe/vtk/BodyVtkOutput.h
+++ b/src/pe/vtk/BodyVtkOutput.h
@@ -42,14 +42,14 @@ public:
       : storageID_( storageID )
       , blockStorage_( blockStorage ) { }
 
-   std::vector< Attributes > getAttributes() const;
+   std::vector< Attributes > getAttributes() const override;
 
-   void configure();
+   void configure() override;
 
-   std::vector< Vector3< real_t > > getPoints();
+   std::vector< Vector3< real_t > > getPoints() override;
 
-   inline void push( std::ostream& os , const uint_t /*data*/, const uint_t point, const uint_t component );
-   inline void push( vtk::Base64Writer& b64, const uint_t /*data*/, const uint_t point, const uint_t component );
+   inline void push( std::ostream& os , const uint_t /*data*/, const uint_t point, const uint_t component ) override;
+   inline void push( vtk::Base64Writer& b64, const uint_t /*data*/, const uint_t point, const uint_t component ) override;
 
 private:
 
diff --git a/src/pe/vtk/EllipsoidVtkOutput.h b/src/pe/vtk/EllipsoidVtkOutput.h
index 767c1b9783d47bac4a11b32401f082067346cee1..b4ab6c2415a95ccd87c92c0b61bac3f036bbd4b1 100644
--- a/src/pe/vtk/EllipsoidVtkOutput.h
+++ b/src/pe/vtk/EllipsoidVtkOutput.h
@@ -46,14 +46,14 @@ public:
       : storageID_( storageID )
       , blockStorage_( blockStorage ) { }
 
-   std::vector< Attributes > getAttributes() const;
+   std::vector< Attributes > getAttributes() const override;
 
-   void configure();
+   void configure() override;
 
-   std::vector< Vector3< real_t > > getPoints();
+   std::vector< Vector3< real_t > > getPoints() override;
 
-   inline void push( std::ostream& os , const uint_t /*data*/, const uint_t point, const uint_t component );
-   inline void push( vtk::Base64Writer& b64, const uint_t /*data*/, const uint_t point, const uint_t component );
+   inline void push( std::ostream& os , const uint_t /*data*/, const uint_t point, const uint_t component ) override;
+   inline void push( vtk::Base64Writer& b64, const uint_t /*data*/, const uint_t point, const uint_t component ) override;
 
 private:
 
diff --git a/src/pe/vtk/SphereVtkOutput.h b/src/pe/vtk/SphereVtkOutput.h
index 655faec083d49e8adbb9397dcb9d9b043b84694c..db307c2738bbef22d42c2b2eec89dd10a859cde1 100755
--- a/src/pe/vtk/SphereVtkOutput.h
+++ b/src/pe/vtk/SphereVtkOutput.h
@@ -46,14 +46,14 @@ public:
       : storageID_( storageID )
       , blockStorage_( blockStorage ) { }
 
-   std::vector< Attributes > getAttributes() const;
+   std::vector< Attributes > getAttributes() const override;
 
-   void configure();
+   void configure() override;
 
-   std::vector< Vector3< real_t > > getPoints();
+   std::vector< Vector3< real_t > > getPoints() override;
 
-   inline void push( std::ostream& os , const uint_t /*data*/, const uint_t point, const uint_t component );
-   inline void push( vtk::Base64Writer& b64, const uint_t /*data*/, const uint_t point, const uint_t component );
+   inline void push( std::ostream& os , const uint_t /*data*/, const uint_t point, const uint_t component ) override;
+   inline void push( vtk::Base64Writer& b64, const uint_t /*data*/, const uint_t point, const uint_t component ) override;
 
 private:
 
diff --git a/src/pe_coupling/amr/InfoCollection.h b/src/pe_coupling/amr/InfoCollection.h
index 86e508e87e9e183fab6464eae655dbd6372984b1..c39d41a6da015a91f64450c4db8654644bc8db9b 100644
--- a/src/pe_coupling/amr/InfoCollection.h
+++ b/src/pe_coupling/amr/InfoCollection.h
@@ -35,8 +35,8 @@
 namespace walberla {
 namespace pe_coupling {
 
-typedef std::map<blockforest::BlockID, BlockInfo>  InfoCollection;
-typedef std::pair<blockforest::BlockID, BlockInfo> InfoCollectionPair;
+using InfoCollection = std::map<blockforest::BlockID, BlockInfo>;
+using InfoCollectionPair = std::pair<blockforest::BlockID, BlockInfo>;
 
 template <typename BoundaryHandling_T>
 void createWithNeighborhood(BlockForest& bf, const BlockDataID boundaryHandlingID,
diff --git a/src/pe_coupling/amr/weight_assignment/WeightAssignmentFunctor.h b/src/pe_coupling/amr/weight_assignment/WeightAssignmentFunctor.h
index 5fa776362e591d01a5286eb7922b179875bfbe06..ad5aa44893869bc0283fec46a6eb1146b22bdfa5 100644
--- a/src/pe_coupling/amr/weight_assignment/WeightAssignmentFunctor.h
+++ b/src/pe_coupling/amr/weight_assignment/WeightAssignmentFunctor.h
@@ -34,7 +34,7 @@ namespace amr {
 class WeightAssignmentFunctor
 {
 public:
-   typedef walberla::blockforest::PODPhantomWeight<double> PhantomBlockWeight;
+   using PhantomBlockWeight = walberla::blockforest::PODPhantomWeight<double>;
 
    WeightAssignmentFunctor( const shared_ptr<InfoCollection>& ic,
                             const std::function<real_t(const BlockInfo&)> & weightEvaluationFct ) :
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/AddedMassForceEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/AddedMassForceEvaluator.h
index c452f886443049220e2529456a8fc5673175bd98..c441e90090d655c91d72ffab35d8babe1f41dba1 100644
--- a/src/pe_coupling/discrete_particle_methods/evaluators/AddedMassForceEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/AddedMassForceEvaluator.h
@@ -70,9 +70,9 @@ class AddedMassForceEvaluator
 
 public:
 
-   typedef GhostLayerField< Vector3<real_t>, 1>             Vec3Field_T;
-   typedef FieldInterpolator_T<Vec3Field_T, FlagField_T>    Vec3FieldInterpolator_T;
-   typedef Distributor_T<Vec3Field_T, FlagField_T>          ForceDistributor_T;
+   using Vec3Field_T = GhostLayerField<Vector3<real_t>, 1>;
+   using Vec3FieldInterpolator_T = FieldInterpolator_T<Vec3Field_T, FlagField_T>;
+   using ForceDistributor_T = Distributor_T<Vec3Field_T, FlagField_T>;
 
    AddedMassForceEvaluator( const shared_ptr<StructuredBlockStorage> & blockStorage,
                             const BlockDataID & forceFieldID, const BlockDataID & bodyStorageID,
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/EffectiveViscosityFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/EffectiveViscosityFieldEvaluator.h
index fc10c9b975cf8e5845432f62d439b354fefb6f3f..f8039c7e6382121f7e19417476a188ca8cbe8914 100644
--- a/src/pe_coupling/discrete_particle_methods/evaluators/EffectiveViscosityFieldEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/EffectiveViscosityFieldEvaluator.h
@@ -62,7 +62,7 @@ real_t calculateEilersEffectiveViscosity( real_t fluidViscosity, real_t porosity
 class EffectiveViscosityFieldEvaluator
 {
 public:
-   typedef GhostLayerField< real_t, 1 >  ScalarField_T;
+   using ScalarField_T = GhostLayerField<real_t, 1>;
 
    EffectiveViscosityFieldEvaluator( const BlockDataID & omegaFieldID, const ConstBlockDataID & solidVolumeFractionFieldID,
                                      const real_t & fluidViscosity,
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/InteractionForceEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/InteractionForceEvaluator.h
index 11709d1e8f4c1dc0c182f64e51e5203db20d85e8..9f5125fc866cad39248a7d553eaca116bb709af1 100644
--- a/src/pe_coupling/discrete_particle_methods/evaluators/InteractionForceEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/InteractionForceEvaluator.h
@@ -78,11 +78,11 @@ class InteractionForceEvaluator
 
 public:
 
-   typedef GhostLayerField< Vector3<real_t>, 1>             Vec3Field_T;
-   typedef GhostLayerField< real_t, 1>                      ScalarField_T;
-   typedef FieldInterpolator_T<Vec3Field_T, FlagField_T>    Vec3FieldInterpolator_T;
-   typedef FieldInterpolator_T<ScalarField_T, FlagField_T>  ScalarFieldInterpolator_T;
-   typedef Distributor_T<Vec3Field_T, FlagField_T>          ForceDistributor_T;
+   using Vec3Field_T = GhostLayerField<Vector3<real_t>, 1>;
+   using ScalarField_T = GhostLayerField<real_t, 1>;
+   using Vec3FieldInterpolator_T = FieldInterpolator_T<Vec3Field_T, FlagField_T>;
+   using ScalarFieldInterpolator_T = FieldInterpolator_T<ScalarField_T, FlagField_T>;
+   using ForceDistributor_T = Distributor_T<Vec3Field_T, FlagField_T>;
 
    InteractionForceEvaluator( const shared_ptr<StructuredBlockStorage> & blockStorage,
                               const BlockDataID & forceFieldID, const BlockDataID & bodyStorageID,
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/LiftForceEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/LiftForceEvaluator.h
index de8518349033f98cfddec3c1bd1f5678b440c151..3243b32bec50af012e8c9465aa27fac3bcc010b4 100644
--- a/src/pe_coupling/discrete_particle_methods/evaluators/LiftForceEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/LiftForceEvaluator.h
@@ -65,9 +65,9 @@ class LiftForceEvaluator
 
 public:
 
-   typedef GhostLayerField< Vector3<real_t>, 1>          Vec3Field_T;
-   typedef FieldInterpolator_T<Vec3Field_T, FlagField_T> Vec3FieldInterpolator_T;
-   typedef Distributor_T<Vec3Field_T, FlagField_T>       ForceDistributor_T;
+   using Vec3Field_T = GhostLayerField<Vector3<real_t>, 1>;
+   using Vec3FieldInterpolator_T = FieldInterpolator_T<Vec3Field_T, FlagField_T>;
+   using ForceDistributor_T = Distributor_T<Vec3Field_T, FlagField_T>;
 
    LiftForceEvaluator( const shared_ptr<StructuredBlockStorage> & blockStorage,
                        const BlockDataID & forceFieldID, const BlockDataID & bodyStorageID, const BlockDataID & flagFieldID, const Set< FlagUID > & domainFlags,
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/PressureFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/PressureFieldEvaluator.h
index 61998ae6dc80918604a101a9eed28a5d6f6cf8df..f7a639e55cf57b0121a8778285a5d2300040224f 100644
--- a/src/pe_coupling/discrete_particle_methods/evaluators/PressureFieldEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/PressureFieldEvaluator.h
@@ -39,8 +39,8 @@ template <typename LatticeModel_T, typename BoundaryHandling_T>
 class PressureFieldEvaluator
 {
 public:
-   typedef lbm::PdfField< LatticeModel_T >        PdfField_T;
-   typedef GhostLayerField< real_t, 1 >           ScalarField_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
+   using ScalarField_T = GhostLayerField<real_t, 1>;
 
    PressureFieldEvaluator( const BlockDataID & pressureFieldID, const ConstBlockDataID & pdfFieldID, const ConstBlockDataID & boundaryHandlingID )
       : pressureFieldID_( pressureFieldID ), pdfFieldID_( pdfFieldID ), boundaryHandlingID_( boundaryHandlingID )
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/PressureGradientFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/PressureGradientFieldEvaluator.h
index 2e8511fca1720e12df06cf04bde7c267fba6bb0e..a206b32ed67ea2164b007726bb838af2dc91ffc5 100644
--- a/src/pe_coupling/discrete_particle_methods/evaluators/PressureGradientFieldEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/PressureGradientFieldEvaluator.h
@@ -42,9 +42,9 @@ template <typename LatticeModel_T, typename BoundaryHandling_T>
 class PressureGradientFieldEvaluator
 {
 public:
-   typedef GhostLayerField< Vector3<real_t>, 1 >  VectorField_T;
-   typedef GhostLayerField< real_t, 1>            ScalarField_T;
-   typedef typename LatticeModel_T::Stencil       Stencil_T;
+   using VectorField_T = GhostLayerField<Vector3<real_t>, 1>;
+   using ScalarField_T = GhostLayerField<real_t, 1>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    PressureGradientFieldEvaluator( const BlockDataID & pressureGradientFieldID, const ConstBlockDataID & pressureFieldID,
                                    const ConstBlockDataID & boundaryHandlingID )
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/SolidVolumeFractionFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/SolidVolumeFractionFieldEvaluator.h
index 586b51bc25d29259c632ed1bb76dcfc4cd08c43b..0ba419f468597ee31d8655e1530d09244c2d4dfb 100644
--- a/src/pe_coupling/discrete_particle_methods/evaluators/SolidVolumeFractionFieldEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/SolidVolumeFractionFieldEvaluator.h
@@ -62,8 +62,8 @@ class SolidVolumeFractionFieldEvaluator
 {
 public:
 
-   typedef GhostLayerField< real_t, 1 >              ScalarField_T;
-   typedef Distributor_T<ScalarField_T, FlagField_T> ScalarDistributor_T;
+   using ScalarField_T = GhostLayerField<real_t, 1>;
+   using ScalarDistributor_T = Distributor_T<ScalarField_T, FlagField_T>;
 
    SolidVolumeFractionFieldEvaluator( const shared_ptr<StructuredBlockStorage> & blockStorage,
                                       const BlockDataID & solidVolumeFractionFieldID, const BlockDataID & bodyStorageID,
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/StressTensorGradientFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/StressTensorGradientFieldEvaluator.h
index 29dce0a61dbaccc8245379b21784b271017c8dbe..613e2a7c35afed6b6318daa0a2138bdae242b25a 100644
--- a/src/pe_coupling/discrete_particle_methods/evaluators/StressTensorGradientFieldEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/StressTensorGradientFieldEvaluator.h
@@ -43,9 +43,9 @@ template <typename LatticeModel_T, typename BoundaryHandling_T>
 class StressTensorGradientFieldEvaluator
 {
 public:
-   typedef GhostLayerField< Matrix3< real_t >, 1> TensorField_T;
-   typedef GhostLayerField< Vector3<real_t>, 1 >  VectorField_T;
-   typedef typename LatticeModel_T::Stencil       Stencil_T;
+   using TensorField_T = GhostLayerField<Matrix3<real_t>, 1>;
+   using VectorField_T = GhostLayerField<Vector3<real_t>, 1>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    StressTensorGradientFieldEvaluator( const BlockDataID & stressTensorGradientFieldID,
                                        const ConstBlockDataID & velocityGradientFieldID,
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/VelocityCurlFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityCurlFieldEvaluator.h
index 0442814dbaa2881004ca675da1fa8af03895bbc3..ed693ff86a278b6687418294d6164dd32ad1d569 100644
--- a/src/pe_coupling/discrete_particle_methods/evaluators/VelocityCurlFieldEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityCurlFieldEvaluator.h
@@ -42,8 +42,8 @@ template <typename LatticeModel_T, typename BoundaryHandling_T>
 class VelocityCurlFieldEvaluator
 {
 public:
-   typedef GhostLayerField< Vector3<real_t>, 1 >  VectorField_T;
-   typedef typename LatticeModel_T::Stencil       Stencil_T;
+   using VectorField_T = GhostLayerField<Vector3<real_t>, 1>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    VelocityCurlFieldEvaluator( const BlockDataID & velocityCurlFieldID, const ConstBlockDataID & velocityFieldID,
                                const ConstBlockDataID & boundaryHandlingID )
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/VelocityFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityFieldEvaluator.h
index 70d62853b3fce1c5a88defc46d8c9a0fcd5c48d8..b82f8fac30ef71d07b8e877264295c065eb013d0 100644
--- a/src/pe_coupling/discrete_particle_methods/evaluators/VelocityFieldEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityFieldEvaluator.h
@@ -44,8 +44,8 @@ template <typename LatticeModel_T, typename BoundaryHandling_T>
 class VelocityFieldEvaluator
 {
 public:
-   typedef lbm::PdfField< LatticeModel_T >        PdfField_T;
-   typedef GhostLayerField< Vector3<real_t>, 1 >  VelocityField_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
+   using VelocityField_T = GhostLayerField<Vector3<real_t>, 1>;
 
    VelocityFieldEvaluator( const BlockDataID & velocityFieldID,
                            const ConstBlockDataID & pdfFieldID, const ConstBlockDataID & boundaryHandlingID )
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/VelocityGradientFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityGradientFieldEvaluator.h
index 9e0397a5ab328b563e3e909801c7857b99ae07b1..0f85958ad8b683cfca4152796d050bf20d5107af 100644
--- a/src/pe_coupling/discrete_particle_methods/evaluators/VelocityGradientFieldEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityGradientFieldEvaluator.h
@@ -44,9 +44,9 @@ template <typename LatticeModel_T, typename BoundaryHandling_T>
 class VelocityGradientFieldEvaluator
 {
 public:
-   typedef GhostLayerField< Matrix3< real_t >, 1> TensorField_T;
-   typedef GhostLayerField< Vector3<real_t>, 1 >  VectorField_T;
-   typedef typename LatticeModel_T::Stencil       Stencil_T;
+   using TensorField_T = GhostLayerField<Matrix3<real_t>, 1>;
+   using VectorField_T = GhostLayerField<Vector3<real_t>, 1>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    VelocityGradientFieldEvaluator( const BlockDataID & velocityGradientFieldID,
                                    const ConstBlockDataID & velocityFieldID,
diff --git a/src/pe_coupling/discrete_particle_methods/evaluators/VelocityTotalTimeDerivativeFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityTotalTimeDerivativeFieldEvaluator.h
index 62e70f9736d34333d272aa91288bfe4bdced4e11..2bcbba9995d66b4294e921d398f5feb15b8ecd5b 100644
--- a/src/pe_coupling/discrete_particle_methods/evaluators/VelocityTotalTimeDerivativeFieldEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/evaluators/VelocityTotalTimeDerivativeFieldEvaluator.h
@@ -46,8 +46,8 @@ using math::Vector3;
 class VelocityTotalTimeDerivativeFieldEvaluator
 {
 public:
-   typedef GhostLayerField< Vector3<real_t>, 1 >  VelocityField_T;
-   typedef GhostLayerField< Matrix3< real_t >, 1> TensorField_T;
+   using VelocityField_T = GhostLayerField<Vector3<real_t>, 1>;
+   using TensorField_T = GhostLayerField<Matrix3<real_t>, 1>;
 
    VelocityTotalTimeDerivativeFieldEvaluator( const BlockDataID & totalTimeDerivativeVelocityFieldID,
                                               const ConstBlockDataID & currentVelocityFieldID,
diff --git a/src/pe_coupling/discrete_particle_methods/gns_lbm/GNSSweep.h b/src/pe_coupling/discrete_particle_methods/gns_lbm/GNSSweep.h
index 72649668d966772116e30af5f1d8ae2e8a33937d..a36e475ef06c216441d20894c6db17fd0670b22e 100644
--- a/src/pe_coupling/discrete_particle_methods/gns_lbm/GNSSweep.h
+++ b/src/pe_coupling/discrete_particle_methods/gns_lbm/GNSSweep.h
@@ -53,9 +53,9 @@ class GNSSweep
 {
 public:
 
-   typedef typename lbm::SweepBase< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T >::PdfField_T  PdfField_T;
-   typedef typename LatticeModel_T::Stencil Stencil_T;
-   typedef GhostLayerField< real_t, 1> ScalarField_T;
+   using PdfField_T = typename lbm::SweepBase<LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T>::PdfField_T;
+   using Stencil_T = typename LatticeModel_T::Stencil;
+   using ScalarField_T = GhostLayerField<real_t, 1>;
 
    static_assert( LatticeModel_T::ForceModel::constant == false, "Only works with non-constant force models!" );
    static_assert( LatticeModel_T::compressible == false,         "Only works with incompressible models!" );
@@ -121,7 +121,7 @@ void GNSSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut
 {
    PdfField_T * src( NULL );
    PdfField_T * dst( NULL );
-   ScalarField_T * solidVolumeFractionField( NULL );
+   ScalarField_T * solidVolumeFractionField( nullptr );
 
    getFields( block, src, dst, solidVolumeFractionField );
 
@@ -187,8 +187,8 @@ template< typename LatticeModel_T, typename Filter_T, typename DensityVelocityIn
 void GNSSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T
 >::stream( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
    lbm::SweepBase<LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T>::getFields( block, src, dst );
    lbm::StreamPull< LatticeModel_T >::execute( src, dst, block, this->filter_, numberOfGhostLayersToInclude );
 }
@@ -257,7 +257,7 @@ shared_ptr< GNSSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelo
 makeGNSSweep( const BlockDataID & pdfFieldID, const BlockDataID & solidVolumeFractionFieldID, const Filter_T & filter,
                const DensityVelocityIn_T & densityVelocityIn, const DensityVelocityOut_T & densityVelocityOut )
 {
-   typedef GNSSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T > Sweep_T;
+   using Sweep_T = GNSSweep<LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T>;
    return shared_ptr< Sweep_T >( new Sweep_T( pdfFieldID, solidVolumeFractionFieldID, filter, densityVelocityIn, densityVelocityOut ) );
 }
 
@@ -266,7 +266,7 @@ shared_ptr< GNSSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelo
 makeGNSSweep( const BlockDataID & srcID, const BlockDataID & dstID, const BlockDataID & solidVolumeFractionFieldID, const Filter_T & filter,
                const DensityVelocityIn_T & densityVelocityIn, const DensityVelocityOut_T & densityVelocityOut )
 {
-   typedef GNSSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T > Sweep_T;
+   using Sweep_T = GNSSweep<LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T>;
    return shared_ptr< Sweep_T >( new Sweep_T( srcID, dstID, solidVolumeFractionFieldID, filter, densityVelocityIn, densityVelocityOut ) );
 }
 
diff --git a/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSExternalForceToForceFieldAdder.h b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSExternalForceToForceFieldAdder.h
index 73fc6f5474635b7307cecbf184988a8e31acc236..9517c8b609770496abdf15ab8c0ca44913ffcd66 100644
--- a/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSExternalForceToForceFieldAdder.h
+++ b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSExternalForceToForceFieldAdder.h
@@ -41,8 +41,8 @@ using math::Vector3;
 class GNSExternalForceToForceFieldAdder
 {
 public:
-   typedef GhostLayerField< Vector3<real_t>, 1 >  ForceField_T;
-   typedef GhostLayerField< real_t, 1 >  ScalarField_T;
+   using ForceField_T = GhostLayerField<Vector3<real_t>, 1>;
+   using ScalarField_T = GhostLayerField<real_t, 1>;
 
    GNSExternalForceToForceFieldAdder( const BlockDataID & forceFieldID, const ConstBlockDataID & solidVolumeFractionFieldID, const Vector3<real_t> & externalForce )
       : forceFieldID_( forceFieldID ), solidVolumeFractionFieldID_( solidVolumeFractionFieldID ), externalForce_( externalForce )
diff --git a/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSPressureFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSPressureFieldEvaluator.h
index c284db5fd20767aedd66df9b3bad60a73e8fd7bf..8ff05a9537d79a9b4af2f2524b0b781d8ef0477b 100644
--- a/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSPressureFieldEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSPressureFieldEvaluator.h
@@ -36,8 +36,8 @@ template <typename LatticeModel_T, typename BoundaryHandling_T>
 class GNSPressureFieldEvaluator
 {
 public:
-   typedef lbm::PdfField< LatticeModel_T >        PdfField_T;
-   typedef GhostLayerField< real_t, 1 >           ScalarField_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
+   using ScalarField_T = GhostLayerField<real_t, 1>;
 
    GNSPressureFieldEvaluator( const BlockDataID & pressureFieldID, const ConstBlockDataID & pdfFieldID,
                               const ConstBlockDataID & solidVolumeFractionFieldID, const ConstBlockDataID & boundaryHandlingID )
diff --git a/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSSmagorinskyLESField.h b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSSmagorinskyLESField.h
index 4ae1a67dfa9d6b6cb5ccbc048da7907e3262893a..f4d8fd414f9df5a18725ee24acdcc419e18f7f50 100644
--- a/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSSmagorinskyLESField.h
+++ b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSSmagorinskyLESField.h
@@ -64,9 +64,9 @@ class GNSSmagorinskyLESField
 {
 public:
 
-   typedef lbm::PdfField< LatticeModel_T >   PdfField_T;
-   typedef GhostLayerField< real_t, 1 >      ScalarField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
+   using ScalarField_T = GhostLayerField<real_t, 1>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
 
    static_assert( LatticeModel_T::CollisionModel::constant == false, "Only works with non-constant relaxation time fields!" );
    static_assert( LatticeModel_T::compressible == false,             "Only works with incompressible models!" );
diff --git a/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSVelocityFieldEvaluator.h b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSVelocityFieldEvaluator.h
index 157c812b7788617cfe659c72a4ba966aa3083dfa..d0fcdf110989add176173580f8f365d970d0750e 100644
--- a/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSVelocityFieldEvaluator.h
+++ b/src/pe_coupling/discrete_particle_methods/gns_lbm/utility/GNSVelocityFieldEvaluator.h
@@ -48,9 +48,9 @@ template <typename LatticeModel_T, typename BoundaryHandling_T>
 class GNSVelocityFieldEvaluator
 {
 public:
-   typedef lbm::PdfField< LatticeModel_T >        PdfField_T;
-   typedef GhostLayerField< Vector3<real_t>, 1 >  VelocityField_T;
-   typedef GhostLayerField< real_t, 1 >           ScalarField_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
+   using VelocityField_T = GhostLayerField<Vector3<real_t>, 1>;
+   using ScalarField_T = GhostLayerField<real_t, 1>;
 
    GNSVelocityFieldEvaluator( const BlockDataID & velocityFieldID, const ConstBlockDataID & pdfFieldID, const ConstBlockDataID & solidVolumeFractionFieldID,
                               const ConstBlockDataID & boundaryHandlingID )
diff --git a/src/pe_coupling/discrete_particle_methods/utility/AveragedInteractionForceFieldToForceFieldAdder.h b/src/pe_coupling/discrete_particle_methods/utility/AveragedInteractionForceFieldToForceFieldAdder.h
index bffee15731a24711405afab296683ba4d5ed29c5..9d8d5a2081ac012db519d293beb16e20fcbd2f87 100644
--- a/src/pe_coupling/discrete_particle_methods/utility/AveragedInteractionForceFieldToForceFieldAdder.h
+++ b/src/pe_coupling/discrete_particle_methods/utility/AveragedInteractionForceFieldToForceFieldAdder.h
@@ -48,7 +48,7 @@ using math::Vector3;
 class AveragedInteractionForceFieldToForceFieldAdder
 {
 public:
-   typedef GhostLayerField< Vector3<real_t>, 1 >  ForceField_T;
+   using ForceField_T = GhostLayerField<Vector3<real_t>, 1>;
 
    AveragedInteractionForceFieldToForceFieldAdder( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & forceFieldID,
                                                    const ConstBlockDataID & interactionForceFieldID, uint_t maximumAveragingSteps )
diff --git a/src/pe_coupling/discrete_particle_methods/utility/BodyVelocityInitializer.h b/src/pe_coupling/discrete_particle_methods/utility/BodyVelocityInitializer.h
index 9ffbad829435d7797e2727455005d769d752344c..575f4cec859840fc248138e4d203b3b5a2a9e437 100644
--- a/src/pe_coupling/discrete_particle_methods/utility/BodyVelocityInitializer.h
+++ b/src/pe_coupling/discrete_particle_methods/utility/BodyVelocityInitializer.h
@@ -60,8 +60,8 @@ class BodyVelocityInitializer
 
 public:
 
-   typedef GhostLayerField< Vector3<real_t>, 1>          Vec3Field_T;
-   typedef FieldInterpolator_T<Vec3Field_T, FlagField_T> Vec3FieldInterpolator_T;
+   using Vec3Field_T = GhostLayerField<Vector3<real_t>, 1>;
+   using Vec3FieldInterpolator_T = FieldInterpolator_T<Vec3Field_T, FlagField_T>;
 
    BodyVelocityInitializer( const shared_ptr<StructuredBlockStorage> & blockStorage,
                             const BlockDataID & bodyStorageID, const BlockDataID & flagFieldID, const Set< FlagUID > & domainFlags,
@@ -84,8 +84,6 @@ public:
          {
             if(!dpmBodySelectorFct_(bodyIt.getBodyID())) continue;
 
-            Vector3<real_t> forceOnFluid( real_t(0) );
-
             Vector3<real_t> bodyPosition = bodyIt->getPosition();
 
             // interpolate fluid velocity to body position
diff --git a/src/pe_coupling/discrete_particle_methods/utility/ExternalForceToForceFieldAdder.h b/src/pe_coupling/discrete_particle_methods/utility/ExternalForceToForceFieldAdder.h
index f5e27bc315afe6fd349eb52c1438772b4c7e496b..b833cae930cdee1b120b3ac40e95daa5b397e10d 100644
--- a/src/pe_coupling/discrete_particle_methods/utility/ExternalForceToForceFieldAdder.h
+++ b/src/pe_coupling/discrete_particle_methods/utility/ExternalForceToForceFieldAdder.h
@@ -39,7 +39,7 @@ using math::Vector3;
 class ExternalForceToForceFieldAdder
 {
 public:
-   typedef GhostLayerField< Vector3<real_t>, 1 >  ForceField_T;
+   using ForceField_T = GhostLayerField<Vector3<real_t>, 1>;
 
    ExternalForceToForceFieldAdder( const BlockDataID & forceFieldID, const Vector3<real_t> & externalForce )
       : forceFieldID_( forceFieldID ), externalForce_( externalForce )
diff --git a/src/pe_coupling/discrete_particle_methods/utility/ForceFieldResetter.h b/src/pe_coupling/discrete_particle_methods/utility/ForceFieldResetter.h
index d58e30d51e2abe08298ba3b688b4cc909c124a28..17278887e1d18dd546ece9ba27511971f240e728 100644
--- a/src/pe_coupling/discrete_particle_methods/utility/ForceFieldResetter.h
+++ b/src/pe_coupling/discrete_particle_methods/utility/ForceFieldResetter.h
@@ -36,7 +36,7 @@ using math::Vector3;
 class ForceFieldResetter
 {
 public:
-   typedef GhostLayerField< Vector3<real_t>, 1 >  ForceField_T;
+   using ForceField_T = GhostLayerField<Vector3<real_t>, 1>;
 
    ForceFieldResetter( const BlockDataID & forceFieldID )
       : forceFieldID_( forceFieldID )
diff --git a/src/pe_coupling/momentum_exchange_method/BodyMapping.h b/src/pe_coupling/momentum_exchange_method/BodyMapping.h
index 96cfd127602147cd22489767e35d0d9ea0b0b7b2..c3addc45724c71c6c1cc052dcefb62d7e83020f2 100644
--- a/src/pe_coupling/momentum_exchange_method/BodyMapping.h
+++ b/src/pe_coupling/momentum_exchange_method/BodyMapping.h
@@ -61,10 +61,10 @@ class BodyMapping
 {
 public:
 
-   typedef lbm::PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename BoundaryHandling_T::FlagField FlagField_T;
-   typedef typename BoundaryHandling_T::flag_t    flag_t;
-   typedef Field< pe::BodyID, 1 >                 BodyField_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
+   using FlagField_T = typename BoundaryHandling_T::FlagField;
+   using flag_t = typename BoundaryHandling_T::flag_t;
+   using BodyField_T = Field<pe::BodyID, 1>;
 
    BodyMapping( const shared_ptr<StructuredBlockStorage> & blockStorage,
                 const BlockDataID & pdfFieldID,
@@ -250,7 +250,7 @@ template< typename BoundaryHandling_T >
 void mapMovingBody( pe::BodyID body, IBlock & block, StructuredBlockStorage & blockStorage,
                     const BlockDataID & boundaryHandlingID, const BlockDataID & bodyFieldID, const FlagUID & obstacle )
 {
-   typedef Field< pe::BodyID, 1 > BodyField_T;
+   using BodyField_T = Field<pe::BodyID, 1>;
 
    WALBERLA_ASSERT_EQUAL( &block.getBlockStorage(), &(blockStorage.getBlockStorage()) );
 
diff --git a/src/pe_coupling/momentum_exchange_method/boundary/CurvedLinear.h b/src/pe_coupling/momentum_exchange_method/boundary/CurvedLinear.h
index 05fee404db1b0cb47a1d1a28216ee7de481dca1d..41dcdf79a3b14df14a9aa2e1b92a4a0ed342f4ba 100644
--- a/src/pe_coupling/momentum_exchange_method/boundary/CurvedLinear.h
+++ b/src/pe_coupling/momentum_exchange_method/boundary/CurvedLinear.h
@@ -64,10 +64,10 @@ namespace pe_coupling {
 template< typename LatticeModel_T, typename FlagField_T >
 class CurvedLinear : public Boundary< typename FlagField_T::flag_t >
 {
-   typedef lbm::PdfField< LatticeModel_T >   PDFField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil_T;
-   typedef typename FlagField_T::flag_t      flag_t;
-   typedef Field< pe::BodyID, 1 >            BodyField_T;
+   using PDFField_T = lbm::PdfField<LatticeModel_T>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
+   using flag_t = typename FlagField_T::flag_t;
+   using BodyField_T = Field<pe::BodyID, 1>;
 
 public:
 
diff --git a/src/pe_coupling/momentum_exchange_method/boundary/CurvedQuadratic.h b/src/pe_coupling/momentum_exchange_method/boundary/CurvedQuadratic.h
index 3ba15e91923e1a2034d3e21ae8c148aee47e8f64..4e5e4f9b4ddd8bb4fe2f6b72342a7383153ed506 100644
--- a/src/pe_coupling/momentum_exchange_method/boundary/CurvedQuadratic.h
+++ b/src/pe_coupling/momentum_exchange_method/boundary/CurvedQuadratic.h
@@ -75,10 +75,10 @@ class CurvedQuadratic : public Boundary< typename FlagField_T::flag_t >
 {
    static_assert( (std::is_same< typename LatticeModel_T::CollisionModel::tag, lbm::collision_model::TRT_tag >::value), "Only works with TRT!" ); // to access lambda_d
 
-   typedef lbm::PdfField< LatticeModel_T >   PDFField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil_T;
-   typedef typename FlagField_T::flag_t      flag_t;
-   typedef Field< pe::BodyID, 1 >            BodyField_T;
+   using PDFField_T = lbm::PdfField<LatticeModel_T>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
+   using flag_t = typename FlagField_T::flag_t;
+   using BodyField_T = Field<pe::BodyID, 1>;
 
 public:
 
diff --git a/src/pe_coupling/momentum_exchange_method/boundary/SimpleBB.h b/src/pe_coupling/momentum_exchange_method/boundary/SimpleBB.h
index 0d7af9d9ae3f9817147bbfdf4ed294c8f85ac848..d3ee85dec238d013089bbaabe865a8e0555c2450 100644
--- a/src/pe_coupling/momentum_exchange_method/boundary/SimpleBB.h
+++ b/src/pe_coupling/momentum_exchange_method/boundary/SimpleBB.h
@@ -59,11 +59,11 @@ namespace pe_coupling {
 template< typename LatticeModel_T, typename FlagField_T >
 class SimpleBB : public Boundary< typename FlagField_T::flag_t >
 {
-   typedef lbm::PdfField< LatticeModel_T >   PDFField_T;
-   typedef typename LatticeModel_T::Stencil  Stencil_T;
-   typedef typename FlagField_T::flag_t      flag_t;
+   using PDFField_T = lbm::PdfField<LatticeModel_T>;
+   using Stencil_T = typename LatticeModel_T::Stencil;
+   using flag_t = typename FlagField_T::flag_t;
 
-   typedef Field< pe::BodyID, 1 > BodyField;
+   using BodyField = Field<pe::BodyID, 1>;
 
 public:
 
diff --git a/src/pe_coupling/momentum_exchange_method/destruction/Destroyer.h b/src/pe_coupling/momentum_exchange_method/destruction/Destroyer.h
index 68d9ae2a3a3c6fdb3a903181c5243e4ff24cd9aa..f1f5a366c3d480079757a1a234f71b2f75f02bdf 100644
--- a/src/pe_coupling/momentum_exchange_method/destruction/Destroyer.h
+++ b/src/pe_coupling/momentum_exchange_method/destruction/Destroyer.h
@@ -33,7 +33,7 @@ class NaNDestroyer
 {
 public:
 
-   typedef lbm::PdfField< LatticeModel_T > PdfField_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
 
    void operator()( const cell_idx_t & x, const cell_idx_t & y, const cell_idx_t & z, IBlock * const /*block*/, PdfField_T * const pdfField ) {
       for (auto d = uint_t(0); d < LatticeModel_T::Stencil::Size; ++d)
diff --git a/src/pe_coupling/momentum_exchange_method/restoration/ExtrapolationDirectionFinder.h b/src/pe_coupling/momentum_exchange_method/restoration/ExtrapolationDirectionFinder.h
index 7e9968d83b16e7070931e6be79f7e79a92efba53..1cdab1b9c50d52d2be5a2ccdacfce395e170d073 100644
--- a/src/pe_coupling/momentum_exchange_method/restoration/ExtrapolationDirectionFinder.h
+++ b/src/pe_coupling/momentum_exchange_method/restoration/ExtrapolationDirectionFinder.h
@@ -118,7 +118,7 @@ class SphereNormalExtrapolationDirectionFinder
 {
 public:
 
-   typedef Field< pe::BodyID, 1 > BodyField_T;
+   using BodyField_T = Field<pe::BodyID, 1>;
 
    SphereNormalExtrapolationDirectionFinder( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & bodyFieldID )
    : blockStorage_( blockStorage ), bodyFieldID_( bodyFieldID )
diff --git a/src/pe_coupling/momentum_exchange_method/restoration/PDFReconstruction.h b/src/pe_coupling/momentum_exchange_method/restoration/PDFReconstruction.h
index 0d6dcfe12768327003ee06bbb17a9b4fba69278f..6de45fbca37be5c7315311469438c4df5b65c9e9 100644
--- a/src/pe_coupling/momentum_exchange_method/restoration/PDFReconstruction.h
+++ b/src/pe_coupling/momentum_exchange_method/restoration/PDFReconstruction.h
@@ -60,10 +60,10 @@ class PDFReconstruction
 {
 public:
 
-   typedef lbm::PdfField< LatticeModel_T >        PdfField_T;
-   typedef typename BoundaryHandling_T::FlagField FlagField_T;
-   typedef typename BoundaryHandling_T::flag_t    flag_t;
-   typedef Field< pe::BodyID, 1 >                 BodyField_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
+   using FlagField_T = typename BoundaryHandling_T::FlagField;
+   using flag_t = typename BoundaryHandling_T::flag_t;
+   using BodyField_T = Field<pe::BodyID, 1>;
 
    inline PDFReconstruction( const shared_ptr<StructuredBlockStorage> & blockStorage,
                              const BlockDataID & pdfFieldID,
diff --git a/src/pe_coupling/momentum_exchange_method/restoration/Reconstructor.h b/src/pe_coupling/momentum_exchange_method/restoration/Reconstructor.h
index ad2b8adfac23e84cda40401867b158d31814d50c..c8ebb191f06d8ab16e3173a8d4e9591717add806 100644
--- a/src/pe_coupling/momentum_exchange_method/restoration/Reconstructor.h
+++ b/src/pe_coupling/momentum_exchange_method/restoration/Reconstructor.h
@@ -73,8 +73,8 @@ class EquilibriumReconstructor
 {
 public:
 
-   typedef lbm::PdfField< LatticeModel_T > PdfField_T;
-   typedef Field< pe::BodyID, 1 >          BodyField_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
+   using BodyField_T = Field<pe::BodyID, 1>;
 
    EquilibriumReconstructor( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & boundaryHandlingID,
                              const BlockDataID & bodyFieldID )
@@ -168,8 +168,8 @@ class EquilibriumAndNonEquilibriumReconstructor
 {
 public:
 
-   typedef lbm::PdfField< LatticeModel_T > PdfField_T;
-   typedef Field< pe::BodyID, 1 >          BodyField_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
+   using BodyField_T = Field<pe::BodyID, 1>;
 
    EquilibriumAndNonEquilibriumReconstructor( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & boundaryHandlingID,
                                               const BlockDataID & bodyFieldID,
@@ -259,8 +259,8 @@ class ExtrapolationReconstructor
 {
 public:
 
-   typedef lbm::PdfField< LatticeModel_T > PdfField_T;
-   typedef Field< pe::BodyID, 1 >          BodyField_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
+   using BodyField_T = Field<pe::BodyID, 1>;
 
    ExtrapolationReconstructor( const shared_ptr<StructuredBlockStorage> & blockStorage, const BlockDataID & boundaryHandlingID,
                                const BlockDataID & bodyFieldID,
diff --git a/src/pe_coupling/partially_saturated_cells_method/BodyAndVolumeFractionMapping.cpp b/src/pe_coupling/partially_saturated_cells_method/BodyAndVolumeFractionMapping.cpp
index d0d36a921672c5cc6793d4e1bc6739dc08b444e4..9d6511d4d69502d07115d5c15cf673cc1ccf624b 100644
--- a/src/pe_coupling/partially_saturated_cells_method/BodyAndVolumeFractionMapping.cpp
+++ b/src/pe_coupling/partially_saturated_cells_method/BodyAndVolumeFractionMapping.cpp
@@ -35,8 +35,8 @@ namespace pe_coupling {
 void mapPSMBodyAndVolumeFraction( const pe::BodyID body, IBlock & block, StructuredBlockStorage & blockStorage,
                                   const BlockDataID bodyAndVolumeFractionFieldID )
 {
-   typedef std::pair< pe::BodyID, real_t >                              BodyAndVolumeFraction_T;
-   typedef GhostLayerField< std::vector< BodyAndVolumeFraction_T >, 1 > BodyAndVolumeFractionField_T;
+   using BodyAndVolumeFraction_T = std::pair<pe::BodyID, real_t>;
+   using BodyAndVolumeFractionField_T = GhostLayerField<std::vector<BodyAndVolumeFraction_T>, 1>;
 
    BodyAndVolumeFractionField_T * bodyAndVolumeFractionField = block.getData< BodyAndVolumeFractionField_T >( bodyAndVolumeFractionFieldID );
    WALBERLA_ASSERT_NOT_NULLPTR( bodyAndVolumeFractionField );
diff --git a/src/pe_coupling/partially_saturated_cells_method/BodyAndVolumeFractionMapping.h b/src/pe_coupling/partially_saturated_cells_method/BodyAndVolumeFractionMapping.h
index 59fb85590fd9db424523e3f2c6cf6db0d9d84bef..56faedf94759b7aa528c27ee4d2685fa41dfccd4 100644
--- a/src/pe_coupling/partially_saturated_cells_method/BodyAndVolumeFractionMapping.h
+++ b/src/pe_coupling/partially_saturated_cells_method/BodyAndVolumeFractionMapping.h
@@ -66,8 +66,8 @@ class BodyAndVolumeFractionMapping
 {
 public:
 
-   typedef std::pair< pe::BodyID, real_t >                              BodyAndVolumeFraction_T;
-   typedef GhostLayerField< std::vector< BodyAndVolumeFraction_T >, 1 > BodyAndVolumeFractionField_T;
+   using BodyAndVolumeFraction_T = std::pair<pe::BodyID, real_t>;
+   using BodyAndVolumeFractionField_T = GhostLayerField<std::vector<BodyAndVolumeFraction_T>, 1>;
 
    BodyAndVolumeFractionMapping( const shared_ptr<StructuredBlockStorage> & blockStorage,
                                  const shared_ptr<pe::BodyStorage> & globalBodyStorage,
diff --git a/src/pe_coupling/partially_saturated_cells_method/PSMSweep.h b/src/pe_coupling/partially_saturated_cells_method/PSMSweep.h
index 78676106bfee4fde3bd3550fd129c72fe749b807..2b326f116aac44498e61010ae8a2987e8c212505 100644
--- a/src/pe_coupling/partially_saturated_cells_method/PSMSweep.h
+++ b/src/pe_coupling/partially_saturated_cells_method/PSMSweep.h
@@ -66,10 +66,10 @@ class PSMSweep
 {
 public:
 
-   typedef typename lbm::SweepBase< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T >::PdfField_T  PdfField_T;
-   typedef typename LatticeModel_T::Stencil                   Stencil_T;
-   typedef std::pair< pe::BodyID, real_t >                    BodyAndVolumeFraction_T;
-   typedef Field< std::vector< BodyAndVolumeFraction_T >, 1 > BodyAndVolumeFractionField_T;
+   using PdfField_T = typename lbm::SweepBase<LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T>::PdfField_T;
+   using Stencil_T = typename LatticeModel_T::Stencil;
+   using BodyAndVolumeFraction_T = std::pair<pe::BodyID, real_t>;
+   using BodyAndVolumeFractionField_T = Field<std::vector<BodyAndVolumeFraction_T>, 1>;
 
    PSMSweep( const BlockDataID & pdfFieldID,
              const BlockDataID & bodyAndVolumeFractionFieldID,
@@ -133,9 +133,9 @@ template< typename LatticeModel_T, typename Filter_T, typename DensityVelocityIn
 void PSMSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T, SolidCollision_T, Weighting_T
 >::streamCollide( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
-   BodyAndVolumeFractionField_T * bodyAndVolumeFractionField( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
+   BodyAndVolumeFractionField_T * bodyAndVolumeFractionField( nullptr );
 
    getFields( block, src, dst, bodyAndVolumeFractionField );
 
@@ -282,8 +282,8 @@ template< typename LatticeModel_T, typename Filter_T, typename DensityVelocityIn
 void PSMSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T, SolidCollision_T, Weighting_T
 >::stream( IBlock * const block, const uint_t numberOfGhostLayersToInclude )
 {
-   PdfField_T * src( NULL );
-   PdfField_T * dst( NULL );
+   PdfField_T * src( nullptr );
+   PdfField_T * dst( nullptr );
    lbm::SweepBase<LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T>::getFields( block, src, dst );
    lbm::StreamPull< LatticeModel_T >::execute( src, dst, block, this->filter_, numberOfGhostLayersToInclude );
 }
@@ -441,7 +441,7 @@ shared_ptr< PSMSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelo
 makePSMSweep( const BlockDataID & pdfFieldID, const BlockDataID & bodyAndVolumeFractionFieldID, const shared_ptr<StructuredBlockStorage> & blockStorage,
               const Filter_T & filter, const DensityVelocityIn_T & densityVelocityIn, const DensityVelocityOut_T & densityVelocityOut )
 {
-   typedef PSMSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T, SolidCollision_T, Weighting_T > PSMS_T;
+   using PSMS_T = PSMSweep<LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T, SolidCollision_T, Weighting_T>;
    return shared_ptr< PSMS_T >( new PSMS_T( pdfFieldID, bodyAndVolumeFractionFieldID, blockStorage, filter, densityVelocityIn, densityVelocityOut ) );
 }
 
@@ -450,7 +450,7 @@ shared_ptr< PSMSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelo
 makePSMSweep( const BlockDataID & srcID, const BlockDataID & dstID, const BlockDataID & bodyAndVolumeFractionFieldID, const shared_ptr<StructuredBlockStorage> & blockStorage,
               const Filter_T & filter, const DensityVelocityIn_T & densityVelocityIn, const DensityVelocityOut_T & densityVelocityOut )
 {
-   typedef PSMSweep< LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T, SolidCollision_T, Weighting_T > PSMS_T;
+   using PSMS_T = PSMSweep<LatticeModel_T, Filter_T, DensityVelocityIn_T, DensityVelocityOut_T, SolidCollision_T, Weighting_T>;
    return shared_ptr< PSMS_T >( new PSMS_T( srcID, dstID, bodyAndVolumeFractionFieldID, blockStorage, filter, densityVelocityIn, densityVelocityOut ) );
 }
 
diff --git a/src/pe_coupling/partially_saturated_cells_method/PSMUtility.h b/src/pe_coupling/partially_saturated_cells_method/PSMUtility.h
index e39c3b7b227a252f5e45b96e23d591eb717ad2aa..b78a4e45f94dcbe31c2741ebadc55e27ebcbb4c6 100644
--- a/src/pe_coupling/partially_saturated_cells_method/PSMUtility.h
+++ b/src/pe_coupling/partially_saturated_cells_method/PSMUtility.h
@@ -92,9 +92,9 @@ template < typename LatticeModel_T, int Weighting_T >
 void initializeDomainForPSM( StructuredBlockStorage & blockStorage,
                              const BlockDataID & pdfFieldID, const BlockDataID & bodyAndVolumeFractionFieldID )
 {
-   typedef lbm::PdfField< LatticeModel_T >                              PdfField_T;
-   typedef std::pair< pe::BodyID, real_t >                              BodyAndVolumeFraction_T;
-   typedef GhostLayerField< std::vector< BodyAndVolumeFraction_T >, 1 > BodyAndVolumeFractionField_T;
+   using PdfField_T = lbm::PdfField<LatticeModel_T>;
+   using BodyAndVolumeFraction_T = std::pair<pe::BodyID, real_t>;
+   using BodyAndVolumeFractionField_T = GhostLayerField<std::vector<BodyAndVolumeFraction_T>, 1>;
 
    // iterate all blocks with an iterator 'block'
    for( auto blockIt = blockStorage.begin(); blockIt != blockStorage.end(); ++blockIt )
diff --git a/src/pe_coupling/utility/BodiesForceTorqueContainer.h b/src/pe_coupling/utility/BodiesForceTorqueContainer.h
index b18d4ec0dd655a6c85648e4676a677850a5249ee..72f6dcc2c9a1bf5298110b8983d0407f9c3f5ba6 100644
--- a/src/pe_coupling/utility/BodiesForceTorqueContainer.h
+++ b/src/pe_coupling/utility/BodiesForceTorqueContainer.h
@@ -34,7 +34,7 @@ class BodiesForceTorqueContainer
 {  
 public:
 
-   typedef std::map< walberla::id_t, std::array<real_t,6> > ForceTorqueStorage_T;
+   using ForceTorqueStorage_T = std::map<walberla::id_t, std::array<real_t, 6>>;
 
    BodiesForceTorqueContainer( const shared_ptr<StructuredBlockForest> & blockForest, const BlockDataID & bodyStorageID, const std::function<bool(
             pe::BodyID)> &bodySelectorFct = selectRegularBodies)
diff --git a/src/postprocessing/MarchingCubes.impl.h b/src/postprocessing/MarchingCubes.impl.h
index 6f4538742a7368703e0a186a70df95b344b1c50d..29f22544f82f2a01eb6196aad0b1c215405b85ff 100644
--- a/src/postprocessing/MarchingCubes.impl.h
+++ b/src/postprocessing/MarchingCubes.impl.h
@@ -378,7 +378,7 @@ void generateIsoSurface_internal( const Field_T & f, real_t threshold,
 
 
 
-   typedef Vector3<real_t> RealVec3;
+   using RealVec3 = Vector3<real_t>;
 
    // the field store for every edge the index of the vertex that was computed on it
    // or -1 for "not yet computed"
diff --git a/src/postprocessing/python/Exports.h b/src/postprocessing/python/Exports.h
deleted file mode 100644
index 17cd6b07a079c50724e4eafc2578b96471865d2e..0000000000000000000000000000000000000000
--- a/src/postprocessing/python/Exports.h
+++ /dev/null
@@ -1,41 +0,0 @@
-//======================================================================================================================
-//
-//  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 Exports.h
-//! \ingroup postprocessing
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include "waLBerlaDefinitions.h"
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-namespace walberla {
-namespace postprocessing {
-
-
-   template<typename RealFields, typename FlagFields>
-   void exportModuleToPython();
-
-
-} // namespace postprocessing
-} // namespace walberla
-
-
-#include "Exports.impl.h"
-
-#endif // WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/postprocessing/python/Exports.impl.h b/src/postprocessing/python/Exports.impl.h
deleted file mode 100644
index 4f67e756e3013d645aed9b456e5d80839c1f4ec6..0000000000000000000000000000000000000000
--- a/src/postprocessing/python/Exports.impl.h
+++ /dev/null
@@ -1,141 +0,0 @@
-//======================================================================================================================
-//
-//  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 Exports.impl.h
-//! \ingroup geometry
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-// Do not reorder includes - the include order is important
-#include "python_coupling/PythonWrapper.h"
-#include "python_coupling/helper/ModuleScope.h"
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-#include "python_coupling/Manager.h"
-#include "python_coupling/helper/MplHelpers.h"
-#include "python_coupling/helper/BlockStorageExportHelpers.h"
-#include "python_coupling/helper/PythonIterableToStdVector.h"
-
-
-#include "postprocessing/FieldToSurfaceMesh.h"
-
-using namespace boost::python;
-
-
-namespace walberla {
-namespace postprocessing {
-
-FunctionExporterClass( realFieldToSurfaceMesh,
-                       shared_ptr<geometry::TriangleMesh>( const shared_ptr<StructuredBlockStorage> &,
-                                                           ConstBlockDataID, real_t, uint_t, bool, int, MPI_Comm  ) );
-
-template<typename FieldVector>
-static shared_ptr<geometry::TriangleMesh> exp_realFieldToSurfaceMesh
-      ( const shared_ptr<StructuredBlockStorage> & bs, const std::string & blockDataStr, real_t threshold,
-        uint_t fCoord = 0, bool calcNormals = false, int targetRank = 0 )
-{
-   if ( bs->begin() == bs->end() )
-      return shared_ptr<geometry::TriangleMesh>();
-   IBlock * firstBlock =  & ( * bs->begin() );
-
-   auto fieldID = python_coupling::blockDataIDFromString( *bs, blockDataStr );
-
-   python_coupling::Dispatcher<FieldVector, Exporter_realFieldToSurfaceMesh > dispatcher( firstBlock );
-   return dispatcher( fieldID )( bs, fieldID, threshold, fCoord, calcNormals, targetRank, MPI_COMM_WORLD) ;
-}
-
-
-template< typename FField>
-typename FField::value_type maskFromFlagList(  const shared_ptr<StructuredBlockStorage> & bs,
-                                               ConstBlockDataID flagFieldID,
-                                               const std::vector< std::string > & flagList )
-{
-   if ( bs->begin() == bs->end() )
-      return 0;
-
-   IBlock & firstBlock = *(  bs->begin() );
-   const FField * flagField = firstBlock.getData< const FField > ( flagFieldID );
-
-   typedef typename FField::flag_t flag_t;
-   flag_t mask = 0;
-   for( auto it = flagList.begin(); it != flagList.end(); ++it )
-   {
-      if ( ! flagField->flagExists( *it ) )
-         throw python_coupling::BlockDataNotConvertible( "Unknown FlagID" );
-
-      mask = flag_t( mask | flagField->getFlag( *it ) );
-   }
-
-   return mask;
-}
-
-
-template<typename FlagField_T>
-boost::python::object adaptedFlagFieldToSurfaceMesh( const shared_ptr<StructuredBlockStorage> & bs,
-                                                     ConstBlockDataID fieldID, const std::vector< std::string > & flagList,
-                                                     bool calcNormals = false, int targetRank = 0 )
-{
-   using namespace boost::python;
-
-   auto mask = maskFromFlagList<FlagField_T>( bs, fieldID, flagList );
-   return object( flagFieldToSurfaceMesh<FlagField_T>(bs, fieldID, mask, calcNormals, targetRank ) );
-}
-
-
-FunctionExporterClass( adaptedFlagFieldToSurfaceMesh,
-                       boost::python::object( const shared_ptr<StructuredBlockStorage> &, ConstBlockDataID,
-                                              const std::vector< std::string > &, bool,int  ) );
-
-
-template<typename FieldVector>
-static boost::python::object exp_flagFieldToSurfaceMesh ( const shared_ptr<StructuredBlockStorage> & bs,
-                                                          const std::string & blockDataName,
-                                                          const boost::python::list & flagList,
-                                                          bool calcNormals = false, int targetRank = 0 )
-{
-   if ( bs->begin() == bs->end() )
-      return boost::python::object(); //TODO check if this is correct
-
-   IBlock * firstBlock =  & ( * bs->begin() );
-
-   auto fieldID = python_coupling::blockDataIDFromString( *bs, blockDataName );
-
-   auto flagVector = python_coupling::pythonIterableToStdVector<std::string>( flagList );
-   python_coupling::Dispatcher<FieldVector, Exporter_adaptedFlagFieldToSurfaceMesh > dispatcher( firstBlock );
-   return dispatcher( fieldID )( bs, fieldID, flagVector, calcNormals, targetRank );
-}
-
-
-
-template<typename RealFields, typename FlagFields>
-void exportModuleToPython()
-{
-   python_coupling::ModuleScope fieldModule( "postprocessing" );
-
-   def( "realFieldToMesh", &exp_realFieldToSurfaceMesh<RealFields>,
-            ( arg("blocks"), arg("blockDataName"),  arg("fCoord")=0, arg("calcNormals") = false, arg("targetRank")=0 ) );
-
-   def( "flagFieldToMesh", &exp_flagFieldToSurfaceMesh<FlagFields>,
-            ( arg("blocks"), arg("blockDataName"), arg("flagList"), arg("calcNormals") = false, arg("targetRank")=0  ) );
-}
-
-
-
-} // namespace postprocessing
-} // namespace walberla
-
-
-#endif //WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/python_coupling/CMakeLists.txt b/src/python_coupling/CMakeLists.txt
index 2b303c171aea8edf4dfde775165beb0ac53ea87b..882a6d559986692861fe38fd58c9f463b32a82c0 100644
--- a/src/python_coupling/CMakeLists.txt
+++ b/src/python_coupling/CMakeLists.txt
@@ -1,3 +1,5 @@
-waLBerla_add_module( DEPENDS core communication domain_decomposition stencil )
-
- 
\ No newline at end of file
+if (WALBERLA_BUILD_WITH_PYTHON)
+   waLBerla_add_module(DEPENDS core communication domain_decomposition stencil field blockforest vtk cuda)
+   target_link_libraries(python_coupling PUBLIC pybind11::embed core domain_decomposition)
+   set_target_properties(python_coupling PROPERTIES CXX_VISIBILITY_PRESET hidden)
+endif()
\ No newline at end of file
diff --git a/src/python_coupling/CreateConfig.cpp b/src/python_coupling/CreateConfig.cpp
index 0a4416c3306b05d4268f01aaeba4ffa3568e033d..5f8f9615c2f346096b567826974d20904ad2b9af 100644
--- a/src/python_coupling/CreateConfig.cpp
+++ b/src/python_coupling/CreateConfig.cpp
@@ -16,248 +16,201 @@
 //! \file CreateConfigFromPythonScript.cpp
 //! \ingroup python
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
 #include "CreateConfig.h"
 
-
+#include "core/StringUtility.h"
 #include "core/config/Config.h"
 #include "core/config/Create.h"
 #include "core/logging/Logging.h"
-#include "core/StringUtility.h"
-
-#include <exception>
-
 
 #ifdef WALBERLA_BUILD_WITH_PYTHON
+#   include "python_coupling/helper/ConfigFromDict.h"
 
-#include "PythonCallback.h"
-#include "PythonWrapper.h"
-#include "DictWrapper.h"
+#   include "PythonCallback.h"
+#   include <pybind11/pybind11.h>
 
-#include "python_coupling/helper/ConfigFromDict.h"
-#include "helper/ExceptionHandling.h"
+namespace walberla
+{
+namespace python_coupling
+{
+namespace py = pybind11;
 
+shared_ptr< Config > createConfigFromPythonScript(const std::string& scriptFile,
+                                                         const std::string& pythonFunctionName,
+                                                         const std::vector< std::string >& argv)
+{
+   importModuleOrFile(scriptFile, argv);
 
-namespace walberla {
-namespace python_coupling {
+   PythonCallback pythonCallback(pythonFunctionName);
 
-   namespace bp = boost::python;
+   pythonCallback();
 
+   using py::dict;
+   using py::object;
 
-   shared_ptr<Config> createConfigFromPythonScript( const std::string & scriptFile,
-                                                    const std::string & pythonFunctionName,
-                                                    const std::vector<std::string> & argv )
-   {
-      importModuleOrFile( scriptFile, argv );
-
-      PythonCallback pythonCallback ( pythonFunctionName );
-
-      pythonCallback();
+   object returnValue = pythonCallback.data().dict()["returnValue"];
+   if (returnValue.is(object())) return shared_ptr< Config >();
 
-      using boost::python::object;
-      using boost::python::dict;
-      using boost::python::extract;
-
-      object returnValue = pythonCallback.data().dict()[ "returnValue" ];
-      if ( returnValue == object() )
-         return shared_ptr<Config>();
+   bool isDict = py::isinstance< dict >(returnValue);
+   if (!isDict) { WALBERLA_ABORT("Python configuration did not return a dictionary object."); }
+   dict returnDict = dict(returnValue);
+   return configFromPythonDict(returnDict);
+}
 
-      bool isDict = extract< dict >( returnValue ).check();
-      if ( ! isDict ) {
-         WALBERLA_ABORT("Python configuration did not return a dictionary object.");
-      }
-      dict returnDict = extract<dict>( returnValue );
-      return configFromPythonDict( returnDict );
-   }
-
-
-   //===================================================================================================================
-   //
-   //  Config Generators and iterators
-   //
-   //===================================================================================================================
+//===================================================================================================================
+//
+//  Config Generators and iterators
+//
+//===================================================================================================================
 
+class PythonMultipleConfigGenerator : public config::ConfigGenerator
+{
+ public:
+   PythonMultipleConfigGenerator(py::object ScenarioConfigGenerator) // NOLINT
+      : ScenarioConfigGenerator_(ScenarioConfigGenerator)            // NOLINT
+   {}
 
-   class PythonMultipleConfigGenerator : public config::ConfigGenerator
+   shared_ptr< Config > next() override
    {
-   public:
-      PythonMultipleConfigGenerator( bp::stl_input_iterator< bp::dict > iterator  )  //NOLINT
-         : iter_( iterator ), firstTime_(true) //NOLINT
-      {}
-
-      shared_ptr<Config> next() override
+      shared_ptr< Config > config = make_shared< Config >();
+      try
       {
-         // this seemingly unnecessary complicated firstTime variable is used
-         // since in alternative version where (++iter_) is at the end of the function
-         // the python generator expression for the next time is already
-         // called before the current simulation finished
-         if ( !firstTime_ )
-            ++iter_;
-         else
-            firstTime_ = false;
-
-         if ( iter_ == bp::stl_input_iterator< bp::dict >()  )
-            return shared_ptr<Config>();
-
-         shared_ptr<Config> config = make_shared<Config>();
-
-         bp::dict configDict = *iter_;
-         configFromPythonDict( config->getWritableGlobalBlock(), configDict );
-
+         py::dict configDict = ScenarioConfigGenerator_.attr("__next__")();
+         configFromPythonDict(config->getWritableGlobalBlock(), configDict);
          return config;
       }
-
-   private:
-      bp::stl_input_iterator< bp::dict > iter_;
-      bool firstTime_;
-   };
-
-
-
-   class PythonSingleConfigGenerator : public config::ConfigGenerator
-   {
-   public:
-      PythonSingleConfigGenerator( const shared_ptr<Config> & config ): config_ ( config ) {}
-
-      shared_ptr<Config> next() override
+      catch (py::error_already_set&)
       {
-         auto res = config_;
-         config_.reset();
-         return res;
+         return shared_ptr<Config>();
       }
+   }
 
-   private:
-      shared_ptr<Config> config_;
-   };
+ private:
+   py::object ScenarioConfigGenerator_;
+};
 
+class PythonSingleConfigGenerator : public config::ConfigGenerator
+{
+ public:
+   PythonSingleConfigGenerator(const shared_ptr< Config >& config) : config_(config) {}
 
-   config::Iterator createConfigIteratorFromPythonScript( const std::string & scriptFile,
-                                                          const std::string & pythonFunctionName,
-                                                          const std::vector<std::string> & argv )
+   shared_ptr< Config > next() override
    {
-      importModuleOrFile( scriptFile, argv );
-
-      PythonCallback pythonCallback ( pythonFunctionName );
+      auto res = config_;
+      config_.reset();
+      return res;
+   }
 
-      pythonCallback();
+ private:
+   shared_ptr< Config > config_;
+};
 
-      bp::object returnValue = pythonCallback.data().dict()[ "returnValue" ];
+config::Iterator createConfigIteratorFromPythonScript(const std::string& scriptFile,
+                                                             const std::string& pythonFunctionName,
+                                                             const std::vector< std::string >& argv)
+{
+   importModuleOrFile(scriptFile, argv);
+   PythonCallback pythonCallback(pythonFunctionName);
+   pythonCallback();
 
-      bool isDict = bp::extract< bp::dict >( returnValue ).check();
+   py::object returnValue = pythonCallback.data().dict()["returnValue"];
+   bool isDict            = py::isinstance< py::dict >(returnValue);
 
-      shared_ptr< config::ConfigGenerator> generator;
-      if ( isDict )
+   shared_ptr< config::ConfigGenerator > generator;
+   if (isDict)
+   {
+      auto config            = make_shared< Config >();
+      py::dict extractedDict = py::cast< py::dict >(returnValue);
+      configFromPythonDict(config->getWritableGlobalBlock(), extractedDict);
+      generator = make_shared< PythonSingleConfigGenerator >(config);
+   }
+   else
+   {
+      try
       {
-         auto config = make_shared<Config>();
-         bp::dict extractedDict = bp::extract<bp::dict> ( returnValue );
-         configFromPythonDict( config->getWritableGlobalBlock(), extractedDict );
-         generator = make_shared<PythonSingleConfigGenerator>( config );
-      }
-      else {
-
-         try {
-            generator= make_shared<PythonMultipleConfigGenerator>( returnValue );
-         }
-         catch ( bp::error_already_set & ) {
-            python_coupling::terminateOnPythonException("Error while running Python config generator");
-         }
+         generator = make_shared< PythonMultipleConfigGenerator >(returnValue);
+      } catch (py::error_already_set&)
+      {
+         std::string message = std::string("Error while running Python function ") + pythonFunctionName;
+         WALBERLA_ABORT_NO_DEBUG_INFO(message);
       }
-
-      return config::Iterator( generator );
    }
 
+   return config::Iterator(generator);
+}
 
 } // namespace python_coupling
 } // namespace walberla
 
-
 #else
 
-
-namespace walberla {
-namespace python_coupling {
-
-   shared_ptr<Config> createConfigFromPythonScript( const std::string &, const std::string &, const std::vector<std::string> &  )
-   {
-      WALBERLA_ABORT( "Tried to run with Python config but waLBerla was built without Python support." );
-      return shared_ptr<Config>();
-   }
-
-
-   config::Iterator createConfigIteratorFromPythonScript( const std::string & , const std::string &, const std::vector<std::string> &   )
-   {
-      WALBERLA_ABORT( "Tried to run with Python config but waLBerla was built without Python support." );
-      return config::Iterator();
-   }
-
+namespace walberla
+{
+namespace python_coupling
+{
+shared_ptr< Config > createConfigFromPythonScript(const std::string&, const std::string&,
+                                                  const std::vector< std::string >&)
+{
+   WALBERLA_ABORT("Tried to run with Python config but waLBerla was built without Python support.");
+   return shared_ptr< Config >();
+}
+
+config::Iterator createConfigIteratorFromPythonScript(const std::string&, const std::string&,
+                                                      const std::vector< std::string >&)
+{
+   WALBERLA_ABORT("Tried to run with Python config but waLBerla was built without Python support.");
+   return config::Iterator();
+}
 
 } // namespace python_coupling
 } // namespace walberla
 
-
 #endif
 
+namespace walberla
+{
+namespace python_coupling
+{
+shared_ptr< Config > createConfig(int argc, char** argv)
+{
+   if (argc < 2) throw std::runtime_error(config::usageString(argv[0]));
 
+   shared_ptr< Config > config;
+   std::string filename(argv[1]);
 
+   auto argVec = std::vector< std::string >(argv + 1, argv + argc);
 
-
-
-
-
-
-
-
-
-namespace walberla {
-namespace python_coupling {
-
-
-
-   shared_ptr<Config> createConfig( int argc, char ** argv )
+   if (string_ends_with(filename, ".py")) { config = createConfigFromPythonScript(filename, "config", argVec); }
+   else
    {
-      if(argc<2)
-         throw std::runtime_error( config::usageString(argv[0]) );
+      config = make_shared< Config >();
+      config::createFromTextFile(*config, filename);
+   }
 
-      shared_ptr<Config> config;
-      std::string filename( argv[1] );
+   config::substituteCommandLineArgs(*config, argc, argv);
 
-      auto argVec = std::vector<std::string> (argv+1, argv + argc);
+   return config;
+}
 
-      if ( string_ends_with( filename, ".py")  ) {
-         config = createConfigFromPythonScript( filename, "config", argVec );
-      }
-      else {
-         config = make_shared<Config>();
-         config::createFromTextFile( *config, filename );
-      }
-
-      config::substituteCommandLineArgs( *config, argc, argv );
+config::Iterator configBegin(int argc, char** argv)
+{
+   if (argc < 2) throw std::runtime_error(config::usageString(argv[0]));
 
-      return config;
+   std::string filename(argv[1]);
+   if (string_ends_with(filename, ".py"))
+   {
+      auto argVec = std::vector< std::string >(argv + 1, argv + argc);
+      return createConfigIteratorFromPythonScript(filename, "config", argVec);
    }
-
-   config::Iterator configBegin( int argc, char ** argv )
+   else
    {
-      if(argc<2)
-         throw std::runtime_error( config::usageString(argv[0]) );
-
-      std::string filename( argv[1] );
-      if ( string_ends_with( filename, ".py")  ) {
-         auto argVec = std::vector<std::string> (argv+1, argv + argc);
-         return createConfigIteratorFromPythonScript( filename, "config", argVec );
-      }
-      else {
-         return config::begin( argc, argv );
-      }
-
+      return config::begin(argc, argv);
    }
-
-
-
-
+}
 
 } // namespace python_coupling
 } // namespace walberla
-
diff --git a/src/python_coupling/CreateConfig.h b/src/python_coupling/CreateConfig.h
index 4c7dec11645b158c7e7e04638f64cf71d2376440..f95d3586e33841976ccaf7f2510372f84a95a951 100644
--- a/src/python_coupling/CreateConfig.h
+++ b/src/python_coupling/CreateConfig.h
@@ -15,7 +15,8 @@
 //
 //! \file CreateConfigFromPythonScript.h
 //! \ingroup python
-//! \author martin
+//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //! \brief Creates a walberla::Config object from a python script
 //
 //======================================================================================================================
diff --git a/src/python_coupling/DictWrapper.h b/src/python_coupling/DictWrapper.h
index 0d2f0cab139433909ec078d7f1cf2b44e15e0a72..c3e1c2686b647f7f52c4522784cf772584fa1d8b 100644
--- a/src/python_coupling/DictWrapper.h
+++ b/src/python_coupling/DictWrapper.h
@@ -16,7 +16,8 @@
 //! \file DictWrapper.h
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
-//! \brief Wrapper to store and extract values from boost::python::dict
+//! \author Markus Holzer <markus.holzer@fau.de>
+//! \brief Wrapper to store and extract values from pybind11
 //
 //! \warning: if you include this header you also have to include Python.h as first header in your
 //!           cpp file
@@ -34,8 +35,6 @@
 
 namespace walberla {
 namespace python_coupling {
-
-
    class DictWrapper
    {
    public:
@@ -43,9 +42,9 @@ namespace python_coupling {
       //** Expose Data *************************************************************************************************
       /*! \name Expose Data */
       //@{
-      template<typename T>  inline void exposePtr( const std::string & name, T * var );
-      template<typename T>  inline void exposePtr( const std::string & name, const shared_ptr<T> & var );
-      template<typename T>  void exposeValue     ( const std::string & name, const T & var );
+      template<typename T>  inline void exposePtr(const char* name, T * var );
+      template<typename T>  inline void exposePtr(const char* name, const shared_ptr<T> & var );
+      template<typename T>  void exposeValue     ( const char* name, const T & var );
       //@}
       //****************************************************************************************************************
 
@@ -53,19 +52,19 @@ namespace python_coupling {
       //** Get Data  ***************************************************************************************************
       /*! \name Get Data */
       //@{
-      template<typename T> inline T    get( const std::string & name );
-      template<typename T> inline bool has( const std::string & name );
-      template<typename T> inline bool checkedGet( const std::string & name, T output );
+      template<typename T> inline T    get( const char* name );
+      template<typename T> inline bool has( const char* name );
+      template<typename T> inline bool checkedGet( const char* name, T output );
       //@}
       //****************************************************************************************************************
 
 
 #ifdef WALBERLA_BUILD_WITH_PYTHON
    public:
-            boost::python::dict & dict()        { return d_; }
-      const boost::python::dict & dict() const  { return d_; }
+            pybind11::dict & dict()        { return d_; }
+      const pybind11::dict & dict() const  { return d_; }
    protected:
-      boost::python::dict d_;
+            pybind11::dict d_;
 #endif
    };
 
diff --git a/src/python_coupling/DictWrapper.impl.h b/src/python_coupling/DictWrapper.impl.h
index b6ffecb9be49db88624e5461c1f9b20ab5670d1b..a2c8530c7448075df762276d1ebf73163c7ffbb3 100644
--- a/src/python_coupling/DictWrapper.impl.h
+++ b/src/python_coupling/DictWrapper.impl.h
@@ -16,14 +16,18 @@
 //! \file DictWrapper.impl.h
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
 #include <functional>
+#include <pybind11/pybind11.h>
 
 namespace walberla {
 namespace python_coupling {
 
+namespace py = pybind11;
+
 
 //===================================================================================================================
 //
@@ -33,17 +37,17 @@ namespace python_coupling {
 
 
 template<typename T>
-void DictWrapper::exposePtr( const std::string & name, T * var ) {
-   this->d_[name] =  boost::python::ptr( var );
+void DictWrapper::exposePtr(const char* name, T * var ) {
+   this->d_[name] = var;
 }
 
 template<typename T>
-void DictWrapper::exposePtr( const std::string & name, const shared_ptr<T> & var ) {
-   this->d_[name] =  boost::python::ptr( var.get() );
+void DictWrapper::exposePtr(const char* name, const shared_ptr<T> & var ) {
+   this->d_[name] = var.get();
 }
 
 template<typename T>
-void DictWrapper::exposeValue( const std::string & name, const T & var ) {
+void DictWrapper::exposeValue( const char* name, const T & var ) {
    this->d_[name] = var;
 }
 
@@ -57,21 +61,21 @@ void DictWrapper::exposeValue( const std::string & name, const T & var ) {
 
 
 template<typename T>
-T DictWrapper::get( const std::string & name ) {
-   return boost::python::extract<T>( d_[name] );
+T DictWrapper::get( const char* name ) {
+   return py::cast<T>( d_[name] );
 }
 
 template<typename T>
-bool DictWrapper::has( const std::string & name )
+bool DictWrapper::has( const char* name )
 {
-   if(! d_.has_key(name) )
+   if(! d_.contains(name) )
       return false;
 
-   return boost::python::extract<T>( d_[name]).check();
+   return py::class_<T>( d_[name]).check();
 }
 
 template<typename T>
-bool DictWrapper::checkedGet( const std::string & name, T output )
+bool DictWrapper::checkedGet( const char* name, T output )
 {
    if ( ! has<T>(name) )
       return false;
@@ -85,20 +89,20 @@ bool DictWrapper::checkedGet( const std::string & name, T output )
 
 
 template<>
-inline DictWrapper DictWrapper::get( const std::string & name ) {
-   auto dictCopy =  boost::python::extract< boost::python::dict >( d_[name] );
+inline DictWrapper DictWrapper::get( const char* name ) {
+   auto dictCopy =  py::dict( d_[name] );
    DictWrapper result;
    result.dict() = dictCopy;
    return result;
 }
 
 template<>
-inline bool DictWrapper::has<DictWrapper >( const std::string & name )
+inline bool DictWrapper::has<DictWrapper >( const char* name )
 {
-   if(! d_.has_key(name) )
+   if(! d_.contains(name) )
       return false;
 
-   return boost::python::extract< boost::python::dict >( d_[name]).check();
+   return py::isinstance<py::dict>(d_[name]);
 }
 
 
@@ -109,22 +113,22 @@ inline bool DictWrapper::has<DictWrapper >( const std::string & name )
 //===================================================================================================================
 
 // void()
-inline void runPythonObject( boost::python::object obj ) {
+inline void runPythonObject( py::object obj ) {
    obj();
 }
 template<>
-inline std::function<void()> DictWrapper::get( const std::string & name ) {
-   boost::python::object obj ( d_[name] );
+inline std::function<void()> DictWrapper::get( const char* name ) {
+   py::object obj ( d_[name] );
    return std::bind( &runPythonObject, obj );
 }
 
 template<>
-inline bool DictWrapper::has<std::function<void()> >( const std::string & name )
+inline bool DictWrapper::has<std::function<void()> >( const char* name )
 {
-   if(! d_.has_key(name) )
+   if(! d_.contains(name) )
       return false;
 
-   return PyCallable_Check( boost::python::object(d_[name]).ptr() ) != 0;
+   return PyCallable_Check( py::object(d_[name]).ptr() ) != 0;
 }
 
 
diff --git a/src/python_coupling/Manager.cpp b/src/python_coupling/Manager.cpp
index b7af694e847bb6cf068ee799529d84e2672f80b6..4b22df9746a03c20ed8047014ad565efb4333573 100644
--- a/src/python_coupling/Manager.cpp
+++ b/src/python_coupling/Manager.cpp
@@ -16,6 +16,7 @@
 //! \file Manager.cpp
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
@@ -26,25 +27,23 @@
 
 #ifdef WALBERLA_BUILD_WITH_PYTHON
 
-#include "core/waLBerlaBuildInfo.h"
-#include "Manager.h"
-#include "core/logging/Logging.h"
-#include "python_coupling/basic_exports/BasicExports.h"
+#   include "core/logging/Logging.h"
+#   include "core/waLBerlaBuildInfo.h"
 
-#include <cstdlib>
+#   include "python_coupling/export/BasicExport.h"
 
+#   include <pybind11/embed.h>
 
-BOOST_PYTHON_MODULE( walberla_cpp )
+#   include "Manager.h"
+
+PYBIND11_MODULE( walberla_cpp, m)
 {
    using namespace walberla::python_coupling;
-   auto manager =Manager::instance();
-   exportBasicWalberlaDatastructures();
-   manager->exportAll();
+   auto manager = Manager::instance();
+   exportBasicWalberlaDatastructures(m);
+   manager->exportAll(m);
 }
 
-using namespace boost::python;
-
-
 namespace walberla {
 namespace python_coupling {
 
@@ -57,7 +56,7 @@ Manager::~Manager( ) //NOLINT
 {
    // To work reliably this would have to be called at the end of the
    // main function. At this position this leads to a segfault in some cases
-   // Py_Finalize();
+   // py::finalize_interpreter();
 }
 
 void Manager::addEntryToPythonPath( const std::string & path )
@@ -75,15 +74,13 @@ void Manager::addPath( const std::string & path )
 {
    WALBERLA_ASSERT( initialized_ );
 
-   object sys = import("sys");
-   list sys_path = extract<list>( sys.attr("path") );
+   py::object sys = py::module::import("sys");
+   py::list sys_path = py::list( sys.attr("path") );
    sys_path.append(path);
 }
 
 void Manager::triggerInitialization()
 {
-   using namespace boost::python;
-
    if ( initialized_ ){
       return;
    }
@@ -91,15 +88,14 @@ void Manager::triggerInitialization()
 
    try
    {
-#if PY_MAJOR_VERSION >= 3
+      // The python module is used as embedded module here. There is a pybind11 macro for that called
+      // PYBIND11_EMBEDDED_MODULE. However it can not be used here since we want a shared lib for the python coupling.
+      // With the C-Call the so is embedded here.
       PyImport_AppendInittab( (char*)"walberla_cpp", PyInit_walberla_cpp );
-#else
-      PyImport_AppendInittab( (char*)"walberla_cpp", initwalberla_cpp );
-#endif
 
-      Py_Initialize();
-      import("__main__");
-      import("walberla_cpp");
+      py::initialize_interpreter();
+      py::module::import("__main__");
+      py::module::import("walberla_cpp");
 
       // Setup python path
       addPath( std::string(WALBERLA_SOURCE_DIR) + "/python" );
@@ -113,54 +109,41 @@ void Manager::triggerInitialization()
       entriesForPythonPath_.clear();
 
    }
-   catch ( boost::python::error_already_set & ) {
-      PyObject *type_ptr = nullptr;
-
-      PyObject *value_ptr = nullptr;
-
-      PyObject *traceback_ptr = nullptr;
-      PyErr_Fetch(&type_ptr, &value_ptr, &traceback_ptr);
-
-      if( type_ptr )
-      {
-         extract<std::string> type_str(( str( handle<>( type_ptr ) ) ));
-         if( type_str.check() )
-            WALBERLA_LOG_DEVEL( type_str() );
+   catch ( py::error_already_set & e ) {
+      if (e.matches(PyExc_ModuleNotFoundError)){
+         py::print("The module walberla_cpp could not be found");
       }
-      if(value_ptr)
-      {
-         extract<std::string> value_str(( str( handle<>( value_ptr ) ) ));
-         if ( value_str.check() )
-            WALBERLA_LOG_DEVEL( value_str() );
+      else {
+         py::print("Unexpected Exception");
+         throw;
       }
-
       WALBERLA_ABORT( "Error while initializing Python" );
    }
 }
 
 
 
-void Manager::exportAll()
+void Manager::exportAll(py::module_ &m)
 {
    for( auto it = exporterFunctions_.begin(); it != exporterFunctions_.end(); ++it ) {
-      (*it)();
+      (*it)(m);
    }
 }
 
-boost::python::object Manager::pythonObjectFromBlockData( IBlock & block, BlockDataID id )
+py::object Manager::pythonObjectFromBlockData( IBlock & block, BlockDataID id )
 {
-   if( block.isDataOfType< boost::python::object > ( id )  )
-      return *block.getData< boost::python::object > ( id );
+   if( block.isDataOfType< py::object > ( id )  ){
+      return *block.getData< py::object > ( id );}
 
 
    for( auto it = blockDataToObjectFunctions_.begin(); it != blockDataToObjectFunctions_.end(); ++it )
    {
       auto res = (*it)( block, id );
-      if ( res != boost::python::object() )
-         return res;
+      if ( !res.is(py::object()) ){
+         return res;}
    }
 
-   return boost::python::object();
+   return py::object();
 }
 
 
diff --git a/src/python_coupling/Manager.h b/src/python_coupling/Manager.h
index ed843ff11649c00ecfedf7b3e02713d8ce4afdb5..8f860a27a7157fe122084cc01418661dfa6220d9 100644
--- a/src/python_coupling/Manager.h
+++ b/src/python_coupling/Manager.h
@@ -16,6 +16,7 @@
 //! \file Manager.h
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //! \brief Singleton managing Python coupling
 //
 //======================================================================================================================
@@ -27,11 +28,13 @@
 #include "python_coupling/helper/MplHelpers.h"
 #include "core/singleton/Singleton.h"
 #include "domain_decomposition/IBlock.h"
+#include <pybind11/pybind11.h>
 
 void init_module_walberla_cpp();
 
 namespace walberla {
 namespace python_coupling {
+namespace py = pybind11;
 
 
    class BlockDataToObjectTester
@@ -44,24 +47,24 @@ namespace python_coupling {
       template<typename TypeToTest>
       void operator() ( NonCopyableWrap<TypeToTest> )
       {
-         using boost::python::object;
-         if ( result_ == object() &&  block_->isDataClassOrSubclassOf<TypeToTest>( blockDataID_ ) ) {
-            result_ = object( boost::python::ptr( block_->getData<TypeToTest>( blockDataID_ ) ) );
+         using py::object;
+         if ( result_.is(py::object()) &&  block_->isDataClassOrSubclassOf<TypeToTest>( blockDataID_ ) ) {
+            result_ = py::cast( block_->getData<TypeToTest>( blockDataID_ ) );
          }
       }
 
-      boost::python::object getResult() { return result_; }
+      py::object getResult() { return result_; }
    private:
       IBlock * block_;
       BlockDataID blockDataID_;
-      boost::python::object result_;
+      py::object result_;
    };
 
-   template<typename TypeList>
-   boost::python::object testBlockData( IBlock & block, BlockDataID blockDataID )
+   template<typename... Types>
+   py::object testBlockData( IBlock & block, BlockDataID blockDataID )
    {
       BlockDataToObjectTester tester( &block, blockDataID );
-      for_each_noncopyable_type< TypeList > ( std::ref(tester) );
+      for_each_noncopyable_type< Types... > ( std::ref(tester) );
       return tester.getResult();
    }
 
@@ -71,8 +74,8 @@ namespace python_coupling {
    public:
       WALBERLA_BEFRIEND_SINGLETON;
 
-      typedef std::function<void()> ExporterFunction;
-      typedef std::function< boost::python::object ( IBlock&, BlockDataID ) > BlockDataToObjectFunction;
+      typedef std::function<void(py::module_&)> ExporterFunction;
+      typedef std::function< py::object ( IBlock&, BlockDataID ) > BlockDataToObjectFunction;
 
 
       ~Manager();
@@ -82,19 +85,21 @@ namespace python_coupling {
       void addEntryToPythonPath( const std::string & path );
 
 
-      void addExporterFunction( const ExporterFunction & f ) { exporterFunctions_.push_back( f ); }
-
-      template<typename TypeList>
-      void addBlockDataConversion() { blockDataToObjectFunctions_.push_back( &testBlockData<TypeList>  ); }
+      void addExporterFunction( const ExporterFunction & f)
+      {
+         exporterFunctions_.push_back( f );
+      }
 
+      template<typename... Types>
+      void addBlockDataConversion() { blockDataToObjectFunctions_.push_back( &testBlockData<Types...>  ); }
 
-   protected:
+      void exportAll(py::module_ &m);
+    protected:
       void addPath( const std::string & path );
       friend void ::init_module_walberla_cpp();
-      void exportAll();
 
-      friend boost::python::object IBlock_getData( boost::python::object, const std::string &  );
-      boost::python::object pythonObjectFromBlockData( IBlock & block, BlockDataID  id );
+      friend py::object IBlock_getData( py::object, const std::string &  );
+      py::object pythonObjectFromBlockData( IBlock & block, BlockDataID  id );
 
       Manager();
 
diff --git a/src/python_coupling/PythonCallback.cpp b/src/python_coupling/PythonCallback.cpp
index 507087fc9f84f2524f05f720b0a5fdfb9ec7cd06..b4d0fb6d8a1fefcdbbf562ab3ea163e26ada1bae 100644
--- a/src/python_coupling/PythonCallback.cpp
+++ b/src/python_coupling/PythonCallback.cpp
@@ -16,36 +16,30 @@
 //! \file PythonCallback.cpp
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
 #include "PythonCallback.h"
-#include "PythonWrapper.h"
 #include "DictWrapper.h"
 
 #ifdef WALBERLA_BUILD_WITH_PYTHON
 
 #include "Manager.h"
 #include "core/Abort.h"
-#include "core/logging/Logging.h"
-#include "helper/ExceptionHandling.h"
 #include "core/Filesystem.h"
 
-#if defined(__GLIBCXX__) && __GLIBCXX__ <= 20160609
-#define CURRENT_PATH_WORKAROUND
-#include <unistd.h>
-#include <errno.h>
-#endif
+#include "pybind11/eval.h"
 
 namespace walberla {
 namespace python_coupling {
 
-   static boost::python::object importModuleOrFileInternal( const std::string & fileOrModuleName, const std::vector< std::string > & argv )
+   static py::object importModuleOrFileInternal( const std::string & fileOrModuleName, const std::vector< std::string > & argv )
    {
       auto manager = python_coupling::Manager::instance();
       manager->triggerInitialization();
 
-      namespace bp = boost::python;
+      namespace py = pybind11;
 
       std::string moduleName = fileOrModuleName;
 
@@ -57,52 +51,25 @@ namespace python_coupling {
       code << "] \n";
 
       filesystem::path path ( fileOrModuleName );
-#ifdef CURRENT_PATH_WORKAROUND
-      // workaround for double free in filesystem::current_path in libstdc++ 5.4 and lower
-      size_t cwd_size = 16;
-      char * cwd_buf = (char*) std::malloc(cwd_size * sizeof(char));
-
-      while( getcwd( cwd_buf, cwd_size ) == NULL )
-      {
-         if (errno == ERANGE)
-         {
-            cwd_size *= 2;
-            cwd_buf = (char*) std::realloc( cwd_buf, cwd_size * sizeof(char) );
-         }
-         else
-         {
-            python_coupling::terminateOnPythonException( std::string("Could not determine working directory") );
-         }
-      }
-
-      std::string cwd(cwd_buf);
-      std::free(cwd_buf);
-      path = filesystem::absolute( path, cwd );
-#else
       path = filesystem::absolute( path );
-#endif
+
       if ( path.extension() == ".py" )
       {
          moduleName = path.stem().string();
-
-
-         if ( ! path.parent_path().empty() )  {
-#ifdef CURRENT_PATH_WORKAROUND
-            std::string p = filesystem::canonical(path.parent_path(), cwd).string();
-#else
+         if ( ! path.parent_path().empty() ) {
             std::string p = filesystem::canonical(path.parent_path()).string();
-#endif
             code << "sys.path.append( r'" << p << "')" << "\n";
          }
       }
-      bp::exec( code.str().c_str(), bp::import("__main__").attr("__dict__") );
+
+      py::exec( code.str().c_str(), py::module::import("__main__").attr("__dict__") );
 
       try {
-         return bp::import( moduleName.c_str() );
+         return py::module::import( moduleName.c_str() );
       }
-      catch ( bp::error_already_set & ) {
-         python_coupling::terminateOnPythonException( std::string("Python Error while loading ") + fileOrModuleName );
-         return boost::python::object();
+      catch ( py::error_already_set &e) {
+         throw py::value_error(e.what());
+         return py::none();
       }
    }
 
@@ -116,7 +83,7 @@ namespace python_coupling {
       : exposedVars_( new DictWrapper() ), callbackDict_( new DictWrapper() )
    {
       Manager::instance()->triggerInitialization();
-      callbackDict_->dict() = boost::python::dict();
+      callbackDict_->dict() = py::dict();
    }
 
 
@@ -125,13 +92,13 @@ namespace python_coupling {
    {
       Manager::instance()->triggerInitialization();
 
-      using namespace boost::python;
+      namespace py = pybind11;
 
       // Add empty callbacks module
       importModuleOrFileInternal( fileOrModuleName, argv );
-      object callbackModule = import( "walberla_cpp.callbacks");
+      py::object callbackModule = py::module::import( "walberla_cpp.callbacks");
 
-      callbackDict_->dict() = extract<dict>( callbackModule.attr( "__dict__" ) );
+      callbackDict_->dict() = py::dict( callbackModule.attr( "__dict__" ) );
    }
 
 
@@ -140,41 +107,36 @@ namespace python_coupling {
    {
       Manager::instance()->triggerInitialization();
 
-      using namespace boost::python;
-
       // Add empty callbacks module
-      object callbackModule = import( "walberla_cpp.callbacks");
+      py::object callbackModule = py::module::import( "walberla_cpp.callbacks");
 
-      callbackDict_->dict() = extract<dict>( callbackModule.attr( "__dict__" ) );
+      callbackDict_->dict() = py::dict( callbackModule.attr( "__dict__" ) );
    }
 
    bool PythonCallback::isCallable() const
    {
-      return callbackDict_->dict().has_key( functionName_ );
+      return callbackDict_->dict().contains( functionName_ );
    }
 
    void PythonCallback::operator() ()
    {
       if ( ! isCallable() )
          WALBERLA_ABORT_NO_DEBUG_INFO( "Could not call python function '" << functionName_ << "'. " <<
-                                        "Did you forget to set the callback function?" );
+                                             "Did you forget to set the callback function?" );
 
-      namespace bp = boost::python;
+      namespace py = pybind11;
 
       try
       {
-         if ( exposedVars_->dict().has_key("returnValue"))
-            bp::api::delitem( exposedVars_->dict(), "returnValue" );
-
-         bp::object function = callbackDict_->dict()[ functionName_ ];
+         py::object function = callbackDict_->dict()[ functionName_.c_str() ];
 
-         bp::object returnVal;
-         returnVal = function( *bp::tuple(), **(exposedVars_->dict() ) );
+         py::object returnVal;
+         returnVal = function( *py::tuple(), **(exposedVars_->dict() ) );
 
          exposedVars_->dict()["returnValue"] = returnVal;
       }
-      catch ( bp::error_already_set & ) {
-         python_coupling::terminateOnPythonException( std::string("Error while running Python function ") + functionName_ );
+      catch ( py::error_already_set &e ) {
+         throw py::value_error(e.what());
       }
    }
 
diff --git a/src/python_coupling/PythonCallback.h b/src/python_coupling/PythonCallback.h
index de7d0eaee2c9a7a3b4383e0ed73ed55a389b56d7..9eeaec6505516b6e80b9aa3b0fdd341912bc0d29 100644
--- a/src/python_coupling/PythonCallback.h
+++ b/src/python_coupling/PythonCallback.h
@@ -16,6 +16,7 @@
 //! \file PythonCallback.h
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
@@ -79,8 +80,8 @@ namespace python_coupling {
       PythonCallback();
       PythonCallback( const std::string & functionName );
       PythonCallback( const std::string & moduleOrFile,
-                      const std::string & functionName,
-                      const std::vector<std::string> & argv = std::vector<std::string>() );
+                             const std::string & functionName,
+                             const std::vector<std::string> & argv = std::vector<std::string>() );
 
             DictWrapper & data()       { return *exposedVars_; }
       const DictWrapper & data() const { return *exposedVars_; }
diff --git a/src/python_coupling/PythonWrapper.h b/src/python_coupling/PythonWrapper.h
index 74ce9918e0bb96766bf493933964a1db18e8d19c..3909af0521faa0673fe7ada7b6c19c2acbe0df9f 100644
--- a/src/python_coupling/PythonWrapper.h
+++ b/src/python_coupling/PythonWrapper.h
@@ -17,6 +17,7 @@
 //! \ingroup core
 //! \author Matthias Markl <matthias.markl@fau.de>
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
@@ -27,25 +28,6 @@
 
 #ifdef WALBERLA_BUILD_WITH_PYTHON // macro defined in waLBerlaDefinitions.h
 
-#ifdef _MSC_VER
-#pragma warning ( push, 3 )
-#pragma warning ( disable: 4244 4275 4800 4251 4267 )
-#ifndef HAVE_ROUND
-#define HAVE_ROUND 1
-#define __CREATED_HAVE_ROUND
-#endif
-#endif
-
-#include <boost/python.hpp>
-#include <boost/python/stl_iterator.hpp>
-#include <boost/python/suite/indexing/vector_indexing_suite.hpp>
-
-#ifdef _MSC_VER
-#ifdef __CREATED_HAVE_ROUND
-#undef HAVE_ROUND
-#undef __CREATED_HAVE_ROUND
-#endif
-#pragma warning ( pop )
-#endif
+#include "pybind11/pybind11.h"
 
 #endif
diff --git a/src/python_coupling/Shell.cpp b/src/python_coupling/Shell.cpp
deleted file mode 100644
index 5749511e067a1f848f6266667dbf2073bcfe1336..0000000000000000000000000000000000000000
--- a/src/python_coupling/Shell.cpp
+++ /dev/null
@@ -1,247 +0,0 @@
-//======================================================================================================================
-//
-//  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 Shell.impl.h
-//! \ingroup python_coupling
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-// Do not reorder includes - the include order is important
-
-#include "PythonWrapper.h"
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-#include "Shell.h"
-#include "DictWrapper.h"
-#include "Manager.h"
-
-#include "core/logging/Logging.h"
-#include "core/StringUtility.h"
-
-
-namespace walberla {
-namespace python_coupling {
-
-
-   Shell::Shell( const std::string & prompt )
-      : exposedVars_( new DictWrapper() )
-   {
-      Manager::instance()->triggerInitialization();
-
-      using namespace boost::python;
-
-      try {
-         object main_module  = import("__main__");
-         dict globals = extract<dict>( main_module.attr( "__dict__" ) );
-
-         exec("import os \n"
-              "import code \n"
-              "try:\n"
-              "   import readline \n"
-              "   import rlcompleter \n"
-              "   readline.parse_and_bind(\"tab: complete\") \n"
-              "   histfile = os.path.join( os.path.expanduser('~'), '.waLBerla_history') \n"
-              "   try:\n"
-              "      readline.read_history_file(histfile)\n"
-              "   except IOError:\n"
-              "      pass\n"
-              "except ImportError:\n"
-              "   pass\n"
-              "\n", globals );
-
-
-         prompt1_ = prompt;
-         for ( uint_t i=0; i < prompt.size(); ++i )
-            prompt2_ += " ";
-
-         prompt1_ += "> ";
-         prompt2_ += "> ";
-
-         exec("import sys", globals );
-         exec( std::string( "sys.ps1 = '" + prompt1_ + "'" ).c_str(), globals );
-         exec( std::string( "sys.ps2 = '" + prompt2_ + "'" ).c_str(), globals );
-      }
-      catch ( error_already_set & ) {
-         PyErr_Print();
-         WALBERLA_ABORT( "Error initializing Shell" );
-      }
-   }
-
-   Shell::~Shell()
-   {
-      using namespace boost::python;
-
-      object main_module  = import("__main__");
-      dict globals = extract<dict>( main_module.attr( "__dict__" ) );
-      exec("readline.write_history_file(histfile)", globals );
-   }
-
-
-   bool Shell::isCompleteCommand ( const std::string & code )
-   {
-      using namespace boost::python;
-      object main_module  = import("__main__");
-      dict globals = extract<dict>( main_module.attr( "__dict__" ) );
-
-      boost::python::dict locals;
-      locals["codeToTest"] = code;
-      object compiledCode = eval( str( "code.compile_command(codeToTest)" ), globals, locals );
-      return ( compiledCode != object() );
-   }
-
-   bool Shell::getCompleteCommand( std::string & result )
-   {
-      uint_t lineCounter = 0;
-
-      while ( true )
-      {
-         char* line;
-
-         if ( lineCounter == 0 )
-            line = PyOS_Readline( stdin, stdout, (char*)prompt1_.c_str() );
-         else
-            line = PyOS_Readline( stdin, stdout, (char*)prompt2_.c_str() );
-
-         if ( line == nullptr || *line == '\0' ) {  // interrupt or EOF
-            result.clear();
-            return false;
-         }
-
-         std::string strLine ( line );
-         std::string strTrimmedLine( line );
-         string_trim( strTrimmedLine );
-
-         PyMem_Free( line );
-
-         lineCounter++;
-         result += strLine;
-
-         bool commandComplete = isCompleteCommand( result );
-
-         if ( lineCounter == 1 && commandComplete )
-            return true;
-         if ( strTrimmedLine.empty() && isCompleteCommand(result) ) // multiline commands have to end with empty line
-            return true;
-      }
-
-      return false;
-   }
-
-
-   void Shell::operator() ()
-   {
-      using namespace boost::python;
-
-      object main_module  = import("__main__");
-      dict globals = extract<dict>( main_module.attr( "__dict__" ) );
-
-      globals.update( exposedVars_->dict() );
-      exec( "from waLBerla import *", globals );
-
-
-      const int  MAX_LINE_LENGTH   = 1024;
-      const char continueMarker    = 'c';
-      const char stopMarker        = 's';
-
-      WALBERLA_ROOT_SECTION()
-      {
-         std::string code;
-
-         while ( true )
-         {
-            code.clear();
-
-            try
-            {
-               if ( ! getCompleteCommand(code ) ) {
-                  std::cout << "\n";
-                  code.resize( MAX_LINE_LENGTH );
-                  code[0] = stopMarker;
-                  MPI_Bcast( (void*) code.c_str(), MAX_LINE_LENGTH, // send stop command
-                             MPI_CHAR, 0, MPI_COMM_WORLD );
-                  break;
-               }
-               else
-               {
-                  std::string codeToSend = continueMarker + code;
-
-                  if ( codeToSend.size() >= uint_c( MAX_LINE_LENGTH ) )
-                  {
-                     WALBERLA_LOG_WARNING("Line length too big, only allowed " << MAX_LINE_LENGTH-1 << " characters" );
-                     continue;
-                  }
-                  codeToSend.resize( MAX_LINE_LENGTH );
-                  MPI_Bcast( (void*) codeToSend.c_str(), MAX_LINE_LENGTH, // send code snippet to other processes
-                             MPI_CHAR, 0, MPI_COMM_WORLD );
-
-                  PyRun_SimpleString( code.c_str() );
-                  fflush( stderr );
-                  WALBERLA_MPI_BARRIER();
-               }
-            }
-            catch( boost::python::error_already_set & ) {
-               PyErr_Print();
-            }
-         }
-      }
-      else
-      {
-         char * buffer = new char[MAX_LINE_LENGTH];
-
-         while ( true)
-         {
-
-            MPI_Bcast( (void*) buffer, MAX_LINE_LENGTH, // send code snippet to other processes
-                       MPI_CHAR, 0, MPI_COMM_WORLD );
-
-            if ( *buffer == stopMarker )
-               break;
-
-            std::string code ( buffer+1 );
-
-            PyRun_SimpleString( code.c_str() );
-            fflush( stderr );
-            WALBERLA_MPI_BARRIER();
-         }
-
-
-         delete [] buffer;
-      }
-   }
-
-
-} // namespace python_coupling
-} // namespace walberla
-
-
-#else
-
-
-#include "Shell.h"
-
-namespace walberla {
-namespace python_coupling {
-
-   Shell::Shell( const std::string & )  {}
-   Shell::~Shell() = default;
-   void Shell::operator()() {}
-
-} // namespace python_coupling
-} // namespace walberla
-
-
-#endif
diff --git a/src/python_coupling/Shell.h b/src/python_coupling/Shell.h
deleted file mode 100644
index 41be5c3c8a6550f851859bd8ee982e85f91c0dca..0000000000000000000000000000000000000000
--- a/src/python_coupling/Shell.h
+++ /dev/null
@@ -1,63 +0,0 @@
-//======================================================================================================================
-//
-//  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 ScriptRunner.h
-//! \ingroup python_coupling
-//! \author Martin Bauer <martin.bauer@fau.de>
-//! \brief Runs a python script that can access waLBerla variables
-//
-//======================================================================================================================
-
-#pragma once
-
-#include <string>
-
-#include "waLBerlaDefinitions.h"
-#include "core/DataTypes.h"
-
-
-
-namespace walberla {
-namespace python_coupling {
-
-   class DictWrapper;
-
-   class Shell
-   {
-   public:
-      Shell( const std::string & prompt = "waLBerla");
-      ~Shell();
-
-      void operator() ();
-
-      inline void run()           { (*this)();           }
-      inline DictWrapper & data() { return *exposedVars_; }
-
-   protected:
-      bool getCompleteCommand( std::string & result );
-      bool isCompleteCommand ( const std::string & code );
-
-      shared_ptr<DictWrapper> exposedVars_;
-      std::string prompt1_;
-      std::string prompt2_;
-   };
-
-
-} // namespace python_coupling
-} // namespace walberla
-
-
-
-
diff --git a/src/python_coupling/TimeloopIntercept.cpp b/src/python_coupling/TimeloopIntercept.cpp
deleted file mode 100644
index 17e7df7884e85e618d3d2e279dbaded26857f769..0000000000000000000000000000000000000000
--- a/src/python_coupling/TimeloopIntercept.cpp
+++ /dev/null
@@ -1,136 +0,0 @@
-//======================================================================================================================
-//
-//  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 TimeloopIntercept.cpp
-//! \ingroup python_coupling
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#include "TimeloopIntercept.h"
-#include "DictWrapper.h"
-#include "PythonCallback.h"
-#include "Shell.h"
-
-#include "core/logging/Logging.h"
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-
-
-#if defined(_MSC_VER)
-
-#else
-#  include <csignal>
-#endif
-
-
-static bool signalTriggered = false;
-
-
-namespace walberla {
-namespace python_coupling {
-
-
-#if defined(_MSC_VER)
-
-void enableSignalHandler() {
-
-}
-
-#else
-
-void customSignalHandler( int  ) {
-   signalTriggered = true;
-}
-
-void enableSignalHandler() {
-   std::signal (SIGUSR1, customSignalHandler );
-}
-
-#endif
-
-
-
-TimeloopIntercept::TimeloopIntercept( const std::string & functionName, uint_t interval, bool enableSignalInterrupt )
-         : callback_ ( walberla::make_shared<PythonCallback> ( functionName ) ),
-           timestep_ ( 0 ),
-           interval_ ( interval ),
-           enableSignalInterrupt_ ( enableSignalInterrupt )
-{
-   if ( enableSignalInterrupt )
-      enableSignalHandler();
-}
-
-TimeloopIntercept::TimeloopIntercept( const std::string & pythonFile, const std::string & functionName,
-                                      uint_t interval, bool enableSignalInterrupt )
-         : callback_ ( walberla::make_shared<PythonCallback> ( pythonFile, functionName ) ),
-           timestep_ ( 0 ),
-           interval_ ( interval ),
-           enableSignalInterrupt_ ( enableSignalInterrupt )
-{
-   if ( enableSignalInterrupt )
-      enableSignalHandler();
-}
-
-
-void TimeloopIntercept::operator() ()
-{
-   ++timestep_;
-   if( interval_ > 0 &&  ( timestep_ % interval_ == 0 ) )
-      (*callback_)();
-
-   if ( enableSignalInterrupt_ && signalTriggered )
-   {
-      WALBERLA_LOG_INFO( "Interrupting Simulation at timestep " << timestep_ );
-      signalTriggered = false;
-      Shell shell;
-      shell.data() = this->callback()->data();
-      shell.run();
-      WALBERLA_LOG_INFO( "Continue Simulation at timestep " << timestep_+1  << " ..." );
-   }
-}
-
-
-
-} // namespace python_coupling
-} // namespace walberla
-
-#else
-
-namespace walberla {
-namespace python_coupling {
-
-TimeloopIntercept::TimeloopIntercept( const std::string & functionName, uint_t interval, bool enableSignalInterrupt )
-         : callback_ ( walberla::make_shared<PythonCallback> ( functionName ) ),
-           timestep_ ( 0 ),
-           interval_ ( interval ),
-           enableSignalInterrupt_ ( enableSignalInterrupt )
-{}
-
-TimeloopIntercept::TimeloopIntercept( const std::string & pythonFile, const std::string & functionName,
-                                      uint_t interval, bool enableSignalInterrupt )
-         : callback_ ( walberla::make_shared<PythonCallback> ( pythonFile, functionName ) ),
-           timestep_ ( 0 ),
-           interval_ ( interval ),
-           enableSignalInterrupt_ ( enableSignalInterrupt )
-{}
-
-void TimeloopIntercept::operator() (){}
-
-} // namespace python_coupling
-} // namespace walberla
-
-#endif
diff --git a/src/python_coupling/TimeloopIntercept.h b/src/python_coupling/TimeloopIntercept.h
deleted file mode 100644
index fc7f496450838347aea55816c42fff5ccb60d631..0000000000000000000000000000000000000000
--- a/src/python_coupling/TimeloopIntercept.h
+++ /dev/null
@@ -1,60 +0,0 @@
-//======================================================================================================================
-//
-//  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 ScriptRunner.h
-//! \ingroup python_coupling
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include "core/DataTypes.h"
-
-namespace walberla {
-namespace python_coupling {
-
-   class DictWrapper;
-   class PythonCallback;
-
-
-   class TimeloopIntercept
-   {
-   public:
-      TimeloopIntercept( const std::string & functionName,
-                         uint_t interval = 1, bool enableSignalInterrupt=true );
-
-      TimeloopIntercept( const std::string & pythonFile, const std::string & functionName,
-                         uint_t interval = 1, bool enableSignalInterrupt=true  );
-
-      inline shared_ptr<PythonCallback> callback() { return callback_;         }
-
-      void operator() ();
-
-   protected:
-      shared_ptr<PythonCallback> callback_;
-      uint_t timestep_;
-      uint_t interval_;
-      bool   enableSignalInterrupt_;
-   };
-
-
-} // namespace python_coupling
-} // namespace walberla
-
-
-
-
-
diff --git a/src/python_coupling/basic_exports/BasicExports.cpp b/src/python_coupling/basic_exports/BasicExports.cpp
deleted file mode 100644
index 2d180f58f3bcfe837465486b21c1df094dea65a8..0000000000000000000000000000000000000000
--- a/src/python_coupling/basic_exports/BasicExports.cpp
+++ /dev/null
@@ -1,1238 +0,0 @@
-//======================================================================================================================
-//
-//  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 BasicExports.cpp
-//! \ingroup python_coupling
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-// Do not reorder includes - the include order is important
-#include "python_coupling/PythonWrapper.h"
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-#include "BasicExports.h"
-#include "MPIExport.h"
-#include "python_coupling/helper/ModuleScope.h"
-#include "core/waLBerlaBuildInfo.h"
-#include "core/logging/Logging.h"
-#include "core/Abort.h"
-#include "core/cell/CellInterval.h"
-#include "core/math/AABB.h"
-#include "core/mpi/MPIIO.h"
-#include "core/timing/ReduceType.h"
-#include "core/timing/TimingPool.h"
-#include "core/timing/TimingTree.h"
-#include "communication/UniformPackInfo.h"
-#include "communication/UniformMPIDatatypeInfo.h"
-#include "domain_decomposition/StructuredBlockStorage.h"
-#include "python_coupling/Manager.h"
-#include "python_coupling/helper/BlockStorageExportHelpers.h"
-#include "stencil/Directions.h"
-
-#include <boost/version.hpp>
-
-#include <functional>
-
-using namespace boost::python;
-
-
-namespace walberla {
-namespace python_coupling {
-
-
-template <class T>
-struct NumpyIntConversion
-{
-    NumpyIntConversion()
-    {
-        converter::registry::push_back( &convertible, &construct, boost::python::type_id<T>() );
-    }
-
-    static void* convertible( PyObject* pyObj)
-    {
-       auto typeName = std::string( Py_TYPE(pyObj)->tp_name );
-       if ( typeName.substr(0,9) == "numpy.int" )
-          return pyObj;
-       return nullptr;
-    }
-
-    static void construct( PyObject* pyObj, converter::rvalue_from_python_stage1_data* data )
-    {
-        handle<> x(borrowed(pyObj));
-        object o(x);
-        T value = extract<T>(o.attr("__int__")());
-        void* storage =( (boost::python::converter::rvalue_from_python_storage<T>*) data)->storage.bytes;
-        new (storage) T(value);
-        data->convertible = storage;
-    }
-};
-
-template <class T>
-struct NumpyFloatConversion
-{
-    NumpyFloatConversion()
-    {
-        converter::registry::push_back( &convertible, &construct, boost::python::type_id<T>() );
-    }
-
-    static void* convertible(PyObject* pyObj)
-    {
-       auto typeName = std::string( Py_TYPE(pyObj)->tp_name );
-       if ( typeName.substr(0,11) == "numpy.float" )
-          return pyObj;
-        return nullptr;
-    }
-
-    static void construct(PyObject* pyObj, converter::rvalue_from_python_stage1_data* data)
-    {
-        handle<> x(borrowed(pyObj));
-        object o(x);
-#ifdef WALBERLA_CXX_COMPILER_IS_GNU
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wmaybe-uninitialized"
-#endif
-        T value = extract<T>(o.attr("__float__")());
-#ifdef WALBERLA_CXX_COMPILER_IS_GNU
-#pragma GCC diagnostic pop
-#endif
-        void* storage =( (boost::python::converter::rvalue_from_python_storage<T>*) data)->storage.bytes;
-        new (storage) T(value);
-        data->convertible = storage;
-    }
-};
-
-
-#if BOOST_VERSION < 106300
-// taken from https://github.com/boostorg/python/commit/97e4b34a15978ca9d7c296da2de89b78bba4e0d5
-template <class T>
-struct exportSharedPtr
-{
-   exportSharedPtr()
-   {
-   converter::registry::insert( &convertible, &construct, boost::python::type_id<std::shared_ptr<T> >()
-#ifndef BOOST_PYTHON_NO_PY_SIGNATURES
-                                , &converter::expected_from_python_type_direct<T>::get_pytype
-#endif
-                              );
-   }
-
-private:
-   static void* convertible( PyObject* p )
-   {
-      if ( p == Py_None )
-         return p;
-
-      return converter::get_lvalue_from_python( p, converter::registered<T>::converters );
-   }
-
-   static void construct( PyObject* source, converter::rvalue_from_python_stage1_data* data )
-   {
-      void* const storage = ( (converter::rvalue_from_python_storage< std::shared_ptr<T> >*) data )->storage.bytes;
-      // Deal with the "None" case.
-      if ( data->convertible == source )
-         new (storage) std::shared_ptr<T>();
-      else
-      {
-         std::shared_ptr<void> hold_convertible_ref_count( (void*)0, converter::shared_ptr_deleter( handle<>( borrowed( source ) ) ) );
-         // use aliasing constructor
-         new (storage) std::shared_ptr<T>( hold_convertible_ref_count, static_cast<T*>(data->convertible) );
-      }
-
-      data->convertible = storage;
-   }
-};
-#endif
-
-//======================================================================================================================
-//
-//  Helper Functions
-//
-//======================================================================================================================
-
-void checkForThreeSequence( const object & o, const char * message )
-{
-   // strange construct required because also the len function throws , if object has no len
-   try {
-      if ( len(o ) != 3 )  throw error_already_set();
-   }
-   catch( error_already_set & ) {
-      PyErr_SetString(PyExc_RuntimeError, message);
-      throw error_already_set();
-   }
-}
-
-//======================================================================================================================
-//
-//  Vector3
-//
-//======================================================================================================================
-
-
-template<typename T>
-struct Vector3_to_PythonTuple
-{
-   static PyObject* convert( Vector3<T> const& v )
-   {
-      auto resultTuple = boost::python::make_tuple(v[0], v[1], v[2] );
-      return boost::python::incref ( boost::python::object ( resultTuple ).ptr () );
-   }
-};
-
-template<typename T>
-struct PythonTuple_to_Vector3
-{
-   PythonTuple_to_Vector3()
-   {
-     boost::python::converter::registry::push_back(
-       &convertible,
-       &construct,
-       boost::python::type_id<Vector3<T> >());
-   }
-
-   static void* convertible(PyObject* obj)
-   {
-      using namespace boost::python;
-
-      if ( ! ( PySequence_Check(obj) && PySequence_Size( obj ) == 3 ))
-         return nullptr;
-
-      object element0 ( handle<>( borrowed( PySequence_GetItem(obj,0) )));
-      object element1 ( handle<>( borrowed( PySequence_GetItem(obj,1) )));
-      object element2 ( handle<>( borrowed( PySequence_GetItem(obj,2) )));
-
-      if (  extract<T>( element0 ).check() &&
-            extract<T>( element1 ).check() &&
-            extract<T>( element2 ).check() )
-         return obj;
-      else
-         return nullptr;
-   }
-
-   static void construct( PyObject* obj, boost::python::converter::rvalue_from_python_stage1_data* data )
-   {
-      using namespace boost::python;
-
-      object element0 ( handle<>( borrowed( PySequence_GetItem(obj,0) )));
-      object element1 ( handle<>( borrowed( PySequence_GetItem(obj,1) )));
-      object element2 ( handle<>( borrowed( PySequence_GetItem(obj,2) )));
-
-
-      // Grab pointer to memory into which to construct the new Vector3
-      void* storage = ( (boost::python::converter::rvalue_from_python_storage<Vector3<T> >*) data )->storage.bytes;
-
-      new (storage) Vector3<T> ( extract<T>( element0 ),
-                                 extract<T>( element1 ),
-                                 extract<T>( element2 ) );
-
-      // Stash the memory chunk pointer for later use by boost.python
-      data->convertible = storage;
-   }
-};
-
-void exportVector3()
-{
-   // To Python
-   boost::python::to_python_converter< Vector3<bool      >, Vector3_to_PythonTuple<bool      > >();
-
-   boost::python::to_python_converter< Vector3<real_t    >, Vector3_to_PythonTuple<real_t    > >();
-
-   boost::python::to_python_converter< Vector3<uint8_t   >, Vector3_to_PythonTuple<uint8_t   > >();
-   boost::python::to_python_converter< Vector3<uint16_t  >, Vector3_to_PythonTuple<uint16_t  > >();
-   boost::python::to_python_converter< Vector3<uint32_t  >, Vector3_to_PythonTuple<uint32_t  > >();
-   boost::python::to_python_converter< Vector3<uint64_t  >, Vector3_to_PythonTuple<uint64_t  > >();
-
-   boost::python::to_python_converter< Vector3<cell_idx_t>, Vector3_to_PythonTuple<cell_idx_t> >();
-
-   // From Python
-   PythonTuple_to_Vector3<bool   >();
-
-   PythonTuple_to_Vector3<real_t   >();
-
-   PythonTuple_to_Vector3<uint8_t  >();
-   PythonTuple_to_Vector3<uint16_t >();
-   PythonTuple_to_Vector3<uint32_t >();
-   PythonTuple_to_Vector3<uint64_t >();
-
-   PythonTuple_to_Vector3<cell_idx_t>();
-}
-
-
-//======================================================================================================================
-//
-//  Cell
-//
-//======================================================================================================================
-
-
-struct Cell_to_PythonTuple
-{
-   static PyObject* convert( Cell const& c )
-   {
-      auto resultTuple = boost::python::make_tuple(c[0], c[1], c[2] );
-      return boost::python::incref ( boost::python::object ( resultTuple ).ptr () );
-   }
-};
-
-struct PythonTuple_to_Cell
-{
-   PythonTuple_to_Cell()
-   {
-     boost::python::converter::registry::push_back(
-       &convertible,
-       &construct,
-       boost::python::type_id< Cell >());
-   }
-
-   static void* convertible( PyObject* obj )
-   {
-      using namespace boost::python;
-
-      if ( ! ( PySequence_Check(obj) && PySequence_Size( obj ) == 3 ))
-         return nullptr;
-
-      object element0 ( handle<>( borrowed( PySequence_GetItem(obj,0) )));
-      object element1 ( handle<>( borrowed( PySequence_GetItem(obj,1) )));
-      object element2 ( handle<>( borrowed( PySequence_GetItem(obj,2) )));
-
-      if (  extract<cell_idx_t>( element0 ).check() &&
-            extract<cell_idx_t>( element1 ).check() &&
-            extract<cell_idx_t>( element2 ).check() )
-         return obj;
-      else
-         return nullptr;
-   }
-
-   static void construct( PyObject* obj, boost::python::converter::rvalue_from_python_stage1_data* data )
-   {
-      using namespace boost::python;
-
-      object element0 ( handle<>( borrowed( PySequence_GetItem(obj,0) )));
-      object element1 ( handle<>( borrowed( PySequence_GetItem(obj,1) )));
-      object element2 ( handle<>( borrowed( PySequence_GetItem(obj,2) )));
-
-
-      // Grab pointer to memory into which to construct the new Vector3
-      void* storage = ( (boost::python::converter::rvalue_from_python_storage<Cell>*) data )->storage.bytes;
-
-      new (storage) Cell( extract<cell_idx_t>( element0 ),
-                          extract<cell_idx_t>( element1 ),
-                          extract<cell_idx_t>( element2 ) );
-
-      // Stash the memory chunk pointer for later use by boost.python
-      data->convertible = storage;
-   }
-};
-
-void exportCell()
-{
-   // To Python
-   boost::python::to_python_converter< Cell, Cell_to_PythonTuple >();
-   // From Python
-   PythonTuple_to_Cell();
-}
-
-//======================================================================================================================
-//
-//  CellInterval
-//
-//======================================================================================================================
-
-
-void cellInterval_setMin( CellInterval & ci, const Cell & min ) {
-   ci.min() = min;
-}
-void cellInterval_setMax( CellInterval & ci, const Cell & max ) {
-   ci.max() = max;
-}
-void cellInterval_shift( CellInterval & ci, cell_idx_t xShift, cell_idx_t yShift, cell_idx_t zShift ) {
-   ci.shift( xShift, yShift, zShift );
-}
-
-boost::python::tuple cellInterval_size( CellInterval & ci ) {
-   return boost::python::make_tuple( ci.xSize(), ci.ySize(), ci.zSize() );
-}
-
-CellInterval cellInterval_getIntersection( CellInterval & ci1, CellInterval & ci2 )
-{
-   CellInterval result ( ci1 );
-   result.intersect( ci2 );
-   return result;
-}
-
-CellInterval cellInterval_getShifted( CellInterval & ci1, cell_idx_t xShift, cell_idx_t yShift, cell_idx_t zShift )
-{
-   CellInterval result ( ci1 );
-   result.shift( xShift, yShift, zShift  );
-   return result;
-}
-
-CellInterval cellInterval_getExpanded1( CellInterval & ci1, cell_idx_t expandVal )
-{
-   CellInterval result ( ci1 );
-   result.expand( expandVal  );
-   return result;
-}
-
-CellInterval cellInterval_getExpanded2( CellInterval & ci1, cell_idx_t xExpand, cell_idx_t yExpand, cell_idx_t zExpand )
-{
-   CellInterval result ( ci1 );
-   result.expand( Cell(xExpand, yExpand, zExpand)  );
-   return result;
-}
-
-void exportCellInterval()
-{
-   const Cell & ( CellInterval::*p_getMin )( ) const = &CellInterval::min;
-   const Cell & ( CellInterval::*p_getMax )( ) const = &CellInterval::max;
-
-   bool ( CellInterval::*p_contains1) ( const Cell         & ) const = &CellInterval::contains;
-   bool ( CellInterval::*p_contains2) ( const CellInterval & ) const = &CellInterval::contains;
-
-   void ( CellInterval::*p_expand1) ( const cell_idx_t ) = &CellInterval::expand;
-   void ( CellInterval::*p_expand2) ( const Cell &     ) = &CellInterval::expand;
-   
-   bool          ( CellInterval::*p_overlaps ) ( const CellInterval & ) const = &CellInterval::overlaps;
-
-   class_<CellInterval>("CellInterval")
-      .def( init<const Cell&, const Cell&>() )
-      .def( init<cell_idx_t, cell_idx_t, cell_idx_t, cell_idx_t, cell_idx_t, cell_idx_t>() )
-      .add_property( "min", make_function( p_getMin, return_value_policy<copy_const_reference>() ), &cellInterval_setMin )
-      .add_property( "max", make_function( p_getMax, return_value_policy<copy_const_reference>() ), &cellInterval_setMax )
-      .add_property( "size", &cellInterval_size  )
-      .def( "empty", &CellInterval::empty )
-      .def( "positiveIndicesOnly", &CellInterval::positiveIndicesOnly )
-      .def( "contains",        p_contains1 )
-      .def( "contains",        p_contains2 )
-      .def( "overlaps",        p_overlaps )
-      .def( "shift",           &cellInterval_shift )
-      .def( "getShifted",      &cellInterval_getShifted )
-      .def( "expand",          p_expand1 )
-      .def( "expand",          p_expand2 )
-      .def( "getExpanded",     &cellInterval_getExpanded1 )
-      .def( "getExpanded",     &cellInterval_getExpanded2 )
-      .def( "intersect",       &CellInterval::intersect )
-      .def( "getIntersection", &cellInterval_getIntersection )
-      .def("__eq__",           &CellInterval::operator==)
-      .def("__ne__",           &CellInterval::operator!=)
-      .add_property( "numCells",  &CellInterval::numCells  )
-      .def( self_ns::str(self) )
-      ;
-}
-
-//======================================================================================================================
-//
-//  AABB
-//
-//======================================================================================================================
-
-
-tuple aabb_getMin( const AABB & domainBB ) {
-   return boost::python::make_tuple( domainBB.xMin(), domainBB.yMin(), domainBB.zMin() );
-}
-tuple aabb_getMax( const AABB & domainBB ) {
-   return boost::python::make_tuple( domainBB.xMax(), domainBB.yMax(), domainBB.zMax() );
-}
-
-void aabb_setMin( AABB & domainBB, object min )
-{
-   checkForThreeSequence(min, "Error assigning minimum of AABB - Sequence of length 3 required" );
-   real_t min0 = extract<real_t>( min[0] );
-   real_t min1 = extract<real_t>( min[1] );
-   real_t min2 = extract<real_t>( min[2] );
-
-   domainBB = AABB( domainBB.max(), AABB::vector_type ( min0, min1, min2 ) );
-}
-
-void aabb_setMax( AABB & domainBB, object max )
-{
-   checkForThreeSequence( max, "Error assigning maximum of AABB - Sequence of length 3 required" );
-
-   real_t max0 = extract<real_t>( max[0] );
-   real_t max1 = extract<real_t>( max[1] );
-   real_t max2 = extract<real_t>( max[2] );
-
-   domainBB = AABB( domainBB.min(), AABB::vector_type ( max0, max1, max2 ) );
-}
-
-
-void exportAABB()
-{
-   bool ( AABB::*p_containsBB  )( const AABB & bb           )     const = &AABB::contains;
-   bool ( AABB::*p_containsVec )( const Vector3<real_t> & point ) const = &AABB::contains;
-
-
-   bool ( AABB::*p_containsClosedInterval1 ) ( const Vector3<real_t> & ) const               = &AABB::containsClosedInterval;
-   bool ( AABB::*p_containsClosedInterval2 ) ( const Vector3<real_t> &, const real_t ) const = &AABB::containsClosedInterval;
-
-   AABB ( AABB::*p_getExtended1 ) ( const real_t            ) const = &AABB::getExtended;
-   AABB ( AABB::*p_getExtended2 ) ( const Vector3<real_t> & ) const = &AABB::getExtended;
-
-   AABB ( AABB::*p_getScaled1 ) ( const real_t            ) const = &AABB::getScaled;
-   AABB ( AABB::*p_getScaled2 ) ( const Vector3<real_t> & ) const = &AABB::getScaled;
-
-   AABB ( AABB::*p_getMerged1 ) ( const Vector3<real_t> & ) const = &AABB::getMerged;
-   AABB ( AABB::*p_getMerged2 ) ( const AABB            & ) const = &AABB::getMerged;
-
-   bool ( AABB::*p_intersects1  )( const AABB & bb            ) const = &AABB::intersects;
-   bool ( AABB::*p_intersects2  )( const AABB & bb, real_t dx ) const = &AABB::intersects;
-
-   bool ( AABB::*p_intersectsClosed1  )( const AABB & bb            ) const = &AABB::intersectsClosedInterval;
-   bool ( AABB::*p_intersectsClosed2  )( const AABB & bb, real_t dx ) const = &AABB::intersectsClosedInterval;
-
-   void ( AABB::*p_extend1 ) ( const real_t            ) = &AABB::extend;
-   void ( AABB::*p_extend2 ) ( const Vector3<real_t> & ) = &AABB::extend;
-
-   void  ( AABB::*p_scale1 ) ( const real_t            )  = &AABB::scale;
-   void  ( AABB::*p_scale2 ) ( const Vector3<real_t> & )  = &AABB::scale;
-
-   void  ( AABB::*p_merge1 ) ( const Vector3<real_t> & )  = &AABB::merge;
-   void  ( AABB::*p_merge2 ) ( const AABB            & )  = &AABB::merge;
-
-   real_t  ( AABB::*p_sqDistance1 ) ( const AABB & )            const = &AABB::sqDistance;
-   real_t  ( AABB::*p_sqDistance2 ) ( const Vector3<real_t> & ) const = &AABB::sqDistance;
-
-   real_t  ( AABB::*p_sqMaxDistance1 ) ( const AABB & )            const = &AABB::sqMaxDistance;
-   real_t  ( AABB::*p_sqMaxDistance2 ) ( const Vector3<real_t> & ) const = &AABB::sqMaxDistance;
-
-
-   class_<AABB>("AABB")
-      .def( init<real_t,real_t,real_t,real_t,real_t,real_t>() )
-      .def( init<Vector3<real_t>,Vector3<real_t> >() )
-      .def("__eq__",     &walberla::math::operator==<real_t, real_t > )
-      .def("__ne__",     &walberla::math::operator!=<real_t, real_t > )
-      .add_property( "min",  &aabb_getMin, &aabb_setMin )
-      .add_property( "max",  &aabb_getMax, &aabb_setMax )
-      .add_property( "size", &AABB::sizes )
-      .def( "empty",    &AABB::empty )
-      .def( "volume", &AABB::volume )
-      .def( "center", &AABB::center )
-      .def( "contains", p_containsBB )
-      .def( "contains", p_containsVec )
-      .def( "containsClosedInterval", p_containsClosedInterval1 )
-      .def( "containsClosedInterval", p_containsClosedInterval2 )
-      .def( "getExtended", p_getExtended1 )
-      .def( "getExtended", p_getExtended2 )
-      .def( "getTranslated", &AABB::getTranslated )
-      .def( "getScaled", p_getScaled1 )
-      .def( "getScaled", p_getScaled2 )
-      .def( "getMerged", p_getMerged1 )
-      .def( "getMerged", p_getMerged2 )
-      .def( "intersects", p_intersects1 )
-      .def( "intersects", p_intersects2 )
-      .def( "intersectsClosedInterval", p_intersectsClosed1 )
-      .def( "intersectsClosedInterval", p_intersectsClosed2 )
-      .def( "intersectionVolume", &AABB::intersectionVolume )
-      .def( "getIntersection", &AABB::getIntersection )
-      .def( "isIdentical", &AABB::isIdentical )
-      .def( "isEqual",     &AABB::isEqual )
-      .def( "sqDistance",       p_sqDistance1 )
-      .def( "sqDistance",       p_sqDistance2 )
-      .def( "sqMaxDistance",    p_sqMaxDistance1 )
-      .def( "sqMaxDistance",    p_sqMaxDistance2 )
-      .def( "sqSignedDistance", &AABB::sqSignedDistance)
-      .def( "distance"        , &AABB::distance)
-      .def( "signedDistance"  , &AABB::signedDistance)
-      .def( "maxDistance"     , &AABB::maxDistance)
-      .def( "extend", p_extend1 )
-      .def( "extend", p_extend2 )
-      .def( "translate", &AABB::translate )
-      .def( "scale", p_scale1 )
-      .def( "scale", p_scale2 )
-      .def( "merge", p_merge1 )
-      .def( "merge", p_merge2 )
-      .def( "intersect", &AABB::intersect )
-      .def( self_ns::str(self) )
-      ;
-}
-
-//======================================================================================================================
-//
-//  Timing
-//
-//======================================================================================================================
-
-dict buildDictFromTimingNode(const WcTimingNode & tn)
-{
-   dict result;
-
-   result["all"] = tn.timer_;
-   for ( auto it = tn.tree_.begin(); it != tn.tree_.end(); ++it)
-   {
-      if (it->second.tree_.empty())
-      {
-         result[it->first] = it->second.timer_;
-      } else
-      {
-         result[it->first] = buildDictFromTimingNode(it->second);
-      }
-   }
-
-   return result;
-}
-
-dict buildDictFromTimingTree(const WcTimingTree & tt)
-{
-   return buildDictFromTimingNode( tt.getRawData() );
-}
-
-void timingTreeStopWrapper(WcTimingTree & tt, const std::string& name)
-{
-   if (!tt.isTimerRunning(name))
-   {
-      PyErr_SetString( PyExc_ValueError, ("Timer '" + name + "' is currently not running!").c_str() );
-      throw error_already_set();
-   }
-   tt.stop(name);
-}
-
-void exportTiming()
-{
-   class_<WcTimer> ("Timer")
-      .def( init<>() )
-      .def( "start",  &WcTimer::start )
-      .def( "stop",   &WcTimer::end   )
-      .def( "reset",  &WcTimer::reset )
-      .def( "merge",  &WcTimer::merge )
-      .add_property( "counter",      &WcTimer::getCounter   )
-      .add_property( "total",        &WcTimer::total        )
-      .add_property( "sumOfSquares", &WcTimer::sumOfSquares )
-      .add_property( "average",      &WcTimer::average      )
-      .add_property( "variance",     &WcTimer::variance     )
-      .add_property( "min",          &WcTimer::min          )
-      .add_property( "max",          &WcTimer::max          )
-      .add_property( "last",         &WcTimer::last         )
-      ;
-
-
-   WcTimer & ( WcTimingPool::*pGetItem ) ( const std::string & ) = &WcTimingPool::operator[];
-
-   {
-      scope classScope =
-      class_<WcTimingPool, shared_ptr<WcTimingPool> > ("TimingPool")
-         .def( init<>() )
-         .def( self_ns::str(self) )
-         .def( "__getitem__",     pGetItem, return_internal_reference<1>() )
-         .def( "__contains__",    &WcTimingPool::timerExists )
-         .def( "getReduced",      &WcTimingPool::getReduced,  ( arg("targetRank") = 0) )
-         .def( "merge",           &WcTimingPool::merge, ( arg("mergeDuplicates") = true) )
-         .def( "clear",           &WcTimingPool::clear )
-         .def( "unifyRegisteredTimersAcrossProcesses", &WcTimingPool::unifyRegisteredTimersAcrossProcesses )
-         .def( "logResultOnRoot", &WcTimingPool::logResultOnRoot, (arg("unifyRegisteredTimers") = false) )
-         .def( self_ns::str(self) )
-         ;
-
-      enum_<timing::ReduceType>("ReduceType")
-          .value("min"  , timing::REDUCE_MIN)
-          .value("avg"  , timing::REDUCE_AVG)
-          .value("max"  , timing::REDUCE_MAX)
-          .value("total", timing::REDUCE_TOTAL)
-          .export_values()
-          ;
-   }
-
-   const WcTimer & ( WcTimingTree::*pTimingTreeGet ) ( const std::string & ) const = &WcTimingTree::operator[];
-   class_<WcTimingTree, shared_ptr<WcTimingTree> > ("TimingTree")
-         .def( init<>() )
-         .def( "__getitem__",  pTimingTreeGet, return_internal_reference<1>() )
-         .def( "start",        &WcTimingTree::start )
-         .def( "stop",         &timingTreeStopWrapper )
-         .def( "getReduced",   &WcTimingTree::getReduced )
-         .def( "toDict",       &buildDictFromTimingTree )
-         .def( self_ns::str(self) )
-    ;
-
-#if BOOST_VERSION < 106300
-   exportSharedPtr<WcTimingTree>();
-#endif
-
-}
-
-
-
-
-//======================================================================================================================
-//
-//  IBlock
-//
-//======================================================================================================================
-
-boost::python::object IBlock_getData( boost::python::object iblockObject, const std::string & stringID ) //NOLINT
-{
-   IBlock * block = boost::python::extract<IBlock*>( iblockObject );
-
-   //typedef std::pair< IBlock *, std::string > BlockStringPair;
-   //static std::map< BlockStringPair, object > cache;
-
-   //auto blockStringPair = std::make_pair( &block, stringID );
-   //auto it = cache.find( blockStringPair );
-   //if ( it != cache.end() )
-   //   return it->second;
-
-   BlockDataID id = blockDataIDFromString( *block, stringID );
-
-   auto manager = python_coupling::Manager::instance();
-   boost::python::object res =  manager->pythonObjectFromBlockData( *block, id );
-
-   if ( res == boost::python::object() )
-      throw BlockDataNotConvertible();
-
-   boost::python::objects::make_nurse_and_patient( res.ptr(), iblockObject.ptr() );
-
-   // write result to cache
-   //cache[blockStringPair] = res;
-   //TODO cache has bugs when cache is destroyed, probably since objects are freed after py_finalize is called
-   //move cache to Manager?
-
-   return res;
-}
-
-
-boost::python::list IBlock_blockDataList( boost::python::object iblockObject ) //NOLINT
-{
-   IBlock * block = boost::python::extract<IBlock*>( iblockObject );
-
-   const std::vector<std::string> & stringIds = block->getBlockStorage().getBlockDataIdentifiers();
-
-   boost::python::list resultList;
-
-   for( auto it = stringIds.begin(); it != stringIds.end(); ++it ) {
-      try {
-         resultList.append( boost::python::make_tuple( *it, IBlock_getData( iblockObject, *it) ) );
-      }
-      catch( BlockDataNotConvertible & /*e*/ ) {
-      }
-   }
-
-   return resultList;
-}
-
-boost::python::object IBlock_iter(  boost::python::object iblockObject )
-{
-   boost::python::list resultList = IBlock_blockDataList( iblockObject ); //NOLINT
-   return resultList.attr("__iter__");
-}
-
-boost::python::tuple IBlock_atDomainMinBorder( IBlock & block )
-{
-   return boost::python::make_tuple( block.getBlockStorage().atDomainXMinBorder(block),
-                                     block.getBlockStorage().atDomainYMinBorder(block),
-                                     block.getBlockStorage().atDomainZMinBorder(block) );
-}
-
-boost::python::tuple IBlock_atDomainMaxBorder( IBlock & block )
-{
-   return boost::python::make_tuple( block.getBlockStorage().atDomainXMaxBorder(block),
-                                     block.getBlockStorage().atDomainYMaxBorder(block),
-                                     block.getBlockStorage().atDomainZMaxBorder(block) );
-}
-
-IBlockID::IDType IBlock_getIntegerID( IBlock & block )
-{
-   return block.getId().getID();
-}
-
-bool IBlock_equals( IBlock & block1, IBlock & block2 )
-{
-   return block1.getId() == block2.getId();
-}
-
-std::string IBlock_str( IBlock & b ) {
-   std::stringstream out;
-   out <<  "Block at " << b.getAABB();
-   return out.str();
-
-}
-
-void exportIBlock()
-{
-   register_exception_translator<NoSuchBlockData>( & NoSuchBlockData::translate );
-   register_exception_translator<BlockDataNotConvertible>( & BlockDataNotConvertible::translate );
-
-   class_<IBlock, boost::noncopyable> ("Block", no_init)
-         .def         ( "__getitem__",       &IBlock_getData )
-         .add_property( "atDomainMinBorder", &IBlock_atDomainMinBorder )
-         .add_property( "atDomainMaxBorder", &IBlock_atDomainMaxBorder )
-         .add_property( "items",             &IBlock_blockDataList)
-         .add_property( "id",                &IBlock_getIntegerID)
-         .def         ( "__hash__",          &IBlock_getIntegerID)
-         .def         ( "__eq__",            &IBlock_equals)
-         .def         ( "__repr__",          &IBlock_str )
-         .add_property( "__iter__",          &IBlock_iter  )
-         .add_property("aabb", make_function(&IBlock::getAABB, return_value_policy<copy_const_reference>()))
-         ;
-
-}
-
-//======================================================================================================================
-//
-//  Logging & Abort
-//
-//======================================================================================================================
-
-
-static void wlb_log_devel              ( const std::string & msg ) { WALBERLA_LOG_DEVEL          ( msg ); }
-static void wlb_log_devel_on_root      ( const std::string & msg ) { WALBERLA_LOG_DEVEL_ON_ROOT  ( msg ); }
-
-static void wlb_log_result             ( const std::string & msg ) { WALBERLA_LOG_RESULT         ( msg ); }
-static void wlb_log_result_on_root     ( const std::string & msg ) { WALBERLA_LOG_RESULT_ON_ROOT ( msg ); }
-
-static void wlb_log_warning            ( const std::string & msg ) { WALBERLA_LOG_WARNING         ( msg ); }
-static void wlb_log_warning_on_root    ( const std::string & msg ) { WALBERLA_LOG_WARNING_ON_ROOT ( msg ); }
-
-#ifdef WALBERLA_LOGLEVEL_INFO
-static void wlb_log_info               ( const std::string & msg ) { WALBERLA_LOG_INFO            ( msg ); }
-static void wlb_log_info_on_root       ( const std::string & msg ) { WALBERLA_LOG_INFO_ON_ROOT    ( msg ); }
-#else
-static void wlb_log_info               ( const std::string & ) {}
-static void wlb_log_info_on_root       ( const std::string & ) {}
-#endif
-
-#ifdef WALBERLA_LOGLEVEL_PROGRESS
-static void wlb_log_progress           ( const std::string & msg ) { WALBERLA_LOG_PROGRESS        ( msg ); }
-static void wlb_log_progress_on_root   ( const std::string & msg ) { WALBERLA_LOG_PROGRESS_ON_ROOT( msg ); }
-#else
-static void wlb_log_progress           ( const std::string & ) {}
-static void wlb_log_progress_on_root   ( const std::string & ) {}
-#endif
-
-#ifdef WALBERLA_LOGLEVEL_DETAIL
-static void wlb_log_detail             ( const std::string & msg ) { WALBERLA_LOG_DETAIL          ( msg ); }
-static void wlb_log_detail_on_root     ( const std::string & msg ) { WALBERLA_LOG_DETAIL_ON_ROOT  ( msg ); }
-#else
-static void wlb_log_detail             ( const std::string & ) {}
-static void wlb_log_detail_on_root     ( const std::string & ) {}
-#endif
-
-static void wlb_abort                  ( const std::string & msg ) { WALBERLA_ABORT_NO_DEBUG_INFO ( msg ); }
-
-void exportLogging()
-{
-   def ( "log_devel"         ,  wlb_log_devel           );
-   def ( "log_devel_on_root" ,  wlb_log_devel_on_root   );
-   def ( "log_result",          wlb_log_result          );
-   def ( "log_result_on_root",  wlb_log_result_on_root  );
-   def ( "log_warning",         wlb_log_warning         );
-   def ( "log_warning_on_root", wlb_log_warning_on_root );
-   def ( "log_info",            wlb_log_info            );
-   def ( "log_info_on_root",    wlb_log_info_on_root    );
-   def ( "log_progress",        wlb_log_progress        );
-   def ( "log_progress_on_root",wlb_log_progress_on_root);
-   def ( "log_detail",          wlb_log_detail          );
-   def ( "log_detail_on_root",  wlb_log_detail_on_root  );
-
-   def ( "abort", wlb_abort );
-}
-
-
-
-
-//======================================================================================================================
-//
-//  StructuredBlockStorage
-//
-//======================================================================================================================
-
-
-object * blockDataCreationHelper( IBlock * block, StructuredBlockStorage * bs,  object callable ) //NOLINT
-{
-   object * res = new object( callable( ptr(block), ptr(bs) ) );
-   return res;
-}
-
-uint_t StructuredBlockStorage_addBlockData( StructuredBlockStorage & s, const std::string & name, object functionPtr ) //NOLINT
-{
-   BlockDataID res = s.addStructuredBlockData(name)
-               << StructuredBlockDataCreator<object>( std::bind( &blockDataCreationHelper, std::placeholders::_1, std::placeholders::_2, functionPtr ) );
-   //TODO extend this for moving block data ( packing und unpacking with pickle )
-   return res;
-}
-
-// Helper function for iteration over StructuredBlockStorage
-// boost::python comes with iteration helpers but non of this worked:
-//    .def("__iter__"   range(&StructuredBlockStorage::begin, &StructuredBlockStorage::end))
-//    .def("__iter__",  range<return_value_policy<copy_non_const_reference> >( beginPtr, endPtr) )
-boost::python::object StructuredBlockStorage_iter( boost::python::object structuredBlockStorage ) //NOLINT
-{
-   shared_ptr<StructuredBlockStorage> s = extract< shared_ptr<StructuredBlockStorage> > ( structuredBlockStorage );
-
-   std::vector< const IBlock* > blocks;
-   s->getBlocks( blocks );
-   boost::python::list resultList;
-
-   for( auto it = blocks.begin(); it != blocks.end(); ++it ) {
-      boost::python::object theObject( ptr( *it ) );
-      // Prevent blockstorage from being destroyed when references to blocks exist
-      boost::python::objects::make_nurse_and_patient( theObject.ptr(), structuredBlockStorage.ptr() );
-      resultList.append( theObject );
-   }
-
-   return resultList.attr("__iter__");
-}
-
-
-boost::python::object StructuredBlockStorage_getItem( boost::python::object structuredBlockStorage, uint_t i ) //NOLINT
-{
-   shared_ptr<StructuredBlockStorage> s = extract< shared_ptr<StructuredBlockStorage> > ( structuredBlockStorage );
-
-   if ( i >= s->size() )
-   {
-      PyErr_SetString( PyExc_RuntimeError, "Index out of bounds");
-      throw error_already_set();
-   }
-
-   std::vector< const IBlock* > blocks;
-   s->getBlocks( blocks );
-
-   boost::python::object theObject( ptr( blocks[i] ) );
-   boost::python::objects::make_nurse_and_patient( theObject.ptr(), structuredBlockStorage.ptr() );
-   return theObject;
-}
-
-boost::python::list StructuredBlockStorage_blocksOverlappedByAABB( StructuredBlockStorage & s, const AABB & aabb ) {
-   std::vector< IBlock*> blocks;
-   s.getBlocksOverlappedByAABB( blocks, aabb );
-
-   boost::python::list resultList;
-   for( auto it = blocks.begin(); it != blocks.end(); ++it )
-      resultList.append( ptr( *it ) );
-   return resultList;
-}
-
-
-boost::python::list StructuredBlockStorage_blocksContainedWithinAABB( StructuredBlockStorage & s, const AABB & aabb ) {
-   std::vector< IBlock*> blocks;
-   s.getBlocksContainedWithinAABB( blocks, aabb );
-
-   boost::python::list resultList;
-   for( auto it = blocks.begin(); it != blocks.end(); ++it )
-      resultList.append( ptr( *it ) );
-   return resultList;
-}
-
-
-object SbS_transformGlobalToLocal ( StructuredBlockStorage & s, IBlock & block, const object & global )
-{
-   if ( extract<CellInterval>( global ).check() )
-   {
-      CellInterval ret;
-      s.transformGlobalToBlockLocalCellInterval( ret, block, extract<CellInterval>( global ) );
-      return object( ret );
-   }
-   else if ( extract<Cell>( global ).check() )
-   {
-      Cell ret;
-      s.transformGlobalToBlockLocalCell( ret, block, extract<Cell>( global ) );
-      return object( ret );
-   }
-
-   PyErr_SetString(PyExc_RuntimeError, "Only CellIntervals and cells can be transformed" );
-   throw error_already_set();
-}
-
-
-object SbS_transformLocalToGlobal ( StructuredBlockStorage & s, IBlock & block, const object & local )
-{
-   if ( extract<CellInterval>( local ).check() )
-   {
-      CellInterval ret;
-      s.transformBlockLocalToGlobalCellInterval( ret, block, extract<CellInterval>( local ) );
-      return object( ret );
-   }
-   else if ( extract<Cell>( local ).check() )
-   {
-      Cell ret;
-      s.transformBlockLocalToGlobalCell( ret, block, extract<Cell>( local ) );
-      return object( ret );
-   }
-   PyErr_SetString(PyExc_RuntimeError, "Only CellIntervals and cells can be transformed" );
-   throw error_already_set();
-}
-
-void SbS_writeBlockData( StructuredBlockStorage & s,const std::string & blockDataId, const std::string & file )
-{
-   mpi::SendBuffer buffer;
-   s.serializeBlockData( blockDataIDFromString(s, blockDataId), buffer);
-   mpi::writeMPIIO(file, buffer);
-}
-
-void SbS_readBlockData( StructuredBlockStorage & s,const std::string & blockDataId, const std::string & file )
-{
-   mpi::RecvBuffer buffer;
-   mpi::readMPIIO(file, buffer);
-
-   s.deserializeBlockData( blockDataIDFromString(s, blockDataId), buffer );
-   if ( ! buffer.isEmpty() ) {
-      PyErr_SetString(PyExc_RuntimeError, "Reading failed - file does not contain matching data for this type." );
-      throw error_already_set();
-   }
-}
-
-CellInterval SbS_getBlockCellBB( StructuredBlockStorage & s, const IBlock * block )
-{
-   return s.getBlockCellBB( *block );
-}
-
-
-Vector3<real_t> SbS_mapToPeriodicDomain1 ( StructuredBlockStorage & s, real_t x, real_t y, real_t z )
-{
-   Vector3<real_t> res ( x,y,z );
-   s.mapToPeriodicDomain( res );
-   return res;
-}
-Vector3<real_t> SbS_mapToPeriodicDomain2 ( StructuredBlockStorage & s, Vector3<real_t> in )
-{
-   s.mapToPeriodicDomain( in );
-   return in;
-}
-Cell SbS_mapToPeriodicDomain3 ( StructuredBlockStorage & s, Cell in, uint_t level = 0 )
-{
-   s.mapToPeriodicDomain( in, level );
-   return in;
-}
-
-object SbS_getBlock1 ( StructuredBlockStorage & s, const real_t x , const real_t y , const real_t z ) {
-   return object( ptr( s.getBlock( x,y,z ) ) );
-
-}
-
-object SbS_getBlock2 ( StructuredBlockStorage & s, const Vector3<real_t> & v ) {
-   return object( ptr( s.getBlock( v ) ) );
-}
-
-
-tuple SbS_periodic(  StructuredBlockStorage & s )
-{
-   return make_tuple( s.isXPeriodic(), s.isYPeriodic(), s.isZPeriodic() );
-}
-
-bool SbS_atDomainXMinBorder( StructuredBlockStorage & s, const IBlock * b ) { return s.atDomainXMinBorder( *b ); }
-bool SbS_atDomainXMaxBorder( StructuredBlockStorage & s, const IBlock * b ) { return s.atDomainXMaxBorder( *b ); }
-bool SbS_atDomainYMinBorder( StructuredBlockStorage & s, const IBlock * b ) { return s.atDomainYMinBorder( *b ); }
-bool SbS_atDomainYMaxBorder( StructuredBlockStorage & s, const IBlock * b ) { return s.atDomainYMaxBorder( *b ); }
-bool SbS_atDomainZMinBorder( StructuredBlockStorage & s, const IBlock * b ) { return s.atDomainZMinBorder( *b ); }
-bool SbS_atDomainZMaxBorder( StructuredBlockStorage & s, const IBlock * b ) { return s.atDomainZMaxBorder( *b ); }
-
-void exportStructuredBlockStorage()
-{
-   bool ( StructuredBlockStorage::*p_blockExists1         ) ( const Vector3< real_t > & ) const = &StructuredBlockStorage::blockExists;
-   bool ( StructuredBlockStorage::*p_blockExistsLocally1  ) ( const Vector3< real_t > & ) const = &StructuredBlockStorage::blockExistsLocally;
-   bool ( StructuredBlockStorage::*p_blockExistsRemotely1 ) ( const Vector3< real_t > & ) const = &StructuredBlockStorage::blockExistsRemotely;
-
-   bool ( StructuredBlockStorage::*p_blockExists2         ) ( const real_t, const real_t, const real_t ) const = &StructuredBlockStorage::blockExists;
-   bool ( StructuredBlockStorage::*p_blockExistsLocally2  ) ( const real_t, const real_t, const real_t ) const = &StructuredBlockStorage::blockExistsLocally;
-   bool ( StructuredBlockStorage::*p_blockExistsRemotely2 ) ( const real_t, const real_t, const real_t ) const = &StructuredBlockStorage::blockExistsRemotely;
-
-   class_<StructuredBlockStorage, shared_ptr<StructuredBlockStorage>, boost::noncopyable>("StructuredBlockStorage", no_init )
-       .def( "getNumberOfLevels",                       &StructuredBlockStorage::getNumberOfLevels )
-       .def( "getDomain",                               &StructuredBlockStorage::getDomain, return_internal_reference<1>() )
-       .def( "mapToPeriodicDomain",                     &SbS_mapToPeriodicDomain1                                 )
-       .def( "mapToPeriodicDomain",                     &SbS_mapToPeriodicDomain2                                 )
-       .def( "mapToPeriodicDomain",                     &SbS_mapToPeriodicDomain3, (arg("level") = 0 )            )
-       .def( "addBlockData",                            &StructuredBlockStorage_addBlockData                      )
-       .def( "__getitem__",                             &StructuredBlockStorage_getItem                           )
-       .def( "__len__",                                 &StructuredBlockStorage::size                             )
-       .def( "getBlock",                                SbS_getBlock1                                             )
-       .def( "getBlock",                                SbS_getBlock2                                             )
-       .def( "containsGlobalBlockInformation",          &StructuredBlockStorage::containsGlobalBlockInformation   )
-       .def( "blocksOverlappedByAABB" ,                 &StructuredBlockStorage_blocksOverlappedByAABB            )
-       .def( "blocksContainedWithinAABB",               &StructuredBlockStorage_blocksContainedWithinAABB         )
-       .def( "blockExists",                             p_blockExists1                                            )
-       .def( "blockExists",                             p_blockExists2                                            )
-       .def( "blockExistsLocally",                      p_blockExistsLocally1                                     )
-       .def( "blockExistsLocally",                      p_blockExistsLocally2                                     )
-       .def( "blockExistsRemotely",                     p_blockExistsRemotely1                                    )
-       .def( "blockExistsRemotely",                     p_blockExistsRemotely2                                    )
-       .def( "atDomainXMinBorder",                      &SbS_atDomainXMinBorder                                   )
-       .def( "atDomainXMaxBorder",                      &SbS_atDomainXMaxBorder                                   )
-       .def( "atDomainYMinBorder",                      &SbS_atDomainYMinBorder                                   )
-       .def( "atDomainYMaxBorder",                      &SbS_atDomainYMaxBorder                                   )
-       .def( "atDomainZMinBorder",                      &SbS_atDomainZMinBorder                                   )
-       .def( "atDomainZMaxBorder",                      &SbS_atDomainZMaxBorder                                   )
-       .def( "dx",                                      &StructuredBlockStorage::dx, ( args("level")=0 )          )
-       .def( "dy",                                      &StructuredBlockStorage::dy, ( args("level")=0 )          )
-       .def( "dz",                                      &StructuredBlockStorage::dz, ( args("level")=0 )          )
-       .def( "getDomainCellBB",                         &StructuredBlockStorage::getDomainCellBB, return_value_policy<copy_const_reference>(),  ( args( "level") = 0 )  )
-       .def( "getBlockCellBB",                          &SbS_getBlockCellBB  )
-       .def( "transformGlobalToLocal",                  &SbS_transformGlobalToLocal )
-       .def( "transformLocalToGlobal",                  &SbS_transformLocalToGlobal )
-       .def( "writeBlockData",                          &SbS_writeBlockData )
-       .def( "readBlockData",                           &SbS_readBlockData )
-       .add_property("__iter__",                        &StructuredBlockStorage_iter                            )
-       .add_property( "containsGlobalBlockInformation", &StructuredBlockStorage::containsGlobalBlockInformation )
-       .add_property( "periodic",                       &SbS_periodic )
-       ;
-
-#if BOOST_VERSION < 106300
-   exportSharedPtr<StructuredBlockStorage>();
-#endif
-}
-
-//======================================================================================================================
-//
-//  Communication
-//
-//======================================================================================================================
-
-void exportCommunication()
-{
-   using communication::UniformPackInfo;
-   class_< UniformPackInfo, shared_ptr<UniformPackInfo>, boost::noncopyable> //NOLINT
-      ( "UniformPackInfo", no_init );
-
-   using communication::UniformMPIDatatypeInfo;
-   class_< UniformMPIDatatypeInfo, shared_ptr<UniformMPIDatatypeInfo>, boost::noncopyable>
-      ( "UniformMPIDatatypeInfo", no_init );
-
-#if BOOST_VERSION < 106300
-   exportSharedPtr<UniformPackInfo>();
-   exportSharedPtr<UniformMPIDatatypeInfo>();
-#endif
-}
-
-//======================================================================================================================
-//
-//  Stencil Directions
-//
-//======================================================================================================================
-
-void exportStencilDirections()
-{
-   ModuleScope build_info( "stencil");
-
-   enum_<stencil::Direction>("Direction")
-       .value("C"  , stencil::C  )
-       .value("N"  , stencil::N  )
-       .value("S"  , stencil::S  )
-       .value("W"  , stencil::W  )
-       .value("E"  , stencil::E  )
-       .value("T"  , stencil::T  )
-       .value("B"  , stencil::B  )
-       .value("NW" , stencil::NW )
-       .value("NE" , stencil::NE )
-       .value("SW" , stencil::SW )
-       .value("SE" , stencil::SE )
-       .value("TN" , stencil::TN )
-       .value("TS" , stencil::TS )
-       .value("TW" , stencil::TW )
-       .value("TE" , stencil::TE )
-       .value("BN" , stencil::BN )
-       .value("BS" , stencil::BS )
-       .value("BW" , stencil::BW )
-       .value("BE" , stencil::BE )
-       .value("TNE", stencil::TNE)
-       .value("TNW", stencil::TNW)
-       .value("TSE", stencil::TSE)
-       .value("TSW", stencil::TSW)
-       .value("BNE", stencil::BNE)
-       .value("BNW", stencil::BNW)
-       .value("BSE", stencil::BSE)
-       .value("BSW", stencil::BSW)
-       .export_values()
-       ;
-   boost::python::list cx;
-
-   boost::python::list cy;
-
-   boost::python::list cz;
-
-   boost::python::list dirStrings;
-   for( uint_t i=0; i < stencil::NR_OF_DIRECTIONS; ++i  )
-   {
-      cx.append( stencil::cx[i] );
-      cy.append( stencil::cy[i] );
-      cz.append( stencil::cz[i] );
-      dirStrings.append( stencil::dirToString[i] );
-   }
-   boost::python::list c;
-   c.append( cx );
-   c.append( cy );
-   c.append( cz );
-
-   using boost::python::scope;
-   scope().attr("cx") = cx;
-   scope().attr("cy") = cy;
-   scope().attr("cz") = cz;
-   scope().attr("c") = c;
-   scope().attr("dirStrings") = dirStrings;
-}
-
-
-//======================================================================================================================
-//
-//  Build Info
-//
-//======================================================================================================================
-
-
-void exportBuildInfo()
-{
-   ModuleScope build_info( "build_info");
-   using boost::python::scope;
-   scope().attr("version")         = WALBERLA_GIT_SHA1;
-   scope().attr("type" )           = WALBERLA_BUILD_TYPE;
-   scope().attr("compiler_flags" ) = WALBERLA_COMPILER_FLAGS;
-   scope().attr("build_machine" )  = WALBERLA_BUILD_MACHINE;
-   scope().attr("source_dir")      = WALBERLA_SOURCE_DIR;
-   scope().attr("build_dir")       = WALBERLA_BUILD_DIR;
-}
-
-
-
-
-void exportBasicWalberlaDatastructures()
-{
-
-   NumpyIntConversion<uint8_t>();
-   NumpyIntConversion<int32_t>();
-   NumpyIntConversion<int64_t>();
-   NumpyIntConversion<uint32_t>();
-   NumpyIntConversion<uint64_t>();
-   NumpyIntConversion<size_t>();
-   NumpyIntConversion<bool>();
-   NumpyFloatConversion<float>();
-   NumpyFloatConversion<double>();
-   NumpyFloatConversion<long double>();
-
-
-   exportMPI();
-
-   exportBuildInfo();
-   exportVector3();
-   exportCell();
-   exportCellInterval();
-   exportAABB();
-
-   exportTiming();
-
-   exportIBlock();
-   exportStructuredBlockStorage();
-   exportCommunication();
-
-   exportLogging();
-   exportStencilDirections();
-
-   // Add empty callbacks module
-   object callbackModule( handle<>( borrowed(PyImport_AddModule("walberla_cpp.callbacks"))));
-   scope().attr("callbacks") = callbackModule;
-
-}
-
-} // namespace python_coupling
-} // namespace walberla
-
-#endif
-
diff --git a/src/python_coupling/basic_exports/MPIExport.cpp b/src/python_coupling/basic_exports/MPIExport.cpp
deleted file mode 100644
index 5811cbab58a6a6497984546d7f19dbe3741e1031..0000000000000000000000000000000000000000
--- a/src/python_coupling/basic_exports/MPIExport.cpp
+++ /dev/null
@@ -1,266 +0,0 @@
-#include "python_coupling/PythonWrapper.h"
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-#include "python_coupling/helper/PythonIterableToStdVector.h"
-
-#include "core/mpi/MPIManager.h"
-#include "core/mpi/Reduce.h"
-#include "core/mpi/Gather.h"
-#include "core/mpi/Broadcast.h"
-
-#include <vector>
-
-using namespace boost::python;
-
-
-
-
-namespace walberla {
-namespace python_coupling {
-
-   typedef std::vector<int64_t>      IntStdVector;
-   typedef std::vector<real_t>       RealStdVector;
-   typedef std::vector<std::string>  StringStdVector;
-
-
-   //===================================================================================================================
-   //
-   //  MPIManager
-   //
-   //===================================================================================================================
-
-
-   static int  rank()              { return MPIManager::instance()->rank();               }
-   static int  worldRank()         { return MPIManager::instance()->worldRank();          }
-   static int  numProcesses()      { return MPIManager::instance()->numProcesses();       }
-   static bool hasCartesianSetup() { return MPIManager::instance()->hasCartesianSetup();  }
-   static bool rankValid()         { return MPIManager::instance()->rankValid();          }
-
-
-
-   //===================================================================================================================
-   //
-   //  Broadcast
-   //
-   //===================================================================================================================
-
-   static object broadcast_string( object value, int sendRank ) //NOLINT
-   {
-      if ( extract<std::string>(value).check() )
-      {
-         std::string extractedValue = extract< std::string >(value);
-         mpi::broadcastObject( extractedValue , sendRank );
-         return object( extractedValue );
-      }
-      StringStdVector extractedValue = pythonIterableToStdVector< StringStdVector::value_type >( value );
-      mpi::broadcastObject( extractedValue, sendRank );
-      return object( extractedValue );
-   }
-
-   static object broadcast_int( object value, int sendRank ) //NOLINT
-   {
-      if ( extract<int64_t>(value).check() )
-      {
-         int64_t extractedValue = extract< int64_t >(value);
-         mpi::broadcastObject( extractedValue , sendRank );
-         return object( extractedValue );
-      }
-      IntStdVector extractedValue = pythonIterableToStdVector< IntStdVector::value_type >( value );
-      mpi::broadcastObject( extractedValue, sendRank );
-      return object( extractedValue );
-   }
-
-   static object broadcast_real( object value, int sendRank ) //NOLINT
-   {
-      if ( extract<real_t>(value).check() )
-      {
-         real_t extractedValue = extract< real_t  >(value);
-         mpi::broadcastObject( extractedValue , sendRank);
-         return object( extractedValue );
-      }
-      RealStdVector extractedValue = pythonIterableToStdVector< RealStdVector::value_type >( value );
-      mpi::broadcastObject( extractedValue , sendRank);
-      return object( extractedValue );
-   }
-
-
-   //===================================================================================================================
-   //
-   //  Reduce
-   //
-   //===================================================================================================================
-
-
-   static object reduce_int( object value, mpi::Operation op, int recvRank ) //NOLINT
-   {
-      if ( extract<int64_t>(value).check() )
-      {
-         int64_t extractedValue = extract< int64_t >(value);
-         mpi::reduceInplace( extractedValue , op, recvRank );
-         return object( extractedValue );
-      }
-      IntStdVector extractedValue = pythonIterableToStdVector< IntStdVector::value_type >( value );
-      mpi::reduceInplace( extractedValue, op, recvRank );
-      return object( extractedValue );
-   }
-
-   static object reduce_real( object value, mpi::Operation op, int recvRank ) //NOLINT
-   {
-      if ( extract<real_t>(value).check() )
-      {
-         real_t extractedValue = extract< real_t  >(value);
-         mpi::reduceInplace( extractedValue , op, recvRank);
-         return object( extractedValue );
-      }
-      RealStdVector extractedValue = pythonIterableToStdVector< RealStdVector::value_type >( value );
-      mpi::reduceInplace( extractedValue , op, recvRank);
-      return object( extractedValue );
-   }
-
-
-   static object allreduce_int( object value, mpi::Operation op ) //NOLINT
-   {
-      if ( extract<int64_t>(value).check() )
-      {
-         int64_t extractedValue = extract< int64_t >(value);
-         mpi::allReduceInplace( extractedValue , op );
-         return object( extractedValue );
-      }
-      IntStdVector extractedValue = pythonIterableToStdVector< IntStdVector::value_type >( value );
-      mpi::allReduceInplace( extractedValue, op );
-      return object( extractedValue );
-   }
-
-   static object allreduce_real( object value, mpi::Operation op ) //NOLINT
-   {
-      if ( extract<real_t>(value).check() )
-      {
-         real_t extractedValue = extract< real_t  >(value);
-         mpi::allReduceInplace( extractedValue , op );
-         return object( extractedValue );
-      }
-      RealStdVector extractedValue = pythonIterableToStdVector< RealStdVector::value_type >( value );
-      mpi::allReduceInplace( extractedValue , op );
-      return object( extractedValue );
-   }
-
-
-   //===================================================================================================================
-   //
-   //  Gather
-   //
-   //===================================================================================================================
-
-   static IntStdVector gather_int( object value, int recvRank ) //NOLINT
-   {
-      if ( ! extract<int64_t>(value).check() )
-      {
-         PyErr_SetString( PyExc_RuntimeError, "Could not gather the given value - unknown type");
-         throw error_already_set();
-      }
-      int64_t extractedValue = extract< int64_t >(value);
-      return mpi::gather( extractedValue , recvRank );
-   }
-
-   static RealStdVector gather_real( object value, int recvRank ) //NOLINT
-   {
-      if ( ! extract<real_t>(value).check() )
-      {
-         PyErr_SetString( PyExc_RuntimeError, "Could not gather the given value - unknown type");
-         throw error_already_set();
-      }
-      real_t extractedValue = extract< real_t  >(value);
-      return mpi::gather( extractedValue , recvRank);
-   }
-
-
-   static IntStdVector allgather_int( object value ) //NOLINT
-   {
-      if ( ! extract<int64_t>(value).check() )
-      {
-         PyErr_SetString( PyExc_RuntimeError, "Could not gather the given value - unknown type");
-         throw error_already_set();
-      }
-      int64_t extractedValue = extract< int64_t >(value);
-      return mpi::allGather( extractedValue );
-   }
-
-   static RealStdVector allgather_real( object value ) //NOLINT
-   {
-      if ( ! extract<real_t>(value).check() )
-      {
-         PyErr_SetString( PyExc_RuntimeError, "Could not gather the given value - unknown type");
-         throw error_already_set();
-      }
-      real_t extractedValue = extract< real_t  >(value);
-      return mpi::allGather( extractedValue );
-   }
-
-
-
-   //===================================================================================================================
-   //
-   //  Export
-   //
-   //===================================================================================================================
-
-   static void worldBarrier()
-   {
-      WALBERLA_MPI_WORLD_BARRIER();
-   }
-
-
-   void exportMPI()
-   {
-      object mpiModule( handle<>( borrowed(PyImport_AddModule("walberla_cpp.mpi"))));
-      scope().attr("mpi") = mpiModule;
-      scope mpiScope = mpiModule;
-
-      def( "rank"             , &rank             );
-      def( "worldRank"        , &worldRank        );
-      def( "numProcesses"     , &numProcesses     );
-      def( "hasCartesianSetup", &hasCartesianSetup);
-      def( "rankValid"        , &rankValid        );
-      def( "worldBarrier"     , &worldBarrier     );
-
-      enum_<mpi::Operation>("Operation")
-              .value("MIN"    ,      mpi::MIN )
-              .value("MAX"    ,      mpi::MAX )
-              .value("SUM"    ,      mpi::SUM )
-              .value("PRODUCT",      mpi::PRODUCT )
-              .value("LOGICAL_AND",  mpi::LOGICAL_AND )
-              .value("BITWISE_AND",  mpi::BITWISE_AND )
-              .value("LOGICAL_OR",   mpi::LOGICAL_OR  )
-              .value("BITWISE_OR",   mpi::BITWISE_OR  )
-              .value("LOGICAL_XOR",  mpi::LOGICAL_XOR )
-              .value("BITWISE_XOR",  mpi::BITWISE_XOR )
-              .export_values();
-
-      def( "broadcastInt",   &broadcast_int,    ( arg("sendRank") = 0) );
-      def( "broadcastReal",  &broadcast_real,   ( arg("sendRank") = 0) );
-      def( "broadcastString",&broadcast_string, ( arg("sendRank") = 0) );
-
-      def( "reduceInt",     &reduce_int,     ( arg("recvRank") = 0 ) );
-      def( "reduceReal",    &reduce_real,    ( arg("recvRank") = 0 ) );
-      def( "allreduceInt",  &allreduce_int  );
-      def( "allreduceReal", &allreduce_real );
-
-
-      class_< IntStdVector>   ("IntStdVector") .def(vector_indexing_suite<IntStdVector>()  );
-      class_< RealStdVector>  ("RealStdVector").def(vector_indexing_suite<RealStdVector>() );
-      class_< StringStdVector>("StringStdVector").def(vector_indexing_suite<StringStdVector>() );
-
-      def( "gatherInt",     &gather_int ,   ( arg("recvRank") = 0 ) );
-      def( "gatherReal",    &gather_real,   ( arg("recvRank") = 0 ) );
-      def( "allgatherInt",  &allgather_int  );
-      def( "allgatherReal", &allgather_real );
-   }
-
-
-
-} // namespace python_coupling
-} // namespace walberla
-
-
-#endif
diff --git a/src/python_coupling/export/BasicExport.cpp b/src/python_coupling/export/BasicExport.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..c4fad8eb6cf39d9442ddbbe46e4e278d5e738a4d
--- /dev/null
+++ b/src/python_coupling/export/BasicExport.cpp
@@ -0,0 +1,720 @@
+//======================================================================================================================
+//
+//  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 BasicExports.cpp
+//! \ingroup python_coupling
+//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
+//
+//======================================================================================================================
+
+// Do not reorder includes - the include order is important
+#include "python_coupling/PythonWrapper.h"
+
+#ifdef WALBERLA_BUILD_WITH_PYTHON
+
+#   include "communication/UniformMPIDatatypeInfo.h"
+#   include "communication/UniformPackInfo.h"
+
+#   include "core/Abort.h"
+#   include "core/cell/CellInterval.h"
+#   include "core/logging/Logging.h"
+#   include "core/math/AABB.h"
+#   include "core/mpi/MPIIO.h"
+#   include "core/timing/ReduceType.h"
+#   include "core/timing/TimingPool.h"
+#   include "core/timing/TimingTree.h"
+#   include "core/waLBerlaBuildInfo.h"
+
+#   include "domain_decomposition/StructuredBlockStorage.h"
+
+#   include "field/GhostLayerField.h"
+
+#   include "python_coupling/Manager.h"
+#   include "python_coupling/helper/BlockStorageExportHelpers.h"
+#   include "python_coupling/helper/OwningIterator.h"
+
+#   include "stencil/Directions.h"
+
+#   include <functional>
+
+#   include "BasicExport.h"
+#   include "MPIExport.h"
+
+#   include <pybind11/stl.h>
+
+
+namespace py = pybind11;
+namespace walberla {
+namespace python_coupling {
+
+//======================================================================================================================
+//
+//  Cell
+//
+//======================================================================================================================
+
+void exportCell(py::module_ &m)
+{
+   py::class_<Cell>(m, "Cell")
+         .def( py::init<cell_idx_t, cell_idx_t, cell_idx_t>())
+         .def("__getitem__",
+           [](const Cell & cell, py::object & idx){
+              return py::make_tuple(cell.x(), cell.y(), cell.z()).attr("__getitem__")(idx);
+           });
+}
+
+//======================================================================================================================
+//
+//  CellInterval
+//
+//======================================================================================================================
+
+
+void cellInterval_setMin( CellInterval & ci, const Cell & min ) {
+   ci.min() = min;
+}
+void cellInterval_setMax( CellInterval & ci, const Cell & max ) {
+   ci.max() = max;
+}
+void cellInterval_shift( CellInterval & ci, cell_idx_t xShift, cell_idx_t yShift, cell_idx_t zShift ) {
+   ci.shift( xShift, yShift, zShift );
+}
+
+py::tuple cellInterval_size( CellInterval & ci ) {
+   return py::make_tuple( ci.xSize(), ci.ySize(), ci.zSize() );
+}
+
+py::tuple cellInterval_min( CellInterval & ci ) {
+   return py::make_tuple( ci.xMin(), ci.yMin(), ci.zMin() );
+}
+
+py::tuple cellInterval_max( CellInterval & ci ) {
+   return py::make_tuple( ci.xMax(), ci.yMax(), ci.zMax() );
+}
+
+CellInterval cellInterval_getIntersection( CellInterval & ci1, CellInterval & ci2 )
+{
+   CellInterval result ( ci1 );
+   result.intersect( ci2 );
+   return result;
+}
+
+CellInterval cellInterval_getShifted( CellInterval & ci1, cell_idx_t xShift, cell_idx_t yShift, cell_idx_t zShift )
+{
+   CellInterval result ( ci1 );
+   result.shift( xShift, yShift, zShift  );
+   return result;
+}
+
+CellInterval cellInterval_getExpanded1( CellInterval & ci1, cell_idx_t expandVal )
+{
+   CellInterval result ( ci1 );
+   result.expand( expandVal  );
+   return result;
+}
+
+CellInterval cellInterval_getExpanded2( CellInterval & ci1, cell_idx_t xExpand, cell_idx_t yExpand, cell_idx_t zExpand )
+{
+   CellInterval result ( ci1 );
+   result.expand( Cell(xExpand, yExpand, zExpand)  );
+   return result;
+}
+
+void exportCellInterval(py::module_ &m)
+{
+   using namespace pybind11::literals;
+   bool ( CellInterval::*p_contains1) ( const Cell         & ) const = &CellInterval::contains;
+   bool ( CellInterval::*p_contains2) ( const CellInterval & ) const = &CellInterval::contains;
+
+   void ( CellInterval::*p_expand1) ( const cell_idx_t ) = &CellInterval::expand;
+   void ( CellInterval::*p_expand2) ( const Cell &     ) = &CellInterval::expand;
+
+   bool ( CellInterval::*p_overlaps ) ( const CellInterval & ) const = &CellInterval::overlaps;
+
+   py::class_<CellInterval>(m, "CellInterval")
+      .def( py::init<cell_idx_t, cell_idx_t, cell_idx_t, cell_idx_t, cell_idx_t, cell_idx_t>())
+      .def_property( "min",  &cellInterval_min, &cellInterval_setMin )
+      .def_property( "max", &cellInterval_max, &cellInterval_setMax )
+      .def_property_readonly( "size", &cellInterval_size  )
+      .def( "empty", &CellInterval::empty )
+      .def( "positiveIndicesOnly", &CellInterval::positiveIndicesOnly )
+      .def( "contains",        p_contains1 )
+      .def( "contains",        p_contains2 )
+      .def( "overlaps",        p_overlaps )
+      .def( "shift",           &cellInterval_shift )
+      .def( "getShifted",      &cellInterval_getShifted )
+      .def( "expand",          p_expand1 )
+      .def( "expand",          p_expand2 )
+      .def( "getExpanded",     &cellInterval_getExpanded1 )
+      .def( "getExpanded",     &cellInterval_getExpanded2 )
+      .def( "intersect",       &CellInterval::intersect )
+      .def( "getIntersection", &cellInterval_getIntersection )
+      .def("__eq__",           &CellInterval::operator==)
+      .def("__ne__",    &CellInterval::operator!=)
+      .def_property_readonly( "numCells",  &CellInterval::numCells  )
+      ;
+}
+
+//======================================================================================================================
+//
+//  AABB
+//
+//======================================================================================================================
+
+
+py::tuple aabb_getMin( const AABB & domainBB ) {
+   return py::make_tuple( domainBB.xMin(), domainBB.yMin(), domainBB.zMin() );
+}
+
+py::tuple aabb_getMax( const AABB & domainBB ) {
+   return py::make_tuple( domainBB.xMax(), domainBB.yMax(), domainBB.zMax() );
+}
+
+py::tuple aabb_getSize( const AABB & domainBB ) {
+   return py::make_tuple( domainBB.sizes()[0], domainBB.sizes()[1], domainBB.sizes()[2] );
+}
+
+py::tuple aabb_getCenter( const AABB & domainBB ) {
+   return py::make_tuple( domainBB.center()[0], domainBB.center()[1], domainBB.center()[2] );
+}
+
+bool p_containsVec( const AABB & domainBB, std::array< real_t , 3 > Point ) {
+   return domainBB.contains(Vector3<real_t>(Point[0], Point[1], Point[2]));
+}
+
+bool p_containsClosedInterval1( const AABB & domainBB, std::array< real_t , 3 > Point ) {
+   return domainBB.containsClosedInterval(Vector3<real_t>(Point[0], Point[1], Point[2]));
+}
+
+bool p_containsClosedInterval2( const AABB & domainBB, std::array< real_t , 3 > Point, real_t dx ) {
+   return domainBB.containsClosedInterval(Vector3<real_t>(Point[0], Point[1], Point[2]), dx);
+}
+
+AABB p_getExtended2( const AABB & domainBB, std::array< real_t , 3 > Point ) {
+   return domainBB.getExtended(Vector3<real_t>(Point[0], Point[1], Point[2]));
+}
+
+AABB p_getScaled2( const AABB & domainBB, std::array< real_t , 3 > Point ) {
+   return domainBB.getScaled(Vector3<real_t>(Point[0], Point[1], Point[2]));
+}
+
+AABB p_getMerged2( const AABB & domainBB, std::array< real_t , 3 > Point ) {
+   return domainBB.getMerged(Vector3<real_t>(Point[0], Point[1], Point[2]));
+}
+
+void p_extend2( AABB & domainBB, std::array< real_t , 3 > Point ) {
+   domainBB.extend(Vector3<real_t>(Point[0], Point[1], Point[2]));
+}
+
+void p_scale2( AABB & domainBB, std::array< real_t , 3 > Point ) {
+   domainBB.scale(Vector3<real_t>(Point[0], Point[1], Point[2]));
+}
+
+void p_merge2( AABB & domainBB, std::array< real_t , 3 > Point ) {
+   domainBB.merge(Vector3<real_t>(Point[0], Point[1], Point[2]));
+}
+
+real_t p_distance( AABB & domainBB, std::array< real_t , 3 > Point ) {
+   return domainBB.distance(Vector3<real_t>(Point[0], Point[1], Point[2]));
+}
+
+real_t p_signedDistance( AABB & domainBB, std::array< real_t , 3 > Point ) {
+   return domainBB.signedDistance(Vector3<real_t>(Point[0], Point[1], Point[2]));
+}
+
+real_t p_maxDistance( AABB & domainBB, std::array< real_t , 3 > Point ) {
+   return domainBB.distance(Vector3<real_t>(Point[0], Point[1], Point[2]));
+}
+
+real_t p_sqDistance2( AABB & domainBB, std::array< real_t , 3 > Point ) {
+   return domainBB.sqDistance(Vector3<real_t>(Point[0], Point[1], Point[2]));
+}
+
+real_t p_sqMaxDistance2( AABB & domainBB, std::array< real_t , 3 > Point ) {
+   return domainBB.sqMaxDistance(Vector3<real_t>(Point[0], Point[1], Point[2]));
+}
+
+void aabb_setMin( AABB & domainBB, const std::array< real_t , 3 >& min )
+{
+   domainBB = AABB( domainBB.max(), AABB::vector_type ( min[0], min[1], min[2] ) );
+}
+
+void aabb_setMax( AABB & domainBB, const std::array< real_t , 3 >& max )
+{
+   domainBB = AABB( domainBB.min(), AABB::vector_type ( max[0], max[1], max[2] ) );
+}
+
+
+void exportAABB(py::module_ &m)
+{
+   bool ( AABB::*p_containsBB  )( const AABB & bb           )     const = &AABB::contains;
+
+   AABB ( AABB::*p_getExtended1 ) ( const real_t            ) const = &AABB::getExtended;
+
+   AABB ( AABB::*p_getScaled1 ) ( const real_t            ) const = &AABB::getScaled;
+
+   AABB ( AABB::*p_getMerged1 ) ( const AABB            & ) const = &AABB::getMerged;
+
+   bool ( AABB::*p_intersects1  )( const AABB & bb            ) const = &AABB::intersects;
+   bool ( AABB::*p_intersects2  )( const AABB & bb, real_t dx ) const = &AABB::intersects;
+
+   bool ( AABB::*p_intersectsClosed1  )( const AABB & bb            ) const = &AABB::intersectsClosedInterval;
+   bool ( AABB::*p_intersectsClosed2  )( const AABB & bb, real_t dx ) const = &AABB::intersectsClosedInterval;
+
+   void ( AABB::*p_extend1 ) ( const real_t            ) = &AABB::extend;
+
+   void  ( AABB::*p_scale1 ) ( const real_t            )  = &AABB::scale;
+
+   void  ( AABB::*p_merge1 ) ( const AABB            & )  = &AABB::merge;
+
+   real_t  ( AABB::*p_sqDistance1 ) ( const AABB & )            const = &AABB::sqDistance;
+
+   real_t  ( AABB::*p_sqMaxDistance1 ) ( const AABB & )            const = &AABB::sqMaxDistance;
+
+
+   py::class_<AABB>(m, "AABB")
+      .def( py::init<real_t,real_t,real_t,real_t,real_t,real_t>() )
+      .def("__eq__",     &walberla::math::operator==<real_t, real_t > )
+      .def("__ne__",     &walberla::math::operator!=<real_t, real_t > )
+      .def_property( "min",  &aabb_getMin, &aabb_setMin )
+      .def_property( "max",  &aabb_getMax, &aabb_setMax )
+      .def_property_readonly( "size", &aabb_getSize )
+      .def_property_readonly( "empty",    &AABB::empty )
+      .def_property_readonly( "volume", &AABB::volume )
+      .def_property_readonly( "center", &aabb_getCenter )
+      .def( "contains", p_containsBB )
+      .def( "contains", &p_containsVec )
+      .def( "containsClosedInterval", &p_containsClosedInterval1 )
+      .def( "containsClosedInterval", &p_containsClosedInterval2 )
+      .def( "getExtended", p_getExtended1 )
+      .def( "getExtended", &p_getExtended2 )
+      .def( "getTranslated", &AABB::getTranslated )
+      .def( "getScaled", p_getScaled1 )
+      .def( "getScaled", &p_getScaled2 )
+      .def( "getMerged", p_getMerged1 )
+      .def( "getMerged", &p_getMerged2 )
+      .def( "intersects", p_intersects1 )
+      .def( "intersects", p_intersects2 )
+      .def( "intersectsClosedInterval", p_intersectsClosed1 )
+      .def( "intersectsClosedInterval", p_intersectsClosed2 )
+      .def( "intersectionVolume", &AABB::intersectionVolume )
+      .def( "getIntersection", &AABB::getIntersection )
+      .def( "isIdentical", &AABB::isIdentical )
+      .def( "isEqual",     &AABB::isEqual )
+      .def( "sqDistance",       p_sqDistance1 )
+      .def( "sqDistance",       &p_sqDistance2 )
+      .def( "sqMaxDistance",    p_sqMaxDistance1 )
+      .def( "sqMaxDistance",    &p_sqMaxDistance2 )
+      .def( "sqSignedDistance", &AABB::sqSignedDistance)
+      .def( "distance"        , &p_distance)
+      .def( "signedDistance"  , &p_signedDistance)
+      .def( "maxDistance"     , &p_maxDistance)
+      .def( "extend", p_extend1 )
+      .def( "extend", &p_extend2 )
+      .def( "translate", &AABB::translate )
+      .def( "scale", p_scale1 )
+      .def( "scale", &p_scale2 )
+      .def( "merge", p_merge1 )
+      .def( "merge", &p_merge2 )
+      .def( "intersect", &AABB::intersect )
+      ;
+}
+
+//======================================================================================================================
+//
+//  Timing
+//
+//======================================================================================================================
+
+py::dict buildDictFromTimingNode(const WcTimingNode & tn)
+{
+   py::dict result;
+
+   result["all"] = tn.timer_;
+   for ( auto it = tn.tree_.begin(); it != tn.tree_.end(); ++it)
+   {
+      std::string key = it->first;
+      if (it->second.tree_.empty())
+      {
+         result[key.c_str()] = it->second.timer_;
+      } else
+      {
+         result[key.c_str()] = buildDictFromTimingNode(it->second);
+      }
+   }
+
+   return result;
+}
+
+py::dict buildDictFromTimingTree(const WcTimingTree & tt)
+{
+   return buildDictFromTimingNode( tt.getRawData() );
+}
+
+void timingTreeStopWrapper(WcTimingTree & tt, const std::string& name)
+{
+   if (!tt.isTimerRunning(name))
+   {
+      throw py::value_error(("Timer '" + name + "' is currently not running!").c_str());
+   }
+   tt.stop(name);
+}
+
+void exportTiming(py::module_ &m)
+{
+   py::class_<WcTimer> (m, "Timer")
+      .def( py::init<>() )
+      .def( "start",  &WcTimer::start )
+      .def( "stop",   &WcTimer::end   )
+      .def( "reset",  &WcTimer::reset )
+      .def( "merge",  &WcTimer::merge )
+      .def_property_readonly( "counter",      &WcTimer::getCounter   )
+      .def_property_readonly( "total",        &WcTimer::total        )
+      .def_property_readonly( "sumOfSquares", &WcTimer::sumOfSquares )
+      .def_property_readonly( "average",      &WcTimer::average      )
+      .def_property_readonly( "variance",     &WcTimer::variance     )
+      .def_property_readonly( "min",          &WcTimer::min          )
+      .def_property_readonly( "max",          &WcTimer::max          )
+      .def_property_readonly( "last",         &WcTimer::last         )
+      ;
+
+
+   WcTimer & ( WcTimingPool::*pGetItem ) ( const std::string & ) = &WcTimingPool::operator[];
+
+   {
+      py::scope classScope =
+      py::class_<WcTimingPool, shared_ptr<WcTimingPool> > (m, "TimingPool")
+         .def( py::init<>() )
+         .def_property_readonly( "__getitem__",     pGetItem)
+         .def( "__contains__",    &WcTimingPool::timerExists )
+         .def( "getReduced",      &WcTimingPool::getReduced)
+         .def( "merge",           &WcTimingPool::merge)
+         .def( "clear",           &WcTimingPool::clear )
+         .def( "unifyRegisteredTimersAcrossProcesses", &WcTimingPool::unifyRegisteredTimersAcrossProcesses )
+         .def( "logResultOnRoot", &WcTimingPool::logResultOnRoot)
+         ;
+      WALBERLA_UNUSED( classScope );
+
+      py::enum_<timing::ReduceType>(m, "ReduceType")
+          .value("min"  , timing::REDUCE_MIN)
+          .value("avg"  , timing::REDUCE_AVG)
+          .value("max"  , timing::REDUCE_MAX)
+          .value("total", timing::REDUCE_TOTAL)
+          .export_values()
+          ;
+   }
+
+   const WcTimer & ( WcTimingTree::*pTimingTreeGet ) ( const std::string & ) const = &WcTimingTree::operator[];
+   py::class_<WcTimingTree, shared_ptr<WcTimingTree> > (m, "TimingTree")
+         .def( py::init<>() )
+         .def_property_readonly( "__getitem__",  pTimingTreeGet )
+         .def( "start",        &WcTimingTree::start )
+         .def( "stop",         &timingTreeStopWrapper )
+         .def( "getReduced",   &WcTimingTree::getReduced )
+         .def( "toDict",       &buildDictFromTimingTree )
+    ;
+}
+
+
+
+
+//======================================================================================================================
+//
+//  IBlock
+//
+//======================================================================================================================
+
+py::object IBlock_getData( py::object iblockObject, const std::string & stringID ) //NOLINT
+{
+   IBlock * block = py::cast<IBlock*>( iblockObject );
+
+   BlockDataID id = blockDataIDFromString( *block, stringID );
+
+   auto manager = python_coupling::Manager::instance();
+   py::object res =  manager->pythonObjectFromBlockData( *block, id );
+
+   if ( res.is(py::object()) )
+      throw BlockDataNotConvertible();
+
+   return manager->pythonObjectFromBlockData( *block, id );
+}
+
+
+std::vector<std::string> IBlock_fieldNames( py::object iblockObject ) //NOLINT
+{
+   IBlock * block = py::cast<IBlock*>( iblockObject );
+
+   return block->getBlockStorage().getBlockDataIdentifiers();
+}
+
+py::tuple IBlock_atDomainMinBorder( IBlock & block )
+{
+   return py::make_tuple( block.getBlockStorage().atDomainXMinBorder(block),
+                                     block.getBlockStorage().atDomainYMinBorder(block),
+                                     block.getBlockStorage().atDomainZMinBorder(block) );
+}
+
+py::tuple IBlock_atDomainMaxBorder( IBlock & block )
+{
+   return py::make_tuple( block.getBlockStorage().atDomainXMaxBorder(block),
+                                     block.getBlockStorage().atDomainYMaxBorder(block),
+                                     block.getBlockStorage().atDomainZMaxBorder(block) );
+}
+
+IBlockID::IDType IBlock_getIntegerID( IBlock & block )
+{
+   return block.getId().getID();
+}
+
+bool IBlock_equals( IBlock & block1, IBlock & block2 )
+{
+   return block1.getId() == block2.getId();
+}
+
+std::string IBlock_str( IBlock & b ) {
+   std::stringstream out;
+   out <<  "Block at " << b.getAABB();
+   return out.str();
+
+}
+
+void exportIBlock(py::module_ &m)
+{
+   static py::exception<NoSuchBlockData> ex(m, "NoSuchBlockData");
+   py::register_exception_translator([](std::exception_ptr p) {
+     try {
+        if (p) std::rethrow_exception(p);
+     } catch (const NoSuchBlockData &e) {
+        // Set NoSuchBlockData as the active python error
+        throw std::out_of_range(e.what());
+     }
+   });
+   static py::exception<BlockDataNotConvertible> ex2(m, "BlockDataNotConvertible");
+   py::register_exception_translator([](std::exception_ptr p) {
+     try {
+        if (p) std::rethrow_exception(p);
+     } catch (const BlockDataNotConvertible &e) {
+        // Set BlockDataNotConvertible as the active python error
+        throw std::invalid_argument(e.what());
+     }
+   });
+
+   py::class_<IBlock, std::unique_ptr<IBlock, py::nodelete>> (m, "Block")
+         .def                  ( "__getitem__",          &IBlock_getData, py::keep_alive<0, 1>()      )
+         .def_property_readonly( "atDomainMinBorder",    &IBlock_atDomainMinBorder                    )
+         .def_property_readonly( "atDomainMaxBorder",    &IBlock_atDomainMaxBorder                    )
+         .def_property_readonly( "fieldNames",           &IBlock_fieldNames                           )
+         .def_property_readonly( "id",                   &IBlock_getIntegerID                         )
+         .def                  ( "__hash__",             &IBlock_getIntegerID                         )
+         .def                  ( "__eq__",               &IBlock_equals                               )
+         .def                  ( "__repr__",             &IBlock_str                                  )
+         .def_property_readonly("aabb",                  &IBlock::getAABB                             )
+         ;
+
+}
+
+//======================================================================================================================
+//
+//  Logging & Abort
+//
+//======================================================================================================================
+
+
+static void wlb_log_devel              ( const std::string & msg ) { WALBERLA_LOG_DEVEL          ( msg ); }
+static void wlb_log_devel_on_root      ( const std::string & msg ) { WALBERLA_LOG_DEVEL_ON_ROOT  ( msg ); }
+
+static void wlb_log_result             ( const std::string & msg ) { WALBERLA_LOG_RESULT         ( msg ); }
+static void wlb_log_result_on_root     ( const std::string & msg ) { WALBERLA_LOG_RESULT_ON_ROOT ( msg ); }
+
+static void wlb_log_warning            ( const std::string & msg ) { WALBERLA_LOG_WARNING         ( msg ); }
+static void wlb_log_warning_on_root    ( const std::string & msg ) { WALBERLA_LOG_WARNING_ON_ROOT ( msg ); }
+
+#ifdef WALBERLA_LOGLEVEL_INFO
+static void wlb_log_info               ( const std::string & msg ) { WALBERLA_LOG_INFO            ( msg ); }
+static void wlb_log_info_on_root       ( const std::string & msg ) { WALBERLA_LOG_INFO_ON_ROOT    ( msg ); }
+#else
+static void wlb_log_info               ( const std::string & ) {}
+static void wlb_log_info_on_root       ( const std::string & ) {}
+#endif
+
+#ifdef WALBERLA_LOGLEVEL_PROGRESS
+static void wlb_log_progress           ( const std::string & msg ) { WALBERLA_LOG_PROGRESS        ( msg ); }
+static void wlb_log_progress_on_root   ( const std::string & msg ) { WALBERLA_LOG_PROGRESS_ON_ROOT( msg ); }
+#else
+static void wlb_log_progress           ( const std::string & ) {}
+static void wlb_log_progress_on_root   ( const std::string & ) {}
+#endif
+
+#ifdef WALBERLA_LOGLEVEL_DETAIL
+static void wlb_log_detail             ( const std::string & msg ) { WALBERLA_LOG_DETAIL          ( msg ); }
+static void wlb_log_detail_on_root     ( const std::string & msg ) { WALBERLA_LOG_DETAIL_ON_ROOT  ( msg ); }
+#else
+static void wlb_log_detail             ( const std::string & ) {}
+static void wlb_log_detail_on_root     ( const std::string & ) {}
+#endif
+
+static void wlb_abort                  ( const std::string & msg ) { WALBERLA_ABORT_NO_DEBUG_INFO ( msg ); }
+
+void exportLogging(py::module_ &m)
+{
+   m.def ( "log_devel"         ,  wlb_log_devel           );
+   m.def ( "log_devel_on_root" ,  wlb_log_devel_on_root   );
+   m.def ( "log_result",          wlb_log_result          );
+   m.def ( "log_result_on_root",  wlb_log_result_on_root  );
+   m.def ( "log_warning",         wlb_log_warning         );
+   m.def ( "log_warning_on_root", wlb_log_warning_on_root );
+   m.def ( "log_info",            wlb_log_info            );
+   m.def ( "log_info_on_root",    wlb_log_info_on_root    );
+   m.def ( "log_progress",        wlb_log_progress        );
+   m.def ( "log_progress_on_root",wlb_log_progress_on_root);
+   m.def ( "log_detail",          wlb_log_detail          );
+   m.def ( "log_detail_on_root",  wlb_log_detail_on_root  );
+
+   m.def ( "abort", wlb_abort );
+}
+
+//======================================================================================================================
+//
+//  Communication
+//
+//======================================================================================================================
+
+void exportCommunication(py::module_ &m)
+{
+   using communication::UniformPackInfo;
+   py::class_< UniformPackInfo, shared_ptr<UniformPackInfo>> //NOLINT
+      (m, "UniformPackInfo" );
+
+   using communication::UniformMPIDatatypeInfo;
+   py::class_< UniformMPIDatatypeInfo, shared_ptr<UniformMPIDatatypeInfo>>
+      (m, "UniformMPIDatatypeInfo" );
+
+}
+
+//======================================================================================================================
+//
+//  Stencil Directions
+//
+//======================================================================================================================
+
+void exportStencilDirections(py::module_ &m)
+{
+
+      py::module_ m2 = m.def_submodule("stencil", "Stencil Extension of the waLBerla python bindings");
+      py::enum_< stencil::Direction >(m2, "Direction")
+         .value("C", stencil::C)
+         .value("N", stencil::N)
+         .value("S", stencil::S)
+         .value("W", stencil::W)
+         .value("E", stencil::E)
+         .value("T", stencil::T)
+         .value("B", stencil::B)
+         .value("NW", stencil::NW)
+         .value("NE", stencil::NE)
+         .value("SW", stencil::SW)
+         .value("SE", stencil::SE)
+         .value("TN", stencil::TN)
+         .value("TS", stencil::TS)
+         .value("TW", stencil::TW)
+         .value("TE", stencil::TE)
+         .value("BN", stencil::BN)
+         .value("BS", stencil::BS)
+         .value("BW", stencil::BW)
+         .value("BE", stencil::BE)
+         .value("TNE", stencil::TNE)
+         .value("TNW", stencil::TNW)
+         .value("TSE", stencil::TSE)
+         .value("TSW", stencil::TSW)
+         .value("BNE", stencil::BNE)
+         .value("BNW", stencil::BNW)
+         .value("BSE", stencil::BSE)
+         .value("BSW", stencil::BSW)
+         .export_values();
+      py::list cx;
+
+      py::list cy;
+
+      py::list cz;
+
+      py::list dirStrings;
+      for (uint_t i = 0; i < stencil::NR_OF_DIRECTIONS; ++i)
+      {
+         cx.append(stencil::cx[i]);
+         cy.append(stencil::cy[i]);
+         cz.append(stencil::cz[i]);
+         dirStrings.append(stencil::dirToString[i]);
+      }
+      py::list c;
+      c.append(cx);
+      c.append(cy);
+      c.append(cz);
+
+      m2.attr("cx") = cx;
+      m2.attr("cy") = cy;
+      m2.attr("cz") = cz;
+      m2.attr("c") = c;
+      m2.attr("dirStrings") = dirStrings;
+}
+
+
+//======================================================================================================================
+//
+//  Build Info
+//
+//======================================================================================================================
+
+
+void exportBuildInfo(py::module_ &m)
+{
+   py::module_ m2 = m.def_submodule("build_info", "Get waLBerla Build Information");
+   m2.attr("version")         = WALBERLA_GIT_SHA1;
+   m2.attr("type" )           = WALBERLA_BUILD_TYPE;
+   m2.attr("compiler_flags" ) = WALBERLA_COMPILER_FLAGS;
+   m2.attr("build_machine" )  = WALBERLA_BUILD_MACHINE;
+   m2.attr("source_dir")      = WALBERLA_SOURCE_DIR;
+   m2.attr("build_dir")       = WALBERLA_BUILD_DIR;
+}
+
+
+
+
+void exportBasicWalberlaDatastructures(py::module_ &m)
+{
+   exportMPI(m);
+
+   exportBuildInfo(m);
+   exportCell(m);
+   exportCellInterval(m);
+   exportAABB(m);
+
+   exportTiming(m);
+
+   exportIBlock(m);
+   exportCommunication(m);
+
+   exportLogging(m);
+   exportStencilDirections(m);
+
+   // Add empty callbacks module
+   m.def_submodule("callbacks", "Empty callbacks module. Needed for the Szenario manager");
+
+}
+
+} // namespace python_coupling
+} // namespace walberla
+
+#endif
+
diff --git a/src/python_coupling/basic_exports/BasicExports.h b/src/python_coupling/export/BasicExport.h
similarity index 87%
rename from src/python_coupling/basic_exports/BasicExports.h
rename to src/python_coupling/export/BasicExport.h
index 27421cd1fdeedba599a9d96ff9a118dbab48ec8b..ba9bb2b07633e82e0eb27244b515abb33b22fae2 100644
--- a/src/python_coupling/basic_exports/BasicExports.h
+++ b/src/python_coupling/export/BasicExport.h
@@ -16,18 +16,21 @@
 //! \file BasicExports.h
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
 #pragma once
 
+#include <pybind11/pybind11.h>
 
 namespace walberla {
 namespace python_coupling {
+namespace py = pybind11;
 
 
 
-   void exportBasicWalberlaDatastructures();
+   void exportBasicWalberlaDatastructures(py::module_ &m);
 
 
 
diff --git a/src/blockforest/python/CommunicationExport.h b/src/python_coupling/export/BlockForestCommunicationExport.h
similarity index 78%
rename from src/blockforest/python/CommunicationExport.h
rename to src/python_coupling/export/BlockForestCommunicationExport.h
index f47406da073bbfe361abcfd00f1913e38bc3a91e..f74ff2fa307f76e54443a8dfeb1b03549b7d9a0f 100644
--- a/src/blockforest/python/CommunicationExport.h
+++ b/src/python_coupling/export/BlockForestCommunicationExport.h
@@ -16,10 +16,12 @@
 //! \file CommunicationExport.h
 //! \ingroup blockforest
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
 #pragma once
+#include <pybind11/pybind11.h>
 
 
 
@@ -27,15 +29,15 @@ namespace walberla {
 namespace blockforest {
 
 
-   template<typename Stencils>
-   void exportUniformBufferedScheme();
+   template<typename... Stencils>
+   void exportUniformBufferedScheme(pybind11::module_& m);
 
-   template<typename Stencils>
-   void exportUniformDirectScheme();
+   template<typename... Stencils>
+   void exportUniformDirectScheme(pybind11::module_& m);
 
 
 } // namespace blockforest
 } // namespace walberla
 
 
-#include "CommunicationExport.impl.h"
+#include "BlockForestCommunicationExport.impl.h"
diff --git a/src/python_coupling/export/BlockForestCommunicationExport.impl.h b/src/python_coupling/export/BlockForestCommunicationExport.impl.h
new file mode 100644
index 0000000000000000000000000000000000000000..a45abe4ff97b9875ea9754500bcf361d020de193
--- /dev/null
+++ b/src/python_coupling/export/BlockForestCommunicationExport.impl.h
@@ -0,0 +1,242 @@
+//======================================================================================================================
+//
+//  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 CommunicationExport.impl.h
+//! \ingroup blockforest
+//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
+//
+//======================================================================================================================
+
+#include "blockforest/communication/UniformBufferedScheme.h"
+#include "blockforest/communication/UniformDirectScheme.h"
+
+#include "python_coupling/helper/MplHelpers.h"
+
+#include <pybind11/pybind11.h>
+
+namespace walberla
+{
+namespace blockforest
+{
+namespace py = pybind11;
+namespace internal
+{
+//===================================================================================================================
+//
+//  UniformBufferedScheme
+//
+//===================================================================================================================
+
+/// the purpose of this class could also be solved by adding return_internal_reference to "createUniformDirectScheme"
+/// however this is not easily possible since it returns not a reference but an py::object
+template< typename Stencil >
+class UniformBufferedSchemeWrapper : public blockforest::communication::UniformBufferedScheme< Stencil >
+{
+ public:
+   UniformBufferedSchemeWrapper(const shared_ptr< StructuredBlockForest >& bf, const int tag)
+      : blockforest::communication::UniformBufferedScheme< Stencil >(bf, tag), blockforest_(bf)
+   {}
+
+ private:
+   shared_ptr< StructuredBlockForest > blockforest_;
+};
+
+struct UniformBufferedSchemeExporter
+{
+   UniformBufferedSchemeExporter(py::module_& m) : m_(m) {}
+   template< typename Stencil >
+   void operator()(python_coupling::NonCopyableWrap< Stencil >) const
+   {
+      typedef UniformBufferedSchemeWrapper< Stencil > UBS;
+      std::string class_name = "UniformBufferedScheme" + std::string(Stencil::NAME);
+
+      py::class_< UBS, shared_ptr< UBS > >(m_, class_name.c_str())
+         .def("__call__", &UBS::operator())
+         .def("communicate", &UBS::communicate)
+         .def("startCommunication", &UBS::startCommunication)
+         .def("wait", &UBS::wait)
+         .def("addPackInfo", &UBS::addPackInfo)
+         .def("addDataToCommunicate", &UBS::addDataToCommunicate)
+         .def("localMode", &UBS::localMode)
+         .def("setLocalMode", &UBS::setLocalMode);
+   }
+   const py::module_& m_;
+};
+
+class UniformBufferedSchemeCreator
+{
+ public:
+   UniformBufferedSchemeCreator( const shared_ptr<StructuredBlockForest> & bf,
+                               const std::string & stencilName,
+                               const int tag )
+      : blockforest_( bf), stencilName_( stencilName ), tag_( tag )
+   {}
+
+   template<typename Stencil>
+   void operator() ( python_coupling::NonCopyableWrap<Stencil> )
+   {
+
+      if ( std::string(Stencil::NAME) == stencilName_ ) {
+         result_ = py::cast( make_shared< UniformBufferedSchemeWrapper<Stencil> > ( blockforest_, tag_ ) );
+      }
+   }
+
+   py::object getResult() { return result_; }
+ private:
+   py::object result_;
+   shared_ptr<StructuredBlockForest> blockforest_;
+   std::string stencilName_;
+   const int tag_;
+};
+
+
+template<typename... Stencils>
+py::object createUniformBufferedScheme( const shared_ptr<StructuredBlockForest> & bf,
+                                        const std::string & stencil, const int tag )
+{
+   UniformBufferedSchemeCreator creator( bf, stencil, tag );
+   python_coupling::for_each_noncopyable_type< Stencils... >  ( std::ref(creator) );
+
+   if ( !creator.getResult() )
+   {
+      throw py::value_error("Unknown stencil.");
+   }
+   return creator.getResult();
+}
+
+//===================================================================================================================
+//
+//  UniformDirectScheme
+//
+//===================================================================================================================
+
+template< typename Stencil >
+class UniformDirectSchemeWrapper : public blockforest::communication::UniformDirectScheme< Stencil >
+{
+ public:
+   UniformDirectSchemeWrapper(const shared_ptr< StructuredBlockForest >& bf, const int tag)
+      : blockforest::communication::UniformDirectScheme< Stencil >(
+           bf, shared_ptr< walberla::communication::UniformMPIDatatypeInfo >(), tag),
+        blockforest_(bf)
+   {}
+
+ private:
+   shared_ptr< StructuredBlockForest > blockforest_;
+};
+
+struct UniformDirectSchemeExporter
+{
+   UniformDirectSchemeExporter(py::module_& m) : m_(m) {}
+   template< typename Stencil >
+   void operator()(python_coupling::NonCopyableWrap< Stencil >) const
+   {
+      typedef UniformDirectSchemeWrapper< Stencil > UDS;
+      std::string class_name = "UniformDirectScheme_" + std::string(Stencil::NAME);
+
+      py::class_< UDS, shared_ptr<UDS> >(m_, class_name.c_str() )
+         .def("__call__", &UDS::operator())
+         .def("communicate", &UDS::communicate)
+         .def("startCommunication", &UDS::startCommunication)
+         .def("wait", &UDS::wait)
+         .def("addDataToCommunicate", &UDS::addDataToCommunicate);
+   }
+   const py::module_ m_;
+};
+
+class UniformDirectSchemeCreator
+{
+ public:
+   UniformDirectSchemeCreator( const shared_ptr<StructuredBlockForest> & bf,
+                               const std::string & stencilName,
+                               const int tag )
+      : blockforest_( bf), stencilName_( stencilName ), tag_( tag )
+   {}
+
+   template<typename Stencil>
+   void operator() ( python_coupling::NonCopyableWrap<Stencil> )
+   {
+
+      if ( std::string(Stencil::NAME) == stencilName_ ) {
+         result_ = py::cast( make_shared< UniformDirectSchemeWrapper<Stencil> > ( blockforest_, tag_ ) );
+      }
+   }
+
+   py::object getResult() { return result_; }
+ private:
+   py::object result_;
+   shared_ptr<StructuredBlockForest> blockforest_;
+   std::string stencilName_;
+   const int tag_;
+};
+
+
+template<typename... Stencils>
+py::object createUniformDirectScheme( const shared_ptr<StructuredBlockForest> & bf,
+                                      const std::string & stencil, const int tag )
+{
+   UniformDirectSchemeCreator creator( bf, stencil, tag );
+   python_coupling::for_each_noncopyable_type< Stencils... >  ( std::ref(creator) );
+
+   if ( !creator.getResult() )
+   {
+      throw py::value_error("Unknown stencil.");
+   }
+   return creator.getResult();
+}
+
+
+
+
+} // namespace internal
+
+template< typename... Stencils >
+void exportUniformDirectScheme(py::module_& m)
+{
+   using namespace py;
+
+   python_coupling::for_each_noncopyable_type< Stencils... >(internal::UniformDirectSchemeExporter(m));
+   m.def( "createUniformDirectScheme",
+           [](const shared_ptr<StructuredBlockForest> & blocks, const std::string & stencil, const int tag)
+            {
+             return internal::createUniformDirectScheme< Stencils... >(blocks, stencil, tag);
+            },
+           "blocks"_a, "stencil"_a, "tag"_a=778 );
+
+
+}
+
+template< typename... Stencils >
+void exportUniformBufferedScheme(py::module_& m)
+{
+   using namespace py;
+
+   py::enum_< LocalCommunicationMode >(m, "LocalCommunicationMode")
+      .value("START", START)
+      .value("WAIT", WAIT)
+      .value("BUFFER", BUFFER)
+      .export_values();
+
+   python_coupling::for_each_noncopyable_type< Stencils... >(internal::UniformBufferedSchemeExporter(m));
+   m.def( "createUniformBufferedScheme",
+          [](const shared_ptr<StructuredBlockForest> & blocks, const std::string & stencil, const int tag)
+          {
+            return internal::createUniformBufferedScheme< Stencils... >(blocks, stencil, tag);
+          },
+          "blocks"_a, "stencil"_a, "tag"_a=778 );
+}
+
+} // namespace blockforest
+} // namespace walberla
\ No newline at end of file
diff --git a/src/python_coupling/export/BlockForestExport.cpp b/src/python_coupling/export/BlockForestExport.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..e4aae6226c92ee24cad6f4d1db07fcfce6c80a66
--- /dev/null
+++ b/src/python_coupling/export/BlockForestExport.cpp
@@ -0,0 +1,382 @@
+//======================================================================================================================
+//
+//  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 Exports.cpp
+//! \ingroup blockforest
+//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
+//
+//======================================================================================================================
+
+// Do not reorder includes - the include order is important
+#include "python_coupling/PythonWrapper.h"
+
+#ifdef WALBERLA_BUILD_WITH_PYTHON
+
+#   include "blockforest/Initialization.h"
+#   include "blockforest/SetupBlock.h"
+#   include "blockforest/SetupBlockForest.h"
+#   include "blockforest/StructuredBlockForest.h"
+
+#   include "core/StringUtility.h"
+#   include "core/mpi/MPIIO.h"
+
+#   include "stencil/D3Q19.h"
+
+#   include <memory>
+#   include <pybind11/stl.h>
+#   include <sstream>
+
+#   include "BlockForestExport.h"
+#   include "python_coupling/helper/OwningIterator.h"
+
+// specialize operator== since == is deprecated in pybind11
+template<>
+bool walberla::domain_decomposition::internal::BlockData::Data< pybind11::object >::operator==(
+        const BlockData::DataBase& rhs) const
+{
+    const Data< pybind11::object >* rhsData = dynamic_cast< const Data< pybind11::object >* >(&rhs);
+    return (rhsData == &rhs) && (data_->is(*(rhsData->data_)));
+}
+
+namespace walberla
+{
+namespace blockforest
+{
+std::string printSetupBlock(const SetupBlock& b)
+{
+   std::stringstream out;
+   out << "SetupBlock at " << b.getAABB();
+   return out.str();
+}
+
+namespace py = pybind11;
+
+//======================================================================================================================
+//
+//  StructuredBlockForest
+//
+//======================================================================================================================
+
+#ifdef WALBERLA_BUILD_WITH_PYTHON
+
+void NoSuchBlockData::translate(  const NoSuchBlockData & e ) {
+   throw py::cast_error(e.what());
+}
+
+void BlockDataNotConvertible::translate(  const BlockDataNotConvertible & e ) {
+   throw py::cast_error(e.what());
+}
+#else
+
+void NoSuchBlockData::translate(  const NoSuchBlockData &  ) {}
+
+void BlockDataNotConvertible::translate(  const BlockDataNotConvertible &  ) {}
+
+#endif
+
+BlockDataID blockDataIDFromString(BlockStorage& bs, const std::string& stringID)
+{
+   auto ids = bs.getBlockDataIdentifiers();
+
+   for (uint_t i = 0; i < ids.size(); ++i)
+      if (ids[i] == stringID) return BlockDataID(i);
+
+   throw NoSuchBlockData();
+}
+
+BlockDataID blockDataIDFromString(IBlock& block, const std::string& stringID)
+{
+   return blockDataIDFromString(block.getBlockStorage(), stringID);
+}
+
+BlockDataID blockDataIDFromString(StructuredBlockForest& bs, const std::string& stringID)
+{
+   return blockDataIDFromString(bs.getBlockStorage(), stringID);
+}
+
+py::iterator StructuredBlockForest_iter(const shared_ptr< StructuredBlockForest >& bf) // NOLINT
+{
+   // shared_ptr<StructuredBlockForest> s = py::cast< shared_ptr<StructuredBlockForest> > ( StructuredBlockForest );
+
+   std::vector< const IBlock* > blocks;
+   bf->getBlocks(blocks);
+   std::vector<py::object> resultList;
+   resultList.reserve(blocks.size());
+
+   for (auto it = blocks.begin(); it != blocks.end(); ++it)
+   {
+      py::object theObject = py::cast(*it);
+      resultList.push_back(theObject);
+   }
+
+   return python_coupling::make_owning_iterator(resultList);
+}
+
+py::object StructuredBlockForest_getItem(const shared_ptr< StructuredBlockForest >& bf, uint_t i) // NOLINT
+{
+   if (i >= bf->size()) { throw py::value_error("Index out of bounds"); }
+
+   std::vector< const IBlock* > blocks;
+   bf->getBlocks(blocks);
+
+   py::object theObject = py::cast(blocks[i]);
+   return theObject;
+}
+
+std::vector<py::object> StructuredBlockForest_blocksOverlappedByAABB(StructuredBlockForest& s, const AABB& aabb)
+{
+   std::vector< IBlock* > blocks;
+   s.getBlocksOverlappedByAABB(blocks, aabb);
+
+   std::vector<py::object> resultList;
+   for (auto it = blocks.begin(); it != blocks.end(); ++it)
+      resultList.push_back(py::cast(*it));
+   return resultList;
+}
+
+std::vector<py::object> StructuredBlockForest_blocksContainedWithinAABB(StructuredBlockForest& s, const AABB& aabb)
+{
+   std::vector< IBlock* > blocks;
+   s.getBlocksContainedWithinAABB(blocks, aabb);
+
+   std::vector<py::object> resultList;
+   for (auto it = blocks.begin(); it != blocks.end(); ++it)
+      resultList.push_back(py::cast(*it));
+   return resultList;
+}
+
+py::object SbF_transformGlobalToLocal(StructuredBlockForest& s, IBlock& block, const py::object& global)
+{
+   if (py::isinstance< CellInterval >(global))
+   {
+      CellInterval ret;
+      s.transformGlobalToBlockLocalCellInterval(ret, block, py::cast< CellInterval >(global));
+      return py::cast(ret);
+   }
+   else if (py::isinstance< Cell >(global))
+   {
+      Cell ret;
+      s.transformGlobalToBlockLocalCell(ret, block, py::cast< Cell >(global));
+      return py::cast(ret);
+   }
+
+   throw py::value_error("Only CellIntervals and cells can be transformed");
+}
+
+py::object SbF_transformLocalToGlobal(StructuredBlockForest& s, IBlock& block, const py::object& local)
+{
+   if (py::isinstance< CellInterval >(local))
+   {
+      CellInterval ret;
+      s.transformBlockLocalToGlobalCellInterval(ret, block, py::cast< CellInterval >(local));
+      return py::cast(ret);
+   }
+   else if (py::isinstance< Cell >(local))
+   {
+      Cell ret;
+      s.transformBlockLocalToGlobalCell(ret, block, py::cast< Cell >(local));
+      return py::cast(ret);
+   }
+   throw py::value_error("Only CellIntervals and cells can be transformed");
+}
+
+void SbF_writeBlockData(StructuredBlockForest& s, const std::string& blockDataId, const std::string& file)
+{
+   mpi::SendBuffer buffer;
+   s.serializeBlockData(blockDataIDFromString(s, blockDataId), buffer);
+   mpi::writeMPIIO(file, buffer);
+}
+
+void SbF_readBlockData(StructuredBlockForest& s, const std::string& blockDataId, const std::string& file)
+{
+   mpi::RecvBuffer buffer;
+   mpi::readMPIIO(file, buffer);
+
+   s.deserializeBlockData(blockDataIDFromString(s, blockDataId), buffer);
+   if (!buffer.isEmpty())
+   { throw py::cast_error("Reading failed - file does not contain matching data for this type."); }
+}
+
+CellInterval SbF_getBlockCellBB(StructuredBlockForest& s, const IBlock* block) { return s.getBlockCellBB(*block); }
+
+std::array<real_t , 3> SbF_mapToPeriodicDomain1(StructuredBlockForest& s, real_t x, real_t y, real_t z)
+{
+   Vector3< real_t > res(x, y, z);
+   s.mapToPeriodicDomain(res);
+   return std::array< real_t, 3 >{ res[0], res[1], res[2] };
+}
+
+std::array<real_t , 3> SbF_mapToPeriodicDomain2(StructuredBlockForest& s, const std::array<real_t, 3>& in)
+{
+   Vector3< real_t > tmp(in[0], in[1], in[2]);
+   s.mapToPeriodicDomain(tmp);
+   return std::array< real_t, 3 >{ tmp[0], tmp[1], tmp[2] };
+}
+
+Cell SbF_mapToPeriodicDomain3(StructuredBlockForest& s, Cell in, uint_t level = 0)
+{
+   s.mapToPeriodicDomain(in, level);
+   return in;
+}
+
+py::object SbF_getBlock1(StructuredBlockForest& s, const real_t x, const real_t y, const real_t z)
+{
+   return py::cast(s.getBlock(x, y, z));
+}
+
+py::object SbF_getBlock2(StructuredBlockForest& s, const std::array<real_t, 3>& v)
+{
+   return py::cast(s.getBlock(Vector3<real_t>(v[0], v[1], v[2])));
+
+}
+
+py::tuple SbF_periodic(StructuredBlockForest& s)
+{
+   return py::make_tuple(s.isXPeriodic(), s.isYPeriodic(), s.isZPeriodic());
+}
+
+bool p_blockExists1(StructuredBlockForest& s, const std::array<real_t, 3>& v)
+{
+   return s.blockExists(Vector3<real_t>(v[0], v[1], v[2]));
+
+}
+
+bool p_blockExistsLocally1(StructuredBlockForest& s, const std::array<real_t, 3>& v)
+{
+   return s.blockExistsLocally(Vector3<real_t>(v[0], v[1], v[2]));
+
+}
+
+bool p_blockExistsRemotely1(StructuredBlockForest& s, const std::array<real_t, 3>& v)
+{
+   return s.blockExistsRemotely(Vector3<real_t>(v[0], v[1], v[2]));
+
+}
+
+py::object * blockDataCreationHelper( IBlock * block,  py::object callable ) //NOLINT
+{
+    py::object * res = new py::object( callable( block ));
+    return res;
+}
+
+uint_t StructuredBlockForest_addBlockData( StructuredBlockForest & s, const std::string & name, py::object functionPtr ) //NOLINT
+{
+    BlockDataID res = s.addBlockData(name)
+            << BlockDataCreator<py::object>( std::bind( &blockDataCreationHelper, std::placeholders::_1, functionPtr ) );
+    return res;
+}
+
+bool SbF_atDomainXMinBorder(StructuredBlockForest& s, const IBlock* b) { return s.atDomainXMinBorder(*b); }
+bool SbF_atDomainXMaxBorder(StructuredBlockForest& s, const IBlock* b) { return s.atDomainXMaxBorder(*b); }
+bool SbF_atDomainYMinBorder(StructuredBlockForest& s, const IBlock* b) { return s.atDomainYMinBorder(*b); }
+bool SbF_atDomainYMaxBorder(StructuredBlockForest& s, const IBlock* b) { return s.atDomainYMaxBorder(*b); }
+bool SbF_atDomainZMinBorder(StructuredBlockForest& s, const IBlock* b) { return s.atDomainZMinBorder(*b); }
+bool SbF_atDomainZMaxBorder(StructuredBlockForest& s, const IBlock* b) { return s.atDomainZMaxBorder(*b); }
+
+void exportBlockForest(py::module_& m)
+{
+   using namespace pybind11::literals;
+
+   bool (StructuredBlockForest::*p_blockExists2)(const real_t, const real_t, const real_t) const =
+      &StructuredBlockForest::blockExists;
+   bool (StructuredBlockForest::*p_blockExistsLocally2)(const real_t, const real_t, const real_t) const =
+      &StructuredBlockForest::blockExistsLocally;
+   bool (StructuredBlockForest::*p_blockExistsRemotely2)(const real_t, const real_t, const real_t) const =
+      &StructuredBlockForest::blockExistsRemotely;
+
+   py::class_< StructuredBlockForest, std::shared_ptr< StructuredBlockForest > >(m, "StructuredBlockForest")
+      .def("getNumberOfLevels", &StructuredBlockForest::getNumberOfLevels)
+      .def_property_readonly("getDomain", &StructuredBlockForest::getDomain)
+      .def( "addBlockData", &StructuredBlockForest_addBlockData)
+      .def("mapToPeriodicDomain", &SbF_mapToPeriodicDomain1)
+      .def("mapToPeriodicDomain", &SbF_mapToPeriodicDomain2)
+      .def("mapToPeriodicDomain", &SbF_mapToPeriodicDomain3)
+      .def("__getitem__", &StructuredBlockForest_getItem, py::keep_alive< 0, 1 >())
+      .def("__len__", &StructuredBlockForest::size)
+      .def("getBlock", SbF_getBlock1, py::keep_alive< 0, 1 >())
+      .def("getBlock", SbF_getBlock2, py::keep_alive< 0, 1 >())
+      .def("containsGlobalBlockInformation", &StructuredBlockForest::containsGlobalBlockInformation)
+      .def("blocksOverlappedByAABB", &StructuredBlockForest_blocksOverlappedByAABB, py::keep_alive< 0, 1 >())
+      .def("blocksContainedWithinAABB", &StructuredBlockForest_blocksContainedWithinAABB, py::keep_alive< 0, 1 >())
+      .def("blockExists", &p_blockExists1)
+      .def("blockExists", p_blockExists2)
+      .def("blockExistsLocally", &p_blockExistsLocally1)
+      .def("blockExistsLocally", p_blockExistsLocally2)
+      .def("blockExistsRemotely", &p_blockExistsRemotely1)
+      .def("blockExistsRemotely", p_blockExistsRemotely2)
+      .def("atDomainXMinBorder", &SbF_atDomainXMinBorder)
+      .def("atDomainXMaxBorder", &SbF_atDomainXMaxBorder)
+      .def("atDomainYMinBorder", &SbF_atDomainYMinBorder)
+      .def("atDomainYMaxBorder", &SbF_atDomainYMaxBorder)
+      .def("atDomainZMinBorder", &SbF_atDomainZMinBorder)
+      .def("atDomainZMaxBorder", &SbF_atDomainZMaxBorder)
+      .def("dx", &StructuredBlockForest::dx)
+      .def("dy", &StructuredBlockForest::dy)
+      .def("dz", &StructuredBlockForest::dz)
+      .def("getDomainCellBB", &StructuredBlockForest::getDomainCellBB, "level"_a=0)
+      .def("getBlockCellBB", &SbF_getBlockCellBB)
+      .def("transformGlobalToLocal", &SbF_transformGlobalToLocal)
+      .def("transformLocalToGlobal", &SbF_transformLocalToGlobal)
+      .def("writeBlockData", &SbF_writeBlockData)
+      .def("readBlockData", &SbF_readBlockData)
+      .def("__iter__", &StructuredBlockForest_iter, py::keep_alive< 0, 1 >())
+      .def_property_readonly("containsGlobalBlockInformation", &StructuredBlockForest::containsGlobalBlockInformation)
+      .def_property_readonly("periodic", &SbF_periodic);
+
+   py::class_< SetupBlock, shared_ptr< SetupBlock > >(m, "SetupBlock")
+      .def("get_level", &SetupBlock::getLevel)
+      .def("set_workload", &SetupBlock::setWorkload)
+      .def("get_workload", &SetupBlock::getWorkload)
+      .def("set_memory", &SetupBlock::setMemory)
+      .def("get_memory", &SetupBlock::getMemory)
+      .def("get_aabb", &SetupBlock::getAABB)
+      .def("__repr__", &printSetupBlock);
+
+   m.def(
+      "createUniformBlockGrid",
+      [](std::array< uint_t, 3 > blocks, std::array< uint_t, 3 > cellsPerBlock, real_t dx,
+         bool oneBlockPerProcess, std::array< bool, 3 > periodic, bool keepGlobalBlockInformation) {
+         return blockforest::createUniformBlockGrid(blocks[0], blocks[1], blocks[2], cellsPerBlock[0], cellsPerBlock[1],
+                                                    cellsPerBlock[2], dx, oneBlockPerProcess, periodic[0], periodic[1], periodic[2],
+                                                    keepGlobalBlockInformation);
+      },
+      "blocks"_a, "cellsPerBlock"_a, "dx"_a = real_t(1), "oneBlockPerProcess"_a = true,
+      "periodic"_a = std::array< bool, 3 >{ false, false, false }, "keepGlobalBlockInformation"_a = false);
+
+    m.def(
+            "createUniformBlockGrid",
+            [](std::array< uint_t, 3 > cells, real_t dx,
+               bool oneBlockPerProcess, std::array< bool, 3 > periodic, bool keepGlobalBlockInformation) {
+                Vector3<uint_t> cellsVec(cells[0], cells[1], cells[2]);
+                Vector3<uint_t> cellsPerBlock;
+                Vector3<uint_t> blocks;
+                uint_t nrOfProcesses = uint_c( MPIManager::instance()->numProcesses() );
+
+                calculateCellDistribution( cellsVec, nrOfProcesses, blocks, cellsPerBlock );
+
+
+                return blockforest::createUniformBlockGrid(blocks[0], blocks[1], blocks[2], cellsPerBlock[0], cellsPerBlock[1],
+                                                           cellsPerBlock[2], dx, oneBlockPerProcess, periodic[0], periodic[1], periodic[2],
+                                                           keepGlobalBlockInformation);
+            },
+            "cells"_a,"dx"_a = real_t(1), "oneBlockPerProcess"_a = true,
+            "periodic"_a = std::array< bool, 3 >{ false, false, false }, "keepGlobalBlockInformation"_a = false);
+}
+
+} // namespace blockforest
+} // namespace walberla
+
+#endif // WALBERLA_BUILD_WITH_PYTHON
\ No newline at end of file
diff --git a/src/python_coupling/export/BlockForestExport.h b/src/python_coupling/export/BlockForestExport.h
new file mode 100644
index 0000000000000000000000000000000000000000..1c5ddaae4a53ae91ceb57a488351c782e16cc736
--- /dev/null
+++ b/src/python_coupling/export/BlockForestExport.h
@@ -0,0 +1,69 @@
+//======================================================================================================================
+//
+//  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 Exports.h
+//! \ingroup blockforest
+//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "waLBerlaDefinitions.h"
+
+
+#ifdef WALBERLA_BUILD_WITH_PYTHON
+
+#   include <pybind11/pybind11.h>
+
+#   include "BlockForestCommunicationExport.h"
+
+namespace walberla {
+namespace blockforest {
+
+   struct NoSuchBlockData : public std::out_of_range {
+      explicit NoSuchBlockData ( ) : std::out_of_range( "No blockdata with the given name found" ) {}
+      explicit NoSuchBlockData ( const std::string & w ) : std::out_of_range(w) {}
+      static void translate( const NoSuchBlockData & e );
+   };
+   struct BlockDataNotConvertible : public std::runtime_error {
+      explicit BlockDataNotConvertible (  ) : std::runtime_error( "This blockdata is not accessible from Python" ) {}
+      explicit BlockDataNotConvertible ( const std::string & w ) : std::runtime_error(w) {}
+      static void translate( const BlockDataNotConvertible &  e );
+   };
+
+   BlockDataID blockDataIDFromString( IBlock & block, const std::string & stringID );
+   BlockDataID blockDataIDFromString( BlockStorage & bs, const std::string & stringID );
+   BlockDataID blockDataIDFromString( StructuredBlockStorage & bs, const std::string & stringID );
+
+   namespace py = pybind11;
+
+   void exportBlockForest(py::module_ &m);
+
+
+   template<typename... Stencils>
+   void exportModuleToPython(py::module_ &m)
+   {
+      exportBlockForest(m);
+      exportUniformBufferedScheme<Stencils...>(m);
+      exportUniformDirectScheme<Stencils...>(m);
+   }
+
+} // namespace blockforest
+} // namespace walberla
+
+
+#endif // WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/cuda/python/Exports.h b/src/python_coupling/export/CUDAExport.h
similarity index 81%
rename from src/cuda/python/Exports.h
rename to src/python_coupling/export/CUDAExport.h
index 5e5b0009068b928d0124373122a78376801fc482..505aa3368e3008dec45eecf5acd9f3a231bc7b6e 100644
--- a/src/cuda/python/Exports.h
+++ b/src/python_coupling/export/CUDAExport.h
@@ -13,9 +13,10 @@
 //  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 FieldExport.h
+//! \file CUDAExport.h
 //! \ingroup cuda
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
@@ -28,14 +29,16 @@ namespace walberla {
 namespace cuda {
 
 
-   template<typename GpuFields, typename CpuFields>
-   void exportModuleToPython();
+   template<typename... GpuFields>
+   void exportModuleToPython(py::module_ &m);
+
+   template<typename... CpuFields>
+   void exportCopyFunctionsToPython(py::module_ &m);
 
 
 } // namespace cuda
 } // namespace walberla
 
-#include "Exports.impl.h"
-
+#include "CUDAExport.impl.h"
 
 #endif //WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/python_coupling/export/CUDAExport.impl.h b/src/python_coupling/export/CUDAExport.impl.h
new file mode 100644
index 0000000000000000000000000000000000000000..eb60759cbec17da1c81b52f80f31706068ea071f
--- /dev/null
+++ b/src/python_coupling/export/CUDAExport.impl.h
@@ -0,0 +1,394 @@
+//======================================================================================================================
+//
+//  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 CUDAExport.impl.h
+//! \ingroup cuda
+//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
+//
+//======================================================================================================================
+
+// Do not reorder includes - the include order is important
+#include "core/logging/Logging.h"
+
+#include "cuda/AddGPUFieldToStorage.h"
+#include "cuda/FieldCopy.h"
+#include "cuda/GPUField.h"
+#include "cuda/communication/GPUPackInfo.h"
+
+#include "field/AddToStorage.h"
+#include "field/communication/UniformMPIDatatypeInfo.h"
+
+#include "python_coupling/PythonWrapper.h"
+#include "python_coupling/helper/MplHelpers.h"
+
+namespace walberla {
+namespace cuda {
+
+
+
+namespace internal {
+using namespace pybind11::literals;
+   //===================================================================================================================
+   //
+   //  Field export
+   //
+   //===================================================================================================================
+
+   template<typename GpuField_T>
+   uint64_t gpufield_ptr(const GpuField_T & gpuField)
+   {
+      return reinterpret_cast<uint64_t>(gpuField.pitchedPtr().ptr);
+      // return gpuField.pitchedPtr();
+   }
+
+   template<typename GpuField_T>
+   std::string gpufield_dtypeStr(const GpuField_T & )
+   {
+      return std::string(field::internal::PythonFormatString<typename GpuField_T::value_type>::get());
+   }
+
+   struct GpuFieldExporter
+   {
+      GpuFieldExporter(py::module_& m) : m_(m) {}
+      template< typename GpuField_T>
+      void operator() ( python_coupling::NonCopyableWrap<GpuField_T> ) const
+      {
+
+         typedef typename GpuField_T::value_type T;
+         std::string data_type_name = field::internal::PythonFormatString<T>::get();
+
+         std::string class_name = "GpuField_" + data_type_name;
+         py::class_<GpuField_T, shared_ptr<GpuField_T>>(m_, class_name.c_str() )
+            .def_property_readonly("layout",              &field::internal::field_layout            < GpuField_T > )
+            .def_property_readonly("size",                &field::internal::field_size              < GpuField_T > )
+            .def_property_readonly("sizeWithGhostLayers", &field::internal::field_sizeWithGhostLayer< GpuField_T > )
+            .def_property_readonly("allocSize",           &field::internal::field_allocSize         < GpuField_T > )
+            .def_property_readonly("strides",             &field::internal::field_strides           < GpuField_T > )
+            .def_property_readonly("offsets",             &field::internal::field_offsets           < GpuField_T > )
+            .def_property_readonly("ptr",                 &gpufield_ptr                             < GpuField_T > )
+            .def_property_readonly("dtypeStr",            &gpufield_dtypeStr                        < GpuField_T > )
+            .def_property_readonly("isPitchedMem",        &GpuField_T::isPitchedMem )
+            .def("swapDataPointers",    &field::internal::field_swapDataPointers  < GpuField_T > )
+            .def_property_readonly("nrOfGhostLayers",     &GpuField_T::nrOfGhostLayers )
+            .def("cloneUninitialized",  &GpuField_T::cloneUninitialized, py::return_value_policy::copy)
+            ;
+
+
+         using field::communication::PackInfo;
+         using communication::GPUPackInfo;
+         std::string GpuFieldPackInfoName = "GpuFieldPackInfo_" + data_type_name;
+         py::class_< GPUPackInfo<GpuField_T>, shared_ptr< GPUPackInfo<GpuField_T> >, walberla::communication::UniformPackInfo>(m_, GpuFieldPackInfoName.c_str() );
+
+         using field::communication::UniformMPIDatatypeInfo;
+         std::string GpuFieldMPIDataTypeInfoName = "GpuFieldMPIDataTypeInfo_" + data_type_name;
+         py::class_< UniformMPIDatatypeInfo<GpuField_T>, shared_ptr< UniformMPIDatatypeInfo<GpuField_T> >, walberla::communication::UniformMPIDatatypeInfo>(m_, GpuFieldMPIDataTypeInfoName.c_str() );
+
+      }
+      const py::module_& m_;
+   };
+
+
+   //===================================================================================================================
+   //
+   //  addToStorage
+   //
+   //===================================================================================================================
+
+   class AddToStorageExporter
+   {
+   public:
+      AddToStorageExporter( const shared_ptr<StructuredBlockForest> & blocks, const std::string & name,
+                            py::object &dtype, uint_t fs, uint_t gl, Layout layout,
+                            bool usePitchedMem )
+         : blocks_( blocks ), name_( name ), dtype_(dtype), fs_( fs ),
+           gl_(gl),layout_( layout), usePitchedMem_(usePitchedMem), found_(true)
+      {}
+
+      template< typename GpuField_T>
+      void operator() ( python_coupling::NonCopyableWrap<GpuField_T> )
+      {
+         typedef typename GpuField_T::value_type T;
+
+         if(python_coupling::isCppEqualToPythonType<T>(py::cast<std::string>(dtype_.attr("__name__"))))
+         {
+            addGPUFieldToStorage< GPUField< T > >(blocks_, name_, fs_, layout_, gl_, usePitchedMem_);
+         }
+      }
+
+      bool successful() const { return found_; }
+   private:
+      shared_ptr< StructuredBlockForest > blocks_;
+      std::string name_;
+      py::object dtype_;
+      uint_t fs_;
+      uint_t gl_;
+      Layout layout_;
+      bool usePitchedMem_;
+      bool found_;
+   };
+
+   template<typename... GpuFields>
+   void addToStorage( const shared_ptr<StructuredBlockForest> & blocks, const std::string & name, py::object &dtype,
+                      uint_t fs, uint_t gl, Layout layout, bool usePitchedMem )
+   {
+      namespace py = pybind11;
+      auto result = make_shared<py::object>();
+      AddToStorageExporter exporter( blocks, name, dtype, fs, gl, layout, usePitchedMem );
+      python_coupling::for_each_noncopyable_type<GpuFields...>( std::ref(exporter) );
+   }
+
+
+   //===================================================================================================================
+   //
+   //  createPackInfo Export
+   //
+   //===================================================================================================================
+
+   class PackInfoExporter
+   {
+    public:
+      PackInfoExporter(const shared_ptr<StructuredBlockForest> & blocks, BlockDataID fieldId, uint_t numberOfGhostLayers)
+         : blocks_(blocks), fieldId_(fieldId), numberOfGhostLayers_( numberOfGhostLayers )
+      {}
+
+      template< typename GpuField_T>
+      void operator() ( python_coupling::NonCopyableWrap<GpuField_T> )
+      {
+         using cuda::communication::GPUPackInfo;
+
+         IBlock * firstBlock =  & ( * blocks_->begin() );
+         if( firstBlock->isDataClassOrSubclassOf<GpuField_T>(fieldId_) )
+         {
+            if ( numberOfGhostLayers_ > 0  )
+            {
+               resultPackInfo_ = py::cast(make_shared< GPUPackInfo< GpuField_T > >(fieldId_, numberOfGhostLayers_));
+            }
+            else
+            {
+               resultPackInfo_ = py::cast(make_shared< GPUPackInfo< GpuField_T > >(fieldId_));
+            }
+         }
+      }
+      py::object getResultPackInfo()
+      {
+         return resultPackInfo_;
+      }
+
+    private:
+      py::object resultPackInfo_;
+      shared_ptr< StructuredBlockStorage > blocks_;
+      BlockDataID fieldId_;
+      uint_t numberOfGhostLayers_;
+   };
+
+
+   template<typename... GpuField_T>
+   static py::object PackInfoWrapper(const shared_ptr<StructuredBlockForest> & blocks,
+                                     const std::string & name, uint_t numberOfGhostLayers )
+   {
+      using cuda::communication::GPUPackInfo;
+      BlockDataID fieldID = python_coupling::blockDataIDFromString( *blocks, name );
+
+      if ( blocks->begin() == blocks->end() ) {
+         // if no blocks are on this field an arbitrary PackInfo can be returned
+         return py::cast( make_shared< GPUPackInfo<GPUField<int8_t>> >( fieldID, numberOfGhostLayers ) );
+      }
+
+      PackInfoExporter exporter(blocks, fieldID, numberOfGhostLayers);
+      python_coupling::for_each_noncopyable_type< GpuField_T... >  ( std::ref(exporter) );
+      if ( ! exporter.getResultPackInfo() ) {
+         throw py::value_error("Failed to create GPU PackInfo");
+      }
+      else {
+         return exporter.getResultPackInfo();
+      }
+   }
+
+   //===================================================================================================================
+   //
+   //  createMPIDatatypeInfo
+   //
+   //===================================================================================================================
+
+   class UniformMPIDatatypeInfoExporter
+   {
+    public:
+      UniformMPIDatatypeInfoExporter(const shared_ptr<StructuredBlockForest> & blocks, BlockDataID fieldId, uint_t numberOfGhostLayers)
+         : blocks_(blocks), fieldId_(fieldId), numberOfGhostLayers_( numberOfGhostLayers )
+      {}
+
+      template< typename GpuField_T>
+      void operator() ( python_coupling::NonCopyableWrap<GpuField_T> )
+      {
+         using field::communication::UniformMPIDatatypeInfo;
+         IBlock * firstBlock =  & ( * blocks_->begin() );
+         if( firstBlock->isDataClassOrSubclassOf<GpuField_T>(fieldId_) )
+         {
+            if ( numberOfGhostLayers_ > 0  )
+               resultMPIDatatypeInfo_ =  py::cast( make_shared< UniformMPIDatatypeInfo<GpuField_T> >( fieldId_, numberOfGhostLayers_ ) );
+            else
+               resultMPIDatatypeInfo_ =  py::cast( make_shared< UniformMPIDatatypeInfo<GpuField_T> >( fieldId_ ) );
+
+         }
+      }
+      py::object getResultUniformMPIDatatype()
+      {
+         return resultMPIDatatypeInfo_;
+      }
+
+    private:
+      py::object resultMPIDatatypeInfo_;
+      shared_ptr< StructuredBlockStorage > blocks_;
+      BlockDataID fieldId_;
+      uint_t numberOfGhostLayers_;
+   };
+
+
+   template<typename... GpuField_T>
+   static py::object UniformMPIDatatypeInfoWrapper(const shared_ptr<StructuredBlockForest> & blocks,
+                                                   const std::string & name, uint_t numberOfGhostLayers )
+   {
+      using field::communication::UniformMPIDatatypeInfo;
+      BlockDataID fieldID = python_coupling::blockDataIDFromString( *blocks, name );
+
+      if ( blocks->begin() == blocks->end() ) {
+         // if no blocks are on this field an arbitrary PackInfo can be returned
+         return py::cast( make_shared< UniformMPIDatatypeInfo<GPUField<int8_t>> >( fieldID, numberOfGhostLayers ) );
+      }
+
+      UniformMPIDatatypeInfoExporter exporter(blocks, fieldID, numberOfGhostLayers);
+      python_coupling::for_each_noncopyable_type< GpuField_T... >  ( std::ref(exporter) );
+      if ( ! exporter.getResultUniformMPIDatatype() ) {
+         throw py::value_error("Failed to create GPU UniformMPIDatatype");
+      }
+      else {
+         return exporter.getResultUniformMPIDatatype();
+      }
+   }
+
+   //===================================================================================================================
+   //
+   //  fieldCopy
+   //
+   //===================================================================================================================
+
+class copyFieldToGpuDispatchExporter
+{
+ public:
+   copyFieldToGpuDispatchExporter( const shared_ptr<StructuredBlockForest> & blocks,
+                                   BlockDataID gpuFieldId, BlockDataID cpuFieldId, bool toGPU)
+      : blocks_( blocks ), gpuFieldId_( gpuFieldId ), cpuFieldId_(cpuFieldId), toGPU_( toGPU )
+   {}
+
+   template< typename CpuField_T>
+   void operator() ( python_coupling::NonCopyableWrap<CpuField_T> )
+   {
+      typedef cuda::GPUField<typename CpuField_T::value_type> GpuField_T;
+      IBlock * firstBlock =  & ( * blocks_->begin() );
+
+      if(firstBlock->isDataClassOrSubclassOf< CpuField_T > ( cpuFieldId_ ) )
+      {
+         if(toGPU_)
+           cuda::fieldCpy<GpuField_T, CpuField_T>(blocks_, gpuFieldId_, cpuFieldId_);
+         else
+           cuda::fieldCpy<CpuField_T, GpuField_T>(blocks_, cpuFieldId_, gpuFieldId_);
+      }
+   }
+ private:
+   shared_ptr< StructuredBlockForest > blocks_;
+   BlockDataID gpuFieldId_;
+   BlockDataID cpuFieldId_;
+   bool toGPU_;
+};
+
+template<typename... CpuFields>
+void copyFieldToGPU(const shared_ptr< StructuredBlockForest > & blocks, const std::string & gpuFieldName,
+                    const std::string & cpuFieldName, bool toGPU )
+{
+   namespace py = pybind11;
+   auto result = make_shared<py::object>();
+
+   BlockDataID gpuFieldId = python_coupling::blockDataIDFromString( *blocks, gpuFieldName );
+   BlockDataID cpuFieldId = python_coupling::blockDataIDFromString( *blocks, cpuFieldName );
+
+   copyFieldToGpuDispatchExporter exporter( blocks, gpuFieldId, cpuFieldId, toGPU );
+   python_coupling::for_each_noncopyable_type<CpuFields...>( std::ref(exporter) );
+}
+} // namespace internal
+
+
+using namespace pybind11::literals;
+
+template<typename... GpuFields>
+void exportModuleToPython(py::module_ &m)
+{
+   py::module_ m2 = m.def_submodule("cuda", "Cuda Extension of the waLBerla python bindings");
+
+   python_coupling::for_each_noncopyable_type<GpuFields...>( internal::GpuFieldExporter(m2) );
+
+   m2.def(
+      "addGpuFieldToStorage",
+      [](const shared_ptr< StructuredBlockForest > & blocks, const std::string & name, py::object &dtype, uint_t fSize,
+         bool usePitchedMem, uint_t ghostLayers, Layout layout) {
+        return internal::addToStorage<GpuFields...>(blocks, name, dtype, fSize, ghostLayers, layout, usePitchedMem);
+      },
+      "blocks"_a, "name"_a, "dtype"_a, "fSize"_a=1, "usePitchedMem"_a=false, "ghostLayers"_a=uint(1), "layout"_a=zyxf);
+
+   m2.def(
+      "createPackInfo",
+      [](const shared_ptr<StructuredBlockForest> & blocks,
+         const std::string & blockDataName, uint_t numberOfGhostLayers ) {
+         return internal::PackInfoWrapper< GpuFields... >(blocks, blockDataName, numberOfGhostLayers);
+      },
+      "blocks"_a, "blockDataName"_a, "numberOfGhostLayers"_a = uint_t(0));
+
+   m2.def(
+      "createMPIDatatypeInfo",
+      [](const shared_ptr<StructuredBlockForest> & blocks,
+         const std::string & blockDataName, uint_t numberOfGhostLayers ) {
+        return internal::UniformMPIDatatypeInfoWrapper< GpuFields... >(blocks, blockDataName, numberOfGhostLayers);
+      },
+      "blocks"_a, "blockDataName"_a, "numberOfGhostLayers"_a = uint_t(0));
+
+}
+
+template<typename... CpuFields >
+void exportCopyFunctionsToPython(py::module_ &m)
+{
+     py::module_ m2 = m.def_submodule("cuda", "Cuda Extension of the waLBerla python bindings");
+
+   m2.def(
+      "copyFieldToGpu",
+      [](const shared_ptr< StructuredBlockForest > & blocks, const std::string & gpuFieldName, const std::string & cpuFieldName) {
+        return internal::copyFieldToGPU<CpuFields...>(blocks, gpuFieldName, cpuFieldName, true);
+      },
+      "blocks"_a, "gpuFieldName"_a, "cpuFieldName"_a);
+
+   m2.def(
+      "copyFieldToCpu",
+      [](const shared_ptr< StructuredBlockForest > & blocks, const std::string & gpuFieldName, const std::string & cpuFieldName) {
+        return internal::copyFieldToGPU<CpuFields...>(blocks, gpuFieldName, cpuFieldName, false);
+      },
+      "blocks"_a, "gpuFieldName"_a, "cpuFieldName"_a);
+}
+
+
+
+
+} // namespace cuda
+} // namespace walberla
+
+
diff --git a/src/field/python/CommunicationExport.h b/src/python_coupling/export/FieldCommunicationExport.h
similarity index 85%
rename from src/field/python/CommunicationExport.h
rename to src/python_coupling/export/FieldCommunicationExport.h
index 8b76146a630c15bcb5b90ea1dc2c2e23a2abc51b..ccbe61496b8cedcda9ca502355defe890e6c13da 100644
--- a/src/field/python/CommunicationExport.h
+++ b/src/python_coupling/export/FieldCommunicationExport.h
@@ -16,6 +16,7 @@
 //! \file CommunicationExport.h
 //! \ingroup field
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
@@ -26,17 +27,17 @@
 
 namespace walberla {
 namespace field {
+namespace py = pybind11;
 
 
-   template<typename FieldTypes>
-   void exportCommunicationClasses();
+   template<typename... FieldTypes>
+   void exportCommunicationClasses(py::module_ &m);
 
 
 } // namespace field
 } // namespace walberla
 
 
-#include "CommunicationExport.impl.h"
-
+#include "FieldCommunicationExport.impl.h"
 
 #endif //WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/python_coupling/export/FieldCommunicationExport.impl.h b/src/python_coupling/export/FieldCommunicationExport.impl.h
new file mode 100644
index 0000000000000000000000000000000000000000..57289631d981822f5ba10c5111279c5da1d79337
--- /dev/null
+++ b/src/python_coupling/export/FieldCommunicationExport.impl.h
@@ -0,0 +1,370 @@
+//======================================================================================================================
+//
+//  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 CommunicationExport.impl.h
+//! \ingroup field
+//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
+//
+//======================================================================================================================
+
+#include "python_coupling/PythonWrapper.h"
+
+#ifdef WALBERLA_BUILD_WITH_PYTHON
+
+#   include "blockforest/StructuredBlockForest.h"
+#   include "python_coupling/helper/BlockStorageExportHelpers.h"
+
+#   include "field/communication/PackInfo.h"
+#   include "field/communication/StencilRestrictedPackInfo.h"
+#   include "field/communication/UniformMPIDatatypeInfo.h"
+
+#   include "python_coupling/helper/MplHelpers.h"
+
+#   include "stencil/D2Q9.h"
+#   include "stencil/D3Q15.h"
+#   include "stencil/D3Q19.h"
+#   include "stencil/D3Q27.h"
+#   include "stencil/D3Q7.h"
+
+#  include <typeinfo>
+
+#  include "pybind11/pybind11.h"
+
+namespace walberla
+{
+namespace field
+{
+namespace internal
+{
+namespace py = pybind11;
+
+//===================================================================================================================
+//
+//  createStencilRestrictedPackInfo Export
+//
+//===================================================================================================================
+
+template< typename FieldType >
+typename std::enable_if<FieldType::F_SIZE == 27, py::object>::type
+createStencilRestrictedPackInfoObject( BlockDataID bdId )
+{
+   typedef GhostLayerField<typename FieldType::value_type, 27> GlField_T;
+   using field::communication::StencilRestrictedPackInfo;
+   return py::cast( make_shared< StencilRestrictedPackInfo<GlField_T, stencil::D3Q27> >( bdId) );
+}
+
+template< typename FieldType >
+typename std::enable_if<FieldType::F_SIZE == 19, py::object>::type
+createStencilRestrictedPackInfoObject( BlockDataID bdId )
+{
+   typedef GhostLayerField<typename FieldType::value_type, 19> GlField_T;
+   using field::communication::StencilRestrictedPackInfo;
+   return py::cast( make_shared< StencilRestrictedPackInfo<GlField_T, stencil::D3Q19> >( bdId) );
+}
+
+template< typename FieldType >
+typename std::enable_if<FieldType::F_SIZE == 15, py::object>::type
+createStencilRestrictedPackInfoObject( BlockDataID bdId )
+{
+   typedef GhostLayerField<typename FieldType::value_type, 15> GlField_T;
+   using field::communication::StencilRestrictedPackInfo;
+   return py::cast( make_shared< StencilRestrictedPackInfo<GlField_T, stencil::D3Q15> >( bdId) );
+}
+
+template< typename FieldType >
+typename std::enable_if<FieldType::F_SIZE == 7, py::object>::type
+createStencilRestrictedPackInfoObject( BlockDataID bdId )
+{
+   typedef GhostLayerField<typename FieldType::value_type, 7> GlField_T;
+   using field::communication::StencilRestrictedPackInfo;
+   return py::cast( make_shared< StencilRestrictedPackInfo<GlField_T, stencil::D3Q7> >( bdId) );
+}
+
+template< typename FieldType >
+typename std::enable_if<FieldType::F_SIZE == 9, py::object>::type
+createStencilRestrictedPackInfoObject( BlockDataID bdId )
+{
+   typedef GhostLayerField<typename FieldType::value_type, 9> GlField_T;
+   using field::communication::StencilRestrictedPackInfo;
+   return py::cast( make_shared< StencilRestrictedPackInfo<GlField_T, stencil::D2Q9> >( bdId) );
+}
+
+template< typename FieldType >
+typename std::enable_if<!(FieldType::F_SIZE == 9  ||
+                          FieldType::F_SIZE == 7  ||
+                          FieldType::F_SIZE == 15 ||
+                          FieldType::F_SIZE == 19 ||
+                          FieldType::F_SIZE == 27), py::object>::type
+createStencilRestrictedPackInfoObject( BlockDataID )
+{
+   throw py::value_error("This works only for fields with fSize in 7, 9, 15, 19 or 27");
+}
+
+class StencilRestrictedPackInfoExporter
+{
+ public:
+   StencilRestrictedPackInfoExporter(const shared_ptr<StructuredBlockForest> & blocks, BlockDataID fieldId)
+      : blocks_(blocks), fieldId_(fieldId)
+   {}
+
+   template< typename FieldType>
+   void operator() ( python_coupling::NonCopyableWrap<FieldType> )
+   {
+      typedef typename FieldType::value_type T;
+      const uint_t F_SIZE = FieldType::F_SIZE;
+      typedef GhostLayerField<T, F_SIZE> GlField_T;
+      IBlock * firstBlock =  & ( * blocks_->begin() );
+      if( firstBlock->isDataClassOrSubclassOf<FieldType>(fieldId_) )
+      {
+         resultStencilRestrictedPackInfo_ = createStencilRestrictedPackInfoObject<GlField_T>(fieldId_);
+      }
+   }
+   py::object getResultStencilRestrictedPackInfo()
+   {
+      return resultStencilRestrictedPackInfo_;
+   }
+
+ private:
+   py::object resultStencilRestrictedPackInfo_;
+   shared_ptr< StructuredBlockStorage > blocks_;
+   BlockDataID fieldId_;
+};
+
+template<typename... FieldTypes>
+static py::object StencilRestrictedPackInfoWrapper(const shared_ptr<StructuredBlockForest> & blocks,
+                                                   const std::string & blockDataName )
+{
+   BlockDataID fieldID = python_coupling::blockDataIDFromString( *blocks, blockDataName );
+
+   if ( blocks->begin() == blocks->end() ) {
+      // if no blocks are on this field an arbitrary PackInfo can be returned
+      return py::cast( make_shared< field::communication::StencilRestrictedPackInfo<GhostLayerField<real_t, 9>, stencil::D2Q9> >( fieldID ) );
+   }
+
+   StencilRestrictedPackInfoExporter exporter(blocks, fieldID);
+   python_coupling::for_each_noncopyable_type< FieldTypes... >  ( std::ref(exporter) );
+   if ( ! exporter.getResultStencilRestrictedPackInfo() ) {
+      throw py::value_error("Failed to create Stencil Restricted PackInfo");
+   }
+   else {
+      return exporter.getResultStencilRestrictedPackInfo();
+   }
+}
+
+//===================================================================================================================
+//
+//  createPackInfo Export
+//
+//===================================================================================================================
+
+class PackInfoExporter
+{
+ public:
+   PackInfoExporter(const shared_ptr<StructuredBlockForest> & blocks, BlockDataID fieldId, uint_t numberOfGhostLayers)
+      : blocks_(blocks), fieldId_(fieldId), numberOfGhostLayers_( numberOfGhostLayers )
+   {}
+
+   template< typename FieldType>
+   void operator() ( python_coupling::NonCopyableWrap<FieldType> )
+   {
+      typedef typename FieldType::value_type T;
+      const uint_t F_SIZE = FieldType::F_SIZE;
+      typedef GhostLayerField<T, F_SIZE> GlField_T;
+      IBlock * firstBlock =  & ( * blocks_->begin() );
+      if( firstBlock->isDataClassOrSubclassOf<FieldType>(fieldId_) )
+      {
+         if ( numberOfGhostLayers_ > 0  )
+         {
+            resultPackInfo_ = py::cast(make_shared< field::communication::PackInfo< GlField_T > >(fieldId_, numberOfGhostLayers_));
+         }
+         else
+         {
+            resultPackInfo_ = py::cast(make_shared< field::communication::PackInfo< GlField_T > >(fieldId_));
+         }
+      }
+   }
+   py::object getResultPackInfo()
+   {
+      return resultPackInfo_;
+   }
+
+ private:
+   py::object resultPackInfo_;
+   shared_ptr< StructuredBlockStorage > blocks_;
+   BlockDataID fieldId_;
+   uint_t numberOfGhostLayers_;
+};
+
+
+template<typename... FieldTypes>
+static py::object PackInfoWrapper(const shared_ptr<StructuredBlockForest> & blocks,
+                                  const std::string & name, uint_t numberOfGhostLayers )
+{
+   BlockDataID fieldID = python_coupling::blockDataIDFromString( *blocks, name );
+
+   if ( blocks->begin() == blocks->end() ) {
+      // if no blocks are on this field an arbitrary PackInfo can be returned
+      return py::cast( make_shared< field::communication::PackInfo<GhostLayerField<real_t,1>> >( fieldID, numberOfGhostLayers ) );
+   }
+
+   PackInfoExporter exporter(blocks, fieldID, numberOfGhostLayers);
+   python_coupling::for_each_noncopyable_type< FieldTypes... >  ( std::ref(exporter) );
+   if ( ! exporter.getResultPackInfo() ) {
+      throw py::value_error("Failed to create PackInfo");
+   }
+   else {
+      return exporter.getResultPackInfo();
+   }
+}
+
+//===================================================================================================================
+//
+//  createMPIDatatypeInfo
+//
+//===================================================================================================================
+
+class UniformMPIDatatypeInfoExporter
+{
+ public:
+   UniformMPIDatatypeInfoExporter(const shared_ptr<StructuredBlockForest> & blocks, BlockDataID fieldId, uint_t numberOfGhostLayers)
+      : blocks_(blocks), fieldId_(fieldId), numberOfGhostLayers_( numberOfGhostLayers )
+   {}
+
+   template< typename FieldType>
+   void operator() ( python_coupling::NonCopyableWrap<FieldType> )
+   {
+      typedef typename FieldType::value_type T;
+      const uint_t F_SIZE = FieldType::F_SIZE;
+      typedef GhostLayerField<T, F_SIZE> GlField_T;
+      IBlock * firstBlock =  & ( * blocks_->begin() );
+      if( firstBlock->isDataClassOrSubclassOf<FieldType>(fieldId_) )
+      {
+         if ( numberOfGhostLayers_ > 0  )
+            resultMPIDatatypeInfo_ =  py::cast( make_shared< field::communication::UniformMPIDatatypeInfo<GlField_T> >( fieldId_, numberOfGhostLayers_ ) );
+         else
+            resultMPIDatatypeInfo_ =  py::cast( make_shared< field::communication::UniformMPIDatatypeInfo<GlField_T> >( fieldId_ ) );
+
+      }
+   }
+   py::object getResultUniformMPIDatatype()
+   {
+      return resultMPIDatatypeInfo_;
+   }
+
+ private:
+   py::object resultMPIDatatypeInfo_;
+   shared_ptr< StructuredBlockStorage > blocks_;
+   BlockDataID fieldId_;
+   uint_t numberOfGhostLayers_;
+};
+
+
+template<typename... FieldTypes>
+static py::object UniformMPIDatatypeInfoWrapper(const shared_ptr<StructuredBlockForest> & blocks,
+                                  const std::string & name, uint_t numberOfGhostLayers )
+{
+   BlockDataID fieldID = python_coupling::blockDataIDFromString( *blocks, name );
+
+   if ( blocks->begin() == blocks->end() ) {
+      // if no blocks are on this field an arbitrary PackInfo can be returned
+      return py::cast( make_shared< field::communication::UniformMPIDatatypeInfo<GhostLayerField<real_t,1>> >( fieldID, numberOfGhostLayers ) );
+   }
+
+   UniformMPIDatatypeInfoExporter exporter(blocks, fieldID, numberOfGhostLayers);
+   python_coupling::for_each_noncopyable_type< FieldTypes... >  ( std::ref(exporter) );
+   if ( ! exporter.getResultUniformMPIDatatype() ) {
+      throw py::value_error("Failed to create UniformMPIDatatype");
+   }
+   else {
+      return exporter.getResultUniformMPIDatatype();
+   }
+}
+
+//===================================================================================================================
+//
+//  exportStencilRestrictedPackInfo
+//
+//===================================================================================================================
+
+template< typename T >
+void exportStencilRestrictedPackInfo(py::module_& m)
+{
+   using field::communication::StencilRestrictedPackInfo;
+   {
+      typedef StencilRestrictedPackInfo< GhostLayerField< T, 9 >, stencil::D2Q9 > Pi;
+      py::class_< Pi, shared_ptr< Pi >, walberla::communication::UniformPackInfo >(m, "StencilRestrictedPackInfo_D2Q9");
+   }
+   {
+      typedef StencilRestrictedPackInfo< GhostLayerField< T, 7 >, stencil::D3Q7 > Pi;
+      py::class_< Pi, shared_ptr< Pi >, walberla::communication::UniformPackInfo >(m, "StencilRestrictedPackInfo_D3Q7");
+   }
+   {
+      typedef StencilRestrictedPackInfo< GhostLayerField< T, 15 >, stencil::D3Q15 > Pi;
+      py::class_< Pi, shared_ptr< Pi >, walberla::communication::UniformPackInfo >(m,
+                                                                                   "StencilRestrictedPackInfo_D3Q15");
+   }
+   {
+      typedef StencilRestrictedPackInfo< GhostLayerField< T, 19 >, stencil::D3Q19 > Pi;
+      py::class_< Pi, shared_ptr< Pi >, walberla::communication::UniformPackInfo >(m,
+                                                                                   "StencilRestrictedPackInfo_D3Q19");
+   }
+   {
+      typedef StencilRestrictedPackInfo< GhostLayerField< T, 27 >, stencil::D3Q27 > Pi;
+      py::class_< Pi, shared_ptr< Pi >, walberla::communication::UniformPackInfo >(m,
+                                                                                   "StencilRestrictedPackInfo_D3Q27");
+   }
+}
+
+} // namespace internal
+
+namespace py = pybind11;
+using namespace pybind11::literals;
+
+template< typename... FieldTypes >
+void exportCommunicationClasses(py::module_& m)
+{
+   py::module_ m2 = m.def_submodule("field", "Field Extension of the waLBerla python bindings");
+   internal::exportStencilRestrictedPackInfo< real_t >(m2);
+
+   m2.def(
+      "createPackInfo",
+      [](const shared_ptr<StructuredBlockForest> & blocks,
+         const std::string & blockDataName, uint_t numberOfGhostLayers ) {
+        return internal::PackInfoWrapper< FieldTypes... >(blocks, blockDataName, numberOfGhostLayers);
+      },
+      "blocks"_a, "blockDataName"_a, "numberOfGhostLayers"_a = uint_t(0));
+
+   m2.def(
+      "createMPIDatatypeInfo",
+      [](const shared_ptr<StructuredBlockForest> & blocks,
+         const std::string & blockDataName, uint_t numberOfGhostLayers ) {
+        return internal::UniformMPIDatatypeInfoWrapper< FieldTypes... >(blocks, blockDataName, numberOfGhostLayers);
+      },
+      "blocks"_a, "blockDataName"_a, "numberOfGhostLayers"_a = uint_t(0));
+
+   m2.def(
+      "createStencilRestrictedPackInfo",
+      [](const shared_ptr<StructuredBlockForest> & blocks,
+         const std::string & blockDataName) {
+        return internal::StencilRestrictedPackInfoWrapper< FieldTypes... >(blocks, blockDataName);
+      },
+      "blocks"_a, "blockDataName"_a);
+}
+
+} // namespace field
+} // namespace walberla
+
+#endif // WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/python_coupling/export/FieldExport.impl.h b/src/python_coupling/export/FieldExport.impl.h
new file mode 100644
index 0000000000000000000000000000000000000000..ad9449849a9c556ec6edbec0e6dacac0a507c036
--- /dev/null
+++ b/src/python_coupling/export/FieldExport.impl.h
@@ -0,0 +1,664 @@
+//======================================================================================================================
+//
+//  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 FieldExport.impl.h
+//! \ingroup field
+//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
+//
+//======================================================================================================================
+#include "core/VectorTrait.h"
+#include "core/logging/Logging.h"
+
+#include "field/AddToStorage.h"
+#include "field/Field.h"
+#include "field/FlagField.h"
+#include "field/GhostLayerField.h"
+#include "field/communication/PackInfo.h"
+#include "field/communication/UniformMPIDatatypeInfo.h"
+#include "field/vtk/FlagFieldMapping.h"
+#include "field/vtk/VTKWriter.h"
+
+#include "python_coupling/PythonWrapper.h"
+#include "python_coupling/helper/MplHelpers.h"
+#include "python_coupling/helper/PybindHelper.h"
+
+#include <iostream>
+#include <type_traits>
+
+#include "GatherExport.impl.h"
+#include "pybind11/numpy.h"
+#include <pybind11/stl.h>
+
+namespace walberla
+{
+namespace field
+{
+
+//*******************************************************************************************************************
+/*! Exports all Fields given in the Sequence
+*
+* Put only Fields in the sequence! The corresponding GhostLayerFields are exported automatically
+*
+* \warning Make sure that the same adaptor type is exported only once!
+*/
+//*******************************************************************************************************************
+template<typename... FieldTypes >
+void exportFields();
+
+
+
+//*******************************************************************************************************************
+/*! Exports all GhostLayerFieldAdaptors given in the Sequence
+*
+* \warning Make sure that the same adaptor type is exported only once!
+*/
+//*******************************************************************************************************************
+template<typename... AdaptorTypes>
+void exportGhostLayerFieldAdaptors();
+
+template<typename AdaptorType>
+void exportGhostLayerFieldAdaptor();
+
+namespace internal
+{
+namespace py = pybind11;
+
+template<class T> struct PythonFormatString                    { inline static char * get() { static char value [] = "B"; return value; } };
+
+template<>        struct PythonFormatString<double>            { inline static char * get() { static char value [] = "d"; return value; } };
+template<>        struct PythonFormatString<float>             { inline static char * get() { static char value [] = "f"; return value; } };
+template<>        struct PythonFormatString<unsigned short>    { inline static char * get() { static char value [] = "H"; return value; } };
+template<>        struct PythonFormatString<int>               { inline static char * get() { static char value [] = "i"; return value; } };
+template<>        struct PythonFormatString<unsigned int>      { inline static char * get() { static char value [] = "I"; return value; } };
+template<>        struct PythonFormatString<long>              { inline static char * get() { static char value [] = "l"; return value; } };
+template<>        struct PythonFormatString<unsigned long>     { inline static char * get() { static char value [] = "L"; return value; } };
+template<>        struct PythonFormatString<long long>         { inline static char * get() { static char value [] = "q"; return value; } };
+template<>        struct PythonFormatString<unsigned long long>{ inline static char * get() { static char value [] = "Q"; return value; } };
+template<>        struct PythonFormatString<int8_t>            { inline static char * get() { static char value [] = "c"; return value; } };
+template<>        struct PythonFormatString<int16_t>           { inline static char * get() { static char value [] = "h"; return value; } };
+template<>        struct PythonFormatString<uint8_t>           { inline static char * get() { static char value [] = "C"; return value; } };
+
+//===================================================================================================================
+//
+//  Aligned Allocation
+//
+//===================================================================================================================
+
+template< typename T >
+shared_ptr< field::FieldAllocator< T > > getAllocator(uint_t alignment)
+{
+   if (alignment == 0)
+      return shared_ptr< field::FieldAllocator< T > >(); // leave to default - auto-detection of alignment
+   else if (alignment == 16)
+      return make_shared< field::AllocateAligned< T, 16 > >();
+   else if (alignment == 32)
+      return make_shared< field::AllocateAligned< T, 32 > >();
+   else if (alignment == 64)
+      return make_shared< field::AllocateAligned< T, 64 > >();
+   else if (alignment == 128)
+      return make_shared< field::AllocateAligned< T, 128 > >();
+   else
+   {
+      throw py::value_error("Alignment parameter has to be one of 0, 16, 32, 64, 128.");
+      return shared_ptr< field::FieldAllocator< T > >();
+   }
+}
+
+template< typename GhostLayerField_T >
+class GhostLayerFieldDataHandling : public field::BlockDataHandling< GhostLayerField_T >
+{
+ public:
+   typedef typename GhostLayerField_T::value_type Value_T;
+
+   GhostLayerFieldDataHandling(const weak_ptr< StructuredBlockStorage >& blocks, const uint_t nrOfGhostLayers,
+                               const Value_T& initValue, const Layout layout, uint_t alignment = 0)
+      : blocks_(blocks), nrOfGhostLayers_(nrOfGhostLayers), initValue_(initValue), layout_(layout),
+        alignment_(alignment)
+   {}
+
+   GhostLayerField_T* allocate(IBlock* const block)
+   {
+      auto blocks = blocks_.lock();
+      WALBERLA_CHECK_NOT_NULLPTR(blocks, "Trying to access 'AlwaysInitializeBlockDataHandling' for a block "
+                                         "storage object that doesn't exist anymore");
+      GhostLayerField_T* field = new GhostLayerField_T(
+         blocks->getNumberOfXCells(*block), blocks->getNumberOfYCells(*block), blocks->getNumberOfZCells(*block),
+         nrOfGhostLayers_, initValue_, layout_, getAllocator< Value_T >(alignment_));
+      return field;
+   }
+
+   GhostLayerField_T* reallocate(IBlock* const block) { return allocate(block); }
+
+ private:
+   weak_ptr< StructuredBlockStorage > blocks_;
+
+   uint_t nrOfGhostLayers_;
+   Value_T initValue_;
+   Layout layout_;
+   uint_t alignment_;
+};
+
+//===================================================================================================================
+//
+//  Field functions redefined for easier export
+//
+//===================================================================================================================
+
+template< typename Field_T >
+py::object field_size(const Field_T& field)
+{
+   return py::make_tuple(field.xSize(), field.ySize(), field.zSize(), field.fSize());
+}
+
+template< typename GlField_T >
+py::tuple field_sizeWithGhostLayer(const GlField_T& field)
+{
+   return py::make_tuple(field.xSizeWithGhostLayer(), field.ySizeWithGhostLayer(), field.zSizeWithGhostLayer(),
+                         field.fSize());
+}
+
+template< typename Field_T >
+py::tuple field_allocSize(const Field_T& field)
+{
+   return py::make_tuple(field.xAllocSize(), field.yAllocSize(), field.zAllocSize(), field.fAllocSize());
+}
+
+template< typename Field_T >
+py::tuple field_strides(const Field_T& field)
+{
+   return py::make_tuple(field.xStride(), field.yStride(), field.zStride(), field.fStride());
+}
+
+template< typename Field_T >
+py::tuple field_offsets(const Field_T& field)
+{
+   return py::make_tuple(field.xOff(), field.yOff(), field.zOff());
+}
+
+template< typename Field_T >
+py::object field_layout(const Field_T& f)
+{
+   return py::cast(f.layout());
+}
+
+template< typename Field_T >
+void field_swapDataPointers(Field_T& f1, Field_T& f2)
+{
+   if (!f1.hasSameAllocSize(f2) || !f1.hasSameSize(f2) || f1.layout() != f2.layout())
+   {
+      throw py::value_error("The data of fields with different sizes or layout cannot be swapped");
+   }
+   f1.swapDataPointers(f2);
+}
+
+template< typename Field_T >
+py::object copyAdaptorToField(const Field_T& f)
+{
+   typedef GhostLayerField< typename Field_T::value_type, Field_T::F_SIZE > ResField;
+   auto res = make_shared< ResField >(f.xSize(), f.ySize(), f.zSize(), f.nrOfGhostLayers());
+
+   auto srcIt = f.beginWithGhostLayerXYZ();
+   auto dstIt = res->beginWithGhostLayerXYZ();
+   while (srcIt != f.end())
+   {
+      for (cell_idx_t fCoord = 0; fCoord < cell_idx_c(Field_T::F_SIZE); ++fCoord)
+         dstIt.getF(fCoord) = srcIt.getF(fCoord);
+
+      ++srcIt;
+      ++dstIt;
+   }
+   return py::cast(res);
+}
+
+//===================================================================================================================
+//
+//  Field export
+//
+//===================================================================================================================
+template< typename Field_T >
+py::array_t< typename Field_T::value_type > toNumpyArray(const Field_T& field)
+{
+   using T    = typename Field_T::value_type;
+   const T* ptr = field.dataAt(0, 0, 0, 0);
+
+   if (field.fSize() == 1)
+   {
+      return pybind11::array_t< T, 0 >({ field.xSize(), field.ySize(), field.zSize() },
+                                       { static_cast< size_t >(field.xStride()) * sizeof(T),
+                                         static_cast< size_t >(field.yStride()) * sizeof(T),
+                                         static_cast< size_t >(field.zStride()) * sizeof(T) },
+                                       ptr, py::cast(field));
+   }
+   else
+   {
+      return pybind11::array_t< T, 0 >(
+         { field.xSize(), field.ySize(), field.zSize(), field.fSize() },
+         { static_cast< size_t >(field.xStride()) * sizeof(T), static_cast< size_t >(field.yStride()) * sizeof(T),
+           static_cast< size_t >(field.zStride()) * sizeof(T), static_cast< size_t >(field.fStride()) * sizeof(T) },
+         ptr, py::cast(field));
+   }
+}
+
+template< typename GlField_T >
+py::array_t< typename GlField_T::value_type > toNumpyArrayWithGhostLayers(const GlField_T& field)
+{
+   using T    = typename GlField_T::value_type;
+   const T* ptr     = field.dataAt(-static_cast< cell_idx_t >(field.nrOfGhostLayers()),
+                                   -static_cast< cell_idx_t >(field.nrOfGhostLayers()),
+                                   -static_cast< cell_idx_t >(field.nrOfGhostLayers()), 0);
+
+
+   if (field.fSize() == 1)
+   {
+      return pybind11::array_t< T, 0 >({ field.xSizeWithGhostLayer(), field.ySizeWithGhostLayer(), field.zSizeWithGhostLayer() },
+                                       { static_cast< size_t >(field.xStride()) * sizeof(T),
+                                         static_cast< size_t >(field.yStride()) * sizeof(T),
+                                         static_cast< size_t >(field.zStride()) * sizeof(T) },
+                                       ptr, py::cast(field));
+   }
+   else
+   {
+      return pybind11::array_t< T, 0 >(
+         { field.xSizeWithGhostLayer(), field.ySizeWithGhostLayer(), field.zSizeWithGhostLayer(), field.fSize() },
+         { static_cast< size_t >(field.xStride()) * sizeof(T), static_cast< size_t >(field.yStride()) * sizeof(T),
+           static_cast< size_t >(field.zStride()) * sizeof(T), static_cast< size_t >(field.fStride()) * sizeof(T) },
+         ptr, py::cast(field));
+   }
+}
+
+
+struct FieldExporter
+{
+   FieldExporter(py::module_& m) : m_(m) {}
+   template< typename FieldType >
+   void operator()(python_coupling::NonCopyableWrap< FieldType >) const
+   {
+      typedef typename FieldType::value_type T;
+      const uint_t F_SIZE = FieldType::F_SIZE;
+      typedef GhostLayerField< T, F_SIZE > GlField_T;
+      typedef Field< T, F_SIZE > Field_T;
+
+      std::string data_type_name = PythonFormatString<T>::get();
+
+      std::string class_name = "Field_" + data_type_name + "_" + std::to_string(FieldType::F_SIZE);
+
+      py::class_< Field_T, shared_ptr< Field_T > >(m_, class_name.c_str())
+         .def_property_readonly("layout", &field_layout< Field_T >)
+         .def_property_readonly("size", &field_size< Field_T >)
+         .def_property_readonly("allocSize", &field_allocSize< Field_T >)
+         .def_property_readonly("strides", &field_strides< Field_T >)
+         .def_property_readonly("offsets", &field_offsets< Field_T >)
+         .def("clone", &Field_T::clone, py::return_value_policy::copy)
+         .def("cloneUninitialized", &Field_T::cloneUninitialized, py::return_value_policy::copy)
+         .def("swapDataPointers", &field_swapDataPointers< Field_T >)
+         .def("__getitem__",        [](const Field_T& self, const py::object& index) {
+             return py::cast(self).attr("__array__")().attr("__getitem__")(index);
+         } )
+          .def("__setitem__",        [](const Field_T& self, const py::object& index,
+                                        const typename Field_T::value_type& value) {
+              py::cast(self).attr("__array__")().attr("__setitem__")(index, value);
+          } )
+         .def("__array__", &toNumpyArray< Field_T >);
+
+      std::string class_nameGL =
+         "GhostLayerField_" + data_type_name + "_" + std::to_string(FieldType::F_SIZE);
+
+      py::class_< GlField_T, shared_ptr< GlField_T >, Field_T >(m_, class_nameGL.c_str())
+         .def_property_readonly("sizeWithGhostLayer", &GlField_T::xSizeWithGhostLayer)
+         .def_property_readonly("nrOfGhostLayers", &GlField_T::nrOfGhostLayers)
+         .def("__array__", &toNumpyArrayWithGhostLayers< GlField_T >);
+
+      using field::communication::PackInfo;
+      std::string FieldPackInfo_name = "FieldPackInfo_" + data_type_name + "_" + std::to_string(FieldType::F_SIZE);
+      py::class_< PackInfo< GlField_T >, shared_ptr< PackInfo< GlField_T > >, walberla::communication::UniformPackInfo >(m_, FieldPackInfo_name.c_str());
+
+      using field::communication::UniformMPIDatatypeInfo;
+      std::string FieldMPIDataTypeInfo_name = "FieldMPIDataTypeInfo_" + data_type_name + "_" + std::to_string(FieldType::F_SIZE);
+      py::class_< UniformMPIDatatypeInfo< GlField_T >, shared_ptr< UniformMPIDatatypeInfo< GlField_T > >, walberla::communication::UniformMPIDatatypeInfo >(
+         m_, FieldMPIDataTypeInfo_name.c_str());
+   }
+   const py::module_& m_;
+};
+
+
+struct FieldAllocatorExporter
+{
+   FieldAllocatorExporter(py::module_& m) : m_(m) {}
+   template< typename T >
+   void operator()(python_coupling::NonCopyableWrap< T >) const
+   {
+      std::string data_type_name = PythonFormatString<T>::get();
+      std::string class_nameFieldAllocator = "FieldAllocator_" + data_type_name;
+      py::class_< FieldAllocator< T >, shared_ptr< FieldAllocator< T > > >(m_, class_nameFieldAllocator.c_str())
+         .def("incrementReferenceCount", &FieldAllocator< T >::incrementReferenceCount)
+         .def("decrementReferenceCount", &FieldAllocator< T >::decrementReferenceCount);
+   }
+   const py::module_& m_;
+};
+
+//===================================================================================================================
+//
+//  addToStorage
+//
+//===================================================================================================================
+
+class AddToStorageExporter
+{
+ public:
+   AddToStorageExporter(const shared_ptr< StructuredBlockForest >& blocks, const std::string& name, py::object& dtype, uint_t fs,
+                        uint_t gl, Layout layout, real_t initValue, uint_t alignment)
+      : blocks_(blocks), name_(name), dtype_(dtype), fs_(fs), gl_(gl), layout_(layout), initValue_(initValue), alignment_(alignment), found_(false)
+   {}
+
+   template< typename FieldType >
+   void operator()(python_coupling::NonCopyableWrap<FieldType>) const
+   {
+      using namespace py;
+      typedef typename FieldType::value_type T;
+      const uint_t F_SIZE = FieldType::F_SIZE;
+
+      if (F_SIZE != fs_) return;
+      if(python_coupling::isCppEqualToPythonType<T>(py::cast<std::string>(dtype_.attr("__name__"))))
+      {
+         typedef internal::GhostLayerFieldDataHandling< GhostLayerField< T, F_SIZE > > DataHandling;
+         auto dataHandling = walberla::make_shared< DataHandling >(blocks_, gl_, initValue_, layout_, alignment_);
+         blocks_->addBlockData(dataHandling, name_);
+      }
+      found_ = true;
+   }
+
+   bool successful() const { return found_; }
+
+ private:
+   shared_ptr< StructuredBlockStorage > blocks_;
+   std::string name_;
+   py::object dtype_;
+   uint_t fs_;
+   uint_t gl_;
+   Layout layout_;
+   real_t initValue_;
+   uint_t alignment_;
+   mutable bool found_;
+};
+
+template< typename... FieldTypes >
+void addToStorage(const shared_ptr< StructuredBlockForest >& blocks, const std::string& name, py::object& dtype,
+                  uint_t fs, uint_t gl, Layout layout, real_t initValue, uint_t alignment)
+{
+   using namespace py;
+
+   auto result = make_shared< py::object >();
+   AddToStorageExporter exporter(blocks, name, dtype, fs, gl, layout, initValue, alignment);
+   python_coupling::for_each_noncopyable_type< FieldTypes... >(exporter);
+
+   if (!exporter.successful())
+   {
+      throw py::value_error("Adding GhostLayerField failed. Maybe the data type and/or the fsize is not exported to python yet");
+   }
+}
+
+inline void addFlagFieldToStorage(const shared_ptr< StructuredBlockStorage >& blocks, const std::string& name,
+                                  uint_t nrOfBits, uint_t gl)
+{
+   if (nrOfBits == 8)
+      field::addFlagFieldToStorage< FlagField< uint8_t > >(blocks, name, gl);
+   else if (nrOfBits == 16)
+      field::addFlagFieldToStorage< FlagField< uint16_t > >(blocks, name, gl);
+   else if (nrOfBits == 32)
+      field::addFlagFieldToStorage< FlagField< uint32_t > >(blocks, name, gl);
+   else if (nrOfBits == 64)
+      field::addFlagFieldToStorage< FlagField< uint64_t > >(blocks, name, gl);
+   else
+   {
+      throw py::value_error("Allowed values for number of bits are: 8,16,32,64");
+   }
+}
+
+//===================================================================================================================
+//
+//  createField
+//
+//===================================================================================================================
+
+class CreateFieldExporter
+{
+ public:
+   CreateFieldExporter( uint_t xs, uint_t ys, uint_t zs, uint_t fs, uint_t gl,
+                        Layout layout, const py::object & dtype, uint_t alignment,
+                        const shared_ptr<py::object> & resultPointer  )
+      : xs_( xs ), ys_(ys), zs_(zs), fs_(fs), gl_(gl),
+        layout_( layout),  dtype_( dtype ), alignment_(alignment), resultPointer_( resultPointer )
+   {}
+
+   template< typename FieldType>
+   void operator() ( python_coupling::NonCopyableWrap<FieldType> ) const
+   {
+      typedef typename FieldType::value_type T;
+      const uint_t F_SIZE = FieldType::F_SIZE;
+
+      if( F_SIZE != fs_ )
+         return;
+
+      if(python_coupling::isCppEqualToPythonType<T>(py::cast<std::string>(dtype_.attr("__name__"))))
+      {
+         T initVal = T();
+         *resultPointer_ = py::cast( make_shared< GhostLayerField<T, F_SIZE> >( xs_,ys_,zs_, gl_, initVal, layout_,
+                                                                             getAllocator<T>(alignment_)));
+      }
+   }
+
+ private:
+   uint_t xs_;
+   uint_t ys_;
+   uint_t zs_;
+   uint_t fs_;
+   uint_t gl_;
+   Layout layout_;
+   py::object dtype_;
+   uint_t alignment_;
+   shared_ptr<py::object> resultPointer_;
+};
+
+template<typename... FieldTypes>
+py::object createPythonField( std::array< uint_t, 4 > size,
+                              py::object & dtype,
+                              uint_t ghostLayers,
+                              Layout layout,
+                              uint_t alignment)
+{
+   uint_t xSize = size[0];
+   uint_t ySize = size[1];
+   uint_t zSize = size[2];
+   uint_t fSize = size[3];
+
+   auto result = make_shared<py::none>();
+   CreateFieldExporter exporter( xSize,ySize, zSize, fSize, ghostLayers, layout, dtype, alignment, result );
+   python_coupling::for_each_noncopyable_type< FieldTypes... >  ( exporter );
+
+   return *result;
+}
+
+//===================================================================================================================
+//
+//  createVTKWriter
+//
+//===================================================================================================================
+
+class CreateVTKWriterExporter
+{
+ public:
+   CreateVTKWriterExporter( const shared_ptr<StructuredBlockForest> & blocks,
+                            ConstBlockDataID fieldId, const std::string & vtkName)
+      : blocks_( blocks ), fieldId_(fieldId), vtkName_( vtkName )
+   {}
+
+   template< typename FieldType>
+   void operator() ( python_coupling::NonCopyableWrap<FieldType> )
+   {
+      IBlock * firstBlock =  & ( * blocks_->begin() );
+      if( firstBlock->isDataClassOrSubclassOf<FieldType>(fieldId_) )
+         writer_ = shared_ptr<field::VTKWriter<FieldType> >( new field::VTKWriter<FieldType>(fieldId_, vtkName_));
+   }
+
+   shared_ptr< vtk::BlockCellDataWriterInterface > getCreatedWriter() {
+      return writer_;
+   }
+
+ private:
+   shared_ptr< vtk::BlockCellDataWriterInterface > writer_;
+   shared_ptr< StructuredBlockStorage > blocks_;
+   ConstBlockDataID fieldId_;
+   std::string vtkName_;
+};
+
+
+template<typename... FieldTypes>
+inline shared_ptr<vtk::BlockCellDataWriterInterface> createVTKWriter(const shared_ptr<StructuredBlockForest> & blocks,
+                                                                     const std::string & name,
+                                                                     const std::string & nameInVtkOutput = "")
+{
+   std::string vtkName = nameInVtkOutput;
+   if( vtkName.empty())
+      vtkName = name;
+
+   if ( blocks->begin() == blocks->end() )
+      return shared_ptr<vtk::BlockCellDataWriterInterface>();
+   auto fieldID = python_coupling::blockDataIDFromString( *blocks, name );
+
+   CreateVTKWriterExporter exporter(blocks, fieldID, vtkName);
+   python_coupling::for_each_noncopyable_type< FieldTypes... >  ( std::ref(exporter) );
+   if ( ! exporter.getCreatedWriter() ) {
+      throw py::value_error("Failed to create VTK writer");
+   }
+   else {
+      return exporter.getCreatedWriter();
+   }
+}
+
+
+//===================================================================================================================
+//
+//  createBinarizationFieldWriter
+//
+//===================================================================================================================
+
+class CreateBinarizationVTKWriterExporter
+{
+ public:
+   CreateBinarizationVTKWriterExporter( const shared_ptr<StructuredBlockStorage> & blocks,
+                                        ConstBlockDataID fieldId, const std::string & vtkName, uint_t mask)
+      : blocks_( blocks ), fieldId_(fieldId), vtkName_( vtkName ), mask_(mask)
+   {}
+
+   template< typename FieldType>
+   void operator() ( python_coupling::NonCopyableWrap<FieldType> )
+   {
+      IBlock * firstBlock =  & ( * blocks_->begin() );
+      if( firstBlock->isDataClassOrSubclassOf<FieldType>(fieldId_) )
+      {
+         typedef field::BinarizationFieldWriter< FieldType > Writer;
+         writer_ = shared_ptr< Writer >(new Writer(fieldId_, vtkName_, static_cast< typename FieldType::value_type >(mask_)));
+      }
+   }
+
+   shared_ptr< vtk::BlockCellDataWriterInterface > getCreatedWriter() {
+      return writer_;
+   }
+
+ private:
+   shared_ptr< vtk::BlockCellDataWriterInterface > writer_;
+   shared_ptr< StructuredBlockStorage > blocks_;
+   ConstBlockDataID fieldId_;
+   std::string vtkName_;
+   uint_t mask_;
+};
+
+
+template<typename... FieldTypes>
+inline shared_ptr<vtk::BlockCellDataWriterInterface> createBinarizationVTKWriter(const shared_ptr<StructuredBlockStorage> & blocks,
+                                                                                 const std::string & name,
+                                                                                 uint_t mask,
+                                                                                 const std::string & nameInVtkOutput = "")
+{
+   std::string vtkName = nameInVtkOutput;
+   if( vtkName.empty())
+      vtkName = name;
+
+   if ( blocks->begin() == blocks->end() )
+      return shared_ptr<vtk::BlockCellDataWriterInterface>();
+   auto fieldID = python_coupling::blockDataIDFromString( *blocks, name );
+
+   CreateBinarizationVTKWriterExporter exporter(blocks, fieldID, vtkName, mask);
+   python_coupling::for_each_noncopyable_type< FieldTypes... >  ( std::ref(exporter) );
+   if ( ! exporter.getCreatedWriter() ) {
+      throw py::value_error("Failed to create binarization field writer");
+   }
+   else {
+      return exporter.getCreatedWriter();
+   }
+}
+
+} // namespace internal
+
+namespace py = pybind11;
+template< typename... FieldTypes >
+void exportFields(py::module_& m)
+{
+   using namespace py;
+
+   py::module_ m2 = m.def_submodule("field", "Field Extension of the waLBerla python bindings");
+
+   py::enum_< Layout >(m2, "Layout").value("fzyx", fzyx).value("zyxf", zyxf).export_values();
+
+   python_coupling::for_each_noncopyable_type< FieldTypes... >(internal::FieldExporter(m2));
+   python_coupling::for_each_noncopyable_type< real_t, int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, uint32_t >(internal::FieldAllocatorExporter(m2));
+
+   m2.def(
+      "createField",
+      [](std::array< uint_t, 4 > size, py::object & dtype, uint_t ghostLayers, Layout layout, uint_t alignment) {
+        return internal::createPythonField< FieldTypes... >(size, dtype, ghostLayers, layout, alignment);
+      },
+      "size"_a, "dtype"_a, "ghostLayers"_a = uint_t(1), "layout"_a = zyxf, "alignment"_a = 0);
+
+   m2.def(
+      "addToStorage",
+      [](const shared_ptr< StructuredBlockForest > & blocks, const std::string & name, py::object &dtype, uint_t fSize,
+         Layout layout, uint_t ghostLayers, real_t initValue, uint_t alignment) {
+         return internal::addToStorage< FieldTypes... >(blocks, name, dtype, fSize, ghostLayers, layout, initValue, alignment);
+      },
+      "blocks"_a, "name"_a, "dtype"_a, "fSize"_a = 1, "layout"_a = zyxf, "ghostLayers"_a = uint_t(1), "initValue"_a = 0.0, "alignment"_a = 0);
+
+   m2.def( "createVTKWriter",
+           [](const shared_ptr<StructuredBlockForest> & blocks, const std::string & name,
+              const std::string & nameInVtkOutput = ""){
+              return internal::createVTKWriter< FieldTypes... >(blocks, name, nameInVtkOutput);
+           },
+       "blocks"_a, "name"_a, "nameInVtkOutput"_a="" );
+
+
+   #define UintFields Field<uint8_t,1 >, Field<uint16_t, 1>, Field<uint32_t, 1>, Field<uint64_t, 1>
+   m2.def( "createBinarizationVTKWriter",
+           [](const shared_ptr<StructuredBlockForest> & blocks, const std::string & name,
+              uint_t mask, const std::string & nameInVtkOutput = ""){
+             return internal::createBinarizationVTKWriter< UintFields >(blocks, name, mask, nameInVtkOutput);
+           },
+           "blocks"_a, "name"_a, "mask"_a, "nameInVtkOutput"_a="" );
+
+
+}
+
+} // namespace field
+} // namespace walberla
diff --git a/src/field/python/Exports.h b/src/python_coupling/export/FieldExports.h
similarity index 63%
rename from src/field/python/Exports.h
rename to src/python_coupling/export/FieldExports.h
index c07bad8689fc1dc959beb83b63e7adfe104a3912..385ad4fe73cb4758ea9a61c66cc1c39dae7338e9 100644
--- a/src/field/python/Exports.h
+++ b/src/python_coupling/export/FieldExports.h
@@ -13,9 +13,10 @@
 //  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 Exports.h
+//! \file FieldExports.h
 //! \ingroup field
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
@@ -26,10 +27,10 @@
 
 #ifdef WALBERLA_BUILD_WITH_PYTHON
 
-#include "python_coupling/helper/ModuleScope.h"
-#include "FieldExport.h"
-#include "GatherExport.h"
-#include "CommunicationExport.h"
+#   include "python_coupling/PythonWrapper.h"
+
+#   include "FieldCommunicationExport.h"
+#   include "FieldExport.impl.h"
 
 namespace walberla {
 namespace field {
@@ -38,26 +39,17 @@ namespace field {
    //*******************************************************************************************************************
    /*! Exports the field types and corresponding function given in the type sequence to python
    *
-   * Automatically exports the Field, GhostLayerField and (if possible) the FlagField version,
-   * so it is enough to add the basic Field<> types to the sequence
-   * For fields that store and unsigned integer type and have an fSize of one, the FlagField is also exported
+   * Automatically exports the Field and GhostLayerField
    *
-   * Example:
-    \code
-      typedef boost::mpl::vector< Field<real_t,1>,
-                                  Field<uint16_t, 1>
-                                  Field<Vector3<real_t>, 1 >  FieldVector;
-    \endcode
-
-   *  This exports the following types:
+   * For example, with the template arguments Field<real_t,1> and Field<uint16_t, 1>,
+   * this exports the following types:
    *     - Field<real_t,1>, GhostLayerField<real_t,1>
-   *     - Field<uint16_t,1>, GhostLayerField<uint16_t,1>, FlagField<uint16_t>
-   *     - Field< Vector3<real_t>, 1 > , GhostLayerField< Vector3<real_t>, 1 >
+   *     - Field<uint16_t,1>, GhostLayerField<uint16_t,1>
    *
    *  Additionally the following free functions are exported
-   *     - field.createField
-   *     - field.createFlagField
    *     - field.addToStorage
+   *     - field.createVTKWriter
+   *     - field.createBinarizationVTKWriter
    *     - field.createPackInfo
    *     - field.createMPIDatatypeInfo
    *
@@ -66,14 +58,11 @@ namespace field {
    * \warning Make sure that the same field type is exported only once!
    */
    //*******************************************************************************************************************
-   template<typename FieldTypes>
-   void exportModuleToPython()
+   template<typename... FieldTypes>
+   void exportModuleToPython(py::module_ &m)
    {
-      python_coupling::ModuleScope fieldModule( "field" );
-
-      exportFields<FieldTypes>();
-
-      exportCommunicationClasses<FieldTypes>();
+      exportFields<FieldTypes...>(m);
+      exportCommunicationClasses<FieldTypes...>(m);
    }
 
 
diff --git a/src/python_coupling/export/GatherExport.impl.h b/src/python_coupling/export/GatherExport.impl.h
new file mode 100644
index 0000000000000000000000000000000000000000..6d567f0756658a92175ff60eca8873a066eb9963
--- /dev/null
+++ b/src/python_coupling/export/GatherExport.impl.h
@@ -0,0 +1,141 @@
+//======================================================================================================================
+//
+//  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 GatherExport.impl.h
+//! \ingroup field
+//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
+//
+//======================================================================================================================
+
+#include "field/Gather.h"
+#include "python_coupling/helper/MplHelpers.h"
+#include "python_coupling/helper/BlockStorageExportHelpers.h"
+#include "python_coupling/helper/SliceToCellInterval.h"
+
+
+namespace walberla {
+namespace field {
+
+//*******************************************************************************************************************
+/*! Exports the gather functionality of waLberla
+*
+* With field.gather a corresponding field will the gathered to the specified process. This field can be viewed as a
+* numpy array with field.toArrayOn all other porcesses an empty pybind11::object will be returned.
+*
+* \hint For large scale simulations it is also possible to provide a slice to keep the gathered data low!
+*/
+//*******************************************************************************************************************
+namespace py = pybind11;
+template<typename... FieldTypes >
+void exportGatherFunctions(py::module_ &m);
+
+namespace internal {
+namespace py = pybind11;
+   //===================================================================================================================
+   //
+   //  Gather
+   //
+   //===================================================================================================================
+
+class GatherExporter
+{
+ public:
+   GatherExporter(const shared_ptr<StructuredBlockForest> & blocks, ConstBlockDataID fieldId,
+                  CellInterval boundingBox = CellInterval(),  int targetRank = 0 )
+      : blocks_( blocks ), fieldId_(fieldId), boundingBox_( boundingBox ), targetRank_(targetRank)
+   {}
+
+   template< typename FieldType>
+   void operator() ( python_coupling::NonCopyableWrap<FieldType> )
+   {
+      typedef Field< typename FieldType::value_type, FieldType::F_SIZE > ResultField;
+      IBlock * firstBlock =  & ( * blocks_->begin() );
+      if( firstBlock->isDataClassOrSubclassOf<FieldType>(fieldId_) )
+      {
+         auto result = make_shared< ResultField > ( 0,0,0 );
+         field::gather< FieldType, ResultField > ( *result, blocks_, fieldId_, boundingBox_, targetRank_, MPI_COMM_WORLD );
+
+         if ( MPIManager::instance()->worldRank() == targetRank_ )
+            resultField_ = py::cast(result);
+         else
+            resultField_ = py::none();
+
+      }
+   }
+   py::object getResultField()
+   {
+      return resultField_;
+   }
+
+ private:
+   py::object resultField_;
+   shared_ptr< StructuredBlockStorage > blocks_;
+   ConstBlockDataID fieldId_;
+   std::string vtkName_;
+   CellInterval boundingBox_;
+   int targetRank_ ;
+};
+
+
+template<typename... FieldTypes>
+static py::object gatherWrapper(const shared_ptr<StructuredBlockForest> & blocks, const std::string & name,
+                                const py::tuple & slice, int targetRank = 0 )
+{
+   BlockDataID fieldID = python_coupling::blockDataIDFromString( *blocks, name );
+   CellInterval boundingBox = python_coupling::globalPythonSliceToCellInterval( blocks, slice );
+
+   if ( blocks->begin() == blocks->end() ) {
+      // if no blocks are on this process the field::gather function can be called with any type
+      // however we have to call it, otherwise a deadlock occurs
+      auto result = make_shared< Field<real_t, 1> > ( 0,0,0 );
+      field::gather< Field<real_t, 1>, Field<real_t, 1> > ( *result, blocks, fieldID, boundingBox, targetRank, MPI_COMM_WORLD );
+      return py::none();
+   }
+
+   GatherExporter exporter(blocks, fieldID, boundingBox, targetRank);
+   python_coupling::for_each_noncopyable_type< FieldTypes... >  ( std::ref(exporter) );
+
+   if ( ! exporter.getResultField() ) {
+      throw py::value_error("Failed to gather Field");
+   }
+   else {
+      return exporter.getResultField();
+   }
+}
+
+} // namespace internal
+
+
+namespace py = pybind11;
+using namespace pybind11::literals;
+template<typename... FieldTypes >
+void exportGatherFunctions(py::module_ &m)
+{
+   py::module_ m2 = m.def_submodule("field", "Field Extension of the waLBerla python bindings");
+
+   m2.def(
+      "gather",
+      [](const shared_ptr<StructuredBlockForest> & blocks, const std::string & name,
+         const py::tuple & slice, int targetRank = 0 ) {
+        return internal::gatherWrapper< FieldTypes... >(blocks, name, slice, targetRank);
+      },
+      "blocks"_a, "name"_a, "slice"_a, "targetRank"_a = uint_t(0));
+}
+
+} // namespace moduleName
+} // namespace walberla
+
+
diff --git a/src/python_coupling/export/MPIExport.cpp b/src/python_coupling/export/MPIExport.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..00e13dfdf54f7b10268646337825a2ea266b53fb
--- /dev/null
+++ b/src/python_coupling/export/MPIExport.cpp
@@ -0,0 +1,278 @@
+//======================================================================================================================
+//
+//  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 MPIExports.cpp
+//! \ingroup python_coupling
+//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
+//
+//======================================================================================================================
+
+#include "python_coupling/PythonWrapper.h"
+
+#ifdef WALBERLA_BUILD_WITH_PYTHON
+
+#include "python_coupling/helper/PythonIterableToStdVector.h"
+
+#include "core/mpi/MPIManager.h"
+#include "core/mpi/Reduce.h"
+#include "core/mpi/Gather.h"
+#include "core/mpi/Broadcast.h"
+
+#include <vector>
+#include "pybind11/stl.h"
+
+namespace py = pybind11;
+
+
+
+
+namespace walberla {
+namespace python_coupling {
+
+   typedef std::vector<int64_t>      IntStdVector;
+   typedef std::vector<real_t>       RealStdVector;
+   typedef std::vector<std::string>  StringStdVector;
+
+
+   //===================================================================================================================
+   //
+   //  MPIManager
+   //
+   //===================================================================================================================
+
+
+   static int  rank()              { return MPIManager::instance()->rank();               }
+   static int  worldRank()         { return MPIManager::instance()->worldRank();          }
+   static int  numProcesses()      { return MPIManager::instance()->numProcesses();       }
+   static bool hasCartesianSetup() { return MPIManager::instance()->hasCartesianSetup();  }
+   static bool rankValid()         { return MPIManager::instance()->rankValid();          }
+
+
+
+   //===================================================================================================================
+   //
+   //  Broadcast
+   //
+   //===================================================================================================================
+
+   static py::object broadcast_string( py::object value, int sendRank ) //NOLINT
+   {
+      if ( py::isinstance<std::string>(value) )
+      {
+         std::string extractedValue = py::cast< std::string >(value);
+         mpi::broadcastObject( extractedValue , sendRank );
+         return py::cast( extractedValue );
+      }
+      StringStdVector extractedValue = pythonIterableToStdVector< StringStdVector::value_type >( value );
+      mpi::broadcastObject( extractedValue, sendRank );
+      return py::cast( extractedValue );
+   }
+
+   static py::object broadcast_int( py::object value, int sendRank ) //NOLINT
+   {
+      if ( py::isinstance<int64_t>(value) )
+      {
+         int64_t extractedValue = py::cast< int64_t >(value);
+         mpi::broadcastObject( extractedValue , sendRank );
+         return py::cast( extractedValue );
+      }
+      IntStdVector extractedValue = pythonIterableToStdVector< IntStdVector::value_type >( value );
+      mpi::broadcastObject( extractedValue, sendRank );
+      return py::cast( extractedValue );
+   }
+
+   static py::object broadcast_real( py::object value, int sendRank ) //NOLINT
+   {
+      if ( py::isinstance<real_t>(value) )
+      {
+         real_t extractedValue = py::cast< real_t  >(value);
+         mpi::broadcastObject( extractedValue , sendRank);
+         return py::cast( extractedValue );
+      }
+      RealStdVector extractedValue = pythonIterableToStdVector< RealStdVector::value_type >( value );
+      mpi::broadcastObject( extractedValue , sendRank);
+      return py::cast( extractedValue );
+   }
+
+
+   //===================================================================================================================
+   //
+   //  Reduce
+   //
+   //===================================================================================================================
+
+
+   static py::object reduce_int( py::object value, mpi::Operation op, int recvRank ) //NOLINT
+   {
+      if ( py::isinstance<int64_t>(value) )
+      {
+         int64_t extractedValue = py::cast< int64_t >(value);
+         mpi::reduceInplace( extractedValue , op, recvRank );
+         return py::cast( extractedValue );
+      }
+      IntStdVector extractedValue = pythonIterableToStdVector< IntStdVector::value_type >( value );
+      mpi::reduceInplace( extractedValue, op, recvRank );
+      return py::cast( extractedValue );
+   }
+
+   static py::object reduce_real( py::object value, mpi::Operation op, int recvRank ) //NOLINT
+   {
+      if ( py::isinstance<real_t>(value) )
+      {
+         real_t extractedValue = py::cast< real_t  >(value);
+         mpi::reduceInplace( extractedValue , op, recvRank);
+         return py::cast( extractedValue );
+      }
+      RealStdVector extractedValue = pythonIterableToStdVector< RealStdVector::value_type >( value );
+      mpi::reduceInplace( extractedValue , op, recvRank);
+      return py::cast( extractedValue );
+   }
+
+
+   static py::object allreduce_int( py::object value, mpi::Operation op ) //NOLINT
+   {
+      if ( py::isinstance<int64_t>(value) )
+      {
+         int64_t extractedValue = py::cast< int64_t >(value);
+         mpi::allReduceInplace( extractedValue , op );
+         return py::cast( extractedValue );
+      }
+      IntStdVector extractedValue = pythonIterableToStdVector< IntStdVector::value_type >( value );
+      mpi::allReduceInplace( extractedValue, op );
+      return py::cast( extractedValue );
+   }
+
+   static py::object allreduce_real( py::object value, mpi::Operation op ) //NOLINT
+   {
+      if ( py::isinstance<real_t>(value) )
+      {
+         real_t extractedValue = py::cast< real_t  >(value);
+         mpi::allReduceInplace( extractedValue , op );
+         return py::cast( extractedValue );
+      }
+      RealStdVector extractedValue = pythonIterableToStdVector< RealStdVector::value_type >( value );
+      mpi::allReduceInplace( extractedValue , op );
+      return py::cast( extractedValue );
+   }
+
+
+   //===================================================================================================================
+   //
+   //  Gather
+   //
+   //===================================================================================================================
+
+   static IntStdVector gather_int( py::object value, int recvRank ) //NOLINT
+   {
+      if ( ! py::isinstance<int64_t>(value) )
+      {
+         throw py::cast_error("Could not gather the given value - unknown type");
+      }
+      int64_t extractedValue = py::cast< int64_t >(value);
+      return mpi::gather( extractedValue , recvRank );
+   }
+
+   static RealStdVector gather_real( py::object value, int recvRank ) //NOLINT
+   {
+      if ( ! py::isinstance<real_t>(value) )
+      {
+         throw py::cast_error("Could not gather the given value - unknown type");
+      }
+      real_t extractedValue = py::cast< real_t  >(value);
+      return mpi::gather( extractedValue , recvRank);
+   }
+
+
+   static IntStdVector allgather_int( py::object value ) //NOLINT
+   {
+      if ( ! py::isinstance<int64_t>(value) )
+      {
+         throw py::cast_error("Could not gather the given value - unknown type");
+      }
+      int64_t extractedValue = py::cast< int64_t >(value);
+      return mpi::allGather( extractedValue );
+   }
+
+   static RealStdVector allgather_real( py::object value ) //NOLINT
+   {
+      if ( ! py::isinstance<real_t>(value) )
+      {
+         throw py::cast_error("Could not gather the given value - unknown type");
+      }
+      real_t extractedValue = py::cast< real_t  >(value);
+      return mpi::allGather( extractedValue );
+   }
+
+
+
+   //===================================================================================================================
+   //
+   //  Export
+   //
+   //===================================================================================================================
+
+   static void worldBarrier()
+   {
+      WALBERLA_MPI_WORLD_BARRIER();
+   }
+
+
+   void exportMPI(py::module_ &m)
+   {
+      py::module_ m2 = m.def_submodule("mpi", "MPI Extension of the waLBerla python bindings");
+
+      m2.def( "rank"             , &rank             );
+      m2.def( "worldRank"        , &worldRank        );
+      m2.def( "numProcesses"     , &numProcesses     );
+      m2.def( "hasCartesianSetup", &hasCartesianSetup);
+      m2.def( "rankValid"        , &rankValid        );
+      m2.def( "worldBarrier"     , &worldBarrier     );
+
+      py::enum_<mpi::Operation>(m2, "Operation")
+              .value("MIN"    ,      mpi::MIN )
+              .value("MAX"    ,      mpi::MAX )
+              .value("SUM"    ,      mpi::SUM )
+              .value("PRODUCT",      mpi::PRODUCT )
+              .value("LOGICAL_AND",  mpi::LOGICAL_AND )
+              .value("BITWISE_AND",  mpi::BITWISE_AND )
+              .value("LOGICAL_OR",   mpi::LOGICAL_OR  )
+              .value("BITWISE_OR",   mpi::BITWISE_OR  )
+              .value("LOGICAL_XOR",  mpi::LOGICAL_XOR )
+              .value("BITWISE_XOR",  mpi::BITWISE_XOR )
+              .export_values();
+
+      m2.def( "broadcastInt",   &broadcast_int);
+      m2.def( "broadcastReal",  &broadcast_real);
+      m2.def( "broadcastString",&broadcast_string);
+
+      m2.def( "reduceInt",     &reduce_int);
+      m2.def( "reduceReal",    &reduce_real);
+      m2.def( "allreduceInt",  &allreduce_int  );
+      m2.def( "allreduceReal", &allreduce_real );
+
+      m2.def( "gatherInt",     &gather_int);
+      m2.def( "gatherReal",    &gather_real);
+      m2.def( "allgatherInt",  &allgather_int  );
+      m2.def( "allgatherReal", &allgather_real );
+   }
+
+
+
+} // namespace python_coupling
+} // namespace walberla
+
+
+#endif
diff --git a/src/python_coupling/basic_exports/MPIExport.h b/src/python_coupling/export/MPIExport.h
similarity index 93%
rename from src/python_coupling/basic_exports/MPIExport.h
rename to src/python_coupling/export/MPIExport.h
index 8e15eb1e7a32abae0395af4bb935d370dd562c67..d1653a734e3c3e7df87892579478735a28ab5438 100644
--- a/src/python_coupling/basic_exports/MPIExport.h
+++ b/src/python_coupling/export/MPIExport.h
@@ -16,6 +16,7 @@
 //! \file MPIExports.h
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
@@ -27,7 +28,7 @@ namespace python_coupling {
 
 
 
-   void exportMPI();
+   void exportMPI(py::module_ &m);
 
 
 
diff --git a/src/vtk/python/Exports.cpp b/src/python_coupling/export/VTKExport.cpp
similarity index 53%
rename from src/vtk/python/Exports.cpp
rename to src/python_coupling/export/VTKExport.cpp
index 719a6baeb654db00896a650a411ab47187c3ccdb..a8ab00e577e39fbf1ad53747f23ec76582a8edf0 100644
--- a/src/vtk/python/Exports.cpp
+++ b/src/python_coupling/export/VTKExport.cpp
@@ -14,7 +14,7 @@
 //  with waLBerla (see COPYING.txt). If not, see <http://www.gnu.org/licenses/>.
 //
 //! \file Exports.cpp
-//! \ingroup timeloop
+//! \ingroup vtk
 //! \author Martin Bauer <martin.bauer@fau.de>
 //
 //======================================================================================================================
@@ -24,68 +24,59 @@
 
 #ifdef WALBERLA_BUILD_WITH_PYTHON
 
-#include "python_coupling/Manager.h"
-#include "python_coupling/helper/ModuleScope.h"
-
+#include "blockforest/StructuredBlockForest.h"
 #include "vtk/VTKOutput.h"
 
-using namespace boost::python;
-
-
 namespace walberla {
 namespace vtk {
 
 namespace internal {
 
+namespace py = pybind11;
 
-   shared_ptr<VTKOutput> VTKOutput_create(const shared_ptr<StructuredBlockStorage> & sbs, const std::string & identifier,
-                                          const std::string & baseFolder, const std::string & executionFolder,
-                                          const bool binary, const bool littleEndian, const bool useMPIIO,
-                                          uint_t ghostLayers=0)
-   {
-      return createVTKOutput_BlockData(*sbs, identifier, 1, ghostLayers, false, baseFolder, executionFolder,
-                                       true, binary, littleEndian, useMPIIO, 0);
-   }
-
-   void VTKOutput_write(const shared_ptr<VTKOutput> &vtkOut, int step)
-   {
-       if (step < 0)
-       {
-          PyErr_SetString(PyExc_ValueError, "Step parameter has to be positive");
-          throw boost::python::error_already_set();
-       }
-       vtkOut->forceWrite(uint_c(step));
-   }
+void VTKOutput_write(const shared_ptr<VTKOutput> &vtkOut, int step)
+{
+    if (step < 0)
+    {
+       throw py::value_error("Step parameter has to be positive");
+    }
+    vtkOut->forceWrite(uint_c(step));
+}
 } // namespace internal
 
-
-void exportModuleToPython()
+namespace py = pybind11;
+using namespace pybind11::literals;
+void exportModuleToPython(py::module_& m)
 {
-   python_coupling::ModuleScope timeloopModule( "vtk" );
+   py::module_ m2 = m.def_submodule("vtk", "VTK Extension of the waLBerla python bindings");
 
 
    void ( VTKOutput::*p_setSamplingResolution1) ( const real_t  ) = &VTKOutput::setSamplingResolution;
    void ( VTKOutput::*p_setSamplingResolution2) ( const real_t, const real_t, const real_t ) = &VTKOutput::setSamplingResolution;
 
-   class_<BlockCellDataWriterInterface, //NOLINT
-           boost::noncopyable,
-           shared_ptr<BlockCellDataWriterInterface> > ("BlockCellDataWriterInterface", no_init)
-       ;
+   py::class_<BlockCellDataWriterInterface, shared_ptr<BlockCellDataWriterInterface> > (m2, "BlockCellDataWriterInterface");
+
+   m2.def(
+      "makeOutput",
+      [](const shared_ptr<StructuredBlockForest> & sbf, const std::string & identifier,
+         const std::string & baseFolder, const std::string & executionFolder,
+         const bool binary, const bool littleEndian, const bool useMPIIO, uint_t ghostLayers=0)
+      {
+        return createVTKOutput_BlockData(*sbf, identifier, 1, ghostLayers, false, baseFolder, executionFolder,
+                                         true, binary, littleEndian, useMPIIO, 0);
+        },
+        "blocks"_a, "name"_a, "baseFolder"_a=".", "executionFolder"_a="vtk", "binary"_a=true,
+                              "littleEndian"_a=true, "useMPIIO"_a=true, "ghostLayers"_a=0);
 
-   def("makeOutput", internal::VTKOutput_create, (arg("blocks"), arg("name"), arg("baseFolder")=".",
-                                                  arg("executionFolder")="vtk", arg("binary")=true,
-                                                  arg("littleEndian")=true, arg("useMPIIO")=true,
-                                                  arg("ghostLayers")=0));
 
-   class_<VTKOutput, shared_ptr<VTKOutput>, boost::noncopyable > ("VTKOutput", no_init)
+   py::class_<VTKOutput, shared_ptr<VTKOutput> > (m2, "VTKOutput")
       .def( "addCellDataWriter"     , &VTKOutput::addCellDataWriter )
       .def( "write"                 , &internal::VTKOutput_write )
       .def( "__call__"              , &internal::VTKOutput_write )
       .def( "addAABBInclusionFilter", &VTKOutput::addAABBInclusionFilter )
       .def( "addAABBExclusionFilter", &VTKOutput::addAABBExclusionFilter )
       .def( "setSamplingResolution" , p_setSamplingResolution1 )
-      .def( "setSamplingResolution" , p_setSamplingResolution2 )
-      ;
+      .def( "setSamplingResolution" , p_setSamplingResolution2 );
 }
 
 
diff --git a/src/vtk/python/Exports.h b/src/python_coupling/export/VTKExport.h
similarity index 65%
rename from src/vtk/python/Exports.h
rename to src/python_coupling/export/VTKExport.h
index a14e864cdb9a2349c6db338ccb01ff81584f0574..d2f955cd722334ea1f37aa72202a7d41f987879b 100644
--- a/src/vtk/python/Exports.h
+++ b/src/python_coupling/export/VTKExport.h
@@ -16,6 +16,7 @@
 //! \file Exports.h
 //! \ingroup vtk
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
@@ -25,11 +26,21 @@
 
 
 #ifdef WALBERLA_BUILD_WITH_PYTHON
+#include <pybind11/pybind11.h>
 
 namespace walberla {
 namespace vtk {
+namespace py = pybind11;
 
-  void exportModuleToPython();
+   //*******************************************************************************************************************
+   /*! Exports the vtk functionality of waLBerla
+    *
+    * With vtk.makeOutput a instance of VTKOutput will be provided for python. I can be used together with
+    * field.createVTKWriter and field.createBinarizationVTKWriter to get the VTK output
+    */
+   //*******************************************************************************************************************
+
+  void exportModuleToPython(py::module_ &m);
 
 } // namespace vtk
 } // namespace walberla
diff --git a/src/python_coupling/helper/BlockStorageExportHelpers.cpp b/src/python_coupling/helper/BlockStorageExportHelpers.cpp
index 4555f0e7d00b0965688305036f9acc3f00bc1de8..016416d117830b50f39820c0d45b41272c8e9e51 100644
--- a/src/python_coupling/helper/BlockStorageExportHelpers.cpp
+++ b/src/python_coupling/helper/BlockStorageExportHelpers.cpp
@@ -16,6 +16,7 @@
 //! \file BlockStorageExportHelpers.cpp
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
@@ -25,24 +26,22 @@
 
 namespace walberla {
 namespace python_coupling {
-
+namespace py = pybind11;
 
 #ifdef WALBERLA_BUILD_WITH_PYTHON
 
 void NoSuchBlockData::translate(  const NoSuchBlockData & e ) {
-   PyErr_SetString(PyExc_RuntimeError, e.what() );
+   throw py::cast_error(e.what());
 }
 
 void BlockDataNotConvertible::translate(  const BlockDataNotConvertible & e ) {
-   PyErr_SetString(PyExc_RuntimeError, e.what() );
+   throw py::cast_error(e.what());
 }
 #else
 
-void NoSuchBlockData::translate(  const NoSuchBlockData &  ) {
-}
+void NoSuchBlockData::translate(  const NoSuchBlockData &  ) {}
 
-void BlockDataNotConvertible::translate(  const BlockDataNotConvertible &  ) {
-}
+void BlockDataNotConvertible::translate(  const BlockDataNotConvertible &  ) {}
 
 #endif
 
diff --git a/src/python_coupling/helper/BlockStorageExportHelpers.h b/src/python_coupling/helper/BlockStorageExportHelpers.h
index 227b454745d44c91c31617fed3ddb63d5117a3a9..0b41dd498276838270d8546bfdcab2406abb641e 100644
--- a/src/python_coupling/helper/BlockStorageExportHelpers.h
+++ b/src/python_coupling/helper/BlockStorageExportHelpers.h
@@ -16,6 +16,7 @@
 //! \file ExportHelpers.h
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
@@ -56,16 +57,6 @@ namespace python_coupling {
 
 #ifdef WALBERLA_BUILD_WITH_PYTHON
 
-
-   template<typename UID, typename StringContainer>
-   Set< UID > uidSetFromStringContainer( const StringContainer & stringContainer )
-   {
-      Set< UID > result;
-      result.insert( boost::python::stl_input_iterator< std::string >( stringContainer ),
-                     boost::python::stl_input_iterator< std::string >( ) );
-      return result;
-   }
-
    template< typename FField>
    typename FField::value_type maskFromFlagList(  const shared_ptr<StructuredBlockStorage> & bs,
                                                   ConstBlockDataID flagFieldID,
diff --git a/src/python_coupling/helper/BoostPythonHelpers.h b/src/python_coupling/helper/BoostPythonHelpers.h
deleted file mode 100644
index 88c4fec8cda433deccdc5ff5746a95d6eeccfa00..0000000000000000000000000000000000000000
--- a/src/python_coupling/helper/BoostPythonHelpers.h
+++ /dev/null
@@ -1,106 +0,0 @@
-
-//======================================================================================================================
-//
-//  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 CppPythonTypeEquality.h
-//! \ingroup python_coupling
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include "core/DataTypes.h"
-#include "python_coupling/PythonWrapper.h"
-
-#include <boost/python/converter/registry.hpp>
-
-
-namespace walberla {
-namespace python_coupling {
-
-#ifdef WALBERLA_CXX_COMPILER_IS_CLANG
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wunused-function"
-#endif
-
-   // fallback - check boost python registry
-   template<typename T>
-   inline static bool isCppEqualToPythonType( PyTypeObject * obj)
-   {
-      boost::python::type_info info = boost::python::type_id<T>();
-      const boost::python::converter::registration* reg = boost::python::converter::registry::query(info);
-      if (reg == NULL)
-         return false;
-
-      try
-      {
-         reg->get_class_object();
-         return ( reg->get_class_object() == obj  );
-      }
-      catch ( ... ) {
-         return false;
-      }
-   }
-
-   // native data types
-   template<> inline bool isCppEqualToPythonType<bool>  ( PyTypeObject * o) { std::string n( o->tp_name ); return ( n == "numpy.bool_"
-                                                                                                          || n =="bool" );           }
-
-
-   template<> inline bool isCppEqualToPythonType<float> ( PyTypeObject * o) { std::string n( o->tp_name ); return ( n == "numpy.float32" ); }
-   template<> bool inline isCppEqualToPythonType<double>( PyTypeObject * o) { std::string n( o->tp_name ); return ( n == "numpy.float64"
-                                                                                                          || n == "numpy.float_"
-                                                                                                          || n =="float");           }
-
-
-   template<> inline bool isCppEqualToPythonType<uint8_t  > ( PyTypeObject * o) { std::string n( o->tp_name ); return ( n == "numpy.uint8"  ); }
-   template<> inline bool isCppEqualToPythonType<uint16_t > ( PyTypeObject * o) { std::string n( o->tp_name ); return ( n == "numpy.uint16" ); }
-   template<> inline bool isCppEqualToPythonType<uint32_t > ( PyTypeObject * o) { std::string n( o->tp_name ); return ( n == "numpy.uint32" ); }
-   template<> inline bool isCppEqualToPythonType<uint64_t > ( PyTypeObject * o) { std::string n( o->tp_name ); return ( n == "numpy.uint64" ); }
-
-
-   template<> inline bool isCppEqualToPythonType<int8_t  > ( PyTypeObject * o) { std::string n( o->tp_name ); return ( n == "numpy.int8"  ); }
-   template<> inline bool isCppEqualToPythonType<int16_t > ( PyTypeObject * o) { std::string n( o->tp_name ); return ( n == "numpy.int16" ); }
-   template<> inline bool isCppEqualToPythonType<int32_t > ( PyTypeObject * o) { std::string n( o->tp_name ); return ( n == "numpy.int32" ); }
-   template<> inline bool isCppEqualToPythonType<int64_t > ( PyTypeObject * o) { std::string n( o->tp_name ); return ( n == "numpy.int64"
-                                                                                                             || n == "int" );         }
-
-#ifdef WALBERLA_CXX_COMPILER_IS_CLANG
-#pragma clang diagnostic pop
-#endif
-
-
-   template< typename T >
-   bool isTypeRegisteredInBoostPython( )
-   {
-      boost::python::type_info info = boost::python::type_id<T>();
-      const boost::python::converter::registration* reg = boost::python::converter::registry::query(info);
-
-      try {
-         reg->get_class_object();
-      }
-      catch( ... ) {
-         PyErr_Clear();
-         return false;
-      }
-      PyErr_Clear();
-      return (reg != NULL);
-   }
-
-} // namespace python_coupling
-} // namespace walberla
-
-
diff --git a/src/python_coupling/helper/ConfigFromDict.cpp b/src/python_coupling/helper/ConfigFromDict.cpp
index 5755373eaa2c7ace3c1b9efe0b6209bcafae529a..e35ca0fd5bb1ed276ba01802de4a1dbeabbc9f7c 100644
--- a/src/python_coupling/helper/ConfigFromDict.cpp
+++ b/src/python_coupling/helper/ConfigFromDict.cpp
@@ -16,6 +16,7 @@
 //! \file ConfigFromDict.cpp
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
@@ -37,59 +38,55 @@ void handlePythonBooleans( std::string & value) {
    if ( value=="False") value="0";
 }
 
-void configFromPythonDict( config::Config::Block & block, boost::python::dict & pythonDict )
-{
-   using namespace boost::python;
-
-   boost::python::list keys = pythonDict.keys();
-   keys.sort();
+namespace py = pybind11;
 
-   for (int i = 0; i < boost::python::len( keys ); ++i)
+void configFromPythonDict( config::Config::Block & block, py::dict & pythonDict )
+{
+   for (auto item : pythonDict)
    {
-      // Extract key
-      boost::python::extract<std::string> extracted_key( keys[i] );
-      if( !extracted_key.check() ) {
+      // py::print(item);
+      if( py::isinstance<std::string>(item.first) ) {
          WALBERLA_LOG_WARNING( "Detected non-string key in waLBerla configuration" );
          continue;
       }
-      std::string key = extracted_key;
 
-      // Extract value
-      extract<std::string>  extracted_str_val   ( pythonDict[key] );
-      extract<dict>         extracted_dict_val  ( pythonDict[key] );
-      extract<list>         extracted_list_val  ( pythonDict[key] );
-      extract<tuple>        extracted_tuple_val ( pythonDict[key] );
+      std::string key = py::str(item.first);
 
       try
       {
-         if( extracted_str_val.check() ){
-            std::string value = extracted_str_val;
+         if( py::isinstance<std::string>(item.second) ){
+            std::string value = py::str(item.second);
             handlePythonBooleans( value );
             block.addParameter( key, value );
          }
-         else if ( extracted_dict_val.check() )
+         else if ( py::isinstance<py::dict>(item.second) )
          {
             walberla::config::Config::Block & childBlock = block.createBlock( key );
-            dict childDict = extracted_dict_val;
-            configFromPythonDict( childBlock, childDict );
+            py::dict childDict = py::dict(pythonDict[key.c_str()]);
+            configFromPythonDict( childBlock, childDict);
          }
-         else if ( extracted_list_val.check() )
+         else if ( py::isinstance<py::list>(item.second) )
          {
-            list childList = extracted_list_val;
-            for( int l=0; l < len( childList ); ++l ) {
+            py::list childList = py::list(pythonDict[key.c_str()]);
+            for(py::size_t i = 0; i < childList.size(); ++i){
                walberla::config::Config::Block & childBlock = block.createBlock( key );
-               dict d = extract<dict>( childList[l] );
+               py::dict d = py::dict(childList[i]);
                configFromPythonDict( childBlock, d );
             }
          }
-         else if (  extracted_tuple_val.check() )
+         else if (  py::isinstance<py::tuple>(item.second) )
          {
             std::stringstream ss;
-            tuple childTuple = extracted_tuple_val;
+            py::tuple childTuple = py::tuple(pythonDict[key.c_str()]);
+
+            WALBERLA_ASSERT(len(childTuple) == 2 || len(childTuple) == 3,
+                            "Config problem: " << key << ": Python tuples are mapped to walberla::Vector2 or Vector3. \n" <<
+                               "So only tuples of size 2 or 3 are supported! Option " << key << "  is ignored ")
+
             if ( len(childTuple) == 2 )
             {
-               std::string e0 = extract<std::string>( childTuple[0].attr("__str__" )() );
-               std::string e1 = extract<std::string>( childTuple[1].attr("__str__" )() );
+               std::string e0 = py::str( childTuple[0].attr("__str__" )() );
+               std::string e1 = py::str( childTuple[1].attr("__str__" )() );
                handlePythonBooleans( e0 );
                handlePythonBooleans( e1 );
                ss << "< " << e0 << " , " << e1 << " > ";
@@ -97,40 +94,32 @@ void configFromPythonDict( config::Config::Block & block, boost::python::dict &
             }
             else if ( len(childTuple) == 3)
             {
-               std::string e0 = extract<std::string>( childTuple[0].attr("__str__" )() );
-               std::string e1 = extract<std::string>( childTuple[1].attr("__str__" )() );
-               std::string e2 = extract<std::string>( childTuple[2].attr("__str__" )() );
+               std::string e0 = py::str( childTuple[0].attr("__str__" )() );
+               std::string e1 = py::str( childTuple[1].attr("__str__" )() );
+               std::string e2 = py::str( childTuple[2].attr("__str__" )() );
                handlePythonBooleans( e0 );
                handlePythonBooleans( e1 );
                handlePythonBooleans( e2 );
-               ss << "< " << e0 << " , " << e1 << ", " << e2 << " > ";
+               ss << "< " << e0 << ", " << e1 << ", " << e2 << " > ";
                block.addParameter( key, ss.str() );
             }
-            else
-            {
-               WALBERLA_LOG_WARNING( "Config problem: " << key << ": Python tuples are mapped to walberla::Vector2 or Vector3. \n" <<
-                                     "So only tuples of size 2 or 3 are supported! Option " << key << "  is ignored ");
-
-            }
          }
          else
          {
             // if value is not a string try to convert it
-            std::string value = extract<std::string>( pythonDict[key].attr("__str__" )() );
+            std::string value = py::str( pythonDict[key.c_str()].attr("__str__" )() );
             block.addParameter ( key, value );
          }
       }
-      catch ( error_already_set & ) {
+      catch ( py::error_already_set & ) {
          WALBERLA_LOG_WARNING ( "Error when reading configuration option " << key << ". Could not be converted to string.");
       }
    }
 }
 
 
-shared_ptr<Config> configFromPythonDict( boost::python::dict & pythonDict )
+shared_ptr<Config> configFromPythonDict( py::dict & pythonDict )
 {
-   using namespace boost::python;
-
    shared_ptr<Config> config = make_shared<Config>();
    configFromPythonDict( config->getWritableGlobalBlock(), pythonDict );
    return config;
diff --git a/src/python_coupling/helper/ConfigFromDict.h b/src/python_coupling/helper/ConfigFromDict.h
index e1bef7f276f3935892129f18b61ef996eb0de213..146ebf0fc0856c3ccb6cccc7e981a19524f9b758 100644
--- a/src/python_coupling/helper/ConfigFromDict.h
+++ b/src/python_coupling/helper/ConfigFromDict.h
@@ -16,6 +16,7 @@
 //! \file ConfigFromDict.h
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
@@ -37,7 +38,7 @@ namespace python_coupling {
    /*! Converts a python dictionary to a config::Block (recursively)
    */
    //*******************************************************************************************************************
-   void configFromPythonDict( config::Config::Block & result, boost::python::dict & pythonDict  );
+   void configFromPythonDict( config::Config::Block & result, py::dict & pythonDict  );
 
 
 
@@ -45,7 +46,7 @@ namespace python_coupling {
    /*! Converts a python dictionary to a waLBerla config object
    */
    //*******************************************************************************************************************
-   shared_ptr<Config> configFromPythonDict( boost::python::dict & pythonDict );
+   shared_ptr<Config> configFromPythonDict( py::dict & pythonDict );
 
 
 
diff --git a/src/python_coupling/helper/ExceptionHandling.h b/src/python_coupling/helper/ExceptionHandling.h
deleted file mode 100644
index e17ebdfd0afde58d731ff714e10495abb6510369..0000000000000000000000000000000000000000
--- a/src/python_coupling/helper/ExceptionHandling.h
+++ /dev/null
@@ -1,71 +0,0 @@
-//======================================================================================================================
-//
-//  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 ExceptionDecode.h
-//! \ingroup python_coupling
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include <string>
-
-
-namespace walberla {
-namespace python_coupling {
-
-   // Call this function when a boost::python::already_set exception was caught
-   // returns formatted error string with traceback
-   inline std::string decodeException()
-   {
-       namespace bp = boost::python;
-
-       PyObject *exc,*val,*tb;
-
-       bp::object formatted_list, formatted;
-       PyErr_Fetch(&exc,&val,&tb);
-       PyErr_NormalizeException(&exc, &val, &tb);
-       bp::handle<> hexc( exc );
-       bp::handle<> hval( bp::allow_null(val) );
-       bp::handle<> htb ( bp::allow_null(tb)  );
-       bp::object traceback( bp::import("traceback"));
-
-       if (!tb) {
-           bp::object format_exception_only( traceback.attr("format_exception_only"));
-           formatted_list = format_exception_only(hexc,hval);
-       } else {
-          bp::object format_exception(traceback.attr("format_exception"));
-          formatted_list = format_exception(hexc,hval,htb);
-       }
-       formatted = bp::str("").join(formatted_list);
-       return bp::extract<std::string>(formatted);
-   }
-
-
-   inline void terminateOnPythonException( const std::string message )
-   {
-      if (PyErr_Occurred()) {
-          std::string decodedException = decodeException();
-          WALBERLA_ABORT_NO_DEBUG_INFO( message << "\n\n" << decodedException  );
-      }
-      WALBERLA_ABORT_NO_DEBUG_INFO( message << " (unable to decode Python exception) " );
-   }
-
-
-} // namespace python_coupling
-} // namespace walberla
-
-
diff --git a/src/python_coupling/helper/ModuleInit.cpp b/src/python_coupling/helper/ModuleInit.cpp
index e917ff50b24f291f6942c8e009ba8ef866eb3e66..3198acfc3842d2fdd4d93e5c88b2a3baa935202c 100644
--- a/src/python_coupling/helper/ModuleInit.cpp
+++ b/src/python_coupling/helper/ModuleInit.cpp
@@ -16,14 +16,14 @@
 //! \file ModuleInit.cpp
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
 #include "ModuleInit.h"
-#include "waLBerlaDefinitions.h"
+#include "core/debug/CheckFunctions.h"
 #include "core/mpi/MPIManager.h"
 #include "core/Abort.h"
-#include "core/debug/CheckFunctions.h"
 
 // Workaround for OpenMPI library: it dynamically loads plugins which causes trouble when walberla itself is a shared lib
 #if defined(OPEN_MPI) && !defined(_WIN32) && OMPI_MAJOR_VERSION < 3
diff --git a/src/python_coupling/helper/ModuleInit.h b/src/python_coupling/helper/ModuleInit.h
index e771bb86c5ec42f47c8f03d80751a3e42e63880a..e85c45d6cfb55ecc3c219a9f420909738125930d 100644
--- a/src/python_coupling/helper/ModuleInit.h
+++ b/src/python_coupling/helper/ModuleInit.h
@@ -16,6 +16,7 @@
 //! \file ModuleInit.h
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
diff --git a/src/python_coupling/helper/ModuleScope.h b/src/python_coupling/helper/ModuleScope.h
deleted file mode 100644
index 09a619781efd683cda9eb3c454da916b69e01556..0000000000000000000000000000000000000000
--- a/src/python_coupling/helper/ModuleScope.h
+++ /dev/null
@@ -1,56 +0,0 @@
-//======================================================================================================================
-//
-//  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 ModuleScope.h
-//! \ingroup python_coupling
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include "python_coupling/PythonWrapper.h"
-
-#include <string>
-
-namespace walberla {
-namespace python_coupling {
-
-
-   class ModuleScope : public boost::python::scope
-   {
-   public:
-      ModuleScope( const std::string & name)
-         : boost::python::scope ( ModuleScope::createNew( name ) )
-      {}
-
-   private:
-      static boost::python::object createNew ( const std::string & name )
-      {
-         using namespace boost::python;
-         object module( handle<>( borrowed(PyImport_AddModule( name.c_str() ) ) ) );
-         scope().attr( name.c_str() ) = module;
-         return module;
-      }
-
-   };
-
-
-
-
-} // namespace python_coupling
-} // namespace walberla
-
-
diff --git a/src/python_coupling/helper/MplHelpers.h b/src/python_coupling/helper/MplHelpers.h
index d9b0086647f491efacf3d7bf56c060124819b66c..0a5a1d9bd9013a1c31af05e2c6d823ba438792d4 100644
--- a/src/python_coupling/helper/MplHelpers.h
+++ b/src/python_coupling/helper/MplHelpers.h
@@ -16,6 +16,7 @@
 //! \file MplHelpers.h
 //! \ingroup python_export
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
@@ -23,11 +24,6 @@
 
 #include "domain_decomposition/IBlock.h"
 
-#include <boost/mpl/for_each.hpp>
-#include <boost/mpl/lambda.hpp>
-#include <boost/mpl/pair.hpp>
-#include <boost/mpl/transform.hpp>
-
 
 #include <functional>
 #include <map>
@@ -37,23 +33,6 @@ namespace walberla {
 namespace python_coupling {
 
 
-template <typename V, typename T, typename Result>
-struct list_of_pairs
-  : boost::mpl::fold<V, Result,
-        boost::mpl::push_back<boost::mpl::_1, boost::mpl::pair<T, boost::mpl::_2> > >
-{};
-
-template<typename V1, typename V2>
-struct combine_vectors
-: boost::mpl::fold<
-    V1,
-    boost::mpl::vector<>,
-    boost::mpl::lambda<list_of_pairs<V2,boost::mpl::_2, boost::mpl::_1> >
->::type
-{};
-
-
-
 template <typename T>
 struct NonCopyableWrap {};
 
@@ -78,15 +57,20 @@ struct NonCopyableWrap {};
 
 
 
-template< typename Sequence, typename F >
+template< typename F >
+void for_each_noncopyable_type( const F & )
+{}
+
+template< typename Type, typename... Types, typename F >
 void for_each_noncopyable_type( const F & f)
 {
-   boost::mpl::for_each< Sequence, NonCopyableWrap< boost::mpl::placeholders::_1> >  ( f );
+   f(NonCopyableWrap<Type>());
+   for_each_noncopyable_type<Types...>(f);
 }
 
 
 
-template<typename FieldTypeList, typename Exporter>
+template<typename Exporter, typename... FieldTypes>
 class Dispatcher
 {
 public:
@@ -102,7 +86,7 @@ public:
          return map_[ blockDataID ];
 
       Exporter exporter( block_, blockDataID );
-      for_each_noncopyable_type< FieldTypeList>  ( std::ref(exporter) );
+      for_each_noncopyable_type< FieldTypes...>  ( std::ref(exporter) );
       map_[ blockDataID ] = exporter.result;
       return exporter.result;
    }
diff --git a/src/python_coupling/helper/OwningIterator.h b/src/python_coupling/helper/OwningIterator.h
new file mode 100644
index 0000000000000000000000000000000000000000..f384acca4a403c862fe2d2987893dd5a4cfc0cb3
--- /dev/null
+++ b/src/python_coupling/helper/OwningIterator.h
@@ -0,0 +1,74 @@
+//======================================================================================================================
+//
+//  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 OwningIterator.h
+//! \ingroup python_coupling
+//! \author Michael Kuron <mkuron@icp.uni-stuttgart.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include <mutex>
+#include <pybind11/pybind11.h>
+
+namespace walberla {
+namespace python_coupling {
+
+namespace py = pybind11;
+
+namespace detail {
+
+template <typename T, py::return_value_policy Policy>
+struct owning_iterator_state {
+   owning_iterator_state(T _obj)
+   : obj(_obj), it(obj.begin()), first_or_done(true) {}
+   T obj;
+   typename T::iterator it;
+   bool first_or_done;
+   static std::once_flag registered;
+};
+
+template <typename T, py::return_value_policy Policy>
+std::once_flag owning_iterator_state<T, Policy>::registered;
+
+} // namespace detail
+
+template <py::return_value_policy Policy = py::return_value_policy::reference_internal,
+          typename T>
+py::iterator make_owning_iterator(T obj) {
+   using state = detail::owning_iterator_state<T, Policy>;
+
+   std::call_once(state::registered, []() {
+      py::class_<state>(py::handle(), "owning_iterator", py::module_local())
+         .def("__iter__", [](state &s) -> state& { return s; })
+         .def("__next__", [](state &s) -> typename T::value_type {
+            if (!s.first_or_done)
+               ++s.it;
+            else
+               s.first_or_done = false;
+            if (s.it == s.obj.end()) {
+               s.first_or_done = true;
+               throw py::stop_iteration();
+            }
+            return *s.it;
+         }, py::keep_alive< 0, 1 >(), Policy);
+   });
+
+   return cast(state(obj));
+}
+
+} // namespace python_coupling
+} // namespace walberla
diff --git a/src/python_coupling/helper/PybindHelper.h b/src/python_coupling/helper/PybindHelper.h
new file mode 100644
index 0000000000000000000000000000000000000000..d43c560ccf38643aeb4a6f562c9a9097e3eb9b33
--- /dev/null
+++ b/src/python_coupling/helper/PybindHelper.h
@@ -0,0 +1,65 @@
+
+//======================================================================================================================
+//
+//  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 CppPythonTypeEquality.h
+//! \ingroup python_coupling
+//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
+//
+//======================================================================================================================
+
+#pragma once
+
+#include "core/DataTypes.h"
+#include "python_coupling/PythonWrapper.h"
+
+namespace walberla {
+namespace python_coupling {
+
+#ifdef WALBERLA_CXX_COMPILER_IS_CLANG
+#pragma clang diagnostic push
+#pragma clang diagnostic ignored "-Wunused-function"
+#endif
+
+// fallback - check for bool
+template<typename T>
+inline static bool isCppEqualToPythonType( std::string n )
+{
+   return ( n == "numpy.bool_" || n =="bool" );
+}
+
+// native data types
+template<> inline bool isCppEqualToPythonType<float> ( std::string n) {return ( n == "numpy.float32" || n =="float32" );}
+template<> inline bool isCppEqualToPythonType<double>( std::string n) {return ( n == "numpy.float64" || n == "numpy.float_" || n =="float64" || n=="float");}
+
+
+template<> inline bool isCppEqualToPythonType<uint8_t  > ( std::string n) {return ( n == "numpy.uint8" || n =="uint8"  );}
+template<> inline bool isCppEqualToPythonType<uint16_t > ( std::string n) {return ( n == "numpy.uint16"|| n =="uint16" );}
+template<> inline bool isCppEqualToPythonType<uint32_t > ( std::string n) {return ( n == "numpy.uint32"|| n =="uint32" );}
+template<> inline bool isCppEqualToPythonType<uint64_t > ( std::string n) {return ( n == "numpy.uint64"|| n =="uint64" );}
+
+
+template<> inline bool isCppEqualToPythonType<int8_t  > ( std::string n) {return ( n == "numpy.int8"  || n =="int8");}
+template<> inline bool isCppEqualToPythonType<int16_t > ( std::string n) {return ( n == "numpy.int16" || n =="int16");}
+template<> inline bool isCppEqualToPythonType<int32_t > ( std::string n) {return ( n == "numpy.int32" || n =="int32");}
+template<> inline bool isCppEqualToPythonType<int64_t > ( std::string n) {return ( n == "numpy.int64" || n =="int64" || n == "int" );}
+
+#ifdef WALBERLA_CXX_COMPILER_IS_CLANG
+#pragma clang diagnostic pop
+#endif
+
+} // namespace python_coupling
+} // namespace walberla
diff --git a/src/python_coupling/helper/PythonIterableToStdVector.h b/src/python_coupling/helper/PythonIterableToStdVector.h
index 5a759aedfe2c15dccd8f67a935d88e5dc7483bdc..71583ec21cbb91d93a7986c466d24abfe48e5bd4 100644
--- a/src/python_coupling/helper/PythonIterableToStdVector.h
+++ b/src/python_coupling/helper/PythonIterableToStdVector.h
@@ -16,26 +16,30 @@
 //! \file PythonIterableToStdVector.h.h
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
 #pragma once
 
 #include <vector>
+#include "pybind11/numpy.h"
 
 namespace walberla {
 namespace python_coupling {
+namespace py = pybind11;
 
 
 template< typename T >
+
 inline
-std::vector< T > pythonIterableToStdVector( const boost::python::object& iterable )
+std::vector< T > pythonIterableToStdVector( const py::object& iterable )
 {
-    return std::vector< T >( boost::python::stl_input_iterator< T >( iterable ),
-                             boost::python::stl_input_iterator< T >( ) );
+   return py::cast<std::vector<T>>(py::array(iterable));
 }
 
 
+
 } // namespace python_coupling
 } // namespace walberla
 
diff --git a/src/python_coupling/helper/SharedPtrDeleter.h b/src/python_coupling/helper/SharedPtrDeleter.h
deleted file mode 100644
index a946cc03800ab0119c36bbee2c289193f2b8c5ae..0000000000000000000000000000000000000000
--- a/src/python_coupling/helper/SharedPtrDeleter.h
+++ /dev/null
@@ -1,65 +0,0 @@
-//======================================================================================================================
-//
-//  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 SharedPtrDeleter.h
-//! \ingroup python_export
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include "python_coupling/PythonWrapper.h"
-
-namespace walberla {
-namespace python_coupling {
-
-
-namespace internal
-{
-
-template<typename T>
-class SharedPtrDeleterTiedToPythonObject
-{
-public:
-   SharedPtrDeleterTiedToPythonObject( PyObject *object ) : object_( object )
-   {
-   }
-
-   void operator()( T * )
-   {
-       Py_DECREF( object_ );
-   }
-
-private:
-   PyObject *object_;
-};
-
-} // namespace internal
-
-
-template<typename T>
-shared_ptr<T> createSharedPtrFromPythonObject(boost::python::object pythonObject) {
-    T * ptr = boost::python::extract<T*>( pythonObject);
-    auto deleter = internal::SharedPtrDeleterTiedToPythonObject<T>(pythonObject.ptr());
-    Py_INCREF(pythonObject.ptr());
-    return shared_ptr<T>(ptr, deleter);
-}
-
-
-} // namespace python_coupling
-} // namespace walberla
-
-
diff --git a/src/python_coupling/helper/SliceToCellInterval.h b/src/python_coupling/helper/SliceToCellInterval.h
index d8ce968297450ba9ce6f1e7d586d1229490b508a..fd0a7ecdc5c2fa4c800b370131e5ea1070bfc786 100644
--- a/src/python_coupling/helper/SliceToCellInterval.h
+++ b/src/python_coupling/helper/SliceToCellInterval.h
@@ -16,205 +16,100 @@
 //! \file SliceToCellInterval.h
 //! \ingroup python_coupling
 //! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
 #pragma once
 
-#include "python_coupling/PythonWrapper.h"
 #include "core/cell/CellInterval.h"
-#include "domain_decomposition/StructuredBlockStorage.h"
-#include <boost/python/slice.hpp>
-
-namespace walberla {
-namespace python_coupling {
-
-
-   namespace internal
-   {
-      inline cell_idx_t normalizeIdx( boost::python::object pyIndex, uint_t coordinateSize )
-      {
-         using namespace boost::python;
 
-         cell_idx_t index;
-         if ( extract<cell_idx_t>( pyIndex ).check() )
-            index = extract<cell_idx_t> ( pyIndex );
-         else if ( extract<double>( pyIndex ).check() )
-            index = cell_idx_c( double ( extract<double>( pyIndex ) ) * double( coordinateSize) );
-         else {
-            PyErr_SetString( PyExc_IndexError, "Incompatible index data type" );
-            throw error_already_set();
-         }
-
-         if ( index < 0 )
-            return cell_idx_c(coordinateSize) + 1 + index;
-         else
-            return index;
-      }
-
-   } // namespace internal
+#include "domain_decomposition/StructuredBlockStorage.h"
 
+#include "python_coupling/PythonWrapper.h"
+namespace py = pybind11;
+namespace walberla
+{
+namespace python_coupling
+{
+namespace internal
+{
+inline cell_idx_t normalizeIdx(py::object pyIndex, uint_t coordinateSize)
+{
+   cell_idx_t index;
+
+   try{
+       index = pyIndex.cast<cell_idx_t>();
+   }
+   catch (std::exception &){
+       try {
+           auto test = pyIndex.cast<real_t>();
+           index = cell_idx_c( test * real_t( coordinateSize) );
+       }
+       catch (std::exception &) {
+           throw py::cast_error("Incompatible index data type");
+       }
+   }
 
-   //*******************************************************************************************************************
-   /*! Creates a CellInterval as subset from the complete domain-cell-bounding-box based on a Python slice
-   *
-   *     Example: Python Slice: [ :, 3, -1 ]  and a domain size of ( 3,4,5 )
-   *                 - x coordinate is the complete valid x-range indicated by the semicolon: i.e. [0,3)
-   *                 - y coordinate is just a normal index i.e. the range from [3,4)
-   *                 - z coordiante is the first valid coordinate from the back [4,5)
-   *
-   *     Python slices are tuples with slice classes as entry. Each slice has start, stop and step.
-   *     Steps are not supported since they can not be encoded in a CellInterval
-   */
-   //*******************************************************************************************************************
-   inline CellInterval globalPythonSliceToCellInterval( const shared_ptr<StructuredBlockStorage> & blocks,
-                                                        boost::python::tuple indexTuple )
+   if (index < 0)
+      return cell_idx_c(coordinateSize) + 1 + index;
+   else
+      return index;
+}
+
+} // namespace internal
+
+//*******************************************************************************************************************
+/*! Creates a CellInterval as subset from the complete domain-cell-bounding-box based on a Python slice
+ *
+ *     Example: Python Slice: [ :, 3, -1 ]  and a domain size of ( 3,4,5 )
+ *                 - x coordinate is the complete valid x-range indicated by the semicolon: i.e. [0,3)
+ *                 - y coordinate is just a normal index i.e. the range from [3,4)
+ *                 - z coordiante is the first valid coordinate from the back [4,5)
+ *
+ *     Python slices are tuples with slice classes as entry. Each slice has start, stop and step.
+ *     Steps are not supported since they can not be encoded in a CellInterval
+ */
+//*******************************************************************************************************************
+inline CellInterval globalPythonSliceToCellInterval(const shared_ptr< StructuredBlockStorage >& blocks,
+                                                    py::tuple indexTuple)
+{
+   using internal::normalizeIdx;
+
+   CellInterval bounds = blocks->getDomainCellBB();
+
+   if (len(indexTuple) != 3)
    {
-      using namespace boost::python;
-      using internal::normalizeIdx;
-
-      CellInterval bounds = blocks->getDomainCellBB();
-
-      if ( len(indexTuple) != 3 )
-      {
-         PyErr_SetString( PyExc_IndexError, "Slice needs three components" );
-         throw error_already_set();
-      }
-
-      CellInterval interval;
-      for( uint_t i=0; i<3; ++i )
-      {
-         if( ! extract< slice >(indexTuple[i]).check() )
-         {
-            cell_idx_t idx = normalizeIdx( indexTuple[i], uint_c( bounds.max()[i] ) );
-            interval.min()[i] = idx;
-            interval.max()[i] = idx;
-         }
-         else if ( extract< slice >(indexTuple[i]).check() )
-         {
-             slice s = extract< slice >(indexTuple[i]);
-
-             // Min
-             if ( s.start() == object() )
-                interval.min()[i] = bounds.min()[i];
-             else
-                interval.min()[i] = normalizeIdx( s.start(), uint_c( bounds.max()[i] ) );
-
-             // Max
-             if ( s.stop() == object() )
-                interval.max()[i] = bounds.max()[i];
-             else
-                interval.max()[i] = normalizeIdx( s.stop(), uint_c( bounds.max()[i] ) );
-
-             if ( s.step() != object() ) {
-                PyErr_SetString( PyExc_IndexError, "Steps in slice not supported." );
-                throw error_already_set();
-             }
-         }
-      }
-      return interval;
+      throw py::index_error("Slice needs three components");
    }
 
-
-
-   //*******************************************************************************************************************
-   /*! Creates a CellInterval based on a Python Slice as subset of a field
-   *
-   *   Similar to globalPythonSliceToCellInterval() with the following additional features:
-   *     - slice may have a forth component: [ :, 3, -1, 'g' ] with the only valid entry 'g' for ghost layers
-   *     - if this ghost layer marker is present, coordinate 0 addresses the outermost ghost layer, otherwise the
-   *       first inner cell is addressed
-   */
-   //*******************************************************************************************************************
-   template<typename Field_T>
-   CellInterval localPythonSliceToCellInterval( const Field_T & field,
-                                                boost::python::tuple indexTuple )
+   CellInterval interval;
+   for (uint_t i = 0; i < 3; ++i)
    {
-      using namespace boost::python;
-      using internal::normalizeIdx;
-
-      bool withGhostLayer=false;
-
-      if ( len(indexTuple) != 3 )
-      {
-         if ( len(indexTuple) == 4 )
-         {
-            std::string marker =  extract<std::string>( indexTuple[3]);
-            if ( marker == std::string("g") )
-               withGhostLayer = true;
-            else
-            {
-               PyErr_SetString( PyExc_IndexError, "Unknown marker in slice" );
-               throw error_already_set();
-            }
-         }
-         else
-         {
-            PyErr_SetString( PyExc_IndexError, "Slice needs three components ( + optional ghost layer marker )" );
-            throw error_already_set();
-         }
-      }
-
-      cell_idx_t gl = cell_idx_c( field.nrOfGhostLayers() );
-
-      CellInterval bounds;;
-      if ( withGhostLayer )
+      if (!py::isinstance< py::slice >(indexTuple[i]))
       {
-         bounds =  field.xyzSizeWithGhostLayer();
-         bounds.shift( gl,gl,gl );
+         cell_idx_t idx    = normalizeIdx(indexTuple[i], uint_c(bounds.max()[i]));
+         interval.min()[i] = idx;
+         interval.max()[i] = idx;
       }
-      else
-         bounds = field.xyzSize();
-
-
-      CellInterval interval;
-
-      for( uint_t i=0; i<3; ++i )
+      else if (py::isinstance< py::slice >(indexTuple[i]))
       {
-         if( !  extract< slice >(indexTuple[i]).check()  )
-         {
-            interval.min()[i] = normalizeIdx( indexTuple[i], uint_c(bounds.max()[i]) );
-            interval.max()[i] = normalizeIdx( indexTuple[i], uint_c(bounds.max()[i]) );
-         }
+         py::slice s = py::cast< py::slice >(indexTuple[i]);
+         // Min
+         if ( py::isinstance< py::none >(s.attr("start")) )
+            interval.min()[i] = bounds.min()[i];
          else
-         {
-             slice s = extract< slice >(indexTuple[i]);
+            interval.min()[i] = normalizeIdx( s.attr("start"), uint_c( bounds.min()[i] ) );
 
-             // Min
-             if ( s.start() == object() )
-                interval.min()[i] = bounds.min()[i];
-             else
-                interval.min()[i] = normalizeIdx( s.start(), uint_c(bounds.max()[i]) );
-
-             // Max
-             if ( s.stop() == object() )
-                interval.max()[i] = bounds.max()[i];
-             else
-                interval.max()[i] = normalizeIdx(  s.stop(), uint_c(bounds.max()[i]) );
-
-             if ( s.step() != object() ) {
-                PyErr_SetString( PyExc_IndexError, "Steps in slice not supported." );
-                throw error_already_set();
-             }
-         }
-      }
-
-      if ( withGhostLayer )
-         interval.shift( -gl,-gl,-gl );
-
-      // Range check
-      if ( ! field.xyzAllocSize().contains( interval ) ) {
-         PyErr_SetString( PyExc_IndexError, "Index out of bounds." );
-         throw error_already_set();
+         // Max
+         if ( py::isinstance< py::none >(s.attr("stop")) )
+            interval.max()[i] = bounds.max()[i];
+         else
+            interval.max()[i] = normalizeIdx( s.attr("stop"), uint_c( bounds.max()[i] ) );
       }
-
-      return interval;
    }
-
-
-
+   return interval;
+}
 
 } // namespace python_coupling
 } // namespace walberla
-
-
diff --git a/src/simd/SIMD.h b/src/simd/SIMD.h
index d0feb81e71504eef64201e8d6b925afb25fb0234..2998cef34775dfe799a287db98ab0f22ef0ee3e9 100644
--- a/src/simd/SIMD.h
+++ b/src/simd/SIMD.h
@@ -127,7 +127,7 @@ namespace simd {
 
 template<typename T> struct is_vector4_type {  static const bool value = false; };
 
-#if ( defined WALBERLA_CXX_COMPILER_IS_GNU ) && ( __GNUC__ >= 6 )
+#ifdef WALBERLA_CXX_COMPILER_IS_GNU
 #   pragma GCC diagnostic push
 #   pragma GCC diagnostic ignored "-Wignored-attributes"
 #endif
@@ -168,7 +168,7 @@ template<typename T> struct is_vector4_type {  static const bool value = false;
 #endif
 
 
-#if ( defined WALBERLA_CXX_COMPILER_IS_GNU ) && ( __GNUC__ >= 6 )
+#ifdef WALBERLA_CXX_COMPILER_IS_GNU
 #   pragma GCC diagnostic pop
 #endif
 
diff --git a/src/stencil/D2CornerStencil.h b/src/stencil/D2CornerStencil.h
index 574da37bc28cdd0e8f678d03d16d23da58e6be34..9d1716100079943e2229f7418c0c47c0ad2ebc3d 100644
--- a/src/stencil/D2CornerStencil.h
+++ b/src/stencil/D2CornerStencil.h
@@ -88,7 +88,7 @@ namespace stencil {
          /*! \name Iteration*/
          //@{
 
-         typedef stencil::Iterator<D2CornerStencil> iterator;
+         using iterator = stencil::Iterator<D2CornerStencil>;
 
          static iterator begin()           { return iterator(0); }
          static iterator beginNoCenter()   { return iterator(noCenterFirstIdx); }
@@ -239,7 +239,7 @@ namespace stencil {
 
    } // namespace internal
 
-   typedef internal::D2CornerStencil<> D2CornerStencil;
+   using D2CornerStencil = internal::D2CornerStencil<>;
 
 } // namespace stencil
 } // namespace walberla
diff --git a/src/stencil/D2Q4.h b/src/stencil/D2Q4.h
index 26131a49ac1407e0c0000ee7d64b2c517937113e..9c1f9b7b7f6da572bdb1f7d88963ec0ee94d19da 100644
--- a/src/stencil/D2Q4.h
+++ b/src/stencil/D2Q4.h
@@ -88,7 +88,7 @@ namespace stencil {
          /*! \name Iteration*/
          //@{
 
-         typedef stencil::Iterator<D2Q4> iterator;
+         using iterator = stencil::Iterator<D2Q4>;
 
          static iterator begin()           { return iterator(0); }
          static iterator beginNoCenter()   { return iterator(noCenterFirstIdx); }
@@ -239,7 +239,7 @@ namespace stencil {
 
    } // namespace internal
 
-   typedef internal::D2Q4<> D2Q4;
+   using D2Q4 = internal::D2Q4<>;
 
 } // namespace stencil
 } // namespace walberla
diff --git a/src/stencil/D2Q5.h b/src/stencil/D2Q5.h
index 8547a4747508e87075aaaa102ec3851ab7da929f..98cfbb0099d1cfdbee82a3ff74c5b5b6d50de15c 100644
--- a/src/stencil/D2Q5.h
+++ b/src/stencil/D2Q5.h
@@ -88,7 +88,7 @@ namespace stencil {
          /*! \name Iteration*/
          //@{
 
-         typedef stencil::Iterator<D2Q5> iterator;
+         using iterator = stencil::Iterator<D2Q5>;
 
          static iterator begin()           { return iterator(0); }
          static iterator beginNoCenter()   { return iterator(noCenterFirstIdx); }
@@ -239,7 +239,7 @@ namespace stencil {
 
    } // namespace internal
 
-   typedef internal::D2Q5<> D2Q5;
+   using D2Q5 = internal::D2Q5<>;
 
 } // namespace stencil
 } // namespace walberla
diff --git a/src/stencil/D2Q9.h b/src/stencil/D2Q9.h
index 4e79ab2feba545ae2640564710c8bd35986215f4..4c3dc0cd9948536a4d13473b57a04dd63f928365 100644
--- a/src/stencil/D2Q9.h
+++ b/src/stencil/D2Q9.h
@@ -88,7 +88,7 @@ namespace stencil {
          /*! \name Iteration*/
          //@{
 
-         typedef stencil::Iterator<D2Q9> iterator;
+         using iterator = stencil::Iterator<D2Q9>;
 
          static iterator begin()           { return iterator(0); }
          static iterator beginNoCenter()   { return iterator(noCenterFirstIdx); }
@@ -239,7 +239,7 @@ namespace stencil {
 
    } // namespace internal
 
-   typedef internal::D2Q9<> D2Q9;
+   using D2Q9 = internal::D2Q9<>;
 
 } // namespace stencil
 } // namespace walberla
diff --git a/src/stencil/D3CornerStencil.h b/src/stencil/D3CornerStencil.h
index 21ba0f491b9fafa388ab85bd440bf3ae6c8df0ac..b234a89d09776919e0c008391cd699ef72a2b9ef 100644
--- a/src/stencil/D3CornerStencil.h
+++ b/src/stencil/D3CornerStencil.h
@@ -88,7 +88,7 @@ namespace stencil {
          /*! \name Iteration*/
          //@{
 
-         typedef stencil::Iterator<D3CornerStencil> iterator;
+         using iterator = stencil::Iterator<D3CornerStencil>;
 
          static iterator begin()           { return iterator(0); }
          static iterator beginNoCenter()   { return iterator(noCenterFirstIdx); }
@@ -239,7 +239,7 @@ namespace stencil {
 
    } // namespace internal
 
-   typedef internal::D3CornerStencil<> D3CornerStencil;
+   using D3CornerStencil = internal::D3CornerStencil<>;
 
 } // namespace stencil
 } // namespace walberla
diff --git a/src/stencil/D3EdgeCornerStencil.h b/src/stencil/D3EdgeCornerStencil.h
index 8e1045b25fb97108edb9d8877f1cf6da810d50ad..e3e6a2832a025c39d9427cd14234ba3910fd5df5 100644
--- a/src/stencil/D3EdgeCornerStencil.h
+++ b/src/stencil/D3EdgeCornerStencil.h
@@ -88,7 +88,7 @@ namespace stencil {
          /*! \name Iteration*/
          //@{
 
-         typedef stencil::Iterator<D3EdgeCornerStencil> iterator;
+         using iterator = stencil::Iterator<D3EdgeCornerStencil>;
 
          static iterator begin()           { return iterator(0); }
          static iterator beginNoCenter()   { return iterator(noCenterFirstIdx); }
@@ -239,7 +239,7 @@ namespace stencil {
 
    } // namespace internal
 
-   typedef internal::D3EdgeCornerStencil<> D3EdgeCornerStencil;
+   using D3EdgeCornerStencil = internal::D3EdgeCornerStencil<>;
 
 } // namespace stencil
 } // namespace walberla
diff --git a/src/stencil/D3Q15.h b/src/stencil/D3Q15.h
index ba7fecb5c5f9eb499955ae2379345836bf5bf9a1..896c8c49b912752204af227920e53e56ba42a798 100644
--- a/src/stencil/D3Q15.h
+++ b/src/stencil/D3Q15.h
@@ -88,7 +88,7 @@ namespace stencil {
          /*! \name Iteration*/
          //@{
 
-         typedef stencil::Iterator<D3Q15> iterator;
+         using iterator = stencil::Iterator<D3Q15>;
 
          static iterator begin()           { return iterator(0); }
          static iterator beginNoCenter()   { return iterator(noCenterFirstIdx); }
@@ -239,7 +239,7 @@ namespace stencil {
 
    } // namespace internal
 
-   typedef internal::D3Q15<> D3Q15;
+   using D3Q15 = internal::D3Q15<>;
 
 } // namespace stencil
 } // namespace walberla
diff --git a/src/stencil/D3Q19.h b/src/stencil/D3Q19.h
index 0d4ca0fb87a334d496c2d99549769527a7c68a09..a7a520f69b4cd2d59e6b03f9ceffa186e5c5bb5a 100644
--- a/src/stencil/D3Q19.h
+++ b/src/stencil/D3Q19.h
@@ -88,7 +88,7 @@ namespace stencil {
          /*! \name Iteration*/
          //@{
 
-         typedef stencil::Iterator<D3Q19> iterator;
+         using iterator = stencil::Iterator<D3Q19>;
 
          static iterator begin()           { return iterator(0); }
          static iterator beginNoCenter()   { return iterator(noCenterFirstIdx); }
@@ -239,7 +239,7 @@ namespace stencil {
 
    } // namespace internal
 
-   typedef internal::D3Q19<> D3Q19;
+   using D3Q19 = internal::D3Q19<>;
 
 } // namespace stencil
 } // namespace walberla
diff --git a/src/stencil/D3Q27.h b/src/stencil/D3Q27.h
index 644ba99fe3a7fa570ec6cc21afc83b57cbb0a079..6ba403bd579e77ecc6b324538eeaa44e83ad64ad 100644
--- a/src/stencil/D3Q27.h
+++ b/src/stencil/D3Q27.h
@@ -88,7 +88,7 @@ namespace stencil {
          /*! \name Iteration*/
          //@{
 
-         typedef stencil::Iterator<D3Q27> iterator;
+         using iterator = stencil::Iterator<D3Q27>;
 
          static iterator begin()           { return iterator(0); }
          static iterator beginNoCenter()   { return iterator(noCenterFirstIdx); }
@@ -239,7 +239,7 @@ namespace stencil {
 
    } // namespace internal
 
-   typedef internal::D3Q27<> D3Q27;
+   using D3Q27 = internal::D3Q27<>;
 
 } // namespace stencil
 } // namespace walberla
diff --git a/src/stencil/D3Q6.h b/src/stencil/D3Q6.h
index 5567a7d79ce80fcb395094b4ea1543efd706f533..8b9f81d3ad1cea8de9ca2991a1de327514d90699 100644
--- a/src/stencil/D3Q6.h
+++ b/src/stencil/D3Q6.h
@@ -88,7 +88,7 @@ namespace stencil {
          /*! \name Iteration*/
          //@{
 
-         typedef stencil::Iterator<D3Q6> iterator;
+         using iterator = stencil::Iterator<D3Q6>;
 
          static iterator begin()           { return iterator(0); }
          static iterator beginNoCenter()   { return iterator(noCenterFirstIdx); }
@@ -239,7 +239,7 @@ namespace stencil {
 
    } // namespace internal
 
-   typedef internal::D3Q6<> D3Q6;
+   using D3Q6 = internal::D3Q6<>;
 
 } // namespace stencil
 } // namespace walberla
diff --git a/src/stencil/D3Q7.h b/src/stencil/D3Q7.h
index 672afa9e8240a1772008430ab52fdd6e17104fb9..8e14c9b001d47f384c466c5bd7e0c9a60775581e 100644
--- a/src/stencil/D3Q7.h
+++ b/src/stencil/D3Q7.h
@@ -88,7 +88,7 @@ namespace stencil {
          /*! \name Iteration*/
          //@{
 
-         typedef stencil::Iterator<D3Q7> iterator;
+         using iterator = stencil::Iterator<D3Q7>;
 
          static iterator begin()           { return iterator(0); }
          static iterator beginNoCenter()   { return iterator(noCenterFirstIdx); }
@@ -239,7 +239,7 @@ namespace stencil {
 
    } // namespace internal
 
-   typedef internal::D3Q7<> D3Q7;
+   using D3Q7 = internal::D3Q7<>;
 
 } // namespace stencil
 } // namespace walberla
diff --git a/src/stencil/Directions.h b/src/stencil/Directions.h
index 4d42e24cd80ee5c15000bb976906a13663a82d7d..cc3cfa29b1e320ca1b0f9c49f118402585036e21 100644
--- a/src/stencil/Directions.h
+++ b/src/stencil/Directions.h
@@ -10,7 +10,6 @@
 
 // core includes
 #include "core/DataTypes.h"
-#include "core/NonCreateable.h"
 #include "core/cell/Cell.h"
 #include "core/debug/Debug.h"
 
diff --git a/src/stencil/EdgeStencil.h b/src/stencil/EdgeStencil.h
index fb35bd94831996a3b8bc8a5272c93f68d3f9ed84..1c4982c995ffa520a81cdf22ff0442dc30ff1bd6 100644
--- a/src/stencil/EdgeStencil.h
+++ b/src/stencil/EdgeStencil.h
@@ -88,7 +88,7 @@ namespace stencil {
          /*! \name Iteration*/
          //@{
 
-         typedef stencil::Iterator<EdgeStencil> iterator;
+         using iterator = stencil::Iterator<EdgeStencil>;
 
          static iterator begin()           { return iterator(0); }
          static iterator beginNoCenter()   { return iterator(noCenterFirstIdx); }
@@ -239,7 +239,7 @@ namespace stencil {
 
    } // namespace internal
 
-   typedef internal::EdgeStencil<> EdgeStencil;
+   using EdgeStencil = internal::EdgeStencil<>;
 
 } // namespace stencil
 } // namespace walberla
diff --git a/src/timeloop/ITimeloop.h b/src/timeloop/ITimeloop.h
index cc43aec38a0256b27a18bc474c913707edd68752..3a77ca9951fe543cbf21247f73e37fe346cca45d 100644
--- a/src/timeloop/ITimeloop.h
+++ b/src/timeloop/ITimeloop.h
@@ -31,7 +31,7 @@ namespace timeloop {
 class ITimeloop
 {
 public:
-   virtual ~ITimeloop() {};
+   virtual ~ITimeloop() = default;
 
    virtual void run()        = 0;
    virtual void singleStep() = 0;
diff --git a/src/timeloop/PerformanceMeter.cpp b/src/timeloop/PerformanceMeter.cpp
index d7986320312cac1969331a2a8678821d09960873..98a6fc350569c876ce9440ef205d90e35be3819c 100644
--- a/src/timeloop/PerformanceMeter.cpp
+++ b/src/timeloop/PerformanceMeter.cpp
@@ -179,7 +179,7 @@ namespace timeloop {
     *******************************************************************************************************************/
    shared_ptr< std::map<std::string, real_t> > PerformanceMeter::getReduced ( int targetRank )
    {
-      typedef std::map<std::string, real_t> ResultMap;
+      using ResultMap = std::map<std::string, real_t>;
       shared_ptr < ResultMap > res;
 
       std::vector<real_t> totalNrCells;
diff --git a/src/timeloop/PerformanceMeter.h b/src/timeloop/PerformanceMeter.h
index 0efac6514774670b2efae9f08274be25a3e4ebb0..e5ccee05930623e27b390692df8f8606da2b4c35 100644
--- a/src/timeloop/PerformanceMeter.h
+++ b/src/timeloop/PerformanceMeter.h
@@ -70,7 +70,7 @@ namespace timeloop {
    class PerformanceMeter
    {
    public:
-      typedef std::function< uint_t(const IBlock &) > CountFunction;
+      using CountFunction = std::function<uint_t (const IBlock &)>;
 
 
       PerformanceMeter( StructuredBlockStorage & blockStorage );
diff --git a/src/timeloop/SelectableFunctionCreators.h b/src/timeloop/SelectableFunctionCreators.h
index 4deaa2ce49eea46714205b7198935b105a9d7b1b..965faf62731b49e438a85fe8ab5148e9be61fe28 100644
--- a/src/timeloop/SelectableFunctionCreators.h
+++ b/src/timeloop/SelectableFunctionCreators.h
@@ -55,7 +55,7 @@ namespace timeloop {
    template <typename FuncType>
    struct SelectableFunction
    {
-      SelectableFunction() {}
+      SelectableFunction() = default;
 
       SelectableFunction ( std::function< FuncType > fct,
                            const std::string& identifier            = std::string(),
@@ -77,7 +77,7 @@ namespace timeloop {
 
    struct BeforeFunction : public SelectableFunction< void () >
    {
-      BeforeFunction() {}
+      BeforeFunction() = default;
       BeforeFunction(std::function< void () > fct,
                      const std::string& id      = std::string(),
                      const Set<SUID>&   req    = Set<SUID>::emptySet(),
@@ -95,7 +95,7 @@ namespace timeloop {
 
    struct AfterFunction : public SelectableFunction< void () >
    {
-       AfterFunction() {}
+       AfterFunction() = default;
        AfterFunction(std::function< void () > fct,
                      const std::string& id      = std::string(),
                      const Set<SUID>&   req    = Set<SUID>::emptySet(),
@@ -128,7 +128,7 @@ namespace timeloop {
       Set<SUID>                                     incompatibleSelectors_;
    };
 
-   typedef FuncCreator<void (IBlock*) > Sweep;
+   using Sweep = FuncCreator<void (IBlock *)>;
 
 
    template<typename SweepClass>
diff --git a/src/timeloop/SweepTimeloop.cpp b/src/timeloop/SweepTimeloop.cpp
index 7fc72a38a9dd3f6c999a3d1fa37a8c677f88b667..15b40719112fceed4775077f5953d5102a12a509 100644
--- a/src/timeloop/SweepTimeloop.cpp
+++ b/src/timeloop/SweepTimeloop.cpp
@@ -54,10 +54,8 @@ void SweepTimeloop::doTimeStep(const Set<SUID> &selectors)
          }
 
          Sweep * selectedSweep = s.sweep.getUnique( selectors + bi->getState() );
-         if( !selectedSweep )
-            WALBERLA_ABORT("Selecting Sweep " << sweepIt->first << ": " <<
-                           "Ambiguous, or no sweep selected. Check your selector " <<
-                            selectors + bi->getState() << std::endl << s.sweep);
+         if (!selectedSweep)
+            continue;
 
          WALBERLA_LOG_PROGRESS_SECTION()
          {
@@ -111,9 +109,7 @@ void SweepTimeloop::doTimeStep(const Set<SUID> &selectors, WcTimingPool &timing)
          Sweep * selectedSweep = s.sweep.getUnique( selectors + bi->getState(), sweepName );
 
          if( !selectedSweep )
-            WALBERLA_ABORT("Selecting Sweep " << sweepIt->first << ": " <<
-                           "Ambiguous, or no sweep selected. Check your selector " <<
-                            selectors + bi->getState() << std::endl << s.sweep);
+            continue;
 
          WALBERLA_LOG_PROGRESS("Running sweep \"" << sweepName << "\" on block " << bi->getId() );
 
diff --git a/src/timeloop/SweepTimeloop.h b/src/timeloop/SweepTimeloop.h
index a9b4d6d2ad88692a1c86e674e82a1c2f80cd897d..3bd6c1488092c6f8528db3f73417600828599e11 100644
--- a/src/timeloop/SweepTimeloop.h
+++ b/src/timeloop/SweepTimeloop.h
@@ -129,7 +129,7 @@ namespace timeloop {
            nextId_(0), firstRun_(true)
       {}
 
-      virtual ~SweepTimeloop()
+      ~SweepTimeloop() override
       {
          for ( auto i = sweeps_.begin(); i != sweeps_.end(); ++i )
             delete i->second;
@@ -166,8 +166,8 @@ namespace timeloop {
             sweeps_.erase( *it );
       }
 
-      virtual void doTimeStep(const Set<SUID> &selectors);
-      virtual void doTimeStep(const Set<SUID> &selectors, WcTimingPool &tp);
+      void doTimeStep(const Set<SUID> &selectors) override;
+      void doTimeStep(const Set<SUID> &selectors, WcTimingPool &tp) override;
 
       uint_t nextId_;
       std::vector<uint_t> sweepsToDelete_;
diff --git a/src/timeloop/Timeloop.h b/src/timeloop/Timeloop.h
index e2cc5500191eab290607be954d4cc6306ae84560..48b7de6ce88342e04c7aac2472f09fd6b566773f 100644
--- a/src/timeloop/Timeloop.h
+++ b/src/timeloop/Timeloop.h
@@ -36,7 +36,7 @@
 namespace walberla {
 namespace timeloop {
 
-typedef std::function<void ()> VoidFctNoArguments;
+using VoidFctNoArguments = std::function<void ()>;
 
 
 //*******************************************************************************************************************
@@ -58,7 +58,7 @@ private:
    {
    public:
       LoggingStamp( const Timeloop & timeloop ) : timeloop_( timeloop ) {}
-      std::string stamp()
+      std::string stamp() override
       {
          std::ostringstream oss;
          int indention;
@@ -74,7 +74,7 @@ private:
              << std::setfill(' ') << std::right << timeloop_.curTimeStep_;
          return std::string("[") + oss.str() + std::string("]");
       }
-      uint_t maxStampWidth()
+      uint_t maxStampWidth() override
       {
          if( timeloop_.nrOfTimeSteps_ > 0 )
             return uint_c( std::ceil( std::log10( real_c( timeloop_.nrOfTimeSteps_ ) ) ) ) + uint_c(2);
@@ -111,7 +111,7 @@ public:
    //@{
    Timeloop( uint_t nrOfTimeSteps );
 
-   virtual ~Timeloop() {}
+   ~Timeloop() override = default;
    //@}
    //****************************************************************************************************************
 
@@ -119,19 +119,19 @@ public:
    //** Execution Control *******************************************************************************************
    /*! \name Execution Control*/
    //@{
-   virtual void run()                  { run(true); }
+   void run() override                  { run(true); }
    void run( const bool logTimeStep );
    void run( WcTimingPool & timing, const bool logTimeStep = true );
 
-   virtual void singleStep() { singleStep(true); }
+   void singleStep() override { singleStep(true); }
    void singleStep( const bool logTimeStep );
    void singleStep( WcTimingPool & timing, const bool logTimeStep = true );
 
-   void stop();
-   void synchronizedStop( bool stop );
+   void stop() override;
+   void synchronizedStop( bool stop ) override;
 
     void setCurrentTimeStepToZero()     { curTimeStep_ = 0;  }
-    void setCurrentTimeStep( uint_t ts) { curTimeStep_ = ts; }
+    void setCurrentTimeStep( uint_t ts) override { curTimeStep_ = ts; }
 
     //@}
    //****************************************************************************************************************
@@ -140,7 +140,7 @@ public:
    //** Registration Functions **************************************************************************************
    /*! \name Registration Functions */
    //@{
-   typedef size_t FctHandle;
+   using FctHandle = size_t;
 
 
     FctHandle addFuncBeforeTimeStep(const VoidFctNoArguments & f,
@@ -173,8 +173,8 @@ public:
    //** Timestep ****************************************************************************************************
    /*! \name Timestep */
    //@{
-   uint_t getCurrentTimeStep() const   { return curTimeStep_;   }
-   uint_t getNrOfTimeSteps()   const   { return nrOfTimeSteps_; }
+   uint_t getCurrentTimeStep() const override   { return curTimeStep_;   }
+   uint_t getNrOfTimeSteps()   const override   { return nrOfTimeSteps_; }
 
    //@}
    //****************************************************************************************************************
@@ -198,7 +198,7 @@ protected:
    uint_t curTimeStep_;   ///< current time step
    uint_t nrOfTimeSteps_; ///< total number of time steps
 
-   typedef selectable::SetSelectableObject<VoidFctNoArguments, SUID> SelectableFunc;
+   using SelectableFunc = selectable::SetSelectableObject<VoidFctNoArguments, SUID>;
    std::vector<SelectableFunc> beforeFunctions_;
    std::vector<SelectableFunc> afterFunctions_;
 
diff --git a/src/timeloop/python/Exports.cpp b/src/timeloop/python/Exports.cpp
deleted file mode 100644
index 3411e7f298a25172ea88ff309d49d1eff8c5cdc7..0000000000000000000000000000000000000000
--- a/src/timeloop/python/Exports.cpp
+++ /dev/null
@@ -1,89 +0,0 @@
-//======================================================================================================================
-//
-//  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 Exports.cpp
-//! \ingroup timeloop
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-// Do not reorder includes - the include order is important
-#include "python_coupling/PythonWrapper.h"
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-#include "python_coupling/Manager.h"
-#include "python_coupling/helper/ModuleScope.h"
-
-#include "timeloop/Timeloop.h"
-#include "timeloop/SweepTimeloop.h"
-
-using namespace boost::python;
-
-
-namespace walberla {
-namespace timeloop {
-
-#ifdef WALBERLA_CXX_COMPILER_IS_GNU
-#pragma GCC diagnostic push
-#pragma GCC diagnostic ignored "-Wconversion"
-#endif
-struct ITimeloopWrap : public ITimeloop, public wrapper<ITimeloop>
-{
-   void run() override                          {        this->get_override( "run" )();                     }
-   void singleStep() override                   {        this->get_override( "singleStep" )();              }
-   void stop() override                         {        this->get_override( "stop" )();                    }
-   void synchronizedStop( bool s ) override     {        this->get_override( "synchronizedStop" )(s);       }
-   void setCurrentTimeStep( uint_t ts) override {        this->get_override( "setCurrentTimeStep" )(ts);    }
-   uint_t getCurrentTimeStep() const override   { return this->get_override( "getCurrentTimeStep" )();      }
-   uint_t getNrOfTimeSteps()   const override   { return this->get_override( "getNrOfTimeSteps" )();        }
-};
-
-#ifdef WALBERLA_CXX_COMPILER_IS_GNU
-#pragma GCC diagnostic pop
-#endif
-
-void exportModuleToPython()
-{
-   python_coupling::ModuleScope timeloopModule( "timeloop" );
-
-   void ( Timeloop::*p_run1) ( const bool  ) = &Timeloop::run;
-   void ( Timeloop::*p_run2) ( WcTimingPool & , const bool ) = &Timeloop::run;
-
-   class_<ITimeloopWrap, boost::noncopyable > ("ITimeloop"  )
-      .def( "getCurrentTimeStep", pure_virtual( &ITimeloop::getCurrentTimeStep  ) )
-      .def( "getNrOfTimeSteps",   pure_virtual( &ITimeloop::getNrOfTimeSteps    ) )
-      .def( "stop",               pure_virtual( &ITimeloop::stop                ) )
-      .def( "synchronizedStop",   pure_virtual( &ITimeloop::synchronizedStop    ) )
-      .def( "run",                pure_virtual( &ITimeloop::run                 ) )
-      ;
-
-   class_<Timeloop, bases<ITimeloop>, boost::noncopyable>( "CppTimeloop", no_init )
-      .def( "run",                p_run1, args("logTimeStep") = true )
-      .def( "run",                p_run2, ( args("timingPool"), args("logTimeStep") = true ) )
-      ;
-   class_< SweepTimeloop , bases<Timeloop>,  boost::noncopyable > ( "CppSweepTimeloop", no_init )
-      .def( init< const shared_ptr<StructuredBlockStorage> & , uint_t  > () )
-      ;
-
-}
-
-
-} // namespace timeloop
-} // namespace walberla
-
-
-#endif //WALBERLA_BUILD_WITH_PYTHON
-
diff --git a/src/timeloop/python/Exports.h b/src/timeloop/python/Exports.h
deleted file mode 100644
index 4126ed455d0c9e1ab464717d30b1113fbc9a63b3..0000000000000000000000000000000000000000
--- a/src/timeloop/python/Exports.h
+++ /dev/null
@@ -1,41 +0,0 @@
-//======================================================================================================================
-//
-//  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 Exports.h
-//! \ingroup timeloop
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
-//======================================================================================================================
-
-#pragma once
-
-#include "waLBerlaDefinitions.h"
-
-
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-
-namespace walberla {
-namespace timeloop {
-
-
-
-   void exportModuleToPython();
-
-
-} // namespace timeloop
-} // namespace walberla
-
-
-#endif // WALBERLA_BUILD_WITH_PYTHON
diff --git a/src/vtk/BlockCellDataWriter.h b/src/vtk/BlockCellDataWriter.h
index d0eabef2ca4b3bf06a58363794b1985f45786d1d..877b5543b5905a34813febd98c90ff76a81ddf9a 100644
--- a/src/vtk/BlockCellDataWriter.h
+++ b/src/vtk/BlockCellDataWriter.h
@@ -110,8 +110,8 @@ namespace internal {
 class BlockCellDataWriter
 {
 public:
-            BlockCellDataWriter( const std::string& id ) : block_( NULL ), blockStorage_( NULL ), identifier_( id ) {}
-   virtual ~BlockCellDataWriter() {}
+            BlockCellDataWriter( const std::string& id ) : block_( nullptr ), blockStorage_( nullptr ), identifier_( id ) {}
+   virtual ~BlockCellDataWriter() = default;
 
    void configure( const IBlock& block, const StructuredBlockStorage& sbs ) { block_ = &block; blockStorage_ = &sbs; configure(); }
 
@@ -171,7 +171,7 @@ protected:
 
 private:
 
-   BlockCellDataWriter() {}
+   BlockCellDataWriter() = default;
 
 }; // class BlockCellDataWriter
 
@@ -197,7 +197,7 @@ inline void BlockCellDataWriter::push( Base64Writer& b64, const cell_idx_t x, co
 
 } // namespace internal
 
-typedef internal::BlockCellDataWriter BlockCellDataWriterInterface;
+using BlockCellDataWriterInterface = internal::BlockCellDataWriter;
 
 
 
@@ -219,19 +219,19 @@ class BlockCellDataWriter : public BlockCellDataWriterInterface
 
 public:
 
-   typedef T value_type;
+   using value_type = T;
 
    static const uint_t F_SIZE = F_SIZE_ARG;
 
             BlockCellDataWriter( const std::string & id ) : BlockCellDataWriterInterface( id ) {}
-   virtual ~BlockCellDataWriter() {}
+   ~BlockCellDataWriter() override = default;
 
-   void push( std::ostream & os, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f )
+   void push( std::ostream & os, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f ) override
    {
       vtk::toStream( os, evaluate( x, y, z, f ) );
    }
 
-   void push( vtk::Base64Writer & b64, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f )
+   void push( vtk::Base64Writer & b64, const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f ) override
    {
       b64 << evaluate( x, y, z, f );
    }
@@ -239,7 +239,7 @@ public:
    void push( std::ostream& os,  const cell_idx_t x,      const cell_idx_t y,      const cell_idx_t z,      const cell_idx_t f,
                                  const real_t localXCell, const real_t localYCell, const real_t localZCell,
                                  const real_t globalX,    const real_t globalY,    const real_t globalZ,
-                                 const real_t samplingDx, const real_t samplingDy, const real_t samplingDz )
+                                 const real_t samplingDx, const real_t samplingDy, const real_t samplingDz ) override
    {
       vtk::toStream( os, evaluate( x, y, z, f, localXCell, localYCell, localZCell,
                                    globalX, globalY, globalZ, samplingDx, samplingDy, samplingDz ) );
@@ -248,14 +248,14 @@ public:
    void push( Base64Writer& b64, const cell_idx_t x,      const cell_idx_t y,      const cell_idx_t z,      const cell_idx_t f,
                                  const real_t localXCell, const real_t localYCell, const real_t localZCell,
                                  const real_t globalX,    const real_t globalY,    const real_t globalZ,
-                                 const real_t samplingDx, const real_t samplingDy, const real_t samplingDz )
+                                 const real_t samplingDx, const real_t samplingDy, const real_t samplingDz ) override
    {
       b64 << evaluate( x, y, z, f, localXCell, localYCell, localZCell, globalX, globalY, globalZ, samplingDx, samplingDy, samplingDz );
    }
 
-   uint_t fSize() const { return F_SIZE; }
+   uint_t fSize() const override { return F_SIZE; }
 
-   std::string typeString() const { return vtk::typeToString< T >(); }
+   std::string typeString() const override { return vtk::typeToString< T >(); }
 
 protected:
    virtual T evaluate( const cell_idx_t x, const cell_idx_t y, const cell_idx_t z, const cell_idx_t f ) = 0;
@@ -285,7 +285,7 @@ template< typename T >
 class BlockCellDataWriterScalingAdapter : public T
 {
 public:
-   typedef typename T::value_type value_type;
+   using value_type = typename T::value_type;
    static const uint_t F_SIZE = T::F_SIZE;
 
    BlockCellDataWriterScalingAdapter( const std::string& id, const T & base, value_type factor ) 
diff --git a/src/vtk/CMakeLists.txt b/src/vtk/CMakeLists.txt
index a3b6bf30eb1ffe9400c4fa2dba951cd3a6596c81..6a12fce9b6bf3e367c9741c36f60a52aa06ac9f1 100644
--- a/src/vtk/CMakeLists.txt
+++ b/src/vtk/CMakeLists.txt
@@ -4,6 +4,6 @@
 #
 ###################################################################################################
 
-waLBerla_add_module( DEPENDS core domain_decomposition python_coupling )
+waLBerla_add_module( DEPENDS core blockforest domain_decomposition python_coupling )
 
 ###################################################################################################
diff --git a/src/vtk/ChainedFilter.h b/src/vtk/ChainedFilter.h
index 573fff5c160854ac95cc72880f3b2c192c8e82c6..187486a7459089f44647bcb9935846b1f6544fd8 100644
--- a/src/vtk/ChainedFilter.h
+++ b/src/vtk/ChainedFilter.h
@@ -37,7 +37,7 @@ class ChainedFilter {
 
 public:
 
-   typedef std::function< void ( CellSet& filteredCells, const IBlock& block, const StructuredBlockStorage& storage, const uint_t ghostLayers ) > CellFilter;
+   using CellFilter = std::function<void (CellSet &, const IBlock &, const StructuredBlockStorage &, const uint_t)>;
 
    void addFilter( const CellFilter& filter ) { filters_.push_back( filter ); }
 
diff --git a/src/vtk/DumpBlockStructureLevel.h b/src/vtk/DumpBlockStructureLevel.h
index 5d0b8953b01504d79ed71c33199c000e25ecece3..6c0e8932b21007cad71d73c7a93c38fbfe4cc772 100644
--- a/src/vtk/DumpBlockStructureLevel.h
+++ b/src/vtk/DumpBlockStructureLevel.h
@@ -37,9 +37,9 @@ public:
 
 protected:
 
-   void configure() { WALBERLA_ASSERT_NOT_NULLPTR( this->block_ ); WALBERLA_ASSERT_NOT_NULLPTR( this->blockStorage_ ); level_ = uint8_c( this->blockStorage_->getLevel( *(this->block_) ) ); }
+   void configure() override { WALBERLA_ASSERT_NOT_NULLPTR( this->block_ ); WALBERLA_ASSERT_NOT_NULLPTR( this->blockStorage_ ); level_ = uint8_c( this->blockStorage_->getLevel( *(this->block_) ) ); }
 
-   uint8_t evaluate( const cell_idx_t, const cell_idx_t, const cell_idx_t, const cell_idx_t ) { return level_; }
+   uint8_t evaluate( const cell_idx_t, const cell_idx_t, const cell_idx_t, const cell_idx_t ) override { return level_; }
 
    uint8_t level_;
 
diff --git a/src/vtk/DumpBlockStructureProcess.h b/src/vtk/DumpBlockStructureProcess.h
index 3bdd64e6cbb22c4da9d5e4d099568461d0fa1eab..0887b824395c806655087447e7ad149667af7f89 100644
--- a/src/vtk/DumpBlockStructureProcess.h
+++ b/src/vtk/DumpBlockStructureProcess.h
@@ -38,9 +38,9 @@ public:
 
 protected:
 
-   void configure() {}
+   void configure() override {}
 
-   int evaluate( const cell_idx_t, const cell_idx_t, const cell_idx_t, const cell_idx_t ) { return MPIManager::instance()->rank(); }
+   int evaluate( const cell_idx_t, const cell_idx_t, const cell_idx_t, const cell_idx_t ) override { return MPIManager::instance()->rank(); }
 
 }; // DumpBlockStructureProcess
 
diff --git a/src/vtk/Initialization.cpp b/src/vtk/Initialization.cpp
index 0b4fa456757bbe78a96d2b413d134201b809a145..4d88f1b21a8f989432a41d352661c7ae0c6936f7 100644
--- a/src/vtk/Initialization.cpp
+++ b/src/vtk/Initialization.cpp
@@ -42,7 +42,7 @@ static void splitVector( T& x, T& y, T& z, const Config::BlockHandle& bb, const
    std::string vector = bb.getParameter< std::string >( vertex );
    coordinates = string_split( vector, "<,> \t" );
 
-   coordinates.erase( std::remove_if( coordinates.begin(), coordinates.end(), std::bind( &std::string::empty,  std::placeholders::_1 ) ), coordinates.end() );
+   coordinates.erase( std::remove_if( coordinates.begin(), coordinates.end(), [](auto &s){ return s.empty(); } ), coordinates.end() );
 
    if( coordinates.size() != 3 )
       WALBERLA_ABORT( errorMsg );
@@ -59,7 +59,7 @@ static std::vector< std::string > splitList( const std::string& string )
    std::vector< std::string > list;
 
    list = string_split( string, ", \t" );
-   list.erase( std::remove_if( list.begin(), list.end(), std::bind( &std::string::empty,  std::placeholders::_1 ) ), list.end() );
+   list.erase( std::remove_if( list.begin(), list.end(), [](auto &s){ return s.empty(); } ), list.end() );
 
    return list;
 }
@@ -70,7 +70,7 @@ static void addStates( Set<SUID>& set, const std::string& string )
 {
    std::vector< std::string > states;
    states = string_split( string, ", \t" );
-   states.erase( std::remove_if( states.begin(), states.end(), std::bind( &std::string::empty,  std::placeholders::_1 ) ), states.end() );
+   states.erase( std::remove_if( states.begin(), states.end(), [](auto &s){ return s.empty(); } ), states.end() );
 
    for( auto it = states.begin(); it != states.end(); ++it )
       set += SUID( *it );
diff --git a/src/vtk/Initialization.h b/src/vtk/Initialization.h
index 22e5902180b3934dd2b39a40caaecb1fe3f2cbda..43304eac26ce9b0cb1a1b30c0c6246634079018f 100644
--- a/src/vtk/Initialization.h
+++ b/src/vtk/Initialization.h
@@ -37,11 +37,11 @@ namespace vtk {
 // For documentation see the documentation of function "initializeVTKOutput" in Initialization.cpp
 
 
-typedef std::function< void () > OutputFunction;
+using OutputFunction = std::function<void ()>;
 
 struct SelectableOutputFunction {
 
-   SelectableOutputFunction() {}
+   SelectableOutputFunction() = default;
    SelectableOutputFunction( OutputFunction of, const Set<SUID>& rgs, const Set<SUID>& igs ) :
       outputFunction( of ), requiredGlobalStates( rgs ), incompatibleGlobalStates( igs ) {}
 
@@ -76,9 +76,7 @@ void initializeVTKOutput( std::map< std::string, SelectableOutputFunction > & ou
                           const std::map< std::string, VTKOutput::CellFilter > & filters,
                           const std::map< std::string, VTKOutput::BeforeFunction > & beforeFunctions );
 
-typedef std::function< void ( std::vector< shared_ptr< BlockCellDataWriterInterface > > & writers,
-                                std::map< std::string, VTKOutput::CellFilter > &            filters,
-                                std::map< std::string, VTKOutput::BeforeFunction > &        beforeFunctions ) > RegisterVTKOutputFunction;
+using RegisterVTKOutputFunction = std::function<void (std::vector<shared_ptr<BlockCellDataWriterInterface>> &, std::map<std::string, VTKOutput::CellFilter> &, std::map<std::string, VTKOutput::BeforeFunction> &)>;
 
 void initializeVTKOutput( std::map< std::string, SelectableOutputFunction > & outputFunctions, const RegisterVTKOutputFunction& registerVTKOutputFunction,
                           const shared_ptr< const StructuredBlockStorage > & storage, const shared_ptr< Config > & config,
diff --git a/src/vtk/PointDataSource.h b/src/vtk/PointDataSource.h
index 382e0e79d82a292ed6c3d4edf957b2a7aa389458..af959aafee3388eedf288cc0d76e6c852a10e248 100644
--- a/src/vtk/PointDataSource.h
+++ b/src/vtk/PointDataSource.h
@@ -49,7 +49,7 @@ public:
       uint_t      components;
    };
 
-   virtual ~PointDataSource() {}
+   virtual ~PointDataSource() = default;
 
    virtual std::vector< Attributes > getAttributes() const = 0;
 
diff --git a/src/vtk/PolylineDataSource.h b/src/vtk/PolylineDataSource.h
index 59048019fe7dcb36069aafd2813a25f64e2618a7..af4b8dbafa9bb9877d08b7041a76313be4235102 100644
--- a/src/vtk/PolylineDataSource.h
+++ b/src/vtk/PolylineDataSource.h
@@ -49,9 +49,9 @@ public:
       uint_t      components;
    };
 
-   typedef std::vector< Vector3< real_t > > Polyline;
+   using Polyline = std::vector<Vector3<real_t>>;
 
-   virtual ~PolylineDataSource() {}
+   virtual ~PolylineDataSource() = default;
 
    virtual std::vector< Attributes > getAttributes() const = 0;
 
diff --git a/src/vtk/VTKOutput.cpp b/src/vtk/VTKOutput.cpp
index 8d8cf88f14bfa4bf4899629d0d85f787e1b6146d..345d4fda3c980210f4b035867ea23e001017b915 100644
--- a/src/vtk/VTKOutput.cpp
+++ b/src/vtk/VTKOutput.cpp
@@ -458,7 +458,7 @@ void VTKOutput::computeOutputPoints( std::vector<Vector3<real_t> > & points, std
    outputPoint.assign( points.size(), true );
    for( uint_t i = 0; i != points.size(); ++i )
    {
-      bool included = ( aabbInclusionFilters_.empty() ) ? true : false;
+      bool included = aabbInclusionFilters_.empty();
       for( auto aabb = aabbInclusionFilters_.begin(); aabb != aabbInclusionFilters_.end() && !included; ++aabb )
          if( aabb->contains( points[ i ][ 0 ], points[ i ][ 1 ], points[ i ][ 2 ] ) ) included = true;
       if( !included )
@@ -665,7 +665,7 @@ void VTKOutput::computeOutputPolylines( std::vector< std::vector< Vector3< real_
       auto outputPointIt = outputPolylinePointIt->begin();
       for( auto pointIt = polylineIt->begin(); pointIt != polylineIt->end(); ++pointIt, ++outputPointIt )
       {
-         bool included = ( aabbInclusionFilters_.empty() ) ? true : false;
+         bool included = aabbInclusionFilters_.empty();
          for( auto aabb = aabbInclusionFilters_.begin(); aabb != aabbInclusionFilters_.end() && !included; ++aabb )
             if( aabb->contains( ( *pointIt )[ 0 ], ( *pointIt )[ 1 ], ( *pointIt )[ 2 ] ) ) included = true;
          if( !included )
diff --git a/src/vtk/VTKOutput.h b/src/vtk/VTKOutput.h
index a9c9ec12df53300279fb05d019e4ba4d4a35b3ab..d90586b5ca9d3cb5bf516733e4be98254a015768 100644
--- a/src/vtk/VTKOutput.h
+++ b/src/vtk/VTKOutput.h
@@ -52,21 +52,19 @@ class VTKOutput : public NonCopyable {
 private:
 
    class VTKGEN : public uid::IndexGenerator< VTKGEN, size_t >{};
-   typedef UID< VTKGEN > VTKUID;
+   using VTKUID = UID<VTKGEN>;
 
    // types used during vertex-index mapping procedure when writing (P)VTU files
-   typedef std::tuple< cell_idx_t, cell_idx_t, cell_idx_t > Vertex;
-   typedef std::tuple< real_t,     real_t,     real_t >     VertexCoord;
-   typedef int32_t Index;
+   using Vertex = std::tuple<cell_idx_t, cell_idx_t, cell_idx_t>;
+   using VertexCoord = std::tuple<real_t, real_t, real_t>;
+   using Index = int32_t;
 
    struct VertexCompare {
       bool operator()( const Vertex& lhs, const Vertex& rhs ) const
       {
-         if( std::get<0>(lhs) < std::get<0>(rhs) ||
+         return std::get<0>(lhs) < std::get<0>(rhs) ||
              ( std::get<0>(lhs) == std::get<0>(rhs) && std::get<1>(lhs) < std::get<1>(rhs) ) ||
-             ( std::get<0>(lhs) == std::get<0>(rhs) && std::get<1>(lhs) == std::get<1>(rhs) && std::get<2>(lhs) < std::get<2>(rhs) ) )
-            return true;
-         return false;
+             ( std::get<0>(lhs) == std::get<0>(rhs) && std::get<1>(lhs) == std::get<1>(rhs) && std::get<2>(lhs) < std::get<2>(rhs) );
       }
    };
 
@@ -143,9 +141,8 @@ public:
                                                                      const bool continuousNumbering, const bool binary, const bool littleEndian,
                                                                      const bool useMPIIO, const uint_t initialExecutionCount );
 
-   typedef std::function< void () > BeforeFunction;
-   typedef std::function< void ( CellSet& filteredCells, const IBlock& block,
-                                   const StructuredBlockStorage& storage, const uint_t ghostLayers ) >  CellFilter;
+   using BeforeFunction = std::function<void ()>;
+   using CellFilter = std::function<void (CellSet &, const IBlock &, const StructuredBlockStorage &, const uint_t)>;
 
    ~VTKOutput();
 
@@ -353,7 +350,7 @@ inline void VTKOutput::addAABBInclusionFilter( const AABB & aabb )
    if( pointDataSource_ || polylineDataSource_ )
       aabbInclusionFilters_.push_back( aabb );
    else
-      cellInclusionFunctions_.push_back( AABBCellFilter(aabb) );
+      cellInclusionFunctions_.emplace_back(AABBCellFilter(aabb) );
 }
 
 
@@ -370,7 +367,7 @@ inline void VTKOutput::addAABBExclusionFilter( const AABB & aabb )
    if( pointDataSource_ || polylineDataSource_ )
       aabbExclusionFilters_.push_back( aabb );
    else
-      cellExclusionFunctions_.push_back( AABBCellFilter(aabb) );
+      cellExclusionFunctions_.emplace_back(AABBCellFilter(aabb) );
 }
 
 
diff --git a/src/waLBerlaDefinitions.in.h b/src/waLBerlaDefinitions.in.h
index 496f223b731285a7fe942d29598fe84324c15e3f..c729cd2afd5e085be341517a3442d1d7e469273b 100644
--- a/src/waLBerlaDefinitions.in.h
+++ b/src/waLBerlaDefinitions.in.h
@@ -41,6 +41,8 @@
 
 #cmakedefine WALBERLA_THREAD_SAFE_LOGGING
 
+#cmakedefine WALBERLA_STL_BOUNDS_CHECKS
+
 // Compiler
 #cmakedefine WALBERLA_CXX_COMPILER_IS_GNU
 #cmakedefine WALBERLA_CXX_COMPILER_IS_INTEL
@@ -49,12 +51,7 @@
 #cmakedefine WALBERLA_CXX_COMPILER_IS_CLANG
 
 #cmakedefine WALBERLA_USE_STD_EXPERIMENTAL_FILESYSTEM
-#cmakedefine WALBERLA_USE_STD_FILESYSTEM
 #cmakedefine WALBERLA_USE_STD_EXPERIMENTAL_ANY
-#cmakedefine WALBERLA_USE_STD_ANY
-#cmakedefine WALBERLA_USE_STD_EXPERIMENTAL_OPTIONAL
-#cmakedefine WALBERLA_USE_STD_OPTIONAL
-#cmakedefine WALBERLA_USE_STD_VARIANT
 #cmakedefine WALBERLA_BUILD_WITH_BACKTRACE
 #ifdef WALBERLA_BUILD_WITH_BACKTRACE
 #define WALBERLA_BACKTRACE_HEADER "${Backtrace_HEADER}"
diff --git a/src/walberla.h b/src/walberla.h
index c6e9858ed1e42fa07a467d9a555b96b341216b59..7136333e455c37e7b23cb3ade33b55504708d8ed 100644
--- a/src/walberla.h
+++ b/src/walberla.h
@@ -34,7 +34,6 @@
 #include "pde/all.h"
 #include "pe_coupling/all.h"
 #include "postprocessing/all.h"
-#include "python_coupling/all.h"
 #include "stencil/all.h"
 #include "timeloop/all.h"
 #include "vtk/all.h"
diff --git a/src/python_coupling/all.h b/tests/core/Angles.test.cpp
similarity index 64%
rename from src/python_coupling/all.h
rename to tests/core/Angles.test.cpp
index 9c16b51e179530d2824bc3f5e6e234b9112dfa90..6190dc41bdaf36fd3c177ac9201614ad57a293b4 100644
--- a/src/python_coupling/all.h
+++ b/tests/core/Angles.test.cpp
@@ -13,20 +13,32 @@
 //  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 all.h
-//! \ingroup python_coupling
-//! \author Christian Godenschwager <christian.godenschwager@fau.de>
-//! \brief Collective header file for module python_coupling
+//! \file 
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
 
-#pragma once
+#include "core/DataTypes.h"
+#include "core/math/Angles.h"
+#include "core/debug/TestSubsystem.h"
 
-#include "waLBerlaDefinitions.h"
+namespace walberla{
 
-#ifdef WALBERLA_BUILD_WITH_PYTHON
-   #include "PythonCallback.h"
-   #include "CreateConfig.h"
-   #include "Shell.h"
-   #include "TimeloopIntercept.h"
-#endif
+int main( int /*argc*/, char** /*argv*/ )
+{
+   using namespace walberla::math;
+
+   debug::enterTestMode();
+
+   WALBERLA_CHECK_FLOAT_EQUAL( radToDeg(half_pi), 90_r );
+   WALBERLA_CHECK_FLOAT_EQUAL( half_pi, degToRad(90_r) );
+
+   return EXIT_SUCCESS;
+}
+
+} //namespace walberla
+
+int main( int argc, char** argv )
+{
+   return walberla::main(argc, argv);
+}
diff --git a/tests/core/CMakeLists.txt b/tests/core/CMakeLists.txt
index 72b8e4537f2c9b7fa1e4c0dfc4bf89db6db682cb..992b516f4b55412222fc66c2ca4043f1d9fca05e 100644
--- a/tests/core/CMakeLists.txt
+++ b/tests/core/CMakeLists.txt
@@ -179,15 +179,26 @@ waLBerla_execute_test( NAME SweepTimeloopTimerReduction PROCESSES 9  )
 # core #
 ########
 
-waLBerla_compile_test( FILES AllHeaderTest.cpp DEPENDS blockforest field geometry pe pe_coupling python_coupling )
+if ( WALBERLA_BUILD_WITH_PYTHON )
+   waLBerla_compile_test( FILES AllHeaderTest.cpp DEPENDS blockforest field geometry pe pe_coupling python_coupling )
+else()
+   waLBerla_compile_test( FILES AllHeaderTest.cpp DEPENDS blockforest field geometry pe pe_coupling )
+endif()
 waLBerla_execute_test( NAME AllHeaderTest )
 
+waLBerla_compile_test( FILES Angles.test.cpp )
+waLBerla_execute_test( NAME Angles )
+
 waLBerla_compile_test( FILES ConcatIterator.cpp )
 waLBerla_execute_test( NAME ConcatIterator )
 
 waLBerla_compile_test( FILES DataTypesTest.cpp )
 waLBerla_execute_test( NAME DataTypesTest )
 
+waLBerla_compile_test( FILES DebugSTLTest.cpp )
+waLBerla_execute_test( NAME DebugSTLTest )
+set_tests_properties(DebugSTLTest PROPERTIES WILL_FAIL TRUE)
+
 waLBerla_compile_test( FILES FunctionTraitsTest.cpp )
 waLBerla_execute_test( NAME FunctionTraitsTest )
 
@@ -212,7 +223,7 @@ waLBerla_execute_test( NAME UNIQUEID PROCESSES 4)
 waLBerla_compile_test( FILES VersionTest.cpp )
 waLBerla_execute_test( NAME VersionTest )
 
-if( WALBERLA_BUILD_WITH_BOOST OR WALBERLA_USE_STD_VARIANT )
+if ( WALBERLA_CXX_COMPILER_IS_INTEL AND NOT CMAKE_CXX_COMPILER_VERSION VERSION_LESS "20.0" )
   waLBerla_compile_test( FILES VariantTest )
   waLBerla_execute_test( NAME VariantTest )
 endif()
diff --git a/src/geometry/python/Exports.h b/tests/core/DebugSTLTest.cpp
similarity index 66%
rename from src/geometry/python/Exports.h
rename to tests/core/DebugSTLTest.cpp
index 5b92ae1da515066a56f14dd3d3ba7020b413d502..173be59a14acbdd99e2bbf828888155db54a51f9 100644
--- a/src/geometry/python/Exports.h
+++ b/tests/core/DebugSTLTest.cpp
@@ -13,26 +13,25 @@
 //  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 PythonExports.h
-//! \ingroup geometry
-//! \author Martin Bauer <martin.bauer@fau.de>
+//! \file GridGeneratorTest.cpp
+//! \author Dominik Thoennes <dominik.thoennes@fau.de>
 //
 //======================================================================================================================
 
-#pragma once
+#include <cstdlib>
+#include <vector>
 
 #include "waLBerlaDefinitions.h"
-#ifdef WALBERLA_BUILD_WITH_PYTHON
 
-namespace walberla {
-namespace geometry {
-
-
-   void exportModuleToPython();
-
-
-} // namespace geometry
-} // namespace walberla
-
-
-#endif // WALBERLA_BUILD_WITH_PYTHON
+// this test is expected to fail
+int main(int /*argc*/, char** /*argv*/)
+{
+   int ret = EXIT_FAILURE;
+#ifdef WALBERLA_STL_BOUNDS_CHECKS
+   std::vector< int > a(100);
+   // this throws an exception if the debug STL is used
+   // otherwise main will return 0 and the test fails since it is expected to fail
+   if (a[200] != 1337) { ret = EXIT_SUCCESS; }
+#endif
+   return ret;
+}
\ No newline at end of file
diff --git a/tests/core/FunctionTraitsTest.cpp b/tests/core/FunctionTraitsTest.cpp
index 4124fb6bfc00e8e1b140c5d9bd766027c88d6c4a..8c378eceaa7f16bf08a8400f31a35ffa028f6a9d 100644
--- a/tests/core/FunctionTraitsTest.cpp
+++ b/tests/core/FunctionTraitsTest.cpp
@@ -30,7 +30,7 @@ template< typename F>
 struct SomeClass
 {
    template< typename T>
-   static bool checkParameter1( typename std::enable_if< (FunctionTraits<F>::arity > 1 ), T >::type * = 0) {
+   static bool checkParameter1( typename std::enable_if< (FunctionTraits<F>::arity > 1 ), T >::type * = nullptr) {
 
       // The keyword "template" before "argument<1>" is crucial when these functions are inside a class. If the
       // keyword is dropped, compilers interpret "<1" as an arithmetic expression and therefore throw errors.
diff --git a/tests/core/GridGeneratorTest.cpp b/tests/core/GridGeneratorTest.cpp
index 7971b5f07b1861e03e8c63027903eb0cf30df2c9..6702794590227854030814f8e2fe648e36bfa031 100644
--- a/tests/core/GridGeneratorTest.cpp
+++ b/tests/core/GridGeneratorTest.cpp
@@ -98,7 +98,7 @@ void rangeBasedTest()
    auto dx = Vector3<real_t>( Grid::iterator::getUnitCellX(spacing), Grid::iterator::getUnitCellY(spacing), Grid::iterator::getUnitCellZ(spacing) );
    auto lowerIt  = typename Grid::iterator(domain, Vector3<real_t>(5,5,5) - dx * 30, spacing);
    auto endIt = typename Grid::iterator();
-   for ( const auto& pt : Grid(domain, Vector3<real_t>(5,5,5) - dx * 30, spacing) )
+   for ( const auto pt : Grid(domain, Vector3<real_t>(5,5,5) - dx * 30, spacing) )
    {
       WALBERLA_CHECK( lowerIt != endIt );
       WALBERLA_CHECK_FLOAT_EQUAL( *lowerIt, pt);
diff --git a/tests/core/VariantTest.cpp b/tests/core/VariantTest.cpp
index a879b5b98a7020a93f51bcb20ea1baf952a97eba..89eaf99ea13a6b7570238baef23eb4aa45df4efc 100644
--- a/tests/core/VariantTest.cpp
+++ b/tests/core/VariantTest.cpp
@@ -41,36 +41,31 @@ int main( int /*argc*/, char** /*argv*/ )
    walberla::variant<int, float> v, w;
    v = 12; // v contains int
    int i = walberla::get<int>( v );
-#ifdef WALBERLA_USE_STD_VARIANT
-   WALBERLA_CHECK( i == 12 ); // boost::variant cannot use == with variant and type
-#endif
    WALBERLA_CHECK( 0 == 12 - i );
    w = walberla::get<int>( v );
    w = v;
-#ifdef WALBERLA_USE_STD_VARIANT
-   WALBERLA_CHECK( w == 12 ); // boost::variant cannot use == with variant and type
-#endif
    WALBERLA_CHECK( 0 == 12 - i );
 
    //  walberla::get<double>(v); // error: no double in [int, float]
    //  walberla::get<3>(v);      // error: valid index values are 0 and 1
 
    try {
-      walberla::get<float>( w ); // w contains int, not float: will throw
+      float f = walberla::get<float>( w ); // w contains int, not float: will throw
+      std::cout << f << std::endl;
    } catch ( const walberla::bad_variant_access& ) {}
 
    walberla::variant<std::string> x( "abc" ); // converting constructors work when unambiguous
    x = "def"; // converting assignment also works when unambiguous
 
    std::cout << "hallo" << std::endl;
-   walberla::variant<std::string, bool> y( "abc" ); // casts to bool when passed a char const *
+   walberla::variant<std::string, bool> y( true );
    std::cout << "eoo" << std::endl;
    WALBERLA_CHECK( walberla::holds_alternative<bool>( y ) ); // succeeds
    y = "xyz"s;
    WALBERLA_CHECK( walberla::holds_alternative<std::string>( y ) ); //succeeds
 
    std::cout << "bye" << std::endl;
-   std::vector<var_t> vec {10, 15l, 1.5, "hello"};
+   std::vector<var_t> vec = {10, 15l, 1.5, "hello"};
 
    for ( auto& z : vec ) {
       // 1. void visitor, only called for side-effects (here, for I/O)
diff --git a/tests/core/mpi/ReduceTest.cpp b/tests/core/mpi/ReduceTest.cpp
index fa376b8d70f2ddab3a7c751cb0d2d6fc3c4f0b53..9093d63f36bf8a9880537ba5253655b94c439423 100644
--- a/tests/core/mpi/ReduceTest.cpp
+++ b/tests/core/mpi/ReduceTest.cpp
@@ -140,8 +140,8 @@ void runTestAllReduceBool()
 
    const bool allTrue  = true;
    const bool allFalse = false;
-   const bool oneTrue  = rank == 0 ? true: false;
-   const bool mixed    = ( rank % 2 ) ? true : false;
+   const bool oneTrue  = rank == 0;
+   const bool mixed    = ( rank % 2 ) != 0;
 
    WALBERLA_CHECK_EQUAL( mpi::allReduce( allTrue,  mpi::LOGICAL_AND ), true  );
    WALBERLA_CHECK_EQUAL( mpi::allReduce( allFalse, mpi::LOGICAL_AND ), false );
@@ -212,8 +212,8 @@ void runTestAllReduceBool()
    std::vector<bool> bools( 4u );
    bools[0] = true;
    bools[1] = false;
-   bools[2] = rank == 0 ? true : false;
-   bools[3] = ( rank % 2 ) ? true : false;
+   bools[2] = rank == 0;
+   bools[3] = ( rank % 2 ) != 0;
 
    {
       std::vector<bool> result( bools );
@@ -255,8 +255,8 @@ void runTestReduceBool( int recvRank )
 
    const bool allTrue  = true;
    const bool allFalse = false;
-   const bool oneTrue  = rank == 0 ? true: false;
-   const bool mixed    = ( rank % 2 ) ? true : false;
+   const bool oneTrue  = rank == 0;
+   const bool mixed    = ( rank % 2 ) != 0;
 
    {
       bool result0 = mpi::reduce( allTrue,  mpi::LOGICAL_AND, recvRank );
@@ -387,8 +387,8 @@ void runTestReduceBool( int recvRank )
    std::vector<bool> bools( 4u );
    bools[0] = true;
    bools[1] = false;
-   bools[2] = rank == 0 ? true : false;
-   bools[3] = ( rank % 2 ) ? true : false;
+   bools[2] = rank == 0;
+   bools[3] = ( rank % 2 ) != 0;
 
    {
       std::vector<bool> result( bools );
diff --git a/tests/geometry/BinaryRawFileTest.cpp b/tests/geometry/BinaryRawFileTest.cpp
index cc219ea1b162c98abaa93f246f6a3c841f276c5f..22ce402d95a9d16f6a284dd8c780fe532052053e 100644
--- a/tests/geometry/BinaryRawFileTest.cpp
+++ b/tests/geometry/BinaryRawFileTest.cpp
@@ -59,7 +59,7 @@ void test( const std::string & filename, const Vector3<uint_t> & size, const std
       
       CellInterval ci( 0, 0, 0, cell_idx_c( size[0] ) - 1, cell_idx_c( size[1] ) - 1, cell_idx_c( size[2] ) - 1 );
 
-      for (const Cell & c : ci)
+      for (const Cell c : ci)
       {
          field->get( c ) = brf.get( uint_c( c[0] ), uint_c( c[1] ), uint_c( c[2] ) );
       }
@@ -100,7 +100,7 @@ void testScaled( const std::string & filename, const Vector3<uint_t> & size, con
 
       CellInterval ci( 0, 0, 0, cell_idx_c( scaledSize[0] ) - 1, cell_idx_c( scaledSize[1] ) - 1, cell_idx_c( scaledSize[2] ) - 1 );
 
-      for (const Cell & c : ci)
+      for (const Cell c : ci)
       {
          auto pos = blocks->getBlockLocalCellCenter( block, c );
          field->get( c ) = brfi.get( pos );
diff --git a/tests/lbm/CMakeLists.txt b/tests/lbm/CMakeLists.txt
index 05ce21c0d7caa42ead1a491b0c689a83ffb4fa9c..a73ef7f47ead3b856ea8be657946868bb89e0687 100644
--- a/tests/lbm/CMakeLists.txt
+++ b/tests/lbm/CMakeLists.txt
@@ -76,41 +76,62 @@ waLBerla_execute_test( NAME QCriterionTest )
 
 # Code Generation
 if( WALBERLA_BUILD_WITH_CODEGEN )
-
-waLBerla_generate_target_from_python(NAME LbCodeGenerationExampleGenerated
-      FILE codegen/LbCodeGenerationExample.py
-      OUT_FILES LbCodeGenerationExample_LatticeModel.cpp LbCodeGenerationExample_LatticeModel.h
-      LbCodeGenerationExample_NoSlip.cpp LbCodeGenerationExample_NoSlip.h
-      LbCodeGenerationExample_UBB.cpp LbCodeGenerationExample_UBB.h )
-waLBerla_compile_test( FILES codegen/LbCodeGenerationExample.cpp DEPENDS LbCodeGenerationExampleGenerated)
-
-waLBerla_generate_target_from_python(NAME FluctuatingMRTGenerated FILE codegen/FluctuatingMRT.py
-                              OUT_FILES FluctuatingMRT_LatticeModel.cpp FluctuatingMRT_LatticeModel.h )
-waLBerla_compile_test( FILES codegen/FluctuatingMRT.cpp DEPENDS FluctuatingMRTGenerated)
-
-waLBerla_generate_target_from_python(NAME FieldLayoutAndVectorizationTestGenerated FILE codegen/FieldLayoutAndVectorizationTest.py
-                                     OUT_FILES FieldLayoutAndVectorizationTest_FZYX_Vec_LatticeModel.cpp FieldLayoutAndVectorizationTest_FZYX_Vec_LatticeModel.h
-                                               FieldLayoutAndVectorizationTest_FZYX_NoVec_LatticeModel.cpp FieldLayoutAndVectorizationTest_FZYX_NoVec_LatticeModel.h
-                                               FieldLayoutAndVectorizationTest_ZYXF_Vec_LatticeModel.cpp FieldLayoutAndVectorizationTest_ZYXF_Vec_LatticeModel.h
-                                               FieldLayoutAndVectorizationTest_ZYXF_NoVec_LatticeModel.cpp FieldLayoutAndVectorizationTest_ZYXF_NoVec_LatticeModel.h )
-waLBerla_compile_test( FILES codegen/FieldLayoutAndVectorizationTest.cpp DEPENDS FieldLayoutAndVectorizationTestGenerated)
-
-
-# set( configs D3Q19_SRT_INCOMP D3Q19_SRT_COMP D3Q27_SRT_INCOMP D3Q27_SRT_COMP D3Q27_TRT_INCOMP D3Q19_MRT_COMP )
-set( configs D3Q19_SRT_INCOMP )
-list( APPEND RefinementTargets "" )
-
-foreach( config ${configs} )
-    waLBerla_generate_target_from_python(NAME CodeGenerationRefinementGenerated_${config}
-            CODEGEN_CFG ${config}
-            FILE codegen/CodeGenerationRefinement.py
-            OUT_FILES CodeGenerationRefinement_${config}_LatticeModel.cpp CodeGenerationRefinement_${config}_LatticeModel.h
-            )
-    list( APPEND RefinementTargets CodeGenerationRefinementGenerated_${config} )
-endforeach()
-
-waLBerla_compile_test( FILES codegen/CodeGenerationRefinement.cpp DEPENDS ${RefinementTargets} )
-
-
+    add_subdirectory(codegen)
+
+    waLBerla_generate_target_from_python(NAME GeneratedOutflowBCGenerated
+            FILE codegen/GeneratedOutflowBC.py
+            OUT_FILES GeneratedOutflowBC_Sweep.cpp GeneratedOutflowBC_Sweep.h
+            GeneratedOutflowBC_MacroSetter.cpp GeneratedOutflowBC_MacroSetter.h
+            GeneratedOutflowBC_Dynamic_UBB.cpp GeneratedOutflowBC_Dynamic_UBB.h
+            GeneratedOutflowBC_Static_UBB.cpp GeneratedOutflowBC_Static_UBB.h
+            GeneratedOutflowBC_NoSlip.cpp GeneratedOutflowBC_NoSlip.h
+            GeneratedOutflowBC_Outflow.cpp GeneratedOutflowBC_Outflow.h
+            GeneratedOutflowBC_PackInfo.cpp GeneratedOutflowBC_PackInfo.h
+            GeneratedOutflowBC_InfoHeader.h)
+    waLBerla_compile_test( FILES codegen/GeneratedOutflowBC.cpp DEPENDS GeneratedOutflowBCGenerated)
+    waLBerla_execute_test( NAME GeneratedOutflowBC COMMAND $<TARGET_FILE:GeneratedOutflowBC> ${CMAKE_CURRENT_SOURCE_DIR}/codegen/GeneratedOutflowBC.prm  )
+
+
+    waLBerla_generate_target_from_python(NAME LbCodeGenerationExampleGenerated
+            FILE codegen/LbCodeGenerationExample.py
+            OUT_FILES LbCodeGenerationExample_LatticeModel.cpp LbCodeGenerationExample_LatticeModel.h
+            LbCodeGenerationExample_NoSlip.cpp LbCodeGenerationExample_NoSlip.h
+            LbCodeGenerationExample_UBB.cpp LbCodeGenerationExample_UBB.h )
+    waLBerla_compile_test( FILES codegen/LbCodeGenerationExample.cpp DEPENDS LbCodeGenerationExampleGenerated)
+
+    waLBerla_generate_target_from_python(NAME FluctuatingMRTGenerated FILE codegen/FluctuatingMRT.py
+            OUT_FILES FluctuatingMRT_LatticeModel.cpp FluctuatingMRT_LatticeModel.h )
+    waLBerla_compile_test( FILES codegen/FluctuatingMRT.cpp DEPENDS FluctuatingMRTGenerated)
+
+    waLBerla_generate_target_from_python(NAME FieldLayoutAndVectorizationTestGenerated FILE codegen/FieldLayoutAndVectorizationTest.py
+            OUT_FILES FieldLayoutAndVectorizationTest_FZYX_Vec_LatticeModel.cpp FieldLayoutAndVectorizationTest_FZYX_Vec_LatticeModel.h
+            FieldLayoutAndVectorizationTest_FZYX_NoVec_LatticeModel.cpp FieldLayoutAndVectorizationTest_FZYX_NoVec_LatticeModel.h
+            FieldLayoutAndVectorizationTest_ZYXF_Vec_LatticeModel.cpp FieldLayoutAndVectorizationTest_ZYXF_Vec_LatticeModel.h
+            FieldLayoutAndVectorizationTest_ZYXF_NoVec_LatticeModel.cpp FieldLayoutAndVectorizationTest_ZYXF_NoVec_LatticeModel.h )
+    waLBerla_compile_test( FILES codegen/FieldLayoutAndVectorizationTest.cpp DEPENDS FieldLayoutAndVectorizationTestGenerated)
+
+    waLBerla_generate_target_from_python(NAME LbmPackInfoGenerationTestCodegen FILE codegen/LbmPackInfoGenerationTest.py
+            OUT_FILES AccessorBasedPackInfoEven.cpp AccessorBasedPackInfoEven.h
+            AccessorBasedPackInfoOdd.cpp AccessorBasedPackInfoOdd.h
+            FromKernelPackInfoPull.cpp FromKernelPackInfoPull.h
+            FromKernelPackInfoPush.cpp FromKernelPackInfoPush.h)
+
+    waLBerla_link_files_to_builddir( "diff_packinfos.sh" )
+    waLBerla_execute_test( NAME LbmPackInfoGenerationDiffTest COMMAND bash diff_packinfos.sh )
+
+    # set( configs D3Q19_SRT_INCOMP D3Q19_SRT_COMP D3Q27_SRT_INCOMP D3Q27_SRT_COMP D3Q27_TRT_INCOMP D3Q19_MRT_COMP )
+    set( configs D3Q19_SRT_INCOMP )
+    list( APPEND RefinementTargets "" )
+
+    foreach( config ${configs} )
+        waLBerla_generate_target_from_python(NAME CodeGenerationRefinementGenerated_${config}
+                CODEGEN_CFG ${config}
+                FILE codegen/CodeGenerationRefinement.py
+                OUT_FILES CodeGenerationRefinement_${config}_LatticeModel.cpp CodeGenerationRefinement_${config}_LatticeModel.h
+                )
+        list( APPEND RefinementTargets CodeGenerationRefinementGenerated_${config} )
+    endforeach()
+
+    waLBerla_compile_test( FILES codegen/CodeGenerationRefinement.cpp DEPENDS ${RefinementTargets} )
 
 endif()
\ No newline at end of file
diff --git a/tests/lbm/codegen/CMakeLists.txt b/tests/lbm/codegen/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..b149eda3f67a65321c9583835277423d3f0db2f0
--- /dev/null
+++ b/tests/lbm/codegen/CMakeLists.txt
@@ -0,0 +1,8 @@
+#############################################################################################################################
+#
+# Tests for lbm module
+#
+#############################################################################################################################
+
+waLBerla_link_files_to_builddir( "*.prm" )
+waLBerla_link_files_to_builddir( "ChannelFlowCodeGenParameter.py" )
\ No newline at end of file
diff --git a/tests/lbm/codegen/FluctuatingMRT.cpp b/tests/lbm/codegen/FluctuatingMRT.cpp
index 342d2d4236e01cffede917dbfa537280dcd5d64c..6707c3b383e66c43da1ef3892c590762135e342f 100644
--- a/tests/lbm/codegen/FluctuatingMRT.cpp
+++ b/tests/lbm/codegen/FluctuatingMRT.cpp
@@ -68,10 +68,10 @@ int main( int argc, char ** argv )
    const double remainingTimeLoggerFrequency = parameters.getParameter< double >( "remainingTimeLoggerFrequency", 3.0 ); // in seconds
 
    // create fields
-   BlockDataID forceFieldId = field::addToStorage<VectorField_T>( blocks, "Force", real_t( 0.0 ));
+   BlockDataID forceFieldId = field::addToStorage<VectorField_T>( blocks, "Force", real_t( 0.0 ), field::fzyx);
 
    LatticeModel_T latticeModel = LatticeModel_T( forceFieldId, omega, 0, omega, omega, seed, temperature, uint_t(0) );
-   BlockDataID pdfFieldId = lbm::addPdfFieldToStorage( blocks, "pdf field", latticeModel, initialVelocity, real_t(1) );
+   BlockDataID pdfFieldId = lbm::addPdfFieldToStorage( blocks, "pdf field", latticeModel, initialVelocity, real_t(1), uint_t(1), field::fzyx );
    BlockDataID flagFieldId = field::addFlagFieldToStorage< FlagField_T >( blocks, "flag field" );
 
    // create and initialize flag field
diff --git a/tests/lbm/codegen/FluctuatingMRT.py b/tests/lbm/codegen/FluctuatingMRT.py
index 98a8812eea1dfe47619ecb3fdf3016e6ca78a5de..6adba1df0d9e7bf7d71c8e901a406faed522006e 100644
--- a/tests/lbm/codegen/FluctuatingMRT.py
+++ b/tests/lbm/codegen/FluctuatingMRT.py
@@ -9,7 +9,7 @@ from lbmpy_walberla import generate_lattice_model
 with CodeGeneration() as ctx:
     omega_shear = sp.symbols("omega_shear")
     temperature = sp.symbols("temperature")
-    force_field, vel_field = ps.fields("force(3), velocity(3): [3D]", layout='zyxf')
+    force_field, vel_field = ps.fields("force(3), velocity(3): [3D]", layout='fzyx')
 
     def rr_getter(moment_group):
         is_shear = [is_shear_moment(m, 3) for m in moment_group]
@@ -37,15 +37,19 @@ with CodeGeneration() as ctx:
         compressible=True,
         weighted=True,
         relaxation_rate_getter=rr_getter,
-        force_model=force_model_from_string('guo', force_field.center_vector)
+        force_model=force_model_from_string('schiller', force_field.center_vector)
     )
     collision_rule = create_lb_collision_rule(
         method,
         fluctuating={
             'temperature': temperature,
             'block_offsets': 'walberla',
+            'rng_node': ps.rng.PhiloxTwoDoubles if ctx.double_accuracy else ps.rng.PhiloxFourFloats,
         },
         optimization={'cse_global': True}
     )
 
-    generate_lattice_model(ctx, 'FluctuatingMRT_LatticeModel', collision_rule)
+    params = {}
+    if ctx.optimize_for_localhost:
+        params['cpu_vectorize_info'] = {'assume_inner_stride_one': True, 'assume_aligned': True}
+    generate_lattice_model(ctx, 'FluctuatingMRT_LatticeModel', collision_rule, field_layout='fzyx', **params)
diff --git a/tests/lbm/codegen/GeneratedOutflowBC.cpp b/tests/lbm/codegen/GeneratedOutflowBC.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..66311c7ebd98866bbb553e92be406fb4b8abc3ef
--- /dev/null
+++ b/tests/lbm/codegen/GeneratedOutflowBC.cpp
@@ -0,0 +1,194 @@
+//======================================================================================================================
+//
+//  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 GeneratedOutflowBC.cpp
+//! \author Markus Holzer <markus.holzer@fau.de>
+//! \brief Shear flow with dynamic UBB on the left (W) with linear flow profile from zero to u_max (flow in x-direction)
+//!        along the y-direction. The upper wall (N) is a static UBB which applies u_max in x-direction.
+//!        On the right (E) an outflow boundary is used and the lower wall (S) is a No-Slip wall.
+//!        It is tested if the shear flow is correctly propagated through the entire domain after n timesteps.
+//
+//======================================================================================================================
+#include "blockforest/Initialization.h"
+#include "blockforest/communication/UniformBufferedScheme.h"
+
+#include "core/Environment.h"
+#include "core/timing/RemainingTimeLogger.h"
+
+#include "field/AddToStorage.h"
+#include "field/vtk/VTKWriter.h"
+
+#include "geometry/InitBoundaryHandling.h"
+#include "timeloop/SweepTimeloop.h"
+
+// Generated Files
+#include "GeneratedOutflowBC_Dynamic_UBB.h"
+#include "GeneratedOutflowBC_InfoHeader.h"
+#include "GeneratedOutflowBC_MacroSetter.h"
+#include "GeneratedOutflowBC_NoSlip.h"
+#include "GeneratedOutflowBC_Outflow.h"
+#include "GeneratedOutflowBC_PackInfo.h"
+#include "GeneratedOutflowBC_Static_UBB.h"
+#include "GeneratedOutflowBC_Sweep.h"
+
+using namespace walberla;
+
+using PackInfo_T  = lbm::GeneratedOutflowBC_PackInfo;
+using flag_t      = walberla::uint8_t;
+using FlagField_T = FlagField< flag_t >;
+
+auto pdfFieldAdder = [](IBlock* const block, StructuredBlockStorage* const storage) {
+   return new PdfField_T(storage->getNumberOfXCells(*block), storage->getNumberOfYCells(*block),
+                         storage->getNumberOfZCells(*block), uint_t(1), field::fzyx,
+                         make_shared< field::AllocateAligned< real_t, 64 > >());
+};
+
+////////////////////////////////////////////
+// Linear Velocity Profile for left wall //
+//////////////////////////////////////////
+
+class ShearProfile
+{
+ public:
+
+   ShearProfile( real_t inflow_velocity ) :
+      inflow_velocity_( inflow_velocity ) {}
+
+   Vector3< real_t > operator()( const Cell& pos, const shared_ptr< StructuredBlockForest >& SbF, IBlock& block ) const;
+
+ private:
+
+   const real_t inflow_velocity_;
+}; // class ShearProfile
+
+Vector3< real_t > ShearProfile::operator()( const Cell& pos, const shared_ptr< StructuredBlockForest >& SbF, IBlock& block ) const
+{
+   Cell globalCell;
+   CellInterval domain = SbF->getDomainCellBB();
+   real_t h_y          = domain.yMax() - domain.yMin();
+   SbF->transformBlockLocalToGlobalCell(globalCell, block, pos);
+
+   real_t u = inflow_velocity_ * (globalCell[1] / h_y);
+
+   Vector3< real_t > result(u, 0.0, 0.0);
+   return result;
+}
+
+//////////
+// MAIN //
+//////////
+
+int main(int argc, char** argv)
+{
+   walberla::Environment walberlaEnv(argc, argv);
+
+   auto blocks = blockforest::createUniformBlockGridFromConfig(walberlaEnv.config());
+
+   // read parameters
+   auto parameters = walberlaEnv.config()->getOneBlock("Parameters");
+
+   const real_t omega     = parameters.getParameter< real_t >("omega", real_c(1.4));
+   const real_t u_max     = parameters.getParameter< real_t >("u_max", real_t(0.05));
+   const uint_t timesteps = parameters.getParameter< uint_t >("timesteps", uint_c(10));
+
+   const double remainingTimeLoggerFrequency =
+      parameters.getParameter< double >("remainingTimeLoggerFrequency", 3.0); // in seconds
+
+   // create fields
+   BlockDataID pdfFieldID     = blocks->addStructuredBlockData< PdfField_T >(pdfFieldAdder, "PDFs");
+   BlockDataID velFieldID     = field::addToStorage< VelocityField_T >(blocks, "velocity", real_t(0), field::fzyx);
+   BlockDataID densityFieldID = field::addToStorage< ScalarField_T >(blocks, "density", real_t(0), field::fzyx);
+
+   BlockDataID flagFieldId = field::addFlagFieldToStorage< FlagField_T >(blocks, "flag field");
+
+   pystencils::GeneratedOutflowBC_MacroSetter setterSweep(pdfFieldID, velFieldID);
+   for (auto& block : *blocks)
+      setterSweep(&block);
+
+   // create and initialize boundary handling
+   const FlagUID fluidFlagUID("Fluid");
+
+   auto boundariesConfig = walberlaEnv.config()->getOneBlock("Boundaries");
+
+   ShearProfile velocityCallback{u_max};
+   std::function< Vector3< real_t >(const Cell&, const shared_ptr< StructuredBlockForest >&, IBlock&) >
+      velocity_initialisation = velocityCallback;
+
+   lbm::GeneratedOutflowBC_Dynamic_UBB ubb_dynamic(blocks, pdfFieldID, velocity_initialisation);
+   lbm::GeneratedOutflowBC_Static_UBB ubb_static(blocks, pdfFieldID, u_max);
+   lbm::GeneratedOutflowBC_NoSlip noSlip(blocks, pdfFieldID);
+   lbm::GeneratedOutflowBC_Outflow outflow(blocks, pdfFieldID);
+
+   geometry::initBoundaryHandling< FlagField_T >(*blocks, flagFieldId, boundariesConfig);
+   geometry::setNonBoundaryCellsToDomain< FlagField_T >(*blocks, flagFieldId, fluidFlagUID);
+
+   ubb_dynamic.fillFromFlagField< FlagField_T >(blocks, flagFieldId, FlagUID("UBB_Inflow"), fluidFlagUID);
+   ubb_static.fillFromFlagField< FlagField_T >(blocks, flagFieldId, FlagUID("UBB_Wall"), fluidFlagUID);
+   noSlip.fillFromFlagField< FlagField_T >(blocks, flagFieldId, FlagUID("NoSlip"), fluidFlagUID);
+   outflow.fillFromFlagField< FlagField_T >(blocks, flagFieldId, FlagUID("Outflow"), fluidFlagUID);
+
+   // create time loop
+   SweepTimeloop timeloop(blocks->getBlockStorage(), timesteps);
+
+   // create communication for PdfField
+   blockforest::communication::UniformBufferedScheme< Stencil_T > communication(blocks);
+   communication.addPackInfo(make_shared< PackInfo_T >(pdfFieldID));
+
+   pystencils::GeneratedOutflowBC_Sweep UpdateSweep(densityFieldID, pdfFieldID, velFieldID, omega);
+
+   // add LBM sweep and communication to time loop
+   timeloop.add() << BeforeFunction(communication, "communication") << Sweep(noSlip, "noSlip boundary");
+   timeloop.add() << Sweep(ubb_dynamic, "ubb inflow");
+   timeloop.add() << Sweep(ubb_static, "ubb wall");
+   timeloop.add() << Sweep(outflow, "outflow boundary");
+   timeloop.add() << Sweep(UpdateSweep, "LB stream & collide");
+
+   // log remaining time
+   timeloop.addFuncAfterTimeStep(timing::RemainingTimeLogger(timeloop.getNrOfTimeSteps(), remainingTimeLoggerFrequency),
+                                 "remaining time logger");
+
+   // VTK Writer
+   uint_t vtkWriteFrequency = parameters.getParameter< uint_t >("vtkWriteFrequency", 0);
+   if (vtkWriteFrequency > 0)
+   {
+      auto vtkOutput = vtk::createVTKOutput_BlockData(*blocks, "GeneratedOutflowBC_VTK", vtkWriteFrequency, 0, false,
+                                                      "vtk_out", "simulation_step", false, true, true, false, 0);
+
+      auto velWriter     = make_shared< field::VTKWriter< VelocityField_T > >(velFieldID, "velocity");
+      auto densityWriter = make_shared< field::VTKWriter< ScalarField_T > >(densityFieldID, "density");
+
+      vtkOutput->addCellDataWriter(velWriter);
+      vtkOutput->addCellDataWriter(densityWriter);
+
+      timeloop.addFuncBeforeTimeStep(vtk::writeFiles(vtkOutput), "VTK Output");
+   }
+   timeloop.run();
+
+   CellInterval domain = blocks->getDomainCellBB();
+   real_t h_y          = domain.yMax() - domain.yMin();
+   for (auto& block : *blocks)
+   {
+      auto velField = block.getData<VelocityField_T>(velFieldID);
+      WALBERLA_FOR_ALL_CELLS_XYZ
+      (
+         velField,
+   Cell globalCell;
+         blocks->transformBlockLocalToGlobalCell(globalCell, block, Cell(x, y, z));
+         WALBERLA_CHECK_FLOAT_EQUAL_EPSILON(velField->get(x, y, z, 0), u_max * (globalCell[1] / h_y), 0.01)
+      )
+   }
+
+   return EXIT_SUCCESS;
+}
diff --git a/tests/lbm/codegen/GeneratedOutflowBC.prm b/tests/lbm/codegen/GeneratedOutflowBC.prm
new file mode 100644
index 0000000000000000000000000000000000000000..b53043d9b3b81915cabf5fe17d408a613eee863d
--- /dev/null
+++ b/tests/lbm/codegen/GeneratedOutflowBC.prm
@@ -0,0 +1,24 @@
+
+Parameters 
+{
+	omega           1.8;
+	timesteps       500;
+	u_max           0.05;
+	vtkWriteFrequency 0;
+	remainingTimeLoggerFrequency 3; // in seconds
+}
+
+DomainSetup
+{
+   blocks        <  1,    1, 1 >;
+   cellsPerBlock <  30, 30, 1 >;
+   periodic      <  0,    0, 0 >;
+}
+
+Boundaries 
+{
+	Border { direction W;    walldistance -1;  flag UBB_Inflow; }
+	Border { direction E;    walldistance -1;  flag Outflow; }
+    Border { direction S;    walldistance -1;  flag NoSlip; }
+    Border { direction N;    walldistance -1;  flag UBB_Wall; }
+}
diff --git a/tests/lbm/codegen/GeneratedOutflowBC.py b/tests/lbm/codegen/GeneratedOutflowBC.py
new file mode 100644
index 0000000000000000000000000000000000000000..0aa6927d004953d829a897103d4dceea02f9f48a
--- /dev/null
+++ b/tests/lbm/codegen/GeneratedOutflowBC.py
@@ -0,0 +1,90 @@
+from pystencils.field import fields
+from lbmpy.macroscopic_value_kernels import macroscopic_values_setter
+from lbmpy.stencils import get_stencil
+from lbmpy.creationfunctions import create_lb_method, create_lb_update_rule
+from lbmpy.boundaries import NoSlip, UBB, ExtrapolationOutflow
+from lbmpy_walberla.additional_data_handler import UBBAdditionalDataHandler, OutflowAdditionalDataHandler
+from pystencils_walberla import CodeGeneration, generate_sweep
+from lbmpy_walberla import RefinementScaling, generate_boundary, generate_lb_pack_info
+
+import sympy as sp
+
+stencil = get_stencil("D2Q9")
+q = len(stencil)
+dim = len(stencil[0])
+
+pdfs, pdfs_tmp = fields(f"pdfs({q}), pdfs_tmp({q}): double[{dim}D]", layout='fzyx')
+velocity_field, density_field = fields(f"velocity({dim}), density(1) : double[{dim}D]", layout='fzyx')
+omega = sp.Symbol("omega")
+u_max = sp.Symbol("u_max")
+
+output = {
+    'density': density_field,
+    'velocity': velocity_field
+}
+
+options = {'method': 'cumulant',
+           'stencil': stencil,
+           'relaxation_rate': omega,
+           'galilean_correction': len(stencil) == 27,
+           'field_name': 'pdfs',
+           'output': output,
+           'optimization': {'symbolic_field': pdfs,
+                            'symbolic_temporary_field': pdfs_tmp,
+                            'cse_global': False,
+                            'cse_pdfs': False}}
+
+method = create_lb_method(**options)
+
+# getter & setter
+setter_assignments = macroscopic_values_setter(method, velocity=velocity_field.center_vector,
+                                               pdfs=pdfs, density=1.0)
+
+update_rule = create_lb_update_rule(lb_method=method, **options)
+
+info_header = f"""
+using namespace walberla;
+#include "stencil/D{dim}Q{q}.h"
+using Stencil_T = walberla::stencil::D{dim}Q{q};
+using PdfField_T = GhostLayerField<real_t, {q}>;
+using VelocityField_T = GhostLayerField<real_t, {dim}>;
+using ScalarField_T = GhostLayerField<real_t, 1>;
+    """
+
+stencil = method.stencil
+
+with CodeGeneration() as ctx:
+    # sweeps
+    generate_sweep(ctx, 'GeneratedOutflowBC_Sweep', update_rule, field_swaps=[(pdfs, pdfs_tmp)])
+    generate_sweep(ctx, 'GeneratedOutflowBC_MacroSetter', setter_assignments)
+
+    # boundaries
+    ubb_dynamic = UBB(lambda *args: None, dim=dim)
+    ubb_data_handler = UBBAdditionalDataHandler(stencil, ubb_dynamic)
+
+    if dim == 2:
+        ubb_static = UBB([sp.Symbol("u_max"), 0])
+    else:
+        ubb_static = UBB([sp.Symbol("u_max"), 0, 0])
+
+    outflow = ExtrapolationOutflow(stencil[4], method)
+    outflow_data_handler = OutflowAdditionalDataHandler(stencil, outflow)
+
+    # Dynamic UBB which is used to produce a specific velocity profile at the inflow.
+    # Note that the additional data handler is needed for that kind of boundary.
+    generate_boundary(ctx, 'GeneratedOutflowBC_Dynamic_UBB', ubb_dynamic, method,
+                      additional_data_handler=ubb_data_handler)
+
+    # Static UBB which is used to apply a certain velocity u_max at the upper wall in x-direction
+    generate_boundary(ctx, 'GeneratedOutflowBC_Static_UBB', ubb_static, method)
+
+    generate_boundary(ctx, 'GeneratedOutflowBC_NoSlip', NoSlip(), method)
+
+    generate_boundary(ctx, 'GeneratedOutflowBC_Outflow', outflow, method,
+                      additional_data_handler=outflow_data_handler)
+
+    # communication
+    generate_lb_pack_info(ctx, 'GeneratedOutflowBC_PackInfo', stencil, pdfs)
+
+    # Info header containing correct template definitions for stencil and field
+    ctx.write_file("GeneratedOutflowBC_InfoHeader.h", info_header)
diff --git a/tests/lbm/codegen/LbmPackInfoGenerationTest.py b/tests/lbm/codegen/LbmPackInfoGenerationTest.py
new file mode 100644
index 0000000000000000000000000000000000000000..5fab9c7abd75de925e619dd80254d66064576ec4
--- /dev/null
+++ b/tests/lbm/codegen/LbmPackInfoGenerationTest.py
@@ -0,0 +1,29 @@
+from lbmpy.creationfunctions import create_lb_collision_rule, create_lb_update_rule
+from lbmpy.advanced_streaming import Timestep
+from lbmpy.stencils import get_stencil
+from pystencils_walberla import CodeGeneration, generate_pack_info_from_kernel
+from lbmpy_walberla.packinfo import generate_lb_pack_info
+from pystencils.field import Field
+
+with CodeGeneration() as ctx:
+    streaming_pattern = 'aa'
+    target = 'cpu'
+    stencil = get_stencil('D3Q19')
+    dim = len(stencil[0])
+    values_per_cell = len(stencil)
+    collision_rule = create_lb_collision_rule(method='srt', stencil=stencil)
+    pdf_field = Field.create_generic('pdfs', dim, index_shape=(values_per_cell,), layout='fzyx')
+    optimization = {
+        'symbolic_field': pdf_field,
+        'target': target
+    }
+
+    #   Generate PackInfo specifically for streaming pattern
+    generate_lb_pack_info(ctx, 'AccessorBasedPackInfo', stencil, pdf_field,
+                          streaming_pattern=streaming_pattern, target=target, namespace='pystencils')
+
+    #   Generate reference using the alternating pull/push approach
+    update_rule_odd = create_lb_update_rule(collision_rule=collision_rule, optimization=optimization,
+                                            streaming_pattern=streaming_pattern, timestep=Timestep.ODD)
+    generate_pack_info_from_kernel(ctx, 'FromKernelPackInfoPull', update_rule_odd, kind='pull', target=target)
+    generate_pack_info_from_kernel(ctx, 'FromKernelPackInfoPush', update_rule_odd, kind='push', target=target)
diff --git a/tests/lbm/diff_packinfos.sh b/tests/lbm/diff_packinfos.sh
new file mode 100755
index 0000000000000000000000000000000000000000..bfa89c5ef63477c61fefac60b7767fe22aaf4233
--- /dev/null
+++ b/tests/lbm/diff_packinfos.sh
@@ -0,0 +1,6 @@
+#!/bin/bash
+
+REGEX='^((#include)|(void)|(uint_t))'
+cd default_codegen
+diff -u -B <(grep -vP "$REGEX" FromKernelPackInfoPull.cpp)  <(grep -vP "$REGEX" AccessorBasedPackInfoEven.cpp) || exit 1
+diff -u -B <(grep -vP "$REGEX" FromKernelPackInfoPush.cpp)  <(grep -vP "$REGEX" AccessorBasedPackInfoOdd.cpp) || exit 1
diff --git a/tests/lbm_mesapd_coupling/CMakeLists.txt b/tests/lbm_mesapd_coupling/CMakeLists.txt
index b17481236e6dd024019e83d87ec227f18ca6c7ca..90cf13471fd1488f91a1e52892e96cbceea859d5 100644
--- a/tests/lbm_mesapd_coupling/CMakeLists.txt
+++ b/tests/lbm_mesapd_coupling/CMakeLists.txt
@@ -13,15 +13,15 @@ waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_MovingMapping PROCESSES 3)
 waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_DragForceSphere FILES momentum_exchange_method/DragForceSphere.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_DragForceSphere COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_DragForceSphere> --funcTest PROCESSES 2)
 
-waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjects FILES momentum_exchange_method/ForceBetweenTwoStationaryObjects.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk )
-waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjectsSS1 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjects> PROCESSES 1)
-waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjectsSS2 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjects> --useSBB PROCESSES 1)
-waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjectsSS3 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjects> --useCompressible PROCESSES 1)
-waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjectsSS4 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjects> --systemVelocity 0.1 PROCESSES 1)
-waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjectsSW1 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjects> --useSphereWallSetup PROCESSES 1)
-waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjectsSW2 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjects> --useSphereWallSetup --useSBB PROCESSES 1)
-waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjectsSW3 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjects> --useSphereWallSetup --useCompressible PROCESSES 1)
-waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjectsSW4 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceBetweenTwoStationaryObjects> --useSphereWallSetup --systemVelocity 0.1 PROCESSES 1)
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs FILES momentum_exchange_method/ForceBetweenTwoStationaryObjects.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk )
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSS1 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs> PROCESSES 1)
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSS2 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs> --useSBB PROCESSES 1)
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSS3 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs> --useCompressible PROCESSES 1)
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSS4 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs> --systemVelocity 0.1 PROCESSES 1)
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSW1 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs> --useSphereWallSetup PROCESSES 1)
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSW2 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs> --useSphereWallSetup --useSBB PROCESSES 1)
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSW3 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs> --useSphereWallSetup --useCompressible PROCESSES 1)
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjsSW4 COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_ForceTwoStatObjs> --useSphereWallSetup --systemVelocity 0.1 PROCESSES 1)
 
 waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_SettlingSphere FILES momentum_exchange_method/SettlingSphere.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk )
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_SettlingSphere COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_MEM_SettlingSphere> --funcTest PROCESSES 4)
@@ -32,9 +32,15 @@ waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_PdfReconstruction PROCESSES
 waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_MEM_UpdateParticleMapping FILES momentum_exchange_method/UpdateParticleMapping.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field vtk)
 waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_MEM_UpdateParticleMapping PROCESSES 1)
 
-waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_UTILITY_LubricationCorrection FILES utility/LubricationCorrection.cpp DEPENDS mesa_pd lbm_mesapd_coupling )
-waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_UTILITY_LubricationCorrection PROCESSES 1 )
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_UTIL_LubricationCorrection FILES utility/LubricationCorrection.cpp DEPENDS mesa_pd lbm_mesapd_coupling )
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_UTIL_LubricationCorrection PROCESSES 1 )
 
-waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_UTILITY_InspectionProbe FILES utility/InspectionProbe.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field )
-waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_UTILITY_InspectionProbe PROCESSES 1 )
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_UTIL_InspectionProbe FILES utility/InspectionProbe.cpp DEPENDS core mesa_pd lbm lbm_mesapd_coupling domain_decomposition field )
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_UTIL_InspectionProbe PROCESSES 1 )
+
+waLBerla_compile_test( NAME LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks FILES utility/HydrodynamicForceOnMultipleBlocks.cpp DEPENDS mesa_pd lbm_mesapd_coupling )
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks_EulerAvg COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks> PROCESSES 4 )
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks_VVAvg COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks> --useVV PROCESSES 4 )
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks_EulerNoAvg COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks> --noForceAveraging PROCESSES 4 )
+waLBerla_execute_test( NAME LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks_VVNoAvg COMMAND $<TARGET_FILE:LBM_MESAPD_COUPLING_UTIL_HydForceMultBlocks> --noForceAveraging --useVV PROCESSES 4 )
 
diff --git a/tests/lbm_mesapd_coupling/momentum_exchange_method/ForceBetweenTwoStationaryObjects.cpp b/tests/lbm_mesapd_coupling/momentum_exchange_method/ForceBetweenTwoStationaryObjects.cpp
index e244543af014d124057c8b187ef59e7395cb8116..8e994002b926f320cad0d631dbcb61b2ecae61e8 100644
--- a/tests/lbm_mesapd_coupling/momentum_exchange_method/ForceBetweenTwoStationaryObjects.cpp
+++ b/tests/lbm_mesapd_coupling/momentum_exchange_method/ForceBetweenTwoStationaryObjects.cpp
@@ -300,9 +300,10 @@ int main( int argc, char **argv )
    bool useCompressible = false;
    bool useSBB = false;
    bool useSphereWallSetup = false;
-   real_t surfaceDistance = real_t(0.5);
+   real_t surfaceDistance = real_t(0.1);
    real_t systemVelocity = real_t(0);
    uint_t timesteps = uint_t(10);
+   real_t radius = real_t(5);
 
    for( int i = 1; i < argc; ++i )
    {
@@ -311,6 +312,7 @@ int main( int argc, char **argv )
       if( std::strcmp( argv[i], "--useSphereWallSetup"  ) == 0 ) { useSphereWallSetup  = true; continue;}
       if( std::strcmp( argv[i], "--surfaceDistance"    ) == 0 ) { surfaceDistance = real_c(std::atof( argv[++i])); continue;}
       if( std::strcmp( argv[i], "--systemVelocity"    ) == 0 ) { systemVelocity = real_c(std::atof( argv[++i])); continue;}
+      if( std::strcmp( argv[i], "--radius"    ) == 0 ) { radius = real_c(std::atof( argv[++i])); continue;}
       if( std::strcmp( argv[i], "--timesteps"    ) == 0 ) { timesteps = uint_c(std::atof( argv[++i])); continue;}
       WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
    }
@@ -320,8 +322,7 @@ int main( int argc, char **argv )
    // SIMULATION PROPERTIES //
    ///////////////////////////
 
-   const uint_t length = uint_t(32);
-   const real_t radius = real_t(5);
+   const uint_t length = uint_t(real_t(4) * radius);
    const Vector3<real_t> velocity(systemVelocity, real_t(0), real_t(0));
 
    ///////////////////////////
@@ -347,7 +348,7 @@ int main( int argc, char **argv )
    auto accessor = make_shared<ParticleAccessor_T >(ps, ss);
    auto sphereShape = ss->create<mesa_pd::data::Sphere>( radius );
 
-   createPlaneSetup(ps, ss, walberla::math::AABB(real_t(0), real_t(0), real_t(0), length, length, length), velocity);
+   createPlaneSetup(ps, ss, walberla::math::AABB(real_t(0), real_t(0), real_t(0), real_c(length), real_c(length), real_c(length)), velocity);
 
    walberla::id_t sphereID;
    if(useSphereWallSetup)
@@ -410,10 +411,9 @@ int main( int argc, char **argv )
       auto hydrodynamicForce = accessor->getHydrodynamicForce(idx);
 
       //WALBERLA_LOG_INFO(hydrodynamicForce);
-      for(uint_t comp = 0; comp < 3; ++comp)
-      {
-         WALBERLA_CHECK_FLOAT_EQUAL(hydrodynamicForce[comp], real_t(0), "Found non-zero force in component " << comp);
-      }
+
+      WALBERLA_CHECK_FLOAT_EQUAL(hydrodynamicForce, Vector3<real_t>(real_t(0)), "Found non-zero force");
+
 
       lbm_mesapd_coupling::ResetHydrodynamicForceTorqueKernel resetHydrodynamicForceTorque;
       ps->forEachParticle(false, mesa_pd::kernel::SelectAll(), *accessor, resetHydrodynamicForceTorque, *accessor );
diff --git a/tests/lbm_mesapd_coupling/utility/HydrodynamicForceOnMultipleBlocks.cpp b/tests/lbm_mesapd_coupling/utility/HydrodynamicForceOnMultipleBlocks.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7f0622dbb2650826d0d6b713c39bc2954a14e15a
--- /dev/null
+++ b/tests/lbm_mesapd_coupling/utility/HydrodynamicForceOnMultipleBlocks.cpp
@@ -0,0 +1,327 @@
+//======================================================================================================================
+//
+//  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   HydrodynamicForceOnMultipleBlocks.cpp
+//! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//
+//======================================================================================================================
+
+
+#include "blockforest/Initialization.h"
+
+#include "core/Environment.h"
+#include "core/debug/Debug.h"
+#include "core/debug/TestSubsystem.h"
+#include "core/logging/Logging.h"
+
+#include "lbm_mesapd_coupling/utility/AddHydrodynamicInteractionKernel.h"
+#include "lbm_mesapd_coupling/utility/AverageHydrodynamicForceTorqueKernel.h"
+#include "lbm_mesapd_coupling/utility/InitializeHydrodynamicForceTorqueForAveragingKernel.h"
+#include "lbm_mesapd_coupling/utility/ResetHydrodynamicForceTorqueKernel.h"
+#include "lbm_mesapd_coupling/utility/ParticleFunctions.h"
+
+#include "mesa_pd/common/ParticleFunctions.h"
+#include "mesa_pd/data/ParticleAccessorWithShape.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/ShapeStorage.h"
+#include "mesa_pd/data/shape/Sphere.h"
+#include "mesa_pd/domain/BlockForestDomain.h"
+#include "mesa_pd/kernel/DoubleCast.h"
+#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
+#include "mesa_pd/kernel/ParticleSelector.h"
+#include "mesa_pd/mpi/SyncNextNeighbors.h"
+#include "mesa_pd/mpi/ReduceProperty.h"
+#include "mesa_pd/mpi/notifications/ForceTorqueNotification.h"
+#include "mesa_pd/mpi/notifications/HydrodynamicForceTorqueNotification.h"
+
+
+namespace hydrodynamic_force_blocks
+{
+
+using namespace walberla;
+
+
+template< typename ParticleAccessor_T>
+class SpherePropertyEvaluator
+{
+public:
+   SpherePropertyEvaluator( const shared_ptr< ParticleAccessor_T > & ac, walberla::id_t sphereUid) :
+         ac_( ac ), sphereUid_( sphereUid )
+   {  }
+
+   void operator()()
+   {
+      Vector3<real_t> pos(real_t(0));
+      Vector3<real_t> transVel(real_t(0));
+      Vector3<real_t> angularVel(real_t(0));
+      Vector3<real_t> force(real_t(0));
+      Vector3<real_t> torque(real_t(0));
+
+      size_t idx = ac_->uidToIdx(sphereUid_);
+      if( idx != ac_->getInvalidIdx())
+      {
+         if(!mesa_pd::data::particle_flags::isSet( ac_->getFlags(idx), mesa_pd::data::particle_flags::GHOST))
+         {
+            pos = ac_->getPosition(idx);
+            transVel = ac_->getLinearVelocity(idx);
+            angularVel = ac_->getAngularVelocity(idx);
+            force = ac_->getForce(idx);
+            torque = ac_->getTorque(idx);
+         }
+      }
+
+      WALBERLA_MPI_SECTION()
+      {
+         mpi::allReduceInplace( pos[0], mpi::SUM );
+         mpi::allReduceInplace( pos[1], mpi::SUM );
+         mpi::allReduceInplace( pos[2], mpi::SUM );
+
+         mpi::allReduceInplace( transVel[0], mpi::SUM );
+         mpi::allReduceInplace( transVel[1], mpi::SUM );
+         mpi::allReduceInplace( transVel[2], mpi::SUM );
+
+         mpi::allReduceInplace( angularVel[0], mpi::SUM );
+         mpi::allReduceInplace( angularVel[1], mpi::SUM );
+         mpi::allReduceInplace( angularVel[2], mpi::SUM );
+
+         mpi::allReduceInplace( force[0], mpi::SUM );
+         mpi::allReduceInplace( force[1], mpi::SUM );
+         mpi::allReduceInplace( force[2], mpi::SUM );
+
+         mpi::allReduceInplace( torque[0], mpi::SUM );
+         mpi::allReduceInplace( torque[1], mpi::SUM );
+         mpi::allReduceInplace( torque[2], mpi::SUM );
+      }
+
+      position_ = pos;
+      linearVel_ = transVel;
+      angularVel_ = angularVel;
+      force_ = force;
+      torque_ = torque;
+   }
+
+   Vector3<real_t> getPosition() const { return position_; }
+   Vector3<real_t> getLinearVel() const { return linearVel_; }
+   Vector3<real_t> getAngularVel() const { return angularVel_; }
+   Vector3<real_t> getForce() const { return force_; }
+   Vector3<real_t> getTorque() const { return torque_; }
+
+
+private:
+
+   shared_ptr< ParticleAccessor_T > ac_;
+   const walberla::id_t sphereUid_;
+
+   Vector3<real_t> position_;
+   Vector3<real_t> linearVel_;
+   Vector3<real_t> angularVel_;
+   Vector3<real_t> force_;
+   Vector3<real_t> torque_;
+};
+
+template< typename ParticleAccessor_T>
+void applyHydrodynamicForceTorqueOnSphere(ParticleAccessor_T & accessor, walberla::id_t sphereUid,
+                                          Vector3<real_t> hydForce, Vector3<real_t> hydTorque)
+{
+
+   uint_t numberOfProcessesWithKnowledgeOfThisSphere = uint_t(0);
+   size_t idx = accessor.uidToIdx(sphereUid);
+   if( idx != accessor.getInvalidIdx())
+   {
+      ++numberOfProcessesWithKnowledgeOfThisSphere;
+   }
+
+   WALBERLA_MPI_SECTION() {
+      mpi::allReduceInplace(numberOfProcessesWithKnowledgeOfThisSphere, mpi::SUM);
+   }
+
+   if( idx != accessor.getInvalidIdx())
+   {
+      accessor.setHydrodynamicForce(idx, hydForce / real_t(numberOfProcessesWithKnowledgeOfThisSphere) );
+      accessor.setHydrodynamicTorque(idx, hydTorque / real_t(numberOfProcessesWithKnowledgeOfThisSphere) );
+   }
+}
+
+
+/*
+ * Two spheres travelling through several blocks. moved by constant (distributed) hydrodynamic force.
+ * This checks setting hydrodynamic forces onto particles and force averaging in parallel setup.
+ * Velocity of both spheres have to be equal throughout the simulation.
+ *
+ */
+int main( int argc, char ** argv )
+{
+   debug::enterTestMode();
+
+   mpi::Environment env( argc, argv );
+
+   const Vector3<uint_t> domainSize( 4 * 32, 32, 32 );
+   const Vector3<uint_t> numberOfBlocksPerDirection(4, 1, 1 );
+   Vector3<uint_t> cellsPerBlockPerDirection( domainSize[0] / numberOfBlocksPerDirection[0],
+                                              domainSize[1] / numberOfBlocksPerDirection[1],
+                                              domainSize[2] / numberOfBlocksPerDirection[2] );
+
+   real_t xPos1 = real_t(0);
+   real_t xPos2 = real_t(45.36281); // random
+
+   real_t radius = real_t(2);
+
+   Vector3<real_t> hydForce( real_t(0.75), 0, 0);
+   Vector3<real_t> hydTorque( real_t(0.75), 0, 0);
+
+   real_t dt = real_t(1);
+   real_t dx = real_t(1);
+   uint_t timesteps = 400;
+
+   bool averageForceTorqueOverTwoTimeSteps = true;
+   bool useVelocityVerlet = false;
+
+
+   for( int i = 1; i < argc; ++i )
+   {
+      if( std::strcmp( argv[i], "--timesteps" )           == 0 ) { timesteps = uint_c( std::atof( argv[++i] ) ); continue; }
+      if( std::strcmp( argv[i], "--noForceAveraging" )    == 0 ) { averageForceTorqueOverTwoTimeSteps = false; continue; }
+      if( std::strcmp( argv[i], "--useVV" )               == 0 ) { useVelocityVerlet = true; continue; }
+      WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
+   }
+
+   // setup as in a coupled simulation even though not needed here for mesa_pd only setup
+   auto blocks = blockforest::createUniformBlockGrid( numberOfBlocksPerDirection[0], numberOfBlocksPerDirection[1], numberOfBlocksPerDirection[2],
+                                                      cellsPerBlockPerDirection[0], cellsPerBlockPerDirection[1], cellsPerBlockPerDirection[2], dx,
+                                                      0, false, false,
+                                                      true, false, false, //periodicity
+                                                      false );
+
+
+   auto rpdDomain = walberla::make_shared<mesa_pd::domain::BlockForestDomain>(blocks->getBlockForestPointer());
+
+   auto ps = walberla::make_shared<mesa_pd::data::ParticleStorage>(2);
+   auto ss = walberla::make_shared<mesa_pd::data::ShapeStorage>();
+   using ParticleAccessor_T = mesa_pd::data::ParticleAccessorWithShape;
+   auto accessor = walberla::make_shared<ParticleAccessor_T >(ps, ss);
+
+   auto sphereShape = ss->create<mesa_pd::data::Sphere>( radius );
+   ss->shapes[sphereShape]->updateMassAndInertia(real_t(1));
+
+   Vector3<real_t> initialPosition1( xPos1, real_t(0.5) * real_c(domainSize[1]), real_t(0.5) * real_c(domainSize[2]));
+   Vector3<real_t> initialPosition2( xPos2, real_t(0.5) * real_c(domainSize[1]), real_t(0.5) * real_c(domainSize[2]));
+
+   walberla::id_t sphereUid1 = 0;
+   if (rpdDomain->isContainedInProcessSubdomain( uint_c(mpi::MPIManager::instance()->rank()), initialPosition1 ))
+   {
+      mesa_pd::data::Particle&& p = *ps->create();
+      p.setPosition(initialPosition1);
+      p.setInteractionRadius(radius);
+      p.setOwner(mpi::MPIManager::instance()->rank());
+      p.setShapeID(sphereShape);
+      sphereUid1 = p.getUid();
+   }
+   mpi::allReduceInplace(sphereUid1, mpi::SUM);
+
+   walberla::id_t sphereUid2 = 0;
+   if (rpdDomain->isContainedInProcessSubdomain( uint_c(mpi::MPIManager::instance()->rank()), initialPosition2 ))
+   {
+      mesa_pd::data::Particle&& p = *ps->create();
+      p.setPosition(initialPosition2);
+      p.setInteractionRadius(radius);
+      p.setOwner(mpi::MPIManager::instance()->rank());
+      p.setShapeID(sphereShape);
+      sphereUid2 = p.getUid();
+   }
+   mpi::allReduceInplace(sphereUid2, mpi::SUM);
+
+
+   // mesa_pd functionality
+   mesa_pd::kernel::VelocityVerletPreForceUpdate  vvIntegratorPreForce( dt );
+   mesa_pd::kernel::VelocityVerletPostForceUpdate vvIntegratorPostForce( dt );
+   mesa_pd::kernel::ExplicitEuler explEulerIntegrator( dt );
+   mesa_pd::mpi::ReduceProperty reduceProperty;
+
+   std::function<void(void)> syncCall = [ps,rpdDomain](){
+      const real_t overlap = real_t( 1.5 );
+      mesa_pd::mpi::SyncNextNeighbors syncNextNeighborFunc;
+      syncNextNeighborFunc(*ps, *rpdDomain, overlap);
+   };
+
+   syncCall();
+
+   // coupling functionality
+   lbm_mesapd_coupling::AddHydrodynamicInteractionKernel addHydrodynamicInteraction;
+   lbm_mesapd_coupling::ResetHydrodynamicForceTorqueKernel resetHydrodynamicForceTorque;
+   lbm_mesapd_coupling::AverageHydrodynamicForceTorqueKernel averageHydrodynamicForceTorque;
+
+   // evaluation functionality
+   SpherePropertyEvaluator<ParticleAccessor_T> sphere1Eval(accessor, sphereUid1);
+   SpherePropertyEvaluator<ParticleAccessor_T> sphere2Eval(accessor, sphereUid2);
+
+   bool useOpenMP = false;
+   for(uint_t t = 0; t < timesteps; ++t) {
+
+      // set hydrodynamic force/torque distributed
+      applyHydrodynamicForceTorqueOnSphere(*accessor, sphereUid1, hydForce, hydTorque);
+      applyHydrodynamicForceTorqueOnSphere(*accessor, sphereUid2, hydForce, hydTorque);
+
+      reduceProperty.operator()<mesa_pd::HydrodynamicForceTorqueNotification>(*ps);
+
+      if (averageForceTorqueOverTwoTimeSteps) {
+         if (t == 0) {
+            lbm_mesapd_coupling::InitializeHydrodynamicForceTorqueForAveragingKernel initializeHydrodynamicForceTorqueForAveragingKernel;
+            ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, initializeHydrodynamicForceTorqueForAveragingKernel, *accessor);
+         }
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, averageHydrodynamicForceTorque, *accessor);
+      }
+
+      if (useVelocityVerlet) {
+         ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPreForce, *accessor);
+         syncCall();
+      }
+
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, addHydrodynamicInteraction, *accessor);
+
+      reduceProperty.operator()<mesa_pd::ForceTorqueNotification>(*ps);
+
+      // check sphere properties
+      sphere1Eval();
+      sphere2Eval();
+
+      WALBERLA_CHECK_FLOAT_EQUAL(sphere1Eval.getLinearVel()[0], sphere2Eval.getLinearVel()[0], "time step " << t << " mismatch in linear vel[0]");
+      WALBERLA_CHECK_FLOAT_EQUAL(sphere1Eval.getLinearVel()[1], sphere2Eval.getLinearVel()[1], "time step " << t << " mismatch in linear vel[1]");
+      WALBERLA_CHECK_FLOAT_EQUAL(sphere1Eval.getLinearVel()[2], sphere2Eval.getLinearVel()[2], "time step " << t << " mismatch in linear vel[2]");
+
+      WALBERLA_CHECK_FLOAT_EQUAL(sphere1Eval.getAngularVel()[0], sphere2Eval.getAngularVel()[0], "time step " << t << " mismatch in angular vel[0]");
+      WALBERLA_CHECK_FLOAT_EQUAL(sphere1Eval.getAngularVel()[1], sphere2Eval.getAngularVel()[1], "time step " << t << " mismatch in angular vel[1]");
+      WALBERLA_CHECK_FLOAT_EQUAL(sphere1Eval.getAngularVel()[2], sphere2Eval.getAngularVel()[2], "time step " << t << " mismatch in angular vel[2]");
+
+      // particle integration
+      if( useVelocityVerlet ) ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, vvIntegratorPostForce, *accessor);
+      else ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectLocal(), *accessor, explEulerIntegrator, *accessor);
+
+      syncCall();
+
+      ps->forEachParticle(useOpenMP, mesa_pd::kernel::SelectAll(), *accessor, resetHydrodynamicForceTorque, *accessor );
+
+      //WALBERLA_LOG_INFO_ON_ROOT(t << " " << sphere1Eval.getPosition()[0] << " " << sphere1Eval.getLinearVel()[0] << " " << sphere1Eval.getForce()[0] << " " << sphere1Eval.getAngularVel()[0] << " " << sphere1Eval.getTorque()[0] );
+   }
+
+   return EXIT_SUCCESS;
+}
+
+} //namespace hydrodynamic_force_blocks
+
+int main( int argc, char ** argv )
+{
+   return hydrodynamic_force_blocks::main(argc, argv);
+}
diff --git a/tests/mesa_pd/CMakeLists.txt b/tests/mesa_pd/CMakeLists.txt
index 3742a2ef2dcabdd68d50fe0c436fd0b21d360435..48c07d87c940f09f35235a6f1d082bb9e2fc1fea 100644
--- a/tests/mesa_pd/CMakeLists.txt
+++ b/tests/mesa_pd/CMakeLists.txt
@@ -37,6 +37,9 @@ waLBerla_execute_test( NAME   MESA_PD_COMMON_IntersectionRatio )
 waLBerla_compile_test( NAME   MESA_PD_ContactDetection FILES ContactDetection.cpp DEPENDS blockforest core pe)
 waLBerla_execute_test( NAME   MESA_PD_ContactDetection PROCESSES 8 )
 
+waLBerla_compile_test( NAME   MESA_PD_Data_ContactHistory FILES data/ContactHistory.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_Data_ContactHistory )
+
 waLBerla_compile_test( NAME   MESA_PD_Data_Flags FILES data/Flags.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_Data_Flags )
 
@@ -66,6 +69,9 @@ waLBerla_execute_test( NAME   MESA_PD_Domain_DistanceCalculation )
 waLBerla_compile_test( NAME   MESA_PD_Domain_DynamicRefinement FILES domain/DynamicRefinement.cpp DEPENDS blockforest core pe )
 waLBerla_execute_test( NAME   MESA_PD_Domain_DynamicRefinement PROCESSES 8)
 
+waLBerla_compile_test( NAME   MESA_PD_Domain_InfiniteDomain FILES domain/InfiniteDomain.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_Domain_InfiniteDomain )
+
 waLBerla_compile_test( NAME   MESA_PD_Domain_SerializeDeserialize FILES domain/SerializeDeserialize.cpp DEPENDS blockforest core pe)
 waLBerla_execute_test( NAME   MESA_PD_Domain_SerializeDeserialize PROCESSES 8 )
 
@@ -78,6 +84,27 @@ waLBerla_execute_test( NAME   MESA_PD_DropTestGeneral )
 waLBerla_compile_test( NAME   MESA_PD_Kernel_ClearNextNeighborSync FILES kernel/ClearNextNeighborSync.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_Kernel_ClearNextNeighborSync PROCESSES 2 )
 
+waLBerla_compile_test( NAME   MESA_PD_Kernel_CNT_AnisotropicVDWContact FILES kernel/cnt/AnisotropicVDWContact.test.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_Kernel_CNT_AnisotropicVDWContact )
+
+waLBerla_compile_test( NAME   MESA_PD_Kernel_CNT_IntegratedVDWContact FILES kernel/cnt/IntegratedVDWContact.test.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_Kernel_CNT_IntegratedVDWContact )
+
+waLBerla_compile_test( NAME   MESA_PD_Kernel_CNT_IsotropicVDWContact FILES kernel/cnt/IsotropicVDWContact.test.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_Kernel_CNT_IsotropicVDWContact )
+
+waLBerla_compile_test( NAME   MESA_PD_Kernel_CNT_VBondContact FILES kernel/cnt/VBondContact.test.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_Kernel_CNT_VBondContact )
+
+waLBerla_compile_test( NAME   MESA_PD_Kernel_CNT_VBondContactIntegration FILES kernel/cnt/VBondContactIntegration.test.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_Kernel_CNT_VBondContactIntegration )
+
+waLBerla_compile_test( NAME   MESA_PD_Kernel_CNT_ViscousDamping FILES kernel/cnt/ViscousDamping.test.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_Kernel_CNT_ViscousDamping )
+
+waLBerla_compile_test( NAME   MESA_PD_Kernel_CNT_WallContact FILES kernel/cnt/WallContact.test.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_Kernel_CNT_WallContact )
+
 waLBerla_compile_test( NAME   MESA_PD_Kernel_CoefficientOfRestitutionSD FILES kernel/CoefficientOfRestitutionSD.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_Kernel_CoefficientOfRestitutionSDEuler COMMAND $<TARGET_FILE:MESA_PD_Kernel_CoefficientOfRestitutionSD> )
 waLBerla_execute_test( NAME   MESA_PD_Kernel_CoefficientOfRestitutionSDVelocityVerlet COMMAND $<TARGET_FILE:MESA_PD_Kernel_CoefficientOfRestitutionSD> --useVV )
@@ -128,6 +155,12 @@ waLBerla_execute_test( NAME   MESA_PD_Kernel_LinearSpringDashpot )
 waLBerla_compile_test( NAME   MESA_PD_Kernel_LinkedCellsVsBruteForce FILES kernel/LinkedCellsVsBruteForce.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_Kernel_LinkedCellsVsBruteForce PROCESSES 27 )
 
+waLBerla_compile_test( NAME   MESA_PD_Kernel_PFCDamping FILES kernel/PFCDamping.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_Kernel_PFCDamping )
+
+waLBerla_compile_test( NAME   MESA_PD_Kernel_SemiImplicitEuler FILES kernel/SemiImplicitEuler.cpp DEPENDS core )
+waLBerla_execute_test( NAME   MESA_PD_Kernel_SemiImplicitEuler )
+
 waLBerla_compile_test( NAME   MESA_PD_Kernel_SingleCast FILES kernel/SingleCast.cpp DEPENDS core )
 waLBerla_execute_test( NAME   MESA_PD_Kernel_SingleCast )
 
@@ -197,4 +230,4 @@ if (WALBERLA_MESAPD_CONVEX_POLYHEDRON_AVAILABLE)
 
     waLBerla_compile_test( NAME   MESA_PD_COLLISIONDETECTION_ConvexPolyhedron_GJK_EPA FILES collision_detection/ConvexPolyhedron_GJK_EPA.cpp DEPENDS core mesa_pd mesh_common )
     waLBerla_execute_test( NAME   MESA_PD_COLLISIONDETECTION_ConvexPolyhedron_GJK_EPA )
-endif()
\ No newline at end of file
+endif()
diff --git a/tests/mesa_pd/ContactDetection.cpp b/tests/mesa_pd/ContactDetection.cpp
index 3b63a9a7e07ad57d9faa97096519b4c7915735d9..41e62d75f7a486ed08a3099c24c35576181ec09c 100644
--- a/tests/mesa_pd/ContactDetection.cpp
+++ b/tests/mesa_pd/ContactDetection.cpp
@@ -13,7 +13,7 @@
 //  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   ContactDetection.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/DropTestAnalytic.cpp b/tests/mesa_pd/DropTestAnalytic.cpp
index 7c3c89ed0b3caec83e9bb699a1062a2e26ef44d9..827f0951f2b51294b3c3f486fd54a777164f885e 100644
--- a/tests/mesa_pd/DropTestAnalytic.cpp
+++ b/tests/mesa_pd/DropTestAnalytic.cpp
@@ -13,7 +13,7 @@
 //  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   DropTestAnalytic.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -25,7 +25,7 @@
 #include <mesa_pd/data/ShapeStorage.h>
 
 #include <mesa_pd/kernel/DoubleCast.h>
-#include <mesa_pd/kernel/ExplicitEuler.h>
+#include <mesa_pd/kernel/SemiImplicitEuler.h>
 #include <mesa_pd/kernel/ParticleSelector.h>
 #include <mesa_pd/kernel/SpringDashpot.h>
 
@@ -102,7 +102,7 @@ int main( int argc, char ** argv )
    real_t dt = real_t(0.00001);
 
    // Init kernels
-   kernel::ExplicitEuler                 explicitEuler( dt );
+   kernel::SemiImplicitEuler             implEuler( dt );
    kernel::SpringDashpot                 dem(1);
    auto meff = real_t(1.0) / ss->shapes[sp->getShapeID()]->getInvMass();
    dem.setParametersFromCOR(0,0,real_t(0.9), dt * real_t(20), meff);
@@ -152,7 +152,7 @@ int main( int argc, char ** argv )
       ps->forEachParticle(false,
                           kernel::SelectLocal(),
                           accessor,
-                          explicitEuler,
+                          implEuler,
                           accessor);
 
 //      if(i%1 == 0)
diff --git a/tests/mesa_pd/DropTestGeneral.cpp b/tests/mesa_pd/DropTestGeneral.cpp
index 3d668694c7f3429e12354e561a24389ca4e87443..52495a885a843f77480fee07e918a746603fdb17 100644
--- a/tests/mesa_pd/DropTestGeneral.cpp
+++ b/tests/mesa_pd/DropTestGeneral.cpp
@@ -13,7 +13,7 @@
 //  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   DropTestGeneral.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -25,16 +25,14 @@
 #include <mesa_pd/data/ShapeStorage.h>
 
 #include <mesa_pd/kernel/DoubleCast.h>
-#include <mesa_pd/kernel/ExplicitEuler.h>
+#include <mesa_pd/kernel/SemiImplicitEuler.h>
 #include <mesa_pd/kernel/ParticleSelector.h>
 #include <mesa_pd/kernel/SpringDashpot.h>
 
 #include <core/Abort.h>
 #include <core/Environment.h>
 #include <core/logging/Logging.h>
-#include <core/waLBerlaBuildInfo.h>
 
-#include <functional>
 #include <memory>
 #include <string>
 #include <type_traits>
@@ -122,7 +120,7 @@ int main( int argc, char ** argv )
    real_t dt = real_t(0.00001);
 
    // Init kernels
-   kernel::ExplicitEuler                 explicitEuler( dt );
+   kernel::SemiImplicitEuler             implEuler( dt );
    kernel::SpringDashpot                 dem(1);
    auto meff = real_t(1.0) / ss->shapes[sp->getShapeID()]->getInvMass();
    dem.setParametersFromCOR(0,0,real_t(0.9), dt * real_t(20), meff);
@@ -172,7 +170,7 @@ int main( int argc, char ** argv )
       ps->forEachParticle(false,
                           kernel::SelectLocal(),
                           accessor,
-                          explicitEuler,
+                          implEuler,
                           accessor);
 
 //      if(i%1 == 0)
diff --git a/tests/mesa_pd/Sorting.cpp b/tests/mesa_pd/Sorting.cpp
index d4408cd71f6f8ad7319c0c82a808d100729d7db4..178ffc2b25dae17d3c0ce8c529b7ac59245ef3d2 100644
--- a/tests/mesa_pd/Sorting.cpp
+++ b/tests/mesa_pd/Sorting.cpp
@@ -13,7 +13,7 @@
 //  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   Sorting.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/collision_detection/AnalyticCollisionFunctions.cpp b/tests/mesa_pd/collision_detection/AnalyticCollisionFunctions.cpp
index 1dc914aaea5572e16e06f75cbfeea0430fd02a51..9f171a32520275043cb35a810cdc0196304a0565 100644
--- a/tests/mesa_pd/collision_detection/AnalyticCollisionFunctions.cpp
+++ b/tests/mesa_pd/collision_detection/AnalyticCollisionFunctions.cpp
@@ -13,7 +13,7 @@
 //  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   AnalyticCollisionFunctions.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/collision_detection/AnalyticContactDetection.cpp b/tests/mesa_pd/collision_detection/AnalyticContactDetection.cpp
index c191edbd6635c004829de9c1ed5ac3aa93489a09..911e7344df4608c7de743e27f05bc0427b7dbf35 100644
--- a/tests/mesa_pd/collision_detection/AnalyticContactDetection.cpp
+++ b/tests/mesa_pd/collision_detection/AnalyticContactDetection.cpp
@@ -13,7 +13,7 @@
 //  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   AnalyticContactDetection.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/collision_detection/BoxSupport.cpp b/tests/mesa_pd/collision_detection/BoxSupport.cpp
index 08ae7bcc0e5110a463735eee663a5d9347652f77..91b20ac41e7632004d9fce8cef0ba2c98a9deedc 100644
--- a/tests/mesa_pd/collision_detection/BoxSupport.cpp
+++ b/tests/mesa_pd/collision_detection/BoxSupport.cpp
@@ -13,7 +13,7 @@
 //  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   BoxSupport.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/collision_detection/ConvexPolyhedron_GJK_EPA.cpp b/tests/mesa_pd/collision_detection/ConvexPolyhedron_GJK_EPA.cpp
index 037073c9cdbba45429d240388e8838d3ec7775bf..2436e89684373784ef13cc1ce43e751ee4e74c96 100644
--- a/tests/mesa_pd/collision_detection/ConvexPolyhedron_GJK_EPA.cpp
+++ b/tests/mesa_pd/collision_detection/ConvexPolyhedron_GJK_EPA.cpp
@@ -13,7 +13,7 @@
 //  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 CollisionTest.cpp
+//! \file
 //! \author Lukas Werner
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/collision_detection/EPA.cpp b/tests/mesa_pd/collision_detection/EPA.cpp
index fa171a209012b7c496d58b4217ae84477126a805..512100a26f73380f9141178fd7130a78fceb6b90 100644
--- a/tests/mesa_pd/collision_detection/EPA.cpp
+++ b/tests/mesa_pd/collision_detection/EPA.cpp
@@ -13,7 +13,7 @@
 //  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   EPA.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/collision_detection/EllipsoidSupport.cpp b/tests/mesa_pd/collision_detection/EllipsoidSupport.cpp
index 820d34005c376b98c6e9b3c17eb8b1980223c12c..03d2d86b8789e8c4a2f64bd7812394171041e79c 100644
--- a/tests/mesa_pd/collision_detection/EllipsoidSupport.cpp
+++ b/tests/mesa_pd/collision_detection/EllipsoidSupport.cpp
@@ -13,7 +13,7 @@
 //  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   EllipsoidSupport.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/collision_detection/GJK.cpp b/tests/mesa_pd/collision_detection/GJK.cpp
index 43f044c5de663f11d9221662409a503c059f7c98..35f4a6672b8ab3f2dc991e36888a0a21d25369f0 100644
--- a/tests/mesa_pd/collision_detection/GJK.cpp
+++ b/tests/mesa_pd/collision_detection/GJK.cpp
@@ -13,7 +13,7 @@
 //  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   GJK.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/collision_detection/GJK_EPA.cpp b/tests/mesa_pd/collision_detection/GJK_EPA.cpp
index 05000b339e8bd0a19116df6ad8cc9751acee8074..998276419fa39744a1d9251dddc6466375a569e5 100644
--- a/tests/mesa_pd/collision_detection/GJK_EPA.cpp
+++ b/tests/mesa_pd/collision_detection/GJK_EPA.cpp
@@ -13,7 +13,7 @@
 //  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 GJK_EPA.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/collision_detection/GeneralContactDetection.cpp b/tests/mesa_pd/collision_detection/GeneralContactDetection.cpp
index 9f3be3feb720aced306b7042bbd9ca47f193afcb..1dcb49882b02fd7cae711158f4bbc1d4817b0e60 100644
--- a/tests/mesa_pd/collision_detection/GeneralContactDetection.cpp
+++ b/tests/mesa_pd/collision_detection/GeneralContactDetection.cpp
@@ -13,7 +13,7 @@
 //  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   GeneralContactDetection.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/collision_detection/SphereSupport.cpp b/tests/mesa_pd/collision_detection/SphereSupport.cpp
index 2c94e0fc5878ffad2525d4d70787bbdfaa31484f..359be9b70f0e8d9783461e226d9934b4f5da77fc 100644
--- a/tests/mesa_pd/collision_detection/SphereSupport.cpp
+++ b/tests/mesa_pd/collision_detection/SphereSupport.cpp
@@ -13,7 +13,7 @@
 //  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   SphereSupport.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/common/IntersectionRatio.cpp b/tests/mesa_pd/common/IntersectionRatio.cpp
index d1184f8376b3277355c3c102ce6adb53685dacff..71db12ae58ce1e05ea246c0cc9a3e670d8752f99 100644
--- a/tests/mesa_pd/common/IntersectionRatio.cpp
+++ b/tests/mesa_pd/common/IntersectionRatio.cpp
@@ -13,7 +13,7 @@
 //  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 IntersectionRatio.cpp
+//! \file
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/data/ContactHistory.cpp b/tests/mesa_pd/data/ContactHistory.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fbc237eda29482c5072ff03eaafdbf4fee5f8078
--- /dev/null
+++ b/tests/mesa_pd/data/ContactHistory.cpp
@@ -0,0 +1,72 @@
+//======================================================================================================================
+//
+//  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   ContactHistory.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/data/ContactHistory.h>
+
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+
+#include <algorithm>
+#include <iostream>
+
+namespace walberla {
+
+using namespace walberla::mesa_pd;
+
+void basic_test()
+{
+   //init data structures
+   data::ContactHistory cs;
+
+   cs.setImpactVelocityMagnitude(1.23456_r);
+   cs.setIsSticking(true);
+   cs.setTangentialSpringDisplacement(Vec3(1.23_r,2.345_r,3.56_r));
+
+   mpi::SendBuffer sb;
+   sb << cs;
+   mpi::RecvBuffer rb(sb);
+
+   data::ContactHistory cs_recv;
+   rb >> cs_recv;
+
+   WALBERLA_CHECK_IDENTICAL(cs.getImpactVelocityMagnitude(), cs_recv.getImpactVelocityMagnitude());
+   WALBERLA_CHECK_IDENTICAL(cs.getIsSticking(), cs_recv.getIsSticking());
+   WALBERLA_CHECK_IDENTICAL(cs.getTangentialSpringDisplacement(), cs_recv.getTangentialSpringDisplacement());
+
+   WALBERLA_LOG_DEVEL( cs );
+}
+
+int main( int argc, char ** argv )
+{
+   Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   mpi::MPIManager::instance()->useWorldComm();
+
+   basic_test();
+
+   return EXIT_SUCCESS;
+}
+
+} //namespace walberla
+
+int main( int argc, char ** argv )
+{
+   return walberla::main(argc, argv);
+}
diff --git a/tests/mesa_pd/data/ConvexPolyhedron.cpp b/tests/mesa_pd/data/ConvexPolyhedron.cpp
index ff9f2ab7690a45d5a8ceadc9ab853925cd3c6ea9..6636e9214ff77668719b704af2d38e4b1574b264 100644
--- a/tests/mesa_pd/data/ConvexPolyhedron.cpp
+++ b/tests/mesa_pd/data/ConvexPolyhedron.cpp
@@ -13,7 +13,7 @@
 //  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 MeshMesapdConvexPolyhedronTest.cpp
+//! \file
 //! \author Lukas Werner
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/data/Flags.cpp b/tests/mesa_pd/data/Flags.cpp
index e54cdcd8538344c86f6c364ce0552654a6b1d7e4..4a9ccd3a9d85e408cdf1a2f9b15f700225b89582 100644
--- a/tests/mesa_pd/data/Flags.cpp
+++ b/tests/mesa_pd/data/Flags.cpp
@@ -13,7 +13,7 @@
 //  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   Flags.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/data/LinkedCells.cpp b/tests/mesa_pd/data/LinkedCells.cpp
index 0653c116d62ada8102e033d472aa773b8aa2b8d5..b56ac162932733c8787e2133477caaea08849402 100644
--- a/tests/mesa_pd/data/LinkedCells.cpp
+++ b/tests/mesa_pd/data/LinkedCells.cpp
@@ -13,7 +13,7 @@
 //  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   LinkedCells.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/data/ParticleStorage.cpp b/tests/mesa_pd/data/ParticleStorage.cpp
index ce3579292574f32c948638013881c4563a8aaa15..df2b938e0a76532137c3f16e6bfbeb057a5a4c80 100644
--- a/tests/mesa_pd/data/ParticleStorage.cpp
+++ b/tests/mesa_pd/data/ParticleStorage.cpp
@@ -13,7 +13,7 @@
 //  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   ParticleStorage.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/data/SparseLinkedCells.cpp b/tests/mesa_pd/data/SparseLinkedCells.cpp
index 090e739e2ce10b74c929dddf53f85a4b7115e5a7..cd4ebe5198199a81fc7fdf8bff21444dd37c35d5 100644
--- a/tests/mesa_pd/data/SparseLinkedCells.cpp
+++ b/tests/mesa_pd/data/SparseLinkedCells.cpp
@@ -13,7 +13,7 @@
 //  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   SparseLinkedCells.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/domain/BlockForestDomain.cpp b/tests/mesa_pd/domain/BlockForestDomain.cpp
index 33d5a4f7299e7f8cf77299c569929d9545eabd8d..6997297c7795b46c129a7c31356287887e8680d7 100644
--- a/tests/mesa_pd/domain/BlockForestDomain.cpp
+++ b/tests/mesa_pd/domain/BlockForestDomain.cpp
@@ -13,7 +13,7 @@
 //  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   BlockForestDomain.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -66,8 +66,8 @@ void main( int argc, char ** argv )
    WALBERLA_CHECK(domain.isContainedInProcessSubdomain(0, Vec3(2,2,2)));
    WALBERLA_CHECK(domain.isContainedInProcessSubdomain(1, Vec3(7,7,7)));
 
-   WALBERLA_CHECK_EQUAL(domain.isContainedInProcessSubdomain(Vec3(2,2,2), real_t(1)), rank == 0 ? true : false);
-   WALBERLA_CHECK_EQUAL(domain.isContainedInProcessSubdomain(Vec3(7,7,7), real_t(1)), rank == 0 ? false : true);
+   WALBERLA_CHECK_EQUAL(domain.isContainedInProcessSubdomain(Vec3(2,2,2), real_t(1)), rank == 0);
+   WALBERLA_CHECK_EQUAL(domain.isContainedInProcessSubdomain(Vec3(7,7,7), real_t(1)), rank != 0);
 
    WALBERLA_CHECK_EQUAL(domain.isContainedInProcessSubdomain(Vec3(real_t(4.5),2,2), real_t(1)), rank == 0 ? false : false);
    WALBERLA_CHECK_EQUAL(domain.isContainedInProcessSubdomain(Vec3(real_t(5.5),7,7), real_t(1)), rank == 0 ? false : false);
diff --git a/tests/mesa_pd/domain/BlockForestSync.cpp b/tests/mesa_pd/domain/BlockForestSync.cpp
index b0fe8cf5da79846a1174e35e2488fd79bc22662c..bbb3a933a8e8be747c929cb20d22c23e0b2f1f4f 100644
--- a/tests/mesa_pd/domain/BlockForestSync.cpp
+++ b/tests/mesa_pd/domain/BlockForestSync.cpp
@@ -13,7 +13,7 @@
 //  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   BlockForestSync.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/domain/BlockForestSyncPeriodic.cpp b/tests/mesa_pd/domain/BlockForestSyncPeriodic.cpp
index 8523ec9fbd1140a1a81608780253f33c8a8314e4..6d8af3efb56a66eee3edceeda2cb5be637b44d39 100644
--- a/tests/mesa_pd/domain/BlockForestSyncPeriodic.cpp
+++ b/tests/mesa_pd/domain/BlockForestSyncPeriodic.cpp
@@ -13,7 +13,7 @@
 //  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   BlockForestSync.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/domain/DistanceCalculation.cpp b/tests/mesa_pd/domain/DistanceCalculation.cpp
index ef4fffa0052567750dec6c818cda679177900064..5f871333d12bec31428190d3c589543d5161f525 100644
--- a/tests/mesa_pd/domain/DistanceCalculation.cpp
+++ b/tests/mesa_pd/domain/DistanceCalculation.cpp
@@ -13,7 +13,7 @@
 //  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   DistanceCalculation.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/domain/DynamicRefinement.cpp b/tests/mesa_pd/domain/DynamicRefinement.cpp
index f9378da981ff54f4b6ea82ac93139327fdd434e6..5f1f83ef786165583ed90da6656122e36d3f83e1 100644
--- a/tests/mesa_pd/domain/DynamicRefinement.cpp
+++ b/tests/mesa_pd/domain/DynamicRefinement.cpp
@@ -13,7 +13,7 @@
 //  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 DynamicRefinement.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/domain/InfiniteDomain.cpp b/tests/mesa_pd/domain/InfiniteDomain.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..74dee98a9fd58c57e9697085ecc2b6d4731171f5
--- /dev/null
+++ b/tests/mesa_pd/domain/InfiniteDomain.cpp
@@ -0,0 +1,60 @@
+//======================================================================================================================
+//
+//  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   InfiniteDomain.cpp
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/domain/InfiniteDomain.h>
+
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+
+#include <iostream>
+
+namespace walberla {
+
+using namespace walberla::mesa_pd;
+
+void main( int argc, char ** argv )
+{
+   Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   mpi::MPIManager::instance()->useWorldComm();
+   auto rank = mpi::MPIManager::instance()->rank();
+
+   auto randomPoint = Vec3(1.23_r,2.34_r,3.45_r);
+   domain::InfiniteDomain domain;
+   WALBERLA_CHECK(domain.isContainedInProcessSubdomain(static_cast<uint_t>(rank), randomPoint));
+   WALBERLA_CHECK(!domain.isContainedInProcessSubdomain(static_cast<uint_t>(rank + 1), randomPoint));
+   WALBERLA_CHECK_EQUAL(domain.findContainingProcessRank(randomPoint), rank);
+   auto pt = randomPoint;
+   domain.periodicallyMapToDomain(pt);
+   WALBERLA_CHECK_IDENTICAL(pt, randomPoint);
+   WALBERLA_CHECK_EQUAL(domain.getNeighborProcesses().size(), 0);
+   WALBERLA_CHECK(domain.intersectsWithProcessSubdomain(static_cast<uint_t>(rank), randomPoint, 1_r));
+   WALBERLA_CHECK(!domain.intersectsWithProcessSubdomain(static_cast<uint_t>(rank + 1), randomPoint, 1_r));
+   domain.correctParticlePosition(pt);
+   WALBERLA_CHECK_IDENTICAL(pt, randomPoint);
+}
+
+}
+
+int main( int argc, char ** argv )
+{
+   walberla::main(argc, argv);
+   return EXIT_SUCCESS;
+}
diff --git a/tests/mesa_pd/domain/SerializeDeserialize.cpp b/tests/mesa_pd/domain/SerializeDeserialize.cpp
index 49517852b46279e26e24a5e09d03dff0cbcd604f..9b7a63781052a2f2b7d187be97a0cb99815e57ce 100644
--- a/tests/mesa_pd/domain/SerializeDeserialize.cpp
+++ b/tests/mesa_pd/domain/SerializeDeserialize.cpp
@@ -13,7 +13,7 @@
 //  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 DynamicRefinement.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/ClearNextNeighborSync.cpp b/tests/mesa_pd/kernel/ClearNextNeighborSync.cpp
index 19154dc7bafcaf8a55c5407ea0733a8d666d58a0..feae33e908e707a0ba5e42b6d4b3a55c4550842f 100644
--- a/tests/mesa_pd/kernel/ClearNextNeighborSync.cpp
+++ b/tests/mesa_pd/kernel/ClearNextNeighborSync.cpp
@@ -13,7 +13,7 @@
 //  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   ClearNextNeighborSync.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/CoefficientOfRestitutionLSD.cpp b/tests/mesa_pd/kernel/CoefficientOfRestitutionLSD.cpp
index 7e316264f934e5088e33028a4dc9fe96714f440b..a9cb84564f6d559a808eeedbb9b6357bbd1d1bbd 100644
--- a/tests/mesa_pd/kernel/CoefficientOfRestitutionLSD.cpp
+++ b/tests/mesa_pd/kernel/CoefficientOfRestitutionLSD.cpp
@@ -13,7 +13,7 @@
 //  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 DEMIntegratorAccuracy.cpp
+//! \file
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
 //======================================================================================================================
@@ -26,7 +26,7 @@
 
 #include "mesa_pd/kernel/DoubleCast.h"
 #include "mesa_pd/kernel/VelocityVerlet.h"
-#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
 #include "mesa_pd/kernel/LinearSpringDashpot.h"
 
 #include "core/Environment.h"
@@ -136,7 +136,7 @@ int main( int argc, char** argv )
    collision_detection::AnalyticContactDetection acd;
    kernel::DoubleCast       double_cast;
 
-   kernel::ExplicitEuler explEuler(dt);
+   kernel::SemiImplicitEuler implEuler(dt);
    kernel::VelocityVerletPreForceUpdate  vvPreForce( dt );
    kernel::VelocityVerletPostForceUpdate vvPostForce( dt );
 
@@ -160,7 +160,7 @@ int main( int argc, char** argv )
       auto force = accessor->getForce(0);
 
       if(useVelocityVerlet) vvPostForce(0,*accessor);
-      else explEuler(0, *accessor);
+      else implEuler(0, *accessor);
 
       WALBERLA_LOG_INFO(steps << ": penetration = " << acd.getPenetrationDepth() << " || vel = " << accessor->getLinearVelocity(0)[2] << " || force = " << force[2]);
 
diff --git a/tests/mesa_pd/kernel/CoefficientOfRestitutionNLSD.cpp b/tests/mesa_pd/kernel/CoefficientOfRestitutionNLSD.cpp
index c422a70b497527a3ff6ef5aaf4181168a7ec9ddb..5912892af2b3df1351804f433dea1b3f4d402790 100644
--- a/tests/mesa_pd/kernel/CoefficientOfRestitutionNLSD.cpp
+++ b/tests/mesa_pd/kernel/CoefficientOfRestitutionNLSD.cpp
@@ -13,7 +13,7 @@
 //  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 DEMIntegratorAccuracy.cpp
+//! \file
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
 //======================================================================================================================
@@ -26,7 +26,7 @@
 
 #include "mesa_pd/kernel/DoubleCast.h"
 #include "mesa_pd/kernel/VelocityVerlet.h"
-#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
 #include "mesa_pd/kernel/NonLinearSpringDashpot.h"
 #include "mesa_pd/mpi/ReduceContactHistory.h"
 
@@ -137,7 +137,7 @@ int main( int argc, char** argv )
    collision_detection::AnalyticContactDetection acd;
    kernel::DoubleCast       double_cast;
 
-   kernel::ExplicitEuler explEuler(dt);
+   kernel::SemiImplicitEuler implEuler(dt);
    kernel::VelocityVerletPreForceUpdate  vvPreForce( dt );
    kernel::VelocityVerletPostForceUpdate vvPostForce( dt );
 
@@ -162,7 +162,7 @@ int main( int argc, char** argv )
       auto force = accessor->getForce(0);
 
       if(useVelocityVerlet) vvPostForce(0,*accessor);
-      else explEuler(0, *accessor);
+      else implEuler(0, *accessor);
 
       WALBERLA_LOG_INFO(steps << ": penetration = " << acd.getPenetrationDepth() << " || vel = " << accessor->getLinearVelocity(0)[2] << " || force = " << force[2]);
 
diff --git a/tests/mesa_pd/kernel/CoefficientOfRestitutionSD.cpp b/tests/mesa_pd/kernel/CoefficientOfRestitutionSD.cpp
index 300e0eb916d6f19330ccc873733387fbd47981e3..00fb530524ebaf88be3dca3754aed6e4ad86d5a1 100644
--- a/tests/mesa_pd/kernel/CoefficientOfRestitutionSD.cpp
+++ b/tests/mesa_pd/kernel/CoefficientOfRestitutionSD.cpp
@@ -13,7 +13,7 @@
 //  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 DEMIntegratorAccuracy.cpp
+//! \file
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
 //======================================================================================================================
@@ -26,7 +26,7 @@
 
 #include "mesa_pd/kernel/DoubleCast.h"
 #include "mesa_pd/kernel/VelocityVerlet.h"
-#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
 #include "mesa_pd/kernel/SpringDashpot.h"
 
 #include "core/Environment.h"
@@ -136,7 +136,7 @@ int main( int argc, char** argv )
    collision_detection::AnalyticContactDetection acd;
    kernel::DoubleCast       double_cast;
 
-   kernel::ExplicitEuler explEuler(dt);
+   kernel::SemiImplicitEuler implEuler(dt);
    kernel::VelocityVerletPreForceUpdate  vvPreForce( dt );
    kernel::VelocityVerletPostForceUpdate vvPostForce( dt );
 
@@ -160,7 +160,7 @@ int main( int argc, char** argv )
       auto force = accessor->getForce(0);
 
       if(useVelocityVerlet) vvPostForce(0,*accessor);
-      else explEuler(0, *accessor);
+      else implEuler(0, *accessor);
 
       WALBERLA_LOG_INFO(steps << ": penetration = " << acd.getPenetrationDepth() << " || vel = " << accessor->getLinearVelocity(0)[2] << " || force = " << force[2]);
 
diff --git a/tests/mesa_pd/kernel/DetectAndStoreContacts.cpp b/tests/mesa_pd/kernel/DetectAndStoreContacts.cpp
index 25b192a539cf338a6de7b987cb748857393670b0..5e4c2db774a8f64fc54a455583defae9759f922d 100644
--- a/tests/mesa_pd/kernel/DetectAndStoreContacts.cpp
+++ b/tests/mesa_pd/kernel/DetectAndStoreContacts.cpp
@@ -13,7 +13,7 @@
 //  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   DetectAndStoreCollisions.cpp
+//! \file
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/DoubleCast.cpp b/tests/mesa_pd/kernel/DoubleCast.cpp
index 4e2c11cdc050541f58d2109820e47996b7594b3b..0d2c80d04a3a7eac546393e12e0214ace2e62872 100644
--- a/tests/mesa_pd/kernel/DoubleCast.cpp
+++ b/tests/mesa_pd/kernel/DoubleCast.cpp
@@ -13,7 +13,7 @@
 //  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   DoubleCast.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/ExplicitEuler.cpp b/tests/mesa_pd/kernel/ExplicitEuler.cpp
index 1685ff1aed29b315bfd36be07ef45b0465b44908..b4673d1895067c3e6aa46b0d50a6de53cfa952e3 100644
--- a/tests/mesa_pd/kernel/ExplicitEuler.cpp
+++ b/tests/mesa_pd/kernel/ExplicitEuler.cpp
@@ -13,7 +13,7 @@
 //  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   ExplicitEuler.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -66,8 +66,10 @@ int main( int argc, char ** argv )
    accessor.setAngularVelocity( 0, angVel);
    accessor.setForce(           0, force);
    accessor.setTorque(          0, torque);
-   accessor.setInvMass(         0, real_t(1.23456));
-   accessor.setInvInertiaBF(    0, Mat3(real_t(1.23456), real_t(0), real_t(0), real_t(0), real_t(1.23456), real_t(0), real_t(0), real_t(0), real_t(1.23456)));
+   accessor.setInvMass(         0, 1.23456_r);
+   accessor.setInvInertiaBF(    0, Mat3(1.23456_r, 0_r, 0_r,
+                                        0_r, 1.23456_r, 0_r,
+                                        0_r, 0_r, 1.23456_r ));
 
    //init kernels
    const real_t dt = real_t(1);
@@ -83,12 +85,16 @@ int main( int argc, char ** argv )
    WALBERLA_CHECK_FLOAT_EQUAL(accessor.getTorque(0), Vec3(0));
 
    //check velocity
-   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getLinearVelocity(0), force * accessor.getInvMass(0) * dt + linVel);
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getLinearVelocity(0), force * accessor.getInvMass(0) * dt +
+                                                             linVel);
    WALBERLA_CHECK_FLOAT_EQUAL(accessor.getAngularVelocity(0), wdot * dt + angVel);
 
    //check position
-   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getPosition(0), linVel * dt + force * accessor.getInvMass(0) * dt * dt);
-   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getRotation(0).getQuaternion(), Quat( (wdot * dt + angVel).getNormalized(), (wdot * dt + angVel).length() * dt ));
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getPosition(0), linVel * dt +
+                                                       0.5_r * force * accessor.getInvMass(0) * dt * dt);
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getRotation(0).getQuaternion(),
+                              Quat( (wdot * dt + angVel).getNormalized(),
+                                    (0.5_r * wdot * dt + angVel).length() * dt ));
 
    accessor.setPosition(        0, Vec3(0,0,0));
    accessor.setRotation(        0, Rot3(Quat()));
@@ -96,8 +102,10 @@ int main( int argc, char ** argv )
    accessor.setAngularVelocity( 0, angVel);
    accessor.setForce(           0, force);
    accessor.setTorque(          0, torque);
-   accessor.setInvMass(         0, real_t(1.23456));
-   accessor.setInvInertiaBF(    0, Mat3(real_t(1.23456), real_t(0), real_t(0), real_t(0), real_t(1.23456), real_t(0), real_t(0), real_t(0), real_t(1.23456)));
+   accessor.setInvMass(         0, 1.23456_r);
+   accessor.setInvInertiaBF(    0, Mat3(1.23456_r, 0_r, 0_r,
+                                           0_r, 1.23456_r, 0_r,
+                                           0_r, 0_r, 1.23456_r ));
    data::particle_flags::set( accessor.getFlagsRef(0), data::particle_flags::FIXED );
 
    integrator(0, accessor);
diff --git a/tests/mesa_pd/kernel/ForceLJ.cpp b/tests/mesa_pd/kernel/ForceLJ.cpp
index 12c49b5c7aa38b3c4f85b51afa723b185b993fe5..adcb643cd4c90f4db741f226245f97f8959b983b 100644
--- a/tests/mesa_pd/kernel/ForceLJ.cpp
+++ b/tests/mesa_pd/kernel/ForceLJ.cpp
@@ -13,7 +13,7 @@
 //  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   ForceLJ.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/GenerateAnalyticContacts.cpp b/tests/mesa_pd/kernel/GenerateAnalyticContacts.cpp
index 757587bebfe82f2cf82851f6473947501a05bd61..4762aed06e159d322033faace72d48c807fedc2d 100644
--- a/tests/mesa_pd/kernel/GenerateAnalyticContacts.cpp
+++ b/tests/mesa_pd/kernel/GenerateAnalyticContacts.cpp
@@ -13,7 +13,7 @@
 //  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   GenerateAnalyticContacts.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/GenerateLinkedCells.cpp b/tests/mesa_pd/kernel/GenerateLinkedCells.cpp
index e7a60f36ed57cfc8a05fbda3c14d884b291f73c6..3ee5183cfcce026112cb192929db1de10bfff5aa 100644
--- a/tests/mesa_pd/kernel/GenerateLinkedCells.cpp
+++ b/tests/mesa_pd/kernel/GenerateLinkedCells.cpp
@@ -13,12 +13,12 @@
 //  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   GenerateLinkedCells.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
 
-#include <mesa_pd/kernel/ExplicitEuler.h>
+#include <mesa_pd/kernel/SemiImplicitEuler.h>
 #include <mesa_pd/kernel/ForceLJ.h>
 #include <mesa_pd/kernel/InsertParticleIntoLinkedCells.h>
 #include <mesa_pd/kernel/ParticleSelector.h>
@@ -72,7 +72,7 @@ int main( int argc, char ** argv )
    //init kernels
    kernel::InsertParticleIntoLinkedCells ipilc;
    kernel::ForceLJ lj(1);
-   kernel::ExplicitEuler integrator( real_t(0.01) );
+   kernel::SemiImplicitEuler integrator( real_t(0.01) );
 
    //timeloop
    for (auto timestep = 0; timestep < 100; ++timestep)
diff --git a/tests/mesa_pd/kernel/HCSITSKernels.cpp b/tests/mesa_pd/kernel/HCSITSKernels.cpp
index c3cb9fd6d800cb94e0e879a7b8ac5bd81321180b..4f0428f3895b8559a50136eaea35191391d4c098 100644
--- a/tests/mesa_pd/kernel/HCSITSKernels.cpp
+++ b/tests/mesa_pd/kernel/HCSITSKernels.cpp
@@ -13,7 +13,7 @@
 //  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   HCSITSKernels.cpp
+//! \file
 //! \author Tobias Leemann <tobias.leemann@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/HeatConduction.cpp b/tests/mesa_pd/kernel/HeatConduction.cpp
index 9c8c4cd9e29419d051ef4b78f0b99c3b53c2a445..06d7c3323238042a948b8ec131d8a7b31fb57afd 100644
--- a/tests/mesa_pd/kernel/HeatConduction.cpp
+++ b/tests/mesa_pd/kernel/HeatConduction.cpp
@@ -13,7 +13,7 @@
 //  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   HeatConduction.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/IntegratorAccuracy.cpp b/tests/mesa_pd/kernel/IntegratorAccuracy.cpp
index 695bae5b9662299ceb77583d726fab3e012e4b62..433ecb5467e60032b94ed0b3ebf81586c98b44ec 100644
--- a/tests/mesa_pd/kernel/IntegratorAccuracy.cpp
+++ b/tests/mesa_pd/kernel/IntegratorAccuracy.cpp
@@ -13,172 +13,254 @@
 //  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   IntegratorAccuracy.cpp
+//! \file
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
 
-#include "mesa_pd/data/ParticleAccessorWithShape.h"
-#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/data/ParticleAccessor.h"
 
-#include "mesa_pd/kernel/ParticleSelector.h"
-#include "mesa_pd/kernel/VelocityVerlet.h"
 #include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
 
 #include "core/Environment.h"
-#include "core/logging/Logging.h"
 #include "core/math/all.h"
 
 #include <iostream>
 
-namespace integrator_accuracy
-{
-
-using namespace walberla;
+namespace walberla {
 
 using namespace walberla::mesa_pd;
 
-
-Vec3 getForce(const Vec3& pos, real_t k)
+class SingleParticleAccessorWithShape : public data::SingleParticleAccessor
 {
-   return -k * pos;
-}
+public:
+   void setInvMass(const size_t /*p_idx*/, const real_t &val)
+   { invMass_ = val; }
 
-real_t analyticalTrajectory(real_t amplitude, real_t timeStep, real_t omega, real_t phase)
-{
-   return amplitude * std::cos(omega * timeStep + phase);
-}
+   const auto &getInvMass(const size_t /*p_idx*/) const
+   { return invMass_; }
 
-real_t analyticalVelocity(real_t amplitude, real_t timeStep, real_t omega, real_t phase)
-{
-   return -amplitude * omega * std::sin(omega * timeStep + phase);
-}
+   void setInvInertiaBF(const size_t /*p_idx*/, const Mat3 &val)
+   { invInertiaBF_ = val; }
 
+   const auto &getInvInertiaBF(const size_t /*p_idx*/) const
+   { return invInertiaBF_; }
 
-/*
- * Simulates a harmonic oscillator to test the accuracy of the integrators.
- * The error of the maximum position and the maximum velocity is compared against the analytical values.
- * Via command line arguments, the simulation can be adapted.
- * Currently tested integrators:
- *  - explicit Euler (default)
- *  - velocity verlet (-useVV)
- */
-int main( int argc, char ** argv )
-{
-   mpi::Environment env(argc, argv);
-   WALBERLA_UNUSED(env);
-   mpi::MPIManager::instance()->useWorldComm();
+private:
+   real_t invMass_;
+   Mat3 invInertiaBF_;
+};
 
-   real_t amplitude       = real_t(1.5);
-   real_t k               = real_t(0.1);
-   real_t mass            = real_t(0.9);
-   real_t dt              = real_t(0.2);
-   bool useVelocityVerlet = false;
-   real_t phaseFraction   = real_t(0);
-   real_t periods         = real_t(1.1);
+struct Oscillator
+{
+   real_t amplitude = 1.5_r;
+   real_t k = 0.1_r;
+   real_t damping = 0_r;
+   real_t mass = 0.9_r;
+   real_t dt = 0.2_r;
+   real_t phaseFraction = 0_r;
+   real_t periods = 10_r;
+   real_t phase = phaseFraction * math::pi;
+   real_t dampingRatio = damping / (2_r * std::sqrt(mass * k));
+   real_t omega = std::sqrt(k / mass) * std::sqrt(1_r - dampingRatio * dampingRatio);
+   real_t decay = std::sqrt(k / mass) * dampingRatio;
+   real_t durationOnePeriod = 2_r * math::pi / omega;
+   uint_t timeSteps = uint_c(periods * durationOnePeriod / dt);
 
-   for( int i = 1; i < argc; ++i )
+   void update()
    {
-      if( std::strcmp( argv[i], "--useVV" )         == 0 ) { useVelocityVerlet = true; continue; }
-      if( std::strcmp( argv[i], "--dt" )            == 0 ) { dt = real_c(std::atof( argv[++i] )); continue; }
-      if( std::strcmp( argv[i], "--amplitude" )     == 0 ) { amplitude = real_c(std::atof( argv[++i] )); continue; }
-      if( std::strcmp( argv[i], "--k" )             == 0 ) { k = real_c(std::atof( argv[++i] )); continue; }
-      if( std::strcmp( argv[i], "--mass" )          == 0 ) { mass = real_c(std::atof( argv[++i] )); continue; }
-      if( std::strcmp( argv[i], "--phaseFraction" ) == 0 ) { phaseFraction = real_c(std::atof( argv[++i] )); continue; }
-      if( std::strcmp( argv[i], "--periods" )       == 0 ) { periods = real_c(std::atof( argv[++i] )); continue; }
-      WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
+      phase = phaseFraction * math::pi;
+      dampingRatio = damping / (2_r * std::sqrt(mass * k));
+      omega = std::sqrt(k / mass) * std::sqrt(1_r - dampingRatio * dampingRatio);
+      decay = std::sqrt(k / mass) * dampingRatio;
+      durationOnePeriod = 2_r * math::pi / omega;
+      timeSteps = uint_c(periods * durationOnePeriod / dt);
    }
 
+   Vec3 getForce(const Vec3 &pos, const Vec3 &vel) const
+   {
+      return -k * pos - damping * vel;
+   }
 
-   real_t phase = phaseFraction * math::pi;
-   real_t omega = std::sqrt(k / mass);
-   real_t durationOnePeriod = real_t(2) * math::pi / omega;
-   uint_t timeSteps = uint_c(periods * durationOnePeriod / dt);
+   real_t getEnergy(const real_t &pos, const real_t &vel) const
+   {
+      return 0.5_r * mass * vel * vel + 0.5_r * k * pos * pos;
+   }
 
-   WALBERLA_LOG_INFO("omega = " << omega << ", T = " << real_t(2) * math::pi / omega << ", time steps = " << timeSteps << ", phase = " << phase << ", periods = " << periods);
+   real_t analyticalPos(real_t t) const
+   {
+      return amplitude * std::exp(-decay * t) * std::cos(omega * t + phase);
+   }
 
-   //initialize particle
-   const auto pos = Vec3(0,0,analyticalTrajectory(amplitude, real_t(0), omega, phase));
-   const auto linVel = Vec3(0,0,analyticalVelocity(amplitude, real_t(0), omega, phase));
+   real_t analyticalVel(real_t t) const
+   {
+      return -decay * amplitude * std::exp(-decay * t) * std::cos(omega * t + phase)
+             -amplitude * std::exp(-decay * t) * omega * std::sin(omega * t + phase);
+   }
+};
 
-   WALBERLA_LOG_INFO("Initial pos = " << pos << ", vel = " << linVel);
+struct ExplicitEuler
+{
+   ExplicitEuler(real_t dt) : integrator(dt) {}
+   void operator()(SingleParticleAccessorWithShape& particle,
+                   const Oscillator& osc)
+   {
+      particle.setForce(0, osc.getForce(particle.getPosition(0),
+                                           particle.getLinearVelocity(0)));
+      integrator(0, particle);
+   }
+   kernel::ExplicitEuler integrator;
+};
 
-   //init data structures
-   auto ps = std::make_shared<data::ParticleStorage>(1);
-   auto ss = std::make_shared<data::ShapeStorage>();
-   auto  smallSphere = ss->create<data::Sphere>( real_t(1) );
-   ss->shapes[smallSphere]->updateMassAndInertia(real_t(2707));
-   data::ParticleAccessorWithShape accessor(ps, ss);
-   
-   data::Particle&& p = *ps->create();
-   p.setPosition(pos);
-   p.setLinearVelocity(linVel);
-   p.setForce(getForce(pos, k));
-   p.setOldForce(getForce(pos, k));
-   p.setInvMass(real_t(1) / mass);
-   p.setShapeID(smallSphere);
-
-   // velocity verlet
-   kernel::VelocityVerletPreForceUpdate  preForce( dt );
-   kernel::VelocityVerletPostForceUpdate postForce( dt );
+struct SemiImplicitEuler
+{
+   SemiImplicitEuler(real_t dt) : integrator(dt) {}
+   void operator()(SingleParticleAccessorWithShape& particle,
+                   const Oscillator& osc)
+   {
+      particle.setForce(0, osc.getForce(particle.getPosition(0),
+                                           particle.getLinearVelocity(0)));
+      integrator(0, particle);
+   }
+   kernel::SemiImplicitEuler integrator;
+};
 
-   // explicit euler
-   kernel::ExplicitEuler explEuler( dt );
-
-   real_t maxVel = 0.;
-   real_t maxRefVel = 0.;
-   real_t maxPos = 0.;
-   real_t maxRefPos = 0.;
-   
-   for (auto i = uint_t(1); i <= timeSteps; ++i)
+struct VelocityVerlet
+{
+   VelocityVerlet(real_t dt) : preVV(dt), postVV(dt) {}
+   void operator()(SingleParticleAccessorWithShape& particle,
+                   const Oscillator& osc)
    {
+      preVV(0, particle);
+      particle.setForce(0, osc.getForce(particle.getPosition(0),
+                                           particle.getLinearVelocity(0)));
+      postVV(0, particle);
+   }
+   kernel::VelocityVerletPreForceUpdate preVV;
+   kernel::VelocityVerletPostForceUpdate postVV;
+};
 
-      if( useVelocityVerlet ) ps->forEachParticle(false, kernel::SelectAll(), accessor, preForce, accessor);
-      p.setForce( getForce(p.getPosition(), k) );
-      auto force = p.getForce();
+struct AccuracyResult
+{
+   real_t maxPosDeviation;
+   real_t maxVelDeviation;
+   real_t maxEneDeviation;
+};
 
-      if( useVelocityVerlet ) ps->forEachParticle(false, kernel::SelectAll(), accessor, postForce, accessor);
-      else  ps->forEachParticle(false, kernel::SelectAll(), accessor, explEuler, accessor);
+template <typename Integrator>
+AccuracyResult checkIntegrator(const Oscillator& osc)
+{
+   //init data structures
+   SingleParticleAccessorWithShape particle;
 
-      real_t refPos = analyticalTrajectory(amplitude, real_c(i) * dt, omega, phase);
-      real_t refVel = analyticalVelocity(amplitude, real_c(i) * dt, omega, phase);
+   //first dummy argument is needed to fulfill accessor interface
+   particle.setPosition(0, Vec3(0, 0, osc.analyticalPos(0_r)));
+   particle.setLinearVelocity(0, Vec3(0, 0, osc.analyticalVel(0_r)));
+   particle.setInvMass(0, 1_r / osc.mass);
+   particle.setForce(0, osc.getForce(Vec3(0, 0, osc.analyticalPos(real_t(0))),
+                                        Vec3(0, 0, osc.analyticalVel(real_t(0)))));
+   particle.setOldForce(0, osc.getForce(Vec3(0, 0, osc.analyticalPos(-osc.dt)),
+                                           Vec3(0, 0, osc.analyticalVel(-osc.dt))));
 
-      WALBERLA_LOG_INFO(i << ": pos = " << p.getPosition()[2] << " " << refPos
-                          << " || vel = " << p.getLinearVelocity()[2] << " " << refVel
-                          << " || force = " << force[2] );
+   // explicit euler
+   Integrator integrator(osc.dt);
 
-      maxPos = std::max(maxPos, std::abs(p.getPosition()[2]));
-      maxRefPos = std::max(maxRefPos, std::abs(refPos));
+   real_t maxPosDeviation = 0_r;
+   real_t maxVelDeviation = 0_r;
+   real_t maxEneDeviation = 0_r;
 
-      maxVel = std::max(maxVel, std::abs(p.getLinearVelocity()[2]));
-      maxRefVel = std::max(maxRefVel, std::abs(refVel));
+   for (auto i = uint_t(0); i <= osc.timeSteps; ++i)
+   {
+      real_t refPos = osc.analyticalPos(real_c(i) * osc.dt);
+      real_t refVel = osc.analyticalVel(real_c(i) * osc.dt);
+      real_t refEne = osc.getEnergy(refPos, refVel);
+
+      maxPosDeviation = std::max(maxPosDeviation, std::abs(particle.getPosition(0)[2] - refPos));
+      maxVelDeviation = std::max(maxVelDeviation, std::abs(particle.getLinearVelocity(0)[2] - refVel));
+      maxEneDeviation = std::max(maxEneDeviation, std::abs(osc.getEnergy(particle.getPosition(0)[2], particle.getLinearVelocity(0)[2]) - refEne));
+
+      integrator(particle, osc);
    }
 
-   real_t relativePositionError = ( maxPos - maxRefPos ) / maxRefPos;
-   WALBERLA_LOG_INFO("error in position = " << relativePositionError * 100. << "%");
+   return {maxPosDeviation, maxVelDeviation, maxEneDeviation};
+}
 
-   real_t relativeVelocityError = ( maxVel - maxRefVel ) / maxRefVel;
-   WALBERLA_LOG_INFO("error in velocity = " << relativeVelocityError * 100. << "%");
+int main(int argc, char **argv)
+{
+   mpi::Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   mpi::MPIManager::instance()->useWorldComm();
 
-   if( useVelocityVerlet )
-   {
-      WALBERLA_CHECK_LESS(relativePositionError, real_t(0.01), "Error in position too large!");
-      WALBERLA_CHECK_LESS(relativeVelocityError, real_t(0.01), "Error in velocity too large!");
-   }
-   else
+   if (std::is_same<real_t, float>::value)
    {
-      WALBERLA_CHECK_LESS(relativePositionError, real_t(0.11), "Error in position too large!");
-      WALBERLA_CHECK_LESS(relativeVelocityError, real_t(0.10), "Error in velocity too large!");
+      WALBERLA_LOG_WARNING("waLBerla build in sp mode: skipping test due to low precision");
+      return EXIT_SUCCESS;
    }
 
+   Oscillator osc;
+
+   AccuracyResult res;
+   osc.dt = 0.1_r;
+   osc.update();
+   res = walberla::checkIntegrator<walberla::ExplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 1.03068993874562120e+00_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 3.33225581358688350e-01_r);
+   res = walberla::checkIntegrator<walberla::SemiImplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 2.92576360173544339e-02_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 1.45120298258364505e-03_r);
+   res = walberla::checkIntegrator<walberla::VelocityVerlet>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 4.24116598691555435e-03_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 1.45059589844465098e-03_r);
+
+   osc.dt = 0.2_r;
+   osc.update();
+   res = walberla::checkIntegrator<walberla::ExplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 2.76387000209972467e+00_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 8.88433661185269896e-01_r);
+   res = walberla::checkIntegrator<walberla::SemiImplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 6.70464626869100577e-02_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 5.81009940233766925e-03_r);
+   res = walberla::checkIntegrator<walberla::VelocityVerlet>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 1.69147419522671719e-02_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 5.78432113979018836e-03_r);
+
+   osc.dt = 0.4_r;
+   osc.update();
+   res = walberla::checkIntegrator<walberla::ExplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 1.04680753378045406e+01_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 3.34470580215144420e+00_r);
+   res = walberla::checkIntegrator<walberla::SemiImplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 1.68291142727994780e-01_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 2.33193930919295134e-02_r);
+   res = walberla::checkIntegrator<walberla::VelocityVerlet>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxPosDeviation, 6.71909796751584687e-02_r);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxVelDeviation, 2.29906986019860066e-02_r);
+
+   //check energy conservation
+   osc.dt = 0.4_r;
+   osc.periods = 1000;
+   osc.update();
+   //res = walberla::checkIntegrator<walberla::ExplicitEuler>(osc);
+   //explicit euler is not symplectic
+   res = walberla::checkIntegrator<walberla::SemiImplicitEuler>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxEneDeviation, 8.03571427989904774e-03_r);
+   res = walberla::checkIntegrator<walberla::VelocityVerlet>(osc);
+   WALBERLA_CHECK_FLOAT_EQUAL( res.maxEneDeviation, 4.99960610503419334e-04_r);
 
    return EXIT_SUCCESS;
 }
 
-} //namespace integrator_accuracy
+} //namespace walberla
+
 
-int main( int argc, char ** argv )
+/*
+ * Simulates a harmonic oscillator to test the accuracy of the integrators.
+ */
+int main(int argc, char **argv)
 {
-   return integrator_accuracy::main(argc, argv);
+   return walberla::main(argc, argv);
 }
 
diff --git a/tests/mesa_pd/kernel/Interfaces.cpp b/tests/mesa_pd/kernel/Interfaces.cpp
index 25fabf62441af81cc6f9a7900ff95564c1484b1d..4c8769ec3f296144c91b5b4991184d237b375293 100644
--- a/tests/mesa_pd/kernel/Interfaces.cpp
+++ b/tests/mesa_pd/kernel/Interfaces.cpp
@@ -13,7 +13,7 @@
 //  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   Interfaces.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/LinearSpringDashpot.cpp b/tests/mesa_pd/kernel/LinearSpringDashpot.cpp
index 18beb0732157060883c716146297fa803e3b82cb..0b749b822dcee35737060803593e528a95380565 100644
--- a/tests/mesa_pd/kernel/LinearSpringDashpot.cpp
+++ b/tests/mesa_pd/kernel/LinearSpringDashpot.cpp
@@ -13,7 +13,7 @@
 //  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   DEMTangentialCollision.cpp
+//! \file
 //! \author Christoph Rettinger <christoph.rettinger@fau.de>
 //
 //======================================================================================================================
@@ -26,7 +26,7 @@
 #include "mesa_pd/data/ShapeStorage.h"
 
 #include "mesa_pd/kernel/DoubleCast.h"
-#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
 #include "mesa_pd/kernel/VelocityVerlet.h"
 #include "mesa_pd/kernel/LinearSpringDashpot.h"
 #include "mesa_pd/mpi/ReduceContactHistory.h"
@@ -152,7 +152,7 @@ int main( int argc, char ** argv )
    kernel::VelocityVerletPostForceUpdate vvPostForce( dt );
 
    // explicit euler
-   kernel::ExplicitEuler explEuler( dt );
+   kernel::SemiImplicitEuler implEuler( dt );
 
    // collision response
    collision_detection::AnalyticContactDetection     acd;
@@ -188,7 +188,7 @@ int main( int argc, char ** argv )
       rch(*ps);
 
       if(useVelocityVerlet) vvPostForce(0,*accessor);
-      else explEuler(0, *accessor);
+      else implEuler(0, *accessor);
 
       ++steps;
    } while (double_cast(0, 1, *accessor, acd, *accessor ) || p.getLinearVelocity()[2] < 0);
diff --git a/tests/mesa_pd/kernel/LinkedCellsVsBruteForce.cpp b/tests/mesa_pd/kernel/LinkedCellsVsBruteForce.cpp
index ce990842f231d75c580768985b9f8ec750adcd46..c52e39327afae37a42460cdcb2510bd5f7195002 100644
--- a/tests/mesa_pd/kernel/LinkedCellsVsBruteForce.cpp
+++ b/tests/mesa_pd/kernel/LinkedCellsVsBruteForce.cpp
@@ -13,7 +13,7 @@
 //  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   LinkedCellsVsBruteForce.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/PFCDamping.cpp b/tests/mesa_pd/kernel/PFCDamping.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..42966022e543f39b43193ec8d165f1cb383c7080
--- /dev/null
+++ b/tests/mesa_pd/kernel/PFCDamping.cpp
@@ -0,0 +1,64 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//
+//======================================================================================================================
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/ParticleAccessor.h>
+
+#include <mesa_pd/kernel/PFCDamping.h>
+
+#include <core/Environment.h>
+
+namespace walberla {
+
+using namespace walberla::mesa_pd;
+
+int main( int argc, char ** argv )
+{
+   Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   mpi::MPIManager::instance()->useWorldComm();
+
+   //init data structures
+   data::SingleParticleAccessor ac;
+
+   ac.setLinearVelocity( 0, Vec3(+2_r,-2_r,+2_r) );
+   ac.setForce         ( 0, Vec3(+2_r,+3_r,-4_r) );
+
+   ac.setAngularVelocity( 0, Vec3(+2_r,-2_r,+2_r) );
+   ac.setTorque         ( 0, Vec3(+3_r,+5_r,-2_r) );
+
+   //init kernels
+   kernel::PFCDamping damping( 0.1_r );
+
+   damping(0, ac);
+
+   WALBERLA_CHECK_FLOAT_EQUAL(ac.getForce(0),  Vec3(1.8_r, 3.3_r, -4.4_r));
+   WALBERLA_CHECK_FLOAT_EQUAL(ac.getTorque(0), Vec3(2.7_r, 5.5_r, -2.2_r));
+
+   return EXIT_SUCCESS;
+}
+
+} //namespace walberla
+
+int main( int argc, char ** argv )
+{
+   return walberla::main(argc, argv);
+}
diff --git a/tests/mesa_pd/kernel/SemiImplicitEuler.cpp b/tests/mesa_pd/kernel/SemiImplicitEuler.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..551d016f11c958d04f92db784f8f4daa9c94a35a
--- /dev/null
+++ b/tests/mesa_pd/kernel/SemiImplicitEuler.cpp
@@ -0,0 +1,133 @@
+//======================================================================================================================
+//
+//  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
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/data/DataTypes.h>
+#include <mesa_pd/data/ParticleAccessor.h>
+
+#include <mesa_pd/kernel/SemiImplicitEuler.h>
+
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+
+#include <iostream>
+
+namespace walberla {
+
+using namespace walberla::mesa_pd;
+
+class SingleParticleAccessor : public data::SingleParticleAccessor
+{
+public:
+   const walberla::real_t& getInvMass(const size_t /*p_idx*/) const {return invMass_;}
+   void setInvMass(const size_t /*p_idx*/, const walberla::real_t& v) { invMass_ = v;}
+   const walberla::mesa_pd::Mat3& getInvInertiaBF(const size_t /*p_idx*/) const {return invInertiaBF_;}
+   void setInvInertiaBF(const size_t /*p_idx*/, const walberla::mesa_pd::Mat3& v) { invInertiaBF_ = v;}
+
+   walberla::real_t        invMass_;
+   walberla::mesa_pd::Mat3 invInertiaBF_;
+};
+
+int main( int argc, char ** argv )
+{
+   Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   mpi::MPIManager::instance()->useWorldComm();
+
+   //init data structures
+   SingleParticleAccessor accessor;
+
+   //initialize particle
+   const auto linVel = Vec3(1,2,3);
+   const auto angVel = Vec3(1,2,3);
+
+   const auto force  = Vec3(1,2,3);
+   const auto torque = Vec3(1,2,3);
+
+   accessor.setPosition(        0, Vec3(0,0,0));
+   accessor.setRotation(        0, Rot3(Quat()));
+   accessor.setLinearVelocity(  0, linVel);
+   accessor.setAngularVelocity( 0, angVel);
+   accessor.setForce(           0, force);
+   accessor.setTorque(          0, torque);
+   accessor.setInvMass(         0, 1.23456_r);
+   accessor.setInvInertiaBF(    0, Mat3(1.23456_r, 0_r, 0_r,
+                                        0_r, 1.23456_r, 0_r,
+                                        0_r, 0_r, 1.23456_r ));
+
+   //init kernels
+   const real_t dt = real_t(1);
+   kernel::SemiImplicitEuler integrator( dt );
+
+   integrator(0, accessor);
+
+   const auto& R = accessor.getRotation(0).getMatrix();
+   const auto wdot = R * accessor.getInvInertiaBF(0) * R.getTranspose() * torque;
+
+   //check force
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getForce(0), Vec3(0));
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getTorque(0), Vec3(0));
+
+   //check velocity
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getLinearVelocity(0), force * accessor.getInvMass(0) * dt +
+                                                             linVel);
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getAngularVelocity(0), wdot * dt + angVel);
+
+   //check position
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getPosition(0), linVel * dt +
+                                                       force * accessor.getInvMass(0) * dt * dt);
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getRotation(0).getQuaternion(),
+                              Quat( (wdot * dt + angVel).getNormalized(),
+                                    (wdot * dt + angVel).length() * dt ));
+
+   accessor.setPosition(        0, Vec3(0,0,0));
+   accessor.setRotation(        0, Rot3(Quat()));
+   accessor.setLinearVelocity(  0, linVel);
+   accessor.setAngularVelocity( 0, angVel);
+   accessor.setForce(           0, force);
+   accessor.setTorque(          0, torque);
+   accessor.setInvMass(         0, 1.23456_r);
+   accessor.setInvInertiaBF(    0, Mat3(1.23456_r, 0_r, 0_r,
+                                           0_r, 1.23456_r, 0_r,
+                                           0_r, 0_r, 1.23456_r ));
+   data::particle_flags::set( accessor.getFlagsRef(0), data::particle_flags::FIXED );
+
+   integrator(0, accessor);
+
+   //check force
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getForce(0), Vec3(0));
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getTorque(0), Vec3(0));
+
+   //check velocity
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getLinearVelocity(0), linVel);
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getAngularVelocity(0), angVel);
+
+   //check position
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getPosition(0), Vec3(0,0,0));
+   WALBERLA_CHECK_FLOAT_EQUAL(accessor.getRotation(0).getQuaternion(), Quat());
+
+   return EXIT_SUCCESS;
+}
+
+} //namespace walberla
+
+int main( int argc, char ** argv )
+{
+   return walberla::main(argc, argv);
+}
diff --git a/tests/mesa_pd/kernel/SingleCast.cpp b/tests/mesa_pd/kernel/SingleCast.cpp
index cf137df8c68077d14a6c18f5dd464823ef383223..4060635f700a8064e7e080f076b567dfba299a03 100644
--- a/tests/mesa_pd/kernel/SingleCast.cpp
+++ b/tests/mesa_pd/kernel/SingleCast.cpp
@@ -13,7 +13,7 @@
 //  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   SingleCast.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/SpherePile.cpp b/tests/mesa_pd/kernel/SpherePile.cpp
index 67e1087a749cb50e0c29f77b40ac197a5598f0a4..6d5184862c69446bbb0a4bc923d05dcfcbcbed20 100644
--- a/tests/mesa_pd/kernel/SpherePile.cpp
+++ b/tests/mesa_pd/kernel/SpherePile.cpp
@@ -13,7 +13,7 @@
 //  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   SpherePile.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -26,7 +26,7 @@
 #include "mesa_pd/data/ShapeStorage.h"
 
 #include "mesa_pd/kernel/DoubleCast.h"
-#include "mesa_pd/kernel/ExplicitEuler.h"
+#include "mesa_pd/kernel/SemiImplicitEuler.h"
 #include "mesa_pd/kernel/SpringDashpot.h"
 #include "mesa_pd/kernel/SpringDashpotSpring.h"
 #include "mesa_pd/mpi/ReduceContactHistory.h"
@@ -98,7 +98,7 @@ int main(int argc, char **argv)
          false);
 
    // explicit euler
-   kernel::ExplicitEuler explEuler(dt);
+   kernel::SemiImplicitEuler implEuler(dt);
    collision_detection::AnalyticContactDetection acd;
    kernel::DoubleCast double_cast;
    kernel::SpringDashpot sd(1);
@@ -147,7 +147,7 @@ int main(int argc, char **argv)
       ps->forEachParticle(false,
                     kernel::SelectLocal(),
                     *ac,
-                    explEuler,
+                    implEuler,
                     *ac);
    }
    WALBERLA_LOG_DEVEL_VAR(sp->getPosition());
@@ -193,7 +193,7 @@ int main(int argc, char **argv)
       ps->forEachParticle(false,
                     kernel::SelectLocal(),
                     *ac,
-                    explEuler,
+                    implEuler,
                     *ac);
    }
    WALBERLA_LOG_DEVEL_VAR(sp->getPosition());
diff --git a/tests/mesa_pd/kernel/SpringDashpot.cpp b/tests/mesa_pd/kernel/SpringDashpot.cpp
index b9feabe924b849106638efc62994f5a93d0758eb..1e90c9dd6d6083ebb9e2f39c82c2e4e2e4ec5672 100644
--- a/tests/mesa_pd/kernel/SpringDashpot.cpp
+++ b/tests/mesa_pd/kernel/SpringDashpot.cpp
@@ -13,7 +13,7 @@
 //  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   SpringDashpot.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/SyncGhostOwners.cpp b/tests/mesa_pd/kernel/SyncGhostOwners.cpp
index cf16223ee801d8202d34b27c2bf2b062e221f831..5c742f6f8b2a49751148a5d89f2498bc426efb08 100644
--- a/tests/mesa_pd/kernel/SyncGhostOwners.cpp
+++ b/tests/mesa_pd/kernel/SyncGhostOwners.cpp
@@ -13,7 +13,7 @@
 //  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   SyncGhostOwners.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/SyncGhostOwnersLarge.cpp b/tests/mesa_pd/kernel/SyncGhostOwnersLarge.cpp
index b04dd3c602f073d804f77c7d9e2e0d21e203ade2..2c66a4eba4437d42e2f370dfcedea3ae0fa24cdc 100644
--- a/tests/mesa_pd/kernel/SyncGhostOwnersLarge.cpp
+++ b/tests/mesa_pd/kernel/SyncGhostOwnersLarge.cpp
@@ -13,7 +13,7 @@
 //  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   SyncGhostOwnersLarge.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/SyncNextNeighbors.cpp b/tests/mesa_pd/kernel/SyncNextNeighbors.cpp
index c6de32e05bc9cd98ffe189614ec9c9b58735f1e6..3a4329b16b395b72d9f87babc51da067e7c9d8df 100644
--- a/tests/mesa_pd/kernel/SyncNextNeighbors.cpp
+++ b/tests/mesa_pd/kernel/SyncNextNeighbors.cpp
@@ -13,7 +13,7 @@
 //  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   SyncNextNeighbors.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/SyncNextNeighborsBlockForest.cpp b/tests/mesa_pd/kernel/SyncNextNeighborsBlockForest.cpp
index 6dfde66aa5e42a212d406c1798a119f55e40ae86..14ea54e85d63428700954688ca8ce3c454167e38 100644
--- a/tests/mesa_pd/kernel/SyncNextNeighborsBlockForest.cpp
+++ b/tests/mesa_pd/kernel/SyncNextNeighborsBlockForest.cpp
@@ -13,7 +13,7 @@
 //  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   SyncNextNeighborsBlockForest.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/TemperatureIntegration.cpp b/tests/mesa_pd/kernel/TemperatureIntegration.cpp
index b84fc9cd285400852b837084603c7185f2fbc9ac..47fd92072a8d7fd9ace91cf86b8cb7a11ab02b2c 100644
--- a/tests/mesa_pd/kernel/TemperatureIntegration.cpp
+++ b/tests/mesa_pd/kernel/TemperatureIntegration.cpp
@@ -13,7 +13,7 @@
 //  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   TemperatureIntegration.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/VelocityVerlet.cpp b/tests/mesa_pd/kernel/VelocityVerlet.cpp
index 1dba566fbcacfc4eb55fd2970acc1092e2ad9eed..d82f349c0c72a593685cf5ed226d04d59e87a984 100644
--- a/tests/mesa_pd/kernel/VelocityVerlet.cpp
+++ b/tests/mesa_pd/kernel/VelocityVerlet.cpp
@@ -13,7 +13,7 @@
 //  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   VelocityVerlet.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/kernel/cnt/AnisotropicVDWContact.test.cpp b/tests/mesa_pd/kernel/cnt/AnisotropicVDWContact.test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9fcde4d9a8389a5858e5cb99d9a193739a6b2d1d
--- /dev/null
+++ b/tests/mesa_pd/kernel/cnt/AnisotropicVDWContact.test.cpp
@@ -0,0 +1,135 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include "SphericalSegmentAccessor.h"
+
+#include "mesa_pd/data/Flags.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/kernel/ParticleSelector.h"
+#include "mesa_pd/kernel/cnt/AnisotropicVDWContact.h"
+#include "mesa_pd/kernel/cnt/ViscousDamping.h"
+#include "mesa_pd/kernel/cnt/Parameters.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
+#include "mesa_pd/vtk/ParticleVtkOutput.h"
+
+#include "core/Environment.h"
+#include "core/math/Constants.h"
+#include "vtk/VTKOutput.h"
+
+namespace walberla {
+using namespace walberla::mesa_pd;
+
+int main(int argc, char **argv)
+{
+   Environment env(argc, argv);
+   walberla::mpi::MPIManager::instance()->useWorldComm();
+
+   if (std::is_same<walberla::real_t, float>::value)
+   {
+      WALBERLA_LOG_WARNING("waLBerla build in sp mode: skipping test due to low precision");
+      return EXIT_SUCCESS;
+   }
+
+   logging::Logging::instance()->setStreamLogLevel(logging::Logging::INFO);
+   logging::Logging::instance()->setFileLogLevel(logging::Logging::INFO);
+
+   WALBERLA_LOG_INFO_ON_ROOT("loading configuration parameters");
+   constexpr auto numSimulationSteps = 20000ll;
+   constexpr auto outputInterval = 100ll;
+
+   WALBERLA_LOG_INFO_ON_ROOT("creating initial particle setup");
+   auto ps = std::make_shared<data::ParticleStorage>(10);
+   auto ac = SphericalSegmentAccessor(ps);
+
+   using namespace kernel::cnt;
+   data::Particle &&sp1 = *ps->create();
+   sp1.setPosition(Vec3(0_r, 0_r, 0_r));
+   sp1.setSegmentID(1);
+   sp1.setClusterID(1);
+
+   data::Particle &&sp2 = *ps->create();
+   sp2.setPosition(Vec3(20_r, 20_r, 20_r));
+   sp2.setSegmentID(2);
+   sp2.setClusterID(2);
+
+   WALBERLA_LOG_INFO_ON_ROOT("setting up VTK output");
+   auto vtkOutput       = make_shared<mesa_pd::vtk::ParticleVtkOutput>(ps);
+   vtkOutput->addOutput<data::SelectParticlePosition>("position");
+   auto vtkWriter       = walberla::vtk::createVTKOutput_PointData(vtkOutput,
+                                                                   "cnt",
+                                                                   1,
+                                                                   "vtk_integrated",
+                                                                   "particles",
+                                                                   false,
+                                                                   false);
+
+   WALBERLA_LOG_INFO_ON_ROOT("setting up interaction models");
+   kernel::cnt::AnisotropicVDWContact vdW_anisotropic;
+   kernel::cnt::ViscousDamping viscous_damping(0.1_r * 1052.0_r, 0.1_r * 1052.0_r);
+   kernel::VelocityVerletPreForceUpdate vv_pre(kernel::cnt::dT);
+   kernel::VelocityVerletPostForceUpdate vv_post(kernel::cnt::dT);
+
+   WALBERLA_LOG_INFO_ON_ROOT("running simulation");
+
+   real_t U = 0_r;
+   for (auto i = 0; i < numSimulationSteps; ++i)
+   {
+      ps->forEachParticle(false,
+                          kernel::SelectAll(),
+                          ac,
+                          vv_pre,
+                          ac);
+
+      U = 0_r;
+      ps->forEachParticlePairHalf(false,
+                                  kernel::SelectAll(),
+                                  ac,
+                                  [&](size_t p_idx1, size_t p_idx2)
+                                  {
+                                     vdW_anisotropic(p_idx1, p_idx2, ac);
+                                     U += vdW_anisotropic.getLastEnergy();
+                                     viscous_damping(p_idx1, p_idx2, ac);
+                                  });
+
+      ps->forEachParticle(false,
+                          kernel::SelectAll(),
+                          ac,
+                          vv_post,
+                          ac);
+
+      if( i % outputInterval == 0 )
+      {
+//         vtkWriter->write();
+//         WALBERLA_LOG_DEVEL(i << " : " << U);
+      }
+   }
+
+   WALBERLA_CHECK_LESS(U, -1.16_r)
+
+   return EXIT_SUCCESS;
+}
+} // namespace walberla
+
+int main(int argc, char *argv[])
+{
+   return walberla::main(argc, argv);
+}
diff --git a/tests/mesa_pd/kernel/cnt/IntegratedVDWContact.test.cpp b/tests/mesa_pd/kernel/cnt/IntegratedVDWContact.test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..05bb96c988d9785bce0b12de23ffb327bb762893
--- /dev/null
+++ b/tests/mesa_pd/kernel/cnt/IntegratedVDWContact.test.cpp
@@ -0,0 +1,135 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include "SphericalSegmentAccessor.h"
+
+#include "mesa_pd/data/Flags.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/kernel/ParticleSelector.h"
+#include "mesa_pd/kernel/cnt/IntegratedVDWContact.h"
+#include "mesa_pd/kernel/cnt/ViscousDamping.h"
+#include "mesa_pd/kernel/cnt/Parameters.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
+#include "mesa_pd/vtk/ParticleVtkOutput.h"
+
+#include "core/Environment.h"
+#include "core/math/Constants.h"
+#include "vtk/VTKOutput.h"
+
+namespace walberla {
+using namespace walberla::mesa_pd;
+
+int main(int argc, char **argv)
+{
+   Environment env(argc, argv);
+   walberla::mpi::MPIManager::instance()->useWorldComm();
+
+   if (std::is_same<walberla::real_t, float>::value)
+   {
+      WALBERLA_LOG_WARNING("waLBerla build in sp mode: skipping test due to low precision");
+      return EXIT_SUCCESS;
+   }
+
+   logging::Logging::instance()->setStreamLogLevel(logging::Logging::INFO);
+   logging::Logging::instance()->setFileLogLevel(logging::Logging::INFO);
+
+   WALBERLA_LOG_INFO_ON_ROOT("loading configuration parameters");
+   constexpr auto numSimulationSteps = 20000ll;
+   constexpr auto outputInterval = 100ll;
+
+   WALBERLA_LOG_INFO_ON_ROOT("creating initial particle setup");
+   auto ps = std::make_shared<data::ParticleStorage>(10);
+   auto ac = SphericalSegmentAccessor(ps);
+
+   using namespace kernel::cnt;
+   data::Particle &&sp1 = *ps->create();
+   sp1.setPosition(Vec3(0_r, 0_r, 0_r));
+   sp1.setSegmentID(1);
+   sp1.setClusterID(1);
+
+   data::Particle &&sp2 = *ps->create();
+   sp2.setPosition(Vec3(20_r, 20_r, 20_r));
+   sp2.setSegmentID(2);
+   sp2.setClusterID(2);
+
+   WALBERLA_LOG_INFO_ON_ROOT("setting up VTK output");
+   auto vtkOutput       = make_shared<mesa_pd::vtk::ParticleVtkOutput>(ps);
+   vtkOutput->addOutput<data::SelectParticlePosition>("position");
+   auto vtkWriter       = walberla::vtk::createVTKOutput_PointData(vtkOutput,
+                                                                   "cnt",
+                                                                   1,
+                                                                   "vtk_integrated",
+                                                                   "particles",
+                                                                   false,
+                                                                   false);
+
+   WALBERLA_LOG_INFO_ON_ROOT("setting up interaction models");
+   kernel::cnt::IntegratedVDWContact vdW_integrated;
+   kernel::cnt::ViscousDamping viscous_damping(0.1_r * 1052.0_r, 0.1_r * 1052.0_r);
+   kernel::VelocityVerletPreForceUpdate vv_pre(kernel::cnt::dT);
+   kernel::VelocityVerletPostForceUpdate vv_post(kernel::cnt::dT);
+
+   WALBERLA_LOG_INFO_ON_ROOT("running simulation");
+
+   real_t U = 0_r;
+   for (auto i = 0; i < numSimulationSteps; ++i)
+   {
+      ps->forEachParticle(false,
+                          kernel::SelectAll(),
+                          ac,
+                          vv_pre,
+                          ac);
+
+      U = 0_r;
+      ps->forEachParticlePairHalf(false,
+                                  kernel::SelectAll(),
+                                  ac,
+                                  [&](size_t p_idx1, size_t p_idx2)
+                                  {
+                                     vdW_integrated(p_idx1, p_idx2, ac);
+                                     U += vdW_integrated.getLastEnergy();
+                                     viscous_damping(p_idx1, p_idx2, ac);
+                                  });
+
+      ps->forEachParticle(false,
+                          kernel::SelectAll(),
+                          ac,
+                          vv_post,
+                          ac);
+
+      if( i % outputInterval == 0 )
+      {
+//         vtkWriter->write();
+//         WALBERLA_LOG_DEVEL(i << " : " << U);
+      }
+   }
+
+   WALBERLA_CHECK_LESS(U, -8_r)
+
+   return EXIT_SUCCESS;
+}
+} // namespace walberla
+
+int main(int argc, char *argv[])
+{
+   return walberla::main(argc, argv);
+}
diff --git a/tests/mesa_pd/kernel/cnt/IsotropicVDWContact.test.cpp b/tests/mesa_pd/kernel/cnt/IsotropicVDWContact.test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..60e4e6eca4450b02359ab963e61064243cd0c339
--- /dev/null
+++ b/tests/mesa_pd/kernel/cnt/IsotropicVDWContact.test.cpp
@@ -0,0 +1,87 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/data/ParticleAccessor.h>
+#include <mesa_pd/data/ParticleStorage.h>
+
+#include <mesa_pd/kernel/cnt/IsotropicVDWContact.h>
+
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+
+#include <iostream>
+
+namespace walberla {
+namespace mesa_pd {
+
+int main(int argc, char **argv)
+{
+   Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   mpi::MPIManager::instance()->useWorldComm();
+
+   if (std::is_same<real_t, float>::value)
+   {
+      WALBERLA_LOG_WARNING("waLBerla build in sp mode: skipping test due to low precision");
+      return EXIT_SUCCESS;
+   }
+
+   //init data structures
+   auto ps = std::make_shared<data::ParticleStorage>(2);
+   data::Particle &&p1 = *ps->create();
+   data::Particle &&p2 = *ps->create();
+
+   data::ParticleAccessor accessor(ps);
+
+   //init kernels
+   kernel::cnt::IsotropicVDWContact vdWContact;
+
+   auto calcForce = [&](const Vec3 pos1, const Vec3 pos2) {
+      p1.setPosition(pos1);
+      p2.setPosition(pos2);
+      clear(p1.getForceRef());
+      clear(p2.getForceRef());
+      vdWContact(0, 1, accessor);
+      return p1.getForce();
+   };
+
+   const Vec3 randomNormal = Vec3(1_r, 2_r, 3_r).getNormalized();
+
+   WALBERLA_LOG_INFO("checking repulsion - equilibrium - attraction");
+   vdWContact(0, 1, accessor);
+   WALBERLA_CHECK_LESS       (dot(randomNormal, calcForce(Vec3(0), randomNormal * (vdWContact.equilibriumDistance() - 1_r))),
+                              0_r);
+   WALBERLA_CHECK_FLOAT_EQUAL(dot(randomNormal, calcForce(Vec3(0), randomNormal * vdWContact.equilibriumDistance())),
+                              0_r);
+   WALBERLA_CHECK_GREATER    (dot(randomNormal, calcForce(Vec3(0), randomNormal * (vdWContact.equilibriumDistance() + 1_r))),
+                              0_r);
+
+   return EXIT_SUCCESS;
+}
+
+} //namespace mesa_pd
+} //namespace walberla
+
+int main(int argc, char **argv)
+{
+   return walberla::mesa_pd::main(argc, argv);
+}
diff --git a/src/mesa_pd/data/shape/ShapeTypes.cpp b/tests/mesa_pd/kernel/cnt/SphericalSegmentAccessor.h
similarity index 55%
rename from src/mesa_pd/data/shape/ShapeTypes.cpp
rename to tests/mesa_pd/kernel/cnt/SphericalSegmentAccessor.h
index d33273b37a30d7fcfb940bffa4e6f21b1cb4fe79..22c09b56486a3d4c6b9332b0b56c0a63bb66fdea 100644
--- a/src/mesa_pd/data/shape/ShapeTypes.cpp
+++ b/tests/mesa_pd/kernel/cnt/SphericalSegmentAccessor.h
@@ -13,27 +13,36 @@
 //  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 ShapeTypes.cpp
+//! \file
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
 
-#include <mesa_pd/data/shape/Box.h>
-#include <mesa_pd/data/shape/CylindricalBoundary.h>
-#include <mesa_pd/data/shape/HalfSpace.h>
-#include <mesa_pd/data/shape/Ellipsoid.h>
-#include <mesa_pd/data/shape/Sphere.h>
+#pragma once
+
+#include "mesa_pd/data/ParticleAccessor.h"
+#include "mesa_pd/kernel/cnt/Parameters.h"
 
 namespace walberla {
 namespace mesa_pd {
-namespace data {
 
-const int Box::SHAPE_TYPE                ;
-const int CylindricalBoundary::SHAPE_TYPE;
-const int HalfSpace::SHAPE_TYPE          ;
-const int Ellipsoid::SHAPE_TYPE          ;
-const int Sphere::SHAPE_TYPE             ;
+class SphericalSegmentAccessor : public data::ParticleAccessor
+{
+public:
+   SphericalSegmentAccessor(std::shared_ptr<data::ParticleStorage>& ps)
+   : ParticleAccessor(ps)
+   {}
+
+   constexpr auto getInvMass(const size_t /*p_idx*/) const {return 1_r / kernel::cnt::mass_T;}
+
+   constexpr auto& getInvInertiaBF(const size_t /*p_idx*/) const {return invI;}
+private:
+   //  - sphere   :  I = (2/5)*mass*radius^2
+   static constexpr auto Ia = 0.4_r * kernel::cnt::mass_T * kernel::cnt::inner_radius * kernel::cnt::inner_radius;
+   static constexpr auto invI = Mat3(1_r/Ia, 0_r, 0_r, 0_r, 1_r/Ia, 0_r, 0_r, 0_r, 1_r/Ia);
+};
 
-} //namespace data
 } //namespace mesa_pd
-} //namespace walberla
+} //namespace walberla
\ No newline at end of file
diff --git a/tests/mesa_pd/kernel/cnt/VBondContact.test.cpp b/tests/mesa_pd/kernel/cnt/VBondContact.test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f0f945883145a8a3fe6c9dd47ea21ed6e81c7ac7
--- /dev/null
+++ b/tests/mesa_pd/kernel/cnt/VBondContact.test.cpp
@@ -0,0 +1,97 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/data/ParticleAccessor.h>
+#include <mesa_pd/data/ParticleStorage.h>
+
+#include <mesa_pd/kernel/cnt/VBondContact.h>
+
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+
+#include <iostream>
+
+namespace walberla {
+namespace mesa_pd {
+
+int main( int argc, char ** argv )
+{
+   Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   mpi::MPIManager::instance()->useWorldComm();
+
+   if (std::is_same<real_t, float>::value)
+   {
+      WALBERLA_LOG_WARNING("waLBerla build in sp mode: skipping test due to low precision");
+      return EXIT_SUCCESS;
+   }
+
+   using Interaction = kernel::cnt::VBondContact;
+
+   //init data structures
+   auto ps = std::make_shared<data::ParticleStorage>(100);
+
+   data::Particle&& p1        = *ps->create();
+   p1.getPositionRef()        = Vec3(0,0,0);
+   p1.getForceRef()           = Vec3(0,0,0);
+   p1.getTypeRef()            = 0;
+
+   data::Particle&& p2        = *ps->create();
+   p2.getPositionRef()        = Vec3(Interaction::a,0,0);
+   p2.getForceRef()           = Vec3(0,0,0);
+   p2.getTypeRef()            = 0;
+
+   data::ParticleAccessor accessor(ps);
+
+   //init kernels
+   Interaction vbond;
+
+   auto calcForce = [&](const Vec3 pos1, const Vec3 pos2) {
+      p1.setPosition(pos1);
+      p2.setPosition(pos2);
+      clear(p1.getForceRef());
+      clear(p2.getForceRef());
+      vbond(0, 1, accessor);
+      WALBERLA_CHECK_FLOAT_EQUAL(p1.getForce(), -p2.getForce());
+      return p1.getForce();
+   };
+
+   const Vec3 randomNormal = Vec3(1_r, 2_r, 3_r).getNormalized();
+
+   WALBERLA_LOG_INFO("checking repulsion - equilibrium - attraction");
+   WALBERLA_CHECK_LESS       (dot(randomNormal, calcForce(Vec3(0), randomNormal * (Interaction::a - 1_r))),
+                              0_r);
+   WALBERLA_CHECK_FLOAT_EQUAL(dot(randomNormal, calcForce(Vec3(0), randomNormal * Interaction::a)),
+                              0_r);
+   WALBERLA_CHECK_GREATER    (dot(randomNormal, calcForce(Vec3(0), randomNormal * (Interaction::a + 1_r))),
+                              0_r);
+
+   return EXIT_SUCCESS;
+}
+
+} //namespace mesa_pd
+} //namespace walberla
+
+int main( int argc, char ** argv )
+{
+   return walberla::mesa_pd::main(argc, argv);
+}
diff --git a/tests/mesa_pd/kernel/cnt/VBondContactEnergies.ref.txt b/tests/mesa_pd/kernel/cnt/VBondContactEnergies.ref.txt
new file mode 100644
index 0000000000000000000000000000000000000000..7608c5ea92ae8eefd6d70197bcf5c0defe6f9cbe
--- /dev/null
+++ b/tests/mesa_pd/kernel/cnt/VBondContactEnergies.ref.txt
@@ -0,0 +1,4 @@
+0.000000000000000000e+00 9.769598930999999488e-25 1.257361055000000031e-14 3.899420607000000291e-15 2.000811218999999922e-12 4.690882740999999754e-11 3.101953598000000151e-10 1.079764155999999945e-09 2.624524298999999930e-09 5.413337421000000074e-09 1.084774037999999922e-08 2.202161062999999972e-08 4.378177583000000040e-08 8.112557732999999511e-08 1.362031657999999954e-07 2.072490092999999965e-07 2.924839492000000040e-07 3.965729058000000265e-07 5.325625850000000057e-07 7.156604478000000376e-07 9.545334436000000305e-07 1.248477076000000054e-06 1.591471921999999927e-06 1.978461101000000019e-06 2.412201610999999981e-06 2.908983651000000108e-06 3.493892384000000026e-06 4.183487506000000047e-06 4.973947920999999592e-06 5.849722148000000380e-06 6.801382250000000419e-06 7.833933107000000422e-06 8.966690885999999394e-06 1.023102611999999984e-05 1.165832549999999958e-05 1.325937714000000014e-05 1.501796961000000055e-05 1.691057721000000048e-05 1.893294801000000075e-05 2.111153134000000168e-05 2.349887435999999891e-05 2.615664121000000147e-05 2.912830002999999975e-05 3.242019864000000189e-05 3.601281649999999693e-05 3.989370787000000139e-05 4.408250137000000325e-05 4.863492345000000269e-05 5.363050996999999876e-05 5.914455918000000094e-05 6.521380048999999711e-05 7.182577398000000192e-05 7.894784417999999374e-05 8.657194118999999579e-05 9.474034216000000376e-05 1.035425176999999944e-04 1.130884250999999996e-04 1.234655437999999960e-04 1.347034647999999896e-04 1.467775908999999993e-04 1.596532001999999945e-04 1.733353551999999940e-04 1.878913344999999990e-04 2.034373955999999939e-04 2.200960170999999928e-04 2.379396507000000128e-04 2.569529171999999939e-04 2.770438951999999950e-04 2.981047208000000268e-04 3.200826908000000098e-04 3.430091486000000031e-04 3.669610926000000002e-04 3.919788862999999918e-04 4.179935286000000015e-04 4.448076550999999885e-04 4.721433329999999797e-04 4.997362688000000271e-04 5.274214652999999501e-04 5.551469528999999686e-04 5.828994754999999471e-04 6.105934510000000275e-04 6.379969888000000446e-04 6.647387717000000215e-04 6.903969353999999921e-04 7.146251129999999646e-04 7.372316414000000366e-04 7.581517388999999820e-04 7.773393340000000129e-04 7.946597574999999776e-04 8.098415648999999876e-04 8.225099688999999671e-04 8.323031783000000390e-04 8.390153918000000256e-04 8.426572231999999519e-04 8.433837805999999616e-04 8.413581837000000396e-04 8.366439647999999616e-04 8.291691587000000162e-04 8.187843143000000067e-04 8.054083557000000235e-04 7.891649440999999685e-04 7.703861584000000051e-04 7.494857218999999944e-04 7.268169406000000434e-04 7.025969469000000259e-04 6.769090151999999569e-04 6.497862570999999969e-04 6.213484284000000187e-04 5.918880394000000222e-04 5.618225952999999573e-04 5.315605759000000376e-04 5.013941135000000385e-04 4.714720982999999910e-04 4.418471189000000208e-04 4.125752137999999882e-04 3.838115558999999843e-04 3.558203543999999795e-04 3.288904724999999860e-04 3.032409837000000141e-04 2.789843069000000125e-04 2.561431809999999989e-04 2.347022120999999882e-04 2.146753759000000108e-04 1.961349607000000103e-04 1.791631984999999985e-04 1.637810812000000077e-04 1.499321497999999873e-04 1.375142358000000075e-04 1.264146304000000095e-04 1.165474269999999953e-04 1.078817780000000009e-04 1.004080292999999990e-04 9.405698425000000621e-05 8.867629388999999883e-05 8.409528164999999439e-05 8.019052618000000550e-05 7.689793735999999604e-05 7.420063447999999365e-05 7.209858209999999682e-05 7.054539821000000484e-05 6.941233475999999936e-05 6.854816966000000598e-05 6.788190945999999463e-05 6.746265518000000086e-05 6.742435539000000467e-05 6.792882366000000413e-05 6.911280103999999536e-05 7.106280527999999546e-05 7.386126210000000481e-05 7.768045076000000624e-05 8.282488939999999673e-05 8.968387272000000133e-05 9.866557706999999860e-05 1.101751333999999983e-04 1.246224312999999874e-04 1.424354752000000102e-04 1.640771495999999985e-04 1.900400481999999977e-04 2.207908629999999993e-04 2.567062459999999843e-04 2.980796680999999956e-04 3.451986834000000124e-04 3.984024905999999813e-04 4.580602211000000049e-04 5.244992345000000326e-04 5.979342737999999774e-04 6.784228293999999913e-04 7.658751436999999872e-04 8.601395017000000110e-04 9.611151113000000485e-04 1.068802304000000076e-03 1.183259439000000071e-03 1.304513818999999963e-03 1.432478119000000008e-03 1.566901386000000077e-03 1.707389487999999914e-03 1.853512819000000065e-03 2.004949989000000179e-03 2.161573521000000053e-03 2.323421534000000028e-03 2.490571904999999871e-03 2.662987130999999816e-03 2.840412905999999908e-03 3.022385029000000146e-03 3.208345773999999977e-03 3.397824131000000154e-03 3.590587542000000121e-03 3.786644539000000049e-03 3.986055001999999625e-03 4.188667612999999804e-03 4.393968871000000255e-03 4.601132616999999668e-03 4.809252398000000116e-03 5.017672239000000277e-03 5.226229318999999797e-03 5.435201659000000150e-03 5.644973555000000129e-03 5.855671629000000315e-03 6.066983778000000112e-03 6.278207814999999838e-03 6.488535436999999691e-03 6.697484469999999933e-03 6.905155137999999942e-03 7.112019814999999585e-03 7.318401837999999673e-03 7.524071957999999873e-03 7.728169687999999864e-03 7.929413899999999613e-03 8.126558282999999175e-03 8.318907130000000733e-03 8.506475640000000366e-03 8.689608087999999356e-03 8.868399936999999192e-03 9.042341763999999296e-03 9.210268918999999985e-03 9.370627918000000098e-03 9.522098059999999434e-03 9.664223426000000464e-03 9.797454865999999721e-03 9.922553097000000669e-03 1.003994249999999935e-02 1.014942559999999945e-02 1.025024497000000083e-02 1.034153013000000017e-02 1.042309114000000009e-02 1.049596581000000056e-02 1.056211724000000012e-02 1.062355786999999961e-02 1.068164414000000000e-02 1.073680678000000013e-02 1.078867998000000022e-02 1.083676226000000048e-02 1.088137529999999922e-02 1.092404083999999956e-02 1.096684222999999916e-02 1.101144853999999965e-02 1.105856929000000044e-02 1.110789606999999964e-02 1.115848357999999971e-02 1.120966122000000009e-02 1.126193665000000017e-02 1.131693991999999961e-02 1.137640707000000043e-02 1.144118782000000042e-02 1.151085137000000015e-02 1.158378019999999932e-02 1.165780565000000026e-02 1.173133908000000003e-02 1.180410112000000039e-02 1.187664239000000080e-02 1.194922755000000017e-02 1.202115758000000048e-02 1.209068639000000084e-02 1.215530664999999941e-02 1.221261438000000032e-02 1.226148720000000067e-02 1.230240005999999955e-02 1.233643390999999978e-02 1.236406902000000002e-02 1.238471908999999940e-02 1.239682075000000071e-02 1.239830167999999948e-02 1.238760952000000047e-02 1.236466165000000075e-02 1.233062359999999996e-02 1.228676870999999976e-02 1.223368277000000046e-02 1.217115801000000046e-02 1.209833601000000029e-02 1.201425635999999950e-02 1.191894391000000025e-02 1.181401506999999948e-02 1.170196878000000044e-02 1.158499904000000018e-02 1.146447159000000063e-02 1.134094681999999951e-02 1.121438741000000024e-02 1.108486562000000029e-02 1.095353758999999955e-02 1.082275411999999992e-02 1.069510960000000059e-02 1.057257600000000020e-02 1.045629944000000047e-02 1.034660192999999929e-02 1.024321331000000064e-02 1.014615722000000025e-02 1.005662126000000066e-02 9.976704293000000431e-03 9.908379862999999732e-03 9.852853618000000091e-03 9.810482602999999469e-03 9.780845563999999298e-03 9.763234384999999738e-03 9.757677556000000571e-03 9.765495532000000381e-03 9.788700317000000040e-03 9.829028404000000191e-03 9.887467607999999783e-03 9.964096013000000793e-03 1.005812730999999967e-02 1.016862276999999995e-02 1.029552536999999920e-02 1.043980951999999920e-02 1.060257203000000009e-02 1.078416719000000086e-02 1.098393832999999993e-02 1.120021409999999960e-02 1.143066476000000053e-02 1.167331096999999970e-02 1.192741718999999929e-02 1.219324616000000069e-02 1.247104618000000054e-02 1.276032289999999916e-02 1.305954806999999954e-02 1.336609579999999999e-02 1.367680658999999993e-02 1.398917602000000066e-02 1.430201648000000055e-02 1.461487914999999929e-02 1.492704513999999934e-02 1.523697599999999930e-02 1.554212664999999986e-02 1.583906532999999894e-02 1.612431426000000140e-02 1.639555138000000148e-02 1.665198710000000137e-02 1.689359329000000159e-02 1.712005676999999934e-02 1.733008886999999956e-02 1.752113310999999993e-02 1.768976662999999921e-02 1.783297475999999840e-02 1.794938402000000044e-02 1.803928897999999836e-02 1.810360151999999834e-02 1.814272402000000062e-02 1.815585927999999877e-02 1.814087203999999870e-02 1.809502045999999975e-02 1.801640255000000121e-02 1.790495707000000145e-02 1.776208275000000156e-02 1.758932824000000034e-02 1.738722163000000140e-02 1.715478230000000090e-02 1.688989051000000172e-02 1.659057280000000162e-02 1.625655717999999889e-02 1.588985876000000019e-02 1.549393919999999918e-02 1.507232015000000043e-02 1.462770042000000048e-02 1.416184898999999983e-02 1.367614416999999964e-02 1.317254150999999998e-02 1.265431285999999919e-02 1.212576974000000030e-02 1.159112269999999952e-02 1.105356524000000035e-02 1.051527447999999962e-02 9.977995902000000653e-03 9.443634378000000454e-03 8.914602941000000780e-03 8.393732972000000556e-03 7.883681687000000313e-03 7.386401348000000248e-03 6.903381672000000017e-03 6.436435528000000392e-03 5.988091241999999974e-03 5.561236193000000314e-03 5.158451686999999952e-03 4.781424777999999758e-03 4.430641622000000036e-03 4.105757944999999937e-03 3.806678989999999919e-03 3.534430675000000202e-03 3.290896903000000047e-03 3.077676196000000194e-03 2.894994097000000172e-03 2.741199906000000002e-03 2.613023865999999917e-03 2.506694246000000167e-03 2.419380144999999909e-03 2.349802287999999944e-03 2.297528666000000011e-03 2.261746045000000063e-03 2.240486695000000125e-03 2.230626851999999941e-03 2.228696117000000029e-03 2.232364377999999927e-03 2.241767958000000054e-03 2.259581146999999936e-03 2.289768263000000002e-03 2.336031652000000128e-03 2.400864114999999924e-03 2.485558073999999829e-03 2.591273702000000071e-03 2.720667758999999967e-03 2.878748034999999844e-03 3.072062817000000175e-03 3.306969062000000067e-03 3.588377315999999838e-03 3.919493427000000051e-03 4.302413633999999792e-03 4.739399045999999859e-03 5.234083926000000254e-03 5.791368725000000002e-03 6.415893377999999994e-03 7.110570699000000328e-03 7.876300435999999267e-03 8.712535723999999260e-03 9.618130162999999691e-03 1.059225001999999916e-02 1.163467510000000017e-02 1.274485958999999925e-02 1.392065854000000026e-02 1.515827135000000041e-02 1.645334082999999961e-02 1.780192783999999875e-02 1.920087721999999900e-02 2.064771435999999960e-02 2.213976700999999853e-02 2.367278429999999934e-02 2.524065719999999888e-02 2.683700945999999837e-02 2.845706488000000062e-02 3.009799229000000020e-02 3.175778475999999767e-02 3.343347700000000311e-02 3.511923727999999995e-02 3.680554146000000054e-02 3.848077602999999680e-02 4.013435226999999966e-02 4.175853368000000121e-02 4.334763560999999737e-02 4.489568946999999977e-02 4.639404914999999824e-02 4.783006241999999963e-02 4.918814719999999724e-02 5.045343716000000311e-02 5.161548377999999881e-02 5.266904822000000042e-02 5.361181120999999883e-02 5.444109255000000036e-02 5.515142979999999862e-02 5.573415335000000276e-02 5.617959186000000271e-02 5.648076586999999787e-02 5.663564170000000286e-02 5.664618837999999684e-02 5.651546579999999764e-02 5.624499641999999855e-02 5.583358832000000216e-02 5.527807033999999897e-02 5.457594631999999890e-02 5.372848905000000147e-02 5.274196005000000159e-02 5.162633611999999789e-02 5.039306847000000339e-02 4.905337064999999719e-02 4.761749207000000317e-02 4.609525795000000287e-02 4.449783776999999968e-02 4.283942745000000191e-02 4.113724154000000105e-02 3.940985358000000105e-02 3.767521521000000151e-02 3.594921043999999705e-02 3.424496932999999965e-02 3.257329569999999758e-02 3.094411949999999883e-02 2.936776412000000058e-02 2.785489245000000169e-02 2.641541680000000086e-02 2.505739411999999972e-02 2.378641927999999850e-02 2.260566114000000029e-02 2.151671485000000023e-02 2.052090223999999963e-02 1.961998363000000065e-02 1.881577829000000146e-02 1.810937452999999975e-02 1.750073615000000138e-02 1.698868438000000064e-02 1.657094543999999836e-02 1.624429527000000040e-02 1.600477163999999841e-02 1.584765619000000000e-02 1.576731689000000075e-02 1.575743360999999967e-02 1.581158225999999833e-02 1.592345733999999888e-02 1.608646433000000125e-02 1.629325535000000075e-02 1.653566748999999933e-02 1.680503951999999870e-02 1.709302124999999978e-02 1.739293466000000135e-02 1.770082757000000090e-02 1.801506891000000110e-02
+0.000000000000000000e+00 1.098045087999999967e-11 2.813681244999999975e-05 1.090188794999999945e-05 4.169281264000000080e-04 1.251764690000000107e-03 1.523427812999999945e-03 8.724563367999999680e-04 1.862166737999999876e-04 2.139689969000000043e-05 4.885859669000000056e-05 3.010388863000000256e-04 1.166116673999999936e-03 2.063944842999999949e-03 1.936229357999999902e-03 9.866180774999999941e-04 3.113903725999999923e-04 2.095340148000000011e-04 3.900488171000000099e-04 1.081855028999999940e-03 2.334334598000000188e-03 3.102649195999999943e-03 2.555769900000000143e-03 1.407288505999999958e-03 7.685405318000000435e-04 7.862179179999999887e-04 1.313863788000000071e-03 2.501508352999999834e-03 3.841578630000000091e-03 4.112757584999999702e-03 3.065362944999999988e-03 1.844464178000000025e-03 1.345228863999999979e-03 1.518534461000000096e-03 2.328443189000000031e-03 3.776796619999999924e-03 4.980570515999999809e-03 4.828829556999999703e-03 3.566178691999999980e-03 2.445262806999999941e-03 2.125824189999999791e-03 2.555812025000000088e-03 3.736235042999999905e-03 5.315531947999999757e-03 6.142351960999999951e-03 5.471705153000000141e-03 4.026527172999999973e-03 3.003180169000000017e-03 2.814801066000000192e-03 3.424525675000000217e-03 4.789582199000000200e-03 6.257026718999999897e-03 6.629351478999999728e-03 5.562887592000000103e-03 4.051922910000000169e-03 3.167537500999999950e-03 3.210545122999999921e-03 4.157046682999999618e-03 5.768405326999999881e-03 7.124078447000000217e-03 7.187952340999999995e-03 6.066060721000000010e-03 4.891911774999999594e-03 4.537224692000000129e-03 5.171990106999999580e-03 6.697878200000000060e-03 8.637042634999999788e-03 9.912537683999999780e-03 9.735341915999999596e-03 8.580728685000000439e-03 7.636438120000000121e-03 7.605032599000000060e-03 8.541060304000000145e-03 1.023322527999999919e-02 1.204787086999999958e-02 1.296710704999999922e-02 1.254143586000000012e-02 1.147406670999999954e-02 1.082485428999999978e-02 1.107738913000000033e-02 1.222173638999999971e-02 1.400682674999999960e-02 1.569740781999999849e-02 1.630586435999999834e-02 1.559380404999999997e-02 1.439193249000000029e-02 1.368881985000000058e-02 1.390419310999999915e-02 1.505856839000000065e-02 1.684837759000000143e-02 1.838536152000000076e-02 1.866772138999999928e-02 1.763167204000000085e-02 1.623979785000000120e-02 1.548684428999999932e-02 1.580225837999999980e-02 1.717242294000000113e-02 1.904884664999999949e-02 2.026330632000000048e-02 1.988493926999999883e-02 1.824582598000000153e-02 1.660877230000000135e-02 1.597302801000000105e-02 1.660384716000000080e-02 1.821871497000000090e-02 1.994014598000000069e-02 2.051084824000000070e-02 1.936525026000000121e-02 1.732504483000000053e-02 1.578945635000000153e-02 1.554882687000000083e-02 1.659365755000000164e-02 1.836679047999999898e-02 1.977005157999999971e-02 1.966549101999999841e-02 1.799557158000000129e-02 1.599993626000000002e-02 1.502037837999999938e-02 1.549817717000000032e-02 1.708752831999999985e-02 1.895811463999999905e-02 1.991527552999999839e-02 1.912430446999999895e-02 1.704456474000000041e-02 1.511228327999999954e-02 1.441806024999999977e-02 1.508497429000000070e-02 1.659711305000000081e-02 1.801958886999999870e-02 1.820636494000000064e-02 1.668101569999999936e-02 1.431009458999999914e-02 1.257222069999999921e-02 1.228618635999999958e-02 1.331381872999999952e-02 1.495475842000000034e-02 1.614320476000000171e-02 1.585241533000000119e-02 1.400626471000000067e-02 1.173601720000000057e-02 1.036107860999999943e-02 1.038674728999999970e-02 1.153037393999999972e-02 1.305986987000000085e-02 1.391537531000000029e-02 1.325078053000000068e-02 1.129482348999999962e-02 9.279844114999999741e-03 8.329654203999999756e-03 8.713958784999999846e-03 1.001731286000000047e-02 1.142824331999999984e-02 1.195428441000000001e-02 1.106497422000000085e-02 9.278476290999999182e-03 7.730237216000000050e-03 7.183857695000000348e-03 7.696862898000000376e-03 8.897470693000000472e-03 1.010542709000000067e-02 1.048124689000000040e-02 9.648590824999999163e-03 8.125785788000000273e-03 6.844495477999999744e-03 6.385196228000000057e-03 6.807871963000000116e-03 7.814717755000000557e-03 8.770060995999999151e-03 8.927271413999999725e-03 8.036764202000000121e-03 6.605048020999999998e-03 5.412051182999999890e-03 4.961157029999999774e-03 5.375675463999999779e-03 6.400032251000000241e-03 7.351659233000000342e-03 7.459640772000000405e-03 6.523098909000000015e-03 5.102333577000000425e-03 4.004419278999999700e-03 3.730894763000000211e-03 4.323699311999999913e-03 5.385101278000000401e-03 6.190949137000000164e-03 6.120221093000000404e-03 5.175369548999999951e-03 3.989793152999999981e-03 3.292571785000000112e-03 3.440746584000000004e-03 4.338893800000000189e-03 5.527947033000000371e-03 6.349665053999999781e-03 6.335613914000000173e-03 5.613985610000000320e-03 4.831529428000000230e-03 4.609486022000000224e-03 5.145515512999999930e-03 6.254976078000000321e-03 7.526838073999999809e-03 8.418941035000000392e-03 8.537631037000000672e-03 8.021649913000000742e-03 7.485492348000000185e-03 7.479211323999999939e-03 8.137342089000000611e-03 9.295454555000000688e-03 1.061315349999999991e-02 1.156819794000000026e-02 1.175118583999999917e-02 1.132383592000000064e-02 1.090947742999999991e-02 1.099061590000000033e-02 1.167474427000000058e-02 1.289243823999999990e-02 1.434386665000000026e-02 1.536176384000000061e-02 1.540473216999999929e-02 1.470838762000000084e-02 1.404344775000000017e-02 1.395747051000000016e-02 1.460560932999999985e-02 1.594009966999999917e-02 1.750513845999999984e-02 1.841935299999999942e-02 1.820005951999999899e-02 1.735958567000000022e-02 1.677895532000000037e-02 1.690258948000000067e-02 1.781730875000000006e-02 1.939444425000000014e-02 2.092484060999999854e-02 2.138203301999999986e-02 2.055932695999999893e-02 1.935148814999999856e-02 1.868018742000000093e-02 1.883046571000000086e-02 1.981195406000000117e-02 2.135862981000000160e-02 2.252370723999999949e-02 2.234093654999999914e-02 2.101662589999999872e-02 1.965624926000000133e-02 1.899716741000000153e-02 1.919034761999999922e-02 2.026346868000000051e-02 2.173851775999999930e-02 2.235602730000000107e-02 2.129760154999999974e-02 1.925890456000000153e-02 1.755590345000000024e-02 1.685837287999999989e-02 1.734000310999999836e-02 1.892921125999999857e-02 2.066988964000000123e-02 2.100959851999999878e-02 1.952545333999999855e-02 1.745251982000000160e-02 1.615768873000000133e-02 1.613806327999999998e-02 1.746475361999999878e-02 1.971777808000000146e-02 2.143443069999999978e-02 2.115688888999999948e-02 1.918617859999999925e-02 1.717451353999999847e-02 1.627954375000000009e-02 1.676609708000000032e-02 1.859569298999999912e-02 2.099558521000000066e-02 2.224083687999999989e-02 2.131215965999999976e-02 1.918918324000000106e-02 1.757820217000000129e-02 1.727032460999999852e-02 1.836830965000000107e-02 2.067597838000000132e-02 2.302525542000000147e-02 2.367539620000000053e-02 2.226253708999999942e-02 2.023965366999999974e-02 1.906296684000000075e-02 1.918643327000000079e-02 2.069907250999999948e-02 2.324047917999999893e-02 2.531067571000000099e-02 2.536759566999999965e-02 2.369515466999999945e-02 2.195629291999999858e-02 2.129226925999999978e-02 2.197881276000000078e-02 2.403205137999999894e-02 2.670580893999999900e-02 2.825365897000000140e-02 2.761692653999999955e-02 2.570521064000000008e-02 2.413747488999999982e-02 2.369226563000000063e-02 2.457055601999999853e-02 2.670524756999999999e-02 2.900604555000000154e-02 2.971543993000000036e-02 2.834332566000000164e-02 2.620686515999999869e-02 2.478243360000000062e-02 2.467662685000000106e-02 2.606119738999999880e-02 2.853287011000000054e-02 3.054048034999999953e-02 3.048783757999999969e-02 2.854593635999999948e-02 2.634156325000000173e-02 2.518380886000000027e-02 2.553799740000000165e-02 2.742574792000000011e-02 3.002675435000000045e-02 3.156011447999999830e-02 3.086550331000000036e-02 2.871578835000000024e-02 2.682638661999999993e-02 2.627104593000000043e-02 2.735563557999999909e-02 2.985267158000000018e-02 3.255671654000000248e-02 3.368562483999999746e-02 3.260004108999999678e-02 3.046505342999999977e-02 2.888656527000000099e-02 2.872135418999999940e-02 3.020524009999999912e-02 3.294390619999999770e-02 3.550070158000000198e-02 3.620675079999999962e-02 3.483208175000000045e-02 3.274427012000000192e-02 3.145377608000000241e-02 3.174348653999999742e-02 3.377649058999999954e-02 3.684776314999999719e-02 3.921267960000000136e-02 3.934740502999999751e-02 3.748318199000000295e-02 3.525845958999999918e-02 3.416759400000000252e-02 3.489814589999999661e-02 3.739553431999999983e-02 4.055388218000000045e-02 4.241686413000000239e-02 4.178222420000000242e-02 3.943135575000000087e-02 3.725031213999999841e-02 3.663120758999999921e-02 3.802921089999999893e-02 4.103495939000000037e-02 4.414328002000000167e-02 4.537400546999999923e-02 4.403746134000000034e-02 4.144459293999999683e-02 3.953507633000000188e-02 3.945078286999999934e-02 4.141465191999999657e-02 4.474581605999999878e-02 4.765077491999999970e-02 4.823564068999999788e-02 4.634610144000000237e-02 4.372950873999999905e-02 4.230135992999999733e-02 4.296592879000000337e-02 4.569302948000000114e-02 4.943655193000000281e-02 5.214975843999999916e-02 5.224664417000000199e-02 5.021736479999999725e-02 4.805112389999999928e-02 4.738447994000000163e-02 4.883466990000000230e-02 5.220264517999999992e-02 5.616499422000000019e-02 5.853909537000000218e-02 5.814050307000000195e-02 5.604309186999999887e-02 5.437324896000000213e-02 5.453920028999999919e-02 5.693871295999999860e-02 6.106411720999999765e-02 6.517070929000000346e-02 6.708689303999999798e-02 6.624436858000000428e-02 6.419995925999999464e-02 6.294107760000000606e-02 6.357073134000000569e-02 6.636902606000000260e-02 7.059402475999999871e-02 7.416614434000000367e-02 7.506358058999999805e-02 7.336223947999999384e-02 7.103757296000000332e-02 7.001715288000000259e-02 7.122601517000000382e-02 7.466185828000000690e-02 7.903714705000000618e-02 8.197763847000000270e-02 8.195389224000000639e-02 7.978274693999999612e-02 7.759072546999999653e-02 7.701304049000000373e-02 7.877744014000000183e-02 8.265841438000000319e-02 8.695718198999999315e-02 8.922108609999999940e-02 8.847277197000000259e-02 8.610597665999999983e-02 8.434468004999999546e-02 8.469502516999999342e-02 8.768319772999999540e-02 9.256396228999999587e-02 9.707931071000000522e-02 9.890680390000000655e-02 9.780079123000000318e-02 9.563030992000000341e-02 9.456857476999999679e-02 9.595472397000000264e-02 1.000612302999999981e-01 1.055922863999999989e-01 1.098871111999999983e-01 1.109595693999999994e-01 1.093151983999999938e-01 1.072503603999999944e-01 1.069366499999999942e-01 1.094850529000000044e-01 1.146157857999999946e-01 1.203232783999999972e-01 1.238128750999999972e-01 1.237864961000000041e-01 1.215057940000000003e-01 1.194530783000000013e-01 1.196059594000000031e-01 1.227585964999999973e-01 1.281164261999999943e-01 1.332265281999999995e-01 1.354948709000000084e-01 1.343891641000000137e-01 1.317553599000000075e-01 1.301257403999999951e-01 1.312192595999999989e-01 1.353715399000000041e-01 1.411820397999999921e-01 1.459246818000000112e-01 1.474503628999999927e-01 1.459470861999999924e-01 1.435610048000000027e-01 1.426689959999999924e-01 1.446930674999999944e-01 1.495596228999999944e-01 1.554236328999999972e-01 1.595303432000000077e-01 1.602909436999999937e-01 1.585256815999999958e-01 1.565866635000000007e-01 1.566915185999999960e-01 1.599191171000000078e-01 1.656968162000000133e-01 1.717846372000000066e-01 1.755503327000000058e-01 1.759471824000000129e-01 1.742015671999999904e-01 1.726916896000000035e-01 1.734601668999999902e-01 1.772769128999999999e-01 1.831133191000000049e-01 1.884492271000000052e-01 1.909162681999999889e-01 1.900980771000000125e-01 1.877147532000000008e-01 1.862775906999999953e-01 1.876377506999999945e-01 1.920841523000000051e-01 1.980314081000000004e-01 2.027781117999999938e-01 2.043181491000000127e-01 2.027646596999999884e-01 2.001122127999999945e-01 1.988157517999999957e-01 2.003950733000000040e-01 2.045964194000000069e-01 2.093754133000000073e-01 2.120625872999999939e-01 2.112029098000000049e-01 2.075790237000000038e-01 2.035907939999999916e-01 2.016904861000000104e-01 2.030011084999999993e-01 2.066936591999999906e-01 2.103853867999999905e-01 2.115652101000000118e-01 2.092677652999999915e-01 2.047355029000000048e-01 2.005599788999999911e-01 1.990186617999999963e-01 2.007246850999999943e-01 2.042720507999999935e-01
+0.000000000000000000e+00 0.000000000000000000e+00 1.830075147000000035e-13 2.271339206999999938e-07 3.697562520999999752e-07 1.343033221999999912e-06 3.547050058999999842e-05 1.861336611999999996e-04 5.159842077000000330e-04 9.728372257999999507e-04 1.435700584000000092e-03 1.870900562999999912e-03 2.396687342000000144e-03 3.197327172999999984e-03 4.412280133000000097e-03 6.070971449999999596e-03 8.058617097999999299e-03 1.015841473000000071e-02 1.220399383000000081e-02 1.422303400000000072e-02 1.643257395999999856e-02 1.910539299000000094e-02 2.240138904000000056e-02 2.622779730000000115e-02 3.023218304999999995e-02 3.402154396999999830e-02 3.747324945000000268e-02 4.084126551000000216e-02 4.456187916999999887e-02 4.895723044999999995e-02 5.404702808999999747e-02 5.953321802000000051e-02 6.496362763000000651e-02 7.001722886000000257e-02 7.472294336000000203e-02 7.944106770999999345e-02 8.464057714000000621e-02 9.062771913000000623e-02 9.734821880999999788e-02 1.043732026000000063e-01 1.111471012999999952e-01 1.173695054000000043e-01 1.232033577999999963e-01 1.291453727000000107e-01 1.356938205999999869e-01 1.430453906999999969e-01 1.509661871999999905e-01 1.589076221000000066e-01 1.663357332000000022e-01 1.730712087000000066e-01 1.793997622999999875e-01 1.858969983000000048e-01 1.930966023000000031e-01 2.011757667000000083e-01 2.098318768000000112e-01 2.184723393000000125e-01 2.266286379999999934e-01 2.342919839000000004e-01 2.419289115000000101e-01 2.502013578000000238e-01 2.595871182000000221e-01 2.701049006999999835e-01 2.813024116000000241e-01 2.925459409000000233e-01 3.034388336000000130e-01 3.140824622000000232e-01 3.250147246999999795e-01 3.368794079999999913e-01 3.500209514000000022e-01 3.642462313000000007e-01 3.789283167999999979e-01 3.934037415000000149e-01 4.073827195999999984e-01 4.210930086999999933e-01 4.350988097999999860e-01 4.499384949999999939e-01 4.657907589999999987e-01 4.823531102999999987e-01 4.990113815000000175e-01 5.152011181000000217e-01 5.307346866000000052e-01 5.459025118000000454e-01 5.612995884999999463e-01 5.774713811999999669e-01 5.945742500999999791e-01 6.122601326000000066e-01 6.298691131000000443e-01 6.468125425999999845e-01 6.629148549000000168e-01 6.785207346999999611e-01 6.943025838999999921e-01 7.108565231000000262e-01 7.283093545000000057e-01 7.461889310000000552e-01 7.636719467999999456e-01 7.800826392999999914e-01 7.953346191000000287e-01 8.100150072999999784e-01 8.250231137000000325e-01 8.409777458999999622e-01 8.577876706000000517e-01 8.746874662000000411e-01 8.907288009999999590e-01 9.054064676999999950e-01 9.190029790000000531e-01 9.324181443000000291e-01 9.465903275000000505e-01 9.618856796000000431e-01 9.778515883999999714e-01 9.934965191000000306e-01 1.007923329999999895e+00 1.020909692999999896e+00 1.033049448000000092e+00 1.045351324000000082e+00 1.058556178999999986e+00 1.072623311000000079e+00 1.086724296000000090e+00 1.099754072000000082e+00 1.111030549999999950e+00 1.120719336000000066e+00 1.129698298999999961e+00 1.138960167000000023e+00 1.148956714000000101e+00 1.159302464999999893e+00 1.169009616000000085e+00 1.177100862999999942e+00 1.183219220000000016e+00 1.187849098999999908e+00 1.192006353000000018e+00 1.196596929999999892e+00 1.201847611000000038e+00 1.207160540000000060e+00 1.211490606999999997e+00 1.214023281000000010e+00 1.214716421000000102e+00 1.214338903000000025e+00 1.213984817000000049e+00 1.214399095999999956e+00 1.215546164000000040e+00 1.216667713000000095e+00 1.216775984000000088e+00 1.215278673000000031e+00 1.212353497000000058e+00 1.208847467000000009e+00 1.205775452999999997e+00 1.203726784000000105e+00 1.202530250999999994e+00 1.201386725000000100e+00 1.199409592000000080e+00 1.196232783999999993e+00 1.192271196999999949e+00 1.188463543999999983e+00 1.185701141000000014e+00 1.184332045999999972e+00 1.184029948999999915e+00 1.184068300999999934e+00 1.183805269000000049e+00 1.183076609000000001e+00 1.182276306999999971e+00 1.182116836999999920e+00 1.183233208999999952e+00 1.185841383999999943e+00 1.189626461000000024e+00 1.193937156000000055e+00 1.198183411999999892e+00 1.202181026999999958e+00 1.206227103000000023e+00 1.210893122999999960e+00 1.216687564000000110e+00 1.223771398000000010e+00 1.231865959000000066e+00 1.240406510999999989e+00 1.248862542999999992e+00 1.257043354999999973e+00 1.265220068000000087e+00 1.273992827999999911e+00 1.283951947000000038e+00 1.295312900000000100e+00 1.307775053000000076e+00 1.320730436999999924e+00 1.333680452999999932e+00 1.346564188000000106e+00 1.359797436000000026e+00 1.374026305999999975e+00 1.389749112000000064e+00 1.407027940000000088e+00 1.425474440999999981e+00 1.444519727999999947e+00 1.463779176999999931e+00 1.483280350000000025e+00 1.503437751999999961e+00 1.524799581999999987e+00 1.547708266000000110e+00 1.572090854000000038e+00 1.597527949999999919e+00 1.623536333999999970e+00 1.649851680999999903e+00 1.676554635000000015e+00 1.704013996000000031e+00 1.732683954000000082e+00 1.762843195000000085e+00 1.794429502000000065e+00 1.827083632999999985e+00 1.860360302999999993e+00 1.893965182000000080e+00 1.927901147999999898e+00 1.962454695999999998e+00 1.998009415000000066e+00 2.034789681999999988e+00 2.072709299000000005e+00 2.111404858000000218e+00 2.150396241000000153e+00 2.189299135999999812e+00 2.228027543000000055e+00 2.266864087000000083e+00 2.306289580000000061e+00 2.346651138999999997e+00 2.387901997999999804e+00 2.429583244999999891e+00 2.471055922000000127e+00 2.511871165000000072e+00 2.552066953999999832e+00 2.592163935999999946e+00 2.632830191000000042e+00 2.674455639000000051e+00 2.716921876999999874e+00 2.759679252000000194e+00 2.802083474999999879e+00 2.843833646000000215e+00 2.885235269999999907e+00 2.927053261999999823e+00 2.970029026000000183e+00 3.014416944999999792e+00 3.059846233000000026e+00 3.105563990999999913e+00 3.150916046999999942e+00 3.195791527999999992e+00 3.240724125999999927e+00 3.286555727000000093e+00 3.333910148999999823e+00 3.382833913999999886e+00 3.432782293999999901e+00 3.482938331000000165e+00 3.532728903999999837e+00 3.582245680999999848e+00 3.632244673999999840e+00 3.683684056999999790e+00 3.737122786000000030e+00 3.792365917000000142e+00 3.848551483000000051e+00 3.904657766999999780e+00 3.960175603999999794e+00 4.015494054000000368e+00 4.071677407000000137e+00 4.129774069999999853e+00 4.190137359000000394e+00 4.252177148000000351e+00 4.314690565000000255e+00 4.376617734999999954e+00 4.437761942999999931e+00 4.498940335000000346e+00 4.561428165999999784e+00 4.626110813999999571e+00 4.692917820999999989e+00 4.760853158999999835e+00 4.828594076999999984e+00 4.895330549999999725e+00 4.961307502000000369e+00 5.027668471000000139e+00 5.095710389999999812e+00 5.166074545999999934e+00 5.238368987000000310e+00 5.311421557000000071e+00 5.384036981000000388e+00 5.455810271999999905e+00 5.527427995000000038e+00 5.600231939000000381e+00 5.675382624000000042e+00 5.753187131999999870e+00 5.832953288999999764e+00 5.913426220999999927e+00 5.993583335000000289e+00 6.073308546999999891e+00 6.153476468000000033e+00 6.235389600999999615e+00 6.319963858999999573e+00 6.407160998000000163e+00 6.495986750000000143e+00 6.585090135999999816e+00 6.673630621000000041e+00 6.761821325999999743e+00 6.850756595000000004e+00 6.941680955999999902e+00 7.035212107999999631e+00 7.130965585000000218e+00 7.227772908000000385e+00 7.324388188000000355e+00 7.420263665000000231e+00 7.515875621000000173e+00 7.612391052000000435e+00 7.710918466999999943e+00 7.811798213000000324e+00 7.914334108000000256e+00 8.017163276999999866e+00 8.119092557000000099e+00 8.219856525000000858e+00 8.320262175999999954e+00 8.421637097999999710e+00 8.524963797999999926e+00 8.630229078999999359e+00 8.736380995999999399e+00 8.841956630000000317e+00 8.946005252000000851e+00 9.048686411000000263e+00 9.151145770999999485e+00 9.254777368999999254e+00 9.360344884999999948e+00 9.467487674999999214e+00 9.574930272999999659e+00 9.681297825999999773e+00 9.786011452000000332e+00 9.889662578999999454e+00 9.993643130000000596e+00 1.009929898999999942e+01 1.020712666000000013e+01 1.031648494999999954e+01 1.042600421999999938e+01 1.053444872999999937e+01 1.064148536999999983e+01 1.074788516999999999e+01 1.085505696999999969e+01 1.096419608999999973e+01 1.107553779000000027e+01 1.118817310999999926e+01 1.130057719999999932e+01 1.141154275999999967e+01 1.152093007000000036e+01 1.162975926999999920e+01 1.173958108000000067e+01 1.185149975000000033e+01 1.196545049000000027e+01 1.208020304999999972e+01 1.219411028000000030e+01 1.230613461000000086e+01 1.241650581000000031e+01 1.252659163999999947e+01 1.263804312000000074e+01 1.275173449999999953e+01 1.286717617000000047e+01 1.298279628000000052e+01 1.309692313999999946e+01 1.320884187000000054e+01 1.331924794999999939e+01 1.342980297000000078e+01 1.354207414000000043e+01 1.365653810999999962e+01 1.377227689000000055e+01 1.388753980999999982e+01 1.400081626000000057e+01 1.411177129999999913e+01 1.422146955999999918e+01 1.433172483999999969e+01 1.444396159999999973e+01 1.455830693000000053e+01 1.467349676000000080e+01 1.478766755999999916e+01 1.489954773999999915e+01 1.500929262000000008e+01 1.511841079999999948e+01 1.522882665999999929e+01 1.534170173000000048e+01 1.545676199999999945e+01 1.557251019999999997e+01 1.568716803999999954e+01 1.579979194000000042e+01 1.591089140999999962e+01 1.602214401999999893e+01 1.613536158999999870e+01 1.625135442999999924e+01 1.636940584999999970e+01 1.648769472000000036e+01 1.660442869999999971e+01 1.671898346000000046e+01 1.683229461000000171e+01 1.694624186000000066e+01 1.706245821999999990e+01 1.718133502999999962e+01 1.730179654000000156e+01 1.742194635999999974e+01 1.754021910000000162e+01 1.765636148000000105e+01 1.777161621999999852e+01 1.788796247999999878e+01 1.800687758000000116e+01 1.812838810999999950e+01 1.825101386000000048e+01 1.837268557999999885e+01 1.849208353999999943e+01 1.860950370000000120e+01 1.872666056999999995e+01 1.884559061999999940e+01 1.896739717000000169e+01 1.909159451000000018e+01 1.921641089999999963e+01 1.933987653000000151e+01 1.946105701999999837e+01 1.958065898999999987e+01 1.970061108000000161e+01 1.982287769999999938e+01 1.994822952000000171e+01 2.007572646999999932e+01 2.020327844999999911e+01 2.032898373000000092e+01 2.045237986000000063e+01 2.057476314999999900e+01 2.069838422999999850e+01 2.082506666000000095e+01 2.095509782999999970e+01 2.108704662999999968e+01 2.121864303999999990e+01 2.134821681000000027e+01 2.147579053999999843e+01 2.160310907999999941e+01 2.173257240999999951e+01 2.186572716999999955e+01 2.200224989000000164e+01 2.214009639000000007e+01 2.227678248999999866e+01 2.241098011000000056e+01 2.254335946000000135e+01 2.267613559999999850e+01 2.281165093999999982e+01 2.295089424000000022e+01 2.309285420999999872e+01 2.323511599000000061e+01 2.337535859999999843e+01 2.351281071000000011e+01 2.364872644000000079e+01 2.378558556000000124e+01 2.392552902999999986e+01 2.406899675000000016e+01 2.421442470999999941e+01 2.435922551999999897e+01 2.450143733000000168e+01 2.464095942999999878e+01 2.477958831000000117e+01 2.491987882999999826e+01 2.506356467000000166e+01 2.521049023999999861e+01 2.535870163999999960e+01 2.550566111999999919e+01 2.564983175000000060e+01 2.579162244999999842e+01 2.593310598000000056e+01 2.607670647000000130e+01 2.622366909000000135e+01 2.637325407000000155e+01 2.652317516000000097e+01 2.667101969000000139e+01 2.681573470000000015e+01 2.695823877000000124e+01 2.710081922000000176e+01 2.724571969000000138e+01 2.739376336000000123e+01 2.754383380000000159e+01 2.769355437999999836e+01 2.784076682999999974e+01 2.798487693999999948e+01 2.812721846999999897e+01 2.827021650000000008e+01 2.841586320000000043e+01 2.856443838999999940e+01 2.871429098000000124e+01 2.886284844000000049e+01 2.900820939999999837e+01 2.915029441000000077e+01 2.929085467999999892e+01 2.943236953999999983e+01 2.957650720999999905e+01 2.972307729999999992e+01 2.987013968000000119e+01 3.001522699999999944e+01 3.015690601000000015e+01 3.029568373000000037e+01 3.043370159999999913e+01 3.057341038999999938e+01 3.071603338999999977e+01 3.086078410000000005e+01 3.100536100000000062e+01 3.114740726999999865e+01 3.128598743999999954e+01 3.142216396999999972e+01 3.155835384999999960e+01 3.169686417000000134e+01 3.183848015999999959e+01 3.198198410999999908e+01 3.212493551000000025e+01 3.226522861000000120e+01 3.240243960000000101e+01 3.253812407000000206e+01
+0.000000000000000000e+00 0.000000000000000000e+00 1.820388512000000118e-07 1.949316547999999890e-09 1.835659695000000072e-06 1.153029472999999969e-05 3.576673243999999751e-05 7.910041913999999526e-05 1.420068822000000047e-04 2.207150739999999924e-04 3.087316432999999893e-04 3.992026884999999906e-04 4.869914638999999976e-04 5.696536955999999523e-04 6.471460942000000416e-04 7.207296093000000251e-04 7.918093385000000376e-04 8.612963849999999812e-04 9.296413845999999988e-04 9.972634849999999825e-04 1.064922221000000032e-03 1.133713457000000077e-03 1.204683884999999946e-03 1.278331994000000103e-03 1.354331284000000017e-03 1.431655401999999905e-03 1.509027687000000098e-03 1.585430894999999979e-03 1.660405850000000070e-03 1.734024779999999944e-03 1.806630231999999982e-03 1.878550367999999929e-03 1.949972113000000102e-03 2.021012960000000049e-03 2.091885869999999918e-03 2.162997547999999815e-03 2.234885766999999794e-03 2.308031638999999934e-03 2.382684312999999981e-03 2.458841100999999835e-03 2.536436459999999882e-03 2.615671346999999883e-03 2.697339018999999911e-03 2.783015259000000061e-03 2.875060340999999967e-03 2.976464827000000011e-03 3.090604424999999870e-03 3.220938299000000119e-03 3.370629492000000189e-03 3.542045707999999949e-03 3.736150864000000165e-03 3.951905979999999902e-03 4.185898409000000063e-03 4.432437447999999953e-03 4.684254510000000239e-03 4.933750593999999996e-03 5.174518488000000131e-03 5.402723031000000210e-03 5.617914629000000416e-03 5.822990658000000083e-03 6.023263596999999837e-03 6.224867196999999894e-03 6.432948231000000343e-03 6.650182287999999858e-03 6.876080433999999693e-03 7.107325145000000129e-03 7.339044014999999803e-03 7.566598271999999681e-03 7.787251490999999581e-03 8.001092405000000130e-03 8.210842822999999194e-03 8.420612812999999849e-03 8.634104365000000794e-03 8.853017304000000004e-03 9.076344709999999844e-03 9.300864213000000283e-03 9.522593302000000731e-03 9.738528259000000034e-03 9.947847481999999922e-03 1.015201005999999927e-02 1.035370864000000057e-02 1.055518746999999959e-02 1.075673828000000068e-02 1.095608535999999938e-02 1.114891649999999977e-02 1.133024057000000043e-02 1.149584839999999962e-02 1.164312944000000030e-02 1.177085838000000058e-02 1.187812323000000080e-02 1.196302443000000007e-02 1.202189554999999986e-02 1.204952289999999988e-02 1.204032353000000083e-02 1.198995683000000055e-02 1.189664368999999999e-02 1.176164455000000032e-02 1.158879103000000008e-02 1.138342692000000010e-02 1.115134469999999947e-02 1.089819489000000072e-02 1.062947971999999956e-02 1.035084556000000038e-02 1.006819685000000068e-02 9.787280457999999669e-03 9.512760048000000121e-03 9.247194327000000241e-03 8.990509614000000843e-03 8.740393823000000562e-03 8.493603770000000136e-03 8.247698447999999433e-03 8.002448080000000227e-03 7.760272414000000431e-03 7.525487340999999913e-03 7.302672425999999668e-03 7.094847880999999995e-03 6.902193378999999830e-03 6.721756726000000307e-03 6.548155515999999621e-03 6.374898922000000155e-03 6.195813028999999986e-03 6.006158600999999923e-03 5.803268454000000236e-03 5.586739892999999756e-03 5.358293921999999869e-03 5.121369903999999605e-03 4.880463528000000308e-03 4.640240200000000405e-03 4.404584964999999630e-03 4.175896170000000310e-03 3.954956033000000225e-03 3.741531232000000137e-03 3.535515882999999866e-03 3.338100145999999945e-03 3.152337362000000136e-03 2.982701635999999989e-03 2.833705075999999861e-03 2.708144931000000186e-03 2.605800002000000122e-03 2.523233280999999904e-03 2.454842134999999943e-03 2.394685441000000102e-03 2.338231226000000041e-03 2.283218397000000201e-03 2.229291164000000210e-03 2.176700191999999829e-03 2.124836293999999865e-03 2.071426117999999872e-03 2.012851521000000050e-03 1.945458292000000087e-03 1.867209893999999997e-03 1.778872166999999902e-03 1.684150842000000032e-03 1.588701789999999968e-03 1.498432656000000043e-03 1.417779775000000027e-03 1.348582348999999986e-03 1.289861816999999990e-03 1.238429316999999910e-03 1.189968600999999999e-03 1.140168454999999914e-03 1.085587204000000106e-03 1.024125495000000052e-03 9.551542765000000301e-04 8.794278554999999508e-04 7.989017024999999850e-04 7.165082577000000431e-04 6.358714384999999665e-04 5.609016402000000434e-04 4.952284829000000530e-04 4.414991636000000115e-04 4.006753678999999831e-04 3.715576743000000124e-04 3.507933891999999877e-04 3.335317473000000139e-04 3.146745652999999830e-04 2.903969536999999974e-04 2.594071743999999940e-04 2.234081492999999885e-04 1.864739303000000005e-04 1.534941247000000050e-04 1.282796421999999887e-04 1.121385784999999951e-04 1.035797203000000033e-04 9.931745666000000065e-05 9.615000697999999879e-05 9.285892839999999589e-05 9.125522881000000606e-05 9.589961765000000449e-05 1.126530642000000054e-04 1.467489311999999881e-04 2.012511027000000086e-04 2.765017350999999829e-04 3.706278019000000166e-04 4.806643355999999970e-04 6.036280572999999650e-04 7.370252030000000410e-04 8.786765931999999729e-04 1.026141401000000078e-03 1.176198896999999929e-03 1.324738481000000030e-03 1.467122376999999969e-03 1.598821624999999941e-03 1.716039621000000053e-03 1.816145390000000089e-03 1.897923297000000042e-03 1.961749048999999849e-03 2.009742666000000066e-03 2.045784332000000118e-03 2.075162938999999904e-03 2.103698297999999876e-03 2.136447911999999911e-03 2.176433569000000082e-03 2.223981881999999820e-03 2.277113009999999840e-03 2.332960945999999931e-03 2.389682080999999854e-03 2.447997759999999932e-03 2.511620076000000128e-03 2.586308548999999811e-03 2.677956598000000212e-03 2.790572791000000153e-03 2.925055899000000067e-03 3.079248269999999930e-03 3.249114373999999802e-03 3.430372028999999993e-03 3.619770022999999927e-03 3.815502806999999941e-03 4.016786692000000325e-03 4.223077375999999793e-03 4.433534486999999615e-03 4.647086754999999944e-03 4.862995103000000187e-03 5.081437708999999615e-03 5.303582145999999678e-03 5.530905586999999846e-03 5.764003636000000243e-03 6.001508826000000185e-03 6.239788413999999665e-03 6.473757584999999953e-03 6.698590735000000025e-03 6.911628349999999858e-03 7.113618173000000114e-03 7.308687989000000294e-03 7.503002249000000241e-03 7.702638161999999719e-03 7.911550814999999612e-03 8.130431203999999648e-03 8.356840872999999476e-03 8.586441926999999544e-03 8.814695650999999563e-03 9.038269204999999432e-03 9.255604173000000351e-03 9.466538740000000549e-03 9.671320078999999861e-03 9.869593944000000854e-03 1.005991795000000036e-02 1.024005453000000065e-02 1.040790538000000050e-02 1.056263298999999926e-02 1.070541067999999929e-02 1.083938760000000001e-02 1.096877582999999934e-02 1.109732515999999926e-02 1.122670262000000085e-02 1.135534506000000082e-02 1.147819544000000004e-02 1.158743505000000076e-02 1.167399843000000055e-02 1.172941788999999971e-02 1.174746320000000059e-02 1.172513755000000046e-02 1.166281767000000059e-02 1.156360002999999928e-02 1.143214111999999935e-02 1.127339190999999963e-02 1.109160051000000063e-02 1.088982099999999918e-02 1.066997631000000078e-02 1.043334731000000043e-02 1.018124936000000015e-02 9.915633615999999359e-03 9.639407798000000291e-03 9.356379386999999492e-03 9.070845182999999826e-03 8.786948751999999752e-03 8.507983792999999212e-03 8.235832803000000049e-03 7.970699568000000693e-03 7.711225640000000416e-03 7.454984807000000248e-03 7.199244425000000079e-03 6.941798911999999717e-03 6.681652526999999793e-03 6.419375526999999797e-03 6.157066919000000332e-03 5.897983267999999668e-03 5.645980832000000241e-03 5.404930740000000143e-03 5.178207488999999990e-03 4.968264806999999825e-03 4.776262952000000402e-03 4.601738406999999857e-03 4.442395624000000133e-03 4.294181803000000040e-03 4.151796299000000239e-03 4.009646989000000342e-03 3.863041380000000000e-03 3.709211928000000214e-03 3.547757625000000012e-03 3.380295963999999792e-03 3.209485711999999959e-03 3.037914019999999986e-03 2.867438648999999940e-03 2.699344291999999923e-03 2.535203121000000146e-03 2.377871091999999784e-03 2.231877397999999877e-03 2.102715072999999935e-03 1.995124498000000208e-03 1.911080611000000058e-03 1.848496493000000018e-03 1.801432949000000058e-03 1.761924603999999971e-03 1.722726310000000007e-03 1.679771926000000028e-03 1.633210188000000073e-03 1.586530949999999949e-03 1.544203826000000021e-03 1.508959192999999917e-03 1.479988882999999948e-03 1.452865952999999894e-03 1.421139404999999982e-03 1.378787645000000050e-03 1.322391497999999979e-03 1.252136445000000004e-03 1.171393962000000016e-03 1.085298931999999911e-03 9.990963830999999556e-04 9.169454146999999906e-04 8.414649593999999752e-04 7.738635806000000034e-04 7.142696037000000530e-04 6.619547717999999664e-04 6.154109309000000338e-04 5.724746999000000520e-04 5.307254516000000288e-04 4.881910606000000228e-04 4.441265271000000231e-04 3.994804029999999792e-04 3.567500820000000226e-04 3.192105520999999964e-04 2.898156044999999860e-04 2.702295482000000200e-04 2.603625255999999773e-04 2.585133214000000213e-04 2.619345707000000239e-04 2.674901391000000209e-04 2.721363636999999920e-04 2.731605601999999885e-04 2.683050983999999946e-04 2.559644066999999757e-04 2.355363272000000072e-04 2.078241537000000009e-04 1.752604007000000080e-04 1.417542710999999912e-04 1.121394326000000030e-04 9.140599956999999603e-05 8.400608521999999704e-05 9.345805127999999324e-05 1.222826734999999982e-04 1.721083782999999949e-04 2.437076380999999930e-04 3.368191064000000050e-04 4.498102149000000080e-04 5.794148548999999591e-04 7.208238189999999733e-04 8.682751520999999707e-04 1.016051477000000066e-03 1.159568349000000050e-03 1.296149590999999936e-03 1.425178852000000100e-03 1.547548482999999957e-03 1.664596123999999896e-03 1.776918861999999969e-03 1.883515277000000080e-03 1.981611274000000082e-03 2.067307152999999821e-03 2.136901844000000175e-03 2.188486285000000066e-03 2.223239369999999940e-03 2.245883143000000030e-03 2.263988864000000215e-03 2.286224474000000170e-03 2.320064704999999939e-03 2.369768258999999794e-03 2.435409236999999845e-03 2.513390998999999806e-03 2.598287540999999912e-03 2.685297431000000130e-03 2.772324287000000210e-03 2.860863017999999876e-03 2.955410642999999928e-03 3.061780337000000012e-03 3.185155612000000001e-03 3.328756505000000140e-03 3.493597152000000022e-03 3.679210747000000083e-03 3.884726459000000139e-03 4.109559672000000392e-03 4.353274094999999913e-03 4.614711631000000154e-03 4.890953213999999979e-03 5.176805357999999906e-03 5.465232294000000116e-03 5.748635493000000402e-03 6.020421086000000283e-03 6.276152406999999794e-03 6.513830709999999732e-03 6.733327947999999570e-03 6.935430771000000195e-03 7.121103547000000138e-03 7.291372968999999989e-03 7.447812013999999779e-03 7.593209216000000074e-03 7.731875903000000327e-03 7.869242012000000203e-03 8.010801822999999433e-03 8.160854594999999295e-03 8.321620219999999180e-03 8.493121343999999670e-03 8.673822093999999927e-03 8.861628364000000146e-03 9.054696164000000311e-03 9.251637878999999579e-03 9.451068414999999612e-03 9.650795642000000243e-03 9.847143717000000213e-03 1.003482275000000014e-02 1.020748328000000045e-02 1.035877426000000011e-02 1.048351951999999983e-02 1.057862350999999937e-02 1.064348017000000049e-02 1.067986915999999918e-02 1.069145955000000078e-02 1.068305202000000009e-02 1.065963464000000034e-02 1.062530369000000072e-02 1.058217646000000033e-02 1.052956290000000086e-02 1.046375540999999916e-02 1.037871926999999958e-02 1.026768682000000002e-02 1.012526933999999962e-02 9.949390924999999136e-03 9.742299588999999219e-03 9.510198683999999958e-03 9.261575311999999810e-03 9.004852016000000625e-03 8.746285713999999226e-03 8.488921463999999847e-03 8.232918779999999145e-03 7.976938022000000170e-03 7.719812500000000112e-03 7.461663282999999626e-03 7.203963768999999823e-03 6.948639965000000425e-03 6.696796147999999928e-03 6.447828580999999913e-03 6.199459132999999948e-03 5.948722671999999954e-03 5.693443002000000280e-03 5.433486795999999813e-03 5.171207046000000397e-03 4.910895174999999925e-03 4.657530011000000393e-03 4.415403703000000263e-03 4.187194101000000016e-03 3.973780282999999844e-03 3.774726647999999962e-03 3.589080146999999981e-03 3.416053819999999817e-03 3.255309731000000182e-03 3.106804826000000071e-03 2.970384114999999933e-03 2.845394691000000063e-03 2.730532482000000167e-03 2.623979136000000132e-03 2.523730737000000161e-03 2.427938923999999922e-03 2.335106928000000182e-03 2.244084395000000079e-03 2.153927635999999986e-03 2.063774644000000019e-03 1.972889047000000052e-03 1.880948476999999954e-03 1.788515997999999985e-03 1.697487801999999959e-03 1.611225452999999908e-03
diff --git a/tests/mesa_pd/kernel/cnt/VBondContactIntegration.ipynb b/tests/mesa_pd/kernel/cnt/VBondContactIntegration.ipynb
new file mode 100644
index 0000000000000000000000000000000000000000..3b4827b2ec29a124287a17dfbe5dba50b9e58fea
--- /dev/null
+++ b/tests/mesa_pd/kernel/cnt/VBondContactIntegration.ipynb
@@ -0,0 +1,94 @@
+{
+ "cells": [
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "northern-indie",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "import matplotlib.pyplot as pl\n",
+    "import numpy as np\n",
+    "import seaborn as sns\n",
+    "\n",
+    "%matplotlib inline\n",
+    "%config InlineBackend.figure_formats = ['svg']\n",
+    "\n",
+    "sns.set_context(\"talk\")"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "distant-tsunami",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "energies = np.loadtxt('VBondContactEnergies.txt').transpose()\n",
+    "ref = np.loadtxt('VBondContactEnergies.ref.txt')"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "failing-studio",
+   "metadata": {},
+   "outputs": [],
+   "source": [
+    "pl.figure( figsize=(10,10) )\n",
+    "pl.subplot(221)\n",
+    "pl.title('tensile')\n",
+    "pl.plot(energies[1], label='MESA-PD')\n",
+    "pl.plot(ref[0], label='reference')\n",
+    "pl.legend()\n",
+    "\n",
+    "pl.subplot(222)\n",
+    "pl.title('shear')\n",
+    "pl.plot(energies[2])\n",
+    "pl.plot(ref[1])\n",
+    "\n",
+    "pl.subplot(223)\n",
+    "pl.title('bending')\n",
+    "pl.plot(energies[3])\n",
+    "pl.plot(ref[2])\n",
+    "\n",
+    "pl.subplot(224)\n",
+    "pl.title('twisting')\n",
+    "pl.plot(energies[4])\n",
+    "pl.plot(ref[3])\n",
+    "\n",
+    "pl.tight_layout()\n",
+    "pl.show()"
+   ]
+  },
+  {
+   "cell_type": "code",
+   "execution_count": null,
+   "id": "enormous-salvation",
+   "metadata": {},
+   "outputs": [],
+   "source": []
+  }
+ ],
+ "metadata": {
+  "kernelspec": {
+   "display_name": "Python 3",
+   "language": "python",
+   "name": "python3"
+  },
+  "language_info": {
+   "codemirror_mode": {
+    "name": "ipython",
+    "version": 3
+   },
+   "file_extension": ".py",
+   "mimetype": "text/x-python",
+   "name": "python",
+   "nbconvert_exporter": "python",
+   "pygments_lexer": "ipython3",
+   "version": "3.8.5"
+  }
+ },
+ "nbformat": 4,
+ "nbformat_minor": 5
+}
diff --git a/tests/mesa_pd/kernel/cnt/VBondContactIntegration.test.cpp b/tests/mesa_pd/kernel/cnt/VBondContactIntegration.test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5c956fe6106ca45940e4da6d7e17160ba89ade34
--- /dev/null
+++ b/tests/mesa_pd/kernel/cnt/VBondContactIntegration.test.cpp
@@ -0,0 +1,161 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include "SphericalSegmentAccessor.h"
+#include "mesa_pd/data/Flags.h"
+#include "mesa_pd/data/ParticleStorage.h"
+#include "mesa_pd/kernel/ParticleSelector.h"
+#include "mesa_pd/kernel/cnt/Parameters.h"
+#include "mesa_pd/kernel/cnt/VBondContact.h"
+#include "mesa_pd/kernel/VelocityVerlet.h"
+#include "mesa_pd/vtk/ParticleVtkOutput.h"
+
+#include "core/Environment.h"
+#include "core/math/Constants.h"
+#include "vtk/VTKOutput.h"
+
+namespace walberla {
+using namespace walberla::mesa_pd;
+
+int main(int argc, char **argv)
+{
+   Environment env(argc, argv);
+   walberla::mpi::MPIManager::instance()->useWorldComm();
+
+   if (std::is_same<real_t, float>::value)
+   {
+      WALBERLA_LOG_WARNING("waLBerla build in sp mode: skipping test due to low precision");
+      return EXIT_SUCCESS;
+   }
+
+   logging::Logging::instance()->setStreamLogLevel(logging::Logging::INFO);
+   logging::Logging::instance()->setFileLogLevel(logging::Logging::INFO);
+
+   WALBERLA_LOG_INFO_ON_ROOT("loading configuration parameters");
+   constexpr auto numSimulationSteps = 500ll;
+
+   WALBERLA_LOG_INFO_ON_ROOT("creating initial particle setup");
+   auto ps = std::make_shared<data::ParticleStorage>(10);
+   auto ac = SphericalSegmentAccessor(ps);
+
+   for (auto i = 0; i < 10; ++i)
+   {
+      data::Particle &&p = *ps->create();
+      p.setPosition(Vec3(500_r, 500_r, 500_r + real_c(i) * 13.56_r));
+      p.setSegmentID(i);
+      p.setClusterID(1);
+      if (i == 0)
+         data::particle_flags::set(p.getFlagsRef(), data::particle_flags::FIXED);
+      p.getRotationRef().rotate(Vec3(0_r, 1_r, 0_r), -0.5_r * math::pi);
+      p.getRotationRef().rotate(Vec3(0_r, 0_r, 1_r), 0_r);
+   }
+   data::Particle &&last_segment = *(ps->end() - 1);
+
+   WALBERLA_LOG_INFO_ON_ROOT("setting up VTK output");
+   auto vtkOutput       = make_shared<mesa_pd::vtk::ParticleVtkOutput>(ps);
+   vtkOutput->addOutput<data::SelectParticlePosition>("position");
+   auto vtkWriter       = walberla::vtk::createVTKOutput_PointData(vtkOutput,
+                                                                   "cnt",
+                                                                   1,
+                                                                   "vtk",
+                                                                   "particles",
+                                                                   false,
+                                                                   false);
+
+   WALBERLA_LOG_INFO_ON_ROOT("setting up interaction models");
+   kernel::cnt::VBondContact vbond;
+   kernel::VelocityVerletPreForceUpdate vv_pre(kernel::cnt::dT);
+   kernel::VelocityVerletPostForceUpdate vv_post(kernel::cnt::dT);
+
+   WALBERLA_LOG_INFO_ON_ROOT("running simulation");
+   auto appliedForce = Vec3(1_r, 0_r, 0_r);
+   auto appliedTorque = Vec3(0_r, 0_r, 1_r);
+
+   real_t tensileEnergy = 0_r;
+   real_t shearEnergy = 0_r;
+   real_t bendingEnergy = 0_r;
+   real_t twistingEnergy = 0_r;
+
+   std::ofstream fout("VBondContactEnergies.txt");
+   for (auto i = 0; i < numSimulationSteps; ++i)
+   {
+      vtkWriter->write();
+
+      ps->forEachParticle(false,
+                          kernel::SelectAll(),
+                          ac,
+                          vv_pre,
+                          ac);
+
+      last_segment.setForce(appliedForce);
+      last_segment.setTorque(appliedTorque);
+      constexpr auto cutoff2 = kernel::cnt::outer_radius * kernel::cnt::outer_radius;
+
+      tensileEnergy = 0_r;
+      shearEnergy = 0_r;
+      bendingEnergy = 0_r;
+      twistingEnergy = 0_r;
+
+      ps->forEachParticlePairHalf(false,
+                                  kernel::SelectAll(),
+                                  ac,
+                                  [&](size_t p_idx1, size_t p_idx2)
+                                  {
+                                     if (ac.getClusterID(p_idx1) != ac.getClusterID(p_idx2)) return;
+                                     if (std::abs(ac.getSegmentID(p_idx1) - ac.getSegmentID(p_idx2)) != 1) return;
+                                     if ((ac.getPosition(p_idx1) - ac.getPosition(p_idx2)).sqrLength() < cutoff2)
+                                     {
+                                        vbond(p_idx1, p_idx2, ac);
+                                        tensileEnergy += vbond.getLastTensileEnergy();
+                                        shearEnergy += vbond.getLastShearEnergy();
+                                        bendingEnergy += vbond.getLastBendingEnergy();
+                                        twistingEnergy += vbond.getLastTwistingEnergy();
+                                     }
+                                  });
+
+      ps->forEachParticle(false,
+                          kernel::SelectAll(),
+                          ac,
+                          vv_post,
+                          ac);
+
+      fout << last_segment.getPosition()[0] << " "
+           << tensileEnergy << " "
+           << shearEnergy << " "
+           << bendingEnergy << " "
+           << twistingEnergy
+           << std::endl;
+   }
+
+   WALBERLA_CHECK_FLOAT_EQUAL(tensileEnergy,  1.88111638964774328e-02_r);
+   WALBERLA_CHECK_FLOAT_EQUAL(shearEnergy,    2.04795345750102054e-01_r);
+   WALBERLA_CHECK_FLOAT_EQUAL(bendingEnergy,  3.28859587360327978e+01_r);
+   WALBERLA_CHECK_FLOAT_EQUAL(twistingEnergy, 1.44177931971837983e-03_r);
+
+   return EXIT_SUCCESS;
+}
+} // namespace walberla
+
+int main(int argc, char *argv[])
+{
+   return walberla::main(argc, argv);
+}
diff --git a/tests/mesa_pd/kernel/cnt/ViscousDamping.test.cpp b/tests/mesa_pd/kernel/cnt/ViscousDamping.test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..593ddecacd371e33686d716eea5f0dc402474377
--- /dev/null
+++ b/tests/mesa_pd/kernel/cnt/ViscousDamping.test.cpp
@@ -0,0 +1,86 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/data/ParticleAccessor.h>
+#include <mesa_pd/data/ParticleStorage.h>
+
+#include <mesa_pd/kernel/cnt/ViscousDamping.h>
+
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+
+#include <iostream>
+
+namespace walberla {
+namespace mesa_pd {
+
+int main( int argc, char ** argv )
+{
+
+   Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   mpi::MPIManager::instance()->useWorldComm();
+
+   if (std::is_same<real_t, float>::value)
+   {
+      WALBERLA_LOG_WARNING("waLBerla build in sp mode: skipping test due to low precision");
+      return EXIT_SUCCESS;
+   }
+
+   //init data structures
+   auto ps = std::make_shared<data::ParticleStorage>(2);
+   data::Particle &&p1 = *ps->create();
+   data::Particle &&p2 = *ps->create();
+
+   data::ParticleAccessor ac(ps);
+
+   //init kernels
+   kernel::cnt::ViscousDamping damping(0.2_r,0.5_r);
+   WALBERLA_CHECK_FLOAT_EQUAL(damping.getForceDampingFactor(),
+                              0.2_r);
+   WALBERLA_CHECK_FLOAT_EQUAL(damping.getTorqueDampingFactor(),
+                              0.5_r);
+
+   p1.setLinearVelocity(Vec3(1_r, 2_r, 3_r));
+   p2.setLinearVelocity(Vec3(3_r, 2_r, 1_r));
+   p1.setAngularVelocity(Vec3(2_r, 3_r, 4_r));
+   p2.setAngularVelocity(Vec3(3_r, 2_r, 1_r));
+   damping(0, 1, ac);
+   WALBERLA_CHECK_FLOAT_EQUAL(p1.getForce(),
+                              Vec3(0.4_r, 0_r, -0.4_r));
+   WALBERLA_CHECK_FLOAT_EQUAL(p2.getForce(),
+                              Vec3(-0.4_r, 0_r, 0.4_r));
+   WALBERLA_CHECK_FLOAT_EQUAL(p1.getTorque(),
+                              Vec3(0.5_r, -0.5_r, -1.5_r));
+   WALBERLA_CHECK_FLOAT_EQUAL(p2.getTorque(),
+                              Vec3(-0.5_r, 0.5_r, 1.5_r));
+
+   return EXIT_SUCCESS;
+}
+
+} //namespace mesa_pd
+} //namespace walberla
+
+int main( int argc, char ** argv )
+{
+   return walberla::mesa_pd::main(argc, argv);
+}
diff --git a/tests/mesa_pd/kernel/cnt/WallContact.test.cpp b/tests/mesa_pd/kernel/cnt/WallContact.test.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..50165fd4fbde9203569ef5c2244690a4d700d47f
--- /dev/null
+++ b/tests/mesa_pd/kernel/cnt/WallContact.test.cpp
@@ -0,0 +1,102 @@
+//======================================================================================================================
+//
+//  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
+//! \author Igor Ostanin <i.ostanin@skoltech.ru>
+//! \author Grigorii Drozdov <drozd013@umn.edu>
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+#include <mesa_pd/data/ParticleAccessor.h>
+#include <mesa_pd/data/ParticleStorage.h>
+
+#include <mesa_pd/kernel/cnt/WallContact.h>
+
+#include <core/Environment.h>
+#include <core/logging/Logging.h>
+
+#include <iostream>
+
+namespace walberla {
+namespace mesa_pd {
+
+int main( int argc, char ** argv )
+{
+   Environment env(argc, argv);
+   WALBERLA_UNUSED(env);
+   mpi::MPIManager::instance()->useWorldComm();
+
+   if (std::is_same<real_t, float>::value)
+   {
+      WALBERLA_LOG_WARNING("waLBerla build in sp mode: skipping test due to low precision");
+      return EXIT_SUCCESS;
+   }
+
+   //init data structures
+   data::SingleParticleAccessor p;
+   p.setPosition(0, Vec3(0,0,0));
+
+   //init kernels
+   kernel::cnt::WallContact wallContact(0_r);
+
+   auto calcForce = [&](const real_t zPos)
+   {
+      clear(p.getForceRef(0));
+      p.getPositionRef(0)[2] = zPos;
+      wallContact(0, p);
+      WALBERLA_CHECK_FLOAT_EQUAL(wallContact.getLastForce(), p.getForce(0)[2]);
+      return p.getForce(0)[2];
+   };
+
+   WALBERLA_LOG_INFO("checking that force is always repulsive");
+   WALBERLA_CHECK_GREATER_EQUAL( calcForce(wallContact.z1 * 0.5_r + wallContact.r),
+                                 0_r );
+   WALBERLA_CHECK_GREATER_EQUAL( calcForce((wallContact.z1 + wallContact.z2) * 0.5_r + wallContact.r),
+                                 0_r );
+   WALBERLA_CHECK_GREATER_EQUAL( calcForce(wallContact.z2 * 2_r + wallContact.r),
+                                 0_r );
+
+   WALBERLA_CHECK_LESS_EQUAL( calcForce(-wallContact.z1 * 0.5_r - wallContact.r),
+                              0_r );
+   WALBERLA_CHECK_LESS_EQUAL( calcForce(-(wallContact.z1 + wallContact.z2) * 0.5_r - wallContact.r),
+                              0_r );
+   WALBERLA_CHECK_LESS_EQUAL( calcForce(-wallContact.z2 * 2_r - wallContact.r),
+                              0_r );
+
+   WALBERLA_LOG_INFO("checking that force is symmetric");
+   WALBERLA_CHECK_FLOAT_EQUAL(calcForce(wallContact.z1 * 0.5_r + wallContact.r),
+                              -calcForce(-wallContact.z1 * 0.5_r - wallContact.r) );
+   WALBERLA_CHECK_FLOAT_EQUAL(calcForce((wallContact.z1 + wallContact.z2) * 0.5_r + wallContact.r),
+                              -calcForce(-(wallContact.z1 + wallContact.z2) * 0.5_r - wallContact.r) );
+   WALBERLA_CHECK_FLOAT_EQUAL(calcForce(wallContact.z2 * 2_r + wallContact.r),
+                              -calcForce(-wallContact.z2 * 2_r - wallContact.r) );
+
+   WALBERLA_LOG_INFO("checking smoothness of the force");
+   WALBERLA_CHECK_FLOAT_EQUAL( calcForce(std::nextafter(wallContact.z1 + wallContact.r, 0_r)),
+                               calcForce(std::nextafter(wallContact.z1 + wallContact.r, std::numeric_limits<real_t>::infinity())) );
+   WALBERLA_CHECK_FLOAT_EQUAL( calcForce(std::nextafter(wallContact.z2 + wallContact.r, 0_r)),
+                               calcForce(std::nextafter(wallContact.z2 + wallContact.r, std::numeric_limits<real_t>::infinity())) );
+
+   return EXIT_SUCCESS;
+}
+
+} //namespace mesa_pd
+} //namespace walberla
+
+int main( int argc, char ** argv )
+{
+   return walberla::mesa_pd::main(argc, argv);
+}
diff --git a/tests/mesa_pd/kernel/interfaces/ExplicitEulerInterfaceCheck.cpp b/tests/mesa_pd/kernel/interfaces/ExplicitEulerInterfaceCheck.cpp
index 4d180c321c03cf4208430a1737495a9d0e8a4365..741d3ab6976744b4f2edd0ad083171d7d9b2472d 100644
--- a/tests/mesa_pd/kernel/interfaces/ExplicitEulerInterfaceCheck.cpp
+++ b/tests/mesa_pd/kernel/interfaces/ExplicitEulerInterfaceCheck.cpp
@@ -13,7 +13,7 @@
 //  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 ExplicitEulerInterfaceCheck.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -37,7 +37,7 @@ namespace mesa_pd {
 class Accessor : public data::IAccessor
 {
 public:
-   virtual ~Accessor() = default;
+   ~Accessor() override = default;
    const walberla::mesa_pd::Vec3& getPosition(const size_t /*p_idx*/) const {return position_;}
    void setPosition(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { position_ = v;}
    
diff --git a/tests/mesa_pd/kernel/interfaces/ExplicitEulerWithShapeInterfaceCheck.cpp b/tests/mesa_pd/kernel/interfaces/ExplicitEulerWithShapeInterfaceCheck.cpp
index a56e2991440b0c750b70357948f7df990f775952..db83474b4ff801832004045124b2fa7759a5245f 100644
--- a/tests/mesa_pd/kernel/interfaces/ExplicitEulerWithShapeInterfaceCheck.cpp
+++ b/tests/mesa_pd/kernel/interfaces/ExplicitEulerWithShapeInterfaceCheck.cpp
@@ -13,7 +13,7 @@
 //  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 ExplicitEulerInterfaceCheck.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -37,7 +37,7 @@ namespace mesa_pd {
 class Accessor : public data::IAccessor
 {
 public:
-   virtual ~Accessor() = default;
+   ~Accessor() override = default;
    const walberla::mesa_pd::Vec3& getPosition(const size_t /*p_idx*/) const {return position_;}
    void setPosition(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { position_ = v;}
    
diff --git a/tests/mesa_pd/kernel/interfaces/ForceLJInterfaceCheck.cpp b/tests/mesa_pd/kernel/interfaces/ForceLJInterfaceCheck.cpp
index b742e0d22cb4665e84e98353ca28688d8de7af0b..26c5152b05881ca729351793b247943b958a0199 100644
--- a/tests/mesa_pd/kernel/interfaces/ForceLJInterfaceCheck.cpp
+++ b/tests/mesa_pd/kernel/interfaces/ForceLJInterfaceCheck.cpp
@@ -13,7 +13,7 @@
 //  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 ForceLJInterfaceCheck.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -37,7 +37,7 @@ namespace mesa_pd {
 class Accessor : public data::IAccessor
 {
 public:
-   virtual ~Accessor() = default;
+   ~Accessor() override = default;
    const walberla::mesa_pd::Vec3& getPosition(const size_t /*p_idx*/) const {return position_;}
    
    walberla::mesa_pd::Vec3& getForceRef(const size_t /*p_idx*/) {return force_;}
diff --git a/tests/mesa_pd/kernel/interfaces/HeatConductionInterfaceCheck.cpp b/tests/mesa_pd/kernel/interfaces/HeatConductionInterfaceCheck.cpp
index 4e30eba2e4b4e6d4e68e26c11af3217bbc6d225a..26d5ecd9672dedd38d088a238e5dcc70253264bf 100644
--- a/tests/mesa_pd/kernel/interfaces/HeatConductionInterfaceCheck.cpp
+++ b/tests/mesa_pd/kernel/interfaces/HeatConductionInterfaceCheck.cpp
@@ -13,7 +13,7 @@
 //  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 HeatConductionInterfaceCheck.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -37,7 +37,7 @@ namespace mesa_pd {
 class Accessor : public data::IAccessor
 {
 public:
-   virtual ~Accessor() = default;
+   ~Accessor() override = default;
    const walberla::real_t& getTemperature(const size_t /*p_idx*/) const {return temperature_;}
    
    const walberla::real_t& getHeatFlux(const size_t /*p_idx*/) const {return heatFlux_;}
diff --git a/tests/mesa_pd/kernel/interfaces/PFCDampingInterfaceCheck.cpp b/tests/mesa_pd/kernel/interfaces/PFCDampingInterfaceCheck.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bcb55bdcc21c55016a3cd5f793de21644557c51c
--- /dev/null
+++ b/tests/mesa_pd/kernel/interfaces/PFCDampingInterfaceCheck.cpp
@@ -0,0 +1,76 @@
+//======================================================================================================================
+//
+//  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
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#include <mesa_pd/data/IAccessor.h>
+#include <mesa_pd/kernel/PFCDamping.h>
+
+#include <core/UniqueID.h>
+
+#include <map>
+
+namespace walberla {
+namespace mesa_pd {
+
+class Accessor : public data::IAccessor
+{
+public:
+   ~Accessor() override = default;
+   const walberla::mesa_pd::Vec3& getLinearVelocity(const size_t /*p_idx*/) const {return linearVelocity_;}
+   void setLinearVelocity(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { linearVelocity_ = v;}
+   
+   const walberla::mesa_pd::Vec3& getForce(const size_t /*p_idx*/) const {return force_;}
+   void setForce(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { force_ = v;}
+   
+   const walberla::mesa_pd::data::particle_flags::FlagT& getFlags(const size_t /*p_idx*/) const {return flags_;}
+   
+   const walberla::mesa_pd::Vec3& getAngularVelocity(const size_t /*p_idx*/) const {return angularVelocity_;}
+   void setAngularVelocity(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { angularVelocity_ = v;}
+   
+   const walberla::mesa_pd::Vec3& getTorque(const size_t /*p_idx*/) const {return torque_;}
+   void setTorque(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { torque_ = v;}
+   
+
+   id_t getInvalidUid() const {return UniqueID<int>::invalidID();}
+   size_t getInvalidIdx() const {return std::numeric_limits<size_t>::max();}
+   /**
+   * @brief Returns the index of particle specified by uid.
+   * @param uid unique id of the particle to be looked up
+   * @return the index of the particle or std::numeric_limits<size_t>::max() if the particle is not found
+   */
+   size_t uidToIdx(const id_t& /*uid*/) const {return 0;}
+   size_t size() const { return 1; }
+private:
+   walberla::mesa_pd::Vec3 linearVelocity_;
+   walberla::mesa_pd::Vec3 force_;
+   walberla::mesa_pd::data::particle_flags::FlagT flags_;
+   walberla::mesa_pd::Vec3 angularVelocity_;
+   walberla::mesa_pd::Vec3 torque_;
+};
+
+template void kernel::PFCDamping::operator()(const size_t p_idx1, Accessor& ac) const;
+
+} //namespace mesa_pd
+} //namespace walberla
\ No newline at end of file
diff --git a/tests/mesa_pd/kernel/interfaces/SemiImplicitEulerInterfaceCheck.cpp b/tests/mesa_pd/kernel/interfaces/SemiImplicitEulerInterfaceCheck.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..5b287377b68bdea2ec67e2a1127f09d528ba3d69
--- /dev/null
+++ b/tests/mesa_pd/kernel/interfaces/SemiImplicitEulerInterfaceCheck.cpp
@@ -0,0 +1,90 @@
+//======================================================================================================================
+//
+//  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
+//! \author Sebastian Eibl <sebastian.eibl@fau.de>
+//
+//======================================================================================================================
+
+//======================================================================================================================
+//
+//  THIS FILE IS GENERATED - PLEASE CHANGE THE TEMPLATE !!!
+//
+//======================================================================================================================
+
+#include <mesa_pd/data/IAccessor.h>
+#include <mesa_pd/kernel/SemiImplicitEuler.h>
+
+#include <core/UniqueID.h>
+
+#include <map>
+
+namespace walberla {
+namespace mesa_pd {
+
+class Accessor : public data::IAccessor
+{
+public:
+   ~Accessor() override = default;
+   const walberla::mesa_pd::Vec3& getPosition(const size_t /*p_idx*/) const {return position_;}
+   void setPosition(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { position_ = v;}
+   
+   const walberla::mesa_pd::Vec3& getLinearVelocity(const size_t /*p_idx*/) const {return linearVelocity_;}
+   void setLinearVelocity(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { linearVelocity_ = v;}
+   
+   const walberla::real_t& getInvMass(const size_t /*p_idx*/) const {return invMass_;}
+   
+   const walberla::mesa_pd::Vec3& getForce(const size_t /*p_idx*/) const {return force_;}
+   void setForce(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { force_ = v;}
+   
+   const walberla::mesa_pd::data::particle_flags::FlagT& getFlags(const size_t /*p_idx*/) const {return flags_;}
+   
+   const walberla::mesa_pd::Rot3& getRotation(const size_t /*p_idx*/) const {return rotation_;}
+   void setRotation(const size_t /*p_idx*/, const walberla::mesa_pd::Rot3& v) { rotation_ = v;}
+   
+   const walberla::mesa_pd::Vec3& getAngularVelocity(const size_t /*p_idx*/) const {return angularVelocity_;}
+   void setAngularVelocity(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { angularVelocity_ = v;}
+   
+   const walberla::mesa_pd::Mat3& getInvInertiaBF(const size_t /*p_idx*/) const {return invInertiaBF_;}
+   
+   const walberla::mesa_pd::Vec3& getTorque(const size_t /*p_idx*/) const {return torque_;}
+   void setTorque(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { torque_ = v;}
+   
+
+   id_t getInvalidUid() const {return UniqueID<int>::invalidID();}
+   size_t getInvalidIdx() const {return std::numeric_limits<size_t>::max();}
+   /**
+   * @brief Returns the index of particle specified by uid.
+   * @param uid unique id of the particle to be looked up
+   * @return the index of the particle or std::numeric_limits<size_t>::max() if the particle is not found
+   */
+   size_t uidToIdx(const id_t& /*uid*/) const {return 0;}
+   size_t size() const { return 1; }
+private:
+   walberla::mesa_pd::Vec3 position_;
+   walberla::mesa_pd::Vec3 linearVelocity_;
+   walberla::real_t invMass_;
+   walberla::mesa_pd::Vec3 force_;
+   walberla::mesa_pd::data::particle_flags::FlagT flags_;
+   walberla::mesa_pd::Rot3 rotation_;
+   walberla::mesa_pd::Vec3 angularVelocity_;
+   walberla::mesa_pd::Mat3 invInertiaBF_;
+   walberla::mesa_pd::Vec3 torque_;
+};
+
+
+
+} //namespace mesa_pd
+} //namespace walberla
\ No newline at end of file
diff --git a/tests/mesa_pd/kernel/interfaces/SpringDashpotInterfaceCheck.cpp b/tests/mesa_pd/kernel/interfaces/SpringDashpotInterfaceCheck.cpp
index b7662042833f7dbacdd429063ba2930be8ab0ecd..2095004ede17783c87ec8bc0885328206720e4ff 100644
--- a/tests/mesa_pd/kernel/interfaces/SpringDashpotInterfaceCheck.cpp
+++ b/tests/mesa_pd/kernel/interfaces/SpringDashpotInterfaceCheck.cpp
@@ -13,7 +13,7 @@
 //  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 SpringDashpotInterfaceCheck.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -37,7 +37,7 @@ namespace mesa_pd {
 class Accessor : public data::IAccessor
 {
 public:
-   virtual ~Accessor() = default;
+   ~Accessor() override = default;
    const walberla::mesa_pd::Vec3& getPosition(const size_t /*p_idx*/) const {return position_;}
    
    const walberla::mesa_pd::Vec3& getLinearVelocity(const size_t /*p_idx*/) const {return linearVelocity_;}
diff --git a/tests/mesa_pd/kernel/interfaces/TemperatureIntegrationInterfaceCheck.cpp b/tests/mesa_pd/kernel/interfaces/TemperatureIntegrationInterfaceCheck.cpp
index dfc209c39f2d3f6890d1017ab687064d1f7d9deb..e85086212ff81d94da2805135a18e7056cbb235c 100644
--- a/tests/mesa_pd/kernel/interfaces/TemperatureIntegrationInterfaceCheck.cpp
+++ b/tests/mesa_pd/kernel/interfaces/TemperatureIntegrationInterfaceCheck.cpp
@@ -13,7 +13,7 @@
 //  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 TemperatureIntegrationInterfaceCheck.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -37,7 +37,7 @@ namespace mesa_pd {
 class Accessor : public data::IAccessor
 {
 public:
-   virtual ~Accessor() = default;
+   ~Accessor() override = default;
    const walberla::real_t& getTemperature(const size_t /*p_idx*/) const {return temperature_;}
    void setTemperature(const size_t /*p_idx*/, const walberla::real_t& v) { temperature_ = v;}
    
diff --git a/tests/mesa_pd/kernel/interfaces/VelocityVerletInterfaceCheck.cpp b/tests/mesa_pd/kernel/interfaces/VelocityVerletInterfaceCheck.cpp
index 06d8507c798a0b91a2af7d5931ca4ae18f62f913..869b4bb897cec19d051e2d46727c2f7cb1a9408d 100644
--- a/tests/mesa_pd/kernel/interfaces/VelocityVerletInterfaceCheck.cpp
+++ b/tests/mesa_pd/kernel/interfaces/VelocityVerletInterfaceCheck.cpp
@@ -13,7 +13,7 @@
 //  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 VelocityVerletInterfaceCheck.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -37,7 +37,7 @@ namespace mesa_pd {
 class Accessor : public data::IAccessor
 {
 public:
-   virtual ~Accessor() = default;
+   ~Accessor() override = default;
    const walberla::mesa_pd::Vec3& getPosition(const size_t /*p_idx*/) const {return position_;}
    void setPosition(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { position_ = v;}
    
diff --git a/tests/mesa_pd/kernel/interfaces/VelocityVerletWithShapeInterfaceCheck.cpp b/tests/mesa_pd/kernel/interfaces/VelocityVerletWithShapeInterfaceCheck.cpp
index 06d8507c798a0b91a2af7d5931ca4ae18f62f913..869b4bb897cec19d051e2d46727c2f7cb1a9408d 100644
--- a/tests/mesa_pd/kernel/interfaces/VelocityVerletWithShapeInterfaceCheck.cpp
+++ b/tests/mesa_pd/kernel/interfaces/VelocityVerletWithShapeInterfaceCheck.cpp
@@ -13,7 +13,7 @@
 //  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 VelocityVerletInterfaceCheck.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
@@ -37,7 +37,7 @@ namespace mesa_pd {
 class Accessor : public data::IAccessor
 {
 public:
-   virtual ~Accessor() = default;
+   ~Accessor() override = default;
    const walberla::mesa_pd::Vec3& getPosition(const size_t /*p_idx*/) const {return position_;}
    void setPosition(const size_t /*p_idx*/, const walberla::mesa_pd::Vec3& v) { position_ = v;}
    
diff --git a/tests/mesa_pd/mpi/BroadcastProperty.cpp b/tests/mesa_pd/mpi/BroadcastProperty.cpp
index de7a0a2d79576ec51173acecf3de2061c4a1e219..6082d41e42f07b29108fb96d807d539bea2715da 100644
--- a/tests/mesa_pd/mpi/BroadcastProperty.cpp
+++ b/tests/mesa_pd/mpi/BroadcastProperty.cpp
@@ -13,7 +13,7 @@
 //  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   BroadcastProperty.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/mpi/ClearGhostOwnerSync.cpp b/tests/mesa_pd/mpi/ClearGhostOwnerSync.cpp
index 84bd3f1bc452e4343d228055bb0ed571542e46e3..6940993fbb38795770781ce4ae235aedac844abc 100644
--- a/tests/mesa_pd/mpi/ClearGhostOwnerSync.cpp
+++ b/tests/mesa_pd/mpi/ClearGhostOwnerSync.cpp
@@ -13,7 +13,7 @@
 //  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 ClearGhostOwnerSync.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/mpi/ClearNextNeighborSync.cpp b/tests/mesa_pd/mpi/ClearNextNeighborSync.cpp
index 19c1ebfa6ef9cfba8099a64e3fb415ac3c8e0261..9c8d71ee11d890d53d4e6b8fb9c15d88bfed3545 100644
--- a/tests/mesa_pd/mpi/ClearNextNeighborSync.cpp
+++ b/tests/mesa_pd/mpi/ClearNextNeighborSync.cpp
@@ -13,7 +13,7 @@
 //  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 ClearNextNeighborSync.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/mpi/Notifications.cpp b/tests/mesa_pd/mpi/Notifications.cpp
index e6eeacca6ed0562666cd8e2adcb4aeb8c5f76513..5f432d21ba037f87a9cef469cc6f6c3c368d595a 100644
--- a/tests/mesa_pd/mpi/Notifications.cpp
+++ b/tests/mesa_pd/mpi/Notifications.cpp
@@ -13,7 +13,7 @@
 //  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   Notifications.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/mpi/ReduceContactHistory.cpp b/tests/mesa_pd/mpi/ReduceContactHistory.cpp
index 1135e1d9382690dcc23bc50b607337d4addd5191..0414ed75ddaf1ae0b77df03d39b682494cdae1e1 100644
--- a/tests/mesa_pd/mpi/ReduceContactHistory.cpp
+++ b/tests/mesa_pd/mpi/ReduceContactHistory.cpp
@@ -13,7 +13,7 @@
 //  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   ReduceContactHistory.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/mpi/ReduceProperty.cpp b/tests/mesa_pd/mpi/ReduceProperty.cpp
index 3f8138ac942f553831adf3854aeca0539822540b..0c5335cd08e88e3f124b791cb08c37acee0c8bfa 100644
--- a/tests/mesa_pd/mpi/ReduceProperty.cpp
+++ b/tests/mesa_pd/mpi/ReduceProperty.cpp
@@ -13,7 +13,7 @@
 //  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   ReduceProperty.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/mpi/ShapePackUnpack.cpp b/tests/mesa_pd/mpi/ShapePackUnpack.cpp
index 3efe46404386cc2a9591c403e35825dfe4b1a157..16cca30ffde43fafae282bf1b4fba715758a6bbe 100644
--- a/tests/mesa_pd/mpi/ShapePackUnpack.cpp
+++ b/tests/mesa_pd/mpi/ShapePackUnpack.cpp
@@ -13,7 +13,7 @@
 //  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   PackUnpack.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp b/tests/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp
index 1dae83425ab8a034c9d46117ed0487aedfec99e3..1df6e0ae84030910a55de5f5ea724384e28fccb6 100644
--- a/tests/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp
+++ b/tests/mesa_pd/mpi/SyncNextNeighborsNoGhosts.cpp
@@ -13,7 +13,7 @@
 //  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   SyncNextNeighborsNoGhosts.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/mpi/VelocityCorrectionNotification.cpp b/tests/mesa_pd/mpi/VelocityCorrectionNotification.cpp
index 79cd6351519295c35e4ab62dd7106537fdc3752e..0b66e9fc0a9097cf4e30d300f7af25821fda2031 100644
--- a/tests/mesa_pd/mpi/VelocityCorrectionNotification.cpp
+++ b/tests/mesa_pd/mpi/VelocityCorrectionNotification.cpp
@@ -13,7 +13,7 @@
 //  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   VelocityCorrectionNotification.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/vtk/ConvexPolyhedronVTKOutput.cpp b/tests/mesa_pd/vtk/ConvexPolyhedronVTKOutput.cpp
index 124493ca93043b9aa6955b5f3444fb4690845e99..f734d0d0a23d24563b2fc1ff364fc780ba92c8d2 100644
--- a/tests/mesa_pd/vtk/ConvexPolyhedronVTKOutput.cpp
+++ b/tests/mesa_pd/vtk/ConvexPolyhedronVTKOutput.cpp
@@ -13,7 +13,7 @@
 //  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 VTKOutput.cpp
+//! \file
 //! \author Lukas Werner
 //
 //======================================================================================================================
diff --git a/tests/mesa_pd/vtk/VTKOutputs.cpp b/tests/mesa_pd/vtk/VTKOutputs.cpp
index 7cd96b260583b75b828e4d4af4ce67b0bd5ec0a2..ee1a02d47c8128422ccf58cf4833ed5b180de396 100644
--- a/tests/mesa_pd/vtk/VTKOutputs.cpp
+++ b/tests/mesa_pd/vtk/VTKOutputs.cpp
@@ -13,7 +13,7 @@
 //  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   VTKOutputs.cpp
+//! \file
 //! \author Sebastian Eibl <sebastian.eibl@fau.de>
 //
 //======================================================================================================================
diff --git a/tests/mesh/MeshOperationsTest.cpp b/tests/mesh/MeshOperationsTest.cpp
index 6cceae10e9a75ee79a88aa3b2b80737df68a3d94..a17d14c7246b1b8fe59c897ead2fe6abf58d0cc9 100644
--- a/tests/mesh/MeshOperationsTest.cpp
+++ b/tests/mesh/MeshOperationsTest.cpp
@@ -120,6 +120,28 @@ void testCube()
    WALBERLA_CHECK_FLOAT_EQUAL( inertiaTensor(0,0), aabb.volume() * ( aabb.ySize() * aabb.ySize() + aabb.zSize() * aabb.zSize() ) / real_t(12) );
    WALBERLA_CHECK_FLOAT_EQUAL( inertiaTensor(1,1), aabb.volume() * ( aabb.xSize() * aabb.xSize() + aabb.zSize() * aabb.zSize() ) / real_t(12) );
    WALBERLA_CHECK_FLOAT_EQUAL( inertiaTensor(2,2), aabb.volume() * ( aabb.xSize() * aabb.xSize() + aabb.ySize() * aabb.ySize() ) / real_t(12) );
+
+
+   rotate(mesh, Vector3<Scalar>{0, 0, 1}, static_cast<Scalar>(M_PI_2), Vector3<Scalar>{-4, 0, 0});
+   aabb = computeAABB( mesh );
+
+   WALBERLA_CHECK_FLOAT_EQUAL( aabb.xMin(), Scalar(-5.5) );
+   WALBERLA_CHECK_FLOAT_EQUAL( aabb.yMin(), Scalar(3) );
+   WALBERLA_CHECK_FLOAT_EQUAL( aabb.zMin(), Scalar(-0.25) );
+
+   WALBERLA_CHECK_FLOAT_EQUAL( aabb.xMax(), Scalar(-2.5) );
+   WALBERLA_CHECK_FLOAT_EQUAL( aabb.yMax(), Scalar(5) );
+   WALBERLA_CHECK_FLOAT_EQUAL( aabb.zMax(), Scalar(0.25) );
+
+
+   WALBERLA_CHECK_FLOAT_EQUAL( computeSurfaceArea( mesh ), Scalar(2) * ( aabb.xSize() * aabb.ySize() + aabb.xSize() * aabb.zSize() + aabb.ySize() * aabb.zSize() ) );
+   WALBERLA_CHECK_FLOAT_EQUAL( computeVolume( mesh ),  aabb.volume() );
+   centroid = computeCentroid( mesh );
+   aabbCenter = aabb.center();
+   WALBERLA_CHECK_FLOAT_EQUAL( centroid[0], aabbCenter[0] );
+   WALBERLA_CHECK_FLOAT_EQUAL( centroid[1], aabbCenter[1] );
+   WALBERLA_CHECK_FLOAT_EQUAL( centroid[2], aabbCenter[2] );
+
 }
 
 int main( int argc, char * argv[] )
diff --git a/tests/pe/SyncEquivalence.cpp b/tests/pe/SyncEquivalence.cpp
index d17e2f0f19d3e14fc3c128f4a386711479726b23..6c03180186aaf7f4bb42e26bc7930414269ee6c9 100644
--- a/tests/pe/SyncEquivalence.cpp
+++ b/tests/pe/SyncEquivalence.cpp
@@ -18,8 +18,6 @@
 //
 //======================================================================================================================
 
-#include "python_coupling/DictWrapper.h"
-
 #include "blockforest/all.h"
 #include "core/all.h"
 #include "domain_decomposition/all.h"
diff --git a/tests/pe_coupling/momentum_exchange_method/TaylorCouetteFlowMEM.cpp b/tests/pe_coupling/momentum_exchange_method/TaylorCouetteFlowMEM.cpp
index 67ce8c233fcc0d50a0c579fcf7d76255f9f071b0..2fad6814a2a32c8644fbfe8998a55e9dc0b1669a 100644
--- a/tests/pe_coupling/momentum_exchange_method/TaylorCouetteFlowMEM.cpp
+++ b/tests/pe_coupling/momentum_exchange_method/TaylorCouetteFlowMEM.cpp
@@ -247,7 +247,7 @@ int main( int argc, char **argv )
       WALBERLA_ABORT("Unrecognized command line argument found: " << argv[i]);
    }
 
-   bool vtkIO =  (vtkIOFreq == 0 ) ? false : true;
+   bool vtkIO =  vtkIOFreq != 0;
 
    ///////////////////////////
    // SIMULATION PROPERTIES //
diff --git a/tests/python_coupling/BasicDatatypeTest.py b/tests/python_coupling/BasicDatatypeTest.py
deleted file mode 100644
index bd5deff65d7dbb54f6195ecaa9b90bdb72692726..0000000000000000000000000000000000000000
--- a/tests/python_coupling/BasicDatatypeTest.py
+++ /dev/null
@@ -1,26 +0,0 @@
-import unittest
-import waLBerla as wlb
-
-
-class BasicDatatypesTest(unittest.TestCase):
-
-    def test_CellInterval(self):
-        ci1 = wlb.CellInterval(0, 0, 0, 5, 5, 5)
-        ci2 = wlb.CellInterval([0] * 3, [5] * 3)
-        self.assertEqual(ci1, ci2, "Equality comparison of CellIntervals failed.")
-        self.assertFalse(ci1 != ci2, "Inequality check for CellIntervals wrong ")
-
-        self.assertEqual(ci1.min, (0, 0, 0), "CellInterval min wrong")
-        self.assertEqual(ci1.max, (5, 5, 5), "CellInterval max wrong")
-
-        self.assertFalse(ci1.empty())
-
-        ci1.intersect(ci2)
-        self.assertTrue(ci1.contains(ci2))
-
-        ci2.expand(1)
-        self.assertFalse(ci1.contains(ci2))
-
-    def test_AABB(self):
-        aabb1 = wlb.AABB(0, 0, 0, 5, 5, 5)  # noqa: F841
-        aabb2 = wlb.AABB([0] * 3, [5] * 3)  # noqa: F841
diff --git a/tests/python_coupling/CMakeLists.txt b/tests/python_coupling/CMakeLists.txt
index 8e7c48fc9daca1b1f569527acccbad1dc42de6d7..462c2b8f201e78ac51bd7d793c3198a6214a2e6a 100644
--- a/tests/python_coupling/CMakeLists.txt
+++ b/tests/python_coupling/CMakeLists.txt
@@ -1,6 +1,6 @@
 ###################################################################################################
 #
-# Tests for timeloop module
+# Tests for python coupling
 #
 ###################################################################################################
 
@@ -13,9 +13,16 @@ if (WALBERLA_BUILD_WITH_PYTHON)
                            
     waLBerla_compile_test( FILES CallbackTest.cpp  DEPENDS blockforest field )
     waLBerla_execute_test( NAME CallbackTest
-                          COMMAND $<TARGET_FILE:CallbackTest> ${CMAKE_CURRENT_SOURCE_DIR}/CallbackTest.py ) 
+                          COMMAND $<TARGET_FILE:CallbackTest> ${CMAKE_CURRENT_SOURCE_DIR}/CallbackTest.py )
+    set_target_properties(CallbackTest PROPERTIES CXX_VISIBILITY_PRESET hidden)
 
     waLBerla_compile_test( FILES FieldExportTest.cpp  DEPENDS blockforest field )
     waLBerla_execute_test( NAME FieldExportTest
                            COMMAND $<TARGET_FILE:FieldExportTest> ${CMAKE_CURRENT_SOURCE_DIR}/FieldExportTest.py )
+    set_target_properties(FieldExportTest PROPERTIES CXX_VISIBILITY_PRESET hidden)
+
+    add_test( NAME PythonWalberlaTest
+          COMMAND python3 -m unittest discover waLBerla_tests/ WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}/python/)
+    SET_TESTS_PROPERTIES(PythonWalberlaTest
+          PROPERTIES ENVIRONMENT "PYTHONPATH=${CMAKE_BINARY_DIR}/apps/pythonmodule:$PYTHONPATH")
 endif()
diff --git a/tests/python_coupling/CallbackTest.cpp b/tests/python_coupling/CallbackTest.cpp
index 95d210b76b683b17bb4d5f259600d46f846a1182..08790f31f02a810a17aa30faf26a33f3b2f1156d 100644
--- a/tests/python_coupling/CallbackTest.cpp
+++ b/tests/python_coupling/CallbackTest.cpp
@@ -13,33 +13,30 @@
 //  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 CallbackTest.h
-//! \author Martin Bauer <martin.bauer@fau.de>
-//
+//! \file CallbackTest.cpp
+//! \author Markus Holzer <markus.holzer@fau.de>
+//!
 //======================================================================================================================
 
-#include "python_coupling/PythonWrapper.h"
+#include "blockforest/Initialization.h"
 
-#include "core/debug/TestSubsystem.h"
 #include "core/Environment.h"
+#include "core/debug/TestSubsystem.h"
 
 #include "field/Field.h"
-#include "field/python/Exports.h"
-#include "blockforest/Initialization.h"
-#include "blockforest/python/Exports.h"
-#include "python_coupling/PythonCallback.h"
-#include "python_coupling/Shell.h"
+
 #include "python_coupling/DictWrapper.h"
 #include "python_coupling/Manager.h"
+#include "python_coupling/PythonCallback.h"
+#include "python_coupling/export/FieldExports.h"
 
 using namespace walberla;
 
 
 int main( int argc, char ** argv )
 {
-   typedef boost::mpl::vector< Field<int,1> > Fields;
    auto pythonManager = python_coupling::Manager::instance();
-   pythonManager->addExporterFunction( field::exportModuleToPython<Fields> );
+   pythonManager->addExporterFunction( field::exportModuleToPython<Field<int,1>> );
 
    if ( argc != 2 ) {
       WALBERLA_ABORT_NO_DEBUG_INFO("Wrong parameter count: \nUsage: \n ./CallbackTest CallbackTest.py");
diff --git a/tests/python_coupling/CallbackTest.py b/tests/python_coupling/CallbackTest.py
index e6aa0a7b3bfae333b86238d9e431c2bf8f159748..95381f4158494b213e804239b4a865ebd6f3a635 100644
--- a/tests/python_coupling/CallbackTest.py
+++ b/tests/python_coupling/CallbackTest.py
@@ -9,9 +9,9 @@ def someCallback(input1, input2):
 
 @waLBerla.callback("cb2")
 def fieldCallback(field):
-    npArray = waLBerla.field.toArray(field)
-    npArray[0, 0, 0] = 42
+    numpy_array = waLBerla.field.toArray(field)
+    numpy_array[0, 0, 0] = 42
 
-    npArrayGl = waLBerla.field.toArray(field, withGhostLayers=True)
-    print(npArrayGl.shape)
-    npArrayGl[0, 0, 0] = 5
+    numpy_array_with_gl = waLBerla.field.toArray(field, with_ghost_layers=True)
+    print(numpy_array_with_gl.shape)
+    numpy_array_with_gl[0, 0, 0] = 5
diff --git a/tests/python_coupling/ConfigFromPythonTest.cpp b/tests/python_coupling/ConfigFromPythonTest.cpp
index 527cc3e559e626fcd2deb9633af5d668f78d1509..7b3437463d4fffc0cb3c4f50c123ab9e43ad913b 100644
--- a/tests/python_coupling/ConfigFromPythonTest.cpp
+++ b/tests/python_coupling/ConfigFromPythonTest.cpp
@@ -1,52 +1,65 @@
 //======================================================================================================================
 //
-//  This file is part of waLBerla. waLBerla is free software: you can 
+//  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 
+//  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 
+//
+//  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 ConfigFromPythonTest.cpp
 //! \ingroup core
-//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
 #include "core/Abort.h"
 #include "core/debug/TestSubsystem.h"
-#include "core/mpi/MPIManager.h"
+#include "core/logging/Logging.h"
+#include "core/math/Vector3.h"
 #include "core/mpi/Environment.h"
+
 #include "python_coupling/CreateConfig.h"
 
 using namespace walberla;
 
-
-int main( int argc, char** argv )
+int main(int argc, char** argv)
 {
    debug::enterTestMode();
 
-   mpi::Environment env( argc, argv );
+   mpi::Environment env(argc, argv);
 
-   auto config =  python_coupling::createConfigFromPythonScript( argv[1] );
+   int counter = 0;
+   for (auto cfg = python_coupling::configBegin(argc, argv); cfg != python_coupling::configEnd(); ++cfg)
+   {
+      auto config                  = *cfg;
+      auto parameters              = config->getOneBlock("DomainSetup");
+      const int test_int           = parameters.getParameter< int >("testInt");
+      const std::string testString = parameters.getParameter< std::string >("testString");
+      const real_t testDouble      = parameters.getParameter< real_t >("testDouble");
+      Vector3< real_t > testVector = parameters.getParameter< Vector3< real_t > >("testVector");
+      const bool testBool          = parameters.getParameter< bool >("testBool");
 
-   WALBERLA_CHECK_EQUAL( argc, 2 );
+      if (counter == 0)
+         WALBERLA_CHECK(test_int == 4)
+      else
+         WALBERLA_CHECK(test_int == 5)
 
-   config->listParameters();
-   WALBERLA_CHECK_EQUAL      ( int        ( config->getParameter<int>        ("testInt")  ),    4          );
-   WALBERLA_CHECK_EQUAL      ( std::string( config->getParameter<std::string>("testString")), "someString" );
-   WALBERLA_CHECK_FLOAT_EQUAL( double     ( config->getParameter<real_t>     ("testDouble")), real_t(42.42));
+      counter++;
 
+      WALBERLA_CHECK(testString == "someString")
+      WALBERLA_CHECK(testDouble > 42 && testDouble < 43)
+      WALBERLA_CHECK(testVector == Vector3< real_t >(0.5, 0.5, 0.7))
+      WALBERLA_CHECK(testBool == false)
 
-   auto subBlock = config->getBlock("subBlock");
-   WALBERLA_CHECK_EQUAL      ( std::string( subBlock.getParameter<std::string>("subKey1") ), std::string("abc") );
-   WALBERLA_CHECK_EQUAL      ( std::string( subBlock.getParameter<std::string>("subKey2") ), std::string("def") );
+      WALBERLA_LOG_INFO_ON_ROOT(test_int)
+   }
 
-   return 0;
+   return EXIT_SUCCESS;
 }
diff --git a/tests/python_coupling/ConfigFromPythonTest.py b/tests/python_coupling/ConfigFromPythonTest.py
index 9c4a4f45acd31777057ae3dafc5809c88c1336b8..d319203af35f01a22ea802ec59d072a893334425 100644
--- a/tests/python_coupling/ConfigFromPythonTest.py
+++ b/tests/python_coupling/ConfigFromPythonTest.py
@@ -1,15 +1,27 @@
-import waLBerla
-
-
-@waLBerla.callback("config")
-def waLBerlaConfig():
-    conf = {
-        'testInt': 4,
-        'testString': "someString",
-        'testDouble': 42.42,
-        '44242': 'ohoh_IntegerKey',
-        'subBlock': {'subKey1': 'abc',
-                     'subKey2': 'def'
-                     }
-    }
-    return conf
+import waLBerla as wlb
+
+
+class Scenario:
+    def __init__(self, number):
+        self.testInt = number
+        self.testString = "someString"
+        self.testDouble = 42.42
+        self.testVector = (0.5, 0.5, 0.7)
+        self.testBool = False
+
+    @wlb.member_callback
+    def config(self):
+        return {
+            'DomainSetup': {
+                'testInt': self.testInt,
+                'testDouble': self.testDouble,
+                'testString': self.testString,
+                'testVector': self.testVector,
+                'testBool': self.testBool
+            }
+        }
+
+
+scenarios = wlb.ScenarioManager()
+scenarios.add(Scenario(4))
+scenarios.add(Scenario(5))
diff --git a/tests/python_coupling/FieldExportTest.cpp b/tests/python_coupling/FieldExportTest.cpp
index 05997b58a12dfb63da6737b1dd0c81a838469c3b..b78bc36828ada3cad436f49d2e103699fc81240e 100644
--- a/tests/python_coupling/FieldExportTest.cpp
+++ b/tests/python_coupling/FieldExportTest.cpp
@@ -13,28 +13,27 @@
 //  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 FieldExportTest.h
-//! \author Martin Bauer <martin.bauer@fau.de>
+//! \file FieldExportTest.cpp
+//! //! \author Markus Holzer <markus.holzer@fau.de>
 //
 //======================================================================================================================
 
-#include "python_coupling/PythonWrapper.h"
+#include "blockforest/Initialization.h"
 
 #include "core/Environment.h"
 #include "core/debug/TestSubsystem.h"
+#include "core/math/Random.h"
 #include "core/math/Vector2.h"
 #include "core/math/Vector3.h"
-#include "core/math/Random.h"
 
-#include "blockforest/Initialization.h"
-#include "blockforest/python/Exports.h"
-#include "field/python/Exports.h"
+#include "python_coupling/DictWrapper.h"
 #include "python_coupling/Manager.h"
 #include "python_coupling/PythonCallback.h"
-#include "python_coupling/DictWrapper.h"
-#include "stencil/D2Q9.h"
+#include "python_coupling/PythonWrapper.h"
+#include "python_coupling/export/BlockForestExport.h"
+#include "python_coupling/export/FieldExports.h"
 
-#include <boost/mpl/vector.hpp>
+#include "stencil/D2Q9.h"
 
 using namespace walberla;
 
@@ -46,36 +45,29 @@ int main( int argc, char ** argv )
    mpi::Environment mpiEnv( argc, argv );
 
    auto pythonManager = python_coupling::Manager::instance();
-   typedef boost::mpl::vector<
-           Field<Vector2<int>,1>,
-           Field<Vector3<int>,1>,
-           Field<int,2>,
-           Field<int,3> > FieldTypes;
-
-   typedef boost::mpl::vector< stencil::D2Q9> Stencils;
 
-   pythonManager->addExporterFunction( field::exportModuleToPython<FieldTypes> );
-   pythonManager->addBlockDataConversion< FieldTypes >() ;
-   pythonManager->addExporterFunction( blockforest::exportModuleToPython<Stencils> );
+   pythonManager->addExporterFunction( field::exportModuleToPython<Field<int, 3>, Field<real_t, 3>> );
+   pythonManager->addBlockDataConversion< Field<int, 3>, Field<real_t, 3> >() ;
+   pythonManager->addExporterFunction( blockforest::exportModuleToPython<stencil::D2Q9> );
 
 
    shared_ptr< StructuredBlockForest > blocks = blockforest::createUniformBlockGrid( 1,1,1, 20,20,1, real_t(1.0), false, true,true,true );
 
-   auto sca2FieldID = field::addToStorage< GhostLayerField<int,2> >( blocks, "sca2Field", int(0), field::fzyx, 1 );
-   auto sca3FieldID = field::addToStorage< GhostLayerField<int,3> >( blocks, "sca3Field", int(0), field::fzyx, 1 );
+   auto srcIntFieldID = field::addToStorage< GhostLayerField<int, 3> >( blocks, "srcIntFieldID", int(0), field::fzyx, 1 );
+   auto dstIntFieldID = field::addToStorage< GhostLayerField<int, 3> >( blocks, "dstIntFieldID", int(0), field::fzyx, 1 );
 
-   auto vec2FieldID = field::addToStorage< GhostLayerField<Vector2<int>,1> >( blocks, "vec2Field", Vector2<int>(), field::zyxf, 1 );
-   auto vec3FieldID = field::addToStorage< GhostLayerField<Vector3<int>,1> >( blocks, "vec3Field", Vector3<int>(), field::zyxf, 1 );
+   auto srcDoubleFieldID = field::addToStorage< GhostLayerField<real_t, 3> >( blocks, "srcDoubleFieldID", real_t(0.0), field::fzyx, 1 );
+   auto dstDoubleFieldID = field::addToStorage< GhostLayerField<real_t, 3> >( blocks, "dstDoubleFieldID", real_t(0.0), field::fzyx, 1 );
 
    // random init
    for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )
    {
-      auto sca2Field = blockIt->getData<GhostLayerField<int,2> >( sca2FieldID );
-      auto sca3Field = blockIt->getData<GhostLayerField<int,3> >( sca3FieldID );
-      for( auto cellIt = sca2Field->begin(); cellIt != sca2Field->end(); ++cellIt )
-         *cellIt = math::intRandom( int(0), int(42) );
-      for( auto cellIt = sca3Field->begin(); cellIt != sca3Field->end(); ++cellIt )
+      auto srcIntField = blockIt->getData<GhostLayerField<int, 3> >( srcIntFieldID );
+      auto srcDoubleField = blockIt->getData<GhostLayerField<real_t, 3> >( srcDoubleFieldID );
+      for( auto cellIt = srcIntField->begin(); cellIt != srcIntField->end(); ++cellIt )
          *cellIt = math::intRandom( int(0), int(42) );
+      for( auto cellIt = srcDoubleField->begin(); cellIt != srcDoubleField->end(); ++cellIt )
+         *cellIt = math::realRandom( real_t(0.0), real_t(42.0) );
    }
 
    // call python function which should copy over the values to the Vector fields
@@ -89,26 +81,26 @@ int main( int argc, char ** argv )
    // check for equivalence
    for( auto blockIt = blocks->begin(); blockIt != blocks->end(); ++blockIt )
    {
-      auto sca2Field = blockIt->getData<GhostLayerField<int,2> >( sca2FieldID );
-      auto sca3Field = blockIt->getData<GhostLayerField<int,3> >( sca3FieldID );
-      auto vec2Field = blockIt->getData<GhostLayerField<Vector2<int>,1 > >( vec2FieldID );
-      auto vec3Field = blockIt->getData<GhostLayerField<Vector3<int>,1 > >( vec3FieldID );
+      auto srcIntField = blockIt->getData<GhostLayerField<int, 3> >( srcIntFieldID );
+      auto dstIntField = blockIt->getData<GhostLayerField<int, 3> >( dstIntFieldID );
+      auto srcDoubleField = blockIt->getData<GhostLayerField<real_t, 3> >( srcDoubleFieldID );
+      auto dstDoubleField = blockIt->getData<GhostLayerField<real_t, 3> >( dstDoubleFieldID );
 
       {
-         for(cell_idx_t z = 0; z < cell_idx_c(sca2Field->zSize()); ++z)
-            for(cell_idx_t y = 0; y < cell_idx_c(sca2Field->zSize()); ++y)
-               for(cell_idx_t x = 0; x < cell_idx_c(sca2Field->zSize()); ++x)
+         for(cell_idx_t z = 0; z < cell_idx_c(srcIntField->zSize()); ++z)
+            for(cell_idx_t y = 0; y < cell_idx_c(srcIntField->zSize()); ++y)
+               for(cell_idx_t x = 0; x < cell_idx_c(srcIntField->zSize()); ++x)
                {
-                  WALBERLA_CHECK_EQUAL( sca2Field->get(x,y,z, 0), vec2Field->get(x,y,z)[0] );
-                  WALBERLA_CHECK_EQUAL( sca2Field->get(x,y,z, 1), vec2Field->get(x,y,z)[1] );
+                  WALBERLA_CHECK_EQUAL( srcIntField->get(x,y,z, 0), dstIntField->get(x,y,z, 0) );
+                  WALBERLA_CHECK_EQUAL( srcIntField->get(x,y,z, 1), dstIntField->get(x,y,z, 1) );
                }
-         for(cell_idx_t z = 0; z < cell_idx_c(sca3Field->zSize()); ++z)
-            for(cell_idx_t y = 0; y < cell_idx_c(sca3Field->zSize()); ++y)
-               for(cell_idx_t x = 0; x < cell_idx_c(sca3Field->zSize()); ++x)
+         for(cell_idx_t z = 0; z < cell_idx_c(srcDoubleField->zSize()); ++z)
+            for(cell_idx_t y = 0; y < cell_idx_c(srcDoubleField->zSize()); ++y)
+               for(cell_idx_t x = 0; x < cell_idx_c(srcDoubleField->zSize()); ++x)
                {
-                  WALBERLA_CHECK_EQUAL( sca3Field->get(x,y,z, 0), vec3Field->get(x,y,z)[0] );
-                  WALBERLA_CHECK_EQUAL( sca3Field->get(x,y,z, 1), vec3Field->get(x,y,z)[1] );
-                  WALBERLA_CHECK_EQUAL( sca3Field->get(x,y,z, 2), vec3Field->get(x,y,z)[2] );
+                  WALBERLA_CHECK_FLOAT_EQUAL( srcDoubleField->get(x,y,z, 0), dstDoubleField->get(x,y,z, 0) );
+                  WALBERLA_CHECK_FLOAT_EQUAL( srcDoubleField->get(x,y,z, 1), dstDoubleField->get(x,y,z, 1) );
+                  WALBERLA_CHECK_FLOAT_EQUAL( srcDoubleField->get(x,y,z, 2), dstDoubleField->get(x,y,z, 2) );
                }
       }
    }
diff --git a/tests/python_coupling/FieldExportTest.py b/tests/python_coupling/FieldExportTest.py
index 7790e9d2cdc5655a26036f49d02b532ccc038503..98da1c797b492fb4c9eca57056866ee105edabd0 100644
--- a/tests/python_coupling/FieldExportTest.py
+++ b/tests/python_coupling/FieldExportTest.py
@@ -6,5 +6,5 @@ import numpy as np
 @waLBerla.callback("theCallback")
 def theCallback(blocks):
     for block in blocks:
-        np.copyto(toArray(block['vec2Field']), toArray(block['sca2Field']))
-        np.copyto(toArray(block['vec3Field']), toArray(block['sca3Field']))
+        np.copyto(toArray(block['srcIntFieldID']), toArray(block['dstIntFieldID']))
+        np.copyto(toArray(block['srcDoubleFieldID']), toArray(block['dstDoubleFieldID']))
diff --git a/tests/simd/CMakeLists.txt b/tests/simd/CMakeLists.txt
index 060dd5b616511af6b610875464cab33a6deb2c60..843dd93158ead79e0aabfdeac00cd909ebea4054 100644
--- a/tests/simd/CMakeLists.txt
+++ b/tests/simd/CMakeLists.txt
@@ -8,12 +8,8 @@
 
 # builds the test
 
-if ( CMAKE_COMPILER_IS_GNUCXX )
-   if( CMAKE_CXX_COMPILER_VERSION VERSION_LESS "4.7.0" )
-      set( MarchNativeString "" )
-   else()
-      set( MarchNativeString "-march=native" )
-   endif()
+if ( WALBERLA_CXX_COMPILER_IS_GNU OR (WALBERLA_CXX_COMPILER_IS_CLANG AND (NOT CMAKE_CXX_COMPILER_ID STREQUAL "AppleClang" OR NOT CMAKE_HOST_SYSTEM_PROCESSOR STREQUAL "arm64")) )
+  set( MarchNativeString "-march=native" )
 endif()
 
 waLBerla_compile_test( NAME   AVX2_AVX_Equivalence FILES SIMD_Equivalence.cpp  )
diff --git a/tests/timeloop/CMakeLists.txt b/tests/timeloop/CMakeLists.txt
index 265f266fd7cb57080a837f54609711ba3401b1df..862c3b64811bc9ba69a28a88420bce355cf90662 100644
--- a/tests/timeloop/CMakeLists.txt
+++ b/tests/timeloop/CMakeLists.txt
@@ -5,5 +5,5 @@
 ###################################################################################################
 
 
-#waLBerla_compile_test( FILES TimeloopAndSweepRegister.cpp DEPENDS field blockforest )
-#waLBerla_execute_test(NAME TimeloopAndSweepRegister )
+waLBerla_compile_test( FILES TimeloopAndSweepRegister.cpp DEPENDS field blockforest )
+waLBerla_execute_test(NAME TimeloopAndSweepRegister )
diff --git a/tests/timeloop/TimeloopAndSweepRegister.cpp b/tests/timeloop/TimeloopAndSweepRegister.cpp
index 9e0a2d39bf242b9c10cf1ae73de599289e852893..94421082135cabe352a01fbb85c550e10a8b66ec 100644
--- a/tests/timeloop/TimeloopAndSweepRegister.cpp
+++ b/tests/timeloop/TimeloopAndSweepRegister.cpp
@@ -1,176 +1,129 @@
 //======================================================================================================================
 //
-//  This file is part of waLBerla. waLBerla is free software: you can 
+//  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 
+//  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 
+//
+//  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 TimeloopAndSweepRegister.cpp
 //! \ingroup timeloop
-//! \author Martin Bauer <martin.bauer@fau.de>
+//! \author Markus Holzer <markus.holzer@fau.de>
 //! \brief test cases that test the registering of Sweeps at timeloop
 //
 //======================================================================================================================
 
-#include "timeloop/SweepTimeloop.h"
+#include "blockforest/Initialization.h"
+
 #include "core/DataTypes.h"
+#include "core/Environment.h"
 #include "core/debug/TestSubsystem.h"
-#include "core/logging/Logging.h"
+
+#include "field/Field.h"
+
+#include "timeloop/SweepTimeloop.h"
 
 #include <string>
 #include <vector>
 
-
-using namespace std;
-using namespace boost;
 using namespace walberla;
 
+using Field_T = Field< uint_t, 1 >;
+
+auto FieldAdder = [](IBlock* const block, StructuredBlockStorage* const storage) {
+   return new Field_T(storage->getNumberOfXCells(*block), storage->getNumberOfYCells(*block),
+                      storage->getNumberOfZCells(*block), uint_t(0.0), field::fzyx,
+                      make_shared< field::AllocateAligned< uint_t, 64 > >());
+};
 
-class GeneralSweep
+class Sweep1
 {
-   public:
-      GeneralSweep(const string & name, vector<string> & vec)
-         : myName(name), outVec(vec)
-      {}
+ public:
+   Sweep1(BlockDataID fieldID) : fieldID_(fieldID) {}
 
+   void operator()(IBlock* block)
+   {
+      auto field = block->getData< Field_T >(fieldID_);
 
-      void operator() (IBlock * ){
-         outVec.push_back(myName);
-      }
+      for (auto iter = field->begin(); iter != field->end(); ++iter)
+         *iter += 1;
+   }
 
-   private:
-      string myName;
-      vector<string> & outVec;
+ private:
+   BlockDataID fieldID_;
 };
 
-
-class GeneralFunction
+class Sweep2
 {
-   public:
-      GeneralFunction(const string & name, vector<string> & vec)
-      : myName(name), outVec(vec)
-      {}
+ public:
+   Sweep2(BlockDataID fieldID) : fieldID_(fieldID) {}
 
-      void operator() (){
-         outVec.push_back(myName);
-      }
-
-   private:
-      string myName;
-      vector<string> & outVec;
-};
+   void operator()(IBlock* block)
+   {
+      auto field = block->getData< Field_T >(fieldID_);
 
+      for (auto iter = field->begin(); iter != field->end(); ++iter)
+         *iter += 2;
+   }
 
+ private:
+   BlockDataID fieldID_;
+};
 
-int main()
+int main(int argc, char** argv)
 {
    debug::enterTestMode();
+   mpi::Environment env(argc, argv);
 
-   vector<string> expectedSequence;
-   vector<string> sequence;
-
-   SUID cpuSelect("CPU");
-   SUID gpuSelect("GPU");
-
-   SUID srtSelect("SRT");
-   SUID mrtSelect("MRT");
-
-
-   //FIXME put a real test in here
-   shared_ptr<SweepTimeloop> tl = make_shared<SweepTimeloop>(shared_ptr<BlockStorage>(),100);
-
-   typedef SweepTimeloop::SweepHandle SH;
-
-   SH sweep1 =  tl->addSweep(        GeneralSweep("CPU1",sequence), cpuSelect);
-                tl->addSweep(sweep1, GeneralSweep("GPU1",sequence), gpuSelect);
-
-   SH sweep2 =  tl->addSweep(        GeneralSweep("CPU2",sequence), cpuSelect);
-                tl->addSweep(sweep2, GeneralSweep("GPU2",sequence), gpuSelect);
+   std::vector<std::string> expectedSequence;
+   std::vector<std::string> sequence;
 
+   SUID sweepSelect1("Sweep1");
+   SUID sweepSelect2("Sweep2");
 
-   tl->addFuncBeforeSweep(sweep1,GeneralFunction("Pre1",sequence));
-   tl->addFuncAfterSweep (sweep1,GeneralFunction("Post1",sequence));
+   shared_ptr< StructuredBlockForest > blocks = blockforest::createUniformBlockGrid(
+      uint_c(4), uint_c(2), uint_c(2), uint_c(10), uint_c(10), uint_c(10), real_c(1), false, false, false, false);
 
-   tl->addFuncBeforeSweep(sweep2,GeneralFunction("Pre2",sequence));
-   tl->addFuncAfterSweep (sweep2,GeneralFunction("Post2",sequence));
+   BlockDataID fieldID = blocks->addStructuredBlockData< Field_T >(FieldAdder, "Test Field");
 
-   typedef Timeloop::FctHandle FH;
-   FH preTs  = tl->addFuncBeforeTimeStep(       GeneralFunction("PreTimestepCPU",sequence),cpuSelect,srtSelect);
-               tl->addFuncBeforeTimeStep(preTs, GeneralFunction("PreTimestepGPU",sequence),gpuSelect,srtSelect);
-   FH postTs = tl->addFuncAfterTimeStep (       GeneralFunction("PostTimestepCPU",sequence),cpuSelect,mrtSelect);
-               tl->addFuncAfterTimeStep (postTs,GeneralFunction("PostTimestepGPU",sequence),gpuSelect,mrtSelect);
+   for (auto& block : *blocks)
+   {
+      if (block.getAABB().min()[0] < 20)
+         block.setState(sweepSelect1);
+      else
+         block.setState(sweepSelect2);
+   }
 
+   uint_t timesteps = 10;
+   SweepTimeloop timeloop(blocks->getBlockStorage(), timesteps);
 
-   //----------  First Run - CPU Selector ---------------------------------------------
+   timeloop.add() << Sweep(Sweep1(fieldID), "Sweep 1", sweepSelect1, sweepSelect2);
+   timeloop.add() << Sweep(Sweep2(fieldID), "Sweep 2", sweepSelect2, sweepSelect1);
 
-   expectedSequence.push_back("PreTimestepCPU");
-   expectedSequence.push_back("Pre1");
-   expectedSequence.push_back("CPU1");
-   expectedSequence.push_back("Post1");
-   expectedSequence.push_back("Pre2");
-   expectedSequence.push_back("CPU2");
-   expectedSequence.push_back("Post2");
-   expectedSequence.push_back("PostTimestepCPU");
+   WcTimingPool timingPool;
 
-   tl->singleStep(cpuSelect);
-
-   WALBERLA_CHECK_EQUAL(expectedSequence.size(), sequence.size());
-   WALBERLA_CHECK( equal(expectedSequence.begin(),expectedSequence.end(), sequence.begin() ) );
-
-   expectedSequence.clear();
-   sequence.clear();
-
-
-   // ------------ Second Run - GPU Selector -------------------------------------------
-
-   expectedSequence.push_back("PreTimestepGPU");
-   expectedSequence.push_back("Pre1");
-   expectedSequence.push_back("GPU1");
-   expectedSequence.push_back("Post1");
-   expectedSequence.push_back("Pre2");
-   expectedSequence.push_back("GPU2");
-   expectedSequence.push_back("Post2");
-   expectedSequence.push_back("PostTimestepGPU");
-
-   tl->singleStep(gpuSelect);
-
-   WALBERLA_CHECK_EQUAL(expectedSequence.size(), sequence.size());
-   WALBERLA_CHECK( equal(expectedSequence.begin(),expectedSequence.end(), sequence.begin() ) );
-
-   expectedSequence.clear();
-   sequence.clear();
-
-
-   // ------------ Second Run - GPU and SRT    -------------------------------------------
-
-
-   expectedSequence.push_back("Pre1");
-   expectedSequence.push_back("GPU1");
-   expectedSequence.push_back("Post1");
-   expectedSequence.push_back("Pre2");
-   expectedSequence.push_back("GPU2");
-   expectedSequence.push_back("Post2");
-   expectedSequence.push_back("PostTimestepGPU");
-
-   tl->singleStep(gpuSelect + srtSelect);
-
-   WALBERLA_CHECK_EQUAL(expectedSequence.size(), sequence.size());
-   WALBERLA_CHECK( equal(expectedSequence.begin(),expectedSequence.end(), sequence.begin() ) );
-
-   expectedSequence.clear();
-   sequence.clear();
-
-
-   return 0;
+   timeloop.run(timingPool);
+   for (auto& block : *blocks)
+   {
+      auto field = block.getData< Field_T >(fieldID);
+      if (block.getAABB().min()[0] < 20)
+      {
+         for (auto iter = field->begin(); iter != field->end(); ++iter)
+            WALBERLA_CHECK_EQUAL(*iter, timesteps)
+      }
+      else
+      {
+         for (auto iter = field->begin(); iter != field->end(); ++iter)
+            WALBERLA_CHECK_EQUAL(*iter, timesteps * 2)
+      }
+   }
+   
+   return EXIT_SUCCESS;
 }
-
-
-
diff --git a/utilities/conda/walberla/bld.bat b/utilities/conda/walberla/bld.bat
index ead78eca75ece3f8f81f54d6c013d2bd769173be..64c851061b24b4c908c7cec4150d4fcf063e110c 100644
--- a/utilities/conda/walberla/bld.bat
+++ b/utilities/conda/walberla/bld.bat
@@ -4,12 +4,9 @@ cd build
 set BOOST_ROOT=%PREFIX%
 cmake -LAH -G"Visual Studio 15 2017 Win64"                   ^
   -DWALBERLA_BUILD_WITH_PYTHON=ON                            ^
-  -DWALBERLA_BUILD_WITH_PYTHON_MODULE=ON                     ^
   -DWALBERLA_BUILD_WITH_MPI=OFF                              ^
   -DWALBERLA_BUILD_WITH_OPENMP=ON                            ^
-  -DPYTHON_EXECUTABLE="%PYTHON%"                             ^
-  -DBoost_USE_STATIC_LIBS=OFF                                ^
-  -DBoost_USE_MULTITHREADED=ON ..
+  -DPYTHON_EXECUTABLE="%PYTHON%"  ..
 if errorlevel 1 exit 1
 
 cmake --build . --config Release --target pythonModuleInstall
diff --git a/utilities/conda/walberla/build.sh b/utilities/conda/walberla/build.sh
index ac6ff0002fadda4361c8cc2f984440678fd13a3c..97256607b26c30a3e5eed47683deac37ef84d99a 100644
--- a/utilities/conda/walberla/build.sh
+++ b/utilities/conda/walberla/build.sh
@@ -5,8 +5,6 @@ cmake \
    -DCMAKE_FIND_ROOT_PATH=${PREFIX} \
    -DCMAKE_INSTALL_PREFIX=${PREFIX} \
    -DWALBERLA_BUILD_WITH_PYTHON=ON \
-   -DWALBERLA_BUILD_WITH_PYTHON_MODULE=ON \
-   -DWALBERLA_BUILD_WITH_PYTHON_LBM=OFF \
    ..
 
 make -j ${CPU_COUNT} pythonModuleInstall
diff --git a/utilities/conda/walberla/meta.yaml b/utilities/conda/walberla/meta.yaml
index 2148479bbb815ac49d584f8a4d79995adaed5a94..80a1cefc01735f4a792068f64548f147fdb5c001 100644
--- a/utilities/conda/walberla/meta.yaml
+++ b/utilities/conda/walberla/meta.yaml
@@ -14,12 +14,10 @@ requirements:
   - make
   host:
   - python
-  - boost
   - mpich [linux]
   - openmesh
   run:
   - python
-  - boost
   - numpy
   - mpich [linux]
   - openmesh