diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index cd81f7d043b4196fa8017d88adde0c02108601a6..4333fcc68d2c9b14ec48191f5fe27838517c455f 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -12,6 +12,7 @@ variables:
   ASAN_OPTIONS: "detect_leaks=0:detect_stack_use_after_return=1"
   # location of AirShowerPhysics/corsika-data
   CORSIKA_DATA: "${CI_PROJECT_DIR}/modules/data" # the git submodule
+  corsika_DIR: "${CI_PROJECT_DIR}/build/install" # for cmake to find corsikaConfig.cmake
   # _alternatively_ corsika-data can be downloaded as submodule:
   GIT_SUBMODULE_STRATEGY: normal # none: we get the submodules in before_script,
                                  # normal: get submodules automatically
@@ -92,7 +93,7 @@ check-clang-format:
   script:
     - mkdir -p build
     - cd build
-    - cmake .. -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHIA8_C8=SYSTEM
+    - cmake .. -DCMAKE_INSTALL_PREFIX=$PWD/install -DCMAKE_BUILD_TYPE=Debug -DUSE_Pythia8_C8=C8
   rules:
     - if: $CI_MERGE_REQUEST_ID
     - if: $CI_COMMIT_TAG
@@ -279,56 +280,6 @@ build_test-clang-8:
 
 
 
-####### EXAMPLE (only manual)  ##############
-
-##########################################################
-# generic example template job
-# normal pipeline for each commit
-.example:
-  stage: example
-  tags:
-    - corsika
-  script:
-    - cd build
-    - set -o pipefail
-    - make -j4 run_examples | gzip -v -9 > examples.log.gz
-  rules:
-    - if: $CI_MERGE_REQUEST_ID
-      when: manual
-    - if: $CI_COMMIT_TAG
-      when: manual
-    - if: $CI_COMMIT_BRANCH
-      when: manual
-  allow_failure: true
-  artifacts:
-    when: always
-    expire_in: 3 days
-    paths:
-      - ${CI_PROJECT_DIR}/build/examples.log.gz
-  cache:
-    paths:
-      - ${CI_PROJECT_DIR}/build/
-    untracked: true
-    policy: pull
-
-# example for gcc
-example-u-18_04:
-  extends: .example
-  image: corsika/devel:u-18.04
-  dependencies:
-    - build-u-18_04
-  cache:
-    key: "${CI_COMMIT_REF_SLUG}-gcc"
-
-# example for clang
-example-clang-8:
-  extends: .example
-  image: corsika/devel:clang-8
-  dependencies:
-    - build-clang-8
-  cache:
-    key: "${CI_COMMIT_REF_SLUG}-clang"
-
 
 
 ####### BUILD-TEST-EXAMPLE (only non-Draft)  ##############
@@ -344,7 +295,12 @@ example-clang-8:
     - cd build
     - cmake --build . -- -j4
     - set -o pipefail
-    - ctest -j4 
+    - ctest -j4
+    - make install
+    - mkdir -p build_examples
+    - cd build_examples
+    - cmake ../install/share/corsika/examples
+    - make -j4
     - make -j4 run_examples | gzip -v -9 > examples.log.gz
   rules:
     - if: '$CI_MERGE_REQUEST_ID && $CI_MERGE_REQUEST_TITLE =~ /^Draft:/'
@@ -362,7 +318,7 @@ example-clang-8:
       junit:
         - ${CI_PROJECT_DIR}/build/test_outputs/junit*.xml
     paths:
-      - ${CI_PROJECT_DIR}/build/examples.log.gz
+      - ${CI_PROJECT_DIR}/build/build_examples/examples.log.gz
   cache:
     paths:
       - ${CI_PROJECT_DIR}/build/
@@ -450,10 +406,15 @@ install-clang-8:
     - corsika
   script:
     - cd build
-    - cmake .. -DCMAKE_BUILD_TYPE=Release -DUSE_PYTHIA8_C8=SYSTEM
+    - cmake .. -DCMAKE_BUILD_TYPE=Release -DUSE_Pythia8_C8=C8
     - cmake --build . -- -j4
     - set -o pipefail
-    - ctest -j4 
+    - ctest -j4
+    - make install
+    - mkdir -p build_examples
+    - cd build_examples
+    - cmake ../install/share/corsika/examples
+    - make -j4
     - make -j4 run_examples | gzip -v -9 > examples.log.gz
   rules:
     - if: '$CI_MERGE_REQUEST_LABELS =~ /Ready for code review/' # run on merge requests, if label 'Ready for code review' is set
@@ -478,7 +439,7 @@ install-clang-8:
       junit:
         - ${CI_PROJECT_DIR}/build/test_outputs/junit*.xml
     paths:
-      - ${CI_PROJECT_DIR}/build/examples.log.gz
+      - ${CI_PROJECT_DIR}/build/build_examples/examples.log.gz
 
 # release for gcc
 release-full-u-18_04:
@@ -513,7 +474,7 @@ coverage:
     - corsika
   script:
     - cd build
-    - cmake .. -DCMAKE_BUILD_TYPE=Coverage -DUSE_PYTHIA8_C8=SYSTEM
+    - cmake .. -DCMAKE_BUILD_TYPE=Coverage -DUSE_Pythia8_C8=C8
     - cmake --build . -- -j4
     - ctest -j4 
     - cmake --build . --target coverage
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 2c7aaa24d24944e3d8ea129964414076f4326d0d..a4d6cd6c9d2a360cc5561085f010f12c9e5a8faf 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,5 +1,16 @@
 cmake_minimum_required (VERSION 3.9)
 
+#+++++++++++++++++++++++++++++
+# project name
+#
+set (c8_version 8.0.0)
+project (
+  corsika
+  VERSION ${c8_version}
+  DESCRIPTION "CORSIKA C++ project (alpha status)"
+  LANGUAGES CXX
+  )
+
 #+++++++++++++++++++++++++++++
 # prevent in-source builds and give warning message
 #
@@ -10,15 +21,16 @@ if ("${CMAKE_BINARY_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
           You must delete them, or cmake will refuse to work.")
 endif ()
 
-#+++++++++++++++++++++++++++++
-# project name
+#++++++++++++++++++++++++++++
+# cmake version-specific settings
 #
-project (
-  corsika
-  VERSION 8.0.0
-  DESCRIPTION "CORSIKA C++ project (alpha status)"
-  LANGUAGES CXX
-  )
+# https://cmake.org/cmake/help/latest/policy/CMP0079.html
+if (${CMAKE_VERSION} VERSION_GREATER "3.13.0")
+  cmake_policy (SET CMP0079 NEW)
+endif ()
+if (POLICY CMP0079)
+    cmake_policy (SET CMP0079 NEW)
+endif ()
 
 #+++++++++++++++++++++++++++++
 # as long as there still are modules using it:
@@ -37,7 +49,7 @@ endif ()
 # cmake path dir, and cmake config
 #
 set (CORSIKA8_CMAKE_DIR "${PROJECT_SOURCE_DIR}/cmake")
-set (CMAKE_MODULE_PATH  "${CORSIKA8_CMAKE_DIR}" ${CMAKE_MODULE_PATH})
+set (CMAKE_MODULE_PATH "${CORSIKA8_CMAKE_DIR}" ${CMAKE_MODULE_PATH})
 include (CorsikaUtilities) # extra cmake function
 set (CMAKE_VERBOSE_MAKEFILE  OFF) # this can be done with `make VERBOSE=1`
 # ignore many irrelevant Up-to-date messages during install
@@ -46,7 +58,7 @@ set (CMAKE_INSTALL_MESSAGE LAZY)
 #+++++++++++++++++++++++++++++
 # Setup hardware and infrastructure dependent defines
 #
-include(CorsikaDefines)
+include (CorsikaDefines)
 
 #+++++++++++++++++++++++++++++
 # check if compiler is C++17 compliant
@@ -68,7 +80,9 @@ set (CMAKE_CXX_EXTENSIONS OFF)
 # FYI: optimizer flags: -O2 would not trade speed for size, neither O2/3 use fast-math
 # debug: O0, relwithdebinfo: 02, release: O3, minsizerel: Os (all defaults)
 set (CMAKE_CXX_FLAGS "-Wall -pedantic -Wextra -Wno-ignored-qualifiers")
-set (CMAKE_Fortran_FLAGS "-std=legacy -Wfunction-elimination")
+if (CORSIKA_SCL_CXX)
+  add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
+endif()
 set (DEFAULT_BUILD_TYPE "Release")
 
 # clang produces a lot of unecessary warnings without this:
@@ -115,6 +129,7 @@ endif (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
 #+++++++++++++++++++++++++++++
 # Setup external dependencies.
 #
+###
 include (conan) # self-provided in 'cmake' directory
 #
 # download and build all dependencies
@@ -150,7 +165,7 @@ if (CMAKE_BUILD_TYPE STREQUAL Coverage)
   add_custom_command (
     OUTPUT coverage.info
     COMMAND ${LCOV_BIN_DIR}/lcov -q --remove raw-coverage.info "*/usr/*" "/usr/*" --output-file coverage2.info
-    COMMAND ${LCOV_BIN_DIR}/lcov --remove coverage2.info "*/externals/*" "*/tests/*" "*/sibyll2.3d.cpp" "*/.conan/*" "*/include/Pythia8/*" "${CMAKE_CURRENT_SOURCE_DIR}/modules/*" "${CMAKE_CURRENT_BINARY_DIR}/modules/*" --output-file coverage.info
+    COMMAND ${LCOV_BIN_DIR}/lcov --remove coverage2.info "*/externals/*" "*/tests/*" "*/sibyll2.3d.cpp" "*/.conan/*" "*/include/Pythia8/*" "${CMAKE_SOURCE_DIR}/modules/*" "${CMAKE_BINARY_DIR}/modules/*" --output-file coverage.info
     COMMAND ${CMAKE_COMMAND} -E remove coverage2.info
     DEPENDS raw-coverage.info
     )
@@ -176,33 +191,43 @@ set (CTEST_CUSTOM_COVERAGE_EXCLUDE "./tests/" "./examples/" "./modules/" "test*.
 # include this test only if NOT run on gitlab-ci; On CI this is a dedicated job:
 if (NOT DEFINED ENV{CI})
   # add call to ./do-copyright.py to run as unit-test-case
-  add_test (NAME copyright_notices COMMAND ./do-copyright.py WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR})
+  add_test (NAME copyright_notices COMMAND ./do-copyright.py WORKING_DIRECTORY ${CMAKE_SOURCE_DIR})
 endif (NOT DEFINED ENV{CI})
 
-#+++++++++++++++++++++++++++++
-# Options
-#
-# HISTORY option selection
-option (WITH_HISTORY "Flag to switch on/off HISTORY" ON)
-
 #+++++++++++++++++++++++++++++
 # Externals
 #
 set (Python_ADDITIONAL_VERSIONS 3)
 find_package (PythonInterp 3 REQUIRED)
 
+#+++++++++++++++++++++++++++++++
+# exporting of CMake targets
+#
+include (CMakePackageConfigHelpers)
+
+# list of targets that will be INSTALLed and EXPORTed
+# Important: all those targets must be individually INSTALLed with "EXPORT CORSIKA8PublicTargets"
+set (public_CORSIKA8_targets CORSIKA8)
+
 
 #+++++++++++++++++++++++++++++
 # CORSIKA8
 #
 add_library (CORSIKA8 INTERFACE)
+set_target_properties (
+  CORSIKA8
+  PROPERTIES
+  INTERFACE_CORSIKA8_MAJOR_VERSION 0
+  COMPATIBLE_INTERFACE_STRING CORSIKA8_MAJOR_VERSION
+  )
+#  
 target_include_directories (
   CORSIKA8
   INTERFACE
-  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-  $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
-  $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}>
-  )
+  $<BUILD_INTERFACE:${CMAKE_SOURCE_DIR}>
+  $<BUILD_INTERFACE:${CMAKE_BINARY_DIR}>
+  $<INSTALL_INTERFACE:include>
+  ) 
 # since CORSIKA8 is a header only library we must specify all link dependencies here:
 target_link_libraries (
   CORSIKA8
@@ -220,25 +245,24 @@ add_subdirectory (src)
 add_subdirectory (documentation)
 
 #+++++++++++++++++++++++++++++
-# =~~~~~~~~~~~~~~~~~~~~~~~~~=
 #  = Add of subdirectories =
-# =~~~~~~~~~~~~~~~~~~~~~~~~~=
+#
 
 #+++++++++++++++++++++++++++++
 # modules
 #
 set (CORSIKA_DATA_WITH_TEST ON) # we want to run the corsika-data unit test
-add_subdirectory (modules/data)
-add_subdirectory (modules/pythia)
+add_subdirectory (modules/data) # this is corsika-data (submodule)
+add_subdirectory (modules/pythia8)
 add_subdirectory (modules/sibyll)
 add_subdirectory (modules/qgsjetII)
 add_subdirectory (modules/urqmd)
 add_subdirectory (modules/conex)
 #
-# we really need a better proposal CMakeList.txt files:
+# we really need a better proposal CMakeList.txt file:
 set (ADD_PYTHON OFF)
-file(READ modules/CMakeLists_PROPOSAL.txt overwrite_CMakeLists_PROPOSAL_txt)
-file(WRITE modules/proposal/CMakeLists.txt ${overwrite_CMakeLists_PROPOSAL_txt})
+file (READ modules/CMakeLists_PROPOSAL.txt overwrite_CMakeLists_PROPOSAL_txt)
+file (WRITE modules/proposal/CMakeLists.txt ${overwrite_CMakeLists_PROPOSAL_txt})
 add_subdirectory (modules/proposal)
 target_link_libraries (CORSIKA8 INTERFACE PROPOSAL)
 
@@ -248,14 +272,80 @@ target_link_libraries (CORSIKA8 INTERFACE PROPOSAL)
 add_subdirectory (tests)
 
 #+++++++++++++++++++++++++++++++
+# installation
+#
+install (
+  TARGETS CORSIKA8
+  EXPORT CORSIKA8PublicTargets
+  PUBLIC_HEADER DESTINATION ${CMAKE_INSTALL_PREFIX}
+  )
+
+#
+# header only part
+#
+install (DIRECTORY corsika DESTINATION include)
+
+write_basic_package_version_file (
+  ${PROJECT_BINARY_DIR}/corsikaConfigVersion.cmake
+  VERSION ${c8_version}
+  COMPATIBILITY SameMajorVersion
+  )
+
+# export targets in build tree
+export (
+  EXPORT CORSIKA8PublicTargets
+  FILE "${CMAKE_CURRENT_BINARY_DIR}/corsikaTargets.cmake"
+  NAMESPACE CORSIKA8::
+  )
+
+# export targets in install tree
+install (
+  EXPORT CORSIKA8PublicTargets
+  FILE corsikaTargets.cmake
+  NAMESPACE CORSIKA8::
+  DESTINATION lib/cmake/corsika
+  )
+
+# config for build tree
+configure_package_config_file (
+  cmake/corsikaConfig.cmake.in
+  ${PROJECT_BINARY_DIR}/corsikaConfig.cmake
+  INSTALL_DESTINATION ${CMAKE_INSTALL_PREFIX}
+  )
+
+# config for install tree
+# overwrite with install location (if it was build as part of corsika)
+set (Pythia8_INCDIR ${Pythia8_INCDIR_INSTALL}) 
+set (Pythia8_LIBDIR ${Pythia8_LIBDIR_INSTALL}) 
+configure_package_config_file (
+  cmake/corsikaConfig.cmake.in
+  ${PROJECT_BINARY_DIR}/cmake/corsikaConfig.cmake
+  INSTALL_DESTINATION ${PROJECT_BINARY_DIR}/do_not_need_this
+  )
+
+install (
+  FILES
+  ${CMAKE_BINARY_DIR}/conanbuildinfo.cmake
+  ${CMAKE_BINARY_DIR}/conaninfo.txt
+  ${CMAKE_BINARY_DIR}/corsikaConfig.cmake
+  ${CMAKE_BINARY_DIR}/corsikaConfigVersion.cmake
+  DESTINATION lib/cmake/corsika
+  )
+
+#
 # examples
-# 
-add_subdirectory (examples)
+#
+install (DIRECTORY examples DESTINATION share/corsika)
+
+#
+# tools
+#
+add_subdirectory (tools)
+
 
 #+++++++++++++++++++++++++++++++
 #
 # final summary output
 #
 include (FeatureSummary)
-add_feature_info (HISTORY WITH_HISTORY "Full information on cascade history for particles.")
 feature_summary (WHAT ALL)
diff --git a/COLLABORATION_AGREEMENT.md b/COLLABORATION_AGREEMENT.md
index ea9ed3634e0dd71c403ef0025ff6ad86bff94821..9b240a03e91b69d84a368cd569f7db7534e7b528 100644
--- a/COLLABORATION_AGREEMENT.md
+++ b/COLLABORATION_AGREEMENT.md
@@ -18,9 +18,6 @@ The MCnet guidelines developed by [www.montecarlonet.org](www.montecarlonet.org)
 are copied in [MCNET_GUIDELINES](MCNET_GUIDELINES) -- they provide a very good 
 additional scope that contributors should read and consider. 
 
-All possible
-liability and licensing question are only handled by the adopted
-software license.
 
 ## The software license of the CORSIKA project
 
@@ -29,10 +26,18 @@ license GPLv3, as copied in full in the file
 [LICENSE](LICENSE). Each source file of the CORSIKA project contains a
 short statement of the copyright and this license. Each binary or
 source code release of CORSIKA contains the file LICENSE. The
-code, documentation and content in the folder [externals](externals)
+code, documentation and content in the folder [./externals](./externals)
 is not integral part of the CORSIKA project and can be based on, or
-include, other licenses, which must be compatible with GPLv3. Check the
-content of this folder for details and additional license information. It depends on the configuration of
+include, other licenses, which must be compatible with GPLv3. 
+
+The folder [./modules](./modules) contains the code of several
+external physics models for your convenience. Please consult the
+original model authors and honor their policies and licenses. 
+Of course, we have their consent to
+distribute their code together with CORSIKA 8. 
+
+Check the content of these folders carefully for details and additional 
+license information. It depends on the configuration of
 the build system to what extend this code is used to build CORSIKA.
 
 
@@ -42,22 +47,3 @@ If you want to contribute, you need to read
 [the GUIDELINES](CONTRIBUTING.md) and comply with these rules, or help to
 improve them. 
 
-
-## The CORSIKA Projects Maintainers
-The CORSIKA Project mainters make all decisions for the CORSIKA
-Project. They can also change the
-[COLLABORATION\_AGREEMENT](COLLABORATION\_AGREEMENT.md), the
-[GUIDELINES](CONTRIBUTING.md) or any other structure or document relevant for the CORSIKA Project.
-
-The current CORSIKA Project maintainers are listed in the file [MAINTAINERS](MAINTAINERS.md). 
-
-and can be contacted via corsika-project@lists.kit.edu.  The chair
-person of the CORSIKA Project is Ralf Ulrich (KIT). Maintainers have special 
-responsibilities for specific parts of the project. 
-
-
-### Planning and performing releases
-
-The CORSIKA maintainers decide on releases of the software, and about the content of it. 
-
-
diff --git a/README.md b/README.md
index a66a44dc6f2a5c87b4ddb357605c77e0130ecea6..1b7175d3d817d0237244d83cc079f8f7a8c823d0 100644
--- a/README.md
+++ b/README.md
@@ -11,7 +11,7 @@ particle cascades with stochastic and continuous processes.
 The software makes extensive use of static design patterns and
 compiler optimization. Thus, the most fundamental configuration
 decision of the user must be performed at compile time. At run time
-only specific model parameters can still be changed.
+model parameters can still be changed.
 
 CORSIKA 8 is by default released under the GPLv3 license. See [license
 file](https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika/blob/master/LICENSE)
@@ -30,58 +30,72 @@ When you plan to contribute to CORSIKA 8 check the guidelines outlined here:
 guidelines](https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika/blob/master/CONTRIBUTING.md). Code
 that fails the review by the CORSIKA author group must be improved
 before it can be merged in the official code base. After your code has
-been accepted and merged you become a contributor of the CORSIKA 8
+been accepted and merged, you become a contributor of the CORSIKA 8
 project (code author). 
 
 IMPORTANT: Before you contribute, you need to read and agree to the
 [collaboration
-agreement](https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika/blob/master/COLLABORATION_AGREEMENT.md). The
-agreement can be discussed, and eventually improved.
+agreement](https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika/blob/master/COLLABORATION_AGREEMENT.md). The agreement can be discussed, and eventually improved.
 
 We also want to point you to the [MCnet
-guidelines](https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika/blob/master/MCNET_GUIDELINES),
-which are very useful also for us.
+guidelines](https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika/blob/master/MCNET_GUIDELINES), which are very useful also for us.
 
 
 ## Get in contact
-  * Connect to https://gitlab.ikp.kit.edu register yourself and join the "Air Shower Physics" group
+  * Connect to https://gitlab.ikp.kit.edu register yourself and join the "Air Shower Physics" group. Write to me (ralf.ulrich@kit.edu) only in case there are problems with that. 
   * Connect to corsika-devel@lists.kit.edu (self-register at
     https://www.lists.kit.edu/sympa/subscribe/corsika-devel) to get in
-    touch with the project
+    touch with the project.
+    
+  * Register on the corsika slack channel. 
 
 
+## Installation
 
-CORSIKA 8 is tested regularly at least on gcc7.3.0 and clang-8.0.0. You will
-also need:
+CORSIKA 8 is tested regularly at least on gcc7.3.0 and clang-8.0.0. 
 
-- Python 3 (supported versions are Python >= 3.6)
+### Prerequisites
+
+You will also need:
+
+- Python 3 (supported versions are Python >= 3.6), with pip
+- conan (via pip)
 - cmake 
 - git
+- g++, gfortran, binutils, make
+
+On a bare Ubuntu 20.04, just add:
+``` shell
+sudo apt-get install python3 python3-pip cmake g++ gfortran git doxygen graphviz
+```
 
-On a bare Ubuntu 18.04, just add:
+On a bare CentOS 7 install python3, pip3 (pip from python3) and cmake3. Any of the devtools 7, 8, 9 should work (at least). 
+Also initialize devtools, before building CORSIKA 8:
 ``` shell
-sudo apt-get install cmake g++ git
+source /opt/rh/devtoolset-9/enable
 ```
 
-CORSIKA 8 uses the [conan](https://conan.io/) package manager to manage our
-dependencies. If you do not have Conan installed, it can be installed with:
+CORSIKA 8 uses the [conan](https://conan.io/) package manager to
+manage our dependencies. If you do not have Conan installed, it can be
+installed with:
 
 ``` shell
 pip install --user conan
 ```
 
+### Compiling
+
 Once Conan is installed, follow these steps to download and install CORSIKA 8:
+
 ``` shell
 git clone --recursive git@gitlab.ikp.kit.edu:AirShowerPhysics/corsika.git
-cd corsika
-mkdir ../corsika-build
-cd ../corsika-build
+mkdir corsika-build
+cd corsika-build
 cmake ../corsika -DCMAKE_INSTALL_PREFIX=../corsika-install
 make -j8
 make install
 ```
 
-Type `make test` to run the unit test suite.
 
 ## Installation (using docker containers)
 
@@ -94,53 +108,81 @@ You only need docker, e.g. on Ubunut: `sudo apt-get install docker` and of cours
 ## Compiling
 
 Follow these steps to download and install CORSIKA 8, master development version
-```
+```shell
 git clone --recursive https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika.git
-cd corsika
 sudo docker run -v $PWD:/corsika -it corsika/devel:clang-8 /bin/bash
 mkdir build
 cd build
-cmake .. -DCMAKE_INSTALL_PREFIX=../install
+cmake ../corsika -DCMAKE_INSTALL_PREFIX=../corsika-install
 make -j8
 make install
 ```
 
-Type `make test` to run the unit test suite.
+## Runing Unit Tests
 
-### Running examples
+Note, before you run *any* executbale you must also define the
+`CORSIKA_DATA` environment variable to point to the location where you
+cloned corsika `modules/data`, thus typically 
+```shell
+export CORSIKA_DATA=$PWD/../corsika/modules/data
+```
+
+To run the Unit Tests, just type `ctest` in your build area.
+
+
+## Running examples
+
+To see how a relatively simple hadron cascade develops,
+see `examples/cascade_example.cpp` for a starting point.
 
-and if you want to see how the first simple hadron cascade develops, see `Documentation/Examples/cascade_example.cc` for a starting point. 
+To run the cascade_example, or any other CORSIKA 8 application, you
+must first compile it wrt. to the CORSIKA 8 header-only framework.  This
+can be done best by copying
+e.g. `corsika-install/share/corsika/examples/` to your working place
+(e.g. `corsika-work`). 
 
-Run the cascade_example with: 
+Next, you need to define the environment variable `corsika_DIR` to point to, either, 
+your build, or your install area. Thus, e.g. 
+```shell
+export corsika_DIR=<dir where you installed CORSIKA 8 to, or where you buld it">
 ```
-cd ../corsika-install
-share/examples/cascade_example
+
+Then compile your example/application with
+```shell
+cd corsika-work
+cmake .
+make
+bin/cascade_example 
 ```
 
 Visualize output (needs gnuplot installed): 
-```
-bash share/tools/plot_tracks.sh tracks.dat 
+```shell
+bash $corsika_DIR/share/corsika/tools/plot_tracks.sh tracks.dat 
 firefox tracks.dat.gif 
 ```
+(note, if you use the corsika_DIR to point to the build area: the script `plot_tracks.sh` is 
+not copied to the build area, it is only part of the source tree at `tools/plot_tracks.sh`)
 
-Or also consider the `vertical_EAS` example in the same directory, which can 
-be configured with command line options. 
+Or also consider the `vertical_EAS` example in the same directory,
+which can be configured with command line options. 
+Run `bin/vertical_EAS` to get basic help.
 
 
 ### Generating doxygen documentation
 
-To generate the documentation, you need doxygen and graphviz. If you work with the docker corsika/devel containers this is already included. 
+To generate the documentation, you need doxygen and graphviz. If you work with 
+the docker corsika/devel containers this is already included. 
 Otherwise, e.g. on Ubuntu 18.04, do:
-```
+```shell
 sudo apt-get install doxygen graphviz
 ```
 Switch to the corsika build directory and do
-```
+```shell
 make doxygen
 make install
 ```
-browse with firefox:
-```
-firefox ../corsika-install/share/doc/html/index.html
+open with firefox:
+```shell
+firefox ../corsika-install/share/corsika/doc/html/index.html
 ```
 
diff --git a/cmake/CorsikaDefines.cmake b/cmake/CorsikaDefines.cmake
index f2b08a67b68f9b92a0a809e01cf38ab5496611b0..2019ce2f906f9025e2181a0c9eb624e99f9ca44e 100644
--- a/cmake/CorsikaDefines.cmake
+++ b/cmake/CorsikaDefines.cmake
@@ -1,11 +1,11 @@
 #
 # Floating point exception support - select implementation to use
 #
-include(CheckIncludeFileCXX)
-CHECK_INCLUDE_FILE_CXX("fenv.h" HAS_FEENABLEEXCEPT)
+include (CheckIncludeFileCXX)
+CHECK_INCLUDE_FILE_CXX ("fenv.h" HAS_FEENABLEEXCEPT)
 if (HAS_FEENABLEEXCEPT) # FLOATING_POINT_ENVIRONMENT
     set (CORSIKA_HAS_FEENABLEEXCEPT 1)
-    set_property(DIRECTORY ${CMAKE_HOME_DIRECTORY} APPEND PROPERTY COMPILE_DEFINITIONS "HAS_FEENABLEEXCEPT")
+    set_property (DIRECTORY ${CMAKE_HOME_DIRECTORY} APPEND PROPERTY COMPILE_DEFINITIONS "HAS_FEENABLEEXCEPT")
 endif ()
 
 
@@ -13,12 +13,17 @@ endif ()
 # General OS Detection
 #
 if (${CMAKE_SYSTEM_NAME} MATCHES "Windows")
-    set(CORSIKA_OS_WINDOWS TRUE)
-     set (CORSIKA_OS "Windows")
+    set (CORSIKA_OS_WINDOWS TRUE)
+    set (CORSIKA_OS "Windows")
 elseif (${CMAKE_SYSTEM_NAME} MATCHES "Linux")
-    set(CORSIKA_OS_LINUX TRUE)
-     set (CORSIKA_OS "Linux")
+    set (CORSIKA_OS_LINUX TRUE)
+    set (CORSIKA_OS "Linux")
+    # check for RedHat/CentOS compiler from the software collections (scl)
+    string (FIND ${CMAKE_CXX_COMPILER} "/opt/rh/devtoolset-" index)
+    if (${index} EQUAL 0)
+      set (CORSIKA_SCL_CXX TRUE)
+    endif ()
 elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
-    set(CORSIKA_OS_MAC TRUE)  
+    set (CORSIKA_OS_MAC TRUE)  
     set (CORSIKA_OS "Mac") 
-endif()
\ No newline at end of file
+endif ()
diff --git a/cmake/CorsikaUtilities.cmake b/cmake/CorsikaUtilities.cmake
index c71e9bea31a13a80e195552f671ac89963af3759..cf8f1ef681a13f6545b613966f9f47f576e5d918 100644
--- a/cmake/CorsikaUtilities.cmake
+++ b/cmake/CorsikaUtilities.cmake
@@ -68,55 +68,3 @@ function (CORSIKA_ADD_TEST)
   endif ()
   add_test (NAME ${name} COMMAND ${name} -o ${PROJECT_BINARY_DIR}/test_outputs/junit-${name}.xml -s -r junit)
 endfunction (CORSIKA_ADD_TEST)
-
-
-#################################################
-#
-# central macro to register an examples in cmake
-#
-# Examples can be globally executed by 'make run_examples'
-#
-# 1) Simple use:
-# Pass the name of the source.cc file as the first
-# argument, without the ".cc" extention.
-#
-# Example: CORSIKA_REGISTER_EXAMPLE (doSomething)
-#
-# The TARGET doSomething must already exists,
-#    i.e. typically via add_executable (doSomething src.cc).
-#
-# Example: CORSIKA_ADD_EXAMPLE (example_one
-#              RUN_OPTION "extra command line options"
-#              )
-#
-# In all cases, you can further customize the target with
-# target_link_libraries(example_one ...) and so on.
-#
-function (CORSIKA_REGISTER_EXAMPLE)
-  cmake_parse_arguments (PARSE_ARGV 1 C8_EXAMPLE "" "" "RUN_OPTIONS")
-  set (name ${ARGV0})
-
-  if (NOT C8_EXAMPLE_RUN_OPTIONS)
-    set (run_options "")
-  else ()
-    set (run_options ${C8_EXAMPLE_RUN_OPTIONS})
-  endif ()
-
-  target_compile_options (${name} PRIVATE -g) # do not skip asserts
-  file (MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/example_outputs/)
-  if (TARGET run_examples)
-  else ()
-    add_custom_target (run_examples)
-  endif ()
-  add_dependencies (run_examples ${name})
-  # just run the command as-is
-  set (CMD ${CMAKE_BINARY_DIR}/bin/${name} ${run_options})
-  add_custom_command (TARGET run_examples
-    POST_BUILD
-    COMMAND ${CMAKE_COMMAND} -E echo ""
-    COMMAND ${CMAKE_COMMAND} -E echo "**************************************"
-    COMMAND ${CMAKE_COMMAND} -E echo "*****   running example: ${name} " ${run_options} VERBATIM
-    COMMAND ${CMD} VERBATIM
-    WORKING_DIRECTORY ${PROJECT_BINARY_DIR}/example_outputs)
-  install (TARGETS ${name} DESTINATION share/examples)
-endfunction (CORSIKA_REGISTER_EXAMPLE)
diff --git a/cmake/FindCORSIKA.cmake b/cmake/FindCORSIKA.cmake
deleted file mode 100644
index 5a18d9772d405ba09479ede5c6c8f6b69d6d37ad..0000000000000000000000000000000000000000
--- a/cmake/FindCORSIKA.cmake
+++ /dev/null
@@ -1,6 +0,0 @@
-
-add_library(CORSIKA INTERFACE)
-
-target_compile_coptions(CORSIKA
-  INTERFACE
-  ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/cmake/FindCORSIKA8.cmake b/cmake/FindCORSIKA8.cmake
deleted file mode 100644
index 39f936323ef79cb71ab0843ea41c7c85eca66526..0000000000000000000000000000000000000000
--- a/cmake/FindCORSIKA8.cmake
+++ /dev/null
@@ -1,9 +0,0 @@
-add_library (CORSIKA8 INTERFACE)
-
-target_include_directories (CORSIKA8
-  INTERFACE
-  ${CMAKE_CURRENT_SOURCE_DIR} ${CMAKE_CURRENT_BINARY_DIR})
-
-target_link_libraries (CORSIKA8 INTERFACE PhysUnits)
-
-set (CORSIKA8_FOUND TRUE)
diff --git a/cmake/FindCorsika8.cmake b/cmake/FindCorsika8.cmake
deleted file mode 100644
index 1a8549ce0c9170c05ef3d4bdce60150de486ee97..0000000000000000000000000000000000000000
--- a/cmake/FindCorsika8.cmake
+++ /dev/null
@@ -1,63 +0,0 @@
-#
-# FindCorsika8
-#
-# This module tries to find the Corsika8 header files and extrats their version.  It
-# sets the following variables.
-#
-# CORSIKA8_FOUND       -  Set ON if Corsika8 headers are found, otherwise OFF.
-#
-# CORSIKA8_INCLUDE_DIR -  Include directory for hydra header files.  (All header
-#                       files will actually be in the Corsika8 subdirectory.)
-# CORSIKA8_VERSION     -  Version of hydra in the form "major.minor.patch".
-#
-
-find_path( CORSIKA8_INCLUDE_DIR
-  PATHS
-    ${CMAKE_SOURCE_DIR}
-    /usr/include
-    /usr/local/include
-    ${CORSIKA8_DIR}
-  NAMES corsika/corsika.h
-  DOC "Corsika8 headers"
-  )
-
-
-if( CORSIKA8_INCLUDE_DIR )
-  list( REMOVE_DUPLICATES CORSIKA8_INCLUDE_DIR )
-endif( CORSIKA8_INCLUDE_DIR )
-
-# Find hydra version
-if (CORSIKA8_INCLUDE_DIR)
-  file( STRINGS ${CORSIKA_INCLUDE_DIR}/corsika/corsika.h
-    version
-    REGEX "#define CORSIKA_VERSION[ \t]+([0-9x]+)"
-    )
-  string( REGEX REPLACE
-    "#define CORSIKA_VERSION[ \t]+"
-    ""
-    version
-    "${version}"
-    )
-
-  string( REGEX MATCH "^[0-9]" major ${version} )
-  string( REGEX REPLACE "^${major}00" "" version "${version}" )
-  string( REGEX MATCH "^[0-9]" minor ${version} )
-  string( REGEX REPLACE "^${minor}0" "" version "${version}" )
-  set( CORSIKA_VERSION "${major}.${minor}.${version}")
-  set( CORSIKA_MAJOR_VERSION "${major}")
-  set( CORSIKA_MINOR_VERSION "${minor}")
-endif()
-
-# Check for required components
-include( FindPackageHandleStandardArgs )
-find_package_handle_standard_args( Corsika8
-  FOUND_VAR CORSIKA8_FOUND
-  REQUIRED_VARS CORSIKA8_INCLUDE_DIR
-  VERSION_VAR CORSIKA8_VERSION
-  )
-
-if(CORSIKA8_FOUND)
-  set(CORSIKA8_INCLUDE_DIRS ${CORSIKA8_INCLUDE_DIR})
-endif()
-
-mark_as_advanced(CORSIKA8_INCLUDE_DIR)
diff --git a/cmake/FindPythia8.cmake b/cmake/FindPythia8.cmake
index 88535cede70317aa9c2c6a53f5e84e08f1924a34..cbd3e75eed41ff3c8dbeae4ceeab55241f111abe 100644
--- a/cmake/FindPythia8.cmake
+++ b/cmake/FindPythia8.cmake
@@ -14,7 +14,7 @@
 #
 
 function (_Pythia8_CONFIG_ option variable type doc)
-  execute_process(COMMAND ${Pythia8_CONFIG} ${option}
+  execute_process (COMMAND ${Pythia8_CONFIG} ${option}
     OUTPUT_VARIABLE _local_out_
     RESULT_VARIABLE _local_res_)
   string (REGEX REPLACE "\n$" "" _local_out_ "${_local_out_}")
@@ -25,6 +25,14 @@ function (_Pythia8_CONFIG_ option variable type doc)
   endif ()
 endfunction (_Pythia8_CONFIG_)
   
+#################################################
+#
+# take directory and assume standard install layout
+#
+
+function (_Pythia8_LAYOUT_ dir variable type doc)
+  set (${variable} "${dir}" CACHE ${type} ${doc})
+endfunction (_Pythia8_LAYOUT_)
 
 
 #################################################
@@ -49,26 +57,47 @@ set (_SEARCH_Pythia8_
   $ENV{PYTHIA8_ROOT}
   ${PYTHIA8_DIR}
   $ENV{PYTHIA8_DIR}
+  ${Pythia8_DIR}
+  $ENV{Pythia8_DIR}
   /opt/pythia8
   )
 
-find_program (Pythia8_CONFIG
-  NAME pythia8-config
+find_file (Pythia8_Pythia_h_LOC
+  NAME Pythia.h
   PATHS ${_SEARCH_Pythia8_}
-  PATH_SUFFIXES "/bin"
-  DOC "The location of the pythia8-config script")
+  PATH_SUFFIXES include/Pythia8
+  DOC "The location of the Pythia8/Pythia.h script"
+  REQUIRED)
 
+if ("${Pythia8_Pythia_h_LOC}" STREQUAL "Pythia8_Pythia_h_LOC-NOTFOUND")
+  message (FATAL_ERROR "Did not find SYSTEM-level Pythia8 in: \"${_SEARCH_Pythia8_}\"")
+endif ()
+
+string (REPLACE "/include/Pythia8/Pythia.h" "" Pythia8_DIR ${Pythia8_Pythia_h_LOC})
+
+set (Pythia8_CONFIG ${Pythia8_DIR}/bin/pythia-config)
 if (Pythia8_CONFIG)
   set (HAVE_Pythia8 1 CACHE BOOL "presence of pythia8, found via pythia8-config")
 
-  _Pythia8_CONFIG_ ("--prefix" Pythia8_PREFIX PATH "location of pythia8 installation")
-  _Pythia8_CONFIG_ ("--includedir" Pythia8_INCLUDE_DIR PATH "pythia8 include directory")
-  _Pythia8_CONFIG_ ("--libdir" Pythia8_LIBRARY STRING "the pythia8 libs")
-  _Pythia8_CONFIG_ ("--datadir" Pythia8_DATA_DIR PATH "the pythia8 data dir")
-  _Pythia8_CONFIG_ ("--cxxflags" Pythia8_CXXFLAGS STRING "the pythia8 cxxflags")
+  # pythia-config is not relocatable
+  #_Pythia8_CONFIG_ ("--prefix" Pythia8_PREFIX PATH "location of pythia8 installation")
+  #_Pythia8_CONFIG_ ("--includedir" Pythia8_INCLUDE_DIR PATH "pythia8 include directory")
+  #_Pythia8_CONFIG_ ("--libdir" Pythia8_LIBRARY STRING "the pythia8 libs")
+  #_Pythia8_CONFIG_ ("--datadir" Pythia8_DATA_DIR PATH "the pythia8 data dir")
+  _Pythia8_LAYOUT_ ("${Pythia8_DIR}" Pythia8_PREFIX PATH "location of pythia8 installation")
+  _Pythia8_LAYOUT_ ("${Pythia8_DIR}/include" Pythia8_INCLUDE_DIR PATH "pythia8 include directory")
+  _Pythia8_LAYOUT_ ("${Pythia8_DIR}/lib" Pythia8_LIBRARY STRING "the pythia8 libs")
+  _Pythia8_LAYOUT_ ("${Pythia8_DIR}/share/Pythia8/xmldoc" Pythia8_DATA_DIR PATH "the pythia8 data dir")
+  
+  # read the config string
+  file (READ "${Pythia8_INCLUDE_DIR}/Pythia8/Pythia.h" Pythia8_TMP_PYTHIA_H)
+  string (REGEX MATCH "#define PYTHIA_VERSION_INTEGER ([0-9]*)" _ ${Pythia8_TMP_PYTHIA_H})  
+  set (Pythia8_VERSION ${CMAKE_MATCH_1})
+  message (STATUS "Found Pythia8 version: ${Pythia8_VERSION}")
+  
 endif ()
 
 # standard cmake infrastructure:
 include (FindPackageHandleStandardArgs)
-find_package_handle_standard_args (Pythia8 DEFAULT_MSG Pythia8_PREFIX Pythia8_INCLUDE_DIR Pythia8_LIBRARY)
-mark_as_advanced (Pythia8_DATA_DIR Pythia8_CXXFLAGS Pythia8_INCLUDE_DIR Pythia8_LIBRARY)
+find_package_handle_standard_args (Pythia8 REQUIRED_VARS Pythia8_PREFIX Pythia8_INCLUDE_DIR Pythia8_LIBRARY VERSION_VAR Pythia8_VERSION)
+mark_as_advanced (Pythia8_DATA_DIR Pythia8_INCLUDE_DIR Pythia8_LIBRARY)
diff --git a/cmake/corsikaConfig.cmake.in b/cmake/corsikaConfig.cmake.in
new file mode 100644
index 0000000000000000000000000000000000000000..81a499c04bb861e6a660d90bee533b461b30f694
--- /dev/null
+++ b/cmake/corsikaConfig.cmake.in
@@ -0,0 +1,85 @@
+set (CORSIKA8_VERSION @c8_version@)
+
+@PACKAGE_INIT@
+
+#+++++++++++++++++++++++++++
+# Options
+#
+option (WITH_HISTORY "Flag to switch on/off HISTORY" ON)
+
+
+#+++++++++++++++++++++++++++++
+# Build types settings
+#
+# setup coverage build type
+set (CMAKE_CXX_FLAGS_COVERAGE "-g --coverage")
+set (CMAKE_EXE_LINKER_FLAGS_COVERAGE "--coverage")
+set (CMAKE_SHARED_LINKER_FLAGS_COVERAGE "--coverage")
+# set a flag to inform code that we are in debug mode
+set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
+set (ALLOWED_BUILD_TYPES Debug Release MinSizeRel RelWithDebInfo Coverage)
+set_property (CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${ALLOWED_BUILD_TYPES})
+set (DEFAULT_BUILD_TYPE "Release")
+if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+  set (CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE
+    STRING "Choose the type of build." FORCE)
+endif (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
+  
+
+#++++++++++++++++++++++++++++
+# General config and flags
+#
+set (CMAKE_CXX_STANDARD @CMAKE_CXX_STANDARD@)
+set (CMAKE_CXX_EXTENSIONS @CMAKE_CXX_EXTENSIONS@)
+set (COMPILE_OPTIONS @COMPILE_OPTIONS@)
+set (CMAKE_VERBOSE_MAKEFILE  @CMAKE_VERBOSE_MAKEFILE@) 
+
+#+++++++++++++++++++++++++++++
+# Setup external dependencies via conan
+#
+include (${CMAKE_CURRENT_LIST_DIR}/conanbuildinfo.cmake)
+conan_basic_setup (TARGETS)
+
+#+++++++++++++++++++++++++++++
+# Import Pythia8
+# since we always import pythia (ExternalProject_Add) we have to
+# import here, too.
+#
+add_library (C8::ext::pythia8 STATIC IMPORTED GLOBAL)
+set_target_properties (
+  C8::ext::pythia8 PROPERTIES
+  IMPORTED_LOCATION @Pythia8_LIBDIR@/libpythia8.a
+  IMPORTED_LINK_INTERFACE_LIBRARIES dl
+  INTERFACE_INCLUDE_DIRECTORIES @Pythia8_INCDIR@
+  )
+set (Pythia8_FOUND @Pythia8_FOUND@)
+message (STATUS "Pythia8 at: @Pythia8_PREFIX@")
+
+
+#++++++++++++++++++++++++++++++
+# import CORSIKA8
+#     
+include ("${CMAKE_CURRENT_LIST_DIR}/corsikaTargets.cmake")
+check_required_components (corsika)
+
+
+#+++++++++++++++++++++++++++++++
+# add further definitions / options
+#
+if (WITH_HISTORY)
+  set_property (
+    TARGET CORSIKA8::CORSIKA8
+    APPEND PROPERTY
+    INTERFACE_COMPILE_DEFINITIONS "WITH_HISTORY"
+  )
+endif (WITH_HISTORY)
+  
+
+
+#+++++++++++++++++++++++++++++++
+#
+# final summary output
+#
+include (FeatureSummary)
+add_feature_info (HISTORY WITH_HISTORY "Full information on cascade history for particles.")
+feature_summary (WHAT ALL)
diff --git a/corsika/detail/framework/analytics/ClassTimer.inl b/corsika/detail/framework/analytics/ClassTimer.inl
index dca6fb5ce128761146936929895d3ce8a65ddb00..8f2c6945a62271b2a51cb079944c518cbd3ad8a9 100644
--- a/corsika/detail/framework/analytics/ClassTimer.inl
+++ b/corsika/detail/framework/analytics/ClassTimer.inl
@@ -23,12 +23,13 @@ namespace corsika {
 
   template <typename TClass, typename TRet, typename... TArgs,
             TRet (TClass::*TFuncPtr)(TArgs...), typename TTimer>
-  ClassTimer<TRet (TClass::*)(TArgs...), TFuncPtr, TTimer>::ClassTimer(TClass& obj)
+  inline ClassTimer<TRet (TClass::*)(TArgs...), TFuncPtr, TTimer>::ClassTimer(TClass& obj)
       : ClassTimerImpl<TClass, TTimer>(obj) {}
 
   template <typename TClass, typename TRet, typename... TArgs,
             TRet (TClass::*TFuncPtr)(TArgs...), typename TTimer>
-  TRet ClassTimer<TRet (TClass::*)(TArgs...), TFuncPtr, TTimer>::call(TArgs... args) {
+  inline TRet ClassTimer<TRet (TClass::*)(TArgs...), TFuncPtr, TTimer>::call(
+      TArgs... args) {
     this->start_ = ClassTimerImpl<TClass, TTimer>::clock_type::now();
     auto tmp = (this->obj_.*TFuncPtr)(std::forward<TArgs>(args)...);
     this->timeDiff_ = std::chrono::duration_cast<
@@ -41,12 +42,13 @@ namespace corsika {
 
   template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...),
             typename TTimer>
-  ClassTimer<void (TClass::*)(TArgs...), TFuncPtr, TTimer>::ClassTimer(TClass& obj)
+  inline ClassTimer<void (TClass::*)(TArgs...), TFuncPtr, TTimer>::ClassTimer(TClass& obj)
       : ClassTimerImpl<TClass, TTimer>(obj) {}
 
   template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...),
             typename TTimer>
-  void ClassTimer<void (TClass::*)(TArgs...), TFuncPtr, TTimer>::call(TArgs... args) {
+  inline void ClassTimer<void (TClass::*)(TArgs...), TFuncPtr, TTimer>::call(
+      TArgs... args) {
     this->start_ = ClassTimerImpl<TClass, TTimer>::clock_type::now();
     (this->obj_.*TFuncPtr)(std::forward<TArgs>(args)...);
     this->timeDiff_ = std::chrono::duration_cast<
@@ -59,12 +61,13 @@ namespace corsika {
 
   template <typename TClass, typename TRet, typename... TArgs,
             TRet (TClass::*TFuncPtr)(TArgs...) const, typename TTimer>
-  ClassTimer<TRet (TClass::*)(TArgs...) const, TFuncPtr, TTimer>::ClassTimer(TClass& obj)
+  inline ClassTimer<TRet (TClass::*)(TArgs...) const, TFuncPtr, TTimer>::ClassTimer(
+      TClass& obj)
       : ClassTimerImpl<TClass, TTimer>(obj) {}
 
   template <typename TClass, typename TRet, typename... TArgs,
             TRet (TClass::*TFuncPtr)(TArgs...) const, typename TTimer>
-  TRet ClassTimer<TRet (TClass::*)(TArgs...) const, TFuncPtr, TTimer>::call(
+  inline TRet ClassTimer<TRet (TClass::*)(TArgs...) const, TFuncPtr, TTimer>::call(
       TArgs... args) {
     this->start_ = ClassTimerImpl<TClass, TTimer>::clock_type::now();
     auto tmp = (this->obj_.*TFuncPtr)(std::forward<TArgs>(args)...);
@@ -77,12 +80,13 @@ namespace corsika {
   /// Specialisation 3
   template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...) const,
             typename TTimer>
-  ClassTimer<void (TClass::*)(TArgs...) const, TFuncPtr, TTimer>::ClassTimer(TClass& obj)
+  inline ClassTimer<void (TClass::*)(TArgs...) const, TFuncPtr, TTimer>::ClassTimer(
+      TClass& obj)
       : ClassTimerImpl<TClass, TTimer>(obj) {}
 
   template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...) const,
             typename TTimer>
-  void ClassTimer<void (TClass::*)(TArgs...) const, TFuncPtr, TTimer>::call(
+  inline void ClassTimer<void (TClass::*)(TArgs...) const, TFuncPtr, TTimer>::call(
       TArgs... args) {
     this->start_ = ClassTimerImpl<TClass, TTimer>::clock_type::now();
     (this->obj_.*TFuncPtr)(std::forward<TArgs>(args)...);
@@ -92,4 +96,4 @@ namespace corsika {
     return;
   }
 
-} // namespace corsika
\ No newline at end of file
+} // namespace corsika
diff --git a/corsika/detail/framework/analytics/FunctionTimer.inl b/corsika/detail/framework/analytics/FunctionTimer.inl
index e00bebc338db81acf099763e01adb7cc3a3d3bf5..911fb683ce86ff5aa6fe38a8bc223ce327fab48e 100644
--- a/corsika/detail/framework/analytics/FunctionTimer.inl
+++ b/corsika/detail/framework/analytics/FunctionTimer.inl
@@ -13,12 +13,12 @@
 namespace corsika {
 
   template <typename TFunc, typename TTime>
-  FunctionTimer<TFunc, TTime>::FunctionTimer(TFunc f)
+  inline FunctionTimer<TFunc, TTime>::FunctionTimer(TFunc f)
       : function_(f) {}
 
   template <typename TFunc, typename TTime>
   template <typename... TArgs>
-  auto FunctionTimer<TFunc, TTime>::operator()(TArgs&&... args)
+  inline auto FunctionTimer<TFunc, TTime>::operator()(TArgs&&... args)
       -> std::invoke_result_t<TFunc, TArgs...> {
     this->startTimer();
     auto tmp = function_(std::forward<TArgs>(args)...);
diff --git a/corsika/detail/framework/core/Cascade.inl b/corsika/detail/framework/core/Cascade.inl
index 784e0801eacccbcc0442ee03a64c80acfe557e43..da2a3500f2b6297fb610ae66187db3f8ef5cb6f3 100644
--- a/corsika/detail/framework/core/Cascade.inl
+++ b/corsika/detail/framework/core/Cascade.inl
@@ -10,15 +10,14 @@
 
 #include <corsika/framework/core/PhysicalUnits.hpp>
 #include <corsika/framework/process/ProcessReturn.hpp>
+#include <corsika/framework/process/ContinuousProcessStepLength.hpp>
+#include <corsika/framework/process/ContinuousProcessIndex.hpp>
 #include <corsika/framework/random/ExponentialDistribution.hpp>
 #include <corsika/framework/random/RNGManager.hpp>
 #include <corsika/framework/random/UniformRealDistribution.hpp>
 #include <corsika/framework/stack/SecondaryView.hpp>
 #include <corsika/media/Environment.hpp>
 
-#include <corsika/setup/SetupStack.hpp>
-#include <corsika/setup/SetupTrajectory.hpp>
-
 #include <cassert>
 #include <cmath>
 #include <iostream>
@@ -29,7 +28,7 @@ namespace corsika {
 
   template <typename TTracking, typename TProcessList, typename TOutput, typename TStack,
             typename TStackView>
-  void Cascade<TTracking, TProcessList, TOutput, TStack, TStackView>::run() {
+  inline void Cascade<TTracking, TProcessList, TOutput, TStack, TStackView>::run() {
     setNodes(); // put each particle on stack in correct environment volume
 
     // start this event (i.e. this shower)
@@ -62,7 +61,8 @@ namespace corsika {
 
   template <typename TTracking, typename TProcessList, typename TOutput, typename TStack,
             typename TStackView>
-  void Cascade<TTracking, TProcessList, TOutput, TStack, TStackView>::forceInteraction() {
+  inline void
+  Cascade<TTracking, TProcessList, TOutput, TStack, TStackView>::forceInteraction() {
     CORSIKA_LOG_TRACE("forced interaction!");
     setNodes();
     auto vParticle = stack_.getNextParticle();
@@ -74,7 +74,8 @@ namespace corsika {
 
   template <typename TTracking, typename TProcessList, typename TOutput, typename TStack,
             typename TStackView>
-  void Cascade<TTracking, TProcessList, TOutput, TStack, TStackView>::step(Particle& vParticle) {
+  inline void Cascade<TTracking, TProcessList, TOutput, TStack, TStackView>::step(
+      Particle& vParticle) {
 
     // determine combined total interaction length (inverse)
     InverseGrammageType const total_inv_lambda =
@@ -124,11 +125,25 @@ namespace corsika {
                                                                           next_interact);
 
     // determine the maximum geometric step length
-    LengthType const continuous_max_dist = sequence_.getMaxStepLength(vParticle, step);
+    ContinuousProcessStepLength const continuousMaxStep =
+        sequence_.getMaxStepLength(vParticle, step);
+    LengthType const continuous_max_dist = continuousMaxStep;
 
     // take minimum of geometry, interaction, decay for next step
-    auto const min_distance =
-        std::min({distance_interact, distance_decay, continuous_max_dist, geomMaxLength});
+    LengthType const min_discrete = std::min(distance_interact, distance_decay);
+    LengthType const min_non_continuous = std::min(min_discrete, geomMaxLength);
+    LengthType const min_distance = std::min(min_non_continuous, continuous_max_dist);
+
+    // inform ContinuousProcesses (if applicable) that it is responsible for step-limit
+    // this would become simpler if we follow the idea of Max to enumerate ALL types of
+    // processes. Then non-contunuous are included and no further logic is needed to
+    // distinguish between continuous and non-continuous limit.
+    ContinuousProcessIndex limitingId;
+    bool const isContinuous = continuous_max_dist < min_non_continuous;
+    if (isContinuous) {
+      limitingId =
+          continuousMaxStep; // the current step IS limited by a known continuous process
+    }
 
     CORSIKA_LOG_DEBUG(
         "transport particle by : {} m "
@@ -139,66 +154,66 @@ namespace corsika {
         min_distance / 1_m, geomMaxLength / 1_m, distance_decay / 1_m,
         distance_interact / 1_m, continuous_max_dist / 1_m);
 
-    // here the particle is actually moved along the trajectory to new position:
-    // std::visit(setup::ParticleUpdate<particle_type>{vParticle}, step);
+    // move particle along the trajectory to new position
+    // also update momentum/direction/time
     step.setLength(min_distance);
     vParticle.setPosition(step.getPosition(1));
-    // assumption: tracking does not change absolute momentum:
+    // assumption: tracking does not change absolute momentum (continuous physics can and
+    // will):
     vParticle.setMomentum(step.getDirection(1) * vParticle.getMomentum().getNorm());
     vParticle.setTime(vParticle.getTime() + step.getDuration());
 
     // apply all continuous processes on particle + track
-    if (sequence_.doContinuous(vParticle, step) == ProcessReturn::ParticleAbsorbed) {
+    if (sequence_.doContinuous(vParticle, step, limitingId) ==
+        ProcessReturn::ParticleAbsorbed) {
       CORSIKA_LOG_DEBUG("Cascade: delete absorbed particle PID={} E={} GeV",
                         vParticle.getPID(), vParticle.getEnergy() / 1_GeV);
-      if (!vParticle.isErased()) vParticle.erase();
+      if (vParticle.isErased()) {
+        CORSIKA_LOG_WARN(
+            "Particle marked as Absorbed in doContinuous, but prematurely erased. This "
+            "may be bug. Check.");
+      } else {
+        vParticle.erase();
+      }
       return;
     }
+    if (isContinuous) {
+      return; // there is nothing further, step is finished
+    }
 
-    CORSIKA_LOG_DEBUG("sth. happening before geometric limit ? {}",
+    CORSIKA_LOG_DEBUG("discrete process before geometric limit ? {}",
                       ((min_distance < geomMaxLength) ? "yes" : "no"));
 
-    if (min_distance < geomMaxLength) { // interaction to happen within geometric limit
+    if (geomMaxLength < min_discrete) {
+      // geometric / tracking limit
 
-      // check whether decay or interaction limits this step the
-      // outcome of decay or interaction MAY be a) new particles in
-      // secondaries, b) the projectile particle deleted (or
-      // changed)
+      if (nextVol != currentLogicalNode) {
+        // boundary crossing, step is limited by volume boundary
 
-      TStackView secondaries(vParticle);
+        CORSIKA_LOG_DEBUG("volume boundary crossing to {}", fmt::ptr(nextVol));
 
-      if (min_distance < continuous_max_dist) {
+        if (nextVol == environment_.getUniverse().get()) {
+          CORSIKA_LOG_DEBUG(
+              "particle left physics world, is now in unknown space -> delete");
+          vParticle.erase();
+        }
+        vParticle.setNode(nextVol);
         /*
-          Create SecondaryView object on Stack. The data container
-          remains untouched and identical, and 'projectil' is identical
-          to 'vParticle' above this line. However,
-          projectil.AddSecondaries populate the SecondaryView, which can
-          then be used afterwards for further processing. Thus: it is
-          important to use projectle/view (and not vParticle) for Interaction,
-          and Decay!
-        */
+          doBoundary may delete the particle (or not)
 
-        [[maybe_unused]] auto projectile = secondaries.getProjectile();
-
-        if (distance_interact < distance_decay) {
-          interaction(secondaries);
-        } else {
-          decay(secondaries);
-          // make sure particle actually did decay if it should have done so
-          if (secondaries.getSize() == 1 &&
-              projectile.getPID() == secondaries.getNextParticle().getPID())
-            throw std::runtime_error(fmt::format("Particle {} decays into itself!",
-                                                 get_name(projectile.getPID())));
-        }
+          caveat: any changes to vParticle, or even the production
+          of new secondaries is currently not passed to ParticleCut,
+          thus, particles outside the desired phase space may be produced.
 
-        sequence_.doSecondaries(secondaries);
-        vParticle.erase();
-      } else { // step-length limitation within volume
+          \todo: this must be fixed.
+        */
 
-        CORSIKA_LOG_DEBUG("step-length limitation");
-        // no further physics happens here. just proceed to next step.
+        sequence_.doBoundaryCrossing(vParticle, *currentLogicalNode, *nextVol);
+        return; // step finished
       }
 
+      CORSIKA_LOG_DEBUG("step limit reached (e.g. deflection). nothing further happens.");
+
       [[maybe_unused]] auto const assertion = [&] {
         auto const* numericalNodeAfterStep =
             environment_.getUniverse()->getContainingNode(vParticle.getPosition());
@@ -211,37 +226,49 @@ namespace corsika {
       assert(assertion()); // numerical and logical nodes should match, since
                            // we did not cross any volume boundary
 
-    } else { // boundary crossing, step is limited by volume boundary
-
-      if (nextVol != currentLogicalNode) {
-
-        CORSIKA_LOG_DEBUG("volume boundary crossing to {}", fmt::ptr(nextVol));
-
-        if (nextVol == environment_.getUniverse().get()) {
-          CORSIKA_LOG_DEBUG(
-              "particle left physics world, is now in unknown space -> delete");
-          vParticle.erase();
-        }
-        vParticle.setNode(nextVol);
-        /*
-          doBoundary may delete the particle (or not)
+      // step length limit
+      return;
+    }
 
-          caveat: any changes to vParticle, or even the production
-          of new secondaries is currently not passed to ParticleCut,
-          thus, particles outside the desired phase space may be produced.
+    // interaction or decay to happen in this step
+    // the outcome of decay or interaction MAY be a) new particles in
+    // secondaries, b) the projectile particle deleted (or
+    // changed)
 
-          \todo: this must be fixed.
-        */
+    TStackView secondaries(vParticle);
 
-        sequence_.doBoundaryCrossing(vParticle, *currentLogicalNode, *nextVol);
-      }
+    /*
+      Create SecondaryView object on Stack. The data container
+      remains untouched and identical, and 'projectil' is identical
+      to 'vParticle' above this line. However,
+      projectil.AddSecondaries populate the SecondaryView, which can
+      then be used afterwards for further processing. Thus: it is
+      important to use projectle/view (and not vParticle) for Interaction,
+      and Decay!
+    */
+
+    [[maybe_unused]] auto projectile = secondaries.getProjectile();
+
+    if (distance_interact < distance_decay) {
+      interaction(secondaries);
+    } else {
+      decay(secondaries);
+      // make sure particle actually did decay if it should have done so
+      // \todo this should go to a validation code and not be included here
+      if (secondaries.getSize() == 1 &&
+          projectile.getPID() == secondaries.getNextParticle().getPID())
+        throw std::runtime_error(fmt::format("Particle {} decays into itself!",
+                                             get_name(projectile.getPID())));
     }
+
+    sequence_.doSecondaries(secondaries);
+    vParticle.erase();
   }
 
   template <typename TTracking, typename TProcessList, typename TOutput, typename TStack,
             typename TStackView>
-  ProcessReturn Cascade<TTracking, TProcessList, TOutput, TStack, TStackView>::decay(
-      TStackView& view) {
+  inline ProcessReturn
+  Cascade<TTracking, TProcessList, TOutput, TStack, TStackView>::decay(TStackView& view) {
     CORSIKA_LOG_DEBUG("decay");
     InverseTimeType const actual_decay_time = sequence_.getInverseLifetime(view.parent());
 
@@ -258,8 +285,8 @@ namespace corsika {
 
   template <typename TTracking, typename TProcessList, typename TOutput, typename TStack,
             typename TStackView>
-  ProcessReturn Cascade<TTracking, TProcessList, TOutput, TStack, TStackView>::interaction(
-      TStackView& view) {
+  inline ProcessReturn Cascade<TTracking, TProcessList, TOutput, TStack,
+                               TStackView>::interaction(TStackView& view) {
     CORSIKA_LOG_DEBUG("collide");
 
     InverseGrammageType const current_inv_length =
@@ -278,7 +305,7 @@ namespace corsika {
 
   template <typename TTracking, typename TProcessList, typename TOutput, typename TStack,
             typename TStackView>
-  void Cascade<TTracking, TProcessList, TOutput, TStack, TStackView>::setNodes() {
+  inline void Cascade<TTracking, TProcessList, TOutput, TStack, TStackView>::setNodes() {
     std::for_each(stack_.begin(), stack_.end(), [&](auto& p) {
       auto const* numericalNode =
           environment_.getUniverse()->getContainingNode(p.getPosition());
@@ -288,7 +315,7 @@ namespace corsika {
 
   template <typename TTracking, typename TProcessList, typename TOutput, typename TStack,
             typename TStackView>
-  void Cascade<TTracking, TProcessList, TOutput, TStack, TStackView>::setEventType(
+  inline void Cascade<TTracking, TProcessList, TOutput, TStack, TStackView>::setEventType(
       TStackView& view, [[maybe_unused]] history::EventType eventType) {
     if constexpr (TStackView::has_event) {
       for (auto&& sec : view) { sec.getEvent()->setEventType(eventType); }
diff --git a/corsika/detail/framework/core/Logging.inl b/corsika/detail/framework/core/Logging.inl
index e99e788b8de6a6eaf2e3965fb10c51049e720058..ca6776fc788ce8ca57a5bfe409d7fa83eb618823 100644
--- a/corsika/detail/framework/core/Logging.inl
+++ b/corsika/detail/framework/core/Logging.inl
@@ -1,4 +1,4 @@
-/*
+/* -*-c++-*-
  * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
  *
  * This software is distributed under the terms of the GNU General Public
@@ -21,15 +21,13 @@ namespace corsika {
     /*
      * The default pattern for CORSIKA8 loggers.
      */
-    std::string const default_pattern{"[%n:%^%-8l%$] %v"};
-
     inline auto set_default_level(level::level_enum const minlevel) -> void {
       spdlog::set_level(minlevel);
     }
 
     template <typename TLogger>
     inline auto add_source_info(TLogger& logger) -> void {
-      logger->set_pattern("[%n:%^%-8l%$(%s:%!:%#)] %v");
+      logger->set_pattern(source_pattern);
     }
 
     template <typename TLogger>
@@ -47,7 +45,13 @@ namespace corsika {
     auto logger = spdlog::stdout_color_mt(name);
 
     // set the default C8 format
-    logger->set_pattern(logging::default_pattern);
+#if (!defined(_GLIBCXX_USE_CXX11_ABI) || _GLIBCXX_USE_CXX11_ABI == 1)
+    logger->set_pattern(default_pattern);
+#else
+    // special case: gcc from the software collections devtoolset
+    std::string dp(default_pattern);
+    logger->set_pattern(dp);
+#endif
 
     // if defaultlog is True, we set this as the default spdlog logger.
     if (defaultlog) { spdlog::set_default_logger(logger); }
diff --git a/corsika/detail/framework/core/ParticleProperties.inl b/corsika/detail/framework/core/ParticleProperties.inl
index b478b1e17f3f12f850482c6c652c8c884f104656..47504f4ea5f0dd02672418e260eb61dd6971c5c3 100644
--- a/corsika/detail/framework/core/ParticleProperties.inl
+++ b/corsika/detail/framework/core/ParticleProperties.inl
@@ -13,72 +13,74 @@
 
 namespace corsika {
 
-  HEPEnergyType constexpr get_energy_threshold(Code const p) {
+  inline HEPEnergyType constexpr get_energy_threshold(Code const p) {
     return particle::detail::thresholds[static_cast<CodeIntType>(p)];
   }
 
-  void constexpr set_energy_threshold(Code const p, HEPEnergyType const val) {
+  inline void constexpr set_energy_threshold(Code const p, HEPEnergyType const val) {
     particle::detail::thresholds[static_cast<CodeIntType>(p)] = val;
   }
 
-  HEPMassType constexpr get_mass(Code const p) {
+  inline HEPMassType constexpr get_mass(Code const p) {
     if (p == Code::Nucleus)
       throw std::runtime_error("Cannot GetMass() of particle::Nucleus -> unspecified");
     return particle::detail::masses[static_cast<CodeIntType>(p)];
   }
 
-  PDGCode constexpr get_PDG(Code const p) {
+  inline PDGCode constexpr get_PDG(Code const p) {
     return particle::detail::pdg_codes[static_cast<CodeIntType>(p)];
   }
 
-  int16_t constexpr get_charge_number(Code const code) {
+  inline int16_t constexpr get_charge_number(Code const code) {
     if (code == Code::Nucleus)
       throw std::runtime_error("charge of particle::Nucleus undefined");
     return particle::detail::electric_charges[static_cast<CodeIntType>(code)];
   }
 
-  ElectricChargeType constexpr get_charge(Code const code) {
+  inline ElectricChargeType constexpr get_charge(Code const code) {
     return get_charge_number(code) * constants::e;
   }
 
-  std::string_view constexpr get_name(Code const code) {
+  inline std::string_view constexpr get_name(Code const code) {
     return particle::detail::names[static_cast<CodeIntType>(code)];
   }
 
-  TimeType constexpr get_lifetime(Code const p) {
+  inline TimeType constexpr get_lifetime(Code const p) {
     return particle::detail::lifetime[static_cast<CodeIntType>(p)] * second;
   }
 
-  bool constexpr is_hadron(Code const p) {
+  inline bool constexpr is_hadron(Code const p) {
     return particle::detail::isHadron[static_cast<CodeIntType>(p)];
   }
 
-  bool constexpr is_em(Code c) {
+  inline bool constexpr is_em(Code const c) {
     return c == Code::Electron || c == Code::Positron || c == Code::Gamma;
   }
 
-  bool constexpr is_muon(Code c) { return c == Code::MuPlus || c == Code::MuMinus; }
+  inline bool constexpr is_muon(Code const c) {
+    return c == Code::MuPlus || c == Code::MuMinus;
+  }
 
-  bool constexpr is_neutrino(Code c) {
+  inline bool constexpr is_neutrino(Code const c) {
     return c == Code::NuE || c == Code::NuMu || c == Code::NuTau || c == Code::NuEBar ||
            c == Code::NuMuBar || c == Code::NuTauBar;
   }
 
-  int constexpr get_nucleus_A(Code const code) {
+  inline int constexpr get_nucleus_A(Code const code) {
     if (code == Code::Nucleus) {
       throw std::runtime_error("get_nucleus_A(Code::Nucleus) is impossible!");
     }
     return particle::detail::nucleusA[static_cast<CodeIntType>(code)];
   }
 
-  int constexpr get_nucleus_Z(Code const code) {
+  inline int constexpr get_nucleus_Z(Code const code) {
     if (code == Code::Nucleus) {
       throw std::runtime_error("get_nucleus_Z(Code::Nucleus) is impossible!");
     }
     return particle::detail::nucleusZ[static_cast<CodeIntType>(code)];
   }
 
-  bool constexpr is_nucleus(Code const code) {
+  inline bool constexpr is_nucleus(Code const code) {
     return (code == Code::Nucleus) || (get_nucleus_A(code) != 0);
   }
 
@@ -86,7 +88,7 @@ namespace corsika {
     return stream << get_name(code);
   }
 
-  inline Code convert_from_PDG(PDGCode p) {
+  inline Code convert_from_PDG(PDGCode const p) {
     static_assert(particle::detail::conversionArray.size() % 2 == 1);
     // this will fail, for the strange case where the maxPDG is negative...
     int constexpr maxPDG{(particle::detail::conversionArray.size() - 1) >> 1};
@@ -102,7 +104,7 @@ namespace corsika {
     return get_mass(Code::Proton) * Z + (A - Z) * get_mass(Code::Neutron);
   }
 
-  std::initializer_list<Code> constexpr get_all_particles() {
+  inline std::initializer_list<Code> constexpr get_all_particles() {
     return particle::detail::all_particles;
   }
 
diff --git a/corsika/detail/framework/geometry/BaseVector.inl b/corsika/detail/framework/geometry/BaseVector.inl
index 50ec4b03b249ec3bfbe89f41c25fd4f33d04dc3f..486a62d2650e47b7163a2d4092169e42a820e442 100644
--- a/corsika/detail/framework/geometry/BaseVector.inl
+++ b/corsika/detail/framework/geometry/BaseVector.inl
@@ -14,17 +14,18 @@
 namespace corsika {
 
   template <typename TDimension>
-  CoordinateSystemPtr BaseVector<TDimension>::getCoordinateSystem() const {
+  inline CoordinateSystemPtr BaseVector<TDimension>::getCoordinateSystem() const {
     return cs_;
   }
 
   template <typename TDimension>
-  QuantityVector<TDimension> const& BaseVector<TDimension>::getQuantityVector() const {
+  inline QuantityVector<TDimension> const& BaseVector<TDimension>::getQuantityVector()
+      const {
     return quantityVector_;
   }
 
   template <typename TDimension>
-  QuantityVector<TDimension>& BaseVector<TDimension>::getQuantityVector() {
+  inline QuantityVector<TDimension>& BaseVector<TDimension>::getQuantityVector() {
     return quantityVector_;
   }
 
diff --git a/corsika/detail/framework/geometry/CoordinateSystem.inl b/corsika/detail/framework/geometry/CoordinateSystem.inl
index efcf716d3320487334713dd79bdf77ddd67f0a24..faa759aa769f9cd3382d06bca45377bb5488c3ee 100644
--- a/corsika/detail/framework/geometry/CoordinateSystem.inl
+++ b/corsika/detail/framework/geometry/CoordinateSystem.inl
@@ -19,9 +19,11 @@
 
 namespace corsika {
 
-  CoordinateSystemPtr CoordinateSystem::getReferenceCS() const { return referenceCS_; }
+  inline CoordinateSystemPtr CoordinateSystem::getReferenceCS() const {
+    return referenceCS_;
+  }
 
-  EigenTransform const& CoordinateSystem::getTransform() const { return transf_; }
+  inline EigenTransform const& CoordinateSystem::getTransform() const { return transf_; }
 
   inline bool CoordinateSystem::operator==(CoordinateSystem const& cs) const {
     return referenceCS_ == cs.referenceCS_ && transf_.matrix() == cs.transf_.matrix();
diff --git a/corsika/detail/framework/geometry/FourVector.inl b/corsika/detail/framework/geometry/FourVector.inl
index 135790bfcfa83efaf1a78f218228d0c2557f5853..5d871430dad5390aaa7b80d8fb83d7e16c165f03 100644
--- a/corsika/detail/framework/geometry/FourVector.inl
+++ b/corsika/detail/framework/geometry/FourVector.inl
@@ -15,47 +15,47 @@
 namespace corsika {
 
   template <typename TTimeType, typename TSpaceVecType>
-  TTimeType FourVector<TTimeType, TSpaceVecType>::getTimeLikeComponent() const {
+  inline TTimeType FourVector<TTimeType, TSpaceVecType>::getTimeLikeComponent() const {
     return timeLike_;
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  TSpaceVecType& FourVector<TTimeType, TSpaceVecType>::getSpaceLikeComponents() {
+  inline TSpaceVecType& FourVector<TTimeType, TSpaceVecType>::getSpaceLikeComponents() {
     return spaceLike_;
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  TSpaceVecType const& FourVector<TTimeType, TSpaceVecType>::getSpaceLikeComponents()
-      const {
+  inline TSpaceVecType const&
+  FourVector<TTimeType, TSpaceVecType>::getSpaceLikeComponents() const {
     return spaceLike_;
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  typename FourVector<TTimeType, TSpaceVecType>::norm_square_type
+  inline typename FourVector<TTimeType, TSpaceVecType>::norm_square_type
   FourVector<TTimeType, TSpaceVecType>::getNormSqr() const {
     return getTimeSquared() - spaceLike_.getSquaredNorm();
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  typename FourVector<TTimeType, TSpaceVecType>::norm_type
+  inline typename FourVector<TTimeType, TSpaceVecType>::norm_type
   FourVector<TTimeType, TSpaceVecType>::getNorm() const {
 
     return sqrt(abs(getNormSqr()));
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  bool FourVector<TTimeType, TSpaceVecType>::isTimelike() const {
+  inline bool FourVector<TTimeType, TSpaceVecType>::isTimelike() const {
     return getTimeSquared() < spaceLike_.getSquaredNorm();
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  bool FourVector<TTimeType, TSpaceVecType>::isSpacelike() const {
+  inline bool FourVector<TTimeType, TSpaceVecType>::isSpacelike() const {
     return getTimeSquared() > spaceLike_.getSquaredNorm();
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::operator+=(
-      FourVector const& b) {
+  inline FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::
+  operator+=(FourVector const& b) {
     timeLike_ += b.timeLike_;
     spaceLike_ += b.spaceLike_;
 
@@ -63,39 +63,39 @@ namespace corsika {
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::operator-=(
-      FourVector const& b) {
+  inline FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::
+  operator-=(FourVector const& b) {
     timeLike_ -= b.timeLike_;
     spaceLike_ -= b.spaceLike_;
     return *this;
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::operator*=(
-      double const b) {
+  inline FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::
+  operator*=(double const b) {
     timeLike_ *= b;
     spaceLike_ *= b;
     return *this;
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::operator/=(
-      double const b) {
+  inline FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::
+  operator/=(double const b) {
     timeLike_ /= b;
     spaceLike_.getComponents() /= b;
     return *this;
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::operator/(
-      double const b) {
+  inline FourVector<TTimeType, TSpaceVecType>& FourVector<TTimeType, TSpaceVecType>::
+  operator/(double const b) {
     *this /= b;
     return *this;
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  typename FourVector<TTimeType, TSpaceVecType>::norm_type
-  FourVector<TTimeType, TSpaceVecType>::operator*(FourVector const& b) {
+  inline typename FourVector<TTimeType, TSpaceVecType>::norm_type
+      FourVector<TTimeType, TSpaceVecType>::operator*(FourVector const& b) {
     if constexpr (std::is_same<time_type, decltype(std::declval<space_type>() / meter *
                                                    second)>::value)
       return timeLike_ * b.timeLike_ * constants::cSquared - spaceLike_.norm();
@@ -104,7 +104,7 @@ namespace corsika {
   }
 
   template <typename TTimeType, typename TSpaceVecType>
-  typename FourVector<TTimeType, TSpaceVecType>::norm_square_type
+  inline typename FourVector<TTimeType, TSpaceVecType>::norm_square_type
   FourVector<TTimeType, TSpaceVecType>::getTimeSquared() const {
     if constexpr (std::is_same<time_type, decltype(std::declval<space_type>() / meter *
                                                    second)>::value)
diff --git a/corsika/detail/framework/geometry/Helix.inl b/corsika/detail/framework/geometry/Helix.inl
index daf0c310fcd14762c3942d20221be35130431f06..03c0fd85decf43765f75dd390b60c71a62ae58ac 100644
--- a/corsika/detail/framework/geometry/Helix.inl
+++ b/corsika/detail/framework/geometry/Helix.inl
@@ -15,23 +15,23 @@
 
 namespace corsika {
 
-  LengthType Helix::getRadius() const { return radius_; }
+  inline LengthType Helix::getRadius() const { return radius_; }
 
-  Point Helix::getPosition(TimeType const t) const {
+  inline Point Helix::getPosition(TimeType const t) const {
     return r0_ + vPar_ * t +
            (vPerp_ * (std::cos(omegaC_ * t) - 1) + uPerp_ * std::sin(omegaC_ * t)) /
                omegaC_;
   }
 
-  Point Helix::getPositionFromArclength(LengthType const l) const {
+  inline Point Helix::getPositionFromArclength(LengthType const l) const {
     return getPosition(getTimeFromArclength(l));
   }
 
-  LengthType Helix::getArcLength(TimeType const t1, TimeType const t2) const {
+  inline LengthType Helix::getArcLength(TimeType const t1, TimeType const t2) const {
     return (vPar_ + vPerp_).getNorm() * (t2 - t1);
   }
 
-  TimeType Helix::getTimeFromArclength(LengthType const l) const {
+  inline TimeType Helix::getTimeFromArclength(LengthType const l) const {
     return l / (vPar_ + vPerp_).getNorm();
   }
 
diff --git a/corsika/detail/framework/geometry/Plane.inl b/corsika/detail/framework/geometry/Plane.inl
index e158e04d36a59bbec08e1b3e2b7e6aebc76ee7ab..54e60157c0b6ab4194365b4bdddfa6311fb67bac 100644
--- a/corsika/detail/framework/geometry/Plane.inl
+++ b/corsika/detail/framework/geometry/Plane.inl
@@ -12,6 +12,8 @@
 #include <corsika/framework/geometry/Point.hpp>
 #include <corsika/framework/geometry/Vector.hpp>
 
+#include <sstream>
+
 namespace corsika {
 
   inline bool Plane::isAbove(Point const& vP) const {
@@ -24,6 +26,12 @@ namespace corsika {
 
   inline Point const& Plane::getCenter() const { return center_; }
 
-  inline Plane::DimLessVec const& Plane::getNormal() const { return normal_; }
+  inline DirectionVector const& Plane::getNormal() const { return normal_; }
+
+  inline std::string Plane::asString() const {
+    std::ostringstream txt;
+    txt << "center=" << center_ << ", normal=" << normal_;
+    return txt.str();
+  }
 
 } // namespace corsika
diff --git a/corsika/detail/framework/geometry/Point.inl b/corsika/detail/framework/geometry/Point.inl
index 6c550c1ce716deb8e3004f775a81b9bebb182d75..c5c0a98a64491d3c6c752b647d60cdb1c2090e73 100644
--- a/corsika/detail/framework/geometry/Point.inl
+++ b/corsika/detail/framework/geometry/Point.inl
@@ -15,11 +15,11 @@
 
 namespace corsika {
 
-  QuantityVector<length_d> const& Point::getCoordinates() const {
+  inline QuantityVector<length_d> const& Point::getCoordinates() const {
     return BaseVector<length_d>::getQuantityVector();
   }
 
-  QuantityVector<length_d>& Point::getCoordinates() {
+  inline QuantityVector<length_d>& Point::getCoordinates() {
     return BaseVector<length_d>::getQuantityVector();
   }
 
@@ -60,7 +60,8 @@ namespace corsika {
   }
 
   /// this always returns a QuantityVector as triple
-  QuantityVector<length_d> Point::getCoordinates(CoordinateSystemPtr const& pCS) const {
+  inline QuantityVector<length_d> Point::getCoordinates(
+      CoordinateSystemPtr const& pCS) const {
     CoordinateSystemPtr const& cs = BaseVector<length_d>::getCoordinateSystem();
     if (*pCS == *cs) {
       return BaseVector<length_d>::getQuantityVector();
@@ -72,12 +73,12 @@ namespace corsika {
   }
 
   /// this always returns a QuantityVector as triple
-  QuantityVector<length_d>& Point::getCoordinates(CoordinateSystemPtr const& pCS) {
+  inline QuantityVector<length_d>& Point::getCoordinates(CoordinateSystemPtr const& pCS) {
     if (*pCS != *BaseVector<length_d>::getCoordinateSystem()) { rebase(pCS); }
     return BaseVector<length_d>::getQuantityVector();
   }
 
-  void Point::rebase(CoordinateSystemPtr const& pCS) {
+  inline void Point::rebase(CoordinateSystemPtr const& pCS) {
     BaseVector<length_d>::setQuantityVector(QuantityVector<length_d>(
         get_transformation(*BaseVector<length_d>::getCoordinateSystem().get(),
                            *pCS.get()) *
@@ -85,14 +86,20 @@ namespace corsika {
     BaseVector<length_d>::setCoordinateSystem(pCS);
   }
 
-  Point Point::operator+(Vector<length_d> const& pVec) const {
+  inline Point Point::operator+(Vector<length_d> const& pVec) const {
     CoordinateSystemPtr const& cs = BaseVector<length_d>::getCoordinateSystem();
     return Point(cs, getCoordinates() + pVec.getComponents(cs));
   }
 
-  Vector<length_d> Point::operator-(Point const& pB) const {
+  inline Vector<length_d> Point::operator-(Point const& pB) const {
     CoordinateSystemPtr const& cs = BaseVector<length_d>::getCoordinateSystem();
     return Vector<length_d>(cs, getCoordinates() - pB.getCoordinates(cs));
   }
 
+  inline std::ostream& operator<<(std::ostream& os, corsika::Point const& p) {
+    auto const& qv = p.getCoordinates();
+    os << qv << " (ref:" << fmt::ptr(p.getCoordinateSystem()) << ")";
+    return os;
+  }
+
 } // namespace corsika
diff --git a/corsika/detail/framework/geometry/QuantityVector.inl b/corsika/detail/framework/geometry/QuantityVector.inl
index 994f70689a97d2cc6f183f7d6e03e9558f08b7ba..2ca24ee5a4a9c080276731e9652f7b03202a82ec 100644
--- a/corsika/detail/framework/geometry/QuantityVector.inl
+++ b/corsika/detail/framework/geometry/QuantityVector.inl
@@ -17,8 +17,8 @@
 namespace corsika {
 
   template <typename TDimension>
-  inline typename QuantityVector<TDimension>::quantity_type
-  QuantityVector<TDimension>::operator[](size_t const index) const {
+  inline typename QuantityVector<TDimension>::quantity_type QuantityVector<TDimension>::
+  operator[](size_t const index) const {
     return quantity_type(phys::units::detail::magnitude_tag, eigenVector_[index]);
   }
 
@@ -145,7 +145,7 @@ namespace corsika {
 
   template <typename TDimension>
   inline std::ostream& operator<<(std::ostream& os,
-                                  corsika::QuantityVector<TDimension> const qv) {
+                                  corsika::QuantityVector<TDimension> const& qv) {
     using quantity_type = phys::units::quantity<TDimension, double>;
 
     os << '(' << qv.eigenVector_(0) << ' ' << qv.eigenVector_(1) << ' '
diff --git a/corsika/detail/framework/geometry/Vector.inl b/corsika/detail/framework/geometry/Vector.inl
index cbfd597b3e556fc04c03b0829b4f949bcb459ae8..352eb8b3095ddec2221c1af41dc8f71b499746e2 100644
--- a/corsika/detail/framework/geometry/Vector.inl
+++ b/corsika/detail/framework/geometry/Vector.inl
@@ -88,7 +88,7 @@ namespace corsika {
   }
 
   template <typename TDimension>
-  void Vector<TDimension>::rebase(CoordinateSystemPtr const& pCS) {
+  inline void Vector<TDimension>::rebase(CoordinateSystemPtr const& pCS) {
     BaseVector<TDimension>::setQuantityVector(QuantityVector<TDimension>(
         get_transformation(*BaseVector<TDimension>::getCoordinateSystem().get(),
                            *pCS.get())
@@ -110,7 +110,7 @@ namespace corsika {
 
   template <typename TDimension>
   template <typename TDimension2>
-  auto Vector<TDimension>::getParallelProjectionOnto(
+  inline auto Vector<TDimension>::getParallelProjectionOnto(
       Vector<TDimension2> const& pVec, CoordinateSystemPtr const& pCS) const {
     auto const ourCompVec = getComponents(pCS);
     auto const otherCompVec = pVec.getComponents(pCS);
@@ -123,34 +123,36 @@ namespace corsika {
 
   template <typename TDimension>
   template <typename TDimension2>
-  auto Vector<TDimension>::getParallelProjectionOnto(
+  inline auto Vector<TDimension>::getParallelProjectionOnto(
       Vector<TDimension2> const& pVec) const {
     return getParallelProjectionOnto<TDimension2>(
         pVec, BaseVector<TDimension>::getCoordinateSystem());
   }
 
   template <typename TDimension>
-  Vector<TDimension> Vector<TDimension>::operator+(Vector<TDimension> const& pVec) const {
+  inline Vector<TDimension> Vector<TDimension>::operator+(
+      Vector<TDimension> const& pVec) const {
     CoordinateSystemPtr const& cs = BaseVector<TDimension>::getCoordinateSystem();
     auto const components = getComponents(cs) + pVec.getComponents(cs);
     return Vector<TDimension>(BaseVector<TDimension>::getCoordinateSystem(), components);
   }
 
   template <typename TDimension>
-  Vector<TDimension> Vector<TDimension>::operator-(Vector<TDimension> const& pVec) const {
+  inline Vector<TDimension> Vector<TDimension>::operator-(
+      Vector<TDimension> const& pVec) const {
     CoordinateSystemPtr const& cs = BaseVector<TDimension>::getCoordinateSystem();
     return Vector<TDimension>(cs, getComponents() - pVec.getComponents(cs));
   }
 
   template <typename TDimension>
-  auto& Vector<TDimension>::operator*=(double const p) {
+  inline auto& Vector<TDimension>::operator*=(double const p) {
     BaseVector<TDimension>::getQuantityVector() *= p;
     return *this;
   }
 
   template <typename TDimension>
   template <typename TScalarDim>
-  auto Vector<TDimension>::operator*(
+  inline auto Vector<TDimension>::operator*(
       phys::units::quantity<TScalarDim, double> const p) const {
     using ProdDim = phys::units::detail::product_d<TDimension, TScalarDim>;
 
@@ -160,51 +162,51 @@ namespace corsika {
 
   template <typename TDimension>
   template <typename TScalarDim>
-  auto Vector<TDimension>::operator/(
+  inline auto Vector<TDimension>::operator/(
       phys::units::quantity<TScalarDim, double> const p) const {
     return (*this) * (1 / p);
   }
 
   template <typename TDimension>
-  auto Vector<TDimension>::operator*(double const p) const {
+  inline auto Vector<TDimension>::operator*(double const p) const {
     return Vector<TDimension>(BaseVector<TDimension>::getCoordinateSystem(),
                               BaseVector<TDimension>::getQuantityVector() * p);
   }
 
   template <typename TDimension>
-  auto Vector<TDimension>::operator/(double const p) const {
+  inline auto Vector<TDimension>::operator/(double const p) const {
     return Vector<TDimension>(BaseVector<TDimension>::getCoordinateSystem(),
                               BaseVector<TDimension>::getQuantityVector() / p);
   }
 
   template <typename TDimension>
-  auto& Vector<TDimension>::operator+=(Vector<TDimension> const& pVec) {
+  inline auto& Vector<TDimension>::operator+=(Vector<TDimension> const& pVec) {
     BaseVector<TDimension>::getQuantityVector() +=
         pVec.getComponents(BaseVector<TDimension>::getCoordinateSystem());
     return *this;
   }
 
   template <typename TDimension>
-  auto& Vector<TDimension>::operator-=(Vector<TDimension> const& pVec) {
+  inline auto& Vector<TDimension>::operator-=(Vector<TDimension> const& pVec) {
     BaseVector<TDimension>::getQuantityVector() -=
         pVec.getComponents(BaseVector<TDimension>::getCoordinateSystem());
     return *this;
   }
 
   template <typename TDimension>
-  auto& Vector<TDimension>::operator-() const {
+  inline auto& Vector<TDimension>::operator-() const {
     return Vector<TDimension>(BaseVector<TDimension>::getCoordinateSystem(),
                               -BaseVector<TDimension>::getQuantityVector());
   }
 
   template <typename TDimension>
-  auto Vector<TDimension>::normalized() const {
+  inline auto Vector<TDimension>::normalized() const {
     return (*this) * (1 / getNorm());
   }
 
   template <typename TDimension>
   template <typename TDimension2>
-  auto Vector<TDimension>::cross(Vector<TDimension2> const& pV) const {
+  inline auto Vector<TDimension>::cross(Vector<TDimension2> const& pV) const {
     auto const c1 = getComponents().eigenVector_;
     auto const c2 =
         pV.getComponents(BaseVector<TDimension>::getCoordinateSystem()).eigenVector_;
@@ -216,7 +218,7 @@ namespace corsika {
 
   template <typename TDimension>
   template <typename TDimension2>
-  auto Vector<TDimension>::dot(Vector<TDimension2> const& pV) const {
+  inline auto Vector<TDimension>::dot(Vector<TDimension2> const& pV) const {
     auto const c1 = getComponents().eigenVector_;
     auto const c2 =
         pV.getComponents(BaseVector<TDimension>::getCoordinateSystem()).eigenVector_;
@@ -228,4 +230,12 @@ namespace corsika {
                                                   bareResult);
   }
 
+  template <typename TDimension>
+  inline std::ostream& operator<<(std::ostream& os,
+                                  corsika::Vector<TDimension> const& v) {
+    auto const& qv = v.getComponents();
+    os << qv << " (ref:" << fmt::ptr(v.getCoordinateSystem()) << ")";
+    return os;
+  }
+
 } // namespace corsika
diff --git a/corsika/detail/framework/process/InteractionCounter.inl b/corsika/detail/framework/process/InteractionCounter.inl
index 34c93ce43120bdd25f1c5824e6b0a561e3a7f06e..d3572bf98ef63faf37f50d2a58cfdf4b610a4179 100644
--- a/corsika/detail/framework/process/InteractionCounter.inl
+++ b/corsika/detail/framework/process/InteractionCounter.inl
@@ -13,7 +13,7 @@
 namespace corsika {
 
   template <class TCountedProcess>
-  InteractionCounter<TCountedProcess>::InteractionCounter(TCountedProcess& process)
+  inline InteractionCounter<TCountedProcess>::InteractionCounter(TCountedProcess& process)
       : process_(process) {}
 
   template <class TCountedProcess>
diff --git a/corsika/detail/framework/process/InteractionHistogram.inl b/corsika/detail/framework/process/InteractionHistogram.inl
index e6cfae869ea9ba6a9c8adc36d4e817a872822609..efeb29c0027e98d52d49722334b9ebb51bf1460a 100644
--- a/corsika/detail/framework/process/InteractionHistogram.inl
+++ b/corsika/detail/framework/process/InteractionHistogram.inl
@@ -16,13 +16,13 @@
 
 namespace corsika {
 
-  InteractionHistogram::InteractionHistogram()
+  inline InteractionHistogram::InteractionHistogram()
       : inthist_cms_{detail::hist_factory(num_bins_cms, lower_edge_cms, upper_edge_cms)}
       , inthist_lab_{detail::hist_factory(num_bins_lab, lower_edge_lab, upper_edge_lab)} {
   }
 
-  void InteractionHistogram::fill(Code projectile_id, HEPEnergyType lab_energy,
-                                  HEPEnergyType mass_target, int A, int Z) {
+  inline void InteractionHistogram::fill(Code projectile_id, HEPEnergyType lab_energy,
+                                         HEPEnergyType mass_target, int A, int Z) {
     auto constexpr inv_eV = 1 / 1_eV;
     if (projectile_id == Code::Nucleus) {
       auto const sqrtS = sqrt(A * A * (constants::nucleonMass * constants::nucleonMass) +
@@ -42,7 +42,7 @@ namespace corsika {
     }
   }
 
-  InteractionHistogram& InteractionHistogram::operator+=(
+  inline InteractionHistogram& InteractionHistogram::operator+=(
       InteractionHistogram const& other) {
     inthist_lab_ += other.inthist_lab_;
     inthist_cms_ += other.inthist_cms_;
@@ -50,7 +50,8 @@ namespace corsika {
     return *this;
   }
 
-  InteractionHistogram InteractionHistogram::operator+(InteractionHistogram other) const {
+  inline InteractionHistogram InteractionHistogram::operator+(
+      InteractionHistogram other) const {
     other.inthist_lab_ += inthist_lab_;
     other.inthist_cms_ += inthist_cms_;
 
diff --git a/corsika/detail/framework/process/ProcessSequence.inl b/corsika/detail/framework/process/ProcessSequence.inl
index 7759270ee0b0cfe0fc416d2c5923b3b9dc1498e3..b93331d05079962f5e710ac8de952935eb289517 100644
--- a/corsika/detail/framework/process/ProcessSequence.inl
+++ b/corsika/detail/framework/process/ProcessSequence.inl
@@ -12,6 +12,8 @@
 #include <corsika/framework/process/BaseProcess.hpp>
 #include <corsika/framework/process/BoundaryCrossingProcess.hpp>
 #include <corsika/framework/process/ContinuousProcess.hpp>
+#include <corsika/framework/process/ContinuousProcessStepLength.hpp>
+#include <corsika/framework/process/ContinuousProcessIndex.hpp>
 #include <corsika/framework/process/DecayProcess.hpp>
 #include <corsika/framework/process/InteractionProcess.hpp>
 #include <corsika/framework/process/ProcessReturn.hpp>
@@ -24,11 +26,14 @@
 
 namespace corsika {
 
-  template <typename TProcess1, typename TProcess2>
+  template <typename TProcess1, typename TProcess2, int IndexStart, int IndexProcess1,
+            int IndexProcess2>
   template <typename TParticle>
-  ProcessReturn ProcessSequence<TProcess1, TProcess2>::doBoundaryCrossing(
-      TParticle& particle, typename TParticle::node_type const& from,
-      typename TParticle::node_type const& to) {
+  inline ProcessReturn ProcessSequence<
+      TProcess1, TProcess2, IndexStart, IndexProcess1,
+      IndexProcess2>::doBoundaryCrossing(TParticle& particle,
+                                         typename TParticle::node_type const& from,
+                                         typename TParticle::node_type const& to) {
     ProcessReturn ret = ProcessReturn::Ok;
 
     if constexpr (std::is_base_of_v<BoundaryCrossingProcess<process1_type>,
@@ -46,25 +51,36 @@ namespace corsika {
     return ret;
   }
 
-  template <typename TProcess1, typename TProcess2>
+  template <typename TProcess1, typename TProcess2, int IndexStart, int IndexProcess1,
+            int IndexProcess2>
   template <typename TParticle, typename TTrack>
-  ProcessReturn ProcessSequence<TProcess1, TProcess2>::doContinuous(TParticle& particle,
-                                                                    TTrack& vT) {
+  inline ProcessReturn
+  ProcessSequence<TProcess1, TProcess2, IndexStart, IndexProcess1, IndexProcess2>::
+      doContinuous(TParticle& particle, TTrack& vT,
+                   [[maybe_unused]] ContinuousProcessIndex const limitId) {
     ProcessReturn ret = ProcessReturn::Ok;
-    if constexpr (std::is_base_of_v<ContinuousProcess<process1_type>, process1_type> ||
-                  t1ProcSeq) {
-      ret |= A_.doContinuous(particle, vT);
+    if constexpr (t1ProcSeq) {
+      ret |= A_.doContinuous(particle, vT, limitId);
+    } else if constexpr (is_continuous_process_v<process1_type>) {
+      ret |=
+          A_.doContinuous(particle, vT, limitId == ContinuousProcessIndex(IndexProcess1));
     }
-    if constexpr (std::is_base_of_v<ContinuousProcess<process2_type>, process2_type> ||
-                  t2ProcSeq) {
-      if (!isAbsorbed(ret)) { ret |= B_.doContinuous(particle, vT); }
+
+    if constexpr (t2ProcSeq) {
+      ret |= B_.doContinuous(particle, vT, limitId);
+    } else if constexpr (is_continuous_process_v<process2_type>) {
+      ret |=
+          B_.doContinuous(particle, vT, limitId == ContinuousProcessIndex(IndexProcess2));
     }
+
     return ret;
   }
 
-  template <typename TProcess1, typename TProcess2>
+  template <typename TProcess1, typename TProcess2, int IndexStart, int IndexProcess1,
+            int IndexProcess2>
   template <typename TSecondaries>
-  void ProcessSequence<TProcess1, TProcess2>::doSecondaries(TSecondaries& vS) {
+  inline void ProcessSequence<TProcess1, TProcess2, IndexStart, IndexProcess1,
+                              IndexProcess2>::doSecondaries(TSecondaries& vS) {
     if constexpr (std::is_base_of_v<SecondariesProcess<process1_type>, process1_type> ||
                   t1ProcSeq) {
       A_.doSecondaries(vS);
@@ -75,8 +91,10 @@ namespace corsika {
     }
   }
 
-  template <typename TProcess1, typename TProcess2>
-  bool ProcessSequence<TProcess1, TProcess2>::checkStep() {
+  template <typename TProcess1, typename TProcess2, int IndexStart, int IndexProcess1,
+            int IndexProcess2>
+  inline bool ProcessSequence<TProcess1, TProcess2, IndexStart, IndexProcess1,
+                              IndexProcess2>::checkStep() {
     bool ret = false;
     if constexpr (std::is_base_of_v<StackProcess<process1_type>, process1_type> ||
                   (t1ProcSeq && !t1SwitchProcSeq)) {
@@ -89,9 +107,11 @@ namespace corsika {
     return ret;
   }
 
-  template <typename TProcess1, typename TProcess2>
+  template <typename TProcess1, typename TProcess2, int IndexStart, int IndexProcess1,
+            int IndexProcess2>
   template <typename TStack>
-  void ProcessSequence<TProcess1, TProcess2>::doStack(TStack& stack) {
+  inline void ProcessSequence<TProcess1, TProcess2, IndexStart, IndexProcess1,
+                              IndexProcess2>::doStack(TStack& stack) {
     if constexpr (std::is_base_of_v<StackProcess<process1_type>, process1_type> ||
                   (t1ProcSeq && !t1SwitchProcSeq)) {
       if (A_.checkStep()) { A_.doStack(stack); }
@@ -102,30 +122,42 @@ namespace corsika {
     }
   }
 
-  template <typename TProcess1, typename TProcess2>
+  template <typename TProcess1, typename TProcess2, int IndexStart, int IndexProcess1,
+            int IndexProcess2>
   template <typename TParticle, typename TTrack>
-  LengthType ProcessSequence<TProcess1, TProcess2>::getMaxStepLength(TParticle& particle,
-                                                                     TTrack& vTrack) {
-    LengthType max_length = // if no other process in the sequence implements it
-        std::numeric_limits<double>::infinity() * meter;
+  inline ContinuousProcessStepLength
+  ProcessSequence<TProcess1, TProcess2, IndexStart, IndexProcess1,
+                  IndexProcess2>::getMaxStepLength(TParticle& particle, TTrack& vTrack) {
+    // if no other process in the sequence implements it
+    ContinuousProcessStepLength max_length(std::numeric_limits<double>::infinity() *
+                                           meter);
 
-    if constexpr (std::is_base_of_v<ContinuousProcess<process1_type>, process1_type> ||
-                  t1ProcSeq) {
-      LengthType const len = A_.getMaxStepLength(particle, vTrack);
-      max_length = std::min(max_length, len);
+    if constexpr (t1ProcSeq) {
+      ContinuousProcessStepLength const step = A_.getMaxStepLength(particle, vTrack);
+      max_length = std::min(max_length, step);
+    } else if constexpr (is_continuous_process_v<process1_type>) {
+      ContinuousProcessStepLength const step(A_.getMaxStepLength(particle, vTrack),
+                                             ContinuousProcessIndex(IndexProcess1));
+      max_length = std::min(max_length, step);
     }
-    if constexpr (std::is_base_of_v<ContinuousProcess<process2_type>, process2_type> ||
-                  t2ProcSeq) {
-      LengthType const len = B_.getMaxStepLength(particle, vTrack);
-      max_length = std::min(max_length, len);
+    //      return ContinuousProcessStepLength(std::min(max_length, len), IndexProcess1);
+    if constexpr (t2ProcSeq) {
+      ContinuousProcessStepLength const step = B_.getMaxStepLength(particle, vTrack);
+      max_length = std::min(max_length, step);
+    } else if constexpr (is_continuous_process_v<process2_type>) {
+      ContinuousProcessStepLength const step(B_.getMaxStepLength(particle, vTrack),
+                                             ContinuousProcessIndex(IndexProcess2));
+      max_length = std::min(max_length, step);
     }
     return max_length;
   }
 
-  template <typename TProcess1, typename TProcess2>
+  template <typename TProcess1, typename TProcess2, int IndexStart, int IndexProcess1,
+            int IndexProcess2>
   template <typename TParticle>
-  InverseGrammageType ProcessSequence<TProcess1, TProcess2>::getInverseInteractionLength(
-      TParticle&& particle) {
+  inline InverseGrammageType
+  ProcessSequence<TProcess1, TProcess2, IndexStart, IndexProcess1,
+                  IndexProcess2>::getInverseInteractionLength(TParticle&& particle) {
 
     InverseGrammageType tot = 0 * meter * meter / gram; // default value
 
@@ -140,11 +172,14 @@ namespace corsika {
     return tot;
   }
 
-  template <typename TProcess1, typename TProcess2>
+  template <typename TProcess1, typename TProcess2, int IndexStart, int IndexProcess1,
+            int IndexProcess2>
   template <typename TSecondaryView>
-  inline ProcessReturn ProcessSequence<TProcess1, TProcess2>::selectInteraction(
-      TSecondaryView& view, [[maybe_unused]] InverseGrammageType lambda_inv_select,
-      [[maybe_unused]] InverseGrammageType lambda_inv_sum) {
+  inline ProcessReturn
+  ProcessSequence<TProcess1, TProcess2, IndexStart, IndexProcess1, IndexProcess2>::
+      selectInteraction(TSecondaryView& view,
+                        [[maybe_unused]] InverseGrammageType lambda_inv_select,
+                        [[maybe_unused]] InverseGrammageType lambda_inv_sum) {
 
     // TODO: add check for lambda_inv_select>lambda_inv_tot
 
@@ -182,10 +217,12 @@ namespace corsika {
     return ProcessReturn::Ok;
   }
 
-  template <typename TProcess1, typename TProcess2>
+  template <typename TProcess1, typename TProcess2, int IndexStart, int IndexProcess1,
+            int IndexProcess2>
   template <typename TParticle>
-  inline InverseTimeType ProcessSequence<TProcess1, TProcess2>::getInverseLifetime(
-      TParticle&& particle) {
+  inline InverseTimeType
+  ProcessSequence<TProcess1, TProcess2, IndexStart, IndexProcess1,
+                  IndexProcess2>::getInverseLifetime(TParticle&& particle) {
 
     InverseTimeType tot = 0 / second; // default value
 
@@ -200,12 +237,15 @@ namespace corsika {
     return tot;
   }
 
-  template <typename TProcess1, typename TProcess2>
+  template <typename TProcess1, typename TProcess2, int IndexStart, int IndexProcess1,
+            int IndexProcess2>
   // select decay process
   template <typename TSecondaryView>
-  inline ProcessReturn ProcessSequence<TProcess1, TProcess2>::selectDecay(
-      TSecondaryView& view, [[maybe_unused]] InverseTimeType decay_inv_select,
-      [[maybe_unused]] InverseTimeType decay_inv_sum) {
+  inline ProcessReturn ProcessSequence<
+      TProcess1, TProcess2, IndexStart, IndexProcess1,
+      IndexProcess2>::selectDecay(TSecondaryView& view,
+                                  [[maybe_unused]] InverseTimeType decay_inv_select,
+                                  [[maybe_unused]] InverseTimeType decay_inv_sum) {
 
     // TODO: add check for decay_inv_select>decay_inv_tot
 
diff --git a/corsika/detail/framework/process/SwitchProcessSequence.inl b/corsika/detail/framework/process/SwitchProcessSequence.inl
index 62d33995afefe53aa3865df31969413d18a63a99..c4f8ede12f859e7c063f91cabbcc01af8b99cbc4 100644
--- a/corsika/detail/framework/process/SwitchProcessSequence.inl
+++ b/corsika/detail/framework/process/SwitchProcessSequence.inl
@@ -12,6 +12,8 @@
 #include <corsika/framework/process/ProcessTraits.hpp>
 #include <corsika/framework/process/BoundaryCrossingProcess.hpp>
 #include <corsika/framework/process/ContinuousProcess.hpp>
+#include <corsika/framework/process/ContinuousProcessStepLength.hpp>
+#include <corsika/framework/process/ContinuousProcessIndex.hpp>
 #include <corsika/framework/process/DecayProcess.hpp>
 #include <corsika/framework/process/InteractionProcess.hpp>
 #include <corsika/framework/process/ProcessReturn.hpp>
@@ -25,10 +27,14 @@
 
 namespace corsika {
 
-  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TProcess1, typename TProcess2, typename TSelect, int IndexStart,
+            int IndexProcess1, int IndexProcess2>
   template <typename TParticle, typename TVTNType>
-  ProcessReturn SwitchProcessSequence<TProcess1, TProcess2, TSelect>::doBoundaryCrossing(
-      TParticle& particle, TVTNType const& from, TVTNType const& to) {
+  inline ProcessReturn
+  SwitchProcessSequence<TProcess1, TProcess2, TSelect, IndexStart, IndexProcess1,
+                        IndexProcess2>::doBoundaryCrossing(TParticle& particle,
+                                                           TVTNType const& from,
+                                                           TVTNType const& to) {
     switch (select_(particle)) {
       case SwitchResult::First: {
         if constexpr (std::is_base_of_v<BoundaryCrossingProcess<process1_type>,
@@ -50,24 +56,27 @@ namespace corsika {
     return ProcessReturn::Ok;
   }
 
-  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TProcess1, typename TProcess2, typename TSelect, int IndexStart,
+            int IndexProcess1, int IndexProcess2>
   template <typename TParticle, typename TTrack>
-  inline ProcessReturn SwitchProcessSequence<TProcess1, TProcess2, TSelect>::doContinuous(
-      TParticle& particle, TTrack& vT) {
+  inline ProcessReturn SwitchProcessSequence<
+      TProcess1, TProcess2, TSelect, IndexStart, IndexProcess1,
+      IndexProcess2>::doContinuous(TParticle& particle, TTrack& vT,
+                                   ContinuousProcessIndex const idLimit) {
     switch (select_(particle)) {
       case SwitchResult::First: {
-        if constexpr (std::is_base_of_v<ContinuousProcess<process1_type>,
-                                        process1_type> ||
-                      t1ProcSeq) {
-          return A_.doContinuous(particle, vT);
+        if constexpr (t1ProcSeq) { return A_.doContinuous(particle, vT, idLimit); }
+        if constexpr (is_continuous_process_v<process1_type>) {
+          return A_.doContinuous(particle, vT,
+                                 idLimit == ContinuousProcessIndex(IndexProcess1));
         }
         break;
       }
       case SwitchResult::Second: {
-        if constexpr (std::is_base_of_v<ContinuousProcess<process2_type>,
-                                        process2_type> ||
-                      t2ProcSeq) {
-          return B_.doContinuous(particle, vT);
+        if constexpr (t2ProcSeq) { return B_.doContinuous(particle, vT, idLimit); }
+        if constexpr (is_continuous_process_v<process2_type>) {
+          return B_.doContinuous(particle, vT,
+                                 idLimit == ContinuousProcessIndex(IndexProcess2));
         }
         break;
       }
@@ -75,10 +84,12 @@ namespace corsika {
     return ProcessReturn::Ok;
   }
 
-  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TProcess1, typename TProcess2, typename TSelect, int IndexStart,
+            int IndexProcess1, int IndexProcess2>
   template <typename TSecondaries>
-  inline void SwitchProcessSequence<TProcess1, TProcess2, TSelect>::doSecondaries(
-      TSecondaries& vS) {
+  inline void
+  SwitchProcessSequence<TProcess1, TProcess2, TSelect, IndexStart, IndexProcess1,
+                        IndexProcess2>::doSecondaries(TSecondaries& vS) {
     const auto& particle = vS.parent();
     switch (select_(particle)) {
       case SwitchResult::First: {
@@ -100,39 +111,42 @@ namespace corsika {
     }
   }
 
-  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TProcess1, typename TProcess2, typename TSelect, int IndexStart,
+            int IndexProcess1, int IndexProcess2>
   template <typename TParticle, typename TTrack>
-  inline LengthType SwitchProcessSequence<TProcess1, TProcess2,
-                                          TSelect>::getMaxStepLength(TParticle& particle,
-                                                                     TTrack& vTrack) {
+  inline ContinuousProcessStepLength
+  SwitchProcessSequence<TProcess1, TProcess2, TSelect, IndexStart, IndexProcess1,
+                        IndexProcess2>::getMaxStepLength(TParticle& particle,
+                                                         TTrack& vTrack) {
     switch (select_(particle)) {
       case SwitchResult::First: {
-        if constexpr (std::is_base_of_v<ContinuousProcess<process1_type>,
-                                        process1_type> ||
-                      t1ProcSeq) {
-          return A_.getMaxStepLength(particle, vTrack);
+        if constexpr (t1ProcSeq) { return A_.getMaxStepLength(particle, vTrack); }
+        if constexpr (is_continuous_process_v<process1_type>) {
+          return ContinuousProcessStepLength(A_.getMaxStepLength(particle, vTrack),
+                                             ContinuousProcessIndex(IndexProcess1));
         }
         break;
       }
       case SwitchResult::Second: {
-        if constexpr (std::is_base_of_v<ContinuousProcess<process2_type>,
-                                        process2_type> ||
-                      t2ProcSeq) {
-          return B_.getMaxStepLength(particle, vTrack);
+        if constexpr (t2ProcSeq) { return B_.getMaxStepLength(particle, vTrack); }
+        if constexpr (is_continuous_process_v<process2_type>) {
+          return ContinuousProcessStepLength(B_.getMaxStepLength(particle, vTrack),
+                                             ContinuousProcessIndex(IndexProcess2));
         }
         break;
       }
     }
 
     // if no other process in the sequence implements it
-    return std::numeric_limits<double>::infinity() * meter;
+    return ContinuousProcessStepLength(std::numeric_limits<double>::infinity() * meter);
   }
 
-  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TProcess1, typename TProcess2, typename TSelect, int IndexStart,
+            int IndexProcess1, int IndexProcess2>
   template <typename TParticle>
-  inline InverseGrammageType
-  SwitchProcessSequence<TProcess1, TProcess2, TSelect>::getInverseInteractionLength(
-      TParticle&& particle) {
+  inline InverseGrammageType SwitchProcessSequence<
+      TProcess1, TProcess2, TSelect, IndexStart, IndexProcess1,
+      IndexProcess2>::getInverseInteractionLength(TParticle&& particle) {
 
     switch (select_(particle)) {
       case SwitchResult::First: {
@@ -155,12 +169,14 @@ namespace corsika {
     return 0 * meter * meter / gram; // default value
   }
 
-  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TProcess1, typename TProcess2, typename TSelect, int IndexStart,
+            int IndexProcess1, int IndexProcess2>
   template <typename TSecondaryView>
-  inline ProcessReturn
-  SwitchProcessSequence<TProcess1, TProcess2, TSelect>::selectInteraction(
-      TSecondaryView& view, [[maybe_unused]] InverseGrammageType lambda_inv_select,
-      [[maybe_unused]] InverseGrammageType lambda_inv_sum) {
+  inline ProcessReturn SwitchProcessSequence<TProcess1, TProcess2, TSelect, IndexStart,
+                                             IndexProcess1, IndexProcess2>::
+      selectInteraction(TSecondaryView& view,
+                        [[maybe_unused]] InverseGrammageType lambda_inv_select,
+                        [[maybe_unused]] InverseGrammageType lambda_inv_sum) {
     switch (select_(view.parent())) {
       case SwitchResult::First: {
         if constexpr (t1ProcSeq) {
@@ -203,11 +219,12 @@ namespace corsika {
     return ProcessReturn::Ok;
   }
 
-  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TProcess1, typename TProcess2, typename TSelect, int IndexStart,
+            int IndexProcess1, int IndexProcess2>
   template <typename TParticle>
   inline InverseTimeType
-  SwitchProcessSequence<TProcess1, TProcess2, TSelect>::getInverseLifetime(
-      TParticle&& particle) {
+  SwitchProcessSequence<TProcess1, TProcess2, TSelect, IndexStart, IndexProcess1,
+                        IndexProcess2>::getInverseLifetime(TParticle&& particle) {
 
     switch (select_(particle)) {
       case SwitchResult::First: {
@@ -229,12 +246,15 @@ namespace corsika {
     return 0 / second; // default value
   }
 
-  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TProcess1, typename TProcess2, typename TSelect, int IndexStart,
+            int IndexProcess1, int IndexProcess2>
   // select decay process
   template <typename TSecondaryView>
-  inline ProcessReturn SwitchProcessSequence<TProcess1, TProcess2, TSelect>::selectDecay(
-      TSecondaryView& view, [[maybe_unused]] InverseTimeType decay_inv_select,
-      [[maybe_unused]] InverseTimeType decay_inv_sum) {
+  inline ProcessReturn SwitchProcessSequence<
+      TProcess1, TProcess2, TSelect, IndexStart, IndexProcess1,
+      IndexProcess2>::selectDecay(TSecondaryView& view,
+                                  [[maybe_unused]] InverseTimeType decay_inv_select,
+                                  [[maybe_unused]] InverseTimeType decay_inv_sum) {
     switch (select_(view.parent())) {
       case SwitchResult::First: {
         if constexpr (t1ProcSeq) {
@@ -277,11 +297,23 @@ namespace corsika {
     return ProcessReturn::Ok;
   }
 
+  /*
+  /// traits marker to identify objectas ProcessSequence
+  template <typename TProcess1, typename TProcess2, typename TSelect>
+  struct is_process_sequence<ProcessSequence<typename std::decay_t<TProcess1>,
+                                                   typename std::decay_t<TProcess2>,
+                                                   typename std::decay_t<TSelect>>>
+      : std::true_type {};
+  */
   /// traits marker to identify objectas ProcessSequence
   template <typename TProcess1, typename TProcess2, typename TSelect>
   struct is_process_sequence<SwitchProcessSequence<TProcess1, TProcess2, TSelect>>
       : std::true_type {};
 
+  template <typename TProcess1, typename TProcess2, typename TSelect>
+  struct is_process_sequence<SwitchProcessSequence<TProcess1, TProcess2, TSelect>&>
+      : std::true_type {};
+
   /// traits marker to identify objectas SwitchProcessSequence
   template <typename TProcess1, typename TProcess2, typename TSelect>
   struct is_switch_process_sequence<SwitchProcessSequence<TProcess1, TProcess2, TSelect>>
diff --git a/corsika/detail/framework/stack/CombinedStack.inl b/corsika/detail/framework/stack/CombinedStack.inl
index 0c95ad15e8a8c57eabf1615e07b60c4bb54f3393..1cdc7abd9e23a5139939b78bffa6b9ce1c7ed7e4 100644
--- a/corsika/detail/framework/stack/CombinedStack.inl
+++ b/corsika/detail/framework/stack/CombinedStack.inl
@@ -59,7 +59,6 @@ namespace corsika {
     pi_b_type::setParticleData(static_cast<pi_b_type&>(p), vB);
   }
 
-  ///@}
   template <template <typename> class TParticleInterfaceA,
             template <typename> class TParticleInterfaceB, typename TStackIterator>
   inline std::string CombinedParticleInterface<TParticleInterfaceA, TParticleInterfaceB,
diff --git a/corsika/detail/framework/stack/SecondaryView.inl b/corsika/detail/framework/stack/SecondaryView.inl
index 33160aa08b9b56f09d0929a3abc75a156c818309..4b27a50871893a6763ccae1f268fd6a1968ac882 100644
--- a/corsika/detail/framework/stack/SecondaryView.inl
+++ b/corsika/detail/framework/stack/SecondaryView.inl
@@ -15,17 +15,11 @@
 #include <vector>
 
 namespace corsika {
-  /*
-  template <typename TStackDataType, template <typename> typename TParticleInterface,
-            template <typename T1, template <class> class T2> class MSecondaryProducer>
-  SecondaryView< TStackDataType,TParticleInterface, MSecondaryProducer>::
-
-  */
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
   template <typename... Args>
-  typename SecondaryView<TStackDataType, TParticleInterface,
-                         MSecondaryProducer>::stack_view_iterator
+  inline typename SecondaryView<TStackDataType, TParticleInterface,
+                                MSecondaryProducer>::stack_view_iterator
   SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::addSecondary(
       const Args... v) {
     CORSIKA_LOG_TRACE("SecondaryView::addSecondary(Args&&)");
@@ -35,8 +29,8 @@ namespace corsika {
 
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
-  typename SecondaryView<TStackDataType, TParticleInterface,
-                         MSecondaryProducer>::stack_view_iterator
+  inline typename SecondaryView<TStackDataType, TParticleInterface,
+                                MSecondaryProducer>::stack_view_iterator
   SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::begin() {
     unsigned int i = 0;
     for (; i < getSize(); ++i) {
@@ -48,8 +42,8 @@ namespace corsika {
 
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
-  typename SecondaryView<TStackDataType, TParticleInterface,
-                         MSecondaryProducer>::const_stack_view_iterator
+  inline typename SecondaryView<TStackDataType, TParticleInterface,
+                                MSecondaryProducer>::const_stack_view_iterator
   SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::begin() const {
     unsigned int i = 0;
     for (; i < getSize(); ++i) {
@@ -61,8 +55,8 @@ namespace corsika {
 
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
-  typename SecondaryView<TStackDataType, TParticleInterface,
-                         MSecondaryProducer>::const_stack_view_iterator
+  inline typename SecondaryView<TStackDataType, TParticleInterface,
+                                MSecondaryProducer>::const_stack_view_iterator
   SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::cbegin() const {
     unsigned int i = 0;
     for (; i < getSize(); ++i) {
@@ -74,8 +68,8 @@ namespace corsika {
 
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
-  typename SecondaryView<TStackDataType, TParticleInterface,
-                         MSecondaryProducer>::stack_view_iterator
+  inline typename SecondaryView<TStackDataType, TParticleInterface,
+                                MSecondaryProducer>::stack_view_iterator
   SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::last() {
     unsigned int i = 0;
     for (; i < getSize(); ++i) {
@@ -86,8 +80,8 @@ namespace corsika {
 
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
-  typename SecondaryView<TStackDataType, TParticleInterface,
-                         MSecondaryProducer>::const_stack_view_iterator
+  inline typename SecondaryView<TStackDataType, TParticleInterface,
+                                MSecondaryProducer>::const_stack_view_iterator
   SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::last() const {
     unsigned int i = 0;
     for (; i < getSize(); ++i) {
@@ -98,8 +92,8 @@ namespace corsika {
 
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
-  typename SecondaryView<TStackDataType, TParticleInterface,
-                         MSecondaryProducer>::const_stack_view_iterator
+  inline typename SecondaryView<TStackDataType, TParticleInterface,
+                                MSecondaryProducer>::const_stack_view_iterator
   SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::clast() const {
     unsigned int i = 0;
     for (; i < getSize(); ++i) {
@@ -111,7 +105,7 @@ namespace corsika {
 
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
-  void SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::swap(
+  inline void SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::swap(
       stack_view_iterator a, stack_view_iterator b) {
 
     CORSIKA_LOG_TRACE("View::swap");
@@ -121,7 +115,7 @@ namespace corsika {
 
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
-  void SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::copy(
+  inline void SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::copy(
       stack_view_iterator a, stack_view_iterator b) {
 
     CORSIKA_LOG_TRACE("View::copy");
@@ -131,7 +125,7 @@ namespace corsika {
 
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
-  void SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::copy(
+  inline void SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::copy(
       const_stack_view_iterator a, stack_view_iterator b) {
 
     CORSIKA_LOG_TRACE("View::copy");
@@ -141,8 +135,8 @@ namespace corsika {
 
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
-  void SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::erase(
-      stack_view_iterator p) {
+  inline void SecondaryView<TStackDataType, TParticleInterface,
+                            MSecondaryProducer>::erase(stack_view_iterator p) {
 
     CORSIKA_LOG_TRACE("SecondaryView::Delete");
     if (isEmpty()) { /*error*/
@@ -157,8 +151,8 @@ namespace corsika {
 
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
-  bool SecondaryView<TStackDataType, TParticleInterface,
-                     MSecondaryProducer>::purgeLastIfDeleted() {
+  inline bool SecondaryView<TStackDataType, TParticleInterface,
+                            MSecondaryProducer>::purgeLastIfDeleted() {
     CORSIKA_LOG_TRACE("SecondaryView::purgeLastIfDeleted");
     if (!isErased(getSize() - 1))
       return false; // the last particle is not marked for deletion. Do nothing.
@@ -170,7 +164,8 @@ namespace corsika {
 
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
-  void SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::purge() {
+  inline void
+  SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::purge() {
     unsigned int iStack = 0;
     unsigned int size = getSize();
     while (iStack < size) {
@@ -187,8 +182,8 @@ namespace corsika {
 
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
-  std::string SecondaryView<TStackDataType, TParticleInterface,
-                            MSecondaryProducer>::asString() const {
+  inline std::string SecondaryView<TStackDataType, TParticleInterface,
+                                   MSecondaryProducer>::asString() const {
     std::string str(fmt::format("size {}\n", getSize()));
     // we make our own begin/end since we want ALL entries
     std::string new_line = "     ";
@@ -206,8 +201,8 @@ namespace corsika {
   template <typename TStackDataType, template <typename> typename TParticleInterface,
             template <typename T1, template <class> class T2> class MSecondaryProducer>
   template <typename... Args>
-  typename SecondaryView<TStackDataType, TParticleInterface,
-                         MSecondaryProducer>::stack_view_iterator
+  inline typename SecondaryView<TStackDataType, TParticleInterface,
+                                MSecondaryProducer>::stack_view_iterator
   SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::addSecondary(
       stack_view_iterator& proj, const Args... v) {
     CORSIKA_LOG_TRACE("SecondaryView::addSecondary(stack_view_iterator&, Args&&)");
diff --git a/corsika/detail/framework/stack/Stack.inl b/corsika/detail/framework/stack/Stack.inl
index 30c7c746095e89323e01bbfdae6b8735b99806a4..4569a23a8ab49c16af4aa6a29a78759bcfc807a1 100644
--- a/corsika/detail/framework/stack/Stack.inl
+++ b/corsika/detail/framework/stack/Stack.inl
@@ -21,15 +21,15 @@ namespace corsika {
 
   template <typename StackData, template <typename> typename MParticleInterface>
   template <typename... TArgs>
-  void Stack<StackData, MParticleInterface>::clear(TArgs... args) {
+  inline void Stack<StackData, MParticleInterface>::clear(TArgs... args) {
     data_.clear(args...);
     deleted_ = std::vector<bool>(data_.getSize(), false);
     nDeleted_ = 0;
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::stack_iterator_type
-  Stack<StackData, MParticleInterface>::begin() {
+  typename Stack<StackData, MParticleInterface>::stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::begin() {
     unsigned int i = 0;
     for (; i < getSize(); ++i) {
       if (!deleted_[i]) break;
@@ -38,14 +38,14 @@ namespace corsika {
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::stack_iterator_type
-  Stack<StackData, MParticleInterface>::end() {
+  typename Stack<StackData, MParticleInterface>::stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::end() {
     return stack_iterator_type(*this, getSize());
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::stack_iterator_type
-  Stack<StackData, MParticleInterface>::last() {
+  typename Stack<StackData, MParticleInterface>::stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::last() {
     unsigned int i = 0;
     for (; i < getSize(); ++i) {
       if (!deleted_[getSize() - 1 - i]) break;
@@ -54,8 +54,8 @@ namespace corsika {
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
-  Stack<StackData, MParticleInterface>::begin() const {
+  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::begin() const {
     unsigned int i = 0;
     for (; i < getSize(); ++i) {
       if (!deleted_[i]) break;
@@ -64,8 +64,8 @@ namespace corsika {
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
-  Stack<StackData, MParticleInterface>::end() const {
+  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::end() const {
     return const_stack_iterator_type(*this, getSize());
   }
 
@@ -80,8 +80,8 @@ namespace corsika {
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
-  Stack<StackData, MParticleInterface>::cbegin() const {
+  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::cbegin() const {
     unsigned int i = 0;
     for (; i < getSize(); ++i) {
       if (!deleted_[i]) break;
@@ -90,14 +90,14 @@ namespace corsika {
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
-  Stack<StackData, MParticleInterface>::cend() const {
+  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::cend() const {
     return const_stack_iterator_type(*this, getSize());
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
-  Stack<StackData, MParticleInterface>::clast() const {
+  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::clast() const {
     unsigned int i = 0;
     for (; i < getSize(); ++i) {
       if (!deleted_[getSize() - 1 - i]) break;
@@ -107,41 +107,40 @@ namespace corsika {
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::stack_iterator_type
-  Stack<StackData, MParticleInterface>::at(unsigned int i) {
+  typename Stack<StackData, MParticleInterface>::stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::at(unsigned int i) {
     return stack_iterator_type(*this, i);
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
-  Stack<StackData, MParticleInterface>::at(unsigned int i) const {
+  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::at(unsigned int i) const {
     return const_stack_iterator_type(*this, i);
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::stack_iterator_type
-  Stack<StackData, MParticleInterface>::first() {
+  typename Stack<StackData, MParticleInterface>::stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::first() {
     return stack_iterator_type{*this, 0};
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
-  Stack<StackData, MParticleInterface>::cfirst() const {
+  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::cfirst() const {
     return const_stack_iterator_type{*this, 0};
   }
-  /// @}
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::stack_iterator_type
-  Stack<StackData, MParticleInterface>::getNextParticle() {
+  typename Stack<StackData, MParticleInterface>::stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::getNextParticle() {
     while (purgeLastIfDeleted()) {}
     return last();
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
   template <typename... TArgs>
-  typename Stack<StackData, MParticleInterface>::stack_iterator_type
-  Stack<StackData, MParticleInterface>::addParticle(const TArgs... v) {
+  typename Stack<StackData, MParticleInterface>::stack_iterator_type inline Stack<
+      StackData, MParticleInterface>::addParticle(const TArgs... v) {
     CORSIKA_LOG_TRACE("Stack::AddParticle");
     data_.incrementSize();
     deleted_.push_back(false);
@@ -149,22 +148,22 @@ namespace corsika {
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  void Stack<StackData, MParticleInterface>::swap(stack_iterator_type a,
-                                                  stack_iterator_type b) {
+  inline void Stack<StackData, MParticleInterface>::swap(stack_iterator_type a,
+                                                         stack_iterator_type b) {
     CORSIKA_LOG_TRACE("Stack::Swap");
     swap(a.getIndex(), b.getIndex());
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  void Stack<StackData, MParticleInterface>::copy(stack_iterator_type a,
-                                                  stack_iterator_type b) {
+  inline void Stack<StackData, MParticleInterface>::copy(stack_iterator_type a,
+                                                         stack_iterator_type b) {
     CORSIKA_LOG_TRACE("Stack::Copy");
     copy(a.getIndex(), b.getIndex());
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  void Stack<StackData, MParticleInterface>::copy(const_stack_iterator_type a,
-                                                  stack_iterator_type b) {
+  inline void Stack<StackData, MParticleInterface>::copy(const_stack_iterator_type a,
+                                                         stack_iterator_type b) {
     CORSIKA_LOG_TRACE("Stack::Copy");
     data_.copy(a.getIndex(), b.getIndex());
     if (deleted_[b.getIndex()] && !deleted_[a.getIndex()]) nDeleted_--;
@@ -173,7 +172,7 @@ namespace corsika {
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  void Stack<StackData, MParticleInterface>::erase(stack_iterator_type p) {
+  inline void Stack<StackData, MParticleInterface>::erase(stack_iterator_type p) {
     CORSIKA_LOG_TRACE("Stack::Delete");
     if (this->isEmpty()) { /*error*/
       throw std::runtime_error("Stack, cannot delete entry since size is zero");
@@ -183,54 +182,52 @@ namespace corsika {
     }
     this->erase(p.getIndex());
   }
-  /**
+
+  /*
    * delete this particle
    */
-
   template <typename StackData, template <typename> typename MParticleInterface>
-  void Stack<StackData, MParticleInterface>::erase(particle_interface_type p) {
+  inline void Stack<StackData, MParticleInterface>::erase(particle_interface_type p) {
     this->erase(p.getIterator());
   }
 
-  /**
+  /*
    * check if there are no further non-deleted particles on stack
    */
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  bool Stack<StackData, MParticleInterface>::isEmpty() {
+  inline bool Stack<StackData, MParticleInterface>::isEmpty() {
     return getEntries() == 0;
   }
 
-  /**
+  /*
    * check if this particle was already deleted
    */
-
   template <typename StackData, template <typename> typename MParticleInterface>
-  bool Stack<StackData, MParticleInterface>::isErased(
+  inline bool Stack<StackData, MParticleInterface>::isErased(
       const stack_iterator_type& p) const {
     return isErased(p.getIndex());
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  bool Stack<StackData, MParticleInterface>::isErased(
+  inline bool Stack<StackData, MParticleInterface>::isErased(
       const const_stack_iterator_type& p) const {
     return isErased(p.getIndex());
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  bool Stack<StackData, MParticleInterface>::isErased(
+  inline bool Stack<StackData, MParticleInterface>::isErased(
       const particle_interface_type& p) const {
     return isErased(p.getIterator());
   }
 
-  /**
+  /*
    * Function to ultimatively remove the last entry from the stack,
    * if it was marked as deleted before. If this is not the case,
    * the function will just return false and do nothing.
    */
-
   template <typename StackData, template <typename> typename MParticleInterface>
-  bool Stack<StackData, MParticleInterface>::purgeLastIfDeleted() {
+  inline bool Stack<StackData, MParticleInterface>::purgeLastIfDeleted() {
     if (!deleted_.back())
       return false; // the last particle is not marked for deletion. Do nothing.
 
@@ -241,7 +238,7 @@ namespace corsika {
     return true;
   }
 
-  /**
+  /*
    * Function to ultimatively remove all entries from the stack
    * marked as deleted.
    *
@@ -251,7 +248,7 @@ namespace corsika {
    */
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  void Stack<StackData, MParticleInterface>::purge() {
+  inline void Stack<StackData, MParticleInterface>::purge() {
     unsigned int iStackFront = 0;
     unsigned int iStackBack = getSize() - 1;
 
@@ -269,12 +266,12 @@ namespace corsika {
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  unsigned int Stack<StackData, MParticleInterface>::getSize() const {
+  inline unsigned int Stack<StackData, MParticleInterface>::getSize() const {
     return data_.getSize();
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  std::string Stack<StackData, MParticleInterface>::asString() const {
+  inline std::string Stack<StackData, MParticleInterface>::asString() const {
     std::string str(fmt::format("size {}, entries {}, deleted {} \n", getSize(),
                                 getEntries(), getErased()));
     // we make our own begin/end since we want ALL entries
@@ -290,7 +287,7 @@ namespace corsika {
 
   template <typename StackData, template <typename> typename MParticleInterface>
   template <typename... TArgs>
-  typename Stack<StackData, MParticleInterface>::stack_iterator_type
+  inline typename Stack<StackData, MParticleInterface>::stack_iterator_type
   Stack<StackData, MParticleInterface>::addSecondary(stack_iterator_type& parent,
                                                      const TArgs... v) {
     CORSIKA_LOG_TRACE("Stack::AddSecondary");
@@ -300,16 +297,16 @@ namespace corsika {
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  void Stack<StackData, MParticleInterface>::swap(unsigned int const a,
-                                                  unsigned int const b) {
+  inline void Stack<StackData, MParticleInterface>::swap(unsigned int const a,
+                                                         unsigned int const b) {
     CORSIKA_LOG_TRACE("Stack::Swap(unsigned int)");
     data_.swap(a, b);
     std::swap(deleted_[a], deleted_[b]);
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  void Stack<StackData, MParticleInterface>::copy(unsigned int const a,
-                                                  unsigned int const b) {
+  inline void Stack<StackData, MParticleInterface>::copy(unsigned int const a,
+                                                         unsigned int const b) {
     CORSIKA_LOG_TRACE("Stack::Copy");
     data_.copy(a, b);
     if (deleted_[b] && !deleted_[a]) nDeleted_--;
@@ -318,24 +315,23 @@ namespace corsika {
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  bool Stack<StackData, MParticleInterface>::isErased(unsigned int const i) const {
+  inline bool Stack<StackData, MParticleInterface>::isErased(unsigned int const i) const {
     if (i >= deleted_.size()) return false;
     return deleted_.at(i);
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  void Stack<StackData, MParticleInterface>::erase(unsigned int const i) {
+  inline void Stack<StackData, MParticleInterface>::erase(unsigned int const i) {
     deleted_[i] = true;
     nDeleted_++;
   }
 
-  /**
+  /*
    * will remove from storage the element i. This is a helper
    * function for SecondaryView.
    */
-
   template <typename StackData, template <typename> typename MParticleInterface>
-  void Stack<StackData, MParticleInterface>::purge(unsigned int i) {
+  inline void Stack<StackData, MParticleInterface>::purge(unsigned int i) {
     unsigned int iStackBack = getSize() - 1;
     // search for last non-deleted particle on stack
     while (deleted_[iStackBack]) { iStackBack--; }
@@ -347,33 +343,21 @@ namespace corsika {
     deleted_.pop_back();
   }
 
-  /**
-   * Function to perform eventual transformation from
-   * StackIterator::getIndex() to index in data stored in
-   * StackData data_. By default (and in almost all cases) this
-   * should just be identiy. See class SecondaryView for an alternative implementation.
-   */
-
   template <typename StackData, template <typename> typename MParticleInterface>
-  unsigned int Stack<StackData, MParticleInterface>::getIndexFromIterator(
+  inline unsigned int Stack<StackData, MParticleInterface>::getIndexFromIterator(
       const unsigned int vI) const {
     // this is too much: CORSIKA_LOG_TRACE("Stack::getIndexFromIterator({})={}", vI, vI);
     return vI;
   }
 
-  /**
-   * @name Return reference to StackData object data_ for data access
-   * @{
-   */
-
   template <typename StackData, template <typename> typename MParticleInterface>
-  typename Stack<StackData, MParticleInterface>::value_type&
+  inline typename Stack<StackData, MParticleInterface>::value_type&
   Stack<StackData, MParticleInterface>::getStackData() {
     return data_;
   }
 
   template <typename StackData, template <typename> typename MParticleInterface>
-  const typename Stack<StackData, MParticleInterface>::value_type&
+  inline const typename Stack<StackData, MParticleInterface>::value_type&
   Stack<StackData, MParticleInterface>::getStackData() const {
     return data_;
   }
diff --git a/corsika/detail/framework/utility/COMBoost.inl b/corsika/detail/framework/utility/COMBoost.inl
index 54d1162c327b8ba2f8be9dea485238a0c343ce46..9b05bef0e5e5d0193d74b637416a0056b520c535 100644
--- a/corsika/detail/framework/utility/COMBoost.inl
+++ b/corsika/detail/framework/utility/COMBoost.inl
@@ -108,6 +108,6 @@ namespace corsika {
     inverseBoost_ << coshEta, -sinhEta, -sinhEta, coshEta;
   }
 
-  CoordinateSystemPtr COMBoost::getRotatedCS() const { return rotatedCS_; }
+  inline CoordinateSystemPtr COMBoost::getRotatedCS() const { return rotatedCS_; }
 
 } // namespace corsika
diff --git a/corsika/detail/framework/utility/CorsikaData.inl b/corsika/detail/framework/utility/CorsikaData.inl
index 80a1b77caa404185bc618183590d48c67a730dbb..e2ea1e99bc3742783020312f725419fbd0d63846 100644
--- a/corsika/detail/framework/utility/CorsikaData.inl
+++ b/corsika/detail/framework/utility/CorsikaData.inl
@@ -11,7 +11,7 @@
 #include <stdexcept>
 #include <string>
 
-std::string corsika::corsika_data(std::string const& key) {
+inline std::string corsika::corsika_data(std::string const& key) {
   if (auto const* p = std::getenv("CORSIKA_DATA"); p != nullptr) {
     auto const path = std::string(p) + "/" + key;
     return path;
diff --git a/corsika/detail/framework/utility/CorsikaFenvOSX.inl b/corsika/detail/framework/utility/CorsikaFenvOSX.inl
index b57a6973da4b892e8da1f2f0ce68a6a3c2486292..e1f7cc480b88bc0d29b52dc9feb04ca7eaf9fe75 100644
--- a/corsika/detail/framework/utility/CorsikaFenvOSX.inl
+++ b/corsika/detail/framework/utility/CorsikaFenvOSX.inl
@@ -28,7 +28,7 @@
 
 extern "C" {
 
-int feenableexcept(int excepts) noexcept {
+inline int feenableexcept(int excepts) noexcept {
   static fenv_t fenv;
   int new_excepts = excepts & FE_ALL_EXCEPT;
   // previous masks
@@ -44,7 +44,7 @@ int feenableexcept(int excepts) noexcept {
   return fesetenv(&fenv) ? -1 : old_excepts;
 }
 
-int fedisableexcept(int excepts) noexcept {
+inline int fedisableexcept(int excepts) noexcept {
   static fenv_t fenv;
   int new_excepts = excepts & FE_ALL_EXCEPT;
   // all previous masks
diff --git a/corsika/detail/media/BaseExponential.inl b/corsika/detail/media/BaseExponential.inl
index 35b506610419a1a7ce5574b3ab16e7192505d785..a40981bef9feb867c082e0d43ddb791352d8768b 100644
--- a/corsika/detail/media/BaseExponential.inl
+++ b/corsika/detail/media/BaseExponential.inl
@@ -15,12 +15,12 @@
 namespace corsika {
 
   template <typename TDerived>
-  auto const& BaseExponential<TDerived>::getImplementation() const {
+  inline auto const& BaseExponential<TDerived>::getImplementation() const {
     return *static_cast<TDerived const*>(this);
   }
 
   template <typename TDerived>
-  GrammageType BaseExponential<TDerived>::getIntegratedGrammage(
+  inline GrammageType BaseExponential<TDerived>::getIntegratedGrammage(
       setup::Trajectory const& traj, LengthType vL, DirectionVector const& axis) const {
     if (vL == LengthType::zero()) { return GrammageType::zero(); }
 
@@ -35,7 +35,7 @@ namespace corsika {
   }
 
   template <typename TDerived>
-  LengthType BaseExponential<TDerived>::getArclengthFromGrammage(
+  inline LengthType BaseExponential<TDerived>::getArclengthFromGrammage(
       setup::Trajectory const& traj, GrammageType grammage,
       DirectionVector const& axis) const {
     auto const uDotA = traj.getDirection(0).dot(axis).magnitude();
@@ -55,8 +55,9 @@ namespace corsika {
   }
 
   template <typename TDerived>
-  BaseExponential<TDerived>::BaseExponential(Point const& point, MassDensityType rho0,
-                                             LengthType lambda)
+  inline BaseExponential<TDerived>::BaseExponential(Point const& point,
+                                                    MassDensityType rho0,
+                                                    LengthType lambda)
       : rho0_(rho0)
       , lambda_(lambda)
       , invLambda_(1 / lambda)
diff --git a/corsika/detail/media/Environment.inl b/corsika/detail/media/Environment.inl
index 792182a9efee3d45fc07176524d6fdb017ed0dca..40ab05db95ad200fbdf98bfcd4e9585fcf8ceabc 100644
--- a/corsika/detail/media/Environment.inl
+++ b/corsika/detail/media/Environment.inl
@@ -13,33 +13,34 @@
 namespace corsika {
 
   template <typename IEnvironmentModel>
-  Environment<IEnvironmentModel>::Environment()
+  inline Environment<IEnvironmentModel>::Environment()
       : coordinateSystem_{get_root_CoordinateSystem()}
       , universe_(std::make_unique<BaseNodeType>(
             std::make_unique<Universe>(coordinateSystem_))) {}
 
   template <typename IEnvironmentModel>
-  typename Environment<IEnvironmentModel>::BaseNodeType::VTNUPtr&
+  inline typename Environment<IEnvironmentModel>::BaseNodeType::VTNUPtr&
   Environment<IEnvironmentModel>::getUniverse() {
     return universe_;
   }
 
   template <typename IEnvironmentModel>
-  typename Environment<IEnvironmentModel>::BaseNodeType::VTNUPtr const&
+  inline typename Environment<IEnvironmentModel>::BaseNodeType::VTNUPtr const&
   Environment<IEnvironmentModel>::getUniverse() const {
     return universe_;
   }
 
   template <typename IEnvironmentModel>
-  CoordinateSystemPtr const& Environment<IEnvironmentModel>::getCoordinateSystem() const {
+  inline CoordinateSystemPtr const& Environment<IEnvironmentModel>::getCoordinateSystem()
+      const {
     return coordinateSystem_;
   }
 
   // factory method for creation of VolumeTreeNodes
   template <typename IEnvironmentModel>
   template <class TVolumeType, typename... TVolumeArgs>
-  std::unique_ptr<VolumeTreeNode<IEnvironmentModel> >
-  Environment<IEnvironmentModel>::createNode(TVolumeArgs&&... args) {
+  std::unique_ptr<VolumeTreeNode<IEnvironmentModel> > inline Environment<
+      IEnvironmentModel>::createNode(TVolumeArgs&&... args) {
     static_assert(std::is_base_of_v<IVolume, TVolumeType>,
                   "unusable type provided, needs to be derived from "
                   "\"Volume\"");
@@ -48,4 +49,4 @@ namespace corsika {
         std::make_unique<TVolumeType>(std::forward<TVolumeArgs>(args)...));
   }
 
-} // namespace corsika
\ No newline at end of file
+} // namespace corsika
diff --git a/corsika/detail/media/FlatExponential.inl b/corsika/detail/media/FlatExponential.inl
index bc782945fe67cc3a6ea21d778913f948a788e62a..cdada02df557031db3c80ce91eb1de75eca1415e 100644
--- a/corsika/detail/media/FlatExponential.inl
+++ b/corsika/detail/media/FlatExponential.inl
@@ -17,16 +17,16 @@
 namespace corsika {
 
   template <typename T>
-  FlatExponential<T>::FlatExponential(Point const& point,
-                                      Vector<dimensionless_d> const& axis,
-                                      MassDensityType rho, LengthType lambda,
-                                      NuclearComposition const& nuclComp)
+  inline FlatExponential<T>::FlatExponential(Point const& point,
+                                             Vector<dimensionless_d> const& axis,
+                                             MassDensityType rho, LengthType lambda,
+                                             NuclearComposition const& nuclComp)
       : BaseExponential<FlatExponential<T>>(point, rho, lambda)
       , axis_(axis)
       , nuclComp_(nuclComp) {}
 
   template <typename T>
-  MassDensityType FlatExponential<T>::getMassDensity(Point const& point) const {
+  inline MassDensityType FlatExponential<T>::getMassDensity(Point const& point) const {
     return BaseExponential<FlatExponential<T>>::getRho0() *
            exp(BaseExponential<FlatExponential<T>>::getInvLambda() *
                (point - BaseExponential<FlatExponential<T>>::getAnchorPoint())
@@ -34,19 +34,19 @@ namespace corsika {
   }
 
   template <typename T>
-  NuclearComposition const& FlatExponential<T>::getNuclearComposition() const {
+  inline NuclearComposition const& FlatExponential<T>::getNuclearComposition() const {
     return nuclComp_;
   }
 
   template <typename T>
-  GrammageType FlatExponential<T>::getIntegratedGrammage(setup::Trajectory const& line,
-                                                         LengthType to) const {
+  inline GrammageType FlatExponential<T>::getIntegratedGrammage(
+      setup::Trajectory const& line, LengthType to) const {
     return BaseExponential<FlatExponential<T>>::getIntegratedGrammage(line, to, axis_);
   }
 
   template <typename T>
-  LengthType FlatExponential<T>::getArclengthFromGrammage(setup::Trajectory const& line,
-                                                          GrammageType grammage) const {
+  inline LengthType FlatExponential<T>::getArclengthFromGrammage(
+      setup::Trajectory const& line, GrammageType grammage) const {
     return BaseExponential<FlatExponential<T>>::getArclengthFromGrammage(line, grammage,
                                                                          axis_);
   }
diff --git a/corsika/detail/media/HomogeneousMedium.inl b/corsika/detail/media/HomogeneousMedium.inl
index d73354aa680c085241c1f71c37daed4bb1221b47..fd0148b0278dcbad64f5ae37ca4a1d9b01243ba6 100644
--- a/corsika/detail/media/HomogeneousMedium.inl
+++ b/corsika/detail/media/HomogeneousMedium.inl
@@ -16,29 +16,30 @@
 namespace corsika {
 
   template <typename T>
-  HomogeneousMedium<T>::HomogeneousMedium(MassDensityType density,
-                                          NuclearComposition const& nuclComp)
+  inline HomogeneousMedium<T>::HomogeneousMedium(MassDensityType density,
+                                                 NuclearComposition const& nuclComp)
       : density_(density)
       , nuclComp_(nuclComp) {}
 
   template <typename T>
-  MassDensityType HomogeneousMedium<T>::getMassDensity(Point const&) const {
+  inline MassDensityType HomogeneousMedium<T>::getMassDensity(Point const&) const {
     return density_;
   }
+
   template <typename T>
-  NuclearComposition const& HomogeneousMedium<T>::getNuclearComposition() const {
+  inline NuclearComposition const& HomogeneousMedium<T>::getNuclearComposition() const {
     return nuclComp_;
   }
 
   template <typename T>
-  GrammageType HomogeneousMedium<T>::getIntegratedGrammage(setup::Trajectory const&,
-                                                           LengthType to) const {
+  inline GrammageType HomogeneousMedium<T>::getIntegratedGrammage(
+      setup::Trajectory const&, LengthType to) const {
     return to * density_;
   }
 
   template <typename T>
-  LengthType HomogeneousMedium<T>::getArclengthFromGrammage(setup::Trajectory const&,
-                                                            GrammageType grammage) const {
+  inline LengthType HomogeneousMedium<T>::getArclengthFromGrammage(
+      setup::Trajectory const&, GrammageType grammage) const {
     return grammage / density_;
   }
 } // namespace corsika
diff --git a/corsika/detail/media/InhomogeneousMedium.inl b/corsika/detail/media/InhomogeneousMedium.inl
index 064e9610efe4ed04e7707cf626cae8f0a355c076..48d1d266e5f308cddb6b990678aa6b66ace2e473 100644
--- a/corsika/detail/media/InhomogeneousMedium.inl
+++ b/corsika/detail/media/InhomogeneousMedium.inl
@@ -17,31 +17,31 @@ namespace corsika {
 
   template <typename T, typename TDensityFunction>
   template <typename... TArgs>
-  InhomogeneousMedium<T, TDensityFunction>::InhomogeneousMedium(
+  inline InhomogeneousMedium<T, TDensityFunction>::InhomogeneousMedium(
       NuclearComposition const& nuclComp, TArgs&&... rhoTArgs)
       : nuclComp_(nuclComp)
       , densityFunction_(rhoTArgs...) {}
 
   template <typename T, typename TDensityFunction>
-  MassDensityType InhomogeneousMedium<T, TDensityFunction>::getMassDensity(
+  inline MassDensityType InhomogeneousMedium<T, TDensityFunction>::getMassDensity(
       Point const& point) const {
     return densityFunction_.evaluateAt(point);
   }
 
   template <typename T, typename TDensityFunction>
-  NuclearComposition const&
+  inline NuclearComposition const&
   InhomogeneousMedium<T, TDensityFunction>::getNuclearComposition() const {
     return nuclComp_;
   }
 
   template <typename T, typename TDensityFunction>
-  GrammageType InhomogeneousMedium<T, TDensityFunction>::getIntegratedGrammage(
+  inline GrammageType InhomogeneousMedium<T, TDensityFunction>::getIntegratedGrammage(
       setup::Trajectory const& line, LengthType to) const {
     return densityFunction_.getIntegrateGrammage(line, to);
   }
 
   template <typename T, typename TDensityFunction>
-  LengthType InhomogeneousMedium<T, TDensityFunction>::getArclengthFromGrammage(
+  inline LengthType InhomogeneousMedium<T, TDensityFunction>::getArclengthFromGrammage(
       setup::Trajectory const& line, GrammageType grammage) const {
     return densityFunction_.getArclengthFromGrammage(line, grammage);
   }
diff --git a/corsika/detail/media/LayeredSphericalAtmosphereBuilder.inl b/corsika/detail/media/LayeredSphericalAtmosphereBuilder.inl
index e42310da2c7b13dc51480ff753b836a181557988..74f00f101bd284fc79f06e8f0e7c02a655b197c7 100644
--- a/corsika/detail/media/LayeredSphericalAtmosphereBuilder.inl
+++ b/corsika/detail/media/LayeredSphericalAtmosphereBuilder.inl
@@ -18,8 +18,9 @@ namespace corsika {
 
   template <typename TMediumInterface, template <typename> typename TMediumModelExtra,
             typename... TModelArgs>
-  void LayeredSphericalAtmosphereBuilder<TMediumInterface, TMediumModelExtra,
-                                         TModelArgs...>::checkRadius(LengthType r) const {
+  inline void LayeredSphericalAtmosphereBuilder<TMediumInterface, TMediumModelExtra,
+                                                TModelArgs...>::checkRadius(LengthType r)
+      const {
     if (r <= previousRadius_) {
       throw std::runtime_error("radius must be greater than previous");
     }
@@ -27,7 +28,7 @@ namespace corsika {
 
   template <typename TMediumInterface, template <typename> typename TMediumModelExtra,
             typename... TModelArgs>
-  void LayeredSphericalAtmosphereBuilder<
+  inline void LayeredSphericalAtmosphereBuilder<
       TMediumInterface, TMediumModelExtra,
       TModelArgs...>::setNuclearComposition(NuclearComposition const& composition) {
     composition_ = std::make_unique<NuclearComposition>(composition);
@@ -35,7 +36,7 @@ namespace corsika {
 
   template <typename TMediumInterface, template <typename> typename TMediumModelExtra,
             typename... TModelArgs>
-  void LayeredSphericalAtmosphereBuilder<
+  inline void LayeredSphericalAtmosphereBuilder<
       TMediumInterface, TMediumModelExtra,
       TModelArgs...>::addExponentialLayer(GrammageType b, LengthType c,
                                           LengthType upperBoundary) {
@@ -70,7 +71,7 @@ namespace corsika {
 
   template <typename TMediumInterface, template <typename> typename TMediumModelExtra,
             typename... TModelArgs>
-  void LayeredSphericalAtmosphereBuilder<
+  inline void LayeredSphericalAtmosphereBuilder<
       TMediumInterface, TMediumModelExtra,
       TModelArgs...>::addLinearLayer(LengthType c, LengthType upperBoundary) {
     auto const radius = earthRadius_ + upperBoundary;
@@ -104,7 +105,7 @@ namespace corsika {
 
   template <typename TMediumInterface, template <typename> typename TMediumModelExtra,
             typename... TModelArgs>
-  Environment<TMediumInterface> LayeredSphericalAtmosphereBuilder<
+  inline Environment<TMediumInterface> LayeredSphericalAtmosphereBuilder<
       TMediumInterface, TMediumModelExtra, TModelArgs...>::assemble() {
     Environment<TMediumInterface> env;
     assemble(env);
@@ -113,7 +114,7 @@ namespace corsika {
 
   template <typename TMediumInterface, template <typename> typename TMediumModelExtra,
             typename... TModelArgs>
-  void LayeredSphericalAtmosphereBuilder<
+  inline void LayeredSphericalAtmosphereBuilder<
       TMediumInterface, TMediumModelExtra,
       TModelArgs...>::assemble(Environment<TMediumInterface>& env) {
     auto& universe = env.getUniverse();
diff --git a/corsika/detail/media/LinearApproximationIntegrator.inl b/corsika/detail/media/LinearApproximationIntegrator.inl
index 7452ff31d3047b235aaf292fedb78dd0b0552ba5..78b7733ed138f12ace9e99ed381776487094cadc 100644
--- a/corsika/detail/media/LinearApproximationIntegrator.inl
+++ b/corsika/detail/media/LinearApproximationIntegrator.inl
@@ -13,12 +13,12 @@
 namespace corsika {
 
   template <typename TDerived>
-  auto const& LinearApproximationIntegrator<TDerived>::getImplementation() const {
+  inline auto const& LinearApproximationIntegrator<TDerived>::getImplementation() const {
     return *static_cast<TDerived const*>(this);
   }
 
   template <typename TDerived>
-  auto LinearApproximationIntegrator<TDerived>::getIntegrateGrammage(
+  inline auto LinearApproximationIntegrator<TDerived>::getIntegrateGrammage(
       setup::Trajectory const& line, LengthType length) const {
     auto const c0 = getImplementation().evaluateAt(line.getPosition(0));
     auto const c1 = getImplementation().rho_.getFirstDerivative(line.getPosition(0),
@@ -27,7 +27,7 @@ namespace corsika {
   }
 
   template <typename TDerived>
-  auto LinearApproximationIntegrator<TDerived>::getArclengthFromGrammage(
+  inline auto LinearApproximationIntegrator<TDerived>::getArclengthFromGrammage(
       setup::Trajectory const& line, GrammageType grammage) const {
     auto const c0 = getImplementation().rho_(line.getPosition(0));
     auto const c1 = getImplementation().rho_.getFirstDerivative(line.getPosition(0),
@@ -37,7 +37,7 @@ namespace corsika {
   }
 
   template <typename TDerived>
-  auto LinearApproximationIntegrator<TDerived>::getMaximumLength(
+  inline auto LinearApproximationIntegrator<TDerived>::getMaximumLength(
       setup::Trajectory const& line, [[maybe_unused]] double relError) const {
     [[maybe_unused]] auto const c1 = getImplementation().rho_.getSecondDerivative(
         line.getPosition(0), line.getDirection(0));
diff --git a/corsika/detail/media/MediumPropertyModel.inl b/corsika/detail/media/MediumPropertyModel.inl
index 90cdd9754774e9a8c55a6625f2172922fec83c76..604175989c410c8ae129b1d2012631dc29c49cc1 100644
--- a/corsika/detail/media/MediumPropertyModel.inl
+++ b/corsika/detail/media/MediumPropertyModel.inl
@@ -14,17 +14,17 @@ namespace corsika {
 
   template <typename T>
   template <typename... Args>
-  MediumPropertyModel<T>::MediumPropertyModel(Medium const medium, Args&&... args)
+  inline MediumPropertyModel<T>::MediumPropertyModel(Medium const medium, Args&&... args)
       : T(std::forward<Args>(args)...)
       , medium_(medium) {}
 
   template <typename T>
-  Medium MediumPropertyModel<T>::getMedium(Point const&) const {
+  inline Medium MediumPropertyModel<T>::getMedium(Point const&) const {
     return medium_;
   }
 
   template <typename T>
-  void MediumPropertyModel<T>::setMedium(Medium const medium) {
+  inline void MediumPropertyModel<T>::setMedium(Medium const medium) {
     medium_ = medium;
   }
 
diff --git a/corsika/detail/media/NuclearComposition.inl b/corsika/detail/media/NuclearComposition.inl
index 230dfe1a144fd7c82cc52b06b7d87f2cf40c8bd5..85582510e5cb0e115c7e97133ee215de1193b2b2 100644
--- a/corsika/detail/media/NuclearComposition.inl
+++ b/corsika/detail/media/NuclearComposition.inl
@@ -22,8 +22,8 @@
 
 namespace corsika {
 
-  NuclearComposition::NuclearComposition(std::vector<Code> const& pComponents,
-                                         std::vector<float> const& pFractions)
+  inline NuclearComposition::NuclearComposition(std::vector<Code> const& pComponents,
+                                                std::vector<float> const& pFractions)
       : numberFractions_(pFractions)
       , components_(pComponents)
       , avgMassNumber_(std::inner_product(
diff --git a/corsika/detail/media/ShowerAxis.inl b/corsika/detail/media/ShowerAxis.inl
index 1be5d40a66c801299e57c95d1eee3b538839f953..a42989e9222f0c4ec6131d42fe37110ff62faf5a 100644
--- a/corsika/detail/media/ShowerAxis.inl
+++ b/corsika/detail/media/ShowerAxis.inl
@@ -14,9 +14,9 @@
 namespace corsika {
 
   template <typename TEnvModel>
-  ShowerAxis::ShowerAxis(Point const& pStart, Vector<length_d> const& length,
-                         Environment<TEnvModel> const& env, bool const doThrow,
-                         int const steps)
+  inline ShowerAxis::ShowerAxis(Point const& pStart, Vector<length_d> const& length,
+                                Environment<TEnvModel> const& env, bool const doThrow,
+                                int const steps)
       : pointStart_(pStart)
       , length_(length)
       , throw_(doThrow)
@@ -58,7 +58,7 @@ namespace corsika {
     assert(std::is_sorted(d_.cbegin(), d_.cend()));
   }
 
-  GrammageType ShowerAxis::getX(LengthType l) const {
+  inline GrammageType ShowerAxis::getX(LengthType l) const {
     double const fractionalBin = l / steplength_;
     int const lower = fractionalBin; // indices of nearest X support points
     double const fraction = fractionalBin - lower;
@@ -97,21 +97,21 @@ namespace corsika {
     return X_[upper] * fraction + X_[lower] * (1 - fraction);
   }
 
-  LengthType ShowerAxis::getSteplength() const { return steplength_; }
+  inline LengthType ShowerAxis::getSteplength() const { return steplength_; }
 
-  GrammageType ShowerAxis::getMaximumX() const { return *X_.rbegin(); }
+  inline GrammageType ShowerAxis::getMaximumX() const { return *X_.rbegin(); }
 
-  GrammageType ShowerAxis::getMinimumX() const { return GrammageType::zero(); }
+  inline GrammageType ShowerAxis::getMinimumX() const { return GrammageType::zero(); }
 
-  GrammageType ShowerAxis::getProjectedX(Point const& p) const {
+  inline GrammageType ShowerAxis::getProjectedX(Point const& p) const {
     auto const projectedLength = (p - pointStart_).dot(axis_normalized_);
     return getX(projectedLength);
   }
 
-  Vector<dimensionless_d> const& ShowerAxis::getDirection() const {
+  inline DirectionVector const& ShowerAxis::getDirection() const {
     return axis_normalized_;
   }
 
-  Point const& ShowerAxis::getStart() const { return pointStart_; }
+  inline Point const& ShowerAxis::getStart() const { return pointStart_; }
 
 } // namespace corsika
diff --git a/corsika/detail/media/SlidingPlanarExponential.inl b/corsika/detail/media/SlidingPlanarExponential.inl
index c5c517954bcc06858cd66e8142bcd1b30ebb097e..56d99404ce835b860e93ba8db21ec2ce34462d4b 100644
--- a/corsika/detail/media/SlidingPlanarExponential.inl
+++ b/corsika/detail/media/SlidingPlanarExponential.inl
@@ -13,7 +13,7 @@
 namespace corsika {
 
   template <typename T>
-  SlidingPlanarExponential<T>::SlidingPlanarExponential(
+  inline SlidingPlanarExponential<T>::SlidingPlanarExponential(
       Point const& p0, MassDensityType rho0, LengthType lambda,
       NuclearComposition const& nuclComp, LengthType referenceHeight)
       : BaseExponential<SlidingPlanarExponential<T>>(p0, rho0, lambda)
@@ -21,7 +21,8 @@ namespace corsika {
       , referenceHeight_(referenceHeight) {}
 
   template <typename T>
-  MassDensityType SlidingPlanarExponential<T>::getMassDensity(Point const& point) const {
+  inline MassDensityType SlidingPlanarExponential<T>::getMassDensity(
+      Point const& point) const {
     auto const height =
         (point - BaseExponential<SlidingPlanarExponential<T>>::getAnchorPoint())
             .getNorm() -
@@ -31,12 +32,13 @@ namespace corsika {
   }
 
   template <typename T>
-  NuclearComposition const& SlidingPlanarExponential<T>::getNuclearComposition() const {
+  inline NuclearComposition const& SlidingPlanarExponential<T>::getNuclearComposition()
+      const {
     return nuclComp_;
   }
 
   template <typename T>
-  GrammageType SlidingPlanarExponential<T>::getIntegratedGrammage(
+  inline GrammageType SlidingPlanarExponential<T>::getIntegratedGrammage(
       setup::Trajectory const& traj, LengthType l) const {
     auto const axis = (traj.getPosition(0) -
                        BaseExponential<SlidingPlanarExponential<T>>::getAnchorPoint())
@@ -46,7 +48,7 @@ namespace corsika {
   }
 
   template <typename T>
-  LengthType SlidingPlanarExponential<T>::getArclengthFromGrammage(
+  inline LengthType SlidingPlanarExponential<T>::getArclengthFromGrammage(
       setup::Trajectory const& traj, GrammageType const grammage) const {
     auto const axis = (traj.getPosition(0) -
                        BaseExponential<SlidingPlanarExponential<T>>::getAnchorPoint())
diff --git a/corsika/detail/media/UniformRefractiveIndex.inl b/corsika/detail/media/UniformRefractiveIndex.inl
index 1abea247066330608dfb4ace74eedddbbe91c7cc..d6e5f5a9d753c9ccf5e6d1154c50433a1ae482d2 100644
--- a/corsika/detail/media/UniformRefractiveIndex.inl
+++ b/corsika/detail/media/UniformRefractiveIndex.inl
@@ -14,17 +14,17 @@ namespace corsika {
 
   template <typename T>
   template <typename... Args>
-  UniformRefractiveIndex<T>::UniformRefractiveIndex(double const n, Args&&... args)
+  inline UniformRefractiveIndex<T>::UniformRefractiveIndex(double const n, Args&&... args)
       : T(std::forward<Args>(args)...)
       , n_(n) {}
 
   template <typename T>
-  double UniformRefractiveIndex<T>::getRefractiveIndex(Point const&) const {
+  inline double UniformRefractiveIndex<T>::getRefractiveIndex(Point const&) const {
     return n_;
   }
 
   template <typename T>
-  void UniformRefractiveIndex<T>::setRefractiveIndex(double const& n) {
+  inline void UniformRefractiveIndex<T>::setRefractiveIndex(double const& n) {
     n_ = n;
   }
 
diff --git a/corsika/detail/media/Universe.inl b/corsika/detail/media/Universe.inl
index b3f4d2541473ac7f3430ecd40bf607cbf4227464..c965ab3352071aa81a89b39f72b8ef612c6803b0 100644
--- a/corsika/detail/media/Universe.inl
+++ b/corsika/detail/media/Universe.inl
@@ -12,10 +12,10 @@
 
 namespace corsika {
 
-  Universe::Universe(CoordinateSystemPtr const& pCS)
+  inline Universe::Universe(CoordinateSystemPtr const& pCS)
       : corsika::Sphere(Point{pCS, 0 * meter, 0 * meter, 0 * meter},
                         meter * std::numeric_limits<double>::infinity()) {}
 
-  bool Universe::contains(corsika::Point const&) const { return true; }
+  inline bool Universe::contains(corsika::Point const&) const { return true; }
 
 } // namespace corsika
diff --git a/corsika/detail/media/VolumeTreeNode.inl b/corsika/detail/media/VolumeTreeNode.inl
index bdae7cc12b3525ef932c3319a67e5e05c683121e..7a334e61f90199063509d7f4cca7cdaf46e4c540 100644
--- a/corsika/detail/media/VolumeTreeNode.inl
+++ b/corsika/detail/media/VolumeTreeNode.inl
@@ -14,7 +14,7 @@
 namespace corsika {
 
   template <typename IModelProperties>
-  bool VolumeTreeNode<IModelProperties>::contains(Point const& p) const {
+  inline bool VolumeTreeNode<IModelProperties>::contains(Point const& p) const {
     return geoVolume_->contains(p);
   }
 
@@ -32,7 +32,7 @@ namespace corsika {
    * \class Point \p p, or nullptr iff \p p is not contained in this volume.
    */
   template <typename IModelProperties>
-  VolumeTreeNode<IModelProperties> const*
+  inline VolumeTreeNode<IModelProperties> const*
   VolumeTreeNode<IModelProperties>::getContainingNode(Point const& p) const {
     if (!contains(p)) { return nullptr; }
 
@@ -54,7 +54,7 @@ namespace corsika {
 
   template <typename IModelProperties>
   template <typename TCallable, bool preorder>
-  void VolumeTreeNode<IModelProperties>::walk(TCallable func) {
+  inline void VolumeTreeNode<IModelProperties>::walk(TCallable func) {
     if constexpr (preorder) { func(*this); }
 
     std::for_each(childNodes_.begin(), childNodes_.end(),
@@ -64,7 +64,7 @@ namespace corsika {
   }
 
   template <typename IModelProperties>
-  void VolumeTreeNode<IModelProperties>::addChild(
+  inline void VolumeTreeNode<IModelProperties>::addChild(
       typename VolumeTreeNode<IModelProperties>::VTNUPtr pChild) {
     pChild->parentNode_ = this;
     childNodes_.push_back(std::move(pChild));
@@ -74,19 +74,9 @@ namespace corsika {
   }
 
   template <typename IModelProperties>
-  void VolumeTreeNode<IModelProperties>::excludeOverlapWith(
+  inline void VolumeTreeNode<IModelProperties>::excludeOverlapWith(
       typename VolumeTreeNode<IModelProperties>::VTNUPtr const& pNode) {
     excludedNodes_.push_back(pNode.get());
   }
 
-  /*
-    template <typename IModelProperties>
-    template <class MediumType, typename... Args>
-    auto VolumeTreeNode<IModelProperties>::createMedium(Args&&... args) {
-      static_assert(std::is_base_of_v<IMediumModel, MediumType>,
-                    "unusable type provided, needs to be derived from \"IMediumModel\"");
-
-      return std::make_shared<MediumType>(std::forward<Args>(args)...);
-    }
-  */
 } // namespace corsika
diff --git a/corsika/detail/media/WeightProvider.inl b/corsika/detail/media/WeightProvider.inl
index c075b236350127c9978fb7c08d87ed035292e5a8..b9cf1be7ae0e94d0a99b66366373ae62861fffc6 100644
--- a/corsika/detail/media/WeightProvider.inl
+++ b/corsika/detail/media/WeightProvider.inl
@@ -13,19 +13,19 @@
 namespace corsika {
 
   template <class AConstIterator, class BConstIterator>
-  WeightProviderIterator<AConstIterator, BConstIterator>::WeightProviderIterator(
+  inline WeightProviderIterator<AConstIterator, BConstIterator>::WeightProviderIterator(
       AConstIterator a, BConstIterator b)
       : aIter_(a)
       , bIter_(b) {}
 
   template <class AConstIterator, class BConstIterator>
-  typename WeightProviderIterator<AConstIterator, BConstIterator>::value_type
-  WeightProviderIterator<AConstIterator, BConstIterator>::operator*() const {
+  inline typename WeightProviderIterator<AConstIterator, BConstIterator>::value_type
+      WeightProviderIterator<AConstIterator, BConstIterator>::operator*() const {
     return ((*aIter_) * (*bIter_)).magnitude();
   }
 
   template <class AConstIterator, class BConstIterator>
-  WeightProviderIterator<AConstIterator, BConstIterator>&
+  inline WeightProviderIterator<AConstIterator, BConstIterator>&
   WeightProviderIterator<AConstIterator,
                          BConstIterator>::operator++() { // prefix ++
     ++aIter_;
@@ -34,15 +34,15 @@ namespace corsika {
   }
 
   template <class AConstIterator, class BConstIterator>
-  bool WeightProviderIterator<AConstIterator, BConstIterator>::operator==(
+  inline bool WeightProviderIterator<AConstIterator, BConstIterator>::operator==(
       WeightProviderIterator other) {
     return aIter_ == other.aIter_;
   }
 
   template <class AConstIterator, class BConstIterator>
-  bool WeightProviderIterator<AConstIterator, BConstIterator>::operator!=(
+  inline bool WeightProviderIterator<AConstIterator, BConstIterator>::operator!=(
       WeightProviderIterator other) {
     return !(*this == other);
   }
 
-} // namespace corsika
\ No newline at end of file
+} // namespace corsika
diff --git a/corsika/detail/modules/HadronicElasticModel.inl b/corsika/detail/modules/HadronicElasticModel.inl
index d0a54a711b7b1c62a48dfffbc8bcab60a8a3c5a0..e048a2d9179fb4cd195af22bafa5e51f403ded65 100644
--- a/corsika/detail/modules/HadronicElasticModel.inl
+++ b/corsika/detail/modules/HadronicElasticModel.inl
@@ -23,13 +23,14 @@
 
 namespace corsika {
 
-  HadronicElasticInteraction::HadronicElasticInteraction(CrossSectionType x,
-                                                         CrossSectionType y)
+  inline HadronicElasticInteraction::HadronicElasticInteraction(CrossSectionType x,
+                                                                CrossSectionType y)
       : parX_(x)
       , parY_(y) {}
 
   template <>
-  GrammageType HadronicElasticInteraction::getInteractionLength(SetupParticle const& p) {
+  inline GrammageType HadronicElasticInteraction::getInteractionLength(
+      SetupParticle const& p) {
     if (p.getPID() == Code::Proton) {
       auto const* currentNode = p.getNode();
       auto const& mediumComposition =
@@ -52,7 +53,7 @@ namespace corsika {
           avgCrossSection += getCrossSection(s) * fractions[i];
         }
 
-        std::cout << "avgCrossSection: " << avgCrossSection / 1_mb << " mb" << std::endl;
+        CORSIKA_LOG_DEBUG("avgCrossSection: {} mb", avgCrossSection / 1_mb);
 
         return avgCrossSection;
       }();
@@ -69,7 +70,7 @@ namespace corsika {
   }
 
   template <typename TParticle>
-  ProcessReturn HadronicElasticInteraction::doInteraction(TParticle& p) {
+  inline ProcessReturn HadronicElasticInteraction::doInteraction(TParticle& p) {
     if (p.getPID() != Code::Proton) { return ProcessReturn::Ok; }
 
     const auto* currentNode = p.getNode();
@@ -116,7 +117,7 @@ namespace corsika {
     auto const s = static_pow<2>(sqrtS);
 
     auto const B = this->B(s);
-    std::cout << B << std::endl;
+    CORSIKA_LOG_DEBUG(B);
 
     ExponentialDistribution tDist(1 / B);
     auto const absT = [&]() {
@@ -133,10 +134,12 @@ namespace corsika {
       return absT;
     }();
 
-    std::cout << "HadronicElasticInteraction: s = " << s * constants::invGeVsq
-              << " GeV²; absT = " << absT * constants::invGeVsq << " GeV² (max./GeV² = "
-              << 4 * constants::invGeVsq * projectileMomentumSquaredNorm << ')'
-              << std::endl;
+    CORSIKA_LOG_DEBUG(
+        "HadronicElasticInteraction: s = {}"
+        " GeV²; absT = {} "
+        " GeV² (max./GeV² = {})",
+        s * constants::invGeVsq, absT * constants::invGeVsq,
+        4 * constants::invGeVsq * projectileMomentumSquaredNorm);
 
     auto const theta = 2 * asin(sqrt(absT / (4 * pProjectileCoMSqNorm)));
     auto const phi = phiDist(RNG_);
@@ -158,17 +161,17 @@ namespace corsika {
     return ProcessReturn::Ok;
   }
 
-  HadronicElasticInteraction::inveV2 HadronicElasticInteraction::B(eV2 s) const {
+  inline HadronicElasticInteraction::inveV2 HadronicElasticInteraction::B(eV2 s) const {
     auto constexpr b_p = 2.3;
     auto const result =
         (2 * b_p + 2 * b_p + 4 * pow(s * constants::invGeVsq, gfEpsilon) - 4.2) *
         constants::invGeVsq;
-    std::cout << "B(" << s << ") = " << result / constants::invGeVsq << " GeV¯²"
-              << std::endl;
+    CORSIKA_LOG_DEBUG("B({}) = {}  GeV¯²", s, result / constants::invGeVsq);
+
     return result;
   }
 
-  CrossSectionType HadronicElasticInteraction::getCrossSection(
+  inline CrossSectionType HadronicElasticInteraction::getCrossSection(
       SquaredHEPEnergyType s) const {
     // assuming every target behaves like a proton, parX_ and parY_ are universal
     CrossSectionType const sigmaTotal = parX_ * pow(s * constants::invGeVsq, gfEpsilon) +
@@ -180,8 +183,8 @@ namespace corsika {
         static_pow<2>(sigmaTotal) /
         (16 * constants::pi * convert_HEP_to_SI<CrossSectionType::dimension_type>(B(s)));
 
-    std::cout << "HEM sigmaTot = " << sigmaTotal / 1_mb << " mb" << std::endl;
-    std::cout << "HEM sigmaElastic = " << sigmaElastic / 1_mb << " mb" << std::endl;
+    CORSIKA_LOG_DEBUG("HEM sigmaTot = {} mb", sigmaTotal / 1_mb);
+    CORSIKA_LOG_DEBUG("HEM sigmaElastic = {} mb", sigmaElastic / 1_mb);
     return sigmaElastic;
   }
 
diff --git a/corsika/detail/modules/LongitudinalProfile.inl b/corsika/detail/modules/LongitudinalProfile.inl
index 592f71464ed54d75cc7a20c30f43d08cd59384ad..48deafe6e79a474f06ecf664d9b70640b8e7756b 100644
--- a/corsika/detail/modules/LongitudinalProfile.inl
+++ b/corsika/detail/modules/LongitudinalProfile.inl
@@ -20,23 +20,28 @@
 
 namespace corsika {
 
-  LongitudinalProfile::LongitudinalProfile(ShowerAxis const& shower_axis, GrammageType dX)
+  inline LongitudinalProfile::LongitudinalProfile(ShowerAxis const& shower_axis,
+                                                  GrammageType dX)
       : dX_(dX)
       , shower_axis_{shower_axis}
       , profiles_{static_cast<unsigned int>(shower_axis.getMaximumX() / dX_) + 1} {}
 
   template <typename TParticle, typename TTrack>
-  ProcessReturn LongitudinalProfile::doContinuous(TParticle const& vP,
-                                                  TTrack const& vTrack) {
+  inline ProcessReturn LongitudinalProfile::doContinuous(TParticle const& vP,
+                                                         TTrack const& vTrack,
+                                                         bool const) {
     auto const pid = vP.getPID();
 
     GrammageType const grammageStart = shower_axis_.getProjectedX(vTrack.getPosition(0));
     GrammageType const grammageEnd = shower_axis_.getProjectedX(vTrack.getPosition(1));
 
-    CORSIKA_LOG_INFO(
-        "pos1={} m, pos2={}, X={} g/cm2", vTrack.getPosition(0).getCoordinates() / 1_m,
-        vTrack.getPosition(1).getCoordinates() / 1_m, grammageStart / 1_g * square(1_cm));
+    CORSIKA_LOG_TRACE("pos1={} m, pos2={}, X1={} g/cm2, X2={} g/cm2",
+                      vTrack.getPosition(0).getCoordinates() / 1_m,
+                      vTrack.getPosition(1).getCoordinates() / 1_m,
+                      grammageStart / 1_g * square(1_cm),
+                      grammageEnd / 1_g * square(1_cm));
 
+    // Note: particle may go also "upward", thus, grammageEnd<grammageStart
     const int binStart = std::ceil(grammageStart / dX_);
     const int binEnd = std::floor(grammageEnd / dX_);
 
@@ -59,8 +64,8 @@ namespace corsika {
     return ProcessReturn::Ok;
   }
 
-  void LongitudinalProfile::save(std::string const& filename, const int width,
-                                 const int precision) {
+  inline void LongitudinalProfile::save(std::string const& filename, const int width,
+                                        const int precision) {
     std::ofstream f{filename};
     f << "# X / g·cm¯², gamma, e+, e-, mu+, mu-, all hadrons" << std::endl;
     for (size_t b = 0; b < profiles_.size(); ++b) {
diff --git a/corsika/detail/modules/ObservationPlane.inl b/corsika/detail/modules/ObservationPlane.inl
index 62d8d1080ef9390dbf146faf910db42710a8631b..37301ba6c409f7fcf001aad81997caaf4a80a8b0 100644
--- a/corsika/detail/modules/ObservationPlane.inl
+++ b/corsika/detail/modules/ObservationPlane.inl
@@ -12,7 +12,6 @@ namespace corsika {
   ObservationPlane<TOutput>::ObservationPlane(Plane const& obsPlane,
                                               DirectionVector const& x_axis,
                                               bool deleteOnHit)
-
       : plane_(obsPlane)
       , deleteOnHit_(deleteOnHit)
       , energy_ground_(0_GeV)
@@ -21,22 +20,17 @@ namespace corsika {
       , yAxis_(obsPlane.getNormal().cross(xAxis_)) {}
 
   template <typename TOutput>
-  ProcessReturn ObservationPlane<TOutput>::doContinuous(
+  inline ProcessReturn ObservationPlane<TOutput>::doContinuous(
       corsika::setup::Stack::particle_type& particle,
-      corsika::setup::Trajectory& trajectory) {
-    TimeType const timeOfIntersection =
-        (plane_.getCenter() - trajectory.getPosition(0)).dot(plane_.getNormal()) /
-        trajectory.getVelocity(0).dot(plane_.getNormal());
-
-    if (timeOfIntersection < TimeType::zero()) { return ProcessReturn::Ok; }
+      corsika::setup::Trajectory& trajectory, bool const stepLimit) {
+    /*
+       The current step did not yet reach the ObservationPlane, do nothing now and wait:
+     */
+    if (!stepLimit) { return ProcessReturn::Ok; }
 
-    if (plane_.isAbove(trajectory.getPosition(0)) ==
-        plane_.isAbove(trajectory.getPosition(1))) {
-      return ProcessReturn::Ok;
-    }
-
-    const auto energy = particle.getEnergy();
-    auto const displacement = trajectory.getPosition(1) - plane_.getCenter();
+    HEPEnergyType const energy = particle.getEnergy();
+    Point const pointOfIntersection = particle.getPosition();
+    Vector const displacement = pointOfIntersection - plane_.getCenter();
 
     // add our particles to the output file stream
     this->write(particle.getPID(), energy, displacement.dot(xAxis_),
@@ -45,7 +39,6 @@ namespace corsika {
     if (deleteOnHit_) {
       count_ground_++;
       energy_ground_ += energy;
-      particle.erase();
       return ProcessReturn::ParticleAbsorbed;
     } else {
       return ProcessReturn::Ok;
@@ -53,90 +46,32 @@ namespace corsika {
   }
 
   template <typename TOutput>
-  LengthType ObservationPlane<TOutput>::getMaxStepLength(
+  inline LengthType ObservationPlane<TOutput>::getMaxStepLength(
       corsika::setup::Stack::particle_type const& vParticle,
       corsika::setup::Trajectory const& trajectory) {
 
-    int chargeNumber;
-    if (is_nucleus(vParticle.getPID())) {
-      chargeNumber = vParticle.getNuclearZ();
-    } else {
-      chargeNumber = get_charge_number(vParticle.getPID());
-    }
-    auto const* currentLogicalVolumeNode = vParticle.getNode();
-    auto magneticfield = currentLogicalVolumeNode->getModelProperties().getMagneticField(
-        vParticle.getPosition());
-    auto direction = trajectory.getVelocity(0).normalized();
-
-    if (chargeNumber != 0 &&
-        abs(plane_.getNormal().dot(
-            trajectory.getLine().getVelocity().cross(magneticfield))) *
-                1_s / 1_m / 1_T >
-            1e-6) {
-      auto const* currentLogicalVolumeNode = vParticle.getNode();
-      auto magneticfield =
-          currentLogicalVolumeNode->getModelProperties().getMagneticField(
-              vParticle.getPosition());
-      auto k =
-          chargeNumber * constants::c * 1_eV / (vParticle.getMomentum().getNorm() * 1_V);
-
-      if (direction.dot(plane_.getNormal()) * direction.dot(plane_.getNormal()) -
-              (plane_.getNormal().dot(trajectory.getPosition(0) - plane_.getCenter()) *
-               plane_.getNormal().dot(direction.cross(magneticfield)) * 2 * k) <
-          0) {
-        return std::numeric_limits<double>::infinity() * 1_m;
-      }
-
-      LengthType MaxStepLength1 =
-          (sqrt(direction.dot(plane_.getNormal()) * direction.dot(plane_.getNormal()) -
-                (plane_.getNormal().dot(trajectory.getPosition(0) - plane_.getCenter()) *
-                 plane_.getNormal().dot(direction.cross(magneticfield)) * 2 * k)) -
-           direction.dot(plane_.getNormal()) / direction.getNorm()) /
-          (plane_.getNormal().dot(direction.cross(magneticfield)) * k);
-
-      LengthType MaxStepLength2 =
-          (-sqrt(direction.dot(plane_.getNormal()) * direction.dot(plane_.getNormal()) -
-                 (plane_.getNormal().dot(trajectory.getPosition(0) - plane_.getCenter()) *
-                  plane_.getNormal().dot(direction.cross(magneticfield)) * 2 * k)) -
-           direction.dot(plane_.getNormal()) / direction.getNorm()) /
-          (plane_.getNormal().dot(direction.cross(magneticfield)) * k);
-
-      if (MaxStepLength1 <= 0_m && MaxStepLength2 <= 0_m) {
-        return std::numeric_limits<double>::infinity() * 1_m;
-      } else if (MaxStepLength1 <= 0_m || MaxStepLength2 < MaxStepLength1) {
-        std::cout << " steplength to obs plane 2: " << MaxStepLength2 << std::endl;
-        return MaxStepLength2 *
-               (direction + direction.cross(magneticfield) * MaxStepLength2 * k / 2)
-                   .getNorm() *
-               1.001;
-      } else if (MaxStepLength2 <= 0_m || MaxStepLength1 < MaxStepLength2) {
-        std::cout << " steplength to obs plane 1: " << MaxStepLength1 << std::endl;
-        return MaxStepLength1 *
-               (direction + direction.cross(magneticfield) * MaxStepLength2 * k / 2)
-                   .getNorm() *
-               1.001;
-      }
-    }
-
-    TimeType const timeOfIntersection =
-        (plane_.getCenter() - trajectory.getPosition(0)).dot(plane_.getNormal()) /
-        trajectory.getVelocity(0).dot(plane_.getNormal());
-
+    Intersections const intersection =
+        setup::Tracking::intersect<corsika::setup::Stack::particle_type>(particle,
+                                                                         plane_);
+    TimeType const timeOfIntersection = intersection.getEntry();
+    CORSIKA_LOG_TRACE("particle={}, pos={}, dir={}, plane={}, timeOfIntersection={}",
+                      particle.asString(), particle.getPosition(),
+                      particle.getDirection(), plane_.asString(), timeOfIntersection);
     if (timeOfIntersection < TimeType::zero()) {
       return std::numeric_limits<double>::infinity() * 1_m;
     }
-
+    if (timeOfIntersection > trajectory.getDuration()) {
+      return std::numeric_limits<double>::infinity() * 1_m;
+    }
     double const fractionOfIntersection = timeOfIntersection / trajectory.getDuration();
-
     auto const pointOfIntersection = trajectory.getPosition(fractionOfIntersection);
-    auto dist = (trajectory.getPosition(0) - pointOfIntersection).getNorm() * 1.0001;
-    CORSIKA_LOG_TRACE("ObservationPlane w/o magnetic field: getMaxStepLength l={} m",
-                      dist / 1_m);
+    auto dist = (trajectory.getPosition(0) - pointOfIntersection).getNorm();
+    CORSIKA_LOG_TRACE("ObservationPlane: getMaxStepLength l={} m", dist / 1_m);
     return dist;
   }
 
   template <typename TOutput>
-  void ObservationPlane<TOutput>::showResults() const {
+  inline void ObservationPlane<TOutput>::showResults() const {
     CORSIKA_LOG_INFO(
         " ******************************\n"
         " ObservationPlane: \n"
@@ -147,7 +82,7 @@ namespace corsika {
   }
 
   template <typename TOutput>
-  YAML::Node ObservationPlane<TOutput>::getConfig() const {
+  inline YAML::Node ObservationPlane<TOutput>::getConfig() const {
     using namespace units::si;
 
     // construct the top-level node
@@ -190,7 +125,7 @@ namespace corsika {
   }
 
   template <typename TOutput>
-  void ObservationPlane<TOutput>::reset() {
+  inline void ObservationPlane<TOutput>::reset() {
     energy_ground_ = 0_GeV;
     count_ground_ = 0;
   }
diff --git a/corsika/detail/modules/OnShellCheck.inl b/corsika/detail/modules/OnShellCheck.inl
index aa6b0453fb24400246c96de206fa415dd0ade7cb..c7ee250f23b6445919b5f0ab637536fcdff2759e 100644
--- a/corsika/detail/modules/OnShellCheck.inl
+++ b/corsika/detail/modules/OnShellCheck.inl
@@ -14,8 +14,8 @@
 
 namespace corsika {
 
-  OnShellCheck::OnShellCheck(const double vMassTolerance, const double vEnergyTolerance,
-                             const bool vError)
+  inline OnShellCheck::OnShellCheck(double const vMassTolerance,
+                                    double const vEnergyTolerance, bool const vError)
       : mass_tolerance_(vMassTolerance)
       , energy_tolerance_(vEnergyTolerance)
       , throw_error_(vError) {
@@ -25,7 +25,7 @@ namespace corsika {
                          energy_tolerance_ * 100);
   }
 
-  OnShellCheck::~OnShellCheck() {
+  inline OnShellCheck::~OnShellCheck() {
     logger_->info(
         " summary \n"
         " particles shifted: {} \n"
@@ -35,7 +35,7 @@ namespace corsika {
   }
 
   template <typename TView>
-  void OnShellCheck::doSecondaries(TView& vS) {
+  inline void OnShellCheck::doSecondaries(TView& vS) {
     for (auto& p : vS) {
       auto const pid = p.getPID();
       if (is_nucleus(pid)) continue;
@@ -70,16 +70,18 @@ namespace corsika {
         if (abs(e_shift_relative) > energy_tolerance_) {
           logger_->warn("warning! shifted particle energy by {} %",
                         e_shift_relative * 100);
-          if (throw_error_)
+          if (throw_error_) {
             throw std::runtime_error(
                 "OnShellCheck: error! shifted energy by large amount!");
+          }
         }
 
         // reset energy
         p.setEnergy(e_shifted);
-      } else
+      } else {
         CORSIKA_LOGGER_DEBUG(logger_, "particle mass for {} OK", pid);
+      }
     }
-  }
+  } // namespace corsika
 
 } // namespace corsika
diff --git a/corsika/detail/modules/ParticleCut.inl b/corsika/detail/modules/ParticleCut.inl
index 7a22b4d719beddef778acab9861b65ba7578217f..ffbbbed8ddd51cb99ffe45f92217ed426fe2ee91 100644
--- a/corsika/detail/modules/ParticleCut.inl
+++ b/corsika/detail/modules/ParticleCut.inl
@@ -12,9 +12,10 @@
 
 namespace corsika {
 
-  ParticleCut::ParticleCut(HEPEnergyType const eEleCut, HEPEnergyType const ePhoCut,
-                           HEPEnergyType const eHadCut, HEPEnergyType const eMuCut,
-                           bool const inv)
+  inline ParticleCut::ParticleCut(HEPEnergyType const eEleCut,
+                                  HEPEnergyType const ePhoCut,
+                                  HEPEnergyType const eHadCut, HEPEnergyType const eMuCut,
+                                  bool const inv)
       : doCutEm_(false)
       , doCutInv_(inv)
       , energy_(0_GeV)
@@ -41,8 +42,8 @@ namespace corsika {
     printThresholds();
   }
 
-  ParticleCut::ParticleCut(HEPEnergyType const eHadCut, HEPEnergyType const eMuCut,
-                           bool const inv)
+  inline ParticleCut::ParticleCut(HEPEnergyType const eHadCut, HEPEnergyType const eMuCut,
+                                  bool const inv)
       : doCutEm_(true)
       , doCutInv_(inv)
       , energy_(0_GeV)
@@ -63,7 +64,7 @@ namespace corsika {
     printThresholds();
   }
 
-  ParticleCut::ParticleCut(HEPEnergyType const eCut, bool const em, bool const inv)
+  inline ParticleCut::ParticleCut(HEPEnergyType const eCut, bool const em, bool const inv)
       : doCutEm_(em)
       , doCutInv_(inv)
       , energy_(0_GeV)
@@ -76,7 +77,7 @@ namespace corsika {
     printThresholds();
   }
 
-  ParticleCut::ParticleCut(
+  inline ParticleCut::ParticleCut(
       std::unordered_map<Code const, HEPEnergyType const> const& eCuts, bool const em,
       bool const inv)
       : doCutEm_(em)
@@ -92,7 +93,7 @@ namespace corsika {
   }
 
   template <typename TParticle>
-  bool ParticleCut::isBelowEnergyCut(TParticle const& vP) const {
+  inline bool ParticleCut::isBelowEnergyCut(TParticle const& vP) const {
     auto const energyLab = vP.getEnergy();
     auto const pid = vP.getPID();
     // nuclei
@@ -105,10 +106,12 @@ namespace corsika {
     }
   }
 
-  bool ParticleCut::isInvisible(Code const& vCode) const { return is_neutrino(vCode); }
+  inline bool ParticleCut::isInvisible(Code const& vCode) const {
+    return is_neutrino(vCode);
+  }
 
   template <typename TParticle>
-  bool ParticleCut::checkCutParticle(TParticle const& particle) {
+  inline bool ParticleCut::checkCutParticle(TParticle const& particle) {
 
     Code const pid = particle.getPID();
     HEPEnergyType energy = particle.getEnergy();
@@ -136,7 +139,7 @@ namespace corsika {
     return false; // this particle will not be removed/cut
   }
 
-  void ParticleCut::doSecondaries(corsika::setup::StackView& vS) {
+  inline void ParticleCut::doSecondaries(corsika::setup::StackView& vS) {
     auto particle = vS.begin();
     while (particle != vS.end()) {
       if (checkCutParticle(particle)) { particle.erase(); }
@@ -144,26 +147,26 @@ namespace corsika {
     }
   }
 
-  ProcessReturn ParticleCut::doContinuous(corsika::setup::Stack::particle_type& particle,
-                                          corsika::setup::Trajectory const&) {
+  inline ProcessReturn ParticleCut::doContinuous(
+      corsika::setup::Stack::particle_type& particle, corsika::setup::Trajectory const&,
+      bool const) {
     CORSIKA_LOG_TRACE("ParticleCut::DoContinuous");
     if (checkCutParticle(particle)) {
       CORSIKA_LOG_TRACE("removing during continuous");
-      particle.erase();
       // signal to upstream code that this particle was deleted
       return ProcessReturn::ParticleAbsorbed;
     }
     return ProcessReturn::Ok;
   }
 
-  void ParticleCut::printThresholds() {
+  inline void ParticleCut::printThresholds() {
     for (auto p : get_all_particles()) {
       auto const Eth = get_energy_threshold(p);
       CORSIKA_LOG_INFO("energy threshold for particle {} is {} GeV", p, Eth / 1_GeV);
     }
   }
 
-  void ParticleCut::showResults() {
+  inline void ParticleCut::showResults() {
     CORSIKA_LOG_INFO(
         " ******************************\n"
         " energy in em.  component (GeV): {} \n "
@@ -175,7 +178,7 @@ namespace corsika {
         em_energy_ / 1_GeV, em_count_, inv_energy_ / 1_GeV, inv_count_, energy_ / 1_GeV);
   }
 
-  void ParticleCut::reset() {
+  inline void ParticleCut::reset() {
     em_energy_ = 0_GeV;
     em_count_ = 0;
     inv_energy_ = 0_GeV;
diff --git a/corsika/detail/modules/StackInspector.inl b/corsika/detail/modules/StackInspector.inl
index 517e017fceb9baf68c071399c5e67bed8a75b3b6..caae6c59c8cb43b01cd1e9789366b51db2f9ec14 100644
--- a/corsika/detail/modules/StackInspector.inl
+++ b/corsika/detail/modules/StackInspector.inl
@@ -24,40 +24,43 @@
 namespace corsika {
 
   template <typename TStack>
-  StackInspector<TStack>::StackInspector(const int vNStep, const bool vReportStack,
-                                         const HEPEnergyType vE0)
+  inline StackInspector<TStack>::StackInspector(int const vNStep, bool const vReportStack,
+                                                HEPEnergyType const vE0)
       : StackProcess<StackInspector<TStack>>(vNStep)
       , ReportStack_(vReportStack)
       , E0_(vE0)
       , StartTime_(std::chrono::system_clock::now()) {}
 
   template <typename TStack>
-  StackInspector<TStack>::~StackInspector() {}
+  inline StackInspector<TStack>::~StackInspector() {}
 
   template <typename TStack>
-  void StackInspector<TStack>::doStack(const TStack& vS) {
+  inline void StackInspector<TStack>::doStack(TStack const& vS) {
 
     [[maybe_unused]] int i = 0;
     HEPEnergyType Etot = 0_GeV;
 
-    for (const auto& iterP : vS) {
+    for (auto const& iterP : vS) {
       HEPEnergyType E = iterP.getEnergy();
       Etot += E;
       if (ReportStack_) {
         CoordinateSystemPtr const& rootCS = get_root_CoordinateSystem(); // for printout
         auto pos = iterP.getPosition().getCoordinates(rootCS);
-        std::cout << "StackInspector: i=" << std::setw(5) << std::fixed << (i++)
-                  << ", id=" << std::setw(30) << iterP.getPID() << " E=" << std::setw(15)
-                  << std::scientific << (E / 1_GeV) << " GeV, "
-                  << " pos=" << pos << " node = " << iterP.getNode();
+        CORSIKA_LOG_INFO(
+            "StackInspector: i= {:5d}"
+            ", id= {:^15}"
+            " E={:15e} GeV, "
+            " pos= {}"
+            " node = {}",
+            (i++), iterP.getPID(), (E / 1_GeV), pos, fmt::ptr(iterP.getNode()));
+
         if (iterP.getPID() == Code::Nucleus)
-          std::cout << " nuc_ref=" << iterP.getNucleusRef();
-        std::cout << std::endl;
+          CORSIKA_LOG_INFO("nuc_ref= {}", iterP.getNucleusRef());
       }
     }
 
     auto const now = std::chrono::system_clock::now();
-    const std::chrono::duration<double> elapsed_seconds = now - StartTime_;
+    std::chrono::duration<double> const elapsed_seconds = now - StartTime_;
     std::time_t const now_time = std::chrono::system_clock::to_time_t(now);
     auto const dE = E0_ - Etot;
     if (dE < dE_threshold_) return;
@@ -67,13 +70,18 @@ namespace corsika {
     std::time_t const eta_time = std::chrono::system_clock::to_time_t(
         StartTime_ + std::chrono::seconds((int)eta_seconds));
 
-    std::cout << "StackInspector: "
-              << " time=" << std::put_time(std::localtime(&now_time), "%T")
-              << ", running=" << elapsed_seconds.count() << " seconds"
-              << " (" << std::setw(3) << int(progress * 100) << "%)"
-              << ", nStep=" << getStep() << ", stackSize=" << vS.getSize()
-              << ", Estack=" << Etot / 1_GeV << " GeV"
-              << ", ETA=" << std::put_time(std::localtime(&eta_time), "%T") << std::endl;
+    CORSIKA_LOG_INFO(
+        "StackInspector: "
+        " time= {}"
+        ", running= {} seconds"
+        " ( {}%)"
+        ", nStep= {}"
+        ", stackSize= {}"
+        ", Estack= {} GeV"
+        ", ETA=",
+        std::put_time(std::localtime(&now_time), "%T"), elapsed_seconds.count(),
+        int(progress * 100), getStep(), vS.getSize(), Etot / 1_GeV,
+        std::put_time(std::localtime(&eta_time), "%T"));
   }
 
-} // namespace corsika
\ No newline at end of file
+} // namespace corsika
diff --git a/corsika/detail/modules/TrackWriter.inl b/corsika/detail/modules/TrackWriter.inl
index efa10e50bd91f2803e7df5f70feb2c5078a9ead9..b5b975818f585dc19a70afbf106360b33e5b6308 100644
--- a/corsika/detail/modules/TrackWriter.inl
+++ b/corsika/detail/modules/TrackWriter.inl
@@ -16,12 +16,13 @@
 namespace corsika {
 
   template <typename TOutput>
-  TrackWriter<TOutput>::TrackWriter() {}
+  inline TrackWriter<TOutput>::TrackWriter() {}
 
   template <typename TOutput>
   template <typename TParticle, typename TTrack>
-  ProcessReturn TrackWriter<TOutput>::doContinuous(const TParticle& vP,
-                                                   const TTrack& vT) {
+  ProcessReturn TrackWriter<TOutput>::doContinuous(const TParticle& vP, const TTrack& vT,
+                                                   bool const) {
+
     auto const start = vT.getPosition(0).getCoordinates();
     auto const end = vT.getPosition(1).getCoordinates();
 
@@ -33,7 +34,8 @@ namespace corsika {
 
   template <typename TOutput>
   template <typename TParticle, typename TTrack>
-  LengthType TrackWriter<TOutput>::getMaxStepLength(const TParticle&, const TTrack&) {
+  inline LengthType TrackWriter<TOutput>::getMaxStepLength(const TParticle&,
+                                                           const TTrack&) {
     return meter * std::numeric_limits<double>::infinity();
   }
 
diff --git a/corsika/detail/modules/TrackingLine.inl b/corsika/detail/modules/TrackingLine.inl
index 52200c42d839d85c21fb8d6676d36c418f03c9e7..84400b81d24555d8022c259e5112cb1da6abe0b5 100644
--- a/corsika/detail/modules/TrackingLine.inl
+++ b/corsika/detail/modules/TrackingLine.inl
@@ -20,7 +20,7 @@
 
 namespace corsika::tracking_line {
 
-  std::optional<std::pair<TimeType, TimeType>> TimeOfIntersection(
+  inline std::optional<std::pair<TimeType, TimeType>> TimeOfIntersection(
       corsika::Line const& line, corsika::Sphere const& sphere) {
     auto const delta = line.getStartPoint() - sphere.getCenter();
     auto const v = line.getVelocity();
@@ -42,7 +42,7 @@ namespace corsika::tracking_line {
     }
   }
 
-  TimeType getTimeOfIntersection(Line const& vLine, Plane const& vPlane) {
+  inline TimeType getTimeOfIntersection(Line const& vLine, Plane const& vPlane) {
 
     auto const delta = vPlane.getCenter() - vLine.getStartPoint();
     auto const v = vLine.getVelocity();
diff --git a/corsika/detail/modules/conex/CONEXhybrid.inl b/corsika/detail/modules/conex/CONEXhybrid.inl
index f722440d304e20a75c83a73e975fe06c1b15b36e..5b350013f27d75eeaf7ed2ed8f1ea5c0bed6cb61 100644
--- a/corsika/detail/modules/conex/CONEXhybrid.inl
+++ b/corsika/detail/modules/conex/CONEXhybrid.inl
@@ -11,6 +11,7 @@
 #include <corsika/modules/conex/CONEX_f.hpp>
 #include <corsika/framework/random/RNGManager.hpp>
 #include <corsika/framework/core/PhysicalConstants.hpp>
+#include <corsika/framework/core/Logging.hpp>
 
 #include <conexConfig.h>
 
@@ -21,9 +22,9 @@
 
 namespace corsika {
 
-  CONEXhybrid::CONEXhybrid(Point center, ShowerAxis const& showerAxis,
-                           LengthType groundDist, LengthType injectionHeight,
-                           HEPEnergyType primaryEnergy, PDGCode primaryPDG)
+  inline CONEXhybrid::CONEXhybrid(Point center, ShowerAxis const& showerAxis,
+                                  LengthType groundDist, LengthType injectionHeight,
+                                  HEPEnergyType primaryEnergy, PDGCode primaryPDG)
       : center_{center}
       , showerAxis_{showerAxis}
       , groundDist_{groundDist}
@@ -35,19 +36,11 @@ namespace corsika {
             make_translation(c8cs, translation.getComponents(c8cs));
         auto const transformCS = make_rotationToZ(intermediateCS, translation);
 
-        std::cout << "translation C8/CONEX obs: " << translation.getComponents()
-                  << std::endl;
+        CORSIKA_LOG_DEBUG("translation C8/CONEX obs: ", translation.getComponents());
 
         /*
         auto const transform = CoordinateSystem::getTransformation(
             intermediateCS2, c8cs); // either this way or vice versa... TODO: test this!
-        std::cout << transform.matrix() << std::endl << std::endl;
-        std::cout << CoordinateSystem::getTransformation(intermediateCS, c8cs).matrix()
-                  << std::endl
-                  << std::endl;
-        std::cout << CoordinateSystem::getTransformation(intermediateCS2, intermediateCS)
-                         .matrix()
-                  << std::endl;
         */
         return transformCS;
       })}
@@ -63,27 +56,25 @@ namespace corsika {
       })}
       , y_sf_{showerAxis_.getDirection().cross(x_sf_)} {
 
-    std::cout << "x_sf (conexObservationCS): " << x_sf_.getComponents(conexObservationCS_)
-              << std::endl;
-    std::cout << "x_sf (C8): " << x_sf_.getComponents(center.getCoordinateSystem())
-              << std::endl;
+    CORSIKA_LOG_DEBUG("x_sf (conexObservationCS): {}",
+                      x_sf_.getComponents(conexObservationCS_));
+    CORSIKA_LOG_DEBUG("x_sf (C8): {}", x_sf_.getComponents(center.getCoordinateSystem()));
+
+    CORSIKA_LOG_DEBUG("y_sf (conexObservationCS): {}",
+                      y_sf_.getComponents(conexObservationCS_));
 
-    std::cout << "y_sf (conexObservationCS): " << y_sf_.getComponents(conexObservationCS_)
-              << std::endl;
-    std::cout << "y_sf (C8): " << y_sf_.getComponents(center.getCoordinateSystem())
-              << std::endl;
+    CORSIKA_LOG_DEBUG("y_sf (C8): {}", y_sf_.getComponents(center.getCoordinateSystem()));
 
-    std::cout << "showerAxisDirection (conexObservationCS): "
-              << showerAxis_.getDirection().getComponents(conexObservationCS_)
-              << std::endl;
-    std::cout << "showerAxisDirection (C8): "
-              << showerAxis_.getDirection().getComponents(center.getCoordinateSystem())
-              << std::endl;
+    CORSIKA_LOG_DEBUG("showerAxisDirection (conexObservationCS): {}",
+                      showerAxis_.getDirection().getComponents(conexObservationCS_));
+    CORSIKA_LOG_DEBUG(
+        "showerAxisDirection (C8): {}",
+        showerAxis_.getDirection().getComponents(center.getCoordinateSystem()));
 
-    std::cout << "showerCore (conexObservationCS): "
-              << showerCore_.getCoordinates(conexObservationCS_) << std::endl;
-    std::cout << "showerCore (C8): "
-              << showerCore_.getCoordinates(center.getCoordinateSystem()) << std::endl;
+    CORSIKA_LOG_DEBUG("showerCore (conexObservationCS): {}",
+                      showerCore_.getCoordinates(conexObservationCS_));
+    CORSIKA_LOG_DEBUG("showerCore (C8): {}",
+                      showerCore_.getCoordinates(center.getCoordinateSystem()));
 
     int randomSeeds[3] = {1234, 0, 0}; // will be overwritten later??
     int heModel = eSibyll23;
@@ -114,7 +105,10 @@ namespace corsika {
                             showerAxisConex.getX().magnitude()) *
                  180 / M_PI;
 
-    std::cout << "theta (deg) = " << theta << "; phi (deg) = " << phi << std::endl;
+    CORSIKA_LOG_DEBUG(
+        "theta (deg) = {}"
+        "; phi (deg) = {}",
+        theta, phi);
 
     int ipart = static_cast<int>(primaryPDG);
     auto rng = RNGManager::getInstance().getRandomStream("cascade");
@@ -130,7 +124,7 @@ namespace corsika {
     ::conex::conexrun_(ipart, eprima, theta, phi, xminp, dimpact, ioseed.data());
   }
 
-  void CONEXhybrid::doSecondaries(setup::StackView& vS) {
+  inline void CONEXhybrid::doSecondaries(setup::StackView& vS) {
     auto p = vS.begin();
     while (p != vS.end()) {
       Code const pid = p.getPID();
@@ -142,9 +136,9 @@ namespace corsika {
     }
   }
 
-  bool CONEXhybrid::addParticle(Code pid, HEPEnergyType energy, HEPEnergyType mass,
-                                Point const& position, DirectionVector const& direction,
-                                TimeType t) {
+  inline bool CONEXhybrid::addParticle(Code pid, HEPEnergyType energy, HEPEnergyType mass,
+                                       Point const& position,
+                                       DirectionVector const& direction, TimeType t) {
 
     auto const it = std::find_if(egs_em_codes_.cbegin(), egs_em_codes_.cend(),
                                  [=](auto const& p) { return pid == p.first; });
@@ -152,8 +146,8 @@ namespace corsika {
 
     // EM particle
     auto const egs_pid = it->second;
-    std::cout << "position conexObs: " << position.getCoordinates(conexObservationCS_)
-              << std::endl;
+    CORSIKA_LOG_DEBUG("position conexObs: {}",
+                      position.getCoordinates(conexObservationCS_));
 
     auto const coords = position.getCoordinates(conexObservationCS_) / 1_m;
     double const x = coords[0].magnitude();
@@ -187,24 +181,23 @@ namespace corsika {
     double const E = energy / 1_GeV;
     double const m = mass / 1_GeV;
 
-    std::cout << "CONEXhybrid: removing " << egs_pid << " " << std::scientific << energy
-              << " GeV" << std::endl;
-
-    std::cout << "#### parameters to cegs4_() ####" << std::endl;
-    std::cout << "egs_pid = " << egs_pid << std::endl;
-    std::cout << "E = " << E << std::endl;
-    std::cout << "m = " << m << std::endl;
-    std::cout << "x = " << x << std::endl;
-    std::cout << "y = " << y << std::endl;
-    std::cout << "altitude = " << altitude << std::endl;
-    std::cout << "slantDistance = " << slantDistance << std::endl;
-    std::cout << "lateralX = " << lateralX << std::endl;
-    std::cout << "lateralY = " << lateralY << std::endl;
-    std::cout << "slantX = " << slantX << std::endl;
-    std::cout << "time = " << time << std::endl;
-    std::cout << "u = " << u << std::endl;
-    std::cout << "v = " << v << std::endl;
-    std::cout << "w = " << w << std::endl;
+    CORSIKA_LOG_DEBUG("CONEXhybrid: removing {} {:5e} GeV", egs_pid, energy);
+
+    CORSIKA_LOG_DEBUG("#### parameters to cegs4_() ####");
+    CORSIKA_LOG_DEBUG("egs_pid = {}", egs_pid);
+    CORSIKA_LOG_DEBUG("E = {}", E);
+    CORSIKA_LOG_DEBUG("m = {}", m);
+    CORSIKA_LOG_DEBUG("x = {}", x);
+    CORSIKA_LOG_DEBUG("y = {}", y);
+    CORSIKA_LOG_DEBUG("altitude = {}", altitude);
+    CORSIKA_LOG_DEBUG("slantDistance = {}", slantDistance);
+    CORSIKA_LOG_DEBUG("lateralX = {}", lateralX);
+    CORSIKA_LOG_DEBUG("lateralY = {}", lateralY);
+    CORSIKA_LOG_DEBUG("slantX = {}", slantX);
+    CORSIKA_LOG_DEBUG("time = {}", time);
+    CORSIKA_LOG_DEBUG("u = {}", u);
+    CORSIKA_LOG_DEBUG("v = {}", v);
+    CORSIKA_LOG_DEBUG("w = {}", w);
 
     ::conex::cxoptl_.dptl[10 - 1] = egs_pid;
     ::conex::cxoptl_.dptl[4 - 1] = E;
@@ -229,7 +222,7 @@ namespace corsika {
     return true;
   }
 
-  void CONEXhybrid::solveCE() {
+  inline void CONEXhybrid::solveCE() {
 
     ::conex::conexcascade_();
 
diff --git a/corsika/detail/modules/energy_loss/BetheBlochPDG.inl b/corsika/detail/modules/energy_loss/BetheBlochPDG.inl
index 1f3d0e9303cb60f40fb588ecad70034c634dd1c8..73fbf351707c35c894dd8cf48e0d250589909527 100644
--- a/corsika/detail/modules/energy_loss/BetheBlochPDG.inl
+++ b/corsika/detail/modules/energy_loss/BetheBlochPDG.inl
@@ -26,14 +26,14 @@ namespace corsika {
     return sqrt((Elab - m) * (Elab + m));
   };
 
-  BetheBlochPDG::BetheBlochPDG(ShowerAxis const& shower_axis)
+  inline BetheBlochPDG::BetheBlochPDG(ShowerAxis const& shower_axis)
       : dX_(10_g / square(1_cm)) // profile binning
       , dX_threshold_(0.0001_g / square(1_cm))
       , shower_axis_(shower_axis)
       , profile_(int(shower_axis.getMaximumX() / dX_) + 1) {}
 
-  HEPEnergyType BetheBlochPDG::getBetheBloch(setup::Stack::particle_type const& p,
-                                             GrammageType const dX) {
+  inline HEPEnergyType BetheBlochPDG::getBetheBloch(setup::Stack::particle_type const& p,
+                                                    GrammageType const dX) {
 
     // all these are material constants and have to come through Environment
     // right now: values for nitrogen_D
@@ -64,18 +64,18 @@ namespace corsika {
     double const beta2 = (gamma2 - 1) / gamma2; // 1-1/gamma2    (1-1/gamma)*(1+1/gamma);
                                                 // (gamma_2-1)/gamma_2 = (1-1/gamma2);
     double constexpr c2 = 1;                    // HEP convention here c=c2=1
-    CORSIKA_LOG_DEBUG("BetheBloch beta2={}, gamma2={}", beta2, gamma2);
+    CORSIKA_LOG_TRACE("BetheBloch beta2={}, gamma2={}", beta2, gamma2);
     [[maybe_unused]] double const eta2 = beta2 / (1 - beta2);
     HEPMassType const Wmax =
         2 * me * c2 * beta2 * gamma2 / (1 + 2 * gamma * me / m + me2 / m2);
     // approx, but <<1%    HEPMassType const Wmax = 2*me*c2*beta2*gamma2;      for HEAVY
     // PARTICLES Wmax ~ 2me v2 for non-relativistic particles
-    CORSIKA_LOG_DEBUG("BetheBloch Wmax={}", Wmax);
+    CORSIKA_LOG_TRACE("BetheBloch Wmax={}", Wmax);
 
     // Sternheimer parameterization, density corrections towards high energies
     // NOTE/TODO: when Cbar is 0 it needs to be approximated from parameterization ->
     // MISSING
-    CORSIKA_LOG_DEBUG("BetheBloch p.getMomentum().getNorm()/m{}=",
+    CORSIKA_LOG_TRACE("BetheBloch p.getMomentum().getNorm()/m{}=",
                       p.getMomentum().getNorm() / m);
     double const x = log10(p.getMomentum().getNorm() / m);
     double delta = 0;
@@ -86,7 +86,7 @@ namespace corsika {
     } else if (x < x0) { // and IF conductor (otherwise, this is 0)
       delta = delta0 * pow(100, 2 * (x - x0));
     }
-    CORSIKA_LOG_DEBUG("BetheBloch delta={}", delta);
+    CORSIKA_LOG_TRACE("BetheBloch delta={}", delta);
 
     // with further low energies correction, accurary ~1% down to beta~0.05 (1MeV for p)
 
@@ -126,32 +126,43 @@ namespace corsika {
   }
 
   // radiation losses according to PDG 2018, ch. 33 ref. [5]
-  HEPEnergyType BetheBlochPDG::getRadiationLosses(setup::Stack::particle_type const& vP,
-                                                  GrammageType const vDX) {
+  inline HEPEnergyType BetheBlochPDG::getRadiationLosses(
+      setup::Stack::particle_type const& vP, GrammageType const vDX) {
     // simple-minded hard-coded value for b(E) inspired by data from
     // http://pdg.lbl.gov/2018/AtomicNuclearProperties/ for N and O.
     auto constexpr b = 3.0 * 1e-6 * square(1_cm) / 1_g;
     return -vP.getEnergy() * b * vDX;
   }
 
-  HEPEnergyType BetheBlochPDG::getTotalEnergyLoss(setup::Stack::particle_type const& vP,
-                                                  GrammageType const vDX) {
+  inline HEPEnergyType BetheBlochPDG::getTotalEnergyLoss(
+      setup::Stack::particle_type const& vP, GrammageType const vDX) {
     return getBetheBloch(vP, vDX) + getRadiationLosses(vP, vDX);
   }
 
-  ProcessReturn BetheBlochPDG::doContinuous(setup::Stack::particle_type& p,
-                                            setup::Trajectory const& t) {
+  inline ProcessReturn BetheBlochPDG::doContinuous(setup::Stack::particle_type& p,
+                                                   setup::Trajectory const& t,
+                                                   bool const) {
+
+    // if this step was limiting the CORSIKA stepping, the particle is lost
+    /* see Issue https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika/-/issues/389
+    if (limitStep) {
+      fillProfile(t, p.getEnergy());
+      p.setEnergy(p.getMass());
+      return ProcessReturn::ParticleAbsorbed;
+    }
+    */
+
     if (p.getChargeNumber() == 0) return ProcessReturn::Ok;
 
     GrammageType const dX =
         p.getNode()->getModelProperties().getIntegratedGrammage(t, t.getLength());
-    CORSIKA_LOG_DEBUG("EnergyLoss pid={}, z={}, dX={} g/cm2", p.getPID(),
+    CORSIKA_LOG_TRACE("EnergyLoss pid={}, z={}, dX={} g/cm2", p.getPID(),
                       p.getChargeNumber(), dX / 1_g * square(1_cm));
     HEPEnergyType dE = getTotalEnergyLoss(p, dX);
     auto E = p.getEnergy();
-    const auto Ekin = E - p.getMass();
+    [[maybe_unused]] const auto Ekin = E - p.getMass();
     auto Enew = E + dE;
-    CORSIKA_LOG_DEBUG("EnergyLoss  dE={} MeV, E={} GeV, Ekin={} GeV, Enew={} GeV",
+    CORSIKA_LOG_TRACE("EnergyLoss  dE={} MeV, E={} GeV, Ekin={} GeV, Enew={} GeV",
                       dE / 1_MeV, E / 1_GeV, Ekin / 1_GeV, Enew / 1_GeV);
     p.setEnergy(Enew);
     updateMomentum(p, Enew);
@@ -159,80 +170,87 @@ namespace corsika {
     return ProcessReturn::Ok;
   }
 
-  LengthType BetheBlochPDG::getMaxStepLength(setup::Stack::particle_type const& vParticle,
-                                             setup::Trajectory const& vTrack) const {
+  inline LengthType BetheBlochPDG::getMaxStepLength(
+      setup::Stack::particle_type const& vParticle,
+      setup::Trajectory const& vTrack) const {
     if (vParticle.getChargeNumber() == 0) {
       return meter * std::numeric_limits<double>::infinity();
     }
 
     auto constexpr dX = 1_g / square(1_cm);
-    auto const dEdX = -getTotalEnergyLoss(vParticle, dX) / dX; // dE > 0
-    //~ auto const Ekin = vParticle.getEnergy() - vParticle.getMass();
-
-    auto const emCut = get_energy_threshold(vParticle.getPID());
-    // in any case: never go below 0.99*emCut This needs to be
-    // slightly smaller than emCut since, either this Step is limited
-    // by energy_lim, then the particle is stopped in a very short
-    // range (before doing anythin else) and is then removed
-    // instantly. The exact position where it reaches emCut is not
-    // important, the important fact is that its E_kin is zero
-    // afterwards.
-    //
-    const auto energy = vParticle.getEnergy();
-    auto energy_lim = std::max(0.9 * energy, 0.99 * emCut);
-
-    auto const maxGrammage = (energy - energy_lim) / dEdX;
+    auto const dEdX = -getTotalEnergyLoss(vParticle, dX) / dX;
+    auto const energy = vParticle.getEnergy();
+    auto const energy_lim = std::max(
+        energy * 0.9,                            // either 10% relative loss max., or
+        get_energy_threshold(vParticle.getPID()) // energy thresholds globally defined for
+                                                 // individual particles
+            *
+            0.99 // need to go 1% below global e-cut to assure removal in ParticleCut. The
+                 // 1% does not matter since at cut-time the entire energy is removed.
+    );
+    auto const maxGrammage = (vParticle.getEnergy() - energy_lim) / dEdX;
 
     return vParticle.getNode()->getModelProperties().getArclengthFromGrammage(
         vTrack, maxGrammage);
   }
 
-  void BetheBlochPDG::updateMomentum(corsika::setup::Stack::particle_type& vP,
-                                     HEPEnergyType Enew) {
+  inline void BetheBlochPDG::updateMomentum(corsika::setup::Stack::particle_type& vP,
+                                            HEPEnergyType Enew) {
     HEPMomentumType Pnew = elab2plab(Enew, vP.getMass());
     auto pnew = vP.getMomentum();
     vP.setMomentum(pnew * Pnew / pnew.getNorm());
   }
 
-  void BetheBlochPDG::fillProfile(setup::Trajectory const& vTrack,
-                                  const HEPEnergyType dE) {
+  inline void BetheBlochPDG::fillProfile(setup::Trajectory const& vTrack,
+                                         const HEPEnergyType dE) {
 
-    GrammageType const grammageStart = shower_axis_.getProjectedX(vTrack.getPosition(0));
-    GrammageType const grammageEnd = shower_axis_.getProjectedX(vTrack.getPosition(1));
-    const auto deltaX = grammageEnd - grammageStart;
+    GrammageType grammageStart = shower_axis_.getProjectedX(vTrack.getPosition(0));
+    GrammageType grammageEnd = shower_axis_.getProjectedX(vTrack.getPosition(1));
+
+    if (grammageStart > grammageEnd) { // particle going upstream
+      std::swap(grammageStart, grammageEnd);
+    }
+
+    GrammageType const deltaX = grammageEnd - grammageStart;
 
-    int const binStart = grammageStart / dX_;
-    if (binStart < 0) return;
-    int const binEnd = grammageEnd / dX_;
-    if (binEnd > int(profile_.size() - 1)) return;
     if (deltaX < dX_threshold_) return;
 
-    CORSIKA_LOG_DEBUG("energy deposit of -dE={} between {} and {}", -dE, grammageStart,
+    // only register the range that is covered by the profile
+    int const maxBin = int(profile_.size() - 1);
+    int binStart = grammageStart / dX_;
+    if (binStart < 0) binStart = 0;
+    if (binStart > maxBin) binStart = maxBin;
+    int binEnd = grammageEnd / dX_;
+    if (binEnd < 0) binEnd = 0;
+    if (binEnd > maxBin) binEnd = maxBin;
+
+    CORSIKA_LOG_TRACE("energy deposit of -dE={} between {} and {}", -dE, grammageStart,
                       grammageEnd);
 
     auto energyCount = HEPEnergyType::zero();
 
-    auto fill = [&](const int bin, const double weight) {
-      auto const increment = -dE * weight;
+    auto const factor = -dE / deltaX;
+    auto fill = [&](int const bin, GrammageType const weight) {
+      auto const increment = factor * weight;
       profile_[bin] += increment;
       energyCount += increment;
 
-      CORSIKA_LOG_DEBUG("filling bin {} with weight {} : {} ", bin, weight, increment);
+      CORSIKA_LOG_TRACE("filling bin {} with weight {} : {} ", bin, weight, increment);
     };
 
     // fill longitudinal profile
     if (binStart == binEnd) {
-      fill(binStart, 1);
+      fill(binStart, deltaX);
     } else {
-      fill(binStart, ((1 + binStart) * dX_ - grammageStart) / deltaX);
-      fill(binEnd, (grammageEnd - binEnd * dX_) / deltaX);
-      for (int bin = binStart + 1; bin < binEnd; ++bin) { fill(bin, dX_ / deltaX); }
+      fill(binStart, ((1 + binStart) * dX_ - grammageStart));
+      fill(binEnd, (grammageEnd - binEnd * dX_));
+      for (int bin = binStart + 1; bin < binEnd; ++bin) { fill(bin, dX_); }
     }
 
-    CORSIKA_LOG_DEBUG("total energy added to histogram: {} ", energyCount);
+    CORSIKA_LOG_TRACE("total energy added to histogram: {} ", energyCount);
   }
 
-  void BetheBlochPDG::printProfile() const {
+  inline void BetheBlochPDG::printProfile() const {
     std::ofstream file("EnergyLossProfile.dat");
     file << "# EnergyLoss profile" << std::endl
          << "# lower X bin edge [g/cm2]  dE/dX [GeV/g/cm2]\n";
@@ -244,11 +262,11 @@ namespace corsika {
     file.close();
   }
 
-  HEPEnergyType BetheBlochPDG::getTotal() const {
+  inline HEPEnergyType BetheBlochPDG::getTotal() const {
     return std::accumulate(profile_.cbegin(), profile_.cend(), HEPEnergyType::zero());
   }
 
-  void BetheBlochPDG::showResults() const {
+  inline void BetheBlochPDG::showResults() const {
     CORSIKA_LOG_INFO(
         " ******************************\n"
         " PROCESS::ContinuousProcess: \n"
@@ -256,6 +274,6 @@ namespace corsika {
         energy_lost_ / 1_GeV);
   }
 
-  void BetheBlochPDG::reset() { energy_lost_ = 0_GeV; }
+  inline void BetheBlochPDG::reset() { energy_lost_ = 0_GeV; }
 
 } // namespace corsika
diff --git a/corsika/detail/modules/proposal/ContinuousProcess.inl b/corsika/detail/modules/proposal/ContinuousProcess.inl
index 8bfbd705b8bb0dfc19d1e7efe79252aa5a6a69be..0e230a762aae1ca039e06570ea0bb2f87fa54a50 100644
--- a/corsika/detail/modules/proposal/ContinuousProcess.inl
+++ b/corsika/detail/modules/proposal/ContinuousProcess.inl
@@ -23,7 +23,8 @@
 
 namespace corsika::proposal {
 
-  void ContinuousProcess::buildCalculator(Code code, NuclearComposition const& comp) {
+  inline void ContinuousProcess::buildCalculator(Code code,
+                                                 NuclearComposition const& comp) {
     // search crosssection builder for given particle
     auto p_cross = cross.find(code);
     if (p_cross == cross.end())
@@ -47,13 +48,13 @@ namespace corsika::proposal {
   }
 
   template <>
-  ContinuousProcess::ContinuousProcess(setup::Environment const& _env)
+  inline ContinuousProcess::ContinuousProcess(setup::Environment const& _env)
       : ProposalProcessBase(_env) {}
 
   template <>
-  void ContinuousProcess::scatter(setup::Stack::particle_type& vP,
-                                  HEPEnergyType const& loss,
-                                  GrammageType const& grammage) {
+  inline void ContinuousProcess::scatter(setup::Stack::particle_type& vP,
+                                         HEPEnergyType const& loss,
+                                         GrammageType const& grammage) {
 
     // get or build corresponding calculators
     auto c = getCalculator(vP, calc);
@@ -87,8 +88,9 @@ namespace corsika::proposal {
   }
 
   template <>
-  ProcessReturn ContinuousProcess::doContinuous(setup::Stack::particle_type& vP,
-                                                setup::Trajectory const& vT) {
+  inline ProcessReturn ContinuousProcess::doContinuous(setup::Stack::particle_type& vP,
+                                                       setup::Trajectory const& vT,
+                                                       bool const) {
 
     if (!canInteract(vP.getPID())) return ProcessReturn::Ok;
     if (vT.getLength() == 0_m) return ProcessReturn::Ok;
@@ -114,31 +116,28 @@ namespace corsika::proposal {
   }
 
   template <>
-  LengthType ContinuousProcess::getMaxStepLength(setup::Stack::particle_type const& vP,
-                                                 setup::Trajectory const& vT) {
+  inline LengthType ContinuousProcess::getMaxStepLength(
+      setup::Stack::particle_type const& vP, setup::Trajectory const& vT) {
     auto const code = vP.getPID();
     if (!canInteract(code)) return meter * std::numeric_limits<double>::infinity();
 
     // Limit the step size of a conitnuous loss. The maximal continuous loss seems to be a
     // hyper parameter which must be adjusted.
     //
-    auto const emCut = get_energy_threshold(
-        code); //! energy thresholds globally defined for individual particles
-
-    // in any case: never go below 0.99*emCut This needs to be
-    // slightly smaller than emCut since, either this Step is limited
-    // by energy_lim, then the particle is stopped in a very short
-    // range (before doing anythin else) and is then removed
-    // instantly. The exact position where it reaches emCut is not
-    // important, the important fact is that its E_kin is zero
-    // afterwards.
-    //
-    auto energy_lim = std::max(0.9 * vP.getEnergy(), 0.99 * emCut);
+    auto const energy = vP.getEnergy();
+    auto const energy_lim = std::max(
+        energy * 0.9, // either 10% relative loss max., or
+        get_energy_threshold(
+            code) // energy thresholds globally defined for individual particles
+            *
+            0.99 // need to go 1% below global e-cut to assure removal in ParticleCut. The
+                 // 1% does not matter since at cut-time the entire energy is removed.
+    );
 
     // solving the track integral for giving energy lim
     auto c = getCalculator(vP, calc);
     auto grammage = get<eDISPLACEMENT>(c->second)->SolveTrackIntegral(
-                        vP.getEnergy() / 1_MeV, energy_lim / 1_MeV) *
+                        energy / 1_MeV, energy_lim / 1_MeV) *
                     1_g / square(1_cm);
 
     // return it in distance aequivalent
@@ -148,12 +147,14 @@ namespace corsika::proposal {
     return dist;
   }
 
-  void ContinuousProcess::showResults() const {
-    std::cout << " ******************************" << std::endl
-              << " PROCESS::ContinuousProcess: " << std::endl;
-    std::cout << " energy lost dE (GeV)      :  " << energy_lost_ / 1_GeV << std::endl;
+  inline void ContinuousProcess::showResults() const {
+    CORSIKA_LOG_DEBUG(
+        " ******************************\n"
+        " PROCESS::ContinuousProcess: \n"
+        " energy lost dE (GeV)      :  {}",
+        energy_lost_ / 1_GeV);
   }
 
-  void ContinuousProcess::reset() { energy_lost_ = 0_GeV; }
+  inline void ContinuousProcess::reset() { energy_lost_ = 0_GeV; }
 
 } // namespace corsika::proposal
diff --git a/corsika/detail/modules/proposal/Interaction.inl b/corsika/detail/modules/proposal/Interaction.inl
index 4874bda1f7fb7f6ca89d898cf7f1e01e8a51b791..fb6c157da082f6bd9865521bbf34642a8fa245b8 100644
--- a/corsika/detail/modules/proposal/Interaction.inl
+++ b/corsika/detail/modules/proposal/Interaction.inl
@@ -24,10 +24,10 @@
 namespace corsika::proposal {
 
   template <>
-  Interaction::Interaction(setup::Environment const& _env)
+  inline Interaction::Interaction(setup::Environment const& _env)
       : ProposalProcessBase(_env) {}
 
-  void Interaction::buildCalculator(Code code, NuclearComposition const& comp) {
+  inline void Interaction::buildCalculator(Code code, NuclearComposition const& comp) {
     // search crosssection builder for given particle
     auto p_cross = cross.find(code);
     if (p_cross == cross.end())
@@ -52,7 +52,7 @@ namespace corsika::proposal {
   }
 
   template <>
-  ProcessReturn Interaction::doInteraction(setup::StackView& view) {
+  inline ProcessReturn Interaction::doInteraction(setup::StackView& view) {
 
     auto const projectile = view.getProjectile();
 
@@ -103,7 +103,7 @@ namespace corsika::proposal {
   }
 
   template <>
-  GrammageType Interaction::getInteractionLength(
+  inline GrammageType Interaction::getInteractionLength(
       setup::Stack::particle_type const& projectile) {
 
     if (canInteract(projectile.getPID())) {
diff --git a/corsika/detail/modules/proposal/ProposalProcessBase.inl b/corsika/detail/modules/proposal/ProposalProcessBase.inl
index 21d0cce576f306055d745a8d50a6a2baa3f21466..992d24e4d5feefc081ca658e6fd270c534ee5d25 100644
--- a/corsika/detail/modules/proposal/ProposalProcessBase.inl
+++ b/corsika/detail/modules/proposal/ProposalProcessBase.inl
@@ -25,12 +25,12 @@
 
 namespace corsika::proposal {
 
-  bool ProposalProcessBase::canInteract(Code pcode) const {
+  inline bool ProposalProcessBase::canInteract(Code pcode) const {
     if (std::find(begin(tracked), end(tracked), pcode) != end(tracked)) return true;
     return false;
   }
 
-  ProposalProcessBase::ProposalProcessBase(setup::Environment const& _env)
+  inline ProposalProcessBase::ProposalProcessBase(setup::Environment const& _env)
       : RNG_(RNGManager::getInstance().getRandomStream("proposal")) {
     _env.getUniverse()->walk([&](auto& vtn) {
       if (vtn.hasModelProperties()) {
@@ -71,7 +71,8 @@ namespace corsika::proposal {
     }
   }
 
-  size_t ProposalProcessBase::hash::operator()(const calc_key_t& p) const noexcept {
+  inline size_t ProposalProcessBase::hash::operator()(const calc_key_t& p) const
+      noexcept {
     return p.first ^ std::hash<Code>{}(p.second);
   }
 
diff --git a/corsika/detail/modules/pythia8/Decay.inl b/corsika/detail/modules/pythia8/Decay.inl
index 662afa95ae2ff806122a2a47c8387d36f4ee4fe0..d58ac21d851ff8b19be258e09e9a63a500784b1a 100644
--- a/corsika/detail/modules/pythia8/Decay.inl
+++ b/corsika/detail/modules/pythia8/Decay.inl
@@ -17,42 +17,34 @@
 
 namespace corsika::pythia8 {
 
-  Decay::Decay(const bool print_listing)
-      : print_listing_(print_listing) {
+  inline Decay::Decay(bool const print_listing)
+      : Pythia8::Pythia(CORSIKA_Pythia8_XML_DIR)
+      , print_listing_(print_listing) {
     init();
   }
 
-  Decay::Decay(std::set<Code> const& those)
+  inline Decay::Decay(std::set<Code> const& those)
       : handleAllDecays_(false)
       , handledDecays_(those) {
     init();
   }
 
-  Decay::~Decay() { CORSIKA_LOG_INFO("Pythia::Decay n={}", count_); }
+  inline Decay::~Decay() { CORSIKA_LOG_INFO("Pythia::Decay n={}", count_); }
 
-  void Decay::init() {
+  inline void Decay::init() {
 
     // run this only once during construction
 
-    // set random number generator in pythia
+    // link random number generator in pythia to CORSIKA8
     Pythia8::RndmEngine* rndm = new corsika::pythia8::Random();
-    pythia_.setRndmEnginePtr(rndm);
+    Pythia8::Pythia::setRndmEnginePtr(rndm);
 
-    /*
-       issue xyz: definition of particles and decay channels use the same mechanism in
-       corsika and pythia we should force pythia to use the file in corsika.
-     */
-    // bool ParticleData::reInit(string startFile, bool xmlFormat = true)
-    // read in particle data from Corsika 8
-    // pythia_.particleData.reInit("/home/felix/ngcorsika/corsika-build/include/corsika/particles/ParticleData.xml");
-    // pythia_.particleData.checkTable();
-
-    pythia_.readString("Next:numberShowInfo = 0");
-    pythia_.readString("Next:numberShowProcess = 0");
-    pythia_.readString("Next:numberShowEvent = 0");
+    Pythia8::Pythia::readString("Next:numberShowInfo = 0");
+    Pythia8::Pythia::readString("Next:numberShowProcess = 0");
+    Pythia8::Pythia::readString("Next:numberShowEvent = 0");
 
-    pythia_.readString("Print:quiet = on");
-    pythia_.readString("Check:particleData = 0");
+    Pythia8::Pythia::readString("Print:quiet = on");
+    Pythia8::Pythia::readString("Check:particleData = off");
 
     /*
        switching off event check in pythia is needed to allow decays that are off-shell
@@ -60,21 +52,21 @@ namespace corsika::pythia8 {
        the consistency of particle masses between event generators is an unsolved issues
     */
     CORSIKA_LOG_INFO("Pythia::Init: switching off event checking in pythia..");
-    pythia_.readString("Check:event = 1");
+    Pythia8::Pythia::readString("Check:event = 1");
 
-    pythia_.readString("ProcessLevel:all = off");
-    pythia_.readString("ProcessLevel:resonanceDecays = off");
+    Pythia8::Pythia::readString("ProcessLevel:all = off");
+    Pythia8::Pythia::readString("ProcessLevel:resonanceDecays = off");
 
     // making sure
     setStable(Code::Pi0);
 
-    //    pythia_.particleData.readString("59:m0 = 101.00");
+    //    Pythia8::Pythia::particleData.readString("59:m0 = 101.00");
 
-    if (!pythia_.init())
+    if (!Pythia8::Pythia::init())
       throw std::runtime_error("Pythia::Decay: Initialization failed!");
   }
 
-  bool Decay::canHandleDecay(Code const vParticleCode) {
+  inline bool Decay::canHandleDecay(Code const vParticleCode) {
     // if known to pythia and not proton, electron or neutrino it can decay
     if (vParticleCode == Code::Proton || vParticleCode == Code::AntiProton ||
         vParticleCode == Code::NuE || vParticleCode == Code::NuMu ||
@@ -88,58 +80,59 @@ namespace corsika::pythia8 {
       return false;
   }
 
-  void Decay::setHandleDecay(Code const vParticleCode) {
+  inline void Decay::setHandleDecay(Code const vParticleCode) {
     handleAllDecays_ = false;
-    CORSIKA_LOG_INFO("Pythia::Decay: set to handle decay of {} ", vParticleCode);
+    CORSIKA_LOG_DEBUG("Pythia::Decay: set to handle decay of {} ", vParticleCode);
     if (Decay::canHandleDecay(vParticleCode))
       handledDecays_.insert(vParticleCode);
     else
       throw std::runtime_error("this decay can not be handled by pythia!");
   }
 
-  void Decay::setHandleDecay(std::vector<Code> const& vParticleList) {
+  inline void Decay::setHandleDecay(std::vector<Code> const& vParticleList) {
     handleAllDecays_ = false;
     for (auto p : vParticleList) setHandleDecay(p);
   }
 
-  bool Decay::isDecayHandled(Code const vParticleCode) {
+  inline bool Decay::isDecayHandled(Code const vParticleCode) {
     if (handleAllDecays_ && canHandleDecay(vParticleCode))
       return true;
     else
       return handledDecays_.find(vParticleCode) != Decay::handledDecays_.end();
   }
 
-  void Decay::setStable(std::vector<Code> const& particleList) {
+  inline void Decay::setStable(std::vector<Code> const& particleList) {
     for (auto p : particleList) Decay::setStable(p);
   }
 
-  void Decay::setUnstable(Code const pCode) {
-    CORSIKA_LOG_INFO("Pythia::Decay: setting {} unstable..", pCode);
-    pythia_.particleData.mayDecay(static_cast<int>(get_PDG(pCode)), true);
+  inline void Decay::setUnstable(Code const pCode) {
+    CORSIKA_LOG_DEBUG("Pythia::Decay: setting {} unstable..", pCode);
+    Pythia8::Pythia::particleData.mayDecay(static_cast<int>(get_PDG(pCode)), true);
   }
 
-  void Decay::setStable(Code const pCode) {
-    CORSIKA_LOG_INFO("Pythia::Decay: setting {} stable..", pCode);
-    pythia_.particleData.mayDecay(static_cast<int>(get_PDG(pCode)), false);
+  inline void Decay::setStable(Code const pCode) {
+    CORSIKA_LOG_DEBUG("Pythia::Decay: setting {} stable..", pCode);
+    Pythia8::Pythia::particleData.mayDecay(static_cast<int>(get_PDG(pCode)), false);
   }
 
-  bool Decay::isStable(Code const vCode) {
-    return pythia_.particleData.canDecay(static_cast<int>(get_PDG(vCode)));
+  inline bool Decay::isStable(Code const vCode) {
+    return Pythia8::Pythia::particleData.canDecay(static_cast<int>(get_PDG(vCode)));
   }
 
-  bool Decay::canDecay(Code const pCode) {
-    const bool ans = pythia_.particleData.canDecay(static_cast<int>(get_PDG(pCode)));
-    CORSIKA_LOG_INFO("Pythia::Decay: checking if particle: {} can decay in PYTHIA? {} ",
-                     pCode, ans);
+  inline bool Decay::canDecay(Code const pCode) {
+    bool const ans =
+        Pythia8::Pythia::particleData.canDecay(static_cast<int>(get_PDG(pCode)));
+    CORSIKA_LOG_DEBUG("Pythia::Decay: checking if particle: {} can decay in PYTHIA? {} ",
+                      pCode, ans);
     return ans;
   }
 
-  void Decay::printDecayConfig(const Code vCode) {
+  inline void Decay::printDecayConfig(Code const vCode) {
     CORSIKA_LOG_INFO("Decay: Pythia decay configuration:");
     CORSIKA_LOG_INFO(" {} is {} ", vCode, (isStable(vCode) ? "stable" : "unstable"));
   }
 
-  void Decay::printDecayConfig() {
+  inline void Decay::printDecayConfig() {
     CORSIKA_LOG_INFO("Pythia::Decay: decay configuration:");
     if (handleAllDecays_)
       CORSIKA_LOG_INFO(" all particles known to Pythia are handled by Pythia::Decay!");
@@ -149,24 +142,24 @@ namespace corsika::pythia8 {
   }
 
   template <typename TParticle>
-  TimeType Decay::getLifetime(TParticle const& particle) {
+  inline TimeType Decay::getLifetime(TParticle const& particle) {
 
-    const auto pid = particle.getPID();
+    auto const pid = particle.getPID();
     if (canDecay(pid)) {
       HEPEnergyType E = particle.getEnergy();
       HEPMassType m = particle.getMass();
 
-      const double gamma = E / m;
+      double const gamma = E / m;
 
-      const TimeType t0 = get_lifetime(pid);
+      TimeType const t0 = get_lifetime(pid);
       auto const lifetime = gamma * t0;
-      CORSIKA_LOG_INFO("Pythia::Decay: code: {}", particle.getPID());
-      CORSIKA_LOG_INFO("Pythia::Decay: MinStep: t0: {}", t0);
-      CORSIKA_LOG_INFO("Pythia::Decay: MinStep: energy: {} GeV", E / 1_GeV);
-      CORSIKA_LOG_INFO("Pythia::Decay: momentum: {} GeV",
-                       particle.getMomentum().getComponents() / 1_GeV);
-      CORSIKA_LOG_INFO("Pythia::Decay: MinStep: gamma: {}", gamma);
-      CORSIKA_LOG_INFO("Pythia::Decay: MinStep: tau: {} ", lifetime);
+      CORSIKA_LOG_TRACE("Pythia::Decay: code: {}", particle.getPID());
+      CORSIKA_LOG_TRACE("Pythia::Decay: MinStep: t0: {}", t0);
+      CORSIKA_LOG_TRACE("Pythia::Decay: MinStep: energy: {} GeV", E / 1_GeV);
+      CORSIKA_LOG_TRACE("Pythia::Decay: momentum: {} GeV",
+                        particle.getMomentum().getComponents() / 1_GeV);
+      CORSIKA_LOG_TRACE("Pythia::Decay: MinStep: gamma: {}", gamma);
+      CORSIKA_LOG_TRACE("Pythia::Decay: MinStep: tau: {} ", lifetime);
 
       return lifetime;
     } else
@@ -174,7 +167,7 @@ namespace corsika::pythia8 {
   }
 
   template <typename TView>
-  void Decay::doDecay(TView& view) {
+  inline void Decay::doDecay(TView& view) {
 
     auto projectile = view.getProjectile();
 
@@ -193,7 +186,7 @@ namespace corsika::pythia8 {
     count_++;
 
     // pythia stack
-    Pythia8::Event& event = pythia_.event;
+    Pythia8::Event& event = Pythia8::Pythia::event;
     event.reset();
 
     auto const particleId = projectile.getPID();
@@ -213,10 +206,10 @@ namespace corsika::pythia8 {
     // add particle to pythia stack
     event.append(pdgCode, 1, 0, 0, px, py, pz, en, m);
 
-    if (!pythia_.next())
+    if (!Pythia8::Pythia::next())
       throw std::runtime_error("Pythia::Decay: decay failed!");
     else
-      CORSIKA_LOG_INFO("Pythia::Decay: particles after decay: {} ", event.size());
+      CORSIKA_LOG_DEBUG("Pythia::Decay: particles after decay: {} ", event.size());
 
     if (print_listing_) {
       // list final state
@@ -234,9 +227,10 @@ namespace corsika::pythia8 {
         FourVector const fourMomRest{Erest, pRest};
         auto const fourMomLab = boost.fromCoM(fourMomRest);
 
-        CORSIKA_LOG_INFO("particle: id={} momentum={} energy={} ", pyId,
-                         fourMomLab.getSpaceLikeComponents().getComponents(labCS) / 1_GeV,
-                         fourMomLab.getTimeLikeComponent());
+        CORSIKA_LOG_TRACE(
+            "particle: id={} momentum={} energy={} ", pyId,
+            fourMomLab.getSpaceLikeComponents().getComponents(labCS) / 1_GeV,
+            fourMomLab.getTimeLikeComponent());
 
         view.addSecondary(std::make_tuple(pyId, fourMomLab.getTimeLikeComponent(),
                                           fourMomLab.getSpaceLikeComponents(), decayPoint,
diff --git a/corsika/detail/modules/pythia8/Interaction.inl b/corsika/detail/modules/pythia8/Interaction.inl
index 61bee857ca971c7895ca8bfa182df5c3fb82871d..4b74ed67f44a9084bfa951c39c98116b5a2945e1 100644
--- a/corsika/detail/modules/pythia8/Interaction.inl
+++ b/corsika/detail/modules/pythia8/Interaction.inl
@@ -21,65 +21,68 @@
 
 namespace corsika::pythia8 {
 
-  Interaction::~Interaction() {
-    std::cout << "Pythia::Interaction n=" << count_ << std::endl;
+  inline Interaction::~Interaction() {
+    CORSIKA_LOG_INFO("Pythia::Interaction n= {}", count_);
   }
 
-  Interaction::Interaction(const bool print_listing)
-      : print_listing_(print_listing) {
+  inline Interaction::Interaction(bool const print_listing)
+      : Pythia8::Pythia(CORSIKA_Pythia8_XML_DIR)
+      , print_listing_(print_listing) {
 
-    std::cout << "Pythia::Interaction n=" << count_ << std::endl;
+    CORSIKA_LOG_INFO("Configuring Pythia8 from: {}", CORSIKA_Pythia8_XML_DIR);
 
     // initialize Pythia
-    if (!initialized_) {
-
-      pythia_.readString("Print:quiet = off");
-      pythia_.readString("Check:particleData = on");      // during init
-      pythia_.readString("Check:event = on");             // default: on
-      pythia_.readString("Check:levelParticleData = 12"); // 1 is default
-      // TODO: proper process initialization for MinBias needed
-      pythia_.readString("HardQCD:all = on");
-      pythia_.readString("ProcessLevel:resonanceDecays = off");
-
-      pythia_.init();
-
-      // any decays in pythia? if yes need to define which particles
-      if (internalDecays_) {
-        // define which particles are passed to corsika, i.e. which particles make it into
-        // history even very shortlived particles like charm or pi0 are of interest here
-        const std::vector<Code> HadronsWeWantTrackedByCorsika = {
-            Code::PiPlus,     Code::PiMinus, Code::Pi0,     Code::KMinus,
-            Code::KPlus,      Code::K0Long,  Code::K0Short, Code::SigmaPlus,
-            Code::SigmaMinus, Code::Lambda0, Code::Xi0,     Code::XiMinus,
-            Code::OmegaMinus, Code::DPlus,   Code::DMinus,  Code::D0,
-            Code::D0Bar};
-
-        Interaction::setStable(HadronsWeWantTrackedByCorsika);
-      }
-
-      // basic initialization of cross section routines
-      sigma_.init(&pythia_.info, pythia_.settings, &pythia_.particleData, &pythia_.rndm);
 
-      initialized_ = true;
+    // reduce output from pythia if set to "on"
+    Pythia8::Pythia::readString("Print:quiet = on");
+    // check if data in particle data file is minimally consistent. Very verbose! set to
+    // "off"! we do not change the basic file provided by pythia.
+    Pythia8::Pythia::readString("Check:particleData = off");
+    Pythia8::Pythia::readString("Check:event = on");             // default: on
+    Pythia8::Pythia::readString("Check:levelParticleData = 12"); // 1 is default
+    /** \TODO: proper process initialization for MinBias needed, see
+        also Issue https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika/-/issues/369 **/
+    Pythia8::Pythia::readString("HardQCD:all = on");
+    Pythia8::Pythia::readString("ProcessLevel:resonanceDecays = off");
+
+    if (!Pythia8::Pythia::init())
+      throw std::runtime_error("Pythia::Interaction: Initialization failed!");
+
+    // any decays in pythia? if yes need to define which particles
+    if (internalDecays_) {
+      // define which particles are passed to corsika, i.e. which particles make it into
+      // history even very shortlived particles like charm or pi0 are of interest here
+      std::vector<Code> const HadronsWeWantTrackedByCorsika = {
+          Code::PiPlus, Code::PiMinus, Code::Pi0,        Code::KMinus,     Code::KPlus,
+          Code::K0Long, Code::K0Short, Code::SigmaPlus,  Code::SigmaMinus, Code::Lambda0,
+          Code::Xi0,    Code::XiMinus, Code::OmegaMinus, Code::DPlus,      Code::DMinus,
+          Code::D0,     Code::D0Bar};
+
+      Interaction::setStable(HadronsWeWantTrackedByCorsika);
     }
+
+    // basic initialization of cross section routines
+    sigma_.init(&(Pythia8::Pythia::info), Pythia8::Pythia::settings,
+                &(Pythia8::Pythia::particleData), &(Pythia8::Pythia::rndm));
   }
 
-  void Interaction::setStable(std::vector<Code> const& particleList) {
+  inline void Interaction::setStable(std::vector<Code> const& particleList) {
     for (auto p : particleList) Interaction::setStable(p);
   }
 
-  void Interaction::setUnstable(Code const pCode) {
-    std::cout << "Pythia::Interaction: setting " << pCode << " unstable.." << std::endl;
-    pythia_.particleData.mayDecay(static_cast<int>(get_PDG(pCode)), true);
+  inline void Interaction::setUnstable(Code const pCode) {
+    CORSIKA_LOG_DEBUG("Pythia::Interaction: setting {} unstable..", pCode);
+    Pythia8::Pythia::particleData.mayDecay(static_cast<int>(get_PDG(pCode)), true);
   }
 
-  void Interaction::setStable(Code const pCode) {
-    std::cout << "Pythia::Interaction: setting " << pCode << " stable.." << std::endl;
-    pythia_.particleData.mayDecay(static_cast<int>(get_PDG(pCode)), false);
+  inline void Interaction::setStable(Code const pCode) {
+    CORSIKA_LOG_DEBUG("Pythia::Interaction: setting {} stable..", pCode);
+    Pythia8::Pythia::particleData.mayDecay(static_cast<int>(get_PDG(pCode)), false);
   }
 
-  void Interaction::configureLabFrameCollision(const Code BeamId, const Code TargetId,
-                                               const HEPEnergyType BeamEnergy) {
+  inline void Interaction::configureLabFrameCollision(Code const BeamId,
+                                                      Code const TargetId,
+                                                      HEPEnergyType const BeamEnergy) {
     // Pythia configuration of the current event
     // very clumsy. I am sure this can be done better..
 
@@ -88,47 +91,49 @@ namespace corsika::pythia8 {
     auto const pdgBeam = static_cast<int>(get_PDG(BeamId));
     std::stringstream stBeam;
     stBeam << "Beams:idA = " << pdgBeam;
-    pythia_.readString(stBeam.str());
+    Pythia8::Pythia::readString(stBeam.str());
     // set target
     auto pdgTarget = static_cast<int>(get_PDG(TargetId));
     // replace hydrogen with proton, otherwise pythia goes into heavy ion mode!
     if (TargetId == Code::Hydrogen) pdgTarget = static_cast<int>(get_PDG(Code::Proton));
     std::stringstream stTarget;
     stTarget << "Beams:idB = " << pdgTarget;
-    pythia_.readString(stTarget.str());
+    Pythia8::Pythia::readString(stTarget.str());
     // set frame to lab. frame
-    pythia_.readString("Beams:frameType = 2");
+    Pythia8::Pythia::readString("Beams:frameType = 2");
     // set beam energy
-    const double Elab = BeamEnergy / 1_GeV;
+    double const Elab = BeamEnergy / 1_GeV;
     std::stringstream stEnergy;
     stEnergy << "Beams:eA = " << Elab;
-    pythia_.readString(stEnergy.str());
+    Pythia8::Pythia::readString(stEnergy.str());
     // target at rest
-    pythia_.readString("Beams:eB = 0.");
+    Pythia8::Pythia::readString("Beams:eB = 0.");
     // initialize this config
-    pythia_.init();
+
+    if (!Pythia8::Pythia::init())
+      throw std::runtime_error("Pythia::Interaction: Initialization failed!");
   }
 
-  bool Interaction::canInteract(Code const pCode) {
+  inline bool Interaction::canInteract(Code const pCode) {
     return pCode == Code::Proton || pCode == Code::Neutron || pCode == Code::AntiProton ||
            pCode == Code::AntiNeutron || pCode == Code::PiMinus || pCode == Code::PiPlus;
   }
 
-  std::tuple<CrossSectionType, CrossSectionType> Interaction::getCrossSection(
-      const Code BeamId, const Code TargetId, const HEPEnergyType CoMenergy) {
+  inline std::tuple<CrossSectionType, CrossSectionType> Interaction::getCrossSection(
+      Code const BeamId, Code const TargetId, HEPEnergyType const CoMenergy) {
     // interaction possible in pythia?
     if (TargetId == Code::Proton || TargetId == Code::Hydrogen) {
       if (canInteract(BeamId) && isValidCoMEnergy(CoMenergy)) {
         // input particle PDG
         auto const pdgCodeBeam = static_cast<int>(get_PDG(BeamId));
         auto const pdgCodeTarget = static_cast<int>(get_PDG(TargetId));
-        const double ecm = CoMenergy / 1_GeV;
+        double const ecm = CoMenergy / 1_GeV;
 
         // calculate cross section
         sigma_.calc(pdgCodeBeam, pdgCodeTarget, ecm);
         if (sigma_.hasSigmaTot()) {
-          const double sigEla = sigma_.sigmaEl();
-          const double sigProd = sigma_.sigmaTot() - sigEla;
+          double const sigEla = sigma_.sigmaEl();
+          double const sigProd = sigma_.sigmaTot() - sigEla;
 
           return std::make_tuple(sigProd * (1_fm * 1_fm), sigEla * (1_fm * 1_fm));
 
@@ -144,8 +149,7 @@ namespace corsika::pythia8 {
     }
   }
 
-  //  template <>
-  GrammageType Interaction::getInteractionLength(
+  inline GrammageType Interaction::getInteractionLength(
       corsika::setup::Stack::particle_type const& particle) {
 
     // coordinate system, get global frame of reference
@@ -168,13 +172,15 @@ namespace corsika::pythia8 {
     pTotLab += pTarget;
     auto const pTotLabNorm = pTotLab.getNorm();
     // calculate cm. energy
-    const HEPEnergyType ECoM = sqrt(
+    HEPEnergyType const ECoM = sqrt(
         (Elab + pTotLabNorm) * (Elab - pTotLabNorm)); // binomial for numerical accuracy
 
-    std::cout << "Interaction: LambdaInt: \n"
-              << " input energy: " << particle.getEnergy() / 1_GeV << std::endl
-              << " beam can interact:" << kInteraction << std::endl
-              << " beam pid:" << particle.getPID() << std::endl;
+    CORSIKA_LOG_DEBUG(
+        "Interaction: LambdaInt: \n"
+        " input energy: {} GeV"
+        " beam can interact: {}"
+        " beam pid: {}",
+        particle.getEnergy() / 1_GeV, kInteraction, particle.getPID());
 
     // TODO: move limits into variables
     if (kInteraction && Elab >= 8.5_GeV && isValidCoMEnergy(ECoM)) {
@@ -185,8 +191,8 @@ namespace corsika::pythia8 {
         ideally as full particle object so that the four momenta
         and the boosts can be defined..
       */
-      const auto* currentNode = particle.getNode();
-      const auto mediumComposition =
+      auto const* currentNode = particle.getNode();
+      auto const mediumComposition =
           currentNode->getModelProperties().getNuclearComposition();
       // determine average interaction length
 
@@ -195,17 +201,16 @@ namespace corsika::pythia8 {
             return std::get<0>(this->getCrossSection(corsikaBeamId, vTargetID, ECoM));
           });
 
-      std::cout << "Interaction: IntLength: weighted CrossSection (mb): "
-                << weightedProdCrossSection / 1_mb << std::endl
-                << "Interaction: IntLength: average mass number: "
-                << mediumComposition.getAverageMassNumber() << std::endl;
+      CORSIKA_LOG_DEBUG(
+          "Interaction: IntLength: weighted CrossSection (mb): {} "
+          "Interaction: IntLength: average mass number: {} ",
+          weightedProdCrossSection / 1_mb, mediumComposition.getAverageMassNumber());
 
       // calculate interaction length in medium
       GrammageType const int_length = mediumComposition.getAverageMassNumber() *
                                       constants::u / weightedProdCrossSection;
-      std::cout << "Interaction: "
-                << "interaction length (g/cm2): " << int_length / (0.001_kg) * 1_cm * 1_cm
-                << std::endl;
+      CORSIKA_LOG_DEBUG("Interaction: interaction length (g/cm2): {} ",
+                        int_length / (0.001_kg) * 1_cm * 1_cm);
 
       return int_length;
     }
@@ -213,20 +218,16 @@ namespace corsika::pythia8 {
     return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
   }
 
-  /**
-     In this function PYTHIA is called to produce one event. The
-     event is copied (and boosted) into the shower lab frame.
-   */
-
   template <class TView>
-  void Interaction::doInteraction(TView& view) {
+  inline void Interaction::doInteraction(TView& view) {
 
     auto projectile = view.getProjectile();
 
     const auto corsikaBeamId = projectile.getPID();
-    std::cout << "Pythia::Interaction: "
-              << "DoInteraction: " << corsikaBeamId << " interaction? "
-              << corsika::pythia8::Interaction::canInteract(corsikaBeamId) << std::endl;
+    CORSIKA_LOG_DEBUG(
+        "Pythia::Interaction: "
+        "DoInteraction: {} interaction? ",
+        corsikaBeamId, corsika::pythia8::Interaction::canInteract(corsikaBeamId));
 
     if (is_nucleus(corsikaBeamId)) {
       // nuclei handled by different process, this should not happen
@@ -246,18 +247,21 @@ namespace corsika::pythia8 {
 
       // define target
       // FOR NOW: target is always at rest
-      const auto eTargetLab = 0_GeV + constants::nucleonMass;
-      const auto pTargetLab = MomentumVector(labCS, 0_GeV, 0_GeV, 0_GeV);
-      const FourVector PtargLab(eTargetLab, pTargetLab);
+      auto const eTargetLab = 0_GeV + constants::nucleonMass;
+      auto const pTargetLab = MomentumVector(labCS, 0_GeV, 0_GeV, 0_GeV);
+      FourVector const PtargLab(eTargetLab, pTargetLab);
+
+      CORSIKA_LOG_DEBUG(
+          "Interaction: ebeam lab: {} GeV"
+          "Interaction: pbeam lab: {} GeV",
+          eProjectileLab / 1_GeV, pProjectileLab.getComponents() / 1_GeV);
 
-      std::cout << "Interaction: ebeam lab: " << eProjectileLab / 1_GeV << std::endl
-                << "Interaction: pbeam lab: " << pProjectileLab.getComponents() / 1_GeV
-                << std::endl;
-      std::cout << "Interaction: etarget lab: " << eTargetLab / 1_GeV << std::endl
-                << "Interaction: ptarget lab: " << pTargetLab.getComponents() / 1_GeV
-                << std::endl;
+      CORSIKA_LOG_DEBUG(
+          "Interaction: etarget lab: {} GeV"
+          "Interaction: ptarget lab: {} GeV ",
+          eTargetLab / 1_GeV, pTargetLab.getComponents() / 1_GeV);
 
-      const FourVector PprojLab(eProjectileLab, pProjectileLab);
+      FourVector const PprojLab(eProjectileLab, pProjectileLab);
 
       // define target kinematics in lab frame
       // define boost to and from CoM frame
@@ -271,18 +275,20 @@ namespace corsika::pythia8 {
       // boost target
       auto const PtargCoM = boost.toCoM(PtargLab);
 
-      std::cout << "Interaction: ebeam CoM: " << PprojCoM.getTimeLikeComponent() / 1_GeV
-                << std::endl
-                << "Interaction: pbeam CoM: "
-                << PprojCoM.getSpaceLikeComponents().getComponents() / 1_GeV << std::endl;
-      std::cout << "Interaction: etarget CoM: " << PtargCoM.getTimeLikeComponent() / 1_GeV
-                << std::endl
-                << "Interaction: ptarget CoM: "
-                << PtargCoM.getSpaceLikeComponents().getComponents() / 1_GeV << std::endl;
+      CORSIKA_LOG_DEBUG(
+          "Interaction: ebeam CoM: {} GeV"
+          "Interaction: pbeam CoM: {} GeV",
+          PprojCoM.getTimeLikeComponent() / 1_GeV,
+          PprojCoM.getSpaceLikeComponents().getComponents() / 1_GeV);
+
+      CORSIKA_LOG_DEBUG(
+          "Interaction: etarget CoM: {} GeV"
+          "Interaction: ptarget CoM: {} GeV",
+          PtargCoM.getTimeLikeComponent() / 1_GeV,
+          PtargCoM.getSpaceLikeComponents().getComponents() / 1_GeV);
 
-      std::cout << "Interaction: position of interaction: " << pOrig.getCoordinates()
-                << std::endl;
-      std::cout << "Interaction: time: " << tOrig << std::endl;
+      CORSIKA_LOG_DEBUG("Interaction: position of interaction: ", pOrig.getCoordinates());
+      CORSIKA_LOG_DEBUG("Interaction: time: {}", tOrig);
 
       HEPEnergyType Etot = eProjectileLab + eTargetLab;
       MomentumVector Ptot = projectile.getMomentum();
@@ -290,8 +296,8 @@ namespace corsika::pythia8 {
       HEPEnergyType Ecm = sqrt(Etot * Etot - Ptot.getSquaredNorm());
 
       // sample target mass number
-      const auto* currentNode = projectile.getNode();
-      const auto& mediumComposition =
+      auto const* currentNode = projectile.getNode();
+      auto const& mediumComposition =
           currentNode->getModelProperties().getNuclearComposition();
       // get cross sections for target materials
       /*
@@ -304,27 +310,30 @@ namespace corsika::pythia8 {
 
       for (size_t i = 0; i < compVec.size(); ++i) {
         auto const targetId = compVec[i];
-        const auto [sigProd, sigEla] = getCrossSection(corsikaBeamId, targetId, Ecm);
-        [[maybe_unused]] const auto& dummy_sigEla = sigEla;
+        auto const [sigProd, sigEla] = getCrossSection(corsikaBeamId, targetId, Ecm);
+        [[maybe_unused]] auto const& dummy_sigEla = sigEla;
         cross_section_of_components[i] = sigProd;
       }
 
-      const auto corsikaTargetId =
+      auto const corsikaTargetId =
           mediumComposition.sampleTarget(cross_section_of_components, RNG_);
-      std::cout << "Interaction: target selected: " << corsikaTargetId << std::endl;
+      CORSIKA_LOG_DEBUG("Interaction: target selected: {}", corsikaTargetId);
 
       if (corsikaTargetId != Code::Hydrogen && corsikaTargetId != Code::Neutron &&
           corsikaTargetId != Code::Proton)
         throw std::runtime_error("DoInteraction: wrong target for PYTHIA");
 
-      std::cout << "Interaction: "
-                << " DoInteraction: E(GeV):" << eProjectileLab / 1_GeV
-                << " Ecm(GeV): " << Ecm / 1_GeV << std::endl;
+      CORSIKA_LOG_DEBUG(
+          "Interaction: "
+          " DoInteraction: E(GeV): {}"
+          " Ecm(GeV): {}",
+          eProjectileLab / 1_GeV, Ecm / 1_GeV);
 
       if (eProjectileLab < 8.5_GeV || !isValidCoMEnergy(Ecm)) {
-        std::cout << "Interaction: "
-                  << " DoInteraction: should have dropped particle.. "
-                  << "THIS IS AN ERROR" << std::endl;
+        CORSIKA_LOG_DEBUG(
+            "Interaction: "
+            " DoInteraction: should have dropped particle.. "
+            "THIS IS AN ERROR");
         throw std::runtime_error("energy too low for PYTHIA");
 
       } else {
@@ -333,10 +342,11 @@ namespace corsika::pythia8 {
         configureLabFrameCollision(corsikaBeamId, corsikaTargetId, eProjectileLab);
 
         // create event in pytia
-        if (!pythia_.next()) throw std::runtime_error("Pythia::DoInteraction: failed!");
+        if (!Pythia8::Pythia::next())
+          throw std::runtime_error("Pythia::DoInteraction: failed!");
 
         // link to pythia stack
-        Pythia8::Event& event = pythia_.event;
+        Pythia8::Event& event = Pythia8::Pythia::event;
 
         if (print_listing_) {
           // print final state
@@ -352,7 +362,7 @@ namespace corsika::pythia8 {
 
           auto const pyId = convert_from_PDG(static_cast<PDGCode>(p8p.id()));
 
-          const MomentumVector pyPlab(
+          MomentumVector const pyPlab(
               labCS, {p8p.px() * 1_GeV, p8p.py() * 1_GeV, p8p.pz() * 1_GeV});
           HEPEnergyType const pyEn = p8p.e() * 1_GeV;
 
@@ -363,9 +373,11 @@ namespace corsika::pythia8 {
           Plab_final += pnew.getMomentum();
           Elab_final += pnew.getEnergy();
         }
-        std::cout << "conservation (all GeV): "
-                  << "Elab_final=" << Elab_final / 1_GeV
-                  << ", Plab_final=" << (Plab_final / 1_GeV).getComponents() << std::endl;
+        CORSIKA_LOG_DEBUG(
+            "conservation (all GeV): "
+            "Elab_final= {}"
+            ", Plab_final= {}",
+            Elab_final / 1_GeV, (Plab_final / 1_GeV).getComponents());
       }
     }
   }
diff --git a/corsika/detail/modules/pythia8/Random.inl b/corsika/detail/modules/pythia8/Random.inl
index c077e0bf732474bdfef413fac35885294f8b14f8..cf1408f1e1fcf722e1f13b0f5960c9ca531659e0 100644
--- a/corsika/detail/modules/pythia8/Random.inl
+++ b/corsika/detail/modules/pythia8/Random.inl
@@ -12,6 +12,6 @@
 
 namespace corsika::pythia8 {
 
-  double Random::flat() { return Dist_(RNG_); }
+  inline double Random::flat() { return Dist_(RNG_); }
 
 } // namespace corsika::pythia8
diff --git a/corsika/detail/modules/qgsjetII/Interaction.inl b/corsika/detail/modules/qgsjetII/Interaction.inl
index afdafc99f528137d4bf8baa2b06c48594c1f478e..a97265abc3a3aa442e9d373d579a37fe3da66412 100644
--- a/corsika/detail/modules/qgsjetII/Interaction.inl
+++ b/corsika/detail/modules/qgsjetII/Interaction.inl
@@ -27,12 +27,12 @@
 
 namespace corsika::qgsjetII {
 
-  Interaction::Interaction(const std::string& dataPath)
+  inline Interaction::Interaction(const std::string& dataPath)
       : data_path_(dataPath) {
     if (dataPath == "") {
       if (std::getenv("CORSIKA_DATA")) {
         data_path_ = std::string(std::getenv("CORSIKA_DATA")) + "/QGSJetII/";
-        std::cout << "Searching for QGSJetII data tables in " << data_path_ << std::endl;
+        CORSIKA_LOG_DEBUG("Searching for QGSJetII data tables in {}", data_path_);
       }
     }
 
@@ -46,14 +46,15 @@ namespace corsika::qgsjetII {
     }
   }
 
-  Interaction::~Interaction() {
-    std::cout << "QgsjetII::Interaction n=" << count_ << std::endl;
+  inline Interaction::~Interaction() {
+    CORSIKA_LOG_DEBUG("QgsjetII::Interaction n= {}", count_);
   }
 
-  CrossSectionType Interaction::getCrossSection(const Code beamId, const Code targetId,
-                                                const HEPEnergyType Elab,
-                                                const unsigned int Abeam,
-                                                const unsigned int targetA) const {
+  inline CrossSectionType Interaction::getCrossSection(const Code beamId,
+                                                       const Code targetId,
+                                                       const HEPEnergyType Elab,
+                                                       const unsigned int Abeam,
+                                                       const unsigned int targetA) const {
     double sigProd = std::numeric_limits<double>::infinity();
 
     if (corsika::qgsjetII::canInteract(beamId)) {
@@ -76,17 +77,19 @@ namespace corsika::qgsjetII {
           throw std::runtime_error("QgsjetII target outside range. ");
       }
 
-      std::cout << "QgsjetII::getCrossSection Elab=" << Elab << " iBeam=" << iBeam
-                << " iProjectile=" << iProjectile << " iTarget=" << iTarget << std::endl;
+      CORSIKA_LOG_DEBUG(
+          "QgsjetII::getCrossSection Elab= {} GeV iBeam= {}"
+          " iProjectile= {} iTarget= {}",
+          Elab / 1_GeV, iBeam, iProjectile, iTarget);
       sigProd = qgsect_(Elab / 1_GeV, iBeam, iProjectile, iTarget);
-      std::cout << "QgsjetII::getCrossSection sigProd=" << sigProd << std::endl;
+      CORSIKA_LOG_DEBUG("QgsjetII::getCrossSection sigProd= {} mb", sigProd);
     }
 
     return sigProd * 1_mb;
   }
 
   template <typename TParticle>
-  GrammageType Interaction::getInteractionLength(const TParticle& vP) const {
+  inline GrammageType Interaction::getInteractionLength(const TParticle& vP) const {
 
     // coordinate system, get global frame of reference
     CoordinateSystemPtr const& rootCS = get_root_CoordinateSystem();
@@ -103,10 +106,12 @@ namespace corsika::qgsjetII {
     // total momentum and energy
     HEPEnergyType Elab = vP.getEnergy();
 
-    std::cout << "Interaction: LambdaInt: \n"
-              << " input energy: " << vP.getEnergy() / 1_GeV << std::endl
-              << " beam can interact:" << kInteraction << std::endl
-              << " beam pid:" << vP.getPID() << std::endl;
+    CORSIKA_LOG_DEBUG(
+        "Interaction: LambdaInt: \n"
+        " input energy: {} GeV"
+        " beam can interact: {}"
+        " beam pid: {}",
+        vP.getEnergy() / 1_GeV, kInteraction, vP.getPID());
 
     if (kInteraction) {
 
@@ -131,16 +136,18 @@ namespace corsika::qgsjetII {
             return getCrossSection(corsikaBeamId, targetID, Elab, Abeam, targetA);
           });
 
-      std::cout << "Interaction: "
-                << "IntLength: weighted CrossSection (mb): "
-                << weightedProdCrossSection / 1_mb << std::endl;
+      CORSIKA_LOG_DEBUG(
+          "Interaction: "
+          "IntLength: weighted CrossSection (mb): {}",
+          weightedProdCrossSection / 1_mb);
 
       // calculate interaction length in medium
       GrammageType const int_length = mediumComposition.getAverageMassNumber() *
                                       constants::u / weightedProdCrossSection;
-      std::cout << "Interaction: "
-                << "interaction length (g/cm2): " << int_length / (0.001_kg) * 1_cm * 1_cm
-                << std::endl;
+      CORSIKA_LOG_DEBUG(
+          "Interaction: "
+          "interaction length (g/cm2): {}",
+          int_length / (0.001_kg) * 1_cm * 1_cm);
 
       return int_length;
     }
@@ -154,14 +161,15 @@ namespace corsika::qgsjetII {
    */
 
   template <typename TView>
-  void Interaction::doInteraction(TView& view) {
+  inline void Interaction::doInteraction(TView& view) {
 
     auto const projectile = view.getProjectile();
 
     const auto corsikaBeamId = projectile.getPID();
-    std::cout << "ProcessQgsjetII: "
-              << "DoInteraction: " << corsikaBeamId << " interaction? "
-              << corsika::qgsjetII::canInteract(corsikaBeamId) << std::endl;
+    CORSIKA_LOG_DEBUG(
+        "ProcessQgsjetII: "
+        "DoInteraction: {} interaction possible? {}",
+        corsikaBeamId, corsika::qgsjetII::canInteract(corsikaBeamId));
 
     if (corsika::qgsjetII::canInteract(corsikaBeamId)) {
 
@@ -187,16 +195,17 @@ namespace corsika::qgsjetII {
 
       HEPEnergyType const projectileEnergyLabPerNucleon = projectileEnergyLab / beamA;
 
-      std::cout << "Interaction: ebeam lab: " << projectileEnergyLab / 1_GeV << std::endl
-                << "Interaction: pbeam lab: "
-                << projectileMomentumLab.getComponents() / 1_GeV << std::endl;
-      std::cout << "Interaction: etarget lab: " << targetEnergyLab / 1_GeV << std::endl
-                << "Interaction: ptarget lab: "
-                << targetMomentumLab.getComponents() / 1_GeV << std::endl;
-
-      std::cout << "Interaction: position of interaction: " << pOrig.getCoordinates()
-                << std::endl;
-      std::cout << "Interaction: time: " << tOrig << std::endl;
+      CORSIKA_LOG_DEBUG(
+          "Interaction: ebeam lab: {} GeV"
+          "Interaction: pbeam lab: {} GeV",
+          projectileEnergyLab / 1_GeV, projectileMomentumLab.getComponents() / 1_GeV);
+      CORSIKA_LOG_DEBUG(
+          "Interaction: etarget lab: {} GeV"
+          "Interaction: ptarget lab: {} GeV",
+          targetEnergyLab / 1_GeV, targetMomentumLab.getComponents() / 1_GeV);
+      CORSIKA_LOG_DEBUG("Interaction: position of interaction: {}",
+                        pOrig.getCoordinates());
+      CORSIKA_LOG_DEBUG("Interaction: time: {} ", tOrig);
 
       // sample target mass number
       auto const* currentNode = projectile.getNode();
@@ -221,7 +230,7 @@ namespace corsika::qgsjetII {
 
       const auto targetCode =
           mediumComposition.sampleTarget(cross_section_of_components, rng_);
-      std::cout << "Interaction: target selected: " << targetCode << std::endl;
+      CORSIKA_LOG_DEBUG("Interaction: target selected: {}", targetCode);
 
       int targetMassNumber = 1;     // proton
       if (is_nucleus(targetCode)) { // nucleus
@@ -232,8 +241,7 @@ namespace corsika::qgsjetII {
         if (targetCode != Proton::code)
           throw std::runtime_error("QgsjetII Taget not possible.");
       }
-      std::cout << "Interaction: target qgsjetII code/A: " << targetMassNumber
-                << std::endl;
+      CORSIKA_LOG_DEBUG("Interaction: target qgsjetII code/A: ", targetMassNumber);
 
       int projectileMassNumber = 1; // "1" means "hadron"
       QgsjetIIHadronType qgsjet_hadron_type =
@@ -274,8 +282,8 @@ namespace corsika::qgsjetII {
         // else if (abs(kBeam)>6) -> throw
       }
 
-      std::cout << "Interaction: "
-                << " DoInteraction: E(GeV):" << projectileEnergyLab / 1_GeV << std::endl;
+      CORSIKA_LOG_DEBUG("Interaction: DoInteraction: E(GeV): {} GeV",
+                        projectileEnergyLab / 1_GeV);
       count_++;
       int qgsjet_hadron_type_int = static_cast<QgsjetIICodeIntType>(qgsjet_hadron_type);
       qgini_(projectileEnergyLab / 1_GeV, qgsjet_hadron_type_int, projectileMassNumber,
@@ -320,8 +328,10 @@ namespace corsika::qgsjetII {
 
             auto const energy = sqrt(momentum.getSquaredNorm() + square(nucleonMass));
             momentum.rebase(originalCS); // transform back into standard lab frame
-            std::cout << "secondary fragment> id=" << idFragm
-                      << " p=" << momentum.getComponents() << std::endl;
+            CORSIKA_LOG_DEBUG(
+                "secondary fragment> id= {}"
+                " p={}",
+                idFragm, momentum.getComponents());
             auto pnew = view.addSecondary(
                 std::make_tuple(idFragm, energy, momentum, pOrig, tOrig));
             Plab_final += pnew.getMomentum();
@@ -352,9 +362,13 @@ namespace corsika::qgsjetII {
 
           auto const energy = sqrt(momentum.getSquaredNorm() + square(nucleusMass));
           momentum.rebase(originalCS); // transform back into standard lab frame
-          std::cout << "secondary fragment> id=" << idFragm
-                    << " p=" << momentum.getComponents() << " A=" << A << " Z=" << Z
-                    << std::endl;
+          CORSIKA_LOG_DEBUG(
+              "secondary fragment> id={}"
+              " p={}"
+              " A={}"
+              " Z= {}",
+              idFragm, momentum.getComponents(), A, Z);
+
           auto pnew = view.addSecondary(
               std::make_tuple(idFragm, energy, momentum, pOrig, tOrig, A, Z));
           Plab_final += pnew.getMomentum();
@@ -370,24 +384,27 @@ namespace corsika::qgsjetII {
         auto const energy = psec.getEnergy();
 
         momentum.rebase(originalCS); // transform back into standard lab frame
-        std::cout << "secondary fragment> id="
-                  << corsika::qgsjetII::convertFromQgsjetII(psec.getPID())
-                  << " p=" << momentum.getComponents() << std::endl;
+        CORSIKA_LOG_DEBUG(
+            "secondary fragment> id= {}"
+            " p= {}",
+            corsika::qgsjetII::convertFromQgsjetII(psec.getPID()),
+            momentum.getComponents());
         auto pnew = view.addSecondary(
             std::make_tuple(corsika::qgsjetII::convertFromQgsjetII(psec.getPID()), energy,
                             momentum, pOrig, tOrig));
         Plab_final += pnew.getMomentum();
         Elab_final += pnew.getEnergy();
       }
-      std::cout << "conservation (all GeV): Ecm_final= n/a" /* << Ecm_final / 1_GeV*/
-                << std::endl
-                << "Elab_final=" << Elab_final / 1_GeV
-                << ", Plab_final=" << (Plab_final / 1_GeV).getComponents()
-                << ", N_wounded,targ="
-                << QGSJetIIFragmentsStackData::getWoundedNucleonsTarget()
-                << ", N_wounded,proj="
-                << QGSJetIIFragmentsStackData::getWoundedNucleonsProjectile()
-                << ", N_fragm,proj=" << qfs.getSize() << std::endl;
+      CORSIKA_LOG_DEBUG(
+          "conservation (all GeV): Ecm_final= n/a" /* << Ecm_final / 1_GeV*/
+          "Elab_final="
+          ", Plab_final={}"
+          ", N_wounded,targ={}"
+          ", N_wounded,proj={}"
+          ", N_fragm,proj={}",
+          Elab_final / 1_GeV, (Plab_final / 1_GeV).getComponents(),
+          QGSJetIIFragmentsStackData::getWoundedNucleonsTarget(),
+          QGSJetIIFragmentsStackData::getWoundedNucleonsProjectile(), qfs.getSize());
     }
   }
 
diff --git a/corsika/detail/modules/qgsjetII/QGSJetIIStack.inl b/corsika/detail/modules/qgsjetII/QGSJetIIStack.inl
index 60f86fb48130ba5df29da5d27c5741baa80c1c47..92422c1dc08fb44f270311b3ddee81a7fb1e2425 100644
--- a/corsika/detail/modules/qgsjetII/QGSJetIIStack.inl
+++ b/corsika/detail/modules/qgsjetII/QGSJetIIStack.inl
@@ -10,58 +10,61 @@
 
 namespace corsika::qgsjetII {
 
-  void QGSJetIIStackData::clear() {
+  inline void QGSJetIIStackData::clear() {
     qgarr12_.nsp = 0;
     qgarr13_.nsf = 0;
     qgarr55_.nwt = 0;
   }
-  unsigned int QGSJetIIStackData::getSize() const { return qgarr12_.nsp; }
-  unsigned int QGSJetIIStackData::getCapacity() const { return nptmax; }
+  inline unsigned int QGSJetIIStackData::getSize() const { return qgarr12_.nsp; }
+  inline unsigned int QGSJetIIStackData::getCapacity() const { return nptmax; }
 
-  void QGSJetIIStackData::setId(const unsigned int i, const int v) {
+  inline void QGSJetIIStackData::setId(const unsigned int i, const int v) {
     qgarr14_.ich[i] = v;
   }
-  void QGSJetIIStackData::setEnergy(const unsigned int i, const HEPEnergyType v) {
+  inline void QGSJetIIStackData::setEnergy(const unsigned int i, const HEPEnergyType v) {
     qgarr14_.esp[i][0] = v / 1_GeV;
   }
 
-  void QGSJetIIStackData::setMomentum(const unsigned int i, const MomentumVector& v) {
+  inline void QGSJetIIStackData::setMomentum(const unsigned int i,
+                                             const MomentumVector& v) {
     auto tmp = v.getComponents();
     qgarr14_.esp[i][2] = tmp[0] / 1_GeV;
     qgarr14_.esp[i][3] = tmp[1] / 1_GeV;
     qgarr14_.esp[i][1] = tmp[2] / 1_GeV;
   }
 
-  int QGSJetIIStackData::getId(const unsigned int i) const { return qgarr14_.ich[i]; }
-  HEPEnergyType QGSJetIIStackData::getEnergy(const int i) const {
+  inline int QGSJetIIStackData::getId(const unsigned int i) const {
+    return qgarr14_.ich[i];
+  }
+  inline HEPEnergyType QGSJetIIStackData::getEnergy(const int i) const {
     return qgarr14_.esp[i][0] * 1_GeV;
   }
-  MomentumVector QGSJetIIStackData::getMomentum(const unsigned int i,
-                                                const CoordinateSystemPtr& CS) const {
+  inline MomentumVector QGSJetIIStackData::getMomentum(
+      const unsigned int i, const CoordinateSystemPtr& CS) const {
     QuantityVector<hepmomentum_d> components = {qgarr14_.esp[i][2] * 1_GeV,
                                                 qgarr14_.esp[i][3] * 1_GeV,
                                                 qgarr14_.esp[i][1] * 1_GeV};
     return MomentumVector(CS, components);
   }
 
-  void QGSJetIIStackData::copy(const unsigned int i1, const unsigned int i2) {
+  inline void QGSJetIIStackData::copy(const unsigned int i1, const unsigned int i2) {
     qgarr14_.ich[i2] = qgarr14_.ich[i1];
     for (unsigned int i = 0; i < 4; ++i) qgarr14_.esp[i2][i] = qgarr14_.esp[i1][i];
   }
 
-  void QGSJetIIStackData::swap(const unsigned int i1, const unsigned int i2) {
+  inline void QGSJetIIStackData::swap(const unsigned int i1, const unsigned int i2) {
     std::swap(qgarr14_.ich[i1], qgarr14_.ich[i2]);
     for (unsigned int i = 0; i < 4; ++i)
       std::swap(qgarr14_.esp[i1][i], qgarr14_.esp[i2][i]);
   }
 
-  void QGSJetIIStackData::incrementSize() { qgarr12_.nsp++; }
-  void QGSJetIIStackData::decrementSize() {
+  inline void QGSJetIIStackData::incrementSize() { qgarr12_.nsp++; }
+  inline void QGSJetIIStackData::decrementSize() {
     if (qgarr12_.nsp > 0) { qgarr12_.nsp--; }
   }
 
   template <typename StackIteratorInterface>
-  void ParticleInterface<StackIteratorInterface>::setParticleData(
+  inline void ParticleInterface<StackIteratorInterface>::setParticleData(
       const int vID, const HEPEnergyType vE, const MomentumVector& vP,
       const HEPMassType) {
     setPID(vID);
@@ -70,7 +73,7 @@ namespace corsika::qgsjetII {
   }
 
   template <typename StackIteratorInterface>
-  void ParticleInterface<StackIteratorInterface>::setParticleData(
+  inline void ParticleInterface<StackIteratorInterface>::setParticleData(
       ParticleInterface<StackIteratorInterface>& /*parent*/, const int vID,
       const HEPEnergyType vE, const MomentumVector& vP, const HEPMassType) {
     setPID(vID);
@@ -79,34 +82,36 @@ namespace corsika::qgsjetII {
   }
 
   template <typename StackIteratorInterface>
-  void ParticleInterface<StackIteratorInterface>::setEnergy(const HEPEnergyType v) {
+  inline void ParticleInterface<StackIteratorInterface>::setEnergy(
+      const HEPEnergyType v) {
     getStackData().setEnergy(getIndex(), v);
   }
 
   template <typename StackIteratorInterface>
-  HEPEnergyType ParticleInterface<StackIteratorInterface>::getEnergy() const {
+  inline HEPEnergyType ParticleInterface<StackIteratorInterface>::getEnergy() const {
     return getStackData().getEnergy(getIndex());
   }
 
   template <typename StackIteratorInterface>
-  void ParticleInterface<StackIteratorInterface>::setPID(const int v) {
+  inline void ParticleInterface<StackIteratorInterface>::setPID(const int v) {
     getStackData().setId(getIndex(), v);
   }
 
   template <typename StackIteratorInterface>
-  corsika::qgsjetII::QgsjetIICode ParticleInterface<StackIteratorInterface>::getPID()
-      const {
+  inline corsika::qgsjetII::QgsjetIICode
+  ParticleInterface<StackIteratorInterface>::getPID() const {
     return static_cast<corsika::qgsjetII::QgsjetIICode>(getStackData().getId(getIndex()));
   }
 
   template <typename StackIteratorInterface>
-  MomentumVector ParticleInterface<StackIteratorInterface>::getMomentum(
+  inline MomentumVector ParticleInterface<StackIteratorInterface>::getMomentum(
       const CoordinateSystemPtr& CS) const {
     return getStackData().getMomentum(getIndex(), CS);
   }
 
   template <typename StackIteratorInterface>
-  void ParticleInterface<StackIteratorInterface>::setMomentum(const MomentumVector& v) {
+  inline void ParticleInterface<StackIteratorInterface>::setMomentum(
+      const MomentumVector& v) {
     getStackData().setMomentum(getIndex(), v);
   }
 
diff --git a/corsika/detail/modules/qgsjetII/qgsjet-II-04.inl b/corsika/detail/modules/qgsjetII/qgsjet-II-04.inl
index 130ac0173920a78bdd774914c5f285a90caa51ff..a36268cf597f1d4fa812b848174d7c7b8828fc26 100644
--- a/corsika/detail/modules/qgsjetII/qgsjet-II-04.inl
+++ b/corsika/detail/modules/qgsjetII/qgsjet-II-04.inl
@@ -11,14 +11,14 @@
 #include <corsika/modules/qgsjetII/qgsjet-II-04.hpp>
 
 #include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/core/Logging.hpp>
 
 #include <iostream>
 #include <random>
 
-datadir::datadir(const std::string& dir) {
+inline datadir::datadir(const std::string& dir) {
   if (dir.length() > 130) {
-    std::cerr << "QGSJetII error, will cut datadir \"" << dir
-              << "\" to 130 characters: " << std::endl;
+    CORSIKA_LOG_ERROR("QGSJetII error, will cut datadir \"{}\" to 130 characters: ", {});
   }
   int i = 0;
   for (i = 0; i < std::min(130, int(dir.length())); ++i) data[i] = dir[i];
@@ -26,7 +26,7 @@ datadir::datadir(const std::string& dir) {
   data[i + 1] = '\0';
 }
 
-double qgran_(int&) {
+inline double qgran_(int&) {
   static corsika::default_prng_type& rng =
       corsika::RNGManager::getInstance().GetRandomStream("qgran");
 
@@ -34,6 +34,6 @@ double qgran_(int&) {
   return dist(rng);
 }
 
-void lzmaopenfile_(const char*, int) {}
-void lzmaclosefile_() {}
-void lzmafillarray_(const double&, const int&) {}
+inline void lzmaopenfile_(const char*, int) {}
+inline void lzmaclosefile_() {}
+inline void lzmafillarray_(const double&, const int&) {}
diff --git a/corsika/detail/modules/sibyll/Interaction.inl b/corsika/detail/modules/sibyll/Interaction.inl
index e2c5f001fcb12dc487558b5efad6a69e4bbdcb37..69c4d43393a8812b11794a7db93ee6f79f6117cb 100644
--- a/corsika/detail/modules/sibyll/Interaction.inl
+++ b/corsika/detail/modules/sibyll/Interaction.inl
@@ -41,8 +41,7 @@ namespace corsika::sibyll {
   }
 
   inline Interaction::~Interaction() {
-    CORSIKA_LOG_DEBUG(
-        fmt::format("Sibyll::Interaction n={}, Nnuc={}", count_, nucCount_));
+    CORSIKA_LOG_DEBUG("Sibyll::Interaction n={}, Nnuc={}", count_, nucCount_);
   }
 
   inline void Interaction::setStable(std::vector<corsika::Code> const& vParticleList) {
@@ -54,13 +53,13 @@ namespace corsika::sibyll {
   }
 
   inline void Interaction::setUnstable(const corsika::Code vCode) {
-    std::cout << "Sibyll::Interaction: setting " << vCode << " unstable.." << std::endl;
+    CORSIKA_LOG_DEBUG("Sibyll::Interaction: setting {} unstable..", vCode);
     const int s_id = abs(corsika::sibyll::convertToSibyllRaw(vCode));
     s_csydec_.idb[s_id - 1] = abs(s_csydec_.idb[s_id - 1]);
   }
 
   inline void Interaction::setStable(const corsika::Code vCode) {
-    std::cout << "Sibyll::Interaction: setting " << vCode << " stable.." << std::endl;
+    CORSIKA_LOG_DEBUG("Sibyll::Interaction: setting {} stable..", vCode);
     const int s_id = abs(corsika::sibyll::convertToSibyllRaw(vCode));
     s_csydec_.idb[s_id - 1] = (-1) * abs(s_csydec_.idb[s_id - 1]);
   }
@@ -166,17 +165,17 @@ namespace corsika::sibyll {
           });
 
       CORSIKA_LOG_DEBUG(
-          fmt::format("Interaction: "
-                      "IntLength: weighted CrossSection (mb): {} ",
-                      weightedProdCrossSection / 1_mb));
+          "Interaction: "
+          "IntLength: weighted CrossSection (mb): {} ",
+          weightedProdCrossSection / 1_mb);
 
       // calculate interaction length in medium
       GrammageType const int_length = mediumComposition.getAverageMassNumber() *
                                       constants::u / weightedProdCrossSection;
       CORSIKA_LOG_DEBUG(
-          fmt::format("Interaction: "
-                      "interaction length (g/cm2): {} ",
-                      int_length / (0.001_kg) * 1_cm * 1_cm));
+          "Interaction: "
+          "interaction length (g/cm2): {} ",
+          int_length / (0.001_kg) * 1_cm * 1_cm);
 
       return int_length;
     }
diff --git a/corsika/detail/modules/sibyll/NuclearInteraction.inl b/corsika/detail/modules/sibyll/NuclearInteraction.inl
index 4470ea28a8b1e2f504711346790b67bf2342370e..91b85c68126ffbdebb2417867fcdd2420361fcbc 100644
--- a/corsika/detail/modules/sibyll/NuclearInteraction.inl
+++ b/corsika/detail/modules/sibyll/NuclearInteraction.inl
@@ -27,8 +27,8 @@
 namespace corsika::sibyll {
 
   template <typename TEnvironment>
-  NuclearInteraction<TEnvironment>::NuclearInteraction(sibyll::Interaction& hadint,
-                                                       TEnvironment const& env)
+  inline NuclearInteraction<TEnvironment>::NuclearInteraction(sibyll::Interaction& hadint,
+                                                              TEnvironment const& env)
       : environment_(env)
       , hadronicInteraction_(hadint) {
 
@@ -49,12 +49,12 @@ namespace corsika::sibyll {
   }
 
   template <typename TEnvironment>
-  NuclearInteraction<TEnvironment>::~NuclearInteraction() {
+  inline NuclearInteraction<TEnvironment>::~NuclearInteraction() {
     CORSIKA_LOG_DEBUG("Nuclib::NuclearInteraction n={} Nnuc={}", count_, nucCount_);
   }
 
   template <typename TEnvironment>
-  void NuclearInteraction<TEnvironment>::printCrossSectionTable(Code pCode) {
+  inline void NuclearInteraction<TEnvironment>::printCrossSectionTable(Code pCode) {
     const int k = targetComponentsIndex_.at(pCode);
     Code pNuclei[] = {Code::Helium, Code::Lithium7, Code::Oxygen,
                       Code::Neon,   Code::Argon,    Code::Iron};
@@ -79,7 +79,7 @@ namespace corsika::sibyll {
   }
 
   template <typename TEnvironment>
-  void NuclearInteraction<TEnvironment>::initializeNuclearCrossSections() {
+  inline void NuclearInteraction<TEnvironment>::initializeNuclearCrossSections() {
 
     auto& universe = *(environment_.getUniverse());
 
@@ -143,7 +143,7 @@ namespace corsika::sibyll {
   }
 
   template <typename TEnvironment>
-  CrossSectionType NuclearInteraction<TEnvironment>::readCrossSectionTable(
+  inline CrossSectionType NuclearInteraction<TEnvironment>::readCrossSectionTable(
       const int ia, Code pTarget, HEPEnergyType elabnuc) {
 
     const int ib = targetComponentsIndex_.at(pTarget) + 1; // table index in fortran
@@ -161,9 +161,8 @@ namespace corsika::sibyll {
   // TODO: remove elastic cross section?
   template <typename TEnvironment>
   template <typename TParticle>
-  std::tuple<CrossSectionType, CrossSectionType>
-  NuclearInteraction<TEnvironment>::getCrossSection(TParticle const& projectile,
-                                                    Code const TargetId) {
+  std::tuple<CrossSectionType, CrossSectionType> inline NuclearInteraction<
+      TEnvironment>::getCrossSection(TParticle const& projectile, Code const TargetId) {
 
     if (projectile.getPID() != Code::Nucleus)
       throw std::runtime_error(
@@ -203,7 +202,7 @@ namespace corsika::sibyll {
 
   template <typename TEnvironment>
   template <typename TParticle>
-  GrammageType NuclearInteraction<TEnvironment>::getInteractionLength(
+  inline GrammageType NuclearInteraction<TEnvironment>::getInteractionLength(
       TParticle const& projectile) {
 
     // coordinate system, get global frame of reference
@@ -240,7 +239,7 @@ namespace corsika::sibyll {
     pTotLab += pTarget;
     auto const pTotLabNorm = pTotLab.getNorm();
     // calculate cm. energy
-    HEPEnergyType const ECoM = sqrt(
+    [[maybe_unused]] HEPEnergyType const ECoM = sqrt(
         (Elab + pTotLabNorm) * (Elab - pTotLabNorm)); // binomial for numerical accuracy
     auto const ECoMNN = sqrt(2. * ElabNuc * constants::nucleonMass);
     CORSIKA_LOG_DEBUG(
@@ -310,7 +309,7 @@ namespace corsika::sibyll {
 
   template <typename TEnvironment>
   template <typename TSecondaryView>
-  void NuclearInteraction<TEnvironment>::doInteraction(TSecondaryView& view) {
+  inline void NuclearInteraction<TEnvironment>::doInteraction(TSecondaryView& view) {
 
     auto projectile = view.getProjectile();
 
@@ -577,7 +576,7 @@ namespace corsika::sibyll {
     }
 
     // add inelastic interactions
-    std::cout << "calculate inelastic nucleon-nucleon interactions.." << std::endl;
+    CORSIKA_LOG_DEBUG("calculate inelastic nucleon-nucleon interactions..");
     for (int j = 0; j < nInelNucleons; ++j) {
       // TODO: sample neutron or proton
       auto pCode = Code::Proton;
diff --git a/corsika/detail/modules/tracking/Intersect.inl b/corsika/detail/modules/tracking/Intersect.inl
index fef16a955801992b636516605eec118d24fd3a04..c653cc5aa698107cd25eb3c009e634f5f9bd8f8a 100644
--- a/corsika/detail/modules/tracking/Intersect.inl
+++ b/corsika/detail/modules/tracking/Intersect.inl
@@ -38,7 +38,7 @@ namespace corsika {
     // first check, where we leave the current volume
     // this assumes our convention, that all Volume primitives must be convex
     // thus, the last entry is always the exit point
-    const Intersections time_intersections_curr =
+    Intersections const time_intersections_curr =
         TDerived::intersect(particle, volumeNode);
     CORSIKA_LOG_TRACE("curr node {}, parent node {}, hasIntersections={} ",
                       fmt::ptr(&volumeNode), fmt::ptr(volumeNode.getParent()),
@@ -56,16 +56,17 @@ namespace corsika {
 
     // where do we collide with any of the next-tree-level volumes
     // entirely contained by currentLogicalVolumeNode
-    for (const auto& node : volumeNode.getChildNodes()) {
+    for (auto const& node : volumeNode.getChildNodes()) {
 
-      const Intersections time_intersections = TDerived::intersect(particle, *node);
+      Intersections const time_intersections = TDerived::intersect(particle, *node);
+      CORSIKA_LOG_TRACE("intersection times with child volume {}", fmt::ptr(node));
       if (!time_intersections.hasIntersections()) { continue; }
-      CORSIKA_LOG_DEBUG("intersection times with child volume {} : enter {} s, exit {} s",
-                        fmt::ptr(node), time_intersections.getEntry() / 1_s,
+      CORSIKA_LOG_TRACE("                                        : enter {} s, exit {} s",
+                        time_intersections.getEntry() / 1_s,
                         time_intersections.getExit() / 1_s);
 
-      const auto t_entry = time_intersections.getEntry();
-      const auto t_exit = time_intersections.getExit();
+      auto const t_entry = time_intersections.getEntry();
+      auto const t_exit = time_intersections.getExit();
       CORSIKA_LOG_TRACE("children t-entry: {}, t-exit: {}, smaller? {} ", t_entry, t_exit,
                         t_entry <= minTime);
       // note, theoretically t can even be smaller than 0 since we
@@ -86,14 +87,14 @@ namespace corsika {
     // current volume
     for (node_type* node : volumeNode.getExcludedNodes()) {
 
-      const Intersections time_intersections = TDerived::intersect(particle, *node);
+      Intersections const time_intersections = TDerived::intersect(particle, *node);
       if (!time_intersections.hasIntersections()) { continue; }
       CORSIKA_LOG_DEBUG(
           "intersection times with exclusion volume {} : enter {} s, exit {} s",
           fmt::ptr(node), time_intersections.getEntry() / 1_s,
           time_intersections.getExit() / 1_s);
-      const auto t_entry = time_intersections.getEntry();
-      const auto t_exit = time_intersections.getExit();
+      auto const t_entry = time_intersections.getEntry();
+      auto const t_exit = time_intersections.getExit();
       CORSIKA_LOG_TRACE("children t-entry: {}, t-exit: {}, smaller? {} ", t_entry, t_exit,
                         t_entry <= minTime);
       // note, theoretically t can even be smaller than 0 since we
diff --git a/corsika/detail/modules/tracking/TrackingLeapFrogCurved.inl b/corsika/detail/modules/tracking/TrackingLeapFrogCurved.inl
index 8545d2bbdc434501070f6a1b11232dc93184c47d..5770fe439dbc309588e990da8669c9f85ceb619c 100644
--- a/corsika/detail/modules/tracking/TrackingLeapFrogCurved.inl
+++ b/corsika/detail/modules/tracking/TrackingLeapFrogCurved.inl
@@ -30,7 +30,7 @@ namespace corsika {
 
     template <typename TParticle>
     inline auto make_LeapFrogStep(TParticle const& particle, LengthType steplength) {
-      if (particle.getMomentum().norm() == 0_GeV) {
+      if (particle.getMomentum().getNorm() == 0_GeV) {
         return std::make_tuple(particle.getPosition(), particle.getMomentum() / 1_GeV,
                                double(0));
       } // charge of the particle
@@ -103,9 +103,9 @@ namespace corsika {
       LengthType const gyroradius =
           (pAlongB_delta * 1_V / (constants::c * abs(chargeNumber) * magnitudeB * 1_eV));
 
-      const double maxRadians = 0.01;
-      const LengthType steplimit = 2 * cos(maxRadians) * sin(maxRadians) * gyroradius;
-      const TimeType steplimit_time = steplimit / initialVelocity.getNorm();
+      double const maxRadians = 0.01;
+      LengthType const steplimit = 2 * cos(maxRadians) * sin(maxRadians) * gyroradius;
+      TimeType const steplimit_time = steplimit / initialVelocity.getNorm();
       CORSIKA_LOG_DEBUG("gyroradius {}, steplimit: {} = {}", gyroradius, steplimit,
                         steplimit_time);
 
@@ -113,7 +113,7 @@ namespace corsika {
       // intersection
       auto [minTime, minNode] = nextIntersect(particle, steplimit_time);
 
-      const auto k =
+      auto const k =
           chargeNumber * constants::cSquared * 1_eV / (particle.getEnergy() * 1_V);
       return std::make_tuple(
           LeapFrogTrajectory(position, initialVelocity, magneticfield, k,
@@ -121,10 +121,9 @@ namespace corsika {
           minNode);                    // next volume node
     }
 
-    template <typename TParticle, typename TMedium>
-    inline Intersections Tracking::intersect(const TParticle& particle,
-                                             const Sphere& sphere,
-                                             const TMedium& medium) {
+    template <typename TParticle>
+    inline Intersections Tracking::intersect(TParticle const& particle,
+                                             Sphere const& sphere) {
 
       if (sphere.getRadius() == 1_km * std::numeric_limits<double>::infinity()) {
         return Intersections();
@@ -132,7 +131,9 @@ namespace corsika {
 
       int const chargeNumber = particle.getChargeNumber();
       auto const& position = particle.getPosition();
-      MagneticFieldVector const& magneticfield = medium.getMagneticField(position);
+      auto const* currentLogicalVolumeNode = particle.getNode();
+      MagneticFieldVector const& magneticfield =
+          currentLogicalVolumeNode->getModelProperties().getMagneticField(position);
 
       VelocityVector const velocity =
           particle.getMomentum() / particle.getEnergy() * constants::c;
@@ -144,28 +145,29 @@ namespace corsika {
       bool const isParallel = (projectedDirectionSqrNorm == 0 * square(1_T));
 
       if (chargeNumber == 0 || magneticfield.getNorm() == 0_T || isParallel) {
-        return tracking_line::Tracking::intersect<TParticle, TMedium>(particle, sphere,
-                                                                      medium);
+        return tracking_line::Tracking::intersect<TParticle>(particle, sphere);
       }
 
       bool const numericallyInside = sphere.contains(particle.getPosition());
-
-      const auto absVelocity = velocity.getNorm();
-      auto energy = particle.getEnergy();
-      auto k = chargeNumber * constants::cSquared * 1_eV / (absVelocity * energy * 1_V);
-
-      auto const denom = (directionBefore.cross(magneticfield)).getSquaredNorm() * k * k;
-      const double a =
-          ((directionBefore.cross(magneticfield)).dot(position - sphere.getCenter()) * k +
-           1) *
-          4 / (1_m * 1_m * denom);
-      const double b = directionBefore.dot(position - sphere.getCenter()) * 8 /
-                       (denom * 1_m * 1_m * 1_m);
-      const double c = ((position - sphere.getCenter()).getSquaredNorm() -
-                        (sphere.getRadius() * sphere.getRadius())) *
-                       4 / (denom * 1_m * 1_m * 1_m * 1_m);
-      CORSIKA_LOG_TRACE("denom={}, a={}, b={}, c={}", denom, a, b, c);
-      std::complex<double>* solutions = quartic_solver::solve_quartic(0, a, b, c);
+      CORSIKA_LOG_TRACE("numericallyInside={}", numericallyInside);
+
+      auto const absVelocity = velocity.getNorm();
+      auto const energy = particle.getEnergy();
+      // this is: k = q/|p|
+      auto const k =
+          chargeNumber * constants::cSquared * 1_eV / (absVelocity * energy * 1_V);
+
+      auto const direction_B_perp = directionBefore.cross(magneticfield);
+      auto const denom = 4. / (direction_B_perp.getSquaredNorm() * k * k);
+      Vector<length_d> const deltaPos = position - sphere.getCenter();
+      double const b = (direction_B_perp.dot(deltaPos) * k + 1) * denom / (1_m * 1_m);
+      double const c = directionBefore.dot(deltaPos) * 2 * denom / (1_m * 1_m * 1_m);
+      LengthType const deltaPosLength = deltaPos.getNorm();
+      double const d = (deltaPosLength + sphere.getRadius()) *
+                       (deltaPosLength - sphere.getRadius()) * denom /
+                       (1_m * 1_m * 1_m * 1_m);
+      CORSIKA_LOG_TRACE("denom={}, b={}, c={}, d={}", denom, b, c, d);
+      std::complex<double> const* solutions = quartic_solver::solve_quartic(0, b, c, d);
       LengthType d_enter, d_exit;
       int first = 0, first_entry = 0, first_exit = 0;
       for (int i = 0; i < 4; i++) {
@@ -183,7 +185,7 @@ namespace corsika {
               }
               first_entry++;
 
-            } else { // thus, dist >= -0.0001_m
+            } else { // thus, dist > -0.0001_m
 
               if (first_exit == 0) {
                 d_exit = dist;
@@ -225,13 +227,92 @@ namespace corsika {
       return Intersections(d_enter / absVelocity, d_exit / absVelocity);
     }
 
-    template <typename TParticle, typename TBaseNodeType>
-    inline Intersections Tracking::intersect(const TParticle& particle,
-                                             const TBaseNodeType& volumeNode) {
-      const Sphere* sphere = dynamic_cast<const Sphere*>(&volumeNode.getVolume());
-      if (sphere) {
-        return intersect(particle, *sphere, volumeNode.getModelProperties());
+    template <typename TParticle>
+    inline Intersections Tracking::intersect(TParticle const& particle,
+                                             Plane const& plane) {
+
+      int chargeNumber;
+      if (is_nucleus(particle.getPID())) {
+        chargeNumber = particle.getNuclearZ();
+      } else {
+        chargeNumber = get_charge_number(particle.getPID());
       }
+      auto const* currentLogicalVolumeNode = particle.getNode();
+      VelocityVector const velocity =
+          particle.getMomentum() / particle.getEnergy() * constants::c;
+      auto const absVelocity = velocity.getNorm();
+      DirectionVector const direction =
+          velocity.normalized(); // determine steplength to next volume
+      Point const position = particle.getPosition();
+
+      auto const magneticfield =
+          currentLogicalVolumeNode->getModelProperties().getMagneticField(position);
+
+      if (chargeNumber != 0 && abs(plane.getNormal().dot(velocity.cross(magneticfield))) >
+                                   1e-6_T * 1_m / 1_s) {
+
+        auto const* currentLogicalVolumeNode = particle.getNode();
+        auto const magneticfield =
+            currentLogicalVolumeNode->getModelProperties().getMagneticField(position);
+        auto const k =
+            chargeNumber * (constants::c * 1_eV / 1_V) / particle.getMomentum().getNorm();
+
+        auto const direction_B_perp = direction.cross(magneticfield);
+        auto const denom = plane.getNormal().dot(direction_B_perp) * k;
+        auto const sqrtArg =
+            direction.dot(plane.getNormal()) * direction.dot(plane.getNormal()) -
+            (plane.getNormal().dot(position - plane.getCenter()) * denom * 2);
+
+        if (sqrtArg < 0) {
+          return Intersections(std::numeric_limits<double>::infinity() * 1_s);
+        }
+        double const sqrSqrtArg = sqrt(sqrtArg);
+        auto const norm_projected =
+            direction.dot(plane.getNormal()) / direction.getNorm();
+        LengthType const MaxStepLength1 = (sqrSqrtArg - norm_projected) / denom;
+        LengthType const MaxStepLength2 = (-sqrSqrtArg - norm_projected) / denom;
+
+        CORSIKA_LOG_TRACE("MaxStepLength1={}, MaxStepLength2={}", MaxStepLength1,
+                          MaxStepLength2);
+
+        // check: both intersections in past
+        if (MaxStepLength1 <= 0_m && MaxStepLength2 <= 0_m) {
+          return Intersections(std::numeric_limits<double>::infinity() * 1_s);
+
+          // check: next intersection is MaxStepLength2
+        } else if (MaxStepLength1 <= 0_m || MaxStepLength2 < MaxStepLength1) {
+          CORSIKA_LOG_TRACE(" steplength to obs plane 2: {} ", MaxStepLength2);
+          return Intersections(
+              MaxStepLength2 *
+              (direction + direction_B_perp * MaxStepLength2 * k / 2).getNorm() /
+              absVelocity);
+
+          // check: next intersections is MaxStepLength1
+        } else if (MaxStepLength2 <= 0_m || MaxStepLength1 < MaxStepLength2) {
+          CORSIKA_LOG_TRACE(" steplength to obs plane 2: {} ", MaxStepLength1);
+          return Intersections(
+              MaxStepLength1 *
+              (direction + direction_B_perp * MaxStepLength1 * k / 2).getNorm() /
+              absVelocity);
+        }
+
+        CORSIKA_LOG_WARN(
+            "Particle wasn't tracked with curved trajectory -> straight (is this an "
+            "ERROR?)");
+
+      } // end if curved-tracking
+
+      CORSIKA_LOG_TRACE("straight tracking with  chargeNumber={}, B={}", chargeNumber,
+                        magneticfield);
+
+      return tracking_line::Tracking::intersect(particle, plane);
+    }
+
+    template <typename TParticle, typename TBaseNodeType>
+    inline Intersections Tracking::intersect(TParticle const& particle,
+                                             TBaseNodeType const& volumeNode) {
+      Sphere const* sphere = dynamic_cast<Sphere const*>(&volumeNode.getVolume());
+      if (sphere) { return intersect(particle, *sphere); }
       throw std::runtime_error(
           "The Volume type provided is not supported in intersect(particle, node)");
     }
diff --git a/corsika/detail/modules/tracking/TrackingLeapFrogStraight.inl b/corsika/detail/modules/tracking/TrackingLeapFrogStraight.inl
index 132a87e5d80abc129bc3375b4cab921c701a565c..61ad3793d0537c47c0efb1b870d11281c663072c 100644
--- a/corsika/detail/modules/tracking/TrackingLeapFrogStraight.inl
+++ b/corsika/detail/modules/tracking/TrackingLeapFrogStraight.inl
@@ -9,8 +9,6 @@
 #pragma once
 
 #include <corsika/framework/geometry/Line.hpp>
-#include <corsika/framework/geometry/Plane.hpp>
-#include <corsika/framework/geometry/Sphere.hpp>
 #include <corsika/framework/geometry/StraightTrajectory.hpp>
 #include <corsika/framework/geometry/Vector.hpp>
 #include <corsika/framework/core/ParticleProperties.hpp>
@@ -30,7 +28,7 @@ namespace corsika {
       VelocityVector initialVelocity =
           particle.getMomentum() / particle.getEnergy() * constants::c;
 
-      const Point initialPosition = particle.getPosition();
+      Point const initialPosition = particle.getPosition();
       CORSIKA_LOG_DEBUG(
           "TrackingB pid: {}"
           " , E = {} GeV",
@@ -55,17 +53,17 @@ namespace corsika {
       const int chargeNumber = particle.getChargeNumber();
       auto magneticfield =
           volumeNode->getModelProperties().getMagneticField(initialPosition);
-      const auto magnitudeB = magneticfield.getNorm();
+      auto const magnitudeB = magneticfield.getNorm();
       CORSIKA_LOG_DEBUG("field={} uT, chargeNumber={}, magnitudeB={} uT",
                         magneticfield.getComponents() / 1_uT, chargeNumber,
                         magnitudeB / 1_T);
       bool const no_deflection = chargeNumber == 0 || magnitudeB == 0_T;
 
       // check, where the first halve-step direction has geometric intersections
-      const auto [initialTrack, initialTrackNextVolume] =
+      auto const [initialTrack, initialTrackNextVolume] =
           tracking_line::Tracking::getTrack(particle);
       { [[maybe_unused]] auto& initialTrackNextVolume_dum = initialTrackNextVolume; }
-      const auto initialTrackLength = initialTrack.getLength(1);
+      auto const initialTrackLength = initialTrack.getLength(1);
 
       CORSIKA_LOG_DEBUG("initialTrack(0)={}, initialTrack(1)={}, initialTrackLength={}",
                         initialTrack.getPosition(0).getCoordinates(),
@@ -96,8 +94,8 @@ namespace corsika {
       // need to follow strongly curved trajectories segment-wise,
       // at least if we don't employ concepts as "Helix
       // Trajectories" or similar
-      const double maxRadians = 0.01;
-      const LengthType steplimit = 2 * cos(maxRadians) * sin(maxRadians) * gyroradius;
+      double const maxRadians = 0.01;
+      LengthType const steplimit = 2 * cos(maxRadians) * sin(maxRadians) * gyroradius;
       CORSIKA_LOG_DEBUG("gyroradius {}, Steplimit: {}", gyroradius, steplimit);
 
       // calculate first halve step for "steplimit"
@@ -112,12 +110,12 @@ namespace corsika {
       CORSIKA_LOG_DEBUG("first halve step length {}, steplimit={}, initialTrackLength={}",
                         firstHalveSteplength, steplimit, initialTrackLength);
       // perform the first halve-step
-      const Point position_mid = initialPosition + direction * firstHalveSteplength;
-      const auto k =
-          chargeNumber * constants::c * 1_eV / (particle.getMomentum().getNorm() * 1_V);
-      const auto new_direction =
+      Point const position_mid = initialPosition + direction * firstHalveSteplength;
+      auto const k =
+          chargeNumber * (constants::c * 1_eV / 1_V) / particle.getMomentum().getNorm();
+      auto const new_direction =
           direction + direction.cross(magneticfield) * firstHalveSteplength * 2 * k;
-      const auto new_direction_norm = new_direction.getNorm(); // by design this is >1
+      auto const new_direction_norm = new_direction.getNorm(); // by design this is >1
       CORSIKA_LOG_DEBUG(
           "position_mid={}, new_direction={}, (new_direction_norm)={}, deflection={}",
           position_mid.getCoordinates(), new_direction.getComponents(),
@@ -128,7 +126,7 @@ namespace corsika {
       // check, where the second halve-step direction has geometric intersections
       particle.setPosition(position_mid);
       particle.setMomentum(new_direction * absMomentum);
-      const auto [finalTrack, finalTrackNextVolume] =
+      auto const [finalTrack, finalTrackNextVolume] =
           tracking_line::Tracking::getTrack(particle);
       particle.setPosition(initialPosition); // this is not nice...
       particle.setMomentum(initialMomentum); // this is not nice...
@@ -162,12 +160,12 @@ namespace corsika {
 
       // perform the second halve-step
       auto const new_direction_normalized = new_direction.normalized();
-      const Point finalPosition =
+      Point const finalPosition =
           position_mid + new_direction_normalized * secondHalveStepLength;
 
-      const LengthType totalStep = firstHalveSteplength + secondHalveStepLength;
-      const auto delta_pos = finalPosition - initialPosition;
-      const auto distance = delta_pos.getNorm();
+      LengthType const totalStep = firstHalveSteplength + secondHalveStepLength;
+      auto const delta_pos = finalPosition - initialPosition;
+      auto const distance = delta_pos.getNorm();
 
       return std::make_tuple(
           StraightTrajectory(
diff --git a/corsika/detail/modules/tracking/TrackingStraight.inl b/corsika/detail/modules/tracking/TrackingStraight.inl
index 2c284b4d01dddc3be3322dc305a84f6f836bd5e5..9a4659250286414cc88f837e96c3ac5898952006 100644
--- a/corsika/detail/modules/tracking/TrackingStraight.inl
+++ b/corsika/detail/modules/tracking/TrackingStraight.inl
@@ -48,9 +48,9 @@ namespace corsika::tracking_line {
                            minNode);                    // next volume node
   }
 
-  template <typename TParticle, typename TMedium>
-  Intersections Tracking::intersect(TParticle const& particle, Sphere const& sphere,
-                                    TMedium const&) {
+  template <typename TParticle>
+  inline Intersections Tracking::intersect(TParticle const& particle,
+                                           Sphere const& sphere) {
     auto const delta = particle.getPosition() - sphere.getCenter();
     auto const velocity = particle.getMomentum() / particle.getEnergy() * constants::c;
     auto const vSqNorm = velocity.getSquaredNorm();
@@ -70,31 +70,28 @@ namespace corsika::tracking_line {
   }
 
   template <typename TParticle, typename TBaseNodeType>
-  Intersections Tracking::intersect(TParticle const& particle,
-                                    TBaseNodeType const& volumeNode) {
+  inline Intersections Tracking::intersect(TParticle const& particle,
+                                           TBaseNodeType const& volumeNode) {
     Sphere const* sphere = dynamic_cast<Sphere const*>(&volumeNode.getVolume());
-    if (sphere) {
-      typedef typename std::remove_const_t<
-          std::remove_reference_t<decltype(volumeNode.getModelProperties())>>
-          medium_type;
-      return Tracking::intersect<TParticle, medium_type>(particle, *sphere,
-                                                         volumeNode.getModelProperties());
-    }
+    if (sphere) { return Tracking::intersect<TParticle>(particle, *sphere); }
     throw std::runtime_error(
         "The Volume type provided is not supported in Intersect(particle, node)");
   }
 
-  template <typename TParticle, typename TMedium>
-  Intersections Tracking::intersect(TParticle const& particle, Plane const& plane,
-                                    TMedium const&) {
+  template <typename TParticle>
+  inline Intersections Tracking::intersect(TParticle const& particle,
+                                           Plane const& plane) {
     auto const delta = plane.getCenter() - particle.getPosition();
     auto const velocity = particle.getMomentum() / particle.getEnergy() * constants::c;
     auto const n = plane.getNormal();
-    auto const c = n.dot(velocity);
+    auto const n_dot_v = n.dot(velocity);
+
+    CORSIKA_LOG_TRACE("n_dot_v={}, delta={}, momentum={}", n_dot_v, delta,
+                      particle.getMomentum());
 
-    return Intersections(c.magnitude() == 0
+    return Intersections(n_dot_v.magnitude() == 0
                              ? std::numeric_limits<TimeType::value_type>::infinity() * 1_s
-                             : n.dot(delta) / c);
+                             : n.dot(delta) / n_dot_v);
   }
 
 } // namespace corsika::tracking_line
diff --git a/corsika/detail/modules/urqmd/UrQMD.inl b/corsika/detail/modules/urqmd/UrQMD.inl
index 372d2d5925450b4798bd4650015e856e643d5de4..a1abaa6abf994b7f07a165b4f42599c3b4aaa89f 100644
--- a/corsika/detail/modules/urqmd/UrQMD.inl
+++ b/corsika/detail/modules/urqmd/UrQMD.inl
@@ -23,11 +23,11 @@
 
 namespace corsika::urqmd {
 
-  UrQMD::UrQMD() { ::urqmd::iniurqmdc8_(); }
+  inline UrQMD::UrQMD() { ::urqmd::iniurqmdc8_(); }
 
-  CrossSectionType UrQMD::getCrossSection(Code vProjectileCode, Code vTargetCode,
-                                          HEPEnergyType vLabEnergy,
-                                          int vAProjectile = 1) {
+  inline CrossSectionType UrQMD::getCrossSection(Code vProjectileCode, Code vTargetCode,
+                                                 HEPEnergyType vLabEnergy,
+                                                 int vAProjectile = 1) {
 
     // the following is a translation of ptsigtot() into C++
     if (vProjectileCode != Code::Nucleus &&
@@ -63,8 +63,8 @@ namespace corsika::urqmd {
 
   template <typename TParticle> // need template here, as this is called both with
                                 // SetupParticle as well as SetupProjectile
-  CrossSectionType UrQMD::getCrossSection(TParticle const& vProjectile,
-                                          Code vTargetCode) const {
+  inline CrossSectionType UrQMD::getCrossSection(TParticle const& vProjectile,
+                                                 Code vTargetCode) const {
     // TODO: return 0 for non-hadrons?
 
     auto const projectileCode = vProjectile.getPID();
@@ -79,7 +79,7 @@ namespace corsika::urqmd {
     return getCrossSection(projectileCode, vTargetCode, projectileEnergyLab, Ap);
   }
 
-  bool UrQMD::canInteract(Code vCode) const {
+  inline bool UrQMD::canInteract(Code vCode) const {
     // According to the manual, UrQMD can use all mesons, baryons and nucleons
     // which are modeled also as input particles. I think it is safer to accept
     // only the usual long-lived species as input.
@@ -95,7 +95,7 @@ namespace corsika::urqmd {
   }
 
   template <typename TParticle>
-  GrammageType UrQMD::getInteractionLength(TParticle const& vParticle) const {
+  inline GrammageType UrQMD::getInteractionLength(TParticle const& vParticle) const {
 
     if (!canInteract(vParticle.getPID())) {
       // we could do the canInteract check in getCrossSection, too but if
@@ -115,7 +115,7 @@ namespace corsika::urqmd {
   }
 
   template <typename TView>
-  void UrQMD::doInteraction(TView& view) {
+  inline void UrQMD::doInteraction(TView& view) {
 
     auto projectile = view.getProjectile();
 
@@ -223,17 +223,15 @@ namespace corsika::urqmd {
       auto const energy = sqrt(momentum.getSquaredNorm() + square(get_mass(code)));
 
       momentum.rebase(originalCS); // transform back into standard lab frame
-      std::cout << i << " " << code << " " << momentum.getComponents() << std::endl;
+      CORSIKA_LOG_DEBUG(" {} {} {} ", i, code, momentum.getComponents());
 
       projectile.addSecondary(
           std::make_tuple(code, energy, momentum, projectilePosition, projectileTime));
     }
-
-    std::cout << "UrQMD generated " << ::urqmd::sys_.npart << " secondaries!"
-              << std::endl;
+    CORSIKA_LOG_DEBUG("UrQMD generated {} secondaries!", ::urqmd::sys_.npart);
   }
 
-  Code convertFromUrQMD(int vItyp, int vIso3) {
+  inline Code convertFromUrQMD(int vItyp, int vIso3) {
     int const pdgInt =
         ::urqmd::pdgid_(vItyp, vIso3); // use the conversion function provided by UrQMD
     if (pdgInt == 0) {                 // ::urqmd::pdgid_ returns 0 on error
@@ -243,7 +241,7 @@ namespace corsika::urqmd {
     return convert_from_PDG(pdg);
   }
 
-  std::pair<int, int> convertToUrQMD(Code code) {
+  inline std::pair<int, int> convertToUrQMD(Code code) {
     static const std::map<int, std::pair<int, int>> mapPDGToUrQMD{
         // data mostly from github.com/afedynitch/ParticleDataTool
         {22, {100, 0}},      // gamma
diff --git a/corsika/detail/stack/VectorStack.inl b/corsika/detail/stack/VectorStack.inl
index 44384a131f8733711c52c72baff2d471af87da79..a89c85ed951595ed585cee02649dc8e698733aac 100644
--- a/corsika/detail/stack/VectorStack.inl
+++ b/corsika/detail/stack/VectorStack.inl
@@ -23,7 +23,7 @@
 namespace corsika {
 
   template <typename StackIteratorInterface>
-  void ParticleInterface<StackIteratorInterface>::setParticleData(
+  inline void ParticleInterface<StackIteratorInterface>::setParticleData(
       std::tuple<Code, HEPEnergyType, MomentumVector, Point, TimeType> const& v) {
     this->setPID(std::get<0>(v));
     this->setEnergy(std::get<1>(v));
@@ -33,7 +33,7 @@ namespace corsika {
   }
 
   template <typename StackIteratorInterface>
-  void ParticleInterface<StackIteratorInterface>::setParticleData(
+  inline void ParticleInterface<StackIteratorInterface>::setParticleData(
       ParticleInterface<StackIteratorInterface> const&,
       std::tuple<Code, HEPEnergyType, MomentumVector, Point, TimeType> const& v) {
     this->setPID(std::get<0>(v));
diff --git a/corsika/detail/stack/history/HistoryObservationPlane.inl b/corsika/detail/stack/history/HistoryObservationPlane.inl
index 0536eaf3ada84db7382461ec95d2ff7cc6ffac1b..66b0f11e450e83b422ce4d0f4d6a5340e86ebf9f 100644
--- a/corsika/detail/stack/history/HistoryObservationPlane.inl
+++ b/corsika/detail/stack/history/HistoryObservationPlane.inl
@@ -13,14 +13,14 @@
 
 namespace corsika::history {
 
-  HistoryObservationPlane::HistoryObservationPlane(setup::Stack const& stack,
-                                                   Plane const& obsPlane,
-                                                   bool deleteOnHit)
+  inline HistoryObservationPlane::HistoryObservationPlane(setup::Stack const& stack,
+                                                          Plane const& obsPlane,
+                                                          bool deleteOnHit)
       : stack_{stack}
       , plane_{obsPlane}
       , deleteOnHit_{deleteOnHit} {}
 
-  ProcessReturn HistoryObservationPlane::DoContinuous(
+  inline ProcessReturn HistoryObservationPlane::DoContinuous(
       setup::Stack::ParticleType const& particle, setup::Trajectory const& trajectory) {
     TimeType const timeOfIntersection =
         (plane_.getCenter() - trajectory.getR0()).dot(plane_.getNormal()) /
@@ -45,8 +45,8 @@ namespace corsika::history {
     }
   }
 
-  LengthType HistoryObservationPlane::MaxStepLength(setup::Stack::ParticleType const&,
-                                                    setup::Trajectory const& trajectory) {
+  inline LengthType HistoryObservationPlane::MaxStepLength(
+      setup::Stack::ParticleType const&, setup::Trajectory const& trajectory) {
     TimeType const timeOfIntersection =
         (plane_.getCenter() - trajectory.getR0()).dot(plane_.getNormal()) /
         trajectory.getV0().dot(plane_.getNormal());
@@ -59,7 +59,7 @@ namespace corsika::history {
     return (trajectory.getR0() - pointOfIntersection).norm() * 1.0001;
   }
 
-  void HistoryObservationPlane::fillHistoryHistogram(
+  inline void HistoryObservationPlane::fillHistoryHistogram(
       setup::Stack::ParticleType const& muon) {
     double const muon_energy = muon.getEnergy() / 1_GeV;
 
diff --git a/corsika/framework/analytics/Timer.hpp b/corsika/framework/analytics/Timer.hpp
index 4df67f488dfaa0ebc998d67689d28238b6e2834b..a2dba0ea5e756046a5740ee9412ddc2fc965de2f 100644
--- a/corsika/framework/analytics/Timer.hpp
+++ b/corsika/framework/analytics/Timer.hpp
@@ -36,10 +36,10 @@ namespace corsika {
         : timeDiff_(0){};
 
     /// Start the timer
-    inline void startTimer() { start_ = clock_type::now(); }
+    void startTimer() { start_ = clock_type::now(); }
 
     /// Stop the timer
-    inline void stopTimer() {
+    void stopTimer() {
       timeDiff_ = std::chrono::duration_cast<duration_type>(clock_type::now() - start_);
     }
 
@@ -48,7 +48,7 @@ namespace corsika {
      * @return Returns the measured runtime of the wrapped function/functor in the unit
      * given by TDuration
      **/
-    inline duration_type getTime() const { return timeDiff_; }
+    duration_type getTime() const { return timeDiff_; }
   };
 
   std::false_type is_timer_impl(...);
@@ -56,7 +56,7 @@ namespace corsika {
   std::true_type is_timer_impl(Timer<T, U> const volatile&);
 
   template <typename T>
-  inline constexpr bool is_timer_v =
+  constexpr bool is_timer_v =
       std::is_same_v<decltype(is_timer_impl(std::declval<T&>())), std::true_type>;
 
-} // namespace corsika
\ No newline at end of file
+} // namespace corsika
diff --git a/corsika/framework/core/Logging.hpp b/corsika/framework/core/Logging.hpp
index 4d4c43a7ba2e4f7e9d2c90e87a905d542b554df8..2daea27c596be86c039f31f7a0b608e28d47ff4e 100644
--- a/corsika/framework/core/Logging.hpp
+++ b/corsika/framework/core/Logging.hpp
@@ -35,9 +35,9 @@
 // if this is a Debug build, include debug messages in objects
 #ifdef DEBUG
 // trace is the highest level of logging (ALL messages will be printed)
-#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
+#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_TRACE
 #else // otherwise, remove everything but "error" and worse messages
-#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
+#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_DEBUG
 #endif
 
 #include <spdlog/fmt/ostr.h> // will output whenerver a streaming operator is found
@@ -50,6 +50,7 @@ namespace corsika {
    * The default pattern for CORSIKA8 loggers.
    */
   const std::string default_pattern{"[%n:%^%-8l%$] %v"};
+  const std::string source_pattern{"[%n:%^%-8l%$(%s:%!:%#)] %v"};
 
   /**
    * Create a new C8-style logger.
diff --git a/corsika/framework/core/ParticleProperties.hpp b/corsika/framework/core/ParticleProperties.hpp
index 85227425c42bf584ffcde2cea20c75d11570709e..e133d68679c27bc597050a46d84c5bba2bfed70e 100644
--- a/corsika/framework/core/ParticleProperties.hpp
+++ b/corsika/framework/core/ParticleProperties.hpp
@@ -48,9 +48,9 @@ namespace corsika {
   using PDGCodeType = std::underlying_type<PDGCode>::type;
 
   // forward declarations to be used in GeneratedParticleProperties
-  int16_t constexpr get_charge_number(Code);     //!< electric charge in units of e
-  ElectricChargeType constexpr get_charge(Code); //!< electric charge
-  HEPMassType constexpr get_mass(Code);          //!< mass
+  int16_t constexpr get_charge_number(Code const);     //!< electric charge in units of e
+  ElectricChargeType constexpr get_charge(Code const); //!< electric charge
+  HEPMassType constexpr get_mass(Code const);          //!< mass
   HEPEnergyType constexpr get_energy_threshold(
       Code const); //!< get energy threshold below which the particle is discarded, by
                    //!< default set to particle mass
@@ -67,29 +67,31 @@ namespace corsika {
   }
 
   //! Particle code according to PDG, "Monte Carlo Particle Numbering Scheme"
-  PDGCode constexpr get_PDG(Code);
-  std::string_view constexpr get_name(Code); //!< name of the particle as string
-  TimeType constexpr get_lifetime(Code);     //!< lifetime
+  PDGCode constexpr get_PDG(Code const);
+  std::string_view constexpr get_name(Code const); //!< name of the particle as string
+  TimeType constexpr get_lifetime(Code const);     //!< lifetime
 
   //! true iff the particle is a hard-coded nucleus or Code::Nucleus
-  bool constexpr is_nucleus(Code);
-  bool constexpr is_hadron(Code);    //!< true iff particle is hadron
-  bool constexpr is_em(Code);        //!< true iff particle is electron, positron or gamma
-  bool constexpr is_muon(Code);      //!< true iff particle is mu+ or mu-
-  bool constexpr is_neutrino(Code);  //!< true iff particle is (anti-) neutrino
-  int constexpr get_nucleus_A(Code); //!< returns A for hard-coded nucleus, otherwise 0
-  int constexpr get_nucleus_Z(Code); //!< returns Z for hard-coded nucleus, otherwise 0
+  bool constexpr is_nucleus(Code const);
+  bool constexpr is_hadron(Code const); //!< true iff particle is hadron
+  bool constexpr is_em(Code const); //!< true iff particle is electron, positron or gamma
+  bool constexpr is_muon(Code const);     //!< true iff particle is mu+ or mu-
+  bool constexpr is_neutrino(Code const); //!< true iff particle is (anti-) neutrino
+  int constexpr get_nucleus_A(
+      Code const); //!< returns A for hard-coded nucleus, otherwise 0
+  int constexpr get_nucleus_Z(
+      Code const); //!< returns Z for hard-coded nucleus, otherwise 0
 
   //! returns mass of (A,Z) nucleus, disregarding binding energy
-  inline HEPMassType get_nucleus_mass(unsigned int const, unsigned int const);
+  HEPMassType get_nucleus_mass(unsigned int const, unsigned int const);
 
   //! convert PDG code to CORSIKA 8 internal code
-  inline Code convert_from_PDG(PDGCode);
+  Code convert_from_PDG(PDGCode const);
 
   std::initializer_list<Code> constexpr get_all_particles();
 
   //! the output stream operator for human-readable particle codes
-  inline std::ostream& operator<<(std::ostream&, corsika::Code);
+  std::ostream& operator<<(std::ostream&, corsika::Code);
 } // namespace corsika
 
 // data arrays, etc., as generated automatically
diff --git a/corsika/framework/core/PhysicalGeometry.hpp b/corsika/framework/core/PhysicalGeometry.hpp
index 36b897f75ed37aa7f8acacb4c6a3bfef15ae307a..b325ad2805a13afa54af891f806ab0b1492d79e7 100644
--- a/corsika/framework/core/PhysicalGeometry.hpp
+++ b/corsika/framework/core/PhysicalGeometry.hpp
@@ -20,6 +20,21 @@
 
 namespace corsika {
 
+  /**
+   * A 3D vector defined in a specific coordinate system with units HEPMomentumType
+   **/
   typedef Vector<hepmomentum_d> MomentumVector;
 
+  /**
+   * A 3D vector defined in a specific coordinate system with no units. But, note, this is
+   * not automatically normaliyed! It is not a "NormalVector".
+   **/
+  typedef Vector<dimensionless_d> DirectionVector;
+
+  /**
+   * A 3D vector defined in a specific coordinate system with units "velocity_t".
+   *
+   **/
+  typedef Vector<SpeedType::dimension_type> VelocityVector;
+
 } // namespace corsika
diff --git a/corsika/framework/geometry/CoordinateSystem.hpp b/corsika/framework/geometry/CoordinateSystem.hpp
index ed9c8184853e61c9d19d102d2543a79d7806d7fc..f15b89e994d6399600322b479a287d1a726e1abd 100644
--- a/corsika/framework/geometry/CoordinateSystem.hpp
+++ b/corsika/framework/geometry/CoordinateSystem.hpp
@@ -40,31 +40,30 @@ namespace corsika {
   /**
    * Creates new CoordinateSystemPtr by translation along \a vector
    */
-  inline CoordinateSystemPtr make_translation(CoordinateSystemPtr const& cs,
-                                              QuantityVector<length_d> const& vector);
+  CoordinateSystemPtr make_translation(CoordinateSystemPtr const& cs,
+                                       QuantityVector<length_d> const& vector);
 
   /**
    * creates a new CoordinateSystem in which vVec points in direction of the new z-axis,
    * \a vVec
    */
   template <typename TDim>
-  inline CoordinateSystemPtr make_rotationToZ(CoordinateSystemPtr const& cs,
-                                              Vector<TDim> const& vVec);
+  CoordinateSystemPtr make_rotationToZ(CoordinateSystemPtr const& cs,
+                                       Vector<TDim> const& vVec);
 
   /**
    * creates a new CoordinateSystem, rotated around axis by angle.
    */
   template <typename TDim>
-  inline CoordinateSystemPtr make_rotation(CoordinateSystemPtr const& cs,
-                                           QuantityVector<TDim> const& axis,
-                                           double const angle);
+  CoordinateSystemPtr make_rotation(CoordinateSystemPtr const& cs,
+                                    QuantityVector<TDim> const& axis, double const angle);
 
   /**
    * creates a new CoordinateSystem, translated by \a translation and rotated around \a
    * axis by \a angle.
    */
   template <typename TDim>
-  inline CoordinateSystemPtr make_translationAndRotation(
+  CoordinateSystemPtr make_translationAndRotation(
       CoordinateSystemPtr const& cs, QuantityVector<length_d> const& translation,
       QuantityVector<TDim> const& axis, double const angle);
 
@@ -119,14 +118,14 @@ namespace corsika {
     /**
      * Checks, if this is the unique ROOT CS
      */
-    inline bool isRoot() const { return !referenceCS_; }
+    bool isRoot() const { return !referenceCS_; }
 
-    inline CoordinateSystemPtr getReferenceCS() const;
+    CoordinateSystemPtr getReferenceCS() const;
 
-    inline EigenTransform const& getTransform() const;
+    EigenTransform const& getTransform() const;
 
-    inline bool operator==(CoordinateSystem const&) const;
-    inline bool operator!=(CoordinateSystem const&) const;
+    bool operator==(CoordinateSystem const&) const;
+    bool operator!=(CoordinateSystem const&) const;
 
   protected:
     static CoordinateSystem createCS() { return CoordinateSystem(); }
@@ -171,8 +170,8 @@ namespace corsika {
    *
    * \todo make this a protected member of CoordinateSystem
    */
-  inline EigenTransform get_transformation(CoordinateSystem const& c1,
-                                           CoordinateSystem const& c2);
+  EigenTransform get_transformation(CoordinateSystem const& c1,
+                                    CoordinateSystem const& c2);
 
 } // namespace corsika
 
diff --git a/corsika/framework/geometry/FourVector.hpp b/corsika/framework/geometry/FourVector.hpp
index 99932012d0ca5d4d99ae7427c62e957e0d37850c..0b87accec038a6d5d796ea91318d86fbbe004317 100644
--- a/corsika/framework/geometry/FourVector.hpp
+++ b/corsika/framework/geometry/FourVector.hpp
@@ -190,8 +190,8 @@ namespace corsika {
    **/
 
   template <typename TTimeType, typename TSpaceVecType>
-  inline std::ostream& operator<<(
-      std::ostream& os, corsika::FourVector<TTimeType, TSpaceVecType> const& qv);
+  std::ostream& operator<<(std::ostream& os,
+                           corsika::FourVector<TTimeType, TSpaceVecType> const& qv);
 
 } // namespace corsika
 
diff --git a/corsika/framework/geometry/Helix.hpp b/corsika/framework/geometry/Helix.hpp
index 5bd9fac77b71ec5e80ba54d26a19b6a3ab7b9d86..3862389889b4d0dcbaba55d00530713f07b25f92 100644
--- a/corsika/framework/geometry/Helix.hpp
+++ b/corsika/framework/geometry/Helix.hpp
@@ -8,11 +8,13 @@
 
 #pragma once
 
-#include <cmath>
 #include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/core/PhysicalGeometry.hpp>
 #include <corsika/framework/geometry/Point.hpp>
 #include <corsika/framework/geometry/Vector.hpp>
 
+#include <cmath>
+
 namespace corsika {
 
   /*!
@@ -31,12 +33,9 @@ namespace corsika {
 
   class Helix {
 
-    ///! \todo move VelocityVec into PhysicalUnits
-    using VelocityVec = Vector<SpeedType::dimension_type>;
-
   public:
-    Helix(Point const& pR0, FrequencyType pOmegaC, VelocityVec const& pvPar,
-          VelocityVec const& pvPerp)
+    Helix(Point const& pR0, FrequencyType pOmegaC, VelocityVector const& pvPar,
+          VelocityVector const& pvPerp)
         : r0_(pR0)
         , omegaC_(pOmegaC)
         , vPar_(pvPar)
@@ -44,24 +43,24 @@ namespace corsika {
         , uPerp_(vPerp_.cross(vPar_.normalized()))
         , radius_(pvPar.getNorm() / abs(pOmegaC)) {}
 
-    inline LengthType getRadius() const;
+    LengthType getRadius() const;
 
-    inline Point getPosition(TimeType const t) const;
+    Point getPosition(TimeType const t) const;
 
-    VelocityVec getVelocity(TimeType const t) const;
+    VelocityVector getVelocity(TimeType const t) const;
 
-    inline Point getPositionFromArclength(LengthType const l) const;
+    Point getPositionFromArclength(LengthType const l) const;
 
-    inline LengthType getArcLength(TimeType const t1, TimeType const t2) const;
+    LengthType getArcLength(TimeType const t1, TimeType const t2) const;
 
-    inline TimeType getTimeFromArclength(LengthType const l) const;
+    TimeType getTimeFromArclength(LengthType const l) const;
 
   private:
     Point r0_;             ///! origin of helix, but this is in the center of the
                            ///! "cylinder" on which the helix rotates
     FrequencyType omegaC_; ///! speed of angular rotation
-    VelocityVec vPar_;     ///! speed along direction of "cylinder"
-    VelocityVec vPerp_, uPerp_;
+    VelocityVector vPar_;  ///! speed along direction of "cylinder"
+    VelocityVector vPerp_, uPerp_;
     LengthType radius_;
   };
 
diff --git a/corsika/framework/geometry/Line.hpp b/corsika/framework/geometry/Line.hpp
index cdb03d6245945a421d31badeae339fa49d9ef71f..7f68984bd07178589e8c24e814e05d65c173e8dc 100644
--- a/corsika/framework/geometry/Line.hpp
+++ b/corsika/framework/geometry/Line.hpp
@@ -31,21 +31,21 @@ namespace corsika {
         : start_point_(pR0)
         , velocity_(pV0) {}
 
-    inline Point getPosition(TimeType const t) const;
+    Point getPosition(TimeType const t) const;
 
-    inline VelocityVector const& getVelocity(TimeType const) const;
+    VelocityVector const& getVelocity(TimeType const) const;
 
-    inline Point getPositionFromArclength(LengthType const l) const;
+    Point getPositionFromArclength(LengthType const l) const;
 
-    inline LengthType getArcLength(TimeType const t1, TimeType const t2) const;
+    LengthType getArcLength(TimeType const t1, TimeType const t2) const;
 
-    inline TimeType getTimeFromArclength(LengthType const t) const;
+    TimeType getTimeFromArclength(LengthType const t) const;
 
-    inline Point const& getStartPoint() const;
+    Point const& getStartPoint() const;
 
-    inline DirectionVector getDirection() const;
+    DirectionVector getDirection() const;
 
-    inline VelocityVector const& getVelocity() const;
+    VelocityVector const& getVelocity() const;
 
   private:
     Point start_point_;
diff --git a/corsika/framework/geometry/Plane.hpp b/corsika/framework/geometry/Plane.hpp
index 9b2eacade48f5d9913d5b5311f1ae595cbcde039..454b527fde9ca1dee0fd4f3e2fd0728317b784e8 100644
--- a/corsika/framework/geometry/Plane.hpp
+++ b/corsika/framework/geometry/Plane.hpp
@@ -9,32 +9,34 @@
 #pragma once
 
 #include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/core/PhysicalGeometry.hpp>
 #include <corsika/framework/geometry/Point.hpp>
 #include <corsika/framework/geometry/Vector.hpp>
 
+#include <string>
+
 namespace corsika {
 
   class Plane {
 
-    ///! \todo move to PhysicalUnits
-    using DimLessVec = Vector<dimensionless_d>;
-
   public:
-    Plane(Point const& vCenter, DimLessVec const& vNormal)
+    Plane(Point const& vCenter, DirectionVector const& vNormal)
         : center_(vCenter)
         , normal_(vNormal.normalized()) {}
 
     bool isAbove(Point const& vP) const;
 
-    LengthType getDistanceTo(corsika::Point const& vP) const;
+    LengthType getDistanceTo(Point const& vP) const;
 
     Point const& getCenter() const;
 
-    DimLessVec const& getNormal() const;
+    DirectionVector const& getNormal() const;
+
+    std::string asString() const;
 
   public:
     Point const center_;
-    DimLessVec const normal_;
+    DirectionVector const normal_;
   };
 
 } // namespace corsika
diff --git a/corsika/framework/geometry/Point.hpp b/corsika/framework/geometry/Point.hpp
index 82d783212b5f2309b1936b465c86434f5674e784..2c684438438455985d0bebfbb95ed6bdafa4c86b 100644
--- a/corsika/framework/geometry/Point.hpp
+++ b/corsika/framework/geometry/Point.hpp
@@ -31,8 +31,8 @@ namespace corsika {
     /** \todo TODO: this should be private or protected, we don NOT want to expose numbers
      * without reference to outside:
      */
-    inline QuantityVector<length_d> const& getCoordinates() const;
-    inline QuantityVector<length_d>& getCoordinates();
+    QuantityVector<length_d> const& getCoordinates() const;
+    QuantityVector<length_d>& getCoordinates();
 
     /**
        this always returns a QuantityVector as triple
@@ -40,7 +40,7 @@ namespace corsika {
        \returns A value type QuantityVector, since it may have to create a temporary
        object to transform to pCS.
     **/
-    inline QuantityVector<length_d> getCoordinates(CoordinateSystemPtr const& pCS) const;
+    QuantityVector<length_d> getCoordinates(CoordinateSystemPtr const& pCS) const;
 
     /**
      * this always returns a QuantityVector as triple
@@ -49,10 +49,10 @@ namespace corsika {
      *   is actually transformed to pCS, if needed. Thus, there may be an implicit call to
      *   \ref rebase.
      **/
-    inline QuantityVector<length_d>& getCoordinates(CoordinateSystemPtr const& pCS);
+    QuantityVector<length_d>& getCoordinates(CoordinateSystemPtr const& pCS);
 
     /**
-     * \defgroup access coordinate components
+     * \name access coordinate components
      * \{
      *
      * Note, if you access components in a different CoordinateSystem
@@ -60,23 +60,23 @@ namespace corsika {
      * created and destroyed each call. This can be avoided by using
      * \ref rebase first.
      **/
-    inline LengthType getX(CoordinateSystemPtr const& pCS) const;
-    inline LengthType getY(CoordinateSystemPtr const& pCS) const;
-    inline LengthType getZ(CoordinateSystemPtr const& pCS) const;
+    LengthType getX(CoordinateSystemPtr const& pCS) const;
+    LengthType getY(CoordinateSystemPtr const& pCS) const;
+    LengthType getZ(CoordinateSystemPtr const& pCS) const;
     /** \} **/
 
     /*!
      * transforms the Point into another CoordinateSystem by changing its
      * coordinates interally
      */
-    inline void rebase(CoordinateSystemPtr const& pCS);
+    void rebase(CoordinateSystemPtr const& pCS);
 
-    inline Point operator+(Vector<length_d> const& pVec) const;
+    Point operator+(Vector<length_d> const& pVec) const;
 
     /*!
      * returns the distance Vector between two points
      */
-    inline Vector<length_d> operator-(Point const& pB) const;
+    Vector<length_d> operator-(Point const& pB) const;
   };
 
 } // namespace corsika
diff --git a/corsika/framework/geometry/QuantityVector.hpp b/corsika/framework/geometry/QuantityVector.hpp
index 235237afe59b8ffbf19413e625e7bdd1178a18e8..d9199dc57490bea7c0204c6256eddc7d4f80ef8c 100644
--- a/corsika/framework/geometry/QuantityVector.hpp
+++ b/corsika/framework/geometry/QuantityVector.hpp
@@ -101,7 +101,7 @@ namespace corsika {
     template <typename T>
     friend class corsika::Vector;
     template <typename TDim>
-    friend std::ostream& operator<<(std::ostream& os, QuantityVector<TDim> qv);
+    friend std::ostream& operator<<(std::ostream& os, QuantityVector<TDim> const& qv);
 
   protected:
     Eigen::Vector3d
@@ -113,8 +113,8 @@ namespace corsika {
    **/
 
   template <typename TDimension>
-  inline std::ostream& operator<<(std::ostream& os,
-                                  corsika::QuantityVector<TDimension> const qv);
+  std::ostream& operator<<(std::ostream& os,
+                           corsika::QuantityVector<TDimension> const& qv);
 
 } // namespace corsika
 
diff --git a/corsika/framework/geometry/Vector.hpp b/corsika/framework/geometry/Vector.hpp
index 0d37c66c7afb5460e09a8f0001ad248bbe59835c..fec720e4df43f33acb09aa536b6d6b482087cf5d 100644
--- a/corsika/framework/geometry/Vector.hpp
+++ b/corsika/framework/geometry/Vector.hpp
@@ -47,14 +47,14 @@ namespace corsika {
      * \todo this should best be protected, we don't want users to use
      * bare coordinates without reference frame
      */
-    inline QuantityVector<TDimension> const& getComponents() const;
-    inline QuantityVector<TDimension>& getComponents();
+    QuantityVector<TDimension> const& getComponents() const;
+    QuantityVector<TDimension>& getComponents();
 
     /*!
      * returns a QuantityVector with the components given in an arbitrary
      * CoordinateSystem
      */
-    inline QuantityVector<TDimension> getComponents(CoordinateSystemPtr const& pCS) const;
+    QuantityVector<TDimension> getComponents(CoordinateSystemPtr const& pCS) const;
 
     /**
      * this always returns a QuantityVector as triple
@@ -63,7 +63,7 @@ namespace corsika {
      *   is actually transformed to pCS, if needed. Thus, there may be an implicit call to
      *   \ref rebase.
      **/
-    inline QuantityVector<TDimension>& getComponents(CoordinateSystemPtr const& pCS);
+    QuantityVector<TDimension>& getComponents(CoordinateSystemPtr const& pCS);
 
     /**
      * \name Access coordinate components
@@ -76,28 +76,28 @@ namespace corsika {
      * \{
      **/
 
-    inline quantity_type getX(CoordinateSystemPtr const& pCS) const;
-    inline quantity_type getY(CoordinateSystemPtr const& pCS) const;
-    inline quantity_type getZ(CoordinateSystemPtr const& pCS) const;
+    quantity_type getX(CoordinateSystemPtr const& pCS) const;
+    quantity_type getY(CoordinateSystemPtr const& pCS) const;
+    quantity_type getZ(CoordinateSystemPtr const& pCS) const;
     /** \} **/
 
     /*!
      * transforms the Vector into another CoordinateSystem by changing
      * its components internally
      */
-    inline void rebase(CoordinateSystemPtr const& pCS);
+    void rebase(CoordinateSystemPtr const& pCS);
 
     /*!
      * returns the norm/length of the Vector. Before using this method,
      * think about whether squaredNorm() might be cheaper for your computation.
      */
-    inline quantity_type getNorm() const;
+    quantity_type getNorm() const;
 
     /*!
      * returns the squared norm of the Vector. Before using this method,
      * think about whether norm() might be cheaper for your computation.
      */
-    inline quantity_square_type getSquaredNorm() const;
+    quantity_square_type getSquaredNorm() const;
 
     /*!
      * returns a Vector \f$ \vec{v}_{\parallel} \f$ which is the parallel projection
diff --git a/corsika/framework/process/BaseProcess.hpp b/corsika/framework/process/BaseProcess.hpp
index e91651d12ea99cafd40718ae288a869852a5cf35..b8c96a54faa504a84c79272ce23f67534b6950b8 100644
--- a/corsika/framework/process/BaseProcess.hpp
+++ b/corsika/framework/process/BaseProcess.hpp
@@ -8,6 +8,10 @@
 
 #pragma once
 
+#include <corsika/framework/process/ProcessTraits.hpp>
+
+#include <type_traits>
+
 namespace corsika {
 
   class TDerived; // fwd decl
@@ -39,8 +43,28 @@ namespace corsika {
     const TDerived& ref() const { return static_cast<const TDerived&>(*this); }
 
   public:
+    //! Default number of processes ist just one, obviously
+    static unsigned int constexpr getNumberOfProcesses() { return 1; }
+
     // Base processor type for use in other template classes
     using process_type = TDerived;
   };
 
+  /**
+   * ProcessTraits specialization
+   **/
+  template <typename TProcess>
+  struct is_process<
+      TProcess,
+      std::enable_if_t<std::is_base_of_v<BaseProcess<typename std::decay_t<TProcess>>,
+                                         typename std::decay_t<TProcess>>>>
+      : std::true_type {};
+
+  template <typename TProcess, int N>
+  struct count_processes<TProcess, N,
+                         typename std::enable_if_t<is_process_v<TProcess> &&
+                                                   !is_process_sequence_v<TProcess>>> {
+    static unsigned int constexpr count = N + 1;
+  };
+
 } // namespace corsika
diff --git a/corsika/framework/process/ContinuousProcess.hpp b/corsika/framework/process/ContinuousProcess.hpp
index 1343c082272a8e0e49830e53c164f985abe5e860..51d73d332fbe00ea3c5845608a0c582f224d5b4c 100644
--- a/corsika/framework/process/ContinuousProcess.hpp
+++ b/corsika/framework/process/ContinuousProcess.hpp
@@ -8,8 +8,10 @@
 
 #pragma once
 
+#include <corsika/framework/core/PhysicalUnits.hpp>
 #include <corsika/framework/process/BaseProcess.hpp>
 #include <corsika/framework/process/ProcessReturn.hpp>
+#include <corsika/framework/process/ProcessTraits.hpp>
 
 namespace corsika {
 
@@ -28,13 +30,41 @@ namespace corsika {
   protected:
   public:
     // here starts the interface part
-    // -> enforce TDerived to implement DoContinuous...
+    /**
+     * Applies the effects of this ContinuousProcess on a Particle on a Track.
+     *
+     * Note, the stepLimit is a flag, if this particular process was responsible for the
+     * track-length limit. This can be used by the process to trigger activity.
+     *
+     * \todo -> enforce TDerived to implement doContinuous...
+     **/
     template <typename TParticle, typename TTrack>
-    ProcessReturn doContinuous(TParticle&, TTrack const&) const;
+    ProcessReturn doContinuous(TParticle&, TTrack const&, bool const stepLimit) const;
 
-    // -> enforce TDerived to implement MaxStepLength...
+    /**
+     * Calculates/returns a possible step length limitation of this continuousprocess.
+     *
+     *
+     * \todo -> enforce TDerived to implement getMaxStepLength...
+     **/
     template <typename TParticle, typename TTrack>
     LengthType getMaxStepLength(TParticle const& p, TTrack const& track) const;
   };
 
+  /**
+   * ProcessTraits specialization
+   **/
+  template <typename TProcess>
+  struct is_continuous_process<
+      TProcess, std::enable_if_t<
+                    std::is_base_of_v<ContinuousProcess<typename std::decay_t<TProcess>>,
+                                      typename std::decay_t<TProcess>>>>
+      : std::true_type {};
+
+  template <typename TProcess, int N>
+  struct count_continuous<TProcess, N,
+                          typename std::enable_if_t<is_continuous_process_v<TProcess>>> {
+    enum { count = N + 1 };
+  };
+
 } // namespace corsika
diff --git a/corsika/framework/process/ContinuousProcessIndex.hpp b/corsika/framework/process/ContinuousProcessIndex.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9d6e84d0f39baaa9a1e0ad7dbf369aba6a967fb3
--- /dev/null
+++ b/corsika/framework/process/ContinuousProcessIndex.hpp
@@ -0,0 +1,34 @@
+/*
+ * (c) Copyright 2021 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#pragma once
+
+namespace corsika {
+
+  /**
+   * To index individual processes (continuous processes) inside a
+   * ProcessSequence.
+   *
+   **/
+
+  class ContinuousProcessIndex {
+  public:
+    ContinuousProcessIndex()
+        : id_(-1) {} // default
+    ContinuousProcessIndex(int const id)
+        : id_(id) {}
+    void setIndex(int const id) { id_ = id; }
+    int getIndex() const { return id_; }
+    bool operator==(ContinuousProcessIndex const v) const { return id_ == v.id_; }
+    bool operator!=(ContinuousProcessIndex const v) const { return id_ != v.id_; }
+
+  private:
+    int id_;
+  };
+
+} // namespace corsika
diff --git a/corsika/framework/process/ContinuousProcessStepLength.hpp b/corsika/framework/process/ContinuousProcessStepLength.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..32b921857aaf6762c584ac5bf68b6885c019e038
--- /dev/null
+++ b/corsika/framework/process/ContinuousProcessStepLength.hpp
@@ -0,0 +1,43 @@
+/*
+ * (c) Copyright 2021 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#pragma once
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/process/ContinuousProcessIndex.hpp>
+
+namespace corsika {
+
+  /**
+   * To store step length in LengthType and unique index in ProcessSequence of shortest
+   * step ContinuousProcess.
+   *
+   **/
+
+  class ContinuousProcessStepLength {
+  public:
+    ContinuousProcessStepLength(LengthType const step)
+        : step_(step)
+        , id_(0) {}
+    ContinuousProcessStepLength(LengthType const step, ContinuousProcessIndex const id)
+        : step_(step)
+        , id_(id) {}
+    void setLength(LengthType const step) { step_ = step; }
+    LengthType getLength() const { return step_; }
+    void setIndex(ContinuousProcessIndex const id) { id_ = id; }
+    ContinuousProcessIndex getIndex() const { return id_; }
+    operator ContinuousProcessIndex() const { return id_; }
+    operator LengthType() const { return step_; }
+    bool operator<(ContinuousProcessStepLength const& r) const { return step_ < r.step_; }
+
+  private:
+    LengthType step_;
+    ContinuousProcessIndex id_;
+  };
+
+} // namespace corsika
diff --git a/corsika/framework/process/ProcessSequence.hpp b/corsika/framework/process/ProcessSequence.hpp
index 70337e1099d09cd4945d7f557f2a1661dedc0d0b..30c270bcbf30acaf0c8a539aada621eb0618886e 100644
--- a/corsika/framework/process/ProcessSequence.hpp
+++ b/corsika/framework/process/ProcessSequence.hpp
@@ -16,6 +16,8 @@
 #include <corsika/framework/process/ProcessTraits.hpp>
 #include <corsika/framework/process/BoundaryCrossingProcess.hpp>
 #include <corsika/framework/process/ContinuousProcess.hpp>
+#include <corsika/framework/process/ContinuousProcessIndex.hpp>
+#include <corsika/framework/process/ContinuousProcessStepLength.hpp>
 #include <corsika/framework/process/DecayProcess.hpp>
 #include <corsika/framework/process/InteractionProcess.hpp>
 #include <corsika/framework/process/ProcessReturn.hpp>
@@ -26,6 +28,21 @@
 
 namespace corsika {
 
+  template <typename TProcess, int N>
+  struct count_continuous<TProcess, N,
+                          typename std::enable_if_t<is_process_sequence_v<TProcess>>> {
+    static unsigned int constexpr count =
+        N + std::decay_t<TProcess>::getNumberOfProcesses();
+  };
+
+  template <typename TProcess, int N>
+  struct count_processes<TProcess, N,
+                         typename std::enable_if_t<is_process_v<TProcess> &&
+                                                   is_process_sequence_v<TProcess>>> {
+    static unsigned int constexpr count =
+        N + std::decay_t<TProcess>::getNumberOfProcesses();
+  };
+
   /**
    *
    *  Definition of a static process list/sequence
@@ -39,13 +56,21 @@ namespace corsika {
    *  they are just classes. This allows us to handle both, rvalue as
    *  well as lvalue Processes in the ProcessSequence.
    *
-   *  The sequence, and the processes use CRTP.
+   *  (The sequence, and the processes use the CRTP, curiously recurring template
+   *  pattern).
    *
-   *  \todo There are several FIXME's in the ProcessSequence.inl due to
-   *  outstanding migration of SecondaryView::parent()
+   * Template parameters:
+   *  - TProcess1 is of type BaseProcess, either a dedicatd process, or a ProcessSequence
+   *  - TProcess2 is of type BaseProcess, either a dedicatd process, or a ProcessSequence
+   *  - ProcessIndexOffset, IndexOfProcess1, IndexOfProcess2 are to count and index each
+   *ContinuousProcess in the entire process-chain
    **/
 
-  template <typename TProcess1, typename TProcess2 = NullModel>
+  template <typename TProcess1, typename TProcess2 = NullModel,
+            int ProcessIndexOffset = 0,
+            int IndexOfProcess1 = count_processes<
+                TProcess1, count_processes<TProcess2, ProcessIndexOffset>::count>::count,
+            int IndexOfProcess2 = count_processes<TProcess2, ProcessIndexOffset>::count>
   class ProcessSequence : public BaseProcess<ProcessSequence<TProcess1, TProcess2>> {
 
     using process1_type = typename std::decay_t<TProcess1>;
@@ -58,16 +83,13 @@ namespace corsika {
     static bool constexpr t2SwitchProcSeq = is_switch_process_sequence_v<process2_type>;
 
     // make sure only BaseProcess types TProcess1/2 are passed
-    static_assert(std::is_base_of_v<BaseProcess<process1_type>, process1_type>,
+    static_assert(is_process_v<process1_type>,
                   "can only use process derived from BaseProcess in "
                   "ProcessSequence, for Process 1");
-    static_assert(std::is_base_of_v<BaseProcess<process2_type>, process2_type>,
+    static_assert(is_process_v<process2_type>,
                   "can only use process derived from BaseProcess in "
                   "ProcessSequence, for Process 2");
 
-    TProcess1 A_; /// process/list A, this is a reference, if possible
-    TProcess2 B_; /// process/list B, this is a reference, if possible
-
   public:
     // resource management
     ProcessSequence() = delete; // only initialized objects
@@ -96,10 +118,11 @@ namespace corsika {
                                      typename TParticle::node_type const& to);
 
     template <typename TParticle, typename TTrack>
-    inline ProcessReturn doContinuous(TParticle& particle, TTrack& vT);
+    ProcessReturn doContinuous(TParticle& particle, TTrack& vT,
+                               ContinuousProcessIndex const limitID);
 
     template <typename TSecondaries>
-    inline void doSecondaries(TSecondaries& vS);
+    void doSecondaries(TSecondaries& vS);
 
     /**
        The processes of type StackProcess do have an internal counter,
@@ -109,44 +132,66 @@ namespace corsika {
        tested if either A_ or B_ are StackProcess and if they are due
        for execution.
      */
-    inline bool checkStep();
+    bool checkStep();
 
     /**
        Execute the StackProcess-es in the ProcessSequence
      */
     template <typename TStack>
-    inline void doStack(TStack& stack);
+    void doStack(TStack& stack);
+
+    /**
+     * Calculate the maximum allowed length of the next tracking step, based on all
+     * ContinuousProcess-es
+     *
+     * The maximum allowed step length is the minimum of the allowed track lenght over all
+     * ContinuousProcess-es in the ProcessSequence.
+     *
+     * \return: ContinuousProcessStepLength which contains the step length itself in
+     *          LengthType, and a unique identifier of the related ContinuousProcess.
+     **/
 
     template <typename TParticle, typename TTrack>
-    inline LengthType getMaxStepLength(TParticle& particle, TTrack& vTrack);
+    ContinuousProcessStepLength getMaxStepLength(TParticle& particle, TTrack& vTrack);
 
     template <typename TParticle>
-    inline GrammageType getInteractionLength(TParticle&& particle) {
+    GrammageType getInteractionLength(TParticle&& particle) {
       return 1. / getInverseInteractionLength(particle);
     }
 
     template <typename TParticle>
-    inline InverseGrammageType getInverseInteractionLength(TParticle&& particle);
+    InverseGrammageType getInverseInteractionLength(TParticle&& particle);
 
     template <typename TSecondaryView>
-    inline ProcessReturn selectInteraction(
+    ProcessReturn selectInteraction(
         TSecondaryView& view, [[maybe_unused]] InverseGrammageType lambda_inv_select,
         [[maybe_unused]] InverseGrammageType lambda_inv_sum =
             InverseGrammageType::zero());
 
     template <typename TParticle>
-    inline TimeType getLifetime(TParticle& particle) {
+    TimeType getLifetime(TParticle& particle) {
       return 1. / getInverseLifetime(particle);
     }
 
     template <typename TParticle>
-    inline InverseTimeType getInverseLifetime(TParticle&& particle);
+    InverseTimeType getInverseLifetime(TParticle&& particle);
 
     // select decay process
     template <typename TSecondaryView>
-    inline ProcessReturn selectDecay(
+    ProcessReturn selectDecay(
         TSecondaryView& view, [[maybe_unused]] InverseTimeType decay_inv_select,
         [[maybe_unused]] InverseTimeType decay_inv_sum = InverseTimeType::zero());
+
+    /**
+     * static counter to uniquely index (count) all ContinuousProcess in switch sequence.
+     **/
+    static unsigned int constexpr getNumberOfProcesses() { return numberOfProcesses_; }
+
+  private:
+    TProcess1 A_; /// process/list A, this is a reference, if possible
+    TProcess2 B_; /// process/list B, this is a reference, if possible
+
+    static unsigned int constexpr numberOfProcesses_ = IndexOfProcess1; // static counter
   };
 
   /**
@@ -170,12 +215,12 @@ namespace corsika {
    *
    * \param vA needs to derive from BaseProcess or ProcessSequence
    * \param vB paramter-pack, needs to derive BaseProcess or ProcessSequence
+   *
    **/
 
   template <typename... TProcesses, typename TProcess1>
-  inline typename std::enable_if_t<
-      std::is_base_of_v<BaseProcess<typename std::decay_t<TProcess1>>,
-                        typename std::decay_t<TProcess1>>,
+  typename std::enable_if_t<
+      is_process_v<typename std::decay_t<TProcess1>>,
       ProcessSequence<TProcess1, decltype(make_sequence(std::declval<TProcesses>()...))>>
   make_sequence(TProcess1&& vA, TProcesses&&... vBs) {
     return ProcessSequence<TProcess1,
@@ -192,12 +237,9 @@ namespace corsika {
    * \param vB needs to derive BaseProcess or ProcessSequence
    **/
   template <typename TProcess1, typename TProcess2>
-  inline typename std::enable_if_t<
-      std::is_base_of_v<BaseProcess<typename std::decay_t<TProcess1>>,
-                        typename std::decay_t<TProcess1>> &&
-          std::is_base_of_v<BaseProcess<typename std::decay_t<TProcess2>>,
-                            typename std::decay_t<TProcess2>>,
-      ProcessSequence<TProcess1, TProcess2>>
+  typename std::enable_if_t<is_process_v<typename std::decay_t<TProcess1>> &&
+                                is_process_v<typename std::decay_t<TProcess2>>,
+                            ProcessSequence<TProcess1, TProcess2>>
   make_sequence(TProcess1&& vA, TProcess2&& vB) {
     return ProcessSequence<TProcess1, TProcess2>(vA, vB);
   }
@@ -211,10 +253,8 @@ namespace corsika {
    * \param vA needs to derive from BaseProcess or ProcessSequence
    **/
   template <typename TProcess>
-  inline typename std::enable_if_t<
-      std::is_base_of_v<BaseProcess<typename std::decay_t<TProcess>>,
-                        typename std::decay_t<TProcess>>,
-      ProcessSequence<TProcess, NullModel>>
+  typename std::enable_if_t<is_process_v<typename std::decay_t<TProcess>>,
+                            ProcessSequence<TProcess, NullModel>>
   make_sequence(TProcess&& vA) {
     return ProcessSequence<TProcess, NullModel>(vA, NullModel());
   }
@@ -226,11 +266,7 @@ namespace corsika {
   struct is_process_sequence<ProcessSequence<TProcess1, TProcess2>> : std::true_type {
     // only switch on for BaseProcesses
     template <typename std::enable_if_t<
-        std::is_base_of_v<BaseProcess<typename std::decay_t<TProcess1>>,
-                          typename std::decay_t<TProcess1>> &&
-            std::is_base_of_v<BaseProcess<typename std::decay_t<TProcess2>>,
-                              typename std::decay_t<TProcess2>>,
-        int>>
+        is_process_v<TProcess1> && is_process_v<TProcess2>, int>>
     is_process_sequence() {}
   };
 
diff --git a/corsika/framework/process/ProcessTraits.hpp b/corsika/framework/process/ProcessTraits.hpp
index 7b303d2d3ce3a9ce1363d2433e08832e80032f46..b08318093e7be4147e5408661b16c8feb1c3921e 100644
--- a/corsika/framework/process/ProcessTraits.hpp
+++ b/corsika/framework/process/ProcessTraits.hpp
@@ -16,6 +16,24 @@
 
 namespace corsika {
 
+  /**
+   * A traits marker to identify BaseProcess, thus any type of process
+   */
+  template <typename TProcess, typename Enable = void>
+  struct is_process : std::false_type {};
+
+  template <typename TProcess>
+  bool constexpr is_process_v = is_process<TProcess>::value;
+
+  /**
+   * A traits marker to identify ContinuousProcess
+   */
+  template <typename TProcess, typename Enable = void>
+  struct is_continuous_process : std::false_type {};
+
+  template <typename TProcess>
+  bool constexpr is_continuous_process_v = is_continuous_process<TProcess>::value;
+
   /**
    *  A traits marker to track which BaseProcess is also a ProcessSequence
    **/
@@ -44,4 +62,20 @@ namespace corsika {
   template <typename TClass>
   bool constexpr contains_stack_process_v = contains_stack_process<TClass>::value;
 
+  /**
+   * traits class to count ContinuousProcess-es, general version
+   **/
+  template <typename TProcess, int N = 0, typename Enable = void>
+  struct count_continuous {
+    static unsigned int constexpr count = N;
+  };
+
+  /**
+   * traits class to count any type of Process, general version
+   **/
+  template <typename TProcess, int N = 0, typename Enable = void>
+  struct count_processes {
+    static unsigned int constexpr count = N;
+  };
+
 } // namespace corsika
diff --git a/corsika/framework/process/SecondariesProcess.hpp b/corsika/framework/process/SecondariesProcess.hpp
index 0d6db4783e48e6f6485f42a8e7b62e0fb285f8cf..daa45cc396f817e623ef107c9fdffd24099b8cbe 100644
--- a/corsika/framework/process/SecondariesProcess.hpp
+++ b/corsika/framework/process/SecondariesProcess.hpp
@@ -28,7 +28,7 @@ namespace corsika {
     /// here starts the interface-definition part
     // -> enforce TDerived to implement DoSecondaries...
     template <typename TSecondaries>
-    inline void doSecondaries(TSecondaries&);
+    void doSecondaries(TSecondaries&);
   };
 
 } // namespace corsika
diff --git a/corsika/framework/process/StackProcess.hpp b/corsika/framework/process/StackProcess.hpp
index 77ca2a479cbc3404d372db701526a91895e6f2b6..7bf23c82c4b5e5a380e64ee8a3eb8632c0d2be2b 100644
--- a/corsika/framework/process/StackProcess.hpp
+++ b/corsika/framework/process/StackProcess.hpp
@@ -34,7 +34,7 @@ namespace corsika {
     /// here starts the interface-definition part
     // -> enforce TDerived to implement DoStack...
     template <typename TStack>
-    inline void doStack(TStack&);
+    void doStack(TStack&);
 
     int getStep() const { return iStep_; }
     bool checkStep() { return !((++iStep_) % nStep_); }
diff --git a/corsika/framework/process/SwitchProcessSequence.hpp b/corsika/framework/process/SwitchProcessSequence.hpp
index dab419ae5a5c66fda13eee9c0247c697e0cdcfd1..22da0facc5c5df27f38e03fadf02db014138cd60 100644
--- a/corsika/framework/process/SwitchProcessSequence.hpp
+++ b/corsika/framework/process/SwitchProcessSequence.hpp
@@ -16,6 +16,7 @@
 #include <corsika/framework/process/ProcessTraits.hpp>
 #include <corsika/framework/process/BoundaryCrossingProcess.hpp>
 #include <corsika/framework/process/ContinuousProcess.hpp>
+#include <corsika/framework/process/ContinuousProcessIndex.hpp>
 #include <corsika/framework/process/DecayProcess.hpp>
 #include <corsika/framework/process/InteractionProcess.hpp>
 #include <corsika/framework/process/ProcessReturn.hpp>
@@ -57,10 +58,19 @@ namespace corsika {
      since this makes no sense. The StackProcess acts on an entire
      particle stack and not on indiviidual particles.
 
+     Template parameters:
+      - TProcess1 is of type BaseProcess, either a dedicatd process, or a ProcessSequence
+      - TProcess2 is of type BaseProcess, either a dedicatd process, or a ProcessSequence
+      - IndexFirstProcess, IndexOfProcess1, IndexOfProcess2 are to count and index each
+  ContinuousProcess in the entire process-chain
+
      See also class \sa ProcessSequence
   **/
 
-  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TProcess1, typename TProcess2, typename TSelect,
+            int IndexFirstProcess = 0,
+            int IndexOfProcess1 = count_processes<TProcess1, IndexFirstProcess>::count,
+            int IndexOfProcess2 = count_processes<TProcess2, IndexOfProcess1>::count>
   class SwitchProcessSequence
       : public BaseProcess<SwitchProcessSequence<TProcess1, TProcess2, TSelect>> {
 
@@ -71,10 +81,10 @@ namespace corsika {
     static bool constexpr t2ProcSeq = is_process_sequence_v<process2_type>;
 
     // make sure only BaseProcess types TProcess1/2 are passed
-    static_assert(std::is_base_of_v<BaseProcess<process1_type>, process1_type>,
+    static_assert(is_process_v<process1_type>,
                   "can only use process derived from BaseProcess in "
                   "SwitchProcessSequence, for Process 1");
-    static_assert(std::is_base_of_v<BaseProcess<process2_type>, process2_type>,
+    static_assert(is_process_v<process2_type>,
                   "can only use process derived from BaseProcess in "
                   "SwitchProcessSequence, for Process 2");
 
@@ -122,48 +132,56 @@ namespace corsika {
                                      TVTNType const& to);
 
     template <typename TParticle, typename TTrack>
-    inline ProcessReturn doContinuous(TParticle& particle, TTrack& vT);
+    ProcessReturn doContinuous(TParticle& particle, TTrack& vT,
+                               ContinuousProcessIndex const limitId);
 
     template <typename TSecondaries>
-    inline void doSecondaries(TSecondaries& vS);
+    void doSecondaries(TSecondaries& vS);
 
     template <typename TParticle, typename TTrack>
-    inline LengthType getMaxStepLength(TParticle& particle, TTrack& vTrack);
+    ContinuousProcessStepLength getMaxStepLength(TParticle& particle, TTrack& vTrack);
 
     template <typename TParticle>
-    inline GrammageType getInteractionLength(TParticle&& particle) {
+    GrammageType getInteractionLength(TParticle&& particle) {
       return 1. / getInverseInteractionLength(particle);
     }
 
     template <typename TParticle>
-    inline InverseGrammageType getInverseInteractionLength(TParticle&& particle);
+    InverseGrammageType getInverseInteractionLength(TParticle&& particle);
 
     template <typename TSecondaryView>
-    inline ProcessReturn selectInteraction(
+    ProcessReturn selectInteraction(
         TSecondaryView& view, [[maybe_unused]] InverseGrammageType lambda_inv_select,
         [[maybe_unused]] InverseGrammageType lambda_inv_sum =
             InverseGrammageType::zero());
 
     template <typename TParticle>
-    inline TimeType getLifetime(TParticle&& particle) {
+    TimeType getLifetime(TParticle&& particle) {
       return 1. / getInverseLifetime(particle);
     }
 
     template <typename TParticle>
-    inline InverseTimeType getInverseLifetime(TParticle&& particle);
+    InverseTimeType getInverseLifetime(TParticle&& particle);
 
     // select decay process
     template <typename TSecondaryView>
-    inline ProcessReturn selectDecay(
+    ProcessReturn selectDecay(
         TSecondaryView& view, [[maybe_unused]] InverseTimeType decay_inv_select,
         [[maybe_unused]] InverseTimeType decay_inv_sum = InverseTimeType::zero());
 
+    /**
+     * static counter to uniquely index (count) all ContinuousProcess in switch sequence.
+     **/
+    static unsigned int constexpr getNumberOfProcesses() { return numberOfProcesses_; }
+
   private:
     TSelect select_; /// selector functor to switch between branch a and b, this is a
                      /// reference, if possible
 
     TProcess1 A_; /// process branch a, this is a reference, if possible
     TProcess2 B_; /// process branch b, this is a reference, if possible
+
+    static unsigned int constexpr numberOfProcesses_ = IndexOfProcess2; // static counter
   };
 
   /**
@@ -178,12 +196,9 @@ namespace corsika {
    **/
 
   template <typename TProcess1, typename TProcess2, typename TSelect>
-  inline typename std::enable_if_t<
-      std::is_base_of_v<BaseProcess<typename std::decay_t<TProcess1>>,
-                        typename std::decay_t<TProcess1>> &&
-          std::is_base_of_v<BaseProcess<typename std::decay_t<TProcess2>>,
-                            typename std::decay_t<TProcess2>>,
-      SwitchProcessSequence<TProcess1, TProcess2, TSelect>>
+  typename std::enable_if_t<is_process_v<typename std::decay_t<TProcess1>> &&
+                                is_process_v<typename std::decay_t<TProcess2>>,
+                            SwitchProcessSequence<TProcess1, TProcess2, TSelect>>
   make_select(TProcess1&& vA, TProcess2&& vB, TSelect selector) {
     return SwitchProcessSequence<TProcess1, TProcess2, TSelect>(vA, vB, selector);
   }
diff --git a/corsika/framework/random/RNGManager.hpp b/corsika/framework/random/RNGManager.hpp
index 2dd6160e993d5a39b2aeb35f62be874047372cf3..0e687adf45d3a93dca4f2e0975d0b9b7d5b82c0d 100644
--- a/corsika/framework/random/RNGManager.hpp
+++ b/corsika/framework/random/RNGManager.hpp
@@ -43,36 +43,35 @@ namespace corsika {
      *
      * \throws sth. when stream \a pModuleName is already registered
      */
-    inline void registerRandomStream(string_type const& streamName);
+    void registerRandomStream(string_type const& streamName);
 
     /*!
      * returns the pre-stored stream of given name \a pStreamName if
      * available
      */
-    inline prng_type& getRandomStream(string_type const& streamName);
+    prng_type& getRandomStream(string_type const& streamName);
 
     /*!
      * Check whether a stream has been registered.
      */
-    inline bool isRegistered(string_type const& streamName) const;
+    bool isRegistered(string_type const& streamName) const;
 
     /*!
      * dumps the names and states of all registered random-number streams
      * into a std::stringstream.
      */
-    inline std::stringstream dumpState() const;
+    std::stringstream dumpState() const;
 
     /**
      * Set explicit seeds for all currently registered streams. The actual seed values
      * are incremented from \a vSeed.
      */
-    inline void seedAll(seed_type seed);
+    void seedAll(seed_type seed);
 
     /**
      * Set seeds for all currently registered streams.
      */
-    inline void seedAll(
-        void); //!< seed all currently registered streams with "real" randomness
+    void seedAll(void); //!< seed all currently registered streams with "real" randomness
 
     /**
      * @fn const streams_type getRngs&()const
@@ -82,7 +81,7 @@ namespace corsika {
      * @post
      * @return RNGManager::streams_type
      */
-    inline const streams_type& getRngs() const { return rngs_; }
+    const streams_type& getRngs() const { return rngs_; }
 
     /**
      * @fn const seeds_type getSeeds&()const
@@ -92,7 +91,7 @@ namespace corsika {
      * @post
      * @return RNGManager::seeds_type
      */
-    inline const seeds_type& getSeeds() const { return seeds_; }
+    const seeds_type& getSeeds() const { return seeds_; }
 
     /**
      * @fn streams_type Rngs()
@@ -102,7 +101,7 @@ namespace corsika {
      * @post
      * @return RNGManager::streams_type&
      */
-    inline streams_type Rngs() { return rngs_; }
+    streams_type Rngs() { return rngs_; }
 
     /**
      * @fn seeds_type Seeds&()
@@ -112,7 +111,7 @@ namespace corsika {
      * @post
      * @return RNGManager::seeds_type&
      */
-    inline seeds_type& Seeds() { return seeds_; }
+    seeds_type& getSeeds() { return seeds_; }
 
   protected:
     RNGManager() = default;
diff --git a/corsika/framework/random/UniformRealDistribution.hpp b/corsika/framework/random/UniformRealDistribution.hpp
index 781a23717a6fef420ee8ed3a8e8d658fa4c5e768..76f147de53008e534c76da5c1157d319c0c35e52 100644
--- a/corsika/framework/random/UniformRealDistribution.hpp
+++ b/corsika/framework/random/UniformRealDistribution.hpp
@@ -36,7 +36,7 @@ namespace corsika {
         : min_(other.getMin())
         , max_(other.getMax()) {}
 
-    inline UniformRealDistribution<value_type>& operator=(
+    UniformRealDistribution<value_type>& operator=(
         UniformRealDistribution<value_type> const& other) {
       if (this == &other) return *this;
       min_ = other.getMin();
@@ -52,7 +52,7 @@ namespace corsika {
      * @post
      * @return quantity_type
      */
-    inline value_type getMax() const { return max_; }
+    value_type getMax() const { return max_; }
 
     /**
      * @fn void setMax(quantity_type)
@@ -62,7 +62,7 @@ namespace corsika {
      * @post
      * @param vMax
      */
-    inline void setMax(value_type const& pmax) { max_ = pmax; }
+    void setMax(value_type const& pmax) { max_ = pmax; }
 
     /**
      * @fn quantity_type getMin()const
@@ -72,7 +72,7 @@ namespace corsika {
      * @post
      * @return
      */
-    inline value_type getMin() const { return min_; }
+    value_type getMin() const { return min_; }
 
     /**
      * @fn void setMin(quantity_type)
@@ -82,7 +82,7 @@ namespace corsika {
      * @post
      * @param vMin
      */
-    inline void setMin(value_type const& pmin) { min_ = pmin; }
+    void setMin(value_type const& pmin) { min_ = pmin; }
 
     /**
      * @fn quantity_type operator ()(Generator&)
@@ -95,7 +95,7 @@ namespace corsika {
      * @return quantity_type
      */
     template <class Generator>
-    inline value_type operator()(Generator& g) {
+    value_type operator()(Generator& g) {
       return min_ + dist_(g) * (max_ - min_);
     }
 
diff --git a/corsika/framework/stack/CombinedStack.hpp b/corsika/framework/stack/CombinedStack.hpp
index d452d8f8b469f7a79317036331d3c345f4d2b15e..e83e33a2f07e7a6f86de83bd516141ee8f77d398 100644
--- a/corsika/framework/stack/CombinedStack.hpp
+++ b/corsika/framework/stack/CombinedStack.hpp
@@ -67,21 +67,20 @@ namespace corsika {
      */
 
     template <typename... TArgs1>
-    inline void setParticleData(std::tuple<TArgs1...> const vA);
+    void setParticleData(std::tuple<TArgs1...> const vA);
 
     template <typename... TArgs1, typename... TArgs2>
-    inline void setParticleData(std::tuple<TArgs1...> const vA,
-                                std::tuple<TArgs2...> const vB);
+    void setParticleData(std::tuple<TArgs1...> const vA, std::tuple<TArgs2...> const vB);
 
     template <typename... TArgs1>
-    inline void setParticleData(pi_a_type& p, std::tuple<TArgs1...> const vA);
+    void setParticleData(pi_a_type& p, std::tuple<TArgs1...> const vA);
 
     template <typename... TArgs1, typename... TArgs2>
-    inline void setParticleData(pi_c_type& p, std::tuple<TArgs1...> const vA,
-                                std::tuple<TArgs2...> const vB);
+    void setParticleData(pi_c_type& p, std::tuple<TArgs1...> const vA,
+                         std::tuple<TArgs2...> const vB);
     ///@}
 
-    inline std::string asString() const;
+    std::string asString() const;
 
   protected:
   };
@@ -99,24 +98,24 @@ namespace corsika {
   struct CombinedStackImpl : public Stack1Impl, public Stack2Impl {
 
   public:
-    inline void clear();
+    void clear();
 
-    inline unsigned int getSize() const { return Stack1Impl::getSize(); }
-    inline unsigned int getCapacity() const { return Stack1Impl::getCapacity(); }
+    unsigned int getSize() const { return Stack1Impl::getSize(); }
+    unsigned int getCapacity() const { return Stack1Impl::getCapacity(); }
 
     /**
      *   Function to copy particle at location i1 in stack to i2
      */
-    inline void copy(const unsigned int i1, const unsigned int i2);
+    void copy(const unsigned int i1, const unsigned int i2);
 
     /**
      *   Function to copy particle at location i2 in stack to i1
      */
-    inline void swap(const unsigned int i1, const unsigned int i2);
+    void swap(const unsigned int i1, const unsigned int i2);
 
-    inline void incrementSize();
+    void incrementSize();
 
-    inline void decrementSize();
+    void decrementSize();
 
   }; // end class CombinedStackImpl
 
diff --git a/corsika/framework/stack/ParticleBase.hpp b/corsika/framework/stack/ParticleBase.hpp
index c7b086c5cdfe3eb612049e73da194b0e1b4431bc..2c62285d5fbcd26d4b06841809cd2a0f8bf8a602 100644
--- a/corsika/framework/stack/ParticleBase.hpp
+++ b/corsika/framework/stack/ParticleBase.hpp
@@ -58,13 +58,13 @@ namespace corsika {
      * Delete this particle on the stack. The corresponding iterator
      * will be invalidated by this operation
      */
-    inline void erase() { this->getIterator().getStack().erase(this->getIterator()); }
+    void erase() { this->getIterator().getStack().erase(this->getIterator()); }
 
     /**
      * Method to retrieve the status of the Particle. Is it already deleted? Or not.
      */
 
-    inline bool isErased() const {
+    bool isErased() const {
       return this->getIterator().getStack().isErased(this->getIterator());
     }
 
@@ -74,7 +74,7 @@ namespace corsika {
      * function description in the user defined ParticleInterface::AddSecondary(...)
      */
     template <typename... TArgs>
-    inline stack_iterator_type addSecondary(const TArgs... args) {
+    stack_iterator_type addSecondary(const TArgs... args) {
 
       return this->getStack().addSecondary(this->getIterator(), args...);
     }
@@ -84,11 +84,11 @@ namespace corsika {
     /**
      * return the corresponding StackIterator for this particle
      */
-    inline stack_iterator_type& getIterator() {
+    stack_iterator_type& getIterator() {
       return static_cast<stack_iterator_type&>(*this);
     }
 
-    inline const stack_iterator_type& getIterator() const {
+    const stack_iterator_type& getIterator() const {
       return static_cast<const stack_iterator_type&>(*this);
     }
 
@@ -99,21 +99,19 @@ namespace corsika {
         and getStackData to retrieve data
         @{
     */
-    inline auto& getStackData() { return this->getIterator().getStackData(); }
+    auto& getStackData() { return this->getIterator().getStackData(); }
 
-    inline const auto& getStackData() const { return this->getIterator().getStackData(); }
+    const auto& getStackData() const { return this->getIterator().getStackData(); }
 
-    inline auto& getStack() { return this->getIterator().getStack(); }
+    auto& getStack() { return this->getIterator().getStack(); }
 
-    inline const auto& getStack() const { return this->getIterator().getStack(); }
+    const auto& getStack() const { return this->getIterator().getStack(); }
 
     /**
      * return the index number of the underlying iterator object
      */
 
-    inline std::size_t getIndex() const {
-      return this->getIterator().getIndexFromIterator();
-    }
+    std::size_t getIndex() const { return this->getIterator().getIndexFromIterator(); }
     ///@}
   };
 
diff --git a/corsika/framework/stack/SecondaryView.hpp b/corsika/framework/stack/SecondaryView.hpp
index e18531b3154e250ab44c33014a92355e0b9a877a..9e5f60324c17f7a4feb144a5a8b638b74fd88a8c 100644
--- a/corsika/framework/stack/SecondaryView.hpp
+++ b/corsika/framework/stack/SecondaryView.hpp
@@ -167,7 +167,7 @@ namespace corsika {
      * SecondaryView is derived from. This projectile should not be
      * used to modify the Stack!
      */
-    inline stack_value_iterator parent()
+    stack_value_iterator parent()
         const { // todo: check if this can't be Conststack_value_iterator
       return stack_value_iterator(inner_stack_, projectile_index_);
     }
@@ -177,7 +177,7 @@ namespace corsika {
      * SecondaryView is derived from. This projectile should not be
      * used to modify the Stack!
      */
-    inline stack_value_iterator asNewParent() const {
+    stack_value_iterator asNewParent() const {
       return stack_value_iterator(inner_stack_, projectile_index_);
     }
 
@@ -185,26 +185,36 @@ namespace corsika {
      * This return a projectile of this SecondaryView, which can be
      * used to modify the SecondaryView
      */
-    inline stack_view_iterator getProjectile() {
+    stack_view_iterator getProjectile() {
       // NOTE: 0 is special marker here for PROJECTILE, see getIndexFromIterator
       return stack_view_iterator(*this, 0);
     }
+
+    /**
+     * This return a projectile of this SecondaryView, which can be
+     * used to modify the SecondaryView
+     */
+    const_stack_view_iterator getProjectile() const {
+      // NOTE: 0 is special marker here for PROJECTILE, see getIndexFromIterator
+      return const_stack_view_iterator(*this, 0);
+    }
+
     /**
      * Method to add a new secondary particle on this SecondaryView
      */
     template <typename... Args>
-    inline stack_view_iterator addSecondary(const Args... v);
+    stack_view_iterator addSecondary(const Args... v);
 
     /**
      * overwrite Stack::getSize to return actual number of secondaries
      */
-    inline unsigned int getSize() const { return indices_.size(); }
+    unsigned int getSize() const { return indices_.size(); }
 
-    inline unsigned int getEntries() const {
+    unsigned int getEntries() const {
       return getSize() - inner_stack_reference_type::getErased();
     }
 
-    inline bool isEmpty() const { return getEntries() == 0; }
+    bool isEmpty() const { return getEntries() == 0; }
 
     /**
      * @name These are functions required by std containers and std loops
@@ -214,48 +224,46 @@ namespace corsika {
      */
     // NOTE: the "+1" is since "0" is special marker here for PROJECTILE, see
     // getIndexFromIterator
-    inline stack_view_iterator begin();
+    stack_view_iterator begin();
 
-    inline stack_view_iterator end() { return stack_view_iterator(*this, getSize() + 1); }
+    stack_view_iterator end() { return stack_view_iterator(*this, getSize() + 1); }
 
-    inline stack_view_iterator last();
+    stack_view_iterator last();
 
-    inline const_stack_view_iterator begin() const;
+    const_stack_view_iterator begin() const;
 
-    inline const_stack_view_iterator end() const {
+    const_stack_view_iterator end() const {
       return const_stack_view_iterator(*this, getSize() + 1);
     }
 
-    inline const_stack_view_iterator last() const;
+    const_stack_view_iterator last() const;
 
-    inline const_stack_view_iterator cbegin() const;
+    const_stack_view_iterator cbegin() const;
 
-    inline const_stack_view_iterator cend() const {
+    const_stack_view_iterator cend() const {
       return const_stack_view_iterator(*this, getSize());
     }
 
-    inline const_stack_view_iterator clast() const;
+    const_stack_view_iterator clast() const;
 
-    inline stack_view_iterator at(unsigned int i) {
-      return stack_view_iterator(*this, i);
-    }
+    stack_view_iterator at(unsigned int i) { return stack_view_iterator(*this, i); }
 
-    inline const_stack_view_iterator at(unsigned int i) const {
+    const_stack_view_iterator at(unsigned int i) const {
       return const_stack_view_iterator(*this, i);
     }
 
-    inline stack_view_iterator first() { return stack_view_iterator{*this, 0}; }
+    stack_view_iterator first() { return stack_view_iterator{*this, 0}; }
 
-    inline const_stack_view_iterator cfirst() const {
+    const_stack_view_iterator cfirst() const {
       return const_stack_view_iterator{*this, 0};
     }
     /// @}
 
-    inline void swap(stack_view_iterator a, stack_view_iterator b);
+    void swap(stack_view_iterator a, stack_view_iterator b);
 
-    inline void copy(stack_view_iterator a, stack_view_iterator b);
+    void copy(stack_view_iterator a, stack_view_iterator b);
 
-    inline void copy(const_stack_view_iterator a, stack_view_iterator b);
+    void copy(const_stack_view_iterator a, stack_view_iterator b);
 
     /**
      * need overwrite Stack::Delete, since we want to call
@@ -269,13 +277,13 @@ namespace corsika {
      * remove the last particle.
      *
      */
-    inline void erase(stack_view_iterator p);
+    void erase(stack_view_iterator p);
 
     /**
      * return next particle from stack, need to overwrtie Stack::getNextParticle to get
      * right reference
      */
-    inline stack_view_iterator getNextParticle() {
+    stack_view_iterator getNextParticle() {
       while (purgeLastIfDeleted()) {}
       return last();
     }
@@ -286,21 +294,21 @@ namespace corsika {
      * need to re-implement for SecondaryView since stack_view_iterator types are a bit
      * different
      */
-    inline bool isErased(const stack_view_iterator& p) const {
+    bool isErased(const stack_view_iterator& p) const {
       return isErased(p.getIndex() - 1);
     }
 
-    inline bool isErased(const const_stack_view_iterator& p) const {
+    bool isErased(const const_stack_view_iterator& p) const {
       return isErased(p.getIndex() - 1);
     }
     /**
      * delete this particle
      */
-    inline bool isErased(const ParticleInterfaceType& p) const {
+    bool isErased(const ParticleInterfaceType& p) const {
       return isErased(p.getIterator());
     }
 
-    inline bool isDeleted(const const_stack_view_iterator& p) const {
+    bool isDeleted(const const_stack_view_iterator& p) const {
       return isDeleted(p.getIndex() - 1);
     }
     /**
@@ -308,7 +316,7 @@ namespace corsika {
      * if it was marked as deleted before. If this is not the case,
      * the function will just return false and do nothing.
      */
-    inline bool purgeLastIfDeleted();
+    bool purgeLastIfDeleted();
 
     /**
      * Function to ultimatively remove all entries from the stack
@@ -318,9 +326,9 @@ namespace corsika {
      * "gaps" in the stack are filled with entries from the back
      * (copied).
      */
-    inline void purge();
+    void purge();
 
-    inline std::string asString() const;
+    std::string asString() const;
 
   protected:
     friend class StackIteratorInterface<
@@ -343,11 +351,11 @@ namespace corsika {
      * stack_view_iterator::addSecondary via ParticleBase
      */
     template <typename... Args>
-    inline stack_view_iterator addSecondary(stack_view_iterator& proj, const Args... v);
+    stack_view_iterator addSecondary(stack_view_iterator& proj, const Args... v);
 
     // forward to inner stack
     // this also checks the allowed bounds of 'i'
-    inline bool isErased(unsigned int i) const {
+    bool isErased(unsigned int i) const {
       if (i >= indices_.size()) return false;
       return inner_stack_.isErased(getIndexFromIterator(i + 1));
     }
@@ -357,7 +365,7 @@ namespace corsika {
      * function the conversion form iterator-index to stack-index is
      * performed.
      */
-    inline unsigned int getIndexFromIterator(const unsigned int vI) const {
+    unsigned int getIndexFromIterator(const unsigned int vI) const {
       // this is too much: CORSIKA_LOG_TRACE("SecondaryView::getIndexFromIterator({})={}",
       // vI, (vI?indices_[vI-1]:projectile_index_));
       if (vI == 0) return projectile_index_;
@@ -389,7 +397,7 @@ namespace corsika {
      * is of course a reference into the SecondaryView itself.
      */
     template <typename Particle>
-    inline void new_secondary(Particle&&) const {
+    void new_secondary(Particle&&) const {
       CORSIKA_LOG_TRACE("DefaultSecondaryProducer::new_secondary(Particle&&)");
     }
 
@@ -402,7 +410,7 @@ namespace corsika {
      * itself.
      */
     template <typename Particle>
-    inline DefaultSecondaryProducer(Particle const&) {
+    DefaultSecondaryProducer(Particle const&) {
 
       CORSIKA_LOG_TRACE("DefaultSecondaryProducer::DefaultSecondaryProducer(Particle&)");
     }
diff --git a/corsika/framework/stack/Stack.hpp b/corsika/framework/stack/Stack.hpp
index e72dc94dbeb304258aab5206fb7e4552474725d4..2ff173fb3ccabcd8d5944ce4dc7452cd96314f80 100644
--- a/corsika/framework/stack/Stack.hpp
+++ b/corsika/framework/stack/Stack.hpp
@@ -152,89 +152,89 @@ namespace corsika {
      * @name Most generic proxy methods for StackData data_
      * @{
      */
-    inline unsigned int getCapacity() const { return data_.getCapacity(); }
+    unsigned int getCapacity() const { return data_.getCapacity(); }
 
-    inline unsigned int getErased() const { return nDeleted_; }
+    unsigned int getErased() const { return nDeleted_; }
 
-    inline unsigned int getEntries() const { return getSize() - getErased(); }
+    unsigned int getEntries() const { return getSize() - getErased(); }
 
     template <typename... TArgs>
-    inline void clear(TArgs... args);
+    void clear(TArgs... args);
     ///@}
 
     /**
      * @name These are functions required by std containers and std loops
      * @{
      */
-    inline stack_iterator_type begin();
+    stack_iterator_type begin();
 
-    inline stack_iterator_type end();
+    stack_iterator_type end();
 
-    inline stack_iterator_type last();
+    stack_iterator_type last();
 
-    inline const_stack_iterator_type begin() const;
+    const_stack_iterator_type begin() const;
 
-    inline const_stack_iterator_type end() const;
+    const_stack_iterator_type end() const;
 
-    inline const_stack_iterator_type last() const;
+    const_stack_iterator_type last() const;
 
-    inline const_stack_iterator_type cbegin() const;
+    const_stack_iterator_type cbegin() const;
 
-    inline const_stack_iterator_type cend() const;
+    const_stack_iterator_type cend() const;
 
-    inline const_stack_iterator_type clast() const;
+    const_stack_iterator_type clast() const;
 
-    inline stack_iterator_type at(unsigned int i);
+    stack_iterator_type at(unsigned int i);
 
-    inline const_stack_iterator_type at(unsigned int i) const;
+    const_stack_iterator_type at(unsigned int i) const;
 
-    inline stack_iterator_type first();
+    stack_iterator_type first();
 
-    inline const_stack_iterator_type cfirst() const;
+    const_stack_iterator_type cfirst() const;
 
-    inline stack_iterator_type getNextParticle();
+    stack_iterator_type getNextParticle();
 
     /**
      * increase stack size, create new particle at end of stack
      */
     template <typename... TArgs>
-    inline stack_iterator_type addParticle(const TArgs... v);
+    stack_iterator_type addParticle(const TArgs... v);
 
-    inline void swap(stack_iterator_type a, stack_iterator_type b);
+    void swap(stack_iterator_type a, stack_iterator_type b);
 
-    inline void copy(stack_iterator_type a, stack_iterator_type b);
+    void copy(stack_iterator_type a, stack_iterator_type b);
 
-    inline void copy(const_stack_iterator_type a, stack_iterator_type b);
+    void copy(const_stack_iterator_type a, stack_iterator_type b);
 
-    inline void erase(stack_iterator_type p);
+    void erase(stack_iterator_type p);
     /**
      * delete this particle
      */
 
-    inline void erase(particle_interface_type p);
+    void erase(particle_interface_type p);
 
     /**
      * check if there are no further non-deleted particles on stack
      */
-
-    inline bool isEmpty();
+    bool isEmpty();
 
     /**
      * check if this particle was already deleted
      */
 
-    inline bool isErased(const stack_iterator_type& p) const;
+    bool isErased(const stack_iterator_type& p) const;
 
-    inline bool isErased(const const_stack_iterator_type& p) const;
+    bool isErased(const const_stack_iterator_type& p) const;
 
-    inline bool isErased(const particle_interface_type& p) const;
+    bool isErased(const particle_interface_type& p) const;
 
     /**
      * Function to ultimatively remove the last entry from the stack,
      * if it was marked as deleted before. If this is not the case,
      * the function will just return false and do nothing.
      */
-    inline bool purgeLastIfDeleted();
+    bool purgeLastIfDeleted();
+
     /**
      * Function to ultimatively remove all entries from the stack
      * marked as deleted.
@@ -243,11 +243,11 @@ namespace corsika {
      * "gaps" in the stack are filled with entries from the back
      * (copied).
      */
-    inline void purge();
+    void purge();
 
-    inline unsigned int getSize() const;
+    unsigned int getSize() const;
 
-    inline std::string asString() const;
+    std::string asString() const;
 
   protected:
     /**
@@ -258,22 +258,21 @@ namespace corsika {
      * StackIterator::AddSecondary via ParticleBase
      */
     template <typename... TArgs>
-    inline stack_iterator_type addSecondary(stack_iterator_type& parent,
-                                            const TArgs... v);
+    stack_iterator_type addSecondary(stack_iterator_type& parent, const TArgs... v);
 
-    inline void swap(unsigned int const a, unsigned int const b);
+    void swap(unsigned int const a, unsigned int const b);
 
-    inline void copy(unsigned int const a, unsigned int const b);
+    void copy(unsigned int const a, unsigned int const b);
 
-    inline bool isErased(unsigned int const i) const;
+    bool isErased(unsigned int const i) const;
 
-    inline void erase(unsigned int const i);
+    void erase(unsigned int const i);
 
     /**
      * will remove from storage the element i. This is a helper
      * function for SecondaryView.
      */
-    inline void purge(unsigned int i);
+    void purge(unsigned int i);
 
     /**
      * Function to perform eventual transformation from
@@ -281,15 +280,15 @@ namespace corsika {
      * StackData data_. By default (and in almost all cases) this
      * should just be identiy. See class SecondaryView for an alternative implementation.
      */
-    inline unsigned int getIndexFromIterator(const unsigned int vI) const;
+    unsigned int getIndexFromIterator(const unsigned int vI) const;
     /**
      * @name Return reference to StackData object data_ for data access
      * @{
      */
 
-    inline value_type& getStackData();
+    value_type& getStackData();
 
-    inline const value_type& getStackData() const;
+    const value_type& getStackData() const;
 
     friend class StackIteratorInterface<value_type, MParticleInterface, Stack>;
     friend class ConstStackIteratorInterface<value_type, MParticleInterface, Stack>;
diff --git a/corsika/framework/stack/StackIteratorInterface.hpp b/corsika/framework/stack/StackIteratorInterface.hpp
index f6240e87800d9232bb100587d68dde50850cb2a5..76815746af5ab15ce5aa61fc48c2f2249f987b32 100644
--- a/corsika/framework/stack/StackIteratorInterface.hpp
+++ b/corsika/framework/stack/StackIteratorInterface.hpp
@@ -146,14 +146,14 @@ namespace corsika {
     /** @name Iterator interface
         @{
     **/
-    inline StackIteratorInterface& operator++() {
+    StackIteratorInterface& operator++() {
       do {
         ++index_;
       } while (
           getStack().isErased(*this)); // this also check the allowed bounds of index_
       return *this;
     }
-    inline StackIteratorInterface operator++(int) {
+    StackIteratorInterface operator++(int) {
       StackIteratorInterface tmp(*this);
       do {
         ++index_;
@@ -161,19 +161,19 @@ namespace corsika {
           getStack().isErased(*this)); // this also check the allowed bounds of index_
       return tmp;
     }
-    inline StackIteratorInterface operator+(int delta) const {
+    StackIteratorInterface operator+(int delta) const {
       return StackIteratorInterface(*data_, index_ + delta);
     }
-    inline bool operator==(StackIteratorInterface const& rhs) const {
+    bool operator==(StackIteratorInterface const& rhs) const {
       return index_ == rhs.index_;
     }
-    inline bool operator!=(StackIteratorInterface const& rhs) const {
+    bool operator!=(StackIteratorInterface const& rhs) const {
       return index_ != rhs.index_;
     }
-    inline bool operator==(
+    bool operator==(
         const ConstStackIteratorInterface<TStackData, TParticleInterface, TStackType>&
             rhs) const; // implemented below
-    inline bool operator!=(
+    bool operator!=(
         const ConstStackIteratorInterface<TStackData, TParticleInterface, TStackType>&
             rhs) const; // implemented below
 
@@ -181,7 +181,7 @@ namespace corsika {
      * Convert iterator to value type, where value type is the user-provided particle
      * readout class
      **/
-    inline particle_interface_type& operator*() {
+    particle_interface_type& operator*() {
       return static_cast<particle_interface_type&>(*this);
     }
 
@@ -189,7 +189,7 @@ namespace corsika {
      * Convert iterator to const value type, where value type is the user-provided
      * particle readout class
      **/
-    inline particle_interface_type const& operator*() const {
+    particle_interface_type const& operator*() const {
       return static_cast<particle_interface_type const&>(*this);
     }
     ///@}
@@ -200,17 +200,17 @@ namespace corsika {
      * @{
      **/
     /// Get current particle index
-    inline unsigned int getIndex() const { return index_; }
+    unsigned int getIndex() const { return index_; }
     /// Get current particle Stack object
-    inline TStackType& getStack() { return *data_; }
+    TStackType& getStack() { return *data_; }
     /// Get current particle const Stack object
-    inline TStackType const& getStack() const { return *data_; }
+    TStackType const& getStack() const { return *data_; }
     /// Get current user particle TStackData object
-    inline TStackData& getStackData() { return data_->getStackData(); }
+    TStackData& getStackData() { return data_->getStackData(); }
     /// Get current const user particle TStackData object
-    inline TStackData const& getStackData() const { return data_->getStackData(); }
+    TStackData const& getStackData() const { return data_->getStackData(); }
     /// Get data index as mapped in Stack class
-    inline unsigned int getIndexFromIterator() const {
+    unsigned int getIndexFromIterator() const {
       return data_->getIndexFromIterator(index_);
     }
     ///@}
@@ -326,11 +326,11 @@ namespace corsika {
         Only the const versions for read-only access
         @{
      */
-    inline unsigned int getIndex() const { return index_; }
-    inline TStackType const& getStack() const { return *data_; }
-    inline TStackData const& getStackData() const { return data_->getStackData(); }
+    unsigned int getIndex() const { return index_; }
+    TStackType const& getStack() const { return *data_; }
+    TStackData const& getStackData() const { return data_->getStackData(); }
     /// Get data index as mapped in Stack class
-    inline unsigned int getIndexFromIterator() const {
+    unsigned int getIndexFromIterator() const {
       return data_->getIndexFromIterator(index_);
     }
     ///@}
diff --git a/corsika/framework/utility/COMBoost.hpp b/corsika/framework/utility/COMBoost.hpp
index 63b8f0a4420f258569bebcc44a2e3f361f6ba6fe..befd3025035105a184f33c1c2a872c750364665e 100644
--- a/corsika/framework/utility/COMBoost.hpp
+++ b/corsika/framework/utility/COMBoost.hpp
@@ -35,16 +35,16 @@ namespace corsika {
 
     //! transforms a 4-momentum from lab frame to the center-of-mass frame
     template <typename FourVector>
-    inline FourVector toCoM(FourVector const& p) const;
+    FourVector toCoM(FourVector const& p) const;
 
     //! transforms a 4-momentum from the center-of-mass frame back to lab frame
     template <typename FourVector>
-    inline FourVector fromCoM(FourVector const& p) const;
+    FourVector fromCoM(FourVector const& p) const;
 
-    inline CoordinateSystemPtr getRotatedCS() const;
+    CoordinateSystemPtr getRotatedCS() const;
 
   protected:
-    inline void setBoost(double coshEta, double sinhEta);
+    void setBoost(double coshEta, double sinhEta);
 
   private:
     Eigen::Matrix2d boost_;
diff --git a/corsika/framework/utility/SaveBoostHistogram.hpp b/corsika/framework/utility/SaveBoostHistogram.hpp
index 90a5621bd54715964a117b041380d20470cf4af3..14e0379966c447cd0c2f8cfa7ccffcf3c29a0478 100644
--- a/corsika/framework/utility/SaveBoostHistogram.hpp
+++ b/corsika/framework/utility/SaveBoostHistogram.hpp
@@ -24,8 +24,8 @@ namespace corsika {
    * runtime_error
    */
   template <class Axes, class Storage>
-  inline void save_hist(boost::histogram::histogram<Axes, Storage> const& h,
-                        std::string const& filename, bool overwrite = true);
+  void save_hist(boost::histogram::histogram<Axes, Storage> const& h,
+                 std::string const& filename, bool overwrite = true);
 } // namespace corsika
 
 #include <corsika/detail/framework/utility/SaveBoostHistogram.inl>
diff --git a/corsika/framework/utility/Singleton.hpp b/corsika/framework/utility/Singleton.hpp
index 78a7df97b6c0aa3c221a5404eaa68dcfde6afce6..d67fca048ab759523e65ff5dbb5298732380cb4e 100644
--- a/corsika/framework/utility/Singleton.hpp
+++ b/corsika/framework/utility/Singleton.hpp
@@ -8,8 +8,6 @@
 
 #pragma once
 
-//#define OFFLINE_USE_GAMMA_SINGLETON
-
 namespace corsika {
   /**
    * \class Singleton Singleton.h utl/Singleton.h
diff --git a/corsika/framework/utility/detail/COMBoost.inl b/corsika/framework/utility/detail/COMBoost.inl
deleted file mode 100644
index aa6af48349ed06df019e709eaadf2d8693b56289..0000000000000000000000000000000000000000
--- a/corsika/framework/utility/detail/COMBoost.inl
+++ /dev/null
@@ -1,125 +0,0 @@
-/*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * This software is distributed under the terms of the GNU General Public
- * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
- * the license.
- */
-
-#pragma once
-
-#include <corsika/framework/geometry/CoordinateSystem.hpp>
-#include <corsika/framework/geometry/FourVector.hpp>
-#include <corsika/framework/geometry/FourVector.hpp>
-#include <corsika/framework/geometry/Vector.hpp>
-#include <corsika/framework/core/PhysicalUnits.hpp>
-#include <corsika/framework/utility/sgn.hpp>
-#include <Eigen/Dense>
-#include "../../core/PhysicalUnits.hpp"
-
-namespace corsika::utl {
-
-  auto const& COMBoost::GetRotationMatrix() const { return fRotation; }
-
-  //! transforms a 4-momentum from lab frame to the center-of-mass frame
-  template <typename FourVector>
-  FourVector COMBoost::toCoM(const FourVector& p) const {
-    using namespace corsika::units::si;
-    auto pComponents = p.GetSpaceLikeComponents().GetComponents(rotatedCS_);
-    Eigen::Vector3d eVecRotated = pComponents.eVector;
-    Eigen::Vector2d lab;
-
-    lab << (p.GetTimeLikeComponent() * (1 / 1_GeV)),
-        (eVecRotated(2) * (1 / 1_GeV).magnitude());
-
-    auto const boostedZ = boost_ * lab;
-    auto const E_CoM = boostedZ(0) * 1_GeV;
-
-    eVecRotated(2) = boostedZ(1) * (1_GeV).magnitude();
-
-    return FourVector(E_CoM,
-                      corsika::geometry::Vector<hepmomentum_d>(rotatedCS_, eVecRotated));
-  }
-
-  //! transforms a 4-momentum from the center-of-mass frame back to lab frame
-  template <typename FourVector>
-  FourVector COMBoost::fromCoM(const FourVector& p) const {
-    using namespace corsika::units::si;
-    auto pCM = p.GetSpaceLikeComponents().GetComponents(rotatedCS_);
-    auto const Ecm = p.GetTimeLikeComponent();
-
-    Eigen::Vector2d com;
-    com << (Ecm * (1 / 1_GeV)), (pCM.eVector(2) * (1 / 1_GeV).magnitude());
-
-    C8LOG_TRACE(
-        "COMBoost::fromCoM Ecm={} GeV"
-        " pcm={} GeV (norm = {} GeV), invariant mass={} GeV",
-        Ecm / 1_GeV, pCM / 1_GeV, pCM.norm() / 1_GeV, p.GetNorm() / 1_GeV);
-
-    auto const boostedZ = inverseBoost_ * com;
-    auto const E_lab = boostedZ(0) * 1_GeV;
-
-    pCM.eVector(2) = boostedZ(1) * (1_GeV).magnitude();
-
-    geometry::Vector<typename decltype(pCM)::dimension> pLab{rotatedCS_, pCM};
-    pLab.rebase(originalCS_);
-
-    FourVector f(E_lab, pLab);
-
-    C8LOG_TRACE("COMBoost::fromCoM --> Elab={} GeV",
-                " plab={} GeV (norm={} GeV) "
-                " GeV), invariant mass = {}",
-                E_lab / 1_GeV, f.GetNorm() / 1_GeV, pLab.GetComponents(),
-                pLab.norm() / 1_GeV);
-
-    return f;
-  }
-
-  COMBoost::COMBoost(FourVector<HEPEnergyType, Vector<hepmomentum_d>> const& Pprojectile,
-                     const HEPMassType massTarget)
-      : fCS(Pprojectile.GetSpaceLikeComponents().GetCoordinateSystem()) {
-    auto const pProjectile = Pprojectile.GetSpaceLikeComponents();
-    auto const pProjNorm = pProjectile.norm();
-    auto const a = (pProjectile / pProjNorm).GetComponents().eVector;
-    auto const a1 = a(0), a2 = a(1);
-
-    auto const s = sgn(a(2));
-    auto const c = 1 / (1 + s * a(2));
-
-    Eigen::Matrix3d A, B;
-
-    if (s > 0) {
-      A << 1, 0, -a1,                     // comment to prevent clang-format
-          0, 1, -a2,                      // .
-          a1, a2, 1;                      // .
-      B << -a1 * a1 * c, -a1 * a2 * c, 0, // .
-          -a1 * a2 * c, -a2 * a2 * c, 0,  // .
-          0, 0, -(a1 * a1 + a2 * a2) * c; // .
-
-    } else {
-      A << 1, 0, a1,                      // comment to prevent clang-format
-          0, -1, -a2,                     // .
-          a1, a2, -1;                     // .
-      B << -a1 * a1 * c, -a1 * a2 * c, 0, // .
-          +a1 * a2 * c, +a2 * a2 * c, 0,  // .
-          0, 0, (a1 * a1 + a2 * a2) * c;  // .
-    }
-
-    fRotation = A + B;
-
-    // calculate boost
-    double const beta = pProjNorm / (Pprojectile.GetTimeLikeComponent() + massTarget);
-
-    /* Accurracy matters here, beta = 1 - epsilon for ultra-relativistic boosts */
-    double const coshEta = 1 / std::sqrt((1 + beta) * (1 - beta));
-    //~ double const coshEta = 1 / std::sqrt((1-beta*beta));
-    double const sinhEta = -beta * coshEta;
-
-    std::cout << "COMBoost (1-beta)=" << 1 - beta << " gamma=" << coshEta << std::endl;
-    std::cout << "  det = " << fRotation.determinant() - 1 << std::endl;
-
-    fBoost << coshEta, sinhEta, sinhEta, coshEta;
-
-    fInverseBoost << coshEta, -sinhEta, -sinhEta, coshEta;
-  }
-} // namespace corsika::utl
diff --git a/corsika/framework/utility/detail/CorsikaFenvFallback.inl b/corsika/framework/utility/detail/CorsikaFenvFallback.inl
deleted file mode 100644
index ba847a3a04dd63dc980b95148113f440e8d70aaf..0000000000000000000000000000000000000000
--- a/corsika/framework/utility/detail/CorsikaFenvFallback.inl
+++ /dev/null
@@ -1,18 +0,0 @@
-/*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * This software is distributed under the terms of the GNU General Public
- * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
- * the license.
- */
-
-#include <corsika/framework/utility/CorsikaFenv.hpp>
-#include <cfenv>
-
-extern "C" {
-#warning No enabling/disabling of floating point exceptions - platform needs better implementation
-
-int feenableexcept(int excepts) { return -1; }
-
-int fedisableexcept(int excepts) { return -1; }
-}
diff --git a/corsika/media/NameModel.hpp b/corsika/media/NameModel.hpp
index 8fe9698ed0bf9ca9684e03491b5ecb9d5122c5d9..f046809c3efe08bc8b97568b854251a904f402d4 100644
--- a/corsika/media/NameModel.hpp
+++ b/corsika/media/NameModel.hpp
@@ -9,7 +9,6 @@
 #pragma once
 
 #include <string>
-//#include <utility>
 
 namespace corsika {
 
diff --git a/corsika/media/NuclearComposition.hpp b/corsika/media/NuclearComposition.hpp
index f8f08704d3bc1f97deba819c886d6d2abd4b5ca9..5341554cd3f132d7c6415112a6eca72058de025b 100644
--- a/corsika/media/NuclearComposition.hpp
+++ b/corsika/media/NuclearComposition.hpp
@@ -33,8 +33,8 @@ namespace corsika {
      *  @param pFractions List of fractions how much each particle contributes. The sum
      *         needs to add up to 1
      **/
-    inline NuclearComposition(std::vector<Code> const& pComponents,
-                              std::vector<float> const& pFractions);
+    NuclearComposition(std::vector<Code> const& pComponents,
+                       std::vector<float> const& pFractions);
 
     /** Sum all all relative composition weighted by func(element)
      *  This function sums all relative compositions given during this classes
@@ -46,29 +46,29 @@ namespace corsika {
      *  @retval returns the weighted sum with the type defined by the return type of func
      **/
     template <typename TFunction>
-    inline auto getWeightedSum(TFunction const& func) const;
+    auto getWeightedSum(TFunction const& func) const;
 
     /** Number of elements in the composition array
      *  @retval returns the number of elements in the composition array
      **/
-    inline size_t getSize() const;
+    size_t getSize() const;
 
     /// Returns a const reference to the fraction
-    inline std::vector<float> const& getFractions() const;
+    std::vector<float> const& getFractions() const;
     /// Returns a const reference to the fraction
-    inline std::vector<Code> const& getComponents() const;
-    inline double const getAverageMassNumber() const;
+    std::vector<Code> const& getComponents() const;
+    double const getAverageMassNumber() const;
 
     template <class TRNG>
-    inline Code sampleTarget(std::vector<CrossSectionType> const& sigma,
-                             TRNG& randomStream) const;
+    Code sampleTarget(std::vector<CrossSectionType> const& sigma,
+                      TRNG& randomStream) const;
 
     // Note: when this class ever modifies its internal data, the hash
     // must be updated, too!
-    inline size_t getHash() const;
+    size_t getHash() const;
 
   private:
-    inline void updateHash();
+    void updateHash();
 
     std::vector<float> const numberFractions_; //!< relative fractions of number density
     std::vector<Code> const components_;       //!< particle codes of consitutents
diff --git a/corsika/media/Universe.hpp b/corsika/media/Universe.hpp
index cdd5c2bd297d5bc39ac27f91e8eeecf7b9b7bf95..e481052d985f87397381f9a1bc554fbfe292159f 100644
--- a/corsika/media/Universe.hpp
+++ b/corsika/media/Universe.hpp
@@ -14,8 +14,8 @@
 namespace corsika {
 
   struct Universe : public corsika::Sphere {
-    inline Universe(corsika::CoordinateSystemPtr const& pCS);
-    inline bool contains(corsika::Point const&) const override;
+    Universe(corsika::CoordinateSystemPtr const& pCS);
+    bool contains(corsika::Point const&) const override;
   };
 
 } // namespace corsika
diff --git a/corsika/media/VolumeTreeNode.hpp b/corsika/media/VolumeTreeNode.hpp
index fba07bc585043bf592bd7cfdfe1b41cddb10332a..dd4b11c34ac8a45f91ed32ff1319ab3f2cf4cbbd 100644
--- a/corsika/media/VolumeTreeNode.hpp
+++ b/corsika/media/VolumeTreeNode.hpp
@@ -29,15 +29,14 @@ namespace corsika {
         : geoVolume_(std::move(pVolume)) {}
 
     //! convenience function equivalent to Volume::isInside
-    inline bool contains(Point const& p) const;
+    bool contains(Point const& p) const;
 
-    inline VolumeTreeNode<IModelProperties> const* excludes(Point const& p) const;
+    VolumeTreeNode<IModelProperties> const* excludes(Point const& p) const;
 
     /** returns a pointer to the sub-VolumeTreeNode which is "responsible" for the given
      * \class Point \p p, or nullptr iff \p p is not contained in this volume.
      */
-    inline VolumeTreeNode<IModelProperties> const* getContainingNode(
-        Point const& p) const;
+    VolumeTreeNode<IModelProperties> const* getContainingNode(Point const& p) const;
 
     /**
      * Traverses the VolumeTree pre- or post-order and calls the functor  \p func for each
@@ -45,33 +44,33 @@ namespace corsika {
      * func is ignored.
      */
     template <typename TCallable, bool preorder = true>
-    inline void walk(TCallable func);
+    void walk(TCallable func);
 
-    inline void addChild(VTNUPtr pChild);
+    void addChild(VTNUPtr pChild);
 
-    inline void excludeOverlapWith(VTNUPtr const& pNode);
+    void excludeOverlapWith(VTNUPtr const& pNode);
 
-    inline VTN_type const* getParent() const { return parentNode_; };
+    VTN_type const* getParent() const { return parentNode_; };
 
-    inline auto const& getChildNodes() const { return childNodes_; }
+    auto const& getChildNodes() const { return childNodes_; }
 
-    inline auto const& getExcludedNodes() const { return excludedNodes_; }
+    auto const& getExcludedNodes() const { return excludedNodes_; }
 
-    inline auto const& getVolume() const { return *geoVolume_; }
+    auto const& getVolume() const { return *geoVolume_; }
 
-    inline auto const& getModelProperties() const { return *modelProperties_; }
+    auto const& getModelProperties() const { return *modelProperties_; }
 
-    inline bool hasModelProperties() const { return modelProperties_.get() != nullptr; }
+    bool hasModelProperties() const { return modelProperties_.get() != nullptr; }
 
     template <typename ModelProperties, typename... Args>
-    inline auto setModelProperties(Args&&... args) {
+    auto setModelProperties(Args&&... args) {
       // static_assert(std::is_base_of_v<IModelProperties, ModelProperties>,
       //            "unusable type provided");
       modelProperties_ = std::make_shared<ModelProperties>(std::forward<Args>(args)...);
       return modelProperties_;
     }
 
-    inline void setModelProperties(IMPSharedPtr ptr) { modelProperties_ = ptr; }
+    void setModelProperties(IMPSharedPtr ptr) { modelProperties_ = ptr; }
 
     // template <class MediumType, typename... Args>
     // static auto createMedium(Args&&... args);
diff --git a/corsika/modules/LongitudinalProfile.hpp b/corsika/modules/LongitudinalProfile.hpp
index baaa5046824c142694097a18ba662ba97647799c..b06e2ef852541342f474881a0209a154cf85602e 100644
--- a/corsika/modules/LongitudinalProfile.hpp
+++ b/corsika/modules/LongitudinalProfile.hpp
@@ -41,7 +41,9 @@ namespace corsika {
                         GrammageType dX = 10_g / square(1_cm)); // profile binning);
 
     template <typename TParticle, typename TTrack>
-    ProcessReturn doContinuous(TParticle const&, TTrack const&);
+    ProcessReturn doContinuous(
+        TParticle const&, TTrack const&,
+        bool const flagLimit = false); // not needed for LongitudinalProfile
 
     template <typename TParticle, typename TTrack>
     LengthType getMaxStepLength(TParticle const&, TTrack const&) {
diff --git a/corsika/modules/ObservationPlane.hpp b/corsika/modules/ObservationPlane.hpp
index f37591c6ec9e76e959f5bf06ff7e19dc5d5e01d9..98cd99ce8763789b14e23df1adae0fa30e8dac97 100644
--- a/corsika/modules/ObservationPlane.hpp
+++ b/corsika/modules/ObservationPlane.hpp
@@ -30,7 +30,8 @@ namespace corsika {
     ObservationPlane(Plane const&, DirectionVector const&, bool = true);
 
     ProcessReturn doContinuous(corsika::setup::Stack::particle_type& vParticle,
-                               corsika::setup::Trajectory& vTrajectory);
+                               corsika::setup::Trajectory& vTrajectory,
+                               bool const stepLimit);
 
     LengthType getMaxStepLength(corsika::setup::Stack::particle_type const&,
                                 corsika::setup::Trajectory const& vTrajectory);
diff --git a/corsika/modules/ParticleCut.hpp b/corsika/modules/ParticleCut.hpp
index ca7f87013d59cf2cbab4194c7e0fa02b31101c42..9842dd034530d7528e1359e63983d8adc85b25f0 100644
--- a/corsika/modules/ParticleCut.hpp
+++ b/corsika/modules/ParticleCut.hpp
@@ -50,15 +50,17 @@ namespace corsika {
                 bool const em, bool const inv);
 
     void doSecondaries(corsika::setup::StackView&);
-    ProcessReturn doContinuous(corsika::setup::Stack::particle_type& vParticle,
-                               corsika::setup::Trajectory const& vTrajectory);
+    ProcessReturn doContinuous(
+        corsika::setup::Stack::particle_type& vParticle,
+        corsika::setup::Trajectory const& vTrajectory,
+        const bool limitFlag = false); // this is not used for ParticleCut
     LengthType getMaxStepLength(corsika::setup::Stack::particle_type const&,
                                 corsika::setup::Trajectory const&) {
       return meter * std::numeric_limits<double>::infinity();
     }
 
     void printThresholds();
-    void showResults();
+    void showResults(); // LCOV_EXCL_LINE
     void reset();
 
     HEPEnergyType getElectronECut() const { return get_energy_threshold(Code::Electron); }
diff --git a/corsika/modules/TrackWriter.hpp b/corsika/modules/TrackWriter.hpp
index 9a9addb9651d61be549708330c4045174c320e70..79e1dfc91bbb52d08300699fc3e6ee1e098feb8a 100644
--- a/corsika/modules/TrackWriter.hpp
+++ b/corsika/modules/TrackWriter.hpp
@@ -21,7 +21,7 @@ namespace corsika {
     TrackWriter();
 
     template <typename TParticle, typename TTrack>
-    ProcessReturn doContinuous(TParticle const&, TTrack const&);
+    ProcessReturn doContinuous(TParticle const&, TTrack const&, bool const limitFlag);
 
     template <typename TParticle, typename TTrack>
     LengthType getMaxStepLength(TParticle const&, TTrack const&);
diff --git a/corsika/modules/energy_loss/BetheBlochPDG.hpp b/corsika/modules/energy_loss/BetheBlochPDG.hpp
index f8e8759376a3833bd3f7df438b2dbe4132c5e3c8..582a0c4e03046cc738105d32d2cfcdd7e94964a9 100644
--- a/corsika/modules/energy_loss/BetheBlochPDG.hpp
+++ b/corsika/modules/energy_loss/BetheBlochPDG.hpp
@@ -43,7 +43,17 @@ namespace corsika {
   public:
     BetheBlochPDG(ShowerAxis const& showerAxis);
 
-    ProcessReturn doContinuous(setup::Stack::particle_type&, setup::Trajectory const&);
+    /** clang-format-off
+     * Interface function of ContinuousProcess.
+     *
+     * \param particle The particle to process in its current state
+     * \param track The trajectory in space of this particle, on which doContinuous should
+     *        act
+     * \param limitFlag flag to identify, if BetheBlochPDG::getMaxStepLength is the
+     *        globally limiting factor (or not)
+     clang-format-on **/
+    ProcessReturn doContinuous(setup::Stack::particle_type& particle,
+                               setup::Trajectory const& track, bool const limitFlag);
     LengthType getMaxStepLength(setup::Stack::particle_type const&,
                                 setup::Trajectory const&)
         const; //! limited by the energy threshold! By default the limit is the particle
diff --git a/corsika/modules/proposal/ContinuousProcess.hpp b/corsika/modules/proposal/ContinuousProcess.hpp
index 3b8976f3ac6bfe7a6d5251740782e4ca8fec89bc..0efbf12ecd6100ae3e6cc7e9bc777cacb2f2dfcb 100644
--- a/corsika/modules/proposal/ContinuousProcess.hpp
+++ b/corsika/modules/proposal/ContinuousProcess.hpp
@@ -67,8 +67,11 @@ namespace corsika::proposal {
     //! If the particle if below the given energy threshold where it will be
     //! considered stochastically, it will be absorbed.
     //!
+    //! \param limitFlag is true, if the track was actually limited by
+    //! proposal::ContinuousProcess::getMaxStepLength
+    //!
     template <typename TParticle, typename TTrack>
-    ProcessReturn doContinuous(TParticle&, TTrack const&);
+    ProcessReturn doContinuous(TParticle&, TTrack const& track, bool const limitFlag);
 
     //!
     //! Calculates maximal step length of process.
diff --git a/corsika/modules/pythia8/Decay.hpp b/corsika/modules/pythia8/Decay.hpp
index 2bdfccb3c82a2a0aa8b653d7c117f062ee9bd1e6..5de68f4fbd2635471cc604a9fae3c2b783354195 100644
--- a/corsika/modules/pythia8/Decay.hpp
+++ b/corsika/modules/pythia8/Decay.hpp
@@ -17,7 +17,7 @@
 
 namespace corsika::pythia8 {
 
-  class Decay : public DecayProcess<Decay> {
+  class Decay : public DecayProcess<Decay>, public Pythia8::Pythia {
 
   public:
     Decay(bool const print_listing = false);
@@ -59,7 +59,6 @@ namespace corsika::pythia8 {
     void setStable(Code const);
 
     // data members
-    Pythia8::Pythia pythia_;
     int count_ = 0;
     bool handleAllDecays_ = true;
     std::set<Code> handledDecays_;
diff --git a/corsika/modules/pythia8/Interaction.hpp b/corsika/modules/pythia8/Interaction.hpp
index 08c1d429bebc47887a7b18f224f00936c9c4f6d7..f2d264db15befbad789b0ba2afd122fa0e586f86 100644
--- a/corsika/modules/pythia8/Interaction.hpp
+++ b/corsika/modules/pythia8/Interaction.hpp
@@ -20,7 +20,7 @@
 
 namespace corsika::pythia8 {
 
-  class Interaction : public InteractionProcess<Interaction> {
+  class Interaction : public InteractionProcess<Interaction>, public Pythia8::Pythia {
 
   public:
     Interaction(const bool print_listing = false);
@@ -30,7 +30,6 @@ namespace corsika::pythia8 {
     void setUnstable(const Code);
     void setStable(const Code);
 
-    bool wasInitialized() { return initialized_; }
     bool isValidCoMEnergy(HEPEnergyType ecm) { return (10_GeV < ecm) && (ecm < 1_PeV); }
 
     bool canInteract(const Code);
@@ -39,24 +38,20 @@ namespace corsika::pythia8 {
     std::tuple<CrossSectionType, CrossSectionType> getCrossSection(
         const Code BeamId, const Code TargetId, const HEPEnergyType CoMenergy);
 
-    // template <typename TParticle>
     GrammageType getInteractionLength(corsika::setup::Stack::particle_type const&);
 
     /**
        In this function PYTHIA is called to produce one event. The
        event is copied (and boosted) into the shower lab frame.
      */
-
     template <typename TView>
     void doInteraction(TView&);
 
   private:
     default_prng_type& RNG_ = RNGManager::getInstance().getRandomStream("pythia");
-    Pythia8::Pythia pythia_;
     Pythia8::SigmaTotal sigma_;
     const bool internalDecays_ = true;
     int count_ = 0;
-    bool initialized_ = false;
     bool print_listing_ = false;
   };
 
diff --git a/corsika/modules/pythia8/Pythia8.hpp b/corsika/modules/pythia8/Pythia8.hpp
index 5b42ac148b97a9bc54059d27d3a34c61f4d8b87a..c74aa8d0dd2f16b9a2fd566b27fb74a2503cb089 100644
--- a/corsika/modules/pythia8/Pythia8.hpp
+++ b/corsika/modules/pythia8/Pythia8.hpp
@@ -8,4 +8,8 @@
 
 #pragma once
 
+// the location of the "xmldoc" pythia directory (data files, config)
+#include <corsika/modules/pythia8/Pythia8ConfigurationDirectory.hpp>
+
+// the main pythia include
 #include <Pythia8/Pythia.h>
diff --git a/corsika/modules/qgsjetII/Interaction.hpp b/corsika/modules/qgsjetII/Interaction.hpp
index 004788f138980e9a07ffe2c1fe8d86444970180c..a45282f58ae3073237adf19a3c0c739d6153047c 100644
--- a/corsika/modules/qgsjetII/Interaction.hpp
+++ b/corsika/modules/qgsjetII/Interaction.hpp
@@ -12,9 +12,6 @@
 #include <corsika/framework/core/PhysicalUnits.hpp>
 #include <corsika/framework/random/RNGManager.hpp>
 #include <corsika/framework/process/InteractionProcess.hpp>
-
-#include <corsika/modules/qgsjetII/Random.hpp>
-
 #include <corsika/modules/qgsjetII/ParticleConversion.hpp>
 #include <qgsjet-II-04.hpp>
 
diff --git a/corsika/modules/tracking/TrackingLeapFrogCurved.hpp b/corsika/modules/tracking/TrackingLeapFrogCurved.hpp
index dff4bc18ea42ee6ab0d399ad1b2dc7d3b9e185aa..5eb28160818abf42f7a4a743165515f2254d89dc 100644
--- a/corsika/modules/tracking/TrackingLeapFrogCurved.hpp
+++ b/corsika/modules/tracking/TrackingLeapFrogCurved.hpp
@@ -60,13 +60,18 @@ namespace corsika {
       template <typename TParticle>
       auto getTrack(TParticle const& particle);
 
-      template <typename TParticle, typename TMedium>
-      static Intersections intersect(const TParticle& particle, const Sphere& sphere,
-                                     const TMedium& medium);
+      //! find intersection of Sphere with Track
+      template <typename TParticle>
+      static Intersections intersect(TParticle const& particle, Sphere const& sphere);
 
+      //! find intersection of Volume node with Track of particle
       template <typename TParticle, typename TBaseNodeType>
-      static Intersections intersect(const TParticle& particle,
-                                     const TBaseNodeType& volumeNode);
+      static Intersections intersect(TParticle const& particle,
+                                     TBaseNodeType const& node);
+
+      //! find intersection of Plane with Track
+      template <typename TParticle>
+      static Intersections intersect(TParticle const& particle, Plane const& plane);
 
     protected:
       /**
diff --git a/corsika/modules/tracking/TrackingStraight.hpp b/corsika/modules/tracking/TrackingStraight.hpp
index b77f8fd211b6c2459e012389b6fa6939088494da..4b498bfa522d5856b2251435a437c7ec42681687 100644
--- a/corsika/modules/tracking/TrackingStraight.hpp
+++ b/corsika/modules/tracking/TrackingStraight.hpp
@@ -41,19 +41,16 @@ namespace corsika::tracking_line {
     auto getTrack(TParticle const& particle);
 
     //! find intersection of Sphere with Track
-    template <typename TParticle, typename TMedium>
-    static Intersections intersect(TParticle const& particle, Sphere const& sphere,
-                                   TMedium const&);
+    template <typename TParticle>
+    static Intersections intersect(TParticle const& particle, Sphere const& sphere);
 
-    //! find intersection of Volume with Track
+    //! find intersection of Volume node with Track of particle
     template <typename TParticle, typename TBaseNodeType>
-    static Intersections intersect(TParticle const& particle,
-                                   TBaseNodeType const& volumeNode);
+    static Intersections intersect(TParticle const& particle, TBaseNodeType const& node);
 
     //! find intersection of Plane with Track
-    template <typename TParticle, typename TMedium>
-    static Intersections intersect(TParticle const& particle, Plane const& plane,
-                                   TMedium const&);
+    template <typename TParticle>
+    static Intersections intersect(TParticle const& particle, Plane const& plane);
   };
 
 } // namespace corsika::tracking_line
diff --git a/corsika/setup/SetupStack.hpp b/corsika/setup/SetupStack.hpp
index 6c9696d899be01c7ae4eff98000b5ebe394b293f..847f83834e57d1afa757151df9e92cce7ee8dca4 100644
--- a/corsika/setup/SetupStack.hpp
+++ b/corsika/setup/SetupStack.hpp
@@ -18,12 +18,15 @@ namespace corsika::setup {
 
 #ifdef WITH_HISTORY
 
+#include <corsika/stack/history/HistoryStackExtension.hpp>
+#include <corsika/stack/history/HistorySecondaryProducer.hpp>
+
   /*
    * the version with history
    */
   using Stack = detail::StackWithHistory;
   template <typename T1, template <typename> typename M2>
-  using StackViewProducer = HistorySecondaryProducer<T1, M2>;
+  using StackViewProducer = history::HistorySecondaryProducer<T1, M2>;
 
 #else // WITH_HISTORY
 
@@ -32,7 +35,7 @@ namespace corsika::setup {
    */
   using Stack = detail::StackWithGeometry;
   template <typename T1, template <typename> typename M2>
-  using StackViewProducer = corsika::DefaultSecondaryProducer<T1, M2>;
+  using StackViewProducer = DefaultSecondaryProducer<T1, M2>;
 
 #endif
 
@@ -58,7 +61,7 @@ namespace corsika::setup {
                                   // CHECK with CLANG: setup::Stack::MPIType>;
                                   detail::StackWithHistoryInterface, StackViewProducer>;
 #elif defined(__GNUC__) || defined(__GNUG__)
-  using StackView = make_view<setup::Stack, StackViewProducer>::type;
+  using StackView = MakeView<setup::Stack, StackViewProducer>::type;
 #endif
 
 #else // WITH_HISTORY
@@ -69,7 +72,7 @@ namespace corsika::setup {
                                   // setup::Stack::MPIType>;
                                   setup::detail::StackWithGeometryInterface>;
 #elif defined(__GNUC__) || defined(__GNUG__)
-  using StackView = corsika::MakeView<setup::Stack>::type;
+  using StackView = MakeView<setup::Stack>::type;
 #endif
 
 #endif // WITH_HISTORY
diff --git a/corsika/setup/SetupTrajectory.hpp b/corsika/setup/SetupTrajectory.hpp
index fdbfcbeaa4d0a678a15583c5ceef93d3b5d3c31f..c40351d26f1c7bdef9d9b8ac18d3653caaf1927d 100644
--- a/corsika/setup/SetupTrajectory.hpp
+++ b/corsika/setup/SetupTrajectory.hpp
@@ -38,8 +38,8 @@ namespace corsika::setup {
      The default tracking algorithm.
    */
 
-  // typedef corsika::process::tracking_leapfrog_curved::Tracking Tracking;
-  // typedef corsika::process::tracking_leapfrog_straight::Tracking Tracking;
+  // typedef corsika::tracking_leapfrog_curved::Tracking Tracking;
+  // typedef corsika::tracking_leapfrog_straight::Tracking Tracking;
   typedef corsika::tracking_line::Tracking Tracking;
 
   /**
@@ -47,6 +47,6 @@ namespace corsika::setup {
   */
   /// definition of Trajectory base class, to be used in tracking and cascades
   typedef StraightTrajectory Trajectory;
-  // typedef corsika::geometry::LeapFrogTrajectory Trajectory;
+  // typedef corsika::LeapFrogTrajectory Trajectory;
 
 } // namespace corsika::setup
diff --git a/corsika/stack/DummyStack.hpp b/corsika/stack/DummyStack.hpp
index bbbfff91a46e1c2a3348040105f0e0afdb0a5855..9bbd51403f98fd53b9d64c92460bcbaeafece400 100644
--- a/corsika/stack/DummyStack.hpp
+++ b/corsika/stack/DummyStack.hpp
@@ -61,21 +61,21 @@ namespace corsika::dummy_stack {
 
     void init() { entries_ = 0; }
 
-    inline void clear() { entries_ = 0; }
+    void clear() { entries_ = 0; }
 
-    inline int getSize() const { return entries_; }
-    inline int getCapacity() const { return entries_; }
+    int getSize() const { return entries_; }
+    int getCapacity() const { return entries_; }
 
     /**
      *   Function to copy particle at location i2 in stack to i1
      */
-    inline void copy(const int /*i1*/, const int /*i2*/) {}
+    void copy(const int /*i1*/, const int /*i2*/) {}
 
-    inline void incrementSize() { entries_++; }
-    inline void decrementSize() { entries_--; }
+    void incrementSize() { entries_++; }
+    void decrementSize() { entries_--; }
 
-    inline int getEntries() const { return entries_; }
-    inline void setEntries(int entries = 0) { entries_ = entries; }
+    int getEntries() const { return entries_; }
+    void setEntries(int entries = 0) { entries_ = entries; }
 
   private:
     int entries_ = 0;
diff --git a/corsika/stack/GeometryNodeStackExtension.hpp b/corsika/stack/GeometryNodeStackExtension.hpp
index b3a15854f58af04c8bae6ceecb0ba9e25644343e..e7298bea4bc4b04d5869ee70f440c3398b90d52a 100644
--- a/corsika/stack/GeometryNodeStackExtension.hpp
+++ b/corsika/stack/GeometryNodeStackExtension.hpp
@@ -38,28 +38,26 @@ namespace corsika::node {
     typedef typename TEnvType::BaseNodeType node_type;
 
     // default version for particle-creation from input data
-    inline void setParticleData(const std::tuple<node_type const*> v) {
+    void setParticleData(const std::tuple<node_type const*> v) {
       setNode(std::get<0>(v));
     }
-    inline void setParticleData(GeometryDataInterface& parent,
-                                const std::tuple<node_type const*>) {
+    void setParticleData(GeometryDataInterface& parent,
+                         const std::tuple<node_type const*>) {
       setNode(parent.getNode()); // copy Node from parent particle!
     }
-    inline void setParticleData() { setNode(nullptr); }
-    inline void setParticleData(GeometryDataInterface& parent) {
+    void setParticleData() { setNode(nullptr); }
+    void setParticleData(GeometryDataInterface& parent) {
       setNode(parent.getNode()); // copy Node from parent particle!
     }
 
-    inline std::string asString() const {
-      return fmt::format("node={}", fmt::ptr(getNode()));
-    }
+    std::string asString() const { return fmt::format("node={}", fmt::ptr(getNode())); }
 
-    inline void setNode(node_type const* v) {
+    void setNode(node_type const* v) {
 
       super_type::getStackData().setNode(super_type::getIndex(), v);
     }
 
-    inline node_type const* getNode() const {
+    node_type const* getNode() const {
       return super_type::getStackData().getNode(super_type::getIndex());
     }
   };
@@ -89,27 +87,27 @@ namespace corsika::node {
     GeometryData<TEnvType>& operator=(GeometryData<TEnvType>&&) = default;
 
     // these functions are needed for the Stack interface
-    inline void clear() { node_vector_.clear(); }
+    void clear() { node_vector_.clear(); }
 
-    inline unsigned int getSize() const { return node_vector_.size(); }
+    unsigned int getSize() const { return node_vector_.size(); }
 
-    inline unsigned int getCapacity() const { return node_vector_.size(); }
+    unsigned int getCapacity() const { return node_vector_.size(); }
 
-    inline void copy(const int i1, const int i2) { node_vector_[i2] = node_vector_[i1]; }
+    void copy(const int i1, const int i2) { node_vector_[i2] = node_vector_[i1]; }
 
-    inline void swap(const int i1, const int i2) {
+    void swap(const int i1, const int i2) {
       std::swap(node_vector_[i1], node_vector_[i2]);
     }
 
     // custom data access function
-    inline void setNode(const int i, node_type const* v) { node_vector_[i] = v; }
+    void setNode(const int i, node_type const* v) { node_vector_[i] = v; }
 
-    inline node_type const* getNode(const int i) const { return node_vector_[i]; }
+    node_type const* getNode(const int i) const { return node_vector_[i]; }
 
     // these functions are also needed by the Stack interface
-    inline void incrementSize() { node_vector_.push_back(nullptr); }
+    void incrementSize() { node_vector_.push_back(nullptr); }
 
-    inline void decrementSize() {
+    void decrementSize() {
       if (node_vector_.size() > 0) { node_vector_.pop_back(); }
     }
 
diff --git a/corsika/stack/NuclearStackExtension.hpp b/corsika/stack/NuclearStackExtension.hpp
index 5d8d573ed35b35b79fa6770461c922c61150a1ea..34bd1377d99a5167b2ac406e506a4c8492173bc3 100644
--- a/corsika/stack/NuclearStackExtension.hpp
+++ b/corsika/stack/NuclearStackExtension.hpp
@@ -54,24 +54,24 @@ namespace corsika::nuclear_stack {
                        unsigned short, unsigned short>
         altenative_particle_data_type;
 
-    inline void setParticleData(particle_data_type const& v);
+    void setParticleData(particle_data_type const& v);
 
-    inline void setParticleData(altenative_particle_data_type const& v);
+    void setParticleData(altenative_particle_data_type const& v);
 
-    inline void setParticleData(super_type& p, particle_data_type const& v);
+    void setParticleData(super_type& p, particle_data_type const& v);
 
-    inline void setParticleData(super_type& p, altenative_particle_data_type const& v);
+    void setParticleData(super_type& p, altenative_particle_data_type const& v);
 
-    inline std::string asString() const;
+    std::string asString() const;
 
     /**
      * @name individual setters
      * @{
      */
-    inline void setNuclearA(const unsigned short vA) {
+    void setNuclearA(const unsigned short vA) {
       super_type::getStackData().setNuclearA(super_type::getIndex(), vA);
     }
-    inline void setNuclearZ(const unsigned short vZ) {
+    void setNuclearZ(const unsigned short vZ) {
       super_type::getStackData().setNuclearZ(super_type::getIndex(), vZ);
     }
     /// @}
@@ -80,10 +80,10 @@ namespace corsika::nuclear_stack {
      * @name individual getters
      * @{
      */
-    inline int getNuclearA() const {
+    int getNuclearA() const {
       return super_type::getStackData().getNuclearA(super_type::getIndex());
     }
-    inline int getNuclearZ() const {
+    int getNuclearZ() const {
       return super_type::getStackData().getNuclearZ(super_type::getIndex());
     }
     /// @}
@@ -91,22 +91,22 @@ namespace corsika::nuclear_stack {
     /**
      * Overwrite normal getParticleMass function with nuclear version
      */
-    inline HEPMassType getMass() const;
+    HEPMassType getMass() const;
     /**
      * Overwirte normal getChargeNumber function with nuclear version
      **/
-    inline int16_t getChargeNumber() const;
+    int16_t getChargeNumber() const;
 
-    inline int getNucleusRef() const {
+    int getNucleusRef() const {
       return super_type::getStackData().getNucleusRef(super_type::getIndex());
     } // LCOV_EXCL_LINE
 
   protected:
-    inline void setNucleusRef(const int vR) {
+    void setNucleusRef(const int vR) {
       super_type::getStackData().setNucleusRef(super_type::getIndex(), vR);
     }
 
-    inline bool isNucleus() const {
+    bool isNucleus() const {
       return super_type::getStackData().isNucleus(super_type::getIndex());
     }
   };
@@ -149,49 +149,45 @@ namespace corsika::nuclear_stack {
 
     void dump() { super_type::dump(); }
 
-    inline void clear();
+    void clear();
 
     unsigned int getSize() const { return nucleusRef_.size(); }
 
     unsigned int getCapacity() const { return nucleusRef_.capacity(); }
 
-    inline void setNuclearA(const unsigned int i, const unsigned short vA) {
+    void setNuclearA(const unsigned int i, const unsigned short vA) {
       nuclearA_[getNucleusRef(i)] = vA;
     }
 
-    inline void setNuclearZ(const unsigned int i, const unsigned short vZ) {
+    void setNuclearZ(const unsigned int i, const unsigned short vZ) {
       nuclearZ_[getNucleusRef(i)] = vZ;
     }
 
-    inline void setNucleusRef(const unsigned int i, const int v) { nucleusRef_[i] = v; }
+    void setNucleusRef(const unsigned int i, const int v) { nucleusRef_[i] = v; }
 
-    inline int getNuclearA(const unsigned int i) const {
-      return nuclearA_[getNucleusRef(i)];
-    }
+    int getNuclearA(const unsigned int i) const { return nuclearA_[getNucleusRef(i)]; }
 
-    inline int getNuclearZ(const unsigned int i) const {
-      return nuclearZ_[getNucleusRef(i)];
-    }
+    int getNuclearZ(const unsigned int i) const { return nuclearZ_[getNucleusRef(i)]; }
     // this function will create new storage for Nuclear Properties, and return the
     // reference to it
-    inline int getNucleusNextRef();
+    int getNucleusNextRef();
 
-    inline int getNucleusRef(const unsigned int i) const;
+    int getNucleusRef(const unsigned int i) const;
 
-    inline bool isNucleus(const unsigned int i) const { return nucleusRef_[i] >= 0; }
+    bool isNucleus(const unsigned int i) const { return nucleusRef_[i] >= 0; }
 
     /**
      *   Function to copy particle at location i1 in stack to i2
      */
-    inline void copy(const unsigned int i1, const unsigned int i2);
+    void copy(const unsigned int i1, const unsigned int i2);
     /**
      *   Function to copy particle at location i2 in stack to i1
      */
-    inline void swap(const unsigned int i1, const unsigned int i2);
+    void swap(const unsigned int i1, const unsigned int i2);
 
-    inline void incrementSize();
+    void incrementSize();
 
-    inline void decrementSize();
+    void decrementSize();
 
   private:
     /// the actual memory to store particle data
diff --git a/corsika/stack/VectorStack.hpp b/corsika/stack/VectorStack.hpp
index 7877469ee39fa4d3f8f3326bd56c112e61da0267..adaae996b63a5b69efa85dc8ad8956e253de44b9 100644
--- a/corsika/stack/VectorStack.hpp
+++ b/corsika/stack/VectorStack.hpp
@@ -119,7 +119,7 @@ namespace corsika {
 
     void dump() const {}
 
-    inline void clear();
+    void clear();
 
     unsigned int getSize() const { return dataPID_.size(); }
     unsigned int getCapacity() const { return dataPID_.size(); }
@@ -149,15 +149,15 @@ namespace corsika {
     /**
      *   Function to copy particle at location i2 in stack to i1
      */
-    inline void copy(size_t i1, size_t i2);
+    void copy(size_t i1, size_t i2);
 
     /**
      *   Function to copy particle at location i2 in stack to i1
      */
-    inline void swap(size_t i1, size_t i2);
+    void swap(size_t i1, size_t i2);
 
-    inline void incrementSize();
-    inline void decrementSize();
+    void incrementSize();
+    void decrementSize();
 
   private:
     /// the actual memory to store particle data
diff --git a/corsika/stack/history/CMakeLists.txt b/corsika/stack/history/CMakeLists.txt
deleted file mode 100644
index 2a47b86189bef946269e8f2815a51b2cfcf171f6..0000000000000000000000000000000000000000
--- a/corsika/stack/history/CMakeLists.txt
+++ /dev/null
@@ -1,69 +0,0 @@
-set (
-  HISTORY_HEADERS
-  EventType.hpp
-  Event.hpp
-  HistorySecondaryProducer.hpp
-  HistoryObservationPlane.hpp
-  HistoryStackExtension.hpp
-  SecondaryParticle.hpp
-  )
-
-set (
-  HISTORY_NAMESPACE
-  corsika/stack/history
-  )
-
-if (WITH_HISTORY)
-  set (
-    HISTORY_SOURCES
-    HistoryObservationPlane.cpp
-    )
-  add_library (CORSIKAhistory STATIC ${HISTORY_SOURCES})
-  set (IS_INTERFACE "")
-else (WITH_HISTORY)
-  add_library (CORSIKAhistory INTERFACE)
-  set (IS_INTERFACE "INTERFACE")
-endif (WITH_HISTORY)
-
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAhistory ${HISTORY_NAMESPACE} ${HISTORY_HEADERS})
-
-target_link_libraries (
-  CORSIKAhistory
-  ${IS_INTERFACE}
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAprocesssequence # for HistoryObservationPlane
-  CORSIKAsetup # for HistoryObservationPlane
-  CORSIKAlogging
-  SuperStupidStack
-  NuclearStackExtension # for testHistoryView.cc
-  C8::ext::boost # for HistoryObservationPlane
-  )
-
-target_include_directories (
-  CORSIKAhistory
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  FILES ${HISTORY_HEADERS} 
-  DESTINATION include/${HISTORY_NAMESPACE}
-  )
-
-# ----------------
-# code unit testing
-CORSIKA_ADD_TEST(testHistoryStack)
-target_link_libraries (
-  testHistoryStack
-  CORSIKAhistory
-  CORSIKAtesting
-  DummyStack
-  )
-CORSIKA_ADD_TEST(testHistoryView)
-target_link_libraries (
-  testHistoryView
-  CORSIKAhistory
-  CORSIKAtesting
-  )
diff --git a/do-clang-format.py b/do-clang-format.py
index 076a5ccdf4c207ff448aef2453fa835233c0ff28..d6ea597535e69da32de80ff31962ba998523305d 100755
--- a/do-clang-format.py
+++ b/do-clang-format.py
@@ -10,11 +10,21 @@ import os
 import sys
 import re
 
+do_progress = False
+try:    
+    from progress.bar import ChargingBar
+    do_progress = True
+except ImportError as e:
+    do_progress = False
+    # no progress bar
+
 parser = argparse.ArgumentParser(description=__doc__)
 parser.add_argument('--apply', action="store_true",
     help="Apply clang-format to files which need changes.")
 parser.add_argument("--all", action="store_true",
     help="Check all files below current path instead of new/modified.")
+parser.add_argument("--docker", action="store_true",
+    help="Use corsika/devel:clang-8 container to run clang-format. Make sure you are in group \"docker\". ")
 
 args = parser.parse_args()
 
@@ -70,18 +80,39 @@ else:
 cmd = "clang-format"
 if "CLANG_FORMAT" in os.environ:
   cmd = os.environ["CLANG_FORMAT"]
-cmd +=  " -style=file"
+if args.docker: 
+  USER=os.environ["USER"]
+  UID=os.getuid()
+  GID=os.getgid()
+  PWD=os.getcwd()
+  # note, currently in container it is clang-8 
+  cmd = "docker container run --rm -v {}:/corsika -w /corsika -u {}:{} corsika/devel:clang-8 clang-format-8".format(PWD,UID,GID)
+cmd += " -style=file"
+
+version = subp.check_output(cmd.split() + ["--version"]).decode("utf-8")
+print (version)
+print ("Note: the clang-format version has an impact on the result. Make sure you are consistent with current CI. Consider \'--docker\' option.")
+
+
+bar = None
+if do_progress:
+    bar = ChargingBar('Processing', max=len(filelist))
+
 if args.apply:
     for filename in filelist:        
+        if bar: bar.next()
         subp.check_call(cmd.split() + ["-i", filename])
-
+    if bar: bar.finish()
+        
 else:
     # only print files which need formatting
     files_need_formatting = 0
     for filename in filelist:
+        if bar: bar.next()
         a = open(filename, "rb").read()
         b = subp.check_output(cmd.split() + [filename])
         if a != b:
             files_need_formatting += 1
             print(filename)
+    if bar: bar.finish()            
     sys.exit(1 if files_need_formatting > 0 else 0)
diff --git a/documentation/CMakeLists.txt b/documentation/CMakeLists.txt
index b3511f84ddbc9ccdce365479323f4d5f7d798596..e62d3c71db4fb27081f910d7623a53eadf85c7bf 100644
--- a/documentation/CMakeLists.txt
+++ b/documentation/CMakeLists.txt
@@ -3,7 +3,7 @@ find_package (Doxygen OPTIONAL_COMPONENTS dot mscgen dia)
 if (DOXYGEN_FOUND)
   if (NOT DOXYGEN_DOT_EXECUTABLE)
     message (FATAL_ERROR "Found doxygen but not 'dot' command, please install graphviz or set DOXYGEN_DOT_EXECUTABLE")
-  endif()
+  endif (NOT DOXYGEN_DOT_EXECUTABLE)
 
   set (DOXYGEN_IN ${CMAKE_CURRENT_SOURCE_DIR}/Doxyfile.in)
   set (DOXYGEN_OUT ${CMAKE_CURRENT_BINARY_DIR}/Doxyfile)
@@ -17,14 +17,15 @@ if (DOXYGEN_FOUND)
     COMMAND ${DOXYGEN_EXECUTABLE} ${DOXYGEN_OUT}
     WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}
     COMMENT "Generating API documentation with Doxygen"
-    VERBATIM)
+    VERBATIM
+    )
 
-  add_custom_command(TARGET doxygen POST_BUILD
+  add_custom_command (TARGET doxygen POST_BUILD
     COMMAND cd ${CMAKE_CURRENT_BINARY_DIR}/latex; pdflatex refman.tex
     )
   
-  install(DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc OPTIONAL)
-  install(FILES ${CMAKE_CURRENT_BINARY_DIR}/latex/refman.pdf DESTINATION share/doc OPTIONAL)
+  install (DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/html DESTINATION share/doc OPTIONAL)
+  install (FILES ${CMAKE_CURRENT_BINARY_DIR}/latex/refman.pdf DESTINATION share/doc OPTIONAL)
          
 else (DOXYGEN_FOUND)
   
diff --git a/examples/CMakeHelper.cmake b/examples/CMakeHelper.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..398863c5c72e99f7e46b5ef142d56a534b7dc67f
--- /dev/null
+++ b/examples/CMakeHelper.cmake
@@ -0,0 +1,50 @@
+#################################################
+#
+# central macro to register an examples in cmake
+#
+# Examples can be globally executed by 'make run_examples'
+#
+# 1) Simple use:
+# Pass the name of the source.cc file as the first
+# argument, without the ".cc" extention.
+#
+# Example: CORSIKA_REGISTER_EXAMPLE (doSomething)
+#
+# The TARGET doSomething must already exists,
+#    i.e. typically via add_executable (doSomething src.cc).
+#
+# Example: CORSIKA_ADD_EXAMPLE (example_one
+#              RUN_OPTION "extra command line options"
+#              )
+#
+# In all cases, you can further customize the target with
+# target_link_libraries (example_one ...) and so on.
+#
+function (CORSIKA_REGISTER_EXAMPLE)
+  cmake_parse_arguments (PARSE_ARGV 1 C8_EXAMPLE "" "" "RUN_OPTIONS")
+  set (name ${ARGV0})
+
+  if (NOT C8_EXAMPLE_RUN_OPTIONS)
+    set (run_options "")
+  else ()
+    set (run_options ${C8_EXAMPLE_RUN_OPTIONS})
+  endif ()
+
+  target_compile_options (${name} PRIVATE -g) # do not skip asserts
+  file (MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/example_outputs/)
+  if (TARGET run_examples)
+  else ()
+    add_custom_target (run_examples)
+  endif ()
+  add_dependencies (run_examples ${name})
+  # just run the command as-is
+  set (CMD ${CMAKE_CURRENT_BINARY_DIR}/bin/${name} ${run_options})
+  add_custom_command (TARGET run_examples
+    POST_BUILD
+    COMMAND ${CMAKE_COMMAND} -E echo ""
+    COMMAND ${CMAKE_COMMAND} -E echo "**************************************"
+    COMMAND ${CMAKE_COMMAND} -E echo "*****   running example: ${name} " ${run_options} VERBATIM
+    COMMAND ${CMD} VERBATIM
+    WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/example_outputs)
+  install (TARGETS ${name} DESTINATION share/examples)
+endfunction (CORSIKA_REGISTER_EXAMPLE)
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
index a9a16a6e7f1d2a66020f709a26f4c4488d1ab2c4..8cd42babe67261c0f17e52805e67bfceb8a09ab7 100644
--- a/examples/CMakeLists.txt
+++ b/examples/CMakeLists.txt
@@ -1,52 +1,58 @@
+cmake_minimum_required (VERSION 3.9)
+
+project (CORSIKA8_examples)
+set (CMAKE_VERBOSE_MAKEFILE OFF) # this can be changed with `make VERBOSE=1`
+
+# this is enough to use CORSIKA8
+find_package (corsika CONFIG REQUIRED)
+
+# this is only for CORSIKA_REGISTER_EXAMPLE
+include ("${CMAKE_CURRENT_SOURCE_DIR}/CMakeHelper.cmake")
+
 add_executable (helix_example helix_example.cpp)
-target_link_libraries (helix_example CORSIKA8)
+target_link_libraries (helix_example CORSIKA8::CORSIKA8)
 CORSIKA_REGISTER_EXAMPLE (helix_example)
 
 add_executable (geometry_example geometry_example.cpp)
-target_link_libraries (geometry_example CORSIKA8)
+target_link_libraries (geometry_example CORSIKA8::CORSIKA8)
 CORSIKA_REGISTER_EXAMPLE (geometry_example)
 
-# # logging back in March was not used
-# #add_executable (logger_example logger_example.cpp)
-# #target_link_libraries (logger_example CORSIKA8)
-# #CORSIKA_REGISTER_EXAMPLE (logger_example)
-
 add_executable (stack_example stack_example.cpp)
-target_link_libraries (stack_example CORSIKA8)
+target_link_libraries (stack_example CORSIKA8::CORSIKA8)
 CORSIKA_REGISTER_EXAMPLE (stack_example)
 
 add_executable (cascade_example cascade_example.cpp)
-target_link_libraries (cascade_example CORSIKA8)
+target_link_libraries (cascade_example CORSIKA8::CORSIKA8)
 CORSIKA_REGISTER_EXAMPLE (cascade_example)
 
 add_executable (boundary_example boundary_example.cpp)
-target_link_libraries (boundary_example CORSIKA8)
+target_link_libraries (boundary_example CORSIKA8::CORSIKA8)
 CORSIKA_REGISTER_EXAMPLE (boundary_example)
 
 add_executable (cascade_proton_example cascade_proton_example.cpp)
-target_link_libraries (cascade_proton_example CORSIKA8)
+target_link_libraries (cascade_proton_example CORSIKA8::CORSIKA8)
 CORSIKA_REGISTER_EXAMPLE (cascade_proton_example)
 
 add_executable (vertical_EAS vertical_EAS.cpp)
-target_link_libraries (vertical_EAS CORSIKA8)
-CORSIKA_REGISTER_EXAMPLE (vertical_EAS RUN_OPTIONS 4 2 10000.)
+target_link_libraries (vertical_EAS CORSIKA8::CORSIKA8)
+CORSIKA_REGISTER_EXAMPLE (vertical_EAS RUN_OPTIONS 4 2 10000. 1)
 
 add_executable (stopping_power stopping_power.cpp)
-target_link_libraries (stopping_power CORSIKA8)
+target_link_libraries (stopping_power CORSIKA8::CORSIKA8)
 CORSIKA_REGISTER_EXAMPLE (stopping_power)
 
 add_executable (staticsequence_example staticsequence_example.cpp)
-target_link_libraries (staticsequence_example CORSIKA8)
+target_link_libraries (staticsequence_example CORSIKA8::CORSIKA8)
 CORSIKA_REGISTER_EXAMPLE (staticsequence_example)
 
 add_executable (particle_list_example particle_list_example.cpp)
-target_link_libraries (particle_list_example CORSIKA8)
+target_link_libraries (particle_list_example CORSIKA8::CORSIKA8)
 CORSIKA_REGISTER_EXAMPLE (particle_list_example)
 
 add_executable (em_shower em_shower.cpp)
-target_link_libraries (em_shower CORSIKA8)
+target_link_libraries (em_shower CORSIKA8::CORSIKA8)
 CORSIKA_REGISTER_EXAMPLE (em_shower RUN_OPTIONS "100.")
 
 add_executable (hybrid_MC hybrid_MC.cpp)
-target_link_libraries (hybrid_MC  CORSIKA8)
+target_link_libraries (hybrid_MC  CORSIKA8::CORSIKA8)
 CORSIKA_REGISTER_EXAMPLE (hybrid_MC RUN_OPTIONS 4 2 10000.)
diff --git a/examples/cascade_example.cpp b/examples/cascade_example.cpp
index 91bc3f7cc99640a10e067da3360eee6e50911020..eb0938d9fe08e93ac3a3f9d972362d9c58a0ef48 100644
--- a/examples/cascade_example.cpp
+++ b/examples/cascade_example.cpp
@@ -54,7 +54,6 @@ using namespace std;
 //
 int main() {
 
-  // logging::set_level(logging::level::info);
   corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
   logging::set_level(logging::level::trace);
 
diff --git a/examples/cascade_proton_example.cpp b/examples/cascade_proton_example.cpp
index 6381feff4d8159e10ec032743a91e2af6e3d109b..4b24f3b0148045a2db97546c37c6a2916e3c1d1e 100644
--- a/examples/cascade_proton_example.cpp
+++ b/examples/cascade_proton_example.cpp
@@ -90,7 +90,7 @@ int main() {
   stack.clear();
   const Code beamCode = Code::Proton;
   const HEPMassType mass = Proton::mass;
-  const HEPEnergyType E0 = 100_GeV;
+  const HEPEnergyType E0 = 1000_GeV;
   double theta = 0.;
   double phi = 0.;
 
diff --git a/examples/em_shower.cpp b/examples/em_shower.cpp
index 2afddc84ee9744909d4378eb3cf2456267655308..e4d8b3cc432a1852f96dbababca6a0c024fc037b 100644
--- a/examples/em_shower.cpp
+++ b/examples/em_shower.cpp
@@ -141,11 +141,10 @@ int main(int argc, char** argv) {
 
   // setup processes, decays and interactions
 
-  // PROPOSAL processs proposal{...};
   ParticleCut cut(10_GeV, 10_GeV, 100_PeV, 100_PeV, true);
-  corsika::proposal::Interaction proposal(env);
-  corsika::proposal::ContinuousProcess em_continuous(env);
-  InteractionCounter proposalCounted(proposal);
+  corsika::proposal::Interaction emCascade(env);
+  corsika::proposal::ContinuousProcess emContinuous(env);
+  InteractionCounter emCascadeCounted(emCascade);
 
   TrackWriter trackWriter("tracks.dat");
 
@@ -156,7 +155,7 @@ int main(int argc, char** argv) {
   ObservationPlane observationLevel(obsPlane, DirectionVector(rootCS, {1., 0., 0.}),
                                     "particles.dat");
 
-  auto sequence = make_sequence(proposalCounted, em_continuous, longprof, cut,
+  auto sequence = make_sequence(emCascadeCounted, emContinuous, longprof, cut,
                                 observationLevel, trackWriter);
   // define air shower object, run simulation
   setup::Tracking tracking;
@@ -169,18 +168,18 @@ int main(int argc, char** argv) {
   EAS.run();
 
   cut.showResults();
-  em_continuous.showResults();
+  emContinuous.showResults();
   observationLevel.showResults();
   const HEPEnergyType Efinal = cut.getCutEnergy() + cut.getInvEnergy() +
-                               cut.getEmEnergy() + em_continuous.getEnergyLost() +
+                               cut.getEmEnergy() + emContinuous.getEnergyLost() +
                                observationLevel.getEnergyGround();
   cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl
        << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl;
   observationLevel.reset();
   cut.reset();
-  em_continuous.reset();
+  emContinuous.reset();
 
-  auto const hists = proposalCounted.getHistogram();
+  auto const hists = emCascadeCounted.getHistogram();
   save_hist(hists.labHist(), "inthist_lab_emShower.npz", true);
   save_hist(hists.CMSHist(), "inthist_cms_emShower.npz", true);
   longprof.save("longprof_emShower.txt");
diff --git a/examples/particle_list_example.cpp b/examples/particle_list_example.cpp
index 4b8d1d75770743d92e7ad2051ce05bb25d1156c9..8a10d9f094fcc5e4838778e2bc5526347b2bfba0 100644
--- a/examples/particle_list_example.cpp
+++ b/examples/particle_list_example.cpp
@@ -35,37 +35,39 @@ using namespace std;
 int main() {
 
   logging::set_level(logging::level::info);
-  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  corsika_logger->set_pattern("[%n:%^%-8l%$] %v");
 
   logging::info(
+      "\n"
       "------------------------------------------\n"
-      "particles in CORSIKA\n"
-      "------------------------------------------\n"
-      "Name                | "
+      "        particles in CORSIKA\n"
+      "------------------------------------------\n");
+  int const width = 20 + 10 + 10 + 10 + 15 + 15 + 17;
+  logging::info(
+      "Name                 | "
       "PDG-id     | "
       "SIBYLL-id  | "
       "QGSJETII-id| "
       "PDG-mass (GeV)   | "
-      "SIBYLL-mass (GeV)|\n"
-      "{:-}",
-      "", 104);
+      "SIBYLL-mass (GeV)|");
+  logging::info("{:->{}}", ' ', width);
   for (auto p : get_all_particles()) {
     if (!is_nucleus(p)) {
       corsika::sibyll::SibyllCode sib_id = corsika::sibyll::convertToSibyll(p);
       auto const sib_mass = (sib_id != corsika::sibyll::SibyllCode::Unknown
                                  ? to_string(corsika::sibyll::getSibyllMass(p) / 1_GeV)
-                                 : "--");
+                                 : "");
       auto const qgs_id = corsika::qgsjetII::convertToQgsjetII(p);
-      logging::info("{:20} | {:10} | {:10} | {:10.5} | {:18.5}", p,
+      logging::info("{:20} | {:10} | {:10} | {:10} | {:>15.5} | {:>15.5} |", p,
                     static_cast<int>(get_PDG(p)),
                     (sib_id != corsika::sibyll::SibyllCode::Unknown
                          ? to_string(static_cast<int>(sib_id))
-                         : "--"),
+                         : ""),
                     (qgs_id != corsika::qgsjetII::QgsjetIICode::Unknown
                          ? to_string(static_cast<int>(qgs_id))
-                         : "--"),
+                         : ""),
                     get_mass(p) / 1_GeV, sib_mass);
     }
   }
-  logging::info("{:-104}", "");
+  logging::info("{:->{}}", ' ', width);
 }
diff --git a/examples/staticsequence_example.cpp b/examples/staticsequence_example.cpp
index d7b9e06b5051d8b8a721b87bba5c9407f3090a70..3f6c5aff75c72ec9058773fabbb6347e0db15274 100644
--- a/examples/staticsequence_example.cpp
+++ b/examples/staticsequence_example.cpp
@@ -24,7 +24,7 @@ class Process1 : public ContinuousProcess<Process1> {
 public:
   Process1() {}
   template <typename D, typename T>
-  ProcessReturn doContinuous(D& d, T&) const {
+  ProcessReturn doContinuous(D& d, T&, bool const) const {
     for (int i = 0; i < nData; ++i) d.p[i] += 1;
     return ProcessReturn::Ok;
   }
@@ -35,7 +35,7 @@ public:
   Process2() {}
 
   template <typename D, typename T>
-  inline ProcessReturn doContinuous(D& d, T&) const {
+  inline ProcessReturn doContinuous(D& d, T&, bool const) const {
     for (int i = 0; i < nData; ++i) d.p[i] -= 0.1 * i;
     return ProcessReturn::Ok;
   }
@@ -46,7 +46,7 @@ public:
   Process3() {}
 
   template <typename D, typename T>
-  inline ProcessReturn doContinuous(D&, T&) const {
+  inline ProcessReturn doContinuous(D&, T&, bool const) const {
     return ProcessReturn::Ok;
   }
 };
@@ -56,7 +56,7 @@ public:
   Process4(const double v)
       : fV(v) {}
   template <typename D, typename T>
-  inline ProcessReturn doContinuous(D& d, T&) const {
+  inline ProcessReturn doContinuous(D& d, T&, bool const) const {
     for (int i = 0; i < nData; ++i) d.p[i] *= fV;
     return ProcessReturn::Ok;
   }
@@ -90,7 +90,7 @@ void modular() {
 
   const int nEv = 10;
   for (int iEv = 0; iEv < nEv; ++iEv) {
-    sequence.doContinuous(particle, track);
+    sequence.doContinuous(particle, track, ContinuousProcessIndex(0));
     for (int i = 0; i < nData; ++i) {
       check[i] += 1. - 0.1 * i;
       check[i] *= 1.5;
diff --git a/examples/vertical_EAS.cpp b/examples/vertical_EAS.cpp
index 02c4b275ace5829a3921b6349e6d8dafc0c42278..552bc2bce98d9bd49fbe569be973e5a7e71a1bb6 100644
--- a/examples/vertical_EAS.cpp
+++ b/examples/vertical_EAS.cpp
@@ -49,6 +49,7 @@
 #include <corsika/modules/Sibyll.hpp>
 #include <corsika/modules/UrQMD.hpp>
 #include <corsika/modules/PROPOSAL.hpp>
+#include <corsika/modules/QGSJetII.hpp>
 
 #include <corsika/setup/SetupStack.hpp>
 #include <corsika/setup/SetupTrajectory.hpp>
@@ -68,6 +69,7 @@
  */
 #include <corsika/modules/sibyll/Random.hpp>
 #include <corsika/modules/urqmd/Random.hpp>
+#include <corsika/modules/qgsjetII/Random.hpp>
 
 using namespace corsika;
 using namespace std;
@@ -91,22 +93,37 @@ void registerRandomStreams(const int seed) {
 template <typename T>
 using MyExtraEnv = MediumPropertyModel<UniformMagneticField<T>>;
 
+// argv : 1.number of nucleons, 2.number of protons,
+//        3.total energy in GeV, 4.number of showers,
+//        5.output directory
+//        6.seed (0 by default to generate random values for all)
+
 int main(int argc, char** argv) {
 
-  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  corsika_logger->set_pattern("[%n:%^%-8l%$] %s:%#: %v");
   logging::set_level(logging::level::info);
 
   CORSIKA_LOG_INFO("vertical_EAS");
 
-  if (argc < 4) {
-    std::cerr << "usage: vertical_EAS <A> <Z> <energy/GeV> [seed]" << std::endl;
-    std::cerr << "       if no seed is given, a random seed is chosen" << std::endl;
+  if (argc < 5) {
+    std::cerr << "usage: vertical_EAS <A> <Z> <energy/GeV> [seed] \n"
+                 "       if A=0, Z is interpreted as PDG code \n"
+                 "       if no seed is given, a random seed is chosen \n"
+              << std::endl;
     return 1;
   }
   feenableexcept(FE_INVALID);
 
+  string output_dir = "";
   int seed = 0;
-  if (argc > 4) seed = std::stoi(std::string(argv[4]));
+  int number_showers = std::stoi(std::string(argv[4]));
+  if (argc > 5) {
+    output_dir = argv[5];
+    if (output_dir.back() != '/' && output_dir != "") output_dir += '/';
+  }
+
+  if (argc > 6) { seed = std::stoi(std::string(argv[6])); }
+
   // initialize random number sequence(s)
   registerRandomStreams(seed);
 
@@ -129,171 +146,214 @@ int main(int argc, char** argv) {
   builder.addExponentialLayer(1144.9069_g / (1_cm * 1_cm), 878153.55_cm, 10_km);
   builder.addExponentialLayer(1305.5948_g / (1_cm * 1_cm), 636143.04_cm, 40_km);
   builder.addExponentialLayer(540.1778_g / (1_cm * 1_cm), 772170.16_cm, 100_km);
-  builder.addLinearLayer(1e9_cm, 112.8_km);
+  builder.addLinearLayer(1e9_cm, 112.8_km + constants::EarthRadius::Mean);
   builder.assemble(env);
 
-  // setup particle stack, and add primary particle
-  setup::Stack stack;
-  stack.clear();
-  const Code beamCode = Code::Nucleus;
-  unsigned short const A = std::stoi(std::string(argv[1]));
-  unsigned short Z = std::stoi(std::string(argv[2]));
-  auto const mass = get_nucleus_mass(A, Z);
-  const HEPEnergyType E0 = 1_GeV * std::stof(std::string(argv[3]));
-  double theta = 0.;
-  auto const thetaRad = theta / 180. * M_PI;
-
-  auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) {
-    return sqrt((Elab - m) * (Elab + m));
-  };
-  HEPMomentumType P0 = elab2plab(E0, mass);
-  auto momentumComponents = [](double thetaRad, HEPMomentumType ptot) {
-    return std::make_tuple(ptot * sin(thetaRad), 0_eV, -ptot * cos(thetaRad));
-  };
-
-  auto const [px, py, pz] = momentumComponents(thetaRad, P0);
-  auto plab = MomentumVector(rootCS, {px, py, pz});
-  cout << "input particle: " << beamCode << endl;
-  cout << "input angles: theta=" << theta << endl;
-  cout << "input momentum: " << plab.getComponents() / 1_GeV
-       << ", norm = " << plab.getNorm() << endl;
-
-  auto const observationHeight = 0_km + builder.getEarthRadius();
-  auto const injectionHeight = 112.75_km + builder.getEarthRadius();
-  auto const t = -observationHeight * cos(thetaRad) +
-                 sqrt(-static_pow<2>(sin(thetaRad) * observationHeight) +
-                      static_pow<2>(injectionHeight));
-  Point const showerCore{rootCS, 0_m, 0_m, observationHeight};
-  Point const injectionPos =
-      showerCore + DirectionVector{rootCS, {-sin(thetaRad), 0, cos(thetaRad)}} * t;
-
-  std::cout << "point of injection: " << injectionPos.getCoordinates() << std::endl;
-
-  if (A != 1) {
-    stack.addParticle(std::make_tuple(beamCode, E0, plab, injectionPos, 0_ns, A, Z));
-
-  } else {
-    if (Z == 1) {
-      stack.addParticle(std::make_tuple(Code::Proton, E0, plab, injectionPos, 0_ns));
-    } else if (Z == 0) {
-      stack.addParticle(std::make_tuple(Code::Neutron, E0, plab, injectionPos, 0_ns));
+  CORSIKA_LOG_DEBUG(
+      "environment setup: universe={}, layer1={}, layer2={}, layer3={}, layer4={}, "
+      "layer5={}",
+      fmt::ptr(env.getUniverse()->getContainingNode(
+          Point(rootCS, {constants::EarthRadius::Mean + 130_km, 0_m, 0_m}))),
+      fmt::ptr(env.getUniverse()->getContainingNode(
+          Point(rootCS, {constants::EarthRadius::Mean + 110_km, 0_m, 0_m}))),
+      fmt::ptr(env.getUniverse()->getContainingNode(
+          Point(rootCS, {constants::EarthRadius::Mean + 50_km, 0_m, 0_m}))),
+      fmt::ptr(env.getUniverse()->getContainingNode(
+          Point(rootCS, {constants::EarthRadius::Mean + 20_km, 0_m, 0_m}))),
+      fmt::ptr(env.getUniverse()->getContainingNode(
+          Point(rootCS, {constants::EarthRadius::Mean + 5_km, 0_m, 0_m}))),
+      fmt::ptr(env.getUniverse()->getContainingNode(
+          Point(rootCS, {constants::EarthRadius::Mean + 2_km, 0_m, 0_m}))));
+
+  for (int i_shower = 1; i_shower < number_showers + 1; i_shower++) {
+
+    // directory for outputs
+    string labHist_dir =
+        output_dir + "inthist_lab_verticalEAS_" + to_string(i_shower) + ".npz";
+    string cMSHist_dir =
+        output_dir + "inthist_cms_verticalEAS_" + to_string(i_shower) + ".npz";
+    string longprof_dir =
+        output_dir + "longprof_verticalEAS_" + to_string(i_shower) + ".txt";
+    string tracks_dir = output_dir + "tracks_" + to_string(i_shower) + ".dat";
+    string particles_dir = output_dir + "particles_" + to_string(i_shower) + ".dat";
+
+    std::cout << std::endl;
+    std::cout << "Shower " << i_shower << "/" << number_showers << std::endl;
+
+    // setup particle stack, and add primary particle
+    setup::Stack stack;
+    stack.clear();
+    unsigned short const A = std::stoi(std::string(argv[1]));
+    Code beamCode;
+    HEPEnergyType mass;
+    unsigned short Z = 0;
+    if (A > 0) {
+      beamCode = Code::Nucleus;
+      Z = std::stoi(std::string(argv[2]));
+      mass = get_nucleus_mass(A, Z);
     } else {
-      std::cerr << "illegal parameters" << std::endl;
-      return EXIT_FAILURE;
+      int pdg = std::stoi(std::string(argv[2]));
+      beamCode = convert_from_PDG(PDGCode(pdg));
+      mass = get_mass(beamCode);
     }
-  }
-
-  // we make the axis much longer than the inj-core distance since the
-  // profile will go beyond the core, depending on zenith angle
-  std::cout << "shower axis length: " << (showerCore - injectionPos).getNorm() * 1.5
-            << std::endl;
-
-  ShowerAxis const showerAxis{injectionPos, (showerCore - injectionPos) * 1.5, env};
-
-  // setup processes, decays and interactions
-
-  corsika::sibyll::Interaction sibyll;
-  InteractionCounter sibyllCounted(sibyll);
-
-  corsika::sibyll::NuclearInteraction sibyllNuc(sibyll, env);
-  InteractionCounter sibyllNucCounted(sibyllNuc);
-
-  corsika::pythia8::Decay decayPythia;
-
-  // use sibyll decay routine for decays of particles unknown to pythia
-  corsika::sibyll::Decay decaySibyll{{
-      Code::N1440Plus,
-      Code::N1440MinusBar,
-      Code::N1440_0,
-      Code::N1440_0Bar,
-      Code::N1710Plus,
-      Code::N1710MinusBar,
-      Code::N1710_0,
-      Code::N1710_0Bar,
-
-      Code::Pi1300Plus,
-      Code::Pi1300Minus,
-      Code::Pi1300_0,
-
-      Code::KStar0_1430_0,
-      Code::KStar0_1430_0Bar,
-      Code::KStar0_1430_Plus,
-      Code::KStar0_1430_MinusBar,
-  }};
-
-  decaySibyll.printDecayConfig();
-
-  // create the output manager that we then register outputs with
-  OutputManager output("vertical_EAS_outputs");
-
-  ParticleCut cut{60_GeV, 60_GeV, 60_GeV, 60_GeV, true};
-  corsika::proposal::Interaction proposal(env);
-  corsika::proposal::ContinuousProcess em_continuous(env);
-  InteractionCounter proposalCounted(proposal);
-
-  OnShellCheck reset_particle_mass(1.e-3, 1.e-1, false);
-
-  TrackWriter trackWriter;
-  output.add("tracks", trackWriter); // register TrackWriter
-
-  LongitudinalProfile longprof{showerAxis};
-
-  Plane const obsPlane(showerCore, DirectionVector(rootCS, {0., 0., 1.}));
-  ObservationPlane observationLevel(obsPlane, DirectionVector(rootCS, {1., 0., 0.}));
-
-  // register the observation plane with the output
-  output.add("obsplane", observationLevel);
-
-  corsika::urqmd::UrQMD urqmd;
-  InteractionCounter urqmdCounted{urqmd};
-  StackInspector<setup::Stack> stackInspect(1000, false, E0);
-
-  // assemble all processes into an ordered process list
-  struct EnergySwitch {
-    HEPEnergyType cutE_;
-    EnergySwitch(HEPEnergyType cutE)
-        : cutE_(cutE) {}
-    SwitchResult operator()(const Particle& p) {
-      if (p.getEnergy() < cutE_)
-        return SwitchResult::First;
-      else
-        return SwitchResult::Second;
+    HEPEnergyType const E0 = 1_GeV * std::stof(std::string(argv[3]));
+    double theta = 0.;
+    auto const thetaRad = theta / 180. * M_PI;
+
+    auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) {
+      return sqrt((Elab - m) * (Elab + m));
+    };
+    HEPMomentumType P0 = elab2plab(E0, mass);
+    auto momentumComponents = [](double thetaRad, HEPMomentumType ptot) {
+      return std::make_tuple(ptot * sin(thetaRad), 0_eV, -ptot * cos(thetaRad));
+    };
+
+    auto const [px, py, pz] = momentumComponents(thetaRad, P0);
+    auto plab = MomentumVector(rootCS, {px, py, pz});
+    cout << "input particle: " << beamCode << endl;
+    cout << "input angles: theta=" << theta << endl;
+    cout << "input momentum: " << plab.getComponents() / 1_GeV
+         << ", norm = " << plab.getNorm() << endl;
+
+    auto const observationHeight = 0_km + builder.getEarthRadius();
+    auto const injectionHeight = 111.75_km + builder.getEarthRadius();
+    auto const t = -observationHeight * cos(thetaRad) +
+                   sqrt(-static_pow<2>(sin(thetaRad) * observationHeight) +
+                        static_pow<2>(injectionHeight));
+    Point const showerCore{rootCS, 0_m, 0_m, observationHeight};
+    Point const injectionPos =
+        showerCore + DirectionVector{rootCS, {-sin(thetaRad), 0, cos(thetaRad)}} * t;
+
+    std::cout << "point of injection: " << injectionPos.getCoordinates() << std::endl;
+
+    if (A > 1) {
+      stack.addParticle(std::make_tuple(beamCode, E0, plab, injectionPos, 0_ns, A, Z));
+    } else {
+      if (A == 1) {
+        if (Z == 1) {
+          stack.addParticle(std::make_tuple(Code::Proton, E0, plab, injectionPos, 0_ns));
+        } else if (Z == 0) {
+          stack.addParticle(std::make_tuple(Code::Neutron, E0, plab, injectionPos, 0_ns));
+        } else {
+          std::cerr << "illegal parameters" << std::endl;
+          return EXIT_FAILURE;
+        }
+      } else {
+        stack.addParticle(std::make_tuple(beamCode, E0, plab, injectionPos, 0_ns));
+      }
     }
-  };
-  auto hadronSequence = make_select(
-      urqmdCounted, make_sequence(sibyllNucCounted, sibyllCounted), EnergySwitch(55_GeV));
-  auto decaySequence = make_sequence(decayPythia, decaySibyll);
-  auto sequence = make_sequence(stackInspect, hadronSequence, reset_particle_mass,
-                                decaySequence, proposalCounted, em_continuous, cut,
-                                trackWriter, observationLevel, longprof);
-
-  // define air shower object, run simulation
-  setup::Tracking tracking;
-  Cascade EAS(env, tracking, sequence, output, stack);
-
-  // to fix the point of first interaction, uncomment the following two lines:
-  //  EAS.forceInteraction();
-
-  EAS.run();
-
-  cut.showResults();
-  em_continuous.showResults();
-  observationLevel.showResults();
-  const HEPEnergyType Efinal = cut.getCutEnergy() + cut.getInvEnergy() +
-                               cut.getEmEnergy() + em_continuous.getEnergyLost() +
-                               observationLevel.getEnergyGround();
-  cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl
-       << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl;
-  observationLevel.reset();
-  cut.reset();
-  em_continuous.reset();
-
-  auto const hists = sibyllCounted.getHistogram() + sibyllNucCounted.getHistogram() +
-                     urqmdCounted.getHistogram() + proposalCounted.getHistogram();
-
-  save_hist(hists.labHist(), "inthist_lab_verticalEAS.npz", true);
-  save_hist(hists.CMSHist(), "inthist_cms_verticalEAS.npz", true);
-  longprof.save("longprof_verticalEAS.txt");
-
-  output.endOfLibrary();
-}
+
+    // we make the axis much longer than the inj-core distance since the
+    // profile will go beyond the core, depending on zenith angle
+    std::cout << "shower axis length: " << (showerCore - injectionPos).getNorm() * 1.5
+              << std::endl;
+
+    ShowerAxis const showerAxis{injectionPos, (showerCore - injectionPos) * 1.5, env};
+
+    // create the output manager that we then register outputs with
+    OutputManager output("vertical_EAS_outputs");
+
+    // setup processes, decays and interactions
+
+    // corsika::qgsjetII::Interaction qgsjet;
+    corsika::sibyll::Interaction sibyll;
+    InteractionCounter sibyllCounted(sibyll);
+
+    corsika::sibyll::NuclearInteraction sibyllNuc(sibyll, env);
+    InteractionCounter sibyllNucCounted(sibyllNuc);
+
+    corsika::pythia8::Decay decayPythia;
+
+    // use sibyll decay routine for decays of particles unknown to pythia
+    corsika::sibyll::Decay decaySibyll{{
+        Code::N1440Plus,
+        Code::N1440MinusBar,
+        Code::N1440_0,
+        Code::N1440_0Bar,
+        Code::N1710Plus,
+        Code::N1710MinusBar,
+        Code::N1710_0,
+        Code::N1710_0Bar,
+
+        Code::Pi1300Plus,
+        Code::Pi1300Minus,
+        Code::Pi1300_0,
+
+        Code::KStar0_1430_0,
+        Code::KStar0_1430_0Bar,
+        Code::KStar0_1430_Plus,
+        Code::KStar0_1430_MinusBar,
+    }};
+
+    decaySibyll.printDecayConfig();
+
+    ParticleCut cut{60_GeV, 60_GeV, 60_GeV, 60_GeV, true};
+    corsika::proposal::Interaction emCascade(env);
+    corsika::proposal::ContinuousProcess emContinuous(env);
+    InteractionCounter emCascadeCounted(emCascade);
+
+    OnShellCheck reset_particle_mass(1.e-3, 1.e-1, false);
+    TrackWriter trackWriter;
+    output.add("tracks", trackWriter); // register TrackWriter
+
+    LongitudinalProfile longprof{showerAxis};
+
+    Plane const obsPlane(showerCore, DirectionVector(rootCS, {0., 0., 1.}));
+    ObservationPlane observationLevel(obsPlane, DirectionVector(rootCS, {1., 0., 0.}));
+    // register the observation plane with the output
+    output.add("obsplane", observationLevel);
+
+    corsika::urqmd::UrQMD urqmd;
+    InteractionCounter urqmdCounted{urqmd};
+    StackInspector<setup::Stack> stackInspect(50000, false, E0);
+
+    // assemble all processes into an ordered process list
+    struct EnergySwitch {
+      HEPEnergyType cutE_;
+      EnergySwitch(HEPEnergyType cutE)
+          : cutE_(cutE) {}
+      SwitchResult operator()(const Particle& p) {
+        if (p.getEnergy() < cutE_)
+          return SwitchResult::First;
+        else
+          return SwitchResult::Second;
+      }
+    };
+    auto hadronSequence =
+        make_select(urqmdCounted, make_sequence(sibyllNucCounted, sibyllCounted),
+                    EnergySwitch(55_GeV));
+    auto decaySequence = make_sequence(decayPythia, decaySibyll);
+    auto sequence =
+        make_sequence(stackInspect, hadronSequence, reset_particle_mass, decaySequence,
+                      emContinuous, cut, trackWriter, observationLevel, longprof);
+
+    // define air shower object, run simulation
+    setup::Tracking tracking;
+    Cascade EAS(env, tracking, sequence, output, stack);
+
+    // to fix the point of first interaction, uncomment the following two lines:
+    //  EAS.forceInteraction();
+
+    EAS.run();
+
+    cut.showResults();
+    emContinuous.showResults();
+    observationLevel.showResults();
+    const HEPEnergyType Efinal = cut.getCutEnergy() + cut.getInvEnergy() +
+                                 cut.getEmEnergy() + emContinuous.getEnergyLost() +
+                                 observationLevel.getEnergyGround();
+    cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl
+         << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl;
+    observationLevel.reset();
+    cut.reset();
+    emContinuous.reset();
+
+    auto const hists = sibyllCounted.getHistogram() + sibyllNucCounted.getHistogram() +
+                       urqmdCounted.getHistogram();
+
+    save_hist(hists.labHist(), labHist_dir, true);
+    save_hist(hists.CMSHist(), cMSHist_dir, true);
+    longprof.save(longprof_dir);
+
+    output.endOfLibrary();
+  }
diff --git a/externals/cnpy/CMakeLists.txt b/externals/cnpy/CMakeLists.txt
index 0fa38d3bd16804b024f83700a2ba6c7c2c316c08..6e60f74d79ee4a641f398a37c9f108e5c487e0e9 100644
--- a/externals/cnpy/CMakeLists.txt
+++ b/externals/cnpy/CMakeLists.txt
@@ -21,12 +21,18 @@ target_include_directories (
   cnpy 
   INTERFACE 
   $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-  $<INSTALL_INTERFACE:include/externals/cnpy>
+  $<INSTALL_INTERFACE:corsika_external/cnpy/include>
+  )
+
+install (
+  FILES cnpy.hpp
+  DESTINATION corsika_external/cnpy/include
   )
 
 install (
   TARGETS cnpy
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
+  EXPORT CORSIKA8PublicTargets
+  LIBRARY DESTINATION corsika_external/cnpy/lib
+  ARCHIVE DESTINATION corsika_external/cnpy/lib
   )
 
diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt
deleted file mode 100644
index ed2ead3f69b33185506f3cd720e30820f7c551f1..0000000000000000000000000000000000000000
--- a/modules/CMakeLists.txt
+++ /dev/null
@@ -1,32 +0,0 @@
-cmake_minimum_required (VERSION 3.9)
-
-project (
-  corsika_modules
-  VERSION 8.0.0
-  DESCRIPTION "CORSIKA modules"
-  LANGUAGES CXX
-  )
-
-include (FeatureSummary)
-
-# as long as there still are modules using it:
-enable_language (Fortran)
-
-set (CMAKE_CXX_STANDARD 17)
-set (CMAKE_CXX_EXTENSIONS OFF)
-
-# enable warnings and disallow non-standard language
-# configure the various build types here, too
-# FYI: optimizer flags: -O2 would not trade speed for size, neither O2/3 use fast-math
-# debug: O0, relwithdebinfo: 02, release: O3, minsizerel: Os (all defaults)
-set (CMAKE_CXX_FLAGS "-Wall -pedantic -Wextra -Wno-ignored-qualifiers")
-set (CMAKE_Fortran_FLAGS "-std=legacy -Wfunction-elimination")
-
-# clang produces a lot of unecessary warnings without this:
-add_compile_options ("$<$<OR:$<CXX_COMPILER_ID:Clang>,$<CXX_COMPILER_ID:AppleClang>>:-Wno-nonportable-include-path>")
-
-# order of subdirectories
-add_subdirectory (Sibyll)
-#add_subdirectory (Pythia)
-add_subdirectory (QGSJetII)
-add_subdirectory (UrQMD)
diff --git a/modules/CMakeLists_PROPOSAL.txt b/modules/CMakeLists_PROPOSAL.txt
index a752d8f7be5a72349f908c2721461a69f3665e58..c487b17cdb880e772e0ac7ccc723f1133fdf71f9 100644
--- a/modules/CMakeLists_PROPOSAL.txt
+++ b/modules/CMakeLists_PROPOSAL.txt
@@ -1,11 +1,12 @@
-
-cmake_minimum_required(VERSION 3.8)
-
 if(CMAKE_PROJECT_NAME STREQUAL corsika)
   message(STATUS "Including PROPOSAL as part of CORSIKA8")
   set (IN_CORSIKA8 ON)
 else(CMAKE_PROJECT_NAME STREQUAL corsika)
   set (IN_CORSIKA8 OFF)
+
+  cmake_minimum_required(VERSION 3.8)
+  project(PROPOSAL VERSION 6.1.2 LANGUAGES CXX)
+  
 endif(CMAKE_PROJECT_NAME STREQUAL corsika)
 
 project(PROPOSAL VERSION 6.1.2 LANGUAGES CXX)
@@ -70,6 +71,13 @@ install(
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
 )
+else (NOT IN_CORSIKA8)
+install(
+    TARGETS PROPOSAL
+    EXPORT CORSIKA8PublicTargets
+    LIBRARY DESTINATION lib/corsika
+    ARCHIVE DESTINATION lib/corsika
+)
 endif (NOT IN_CORSIKA8)
 
 # input version from the project call, so the library knows its own version
@@ -82,21 +90,37 @@ install(
     FILES ${PROJECT_BINARY_DIR}/include/PROPOSAL/version.h
     DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/include/PROPOSAL
 )
-endif (NOT IN_CORSIKA8)
 target_include_directories(
     PROPOSAL PUBLIC
     $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
     $<INSTALL_INTERFACE:include>
 )
+else (NOT IN_CORSIKA8)
+install(
+    FILES ${PROJECT_BINARY_DIR}/include/PROPOSAL/version.h
+    DESTINATION include/corsika_modules/PROPOSAL
+)
+target_include_directories(
+    PROPOSAL PUBLIC
+    $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
+    $<INSTALL_INTERFACE:include/corsika_modules>
+)
+endif (NOT IN_CORSIKA8)
 
 # install header files
-if (NOT IN_CORSIKA8)
 file(GLOB_RECURSE INC_FILES ${PROJECT_SOURCE_DIR}/include/*)
+if (NOT IN_CORSIKA8)
 foreach(INC_FILE ${INC_FILES})
     file(RELATIVE_PATH REL_FILE ${PROJECT_SOURCE_DIR}/include ${INC_FILE})
     get_filename_component(DIR ${REL_FILE} DIRECTORY)
     install(FILES include/${REL_FILE} DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/${DIR})
 endforeach()
+else (NOT IN_CORSIKA8)
+foreach(INC_FILE ${INC_FILES})
+    file(RELATIVE_PATH REL_FILE ${PROJECT_SOURCE_DIR}/include/ ${INC_FILE})
+    get_filename_component(DIR ${REL_FILE} DIRECTORY)
+    install(FILES include/${REL_FILE} DESTINATION include/corsika_modules/${DIR})
+endforeach()
 endif (NOT IN_CORSIKA8)
 
 
diff --git a/modules/data b/modules/data
index 8b76a9ca2599cd0ce1f204b17362eb06bbcf5277..3c8292657e68dc697925ce1ddec491b219075c51 160000
--- a/modules/data
+++ b/modules/data
@@ -1 +1 @@
-Subproject commit 8b76a9ca2599cd0ce1f204b17362eb06bbcf5277
+Subproject commit 3c8292657e68dc697925ce1ddec491b219075c51
diff --git a/modules/pythia/CMakeLists.txt b/modules/pythia/CMakeLists.txt
deleted file mode 100644
index 980e42375a04347de5b8fd96cad79f6526d66a8a..0000000000000000000000000000000000000000
--- a/modules/pythia/CMakeLists.txt
+++ /dev/null
@@ -1,79 +0,0 @@
-include (ExternalProject)
-
-# eventually add AUTO here, too:
-set (ThirdPartyChoiceValues "C8;SYSTEM" CACHE STRING
-    "List of possible values for the ThirdParty package choice")
-mark_as_advanced (ThirdPartyChoiceValues)
-
-##############################################################################
-# check for Pythia8: either use C8 or system-level installation
-
-message ("***** Configuring Pythia8 version")
-
-set (USE_PYTHIA8_C8 "C8" CACHE STRING
-    "Selection of pythia8 package. Can be \'C8\' or \'SYSTEM\'. Default: \'C8\'.")
-set_property (CACHE USE_PYTHIA8_C8 PROPERTY STRINGS ${ThirdPartyChoiceValues})
-if (NOT (${USE_PYTHIA8_C8} IN_LIST ThirdPartyChoiceValues))
-  message (SEND_ERROR "Illegal USE_PYTHIA8_C8=\"${USE_PYTHIA8_C8}\" can only be one of: ${ThirdPartyChoiceValues}")
-endif (NOT (${USE_PYTHIA8_C8} IN_LIST ThirdPartyChoiceValues))
-message (STATUS "USE_PYTHIA8_C8='${USE_PYTHIA8_C8}'")
-
-add_library (C8::ext::pythia8 STATIC IMPORTED GLOBAL)
-if ("x_${USE_PYTHIA8_C8}" STREQUAL "x_SYSTEM")
-  
-  find_package (Pythia8 REQUIRED) 
-  message (STATUS "Using system-level Pythia8 version ${Pythia8_VERSION} at ${Pythia8_INCLUDE_DIR}")
-  set_target_properties (
-    C8::ext::pythia8 PROPERTIES
-    IMPORTED_LOCATION ${Pythia8_LIBRARY}/libpythia8.a
-    IMPORTED_LINK_INTERFACE_LIBRARIES dl
-    INTERFACE_INCLUDE_DIRECTORIES ${Pythia8_INCLUDE_DIR}
-    )
-  set (Pythia8_FOUND 1 PARENT_SCOPE)
-
-  message ("Pythia8_LIBRARY=${Pythia8_LIBRARY}")
-  message ("Pythia8_INCLUDE_DIR=${Pythia8_INCLUDE_DIR}")
-
-else ()
-
-  set (_C8_Pythia8_VERSION "8235")
-  message (STATUS "Building modules/pythia8 using pythia${_C8_Pythia8_VERSION}-stripped.tar.bz2")
-  message (STATUS "This will take a bit.....")
-  ExternalProject_Add (pythia8
-    URL ${CMAKE_CURRENT_SOURCE_DIR}/pythia${_C8_Pythia8_VERSION}-stripped.tar.bz2
-    URL_MD5 83132880c0594b808bd7fd37fb642606
-    SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/pythia8/source
-    INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/pythia8/install
-    CONFIGURE_COMMAND ./configure --cxx-common=-Wno-deprecated-copy --prefix=${CMAKE_CURRENT_BINARY_DIR}/pythia8/install
-    BUILD_IN_SOURCE ON
-    EXCLUDE_FROM_ALL TRUE
-    )
-  set (Pythia8_FOUND 1 PARENT_SCOPE)
-  ExternalProject_Get_Property (pythia8 INSTALL_DIR)
-  set (Pythia8_VERSION ${_C8_Pythia8_VERSION} CACHE STRING "Version of Pythia8")
-  set (Pythia8_PREFIX ${INSTALL_DIR})
-  set (Pythia8_INCLUDE_DIR  ${Pythia8_PREFIX}/include)
-  set (Pythia8_LIBRARY ${Pythia8_PREFIX}/lib/libpythia8.a)
-  add_dependencies (C8::ext::pythia8 pythia8)
-
-  # create include directory at config time
-  file (MAKE_DIRECTORY ${Pythia8_INCLUDE_DIR})
-
-  set (Pythia8_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/externals/pythia8)
-  install (DIRECTORY ${INSTALL_DIR}/ DESTINATION ${Pythia8_INSTALL_DIR})
-  
-  set_target_properties (
-    C8::ext::pythia8 PROPERTIES
-    IMPORTED_LOCATION ${Pythia8_LIBRARY}
-    IMPORTED_LINK_INTERFACE_LIBRARIES dl
-    INTERFACE_INCLUDE_DIRECTORIES
-      $<BUILD_INTERFACE:${Pythia8_INCLUDE_DIR}>
-    )
-
-    message (STATUS "Pythia8_INCLUDE_DIR ${Pythia8_INCLUDE_DIR}")
-endif ()
-
-
-##### add pythia8 to CORSIKA8 build
-add_dependencies (CORSIKA8 C8::ext::pythia8)
-target_link_libraries (CORSIKA8 INTERFACE C8::ext::pythia8)
diff --git a/modules/pythia/pythia8235-stripped.tar.bz2 b/modules/pythia/pythia8235-stripped.tar.bz2
deleted file mode 100644
index da280f738809e54eace85622485d13492448250e..0000000000000000000000000000000000000000
Binary files a/modules/pythia/pythia8235-stripped.tar.bz2 and /dev/null differ
diff --git a/modules/pythia8/CMakeLists.txt b/modules/pythia8/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..91433eb8ddcaec6a042eab83864a5763833ac046
--- /dev/null
+++ b/modules/pythia8/CMakeLists.txt
@@ -0,0 +1,123 @@
+include (ExternalProject)
+
+# eventually add AUTO here, too:
+set (ThirdPartyChoiceValues "C8;SYSTEM" CACHE STRING
+    "List of possible values for the ThirdParty package choice")
+mark_as_advanced (ThirdPartyChoiceValues)
+
+##############################################################################
+# check for Pythia8: either use C8 or system-level installation
+
+message (STATUS "***** Configuring Pythia8 version")
+
+set (USE_Pythia8_C8 "C8" CACHE STRING
+    "Selection of pythia8 package. Can be \'C8\' or \'SYSTEM\'. Default: \'C8\'.")
+set_property (CACHE USE_Pythia8_C8 PROPERTY STRINGS ${ThirdPartyChoiceValues})
+if (NOT (${USE_Pythia8_C8} IN_LIST ThirdPartyChoiceValues))
+  message (SEND_ERROR "Illegal USE_Pythia8_C8=\"${USE_Pythia8_C8}\" can only be one of: ${ThirdPartyChoiceValues}")
+endif (NOT (${USE_Pythia8_C8} IN_LIST ThirdPartyChoiceValues))
+message (STATUS "USE_Pythia8_C8='${USE_Pythia8_C8}'")
+
+set (CORSIKA_Pythia8_MODULE_DIR ${PROJECT_BINARY_DIR}/corsika/modules/pythia8)
+file (MAKE_DIRECTORY ${CORSIKA_Pythia8_MODULE_DIR})
+
+add_library (C8::ext::pythia8 STATIC IMPORTED GLOBAL)
+if ("x_${USE_Pythia8_C8}" STREQUAL "x_SYSTEM")
+  
+  find_package (Pythia8 8245 EXACT REQUIRED) 
+  message (STATUS "Using system-level Pythia8 version ${Pythia8_VERSION} at ${Pythia8_PREFIX}")
+  set (Pythia8_INCLUDE_DIRS ${Pythia8_INCLUDE_DIR})
+  set_target_properties (
+    C8::ext::pythia8 PROPERTIES
+    IMPORTED_LOCATION ${Pythia8_LIBRARY}/libpythia8.a
+    IMPORTED_LINK_INTERFACE_LIBRARIES dl
+    INTERFACE_INCLUDE_DIRECTORIES "${Pythia8_INCLUDE_DIRS}"
+    )
+  set (Pythia8_FOUND 1 PARENT_SCOPE)
+
+  set (CORSIKA_Pythia8_LOCATION ${Pythia8_PREFIX}) # build and install location, pythia8 is external
+  configure_file (Pythia8ConfigurationDirectory.hpp.in ${CORSIKA_Pythia8_MODULE_DIR}/Pythia8ConfigurationDirectory.hpp  @ONLY)
+  configure_file (Pythia8ConfigurationDirectory.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/Pythia8ConfigurationDirectory_install.hpp  @ONLY)
+
+  # install and build are both the same here since it is external
+  set (Pythia8_PREFIX_INSTALL ${Pythia8_PREFIX}) 
+  set (Pythia8_LIBDIR ${Pythia8_LIBRARY}) 
+  set (Pythia8_LIBDIR_INSTALL ${Pythia8_LIBRARY}) 
+  set (Pythia8_INCDIR ${Pythia8_INCLUDE_DIR}) 
+  set (Pythia8_INCDIR_INSTALL ${Pythia8_INCLUDE_DIR}) 
+  
+  message (STATUS "Pythia8 (system) at ${Pythia8_PREFIX}")
+
+else ()
+
+  set (_C8_Pythia8_VERSION "8245")
+  message (STATUS "Building modules/pythia8 using pythia${_C8_Pythia8_VERSION}-stripped.tar.bz2")
+  message (STATUS "This will take a bit.....")
+  # this is not a full Pythia8 install, it is a bit simplified, e.g. no pythia8-config, no examples
+  # and also, it is fitted into the normal CORSIKA8 install "include/corsika_modules/Pythia8, lib/corsika"
+  ExternalProject_Add (pythia8
+    URL ${CMAKE_CURRENT_SOURCE_DIR}/pythia${_C8_Pythia8_VERSION}-stripped.tar.bz2
+    URL_MD5 d3e951a2f101e8cfec26405cb61db83b
+    SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/pythia8/source"
+    INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/pythia8/install"
+    CONFIGURE_COMMAND ./configure --prefix=${CMAKE_CURRENT_BINARY_DIR}/pythia8/install
+    INSTALL_COMMAND make install
+    COMMAND rm -rf ${CMAKE_CURRENT_BINARY_DIR}/pythia8/install/bin
+    COMMAND rm -rf ${CMAKE_CURRENT_BINARY_DIR}/pythia8/install/share/Pythia8/examples
+    COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/pythia8/install/include/corsika_modules
+    COMMAND mkdir -p ${CMAKE_CURRENT_BINARY_DIR}/pythia8/install/lib/corsika
+    COMMAND rm -rf ${CMAKE_CURRENT_BINARY_DIR}/pythia8/install/include/corsika_modules/Pythia8
+    COMMAND mv -f ${CMAKE_CURRENT_BINARY_DIR}/pythia8/install/include/Pythia8 ${CMAKE_CURRENT_BINARY_DIR}/pythia8/install/include/corsika_modules/
+    COMMAND mv ${CMAKE_CURRENT_BINARY_DIR}/pythia8/install/lib/libpythia8.a  ${CMAKE_CURRENT_BINARY_DIR}/pythia8/install/lib/corsika/
+    BUILD_IN_SOURCE ON
+    EXCLUDE_FROM_ALL TRUE
+    )
+  set (Pythia8_FOUND 1 PARENT_SCOPE)
+  set (Pythia8_PREFIX_INSTALL ${CMAKE_INSTALL_PREFIX}) # this is "make install"
+  ExternalProject_Get_Property (pythia8 INSTALL_DIR) # this is "make"
+  set (Pythia8_VERSION ${_C8_Pythia8_VERSION} CACHE STRING "Version of Pythia8")
+  set (Pythia8_PREFIX ${INSTALL_DIR})
+  set (Pythia8_INCLUDE_DIR  ${Pythia8_PREFIX}/include/corsika_modules)
+  set (Pythia8_LIBDIR ${Pythia8_PREFIX}/lib/corsika)
+  set (Pythia8_LIBDIR_INSTALL ${Pythia8_PREFIX_INSTALL}/lib/corsika) 
+  set (Pythia8_INCDIR ${Pythia8_PREFIX}/include/corsika_modules)
+  set (Pythia8_INCDIR_INSTALL ${Pythia8_PREFIX_INSTALL}/include/corsika_modules) 
+  add_dependencies (C8::ext::pythia8 pythia8)
+
+  set (CORSIKA_Pythia8_LOCATION ${Pythia8_PREFIX}) # build location
+  configure_file (Pythia8ConfigurationDirectory.hpp.in ${CORSIKA_Pythia8_MODULE_DIR}/Pythia8ConfigurationDirectory.hpp  @ONLY)
+  set (CORSIKA_Pythia8_LOCATION ${Pythia8_PREFIX_INSTALL}) # install location
+  configure_file (Pythia8ConfigurationDirectory.hpp.in ${CMAKE_CURRENT_BINARY_DIR}/Pythia8ConfigurationDirectory_install.hpp  @ONLY)
+  
+  install (DIRECTORY ${Pythia8_PREFIX}/ DESTINATION ${Pythia8_PREFIX_INSTALL})
+
+  set (Pythia8_INCLUDE_DIRS ${Pythia8_INCLUDE_DIR})
+  
+  set_target_properties (
+    C8::ext::pythia8
+    PROPERTIES
+    IMPORTED_LOCATION ${Pythia8_LIBDIR}/libpythia8.a
+    IMPORTED_LINK_INTERFACE_LIBRARIES dl
+    INTERFACE_INCLUDE_DIRECTORIES "${Pythia8_INCLUDE_DIRS}"
+    )
+  
+    message (STATUS "Pythia8 build at: ${Pythia8_PREFIX}, final install to: ${Pythia8_PREFIX_INSTALL}")
+endif ()
+
+# the install location of Pythia8
+install (
+  FILES ${CMAKE_CURRENT_BINARY_DIR}/Pythia8ConfigurationDirectory_install.hpp
+  RENAME Pythia8ConfigurationDirectory.hpp
+  DESTINATION include/corsika/modules/pythia8
+  )
+
+##### add pythia8 to CORSIKA8 build
+add_dependencies (CORSIKA8 C8::ext::pythia8)
+target_link_libraries (CORSIKA8 INTERFACE C8::ext::pythia8)
+
+# we need those for exporting targets and dependencies:
+set (Pythia8_FOUND 1 PARENT_SCOPE)
+set (Pythia8_INCDIR ${Pythia8_INCDIR} PARENT_SCOPE)
+set (Pythia8_INCDIR_INSTALL ${Pythia8_INCDIR_INSTALL} PARENT_SCOPE)
+set (Pythia8_LIBDIR ${Pythia8_LIBDIR} PARENT_SCOPE)
+set (Pythia8_LIBDIR_INSTALL ${Pythia8_LIBDIR_INSTALL} PARENT_SCOPE)
diff --git a/modules/pythia8/Pythia8ConfigurationDirectory.hpp.in b/modules/pythia8/Pythia8ConfigurationDirectory.hpp.in
new file mode 100644
index 0000000000000000000000000000000000000000..d05df326aabcbfcd4c4342aa3621af2b116c8d7d
--- /dev/null
+++ b/modules/pythia8/Pythia8ConfigurationDirectory.hpp.in
@@ -0,0 +1,35 @@
+/*
+ * (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * This software is distributed under the terms of the GNU General Public
+ * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
+ * the license.
+ */
+
+#pragma once
+
+#include <string>
+
+/**
+ * \file ConfigurationDirectory.hpp
+ *
+ * The location of Pythia XMLDOC configuration data files (particle
+ * data, decays, PDFs etc.) can be at different locations, depending
+ * on wether system version of pythia8, or the local CORSIKA8 version
+ * has been used. Also for the latter it is build in the
+ * ${PROJECT_BINARY_DIR}/modules/pythia/pythia8/install, while during
+ * installation (make install) it is copied to
+ * ${CMAKE_INSTALL_PREFIX}/share/external/pythia8. Thus, at config
+ * time different version of ConfigurationDirectory.hpp are created to
+ * always point to the right locations. 
+ **/
+
+namespace corsika::pythia8 {
+
+  /**
+   *
+   * Location of the pythia XML directory with crucial input and config data, from cmake:
+   **/
+  static std::string const CORSIKA_Pythia8_XML_DIR("@CORSIKA_Pythia8_LOCATION@/share/Pythia8/xmldoc/");
+  
+}
diff --git a/modules/pythia8/pythia8245-stripped.tar.bz2 b/modules/pythia8/pythia8245-stripped.tar.bz2
new file mode 100644
index 0000000000000000000000000000000000000000..0ad96435f488cac464393fee13b4d5f4a38794fd
Binary files /dev/null and b/modules/pythia8/pythia8245-stripped.tar.bz2 differ
diff --git a/modules/pythia8/strip-pythia8-package.py b/modules/pythia8/strip-pythia8-package.py
new file mode 100755
index 0000000000000000000000000000000000000000..2ddd8435a2a9379ea7b168be6f6d40d41c976719
--- /dev/null
+++ b/modules/pythia8/strip-pythia8-package.py
@@ -0,0 +1,98 @@
+#!/usr/bin/env python3
+
+import os
+import sys
+import shutil
+
+if (not os.path.exists('bin/pythia8-config') or
+    not os.path.exists('include/Pythia8/Pythia.h') or
+    not os.path.exists('src/Event.cc')) :
+    print ("You can only run this script from within an unpacked PYTHIA tar filedirectory. This was not detected!")
+    sys.exit(1)
+
+# find version
+version = 'undefined'
+pythia_header = open('include/Pythia8/Pythia.h')
+for line in pythia_header:
+    line_list = line.strip().split(' ')
+    if (len(line_list) == 3 and
+        line_list[0] == '#define' and
+        line_list[1] == 'PYTHIA_VERSION_INTEGER') :
+        version = line_list[2]
+        break
+pythia_header.close()
+
+if (version == 'undefined'):
+    print ("Could not find Pythia version info")
+    sys.exit(2)
+
+print ("Pythia version: {}".format(version))
+
+if (os.path.exists('c8_package')) :
+    print ('Temporary directory \"c8_package\" already exists. Cleanup first.')
+    sys.exit(3)
+
+tmpdir = 'c8_package/pythia{}'.format(version)
+print ('create \"{}\"'.format(tmpdir))
+os.makedirs(tmpdir)
+
+copy_files_s = "AUTHORS  bin  CODINGSTYLE  configure  COPYING  GUIDELINES  include  Makefile  Makefile.inc  README  share  src"
+copy_files = copy_files_s.split()
+
+for filename in os.listdir('./') :
+    file = os.path.join('./', filename)
+    fileNew = os.path.join(tmpdir, filename)
+    if (filename in copy_files) :
+        isdir = ''
+        if (os.path.isdir(file)): isdir = ' [dir]'
+        print ('Keep \"{}\"{}'.format(file, isdir))
+        if (os.path.isfile(file)):
+            shutil.copy(file, tmpdir)
+        else:
+            shutil.copytree(file, fileNew)
+
+print ('Remove \"include/Pythia8Plugins\"')
+plugindir = os.path.join(tmpdir, 'include/Pythia8Plugins')
+if (os.path.exists(plugindir)):
+    shutil.rmtree(plugindir)
+    
+# remove anything  in share/Pythia but xmldoc that contains parameter definitions \"<parm\"
+sharedir = os.path.join(tmpdir, 'share/Pythia8')
+if (not os.path.isdir(sharedir)) :
+    print ('Why there is no \"share/Pythia8\" directory?')
+else :
+    print ('Only keep \"xmldoc\" inside \"share/Phythia8\"')
+
+    for filename in os.listdir(sharedir) :
+        file = os.path.join(sharedir, filename)
+        if (os.path.isdir(file)) :
+            if (not 'xmldoc' in file) :
+                print ('Remove \"{}\"'.format(file))
+                shutil.rmtree(file)
+            else :
+                cleanupXMLDOC = False
+                if (cleanupXMLDOC) :
+                    for filename_xml in os.listdir(file) :
+                        xml = os.path.join(file, filename_xml)
+                        xml_data = open(xml)
+                        keep = False
+                        for xml_line in xml_data:
+                            if ('<parm ' in xml_line) :
+                                keep = True
+                                break
+                        xml_data.close()
+                        if (not keep):
+                            print ('Removing {}'.format(xml))
+                            os.unlink(xml)
+
+# ... 'examples' subdirectory must exist ...
+os.mkdir(os.path.join(tmpdir, 'examples'))
+
+print ('Create tar file \"pythia{version}-stripped.tar.bz2\"'.format(version=version))
+os.system('cd c8_package && tar cjf pythia{version}-stripped.tar.bz2 pythia{version} && mv pythia{version}-stripped.tar.bz2 ..'.format(version=version))
+os.system('md5sum pythia{version}-stripped.tar.bz2'.format(version=version))
+
+# cleanup
+shutil.rmtree('c8_package')
+
+
diff --git a/modules/qgsjetII/CMakeLists.txt b/modules/qgsjetII/CMakeLists.txt
index 2ddb6b023f7e4d0320a7f827027c165d504b6a48..bef449e65c6e91cbc2066c73dcb389a1e2e1f87f 100644
--- a/modules/qgsjetII/CMakeLists.txt
+++ b/modules/qgsjetII/CMakeLists.txt
@@ -1,7 +1,3 @@
-cmake_minimum_required (VERSION 3.1)
-
-project (libQGSJetII)
-
 set (
   MODEL_SOURCES
   qgsjet-II-04-mod1.f
@@ -15,27 +11,52 @@ set (
 
 enable_language (Fortran)
 add_library (QGSJetII SHARED ${MODEL_SOURCES})
-set_target_properties (QGSJetII PROPERTIES POSITION_INDEPENDENT_CODE 1)
+set_target_properties (
+  QGSJetII
+  PROPERTIES
+  POSITION_INDEPENDENT_CODE 1
+  )
 
 target_include_directories (
   QGSJetII
   PUBLIC
   $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-  $<INSTALL_INTERFACE:include/include>
+  $<INSTALL_INTERFACE:include/corsika_modules/qgsjetII>
   )
 target_link_libraries (QGSJetII CorsikaData)
 
 add_library (QGSJetII_static STATIC ${MODEL_SOURCES})
-set_target_properties (QGSJetII_static PROPERTIES POSITION_INDEPENDENT_CODE 1)
+set_target_properties (
+  QGSJetII_static
+  PROPERTIES
+  POSITION_INDEPENDENT_CODE 1
+  )
 
 target_include_directories (
   QGSJetII_static
   PUBLIC
   $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-  $<INSTALL_INTERFACE:include/include>
+  $<INSTALL_INTERFACE:include/corsika_modules/qgsjetII>
   )
 target_link_libraries (QGSJetII_static CorsikaData)
 
+install (
+  FILES
+  ${MODEL_HEADERS}
+  DESTINATION include/corsika_modules/qgsjetII
+  )
+
+install (
+  TARGETS QGSJetII
+  EXPORT CORSIKA8PublicTargets
+  DESTINATION lib/corsika
+  )
+
+install (
+  TARGETS QGSJetII_static
+  EXPORT CORSIKA8PublicTargets
+  DESTINATION lib/corsika
+  )
 
 # add qgsjetII to corsika8 build
 add_dependencies (CORSIKA8 QGSJetII_static)
diff --git a/modules/sibyll/CMakeLists.txt b/modules/sibyll/CMakeLists.txt
index 6dfe2d5c3f739d4494024adfe04bff1d3b861749..c974237befbc23ad84283a71759a94867aa5a7e6 100644
--- a/modules/sibyll/CMakeLists.txt
+++ b/modules/sibyll/CMakeLists.txt
@@ -15,23 +15,32 @@ set (
 
 enable_language (Fortran)
 add_library (Sibyll SHARED ${MODEL_SOURCES})
-set_property(TARGET Sibyll PROPERTY POSITION_INDEPENDENT_CODE 1)
-
 add_library (Sibyll_static STATIC ${MODEL_SOURCES})
-set_property(TARGET Sibyll_static PROPERTY POSITION_INDEPENDENT_CODE 1)
+
+set_target_properties (
+  Sibyll
+  PROPERTIES
+  POSITION_INDEPENDENT_CODE 1
+  )
+
+set_target_properties (
+  Sibyll_static
+  PROPERTIES
+  POSITION_INDEPENDENT_CODE 1
+  )
 
 target_include_directories (
   Sibyll
   PUBLIC
   $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-  $<INSTALL_INTERFACE:include/include>
+  $<INSTALL_INTERFACE:include/corsika_modules/sibyll>
   )
 
 target_include_directories (
   Sibyll_static
   PUBLIC
   $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
-  $<INSTALL_INTERFACE:include/include>
+  $<INSTALL_INTERFACE:include/corsika_modules/sibyll>
   )
 
 target_link_libraries (
@@ -40,6 +49,24 @@ target_link_libraries (
   gfortran
   )
 
+install (
+  FILES
+  ${MODEL_HEADERS}
+  DESTINATION include/corsika_modules/sibyll
+  )
+
+install (
+  TARGETS Sibyll
+  EXPORT CORSIKA8PublicTargets
+  LIBRARY DESTINATION lib/corsika
+  )
+
+install (
+  TARGETS Sibyll_static
+  EXPORT CORSIKA8PublicTargets
+  ARCHIVE DESTINATION lib/corsika
+  )
+
 # add sibyll to corsika8 build
 add_dependencies (CORSIKA8 Sibyll_static)
 target_link_libraries (CORSIKA8 INTERFACE Sibyll_static)
diff --git a/modules/urqmd/CMakeLists.txt b/modules/urqmd/CMakeLists.txt
index 4c37ed1e0cd735cc3dca3364570e84dee550b5c0..b6048484806a24e2760f0684074def6726de1e7d 100644
--- a/modules/urqmd/CMakeLists.txt
+++ b/modules/urqmd/CMakeLists.txt
@@ -1,6 +1,3 @@
-cmake_minimum_required (VERSION 3.1)
-project (libUrQMD)
-
 set (
   MODEL_SOURCES
   urqmd.cpp
@@ -43,14 +40,48 @@ set (
   )
 
 enable_language (Fortran)
+
 add_library (UrQMD SHARED ${MODEL_SOURCES})
-target_include_directories (UrQMD PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-set_target_properties (UrQMD PROPERTIES POSITION_INDEPENDENT_CODE 1)
+target_include_directories (UrQMD PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+  $<INSTALL_INTERFACE:include/corsika_modules/urqmd>
+  )
+set_target_properties (
+  UrQMD
+  PROPERTIES
+  POSITION_INDEPENDENT_CODE 1
+  )
 
 add_library (UrQMD_static STATIC ${MODEL_SOURCES})
-target_include_directories (UrQMD_static PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
-set_target_properties (UrQMD_static PROPERTIES POSITION_INDEPENDENT_CODE 1)
+target_include_directories (UrQMD_static PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+  $<INSTALL_INTERFACE:include/corsika_modules/urqmd>
+  )
+set_target_properties (
+  UrQMD_static
+  PROPERTIES
+  POSITION_INDEPENDENT_CODE 1
+  )
 
+install (
+  FILES
+  ${MODEL_HEADERS}
+  DESTINATION include/corsika_modules/urqmd
+  )
+
+install (
+  TARGETS UrQMD
+  EXPORT CORSIKA8PublicTargets
+  LIBRARY DESTINATION lib/corsika
+  INCLUDES DESTINATION include/corsika_modules/urqmd
+  )
+
+install (
+  TARGETS UrQMD_static
+  EXPORT CORSIKA8PublicTargets
+  ARCHIVE DESTINATION lib/corsika
+  INCLUDES DESTINATION include/corsika_modules/urqmd
+  )
 
 # add UrQMD to CORSIKA8 build
 add_dependencies (CORSIKA8 UrQMD_static)
diff --git a/src/framework/core/CMakeLists.txt b/src/framework/core/CMakeLists.txt
index 68423b1aa03bddcfd9ff4328e8e3b0b7be91987b..558b227f68801ec98c794c832ae5d8e654c1a703 100644
--- a/src/framework/core/CMakeLists.txt
+++ b/src/framework/core/CMakeLists.txt
@@ -24,3 +24,10 @@ add_custom_target (GenParticlesHeaders
   DEPENDS ${output_dir}/GeneratedParticleProperties.inc
           ${output_dir}/GeneratedParticleClasses.inc)
 add_dependencies (CORSIKA8 GenParticlesHeaders)
+
+install (
+  FILES
+  ${output_dir}/GeneratedParticleProperties.inc
+  ${output_dir}/GeneratedParticleClasses.inc
+  DESTINATION include/corsika/framework/core
+  )
diff --git a/src/framework/core/ParticleData.xml b/src/framework/core/ParticleData.xml
index 40cd64c8a4e9b5dc03a8f87e32e308f16e3a3b94..db89d04628abe4613e0c4a673575bddb550aed9e 100644
--- a/src/framework/core/ParticleData.xml
+++ b/src/framework/core/ParticleData.xml
@@ -166,7 +166,7 @@
  <channel onMode="1" bRatio="0.1081660" products="-13 14"/> 
  <channel onMode="1" bRatio="0.1080870" products="-15 16"/> 
 </particle> 
- 
+ -->
 <particle id="25" name="h0" spinType="1" chargeType="0" colType="0" 
           m0="125.00000" mWidth="0.00374" mMin="50.00000" mMax="0.00000"> 
  <channel onMode="1" bRatio="0.0000009" products="1 -1"/> 
@@ -246,7 +246,7 @@
  <channel onMode="1" bRatio="0.0000000" meMode="103" products="1000016 -2000016"/> 
  <channel onMode="1" bRatio="0.0000000" meMode="103" products="-1000016 2000016"/> 
 </particle> 
- 
+<!-- 
 <particle id="32" name="Z'0" spinType="3" chargeType="0" colType="0" 
           m0="500.00000" mWidth="14.54029" mMin="10.00000" mMax="0.00000"> 
  <channel onMode="1" bRatio="0.1458350" products="1 -1"/> 
diff --git a/src/media/CMakeLists.txt b/src/media/CMakeLists.txt
index 94b578560d71feacf38c2abec26f37446762e313..df3b6e9eb6ba14999912a2652aa3d1395a002f39 100644
--- a/src/media/CMakeLists.txt
+++ b/src/media/CMakeLists.txt
@@ -16,3 +16,8 @@ add_custom_command (
 
 add_custom_target (GenMediaProperties DEPENDS ${output_dir}/GeneratedMediaProperties.inc)
 add_dependencies (CORSIKA8 GenMediaProperties)
+
+install (
+  FILES ${output_dir}/GeneratedMediaProperties.inc
+  DESTINATION include/corsika/media
+  )
diff --git a/src/modules/qgsjetII/CMakeLists.txt b/src/modules/qgsjetII/CMakeLists.txt
index 297e2690ddf4119494eb2736aa68293109699c69..36ebe9b693b927de9acfca03be3dc016440c4050 100644
--- a/src/modules/qgsjetII/CMakeLists.txt
+++ b/src/modules/qgsjetII/CMakeLists.txt
@@ -20,3 +20,7 @@ add_custom_command (
 add_custom_target (SourceDirLinkQgs DEPENDS ${output_dir}/Generated.inc)
 add_dependencies (CORSIKA8 SourceDirLinkQgs)
 
+install (
+  FILES ${output_dir}/Generated.inc
+  DESTINATION include/corsika/modules/qgsjetII
+  )
diff --git a/src/modules/qgsjetII/code_generator.py b/src/modules/qgsjetII/code_generator.py
index ce7fcae263f6e2b9a64b08c5ef56fc85aa99e9b7..8321aad19ab1ae30252b930800a62aa7f0fd7b6b 100755
--- a/src/modules/qgsjetII/code_generator.py
+++ b/src/modules/qgsjetII/code_generator.py
@@ -93,7 +93,7 @@ def read_qgsjetII_codes(filename, particle_db):
             if len(line)==0 or line[0] == '#':
                 continue
             line = line.split('#')[0]
-            print (line)
+            print ('QGSJetII codes: ', line)
             identifier, model_code, xsType = line.split()
             try:
                 particle_db[identifier]["qgsjetII_code"] = int(model_code)
@@ -202,7 +202,7 @@ if __name__ == "__main__":
     particle_db = load_particledb(sys.argv[1])
     read_qgsjetII_codes(sys.argv[2], particle_db)
     set_default_qgsjetII_definition(particle_db)
-    
+
     with open("Generated.inc", "w") as f:
         print("// this file is automatically generated\n// edit at your own risk!\n", file=f)
         print(generate_qgsjetII_start(), file=f)
diff --git a/src/modules/sibyll/CMakeLists.txt b/src/modules/sibyll/CMakeLists.txt
index c2e6dd0bfc4840f59f4ce33a981186bdcff3b248..b3d97a12349a5a5f2cbd9917404a8685894f0096 100644
--- a/src/modules/sibyll/CMakeLists.txt
+++ b/src/modules/sibyll/CMakeLists.txt
@@ -20,3 +20,7 @@ add_custom_command (
 add_custom_target (SourceDirLinkSib DEPENDS ${output_dir}/Generated.inc)
 add_dependencies (CORSIKA8 SourceDirLinkSib)
 
+install (
+  FILES ${output_dir}/Generated.inc
+  DESTINATION include/corsika/modules/sibyll
+  )
diff --git a/tests/common/SetupTestTrajectory.hpp b/tests/common/SetupTestTrajectory.hpp
index 0c06d7f39aea76fd2b466ca1974d95a616bcfefb..8272317193a1d1864b5c7928c22947dcc7243e2f 100644
--- a/tests/common/SetupTestTrajectory.hpp
+++ b/tests/common/SetupTestTrajectory.hpp
@@ -13,33 +13,34 @@
 #include <corsika/framework/geometry/Helix.hpp>
 #include <corsika/framework/geometry/StraightTrajectory.hpp>
 
-// #include <corsika/framework/geometry/LeapFrogTrajectory.hpp>
-// #include <corsika/modules/TrackingLine.hpp>
-// #include <corsika/modules/TrackingCurved.hpp> // simple leap-frog implementation
-// #include <corsika/modules/TrackingLeapFrog.hpp> // more complete leap-frog
+#include <corsika/framework/geometry/LeapFrogTrajectory.hpp>
+//#include <corsika/modules/TrackingLine.hpp>
+//#include <corsika/modules/TrackingCurved.hpp> // simple leap-frog implementation
+//#include <corsika/modules/TrackingLeapFrog.hpp> // more complete leap-frog
 // implementation
 
 namespace corsika::setup::testing {
 
   template <typename TTrack>
-  TTrack make_track(Line const& line, TimeType const tEnd);
+  TTrack make_track(
+      Line const line,
+      TimeType const tEnd = std::numeric_limits<TimeType::value_type>::infinity() * 1_s);
 
   template <>
-  inline StraightTrajectory make_track<StraightTrajectory>(Line const& line,
+  inline StraightTrajectory make_track<StraightTrajectory>(Line const line,
                                                            TimeType const tEnd) {
     return StraightTrajectory(line, tEnd);
   }
 
-  /*
-    template <>
-    inline LeapFrogTrajectory make_track<LeapFrogTrajectory>(Line const& line,
-                                                             TimeType const tEnd) {
-
-      auto const k = square(0_m) / (square(1_s) * 1_V);
-      return LeapFrogTrajectory(
-          line.getStartPoint(), line.getVelocity(),
-          MagneticFieldVector{line.getStartPoint().getCoordinateSystem(), 0_T, 0_T, 0_T},
-          k, tEnd);
-    }
-  */
+  template <>
+  inline LeapFrogTrajectory make_track<LeapFrogTrajectory>(Line const line,
+                                                           TimeType const tEnd) {
+
+    auto const k = square(0_m) / (square(1_s) * 1_V);
+    return LeapFrogTrajectory(
+        line.getStartPoint(), line.getVelocity(),
+        MagneticFieldVector{line.getStartPoint().getCoordinateSystem(), 0_T, 0_T, 0_T}, k,
+        tEnd);
+  }
+
 } // namespace corsika::setup::testing
diff --git a/tests/framework/testCascade.cpp b/tests/framework/testCascade.cpp
index 8a7978b8446677132529b41c2c905abe7dab9cef..633f30f47328970be8374552ecbb86411aaa6936 100644
--- a/tests/framework/testCascade.cpp
+++ b/tests/framework/testCascade.cpp
@@ -24,6 +24,8 @@
 #include <corsika/media/HomogeneousMedium.hpp>
 #include <corsika/media/NuclearComposition.hpp>
 
+#include <SetupTestTrajectory.hpp>
+
 #include <catch2/catch.hpp>
 
 using namespace corsika;
@@ -68,14 +70,11 @@ public:
   auto getTrack(TParticle const& particle) {
     VelocityVector const initialVelocity =
         particle.getMomentum() / particle.getEnergy() * constants::c;
+    Line const theLine = Line(particle.getPosition(), initialVelocity);
+    TimeType const tEnd = std::numeric_limits<TimeType::value_type>::infinity() * 1_s;
     return std::make_tuple(
-        StraightTrajectory(
-            Line(particle.getPosition(), initialVelocity),
-            std::numeric_limits<TimeType::value_type>::infinity() * 1_s), // trajectory,
-                                                                          // just
-                                                                          // go
-                                                                          // ahead
-                                                                          // forever
+        corsika::setup::testing::make_track<setup::Trajectory>(theLine, tEnd),
+        // trajectory: just go ahead forever
         particle.getNode()); // next volume node
   }
 };
diff --git a/tests/framework/testFunctionTimer.cpp b/tests/framework/testFunctionTimer.cpp
index f0bc38e67ec68a7aa85c898f007e1fd405b4dc1c..46d97f710f5793a5f27bbc4ed255c49cbdad627b 100644
--- a/tests/framework/testFunctionTimer.cpp
+++ b/tests/framework/testFunctionTimer.cpp
@@ -12,7 +12,6 @@
 #include <catch2/catch.hpp>
 
 #include <chrono>
-#include <iostream>
 #include <thread>
 
 using namespace corsika;
@@ -39,15 +38,15 @@ TEST_CASE("FunctionTimer", "[Timer]") {
 
     auto test = corsika::FunctionTimer(testFunc);
 
-    std::cout << test() << std::endl;
-    std::cout << test.getTime().count() << std::endl;
+    CORSIKA_LOG_DEBUG(test());
+    CORSIKA_LOG_DEBUG(test.getTime().count());
   }
 
   SECTION("Measure runtime of a class functor") {
     TestClass testC;
     auto test = corsika::FunctionTimer(testC);
 
-    std::cout << test() << std::endl;
-    std::cout << test.getTime().count() << std::endl;
+    CORSIKA_LOG_DEBUG(test());
+    CORSIKA_LOG_DEBUG(test.getTime().count());
   }
 }
diff --git a/tests/framework/testGeometry.cpp b/tests/framework/testGeometry.cpp
index a39201c04de97d13ccc24343cd5b4a0148a2c5a5..e8847ee5e824242344147f46afeb37dfe0eb3708 100644
--- a/tests/framework/testGeometry.cpp
+++ b/tests/framework/testGeometry.cpp
@@ -26,18 +26,20 @@ using namespace corsika::testing;
 
 double constexpr absMargin = 1.0e-8;
 
-TEST_CASE("transformations between CoordinateSystems") {
+TEST_CASE("Geometry CoordinateSystems") {
 
   logging::set_level(logging::level::info);
-  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  corsika_logger->set_pattern("[%n:%^%-8l%$] %v");
 
   CoordinateSystemPtr rootCS = get_root_CoordinateSystem();
 
   QuantityVector<length_d> const coordinates{0_m, 0_m, 0_m};
   Point p1(rootCS, coordinates);
+  CORSIKA_LOG_INFO("Point p1={}", p1);
 
   QuantityVector<magnetic_flux_density_d> components{1. * tesla, 0. * tesla, 0. * tesla};
   Vector<magnetic_flux_density_d> v1(rootCS, components);
+  CORSIKA_LOG_INFO("Vector<magnetic_flux_density_d> v1={}", v1);
 
   CHECK((p1.getCoordinates() - coordinates).getNorm().magnitude() ==
         Approx(0).margin(absMargin));
@@ -46,6 +48,7 @@ TEST_CASE("transformations between CoordinateSystems") {
 
   SECTION("translations") {
     QuantityVector<length_d> const translationVector{0_m, 4_m, 0_m};
+    CORSIKA_LOG_INFO("QuantityVector<length_d> translationVector={}", translationVector);
 
     CoordinateSystemPtr translatedCS = make_translation(rootCS, translationVector);
 
@@ -184,7 +187,7 @@ TEST_CASE("transformations between CoordinateSystems") {
   }
 }
 
-TEST_CASE("CoordinateSystem hirarchy") {
+TEST_CASE("Geometry CoordinateSystem-hirarchy") {
 
   CoordinateSystemPtr rootCS = get_root_CoordinateSystem();
 
@@ -232,7 +235,7 @@ TEST_CASE("CoordinateSystem hirarchy") {
   CHECK((p1 - p6).getNorm().magnitude() == Approx(0).margin(absMargin));
 }
 
-TEST_CASE("Sphere") {
+TEST_CASE("Geometry Sphere") {
   CoordinateSystemPtr const& rootCS = get_root_CoordinateSystem();
   Point center(rootCS, {0_m, 3_m, 4_m});
   Sphere sphere(center, 5_m);
@@ -251,7 +254,7 @@ TEST_CASE("Sphere") {
   }
 }
 
-TEST_CASE("Trajectories") {
+TEST_CASE("Geometry Trajectories") {
   CoordinateSystemPtr rootCS = get_root_CoordinateSystem();
   Point r0(rootCS, {0_m, 0_m, 0_m});
 
diff --git a/tests/framework/testInteractionCounter.cpp b/tests/framework/testInteractionCounter.cpp
index bf73fc578316eb3c7e948c22671f6a1aad220ddc..556d4733b60b82bf295ff47463a24d3775c48553 100644
--- a/tests/framework/testInteractionCounter.cpp
+++ b/tests/framework/testInteractionCounter.cpp
@@ -83,7 +83,7 @@ TEST_CASE("InteractionCounter", "[process]") {
       auto const file = GENERATE(as<std::string>{}, "testInteractionCounter_file1",
                                  "testInteractionCounter_file2");
 
-      std::cout << file + ".npz vs " << refDataDir + "/" + file + "_REF.npz" << std::endl;
+      CORSIKA_LOG_INFO("{0}.npz vs {1}/{0}_REF.npz", file, refDataDir);
 
       // compare to binary reference data
       // note that this currenly compares the whole files byte by byte. If the new
diff --git a/tests/framework/testProcessSequence.cpp b/tests/framework/testProcessSequence.cpp
index ac80847cabbd24913175b44a6a78e62af7e9db2c..efdaa2fc45fe55945b331d6dd741cfaf294dee09 100644
--- a/tests/framework/testProcessSequence.cpp
+++ b/tests/framework/testProcessSequence.cpp
@@ -9,6 +9,8 @@
 #include <corsika/framework/process/ProcessSequence.hpp>
 #include <corsika/framework/process/SwitchProcessSequence.hpp>
 #include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/process/ProcessTraits.hpp>
+#include <corsika/framework/process/ContinuousProcessStepLength.hpp>
 
 #include <catch2/catch.hpp>
 
@@ -17,10 +19,12 @@
 #include <iostream>
 #include <typeinfo>
 
+#include <boost/type_index.hpp>
+
 using namespace corsika;
 using namespace std;
 
-static const int nData = 10;
+static int const nData = 10;
 
 int globalCount = 0; // simple counter
 
@@ -31,75 +35,130 @@ int checkCont = 0;     // use this as a bit field
 
 class ContinuousProcess1 : public ContinuousProcess<ContinuousProcess1> {
 public:
-  ContinuousProcess1(const int v)
-      : v_(v) {
-
-    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
+  ContinuousProcess1(int const v, LengthType const step)
+      : v_(v)
+      , step_(step) {
+
+    CORSIKA_LOG_DEBUG(
+        "globalCount: {} "
+        ", v_: {} ",
+        globalCount, v_);
     globalCount++;
   }
 
+  void setStep(LengthType const v) { step_ = v; }
+
   template <typename D, typename T>
-  inline ProcessReturn doContinuous(D& d, T&) const {
-    cout << "ContinuousProcess1::DoContinuous" << endl;
+  ProcessReturn doContinuous(D& d, T&, bool const flag) const {
+    flag_ = flag;
+    CORSIKA_LOG_TRACE("ContinuousProcess1::DoContinuous");
     checkCont |= 1;
     for (int i = 0; i < nData; ++i) d.data_[i] += 0.933;
     return ProcessReturn::Ok;
   }
 
+  template <typename TParticle, typename TTrack>
+  LengthType getMaxStepLength(TParticle&, TTrack&) {
+    return step_;
+  }
+
+  bool getFlag() const { return flag_; }
+  void resetFlag() { flag_ = false; }
+
 private:
   int v_ = 0;
+  LengthType step_ = 0_m;
+  mutable bool flag_ = false;
 };
 
 class ContinuousProcess2 : public ContinuousProcess<ContinuousProcess2> {
 public:
-  ContinuousProcess2(const int v)
-      : v_(v) {
-    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
+  ContinuousProcess2(int const v, LengthType const step)
+      : v_(v)
+      , step_(step) {
+    CORSIKA_LOG_DEBUG(
+        "globalCount: {}"
+        ", v_: {}",
+        globalCount, v_);
     globalCount++;
   }
 
+  void setStep(LengthType const v) { step_ = v; }
+
   template <typename D, typename T>
-  inline ProcessReturn doContinuous(D& d, T&) const {
-    cout << "ContinuousProcess2::DoContinuous" << endl;
+  ProcessReturn doContinuous(D& d, T&, bool const flag) const {
+    flag_ = flag;
+    CORSIKA_LOG_DEBUG("ContinuousProcess2::DoContinuous");
     checkCont |= 2;
     for (int i = 0; i < nData; ++i) d.data_[i] += 0.111;
     return ProcessReturn::Ok;
   }
 
+  template <typename TParticle, typename TTrack>
+  LengthType getMaxStepLength(TParticle&, TTrack&) {
+    return step_;
+  }
+
+  bool getFlag() const { return flag_; }
+  void resetFlag() { flag_ = false; }
+
 private:
   int v_ = 0;
+  LengthType step_ = 0_m;
+  mutable bool flag_ = false;
 };
 
 class ContinuousProcess3 : public ContinuousProcess<ContinuousProcess3> {
 public:
-  ContinuousProcess3(const int v)
-      : v_(v) {
-    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
+  ContinuousProcess3(int const v, LengthType const step)
+      : v_(v)
+      , step_(step) {
+    CORSIKA_LOG_DEBUG(
+        "globalCount: {}"
+        ", v_: {} ",
+        globalCount, v_);
     globalCount++;
   }
 
+  void setStep(LengthType const v) { step_ = v; }
+
   template <typename D, typename T>
-  inline ProcessReturn doContinuous(D& d, T&) const {
-    cout << "ContinuousProcess3::DoContinuous" << endl;
+  ProcessReturn doContinuous(D& d, T&, bool const flag) const {
+    flag_ = flag;
+    CORSIKA_LOG_DEBUG("ContinuousProcess3::DoContinuous");
     checkCont |= 4;
     for (int i = 0; i < nData; ++i) d.data_[i] += 0.333;
     return ProcessReturn::Ok;
   }
 
+  template <typename TParticle, typename TTrack>
+  LengthType getMaxStepLength(TParticle&, TTrack&) {
+    return step_;
+  }
+
+  bool getFlag() const { return flag_; }
+  void resetFlag() { flag_ = false; }
+
 private:
   int v_ = 0;
+  LengthType step_ = 0_m;
+  mutable bool flag_ = false;
 };
 
 class Process1 : public InteractionProcess<Process1> {
 public:
-  Process1(const int v)
+  Process1(int const v)
       : v_(v) {
-    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
+    CORSIKA_LOG_DEBUG(
+        "globalCount: {}"
+        ", v_: {}",
+        globalCount, v_);
+    ;
     globalCount++;
   }
 
   template <typename TView>
-  inline void doInteraction(TView& v) const {
+  void doInteraction(TView& v) const {
     checkInteract |= 1;
     for (int i = 0; i < nData; ++i) v.parent().data_[i] += 1 + i;
   }
@@ -115,21 +174,24 @@ private:
 
 class Process2 : public InteractionProcess<Process2> {
 public:
-  Process2(const int v)
+  Process2(int const v)
       : v_(v) {
-    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
+    CORSIKA_LOG_DEBUG(
+        "globalCount: {}"
+        ", v_: {}",
+        globalCount, v_);
     globalCount++;
   }
 
   template <typename TView>
-  inline void doInteraction(TView& v) const {
+  void doInteraction(TView& v) const {
     checkInteract |= 2;
     for (int i = 0; i < nData; ++i) v.parent().data_[i] /= 1.1;
-    cout << "Process2::doInteraction" << endl;
+    CORSIKA_LOG_DEBUG("Process2::doInteraction");
   }
   template <typename Particle>
   GrammageType getInteractionLength(Particle&) const {
-    cout << "Process2::GetInteractionLength" << endl;
+    CORSIKA_LOG_DEBUG("Process2::GetInteractionLength");
     return 20_g / (1_cm * 1_cm);
   }
 
@@ -139,21 +201,24 @@ private:
 
 class Process3 : public InteractionProcess<Process3> {
 public:
-  Process3(const int v)
+  Process3(int const v)
       : v_(v) {
-    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
+    CORSIKA_LOG_DEBUG(
+        "globalCount: {}"
+        ", v_: {}",
+        globalCount, v_);
     globalCount++;
   }
 
   template <typename TView>
-  inline void doInteraction(TView& v) const {
+  void doInteraction(TView& v) const {
     checkInteract |= 4;
     for (int i = 0; i < nData; ++i) v.parent().data_[i] *= 1.01;
-    cout << "Process3::doInteraction" << endl;
+    CORSIKA_LOG_DEBUG("Process3::doInteraction");
   }
   template <typename Particle>
   GrammageType getInteractionLength(Particle&) const {
-    cout << "Process3::GetInteractionLength" << endl;
+    CORSIKA_LOG_DEBUG("Process3::GetInteractionLength");
     return 30_g / (1_cm * 1_cm);
   }
 
@@ -163,15 +228,18 @@ private:
 
 class Process4 : public BaseProcess<Process4> {
 public:
-  Process4(const int v)
+  Process4(int const v)
       : v_(v) {
-    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
+    CORSIKA_LOG_DEBUG(
+        "globalCount: {}"
+        ", v_: {}",
+        globalCount, v_);
     globalCount++;
   }
 
   template <typename D, typename T>
-  inline ProcessReturn doContinuous(D& d, T&) const {
-    std::cout << "Base::doContinuous" << std::endl;
+  ProcessReturn doContinuous(D& d, T&, bool const) const {
+    CORSIKA_LOG_DEBUG("Base::doContinuous");
     checkCont |= 8;
     for (int i = 0; i < nData; ++i) { d.data_[i] /= 1.2; }
     return ProcessReturn::Ok;
@@ -187,8 +255,8 @@ private:
 
 class Decay1 : public DecayProcess<Decay1> {
 public:
-  Decay1(const int) {
-    cout << "Decay1()" << endl;
+  Decay1(int const) {
+    CORSIKA_LOG_DEBUG("Decay1()");
     globalCount++;
   }
 
@@ -204,8 +272,8 @@ public:
 
 class Decay2 : public DecayProcess<Decay2> {
 public:
-  Decay2(const int) {
-    cout << "Decay2()" << endl;
+  Decay2(int const) {
+    CORSIKA_LOG_DEBUG("Decay2()");
     globalCount++;
   }
 
@@ -221,7 +289,7 @@ public:
 
 class Stack1 : public StackProcess<Stack1> {
 public:
-  Stack1(const int n)
+  Stack1(int const n)
       : StackProcess(n) {}
   template <typename TStack>
   ProcessReturn doStack(TStack&) {
@@ -234,12 +302,16 @@ private:
   int count_ = 0;
 };
 
+// The stack is non-existent for this example
 struct DummyStack {};
+// our data object (particle) is a simple arrary of doubles
 struct DummyData {
   double data_[nData] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
 };
+// there is no real trajectory/track
 struct DummyTrajectory {};
-
+// since there is no stack, there is also no view. This is a simplistic dummy object
+// sufficient here.
 struct DummyView {
   DummyView(DummyData& p)
       : p_(p) {}
@@ -247,10 +319,22 @@ struct DummyView {
   DummyData& parent() { return p_; }
 };
 
-TEST_CASE("Process Sequence", "[Process Sequence]") {
+TEST_CASE("ProcessSequence General", "ProcessSequence") {
 
   logging::set_level(logging::level::info);
-  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  corsika_logger->set_pattern("[%n:%^%-8l%$]: %v");
+
+  SECTION("BaseProcess") {
+
+    Process1 m1(0);
+    const Process4 m4(3);
+
+    CHECK(is_process_v<Process1>);
+    CHECK_FALSE(is_process_v<DummyData>);
+    CHECK(is_process_v<decltype(m4)>);
+    CHECK(is_process_v<decltype(Decay1(1))>);
+    CHECK(is_process_v<decltype(ContinuousProcess3{3, 3_m})>);
+  }
 
   SECTION("Check construction") {
     globalCount = 0;
@@ -264,10 +348,15 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
     CHECK(globalCount == 4);
 
     auto sequence1 = make_sequence(m1, m2, m3, m4);
-    CHECK(is_process_sequence_v<decltype(sequence1)> == true);
-    CHECK(is_process_sequence_v<decltype(m2)> == false);
-    CHECK(is_switch_process_sequence_v<decltype(sequence1)> == false);
-    CHECK(is_switch_process_sequence_v<decltype(m2)> == false);
+    CHECK(is_process_v<decltype(sequence1)>);
+    CHECK(is_process_v<decltype(m2)>);
+    CHECK(is_process_sequence_v<decltype(sequence1)>);
+    CHECK_FALSE(is_process_sequence_v<decltype(m2)>);
+    CHECK_FALSE(is_switch_process_sequence_v<decltype(sequence1)>);
+    CHECK_FALSE(is_switch_process_sequence_v<decltype(m2)>);
+
+    CHECK_FALSE(is_process_sequence_v<decltype(Decay1(7))>);
+    CHECK_FALSE(is_switch_process_sequence_v<decltype(Decay1(7))>);
 
     auto sequence2 = make_sequence(m1, m2, m3);
     CHECK(is_process_sequence_v<decltype(sequence2)> == true);
@@ -278,7 +367,7 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
 
   SECTION("interaction length") {
     globalCount = 0;
-    ContinuousProcess1 cp1(0);
+    ContinuousProcess1 cp1(0, 1_m);
     Process2 m2(1);
     Process3 m3(2);
 
@@ -287,7 +376,10 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
     auto sequence2 = make_sequence(cp1, m2, m3);
     GrammageType const tot = sequence2.getInteractionLength(particle);
     InverseGrammageType const tot_inv = sequence2.getInverseInteractionLength(particle);
-    cout << "lambda_tot=" << tot << "; lambda_tot_inv=" << tot_inv << endl;
+    CORSIKA_LOG_DEBUG(
+        "lambda_tot={}"
+        "; lambda_tot_inv={}",
+        tot, tot_inv);
 
     CHECK(tot / 1_g * square(1_cm) == 12);
     CHECK(tot_inv * 1_g / square(1_cm) == 1. / 12);
@@ -296,7 +388,7 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
 
   SECTION("lifetime") {
     globalCount = 0;
-    ContinuousProcess1 cp1(0);
+    ContinuousProcess1 cp1(0, 1_m);
     Process2 m2(1);
     Process3 m3(2);
     Decay1 d3(3);
@@ -306,43 +398,77 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
     auto sequence2 = make_sequence(cp1, m2, m3, d3);
     TimeType const tot = sequence2.getLifetime(particle);
     InverseTimeType const tot_inv = sequence2.getInverseLifetime(particle);
-    cout << "lambda_tot=" << tot << "; lambda_tot_inv=" << tot_inv << endl;
+    CORSIKA_LOG_DEBUG(
+        "lambda_tot={}"
+        "; lambda_tot_inv={}",
+        tot, tot_inv);
 
     CHECK(tot / 1_s == 1);
     CHECK(tot_inv * 1_s == 1.);
     globalCount = 0;
   }
 
-  SECTION("sectionTwo") {
+  SECTION("ContinousProcess") {
     globalCount = 0;
-    ContinuousProcess1 cp1(0); // += 0.933
-    ContinuousProcess2 cp2(1); // += 0.111
-    Process2 m2(2);            //  /= 1.1
-    Process3 m3(3);            //  *= 1.01
+    ContinuousProcess1 cp1(0, 1_m);   // += 0.933
+    ContinuousProcess2 cp2(1, 1.1_m); // += 0.111
+    Process2 m2(2);                   //  /= 1.1
+    Process3 m3(3);                   //  *= 1.01
 
     auto sequence2 = make_sequence(cp1, m2, m3, cp2);
 
+    std::cout << boost::typeindex::type_id<decltype(sequence2)>().pretty_name()
+              << std::endl;
+
     DummyData particle;
     DummyTrajectory track;
 
-    cout << "-->init sequence2" << endl;
+    cp1.resetFlag();
+    cp2.resetFlag();
+
+    ContinuousProcessStepLength const step1 = sequence2.getMaxStepLength(particle, track);
+    CHECK(LengthType(step1) == 1_m);
+    sequence2.doContinuous(particle, track, step1);
+    CHECK(cp1.getFlag());
+    CHECK_FALSE(cp2.getFlag());
+    CORSIKA_LOG_INFO("step1, l={}, i={}", LengthType(step1),
+                     ContinuousProcessIndex(step1).getIndex());
+
+    cp1.resetFlag();
+    cp2.resetFlag();
+
+    cp1.setStep(10_m);
+    ContinuousProcessStepLength const step2 = sequence2.getMaxStepLength(particle, track);
+    CHECK(LengthType(step2) == 1.1_m);
+    CHECK(ContinuousProcessIndex(step1) != ContinuousProcessIndex(step2));
+    sequence2.doContinuous(particle, track, step2);
+    CHECK_FALSE(cp1.getFlag());
+    CHECK(cp2.getFlag());
+    CORSIKA_LOG_INFO("step2, l={}, i={}", LengthType(step2),
+                     ContinuousProcessIndex(step2).getIndex());
+
+    CORSIKA_LOG_DEBUG("-->init sequence2");
     globalCount = 0;
-    cout << "-->docont" << endl;
+    CORSIKA_LOG_DEBUG("-->docont");
 
     // validation data
     double test_data[nData] = {0};
 
-    const int nLoop = 5;
-    cout << "Running loop with n=" << nLoop << endl;
+    // reset
+    particle = DummyData();
+    track = DummyTrajectory();
+
+    int const nLoop = 5;
+    CORSIKA_LOG_DEBUG("Running loop with n={}", nLoop);
     for (int iLoop = 0; iLoop < nLoop; ++iLoop) {
       for (int i = 0; i < nData; ++i) { test_data[i] += 0.933 + 0.111; }
-      sequence2.doContinuous(particle, track);
+      sequence2.doContinuous(particle, track, ContinuousProcessIndex(1));
     }
     for (int i = 0; i < nData; i++) {
-      cout << "data_[" << i << "]=" << particle.data_[i] << endl;
+      CORSIKA_LOG_DEBUG("data_[{}]={}", i, particle.data_[i]);
       CHECK(particle.data_[i] == Approx(test_data[i]).margin(1e-9));
     }
-    cout << "done" << endl;
+    CORSIKA_LOG_DEBUG("done");
   }
 
   SECTION("StackProcess") {
@@ -355,14 +481,14 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
 
     DummyStack stack;
 
-    const int nLoop = 20;
+    int const nLoop = 20;
     for (int i = 0; i < nLoop; ++i) { sequence1.doStack(stack); }
 
     CHECK(s1.getCount() == 20);
     CHECK(s2.getCount() == 10);
 
-    ContinuousProcess2 cp2(1); // += 0.111
-    Process2 m2(2);            //  /= 1.1
+    ContinuousProcess2 cp2(1, 2_m); // += 0.111
+    Process2 m2(2);                 //  /= 1.1
     auto sequence2 = make_sequence(cp2, m2);
     auto sequence3 = make_sequence(cp2, m2, s1);
 
@@ -373,39 +499,64 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
   }
 }
 
-TEST_CASE("Switch Process Sequence", "[Process Sequence]") {
+TEST_CASE("SwitchProcessSequence", "ProcessSequence") {
 
   logging::set_level(logging::level::info);
-  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  corsika_logger->set_pattern("[%n:%^%-8l%$]: %v");
+
+  /**
+   * In this example switching is done only by "data_[0]>0", where
+   * data in an arrray of doubles, DummyData.
+   */
+
+  struct SwitchSelect {
+    SwitchResult operator()(DummyData const& p) const {
+      if (p.data_[0] > 0) return SwitchResult::First;
+      return SwitchResult::Second;
+    }
+  };
+  SwitchSelect select1;
+
+  auto cp1 = ContinuousProcess1(0, 1_m);
+  auto cp2 = ContinuousProcess2(0, 2_m);
+  auto cp3 = ContinuousProcess3(0, 3_m);
+
+  auto sequence1 = make_sequence(Process1(0), cp2, Decay1(0));
+  auto sequence2 = make_sequence(cp3, Process2(0), Decay2(0));
+
+  auto sequence3 = make_sequence(cp1, Process3(0),
+                                 SwitchProcessSequence(sequence1, sequence2, select1));
 
   SECTION("Check construction") {
 
-    struct TestSelect {
-      SwitchResult operator()(const DummyData& p) const {
-        std::cout << "TestSelect data=" << p.data_[0] << std::endl;
-        if (p.data_[0] > 0) return SwitchResult::First;
-        return SwitchResult::Second;
-      }
-    };
-    TestSelect select1;
+    auto sequence_alt =
+        make_sequence(cp1, Process3(0),
+                      make_select(make_sequence(Process1(0), cp2, Decay1(0)),
+                                  make_sequence(cp3, Process2(0), Decay2(0)), select1));
 
-    auto sequence1 = make_sequence(Process1(0), ContinuousProcess2(0), Decay1(0));
-    auto sequence2 = make_sequence(ContinuousProcess3(0), Process2(0), Decay2(0));
+    auto switch_seq = SwitchProcessSequence(sequence1, sequence2, select1);
+    CHECK(is_process_sequence_v<decltype(switch_seq)>);
+    CHECK(is_switch_process_sequence_v<decltype(switch_seq)>);
+    // CHECK(is_switch_process_sequence_v<decltype(&switch_seq)>);
+    CHECK(is_switch_process_sequence_v<decltype(
+              SwitchProcessSequence(sequence1, sequence2, select1))>);
 
-    auto sequence3 = make_sequence(ContinuousProcess1(0), Process3(0),
-                                   SwitchProcessSequence(sequence1, sequence2, select1));
+    CHECK(is_process_sequence_v<decltype(sequence3)>);
+    CHECK_FALSE(is_switch_process_sequence_v<decltype(sequence3)>);
 
-    auto sequence_alt = make_sequence(
-        ContinuousProcess1(0), Process3(0),
-        make_select(make_sequence(Process1(0), ContinuousProcess2(0), Decay1(0)),
-                    make_sequence(ContinuousProcess3(0), Process2(0), Decay2(0)),
-                    select1));
+    CHECK(is_process_sequence_v<decltype(
+              SwitchProcessSequence(sequence1, sequence2, select1))>);
+    CHECK(is_switch_process_sequence_v<decltype(
+              SwitchProcessSequence(sequence1, sequence2, select1))>);
 
     // check that same process sequence can be build in different ways
     CHECK(typeid(sequence3) == typeid(sequence_alt));
-    CHECK(is_process_sequence_v<decltype(sequence3)> == true);
+    CHECK(is_process_sequence_v<decltype(sequence3)>);
     CHECK(is_process_sequence_v<decltype(
-              SwitchProcessSequence(sequence1, sequence2, select1))> == true);
+              SwitchProcessSequence(sequence1, sequence2, select1))>);
+  }
+
+  SECTION("Check interfaces") {
 
     DummyData particle;
     DummyTrajectory track;
@@ -416,7 +567,7 @@ TEST_CASE("Switch Process Sequence", "[Process Sequence]") {
     checkSec = 0;
     checkCont = 0;
     particle.data_[0] = 100; // data positive
-    sequence3.doContinuous(particle, track);
+    sequence3.doContinuous(particle, track, ContinuousProcessIndex(1));
     CHECK(checkInteract == 0);
     CHECK(checkDecay == 0);
     CHECK(checkCont == 0b011);
@@ -427,7 +578,7 @@ TEST_CASE("Switch Process Sequence", "[Process Sequence]") {
     checkSec = 0;
     checkCont = 0;
     particle.data_[0] = -100; // data negative
-    sequence_alt.doContinuous(particle, track);
+    sequence3.doContinuous(particle, track, ContinuousProcessIndex(1));
     CHECK(checkInteract == 0);
     CHECK(checkDecay == 0);
     CHECK(checkCont == 0b101);
@@ -478,4 +629,168 @@ TEST_CASE("Switch Process Sequence", "[Process Sequence]") {
     CHECK(checkCont == 0);
     CHECK(checkSec == 0);
   }
+
+  SECTION("Check ContinuousProcesses in SwitchProcessSequence") {
+
+    DummyData particle;
+    DummyTrajectory track;
+
+    particle.data_[0] =
+        100; // data positive, selects particular branch on SwitchProcessSequence
+
+    cp1.setStep(10_m);
+    cp2.setStep(15_m);
+    cp3.setStep(100_m);
+
+    cp1.resetFlag();
+    cp2.resetFlag();
+    cp3.resetFlag();
+
+    ContinuousProcessStepLength const step1 = sequence3.getMaxStepLength(particle, track);
+    CHECK(LengthType(step1) == 10_m);
+    sequence3.doContinuous(particle, track, step1);
+    CHECK(cp1.getFlag());
+    CHECK_FALSE(cp2.getFlag());
+    CHECK_FALSE(cp3.getFlag());
+    CORSIKA_LOG_INFO("step1, l={}, i={}", LengthType(step1),
+                     ContinuousProcessIndex(step1).getIndex());
+
+    particle.data_[0] =
+        100; // data positive, selects particular branch on SwitchProcessSequence
+
+    cp1.setStep(50_m);
+    cp2.setStep(15_m);
+    cp3.setStep(100_m);
+
+    cp1.resetFlag();
+    cp2.resetFlag();
+    cp3.resetFlag();
+
+    ContinuousProcessStepLength const step2 = sequence3.getMaxStepLength(particle, track);
+    CHECK(LengthType(step2) == 15_m);
+    sequence3.doContinuous(particle, track, step2);
+    CHECK_FALSE(cp1.getFlag());
+    CHECK(cp2.getFlag());
+    CHECK_FALSE(cp3.getFlag());
+    CORSIKA_LOG_INFO("step2, len_cont={}, indexLimit={} type={}", LengthType(step2),
+                     ContinuousProcessIndex(step2).getIndex(),
+                     boost::typeindex::type_id<decltype(sequence3)>().pretty_name());
+
+    particle.data_[0] =
+        -100; // data positive, selects particular branch on SwitchProcessSequence
+
+    cp1.setStep(11_m);
+    cp2.setStep(15_m);
+    cp3.setStep(100_m);
+
+    cp1.resetFlag();
+    cp2.resetFlag();
+    cp3.resetFlag();
+
+    ContinuousProcessStepLength const step3 = sequence3.getMaxStepLength(particle, track);
+    CHECK(LengthType(step3) == 11_m);
+    sequence3.doContinuous(particle, track, step3);
+    CHECK(cp1.getFlag());
+    CHECK_FALSE(cp2.getFlag());
+    CHECK_FALSE(cp3.getFlag());
+    CORSIKA_LOG_INFO("step3, len_cont={}, indexLimit={} type={}", LengthType(step3),
+                     ContinuousProcessIndex(step3).getIndex(),
+                     boost::typeindex::type_id<decltype(sequence3)>().pretty_name());
+
+    particle.data_[0] =
+        -100; // data positive, selects particular branch on SwitchProcessSequence
+
+    cp1.setStep(11_m);
+    cp2.setStep(15_m);
+    cp3.setStep(2_m);
+
+    cp1.resetFlag();
+    cp2.resetFlag();
+    cp3.resetFlag();
+
+    ContinuousProcessStepLength const step4 = sequence3.getMaxStepLength(particle, track);
+    CHECK(LengthType(step4) == 2_m);
+    sequence3.doContinuous(particle, track, step4);
+    CHECK_FALSE(cp1.getFlag());
+    CHECK_FALSE(cp2.getFlag());
+    CHECK(cp3.getFlag());
+    CORSIKA_LOG_INFO("step4, len_cont={}, indexLimit={} type={}", LengthType(step4),
+                     ContinuousProcessIndex(step4).getIndex(),
+                     boost::typeindex::type_id<decltype(sequence3)>().pretty_name());
+  }
+}
+
+TEST_CASE("ProcessSequence Indexing", "ProcessSequence") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$]: %v");
+
+  SECTION("Indexing") {
+
+    int const n0 = count_continuous<Decay2>::count;
+    int const n1 = count_continuous<ContinuousProcess3>::count;
+    int const n2 = count_continuous<ContinuousProcess2,
+                                    count_continuous<ContinuousProcess3>::count>::count;
+    int const n1_b =
+        count_continuous<Process2, count_continuous<ContinuousProcess3>::count>::count;
+    int const n1_c =
+        count_continuous<ContinuousProcess3, count_continuous<Process2>::count>::count;
+    int const n12 =
+        count_continuous<ContinuousProcess2,
+                         count_continuous<ContinuousProcess3, 10>::count>::count;
+    int const n11_b =
+        count_continuous<Process1,
+                         count_continuous<ContinuousProcess3, 10>::count>::count;
+    int const n11_c = count_continuous<ContinuousProcess3,
+                                       count_continuous<Process1, 10>::count>::count;
+
+    CHECK(n0 == 0);
+    CHECK(n1 == 1);
+    CHECK(n1_b == 1);
+    CHECK(n1_c == 1);
+    CHECK(n2 == 2);
+    CHECK(n11_b == 11);
+    CHECK(n11_c == 11);
+    CHECK(n12 == 12);
+
+    std::cout << count_continuous<ContinuousProcess3>::count << std::endl;
+    std::cout << count_continuous<Process3>::count << std::endl;
+
+    struct SwitchSelect {
+      SwitchResult operator()(DummyData const& p) const {
+        std::cout << "SwitchSelect data=" << p.data_[0] << std::endl;
+        if (p.data_[0] > 0) return SwitchResult::First;
+        return SwitchResult::Second;
+      }
+    };
+
+    auto sequence1 = make_sequence(Process1(0), ContinuousProcess2(0, 2_m), Decay1(0));
+    auto sequence2 = make_sequence(ContinuousProcess3(0, 3_m), Process2(0), Decay2(0),
+                                   ContinuousProcess1(0, 1_m));
+
+    SwitchSelect select1;
+    auto switch_seq = SwitchProcessSequence(sequence1, sequence2, select1);
+
+    auto sequence3 = make_sequence(ContinuousProcess1(0, 1_m), Process3(0), switch_seq);
+    auto sequence4 = make_sequence(ContinuousProcess1(0, 1_m), Process3(0),
+                                   SwitchProcessSequence(sequence1, sequence2, select1));
+
+    int const switch_seq_n = count_continuous<decltype(switch_seq)>::count;
+    int const sequence3_n = count_continuous<decltype(sequence3)>::count;
+
+    CHECK(decltype(sequence1)::getNumberOfProcesses() == 3);
+    CHECK(count_continuous<decltype(sequence1)>::count == 3);
+    CHECK(count_continuous<decltype(sequence2)>::count == 4);
+    CHECK(switch_seq_n == 7);
+    CHECK(sequence3_n == 9);
+    CHECK(count_continuous<decltype(sequence4)>::count == 9);
+
+    std::cout << "switch_seq "
+              << boost::typeindex::type_id<decltype(switch_seq)>().pretty_name()
+              << std::endl;
+
+    std::cout << "sequence3 "
+              << boost::typeindex::type_id<decltype(sequence3)>().pretty_name()
+              << std::endl;
+  }
 }
diff --git a/tests/media/testEnvironment.cpp b/tests/media/testEnvironment.cpp
index d9e42cf0cdcb9b6cf22c6e6b4a7611ffa3c2f8b6..ca16d9f589d0e84932e004b4df440335be90fd9f 100644
--- a/tests/media/testEnvironment.cpp
+++ b/tests/media/testEnvironment.cpp
@@ -301,3 +301,105 @@ TEST_CASE("LayeredSphericalAtmosphereBuilder w/ magnetic field") {
                                      .getMagneticField(pTest2)
                                      .getComponents(gCS));
 }
+
+TEST_CASE("media", "LayeredSphericalAtmosphereBuilder USStd") {
+  // setup environment, geometry
+  Point const center{gCS, 0_m, 0_m, 0_m};
+
+  // setup our interface types
+  auto builder = make_layered_spherical_atmosphere_builder<>::create(
+      center, constants::EarthRadius::Mean);
+
+  builder.setNuclearComposition(
+      {{Code::Nitrogen, Code::Oxygen},
+       {0.7847f, 1.f - 0.7847f}}); // values taken from AIRES manual, Ar removed for now
+
+  builder.addExponentialLayer(1222.6562_g / (1_cm * 1_cm), 994186.38_cm, 4_km);
+  builder.addExponentialLayer(1144.9069_g / (1_cm * 1_cm), 878153.55_cm, 10_km);
+  builder.addExponentialLayer(1305.5948_g / (1_cm * 1_cm), 636143.04_cm, 40_km);
+  builder.addExponentialLayer(540.1778_g / (1_cm * 1_cm), 772170.16_cm, 100_km);
+  builder.addLinearLayer(1e9_cm, 112.8_km);
+
+  Environment<IMediumModel> env;
+  builder.assemble(env);
+
+  typedef typename Environment<IMediumModel>::BaseNodeType::VTN_type node_type;
+  node_type const* universe = env.getUniverse().get();
+
+  // far out ther is the universe
+  CHECK(universe->getContainingNode(Point(gCS, {10000_km, 0_m, 0_m})) == universe);
+  CHECK(universe->getContainingNode(Point(gCS, {0_m, 10000_km, 0_m})) == universe);
+
+  // at 112.8km there is transition to atmosphere
+  CHECK(universe->getContainingNode(
+            Point(gCS, {constants::EarthRadius::Mean + 112.8_km + 1_cm, 0_m, 0_m})) ==
+        universe);
+  CHECK(universe->getContainingNode(
+            Point(gCS, {0_m, constants::EarthRadius::Mean + 112.8_km + 1_cm, 0_m})) ==
+        universe);
+
+  // check layer transition at 112.8km
+
+  node_type const* layer1_not_yet = universe->getContainingNode(
+      Point(gCS, {constants::EarthRadius::Mean + 112.8_km + 1_cm, 0_m, 0_m}));
+  node_type const* layer1 = universe->getContainingNode(
+      Point(gCS, {constants::EarthRadius::Mean + 112.8_km - 1_cm, 0_m, 0_m}));
+  node_type const* layer1_also = universe->getContainingNode(
+      Point(gCS, {0_m, constants::EarthRadius::Mean + 112.8_km - 1_cm, 0_m}));
+
+  CHECK(layer1_not_yet == universe);
+  CHECK(layer1 != universe);
+  CHECK(layer1 == layer1_also);
+
+  // check layer transition at 100km
+
+  node_type const* layer2_not_yet = universe->getContainingNode(
+      Point(gCS, {constants::EarthRadius::Mean + 100_km + 1_cm, 0_m, 0_m}));
+  node_type const* layer2 = universe->getContainingNode(
+      Point(gCS, {constants::EarthRadius::Mean + 100_km - 1_cm, 0_m, 0_m}));
+  node_type const* layer2_also = universe->getContainingNode(
+      Point(gCS, {0_m, constants::EarthRadius::Mean + 100_km - 1_cm, 0_m}));
+
+  CHECK(layer2_not_yet == layer1);
+  CHECK(layer2 != layer1);
+  CHECK(layer2 == layer2_also);
+
+  // check layer transition at 40km
+
+  node_type const* layer3_not_yet = universe->getContainingNode(
+      Point(gCS, {constants::EarthRadius::Mean + 40_km + 1_cm, 0_m, 0_m}));
+  node_type const* layer3 = universe->getContainingNode(
+      Point(gCS, {constants::EarthRadius::Mean + 40_km - 1_cm, 0_m, 0_m}));
+  node_type const* layer3_also = universe->getContainingNode(
+      Point(gCS, {0_m, constants::EarthRadius::Mean + 40_km - 1_cm, 0_m}));
+
+  CHECK(layer3_not_yet == layer2);
+  CHECK(layer3 != layer2);
+  CHECK(layer3 == layer3_also);
+
+  // check layer transition at 10km
+
+  node_type const* layer4_not_yet = universe->getContainingNode(
+      Point(gCS, {constants::EarthRadius::Mean + 10_km + 1_cm, 0_m, 0_m}));
+  node_type const* layer4 = universe->getContainingNode(
+      Point(gCS, {constants::EarthRadius::Mean + 10_km - 1_cm, 0_m, 0_m}));
+  node_type const* layer4_also = universe->getContainingNode(
+      Point(gCS, {0_m, constants::EarthRadius::Mean + 10_km - 1_cm, 0_m}));
+
+  CHECK(layer4_not_yet == layer3);
+  CHECK(layer4 != layer3);
+  CHECK(layer4 == layer4_also);
+
+  // check layer transition at 4km
+
+  node_type const* layer5_not_yet = universe->getContainingNode(
+      Point(gCS, {constants::EarthRadius::Mean + 4_km + 1_cm, 0_m, 0_m}));
+  node_type const* layer5 = universe->getContainingNode(
+      Point(gCS, {constants::EarthRadius::Mean + 4_km - 1_cm, 0_m, 0_m}));
+  node_type const* layer5_also = universe->getContainingNode(
+      Point(gCS, {0_m, constants::EarthRadius::Mean + 4_km - 1_cm, 0_m}));
+
+  CHECK(layer5_not_yet == layer4);
+  CHECK(layer5 != layer4);
+  CHECK(layer5 == layer5_also);
+}
diff --git a/tests/media/testMedium.cpp b/tests/media/testMedium.cpp
index 670c25d9246e3f1ffacef09cde5275f85500242d..edcba45fc6fe01a3b5554878fcca735627b7a9d7 100644
--- a/tests/media/testMedium.cpp
+++ b/tests/media/testMedium.cpp
@@ -101,7 +101,8 @@ TEST_CASE("MediumPropertyModel w/ Homogeneous") {
   auto const tEnd = 1_s;
 
   // and the associated trajectory
-  setup::Trajectory const trajectory(line, tEnd);
+  setup::Trajectory const trajectory =
+      corsika::setup::testing::make_track<setup::Trajectory>(line, tEnd);
 
   // and check the integrated grammage
   CHECK((medium.getIntegratedGrammage(trajectory, 3_m) / (density * 3_m)) == Approx(1));
diff --git a/tests/modules/testCONEX.cpp b/tests/modules/testCONEX.cpp
index 621ae626a95b5fe6b1e73953b5d0d94c5a71c4ba..34718d69bb94e93c7e032e42df8f7e8e9c541f6e 100644
--- a/tests/modules/testCONEX.cpp
+++ b/tests/modules/testCONEX.cpp
@@ -97,13 +97,15 @@ TEST_CASE("CONEXSourceCut") {
 
   auto const emPosition = showerCore + showerAxis.getDirection() * (-20_km);
 
-  std::cout << "position injection: "
-            << injectionPos.getCoordinates(conex.getObserverCS()) << " "
-            << injectionPos.getCoordinates(rootCS) << std::endl;
-  std::cout << "position core: " << showerCore.getCoordinates(conex.getObserverCS())
-            << " " << showerCore.getCoordinates(rootCS) << std::endl;
-  std::cout << "position EM: " << emPosition.getCoordinates(conex.getObserverCS()) << " "
-            << emPosition.getCoordinates(rootCS) << std::endl;
+  CORSIKA_LOG_DEBUG("position injection: {} {}",
+                    injectionPos.getCoordinates(conex.getObserverCS()),
+                    injectionPos.getCoordinates(rootCS));
+  CORSIKA_LOG_DEBUG("position core: {} {}",
+                    showerCore.getCoordinates(conex.getObserverCS()),
+                    showerCore.getCoordinates(rootCS));
+  CORSIKA_LOG_DEBUG("position EM: {} {}",
+                    emPosition.getCoordinates(conex.getObserverCS()),
+                    emPosition.getCoordinates(rootCS));
 
   conex.addParticle(Code::Proton, Eem, 0_eV, emPosition, momentum.normalized(), 0_s);
   // supperimpose a photon
diff --git a/tests/modules/testObservationPlane.cpp b/tests/modules/testObservationPlane.cpp
index 9b9be4656d447b76552d63ae8e6b83657effa3d2..90a12288bd0b24c4c3021adaf66630fd1265d688 100644
--- a/tests/modules/testObservationPlane.cpp
+++ b/tests/modules/testObservationPlane.cpp
@@ -23,10 +23,10 @@
 
 using namespace corsika;
 
-TEST_CASE("ContinuousProcess interface", "[proccesses][observation_plane]") {
+TEST_CASE("ObservationPlane", "[proccesses][observation_plane]") {
 
-  logging::set_level(logging::level::info);
-  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  logging::set_level(logging::level::trace);
+  corsika_logger->set_pattern("[%n:%^%-8l%$]: %v");
 
   auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Oxygen);
   auto const& cs = *csPtr;
@@ -34,48 +34,64 @@ TEST_CASE("ContinuousProcess interface", "[proccesses][observation_plane]") {
   [[maybe_unused]] auto const& node_dummy = nodePtr;
 
   /*
-    Test with downward going 1_GeV neutrino, starting at 0,1_m,10m
+    Test with 1_GeV neutrino, starting at 0,0,0 travelling into +x direction (see
+    setup_stack).
 
-    ObservationPlane has origin at 0,0,0
+    ObservationPlane has origin at 10,0,0 and a normal in x-direction
    */
   auto [stack, viewPtr] =
       setup::testing::setup_stack(Code::NuE, 0, 0, 1_GeV, nodePtr, cs);
   [[maybe_unused]] setup::StackView& view = *viewPtr;
   auto particle = stack->getNextParticle();
 
+  // dummy track. Not used for calculation!
   Point const start(cs, {0_m, 1_m, 10_m});
   VelocityVector vec(cs, 0_m / second, 0_m / second, -constants::c);
   Line line(start, vec);
-
-  setup::Trajectory track =
+  setup::Trajectory no_used_track =
       setup::testing::make_track<setup::Trajectory>(line, 12_m / constants::c);
 
-  particle.setPosition(Point(cs, {1_m, 1_m, 10_m})); // moving already along -z
-
   SECTION("horizontal plane") {
 
-    Plane const obsPlane(Point(cs, {0_m, 0_m, 0_m}), DirectionVector(cs, {0., 0., 1.}));
-    ObservationPlane obs(obsPlane, DirectionVector(cs, {1., 0., 0.}), "particles.dat",
+    Plane const obsPlane(Point(cs, {10_m, 0_m, 0_m}), DirectionVector(cs, {1., 0., 0.}));
+    ObservationPlane obs(obsPlane, DirectionVector(cs, {0., 1., 0.}), "particles.dat",
                          true);
 
-    LengthType const length = obs.getMaxStepLength(particle, track);
-    ProcessReturn const ret = obs.doContinuous(particle, track);
+    LengthType const length = obs.getMaxStepLength(particle, no_used_track);
+    ProcessReturn const ret = obs.doContinuous(particle, no_used_track, true);
 
     CHECK(length / 10_m == Approx(1).margin(1e-4));
     CHECK(ret == ProcessReturn::ParticleAbsorbed);
   }
 
-  SECTION("inclined plane") {}
-
   SECTION("transparent plane") {
-    Plane const obsPlane(Point(cs, {0_m, 0_m, 0_m}), DirectionVector(cs, {0., 0., 1.}));
-    ObservationPlane obs(obsPlane, DirectionVector(cs, {1., 0., 0.}), "particles.dat",
+    Plane const obsPlane(Point(cs, {1_m, 0_m, 0_m}), DirectionVector(cs, {1., 0., 0.}));
+    ObservationPlane obs(obsPlane, DirectionVector(cs, {0., 0., 1.}), "particles.dat",
                          false);
 
-    LengthType const length = obs.getMaxStepLength(particle, track);
-    ProcessReturn const ret = obs.doContinuous(particle, track);
+    LengthType const length = obs.getMaxStepLength(particle, no_used_track);
+    ProcessReturn const ret = obs.doContinuous(particle, no_used_track, true);
 
-    CHECK(length / 10_m == Approx(1).margin(1e-4));
+    CHECK(length / 1_m == Approx(1).margin(1e-4));
     CHECK(ret == ProcessReturn::Ok);
   }
+
+  SECTION("inclined plane, inclined particle") {
+
+    MomentumVector const pnew = MomentumVector(cs, {1_GeV, 0.5_GeV, -0.4_GeV});
+    HEPEnergyType const enew = sqrt(pnew.dot(pnew));
+    particle.setMomentum(pnew);
+    particle.setEnergy(enew);
+
+    Plane const obsPlane(Point(cs, {10_m, 5_m, 5_m}),
+                         DirectionVector(cs, {1, 0.1, -0.05}));
+    ObservationPlane obs(obsPlane, DirectionVector(cs, {0., 1., 0.}), "particles.dat",
+                         true);
+
+    LengthType const length = obs.getMaxStepLength(particle, no_used_track);
+    ProcessReturn const ret = obs.doContinuous(particle, no_used_track, true);
+
+    CHECK(length / 10_m == Approx(1.1375).margin(1e-4));
+    CHECK(ret == ProcessReturn::ParticleAbsorbed);
+  }
 }
diff --git a/tests/modules/testParticleCut.cpp b/tests/modules/testParticleCut.cpp
index 0dede160e563c6abab6a9a3fb2b1f0318f3dc1c7..1bc67084bc633c62539ec8a644eb42528a8e4106 100644
--- a/tests/modules/testParticleCut.cpp
+++ b/tests/modules/testParticleCut.cpp
@@ -14,6 +14,7 @@
 #include <corsika/framework/geometry/Vector.hpp>
 #include <corsika/framework/utility/CorsikaFenv.hpp>
 #include <corsika/media/Environment.hpp>
+#include <corsika/framework/process/ContinuousProcessIndex.hpp>
 
 #include <SetupTestStack.hpp>
 #include <SetupTestTrajectory.hpp>
@@ -22,7 +23,7 @@
 
 using namespace corsika;
 
-TEST_CASE("ParticleCut", "[processes]") {
+TEST_CASE("ParticleCut", "processes") {
 
   logging::set_level(logging::level::info);
   corsika_logger->set_pattern("[%n:%^%-8l%$] %v");
@@ -221,7 +222,10 @@ TEST_CASE("ParticleCut", "[processes]") {
     for (auto proType : particleList) {
       auto particle = stack.addParticle(std::make_tuple(
           proType, Eabove, MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), point0, 0_ns));
-      cut.doContinuous(particle, track);
+
+      if (cut.doContinuous(particle, track) == ProcessReturn::ParticleAbsorbed) {
+        particle.erase();
+      }
     }
 
     CHECK(stack.getEntries() == 9);
diff --git a/tests/modules/testPythia8.cpp b/tests/modules/testPythia8.cpp
index 4f93280f513ffdf243259d99dc4ea675bf60cb83..8e178affc3f7c26c5a34df8caf2777386dba8d24 100644
--- a/tests/modules/testPythia8.cpp
+++ b/tests/modules/testPythia8.cpp
@@ -27,8 +27,6 @@ TEST_CASE("Pythia", "[processes]") {
 
   SECTION("linking pythia") {
     using namespace Pythia8;
-    using std::cout;
-    using std::endl;
 
     // Generator. Process selection. LHC initialization. Histogram.
     Pythia pythia;
@@ -50,15 +48,15 @@ TEST_CASE("Pythia", "[processes]") {
     event.append(321, 1, 0, 0, 0., 0., 100., sqrt(pz * pz + m * m), m);
 
     if (!pythia.next())
-      cout << "decay failed!" << endl;
+      CORSIKA_LOG_CRITICAL("decay failed!");
     else
-      cout << "particles after decay: " << event.size() << endl;
+      CORSIKA_LOG_DEBUG("particles after decay: {}", event.size());
     event.list();
 
     // loop over final state
     for (int i = 0; i < pythia.event.size(); ++i)
       if (pythia.event[i].isFinal()) {
-        cout << "particle: id=" << pythia.event[i].id() << endl;
+        CORSIKA_LOG_DEBUG("particle: id= {}", pythia.event[i].id());
       }
   }
 
@@ -94,7 +92,7 @@ auto sumMomentum(TStackView const& view, CoordinateSystemPtr const& vCS) {
   return sum;
 }
 
-TEST_CASE("pythia process") {
+TEST_CASE("PythiaInterface", "[processes]") {
 
   logging::set_level(logging::level::info);
   corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
@@ -157,9 +155,11 @@ TEST_CASE("pythia process") {
 
   SECTION("pythia interaction") {
 
-    // feenableexcept(FE_INVALID); \todo how does this work nowadays
+    //! feenableexcept(FE_INVALID); \todo how does this work nowadays
+
+    // this will be a p-p collision at sqrts=3.5TeV -> no problem for pythia
     auto [stackPtr, secViewPtr] = setup::testing::setup_stack(
-        Code::PiPlus, 0, 0, 100_GeV, (setup::Environment::BaseNodeType* const)nodePtr,
+        Code::Proton, 0, 0, 7_TeV, (setup::Environment::BaseNodeType* const)nodePtr,
         *csPtr);
     auto& view = *secViewPtr;
     auto particle = stackPtr->first();
@@ -167,6 +167,7 @@ TEST_CASE("pythia process") {
     corsika::pythia8::Interaction model;
     model.doInteraction(view);
     [[maybe_unused]] const GrammageType length = model.getInteractionLength(particle);
-    CHECK(length / 1_kg * square(1_m) == Approx(82.2524));
+    CHECK(length / 1_kg * square(1_m) == Approx(43.04).margin(5e-1));
+    CHECK(view.getSize() == 38);
   }
 }
diff --git a/tests/modules/testQGSJetII.cpp b/tests/modules/testQGSJetII.cpp
index 3ddbb254c133050e07b5ec257683767df8508be7..8b039f87ca2979ebdef9831b287c73ef8a2c0478 100644
--- a/tests/modules/testQGSJetII.cpp
+++ b/tests/modules/testQGSJetII.cpp
@@ -19,7 +19,16 @@
 #include <string>
 #include <cstdlib>
 #include <experimental/filesystem>
-#include <iostream>
+
+/*
+  NOTE, WARNING, ATTENTION
+
+  The sibyll/Random.hpp implements the hook of sibyll to the C8 random
+  number generator. It has to occur excatly ONCE per linked
+  executable. If you include the header below in multiple "tests" and
+  link them togehter, it will fail.
+ */
+#include <corsika/modules/qgsjetII/Random.hpp>
 
 using namespace corsika;
 
@@ -49,10 +58,11 @@ TEST_CASE("CORSIKA_DATA", "[processes]") {
     CHECK(data != 0);
     CHECK(std::experimental::filesystem::is_directory(
         std::experimental::filesystem::path(std::string(data) + "/QGSJetII")));
-    std::cout << "data: " << data << " isDir: "
-              << std::experimental::filesystem::is_directory(std::string(data) +
-                                                             "/QGSJetII")
-              << std::endl;
+    CORSIKA_LOG_INFO(
+        "data: {}"
+        " isDir: {}"
+        "/QGSJetII",
+        data, std::experimental::filesystem::is_directory(std::string(data)));
   }
 }
 
diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..02b0ff18968a2d183acb2ee96f32a683bc941dd4
--- /dev/null
+++ b/tools/CMakeLists.txt
@@ -0,0 +1,7 @@
+set (scripts
+  plot_crossings.sh
+  plot_tracks.sh
+  read_hist.py
+  )
+
+install (PROGRAMS ${scripts} DESTINATION share/corsika/tools)