diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml
index 9ebe0555b9b6f4745c25e3e1bf09a3c1723a05eb..3ae98cb46f40f4ae24a248e9778afa30d8632726 100644
--- a/.gitlab-ci.yml
+++ b/.gitlab-ci.yml
@@ -11,7 +11,7 @@ variables:
   LSAN_OPTIONS: "log_threads=1"
   ASAN_OPTIONS: "detect_leaks=0:detect_stack_use_after_return=1"
   # location of AirShowerPhysics/corsika-data
-  CORSIKA_DATA: "${CI_PROJECT_DIR}/Data" # the git submodule
+  CORSIKA_DATA: "${CI_PROJECT_DIR}/modules/data" # the git submodule
   # _alternatively_ corsika-data can be downloaded as submodule:
   GIT_SUBMODULE_STRATEGY: normal # none: we get the submodules in before_script,
                                  # normal: get submodules automatically
@@ -29,7 +29,6 @@ stages:
   - build
   - test
   - build_test
-  - python
   - example
   - build_test_example
   - install
@@ -68,8 +67,18 @@ check-clang-format:
     - if: $CI_COMMIT_BRANCH
   allow_failure: true
 
-
-
+### CodeQuality tool ####
+#include:
+#  - template: Code-Quality.gitlab-ci.yml
+#
+#code_quality:
+#  stage: quality
+#  rules:
+#    - if: '$CODE_QUALITY_DISABLED'
+#      when: never
+#    - if: '$CI_PIPELINE_SOURCE == "merge_request_event"' # Run code quality job in merge request pipelines
+#    - if: '$CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH'      # Run code quality job in pipelines on the master branch (but not in other branch pipelines)
+#    - if: '$CI_COMMIT_TAG'                               # Run code quality job in pipelines for tags
 
 ####### CONFIG ##############
 
@@ -80,19 +89,24 @@ check-clang-format:
   stage: config
   tags:
     - corsika
-  script:    
+  script:
     - mkdir -p build
     - cd build
-    - cmake .. -DCMAKE_BUILD_TYPE=Debug -DWITH_PYTHIA=ON
+    - cmake .. -DCMAKE_BUILD_TYPE=Debug -DUSE_PYTHIA8_C8=SYSTEM
   rules:
     - if: $CI_MERGE_REQUEST_ID
     - if: $CI_COMMIT_TAG
     - if: $CI_COMMIT_BRANCH
+  artifacts:
+    when: on_failure
+    expire_in: 3 days
+    paths:
+      - ${CI_PROJECT_DIR}/build/CMakeFiles/CMakeOutput.log
   cache:
     paths:
       - ${CI_PROJECT_DIR}/build/
     untracked: true
-    policy: pull-push
+    policy: pull-push    
 
 # config for gcc
 config-u-18_04:
@@ -272,8 +286,6 @@ build_test-clang-8:
 # normal pipeline for each commit
 .example:
   stage: example
-  before_script:
-    - apt-get -qq update && apt-get -qq install -y gdb
   tags:
     - corsika
   script:
@@ -328,8 +340,6 @@ example-clang-8:
   stage: build_test_example
   tags:
     - corsika
-  before_script:
-    - apt-get -qq update && apt-get -qq install -y gdb
   script:
     - cd build
     - cmake --build . -- -j4
@@ -438,11 +448,9 @@ install-clang-8:
   stage: optional
   tags:
     - corsika
-  before_script:
-    - apt-get -qq update && apt-get -qq install -y gdb
   script:
     - cd build
-    - cmake .. -DCMAKE_BUILD_TYPE=Release
+    - cmake .. -DCMAKE_BUILD_TYPE=Release -DUSE_PYTHIA8_C8=SYSTEM
     - cmake --build . -- -j4
     - set -o pipefail
     - ctest -j4 
@@ -505,7 +513,7 @@ coverage:
     - corsika
   script:
     - cd build
-    - cmake .. -DCMAKE_BUILD_TYPE=Coverage
+    - cmake .. -DCMAKE_BUILD_TYPE=Coverage -DUSE_PYTHIA8_C8=SYSTEM
     - cmake --build . -- -j4
     - ctest -j4 
     - cmake --build . --target coverage
@@ -535,41 +543,6 @@ coverage:
 
 
 
-##########################################################
-documentation:
-  image: corsika/devel:u-18.04
-  dependencies:
-    - config-u-18_04
-  stage: optional
-  tags:
-    - corsika
-  script:
-    - cd build
-    - cmake --build . --target doxygen -- -j4
-    - mkdir -p .public
-    - cp -r Documentation/Doxygen/html .public/
-    - mv .public ../public
-  rules:
-    - if: '$CI_MERGE_REQUEST_LABELS =~ /Ready for code review/' # run on merge requests, if label 'Ready for code review' is set
-    - if: $CI_COMMIT_BRANCH == $CI_DEFAULT_BRANCH
-      when: manual
-      allow_failure: true
-    - if: $CI_MERGE_REQUEST_ID
-      when: manual
-      allow_failure: true
-    - if: $CI_COMMIT_TAG
-      when: manual
-      allow_failure: true
-  artifacts:
-    expire_in: 3 weeks
-    paths:
-      - ${CI_PROJECT_DIR}/public
-  cache:
-    paths:
-      - ${CI_PROJECT_DIR}/build/
-    untracked: true
-    policy: pull
-    key: "${CI_COMMIT_REF_SLUG}-gcc"
 
 
 
@@ -604,36 +577,3 @@ sanity:
     key: "${CI_COMMIT_REF_SLUG}-gcc"
 
 
-
-##########################################################
-# template for all Python jobs
-.python:
-  stage: python
-  tags:
-    - corsika
-  script:
-    - cd ${CI_PROJECT_DIR}/Python  # change into the Python directory
-    - pip install --user -e '.[test]'  # install the package + test deps
-    - make all 2&>1 | tee python-test.log  # this runs all of the Python tests
-    - echo "finished" >> python-test.log # create even an empty file...
-    - cd ${CI_PROJECT_DIR}  # reset the directory    
-  artifacts:
-    when: always
-    expire_in: 1 year
-    paths:
-      - ${CI_PROJECT_DIR}/Python/python-test.log
-  allow_failure: true
-
-# we now configure the jobs for the three
-# supported Python versions
-python-3.6:
-  extends: .python
-  image: python:3.6
-
-python-3.7:
-  extends: .python
-  image: python:3.7
-
-python-3.8:
-  extends: .python
-  image: python:3.8
diff --git a/.gitmodules b/.gitmodules
index 185d42cffbd8fdb9c3afd31f6f8434ed02fab762..c465e047e874a33bbe7580aff5c15212080c19be 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,12 +1,15 @@
-[submodule "Data"]
-	path = Data
+[submodule "modules/data"]
+	path = modules/data
 	url = ../../AirShowerPhysics/corsika-data
 	branch = master
-[submodule "ThirdParty/spdlog"]
-	path = ThirdParty/spdlog
-	url = https://github.com/gabime/spdlog.git
-	shallow = true
-[submodule "Processes/Proposal/PROPOSAL"]
-	path = Processes/Proposal/PROPOSAL
+        shallow = true
+[submodule "modules/proposal"]
+	path = modules/proposal
 	url = https://github.com/tudo-astroparticlephysics/PROPOSAL.git
-	shallow = true
+	branch = restructure_parametrization
+        shallow = true
+[submodule "modules/conex"]
+	path = modules/conex
+	url = ../../AirShowerPhysics/cxroot
+	branch = master
+        shallow = true
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e24a55b9592c0917db26acdab4fbdaf89a27d469..541b597d633d0d27c66420299ad82c3f7604af3d 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -1,7 +1,8 @@
 cmake_minimum_required (VERSION 3.9)
-# we would need 3.16 to have CMP0097 for external subproject submodule (non) support
 
+#+++++++++++++++++++++++++++++
 # prevent in-source builds and give warning message
+#
 if ("${CMAKE_BINARY_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}") 
   message (FATAL_ERROR "In-source builds are disabled.
     Please create a build-dir and use `cmake <source-dir>` inside it.
@@ -9,57 +10,94 @@ if ("${CMAKE_BINARY_DIR}" STREQUAL "${CMAKE_SOURCE_DIR}")
           You must delete them, or cmake will refuse to work.")
 endif ()
 
+#+++++++++++++++++++++++++++++
+# project name
+#
 project (
   corsika
   VERSION 8.0.0
-  DESCRIPTION "CORSIKA C++ project"
+  DESCRIPTION "CORSIKA C++ project (alpha status)"
   LANGUAGES CXX
   )
 
-include (FeatureSummary)
-
+#+++++++++++++++++++++++++++++
 # as long as there still are modules using it:
+#
 enable_language (Fortran)
+set (CMAKE_Fortran_FLAGS "-std=legacy -Wfunction-elimination")
 
-# TEMPORARY: this should be removed, the sanitizers should be always enabled
-option (WITH_CORSIKA_SANITIZERS_ENABLED "temporary way to globally disable sanitizers until the currently failing tests are fixed" OFF)
-add_feature_info (CORSIKA_SANITIZERS_ENABLED WITH_CORSIKA_SANITIZERS_ENABLED "Switch to run c++ sanitzers on CORSIKA objects and code.")
-
-option (WITH_COAST "Flag to switch on/off COAST (reverse) interface" OFF)
-add_feature_info (COAST WITH_COAST "The COAST interface, so that you can write C8 processes to run inside C7.")
-
-# HISTORY option selection
-option (WITH_HISTORY "Flag to switch on/off HISTORY" ON)
-add_feature_info (HISTORY WITH_HISTORY "The Foo feature provides very cool stuffdddd.")
-
-# check for python
-set (Python_ADDITIONAL_VERSIONS 3)
-find_package (PythonInterp 3 REQUIRED)
+#+++++++++++++++++++++++++++++
+# warn user if system is not UNIX
+#
+if (NOT UNIX)
+  message (FATAL_ERROR "| CORSIKA8 > This is an unsupported system.")
+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})
+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
 set (CMAKE_INSTALL_MESSAGE LAZY)
 
-# directory for local cmake modules
-set (CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/CMakeModules)
-include (CorsikaUtilities) # a few cmake function
+#+++++++++++++++++++++++++++++
+# Setup hardware and infrastructure dependent defines
+#
+include(CorsikaDefines)
 
+#+++++++++++++++++++++++++++++
+# check if compiler is C++17 compliant
+#
+include (CheckCXXCompilerFlag)
+check_CXX_compiler_flag ("--std=c++17" COMPILER_SUPPORTS_CXX17)
+if (NOT COMPILER_SUPPORTS_CXX17)
+ message (FATAL "| CORSIKA8 > The compiler ${CMAKE_CXX_COMPILER} has no C++17 support. Please use a different C++ compiler.")
+endif ()
+# set CXX compile flags and options and warning settings
 set (CMAKE_CXX_STANDARD 17)
 set (CMAKE_CXX_EXTENSIONS OFF)
-enable_testing ()
-set (CTEST_OUTPUT_ON_FAILURE 1)
-list (APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure")
 
+#+++++++++++++++++++++++++++++
+# Compiler and linker flags, settings
+#
+# 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")
+set (DEFAULT_BUILD_TYPE "Release")
+
+# 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>
+  )
+
+#+++++++++++++++++++++++++++++
+# 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")
+
+#+++++++++++++++++++++++++++++
+# Build type selection
+#
 # Set the possible values of build type for cmake-gui and command line check
 set (ALLOWED_BUILD_TYPES Debug Release MinSizeRel RelWithDebInfo Coverage)
 set_property (CACHE CMAKE_BUILD_TYPE PROPERTY STRINGS ${ALLOWED_BUILD_TYPES})
-
 # Set a default build type if none was specified
 # by default: "Debug", if local ".git" directory is found, otherwise "Release"
 set (DEFAULT_BUILD_TYPE "Release")
 if (EXISTS "${CMAKE_SOURCE_DIR}/.git")
   set (DEFAULT_BUILD_TYPE "Debug")
 endif ()
-
 if (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
   message (STATUS "Setting build type to '${DEFAULT_BUILD_TYPE}' as no other was specified.")
   set (CMAKE_BUILD_TYPE "${DEFAULT_BUILD_TYPE}" CACHE
@@ -74,42 +112,33 @@ else (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
   message (STATUS "Build type is: ${CMAKE_BUILD_TYPE}")
 endif (NOT CMAKE_BUILD_TYPE AND NOT CMAKE_CONFIGURATION_TYPES)
 
-# 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), coverage -> O0
-set (CMAKE_CXX_FLAGS "-Wall -pedantic -Wextra -Wno-ignored-qualifiers")
-set (CMAKE_Fortran_FLAGS "-std=legacy -Wfunction-elimination")
-
-# 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")
-
-# 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>")
-
-# set a flag to inform code that we are in debug mode
-set (CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -DDEBUG")
-
-# COAST - interface, this requires CORSIKA7 to be installed first
-# COAST will allow you to program code in CORSIKA8 and execute it inside CORSIKA7
-if (WITH_COAST)
-  message (STATUS "Compiling CORSIKA8 for the use with COAST/corsika7.")
-  add_compile_options ("-fPIC")
-endif ()
+#+++++++++++++++++++++++++++++
+# Setup external dependencies.
+#
+include (conan) # self-provided in 'cmake' directory
+#
+# download and build all dependencies
+conan_cmake_run (CONANFILE conanfile.txt
+                 BASIC_SETUP CMAKE_TARGETS
+                 BUILD missing
+                 BUILD_TYPE "Release")
+#
+# add cnpy temporarily. As long as SaveBoostHistogram does not save to parquet directly
+#
+add_subdirectory (externals/cnpy)
 
+#+++++++++++++++++++++++++++++
+# Coverage
+#
 # targets and settings needed to generate coverage reports
 if (CMAKE_BUILD_TYPE STREQUAL Coverage)
   find_package (Perl REQUIRED)
 
-  # compile coverage under -O2 to remove unused functions
-  #  add_compile_options ("-O2")
   # compile coverage under -O0 to avoid any optimization, function elimation etc.
   add_compile_options ("-O0")
   
   set (GCOV gcov CACHE STRING "gcov executable" FORCE)
-  set (LCOV_BIN_DIR "${PROJECT_SOURCE_DIR}/ThirdParty/lcov/bin")
+  set (LCOV_BIN_DIR "${PROJECT_SOURCE_DIR}/externals/lcov/bin")
   # collect coverage data
   add_custom_command (
     OUTPUT raw-coverage.info
@@ -119,8 +148,8 @@ if (CMAKE_BUILD_TYPE STREQUAL Coverage)
   # remove uninteresting entries
   add_custom_command (
     OUTPUT coverage.info
-    COMMAND ${LCOV_BIN_DIR}/lcov -q --remove raw-coverage.info "*/usr/*" --output-file coverage2.info
-    COMMAND ${LCOV_BIN_DIR}/lcov --remove coverage2.info "*/ThirdParty/*" --output-file 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 ${CMAKE_COMMAND} -E remove coverage2.info
     DEPENDS raw-coverage.info
     )
@@ -133,39 +162,97 @@ if (CMAKE_BUILD_TYPE STREQUAL Coverage)
   add_custom_target (coverage DEPENDS coverage-report)
 endif ()
 
-# include this test only if NOT run on gitlab-ci, since there we have a dedicated job for it:
+#+++++++++++++++++++++++++++++
+# CTest config
+#
+enable_testing ()
+if (${CMAKE_VERSION} VERSION_LESS "3.12.0")  
+  list (APPEND CMAKE_CTEST_ARGUMENTS "--output-on-failure")
+else (${CMAKE_VERSION} VERSION_LESS "3.12.0")
+  set (CTEST_OUTPUT_ON_FAILURE 1) # this is for new versions of cmake
+endif (${CMAKE_VERSION} VERSION_LESS "3.12.0")
+set (CTEST_CUSTOM_COVERAGE_EXCLUDE "./tests/" "./examples/" "./modules/" "test*.(hpp/cpp)$")
+# 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})
 endif (NOT DEFINED ENV{CI})
-  
-if (DEFINED ENV{CORSIKA_DATA})
-  message ("Found  corsika-data in $ENV{CORSIKA_DATA}")
-  set (CORSIKA_DATA $ENV{CORSIKA_DATA})
-  add_subdirectory ($ENV{CORSIKA_DATA} corsika_data)
-else ()
-  message ("CORSIKA_DATA not defined: NEED to download AirShowerPhysics/corsika-data via git submodule")
-  message ("cmake will fail if you did not do this already.")
-  set (CORSIKA_DATA ${PROJECT_SOURCE_DIR}/Data)
-  add_subdirectory (Data)
-endif ()
 
-# include potential ThirdParty code provided with CORSIKA
-add_subdirectory (ThirdParty)
-
-# order of subdirectories
-add_subdirectory (Framework)
-add_subdirectory (Environment)
-add_subdirectory (Stack)
-add_subdirectory (Setup)
-add_subdirectory (Processes)
-add_subdirectory (Documentation)
-add_subdirectory (Main)
-add_subdirectory (Tools)
-if (WITH_COAST)
-  add_subdirectory (COAST)
-endif ()
+#+++++++++++++++++++++++++++++
+# 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)
 
 
+#+++++++++++++++++++++++++++++
+# CORSIKA8
+#
+add_library (CORSIKA8 INTERFACE)
+target_include_directories (
+  CORSIKA8
+  INTERFACE
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_BINARY_DIR}>
+  $<INSTALL_INTERFACE:${CMAKE_INSTALL_PREFIX}>
+  )
+# since CORSIKA8 is a header only library we must specify all link dependencies here:
+target_link_libraries (
+  CORSIKA8
+  INTERFACE
+  CONAN_PKG::eigen
+  CONAN_PKG::spdlog
+  CONAN_PKG::boost
+  cnpy # for SaveBoostHistogram
+  stdc++fs # experimental::filesystem
+  )
+# "src" is needed, since some headers (namely GeneratedParticleProperties.inc ec.) are produced by python script from e.g. ParticleData.xml
+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/sibyll)
+add_subdirectory (modules/qgsjetII)
+add_subdirectory (modules/urqmd)
+add_subdirectory (modules/conex)
+#
+# we really need a better proposal CMakeList.txt files:
+set (ADD_PYTHON OFF)
+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)
+
+#+++++++++++++++++++++++++++++++
+# unit testing
+# 
+add_subdirectory (tests)
+
+#+++++++++++++++++++++++++++++++
+# examples
+# 
+add_subdirectory (examples)
+
+#+++++++++++++++++++++++++++++++
+#
 # 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/CMakeModules/CorsikaUtilities.cmake b/CMakeModules/CorsikaUtilities.cmake
deleted file mode 100644
index 96d29eeb614391ae0f4fd05a4892e7d02eeeef2d..0000000000000000000000000000000000000000
--- a/CMakeModules/CorsikaUtilities.cmake
+++ /dev/null
@@ -1,226 +0,0 @@
-#
-# (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
-#
-# See file AUTHORS for a list of contributors.
-#
-# 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.
-#
-
-#################################################
-#
-# takes a list of input files and prepends a path
-#
-
-function (CORSIKA_PREPEND_PATH return prefix)
-  set (listVar "")
-  foreach (f ${ARGN})
-    list (APPEND listVar "${prefix}/${f}")
-  endforeach (f)
-  set (${return} "${listVar}" PARENT_SCOPE)
-endfunction (CORSIKA_PREPEND_PATH)
-
-
-#################################################
-#
-# use: CORSIKA_COPY_HEADERS_TO_NAMESPACE theLib theNamesapce header1.h header2.h ...
-#
-# creates a dependence of theLib on the presence of all listed header files in the
-# build-directory include/theNamespace directory
-#
-# if needed, create symbolic links from the source files to this build-directory location
-#
-# any path information from input filenames is stripped, IF path was specified it is used for the link destination,
-# if NOT the link is relative to the CMAKE_CURRENT_SOURCE_DIR
-# 
-function (CORSIKA_COPY_HEADERS_TO_NAMESPACE for_library in_namespace)
-  set (HEADERS_BUILD "")
-  foreach (HEADER ${ARGN})
-    # find eventual path, and handle it specificly
-    get_filename_component (barename ${HEADER} NAME)
-    get_filename_component (baredir ${HEADER} DIRECTORY)
-    # remove path, prepend build-directory destination
-    list (APPEND HEADERS_BUILD "${PROJECT_BINARY_DIR}/include/${in_namespace}/${barename}")
-    # define source location, use path if specified, otherwise CMAKE_CURRENT_SOURCE_DIR
-    set (FROM_DIR ${CMAKE_CURRENT_SOURCE_DIR})
-    if (NOT "${baredir}" STREQUAL "")
-      set (FROM_DIR ${baredir})
-    endif ()
-    # define command to perform the symbolic linking
-    add_custom_command (
-      OUTPUT ${PROJECT_BINARY_DIR}/include/${in_namespace}/${barename}
-      COMMAND ${CMAKE_COMMAND} -E make_directory ${PROJECT_BINARY_DIR}/include/${in_namespace}
-      COMMAND ${CMAKE_COMMAND} -E create_symlink ${FROM_DIR}/${barename} ${PROJECT_BINARY_DIR}/include/${in_namespace}/${barename}
-      COMMENT "Generate link: ${PROJECT_BINARY_DIR}/include/${in_namespace}/${barename}"
-      )
-  endforeach (HEADER)
-  
-  # main target for this build step, depends on header files in build area
-  add_custom_target (
-    ${for_library}_BUILD
-    DEPENDS ${HEADERS_BUILD}
-    )
-  
-  # connect the _BUILD target to the main (external) target
-  add_dependencies (${for_library} ${for_library}_BUILD)
-
-endfunction (CORSIKA_COPY_HEADERS_TO_NAMESPACE)
-
-
-
-
-#################################################
-#
-# use: CORSIKA_ADD_FILES_ABSOLUTE varname
-#
-# add list of filenames with absolute paths (pointing to CMAKE_SOURCE_DIR) to ${varname} in PARAENT_SCOPE
-# 
-
-macro (CORSIKA_ADD_FILES_ABSOLUTE varname)
-  file (RELATIVE_PATH _relPath "${PROJECT_SOURCE_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}")
-  foreach (_src ${ARGN})
-    if (_relPath)
-      list (APPEND "${varname}" "${CMAKE_SOURCE_DIR}/${_relPath}/${_src}")
-    else()
-      list (APPEND "${varname}" "${CMAKE_SOURCE_DIR}/${_src}")
-    endif()
-  endforeach()
-  if (_relPath)
-    # propagate SRCS to parent directory
-    set ("${varname}" "${${varname}}" PARENT_SCOPE)
-  endif()
-endmacro(CORSIKA_ADD_FILES_ABSOLUTE)
-
-
-
-#################################################
-#
-# central macro to register unit tests in cmake
-#
-# 1) Simple use:
-# Pass the name of the test.cc file as the first
-# argument, without the ".cc" extention.
-#
-# Example: CORSIKA_ADD_TEST (testSomething)
-#
-# This generates target testSomething from file testSomething.cc.
-#
-# 2) Customize sources:
-# If 1) doesn't work, use the SOURCES keyword to explicitly
-# specify the sources.
-#
-# Example: CORSIKA_ADD_TEST (testSomething
-#              SOURCES source1.cc source2.cc someheader.h)
-#
-# 3) Customize sanitizers:
-# You can override which sanitizers are compiled into the
-# test, but only do this if the defaults do not work.
-#
-# Example: CORSIKA_ADD_TEST (testSomething SANITIZE undefined)
-#
-# Only uses the sanitizer for undefined behavior.
-#
-# In all cases, you can further customize the target with
-# target_link_libraries(testSomething ...) and so on.
-#
-# TEMPORARY: All sanitizers are currently globally disabled by default, to enable them,
-# set CORSIKA_SANITIZERS_ENABLED to TRUE.
-function (CORSIKA_ADD_TEST)
-  cmake_parse_arguments (PARSE_ARGV 1 C8_ADD_TEST "" "SANITIZE" "SOURCES")
-  set (name ${ARGV0})
-
-  if (NOT C8_ADD_TEST_SOURCES)
-    set (sources ${name}.cc)
-  else ()
-    set (sources ${C8_ADD_TEST_SOURCES})
-  endif ()
-
-  if (NOT C8_ADD_TEST_SANITIZE)
-    set(sanitize "address,undefined")
-  else ()
-    set(sanitize ${C8_ADD_TEST_SANITIZE})
-  endif ()
-
-  add_executable (${name} ${sources})
-  target_compile_options (${name} PRIVATE -g) # do not skip asserts
-  target_include_directories (${name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
-  file (MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/test_outputs/)
-  if (CORSIKA_SANITIZERS_ENABLED)
-    # -O1 is suggested in clang docs to get reasonable performance
-    target_compile_options (${name} PRIVATE -O1 -fno-omit-frame-pointer -fsanitize=${sanitize} -fno-sanitize-recover=all)
-    set_target_properties (${name} PROPERTIES LINK_FLAGS "-fsanitize=${sanitize}")
-  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 exmaple in cmake
-#
-# Examples can be globally executed by 'make run_examples'
-#
-# 1) Simple use:
-# Pass the name of the test.cc file as the first
-# argument, without the ".cc" extention.
-#
-# Example: CORSIKA_ADD_EXAMPLE (testSomething)
-#
-# This generates target testSomething from file testSomething.cc.
-#
-# 2) Customize sources:
-# If 1) doesn't work, use the SOURCES keyword to explicitly
-# specify the sources.
-#
-# Example: CORSIKA_ADD_EXAMPLE (testSomething
-#              SOURCES source1.cc source2.cc someheader.h
-#              RUN_OPTION "extra command line options"
-#              RUN_IN_GDB)
-#
-# In all cases, you can further customize the target with
-# target_link_libraries(testSomething ...) and so on.
-#
-function (CORSIKA_ADD_EXAMPLE)
-  cmake_parse_arguments (PARSE_ARGV 1 C8_ADD_EXAMPLE "RUN_IN_GDB" "" "SOURCES;RUN_OPTIONS")
-  set (name ${ARGV0})
-
-  if (NOT C8_ADD_EXAMPLE_SOURCES)
-    set (sources ${name}.cc)
-  else ()
-    set (sources ${C8_ADD_EXAMPLE_SOURCES})
-  endif ()
-
-  if (NOT C8_ADD_EXAMPLE_RUN_OPTIONS)
-    set (run_options "")
-  else ()
-    set (run_options ${C8_ADD_EXAMPLE_RUN_OPTIONS})
-  endif ()
-
-  add_executable (${name} ${sources})
-  target_compile_options (${name} PRIVATE -g) # do not skip asserts
-  target_include_directories (${name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
-  file (MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/example_outputs/)
-  if (TARGET run_examples)
-  else ()
-    add_custom_target (run_examples)
-  endif ()
-  add_dependencies (run_examples ${name})
-  if (C8_ADD_EXAMPLE_RUN_IN_GDB)
-    # convert cmake list into real string:
-    string (REPLACE ";" " " run_options_str "${run_options}")
-    # run the command in gdb and study backtrace a bit
-    set (CMD gdb -q --batch -ex "run ${run_options_str}" -ex bt -ex "info locals" -ex "up" -ex "info locals" -ex "up" -ex "info locals" -ex "up" -ex "info locals" -ex quit "${CMAKE_CURRENT_BINARY_DIR}/${name}") 
-  else (C8_ADD_EXAMPLE_RUN_IN_GDB)
-    # just run the command as-is
-    set (CMD ${CMAKE_CURRENT_BINARY_DIR}/${name} ${run_options})
-  endif (C8_ADD_EXAMPLE_RUN_IN_GDB)
-  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_ADD_EXAMPLE)
diff --git a/CMakeModules/FindPythia8.cmake b/CMakeModules/FindPythia8.cmake
deleted file mode 100644
index 6b47fd9c3057632ade3ea9e688703c51e4da5d2d..0000000000000000000000000000000000000000
--- a/CMakeModules/FindPythia8.cmake
+++ /dev/null
@@ -1,109 +0,0 @@
-#
-# (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
-#
-# See file AUTHORS for a list of contributors.
-#
-# 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.
-#
-
-#################################################
-#
-# run pythia8-config and interpret result
-#
-
-function (Pythia8_CONFIG_ option variable type doc)
-  execute_process (COMMAND ${Pythia8_CONFIG} ${option}
-    OUTPUT_VARIABLE _local_out_
-    RESULT_VARIABLE _local_res_)
-  string (REGEX REPLACE "\n$" "" _local_out_ "${_local_out_}")
-  if (NOT ${_local_res_} EQUAL 0)
-    message (WARNING "Error in running ${Pythia8_CONFIG} ${option}")
-  else ()
-    set (${variable} "${_local_out_}" CACHE ${type} ${doc})
-  endif ()
-endfunction (Pythia8_CONFIG_)
-  
-
-
-#################################################
-# 
-# Searched Pythia8 on system. Expect pythia8-config in PATH, or typical installation location
-#
-# This module defines
-# Pythia8_INCLUDE_DIR   where to locate Pythia.h file
-# Pythia8_LIBRARY       (not cached) the libraries to link against to use Pythia8
-# Pythia8_VERSION 
-#
-
-set (SEARCH_Pythia8_
-  ${WITH_PYTHIA8}
-  ${PYTHIA8_DIR}
-  $ENV{PYTHIA8_DIR}
-  ${PYTHIA8}
-  $ENV{PYTHIA8}
-  ${PYTHIA8DIR}
-  $ENV{PYTHIA8DIR}
-  ${PYTHIA8_ROOT}
-  $ENV{PYTHIA8_ROOT}
-  /opt/pythia8
-  )
-
-find_program (Pythia8_CONFIG
-  NAME pythia8-config
-  PATHS ${SEARCH_Pythia8_}
-  PATH_SUFFIXES bin
-  DOC "The location of the pythia8-config script"
-  )
-
-if (Pythia8_CONFIG)
-
-  Pythia8_CONFIG_ ("--includedir" Pythia8_INCLUDE_DIR PATH "pythia8 include directory")
-  Pythia8_CONFIG_ ("--prefix" Pythia8_PREFIX PATH "pythia8 prefix directory")
-  find_library (Pythia8_LIBRARY
-    NAMES libpythia8.a libpythia8.so
-    PATH_SUFFIXES lib
-    PATHS ${Pythia8_PREFIX}
-    NO_DEFAULT_PATH
-    DOC "pythia8 library"
-    )
-  set (Pythia8_VERSION "n/a")
-
-else ()
-
-  # if we get here, pythia8-config was not found by CMake so we use
-  # CMake to try and find Pythia8 for us (but let the user know first).
-  # We set these variables to exactly match the format of pythia8-config.
-  # If any one of the variables is not found, CMake will automatically report
-  # that Pythia8 is NOT FOUND (which is what we want).
-  message (STATUS "pythia8-config was not found. Attempting to manually locate Pythia8...")
-
-  # find the main header
-  find_path (Pythia8_INCLUDE_DIR
-    NAME Pythia8/Pythia.h
-    PATH_SUFFIXES include
-    PATHS ${SEARCH_Pythia8_}
-    )
-
-  # and find the main library
-  find_library (Pythia8_LIBRARY
-    NAMES libpythia8.a libpythia8.so
-    PATH_SUFFIXES lib
-    PATHS ${SEARCH_Pythia8_}
-    )
-endif ()
-
-# also determine Pythia8 detailed version number
-if (EXISTS "${Pythia8_INCLUDE_DIR}/Pythia8/Pythia.h")
-  file (READ "${Pythia8_INCLUDE_DIR}/Pythia8/Pythia.h" PYTHIA_H_DATA)
-  string (REGEX MATCH "#define PYTHIA_VERSION_INTEGER ([0-9]*)" test "${PYTHIA_H_DATA}")
-  set (Pythia8_VERSION ${CMAKE_MATCH_1})
-endif ()
-
-# standard cmake infrastructure:
-include (FindPackageHandleStandardArgs)
-find_package_handle_standard_args (Pythia8
-  "Did not find system-level Pythia8."
-  Pythia8_INCLUDE_DIR Pythia8_LIBRARY Pythia8_VERSION)
-mark_as_advanced (Pythia8_INCLUDE_DIR Pythia8_LIBRARY Pythia8_VERSION)
diff --git a/COAST/CMakeLists.txt b/COAST/CMakeLists.txt
deleted file mode 100644
index dc09ce62da9be3f057c59b66da61c68477eace6c..0000000000000000000000000000000000000000
--- a/COAST/CMakeLists.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-set (
-  COAST_HEADERS
-  COASTProcess.h
-  COASTStack.h
-  ParticleConversion.h
-  )
-
-set (
-  COAST_SOURCES
-  COASTUserLib.cc
-  COASTProcess.cc
-  ParticleConversion.cc
-  )
-
-set (
-  COAST_NAMESPACE
-  corsika/coast
-  )
-
-add_library (COAST SHARED ${COAST_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (COAST ${COAST_NAMESPACE} ${COAST_HEADERS})
-
-set_target_properties (
-  COAST
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-#  PUBLIC_HEADER "${MODEL_HEADERS}"
-  )
-
-target_link_libraries (
-  COAST
-  PUBLIC
-  CORSIKAgeometry
-  CORSIKAunits
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAsetup
-  # SuperStupidStack
-  )
-
-target_include_directories (
-  COAST
-  PUBLIC
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-target_include_directories (
-  COAST
-  SYSTEM
-  PUBLIC
-  $ENV{COAST_DIR}/include
-  )
-
-install (
-  TARGETS COAST
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-  PUBLIC_HEADER DESTINATION include/${COAST_NAMESPACE}
-  )
-
-#install (
-#  FILES ${COAST_HEADERS} 
-#  DESTINATION include/${COAST_NAMESPACE}
-#  )
diff --git a/COAST/COASTProcess.cc b/COAST/COASTProcess.cc
deleted file mode 100644
index 827d2f0fa5b05ba2ee41f2bc11bdee1cca431abf..0000000000000000000000000000000000000000
--- a/COAST/COASTProcess.cc
+++ /dev/null
@@ -1,43 +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/coast/COASTProcess.h>
-#include <corsika/coast/COASTStack.h>
-
-#include <iostream>
-
-using namespace std;
-
-namespace corsika::coast {
-
-  /**
-     the init function is called during the start of corsika.
-   */
-  void COASTProcess::Init() {
-    cout << "************* Greetings from CORSIKA8 *****************" << endl;
-  }
-
-  /**
-     the docontinous function is called for each tracking step in
-     corsika. Take care: you cannot modify the particle, the track or
-     the stack from here (docontinuous) inside corisika7. In corsika8
-     you will be able to do that.
-   */
-  corsika::process::EProcessReturn COASTProcess::DoContinuous(const Particle& p,
-                                                              const Track& t,
-                                                              const Stack&) {
-    using namespace corsika::units::si;
-    auto const start = t.GetPosition(0).GetCoordinates();
-    auto const delta = t.GetPosition(1).GetCoordinates() - start;
-    auto const name = corsika::particles::GetName(p.GetPID());
-    cout << "CORSIKA8: particle=" << name << ", pos=" << start
-         << " track-l=" << delta.norm() << ", track-t=" << t.GetDuration() << endl;
-    return corsika::process::EProcessReturn::eOk;
-  }
-
-} // namespace corsika::coast
diff --git a/COAST/COASTProcess.h b/COAST/COASTProcess.h
deleted file mode 100644
index 2886d13c4d7973c70ac5ef4886a968f02842a206..0000000000000000000000000000000000000000
--- a/COAST/COASTProcess.h
+++ /dev/null
@@ -1,41 +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/particles/ParticleProperties.h>
-#include <corsika/process/ContinuousProcess.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/coast/COASTStack.h>
-
-#include <limits>
-
-typedef corsika::coast::COASTStack Stack;
-typedef corsika::coast::COASTStack::ParticleType Particle;
-typedef corsika::geometry::Trajectory<corsika::geometry::Line> Track;
-
-namespace corsika::coast {
-
-  class COASTProcess : public corsika::process::ContinuousProcess<COASTProcess> {
-
-  public:
-    void Init();
-
-    corsika::process::EProcessReturn DoContinuous(const Particle&, const Track&,
-                                                  const Stack&);
-
-    corsika::units::si::LengthType MaxStepLength(Particle&, Track&) {
-      return corsika::units::si::meter * std::numeric_limits<double>::infinity();
-    }
-
-  private:
-  };
-
-} // namespace corsika::coast
diff --git a/COAST/COASTStack.h b/COAST/COASTStack.h
deleted file mode 100644
index 72e0594a78aaf0931e61cccadb198bcfeff172c9..0000000000000000000000000000000000000000
--- a/COAST/COASTStack.h
+++ /dev/null
@@ -1,187 +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/coast/ParticleConversion.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/stack/ParticleBase.h>
-#include <corsika/stack/Stack.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h> // remove
-#include <corsika/geometry/Vector.h>
-
-#include <crs/CParticle.h>
-#include <crs/CorsikaTypes.h>
-
-#include <algorithm>
-#include <vector>
-
-namespace corsika::coast {
-
-  typedef corsika::geometry::Vector<corsika::units::si::hepmomentum_d> MomentumVector;
-
-  /**
-   * Example of a particle object on the stack.
-   */
-
-  template <typename StackIteratorInterface>
-  class ParticleInterface : public corsika::stack::ParticleBase<StackIteratorInterface> {
-
-    using corsika::stack::ParticleBase<StackIteratorInterface>::GetStackData;
-    using corsika::stack::ParticleBase<StackIteratorInterface>::GetIndex;
-
-  public:
-    corsika::particles::Code GetPID() const { return GetStackData().GetPID(GetIndex()); }
-
-    corsika::units::si::HEPEnergyType GetEnergy() const {
-      return GetStackData().GetEnergy(GetIndex());
-    }
-
-    MomentumVector GetMomentum(const corsika::geometry::CoordinateSystem& cs) const {
-      using namespace corsika::units::si;
-      const HEPEnergyType mass = corsika::particles::GetMass(GetPID());
-      const auto P = sqrt((GetEnergy() - mass) * (GetEnergy() + mass));
-      const auto p = GetStackData().GetDirection(cs);
-      return p * P;
-    }
-
-    corsika::geometry::Point GetPosition(
-        const corsika::geometry::CoordinateSystem& cs) const {
-      return GetStackData().GetPosition(cs, GetIndex());
-    }
-
-    corsika::geometry::Vector<corsika::units::si::speed_d> GetVelocity(
-        const corsika::geometry::CoordinateSystem& cs) const {
-      return GetStackData().GetVelocity(cs, GetIndex());
-    }
-
-    corsika::units::si::TimeType GetTime() const {
-      return GetStackData().GetTime(GetIndex());
-    }
-
-    corsika::geometry::Vector<corsika::units::si::dimensionless_d> GetDirection(
-        const corsika::geometry::CoordinateSystem& cs) const {
-      return GetStackData().GetDirection(cs);
-    }
-
-    corsika::units::si::TimeType GetTimeInterval() const {
-      return GetStackData().GetTimeInterval();
-    }
-  };
-
-  /**
-   *
-   * Memory implementation of the most simple (stupid) particle stack object.
-   */
-
-  class COASTStackImpl {
-
-    const crs::CParticle* fParticle1 = 0;
-    const crs::CParticle* fParticle2 = 0;
-
-  public:
-    COASTStackImpl(const crs::CParticle* v1, const crs::CParticle* v2) {
-      fParticle1 = v1;
-      fParticle2 = v2;
-    }
-
-    void Init() {}
-    void Clear() {}
-
-    // there is one particle only
-    int GetSize() const { return 1; }
-    int GetCapacity() const { return 1; }
-
-    // you cannot modify the particle:
-    // there are no Set... function defined
-
-    // readout particle data, there is just one particle!
-    /*
-      double x;
-      double y;
-      double z;
-      double depth;
-      double time;
-      double energy;
-      double weight;
-      int    particleId;
-      int    hadronicGeneration;
-    */
-    corsika::particles::Code GetPID(const int) const {
-      return ConvertFromCoast(static_cast<CoastCode>(fParticle1->particleId));
-    }
-    corsika::units::si::HEPEnergyType GetEnergy(const int) const {
-      using namespace corsika::units::si;
-      return fParticle1->energy * 1_GeV;
-    }
-    corsika::geometry::Vector<corsika::units::si::dimensionless_d> GetDirection(
-        const corsika::geometry::CoordinateSystem& cs) const {
-      using namespace corsika::units::si;
-      corsika::geometry::Point p1(
-          cs, {fParticle1->x * 1_cm, fParticle1->y * 1_cm, fParticle1->z * 1_cm});
-      corsika::geometry::Point p2(
-          cs, {fParticle2->x * 1_cm, fParticle2->y * 1_cm, fParticle2->z * 1_cm});
-      const corsika::geometry::Vector D = p2 - p1;
-      const auto magD = D.norm();
-      const corsika::geometry::Vector dir = D / magD;
-      return dir;
-    }
-    corsika::geometry::Vector<corsika::units::si::speed_d> GetVelocity(
-        const corsika::geometry::CoordinateSystem& cs, const int) const {
-      using namespace corsika::units::si;
-      corsika::geometry::Vector<corsika::units::si::dimensionless_d> dir =
-          GetDirection(cs);
-      corsika::geometry::Point p1(
-          cs, {fParticle1->x * 1_cm, fParticle1->y * 1_cm, fParticle1->z * 1_cm});
-      corsika::geometry::Point p2(
-          cs, {fParticle2->x * 1_cm, fParticle2->y * 1_cm, fParticle2->z * 1_cm});
-      const corsika::geometry::Vector D = p2 - p1;
-      const LengthType magD = D.norm();
-      const TimeType deltaT = GetTimeInterval();
-      return dir * magD / deltaT;
-    }
-    corsika::geometry::Point GetPosition(const corsika::geometry::CoordinateSystem& cs,
-                                         const int) const {
-      using namespace corsika::units::si;
-      return corsika::geometry::Point(
-          cs, {fParticle1->x * 1_cm, fParticle1->y * 1_cm, fParticle1->z * 1_cm});
-    }
-    corsika::units::si::TimeType GetTime(const int) const {
-      using namespace corsika::units::si;
-      return fParticle1->time * 1_s;
-    }
-
-    corsika::units::si::TimeType GetTimeInterval() const {
-      using namespace corsika::units::si;
-      return (fParticle2->time - fParticle1->time) * 1_s;
-    }
-
-    /**
-     *   We do not allow copying!
-     */
-    void Copy(const int, const int) {}
-
-    /**
-     *   We do not allow swapping particles, there is just one...
-     */
-    void Swap(const int, const int) {}
-
-    // size cannot be increased, do nothing
-    void IncrementSize() {}
-
-    // size cannot be decremented, do nothing
-    void DecrementSize() {}
-
-  }; // end class COASTStackImpl
-
-  typedef corsika::stack::Stack<COASTStackImpl, ParticleInterface> COASTStack;
-
-} // namespace corsika::coast
diff --git a/COAST/COASTUserLib.cc b/COAST/COASTUserLib.cc
deleted file mode 100644
index d5b8b0b7c662a556f7d6fa6eb91bce147af6b829..0000000000000000000000000000000000000000
--- a/COAST/COASTUserLib.cc
+++ /dev/null
@@ -1,107 +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 <interface/CorsikaInterface.h>
-
-#include <corsika/coast/COASTProcess.h>
-#include <corsika/coast/COASTStack.h>
-#include <corsika/geometry/CoordinateSystem.h>
-#include <corsika/geometry/Line.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/Trajectory.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <crs/CInteraction.h>
-#include <crs/CParticle.h>
-#include <crs/CorsikaTypes.h>
-#include <crs/TSubBlock.h>
-
-#include <iostream>
-#include <sstream>
-
-using namespace std;
-using namespace corsika;
-using namespace corsika::units::si;
-
-corsika::coast::COASTProcess gCorsikaProcess;
-
-/*
-  Data is one CORSIKA data-block constining of 21 SubBlocks.
-  A SubBlock can be:
-  - thinned mode:     39 (Particles) * 8 (ENTRIES) * 4 (BYTES)
-  - not-thinned mode: 39 (Particles) * 7 (ENTRIES) * 4 (BYTES)
-*/
-extern "C" void wrida_([[maybe_unused]] const CREAL* Data) {
-  // crs::CParticleFortranPtr p;
-  // const bool isF = prminfo_(p);
-}
-
-extern "C" void inida_([[maybe_unused]] const char* filename,
-                       [[maybe_unused]] const int& thinning,
-                       [[maybe_unused]] const int& /*curved*/,
-                       [[maybe_unused]] const int& /*slant*/,
-                       [[maybe_unused]] const int& /*stackinput*/,
-                       [[maybe_unused]] const int& /*preshower*/,
-                       [[maybe_unused]] int str_length) {
-  gCorsikaProcess.Init();
-}
-
-extern "C" void cloda_() {
-  // crs::CParticleFortranPtr pptr;
-  // const bool isF = prminfo_(pptr);
-  // gCorsikaProcess.Close();
-}
-
-void interaction_([[maybe_unused]] const crs::CInteraction& interaction) {
-  /*
-    all interactions in the shower are available in this function !
-    the information availabel in the CInteraction class are:
-    double x;
-    double y;
-    double z;
-    double etot;      // lab energy
-    double sigma;     // cross-section of process
-    double kela;      // elasticity
-    int    projId;    // projectile
-    int    targetId;  // target
-    double time;
-  */
-}
-
-extern "C" void track_([[maybe_unused]] const crs::CParticle& pre,
-                       [[maybe_unused]] const crs::CParticle& post) {
-  /*
-    all particles in the shower are available in this function !
-    The pre and post objecte are the two endpoints for one single track
-    in the shower, where the information available in CParticle is:
-    double x;
-    double y;
-    double z;
-    double depth;
-    double time;
-    double energy;
-    double weight;
-    int    particleId;
-    int    hadronicGeneration;
-  */
-  coast::COASTStack stack(&pre, &post);
-  const auto particle = stack.GetNextParticle();
-  const geometry::CoordinateSystem& rootCS =
-      geometry::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-  geometry::Line const line(particle.GetPosition(rootCS), particle.GetVelocity(rootCS));
-  const TimeType time = particle.GetTimeInterval();
-  const geometry::Trajectory<geometry::Line> track(line, time);
-  gCorsikaProcess.DoContinuous(particle, track, stack);
-}
-
-extern "C" void tabularizedatmosphere_([[maybe_unused]] const int& nPoints,
-                                       [[maybe_unused]] const double* height,
-                                       [[maybe_unused]] const double* refractiveIndex) {
-  // for special use only but should be defined because it is delcared in CORSIKA.F
-}
diff --git a/COAST/ParticleConversion.cc b/COAST/ParticleConversion.cc
deleted file mode 100644
index de8ca909f819331f4a4afbde917a7a2829ebb048..0000000000000000000000000000000000000000
--- a/COAST/ParticleConversion.cc
+++ /dev/null
@@ -1,35 +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/coast/ParticleConversion.h>
-#include <corsika/particles/ParticleProperties.h>
-
-#include <exception>
-#include <iostream>
-#include <sstream>
-
-using namespace std;
-
-namespace corsika::coast {
-
-  /**
-     Convert particle code, and check if it does exists. Throw exeption, if not!
-   */
-
-  corsika::particles::Code ConvertFromCoast(CoastCode pCode) {
-    if (coast2corsika.count(pCode) == 0) {
-      ostringstream err;
-      err << "corsika::coast::ConvertFromCoast CoastCode does not exists="
-          << static_cast<CoastCodeIntType>(pCode) << endl;
-      cout << err.str() << endl;
-      throw std::runtime_error(err.str());
-    }
-    return coast2corsika.find(pCode)->second;
-  }
-
-} // namespace corsika::coast
diff --git a/COAST/ParticleConversion.h b/COAST/ParticleConversion.h
deleted file mode 100644
index 68613783bbabfcfa55d4246b0473c0a1291cfcb5..0000000000000000000000000000000000000000
--- a/COAST/ParticleConversion.h
+++ /dev/null
@@ -1,209 +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/particles/ParticleProperties.h>
-
-#include <map>
-
-namespace corsika::coast {
-
-  /**
-     Here we define the original CORSIKA particle codes, see corsika manual
-   */
-  enum class CoastCode : int16_t {
-    Gamma = 1,
-    Positron = 2,
-    Electron = 3,
-    MuonBar = 5,
-    Muon = 6,
-    Pi0 = 7,
-    PiP = 8,
-    PiM = 9,
-    Klong = 10,
-    KP = 11,
-    KM = 12,
-    Neutron = 13,
-    Proton = 14,
-    ProtonBar = 15,
-    Kshort = 16,
-    Eta = 17,
-    Lambda = 18,
-    SigmaPlus = 19,
-    Sigma0 = 20,
-    SigmaMinus = 21,
-    Xi0 = 22,
-    XiMinus = 23,
-    OmegaMinus = 24,
-    NeutronBar = 25,
-    LambdaBar = 26,
-    SigmaMinusBar = 27,
-    Sigma0Bar = 28,
-    SigmaPlusBar = 29,
-    Xi0Bar = 30,
-    XiPlusBar = 31,
-    OmegaPlusBar = 32,
-
-    EtaPrime = 48,
-    Phi = 49,
-    omega = 50,
-    Rho0 = 51,
-    RhoPlus = 52,
-    RhoMinus = 53,
-    DeltaPlusPlus = 54,
-    DeltaPlus = 55,
-    Delta0 = 56,
-    DeltaMinus = 57,
-    DeltaMinusMinusBar = 58,
-    DeltaMinusBar = 59,
-    Delta0Bar = 60,
-    DeltaPlusBar = 61,
-    KStar0 = 62,
-    KStarPlus = 63,
-    KStarMinus = 64,
-    KStar0Bar = 65,
-
-    NeutrinoE = 66,
-    NeutrinoEBar = 67,
-    NeutrinoMu = 68,
-    NeutrinoMuBar = 69,
-
-    Code71 = 71,
-    Code72 = 72,
-    Code73 = 73,
-    Code74 = 74,
-    Code75 = 75,
-    Code76 = 76,
-
-    Code85 = 85,
-    Code86 = 86,
-
-    Code95 = 95,
-    Code96 = 96,
-
-    ProtonNucleus = 101,
-    Deuterium = 201,
-    Tritium = 301,
-    He3 = 302,
-    Helium = 402,
-    Lithium = 603,
-    Beryllium = 904,
-    Boron = 1005,
-    Carbon = 1206,
-    Carbon13 = 1306,
-    Nitrogen = 1407,
-    Oxygen = 1608,
-    Fluor = 1809,
-    Neon21 = 2110,
-    Neon = 2210,
-    Argon = 1838,
-    Iron = 5628,
-    Xenon = 12854,
-    Radon = 13888,
-  };
-
-  using CoastCodeIntType = std::underlying_type<CoastCode>::type;
-
-  /**
-     Here we convert CORSIKA7 to CORSIKA8 codes
-   */
-
-  const std::map<corsika::coast::CoastCode, corsika::particles::Code> coast2corsika = {
-      {CoastCode::Gamma, corsika::particles::Code::Gamma},
-      {CoastCode::Positron, corsika::particles::Code::Positron},
-      {CoastCode::Electron, corsika::particles::Code::Electron},
-      //{CoastCode::    ,corsika::particles::Code::Unknown}, // 4
-      {CoastCode::MuonBar, corsika::particles::Code::MuPlus},
-      {CoastCode::Muon, corsika::particles::Code::MuMinus},
-      {CoastCode::Pi0, corsika::particles::Code::Pi0},
-      {CoastCode::PiP, corsika::particles::Code::PiPlus},
-      {CoastCode::PiM, corsika::particles::Code::PiMinus},
-      {CoastCode::Klong, corsika::particles::Code::K0Long}, // 10
-      {CoastCode::KP, corsika::particles::Code::KPlus},
-      {CoastCode::KM, corsika::particles::Code::KMinus},
-      {CoastCode::Neutron, corsika::particles::Code::Neutron},
-      {CoastCode::Proton, corsika::particles::Code::Proton}, // 14
-      {CoastCode::ProtonBar, corsika::particles::Code::AntiProton},
-      {CoastCode::Kshort, corsika::particles::Code::K0Short},
-      {CoastCode::Eta, corsika::particles::Code::Eta}, // 17
-      {CoastCode::Lambda, corsika::particles::Code::Lambda0},
-      {CoastCode::SigmaPlus, corsika::particles::Code::SigmaPlus},
-      {CoastCode::Sigma0, corsika::particles::Code::Sigma0}, // 20
-      {CoastCode::SigmaMinus, corsika::particles::Code::SigmaMinus},
-      {CoastCode::Xi0, corsika::particles::Code::Xi0},
-      {CoastCode::XiMinus, corsika::particles::Code::XiMinus},
-      {CoastCode::OmegaMinus, corsika::particles::Code::OmegaMinus},
-      {CoastCode::NeutronBar, corsika::particles::Code::AntiNeutron}, // 25
-      {CoastCode::LambdaBar, corsika::particles::Code::Lambda0Bar},
-      {CoastCode::SigmaMinusBar, corsika::particles::Code::SigmaMinusBar},
-      {CoastCode::Sigma0Bar, corsika::particles::Code::Sigma0Bar},
-      {CoastCode::SigmaPlusBar, corsika::particles::Code::SigmaPlusBar},
-      {CoastCode::Xi0Bar, corsika::particles::Code::Xi0Bar},
-      {CoastCode::XiPlusBar, corsika::particles::Code::XiPlusBar},
-      {CoastCode::OmegaPlusBar, corsika::particles::Code::OmegaPlusBar}, // 32
-      //{CoastCode::    ,corsika::particles::Code::Unknown}, // eta-prime
-      //{CoastCode::    ,corsika::particles::Code::Unknown}, // PHI
-      //{CoastCode::    ,corsika::particles::Code::Unknown}, // omega
-      {CoastCode::Rho0, corsika::particles::Code::Rho0}, // 51
-      {CoastCode::RhoPlus, corsika::particles::Code::RhoPlus},
-      {CoastCode::RhoMinus, corsika::particles::Code::RhoMinus},
-      {CoastCode::DeltaPlusPlus, corsika::particles::Code::DeltaPlusPlus},
-      {CoastCode::DeltaPlus, corsika::particles::Code::DeltaPlus},
-      {CoastCode::Delta0, corsika::particles::Code::Delta0}, // 56
-      //{CoastCode::    ,corsika::particles::Code::Unknown}, // DeltaMinus},
-      {CoastCode::DeltaMinusMinusBar, corsika::particles::Code::DeltaMinusMinusBar},
-      {CoastCode::DeltaMinusBar, corsika::particles::Code::DeltaMinusBar},
-      {CoastCode::Delta0Bar, corsika::particles::Code::Delta0Bar},
-      //{CoastCode::    ,corsika::particles::Code::Unknown}, // DeltaPlusBar
-      {CoastCode::KStar0, corsika::particles::Code::KStar0}, // 62
-      {CoastCode::KStarPlus, corsika::particles::Code::KStarPlus},
-      {CoastCode::KStarMinus, corsika::particles::Code::KStarMinus},
-      {CoastCode::KStar0Bar, corsika::particles::Code::KStar0Bar},
-      {CoastCode::NeutrinoE, corsika::particles::Code::NuE},
-      {CoastCode::NeutrinoEBar, corsika::particles::Code::NuEBar},
-      {CoastCode::NeutrinoMu, corsika::particles::Code::NuMu},
-      {CoastCode::NeutrinoMuBar, corsika::particles::Code::NuMuBar}, // 69
-      {CoastCode::Code71, corsika::particles::Code::Unknown},
-      {CoastCode::Code72, corsika::particles::Code::Unknown},
-      {CoastCode::Code73, corsika::particles::Code::Unknown},
-      {CoastCode::Code74, corsika::particles::Code::Unknown},
-      {CoastCode::Code75, corsika::particles::Code::Unknown},
-      {CoastCode::Code76, corsika::particles::Code::Unknown},
-      {CoastCode::Code85, corsika::particles::Code::Unknown},
-      {CoastCode::Code86, corsika::particles::Code::Unknown},
-      {CoastCode::Code95, corsika::particles::Code::Unknown},
-      {CoastCode::Code96, corsika::particles::Code::Unknown},
-
-      {CoastCode::ProtonNucleus, corsika::particles::Code::Proton}, // 101
-      {CoastCode::Deuterium, corsika::particles::Code::Deuterium},
-      {CoastCode::Tritium, corsika::particles::Code::Tritium},
-      {CoastCode::He3, corsika::particles::Code::Helium3},
-      {CoastCode::Helium, corsika::particles::Code::Helium}, // 402
-      //{CoastCode::Lithium, corsika::particles::Code::Lithium},
-      //{CoastCode::Beryllium, corsika::particles::Code::Beryllium},
-      //{CoastCode::Boron, corsika::particles::Code::Boron},
-      //{CoastCode::Carbon, corsika::particles::Code::Carbon},
-      //{CoastCode::Carbon13, corsika::particles::Code::Carbon13},
-      //{CoastCode::Nitrogen, corsika::particles::Code::Nitrogen},
-      //{CoastCode::Fluor, corsika::particles::Code::Fluor},
-      //{CoastCode::Oxygen, corsika::particles::Code::Oxygen},
-      //{CoastCode::Neon21, corsika::particles::Code::Neon21},
-      //{CoastCode::Neon, corsika::particles::Code::Neon},
-      //{CoastCode::Argon, corsika::particles::Code::Argon},
-      //{CoastCode::Xenon, corsika::particles::Code::Xenon},
-      //{CoastCode::Radon, corsika::particles::Code::Radon},
-      {CoastCode::Iron, corsika::particles::Code::Iron}, // 5628
-
-      //{CoastCode::, corsika::particles::Code::},
-
-  };
-
-  corsika::particles::Code ConvertFromCoast(CoastCode pCode);
-
-} // namespace corsika::coast
diff --git a/COAST/README.md b/COAST/README.md
deleted file mode 100644
index 96e62845bd4843c21e97d15e8c4a249c69cec0d6..0000000000000000000000000000000000000000
--- a/COAST/README.md
+++ /dev/null
@@ -1,65 +0,0 @@
-# COAST interface {#COAST}
-
-With the COAST interface of CORSIKA 8 you can write a CORSIKA 8
-"process" class and link it to CORSIKA 7. This can be very powerful to
-benchmark new CORSIKA 8 physics code in the context of "old" CORSIKA 7
-simulations. 
-
-This is based on corsika7/trunk/coast/CoastOptions/example with an
-additional interface to CORSIKA 8. See code in namespace corsika::coast for all details.
-
-We provide a step-by-step example for a "COAST user library" using CORSIKA 8
-technology. It explains the steps, how to use the COAST_USER_LIB
-option of CORSIKA together with CORSIKA8/COAST
-
-
-Step 1:
--------
-
-Configure CORSIKA8 with 'cmake -DWITH_COAST=1' and other options you
-prefer. You have to define COAST_DIR environment variable to the
-location of your existing CORSIKA7 installation. Check that 'ls
-$COAST_DIR/include/interface' contains the file
-'CorsikaInterface.h'. If this is not the case, this is not a valid or
-proper installation of CORSIKA. First compile CORSIKA, e.g. with
-ROOTOUT option, or CoReas to get COAST installed!
-
-
-Step 2:
--------
-
-Compile CORSIKA8, edit COAST/COASTProcess.cc to modify/add your
-physics module. 
-
-There should be a libCOAST.so as a result! This is what you need.
-
-
-Step 3:
--------
-
-Create COAST_USER_LIB environment variable to point at your current
-directory, where now the 'libCOAST.so' library is located.
-Add the path in $COAST_USER_LIB to your LD_LIBRARY_PATH environment
-variable.
-
-
-Step 4:
--------
-
-Go back to your CORSIKA directory and re-start 'coconut'. The option
-COAST_USER_LIB will now be visible. Please select it, copile CORSIKA
-and start the executable. In the generated console output you will the
-statements from your COAST library during Init and Close of the
-simulation.  
-Add any kind of code now working on the CParticle or
-CInteraction class to start using the full power of COAST.
-
-Note: the default COASTProcess just prints out tons of ASCII. This is
-not very useful, don't run a full MC with this...
-
-
-Step 5:
--------
-
-enjoy...
-
diff --git a/COLLABORATION_AGREEMENT.md b/COLLABORATION_AGREEMENT.md
index bdc9bdf9e5aa3406e6eb6208ad31d481ba3c7c6b..ea9ed3634e0dd71c403ef0025ff6ad86bff94821 100644
--- a/COLLABORATION_AGREEMENT.md
+++ b/COLLABORATION_AGREEMENT.md
@@ -23,12 +23,13 @@ liability and licensing question are only handled by the adopted
 software license.
 
 ## The software license of the CORSIKA project
+
 The license adopted for the CORSIKA project is the explicit copyleft
 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 [ThirdParty](ThirdParty)
+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
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 000470412f22b17b22b1a38db5176fb2f776b207..be0fb7e43a6f4def6aed87491b0a862f4b0ba483 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -8,7 +8,7 @@ change/improve them.
 
 # How to contribute
 
-  - We organize all development via `Issues` that may be feature requests,
+  - We organize all development via gitlab `Issues` that may be feature requests,
     ideas, discussions, or bugs fix requests. 
   - New issues can be created, or existing issues
     picked up or contributed to. 
@@ -19,7 +19,7 @@ change/improve them.
     created directly via the gitlab web interface. 
   - Proposed code to close one issue (located in a specific git
     branch) is reviewed, discussed, and eventually merged
-    into the master branch to close the issue.
+    into the master branch via a merge-request (MR) to close the issue.
   - all merge request will undergo a code review, and must be approved before merge, in order to ensure high code qualtiy: [Code Approval Procedure](https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika/-/wikis/Code-Approval-Procedure)
 
 
@@ -32,9 +32,9 @@ formatting. We provide a script `do-clang-format.sh`, which can be
 very useful. But we urge everybody to integrate `clang-format` already
 on the level of your source code editor. See [the official
 page](https://clang.llvm.org/docs/ClangFormat.html) for information
-about `clang-format` and its editor integration. At least: run
-`do-clang-format.sh` prior to any `git add` command. Code with
-improper formatting will not be accepted for merging. 
+about `clang-format` and its editor integrations. At least: run
+`do-clang-format.sh` prior to any `git add/commit` command. Code with
+improper formatting will not be accepted for merging. It will trigger automatic warnings by the continuous integration (CI) system.
 
 The definition of source code format is written down in the file
 [.clang-format](.clang-format) and can be changed, if the CORSIKA
@@ -43,134 +43,36 @@ e.g. [link1](https://clangformat.com/) or
 [link2](https://zed0.co.uk/clang-format-configurator/).
 
 
-## Naming conventions
-
-While `clang-format` does the structural formatting, we still need to agree on naming conventions:
-
-  - Classes and structs start with capital letters ```LikeThis```
-  - Class member variables start non-captial and have a trailing "_" ```likeThis_```
-  - Class member functions start with small letters ```LikeThis::doSomething(...)``` or ```LikeThis::do_something(...)```
-  - Any class setter begins with "set_" ```LikeThis::set_something(...)```
-  - Any class getter is named accoring to its property: ```LikeThis::something()```
-  - Logical getters may start with ```is_``` or ```has_```
-  - enums should be ```enum class``` 
-  - Named variables start with small letter ```likeThis```
-  - types in template definitions start with "T" ```TLikeThis```
-  
-  - use names that explain and document the code, thus for example
-  ``` 
-     TimeType TimeOfIntersection(Line const& line, Plane const& plane) {
-         auto const line_direction = line.GetDirection();     
-         auto const plane_normal = plane.GetNormal();     
-         return plane_normal.dot(plane.GetCenter()-line.GetPosition()) / plane_normal.dot(line_direction);     
-         }
-  ```
-    and not 
-  ```
-       TimeType TimeOfIntersection(Line const& l, Plane const& p) {
-         auto const d = p.GetCenter() - l.GetR0();     
-         auto const v = l.GetV0();     
-         auto const n = p.GetNormal();     
-         auto const c = n.dot(v);    
-         return n.dot(d) / c;     
-         }
-  ```
-    This actually is suffient to document the code, no further comments should be added in such cases. Only if further clarification is needed.  
-
-  - We use namespaces to avoid clashes and to structure code
-    - *Everything* is part of one of those namespaces:
-       - ```corsika::framework```, ```corsika::physics```, or ```corsika::process```
-    - All classes and objects are encapsulated into suited sub-namespaces,
-      thus ```corsika::framework::geometry```, ```corsika::physics::process```, ```corsika::physics::units```, etc.
-    - Namespace names do not use capital letters.  
-  - Every header file is located in the source tree in ```include/corsika/[namespace]```, which also means that in almost all cases each header file
-    can only provide definitions for a _single_ namespace. It is one
-    main purpose of namespaces to structure the location of header
-    files.
-  - Each header file uses an include protection ```#pragma once```
-    immediately below the copyright statement.
-  - Header files should always be included with `<..>`, thus,
-    `#include <corsika/geometry/Point.h>` since the build system
-    will always provide the correct include directives (and files
-    anyway cannot be found in file-system paths, which generally do
-    not follow the namespace naming conventions outlined
-    here).
-  - Header files are named after the main class (or object) they
-    define. This also means each header file name starts with a
-    capital letter.
-  
-
-## Coding rules
-
-  - Warnings should be fixed, when they appear on the system(s) currently used by the CI, by altering the code unless the warning originates from third-party code and cannot be fixed. In that case, the warning should be locally silenced with appropriate pragmas or by locally turning off warnings for that translation unit. 
-  - All unit tests must succeed on the specified systems/configurations on gitlab-ci. If tests fail, code is not merged.
-  - The unit test code coverage should not decrease due to a new merge request. 
-  - We use C++17 features wherever useful and helpful. 
-  - On any major error or malfunction we throw an exception. This is needed and required for complex physics and shower debugging.
-  - We never catch exceptions for error handling, there might be very few special exceptions from this. We need to discuss such cases.
-  - Everything that should not change should be ```const```
-  - Everything that does not need to be visible to the outside of a class/struct should be `private` or `protected`
-  - We prefer the use of references, wherever useful
-  - There cannot be any raw pointer in the interface of any class or object
-    exposed to outside users, there might be (technical) raw pointers for very special cases
-    inside of classes.
-  - (member)functions that do something (are not just getters/setters), model actions or transformations, and should therefore be verbs. Classes are nouns.
-  - When you contribute new code, or extend existing code, at the same time provide unit-tests for all functionality.
-  - When you contribute new physics algorithms, in addition you also need to provide a validation module (this is still TBD what exactly this means)
-  - Code must be documented with `doxygen` commands extensively -> MAKE THIS INVESTMENT OF YOUR TIME EARLY, IT REALLY HELPS
-  - There should not be any useless comments in code, in particular absolutely avoid to commit commented-out debug remnants
-  - Add sufficient and meaningful comments to the code (only in English)
-  
-## CMAKE formatting
-
-  - command are lower cases, e.g. ```set (...)```
-  - variables upper case, e.g. ```set (VAR1 Text)```
-
-Since cmake itself lacks structure almost entirely:
-  - put a space between command and start of parenthesis, e.g. ```command (...)``` 
-  - add two spaces for logical indent 
-    ```
-    if (condition)
-      do something
-    endif (condition)
-    ```
-  - break long lines to start with new keyword in new line (indented)
-    ```
-    install (
-      FILES ${CORSIKAstackinterface_HEADERS}
-      DESTINATION include/${CORSIKAstackinterface_NAMESPACE}
-      )  
-    ```
-  - add plenty of comments to all cmake code
-  - use expressive variables and functions
+## Coding rules, conventions and guidelines
+
+Please read the [Coding wiki page](https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika/-/wikis/Coding-Conventions-and-Guidelines).
+
 
 
 
 ## Release versioning scheme
 
-Releases of CORSIKA are thought to be the baseline for larger scale
-testing, and full production.  The releases are numbered as x.y.z,
-starting with x=8 form the gitlab c++ version. X will only be
-incremented for major design or physics changes. The y index is
-updated for new official releases that normally contain improved or
-enhanced physics performance, and may also contain normal interface
+Releases of CORSIKA 8 are thought to be the baseline for larger scale
+validation, and full production.  The releases are numbered as x.y.z,
+starting with x=8, which will not be changed for CORSIKA 8. The y index is
+updated for new releases that normally contain improved or
+enhanced physics performance, and also interface
 changes to accomodate improvements. The z index can be updated more
 frequently for bug fixes or new features. Changes in z will not
-contain (major) interface changes, thus, production code will remain
-fully compatible within changes of z.  Special releases of CORSIKA can
-also have a tag name from git, e.g. as in the "milestone1" release.
+contain interface changes, thus, production code will remain
+fully compatible within changes of z.  Special releases of CORSIKA will
+also have a release names.
 
 
 # How to become scientific author of the CORSIKA Project
 
 The CORSIKA Project decides on who becomes scientific author. The
-following conditions are clearly sufficient, but not all of them are
+following conditions are sufficient, but not all of them are
 required all the time:
   - responsibility for a particular functionality or software/management part 
   - have read and follow these [GUIDELINES](CONTRIBUTING.md)
   - active in the CORSIKA Project, that means responsive to
     discussions and problems in corsika-devel@list.kit.edu or on https//gitlab.ikp.kit.edu, 
-    of relevant *Issues*,
-    or in (phone) meetings
-  - agreement to the [COLLABORATION_AGREEMENT](COLLABORATION_AGREEMENT.md) is strictly required
-  - the members of the CORSIKA Project panel agree
+    of relevant *Issues*, or in (phone) meetings
+  - agreement to the [COLLABORATION_AGREEMENT](COLLABORATION_AGREEMENT.md) 
+  - the members of the CORSIKA Project must agree
diff --git a/Data b/Data
deleted file mode 160000
index 4d1785e74a7bf6769ee016509b357e63d7117438..0000000000000000000000000000000000000000
--- a/Data
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 4d1785e74a7bf6769ee016509b357e63d7117438
diff --git a/Documentation/CMakeLists.txt b/Documentation/CMakeLists.txt
deleted file mode 100644
index ffab73c843a22a06928395980a8b1e097a194234..0000000000000000000000000000000000000000
--- a/Documentation/CMakeLists.txt
+++ /dev/null
@@ -1,3 +0,0 @@
-add_subdirectory (Doxygen)
-add_subdirectory (Examples)
-
diff --git a/Documentation/Examples/CMakeLists.txt b/Documentation/Examples/CMakeLists.txt
deleted file mode 100644
index adbbca31ce43a2a4cfcca7808f9930d6cbbe1b27..0000000000000000000000000000000000000000
--- a/Documentation/Examples/CMakeLists.txt
+++ /dev/null
@@ -1,195 +0,0 @@
-
-CORSIKA_ADD_EXAMPLE (helix_example)
-target_link_libraries (helix_example CORSIKAgeometry CORSIKAunits)
-
-CORSIKA_ADD_EXAMPLE (particle_list_example)
-target_link_libraries (particle_list_example CORSIKAparticles CORSIKAunits CORSIKAprocesses ProcessSibyll ProcessQGSJetII)
-
-CORSIKA_ADD_EXAMPLE (geometry_example)
-target_link_libraries (geometry_example CORSIKAgeometry CORSIKAunits)
-
-CORSIKA_ADD_EXAMPLE (stack_example)
-target_link_libraries (stack_example CORSIKAsetup CORSIKAunits)
-
-# address sanitizer is making this example too slow, so we only do "undefined"
-CORSIKA_ADD_EXAMPLE (boundary_example)
-target_link_libraries (boundary_example
-  CORSIKAsetup
-  CORSIKAunits
-  CORSIKAlogging
-  CORSIKArandom
-  ProcessSibyll
-  CORSIKAcascade
-  ProcessTrackWriter
-  ProcessParticleCut
-  ProcessTrackingLine
-  ProcessPythia8
-  CORSIKAprocesses
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAprocesssequence
-  C8::ext::boost # boost::histogram
-  )
-
-CORSIKA_ADD_EXAMPLE (cascade_example)
-target_link_libraries (cascade_example
-  CORSIKAsetup
-  CORSIKAunits
-  CORSIKAlogging
-  CORSIKArandom
-  ProcessSibyll
-  ProcessProposal
-  CORSIKAcascade
-  ProcessEnergyLoss
-  ProcessTrackWriter
-  ProcessStackInspector
-  ProcessTrackingLine
-  ProcessParticleCut
-  ProcessHadronicElasticModel
-  ProcessStackInspector
-  CORSIKAprocesses
-  CORSIKAcascade
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAprocesssequence
-  )
-
-if (Pythia8_FOUND)
-  CORSIKA_ADD_EXAMPLE (cascade_proton_example)
-  target_link_libraries (cascade_proton_example
-    CORSIKAsetup
-    CORSIKAunits
-    CORSIKAlogging
-    CORSIKArandom
-    ProcessSibyll
-    ProcessPythia8
-    ProcessUrQMD
-    CORSIKAcascade
-    ProcessEnergyLoss
-    ProcessTrackWriter
-    ProcessStackInspector
-    ProcessTrackingLine
-    ProcessParticleCut
-    ProcessOnShellCheck
-    ProcessHadronicElasticModel
-    ProcessStackInspector
-    CORSIKAprocesses
-    CORSIKAcascade
-    CORSIKAparticles
-    CORSIKAgeometry
-    CORSIKAenvironment
-    CORSIKAprocesssequence
-    )
-
-  CORSIKA_ADD_EXAMPLE (vertical_EAS RUN_OPTIONS 4 2 10000.)
-  target_link_libraries (vertical_EAS
-    CORSIKAsetup
-    CORSIKAunits
-    CORSIKAlogging
-    CORSIKArandom
-    CORSIKAhistory
-    ProcessSibyll
-    ProcessPythia8
-    ProcessUrQMD
-    CORSIKAcascade
-    ProcessProposal
-    ProcessPythia8
-    ProcessObservationPlane
-    ProcessInteractionCounter
-    ProcessTrackWriter
-    ProcessEnergyLoss
-    ProcessTrackingLine
-    ProcessParticleCut
-    ProcessOnShellCheck
-    ProcessStackInspector
-    ProcessLongitudinalProfile
-    CORSIKAprocesses
-    CORSIKAcascade
-    CORSIKAparticles
-    CORSIKAgeometry
-    CORSIKAenvironment
-    CORSIKAprocesssequence
-    CORSIKAhistory # for HistoryObservationPlane
-    )
-
-  CORSIKA_ADD_EXAMPLE (hybrid_MC RUN_OPTIONS 4 2 10000.)
-  target_link_libraries (hybrid_MC
-    CORSIKAsetup
-    CORSIKAunits
-    CORSIKAlogging
-    CORSIKArandom
-    CORSIKAhistory
-    ProcessCONEXSourceCut
-    ProcessInteractionCounter
-    ProcessSibyll
-    ProcessPythia8
-    ProcessUrQMD
-    CORSIKAcascade
-    ProcessPythia8
-    ProcessObservationPlane
-    ProcessInteractionCounter
-    ProcessTrackWriter
-    ProcessEnergyLoss
-    ProcessTrackingLine
-    ProcessParticleCut
-    ProcessOnShellCheck
-    ProcessStackInspector
-    ProcessLongitudinalProfile
-    CORSIKAprocesses
-    CORSIKAcascade
-    CORSIKAparticles
-    CORSIKAgeometry
-    CORSIKAenvironment
-    CORSIKAprocesssequence
-    CORSIKAhistory # for HistoryObservationPlane
-    )
-endif()
-
-CORSIKA_ADD_EXAMPLE (stopping_power stopping_power)
-target_link_libraries (stopping_power
-  CORSIKAsetup
-  CORSIKAunits
-  CORSIKAlogging
-  ProcessEnergyLoss
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAenvironment
-  )
-
-CORSIKA_ADD_EXAMPLE (staticsequence_example)
-target_link_libraries (staticsequence_example
-  CORSIKAprocesssequence
-  CORSIKAunits
-  CORSIKAgeometry
-  CORSIKAlogging)
-
-
-CORSIKA_ADD_EXAMPLE (em_shower RUN_OPTIONS "100.")
-target_link_libraries (em_shower
-  SuperStupidStack
-  CORSIKAunits
-  CORSIKAlogging
-  CORSIKArandom
-  ProcessSibyll
-  ProcessPythia8
-  ProcessUrQMD
-  CORSIKAcascade
-  ProcessEnergyLoss
-  ProcessObservationPlane
-  ProcessInteractionCounter
-  ProcessTrackWriter
-  ProcessProposal
-  ProcessTrackingLine
-  ProcessParticleCut
-  ProcessOnShellCheck
-  ProcessStackInspector
-  ProcessLongitudinalProfile
-  CORSIKAprocesses
-  CORSIKAcascade
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAprocesssequence
-  )
diff --git a/Documentation/Examples/boundary_example.cc b/Documentation/Examples/boundary_example.cc
deleted file mode 100644
index ed9d7bd43cc24c03df3642315cd484afde1fb99b..0000000000000000000000000000000000000000
--- a/Documentation/Examples/boundary_example.cc
+++ /dev/null
@@ -1,189 +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/cascade/Cascade.h>
-#include <corsika/process/ProcessSequence.h>
-
-#include <corsika/setup/SetupEnvironment.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/HomogeneousMedium.h>
-#include <corsika/environment/NuclearComposition.h>
-
-#include <corsika/geometry/Sphere.h>
-
-#include <corsika/process/sibyll/Decay.h>
-#include <corsika/process/sibyll/Interaction.h>
-#include <corsika/process/sibyll/NuclearInteraction.h>
-
-#include <corsika/process/track_writer/TrackWriter.h>
-
-#include <corsika/process/particle_cut/ParticleCut.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/random/RNGManager.h>
-
-#include <corsika/utl/CorsikaFenv.h>
-
-#include <corsika/logging/Logging.h>
-
-#include <iostream>
-#include <limits>
-#include <typeinfo>
-
-using namespace corsika;
-using namespace corsika::process;
-using namespace corsika::units;
-using namespace corsika::particles;
-using namespace corsika::random;
-using namespace corsika::geometry;
-using namespace corsika::environment;
-
-using namespace std;
-using namespace corsika::units::si;
-
-template <bool deleteParticle>
-struct MyBoundaryCrossingProcess
-    : public BoundaryCrossingProcess<MyBoundaryCrossingProcess<deleteParticle>> {
-
-  MyBoundaryCrossingProcess(std::string const& filename) { fFile.open(filename); }
-
-  template <typename Particle>
-  EProcessReturn DoBoundaryCrossing(Particle& p,
-                                    typename Particle::BaseNodeType const& from,
-                                    typename Particle::BaseNodeType const& to) {
-
-    C8LOG_INFO("MyBoundaryCrossingProcess: crossing! from: {} to: {} ", fmt::ptr(&from),
-               fmt::ptr(&to));
-
-    auto const& name = particles::GetName(p.GetPID());
-    auto const start = p.GetPosition().GetCoordinates();
-
-    fFile << name << "    " << start[0] / 1_m << ' ' << start[1] / 1_m << ' '
-          << start[2] / 1_m << '\n';
-
-    if constexpr (deleteParticle) { p.Delete(); }
-
-    return EProcessReturn::eOk;
-  }
-
-private:
-  std::ofstream fFile;
-};
-
-//
-// The example main program for a particle cascade
-//
-int main() {
-
-  logging::SetLevel(logging::level::trace);
-
-  C8LOG_INFO("boundary_example");
-
-  feenableexcept(FE_INVALID);
-  // initialize random number sequence(s)
-  random::RNGManager::GetInstance().RegisterRandomStream("cascade");
-
-  // setup environment, geometry
-  using EnvType = setup::Environment;
-  EnvType env;
-  auto& universe = *(env.GetUniverse());
-
-  const CoordinateSystem& rootCS = env.GetCoordinateSystem();
-
-  // create "world" as infinite sphere filled with protons
-  auto world = EnvType::CreateNode<Sphere>(
-      Point{rootCS, 0_m, 0_m, 0_m}, 100_km);
-
-  using MyHomogeneousModel =
-      environment::MediumPropertyModel<environment::UniformMagneticField<
-          environment::HomogeneousMedium<setup::EnvironmentInterface>>>;
-
-  auto const props = world->SetModelProperties<MyHomogeneousModel>(
-      environment::Medium::AirDry1Atm, Vector(rootCS, 0_T, 0_T, 0_T),
-      1_kg / (1_m * 1_m * 1_m),
-      environment::NuclearComposition(
-          std::vector<particles::Code>{particles::Code::Proton},
-          std::vector<float>{1.f}));
-
-  // add a "target" sphere with 5km readius at 0,0,0
-  auto target = EnvType::CreateNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 5_km);
-  target->SetModelProperties(props);
-
-  world->AddChild(std::move(target));
-  universe.AddChild(std::move(world));
-
-  // setup processes, decays and interactions
-  setup::Tracking tracking;
-
-  random::RNGManager::GetInstance().RegisterRandomStream("sibyll");
-  process::sibyll::Interaction sibyll;
-  process::sibyll::Decay decay;
-
-  process::particle_cut::ParticleCut cut(50_GeV, true, true);
-
-  process::track_writer::TrackWriter trackWriter("boundary_tracks.dat");
-  MyBoundaryCrossingProcess<true> boundaryCrossing("crossings.dat");
-
-  // assemble all processes into an ordered process list
-  auto sequence = process::sequence(sibyll, decay, cut, boundaryCrossing, trackWriter);
-
-  // setup particle stack, and add primary particles
-  setup::Stack stack;
-  stack.Clear();
-  const Code beamCode = Code::MuPlus;
-  const HEPMassType mass = particles::GetMass(beamCode);
-  const HEPEnergyType E0 = 100_GeV;
-
-  std::uniform_real_distribution distTheta(0., 180.);
-  std::uniform_real_distribution distPhi(0., 360.);
-  std::mt19937 rng;
-
-  for (int i = 0; i < 100; ++i) {
-    double const theta = distTheta(rng);
-    double const phi = distPhi(rng);
-
-    auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) {
-      return sqrt((Elab - m) * (Elab + m));
-    };
-    HEPMomentumType P0 = elab2plab(E0, mass);
-    auto momentumComponents = [](double theta, double phi, HEPMomentumType ptot) {
-      return std::make_tuple(ptot * sin(theta) * cos(phi), ptot * sin(theta) * sin(phi),
-                             -ptot * cos(theta));
-    };
-    auto const [px, py, pz] =
-        momentumComponents(theta / 180. * M_PI, phi / 180. * M_PI, P0);
-    auto plab = corsika::stack::MomentumVector(rootCS, {px, py, pz});
-    C8LOG_INFO(
-        "input particle: {} "
-        "input angles: theta={} phi={}"
-        "input momentum: {} GeV",
-        beamCode, theta, phi, plab.GetComponents() / 1_GeV);
-    // shoot particles from inside target out
-    Point pos(rootCS, 0_m, 0_m, 0_m);
-    stack.AddParticle(
-        std::tuple<particles::Code, units::si::HEPEnergyType,
-                   corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
-            beamCode, E0, plab, pos, 0_ns});
-  }
-
-  // define air shower object, run simulation
-  cascade::Cascade EAS(env, tracking, sequence, stack);
-
-  EAS.Run();
-
-  C8LOG_INFO("Result: E0={}GeV", E0 / 1_GeV);
-  cut.ShowResults();
-  [[maybe_unused]] const HEPEnergyType Efinal =
-      (cut.GetCutEnergy() + cut.GetInvEnergy() + cut.GetEmEnergy());
-  C8LOG_INFO("Total energy (GeV): {} relative difference (%): {}", Efinal / 1_GeV,
-             (Efinal / E0 - 1.) * 100);
-}
diff --git a/Documentation/Examples/cascade_example.cc b/Documentation/Examples/cascade_example.cc
deleted file mode 100644
index 8b9b5b8ad0b0c50830b01ec21fd7dc6163c91d20..0000000000000000000000000000000000000000
--- a/Documentation/Examples/cascade_example.cc
+++ /dev/null
@@ -1,172 +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/cascade/Cascade.h>
-#include <corsika/process/ProcessSequence.h>
-#include <corsika/process/energy_loss/EnergyLoss.h>
-#include <corsika/process/stack_inspector/StackInspector.h>
-
-#include <corsika/setup/SetupEnvironment.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/HomogeneousMedium.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/environment/ShowerAxis.h>
-
-#include <corsika/geometry/Sphere.h>
-
-#include <corsika/process/sibyll/Decay.h>
-#include <corsika/process/sibyll/Interaction.h>
-#include <corsika/process/sibyll/NuclearInteraction.h>
-
-#include <corsika/process/particle_cut/ParticleCut.h>
-#include <corsika/process/track_writer/TrackWriter.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/random/RNGManager.h>
-
-#include <corsika/utl/CorsikaFenv.h>
-#include <corsika/logging/Logging.h>
-
-#include <iostream>
-#include <limits>
-
-using namespace corsika;
-using namespace corsika::process;
-using namespace corsika::units;
-using namespace corsika::particles;
-using namespace corsika::random;
-using namespace corsika::geometry;
-using namespace corsika::environment;
-
-using namespace std;
-using namespace corsika::units::si;
-
-//
-// The example main program for a particle cascade
-//
-int main() {
-
-  logging::SetLevel(logging::level::info);
-
-  C8LOG_INFO("vertical_EAS");
-
-  std::cout << "cascade_example" << std::endl;
-
-  const LengthType height_atmosphere = 112.8_km;
-
-  feenableexcept(FE_INVALID);
-  // initialize random number sequence(s)
-  random::RNGManager::GetInstance().RegisterRandomStream("cascade");
-
-  // setup environment, geometry
-  setup::Environment env;
-  auto& universe = *(env.GetUniverse());
-
-  const CoordinateSystem& rootCS = env.GetCoordinateSystem();
-
-  auto world = setup::Environment::CreateNode<Sphere>(
-      Point{rootCS, 0_m, 0_m, 0_m}, 150_km);
-
-  using MyHomogeneousModel =
-      environment::MediumPropertyModel<environment::UniformMagneticField<
-          environment::HomogeneousMedium<setup::EnvironmentInterface>>>;
-
-  // fraction of oxygen
-  const float fox = 0.20946;
-  auto const props = world->SetModelProperties<MyHomogeneousModel>(
-      environment::Medium::AirDry1Atm, Vector(rootCS, 0_T, 0_T, 0_T),
-      1_kg / (1_m * 1_m * 1_m),
-      environment::NuclearComposition(
-          std::vector<particles::Code>{particles::Code::Nitrogen,
-                                       particles::Code::Oxygen},
-          std::vector<float>{1.f - fox, fox}));
-
-  auto innerMedium =
-      setup::Environment::CreateNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 5000_m);
-
-  innerMedium->SetModelProperties(props);
-  world->AddChild(std::move(innerMedium));
-  universe.AddChild(std::move(world));
-
-  // setup particle stack, and add primary particle
-  setup::Stack stack;
-  stack.Clear();
-  const Code beamCode = Code::Nucleus;
-  const int nuclA = 4;
-  const int nuclZ = int(nuclA / 2.15 + 0.7);
-  const HEPMassType mass = GetNucleusMass(nuclA, nuclZ);
-  const HEPEnergyType E0 = nuclA * 1_TeV;
-  double theta = 0.;
-  double phi = 0.;
-
-  Point const injectionPos(
-      rootCS, 0_m, 0_m,
-      height_atmosphere); // this is the CORSIKA 7 start of atmosphere/universe
-
-  ShowerAxis const showerAxis{injectionPos, Vector{rootCS, 0_m, 0_m, -100_km}, env};
-
-  {
-    auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) {
-      return sqrt((Elab - m) * (Elab + m));
-    };
-    HEPMomentumType P0 = elab2plab(E0, mass);
-    auto momentumComponents = [](double theta, double phi, HEPMomentumType ptot) {
-      return std::make_tuple(ptot * sin(theta) * cos(phi), ptot * sin(theta) * sin(phi),
-                             -ptot * cos(theta));
-    };
-    auto const [px, py, pz] =
-        momentumComponents(theta / 180. * M_PI, phi / 180. * M_PI, P0);
-    auto plab = corsika::stack::MomentumVector(rootCS, {px, py, pz});
-    cout << "input particle: " << beamCode << endl;
-    cout << "input angles: theta=" << theta << " phi=" << phi << endl;
-    cout << "input momentum: " << plab.GetComponents() / 1_GeV << endl;
-    stack.AddParticle(std::tuple<particles::Code, units::si::HEPEnergyType,
-                                 corsika::stack::MomentumVector, geometry::Point,
-                                 units::si::TimeType, unsigned short, unsigned short>{
-        beamCode, E0, plab, injectionPos, 0_ns, nuclA, nuclZ});
-  }
-
-  // setup processes, decays and interactions
-  setup::Tracking tracking;
-  stack_inspector::StackInspector<setup::Stack> stackInspect(1, true, E0);
-
-  random::RNGManager::GetInstance().RegisterRandomStream("sibyll");
-  random::RNGManager::GetInstance().RegisterRandomStream("pythia");
-  process::sibyll::Interaction sibyll;
-  process::sibyll::NuclearInteraction sibyllNuc(sibyll, env);
-  process::sibyll::Decay decay;
-  // cascade with only HE model ==> HE cut
-  process::particle_cut::ParticleCut cut(80_GeV, true, true);
-
-  process::track_writer::TrackWriter trackWriter("tracks.dat");
-  process::energy_loss::EnergyLoss eLoss{showerAxis, cut.GetECut()};
-
-  // assemble all processes into an ordered process list
-  auto sequence =
-      process::sequence(stackInspect, sibyll, sibyllNuc, decay, eLoss, cut, trackWriter);
-
-  // define air shower object, run simulation
-  cascade::Cascade EAS(env, tracking, sequence, stack);
-
-  EAS.Run();
-
-  eLoss.PrintProfile(); // print longitudinal profile
-
-  cut.ShowResults();
-  const HEPEnergyType Efinal =
-      cut.GetCutEnergy() + cut.GetInvEnergy() + cut.GetEmEnergy();
-  cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl
-       << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl;
-  cout << "total dEdX energy (GeV): " << eLoss.GetTotal() / 1_GeV << endl
-       << "relative difference (%): " << eLoss.GetTotal() / E0 * 100 << endl;
-  cut.Reset();
-}
diff --git a/Documentation/Examples/cascade_proton_example.cc b/Documentation/Examples/cascade_proton_example.cc
deleted file mode 100644
index b36239219320f05e006fe078404f5e0d51eef117..0000000000000000000000000000000000000000
--- a/Documentation/Examples/cascade_proton_example.cc
+++ /dev/null
@@ -1,160 +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/cascade/Cascade.h>
-#include <corsika/process/ProcessSequence.h>
-#include <corsika/process/hadronic_elastic_model/HadronicElasticModel.h>
-#include <corsika/process/stack_inspector/StackInspector.h>
-#include <corsika/process/energy_loss/EnergyLoss.h>
-
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/HomogeneousMedium.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/environment/ShowerAxis.h>
-
-#include <corsika/geometry/Sphere.h>
-
-#include <corsika/process/sibyll/Decay.h>
-#include <corsika/process/sibyll/Interaction.h>
-#include <corsika/process/sibyll/NuclearInteraction.h>
-
-#include <corsika/process/pythia/Decay.h>
-#include <corsika/process/pythia/Interaction.h>
-
-#include <corsika/process/track_writer/TrackWriter.h>
-
-#include <corsika/process/particle_cut/ParticleCut.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/random/RNGManager.h>
-
-#include <corsika/utl/CorsikaFenv.h>
-
-#include <corsika/logging/Logging.h>
-
-#include <iostream>
-#include <limits>
-#include <typeinfo>
-
-using namespace corsika;
-using namespace corsika::process;
-using namespace corsika::units;
-using namespace corsika::particles;
-using namespace corsika::random;
-using namespace corsika::geometry;
-using namespace corsika::environment;
-
-using namespace std;
-using namespace corsika::units::si;
-
-//
-// The example main program for a particle cascade
-//
-int main() {
-
-  logging::SetLevel(logging::level::info);
-
-  std::cout << "cascade_proton_example" << std::endl;
-
-  feenableexcept(FE_INVALID);
-  // initialize random number sequence(s)
-  random::RNGManager::GetInstance().RegisterRandomStream("cascade");
-
-  // setup environment, geometry
-  using EnvType = setup::Environment;
-  EnvType env;
-  auto& universe = *(env.GetUniverse());
-  const CoordinateSystem& rootCS = env.GetCoordinateSystem();
-
-  auto world = EnvType::CreateNode<Sphere>(
-      Point{rootCS, 0_m, 0_m, 0_m}, 150_km);
-
-  using MyHomogeneousModel =
-      environment::MediumPropertyModel<environment::UniformMagneticField<
-          environment::HomogeneousMedium<setup::EnvironmentInterface>>>;
-
-  world->SetModelProperties<MyHomogeneousModel>(
-      environment::Medium::AirDry1Atm, geometry::Vector(rootCS, 0_T, 0_T, 1_T),
-      1_kg / (1_m * 1_m * 1_m),
-      NuclearComposition(std::vector<particles::Code>{particles::Code::Hydrogen},
-                         std::vector<float>{(float)1.}));
-
-  universe.AddChild(std::move(world));
-
-  // setup particle stack, and add primary particle
-  setup::Stack stack;
-  stack.Clear();
-  const Code beamCode = Code::Proton;
-  const HEPMassType mass = particles::Proton::GetMass();
-  const HEPEnergyType E0 = 100_GeV;
-  double theta = 0.;
-  double phi = 0.;
-
-  Point injectionPos(rootCS, 0_m, 0_m, 0_m);
-  {
-    auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) {
-      return sqrt(Elab * Elab - m * m);
-    };
-    HEPMomentumType P0 = elab2plab(E0, mass);
-    auto momentumComponents = [](double theta, double phi, HEPMomentumType ptot) {
-      return std::make_tuple(ptot * sin(theta) * cos(phi), ptot * sin(theta) * sin(phi),
-                             -ptot * cos(theta));
-    };
-    auto const [px, py, pz] =
-        momentumComponents(theta / 180. * M_PI, phi / 180. * M_PI, P0);
-    auto plab = corsika::stack::MomentumVector(rootCS, {px, py, pz});
-    cout << "input particle: " << beamCode << endl;
-    cout << "input angles: theta=" << theta << " phi=" << phi << endl;
-    cout << "input momentum: " << plab.GetComponents() / 1_GeV << endl;
-    stack.AddParticle(
-        std::tuple<particles::Code, units::si::HEPEnergyType,
-                   corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
-            beamCode, E0, plab, injectionPos, 0_ns});
-  }
-
-  // setup processes, decays and interactions
-  setup::Tracking tracking;
-  stack_inspector::StackInspector<setup::Stack> stackInspect(1, true, E0);
-
-  random::RNGManager::GetInstance().RegisterRandomStream("sibyll");
-  random::RNGManager::GetInstance().RegisterRandomStream("pythia");
-  //  process::sibyll::Interaction sibyll(env);
-  process::pythia::Interaction pythia;
-  //  process::sibyll::NuclearInteraction sibyllNuc(env, sibyll);
-  //  process::sibyll::Decay decay;
-  process::pythia::Decay decay;
-  process::particle_cut::ParticleCut cut(60_GeV, true, true);
-
-  // random::RNGManager::GetInstance().RegisterRandomStream("HadronicElasticModel");
-  // process::HadronicElasticModel::HadronicElasticInteraction
-  // hadronicElastic(env);
-
-  process::track_writer::TrackWriter trackWriter("tracks.dat");
-  ShowerAxis const showerAxis{injectionPos, Vector{rootCS, 0_m, 0_m, -100_km}, env};
-  process::energy_loss::EnergyLoss eLoss{showerAxis, cut.GetECut()};
-
-  // assemble all processes into an ordered process list
-  // auto sequence = sibyll << decay << hadronicElastic << cut << trackWriter;
-  auto sequence = process::sequence(pythia, decay, eLoss, cut, trackWriter, stackInspect);
-
-  // define air shower object, run simulation
-  cascade::Cascade EAS(env, tracking, sequence, stack);
-
-  EAS.Run();
-
-  cout << "Result: E0=" << E0 / 1_GeV << endl;
-  cut.ShowResults();
-  const HEPEnergyType Efinal =
-      cut.GetCutEnergy() + cut.GetInvEnergy() + cut.GetEmEnergy();
-  cout << "total energy (GeV): " << Efinal / 1_GeV << endl
-       << "relative difference (%): " << (Efinal / E0 - 1.) * 100 << endl;
-}
diff --git a/Documentation/Examples/em_shower.cc b/Documentation/Examples/em_shower.cc
deleted file mode 100644
index 07c4cfb7b8dac86bb1dc389fc1c72ab896a00c75..0000000000000000000000000000000000000000
--- a/Documentation/Examples/em_shower.cc
+++ /dev/null
@@ -1,184 +0,0 @@
-/*
- * (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.
- */
-
-#include <corsika/cascade/Cascade.h>
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/LayeredSphericalAtmosphereBuilder.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/environment/ShowerAxis.h>
-#include <corsika/geometry/Plane.h>
-#include <corsika/geometry/Sphere.h>
-#include <corsika/process/ProcessSequence.h>
-#include <corsika/process/StackProcess.h>
-#include <corsika/process/longitudinal_profile/LongitudinalProfile.h>
-#include <corsika/process/observation_plane/ObservationPlane.h>
-#include <corsika/process/particle_cut/ParticleCut.h>
-#include <corsika/process/proposal/ContinuousProcess.h>
-#include <corsika/process/proposal/Interaction.h>
-#include <corsika/process/track_writer/TrackWriter.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/CorsikaFenv.h>
-#include <corsika/process/interaction_counter/InteractionCounter.hpp>
-
-#include <corsika/logging/Logging.h>
-
-#include <iomanip>
-#include <iostream>
-#include <limits>
-#include <string>
-#include <typeinfo>
-
-using namespace corsika;
-using namespace corsika::process;
-using namespace corsika::units;
-using namespace corsika::particles;
-using namespace corsika::random;
-using namespace corsika::geometry;
-using namespace corsika::environment;
-
-using namespace std;
-using namespace corsika::units::si;
-
-void registerRandomStreams() {
-  random::RNGManager::GetInstance().RegisterRandomStream("cascade");
-  random::RNGManager::GetInstance().RegisterRandomStream("proposal");
-  random::RNGManager::GetInstance().SeedAll();
-}
-
-template <typename T>
-using MyExtraEnv = environment::MediumPropertyModel<environment::UniformMagneticField<T>>;
-
-int main(int argc, char** argv) {
-
-  logging::SetLevel(logging::level::info);
-
-  if (argc != 2) {
-    std::cerr << "usage: em_shower <energy/GeV>" << std::endl;
-    return 1;
-  }
-  feenableexcept(FE_INVALID);
-  // initialize random number sequence(s)
-  registerRandomStreams();
-
-  // setup environment, geometry
-  using EnvType = setup::Environment;
-  EnvType env;
-  const CoordinateSystem& rootCS = env.GetCoordinateSystem(); 
-  Point const center{rootCS, 0_m, 0_m, 0_m};
-  auto builder = environment::make_layered_spherical_atmosphere_builder<
-      setup::EnvironmentInterface,
-      MyExtraEnv>::create(center, units::constants::EarthRadius::Mean,
-                          environment::Medium::AirDry1Atm,
-                          geometry::Vector{rootCS, 0_T, 50_uT, 0_T});
-  builder.setNuclearComposition(
-      {{particles::Code::Nitrogen, particles::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);
-  builder.assemble(env);
-
-  // setup particle stack, and add primary particle
-  setup::Stack stack;
-  stack.Clear();
-  const Code beamCode = Code::Electron;
-  auto const mass = particles::GetMass(beamCode);
-  const HEPEnergyType E0 = 1_GeV * std::stof(std::string(argv[1]));
-  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 = corsika::stack::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.norm()
-       << endl;
-
-  auto const observationHeight = 1.4_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 +
-      Vector<dimensionless_d>{rootCS, {-sin(thetaRad), 0, cos(thetaRad)}} * t;
-
-  std::cout << "point of injection: " << injectionPos.GetCoordinates() << std::endl;
-
-  stack.AddParticle(
-      std::tuple<particles::Code, units::si::HEPEnergyType,
-                 corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
-          beamCode, E0, plab, injectionPos, 0_ns});
-
-  std::cout << "shower axis length: " << (showerCore - injectionPos).norm() * 1.02
-            << std::endl;
-
-  environment::ShowerAxis const showerAxis{injectionPos,
-                                           (showerCore - injectionPos) * 1.02, env};
-
-  // setup processes, decays and interactions
-
-  // PROPOSAL processs proposal{...};
-  process::particle_cut::ParticleCut cut(10_GeV, false, true);
-  process::proposal::Interaction proposal(env, cut.GetECut());
-  process::proposal::ContinuousProcess em_continuous(env, cut.GetECut());
-  process::interaction_counter::InteractionCounter proposalCounted(proposal);
-
-  process::track_writer::TrackWriter trackWriter("tracks.dat");
-
-  // long. profile; columns for gamma, e+, e- still need to be added
-  process::longitudinal_profile::LongitudinalProfile longprof{showerAxis};
-
-  Plane const obsPlane(showerCore, Vector<dimensionless_d>(rootCS, {0., 0., 1.}));
-  process::observation_plane::ObservationPlane observationLevel(
-      obsPlane, Vector<dimensionless_d>(rootCS, {1., 0., 0.}), "particles.dat");
-
-  auto sequence = process::sequence(proposalCounted, em_continuous, longprof, cut,
-                                    observationLevel, trackWriter);
-  // define air shower object, run simulation
-  setup::Tracking tracking;
-  cascade::Cascade EAS(env, tracking, sequence, stack);
-
-  // to fix the point of first interaction, uncomment the following two lines:
-  //  EAS.SetNodes();
-  //  EAS.forceInteraction();
-
-  EAS.Run();
-
-  cut.ShowResults();
-  em_continuous.showResults();
-  observationLevel.ShowResults();
-  const HEPEnergyType Efinal = cut.GetCutEnergy() + cut.GetInvEnergy() +
-                               cut.GetEmEnergy() + em_continuous.energyLost() +
-                               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 = proposalCounted.GetHistogram();
-  hists.saveLab("inthist_lab_emShower.npz");
-  hists.saveCMS("inthist_cms_emShower.npz");
-  longprof.save("longprof_emShower.txt");
-}
diff --git a/Documentation/Examples/geometry_example.cc b/Documentation/Examples/geometry_example.cc
deleted file mode 100644
index da23d81e02baae5381358ff13b12f93d071c5dfe..0000000000000000000000000000000000000000
--- a/Documentation/Examples/geometry_example.cc
+++ /dev/null
@@ -1,83 +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/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Sphere.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <cstdlib>
-#include <iostream>
-#include <typeinfo>
-
-using namespace corsika;
-using namespace corsika::geometry;
-using namespace corsika::units::si;
-
-int main() {
-
-  std::cout << "geometry_example" << std::endl;
-
-  // define the root coordinate system
-  geometry::CoordinateSystem& root =
-      geometry::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-  // another CS defined by a translation relative to the root CS
-  CoordinateSystem cs2 = root.translate({0_m, 0_m, 1_m});
-
-  // rotations are possible, too; parameters are axis vector and angle
-  CoordinateSystem cs3 =
-      root.rotate(QuantityVector<length_d>{1_m, 0_m, 0_m}, 90 * degree_angle);
-
-  // now let's define some geometrical objects:
-  Point const p1(root, {0_m, 0_m, 0_m}); // the origin of the root CS
-  Point const p2(cs2, {0_m, 0_m, 0_m});  // the origin of cs2
-
-  Vector<length_d> const diff =
-      p2 -
-      p1; // the distance between the points, basically the translation vector given above
-  auto const norm = diff.squaredNorm(); // squared length with the right dimension
-
-  // print the components of the vector as given in the different CS
-  std::cout << "p2-p1 components in root: " << diff.GetComponents(root) << std::endl;
-  std::cout << "p2-p1 components in cs2: " << diff.GetComponents(cs2)
-            << std::endl; // by definition invariant under translations
-  std::cout << "p2-p1 components in cs3: " << diff.GetComponents(cs3)
-            << std::endl; // but not under rotations
-  std::cout << "p2-p1 norm^2: " << norm << std::endl;
-  assert(norm == 1 * meter * meter);
-
-  Sphere s(p1, 10_m); // define a sphere around a point with a radius
-  std::cout << "p1 inside s:  " << s.Contains(p2) << std::endl;
-  assert(s.Contains(p2) == 1);
-
-  Sphere s2(p1, 3_um); // another sphere
-  std::cout << "p1 inside s2: " << s2.Contains(p2) << std::endl;
-  assert(s2.Contains(p2) == 0);
-
-  // let's try parallel projections:
-  auto const v1 = Vector<length_d>(root, {1_m, 1_m, 0_m});
-  auto const v2 = Vector<length_d>(root, {1_m, 0_m, 0_m});
-
-  auto const v3 = v1.parallelProjectionOnto(v2);
-
-  // cross product
-  auto const cross =
-      v1.cross(v2).normalized(); // normalized() returns dimensionless, normalized vectors
-
-  // if a CS is not given as parameter for getComponents(), the components
-  // in the "home" CS are returned
-  std::cout << "v1: " << v1.GetComponents() << std::endl;
-  std::cout << "v2: " << v2.GetComponents() << std::endl;
-  std::cout << "parallel projection of v1 onto v2: " << v3.GetComponents() << std::endl;
-  std::cout << "normalized cross product of v1 x v2" << cross.GetComponents()
-            << std::endl;
-
-  return EXIT_SUCCESS;
-}
diff --git a/Documentation/Examples/hybrid_MC.cc b/Documentation/Examples/hybrid_MC.cc
deleted file mode 100644
index 2216e4b33b325b09bf4c209d154bf79ceda2bb66..0000000000000000000000000000000000000000
--- a/Documentation/Examples/hybrid_MC.cc
+++ /dev/null
@@ -1,280 +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.
- */
-
-/* clang-format off */
-// InteractionCounter used boost/histogram, which
-// fails if boost/type_traits have been included before. Thus, we have
-// to include it first...
-#include <corsika/process/interaction_counter/InteractionCounter.hpp>
-/* clang-format on */
-#include <corsika/cascade/Cascade.h>
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/FlatExponential.h>
-#include <corsika/environment/LayeredSphericalAtmosphereBuilder.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/environment/ShowerAxis.h>
-#include <corsika/geometry/Plane.h>
-#include <corsika/geometry/Sphere.h>
-#include <corsika/logging/Logging.h>
-#include <corsika/process/ProcessSequence.h>
-#include <corsika/process/SwitchProcessSequence.h>
-#include <corsika/process/StackProcess.h>
-#include <corsika/process/conex_source_cut/CONEXSourceCut.h>
-#include <corsika/process/energy_loss/EnergyLoss.h>
-#include <corsika/process/longitudinal_profile/LongitudinalProfile.h>
-#include <corsika/process/observation_plane/ObservationPlane.h>
-#include <corsika/process/on_shell_check/OnShellCheck.h>
-#include <corsika/process/particle_cut/ParticleCut.h>
-#include <corsika/process/pythia/Decay.h>
-#include <corsika/process/sibyll/Decay.h>
-#include <corsika/process/sibyll/Interaction.h>
-#include <corsika/process/sibyll/NuclearInteraction.h>
-#include <corsika/process/urqmd/UrQMD.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/CorsikaFenv.h>
-
-#include <iomanip>
-#include <iostream>
-#include <limits>
-#include <string>
-
-using namespace corsika;
-using namespace corsika::process;
-using namespace corsika::units;
-using namespace corsika::particles;
-using namespace corsika::random;
-using namespace corsika::geometry;
-using namespace corsika::environment;
-
-using namespace std;
-using namespace corsika::units::si;
-
-void registerRandomStreams(const int seed) {
-  random::RNGManager::GetInstance().RegisterRandomStream("cascade");
-  random::RNGManager::GetInstance().RegisterRandomStream("qgsjet");
-  random::RNGManager::GetInstance().RegisterRandomStream("sibyll");
-  random::RNGManager::GetInstance().RegisterRandomStream("pythia");
-  random::RNGManager::GetInstance().RegisterRandomStream("urqmd");
-  random::RNGManager::GetInstance().RegisterRandomStream("proposal");
-
-  if (seed == 0)
-    random::RNGManager::GetInstance().SeedAll();
-  else
-    random::RNGManager::GetInstance().SeedAll(seed);
-}
-
-template <typename T>
-using MyExtraEnv = environment::MediumPropertyModel<environment::UniformMagneticField<T>>;
-
-int main(int argc, char** argv) {
-
-  logging::SetLevel(logging::level::info);
-
-  C8LOG_INFO("hybrid_MC");
-
-  if (argc < 4) {
-    std::cerr << "usage: hybrid_MC <A> <Z> <energy/GeV> [seed]" << std::endl;
-    std::cerr << "       if no seed is given, a random seed is chosen" << std::endl;
-    return 1;
-  }
-  feenableexcept(FE_INVALID);
-
-  int seed = 0;
-  if (argc > 4) seed = std::stoi(std::string(argv[4]));
-  // initialize random number sequence(s)
-  registerRandomStreams(seed);
-
-  // setup environment, geometry
-  using EnvType = setup::Environment;
-  EnvType env;
-  const CoordinateSystem& rootCS = env.GetCoordinateSystem();
-  Point const center{rootCS, 0_m, 0_m, 0_m};
-  auto builder = environment::make_layered_spherical_atmosphere_builder<
-      setup::EnvironmentInterface,
-      MyExtraEnv>::create(center, units::constants::EarthRadius::Mean,
-                          environment::Medium::AirDry1Atm,
-                          geometry::Vector{rootCS, 0_T, 50_uT, 0_T});
-  builder.setNuclearComposition(
-      {{particles::Code::Nitrogen, particles::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);
-  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 = particles::GetNucleusMass(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 = corsika::stack::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.norm()
-       << endl;
-
-  auto const observationHeight = 0_km + builder.getEarthRadius();
-  auto const injectionHeight = 112.75_km + builder.getEarthRadius();
-  auto const t = -observationHeight * cos(thetaRad) +
-                 sqrt(-units::static_pow<2>(sin(thetaRad) * observationHeight) +
-                      units::static_pow<2>(injectionHeight));
-  Point const showerCore{rootCS, 0_m, 0_m, observationHeight};
-  Point const injectionPos =
-      showerCore +
-      Vector<dimensionless_d>{rootCS, {-sin(thetaRad), 0, cos(thetaRad)}} * t;
-
-  std::cout << "point of injection: " << injectionPos.GetCoordinates() << std::endl;
-
-  if (A != 1) {
-    stack.AddParticle(std::tuple<particles::Code, units::si::HEPEnergyType,
-                                 corsika::stack::MomentumVector, geometry::Point,
-                                 units::si::TimeType, unsigned short, unsigned short>{
-        beamCode, E0, plab, injectionPos, 0_ns, A, Z});
-
-  } else {
-    stack.AddParticle(
-        std::tuple<particles::Code, units::si::HEPEnergyType,
-                   corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
-            particles::Code::Proton, E0, plab, injectionPos, 0_ns});
-  }
-
-  std::cout << "shower axis length: " << (showerCore - injectionPos).norm() * 1.02
-            << std::endl;
-
-  environment::ShowerAxis const showerAxis{injectionPos,
-                                           (showerCore - injectionPos) * 1.02, env};
-
-  // setup processes, decays and interactions
-
-  process::sibyll::Interaction sibyll;
-  process::interaction_counter::InteractionCounter sibyllCounted(sibyll);
-
-  process::sibyll::NuclearInteraction sibyllNuc(sibyll, env);
-  process::interaction_counter::InteractionCounter sibyllNucCounted(sibyllNuc);
-
-  process::pythia::Decay decayPythia;
-
-  // use sibyll decay routine for decays of particles unknown to pythia
-  process::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();
-
-  process::particle_cut::ParticleCut cut{60_GeV, false, true};
-  process::energy_loss::EnergyLoss eLoss{showerAxis, cut.GetECut()};
-
-  corsika::process::conex_source_cut::CONEXSourceCut conex(
-      center, showerAxis, t, injectionHeight, E0,
-      particles::GetPDG(particles::Code::Proton));
-
-  process::on_shell_check::OnShellCheck reset_particle_mass(1.e-3, 1.e-1, false);
-
-  process::longitudinal_profile::LongitudinalProfile longprof{showerAxis};
-
-  Plane const obsPlane(showerCore, Vector<dimensionless_d>(rootCS, {0., 0., 1.}));
-  process::observation_plane::ObservationPlane observationLevel(
-      obsPlane, Vector<dimensionless_d>(rootCS, {1., 0., 0.}), "particles.dat");
-
-  process::UrQMD::UrQMD urqmd;
-  process::interaction_counter::InteractionCounter urqmdCounted{urqmd};
-
-  // assemble all processes into an ordered process list
-  struct EnergySwitch {
-    HEPEnergyType cutE_;
-    EnergySwitch(HEPEnergyType cutE)
-        : cutE_(cutE) {}
-    process::SwitchResult operator()(const setup::Stack::ParticleType& p) {
-      if (p.GetEnergy() < cutE_)
-        return process::SwitchResult::First;
-      else
-        return process::SwitchResult::Second;
-    }
-  };
-  auto hadronSequence =
-      process::select(urqmdCounted, process::sequence(sibyllNucCounted, sibyllCounted),
-                      EnergySwitch(55_GeV));
-  auto decaySequence = process::sequence(decayPythia, decaySibyll);
-  auto sequence = process::sequence(hadronSequence, reset_particle_mass, decaySequence,
-                                    eLoss, cut, conex, longprof, observationLevel);
-
-  // define air shower object, run simulation
-  setup::Tracking tracking;
-  cascade::Cascade EAS(env, tracking, sequence, stack);
-
-  // to fix the point of first interaction, uncomment the following two lines:
-  //  EAS.SetNodes();
-  //  EAS.forceInteraction();
-
-  EAS.Run();
-
-  cut.ShowResults();
-  eLoss.showResults();
-  observationLevel.ShowResults();
-  const HEPEnergyType Efinal = cut.GetCutEnergy() + cut.GetInvEnergy() +
-                               cut.GetEmEnergy() + eLoss.energyLost() +
-                               observationLevel.GetEnergyGround();
-  cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl
-       << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl;
-  observationLevel.Reset();
-  cut.Reset();
-  eLoss.reset();
-
-  conex.SolveCE();
-
-  auto const hists = sibyllCounted.GetHistogram() + sibyllNucCounted.GetHistogram() +
-                     urqmdCounted.GetHistogram();
-
-  hists.saveLab("inthist_lab.txt");
-  hists.saveCMS("inthist_cms.txt");
-
-  hists.saveLab("inthist_lab.txt");
-  hists.saveCMS("inthist_cms.txt");
-
-  longprof.save("longprof.txt");
-
-  std::ofstream finish("finished");
-  finish << "run completed without error" << std::endl;
-}
diff --git a/Documentation/Examples/particle_list_example.cc b/Documentation/Examples/particle_list_example.cc
deleted file mode 100644
index 342c08e7ff6e7cceec63f2aa3583f5fb46199297..0000000000000000000000000000000000000000
--- a/Documentation/Examples/particle_list_example.cc
+++ /dev/null
@@ -1,65 +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/particles/ParticleProperties.h>
-#include <corsika/process/ProcessSequence.h>
-#include <corsika/process/qgsjetII/ParticleConversion.h>
-#include <corsika/process/sibyll/ParticleConversion.h>
-#include <corsika/setup/SetupEnvironment.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <iomanip>
-#include <iostream>
-#include <string>
-using namespace corsika::units;
-using namespace corsika::units::si;
-using namespace corsika::particles;
-using namespace std;
-
-//
-// The example main program for a particle list
-//
-int main() {
-
-  std::cout << "particle_list_example" << std::endl;
-
-  cout << "------------------------------------------"
-       << "particles in CORSIKA"
-       << "------------------------------------------" << endl;
-  cout << std::setw(20) << "Name"
-       << " | " << std::setw(10) << "PDG-id"
-       << " | " << std::setw(10) << "SIBYLL-id"
-       << " | " << std::setw(10) << "QGSJETII-id"
-       << " | " << std::setw(18) << "PDG-mass (GeV)"
-       << " | " << std::setw(18) << "SIBYLL-mass (GeV)"
-       << " | " << endl;
-  cout << std::setw(104) << std::setfill('-') << "-" << endl;
-  for (auto p : getAllParticles()) {
-    if (!IsNucleus(p)) {
-      corsika::process::sibyll::SibyllCode sib_id =
-          corsika::process::sibyll::ConvertToSibyll(p);
-      auto const sib_mass =
-          (sib_id != corsika::process::sibyll::SibyllCode::Unknown
-               ? to_string(corsika::process::sibyll::GetSibyllMass(p) / 1_GeV)
-               : "--");
-      auto const qgs_id = corsika::process::qgsjetII::ConvertToQgsjetII(p);
-      cout << std::setw(20) << std::setfill(' ') << p << " | " << std::setw(10)
-           << static_cast<int>(GetPDG(p)) << " | " << std::setw(10)
-           << (sib_id != corsika::process::sibyll::SibyllCode::Unknown
-                   ? to_string(static_cast<int>(sib_id))
-                   : "--")
-           << " | " << std::setw(10)
-           << (qgs_id != corsika::process::qgsjetII::QgsjetIICode::Unknown
-                   ? to_string(static_cast<int>(qgs_id))
-                   : "--")
-           << " | " << std::setw(18) << std::setprecision(5) << GetMass(p) / 1_GeV
-           << " | " << std::setw(18) << std::setprecision(5) << sib_mass << " | " << endl;
-    }
-  }
-  cout << std::setw(104) << std::setfill('-') << "-" << endl;
-}
diff --git a/Documentation/Examples/stack_example.cc b/Documentation/Examples/stack_example.cc
deleted file mode 100644
index cc638dbe8e622f27db71210a109784a0f4de7eff..0000000000000000000000000000000000000000
--- a/Documentation/Examples/stack_example.cc
+++ /dev/null
@@ -1,59 +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/particles/ParticleProperties.h>
-#include <corsika/stack/super_stupid/SuperStupidStack.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-
-#include <cassert>
-#include <iomanip>
-#include <iostream>
-
-using namespace corsika;
-using namespace corsika::units::si;
-using namespace corsika::stack;
-using namespace corsika::geometry;
-using namespace std;
-
-void fill(corsika::stack::super_stupid::SuperStupidStack& s) {
-  const geometry::CoordinateSystem& rootCS =
-      geometry::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-  for (int i = 0; i < 11; ++i) {
-    s.AddParticle(
-        std::tuple<particles::Code, units::si::HEPEnergyType,
-                   corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
-            particles::Code::Electron, 1.5_GeV * i,
-            corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 1_GeV}),
-            geometry::Point(rootCS, 0_m, 0_m, 0_m), 0_ns});
-  }
-}
-
-void read(corsika::stack::super_stupid::SuperStupidStack& s) {
-  assert(s.getEntries() == 11); // stack has 11 particles
-
-  HEPEnergyType total_energy;
-  int i = 0;
-  for (auto& p : s) {
-    total_energy += p.GetEnergy();
-    // particles are electrons with 1.5 GeV energy times i
-    assert(p.GetPID() == particles::Code::Electron);
-    assert(p.GetEnergy() == 1.5_GeV * (i++));
-  }
-}
-
-int main() {
-
-  std::cout << "stack_example" << std::endl;
-
-  corsika::stack::super_stupid::SuperStupidStack s;
-  fill(s);
-  read(s);
-  return 0;
-}
diff --git a/Documentation/Examples/vertical_EAS.cc b/Documentation/Examples/vertical_EAS.cc
deleted file mode 100644
index 6fbed4a73ecfd8e99db28d11754037e094033dc7..0000000000000000000000000000000000000000
--- a/Documentation/Examples/vertical_EAS.cc
+++ /dev/null
@@ -1,290 +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.
- */
-
-/* clang-format off */
-// InteractionCounter used boost/histogram, which
-// fails if boost/type_traits have been included before. Thus, we have
-// to include it first...
-#include <corsika/process/interaction_counter/InteractionCounter.hpp>
-/* clang-format on */
-#include <corsika/cascade/Cascade.h>
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/FlatExponential.h>
-#include <corsika/environment/HomogeneousMedium.h>
-#include <corsika/environment/IMagneticFieldModel.h>
-#include <corsika/environment/LayeredSphericalAtmosphereBuilder.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/environment/ShowerAxis.h>
-#include <corsika/environment/SlidingPlanarExponential.h>
-#include <corsika/environment/UniformMagneticField.h>
-#include <corsika/geometry/Plane.h>
-#include <corsika/geometry/Sphere.h>
-#include <corsika/logging/Logging.h>
-#include <corsika/process/ProcessSequence.h>
-#include <corsika/process/SwitchProcessSequence.h>
-#include <corsika/process/StackProcess.h>
-#include <corsika/process/energy_loss/EnergyLoss.h>
-#include <corsika/process/longitudinal_profile/LongitudinalProfile.h>
-#include <corsika/process/observation_plane/ObservationPlane.h>
-#include <corsika/process/on_shell_check/OnShellCheck.h>
-#include <corsika/process/particle_cut/ParticleCut.h>
-#include <corsika/process/track_writer/TrackWriter.h>
-#include <corsika/process/proposal/ContinuousProcess.h>
-#include <corsika/process/proposal/Interaction.h>
-#include <corsika/process/pythia/Decay.h>
-#include <corsika/process/sibyll/Decay.h>
-#include <corsika/process/stack_inspector/StackInspector.h>
-#include <corsika/process/sibyll/Interaction.h>
-#include <corsika/process/sibyll/NuclearInteraction.h>
-#include <corsika/process/urqmd/UrQMD.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/CorsikaFenv.h>
-
-#include <iomanip>
-#include <iostream>
-#include <limits>
-#include <string>
-
-using namespace corsika;
-using namespace corsika::process;
-using namespace corsika::units;
-using namespace corsika::particles;
-using namespace corsika::random;
-using namespace corsika::geometry;
-using namespace corsika::environment;
-
-using namespace std;
-using namespace corsika::units::si;
-
-using Particle = setup::Stack::StackIterator;
-
-void registerRandomStreams(const int seed) {
-  random::RNGManager::GetInstance().RegisterRandomStream("cascade");
-  random::RNGManager::GetInstance().RegisterRandomStream("qgsjet");
-  random::RNGManager::GetInstance().RegisterRandomStream("sibyll");
-  random::RNGManager::GetInstance().RegisterRandomStream("pythia");
-  random::RNGManager::GetInstance().RegisterRandomStream("urqmd");
-  random::RNGManager::GetInstance().RegisterRandomStream("proposal");
-
-  if (seed == 0)
-    random::RNGManager::GetInstance().SeedAll();
-  else
-    random::RNGManager::GetInstance().SeedAll(seed);
-}
-
-template <typename T>
-using MyExtraEnv = environment::MediumPropertyModel<environment::UniformMagneticField<T>>;
-
-int main(int argc, char** argv) {
-
-  logging::SetLevel(logging::level::info);
-
-  C8LOG_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;
-    return 1;
-  }
-  feenableexcept(FE_INVALID);
-
-  int seed = 0;
-  if (argc > 4) seed = std::stoi(std::string(argv[4]));
-  // initialize random number sequence(s)
-  registerRandomStreams(seed);
-
-  // setup environment, geometry
-  using EnvType = setup::Environment;
-  EnvType env;
-  const CoordinateSystem& rootCS = env.GetCoordinateSystem();
-  Point const center{rootCS, 0_m, 0_m, 0_m};
-  auto builder = environment::make_layered_spherical_atmosphere_builder<
-      setup::EnvironmentInterface,
-      MyExtraEnv>::create(center, units::constants::EarthRadius::Mean,
-                          environment::Medium::AirDry1Atm,
-                          geometry::Vector{rootCS, 0_T, 50_uT, 0_T});
-  builder.setNuclearComposition(
-      {{particles::Code::Nitrogen, particles::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);
-  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 = particles::GetNucleusMass(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 = corsika::stack::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.norm()
-       << endl;
-
-  auto const observationHeight = 0_km + builder.getEarthRadius();
-  auto const injectionHeight = 112.75_km + builder.getEarthRadius();
-  auto const t = -observationHeight * cos(thetaRad) +
-                 sqrt(-units::static_pow<2>(sin(thetaRad) * observationHeight) +
-                      units::static_pow<2>(injectionHeight));
-
-  Point const showerCore{rootCS, 0_m, 0_m, observationHeight};
-  Point const injectionPos =
-      showerCore +
-      Vector<dimensionless_d>{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::tuple<particles::Code, units::si::HEPEnergyType,
-                                   corsika::stack::MomentumVector, geometry::Point,
-                                   units::si::TimeType>{particles::Code::Proton, E0, plab,
-                                                        injectionPos, 0_ns});
-    } else if (Z == 0) {
-      stack.AddParticle(std::tuple<particles::Code, units::si::HEPEnergyType,
-                                   corsika::stack::MomentumVector, geometry::Point,
-                                   units::si::TimeType>{particles::Code::Neutron, E0,
-                                                        plab, injectionPos, 0_ns});
-    } else {
-      std::cerr << "illegal parameters" << std::endl;
-      return EXIT_FAILURE;
-    }
-  }
-
-  // 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).norm() * 1.5
-            << std::endl;
-
-  environment::ShowerAxis const showerAxis{injectionPos,
-                                           (showerCore - injectionPos) * 1.5, env};
-
-  // setup processes, decays and interactions
-
-  process::particle_cut::ParticleCut cut{60_GeV, false, true};
-  process::proposal::Interaction proposal(env, cut.GetECut());
-  process::proposal::ContinuousProcess em_continuous(env, cut.GetECut());
-  process::interaction_counter::InteractionCounter proposalCounted(proposal);
-
-  process::sibyll::Interaction sibyll;
-  process::interaction_counter::InteractionCounter sibyllCounted(sibyll);
-
-  process::sibyll::NuclearInteraction sibyllNuc(sibyll, env);
-  process::interaction_counter::InteractionCounter sibyllNucCounted(sibyllNuc);
-
-  process::pythia::Decay decayPythia;
-
-  // use sibyll decay routine for decays of particles unknown to pythia
-  process::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();
-
-  process::on_shell_check::OnShellCheck reset_particle_mass(1.e-3, 1.e-1, false);
-
-  process::track_writer::TrackWriter trackWriter("tracks.dat");
-  process::longitudinal_profile::LongitudinalProfile longprof{showerAxis};
-
-  Plane const obsPlane(showerCore, Vector<dimensionless_d>(rootCS, {0., 0., 1.}));
-  process::observation_plane::ObservationPlane observationLevel(
-      obsPlane, Vector<dimensionless_d>(rootCS, {1., 0., 0.}), "particles.dat");
-
-  process::UrQMD::UrQMD urqmd;
-  process::interaction_counter::InteractionCounter urqmdCounted{urqmd};
-
-  // assemble all processes into an ordered process list
-  struct EnergySwitch {
-    HEPEnergyType cutE_;
-    EnergySwitch(HEPEnergyType cutE)
-        : cutE_(cutE) {}
-    process::SwitchResult operator()(const Particle& p) {
-      if (p.GetEnergy() < cutE_)
-        return process::SwitchResult::First;
-      else
-        return process::SwitchResult::Second;
-    }
-  };
-  auto hadronSequence =
-      process::select(urqmdCounted, process::sequence(sibyllNucCounted, sibyllCounted),
-                      EnergySwitch(55_GeV));
-  auto decaySequence = process::sequence(decayPythia, decaySibyll);
-  stack_inspector::StackInspector<setup::Stack> stackInspect(1000, false, E0);
-  auto sequence = process::sequence(stackInspect, hadronSequence, reset_particle_mass,
-                                    decaySequence, proposalCounted, em_continuous, cut,
-                                    trackWriter, observationLevel, longprof);
-
-  // define air shower object, run simulation
-  setup::Tracking tracking;
-  cascade::Cascade EAS(env, tracking, sequence, 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.energyLost() +
-                               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();
-
-  hists.saveLab("inthist_lab_verticalEAS.npz");
-  hists.saveCMS("inthist_cms_verticalEAS.npz");
-  longprof.save("longprof_verticalEAS.txt");
-}
diff --git a/Environment/BaseExponential.h b/Environment/BaseExponential.h
deleted file mode 100644
index bf8c16d961c4cfdeb28827851dd8e20e8040c17e..0000000000000000000000000000000000000000
--- a/Environment/BaseExponential.h
+++ /dev/null
@@ -1,111 +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/geometry/Line.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <limits>
-
-namespace corsika::environment {
-
-  /**
-   * This class provides the grammage/length conversion functionality for
-   * (locally) flat exponential atmospheres.
-   */
-  template <class TDerived>
-  class BaseExponential {
-  protected:
-    units::si::MassDensityType const fRho0;
-    units::si::LengthType const fLambda;
-    units::si::InverseLengthType const fInvLambda;
-    geometry::Point const fP0;
-
-    auto const& GetImplementation() const { return *static_cast<TDerived const*>(this); }
-
-    // clang-format off
-    /**
-     * For a (normalized) axis \f$ \vec{a} \f$, the grammage along a non-orthogonal line with (normalized)
-     * direction \f$ \vec{u} \f$ is given by
-     * \f[
-     *   X = \frac{\varrho_0 \lambda}{\vec{u} \cdot \vec{a}} \left( \exp\left( \vec{u} \cdot \vec{a} \frac{l}{\lambda} \right) - 1 \right)
-     * \f], where \f$ \varrho_0 \f$ is the density at the starting point.
-     * 
-     * If \f$ \vec{u} \cdot \vec{a} = 0 \f$, the calculation is just like with a homogeneous density:
-     * \f[
-     *   X = \varrho_0 l;
-     * \f]
-     */
-    // clang-format on
-    units::si::GrammageType IntegratedGrammage(
-        setup::Trajectory const& vLine, units::si::LengthType vL,
-        geometry::Vector<units::si::dimensionless_d> const& vAxis) const {
-      if (vL == units::si::LengthType::zero()) { return units::si::GrammageType::zero(); }
-
-      auto const uDotA = vLine.GetDirection(0).dot(vAxis).magnitude();
-      auto const rhoStart = GetImplementation().GetMassDensity(vLine.GetLine().GetR0());
-
-      if (uDotA == 0) {
-        return vL * rhoStart;
-      } else {
-        return rhoStart * (fLambda / uDotA) * (exp(uDotA * vL * fInvLambda) - 1);
-      }
-    }
-
-    // clang-format off
-    /**
-     * For a (normalized) axis \f$ \vec{a} \f$, the length of a non-orthogonal line with (normalized)
-     * direction \f$ \vec{u} \f$ corresponding to grammage \f$ X \f$ is given by
-     * \f[
-     *   l = \begin{cases}
-     *   \frac{\lambda}{\vec{u} \cdot \vec{a}} \log\left(Y \right), & \text{if} Y :=  0 > 1 +
-     *     \vec{u} \cdot \vec{a} \frac{X}{\rho_0 \lambda} 
-     *   \infty & \text{else,}
-     *   \end{cases}
-     * \f] where \f$ \varrho_0 \f$ is the density at the starting point.
-     * 
-     * If \f$ \vec{u} \cdot \vec{a} = 0 \f$, the calculation is just like with a homogeneous density:
-     * \f[
-     *   l =  \frac{X}{\varrho_0}
-     * \f]
-     */
-    // clang-format on
-    units::si::LengthType ArclengthFromGrammage(
-        setup::Trajectory const& vLine, units::si::GrammageType vGrammage,
-        geometry::Vector<units::si::dimensionless_d> const& vAxis) const {
-      auto const uDotA = vLine.GetDirection(0).dot(vAxis).magnitude();
-      auto const rhoStart = GetImplementation().GetMassDensity(vLine.GetLine().GetR0());
-
-      if (uDotA == 0) {
-        return vGrammage / rhoStart;
-      } else {
-        auto const logArg = vGrammage * fInvLambda * uDotA / rhoStart + 1;
-        if (logArg > 0) {
-          return fLambda / uDotA * log(logArg);
-        } else {
-          return std::numeric_limits<typename decltype(
-                     vGrammage)::value_type>::infinity() *
-                 units::si::meter;
-        }
-      }
-    }
-
-  public:
-    BaseExponential(geometry::Point const& vP0, units::si::MassDensityType vRho,
-                    units::si::LengthType vLambda)
-        : fRho0(vRho)
-        , fLambda(vLambda)
-        , fInvLambda(1 / vLambda)
-        , fP0(vP0) {}
-  };
-
-} // namespace corsika::environment
diff --git a/Environment/CMakeLists.txt b/Environment/CMakeLists.txt
deleted file mode 100644
index 3231ab4a8a643b682a50723e9b64dc3837e1c761..0000000000000000000000000000000000000000
--- a/Environment/CMakeLists.txt
+++ /dev/null
@@ -1,116 +0,0 @@
-add_custom_command (
-  OUTPUT  ${PROJECT_BINARY_DIR}/Environment/GeneratedMediaProperties.inc
-  COMMAND ${PROJECT_SOURCE_DIR}/Environment/readProperties.py 
-          ${PROJECT_SOURCE_DIR}/Environment/properties8.dat
-  DEPENDS readProperties.py
-          properties8.dat
-  WORKING_DIRECTORY
-          ${PROJECT_BINARY_DIR}/Environment
-  COMMENT "Read NIST properties8 data file and produce C++ source code GeneratedMediaProperties.inc"
-  VERBATIM
-  )
-
-
-set (
-  ENVIRONMENT_SOURCES
-  ShowerAxis.cc
-)
-
-set (
-  ENVIRONMENT_HEADERS
-  VolumeTreeNode.h
-  IEmpty.hpp
-  IMediumModel.h
-  NuclearComposition.h
-  HomogeneousMedium.h
-  InhomogeneousMedium.h
-  HomogeneousMedium.h
-  LinearApproximationIntegrator.h
-  DensityFunction.h
-  Environment.h
-  NameModel.h
-  BaseExponential.h
-  FlatExponential.h
-  SlidingPlanarExponential.h
-  LayeredSphericalAtmosphereBuilder.h
-  ShowerAxis.h
-  IMagneticFieldModel.h
-  UniformMagneticField.h
-  NoMagneticField.h
-  IRefractiveIndexModel.h
-  UniformRefractiveIndex.h
-  IMediumPropertyModel.h
-  MediumProperties.h
-  MediumPropertyModel.h
-  ${PROJECT_BINARY_DIR}/Environment/GeneratedMediaProperties.inc # this one is auto-generated
-  )
-
-set (
-  ENVIRONMENT_NAMESPACE
-  corsika/environment
-  )
-
-add_library (CORSIKAenvironment STATIC ${ENVIRONMENT_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAenvironment ${ENVIRONMENT_NAMESPACE} ${ENVIRONMENT_HEADERS})
-
-set_target_properties (
-  CORSIKAenvironment
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  PUBLIC_HEADER "${ENVIRONMENT_HEADERS}"
-  )
-
-# ....................................................
-# since GeneratedMediaProperties.inc is an automatically produced file in the build directory,
-# create a symbolic link into the source tree, so that it can be found and edited more easily
-# this is not needed for the build to succeed! .......
-add_custom_command (
-  OUTPUT  ${CMAKE_CURRENT_SOURCE_DIR}/GeneratedParticleProperties.inc
-  COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_BINARY_DIR}/include/corsika/environment/GeneratedMediaProperties.inc ${CMAKE_CURRENT_SOURCE_DIR}/GeneratedMediaProperties.inc
-  COMMENT "Generate link in source-dir: ${CMAKE_CURRENT_SOURCE_DIR}/GeneratedMediaProperties.inc"
-  )
-add_custom_target (SourceDirLinkMedia DEPENDS ${PROJECT_BINARY_DIR}/Environment/GeneratedMediaProperties.inc)
-add_dependencies (CORSIKAenvironment SourceDirLinkMedia)
-# .....................................................
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  CORSIKAenvironment
-  CORSIKAgeometry
-  CORSIKAparticles
-  CORSIKAunits
-  CORSIKAlogging
-  )
-
-target_include_directories (
-  CORSIKAenvironment
-  PUBLIC
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include>
-  )
-
-install (
-  TARGETS CORSIKAenvironment
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-  PUBLIC_HEADER DESTINATION include/${ENVIRONMENT_NAMESPACE}
-  )
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST(testEnvironment)
-target_link_libraries (
-  testEnvironment
-  CORSIKAsetup
-  CORSIKAenvironment
-  CORSIKAtesting
-  )
-
-CORSIKA_ADD_TEST(testShowerAxis)
-target_link_libraries (
-  testShowerAxis
-  CORSIKAsetup
-  CORSIKAenvironment
-  CORSIKAtesting
-  )
diff --git a/Environment/Environment.h b/Environment/Environment.h
deleted file mode 100644
index 1f807de45bc698718c74f93cbc7021c8329936b2..0000000000000000000000000000000000000000
--- a/Environment/Environment.h
+++ /dev/null
@@ -1,61 +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/environment/VolumeTreeNode.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Sphere.h>
-#include <limits>
-
-namespace corsika::environment {
-  struct Universe : public corsika::geometry::Sphere {
-    Universe(corsika::geometry::CoordinateSystem const& pCS)
-        : corsika::geometry::Sphere(
-              corsika::geometry::Point{pCS, 0 * corsika::units::si::meter,
-                                       0 * corsika::units::si::meter,
-                                       0 * corsika::units::si::meter},
-              corsika::units::si::meter * std::numeric_limits<double>::infinity()) {}
-
-    bool Contains(corsika::geometry::Point const&) const override { return true; }
-  };
-
-  template <typename IEnvironmentModel>
-  class Environment {
-  public:
-    using BaseNodeType = VolumeTreeNode<IEnvironmentModel>;
-
-    Environment()
-        : fCoordinateSystem{corsika::geometry::RootCoordinateSystem::GetInstance()
-                                .GetRootCoordinateSystem()}
-        , fUniverse(std::make_unique<BaseNodeType>(
-              std::make_unique<Universe>(fCoordinateSystem))) {}
-
-    auto& GetUniverse() { return fUniverse; }
-    auto const& GetUniverse() const { return fUniverse; }
-
-    auto const& GetCoordinateSystem() const { return fCoordinateSystem; }
-
-    // factory method for creation of VolumeTreeNodes
-    template <class TVolumeType, typename... TVolumeArgs>
-    static auto CreateNode(TVolumeArgs&&... args) {
-      static_assert(std::is_base_of_v<corsika::geometry::Volume, TVolumeType>,
-                    "unusable type provided, needs to be derived from "
-                    "\"corsika::geometry::Volume\"");
-
-      return std::make_unique<BaseNodeType>(
-          std::make_unique<TVolumeType>(std::forward<TVolumeArgs>(args)...));
-    }
-
-  private:
-    corsika::geometry::CoordinateSystem const& fCoordinateSystem;
-    typename BaseNodeType::VTNUPtr fUniverse;
-  };
-
-} // namespace corsika::environment
diff --git a/Environment/FlatExponential.h b/Environment/FlatExponential.h
deleted file mode 100644
index 8aeafbce4a776c1ae1dd4b335c3578962a21e215..0000000000000000000000000000000000000000
--- a/Environment/FlatExponential.h
+++ /dev/null
@@ -1,66 +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/environment/BaseExponential.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/geometry/Line.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/setup/SetupTrajectory.h>
-
-namespace corsika::environment {
-
-  //clang-format off
-  /**
-   * flat exponential density distribution with
-   * \f[
-   *  \varrho(r) = \varrho_0 \exp\left( \frac{1}{\lambda} (r - p) \cdot
-   *    \vec{a} \right).
-   * \f]
-   * \f$ \vec{a} \f$ denotes the axis and should be normalized to avoid degeneracy
-   * with the scale parameter \f$ \lambda \f$.
-   */
-  //clang-format on
-  template <class T>
-  class FlatExponential : public BaseExponential<FlatExponential<T>>, public T {
-    geometry::Vector<units::si::dimensionless_d> const fAxis;
-    NuclearComposition const fNuclComp;
-
-    using Base = BaseExponential<FlatExponential<T>>;
-
-  public:
-    FlatExponential(geometry::Point const& vP0,
-                    geometry::Vector<units::si::dimensionless_d> const& vAxis,
-                    units::si::MassDensityType vRho, units::si::LengthType vLambda,
-                    NuclearComposition vNuclComp)
-        : Base(vP0, vRho, vLambda)
-        , fAxis(vAxis)
-        , fNuclComp(vNuclComp) {}
-
-    units::si::MassDensityType GetMassDensity(geometry::Point const& vP) const override {
-      return Base::fRho0 * exp(Base::fInvLambda * (vP - Base::fP0).dot(fAxis));
-    }
-
-    NuclearComposition const& GetNuclearComposition() const override { return fNuclComp; }
-
-    units::si::GrammageType IntegratedGrammage(setup::Trajectory const& vLine,
-                                               units::si::LengthType vTo) const override {
-      return Base::IntegratedGrammage(vLine, vTo, fAxis);
-    }
-
-    units::si::LengthType ArclengthFromGrammage(
-        setup::Trajectory const& vLine,
-        units::si::GrammageType vGrammage) const override {
-      return Base::ArclengthFromGrammage(vLine, vGrammage, fAxis);
-    }
-  };
-} // namespace corsika::environment
diff --git a/Environment/HomogeneousMedium.h b/Environment/HomogeneousMedium.h
deleted file mode 100644
index f39cc70610c7e9fdca38172e51f1d9c8179f494b..0000000000000000000000000000000000000000
--- a/Environment/HomogeneousMedium.h
+++ /dev/null
@@ -1,60 +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/environment/NuclearComposition.h>
-#include <corsika/geometry/Line.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/Trajectory.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <cassert>
-
-/**
- * a homogeneous medium
- */
-
-namespace corsika::environment {
-
-  template <class T>
-  class HomogeneousMedium : public T {
-    corsika::units::si::MassDensityType const fDensity;
-    NuclearComposition const fNuclComp;
-
-  public:
-    HomogeneousMedium(corsika::units::si::MassDensityType pDensity,
-                      NuclearComposition pNuclComp)
-        : fDensity(pDensity)
-        , fNuclComp(pNuclComp) {}
-
-    corsika::units::si::MassDensityType GetMassDensity(
-        corsika::geometry::Point const&) const override {
-      return fDensity;
-    }
-    NuclearComposition const& GetNuclearComposition() const override { return fNuclComp; }
-
-    corsika::units::si::GrammageType IntegratedGrammage(
-        corsika::setup::Trajectory const&,
-        corsika::units::si::LengthType pTo) const override {
-      using namespace corsika::units::si;
-      return pTo * fDensity;
-    }
-
-    corsika::units::si::LengthType ArclengthFromGrammage(
-        corsika::setup::Trajectory const&,
-        corsika::units::si::GrammageType pGrammage) const override {
-      return pGrammage / fDensity;
-    }
-  };
-
-} // namespace corsika::environment
diff --git a/Environment/IMediumModel.h b/Environment/IMediumModel.h
deleted file mode 100644
index 7a3b951711b8fe19f928eed3c5cf3d21a17e73d6..0000000000000000000000000000000000000000
--- a/Environment/IMediumModel.h
+++ /dev/null
@@ -1,40 +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/environment/NuclearComposition.h>
-#include <corsika/geometry/Line.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/setup/SetupTrajectory.h>
-
-namespace corsika::environment {
-
-  class IMediumModel {
-  public:
-    virtual ~IMediumModel() = default; // LCOV_EXCL_LINE
-
-    virtual corsika::units::si::MassDensityType GetMassDensity(
-        corsika::geometry::Point const&) const = 0;
-
-    // todo: think about the mixin inheritance of the trajectory vs the BaseTrajectory
-    // approach; for now, only lines are supported
-    virtual corsika::units::si::GrammageType IntegratedGrammage(
-        corsika::setup::Trajectory const&,
-        corsika::units::si::LengthType) const = 0;
-
-    virtual corsika::units::si::LengthType ArclengthFromGrammage(
-        corsika::setup::Trajectory const&,
-        corsika::units::si::GrammageType) const = 0;
-
-    virtual NuclearComposition const& GetNuclearComposition() const = 0;
-  };
-
-} // namespace corsika::environment
diff --git a/Environment/InhomogeneousMedium.h b/Environment/InhomogeneousMedium.h
deleted file mode 100644
index 76378d5d5202527b54d32811ec688f00807b0382..0000000000000000000000000000000000000000
--- a/Environment/InhomogeneousMedium.h
+++ /dev/null
@@ -1,56 +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/environment/NuclearComposition.h>
-#include <corsika/geometry/Line.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/Trajectory.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/units/PhysicalUnits.h>
-
-/**
- * A general inhomogeneous medium. The mass density distribution TDensityFunction must be
- * a \f$C^2\f$-function.
- */
-
-namespace corsika::environment {
-
-  template <class T, class TDensityFunction>
-  class InhomogeneousMedium : public T {
-    NuclearComposition const fNuclComp;
-    TDensityFunction const fDensityFunction;
-
-  public:
-    template <typename... Args>
-    InhomogeneousMedium(NuclearComposition pNuclComp, Args&&... rhoArgs)
-        : fNuclComp(pNuclComp)
-        , fDensityFunction(rhoArgs...) {}
-
-    corsika::units::si::MassDensityType GetMassDensity(
-        corsika::geometry::Point const& p) const override {
-      return fDensityFunction.EvaluateAt(p);
-    }
-    NuclearComposition const& GetNuclearComposition() const override { return fNuclComp; }
-
-    corsika::units::si::GrammageType IntegratedGrammage(
-        corsika::setup::Trajectory const& pLine,
-        corsika::units::si::LengthType pTo) const override {
-      return fDensityFunction.IntegrateGrammage(pLine, pTo);
-    }
-
-    corsika::units::si::LengthType ArclengthFromGrammage(
-        corsika::setup::Trajectory const& pLine,
-        corsika::units::si::GrammageType pGrammage) const override {
-      return fDensityFunction.ArclengthFromGrammage(pLine, pGrammage);
-    }
-  };
-
-} // namespace corsika::environment
diff --git a/Environment/LayeredSphericalAtmosphereBuilder.h b/Environment/LayeredSphericalAtmosphereBuilder.h
deleted file mode 100644
index 096f9b181cb9596da1b93a844792a3b07cae9ece..0000000000000000000000000000000000000000
--- a/Environment/LayeredSphericalAtmosphereBuilder.h
+++ /dev/null
@@ -1,219 +0,0 @@
-/*
- * (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.
- */
-
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/FlatExponential.h>
-#include <corsika/environment/HomogeneousMedium.h>
-#include <corsika/environment/IMediumModel.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/environment/SlidingPlanarExponential.h>
-#include <corsika/environment/VolumeTreeNode.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/units/PhysicalConstants.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <functional>
-#include <memory>
-#include <stack>
-#include <tuple>
-#include <type_traits>
-
-namespace corsika::environment {
-
-  // forward-decl
-  template <typename TMediumInterface, template <typename> typename MExtraEnvirnoment>
-  struct make_layered_spherical_atmosphere_builder;
-
-  /**
-   * Helper class to setup concentric spheres of layered atmosphere
-   * with spcified density profiles (exponential, linear, ...).
-   *
-   * This can be used most importantly to replicate CORSIKA7
-   * atmospheres.
-   *
-   * Each layer by definition has a density profile and a (constant)
-   * nuclear composition model.
-   *
-   */
-
-  namespace detail {
-
-    struct NoExtraModelInner {};
-
-    template <typename M>
-    struct NoExtraModel {};
-
-    template <template <typename> typename M>
-    struct has_extra_models : std::true_type {};
-
-    template <>
-    struct has_extra_models<NoExtraModel> : std::false_type {};
-
-  } // namespace detail
-
-  template <typename TMediumInterface = environment::IMediumModel,
-            template <typename> typename TMediumModelExtra = detail::NoExtraModel,
-            typename... TModelArgs>
-  class LayeredSphericalAtmosphereBuilder {
-    std::unique_ptr<NuclearComposition> composition_;
-    geometry::Point center_;
-    units::si::LengthType previousRadius_{units::si::LengthType::zero()};
-    units::si::LengthType earthRadius_;
-    std::tuple<TModelArgs...> const additionalModelArgs_;
-
-    std::stack<typename VolumeTreeNode<TMediumInterface>::VTNUPtr>
-        layers_; // innermost layer first
-
-    void checkRadius(units::si::LengthType r) const {
-      if (r <= previousRadius_) {
-        throw std::runtime_error("radius must be greater than previous");
-      }
-    }
-
-    LayeredSphericalAtmosphereBuilder() = delete;
-    LayeredSphericalAtmosphereBuilder(const LayeredSphericalAtmosphereBuilder&) = delete;
-    LayeredSphericalAtmosphereBuilder(const LayeredSphericalAtmosphereBuilder&&) = delete;
-    LayeredSphericalAtmosphereBuilder& operator=(
-        const LayeredSphericalAtmosphereBuilder&) = delete;
-
-    // friend, to allow construction
-    template <typename, template <typename> typename>
-    friend struct make_layered_spherical_atmosphere_builder;
-
-  protected:
-    LayeredSphericalAtmosphereBuilder(TModelArgs... args, corsika::geometry::Point center,
-                                      units::si::LengthType earthRadius)
-        : center_(center)
-        , earthRadius_(earthRadius)
-        , additionalModelArgs_{args...} {}
-
-  public:
-    void setNuclearComposition(NuclearComposition composition) {
-      composition_ = std::make_unique<NuclearComposition>(composition);
-    }
-
-    void addExponentialLayer(units::si::GrammageType b, units::si::LengthType c,
-                             units::si::LengthType upperBoundary) {
-      using namespace units::si;
-
-      auto const radius = earthRadius_ + upperBoundary;
-      checkRadius(radius);
-      previousRadius_ = radius;
-
-      auto node = std::make_unique<VolumeTreeNode<TMediumInterface>>(
-          std::make_unique<geometry::Sphere>(center_, radius));
-
-      auto const rho0 = b / c;
-      std::cout << "rho0 = " << rho0 << ", c = " << c << std::endl;
-
-      if constexpr (detail::has_extra_models<TMediumModelExtra>::value) {
-        // helper lambda in which the last 5 arguments to make_shared<...> are bound
-        auto lastBound = [&](auto... argPack) {
-          return std::make_shared<
-              TMediumModelExtra<environment::SlidingPlanarExponential<TMediumInterface>>>(
-              argPack..., center_, rho0, -c, *composition_, earthRadius_);
-        };
-
-        // now unpack the additional arguments
-        auto model = std::apply(lastBound, additionalModelArgs_);
-        node->SetModelProperties(std::move(model));
-      } else {
-        node->template SetModelProperties<SlidingPlanarExponential<TMediumInterface>>(
-            center_, rho0, -c, *composition_, earthRadius_);
-      }
-
-      layers_.push(std::move(node));
-    }
-
-    void addLinearLayer(units::si::LengthType c, units::si::LengthType upperBoundary) {
-      using namespace units::si;
-
-      auto const radius = earthRadius_ + upperBoundary;
-      checkRadius(radius);
-      previousRadius_ = radius;
-
-      auto node = std::make_unique<VolumeTreeNode<TMediumInterface>>(
-          std::make_unique<geometry::Sphere>(center_, radius));
-
-      units::si::GrammageType constexpr b = 1 * 1_g / (1_cm * 1_cm);
-      auto const rho0 = b / c;
-
-      std::cout << "rho0 = " << rho0;
-
-      if constexpr (detail::has_extra_models<TMediumModelExtra>::value) {
-        // helper lambda in which the last 2 arguments to make_shared<...> are bound
-        auto lastBound = [&](auto... argPack) {
-          return std::make_shared<
-              TMediumModelExtra<environment::HomogeneousMedium<TMediumInterface>>>(
-              argPack..., rho0, *composition_);
-        };
-
-        // now unpack the additional arguments
-        auto model = std::apply(lastBound, additionalModelArgs_);
-
-        node->SetModelProperties(std::move(model));
-      } else {
-        node->template SetModelProperties<
-            environment::HomogeneousMedium<TMediumInterface>>(rho0, *composition_);
-      }
-
-      layers_.push(std::move(node));
-    }
-
-    int size() const { return layers_.size(); }
-
-    void assemble(Environment<TMediumInterface>& env) {
-      auto& universe = env.GetUniverse();
-      auto* outmost = universe.get();
-
-      while (!layers_.empty()) {
-        auto l = std::move(layers_.top());
-        auto* tmp = l.get();
-        outmost->AddChild(std::move(l));
-        layers_.pop();
-        outmost = tmp;
-      }
-    }
-
-    Environment<TMediumInterface> assemble() {
-      Environment<TMediumInterface> env;
-      assemble(env);
-      return env;
-    }
-
-    /**
-     * Get the current Earth radius.
-     */
-    units::si::LengthType getEarthRadius() const { return earthRadius_; }
-
-  }; // end class LayeredSphericalAtmosphereBuilder
-
-  /**
-   * \class make_layered_spherical_atmosphere_builder
-   *
-   * Helper class to create LayeredSphericalAtmosphereBuilder, the
-   * extra environment models have to be passed as template-template
-   * argument to make_layered_spherical_atmosphere_builder, the member
-   * function `create` does then take an unspecified number of extra
-   * parameters to internalize those models for all layers later
-   * produced.
-   **/
-  template <typename TMediumInterface = environment::IMediumModel,
-            template <typename> typename MExtraEnvirnoment = detail::NoExtraModel>
-  struct make_layered_spherical_atmosphere_builder {
-    template <typename... TArgs>
-    static auto create(geometry::Point const& center, units::si::LengthType earthRadius,
-                       TArgs... args) {
-      return environment::LayeredSphericalAtmosphereBuilder<TMediumInterface,
-                                                            MExtraEnvirnoment, TArgs...>{
-          std::forward<TArgs>(args)..., center, earthRadius};
-    }
-  };
-
-} // namespace corsika::environment
diff --git a/Environment/LinearApproximationIntegrator.h b/Environment/LinearApproximationIntegrator.h
deleted file mode 100644
index 3a6c37ccb6c7b92227093322a3024a864907fe0e..0000000000000000000000000000000000000000
--- a/Environment/LinearApproximationIntegrator.h
+++ /dev/null
@@ -1,50 +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 <limits>
-
-#include <corsika/geometry/Line.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-namespace corsika::environment {
-  template <class TDerived>
-  class LinearApproximationIntegrator {
-    auto const& GetImplementation() const { return *static_cast<TDerived const*>(this); }
-
-  public:
-    auto IntegrateGrammage(corsika::setup::Trajectory const& line,
-                           corsika::units::si::LengthType length) const {
-      auto const c0 = GetImplementation().EvaluateAt(line.GetPosition(0));
-      auto const c1 = GetImplementation().fRho.FirstDerivative(line.GetPosition(0),
-                                                               line.GetDirection(0));
-      return (c0 + 0.5 * c1 * length) * length;
-    }
-
-    auto ArclengthFromGrammage(corsika::setup::Trajectory const& line,
-                               corsika::units::si::GrammageType grammage) const {
-      auto const c0 = GetImplementation().fRho(line.GetPosition(0));
-      auto const c1 = GetImplementation().fRho.FirstDerivative(line.GetPosition(0),
-                                                               line.GetDirection(0));
-
-      return (1 - 0.5 * grammage * c1 / (c0 * c0)) * grammage / c0;
-    }
-
-    auto MaximumLength(corsika::setup::Trajectory const& line,
-                       [[maybe_unused]] double relError) const {
-      using namespace corsika::units::si;
-      [[maybe_unused]] auto const c1 = GetImplementation().fRho.SecondDerivative(
-          line.GetPosition(0), line.GetDirection(0));
-
-      // todo: provide a real, working implementation
-      return 1_m * std::numeric_limits<double>::infinity();
-    }
-  };
-} // namespace corsika::environment
diff --git a/Environment/MediumPropertyModel.h b/Environment/MediumPropertyModel.h
deleted file mode 100644
index bf05be4dcae04c5d56f1291e1e8caa886480a82b..0000000000000000000000000000000000000000
--- a/Environment/MediumPropertyModel.h
+++ /dev/null
@@ -1,55 +0,0 @@
-/*
- * (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 <corsika/environment/IMediumPropertyModel.h>
-
-namespace corsika::environment {
-
-  /**
-   * A uniform refractive index.
-   *
-   * This class returns the same refractive index
-   * for all evaluated locations.
-   *
-   */
-  template <typename T>
-  class MediumPropertyModel : public T {
-
-    Medium medium_; ///< The medium code
-
-  public:
-    /**
-     * Construct a MediumPropertyModel
-     *
-     * This is initialized with a fixed refractive index
-     * and returns this refractive index at all locations.
-     *
-     * @param field    The refractive index to return everywhere.
-     */
-    template <typename... Args>
-    MediumPropertyModel(const Medium medium, Args&&... args)
-        : T(std::forward<Args>(args)...)
-        , medium_(medium) {}
-
-    /**
-     * Evaluate the medium type at a given location.
-     *
-     * @param  point    The location to evaluate at.
-     * @returns    The medium type as enum environment::Medium
-     */
-    Medium medium(corsika::geometry::Point const&) const final override {
-      return medium_;
-    }
-
-    void set_medium(Medium v) { medium_ = v; }
-
-  }; // END: class MediumPropertyModel
-
-} // namespace corsika::environment
diff --git a/Environment/MediumTypes.h b/Environment/MediumTypes.h
deleted file mode 100644
index bc44560fd2251927d10f0c2c65d55c60d656d2f6..0000000000000000000000000000000000000000
--- a/Environment/MediumTypes.h
+++ /dev/null
@@ -1,22 +0,0 @@
-/*
- * (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
-
-namespace corsika::environment {
-
-  /**
-   * Medium types are useful most importantly for effective models
-   * like energy losses. a particular medium (mixture of components)
-   * may have specif properties not reflected by its mixture of
-   * components.
-   */
-
-  enum class EMediumType { eUnknown, eAir, eWater, eIce, eRock };
-
-} // namespace corsika::environment
diff --git a/Environment/NoMagneticField.h b/Environment/NoMagneticField.h
deleted file mode 100644
index 74cbd8cfc51edac6db9bd05269f33ddb37aa23b9..0000000000000000000000000000000000000000
--- a/Environment/NoMagneticField.h
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * (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 <corsika/environment/IMagneticFieldModel.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-
-namespace corsika::environment {
-
-  /**
-   * A uniform (constant) magnetic field.
-   *
-   * This class returns the same magnetic field vector
-   * for all evaluated locations.
-   *
-   */
-  template <typename T>
-  class NoMagneticField : public T {
-
-    // a type-alias for a magnetic field vector
-    using MagneticFieldVector =
-        corsika::geometry::Vector<corsika::units::si::magnetic_flux_density_d>;
-
-  public:
-    /**
-     * Construct a NoMagneticField.
-     *
-     * This is initialized with a fixed magnetic field
-     * and returns this magnetic field at all locations.
-     *
-     * @param field    The fixed magnetic field to return.
-     */
-    template <typename... Args>
-    NoMagneticField(Args&&... args)
-        : T(std::forward<Args>(args)...) {}
-
-    /**
-     * Evaluate the magnetic field at a given location.
-     *
-     * @param  point    The location to evaluate the field at.
-     * @returns    The magnetic field vector.
-     */
-    MagneticFieldVector GetMagneticField(
-        corsika::geometry::Point const&) const final override {
-      CoordinateSystem const& gCS =
-          RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-      return MagneticFieldVector(gCS, {0_T, 0_T, 0_T});
-    }
-
-  }; // END: class MagneticField
-
-} // namespace corsika::environment
diff --git a/Environment/NuclearComposition.h b/Environment/NuclearComposition.h
deleted file mode 100644
index 42b1beca0af1791e057183de49efe4d2710bd192..0000000000000000000000000000000000000000
--- a/Environment/NuclearComposition.h
+++ /dev/null
@@ -1,148 +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/particles/ParticleProperties.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <cassert>
-#include <functional>
-#include <numeric>
-#include <random>
-#include <stdexcept>
-#include <vector>
-
-namespace corsika::environment {
-  class NuclearComposition {
-    std::vector<float> const fNumberFractions; //!< relative fractions of number density
-    std::vector<corsika::particles::Code> const
-        fComponents; //!< particle codes of consitutents
-
-    double const fAvgMassNumber;
-
-    std::size_t hash_;
-
-    template <class AConstIterator, class BConstIterator>
-    class WeightProviderIterator {
-      AConstIterator fAIter;
-      BConstIterator fBIter;
-
-    public:
-      using value_type = double;
-      using iterator_category = std::input_iterator_tag;
-      using pointer = value_type*;
-      using reference = value_type&;
-      using difference_type = ptrdiff_t;
-
-      WeightProviderIterator(AConstIterator a, BConstIterator b)
-          : fAIter(a)
-          , fBIter(b) {}
-
-      value_type operator*() const { return ((*fAIter) * (*fBIter)).magnitude(); }
-
-      WeightProviderIterator& operator++() { // prefix ++
-        ++fAIter;
-        ++fBIter;
-        return *this;
-      }
-
-      auto operator==(WeightProviderIterator other) { return fAIter == other.fAIter; }
-
-      auto operator!=(WeightProviderIterator other) { return !(*this == other); }
-    };
-
-  public:
-    NuclearComposition(std::vector<corsika::particles::Code> pComponents,
-                       std::vector<float> pFractions)
-        : fNumberFractions(pFractions)
-        , fComponents(pComponents)
-        , fAvgMassNumber(std::inner_product(
-              pComponents.cbegin(), pComponents.cend(), pFractions.cbegin(), 0.,
-              std::plus<double>(), [](auto const compID, auto const fraction) -> double {
-                if (particles::IsNucleus(compID)) {
-                  return particles::GetNucleusA(compID) * fraction;
-                } else {
-                  return particles::GetMass(compID) /
-                         units::si::ConvertSIToHEP(units::constants::u) * fraction;
-                }
-              })) {
-      assert(pComponents.size() == pFractions.size());
-      auto const sumFractions =
-          std::accumulate(pFractions.cbegin(), pFractions.cend(), 0.f);
-
-      if (!(0.999f < sumFractions && sumFractions < 1.001f)) {
-        throw std::runtime_error("element fractions do not add up to 1");
-      }
-      updateHash();
-    }
-
-    template <typename TFunction>
-    auto WeightedSum(TFunction func) const {
-      using ResultQuantity = decltype(func(*fComponents.cbegin()));
-
-      auto const prod = [&](auto const compID, auto const fraction) {
-        return func(compID) * fraction;
-      };
-
-      if constexpr (phys::units::is_quantity_v<ResultQuantity>) {
-        return std::inner_product(
-            fComponents.cbegin(), fComponents.cend(), fNumberFractions.cbegin(),
-            ResultQuantity::zero(), // .zero() is defined for quantity types only
-            std::plus<ResultQuantity>(), prod);
-      } else {
-        return std::inner_product(
-            fComponents.cbegin(), fComponents.cend(), fNumberFractions.cbegin(),
-            ResultQuantity(0), // in other cases we have to use a bare 0
-            std::plus<ResultQuantity>(), prod);
-      }
-    }
-
-    auto size() const { return fNumberFractions.size(); }
-
-    auto const& GetFractions() const { return fNumberFractions; }
-    auto const& GetComponents() const { return fComponents; }
-    auto const GetAverageMassNumber() const { return fAvgMassNumber; }
-
-    template <class TRNG>
-    corsika::particles::Code SampleTarget(
-        std::vector<corsika::units::si::CrossSectionType> const& sigma,
-        TRNG& randomStream) const {
-      using namespace corsika::units::si;
-
-      assert(sigma.size() == fNumberFractions.size());
-
-      std::discrete_distribution channelDist(
-          WeightProviderIterator<decltype(fNumberFractions.begin()),
-                                 decltype(sigma.begin())>(fNumberFractions.begin(),
-                                                          sigma.begin()),
-          WeightProviderIterator<decltype(fNumberFractions.begin()),
-                                 decltype(sigma.end())>(fNumberFractions.end(),
-                                                        sigma.end()));
-
-      auto const iChannel = channelDist(randomStream);
-      return fComponents[iChannel];
-    }
-
-    // Note: when this class ever modifies its internal data, the hash
-    // must be updated, too!
-    size_t hash() const { return hash_; }
-
-  private:
-    void updateHash() {
-      std::vector<std::size_t> hashes;
-      for (float ifrac : GetFractions()) hashes.push_back(std::hash<float>{}(ifrac));
-      for (corsika::particles::Code icode : GetComponents())
-        hashes.push_back(std::hash<int>{}(static_cast<int>(icode)));
-      std::size_t h = std::hash<double>{}(GetAverageMassNumber());
-      for (std::size_t ih : hashes) h = h ^ (ih << 1);
-      hash_ = h;
-    }
-  };
-
-} // namespace corsika::environment
diff --git a/Environment/ShowerAxis.cc b/Environment/ShowerAxis.cc
deleted file mode 100644
index 76fdc66724ba57850dd404732f0b7940984176fe..0000000000000000000000000000000000000000
--- a/Environment/ShowerAxis.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * (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.
- */
-
-#include <corsika/environment/ShowerAxis.h>
-#include <corsika/logging/Logging.h>
-
-#include <string>
-
-using namespace corsika::environment;
-using namespace corsika::units::si;
-using namespace corsika;
-
-GrammageType ShowerAxis::X(LengthType l) const {
-  double const fractionalBin = l / steplength_;
-  int const lower = fractionalBin; // indices of nearest X support points
-  double const frac = fractionalBin - lower;
-  unsigned int const upper = lower + 1;
-
-  if (fractionalBin < 0) {
-    C8LOG_ERROR("cannot extrapolate to points behind point of injection l={} m", l / 1_m);
-    if (throw_) {
-      throw std::runtime_error("cannot extrapolate to points behind point of injection");
-    }
-    return minimumX();
-  }
-
-  if (upper >= X_.size()) {
-    const std::string err =
-        fmt::format("shower axis too short, cannot extrapolate (l / max_length_ = {} )",
-                    l / max_length_);
-    C8LOG_ERROR(err);
-    if (throw_) { throw std::runtime_error(err.c_str()); }
-    return maximumX();
-  }
-
-  C8LOG_TRACE("showerAxis::X frac={}, fractionalBin={}, lower={}, upper={}", frac,
-              fractionalBin, lower, upper);
-  assert(0 <= frac && frac <= 1.);
-
-  C8LOG_TRACE("ShowerAxis::X l={} m, lower={}, frac={}, upper={}", l / 1_m, lower, frac,
-              upper);
-
-  // linear interpolation between X[lower] and X[upper]
-  return X_[upper] * frac + X_[lower] * (1 - frac);
-}
-
-LengthType ShowerAxis::steplength() const { return steplength_; }
-
-GrammageType ShowerAxis::maximumX() const { return *X_.rbegin(); }
-
-GrammageType ShowerAxis::minimumX() const { return GrammageType::zero(); }
-
-GrammageType ShowerAxis::projectedX(geometry::Point const& p) const {
-  auto const projectedLength = (p - pointStart_).dot(axis_normalized_);
-  return X(projectedLength);
-}
-
-geometry::Vector<units::si::dimensionless_d> const& ShowerAxis::GetDirection() const {
-  return axis_normalized_;
-}
-
-geometry::Point const& ShowerAxis::GetStart() const { return pointStart_; }
diff --git a/Environment/ShowerAxis.h b/Environment/ShowerAxis.h
deleted file mode 100644
index 052aa0bff6ac286039b4fbe580b4093b55d3e396..0000000000000000000000000000000000000000
--- a/Environment/ShowerAxis.h
+++ /dev/null
@@ -1,121 +0,0 @@
-/*
- * (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 <corsika/environment/Environment.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <cassert>
-#include <cstdlib>
-#include <fstream>
-#include <functional>
-#include <iterator>
-#include <memory>
-#include <stdexcept>
-#include <vector>
-
-#include <iostream>
-
-#include <boost/math/quadrature/gauss_kronrod.hpp>
-
-namespace corsika::environment {
-
-  /**
-   * \class ShowerAxis
-   *
-   * The environment::ShowerAxis is created from a geometry::Point and
-   * a geometry::Vector and inside an Environment. It internally uses
-   * a table with steps=10000 (default) rows for interpolation.
-   *
-   * The shower axis can convert location in the shower into a
-   * projected grammage along the shower axis.
-   *
-   **/
-
-  class ShowerAxis {
-  public:
-    template <typename TEnvModel>
-    ShowerAxis(geometry::Point const& pStart,
-               geometry::Vector<units::si::length_d> length,
-               environment::Environment<TEnvModel> const& env, bool doThrow = false,
-               int steps = 10'000)
-        : pointStart_(pStart)
-        , length_(length)
-        , throw_(doThrow)
-        , max_length_(length_.norm())
-        , steplength_(max_length_ / steps)
-        , axis_normalized_(length / max_length_)
-        , X_(steps + 1) {
-      auto const* const universe = env.GetUniverse().get();
-
-      auto rho = [pStart, length, universe](double x) {
-        auto const p = pStart + length * x;
-        auto const* node = universe->GetContainingNode(p);
-        return node->GetModelProperties().GetMassDensity(p).magnitude();
-      };
-
-      double error;
-      int k = 0;
-      X_[0] = units::si::GrammageType::zero();
-      auto sum = units::si::GrammageType::zero();
-
-      for (int i = 1; i <= steps; ++i) {
-        auto const x_prev = (i - 1.) / steps;
-        auto const d_prev = max_length_ * x_prev;
-        auto const x = double(i) / steps;
-        auto const r = boost::math::quadrature::gauss_kronrod<double, 15>::integrate(
-            rho, x_prev, x, 15, 1e-9, &error);
-        auto const result =
-            units::si::MassDensityType(phys::units::detail::magnitude_tag, r) *
-            max_length_;
-
-        sum += result;
-        X_[i] = sum;
-
-        for (; sum > k * X_binning_; ++k) {
-          d_.emplace_back(d_prev + k * X_binning_ * steplength_ / result);
-        }
-      }
-
-      assert(std::is_sorted(X_.cbegin(), X_.cend()));
-      assert(std::is_sorted(d_.cbegin(), d_.cend()));
-    }
-
-    units::si::LengthType steplength() const;
-
-    units::si::GrammageType maximumX() const;
-
-    units::si::GrammageType minimumX() const;
-
-    units::si::GrammageType projectedX(geometry::Point const& p) const;
-
-    units::si::GrammageType X(units::si::LengthType) const;
-
-    geometry::Vector<units::si::dimensionless_d> const& GetDirection() const;
-
-    geometry::Point const& GetStart() const;
-
-  private:
-    geometry::Point const pointStart_;
-    geometry::Vector<units::si::length_d> const length_;
-    bool throw_ = false;
-    units::si::LengthType const max_length_, steplength_;
-    geometry::Vector<units::si::dimensionless_d> const axis_normalized_;
-    std::vector<units::si::GrammageType> X_;
-
-    // for storing the lengths corresponding to equidistant X values
-    units::si::GrammageType const X_binning_ = std::invoke([]() {
-      using namespace units::si;
-      return 1_g / 1_cm / 1_cm;
-    });
-    std::vector<units::si::LengthType> d_;
-  };
-} // namespace corsika::environment
diff --git a/Environment/SlidingPlanarExponential.h b/Environment/SlidingPlanarExponential.h
deleted file mode 100644
index aae3ccef18ebd1abfafe48afe60963a3ed5da6f1..0000000000000000000000000000000000000000
--- a/Environment/SlidingPlanarExponential.h
+++ /dev/null
@@ -1,72 +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/environment/FlatExponential.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/geometry/Line.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/Trajectory.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::environment {
-
-  // clang-format off
-  /**
-   * The SlidingPlanarExponential models mass density as
-   * \f[
-   *   \varrho(r) = \varrho_0 \exp\left( \frac{|p_0 - r|}{\lambda} \right).
-   * \f]
-   * For grammage/length conversion, the density distribution is approximated as
-   * locally flat at the starting point \f$ r_0 \f$ of the trajectory with the axis pointing
-   * from \f$ p_0 \f$ to \f$ r_0 \f$.
-   */
-   //clang-format on
-   
-  template <class T>
-  class SlidingPlanarExponential : public BaseExponential<SlidingPlanarExponential<T>>,
-                                   public T {
-    NuclearComposition const nuclComp_;
-    units::si::LengthType const referenceHeight_;
-
-    using Base = BaseExponential<SlidingPlanarExponential<T>>;
-
-  public:
-    SlidingPlanarExponential(geometry::Point const& p0, units::si::MassDensityType rho0,
-                             units::si::LengthType lambda, NuclearComposition nuclComp, units::si::LengthType referenceHeight = units::si::LengthType::zero())
-        : Base(p0, rho0, lambda)
-        , nuclComp_(nuclComp),
-        referenceHeight_(referenceHeight) {}
-
-    units::si::MassDensityType GetMassDensity(
-        geometry::Point const& p) const override {
-      auto const height = (p - Base::fP0).norm() - referenceHeight_;
-      return Base::fRho0 * exp(Base::fInvLambda * height);
-    }
-
-    NuclearComposition const& GetNuclearComposition() const override { return nuclComp_; }
-
-    units::si::GrammageType IntegratedGrammage(
-        setup::Trajectory const& line,
-        units::si::LengthType l) const override {
-      auto const axis = (line.GetLine().GetR0() - Base::fP0).normalized();
-      return Base::IntegratedGrammage(line, l, axis);
-    }
-
-    units::si::LengthType ArclengthFromGrammage(
-        setup::Trajectory const& line,
-        units::si::GrammageType grammage) const override {
-      auto const axis = (line.GetLine().GetR0() - Base::fP0).normalized();
-      return Base::ArclengthFromGrammage(line, grammage, axis);
-    }
-  };
-
-} // namespace corsika::environment
diff --git a/Environment/VolumeTreeNode.h b/Environment/VolumeTreeNode.h
deleted file mode 100644
index ad3306810d4efccbee8905d5d281785e6180880d..0000000000000000000000000000000000000000
--- a/Environment/VolumeTreeNode.h
+++ /dev/null
@@ -1,134 +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/environment/IEmpty.hpp>
-#include <corsika/geometry/Volume.h>
-#include <memory>
-#include <vector>
-
-namespace corsika::environment {
-
-  template <typename TModelProperties = IEmpty>
-  class VolumeTreeNode {
-  public:
-    using IModelProperties = TModelProperties;
-    using VTN_type = VolumeTreeNode<IModelProperties>;
-    using VTNUPtr = std::unique_ptr<VolumeTreeNode<IModelProperties>>;
-    using IMPSharedPtr = std::shared_ptr<IModelProperties>;
-    using VolUPtr = std::unique_ptr<corsika::geometry::Volume>;
-
-    VolumeTreeNode(VolUPtr pVolume = nullptr)
-        : fGeoVolume(std::move(pVolume)) {}
-
-    //! convenience function equivalent to Volume::Contains
-    bool Contains(corsika::geometry::Point const& p) const {
-      return fGeoVolume->Contains(p);
-    }
-
-    VolumeTreeNode<IModelProperties> const* Excludes(
-        corsika::geometry::Point const& p) const {
-      auto exclContainsIter =
-          std::find_if(fExcludedNodes.cbegin(), fExcludedNodes.cend(),
-                       [&](auto const& s) { return bool(s->Contains(p)); });
-
-      return exclContainsIter != fExcludedNodes.cend() ? *exclContainsIter : nullptr;
-    }
-
-    /** 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.
-     */
-    VolumeTreeNode<IModelProperties> const* GetContainingNode(
-        corsika::geometry::Point const& p) const {
-      if (!Contains(p)) { return nullptr; }
-
-      if (auto const childContainsIter =
-              std::find_if(fChildNodes.cbegin(), fChildNodes.cend(),
-                           [&](auto const& s) { return bool(s->Contains(p)); });
-          childContainsIter == fChildNodes.cend()) // not contained in any of the children
-      {
-        if (auto const exclContainsIter = Excludes(p)) // contained in any excluded nodes
-        {
-          return exclContainsIter->GetContainingNode(p);
-        } else {
-          return this;
-        }
-      } else {
-        return (*childContainsIter)->GetContainingNode(p);
-      }
-    }
-
-    /**
-     * Traverses the VolumeTree pre- or post-order and calls the functor  \p func for each
-     * node. \p func takes a reference to VolumeTreeNode as argument. The return value \p
-     * func is ignored.
-     */
-    template <typename TCallable, bool preorder = true>
-    void walk(TCallable func) {
-      if constexpr (preorder) { func(*this); }
-
-      std::for_each(fChildNodes.begin(), fChildNodes.end(),
-                    [&](auto& v) { v->walk(func); });
-
-      if constexpr (!preorder) { func(*this); };
-    }
-
-    void AddChild(VTNUPtr pChild) {
-      pChild->fParentNode = this;
-      fChildNodes.push_back(std::move(pChild));
-      // It is a bad idea to return an iterator to the inserted element
-      // because it might get invalidated when the vector needs to grow
-      // later and the caller won't notice.
-    }
-
-    void ExcludeOverlapWith(VTNUPtr const& pNode) {
-      fExcludedNodes.push_back(pNode.get());
-    }
-
-    const VTN_type* GetParent() const { return fParentNode; };
-
-    auto const& GetChildNodes() const { return fChildNodes; }
-
-    auto const& GetExcludedNodes() const { return fExcludedNodes; }
-
-    auto const& GetVolume() const { return *fGeoVolume; }
-
-    auto const& GetModelProperties() const { return *fModelProperties; }
-
-    bool HasModelProperties() const { return fModelProperties.get() != nullptr; }
-
-    template <typename ModelProperties, typename... Args>
-    auto SetModelProperties(Args&&... args) {
-      static_assert(std::is_base_of_v<IModelProperties, ModelProperties>,
-                    "unusable model properties type provided");
-
-      fModelProperties = std::make_shared<ModelProperties>(std::forward<Args>(args)...);
-      return fModelProperties;
-    }
-
-    void SetModelProperties(IMPSharedPtr ptr) { fModelProperties = ptr; }
-
-    /*
-    template <class MediumType, typename... Args>
-    static auto CreateMedium(Args&&... args) {
-      static_assert(std::is_base_of_v<, MediumType>,
-                    "unusable type provided, needs to be derived from \"IMediumModel\"");
-
-      return std::make_shared<MediumType>(std::forward<Args>(args)...);
-    }
-    */
-  private:
-    std::vector<VTNUPtr> fChildNodes;
-    std::vector<VolumeTreeNode<IModelProperties> const*> fExcludedNodes;
-    VolumeTreeNode<IModelProperties> const* fParentNode = nullptr;
-    VolUPtr fGeoVolume;
-    IMPSharedPtr fModelProperties;
-  };
-
-} // namespace corsika::environment
diff --git a/Environment/testEnvironment.cc b/Environment/testEnvironment.cc
deleted file mode 100644
index eecae62928a54b4876a164389c5d84626c93cef0..0000000000000000000000000000000000000000
--- a/Environment/testEnvironment.cc
+++ /dev/null
@@ -1,478 +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/environment/DensityFunction.h>
-#include <corsika/environment/FlatExponential.h>
-#include <corsika/environment/HomogeneousMedium.h>
-#include <corsika/environment/IMagneticFieldModel.h>
-#include <corsika/environment/IMediumModel.h>
-#include <corsika/environment/IMediumPropertyModel.h>
-#include <corsika/environment/IRefractiveIndexModel.h>
-#include <corsika/environment/InhomogeneousMedium.h>
-#include <corsika/environment/LayeredSphericalAtmosphereBuilder.h>
-#include <corsika/environment/LinearApproximationIntegrator.h>
-#include <corsika/environment/MediumPropertyModel.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/environment/SlidingPlanarExponential.h>
-#include <corsika/environment/UniformMagneticField.h>
-#include <corsika/environment/UniformRefractiveIndex.h>
-#include <corsika/environment/VolumeTreeNode.h>
-#include <corsika/geometry/Line.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <catch2/catch.hpp>
-
-using namespace corsika::geometry;
-using namespace corsika::environment;
-using namespace corsika::particles;
-using namespace corsika::units::si;
-using namespace corsika;
-
-CoordinateSystem const& gCS =
-    RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-Point const gOrigin(gCS, {0_m, 0_m, 0_m});
-
-TEST_CASE("HomogeneousMedium") {
-  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
-                                             std::vector<float>{1.f});
-  HomogeneousMedium<IMediumModel> const medium(19.2_g / cube(1_cm), protonComposition);
-}
-
-TEST_CASE("FlatExponential") {
-  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
-                                             std::vector<float>{1.f});
-
-  Vector const axis(gCS, QuantityVector<dimensionless_d>(0, 0, 1));
-  LengthType const lambda = 3_m;
-  auto const rho0 = 1_g / units::static_pow<3>(1_cm);
-  FlatExponential<IMediumModel> const medium(gOrigin, axis, rho0, lambda,
-                                             protonComposition);
-  auto const tEnd = 5_s;
-
-  SECTION("horizontal") {
-    Line const line(gOrigin, Vector<SpeedType::dimension_type>(
-                                 gCS, {20_cm / second, 0_m / second, 0_m / second}));
-    setup::Trajectory const trajectory = setup::testing::make_track<setup::Trajectory>(line, tEnd);
-    CHECK((medium.IntegratedGrammage(trajectory, 2_m) / (rho0 * 2_m)) == Approx(1));
-    CHECK((medium.ArclengthFromGrammage(trajectory, rho0 * 5_m) / 5_m) == Approx(1));
-  }
-
-  SECTION("vertical") {
-    Line const line(gOrigin, Vector<SpeedType::dimension_type>(
-                                 gCS, {0_m / second, 0_m / second, 5_m / second}));
-    setup::Trajectory const trajectory = setup::testing::make_track<setup::Trajectory>(line, tEnd);
-    LengthType const length = 2 * lambda;
-    GrammageType const exact = rho0 * lambda * (exp(length / lambda) - 1);
-
-    CHECK((medium.IntegratedGrammage(trajectory, length) / exact) == Approx(1));
-    CHECK((medium.ArclengthFromGrammage(trajectory, exact) / length) == Approx(1));
-  }
-
-  SECTION("escape grammage") {
-    Line const line(gOrigin, Vector<SpeedType::dimension_type>(
-                                 gCS, {0_m / second, 0_m / second, -5_m / second}));
-    setup::Trajectory const trajectory = setup::testing::make_track<setup::Trajectory>(line, tEnd);
-
-    GrammageType const escapeGrammage = rho0 * lambda;
-
-    CHECK(trajectory.GetDirection(0).dot(axis).magnitude() < 0);
-    CHECK(medium.ArclengthFromGrammage(trajectory, 1.2 * escapeGrammage) ==
-          std::numeric_limits<typename GrammageType::value_type>::infinity() * 1_m);
-  }
-
-  SECTION("inclined") {
-    Line const line(gOrigin, Vector<SpeedType::dimension_type>(
-                                 gCS, {0_m / second, 5_m / second, 5_m / second}));
-    setup::Trajectory const trajectory = setup::testing::make_track<setup::Trajectory>(line, tEnd);
-    double const cosTheta = M_SQRT1_2;
-    LengthType const length = 2 * lambda;
-    GrammageType const exact =
-        rho0 * lambda * (exp(cosTheta * length / lambda) - 1) / cosTheta;
-    CHECK((medium.IntegratedGrammage(trajectory, length) / exact) == Approx(1));
-    CHECK((medium.ArclengthFromGrammage(trajectory, exact) / length) == Approx(1));
-  }
-}
-
-TEST_CASE("SlidingPlanarExponential") {
-  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
-                                             std::vector<float>{1.f});
-
-  LengthType const lambda = 3_m;
-  auto const rho0 = 1_g / units::static_pow<3>(1_cm);
-  auto const tEnd = 5_s;
-
-  SlidingPlanarExponential<IMediumModel> const medium(gOrigin, rho0, lambda,
-                                                      protonComposition);
-
-  SECTION("density") {
-    CHECK(medium.GetMassDensity({gCS, {0_m, 0_m, 3_m}}) /
-              medium.GetMassDensity({gCS, {0_m, 3_m, 0_m}}) ==
-          Approx(1));
-  }
-
-  SECTION("vertical") {
-    Vector const axis(gCS, QuantityVector<dimensionless_d>(0, 0, 1));
-    FlatExponential<IMediumModel> const flat(gOrigin, axis, rho0, lambda,
-                                             protonComposition);
-    Line const line({gCS, {0_m, 0_m, 1_m}},
-                    Vector<SpeedType::dimension_type>(
-                        gCS, {0_m / second, 0_m / second, 5_m / second}));
-    setup::Trajectory const trajectory = setup::testing::make_track<setup::Trajectory>(line, tEnd);
-
-    CHECK(medium.GetMassDensity({gCS, {0_mm, 0_m, 3_m}}).magnitude() ==
-          flat.GetMassDensity({gCS, {0_mm, 0_m, 3_m}}).magnitude());
-    CHECK(medium.IntegratedGrammage(trajectory, 2_m).magnitude() ==
-          flat.IntegratedGrammage(trajectory, 2_m).magnitude());
-    CHECK(medium.ArclengthFromGrammage(trajectory, rho0 * 5_m).magnitude() ==
-          flat.ArclengthFromGrammage(trajectory, rho0 * 5_m).magnitude());
-  }
-}
-
-auto constexpr rho0 = 1_kg / 1_m / 1_m / 1_m;
-
-struct Exponential {
-  auto operator()(corsika::geometry::Point const& p) const {
-    return exp(p.GetCoordinates()[0] / 1_m) * rho0;
-  }
-
-  template <int N>
-  auto Derivative(Point const& p, Vector<dimensionless_d> const& v) const {
-    return v.GetComponents()[0] * (*this)(p) / corsika::units::static_pow<N>(1_m);
-  }
-
-  auto FirstDerivative(Point const& p, Vector<dimensionless_d> const& v) const {
-    return Derivative<1>(p, v);
-  }
-
-  auto SecondDerivative(Point const& p, Vector<dimensionless_d> const& v) const {
-    return Derivative<2>(p, v);
-  }
-};
-
-TEST_CASE("InhomogeneousMedium") {
-  Vector direction(gCS, QuantityVector<dimensionless_d>(1, 0, 0));
-
-  Line line(gOrigin, Vector<SpeedType::dimension_type>(
-                         gCS, {20_m / second, 0_m / second, 0_m / second}));
-
-  auto const tEnd = 5_s;
-  setup::Trajectory const trajectory = setup::testing::make_track<setup::Trajectory>(line, tEnd);
-
-  Exponential const e;
-  DensityFunction<decltype(e), LinearApproximationIntegrator> const rho(e);
-
-  SECTION("DensityFunction") {
-    CHECK(e.Derivative<1>(gOrigin, direction) / (1_kg / 1_m / 1_m / 1_m / 1_m) ==
-          Approx(1));
-    CHECK(rho.EvaluateAt(gOrigin) == e(gOrigin));
-  }
-
-  auto const exactGrammage = [](auto l) { return 1_m * rho0 * (exp(l / 1_m) - 1); };
-  auto const exactLength = [](auto X) { return 1_m * log(1 + X / (rho0 * 1_m)); };
-
-  auto constexpr l = 15_cm;
-
-  NuclearComposition const composition{{Code::Proton}, {1.f}};
-  InhomogeneousMedium<IMediumModel, decltype(rho)> const inhMedium(composition, rho);
-
-  SECTION("Integration") {
-    CHECK(rho.IntegrateGrammage(trajectory, l) / exactGrammage(l) ==
-          Approx(1).epsilon(1e-2));
-    CHECK(rho.ArclengthFromGrammage(trajectory, exactGrammage(l)) /
-              exactLength(exactGrammage(l)) ==
-          Approx(1).epsilon(1e-2));
-    CHECK(rho.MaximumLength(trajectory, 1e-2) >
-          l); // todo: write reasonable test when implementation is working
-
-    CHECK(rho.IntegrateGrammage(trajectory, l) ==
-          inhMedium.IntegratedGrammage(trajectory, l));
-    CHECK(rho.ArclengthFromGrammage(trajectory, 20_g / (1_cm * 1_cm)) ==
-          inhMedium.ArclengthFromGrammage(trajectory, 20_g / (1_cm * 1_cm)));
-  }
-}
-
-TEST_CASE("LayeredSphericalAtmosphereBuilder") {
-
-  LayeredSphericalAtmosphereBuilder builder =
-      environment::make_layered_spherical_atmosphere_builder<>::create(
-          gOrigin, units::constants::EarthRadius::Mean);
-
-  builder.setNuclearComposition(
-      {{{particles::Code::Nitrogen, particles::Code::Oxygen}}, {{.6, .4}}});
-
-  builder.addLinearLayer(1_km, 10_km);
-  builder.addLinearLayer(2_km, 20_km);
-  builder.addExponentialLayer(540.1778_g / (1_cm * 1_cm), 772170.16_cm, 30_km);
-
-  CHECK(builder.size() == 3);
-
-  auto const builtEnv = builder.assemble();
-  auto const& univ = builtEnv.GetUniverse();
-
-  CHECK(builder.size() == 0);
-
-  auto const R = builder.getEarthRadius();
-
-  CHECK(univ->GetChildNodes().size() == 1);
-
-  CHECK(univ->GetContainingNode(Point(gCS, 0_m, 0_m, R + 35_km)) == univ.get());
-  CHECK(dynamic_cast<Sphere const&>(
-            univ->GetContainingNode(Point(gCS, 0_m, 0_m, R + 8_km))->GetVolume())
-            .GetRadius() == R + 10_km);
-  CHECK(dynamic_cast<Sphere const&>(
-            univ->GetContainingNode(Point(gCS, 0_m, 0_m, R + 12_km))->GetVolume())
-            .GetRadius() == R + 20_km);
-  CHECK(dynamic_cast<Sphere const&>(
-            univ->GetContainingNode(Point(gCS, 0_m, 0_m, R + 24_km))->GetVolume())
-            .GetRadius() == R + 30_km);
-}
-
-TEST_CASE("UniformMagneticField w/ Homogeneous Medium") {
-
-  // setup our interface types
-  using IModelInterface = IMagneticFieldModel<IMediumModel>;
-  using AtmModel = UniformMagneticField<HomogeneousMedium<IModelInterface>>;
-
-  // the composition we use for the homogenous medium
-  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
-                                             std::vector<float>{1.f});
-
-  // create a magnetic field vector
-  Vector B0(gCS, 0_T, 0_T, 0_T);
-
-  // the constant density
-  const auto density{19.2_g / cube(1_cm)};
-
-  // create our atmospheric model
-  AtmModel medium(B0, density, protonComposition);
-
-  // and test at several locations
-  CHECK(B0.GetComponents(gCS) ==
-        medium.GetMagneticField(Point(gCS, -10_m, 4_m, 35_km)).GetComponents(gCS));
-  CHECK(
-      B0.GetComponents(gCS) ==
-      medium.GetMagneticField(Point(gCS, 1000_km, -1000_km, 1000_km)).GetComponents(gCS));
-  CHECK(B0.GetComponents(gCS) ==
-        medium.GetMagneticField(Point(gCS, 0_m, 0_m, 0_m)).GetComponents(gCS));
-
-  // create a new magnetic field vector
-  Vector B1(gCS, 23_T, 57_T, -4_T);
-
-  // and update this atmospheric model
-  medium.SetMagneticField(B1);
-
-  // and test at several locations
-  CHECK(B1.GetComponents(gCS) ==
-        medium.GetMagneticField(Point(gCS, -10_m, 4_m, 35_km)).GetComponents(gCS));
-  CHECK(
-      B1.GetComponents(gCS) ==
-      medium.GetMagneticField(Point(gCS, 1000_km, -1000_km, 1000_km)).GetComponents(gCS));
-  CHECK(B1.GetComponents(gCS) ==
-        medium.GetMagneticField(Point(gCS, 0_m, 0_m, 0_m)).GetComponents(gCS));
-
-  // check the density and nuclear composition
-  CHECK(density == medium.GetMassDensity(Point(gCS, 0_m, 0_m, 0_m)));
-  medium.GetNuclearComposition();
-
-  // create a line of length 1 m
-  Line const line(gOrigin, Vector<SpeedType::dimension_type>(
-                               gCS, {1_m / second, 0_m / second, 0_m / second}));
-
-  // the end time of our line
-  auto const tEnd = 1_s;
-
-  // and the associated trajectory
-  setup::Trajectory const trajectory = setup::testing::make_track<setup::Trajectory>(line, tEnd);
-
-  // and check the integrated grammage
-  CHECK((medium.IntegratedGrammage(trajectory, 3_m) / (density * 3_m)) == Approx(1));
-  CHECK((medium.ArclengthFromGrammage(trajectory, density * 5_m) / 5_m) == Approx(1));
-}
-
-TEST_CASE("LayeredSphericalAtmosphereBuilder w/ magnetic field") {
-
-  // setup our interface types
-  using ModelInterface = IMagneticFieldModel<IMediumModel>;
-
-  // the composition we use for the homogenous medium
-  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
-                                             std::vector<float>{1.f});
-
-  // create magnetic field vectors
-  Vector B0(gCS, 0_T, 0_T, 1_T);
-
-  LayeredSphericalAtmosphereBuilder builder =
-      environment::make_layered_spherical_atmosphere_builder<
-          ModelInterface,
-          UniformMagneticField>::create(gOrigin, units::constants::EarthRadius::Mean, B0);
-
-  builder.setNuclearComposition(
-      {{{particles::Code::Nitrogen, particles::Code::Oxygen}}, {{.6, .4}}});
-  builder.addLinearLayer(1_km, 10_km);
-  builder.addExponentialLayer(1222.6562_g / (1_cm * 1_cm), 994186.38_cm, 20_km);
-
-  CHECK(builder.size() == 2);
-
-  auto const builtEnv = builder.assemble();
-  auto const& univ = builtEnv.GetUniverse();
-
-  CHECK(builder.size() == 0);
-  CHECK(univ->GetChildNodes().size() == 1);
-  auto const R = builder.getEarthRadius();
-
-  // check magnetic field at several locations
-  const Point pTest(gCS, -10_m, 4_m, R + 35_m);
-  CHECK(B0.GetComponents(gCS) == univ->GetContainingNode(pTest)
-                                     ->GetModelProperties()
-                                     .GetMagneticField(pTest)
-                                     .GetComponents(gCS));
-  const Point pTest2(gCS, 10_m, -4_m, R + 15_km);
-  CHECK(B0.GetComponents(gCS) == univ->GetContainingNode(pTest2)
-                                     ->GetModelProperties()
-                                     .GetMagneticField(pTest2)
-                                     .GetComponents(gCS));
-}
-
-TEST_CASE("UniformRefractiveIndex w/ Homogeneous") {
-
-  // setup our interface types
-  using IModelInterface = IRefractiveIndexModel<IMediumModel>;
-  using AtmModel = UniformRefractiveIndex<HomogeneousMedium<IModelInterface>>;
-
-  // the constant density
-  const auto density{19.2_g / cube(1_cm)};
-
-  // the composition we use for the homogenous medium
-  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
-                                             std::vector<float>{1.f});
-
-  // the refrative index that we use
-  const double n{1.000327};
-
-  // create the atmospheric model
-  AtmModel medium(n, density, protonComposition);
-
-  // and require that it is constant
-  CHECK(n == medium.GetRefractiveIndex(Point(gCS, -10_m, 4_m, 35_km)));
-  CHECK(n == medium.GetRefractiveIndex(Point(gCS, +210_m, 0_m, 7_km)));
-  CHECK(n == medium.GetRefractiveIndex(Point(gCS, 0_m, 0_m, 0_km)));
-  CHECK(n == medium.GetRefractiveIndex(Point(gCS, 100_km, 400_km, 350_km)));
-
-  // a new refractive index
-  const double n2{2.3472123};
-
-  // update the refractive index of this atmospheric model
-  medium.SetRefractiveIndex(n2);
-
-  // check that the returned refractive index is correct
-  CHECK(n2 == medium.GetRefractiveIndex(Point(gCS, -10_m, 4_m, 35_km)));
-  CHECK(n2 == medium.GetRefractiveIndex(Point(gCS, +210_m, 0_m, 7_km)));
-  CHECK(n2 == medium.GetRefractiveIndex(Point(gCS, 0_m, 0_m, 0_km)));
-  CHECK(n2 == medium.GetRefractiveIndex(Point(gCS, 100_km, 400_km, 350_km)));
-
-  // define our axis vector
-  Vector const axis(gCS, QuantityVector<dimensionless_d>(0, 0, 1));
-
-  // check the density and nuclear composition
-  CHECK(density == medium.GetMassDensity(Point(gCS, 0_m, 0_m, 0_m)));
-  medium.GetNuclearComposition();
-
-  // create a line of length 1 m
-  Line const line(gOrigin, Vector<SpeedType::dimension_type>(
-                               gCS, {1_m / second, 0_m / second, 0_m / second}));
-
-  // the end time of our line
-  auto const tEnd = 1_s;
-
-  // and the associated trajectory
-  setup::Trajectory const trajectory = setup::testing::make_track<setup::Trajectory>(line, tEnd);
-
-  // and check the integrated grammage
-  CHECK((medium.IntegratedGrammage(trajectory, 3_m) / (density * 3_m)) == Approx(1));
-  CHECK((medium.ArclengthFromGrammage(trajectory, density * 5_m) / 5_m) == Approx(1));
-}
-
-TEST_CASE("MediumProperties") {
-
-  // test access of medium properties via enum and class types
-
-  const Medium type = Medium::AirDry1Atm;
-  const MediumData& air = mediumData(type);
-  CHECK(air.Ieff() == 85.7);
-  CHECK(air.Cbar() == 10.5961);
-  CHECK(air.x0() == 1.7418);
-  CHECK(air.x1() == 4.2759);
-  CHECK(air.aa() == 0.10914);
-  CHECK(air.sk() == 3.3994);
-  CHECK(air.dlt0() == 0.0);
-}
-
-TEST_CASE("MediumPropertyModel w/ Homogeneous") {
-
-  // setup our interface types
-  using IModelInterface = IMediumPropertyModel<IMediumModel>;
-  using AtmModel = MediumPropertyModel<HomogeneousMedium<IModelInterface>>;
-
-  // the constant density
-  const auto density{19.2_g / cube(1_cm)};
-
-  // the composition we use for the homogenous medium
-  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
-                                             std::vector<float>{1.f});
-
-  // the refrative index that we use
-  const Medium type = Medium::AirDry1Atm;
-
-  // create the atmospheric model
-  AtmModel medium(type, density, protonComposition);
-
-  // and require that it is constant
-  CHECK(type == medium.medium(Point(gCS, -10_m, 4_m, 35_km)));
-  CHECK(type == medium.medium(Point(gCS, +210_m, 0_m, 7_km)));
-  CHECK(type == medium.medium(Point(gCS, 0_m, 0_m, 0_km)));
-  CHECK(type == medium.medium(Point(gCS, 100_km, 400_km, 350_km)));
-
-  // a new refractive index
-  const Medium type2 = Medium::StandardRock;
-
-  // update the refractive index of this atmospheric model
-  medium.set_medium(type2);
-
-  // check that the returned refractive index is correct
-  CHECK(type2 == medium.medium(Point(gCS, -10_m, 4_m, 35_km)));
-  CHECK(type2 == medium.medium(Point(gCS, +210_m, 0_m, 7_km)));
-  CHECK(type2 == medium.medium(Point(gCS, 0_m, 0_m, 0_km)));
-  CHECK(type2 == medium.medium(Point(gCS, 100_km, 400_km, 350_km)));
-
-  // define our axis vector
-  Vector const axis(gCS, QuantityVector<dimensionless_d>(0, 0, 1));
-
-  // check the density and nuclear composition
-  CHECK(density == medium.GetMassDensity(Point(gCS, 0_m, 0_m, 0_m)));
-  medium.GetNuclearComposition();
-
-  // create a line of length 1 m
-  Line const line(gOrigin, Vector<SpeedType::dimension_type>(
-                               gCS, {1_m / second, 0_m / second, 0_m / second}));
-
-  // the end time of our line
-  auto const tEnd = 1_s;
-
-  // and the associated trajectory
-  setup::Trajectory const trajectory = setup::testing::make_track<setup::Trajectory>(line, tEnd);
-
-  // and check the integrated grammage
-  CHECK((medium.IntegratedGrammage(trajectory, 3_m) / (density * 3_m)) == Approx(1));
-  CHECK((medium.ArclengthFromGrammage(trajectory, density * 5_m) / 5_m) == Approx(1));
-}
diff --git a/Environment/testShowerAxis.cc b/Environment/testShowerAxis.cc
deleted file mode 100644
index e2862176bc0864bac62238d6b06dae103bccf3b2..0000000000000000000000000000000000000000
--- a/Environment/testShowerAxis.cc
+++ /dev/null
@@ -1,86 +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/environment/DensityFunction.h>
-#include <corsika/environment/HomogeneousMedium.h>
-#include <corsika/environment/IMediumModel.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/environment/ShowerAxis.h>
-#include <corsika/environment/VolumeTreeNode.h>
-#include <corsika/geometry/Line.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <catch2/catch.hpp>
-
-using namespace corsika::geometry;
-using namespace corsika::environment;
-using namespace corsika::particles;
-using namespace corsika::units;
-using namespace corsika::units::si;
-using namespace corsika;
-
-const auto density = 1_kg / (1_m * 1_m * 1_m);
-
-auto setupEnvironment(particles::Code vTargetCode) {
-  // setup environment, geometry
-  auto env = std::make_unique<environment::Environment<environment::IMediumModel>>();
-  auto& universe = *(env->GetUniverse());
-  const geometry::CoordinateSystem& cs = env->GetCoordinateSystem();
-
-  auto theMedium =
-      environment::Environment<environment::IMediumModel>::CreateNode<geometry::Sphere>(
-          geometry::Point{cs, 0_m, 0_m, 0_m},
-          1_km * std::numeric_limits<double>::infinity());
-
-  using MyHomogeneousModel = environment::HomogeneousMedium<environment::IMediumModel>;
-  theMedium->SetModelProperties<MyHomogeneousModel>(
-      density, environment::NuclearComposition(std::vector<particles::Code>{vTargetCode},
-                                               std::vector<float>{1.}));
-
-  auto const* nodePtr = theMedium.get();
-  universe.AddChild(std::move(theMedium));
-
-  return std::make_tuple(std::move(env), &cs, nodePtr);
-}
-
-TEST_CASE("Homogeneous Density") {
-  auto [env, csPtr, nodePtr] = setupEnvironment(particles::Code::Nitrogen);
-  auto const& cs = *csPtr;
-  [[maybe_unused]] auto const& env_dummy = env;
-  [[maybe_unused]] auto const& node_dummy = nodePtr;
-
-  auto const observationHeight = 0_km;
-  auto const injectionHeight = 10_km;
-  auto const t = -observationHeight + injectionHeight;
-  Point const showerCore{cs, 0_m, 0_m, observationHeight};
-  Point const injectionPos = showerCore + Vector<dimensionless_d>{cs, {0, 0, 1}} * t;
-
-  environment::ShowerAxis const showerAxis{injectionPos, (showerCore - injectionPos),
-                                           *env,
-                                           false, // -> do not throw exceptions
-                                           20};   // -> number of bins
-
-  CHECK(showerAxis.steplength() == 500_m);
-
-  CHECK(showerAxis.maximumX() / (10_km * density) == Approx(1).epsilon(1e-8));
-
-  CHECK(showerAxis.minimumX() == 0_g / square(1_cm));
-
-  const Point p{cs, 10_km, 20_km, 8.3_km};
-  CHECK(showerAxis.projectedX(p) / (1.7_km * density) == Approx(1).epsilon(1e-8));
-
-  const units::si::LengthType d = 6.789_km;
-  CHECK(showerAxis.X(d) / (d * density) == Approx(1).epsilon(1e-8));
-
-  const Vector<dimensionless_d> dir{cs, {0, 0, -1}};
-  CHECK(showerAxis.GetDirection().GetComponents(cs) == dir.GetComponents(cs));
-  CHECK(showerAxis.GetStart().GetCoordinates() == injectionPos.GetCoordinates());
-}
diff --git a/Framework/Analytics/CMakeLists.txt b/Framework/Analytics/CMakeLists.txt
deleted file mode 100644
index ededac54ff99ec6ee8089bc55f1243752e842fce..0000000000000000000000000000000000000000
--- a/Framework/Analytics/CMakeLists.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-# create the library
-add_library (CORSIKAanalytics INTERFACE)
-
-# namespace of library -> location of header files
-set (
-  CORSIKAanalytics_NAMESPACE
-  corsika/analytics
-  )
-
-# header files of this library
-set (
-  CORSIKAanalytics_HEADERS
-  ClassTimer.h
-  FunctionTimer.h
-  )
-
-# copy the headers into the namespace
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAanalytics
-  ${CORSIKAanalytics_NAMESPACE}
-  ${CORSIKAanalytics_HEADERS}
-  )
-
-# include directive for upstream code
-target_include_directories (
-  CORSIKAanalytics
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/>
-  C8::ext:boost
-  )
-
-# and link against spdlog
-#target_link_libraries(
-#  CORSIKAanalytics
-#  INTERFACE
-#  spdlog::spdlog
-#)
-
-# install library
-install (
-  FILES ${CORSIKAanalytics_HEADERS}
-  DESTINATION include/${CORSIKAanalytics_NAMESPACE}
-  )
-
-# ----------------
-# code unit testing
-
-
-CORSIKA_ADD_TEST (testFunctionTimer)
-target_link_libraries (
- testFunctionTimer
- CORSIKAanalytics
- CORSIKAtesting
- )
-
- CORSIKA_ADD_TEST (testClassTimer)
-target_link_libraries (
- testClassTimer
- CORSIKAanalytics
- CORSIKAtesting
- )
diff --git a/Framework/Analytics/ClassTimer.h b/Framework/Analytics/ClassTimer.h
deleted file mode 100644
index cb867a56f226227d2c09582d3fde57627b5254cc..0000000000000000000000000000000000000000
--- a/Framework/Analytics/ClassTimer.h
+++ /dev/null
@@ -1,157 +0,0 @@
-/*
- * (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.
- */
-
-// Another possibility:
-// https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Execute-Around_Pointer
-// for a more global approach
-//
-// In this case here only a single function is measured via member function pointer.
-
-#pragma once
-
-#include <chrono>
-#include <utility>
-
-namespace corsika::analytics {
-
-  /// Measure the runtime of a single class function
-  /**
-   * @tparam TClassFunc Type of the member function pointer that should be wrapped
-   * @tparam TFunc Actual function of the type defined in TClass
-   */
-  template <typename TClassFunc, TClassFunc TFunc>
-  class ClassTimer;
-
-  /// Measure the runtime of a single class function
-  /** Specialisation to capture exact information about the composition of the member
-   * function pointer used.
-   *
-   *  This class wrapes a single function and allowes the measureing of its runtime if it
-   * called via the "call(...)" function
-   *
-   * @tparam TClass Class of the function that should be wrapped
-   * @tparam TRet   Return value of the wrapped function
-   * @tparam TArgs  Arguments passed to the wrapped function
-   * @tparam TFuncPtr Actual function of the type defined by TRet
-   * TClass::TFuncPtr(TArgs...)
-   */
-  template <typename TClass, typename TRet, typename... TArgs,
-            TRet (TClass::*TFuncPtr)(TArgs...)>
-  class ClassTimer<TRet (TClass::*)(TArgs...), TFuncPtr> {
-  private:
-    using TClock = std::chrono::high_resolution_clock;
-    using TDuration = std::chrono::microseconds;
-
-    TClass& vObj;
-
-    typename TClock::time_point vStart;
-    TDuration vDiff;
-
-  public:
-    ClassTimer(TClass& obj)
-        : vObj(obj) {}
-
-    /// Executes the wrapped function
-    /** This function executes and measure the runtime of the wrapped function with the
-     * highest precision available (high_resolution_clock).
-     *
-     * @param args Arguments are perfect forwarded to the wrapped function.
-     * @return Returns the return value of the wrapped function. This value get copied
-     * during the process and therefore must be copie constructible!
-     */
-    TRet call(TArgs... args) {
-      vStart = TClock::now();
-      auto tmp = (vObj.*TFuncPtr)(std::forward<TArgs>(args)...);
-      vDiff = std::chrono::duration_cast<TDuration>(TClock::now() - vStart);
-      return tmp;
-    }
-
-    /// returns the last runtime of the wraped function accessed via call
-    inline TDuration getTime() const { return vDiff; }
-  };
-
-  /// Specialisation for member functions without return value
-  template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...)>
-  class ClassTimer<void (TClass::*)(TArgs...), TFuncPtr> {
-  private:
-    using TClock = std::chrono::high_resolution_clock;
-    using TDuration = std::chrono::microseconds;
-
-    TClass& vObj;
-
-    typename TClock::time_point vStart;
-    TDuration vDiff;
-
-  public:
-    ClassTimer(TClass& obj)
-        : vObj(obj) {}
-
-    void call(TArgs... args) {
-      vStart = TClock::now();
-      (vObj.*TFuncPtr)(std::forward<TArgs>(args)...);
-      vDiff = std::chrono::duration_cast<TDuration>(TClock::now() - vStart);
-      return;
-    }
-
-    inline TDuration getTime() const { return vDiff; }
-  };
-
-  /// Specialisation for const member functions
-  template <typename TClass, typename TRet, typename... TArgs,
-            TRet (TClass::*TFuncPtr)(TArgs...) const>
-  class ClassTimer<TRet (TClass::*)(TArgs...) const, TFuncPtr> {
-  private:
-    using TClock = std::chrono::high_resolution_clock;
-    using TDuration = std::chrono::microseconds;
-
-    const TClass& vObj;
-
-    typename TClock::time_point vStart;
-    TDuration vDiff;
-
-  public:
-    ClassTimer(TClass& obj)
-        : vObj(obj) {}
-
-    TRet call(TArgs... args) {
-      vStart = TClock::now();
-      auto tmp = (vObj.*TFuncPtr)(std::forward<TArgs>(args)...);
-      vDiff = std::chrono::duration_cast<TDuration>(TClock::now() - vStart);
-      return tmp;
-    }
-
-    inline TDuration getTime() const { return vDiff; }
-  };
-
-  /// Specialisation for const member functions without return value
-  template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...) const>
-  class ClassTimer<void (TClass::*)(TArgs...) const, TFuncPtr> {
-  private:
-    using TClock = std::chrono::high_resolution_clock;
-    using TDuration = std::chrono::microseconds;
-
-    const TClass& obj_;
-
-    typename TClock::time_point start_;
-    TDuration timeDiff_;
-
-  public:
-    ClassTimer(TClass& obj)
-        : obj_(obj) {}
-
-    void call(TArgs... args) {
-      start_ = TClock::now();
-      (obj_.*TFuncPtr)(std::forward<TArgs>(args)...);
-      timeDiff_ = std::chrono::duration_cast<TDuration>(TClock::now() - start_);
-      return;
-    }
-
-    inline TDuration getTime() const { return timeDiff_; }
-  };
-
-} // namespace corsika::analytics
\ No newline at end of file
diff --git a/Framework/Analytics/FunctionTimer.h b/Framework/Analytics/FunctionTimer.h
deleted file mode 100644
index 4dfe066b6b9a2b6b976bd67d8cf95845b0dcd87d..0000000000000000000000000000000000000000
--- a/Framework/Analytics/FunctionTimer.h
+++ /dev/null
@@ -1,47 +0,0 @@
-/*
- * (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 <chrono>
-#include <utility>
-
-namespace corsika::analytics {
-
-  /// Wraps and measures the runtime of a single function type object
-  /**
-   *
-   * @tparam TFunc funtion pointer that should be wrapped
-   * @tparam TClock type of the clock that should be used for measurements
-   * @tparam TDuration type of std::duration to measure the elapsed time
-   */
-  template <typename TFunc, typename TClock = std::chrono::high_resolution_clock,
-            typename TDuration = std::chrono::microseconds>
-  class FunctionTimer {
-  private:
-    typename TClock::time_point start_;
-    TDuration timeDiff_;
-
-    TFunc function_;
-
-  public:
-    /// Constructs the wrapper with the given functionpointer
-    FunctionTimer(TFunc f)
-        : function_(f) {}
-
-    template <typename... TArgs>
-    auto operator()(TArgs&&... args) -> std::invoke_result_t<TFunc, TArgs...> {
-      start_ = TClock::now();
-      auto tmp = function_(std::forward<TArgs>(args)...);
-      timeDiff_ = std::chrono::duration_cast<TDuration>(TClock::now() - start_);
-      return tmp;
-    }
-
-    inline TDuration getTime() const { return timeDiff_; }
-  };
-
-} // namespace corsika::analytics
diff --git a/Framework/CMakeLists.txt b/Framework/CMakeLists.txt
deleted file mode 100644
index 4e7baba255038652c60894b20f75df3606a80742..0000000000000000000000000000000000000000
--- a/Framework/CMakeLists.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-add_subdirectory (Analytics)
-add_subdirectory (Cascade)
-add_subdirectory (Geometry)
-add_subdirectory (Logging)
-add_subdirectory (Particles)
-add_subdirectory (ProcessSequence)
-add_subdirectory (Random)
-add_subdirectory (StackInterface)
-add_subdirectory (Testing)
-add_subdirectory (Utilities)
-add_subdirectory (Units)
-
-
-
-
-
-
-
diff --git a/Framework/Cascade/CMakeLists.txt b/Framework/Cascade/CMakeLists.txt
deleted file mode 100644
index 2a12099736e967a7bbcfc9db3395f46fc94590cd..0000000000000000000000000000000000000000
--- a/Framework/Cascade/CMakeLists.txt
+++ /dev/null
@@ -1,55 +0,0 @@
-# namespace of library -> location of header files
-set (
-  CORSIKAcascade_NAMESPACE
-  corsika/cascade
-  )
-
-# header files of this library
-set (
-  CORSIKAcascade_HEADERS
-  Cascade.h
-  testCascade.h
-  )
-
-add_library (CORSIKAcascade INTERFACE)
-
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAcascade ${CORSIKAcascade_NAMESPACE} ${CORSIKAcascade_HEADERS})
-
-target_link_libraries(
-  CORSIKAcascade
-  INTERFACE
-  CORSIKAsetup
-  CORSIKArandom
-  CORSIKAstackinterface
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAprocesssequence
-  CORSIKAunits
-  CORSIKAlogging
-  )
-
-# include directive for upstream code
-target_include_directories (
-  CORSIKAcascade
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/>
-  )
-
-# install library
-install (
-  FILES ${CORSIKAcascade_HEADERS}
-  DESTINATION include/${CORSIKAcascade_NAMESPACE}
-  )
-
-# ----------------
-# code unit testing
-CORSIKA_ADD_TEST(testCascade)
-target_link_libraries (
-  testCascade
-  CORSIKAcascade
-  ProcessStackInspector
-  ProcessTrackingLine
-  CORSIKAtesting
-  )
diff --git a/Framework/Cascade/Cascade.dox b/Framework/Cascade/Cascade.dox
deleted file mode 100644
index 7ea74e9c5f48191e010effd294c741494239b7c6..0000000000000000000000000000000000000000
--- a/Framework/Cascade/Cascade.dox
+++ /dev/null
@@ -1,8 +0,0 @@
-/**
-
-
-  Here are have to explain the corsika::cascade::Cascade class and its
-  functionality.
-  
-
-*/
\ No newline at end of file
diff --git a/Framework/Cascade/Cascade.h b/Framework/Cascade/Cascade.h
deleted file mode 100644
index bc29bb620fea26ae1225f4832395bb6834426fbd..0000000000000000000000000000000000000000
--- a/Framework/Cascade/Cascade.h
+++ /dev/null
@@ -1,395 +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/environment/Environment.h>
-#include <corsika/logging/Logging.h>
-#include <corsika/process/ProcessReturn.h>
-#include <corsika/random/ExponentialDistribution.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/random/UniformRealDistribution.h>
-#include <corsika/stack/SecondaryView.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/stack/history/EventType.hpp>
-#include <corsika/stack/history/HistorySecondaryProducer.hpp>
-
-/*  see Issue 161, we need to include SetupStack only because we need
-    to globally define StackView. This is clearly not nice and should
-    be changed, when possible. It might be that StackView needs to be
-    templated in Cascade, but this would be even worse... so we don't
-    do that until it is really needed.
- */
-#include <corsika/setup/SetupStack.h>
-
-#include <cassert>
-#include <cmath>
-#include <limits>
-
-#include <boost/type_index.hpp>
-using boost::typeindex::type_id_with_cvr;
-
-#include <fstream>
-
-/**
- * The cascade namespace assembles all objects needed to simulate full particles cascades.
- */
-
-namespace corsika::cascade {
-
-  /**
-   * \class Cascade
-   *
-   * The Cascade class is constructed from template arguments making
-   * it very versatile. Via the template arguments physics models are
-   * plugged into the cascade simulation.
-   *
-   * <b>TTracking</b> must be a class according to the
-   * TrackingInterface providing the functions:
-   * <code>auto GetTrack(Particle const& p)</auto>,
-   * with the return type <code>geometry::Trajectory<corsika::geometry::Line>
-   * </code>
-   *
-   * <b>TProcessList</b> must be a ProcessSequence.   *
-   * <b>Stack</b> is the storage object for particle data, i.e. with
-   * Particle class type <code>Stack::ParticleType</code>
-   *
-   *
-   */
-
-  template <typename TTracking, typename TProcessList, typename TStack,
-            /*
-              TStackView is needed as explicit template parameter because
-              of issue 161 and the
-              inability of clang to understand "stack::MakeView" so far.
-             */
-            typename TStackView = corsika::setup::StackView>
-  class Cascade {
-    using Particle = typename TStack::ParticleType;
-    using VolumeTreeNode =
-        std::remove_pointer_t<decltype(((Particle*)nullptr)->GetNode())>;
-    using MediumInterface = typename VolumeTreeNode::IModelProperties;
-
-    // we only want fully configured objects
-    Cascade() = delete;
-
-  public:
-    /**
-     * Cascade class cannot be default constructed, but needs a valid
-     * list of physics processes for configuration at construct time.
-     */
-    Cascade(corsika::environment::Environment<MediumInterface> const& env, TTracking& tr,
-            TProcessList& pl, TStack& stack)
-        : environment_(env)
-        , tracking_(tr)
-        , process_sequence_(pl)
-        , stack_(stack)
-        , count_(0) {
-      C8LOG_INFO(c8_ascii_);
-      if constexpr (TStackView::has_event) {
-        C8LOG_INFO(" - With full cascade HISTORY.");
-      }
-    }
-
-    ~Cascade(){};
-
-    /**
-     * The Run function is the main simulation loop, which processes
-     * particles from the Stack until the Stack is empty.
-     */
-    void Run() {
-      setNodes();
-
-      while (!stack_.IsEmpty()) {
-        while (!stack_.IsEmpty()) {
-          C8LOG_TRACE("Stack: {}", stack_.as_string());
-          count_++;
-          auto pNext = stack_.GetNextParticle();
-          C8LOG_DEBUG(
-              "============== next particle : count={}, pid={} "
-              ", stack entries={}"
-              ", stack deleted={}",
-              count_, pNext.GetPID(), stack_.getEntries(), stack_.getDeleted());
-          Step(pNext);
-          process_sequence_.DoStack(stack_);
-        }
-        // do cascade equations, which can put new particles on Stack,
-        // thus, the double loop
-        // DoCascadeEquations();
-      }
-    }
-
-    /**
-     * Force an interaction of the top particle of the stack at its current position.
-     * Note that SetNodes() or an equivalent procedure needs to be called first if you
-     * want to call forceInteraction() for the primary interaction.
-     */
-    void forceInteraction() {
-      C8LOG_DEBUG("forced interaction!");
-      setNodes();
-      auto vParticle = stack_.GetNextParticle();
-      TStackView secondaries(vParticle);
-      interaction(secondaries);
-      process_sequence_.DoSecondaries(secondaries);
-      vParticle.Delete(); // primary particle has interacted and is gone
-    }
-
-  private:
-    /**
-     * The Step function is executed for each particle from the
-     * stack. It will calcualte geometric transport of the particles,
-     * and apply continuous and stochastic processes to it, which may
-     * lead to energy losses, scattering, absorption, decays and the
-     * production of secondary particles.
-     *
-     * New particles produced in one step are subject to further
-     * processing, e.g. thinning, etc.
-     */
-    void Step(Particle& vParticle) {
-      using namespace corsika;
-      using namespace corsika::units::si;
-
-      // determine combined total interaction length (inverse)
-      InverseGrammageType const total_inv_lambda =
-          process_sequence_.GetInverseInteractionLength(vParticle);
-
-      // sample random exponential step length in grammage
-      corsika::random::ExponentialDistribution expDist(1 / total_inv_lambda);
-      GrammageType const next_interact = expDist(rng_);
-
-      C8LOG_DEBUG(
-          "total_lambda={} g/cm2, "
-          ", next_interact={} g/cm2",
-          double((1. / total_inv_lambda) / 1_g * 1_cm * 1_cm),
-          double(next_interact / 1_g * 1_cm * 1_cm));
-
-      auto const* currentLogicalNode = vParticle.GetNode();
-
-      // assert that particle stays outside void Universe if it has no
-      // model properties set
-      assert((currentLogicalNode != &*environment_.GetUniverse() ||
-              environment_.GetUniverse()->HasModelProperties()) &&
-             "FATAL: The environment model has no valid properties set!");
-
-      // determine combined total inverse decay time
-      InverseTimeType const total_inv_lifetime =
-          process_sequence_.GetInverseLifetime(vParticle);
-
-      // sample random exponential decay time
-      corsika::random::ExponentialDistribution expDistDecay(1 / total_inv_lifetime);
-      TimeType const next_decay = expDistDecay(rng_);
-      C8LOG_DEBUG(
-          "total_lifetime={} s"
-          ", next_decay={} s",
-          (1 / total_inv_lifetime) / 1_s, next_decay / 1_s);
-
-      // convert next_decay from time to length [m]
-      LengthType const distance_decay = next_decay * vParticle.GetMomentum().norm() /
-                                        vParticle.GetEnergy() * units::constants::c;
-
-      // determine geometric tracking
-      auto [step, nextVol] = tracking_.GetTrack(vParticle);
-      auto geomMaxLength = step.GetLength(1);
-
-      // convert next_step from grammage to length
-      LengthType const distance_interact =
-          currentLogicalNode->GetModelProperties().ArclengthFromGrammage(step,
-                                                                         next_interact);
-
-      // determine the maximum geometric step length
-      LengthType const continuous_max_dist = process_sequence_.MaxStepLength(vParticle, step);
-
-      // take minimum of geometry, interaction, decay for next step
-      auto min_distance =
-          std::min({distance_interact, distance_decay, continuous_max_dist, geomMaxLength});
-
-      C8LOG_DEBUG(
-          "transport particle by : {} m "
-          "Medium transition after: {} m "
-          "Decay after: {} m "
-          "Interaction after: {} m "
-          "Continuous limit: {} m ",
-          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:
-      step.SetLength(min_distance);
-      vParticle.SetPosition(step.GetPosition(1));
-      vParticle.SetMomentum(step.GetDirection(1) * vParticle.GetMomentum().norm());
-      vParticle.SetTime(vParticle.GetTime() + step.GetDuration());
-      std::cout << "New Position: " << vParticle.GetPosition().GetCoordinates()
-                << std::endl;
-
-      // apply all continuous processes on particle + track
-      process::EProcessReturn status = process_sequence_.DoContinuous(vParticle, step);
-
-      if (status == process::EProcessReturn::eParticleAbsorbed) {
-        C8LOG_DEBUG("Cascade: delete absorbed particle PID={} E={} GeV",
-                    vParticle.GetPID(), vParticle.GetEnergy() / 1_GeV);
-        if (!vParticle.isDeleted()) vParticle.Delete();
-        return;
-      }
-
-      C8LOG_DEBUG("sth. happening before geometric limit ? {}",
-                  ((min_distance < geomMaxLength) ? "yes" : "no"));
-
-      if (min_distance < geomMaxLength) { // interaction to happen within geometric 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)
-
-        TStackView secondaries(vParticle);
-
-        if (min_distance < continuous_max_dist) {
-          /*
-            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
-            if (secondaries.getSize() == 1 &&
-                projectile.GetPID() == secondaries.GetNextParticle().GetPID())
-              throw std::runtime_error(
-                  fmt::format("Cascade: {} decayed into itself!",
-                              particles::GetName(projectile.GetPID())));
-          }
-
-          process_sequence_.DoSecondaries(secondaries);
-          vParticle.Delete();
-
-        } else { // step-length limitation within volume
-          C8LOG_DEBUG("step-length limitation");
-          // no extra physics happens here. just proceed to next step.
-        }
-
-        [[maybe_unused]] auto const assertion = [&] {
-          auto const* numericalNodeAfterStep =
-              environment_.GetUniverse()->GetContainingNode(vParticle.GetPosition());
-          C8LOG_TRACE("Geometry check: numericalNodeAfterStep={} currentLogicalNode={}",
-                      fmt::ptr(numericalNodeAfterStep), fmt::ptr(currentLogicalNode));
-          return numericalNodeAfterStep == currentLogicalNode;
-        };
-        assert(assertion()); // numerical and logical nodes should
-                             // match, we did not cross any volume
-                             // boundary
-
-      } else { // boundary crossing, step is limited by volume boundary
-
-	if (nextVol != currentLogicalNode) {
-	
-	  C8LOG_DEBUG("volume boundary crossing to {}", fmt::ptr(nextVol));
-
-	  if (nextVol == environment_.GetUniverse().get()) {
-	    C8LOG_DEBUG("particle left physics world, is now in unknown space -> delete");
-	    vParticle.Delete();
-	  }
-	  vParticle.SetNode(nextVol);
-	  /*
-	    DoBoundary may delete the particle (or not)
-	    
-	    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.
-	    
-	    todo: this must be fixed.
-	  */
-	  process_sequence_.DoBoundaryCrossing(vParticle, *currentLogicalNode, *nextVol);
-	}
-      }
-    }
-
-    process::EProcessReturn decay(TStackView& view) {
-      C8LOG_DEBUG("decay");
-      units::si::InverseTimeType const actual_decay_time =
-          process_sequence_.GetInverseLifetime(view.parent());
-
-      random::UniformRealDistribution<units::si::InverseTimeType> uniDist(
-          actual_decay_time);
-      const auto sample_process = uniDist(rng_);
-      auto const returnCode = process_sequence_.SelectDecay(view, sample_process);
-      if (returnCode != process::EProcessReturn::eDecayed) {
-        C8LOG_WARN("Particle did not decay!");
-      }
-      SetEventType(view, history::EventType::Decay);
-      return returnCode;
-    }
-
-    process::EProcessReturn interaction(TStackView& view) {
-      C8LOG_DEBUG("collide");
-
-      units::si::InverseGrammageType const current_inv_length =
-          process_sequence_.GetInverseInteractionLength(view.parent());
-
-      random::UniformRealDistribution<units::si::InverseGrammageType> uniDist(
-          current_inv_length);
-      const auto sample_process = uniDist(rng_);
-      auto const returnCode = process_sequence_.SelectInteraction(view, sample_process);
-      if (returnCode != process::EProcessReturn::eInteracted) {
-        C8LOG_WARN("Particle did not interact!");
-      }
-      SetEventType(view, history::EventType::Interaction);
-      return returnCode;
-    }
-
-    /**
-     * set the nodes for all particles on the stack according to their numerical
-     * position
-     */
-    void setNodes() {
-      std::for_each(stack_.begin(), stack_.end(), [&](auto& p) {
-        auto const* numericalNode =
-            environment_.GetUniverse()->GetContainingNode(p.GetPosition());
-        p.SetNode(numericalNode);
-      });
-    }
-
-    void SetEventType(TStackView& view, [[maybe_unused]] history::EventType eventType) {
-      if constexpr (TStackView::has_event) {
-        for (auto&& sec : view) { sec.GetEvent()->setEventType(eventType); }
-      }
-    }
-
-    // but this here temporarily. Should go into dedicated file later:
-    const char* c8_ascii_ =
-        R"V0G0N(
-  ,ad8888ba,     ,ad8888ba,    88888888ba    ad88888ba   88  88      a8P          db              ad88888ba   
- d8"'    `"8b   d8"'    `"8b   88      "8b  d8"     "8b  88  88    ,88'          d88b            d8"     "8b  
-d8'            d8'        `8b  88      ,8P  Y8,          88  88  ,88"           d8'`8b           Y8a     a8P  
-88             88          88  88aaaaaa8P'  `Y8aaaaa,    88  88,d88'           d8'  `8b           "Y8aaa8P"   
-88             88          88  88""""88'      `"""""8b,  88  8888"88,         d8YaaaaY8b          ,d8"""8b,   
-Y8,            Y8,        ,8P  88    `8b            `8b  88  88P   Y8b       d8""""""""8b        d8"     "8b  
- Y8a.    .a8P   Y8a.    .a8P   88     `8b   Y8a     a8P  88  88     "88,    d8'        `8b       Y8a     a8P  
-  `"Y8888Y"'     `"Y8888Y"'    88      `8b   "Y88888P"   88  88       Y8b  d8'          `8b       "Y88888P"
-	)V0G0N";
-
-  private:
-    // Data members
-    corsika::environment::Environment<MediumInterface> const& environment_;
-    TTracking& tracking_;
-    TProcessList& process_sequence_;
-    TStack& stack_;
-    corsika::random::RNG& rng_ =
-        corsika::random::RNGManager::GetInstance().GetRandomStream("cascade");
-    unsigned int count_ = 0;
-
-  }; // end class Cascade
-
-} // namespace corsika::cascade
diff --git a/Framework/Cascade/testCascade.cc b/Framework/Cascade/testCascade.cc
deleted file mode 100644
index a4863d65e68161607b460b87a943382bff677537..0000000000000000000000000000000000000000
--- a/Framework/Cascade/testCascade.cc
+++ /dev/null
@@ -1,194 +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/cascade/testCascade.h>
-
-#include <corsika/cascade/Cascade.h>
-
-#include <corsika/process/ProcessSequence.h>
-#include <corsika/process/NullModel.h>
-#include <corsika/process/stack_inspector/StackInspector.h>
-
-#include <corsika/particles/ParticleProperties.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-
-#include <corsika/environment/HomogeneousMedium.h>
-#include <corsika/environment/NuclearComposition.h>
-
-#include <catch2/catch.hpp>
-
-using namespace corsika;
-using namespace corsika::process;
-using namespace corsika::units;
-using namespace corsika::units::si;
-using namespace corsika::geometry;
-
-#include <limits>
-using namespace std;
-
-/**
- * testCascade implements an e.m. Heitler model with energy splitting
- * and a critical energy.
- *
- * It resembles one of the most simple cascades you can simulate with CORSIKA8.
- **/
-
-/*
-  The dummy env (here) doesn't need to have any propoerties
- */
-auto MakeDummyEnv() {
-  TestEnvironmentType env; // dummy environment
-  auto& universe = *(env.GetUniverse());
-
-  auto world = TestEnvironmentType::CreateNode<Sphere>(
-      Point{env.GetCoordinateSystem(), 0_m, 0_m, 0_m},
-      1_m * std::numeric_limits<double>::infinity());
-
-  using MyEmptyModel = environment::Empty<environment::IEmpty>;
-  world->SetModelProperties<MyEmptyModel>();
-
-  universe.AddChild(std::move(world));
-
-  return env;
-}
-
-/**
- * \class DummyTracking
- *
- * For the Heitler model we don't need particle transport.
- **/
-class DummyTracking {
-
-public:
-  template <typename TParticle>
-  auto GetTrack(TParticle const& particle) {
-    using namespace corsika::units::si;
-    using namespace corsika::geometry;
-    geometry::Vector<SpeedType::dimension_type> const initialVelocity =
-        particle.GetMomentum() / particle.GetEnergy() * corsika::units::constants::c;
-    return std::make_tuple(
-        geometry::LineTrajectory(
-            geometry::Line(particle.GetPosition(), initialVelocity),
-            std::numeric_limits<TimeType::value_type>::infinity() * 1_s), // trajectory,
-                                                                          // just
-                                                                          // go
-                                                                          // ahead
-                                                                          // forever
-        particle.GetNode()); // next volume node
-  }
-};
-
-class ProcessSplit : public process::InteractionProcess<ProcessSplit> {
-
-  int fCalls = 0;
-
-public:
-  template <typename Particle>
-  corsika::units::si::GrammageType GetInteractionLength(Particle const&) const {
-    return 0_g / square(1_cm);
-  }
-
-  template <typename TSecondaryView>
-  corsika::process::EProcessReturn DoInteraction(TSecondaryView& view) {
-    fCalls++;
-    auto const projectile = view.GetProjectile();
-    const HEPEnergyType E = projectile.GetEnergy();
-    view.AddSecondary(std::make_tuple(projectile.GetPID(), E / 2,
-                                      projectile.GetMomentum(), projectile.GetPosition(),
-                                      projectile.GetTime()));
-    view.AddSecondary(std::make_tuple(projectile.GetPID(), E / 2,
-                                      projectile.GetMomentum(), projectile.GetPosition(),
-                                      projectile.GetTime()));
-    return EProcessReturn::eInteracted;
-  }
-
-  int GetCalls() const { return fCalls; }
-};
-
-class ProcessCut : public process::SecondariesProcess<ProcessCut> {
-
-  int fCount = 0;
-  int fCalls = 0;
-  HEPEnergyType fEcrit;
-
-public:
-  ProcessCut(HEPEnergyType e)
-      : fEcrit(e) {}
-
-  template <typename TStack>
-  EProcessReturn DoSecondaries(TStack& vS) {
-    fCalls++;
-    auto p = vS.begin();
-    while (p != vS.end()) {
-      HEPEnergyType E = p.GetEnergy();
-      if (E < fEcrit) {
-        p.Delete();
-        fCount++;
-      }
-      ++p; // next particle
-    }
-    C8LOG_INFO(fmt::format("ProcessCut::DoSecondaries size={} count={}", vS.getEntries(),
-                           fCount));
-    return EProcessReturn::eOk;
-  }
-
-  int GetCount() const { return fCount; }
-  int GetCalls() const { return fCalls; }
-};
-
-TEST_CASE("Cascade", "[Cascade]") {
-
-  logging::SetLevel(logging::level::trace);
-
-  HEPEnergyType E0 = 100_GeV;
-
-  random::RNGManager& rmng = random::RNGManager::GetInstance();
-  rmng.RegisterRandomStream("cascade");
-
-  auto env = MakeDummyEnv();
-  auto const& rootCS = env.GetCoordinateSystem();
-
-  stack_inspector::StackInspector<TestCascadeStack> stackInspect(1, true, E0);
-  process::NullModel nullModel;
-
-  const HEPEnergyType Ecrit = 85_MeV;
-  ProcessSplit split;
-  ProcessCut cut(Ecrit);
-  auto sequence = process::sequence(nullModel, stackInspect, split, cut);
-  TestCascadeStack stack;
-  stack.Clear();
-  stack.AddParticle(std::make_tuple(
-      particles::Code::Electron, E0,
-      corsika::stack::MomentumVector(
-          rootCS, {0_GeV, 0_GeV,
-                   -sqrt(E0 * E0 - units::static_pow<2>(
-                                       particles::GetMass(particles::Code::Electron)))}),
-      Point(rootCS, {0_m, 0_m, 10_km}), 0_ns));
-
-  DummyTracking tracking;
-  cascade::Cascade<DummyTracking, decltype(sequence), TestCascadeStack,
-                   TestCascadeStackView>
-      EAS(env, tracking, sequence, stack);
-
-  SECTION("full cascade") {
-    EAS.Run();
-
-    CHECK(cut.GetCount() == 2048);
-    CHECK(cut.GetCalls() == 2047); // final particle is still on stack and not yet deleted
-    CHECK(split.GetCalls() == 2047);
-  }
-
-  SECTION("forced interaction") {
-    EAS.forceInteraction();
-    CHECK(stack.getEntries() == 2);
-    CHECK(split.GetCalls() == 1);
-  }
-}
diff --git a/Framework/Cascade/testCascade.h b/Framework/Cascade/testCascade.h
deleted file mode 100644
index e057441433a58f24e374ee925497fc445ab3cae9..0000000000000000000000000000000000000000
--- a/Framework/Cascade/testCascade.h
+++ /dev/null
@@ -1,45 +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/environment/Environment.h>
-#include <corsika/environment/IEmpty.hpp>
-
-#include <corsika/stack/CombinedStack.h>
-#include <corsika/stack/SecondaryView.h>
-#include <corsika/stack/node/GeometryNodeStackExtension.h>
-#include <corsika/stack/nuclear_extension/NuclearStackExtension.h>
-
-using TestEnvironmentInterface = corsika::environment::IEmpty;
-using TestEnvironmentType = corsika::environment::Environment<TestEnvironmentInterface>;
-
-template <typename T>
-using SetupGeometryDataInterface =
-    corsika::stack::node::GeometryDataInterface<T, TestEnvironmentType>;
-
-// combine particle data stack with geometry information for tracking
-template <typename StackIter>
-using StackWithGeometryInterface = corsika::stack::CombinedParticleInterface<
-    corsika::stack::nuclear_extension::ParticleDataStack::MPIType,
-    SetupGeometryDataInterface, StackIter>;
-
-using TestCascadeStack = corsika::stack::CombinedStack<
-    typename corsika::stack::nuclear_extension::ParticleDataStack::StackImpl,
-    corsika::stack::node::GeometryData<TestEnvironmentType>, StackWithGeometryInterface>;
-
-/*
-  See also Issue 161
-*/
-#if defined(__clang__)
-using TestCascadeStackView =
-    corsika::stack::SecondaryView<typename TestCascadeStack::StackImpl,
-                                  StackWithGeometryInterface>;
-#elif defined(__GNUC__) || defined(__GNUG__)
-using TestCascadeStackView = corsika::stack::MakeView<TestCascadeStack>::type;
-#endif
diff --git a/Framework/Geometry/BaseTrajectory.h b/Framework/Geometry/BaseTrajectory.h
deleted file mode 100644
index 6d61e2eeb820064afc1d7f51c9b991e930960e93..0000000000000000000000000000000000000000
--- a/Framework/Geometry/BaseTrajectory.h
+++ /dev/null
@@ -1,59 +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/geometry/Point.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <string>
-
-namespace corsika::geometry {
-
-  /*!
-   * Interface / base class for trajectories.
-   */
-  class BaseTrajectory {
-
-    BaseTrajectory() = delete;
-
-  public:
-    BaseTrajectory(corsika::units::si::TimeType start, corsika::units::si::TimeType end)
-        : fTStart(start)
-        , fTEnd(end) {}
-
-    //!< for \f$ t = 0 \f$, the starting Point shall be returned.
-    virtual Point GetPosition(corsika::units::si::TimeType) const = 0;
-
-    //!< the Point is return from u=0 (start) to u=1 (end)
-    virtual Point GetPosition(double u) const = 0;
-
-    /*!
-     * returns the length between two points of the trajectory
-     * parameterized by \arg t1 and \arg t2. Requires \arg t2 > \arg t1.
-     */
-
-    virtual corsika::units::si::TimeType TimeFromArclength(
-        corsika::units::si::LengthType) const = 0;
-
-    virtual LengthType ArcLength(corsika::units::si::TimeType t1,
-                                 corsika::units::si::TimeType t2) const = 0;
-
-    virtual corsika::units::si::TimeType GetDuration(
-        corsika::units::si::TimeType t1, corsika::units::si::TimeType t2) const {
-      return t2 - t1;
-    }
-
-    virtual Point GetEndpoint() const { return GetPosition(fTEnd); }
-    virtual Point GetStartpoint() const { return GetPosition(fTStart); }
-
-  protected:
-    corsika::units::si::TimeType const fTStart, fTEnd;
-  };
-
-} // namespace corsika::geometry
diff --git a/Framework/Geometry/BaseVector.h b/Framework/Geometry/BaseVector.h
deleted file mode 100644
index f66ee84e2e6d4888d2d848a76590dcbf522ca981..0000000000000000000000000000000000000000
--- a/Framework/Geometry/BaseVector.h
+++ /dev/null
@@ -1,34 +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/geometry/CoordinateSystem.h>
-#include <corsika/geometry/QuantityVector.h>
-
-namespace corsika::geometry {
-
-  /*!
-   * Common base class for Vector and Point. Currently it does basically nothing.
-   */
-
-  template <typename dim>
-  class BaseVector {
-  protected:
-    QuantityVector<dim> qVector;
-    CoordinateSystem const* cs;
-
-  public:
-    BaseVector(CoordinateSystem const& pCS, QuantityVector<dim> pQVector)
-        : qVector(pQVector)
-        , cs(&pCS) {}
-
-    auto const& GetCoordinateSystem() const { return *cs; }
-  };
-
-} // namespace corsika::geometry
diff --git a/Framework/Geometry/CMakeLists.txt b/Framework/Geometry/CMakeLists.txt
deleted file mode 100644
index d302289b0396ed786d3a9c95d3b2aed49145c413..0000000000000000000000000000000000000000
--- a/Framework/Geometry/CMakeLists.txt
+++ /dev/null
@@ -1,78 +0,0 @@
-set (
-  GEOMETRY_SOURCES
-  CoordinateSystem.cc
-  )
-
-set (
-  GEOMETRY_HEADERS
-  Vector.h
-  Point.h
-  Line.h
-  Sphere.h
-  Plane.h
-  Volume.h
-  CoordinateSystem.h
-  RootCoordinateSystem.h
-  Helix.h
-  BaseVector.h
-  QuantityVector.h
-  Trajectory.h
-  FourVector.h
-  Intersections.hpp
-  )
-
-set (
-  GEOMETRY_NAMESPACE
-  corsika/geometry
-  )
-
-add_library (CORSIKAgeometry STATIC ${GEOMETRY_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAgeometry ${GEOMETRY_NAMESPACE} ${GEOMETRY_HEADERS})
-
-set_target_properties (
-  CORSIKAgeometry
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  PUBLIC_HEADER "${GEOMETRY_HEADERS}"
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  CORSIKAgeometry
-  CORSIKAunits
-  CORSIKAutilities
-  C8::ext::eigen3
-  )
-
-target_include_directories (
-  CORSIKAgeometry
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS CORSIKAgeometry
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-  PUBLIC_HEADER DESTINATION include/${GEOMETRY_NAMESPACE}
-  )
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST(testGeometry)
-target_link_libraries (
-  testGeometry
-  CORSIKAgeometry
-  CORSIKAunits
-  CORSIKAtesting
-  )
-
-CORSIKA_ADD_TEST(testFourVector)
-target_link_libraries (
-  testFourVector
-  CORSIKAgeometry
-  CORSIKAunits
-  CORSIKAtesting
-  )
diff --git a/Framework/Geometry/CoordinateSystem.cc b/Framework/Geometry/CoordinateSystem.cc
deleted file mode 100644
index e822267277f9542ba64d7e745691aee986eac62d..0000000000000000000000000000000000000000
--- a/Framework/Geometry/CoordinateSystem.cc
+++ /dev/null
@@ -1,60 +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/geometry/CoordinateSystem.h>
-#include <stdexcept>
-
-using namespace corsika::geometry;
-
-/**
- * returns the transformation matrix necessary to transform primitives with coordinates
- * in \a pFrom to \a pTo, e.g.
- * \f$ \vec{v}^{\text{(to)}} = \mathcal{M} \vec{v}^{\text{(from)}} \f$
- * (\f$ \vec{v}^{(.)} \f$ denotes the coordinates/components of the component in
- * the indicated CoordinateSystem).
- */
-EigenTransform CoordinateSystem::GetTransformation(CoordinateSystem const& pFrom,
-                                                   CoordinateSystem const& pTo) {
-  CoordinateSystem const* a{&pFrom};
-  CoordinateSystem const* b{&pTo};
-  CoordinateSystem const* commonBase{nullptr};
-
-  while (a != b && b != nullptr) {
-    a = &pFrom;
-
-    while (a != b && a != nullptr) { a = a->GetReference(); }
-
-    if (a == b) break;
-
-    b = b->GetReference();
-  }
-
-  if (a == b && a != nullptr) {
-    commonBase = a;
-
-  } else {
-    throw std::runtime_error("no connection between coordinate systems found!");
-  }
-
-  EigenTransform t = EigenTransform::Identity();
-  auto* p = &pFrom;
-
-  while (p != commonBase) {
-    t = p->GetTransform() * t;
-    p = p->GetReference();
-  }
-
-  p = &pTo;
-
-  while (p != commonBase) {
-    t = t * p->GetTransform().inverse(Eigen::TransformTraits::Isometry);
-    p = p->GetReference();
-  }
-
-  return t;
-}
diff --git a/Framework/Geometry/CoordinateSystem.h b/Framework/Geometry/CoordinateSystem.h
deleted file mode 100644
index bc9ecd9a2bcacf5625437aa4ffab19ec9c8a171b..0000000000000000000000000000000000000000
--- a/Framework/Geometry/CoordinateSystem.h
+++ /dev/null
@@ -1,129 +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/geometry/QuantityVector.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/sgn.h>
-#include <Eigen/Dense>
-#include <stdexcept>
-
-typedef Eigen::Transform<double, 3, Eigen::Affine> EigenTransform;
-typedef Eigen::Translation<double, 3> EigenTranslation;
-
-namespace corsika::geometry {
-
-  class RootCoordinateSystem;
-  template <typename T>
-  class Vector;
-
-  using corsika::units::si::length_d;
-
-  class CoordinateSystem {
-    CoordinateSystem const* reference = nullptr;
-    EigenTransform transf;
-
-    CoordinateSystem()
-        : // for creating the root CS
-        transf(EigenTransform::Identity()) {}
-
-  protected:
-    static auto CreateCS() { return CoordinateSystem(); }
-    friend corsika::geometry::RootCoordinateSystem; /// this is the only class that can
-                                                    /// create ONE unique root CS
-
-  public:
-    static EigenTransform GetTransformation(CoordinateSystem const& c1,
-                                            CoordinateSystem const& c2);
-
-    CoordinateSystem(CoordinateSystem const& reference, EigenTransform const& transf)
-        : reference(&reference)
-        , transf(transf) {}
-
-    auto& operator=(const CoordinateSystem& pCS) {
-      reference = pCS.reference;
-      transf = pCS.transf;
-      return *this;
-    }
-
-    auto translate(QuantityVector<length_d> vector) const {
-      EigenTransform const translation{EigenTranslation(vector.eVector)};
-
-      return CoordinateSystem(*this, translation);
-    }
-
-    /**
-     * creates a new CS in which vVec points in direction of the new z-axis
-     */
-    template <typename TDim>
-    auto RotateToZ(Vector<TDim> vVec) const {
-      auto const a = vVec.normalized().GetComponents(*this).eVector;
-      auto const a1 = a(0), a2 = a(1);
-
-      auto const s = utl::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,                      // .
-            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;  // .
-      }
-
-      return CoordinateSystem(*this, EigenTransform(A + B));
-    }
-
-    template <typename TDim>
-    auto rotate(QuantityVector<TDim> axis, double angle) const {
-      if (axis.eVector.isZero()) {
-        throw std::runtime_error("null-vector given as axis parameter");
-      }
-
-      EigenTransform const rotation{Eigen::AngleAxisd(angle, axis.eVector.normalized())};
-
-      return CoordinateSystem(*this, rotation);
-    }
-
-    template <typename TDim>
-    auto translateAndRotate(QuantityVector<phys::units::length_d> translation,
-                            QuantityVector<TDim> axis, double angle) {
-      if (axis.eVector.isZero()) {
-        throw std::runtime_error("null-vector given as axis parameter");
-      }
-
-      EigenTransform const transf{Eigen::AngleAxisd(angle, axis.eVector.normalized()) *
-                                  EigenTranslation(translation.eVector)};
-
-      return CoordinateSystem(*this, transf);
-    }
-
-    auto const* GetReference() const { return reference; }
-
-    auto const& GetTransform() const { return transf; }
-
-    bool operator==(CoordinateSystem const& cs) const {
-      return reference == cs.reference && transf.matrix() == cs.transf.matrix();
-    }
-
-    bool operator!=(CoordinateSystem const& cs) const { return !(cs == *this); }
-  };
-
-} // namespace corsika::geometry
diff --git a/Framework/Geometry/FourVector.h b/Framework/Geometry/FourVector.h
deleted file mode 100644
index 2ea2b28d42616f07545742d2589528680cd2658c..0000000000000000000000000000000000000000
--- a/Framework/Geometry/FourVector.h
+++ /dev/null
@@ -1,214 +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/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <iostream>
-
-namespace corsika::geometry {
-
-  /**
-     FourVector supports "full" units, e.g. E in [GeV/c] and p in [GeV],
-     or also t in [s] and r in [m], etc.
-
-     However, for HEP applications it is also possible to use E and p
-     both in [GeV].
-
-     The FourVector can return NormSqr and Norm, whereas Norm is
-     sqrt(abs(NormSqr)). The physical units are always calculated and
-     returned properly.
-
-     FourVector can also return if it is TimeLike, SpaceLike or PhotonLike.
-
-     When a FourVector is initialized with a lvalue reference, this is
-     also used for the internal storage, which should lead to complete
-     disappearance of the FourVector class during optimization.
-   */
-
-  template <typename TimeType, typename SpaceVecType>
-  class FourVector {
-
-  public:
-    using SpaceType = typename std::decay<SpaceVecType>::type::Quantity;
-
-    //! check the types and the physical units here:
-    static_assert(
-        std::is_same<typename std::decay<TimeType>::type, SpaceType>::value ||
-            std::is_same<typename std::decay<TimeType>::type,
-                         decltype(std::declval<SpaceType>() / corsika::units::si::meter *
-                                  corsika::units::si::second)>::value,
-        "Units of time-like and space-like coordinates must either be idential "
-        "(e.g. GeV) or [E/c]=[p]");
-
-  public:
-    FourVector(const TimeType& eT, const SpaceVecType& eS)
-        : fTimeLike(eT)
-        , fSpaceLike(eS) {}
-
-    TimeType GetTimeLikeComponent() const { return fTimeLike; }
-    SpaceVecType& GetSpaceLikeComponents() { return fSpaceLike; }
-    const SpaceVecType& GetSpaceLikeComponents() const { return fSpaceLike; }
-
-    auto GetNormSqr() const { return GetTimeSquared() - fSpaceLike.squaredNorm(); }
-
-    SpaceType GetNorm() const { return sqrt(abs(GetNormSqr())); }
-
-    bool IsTimelike() const {
-      return GetTimeSquared() < fSpaceLike.squaredNorm();
-    } //! Norm2 < 0
-
-    bool IsSpacelike() const {
-      return GetTimeSquared() > fSpaceLike.squaredNorm();
-    } //! Norm2 > 0
-
-    /* this is not numerically stable
-    bool IsPhotonlike() const {
-      return GetTimeSquared() == fSpaceLike.squaredNorm();
-    } //! Norm2 == 0
-    */
-
-    FourVector& operator+=(const FourVector& b) {
-      fTimeLike += b.fTimeLike;
-      fSpaceLike += b.fSpaceLike;
-      return *this;
-    }
-
-    FourVector& operator-=(const FourVector& b) {
-      fTimeLike -= b.fTimeLike;
-      fSpaceLike -= b.fSpaceLike;
-      return *this;
-    }
-
-    FourVector& operator*=(const double b) {
-      fTimeLike *= b;
-      fSpaceLike *= b;
-      return *this;
-    }
-
-    FourVector& operator/=(const double b) {
-      fTimeLike /= b;
-      fSpaceLike.GetComponents() /= b; // TODO: WHY IS THIS??????
-      return *this;
-    }
-
-    FourVector& operator/(const double b) {
-      *this /= b;
-      return *this;
-    }
-
-    /**
-       Note that the product between two 4-vectors assumes that you use
-       the same "c" convention for both. Only the LHS vector is checked
-       for this. You cannot mix different conventions due to
-       unit-checking.
-     */
-    SpaceType operator*(const FourVector& b) {
-      if constexpr (std::is_same<typename std::decay<TimeType>::type,
-                                 decltype(std::declval<SpaceType>() /
-                                          corsika::units::si::meter *
-                                          corsika::units::si::second)>::value)
-        return fTimeLike * b.fTimeLike *
-                   (corsika::units::constants::c * corsika::units::constants::c) -
-               fSpaceLike.norm();
-      else
-        return fTimeLike * fTimeLike - fSpaceLike.norm();
-    }
-
-  private:
-    /**
-       This function is automatically compiled to use of ignore the
-       extra factor of "c" for the time-like quantity
-     */
-    auto GetTimeSquared() const {
-      if constexpr (std::is_same<typename std::decay<TimeType>::type,
-                                 decltype(std::declval<SpaceType>() /
-                                          corsika::units::si::meter *
-                                          corsika::units::si::second)>::value)
-        return fTimeLike * fTimeLike *
-               (corsika::units::constants::c * corsika::units::constants::c);
-      else
-        return fTimeLike * fTimeLike;
-    }
-
-  protected:
-    //! the data members
-    TimeType fTimeLike;
-    SpaceVecType fSpaceLike;
-
-    //! the friends: math operators
-    template <typename T, typename U>
-    friend FourVector<typename std::decay<T>::type, typename std::decay<U>::type>
-    operator+(const FourVector<T, U>&, const FourVector<T, U>&);
-
-    template <typename T, typename U>
-    friend FourVector<typename std::decay<T>::type, typename std::decay<U>::type>
-    operator-(const FourVector<T, U>&, const FourVector<T, U>&);
-
-    template <typename T, typename U>
-    friend FourVector<typename std::decay<T>::type, typename std::decay<U>::type>
-    operator*(const FourVector<T, U>&, const double);
-
-    template <typename T, typename U>
-    friend FourVector<typename std::decay<T>::type, typename std::decay<U>::type>
-    operator/(const FourVector<T, U>&, const double);
-  };
-
-  /**
-      The math operator+
-   */
-  template <typename TimeType, typename SpaceVecType>
-  inline FourVector<typename std::decay<TimeType>::type,
-                    typename std::decay<SpaceVecType>::type>
-  operator+(const FourVector<TimeType, SpaceVecType>& a,
-            const FourVector<TimeType, SpaceVecType>& b) {
-    return FourVector<typename std::decay<TimeType>::type,
-                      typename std::decay<SpaceVecType>::type>(
-        a.fTimeLike + b.fTimeLike, a.fSpaceLike + b.fSpaceLike);
-  }
-
-  /**
-     The math operator-
-  */
-  template <typename TimeType, typename SpaceVecType>
-  inline FourVector<typename std::decay<TimeType>::type,
-                    typename std::decay<SpaceVecType>::type>
-  operator-(const FourVector<TimeType, SpaceVecType>& a,
-            const FourVector<TimeType, SpaceVecType>& b) {
-    return FourVector<typename std::decay<TimeType>::type,
-                      typename std::decay<SpaceVecType>::type>(
-        a.fTimeLike - b.fTimeLike, a.fSpaceLike - b.fSpaceLike);
-  }
-
-  /**
-     The math operator*
-  */
-  template <typename TimeType, typename SpaceVecType>
-  inline FourVector<typename std::decay<TimeType>::type,
-                    typename std::decay<SpaceVecType>::type>
-  operator*(const FourVector<TimeType, SpaceVecType>& a, const double b) {
-    return FourVector<typename std::decay<TimeType>::type,
-                      typename std::decay<SpaceVecType>::type>(a.fTimeLike * b,
-                                                               a.fSpaceLike * b);
-  }
-
-  /**
-      The math operator/
-   */
-  template <typename TimeType, typename SpaceVecType>
-  inline FourVector<typename std::decay<TimeType>::type,
-                    typename std::decay<SpaceVecType>::type>
-  operator/(const FourVector<TimeType, SpaceVecType>& a, const double b) {
-    return FourVector<typename std::decay<TimeType>::type,
-                      typename std::decay<SpaceVecType>::type>(a.fTimeLike / b,
-                                                               a.fSpaceLike / b);
-  }
-
-} // namespace corsika::geometry
diff --git a/Framework/Geometry/Helix.h b/Framework/Geometry/Helix.h
deleted file mode 100644
index 5deaa082275d0e459cdd633b421bb3e685742392..0000000000000000000000000000000000000000
--- a/Framework/Geometry/Helix.h
+++ /dev/null
@@ -1,76 +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/geometry/Point.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <cmath>
-
-namespace corsika::geometry {
-  /*!
-   * A Helix is defined by the cyclotron frequency \f$ \omega_c \f$, the initial
-   * Point r0 and
-   * the velocity vectors \f$ \vec{v}_{\parallel} \f$ and \f$ \vec{v}_{\perp} \f$
-   * denoting the projections of the initial velocity \f$ \vec{v}_0 \f$ parallel
-   * and perpendicular to the axis \f$ \vec{B} \f$, respectively, i.e.
-   * \f{align*}{
-        \vec{v}_{\parallel} &= \frac{\vec{v}_0 \cdot \vec{B}}{\vec{B}^2} \vec{B} \\
-        \vec{v}_{\perp} &= \vec{v}_0 - \vec{v}_{\parallel}
-     \f}
-   */
-
-  class Helix {
-
-    using VelocityVec = Vector<corsika::units::si::SpeedType::dimension_type>;
-
-    Point const r0;
-    corsika::units::si::FrequencyType const omegaC;
-    VelocityVec const vPar;
-    VelocityVec const vPerp, uPerp;
-
-    corsika::units::si::LengthType const radius;
-
-  public:
-    Helix(Point const& pR0, corsika::units::si::FrequencyType pOmegaC,
-          VelocityVec const& pvPar, VelocityVec const& pvPerp)
-        : r0(pR0)
-        , omegaC(pOmegaC)
-        , vPar(pvPar)
-        , vPerp(pvPerp)
-        , uPerp(vPerp.cross(vPar.normalized()))
-        , radius(pvPar.norm() / abs(pOmegaC)) {}
-
-    Point GetPosition(corsika::units::si::TimeType t) const {
-      return r0 + vPar * t +
-             (vPerp * (cos(omegaC * t) - 1) + uPerp * sin(omegaC * t)) / omegaC;
-    }
-
-    VelocityVec GetVelocity(corsika::units::si::TimeType t) const {
-      return vPar + (vPerp * (cos(omegaC * t) - 1) + uPerp * sin(omegaC * t));
-    }
-
-    Point PositionFromArclength(corsika::units::si::LengthType l) const {
-      return GetPosition(TimeFromArclength(l));
-    }
-
-    auto GetRadius() const { return radius; }
-
-    corsika::units::si::LengthType ArcLength(corsika::units::si::TimeType t1,
-                                             corsika::units::si::TimeType t2) const {
-      return (vPar + vPerp).norm() * (t2 - t1);
-    }
-
-    corsika::units::si::TimeType TimeFromArclength(
-        corsika::units::si::LengthType l) const {
-      return l / (vPar + vPerp).norm();
-    }
-  };
-
-} // namespace corsika::geometry
diff --git a/Framework/Geometry/Line.h b/Framework/Geometry/Line.h
deleted file mode 100644
index f54b02dbd0914b94265b596107037fbac7d0ec43..0000000000000000000000000000000000000000
--- a/Framework/Geometry/Line.h
+++ /dev/null
@@ -1,64 +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/geometry/Point.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::geometry {
-
-  /**
-   * \class Line
-   *
-   * A Line describes a movement in three dimensional space. It
-   * consists of a Point `$\vec{p_0}$` and and a speed-Vector
-   * `$\vec{v}$`, so that it can return GetPosition as
-   * `$\vec{p_0}*\vec{v}*t$` for any value of time `$t$`.
-   *
-   **/
-
-  class Line {
-
-    using VelocityVec = Vector<corsika::units::si::SpeedType::dimension_type>;
-
-    Point const r0;
-    VelocityVec const v0;
-
-  public:
-    Line() = delete;
-    Line(const Line&) = default;
-    Line(Line&&) = default;
-    Line& operator=(const Line&) = delete;
-    Line(Point const& pR0, VelocityVec const& pV0)
-        : r0(pR0)
-        , v0(pV0) {}
-
-    Point GetPosition(corsika::units::si::TimeType t) const { return r0 + v0 * t; }
-    VelocityVec GetVelocity(corsika::units::si::TimeType) const { return v0; }
-
-    Point PositionFromArclength(corsika::units::si::LengthType l) const {
-      return r0 + v0.normalized() * l;
-    }
-
-    LengthType ArcLength(corsika::units::si::TimeType t1,
-                         corsika::units::si::TimeType t2) const {
-      return v0.norm() * (t2 - t1);
-    }
-
-    corsika::units::si::TimeType TimeFromArclength(
-        corsika::units::si::LengthType t) const {
-      return t / v0.norm();
-    }
-
-    auto GetR0() const { return r0; }
-    auto GetV0() const { return v0; }
-  };
-
-} // namespace corsika::geometry
diff --git a/Framework/Geometry/Plane.h b/Framework/Geometry/Plane.h
deleted file mode 100644
index 9a3b04ff45a5b48cded875474c6eaad9bb6d100b..0000000000000000000000000000000000000000
--- a/Framework/Geometry/Plane.h
+++ /dev/null
@@ -1,40 +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/geometry/Point.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::geometry {
-  class Plane {
-
-    using DimLessVec = Vector<corsika::units::si::dimensionless_d>;
-
-    Point const fCenter;
-    DimLessVec const fNormal;
-
-  public:
-    Plane(Point const& vCenter, DimLessVec const& vNormal)
-        : fCenter(vCenter)
-        , fNormal(vNormal.normalized()) {}
-
-    bool IsAbove(Point const& vP) const {
-      return fNormal.dot(vP - fCenter) > corsika::units::si::LengthType::zero();
-    }
-
-    units::si::LengthType DistanceTo(geometry::Point const& vP) const {
-      return (fNormal * (vP - fCenter).dot(fNormal)).norm();
-    }
-
-    Point const& GetCenter() const { return fCenter; }
-    DimLessVec const& GetNormal() const { return fNormal; }
-  };
-
-} // namespace corsika::geometry
diff --git a/Framework/Geometry/Point.h b/Framework/Geometry/Point.h
deleted file mode 100644
index 20e46032c12b341dfce300ff326c247f4da76b67..0000000000000000000000000000000000000000
--- a/Framework/Geometry/Point.h
+++ /dev/null
@@ -1,74 +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/geometry/BaseVector.h>
-#include <corsika/geometry/QuantityVector.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::geometry {
-
-  using corsika::units::si::length_d;
-  using corsika::units::si::LengthType;
-
-  /*!
-   * A Point represents a point in position space. It is defined by its
-   * coordinates with respect to some CoordinateSystem.
-   */
-  class Point : public BaseVector<length_d> {
-  public:
-    Point(CoordinateSystem const& pCS, QuantityVector<length_d> pQVector)
-        : BaseVector<length_d>(pCS, pQVector) {}
-
-    Point(CoordinateSystem const& cs, LengthType x, LengthType y, LengthType z)
-        : BaseVector<length_d>(cs, {x, y, z}) {}
-
-    // TODO: this should be private or protected, we don NOT want to expose numbers
-    // without reference to outside:
-    auto GetCoordinates() const { return BaseVector<length_d>::qVector; }
-    auto GetX() const { return BaseVector<length_d>::qVector.GetX(); }
-    auto GetY() const { return BaseVector<length_d>::qVector.GetY(); }
-    auto GetZ() const { return BaseVector<length_d>::qVector.GetZ(); }
-
-    /// this always returns a QuantityVector as triple
-    auto GetCoordinates(CoordinateSystem const& pCS) const {
-      if (&pCS == BaseVector<length_d>::cs) {
-        return BaseVector<length_d>::qVector;
-      } else {
-        return QuantityVector<length_d>(
-            CoordinateSystem::GetTransformation(*BaseVector<length_d>::cs, pCS) *
-            BaseVector<length_d>::qVector.eVector);
-      }
-    }
-
-    /*!
-     * transforms the Point into another CoordinateSystem by changing its
-     * coordinates interally
-     */
-    void rebase(CoordinateSystem const& pCS) {
-      BaseVector<length_d>::qVector = GetCoordinates(pCS);
-      BaseVector<length_d>::cs = &pCS;
-    }
-
-    Point operator+(Vector<length_d> const& pVec) const {
-      return Point(*BaseVector<length_d>::cs,
-                   GetCoordinates() + pVec.GetComponents(*BaseVector<length_d>::cs));
-    }
-
-    /*!
-     * returns the distance Vector between two points
-     */
-    Vector<length_d> operator-(Point const& pB) const {
-      auto& cs = *BaseVector<length_d>::cs;
-      return Vector<length_d>(cs, GetCoordinates() - pB.GetCoordinates(cs));
-    }
-  };
-
-} // namespace corsika::geometry
diff --git a/Framework/Geometry/QuantityVector.h b/Framework/Geometry/QuantityVector.h
deleted file mode 100644
index b45138bf76eeb7a7a3e653a9df3f7e7a0a828930..0000000000000000000000000000000000000000
--- a/Framework/Geometry/QuantityVector.h
+++ /dev/null
@@ -1,138 +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/units/PhysicalUnits.h>
-
-#include <Eigen/Dense>
-
-#include <iostream>
-#include <utility>
-
-namespace corsika::geometry {
-
-  /*!
-   * A QuantityVector is a three-component container based on Eigen::Vector3d
-   * with a phys::units::si::dimension. Arithmethic operators are defined that
-   * propagate the dimensions by dimensional analysis.
-   */
-
-  template <typename dim>
-  class QuantityVector {
-  public:
-    using Quantity = phys::units::quantity<dim, double>; //< the phys::units::quantity
-                                                         // corresponding to the dimension
-
-  public:
-    Eigen::Vector3d eVector; //!< the actual container where the raw numbers are stored
-
-    typedef dim dimension; //!< should be a phys::units::dimension
-
-    QuantityVector(Quantity a, Quantity b, Quantity c)
-        : eVector{a.magnitude(), b.magnitude(), c.magnitude()} {}
-
-    QuantityVector(double a, double b, double c)
-        : eVector{a, b, c} {
-      static_assert(
-          std::is_same_v<dim, phys::units::dimensionless_d>,
-          "initialization of dimensionful QuantityVector with pure numbers not allowed!");
-    }
-
-    QuantityVector(Eigen::Vector3d pBareVector)
-        : eVector(pBareVector) {}
-
-    auto operator[](size_t index) const {
-      return Quantity(phys::units::detail::magnitude_tag, eVector[index]);
-    }
-
-    auto GetX() const { return (*this)[0]; }
-
-    auto GetY() const { return (*this)[1]; }
-
-    auto GetZ() const { return (*this)[2]; }
-
-    auto norm() const {
-      return Quantity(phys::units::detail::magnitude_tag, eVector.norm());
-    }
-
-    auto squaredNorm() const {
-      using QuantitySquared =
-          decltype(std::declval<Quantity>() * std::declval<Quantity>());
-      return QuantitySquared(phys::units::detail::magnitude_tag, eVector.squaredNorm());
-    }
-
-    auto operator+(QuantityVector<dim> const& pQVec) const {
-      return QuantityVector<dim>(eVector + pQVec.eVector);
-    }
-
-    auto operator-(QuantityVector<dim> const& pQVec) const {
-      return QuantityVector<dim>(eVector - pQVec.eVector);
-    }
-
-    template <typename ScalarDim>
-    auto operator*(phys::units::quantity<ScalarDim, double> const p) const {
-      using ResQuantity = phys::units::detail::Product<ScalarDim, dim, double, double>;
-
-      if constexpr (std::is_same<ResQuantity, double>::value) // result dimensionless, not
-                                                              // a "Quantity" anymore
-      {
-        return QuantityVector<phys::units::dimensionless_d>(eVector * p.magnitude());
-      } else {
-        return QuantityVector<typename ResQuantity::dimension_type>(eVector *
-                                                                    p.magnitude());
-      }
-    }
-
-    template <typename ScalarDim>
-    auto operator/(phys::units::quantity<ScalarDim, double> const p) const {
-      return (*this) * (1 / p);
-    }
-
-    auto operator*(double const p) const { return QuantityVector<dim>(eVector * p); }
-
-    auto operator/(double const p) const { return QuantityVector<dim>(eVector / p); }
-
-    auto& operator/=(double const p) {
-      eVector /= p;
-      return *this;
-    }
-
-    auto& operator*=(double const p) {
-      eVector *= p;
-      return *this;
-    }
-
-    auto& operator+=(QuantityVector<dim> const& pQVec) {
-      eVector += pQVec.eVector;
-      return *this;
-    }
-
-    auto& operator-=(QuantityVector<dim> const& pQVec) {
-      eVector -= pQVec.eVector;
-      return *this;
-    }
-
-    auto& operator-() const { return QuantityVector<dim>(-eVector); }
-
-    auto normalized() const { return QuantityVector<dim>(eVector.normalized()); }
-
-    auto operator==(QuantityVector<dim> const& p) const { return eVector == p.eVector; }
-  };
-
-  template <typename dim>
-  auto& operator<<(std::ostream& os, corsika::geometry::QuantityVector<dim> qv) {
-    using Quantity = phys::units::quantity<dim, double>;
-
-    os << '(' << qv.eVector(0) << ' ' << qv.eVector(1) << ' ' << qv.eVector(2) << ") "
-       << phys::units::to_unit_symbol<dim, double>(
-              Quantity(phys::units::detail::magnitude_tag, 1));
-    return os;
-  }
-
-} // namespace corsika::geometry
diff --git a/Framework/Geometry/RootCoordinateSystem.h b/Framework/Geometry/RootCoordinateSystem.h
deleted file mode 100644
index a2208fd716c11279dcdf119220b02b8d3e99e6d6..0000000000000000000000000000000000000000
--- a/Framework/Geometry/RootCoordinateSystem.h
+++ /dev/null
@@ -1,40 +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/utl/Singleton.h>
-
-#include <corsika/geometry/CoordinateSystem.h>
-
-/*!
- * This is the only way to get a root-coordinate system, and it is a
- * singleton. All other CoordinateSystems must be relative to the
- * RootCoordinateSystem
- */
-
-namespace corsika::geometry {
-
-  class RootCoordinateSystem : public corsika::utl::Singleton<RootCoordinateSystem> {
-
-    friend class corsika::utl::Singleton<RootCoordinateSystem>;
-
-  protected:
-    RootCoordinateSystem() {}
-
-  public:
-    corsika::geometry::CoordinateSystem& GetRootCoordinateSystem() { return fRootCS; }
-    const corsika::geometry::CoordinateSystem& GetRootCoordinateSystem() const {
-      return fRootCS;
-    }
-
-  private:
-    corsika::geometry::CoordinateSystem fRootCS; // THIS IS IT
-  };
-
-} // namespace corsika::geometry
diff --git a/Framework/Geometry/Sphere.h b/Framework/Geometry/Sphere.h
deleted file mode 100644
index a2a77efec78e2d6c3b063185c73be8423c265c5a..0000000000000000000000000000000000000000
--- a/Framework/Geometry/Sphere.h
+++ /dev/null
@@ -1,36 +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/geometry/Point.h>
-#include <corsika/geometry/Volume.h>
-#include <corsika/geometry/Line.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::geometry {
-
-  class Sphere : public Volume {
-    Point const fCenter;
-    LengthType const fRadius;
-
-  public:
-    Sphere(Point const& pCenter, LengthType const pRadius)
-        : fCenter(pCenter)
-        , fRadius(pRadius) {}
-
-    //! returns true if the Point p is within the sphere
-    bool Contains(Point const& p) const override {
-      return fRadius * fRadius > (fCenter - p).squaredNorm();
-    }
-
-    const Point& GetCenter() const { return fCenter; }
-    LengthType GetRadius() const { return fRadius; }
-  };
-
-} // namespace corsika::geometry
diff --git a/Framework/Geometry/Trajectory.h b/Framework/Geometry/Trajectory.h
deleted file mode 100644
index 1580a0db36b6c8e6d973513ac27916bc27a0498e..0000000000000000000000000000000000000000
--- a/Framework/Geometry/Trajectory.h
+++ /dev/null
@@ -1,261 +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/geometry/Line.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::geometry {
-
-  /**
-   * \class LineTrajectory
-   *
-   * A Trajectory is a description of a momvement of an object in
-   * three-dimensional space that describes the trajectory (connection
-   * between two Points in space), as well as the direction of motion
-   * at any given point.
-   *
-   * A Trajectory has a start `0` and an end `1`, where
-   * e.g. GetPosition(0) returns the start point and GetDirection(1)
-   * the direction of motion at the end. Values outside 0...1 are not
-   * defined.
-   *
-   * A Trajectory has a length in [m], GetLength, a duration in [s], GetDuration.
-   *
-   * Note: so far it is assumed that the speed (d|vec{r}|/dt) between
-   * start and end does not change and is constant for the entire
-   * Trajectory.
-   *
-   **/
-
-  class LineTrajectory {
-
-    using VelocityVec = Vector<corsika::units::si::SpeedType::dimension_type>;
-
-  public:
-    LineTrajectory() = delete;
-    LineTrajectory(const LineTrajectory&) = default;
-    LineTrajectory(LineTrajectory&&) = default;
-    LineTrajectory& operator=(const LineTrajectory&) = delete;
-
-    /**
-     * \param theLine The geometric \sa Line object that represents a straight-line
-     * connection 
-     *
-     * \param timeLength The time duration to traverse the straight trajectory
-     * in units of \sa TimeType
-     */
-    LineTrajectory(Line const& theLine, corsika::units::si::TimeType timeLength)
-        : line_(theLine)
-        , timeLength_(timeLength)
-        , timeStep_(timeLength)
-        , initialVelocity_(theLine.GetVelocity(corsika::units::si::TimeType::zero()))
-        , finalVelocity_(theLine.GetVelocity(timeLength)) {}
-
-    /**
-     * \param theLine The geometric \sa Line object that represents a straight-line
-     * connection 
-     * 
-     * \param timeLength The time duration to traverse the straight trajectory
-     * in units of \sa TimeType 
-     * 
-     * \param timeStep Time duration to folow eventually curved
-     * trajectory in units of \sa TimesType 
-     * 
-     * \param initialV Initial velocity vector at
-     * start of trajectory \param finalV Final velocity vector at start of trajectory
-     */
-    LineTrajectory(
-        Line const& theLine,
-        corsika::units::si::TimeType timeLength, // length of theLine (straight)
-        corsika::units::si::TimeType timeStep,   // length of bend step (curved)
-        const VelocityVec& initialV, const VelocityVec& finalV)
-        : line_(theLine)
-        , timeLength_(timeLength)
-        , timeStep_(timeStep)
-        , initialVelocity_(initialV)
-        , finalVelocity_(finalV) {}
-
-    const Line& GetLine() const { return line_; }
-    Point GetPosition(double u) const { return line_.GetPosition(timeLength_ * u); }
-    VelocityVec GetVelocity(double u) const {
-      return initialVelocity_ * (1 - u) + finalVelocity_ * u;
-    }
-    Vector<corsika::units::si::dimensionless_d> GetDirection(double u) const {
-      return GetVelocity(u).normalized();
-    }
-
-    ///! duration along potentially bend trajectory
-    corsika::units::si::TimeType GetDuration(double u = 1) const { return u * timeStep_; }
-
-    ///! total length along potentially bend trajectory
-    corsika::units::si::LengthType GetLength(double u = 1) const {
-      using namespace corsika::units::si;
-      if (timeLength_ == 0_s) return 0_m;
-      if (timeStep_ ==
-          std::numeric_limits<corsika::units::si::TimeType::value_type>::infinity() * 1_s)
-        return std::numeric_limits<
-                   corsika::units::si::LengthType::value_type>::infinity() *
-               1_m;
-      return GetDistance(u) * timeStep_ / timeLength_;
-    }
-
-    ///! set new duration along potentially bend trajectory.
-    void SetLength(corsika::units::si::LengthType limit) {
-      SetDuration(line_.TimeFromArclength(limit));
-    }
-
-    ///! set new duration along potentially bend trajectory.
-    //   Scale other properties by "limit/timeLength_"
-    void SetDuration(corsika::units::si::TimeType limit) {
-      using namespace corsika::units::si;
-      if (timeStep_ == 0_s) {
-        timeLength_ = 0_s;
-        SetFinalVelocity(GetVelocity(0));
-        timeStep_ = limit;
-      } else {
-        // for infinite steps there can't be a difference between
-        // curved and straight trajectory, this is fundamentally
-        // undefined: assume they are the same (which, i.e. is always correct for a
-        // straight line trajectory).
-        //
-        // Final note: only straight-line trajectories should have
-        // infinite steps! Everything else is ill-defined.
-        if (timeStep_ == std::numeric_limits<
-                             corsika::units::si::TimeType::value_type>::infinity() *
-                             1_s ||
-            timeLength_ == std::numeric_limits<
-                               corsika::units::si::TimeType::value_type>::infinity() *
-                               1_s) {
-          timeLength_ = limit;
-          timeStep_ = limit;
-          // ...and don't touch velocity
-        } else {
-          const double scale = limit / timeStep_;
-          timeLength_ *= scale;
-          SetFinalVelocity(GetVelocity(scale));
-          timeStep_ = limit;
-        }
-      }
-    }
-
-  protected:
-    ///! total length along straight trajectory
-    corsika::units::si::LengthType GetDistance(double u) const {
-      assert(u <= 1);
-      assert(u >= 0);
-      return line_.ArcLength(0 * corsika::units::si::second, u * timeLength_);
-    }
-
-    void SetFinalVelocity(const VelocityVec& v) { finalVelocity_ = v; }
-
-  private:
-    Line line_;
-    corsika::units::si::TimeType
-        timeLength_; ///! length of straight step (shortest connecting line)
-    corsika::units::si::TimeType timeStep_; ///! length of bend step (curved)
-    VelocityVec initialVelocity_;
-    VelocityVec finalVelocity_;
-  };
-
-  /**
-   * \class LeapFrogTrajectory
-   *
-   *
-   **/
-
-  class LeapFrogTrajectory {
-
-    using VelocityVec = Vector<corsika::units::si::SpeedType::dimension_type>;
-    typedef corsika::geometry::Vector<corsika::units::si::magnetic_flux_density_d>
-        MagneticFieldVector;
-
-  public:
-    LeapFrogTrajectory() = delete;
-    LeapFrogTrajectory(const LeapFrogTrajectory&) = default;
-    LeapFrogTrajectory(LeapFrogTrajectory&&) = default;
-    LeapFrogTrajectory& operator=(const LeapFrogTrajectory&) = delete;
-    LeapFrogTrajectory(const Point& pos, const VelocityVec& initialVelocity,
-                       MagneticFieldVector Bfield,
-                       const decltype(square(corsika::units::si::meter) /
-                                      (square(corsika::units::si::second) *
-                                       corsika::units::si::volt)) k,
-                       corsika::units::si::TimeType timeStep) // leap-from total length
-        : initialPosition_(pos)
-        , initialVelocity_(initialVelocity)
-        , initialDirection_(initialVelocity.normalized())
-        , magneticfield_(Bfield)
-        , k_(k)
-        , timeStep_(timeStep) {}
-
-    const Line GetLine() const {
-      using namespace corsika::units::si;
-      auto D = GetPosition(1) - GetPosition(0);
-      auto d = D.norm();
-      auto v = initialVelocity_;
-      if (d>1_um) { // if trajectory is ultra-short, we do not
-		    // re-calculate velocity, just use initial
-		    // value. Otherwise, this is numerically unstable
-	v = D/d * GetVelocity(0).norm();
-      }
-      return Line(GetPosition(0), v);
-    }
-    Point GetPosition(double u) const {
-      Point position = initialPosition_ + initialVelocity_ * timeStep_ * u / 2;
-      VelocityVec velocity =
-          initialVelocity_ + initialVelocity_.cross(magneticfield_) * timeStep_ * u * k_;
-      return position + velocity * timeStep_ * u / 2;
-    }
-    VelocityVec GetVelocity(double u) const {
-      return initialVelocity_ +
-             initialVelocity_.cross(magneticfield_) * timeStep_ * u * k_;
-    }
-
-    Vector<corsika::units::si::dimensionless_d> GetDirection(double u) const {
-      return GetVelocity(u).normalized();
-    }
-
-    ///! duration along potentially bend trajectory
-    corsika::units::si::TimeType GetDuration(double u = 1) const {
-      return u * timeStep_ *
-             (double(GetVelocity(u).norm() / initialVelocity_.norm()) + 1.0) / 2;
-    }
-
-    ///! total length along potentially bend trajectory
-    corsika::units::si::LengthType GetLength(double u = 1) const {
-      using namespace corsika::units::si;
-      return timeStep_ * initialVelocity_.norm() * u;
-    }
-
-    ///! set new duration along potentially bend trajectory.
-    void SetLength(corsika::units::si::LengthType limit) {
-      using namespace corsika::units::si;
-      if (initialVelocity_.norm() == 0_m / 1_s) SetDuration(0_s);
-      SetDuration(limit / initialVelocity_.norm());
-    }
-
-    ///! set new duration along potentially bend trajectory.
-    //   Scale other properties by "limit/timeLength_"
-    void SetDuration(corsika::units::si::TimeType limit) {
-      using namespace corsika::units::si;
-      timeStep_ = limit;
-    }
-
-  private:
-    Point initialPosition_;
-    VelocityVec initialVelocity_;
-    geometry::Vector<corsika::units::si::dimensionless_d> initialDirection_;
-    MagneticFieldVector magneticfield_;
-    decltype(square(corsika::units::si::meter) /
-             (square(corsika::units::si::second) * corsika::units::si::volt)) k_;
-    corsika::units::si::TimeType timeStep_;
-  };
-
-} // namespace corsika::geometry
diff --git a/Framework/Geometry/Vector.h b/Framework/Geometry/Vector.h
deleted file mode 100644
index 8be8fcfefa6b6716157233a7bcebafd1f9f0387c..0000000000000000000000000000000000000000
--- a/Framework/Geometry/Vector.h
+++ /dev/null
@@ -1,181 +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/geometry/BaseVector.h>
-#include <corsika/geometry/QuantityVector.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-/*!
- * A Vector represents a 3-vector in Euclidean space. It is defined by components
- * given in a specific CoordinateSystem. It has a physical dimension ("unit")
- * as part of its type, so you cannot mix up e.g. electric with magnetic fields
- * (but you could calculate their cross-product to get an energy flux vector).
- *
- * When transforming coordinate systems, a Vector is subject to the rotational
- * part only and invariant under translations.
- */
-
-namespace corsika::geometry {
-
-  template <typename dim>
-  class Vector : public BaseVector<dim> {
-  public:
-    using Quantity = phys::units::quantity<dim, double>;
-
-  public:
-    Vector(CoordinateSystem const& pCS, QuantityVector<dim> pQVector)
-        : BaseVector<dim>(pCS, pQVector) {}
-
-    Vector(CoordinateSystem const& cs, Quantity x, Quantity y, Quantity z)
-        : BaseVector<dim>(cs, QuantityVector<dim>(x, y, z)) {}
-
-    /*!
-     * returns a QuantityVector with the components given in the "home"
-     * CoordinateSystem of the Vector
-     */
-    auto GetComponents() const { return BaseVector<dim>::qVector; }
-
-    /*!
-     * returns a QuantityVector with the components given in an arbitrary
-     * CoordinateSystem
-     */
-    auto GetComponents(CoordinateSystem const& pCS) const {
-      if (&pCS == BaseVector<dim>::cs) {
-        return BaseVector<dim>::qVector;
-      } else {
-        return QuantityVector<dim>(
-            CoordinateSystem::GetTransformation(*BaseVector<dim>::cs, pCS).linear() *
-            BaseVector<dim>::qVector.eVector);
-      }
-    }
-
-    /*!
-     * transforms the Vector into another CoordinateSystem by changing
-     * its components internally
-     */
-    void rebase(CoordinateSystem const& pCS) {
-      BaseVector<dim>::qVector = GetComponents(pCS);
-      BaseVector<dim>::cs = &pCS;
-    }
-
-    /*!
-     * returns the norm/length of the Vector. Before using this method,
-     * think about whether squaredNorm() might be cheaper for your computation.
-     */
-    auto norm() const { return BaseVector<dim>::qVector.norm(); }
-    auto GetNorm() const { return BaseVector<dim>::qVector.norm(); }
-
-    /*!
-     * returns the squared norm of the Vector. Before using this method,
-     * think about whether norm() might be cheaper for your computation.
-     */
-    auto squaredNorm() const { return BaseVector<dim>::qVector.squaredNorm(); }
-    auto GetSquaredNorm() const { return BaseVector<dim>::qVector.squaredNorm(); }
-
-    /*!
-     * returns a Vector \f$ \vec{v}_{\parallel} \f$ which is the parallel projection
-     * of this vector \f$ \vec{v}_1 \f$ along another Vector \f$ \vec{v}_2 \f$ given by
-     *   \f[
-     *     \vec{v}_{\parallel} = \frac{\vec{v}_1 \cdot \vec{v}_2}{\vec{v}_2^2} \vec{v}_2
-     *   \f]
-     */
-    template <typename dim2>
-    auto parallelProjectionOnto(Vector<dim2> const& pVec,
-                                CoordinateSystem const& pCS) const {
-      auto const ourCompVec = GetComponents(pCS);
-      auto const otherCompVec = pVec.GetComponents(pCS);
-      auto const& a = ourCompVec.eVector;
-      auto const& b = otherCompVec.eVector;
-
-      return Vector<dim>(pCS, QuantityVector<dim>(b * ((a.dot(b)) / b.squaredNorm())));
-    }
-
-    template <typename dim2>
-    auto parallelProjectionOnto(Vector<dim2> const& pVec) const {
-      return parallelProjectionOnto<dim2>(pVec, *BaseVector<dim>::cs);
-    }
-
-    auto operator+(Vector<dim> const& pVec) const {
-      auto const components =
-          GetComponents(*BaseVector<dim>::cs) + pVec.GetComponents(*BaseVector<dim>::cs);
-      return Vector<dim>(*BaseVector<dim>::cs, components);
-    }
-
-    auto operator-(Vector<dim> const& pVec) const {
-      auto const components = GetComponents() - pVec.GetComponents(*BaseVector<dim>::cs);
-      return Vector<dim>(*BaseVector<dim>::cs, components);
-    }
-
-    auto& operator*=(double const p) {
-      BaseVector<dim>::qVector *= p;
-      return *this;
-    }
-
-    template <typename ScalarDim>
-    auto operator*(phys::units::quantity<ScalarDim, double> const p) const {
-      using ProdDim = phys::units::detail::product_d<dim, ScalarDim>;
-
-      return Vector<ProdDim>(*BaseVector<dim>::cs, BaseVector<dim>::qVector * p);
-    }
-
-    template <typename ScalarDim>
-    auto operator/(phys::units::quantity<ScalarDim, double> const p) const {
-      return (*this) * (1 / p);
-    }
-
-    auto operator*(double const p) const {
-      return Vector<dim>(*BaseVector<dim>::cs, BaseVector<dim>::qVector * p);
-    }
-
-    auto operator/(double const p) const {
-      return Vector<dim>(*BaseVector<dim>::cs, BaseVector<dim>::qVector / p);
-    }
-
-    auto& operator+=(Vector<dim> const& pVec) {
-      BaseVector<dim>::qVector += pVec.GetComponents(*BaseVector<dim>::cs);
-      return *this;
-    }
-
-    auto& operator-=(Vector<dim> const& pVec) {
-      BaseVector<dim>::qVector -= pVec.GetComponents(*BaseVector<dim>::cs);
-      return *this;
-    }
-
-    auto& operator-() const {
-      return Vector<dim>(*BaseVector<dim>::cs, -BaseVector<dim>::qVector);
-    }
-
-    auto normalized() const { return (*this) * (1 / norm()); }
-
-    template <typename dim2>
-    auto cross(Vector<dim2> pV) const {
-      auto const c1 = GetComponents().eVector;
-      auto const c2 = pV.GetComponents(*BaseVector<dim>::cs).eVector;
-      auto const bareResult = c1.cross(c2);
-
-      using ProdDim = phys::units::detail::product_d<dim, dim2>;
-      return Vector<ProdDim>(*BaseVector<dim>::cs, bareResult);
-    }
-
-    template <typename dim2>
-    auto dot(Vector<dim2> pV) const {
-      auto const c1 = GetComponents().eVector;
-      auto const c2 = pV.GetComponents(*BaseVector<dim>::cs).eVector;
-      auto const bareResult = c1.dot(c2);
-
-      using ProdDim = phys::units::detail::product_d<dim, dim2>;
-
-      return phys::units::quantity<ProdDim, double>(phys::units::detail::magnitude_tag,
-                                                    bareResult);
-    }
-  };
-
-} // namespace corsika::geometry
diff --git a/Framework/Geometry/testFourVector.cc b/Framework/Geometry/testFourVector.cc
deleted file mode 100644
index 4c169258290efe3220829027535ff329b6b4142b..0000000000000000000000000000000000000000
--- a/Framework/Geometry/testFourVector.cc
+++ /dev/null
@@ -1,181 +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 <catch2/catch.hpp>
-
-#include <corsika/geometry/CoordinateSystem.h>
-#include <corsika/geometry/FourVector.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <cmath>
-
-using namespace corsika::geometry;
-using namespace corsika::units::si;
-
-TEST_CASE("four vectors") {
-
-  // this is just needed as a baseline
-  CoordinateSystem& rootCS =
-      RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-  /*
-    Test: P2 = E2 - p2 all in [GeV]
-    This is the typical HEP application
-   */
-  SECTION("Energy momentum in hep-units") {
-
-    HEPEnergyType E0 = 10_GeV;
-    Vector<hepmomentum_d> P0(rootCS, {10_GeV, 10_GeV, 10_GeV});
-
-    FourVector p0(E0, P0);
-
-    REQUIRE(p0.GetNormSqr() == -200_GeV * 1_GeV);
-    REQUIRE(p0.GetNorm() == sqrt(200_GeV * 1_GeV));
-  }
-
-  /*
-    Check space/time-like
-   */
-  SECTION("Space/time likeness") {
-
-    HEPEnergyType E0 = 20_GeV;
-    Vector<hepmomentum_d> P0(rootCS, {10_GeV, 0_GeV, 0_GeV});
-    Vector<hepmomentum_d> P1(rootCS, {10_GeV, 10_GeV, 20_GeV});
-    Vector<hepmomentum_d> P2(rootCS, {0_GeV, 20_GeV, 0_GeV});
-
-    FourVector p0(E0, P0);
-    FourVector p1(E0, P1);
-    FourVector p2(E0, P2);
-
-    CHECK(p0.IsSpacelike());
-    CHECK(!p0.IsTimelike());
-    // CHECK(!p0.IsPhotonlike());
-
-    CHECK(!p1.IsSpacelike());
-    CHECK(p1.IsTimelike());
-    // CHECK(!p1.IsPhotonlike());
-
-    CHECK(!p2.IsSpacelike());
-    CHECK(!p2.IsTimelike());
-    // CHECK(p2.IsPhotonlike());
-  }
-
-  /*
-    Test: P2 = E2/c2 - p2 with E in [GeV/c] and P in [GeV]
-    This requires additional factors of c
-   */
-  SECTION("Energy momentum in SI-units") {
-
-    auto E1 = 100_GeV / corsika::units::constants::c;
-    Vector<hepmomentum_d> P1(rootCS, {10_GeV, 5_GeV, 15_GeV});
-
-    FourVector p1(E1, P1);
-
-    const double check = 100 * 100 - 10 * 10 - 5 * 5 - 15 * 15; // for dummies...
-
-    REQUIRE(p1.GetNormSqr() / 1_GeV / 1_GeV == Approx(check));
-    REQUIRE(p1.GetNorm() / 1_GeV == Approx(sqrt(check)));
-  }
-
-  /**
-    Test: P2 = T2/c2 - r2 with T in [s] and r in [m]
-    This requires additional factors of c
-   */
-  SECTION("Spacetime in SI-units") {
-
-    TimeType T2 = 10_m / corsika::units::constants::c;
-    Vector<length_d> P2(rootCS, {10_m, 5_m, 5_m});
-
-    const double check = 10 * 10 - 10 * 10 - 5 * 5 - 5 * 5; // for dummies...
-
-    FourVector p2(T2, P2);
-
-    REQUIRE(p2.GetNormSqr() == check * 1_m * 1_m);
-    REQUIRE(p2.GetNorm() == sqrt(abs(check)) * 1_m);
-  }
-
-  /**
-     Testing the math operators
-   */
-
-  SECTION("Operators and comutions") {
-
-    HEPEnergyType E1 = 100_GeV;
-    Vector<hepmomentum_d> P1(rootCS, {0_GeV, 0_GeV, 0_GeV});
-
-    HEPEnergyType E2 = 0_GeV;
-    Vector<hepmomentum_d> P2(rootCS, {10_GeV, 0_GeV, 0_GeV});
-
-    FourVector p1(E1, P1);
-    const FourVector p2(E2, P2);
-
-    REQUIRE(p1.GetNorm() / 1_GeV == Approx(100.));
-    REQUIRE(p2.GetNorm() / 1_GeV == Approx(10.));
-
-    SECTION("product") {
-      FourVector p3 = p1 + p2;
-      REQUIRE(p3.GetNorm() / 1_GeV == Approx(sqrt(100. * 100. - 100.)));
-      p3 -= p2;
-      REQUIRE(p3.GetNorm() / 1_GeV == Approx(100.));
-      REQUIRE(p1.GetNorm() / 1_GeV == Approx(100.));
-      REQUIRE(p2.GetNorm() / 1_GeV == Approx(10.));
-    }
-
-    SECTION("difference") {
-      FourVector p3 = p1 - p2;
-      REQUIRE(p3.GetNorm() / 1_GeV == Approx(sqrt(100. * 100. - 100.)));
-      p3 += p2;
-      REQUIRE(p3.GetNorm() / 1_GeV == Approx(100.));
-      REQUIRE(p1.GetNorm() / 1_GeV == Approx(100.));
-      REQUIRE(p2.GetNorm() / 1_GeV == Approx(10.));
-    }
-
-    SECTION("scale") {
-      double s = 10;
-      FourVector p3 = p1 * s;
-      REQUIRE(p3.GetNorm() / 1_GeV == Approx(sqrt(100. * 100. * s * s)));
-      p3 /= 10;
-      REQUIRE(p3.GetNorm() / 1_GeV == Approx(sqrt(100. * 100.)));
-      REQUIRE(p1.GetNorm() / 1_GeV == Approx(100.));
-      REQUIRE(p2.GetNorm() / 1_GeV == Approx(10.));
-    }
-  }
-
-  /**
-     The FourVector class can be used with reference template
-     arguments. In this configuration it does not hold any data
-     itself, but rather just refers to data located elsewhere. Thus,
-     it merely provides the physical/mathematical wrapper around the
-     data.
-   */
-
-  SECTION("Use as wrapper") {
-
-    TimeType T = 10_m / corsika::units::constants::c;
-    Vector<length_d> P(rootCS, {10_m, 5_m, 5_m});
-
-    const TimeType T_c = 10_m / corsika::units::constants::c;
-    const Vector<length_d> P_c(rootCS, {10_m, 5_m, 5_m});
-
-    // FourVector<TimeType&, Vector<length_d>&> p0(T_c, P_c); // this does not compile,
-    // and it shoudn't!
-    FourVector<TimeType&, Vector<length_d>&> p1(T, P);
-    FourVector<const TimeType&, const Vector<length_d>&> p2(T, P);
-    FourVector<const TimeType&, const Vector<length_d>&> p3(T_c, P_c);
-
-    p1 *= 10;
-    // p2 *= 10; // this does not compile, and it shoudn't !
-    // p3 *= 10; // this does not compile, and it shoudn't !!
-
-    const double check = 10 * 10 - 10 * 10 - 5 * 5 - 5 * 5; // for dummies...
-    REQUIRE(p1.GetNormSqr() / (1_m * 1_m) == Approx(10. * 10. * check));
-    REQUIRE(p2.GetNorm() / 1_m == Approx(10 * sqrt(abs(check))));
-    REQUIRE(p3.GetNorm() / 1_m == Approx(sqrt(abs(check))));
-  }
-}
diff --git a/Framework/Geometry/testGeometry.cc b/Framework/Geometry/testGeometry.cc
deleted file mode 100644
index b528bf1bf3d4d922092a0e517ba8dd1208184749..0000000000000000000000000000000000000000
--- a/Framework/Geometry/testGeometry.cc
+++ /dev/null
@@ -1,264 +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 <catch2/catch.hpp>
-
-#include <corsika/geometry/CoordinateSystem.h>
-#include <corsika/geometry/Helix.h>
-#include <corsika/geometry/Line.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Sphere.h>
-#include <corsika/geometry/Trajectory.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <cmath>
-
-using namespace corsika::geometry;
-using namespace corsika::units::si;
-
-double constexpr absMargin = 1.0e-8;
-
-TEST_CASE("transformations between CoordinateSystems") {
-  CoordinateSystem& rootCS =
-      RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-  REQUIRE(CoordinateSystem::GetTransformation(rootCS, rootCS)
-              .isApprox(EigenTransform::Identity()));
-
-  QuantityVector<length_d> const coordinates{0_m, 0_m, 0_m};
-  Point p1(rootCS, coordinates);
-
-  QuantityVector<magnetic_flux_density_d> components{1. * tesla, 0. * tesla, 0. * tesla};
-  Vector<magnetic_flux_density_d> v1(rootCS, components);
-
-  REQUIRE((p1.GetCoordinates() - coordinates).norm().magnitude() ==
-          Approx(0).margin(absMargin));
-  REQUIRE((p1.GetCoordinates(rootCS) - coordinates).norm().magnitude() ==
-          Approx(0).margin(absMargin));
-
-  /*
-  SECTION("unconnected CoordinateSystems") {
-    CoordinateSystem rootCS2 = CoordinateSystem::CreateRootCS();
-    REQUIRE_THROWS(CoordinateSystem::GetTransformation(rootCS, rootCS2));
-    }*/
-
-  SECTION("translations") {
-    QuantityVector<length_d> const translationVector{0_m, 4_m, 0_m};
-
-    CoordinateSystem translatedCS = rootCS.translate(translationVector);
-
-    REQUIRE(translatedCS.GetReference() == &rootCS);
-
-    REQUIRE((p1.GetCoordinates(translatedCS) + translationVector).norm().magnitude() ==
-            Approx(0).margin(absMargin));
-
-    // Vectors are not subject to translations
-    REQUIRE(
-        (v1.GetComponents(rootCS) - v1.GetComponents(translatedCS)).norm().magnitude() ==
-        Approx(0).margin(absMargin));
-
-    Point p2(translatedCS, {0_m, 0_m, 0_m});
-    REQUIRE(((p2 - p1).GetComponents() - translationVector).norm().magnitude() ==
-            Approx(0).margin(absMargin));
-  }
-
-  SECTION("multiple translations") {
-    QuantityVector<length_d> const tv1{0_m, 5_m, 0_m};
-    CoordinateSystem cs2 = rootCS.translate(tv1);
-
-    QuantityVector<length_d> const tv2{3_m, 0_m, 0_m};
-    CoordinateSystem cs3 = rootCS.translate(tv2);
-
-    QuantityVector<length_d> const tv3{0_m, 0_m, 2_m};
-    CoordinateSystem cs4 = cs3.translate(tv3);
-
-    REQUIRE(cs4.GetReference()->GetReference() == &rootCS);
-
-    REQUIRE(CoordinateSystem::GetTransformation(cs3, cs2).isApprox(
-        rootCS.translate({3_m, -5_m, 0_m}).GetTransform()));
-    REQUIRE(CoordinateSystem::GetTransformation(cs2, cs3).isApprox(
-        rootCS.translate({-3_m, +5_m, 0_m}).GetTransform()));
-  }
-
-  SECTION("rotations") {
-    QuantityVector<length_d> const axis{0_m, 0_m, 1_km};
-    double const angle = 90. / 180. * M_PI;
-
-    CoordinateSystem rotatedCS = rootCS.rotate(axis, angle);
-    REQUIRE(rotatedCS.GetReference() == &rootCS);
-
-    REQUIRE(v1.GetComponents(rotatedCS)[1].magnitude() ==
-            Approx((-1. * tesla).magnitude()));
-
-    // vector norm invariant under rotation
-    REQUIRE(v1.GetComponents(rotatedCS).norm().magnitude() ==
-            Approx(v1.GetComponents(rootCS).norm().magnitude()));
-  }
-
-  SECTION("multiple rotations") {
-    QuantityVector<length_d> const zAxis{0_m, 0_m, 1_km};
-    QuantityVector<length_d> const yAxis{0_m, 7_nm, 0_m};
-    QuantityVector<length_d> const xAxis{2_m, 0_nm, 0_m};
-
-    QuantityVector<magnetic_flux_density_d> components{1. * tesla, 2. * tesla,
-                                                       3. * tesla};
-    Vector<magnetic_flux_density_d> v1(rootCS, components);
-
-    double const angle = 90. / 180. * M_PI;
-
-    CoordinateSystem rotated1 = rootCS.rotate(zAxis, angle);
-    CoordinateSystem rotated2 = rotated1.rotate(yAxis, angle);
-    CoordinateSystem rotated3 = rotated2.rotate(zAxis, -angle);
-
-    CoordinateSystem combined = rootCS.rotate(xAxis, -angle);
-
-    auto comp1 = v1.GetComponents(rotated3);
-    auto comp3 = v1.GetComponents(combined);
-    REQUIRE((comp1 - comp3).norm().magnitude() == Approx(0).margin(absMargin));
-  }
-
-  SECTION("RotateToZ positive") {
-    Vector const v{rootCS, 0_m, 1_m, 1_m};
-    auto const csPrime = rootCS.RotateToZ(v);
-    Vector const zPrime{csPrime, 0_m, 0_m, 5_m};
-    Vector const xPrime{csPrime, 5_m, 0_m, 0_m};
-    Vector const yPrime{csPrime, 0_m, 5_m, 0_m};
-
-    CHECK(xPrime.dot(v).magnitude() == Approx(0).margin(absMargin));
-    CHECK(yPrime.dot(v).magnitude() == Approx(0).margin(absMargin));
-    CHECK((zPrime.dot(v) / 1_m).magnitude() == Approx(5 * sqrt(2)));
-
-    CHECK(zPrime.GetComponents(rootCS)[1].magnitude() ==
-          Approx(zPrime.GetComponents(rootCS)[2].magnitude()));
-    CHECK(zPrime.GetComponents(rootCS)[0].magnitude() == Approx(0));
-
-    CHECK(xPrime.GetComponents(rootCS).eVector.dot(
-              yPrime.GetComponents(rootCS).eVector) == Approx(0));
-    CHECK(zPrime.GetComponents(rootCS).eVector.dot(
-              xPrime.GetComponents(rootCS).eVector) == Approx(0));
-    CHECK(yPrime.GetComponents(rootCS).eVector.dot(
-              zPrime.GetComponents(rootCS).eVector) == Approx(0));
-
-    CHECK(yPrime.GetComponents(rootCS).eVector.dot(
-              yPrime.GetComponents(rootCS).eVector) == Approx((5_m * 5_m).magnitude()));
-    CHECK(xPrime.GetComponents(rootCS).eVector.dot(
-              xPrime.GetComponents(rootCS).eVector) == Approx((5_m * 5_m).magnitude()));
-    CHECK(zPrime.GetComponents(rootCS).eVector.dot(
-              zPrime.GetComponents(rootCS).eVector) == Approx((5_m * 5_m).magnitude()));
-  }
-
-  SECTION("RotateToZ negative") {
-    Vector const v{rootCS, 0_m, 0_m, -1_m};
-    auto const csPrime = rootCS.RotateToZ(v);
-    Vector const zPrime{csPrime, 0_m, 0_m, 5_m};
-    Vector const xPrime{csPrime, 5_m, 0_m, 0_m};
-    Vector const yPrime{csPrime, 0_m, 5_m, 0_m};
-
-    CHECK(zPrime.dot(v).magnitude() > 0);
-    CHECK(xPrime.GetComponents(rootCS).eVector.dot(v.GetComponents().eVector) ==
-          Approx(0));
-    CHECK(yPrime.GetComponents(rootCS).eVector.dot(v.GetComponents().eVector) ==
-          Approx(0));
-
-    CHECK(xPrime.GetComponents(rootCS).eVector.dot(
-              yPrime.GetComponents(rootCS).eVector) == Approx(0));
-    CHECK(zPrime.GetComponents(rootCS).eVector.dot(
-              xPrime.GetComponents(rootCS).eVector) == Approx(0));
-    CHECK(yPrime.GetComponents(rootCS).eVector.dot(
-              zPrime.GetComponents(rootCS).eVector) == Approx(0));
-
-    CHECK(yPrime.GetComponents(rootCS).eVector.dot(
-              yPrime.GetComponents(rootCS).eVector) == Approx((5_m * 5_m).magnitude()));
-    CHECK(xPrime.GetComponents(rootCS).eVector.dot(
-              xPrime.GetComponents(rootCS).eVector) == Approx((5_m * 5_m).magnitude()));
-    CHECK(zPrime.GetComponents(rootCS).eVector.dot(
-              zPrime.GetComponents(rootCS).eVector) == Approx((5_m * 5_m).magnitude()));
-  }
-}
-
-TEST_CASE("Sphere") {
-  CoordinateSystem& rootCS =
-      RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-  Point center(rootCS, {0_m, 3_m, 4_m});
-  Sphere sphere(center, 5_m);
-
-  SECTION("GetCenter") {
-    CHECK((sphere.GetCenter().GetCoordinates(rootCS) -
-           QuantityVector<length_d>(0_m, 3_m, 4_m))
-              .norm()
-              .magnitude() == Approx(0).margin(absMargin));
-    CHECK(sphere.GetRadius() / 5_m == Approx(1));
-  }
-
-  SECTION("Contains") {
-    REQUIRE_FALSE(sphere.Contains(Point(rootCS, {100_m, 0_m, 0_m})));
-    REQUIRE(sphere.Contains(Point(rootCS, {2_m, 3_m, 4_m})));
-  }
-}
-
-TEST_CASE("Trajectories") {
-  CoordinateSystem& rootCS =
-      RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-  Point r0(rootCS, {0_m, 0_m, 0_m});
-
-  SECTION("Line") {
-    Vector<SpeedType::dimension_type> v0(rootCS,
-                                         {3_m / second, 0_m / second, 0_m / second});
-
-    Line const line(r0, v0);
-    CHECK(
-        (line.GetPosition(2_s).GetCoordinates() - QuantityVector<length_d>(6_m, 0_m, 0_m))
-            .norm()
-            .magnitude() == Approx(0).margin(absMargin));
-
-    CHECK((line.PositionFromArclength(4_m).GetCoordinates() -
-           QuantityVector<length_d>(4_m, 0_m, 0_m))
-              .norm()
-              .magnitude() == Approx(0).margin(absMargin));
-
-    CHECK((line.GetPosition(7_s) - line.PositionFromArclength(line.ArcLength(0_s, 7_s)))
-              .norm()
-              .magnitude() == Approx(0).margin(absMargin));
-
-    auto const t = 1_s;
-    LineTrajectory base(line, t);
-    CHECK(line.GetPosition(t).GetCoordinates() == base.GetPosition(1.).GetCoordinates());
-
-    CHECK((base.GetVelocity(0).normalized().GetComponents(rootCS) -
-           QuantityVector<dimensionless_d>{1, 0, 0})
-              .eVector.norm() == Approx(0).margin(absMargin));
-  }
-
-  SECTION("Helix") {
-    Vector<SpeedType::dimension_type> const vPar(
-        rootCS, {0_m / second, 0_m / second, 4_m / second});
-
-    Vector<SpeedType::dimension_type> const vPerp(
-        rootCS, {3_m / second, 0_m / second, 0_m / second});
-
-    auto const T = 1_s;
-    auto const omegaC = 2 * M_PI / T;
-
-    Helix const helix(r0, omegaC, vPar, vPerp);
-
-    CHECK((helix.GetPosition(1_s).GetCoordinates() -
-           QuantityVector<length_d>(0_m, 0_m, 4_m))
-              .norm()
-              .magnitude() == Approx(0).margin(absMargin));
-
-    CHECK((helix.GetPosition(0.25_s).GetCoordinates() -
-           QuantityVector<length_d>(-3_m / (2 * M_PI), -3_m / (2 * M_PI), 1_m))
-              .norm()
-              .magnitude() == Approx(0).margin(absMargin));
-
-    CHECK(
-        (helix.GetPosition(7_s) - helix.PositionFromArclength(helix.ArcLength(0_s, 7_s)))
-            .norm()
-            .magnitude() == Approx(0).margin(absMargin));
-  }
-}
diff --git a/Framework/Logging/CMakeLists.txt b/Framework/Logging/CMakeLists.txt
deleted file mode 100644
index 8aa0abe3cebd3f9c5b18b05a6082d7ca2c36ef61..0000000000000000000000000000000000000000
--- a/Framework/Logging/CMakeLists.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-# create the library
-add_library (CORSIKAlogging INTERFACE)
-
-# namespace of library -> location of header files
-set (
-  CORSIKAlogging_NAMESPACE
-  corsika/logging
-  )
-
-# header files of this library
-set (
-  CORSIKAlogging_HEADERS
-  Logging.h
-  )
-
-# copy the headers into the namespace
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAlogging
-  ${CORSIKAlogging_NAMESPACE}
-  ${CORSIKAlogging_HEADERS}
-  )
-
-# include directive for upstream code
-target_include_directories (
-  CORSIKAlogging
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/>
-  )
-
-# and link against spdlog
-target_link_libraries(
-  CORSIKAlogging
-  INTERFACE
-  spdlog::spdlog
-)
-
-# install library
-install (
-  FILES ${CORSIKAlogging_HEADERS}
-  DESTINATION include/${CORSIKAlogging_NAMESPACE}
-  )
-
-# ----------------
-# code unit testing
-
-CORSIKA_ADD_TEST (testLogging)
-target_link_libraries (
- testLogging
- CORSIKAlogging
- CORSIKAtesting
- )
diff --git a/Framework/Logging/Logging.h b/Framework/Logging/Logging.h
deleted file mode 100644
index 814f35de4de5c025c3bab8eaa7fdf51e135e7010..0000000000000000000000000000000000000000
--- a/Framework/Logging/Logging.h
+++ /dev/null
@@ -1,189 +0,0 @@
-/*
- * (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.
- */
-
-/**
- *  @File Logging.h
- *
- * CORSIKA8 logging utilities.
- *
- * See testLogging.cc for a complete set of examples for
- * how the logging functions should be used.
- */
-
-#pragma once
-
-// Configure some behaviour of sdlog.
-// This must be done before spdlog is included.
-
-// use the coarse system clock. This is *much* faster
-// but introduces a real time error of O(10 ms).
-#define SPDLOG_CLOCK_COARSE
-
-// do not create a default logger (we provide our own "corsika" logger)
-#define SPDLOG_DISABLE_DEFAULT_LOGGER
-
-// use __PRETTY_FUNCTION__ instead of __FUNCTION__ where
-// printing function names in trace statements. This is much
-// nicer than __FUNCTION__ under GCC/clang.
-#define SPDLOG_FUNCTION __PRETTY_FUNCTION__
-
-// 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_TRACE
-#else // otherwise, remove everything but "critical" messages
-#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_CRITICAL
-#endif
-
-#include <spdlog/fmt/ostr.h> // will output whenerver a streaming operator is found
-#include <spdlog/sinks/stdout_color_sinks.h>
-#include <spdlog/spdlog.h>
-
-namespace corsika::logging {
-
-  // bring spdlog into the corsika::logging namespace
-  using namespace spdlog;
-
-  /*
-   * The default pattern for CORSIKA8 loggers.
-   */
-  const std::string default_pattern{"[%n:%^%-8l%$] %v"};
-
-  /**
-   * Create a new C8-style logger.
-   *
-   * Use this if you are explicitly (and can guarantee) that you
-   * are creating a logger for the first time. It is recommended
-   * that for regular usage, the `GetLogger` function is used instead
-   * as that will also create the logger if it has not yet been created.
-   *
-   * Calling `CreateLogger` twice to create the same logger will
-   * result in an spdlog duplicate exception.
-   *
-   * @param name           The unique name of the logger.
-   * @param defaultlog     If True, set this as the default logger.
-   * @returns              The constructed and formatted logger.
-   */
-  inline auto CreateLogger(std::string const& name, bool const defaultlog = false) {
-
-    // create the logger
-    // this is currently a colorized multi-threading safe logger
-    auto logger = spdlog::stdout_color_mt(name);
-
-    // set the default C8 format
-    logger->set_pattern(default_pattern);
-
-    // if defaultlog is True, we set this as the default spdlog logger.
-    if (defaultlog) { spdlog::set_default_logger(logger); }
-
-    // and return the logger
-    return logger;
-  }
-
-  /**
-   * Get a smart pointer to an existing logger.
-   *
-   * This should be the default method for code to obtain a
-   * logger. If the logger *does not* exist, it is *created* and
-   * returned to the caller.
-   *
-   * This should be preferred over `CreateLogger`.
-   *
-   * @param name    The name of the logger to get.
-   * @param defaultlog   If True, make this the default logger.
-   * @returns              The constructed and formatted logger.
-   */
-  inline auto GetLogger(std::string const& name, bool const defaultlog = false) {
-
-    // attempt to get the logger from the registry
-    auto logger = spdlog::get(name);
-
-    // weg found the logger, so just return it
-    if (logger) {
-      return logger;
-    } else { // logger was not found so create it
-      return CreateLogger(name, defaultlog);
-    }
-  }
-
-  /**
-   * The default "corsika" logger.
-   */
-  inline auto corsika = GetLogger("corsika", true);
-
-  /**
-   * Set the default log level for all *newly* created loggers.
-   *
-   *  @param name    The minimum log level required to print.
-   *
-   */
-  inline auto SetDefaultLevel(level::level_enum const minlevel) -> void {
-    spdlog::set_level(minlevel);
-  }
-
-  /**
-   * Set the log level for the "corsika" logger.
-   *
-   * @param name    The minimum log level required to print.
-   *
-   */
-  inline auto SetLevel(level::level_enum const minlevel) -> void {
-    corsika->set_level(minlevel);
-  }
-
-  /**
-   * Set the log level for a specific logger.
-   *
-   * @param logger  The logger to set the level of.
-   * @param name    The minimum log level required to print.
-   *
-   */
-  template <typename TLogger>
-  inline auto SetLevel(TLogger& logger, level::level_enum const minlevel) -> void {
-    logger->set_level(minlevel);
-  }
-
-  /**
-   * Add the source (filename, line no) info to the logger.
-   *
-   * @param logger  The logger to set the level of.
-   *
-   */
-  template <typename TLogger>
-  inline auto AddSourceInfo(TLogger& logger) -> void {
-    logger->set_pattern("[%n:%^%-8l%$(%s:%!:%#)] %v");
-  }
-
-  /**
-   * Reset the logging pattern to the default.
-   *
-   * @param logger  The logger to set the level of.
-   *
-   */
-  template <typename TLogger>
-  inline auto ResetPattern(TLogger& logger) -> void {
-    logger->set_pattern(default_pattern);
-  }
-
-// define our macro-style loggers
-#define C8LOG_TRACE SPDLOG_TRACE
-#define C8LOG_DEBUG SPDLOG_DEBUG
-#define C8LOG_INFO SPDLOG_INFO
-#define C8LOG_WARN SPDLOG_WARN
-#define C8LOG_ERROR SPDLOG_ERROR
-#define C8LOG_CRITICAL SPDLOG_CRITICAL
-
-// and the specific logger versions
-#define C8LOG_LOGGER_TRACE SPDLOG_LOGGER_TRACE
-#define C8LOG_LOGGER_DEBUG SPDLOG_LOGGER_DEBUG
-#define C8LOG_LOGGER_INFO SPDLOG_LOGGER_INFO
-#define C8LOG_LOGGER_WARN SPDLOG_LOGGER_WARN
-#define C8LOG_LOGGER_ERROR SPDLOG_LOGGER_ERROR
-#define C8LOG_LOGGER_CRITICAL SPDLOG_LOGGER_CRITICAL
-
-} // namespace corsika::logging
diff --git a/Framework/Logging/testLogging.cc b/Framework/Logging/testLogging.cc
deleted file mode 100644
index bf919058053a662873ec472c76330e5f16b6a1d2..0000000000000000000000000000000000000000
--- a/Framework/Logging/testLogging.cc
+++ /dev/null
@@ -1,117 +0,0 @@
-/*
- * (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.
- */
-
-#include <corsika/logging/Logging.h>
-
-#include <catch2/catch.hpp>
-
-using namespace corsika;
-
-TEST_CASE("Logging", "[Logging]") {
-  SECTION("top level functions using corsika logger") {
-    logging::info("This is an info message!");
-    logging::warn("This is a warning message!");
-    logging::debug("This is a debug message!");
-    logging::error("This is an error message!");
-    logging::critical("This is a critical error message!");
-  }
-
-  SECTION("create a specific logger") {
-
-    // create a logger manually
-    auto logger = logging::CreateLogger("loggerA");
-
-    // set a custom pattern for this logger
-    logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
-
-    // and make sure we can log with this created object
-    logger->info("This is an info message!");
-    logger->warn("This is a warning message!");
-    logger->debug("This is a debug message!");
-    logger->error("This is an error message!");
-    logger->critical("This is a critical error message!");
-
-    // get a reference to the logger using Get
-    auto other = logging::GetLogger("loggerA");
-
-    // and make sure we can use this other reference to log
-    other->info("This is an info message!");
-    other->warn("This is a warning message!");
-    other->debug("This is a debug message!");
-    other->error("This is an error message!");
-    other->critical("This is a critical error message!");
-  }
-
-  SECTION("get a new logger") {
-
-    // get a reference to an unknown logger
-    auto logger = logging::GetLogger("loggerB");
-
-    // and make sure we can log with this created object
-    logger->info("This is an info message!");
-    logger->warn("This is a warning message!");
-    logger->debug("This is a debug message!");
-    logger->error("This is an error message!");
-    logger->critical("This is a critical error message!");
-  }
-
-  SECTION("test log level") {
-
-    // set the default log level
-    logging::SetDefaultLevel(logging::level::critical);
-
-    // and make sure we can log with this created object
-    logging::info("This should NOT be printed!");
-    logging::warn("This should NOT be printed!");
-    logging::debug("This should NOT be printed!");
-    logging::error("This should NOT be printed!");
-    logging::critical("This SHOULD BE printed!!");
-
-    // get a reference to an unknown logger
-    auto logger = logging::GetLogger("loggerD");
-
-    // now set the default log level for this logger
-    logging::SetLevel(logger, logging::level::critical);
-
-    // now try the various logging functions
-    logger->info("This should NOT be printed!");
-    logger->warn("This should NOT be printed!");
-    logger->debug("This should NOT be printed!");
-    logger->error("This should NOT be printed!");
-    logger->critical("This SHOULD BE printed!!");
-
-    // and reset it for the next tests
-    logging::SetDefaultLevel(logging::level::debug);
-    logging::SetLevel(logging::level::debug);
-  }
-
-  SECTION("test macro style logging") {
-
-    // these print with the "corsika" logger
-    C8LOG_INFO("test macro style logging");
-    C8LOG_DEBUG("test macro style logging");
-    C8LOG_ERROR("test macro style logging");
-    C8LOG_CRITICAL("test macro style logging");
-
-    // get a reference to an unknown logger
-    auto logger = logging::GetLogger("loggerE");
-
-    // add the filename, source information to this logger
-    logging::AddSourceInfo(logger);
-
-    // these print with the "loggerE" logger
-    C8LOG_LOGGER_INFO(logger, "test macro style logging");
-    C8LOG_LOGGER_WARN(logger, "test macro style logging");
-
-    // reset the logging pattern
-    logging::ResetPattern(logger);
-
-    // these trace macros should not print file, function, and line
-    C8LOG_LOGGER_TRACE(logger, "test macro style logging:");
-  }
-}
diff --git a/Framework/Particles/CMakeLists.txt b/Framework/Particles/CMakeLists.txt
deleted file mode 100644
index 96d89ebeb0a3c73ef3b21b44b5e3a2fdb42a5dfb..0000000000000000000000000000000000000000
--- a/Framework/Particles/CMakeLists.txt
+++ /dev/null
@@ -1,93 +0,0 @@
-add_custom_command (
-  OUTPUT  ${PROJECT_BINARY_DIR}/Framework/Particles/GeneratedParticleProperties.inc
-          ${PROJECT_BINARY_DIR}/Framework/Particles/particle_db.pkl
-  COMMAND ${PROJECT_SOURCE_DIR}/Framework/Particles/pdxml_reader.py 
-          ${PROJECT_SOURCE_DIR}/Framework/Particles/ParticleData.xml
-          ${PROJECT_SOURCE_DIR}/Framework/Particles/NuclearData.xml
-          ${PROJECT_SOURCE_DIR}/Framework/Particles/ParticleClassNames.xml
-  DEPENDS pdxml_reader.py
-          ParticleData.xml
-          NuclearData.xml
-          ParticleClassNames.xml
-  WORKING_DIRECTORY
-          ${PROJECT_BINARY_DIR}/Framework/Particles/
-  COMMENT "Read PYTHIA8 particle data and produce C++ source code GeneratedParticleProperties.inc"
-  VERBATIM
-  )
-
-set (
-  PARTICLE_SOURCES
-  ParticleProperties.cc
-  )
-
-# all public header files of library, includes automatic generated file(s)
-set (
-  PARTICLE_HEADERS
-  ParticleProperties.h
-  ${PROJECT_BINARY_DIR}/Framework/Particles/GeneratedParticleProperties.inc # this one is auto-generated
-  )
-
-set (
-  PARTICLE_NAMESPACE
-  corsika/particles
-  )
-
-add_library (CORSIKAparticles STATIC ${PARTICLE_SOURCES})
-
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAparticles ${PARTICLE_NAMESPACE} ${PARTICLE_HEADERS})
-
-# ....................................................
-# since GeneratedParticleProperties.inc is an automatically produced file in the build directory,
-# create a symbolic link into the source tree, so that it can be found and edited more easily
-# this is not needed for the build to succeed! .......
-add_custom_command (
-  OUTPUT  ${CMAKE_CURRENT_SOURCE_DIR}/GeneratedParticleProperties.inc
-  COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_BINARY_DIR}/include/corsika/particles/GeneratedParticleProperties.inc ${CMAKE_CURRENT_SOURCE_DIR}/GeneratedParticleProperties.inc
-  COMMENT "Generate link in source-dir: ${CMAKE_CURRENT_SOURCE_DIR}/GeneratedParticleProperties.inc"
-  )
-add_custom_target (SourceDirLink DEPENDS ${PROJECT_BINARY_DIR}/Framework/Particles/GeneratedParticleProperties.inc)
-add_dependencies (CORSIKAparticles SourceDirLink)
-# .....................................................
-
-target_link_libraries (
-  CORSIKAparticles
-  PUBLIC
-  CORSIKAunits
-  CORSIKAlogging
-  )
-
-set_target_properties (
-  CORSIKAparticles
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  PUBLIC_HEADER "${PARTICLE_HEADERS}"
-  )
-
-target_include_directories (
-  CORSIKAparticles
-  PUBLIC
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include>
-  )
-
-install (
-  FILES
-  ${PARTICLE_HEADERS}
-  DESTINATION
-  include/${PARTICLE_NAMESPACE}
-  )
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST(testParticles
-  SOURCES
-    testParticles.cc
-    ${PROJECT_BINARY_DIR}/Framework/Particles/GeneratedParticleProperties.inc
-)
-target_link_libraries (
-  testParticles
-  CORSIKAparticles
-  CORSIKAunits
-  CORSIKAtesting
-  )
diff --git a/Framework/Particles/ParticleProperties.cc b/Framework/Particles/ParticleProperties.cc
deleted file mode 100644
index a52883bf83b14a97a7a732a700db59eaf502cd8b..0000000000000000000000000000000000000000
--- a/Framework/Particles/ParticleProperties.cc
+++ /dev/null
@@ -1,30 +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/particles/ParticleProperties.h>
-#include <iostream>
-
-namespace corsika::particles {
-
-  std::ostream& operator<<(std::ostream& stream, corsika::particles::Code const p) {
-    return stream << corsika::particles::GetName(p);
-  }
-
-  Code ConvertFromPDG(PDGCode p) {
-    static_assert(detail::conversionArray.size() % 2 == 1);
-    // this will fail, for the strange case where the maxPDG is negative...
-    unsigned int constexpr maxPDG{(detail::conversionArray.size() - 1) >> 1};
-    auto k = static_cast<PDGCodeType>(p);
-    if ((unsigned int)abs(k) <= maxPDG) {
-      return detail::conversionArray[k + maxPDG];
-    } else {
-      return detail::conversionMap.at(p);
-    }
-  }
-
-} // namespace corsika::particles
diff --git a/Framework/Particles/ParticleProperties.h b/Framework/Particles/ParticleProperties.h
deleted file mode 100644
index 7abcc768b5444e15414f6880c6a56aa54b288087..0000000000000000000000000000000000000000
--- a/Framework/Particles/ParticleProperties.h
+++ /dev/null
@@ -1,160 +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.
- */
-
-/**
-   @file Particles.h
-
-   Interface to particle properties
- */
-
-#pragma once
-
-#include <array>
-#include <cstdint>
-#include <iosfwd>
-
-#include <corsika/units/PhysicalUnits.h>
-
-/**
- *
- * The properties of all elementary particles is stored here. The data
- * are taken from the Pythia ParticleData.xml file.
- *
- */
-
-namespace corsika::particles {
-
-  /**
-   * @enum Code
-   * The Code enum is the actual place to define CORSIKA 8 particle codes.
-   */
-  enum class Code : int16_t;
-  enum class PDGCode : int32_t;
-  using CodeIntType = std::underlying_type<Code>::type;
-  using PDGCodeType = std::underlying_type<PDGCode>::type;
-
-  // forward declarations to be used in GeneratedParticleProperties
-  int16_t constexpr GetChargeNumber(Code const);
-  corsika::units::si::ElectricChargeType constexpr GetCharge(Code const);
-  corsika::units::si::HEPMassType constexpr GetMass(Code const);
-  PDGCode constexpr GetPDG(Code const);
-  constexpr std::string const& GetName(Code const);
-  corsika::units::si::TimeType constexpr GetLifetime(Code const);
-
-  bool constexpr IsNucleus(Code const);
-  bool constexpr IsHadron(Code const);
-  bool constexpr IsEM(Code const);
-  bool constexpr IsMuon(Code const);
-  bool constexpr IsNeutrino(Code const);
-  int constexpr GetNucleusA(Code const);
-  int constexpr GetNucleusZ(Code const);
-
-} // namespace corsika::particles
-
-// here we read the implementation of all those objects and function
-#include <corsika/particles/GeneratedParticleProperties.inc>
-
-namespace corsika::particles {
-
-  /*!
-   * returns mass of particle in natural units
-   */
-  corsika::units::si::HEPMassType constexpr GetMass(Code const p) {
-    if (p == Code::Nucleus)
-      throw std::runtime_error("Cannot GetMass() of particle::Nucleus -> unspecified");
-    return detail::masses[static_cast<CodeIntType>(p)];
-  }
-
-  /*!
-   * returns PDG id
-   */
-  PDGCode constexpr GetPDG(Code const p) {
-    return detail::pdg_codes[static_cast<CodeIntType>(p)];
-  }
-
-  /*!
-   * returns electric charge number of particle return 1 for a proton.
-   */
-  int16_t constexpr GetChargeNumber(Code const p) {
-    if (p == Code::Nucleus)
-      throw std::runtime_error(
-          "Cannot GetChargeNumber() of particle::Nucleus -> unspecified");
-    // electric_charges stores charges in units of (e/3), e.g. 3 for a proton
-    return detail::electric_charges[static_cast<CodeIntType>(p)] / 3;
-  }
-
-  /*!
-   * returns electric charge of particle, e.g. return 1.602e-19_C for a proton.
-   */
-  corsika::units::si::ElectricChargeType constexpr GetCharge(Code const p) {
-    if (p == Code::Nucleus)
-      throw std::runtime_error("Cannot GetCharge() of particle::Nucleus -> unspecified");
-    return GetChargeNumber(p) * corsika::units::constants::e;
-  }
-
-  constexpr std::string const& GetName(Code const p) {
-    return detail::names[static_cast<CodeIntType>(p)];
-  }
-
-  corsika::units::si::TimeType constexpr GetLifetime(Code const p) {
-    return detail::lifetime[static_cast<CodeIntType>(p)] * corsika::units::si::second;
-  }
-
-  bool constexpr IsHadron(Code const p) {
-    return detail::isHadron[static_cast<CodeIntType>(p)];
-  }
-
-  bool constexpr IsEM(Code c) {
-    return c == Code::Electron || c == Code::Positron || c == Code::Gamma;
-  }
-
-  bool constexpr IsMuon(Code c) { return c == Code::MuPlus || c == Code::MuMinus; }
-
-  bool constexpr IsNeutrino(Code c) {
-    return c == Code::NuE || c == Code::NuMu || c == Code::NuTau || c == Code::NuEBar ||
-           c == Code::NuMuBar || c == Code::NuTauBar;
-  }
-
-  int constexpr GetNucleusA(Code const p) {
-    if (p == Code::Nucleus) {
-      throw std::runtime_error("GetNucleusA(Code::Nucleus) is impossible!");
-    }
-    return detail::nucleusA[static_cast<CodeIntType>(p)];
-  }
-
-  int constexpr GetNucleusZ(Code const p) {
-    if (p == Code::Nucleus) {
-      throw std::runtime_error("GetNucleusZ(Code::Nucleus) is impossible!");
-    }
-    return detail::nucleusZ[static_cast<CodeIntType>(p)];
-  }
-
-  bool constexpr IsNucleus(Code const p) {
-    return (p == Code::Nucleus) || (GetNucleusA(p) != 0);
-  }
-
-  /**
-   * the output operator for humand-readable particle codes
-   **/
-
-  std::ostream& operator<<(std::ostream&, corsika::particles::Code);
-
-  Code ConvertFromPDG(PDGCode);
-
-  /**
-   * Get mass of nucleus
-   **/
-  corsika::units::si::HEPMassType constexpr GetNucleusMass(const int vA, const int vZ) {
-    return Proton::GetMass() * vZ + (vA - vZ) * Neutron::GetMass();
-  }
-
-  std::initializer_list<Code> constexpr getAllParticles() {
-    return detail::all_particles;
-  }
-
-} // namespace corsika::particles
diff --git a/Framework/Particles/particles.dox b/Framework/Particles/particles.dox
deleted file mode 100644
index 22a36c59f0a752074c076d1fb0f6450f0dff51dd..0000000000000000000000000000000000000000
--- a/Framework/Particles/particles.dox
+++ /dev/null
@@ -1,15 +0,0 @@
-/**
-@page Particles Particle properties
-
-The properties of all particles are saved in static and flat
-arrays. There is a enum corsika::particles::Code to identify each
-particles, and each individual particles has its own static class,
-which can be used to retrieve its physical properties. 
-
-
-
-See namespace corsika::particles for all classes.
-
-
-
-*/
\ No newline at end of file
diff --git a/Framework/Particles/testParticles.cc b/Framework/Particles/testParticles.cc
deleted file mode 100644
index 8b3f7184c6b31d5b447dbdb173847cdfc0d080ae..0000000000000000000000000000000000000000
--- a/Framework/Particles/testParticles.cc
+++ /dev/null
@@ -1,143 +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/particles/ParticleProperties.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <catch2/catch.hpp>
-
-using namespace corsika::units;
-using namespace corsika::units::si;
-using namespace corsika::particles;
-
-TEST_CASE("ParticleProperties", "[Particles]") {
-
-  SECTION("Types") {
-    CHECK(Electron::GetCode() == Code::Electron);
-    CHECK(Positron::GetCode() == Code::Positron);
-    CHECK(Proton::GetCode() == Code::Proton);
-    CHECK(Neutron::GetCode() == Code::Neutron);
-    CHECK(Gamma::GetCode() == Code::Gamma);
-    CHECK(PiPlus::GetCode() == Code::PiPlus);
-  }
-
-  SECTION("Masses") {
-    CHECK(Electron::GetMass() / (511_keV) == Approx(1));
-    CHECK(Electron::GetMass() / GetMass(Code::Electron) == Approx(1));
-
-    CHECK((Proton::GetMass() + Neutron::GetMass()) /
-              corsika::units::constants::nucleonMass ==
-          Approx(2));
-  }
-
-  SECTION("Charges") {
-    CHECK(Electron::GetCharge() / constants::e == Approx(-1));
-    CHECK(Positron::GetCharge() / constants::e == Approx(+1));
-    CHECK(GetCharge(Positron::GetAntiParticle()) / constants::e == Approx(-1));
-  }
-
-  SECTION("Names") {
-    CHECK(Electron::GetName() == "e-");
-    CHECK(PiMinus::GetName() == "pi-");
-    CHECK(Nucleus::GetName() == "nucleus");
-    CHECK(Iron::GetName() == "iron");
-  }
-
-  SECTION("PDG") {
-    CHECK(GetPDG(Code::PiPlus) == PDGCode::PiPlus);
-    CHECK(GetPDG(Code::DPlus) == PDGCode::DPlus);
-    CHECK(GetPDG(Code::NuMu) == PDGCode::NuMu);
-    CHECK(GetPDG(Code::NuE) == PDGCode::NuE);
-    CHECK(GetPDG(Code::MuMinus) == PDGCode::MuMinus);
-
-    CHECK(static_cast<int>(GetPDG(Code::PiPlus)) == 211);
-    CHECK(static_cast<int>(GetPDG(Code::DPlus)) == 411);
-    CHECK(static_cast<int>(GetPDG(Code::NuMu)) == 14);
-    CHECK(static_cast<int>(GetPDG(Code::NuEBar)) == -12);
-    CHECK(static_cast<int>(GetPDG(Code::MuMinus)) == 13);
-  }
-
-  SECTION("Conversion PDG -> internal") {
-    CHECK(ConvertFromPDG(PDGCode::KStarMinus) == Code::KStarMinus);
-    CHECK(ConvertFromPDG(PDGCode::MuPlus) == Code::MuPlus);
-    CHECK(ConvertFromPDG(PDGCode::SigmaStarCMinusBar) == Code::SigmaStarCMinusBar);
-  }
-
-  SECTION("Lifetimes") {
-    CHECK(GetLifetime(Code::Electron) ==
-          std::numeric_limits<double>::infinity() * corsika::units::si::second);
-    CHECK(GetLifetime(Code::DPlus) < GetLifetime(Code::Gamma));
-    CHECK(GetLifetime(Code::RhoPlus) / corsika::units::si::second ==
-          (Approx(4.414566727909413e-24).epsilon(1e-3)));
-    CHECK(GetLifetime(Code::SigmaMinusBar) / corsika::units::si::second ==
-          (Approx(8.018880848563575e-11).epsilon(1e-5)));
-    CHECK(GetLifetime(Code::MuPlus) / corsika::units::si::second ==
-          (Approx(2.1970332555864364e-06).epsilon(1e-5)));
-  }
-
-  SECTION("Particle groups: electromagnetic") {
-    CHECK(IsEM(Code::Gamma));
-    CHECK(IsEM(Code::Electron));
-    CHECK_FALSE(IsEM(Code::MuPlus));
-    CHECK_FALSE(IsEM(Code::NuE));
-    CHECK_FALSE(IsEM(Code::Proton));
-    CHECK_FALSE(IsEM(Code::PiPlus));
-    CHECK_FALSE(IsEM(Code::Oxygen));
-  }
-
-  SECTION("Particle groups: hadrons") {
-    CHECK_FALSE(IsHadron(Code::Gamma));
-    CHECK_FALSE(IsHadron(Code::Electron));
-    CHECK_FALSE(IsHadron(Code::MuPlus));
-    CHECK_FALSE(IsHadron(Code::NuE));
-    CHECK(IsHadron(Code::Proton));
-    CHECK(IsHadron(Code::PiPlus));
-    CHECK(IsHadron(Code::Oxygen));
-    CHECK(IsHadron(Code::Nucleus));
-  }
-
-  SECTION("Particle groups: muons") {
-    CHECK_FALSE(IsMuon(Code::Gamma));
-    CHECK_FALSE(IsMuon(Code::Electron));
-    CHECK(IsMuon(Code::MuPlus));
-    CHECK_FALSE(IsMuon(Code::NuE));
-    CHECK_FALSE(IsMuon(Code::Proton));
-    CHECK_FALSE(IsMuon(Code::PiPlus));
-    CHECK_FALSE(IsMuon(Code::Oxygen));
-  }
-
-  SECTION("Particle groups: neutrinos") {
-    CHECK_FALSE(IsNeutrino(Code::Gamma));
-    CHECK_FALSE(IsNeutrino(Code::Electron));
-    CHECK_FALSE(IsNeutrino(Code::MuPlus));
-    CHECK(IsNeutrino(Code::NuE));
-    CHECK_FALSE(IsNeutrino(Code::Proton));
-    CHECK_FALSE(IsNeutrino(Code::PiPlus));
-    CHECK_FALSE(IsNeutrino(Code::Oxygen));
-  }
-
-  SECTION("Nuclei") {
-    CHECK_FALSE(IsNucleus(Code::Gamma));
-    CHECK(IsNucleus(Code::Argon));
-    CHECK_FALSE(IsNucleus(Code::Proton));
-    CHECK(IsNucleus(Code::Hydrogen));
-    CHECK(Argon::IsNucleus());
-    CHECK_FALSE(EtaC::IsNucleus());
-
-    CHECK(GetNucleusA(Code::Hydrogen) == 1);
-    CHECK(GetNucleusA(Code::Tritium) == 3);
-    CHECK(Hydrogen::GetNucleusZ() == 1);
-    CHECK(Tritium::GetNucleusA() == 3);
-
-    // Nucleus is a generic object, it has no specific properties
-    CHECK_THROWS(GetNucleusA(Code::Nucleus));
-    CHECK_THROWS(GetNucleusZ(Code::Nucleus));
-    CHECK_THROWS(GetMass(Code::Nucleus));
-    CHECK_THROWS(GetCharge(Code::Nucleus));
-  }
-}
diff --git a/Framework/ProcessSequence/BoundaryCrossingProcess.h b/Framework/ProcessSequence/BoundaryCrossingProcess.h
deleted file mode 100644
index 6526648a8fc36f422ec22ebdea529e572c42ef95..0000000000000000000000000000000000000000
--- a/Framework/ProcessSequence/BoundaryCrossingProcess.h
+++ /dev/null
@@ -1,32 +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/process/BaseProcess.h>
-#include <corsika/process/ProcessReturn.h>
-
-#include <type_traits>
-
-namespace corsika::process {
-
-  template <typename TDerived>
-  class BoundaryCrossingProcess : public BaseProcess<TDerived> {
-  private:
-  protected:
-  public:
-    /**
-     * This method is called when a particle crosses the boundary between the nodes
-     * \p from and \p to.
-     */
-    template <typename TParticle, typename TVolumeNode>
-    EProcessReturn DoBoundaryCrossing(TParticle&, TVolumeNode const& from,
-                                      TVolumeNode const& to);
-  };
-
-} // namespace corsika::process
diff --git a/Framework/ProcessSequence/CMakeLists.txt b/Framework/ProcessSequence/CMakeLists.txt
deleted file mode 100644
index 7a75548932875f31f19f4fdd0aab3fbf19cbf867..0000000000000000000000000000000000000000
--- a/Framework/ProcessSequence/CMakeLists.txt
+++ /dev/null
@@ -1,82 +0,0 @@
-add_library (CORSIKAprocesssequence INTERFACE)
-
-#namespace of library->location of header files
-set (
-  CORSIKAprocesssequence_NAMESPACE
-  corsika/process
-  )
-
-#header files of this library
-set (
-  CORSIKAprocesssequence_HEADERS
-  BaseProcess.h
-  BoundaryCrossingProcess.h
-  ContinuousProcess.h
-  SecondariesProcess.h
-  InteractionProcess.h
-  StackProcess.h
-  DecayProcess.h
-  ProcessSequence.h
-  SwitchProcessSequence.h
-  ProcessReturn.h
-  ProcessTraits.h
-  NullModel.h
-  )
-
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAprocesssequence ${CORSIKAprocesssequence_NAMESPACE} ${CORSIKAprocesssequence_HEADERS})
-
-#include directive for upstream code
-target_link_libraries (
-  CORSIKAprocesssequence
-  INTERFACE
-  CORSIKAsetup
-  CORSIKAlogging
-  )
-
-target_include_directories (
-  CORSIKAprocesssequence
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/>
-  )
-
-#install library
-install (
-  FILES ${CORSIKAprocesssequence_HEADERS}
-  DESTINATION include/${CORSIKAprocesssequence_NAMESPACE}
-  )
-  
-target_link_libraries (
-  CORSIKAprocesssequence
-  INTERFACE
-  CORSIKAenvironment
-)
-
-#-- -- -- -- -- -- -- --
-#code unit testing
-CORSIKA_ADD_TEST (testProcessSequence)
-target_link_libraries (
-  testProcessSequence
-  CORSIKAgeometry
-  CORSIKAprocesssequence
-  CORSIKAtesting
-  )
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST (testNullModel)
-target_link_libraries (
-  testNullModel
-  CORSIKAsetup
-  CORSIKAgeometry
-  CORSIKAunits
-  CORSIKAtesting
-  )
-
-# # CORSIKA_ADD_TEST (testSwitchProcessSequence)
-# # target_link_libraries (
-# #   testSwitchProcessSequence
-# #   CORSIKAgeometry
-# #   CORSIKAprocesssequence
-# #   CORSIKAtesting
-# #   )
diff --git a/Framework/ProcessSequence/DecayProcess.h b/Framework/ProcessSequence/DecayProcess.h
deleted file mode 100644
index 09ba807434de88dbbe1952ebf1c86de7e260d0ed..0000000000000000000000000000000000000000
--- a/Framework/ProcessSequence/DecayProcess.h
+++ /dev/null
@@ -1,49 +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/process/BaseProcess.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::process {
-
-  /**
-     \class DecayProcess
-
-     The structural base type of a process object in a
-     ProcessSequence. Both, the ProcessSequence and all its elements
-     are of type DecayProcess<T>
-
-   */
-
-  template <typename TDerived>
-  struct DecayProcess : BaseProcess<TDerived> {
-  public:
-    using BaseProcess<TDerived>::GetRef;
-
-    /// here starts the interface-definition part
-    // -> enforce TDerived to implement DoDecay...
-    template <typename TParticle>
-    EProcessReturn DoDecay(TParticle&);
-
-    template <typename TParticle>
-    corsika::units::si::TimeType GetLifetime(const TParticle&);
-
-    template <typename TParticle>
-    corsika::units::si::InverseTimeType GetInverseLifetime(const TParticle& particle) {
-      return 1. / GetRef().GetLifetime(particle);
-    }
-
-    /*    template <typename TParticle>
-    corsika::units::si::InverseTimeType GetInverseInteractionLength(TParticle&& particle)
-    { auto p = std::move(particle); return 1. / GetRef().GetLifetime(p);
-      }*/
-  };
-
-} // namespace corsika::process
diff --git a/Framework/ProcessSequence/ProcessReturn.h b/Framework/ProcessSequence/ProcessReturn.h
deleted file mode 100644
index c4e7d5890411e461b8d50c4b31d44e6803b6c8d2..0000000000000000000000000000000000000000
--- a/Framework/ProcessSequence/ProcessReturn.h
+++ /dev/null
@@ -1,58 +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
-
-namespace corsika::process {
-
-  /**
-     since in a process sequence many status updates can accumulate
-     for a single particle, this enum should define only bit-flags
-     that can be accumulated easily with "|="
-   */
-
-  enum class EProcessReturn : int {
-    eOk = (1 << 0),
-    eParticleAbsorbed = (1 << 2),
-    eInteracted = (1 << 3),
-    eDecayed = (1 << 4),
-  };
-
-  inline EProcessReturn operator|(EProcessReturn a, EProcessReturn b) {
-    return static_cast<EProcessReturn>(static_cast<int>(a) | static_cast<int>(b));
-  }
-
-  inline EProcessReturn& operator|=(EProcessReturn& a, const EProcessReturn b) {
-    return a = a | b;
-  }
-
-  inline EProcessReturn operator&(const EProcessReturn a, const EProcessReturn b) {
-    return static_cast<EProcessReturn>(static_cast<int>(a) & static_cast<int>(b));
-  }
-
-  inline bool operator==(const EProcessReturn a, const EProcessReturn b) {
-    return (static_cast<int>(a) & static_cast<int>(b)) != 0;
-  }
-
-  inline bool isOk(const EProcessReturn a) {
-    return static_cast<int>(a & EProcessReturn::eOk);
-  }
-
-  inline bool isAbsorbed(const EProcessReturn a) {
-    return static_cast<int>(a & EProcessReturn::eParticleAbsorbed);
-  }
-
-  inline bool isDecayed(const EProcessReturn a) {
-    return static_cast<int>(a & EProcessReturn::eDecayed);
-  }
-
-  inline bool isInteracted(const EProcessReturn a) {
-    return static_cast<int>(a & EProcessReturn::eInteracted);
-  }
-
-} // namespace corsika::process
diff --git a/Framework/ProcessSequence/ProcessSequence.h b/Framework/ProcessSequence/ProcessSequence.h
deleted file mode 100644
index 02a3d062ea09792f9c9e4f40d030bd7fc3c02414..0000000000000000000000000000000000000000
--- a/Framework/ProcessSequence/ProcessSequence.h
+++ /dev/null
@@ -1,396 +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/process/BaseProcess.h>
-#include <corsika/process/ProcessTraits.h>
-#include <corsika/process/BoundaryCrossingProcess.h>
-#include <corsika/process/ContinuousProcess.h>
-#include <corsika/process/DecayProcess.h>
-#include <corsika/process/InteractionProcess.h>
-#include <corsika/process/ProcessReturn.h>
-#include <corsika/process/SecondariesProcess.h>
-#include <corsika/process/StackProcess.h>
-#include <corsika/process/NullModel.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/logging/Logging.h>
-
-#include <cmath>
-#include <limits>
-
-namespace corsika::process {
-
-  /**
-     \class ProcessSequence
-
-     A compile time static list of processes. The compiler will
-     generate a new type based on template logic containing all the
-     elements provided by the user.
-
-     TProcess1 and TProcess2 must both be derived from BaseProcess,
-     and are both references if possible (lvalue), otherwise (rvalue)
-     they are just classes. This allows us to handle both, rvalue as
-     well as lvalue Processes in the ProcessSequence.
-
-     \comment Using CRTP pattern,
-     https://en.wikipedia.org/wiki/Curiously_recurring_template_pattern
-   */
-  template <typename TProcess1, typename TProcess2 = NullModel>
-  class ProcessSequence : public BaseProcess<ProcessSequence<TProcess1, TProcess2>> {
-
-    using TProcess1type = typename std::decay_t<TProcess1>;
-    using TProcess2type = typename std::decay_t<TProcess2>;
-
-    static bool constexpr t1ProcSeq = is_process_sequence_v<TProcess1type>;
-    static bool constexpr t2ProcSeq = is_process_sequence_v<TProcess2type>;
-
-    static bool constexpr t1SwitchProcSeq = is_switch_process_sequence_v<TProcess1type>;
-    static bool constexpr t2SwitchProcSeq = is_switch_process_sequence_v<TProcess2type>;
-
-    // make sure only BaseProcess types TProcess1/2 are passed
-    static_assert(std::is_base_of_v<BaseProcess<TProcess1type>, TProcess1type>,
-                  "can only use process derived from BaseProcess in "
-                  "ProcessSequence, for Process 1");
-    static_assert(std::is_base_of_v<BaseProcess<TProcess2type>, TProcess2type>,
-                  "can only use process derived from BaseProcess in "
-                  "ProcessSequence, for Process 2");
-
-    TProcess1 A_; // this is a reference, if possible
-    TProcess2 B_; // this is a reference, if possible
-
-  public:
-    ProcessSequence(TProcess1 in_A, TProcess2 in_B)
-        : A_(in_A)
-        , B_(in_B) {}
-
-    template <typename TParticle, typename TVTNType>
-    EProcessReturn DoBoundaryCrossing(TParticle& particle, TVTNType const& from,
-                                      TVTNType const& to) {
-      EProcessReturn ret = EProcessReturn::eOk;
-
-      if constexpr (std::is_base_of_v<BoundaryCrossingProcess<TProcess1type>,
-                                      TProcess1type> ||
-                    t1ProcSeq) {
-        ret |= A_.DoBoundaryCrossing(particle, from, to);
-      }
-
-      if constexpr (std::is_base_of_v<BoundaryCrossingProcess<TProcess2type>,
-                                      TProcess2type> ||
-                    t2ProcSeq) {
-        ret |= B_.DoBoundaryCrossing(particle, from, to);
-      }
-
-      return ret;
-    }
-
-    template <typename TParticle, typename TTrack>
-    inline EProcessReturn DoContinuous(TParticle& particle, TTrack& vT) {
-      EProcessReturn ret = EProcessReturn::eOk;
-      if constexpr (std::is_base_of_v<ContinuousProcess<TProcess1type>, TProcess1type> ||
-                    t1ProcSeq) {
-        ret |= A_.DoContinuous(particle, vT);
-      }
-      if constexpr (std::is_base_of_v<ContinuousProcess<TProcess2type>, TProcess2type> ||
-                    t2ProcSeq) {
-        if (!isAbsorbed(ret)) { ret |= B_.DoContinuous(particle, vT); }
-      }
-      return ret;
-    }
-
-    template <typename TSecondaries>
-    inline void DoSecondaries(TSecondaries& vS) {
-      if constexpr (std::is_base_of_v<SecondariesProcess<TProcess1type>, TProcess1type> ||
-                    t1ProcSeq) {
-        A_.DoSecondaries(vS);
-      }
-      if constexpr (std::is_base_of_v<SecondariesProcess<TProcess2type>, TProcess2type> ||
-                    t2ProcSeq) {
-        B_.DoSecondaries(vS);
-      }
-    }
-
-    /**
-       The processes of type StackProcess do have an internal counter,
-       so they can be exectuted only each N steps. Often these are
-       "maintenacne processes" that do not need to run after each
-       single step of the simulations. In the CheckStep function it is
-       tested if either A_ or B_ are StackProcess and if they are due
-       for execution.
-     */
-    inline bool CheckStep() {
-      bool ret = false;
-      if constexpr (std::is_base_of_v<StackProcess<TProcess1type>, TProcess1type> ||
-                    (t1ProcSeq && !t1SwitchProcSeq)) {
-        ret |= A_.CheckStep();
-      }
-      if constexpr (std::is_base_of_v<StackProcess<TProcess2type>, TProcess2type> ||
-                    (t2ProcSeq && !t2SwitchProcSeq)) {
-        ret |= B_.CheckStep();
-      }
-      return ret;
-    }
-
-    /**
-       Execute the StackProcess-es in the ProcessSequence
-     */
-    template <typename TStack>
-    inline void DoStack(TStack& stack) {
-      if constexpr (std::is_base_of_v<StackProcess<TProcess1type>, TProcess1type> ||
-                    (t1ProcSeq && !t1SwitchProcSeq)) {
-        if (A_.CheckStep()) { A_.DoStack(stack); }
-      }
-      if constexpr (std::is_base_of_v<StackProcess<TProcess2type>, TProcess2type> ||
-                    (t2ProcSeq && !t2SwitchProcSeq)) {
-        if (B_.CheckStep()) { B_.DoStack(stack); }
-      }
-    }
-
-    template <typename TParticle, typename TTrack>
-    inline corsika::units::si::LengthType MaxStepLength(TParticle& particle,
-                                                        TTrack& vTrack) {
-      corsika::units::si::LengthType
-          max_length = // if no other process in the sequence implements it
-          std::numeric_limits<double>::infinity() * corsika::units::si::meter;
-
-      if constexpr (std::is_base_of_v<ContinuousProcess<TProcess1type>, TProcess1type> ||
-                    t1ProcSeq) {
-        corsika::units::si::LengthType const len = A_.MaxStepLength(particle, vTrack);
-        max_length = std::min(max_length, len);
-      }
-      if constexpr (std::is_base_of_v<ContinuousProcess<TProcess2type>, TProcess2type> ||
-                    t2ProcSeq) {
-        corsika::units::si::LengthType const len = B_.MaxStepLength(particle, vTrack);
-        max_length = std::min(max_length, len);
-      }
-      return max_length;
-    }
-
-    template <typename TParticle>
-    inline corsika::units::si::GrammageType GetInteractionLength(TParticle&& particle) {
-      return 1. / GetInverseInteractionLength(particle);
-    }
-
-    template <typename TParticle>
-    inline corsika::units::si::InverseGrammageType GetInverseInteractionLength(
-        TParticle&& particle) {
-      using namespace corsika::units::si;
-
-      InverseGrammageType tot = 0 * meter * meter / gram; // default value
-
-      if constexpr (std::is_base_of_v<InteractionProcess<TProcess1type>, TProcess1type> ||
-                    t1ProcSeq) {
-        tot += A_.GetInverseInteractionLength(particle);
-      }
-      if constexpr (std::is_base_of_v<InteractionProcess<TProcess2type>, TProcess2type> ||
-                    t2ProcSeq) {
-        tot += B_.GetInverseInteractionLength(particle);
-      }
-      return tot;
-    }
-
-    template <typename TSecondaryView>
-    inline EProcessReturn SelectInteraction(
-        TSecondaryView& view,
-        [[maybe_unused]] corsika::units::si::InverseGrammageType lambda_inv_select,
-        [[maybe_unused]] corsika::units::si::InverseGrammageType lambda_inv_sum =
-            corsika::units::si::InverseGrammageType::zero()) {
-
-      // TODO: add check for lambda_inv_select>lambda_inv_tot
-
-      if constexpr (t1ProcSeq) {
-        // if A is a process sequence --> check inside
-        const EProcessReturn ret =
-            A_.SelectInteraction(view, lambda_inv_select, lambda_inv_sum);
-        // if A_ did succeed, stop routine. Not checking other static branch B_.
-        if (ret != EProcessReturn::eOk) { return ret; }
-      } else if constexpr (std::is_base_of_v<InteractionProcess<TProcess1type>,
-                                             TProcess1type>) {
-        // if this is not a ContinuousProcess --> evaluate probability
-        const auto particle = view.parent();
-        lambda_inv_sum += A_.GetInverseInteractionLength(particle);
-        // check if we should execute THIS process and then EXIT
-        if (lambda_inv_select <= lambda_inv_sum) {
-          A_.DoInteraction(view);
-          return EProcessReturn::eInteracted;
-        }
-      } // end branch A_
-
-      if constexpr (t2ProcSeq) {
-        // if B_ is a process sequence --> check inside
-        return B_.SelectInteraction(view, lambda_inv_select, lambda_inv_sum);
-      } else if constexpr (std::is_base_of_v<InteractionProcess<TProcess2type>,
-                                             TProcess2type>) {
-        // if this is not a ContinuousProcess --> evaluate probability
-        lambda_inv_sum += B_.GetInverseInteractionLength(view.parent());
-        // check if we should execute THIS process and then EXIT
-        if (lambda_inv_select <= lambda_inv_sum) {
-          B_.DoInteraction(view);
-          return EProcessReturn::eInteracted;
-        }
-      } // end branch B_
-      return EProcessReturn::eOk;
-    }
-
-    template <typename TParticle>
-    inline corsika::units::si::TimeType GetLifetime(TParticle& particle) {
-      return 1. / GetInverseLifetime(particle);
-    }
-
-    template <typename TParticle>
-    inline corsika::units::si::InverseTimeType GetInverseLifetime(TParticle&& particle) {
-      using namespace corsika::units::si;
-
-      corsika::units::si::InverseTimeType tot = 0 / second; // default value
-
-      if constexpr (std::is_base_of_v<DecayProcess<TProcess1type>, TProcess1type> ||
-                    t1ProcSeq) {
-        tot += A_.GetInverseLifetime(particle);
-      }
-      if constexpr (std::is_base_of_v<DecayProcess<TProcess2type>, TProcess2type> ||
-                    t2ProcSeq) {
-        tot += B_.GetInverseLifetime(particle);
-      }
-      return tot;
-    }
-
-    // select decay process
-    template <typename TSecondaryView>
-    inline EProcessReturn SelectDecay(
-        TSecondaryView& view,
-        [[maybe_unused]] corsika::units::si::InverseTimeType decay_inv_select,
-        [[maybe_unused]] corsika::units::si::InverseTimeType decay_inv_sum =
-            corsika::units::si::InverseTimeType::zero()) {
-
-      // TODO: add check for decay_inv_select>decay_inv_tot
-
-      if constexpr (t1ProcSeq) {
-        // if A_ is a process sequence --> check inside
-        const EProcessReturn ret = A_.SelectDecay(view, decay_inv_select, decay_inv_sum);
-        // if A_ did succeed, stop routine here (not checking other static branch B_)
-        if (ret != EProcessReturn::eOk) { return ret; }
-      } else if constexpr (std::is_base_of_v<DecayProcess<TProcess1type>,
-                                             TProcess1type>) {
-        // if this is not a ContinuousProcess --> evaluate probability
-        decay_inv_sum += A_.GetInverseLifetime(view.parent());
-        // check if we should execute THIS process and then EXIT
-        if (decay_inv_select <= decay_inv_sum) { // more pedagogical: rndm_select <
-                                                 // decay_inv_sum / decay_inv_tot
-          A_.DoDecay(view);
-          return EProcessReturn::eDecayed;
-        }
-      } // end branch A_
-
-      if constexpr (t2ProcSeq) {
-        // if B_ is a process sequence --> check inside
-        return B_.SelectDecay(view, decay_inv_select, decay_inv_sum);
-      } else if constexpr (std::is_base_of_v<DecayProcess<TProcess2type>,
-                                             TProcess2type>) {
-        // if this is not a ContinuousProcess --> evaluate probability
-        decay_inv_sum += B_.GetInverseLifetime(view.parent());
-        // check if we should execute THIS process and then EXIT
-        if (decay_inv_select <= decay_inv_sum) {
-          B_.DoDecay(view);
-          return EProcessReturn::eDecayed;
-        }
-      } // end branch B_
-      return EProcessReturn::eOk;
-    }
-  };
-
-  /**
-   * \function sequence
-   *
-   * to construct ProcessSequences in a flexible and dynamic way the
-   * `sequence` factory functions are provided
-   *
-   * Any objects of type
-   *  - BaseProcess,
-   *  - ContinuousProcess, and
-   *  - Interaction/DecayProcess,
-   *  - StackProcess,
-   *  - SecondariesProcess
-   * can be assembled into a ProcessSequence, all
-   * combinatorics are allowed.
-
-   * The sequence function checks that all its arguments are all of
-   * types derived from BaseProcess. Also the ProcessSequence itself
-   * is derived from type BaseProcess
-   **/
-
-  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>>,
-      ProcessSequence<TProcess1, decltype(sequence(std::declval<TProcesses>()...))>>
-  sequence(TProcess1&& vA, TProcesses&&... vBs) {
-    return ProcessSequence<TProcess1, decltype(sequence(std::declval<TProcesses>()...))>(
-        vA, sequence(std::forward<TProcesses>(vBs)...));
-  }
-
-  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>>
-  sequence(TProcess1&& vA, TProcess2&& vB) {
-    return ProcessSequence<TProcess1, TProcess2>(vA, vB);
-  }
-
-  /**
-   * also allow a single Process in ProcessSequence, accompany by
-   * `NullModel`
-   **/
-  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>>
-  sequence(TProcess&& vA) {
-    return ProcessSequence<TProcess, NullModel>(vA, NullModel());
-  }
-
-  /**
-   * traits marker to identify objectas ProcessSequence
-   **/
-  template <typename TProcess1, typename TProcess2>
-  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_sequence() {}
-  };
-
-  /**
-   * traits marker to identify objects containing any StackProcesses
-   **/
-  namespace detail {
-    // need helper alias to achieve this:
-    template <typename TProcess1, typename TProcess2,
-              typename = typename std::enable_if_t<
-                  contains_stack_process_v<TProcess1> ||
-                      std::is_base_of_v<StackProcess<typename std::decay_t<TProcess1>>,
-                                        typename std::decay_t<TProcess1>> ||
-                      contains_stack_process_v<TProcess2> ||
-                      std::is_base_of_v<StackProcess<typename std::decay_t<TProcess2>>,
-                                        typename std::decay_t<TProcess2>>,
-                  int>>
-    using enable_if_stack = ProcessSequence<TProcess1, TProcess2>;
-  } // namespace detail
-
-  template <typename TProcess1, typename TProcess2>
-  struct contains_stack_process<detail::enable_if_stack<TProcess1, TProcess2>>
-      : std::true_type {};
-
-} // namespace corsika::process
diff --git a/Framework/ProcessSequence/ProcessSignature.h b/Framework/ProcessSequence/ProcessSignature.h
deleted file mode 100644
index 346e8a33cc2bb3898c5cce5153cac573d4ee7bd0..0000000000000000000000000000000000000000
--- a/Framework/ProcessSequence/ProcessSignature.h
+++ /dev/null
@@ -1,26 +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
-
-#define FORCE_SIGNATURE(nameTrait, nameMethod, signatureMethod)                \
-  template <typename U>                                                        \
-  class nameTrait {                                                            \
-  private:                                                                     \
-    template <typename T, T>                                                   \
-    struct helper;                                                             \
-    template <typename T>                                                      \
-    static std::uint8_t check(helper<signatureMethod, &nameMethod>*);          \
-    template <typename T>                                                      \
-    static std::uint16_t check(...);                                           \
-                                                                               \
-  public:                                                                      \
-    static constexpr bool value = sizeof(check<U>(0)) == sizeof(std::uint8_t); \
-  }
-
-// FORCE_SIGNATURE(thisMustBeDefined, T::thisMustBeDefined, int(*)(void));
diff --git a/Framework/ProcessSequence/SwitchProcessSequence.h b/Framework/ProcessSequence/SwitchProcessSequence.h
deleted file mode 100644
index 9f69e73c40c61c7ad5461ac77120ec4a182e4ce2..0000000000000000000000000000000000000000
--- a/Framework/ProcessSequence/SwitchProcessSequence.h
+++ /dev/null
@@ -1,386 +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/process/BaseProcess.h>
-#include <corsika/process/ProcessTraits.h>
-#include <corsika/process/BoundaryCrossingProcess.h>
-#include <corsika/process/ContinuousProcess.h>
-#include <corsika/process/DecayProcess.h>
-#include <corsika/process/InteractionProcess.h>
-#include <corsika/process/ProcessReturn.h>
-#include <corsika/process/SecondariesProcess.h>
-#include <corsika/process/StackProcess.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <cmath>
-#include <limits>
-#include <type_traits>
-
-namespace corsika::process {
-
-  // enum for the process switch selection: identify if First or
-  // Second process branch should be used.
-  enum class SwitchResult { First, Second };
-
-  /**
-     \class SwitchProcessSequence
-
-     A compile time static list of processes that uses an internal
-     TSelect class to switch between different versions of processes
-     (or process sequence).
-
-     TProcess1 and TProcess2 must be derived from BaseProcess and are
-     both references if possible (lvalue), otherwise (rvalue) they are
-     just classes. This allows us to handle both, rvalue as well as
-     lvalue Processes in the SwitchProcessSequence.
-
-     TSelect has to implement a `operator()(const Particle&)` and has to
-     return either SwitchResult::First or SwitchResult::Second. Note:
-     TSelect may absolutely also use random numbers to sample between
-     its results. This can be used to achieve arbitrarily smooth
-     transition or mixtures of processes.
-
-     Warning: do not put StackProcess into a SwitchProcessSequence
-     since this makes no sense. The StackProcess acts on an entire
-     particle stack and not on indiviidual particles.
-
-     \comment See also class ProcessSequence
-  **/
-
-  template <typename TProcess1, typename TProcess2, typename TSelect>
-  class SwitchProcessSequence
-      : public BaseProcess<SwitchProcessSequence<TProcess1, TProcess2, TSelect>> {
-
-    using TProcess1type = typename std::decay_t<TProcess1>;
-    using TProcess2type = typename std::decay_t<TProcess2>;
-
-    static bool constexpr t1ProcSeq = is_process_sequence_v<TProcess1type>;
-    static bool constexpr t2ProcSeq = is_process_sequence_v<TProcess2type>;
-
-    // make sure only BaseProcess types TProcess1/2 are passed
-    static_assert(std::is_base_of_v<BaseProcess<TProcess1type>, TProcess1type>,
-                  "can only use process derived from BaseProcess in "
-                  "SwitchProcessSequence, for Process 1");
-    static_assert(std::is_base_of_v<BaseProcess<TProcess2type>, TProcess2type>,
-                  "can only use process derived from BaseProcess in "
-                  "SwitchProcessSequence, for Process 2");
-
-    // make sure none of TProcess1/2 is a StackProcess
-    static_assert(!std::is_base_of_v<StackProcess<TProcess1type>, TProcess1type>,
-                  "cannot use StackProcess in SwitchProcessSequence, for Process 1");
-    static_assert(!std::is_base_of_v<StackProcess<TProcess2type>, TProcess2type>,
-                  "cannot use StackProcess in SwitchProcessSequence, for Process 2");
-
-    // if TProcess1/2 are already ProcessSequences, make sure they do not contain
-    // any StackProcess
-    static_assert(!contains_stack_process_v<TProcess1type>,
-                  "cannot use StackProcess in SwitchProcessSequence, remove from "
-                  "ProcessSequence 1");
-    static_assert(!contains_stack_process_v<TProcess2type>,
-                  "cannot use StackProcess in SwitchProcessSequence, remove from "
-                  "ProcessSequence 2");
-
-    TSelect select_; // this is a reference, if possible
-
-    TProcess1 A_; // this is a reference, if possible
-    TProcess2 B_; // this is a reference, if possible
-
-  public:
-    SwitchProcessSequence(TProcess1 in_A, TProcess2 in_B, TSelect sel)
-        : select_(sel)
-        , A_(in_A)
-        , B_(in_B) {}
-
-    template <typename TParticle, typename TVTNType>
-    EProcessReturn DoBoundaryCrossing(TParticle& particle, TVTNType const& from,
-                                      TVTNType const& to) {
-      switch (select_(particle)) {
-        case SwitchResult::First: {
-          if constexpr (std::is_base_of_v<BoundaryCrossingProcess<TProcess1type>,
-                                          TProcess1type> ||
-                        t1ProcSeq) {
-            return A_.DoBoundaryCrossing(particle, from, to);
-          }
-          break;
-        }
-        case SwitchResult::Second: {
-          if constexpr (std::is_base_of_v<BoundaryCrossingProcess<TProcess2type>,
-                                          TProcess2type> ||
-                        t2ProcSeq) {
-            return B_.DoBoundaryCrossing(particle, from, to);
-          }
-          break;
-        }
-      }
-      return EProcessReturn::eOk;
-    }
-
-    template <typename TParticle, typename TTrack>
-    inline EProcessReturn DoContinuous(TParticle& particle, TTrack& vT) {
-      switch (select_(particle)) {
-        case SwitchResult::First: {
-          if constexpr (std::is_base_of_v<ContinuousProcess<TProcess1type>,
-                                          TProcess1type> ||
-                        t1ProcSeq) {
-            return A_.DoContinuous(particle, vT);
-          }
-          break;
-        }
-        case SwitchResult::Second: {
-          if constexpr (std::is_base_of_v<ContinuousProcess<TProcess2type>,
-                                          TProcess2type> ||
-                        t2ProcSeq) {
-            return B_.DoContinuous(particle, vT);
-          }
-          break;
-        }
-      }
-      return EProcessReturn::eOk;
-    }
-
-    template <typename TSecondaries>
-    inline void DoSecondaries(TSecondaries& vS) {
-      const auto& particle = vS.parent();
-      switch (select_(particle)) {
-        case SwitchResult::First: {
-          if constexpr (std::is_base_of_v<SecondariesProcess<TProcess1type>,
-                                          TProcess1type> ||
-                        t1ProcSeq) {
-            A_.DoSecondaries(vS);
-          }
-          break;
-        }
-        case SwitchResult::Second: {
-          if constexpr (std::is_base_of_v<SecondariesProcess<TProcess2type>,
-                                          TProcess2type> ||
-                        t2ProcSeq) {
-            B_.DoSecondaries(vS);
-          }
-          break;
-        }
-      }
-    }
-
-    template <typename TParticle, typename TTrack>
-    inline corsika::units::si::LengthType MaxStepLength(TParticle& particle,
-                                                        TTrack& vTrack) {
-      switch (select_(particle)) {
-        case SwitchResult::First: {
-          if constexpr (std::is_base_of_v<ContinuousProcess<TProcess1type>,
-                                          TProcess1type> ||
-                        t1ProcSeq) {
-            return A_.MaxStepLength(particle, vTrack);
-          }
-          break;
-        }
-        case SwitchResult::Second: {
-          if constexpr (std::is_base_of_v<ContinuousProcess<TProcess2type>,
-                                          TProcess2type> ||
-                        t2ProcSeq) {
-            return B_.MaxStepLength(particle, vTrack);
-          }
-          break;
-        }
-      }
-
-      // if no other process in the sequence implements it
-      return std::numeric_limits<double>::infinity() * corsika::units::si::meter;
-    }
-
-    template <typename TParticle>
-    inline corsika::units::si::GrammageType GetInteractionLength(TParticle&& particle) {
-      return 1. / GetInverseInteractionLength(particle);
-    }
-
-    template <typename TParticle>
-    inline corsika::units::si::InverseGrammageType GetInverseInteractionLength(
-        TParticle&& particle) {
-      using namespace corsika::units::si;
-
-      switch (select_(particle)) {
-        case SwitchResult::First: {
-          if constexpr (std::is_base_of_v<InteractionProcess<TProcess1type>,
-                                          TProcess1type> ||
-                        t1ProcSeq) {
-            return A_.GetInverseInteractionLength(particle);
-          }
-          break;
-        }
-        case SwitchResult::Second: {
-          if constexpr (std::is_base_of_v<InteractionProcess<TProcess2type>,
-                                          TProcess2type> ||
-                        t2ProcSeq) {
-            return B_.GetInverseInteractionLength(particle);
-          }
-          break;
-        }
-      }
-      return 0 * meter * meter / gram; // default value
-    }
-
-    template <typename TSecondaryView>
-    inline EProcessReturn SelectInteraction(
-        TSecondaryView& view,
-        [[maybe_unused]] corsika::units::si::InverseGrammageType lambda_inv_select,
-        [[maybe_unused]] corsika::units::si::InverseGrammageType lambda_inv_sum =
-            corsika::units::si::InverseGrammageType::zero()) {
-      switch (select_(view.parent())) {
-        case SwitchResult::First: {
-          if constexpr (t1ProcSeq) {
-            // if A_ is a process sequence --> check inside
-            const EProcessReturn ret =
-                A_.SelectInteraction(view, lambda_inv_select, lambda_inv_sum);
-            // if A_ did succeed, stop routine. Not checking other static branch B_.
-            if (ret != EProcessReturn::eOk) { return ret; }
-          } else if constexpr (std::is_base_of_v<InteractionProcess<TProcess1type>,
-                                                 TProcess1type>) {
-            // if this is not a ContinuousProcess --> evaluate probability
-            lambda_inv_sum += A_.GetInverseInteractionLength(view.parent());
-            // check if we should execute THIS process and then EXIT
-            if (lambda_inv_select < lambda_inv_sum) {
-              A_.DoInteraction(view);
-              return EProcessReturn::eInteracted;
-            }
-          } // end branch A_
-          break;
-        }
-
-        case SwitchResult::Second: {
-
-          if constexpr (t2ProcSeq) {
-            // if B_ is a process sequence --> check inside
-            return B_.SelectInteraction(view, lambda_inv_select, lambda_inv_sum);
-          } else if constexpr (std::is_base_of_v<InteractionProcess<TProcess2type>,
-                                                 TProcess2type>) {
-            // if this is not a ContinuousProcess --> evaluate probability
-            lambda_inv_sum += B_.GetInverseInteractionLength(view.parent());
-            // check if we should execute THIS process and then EXIT
-            if (lambda_inv_select < lambda_inv_sum) {
-              B_.DoInteraction(view);
-              return EProcessReturn::eInteracted;
-            }
-          } // end branch B_
-          break;
-        }
-      }
-      return EProcessReturn::eOk;
-    }
-
-    template <typename TParticle>
-    inline corsika::units::si::TimeType GetLifetime(TParticle&& particle) {
-      return 1. / GetInverseLifetime(particle);
-    }
-
-    template <typename TParticle>
-    inline corsika::units::si::InverseTimeType GetInverseLifetime(TParticle&& particle) {
-      using namespace corsika::units::si;
-
-      switch (select_(particle)) {
-        case SwitchResult::First: {
-          if constexpr (std::is_base_of_v<DecayProcess<TProcess1type>, TProcess1type> ||
-                        t1ProcSeq) {
-            return A_.GetInverseLifetime(particle);
-          }
-          break;
-        }
-
-        case SwitchResult::Second: {
-          if constexpr (std::is_base_of_v<DecayProcess<TProcess2type>, TProcess2type> ||
-                        t2ProcSeq) {
-            return B_.GetInverseLifetime(particle);
-          }
-          break;
-        }
-      }
-      return 0 / second; // default value
-    }
-
-    // select decay process
-    template <typename TSecondaryView>
-    inline EProcessReturn SelectDecay(
-        TSecondaryView& view,
-        [[maybe_unused]] corsika::units::si::InverseTimeType decay_inv_select,
-        [[maybe_unused]] corsika::units::si::InverseTimeType decay_inv_sum =
-            corsika::units::si::InverseTimeType::zero()) {
-      switch (select_(view.parent())) {
-        case SwitchResult::First: {
-          if constexpr (t1ProcSeq) {
-            // if A_ is a process sequence --> check inside
-            const EProcessReturn ret =
-                A_.SelectDecay(view, decay_inv_select, decay_inv_sum);
-            // if A_ did succeed, stop routine here (not checking other static branch B_)
-            if (ret != EProcessReturn::eOk) { return ret; }
-          } else if constexpr (std::is_base_of_v<DecayProcess<TProcess1type>,
-                                                 TProcess1type>) {
-            // if this is not a ContinuousProcess --> evaluate probability
-            decay_inv_sum += A_.GetInverseLifetime(view.parent());
-            // check if we should execute THIS process and then EXIT
-            if (decay_inv_select < decay_inv_sum) {
-              // more pedagogical: rndm_select < decay_inv_sum / decay_inv_tot
-              A_.DoDecay(view);
-              return EProcessReturn::eDecayed;
-            }
-          } // end branch A_
-          break;
-        }
-
-        case SwitchResult::Second: {
-
-          if constexpr (t2ProcSeq) {
-            // if B_ is a process sequence --> check inside
-            return B_.SelectDecay(view, decay_inv_select, decay_inv_sum);
-          } else if constexpr (std::is_base_of_v<DecayProcess<TProcess2type>,
-                                                 TProcess2type>) {
-            // if this is not a ContinuousProcess --> evaluate probability
-            decay_inv_sum += B_.GetInverseLifetime(view.parent());
-            // check if we should execute THIS process and then EXIT
-            if (decay_inv_select < decay_inv_sum) {
-              B_.DoDecay(view);
-              return EProcessReturn::eDecayed;
-            }
-          } // end branch B_
-          break;
-        }
-      }
-      return EProcessReturn::eOk;
-    }
-  };
-
-  // the method `select(proc1,proc1,selector)` assembles many
-  // BaseProcesses, and ProcessSequences into a SwitchProcessSequence,
-  // all combinatorics must be allowed, this is why we define a macro
-  // to define all combinations here:
-
-  // Both, Processes1 and Processes2, must derive from BaseProcesses
-
-  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>>
-  select(TProcess1&& vA, TProcess2&& vB, TSelect selector) {
-    return SwitchProcessSequence<TProcess1, TProcess2, TSelect>(vA, vB, selector);
-  }
-
-  /// traits marker to identify objectas ProcessSequence
-  template <typename TProcess1, typename TProcess2, typename TSelect>
-  struct is_process_sequence<
-      corsika::process::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<
-      corsika::process::SwitchProcessSequence<TProcess1, TProcess2, TSelect>>
-      : std::true_type {};
-
-} // namespace corsika::process
diff --git a/Framework/ProcessSequence/testSwitchProcessSequence.cc b/Framework/ProcessSequence/testSwitchProcessSequence.cc
deleted file mode 100644
index a778f2efb5dd587b3fc2341dbb7cb5e205c671ab..0000000000000000000000000000000000000000
--- a/Framework/ProcessSequence/testSwitchProcessSequence.cc
+++ /dev/null
@@ -1,247 +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/process/switch_process/SwitchProcess.h>
-#include <corsika/stack/SecondaryView.h>
-#include <corsika/stack/Stack.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <catch2/catch.hpp>
-
-#include <algorithm>
-#include <random>
-
-using namespace corsika;
-using namespace corsika::process;
-using namespace corsika::units::si;
-
-class TestStackData {
-
-public:
-  // these functions are needed for the Stack interface
-  void Clear() { fData.clear(); }
-  unsigned int GetSize() const { return fData.size(); }
-  unsigned int GetCapacity() const { return fData.size(); }
-  void Copy(int i1, int i2) { fData[i2] = fData[i1]; }
-  void Swap(int i1, int i2) { std::swap(fData[i1], fData[i2]); }
-
-  // custom data access function
-  void SetData(unsigned int i, HEPEnergyType v) { fData[i] = v; }
-  HEPEnergyType GetData(const unsigned int i) const { return fData[i]; }
-
-  // these functions are also needed by the Stack interface
-  void IncrementSize() { fData.resize(fData.size() + 1); }
-  void DecrementSize() {
-    if (fData.size() > 0) { fData.pop_back(); }
-  }
-
-  // custom private data section
-private:
-  std::vector<HEPEnergyType> fData;
-};
-
-/**
- * From static_cast of a StackIterator over entries in the
- * TestStackData class you get and object of type
- * TestParticleInterface defined here
- *
- * It provides Get/Set methods to read and write data to the "Data"
- * storage of TestStackData obtained via
- * "StackIteratorInterface::GetStackData()", given the index of the
- * iterator "StackIteratorInterface::GetIndex()"
- *
- */
-template <typename StackIteratorInterface>
-class TestParticleInterface
-    : public corsika::stack::ParticleBase<StackIteratorInterface> {
-
-public:
-  using corsika::stack::ParticleBase<StackIteratorInterface>::GetStackData;
-  using corsika::stack::ParticleBase<StackIteratorInterface>::GetIndex;
-
-  /*
-     The SetParticleData methods are called for creating new entries
-     on the stack. You can specifiy various parametric versions to
-     perform this task:
-  */
-
-  // default version for particle-creation from input data
-  void SetParticleData(const std::tuple<HEPEnergyType> v) { SetEnergy(std::get<0>(v)); }
-  void SetParticleData(TestParticleInterface<StackIteratorInterface>& /*parent*/,
-                       std::tuple<HEPEnergyType> v) {
-    SetEnergy(std::get<0>(v));
-  }
-
-  // here are the fundamental methods for access to TestStackData data
-  void SetEnergy(HEPEnergyType v) { GetStackData().SetData(GetIndex(), v); }
-  HEPEnergyType GetEnergy() const { return GetStackData().GetData(GetIndex()); }
-};
-
-using SimpleStack = corsika::stack::Stack<TestStackData, TestParticleInterface>;
-
-// see issue 161
-#if defined(__clang__)
-using StackTestView = corsika::stack::SecondaryView<TestStackData, TestParticleInterface>;
-#elif defined(__GNUC__) || defined(__GNUG__)
-using StackTestView = corsika::stack::MakeView<SimpleStack>::type;
-#endif
-
-auto constexpr kgMSq = 1_kg / (1_m * 1_m);
-
-template <int N>
-struct DummyProcess : InteractionProcess<DummyProcess<N>> {
-
-  template <typename TParticle>
-  corsika::units::si::GrammageType GetInteractionLength(TParticle const&) const {
-    return N * kgMSq;
-  }
-
-  template <typename TSecondaries>
-  corsika::process::EProcessReturn DoInteraction(TSecondaries& vSec) {
-    // to figure out which process was selected in the end, we produce N
-    // secondaries for DummyProcess<N>
-
-    for (int i = 0; i < N; ++i) {
-      vSec.AddSecondary(std::tuple<HEPEnergyType>{vSec.GetEnergy() / N});
-    }
-
-    return EProcessReturn::eOk;
-  }
-};
-
-using DummyLowEnergyProcess = DummyProcess<1>;
-using DummyHighEnergyProcess = DummyProcess<2>;
-using DummyAdditionalProcess = DummyProcess<3>;
-
-TEST_CASE("SwitchProcess from InteractionProcess") {
-  DummyLowEnergyProcess low;
-  DummyHighEnergyProcess high;
-  DummyAdditionalProcess proc;
-
-  switch_process::SwitchProcess switchProcess(low, high, 1_TeV);
-  auto seq = switchProcess << proc;
-
-  SimpleStack stack;
-
-  SECTION("low energy") {
-    stack.AddParticle(std::tuple<HEPEnergyType>{0.5_TeV});
-    auto p = stack.GetNextParticle();
-
-    // low energy process returns 1 kg/m²
-    SECTION("interaction length") {
-      CHECK(switchProcess.GetInteractionLength(p) / kgMSq == Approx(1));
-      CHECK(seq.GetInteractionLength(p) / kgMSq == Approx(3. / 4));
-    }
-  }
-
-  SECTION("high energy") {
-    stack.AddParticle(std::tuple<HEPEnergyType>{4_TeV});
-    auto p = stack.GetNextParticle();
-
-    // high energy process returns 2 kg/m²
-    SECTION("interaction length") {
-      CHECK(switchProcess.GetInteractionLength(p) / kgMSq == Approx(2));
-      CHECK(seq.GetInteractionLength(p) / kgMSq == Approx(6. / 5));
-    }
-
-    // high energy process creates 2 secondaries
-    SECTION("SelectInteraction") {
-      typename SimpleStack::ParticleType theParticle =
-          stack.GetNextParticle(); // as in corsika::Cascade
-      StackTestView view(theParticle);
-      auto projectile = view.GetProjectile();
-
-      InverseGrammageType invLambda = 0 / kgMSq;
-      switchProcess.SelectInteraction(p, projectile, 0.01 / kgMSq, invLambda);
-
-      CHECK(view.getSize() == 2);
-    }
-  }
-}
-
-TEST_CASE("SwitchProcess from ProcessSequence") {
-  DummyProcess<1> innerA;
-  DummyProcess<2> innerB;
-  DummyProcess<3> outer;
-  DummyProcess<4> additional;
-
-  auto seq = innerA << innerB;
-
-  switch_process::SwitchProcess switchProcess(seq, outer, 1_TeV);
-  auto completeSeq = switchProcess << additional;
-
-  SimpleStack stack;
-
-  SECTION("low energy") {
-    stack.AddParticle(std::tuple<HEPEnergyType>{0.5_TeV});
-    auto p = stack.GetNextParticle();
-
-    SECTION("interaction length") {
-      CHECK(switchProcess.GetInteractionLength(p) / kgMSq == Approx(2. / 3));
-      CHECK(completeSeq.GetInteractionLength(p) / kgMSq == Approx(4. / 7));
-    }
-
-    SECTION("SelectInteraction") {
-      std::vector<int> numberOfSecondaries;
-
-      for (int i = 0; i < 1000; ++i) {
-        typename SimpleStack::ParticleType theParticle =
-            stack.GetNextParticle(); // as in corsika::Cascade
-        StackTestView view(theParticle);
-        auto projectile = view.GetProjectile();
-
-        double r = i / 1000.;
-        InverseGrammageType invLambda = r * 7. / 4 / kgMSq;
-
-        InverseGrammageType accumulator = 0 / kgMSq;
-        completeSeq.SelectInteraction(p, projectile, invLambda, accumulator);
-
-        numberOfSecondaries.push_back(view.getSize());
-      }
-
-      auto const mean =
-          std::accumulate(numberOfSecondaries.cbegin(), numberOfSecondaries.cend(), 0.) /
-          numberOfSecondaries.size();
-      CHECK(mean == Approx(12. / 7.).margin(0.01));
-    }
-  }
-
-  SECTION("high energy") {
-    stack.AddParticle(std::tuple<HEPEnergyType>{3.0_TeV});
-    auto p = stack.GetNextParticle();
-
-    SECTION("interaction length") {
-      CHECK(switchProcess.GetInteractionLength(p) / kgMSq == Approx(3));
-      CHECK(completeSeq.GetInteractionLength(p) / kgMSq == Approx(12. / 7.));
-    }
-
-    SECTION("SelectInteraction") {
-      std::vector<int> numberOfSecondaries;
-
-      for (int i = 0; i < 1000; ++i) {
-        typename SimpleStack::ParticleType theParticle =
-            stack.GetNextParticle(); // as in corsika::Cascade
-        StackTestView view(theParticle);
-        auto projectile = view.GetProjectile();
-
-        double r = i / 1000.;
-        InverseGrammageType invLambda = r * 7. / 12. / kgMSq;
-
-        InverseGrammageType accumulator = 0 / kgMSq;
-        completeSeq.SelectInteraction(p, projectile, invLambda, accumulator);
-
-        numberOfSecondaries.push_back(view.getSize());
-      }
-
-      auto const mean =
-          std::accumulate(numberOfSecondaries.cbegin(), numberOfSecondaries.cend(), 0.) /
-          numberOfSecondaries.size();
-      CHECK(mean == Approx(24. / 7.).margin(0.01));
-    }
-  }
-}
diff --git a/Framework/ProcessSequence/testSwitchProcessSequence.h b/Framework/ProcessSequence/testSwitchProcessSequence.h
deleted file mode 100644
index 709dc8510684c9e7c4dc8ebbb91fa20611ac17ad..0000000000000000000000000000000000000000
--- a/Framework/ProcessSequence/testSwitchProcessSequence.h
+++ /dev/null
@@ -1,247 +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/process/switch_process/SwitchProcess.h>
-#include <corsika/stack/SecondaryView.h>
-#include <corsika/stack/Stack.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <catch2/catch.hpp>
-
-#include <algorithm>
-#include <random>
-
-using namespace corsika;
-using namespace corsika::process;
-using namespace corsika::units::si;
-
-class TestStackData {
-
-public:
-  // these functions are needed for the Stack interface
-  void Clear() { fData.clear(); }
-  unsigned int GetSize() const { return fData.size(); }
-  unsigned int GetCapacity() const { return fData.size(); }
-  void Copy(int i1, int i2) { fData[i2] = fData[i1]; }
-  void Swap(int i1, int i2) { std::swap(fData[i1], fData[i2]); }
-
-  // custom data access function
-  void SetData(unsigned int i, HEPEnergyType v) { fData[i] = v; }
-  HEPEnergyType GetData(const unsigned int i) const { return fData[i]; }
-
-  // these functions are also needed by the Stack interface
-  void IncrementSize() { fData.resize(fData.size() + 1); }
-  void DecrementSize() {
-    if (fData.size() > 0) { fData.pop_back(); }
-  }
-
-  // custom private data section
-private:
-  std::vector<HEPEnergyType> fData;
-};
-
-/**
- * From static_cast of a StackIterator over entries in the
- * TestStackData class you get and object of type
- * TestParticleInterface defined here
- *
- * It provides Get/Set methods to read and write data to the "Data"
- * storage of TestStackData obtained via
- * "StackIteratorInterface::GetStackData()", given the index of the
- * iterator "StackIteratorInterface::GetIndex()"
- *
- */
-template <typename StackIteratorInterface>
-class TestParticleInterface
-    : public corsika::stack::ParticleBase<StackIteratorInterface> {
-
-public:
-  using corsika::stack::ParticleBase<StackIteratorInterface>::GetStackData;
-  using corsika::stack::ParticleBase<StackIteratorInterface>::GetIndex;
-
-  /*
-     The SetParticleData methods are called for creating new entries
-     on the stack. You can specifiy various parametric versions to
-     perform this task:
-  */
-
-  // default version for particle-creation from input data
-  void SetParticleData(const std::tuple<HEPEnergyType> v) { SetEnergy(std::get<0>(v)); }
-  void SetParticleData(TestParticleInterface<StackIteratorInterface>& /*parent*/,
-                       std::tuple<HEPEnergyType> v) {
-    SetEnergy(std::get<0>(v));
-  }
-
-  // here are the fundamental methods for access to TestStackData data
-  void SetEnergy(HEPEnergyType v) { GetStackData().SetData(GetIndex(), v); }
-  HEPEnergyType GetEnergy() const { return GetStackData().GetData(GetIndex()); }
-};
-
-using SimpleStack = corsika::stack::Stack<TestStackData, TestParticleInterface>;
-
-// see issue 161
-#if defined(__clang__)
-using StackTestView = corsika::stack::SecondaryView<TestStackData, TestParticleInterface>;
-#elif defined(__GNUC__) || defined(__GNUG__)
-using StackTestView = corsika::stack::MakeView<SimpleStack>::type;
-#endif
-
-auto constexpr kgMSq = 1_kg / (1_m * 1_m);
-
-template <int N>
-struct DummyProcess : InteractionProcess<DummyProcess<N>> {
-
-  template <typename TParticle>
-  corsika::units::si::GrammageType GetInteractionLength(TParticle const&) const {
-    return N * kgMSq;
-  }
-
-  template <typename TSecondaries>
-  corsika::process::EProcessReturn DoInteraction(TSecondaries& vSec) {
-    // to figure out which process was selected in the end, we produce N
-    // secondaries for DummyProcess<N>
-
-    for (int i = 0; i < N; ++i) {
-      vSec.AddSecondary(std::tuple<HEPEnergyType>{vSec.GetEnergy() / N});
-    }
-
-    return EProcessReturn::eOk;
-  }
-};
-
-using DummyLowEnergyProcess = DummyProcess<1>;
-using DummyHighEnergyProcess = DummyProcess<2>;
-using DummyAdditionalProcess = DummyProcess<3>;
-
-TEST_CASE("SwitchProcess from InteractionProcess") {
-  DummyLowEnergyProcess low;
-  DummyHighEnergyProcess high;
-  DummyAdditionalProcess proc;
-
-  switch_process::SwitchProcess switchProcess(low, high, 1_TeV);
-  auto seq = switchProcess << proc;
-
-  SimpleStack stack;
-
-  SECTION("low energy") {
-    stack.AddParticle(std::tuple<HEPEnergyType>{0.5_TeV});
-    auto p = stack.GetNextParticle();
-
-    // low energy process returns 1 kg/m²
-    SECTION("interaction length") {
-      REQUIRE(switchProcess.GetInteractionLength(p) / kgMSq == Approx(1));
-      REQUIRE(seq.GetTotalInteractionLength(p) / kgMSq == Approx(3. / 4));
-    }
-  }
-
-  SECTION("high energy") {
-    stack.AddParticle(std::tuple<HEPEnergyType>{4_TeV});
-    auto p = stack.GetNextParticle();
-
-    // high energy process returns 2 kg/m²
-    SECTION("interaction length") {
-      REQUIRE(switchProcess.GetInteractionLength(p) / kgMSq == Approx(2));
-      REQUIRE(seq.GetTotalInteractionLength(p) / kgMSq == Approx(6. / 5));
-    }
-
-    // high energy process creates 2 secondaries
-    SECTION("SelectInteraction") {
-      typename SimpleStack::ParticleType theParticle =
-          stack.GetNextParticle(); // as in corsika::Cascade
-      StackTestView view(theParticle);
-      auto projectile = view.GetProjectile();
-
-      InverseGrammageType invLambda = 0 / kgMSq;
-      switchProcess.SelectInteraction(p, projectile, 0.01 / kgMSq, invLambda);
-
-      REQUIRE(view.getSize() == 2);
-    }
-  }
-}
-
-TEST_CASE("SwitchProcess from ProcessSequence") {
-  DummyProcess<1> innerA;
-  DummyProcess<2> innerB;
-  DummyProcess<3> outer;
-  DummyProcess<4> additional;
-
-  auto seq = innerA << innerB;
-
-  switch_process::SwitchProcess switchProcess(seq, outer, 1_TeV);
-  auto completeSeq = switchProcess << additional;
-
-  SimpleStack stack;
-
-  SECTION("low energy") {
-    stack.AddParticle(std::tuple<HEPEnergyType>{0.5_TeV});
-    auto p = stack.GetNextParticle();
-
-    SECTION("interaction length") {
-      REQUIRE(switchProcess.GetInteractionLength(p) / kgMSq == Approx(2. / 3));
-      REQUIRE(completeSeq.GetTotalInteractionLength(p) / kgMSq == Approx(4. / 7));
-    }
-
-    SECTION("SelectInteraction") {
-      std::vector<int> numberOfSecondaries;
-
-      for (int i = 0; i < 1000; ++i) {
-        typename SimpleStack::ParticleType theParticle =
-            stack.GetNextParticle(); // as in corsika::Cascade
-        StackTestView view(theParticle);
-        auto projectile = view.GetProjectile();
-
-        double r = i / 1000.;
-        InverseGrammageType invLambda = r * 7. / 4 / kgMSq;
-
-        InverseGrammageType accumulator = 0 / kgMSq;
-        completeSeq.SelectInteraction(p, projectile, invLambda, accumulator);
-
-        numberOfSecondaries.push_back(view.getSize());
-      }
-
-      auto const mean =
-          std::accumulate(numberOfSecondaries.cbegin(), numberOfSecondaries.cend(), 0.) /
-          numberOfSecondaries.size();
-      REQUIRE(mean == Approx(12. / 7.).margin(0.01));
-    }
-  }
-
-  SECTION("high energy") {
-    stack.AddParticle(std::tuple<HEPEnergyType>{3.0_TeV});
-    auto p = stack.GetNextParticle();
-
-    SECTION("interaction length") {
-      REQUIRE(switchProcess.GetInteractionLength(p) / kgMSq == Approx(3));
-      REQUIRE(completeSeq.GetTotalInteractionLength(p) / kgMSq == Approx(12. / 7.));
-    }
-
-    SECTION("SelectInteraction") {
-      std::vector<int> numberOfSecondaries;
-
-      for (int i = 0; i < 1000; ++i) {
-        typename SimpleStack::ParticleType theParticle =
-            stack.GetNextParticle(); // as in corsika::Cascade
-        StackTestView view(theParticle);
-        auto projectile = view.GetProjectile();
-
-        double r = i / 1000.;
-        InverseGrammageType invLambda = r * 7. / 12. / kgMSq;
-
-        InverseGrammageType accumulator = 0 / kgMSq;
-        completeSeq.SelectInteraction(p, projectile, invLambda, accumulator);
-
-        numberOfSecondaries.push_back(view.getSize());
-      }
-
-      auto const mean =
-          std::accumulate(numberOfSecondaries.cbegin(), numberOfSecondaries.cend(), 0.) /
-          numberOfSecondaries.size();
-      REQUIRE(mean == Approx(24. / 7.).margin(0.01));
-    }
-  }
-}
diff --git a/Framework/Random/CMakeLists.txt b/Framework/Random/CMakeLists.txt
deleted file mode 100644
index f909f77f1f32c88f77a52482839af3cf43e2949a..0000000000000000000000000000000000000000
--- a/Framework/Random/CMakeLists.txt
+++ /dev/null
@@ -1,53 +0,0 @@
-set (
-  CORSIKArandom_SOURCES
-  RNGManager.cc
-  )
-
-set (
-  CORSIKArandom_HEADERS
-  RNGManager.h
-  UniformRealDistribution.h
-  ExponentialDistribution.h
-  )
-
-set (
-  CORSIKArandom_NAMESPACE
-  corsika/random
-  )
-
-add_library (CORSIKArandom STATIC ${CORSIKArandom_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKArandom ${CORSIKArandom_NAMESPACE} ${CORSIKArandom_HEADERS})
-
-target_link_libraries (
-  CORSIKArandom
-  CORSIKAutilities
-  CORSIKAlogging
-  )
-
-target_include_directories (
-  CORSIKArandom
-  PUBLIC
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/>
-  )
-
-# target dependencies on other libraries (also the header onlys)
-# none
-
-install (
-  TARGETS CORSIKArandom
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-  PUBLIC_HEADER DESTINATION include/${CORSIKArandom_NAMESPACE}
-  )
-
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST(testRandom)
-target_link_libraries (
-  testRandom
-  CORSIKArandom
-  CORSIKAtesting
-  CORSIKAunits
-  )
diff --git a/Framework/Random/ExponentialDistribution.h b/Framework/Random/ExponentialDistribution.h
deleted file mode 100644
index 2431e316e1465ba669610993b127b4aeef735c9e..0000000000000000000000000000000000000000
--- a/Framework/Random/ExponentialDistribution.h
+++ /dev/null
@@ -1,33 +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/units/PhysicalUnits.h>
-#include <random>
-
-namespace corsika::random {
-
-  template <class TQuantity>
-  class ExponentialDistribution {
-    using RealType = typename TQuantity::value_type;
-    std::exponential_distribution<RealType> dist{1.};
-
-    TQuantity const fBeta;
-
-  public:
-    ExponentialDistribution(TQuantity beta)
-        : fBeta(beta) {}
-
-    template <class Generator>
-    TQuantity operator()(Generator& g) {
-      return fBeta * dist(g);
-    }
-  };
-
-} // namespace corsika::random
diff --git a/Framework/Random/RNGManager.cc b/Framework/Random/RNGManager.cc
deleted file mode 100644
index 8bbcf15dd391a744d69e017f7570e5163a0308a2..0000000000000000000000000000000000000000
--- a/Framework/Random/RNGManager.cc
+++ /dev/null
@@ -1,64 +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/random/RNGManager.h>
-#include <corsika/logging/Logging.h>
-
-#include <sstream>
-
-void corsika::random::RNGManager::RegisterRandomStream(std::string const& pStreamName) {
-  corsika::random::RNG rng;
-
-  if (auto const& it = seeds.find(pStreamName); it != seeds.end()) {
-    rng.seed(it->second);
-  }
-
-  rngs[pStreamName] = std::move(rng);
-}
-
-corsika::random::RNG& corsika::random::RNGManager::GetRandomStream(
-    std::string const& pStreamName) {
-  if (IsRegistered(pStreamName)) {
-    return rngs.at(pStreamName);
-  } else { // this stream name is not in the map
-    throw std::runtime_error("'" + pStreamName + "' is not a registered stream.");
-  }
-}
-
-bool corsika::random::RNGManager::IsRegistered(std::string const& pStreamName) const {
-  return rngs.count(pStreamName) > 0;
-}
-
-std::stringstream corsika::random::RNGManager::dumpState() const {
-  std::stringstream buffer;
-  for (auto const& [streamName, rng] : rngs) {
-    buffer << '"' << streamName << "\" = \"" << rng << '"' << std::endl;
-  }
-
-  return buffer;
-}
-
-void corsika::random::RNGManager::SeedAll(uint64_t vSeed) {
-  for (auto& entry : rngs) {
-    auto seed = vSeed++;
-    C8LOG_TRACE("Random seed stream {} seed {}", entry.first, seed);
-    entry.second.seed(seed);
-  }
-}
-
-void corsika::random::RNGManager::SeedAll() {
-  std::random_device rd;
-  std::seed_seq sseq{rd(), rd(), rd(), rd(), rd(), rd()};
-  for (auto& entry : rngs) {
-    std::vector<std::uint32_t> seeds(1);
-    sseq.generate(seeds.begin(), seeds.end());
-    std::uint32_t seed = seeds[0];
-    C8LOG_TRACE("Random seed stream {} seed {}", entry.first, seed);
-    entry.second.seed(seed);
-  }
-}
diff --git a/Framework/Random/RNGManager.h b/Framework/Random/RNGManager.h
deleted file mode 100644
index 0826642a86e57df9f25ab0c297e5ce080839565b..0000000000000000000000000000000000000000
--- a/Framework/Random/RNGManager.h
+++ /dev/null
@@ -1,73 +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/utl/Singleton.h>
-
-#include <map>
-#include <random>
-#include <string>
-
-/*!
- * With this class modules can register streams of random numbers.
- */
-
-namespace corsika::random {
-
-  using RNG = std::mt19937; //!< the actual RNG type that will be used
-
-  /*!
-   * Manage random number generators.
-   */
-  class RNGManager final : public corsika::utl::Singleton<RNGManager> {
-
-    friend class corsika::utl::Singleton<RNGManager>;
-
-    std::map<std::string, RNG> rngs;
-    std::map<std::string, std::seed_seq> seeds;
-
-  protected:
-    RNGManager() {}
-
-  public:
-    /*!
-     * This function is to be called by a module requiring a random-number
-     * stream during its initialization.
-     *
-     * \throws sth. when stream \a pModuleName is already registered
-     */
-    void RegisterRandomStream(std::string const& pStreamName);
-
-    /*!
-     * returns the pre-stored stream of given name \a pStreamName if
-     * available
-     */
-    RNG& GetRandomStream(std::string const& pStreamName);
-
-    /*!
-     * Check whether a stream has been registered.
-     */
-    bool IsRegistered(std::string const& pStreamName) const;
-
-    /*!
-     * dumps the names and states of all registered random-number streams
-     * into a std::stringstream.
-     */
-    std::stringstream dumpState() const;
-
-    /**
-     * Set explicit seeds for all currently registered streams. The actual seed values
-     * are incremented from \a vSeed.
-     */
-    void SeedAll(uint64_t vSeed);
-
-    void SeedAll(); //!< seed all currently registered streams with "real" randomness
-  };
-
-} // namespace corsika::random
diff --git a/Framework/Random/UniformRealDistribution.h b/Framework/Random/UniformRealDistribution.h
deleted file mode 100644
index 0c63e85428a1e2556a81995b386ba46493546866..0000000000000000000000000000000000000000
--- a/Framework/Random/UniformRealDistribution.h
+++ /dev/null
@@ -1,36 +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 <random>
-
-namespace corsika::random {
-
-  template <class TQuantity>
-  class UniformRealDistribution {
-    using RealType = typename TQuantity::value_type;
-    std::uniform_real_distribution<RealType> dist{RealType(0.), RealType(1.)};
-
-    TQuantity const a, b;
-
-  public:
-    UniformRealDistribution(TQuantity b)
-        : a{TQuantity(phys::units::detail::magnitude_tag, 0)}
-        , b(b) {}
-    UniformRealDistribution(TQuantity a, TQuantity b)
-        : a(a)
-        , b(b) {}
-
-    template <class Generator>
-    TQuantity operator()(Generator& g) {
-      return a + dist(g) * (b - a);
-    }
-  };
-
-} // namespace corsika::random
diff --git a/Framework/StackInterface/CMakeLists.txt b/Framework/StackInterface/CMakeLists.txt
deleted file mode 100644
index 956f1873b6f8781c426ab4ee52d31fac80c72e6f..0000000000000000000000000000000000000000
--- a/Framework/StackInterface/CMakeLists.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-set (
-  CORSIKAstackinterface_HEADERS
-  ParticleBase.h
-  StackIteratorInterface.h
-  Stack.h
-  SecondaryView.h
-  CombinedStack.h
-  )
-
-set (
-  CORSIKAstackinterface_NAMESPACE
-  corsika/stack
-  )
-
-add_library (
-  CORSIKAstackinterface
-  INTERFACE
-  )
-
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (
-  CORSIKAstackinterface ${CORSIKAstackinterface_NAMESPACE} ${CORSIKAstackinterface_HEADERS}
-  )
-
-target_link_libraries (
-  CORSIKAstackinterface
-  INTERFACE
-  CORSIKAlogging
-  CORSIKAsetup  
-  )
-
-target_include_directories (
-  CORSIKAstackinterface
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include>
-  )
-
-install (
-  FILES ${CORSIKAstackinterface_HEADERS}
-  DESTINATION include/${CORSIKAstackinterface_NAMESPACE}
-  )
-
-#code testing
-CORSIKA_ADD_TEST(testStackInterface)
-target_link_libraries (testStackInterface CORSIKAstackinterface CORSIKAtesting)
-
-CORSIKA_ADD_TEST(testSecondaryView)
-target_link_libraries (testSecondaryView CORSIKAstackinterface CORSIKAtesting)
-
-CORSIKA_ADD_TEST(testCombinedStack)
-target_link_libraries (testCombinedStack CORSIKAstackinterface CORSIKAtesting)
diff --git a/Framework/StackInterface/CombinedStack.h b/Framework/StackInterface/CombinedStack.h
deleted file mode 100644
index d8cecfd57bdf2fe43d549dffd1e4432818a6f961..0000000000000000000000000000000000000000
--- a/Framework/StackInterface/CombinedStack.h
+++ /dev/null
@@ -1,168 +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/logging/Logging.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/stack/Stack.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::stack {
-
-  /**
-   * @class CombinedParticleInterface
-   *
-   * You may combine two StackData object, see class CombinedStackImpl
-   * below, into one Stack, using a combined StackIterator (aka
-   * CombinedParticleInterface) interface class.
-   *
-   * This allows to add specific information to a given Stack, could
-   * be special information on a subset of entries
-   * (e.g. NuclearStackExtension) or also (multi) thinning weights for
-   * all particles.
-   *
-   * Many Stacks can be combined into more complex object.
-   *
-   * The two sub-stacks must both provide their independent
-   * ParticleInterface classes.
-   *
-   */
-  template <template <typename> typename ParticleInterfaceA,
-            template <typename> typename ParticleInterfaceB, typename StackIterator>
-  class CombinedParticleInterface
-      : public ParticleInterfaceB<ParticleInterfaceA<StackIterator>> {
-
-    using PI_C =
-        CombinedParticleInterface<ParticleInterfaceA, ParticleInterfaceB, StackIterator>;
-    using PI_A = ParticleInterfaceA<StackIterator>;
-    using PI_B = ParticleInterfaceB<ParticleInterfaceA<StackIterator>>;
-
-  protected:
-    using PI_B::GetIndex;     // choose B, A would also work
-    using PI_B::GetStackData; // choose B, A would also work
-
-  public:
-    /**
-     * @name wrapper for user functions
-     * @{
-     *
-     * In this set of functions we call the user-provide
-     * ParticleInterface SetParticleData(...) methods, either with
-     * parent particle reference, or w/o.
-     *
-     * There is one implicit assumption here: if only one data tuple
-     * is provided for SetParticleData, the data is passed on to
-     * ParticleInterfaceA and the ParticleInterfaceB is
-     * default-initialized. There are many occasions where this is the
-     * desired behaviour, e.g. for thinning etc.
-     *
-     */
-
-    template <typename... Args1>
-    void SetParticleData(const std::tuple<Args1...> vA) {
-      PI_A::SetParticleData(vA);
-      PI_B::SetParticleData();
-    }
-    template <typename... Args1, typename... Args2>
-    void SetParticleData(const std::tuple<Args1...> vA, const std::tuple<Args2...> vB) {
-      PI_A::SetParticleData(vA);
-      PI_B::SetParticleData(vB);
-    }
-
-    template <typename... Args1>
-    void SetParticleData(PI_C& p, const std::tuple<Args1...> vA) {
-      // static_assert(MT<I>::has_not, "error");
-      PI_A::SetParticleData(static_cast<PI_A&>(p), vA); // original stack
-      PI_B::SetParticleData(static_cast<PI_B&>(p));     // addon stack
-    }
-    template <typename... Args1, typename... Args2>
-    void SetParticleData(PI_C& p, const std::tuple<Args1...> vA,
-                         const std::tuple<Args2...> vB) {
-      PI_A::SetParticleData(static_cast<PI_A&>(p), vA);
-      PI_B::SetParticleData(static_cast<PI_B&>(p), vB);
-    }
-    ///@}
-
-    std::string as_string() const {
-      return fmt::format("[[{}][{}]]", PI_A::as_string(), PI_B::as_string());
-    }
-  };
-
-  /**
-   * @class CombinedStackImpl
-   *
-   * Memory implementation of a combined data stack.
-   *
-   * The two stack data user objects Stack1Impl and Stack2Impl are
-   * merged into one consistent Stack container object providing
-   * access to the combined number of data entries.
-   */
-  template <typename Stack1Impl, typename Stack2Impl>
-  class CombinedStackImpl : public Stack1Impl, public Stack2Impl {
-
-  public:
-    void Clear() {
-      Stack1Impl::Clear();
-      Stack2Impl::Clear();
-    }
-
-    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
-     */
-    void Copy(const unsigned int i1, const unsigned int i2) {
-      if (i1 >= GetSize() || i2 >= GetSize()) {
-        std::ostringstream err;
-        err << "CombinedStack: trying to access data beyond size of stack!";
-        throw std::runtime_error(err.str());
-      }
-      Stack1Impl::Copy(i1, i2);
-      Stack2Impl::Copy(i1, i2);
-    }
-
-    /**
-     *   Function to copy particle at location i2 in stack to i1
-     */
-    void Swap(const unsigned int i1, const unsigned int i2) {
-      if (i1 >= GetSize() || i2 >= GetSize()) {
-        std::ostringstream err;
-        err << "CombinedStack: trying to access data beyond size of stack!";
-        throw std::runtime_error(err.str());
-      }
-      Stack1Impl::Swap(i1, i2);
-      Stack2Impl::Swap(i1, i2);
-    }
-
-    void IncrementSize() {
-      Stack1Impl::IncrementSize();
-      Stack2Impl::IncrementSize();
-    }
-
-    void DecrementSize() {
-      Stack1Impl::DecrementSize();
-      Stack2Impl::DecrementSize();
-    }
-
-  }; // end class CombinedStackImpl
-
-  /**
-   * Helper template alias `CombinedStack` to construct new combined
-   * stack from two stack data objects and a particle readout interface.
-   *
-   * Note that the Stack2Impl provides only /additional/ data to
-   * Stack1Impl. This is important (see above) since tuple data for
-   * initialization are forwarded to Stack1Impl (first).
-   */
-
-  template <typename Stack1Impl, typename Stack2Impl, template <typename> typename _PI>
-  using CombinedStack = Stack<CombinedStackImpl<Stack1Impl, Stack2Impl>, _PI>;
-
-} // namespace corsika::stack
diff --git a/Framework/StackInterface/SecondaryView.h b/Framework/StackInterface/SecondaryView.h
deleted file mode 100644
index 709a8c4feb658659f9369f938490b7101e91b1da..0000000000000000000000000000000000000000
--- a/Framework/StackInterface/SecondaryView.h
+++ /dev/null
@@ -1,497 +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/stack/Stack.h>
-
-#include <corsika/logging/Logging.h>
-
-#include <stdexcept>
-#include <vector>
-
-namespace corsika::stack {
-
-  // forward-decl:
-  template <class T1, template <class> class T2>
-  class DefaultSecondaryProducer;
-
-  /**
-   * @class SecondaryView
-   *
-   * SecondaryView can only be constructed by giving a valid
-   * Projectile particle, following calls to AddSecondary will
-   * populate the original Stack, but will be directly accessible via
-   * the SecondaryView, e.g.
-
-     This allows to write code like
-     \verbatim
-     auto projectileInput = mainStack.GetNextParticle();
-     const unsigned int nMain = mainStack.GetSize();
-     SecondaryView<StackData, ParticleInterface> mainStackView(projectileInput);
-     mainStackView.AddSecondary(...data...);
-     mainStackView.AddSecondary(...data...);
-     mainStackView.AddSecondary(...data...);
-     mainStackView.AddSecondary(...data...);
-     assert(mainStackView.GetSize() == 4);
-     assert(mainStack.GetSize() = nMain+4);
-     \endverbatim
-
-     All operations possible on a Stack object are also possible on a
-     SecondaryView object. This means you can add, delete, copy, swap,
-     iterate, etc.
-
-     *Further information about implementation (for developers):* All
-     data is stored in the original stack privided at construction
-     time. The secondary particle (view) indices are stored in an
-     extra std::vector of SecondaryView class 'indices_' referring to
-     the original stack slot indices. The index of the primary
-     projectle particle is also explicitly stored in
-     'projectile_index_'. StackIterator indices
-     'i = StackIterator::GetIndex()' are referring to those numbers,
-     where 'i==0' refers to the 'projectile_index_', and
-     'StackIterator::GetIndex()>0' to 'indices_[i-1]', see function
-     GetIndexFromIterator.
-   */
-
-  template <typename StackDataType, template <typename> typename ParticleInterface,
-            template <class T1, template <class> class T2> class MSecondaryProducer =
-                DefaultSecondaryProducer>
-
-  class SecondaryView : public Stack<StackDataType&, ParticleInterface>,
-                        public MSecondaryProducer<StackDataType, ParticleInterface> {
-    using ViewType = SecondaryView<StackDataType, ParticleInterface, MSecondaryProducer>;
-
-  private:
-    /**
-     * Helper type for inside this class
-     */
-    using InnerStackTypeRef = Stack<StackDataType&, ParticleInterface>;
-    using InnerStackTypeRef::getDeleted;
-
-    /**
-     * @name We need this "special" types with non-reference StackData for
-     * the constructor of the SecondaryView class
-     * @{
-     */
-    using InnerStackTypeValue = Stack<StackDataType, ParticleInterface>;
-
-  public:
-    using StackIteratorValue =
-        StackIteratorInterface<typename std::remove_reference<StackDataType>::type,
-                               ParticleInterface, InnerStackTypeValue>;
-    using ConstStackIteratorValue =
-        ConstStackIteratorInterface<typename std::remove_reference<StackDataType>::type,
-                                    ParticleInterface, InnerStackTypeValue>;
-    /// @}
-
-    using StackIterator =
-        StackIteratorInterface<typename std::remove_reference<StackDataType>::type,
-                               ParticleInterface, ViewType>;
-    using ConstStackIterator =
-        ConstStackIteratorInterface<typename std::remove_reference<StackDataType>::type,
-                                    ParticleInterface, ViewType>;
-
-    /**
-     * this is the full type of the declared ParticleInterface: typedef typename
-     */
-    using ParticleType = StackIterator;
-    using ParticleInterfaceType = typename StackIterator::ParticleInterfaceType;
-
-    friend class StackIteratorInterface<
-        typename std::remove_reference<StackDataType>::type, ParticleInterface, ViewType>;
-
-    friend class ConstStackIteratorInterface<
-        typename std::remove_reference<StackDataType>::type, ParticleInterface, ViewType>;
-
-    friend class ParticleBase<StackIterator>;
-
-  private:
-    /**
-     * This is not accessible, since we don't want to allow creating a
-     * new stack.
-     */
-    template <typename... Args>
-    SecondaryView(Args... args) = delete;
-    SecondaryView() = delete;
-
-  private:
-    InnerStackTypeValue& inner_stack_;
-    unsigned int projectile_index_;
-    std::vector<unsigned int> indices_;
-
-  public:
-    /**
-       SecondaryView can only be constructed passing it a valid
-       StackIterator to another Stack object (here: lvalue)
-     **/
-    SecondaryView(StackIteratorValue& particle)
-        : Stack<StackDataType&, ParticleInterface>(particle.GetStackData())
-        , MSecondaryProducer<StackDataType, ParticleInterface>{particle}
-        , inner_stack_(particle.GetStack())
-        , projectile_index_(particle.GetIndex()) {
-      C8LOG_TRACE("SecondaryView::SecondaryView(particle&)");
-    }
-    /**
-       SecondaryView can only be constructed passing it a valid
-       StackIterator to another Stack object (here: rvalue)
-     **/
-    SecondaryView(StackIteratorValue&& particle)
-        : Stack<StackDataType&, ParticleInterface>(particle.GetStackData())
-        , MSecondaryProducer<StackDataType, ParticleInterface>{particle}
-        , inner_stack_(particle.GetStack())
-        , projectile_index_(particle.GetIndex()) {
-      C8LOG_TRACE("SecondaryView::SecondaryView(particle&&)");
-    }
-    /**
-     * Also allow to create a new View from a Projectile (StackIterator on View)
-     *
-     * Note, the view generated this way will be equivalent to the orignal view in
-     * terms of reference to the underlying data stack. It is not a "view to a view".
-     */
-    SecondaryView(ViewType& view, StackIterator& projectile)
-        : Stack<StackDataType&, ParticleInterface>{view.GetStackData()}
-        , MSecondaryProducer<StackDataType, ParticleInterface>{StackIteratorValue{
-              view.inner_stack_, view.GetIndexFromIterator(projectile.GetIndex())}}
-        , inner_stack_{view.inner_stack_}
-        , projectile_index_{view.GetIndexFromIterator(projectile.GetIndex())} {
-      C8LOG_TRACE("SecondaryView::SecondaryView(view, projectile)");
-    }
-
-    /**
-     * This returns the projectile/parent in the original Stack, where this
-     * SecondaryView is derived from. This projectile should not be
-     * used to modify the Stack!
-     */
-    StackIteratorValue parent()
-        const { // todo: check if this can't be ConstStackIteratorValue
-      return StackIteratorValue(inner_stack_, projectile_index_);
-    }
-
-    /**
-     * This returns the projectile/parent in the original Stack, where this
-     * SecondaryView is derived from. This projectile should not be
-     * used to modify the Stack!
-     */
-    StackIteratorValue asNewParent() const {
-      return StackIteratorValue(inner_stack_, projectile_index_);
-    }
-
-    /**
-     * This return a projectile of this SecondaryView, which can be
-     * used to modify the SecondaryView
-     */
-    StackIterator GetProjectile() {
-      // NOTE: 0 is special marker here for PROJECTILE, see GetIndexFromIterator
-      return StackIterator(*this, 0);
-    }
-
-  public:
-    /**
-     * Method to add a new secondary particle on this SecondaryView
-     */
-    template <typename... Args>
-    StackIterator AddSecondary(const Args... v) {
-      C8LOG_TRACE("SecondaryView::AddSecondary(Args&&)");
-      StackIterator proj = GetProjectile(); // make this const
-      return AddSecondary(proj, v...);
-    }
-
-  protected:
-    /**
-     * Overwrite of Stack::StackIterator
-     *
-     * increase stack size, create new particle at end of stack,
-     * related to parent particle/projectile
-     *
-     * This should only get internally called from a
-     * StackIterator::AddSecondary via ParticleBase
-     */
-    template <typename... Args>
-    StackIterator AddSecondary(StackIterator& proj, const Args... v) {
-      C8LOG_TRACE("SecondaryView::AddSecondary(StackIterator&, Args&&)");
-      // make space on stack
-      InnerStackTypeRef::GetStackData().IncrementSize();
-      inner_stack_.deleted_.push_back(false);
-      // get current number of secondaries on stack
-      const unsigned int idSec = getSize();
-      // determine index on (inner) stack where new particle will be located
-      const unsigned int index = InnerStackTypeRef::GetStackData().GetSize() - 1;
-      indices_.push_back(index);
-      // NOTE: "+1" is since "0" is special marker here for PROJECTILE, see
-      // GetIndexFromIterator
-      auto sec = StackIterator(*this, idSec + 1, proj, v...);
-      MSecondaryProducer<StackDataType, ParticleInterface>::new_secondary(sec);
-      return sec;
-    }
-
-  public:
-    /**
-     * overwrite Stack::GetSize to return actual number of secondaries
-     */
-    unsigned int getSize() const { return indices_.size(); }
-    unsigned int getEntries() const { return getSize() - getDeleted(); }
-    bool IsEmpty() const { return getEntries() == 0; }
-
-    /**
-     * @name These are functions required by std containers and std loops
-     * The Stack-versions must be overwritten, since here we need the correct
-     * SecondaryView::getSize
-     * @{
-     */
-    // NOTE: the "+1" is since "0" is special marker here for PROJECTILE, see
-    // GetIndexFromIterator
-    StackIterator begin() {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!isDeleted(i)) break;
-      }
-      return StackIterator(*this, i + 1);
-    }
-    auto end() { return StackIterator(*this, getSize() + 1); }
-    auto last() {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!isDeleted(getSize() - 1 - i)) break;
-      }
-      return StackIterator(*this, getSize() - 1 - i + 1);
-    }
-
-    auto begin() const {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!isDeleted(i)) break;
-      }
-      return ConstStackIterator(*this, i + 1);
-    }
-    auto end() const { return ConstStackIterator(*this, getSize() + 1); }
-    auto last() const {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!isDeleted(getSize() - 1 - i)) break;
-      }
-      return ConstStackIterator(*this, getSize() - 1 - i + 1);
-    }
-
-    auto cbegin() const {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!isDeleted(i)) break;
-      }
-      return ConstStackIterator(*this, i + 1);
-    }
-    auto cend() const { return ConstStackIterator(*this, getSize()); }
-    auto clast() const {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!isDeleted(getSize() - 1 - i)) break;
-      }
-      return ConstStackIterator(*this, getSize() - 1 - i + 1);
-    }
-    StackIterator at(unsigned int i) { return StackIterator(*this, i); }
-    ConstStackIterator at(unsigned int i) const { return ConstStackIterator(*this, i); }
-    StackIterator first() { return StackIterator{*this, 0}; }
-    ConstStackIterator cfirst() const { return ConstStackIterator{*this, 0}; }
-    /// @}
-
-    void Swap(StackIterator a, StackIterator b) {
-      C8LOG_TRACE("View::Swap");
-      inner_stack_.Swap(GetIndexFromIterator(a.GetIndex()),
-                        GetIndexFromIterator(b.GetIndex()));
-    }
-    void Copy(StackIterator a, StackIterator b) {
-      C8LOG_TRACE("View::Copy");
-      inner_stack_.Copy(GetIndexFromIterator(a.GetIndex()),
-                        GetIndexFromIterator(b.GetIndex()));
-    }
-    void Copy(ConstStackIterator a, StackIterator b) {
-      C8LOG_TRACE("View::Copy");
-      inner_stack_.Copy(GetIndexFromIterator(a.GetIndex()),
-                        GetIndexFromIterator(b.GetIndex()));
-    }
-
-    /**
-     * need overwrite Stack::Delete, since we want to call
-     * SecondaryView::DeleteLast
-     *
-     * The particle is deleted on the underlying (internal) stack. The
-     * local references in SecondaryView in indices_ must be fixed,
-     * too.  The approach is to a) check if the particle 'p' is at the
-     * very end of the internal stack, b) if not: move it there by
-     * copying the last particle to the current particle location, c)
-     * remove the last particle.
-     *
-     */
-    void Delete(StackIterator p) {
-      C8LOG_TRACE("SecondaryView::Delete");
-      if (IsEmpty()) { /*error*/
-        throw std::runtime_error("Stack, cannot delete entry since size is zero");
-      }
-      if (isDeleted(p.GetIndex() - 1)) { /*error*/
-        throw std::runtime_error("Stack, cannot delete entry since already deleted");
-      }
-      inner_stack_.Delete(GetIndexFromIterator(p.GetIndex()));
-      InnerStackTypeRef::nDeleted_++; // also count in SecondaryView
-    }
-
-    /**
-     * return next particle from stack, need to overwrtie Stack::GetNextParticle to get
-     * right reference
-     */
-    StackIterator GetNextParticle() {
-      while (purgeLastIfDeleted()) {}
-      return last();
-    }
-
-    /**
-     * check if this particle was already deleted
-     *
-     * need to re-implement for SecondaryView since StackIterator types are a bit
-     * different
-     */
-    bool isDeleted(const StackIterator& p) const { return isDeleted(p.GetIndex() - 1); }
-    bool isDeleted(const ConstStackIterator& p) const {
-      return isDeleted(p.GetIndex() - 1);
-    }
-    /**
-     * delete this particle
-     */
-    bool isDeleted(const ParticleInterfaceType& p) const {
-      return isDeleted(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.
-     */
-    bool purgeLastIfDeleted() {
-      C8LOG_TRACE("SecondaryView::purgeLastIfDeleted");
-      if (!isDeleted(getSize() - 1))
-        return false; // the last particle is not marked for deletion. Do nothing.
-      inner_stack_.purge(GetIndexFromIterator(getSize()));
-      InnerStackTypeRef::nDeleted_--;
-      indices_.pop_back();
-      return true;
-    }
-
-    /**
-     * Function to ultimatively remove all entries from the stack
-     * marked as deleted.
-     *
-     * Careful: this will re-order the entries on the stack, since
-     * "gaps" in the stack are filled with entries from the back
-     * (copied).
-     */
-    void purge() {
-      unsigned int iStack = 0;
-      unsigned int size = getSize();
-      while (iStack < size) {
-        if (isDeleted(iStack)) {
-          inner_stack_.purge(iStack);
-          indices_.erase(indices_.begin() + iStack);
-        }
-        size = getSize();
-        iStack++;
-      }
-      InnerStackTypeRef::nDeleted_ = 0;
-    }
-
-    std::string as_string() const {
-      std::string str(fmt::format("size {}\n", getSize()));
-      // we make our own begin/end since we want ALL entries
-      std::string new_line = "     ";
-      for (unsigned int iPart = 0; iPart != getSize(); ++iPart) {
-        ConstStackIterator itPart(*this, iPart);
-        str += fmt::format(
-            "{}{}{}", new_line, itPart.as_string(),
-            (inner_stack_.deleted_[GetIndexFromIterator(itPart.GetIndex())] ? " [deleted]"
-                                                                            : ""));
-        new_line = "\n     ";
-      }
-      return str;
-    }
-
-  protected:
-    // forward to inner stack
-    // this also checks the allowed bounds of 'i'
-    bool isDeleted(unsigned int i) const {
-      if (i >= indices_.size()) return false;
-      return inner_stack_.isDeleted(GetIndexFromIterator(i + 1));
-    }
-
-    /**
-     * We only want to 'see' secondaries indexed in indices_. In this
-     * function the conversion form iterator-index to stack-index is
-     * performed.
-     */
-    unsigned int GetIndexFromIterator(const unsigned int vI) const {
-      // this is too much: C8LOG_TRACE("SecondaryView::GetIndexFromIterator({})={}", vI,
-      // (vI?indices_[vI-1]:projectile_index_));
-      if (vI == 0) return projectile_index_;
-      return indices_[vI - 1];
-    }
-  };
-
-  /**
-   * Class to handle the generation of new secondaries. Used as default mix-in for
-   * SecondaryView.
-   */
-  template <class T1, template <class> class T2>
-  class DefaultSecondaryProducer {
-    using View = SecondaryView<T1, T2, DefaultSecondaryProducer>;
-
-  public:
-    static bool constexpr has_event{false};
-
-    /**
-     * Method is called after a new secondary has been created on the
-     * SecondaryView. Extra logic can be introduced here.
-     *
-     * The input Particle is the new secondary that was produced and
-     * is of course a reference into the SecondaryView itself.
-     */
-    template <typename Particle>
-    auto new_secondary(Particle&&) const {
-      C8LOG_TRACE("DefaultSecondaryProducer::new_secondary(Particle&&)");
-    }
-
-    /**
-     * Method is called when a new SecondaryView is being created
-     * created. Extra logic can be introduced here.
-     *
-     * The input Particle is a reference object into the original
-     * parent stack! It is not a reference into the SecondaryView
-     * itself.
-     */
-    template <typename Particle>
-    DefaultSecondaryProducer(Particle const&) {
-      C8LOG_TRACE("DefaultSecondaryProducer::DefaultSecondaryProducer(Particle&)");
-    }
-  };
-
-  /*
-    See Issue 161
-
-    unfortunately clang does not support this in the same way (yet) as
-    gcc, so we have to distinguish here. If clang cataches up, we
-    could remove the #if here and elsewhere. The gcc code is much more
-    generic and universal.
-  */
-#if not defined(__clang__) && defined(__GNUC__) || defined(__GNUG__)
-  template <typename TStack,
-            template <class TStack_, template <class> class MPIType_>
-            class MSecondaryProducer = corsika::stack::DefaultSecondaryProducer,
-            template <typename> typename MPIType_ = TStack::template MPIType>
-  struct MakeView {
-    using type = corsika::stack::SecondaryView<typename TStack::StackImpl, MPIType_,
-                                               MSecondaryProducer>;
-  };
-#endif
-
-} // namespace corsika::stack
diff --git a/Framework/StackInterface/Stack.dox b/Framework/StackInterface/Stack.dox
deleted file mode 100644
index 8ac29c628c08a5c2a65ad8831d762b9b67c830e7..0000000000000000000000000000000000000000
--- a/Framework/StackInterface/Stack.dox
+++ /dev/null
@@ -1,20 +0,0 @@
-/**
-  @page Stack Description of particle stacks
-
-  In the CORSIKA 8 framework particle data is always stored in
-  particle stacks. A particle is, thus, always a reference (iterator)
-  to an entry on a stack, e.g.
-
-  \verbatim
-  ModelStack stack;
-  stack.begin(); // returns an iterator: StackIterator, ConstStackIterator
-
-  *stack.begin(); // return a reference to ParticleInterfaceType, which is the class provided by the user to read/write particle properties
-  
-  \endverbatim
-
-  All functionality and algorithms for stack data access is located in the namespace corsika::stack
-
-  The minimal example of how to use this is shown in stack_example.cc  
-
-*/
\ No newline at end of file
diff --git a/Framework/StackInterface/Stack.h b/Framework/StackInterface/Stack.h
deleted file mode 100644
index 85ec6418fe0898573b3db16d9d52e867404d3b76..0000000000000000000000000000000000000000
--- a/Framework/StackInterface/Stack.h
+++ /dev/null
@@ -1,412 +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/logging/Logging.h>
-#include <corsika/stack/StackIteratorInterface.h>
-#include <corsika/utl/MetaProgramming.h>
-
-#include <stdexcept>
-#include <string>
-#include <vector>
-
-/**
-   All classes around management of particles on a stack.
- */
-
-namespace corsika::stack {
-
-  /**
-     This is just a forward declatation for the user-defined
-     ParticleInterface, which is one of the essential template
-     parameters for the Stack.
-
-     <b>Important:</b> ParticleInterface must inherit from ParticleBase !
-   */
-
-  template <typename>
-  class ParticleInterface;
-
-  /**
-     The Stack class provides (and connects) the main particle data storage machinery.
-
-     The StackDataType type is the user-provided bare data storage
-     object. This can be of any complexity, from a simple struct
-     (fortran common block), to a combination of different and
-     distributed data sources.
-
-     The user-provided ParticleInterface template type is the base
-     class type of the StackIteratorInterface class (CRTP) and must
-     provide all functions to read single particle data from the
-     StackDataType, given an 'unsigned int' index.
-
-     The Stack implements the
-     std-type begin/end function to allow integration in normal for
-     loops, ranges, etc.
-   */
-
-  template <typename TStackData, template <typename> typename MParticleInterface>
-  class Stack {
-    using StackDataValueType = std::remove_reference_t<TStackData>;
-
-  private:
-    TStackData data_; ///< this in general holds all the data and can be quite big
-    std::vector<bool> deleted_; ///< bit field to flag deleted entries
-  protected:
-    unsigned int nDeleted_ = 0;
-
-  private:
-    Stack(Stack&) = delete; ///< since Stack can be very big, we don't want to copy it
-    Stack& operator=(Stack&) =
-        delete; ///< since Stack can be very big, we don't want to copy it
-
-  public:
-    /**
-     * if TStackData is a reference member we *HAVE* to initialize
-     * it in the constructor, this is typically needed for SecondaryView
-     */
-    template <typename _ = TStackData, typename = utl::enable_if<std::is_reference<_>>>
-    Stack(TStackData vD)
-        : data_(vD)
-        , deleted_(std::vector<bool>(data_.GetSize(), false))
-        , nDeleted_(0) {}
-
-    /**
-     * This constructor takes any argument and passes it on to the
-     * TStackData user class. If the user did not provide a suited
-     * constructor this will fail with an error message.
-     *
-     * Furthermore, this is disabled with enable_if for SecondaryView
-     * stacks, where the inner data container is always a reference
-     * and cannot be initialized here.
-     */
-    template <typename... TArgs, typename _ = TStackData,
-              typename = utl::disable_if<std::is_reference<_>>>
-    Stack(TArgs... args)
-        : data_(args...)
-        , deleted_(std::vector<bool>(data_.GetSize(), false))
-        , nDeleted_(0) {}
-
-  public:
-    typedef TStackData
-        StackImpl; ///< this is the type of the user-provided data structure
-
-    template <typename TSI>
-    using MPIType = MParticleInterface<TSI>;
-
-    /**
-     * Via the StackIteratorInterface and ConstStackIteratorInterface
-     * specialization, the type of the StackIterator
-     * template class is declared for a particular stack data
-     * object. Using CRTP, this also determines the type of
-     * MParticleInterface template class simultaneously.
-     */
-    using StackIterator =
-        StackIteratorInterface<StackDataValueType, MParticleInterface, Stack>;
-    using ConstStackIterator =
-        ConstStackIteratorInterface<StackDataValueType, MParticleInterface, Stack>;
-
-    /**
-     * this is the full type of the user-declared MParticleInterface
-     */
-    using ParticleInterfaceType = typename StackIterator::ParticleInterfaceType;
-    /**
-     * In all programming context, the object to access, copy, and
-     * transport particle data is via the StackIterator
-     */
-    using ParticleType = StackIterator;
-
-    // friends are needed since they need access to protected members
-    friend class StackIteratorInterface<StackDataValueType, MParticleInterface, Stack>;
-    friend class ConstStackIteratorInterface<StackDataValueType, MParticleInterface,
-                                             Stack>;
-    template <typename T1, //=TStackData,
-              template <typename>
-              typename M1, //=MParticleInterface,
-                           //             template<typename>typename M2>
-              template <class T2, template <class> class T3> class MSecondaryProducer>
-    friend class SecondaryView; //<TStackData,MParticleInterface,M>; // access for
-                                // SecondaryView
-
-    friend class ParticleBase<StackIterator>;
-
-  public:
-    /**
-     * @name Most generic proxy methods for TStackData data_
-     * @{
-     */
-    unsigned int GetCapacity() const { return data_.GetCapacity(); }
-    unsigned int getDeleted() const { return nDeleted_; }
-    unsigned int getEntries() const { return getSize() - getDeleted(); }
-
-    template <typename... TArgs>
-    void Clear(TArgs... args) {
-      data_.Clear(args...);
-      deleted_ = std::vector<bool>(data_.GetSize(), false);
-      nDeleted_ = 0;
-    }
-    ///@}
-
-  public:
-    /**
-     * @name These are functions required by std containers and std loops
-     * @{
-     */
-    StackIterator begin() {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!deleted_[i]) break;
-      }
-      return StackIterator(*this, i);
-    }
-    StackIterator end() { return StackIterator(*this, getSize()); }
-    StackIterator last() {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!deleted_[getSize() - 1 - i]) break;
-      }
-      return StackIterator(*this, getSize() - 1 - i);
-    }
-
-    ConstStackIterator begin() const {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!deleted_[i]) break;
-      }
-      return ConstStackIterator(*this, i);
-    }
-    ConstStackIterator end() const { return ConstStackIterator(*this, getSize()); }
-    ConstStackIterator last() const {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!deleted_[getSize() - 1 - i]) break;
-      }
-      return ConstStackIterator(*this, getSize() - 1 - i);
-    }
-
-    ConstStackIterator cbegin() const {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!deleted_[i]) break;
-      }
-      return ConstStackIterator(*this, i);
-    }
-    ConstStackIterator cend() const { return ConstStackIterator(*this, getSize()); }
-    ConstStackIterator clast() const {
-      unsigned int i = 0;
-      for (; i < getSize(); ++i) {
-        if (!deleted_[getSize() - 1 - i]) break;
-      }
-      return ConstStackIterator(*this, getSize() - 1 - i);
-    }
-    StackIterator at(unsigned int i) { return StackIterator(*this, i); }
-    ConstStackIterator at(unsigned int i) const { return ConstStackIterator(*this, i); }
-    StackIterator first() { return StackIterator{*this, 0}; }
-    ConstStackIterator cfirst() const { return ConstStackIterator{*this, 0}; }
-    /// @}
-
-    StackIterator GetNextParticle() {
-      while (purgeLastIfDeleted()) {}
-      return last();
-    }
-
-    /**
-     * increase stack size, create new particle at end of stack
-     */
-    template <typename... TArgs>
-    StackIterator AddParticle(const TArgs... v) {
-      C8LOG_TRACE("Stack::AddParticle");
-      data_.IncrementSize();
-      deleted_.push_back(false);
-      return StackIterator(*this, getSize() - 1, v...);
-    }
-
-  protected:
-    /**
-     * increase stack size, create new particle at end of stack, related to parent
-     * particle/projectile
-     *
-     * This should only get internally called from a
-     * StackIterator::AddSecondary via ParticleBase
-     */
-    template <typename... TArgs>
-    StackIterator AddSecondary(StackIterator& parent, const TArgs... v) {
-      C8LOG_TRACE("Stack::AddSecondary");
-      data_.IncrementSize();
-      deleted_.push_back(false);
-      return StackIterator(*this, getSize() - 1, parent, v...);
-    }
-
-  public:
-    void Swap(StackIterator a, StackIterator b) {
-      C8LOG_TRACE("Stack::Swap");
-      Swap(a.GetIndex(), b.GetIndex());
-    }
-    void Copy(StackIterator a, StackIterator b) {
-      C8LOG_TRACE("Stack::Copy");
-      Copy(a.GetIndex(), b.GetIndex());
-    }
-    void Copy(ConstStackIterator a, StackIterator b) {
-      C8LOG_TRACE("Stack::Copy");
-      data_.Copy(a.GetIndex(), b.GetIndex());
-      if (deleted_[b.GetIndex()] && !deleted_[a.GetIndex()]) nDeleted_--;
-      if (!deleted_[b.GetIndex()] && deleted_[a.GetIndex()]) nDeleted_++;
-      deleted_[b.GetIndex()] = deleted_[a.GetIndex()];
-    }
-
-  protected:
-    void Swap(unsigned int a, unsigned int b) {
-      C8LOG_TRACE("Stack::Swap(unsigned int)");
-      data_.Swap(a, b);
-      std::swap(deleted_[a], deleted_[b]);
-    }
-    void Copy(unsigned int a, unsigned int b) {
-      C8LOG_TRACE("Stack::Copy");
-      data_.Copy(a, b);
-      if (deleted_[b] && !deleted_[a]) nDeleted_--;
-      if (!deleted_[b] && deleted_[a]) nDeleted_++;
-      deleted_[b] = deleted_[a];
-    }
-
-    /**
-     * delete this particle
-     */
-  public:
-    void Delete(StackIterator p) {
-      C8LOG_TRACE("Stack::Delete");
-      if (IsEmpty()) { /*error*/
-        throw std::runtime_error("Stack, cannot delete entry since size is zero");
-      }
-      if (deleted_[p.GetIndex()]) { /*error*/
-        throw std::runtime_error("Stack, cannot delete entry since already deleted");
-      }
-      Delete(p.GetIndex());
-    }
-    /**
-     * delete this particle
-     */
-    void Delete(ParticleInterfaceType p) { Delete(p.GetIterator()); }
-
-    /**
-     * check if there are no further non-deleted particles on stack
-     */
-    bool IsEmpty() { return getEntries() == 0; }
-
-    /**
-     * check if this particle was already deleted
-     */
-    bool isDeleted(const StackIterator& p) const { return isDeleted(p.GetIndex()); }
-    bool isDeleted(const ConstStackIterator& p) const { return isDeleted(p.GetIndex()); }
-    bool isDeleted(const ParticleInterfaceType& p) const {
-      return isDeleted(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.
-     */
-    bool purgeLastIfDeleted() {
-      if (!deleted_.back())
-        return false; // the last particle is not marked for deletion. Do nothing.
-      C8LOG_TRACE("Stack::purgeLastIfDeleted: yes");
-      data_.DecrementSize();
-      nDeleted_--;
-      deleted_.pop_back();
-      return true;
-    }
-
-    /**
-     * Function to ultimatively remove all entries from the stack
-     * marked as deleted.
-     *
-     * Careful: this will re-order the entries on the stack, since
-     * "gaps" in the stack are filled with entries from the back
-     * (copied).
-     */
-    void purge() {
-      unsigned int iStackFront = 0;
-      unsigned int iStackBack = getSize() - 1;
-      for (unsigned int iDeleted = 0; iDeleted < getDeleted(); ++iDeleted) {
-        // search first delete entry on stack
-        while (!deleted_[iStackFront]) { iStackFront++; }
-        // search for last non-deleted particle on stack
-        while (deleted_[iStackBack]) { iStackBack--; }
-        // copy entry from iStackBack to iStackFront
-        data_.Copy(iStackBack, iStackFront);
-        data_.DecrementSize();
-      }
-      deleted_.clear();
-      nDeleted_ = 0;
-    }
-
-    unsigned int getSize() const { return data_.GetSize(); }
-
-    std::string as_string() const {
-      std::string str(fmt::format("size {}, entries {}, deleted {} \n", getSize(),
-                                  getEntries(), getDeleted()));
-      // we make our own begin/end since we want ALL entries
-      std::string new_line = "     ";
-      for (unsigned int iPart = 0; iPart != getSize(); ++iPart) {
-        ConstStackIterator itPart(*this, iPart);
-        str += fmt::format("{}{}{}", new_line, itPart.as_string(),
-                           (deleted_[itPart.GetIndex()] ? " [deleted]" : ""));
-        new_line = "\n     ";
-      }
-      return str;
-    }
-
-  protected:
-    bool isDeleted(unsigned int i) const {
-      if (i >= deleted_.size()) return false;
-      return deleted_.at(i);
-    }
-
-    void Delete(unsigned int i) {
-      deleted_[i] = true;
-      nDeleted_++;
-    }
-
-    /**
-     * will remove from storage the element i. This is a helper
-     * function for SecondaryView.
-     */
-    void purge(unsigned int i) {
-      unsigned int iStackBack = getSize() - 1;
-      // search for last non-deleted particle on stack
-      while (deleted_[iStackBack]) { iStackBack--; }
-      // copy entry from iStackBack to iStackFront
-      data_.Copy(iStackBack, i);
-      if (deleted_[i]) nDeleted_--;
-      deleted_[i] = deleted_[iStackBack];
-      data_.DecrementSize();
-      deleted_.pop_back();
-    }
-
-    /**
-     * Function to perform eventual transformation from
-     * StackIterator::GetIndex() to index in data stored in
-     * TStackData data_. By default (and in almost all cases) this
-     * should just be identiy. See class SecondaryView for an alternative implementation.
-     */
-    unsigned int GetIndexFromIterator(const unsigned int vI) const {
-      // this is too much: C8LOG_TRACE("Stack::GetIndexFromIterator({})={}", vI, vI);
-      return vI;
-    }
-
-    /**
-     * @name Return reference to TStackData object data_ for data access
-     * @{
-     */
-    StackDataValueType& GetStackData() { return data_; }
-    const StackDataValueType& GetStackData() const { return data_; }
-    ///@}
-  };
-
-} // namespace corsika::stack
diff --git a/Framework/StackInterface/testCombinedStack.cc b/Framework/StackInterface/testCombinedStack.cc
deleted file mode 100644
index 9e4d9274f256544a8ea12e0541a2f87b3d130955..0000000000000000000000000000000000000000
--- a/Framework/StackInterface/testCombinedStack.cc
+++ /dev/null
@@ -1,393 +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.
- */
-
-#define protected public // to also test the internal state of objects
-
-#include <corsika/stack/CombinedStack.h>
-#include <corsika/stack/SecondaryView.h>
-#include <corsika/stack/Stack.h>
-
-#include <testTestStack.h> // for testing: simple stack. This is a
-// test-build, and inluce file is obtained from CMAKE_CURRENT_SOURCE_DIR
-
-#include <iomanip>
-#include <vector>
-
-#include <catch2/catch.hpp>
-
-using namespace corsika;
-using namespace corsika::stack;
-using namespace std;
-
-////////////////////////////////////////////////////////////
-// first level test: combine two stacks:
-//                   StackTest = (TestStackData + TestStackData2)
-
-// definition of stack-data object
-class TestStackData2 {
-
-public:
-  // these functions are needed for the Stack interface
-  void Clear() { fData2.clear(); }
-  unsigned int GetSize() const { return fData2.size(); }
-  unsigned int GetCapacity() const { return fData2.size(); }
-  void Copy(const int i1, const int i2) { fData2[i2] = fData2[i1]; }
-  void Swap(const int i1, const int i2) {
-    double tmp0 = fData2[i1];
-    fData2[i1] = fData2[i2];
-    fData2[i2] = tmp0;
-  }
-
-  // custom data access function
-  void SetData2(const int i, const double v) { fData2[i] = v; }
-  double GetData2(const int i) const { return fData2[i]; }
-
-  // these functions are also needed by the Stack interface
-  void IncrementSize() { fData2.push_back(0.); }
-  void DecrementSize() {
-    if (fData2.size() > 0) { fData2.pop_back(); }
-  }
-
-  // custom private data section
-private:
-  std::vector<double> fData2;
-};
-
-// defintion of a stack-readout object, the iteractor dereference
-// operator will deliver access to these function
-template <typename T>
-class TestParticleInterface2 : public T {
-
-public:
-  using T::GetIndex;
-  using T::GetStackData;
-  using T::SetParticleData;
-
-  // default version for particle-creation from input data
-  void SetParticleData(const std::tuple<double> v = {0.}) { SetData2(std::get<0>(v)); }
-  void SetParticleData(TestParticleInterface2<T>& parent,
-                       const std::tuple<double> v = {0.}) {
-    SetData2(parent.GetData2() + std::get<0>(v));
-  }
-  void SetData2(const double v) { GetStackData().SetData2(GetIndex(), v); }
-  double GetData2() const { return GetStackData().GetData2(GetIndex()); }
-};
-
-// combined stack: StackTest = (TestStackData + TestStackData2)
-template <typename StackIter>
-using CombinedTestInterfaceType =
-    corsika::stack::CombinedParticleInterface<TestParticleInterface,
-                                              TestParticleInterface2, StackIter>;
-
-using StackTest = CombinedStack<TestStackData, TestStackData2, CombinedTestInterfaceType>;
-
-TEST_CASE("Combined Stack", "[stack]") {
-
-  // helper function for sum over stack data
-  auto sum = [](const StackTest& stack) {
-    double v = 0;
-    for (const auto& p : stack) v += p.GetData();
-    return v;
-  };
-  auto sum2 = [](const StackTest& stack) {
-    double v = 0;
-    for (const auto& p : stack) v += p.GetData2();
-    return v;
-  };
-
-  SECTION("StackInterface") {
-
-    // construct a valid Stack object
-    StackTest s;
-    s.Clear();
-    s.AddParticle(std::tuple{0.});
-    s.Copy(s.cbegin(), s.begin());
-    s.Swap(s.begin(), s.begin());
-    CHECK(s.getSize() == 1);
-  }
-
-  SECTION("construct") {
-
-    // construct a valid, empty Stack object
-    StackTest s;
-  }
-
-  SECTION("write and read") {
-
-    StackTest s;
-    s.AddParticle(std::tuple{9.9});
-    CHECK(sum2(s) == 0.);
-    CHECK(sum(s) == 9.9);
-  }
-
-  SECTION("delete from stack") {
-
-    StackTest s;
-    CHECK(s.getSize() == 0);
-    StackTest::StackIterator p =
-        s.AddParticle(std::tuple{0.}); // valid way to access particle data
-    p.SetData(8.9);
-    p.SetData2(3.);
-    CHECK(sum2(s) == 3.);
-    CHECK(sum(s) == 8.9);
-    CHECK(s.getSize() == 1);
-    CHECK(s.getEntries() == 1);
-    s.Delete(p);
-    CHECK(s.getSize() == 1);
-    CHECK(s.getEntries() == 0);
-  }
-
-  SECTION("delete particle") {
-
-    StackTest s;
-    CHECK(s.getSize() == 0);
-    auto p = s.AddParticle(
-        std::tuple{9.9}); // also valid way to access particle data, identical to above
-    CHECK(s.getSize() == 1);
-    CHECK(s.getEntries() == 1);
-    p.Delete();
-    CHECK(s.getSize() == 1);
-    CHECK(s.getEntries() == 0);
-  }
-
-  SECTION("create secondaries") {
-    StackTest s;
-    CHECK(s.getSize() == 0);
-    auto iter = s.AddParticle(std::tuple{9.9});
-    iter.SetData2(2);
-    CHECK(s.getSize() == 1);
-    CHECK(s.getEntries() == 1);
-    iter.AddSecondary(std::tuple{4.4});
-    CHECK(s.getSize() == 2);
-    CHECK(s.getEntries() == 2);
-    // p.AddSecondary(3.3, 2.2, 1.);
-    // CHECK(s.getSize() == 3);
-    double v = 0;
-    for (const auto& i : s) {
-      v += i.GetData();
-      CHECK(i.GetData2() == 2);
-    }
-    CHECK(v == 9.9 + 4.4);
-  }
-
-  SECTION("get next particle") {
-    StackTest s;
-    CHECK(s.getSize() == 0);
-    CHECK(s.getEntries() == 0);
-    CHECK(s.IsEmpty());
-
-    auto p1 = s.AddParticle(std::tuple{9.9});
-    auto p2 = s.AddParticle(std::tuple{8.8});
-    p1.SetData2(20.2);
-    p2.SetData2(20.3);
-    CHECK(s.getSize() == 2);
-    CHECK(s.getEntries() == 2);
-    CHECK(!s.IsEmpty());
-
-    auto particle = s.GetNextParticle(); // first particle
-    CHECK(particle.GetData() == 8.8);
-    CHECK(particle.GetData2() == 20.3);
-
-    particle.Delete(); // only marks (last) particle as "deleted"
-    CHECK(s.getSize() == 2);
-    CHECK(s.getEntries() == 1);
-    CHECK(!s.IsEmpty());
-
-    /*
-      This following call to GetNextParticle will realize that the
-      current last particle on the stack was marked "deleted" and will
-      purge it: stack size is reduced by one.
-     */
-    auto particle2 = s.GetNextParticle(); // first particle
-    CHECK(s.getSize() == 1);
-    CHECK(s.getEntries() == 1);
-    CHECK(!s.IsEmpty());
-    CHECK(particle2.GetData() == 9.9);
-    CHECK(particle2.GetData2() == 20.2);
-
-    particle2.Delete(); // also mark this particle as "deleted"
-    CHECK(s.getSize() == 1);
-    CHECK(s.getEntries() == 0);
-    CHECK(s.IsEmpty());
-  }
-}
-
-////////////////////////////////////////////////////////////
-// next level: combine three stacks:
-// combined stack: StackTest2 = ((TestStackData + TestStackData2) + TestStackData3)
-
-// definition of stack-data object
-class TestStackData3 {
-
-public:
-  // these functions are needed for the Stack interface
-  void Clear() { fData3.clear(); }
-  unsigned int getSize() const { return fData3.size(); }
-  unsigned int GetCapacity() const { return fData3.size(); }
-  void Copy(const int i1, const int i2) { fData3[i2] = fData3[i1]; }
-  void Swap(const int i1, const int i2) {
-    double tmp0 = fData3[i1];
-    fData3[i1] = fData3[i2];
-    fData3[i2] = tmp0;
-  }
-
-  // custom data access function
-  void SetData3(const int i, const double v) { fData3[i] = v; }
-  double GetData3(const int i) const { return fData3[i]; }
-
-  // these functions are also needed by the Stack interface
-  void IncrementSize() { fData3.push_back(0.); }
-  void DecrementSize() {
-    if (fData3.size() > 0) { fData3.pop_back(); }
-  }
-
-  // custom private data section
-private:
-  std::vector<double> fData3;
-};
-
-// ---------------------------------------
-// defintion of a stack-readout object, the iteractor dereference
-// operator will deliver access to these function
-template <typename T>
-class TestParticleInterface3 : public T {
-
-public:
-  using T::GetIndex;
-  using T::GetStackData;
-  using T::SetParticleData;
-
-  // default version for particle-creation from input data
-  void SetParticleData(const std::tuple<double> v = {0.}) { SetData3(std::get<0>(v)); }
-  void SetParticleData(TestParticleInterface3<T>& parent,
-                       const std::tuple<double> v = {0.}) {
-    SetData3(parent.GetData3() + std::get<0>(v));
-  }
-  void SetData3(const double v) { GetStackData().SetData3(GetIndex(), v); }
-  double GetData3() const { return GetStackData().GetData3(GetIndex()); }
-};
-
-// double combined stack:
-// combined stack
-template <typename StackIter>
-using CombinedTestInterfaceType2 =
-    corsika::stack::CombinedParticleInterface<StackTest::MPIType, TestParticleInterface3,
-                                              StackIter>;
-
-using StackTest2 = CombinedStack<typename StackTest::StackImpl, TestStackData3,
-                                 CombinedTestInterfaceType2>;
-
-TEST_CASE("Combined Stack - multi", "[stack]") {
-
-  SECTION("create secondaries") {
-
-    StackTest2 s;
-    CHECK(s.getSize() == 0);
-    CHECK(s.IsEmpty()); // size = entries = 0
-
-    // add new particle, only provide tuple data for StackTest
-    auto p1 = s.AddParticle(std::tuple{9.9});
-    // add new particle, provide tuple data for both StackTest and TestStackData3
-    auto p2 = s.AddParticle(std::tuple{8.8}, std::tuple{0.1});
-
-    CHECK(s.getSize() == 2);
-    CHECK(!s.IsEmpty()); // size = entries = 2
-
-    // examples to explicitly change data on stack
-    p2.SetData2(0.1); // not clear why this is needed, need to check
-                      // SetParticleData workflow for more complicated
-                      // settings
-    p1.SetData3(20.2);
-    p2.SetData3(10.3);
-
-    CHECK(p1.GetData() == 9.9);
-    CHECK(p1.GetData2() == 0.);
-    p1.SetData2(10.2);
-    CHECK(p1.GetData2() == 10.2);
-    CHECK(p1.GetData3() == 20.2);
-
-    CHECK(p2.GetData() == 8.8);
-    CHECK(p2.GetData2() == 0.1);
-    CHECK(p2.GetData3() == 10.3);
-
-    auto particle = s.GetNextParticle(); // first particle
-    CHECK(particle.GetData() == 8.8);
-    CHECK(particle.GetData2() == 0.1);
-    CHECK(particle.GetData3() == 10.3);
-
-    auto sec = particle.AddSecondary(std::tuple{4.4});
-    CHECK(s.getSize() == 3);
-    CHECK(s.getEntries() == 3);
-    CHECK(sec.GetData() == 4.4);
-    CHECK(sec.GetData2() == 0.1);
-    CHECK(sec.GetData3() == 10.3);
-
-    sec.Delete(); // mark for deletion: size=3, entries=2
-    CHECK(s.getSize() == 3);
-    CHECK(s.getEntries() == 2);
-    CHECK(!s.IsEmpty());
-
-    s.last().Delete(); // mark for deletion: size=3, entries=1
-    CHECK(s.getSize() == 3);
-    CHECK(s.getEntries() == 1);
-    CHECK(!s.IsEmpty());
-
-    /*
-       GetNextParticle will find two entries marked as "deleted" and
-       will purge this from the end of the stack: size = 1
-    */
-    s.GetNextParticle().Delete(); // mark for deletion: size=3, entries=0
-    CHECK(s.getSize() == 1);
-    CHECK(s.getEntries() == 0);
-    CHECK(s.IsEmpty());
-  }
-}
-
-////////////////////////////////////////////////////////////
-
-// final level test, create SecondaryView on StackTest2
-
-/*
-  See Issue 161
-
-  unfortunately clang does not support this in the same way (yet) as
-  gcc, so we have to distinguish here. If clang cataches up, we could
-  remove the clang branch here and also in corsika::Cascade. The gcc
-  code is much more generic and universal.
- */
-template <typename StackIter>
-using CombinedTestInterfaceType2 =
-    corsika::stack::CombinedParticleInterface<StackTest::MPIType, TestParticleInterface3,
-                                              StackIter>;
-
-using StackTest2 = CombinedStack<typename StackTest::StackImpl, TestStackData3,
-                                 CombinedTestInterfaceType2>;
-
-#if defined(__clang__)
-using StackTestView =
-    SecondaryView<typename StackTest2::StackImpl, CombinedTestInterfaceType2>;
-#elif defined(__GNUC__) || defined(__GNUG__)
-using StackTestView = corsika::stack::MakeView<StackTest2>::type;
-#endif
-
-using Particle2 = typename StackTest2::ParticleType;
-
-TEST_CASE("Combined Stack - secondary view") {
-
-  SECTION("create secondaries via secondaryview") {
-
-    StackTest2 stack;
-    auto particle = stack.AddParticle(std::tuple{9.9});
-    StackTestView view(particle);
-
-    auto projectile = view.GetProjectile();
-    projectile.AddSecondary(std::tuple{8.8});
-
-    CHECK(stack.getSize() == 2);
-  }
-}
diff --git a/Framework/Testing/CMakeLists.txt b/Framework/Testing/CMakeLists.txt
deleted file mode 100644
index 0c616e186861468c4b920a5c1387fec055904229..0000000000000000000000000000000000000000
--- a/Framework/Testing/CMakeLists.txt
+++ /dev/null
@@ -1,52 +0,0 @@
-set (
-  TESTING_SOURCES
-  TestMain.cc
-  )
-
-set (
-  TESTING_HEADERS
-  )
-
-set (
-  TESTING_NAMESPACE
-  corsika/testing
-  )
-
-add_library (CORSIKAtesting STATIC ${TESTING_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAtesting ${TESTING_NAMESPACE} ${TESTING_HEADERS})
-
-set_target_properties (
-  CORSIKAtesting
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  PUBLIC_HEADER "${TESTING_HEADERS}"
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  CORSIKAtesting
-  CORSIKAthirdparty
-  )
-
-# target_include_directories (
-#  CORSIKAtesting
-#  SYSTEM
-#  PUBLIC    ${CATCH2_INCLUDE_DIR}
-#  INTERFACE ${CATCH2_INCLUDE_DIR}
-#  )
-
-target_include_directories (
-  CORSIKAtesting
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS CORSIKAtesting
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-  PUBLIC_HEADER DESTINATION include/${TESTING_NAMESPACE}
-  )
-
diff --git a/Framework/Units/CMakeLists.txt b/Framework/Units/CMakeLists.txt
deleted file mode 100644
index 8386ae0264d3e5fc0f6cd63cc1617f2c82f7c6e9..0000000000000000000000000000000000000000
--- a/Framework/Units/CMakeLists.txt
+++ /dev/null
@@ -1,23 +0,0 @@
-add_library (CORSIKAunits INTERFACE)
-
-set (CORSIKAunits_NAMESPACE corsika/units)
-set (
-  CORSIKAunits_HEADERS
-  PhysicalUnits.h
-  PhysicalConstants.h
-  )
-
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAunits ${CORSIKAunits_NAMESPACE} ${CORSIKAunits_HEADERS})
-
-target_include_directories (CORSIKAunits
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/ThirdParty>
-  $<INSTALL_INTERFACE:include>
-  )
-
-install (FILES ${CORSIKAunits_HEADERS} DESTINATION include/${CORSIKAunits_NAMESPACE})
-
-# code testing
-CORSIKA_ADD_TEST (testUnits)
-target_link_libraries (testUnits CORSIKAunits CORSIKAtesting)
diff --git a/Framework/Units/testUnits.cc b/Framework/Units/testUnits.cc
deleted file mode 100644
index bae1ce1d7123d82c9878bc4a4bbd319f33ce9912..0000000000000000000000000000000000000000
--- a/Framework/Units/testUnits.cc
+++ /dev/null
@@ -1,156 +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 <catch2/catch.hpp>
-
-#include <corsika/units/PhysicalUnits.h>
-
-#include <array>
-#include <sstream>
-
-using namespace corsika;
-using namespace corsika::units::si;
-
-TEST_CASE("PhysicalUnits", "[Units]") {
-
-  SECTION("Consistency") {
-    REQUIRE(1_m / 1_m == Approx(1));
-    // REQUIRE_FALSE( 1_m/1_s == 1 ); // static assert
-  }
-
-  SECTION("Constructors") {
-    [[maybe_unused]] auto E1 = 10_GeV;
-    REQUIRE(E1 == 10_GeV);
-
-    LengthType l1 = 10_nm;
-    [[maybe_unused]] auto l2 = l1;
-    REQUIRE(l2 == l1);
-
-    LengthType arr0[5];
-    arr0[0] = 5_m;
-
-    [[maybe_unused]] LengthType arr1[2] = {{1_mm}, {2_cm}};
-
-    [[maybe_unused]] std::array<HEPEnergyType, 4> arr2; // empty array
-
-    [[maybe_unused]] std::array<HEPEnergyType, 4> arr3 = {1_GeV, 1_eV, 5_MeV};
-
-    auto p1 = 10_s * newton;
-    REQUIRE(p1 == 10_s * newton);
-  }
-
-  SECTION("Powers in literal units") {
-    REQUIRE(1_s / 1_ds == Approx(1e1));
-    REQUIRE(1_m / 1_cm == Approx(1e2));
-    REQUIRE(1_m / 1_mm == Approx(1e3));
-    REQUIRE(1_V / 1_uV == Approx(1e6));
-    REQUIRE(1_s / 1_ns == Approx(1e9));
-    REQUIRE(1_eV / 1_peV == Approx(1e12));
-    REQUIRE(1_A / 1_fA == Approx(1e15));
-    REQUIRE(1_mol / 1_amol == Approx(1e18));
-    REQUIRE(1_K / 1_zK == Approx(1e21));
-    REQUIRE(1_K / 1_yK == Approx(1e24));
-    REQUIRE(1_b / 1_mb == Approx(1e3));
-
-    REQUIRE(1_A / 1_hA == Approx(1e-2));
-    REQUIRE(1_m / 1_km == Approx(1e-3));
-    REQUIRE(1_m / 1_Mm == Approx(1e-6));
-    REQUIRE(1_V / 1_GV == Approx(1e-9));
-    REQUIRE(1_s / 1_Ts == Approx(1e-12));
-    REQUIRE(1_eV / 1_PeV == Approx(1e-15));
-    REQUIRE(1_A / 1_EA == Approx(1e-18));
-    REQUIRE(1_K / 1_ZK == Approx(1e-21));
-    REQUIRE(1_mol / 1_Ymol == Approx(1e-24));
-
-    REQUIRE(std::min(1_A, 2_A) == 1_A);
-  }
-
-  SECTION("Powers and units") {
-    REQUIRE(1 * ampere / 1_A == Approx(1e0));
-    REQUIRE(mega * bar / bar == Approx(1e6));
-  }
-
-  SECTION("Formulas") {
-    const HEPEnergyType E2 = 20_GeV * 2;
-    REQUIRE(E2 == 40_GeV);
-    REQUIRE(E2 / 1_GeV == Approx(40));
-
-    const MassType m = 1_kg;
-    const SpeedType v = 1_m / 1_s;
-    REQUIRE(m * v == 1_s * newton);
-
-    const double lgE = log10(E2 / 1_GeV);
-    REQUIRE(lgE == Approx(log10(40.)));
-
-    const auto E3 = E2 + 100_GeV + pow(10, lgE) * 1_GeV;
-    REQUIRE(E3 == 180_GeV);
-  }
-
-  SECTION("Output") {
-    {
-      const HEPEnergyType E = 5_eV;
-      std::stringstream stream;
-      stream << E;
-      REQUIRE(stream.str() == std::string("5 eV"));
-    }
-    {
-      const HEPEnergyType E = 5_EeV;
-      std::stringstream stream;
-      stream << E;
-      REQUIRE(stream.str() == std::string("5e+18 eV"));
-    }
-  }
-
-  SECTION("Special") {
-
-    const LengthType farAway = std::numeric_limits<double>::infinity() * meter;
-    REQUIRE(farAway > 100000_m);
-    REQUIRE_FALSE(farAway < 1e19 * meter);
-  }
-
-  SECTION("static_pow") {
-    using namespace corsika::units;
-    double x = 235.7913;
-    REQUIRE(1 == static_pow<0, double>(x));
-    REQUIRE(x == static_pow<1, double>(x));
-    REQUIRE(x * x == static_pow<2, double>(x));
-    REQUIRE(1 / x == static_pow<-1, double>(x));
-    REQUIRE(1 / x / x == static_pow<-2, double>(x));
-  }
-
-  SECTION("HEP/SI conversion") {
-    auto const invEnergy = 1 / 197.326978_MeV; // should be convertible to length or time
-
-    LengthType const length = ConvertHEPToSI<LengthType::dimension_type>(invEnergy);
-    REQUIRE((length / 1_fm) == Approx(1));
-
-    TimeType const time = ConvertHEPToSI<TimeType::dimension_type>(invEnergy);
-    REQUIRE((time / (1_fm / corsika::units::constants::c)) == Approx(1));
-
-    auto const protonMass = 938.272'081'3_MeV; // convertible to mass or SI energy
-    MassType protonMassSI = ConvertHEPToSI<MassType::dimension_type>(protonMass);
-    REQUIRE((protonMassSI / 1.672'621'898e-27_kg) == Approx(1));
-    REQUIRE((protonMassSI / (1.007'276 * corsika::units::constants::u)) == Approx(1));
-  }
-
-  SECTION("SI/HEP conversion") {
-    REQUIRE(ConvertSIToHEP(units::constants::c) == Approx(1));
-    REQUIRE(ConvertSIToHEP(units::constants::hBar) == Approx(1));
-
-    {
-      auto const invLength = 1 / 197.326978_fm; // should be convertible to HEPEnergy
-      HEPEnergyType const energy = ConvertSIToHEP(invLength);
-      REQUIRE(energy / 1_MeV == Approx(1));
-    }
-
-    REQUIRE(ConvertSIToHEP(6.5823e-25_s) * 1_GeV == Approx(1).epsilon(1e-4));
-
-    REQUIRE(ConvertSIToHEP(3.8938e-32 * meter * meter) * 1_GeV * 1_GeV ==
-            Approx(1).epsilon(1e-4));
-  }
-}
diff --git a/Framework/Utilities/Bit.h b/Framework/Utilities/Bit.h
deleted file mode 100644
index 1443ddf9b3a06acd6c434180ca7c103e1d5914d7..0000000000000000000000000000000000000000
--- a/Framework/Utilities/Bit.h
+++ /dev/null
@@ -1,91 +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
-
-/**
-  \author Hans Dembinski
-  \author Lukas Nellen
-  \author Darko Veberic
-  \date 27 Jan 2014
-
-  \version $Id: Bit.h 25126 2014-02-03 22:13:10Z darko $
-*/
-
-#include <exception>
-
-// #include <utl/AugerException.h>
-
-namespace corsika::utl {
-
-  namespace Bit {
-
-    template <typename T>
-    class Array {
-    public:
-      Array(T& target)
-          : fTarget(target) {}
-
-      class Bit {
-      public:
-        Bit(T& target, T mask)
-            : fTarget(target)
-            , fMask(mask) {}
-
-        operator bool() const { return fTarget & fMask; }
-
-        bool operator~() const { return !bool(*this); }
-
-        Bit& operator=(const bool value) {
-          if (value)
-            fTarget |= fMask;
-          else
-            fTarget &= ~fMask;
-          return *this;
-        }
-
-        Bit& Flip() { return *this = ~(*this); }
-
-      private:
-        T& fTarget;
-        T fMask;
-      };
-
-      Bit operator[](unsigned int position) { return Bit(fTarget, T(1) << position); }
-
-      Bit At(unsigned int position) {
-        if (position >= 8 * sizeof(T))
-          // throw std::exceptionOutOfBoundException("Running out of bits.");
-          throw std::exception("Running out of bits.");
-        return (*this)[position];
-      }
-
-      template <typename M>
-      Array& Mask(const M mask, const bool value) {
-        Bit(fTarget, mask) = value;
-        return *this;
-      }
-
-      template <typename M>
-      T Get(const M mask) {
-        return fTarget & T(mask);
-      }
-
-    private:
-      T& fTarget;
-    };
-
-  } // namespace Bit
-
-  // helper
-  template <typename T>
-  inline Bit::Array<T> AsBitArray(T& target) {
-    return Bit::Array<T>(target);
-  }
-
-} // namespace corsika::utl
diff --git a/Framework/Utilities/CMakeLists.txt b/Framework/Utilities/CMakeLists.txt
deleted file mode 100644
index 3373c01718f97a8676ca0b5c5166710d2861e68f..0000000000000000000000000000000000000000
--- a/Framework/Utilities/CMakeLists.txt
+++ /dev/null
@@ -1,118 +0,0 @@
-#
-# cfenv feature test - select implementation to use
-#
-try_compile (HAS_FEENABLEEXCEPT "${CMAKE_CURRENT_BINARY_DIR}" "${CMAKE_CURRENT_SOURCE_DIR}/try_feenableexcept.cc")
-if (HAS_FEENABLEEXCEPT)
-  set (CORSIKA_FENV "CorsikaFenvDefault.cc")
-  set_property(DIRECTORY ${CMAKE_HOME_DIRECTORY} APPEND PROPERTY COMPILE_DEFINITIONS "HAS_FEENABLEEXCEPT")
-else ()
-  if (APPLE)
-    set (CORSIKA_FENV "CorsikaFenvOSX.cc")
-  else()
-    set (CORSIKA_FENV "CorsikaFenvFallback.cc")
-  endif()
-endif ()
-
-#
-# library setup
-#
-set (
-  UTILITIES_SOURCES  
-  COMBoost.cc
-  CorsikaData.cc
-  quartic.cpp
-  ${CORSIKA_FENV})
-
-set (
-  UTILITIES_HEADERS
-  CorsikaData.h
-  COMBoost.h
-  Bit.h
-  Singleton.h
-  sgn.h
-  CorsikaFenv.h
-  MetaProgramming.h
-  quartic.h
-  )
-
-set (
-  UTILITIES_DEPENDS
-  CORSIKAgeometry
-  CORSIKAunits
-  CORSIKAlogging
-  C8::ext::boost # so far only for MetaProgramming
-  C8::ext::eigen3 # for COMboost
-  )
-
-if (TARGET cnpy)
-  LIST (APPEND
-    UTILITIES_HEADERS
-    SaveBoostHistogram.hpp
-    )
-  LIST (APPEND
-    UTILITIES_DEPENDS
-    cnpy # for SaveBoostHistogram
-    )
-endif (TARGET cnpy)
-  
-set (
-  UTILITIES_NAMESPACE
-  corsika/utl
-  )
-
-add_library (CORSIKAutilities STATIC ${UTILITIES_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAutilities ${UTILITIES_NAMESPACE} ${UTILITIES_HEADERS})
-
-set_target_properties (
-  CORSIKAutilities
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  PUBLIC_HEADER "${UTILITIES_HEADERS}"
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  CORSIKAutilities
-  ${UTILITIES_DEPENDS}
-  )
-
-target_include_directories (
-  CORSIKAutilities
-  PUBLIC
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS CORSIKAutilities
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-  PUBLIC_HEADER DESTINATION include/${UTILITIES_NAMESPACE}
-  )
-
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST(testCOMBoost)
-target_link_libraries (
-  testCOMBoost
-  CORSIKAutilities
-  CORSIKAtesting
-  )
-
-CORSIKA_ADD_TEST(testCorsikaFenv)
-target_link_libraries (
-  testCorsikaFenv
-  CORSIKAutilities
-  CORSIKAtesting
-)
-
-if (TARGET cnpy)
-  CORSIKA_ADD_TEST(testSaveBoostHistogram)
-  target_link_libraries (
-    testSaveBoostHistogram
-    CORSIKAutilities
-    CORSIKAtesting
-    )
-endif (TARGET cnpy)
diff --git a/Framework/Utilities/COMBoost.cc b/Framework/Utilities/COMBoost.cc
deleted file mode 100644
index 5bb4be9da2a2784dc70efa0a7b6b5b1233d3b3f4..0000000000000000000000000000000000000000
--- a/Framework/Utilities/COMBoost.cc
+++ /dev/null
@@ -1,62 +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/geometry/CoordinateSystem.h>
-#include <corsika/geometry/FourVector.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/COMBoost.h>
-#include <corsika/utl/sgn.h>
-#include <corsika/logging/Logging.h>
-
-#include <cmath>
-
-using namespace corsika::utl;
-using namespace corsika::units::si;
-using namespace corsika::geometry;
-
-COMBoost::COMBoost(FourVector<HEPEnergyType, Vector<hepmomentum_d>> const& Pprojectile,
-                   const HEPMassType massTarget)
-    : originalCS_{Pprojectile.GetSpaceLikeComponents().GetCoordinateSystem()}
-    , rotatedCS_{originalCS_.RotateToZ(Pprojectile.GetSpaceLikeComponents())} {
-  auto const pProjectile = Pprojectile.GetSpaceLikeComponents();
-  auto const pProjNormSquared = pProjectile.squaredNorm();
-  auto const pProjNorm = sqrt(pProjNormSquared);
-
-  auto const eProjectile = Pprojectile.GetTimeLikeComponent();
-  auto const massProjectileSquared = eProjectile * eProjectile - pProjNormSquared;
-  auto const s =
-      massTarget * massTarget + massProjectileSquared + 2 * eProjectile * massTarget;
-
-  auto const sqrtS = sqrt(s);
-  auto const sinhEta = -pProjNorm / sqrtS;
-  auto const coshEta = sqrt(1 + pProjNormSquared / s);
-
-  setBoost(coshEta, sinhEta);
-
-  C8LOG_TRACE("COMBoost (1-beta)={}, gamma={}, det={}", 1 - sinhEta / coshEta, coshEta,
-              boost_.determinant() - 1);
-}
-
-COMBoost::COMBoost(geometry::Vector<units::si::hepmomentum_d> const& momentum,
-                   units::si::HEPEnergyType mass)
-    : originalCS_{momentum.GetCoordinateSystem()}
-    , rotatedCS_{originalCS_.RotateToZ(momentum)} {
-  auto const squaredNorm = momentum.squaredNorm();
-  auto const norm = sqrt(squaredNorm);
-  auto const sinhEta = -norm / mass;
-  auto const coshEta = sqrt(1 + squaredNorm / (mass * mass));
-  setBoost(coshEta, sinhEta);
-}
-
-void COMBoost::setBoost(double coshEta, double sinhEta) {
-  boost_ << coshEta, sinhEta, sinhEta, coshEta;
-  inverseBoost_ << coshEta, -sinhEta, -sinhEta, coshEta;
-}
-
-CoordinateSystem const& COMBoost::GetRotatedCS() const { return rotatedCS_; }
diff --git a/Framework/Utilities/COMBoost.h b/Framework/Utilities/COMBoost.h
deleted file mode 100644
index 73b7c93001321dff22902477dc701aee043e9040..0000000000000000000000000000000000000000
--- a/Framework/Utilities/COMBoost.h
+++ /dev/null
@@ -1,99 +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/geometry/CoordinateSystem.h>
-#include <corsika/geometry/FourVector.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/logging/Logging.h>
-
-#include <Eigen/Dense>
-
-namespace corsika::utl {
-
-  /**
-     This utility class handles Lorentz boost between different
-     referenence frames, using FourVectors.
-   */
-
-  class COMBoost {
-    Eigen::Matrix2d boost_, inverseBoost_;
-    corsika::geometry::CoordinateSystem const &originalCS_, rotatedCS_;
-
-    void setBoost(double coshEta, double sinhEta);
-
-  public:
-    //! construct a COMBoost given four-vector of projectile and mass of target
-    COMBoost(
-        const corsika::geometry::FourVector<
-            corsika::units::si::HEPEnergyType,
-            corsika::geometry::Vector<corsika::units::si::hepmomentum_d>>& Pprojectile,
-        const corsika::units::si::HEPEnergyType massTarget);
-
-    //! construct a COMBoost to boost into the rest frame given a 3-momentum and mass
-    COMBoost(geometry::Vector<units::si::hepmomentum_d> const& momentum,
-             units::si::HEPEnergyType mass);
-
-    //! transforms a 4-momentum from lab frame to the center-of-mass frame
-    template <typename FourVector>
-    FourVector 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 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;
-    }
-
-    geometry::CoordinateSystem const& GetRotatedCS() const;
-  };
-} // namespace corsika::utl
diff --git a/Framework/Utilities/CorsikaFenv.h b/Framework/Utilities/CorsikaFenv.h
deleted file mode 100644
index 02614eceddce18d0fa94ca1a7d964386471c5eea..0000000000000000000000000000000000000000
--- a/Framework/Utilities/CorsikaFenv.h
+++ /dev/null
@@ -1,21 +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 <cfenv>
-
-/*
- * Same declaration of function as provided in GLIBC
- * Repetition allowed in the case where cfenv defines the functions already, no clash.
- */
-extern "C" {
-
-int feenableexcept(int excepts);
-int fedisableexcept(int excepts);
-}
diff --git a/Framework/Utilities/MetaProgramming.h b/Framework/Utilities/MetaProgramming.h
deleted file mode 100644
index 689357769ebf357fe6cd085a1336e71d89a4fbb0..0000000000000000000000000000000000000000
--- a/Framework/Utilities/MetaProgramming.h
+++ /dev/null
@@ -1,60 +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 <type_traits>
-
-/**
-  \file MetaProgramming.h
-
-  Tools to emulate conditional compilation of method overloads.
-
-  ```
-  #include <corsika/utl/MetaProgramming.h>
-
-  template <class T>
-  struct Foo {
-    template <class U, class = corsika::utl::enable_if<std::reference<U>>>
-    void enable_only_if_U_is_reference(U x) {}
-
-    template <class _ = T, class = corsika::utl::disable_if<std::reference<_>>>
-    void enable_only_if_T_is_not_reference() {}
-  };
-  ```
-
-  To combine traits, use boost::mp11::mp_and or boost::mp11::mp_or.
-
-  ```
-  #include <corsika/utl/MetaProgramming.h>
-  #include <boost/mp11.hpp>
-
-  template <class T>
-  struct Foo {
-    template <class U, class = corsika::utl::enable_if<
-      boost::mp11::mp_and<std::is_reference<T>, std::is_const<T>>
-    >
-    void enable_only_if_U_is_const_reference(U x) {}
-  };
-  ```
-
-  Common combined traits may be implemented in this header. For example:
-  ```
-  template <class T>
-  using is_cref = boost::mp11::mp_and<std::is_reference<T>, std::is_const<T>>;
-  ```
-}
-*/
-
-namespace corsika::utl {
-  template <class Trait>
-  using enable_if = typename std::enable_if<(Trait::value == true)>::type;
-
-  template <class Trait>
-  using disable_if = typename std::enable_if<(Trait::value == false)>::type;
-} // namespace corsika::utl
diff --git a/Framework/Utilities/quartic.cpp b/Framework/Utilities/quartic.cpp
deleted file mode 100644
index 88da8cc905fc94307d9e89104a9d47add6747702..0000000000000000000000000000000000000000
--- a/Framework/Utilities/quartic.cpp
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * (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.
- */
-
-#include <cmath>
-#include "quartic.h"
-
-//---------------------------------------------------------------------------
-// solve cubic equation x^3 + a*x^2 + b*x + c
-// x - array of size 3
-// In case 3 real roots: => x[0], x[1], x[2], return 3
-//         2 real roots: x[0], x[1],          return 2
-//         1 real root : x[0], x[1] ± i*x[2], return 1
-unsigned int solveP3(double* x, double a, double b, double c) {
-  double a2 = a * a;
-  double q = (a2 - 3 * b) / 9;
-  double r = (a * (2 * a2 - 9 * b) + 27 * c) / 54;
-  double r2 = r * r;
-  double q3 = q * q * q;
-  double A, B;
-  if (r2 < q3) {
-    double t = r / sqrt(q3);
-    if (t < -1) t = -1;
-    if (t > 1) t = 1;
-    t = acos(t);
-    a /= 3;
-    q = -2 * sqrt(q);
-    x[0] = q * cos(t / 3) - a;
-    x[1] = q * cos((t + M_2PI) / 3) - a;
-    x[2] = q * cos((t - M_2PI) / 3) - a;
-    return 3;
-  } else {
-    A = -pow(fabs(r) + sqrt(r2 - q3), 1. / 3);
-    if (r < 0) A = -A;
-    B = (0 == A ? 0 : q / A);
-
-    a /= 3;
-    x[0] = (A + B) - a;
-    x[1] = -0.5 * (A + B) - a;
-    x[2] = 0.5 * sqrt(3.) * (A - B);
-    if (fabs(x[2]) < eps) {
-      x[2] = x[1];
-      return 2;
-    }
-
-    return 1;
-  }
-}
-
-//---------------------------------------------------------------------------
-// solve quartic equation x^4 + a*x^3 + b*x^2 + c*x + d
-// Attention - this function returns dynamically allocated array. It has to be released
-// afterwards.
-DComplex* solve_quartic(double a, double b, double c, double d) {
-  double a3 = -b;
-  double b3 = a * c - 4. * d;
-  double c3 = -a * a * d - c * c + 4. * b * d;
-
-  // cubic resolvent
-  // y^3 − b*y^2 + (ac−4d)*y − a^2*d−c^2+4*b*d = 0
-
-  double x3[3];
-  unsigned int iZeroes = solveP3(x3, a3, b3, c3);
-
-  double q1, q2, p1, p2, D, sqD, y;
-
-  y = x3[0];
-  // The essence - choosing Y with maximal absolute value.
-  if (iZeroes != 1) {
-    if (fabs(x3[1]) > fabs(y)) y = x3[1];
-    if (fabs(x3[2]) > fabs(y)) y = x3[2];
-  }
-
-  // h1+h2 = y && h1*h2 = d  <=>  h^2 -y*h + d = 0    (h === q)
-
-  D = y * y - 4 * d;
-  if (fabs(D) < eps) // in other words - D==0
-  {
-    q1 = q2 = y * 0.5;
-    // g1+g2 = a && g1+g2 = b-y   <=>   g^2 - a*g + b-y = 0    (p === g)
-    D = a * a - 4 * (b - y);
-    if (fabs(D) < eps) // in other words - D==0
-      p1 = p2 = a * 0.5;
-
-    else {
-      sqD = sqrt(D);
-      p1 = (a + sqD) * 0.5;
-      p2 = (a - sqD) * 0.5;
-    }
-  } else {
-    sqD = sqrt(D);
-    q1 = (y + sqD) * 0.5;
-    q2 = (y - sqD) * 0.5;
-    // g1+g2 = a && g1*h2 + g2*h1 = c       ( && g === p )  Krammer
-    p1 = (a * q1 - c) / (q1 - q2);
-    p2 = (c - a * q2) / (q1 - q2);
-  }
-
-  DComplex* retval = new DComplex[4];
-
-  // solving quadratic eq. - x^2 + p1*x + q1 = 0
-  D = p1 * p1 - 4 * q1;
-  if (D < 0.0) {
-    retval[0].real(-p1 * 0.5);
-    retval[0].imag(sqrt(-D) * 0.5);
-    retval[1] = std::conj(retval[0]);
-  } else {
-    sqD = sqrt(D);
-    retval[0].real((-p1 + sqD) * 0.5);
-    retval[1].real((-p1 - sqD) * 0.5);
-  }
-
-  // solving quadratic eq. - x^2 + p2*x + q2 = 0
-  D = p2 * p2 - 4 * q2;
-  if (D < 0.0) {
-    retval[2].real(-p2 * 0.5);
-    retval[2].imag(sqrt(-D) * 0.5);
-    retval[3] = std::conj(retval[2]);
-  } else {
-    sqD = sqrt(D);
-    retval[2].real((-p2 + sqD) * 0.5);
-    retval[3].real((-p2 - sqD) * 0.5);
-  }
-
-  return retval;
-}
diff --git a/Framework/Utilities/quartic.h b/Framework/Utilities/quartic.h
deleted file mode 100644
index ac96afb10e57cff019344d650a1df141ad69b223..0000000000000000000000000000000000000000
--- a/Framework/Utilities/quartic.h
+++ /dev/null
@@ -1,66 +0,0 @@
-/***************************************************************************
- *   Copyright (C) 2016 by Саша Миленковић                                 *
- *   sasa.milenkovic.xyz@gmail.com                                         *
- *                                                                         *
- *   This program is free software; you can redistribute it and/or modify  *
- *   it under the terms of the GNU General Public License as published by  *
- *   the Free Software Foundation; either version 2 of the License, or     *
- *   (at your option) any later version.                                   *
- *   This program is distributed in the hope that it will be useful,       *
- *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
- *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
- *   GNU General Public License for more details.                          *
- *   ( http://www.gnu.org/licenses/gpl-3.0.en.html )                       *
- *									   *
- *   You should have received a copy of the GNU General Public License     *
- *   along with this program; if not, write to the                         *
- *   Free Software Foundation, Inc.,                                       *
- *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             *
- ***************************************************************************/
-
-#ifndef QUARTIC_H_INCLUDED
-#define QUARTIC_H_INCLUDED
-
-#include <complex>
-
-const double PI = 3.141592653589793238463L;
-const double M_2PI = 2 * PI;
-const double eps = 1e-12;
-
-typedef std::complex<double> DComplex;
-
-//---------------------------------------------------------------------------
-// useful for testing
-inline DComplex polinom_2(DComplex x, double a, double b) {
-  // Horner's scheme for x*x + a*x + b
-  return x * (x + a) + b;
-}
-
-//---------------------------------------------------------------------------
-// useful for testing
-inline DComplex polinom_3(DComplex x, double a, double b, double c) {
-  // Horner's scheme for x*x*x + a*x*x + b*x + c;
-  return x * (x * (x + a) + b) + c;
-}
-
-//---------------------------------------------------------------------------
-// useful for testing
-inline DComplex polinom_4(DComplex x, double a, double b, double c, double d) {
-  // Horner's scheme for x*x*x*x + a*x*x*x + b*x*x + c*x + d;
-  return x * (x * (x * (x + a) + b) + c) + d;
-}
-
-//---------------------------------------------------------------------------
-// x - array of size 3
-// In case 3 real roots: => x[0], x[1], x[2], return 3
-//         2 real roots: x[0], x[1],          return 2
-//         1 real root : x[0], x[1] ± i*x[2], return 1
-unsigned int solveP3(double* x, double a, double b, double c);
-
-//---------------------------------------------------------------------------
-// solve quartic equation x^4 + a*x^3 + b*x^2 + c*x + d
-// Attention - this function returns dynamically allocated array. It has to be released
-// afterwards.
-DComplex* solve_quartic(double a, double b, double c, double d);
-
-#endif // QUARTIC_H_INCLUDED
diff --git a/Framework/Utilities/testCOMBoost.cc b/Framework/Utilities/testCOMBoost.cc
deleted file mode 100644
index b6b677d23b62d21c01a1783be81d6f76cdce76b7..0000000000000000000000000000000000000000
--- a/Framework/Utilities/testCOMBoost.cc
+++ /dev/null
@@ -1,220 +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 <catch2/catch.hpp>
-
-#include <corsika/geometry/FourVector.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/COMBoost.h>
-
-#include <iostream>
-
-using namespace corsika::geometry;
-using namespace corsika::utl;
-using namespace corsika::units::si;
-using corsika::units::constants::c;
-using corsika::units::constants::cSquared;
-
-double constexpr absMargin = 1e-6;
-
-CoordinateSystem const& rootCS =
-    RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-// helper function for energy-momentum
-// relativistic energy
-auto const energy = [](HEPMassType m, Vector<hepmomentum_d> const& p) {
-  return sqrt(m * m + p.squaredNorm());
-};
-
-auto const momentum = [](HEPEnergyType E, HEPMassType m) { return sqrt(E * E - m * m); };
-
-// helper function for mandelstam-s
-auto const s = [](HEPEnergyType E, QuantityVector<hepmomentum_d> const& p) {
-  return E * E - p.squaredNorm();
-};
-
-TEST_CASE("boosts") {
-  // define target kinematics in lab frame
-  HEPMassType const targetMass = 1_GeV;
-  Vector<hepmomentum_d> pTargetLab{rootCS, {0_eV, 0_eV, 0_eV}};
-  HEPEnergyType const eTargetLab = energy(targetMass, pTargetLab);
-
-  /*
-    General tests check the interface and basic operation
-   */
-
-  SECTION("General tests") {
-
-    // define projectile kinematics in lab frame
-    HEPMassType const projectileMass = 1._GeV;
-    Vector<hepmomentum_d> pProjectileLab{rootCS, {0_GeV, 20_GeV, 0_GeV}};
-    HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab);
-    const FourVector PprojLab(eProjectileLab, pProjectileLab);
-
-    // define boost to com frame
-    COMBoost boost(PprojLab, targetMass);
-
-    // boost projecticle
-    auto const PprojCoM = boost.toCoM(PprojLab);
-
-    // boost target
-    auto const PtargCoM = boost.toCoM(FourVector(targetMass, pTargetLab));
-
-    // sum of momenta in CoM, should be 0
-    auto const sumPCoM =
-        PprojCoM.GetSpaceLikeComponents() + PtargCoM.GetSpaceLikeComponents();
-    CHECK(sumPCoM.norm() / 1_GeV == Approx(0).margin(absMargin));
-
-    // mandelstam-s should be invariant under transformation
-    CHECK(s(eProjectileLab + eTargetLab,
-            pProjectileLab.GetComponents() + pTargetLab.GetComponents()) /
-              1_GeV / 1_GeV ==
-          Approx(s(PprojCoM.GetTimeLikeComponent() + PtargCoM.GetTimeLikeComponent(),
-                   PprojCoM.GetSpaceLikeComponents().GetComponents() +
-                       PtargCoM.GetSpaceLikeComponents().GetComponents()) /
-                 1_GeV / 1_GeV));
-
-    // boost back...
-    auto const PprojBack = boost.fromCoM(PprojCoM);
-
-    // ...should yield original values before the boosts
-    CHECK(PprojBack.GetTimeLikeComponent() / PprojLab.GetTimeLikeComponent() ==
-          Approx(1));
-    CHECK(
-        (PprojBack.GetSpaceLikeComponents() - PprojLab.GetSpaceLikeComponents()).norm() /
-            PprojLab.GetSpaceLikeComponents().norm() ==
-        Approx(0).margin(absMargin));
-  }
-
-  /*
-    special case: projectile along -z
-   */
-
-  SECTION("Test boost along z-axis") {
-
-    // define projectile kinematics in lab frame
-    HEPMassType const projectileMass = 1_GeV;
-    Vector<hepmomentum_d> pProjectileLab{rootCS, {0_GeV, 0_GeV, -20_GeV}};
-    HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab);
-    const FourVector PprojLab(eProjectileLab, pProjectileLab);
-
-    auto const sqrt_s_lab =
-        sqrt(s(eProjectileLab + targetMass, pProjectileLab.GetComponents(rootCS)));
-
-    // define boost to com frame
-    COMBoost boost(PprojLab, targetMass);
-
-    // boost projecticle
-    auto const PprojCoM = boost.toCoM(PprojLab);
-    auto const a = PprojCoM.GetSpaceLikeComponents().GetComponents(boost.GetRotatedCS());
-    CHECK(a.GetX() / 1_GeV == Approx(0));
-    CHECK(a.GetY() / 1_GeV == Approx(0));
-    CHECK(a.GetZ() / (momentum(sqrt_s_lab / 2, projectileMass)) == Approx(1));
-
-    // boost target
-    auto const PtargCoM = boost.toCoM(FourVector(targetMass, pTargetLab));
-    CHECK(PtargCoM.GetTimeLikeComponent() / sqrt_s_lab == Approx(.5));
-
-    // sum of momenta in CoM, should be 0
-    auto const sumPCoM =
-        PprojCoM.GetSpaceLikeComponents() + PtargCoM.GetSpaceLikeComponents();
-    CHECK(sumPCoM.norm() / 1_GeV == Approx(0).margin(absMargin));
-  }
-
-  /*
-    special case: projectile with arbitrary direction
-   */
-
-  SECTION("Test boost along tilted axis") {
-
-    const HEPMomentumType P0 = 1_PeV;
-    double theta = 33.;
-    double phi = -10.;
-    auto momentumComponents = [](double theta, double phi, HEPMomentumType ptot) {
-      return std::make_tuple(ptot * sin(theta) * cos(phi), ptot * sin(theta) * sin(phi),
-                             -ptot * cos(theta));
-    };
-    auto const [px, py, pz] =
-        momentumComponents(theta / 180. * M_PI, phi / 180. * M_PI, P0);
-
-    // define projectile kinematics in lab frame
-    HEPMassType const projectileMass = 1_GeV;
-    Vector<hepmomentum_d> pProjectileLab(rootCS, {px, py, pz});
-    HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab);
-    const FourVector PprojLab(eProjectileLab, pProjectileLab);
-
-    // define boost to com frame
-    COMBoost boost(PprojLab, targetMass);
-
-    // boost projecticle
-    auto const PprojCoM = boost.toCoM(PprojLab);
-
-    // boost target
-    auto const PtargCoM = boost.toCoM(FourVector(targetMass, pTargetLab));
-
-    // sum of momenta in CoM, should be 0
-    auto const sumPCoM =
-        PprojCoM.GetSpaceLikeComponents() + PtargCoM.GetSpaceLikeComponents();
-    CHECK(sumPCoM.norm() / 1_GeV == Approx(0).margin(absMargin));
-  }
-
-  /*
-    test the ultra-high energy behaviour: E=ZeV
-   */
-
-  SECTION("High energy") {
-    // define projectile kinematics in lab frame
-    HEPMassType const projectileMass = 1_GeV;
-    HEPMomentumType P0 = 1_ZeV;
-    Vector<hepmomentum_d> pProjectileLab{rootCS, {0_GeV, 0_PeV, -P0}};
-    HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab);
-    const FourVector PprojLab(eProjectileLab, pProjectileLab);
-
-    // define boost to com frame
-    COMBoost boost(PprojLab, targetMass);
-
-    // boost projecticle
-    auto const PprojCoM = boost.toCoM(PprojLab);
-
-    // boost target
-    auto const PtargCoM = boost.toCoM(FourVector(targetMass, pTargetLab));
-
-    // sum of momenta in CoM, should be 0
-    auto const sumPCoM =
-        PprojCoM.GetSpaceLikeComponents() + PtargCoM.GetSpaceLikeComponents();
-    CHECK(sumPCoM.norm() / P0 == Approx(0).margin(absMargin)); // MAKE RELATIVE CHECK
-  }
-}
-
-TEST_CASE("rest frame") {
-  HEPMassType const projectileMass = 1_GeV;
-  HEPMomentumType const P0 = 1_TeV;
-  Vector<hepmomentum_d> pProjectileLab{rootCS, {0_GeV, P0, 0_GeV}};
-  HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab);
-  const FourVector PprojLab(eProjectileLab, pProjectileLab);
-
-  COMBoost boostRest(pProjectileLab, projectileMass);
-  auto const& csPrime = boostRest.GetRotatedCS();
-  FourVector const rest4Mom = boostRest.toCoM(PprojLab);
-
-  CHECK(rest4Mom.GetTimeLikeComponent() / 1_GeV == Approx(projectileMass / 1_GeV));
-  CHECK(rest4Mom.GetSpaceLikeComponents().norm() / 1_GeV == Approx(0).margin(absMargin));
-
-  FourVector const a{0_eV, Vector{csPrime, 0_eV, 5_GeV, 0_eV}};
-  FourVector const b{0_eV, Vector{rootCS, 3_GeV, 0_eV, 0_eV}};
-  auto const aLab = boostRest.fromCoM(a);
-  auto const bLab = boostRest.fromCoM(b);
-
-  CHECK(aLab.GetNorm() / a.GetNorm() == Approx(1));
-  CHECK(aLab.GetSpaceLikeComponents().GetComponents(csPrime)[1].magnitude() ==
-        Approx((5_GeV).magnitude()));
-  CHECK(bLab.GetSpaceLikeComponents().GetComponents(rootCS)[0].magnitude() ==
-        Approx((3_GeV).magnitude()));
-}
diff --git a/Framework/Utilities/testCorsikaFenv.cc b/Framework/Utilities/testCorsikaFenv.cc
deleted file mode 100644
index ebfde1043070e84f9bf79b6f5c227b6f66686a6d..0000000000000000000000000000000000000000
--- a/Framework/Utilities/testCorsikaFenv.cc
+++ /dev/null
@@ -1,26 +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/utl/CorsikaFenv.h>
-
-#include <cmath>
-#include <csignal>
-#include <iostream>
-
-extern "C" {
-static void handle_fpe(int /*signo*/) { exit(0); }
-}
-
-int main() {
-  feenableexcept(FE_ALL_EXCEPT);
-  signal(SIGFPE, handle_fpe);
-
-  std::cout << std::log(0.) << std::endl;
-
-  exit(1);
-}
\ No newline at end of file
diff --git a/MAINTAINERS.md b/MAINTAINERS.md
index 85e3786be3061bba45a7e4814bd69c0ce765e300..a57e4bfbbbff4526527819a3f2e4840c58a9b799 100644
--- a/MAINTAINERS.md
+++ b/MAINTAINERS.md
@@ -1,23 +1,25 @@
+
 Maintainers of the CORSIKA8 project are collaborators actively taking
 care of code contributions, quality control, development, and related
 discussions. 
 
-General:
+General and infrastructure:
 - Ralf Ulrich <ralf.ulrich@kit.edu>, KIT
 - Maximilian Reininghaus <maximilian.reininghaus@kit.edu>, KIT
 - Hans Dembinski <hdembins@mpi-hd.mpg.de>, Dortmund
+- Antonio Augusto Alves Junior <antonio.junior@kit.edu>, KIT
 
 High performance, GPU: 
 - Dominik Baack <dominik.baack@tu-dortmund.de>, Dortmund
 - Antonio Augusto Alves Junior <antonio.junior@kit.edu>, KIT
 - Luisa Arrabito <arrabito@in2p3.fr>, Montpellier
 
-Electromagnetic models, and infrastructure: 
+Electromagnetic models: 
 - Jean-Marco Alameddine <jean-marco.alameddine@udo.edu>, Dortmund
 - Jan Soedingrekso <jan.soedingrekso@tu-dortmund.de>, Dortmund
 - Maximilian Sackel <maximilian.sackel@udo.edu>, Dortmund
 
-Hadron models and infrastructure:
+Hadron models:
 - Felix Riehn <friehn@lip.pt>, Santiago/Lisbon
 - Anatoli Fedynitch <anatoli.fedynitch@icecube.wisc.edu> ICRR Tokyo
 
diff --git a/Main/CMakeLists.txt b/Main/CMakeLists.txt
deleted file mode 100644
index 8b137891791fe96927ad78e64b0aad7bded08bdc..0000000000000000000000000000000000000000
--- a/Main/CMakeLists.txt
+++ /dev/null
@@ -1 +0,0 @@
-
diff --git a/Processes/AnalyticProcessors/CMakeLists.txt b/Processes/AnalyticProcessors/CMakeLists.txt
deleted file mode 100644
index 874c714de11efbd3665cf30f93ed7a150ce3a527..0000000000000000000000000000000000000000
--- a/Processes/AnalyticProcessors/CMakeLists.txt
+++ /dev/null
@@ -1,58 +0,0 @@
-set (
-  MODEL_SOURCES  
-  )
-
-set (
-  MODEL_HEADERS
-  ExecTime.h
-  ExecTimeImpl.h
-  ImplBoundary.h
-  ImplContinuous.h
-  ImplDecay.h
-  ImplInteraction.h
-  ImplSecondaries.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/analytic_processors
-  )
-
-add_library (AnalyticProcessors INTERFACE)  #STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (AnalyticProcessors ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-target_link_libraries(AnalyticProcessors 
-  INTERFACE
-  CORSIKAlogging)
-
-target_include_directories (
-  AnalyticProcessors 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS AnalyticProcessors
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib 
-  )
-
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST (testExecTime
- SOURCES
- testExecTime.cc
- ${MODEL_HEADERS}
-)
-
-target_link_libraries (
-  testExecTime  
-  AnalyticProcessors
-  ExampleProcessors
-  CORSIKAtesting
-  C8::ext::eigen3
-  CORSIKAthirdparty # for catch2  
-  )
-
diff --git a/Processes/AnalyticProcessors/ExecTime.h b/Processes/AnalyticProcessors/ExecTime.h
deleted file mode 100644
index e80513ccc7d9299df0f10202c1018d32cbd0d01b..0000000000000000000000000000000000000000
--- a/Processes/AnalyticProcessors/ExecTime.h
+++ /dev/null
@@ -1,73 +0,0 @@
-/*
- * (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 <chrono>
-#include <type_traits>
-
-#include <corsika/logging/Logging.h>
-
-#include <corsika/process/BoundaryCrossingProcess.h>
-#include <corsika/process/ContinuousProcess.h>
-#include <corsika/process/DecayProcess.h>
-#include <corsika/process/InteractionProcess.h>
-#include <corsika/process/SecondariesProcess.h>
-#include <corsika/process/StackProcess.h>
-
-#include <corsika/process/analytic_processors/ImplBoundary.h>
-#include <corsika/process/analytic_processors/ImplContinuous.h>
-#include <corsika/process/analytic_processors/ImplDecay.h>
-#include <corsika/process/analytic_processors/ImplInteraction.h>
-#include <corsika/process/analytic_processors/ImplSecondaries.h>
-
-namespace corsika::process {
-  namespace analytic_processors {
-
-    ///  Time measurement of individual processes
-    /** This class allowes to log the runtime spend in all default calls to the process.
-     *  No distinction is made between individual function calls of the process, the
-     * runtime is accumulated and or avaraged without differentiation.
-     *
-     *  The class is currently only implemented for BoundaryProcess, ContinuousProcess,
-     * DecayProcess, InteractionProcess and SecondariesProcess and captures only the
-     * according functions of the base class given as template parameter. Trying to access
-     * BoundaryProcess functions with a DecayProcess as template parameter will currently
-     * give long errormessages.
-     *
-     * Inherits all functionality of the class that should be measured, this includes
-     * functions like getters and setters
-     */
-    template <typename T>
-    class ExecTime
-        : public Boundary<T, std::is_base_of<corsika::process::BoundaryCrossingProcess<
-                                                 typename T::TProcessType>,
-                                             T>::value>,
-          public Continuous<T, std::is_base_of<corsika::process::ContinuousProcess<
-                                                   typename T::TProcessType>,
-                                               T>::value>,
-          public Decay<
-              T, std::is_base_of<corsika::process::DecayProcess<typename T::TProcessType>,
-                                 T>::value>,
-          public Interaction<T, std::is_base_of<corsika::process::InteractionProcess<
-                                                    typename T::TProcessType>,
-                                                T>::value>,
-          public Secondaries<T, std::is_base_of<corsika::process::SecondariesProcess<
-                                                    typename T::TProcessType>,
-                                                T>::value> {
-      static_assert(std::is_base_of<corsika::process::_BaseProcess, T>::value,
-                    "error message");
-
-    public:
-      ~ExecTime() {
-        C8LOG_INFO("Accumulated time spend in process {} is {} µs", typeid(T).name(),
-                   this->sumTime());
-      }
-    };
-  } // namespace analytic_processors
-} // namespace corsika::process
\ No newline at end of file
diff --git a/Processes/AnalyticProcessors/ExecTimeImpl.h b/Processes/AnalyticProcessors/ExecTimeImpl.h
deleted file mode 100644
index 1dffcf1fa03608645394c51eea2d26be12a7a3a9..0000000000000000000000000000000000000000
--- a/Processes/AnalyticProcessors/ExecTimeImpl.h
+++ /dev/null
@@ -1,108 +0,0 @@
-/*
- * (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 <chrono>
-#include <type_traits>
-
-#include <corsika/logging/Logging.h>
-
-#include <corsika/process/BoundaryCrossingProcess.h>
-#include <corsika/process/ContinuousProcess.h>
-#include <corsika/process/DecayProcess.h>
-#include <corsika/process/InteractionProcess.h>
-#include <corsika/process/SecondariesProcess.h>
-#include <corsika/process/StackProcess.h>
-
-#include <corsika/process/analytic_processors/ImplBoundary.h>
-#include <corsika/process/analytic_processors/ImplContinuous.h>
-#include <corsika/process/analytic_processors/ImplDecay.h>
-#include <corsika/process/analytic_processors/ImplInteraction.h>
-#include <corsika/process/analytic_processors/ImplSecondaries.h>
-
-namespace corsika::process {
-  namespace analytic_processors {
-
-    namespace detail {
-
-      /// Process type independent functionality of the Process runtime measurement class
-      /// ExecTime
-      /** Inherits all functionality of the class that should be sampled, this includes
-       * special functions for getters and setters
-       *
-       *
-       *
-       */
-      template <typename T>
-      class ExecTimeImpl : public T {
-      private:
-        std::chrono::high_resolution_clock::time_point startTime_;
-        std::chrono::duration<double, std::micro> cumulatedTime_;
-        volatile double mean_;
-        volatile double mean2_;
-        volatile double min_;
-        volatile double max_;
-        volatile long long n_;
-
-      protected:
-      public:
-        using _T = T;
-
-        ExecTimeImpl() {
-          min_ = std::numeric_limits<double>::max();
-          cumulatedTime_ = std::chrono::duration<double, std::micro>(0);
-          max_ = 0;
-          mean_ = 0;
-          mean2_ = 0;
-          n_ = 0;
-        }
-
-        /// Starts the internal
-        inline void start() { startTime_ = std::chrono::high_resolution_clock::now(); }
-
-        /// Stops the internal timer and updates measurements
-        inline void stop() {
-          auto end = std::chrono::high_resolution_clock::now();
-          std::chrono::duration<double, std::micro> timeDiv =
-              std::chrono::duration_cast<std::chrono::duration<double, std::micro> >(
-                  end - startTime_);
-
-          this->update(timeDiv);
-        }
-
-        /// Updates the floating mean and variance as well as the global min and max of
-        /// the sampled runtimes
-        void update(std::chrono::duration<double, std::micro> timeDif) {
-
-          cumulatedTime_ += timeDif;
-          n_ = n_ + 1;
-
-          if (max_ < timeDif.count()) max_ = timeDif.count();
-
-          if (timeDif.count() < min_) min_ = timeDif.count();
-
-          double delta = timeDif.count() - mean_;
-          mean_ += delta / static_cast<double>(n_);
-
-          double delta2 = timeDif.count() - mean_;
-
-          mean2_ += delta * delta2;
-        }
-
-        inline double mean() const { return mean_; }
-        inline double min() const { return min_; }
-        inline double max() const { return max_; }
-        inline double var() const { return mean2_ / n_; }
-        inline double sumTime() const { return cumulatedTime_.count(); }
-      };
-
-    } // namespace detail
-
-  } // namespace analytic_processors
-} // namespace corsika::process
\ No newline at end of file
diff --git a/Processes/AnalyticProcessors/ImplBoundary.h b/Processes/AnalyticProcessors/ImplBoundary.h
deleted file mode 100644
index 681c7048f91c448ffb067d5870d2bce291c99bc1..0000000000000000000000000000000000000000
--- a/Processes/AnalyticProcessors/ImplBoundary.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * (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 <corsika/process/analytic_processors/ExecTimeImpl.h>
-
-#include <corsika/analytics/ClassTimer.h>
-
-namespace corsika::process {
-  namespace analytic_processors {
-
-    namespace detail {
-      template <typename T>
-      class ExecTimeImpl;
-    }
-
-    /// Base for Boundary Implementation
-    template <class T, bool TCheck>
-    class Boundary;
-
-    /// Specialisation if class is not BoundaryProcess
-    template <class T>
-    class Boundary<T, false> {};
-
-    /// Specialisation if class is a BoundaryProcess
-    template <class T>
-    class Boundary<T, true> : public detail::ExecTimeImpl<T> {
-    private:
-    public:
-      template <typename Particle, typename VTNType>
-      EProcessReturn DoBoundaryCrossing(Particle& p, VTNType const& from,
-                                        VTNType const& to) {
-
-        // Use of the ClassTimer function -> see ClassTimer for documentation
-        auto tc = corsika::analytics::ClassTimer<
-            EProcessReturn (detail::ExecTimeImpl<T>::_T::*)(Particle&, VTNType const&,
-                                                            VTNType const&),
-            &detail::ExecTimeImpl<T>::_T::template DoBoundaryCrossing<Particle, VTNType>>(
-            *this);
-
-        EProcessReturn r = tc.call(p, from, to);
-        this->update(
-            std::chrono::duration_cast<std::chrono::duration<double, std::micro>>(
-                tc.getTime()));
-        return r;
-      }
-    };
-  } // namespace analytic_processors
-} // namespace corsika::process
\ No newline at end of file
diff --git a/Processes/AnalyticProcessors/ImplContinuous.h b/Processes/AnalyticProcessors/ImplContinuous.h
deleted file mode 100644
index 06a0b9b8dbf8dd8375c46f807f2f7681b81dac2c..0000000000000000000000000000000000000000
--- a/Processes/AnalyticProcessors/ImplContinuous.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * (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 <corsika/process/ContinuousProcess.h>
-
-#include <corsika/process/analytic_processors/ExecTimeImpl.h>
-
-namespace corsika::process {
-  namespace analytic_processors {
-
-    namespace detail {
-      template <typename T>
-      class ExecTimeImpl;
-    }
-
-    /// Base for Continuous Implementation
-    template <class T, bool TCheck>
-    class Continuous;
-
-    /// Specialisation if class is not ContinuousProcess
-    template <class T>
-    class Continuous<T, false> {};
-
-    /// Specialisation if class is a ContinuousProcess
-    template <class T>
-    class Continuous<T, true> : public detail::ExecTimeImpl<T> {
-    private:
-    public:
-      template <typename Particle, typename Track>
-      EProcessReturn DoContinuous(Particle& p, Track const& t) {
-        this->start();
-        auto r = detail::ExecTimeImpl<T>::DoContinuous(p, t);
-        this->stop();
-        return r;
-      }
-
-      template <typename Particle, typename Track>
-      units::si::LengthType MaxStepLength(Particle const& p, Track const& track) const {
-        this->start();
-        auto r = T::MaxStepLength(p, track);
-        this->stop();
-        return r;
-      }
-    };
-  } // namespace analytic_processors
-} // namespace corsika::process
\ No newline at end of file
diff --git a/Processes/AnalyticProcessors/ImplDecay.h b/Processes/AnalyticProcessors/ImplDecay.h
deleted file mode 100644
index b514c835474250b0f529ad5c0c0c9c86e121a7f2..0000000000000000000000000000000000000000
--- a/Processes/AnalyticProcessors/ImplDecay.h
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * (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 <corsika/process/DecayProcess.h>
-
-#include <corsika/process/analytic_processors/ExecTimeImpl.h>
-
-namespace corsika::process {
-  namespace analytic_processors {
-
-    namespace detail {
-      template <typename T>
-      class ExecTimeImpl;
-    }
-
-    /// Base for Decay Implementation
-    template <class T, bool TCheck>
-    class Decay;
-
-    /// Specialisation if class is not DecayProcess
-    template <class T>
-    class Decay<T, false> {};
-
-    /// Specialisation if class is a DecayProcess
-    template <class T>
-    class Decay<T, true> : public detail::ExecTimeImpl<T> {
-    private:
-    public:
-      template <typename Particle>
-      EProcessReturn DoDecay(Particle& p) {
-        this->start();
-        auto r = detail::ExecTimeImpl<T>::DoDecay(p);
-        this->stop();
-        return r;
-      }
-
-      template <typename Particle>
-      corsika::units::si::TimeType GetLifetime(Particle& p) {
-        this->start();
-        auto r = T::GetLifetime(p);
-        this->stop();
-        return r;
-      }
-    };
-  } // namespace analytic_processors
-} // namespace corsika::process
\ No newline at end of file
diff --git a/Processes/AnalyticProcessors/ImplInteraction.h b/Processes/AnalyticProcessors/ImplInteraction.h
deleted file mode 100644
index 7b9977fa62ef93a022ce790759d43621e391422e..0000000000000000000000000000000000000000
--- a/Processes/AnalyticProcessors/ImplInteraction.h
+++ /dev/null
@@ -1,48 +0,0 @@
-/*
- * (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 <corsika/process/InteractionProcess.h>
-
-#include <corsika/process/analytic_processors/ExecTimeImpl.h>
-
-namespace corsika::process {
-  namespace analytic_processors {
-
-    namespace detail {
-      template <typename T>
-      class ExecTimeImpl;
-    }
-
-    template <class T, bool TCheck>
-    class Interaction;
-
-    template <class T>
-    class Interaction<T, false> {};
-
-    template <class T>
-    class Interaction<T, true> : public detail::ExecTimeImpl<T> {
-    private:
-    public:
-      template <typename Particle>
-      EProcessReturn DoInteraction(Particle& p) {
-        this->start();
-        auto r = detail::ExecTimeImpl<T>::DoInteraction(p);
-        this->stop();
-        return r;
-      }
-
-      template <typename Particle>
-      corsika::units::si::GrammageType GetInteractionLength(Particle& p) {
-        this->start();
-        auto r = T::GetInteractionLength(p);
-        this->stop();
-        return r;
-      }
-    };
-  } // namespace analytic_processors
-} // namespace corsika::process
\ No newline at end of file
diff --git a/Processes/AnalyticProcessors/ImplSecondaries.h b/Processes/AnalyticProcessors/ImplSecondaries.h
deleted file mode 100644
index 9e104a945037f5ab1ea26af61a7b3000abcb7b8b..0000000000000000000000000000000000000000
--- a/Processes/AnalyticProcessors/ImplSecondaries.h
+++ /dev/null
@@ -1,40 +0,0 @@
-/*
- * (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 <corsika/process/SecondariesProcess.h>
-
-#include <corsika/process/analytic_processors/ExecTimeImpl.h>
-
-namespace corsika::process {
-  namespace analytic_processors {
-
-    namespace detail {
-      template <typename T>
-      class ExecTimeImpl;
-    }
-
-    template <class T, bool TCheck>
-    class Secondaries;
-
-    template <class T>
-    class Secondaries<T, false> {};
-
-    template <class T>
-    class Secondaries<T, true> : public detail::ExecTimeImpl<T> {
-    private:
-    public:
-      template <typename Secondaries>
-      inline EProcessReturn DoSecondaries(Secondaries& sec) {
-        this->start();
-        auto r = detail::ExecTimeImpl<T>::DoSecondaries(sec);
-        this->stop();
-        return r;
-      }
-    };
-  } // namespace analytic_processors
-} // namespace corsika::process
\ No newline at end of file
diff --git a/Processes/CMakeLists.txt b/Processes/CMakeLists.txt
deleted file mode 100644
index 63e66a331dfd12c2f3965933db81979fb1931e8a..0000000000000000000000000000000000000000
--- a/Processes/CMakeLists.txt
+++ /dev/null
@@ -1,51 +0,0 @@
-# general
-add_subdirectory (AnalyticProcessors)
-add_subdirectory (ExampleProcessors)
-
-# tracking
-add_subdirectory (Tracking)
-add_subdirectory (TrackingLine)
-add_subdirectory (TrackingLeapFrogStraight)
-add_subdirectory (TrackingLeapFrogCurved)
-# hadron interaction models
-add_subdirectory (Sibyll)
-add_subdirectory (QGSJetII)
-if (Pythia8_FOUND)
-  add_subdirectory (Pythia)
-endif (Pythia8_FOUND)
-if (CONEX_FOUND)
-  add_subdirectory (CONEXSourceCut)
-endif (CONEX_FOUND)
-add_subdirectory (HadronicElasticModel)
-add_subdirectory (UrQMD)
-add_subdirectory (Proposal)
-
-# continuous physics
-add_subdirectory (EnergyLoss)
-add_subdirectory (LongitudinalProfile)
-add_subdirectory (TrackWriter)
-add_subdirectory (ObservationPlane)
-# stack processes
-add_subdirectory (StackInspector)
-# secondaries process
-# cuts, thinning, etc.
-add_subdirectory (ParticleCut)
-add_subdirectory (OnShellCheck)
-# meta-processes
-add_subdirectory (InteractionCounter)
-
-
-##########################################
-# add_custom_target(CORSIKAprocesses)
-add_library (CORSIKAprocesses INTERFACE)
-add_dependencies (CORSIKAprocesses ProcessSibyll)
-add_dependencies (CORSIKAprocesses ProcessProposal)
-if (Pythia8_FOUND)
-  add_dependencies (CORSIKAprocesses ProcessPythia8)
-endif (Pythia8_FOUND)
-add_dependencies (CORSIKAprocesses ProcessStackInspector)
-add_dependencies (CORSIKAprocesses ProcessTrackingLine)
-add_dependencies (CORSIKAprocesses ProcessEnergyLoss)
-add_dependencies (CORSIKAprocesses ProcessUrQMD)
-add_dependencies (CORSIKAprocesses ProcessParticleCut)
-add_dependencies (CORSIKAprocesses ProcessOnShellCheck)
diff --git a/Processes/CONEXSourceCut/CMakeLists.txt b/Processes/CONEXSourceCut/CMakeLists.txt
deleted file mode 100644
index 708046731afa5de9d172a7a3f2992a292ad18c4c..0000000000000000000000000000000000000000
--- a/Processes/CONEXSourceCut/CMakeLists.txt
+++ /dev/null
@@ -1,75 +0,0 @@
-set (
-  MODEL_SOURCES
-  CONEXSourceCut.cc
-)
-
-set (
-  MODEL_HEADERS
-  CONEXSourceCut.h
-  CONEX_f.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/conex_source_cut
-  )
-
-add_library (ProcessCONEXSourceCut STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessCONEXSourceCut ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-set_target_properties (
-  ProcessCONEXSourceCut
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessCONEXSourceCut
-  CORSIKAunits
-  CORSIKAparticles
-  CORSIKAprocesssequence
-  CORSIKAsetup
-  C8::ext::conex
-  ProcessSibyll
-  ProcessUrQMD
-  )
-
-target_include_directories (
-  ProcessCONEXSourceCut 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS ProcessCONEXSourceCut
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-  )
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST(testCONEXSourceCut SOURCES
-  testCONEXSourceCut.cc
-  ${MODEL_HEADERS}
-)
-# 
-target_compile_definitions (
-  testCONEXSourceCut
-  PRIVATE
-  REFDATADIR="${CMAKE_CURRENT_SOURCE_DIR}"
-  )
-#
-target_link_libraries (
-  testCONEXSourceCut
-  ProcessCONEXSourceCut
-  CORSIKAunits
-  CORSIKAstackinterface
-  CORSIKAprocesssequence
-  CORSIKAsetup
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAtesting
-)
diff --git a/Processes/CONEXSourceCut/CONEXSourceCut.cc b/Processes/CONEXSourceCut/CONEXSourceCut.cc
deleted file mode 100644
index d6c6e7e282599d373725b82d817e583ba9f37354..0000000000000000000000000000000000000000
--- a/Processes/CONEXSourceCut/CONEXSourceCut.cc
+++ /dev/null
@@ -1,298 +0,0 @@
-/*
- * (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.
- */
-
-#include <corsika/logging/Logging.h>
-#include <corsika/process/conex_source_cut/CONEXSourceCut.h>
-#include <corsika/process/conex_source_cut/CONEX_f.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/units/PhysicalConstants.h>
-
-#include <algorithm>
-#include <fstream>
-#include <iomanip>
-#include <utility>
-
-using namespace corsika::process::conex_source_cut;
-using namespace corsika::units::si;
-using namespace corsika::particles;
-using namespace corsika::setup;
-
-corsika::process::EProcessReturn CONEXSourceCut::DoSecondaries(
-    corsika::setup::StackView& vS) {
-  auto p = vS.begin();
-  while (p != vS.end()) {
-    Code const pid = p.GetPID();
-    if (addParticle(pid, p.GetEnergy(), p.GetMass(), p.GetPosition(),
-                    p.GetMomentum().normalized(), p.GetTime())) {
-      p.Delete();
-    }
-    ++p;
-  }
-  return corsika::process::EProcessReturn::eOk;
-}
-
-bool CONEXSourceCut::addParticle(particles::Code pid, HEPEnergyType energy,
-                                 HEPEnergyType mass, geometry::Point const& position,
-                                 geometry::Vector<dimensionless_d> 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; });
-  if (it == egs_em_codes_.cend()) { return false; }
-
-  // EM particle
-  auto const egs_pid = it->second;
-  std::cout << "position conexObs: " << position.GetCoordinates(conexObservationCS_)
-            << std::endl;
-
-  auto const coords = position.GetCoordinates(conexObservationCS_) / 1_m;
-  double const x = coords[0].magnitude();
-  double const y = coords[1].magnitude();
-
-  double const altitude = ((position - center_).norm() - conex::earthRadius) / 1_m;
-  auto const d = position - showerCore_;
-
-  // distance from core to particle projected along shower axis
-  double const slantDistance = -d.dot(showerAxis_.GetDirection()) / 1_m;
-
-  // lateral coordinates in CONEX shower frame
-  auto const dShowerPlane = d - d.parallelProjectionOnto(showerAxis_.GetDirection());
-  double const lateralX = dShowerPlane.dot(x_sf_) / 1_m;
-  double const lateralY = dShowerPlane.dot(y_sf_) / 1_m;
-
-  double const slantX = showerAxis_.projectedX(position) * (1_cm * 1_cm / 1_g);
-
-  double const time = (t * units::constants::c - groundDist_) / 1_m;
-
-  // fill u,v,w momentum direction in EGS frame
-  double const u = direction.dot(y_sf_).magnitude();
-  double const v = direction.dot(x_sf_).magnitude();
-  double const w = direction.dot(showerAxis_.GetDirection()).magnitude();
-
-  double const weight = 1; // NEEDS TO BE CHANGED WHEN WE HAVE WEIGHTS!
-
-  // generation, TO BE CHANGED WHEN WE HAVE THAT INFORMATION AVAILABLE
-  int const latchin = 1;
-
-  double const E = energy / 1_GeV;
-  double const m = mass / 1_GeV;
-
-  std::cout << "CONEXSourceCut: 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;
-
-  conex::cxoptl_.dptl[10 - 1] = egs_pid;
-  conex::cxoptl_.dptl[4 - 1] = E;
-  conex::cxoptl_.dptl[5 - 1] = m;
-  conex::cxoptl_.dptl[6 - 1] = x;
-  conex::cxoptl_.dptl[7 - 1] = y;
-  conex::cxoptl_.dptl[8 - 1] = altitude;
-  conex::cxoptl_.dptl[9 - 1] = time;
-  conex::cxoptl_.dptl[11 - 1] = weight;
-  conex::cxoptl_.dptl[12 - 1] = latchin;
-  conex::cxoptl_.dptl[13 - 1] = slantX;
-  conex::cxoptl_.dptl[14 - 1] = lateralX;
-  conex::cxoptl_.dptl[15 - 1] = lateralY;
-  conex::cxoptl_.dptl[16 - 1] = slantDistance;
-  conex::cxoptl_.dptl[2 - 1] = u;
-  conex::cxoptl_.dptl[1 - 1] = v;
-  conex::cxoptl_.dptl[3 - 1] = w;
-
-  int n = 1, i = 1;
-  conex::cegs4_(n, i);
-
-  return true;
-}
-
-void CONEXSourceCut::SolveCE() {
-
-  conex::conexcascade_();
-
-  int nX = conex::get_number_of_depth_bins_(); // make sure this works!
-
-  int icut = 1;
-  int icutg = 2;
-  int icute = 3;
-  int icutm = 2;
-  int icuth = 3;
-  int iSec = 0;
-
-  const int maxX = nX;
-
-  auto X = std::make_unique<float[]>(maxX);
-  auto H = std::make_unique<float[]>(maxX);
-  auto D = std::make_unique<float[]>(maxX);
-  auto N = std::make_unique<float[]>(maxX);
-  auto dEdX = std::make_unique<float[]>(maxX);
-  auto Mu = std::make_unique<float[]>(maxX);
-  auto dMu = std::make_unique<float[]>(maxX);
-  auto Gamma = std::make_unique<float[]>(maxX);
-  auto Electrons = std::make_unique<float[]>(maxX);
-  auto Hadrons = std::make_unique<float[]>(maxX);
-
-  float EGround[3], fitpars[13];
-
-  conex::get_shower_data_(icut, iSec, nX, X[0], N[0], fitpars[0], H[0], D[0]);
-  conex::get_shower_edep_(icut, nX, dEdX[0], EGround[0]);
-  conex::get_shower_muon_(icutm, nX, Mu[0], dMu[0]);
-  conex::get_shower_gamma_(icutg, nX, Gamma[0]);
-  conex::get_shower_electron_(icute, nX, Electrons[0]);
-  conex::get_shower_hadron_(icuth, nX, Hadrons[0]);
-
-  std::ofstream file{"conex_output.txt"};
-  file << fmt::format("#{:>8} {:>13} {:>13} {:>13} {:>13} {:>13} {:>13} {:>13}\n", "X",
-                      "N", "dEdX", "Mu", "dMu", "Gamma", "El", "Had");
-  for (int i = 0; i < nX; ++i) {
-    file << fmt::format(
-        " {:>8.2f} {:>13.3} {:>13.3} {:>13.3} {:>13.3} {:>13.3} {:>13.3} {:>13.3}\n",
-        X[i], N[i], dEdX[i], Mu[i], dMu[i], Gamma[i], Electrons[i], Hadrons[i]);
-  }
-  file.close();
-
-  std::ofstream fitout{"conex_fit.txt"};
-  fitout << fitpars[1 - 1] << " # log10(eprima/eV)" << std::endl;
-  fitout << fitpars[2 - 1] << " # theta" << std::endl;
-  fitout << fitpars[3 - 1] << " # X1 (first interaction)" << std::endl;
-  fitout << fitpars[4 - 1] << " # Nmax" << std::endl;
-  fitout << fitpars[5 - 1] << " # X0" << std::endl;
-  fitout << fitpars[6 - 1] << " # P1" << std::endl;
-  fitout << fitpars[7 - 1] << " # P2" << std::endl;
-  fitout << fitpars[8 - 1] << " # P3" << std::endl;
-  fitout << fitpars[9 - 1] << " # chi^2 / sqrt(Nmax)" << std::endl;
-  fitout << fitpars[10 - 1] << " # Xmax" << std::endl;
-  fitout << fitpars[11 - 1] << " # phi" << std::endl;
-  fitout << fitpars[12 - 1] << " # inelasticity 1st int." << std::endl;
-  fitout << fitpars[13 - 1] << " # ???" << std::endl;
-  fitout.close();
-}
-
-CONEXSourceCut::CONEXSourceCut(geometry::Point center,
-                               environment::ShowerAxis const& showerAxis,
-                               units::si::LengthType groundDist,
-                               units::si::LengthType injectionHeight,
-                               units::si::HEPEnergyType primaryEnergy,
-                               particles::PDGCode primaryPDG)
-    : center_{center}
-    , showerAxis_{showerAxis}
-    , groundDist_{groundDist}
-    , showerCore_{showerAxis_.GetStart() + showerAxis_.GetDirection() * groundDist_}
-    , conexObservationCS_{std::invoke([&]() {
-      auto const& c8cs = center.GetCoordinateSystem();
-      auto const translation = showerCore_ - center;
-      auto const intermediateCS = c8cs.translate(translation.GetComponents(c8cs));
-      auto const intermediateCS2 = intermediateCS.RotateToZ(translation);
-
-      std::cout << "translation C8/CONEX obs: " << translation.GetComponents()
-                << std::endl;
-
-      auto const transform = geometry::CoordinateSystem::GetTransformation(
-          intermediateCS2, c8cs); // either this way or vice versa... TODO: test this!
-      std::cout << transform.matrix() << std::endl << std::endl;
-      std::cout
-          << geometry::CoordinateSystem::GetTransformation(intermediateCS, c8cs).matrix()
-          << std::endl
-          << std::endl;
-      std::cout << geometry::CoordinateSystem::GetTransformation(intermediateCS2,
-                                                                 intermediateCS)
-                       .matrix()
-                << std::endl;
-
-      return geometry::CoordinateSystem(c8cs, transform);
-    })}
-    , x_sf_{std::invoke([&]() {
-      geometry::Vector<length_d> const a{conexObservationCS_, 0._m, 0._m, 1._m};
-      auto b = a.cross(showerAxis_.GetDirection());
-      auto const lengthB = b.norm();
-      if (lengthB < 1e-10_m) {
-        b = geometry::Vector<length_d>{conexObservationCS_, 1_m, 0_m, 0_m};
-      }
-
-      return b.normalized();
-    })}
-    , 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;
-
-  std::cout << "y_sf (conexObservationCS): " << y_sf_.GetComponents(conexObservationCS_)
-            << std::endl;
-  std::cout << "y_sf (C8): " << y_sf_.GetComponents(center.GetCoordinateSystem())
-            << std::endl;
-
-  std::cout << "showerAxisDirection (conexObservationCS): "
-            << showerAxis_.GetDirection().GetComponents(conexObservationCS_) << std::endl;
-  std::cout << "showerAxisDirection (C8): "
-            << showerAxis_.GetDirection().GetComponents(center.GetCoordinateSystem())
-            << std::endl;
-
-  std::cout << "showerCore (conexObservationCS): "
-            << showerCore_.GetCoordinates(conexObservationCS_) << std::endl;
-  std::cout << "showerCore (C8): "
-            << showerCore_.GetCoordinates(center.GetCoordinateSystem()) << std::endl;
-
-  int randomSeeds[3] = {1234, 0, 0}; // will be overwritten later??
-  int heModel = eSibyll23;
-
-  int nShower = 1; // large to avoid final stats.
-  int maxDetail = 0;
-#ifdef CONEX_EXTENSIONS
-  int particleListMode = 0;
-#endif
-
-  std::string configPath = CONEX_CONFIG_PATH;
-  conex::initconex_(nShower, randomSeeds, heModel, maxDetail,
-#ifdef CONEX_EXTENSIONS
-                    particleListMode,
-#endif
-                    configPath.c_str(), configPath.size());
-
-  double eprima = primaryEnergy / 1_GeV;
-
-  // set phi, theta
-  geometry::Vector<length_d> ez{conexObservationCS_, {0._m, 0._m, -1_m}};
-  auto const c = showerAxis_.GetDirection().dot(ez) / 1_m;
-  double theta = std::acos(c) * 180 / M_PI;
-
-  auto const showerAxisConex =
-      showerAxis_.GetDirection().GetComponents(conexObservationCS_);
-  double phi = std::atan2(-showerAxisConex.GetY().magnitude(),
-                          showerAxisConex.GetX().magnitude()) *
-               180 / M_PI;
-
-  std::cout << "theta (deg) = " << theta << "; phi (deg) = " << phi << std::endl;
-
-  int ipart = static_cast<int>(primaryPDG);
-  auto rng = corsika::random::RNGManager::GetInstance().GetRandomStream("cascade");
-
-  double dimpact = 0.; // valid only if shower core is fixed on the observation plane; for
-                       // skimming showers an offset is needed like in CONEX
-
-  std::array<int, 3> ioseed{static_cast<int>(rng()), static_cast<int>(rng()),
-                            static_cast<int>(rng())};
-
-  double xminp = injectionHeight / 1_m;
-
-  conex::conexrun_(ipart, eprima, theta, phi, xminp, dimpact, ioseed.data());
-}
diff --git a/Processes/CONEXSourceCut/CONEXSourceCut.h b/Processes/CONEXSourceCut/CONEXSourceCut.h
deleted file mode 100644
index b8a714666ca49342f252fbb7061701d739db7be7..0000000000000000000000000000000000000000
--- a/Processes/CONEXSourceCut/CONEXSourceCut.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * (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 <corsika/environment/ShowerAxis.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/process/SecondariesProcess.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/process/conex_source_cut/CONEX_f.h>
-
-namespace conex {
-  corsika::units::si::LengthType constexpr earthRadius{6371315 *
-                                                       corsika::units::si::meter};
-} // namespace conex
-
-namespace corsika::process {
-  namespace conex_source_cut {
-    class CONEXSourceCut : public process::SecondariesProcess<CONEXSourceCut> {
-
-    public:
-      CONEXSourceCut(geometry::Point center, environment::ShowerAxis const& showerAxis,
-                     units::si::LengthType groundDist,
-                     units::si::LengthType injectionHeight,
-                     units::si::HEPEnergyType primaryEnergy, particles::PDGCode pdg);
-      corsika::process::EProcessReturn DoSecondaries(corsika::setup::StackView&);
-
-      void SolveCE();
-
-      bool addParticle(particles::Code pid, units::si::HEPEnergyType energy,
-                       units::si::HEPEnergyType mass, geometry::Point const& position,
-                       geometry::Vector<units::si::dimensionless_d> const& direction,
-                       units::si::TimeType t);
-
-      auto const& GetObserverCS() const { return conexObservationCS_; }
-
-    private:
-      //! CONEX e.m. particle codes
-      static std::array<std::pair<particles::Code, int>, 3> constexpr egs_em_codes_{
-          {{particles::Code::Gamma, 0},
-           {particles::Code::Electron, -1},
-           {particles::Code::Positron, -1}}};
-
-      geometry::Point const center_; //!< center of CONEX Earth
-      environment::ShowerAxis const& showerAxis_;
-      units::si::LengthType groundDist_; //!< length from injection point to shower core
-      geometry::Point const showerCore_; //!< shower core
-      geometry::CoordinateSystem const conexObservationCS_; //!< CONEX observation frame
-      geometry::Vector<units::si::dimensionless_d> const x_sf_,
-          y_sf_; //!< unit vectors of CONEX shower frame, z_sf is shower axis direction
-    };
-  } // namespace conex_source_cut
-} // namespace corsika::process
diff --git a/Processes/CONEXSourceCut/conex_output_REF.txt b/Processes/CONEXSourceCut/conex_output_REF.txt
deleted file mode 100644
index fd32d4e2effa259d0b409524bc3d550522ee66a0..0000000000000000000000000000000000000000
--- a/Processes/CONEXSourceCut/conex_output_REF.txt
+++ /dev/null
@@ -1,208 +0,0 @@
-#       X             N          dEdX            Mu           dMu         Gamma            El           Had
-     0.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-    10.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-    20.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-    30.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-    40.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-    50.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-    60.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-    70.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-    80.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-    90.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   100.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   110.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   120.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   130.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   140.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   150.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   160.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   170.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   180.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   190.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   200.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   210.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   220.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   230.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   240.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   250.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   260.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   270.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   280.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   290.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   300.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   310.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   320.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   330.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   340.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   350.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   360.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   370.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   380.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   390.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   400.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   410.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   420.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   430.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   440.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   450.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   460.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   470.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   480.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   490.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   500.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   510.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   520.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   530.00          0.00          0.00          0.00          0.00          0.00          0.00          0.00
-   540.00          0.00       0.00062          0.00          0.00          1.00          0.00          0.00
-   550.00         0.512       0.00246      0.000784      0.000802          1.69         0.272       0.00518
-   560.00          1.45       0.00598       0.00326       0.00256          4.23         0.787       0.00985
-   570.00          3.23        0.0123       0.00719       0.00411          9.26          1.78        0.0145
-   580.00          6.30        0.0227        0.0126       0.00569          18.0          3.51        0.0195
-   590.00          11.2        0.0385        0.0194       0.00734          32.0          6.27        0.0248
-   600.00          18.4        0.0613        0.0278       0.00906          53.3          10.4        0.0304
-   610.00          28.6        0.0925        0.0376        0.0108          84.1          16.2        0.0364
-   620.00          42.3         0.134        0.0489        0.0125         127.0          24.1        0.0425
-   630.00          60.1         0.186        0.0615        0.0142         184.0          34.5        0.0489
-   640.00          82.5         0.251        0.0753        0.0158         258.0          47.5        0.0553
-   650.00         110.0         0.328        0.0902        0.0173         350.0          63.4        0.0618
-   660.00         142.0         0.416         0.106        0.0186         464.0          82.5        0.0681
-   670.00         180.0         0.518         0.123        0.0198         600.0         105.0        0.0744
-   680.00         223.0         0.632          0.14        0.0208         759.0         130.0        0.0803
-   690.00         270.0         0.757         0.157        0.0216         942.0         158.0         0.086
-   700.00         322.0         0.892         0.175        0.0223      1.15e+03         189.0        0.0913
-   710.00         378.0          1.04         0.193        0.0227      1.38e+03         222.0        0.0962
-   720.00         437.0          1.19          0.21         0.023      1.62e+03         257.0         0.101
-   730.00         497.0          1.34         0.227         0.023      1.89e+03         294.0         0.104
-   740.00         559.0          1.50         0.244        0.0229      2.17e+03         331.0         0.108
-   750.00         621.0          1.65          0.26        0.0227      2.46e+03         369.0         0.111
-   760.00         683.0          1.81         0.275        0.0223      2.76e+03         406.0         0.113
-   770.00         742.0          1.96         0.289        0.0218      3.06e+03         443.0         0.114
-   780.00         799.0          2.10         0.303        0.0212      3.36e+03         478.0         0.116
-   790.00         853.0          2.23         0.315        0.0205      3.66e+03         511.0         0.116
-   800.00         902.0          2.35         0.327        0.0197      3.94e+03         542.0         0.116
-   810.00         947.0          2.45         0.337        0.0189      4.21e+03         570.0         0.116
-   820.00         986.0          2.55         0.347         0.018      4.47e+03         595.0         0.115
-   830.00      1.02e+03          2.63         0.355        0.0171      4.70e+03         616.0         0.113
-   840.00      1.05e+03          2.69         0.362        0.0162      4.92e+03         634.0         0.112
-   850.00      1.07e+03          2.74         0.369        0.0153      5.10e+03         648.0         0.109
-   860.00      1.08e+03          2.77         0.374        0.0144      5.27e+03         659.0         0.107
-   870.00      1.09e+03          2.79         0.379        0.0135      5.40e+03         666.0         0.104
-   880.00      1.10e+03          2.79         0.382        0.0126      5.50e+03         669.0         0.101
-   890.00      1.09e+03          2.78         0.385        0.0117      5.58e+03         669.0        0.0983
-   900.00      1.08e+03          2.76         0.387        0.0109      5.63e+03         665.0         0.095
-   910.00      1.07e+03          2.72         0.388        0.0101      5.64e+03         658.0        0.0916
-   920.00      1.05e+03          2.67         0.388       0.00933      5.64e+03         649.0        0.0881
-   930.00      1.03e+03          2.62         0.388       0.00861      5.60e+03         636.0        0.0845
-   940.00      1.01e+03          2.55         0.388       0.00793      5.54e+03         622.0        0.0809
-   950.00         978.0          2.48         0.387       0.00729      5.46e+03         605.0        0.0773
-   960.00         947.0          2.39         0.385       0.00669      5.36e+03         587.0        0.0737
-   970.00         913.0          2.31         0.383       0.00613      5.24e+03         567.0        0.0702
-   980.00         878.0          2.22          0.38       0.00561      5.11e+03         546.0        0.0667
-   990.00         841.0          2.12         0.378       0.00512      4.96e+03         524.0        0.0633
-  1000.00         803.0          2.03         0.375       0.00467      4.80e+03         501.0        0.0599
-  1010.00         765.0          1.93         0.371       0.00426      4.63e+03         478.0        0.0566
-  1020.00         726.0          1.83         0.368       0.00387      4.45e+03         455.0        0.0535
-  1030.00         688.0          1.73         0.364       0.00352      4.26e+03         431.0        0.0504
-  1040.00         650.0          1.64          0.36        0.0032      4.07e+03         408.0        0.0475
-  1050.00         612.0          1.54         0.356        0.0029      3.88e+03         385.0        0.0447
-  1060.00         575.0          1.45         0.352       0.00263      3.69e+03         362.0         0.042
-  1070.00         539.0          1.36         0.348       0.00239      3.50e+03         340.0        0.0394
-  1080.00         504.0          1.27         0.344       0.00216      3.31e+03         319.0         0.037
-  1090.00         470.0          1.19          0.34       0.00196      3.12e+03         298.0        0.0346
-  1100.00         438.0          1.10         0.336       0.00177      2.94e+03         278.0        0.0324
-  1110.00         407.0          1.03         0.331        0.0016      2.76e+03         258.0        0.0303
-  1120.00         377.0         0.952         0.327       0.00145      2.58e+03         240.0        0.0283
-  1130.00         349.0         0.881         0.323       0.00131      2.41e+03         222.0        0.0265
-  1140.00         323.0         0.814         0.319       0.00119      2.25e+03         206.0        0.0247
-  1150.00         298.0         0.751         0.314       0.00108      2.10e+03         190.0         0.023
-  1160.00         274.0         0.692          0.31      0.000973      1.95e+03         175.0        0.0215
-  1170.00         252.0         0.636         0.306      0.000881      1.81e+03         161.0          0.02
-  1180.00         231.0         0.584         0.302      0.000798      1.67e+03         148.0        0.0186
-  1190.00         212.0         0.535         0.298      0.000722      1.55e+03         136.0        0.0173
-  1200.00         194.0          0.49         0.294      0.000655      1.43e+03         125.0        0.0161
-  1210.00         177.0         0.447          0.29      0.000593      1.32e+03         114.0         0.015
-  1220.00         161.0         0.408         0.286      0.000538      1.21e+03         104.0         0.014
-  1230.00         147.0         0.372         0.283      0.000489      1.11e+03          94.8         0.013
-  1240.00         134.0         0.338         0.279      0.000444      1.02e+03          86.3        0.0121
-  1250.00         121.0         0.307         0.275      0.000403         933.0          78.5        0.0112
-  1260.00         110.0         0.279         0.272      0.000367         853.0          71.3        0.0104
-  1270.00          99.8         0.253         0.268      0.000335         779.0          64.7        0.0097
-  1280.00          90.3         0.229         0.265      0.000305         710.0          58.6       0.00901
-  1290.00          81.6         0.207         0.262      0.000278         647.0          53.0       0.00838
-  1300.00          73.7         0.187         0.258      0.000254         588.0          47.9       0.00779
-  1310.00          66.5         0.169         0.255      0.000232         534.0          43.2       0.00724
-  1320.00          59.9         0.152         0.252      0.000212         485.0          39.0       0.00673
-  1330.00          53.9         0.137         0.249      0.000194         439.0          35.1       0.00626
-  1340.00          48.5         0.123         0.246      0.000178         397.0          31.6       0.00582
-  1350.00          43.5         0.111         0.243      0.000163         359.0          28.4       0.00541
-  1360.00          39.1        0.0995          0.24       0.00015         324.0          25.5       0.00503
-  1370.00          35.0        0.0892         0.237      0.000138         293.0          22.9       0.00469
-  1380.00          31.4          0.08         0.234      0.000127         264.0          20.5       0.00436
-  1390.00          28.1        0.0716         0.231      0.000117         237.0          18.4       0.00406
-  1400.00          25.1        0.0641         0.229      0.000108         213.0          16.4       0.00379
-  1410.00          22.4        0.0573         0.226      9.93e-05         192.0          14.7       0.00353
-  1420.00          20.0        0.0512         0.223      9.17e-05         172.0          13.1       0.00329
-  1430.00          17.9        0.0457         0.221      8.48e-05         154.0          11.7       0.00307
-  1440.00          16.0        0.0408         0.218      7.85e-05         138.0          10.4       0.00287
-  1450.00          14.2        0.0364         0.216      7.28e-05         124.0          9.29       0.00268
-  1460.00          12.7        0.0325         0.214      6.75e-05         111.0          8.27        0.0025
-  1470.00          11.3        0.0289         0.211      6.27e-05          98.9          7.36       0.00234
-  1480.00          10.0        0.0258         0.209      5.83e-05          88.3          6.54       0.00219
-  1490.00          8.93        0.0229         0.207      5.42e-05          78.7          5.81       0.00205
-  1500.00          7.94        0.0204         0.204      5.05e-05          70.2          5.16       0.00192
-  1510.00          7.05        0.0182         0.202      4.70e-05          62.5          4.58        0.0018
-  1520.00          6.27        0.0161           0.2      4.39e-05          55.7          4.06       0.00169
-  1530.00          5.57        0.0144         0.198      4.10e-05          49.5          3.60       0.00159
-  1540.00          4.95        0.0128         0.196      3.83e-05          44.0          3.18        0.0015
-  1550.00          4.40        0.0114         0.194      3.58e-05          39.1          2.82       0.00141
-  1560.00          3.91        0.0101         0.192      3.35e-05          34.7          2.49       0.00133
-  1570.00          3.47       0.00899          0.19      3.14e-05          30.8          2.20       0.00125
-  1580.00          3.09         0.008         0.188      2.94e-05          27.3          1.95       0.00118
-  1590.00          2.75       0.00712         0.186      2.76e-05          24.2          1.72       0.00112
-  1600.00          2.44       0.00635         0.185      2.59e-05          21.4          1.52       0.00106
-  1610.00          2.18       0.00566         0.183      2.44e-05          19.0          1.34         0.001
-  1620.00          1.94       0.00505         0.181      2.29e-05          16.8          1.19      0.000948
-  1630.00          1.73       0.00451         0.179      2.16e-05          14.9          1.05        0.0009
-  1640.00          1.55       0.00404         0.177      2.04e-05          13.1         0.923      0.000855
-  1650.00          1.38       0.00362         0.176      1.92e-05          11.6         0.814      0.000814
-  1660.00          1.24       0.00324         0.174      1.81e-05          10.3         0.718      0.000775
-  1670.00          1.11       0.00292         0.173      1.71e-05          9.07         0.634       0.00074
-  1680.00         0.998       0.00263         0.171      1.62e-05          8.01         0.559      0.000706
-  1690.00           0.9       0.00237         0.169      1.53e-05          7.08         0.494      0.000675
-  1700.00         0.812       0.00214         0.168      1.45e-05          6.25         0.436      0.000647
-  1710.00         0.736       0.00195         0.166      1.38e-05          5.52         0.385       0.00062
-  1720.00         0.668       0.00177         0.165      1.31e-05          4.87          0.34      0.000595
-  1730.00         0.608       0.00161         0.163      1.24e-05          4.30         0.301      0.000572
-  1740.00         0.556       0.00148         0.162      1.18e-05          3.80         0.266       0.00055
-  1750.00         0.509       0.00136         0.161      1.12e-05          3.36         0.236       0.00053
-  1760.00         0.469       0.00125         0.159      1.07e-05          2.97         0.209      0.000512
-  1770.00         0.433       0.00116         0.158      1.02e-05          2.63         0.185      0.000494
-  1780.00         0.401       0.00108         0.156      9.71e-06          2.33         0.165      0.000478
-  1790.00         0.373         0.001         0.155      9.27e-06          2.06         0.147      0.000463
-  1800.00         0.349      0.000939         0.154      8.86e-06          1.83         0.131      0.000448
-  1810.00         0.327      0.000882         0.152      8.48e-06          1.62         0.117      0.000435
-  1820.00         0.308      0.000831         0.151      8.12e-06          1.44         0.105      0.000423
-  1830.00         0.291      0.000787          0.15      7.78e-06          1.28        0.0941      0.000411
-  1840.00         0.276      0.000747         0.149      7.46e-06          1.14        0.0847        0.0004
-  1850.00         0.262      0.000712         0.147      7.16e-06          1.02        0.0764       0.00039
-  1860.00         0.251      0.000681         0.146      6.88e-06         0.914        0.0692      0.000381
-  1870.00          0.24      0.000653         0.145      6.62e-06          0.82        0.0629      0.000372
-  1880.00         0.231      0.000629         0.144      6.37e-06         0.737        0.0573      0.000364
-  1890.00         0.222      0.000607         0.143      6.14e-06         0.665        0.0524      0.000356
-  1900.00         0.215      0.000587         0.142      5.92e-06         0.601        0.0481      0.000349
-  1910.00         0.208      0.000569         0.141      5.72e-06         0.545        0.0443      0.000342
-  1920.00         0.202      0.000553         0.139      5.52e-06         0.495         0.041      0.000335
-  1930.00         0.197      0.000539         0.138      5.34e-06         0.452        0.0381      0.000329
-  1940.00         0.192      0.000526         0.137      5.17e-06         0.414        0.0355      0.000324
-  1950.00         0.188      0.000514         0.136      5.01e-06         0.381        0.0332      0.000318
-  1960.00         0.184      0.000503         0.135      4.85e-06         0.351        0.0312      0.000313
-  1970.00          0.18      0.000493         0.134      4.71e-06         0.325        0.0295      0.000309
-  1980.00         0.177      0.000484         0.133      4.57e-06         0.303        0.0279      0.000304
-  1990.00         0.174      0.000476         0.132      4.44e-06         0.283        0.0265        0.0003
-  2000.00         0.171      0.000469         0.131      4.32e-06         0.265        0.0252      0.000296
-  2010.00         0.168      0.000461          0.13      4.20e-06         0.249        0.0241      0.000292
-  2020.00         0.166      0.000455         0.129      4.10e-06         0.235        0.0232      0.000289
-  2030.00         0.164      0.000449         0.128      3.99e-06         0.223        0.0223      0.000285
-  2040.00         0.162      0.000443         0.127      3.89e-06         0.212        0.0215      0.000282
-  2050.00          0.16      0.000437         0.127      3.80e-06         0.202        0.0208      0.000279
-  2060.00         0.158          0.00         0.126      3.71e-06         0.193        0.0201      0.000276
diff --git a/Processes/CONEXSourceCut/testCONEXSourceCut.cc b/Processes/CONEXSourceCut/testCONEXSourceCut.cc
deleted file mode 100644
index 4c17d7fb419fa00a55a13b8d21aaa198a11ba05f..0000000000000000000000000000000000000000
--- a/Processes/CONEXSourceCut/testCONEXSourceCut.cc
+++ /dev/null
@@ -1,149 +0,0 @@
-/*
- * (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.
- */
-
-#include <corsika/setup/SetupEnvironment.h>
-
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/LayeredSphericalAtmosphereBuilder.h>
-#include <corsika/environment/MediumPropertyModel.h>
-#include <corsika/environment/UniformMagneticField.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-
-#include <corsika/particles/ParticleProperties.h>
-
-#include <corsika/process/conex_source_cut/CONEXSourceCut.h>
-#include <corsika/process/sibyll/Interaction.h>
-#include <corsika/process/sibyll/NuclearInteraction.h>
-
-#include <corsika/random/RNGManager.h>
-
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/CorsikaFenv.h>
-
-#include <catch2/catch.hpp>
-
-using namespace corsika;
-using namespace corsika::environment;
-using namespace corsika::geometry;
-using namespace corsika::units::si;
-
-const std::string refDataDir = std::string(REFDATADIR); // from cmake
-
-template <typename T>
-using MExtraEnvirnoment =
-    environment::MediumPropertyModel<environment::UniformMagneticField<T>>;
-
-TEST_CASE("CONEXSourceCut") {
-  random::RNGManager::GetInstance().RegisterRandomStream("cascade");
-  random::RNGManager::GetInstance().RegisterRandomStream("sibyll");
-
-  feenableexcept(FE_INVALID);
-
-  // setup environment, geometry
-  setup::Environment env;
-  const CoordinateSystem& rootCS = env.GetCoordinateSystem();
-  Point const center{rootCS, 0_m, 0_m, 0_m};
-
-  auto builder = environment::make_layered_spherical_atmosphere_builder<
-      setup::EnvironmentInterface,
-      MExtraEnvirnoment>::create(center, conex::earthRadius,
-                                 environment::Medium::AirDry1Atm,
-                                 geometry::Vector{rootCS, 0_T, 50_mT, 0_T});
-
-  builder.setNuclearComposition(
-      {{particles::Code::Nitrogen, particles::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);
-  builder.assemble(env);
-
-  const HEPEnergyType E0 = 1_PeV;
-  double thetaDeg = 60.;
-  auto const thetaRad = thetaDeg / 180. * M_PI;
-
-  auto const observationHeight = 1.4_km + conex::earthRadius;
-  auto const injectionHeight = 112.75_km + conex::earthRadius;
-  auto const t = -observationHeight * cos(thetaRad) +
-                 sqrt(-units::static_pow<2>(sin(thetaRad) * observationHeight) +
-                      units::static_pow<2>(injectionHeight));
-  Point const showerCore{rootCS, 0_m, 0_m, observationHeight};
-  Point const injectionPos =
-      showerCore +
-      Vector<dimensionless_d>{rootCS, {-sin(thetaRad), 0, cos(thetaRad)}} * t;
-
-  environment::ShowerAxis const showerAxis{injectionPos,
-                                           (showerCore - injectionPos) * 1.02, env};
-
-  // need to initialize Sibyll, done in constructor:
-  process::sibyll::Interaction sibyll;
-  [[maybe_unused]] process::sibyll::NuclearInteraction sibyllNuc(sibyll, env);
-
-  corsika::process::conex_source_cut::CONEXSourceCut conex(
-      center, showerAxis, t, injectionHeight, E0,
-      particles::GetPDG(particles::Code::Proton));
-
-  HEPEnergyType const Eem{1_PeV};
-  auto const momentum = showerAxis.GetDirection() * Eem;
-
-  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;
-
-  conex.addParticle(particles::Code::Proton, Eem, 0_eV, emPosition, momentum.normalized(),
-                    0_s);
-  // supperimpose a photon
-  auto const momentumPhoton = showerAxis.GetDirection() * 1_TeV;
-  conex.addParticle(particles::Code::Gamma, 1_TeV, 0_eV, emPosition,
-                    momentumPhoton.normalized(), 0_s);
-  conex.SolveCE();
-}
-
-#include <algorithm>
-#include <iterator>
-#include <string>
-#include <fstream>
-
-TEST_CASE("ConexOutput", "[output validation]") {
-
-  auto file = GENERATE(as<std::string>{}, "conex_fit", "conex_output");
-
-  SECTION(std::string("check saved data, ") + file + ".txt") {
-
-    // compare to binary reference data
-    std::ifstream file1(file + ".txt");
-    std::ifstream file1ref(refDataDir + "/" + file + "_REF.txt");
-
-    std::istreambuf_iterator<char> begin1(file1);
-    std::istreambuf_iterator<char> begin1ref(file1ref);
-
-    std::istreambuf_iterator<char> end;
-
-    while (begin1 != end && begin1ref != end) {
-      CHECK(*begin1 == *begin1ref);
-      ++begin1;
-      ++begin1ref;
-    }
-    CHECK(begin1 == end);
-    CHECK(begin1ref == end);
-    file1.close();
-    file1ref.close();
-  }
-}
diff --git a/Processes/EnergyLoss/CMakeLists.txt b/Processes/EnergyLoss/CMakeLists.txt
deleted file mode 100644
index 3df3978d5fdb4a23b3548e5c68b8a55f6c11f4d8..0000000000000000000000000000000000000000
--- a/Processes/EnergyLoss/CMakeLists.txt
+++ /dev/null
@@ -1,63 +0,0 @@
-set (
-  MODEL_SOURCES
-  EnergyLoss.cc
-  )
-
-set (
-  MODEL_HEADERS
-  EnergyLoss.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/energy_loss
-  )
-
-add_library (ProcessEnergyLoss STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessEnergyLoss ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-set_target_properties (
-  ProcessEnergyLoss
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-#  PUBLIC_HEADER "${MODEL_HEADERS}"
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessEnergyLoss
-  CORSIKAunits
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAsetup
-  CORSIKAlogging
-  )
-
-target_include_directories (
-  ProcessEnergyLoss 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS ProcessEnergyLoss
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-#  PUBLIC_HEADER DESTINATION include/${MODEL_NAMESPACE}
-  )
-
-
-# --------------------
-# code unit testing
-#CORSIKA_ADD_TEST (testNullModel testNullModel.cc)
-#target_link_libraries (
-#  testNullModel  ProcessNullModel
-#  CORSIKAsetup
-#  CORSIKAgeometry
-#  CORSIKAunits
-#  CORSIKAthirdparty # for catch2
-#  )
-
diff --git a/Processes/EnergyLoss/EnergyLoss.cc b/Processes/EnergyLoss/EnergyLoss.cc
deleted file mode 100644
index 66c8a76e24858c08431c15a8a5c4995c72096fce..0000000000000000000000000000000000000000
--- a/Processes/EnergyLoss/EnergyLoss.cc
+++ /dev/null
@@ -1,279 +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/process/energy_loss/EnergyLoss.h>
-
-#include <corsika/particles/ParticleProperties.h>
-
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <corsika/logging/Logging.h>
-
-#include <corsika/geometry/Line.h>
-
-#include <cmath>
-#include <fstream>
-#include <iostream>
-#include <limits>
-#include <numeric>
-
-using namespace std;
-
-using namespace corsika;
-using namespace corsika::units::si;
-using SetupParticle = corsika::setup::Stack::ParticleType;
-using SetupTrack = corsika::setup::Trajectory;
-
-using namespace corsika::process::energy_loss;
-
-EnergyLoss::EnergyLoss(environment::ShowerAxis const& shower_axis,
-                       corsika::units::si::HEPEnergyType emCut)
-    : shower_axis_(shower_axis)
-    , emCut_(emCut)
-    , profile_(int(shower_axis.maximumX() / dX_) + 1) {}
-
-auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) {
-  return sqrt((Elab - m) * (Elab + m));
-};
-
-/**
- *   PDG2018, passage of particles through matter
- *
- * Note, that \f$I_{\mathrm{eff}}\f$ of composite media a determined from \f$ \ln I =
- * \sum_i a_i \ln(I_i) \f$ where \f$ a_i \f$ is the fraction of the electron population
- * (\f$\sim Z_i\f$) of the \f$i\f$-th element. This can also be used for shell
- * corrections or density effects.
- *
- * The \f$I_{\mathrm{eff}}\f$ of compounds is not better than a few percent, if not
- * measured explicitly.
- *
- * For shell correction, see Sec 6 of https://www.nap.edu/read/20066/chapter/8#115
- *
- */
-HEPEnergyType EnergyLoss::BetheBloch(SetupParticle const& p, GrammageType const dX) {
-
-  // all these are material constants and have to come through Environment
-  // right now: values for nitrogen_D
-  // 7 nitrogen_gas 82.0 0.49976 D E 0.0011653 0.0 1.7378 4.1323 0.15349 3.2125 10.54
-  auto Ieff = 82.0_eV;
-  [[maybe_unused]] auto Zmat = 7;
-  auto ZoverA = 0.49976_mol / 1_g;
-  const double x0 = 1.7378;
-  const double x1 = 4.1323;
-  const double Cbar = 10.54;
-  const double delta0 = 0.0;
-  const double aa = 0.15349;
-  const double sk = 3.2125;
-  // end of material constants
-
-  // this is the Bethe-Bloch coefficiet 4pi N_A r_e^2 m_e c^2
-  auto constexpr K = 0.307075_MeV / 1_mol * square(1_cm);
-  HEPEnergyType const E = p.GetEnergy();
-  HEPMassType const m = p.GetMass();
-  double const gamma = E / m;
-  int const Z = p.GetChargeNumber();
-  int const Z2 = Z * Z;
-  HEPMassType constexpr me = particles::Electron::GetMass();
-  auto const m2 = m * m;
-  auto constexpr me2 = me * me;
-  double const gamma2 = gamma * gamma;
-
-  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
-  C8LOG_DEBUG("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
-  C8LOG_DEBUG("BetheBloch Wmax={}", Wmax);
-
-  // Sternheimer parameterization, density corrections towards high energies
-  // NOTE/TODO: when Cbar is 0 it needs to be approximated from parameterization ->
-  // MISSING
-  C8LOG_DEBUG("BetheBloch p.GetMomentum().GetNorm()/m{}=", p.GetMomentum().GetNorm() / m);
-  double const x = log10(p.GetMomentum().GetNorm() / m);
-  double delta = 0;
-  if (x >= x1) {
-    delta = 2 * (log(10)) * x - Cbar;
-  } else if (x < x1 && x >= x0) {
-    delta = 2 * (log(10)) * x - Cbar + aa * pow((x1 - x), sk);
-  } else if (x < x0) { // and IF conductor (otherwise, this is 0)
-    delta = delta0 * pow(100, 2 * (x - x0));
-  }
-  C8LOG_DEBUG("BetheBloch delta={}", delta);
-
-  // with further low energies correction, accurary ~1% down to beta~0.05 (1MeV for p)
-
-  // shell correction, <~100MeV
-  // need more clarity about formulas and units
-  const double Cadj = 0;
-  /*
-  // https://www.nap.edu/read/20066/chapter/8#104
-  HEPEnergyType Iadj = 12_eV * Z + 7_eV;  // Iadj<163eV
-  if (Iadj>=163_eV)
-    Iadj = 9.76_eV * Z + 58.8_eV * pow(Z, -0.19);  // Iadj>=163eV
-  double const Cadj = (0.422377/eta2 + 0.0304043/(eta2*eta2) -
-  0.00038106/(eta2*eta2*eta2)) * 1e-6 * Iadj*Iadj + (3.858019/eta2 -
-  0.1667989/(eta2*eta2) + 0.00157955/(eta2*eta2*eta2)) * 1e-9 * Iadj*Iadj*Iadj;
-  */
-
-  // Barkas correction O(Z3) higher-order Born approximation
-  // see Appl. Phys. 85 (1999) 1249
-  // double A = 1;
-  // if (p.GetPID() == particles::Code::Nucleus) A = p.GetNuclearA();
-  // double const Erel = (p.GetEnergy()-p.GetMass()) / A / 1_keV;
-  // double const Llow = 0.01 * Erel;
-  // double const Lhigh = 1.5/pow(Erel, 0.4) + 45000./Zmat * pow(Erel, 1.6);
-  // double const barkas = Z * Llow*Lhigh/(Llow+Lhigh); // RU, I think the Z was
-  // missing...
-  double const barkas = 1; // does not work yet
-
-  // Bloch correction for O(Z4) higher-order Born approximation
-  // see Appl. Phys. 85 (1999) 1249
-  const double alpha = 1. / 137.035999173;
-  double const y2 = Z * Z * alpha * alpha / beta2;
-  double const bloch = -y2 * (1.202 - y2 * (1.042 - 0.855 * y2 + 0.343 * y2 * y2));
-
-  double const aux = 2 * me * c2 * beta2 * gamma2 * Wmax / (Ieff * Ieff);
-  return -K * Z2 * ZoverA / beta2 *
-         (0.5 * log(aux) - beta2 - Cadj / Z - delta / 2 + barkas + bloch) * dX;
-}
-
-// radiation losses according to PDG 2018, ch. 33 ref. [5]
-HEPEnergyType EnergyLoss::RadiationLosses(SetupParticle 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 EnergyLoss::TotalEnergyLoss(SetupParticle const& vP,
-                                          GrammageType const vDX) {
-  return BetheBloch(vP, vDX) + RadiationLosses(vP, vDX);
-}
-
-process::EProcessReturn EnergyLoss::DoContinuous(SetupParticle& p, SetupTrack const& t) {
-  if (p.GetChargeNumber() == 0) return process::EProcessReturn::eOk;
-
-  GrammageType const dX =
-      p.GetNode()->GetModelProperties().IntegratedGrammage(t, t.GetLength());
-  C8LOG_DEBUG("EnergyLoss pid={}, z={}, dX={} g/cm2", p.GetPID(), p.GetChargeNumber(),
-              dX / 1_g * square(1_cm));
-  HEPEnergyType dE = TotalEnergyLoss(p, dX);
-  auto E = p.GetEnergy();
-  [[maybe_unused]] const auto Ekin = E - p.GetMass();
-  auto Enew = E + dE;
-  C8LOG_DEBUG("EnergyLoss  dE={} MeV, E={} GeV, Ekin={} GeV, Enew={} GeV", dE / 1_MeV,
-              E / 1_GeV, Ekin / 1_GeV, Enew / 1_GeV);
-  p.SetEnergy(Enew);
-  MomentumUpdate(p, Enew);
-  FillProfile(t, dE);
-  return process::EProcessReturn::eOk;
-}
-
-LengthType EnergyLoss::MaxStepLength(SetupParticle const& vParticle,
-                                     SetupTrack const& vTrack) const {
-  if (vParticle.GetChargeNumber() == 0) {
-    return units::si::meter * std::numeric_limits<double>::infinity();
-  }
-
-  auto constexpr dX = 1_g / square(1_cm);
-  auto const dEdX = -TotalEnergyLoss(vParticle, dX) / dX; // dE > 0
-  //~ auto const Ekin = vParticle.GetEnergy() - vParticle.GetMass();
-
-  // 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;
-
-  return vParticle.GetNode()->GetModelProperties().ArclengthFromGrammage(vTrack,
-                                                                         maxGrammage);
-}
-
-void EnergyLoss::MomentumUpdate(corsika::setup::Stack::ParticleType& vP,
-                                corsika::units::si::HEPEnergyType Enew) {
-  HEPMomentumType Pnew = elab2plab(Enew, vP.GetMass());
-  auto pnew = vP.GetMomentum();
-  vP.SetMomentum(pnew * Pnew / pnew.GetNorm());
-}
-
-void EnergyLoss::FillProfile(SetupTrack const& vTrack, const HEPEnergyType dE) {
-
-  GrammageType const grammageStart = shower_axis_.projectedX(vTrack.GetPosition(0));
-  GrammageType const grammageEnd = shower_axis_.projectedX(vTrack.GetPosition(1));
-  const auto deltaX = grammageEnd - grammageStart;
-
-  int binStart = grammageStart / dX_;
-  if (binStart < 0) return;
-  int binEnd = grammageEnd / dX_;
-  if (binEnd > int(profile_.size() - 1)) return;
-  if (deltaX < dX_threshold_) return;
-
-  C8LOG_DEBUG("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;
-    profile_[bin] += increment;
-    energyCount += increment;
-
-    C8LOG_DEBUG("filling bin {} with weight {} : {} ", bin, weight, increment);
-  };
-
-  // fill longitudinal profile
-  if (binStart == binEnd) {
-    fill(binStart, 1);
-  } else {
-    fill(binStart, ((1 + binStart) * dX_ - grammageStart) / deltaX);
-    fill(binEnd, (grammageEnd - binEnd * dX_) / deltaX);
-    for (int bin = binStart + 1; bin < binEnd; ++bin) { fill(bin, 1); }
-  }
-
-  C8LOG_DEBUG("total energy added to histogram: {} ", energyCount);
-}
-
-void EnergyLoss::PrintProfile() const {
-  std::ofstream file("EnergyLossProfile.dat");
-  file << "# EnergyLoss profile" << std::endl
-       << "# lower X bin edge [g/cm2]  dE/dX [GeV/g/cm2]" << endl;
-  double const deltaX = dX_ / 1_g * square(1_cm);
-  for (size_t i = 0; i < profile_.size(); ++i) {
-    file << std::scientific << std::setw(15) << i * deltaX << std::setw(15)
-         << profile_.at(i) / (deltaX * 1_GeV) << endl;
-  }
-}
-
-HEPEnergyType EnergyLoss::GetTotal() const {
-  return std::accumulate(profile_.cbegin(), profile_.cend(), HEPEnergyType::zero());
-}
-
-void EnergyLoss::showResults() const {
-  using namespace corsika::units::si; // required for operator::_MeV
-  std::cout << " ******************************" << std::endl
-            << " PROCESS::ContinuousProcess: " << std::endl;
-  std::cout << " energy lost dE (GeV)      :  " << energy_lost_ / 1_GeV << std::endl;
-}
-
-void EnergyLoss::reset() {
-  using namespace corsika::units::si; // required for operator::_MeV
-  energy_lost_ = 0_GeV;
-}
diff --git a/Processes/EnergyLoss/EnergyLoss.h b/Processes/EnergyLoss/EnergyLoss.h
deleted file mode 100644
index f60e16b358f2dbf9671009b5c0c4eef1eeaae9a4..0000000000000000000000000000000000000000
--- a/Processes/EnergyLoss/EnergyLoss.h
+++ /dev/null
@@ -1,71 +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/environment/ShowerAxis.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/process/ContinuousProcess.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <map>
-
-namespace corsika::process::energy_loss {
-
-  class EnergyLoss : public corsika::process::ContinuousProcess<EnergyLoss> {
-
-    using MeVgcm2 = decltype(1e6 * units::si::electronvolt / units::si::gram *
-                             units::si::square(1e-2 * units::si::meter));
-
-    void MomentumUpdate(setup::Stack::ParticleType&, units::si::HEPEnergyType Enew);
-
-  public:
-    EnergyLoss(environment::ShowerAxis const& showerAxis,
-               corsika::units::si::HEPEnergyType emCut);
-
-    process::EProcessReturn DoContinuous(setup::Stack::ParticleType&,
-                                         setup::Trajectory const&);
-    units::si::LengthType MaxStepLength(setup::Stack::ParticleType const&,
-                                        setup::Trajectory const&) const;
-    units::si::HEPEnergyType GetTotal() const;
-    void PrintProfile() const;
-    static units::si::HEPEnergyType BetheBloch(setup::Stack::ParticleType const&,
-                                               const units::si::GrammageType);
-    static units::si::HEPEnergyType RadiationLosses(setup::Stack::ParticleType const&,
-                                                    const units::si::GrammageType);
-    static units::si::HEPEnergyType TotalEnergyLoss(setup::Stack::ParticleType const&,
-                                                    const units::si::GrammageType);
-
-    void showResults() const;
-    void reset();
-    corsika::units::si::HEPEnergyType energyLost() const { return energy_lost_; }
-
-  private:
-    void FillProfile(setup::Trajectory const&, units::si::HEPEnergyType);
-
-    units::si::GrammageType const dX_ = std::invoke([]() {
-      using namespace units::si;
-      return 10_g / square(1_cm);
-    }); // profile binning
-
-  private:
-    environment::ShowerAxis const& shower_axis_;
-    corsika::units::si::HEPEnergyType emCut_;
-    std::vector<units::si::HEPEnergyType> profile_; // longitudinal profile
-    units::si::HEPEnergyType energy_lost_ = 0 * units::si::electronvolt;
-  };
-
-  units::si::GrammageType const dX_threshold_ = std::invoke([]() {
-    using namespace units::si;
-    return 0.0001_g / square(1_cm);
-  });
-} // namespace corsika::process::energy_loss
diff --git a/Processes/ExampleProcessors/CMakeLists.txt b/Processes/ExampleProcessors/CMakeLists.txt
deleted file mode 100644
index c8ccedeaa9e96eedc69e1842d9f3d0adb0ce3a85..0000000000000000000000000000000000000000
--- a/Processes/ExampleProcessors/CMakeLists.txt
+++ /dev/null
@@ -1,47 +0,0 @@
-set (
-  ExampleProcessors_HEADERS
-  DummyBoundaryCrossingProcess.h
-  DummyContinuousProcess.h
-  DummyDecayProcess.h
-  DummyInteractionProcess.h
-  DummySecondariesProcess.h  
-  )
-
-set (
-  ExampleProcessors_NAMESPACE
-  corsika/process/example_processors
-  )
-
-add_library (ExampleProcessors INTERFACE)
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ExampleProcessors ${ExampleProcessors_NAMESPACE} ${ExampleProcessors_HEADERS})
-
-
-target_include_directories (
-  ExampleProcessors 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  FILES ${ExampleProcessors_HEADERS}
-  DESTINATION include/${ExampleProcessors_NAMESPACE}
-  )
-
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST (TestDummy)
-
-
-target_link_libraries (
-  TestDummy  
-  ExampleProcessors
-  CORSIKAunits
-  CORSIKAstackinterface
-  CORSIKAprocesssequence
-  CORSIKAsetup
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAtesting
-  )
diff --git a/Processes/ExampleProcessors/DummyBoundaryCrossingProcess.h b/Processes/ExampleProcessors/DummyBoundaryCrossingProcess.h
deleted file mode 100644
index 235b86996daa32fbf763766f287f9936f8097a1b..0000000000000000000000000000000000000000
--- a/Processes/ExampleProcessors/DummyBoundaryCrossingProcess.h
+++ /dev/null
@@ -1,33 +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/process/BoundaryCrossingProcess.h>
-
-#include <chrono>
-#include <thread>
-
-namespace corsika::process {
-  namespace example_processors {
-
-    template <int ISleep>
-    class DummyBoundaryCrossingProcess
-        : public BoundaryCrossingProcess<DummyBoundaryCrossingProcess<ISleep>> {
-    private:
-    protected:
-    public:
-      template <typename Particle, typename VTNType>
-      EProcessReturn DoBoundaryCrossing(Particle&, VTNType const&, VTNType const&) {
-        std::this_thread::sleep_for(std::chrono::milliseconds(ISleep));
-        return EProcessReturn::eOk;
-      }
-    };
-
-  } // namespace example_processors
-} // namespace corsika::process
\ No newline at end of file
diff --git a/Processes/ExampleProcessors/DummyContinuousProcess.h b/Processes/ExampleProcessors/DummyContinuousProcess.h
deleted file mode 100644
index 85cf237692a528fec3e1fc94155380ff5c745b8b..0000000000000000000000000000000000000000
--- a/Processes/ExampleProcessors/DummyContinuousProcess.h
+++ /dev/null
@@ -1,40 +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/process/ContinuousProcess.h>
-
-#include <chrono>
-#include <thread>
-
-namespace corsika::process {
-  namespace example_processors {
-
-    template <int ISleep>
-    class DummyContinuousProcess
-        : public ContinuousProcess<DummyContinuousProcess<ISleep>> {
-    private:
-    public:
-      template <typename Particle, typename Track>
-      inline EProcessReturn DoContinuous(Particle&, Track const&) const {
-        std::this_thread::sleep_for(std::chrono::milliseconds(ISleep));
-        return process::EProcessReturn::eOk;
-      }
-
-      template <typename Particle, typename Track>
-      inline units::si::LengthType MaxStepLength(Particle const&, Track const&) const {
-        std::this_thread::sleep_for(std::chrono::milliseconds(ISleep));
-        return units::si::meter * std::numeric_limits<double>::infinity();
-      }
-
-      std::string name() { return "DummyContinuousProcess"; }
-    };
-
-  } // namespace example_processors
-} // namespace corsika::process
\ No newline at end of file
diff --git a/Processes/ExampleProcessors/DummyDecayProcess.h b/Processes/ExampleProcessors/DummyDecayProcess.h
deleted file mode 100644
index b2546e5299f35c1017d360cfcbcc1d41b4c913f8..0000000000000000000000000000000000000000
--- a/Processes/ExampleProcessors/DummyDecayProcess.h
+++ /dev/null
@@ -1,40 +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/process/DecayProcess.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <chrono>
-#include <thread>
-
-namespace corsika::process {
-  namespace example_processors {
-
-    template <int ISleep>
-    class DummyDecayProcess : public DecayProcess<DummyDecayProcess<ISleep>> {
-    private:
-    public:
-      template <typename Particle>
-      EProcessReturn DoDecay(Particle&) {
-        std::this_thread::sleep_for(std::chrono::milliseconds(ISleep));
-        return process::EProcessReturn::eOk;
-      }
-
-      template <typename Particle>
-      corsika::units::si::TimeType GetLifetime(Particle&) {
-        using namespace corsika::units::si;
-
-        std::this_thread::sleep_for(std::chrono::milliseconds(ISleep));
-        return std::numeric_limits<double>::infinity() * 1_s;
-      }
-    };
-
-  } // namespace example_processors
-} // namespace corsika::process
\ No newline at end of file
diff --git a/Processes/ExampleProcessors/DummyInteractionProcess.h b/Processes/ExampleProcessors/DummyInteractionProcess.h
deleted file mode 100644
index 3bda555d7aa5336b60caa18de6683bdaac3b84bc..0000000000000000000000000000000000000000
--- a/Processes/ExampleProcessors/DummyInteractionProcess.h
+++ /dev/null
@@ -1,40 +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/process/InteractionProcess.h>
-
-#include <chrono>
-#include <thread>
-
-namespace corsika::process {
-  namespace example_processors {
-
-    template <int ISleep>
-    class DummyInteractionProcess
-        : public InteractionProcess<DummyInteractionProcess<ISleep> > {
-    private:
-    public:
-      template <typename Particle>
-      EProcessReturn DoInteraction(Particle&) {
-        std::this_thread::sleep_for(std::chrono::milliseconds(ISleep));
-        return process::EProcessReturn::eOk;
-      }
-
-      template <typename TParticle>
-      corsika::units::si::GrammageType GetInteractionLength(TParticle&) {
-        using namespace corsika::units::si;
-
-        std::this_thread::sleep_for(std::chrono::milliseconds(ISleep));
-        return std::numeric_limits<double>::infinity() * (1_g / 1_cm / 1_cm);
-      }
-    };
-
-  } // namespace example_processors
-} // namespace corsika::process
\ No newline at end of file
diff --git a/Processes/ExampleProcessors/DummySecondariesProcess.h b/Processes/ExampleProcessors/DummySecondariesProcess.h
deleted file mode 100644
index 30cdab881fad98c891368f795dba9c15d347328f..0000000000000000000000000000000000000000
--- a/Processes/ExampleProcessors/DummySecondariesProcess.h
+++ /dev/null
@@ -1,32 +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/process/SecondariesProcess.h>
-
-#include <chrono>
-#include <thread>
-
-namespace corsika::process {
-  namespace example_processors {
-
-    template <int ISleep>
-    class DummySecondariesProcess
-        : public SecondariesProcess<DummySecondariesProcess<ISleep>> {
-    private:
-    public:
-      template <typename TSecondaries>
-      inline EProcessReturn DoSecondaries(TSecondaries&) {
-        std::this_thread::sleep_for(std::chrono::milliseconds(ISleep));
-        return process::EProcessReturn::eOk;
-      }
-    };
-
-  } // namespace example_processors
-} // namespace corsika::process
\ No newline at end of file
diff --git a/Processes/ExampleProcessors/TestDummy.cc b/Processes/ExampleProcessors/TestDummy.cc
deleted file mode 100644
index 0781310d0b48d851eb2538f00521005118af53df..0000000000000000000000000000000000000000
--- a/Processes/ExampleProcessors/TestDummy.cc
+++ /dev/null
@@ -1,95 +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/process/example_processors/DummyBoundaryCrossingProcess.h>
-#include <corsika/process/example_processors/DummyContinuousProcess.h>
-#include <corsika/process/example_processors/DummyDecayProcess.h>
-#include <corsika/process/example_processors/DummyInteractionProcess.h>
-#include <corsika/process/example_processors/DummySecondariesProcess.h>
-
-#include <corsika/process/ProcessReturn.h>
-
-#include <catch2/catch.hpp>
-
-#include <chrono>
-
-using namespace corsika;
-using namespace corsika::process;
-using namespace corsika::process::example_processors;
-
-using namespace corsika::units::si;
-
-TEST_CASE("Dummy Processes") {
-  DummyBoundaryCrossingProcess<1000> dbc;
-  DummyContinuousProcess<1000> dc;
-  DummyDecayProcess<1000> dd;
-  DummyInteractionProcess<1000> di;
-  DummySecondariesProcess<1000> dse;
-
-  int tmp = 0;
-  SECTION("BoundaryCrossing") {
-    auto start = std::chrono::steady_clock::now();
-    REQUIRE(dbc.DoBoundaryCrossing(tmp, 0, 0) == EProcessReturn::eOk);
-    auto end = std::chrono::steady_clock::now();
-    REQUIRE(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() ==
-            Approx(1000).margin(1));
-  }
-
-  SECTION("Continuous") {
-    auto start = std::chrono::steady_clock::now();
-    REQUIRE(dc.DoContinuous(tmp, nullptr) == EProcessReturn::eOk);
-    auto end = std::chrono::steady_clock::now();
-    REQUIRE(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() ==
-            Approx(1000).margin(1));
-
-    start = std::chrono::steady_clock::now();
-    REQUIRE(dc.MaxStepLength(nullptr, nullptr) ==
-            units::si::meter * std::numeric_limits<double>::infinity());
-    end = std::chrono::steady_clock::now();
-    REQUIRE(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() ==
-            Approx(1000).margin(1));
-  }
-
-  SECTION("Decay") {
-    auto start = std::chrono::steady_clock::now();
-    REQUIRE(dd.DoDecay(tmp) == EProcessReturn::eOk);
-    auto end = std::chrono::steady_clock::now();
-    REQUIRE(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() ==
-            Approx(1000).margin(1));
-
-    start = std::chrono::steady_clock::now();
-    REQUIRE(dd.GetLifetime(tmp) ==
-            units::si::second * std::numeric_limits<double>::infinity());
-    end = std::chrono::steady_clock::now();
-    REQUIRE(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() ==
-            Approx(1000).margin(1));
-  }
-
-  SECTION("Interaction") {
-    auto start = std::chrono::steady_clock::now();
-    REQUIRE(di.DoInteraction(tmp) == EProcessReturn::eOk);
-    auto end = std::chrono::steady_clock::now();
-    REQUIRE(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() ==
-            Approx(1000).margin(1));
-
-    start = std::chrono::steady_clock::now();
-    REQUIRE(di.GetInteractionLength(tmp) ==
-            (units::si::gram / 1_cm / 1_cm) * std::numeric_limits<double>::infinity());
-    end = std::chrono::steady_clock::now();
-    REQUIRE(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() ==
-            Approx(1000).margin(1));
-  }
-
-  SECTION("Secondaries") {
-    auto start = std::chrono::steady_clock::now();
-    REQUIRE(dse.DoSecondaries(tmp) == EProcessReturn::eOk);
-    auto end = std::chrono::steady_clock::now();
-    REQUIRE(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() ==
-            Approx(1000).margin(1));
-  }
-}
diff --git a/Processes/HadronicElasticModel/CMakeLists.txt b/Processes/HadronicElasticModel/CMakeLists.txt
deleted file mode 100644
index 55d774ec43981d88c3ab1d28e74cb8374888b1e7..0000000000000000000000000000000000000000
--- a/Processes/HadronicElasticModel/CMakeLists.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-set (
-  MODEL_SOURCES
-  HadronicElasticModel.cc
-  )
-
-set (
-  MODEL_HEADERS
-  HadronicElasticModel.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/hadronic_elastic_model
-  )
-
-add_library (ProcessHadronicElasticModel STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessHadronicElasticModel ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-set_target_properties (
-  ProcessHadronicElasticModel
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-#  PUBLIC_HEADER "${MODEL_HEADERS}"
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessHadronicElasticModel
-  CORSIKAunits
-  CORSIKAgeometry
-  CORSIKAsetup
-  CORSIKAutilities
-  )
-
-target_include_directories (
-  ProcessHadronicElasticModel 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS ProcessHadronicElasticModel
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-#  PUBLIC_HEADER DESTINATION include/${MODEL_NAMESPACE}
-  )
-
-
-# --------------------
-# code unit testing
-# CORSIKA_ADD_TEST (testProcessHadronicElasticModel)
-# target_link_libraries (
-  # testProcessHadronicElasticModel
-  # ProcessHadronicElasticModel
-  # CORSIKAsetup
-  # CORSIKAgeometry
-  # CORSIKAunits
-  # CORSIKAthirdparty # for catch2
-  # )
diff --git a/Processes/HadronicElasticModel/HadronicElasticModel.cc b/Processes/HadronicElasticModel/HadronicElasticModel.cc
deleted file mode 100644
index cdc01d9a92aa741de6b3af9775d021063c34d644..0000000000000000000000000000000000000000
--- a/Processes/HadronicElasticModel/HadronicElasticModel.cc
+++ /dev/null
@@ -1,194 +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/environment/Environment.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/geometry/FourVector.h>
-#include <corsika/process/hadronic_elastic_model/HadronicElasticModel.h>
-#include <corsika/random/ExponentialDistribution.h>
-#include <corsika/utl/COMBoost.h>
-
-#include <corsika/setup/SetupStack.h>
-
-#include <iomanip>
-#include <iostream>
-
-using SetupParticle = corsika::setup::Stack::ParticleType;
-using SetupView = corsika::setup::StackView;
-
-using namespace corsika::process::HadronicElasticModel;
-using namespace corsika;
-
-HadronicElasticInteraction::HadronicElasticInteraction(units::si::CrossSectionType x,
-                                                       units::si::CrossSectionType y)
-    : fX(x)
-    , fY(y) {}
-
-template <>
-units::si::GrammageType HadronicElasticInteraction::GetInteractionLength(
-    SetupParticle const& p) {
-  using namespace units::si;
-  if (p.GetPID() == particles::Code::Proton) {
-    auto const* currentNode = p.GetNode();
-    auto const& mediumComposition =
-        currentNode->GetModelProperties().GetNuclearComposition();
-
-    auto const& components = mediumComposition.GetComponents();
-    auto const& fractions = mediumComposition.GetFractions();
-
-    auto const projectileMomentum = p.GetMomentum();
-    auto const projectileMomentumSquaredNorm = projectileMomentum.squaredNorm();
-    auto const projectileEnergy = p.GetEnergy();
-
-    auto const avgCrossSection = [&]() {
-      CrossSectionType avgCrossSection = 0_b;
-
-      for (size_t i = 0; i < fractions.size(); ++i) {
-        auto const targetMass = particles::GetMass(components[i]);
-        auto const s = units::static_pow<2>(projectileEnergy + targetMass) -
-                       projectileMomentumSquaredNorm;
-        avgCrossSection += CrossSection(s) * fractions[i];
-      }
-
-      std::cout << "avgCrossSection: " << avgCrossSection / 1_mb << " mb" << std::endl;
-
-      return avgCrossSection;
-    }();
-
-    auto const avgTargetMassNumber = mediumComposition.GetAverageMassNumber();
-
-    GrammageType const interactionLength =
-        avgTargetMassNumber * units::constants::u / avgCrossSection;
-
-    return interactionLength;
-  } else {
-    return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
-  }
-}
-
-template <>
-process::EProcessReturn HadronicElasticInteraction::DoInteraction(SetupView& view) {
-  using namespace units::si;
-  using namespace units::constants;
-
-  auto p = view.GetProjectile();
-  if (p.GetPID() != particles::Code::Proton) { return process::EProcessReturn::eOk; }
-
-  const auto* currentNode = p.GetNode();
-  const auto& composition = currentNode->GetModelProperties().GetNuclearComposition();
-  const auto& components = composition.GetComponents();
-
-  std::vector<units::si::CrossSectionType> cross_section_of_components(
-      composition.GetComponents().size());
-
-  auto const projectileMomentum = p.GetMomentum();
-  auto const projectileMomentumSquaredNorm = projectileMomentum.squaredNorm();
-  auto const projectileEnergy = p.GetEnergy();
-
-  for (size_t i = 0; i < components.size(); ++i) {
-    auto const targetMass = particles::GetMass(components[i]);
-    auto const s = units::static_pow<2>(projectileEnergy + targetMass) -
-                   projectileMomentumSquaredNorm;
-    cross_section_of_components[i] = CrossSection(s);
-  }
-
-  const auto targetCode = composition.SampleTarget(cross_section_of_components, fRNG);
-
-  auto const targetMass = particles::GetMass(targetCode);
-
-  std::uniform_real_distribution phiDist(0., 2 * M_PI);
-
-  geometry::FourVector const projectileLab(projectileEnergy, projectileMomentum);
-  geometry::FourVector const targetLab(
-      targetMass, geometry::Vector<units::si::hepmomentum_d>(
-                      projectileMomentum.GetCoordinateSystem(), {0_eV, 0_eV, 0_eV}));
-  utl::COMBoost const boost(projectileLab, targetMass);
-
-  auto const projectileCoM = boost.toCoM(projectileLab);
-  auto const targetCoM = boost.toCoM(targetLab);
-
-  auto const pProjectileCoMSqNorm = projectileCoM.GetSpaceLikeComponents().squaredNorm();
-  auto const pProjectileCoMNorm = sqrt(pProjectileCoMSqNorm);
-
-  auto const eProjectileCoM = projectileCoM.GetTimeLikeComponent();
-  auto const eTargetCoM = targetCoM.GetTimeLikeComponent();
-
-  auto const sqrtS = eProjectileCoM + eTargetCoM;
-  auto const s = units::static_pow<2>(sqrtS);
-
-  auto const B = this->B(s);
-  std::cout << B << std::endl;
-
-  random::ExponentialDistribution tDist(1 / B);
-  auto const absT = [&]() {
-    decltype(tDist(fRNG)) absT;
-    auto const maxT = 4 * pProjectileCoMSqNorm;
-
-    do {
-      // |t| cannot become arbitrarily large, max. given by GER eq. (4.16), so we just
-      // throw again until we have an acceptable value.
-      absT = tDist(fRNG);
-    } while (absT >= maxT);
-
-    return absT;
-  }();
-
-  std::cout << "HadronicElasticInteraction: s = " << s * invGeVsq
-            << " GeV²; absT = " << absT * invGeVsq
-            << " GeV² (max./GeV² = " << 4 * invGeVsq * projectileMomentumSquaredNorm
-            << ')' << std::endl;
-
-  auto const theta = 2 * asin(sqrt(absT / (4 * pProjectileCoMSqNorm)));
-  auto const phi = phiDist(fRNG);
-
-  auto const projectileScatteredLab =
-      boost.fromCoM(geometry::FourVector<HEPEnergyType, geometry::Vector<hepmomentum_d>>(
-          eProjectileCoM,
-          geometry::Vector<hepmomentum_d>(projectileMomentum.GetCoordinateSystem(),
-                                          {pProjectileCoMNorm * sin(theta) * cos(phi),
-                                           pProjectileCoMNorm * sin(theta) * sin(phi),
-                                           pProjectileCoMNorm * cos(theta)})));
-
-  view.AddSecondary(
-      std::tuple<particles::Code, units::si::HEPEnergyType,
-                 corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
-          p.GetPID(),
-          sqrt(projectileScatteredLab.GetSpaceLikeComponents().squaredNorm() +
-               units::static_pow<2>(particles::GetMass(p.GetPID()))),
-          projectileScatteredLab.GetSpaceLikeComponents(), p.GetPosition(), p.GetTime()});
-
-  return process::EProcessReturn::eOk;
-}
-
-HadronicElasticInteraction::inveV2 HadronicElasticInteraction::B(eV2 s) const {
-  using namespace units::constants;
-  auto constexpr b_p = 2.3;
-  auto const result =
-      (2 * b_p + 2 * b_p + 4 * pow(s * invGeVsq, gfEpsilon) - 4.2) * invGeVsq;
-  std::cout << "B(" << s << ") = " << result / invGeVsq << " GeV¯²" << std::endl;
-  return result;
-}
-
-units::si::CrossSectionType HadronicElasticInteraction::CrossSection(
-    SquaredHEPEnergyType s) const {
-  using namespace units::si;
-  using namespace units::constants;
-  // assuming every target behaves like a proton, fX and fY are universal
-  CrossSectionType const sigmaTotal =
-      fX * pow(s * invGeVsq, gfEpsilon) + fY * pow(s * invGeVsq, -gfEta);
-
-  // according to Schuler & Sjöstrand, PRD 49, 2257 (1994)
-  // (we ignore rho because rho^2 is just ~2 %)
-  auto const sigmaElastic =
-      units::static_pow<2>(sigmaTotal) /
-      (16 * M_PI * ConvertHEPToSI<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;
-  return sigmaElastic;
-}
diff --git a/Processes/HadronicElasticModel/HadronicElasticModel.h b/Processes/HadronicElasticModel/HadronicElasticModel.h
deleted file mode 100644
index 3273bfec15aad2a47128a169e1031790f0dda3c6..0000000000000000000000000000000000000000
--- a/Processes/HadronicElasticModel/HadronicElasticModel.h
+++ /dev/null
@@ -1,61 +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/particles/ParticleProperties.h>
-#include <corsika/process/InteractionProcess.h>
-#include <corsika/random/RNGManager.h>
-
-#include <corsika/units/PhysicalConstants.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::process::HadronicElasticModel {
-  /**
-   * A simple model for elastic hadronic interactions based on the formulas
-   * in Gaisser, Engel, Resconi, Cosmic Rays and Particle Physics (Cambridge Univ. Press,
-   * 2016), section 4.2 and Donnachie, Landshoff, Phys. Lett. B 296, 227 (1992)
-   *
-   * Currently only \f$p\f$ projectiles are supported and cross-sections are assumed to be
-   * \f$pp\f$-like even for nuclei.
-   */
-  class HadronicElasticInteraction
-      : public corsika::process::InteractionProcess<HadronicElasticInteraction> {
-  private:
-    corsika::units::si::CrossSectionType const fX, fY;
-
-    static double constexpr gfEpsilon = 0.0808;
-    static double constexpr gfEta = 0.4525;
-    // Froissart-Martin is not violated up for sqrt s < 10^32 eV with these values [DL].
-
-    using SquaredHEPEnergyType = decltype(corsika::units::si::HEPEnergyType() *
-                                          corsika::units::si::HEPEnergyType());
-
-    using eV2 = decltype(units::si::square(units::si::electronvolt));
-    using inveV2 = decltype(1 / units::si::square(units::si::electronvolt));
-
-    corsika::random::RNG& fRNG =
-        corsika::random::RNGManager::GetInstance().GetRandomStream(
-            "HadronicElasticModel");
-
-    inveV2 B(eV2 s) const;
-    corsika::units::si::CrossSectionType CrossSection(SquaredHEPEnergyType s) const;
-
-  public:
-    HadronicElasticInteraction( // x & y values taken from DL for pp collisions
-        units::si::CrossSectionType x = 0.0217 * units::si::barn,
-        units::si::CrossSectionType y = 0.05608 * units::si::barn);
-
-    template <typename Particle>
-    corsika::units::si::GrammageType GetInteractionLength(Particle const& p);
-
-    template <typename TStackView>
-    corsika::process::EProcessReturn DoInteraction(TStackView&);
-  };
-
-} // namespace corsika::process::HadronicElasticModel
diff --git a/Processes/InteractionCounter/CMakeLists.txt b/Processes/InteractionCounter/CMakeLists.txt
deleted file mode 100644
index 235246c713af90d1c48a7080284544b87131ffbb..0000000000000000000000000000000000000000
--- a/Processes/InteractionCounter/CMakeLists.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-set (
-  MODEL_HEADERS
-  InteractionCounter.hpp
-  InteractionHistogram.hpp
-  )
-
-set (
-  MODEL_SOURCES
-  InteractionHistogram.cc
-)
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/interaction_counter
-  )
-
-add_library (ProcessInteractionCounter STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessInteractionCounter ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-set_target_properties (
-  ProcessInteractionCounter
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessInteractionCounter
-  CORSIKAunits
-  CORSIKAutilities
-  CORSIKAprocesssequence
-  CORSIKAthirdparty
-  C8::ext::boost
-  )
-
-target_include_directories (
-  ProcessInteractionCounter 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (FILES ${MODEL_HEADERS} DESTINATION include/${MODEL_NAMESPACE})
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST(testInteractionCounter SOURCES
-  testInteractionCounter.cc
-  ${MODEL_HEADERS}
-)
-#
-target_compile_definitions (
-  testInteractionCounter
-  PRIVATE
-  REFDATADIR="${CMAKE_CURRENT_SOURCE_DIR}"
-  )
-#
-target_link_libraries (
-  testInteractionCounter
-  ProcessInteractionCounter
-  CORSIKAsetup
-  CORSIKAunits
-  CORSIKAenvironment
-  CORSIKAtesting
-  )
diff --git a/Processes/InteractionCounter/InteractionCounter.hpp b/Processes/InteractionCounter/InteractionCounter.hpp
deleted file mode 100644
index c565eb6a106f13877b96511b7de7918cd16e4d8d..0000000000000000000000000000000000000000
--- a/Processes/InteractionCounter/InteractionCounter.hpp
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * (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 <corsika/process/interaction_counter/InteractionHistogram.hpp>
-
-#include <corsika/process/InteractionProcess.h>
-#include <corsika/process/ProcessSequence.h>
-#include <corsika/setup/SetupStack.h>
-
-namespace corsika::process::interaction_counter {
-  /*!
-   * Wrapper around an InteractionProcess that fills histograms of the number
-   * of calls to DoInteraction() binned in projectile energy (both in
-   * lab and center-of-mass frame) and species
-   */
-  template <class TCountedProcess>
-  class InteractionCounter
-      : public InteractionProcess<InteractionCounter<TCountedProcess>> {
-
-    TCountedProcess& process_;
-    InteractionHistogram histogram_;
-
-  public:
-    InteractionCounter(TCountedProcess& process)
-        : process_(process) {}
-
-    template <typename TSecondaryView>
-    auto DoInteraction(TSecondaryView& view) {
-      auto const projectile = view.GetProjectile();
-      auto const massNumber = projectile.GetNode()
-                                  ->GetModelProperties()
-                                  .GetNuclearComposition()
-                                  .GetAverageMassNumber();
-      auto const massTarget = massNumber * units::constants::nucleonMass;
-
-      if (auto const projectile_id = projectile.GetPID();
-          projectile_id == particles::Code::Nucleus) {
-        auto const A = projectile.GetNuclearA();
-        auto const Z = projectile.GetNuclearZ();
-        histogram_.fill(projectile_id, projectile.GetEnergy(), massTarget, A, Z);
-      } else {
-        histogram_.fill(projectile_id, projectile.GetEnergy(), massTarget);
-      }
-      return process_.DoInteraction(view);
-    }
-
-    template <typename TParticle>
-    auto GetInteractionLength(TParticle const& particle) const {
-      return process_.GetInteractionLength(particle);
-    }
-
-    InteractionHistogram const& GetHistogram() const { return histogram_; }
-  };
-
-} // namespace corsika::process::interaction_counter
diff --git a/Processes/InteractionCounter/InteractionHistogram.cc b/Processes/InteractionCounter/InteractionHistogram.cc
deleted file mode 100644
index 45d04469b4c8fa7f9dd7ed1f22b41817ab4f3a2a..0000000000000000000000000000000000000000
--- a/Processes/InteractionCounter/InteractionHistogram.cc
+++ /dev/null
@@ -1,67 +0,0 @@
-/*
- * (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.
- */
-
-#include <corsika/process/interaction_counter/InteractionHistogram.hpp>
-
-#include <fstream>
-#include <string>
-
-using namespace corsika::process::interaction_counter;
-
-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(particles::Code projectile_id,
-                                units::si::HEPEnergyType lab_energy,
-                                units::si::HEPEnergyType mass_target, int A, int Z) {
-  using namespace units::si;
-  auto constexpr inv_eV = 1 / 1_eV;
-  if (projectile_id == particles::Code::Nucleus) {
-    auto const sqrtS =
-        sqrt(A * A * (units::constants::nucleonMass * units::constants::nucleonMass) +
-             mass_target * mass_target + 2 * lab_energy * mass_target);
-
-    int32_t const pdg = 1'000'000'000l + Z * 10'000l + A * 10l;
-
-    inthist_lab_(pdg, lab_energy * inv_eV);
-    inthist_cms_(pdg, sqrtS * inv_eV);
-  } else {
-    auto const projectile_mass = particles::GetMass(projectile_id);
-    auto const sqrtS = sqrt(projectile_mass * projectile_mass +
-                            mass_target * mass_target + 2 * lab_energy * mass_target);
-
-    inthist_cms_(static_cast<int>(particles::GetPDG(projectile_id)), sqrtS * inv_eV);
-    inthist_lab_(static_cast<int>(particles::GetPDG(projectile_id)), lab_energy * inv_eV);
-  }
-}
-
-void InteractionHistogram::saveLab(std::string const& filename,
-                                   utl::SaveMode mode) const {
-  corsika::utl::save_hist(inthist_lab_, filename, mode);
-}
-
-void InteractionHistogram::saveCMS(std::string const& filename,
-                                   utl::SaveMode mode) const {
-  corsika::utl::save_hist(inthist_cms_, filename, mode);
-}
-
-InteractionHistogram& InteractionHistogram::operator+=(
-    InteractionHistogram const& other) {
-  inthist_lab_ += other.inthist_lab_;
-  inthist_cms_ += other.inthist_cms_;
-
-  return *this;
-}
-
-InteractionHistogram InteractionHistogram::operator+(InteractionHistogram other) const {
-  other.inthist_lab_ += inthist_lab_;
-  other.inthist_cms_ += inthist_cms_;
-
-  return other;
-}
diff --git a/Processes/InteractionCounter/InteractionHistogram.hpp b/Processes/InteractionCounter/InteractionHistogram.hpp
deleted file mode 100644
index d374eb8d1778379134222b8de0beeb0dcfc181fb..0000000000000000000000000000000000000000
--- a/Processes/InteractionCounter/InteractionHistogram.hpp
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * (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 <corsika/particles/ParticleProperties.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/SaveBoostHistogram.hpp>
-
-#include <boost/histogram.hpp>
-#include <fstream>
-#include <functional>
-#include <map>
-#include <utility>
-
-namespace corsika::process::interaction_counter {
-  namespace detail {
-    inline auto hist_factory(unsigned int bin_number, float e_low, float e_high) {
-      namespace bh = boost::histogram;
-      namespace bha = bh::axis;
-
-      auto h = bh::make_histogram(
-          bha::category<int, bh::use_default, bha::option::growth_t>{{2212, 2112},
-                                                                     "projectile PDG"},
-          bha::regular<float, bha::transform::log>{bin_number, e_low, e_high,
-                                                   "energy/eV"});
-      return h;
-    }
-  } // namespace detail
-
-  class InteractionHistogram {
-    static double constexpr lower_edge_cms = 1e3, upper_edge_cms = 1e17; // eV sqrt s
-    static double constexpr lower_edge_lab = 1e3, upper_edge_lab = 1e21; // eV lab
-    static unsigned int constexpr num_bins_lab = 18 * 10, num_bins_cms = 14 * 10;
-
-    using hist_type =
-        decltype(detail::hist_factory(num_bins_lab, lower_edge_lab, upper_edge_lab));
-
-    hist_type inthist_cms_, inthist_lab_;
-
-  public:
-    InteractionHistogram();
-
-    //! fill both CMS and lab histograms at the same time
-    void fill(particles::Code projectile_id, units::si::HEPEnergyType lab_energy,
-              units::si::HEPEnergyType mass_target, int A = 0, int Z = 0);
-
-    hist_type const& CMSHist() const { return inthist_cms_; }
-
-    hist_type const& labHist() const { return inthist_lab_; }
-
-    void saveLab(std::string const& filename,
-                 utl::SaveMode mode = utl::SaveMode::append) const;
-
-    void saveCMS(std::string const& filename,
-                 utl::SaveMode mode = utl::SaveMode::append) const;
-
-    InteractionHistogram& operator+=(InteractionHistogram const& other);
-
-    InteractionHistogram operator+(InteractionHistogram other) const;
-  };
-
-} // namespace corsika::process::interaction_counter
diff --git a/Processes/LongitudinalProfile/CMakeLists.txt b/Processes/LongitudinalProfile/CMakeLists.txt
deleted file mode 100644
index 33e0df66db22b5b61174f72e7d887b83393abd82..0000000000000000000000000000000000000000
--- a/Processes/LongitudinalProfile/CMakeLists.txt
+++ /dev/null
@@ -1,62 +0,0 @@
-set (
-  MODEL_SOURCES
-  LongitudinalProfile.cc
-  )
-
-set (
-  MODEL_HEADERS
-  LongitudinalProfile.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/longitudinal_profile
-  )
-
-add_library (ProcessLongitudinalProfile STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessLongitudinalProfile ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-set_target_properties (
-  ProcessLongitudinalProfile
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-#  PUBLIC_HEADER "${MODEL_HEADERS}"
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessLongitudinalProfile
-  CORSIKAenvironment
-  CORSIKAunits
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAsetup
-  )
-
-target_include_directories (
-  ProcessLongitudinalProfile 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS ProcessLongitudinalProfile
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-#  PUBLIC_HEADER DESTINATION include/${MODEL_NAMESPACE}
-  )
-
-
-# --------------------
-# code unit testing
-# CORSIKA_ADD_TEST(testNullModel)
-#target_link_libraries (
-#  testNullModel  ProcessNullModel
-#  CORSIKAsetup
-#  CORSIKAgeometry
-#  CORSIKAunits
-#  CORSIKAthirdparty # for catch2
-#  )
-
diff --git a/Processes/LongitudinalProfile/LongitudinalProfile.cc b/Processes/LongitudinalProfile/LongitudinalProfile.cc
deleted file mode 100644
index f9fd05c1baf2e450fc22e791a60e788a51ceda57..0000000000000000000000000000000000000000
--- a/Processes/LongitudinalProfile/LongitudinalProfile.cc
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * (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.
- */
-
-#include <corsika/process/longitudinal_profile/LongitudinalProfile.h>
-
-#include <corsika/particles/ParticleProperties.h>
-
-#include <corsika/logging/Logging.h>
-
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <cmath>
-#include <iomanip>
-#include <limits>
-
-using namespace corsika::setup;
-using Particle = Stack::ParticleType;
-using Track = Trajectory;
-
-using namespace corsika::process::longitudinal_profile;
-using namespace corsika::units::si;
-
-LongitudinalProfile::LongitudinalProfile(environment::ShowerAxis const& shower_axis,
-                                         units::si::GrammageType dX)
-    : dX_(dX)
-    , shower_axis_{shower_axis}
-    , profiles_{static_cast<unsigned int>(shower_axis.maximumX() / dX_) + 1} {}
-
-template <>
-corsika::process::EProcessReturn LongitudinalProfile::DoContinuous(Particle const& vP,
-                                                                   Track const& vTrack) {
-  auto const pid = vP.GetPID();
-
-  GrammageType const grammageStart = shower_axis_.projectedX(vTrack.GetPosition(0));
-  GrammageType const grammageEnd = shower_axis_.projectedX(vTrack.GetPosition(1));
-
-  C8LOG_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));
-
-  const int binStart = std::ceil(grammageStart / dX_);
-  const int binEnd = std::floor(grammageEnd / dX_);
-
-  for (int b = binStart; b <= binEnd; ++b) {
-    if (pid == particles::Code::Gamma) {
-      profiles_.at(b)[ProfileIndex::Gamma]++;
-    } else if (pid == particles::Code::Positron) {
-      profiles_.at(b)[ProfileIndex::Positron]++;
-    } else if (pid == particles::Code::Electron) {
-      profiles_.at(b)[ProfileIndex::Electron]++;
-    } else if (pid == particles::Code::MuPlus) {
-      profiles_.at(b)[ProfileIndex::MuPlus]++;
-    } else if (pid == particles::Code::MuMinus) {
-      profiles_.at(b)[ProfileIndex::MuMinus]++;
-    } else if (particles::IsHadron(pid)) {
-      profiles_.at(b)[ProfileIndex::Hadron]++;
-    }
-  }
-
-  return corsika::process::EProcessReturn::eOk;
-}
-
-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) {
-    f << std::setprecision(5) << std::setw(11) << b * (dX_ / (1_g / 1_cm / 1_cm));
-    for (auto const& N : profiles_.at(b)) {
-      f << std::setw(width) << std::setprecision(precision) << std::scientific << N;
-    }
-    f << std::endl;
-  }
-}
diff --git a/Processes/NullModel/CMakeLists.txt b/Processes/NullModel/CMakeLists.txt
deleted file mode 100644
index 07e653a7ce0717c71c5ea7a25a64417098478987..0000000000000000000000000000000000000000
--- a/Processes/NullModel/CMakeLists.txt
+++ /dev/null
@@ -1,60 +0,0 @@
-set (
-  MODEL_SOURCES
-  NullModel.cc
-  )
-
-set (
-  MODEL_HEADERS
-  NullModel.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/null_model
-  )
-
-add_library (ProcessNullModel STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessNullModel ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-set_target_properties (
-  ProcessNullModel
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-#  PUBLIC_HEADER "${MODEL_HEADERS}"
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessNullModel
-  CORSIKAunits
-  CORSIKAgeometry
-  CORSIKAsetup
-  )
-
-target_include_directories (
-  ProcessNullModel 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS ProcessNullModel
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-#  PUBLIC_HEADER DESTINATION include/${MODEL_NAMESPACE}
-  )
-
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST (testNullModel)
-target_link_libraries (
-  testNullModel
-  ProcessNullModel
-  CORSIKAsetup
-  CORSIKAgeometry
-  CORSIKAunits
-  CORSIKAtesting
-  )
diff --git a/Processes/NullModel/testNullModel.cc b/Processes/NullModel/testNullModel.cc
deleted file mode 100644
index cc142637f1a6517873cf9207107192117e35e2f0..0000000000000000000000000000000000000000
--- a/Processes/NullModel/testNullModel.cc
+++ /dev/null
@@ -1,54 +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 <catch2/catch.hpp>
-
-#include <corsika/process/null_model/NullModel.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-using namespace corsika::units::si;
-using namespace corsika::process::null_model;
-using namespace corsika;
-
-TEST_CASE("NullModel", "[processes]") {
-
-  auto [env, csPtr, nodePtr] = setup::testing::setupEnvironment(particles::Code::Oxygen);
-  auto const& cs = *csPtr;
-  [[maybe_unused]] auto const& env_dummy = env;
-  [[maybe_unused]] auto const& node_dummy = nodePtr;
-
-  auto [stack, view] =
-      setup::testing::setupStack(particles::Code::Electron, 0, 0, 100_GeV, nodePtr, cs);
-  [[maybe_unused]] const auto& dummyView = view;
-  auto particle = stack->first();
-
-  geometry::Point const origin(cs, {0_m, 0_m, 0_m});
-  geometry::Vector<units::si::SpeedType::dimension_type> v(cs, 0_m / second, 0_m / second,
-                                                           1_m / second);
-  geometry::Line line(origin, v);
-  geometry::Trajectory<geometry::Line> track(line, 10_s);
-
-  SECTION("interface") {
-
-    NullModel model(10_m);
-
-    [[maybe_unused]] const process::EProcessReturn ret =
-        model.DoContinuous(particle, track);
-    LengthType const length = model.MaxStepLength(particle, track);
-
-    CHECK((length / 10_m) == Approx(1));
-  }
-}
diff --git a/Processes/ObservationPlane/CMakeLists.txt b/Processes/ObservationPlane/CMakeLists.txt
deleted file mode 100644
index 800c5dbd6b2e900f8e2228fe9e64615f304c4d28..0000000000000000000000000000000000000000
--- a/Processes/ObservationPlane/CMakeLists.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-set (
-  MODEL_HEADERS
-  ObservationPlane.h
-  )
-
-set (
-  MODEL_SOURCES
-  ObservationPlane.cc
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/observation_plane
-  )
-
-add_library (ProcessObservationPlane STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessObservationPlane ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-#target dependencies on other libraries(also the header onlys)
-target_link_libraries (
-  ProcessObservationPlane
-  CORSIKAgeometry
-  CORSIKAprocesssequence
-  CORSIKAlogging
-  )
-
-target_include_directories (
-  ProcessObservationPlane 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (FILES ${MODEL_HEADERS} DESTINATION include/${MODEL_NAMESPACE})
-
-#-- -- -- -- -- -- -- -- -- --
-#code unit testing
-CORSIKA_ADD_TEST(testObservationPlane)
-target_link_libraries (
-  testObservationPlane
-  ProcessObservationPlane
-  CORSIKAstackinterface
-  CORSIKAthirdparty # for catch2
-)
diff --git a/Processes/ObservationPlane/ObservationPlane.cc b/Processes/ObservationPlane/ObservationPlane.cc
deleted file mode 100644
index dd8b797436a7bb1c892b75ca742ff6a9e0557ade..0000000000000000000000000000000000000000
--- a/Processes/ObservationPlane/ObservationPlane.cc
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * (c) Copyright 2019 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/process/observation_plane/ObservationPlane.h>
-#include <corsika/logging/Logging.h>
-
-#include <fstream>
-
-using namespace corsika::process::observation_plane;
-using namespace corsika::units::si;
-
-ObservationPlane::ObservationPlane(
-    geometry::Plane const& obsPlane,
-    geometry::Vector<units::si::dimensionless_d> const& x_axis,
-    std::string const& filename, bool deleteOnHit)
-    : plane_(obsPlane)
-    , outputStream_(filename)
-    , deleteOnHit_(deleteOnHit)
-    , energy_ground_(0_GeV)
-    , count_ground_(0)
-    , xAxis_(x_axis.normalized())
-    , yAxis_(obsPlane.GetNormal().cross(xAxis_)) {
-  outputStream_ << "#PDG code, energy / eV, x distance / m, y distance / m" << std::endl;
-}
-
-corsika::process::EProcessReturn ObservationPlane::DoContinuous(
-    setup::Stack::ParticleType& particle, setup::Trajectory const& trajectory) {
-  
-  TimeType const timeOfIntersection =
-      (plane_.GetCenter() - trajectory.GetLine().GetR0()).dot(plane_.GetNormal()) /
-      trajectory.GetLine().GetV0().dot(plane_.GetNormal());
-
-  if (timeOfIntersection < TimeType::zero()) { return process::EProcessReturn::eOk; }
-
-  if (plane_.IsAbove(trajectory.GetLine().GetR0()) ==
-      plane_.IsAbove(trajectory.GetPosition(1))) {
-    return process::EProcessReturn::eOk;
-  }
-
-  const auto energy = particle.GetEnergy();
-  auto const displacement = trajectory.GetPosition(1) - plane_.GetCenter();
-
-  outputStream_ << static_cast<int>(particles::GetPDG(particle.GetPID())) << ' '
-                << energy / 1_eV << ' ' << displacement.dot(xAxis_) / 1_m << ' '
-                << displacement.dot(yAxis_) / 1_m
-                << (trajectory.GetPosition(1) - plane_.GetCenter()).norm() / 1_m
-                << std::endl;
-
-  if (deleteOnHit_) {
-    count_ground_++;
-    energy_ground_ += energy;
-    particle.Delete();
-    return process::EProcessReturn::eParticleAbsorbed;
-  } else {
-    return process::EProcessReturn::eOk;
-  }
-}
-
-LengthType ObservationPlane::MaxStepLength(setup::Stack::ParticleType const& vParticle,
-                                           setup::Trajectory const& trajectory) {
-  int chargeNumber;
-  if (corsika::particles::IsNucleus(vParticle.GetPID())) {
-    chargeNumber = vParticle.GetNuclearZ();
-  } else {
-    chargeNumber = corsika::particles::GetChargeNumber(vParticle.GetPID());
-  }
-  auto const* currentLogicalVolumeNode = vParticle.GetNode();
-  auto magneticfield = currentLogicalVolumeNode->GetModelProperties().GetMagneticField(
-      vParticle.GetPosition());
-  auto direction = trajectory.GetLine().GetV0().normalized();
-
-  if (chargeNumber != 0 &&
-      abs(plane_.GetNormal().dot(trajectory.GetLine().GetV0().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 * corsika::units::constants::c * 1_eV /
-             (vParticle.GetMomentum().norm() * 1_V);
-
-    if (direction.dot(plane_.GetNormal()) * direction.dot(plane_.GetNormal()) -
-            (plane_.GetNormal().dot(trajectory.GetLine().GetR0() - 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.GetLine().GetR0() - 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.GetLine().GetR0() - 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)
-                 .norm() *
-             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)
-                 .norm() *
-             1.001;
-    }
-  }
-  TimeType const timeOfIntersection =
-      (plane_.GetCenter() - trajectory.GetLine().GetR0()).dot(plane_.GetNormal()) /
-      trajectory.GetLine().GetV0().dot(plane_.GetNormal());
-
-  if (timeOfIntersection < TimeType::zero()) {
-    return std::numeric_limits<double>::infinity() * 1_m;
-  }
-
-  auto const pointOfIntersection = trajectory.GetLine().GetPosition(timeOfIntersection);
-  std::cout << " obs plane non b-field " << std::endl;
-  return (trajectory.GetLine().GetR0() - pointOfIntersection).norm() * 1.0001;
-}
-
-void ObservationPlane::ShowResults() const {
-  C8LOG_INFO(
-      " ******************************\n"
-      " ObservationPlane: \n"
-      " energy in ground (GeV)     :  {}\n"
-      " no. of particles in ground :  {}\n"
-      " ******************************",
-      energy_ground_ / 1_GeV, count_ground_);
-}
-
-void ObservationPlane::Reset() {
-  energy_ground_ = 0_GeV;
-  count_ground_ = 0;
-}
diff --git a/Processes/ObservationPlane/ObservationPlane.h b/Processes/ObservationPlane/ObservationPlane.h
deleted file mode 100644
index 3b530f6213a6d88160329c3165e004a3ec6b66b7..0000000000000000000000000000000000000000
--- a/Processes/ObservationPlane/ObservationPlane.h
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * (c) Copyright 2019 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/geometry/Plane.h>
-#include <corsika/process/ContinuousProcess.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <fstream>
-
-namespace corsika::process::observation_plane {
-
-  /**
-   * The ObservationPlane writes PDG codes, energies, and distances of particles to the
-   * central point of the plane into its output file. The particles are considered
-   * "absorbed" afterwards.
-   */
-  class ObservationPlane : public corsika::process::ContinuousProcess<ObservationPlane> {
-
-  public:
-    ObservationPlane(geometry::Plane const&,
-                     geometry::Vector<units::si::dimensionless_d> const&,
-                     std::string const&, bool = true);
-
-    corsika::process::EProcessReturn DoContinuous(
-        corsika::setup::Stack::ParticleType& vParticle,
-        corsika::setup::Trajectory const& vTrajectory);
-
-    corsika::units::si::LengthType MaxStepLength(
-        corsika::setup::Stack::ParticleType const&,
-        corsika::setup::Trajectory const& vTrajectory);
-
-    void ShowResults() const;
-    void Reset();
-    corsika::units::si::HEPEnergyType GetEnergyGround() const { return energy_ground_; }
-
-  private:
-    geometry::Plane const plane_;
-    std::ofstream outputStream_;
-    bool const deleteOnHit_;
-
-    units::si::HEPEnergyType energy_ground_ = 0 * units::si::electronvolt;
-    unsigned int count_ground_ = 0;
-    geometry::Vector<units::si::dimensionless_d> const xAxis_, yAxis_;
-  };
-} // namespace corsika::process::observation_plane
diff --git a/Processes/ObservationPlane/ObservationPlane_interpolation.cc b/Processes/ObservationPlane/ObservationPlane_interpolation.cc
deleted file mode 100644
index 8460c5f5ba34947adc2e2693de897e69dbbebf7d..0000000000000000000000000000000000000000
--- a/Processes/ObservationPlane/ObservationPlane_interpolation.cc
+++ /dev/null
@@ -1,68 +0,0 @@
-/*
- * (c) Copyright 2019 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * See file AUTHORS for a list of contributors.
- *
- * 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/process/observation_plane/ObservationPlane.h>
-
-#include <fstream>
-
-using namespace corsika::process::observation_plane;
-using namespace corsika::units::si;
-
-ObservationPlane::ObservationPlane(
-    geometry::Plane const& obsPlane,
-    geometry::Vector<units::si::dimensionless_d> const& x_axis,
-    std::string const& filename, bool deleteOnHit)
-    : plane_(obsPlane)
-    , outputStream_(filename)
-    , deleteOnHit_(deleteOnHit)
-    , xAxis_(x_axis.normalized())
-    , yAxis_(obsPlane.GetNormal().cross(xAxis_)) {
-  outputStream_ << "#PDG code, energy / eV, x distance / m, y distance / m" << std::endl;
-}
-
-corsika::process::EProcessReturn ObservationPlane::DoContinuous(
-    setup::Stack::ParticleType const& particle, setup::Trajectory const& trajectory) {
-  TimeType const timeOfIntersection =
-      (plane_.GetCenter() - trajectory.GetR0()).dot(plane_.GetNormal()) /
-      trajectory.GetV0().dot(plane_.GetNormal());
-
-  if (timeOfIntersection < TimeType::zero()) { return process::EProcessReturn::eOk; }
-
-  if (plane_.IsAbove(trajectory.GetR0()) == plane_.IsAbove(trajectory.GetPosition(1))) {
-    return process::EProcessReturn::eOk;
-  }
-
-  auto const displacement = trajectory.GetPosition(1) - plane_.GetCenter();
-
-  outputStream_ << static_cast<int>(particles::GetPDG(particle.GetPID())) << ' '
-                << particle.GetEnergy() * (1 / 1_eV) << ' '
-                << displacement.dot(xAxis_) / 1_m << ' ' << displacement.dot(yAxis_) / 1_m
-                << ' ' << std::endl;
-
-  if (deleteOnHit_) {
-    return process::EProcessReturn::eParticleAbsorbed;
-  } else {
-    return process::EProcessReturn::eOk;
-  }
-}
-
-LengthType ObservationPlane::MaxStepLength(setup::Stack::ParticleType const&,
-                                           setup::Trajectory const& trajectory) {
-  TimeType const timeOfIntersection =
-      (plane_.GetCenter() - trajectory.GetR0()).dot(plane_.GetNormal()) /
-      trajectory.GetV0().dot(plane_.GetNormal());
-
-  if (timeOfIntersection < TimeType::zero()) {
-    return std::numeric_limits<double>::infinity() * 1_m;
-  }
-
-  auto const pointOfIntersection = trajectory.GetPosition(timeOfIntersection);
-  return (trajectory.GetR0() - pointOfIntersection).norm() * 1.0001;
-}
diff --git a/Processes/ObservationPlane/testObservationPlane.cc b/Processes/ObservationPlane/testObservationPlane.cc
deleted file mode 100644
index ddaaaad96e4795df9cb2695fde09a3de24e0c069..0000000000000000000000000000000000000000
--- a/Processes/ObservationPlane/testObservationPlane.cc
+++ /dev/null
@@ -1,86 +0,0 @@
-/*
- * (c) Copyright 2019 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.
- */
-
-#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one
-                          // cpp file
-#include <catch2/catch.hpp>
-
-#include <corsika/process/observation_plane/ObservationPlane.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-using namespace corsika::units::si;
-using namespace corsika::process::observation_plane;
-using namespace corsika;
-using namespace corsika::geometry;
-using namespace corsika::particles;
-
-TEST_CASE("ContinuousProcess interface", "[proccesses][observation_plane]") {
-
-  auto [env, csPtr, nodePtr] = setup::testing::setupEnvironment(particles::Code::Oxygen);
-  auto const& cs = *csPtr;
-  [[maybe_unused]] auto const& env_dummy = env;
-  [[maybe_unused]] auto const& node_dummy = nodePtr;
-
-  /*
-    Test with downward going 1_GeV neutrino, starting at 0, 1_m, 10m
-
-    ObservationPlane has origin at 0,0,0
-   */
-
-  auto [stack, viewPtr] =
-      setup::testing::setupStack(particles::Code::NuE, 0, 0, 1_GeV, nodePtr, cs);
-  [[maybe_unused]] setup::StackView& view = *viewPtr;
-  auto particle = stack->GetNextParticle();
-
-  Point const start(cs, {0_m, 1_m, 10_m});
-  Vector<units::si::SpeedType::dimension_type> vec(cs, 0_m / second, 0_m / second,
-                                                   -units::constants::c);
-  Line line(start, vec);
-  setup::Trajectory track =
-      setup::testing::make_track<setup::Trajectory>(line, 12_m / units::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}),
-                         Vector<dimensionless_d>(cs, {0., 0., 1.}));
-    ObservationPlane obs(obsPlane, Vector<dimensionless_d>(cs, {1., 0., 0.}),
-                         "particles.dat", true);
-
-    const LengthType length = obs.MaxStepLength(particle, track);
-    const process::EProcessReturn ret = obs.DoContinuous(particle, track);
-
-    REQUIRE(length / 10_m == Approx(1).margin(1e-4));
-    REQUIRE(ret == process::EProcessReturn::eParticleAbsorbed);
-  }
-
-  SECTION("inclined plane") {}
-
-  SECTION("transparent plane") {
-    Plane const obsPlane(Point(cs, {0_m, 0_m, 0_m}),
-                         Vector<dimensionless_d>(cs, {0., 0., 1.}));
-    ObservationPlane obs(obsPlane, Vector<dimensionless_d>(cs, {1., 0., 0.}),
-                         "particles.dat", false);
-
-    const LengthType length = obs.MaxStepLength(particle, track);
-    const process::EProcessReturn ret = obs.DoContinuous(particle, track);
-
-    REQUIRE(length / 10_m == Approx(1).margin(1e-4));
-    REQUIRE(ret == process::EProcessReturn::eOk);
-  }
-}
diff --git a/Processes/OnShellCheck/CMakeLists.txt b/Processes/OnShellCheck/CMakeLists.txt
deleted file mode 100644
index 0d2191ad34fd420b322efdf39c39d024a0f8184d..0000000000000000000000000000000000000000
--- a/Processes/OnShellCheck/CMakeLists.txt
+++ /dev/null
@@ -1,67 +0,0 @@
-set (
-  MODEL_SOURCES
-  OnShellCheck.cc
-)
-
-set (
-  MODEL_HEADERS
-  OnShellCheck.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/on_shell_check
-  )
-
-add_library (ProcessOnShellCheck STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessOnShellCheck ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-set_target_properties (
-  ProcessOnShellCheck
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-#  PUBLIC_HEADER "${MODEL_HEADERS}"
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessOnShellCheck
-  CORSIKAunits
-  CORSIKAparticles
-  CORSIKAprocesssequence
-  CORSIKAsetup
-  )
-
-target_include_directories (
-  ProcessOnShellCheck 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS ProcessOnShellCheck
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-#  PUBLIC_HEADER DESTINATION include/${MODEL_NAMESPACE}
-  )
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST(testOnShellCheck SOURCES
-  testOnShellCheck.cc
-  ${MODEL_HEADERS}
-)
-
-target_link_libraries (
-  testOnShellCheck
-  ProcessOnShellCheck
-  CORSIKAunits
-  CORSIKAstackinterface
-  CORSIKAprocesssequence
-  CORSIKAsetup
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAtesting
-  )
diff --git a/Processes/OnShellCheck/OnShellCheck.cc b/Processes/OnShellCheck/OnShellCheck.cc
deleted file mode 100644
index 6ad035d14cac0a3f5f17a06397c9f07633e0db83..0000000000000000000000000000000000000000
--- a/Processes/OnShellCheck/OnShellCheck.cc
+++ /dev/null
@@ -1,77 +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/geometry/FourVector.h>
-#include <corsika/process/on_shell_check/OnShellCheck.h>
-
-using namespace std;
-
-using namespace corsika;
-using namespace corsika::process;
-using namespace corsika::units::si;
-using namespace corsika::particles;
-using namespace corsika::setup;
-
-namespace corsika::process {
-  namespace on_shell_check {
-
-    void OnShellCheck::Init() {
-      std::cout << "OnShellCheck: mass tolerance is set to " << mass_tolerance_ * 100
-                << "%" << endl
-                << "              energy tolerance is set to " << energy_tolerance_ * 100
-                << "%" << std::endl;
-    }
-
-    EProcessReturn OnShellCheck::DoSecondaries(corsika::setup::StackView& vS) {
-      for (auto& p : vS) {
-        auto const pid = p.GetPID();
-        if (!particles::IsHadron(pid) || particles::IsNucleus(pid)) continue;
-        auto const e_original = p.GetEnergy();
-        auto const p_original = p.GetMomentum();
-        auto const Plab = corsika::geometry::FourVector(e_original, p_original);
-        auto const m_kinetic = Plab.GetNorm();
-        auto const m_corsika = particles::GetMass(pid);
-        auto const m_err_abs = abs(m_kinetic - m_corsika);
-        if (m_err_abs >= mass_tolerance_ * m_corsika) {
-          const HEPEnergyType e_shifted =
-              sqrt(p_original.GetSquaredNorm() + m_corsika * m_corsika);
-          auto const e_shift_relative = (e_shifted / e_original - 1);
-          count_ = count_ + 1;
-          average_shift_ += abs(e_shift_relative);
-          if (abs(e_shift_relative) > max_shift_) max_shift_ = abs(e_shift_relative);
-          std::cout << "OnShellCheck: shift particle mass for " << pid << std::endl
-                    << std::setw(40) << std::setfill(' ')
-                    << "corsika mass (GeV): " << m_corsika / 1_GeV << std::endl
-                    << std::setw(40) << std::setfill(' ')
-                    << "kinetic mass (GeV): " << m_kinetic / 1_GeV << std::endl
-                    << std::setw(40) << std::setfill(' ')
-                    << "m_kin-m_cor (GeV): " << m_err_abs / 1_GeV << std::endl
-                    << std::setw(40) << std::setfill(' ')
-                    << "mass tolerance (GeV): " << (m_corsika * mass_tolerance_) / 1_GeV
-                    << std::endl;
-          /*
-            For now we warn if the necessary shift is larger than 1%.
-            we could promote this to an error.
-          */
-          if (abs(e_shift_relative) > energy_tolerance_) {
-            std::cout << "OnShellCheck: warning! shifted particle energy by "
-                      << e_shift_relative * 100 << " %" << std::endl;
-            if (throw_error_)
-              throw std::runtime_error(
-                  "OnShellCheck: error! shifted energy by large amount!");
-          }
-
-          // reset energy
-          p.SetEnergy(e_shifted);
-        } else
-          std::cout << "OnShellCheck: particle mass for " << pid << " OK" << std::endl;
-      }
-      return EProcessReturn::eOk;
-    }
-  } // namespace on_shell_check
-} // namespace corsika::process
diff --git a/Processes/OnShellCheck/OnShellCheck.h b/Processes/OnShellCheck/OnShellCheck.h
deleted file mode 100644
index 940c705e2500b1c219c78bf24bae8728f18d2b10..0000000000000000000000000000000000000000
--- a/Processes/OnShellCheck/OnShellCheck.h
+++ /dev/null
@@ -1,52 +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.
- */
-
-#ifndef _corsika_process_on_shell_check_OnShellCheck_h_
-#define _corsika_process_on_shell_check_OnShellCheck_h_
-
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/process/SecondariesProcess.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::process {
-  namespace on_shell_check {
-    class OnShellCheck : public process::SecondariesProcess<OnShellCheck> {
-      double average_shift_ = 0;
-      double max_shift_ = 0;
-      double count_ = 0;
-
-    public:
-      OnShellCheck(const double vMassTolerance, const double vEnergyTolerance,
-                   const bool vError)
-          : mass_tolerance_(vMassTolerance)
-          , energy_tolerance_(vEnergyTolerance)
-          , throw_error_(vError) {}
-
-      ~OnShellCheck() {
-        std::cout << "OnShellCheck: summary" << std::endl
-                  << " particles shifted: " << int(count_) << std::endl;
-        if (count_)
-          std::cout << " average energy shift (%): " << average_shift_ / count_ * 100.
-                    << std::endl
-                    << " max. energy shift (%): " << max_shift_ * 100. << std::endl;
-      };
-
-      EProcessReturn DoSecondaries(corsika::setup::StackView&);
-
-      void Init();
-
-    private:
-      double mass_tolerance_;
-      double energy_tolerance_;
-      bool throw_error_;
-    };
-  } // namespace on_shell_check
-} // namespace corsika::process
-
-#endif
diff --git a/Processes/OnShellCheck/testOnShellCheck.cc b/Processes/OnShellCheck/testOnShellCheck.cc
deleted file mode 100644
index 77c64e178da2726fabcd7c3792af60d270417c25..0000000000000000000000000000000000000000
--- a/Processes/OnShellCheck/testOnShellCheck.cc
+++ /dev/null
@@ -1,88 +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/process/on_shell_check/OnShellCheck.h>
-
-#include <corsika/environment/Environment.h>
-#include <corsika/geometry/FourVector.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/CorsikaFenv.h>
-
-#include <corsika/setup/SetupStack.h>
-
-#include <catch2/catch.hpp>
-
-using namespace corsika;
-using namespace corsika::process::on_shell_check;
-using namespace corsika::units;
-using namespace corsika::units::si;
-
-TEST_CASE("OnShellCheck", "[processes]") {
-  feenableexcept(FE_INVALID);
-  using EnvType = setup::Environment;
-  EnvType env;
-  const geometry::CoordinateSystem& rootCS = env.GetCoordinateSystem();
-
-  // setup empty particle stack
-  setup::Stack stack;
-  stack.Clear();
-  // two energies
-  const HEPEnergyType E = 10_GeV;
-  // list of arbitrary particles
-  std::array const particleList{particles::Code::PiPlus, particles::Code::PiMinus,
-                                particles::Code::Helium, particles::Code::Gamma};
-
-  std::array const mass_shifts{1.1, 1.001, 1.0, 1.0};
-
-  SECTION("check particle masses") {
-
-    OnShellCheck check(1.e-2, 0.01, false);
-
-    check.Init();
-
-    // add primary particle to stack
-    auto particle = stack.AddParticle(
-        std::tuple<particles::Code, units::si::HEPEnergyType,
-                   corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
-            particles::Code::Proton, E,
-            corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
-            geometry::Point(rootCS, 0_m, 0_m, 0_m), 0_ns});
-    // view on secondary particles
-    setup::StackView view{particle};
-    // ref. to primary particle through the secondary view.
-    // only this way the secondary view is populated
-    auto projectile = view.GetProjectile();
-    // add secondaries, all with energies above the threshold
-    // only cut is by species
-    int count = -1;
-    for (auto proType : particleList) {
-      count++;
-      const auto pz = sqrt((E - particles::GetMass(proType) * mass_shifts[count]) *
-                           (E + particles::GetMass(proType) * mass_shifts[count]));
-      auto const momentum = corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, pz});
-      projectile.AddSecondary(std::tuple<particles::Code, units::si::HEPEnergyType,
-                                         corsika::stack::MomentumVector, geometry::Point,
-                                         units::si::TimeType>{
-          proType, E, momentum, geometry::Point(rootCS, 0_m, 0_m, 0_m), 0_ns});
-    }
-    check.DoSecondaries(view);
-    int i = -1;
-    for (auto& p : view) {
-      i++;
-      auto const Plab = corsika::geometry::FourVector(p.GetEnergy(), p.GetMomentum());
-      auto const m_kinetic = Plab.GetNorm();
-      if (i == 0)
-        CHECK(m_kinetic / particles::PiPlus::GetMass() == Approx(1));
-      else if (i == 1)
-        CHECK_FALSE(m_kinetic / particles::PiMinus::GetMass() == Approx(1));
-    }
-  }
-}
diff --git a/Processes/ParticleCut/CMakeLists.txt b/Processes/ParticleCut/CMakeLists.txt
deleted file mode 100644
index db1ee34c6db71a4013d19f3d35b2d9a5799ba24f..0000000000000000000000000000000000000000
--- a/Processes/ParticleCut/CMakeLists.txt
+++ /dev/null
@@ -1,66 +0,0 @@
-set (
-  MODEL_SOURCES
-  ParticleCut.cc
-)
-
-set (
-  MODEL_HEADERS
-  ParticleCut.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/particle_cut
-  )
-
-add_library (ProcessParticleCut STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessParticleCut ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-set_target_properties (
-  ProcessParticleCut
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessParticleCut
-  CORSIKAunits
-  CORSIKAparticles
-  CORSIKAprocesssequence
-  CORSIKAsetup
-  CORSIKAlogging
-  )
-
-target_include_directories (
-  ProcessParticleCut 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS ProcessParticleCut
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-  )
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST(testParticleCut SOURCES
-  testParticleCut.cc
-  ${MODEL_HEADERS}
-)
-
-target_link_libraries (
-  testParticleCut
-  ProcessParticleCut
-  CORSIKAunits
-  CORSIKAstackinterface
-  CORSIKAprocesssequence
-  CORSIKAsetup
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAtesting
-  )
diff --git a/Processes/ParticleCut/ParticleCut.cc b/Processes/ParticleCut/ParticleCut.cc
deleted file mode 100644
index 5176ab88737ff10f310ede66f397d861d49c73c1..0000000000000000000000000000000000000000
--- a/Processes/ParticleCut/ParticleCut.cc
+++ /dev/null
@@ -1,146 +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/logging/Logging.h>
-#include <corsika/process/particle_cut/ParticleCut.h>
-
-using namespace std;
-
-using namespace corsika;
-using namespace corsika::process;
-using namespace corsika::units::si;
-using namespace corsika::particles;
-using namespace corsika::setup;
-
-namespace corsika::process {
-  namespace particle_cut {
-
-    template <typename TParticle>
-    bool ParticleCut::ParticleIsBelowEnergyCut(TParticle const& vP) const {
-      auto const energyLab = vP.GetEnergy();
-      // nuclei
-      if (vP.GetPID() == particles::Code::Nucleus) {
-        // calculate energy per nucleon
-        auto const ElabNuc = energyLab / vP.GetNuclearA();
-        return (ElabNuc <= energy_cut_);
-      } else {
-        return (energyLab <= energy_cut_);
-      }
-    }
-
-    bool ParticleCut::ParticleIsEmParticle(Code vCode) const {
-      switch (vCode) {
-        case Code::Gamma:
-        case Code::Electron:
-        case Code::Positron:
-          return true;
-        default:
-          return false;
-      }
-    }
-
-    bool ParticleCut::ParticleIsInvisible(Code vCode) const {
-      switch (vCode) {
-        case Code::NuE:
-        case Code::NuEBar:
-        case Code::NuMu:
-        case Code::NuMuBar:
-          return true;
-
-        default:
-          return false;
-      }
-    }
-
-    template <typename TParticle>
-    bool ParticleCut::checkCutParticle(const TParticle& particle) {
-
-      const Code pid = particle.GetPID();
-      HEPEnergyType energy = particle.GetEnergy();
-      C8LOG_DEBUG(fmt::format("ParticleCut: checking {}, E= {} GeV, EcutTot={} GeV", pid,
-                              energy / 1_GeV,
-                              (fEmEnergy + fInvEnergy + fEnergy) / 1_GeV));
-      if (bCutEm && ParticleIsEmParticle(pid)) {
-        C8LOG_DEBUG("removing em. particle...");
-        fEmEnergy += energy;
-        uiEmCount += 1;
-        return true;
-      } else if (bCutInv && ParticleIsInvisible(pid)) {
-        C8LOG_DEBUG("removing inv. particle...");
-        fInvEnergy += energy;
-        uiInvCount += 1;
-        return true;
-      } else if (ParticleIsBelowEnergyCut(particle)) {
-        C8LOG_DEBUG("removing low en. particle...");
-        fEnergy += energy;
-        return true;
-      } else if (particle.GetTime() > 10_ms) {
-        C8LOG_DEBUG("removing OLD particle...");
-        fEnergy += energy;
-        return true;
-      }
-      return false; // this particle will not be removed/cut
-    }
-
-    void ParticleCut::DoSecondaries(corsika::setup::StackView& vS) {
-      auto particle = vS.begin();
-      while (particle != vS.end()) {
-        if (checkCutParticle(particle)) { particle.Delete(); }
-        ++particle; // next entry in SecondaryView
-      }
-    }
-
-    process::EProcessReturn ParticleCut::DoContinuous(
-        corsika::setup::Stack::ParticleType& particle,
-        corsika::setup::Trajectory const&) {
-      C8LOG_TRACE("ParticleCut::DoContinuous");
-      if (checkCutParticle(particle)) {
-        C8LOG_TRACE("removing during continuous");
-        particle.Delete();
-        // signal to upstream code that this particle was deleted
-        return process::EProcessReturn::eParticleAbsorbed;
-      }
-      return process::EProcessReturn::eOk;
-    }
-
-    ParticleCut::ParticleCut(const units::si::HEPEnergyType eCut, bool em, bool inv)
-        : energy_cut_(eCut)
-        , bCutEm(em)
-        , bCutInv(inv) {
-      fEmEnergy = 0_GeV;
-      uiEmCount = 0;
-      fInvEnergy = 0_GeV;
-      uiInvCount = 0;
-      fEnergy = 0_GeV;
-    }
-
-    // LCOV_EXCL_START
-    void ParticleCut::ShowResults() const {
-      C8LOG_INFO(fmt::format(
-          " ******************************\n"
-          " ParticleCut: \n"
-          " energy in em.  component (GeV):  {}\n"
-          " no. of em.  particles injected:  {}\n"
-          " energy in inv. component (GeV):  {}\n"
-          " no. of inv. particles injected:  {}\n"
-          " energy below particle cut (GeV): {}\n"
-          " ******************************",
-          fEmEnergy / 1_GeV, uiEmCount, fInvEnergy / 1_GeV, uiInvCount, fEnergy / 1_GeV));
-    }
-    // LCOV_EXCL_STOP
-
-    void ParticleCut::Reset() {
-      fEmEnergy = 0_GeV;
-      uiEmCount = 0;
-      fInvEnergy = 0_GeV;
-      uiInvCount = 0;
-      fEnergy = 0_GeV;
-    }
-
-  } // namespace particle_cut
-} // namespace corsika::process
diff --git a/Processes/ParticleCut/ParticleCut.h b/Processes/ParticleCut/ParticleCut.h
deleted file mode 100644
index b0e8977530a8fd7a800908bd6eb48519a411d9f0..0000000000000000000000000000000000000000
--- a/Processes/ParticleCut/ParticleCut.h
+++ /dev/null
@@ -1,67 +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/particles/ParticleProperties.h>
-#include <corsika/process/ContinuousProcess.h>
-#include <corsika/process/SecondariesProcess.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::process {
-  namespace particle_cut {
-    class ParticleCut : public process::SecondariesProcess<ParticleCut>,
-                        public corsika::process::ContinuousProcess<ParticleCut> {
-
-      units::si::HEPEnergyType const energy_cut_;
-      bool bCutEm;
-      bool bCutInv;
-
-      units::si::HEPEnergyType fEnergy = 0 * units::si::electronvolt;
-      units::si::HEPEnergyType fEmEnergy = 0 * units::si::electronvolt;
-      unsigned int uiEmCount = 0;
-      units::si::HEPEnergyType fInvEnergy = 0 * units::si::electronvolt;
-      unsigned int uiInvCount = 0;
-
-    public:
-      ParticleCut(const units::si::HEPEnergyType eCut, bool em, bool inv);
-
-      void DoSecondaries(corsika::setup::StackView&);
-
-      EProcessReturn DoContinuous(corsika::setup::Stack::ParticleType& vParticle,
-                                  corsika::setup::Trajectory const& vTrajectory);
-
-      corsika::units::si::LengthType MaxStepLength(
-          corsika::setup::Stack::ParticleType const&, corsika::setup::Trajectory const&) {
-        return units::si::meter * std::numeric_limits<double>::infinity();
-      }
-
-      units::si::HEPEnergyType GetECut() const { return energy_cut_; }
-      units::si::HEPEnergyType GetInvEnergy() const { return fInvEnergy; }
-      units::si::HEPEnergyType GetCutEnergy() const { return fEnergy; }
-      units::si::HEPEnergyType GetEmEnergy() const { return fEmEnergy; }
-      unsigned int GetNumberEmParticles() const { return uiEmCount; }
-      unsigned int GetNumberInvParticles() const { return uiInvCount; }
-
-      void ShowResults() const;
-      void Reset();
-
-    protected:
-      template <typename TParticle>
-      bool checkCutParticle(const TParticle& p);
-
-      template <typename TParticle>
-      bool ParticleIsBelowEnergyCut(TParticle const&) const;
-
-      bool ParticleIsEmParticle(particles::Code) const;
-      bool ParticleIsInvisible(particles::Code) const;
-    };
-  } // namespace particle_cut
-} // namespace corsika::process
diff --git a/Processes/ParticleCut/testParticleCut.cc b/Processes/ParticleCut/testParticleCut.cc
deleted file mode 100644
index c2343f76373acc2712f553e2803dd4ea10e02644..0000000000000000000000000000000000000000
--- a/Processes/ParticleCut/testParticleCut.cc
+++ /dev/null
@@ -1,197 +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/process/particle_cut/ParticleCut.h>
-
-#include <corsika/environment/Environment.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/CorsikaFenv.h>
-
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <catch2/catch.hpp>
-
-using namespace corsika;
-using namespace corsika::process::particle_cut;
-using namespace corsika::units;
-using namespace corsika::units::si;
-
-TEST_CASE("ParticleCut", "[processes]") {
-  feenableexcept(FE_INVALID);
-  using EnvType = setup::Environment;
-
-  EnvType env;
-  const geometry::CoordinateSystem& rootCS = env.GetCoordinateSystem();
-
-  // setup empty particle stack
-  setup::Stack stack;
-  stack.Clear();
-  // two energies
-  const HEPEnergyType Eabove = 1_TeV;
-  const HEPEnergyType Ebelow = 10_GeV;
-  // list of arbitrary particles
-  const std::vector<particles::Code> particleList = {
-      particles::Code::PiPlus,   particles::Code::PiMinus, particles::Code::KPlus,
-      particles::Code::KMinus,   particles::Code::K0Long,  particles::Code::K0Short,
-      particles::Code::Electron, particles::Code::MuPlus,  particles::Code::NuE,
-      particles::Code::Neutron,  particles::Code::NuMu};
-
-  // common staring point
-  const geometry::Point point0(rootCS, 0_m, 0_m, 0_m);
-
-  SECTION("cut on particle type: inv") {
-
-    ParticleCut cut(20_GeV, false, true);
-    CHECK(cut.GetECut() == 20_GeV);
-
-    // add primary particle to stack
-    auto particle = stack.AddParticle(
-        std::tuple<particles::Code, units::si::HEPEnergyType,
-                   corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
-            particles::Code::Proton, Eabove,
-            corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), point0, 0_ns});
-    // view on secondary particles
-    setup::StackView view(particle);
-    // ref. to primary particle through the secondary view.
-    // only this way the secondary view is populated
-    auto projectile = view.GetProjectile();
-    // add secondaries, all with energies above the threshold
-    // only cut is by species
-    for (auto proType : particleList)
-      projectile.AddSecondary(std::make_tuple(
-          proType, Eabove, corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
-          point0, 0_ns));
-    CHECK(view.getEntries() == 11);
-    CHECK(stack.getEntries() == 12);
-
-    cut.DoSecondaries(view);
-
-    CHECK(view.getEntries() == 9);
-    CHECK(cut.GetNumberInvParticles() == 2);
-    CHECK(cut.GetInvEnergy() / 1_GeV == 2000);
-  }
-
-  SECTION("cut on particle type: em") {
-
-    ParticleCut cut(20_GeV, true, false);
-
-    // add primary particle to stack
-    auto particle = stack.AddParticle(std::make_tuple(
-        particles::Code::Proton, Eabove,
-        corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), point0, 0_ns));
-    // view on secondary particles
-    corsika::setup::StackView view(particle);
-    // ref. to primary particle through the secondary view.
-    // only this way the secondary view is populated
-    auto projectile = view.GetProjectile();
-    // add secondaries, all with energies above the threshold
-    // only cut is by species
-    for (auto proType : particleList) {
-      projectile.AddSecondary(std::make_tuple(
-          proType, Eabove, corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
-          point0, 0_ns));
-    }
-    cut.DoSecondaries(view);
-
-    CHECK(view.getEntries() == 10);
-    CHECK(cut.GetNumberEmParticles() == 1);
-    CHECK(cut.GetEmEnergy() / 1_GeV == 1000);
-  }
-
-  SECTION("cut low energy") {
-    ParticleCut cut(20_GeV, true, true);
-
-    // add primary particle to stack
-    auto particle = stack.AddParticle(std::make_tuple(
-        particles::Code::Proton, Eabove,
-        corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), point0, 0_ns));
-    // view on secondary particles
-    setup::StackView view{particle};
-    // ref. to primary particle through the secondary view.
-    // only this way the secondary view is populated
-    auto projectile = view.GetProjectile();
-    // add secondaries, all with energies below the threshold
-    // only cut is by species
-    for (auto proType : particleList)
-      projectile.AddSecondary(std::make_tuple(
-          proType, Ebelow, corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
-          point0, 0_ns));
-    unsigned short A = 18;
-    unsigned short Z = 8;
-    projectile.AddSecondary(
-        std::make_tuple(particles::Code::Nucleus, Eabove * A,
-                        corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
-                        point0, 0_ns, A, Z));
-    projectile.AddSecondary(
-        std::make_tuple(particles::Code::Nucleus, Ebelow * A,
-                        corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
-                        point0, 0_ns, A, Z));
-
-    cut.DoSecondaries(view);
-
-    CHECK(view.getEntries() == 1);
-    CHECK(view.getSize() == 13);
-  }
-
-  SECTION("cut on time") {
-    ParticleCut cut(20_GeV, false, false);
-    const TimeType too_late = 1_s;
-
-    // add primary particle to stack
-    auto particle = stack.AddParticle(std::make_tuple(
-        particles::Code::Proton, Eabove,
-        corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), point0, 1_ns));
-    // view on secondary particles
-    corsika::setup::StackView view(particle);
-    // ref. to primary particle through the secondary view.
-    // only this way the secondary view is populated
-    auto projectile = view.GetProjectile();
-    // add secondaries, all with energies above the threshold
-    // only cut is by species
-    for (auto proType : particleList) {
-      projectile.AddSecondary(std::make_tuple(
-          proType, Eabove, corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
-          point0, too_late));
-    }
-    cut.DoSecondaries(view);
-
-    CHECK(view.getEntries() == 0);
-    CHECK(cut.GetCutEnergy() / 1_GeV == 11000);
-    cut.Reset();
-    CHECK(cut.GetCutEnergy() == 0_GeV);
-  }
-
-  corsika::setup::Trajectory const track = setup::testing::make_track<setup::Trajectory>(
-      geometry::Line{point0,
-                     geometry::Vector<units::si::SpeedType::dimension_type>{
-                         rootCS, {0_m / second, 0_m / second, -units::constants::c}}},
-      12_m / units::constants::c);
-
-  SECTION("cut on DoContinous, just invisibles") {
-
-    ParticleCut cut(20_GeV, false, true);
-    CHECK(cut.GetECut() == 20_GeV);
-
-    // add particles, all with energies above the threshold
-    // only cut is by species
-    for (auto proType : particleList) {
-      auto particle = stack.AddParticle(std::make_tuple(
-          proType, Eabove, corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
-          point0, 0_ns));
-      cut.DoContinuous(particle, track);
-    }
-
-    CHECK(stack.getEntries() == 9);
-    CHECK(cut.GetNumberInvParticles() == 2);
-    CHECK(cut.GetInvEnergy() / 1_GeV == 2000);
-  }
-}
diff --git a/Processes/Proposal/CMakeLists.txt b/Processes/Proposal/CMakeLists.txt
deleted file mode 100644
index 08a37a68b441536a1fba53314165b00991d64d19..0000000000000000000000000000000000000000
--- a/Processes/Proposal/CMakeLists.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-file(READ CMakeLists_PROPOSAL.txt overwrite_CMakeLists_PROPOSAL_txt)
-file(WRITE PROPOSAL/CMakeLists.txt ${overwrite_CMakeLists_PROPOSAL_txt})
-add_subdirectory (PROPOSAL)
-
-FILE (GLOB MODEL_SOURCES *.cc)
-FILE (GLOB MODEL_HEADERS *.h)
-SET (MODEL_NAMESPACE corsika/process/proposal)
-
-ADD_LIBRARY (ProcessProposal STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessProposal ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-SET_TARGET_PROPERTIES (
-  ProcessProposal
-  PROPERTIES VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  )
-
-TARGET_LINK_LIBRARIES (
-  ProcessProposal
-  CORSIKAparticles
-  CORSIKAutilities
-  CORSIKAunits
-  CORSIKAthirdparty
-  CORSIKAgeometry
-  CORSIKAlogging
-  CORSIKAenvironment
-  ProcessParticleCut
-  PROPOSAL::PROPOSAL
-  )
-
-TARGET_INCLUDE_DIRECTORIES (
-  ProcessProposal
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-INSTALL (
-  TARGETS ProcessProposal
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-  )
diff --git a/Processes/Proposal/ContinuousProcess.cc b/Processes/Proposal/ContinuousProcess.cc
deleted file mode 100644
index fb4d528fa83d41178739c3a1c25888feff808a16..0000000000000000000000000000000000000000
--- a/Processes/Proposal/ContinuousProcess.cc
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * (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.
- */
-
-#include <PROPOSAL/PROPOSAL.h>
-#include <corsika/environment/IMediumModel.h>
-#include <corsika/process/proposal/ContinuousProcess.h>
-#include <corsika/process/proposal/Interaction.h>
-#include <corsika/setup/SetupEnvironment.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/COMBoost.h>
-#include <corsika/logging/Logging.h>
-
-#include <iostream>
-
-namespace corsika::process::proposal {
-
-  void ContinuousProcess::BuildCalculator(particles::Code code,
-                                          environment::NuclearComposition const& comp) {
-    // search crosssection builder for given particle
-    auto p_cross = cross.find(code);
-    if (p_cross == cross.end())
-      throw std::runtime_error("PROPOSAL could not find corresponding builder");
-
-    // interpolate the crosssection for given media and energy cut. These may
-    // take some minutes if you have to build the tables and cannot read the
-    // from disk
-    auto c = p_cross->second(media.at(comp.hash()), emCut_);
-
-    // Build displacement integral and scattering object and interpolate them too and
-    // saved in the calc map by a key build out of a hash of composed of the component and
-    // particle code.
-    auto disp = PROPOSAL::make_displacement(c, true);
-    auto scatter =
-        PROPOSAL::make_scattering("highland", particle[code], media.at(comp.hash()));
-    calc[std::make_pair(comp.hash(), code)] =
-        std::make_tuple(std::move(disp), std::move(scatter));
-  }
-
-  template <>
-  ContinuousProcess::ContinuousProcess(setup::Environment const& _env,
-                                       corsika::units::si::HEPEnergyType _emCut)
-      : ProposalProcessBase(_env, _emCut) {}
-
-  template <>
-  void ContinuousProcess::Scatter(setup::Stack::ParticleType& vP,
-                                  corsika::units::si::HEPEnergyType const& loss,
-                                  corsika::units::si::GrammageType const& grammage) {
-    using namespace corsika::units::si; // required for operator::_MeV
-
-    // Get or build corresponding calculators
-    auto c = GetCalculator(vP, calc);
-
-    // Cast corsika vector to proposal vector
-    auto vP_dir = vP.GetDirection();
-    auto d = vP_dir.GetComponents();
-    auto direction = PROPOSAL::Vector3D(d.GetX().magnitude(), d.GetY().magnitude(),
-                                        d.GetZ().magnitude());
-
-    auto E_f = vP.GetEnergy() - loss;
-
-    // draw random numbers required for scattering process
-    std::uniform_real_distribution<double> distr(0., 1.);
-    auto rnd = array<double, 4>();
-    for (auto& it : rnd) it = distr(fRNG);
-
-    // calculate deflection based on particle energy, loss
-    auto [mean_dir, final_dir] = get<eSCATTERING>(c->second)->Scatter(
-        grammage / 1_g * square(1_cm), vP.GetEnergy() / 1_MeV, E_f / 1_MeV, direction,
-        rnd);
-
-    // TODO: neglect mean direction deflection because Trajectory is a const ref
-    (void)mean_dir;
-
-    // update particle direction after continuous loss caused by multiple
-    // scattering
-    auto vec = corsika::geometry::QuantityVector(
-        final_dir.GetX() * E_f, final_dir.GetY() * E_f, final_dir.GetZ() * E_f);
-    vP.SetMomentum(corsika::stack::MomentumVector(vP_dir.GetCoordinateSystem(), vec));
-  }
-
-  template <>
-  EProcessReturn ContinuousProcess::DoContinuous(setup::Stack::ParticleType& vP,
-                                                 setup::Trajectory const& vT) {
-    using namespace corsika::units::si; // required for operator::_MeV
-
-    if (!CanInteract(vP.GetPID())) return process::EProcessReturn::eOk;
-    if (vT.GetLength() == 0_m) return process::EProcessReturn::eOk;
-
-    // calculate passed grammage
-    auto dX = vP.GetNode()->GetModelProperties().IntegratedGrammage(vT, vT.GetLength());
-
-    // Get or build corresponding track integral calculator and solve the
-    // integral
-    auto c = GetCalculator(vP, calc);
-    auto final_energy = get<eDISPLACEMENT>(c->second)->UpperLimitTrackIntegral(
-                            vP.GetEnergy() / 1_MeV, dX / 1_g * 1_cm * 1_cm) *
-                        1_MeV;
-    auto dE = vP.GetEnergy() - final_energy;
-    energy_lost_ += dE;
-
-    // if the particle has a charge take multiple scattering into account
-    if (vP.GetChargeNumber() != 0) Scatter(vP, dE, dX);
-    vP.SetEnergy(final_energy);
-    vP.SetMomentum(vP.GetMomentum() * vP.GetEnergy() / vP.GetMomentum().GetNorm());
-    return process::EProcessReturn::eOk;
-  }
-
-  template <>
-  units::si::LengthType ContinuousProcess::MaxStepLength(
-      setup::Stack::ParticleType const& vP, setup::Trajectory const& vT) {
-    using namespace corsika::units::si; // required for operator::_MeV
-
-    if (!CanInteract(vP.GetPID()))
-      return units::si::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.
-    //
-    // 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_);
-
-    // 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) *
-                    1_g / square(1_cm);
-
-    // return it in distance aequivalent
-    auto dist = vP.GetNode()->GetModelProperties().ArclengthFromGrammage(vT, grammage);
-    C8LOG_TRACE("PROPOSAL::MaxStepLength X={} g/cm2, l={} m ",
-                grammage / 1_g * square(1_cm), dist / 1_m);
-    return dist;
-  }
-
-  void ContinuousProcess::showResults() const {
-    using namespace corsika::units::si; // required for operator::_MeV
-    std::cout << " ******************************" << std::endl
-              << " PROCESS::ContinuousProcess: " << std::endl;
-    std::cout << " energy lost dE (GeV)      :  " << energy_lost_ / 1_GeV << std::endl;
-  }
-
-  void ContinuousProcess::reset() {
-    using namespace corsika::units::si; // required for operator::_MeV
-    energy_lost_ = 0_GeV;
-  }
-
-} // namespace corsika::process::proposal
diff --git a/Processes/Proposal/Interaction.cc b/Processes/Proposal/Interaction.cc
deleted file mode 100644
index f5ce68a800d56a2f855a46670638400b96fbffe5..0000000000000000000000000000000000000000
--- a/Processes/Proposal/Interaction.cc
+++ /dev/null
@@ -1,114 +0,0 @@
-/*
- * (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.
- */
-
-#include <corsika/environment/IMediumModel.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/process/proposal/Interaction.h>
-#include <corsika/setup/SetupEnvironment.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/COMBoost.h>
-#include <limits>
-#include <memory>
-#include <random>
-#include <tuple>
-
-namespace corsika::process::proposal {
-
-  template <>
-  Interaction::Interaction(setup::Environment const& _env,
-                           corsika::units::si::HEPEnergyType _emCut)
-      : ProposalProcessBase(_env, _emCut) {}
-
-  void Interaction::BuildCalculator(particles::Code code,
-                                    environment::NuclearComposition const& comp) {
-    // search crosssection builder for given particle
-    auto p_cross = cross.find(code);
-    if (p_cross == cross.end())
-      throw std::runtime_error("PROPOSAL could not find corresponding builder");
-
-    // interpolate the crosssection for given media and energy cut. These may
-    // take some minutes if you have to build the tables and cannot read the
-    // from disk
-    auto c = p_cross->second(media.at(comp.hash()), emCut_);
-
-    // Look which interactions take place and build the corresponding
-    // interaction and secondarie builder. The interaction integral will
-    // interpolated too and saved in the calc map by a key build out of a hash
-    // of composed of the component and particle code.
-    auto inter_types = PROPOSAL::CrossSectionVector::GetInteractionTypes(c);
-    calc[std::make_pair(comp.hash(), code)] = std::make_tuple(
-        PROPOSAL::make_secondaries(inter_types, particle[code], media.at(comp.hash())),
-        PROPOSAL::make_interaction(c, true));
-  }
-
-  template <>
-  corsika::process::EProcessReturn Interaction::DoInteraction(setup::StackView& view) {
-    using namespace corsika::units::si; // required for operator::_MeV
-
-    auto const projectile = view.GetProjectile();
-
-    if (CanInteract(projectile.GetPID())) {
-      // Get or build corresponding calculators
-      auto c = GetCalculator(projectile, calc);
-
-      // Get the rates of the interaction types for every component.
-      std::uniform_real_distribution<double> distr(0., 1.);
-
-      // sample a interaction-type, loss and component
-      auto rates = get<eINTERACTION>(c->second)->Rates(projectile.GetEnergy() / 1_MeV);
-      auto [type, comp_ptr, v] = get<eINTERACTION>(c->second)->SampleLoss(
-          projectile.GetEnergy() / 1_MeV, rates, distr(fRNG));
-
-      // Read how much random numbers are required to calculate the secondaries.
-      // Calculate the secondaries and deploy them on the corsika stack.
-      auto rnd =
-          vector<double>(get<eSECONDARIES>(c->second)->RequiredRandomNumbers(type));
-      for (auto& it : rnd) it = distr(fRNG);
-      auto point = PROPOSAL::Vector3D(projectile.GetPosition().GetX() / 1_cm,
-                                      projectile.GetPosition().GetY() / 1_cm,
-                                      projectile.GetPosition().GetZ() / 1_cm);
-      auto projectile_dir = projectile.GetDirection();
-      auto d = projectile_dir.GetComponents();
-      auto direction = PROPOSAL::Vector3D(d.GetX().magnitude(), d.GetY().magnitude(),
-                                          d.GetZ().magnitude());
-      auto loss = make_tuple(static_cast<int>(type), point, direction,
-                             v * projectile.GetEnergy() / 1_MeV, 0.);
-      auto sec = get<eSECONDARIES>(c->second)->CalculateSecondaries(
-          projectile.GetEnergy() / 1_MeV, loss, *comp_ptr, rnd);
-      for (auto& s : sec) {
-        auto E = get<PROPOSAL::Loss::ENERGY>(s) * 1_MeV;
-        auto vec = corsika::geometry::QuantityVector(
-            get<PROPOSAL::Loss::DIRECTION>(s).GetX() * E,
-            get<PROPOSAL::Loss::DIRECTION>(s).GetY() * E,
-            get<PROPOSAL::Loss::DIRECTION>(s).GetZ() * E);
-        auto p =
-            corsika::stack::MomentumVector(projectile_dir.GetCoordinateSystem(), vec);
-        auto sec_code = corsika::particles::ConvertFromPDG(
-            static_cast<particles::PDGCode>(get<PROPOSAL::Loss::TYPE>(s)));
-        view.AddSecondary(
-            make_tuple(sec_code, E, p, projectile.GetPosition(), projectile.GetTime()));
-      }
-    }
-    return process::EProcessReturn::eOk;
-  }
-
-  template <>
-  corsika::units::si::GrammageType Interaction::GetInteractionLength(
-      setup::Stack::StackIterator const& projectile) {
-    using namespace corsika::units::si; // required for operator::_MeV
-
-    if (CanInteract(projectile.GetPID())) {
-      auto c = GetCalculator(projectile, calc);
-      return get<eINTERACTION>(c->second)->MeanFreePath(projectile.GetEnergy() / 1_MeV) *
-             1_g / (1_cm * 1_cm);
-    }
-    return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
-  }
-} // namespace corsika::process::proposal
diff --git a/Processes/Proposal/ProposalProcessBase.cc b/Processes/Proposal/ProposalProcessBase.cc
deleted file mode 100644
index a29dd15c2611a268a358094503d14a5b09c7a98c..0000000000000000000000000000000000000000
--- a/Processes/Proposal/ProposalProcessBase.cc
+++ /dev/null
@@ -1,79 +0,0 @@
-/*
- * (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.
- */
-
-#include <corsika/environment/IMediumModel.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/process/proposal/ProposalProcessBase.h>
-#include <corsika/setup/SetupEnvironment.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/COMBoost.h>
-#include <cstdlib>
-#include <iostream>
-#include <limits>
-#include <memory>
-#include <random>
-#include <tuple>
-
-namespace corsika::process::proposal {
-  bool ProposalProcessBase::CanInteract(particles::Code pcode) const {
-    if (std::find(begin(tracked), end(tracked), pcode) != end(tracked)) return true;
-    return false;
-  }
-
-  ProposalProcessBase::ProposalProcessBase(setup::Environment const& _env,
-                                           corsika::units::si::HEPEnergyType _emCut)
-      : emCut_(_emCut)
-      , fRNG(corsika::random::RNGManager::GetInstance().GetRandomStream("proposal")) {
-    using namespace corsika::units::si; // required for operator::_MeV
-    _env.GetUniverse()->walk([&](auto& vtn) {
-      if (vtn.HasModelProperties()) {
-        const auto& prop = vtn.GetModelProperties();
-        const auto& medium = mediumData(prop.medium(corsika::geometry::Point(
-            geometry::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem(), 0_cm,
-            0_cm, 0_cm)));
-
-        auto comp_vec = std::vector<PROPOSAL::Components::Component>();
-        const auto& comp = prop.GetNuclearComposition();
-        auto frac_iter = comp.GetFractions().cbegin();
-        for (auto& pcode : comp.GetComponents()) {
-          comp_vec.emplace_back(GetName(pcode), GetNucleusZ(pcode), GetNucleusA(pcode),
-                                *frac_iter);
-          ++frac_iter;
-        }
-
-        media[comp.hash()] =
-            PROPOSAL::Medium(medium.name(), medium.Ieff(), -medium.Cbar(), medium.aa(),
-                             medium.sk(), medium.x0(), medium.x1(), medium.dlt0(),
-                             medium.corrected_density(), comp_vec);
-      }
-    });
-
-    PROPOSAL::InterpolationDef::order_of_interpolation = 2;
-    PROPOSAL::InterpolationDef::nodes_cross_section = 100;
-    PROPOSAL::InterpolationDef::nodes_propagate = 1000;
-
-    //! If corsika data exist store interpolation tables to the corresponding
-    //! path, otherwise interpolation tables would only stored in main memory if
-    //! no explicit intrpolation def is specified.
-    if (auto data_path = std::getenv("CORSIKA_DATA")) {
-      PROPOSAL::InterpolationDef::path_to_tables = std::string(data_path) + "/PROPOSAL";
-    } else {
-      throw std::runtime_error(
-          "It is not recommended to run PROPOSAL without its tables in "
-          "$CORSIKA_DATA/PROPOSAL. This would be extremely slow. Please provide the "
-          "table directory. ");
-    }
-  }
-
-  size_t ProposalProcessBase::hash::operator()(const calc_key_t& p) const noexcept {
-    return p.first ^ std::hash<particles::Code>{}(p.second);
-  }
-
-} // namespace corsika::process::proposal
diff --git a/Processes/Pythia/CMakeLists.txt b/Processes/Pythia/CMakeLists.txt
deleted file mode 100644
index f4fab94931ae0aba3aeb08e33e0ebe6e13e0f2d3..0000000000000000000000000000000000000000
--- a/Processes/Pythia/CMakeLists.txt
+++ /dev/null
@@ -1,73 +0,0 @@
-set (
-  MODEL_SOURCES
-  Decay.cc
-  Random.cc
-  Interaction.cc
-  )
-
-set (
-  MODEL_HEADERS
-  Decay.h
-  Random.h
-  Interaction.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/pythia
-  )
-
-add_library (ProcessPythia8 STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessPythia8 ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-
-set_target_properties (
-  ProcessPythia8
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessPythia8
-  CORSIKAprocesssequence
-  CORSIKAparticles
-  CORSIKAutilities
-  CORSIKAunits
-  CORSIKAthirdparty
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAsetup
-  CORSIKArandom
-  C8::ext::pythia8
-  )
-
-target_include_directories (
-  ProcessPythia8
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS ProcessPythia8
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-  )
-
-
-# --------------------
-# code unit testing
-
-CORSIKA_ADD_TEST(testPythia8
-  SOURCES
-  testPythia8.cc
-  ${MODEL_HEADERS}
-)
-target_link_libraries (
-  testPythia8
-  ProcessPythia8
-  CORSIKAtesting
-  )
-
diff --git a/Processes/Pythia/Decay.cc b/Processes/Pythia/Decay.cc
deleted file mode 100644
index 92920dfbacdcaa809df665db59ae57bc70d8b46c..0000000000000000000000000000000000000000
--- a/Processes/Pythia/Decay.cc
+++ /dev/null
@@ -1,267 +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 <Pythia8/Pythia.h>
-#include <corsika/process/pythia/Decay.h>
-#include <corsika/process/pythia/Random.h>
-
-#include <corsika/geometry/FourVector.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/utl/COMBoost.h>
-
-using std::cout;
-using std::endl;
-using std::tuple;
-using std::vector;
-
-using namespace corsika;
-using namespace corsika::setup;
-using View = corsika::setup::StackView;
-using Particle = corsika::setup::Stack::ParticleType;
-using Track = Trajectory;
-
-namespace corsika::process::pythia {
-
-  Decay::Decay(const bool print_listing)
-      : print_listing_(print_listing) {
-
-    // set random number generator in pythia
-    Pythia8::RndmEngine* rndm = new corsika::process::pythia::Random();
-    fPythia.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
-    // fPythia.particleData.reInit("/home/felix/ngcorsika/corsika-build/include/corsika/particles/ParticleData.xml");
-    // fPythia.particleData.checkTable();
-
-    fPythia.readString("Next:numberShowInfo = 0");
-    fPythia.readString("Next:numberShowProcess = 0");
-    fPythia.readString("Next:numberShowEvent = 0");
-
-    fPythia.readString("Print:quiet = on");
-    fPythia.readString("Check:particleData = 0");
-
-    /*
-       switching off event check in pythia is needed to allow decays that are off-shell
-       according to the mass definition in pythia.
-       the consistency of particle masses between event generators is an unsolved issues
-    */
-    cout << "Pythia::Init: switching off event checking in pythia.." << endl;
-    fPythia.readString("Check:event = 1");
-
-    fPythia.readString("ProcessLevel:all = off");
-    fPythia.readString("ProcessLevel:resonanceDecays = off");
-
-    // making sure
-    SetStable(particles::Code::Pi0);
-
-    //    fPythia.particleData.readString("59:m0 = 101.00");
-
-    if (!fPythia.init())
-      throw std::runtime_error("Pythia::Decay: Initialization failed!");
-  }
-
-  Decay::Decay(std::set<particles::Code> vHandled)
-      : handleAllDecays_(false)
-      , handledDecays_(vHandled) {}
-
-  Decay::~Decay() { cout << "Pythia::Decay n=" << fCount << endl; }
-
-  bool Decay::CanHandleDecay(const particles::Code vParticleCode) {
-    using namespace corsika::particles;
-    // 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 ||
-        vParticleCode == Code::NuTau || vParticleCode == Code::NuEBar ||
-        vParticleCode == Code::NuMuBar || vParticleCode == Code::NuTauBar ||
-        vParticleCode == Code::Electron || vParticleCode == Code::Positron)
-      return false;
-    else if (CanDecay(vParticleCode)) // non-zero for particles known to sibyll
-      return true;
-    else
-      return false;
-  }
-
-  void Decay::SetHandleDecay(const particles::Code vParticleCode) {
-    handleAllDecays_ = false;
-    cout << "Pythia::Decay: set to handle decay of " << vParticleCode << endl;
-    if (Decay::CanHandleDecay(vParticleCode))
-      handledDecays_.insert(vParticleCode);
-    else
-      throw std::runtime_error("this decay can not be handled by pythia!");
-  }
-
-  void Decay::SetHandleDecay(const vector<particles::Code> vParticleList) {
-    handleAllDecays_ = false;
-    for (auto p : vParticleList) SetHandleDecay(p);
-  }
-
-  bool Decay::IsDecayHandled(const corsika::particles::Code vParticleCode) {
-    if (handleAllDecays_ && CanHandleDecay(vParticleCode))
-      return true;
-    else
-      return handledDecays_.find(vParticleCode) != Decay::handledDecays_.end();
-  }
-
-  bool Decay::IsStable(const particles::Code vCode) {
-    return fPythia.particleData.canDecay(static_cast<int>(particles::GetPDG(vCode)));
-  }
-
-  void Decay::PrintDecayConfig(const particles::Code vCode) {
-    cout << "Decay: Pythia decay configuration:" << endl;
-    cout << vCode << " is ";
-    if (IsStable(vCode))
-      cout << "stable" << endl;
-    else
-      cout << "unstable" << endl;
-  }
-
-  void Decay::PrintDecayConfig() {
-    cout << "Pythia::Decay: decay configuration:" << endl;
-    if (handleAllDecays_)
-      cout << " all particles known to Pythia are handled by Pythia::Decay!" << endl;
-    else
-      for (auto& pCode : handledDecays_)
-        cout << "Decay of " << pCode << " is handled by Pythia!" << endl;
-  }
-
-  void Decay::SetStable(const vector<particles::Code> particleList) {
-    for (auto p : particleList) Decay::SetStable(p);
-  }
-
-  bool Decay::CanDecay(const particles::Code pCode) {
-    std::cout << "Pythia::Decay: checking if particle: " << pCode
-              << " can decay in PYTHIA? ";
-    const bool ans =
-        fPythia.particleData.canDecay(static_cast<int>(particles::GetPDG(pCode)));
-    std::cout << ans << std::endl;
-    return ans;
-  }
-
-  void Decay::SetUnstable(const particles::Code pCode) {
-    cout << "Pythia::Decay: setting " << pCode << " unstable.." << endl;
-    fPythia.particleData.mayDecay(static_cast<int>(particles::GetPDG(pCode)), true);
-  }
-
-  void Decay::SetStable(const particles::Code pCode) {
-    cout << "Pythia::Decay: setting " << pCode << " stable.." << endl;
-    fPythia.particleData.mayDecay(static_cast<int>(particles::GetPDG(pCode)), false);
-  }
-
-  template <>
-  units::si::TimeType Decay::GetLifetime(Particle const& vP) {
-    using namespace units::si;
-
-    const auto pid = vP.GetPID();
-    if (CanDecay(pid)) {
-      HEPEnergyType E = vP.GetEnergy();
-      HEPMassType m = vP.GetMass();
-
-      const double gamma = E / m;
-
-      const TimeType t0 = particles::GetLifetime(pid);
-      auto const lifetime = gamma * t0;
-      cout << "Pythia::Decay: code: " << vP.GetPID() << endl;
-      cout << "Pythia::Decay: MinStep: t0: " << t0 << endl;
-      cout << "Pythia::Decay: MinStep: energy: " << E / 1_GeV << " GeV" << endl;
-      cout << "Pythia::Decay: momentum: " << vP.GetMomentum().GetComponents() / 1_GeV
-           << " GeV" << endl;
-      cout << "Pythia::Decay: MinStep: gamma: " << gamma << endl;
-      cout << "Pythia::Decay: MinStep: tau: " << lifetime << endl;
-
-      return lifetime;
-    } else
-      return std::numeric_limits<double>::infinity() * 1_s;
-  }
-
-  template <>
-  void Decay::DoDecay(View& view) {
-    using geometry::Point;
-    using namespace units;
-    using namespace units::si;
-
-    auto const projectile = view.GetProjectile();
-
-    auto const& decayPoint = projectile.GetPosition();
-    auto const t0 = projectile.GetTime();
-
-    auto const& labMomentum = projectile.GetMomentum();
-    geometry::CoordinateSystem const& labCS = labMomentum.GetCoordinateSystem();
-
-    // define target kinematics in lab frame
-    // define boost to and from CoM frame
-    // CoM frame definition in Pythia projectile: +z
-    utl::COMBoost const boost(labMomentum, projectile.GetMass());
-    auto const& rotatedCS = boost.GetRotatedCS();
-
-    fCount++;
-
-    // pythia stack
-    Pythia8::Event& event = fPythia.event;
-    event.reset();
-
-    auto const particleId = projectile.GetPID();
-
-    // set particle unstable
-    Decay::SetUnstable(particleId);
-
-    // input particle PDG
-    auto const pdgCode = static_cast<int>(particles::GetPDG(particleId));
-
-    double constexpr px = 0;
-    double constexpr py = 0;
-    double constexpr pz = 0;
-    double const en = projectile.GetMass() / 1_GeV;
-    double const m = en;
-
-    // add particle to pythia stack
-    event.append(pdgCode, 1, 0, 0, px, py, pz, en, m);
-
-    if (!fPythia.next())
-      throw std::runtime_error("Pythia::Decay: decay failed!");
-    else
-      cout << "Pythia::Decay: particles after decay: " << event.size() << endl;
-
-    if (print_listing_) {
-      // list final state
-      event.list();
-    }
-
-    // loop over final state
-    for (int i = 0; i < event.size(); ++i)
-      if (event[i].isFinal()) {
-        auto const pyId =
-            particles::ConvertFromPDG(static_cast<particles::PDGCode>(event[i].id()));
-        HEPEnergyType const Erest = event[i].e() * 1_GeV;
-        MomentumVector const pRest(
-            rotatedCS,
-            {event[i].px() * 1_GeV, event[i].py() * 1_GeV, event[i].pz() * 1_GeV});
-        geometry::FourVector const fourMomRest{Erest, pRest};
-        auto const fourMomLab = boost.fromCoM(fourMomRest);
-
-        cout << "particle: id=" << pyId << " momentum="
-             << fourMomLab.GetSpaceLikeComponents().GetComponents(labCS) / 1_GeV
-             << " energy=" << fourMomLab.GetTimeLikeComponent() << endl;
-
-        view.AddSecondary(
-            tuple<particles::Code, units::si::HEPEnergyType,
-                  corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
-                pyId, fourMomLab.GetTimeLikeComponent(),
-                fourMomLab.GetSpaceLikeComponents(), decayPoint, t0});
-      }
-
-    // set particle stable
-    Decay::SetStable(particleId);
-  }
-
-} // namespace corsika::process::pythia
diff --git a/Processes/Pythia/Decay.h b/Processes/Pythia/Decay.h
deleted file mode 100644
index 51a43b7e3824ec399eefa4bf96893144cb2db8f4..0000000000000000000000000000000000000000
--- a/Processes/Pythia/Decay.h
+++ /dev/null
@@ -1,80 +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 <Pythia8/Pythia.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/process/DecayProcess.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/geometry/FourVector.h>
-
-namespace corsika::process {
-
-  namespace pythia {
-
-    typedef corsika::geometry::Vector<corsika::units::si::hepmomentum_d> MomentumVector;
-
-    class Decay : public corsika::process::DecayProcess<Decay> {
-      int fCount = 0;
-      bool handleAllDecays_ = true;
-      bool print_listing_ = false;
-
-    public:
-      Decay(const bool print_listing = false);
-      Decay(std::set<particles::Code>);
-      ~Decay();
-
-      // is Pythia::Decay set to handle the decay of this particle?
-      bool IsDecayHandled(const corsika::particles::Code);
-
-      // is decay possible in principle?
-      bool CanHandleDecay(const corsika::particles::Code);
-
-      // set Pythia::Decay to handle the decay of this particle!
-      void SetHandleDecay(const corsika::particles::Code);
-      // set Pythia::Decay to handle the decay of this list of particles!
-      void SetHandleDecay(const std::vector<particles::Code>);
-      // set Pythia::Decay to handle all particle decays
-      void SetHandleAllDecays();
-
-      // print internal configuration for this particle
-      void PrintDecayConfig(const corsika::particles::Code);
-      // print configuration of decays in corsika
-      void PrintDecayConfig();
-
-      bool CanDecay(const corsika::particles::Code);
-
-      /**
-       In this function PYTHIA is asked for the lifetime of the input particle.
-       Unknown particles should return an infinite lifetime so that another decay process
-       can act on the particle later on.
-     */
-
-      template <typename TParticle>
-      corsika::units::si::TimeType GetLifetime(TParticle const&);
-
-      /**
-       In this function PYTHIA is called to execute the decay of the input particle.
-     */
-
-      template <typename TSecondaryView>
-      void DoDecay(TSecondaryView&);
-
-    private:
-      void SetUnstable(const corsika::particles::Code);
-      void SetStable(const corsika::particles::Code);
-      void SetStable(const std::vector<particles::Code>);
-      bool IsStable(const corsika::particles::Code);
-
-      Pythia8::Pythia fPythia;
-      std::set<particles::Code> handledDecays_;
-    };
-
-  } // namespace pythia
-} // namespace corsika::process
diff --git a/Processes/Pythia/Interaction.cc b/Processes/Pythia/Interaction.cc
deleted file mode 100644
index 7a0dbfa64878c77d3fbed0b6fd58798219dc0b38..0000000000000000000000000000000000000000
--- a/Processes/Pythia/Interaction.cc
+++ /dev/null
@@ -1,405 +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/process/pythia/Interaction.h>
-
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/geometry/FourVector.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/utl/COMBoost.h>
-
-#include <tuple>
-
-using std::cout;
-using std::endl;
-using std::tuple;
-
-using namespace corsika;
-using namespace corsika::setup;
-using SecondaryView = corsika::setup::StackView;
-using Particle = corsika::setup::Stack::ParticleType;
-
-namespace corsika::process::pythia {
-
-  typedef corsika::geometry::Vector<corsika::units::si::hepmomentum_d> MomentumVector;
-
-  Interaction::Interaction(const bool print_listing)
-      : print_listing_(print_listing) {
-
-    cout << "Pythia::Interaction n=" << fCount << endl;
-
-    using random::RNGManager;
-
-    // initialize Pythia
-    if (!fInitialized) {
-
-      fPythia.readString("Print:quiet = on");
-      // TODO: proper process initialization for MinBias needed
-      fPythia.readString("HardQCD:all = on");
-      fPythia.readString("ProcessLevel:resonanceDecays = off");
-
-      fPythia.init();
-
-      // any decays in pythia? if yes need to define which particles
-      if (fInternalDecays) {
-        // 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<particles::Code> HadronsWeWantTrackedByCorsika = {
-            particles::Code::PiPlus,     particles::Code::PiMinus,
-            particles::Code::Pi0,        particles::Code::KMinus,
-            particles::Code::KPlus,      particles::Code::K0Long,
-            particles::Code::K0Short,    particles::Code::SigmaPlus,
-            particles::Code::SigmaMinus, particles::Code::Lambda0,
-            particles::Code::Xi0,        particles::Code::XiMinus,
-            particles::Code::OmegaMinus, particles::Code::DPlus,
-            particles::Code::DMinus,     particles::Code::D0,
-            particles::Code::D0Bar};
-
-        Interaction::SetParticleListStable(HadronsWeWantTrackedByCorsika);
-      }
-
-      // basic initialization of cross section routines
-      fSigma.init(&fPythia.info, fPythia.settings, &fPythia.particleData, &fPythia.rndm);
-
-      fInitialized = true;
-    }
-  }
-
-  void Interaction::SetParticleListStable(
-      std::vector<particles::Code> const& particleList) {
-    for (auto p : particleList) Interaction::SetStable(p);
-  }
-
-  void Interaction::SetUnstable(const particles::Code pCode) {
-    cout << "Pythia::Interaction: setting " << pCode << " unstable.." << endl;
-    fPythia.particleData.mayDecay(static_cast<int>(particles::GetPDG(pCode)), true);
-  }
-
-  void Interaction::SetStable(const particles::Code pCode) {
-    cout << "Pythia::Interaction: setting " << pCode << " stable.." << endl;
-    fPythia.particleData.mayDecay(static_cast<int>(particles::GetPDG(pCode)), false);
-  }
-
-  void Interaction::ConfigureLabFrameCollision(
-      const particles::Code BeamId, const particles::Code TargetId,
-      const units::si::HEPEnergyType BeamEnergy) {
-    using namespace units::si;
-    // Pythia configuration of the current event
-    // very clumsy. I am sure this can be done better..
-
-    // set beam
-    // beam id for pythia
-    auto const pdgBeam = static_cast<int>(particles::GetPDG(BeamId));
-    std::stringstream stBeam;
-    stBeam << "Beams:idA = " << pdgBeam;
-    fPythia.readString(stBeam.str());
-    // set target
-    auto pdgTarget = static_cast<int>(particles::GetPDG(TargetId));
-    // replace hydrogen with proton, otherwise pythia goes into heavy ion mode!
-    if (TargetId == particles::Code::Hydrogen)
-      pdgTarget = static_cast<int>(particles::GetPDG(particles::Code::Proton));
-    std::stringstream stTarget;
-    stTarget << "Beams:idB = " << pdgTarget;
-    fPythia.readString(stTarget.str());
-    // set frame to lab. frame
-    fPythia.readString("Beams:frameType = 2");
-    // set beam energy
-    const double Elab = BeamEnergy / 1_GeV;
-    std::stringstream stEnergy;
-    stEnergy << "Beams:eA = " << Elab;
-    fPythia.readString(stEnergy.str());
-    // target at rest
-    fPythia.readString("Beams:eB = 0.");
-    // initialize this config
-    fPythia.init();
-  }
-
-  bool Interaction::CanInteract(const corsika::particles::Code pCode) {
-    return pCode == corsika::particles::Code::Proton ||
-           pCode == corsika::particles::Code::Neutron ||
-           pCode == corsika::particles::Code::AntiProton ||
-           pCode == corsika::particles::Code::AntiNeutron ||
-           pCode == corsika::particles::Code::PiMinus ||
-           pCode == corsika::particles::Code::PiPlus;
-  }
-
-  tuple<units::si::CrossSectionType, units::si::CrossSectionType>
-  Interaction::GetCrossSection(const particles::Code BeamId,
-                               const particles::Code TargetId,
-                               const units::si::HEPEnergyType CoMenergy) {
-    using namespace units::si;
-
-    // interaction possible in pythia?
-    if (TargetId == particles::Code::Proton || TargetId == particles::Code::Hydrogen) {
-      if (CanInteract(BeamId) && ValidCoMEnergy(CoMenergy)) {
-        // input particle PDG
-        auto const pdgCodeBeam = static_cast<int>(particles::GetPDG(BeamId));
-        auto const pdgCodeTarget = static_cast<int>(particles::GetPDG(TargetId));
-        const double ecm = CoMenergy / 1_GeV;
-
-        // calculate cross section
-        fSigma.calc(pdgCodeBeam, pdgCodeTarget, ecm);
-        if (fSigma.hasSigmaTot()) {
-          const double sigEla = fSigma.sigmaEl();
-          const double sigProd = fSigma.sigmaTot() - sigEla;
-
-          return std::make_tuple(sigProd * (1_fm * 1_fm), sigEla * (1_fm * 1_fm));
-
-        } else
-          throw std::runtime_error("pythia cross section init failed");
-
-      } else {
-        return std::make_tuple(std::numeric_limits<double>::infinity() * 1_mb,
-                               std::numeric_limits<double>::infinity() * 1_mb);
-      }
-    } else {
-      throw std::runtime_error("invalid target for pythia");
-    }
-  }
-
-  template <>
-  units::si::GrammageType Interaction::GetInteractionLength(const Particle& p) {
-
-    using namespace units;
-    using namespace units::si;
-    using namespace geometry;
-
-    // coordinate system, get global frame of reference
-    CoordinateSystem& rootCS =
-        RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-    const particles::Code corsikaBeamId = p.GetPID();
-
-    // beam particles for pythia : 1, 2, 3 for p, pi, k
-    // read from cross section code table
-    const bool kInteraction = CanInteract(corsikaBeamId);
-
-    // FOR NOW: assume target is at rest
-    process::pythia::MomentumVector pTarget(rootCS, {0_GeV, 0_GeV, 0_GeV});
-
-    // total momentum and energy
-    HEPEnergyType Elab = p.GetEnergy() + constants::nucleonMass;
-    process::pythia::MomentumVector pTotLab(rootCS, {0_GeV, 0_GeV, 0_GeV});
-    pTotLab += p.GetMomentum();
-    pTotLab += pTarget;
-    auto const pTotLabNorm = pTotLab.norm();
-    // calculate cm. energy
-    const HEPEnergyType ECoM = sqrt(
-        (Elab + pTotLabNorm) * (Elab - pTotLabNorm)); // binomial for numerical accuracy
-
-    cout << "Interaction: LambdaInt: \n"
-         << " input energy: " << p.GetEnergy() / 1_GeV << endl
-         << " beam can interact:" << kInteraction << endl
-         << " beam pid:" << p.GetPID() << endl;
-
-    // TODO: move limits into variables
-    if (kInteraction && Elab >= 8.5_GeV && ValidCoMEnergy(ECoM)) {
-
-      // get target from environment
-      /*
-        the target should be defined by the Environment,
-        ideally as full particle object so that the four momenta
-        and the boosts can be defined..
-      */
-      const auto* currentNode = p.GetNode();
-      const auto mediumComposition =
-          currentNode->GetModelProperties().GetNuclearComposition();
-      // determine average interaction length
-
-      auto const weightedProdCrossSection =
-          mediumComposition.WeightedSum([=](auto vTargetID) {
-            return std::get<0>(this->GetCrossSection(corsikaBeamId, vTargetID, ECoM));
-          });
-
-      cout << "Interaction: IntLength: weighted CrossSection (mb): "
-           << weightedProdCrossSection / 1_mb << endl
-           << "Interaction: IntLength: average mass number: "
-           << mediumComposition.GetAverageMassNumber() << endl;
-
-      // calculate interaction length in medium
-      GrammageType const int_length = mediumComposition.GetAverageMassNumber() *
-                                      units::constants::u / weightedProdCrossSection;
-      cout << "Interaction: "
-           << "interaction length (g/cm2): " << int_length / (0.001_kg) * 1_cm * 1_cm
-           << endl;
-
-      return int_length;
-    }
-
-    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 <>
-  process::EProcessReturn Interaction::DoInteraction(SecondaryView& view) {
-
-    using namespace units;
-    using namespace utl;
-    using namespace units::si;
-    using namespace geometry;
-
-    auto const projectile = view.GetProjectile();
-
-    const auto corsikaBeamId = projectile.GetPID();
-    cout << "Pythia::Interaction: "
-         << "DoInteraction: " << corsikaBeamId << " interaction? "
-         << process::pythia::Interaction::CanInteract(corsikaBeamId) << endl;
-
-    if (particles::IsNucleus(corsikaBeamId)) {
-      // nuclei handled by different process, this should not happen
-      throw std::runtime_error("Nuclear projectile are not handled by PYTHIA!");
-    }
-
-    if (process::pythia::Interaction::CanInteract(corsikaBeamId)) {
-
-      const CoordinateSystem& rootCS =
-          RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-      // position and time of interaction, not used in Sibyll
-      Point pOrig = projectile.GetPosition();
-      TimeType tOrig = projectile.GetTime();
-
-      // define target
-      // FOR NOW: target is always at rest
-      const auto eTargetLab = 0_GeV + constants::nucleonMass;
-      const auto pTargetLab = MomentumVector(rootCS, 0_GeV, 0_GeV, 0_GeV);
-      const FourVector PtargLab(eTargetLab, pTargetLab);
-
-      // define projectile
-      HEPEnergyType const eProjectileLab = projectile.GetEnergy();
-      auto const pProjectileLab = projectile.GetMomentum();
-
-      cout << "Interaction: ebeam lab: " << eProjectileLab / 1_GeV << endl
-           << "Interaction: pbeam lab: " << pProjectileLab.GetComponents() / 1_GeV
-           << endl;
-      cout << "Interaction: etarget lab: " << eTargetLab / 1_GeV << endl
-           << "Interaction: ptarget lab: " << pTargetLab.GetComponents() / 1_GeV << endl;
-
-      const FourVector PprojLab(eProjectileLab, pProjectileLab);
-
-      // define target kinematics in lab frame
-      // define boost to and from CoM frame
-      // CoM frame definition in Pythia projectile: +z
-      COMBoost const boost(PprojLab, constants::nucleonMass);
-
-      // just for show:
-      // boost projecticle
-      auto const PprojCoM = boost.toCoM(PprojLab);
-
-      // boost target
-      auto const PtargCoM = boost.toCoM(PtargLab);
-
-      cout << "Interaction: ebeam CoM: " << PprojCoM.GetTimeLikeComponent() / 1_GeV
-           << endl
-           << "Interaction: pbeam CoM: "
-           << PprojCoM.GetSpaceLikeComponents().GetComponents() / 1_GeV << endl;
-      cout << "Interaction: etarget CoM: " << PtargCoM.GetTimeLikeComponent() / 1_GeV
-           << endl
-           << "Interaction: ptarget CoM: "
-           << PtargCoM.GetSpaceLikeComponents().GetComponents() / 1_GeV << endl;
-
-      cout << "Interaction: position of interaction: " << pOrig.GetCoordinates() << endl;
-      cout << "Interaction: time: " << tOrig << endl;
-
-      HEPEnergyType Etot = eProjectileLab + eTargetLab;
-      MomentumVector Ptot = projectile.GetMomentum();
-      // invariant mass, i.e. cm. energy
-      HEPEnergyType Ecm = sqrt(Etot * Etot - Ptot.squaredNorm());
-
-      // sample target mass number
-      const auto* currentNode = projectile.GetNode();
-      const auto& mediumComposition =
-          currentNode->GetModelProperties().GetNuclearComposition();
-      // get cross sections for target materials
-      /*
-        Here we read the cross section from the interaction model again,
-        should be passed from GetInteractionLength if possible
-       */
-      //#warning reading interaction cross section again, should not be necessary
-      auto const& compVec = mediumComposition.GetComponents();
-      std::vector<si::CrossSectionType> cross_section_of_components(compVec.size());
-
-      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;
-        cross_section_of_components[i] = sigProd;
-      }
-
-      const auto corsikaTargetId =
-          mediumComposition.SampleTarget(cross_section_of_components, fRNG);
-      cout << "Interaction: target selected: " << corsikaTargetId << endl;
-
-      if (corsikaTargetId != particles::Code::Hydrogen &&
-          corsikaTargetId != particles::Code::Neutron &&
-          corsikaTargetId != particles::Code::Proton)
-        throw std::runtime_error("DoInteraction: wrong target for PYTHIA");
-
-      cout << "Interaction: "
-           << " DoInteraction: E(GeV):" << eProjectileLab / 1_GeV
-           << " Ecm(GeV): " << Ecm / 1_GeV << endl;
-
-      if (eProjectileLab < 8.5_GeV || !ValidCoMEnergy(Ecm)) {
-        cout << "Interaction: "
-             << " DoInteraction: should have dropped particle.. "
-             << "THIS IS AN ERROR" << endl;
-        throw std::runtime_error("energy too low for PYTHIA");
-
-      } else {
-        fCount++;
-
-        ConfigureLabFrameCollision(corsikaBeamId, corsikaTargetId, eProjectileLab);
-
-        // create event in pytia
-        if (!fPythia.next()) throw std::runtime_error("Pythia::DoInteraction: failed!");
-
-        // link to pythia stack
-        Pythia8::Event& event = fPythia.event;
-
-        if (print_listing_) {
-          // list final state
-          event.list();
-        }
-
-        MomentumVector Plab_final(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
-        HEPEnergyType Elab_final = 0_GeV;
-        for (int i = 0; i < event.size(); ++i) {
-          Pythia8::Particle& p8p = event[i];
-          // skip particles that have decayed in pythia
-          if (!p8p.isFinal()) continue;
-
-          auto const pyId =
-              particles::ConvertFromPDG(static_cast<particles::PDGCode>(p8p.id()));
-
-          const MomentumVector pyPlab(
-              rootCS, {p8p.px() * 1_GeV, p8p.py() * 1_GeV, p8p.pz() * 1_GeV});
-          HEPEnergyType const pyEn = p8p.e() * 1_GeV;
-
-          // add to corsika stack
-          auto pnew = view.AddSecondary(
-              tuple<particles::Code, units::si::HEPEnergyType, stack::MomentumVector,
-                    geometry::Point, units::si::TimeType>{pyId, pyEn, pyPlab, pOrig,
-                                                          tOrig});
-
-          Plab_final += pnew.GetMomentum();
-          Elab_final += pnew.GetEnergy();
-        }
-        cout << "conservation (all GeV): "
-             << "Elab_final=" << Elab_final / 1_GeV
-             << ", Plab_final=" << (Plab_final / 1_GeV).GetComponents() << endl;
-      }
-    }
-    return process::EProcessReturn::eOk;
-  }
-
-} // namespace corsika::process::pythia
diff --git a/Processes/Pythia/Interaction.h b/Processes/Pythia/Interaction.h
deleted file mode 100644
index ef2bdfbcc5566951434c73df86ec621e561f48b9..0000000000000000000000000000000000000000
--- a/Processes/Pythia/Interaction.h
+++ /dev/null
@@ -1,69 +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 <Pythia8/Pythia.h>
-
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/process/InteractionProcess.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <tuple>
-
-namespace corsika::process::pythia {
-
-  class Interaction : public corsika::process::InteractionProcess<Interaction> {
-
-    int fCount = 0;
-    bool fInitialized = false;
-    bool print_listing_ = false;
-
-  public:
-    Interaction(const bool print_listing = false);
-    ~Interaction() = default;
-
-    void SetParticleListStable(std::vector<particles::Code> const&);
-    void SetUnstable(const corsika::particles::Code);
-    void SetStable(const corsika::particles::Code);
-
-    bool WasInitialized() { return fInitialized; }
-    bool ValidCoMEnergy(corsika::units::si::HEPEnergyType ecm) {
-      using namespace corsika::units::si;
-      return (10_GeV < ecm) && (ecm < 1_PeV);
-    }
-
-    bool CanInteract(const corsika::particles::Code);
-    void ConfigureLabFrameCollision(const corsika::particles::Code,
-                                    const corsika::particles::Code,
-                                    const corsika::units::si::HEPEnergyType);
-    std::tuple<corsika::units::si::CrossSectionType, corsika::units::si::CrossSectionType>
-    GetCrossSection(const corsika::particles::Code BeamId,
-                    const corsika::particles::Code TargetId,
-                    const corsika::units::si::HEPEnergyType CoMenergy);
-
-    template <typename TParticle>
-    corsika::units::si::GrammageType GetInteractionLength(const TParticle&);
-
-    /**
-       In this function PYTHIA is called to produce one event. The
-       event is copied (and boosted) into the shower lab frame.
-     */
-
-    template <typename TSecondaryView>
-    corsika::process::EProcessReturn DoInteraction(TSecondaryView&);
-
-  private:
-    corsika::random::RNG& fRNG =
-        corsika::random::RNGManager::GetInstance().GetRandomStream("pythia");
-    Pythia8::Pythia fPythia;
-    Pythia8::SigmaTotal fSigma;
-    const bool fInternalDecays = true;
-  };
-
-} // namespace corsika::process::pythia
diff --git a/Processes/Pythia/Random.cc b/Processes/Pythia/Random.cc
deleted file mode 100644
index acf2837a5ee7ee991a672ab28e21e8fbe16c87b2..0000000000000000000000000000000000000000
--- a/Processes/Pythia/Random.cc
+++ /dev/null
@@ -1,15 +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/process/pythia/Random.h>
-
-namespace corsika::process::pythia {
-
-  double Random::flat() { return fDist(fRNG); }
-
-} // namespace corsika::process::pythia
diff --git a/Processes/Pythia/Random.h b/Processes/Pythia/Random.h
deleted file mode 100644
index c649e42bc4a0724677b8f23f353b93ebbfb05bd6..0000000000000000000000000000000000000000
--- a/Processes/Pythia/Random.h
+++ /dev/null
@@ -1,28 +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 <Pythia8/Pythia.h>
-#include <corsika/random/RNGManager.h>
-
-namespace corsika::process {
-
-  namespace pythia {
-
-    class Random : public Pythia8::RndmEngine {
-      double flat();
-
-    private:
-      std::uniform_real_distribution<double> fDist;
-      corsika::random::RNG& fRNG =
-          corsika::random::RNGManager::GetInstance().GetRandomStream("pythia");
-    };
-
-  } // namespace pythia
-} // namespace corsika::process
diff --git a/Processes/Pythia/testPythia8.cc b/Processes/Pythia/testPythia8.cc
deleted file mode 100644
index 71b7768e5c66f60929f9d9383035276e81f5d9e9..0000000000000000000000000000000000000000
--- a/Processes/Pythia/testPythia8.cc
+++ /dev/null
@@ -1,163 +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 <Pythia8/Pythia.h>
-#include <corsika/process/pythia/Decay.h>
-#include <corsika/process/pythia/Interaction.h>
-
-#include <corsika/random/RNGManager.h>
-
-#include <corsika/particles/ParticleProperties.h>
-
-#include <corsika/geometry/Point.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/utl/CorsikaFenv.h>
-
-#include <catch2/catch.hpp>
-
-TEST_CASE("Pythia", "[processes]") {
-
-  SECTION("linking pythia") {
-    using namespace Pythia8;
-    using std::cout;
-    using std::endl;
-
-    // Generator. Process selection. LHC initialization. Histogram.
-    Pythia pythia;
-
-    pythia.readString("Next:numberShowInfo = 0");
-    pythia.readString("Next:numberShowProcess = 0");
-    pythia.readString("Next:numberShowEvent = 0");
-
-    pythia.readString("ProcessLevel:all = off");
-
-    pythia.init();
-
-    Event& event = pythia.event;
-    event.reset();
-
-    pythia.particleData.mayDecay(321, true);
-    double pz = 100.;
-    double m = 0.49368;
-    event.append(321, 1, 0, 0, 0., 0., 100., sqrt(pz * pz + m * m), m);
-
-    if (!pythia.next())
-      cout << "decay failed!" << endl;
-    else
-      cout << "particles after decay: " << event.size() << endl;
-    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;
-      }
-  }
-
-  SECTION("pythia interface") {
-    using namespace corsika;
-
-    random::RNGManager::GetInstance().RegisterRandomStream("pythia");
-
-    process::pythia::Decay model;
-  }
-}
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/setup/SetupEnvironment.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-using namespace corsika;
-using namespace corsika::units::si;
-
-template <typename TStackView>
-auto sumMomentum(TStackView const& view, geometry::CoordinateSystem const& vCS) {
-  geometry::Vector<hepenergy_d> sum{vCS, 0_eV, 0_eV, 0_eV};
-
-  for (auto const& p : view) { sum += p.GetMomentum(); }
-
-  return sum;
-}
-
-TEST_CASE("pythia process") {
-
-  auto [env, csPtr, nodePtr] = setup::testing::setupEnvironment(particles::Code::Proton);
-  auto const& cs = *csPtr;
-  [[maybe_unused]] auto const& env_dummy = env;
-  [[maybe_unused]] auto const& node_dummy = nodePtr;
-
-  SECTION("pythia decay") {
-    feenableexcept(FE_INVALID);
-    const HEPEnergyType P0 = 10_GeV;
-    auto [stackPtr, secViewPtr] =
-        setup::testing::setupStack(particles::Code::PiPlus, 0, 0, P0, nodePtr, *csPtr);
-    const auto plab = corsika::stack::MomentumVector(
-        cs, {P0, 0_eV, 0_eV}); // this is secret knowledge about setupStack
-    auto& stack = *stackPtr;
-    auto& view = *secViewPtr;
-    auto particle = stackPtr->first();
-
-    random::RNGManager::GetInstance().RegisterRandomStream("pythia");
-
-    process::pythia::Decay model;
-
-    [[maybe_unused]] const TimeType time = model.GetLifetime(particle);
-    model.DoDecay(view);
-    CHECK(stack.getEntries() == 3);
-    auto const pSum = sumMomentum(view, cs);
-    CHECK((pSum - plab).norm() / 1_GeV == Approx(0).margin(1e-4));
-    CHECK((pSum.norm() - plab.norm()) / 1_GeV == Approx(0).margin(1e-4));
-  }
-
-  SECTION("pythia decay config") {
-    process::pythia::Decay model({particles::Code::PiPlus, particles::Code::PiMinus});
-    REQUIRE(model.IsDecayHandled(particles::Code::PiPlus));
-    REQUIRE(model.IsDecayHandled(particles::Code::PiMinus));
-    REQUIRE_FALSE(model.IsDecayHandled(particles::Code::KPlus));
-
-    const std::vector<particles::Code> particleTestList = {
-        particles::Code::PiPlus, particles::Code::PiMinus, particles::Code::KPlus,
-        particles::Code::Lambda0Bar, particles::Code::D0Bar};
-
-    // setup decays
-    model.SetHandleDecay(particleTestList);
-    for (auto& pCode : particleTestList) REQUIRE(model.IsDecayHandled(pCode));
-
-    // individually
-    model.SetHandleDecay(particles::Code::KMinus);
-
-    // possible decays
-    REQUIRE_FALSE(model.CanHandleDecay(particles::Code::Proton));
-    REQUIRE_FALSE(model.CanHandleDecay(particles::Code::Electron));
-    REQUIRE(model.CanHandleDecay(particles::Code::PiPlus));
-    REQUIRE(model.CanHandleDecay(particles::Code::MuPlus));
-  }
-
-  SECTION("pythia interaction") {
-
-    feenableexcept(FE_INVALID);
-    auto [stackPtr, secViewPtr] = setup::testing::setupStack(particles::Code::PiPlus, 0,
-                                                             0, 100_GeV, nodePtr, *csPtr);
-    auto& view = *secViewPtr;
-    auto particle = stackPtr->first();
-
-    process::pythia::Interaction model;
-
-    [[maybe_unused]] const process::EProcessReturn ret = model.DoInteraction(view);
-    [[maybe_unused]] const GrammageType length = model.GetInteractionLength(particle);
-  }
-}
diff --git a/Processes/QGSJetII/CMakeLists.txt b/Processes/QGSJetII/CMakeLists.txt
deleted file mode 100644
index 77d43794c751b59713b6d962a4cc26177a0b81aa..0000000000000000000000000000000000000000
--- a/Processes/QGSJetII/CMakeLists.txt
+++ /dev/null
@@ -1,107 +0,0 @@
-if (NOT DEFINED ENV{CORSIKA_DATA})
-  message (WARNING "WARNING: QGSJetII will not work without corsika-data. Set CORSIKA_DATA path!")
-endif ()
-
-add_custom_command (
-  OUTPUT  ${PROJECT_BINARY_DIR}/Processes/QGSJetII/Generated.inc
-  COMMAND ${PROJECT_SOURCE_DIR}/Processes/QGSJetII/code_generator.py 
-          ${PROJECT_BINARY_DIR}/Framework/Particles/particle_db.pkl
-          ${PROJECT_SOURCE_DIR}/Processes/QGSJetII/qgsjet-II-04-codes.dat
-  DEPENDS code_generator.py
-          qgsjet-II-04-codes.dat
-          CORSIKAparticles
-  WORKING_DIRECTORY
-          ${PROJECT_BINARY_DIR}/Processes/QGSJetII/
-  COMMENT "Generate conversion tables for particle codes QGSJetII <-> CORSIKA"
-  VERBATIM
-  )
-  
-set (
-  MODEL_SOURCES
-  ParticleConversion.cc
-  Interaction.cc
-  qgsjet-II-04.f
-  qgsjet-II-04.cc
-  )
-
-set (
-  MODEL_HEADERS
-  ParticleConversion.h
-  qgsjet-II-04.h
-  QGSJetIIStack.h
-  QGSJetIIFragmentsStack.h
-  Interaction.h
-  ${PROJECT_BINARY_DIR}/Processes/QGSJetII/Generated.inc
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/qgsjetII
-  )
-
-add_library (ProcessQGSJetII STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessQGSJetII ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-# ....................................................
-# since Generated.inc is an automatically produced file in the build directory,
-# create a symbolic link into the source tree, so that it can be found and edited more easily
-# this is not needed for the build to succeed! .......
-add_custom_command (
-  OUTPUT  ${CMAKE_CURRENT_SOURCE_DIR}/Generated.inc
-  COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_BINARY_DIR}/include/corsika/process/qgsjetII/Generated.inc ${CMAKE_CURRENT_SOURCE_DIR}/Generated.inc
-  COMMENT "Generate link in source-dir: ${CMAKE_CURRENT_SOURCE_DIR}/Generated.inc"
-  )
-add_custom_target (SourceDirLinkQgsII DEPENDS ${PROJECT_BINARY_DIR}/Processes/QGSJetII/Generated.inc)
-add_dependencies (ProcessQGSJetII SourceDirLinkQgsII)
-# .....................................................
-
-
-
-set_target_properties (
-  ProcessQGSJetII
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessQGSJetII
-  CORSIKAprocesssequence
-  CORSIKAparticles
-  CORSIKAutilities
-  CORSIKAunits
-  CORSIKAthirdparty
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAsetup
-  CORSIKArandom
-  CorsikaData
-  )
-
-target_include_directories (
-  ProcessQGSJetII 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS ProcessQGSJetII
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-  )
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST(testQGSJetII
-  SOURCES
-  testQGSJetII.cc
-  ${MODEL_HEADERS}
-)
-
-target_link_libraries (
-  testQGSJetII
-  ProcessQGSJetII
-  CORSIKAtesting
-  stdc++fs)
diff --git a/Processes/QGSJetII/Interaction.cc b/Processes/QGSJetII/Interaction.cc
deleted file mode 100644
index c16233af582c5908261f88e6de89af58748b0e09..0000000000000000000000000000000000000000
--- a/Processes/QGSJetII/Interaction.cc
+++ /dev/null
@@ -1,406 +0,0 @@
-/*
- * (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.
- */
-
-#include <corsika/process/qgsjetII/Interaction.h>
-
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/geometry/FourVector.h>
-#include <corsika/geometry/QuantityVector.h>
-#include <corsika/process/qgsjetII/QGSJetIIFragmentsStack.h>
-#include <corsika/process/qgsjetII/QGSJetIIStack.h>
-#include <corsika/process/qgsjetII/qgsjet-II-04.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/utl/COMBoost.h>
-
-#include <random>
-#include <sstream>
-#include <string>
-#include <tuple>
-
-using std::cout;
-using std::endl;
-using std::ostringstream;
-using std::string;
-using std::tuple;
-
-using namespace corsika;
-using namespace corsika::setup;
-using SetupParticle = setup::Stack::StackIterator;
-using SetupView = setup::StackView;
-using Track = Trajectory;
-
-namespace corsika::process::qgsjetII {
-
-  Interaction::Interaction(const string& dataPath)
-      : data_path_(dataPath) {
-    if (dataPath == "") {
-      if (std::getenv("CORSIKA_DATA")) {
-        data_path_ = string(std::getenv("CORSIKA_DATA")) + "/QGSJetII/";
-        cout << "Searching for QGSJetII data tables in " << data_path_ << endl;
-      }
-    }
-
-    // initialize QgsjetII
-    if (!initialized_) {
-      qgset_();
-      datadir DIR(data_path_);
-      qgaini_(DIR.data);
-      initialized_ = true;
-    }
-  }
-
-  Interaction::~Interaction() { cout << "QgsjetII::Interaction n=" << count_ << endl; }
-
-  units::si::CrossSectionType Interaction::GetCrossSection(
-      const particles::Code beamId, const particles::Code targetId,
-      const units::si::HEPEnergyType Elab, const unsigned int Abeam,
-      const unsigned int targetA) const {
-    using namespace units::si;
-    double sigProd = std::numeric_limits<double>::infinity();
-
-    if (process::qgsjetII::CanInteract(beamId)) {
-
-      const int xsCode = process::qgsjetII::GetQgsjetIIXSCodeRaw(beamId);
-      int iTarget = 1;
-      if (particles::IsNucleus(targetId)) {
-        iTarget = targetA;
-        if (iTarget > maxMassNumber_ || iTarget <= 0) {
-          std::ostringstream txt;
-          txt << "QgsjetII target outside range. iTarget=" << iTarget;
-          throw std::runtime_error(txt.str().c_str());
-        }
-      }
-      int iProjectile = 1;
-      if (particles::IsNucleus(beamId)) {
-        iProjectile = Abeam;
-        if (iProjectile > maxMassNumber_ || iProjectile <= 0)
-          throw std::runtime_error("QgsjetII target outside range. ");
-      }
-
-      cout << "QgsjetII::GetCrossSection Elab=" << Elab << " xs-code=" << xsCode
-           << " iProjectile=" << iProjectile << " iTarget=" << iTarget << endl;
-      sigProd = qgsect_(Elab / 1_GeV, xsCode, iProjectile, iTarget);
-      cout << "QgsjetII::GetCrossSection sigProd=" << sigProd << endl;
-    }
-
-    return sigProd * 1_mb;
-  }
-
-  template <>
-  units::si::GrammageType Interaction::GetInteractionLength(
-      SetupParticle const& vP) const {
-
-    using namespace units;
-    using namespace units::si;
-    using namespace geometry;
-
-    // coordinate system, get global frame of reference
-    CoordinateSystem& rootCS =
-        RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-    const particles::Code corsikaBeamId = vP.GetPID();
-
-    // beam particles for qgsjetII : 1, 2, 3 for p, pi, k
-    // read from cross section code table
-    const bool kInteraction = process::qgsjetII::CanInteract(corsikaBeamId);
-
-    // FOR NOW: assume target is at rest
-    MomentumVector pTarget(rootCS, {0_GeV, 0_GeV, 0_GeV});
-
-    // total momentum and energy
-    HEPEnergyType Elab = vP.GetEnergy();
-
-    cout << "Interaction: LambdaInt: \n"
-         << " input energy: " << vP.GetEnergy() / 1_GeV << endl
-         << " beam can interact:" << kInteraction << endl
-         << " beam pid:" << vP.GetPID() << endl;
-
-    if (kInteraction) {
-
-      int Abeam = 0;
-      if (particles::IsNucleus(vP.GetPID())) Abeam = vP.GetNuclearA();
-
-      // get target from environment
-      /*
-        the target should be defined by the Environment,
-        ideally as full particle object so that the four momenta
-        and the boosts can be defined..
-      */
-
-      auto const* currentNode = vP.GetNode();
-      const auto& mediumComposition =
-          currentNode->GetModelProperties().GetNuclearComposition();
-
-      si::CrossSectionType weightedProdCrossSection = mediumComposition.WeightedSum(
-          [=](particles::Code targetID) -> si::CrossSectionType {
-            int targetA = 0;
-            if (corsika::particles::IsNucleus(targetID))
-              targetA = particles::GetNucleusA(targetID);
-            return GetCrossSection(corsikaBeamId, targetID, Elab, Abeam, targetA);
-          });
-
-      cout << "Interaction: "
-           << "IntLength: weighted CrossSection (mb): " << weightedProdCrossSection / 1_mb
-           << endl;
-
-      // calculate interaction length in medium
-      GrammageType const int_length = mediumComposition.GetAverageMassNumber() *
-                                      units::constants::u / weightedProdCrossSection;
-      cout << "Interaction: "
-           << "interaction length (g/cm2): " << int_length / (0.001_kg) * 1_cm * 1_cm
-           << endl;
-
-      return int_length;
-    }
-
-    return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
-  }
-
-  /**
-     In this function QGSJETII is called to produce one event. The
-     event is copied (and boosted) into the shower lab frame.
-   */
-
-  template <>
-  process::EProcessReturn Interaction::DoInteraction(SetupView& view) {
-
-    using namespace units;
-    using namespace utl;
-    using namespace units::si;
-    using namespace geometry;
-
-    auto const projectile = view.GetProjectile();
-
-    const auto corsikaBeamId = projectile.GetPID();
-    cout << "ProcessQgsjetII: "
-         << "DoInteraction: " << corsikaBeamId << " interaction? "
-         << process::qgsjetII::CanInteract(corsikaBeamId) << endl;
-
-    if (process::qgsjetII::CanInteract(corsikaBeamId)) {
-
-      const CoordinateSystem& rootCS =
-          RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-      // position and time of interaction, not used in QgsjetII
-      Point pOrig = projectile.GetPosition();
-      TimeType tOrig = projectile.GetTime();
-
-      // define target
-      // for QgsjetII is always a single nucleon
-      // FOR NOW: target is always at rest
-      const auto targetEnergyLab = 0_GeV + constants::nucleonMass;
-      const auto targetMomentumLab = MomentumVector(rootCS, 0_GeV, 0_GeV, 0_GeV);
-      const FourVector PtargLab(targetEnergyLab, targetMomentumLab);
-
-      // define projectile
-      HEPEnergyType const projectileEnergyLab = projectile.GetEnergy();
-      auto const projectileMomentumLab = projectile.GetMomentum();
-
-      int beamA = 1;
-      if (particles::IsNucleus(corsikaBeamId)) beamA = projectile.GetNuclearA();
-
-      const HEPEnergyType projectileEnergyLabPerNucleon = projectileEnergyLab / beamA;
-
-      cout << "Interaction: ebeam lab: " << projectileEnergyLab / 1_GeV << endl
-           << "Interaction: pbeam lab: " << projectileMomentumLab.GetComponents() / 1_GeV
-           << endl;
-      cout << "Interaction: etarget lab: " << targetEnergyLab / 1_GeV << endl
-           << "Interaction: ptarget lab: " << targetMomentumLab.GetComponents() / 1_GeV
-           << endl;
-
-      cout << "Interaction: position of interaction: " << pOrig.GetCoordinates() << endl;
-      cout << "Interaction: time: " << tOrig << endl;
-
-      // sample target mass number
-      auto const* currentNode = projectile.GetNode();
-      auto const& mediumComposition =
-          currentNode->GetModelProperties().GetNuclearComposition();
-      // get cross sections for target materials
-      /*
-        Here we read the cross section from the interaction model again,
-        should be passed from GetInteractionLength if possible
-       */
-      auto const& compVec = mediumComposition.GetComponents();
-      std::vector<si::CrossSectionType> cross_section_of_components(compVec.size());
-
-      for (size_t i = 0; i < compVec.size(); ++i) {
-        auto const targetId = compVec[i];
-        int targetA = 0;
-        if (corsika::particles::IsNucleus(targetId))
-          targetA = particles::GetNucleusA(targetId);
-        const auto sigProd =
-            GetCrossSection(corsikaBeamId, targetId, projectileEnergyLab, beamA, targetA);
-        cross_section_of_components[i] = sigProd;
-      }
-
-      const auto targetCode =
-          mediumComposition.SampleTarget(cross_section_of_components, rng_);
-      cout << "Interaction: target selected: " << targetCode << endl;
-
-      int targetMassNumber = 1;               // proton
-      if (particles::IsNucleus(targetCode)) { // nucleus
-        targetMassNumber = particles::GetNucleusA(targetCode);
-        if (targetMassNumber > maxMassNumber_)
-          throw std::runtime_error("QgsjetII target mass outside range.");
-      } else {
-        if (targetCode != particles::Proton::GetCode())
-          throw std::runtime_error("QgsjetII Taget not possible.");
-      }
-      cout << "Interaction: target qgsjetII code/A: " << targetMassNumber << endl;
-
-      int projectileMassNumber = 1; // "1" means "hadron"
-      QgsjetIIHadronType qgsjet_hadron_type =
-          process::qgsjetII::GetQgsjetIIHadronType(corsikaBeamId);
-      if (qgsjet_hadron_type == QgsjetIIHadronType::NucleusType) {
-        projectileMassNumber = projectile.GetNuclearA();
-        if (projectileMassNumber > maxMassNumber_)
-          throw std::runtime_error("QgsjetII projectile mass outside range.");
-        std::array<QgsjetIIHadronType, 2> constexpr nucleons = {
-            QgsjetIIHadronType::ProtonType, QgsjetIIHadronType::NeutronType};
-        std::uniform_int_distribution select(0, 1);
-        qgsjet_hadron_type = nucleons[select(rng_)];
-      } else {
-        // from conex: replace pi0 or rho0 with pi+/pi- in alternating sequence
-        if (qgsjet_hadron_type == QgsjetIIHadronType::NeutralLightMesonType) {
-          qgsjet_hadron_type = alternate_;
-          alternate_ = (alternate_ == QgsjetIIHadronType::PiPlusType
-                            ? QgsjetIIHadronType::PiMinusType
-                            : QgsjetIIHadronType::PiPlusType);
-        }
-      }
-      cout << "Interaction: projectile qgsjetII code/A: " << projectileMassNumber << " "
-           << corsikaBeamId << endl;
-
-      int qgsjet_hadron_type_int = static_cast<QgsjetIICodeIntType>(qgsjet_hadron_type);
-
-      cout << "Interaction: "
-           << " DoInteraction: E(GeV):" << projectileEnergyLab / 1_GeV << endl;
-      count_++;
-      qgini_(projectileEnergyLab / 1_GeV, qgsjet_hadron_type_int, projectileMassNumber,
-             targetMassNumber);
-      qgconf_();
-
-      // bookkeeping
-      MomentumVector Plab_final(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
-      HEPEnergyType Elab_final = 0_GeV;
-
-      // to read the secondaries
-      // define rotation to and from CoM frame
-      // CoM frame definition in QgsjetII projectile: +z
-      auto const& originalCS = projectileMomentumLab.GetCoordinateSystem();
-      geometry::CoordinateSystem const zAxisFrame =
-          originalCS.RotateToZ(projectileMomentumLab);
-
-      // nuclear projectile fragments
-      QGSJetIIFragmentsStack qfs;
-      for (auto& fragm : qfs) {
-        particles::Code idFragm = particles::Code::Nucleus;
-        const int A = fragm.GetFragmentSize();
-        int Z = 0;
-        switch (A) {
-          case 1: { // proton/neutron
-            std::uniform_real_distribution<double> select;
-            if (select(rng_) > 0.5) {
-              idFragm = particles::Code::Proton;
-              Z = 1;
-            } else {
-              idFragm = particles::Code::Neutron;
-              Z = 0;
-            }
-
-            const HEPMassType nucleonMass = particles::GetMass(idFragm);
-
-            auto momentum = geometry::Vector(
-                zAxisFrame, corsika::geometry::QuantityVector<hepmomentum_d>{
-                                0.0_GeV, 0.0_GeV,
-                                sqrt((projectileEnergyLabPerNucleon + nucleonMass) *
-                                     (projectileEnergyLabPerNucleon - nucleonMass))});
-
-            auto const energy = sqrt(momentum.squaredNorm() + square(nucleonMass));
-            momentum.rebase(originalCS); // transform back into standard lab frame
-            std::cout << "secondary fragment> id=" << idFragm
-                      << " p=" << momentum.GetComponents() << std::endl;
-            auto pnew = view.AddSecondary(
-                tuple<particles::Code, units::si::HEPEnergyType, stack::MomentumVector,
-                      geometry::Point, units::si::TimeType>{idFragm, energy, momentum,
-                                                            pOrig, tOrig});
-            Plab_final += pnew.GetMomentum();
-            Elab_final += pnew.GetEnergy();
-          } break;
-          case 2: // deuterium
-            Z = 1;
-            break;
-          case 3: // tritium
-            Z = 1;
-            break;
-          case 4: // helium
-            Z = 2;
-            break;
-          default: // nucleus
-          {
-            Z = int(A / 2.15 + 0.7);
-          }
-        }
-
-        if (idFragm == particles::Code::Nucleus) { // thus: not p or n
-          const HEPMassType nucleusMass =
-              particles::Proton::GetMass() * Z + particles::Neutron::GetMass() * (A - Z);
-          auto momentum = geometry::Vector(
-              zAxisFrame, geometry::QuantityVector<hepmomentum_d>{
-                              0.0_GeV, 0.0_GeV,
-                              sqrt((projectileEnergyLabPerNucleon * A + nucleusMass) *
-                                   (projectileEnergyLabPerNucleon * A - nucleusMass))});
-
-          auto const energy = sqrt(momentum.squaredNorm() + 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;
-          auto pnew = view.AddSecondary(
-              tuple<particles::Code, units::si::HEPEnergyType, stack::MomentumVector,
-                    geometry::Point, units::si::TimeType, unsigned short, unsigned short>{
-                  idFragm, energy, momentum, pOrig, tOrig, A, Z});
-          Plab_final += pnew.GetMomentum();
-          Elab_final += pnew.GetEnergy();
-        }
-      }
-
-      // secondaries
-      QGSJetIIStack qs;
-      for (auto& psec : qs) {
-
-        auto momentum = psec.GetMomentum(zAxisFrame);
-        auto const energy = psec.GetEnergy();
-
-        momentum.rebase(originalCS); // transform back into standard lab frame
-        std::cout << "secondary fragment> id="
-                  << process::qgsjetII::ConvertFromQgsjetII(psec.GetPID())
-                  << " p=" << momentum.GetComponents() << std::endl;
-        auto pnew = view.AddSecondary(
-            tuple<particles::Code, units::si::HEPEnergyType, stack::MomentumVector,
-                  geometry::Point, units::si::TimeType>{
-                process::qgsjetII::ConvertFromQgsjetII(psec.GetPID()), energy, momentum,
-                pOrig, tOrig});
-        Plab_final += pnew.GetMomentum();
-        Elab_final += pnew.GetEnergy();
-      }
-      cout << "conservation (all GeV): Ecm_final= n/a" /* << Ecm_final / 1_GeV*/ << 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.getEntries() << endl;
-    }
-    return process::EProcessReturn::eOk;
-  }
-
-} // namespace corsika::process::qgsjetII
diff --git a/Processes/QGSJetII/Interaction.h b/Processes/QGSJetII/Interaction.h
deleted file mode 100644
index bef66e3ce3ac61ea8975aaf89ed4ae3645b4d811..0000000000000000000000000000000000000000
--- a/Processes/QGSJetII/Interaction.h
+++ /dev/null
@@ -1,62 +0,0 @@
-/*
- * (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 <corsika/particles/ParticleProperties.h>
-#include <corsika/process/InteractionProcess.h>
-#include <corsika/process/qgsjetII/ParticleConversion.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <string>
-
-namespace corsika::process::qgsjetII {
-
-  class Interaction : public corsika::process::InteractionProcess<Interaction> {
-
-    std::string data_path_;
-    int count_ = 0;
-    bool initialized_ = false;
-    QgsjetIIHadronType alternate_ =
-        QgsjetIIHadronType::PiPlusType; // for pi0, rho0 projectiles
-
-  public:
-    Interaction(const std::string& dataPath = "");
-    ~Interaction();
-
-    bool WasInitialized() { return initialized_; }
-    int GetMaxTargetMassNumber() const { return maxMassNumber_; }
-    bool IsValidTarget(corsika::particles::Code TargetId) const {
-      return (corsika::particles::GetNucleusA(TargetId) < maxMassNumber_) &&
-             corsika::particles::IsNucleus(TargetId);
-    }
-
-    corsika::units::si::CrossSectionType GetCrossSection(
-        const corsika::particles::Code, const corsika::particles::Code,
-        const corsika::units::si::HEPEnergyType, const unsigned int Abeam = 0,
-        const unsigned int Atarget = 0) const;
-
-    template <typename TParticle>
-    corsika::units::si::GrammageType GetInteractionLength(TParticle const&) const;
-
-    /**
-       In this function QGSJETII is called to produce one event. The
-       event is copied (and boosted) into the shower lab frame.
-     */
-
-    template <typename TSecondaryView>
-    corsika::process::EProcessReturn DoInteraction(TSecondaryView&);
-
-  private:
-    corsika::random::RNG& rng_ =
-        corsika::random::RNGManager::GetInstance().GetRandomStream("qgsjet");
-    static constexpr int maxMassNumber_ = 208;
-  };
-
-} // namespace corsika::process::qgsjetII
diff --git a/Processes/QGSJetII/QGSJetIIFragmentsStack.h b/Processes/QGSJetII/QGSJetIIFragmentsStack.h
deleted file mode 100644
index 5da94661c7d054bf93ceae558cab13b9bc3f0310..0000000000000000000000000000000000000000
--- a/Processes/QGSJetII/QGSJetIIFragmentsStack.h
+++ /dev/null
@@ -1,74 +0,0 @@
-/*
- * (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 <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/process/qgsjetII/ParticleConversion.h>
-#include <corsika/process/qgsjetII/qgsjet-II-04.h>
-#include <corsika/stack/Stack.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::process::qgsjetII {
-
-  class QGSJetIIFragmentsStackData {
-
-  public:
-    void Dump() const {}
-
-    void Clear() {
-      qgarr13_.nsf = 0;
-      qgarr55_.nwt = 0;
-    }
-    unsigned int GetSize() const { return qgarr13_.nsf; }
-    unsigned int GetCapacity() const { return iapmax; }
-
-    static unsigned int GetWoundedNucleonsTarget() { return qgarr55_.nwt; }
-    static unsigned int GetWoundedNucleonsProjectile() { return qgarr55_.nwp; }
-
-    int GetFragmentSize(const unsigned int i) const { return qgarr13_.iaf[i]; }
-    void SetFragmentSize(const unsigned int i, const int v) { qgarr13_.iaf[i] = v; }
-
-    void Copy(const unsigned int i1, const unsigned int i2) {
-      qgarr13_.iaf[i2] = qgarr13_.iaf[i1];
-    }
-
-    void Swap(const unsigned int i1, const unsigned int i2) {
-      std::swap(qgarr13_.iaf[i1], qgarr13_.iaf[i2]);
-    }
-
-    void IncrementSize() { qgarr13_.nsf++; }
-    void DecrementSize() {
-      if (qgarr13_.nsf > 0) { qgarr13_.nsf--; }
-    }
-  };
-
-  template <typename StackIteratorInterface>
-  class FragmentsInterface : public corsika::stack::ParticleBase<StackIteratorInterface> {
-
-    using corsika::stack::ParticleBase<StackIteratorInterface>::GetStackData;
-    using corsika::stack::ParticleBase<StackIteratorInterface>::GetIndex;
-
-  public:
-    void SetParticleData(const int vSize) { SetFragmentSize(vSize); }
-
-    void SetParticleData(FragmentsInterface<StackIteratorInterface>& /*parent*/,
-                         const int vSize) {
-      SetFragmentSize(vSize);
-    }
-
-    void SetFragmentSize(const int v) { GetStackData().SetFragmentSize(GetIndex(), v); }
-
-    double GetFragmentSize() const { return GetStackData().GetFragmentSize(GetIndex()); }
-  };
-
-  typedef corsika::stack::Stack<QGSJetIIFragmentsStackData, FragmentsInterface>
-      QGSJetIIFragmentsStack;
-
-} // end namespace corsika::process::qgsjetII
diff --git a/Processes/QGSJetII/QGSJetIIStack.h b/Processes/QGSJetII/QGSJetIIStack.h
deleted file mode 100644
index 04685ab7cf0a3efc89f6c953b6c3f0354758003c..0000000000000000000000000000000000000000
--- a/Processes/QGSJetII/QGSJetIIStack.h
+++ /dev/null
@@ -1,130 +0,0 @@
-/*
- * (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 <corsika/geometry/CoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/process/qgsjetII/ParticleConversion.h>
-#include <corsika/process/qgsjetII/qgsjet-II-04.h>
-#include <corsika/stack/Stack.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::process::qgsjetII {
-
-  typedef corsika::geometry::Vector<corsika::units::si::hepmomentum_d> MomentumVector;
-
-  class QGSJetIIStackData {
-
-  public:
-    void Dump() const {}
-
-    void Clear() {
-      qgarr12_.nsp = 0;
-      qgarr13_.nsf = 0;
-      qgarr55_.nwt = 0;
-    }
-    unsigned int GetSize() const { return qgarr12_.nsp; }
-    unsigned int GetCapacity() const { return nptmax; }
-
-    void SetId(const unsigned int i, const int v) { qgarr14_.ich[i] = v; }
-    void SetEnergy(const unsigned int i, const corsika::units::si::HEPEnergyType v) {
-      using namespace corsika::units::si;
-      qgarr14_.esp[i][0] = v / 1_GeV;
-    }
-
-    void SetMomentum(const unsigned int i, const MomentumVector& v) {
-      using namespace corsika::units::si;
-      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 GetId(const unsigned int i) const { return qgarr14_.ich[i]; }
-    corsika::units::si::HEPEnergyType GetEnergy(const int i) const {
-      using namespace corsika::units::si;
-      return qgarr14_.esp[i][0] * 1_GeV;
-    }
-    MomentumVector GetMomentum(const unsigned int i,
-                               const corsika::geometry::CoordinateSystem& CS) const {
-      using namespace corsika::units::si;
-      geometry::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 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 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 IncrementSize() { qgarr12_.nsp++; }
-    void DecrementSize() {
-      if (qgarr12_.nsp > 0) { qgarr12_.nsp--; }
-    }
-  };
-
-  template <typename StackIteratorInterface>
-  class ParticleInterface : public corsika::stack::ParticleBase<StackIteratorInterface> {
-
-    using corsika::stack::ParticleBase<StackIteratorInterface>::GetStackData;
-    using corsika::stack::ParticleBase<StackIteratorInterface>::GetIndex;
-
-  public:
-    void SetParticleData(const int vID, const corsika::units::si::HEPEnergyType vE,
-                         const MomentumVector& vP,
-                         const corsika::units::si::HEPMassType) {
-      SetPID(vID);
-      SetEnergy(vE);
-      SetMomentum(vP);
-    }
-
-    void SetParticleData(ParticleInterface<StackIteratorInterface>& /*parent*/,
-                         const int vID, const corsika::units::si::HEPEnergyType vE,
-                         const MomentumVector& vP,
-                         const corsika::units::si::HEPMassType) {
-      SetPID(vID);
-      SetEnergy(vE);
-      SetMomentum(vP);
-    }
-
-    void SetEnergy(const corsika::units::si::HEPEnergyType v) {
-      GetStackData().SetEnergy(GetIndex(), v);
-    }
-
-    corsika::units::si::HEPEnergyType GetEnergy() const {
-      return GetStackData().GetEnergy(GetIndex());
-    }
-
-    void SetPID(const int v) { GetStackData().SetId(GetIndex(), v); }
-
-    corsika::process::qgsjetII::QgsjetIICode GetPID() const {
-      return static_cast<corsika::process::qgsjetII::QgsjetIICode>(
-          GetStackData().GetId(GetIndex()));
-    }
-
-    MomentumVector GetMomentum(const corsika::geometry::CoordinateSystem& CS) const {
-      return GetStackData().GetMomentum(GetIndex(), CS);
-    }
-
-    void SetMomentum(const MomentumVector& v) {
-      GetStackData().SetMomentum(GetIndex(), v);
-    }
-  };
-
-  typedef corsika::stack::Stack<QGSJetIIStackData, ParticleInterface> QGSJetIIStack;
-
-} // end namespace corsika::process::qgsjetII
diff --git a/Processes/QGSJetII/testQGSJetII.cc b/Processes/QGSJetII/testQGSJetII.cc
deleted file mode 100644
index 84dad94cfea6e61825e98cedcf4bc9081cc62f38..0000000000000000000000000000000000000000
--- a/Processes/QGSJetII/testQGSJetII.cc
+++ /dev/null
@@ -1,159 +0,0 @@
-/*
- * (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.
- */
-
-#include <corsika/process/qgsjetII/Interaction.h>
-#include <corsika/process/qgsjetII/ParticleConversion.h>
-
-#include <corsika/random/RNGManager.h>
-
-#include <corsika/particles/ParticleProperties.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <catch2/catch.hpp>
-
-#include <cstdlib>
-#include <experimental/filesystem>
-#include <iostream>
-
-using namespace corsika;
-using namespace corsika::process::qgsjetII;
-using namespace corsika::units::si;
-
-template <typename TStackView>
-auto sumCharge(TStackView const& view) {
-  int totalCharge = 0;
-
-  for (auto const& p : view) { totalCharge += particles::GetChargeNumber(p.GetPID()); }
-
-  return totalCharge;
-}
-
-template <typename TStackView>
-auto sumMomentum(TStackView const& view, geometry::CoordinateSystem const& vCS) {
-  geometry::Vector<hepenergy_d> sum{vCS, 0_eV, 0_eV, 0_eV};
-
-  for (auto const& p : view) { sum += p.GetMomentum(); }
-
-  return sum;
-}
-
-TEST_CASE("CORSIKA_DATA", "[processes]") {
-
-  SECTION("check CORSIKA_DATA") {
-
-    const char* data = std::getenv("CORSIKA_DATA");
-    // these REQUIRES are needed:
-    REQUIRE(data != 0);
-    REQUIRE(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;
-  }
-}
-
-TEST_CASE("QgsjetII", "[processes]") {
-
-  SECTION("Corsika -> QgsjetII") {
-    CHECK(process::qgsjetII::ConvertToQgsjetII(particles::PiMinus::GetCode()) ==
-          process::qgsjetII::QgsjetIICode::PiMinus);
-    CHECK(process::qgsjetII::ConvertToQgsjetIIRaw(particles::Proton::GetCode()) == 2);
-  }
-
-  SECTION("QgsjetII -> Corsika") {
-    CHECK(particles::PiPlus::GetCode() == process::qgsjetII::ConvertFromQgsjetII(
-                                              process::qgsjetII::QgsjetIICode::PiPlus));
-  }
-
-  SECTION("Corsika -> QgsjetII") {
-    CHECK(process::qgsjetII::ConvertToQgsjetII(particles::PiMinus::GetCode()) ==
-          process::qgsjetII::QgsjetIICode::PiMinus);
-    CHECK(process::qgsjetII::ConvertToQgsjetIIRaw(particles::Proton::GetCode()) == 2);
-  }
-
-  SECTION("canInteractInQgsjetII") {
-
-    CHECK(process::qgsjetII::CanInteract(particles::Proton::GetCode()));
-    CHECK(process::qgsjetII::CanInteract(particles::Code::KPlus));
-    CHECK(process::qgsjetII::CanInteract(particles::Nucleus::GetCode()));
-    // CHECK(process::qgsjetII::CanInteract(particles::Helium::GetCode()));
-
-    CHECK_FALSE(process::qgsjetII::CanInteract(particles::EtaC::GetCode()));
-    CHECK_FALSE(process::qgsjetII::CanInteract(particles::SigmaC0::GetCode()));
-  }
-
-  SECTION("cross-section type") {
-
-    CHECK(process::qgsjetII::GetQgsjetIIXSCode(particles::Code::Neutron) ==
-          process::qgsjetII::QgsjetIIXSClass::Baryons);
-    CHECK(process::qgsjetII::GetQgsjetIIXSCode(particles::Code::K0Long) ==
-          process::qgsjetII::QgsjetIIXSClass::Kaons);
-    CHECK(process::qgsjetII::GetQgsjetIIXSCode(particles::Code::Proton) ==
-          process::qgsjetII::QgsjetIIXSClass::Baryons);
-    CHECK(process::qgsjetII::GetQgsjetIIXSCode(particles::Code::PiMinus) ==
-          process::qgsjetII::QgsjetIIXSClass::LightMesons);
-  }
-}
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/setup/SetupEnvironment.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-using namespace corsika::units::si;
-using namespace corsika::units;
-using namespace corsika;
-
-TEST_CASE("QgsjetIIInterface", "[processes]") {
-
-  auto [env, csPtr, nodePtr] = setup::testing::setupEnvironment(particles::Code::Oxygen);
-  [[maybe_unused]] auto const& env_dummy = env;
-  [[maybe_unused]] auto const& node_dummy = nodePtr;
-
-  corsika::random::RNGManager::GetInstance().RegisterRandomStream("qgsjet");
-
-  SECTION("InteractionInterface") {
-
-    auto [stackPtr, secViewPtr] = setup::testing::setupStack(particles::Code::Proton, 0,
-                                                             0, 110_GeV, nodePtr, *csPtr);
-    setup::StackView& view = *(secViewPtr.get());
-    auto particle = stackPtr->first();
-    auto projectile = secViewPtr->GetProjectile();
-    auto const projectileMomentum = projectile.GetMomentum();
-
-    Interaction model;
-
-    [[maybe_unused]] const process::EProcessReturn ret = model.DoInteraction(view);
-    [[maybe_unused]] const GrammageType length = model.GetInteractionLength(particle);
-
-    CHECK(length / (1_g / square(1_cm)) == Approx(93.04).margin(0.1));
-
-    /***********************************
-     It as turned out already two times (#291 and #307) that the detailed output of
-    QGSJetII event generation depends on the gfortran version used. This is not reliable
-    and cannot be tested in a unit test here. One related problem was already found (#291)
-    and is realted to undefined behaviour in the evaluation of functions in logical
-    expressions. It is not clear if #307 is the same issue.
-
-     CHECK(view.GetSize() == 14);
-     CHECK(sumCharge(view) == 2);
-    ************************************/
-    auto const secMomSum = sumMomentum(view, projectileMomentum.GetCoordinateSystem());
-    CHECK((secMomSum - projectileMomentum).norm() / projectileMomentum.norm() ==
-          Approx(0).margin(1e-2));
-  }
-}
diff --git a/Processes/Sibyll/CMakeLists.txt b/Processes/Sibyll/CMakeLists.txt
deleted file mode 100644
index 856135c789ff02fa40fd77b3e18d93f68a8e3dfa..0000000000000000000000000000000000000000
--- a/Processes/Sibyll/CMakeLists.txt
+++ /dev/null
@@ -1,111 +0,0 @@
-add_custom_command (
-  OUTPUT  ${PROJECT_BINARY_DIR}/Processes/Sibyll/Generated.inc
-  COMMAND ${PROJECT_SOURCE_DIR}/Processes/Sibyll/code_generator.py 
-          ${PROJECT_BINARY_DIR}/Framework/Particles/particle_db.pkl
-          ${PROJECT_SOURCE_DIR}/Processes/Sibyll/sibyll_codes.dat
-  DEPENDS code_generator.py
-          sibyll_codes.dat          
-          CORSIKAparticles
-  WORKING_DIRECTORY
-          ${PROJECT_BINARY_DIR}/Processes/Sibyll/
-  COMMENT "Generate conversion tables for particle codes SIBYLL <-> CORSIKA"
-  VERBATIM
-  )
-  
-set (
-  MODEL_SOURCES
-  ParticleConversion.cc
-  Interaction.cc
-  Decay.cc
-  NuclearInteraction.cc
-  sibyll2.3d.f
-  nuclib.f
-  signuc.f
-  sibyll2.3d.cc
-  gasdev.f
-  )
-
-set (
-  MODEL_HEADERS
-  ParticleConversion.h
-  sibyll2.3d.h
-  nuclib.h
-  SibStack.h
-  Decay.h
-  Interaction.h
-  NuclearInteraction.h
-  ${PROJECT_BINARY_DIR}/Processes/Sibyll/Generated.inc
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/sibyll
-  )
-
-add_library (ProcessSibyll STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessSibyll ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-# ....................................................
-# since Generated.inc is an automatically produced file in the build directory,
-# create a symbolic link into the source tree, so that it can be found and edited more easily
-# this is not needed for the build to succeed! .......
-add_custom_command (
-  OUTPUT  ${CMAKE_CURRENT_SOURCE_DIR}/Generated.inc
-  COMMAND ${CMAKE_COMMAND} -E create_symlink ${PROJECT_BINARY_DIR}/include/corsika/process/sibyll/Generated.inc ${CMAKE_CURRENT_SOURCE_DIR}/Generated.inc
-  COMMENT "Generate link in source-dir: ${CMAKE_CURRENT_SOURCE_DIR}/Generated.inc"
-  )
-add_custom_target (SourceDirLinkSib DEPENDS ${PROJECT_BINARY_DIR}/Processes/Sibyll/Generated.inc)
-add_dependencies (ProcessSibyll SourceDirLinkSib)
-# .....................................................
-
-
-
-set_target_properties (
-  ProcessSibyll
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessSibyll
-  CORSIKAprocesssequence
-  CORSIKAparticles
-  CORSIKAutilities
-  CORSIKAunits
-  CORSIKAthirdparty
-  CORSIKAgeometry
-  CORSIKAenvironment
-  CORSIKAsetup
-  CORSIKArandom
-  CORSIKAlogging
-  )
-
-target_include_directories (
-  ProcessSibyll 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS ProcessSibyll
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-  )
-
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST(testSibyll
-  SOURCES
-  testSibyll.cc
-  ${MODEL_HEADERS}
-)
-
-target_link_libraries (
-  testSibyll
-  ProcessSibyll
-  CORSIKAtesting
-  )
diff --git a/Processes/Sibyll/Decay.cc b/Processes/Sibyll/Decay.cc
deleted file mode 100644
index 8297739b793e33d7b4fe9b65bfd66def992f372e..0000000000000000000000000000000000000000
--- a/Processes/Sibyll/Decay.cc
+++ /dev/null
@@ -1,232 +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/process/sibyll/Decay.h>
-
-#include <corsika/process/sibyll/ParticleConversion.h>
-#include <corsika/process/sibyll/SibStack.h>
-
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-using std::make_tuple;
-using std::tuple;
-using std::vector;
-
-using namespace corsika;
-using namespace corsika::setup;
-
-using SetupView = corsika::setup::StackView;
-using SetupProjectile = corsika::setup::StackView::ParticleType;
-using Particle = corsika::setup::Stack::ParticleType;
-
-namespace corsika::process::sibyll {
-
-  Decay::Decay(const bool sibyll_printout_on)
-      : sibyll_listing_(sibyll_printout_on) {
-    // switch off decays to avoid internal decay chains
-    SetAllStable();
-    // handle all decays by default
-    handleAllDecays_ = true;
-  }
-
-  Decay::Decay(std::set<particles::Code> vHandled)
-      : handleAllDecays_(false)
-      , handledDecays_(vHandled) {
-    SetAllStable();
-  }
-
-  Decay::~Decay() { C8LOG_DEBUG("Sibyll::Decay n={}", count_); }
-
-  bool Decay::CanHandleDecay(const particles::Code vParticleCode) {
-    using namespace corsika::particles;
-    // if known to sibyll and not proton or neutrino it can decay
-    if (vParticleCode == Code::Proton || vParticleCode == Code::AntiProton ||
-        vParticleCode == Code::NuE || vParticleCode == Code::NuMu ||
-        vParticleCode == Code::NuTau || vParticleCode == Code::NuEBar ||
-        vParticleCode == Code::NuMuBar || vParticleCode == Code::NuTauBar ||
-        vParticleCode == Code::Electron || vParticleCode == Code::Positron)
-      return false;
-    else if (process::sibyll::ConvertToSibyllRaw(
-                 vParticleCode)) // non-zero for particles known to sibyll
-      return true;
-    else
-      return false;
-  }
-
-  void Decay::SetHandleDecay(const particles::Code vParticleCode) {
-    handleAllDecays_ = false;
-    C8LOG_DEBUG("Sibyll::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 sibyll!");
-  }
-
-  void Decay::SetHandleDecay(const vector<particles::Code> vParticleList) {
-    handleAllDecays_ = false;
-    for (auto p : vParticleList) Decay::SetHandleDecay(p);
-  }
-
-  bool Decay::IsDecayHandled(const corsika::particles::Code vParticleCode) {
-    if (handleAllDecays_ && Decay::CanHandleDecay(vParticleCode))
-      return true;
-    else
-      return Decay::handledDecays_.find(vParticleCode) != Decay::handledDecays_.end()
-                 ? true
-                 : false;
-  }
-
-  void Decay::SetStable(const vector<particles::Code> vParticleList) {
-    for (auto p : vParticleList) Decay::SetStable(p);
-  }
-
-  void Decay::SetUnstable(const vector<particles::Code> vParticleList) {
-    for (auto p : vParticleList) Decay::SetUnstable(p);
-  }
-
-  bool Decay::IsStable(const particles::Code vCode) {
-    return abs(process::sibyll::ConvertToSibyllRaw(vCode)) <= 0 ? true : false;
-  }
-
-  bool Decay::IsUnstable(const particles::Code vCode) {
-    return abs(process::sibyll::ConvertToSibyllRaw(vCode)) > 0 ? true : false;
-  }
-
-  void Decay::SetDecay(const particles::Code vCode, const bool vMakeUnstable) {
-    vMakeUnstable ? SetUnstable(vCode) : SetStable(vCode);
-  }
-
-  void Decay::SetUnstable(const particles::Code vCode) {
-    C8LOG_DEBUG("Sibyll::Decay: setting {} unstable. ", vCode);
-    const int s_id = abs(process::sibyll::ConvertToSibyllRaw(vCode));
-    s_csydec_.idb[s_id - 1] = abs(s_csydec_.idb[s_id - 1]);
-  }
-
-  void Decay::SetStable(const particles::Code vCode) {
-    C8LOG_DEBUG("Sibyll::Decay: setting {} stable. ", vCode);
-    const int s_id = abs(process::sibyll::ConvertToSibyllRaw(vCode));
-    s_csydec_.idb[s_id - 1] = (-1) * abs(s_csydec_.idb[s_id - 1]);
-  }
-
-  void Decay::SetAllStable() {
-    for (int i = 0; i < 99; ++i) s_csydec_.idb[i] = -1 * abs(s_csydec_.idb[i]);
-  }
-
-  void Decay::SetAllUnstable() {
-    for (int i = 0; i < 99; ++i) s_csydec_.idb[i] = abs(s_csydec_.idb[i]);
-  }
-
-  void Decay::PrintDecayConfig([[maybe_unused]] const particles::Code vCode) {
-    [[maybe_unused]] const int sibCode = process::sibyll::ConvertToSibyllRaw(vCode);
-    [[maybe_unused]] const int absSibCode = abs(sibCode);
-    C8LOG_DEBUG("Decay: Sibyll decay configuration: {} is {}", vCode,
-                (s_csydec_.idb[absSibCode - 1] <= 0) ? "stable" : "unstable");
-  }
-
-  void Decay::PrintDecayConfig() {
-    C8LOG_DEBUG("Sibyll::Decay: decay configuration:");
-    if (handleAllDecays_) {
-      C8LOG_DEBUG("     all particles known to Sibyll are handled by Sibyll::Decay!");
-    } else {
-      for ([[maybe_unused]] auto& pCode : handledDecays_) {
-        C8LOG_DEBUG("      Decay of {}  is handled by Sibyll!", pCode);
-      }
-    }
-  }
-
-  template <>
-  units::si::TimeType Decay::GetLifetime(Particle const& vP) {
-    using namespace units::si;
-
-    const particles::Code pid = vP.GetPID();
-    if (Decay::IsDecayHandled(pid)) {
-      HEPEnergyType E = vP.GetEnergy();
-      HEPMassType m = vP.GetMass();
-
-      const double gamma = E / m;
-
-      const TimeType t0 = particles::GetLifetime(vP.GetPID());
-      auto const lifetime = gamma * t0;
-
-      [[maybe_unused]] const auto mkin =
-          (E * E - vP.GetMomentum().squaredNorm()); // delta_mass(vP.GetMomentum(), E, m);
-      C8LOG_DEBUG("Sibyll::Decay: code: {} ", vP.GetPID());
-      C8LOG_DEBUG("Sibyll::Decay: MinStep: t0: {} ", t0);
-      C8LOG_DEBUG("Sibyll::Decay: MinStep: energy: {} GeV ", E / 1_GeV);
-      C8LOG_DEBUG("Sibyll::Decay: momentum: {} GeV ",
-                  vP.GetMomentum().GetComponents() / 1_GeV);
-      C8LOG_DEBUG("Sibyll::Decay: momentum: shell mass-kin. inv. mass {} {}",
-                  mkin / 1_GeV / 1_GeV, m / 1_GeV * m / 1_GeV);
-      [[maybe_unused]] auto sib_id = process::sibyll::ConvertToSibyllRaw(vP.GetPID());
-      C8LOG_DEBUG("Sibyll::Decay: sib mass: {}", get_sibyll_mass2(sib_id));
-      C8LOG_DEBUG("Sibyll::Decay: MinStep: gamma:  {}", gamma);
-      C8LOG_DEBUG("Sibyll::Decay: MinStep: tau {} s: ", lifetime / 1_s);
-
-      return lifetime;
-    } else
-      return std::numeric_limits<double>::infinity() * 1_s;
-  }
-
-  template <>
-  void Decay::DoDecay(SetupView& view) {
-    using geometry::Point;
-    using namespace units::si;
-
-    auto const projectile = view.GetProjectile();
-
-    const particles::Code pCode = projectile.GetPID();
-    // check if sibyll is configured to handle this decay!
-    if (!IsDecayHandled(pCode))
-      throw std::runtime_error("STOP! Sibyll not configured to execute this decay!");
-
-    count_++;
-    SibStack ss;
-    ss.Clear();
-
-    // copy particle to sibyll stack
-    ss.AddParticle(process::sibyll::ConvertToSibyllRaw(pCode), projectile.GetEnergy(),
-                   projectile.GetMomentum(),
-                   // setting particle mass with Corsika values, may be inconsistent
-                   // with sibyll internal values
-                   particles::GetMass(pCode));
-    // remember position
-    Point const decayPoint = projectile.GetPosition();
-    TimeType const t0 = projectile.GetTime();
-    // remember if particles is unstable
-    // auto const priorIsUnstable = IsUnstable(pCode);
-    // switch on decay for this particle
-    SetUnstable(pCode);
-    PrintDecayConfig(pCode);
-
-    // call sibyll decay
-    C8LOG_DEBUG("Decay: calling Sibyll decay routine..");
-    decsib_();
-
-    if (sibyll_listing_) {
-      // print output
-      int print_unit = 6;
-      sib_list_(print_unit);
-    }
-
-    // reset to stable
-    SetStable(pCode);
-
-    // copy particles from sibyll stack to corsika
-    for (auto& psib : ss) {
-      // FOR NOW: skip particles that have decayed in Sibyll, move to iterator?
-      if (psib.HasDecayed()) continue;
-      // add to corsika stack
-      view.AddSecondary(make_tuple(process::sibyll::ConvertFromSibyll(psib.GetPID()),
-                                   psib.GetEnergy(), psib.GetMomentum(), decayPoint, t0));
-    }
-    // empty sibyll stack
-    ss.Clear();
-  }
-
-} // namespace corsika::process::sibyll
diff --git a/Processes/Sibyll/Decay.h b/Processes/Sibyll/Decay.h
deleted file mode 100644
index e5862344e86c13d6dbc11ee78246987fe3fcf198..0000000000000000000000000000000000000000
--- a/Processes/Sibyll/Decay.h
+++ /dev/null
@@ -1,87 +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/particles/ParticleProperties.h>
-#include <corsika/process/DecayProcess.h>
-#include <corsika/process/SecondariesProcess.h>
-
-#include <set>
-#include <vector>
-
-namespace corsika::process {
-
-  namespace sibyll {
-
-    class Decay : public corsika::process::DecayProcess<Decay> {
-      int count_ = 0;
-      bool handleAllDecays_ = true;
-      bool sibyll_listing_ = false;
-
-    public:
-      Decay(const bool sibyll_listing = false);
-      Decay(std::set<particles::Code>);
-      ~Decay();
-
-      void PrintDecayConfig(const corsika::particles::Code);
-      void PrintDecayConfig();
-      void SetHadronsUnstable();
-
-      // is Sibyll::Decay set to handle the decay of this particle?
-      bool IsDecayHandled(const corsika::particles::Code);
-
-      // is decay possible in principle?
-      bool CanHandleDecay(const corsika::particles::Code);
-
-      // set Sibyll::Decay to handle the decay of this particle!
-      void SetHandleDecay(const corsika::particles::Code);
-      // set Sibyll::Decay to handle the decay of this list of particles!
-      void SetHandleDecay(const std::vector<particles::Code>);
-      // set Sibyll::Decay to handle all particle decays
-      void SetHandleAllDecay();
-
-      template <typename TParticle>
-      corsika::units::si::TimeType GetLifetime(TParticle const&);
-
-      /**
-       In this function SIBYLL is called to produce to decay the input particle.
-     */
-
-      template <typename TSecondaryView>
-      void DoDecay(TSecondaryView&);
-
-      template <typename TParticleView>
-      EProcessReturn DoSecondaries(TParticleView&);
-
-    private:
-      // internal routines to set particles stable and unstable in the COMMON blocks in
-      // sibyll
-      void SetStable(const std::vector<particles::Code>);
-      void SetUnstable(const std::vector<particles::Code>);
-
-      void SetStable(const corsika::particles::Code);
-      void SetUnstable(const corsika::particles::Code);
-
-      // internally set all particles to decay/not to decay
-      void SetAllUnstable();
-      void SetAllStable();
-
-      // will this particle be stable in sibyll ?
-      bool IsStable(const corsika::particles::Code);
-      // will this particle decay in sibyll ?
-      bool IsUnstable(const corsika::particles::Code);
-      // set particle with input code to decay or not
-      void SetDecay(const particles::Code, const bool);
-
-      std::set<particles::Code> handledDecays_;
-    };
-
-  } // namespace sibyll
-
-} // namespace corsika::process
diff --git a/Processes/Sibyll/Interaction.h b/Processes/Sibyll/Interaction.h
deleted file mode 100644
index 52a43309d06efe3f0fbc622b84dc29a82c9d7328..0000000000000000000000000000000000000000
--- a/Processes/Sibyll/Interaction.h
+++ /dev/null
@@ -1,70 +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/particles/ParticleProperties.h>
-#include <corsika/process/InteractionProcess.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <tuple>
-
-namespace corsika::process::sibyll {
-
-  class Interaction : public corsika::process::InteractionProcess<Interaction> {
-
-    int count_ = 0;
-    int nucCount_ = 0;
-    static bool initialized_; ///! flag to assure init is done only once
-    bool sibyll_listing_;
-
-  public:
-    Interaction(const bool sibyll_printout_on = false);
-    ~Interaction();
-
-    void SetAllStable();
-
-    static bool WasInitialized() { return initialized_; }
-    bool IsValidCoMEnergy(corsika::units::si::HEPEnergyType ecm) const {
-      return (minEnergyCoM_ <= ecm) && (ecm <= maxEnergyCoM_);
-    }
-    int GetMaxTargetMassNumber() const { return maxTargetMassNumber_; }
-    corsika::units::si::HEPEnergyType GetMinEnergyCoM() const { return minEnergyCoM_; }
-    corsika::units::si::HEPEnergyType GetMaxEnergyCoM() const { return maxEnergyCoM_; }
-    bool IsValidTarget(corsika::particles::Code TargetId) const {
-      return (corsika::particles::GetNucleusA(TargetId) < maxTargetMassNumber_) &&
-             corsika::particles::IsNucleus(TargetId);
-    }
-
-    std::tuple<corsika::units::si::CrossSectionType, corsika::units::si::CrossSectionType>
-    GetCrossSection(const corsika::particles::Code, const corsika::particles::Code,
-                    const corsika::units::si::HEPEnergyType) const;
-
-    template <typename TParticle>
-    corsika::units::si::GrammageType GetInteractionLength(const TParticle&) const;
-
-    /**
-       In this function SIBYLL is called to produce one event. The
-       event is copied (and boosted) into the shower lab frame.
-     */
-
-    template <typename TSecondaryView>
-    corsika::process::EProcessReturn DoInteraction(TSecondaryView&);
-
-  private:
-    corsika::random::RNG& RNG_ =
-        corsika::random::RNGManager::GetInstance().GetRandomStream("sibyll");
-
-    const corsika::units::si::HEPEnergyType minEnergyCoM_ =
-        10. * 1e9 * corsika::units::si::electronvolt;
-    const corsika::units::si::HEPEnergyType maxEnergyCoM_ =
-        1.e6 * 1e9 * corsika::units::si::electronvolt;
-    const int maxTargetMassNumber_ = 18;
-  };
-
-} // namespace corsika::process::sibyll
diff --git a/Processes/Sibyll/NuclearInteraction.cc b/Processes/Sibyll/NuclearInteraction.cc
deleted file mode 100644
index 4c22491a41ffaec2a24c1870357a132542a59143..0000000000000000000000000000000000000000
--- a/Processes/Sibyll/NuclearInteraction.cc
+++ /dev/null
@@ -1,640 +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/process/sibyll/Interaction.h>
-#include <corsika/process/sibyll/NuclearInteraction.h>
-
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/geometry/FourVector.h>
-#include <corsika/process/sibyll/nuclib.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/COMBoost.h>
-
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <corsika/logging/Logging.h>
-
-#include <set>
-#include <sstream>
-
-using std::make_tuple;
-using std::tuple;
-using std::vector;
-
-using namespace corsika;
-using Particle = corsika::setup::Stack::ParticleType; // StackIterator; // ParticleType;
-using View = corsika::setup::StackView;               // StackView::ParticleType;
-using Track = setup::Trajectory;
-
-namespace corsika::process::sibyll {
-
-  template <>
-  NuclearInteraction<setup::Environment>::~NuclearInteraction() {
-    C8LOG_DEBUG(
-        fmt::format("Nuclib::NuclearInteraction n={} Nnuc={}", count_, nucCount_));
-  }
-
-  template <>
-  void NuclearInteraction<setup::Environment>::PrintCrossSectionTable(
-      corsika::particles::Code pCode) {
-    using namespace corsika::particles;
-    const int k = targetComponentsIndex_.at(pCode);
-    Code pNuclei[] = {Code::Helium, Code::Lithium7, Code::Oxygen,
-                      Code::Neon,   Code::Argon,    Code::Iron};
-    std::ostringstream table;
-    table << "Nuclear CrossSectionTable pCode=" << pCode << " :\n en/A ";
-    for (auto& j : pNuclei) table << std::setw(9) << j;
-    table << "\n";
-
-    // loop over energy bins
-    for (unsigned int i = 0; i < GetNEnergyBins(); ++i) {
-      table << " " << i << "  ";
-      for (auto& n : pNuclei) {
-        auto const j = GetNucleusA(n);
-        table << " " << std::setprecision(5) << std::setw(8)
-              << cnucsignuc_.sigma[j - 1][k][i];
-      }
-      table << "\n";
-    }
-    C8LOG_DEBUG(table.str());
-  }
-
-  template <>
-  void NuclearInteraction<setup::Environment>::InitializeNuclearCrossSections() {
-    using namespace corsika::particles;
-    using namespace units::si;
-
-    auto& universe = *(environment_.GetUniverse());
-
-    auto const allElementsInUniverse = std::invoke([&]() {
-      std::set<particles::Code> allElementsInUniverse;
-      auto collectElements = [&](auto& vtn) {
-        if (vtn.HasModelProperties()) {
-          auto const& comp =
-              vtn.GetModelProperties().GetNuclearComposition().GetComponents();
-          for (auto const c : comp) allElementsInUniverse.insert(c);
-        }
-      };
-      universe.walk(collectElements);
-      return allElementsInUniverse;
-    });
-
-    C8LOG_DEBUG("NuclearInteraction: initializing nuclear cross sections...");
-
-    // loop over target components, at most 4!!
-    int k = -1;
-    for (auto& ptarg : allElementsInUniverse) {
-      ++k;
-      C8LOG_DEBUG(fmt::format("NuclearInteraction: init target component: {}", ptarg));
-      const int ib = GetNucleusA(ptarg);
-      if (!hadronicInteraction_.IsValidTarget(ptarg)) {
-        C8LOG_DEBUG(fmt::format(
-            "NuclearInteraction::InitializeNuclearCrossSections: target nucleus? id={}",
-            ptarg));
-        throw std::runtime_error(
-            " target can not be handled by hadronic interaction model! ");
-      }
-      targetComponentsIndex_.insert(std::pair<Code, int>(ptarg, k));
-      // loop over energies, fNEnBins log. energy bins
-      for (unsigned int i = 0; i < GetNEnergyBins(); ++i) {
-        // hard coded energy grid, has to be aligned to definition in signuc2!!, no
-        // comment..
-        const units::si::HEPEnergyType Ecm = pow(10., 1. + 1. * i) * 1_GeV;
-        // get p-p cross sections
-        auto const protonId = Code::Proton;
-        auto const [siginel, sigela] =
-            hadronicInteraction_.GetCrossSection(protonId, protonId, Ecm);
-        const double dsig = siginel / 1_mb;
-        const double dsigela = sigela / 1_mb;
-        // loop over projectiles, mass numbers from 2 to fMaxNucleusAProjectile
-        for (unsigned int j = 1; j < gMaxNucleusAProjectile_; ++j) {
-          const int jj = j + 1;
-          double sig_out, dsig_out, sigqe_out, dsigqe_out;
-          sigma_mc_(jj, ib, dsig, dsigela, gNSample_, sig_out, dsig_out, sigqe_out,
-                    dsigqe_out);
-          // write to table
-          cnucsignuc_.sigma[j][k][i] = sig_out;
-          cnucsignuc_.sigqe[j][k][i] = sigqe_out;
-        }
-      }
-    }
-    C8LOG_DEBUG(
-        fmt::format("NuclearInteraction: cross sections for {} "
-                    " components initialized!",
-                    targetComponentsIndex_.size()));
-    for (auto& ptarg : allElementsInUniverse) { PrintCrossSectionTable(ptarg); }
-  }
-
-  template <>
-  units::si::CrossSectionType
-  NuclearInteraction<setup::Environment>::ReadCrossSectionTable(
-      const int ia, particles::Code pTarget, units::si::HEPEnergyType elabnuc) {
-    using namespace corsika::particles;
-    using namespace units::si;
-    const int ib = targetComponentsIndex_.at(pTarget) + 1; // table index in fortran
-    auto const ECoMNuc = sqrt(2. * corsika::units::constants::nucleonMass * elabnuc);
-    if (ECoMNuc < GetMinEnergyPerNucleonCoM() || ECoMNuc > GetMaxEnergyPerNucleonCoM())
-      throw std::runtime_error("NuclearInteraction: energy outside tabulated range!");
-    const double e0 = elabnuc / 1_GeV;
-    double sig;
-    C8LOG_DEBUG(fmt::format("ReadCrossSectionTable: {} {} {}", ia, ib, e0));
-    signuc2_(ia, ib, e0, sig);
-    C8LOG_DEBUG(fmt::format("ReadCrossSectionTable: sig={}", sig));
-    return sig * 1_mb;
-  }
-
-  // TODO: remove elastic cross section?
-  template <>
-  template <>
-  tuple<units::si::CrossSectionType, units::si::CrossSectionType>
-  NuclearInteraction<setup::Environment>::GetCrossSection(
-      Particle const& vP, const particles::Code TargetId) {
-    using namespace units::si;
-    if (vP.GetPID() != particles::Code::Nucleus)
-      throw std::runtime_error(
-          "NuclearInteraction: GetCrossSection: particle not a nucleus!");
-
-    const unsigned int iBeamA = vP.GetNuclearA();
-    HEPEnergyType LabEnergyPerNuc = vP.GetEnergy() / iBeamA;
-    C8LOG_DEBUG(
-        fmt::format("NuclearInteraction: GetCrossSection: called with: beamNuclA={} "
-                    " TargetId={} LabEnergyPerNuc={}GeV ",
-                    iBeamA, TargetId, LabEnergyPerNuc / 1_GeV));
-
-    // use nuclib to calc. nuclear cross sections
-    // TODO: for now assumes air with hard coded composition
-    // extend to arbitrary mixtures, requires smarter initialization
-    // get nuclib projectile code: nucleon number
-    if (iBeamA > GetMaxNucleusAProjectile() || iBeamA < 2) {
-      C8LOG_DEBUG(
-          "NuclearInteraction: beam nucleus outside allowed range for NUCLIB!"
-          "A=" +
-          std::to_string(iBeamA));
-      throw std::runtime_error(
-          "NuclearInteraction: GetCrossSection: beam nucleus outside allowed range for "
-          "NUCLIB!");
-    }
-
-    if (hadronicInteraction_.IsValidTarget(TargetId)) {
-      auto const sigProd = ReadCrossSectionTable(iBeamA, TargetId, LabEnergyPerNuc);
-      C8LOG_DEBUG("cross section (mb): " + std::to_string(sigProd / 1_mb));
-      return std::make_tuple(sigProd, 0_mb);
-    } else {
-      throw std::runtime_error("target outside range.");
-    }
-    return std::make_tuple(std::numeric_limits<double>::infinity() * 1_mb,
-                           std::numeric_limits<double>::infinity() * 1_mb);
-  }
-
-  template <>
-  template <>
-  units::si::GrammageType NuclearInteraction<setup::Environment>::GetInteractionLength(
-      Particle const& vP) {
-
-    using namespace units;
-    using namespace units::si;
-    using namespace geometry;
-
-    // coordinate system, get global frame of reference
-    CoordinateSystem& rootCS =
-        RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-    const particles::Code corsikaBeamId = vP.GetPID();
-
-    if (corsikaBeamId != particles::Code::Nucleus) {
-      // check if target-style nucleus (enum), these are not allowed as projectile
-      if (particles::IsNucleus(corsikaBeamId))
-        throw std::runtime_error(
-            "NuclearInteraction: GetInteractionLength: Wrong nucleus type. Nuclear "
-            "projectiles should use NuclearStackExtension!");
-      else {
-        // no nuclear interaction
-        return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
-      }
-    }
-
-    // read from cross section code table
-
-    // FOR NOW: assume target is at rest
-    corsika::stack::MomentumVector pTarget(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
-
-    // total momentum and energy
-    HEPEnergyType Elab = vP.GetEnergy() + constants::nucleonMass;
-    unsigned int const nuclA = vP.GetNuclearA();
-    auto const ElabNuc = vP.GetEnergy() / nuclA;
-
-    corsika::stack::MomentumVector pTotLab(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
-    pTotLab += vP.GetMomentum();
-    pTotLab += pTarget;
-    auto const pTotLabNorm = pTotLab.norm();
-    // calculate cm. energy
-    [[maybe_unused]] const HEPEnergyType ECoM = sqrt(
-        (Elab + pTotLabNorm) * (Elab - pTotLabNorm)); // binomial for numerical accuracy
-    auto const ECoMNN = sqrt(2. * ElabNuc * constants::nucleonMass);
-    C8LOG_DEBUG(
-        fmt::format("NuclearInteraction: LambdaInt: \n"
-                    " input energy: {}GeV\n"
-                    " input energy CoM: {}GeV\n"
-                    " beam pid: {}\n"
-                    " beam A: {}\n"
-                    " input energy per nucleon: {}GeV\n"
-                    " input energy CoM per nucleon: {}GeV ",
-                    Elab / 1_GeV, ECoM / 1_GeV, particles::GetName(corsikaBeamId), nuclA,
-                    ElabNuc / 1_GeV, ECoMNN / 1_GeV));
-    //      throw std::runtime_error("stop here");
-
-    // energy limits
-    // TODO: values depend on hadronic interaction model !! this is sibyll specific
-    if (ElabNuc >= 8.5_GeV && ECoMNN >= gMinEnergyPerNucleonCoM_ &&
-        ECoMNN < gMaxEnergyPerNucleonCoM_) {
-
-      // get target from environment
-      /*
-        the target should be defined by the Environment,
-        ideally as full particle object so that the four momenta
-        and the boosts can be defined..
-      */
-      auto const* const currentNode = vP.GetNode();
-      auto const& mediumComposition =
-          currentNode->GetModelProperties().GetNuclearComposition();
-      // determine average interaction length
-      // weighted sum
-      int i = -1;
-      si::CrossSectionType weightedProdCrossSection = 0_mb;
-      // get weights of components from environment/medium
-      const auto& w = mediumComposition.GetFractions();
-      // loop over components in medium
-      for (auto const targetId : mediumComposition.GetComponents()) {
-        i++;
-        C8LOG_DEBUG("NuclearInteraction: get interaction length for target: " +
-                    particles::GetName(targetId));
-        auto const [productionCrossSection, elaCrossSection] =
-            GetCrossSection(vP, targetId);
-        [[maybe_unused]] auto& dummy_elaCrossSection = elaCrossSection;
-
-        C8LOG_DEBUG(
-            "NuclearInteraction: "
-            "IntLength: nuclib return (mb): " +
-            std::to_string(productionCrossSection / 1_mb));
-        weightedProdCrossSection += w[i] * productionCrossSection;
-      }
-      C8LOG_DEBUG(
-          "NuclearInteraction: "
-          "IntLength: weighted CrossSection (mb): " +
-          std::to_string(weightedProdCrossSection / 1_mb));
-
-      // calculate interaction length in medium
-      GrammageType const int_length = mediumComposition.GetAverageMassNumber() *
-                                      units::constants::u / weightedProdCrossSection;
-      C8LOG_DEBUG(
-          "NuclearInteraction: "
-          "interaction length (g/cm2): " +
-          std::to_string(int_length * (1_cm * 1_cm / (0.001_kg))));
-
-      return int_length;
-    } else {
-      return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
-    }
-  }
-
-  template <>
-  template <>
-  process::EProcessReturn NuclearInteraction<setup::Environment>::DoInteraction(
-      View& view) {
-
-    // this routine superimposes different nucleon-nucleon interactions
-    // in a nucleus-nucleus interaction, based the SIBYLL routine SIBNUC
-
-    using namespace units;
-    using namespace utl;
-    using namespace units::si;
-    using namespace geometry;
-
-    auto projectile = view.GetProjectile();
-
-    const auto ProjId = projectile.GetPID();
-    // TODO: calculate projectile mass in nuclearStackExtension
-    //      const auto ProjMass = projectile.GetMass();
-    C8LOG_DEBUG("NuclearInteraction: DoInteraction: called with:" +
-                particles::GetName(ProjId));
-
-    // check if target-style nucleus (enum)
-    if (ProjId != particles::Code::Nucleus)
-      throw std::runtime_error(
-          "NuclearInteraction: DoInteraction: Wrong nucleus type. Nuclear projectiles "
-          "should use NuclearStackExtension!");
-
-    auto const ProjMass = projectile.GetNuclearZ() * particles::Proton::GetMass() +
-                          (projectile.GetNuclearA() - projectile.GetNuclearZ()) *
-                              particles::Neutron::GetMass();
-    C8LOG_DEBUG("NuclearInteraction: projectile mass: " +
-                std::to_string(ProjMass / 1_GeV));
-
-    count_++;
-
-    const CoordinateSystem& rootCS =
-        RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-    // position and time of interaction, not used in NUCLIB
-    Point pOrig = projectile.GetPosition();
-    TimeType tOrig = projectile.GetTime();
-
-    C8LOG_DEBUG(
-        fmt::format("Interaction: position of interaction: {}", pOrig.GetCoordinates()));
-    C8LOG_DEBUG("Interaction: time: " + std::to_string(tOrig / 1_s));
-
-    // projectile nucleon number
-    const unsigned int kAProj = projectile.GetNuclearA();
-    if (kAProj > GetMaxNucleusAProjectile())
-      throw std::runtime_error("Projectile nucleus too large for NUCLIB!");
-
-    // kinematics
-    // define projectile nucleus
-    HEPEnergyType const eProjectileLab = projectile.GetEnergy();
-    auto const pProjectileLab = projectile.GetMomentum();
-    const FourVector PprojLab(eProjectileLab, pProjectileLab);
-
-    C8LOG_DEBUG(
-        fmt::format("NuclearInteraction: eProj lab: {} "
-                    "pProj lab: {} ",
-                    eProjectileLab / 1_GeV, pProjectileLab.GetComponents() / 1_GeV));
-    ;
-
-    // define projectile nucleon
-    HEPEnergyType const eProjectileNucLab = projectile.GetEnergy() / kAProj;
-    auto const pProjectileNucLab = projectile.GetMomentum() / kAProj;
-    const FourVector PprojNucLab(eProjectileNucLab, pProjectileNucLab);
-
-    C8LOG_DEBUG(fmt::format(
-        "NuclearInteraction: eProjNucleon lab (GeV): {} "
-        "pProjNucleon lab (GeV): {}",
-        eProjectileNucLab / 1_GeV, pProjectileNucLab.GetComponents() / 1_GeV));
-
-    // define target
-    // always a nucleon
-    // target is always at rest
-    const auto eTargetNucLab = 0_GeV + constants::nucleonMass;
-    const auto pTargetNucLab =
-        corsika::stack::MomentumVector(rootCS, 0_GeV, 0_GeV, 0_GeV);
-    const FourVector PtargNucLab(eTargetNucLab, pTargetNucLab);
-
-    C8LOG_DEBUG(
-        fmt::format("NuclearInteraction: etarget lab(GeV): {}"
-                    "NuclearInteraction: ptarget lab(GeV): {} ",
-                    eTargetNucLab / 1_GeV, pTargetNucLab.GetComponents() / 1_GeV));
-
-    // center-of-mass energy in nucleon-nucleon frame
-    auto const PtotNN4 = PtargNucLab + PprojNucLab;
-    HEPEnergyType EcmNN = PtotNN4.GetNorm();
-    C8LOG_DEBUG("NuclearInteraction: nuc-nuc cm energy: " +
-                std::to_string(EcmNN / 1_GeV));
-
-    if (!hadronicInteraction_.IsValidCoMEnergy(EcmNN)) {
-      C8LOG_DEBUG(
-          "NuclearInteraction: nuc-nuc. CoM energy too low for hadronic "
-          "interaction model!");
-      throw std::runtime_error("NuclearInteraction: DoInteraction: energy too low!");
-    }
-
-    // define boost to NUCLEON-NUCLEON frame
-    COMBoost const boost(PprojNucLab, constants::nucleonMass);
-    // boost projecticle
-    [[maybe_unused]] auto const PprojNucCoM = boost.toCoM(PprojNucLab);
-
-    // boost target
-    [[maybe_unused]] auto const PtargNucCoM = boost.toCoM(PtargNucLab);
-
-    C8LOG_DEBUG(
-        fmt::format("Interaction: ebeam CoM: {} "
-                    ", pbeam CoM: {}",
-                    PprojNucCoM.GetTimeLikeComponent() / 1_GeV,
-                    PprojNucCoM.GetSpaceLikeComponents().GetComponents() / 1_GeV));
-    C8LOG_DEBUG(
-        fmt::format("Interaction: etarget CoM: {}"
-                    ", ptarget CoM: {}",
-                    PtargNucCoM.GetTimeLikeComponent() / 1_GeV,
-                    PtargNucCoM.GetSpaceLikeComponents().GetComponents() / 1_GeV));
-
-    // sample target nucleon number
-    //
-    // proton stand-in for nucleon
-    const auto beamId = particles::Proton::GetCode();
-    auto const* const currentNode = projectile.GetNode();
-    const auto& mediumComposition =
-        currentNode->GetModelProperties().GetNuclearComposition();
-    C8LOG_DEBUG("get nucleon-nucleus cross sections for target materials..");
-    // get cross sections for target materials
-    // using nucleon-target-nucleus cross section!!!
-    /*
-      Here we read the cross section from the interaction model again,
-      should be passed from GetInteractionLength if possible
-    */
-    auto const& compVec = mediumComposition.GetComponents();
-    vector<si::CrossSectionType> cross_section_of_components(compVec.size());
-
-    for (size_t i = 0; i < compVec.size(); ++i) {
-      auto const targetId = compVec[i];
-      C8LOG_DEBUG("target component: " + particles::GetName(targetId));
-      C8LOG_DEBUG("beam id: " + particles::GetName(beamId));
-      const auto [sigProd, sigEla] =
-          hadronicInteraction_.GetCrossSection(beamId, targetId, EcmNN);
-      cross_section_of_components[i] = sigProd;
-      [[maybe_unused]] auto sigElaCopy = sigEla; // ONLY TO AVOID COMPILER WARNINGS
-    }
-
-    const auto targetCode =
-        mediumComposition.SampleTarget(cross_section_of_components, RNG_);
-    C8LOG_DEBUG("Interaction: target selected: " + particles::GetName(targetCode));
-    /*
-      FOR NOW: allow nuclei with A<18 or protons only.
-      when medium composition becomes more complex, approximations will have to be
-      allowed air in atmosphere also contains some Argon.
-    */
-    int kATarget = -1;
-    if (IsNucleus(targetCode)) kATarget = GetNucleusA(targetCode);
-    if (targetCode == particles::Proton::GetCode()) kATarget = 1;
-    C8LOG_DEBUG("NuclearInteraction: nuclib target code: " + std::to_string(kATarget));
-    if (!hadronicInteraction_.IsValidTarget(targetCode))
-      throw std::runtime_error("target outside range. ");
-    // end of target sampling
-
-    // superposition
-    C8LOG_DEBUG("NuclearInteraction: sampling nuc. multiple interaction structure.. ");
-    // get nucleon-nucleon cross section
-    // (needed to determine number of nucleon-nucleon scatterings)
-    const auto protonId = particles::Proton::GetCode();
-    const auto [prodCrossSection, elaCrossSection] =
-        hadronicInteraction_.GetCrossSection(protonId, protonId, EcmNN);
-    const double sigProd = prodCrossSection / 1_mb;
-    const double sigEla = elaCrossSection / 1_mb;
-    // sample number of interactions (only input variables, output in common cnucms)
-    // nuclear multiple scattering according to glauber (r.i.p.)
-    int_nuc_(kATarget, kAProj, sigProd, sigEla);
-
-    C8LOG_DEBUG(
-        fmt::format("number of nucleons in target           : {}\n"
-                    "number of wounded nucleons in target   : {}\n"
-                    "number of nucleons in projectile       : {}\n"
-                    "number of wounded nucleons in project. : {}\n"
-                    "number of inel. nuc.-nuc. interactions : {}\n"
-                    "number of elastic nucleons in target   : {}\n"
-                    "number of elastic nucleons in project. : {}\n"
-                    "impact parameter: {}",
-                    kATarget, cnucms_.na, kAProj, cnucms_.nb, cnucms_.ni, cnucms_.nael,
-                    cnucms_.nbel, cnucms_.b));
-
-    // calculate fragmentation
-    C8LOG_DEBUG("calculating nuclear fragments..");
-    // number of interactions
-    // include elastic
-    const int nElasticNucleons = cnucms_.nbel;
-    const int nInelNucleons = cnucms_.nb;
-    const int nIntProj = nInelNucleons + nElasticNucleons;
-    const double impactPar = cnucms_.b; // only needed to avoid passing common var.
-    int nFragments = 0;
-    // number of fragments is limited to 60
-    int AFragments[60];
-    // call fragmentation routine
-    // input: target A, projectile A, number of int. nucleons in projectile, impact
-    // parameter (fm) output: nFragments, AFragments in addition the momenta ar stored
-    // in pf in common fragments, neglected
-    fragm_(kATarget, kAProj, nIntProj, impactPar, nFragments, AFragments);
-
-    // this should not occur but well :)
-    if (nFragments > (int)GetMaxNFragments())
-      throw std::runtime_error("Number of nuclear fragments in NUCLIB exceeded!");
-
-    C8LOG_DEBUG("number of fragments: " + std::to_string(nFragments));
-    for (int j = 0; j < nFragments; ++j)
-      C8LOG_DEBUG(fmt::format("fragment {}: A={} px={} py={} pz={}", j, AFragments[j],
-                              fragments_.ppp[j][0], fragments_.ppp[j][1],
-                              fragments_.ppp[j][2]));
-
-    C8LOG_DEBUG("adding nuclear fragments to particle stack..");
-    // put nuclear fragments on corsika stack
-    for (int j = 0; j < nFragments; ++j) {
-      particles::Code specCode;
-      const int nuclA = AFragments[j];
-      // get Z from stability line
-      const int nuclZ = int(nuclA / 2.15 + 0.7);
-
-      // TODO: do we need to catch single nucleons??
-      if (nuclA == 1)
-        // TODO: sample neutron or proton
-        specCode = particles::Code::Proton;
-      else
-        specCode = particles::Code::Nucleus;
-
-      // TODO: mass of nuclei?
-      const HEPMassType mass =
-          particles::Proton::GetMass() * nuclZ +
-          (nuclA - nuclZ) * particles::Neutron::GetMass(); // this neglects binding energy
-
-      C8LOG_DEBUG("NuclearInteraction: adding fragment: " + particles::GetName(specCode));
-      C8LOG_DEBUG("NuclearInteraction: A,Z: " + std::to_string(nuclA) + ", " +
-                  std::to_string(nuclZ));
-      C8LOG_DEBUG("NuclearInteraction: mass: " + std::to_string(mass / 1_GeV));
-
-      // CORSIKA 7 way
-      // spectators inherit momentum from original projectile
-      const double mass_ratio = mass / ProjMass;
-
-      C8LOG_DEBUG("NuclearInteraction: mass ratio " + std::to_string(mass_ratio));
-
-      auto const Plab = PprojLab * mass_ratio;
-
-      C8LOG_DEBUG(fmt::format("NuclearInteraction: fragment momentum: {}",
-                              Plab.GetSpaceLikeComponents().GetComponents() / 1_GeV));
-
-      if (nuclA == 1)
-        // add nucleon
-        projectile.AddSecondary(make_tuple(specCode, Plab.GetTimeLikeComponent(),
-                                           Plab.GetSpaceLikeComponents(), pOrig, tOrig));
-      else
-        // add nucleus
-        projectile.AddSecondary(make_tuple(specCode, Plab.GetTimeLikeComponent(),
-                                           Plab.GetSpaceLikeComponents(), pOrig, tOrig,
-                                           nuclA, nuclZ));
-    }
-
-    // add elastic nucleons to corsika stack
-    // TODO: the elastic interaction could be external like the inelastic interaction,
-    // e.g. use existing ElasticModel
-    C8LOG_DEBUG("adding elastically scattered nucleons to particle stack..");
-    for (int j = 0; j < nElasticNucleons; ++j) {
-      // TODO: sample proton or neutron
-      auto const elaNucCode = particles::Code::Proton;
-
-      // CORSIKA 7 way
-      // elastic nucleons inherit momentum from original projectile
-      // neglecting momentum transfer in interaction
-      const double mass_ratio = particles::GetMass(elaNucCode) / ProjMass;
-      auto const Plab = PprojLab * mass_ratio;
-
-      projectile.AddSecondary(make_tuple(elaNucCode, Plab.GetTimeLikeComponent(),
-                                         Plab.GetSpaceLikeComponents(), pOrig, tOrig));
-    }
-
-    // add inelastic interactions
-    C8LOG_DEBUG("calculate inelastic nucleon-nucleon interactions..");
-    for (int j = 0; j < nInelNucleons; ++j) {
-      // TODO: sample neutron or proton
-      auto pCode = particles::Proton::GetCode();
-      // temporarily add to stack, will be removed after interaction in DoInteraction
-      C8LOG_DEBUG(fmt::format("inelastic interaction no. {}", j));
-      setup::Stack nucleonStack;
-      // auto inelasticNucleon = projectile.AddSecondary(
-      auto inelasticNucleon = nucleonStack.AddParticle(
-          make_tuple(pCode, PprojNucLab.GetTimeLikeComponent(),
-                     PprojNucLab.GetSpaceLikeComponents(), pOrig, tOrig));
-      inelasticNucleon.SetNode(projectile.GetNode());
-      // create inelastic interaction for each nucleon
-      C8LOG_TRACE("calling HadronicInteraction...");
-      // create new StackView for each of the nucleons
-      View nucleon_secondaries(inelasticNucleon);
-      // all inner hadronic event generator
-      hadronicInteraction_.DoInteraction(nucleon_secondaries);
-      // inelasticNucleon.Delete(); // this is just a temporary object
-      for (const auto& pSec : nucleon_secondaries) {
-        projectile.AddSecondary(make_tuple(pSec.GetPID(), pSec.GetEnergy(),
-                                           pSec.GetMomentum(), pSec.GetPosition(),
-                                           pSec.GetTime()));
-      }
-    }
-
-    C8LOG_DEBUG("NuclearInteraction: DoInteraction: done");
-
-    return process::EProcessReturn::eOk;
-  }
-
-  template <>
-  NuclearInteraction<setup::Environment>::NuclearInteraction(
-      process::sibyll::Interaction& hadint, setup::Environment const& env)
-      : environment_(env)
-      , hadronicInteraction_(hadint) {
-
-    // initialize hadronic interaction module
-    // TODO: safe to run multiple initializations?
-
-    // check compatibility of energy ranges, someone could try to use low-energy model..
-    if (!hadronicInteraction_.IsValidCoMEnergy(GetMinEnergyPerNucleonCoM()) ||
-        !hadronicInteraction_.IsValidCoMEnergy(GetMaxEnergyPerNucleonCoM()))
-      throw std::runtime_error(
-          "NuclearInteraction: hadronic interaction model incompatible!");
-
-    // initialize nuclib
-    // TODO: make sure this does not overlap with sibyll
-    nuc_nuc_ini_();
-
-    // initialize cross sections
-    InitializeNuclearCrossSections();
-  }
-
-} // namespace corsika::process::sibyll
diff --git a/Processes/Sibyll/NuclearInteraction.h b/Processes/Sibyll/NuclearInteraction.h
deleted file mode 100644
index 4a561fad1298d78e8c45f03bd25711639366e1d6..0000000000000000000000000000000000000000
--- a/Processes/Sibyll/NuclearInteraction.h
+++ /dev/null
@@ -1,77 +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/particles/ParticleProperties.h>
-#include <corsika/process/InteractionProcess.h>
-#include <corsika/random/RNGManager.h>
-
-namespace corsika::process::sibyll {
-
-  class Interaction; // fwd-decl
-
-  /**
-   *
-   *
-   **/
-  template <class TEnvironment>
-  class NuclearInteraction
-      : public corsika::process::InteractionProcess<NuclearInteraction<TEnvironment>> {
-
-    int count_ = 0;
-    int nucCount_ = 0;
-
-  public:
-    NuclearInteraction(corsika::process::sibyll::Interaction&, TEnvironment const&);
-    ~NuclearInteraction();
-
-    void InitializeNuclearCrossSections();
-    void PrintCrossSectionTable(corsika::particles::Code);
-    corsika::units::si::CrossSectionType ReadCrossSectionTable(
-        const int, corsika::particles::Code, corsika::units::si::HEPEnergyType);
-    corsika::units::si::HEPEnergyType GetMinEnergyPerNucleonCoM() {
-      return gMinEnergyPerNucleonCoM_;
-    }
-    corsika::units::si::HEPEnergyType GetMaxEnergyPerNucleonCoM() {
-      return gMaxEnergyPerNucleonCoM_;
-    }
-    unsigned int constexpr GetMaxNucleusAProjectile() { return gMaxNucleusAProjectile_; }
-    unsigned int constexpr GetMaxNFragments() { return gMaxNFragments_; }
-    unsigned int constexpr GetNEnergyBins() { return gNEnBins_; }
-
-    template <typename Particle>
-    std::tuple<corsika::units::si::CrossSectionType, corsika::units::si::CrossSectionType>
-    GetCrossSection(Particle const& p, const corsika::particles::Code TargetId);
-
-    template <typename Particle>
-    corsika::units::si::GrammageType GetInteractionLength(Particle const&);
-
-    template <typename TSecondaryView>
-    corsika::process::EProcessReturn DoInteraction(TSecondaryView&);
-
-  private:
-    TEnvironment const& environment_;
-    corsika::process::sibyll::Interaction& hadronicInteraction_;
-    std::map<corsika::particles::Code, int> targetComponentsIndex_;
-    corsika::random::RNG& RNG_ =
-        corsika::random::RNGManager::GetInstance().GetRandomStream("sibyll");
-    static constexpr unsigned int gNSample_ =
-        500; // number of samples in MC estimation of cross section
-    static constexpr unsigned int gMaxNucleusAProjectile_ = 56;
-    static constexpr unsigned int gNEnBins_ = 6;
-    static constexpr unsigned int gMaxNFragments_ = 60;
-    // energy limits defined by table used for cross section in signuc.f
-    // 10**1 GeV to 10**6 GeV
-    static constexpr corsika::units::si::HEPEnergyType gMinEnergyPerNucleonCoM_ =
-        10. * 1e9 * corsika::units::si::electronvolt;
-    static constexpr corsika::units::si::HEPEnergyType gMaxEnergyPerNucleonCoM_ =
-        1.e6 * 1e9 * corsika::units::si::electronvolt;
-  };
-
-} // namespace corsika::process::sibyll
diff --git a/Processes/Sibyll/ParticleConversion.cc b/Processes/Sibyll/ParticleConversion.cc
deleted file mode 100644
index a6cf965cb4908a8ce129116626443975ab6e9275..0000000000000000000000000000000000000000
--- a/Processes/Sibyll/ParticleConversion.cc
+++ /dev/null
@@ -1,25 +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/particles/ParticleProperties.h>
-#include <corsika/process/sibyll/ParticleConversion.h>
-
-using namespace corsika::process::sibyll;
-
-corsika::units::si::HEPMassType corsika::process::sibyll::GetSibyllMass(
-    corsika::particles::Code const pCode) {
-  using namespace corsika::units;
-  using namespace corsika::units::si;
-  if (pCode == corsika::particles::Code::Nucleus)
-    throw std::runtime_error("Cannot GetMass() of particle::Nucleus -> unspecified");
-  auto sCode = ConvertToSibyllRaw(pCode);
-  if (sCode == 0)
-    throw std::runtime_error("GetSibyllMass: unknown particle!");
-  else
-    return sqrt(get_sibyll_mass2(sCode)) * 1_GeV;
-}
diff --git a/Processes/Sibyll/SibStack.h b/Processes/Sibyll/SibStack.h
deleted file mode 100644
index e3ebcc8ff87ba629a32c93f30840c131d5ae0cbb..0000000000000000000000000000000000000000
--- a/Processes/Sibyll/SibStack.h
+++ /dev/null
@@ -1,146 +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/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/process/sibyll/ParticleConversion.h>
-#include <corsika/process/sibyll/sibyll2.3d.h>
-#include <corsika/stack/Stack.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::process::sibyll {
-
-  typedef corsika::geometry::Vector<corsika::units::si::hepmomentum_d> MomentumVector;
-
-  class SibStackData {
-
-  public:
-    void Dump() const {}
-
-    void Clear() { s_plist_.np = 0; }
-    unsigned int GetSize() const { return s_plist_.np; }
-    unsigned int GetCapacity() const { return 8000; }
-
-    void SetId(const unsigned int i, const int v) { s_plist_.llist[i] = v; }
-    void SetEnergy(const unsigned int i, const corsika::units::si::HEPEnergyType v) {
-      using namespace corsika::units::si;
-      s_plist_.p[3][i] = v / 1_GeV;
-    }
-    void SetMass(const unsigned int i, const corsika::units::si::HEPMassType v) {
-      using namespace corsika::units::si;
-      s_plist_.p[4][i] = v / 1_GeV;
-    }
-    void SetMomentum(const unsigned int i, const MomentumVector& v) {
-      using namespace corsika::units::si;
-      auto tmp = v.GetComponents();
-      for (int idx = 0; idx < 3; ++idx) s_plist_.p[idx][i] = tmp[idx] / 1_GeV;
-    }
-
-    int GetId(const unsigned int i) const { return s_plist_.llist[i]; }
-    corsika::units::si::HEPEnergyType GetEnergy(const int i) const {
-      using namespace corsika::units::si;
-      return s_plist_.p[3][i] * 1_GeV;
-    }
-    corsika::units::si::HEPEnergyType GetMass(const unsigned int i) const {
-      using namespace corsika::units::si;
-      return s_plist_.p[4][i] * 1_GeV;
-    }
-    MomentumVector GetMomentum(const unsigned int i) const {
-      using corsika::geometry::CoordinateSystem;
-      using corsika::geometry::QuantityVector;
-      using corsika::geometry::RootCoordinateSystem;
-      using namespace corsika::units::si;
-      CoordinateSystem& rootCS =
-          RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-      QuantityVector<hepmomentum_d> components = {
-          s_plist_.p[0][i] * 1_GeV, s_plist_.p[1][i] * 1_GeV, s_plist_.p[2][i] * 1_GeV};
-      return MomentumVector(rootCS, components);
-    }
-
-    void Copy(const unsigned int i1, const unsigned int i2) {
-      s_plist_.llist[i2] = s_plist_.llist[i1];
-      for (unsigned int i = 0; i < 5; ++i) s_plist_.p[i][i2] = s_plist_.p[i][i1];
-    }
-
-    void Swap(const unsigned int i1, const unsigned int i2) {
-      std::swap(s_plist_.llist[i1], s_plist_.llist[i2]);
-      for (unsigned int i = 0; i < 5; ++i)
-        std::swap(s_plist_.p[i][i1], s_plist_.p[i][i2]);
-    }
-
-    void IncrementSize() { s_plist_.np++; }
-    void DecrementSize() {
-      if (s_plist_.np > 0) { s_plist_.np--; }
-    }
-  };
-
-  template <typename StackIteratorInterface>
-  class ParticleInterface : public corsika::stack::ParticleBase<StackIteratorInterface> {
-
-    using corsika::stack::ParticleBase<StackIteratorInterface>::GetStackData;
-    using corsika::stack::ParticleBase<StackIteratorInterface>::GetIndex;
-
-  public:
-    void SetParticleData(const int vID, // corsika::process::sibyll::SibyllCode vID,
-                         const corsika::units::si::HEPEnergyType vE,
-                         const MomentumVector& vP,
-                         const corsika::units::si::HEPMassType vM) {
-      SetPID(vID);
-      SetEnergy(vE);
-      SetMomentum(vP);
-      SetMass(vM);
-    }
-
-    void SetParticleData(ParticleInterface<StackIteratorInterface>& /*parent*/,
-                         const int vID, //  corsika::process::sibyll::SibyllCode vID,
-                         const corsika::units::si::HEPEnergyType vE,
-                         const MomentumVector& vP,
-                         const corsika::units::si::HEPMassType vM) {
-      SetPID(vID);
-      SetEnergy(vE);
-      SetMomentum(vP);
-      SetMass(vM);
-    }
-
-    void SetEnergy(const corsika::units::si::HEPEnergyType v) {
-      GetStackData().SetEnergy(GetIndex(), v);
-    }
-
-    corsika::units::si::HEPEnergyType GetEnergy() const {
-      return GetStackData().GetEnergy(GetIndex());
-    }
-
-    bool HasDecayed() const { return abs(GetStackData().GetId(GetIndex())) > 100; }
-
-    void SetMass(const corsika::units::si::HEPMassType v) {
-      GetStackData().SetMass(GetIndex(), v);
-    }
-
-    corsika::units::si::HEPEnergyType GetMass() const {
-      return GetStackData().GetMass(GetIndex());
-    }
-
-    void SetPID(const int v) { GetStackData().SetId(GetIndex(), v); }
-
-    corsika::process::sibyll::SibyllCode GetPID() const {
-      return static_cast<corsika::process::sibyll::SibyllCode>(
-          GetStackData().GetId(GetIndex()));
-    }
-
-    MomentumVector GetMomentum() const { return GetStackData().GetMomentum(GetIndex()); }
-
-    void SetMomentum(const MomentumVector& v) {
-      GetStackData().SetMomentum(GetIndex(), v);
-    }
-  };
-
-  typedef corsika::stack::Stack<SibStackData, ParticleInterface> SibStack;
-
-} // end namespace corsika::process::sibyll
diff --git a/Processes/Sibyll/rndm_dbl.f b/Processes/Sibyll/rndm_dbl.f
deleted file mode 100644
index 42db719788f480150f95c2a68d73cc706647586c..0000000000000000000000000000000000000000
--- a/Processes/Sibyll/rndm_dbl.f
+++ /dev/null
@@ -1,416 +0,0 @@
-C***********************************************************************
-C
-C    interface to PHOJET double precision random number generator 
-C    for SIBYLL \FR'14
-C
-C***********************************************************************
-      DOUBLE PRECISION FUNCTION S_RNDM(IDUMMY)
-      IMPLICIT DOUBLE PRECISION (A-H,O-Z)
-      DUMMY = dble(IDUMMY)
-      S_RNDM= PHO_RNDM(DUMMY)
-      END
-
-C***********************************************************************
-C
-C    initialization routine for double precision random number generator
-C    calls PHO_RNDIN \FR'14
-C
-C***********************************************************************
-      SUBROUTINE RND_INI
-      IMPLICIT DOUBLE PRECISION (A-H,O-Z)
-      COMMON /RNDMGAS/ ISET
-      ISET = 0
-      CALL PHO_RNDIN(12,34,56,78)
-      END
-
-
-      DOUBLE PRECISION FUNCTION GASDEV(Idum)
-C***********************************************************************
-C     Gaussian deviation
-C***********************************************************************
-      IMPLICIT DOUBLE PRECISION (A-H,O-Z)
-      IMPLICIT INTEGER(I-N)
-      COMMON /RNDMGAS/ ISET
-      SAVE
-      DATA ISET/0/      
-      gasdev=idum
-      IF (ISET.EQ.0) THEN
-1       V1=2.D0*S_RNDM(0)-1.D0
-        V2=2.D0*S_RNDM(1)-1.D0
-        R=V1**2+V2**2
-        IF(R.GE.1.D0)GO TO 1
-        FAC=SQRT(-2.D0*LOG(R)/R)
-        GSET=V1*FAC
-        GASDEV=V2*FAC
-        ISET=1
-      ELSE
-        GASDEV=GSET
-        ISET=0
-      ENDIF
-      RETURN
-      END
-C***********************************************************************
-      
-
-      DOUBLE PRECISION FUNCTION PHO_RNDM(DUMMY)
-C***********************************************************************
-C
-C    random number generator
-C
-C    initialization by call to PHO_RNDIN needed!
-C     
-C    the algorithm is taken from
-C      G.Marsaglia, A.Zaman: 'Toward a unversal random number generator'
-C      Florida State Univ. preprint FSU-SCRI-87-70
-C
-C    implementation by K. Hahn (Dec. 88), changed to include possibility
-C    of saving / reading generator registers to / from file (R.E. 10/98)
-C
-C    generator should not depend on the hardware (if a real has
-C    at least 24 significant bits in internal representation),
-C    the period is about 2**144,
-C
-C    internal registers:
-C       U(97),C,CD,CM,I,J  - seed values as initialized in PHO_RNDIN
-C
-C
-C***********************************************************************
-      IMPLICIT DOUBLE PRECISION (A-H,O-Z)
-      SAVE
-
-      COMMON /PORAND/ U(97),C,CD,CM,I,J
-
- 100  CONTINUE
-      RNDMI = DUMMY
-      RNDMI = U(I)-U(J)
-      IF ( RNDMI.LT.0.D0 ) RNDMI = RNDMI+1.D0
-      U(I) = RNDMI
-      I    = I-1
-      IF ( I.EQ.0 ) I = 97
-      J    = J-1
-      IF ( J.EQ.0 ) J = 97
-      C    = C-CD
-      IF ( C.LT.0.D0 ) C = C+CM
-      RNDMI = RNDMI-C
-      IF ( RNDMI.LT.0.D0 ) RNDMI = RNDMI+1.D0
-
-      IF((ABS(RNDMI).LT.0.D0).OR.(ABS(RNDMI-1.D0).LT.1.D-10)) GOTO 100
-      PHO_RNDM = RNDMI
-
-      END
-
-
-CDECK  ID>, PHO_RNDIN
-      SUBROUTINE PHO_RNDIN(NA1,NA2,NA3,NB1)
-C***********************************************************************
-C
-C     initialization of PHO_RNDM, has to be called before using PHO_RNDM
-C
-C     input:
-C       NA1,NA2,NA3,NB1  - values for initializing the generator
-C                          NA? must be in 1..178 and not all 1;
-C                          12,34,56  are the standard values
-C                          NB1 must be in 1..168;
-C                          78  is the standard value
-C
-C***********************************************************************
-      IMPLICIT DOUBLE PRECISION (A-H,O-Z)
-      SAVE
-
-      COMMON /PORAND/ U(97),C,CD,CM,I,J
-      MA1 = NA1
-      MA2 = NA2
-      MA3 = NA3
-      MB1 = NB1
-      I   = 97
-      J   = 33
-      DO 20 II2 = 1,97
-        S = 0.D0
-        T = 0.5D0
-        DO 10 II1 = 1,24
-          MAT  = MOD(MOD(MA1*MA2,179)*MA3,179)
-          MA1  = MA2
-          MA2  = MA3
-          MA3  = MAT
-          MB1  = MOD(53*MB1+1,169)
-          IF ( MOD(MB1*MAT,64).GE.32 ) S = S+T
-          T    = 0.5D0*T
- 10     CONTINUE
-        U(II2) = S
- 20   CONTINUE
-      C  =   362436.D0/16777216.D0
-      CD =  7654321.D0/16777216.D0
-      CM = 16777213.D0/16777216.D0
-
-      END
-
-
-CDECK  ID>, PHO_RNDSI
-      SUBROUTINE PHO_RNDSI(UIN,CIN,CDIN,CMIN,IIN,JIN)
-C***********************************************************************
-C
-C     updates internal random number generator registers using
-C     registers given as arguments
-C
-C***********************************************************************
-      IMPLICIT DOUBLE PRECISION (A-H,O-Z)
-      SAVE
-
-      DIMENSION UIN(97)
-      COMMON /PORAND/ U(97),C,CD,CM,I,J
-      DO 10 KKK = 1,97
-        U(KKK) = UIN(KKK)
- 10   CONTINUE
-      C  = CIN
-      CD = CDIN
-      CM = CMIN
-      I  = IIN
-      J  = JIN
-
-      END
-
-
-CDECK  ID>, PHO_RNDSO
-      SUBROUTINE PHO_RNDSO(UOUT,COUT,CDOUT,CMOUT,IOUT,JOUT)
-C***********************************************************************
-C
-C     copies internal registers from randon number generator
-C     to arguments
-C
-C***********************************************************************
-      IMPLICIT DOUBLE PRECISION (A-H,O-Z)
-      SAVE
-
-      DIMENSION UOUT(97)
-      COMMON /PORAND/ U(97),C,CD,CM,I,J
-      DO 10 KKK = 1,97
-        UOUT(KKK) = U(KKK)
- 10   CONTINUE
-      COUT  = C
-      CDOUT = CD
-      CMOUT = CM
-      IOUT  = I
-      JOUT  = J
-
-      END
-
-
-CDECK  ID>, PHO_RNDTE
-      SUBROUTINE PHO_RNDTE(IO)
-C***********************************************************************
-C
-C     test of random number generator PHO_RNDM
-C
-C     input:
-C       IO defines output
-C           0  output only if an error is detected
-C           1  output independend on an error
-C
-C     uses PHO_RNDSI and PHO_RNDSO to bring the random number generator
-C     to same status as it had before the test run
-C
-C***********************************************************************
-      IMPLICIT DOUBLE PRECISION (A-H,O-Z)
-      SAVE
-
-
-C  input/output channels
-      INTEGER LI,LO
-      COMMON /POINOU/ LI,LO
-
-      DIMENSION UU(97)
-      DIMENSION U(6),X(6),D(6)
-      DATA U / 6533892.D0 , 14220222.D0 ,  7275067.D0 ,
-     &         6172232.D0 ,  8354498.D0 , 10633180.D0 /
-
-      CALL PHO_RNDSO(UU,CC,CCD,CCM,II,JJ)
-
-      CALL PHO_RNDIN(12,34,56,78)
-      DO 10 II1 = 1,20000
-        XX      = PHO_RNDM(SD)
- 10   CONTINUE
-
-      SD        = 0.D0
-      DO 20 II2 = 1,6
-        X(II2)  = 4096.D0*(4096.D0*PHO_RNDM(XX))
-        D(II2)  = X(II2)-U(II2)
-        SD      = SD+ABS(D(II2))
- 20   CONTINUE
-
-      CALL PHO_RNDSI(UU,CC,CCD,CCM,II,JJ)
-
-      IF ((IO.EQ.1).OR.(ABS(SD).GT.0.D-10)) THEN
-        WRITE(LO,50) (U(I),X(I),D(I),I=1,6)
-      ENDIF
-
- 50   FORMAT(/,' PHO_RNDTE: test of the random number generator:',/,
-     &  '    expected value    calculated value     difference',/,
-     &  6(F17.1,F20.1,F15.3,/),
-     &  ' generator has the same status as before calling PHO_RNDTE',/)
-
-      END
-
-
-CDECK  ID>, PHO_RNDST
-      SUBROUTINE PHO_RNDST(MODE,FILENA)
-C***********************************************************************
-C
-C     read / write random number generator status from / to file
-C
-C     input:    MODE        1   read registers from file
-C                           2   dump registers to file
-C
-C               FILENA      file name
-C
-C***********************************************************************
-
-      IMPLICIT NONE
-
-
-
-      SAVE
-
-      INTEGER       MODE
-      CHARACTER*(*) FILENA
-
-
-C  input/output channels
-      INTEGER LI,LO
-      COMMON /POINOU/ LI,LO
-
-
-      DOUBLE PRECISION UU,CC,CCD,CCM
-      DIMENSION UU(97)
-
-      INTEGER I,II,JJ
-
-      CHARACTER*80 CH_DUMMY
-
-      IF(MODE.EQ.1) THEN
-
-        WRITE(LO,'(/,1X,2A,A,/)') 'PHO_RNDST: ',
-     &    'reading random number registers from file ',FILENA
-
-        OPEN(12,FILE=FILENA,ERR=1010,STATUS='OLD')
-        READ(12,*,ERR=1010) CH_DUMMY
-        DO I=1,97
-          READ(12,*,ERR=1010) UU(I)
-        ENDDO
-        READ(12,*,ERR=1010) CC
-        READ(12,*,ERR=1010) CCD
-        READ(12,*,ERR=1010) CCM
-        READ(12,*,ERR=1010) II,JJ
-        CLOSE(12)
-        CALL PHO_RNDSI(UU,CC,CCD,CCM,II,JJ)
-
-      ELSE IF(MODE.EQ.2) THEN
-
-        WRITE(LO,'(/,1X,2A,A,/)') 'PHO_RNDST: ',
-     &    'dumping random number registers to file ',FILENA
-
-        OPEN(12,FILE=FILENA,ERR=1010,STATUS='UNKNOWN')
-        CALL PHO_RNDSO(UU,CC,CCD,CCM,II,JJ)
-        WRITE(12,'(1X,A)',ERR=1020) 'random number status registers:'
-        DO I=1,97
-          WRITE(12,'(1X,1P,E28.20)',ERR=1020) UU(I)
-        ENDDO
-        WRITE(12,'(1X,1P,E28.20)',ERR=1020) CC
-        WRITE(12,'(1X,1P,E28.20)',ERR=1020) CCD
-        WRITE(12,'(1X,1P,E28.20)',ERR=1020) CCM
-        WRITE(12,'(1X,2I4)',ERR=1020) II,JJ
-        CLOSE(12)
-
-      ELSE
-
-        WRITE(LO,'(/,1X,2A,I6,/)') 'PHO_RNDST: ',
-     &    'called with invalid mode, nothing done (mode)',MODE
-
-      ENDIF
-
-      RETURN
-
- 1010 CONTINUE
-      WRITE(LO,'(1X,2A,A,/)') 'PHO_RNDST: ',
-     &  'cannot open or read file ',FILENA
-      RETURN
-
- 1020 CONTINUE
-      WRITE(LO,'(1X,A,A,/)') 'PHO_RNDST: ',
-     &  'cannot open or write file ',FILENA
-      RETURN
-    
-      END
-
-C----------------------------------------
-C standard generator
-C----------------------------------------
-      REAL FUNCTION S_RNDM_std(IDUMMY)
-C...Generator  from the LUND montecarlo
-C...Purpose: to generate random numbers uniformly distributed between
-C...0 and 1, excluding the endpoints.
-      COMMON/LUDATR/MRLU(6),RRLU(100)
-      SAVE /LUDATR/
-      EQUIVALENCE (MRLU1,MRLU(1)),(MRLU2,MRLU(2)),(MRLU3,MRLU(3)),
-     &(MRLU4,MRLU(4)),(MRLU5,MRLU(5)),(MRLU6,MRLU(6)),
-     &(RRLU98,RRLU(98)),(RRLU99,RRLU(99)),(RRLU00,RRLU(100))
- 
-C...  Initialize generation from given seed.
-      S_RNDM_std = real(idummy)
-      IF(MRLU2.EQ.0) THEN
-        IF (MRLU1 .EQ. 0)  MRLU1 = 19780503    ! initial seed
-        IJ=MOD(MRLU1/30082,31329)
-        KL=MOD(MRLU1,30082)
-        I=MOD(IJ/177,177)+2
-        J=MOD(IJ,177)+2
-        K=MOD(KL/169,178)+1
-        L=MOD(KL,169)
-        DO 110 II=1,97
-        S=0.
-        T=0.5
-        DO 100 JJ=1,24
-        M=MOD(MOD(I*J,179)*K,179)
-        I=J
-        J=K
-        K=M
-        L=MOD(53*L+1,169)
-        IF(MOD(L*M,64).GE.32) S=S+T
-        T=0.5*T
-  100   CONTINUE
-        RRLU(II)=S
-  110   CONTINUE
-        TWOM24=1.
-        DO 120 I24=1,24
-        TWOM24=0.5*TWOM24
-  120   CONTINUE
-        RRLU98=362436.*TWOM24
-        RRLU99=7654321.*TWOM24
-        RRLU00=16777213.*TWOM24
-        MRLU2=1
-        MRLU3=0
-        MRLU4=97
-        MRLU5=33
-      ENDIF
- 
-C...Generate next random number.
-  130 RUNI=RRLU(MRLU4)-RRLU(MRLU5)
-      IF(RUNI.LT.0.) RUNI=RUNI+1.
-      RRLU(MRLU4)=RUNI
-      MRLU4=MRLU4-1
-      IF(MRLU4.EQ.0) MRLU4=97
-      MRLU5=MRLU5-1
-      IF(MRLU5.EQ.0) MRLU5=97
-      RRLU98=RRLU98-RRLU99
-      IF(RRLU98.LT.0.) RRLU98=RRLU98+RRLU00
-      RUNI=RUNI-RRLU98
-      IF(RUNI.LT.0.) RUNI=RUNI+1.
-      IF(RUNI.LE.0.OR.RUNI.GE.1.) GOTO 130
- 
-C...Update counters. Random number to output.
-      MRLU3=MRLU3+1
-      IF(MRLU3.EQ.1000000000) THEN
-        MRLU2=MRLU2+1
-        MRLU3=0
-      ENDIF
-      S_RNDM_std=RUNI
-
-      END
diff --git a/Processes/Sibyll/sibyll2.3d.cc b/Processes/Sibyll/sibyll2.3d.cc
deleted file mode 100644
index 16db131f7cede092ced66a7b8eebd4e22658c560..0000000000000000000000000000000000000000
--- a/Processes/Sibyll/sibyll2.3d.cc
+++ /dev/null
@@ -1,23 +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/process/sibyll/sibyll2.3d.h>
-
-#include <corsika/random/RNGManager.h>
-#include <random>
-
-int get_nwounded() { return s_chist_.nwd; }
-double get_sibyll_mass2(int& id) { return s_mass1_.am2[abs(id) - 1]; }
-
-double s_rndm_(int&) {
-  static corsika::random::RNG& rng =
-      corsika::random::RNGManager::GetInstance().GetRandomStream("sibyll");
-
-  std::uniform_real_distribution<double> dist;
-  return dist(rng);
-}
diff --git a/Processes/Sibyll/testSibyll.cc b/Processes/Sibyll/testSibyll.cc
deleted file mode 100644
index c229e886178806bad80b05764e9e82db1e167cdd..0000000000000000000000000000000000000000
--- a/Processes/Sibyll/testSibyll.cc
+++ /dev/null
@@ -1,246 +0,0 @@
-/*
- * (c) Copyright 2019 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/process/sibyll/Decay.h>
-#include <corsika/process/sibyll/Interaction.h>
-#include <corsika/process/sibyll/NuclearInteraction.h>
-#include <corsika/process/sibyll/ParticleConversion.h>
-
-#include <corsika/random/RNGManager.h>
-
-#include <corsika/particles/ParticleProperties.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <catch2/catch.hpp>
-#include <tuple>
-
-using namespace corsika;
-using namespace corsika::process::sibyll;
-using namespace corsika::units;
-using namespace corsika::units::si;
-
-TEST_CASE("Sibyll", "[processes]") {
-
-  SECTION("Sibyll -> Corsika") {
-    CHECK(particles::Electron::GetCode() ==
-          process::sibyll::ConvertFromSibyll(process::sibyll::SibyllCode::Electron));
-  }
-
-  SECTION("Corsika -> Sibyll") {
-    CHECK(process::sibyll::ConvertToSibyll(particles::Electron::GetCode()) ==
-          process::sibyll::SibyllCode::Electron);
-    CHECK(process::sibyll::ConvertToSibyllRaw(particles::Proton::GetCode()) == 13);
-    CHECK(process::sibyll::ConvertToSibyll(particles::XiStarC0::GetCode()) ==
-          process::sibyll::SibyllCode::XiStarC0);
-  }
-
-  SECTION("canInteractInSibyll") {
-
-    CHECK(process::sibyll::CanInteract(particles::Proton::GetCode()));
-    CHECK(process::sibyll::CanInteract(particles::Code::XiCPlus));
-
-    CHECK_FALSE(process::sibyll::CanInteract(particles::Electron::GetCode()));
-    CHECK_FALSE(process::sibyll::CanInteract(particles::SigmaC0::GetCode()));
-
-    CHECK_FALSE(process::sibyll::CanInteract(particles::Nucleus::GetCode()));
-    CHECK_FALSE(process::sibyll::CanInteract(particles::Helium::GetCode()));
-  }
-
-  SECTION("cross-section type") {
-
-    CHECK(process::sibyll::GetSibyllXSCode(particles::Code::Electron) == 0);
-    CHECK(process::sibyll::GetSibyllXSCode(particles::Code::K0Long) == 3);
-    CHECK(process::sibyll::GetSibyllXSCode(particles::Code::SigmaPlus) == 1);
-    CHECK(process::sibyll::GetSibyllXSCode(particles::Code::PiMinus) == 2);
-  }
-
-  SECTION("sibyll mass") {
-
-    CHECK_FALSE(process::sibyll::GetSibyllMass(particles::Code::Electron) == 0_GeV);
-  }
-}
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/setup/SetupEnvironment.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/HomogeneousMedium.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/environment/UniformMagneticField.h>
-
-using namespace corsika::units::si;
-using namespace corsika::units;
-
-template <typename TStackView>
-auto sumMomentum(TStackView const& view, geometry::CoordinateSystem const& vCS) {
-  geometry::Vector<hepenergy_d> sum{vCS, 0_eV, 0_eV, 0_eV};
-  for (auto const& p : view) { sum += p.GetMomentum(); }
-  return sum;
-}
-
-TEST_CASE("SibyllInterface", "[processes]") {
-
-  auto [env, csPtr, nodePtr] = setup::testing::setupEnvironment(particles::Code::Oxygen);
-  auto const& cs = *csPtr;
-  [[maybe_unused]] auto const& env_dummy = env;
-  [[maybe_unused]] auto const& node_dummy = nodePtr;
-
-  random::RNGManager::GetInstance().RegisterRandomStream("sibyll");
-
-  SECTION("InteractionInterface - low energy") {
-
-    const HEPEnergyType P0 = 60_GeV;
-    auto [stack, viewPtr] =
-        setup::testing::setupStack(particles::Code::Proton, 0, 0, P0, nodePtr, cs);
-    const auto plab = corsika::stack::MomentumVector(
-        cs, {P0, 0_eV, 0_eV}); // this is secret knowledge about setupStack
-    setup::StackView& view = *viewPtr;
-
-    auto particle = stack->first();
-
-    Interaction model;
-
-    [[maybe_unused]] const process::EProcessReturn ret = model.DoInteraction(view);
-    auto const pSum = sumMomentum(view, cs);
-
-    /*
-      Interactions between hadrons (h) and nuclei (A) in Sibyll are treated in the
-      hadron-nucleon center-of-mass frame (hnCoM). The incoming hadron (h) and
-      nucleon (N) are assumed massless, such that the energy and momentum in the hnCoM are
-      : E_i_cm = 0.5 * SQS and P_i_cm = +- 0.5 * SQS  where i is either the projectile
-      hadron or the target nucleon and SQS is the hadron-nucleon center-of-mass energy.
-
-      The true energies and momenta, accounting for the hadron masses, are: E_i = ( S +
-      m_i**2 - m_j**2 ) / (2 * SQS) and Pcm = +-
-      sqrt( (S-(m_j+m_i)**2) * (s-(m_j-m_i)**2) ) / (2*SQS) where m_i is the projectiles
-      mass and m_j is the target particles mass. In terms of lab. frame variables Pcm =
-      m_j * Plab_i / SQS, where Plab_i is the momentum of the projectile (i) in the lab.
-      and m_j is the mass of the target, i.e. the particle at rest (usually a nucleon).
-
-      Any hadron-nucleus event can contain several nucleon interactions. In case of Nw
-      (number of wounded nucleons) nucleons interacting in the hadron-nucleus interaction,
-      the total energy and momentum in the hadron(i)-nucleon(N) center-of-mass frame are:
-      momentum: p_projectile + p_nucleon_1 + p_nucleon_2 + .... p_nucleon_Nw = -(Nw-1) *
-      Pcm with center-of-mass momentum Pcm = p_projectile = - p_nucleon_i. For the energy:
-      E_projectile + E_nucleon_1 + ... E_nucleon_Nw = E_projectile + Nw * E_nucleon.
-
-      Using the above definitions of center-of-mass energies and momenta this leads to the
-      total energy: E_tot = SQS/2 * (1+Nw) + (m_N**2-m_i**2)/(2*SQS) * (Nw-1) and P_tot
-      = -m_N * Plab_i / SQS * (Nw-1).
-
-      A Lorentztransformation of these quantities to the lab. frame recovers Plab_i for
-      the total momentum, so momentum is exactly conserved, and Elab_i + Nw * m_N for the
-      total energy. Not surprisingly the total energy differs from the total energy before
-      the collision by the mass of the additional nucleons (Nw-1)*m_N. In relative terms
-      the additional energy is entirely negligible and as it is not kinetic energy there
-      is zero influence on the shower development.
-
-      Due to the ommission of the hadron masses in Sibyll, the total energy and momentum
-      in the center-of-mass system after the collision are just: E_tot = SQS/2 * (1+Nw)
-      and P_tot = SQS/2 * (1-Nw). After the Lorentztransformation the total momentum in
-      the lab. thus differs from the initial value by (1-Nw)/2 * ( m_N + m_i**2 / (2 *
-      Plab_i) ) and momentum is NOT conserved. Note however that the second term quickly
-      vanishes as the lab. momentum of the projectile increases. The first term is fixed
-      as it depends only on the number of additional nucleons, in relative terms it is
-      always small at high energies.
-
-      For this reason the numerical precision in these tests is limited to 5% to still
-      pass at low energies and no absolute check is implemented, e.g.
-
-          CHECK(pSum.GetComponents(cs).GetX() / P0 == Approx(1).margin(0.05));
-          CHECK((pSum - plab).norm()/1_GeV == Approx(0).margin(plab.norm() * 0.05/1_GeV));
-
-      /FR'2020
-
-      See also:
-
-      Issue 272 / MR 204
-      https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika/-/merge_requests/204
-
-    */
-
-    CHECK(pSum.GetComponents(cs).GetX() / P0 == Approx(1).margin(0.05));
-    CHECK(pSum.GetComponents(cs).GetY() / 1_GeV == Approx(0).margin(1e-4));
-    CHECK(pSum.GetComponents(cs).GetZ() / 1_GeV == Approx(0).margin(1e-4));
-
-    CHECK((pSum - plab).norm() / 1_GeV == Approx(0).margin(plab.norm() * 0.05 / 1_GeV));
-    CHECK(pSum.norm() / P0 == Approx(1).margin(0.05));
-    [[maybe_unused]] const GrammageType length = model.GetInteractionLength(particle);
-    CHECK(length / 1_g * 1_cm * 1_cm == Approx(88.7).margin(0.1));
-    // CHECK(view.getSize() == 20); // also sibyll not stable wrt. to compiler changes
-  }
-
-  SECTION("NuclearInteractionInterface") {
-
-    auto [stack, viewPtr] =
-        setup::testing::setupStack(particles::Code::Nucleus, 4, 2, 500_GeV, nodePtr, cs);
-    setup::StackView& view = *viewPtr;
-    auto particle = stack->first();
-
-    Interaction hmodel;
-    NuclearInteraction model(hmodel, *env);
-
-    [[maybe_unused]] const process::EProcessReturn ret = model.DoInteraction(view);
-    [[maybe_unused]] const GrammageType length = model.GetInteractionLength(particle);
-    CHECK(length / 1_g * 1_cm * 1_cm == Approx(44.2).margin(.1));
-    // CHECK(view.getSize() == 11); // also sibyll not stable wrt. to compiler changes
-  }
-
-  SECTION("DecayInterface") {
-
-    auto [stackPtr, viewPtr] =
-        setup::testing::setupStack(particles::Code::Lambda0, 0, 0, 10_GeV, nodePtr, cs);
-    setup::StackView& view = *viewPtr;
-    auto& stack = *stackPtr;
-    auto particle = stack.first();
-
-    Decay model;
-    model.PrintDecayConfig();
-    [[maybe_unused]] const TimeType time = model.GetLifetime(particle);
-
-    /*[[maybe_unused]] const process::EProcessReturn ret =*/model.DoDecay(view);
-    // run checks
-    // lambda decays into proton and pi- or neutron and pi+
-    CHECK(stack.getEntries() == 3);
-  }
-
-  SECTION("DecayConfiguration") {
-
-    Decay model({particles::Code::PiPlus, particles::Code::PiMinus});
-    CHECK(model.IsDecayHandled(particles::Code::PiPlus));
-    CHECK(model.IsDecayHandled(particles::Code::PiMinus));
-    CHECK_FALSE(model.IsDecayHandled(particles::Code::KPlus));
-
-    const std::vector<particles::Code> particleTestList = {
-        particles::Code::PiPlus, particles::Code::PiMinus, particles::Code::KPlus,
-        particles::Code::Lambda0Bar, particles::Code::D0Bar};
-
-    // setup decays
-    model.SetHandleDecay(particleTestList);
-    for (auto& pCode : particleTestList) CHECK(model.IsDecayHandled(pCode));
-
-    // individually
-    model.SetHandleDecay(particles::Code::KMinus);
-
-    // possible decays
-    CHECK_FALSE(model.CanHandleDecay(particles::Code::Proton));
-    CHECK_FALSE(model.CanHandleDecay(particles::Code::Electron));
-    CHECK(model.CanHandleDecay(particles::Code::PiPlus));
-    CHECK(model.CanHandleDecay(particles::Code::MuPlus));
-  }
-}
diff --git a/Processes/StackInspector/CMakeLists.txt b/Processes/StackInspector/CMakeLists.txt
deleted file mode 100644
index 7ada9c0e57b8aaf51c6006392185f5a37d41870b..0000000000000000000000000000000000000000
--- a/Processes/StackInspector/CMakeLists.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-set (
-  MODEL_SOURCES
-  StackInspector.cc
-  )
-
-set (
-  MODEL_HEADERS
-  StackInspector.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/stack_inspector
-  )
-
-add_library (ProcessStackInspector STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessStackInspector ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-set_target_properties (
-  ProcessStackInspector
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-#  PUBLIC_HEADER "${MODEL_HEADERS}"
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessStackInspector
-  CORSIKAcascade
-  CORSIKAunits
-  CORSIKAgeometry
-  CORSIKAsetup
-  CORSIKAlogging
-  )
-
-target_include_directories (
-  ProcessStackInspector 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS ProcessStackInspector
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-#  PUBLIC_HEADER DESTINATION include/${MODEL_NAMESPACE}
-  )
-
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST (testStackInspector)
-target_link_libraries (
-  testStackInspector
-  ProcessStackInspector
-  CORSIKAgeometry
-  CORSIKAunits
-  CORSIKAtesting
-  )
diff --git a/Processes/StackInspector/StackInspector.cc b/Processes/StackInspector/StackInspector.cc
deleted file mode 100644
index 6bd1bceb3c912abbaf971ba18039504a6422c0b0..0000000000000000000000000000000000000000
--- a/Processes/StackInspector/StackInspector.cc
+++ /dev/null
@@ -1,87 +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/geometry/RootCoordinateSystem.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/process/stack_inspector/StackInspector.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <chrono>
-#include <iomanip>
-#include <iostream>
-#include <limits>
-using namespace std;
-
-using namespace corsika;
-using namespace corsika::particles;
-using namespace corsika::units::si;
-using namespace corsika::process::stack_inspector;
-
-template <typename TStack>
-StackInspector<TStack>::StackInspector(const int vNStep, const bool vReportStack,
-                                       const HEPEnergyType vE0)
-    : StackProcess<StackInspector<TStack>>(vNStep)
-    , ReportStack_(vReportStack)
-    , E0_(vE0)
-    , StartTime_(std::chrono::system_clock::now()) {
-
-  ReportStack_ = false;
-  StartTime_ = std::chrono::system_clock::now();
-}
-
-template <typename TStack>
-StackInspector<TStack>::~StackInspector() {}
-
-template <typename TStack>
-void StackInspector<TStack>::DoStack(const TStack& vS) {
-  [[maybe_unused]] int i = 0;
-  HEPEnergyType Etot = 0_GeV;
-
-  for (const auto& iterP : vS) {
-    HEPEnergyType E = iterP.GetEnergy();
-    Etot += E;
-    if (ReportStack_) {
-      geometry::CoordinateSystem& rootCS = geometry::RootCoordinateSystem::GetInstance()
-                                               .GetRootCoordinateSystem(); // for printout
-      auto pos = iterP.GetPosition().GetCoordinates(rootCS);
-      cout << "StackInspector: i=" << setw(5) << fixed << (i++) << ", id=" << setw(30)
-           << iterP.GetPID() << " E=" << setw(15) << scientific << (E / 1_GeV) << " GeV, "
-           << " pos=" << pos << " node = " << iterP.GetNode();
-      if (iterP.GetPID() == Code::Nucleus) cout << " nuc_ref=" << iterP.GetNucleusRef();
-      cout << endl;
-    }
-  }
-
-  auto const now = std::chrono::system_clock::now();
-  const std::chrono::duration<double> 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;
-  double const progress = dE / E0_;
-
-  double const eta_seconds = elapsed_seconds.count() / progress;
-  std::time_t const eta_time = std::chrono::system_clock::to_time_t(
-      StartTime_ + std::chrono::seconds((int)eta_seconds));
-
-  cout << "StackInspector: "
-       << " time=" << std::put_time(std::localtime(&now_time), "%T")
-       << ", running=" << elapsed_seconds.count() << " seconds"
-       << " (" << setw(3) << int(progress * 100) << "%)"
-       << ", nStep=" << GetStep() << ", stackEntries=" << vS.getEntries()
-       << ", Estack=" << Etot / 1_GeV << " GeV"
-       << ", ETA=" << std::put_time(std::localtime(&eta_time), "%T") << endl;
-  return;
-}
-
-#include <corsika/cascade/testCascade.h>
-#include <corsika/setup/SetupStack.h>
-
-template class process::stack_inspector::StackInspector<setup::Stack>;
-template class process::stack_inspector::StackInspector<TestCascadeStack>;
diff --git a/Processes/StackInspector/StackInspector.h b/Processes/StackInspector/StackInspector.h
deleted file mode 100644
index 6d99a4164844c4fae10fb0e29b15afc15a67d8aa..0000000000000000000000000000000000000000
--- a/Processes/StackInspector/StackInspector.h
+++ /dev/null
@@ -1,52 +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/process/StackProcess.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <chrono>
-
-namespace corsika::process {
-
-  namespace stack_inspector {
-
-    template <typename TStack>
-    class StackInspector : public corsika::process::StackProcess<StackInspector<TStack>> {
-
-      typedef typename TStack::ParticleType Particle;
-
-      using corsika::process::StackProcess<StackInspector<TStack>>::GetStep;
-
-    public:
-      StackInspector(const int vNStep, const bool vReportStack,
-                     const corsika::units::si::HEPEnergyType vE0);
-      ~StackInspector();
-
-      void DoStack(const TStack&);
-
-      /**
-       * To set a new E0, for example when a new shower event is started
-       */
-      void SetE0(const corsika::units::si::HEPEnergyType vE0) { E0_ = vE0; }
-
-    private:
-      bool ReportStack_;
-      corsika::units::si::HEPEnergyType E0_;
-      const corsika::units::si::HEPEnergyType dE_threshold_ = std::invoke([]() {
-        using namespace units::si;
-        return 1_eV;
-      });
-      decltype(std::chrono::system_clock::now()) StartTime_;
-    };
-
-  } // namespace stack_inspector
-
-} // namespace corsika::process
diff --git a/Processes/StackInspector/testStackInspector.cc b/Processes/StackInspector/testStackInspector.cc
deleted file mode 100644
index 3d4cdec2c4b63bfbfb99479848e5711013278930..0000000000000000000000000000000000000000
--- a/Processes/StackInspector/testStackInspector.cc
+++ /dev/null
@@ -1,49 +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 <catch2/catch.hpp>
-
-#include <corsika/process/stack_inspector/StackInspector.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/cascade/testCascade.h>
-
-using namespace corsika::units::si;
-using namespace corsika::process::stack_inspector;
-using namespace corsika;
-using namespace corsika::geometry;
-
-TEST_CASE("StackInspector", "[processes]") {
-
-  auto const& rootCS =
-      geometry::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-  geometry::Point const origin(rootCS, {0_m, 0_m, 0_m});
-  geometry::Vector<units::si::SpeedType::dimension_type> v(rootCS, 0_m / second,
-                                                           0_m / second, 1_m / second);
-  geometry::Line line(origin, v);
-  geometry::LineTrajectory track(line, 10_s);
-
-  TestCascadeStack stack;
-  stack.Clear();
-  HEPEnergyType E0 = 100_GeV;
-  stack.AddParticle(
-      std::make_tuple(particles::Code::Electron, E0,
-                      corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, -1_GeV}),
-                      Point(rootCS, {0_m, 0_m, 10_km}), 0_ns));
-
-  SECTION("interface") {
-
-    StackInspector<TestCascadeStack> model(1, true, E0);
-    model.DoStack(stack);
-  }
-}
diff --git a/Processes/SwitchProcess/CMakeLists.txt b/Processes/SwitchProcess/CMakeLists.txt
deleted file mode 100644
index 352eac7c865da4d37b20b5e52c4eacac11ebb72a..0000000000000000000000000000000000000000
--- a/Processes/SwitchProcess/CMakeLists.txt
+++ /dev/null
@@ -1,40 +0,0 @@
-set (
-  MODEL_HEADERS
-  SwitchProcess.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/switch_process
-  )
-
-add_library (ProcessSwitch INTERFACE)
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessSwitch ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessSwitch
-  INTERFACE
-  CORSIKAunits
-  CORSIKAprocesssequence
-  )
-
-target_include_directories (
-  ProcessSwitch 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (FILES ${MODEL_HEADERS} DESTINATION include/${MODEL_NAMESPACE})
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST(testSwitchProcess)
-target_link_libraries (
-  testSwitchProcess
-  ProcessSwitch
-  CORSIKAstackinterface
-  CORSIKAtesting
-)
-
diff --git a/Processes/SwitchProcess/SwitchProcess.h b/Processes/SwitchProcess/SwitchProcess.h
deleted file mode 100644
index 6da1b82d86c6075bf672d935bd4022e45f3b6800..0000000000000000000000000000000000000000
--- a/Processes/SwitchProcess/SwitchProcess.h
+++ /dev/null
@@ -1,100 +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/process/InteractionProcess.h>
-#include <corsika/process/ProcessSequence.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::process::switch_process {
-
-  /**
-   * This process provides an energy-based switch between two interaction processes P1 and
-   * P2. For energies below the threshold, P1 is invoked, otherwise P2. Both can be either
-   * single interaction processes or multiple ones combined in a ProcessSequence. A
-   * SwitchProcess itself will always be regarded as a distinct case when assembled into a
-   * (greater) ProcessSequence.
-   */
-
-  template <class TLowEProcess, class THighEProcess>
-  class SwitchProcess : public BaseProcess<SwitchProcess<TLowEProcess, THighEProcess>> {
-    TLowEProcess& fLowEProcess;
-    THighEProcess& fHighEProcess;
-    units::si::HEPEnergyType const fThresholdEnergy;
-
-  public:
-    SwitchProcess(TLowEProcess& vLowEProcess, THighEProcess& vHighEProcess,
-                  units::si::HEPEnergyType vThresholdEnergy)
-        : fLowEProcess(vLowEProcess)
-        , fHighEProcess(vHighEProcess)
-        , fThresholdEnergy(vThresholdEnergy) {}
-
-    template <typename TParticle>
-    corsika::units::si::InverseGrammageType GetInverseInteractionLength(TParticle& p) {
-      return 1 / GetInteractionLength(p);
-    }
-
-    template <typename TParticle>
-    units::si::GrammageType GetInteractionLength(TParticle& vParticle) {
-      if (vParticle.GetEnergy() < fThresholdEnergy) {
-        if constexpr (is_process_sequence_v<TLowEProcess>) {
-          return fLowEProcess.GetTotalInteractionLength(vParticle);
-        } else {
-          return fLowEProcess.GetInteractionLength(vParticle);
-        }
-      } else {
-        if constexpr (is_process_sequence_v<THighEProcess>) {
-          return fHighEProcess.GetTotalInteractionLength(vParticle);
-        } else {
-          return fHighEProcess.GetInteractionLength(vParticle);
-        }
-      }
-    }
-
-    // required to conform to ProcessSequence interface. We cannot just
-    // implement DoInteraction() because we want to call SelectInteraction
-    // in case a member process is a ProcessSequence.
-    template <typename TParticle, typename TSecondaries>
-    EProcessReturn SelectInteraction(
-        TParticle& vP, TSecondaries& vS,
-        [[maybe_unused]] corsika::units::si::InverseGrammageType lambda_select,
-        corsika::units::si::InverseGrammageType& lambda_inv_count) {
-      if (vP.GetEnergy() < fThresholdEnergy) {
-        if constexpr (is_process_sequence_v<TLowEProcess> ||
-                      is_switch_process_v<TLowEProcess>) {
-          return fLowEProcess.SelectInteraction(vP, vS, lambda_select, lambda_inv_count);
-        } else {
-          lambda_inv_count += fLowEProcess.GetInverseInteractionLength(vP);
-          // check if we should execute THIS process and then EXIT
-          if (lambda_select < lambda_inv_count) {
-            fLowEProcess.DoInteraction(vS);
-            return EProcessReturn::eInteracted;
-          } else {
-            return EProcessReturn::eOk;
-          }
-        }
-      } else {
-        if constexpr (is_process_sequence_v<THighEProcess> ||
-                      is_switch_process_v<THighEProcess>) {
-          return fHighEProcess.SelectInteraction(vP, vS, lambda_select, lambda_inv_count);
-        } else {
-          lambda_inv_count += fHighEProcess.GetInverseInteractionLength(vP);
-          // check if we should execute THIS process and then EXIT
-          if (lambda_select < lambda_inv_count) {
-            fHighEProcess.DoInteraction(vS);
-            return EProcessReturn::eInteracted;
-          } else {
-            return EProcessReturn::eOk;
-          }
-        }
-      }
-    }
-  };
-} // namespace corsika::process::switch_process
diff --git a/Processes/TrackWriter/CMakeLists.txt b/Processes/TrackWriter/CMakeLists.txt
deleted file mode 100644
index 4c7a19556f8913aa1f385b72b802d525532e1c08..0000000000000000000000000000000000000000
--- a/Processes/TrackWriter/CMakeLists.txt
+++ /dev/null
@@ -1,61 +0,0 @@
-set (
-  MODEL_SOURCES
-  TrackWriter.cc
-  )
-
-set (
-  MODEL_HEADERS
-  TrackWriter.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/track_writer
-  )
-
-add_library (ProcessTrackWriter STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessTrackWriter ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-set_target_properties (
-  ProcessTrackWriter
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-#  PUBLIC_HEADER "${MODEL_HEADERS}"
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessTrackWriter
-  CORSIKAunits
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAsetup
-  )
-
-target_include_directories (
-  ProcessTrackWriter 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS ProcessTrackWriter
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-#  PUBLIC_HEADER DESTINATION include/${MODEL_NAMESPACE}
-  )
-
-
-# --------------------
-# code unit testing
-# CORSIKA_ADD_TEST(testNullModel)
-#target_link_libraries (
-#  testNullModel  ProcessNullModel
-#  CORSIKAsetup
-#  CORSIKAgeometry
-#  CORSIKAunits
-#  CORSIKAthirdparty # for catch2
-#  )
-
diff --git a/Processes/TrackWriter/TrackWriter.cc b/Processes/TrackWriter/TrackWriter.cc
deleted file mode 100644
index 2d9825d869f69a1da7956c7631e8ffdb6d85fea2..0000000000000000000000000000000000000000
--- a/Processes/TrackWriter/TrackWriter.cc
+++ /dev/null
@@ -1,59 +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/process/track_writer/TrackWriter.h>
-
-#include <corsika/particles/ParticleProperties.h>
-
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <iomanip>
-#include <limits>
-
-using namespace corsika::setup;
-using Particle = Stack::ParticleType;
-using Track = Trajectory;
-
-namespace corsika::process::track_writer {
-
-  TrackWriter::TrackWriter(std::string const& filename)
-      : fFilename(filename) {
-
-    using namespace std::string_literals;
-
-    fFile.open(fFilename);
-    fFile
-        << "# PID, E / eV, start coordinates / m, displacement vector to end / m, steplength / m "s
-        << '\n';
-  }
-
-  template <>
-  process::EProcessReturn TrackWriter::DoContinuous(Particle& vP, Track& vT) {
-    using namespace units::si;
-    auto const start = vT.GetPosition(0).GetCoordinates();
-    auto const delta = vT.GetPosition(1).GetCoordinates() - start;
-    auto const pdg = static_cast<int>(particles::GetPDG(vP.GetPID()));
-
-    // clang-format off
-    fFile << std::setw(7) << pdg
-          << std::setw(width) << std::scientific << std::setprecision(precision) << vP.GetEnergy() / 1_eV
-          << std::setw(width) << std::scientific << std::setprecision(precision) << start[0] / 1_m 
-          << std::setw(width) << std::scientific << std::setprecision(precision) << start[1] / 1_m
-          << std::setw(width) << std::scientific << std::setprecision(precision) << start[2] / 1_m
-          << std::setw(width) << std::scientific << std::setprecision(precision) << delta[0] / 1_m
-          << std::setw(width) << std::scientific << std::setprecision(precision) << delta[1] / 1_m
-          << std::setw(width) << std::scientific << std::setprecision(precision) << delta[2] / 1_m 
-          << std::setw(width) << std::scientific << std::setprecision(precision) << delta.norm() / 1_m
-          << '\n';
-    // clang-format on
-
-    return process::EProcessReturn::eOk;
-  }
-
-} // namespace corsika::process::track_writer
diff --git a/Processes/TrackWriter/TrackWriter.h b/Processes/TrackWriter/TrackWriter.h
deleted file mode 100644
index b62e09feac758cf1a30ef1966d4e02379313076e..0000000000000000000000000000000000000000
--- a/Processes/TrackWriter/TrackWriter.h
+++ /dev/null
@@ -1,40 +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/process/ContinuousProcess.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <fstream>
-#include <string>
-
-namespace corsika::process::track_writer {
-
-  class TrackWriter : public corsika::process::ContinuousProcess<TrackWriter> {
-
-  public:
-    TrackWriter(std::string const& filename);
-
-    template <typename Particle, typename Track>
-    corsika::process::EProcessReturn DoContinuous(Particle&, Track&);
-
-    template <typename Particle, typename Track>
-    corsika::units::si::LengthType MaxStepLength(Particle&, Track&) {
-      return units::si::meter * std::numeric_limits<double>::infinity();
-    }
-
-  private:
-    std::string const fFilename;
-    std::ofstream fFile;
-
-    int width = 14;
-    int precision = 6;
-  };
-
-} // namespace corsika::process::track_writer
diff --git a/Processes/Tracking/CMakeLists.txt b/Processes/Tracking/CMakeLists.txt
deleted file mode 100644
index 088b37a41dbd10cd79e30791a166a92ba5229e39..0000000000000000000000000000000000000000
--- a/Processes/Tracking/CMakeLists.txt
+++ /dev/null
@@ -1,44 +0,0 @@
-set (
-  MODEL_HEADERS
-  Intersect.hpp
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/tracking
-  )
-
-add_library (ProcessTrackingIntersects INTERFACE)
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessTrackingIntersects ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessTrackingIntersects
-  INTERFACE
-  CORSIKAsetup
-  CORSIKAutilities
-  CORSIKAenvironment
-  CORSIKAunits
-  CORSIKAgeometry
-  CORSIKAlogging
-  )
-
-target_include_directories (
-  ProcessTrackingIntersects
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (FILES ${MODEL_HEADERS} DESTINATION include/${MODEL_NAMESPACE})
-
-# #-- -- -- -- -- -- -- -- -- --
-# #code unit testing
-CORSIKA_ADD_TEST (testTracking)
-target_link_libraries (
-   testTracking
-   ProcessTrackingLine
-   ProcessTrackingLeapFrogStraight
-   ProcessTrackingLeapFrogCurved
-   CORSIKAtesting
-)
diff --git a/Processes/Tracking/Intersect.hpp b/Processes/Tracking/Intersect.hpp
deleted file mode 100644
index 717ad6ca99647ea1b68e5dce5ad645db0c67449d..0000000000000000000000000000000000000000
--- a/Processes/Tracking/Intersect.hpp
+++ /dev/null
@@ -1,137 +0,0 @@
-/*
- * (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 <corsika/geometry/Point.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/logging/Logging.h>
-#include <corsika/geometry/Intersections.hpp>
-
-#include <limits>
-
-namespace corsika::process::tracking {
-
-  /**
-   * \class Intersect
-   *
-   * This is a CRTP class to provide a generic volume-tree
-   * intersection for the purpose of tracking.
-   *
-   * It return the closest distance in time to the next geometric
-   * intersection, as well as a pointer to the corresponding new
-   * volume.
-   *
-   * User may provide an optional global step-length limit as
-   * parameter. This may be needd for (simpler) algorithms in magnetic
-   * fields, where tracking errors grow linearly with step-length.
-   * Obviously, in the case of the step-length limit, the returend
-   * "next" volume is just the current one.
-   *
-   **/
-
-  template <typename TDerived>
-  class Intersect {
-
-  protected:
-    template <typename TParticle>
-    auto nextIntersect(
-        const TParticle& particle,
-        corsika::units::si::TimeType step_limit =
-            std::numeric_limits<corsika::units::si::TimeType::value_type>::infinity() *
-            corsika::units::si::second) const {
-      using namespace corsika::units::si;
-      using namespace corsika::geometry;
-
-      typedef
-          typename std::remove_reference<decltype(*particle.GetNode())>::type node_type;
-      node_type& volumeNode =
-          *particle.GetNode(); // current "logical" node, from previous tracking step
-      C8LOG_DEBUG("volumeNode={}, numericallyInside={} ", fmt::ptr(&volumeNode),
-                  volumeNode.GetVolume().Contains(particle.GetPosition()));
-
-      // start values:
-      TimeType minTime = step_limit;
-      node_type* minNode = &volumeNode;
-
-      // determine the first geometric collision with any other Volume boundary
-
-      // 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 =
-          TDerived::Intersect(particle, volumeNode);
-      C8LOG_TRACE("curr node {}, parent node {}, hasIntersections={} ",
-                  fmt::ptr(&volumeNode), fmt::ptr(volumeNode.GetParent()),
-                  time_intersections_curr.hasIntersections());
-      if (time_intersections_curr.hasIntersections()) {
-        C8LOG_DEBUG("intersection times with currentLogicalVolumeNode: {} s and {} s",
-                    time_intersections_curr.getEntry() / 1_s,
-                    time_intersections_curr.getExit() / 1_s);
-        if (time_intersections_curr.getExit() <= minTime) {
-          minTime =
-              time_intersections_curr.getExit(); // we exit currentLogicalVolumeNode here
-          minNode = volumeNode.GetParent();
-        }
-      }
-
-      // where do we collide with any of the next-tree-level volumes
-      // entirely contained by currentLogicalVolumeNode
-      for (const auto& node : volumeNode.GetChildNodes()) {
-
-        const Intersections time_intersections = TDerived::Intersect(particle, *node);
-        if (!time_intersections.hasIntersections()) { continue; }
-        C8LOG_DEBUG("intersection times with child 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();
-        C8LOG_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
-        // KNOW we can't yet be in this volume yet, so we HAVE TO
-        // enter it IF exit point is not also in the "past", AND if
-        // extry point is [[much much]] closer than exit point
-        // (because we might have already numerically "exited" it)!
-        if (t_exit > 0_s && t_entry <= minTime &&
-            -t_entry < t_exit) { // protection agains numerical problem, when we already
-                                 // _exited_ before
-                                 // enter chile volume here
-          minTime = t_entry;
-          minNode = node.get();
-        }
-      }
-
-      // these are volumes from the previous tree-level that are cut-out partly from the
-      // current volume
-      for (node_type* node : volumeNode.GetExcludedNodes()) {
-
-        const Intersections time_intersections = TDerived::Intersect(particle, *node);
-        if (!time_intersections.hasIntersections()) { continue; }
-        C8LOG_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();
-        C8LOG_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
-        // KNOW we can't yet be in this volume yet, so we HAVE TO
-        // enter it IF exit point is not also in the "past"!
-        if (t_exit > 0_s && t_entry <= minTime) { // enter volumen child here
-          minTime = t_entry;
-          minNode = node;
-        }
-      }
-      C8LOG_TRACE("t-intersect: {}, node {} ", minTime, fmt::ptr(minNode));
-      return std::make_tuple(minTime, minNode);
-    }
-  }; // namespace corsika::process::tracking
-} // namespace corsika::process::tracking
diff --git a/Processes/TrackingLeapFrogCurved/CMakeLists.txt b/Processes/TrackingLeapFrogCurved/CMakeLists.txt
deleted file mode 100644
index 8b3794d695fc1b0ac1e57d98241f7f876ede0833..0000000000000000000000000000000000000000
--- a/Processes/TrackingLeapFrogCurved/CMakeLists.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-set (
-  MODEL_HEADERS
-  Tracking.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/tracking_leapfrog_curved
-  )
-
-add_library (ProcessTrackingLeapFrogCurved INTERFACE)
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessTrackingLeapFrogCurved ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessTrackingLeapFrogCurved
-  INTERFACE
-  ProcessTrackingIntersects
-  CORSIKAsetup
-  CORSIKAutilities
-  CORSIKAenvironment
-  CORSIKAunits
-  CORSIKAenvironment
-  CORSIKAgeometry
-  CORSIKAlogging
-  )
-
-target_include_directories (
-  ProcessTrackingLeapFrogCurved 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (FILES ${MODEL_HEADERS} DESTINATION include/${MODEL_NAMESPACE})
-
-# Note: all Tracking Algorithms are tested in testTracking
diff --git a/Processes/TrackingLeapFrogCurved/Tracking.h b/Processes/TrackingLeapFrogCurved/Tracking.h
deleted file mode 100644
index da636d6563583096e524a8f08b60150a44169068..0000000000000000000000000000000000000000
--- a/Processes/TrackingLeapFrogCurved/Tracking.h
+++ /dev/null
@@ -1,316 +0,0 @@
-/*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * See file AUTHORS for a list of contributors.
- *
- * 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/process/tracking_line/Tracking.h>
-#include <corsika/process/tracking/Intersect.hpp>
-#include <corsika/geometry/Line.h>
-#include <corsika/geometry/Plane.h>
-#include <corsika/geometry/Sphere.h>
-#include <corsika/geometry/Trajectory.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/geometry/Intersections.hpp>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/quartic.h>
-#include <corsika/logging/Logging.h>
-
-#include <type_traits>
-#include <utility>
-
-#include <fstream>
-
-namespace corsika::process {
-
-  namespace tracking_leapfrog_curved {
-
-    typedef corsika::geometry::Vector<corsika::units::si::magnetic_flux_density_d>
-        MagneticFieldVector;
-
-    /**
-     * \function LeapFrogStep
-     *
-     * Performs one leap-frog step consistent of two halve-steps with steplength/2
-     * The step is caluculated analytically precisely to reach to the next volume
-     *boundary.
-     **/
-    template <typename TParticle>
-    auto LeapFrogStep(const TParticle& particle,
-                      corsika::units::si::LengthType steplength) {
-      using namespace corsika::units::si;
-      if (particle.GetMomentum().norm() == 0_GeV) {
-        return std::make_tuple(particle.GetPosition(), particle.GetMomentum() / 1_GeV,
-                               double(0));
-      } // charge of the particle
-      const int chargeNumber = particle.GetChargeNumber();
-      auto const* currentLogicalVolumeNode = particle.GetNode();
-      MagneticFieldVector const& magneticfield =
-          currentLogicalVolumeNode->GetModelProperties().GetMagneticField(
-              particle.GetPosition());
-      geometry::Vector<SpeedType::dimension_type> velocity =
-          particle.GetMomentum() / particle.GetEnergy() * corsika::units::constants::c;
-      decltype(corsika::units::si::meter /
-               (corsika::units::si::second * corsika::units::si::volt)) k =
-          chargeNumber * corsika::units::constants::cSquared * 1_eV /
-          (velocity.norm() * particle.GetEnergy() * 1_V);
-      geometry::Vector<dimensionless_d> direction = velocity.normalized();
-      auto position = particle.GetPosition(); // First Movement
-      // assuming magnetic field does not change during movement
-      position =
-          position + direction * steplength / 2; // Change of direction by magnetic field
-      direction =
-          direction + direction.cross(magneticfield) * steplength * k; // Second Movement
-      position = position + direction * steplength / 2;
-      auto steplength_true = steplength * (1.0 + (double)direction.norm()) / 2;
-      return std::make_tuple(position, direction.normalized(), steplength_true);
-    }
-
-    /**
-     * \class Tracking
-     *
-     * The class tracking_leapfrog_curved::Tracking is based on the
-     * Bachelor thesis of Andre Schmidt (KIT). It implements a
-     * two-step leap-frog algorithm, but with analytically exact geometric
-     * intersections between leap-frog steps and geometric volumes
-     * (spheres, planes).
-     *
-     **/
-
-    class Tracking : public corsika::process::tracking::Intersect<Tracking> {
-
-    public:
-      Tracking()
-          : straightTracking_{tracking_line::Tracking()} {}
-
-      template <typename TParticle>
-      auto GetTrack(TParticle const& particle) {
-        using namespace corsika::units::si;
-        using namespace corsika::geometry;
-        geometry::Vector<SpeedType::dimension_type> const initialVelocity =
-            particle.GetMomentum() / particle.GetEnergy() * corsika::units::constants::c;
-
-        auto const position = particle.GetPosition();
-        C8LOG_DEBUG(
-            "Tracking pid: {}"
-            " , E = {} GeV",
-            particle.GetPID(), particle.GetEnergy() / 1_GeV);
-        C8LOG_DEBUG("Tracking pos: {}", position.GetCoordinates());
-        C8LOG_DEBUG("Tracking   E: {} GeV", particle.GetEnergy() / 1_GeV);
-        C8LOG_DEBUG("Tracking   p: {} GeV",
-                    particle.GetMomentum().GetComponents() / 1_GeV);
-        C8LOG_DEBUG("Tracking   v: {} ", initialVelocity.GetComponents());
-
-        typedef
-            typename std::remove_reference<decltype(*particle.GetNode())>::type node_type;
-        node_type& volumeNode = *particle.GetNode();
-
-        // for the event of magnetic fields and curved trajectories, we need to limit
-        // maximum step-length since we need to follow curved
-        // trajectories segment-wise -- at least if we don't employ concepts as "Helix
-        // Trajectories" or similar
-        MagneticFieldVector const& magneticfield =
-            volumeNode.GetModelProperties().GetMagneticField(position);
-        corsika::units::si::MagneticFluxType const magnitudeB = magneticfield.norm();
-        int const chargeNumber = particle.GetChargeNumber();
-        bool const no_deflection = chargeNumber == 0 || magnitudeB == 0_T;
-
-        if (no_deflection) { return GetLinearTrajectory(particle); }
-
-        HEPMomentumType const pAlongB_delta =
-            (particle.GetMomentum() -
-             particle.GetMomentum().parallelProjectionOnto(magneticfield))
-                .norm();
-
-        if (pAlongB_delta == 0_GeV) {
-          // particle travel along, parallel to magnetic field. Rg is
-          // "0", but for purpose of step limit we return infinity here.
-          C8LOG_TRACE("pAlongB_delta is 0_GeV --> parallel");
-          return GetLinearTrajectory(particle);
-        }
-
-        LengthType const gyroradius =
-            (pAlongB_delta * 1_V /
-             (corsika::units::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.norm();
-        C8LOG_DEBUG("gyroradius {}, steplimit: {} = {}", gyroradius, steplimit,
-                    steplimit_time);
-
-        // traverse the environment volume tree and find next
-        // intersection
-        auto [minTime, minNode] =
-            tracking::Intersect<Tracking>::nextIntersect(particle, steplimit_time);
-
-        const auto k = chargeNumber * corsika::units::constants::cSquared * 1_eV /
-                       (particle.GetEnergy() * 1_V);
-        return std::make_tuple(
-            geometry::LeapFrogTrajectory(position, initialVelocity, magneticfield, k,
-                                         minTime), // trajectory
-            minNode);                              // next volume node
-      }
-
-      template <typename TParticle, typename TMedium>
-      static geometry::Intersections Intersect(const TParticle& particle,
-                                               const corsika::geometry::Sphere& sphere,
-                                               const TMedium& medium) {
-        using namespace corsika::units::si;
-
-        if (sphere.GetRadius() == 1_km * std::numeric_limits<double>::infinity()) {
-          return geometry::Intersections();
-        }
-
-        const int chargeNumber = particle.GetChargeNumber();
-        const auto& position = particle.GetPosition();
-        MagneticFieldVector const& magneticfield = medium.GetMagneticField(position);
-
-        const geometry::Vector<SpeedType::dimension_type> velocity =
-            particle.GetMomentum() / particle.GetEnergy() * corsika::units::constants::c;
-        const geometry::Vector<dimensionless_d> directionBefore =
-            velocity.normalized(); // determine steplength to next volume
-
-        auto const projectedDirection = directionBefore.cross(magneticfield);
-        auto const projectedDirectionSqrNorm = projectedDirection.GetSquaredNorm();
-        bool const isParallel = (projectedDirectionSqrNorm == 0 * square(1_T));
-
-        if (chargeNumber == 0 || magneticfield.norm() == 0_T || isParallel) {
-          return tracking_line::Tracking::Intersect(particle, sphere, medium);
-        }
-
-        bool const numericallyInside = sphere.Contains(particle.GetPosition());
-
-        const auto absVelocity = velocity.norm();
-        auto energy = particle.GetEnergy();
-        auto k = chargeNumber * corsika::units::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);
-        C8LOG_TRACE("denom={}, a={}, b={}, c={}", denom, a, b, c);
-        std::complex<double>* solutions = solve_quartic(0, a, b, c);
-        LengthType d_enter, d_exit;
-        int first = 0, first_entry = 0, first_exit = 0;
-        for (int i = 0; i < 4; i++) {
-          if (solutions[i].imag() == 0) {
-            LengthType const dist = solutions[i].real() * 1_m;
-            C8LOG_TRACE("Solution (real) for current Volume: {} ", dist);
-            if (numericallyInside) {
-              // there must be an entry (negative) and exit (positive) solution
-              if (dist < -0.0001_m) { // security margin to assure transfer to next
-                                      // logical volume
-                if (first_entry == 0) {
-                  d_enter = dist;
-                } else {
-                  d_enter = std::max(d_enter, dist); // closest negative to zero (-1e-4) m
-                }
-                first_entry++;
-
-              } else { // thus, dist >= -0.0001_m
-
-                if (first_exit == 0) {
-                  d_exit = dist;
-                } else {
-                  d_exit = std::min(d_exit, dist); // closest positive to zero (-1e-4) m
-                }
-                first_exit++;
-              }
-              first = int(first_exit > 0) + int(first_entry > 0);
-
-            } else { // thus, numericallyInside == false
-
-              // both physical solutions (entry, exit) must be positive, and as small as
-              // possible
-              if (dist < -0.0001_m) { // need small numerical margin, to assure transport
-                // into next logical volume
-                continue;
-              }
-              if (first == 0) {
-                d_enter = dist;
-              } else {
-                if (dist < d_enter) {
-                  d_exit = d_enter;
-                  d_enter = dist;
-                } else {
-                  d_exit = dist;
-                }
-              }
-              first++;
-            }
-          } // loop over solutions
-        }
-        delete[] solutions;
-
-        if (first != 2) { // entry and exit points found
-          C8LOG_DEBUG("no intersection! count={}", first);
-          return geometry::Intersections();
-        }
-        return geometry::Intersections(d_enter / absVelocity, d_exit / absVelocity);
-      }
-
-      template <typename TParticle, typename TBaseNodeType>
-      static geometry::Intersections Intersect(const TParticle& particle,
-                                               const TBaseNodeType& volumeNode) {
-        const geometry::Sphere* sphere =
-            dynamic_cast<const geometry::Sphere*>(&volumeNode.GetVolume());
-        if (sphere) {
-          return Intersect(particle, *sphere, volumeNode.GetModelProperties());
-        }
-        throw std::runtime_error(
-            "The Volume type provided is not supported in Intersect(particle, node)");
-      }
-
-    protected:
-      /**
-       * Use internally stored class tracking_line::Tracking to
-       * perform a straight line tracking, if no magnetic bendig was
-       * detected.
-       *
-       */
-      template <typename TParticle>
-      auto GetLinearTrajectory(TParticle& particle) {
-
-        using namespace corsika::units::si;
-
-        // perform simple linear tracking
-        auto [straightTrajectory, minNode] = straightTracking_.GetTrack(particle);
-
-        // return as leap-frog trajectory
-        return std::make_tuple(
-            geometry::LeapFrogTrajectory(
-                straightTrajectory.GetLine().GetR0(),
-                straightTrajectory.GetLine().GetV0(),
-                MagneticFieldVector(particle.GetPosition().GetCoordinateSystem(), 0_T,
-                                    0_T, 0_T),
-                square(0_m) / (square(1_s) * 1_V),
-                straightTrajectory.GetDuration()), // trajectory
-            minNode);                              // next volume node
-      }
-
-    protected:
-      tracking_line::Tracking
-          straightTracking_; ///! we want this for neutral and B=0T tracks
-
-    }; // namespace tracking_leapfrog_curved
-
-  } // namespace tracking_leapfrog_curved
-
-} // namespace corsika::process
diff --git a/Processes/TrackingLeapFrogStraight/CMakeLists.txt b/Processes/TrackingLeapFrogStraight/CMakeLists.txt
deleted file mode 100644
index fba5345506d54fb40d457f58c5a285fa7119abf4..0000000000000000000000000000000000000000
--- a/Processes/TrackingLeapFrogStraight/CMakeLists.txt
+++ /dev/null
@@ -1,36 +0,0 @@
-set (
-  MODEL_HEADERS
-  Tracking.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/tracking_leapfrog_straight
-  )
-
-add_library (ProcessTrackingLeapFrogStraight INTERFACE)
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessTrackingLeapFrogStraight ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessTrackingLeapFrogStraight
-  INTERFACE
-  CORSIKAsetup
-  CORSIKAutilities
-  CORSIKAenvironment
-  CORSIKAunits
-  CORSIKAenvironment
-  CORSIKAgeometry
-  CORSIKAlogging
-  )
-
-target_include_directories (
-  ProcessTrackingLeapFrogStraight 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (FILES ${MODEL_HEADERS} DESTINATION include/${MODEL_NAMESPACE})
-
-# Note: all Tracking Algorithms are tested in testTracking
diff --git a/Processes/TrackingLeapFrogStraight/Tracking.h b/Processes/TrackingLeapFrogStraight/Tracking.h
deleted file mode 100644
index efa46981fe1894dc8154e77268e086a35bf06943..0000000000000000000000000000000000000000
--- a/Processes/TrackingLeapFrogStraight/Tracking.h
+++ /dev/null
@@ -1,235 +0,0 @@
-/*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
- *
- * See file AUTHORS for a list of contributors.
- *
- * 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/geometry/Line.h>
-#include <corsika/geometry/Plane.h>
-#include <corsika/geometry/Sphere.h>
-#include <corsika/geometry/Trajectory.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/process/tracking_line/Tracking.h>
-
-#include <type_traits>
-#include <utility>
-
-#include <fstream>
-
-namespace corsika::process {
-
-  namespace tracking_leapfrog_straight {
-
-    typedef corsika::geometry::Vector<corsika::units::si::magnetic_flux_density_d>
-        MagneticFieldVector;
-
-    /**
-     * \class Tracking
-     *
-     * The class tracking_leapfrog_straight::Tracking inherits from
-     * tracking_line::Tracking and adds a (two-step) Leap-Frog
-     * algorithms with two halve-steps and magnetic deflection.
-     *
-     * The two halve steps are implemented as two straight explicit
-     * `tracking_line::Tracking`s and all geometry intersections are,
-     * thus, based on those two straight line elements.
-     *
-     * As a precaution for numerical instability, the steplength is
-     * limited to correspond to a straight line distance to the next
-     * volume intersection. In typical situations this leads to about
-     * (at least) one full leap-frog step to the next volume boundary.
-     *
-     **/
-
-    class Tracking : public tracking_line::Tracking {
-
-    public:
-      /**
-       * \param firstFraction fraction of first leap-frog halve step
-       * relative to full linear step to next volume boundary. This
-       * should not be less than 0.5, otherwise you risk that
-       * particles will never travel from one volume to the next
-       * one. A cross should be possible (even likely). If
-       * firstFraction is too big (~1) the resulting calculated error
-       * will be largest.
-       *
-       */
-      Tracking(double firstFraction = 0.55)
-          : firstFraction_(firstFraction) {}
-
-      template <typename Particle>
-      auto GetTrack(Particle& particle) {
-        using namespace corsika::units::si;
-        using namespace corsika::geometry;
-        geometry::Vector<SpeedType::dimension_type> initialVelocity =
-            particle.GetMomentum() / particle.GetEnergy() * corsika::units::constants::c;
-
-        const Point initialPosition = particle.GetPosition();
-        C8LOG_DEBUG(
-            "TrackingB pid: {}"
-            " , E = {} GeV",
-            particle.GetPID(), particle.GetEnergy() / 1_GeV);
-        C8LOG_DEBUG("TrackingB pos: {}", initialPosition.GetCoordinates());
-        C8LOG_DEBUG("TrackingB   E: {} GeV", particle.GetEnergy() / 1_GeV);
-        C8LOG_DEBUG("TrackingB   p: {} GeV",
-                    particle.GetMomentum().GetComponents() / 1_GeV);
-        C8LOG_DEBUG("TrackingB   v: {} ", initialVelocity.GetComponents());
-
-        typedef decltype(particle.GetNode()) node_type;
-        const node_type volumeNode = particle.GetNode();
-
-        // check if particle is moving at all
-        const auto absVelocity = initialVelocity.norm();
-        if (absVelocity * 1_s == 0_m) {
-          return std::make_tuple(
-              geometry::LineTrajectory(geometry::Line(initialPosition, initialVelocity),
-                                       0_s),
-              volumeNode);
-        }
-
-        // charge of the particle, and magnetic field
-        const int chargeNumber = particle.GetChargeNumber();
-        auto magneticfield =
-            volumeNode->GetModelProperties().GetMagneticField(initialPosition);
-        const auto magnitudeB = magneticfield.GetNorm();
-        C8LOG_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] =
-            tracking_line::Tracking::GetTrack(particle);
-        { [[maybe_unused]] auto& initialTrackNextVolume_dum = initialTrackNextVolume; }
-        const auto initialTrackLength = initialTrack.GetLength(1);
-
-        C8LOG_DEBUG("initialTrack(0)={}, initialTrack(1)={}, initialTrackLength={}",
-                    initialTrack.GetPosition(0).GetCoordinates(),
-                    initialTrack.GetPosition(1).GetCoordinates(), initialTrackLength);
-
-        // if particle is non-deflectable, we are done:
-        if (no_deflection) {
-          C8LOG_DEBUG("no deflection. tracking finished");
-          return std::make_tuple(initialTrack, initialTrackNextVolume);
-        }
-
-	HEPMomentumType const pAlongB_delta =
-	  (particle.GetMomentum() -
-	   particle.GetMomentum().parallelProjectionOnto(magneticfield))
-	  .norm();
-
-        if (pAlongB_delta == 0_GeV) {
-          // particle travel along, parallel to magnetic field. Rg is
-          // "0", but for purpose of step limit we return infinity here.
-          C8LOG_TRACE("pAlongB_delta is 0_GeV --> parallel");
-          return std::make_tuple(initialTrack, initialTrackNextVolume);
-        }
-
-        LengthType const gyroradius =
-            (pAlongB_delta * 1_V /
-             (corsika::units::constants::c * abs(chargeNumber) * magnitudeB * 1_eV));
-	
-        // we need to limit maximum step-length since we absolutely
-        // 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;
-        C8LOG_DEBUG("gyroradius {}, Steplimit: {}", gyroradius, steplimit);
-
-	
-        // calculate first halve step for "steplimit"
-        const auto initialMomentum = particle.GetMomentum();
-        const auto absMomentum = initialMomentum.norm();
-        const geometry::Vector<dimensionless_d> direction = initialVelocity.normalized();
-
-        // avoid any intersections within first halve steplength
-        LengthType const firstHalveSteplength =
-            std::min(steplimit, initialTrackLength * firstFraction_);
-
-        C8LOG_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 * corsika::units::constants::c * 1_eV /
-                       (particle.GetMomentum().norm() * 1_V);
-        const auto new_direction =
-            direction + direction.cross(magneticfield) * firstHalveSteplength * 2 * k;
-        const auto new_direction_norm = new_direction.norm(); // by design this is >1
-        C8LOG_DEBUG(
-            "position_mid={}, new_direction={}, (new_direction_norm)={}, deflection={}",
-            position_mid.GetCoordinates(), new_direction.GetComponents(),
-            new_direction_norm,
-            acos(std::min(1.0, direction.dot(new_direction) / new_direction_norm)) * 180 /
-                M_PI);
-
-        // check, where the second halve-step direction has geometric intersections
-        particle.SetPosition(position_mid);
-        particle.SetMomentum(new_direction * absMomentum);
-        const auto [finalTrack, finalTrackNextVolume] =
-            tracking_line::Tracking::GetTrack(particle);
-        particle.SetPosition(initialPosition); // this is not nice...
-        particle.SetMomentum(initialMomentum); // this is not nice...
-
-        LengthType const finalTrackLength = finalTrack.GetLength(1);
-        LengthType const secondLeapFrogLength = firstHalveSteplength * new_direction_norm;
-
-        // check if volume transition is obvious, OR
-        // for numerical reasons, particles slighly bend "away" from a
-        // volume boundary have a very hard time to cross the border,
-        // thus, if secondLeapFrogLength is just slighly shorter (1e-4m) than
-        // finalTrackLength we better just [extend the
-        // secondLeapFrogLength slightly and] force the volume
-        // crossing:
-        bool const switch_volume = finalTrackLength - 0.0001_m <= secondLeapFrogLength;
-        LengthType const secondHalveStepLength =
-            std::min(secondLeapFrogLength, finalTrackLength);
-
-        C8LOG_DEBUG(
-            "finalTrack(0)={}, finalTrack(1)={}, finalTrackLength={}, "
-            "secondLeapFrogLength={}, secondHalveStepLength={}, "
-            "secondLeapFrogLength-finalTrackLength={}, "
-            "secondHalveStepLength-finalTrackLength={}, "
-            "nextVol={}, transition={}",
-            finalTrack.GetPosition(0).GetCoordinates(),
-            finalTrack.GetPosition(1).GetCoordinates(), finalTrackLength,
-            secondLeapFrogLength, secondHalveStepLength,
-            secondLeapFrogLength - finalTrackLength,
-            secondHalveStepLength - finalTrackLength, fmt::ptr(finalTrackNextVolume),
-            switch_volume);
-
-        // perform the second halve-step
-        auto const new_direction_normalized = new_direction.normalized();
-        const Point finalPosition =
-            position_mid + new_direction_normalized * secondHalveStepLength;
-
-        const LengthType totalStep = firstHalveSteplength + secondHalveStepLength;
-        const auto delta_pos = finalPosition - initialPosition;
-        const auto distance = delta_pos.norm();
-
-        return std::make_tuple(
-            geometry::LineTrajectory(
-                geometry::Line(initialPosition,
-                               (distance == 0_m ? initialVelocity
-                                                : delta_pos.normalized() * absVelocity)),
-                distance / absVelocity,  // straight distance
-                totalStep / absVelocity, // bend distance
-                initialVelocity,
-                new_direction_normalized * absVelocity), // trajectory
-            (switch_volume ? finalTrackNextVolume : volumeNode));
-      }
-
-    protected:
-      double firstFraction_;
-    };
-
-  } // namespace tracking_leapfrog_straight
-
-} // namespace corsika::process
diff --git a/Processes/TrackingLine/CMakeLists.txt b/Processes/TrackingLine/CMakeLists.txt
deleted file mode 100644
index 75dcaec7e4df4f510adab8f0801413fee77c90e0..0000000000000000000000000000000000000000
--- a/Processes/TrackingLine/CMakeLists.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-set (
-  MODEL_HEADERS
-  Tracking.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/tracking_line
-  )
-
-add_library (ProcessTrackingLine INTERFACE)
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessTrackingLine ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessTrackingLine
-  INTERFACE
-  ProcessTrackingIntersects
-  CORSIKAsetup
-  CORSIKAutilities
-  CORSIKAenvironment
-  CORSIKAunits
-  CORSIKAenvironment
-  CORSIKAgeometry
-  CORSIKAlogging
-  )
-
-target_include_directories (
-  ProcessTrackingLine 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (FILES ${MODEL_HEADERS} DESTINATION include/${MODEL_NAMESPACE})
-
-# Note: all Tracking Algorithms are tested in testTracking
diff --git a/Processes/TrackingLine/Tracking.cc b/Processes/TrackingLine/Tracking.cc
deleted file mode 100644
index 6529bf8b705594809f8ab1515ec4d2891a08aa66..0000000000000000000000000000000000000000
--- a/Processes/TrackingLine/Tracking.cc
+++ /dev/null
@@ -1,24 +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/environment/Environment.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/QuantityVector.h>
-#include <corsika/geometry/Sphere.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/process/tracking_line/Tracking.h>
-#include <corsika/logging/Logging.h>
-
-#include <limits>
-#include <stdexcept>
-#include <utility>
-
-using namespace corsika::geometry;
-using namespace corsika::units::si;
-
-namespace corsika::process::tracking_line {} // namespace corsika::process::tracking_line
diff --git a/Processes/TrackingLine/Tracking.h b/Processes/TrackingLine/Tracking.h
deleted file mode 100644
index 0dfac370a702e864068fdd523e20ec488886629c..0000000000000000000000000000000000000000
--- a/Processes/TrackingLine/Tracking.h
+++ /dev/null
@@ -1,124 +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/geometry/Line.h>
-#include <corsika/geometry/Plane.h>
-#include <corsika/geometry/Sphere.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/geometry/Intersections.hpp>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/logging/Logging.h>
-#include <corsika/process/tracking/Intersect.hpp>
-#include <corsika/geometry/Trajectory.h>
-
-#include <type_traits>
-#include <utility>
-
-namespace corsika::process {
-
-  namespace tracking_line {
-
-    /**
-     * \class Tracking
-     *
-     *
-     *
-     **/
-
-    class Tracking : public corsika::process::tracking::Intersect<Tracking> {
-
-    public:
-
-      template <typename TParticle>
-      auto GetTrack(TParticle const& particle) {
-        using namespace corsika::units::si;
-        using namespace corsika::geometry;
-        geometry::Vector<SpeedType::dimension_type> const initialVelocity =
-            particle.GetMomentum() / particle.GetEnergy() * corsika::units::constants::c;
-
-        auto const initialPosition = particle.GetPosition();
-        C8LOG_DEBUG(
-            "Tracking pid: {}"
-            " , E = {} GeV",
-            particle.GetPID(), particle.GetEnergy() / 1_GeV);
-        C8LOG_DEBUG("Tracking pos: {}", initialPosition.GetCoordinates());
-        C8LOG_DEBUG("Tracking   E: {} GeV", particle.GetEnergy() / 1_GeV);
-        C8LOG_DEBUG("Tracking   p: {} GeV",
-                    particle.GetMomentum().GetComponents() / 1_GeV);
-        C8LOG_DEBUG("Tracking   v: {} ", initialVelocity.GetComponents());
-
-        // traverse the environment volume tree and find next
-        // intersection
-        auto [minTime, minNode] = tracking::Intersect<Tracking>::nextIntersect(particle);
-
-        return std::make_tuple(
-            geometry::LineTrajectory(geometry::Line(initialPosition, initialVelocity),
-                                     minTime), // trajectory
-            minNode);                          // next volume node
-      }
-
-      template <typename TParticle, typename TMedium>
-      static geometry::Intersections Intersect(const TParticle& particle,
-                                               const corsika::geometry::Sphere& sphere,
-                                               const TMedium&) {
-        using namespace corsika::units::si;
-        auto const delta = particle.GetPosition() - sphere.GetCenter();
-        auto const velocity =
-            particle.GetMomentum() / particle.GetEnergy() * corsika::units::constants::c;
-        auto const vSqNorm = velocity.squaredNorm();
-        auto const R = sphere.GetRadius();
-
-        auto const vDotDelta = velocity.dot(delta);
-        auto const discriminant =
-            vDotDelta * vDotDelta - vSqNorm * (delta.squaredNorm() - R * R);
-
-        if (discriminant.magnitude() > 0) {
-          auto const sqDisc = sqrt(discriminant);
-          auto const invDenom = 1 / vSqNorm;
-          return geometry::Intersections((-vDotDelta - sqDisc) * invDenom,
-                                         (-vDotDelta + sqDisc) * invDenom);
-        }
-        return geometry::Intersections();
-      }
-
-      template <typename TParticle, typename TBaseNodeType>
-      static geometry::Intersections Intersect(const TParticle& particle,
-                                               const TBaseNodeType& volumeNode) {
-        const geometry::Sphere* sphere =
-            dynamic_cast<const geometry::Sphere*>(&volumeNode.GetVolume());
-        if (sphere) {
-          return Intersect(particle, *sphere, volumeNode.GetModelProperties());
-        }
-        throw std::runtime_error(
-            "The Volume type provided is not supported in Intersect(particle, node)");
-      }
-
-      template <typename TParticle, typename TMedium>
-      static geometry::Intersections Intersect(const TParticle& particle,
-                                               const geometry::Plane& plane,
-                                               const TMedium&) {
-        using namespace corsika::units::si;
-        auto const delta = plane.GetCenter() - particle.GetPosition();
-        auto const velocity =
-            particle.GetMomentum() / particle.GetEnergy() * corsika::units::constants::c;
-        auto const n = plane.GetNormal();
-        auto const c = n.dot(velocity);
-
-        return Intersections(c.magnitude() == 0
-                                 ? std::numeric_limits<TimeType::value_type>::infinity() *
-                                       1_s
-                                 : n.dot(delta) / c);
-      }
-
-    };
-
-  } // namespace tracking_line
-
-} // namespace corsika::process
diff --git a/Processes/TrackingLine/testTrackingLineStack.h b/Processes/TrackingLine/testTrackingLineStack.h
deleted file mode 100644
index 5c714048a5eb1e3978f39f6815c80eaaebcdc8a0..0000000000000000000000000000000000000000
--- a/Processes/TrackingLine/testTrackingLineStack.h
+++ /dev/null
@@ -1,39 +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/environment/Environment.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/Vector.h>
-
-#include <corsika/particles/ParticleProperties.h>
-
-#include <corsika/stack/CombinedStack.h>
-#include <corsika/stack/node/GeometryNodeStackExtension.h>
-#include <corsika/stack/nuclear_extension/NuclearStackExtension.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-using TestEnvironmentType =
-    corsika::environment::Environment<corsika::environment::Empty>;
-
-template <typename T>
-using SetupGeometryDataInterface =
-    corsika::stack::node::GeometryDataInterface<T, TestEnvironmentType>;
-
-// combine particle data stack with geometry information for tracking
-template <typename StackIter>
-using StackWithGeometryInterface = corsika::stack::CombinedParticleInterface<
-    corsika::stack::nuclear_extension::ParticleDataStack::MPIType,
-    SetupGeometryDataInterface, StackIter>;
-
-using TestTrackingLineStack = corsika::stack::CombinedStack<
-    typename corsika::stack::nuclear_extension::ParticleDataStack::StackImpl,
-    corsika::stack::node::GeometryData<TestEnvironmentType>, StackWithGeometryInterface>;
diff --git a/Processes/UrQMD/CMakeLists.txt b/Processes/UrQMD/CMakeLists.txt
deleted file mode 100644
index 0fbdb064c86e4f8ab8e63dda4be96ff09b72004c..0000000000000000000000000000000000000000
--- a/Processes/UrQMD/CMakeLists.txt
+++ /dev/null
@@ -1,96 +0,0 @@
-set (
-  MODEL_SOURCES
-  UrQMD.cc
-  urqmdInterface.F
-  addpart.f
-  angdis.f
-  anndec.f
-  blockres.f
-  boxprg.f
-  cascinit.f
-  coload.f
-  dectim.f
-  delpart.f
-  detbal.f
-  dwidth.f
-  error.f
-  getmass.f
-  getspin.f
-  init.f
-  iso.f
-  ityp2pdg.f
-  jdecay2.f
-  make22.f
-  numrec.f
-  output.f
-  paulibl.f
-  proppot.f
-  saveinfo.f
-  scatter.f
-  siglookup.f
-  string.f
-  tabinit.f
-  urqmd.f
-  whichres.f
-)
-
-set (
-  MODEL_HEADERS
-  UrQMD.h
-  )
-
-set (
-  MODEL_NAMESPACE
-  corsika/process/urqmd
-  )
-
-add_library (ProcessUrQMD STATIC ${MODEL_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (ProcessUrQMD ${MODEL_NAMESPACE} ${MODEL_HEADERS})
-
-set_target_properties (
-  ProcessUrQMD
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  )
-
-# target dependencies on other libraries (also the header onlys)
-target_link_libraries (
-  ProcessUrQMD
-  CORSIKAprocesssequence
-  CORSIKAparticles
-  CORSIKAunits
-  CORSIKAgeometry
-  CORSIKArandom
-  CORSIKAsetup
-  CORSIKAthirdparty
-  )
-
-target_include_directories (
-  ProcessUrQMD 
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  TARGETS ProcessUrQMD
-  LIBRARY DESTINATION lib
-  ARCHIVE DESTINATION lib
-  )
-
-
-# --------------------
-# code unit testing
-CORSIKA_ADD_TEST(testUrQMD SOURCES testUrQMD.cc ${MODEL_HEADERS})
-target_link_libraries (
-  testUrQMD
-  ProcessUrQMD
-  CORSIKAtesting
-  )
-
-add_executable(urqmd_xs urqmd_xs.cc)
-target_link_libraries(urqmd_xs
-	ProcessUrQMD
-	CORSIKAparticles
-)
diff --git a/Processes/UrQMD/UrQMD.cc b/Processes/UrQMD/UrQMD.cc
deleted file mode 100644
index 9d78d20ae7f209cdc62d0c368ee336a70dabd49a..0000000000000000000000000000000000000000
--- a/Processes/UrQMD/UrQMD.cc
+++ /dev/null
@@ -1,478 +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/geometry/QuantityVector.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/logging/Logging.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/process/urqmd/UrQMD.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <algorithm>
-#include <array>
-#include <cassert>
-#include <cmath>
-#include <fstream>
-#include <functional>
-#include <random>
-#include <sstream>
-
-using namespace corsika::process::UrQMD;
-using namespace corsika::units::si;
-
-using SetupStack = corsika::setup::Stack;
-using SetupParticle = corsika::setup::Stack::StackIterator;
-using SetupView = corsika::setup::StackView;
-
-UrQMD::UrQMD(std::string const& xs_file) {
-  readXSFile(xs_file);
-  iniurqmdc8_();
-}
-
-CrossSectionType UrQMD::GetTabulatedCrossSection(particles::Code projectileCode,
-                                                 corsika::particles::Code targetCode,
-                                                 HEPEnergyType labEnergy) const {
-  // translated to C++ from CORSIKA 7 subroutine cxtot_u
-
-  auto const kinEnergy = labEnergy - particles::GetMass(projectileCode);
-
-  assert(kinEnergy >= HEPEnergyType::zero());
-
-  double const logKinEnergy = std::log10(kinEnergy * (1 / 1_GeV));
-  double const ye = std::max(10 * logKinEnergy + 10.5, 1.);
-  int const je = std::min(int(ye), int(xs_interp_support_table_.shape()[2] - 2));
-  std::array<double, 3> w;
-  w[2 - 1] = ye - je;
-  w[3 - 1] = w[2 - 1] * (w[2 - 1] - 1.) * .5;
-  w[1 - 1] = 1 - w[2 - 1] + w[3 - 1];
-  w[2 - 1] = w[2 - 1] - 2 * w[3 - 1];
-
-  int projectileIndex;
-  switch (projectileCode) {
-    case particles::Code::Proton:
-      projectileIndex = 0;
-      break;
-    case particles::Code::AntiProton:
-      projectileIndex = 1;
-      break;
-    case particles::Code::Neutron:
-      projectileIndex = 2;
-      break;
-    case particles::Code::AntiNeutron:
-      projectileIndex = 3;
-      break;
-    case particles::Code::PiPlus:
-      projectileIndex = 4;
-      break;
-    case particles::Code::PiMinus:
-      projectileIndex = 5;
-      break;
-    case particles::Code::KPlus:
-      projectileIndex = 6;
-      break;
-    case particles::Code::KMinus:
-      projectileIndex = 7;
-      break;
-    case particles::Code::K0Short:
-    case particles::Code::K0Long:
-    /* since K0Short and K0Long are treated the same, we can also add K0 and K0Bar
-     * to the list. This is a deviation from CORSIKA 7. */
-    case particles::Code::K0:
-    case particles::Code::K0Bar:
-      projectileIndex = 8;
-      break;
-    default: {
-      C8LOG_WARN("WARNING: UrQMD cross-section not tabulated for {}", projectileCode);
-      return CrossSectionType::zero();
-    }
-  }
-
-  int targetIndex;
-  switch (targetCode) {
-    case particles::Code::Nitrogen:
-      targetIndex = 0;
-      break;
-    case particles::Code::Oxygen:
-      targetIndex = 1;
-      break;
-    case particles::Code::Argon:
-      targetIndex = 2;
-      break;
-    default:
-      std::stringstream ss;
-      ss << "UrQMD cross-section not tabluated for target " << targetCode;
-      throw std::runtime_error(ss.str().data());
-  }
-
-  auto result = CrossSectionType::zero();
-  for (int i = 0; i < 3; ++i) {
-    result +=
-        xs_interp_support_table_[projectileIndex][targetIndex][je + i - 1 - 1] * w[i];
-  }
-
-  C8LOG_DEBUG("UrQMD::GetTabulatedCrossSection proj={}, targ={}, E={}GeV, sigma={}",
-              particles::GetName(projectileCode), particles::GetName(targetCode),
-              labEnergy / 1_GeV, result);
-
-  return result;
-}
-
-CrossSectionType UrQMD::GetCrossSection(particles::Code projectileCode,
-                                        corsika::particles::Code targetCode,
-                                        HEPEnergyType labEnergy, int projectileA) const {
-  // the following is a (incomplete!) translation of ptsigtot() into C++
-  if (projectileCode != particles::Code::Nucleus &&
-      !IsNucleus(targetCode)) { // both particles are "special"
-    auto const mProj = particles::GetMass(projectileCode);
-    auto const mTar = particles::GetMass(targetCode);
-    double sqrtS = sqrt(units::static_pow<2>(mProj) + units::static_pow<2>(mTar) +
-                        2 * labEnergy * mTar) *
-                   (1 / 1_GeV);
-
-    // we must set some UrQMD globals first...
-    auto const [ityp, iso3] = ConvertToUrQMD(projectileCode);
-    inputs_.spityp[0] = ityp;
-    inputs_.spiso3[0] = iso3;
-
-    auto const [itypTar, iso3Tar] = ConvertToUrQMD(targetCode);
-    inputs_.spityp[1] = itypTar;
-    inputs_.spiso3[1] = iso3Tar;
-
-    int one = 1;
-    int two = 2;
-    int three = 3;
-
-    double const totalXS = sigtot_(one, two, sqrtS);
-
-    // subtract elastic cross-section as in ptsigtot()
-    int itypmn, itypmx, iso3mn, iso3mx;
-    if (ityp < itypTar) {
-      itypmn = ityp;
-      itypmx = itypTar;
-
-      iso3mn = iso3;
-      iso3mx = iso3Tar;
-    } else {
-      itypmx = ityp;
-      itypmn = itypTar;
-
-      iso3mx = iso3;
-      iso3mn = iso3Tar;
-    }
-
-    int isigline = collclass_(itypmx, iso3mx, itypmn, iso3mn);
-    int iline = readsigmaln_(three, one, isigline);
-    double sigEl;
-    double massProj = mProj / 1_GeV;
-    double massTar = mTar / 1_GeV;
-
-    crossx_(iline, sqrtS, ityp, iso3, massProj, itypTar, iso3Tar, massTar, sigEl);
-
-    if (totalXS > sigEl) {
-      return (totalXS - sigEl) * 1_mb;
-    } else {
-      return sigEl * 0_mb;
-    }
-  } else {
-    int const Ap = projectileA;
-    int const At = IsNucleus(targetCode) ? particles::GetNucleusA(targetCode) : 1;
-
-    double const maxImpact = nucrad_(Ap) + nucrad_(At) + 2 * options_.CTParam[30 - 1];
-    return 10_mb * M_PI * units::static_pow<2>(maxImpact);
-    // is a constant cross-section really reasonable?
-  }
-}
-
-template <typename TParticle> // need template here, as this is called both with
-                              // SetupParticle as well as SetupProjectile
-CrossSectionType UrQMD::GetCrossSection(TParticle const& projectile,
-                                        corsika::particles::Code targetCode) const {
-
-  auto const projectileCode = projectile.GetPID();
-  auto const projectileEnergyLab = projectile.GetEnergy();
-
-  if (projectileCode == particles::Code::Nucleus) {
-    /*
-     * unfortunately unavoidable at the moment until we have tools to get the actual
-     * inealstic cross-section from UrQMD
-     */
-    return CrossSectionType::zero();
-  }
-
-  return GetTabulatedCrossSection(projectileCode, targetCode, projectileEnergyLab);
-}
-
-bool UrQMD::CanInteract(particles::Code code) 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.
-
-  // Interactions with nucleus projectiles are possible in principle with UrQMD
-  // but right now we don't have access to the inelastic (production) cross-section,
-  // so we unfortunately have to forbid these interactions for the time being.
-
-  static particles::Code const validProjectileCodes[] = {
-      particles::Code::Proton,      particles::Code::AntiProton, particles::Code::Neutron,
-      particles::Code::AntiNeutron, particles::Code::PiPlus,     particles::Code::PiMinus,
-      particles::Code::KPlus,       particles::Code::KMinus,     particles::Code::K0Short,
-      particles::Code::K0Long};
-
-  return std::find(std::cbegin(validProjectileCodes), std::cend(validProjectileCodes),
-                   code) != std::cend(validProjectileCodes);
-}
-
-GrammageType UrQMD::GetInteractionLength(SetupParticle const& particle) const {
-  if (!CanInteract(particle.GetPID())) {
-    // we could do the canInteract check in GetCrossSection, too but if
-    // we do it here we have the advantage of avoiding the loop
-    return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
-  }
-
-  auto const& mediumComposition =
-      particle.GetNode()->GetModelProperties().GetNuclearComposition();
-  using namespace std::placeholders;
-
-  CrossSectionType const weightedProdCrossSection = mediumComposition.WeightedSum(
-      std::bind(&UrQMD::GetCrossSection<decltype(particle)>, this, particle, _1));
-
-  return mediumComposition.GetAverageMassNumber() * units::constants::u /
-         weightedProdCrossSection;
-}
-
-corsika::process::EProcessReturn UrQMD::DoInteraction(SetupView& view) {
-  using namespace units::si;
-
-  auto const projectile = view.GetProjectile();
-
-  auto projectileCode = projectile.GetPID();
-  auto const projectileEnergyLab = projectile.GetEnergy();
-  auto const& projectileMomentumLab = projectile.GetMomentum();
-  auto const& projectilePosition = projectile.GetPosition();
-  auto const projectileTime = projectile.GetTime();
-
-  C8LOG_DEBUG("UrQMD::DoInteraction pid={} E={} GeV", projectileCode,
-              projectileEnergyLab / 1_GeV);
-
-  // sample target particle
-  auto const& mediumComposition =
-      projectile.GetNode()->GetModelProperties().GetNuclearComposition();
-  auto const componentCrossSections = std::invoke([&]() {
-    auto const& components = mediumComposition.GetComponents();
-    std::vector<CrossSectionType> crossSections;
-    crossSections.reserve(components.size());
-
-    for (auto const c : components) {
-      crossSections.push_back(GetCrossSection(projectile, c));
-    }
-
-    return crossSections;
-  });
-
-  auto const targetCode = mediumComposition.SampleTarget(componentCrossSections, rng_);
-  auto const targetA = particles::GetNucleusA(targetCode);
-  auto const targetZ = particles::GetNucleusZ(targetCode);
-
-  inputs_.nevents = 1;
-  sys_.eos = 0; // could be configurable in principle
-  inputs_.outsteps = 1;
-  sys_.nsteps = 1;
-
-  // initialization regarding projectile
-  if (particles::Code::Nucleus == projectileCode) {
-    // is this everything?
-    inputs_.prspflg = 0;
-
-    sys_.Ap = projectile.GetNuclearA();
-    sys_.Zp = projectile.GetNuclearZ();
-    rsys_.ebeam = (projectileEnergyLab - projectile.GetMass()) * (1 / 1_GeV) /
-                  projectile.GetNuclearA();
-
-    rsys_.bdist = nucrad_(targetA) + nucrad_(sys_.Ap) + 2 * options_.CTParam[30 - 1];
-
-    int const id = 1;
-    cascinit_(sys_.Zp, sys_.Ap, id);
-  } else {
-    inputs_.prspflg = 1;
-    sys_.Ap = 1; // even for non-baryons this has to be set, see vanilla UrQMD.f
-    rsys_.bdist = nucrad_(targetA) + nucrad_(1) + 2 * options_.CTParam[30 - 1];
-    rsys_.ebeam = (projectileEnergyLab - projectile.GetMass()) * (1 / 1_GeV);
-
-    if (projectileCode == particles::Code::K0Long ||
-        projectileCode == particles::Code::K0Short) {
-      projectileCode = booleanDist_(rng_) ? particles::Code::K0 : particles::Code::K0Bar;
-    }
-
-    auto const [ityp, iso3] = ConvertToUrQMD(projectileCode);
-    // todo: conversion of K_long/short into strong eigenstates;
-    inputs_.spityp[0] = ityp;
-    inputs_.spiso3[0] = iso3;
-  }
-
-  // initilazation regarding target
-  if (particles::IsNucleus(targetCode)) {
-    sys_.Zt = targetZ;
-    sys_.At = targetA;
-    inputs_.trspflg = 0; // nucleus as target
-    int const id = 2;
-    cascinit_(sys_.Zt, sys_.At, id);
-  } else {
-    inputs_.trspflg = 1; // special particle as target
-    auto const [ityp, iso3] = ConvertToUrQMD(targetCode);
-    inputs_.spityp[1] = ityp;
-    inputs_.spiso3[1] = iso3;
-  }
-
-  int iflb = 0; // flag for retrying interaction in case of empty event, 0 means retry
-  urqmd_(iflb);
-
-  // now retrieve secondaries from UrQMD
-  auto const& originalCS = projectileMomentumLab.GetCoordinateSystem();
-  geometry::CoordinateSystem const zAxisFrame =
-      originalCS.RotateToZ(projectileMomentumLab);
-
-  for (int i = 0; i < sys_.npart; ++i) {
-    auto code = ConvertFromUrQMD(isys_.ityp[i], isys_.iso3[i]);
-    if (code == particles::Code::K0 || code == particles::Code::K0Bar) {
-      code = booleanDist_(rng_) ? particles::Code::K0Short : particles::Code::K0Long;
-    }
-
-    // "coor_.p0[i] * 1_GeV" is likely off-shell as UrQMD doesn't preserve masses well
-    auto momentum = geometry::Vector(
-        zAxisFrame,
-        geometry::QuantityVector<dimensionless_d>{coor_.px[i], coor_.py[i], coor_.pz[i]} *
-            1_GeV);
-
-    auto const energy = sqrt(momentum.squaredNorm() + square(particles::GetMass(code)));
-
-    momentum.rebase(originalCS); // transform back into standard lab frame
-    C8LOG_DEBUG(" Secondary {} code {} p={} GeV", i, code,
-                momentum.GetComponents() / 1_GeV);
-
-    view.AddSecondary(
-        std::make_tuple(code, energy, momentum, projectilePosition, projectileTime));
-  }
-
-  C8LOG_DEBUG("UrQMD generated {} secondaries!", sys_.npart);
-
-  return process::EProcessReturn::eOk;
-}
-
-/**
- * the random number generator function of UrQMD
- */
-double corsika::process::UrQMD::ranf_(int&) {
-  static corsika::random::RNG& rng =
-      corsika::random::RNGManager::GetInstance().GetRandomStream("urqmd");
-  static std::uniform_real_distribution<double> dist;
-
-  return dist(rng);
-}
-
-corsika::particles::Code corsika::process::UrQMD::ConvertFromUrQMD(int vItyp, int vIso3) {
-  int const pdgInt =
-      pdgid_(vItyp, vIso3); // use the conversion function provided by UrQMD
-  if (pdgInt == 0) {        // pdgid_ returns 0 on error
-    throw std::runtime_error("UrQMD pdgid() returned 0");
-  }
-  auto const pdg = static_cast<particles::PDGCode>(pdgInt);
-  return particles::ConvertFromPDG(pdg);
-}
-
-std::pair<int, int> corsika::process::UrQMD::ConvertToUrQMD(
-    corsika::particles::Code code) {
-  static const std::map<int, std::pair<int, int>> mapPDGToUrQMD{
-      // data mostly from github.com/afedynitch/ParticleDataTool
-      {22, {100, 0}},      // gamma
-      {111, {101, 0}},     // pi0
-      {211, {101, 2}},     // pi+
-      {-211, {101, -2}},   // pi-
-      {321, {106, 1}},     // K+
-      {-321, {-106, -1}},  // K-
-      {311, {106, -1}},    // K0
-      {-311, {-106, 1}},   // K0bar
-      {2212, {1, 1}},      // p
-      {2112, {1, -1}},     // n
-      {-2212, {-1, -1}},   // pbar
-      {-2112, {-1, 1}},    // nbar
-      {221, {102, 0}},     // eta
-      {213, {104, 2}},     // rho+
-      {-213, {104, -2}},   // rho-
-      {113, {104, 0}},     // rho0
-      {323, {108, 2}},     // K*+
-      {-323, {108, -2}},   // K*-
-      {313, {108, 0}},     // K*0
-      {-313, {-108, 0}},   // K*0-bar
-      {223, {103, 0}},     // omega
-      {333, {109, 0}},     // phi
-      {3222, {40, 2}},     // Sigma+
-      {3212, {40, 0}},     // Sigma0
-      {3112, {40, -2}},    // Sigma-
-      {3322, {49, 0}},     // Xi0
-      {3312, {49, -1}},    // Xi-
-      {3122, {27, 0}},     // Lambda0
-      {2224, {17, 4}},     // Delta++
-      {2214, {17, 2}},     // Delta+
-      {2114, {17, 0}},     // Delta0
-      {1114, {17, -2}},    // Delta-
-      {3224, {41, 2}},     // Sigma*+
-      {3214, {41, 0}},     // Sigma*0
-      {3114, {41, -2}},    // Sigma*-
-      {3324, {50, 0}},     // Xi*0
-      {3314, {50, -1}},    // Xi*-
-      {3334, {55, 0}},     // Omega-
-      {411, {133, 2}},     // D+
-      {-411, {133, -2}},   // D-
-      {421, {133, 0}},     // D0
-      {-421, {-133, 0}},   // D0-bar
-      {441, {107, 0}},     // etaC
-      {431, {138, 1}},     // Ds+
-      {-431, {138, -1}},   // Ds-
-      {433, {139, 1}},     // Ds*+
-      {-433, {139, -1}},   // Ds*-
-      {413, {134, 1}},     // D*+
-      {-413, {134, -1}},   // D*-
-      {10421, {134, 0}},   // D*0
-      {-10421, {-134, 0}}, // D*0-bar
-      {443, {135, 0}},     // jpsi
-  };
-
-  return mapPDGToUrQMD.at(static_cast<int>(GetPDG(code)));
-}
-
-void UrQMD::readXSFile(std::string const& filename) {
-  std::ifstream file(filename, std::ios::in);
-
-  if (!file.is_open()) { throw std::runtime_error(filename + " could not be opened."); }
-
-  std::string line;
-
-  std::getline(file, line);
-  std::stringstream ss(line);
-
-  char dummy;
-  int nTargets, nProjectiles, nSupports;
-  ss >> dummy >> nTargets >> nProjectiles >> nSupports;
-
-  decltype(xs_interp_support_table_)::extent_gen extents;
-  xs_interp_support_table_.resize(extents[nProjectiles][nTargets][nSupports]);
-
-  for (int i = 0; i < nTargets; ++i) {
-    for (int j = 0; j < nProjectiles; ++j) {
-      for (int k = 0; k < nSupports; ++k) {
-        std::getline(file, line);
-        std::stringstream s(line);
-        double energy, sigma;
-        s >> energy >> sigma;
-        xs_interp_support_table_[j][i][k] = sigma * 1_mb;
-      }
-
-      std::getline(file, line);
-      std::getline(file, line);
-    }
-  }
-}
diff --git a/Processes/UrQMD/UrQMD.h b/Processes/UrQMD/UrQMD.h
deleted file mode 100644
index 937b6d702f63443fe0e221060e86739010ffcae8..0000000000000000000000000000000000000000
--- a/Processes/UrQMD/UrQMD.h
+++ /dev/null
@@ -1,152 +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/particles/ParticleProperties.h>
-#include <corsika/process/InteractionProcess.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/CorsikaData.h>
-
-#include <boost/multi_array.hpp>
-
-#include <array>
-#include <random>
-#include <string>
-#include <utility>
-
-namespace corsika::process::UrQMD {
-  class UrQMD : public corsika::process::InteractionProcess<UrQMD> {
-  public:
-    UrQMD(std::string const& path = utl::CorsikaData("UrQMD/UrQMD-1.3.1-xs.dat"));
-    corsika::units::si::GrammageType GetInteractionLength(
-        corsika::setup::Stack::StackIterator const&) const;
-
-    template <typename TParticle>
-    corsika::units::si::CrossSectionType GetCrossSection(TParticle const&,
-                                                         corsika::particles::Code) const;
-
-    corsika::units::si::CrossSectionType GetCrossSection(
-        particles::Code, particles::Code, corsika::units::si::HEPEnergyType,
-        int Ap = 1) const;
-
-    corsika::units::si::CrossSectionType GetTabulatedCrossSection(
-        particles::Code, particles::Code, corsika::units::si::HEPEnergyType) const;
-
-    corsika::process::EProcessReturn DoInteraction(corsika::setup::StackView&);
-
-    bool CanInteract(particles::Code) const;
-
-  private:
-    void readXSFile(std::string const&);
-
-    corsika::random::RNG& rng_ =
-        corsika::random::RNGManager::GetInstance().GetRandomStream("urqmd");
-
-    std::uniform_int_distribution<int> booleanDist_{0, 1};
-    boost::multi_array<corsika::units::si::CrossSectionType, 3> xs_interp_support_table_;
-  };
-
-  namespace details::constants {
-    // from coms.f
-    int constexpr nmax = 500;
-
-    // from options.f
-    int constexpr numcto = 400;
-    int constexpr numctp = 400;
-
-    // from inputs.f
-    int constexpr aamax = 300;
-
-  } // namespace details::constants
-
-  template <typename T>
-  using nmaxArray = std::array<T, details::constants::nmax>;
-  using nmaxIntArray = nmaxArray<int>;
-  using nmaxDoubleArray = nmaxArray<double>;
-
-  extern "C" {
-  // FORTRAN functions defined in UrQMD
-  void iniurqmdc8_();
-  double ranf_(int&);
-  void cascinit_(int const&, int const&, int const&);
-  double nucrad_(int const&);
-  void urqmd_(int&);
-  int pdgid_(int const&, int const&);
-  double sigtot_(int&, int&, double&);
-  int collclass_(int&, int&, int&, int&);
-  double crossx_(int const&, double const&, int const&, int const&, double const&,
-                 int const&, int const&, double const&, double&);
-  int readsigmaln_(int const&, int const&, int const&);
-
-  // defined in coms.f
-  extern struct {
-    int npart, nbar, nmes, ctag, nsteps, uid_cnt, ranseed, event;
-    int Ap; // projectile mass number (in case of nucleus)
-    int At; // target mass number (in case of nucleus)
-    int Zp; // projectile charge number (in case of nucleus)
-    int Zt; // target charge number (in case of nucleus)
-    int eos, dectag, NHardRes, NSoftRes, NDecRes, NElColl, NBlColl;
-  } sys_;
-
-  extern struct {
-    double time, acttime, bdist, bimp, bmin;
-    double ebeam; // lab-frame energy of projectile
-    double ecm;
-  } rsys_;
-
-  // defined in coms.f
-  extern struct {
-    nmaxIntArray spin, ncoll, charge, ityp, lstcoll, iso3, origin, strid, uid;
-  } isys_;
-
-  // defined in coor.f
-  extern struct {
-    nmaxDoubleArray r0, rx, ry, rz, p0, px, py, pz, fmass, rww, dectime;
-  } coor_;
-
-  // defined in inputs.f
-  extern struct {
-    int nevents;
-    std::array<int, 2> spityp; // particle codes of: [0]: projectile, [1]: target
-    int prspflg;               // projectile special flag
-    int trspflg; // target special flag, set to 1 unless target is nucleus > H
-    std::array<int, 2> spiso3; // particle codes of: [0]: projectile, [1]: target
-    int outsteps, bflag, srtflag, efuncflag, nsrt, npb, firstev;
-  } inputs_;
-
-  // defined in inputs.f
-  extern struct {
-    double srtmin, srtmax, pbeam, betann, betatar, betapro, pbmin, pbmax;
-  } input2_;
-
-  // defined in options.f
-  extern struct {
-    std::array<double, details::constants::numcto> CTOption;
-    std::array<double, details::constants::numctp> CTParam;
-  } options_;
-
-  extern struct {
-    int fixedseed, bf13, bf14, bf15, bf16, bf17, bf18, bf19, bf20;
-  } loptions_;
-
-  // defined in urqmdInterface.F
-  extern struct { std::array<double, 3> xs, bim; } cxs_u2_;
-  }
-
-  /**
-   * convert CORSIKA code to UrQMD code tuple
-   *
-   * In the current implementation a detour via the PDG code is made.
-   */
-  std::pair<int, int> ConvertToUrQMD(particles::Code);
-  particles::Code ConvertFromUrQMD(int vItyp, int vIso3);
-
-} // namespace corsika::process::UrQMD
diff --git a/Processes/UrQMD/testUrQMD.cc b/Processes/UrQMD/testUrQMD.cc
deleted file mode 100644
index 17a010c2fb324bea5aa800c793451bafbce48f7c..0000000000000000000000000000000000000000
--- a/Processes/UrQMD/testUrQMD.cc
+++ /dev/null
@@ -1,170 +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/process/urqmd/UrQMD.h>
-#include <corsika/random/RNGManager.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-
-#include <corsika/units/PhysicalConstants.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/utl/CorsikaFenv.h>
-
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/HomogeneousMedium.h>
-#include <corsika/environment/NuclearComposition.h>
-
-#include <tuple>
-#include <utility>
-
-#include <catch2/catch.hpp>
-
-using namespace corsika;
-using namespace corsika::process::UrQMD;
-using namespace corsika::units::si;
-
-template <typename TStackView>
-auto sumCharge(TStackView const& view) {
-  int totalCharge = 0;
-
-  for (auto const& p : view) { totalCharge += particles::GetChargeNumber(p.GetPID()); }
-
-  return totalCharge;
-}
-
-template <typename TStackView>
-auto sumMomentum(TStackView const& view, geometry::CoordinateSystem const& vCS) {
-  geometry::Vector<hepenergy_d> sum{vCS, 0_eV, 0_eV, 0_eV};
-
-  for (auto const& p : view) { sum += p.GetMomentum(); }
-
-  return sum;
-}
-
-TEST_CASE("UrQMD") {
-  SECTION("conversion") {
-    REQUIRE_THROWS(process::UrQMD::ConvertFromUrQMD(106, 0));
-    REQUIRE(process::UrQMD::ConvertFromUrQMD(101, 0) == particles::Code::Pi0);
-    REQUIRE(process::UrQMD::ConvertToUrQMD(particles::Code::PiPlus) ==
-            std::make_pair<int, int>(101, 2));
-  }
-
-  feenableexcept(FE_INVALID);
-  corsika::random::RNGManager::GetInstance().RegisterRandomStream("urqmd");
-  UrQMD urqmd;
-
-  SECTION("interaction length") {
-    auto [env, csPtr, nodePtr] =
-        setup::testing::setupEnvironment(particles::Code::Nitrogen);
-    auto const& cs = *csPtr;
-    [[maybe_unused]] auto const& env_dummy = env;
-    [[maybe_unused]] auto const& node_dummy = nodePtr;
-
-    particles::Code validProjectileCodes[] = {
-        particles::Code::PiPlus,  particles::Code::PiMinus, particles::Code::Proton,
-        particles::Code::Neutron, particles::Code::KPlus,   particles::Code::KMinus,
-        particles::Code::K0,      particles::Code::K0Bar,   particles::Code::K0Long};
-
-    for (auto code : validProjectileCodes) {
-      auto [stack, view] = setup::testing::setupStack(code, 0, 0, 100_GeV, nodePtr, cs);
-      REQUIRE(stack->getEntries() == 1);
-      REQUIRE(view->getEntries() == 0);
-
-      // simple check whether the cross-section is non-vanishing
-      // only nuclei with available tabluated data so far
-      REQUIRE(urqmd.GetInteractionLength(stack->GetNextParticle()) > 1_g / square(1_cm));
-    }
-  }
-
-  SECTION("nucleus projectile") {
-    auto [env, csPtr, nodePtr] =
-        setup::testing::setupEnvironment(particles::Code::Oxygen);
-    [[maybe_unused]] auto const& env_dummy = env;      // against warnings
-    [[maybe_unused]] auto const& node_dummy = nodePtr; // against warnings
-
-    unsigned short constexpr A = 14, Z = 7;
-    auto [stackPtr, secViewPtr] = setup::testing::setupStack(particles::Code::Nucleus, A,
-                                                             Z, 400_GeV, nodePtr, *csPtr);
-    REQUIRE(stackPtr->getEntries() == 1);
-    REQUIRE(secViewPtr->getEntries() == 0);
-
-    // must be assigned to variable, cannot be used as rvalue?!
-    auto projectile = secViewPtr->GetProjectile();
-    auto const projectileMomentum = projectile.GetMomentum();
-    [[maybe_unused]] process::EProcessReturn const ret = urqmd.DoInteraction(*secViewPtr);
-
-    REQUIRE(sumCharge(*secViewPtr) ==
-            Z + particles::GetChargeNumber(particles::Code::Oxygen));
-
-    auto const secMomSum =
-        sumMomentum(*secViewPtr, projectileMomentum.GetCoordinateSystem());
-    REQUIRE((secMomSum - projectileMomentum).norm() / projectileMomentum.norm() ==
-            Approx(0).margin(1e-2));
-  }
-
-  SECTION("\"special\" projectile") {
-    auto [env, csPtr, nodePtr] =
-        setup::testing::setupEnvironment(particles::Code::Oxygen);
-    [[maybe_unused]] auto const& env_dummy = env;      // against warnings
-    [[maybe_unused]] auto const& node_dummy = nodePtr; // against warnings
-
-    auto [stackPtr, secViewPtr] = setup::testing::setupStack(particles::Code::PiPlus, 0,
-                                                             0, 400_GeV, nodePtr, *csPtr);
-    REQUIRE(stackPtr->getEntries() == 1);
-    REQUIRE(secViewPtr->getEntries() == 0);
-
-    // must be assigned to variable, cannot be used as rvalue?!
-    auto projectile = secViewPtr->GetProjectile();
-    auto const projectileMomentum = projectile.GetMomentum();
-
-    [[maybe_unused]] process::EProcessReturn const ret = urqmd.DoInteraction(*secViewPtr);
-
-    REQUIRE(sumCharge(*secViewPtr) ==
-            particles::GetChargeNumber(particles::Code::PiPlus) +
-                particles::GetChargeNumber(particles::Code::Oxygen));
-
-    auto const secMomSum =
-        sumMomentum(*secViewPtr, projectileMomentum.GetCoordinateSystem());
-    REQUIRE((secMomSum - projectileMomentum).norm() / projectileMomentum.norm() ==
-            Approx(0).margin(1e-2));
-  }
-
-  SECTION("K0Long projectile") {
-    auto [env, csPtr, nodePtr] =
-        setup::testing::setupEnvironment(particles::Code::Oxygen);
-    [[maybe_unused]] auto const& env_dummy = env;      // against warnings
-    [[maybe_unused]] auto const& node_dummy = nodePtr; // against warnings
-
-    auto [stackPtr, secViewPtr] = setup::testing::setupStack(particles::Code::K0Long, 0,
-                                                             0, 400_GeV, nodePtr, *csPtr);
-    REQUIRE(stackPtr->getEntries() == 1);
-    REQUIRE(secViewPtr->getEntries() == 0);
-
-    // must be assigned to variable, cannot be used as rvalue?!
-    auto projectile = secViewPtr->GetProjectile();
-    auto const projectileMomentum = projectile.GetMomentum();
-
-    [[maybe_unused]] process::EProcessReturn const ret = urqmd.DoInteraction(*secViewPtr);
-
-    REQUIRE(sumCharge(*secViewPtr) ==
-            particles::GetChargeNumber(particles::Code::K0Long) +
-                particles::GetChargeNumber(particles::Code::Oxygen));
-
-    auto const secMomSum =
-        sumMomentum(*secViewPtr, projectileMomentum.GetCoordinateSystem());
-    REQUIRE((secMomSum - projectileMomentum).norm() / projectileMomentum.norm() ==
-            Approx(0).margin(1e-2));
-  }
-}
diff --git a/Processes/UrQMD/urqmd_xs.cc b/Processes/UrQMD/urqmd_xs.cc
deleted file mode 100644
index 8fd9e225165ceb802cc5c5cd93e682f4cb8b6e82..0000000000000000000000000000000000000000
--- a/Processes/UrQMD/urqmd_xs.cc
+++ /dev/null
@@ -1,41 +0,0 @@
-/*
- * (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.
- */
-
-// a little helper to dump UrQMD cross-sections
-
-#include <corsika/process/urqmd/UrQMD.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <cstdlib>
-#include <fstream>
-#include <iostream>
-
-using namespace corsika;
-using namespace corsika::units::si;
-
-int main() {
-  random::RNGManager::GetInstance().RegisterRandomStream("UrQMD");
-  corsika::process::UrQMD::UrQMD urqmd;
-
-  std::vector<particles::Code> const projectiles{
-      {particles::Code::Proton, particles::Code::AntiProton, particles::Code::Neutron,
-       particles::Code::AntiNeutron, particles::Code::PiPlus, particles::Code::PiMinus,
-       particles::Code::KPlus, particles::Code::KMinus, particles::Code::K0Short}};
-
-  for (auto const& p : projectiles) {
-    std::ofstream file(std::string("xs_") + particles::GetName(p) + ".dat");
-    for (auto Elab = particles::GetMass(p) + 200_MeV; Elab <= 10_TeV; Elab *= 1.02) {
-      file << Elab / 1_GeV << '\t'
-           << urqmd.GetTabulatedCrossSection(p, particles::Code::Nitrogen, Elab) / 1_mb
-           << std::endl;
-    }
-  }
-
-  return EXIT_SUCCESS;
-}
diff --git a/Python/.gitignore b/Python/.gitignore
deleted file mode 100644
index d82fa7a96c90159c0dba3052f3a09284f027a1a7..0000000000000000000000000000000000000000
--- a/Python/.gitignore
+++ /dev/null
@@ -1,143 +0,0 @@
-# Byte-compiled / optimized / DLL files
-__pycache__/
-*.py[cod]
-*$py.class
-
-# C extensions
-*.so
-
-# Distribution / packaging
-.Python
-build/
-develop-eggs/
-dist/
-downloads/
-eggs/
-.eggs/
-lib/
-lib64/
-parts/
-sdist/
-var/
-wheels/
-pip-wheel-metadata/
-share/python-wheels/
-*.egg-info/
-.installed.cfg
-*.egg
-MANIFEST
-
-# PyInstaller
-#  Usually these files are written by a python script from a template
-#  before PyInstaller builds the exe, so as to inject date/other infos into it.
-*.manifest
-*.spec
-
-# Installer logs
-pip-log.txt
-pip-delete-this-directory.txt
-
-# Unit test / coverage reports
-htmlcov/
-.tox/
-.nox/
-.coverage
-.coverage.*
-.cache
-nosetests.xml
-coverage.xml
-*.cover
-*.py,cover
-.hypothesis/
-.pytest_cache/
-cover/
-
-# Translations
-*.mo
-*.pot
-
-# Django stuff:
-*.log
-local_settings.py
-db.sqlite3
-db.sqlite3-journal
-
-# Flask stuff:
-instance/
-.webassets-cache
-
-# Scrapy stuff:
-.scrapy
-
-# Sphinx documentation
-docs/_build/
-
-# PyBuilder
-.pybuilder/
-target/
-
-# Jupyter Notebook
-.ipynb_checkpoints
-
-# IPython
-profile_default/
-ipython_config.py
-
-# pyenv
-#   For a library or package, you might want to ignore these files since the code is
-#   intended to run in multiple environments; otherwise, check them in:
-# .python-version
-
-# pipenv
-#   According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
-#   However, in case of collaboration, if having platform-specific dependencies or dependencies
-#   having no cross-platform support, pipenv may install dependencies that don't work, or not
-#   install all needed dependencies.
-#Pipfile.lock
-
-# PEP 582; used by e.g. github.com/David-OConnor/pyflow
-__pypackages__/
-
-# Celery stuff
-celerybeat-schedule
-celerybeat.pid
-
-# SageMath parsed files
-*.sage.py
-
-# Environments
-.env
-.venv
-env/
-venv/
-ENV/
-env.bak/
-venv.bak/
-
-# Spyder project settings
-.spyderproject
-.spyproject
-
-# Rope project settings
-.ropeproject
-
-# mkdocs documentation
-/site
-
-# mypy
-.mypy_cache/
-.dmypy.json
-dmypy.json
-
-# Pyre type checker
-.pyre/
-
-# pytype static type analyzer
-.pytype/
-
-# Cython debug symbols
-cython_debug/
-
-# static files generated from Django application using `collectstatic`
-media
-static
diff --git a/Python/LICENSE b/Python/LICENSE
deleted file mode 100644
index 94a9ed024d3859793618152ea559a168bbcbb5e2..0000000000000000000000000000000000000000
--- a/Python/LICENSE
+++ /dev/null
@@ -1,674 +0,0 @@
-                    GNU GENERAL PUBLIC LICENSE
-                       Version 3, 29 June 2007
-
- Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
-                            Preamble
-
-  The GNU General Public License is a free, copyleft license for
-software and other kinds of works.
-
-  The licenses for most software and other practical works are designed
-to take away your freedom to share and change the works.  By contrast,
-the GNU General Public License is intended to guarantee your freedom to
-share and change all versions of a program--to make sure it remains free
-software for all its users.  We, the Free Software Foundation, use the
-GNU General Public License for most of our software; it applies also to
-any other work released this way by its authors.  You can apply it to
-your programs, too.
-
-  When we speak of free software, we are referring to freedom, not
-price.  Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-them if you wish), that you receive source code or can get it if you
-want it, that you can change the software or use pieces of it in new
-free programs, and that you know you can do these things.
-
-  To protect your rights, we need to prevent others from denying you
-these rights or asking you to surrender the rights.  Therefore, you have
-certain responsibilities if you distribute copies of the software, or if
-you modify it: responsibilities to respect the freedom of others.
-
-  For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must pass on to the recipients the same
-freedoms that you received.  You must make sure that they, too, receive
-or can get the source code.  And you must show them these terms so they
-know their rights.
-
-  Developers that use the GNU GPL protect your rights with two steps:
-(1) assert copyright on the software, and (2) offer you this License
-giving you legal permission to copy, distribute and/or modify it.
-
-  For the developers' and authors' protection, the GPL clearly explains
-that there is no warranty for this free software.  For both users' and
-authors' sake, the GPL requires that modified versions be marked as
-changed, so that their problems will not be attributed erroneously to
-authors of previous versions.
-
-  Some devices are designed to deny users access to install or run
-modified versions of the software inside them, although the manufacturer
-can do so.  This is fundamentally incompatible with the aim of
-protecting users' freedom to change the software.  The systematic
-pattern of such abuse occurs in the area of products for individuals to
-use, which is precisely where it is most unacceptable.  Therefore, we
-have designed this version of the GPL to prohibit the practice for those
-products.  If such problems arise substantially in other domains, we
-stand ready to extend this provision to those domains in future versions
-of the GPL, as needed to protect the freedom of users.
-
-  Finally, every program is threatened constantly by software patents.
-States should not allow patents to restrict development and use of
-software on general-purpose computers, but in those that do, we wish to
-avoid the special danger that patents applied to a free program could
-make it effectively proprietary.  To prevent this, the GPL assures that
-patents cannot be used to render the program non-free.
-
-  The precise terms and conditions for copying, distribution and
-modification follow.
-
-                       TERMS AND CONDITIONS
-
-  0. Definitions.
-
-  "This License" refers to version 3 of the GNU General Public License.
-
-  "Copyright" also means copyright-like laws that apply to other kinds of
-works, such as semiconductor masks.
-
-  "The Program" refers to any copyrightable work licensed under this
-License.  Each licensee is addressed as "you".  "Licensees" and
-"recipients" may be individuals or organizations.
-
-  To "modify" a work means to copy from or adapt all or part of the work
-in a fashion requiring copyright permission, other than the making of an
-exact copy.  The resulting work is called a "modified version" of the
-earlier work or a work "based on" the earlier work.
-
-  A "covered work" means either the unmodified Program or a work based
-on the Program.
-
-  To "propagate" a work means to do anything with it that, without
-permission, would make you directly or secondarily liable for
-infringement under applicable copyright law, except executing it on a
-computer or modifying a private copy.  Propagation includes copying,
-distribution (with or without modification), making available to the
-public, and in some countries other activities as well.
-
-  To "convey" a work means any kind of propagation that enables other
-parties to make or receive copies.  Mere interaction with a user through
-a computer network, with no transfer of a copy, is not conveying.
-
-  An interactive user interface displays "Appropriate Legal Notices"
-to the extent that it includes a convenient and prominently visible
-feature that (1) displays an appropriate copyright notice, and (2)
-tells the user that there is no warranty for the work (except to the
-extent that warranties are provided), that licensees may convey the
-work under this License, and how to view a copy of this License.  If
-the interface presents a list of user commands or options, such as a
-menu, a prominent item in the list meets this criterion.
-
-  1. Source Code.
-
-  The "source code" for a work means the preferred form of the work
-for making modifications to it.  "Object code" means any non-source
-form of a work.
-
-  A "Standard Interface" means an interface that either is an official
-standard defined by a recognized standards body, or, in the case of
-interfaces specified for a particular programming language, one that
-is widely used among developers working in that language.
-
-  The "System Libraries" of an executable work include anything, other
-than the work as a whole, that (a) is included in the normal form of
-packaging a Major Component, but which is not part of that Major
-Component, and (b) serves only to enable use of the work with that
-Major Component, or to implement a Standard Interface for which an
-implementation is available to the public in source code form.  A
-"Major Component", in this context, means a major essential component
-(kernel, window system, and so on) of the specific operating system
-(if any) on which the executable work runs, or a compiler used to
-produce the work, or an object code interpreter used to run it.
-
-  The "Corresponding Source" for a work in object code form means all
-the source code needed to generate, install, and (for an executable
-work) run the object code and to modify the work, including scripts to
-control those activities.  However, it does not include the work's
-System Libraries, or general-purpose tools or generally available free
-programs which are used unmodified in performing those activities but
-which are not part of the work.  For example, Corresponding Source
-includes interface definition files associated with source files for
-the work, and the source code for shared libraries and dynamically
-linked subprograms that the work is specifically designed to require,
-such as by intimate data communication or control flow between those
-subprograms and other parts of the work.
-
-  The Corresponding Source need not include anything that users
-can regenerate automatically from other parts of the Corresponding
-Source.
-
-  The Corresponding Source for a work in source code form is that
-same work.
-
-  2. Basic Permissions.
-
-  All rights granted under this License are granted for the term of
-copyright on the Program, and are irrevocable provided the stated
-conditions are met.  This License explicitly affirms your unlimited
-permission to run the unmodified Program.  The output from running a
-covered work is covered by this License only if the output, given its
-content, constitutes a covered work.  This License acknowledges your
-rights of fair use or other equivalent, as provided by copyright law.
-
-  You may make, run and propagate covered works that you do not
-convey, without conditions so long as your license otherwise remains
-in force.  You may convey covered works to others for the sole purpose
-of having them make modifications exclusively for you, or provide you
-with facilities for running those works, provided that you comply with
-the terms of this License in conveying all material for which you do
-not control copyright.  Those thus making or running the covered works
-for you must do so exclusively on your behalf, under your direction
-and control, on terms that prohibit them from making any copies of
-your copyrighted material outside their relationship with you.
-
-  Conveying under any other circumstances is permitted solely under
-the conditions stated below.  Sublicensing is not allowed; section 10
-makes it unnecessary.
-
-  3. Protecting Users' Legal Rights From Anti-Circumvention Law.
-
-  No covered work shall be deemed part of an effective technological
-measure under any applicable law fulfilling obligations under article
-11 of the WIPO copyright treaty adopted on 20 December 1996, or
-similar laws prohibiting or restricting circumvention of such
-measures.
-
-  When you convey a covered work, you waive any legal power to forbid
-circumvention of technological measures to the extent such circumvention
-is effected by exercising rights under this License with respect to
-the covered work, and you disclaim any intention to limit operation or
-modification of the work as a means of enforcing, against the work's
-users, your or third parties' legal rights to forbid circumvention of
-technological measures.
-
-  4. Conveying Verbatim Copies.
-
-  You may convey verbatim copies of the Program's source code as you
-receive it, in any medium, provided that you conspicuously and
-appropriately publish on each copy an appropriate copyright notice;
-keep intact all notices stating that this License and any
-non-permissive terms added in accord with section 7 apply to the code;
-keep intact all notices of the absence of any warranty; and give all
-recipients a copy of this License along with the Program.
-
-  You may charge any price or no price for each copy that you convey,
-and you may offer support or warranty protection for a fee.
-
-  5. Conveying Modified Source Versions.
-
-  You may convey a work based on the Program, or the modifications to
-produce it from the Program, in the form of source code under the
-terms of section 4, provided that you also meet all of these conditions:
-
-    a) The work must carry prominent notices stating that you modified
-    it, and giving a relevant date.
-
-    b) The work must carry prominent notices stating that it is
-    released under this License and any conditions added under section
-    7.  This requirement modifies the requirement in section 4 to
-    "keep intact all notices".
-
-    c) You must license the entire work, as a whole, under this
-    License to anyone who comes into possession of a copy.  This
-    License will therefore apply, along with any applicable section 7
-    additional terms, to the whole of the work, and all its parts,
-    regardless of how they are packaged.  This License gives no
-    permission to license the work in any other way, but it does not
-    invalidate such permission if you have separately received it.
-
-    d) If the work has interactive user interfaces, each must display
-    Appropriate Legal Notices; however, if the Program has interactive
-    interfaces that do not display Appropriate Legal Notices, your
-    work need not make them do so.
-
-  A compilation of a covered work with other separate and independent
-works, which are not by their nature extensions of the covered work,
-and which are not combined with it such as to form a larger program,
-in or on a volume of a storage or distribution medium, is called an
-"aggregate" if the compilation and its resulting copyright are not
-used to limit the access or legal rights of the compilation's users
-beyond what the individual works permit.  Inclusion of a covered work
-in an aggregate does not cause this License to apply to the other
-parts of the aggregate.
-
-  6. Conveying Non-Source Forms.
-
-  You may convey a covered work in object code form under the terms
-of sections 4 and 5, provided that you also convey the
-machine-readable Corresponding Source under the terms of this License,
-in one of these ways:
-
-    a) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by the
-    Corresponding Source fixed on a durable physical medium
-    customarily used for software interchange.
-
-    b) Convey the object code in, or embodied in, a physical product
-    (including a physical distribution medium), accompanied by a
-    written offer, valid for at least three years and valid for as
-    long as you offer spare parts or customer support for that product
-    model, to give anyone who possesses the object code either (1) a
-    copy of the Corresponding Source for all the software in the
-    product that is covered by this License, on a durable physical
-    medium customarily used for software interchange, for a price no
-    more than your reasonable cost of physically performing this
-    conveying of source, or (2) access to copy the
-    Corresponding Source from a network server at no charge.
-
-    c) Convey individual copies of the object code with a copy of the
-    written offer to provide the Corresponding Source.  This
-    alternative is allowed only occasionally and noncommercially, and
-    only if you received the object code with such an offer, in accord
-    with subsection 6b.
-
-    d) Convey the object code by offering access from a designated
-    place (gratis or for a charge), and offer equivalent access to the
-    Corresponding Source in the same way through the same place at no
-    further charge.  You need not require recipients to copy the
-    Corresponding Source along with the object code.  If the place to
-    copy the object code is a network server, the Corresponding Source
-    may be on a different server (operated by you or a third party)
-    that supports equivalent copying facilities, provided you maintain
-    clear directions next to the object code saying where to find the
-    Corresponding Source.  Regardless of what server hosts the
-    Corresponding Source, you remain obligated to ensure that it is
-    available for as long as needed to satisfy these requirements.
-
-    e) Convey the object code using peer-to-peer transmission, provided
-    you inform other peers where the object code and Corresponding
-    Source of the work are being offered to the general public at no
-    charge under subsection 6d.
-
-  A separable portion of the object code, whose source code is excluded
-from the Corresponding Source as a System Library, need not be
-included in conveying the object code work.
-
-  A "User Product" is either (1) a "consumer product", which means any
-tangible personal property which is normally used for personal, family,
-or household purposes, or (2) anything designed or sold for incorporation
-into a dwelling.  In determining whether a product is a consumer product,
-doubtful cases shall be resolved in favor of coverage.  For a particular
-product received by a particular user, "normally used" refers to a
-typical or common use of that class of product, regardless of the status
-of the particular user or of the way in which the particular user
-actually uses, or expects or is expected to use, the product.  A product
-is a consumer product regardless of whether the product has substantial
-commercial, industrial or non-consumer uses, unless such uses represent
-the only significant mode of use of the product.
-
-  "Installation Information" for a User Product means any methods,
-procedures, authorization keys, or other information required to install
-and execute modified versions of a covered work in that User Product from
-a modified version of its Corresponding Source.  The information must
-suffice to ensure that the continued functioning of the modified object
-code is in no case prevented or interfered with solely because
-modification has been made.
-
-  If you convey an object code work under this section in, or with, or
-specifically for use in, a User Product, and the conveying occurs as
-part of a transaction in which the right of possession and use of the
-User Product is transferred to the recipient in perpetuity or for a
-fixed term (regardless of how the transaction is characterized), the
-Corresponding Source conveyed under this section must be accompanied
-by the Installation Information.  But this requirement does not apply
-if neither you nor any third party retains the ability to install
-modified object code on the User Product (for example, the work has
-been installed in ROM).
-
-  The requirement to provide Installation Information does not include a
-requirement to continue to provide support service, warranty, or updates
-for a work that has been modified or installed by the recipient, or for
-the User Product in which it has been modified or installed.  Access to a
-network may be denied when the modification itself materially and
-adversely affects the operation of the network or violates the rules and
-protocols for communication across the network.
-
-  Corresponding Source conveyed, and Installation Information provided,
-in accord with this section must be in a format that is publicly
-documented (and with an implementation available to the public in
-source code form), and must require no special password or key for
-unpacking, reading or copying.
-
-  7. Additional Terms.
-
-  "Additional permissions" are terms that supplement the terms of this
-License by making exceptions from one or more of its conditions.
-Additional permissions that are applicable to the entire Program shall
-be treated as though they were included in this License, to the extent
-that they are valid under applicable law.  If additional permissions
-apply only to part of the Program, that part may be used separately
-under those permissions, but the entire Program remains governed by
-this License without regard to the additional permissions.
-
-  When you convey a copy of a covered work, you may at your option
-remove any additional permissions from that copy, or from any part of
-it.  (Additional permissions may be written to require their own
-removal in certain cases when you modify the work.)  You may place
-additional permissions on material, added by you to a covered work,
-for which you have or can give appropriate copyright permission.
-
-  Notwithstanding any other provision of this License, for material you
-add to a covered work, you may (if authorized by the copyright holders of
-that material) supplement the terms of this License with terms:
-
-    a) Disclaiming warranty or limiting liability differently from the
-    terms of sections 15 and 16 of this License; or
-
-    b) Requiring preservation of specified reasonable legal notices or
-    author attributions in that material or in the Appropriate Legal
-    Notices displayed by works containing it; or
-
-    c) Prohibiting misrepresentation of the origin of that material, or
-    requiring that modified versions of such material be marked in
-    reasonable ways as different from the original version; or
-
-    d) Limiting the use for publicity purposes of names of licensors or
-    authors of the material; or
-
-    e) Declining to grant rights under trademark law for use of some
-    trade names, trademarks, or service marks; or
-
-    f) Requiring indemnification of licensors and authors of that
-    material by anyone who conveys the material (or modified versions of
-    it) with contractual assumptions of liability to the recipient, for
-    any liability that these contractual assumptions directly impose on
-    those licensors and authors.
-
-  All other non-permissive additional terms are considered "further
-restrictions" within the meaning of section 10.  If the Program as you
-received it, or any part of it, contains a notice stating that it is
-governed by this License along with a term that is a further
-restriction, you may remove that term.  If a license document contains
-a further restriction but permits relicensing or conveying under this
-License, you may add to a covered work material governed by the terms
-of that license document, provided that the further restriction does
-not survive such relicensing or conveying.
-
-  If you add terms to a covered work in accord with this section, you
-must place, in the relevant source files, a statement of the
-additional terms that apply to those files, or a notice indicating
-where to find the applicable terms.
-
-  Additional terms, permissive or non-permissive, may be stated in the
-form of a separately written license, or stated as exceptions;
-the above requirements apply either way.
-
-  8. Termination.
-
-  You may not propagate or modify a covered work except as expressly
-provided under this License.  Any attempt otherwise to propagate or
-modify it is void, and will automatically terminate your rights under
-this License (including any patent licenses granted under the third
-paragraph of section 11).
-
-  However, if you cease all violation of this License, then your
-license from a particular copyright holder is reinstated (a)
-provisionally, unless and until the copyright holder explicitly and
-finally terminates your license, and (b) permanently, if the copyright
-holder fails to notify you of the violation by some reasonable means
-prior to 60 days after the cessation.
-
-  Moreover, your license from a particular copyright holder is
-reinstated permanently if the copyright holder notifies you of the
-violation by some reasonable means, this is the first time you have
-received notice of violation of this License (for any work) from that
-copyright holder, and you cure the violation prior to 30 days after
-your receipt of the notice.
-
-  Termination of your rights under this section does not terminate the
-licenses of parties who have received copies or rights from you under
-this License.  If your rights have been terminated and not permanently
-reinstated, you do not qualify to receive new licenses for the same
-material under section 10.
-
-  9. Acceptance Not Required for Having Copies.
-
-  You are not required to accept this License in order to receive or
-run a copy of the Program.  Ancillary propagation of a covered work
-occurring solely as a consequence of using peer-to-peer transmission
-to receive a copy likewise does not require acceptance.  However,
-nothing other than this License grants you permission to propagate or
-modify any covered work.  These actions infringe copyright if you do
-not accept this License.  Therefore, by modifying or propagating a
-covered work, you indicate your acceptance of this License to do so.
-
-  10. Automatic Licensing of Downstream Recipients.
-
-  Each time you convey a covered work, the recipient automatically
-receives a license from the original licensors, to run, modify and
-propagate that work, subject to this License.  You are not responsible
-for enforcing compliance by third parties with this License.
-
-  An "entity transaction" is a transaction transferring control of an
-organization, or substantially all assets of one, or subdividing an
-organization, or merging organizations.  If propagation of a covered
-work results from an entity transaction, each party to that
-transaction who receives a copy of the work also receives whatever
-licenses to the work the party's predecessor in interest had or could
-give under the previous paragraph, plus a right to possession of the
-Corresponding Source of the work from the predecessor in interest, if
-the predecessor has it or can get it with reasonable efforts.
-
-  You may not impose any further restrictions on the exercise of the
-rights granted or affirmed under this License.  For example, you may
-not impose a license fee, royalty, or other charge for exercise of
-rights granted under this License, and you may not initiate litigation
-(including a cross-claim or counterclaim in a lawsuit) alleging that
-any patent claim is infringed by making, using, selling, offering for
-sale, or importing the Program or any portion of it.
-
-  11. Patents.
-
-  A "contributor" is a copyright holder who authorizes use under this
-License of the Program or a work on which the Program is based.  The
-work thus licensed is called the contributor's "contributor version".
-
-  A contributor's "essential patent claims" are all patent claims
-owned or controlled by the contributor, whether already acquired or
-hereafter acquired, that would be infringed by some manner, permitted
-by this License, of making, using, or selling its contributor version,
-but do not include claims that would be infringed only as a
-consequence of further modification of the contributor version.  For
-purposes of this definition, "control" includes the right to grant
-patent sublicenses in a manner consistent with the requirements of
-this License.
-
-  Each contributor grants you a non-exclusive, worldwide, royalty-free
-patent license under the contributor's essential patent claims, to
-make, use, sell, offer for sale, import and otherwise run, modify and
-propagate the contents of its contributor version.
-
-  In the following three paragraphs, a "patent license" is any express
-agreement or commitment, however denominated, not to enforce a patent
-(such as an express permission to practice a patent or covenant not to
-sue for patent infringement).  To "grant" such a patent license to a
-party means to make such an agreement or commitment not to enforce a
-patent against the party.
-
-  If you convey a covered work, knowingly relying on a patent license,
-and the Corresponding Source of the work is not available for anyone
-to copy, free of charge and under the terms of this License, through a
-publicly available network server or other readily accessible means,
-then you must either (1) cause the Corresponding Source to be so
-available, or (2) arrange to deprive yourself of the benefit of the
-patent license for this particular work, or (3) arrange, in a manner
-consistent with the requirements of this License, to extend the patent
-license to downstream recipients.  "Knowingly relying" means you have
-actual knowledge that, but for the patent license, your conveying the
-covered work in a country, or your recipient's use of the covered work
-in a country, would infringe one or more identifiable patents in that
-country that you have reason to believe are valid.
-
-  If, pursuant to or in connection with a single transaction or
-arrangement, you convey, or propagate by procuring conveyance of, a
-covered work, and grant a patent license to some of the parties
-receiving the covered work authorizing them to use, propagate, modify
-or convey a specific copy of the covered work, then the patent license
-you grant is automatically extended to all recipients of the covered
-work and works based on it.
-
-  A patent license is "discriminatory" if it does not include within
-the scope of its coverage, prohibits the exercise of, or is
-conditioned on the non-exercise of one or more of the rights that are
-specifically granted under this License.  You may not convey a covered
-work if you are a party to an arrangement with a third party that is
-in the business of distributing software, under which you make payment
-to the third party based on the extent of your activity of conveying
-the work, and under which the third party grants, to any of the
-parties who would receive the covered work from you, a discriminatory
-patent license (a) in connection with copies of the covered work
-conveyed by you (or copies made from those copies), or (b) primarily
-for and in connection with specific products or compilations that
-contain the covered work, unless you entered into that arrangement,
-or that patent license was granted, prior to 28 March 2007.
-
-  Nothing in this License shall be construed as excluding or limiting
-any implied license or other defenses to infringement that may
-otherwise be available to you under applicable patent law.
-
-  12. No Surrender of Others' Freedom.
-
-  If conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License.  If you cannot convey a
-covered work so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you may
-not convey it at all.  For example, if you agree to terms that obligate you
-to collect a royalty for further conveying from those to whom you convey
-the Program, the only way you could satisfy both those terms and this
-License would be to refrain entirely from conveying the Program.
-
-  13. Use with the GNU Affero General Public License.
-
-  Notwithstanding any other provision of this License, you have
-permission to link or combine any covered work with a work licensed
-under version 3 of the GNU Affero General Public License into a single
-combined work, and to convey the resulting work.  The terms of this
-License will continue to apply to the part which is the covered work,
-but the special requirements of the GNU Affero General Public License,
-section 13, concerning interaction through a network will apply to the
-combination as such.
-
-  14. Revised Versions of this License.
-
-  The Free Software Foundation may publish revised and/or new versions of
-the GNU General Public License from time to time.  Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-  Each version is given a distinguishing version number.  If the
-Program specifies that a certain numbered version of the GNU General
-Public License "or any later version" applies to it, you have the
-option of following the terms and conditions either of that numbered
-version or of any later version published by the Free Software
-Foundation.  If the Program does not specify a version number of the
-GNU General Public License, you may choose any version ever published
-by the Free Software Foundation.
-
-  If the Program specifies that a proxy can decide which future
-versions of the GNU General Public License can be used, that proxy's
-public statement of acceptance of a version permanently authorizes you
-to choose that version for the Program.
-
-  Later license versions may give you additional or different
-permissions.  However, no additional obligations are imposed on any
-author or copyright holder as a result of your choosing to follow a
-later version.
-
-  15. Disclaimer of Warranty.
-
-  THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
-APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
-HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
-OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
-THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
-PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
-IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
-ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
-
-  16. Limitation of Liability.
-
-  IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
-THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
-GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
-USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
-DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
-PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
-EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
-SUCH DAMAGES.
-
-  17. Interpretation of Sections 15 and 16.
-
-  If the disclaimer of warranty and limitation of liability provided
-above cannot be given local legal effect according to their terms,
-reviewing courts shall apply local law that most closely approximates
-an absolute waiver of all civil liability in connection with the
-Program, unless a warranty or assumption of liability accompanies a
-copy of the Program in return for a fee.
-
-                     END OF TERMS AND CONDITIONS
-
-            How to Apply These Terms to Your New Programs
-
-  If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
-  To do so, attach the following notices to the program.  It is safest
-to attach them to the start of each source file to most effectively
-state the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
-    <one line to give the program's name and a brief idea of what it does.>
-    Copyright (C) <year>  <name of author>
-
-    This program is free software: you can redistribute it and/or modify
-    it under the terms of the GNU General Public License as published by
-    the Free Software Foundation, either version 3 of the License, or
-    (at your option) any later version.
-
-    This program is distributed in the hope that it will be useful,
-    but WITHOUT ANY WARRANTY; without even the implied warranty of
-    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-    GNU General Public License for more details.
-
-    You should have received a copy of the GNU General Public License
-    along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-Also add information on how to contact you by electronic and paper mail.
-
-  If the program does terminal interaction, make it output a short
-notice like this when it starts in an interactive mode:
-
-    <program>  Copyright (C) <year>  <name of author>
-    This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
-    This is free software, and you are welcome to redistribute it
-    under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License.  Of course, your program's commands
-might be different; for a GUI interface, you would use an "about box".
-
-  You should also get your employer (if you work as a programmer) or school,
-if any, to sign a "copyright disclaimer" for the program, if necessary.
-For more information on this, and how to apply and follow the GNU GPL, see
-<http://www.gnu.org/licenses/>.
-
-  The GNU General Public License does not permit incorporating your program
-into proprietary programs.  If your program is a subroutine library, you
-may consider it more useful to permit linking proprietary applications with
-the library.  If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.  But first, please read
-<http://www.gnu.org/philosophy/why-not-lgpl.html>.
diff --git a/Python/MANIFEST.in b/Python/MANIFEST.in
deleted file mode 100644
index 71ad1dd3fe695d51c480ed670804f50fedca6644..0000000000000000000000000000000000000000
--- a/Python/MANIFEST.in
+++ /dev/null
@@ -1,4 +0,0 @@
-include README.md LICENSE
-recursive-include corsika *
-recursive-include scripts *
-recursive-include examples *
diff --git a/Python/Makefile b/Python/Makefile
deleted file mode 100644
index 59a1613fdf9cd1605eb64b675e2e2a82ba85e034..0000000000000000000000000000000000000000
--- a/Python/Makefile
+++ /dev/null
@@ -1,30 +0,0 @@
-##
-# ##
-# corsika
-#
-# @file
-
-# find python3
-PYTHON=`which python3`
-
-# our testing targets
-.PHONY: tests flake black isort all
-
-all: mypy isort black flake tests
-
-tests:
-	${PYTHON} -m pytest --cov=corsika tests
-
-flake:
-	${PYTHON} -m flake8 corsika
-
-black:
-	${PYTHON} -m black -t py37 corsika tests
-
-isort:
-	${PYTHON} -m isort --atomic corsika tests
-
-mypy:
-	${PYTHON} -m mypy corsika
-
-# end
diff --git a/Python/README.md b/Python/README.md
deleted file mode 100644
index 1f421371a2a71ef94d83e5a1cdd7f8bf9ab27f7a..0000000000000000000000000000000000000000
--- a/Python/README.md
+++ /dev/null
@@ -1,56 +0,0 @@
-# Python for CORSIKA8
-
-![License](https://img.shields.io/badge/license-GPL3-blue)
-![Python](https://img.shields.io/badge/python-3.6%20%7C%203.7%20%7C%203.8-blue)
-[![Code style: black](https://img.shields.io/badge/code%20style-black-000000.svg)](https://github.com/psf/black)
-
-## Installation
-
-To install the CORSIKA 8 Python library, you will need Python >= 3.6. The below
-instructions are assuming that `python` refers to Python 3.\*. If `python` still
-refers to Python 2.\*, please replace `python` with `python3` and `pip` with
-`pip3`.
-
-To install this package, change into the `corsika/Python` directory and run
-
-    pip install --user -e .
-
-You can verify that the installation was successful by trying to import `corsika`
-
-    python -c 'import corsika'
-
-## Running the Tests
-    
-If you wish to develop new features in `corsika`, you will also need to install
-some additional dependencies so you can run our unit tests. These can be
-installed with
-
-    pip install --user -e '.[test]'
-
-Once that is completed, you can run the unit tests directory from the `corsika` directory
-
-    python -m pytest tests
-
-## Development Guide
-
-`corsika` is a type Python package with type-checking using
-[Mypy](mypy-lang.org) and all code is formatted with the
-[Black](https://black.readthedocs.io/en/stable/) formatter.
-
-All code must pass these checks, as well as several others, before it can be
-merged into CORSIKA. A `Makefile` is provided to simplify running these in
-tasks.
-
-    make mypy   # this will run the mypy type checker
-    
-    make isort  # this will automatically sort `import` statements
-    
-    make black  # this will automatically format your code
-    
-    make flake  # this checks that all code passes our linting rules
-    
-    make tests  # this runs our test suite
-    
-Lastly, we provide an `all` target that runs *all* of the above in the sequence shown above:
-
-    make all  # run all of the above tests
diff --git a/Python/corsika/__init__.py b/Python/corsika/__init__.py
deleted file mode 100644
index cf4f1519eae59106d31ae10721fa393f8fb51c8e..0000000000000000000000000000000000000000
--- a/Python/corsika/__init__.py
+++ /dev/null
@@ -1,9 +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.
-"""
-
-__version__: str = "8.0.0-alpha"
diff --git a/Python/corsika/py.typed b/Python/corsika/py.typed
deleted file mode 100644
index e69de29bb2d1d6434b8b29ae775ad8c2e48c5391..0000000000000000000000000000000000000000
diff --git a/Python/setup.cfg b/Python/setup.cfg
deleted file mode 100644
index d318235ee166e27b1e147bae61daf5ef95e91f81..0000000000000000000000000000000000000000
--- a/Python/setup.cfg
+++ /dev/null
@@ -1,61 +0,0 @@
-[flake8]
-# use a slightly longer line to be consistent with black
-max-line-length = 88
-
-# E231 is missing whitespace after a comma
-# this contradicts the black formatting rules
-# and therefore creates spurious errors
-ignore = E231
-
-# we set various directories we want to exclude
-exclude =
-    # Don't bother checking in cache directories
-    __pycache__
-
-
-[isort]
-# use parenthesis for multi-line imports
-use_parentheses = true
-
-
-[mypy]
-# the primary Python version
-python_version = 3.7
-
-# allow returning Any
-# this creates excessive errors when using libraries
-# that don't have MyPy typing support
-warn_return_any = False
-
-# don't allow untyped functions
-disallow_untyped_defs = True
-
-# warn if any part of this config is mispelled
-warn_unused_configs = True
-
-# warn for missing type information
-warn_incomplete_stub = True
-
-# warn us if we don't return from a function explicitly
-warn_no_return = True
-
-# use incremental typing to speed things up
-incremental = True
-
-# show error contexts
-show_error_context = True
-
-# and show the column numbers for errors
-show_column_numbers = True
-
-# ignore missing types for setuptools
-[mypy-setuptools.*]
-ignore_missing_imports = True
-
-# ignore missing types for numpy
-[mypy-numpy.*]
-ignore_missing_imports = True
-
-# ignore missing types for matplotlib
-[mypy-matplotlib.*]
-ignore_missing_imports = True
diff --git a/Python/setup.py b/Python/setup.py
deleted file mode 100644
index 36360358f181b35f8c36c548c3778b92436c0c7e..0000000000000000000000000000000000000000
--- a/Python/setup.py
+++ /dev/null
@@ -1,50 +0,0 @@
-from os import path
-from setuptools import setup
-
-# the stereo version
-__version__ = "8.0.0-alpha"
-
-# get the absolute path of this project
-here = path.abspath(path.dirname(__file__))
-
-# Get the long description from the README file
-with open(path.join(here, "README.md"), encoding="utf-8") as f:
-    long_description = f.read()
-
-# the standard setup info
-setup(
-    name="corsika",
-    version=__version__,
-    description="A Python package for working with CORSIKA 8",
-    long_description=long_description,
-    long_description_content_type="text/markdown",
-    url="https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika",
-    author="CORSIKA 8 Collaboration",
-    author_email="corsika-devel@lists.kit.edu",
-    classifiers=[
-        "Development Status :: 3 - Alpha",
-        "Intended Audience :: Science/Research",
-        "Topic :: Scientific/Engineering :: Physics",
-        "Programming Language :: Python :: 3.6",
-        "Programming Language :: Python :: 3.7",
-        "Programming Language :: Python :: 3.8",
-    ],
-    keywords=["cosmic ray", "physics", "astronomy", "simulation"],
-    packages=["corsika"],
-    python_requires=">=3.6*, <4",
-    install_requires=["numpy", "pyyaml",],
-    extras_require={
-        "test": [
-            "pytest",
-            "black",
-            "mypy",
-            "isort",
-            "coverage",
-            "pytest-cov",
-            "flake8",
-        ],
-    },
-    scripts=[],
-    project_urls={"code": "https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika"},
-    include_package_data=True,
-)
diff --git a/Python/tests/test_corsika.py b/Python/tests/test_corsika.py
deleted file mode 100644
index be083394e5448f971558c12316f5a6d26b5a3584..0000000000000000000000000000000000000000
--- a/Python/tests/test_corsika.py
+++ /dev/null
@@ -1,15 +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.
-"""
-import corsika
-
-
-def test_corsika_version() -> None:
-    """
-    Check the current CORSIKA version.
-    """
-    assert corsika.__version__ == "8.0.0-alpha"
diff --git a/README.md b/README.md
index 4837a8f63327332dc31a7093ff6ca770ad94e569..a66a44dc6f2a5c87b4ddb357605c77e0130ecea6 100644
--- a/README.md
+++ b/README.md
@@ -51,37 +51,28 @@ which are very useful also for us.
 
 
 
-## Installation (from source)
+CORSIKA 8 is tested regularly at least on gcc7.3.0 and clang-8.0.0. You will
+also need:
 
-### Prerequisites
-
-CORSIKA 8 is tested regularly via gitlab-CI using recent gcc and clang
-versions.  Additional software prerequisites: cmake, g++, git.
-Furthermore, eigen3, boost, catch2, spdlog are shipped in the
-ThirdParty directory, so an installation on the system is optional.
-Also Pythia 8, CONEX and PROPOSAL are distributed in the ThirdParty
-folder. You may also install those packages on your system and use
-those; we test with Pythia version 8.235.
+- Python 3 (supported versions are Python >= 3.6)
+- cmake 
+- git
 
 On a bare Ubuntu 18.04, just add:
-```
-sudo apt install binutils gfortran make python3 cmake gcc g++ git libz-dev libspdlog-dev libeigen3-dev libboost-iostreams-dev
+``` shell
+sudo apt-get install cmake g++ git
 ```
 
-Furthermore these packages are also reqommended:
-add ```gcc ssh-client less rsync libboost-dev```
+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:
 
-If you work with FreeBSD, run:
+``` shell
+pip install --user conan
 ```
-pkg install git cmake python3 flang eigen
-```
-or add ```boost-libs``` if you want to use the system versions. 
-
-### Compiling
 
-Follow these steps to download and install CORSIKA 8, master development version
-```
-git clone --recursive https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika.git
+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
@@ -116,12 +107,9 @@ make install
 
 Type `make test` to run the unit test suite.
 
-## Running, Examples
-
-There are various examples in the folder `Documentation/Examples`. 
+### Running examples
 
-If you want to see how the first simple hadron cascade develops, 
-see `Documentation/Examples/cascade_example.cc` 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. 
 
 Run the cascade_example with: 
 ```
diff --git a/Setup/CMakeLists.txt b/Setup/CMakeLists.txt
deleted file mode 100644
index 94b1fc731c1cac8abd73e1ca0257254bdd9975f0..0000000000000000000000000000000000000000
--- a/Setup/CMakeLists.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-set (
-  SETUP_HEADERS
-  SetupStack.h
-  SetupEnvironment.h
-  SetupTrajectory.h
-  )
-
-set (
-  SETUP_NAMESPACE
-  corsika/setup
-  )
-
-add_library (CORSIKAsetup INTERFACE)
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAsetup ${SETUP_NAMESPACE} ${SETUP_HEADERS})
-
-target_link_libraries (
-  CORSIKAsetup
-  INTERFACE
-  CORSIKAgeometry
-  NuclearStackExtension
-  GeometryNodeStackExtension
-  CORSIKAhistory
-  )
-
-if (WITH_HISTORY)
-  target_compile_definitions (CORSIKAsetup INTERFACE "WITH_HISTORY")
-endif (WITH_HISTORY)
-
-target_include_directories (
-  CORSIKAsetup
-  INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
-  )
-
-install (
-  FILES ${SETUP_HEADERS} 
-  DESTINATION include/${SETUP_NAMESPACE}
-  )
diff --git a/Setup/SetupEnvironment.h b/Setup/SetupEnvironment.h
deleted file mode 100644
index 87f0c8e305e70b82d657edb16ed23974368f5855..0000000000000000000000000000000000000000
--- a/Setup/SetupEnvironment.h
+++ /dev/null
@@ -1,79 +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/environment/Environment.h>
-#include <corsika/environment/IMagneticFieldModel.h>
-#include <corsika/environment/IMediumModel.h>
-#include <corsika/environment/IMediumPropertyModel.h>
-#include <corsika/environment/IRefractiveIndexModel.h>
-#include <corsika/environment/IMagneticFieldModel.h>
-
-namespace corsika::setup {
-
-  /**
-     Definition of the default environemnt model interface. Each model
-     interface provides properties of the environment in a position
-     bdependent way.
-   */
-
-  using EnvironmentInterface = environment::IMediumPropertyModel<
-      environment::IMagneticFieldModel<environment::IMediumModel>>;
-  using Environment = environment::Environment<EnvironmentInterface>;
-
-} // end namespace corsika::setup
-
-#include <corsika/environment/HomogeneousMedium.h>
-#include <corsika/environment/InhomogeneousMedium.h>
-#include <corsika/environment/MediumPropertyModel.h>
-#include <corsika/environment/UniformMagneticField.h>
-
-/**
- * standard environment for unit testing. This can be moved to
- * "test" directory, when available.
- */
-namespace corsika::setup::testing {
-
-  inline auto setupEnvironment(particles::Code vTargetCode,
-                               const corsika::units::si::MagneticFluxType BfieldZ =
-                                   corsika::units::si::MagneticFluxType::zero()) {
-
-    using namespace corsika::units::si;
-    using namespace corsika;
-
-    auto env = std::make_unique<setup::Environment>();
-    auto& universe = *(env->GetUniverse());
-    const geometry::CoordinateSystem& cs = env->GetCoordinateSystem();
-
-    /**
-     * our world is a sphere at 0,0,0 with R=infty
-     */
-    auto world = setup::Environment::CreateNode<geometry::Sphere>(
-        geometry::Point{cs, 0_m, 0_m, 0_m}, 100_km);
-
-    /**
-     * construct suited environment medium model:
-     */
-    using MyHomogeneousModel =
-        environment::MediumPropertyModel<environment::UniformMagneticField<
-            environment::HomogeneousMedium<setup::EnvironmentInterface>>>;
-
-    world->SetModelProperties<MyHomogeneousModel>(
-        environment::Medium::AirDry1Atm, geometry::Vector(cs, 0_T, 0_T, BfieldZ),
-        1_kg / (1_m * 1_m * 1_m),
-        environment::NuclearComposition(std::vector<particles::Code>{vTargetCode},
-                                        std::vector<float>{1.}));
-
-    auto* nodePtr = world.get();
-    universe.AddChild(std::move(world));
-
-    return std::make_tuple(std::move(env), &cs, nodePtr);
-  }
-
-} // namespace corsika::setup::testing
diff --git a/Setup/SetupStack.h b/Setup/SetupStack.h
deleted file mode 100644
index f06aaf76093c88121e6f5d3f096d958fd5966909..0000000000000000000000000000000000000000
--- a/Setup/SetupStack.h
+++ /dev/null
@@ -1,173 +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/stack/CombinedStack.h>
-#include <corsika/stack/node/GeometryNodeStackExtension.h>
-#include <corsika/stack/nuclear_extension/NuclearStackExtension.h>
-#include <corsika/stack/history/HistorySecondaryProducer.hpp>
-#include <corsika/stack/history/HistoryStackExtension.hpp>
-
-#include <corsika/setup/SetupEnvironment.h>
-
-namespace corsika::setup {
-
-  namespace detail {
-
-    // ------------------------------------------
-    // add geometry node tracking data to stack:
-
-    // the GeometryNode stack needs to know the type of geometry-nodes from the
-    // environment:
-    template <typename TStackIter>
-    using SetupGeometryDataInterface = typename stack::node::MakeGeometryDataInterface<
-        TStackIter, corsika::setup::Environment>::type;
-
-    // combine particle data stack with geometry information for tracking
-    template <typename TStackIter>
-    using StackWithGeometryInterface = corsika::stack::CombinedParticleInterface<
-        stack::nuclear_extension::ParticleDataStack::MPIType, SetupGeometryDataInterface,
-        TStackIter>;
-
-    using StackWithGeometry = corsika::stack::CombinedStack<
-        typename corsika::stack::nuclear_extension::ParticleDataStack::StackImpl,
-        corsika::stack::node::GeometryData<setup::Environment>,
-        StackWithGeometryInterface>;
-
-    // ------------------------------------------
-    // Add [optional] history data to stack, too:
-
-    // combine dummy stack with geometry information for tracking
-    template <typename TStackIter>
-    using StackWithHistoryInterface = corsika::stack::CombinedParticleInterface<
-        StackWithGeometry::MPIType, history::HistoryEventDataInterface, TStackIter>;
-
-    using StackWithHistory =
-        corsika::stack::CombinedStack<typename StackWithGeometry::StackImpl,
-                                      history::HistoryEventData,
-                                      StackWithHistoryInterface>;
-
-  } // namespace detail
-
-  // ---------------------------------------
-  // this is the FINAL stack we use in C8:
-
-#ifdef WITH_HISTORY
-
-  /*
-   * the version with history
-   */
-  using Stack = detail::StackWithHistory;
-  template <typename T1, template <typename> typename M2>
-  using StackViewProducer = corsika::history::HistorySecondaryProducer<T1, M2>;
-
-  namespace detail {
-    /*
-      See Issue 161
-
-      unfortunately clang does not support this in the same way (yet) as
-      gcc, so we have to distinguish here. If clang cataches up, we
-      could remove the clang branch here and also in
-      corsika::Cascade. The gcc code is much more generic and
-      universal. If we could do the gcc version, we won't had to define
-      StackView globally, we could do it with MakeView whereever it is
-      actually needed. Keep an eye on this!
-    */
-#if defined(__clang__)
-    using TheStackView = corsika::stack::SecondaryView<
-        typename corsika::setup::Stack::StackImpl,
-        // CHECK with CLANG: corsika::setup::Stack::MPIType>;
-        corsika::setup::detail::StackWithHistoryInterface, StackViewProducer>;
-#elif defined(__GNUC__) || defined(__GNUG__)
-    using TheStackView =
-        corsika::stack::MakeView<corsika::setup::Stack, StackViewProducer>::type;
-#endif
-  } // namespace detail
-
-#else // WITH_HISTORY
-
-  /*
-   * the version without history
-   */
-  using Stack = detail::StackWithGeometry;
-  template <typename T1, template <typename> typename M2>
-  using StackViewProducer = corsika::stack::DefaultSecondaryProducer<T1, M2>;
-
-  namespace detail {
-    /*
-      See Issue 161
-
-      unfortunately clang does not support this in the same way (yet) as
-      gcc, so we have to distinguish here. If clang cataches up, we
-      could remove the clang branch here and also in
-      corsika::Cascade. The gcc code is much more generic and
-      universal. If we could do the gcc version, we won't had to define
-      StackView globally, we could do it with MakeView whereever it is
-      actually needed. Keep an eye on this!
-    */
-#if defined(__clang__)
-    using TheStackView =
-        corsika::stack::SecondaryView<typename corsika::setup::Stack::StackImpl,
-                                      // CHECK with CLANG:
-                                      // corsika::setup::Stack::MPIType>;
-                                      corsika::setup::detail::StackWithGeometryInterface>;
-#elif defined(__GNUC__) || defined(__GNUG__)
-    using TheStackView = corsika::stack::MakeView<corsika::setup::Stack>::type;
-#endif
-  } // namespace detail
-
-#endif
-
-  // ---------------------------------------
-  // this is the FINAL stackitertor (particle type) we use in C8:
-
-  using StackView = detail::TheStackView;
-
-} // namespace corsika::setup
-
-/**
- * standard stack setup for unit tests. This can be moved to "test"
- * directory, when available.
- */
-
-namespace corsika::setup::testing {
-
-  inline auto setupStack(particles::Code vProjectileType, int vA, int vZ,
-                         units::si::HEPEnergyType vMomentum,
-                         const setup::Environment::BaseNodeType* vNodePtr,
-                         geometry::CoordinateSystem const& cs) {
-
-    using namespace corsika;
-    using namespace corsika::units::si;
-
-    auto stack = std::make_unique<setup::Stack>();
-
-    geometry::Point const origin(cs, {0_m, 0_m, 0_m});
-    corsika::stack::MomentumVector const pLab(cs, {vMomentum, 0_GeV, 0_GeV});
-
-    if (vProjectileType == particles::Code::Nucleus) {
-      auto constexpr mN = corsika::units::constants::nucleonMass;
-      HEPEnergyType const E0 = sqrt(units::static_pow<2>(mN * vA) + pLab.squaredNorm());
-      auto particle = stack->AddParticle(
-          std::make_tuple(particles::Code::Nucleus, E0, pLab, origin, 0_ns, vA, vZ));
-      particle.SetNode(vNodePtr);
-      return std::make_tuple(std::move(stack),
-                             std::make_unique<setup::StackView>(particle));
-    } else { // not a nucleus
-      HEPEnergyType const E0 = sqrt(
-          units::static_pow<2>(particles::GetMass(vProjectileType)) + pLab.squaredNorm());
-      auto particle =
-          stack->AddParticle(std::make_tuple(vProjectileType, E0, pLab, origin, 0_ns));
-      particle.SetNode(vNodePtr);
-      return std::make_tuple(std::move(stack),
-                             std::make_unique<setup::StackView>(particle));
-    }
-  }
-
-} // namespace corsika::setup::testing
diff --git a/Setup/SetupTrajectory.h b/Setup/SetupTrajectory.h
deleted file mode 100644
index 2a42db7a649054b5cbe72ae6590174a4f926320a..0000000000000000000000000000000000000000
--- a/Setup/SetupTrajectory.h
+++ /dev/null
@@ -1,85 +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/geometry/Helix.h>
-#include <corsika/geometry/Line.h>
-#include <corsika/geometry/Trajectory.h>
-
-#include <corsika/process/tracking_line/Tracking.h>
-#include <corsika/process/tracking_leapfrog_curved/Tracking.h>
-#include <corsika/process/tracking_leapfrog_straight/Tracking.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-namespace corsika::setup {
-
-  /**
-    Note/Warning:     Tracking and Trajectory must fit together !
-
-    tracking_leapfrog_curved::Tracking is the result of the Bachelor
-    thesis of Andre Schmidt, KIT. This is a leap-frog algorithm with
-    an analytical, precise calculation of volume intersections. This
-    algorithm needs a LeapFrogTrajectory.
-
-    tracking_leapfrog_straight::Tracking is a more simple and direct
-    leap-frog implementation. The two halve steps are coded explicitly
-    as two straight segments. Intersections with other volumes are
-    calculate only on the straight segments. This algorithm is based
-    on LineTrajectory.
-
-    tracking_line::Tracking is a pure straight tracker. It is based on
-    LineTrajectory.
-   */  
-  typedef corsika::process::tracking_leapfrog_curved::Tracking Tracking;
-  //typedef corsika::process::tracking_leapfrog_straight::Tracking Tracking;
-  //typedef corsika::process::tracking_line::Tracking Tracking;
-
-  /// definition of Trajectory base class, to be used in tracking and cascades
-  //typedef corsika::geometry::LineTrajectory Trajectory;
-  typedef corsika::geometry::LeapFrogTrajectory Trajectory;
-
-  /**
-
-     The following section is for unit testing only. Eventually it should
-     be moved to "tests".
-    
-    
-   */
-  
-  namespace testing {
-
-    template <typename TTrack>
-    TTrack make_track(const corsika::geometry::Line& line,
-                      const corsika::units::si::TimeType tEnd);
-
-    template <>
-    inline corsika::geometry::LineTrajectory
-    make_track<corsika::geometry::LineTrajectory>(
-        const corsika::geometry::Line& line, const corsika::units::si::TimeType tEnd) {
-      return corsika::geometry::LineTrajectory(line, tEnd);
-    }
-
-    template <>
-    inline corsika::geometry::LeapFrogTrajectory
-    make_track<corsika::geometry::LeapFrogTrajectory>(
-        const corsika::geometry::Line& line, const corsika::units::si::TimeType tEnd) {
-      using namespace corsika::units::si;
-      typedef corsika::geometry::Vector<magnetic_flux_density_d> MagneticFieldVector;
-
-      auto const k = square(0_m) / (square(1_s) * 1_V);
-      return corsika::geometry::LeapFrogTrajectory(
-          line.GetR0(), line.GetV0(),
-          MagneticFieldVector{line.GetR0().GetCoordinateSystem(), 0_T, 0_T, 0_T}, k,
-          tEnd);
-    }
-
-  } // namespace testing
-
-} // namespace corsika::setup
diff --git a/Stack/CMakeLists.txt b/Stack/CMakeLists.txt
deleted file mode 100644
index ae5ee2acaabd768f8ac536e95a5706cdaae629cd..0000000000000000000000000000000000000000
--- a/Stack/CMakeLists.txt
+++ /dev/null
@@ -1,5 +0,0 @@
-add_subdirectory (DummyStack)
-add_subdirectory (SuperStupidStack)
-add_subdirectory (NuclearStackExtension)
-add_subdirectory (GeometryNodeStackExtension)
-add_subdirectory (History)
diff --git a/Stack/DummyStack/CMakeLists.txt b/Stack/DummyStack/CMakeLists.txt
deleted file mode 100644
index b7e09a9e49706079719e48c7a043ca4fa40404a7..0000000000000000000000000000000000000000
--- a/Stack/DummyStack/CMakeLists.txt
+++ /dev/null
@@ -1,37 +0,0 @@
-set (DummyStack_HEADERS DummyStack.h)
-set (DummyStack_NAMESPACE corsika/stack/dummy)
-
-add_library (DummyStack INTERFACE)
-
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (DummyStack ${DummyStack_NAMESPACE} ${DummyStack_HEADERS})
-
-target_link_libraries (
-  DummyStack
-  INTERFACE
-  CORSIKAstackinterface
-  CORSIKAunits
-  CORSIKAparticles
-  )
-
-target_include_directories (
-  DummyStack
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include>
-  )
-
-install (
-  FILES
-  ${DummyStack_HEADERS}
-  DESTINATION
-  include/${DummyStack_NAMESPACE}
-  )
-
-# ----------------
-# code unit testing
- CORSIKA_ADD_TEST(testDummyStack)
- target_link_libraries (
-   testDummyStack
-   DummyStack
-   CORSIKAtesting
-   )
diff --git a/Stack/DummyStack/DummyStack.h b/Stack/DummyStack/DummyStack.h
deleted file mode 100644
index 26270b51cc0f8b9202ccdcba6dc268e6b29a3198..0000000000000000000000000000000000000000
--- a/Stack/DummyStack/DummyStack.h
+++ /dev/null
@@ -1,87 +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/logging/Logging.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/stack/Stack.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <string>
-#include <tuple>
-
-namespace corsika::stack {
-
-  namespace dummy {
-
-    /**
-     * Example of a particle object on the stack, with NO DATA.
-     */
-
-    /**
-       however, conceptually we need to provide fake data. A stack without data does not
-       work...
-     */
-
-    struct NoData { /* nothing */
-      int nothing = 0;
-    };
-
-    template <typename StackIteratorInterface>
-    class ParticleInterface
-        : public corsika::stack::ParticleBase<StackIteratorInterface> {
-
-    protected:
-      using corsika::stack::ParticleBase<StackIteratorInterface>::GetStack;
-      using corsika::stack::ParticleBase<StackIteratorInterface>::GetStackData;
-
-    public:
-      using corsika::stack::ParticleBase<StackIteratorInterface>::GetIndex;
-
-    public:
-      void SetParticleData(const std::tuple<NoData>& /*v*/) {}
-      void SetParticleData(ParticleInterface<StackIteratorInterface>& /*parent*/,
-                           const std::tuple<NoData>& /*v*/) {}
-
-      std::string as_string() const { return "dummy-data"; }
-    };
-
-    /**
-     *
-     * Memory implementation of the most simple (no-data) particle stack object.
-     */
-
-    class DummyStackImpl {
-
-    public:
-      void Init() { entries_ = 0; }
-
-      void Clear() { entries_ = 0; }
-
-      int GetSize() const { return entries_; }
-      int GetCapacity() const { return entries_; }
-
-      /**
-       *   Function to copy particle at location i2 in stack to i1
-       */
-      void Copy(const int /*i1*/, const int /*i2*/) {}
-
-      void IncrementSize() { entries_++; }
-      void DecrementSize() { entries_--; }
-
-    private:
-      int entries_ = 0;
-
-    }; // end class DummyStackImpl
-
-    typedef Stack<DummyStackImpl, ParticleInterface> DummyStack;
-
-  } // namespace dummy
-
-} // namespace corsika::stack
diff --git a/Stack/GeometryNodeStackExtension/CMakeLists.txt b/Stack/GeometryNodeStackExtension/CMakeLists.txt
deleted file mode 100644
index 8d7a3c7d06606e871892cf99b9bc37a7b9a8bbad..0000000000000000000000000000000000000000
--- a/Stack/GeometryNodeStackExtension/CMakeLists.txt
+++ /dev/null
@@ -1,38 +0,0 @@
-set (GeometryNodeStackExtension_HEADERS GeometryNodeStackExtension.h)
-set (GeometryNodeStackExtension_NAMESPACE corsika/stack/node)
-
-add_library (GeometryNodeStackExtension INTERFACE)
-
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (GeometryNodeStackExtension ${GeometryNodeStackExtension_NAMESPACE} ${GeometryNodeStackExtension_HEADERS})
-
-target_link_libraries (
-  GeometryNodeStackExtension
-  INTERFACE
-  CORSIKAstackinterface
-  CORSIKAunits
-  CORSIKAparticles
-  CORSIKAgeometry
-  )
-
-target_include_directories (
-  GeometryNodeStackExtension
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include>
-  )
-
-install (
-  FILES
-  ${GeometryNodeStackExtension_HEADERS}
-  DESTINATION
-  include/${GeometryNodeStackExtension_NAMESPACE}
-  )
-
-# ----------------
-# code unit testing
- CORSIKA_ADD_TEST(testGeometryNodeStackExtension)
- target_link_libraries (
-   testGeometryNodeStackExtension
-   GeometryNodeStackExtension
-   CORSIKAtesting
-   )
diff --git a/Stack/GeometryNodeStackExtension/GeometryNodeStackExtension.h b/Stack/GeometryNodeStackExtension/GeometryNodeStackExtension.h
deleted file mode 100644
index d33997b8eecddca3d53be9fd4f1f7b1b95ad4835..0000000000000000000000000000000000000000
--- a/Stack/GeometryNodeStackExtension/GeometryNodeStackExtension.h
+++ /dev/null
@@ -1,99 +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/logging/Logging.h>
-#include <corsika/stack/Stack.h>
-
-#include <tuple>
-#include <utility>
-#include <vector>
-
-namespace corsika::stack::node {
-
-  /**
-   * @class GeometryDataInterface
-   *
-   * corresponding defintion of a stack-readout object, the iteractor
-   * dereference operator will deliver access to these function
-  // defintion of a stack-readout object, the iteractor dereference
-  // operator will deliver access to these function
-   */
-  template <typename T, typename TEnvType>
-  class GeometryDataInterface : public T {
-
-  protected:
-    using T::GetStack;
-    using T::GetStackData;
-
-  public:
-    using T::GetIndex;
-    using BaseNodeType = typename TEnvType::BaseNodeType;
-
-  public:
-    // default version for particle-creation from input data
-    void SetParticleData(const std::tuple<BaseNodeType const*> v) {
-      SetNode(std::get<0>(v));
-    }
-    void SetParticleData(GeometryDataInterface& parent,
-                         const std::tuple<BaseNodeType const*>) {
-      SetNode(parent.GetNode()); // copy Node from parent particle!
-    }
-    void SetParticleData() { SetNode(nullptr); }
-    void SetParticleData(GeometryDataInterface& parent) {
-      SetNode(parent.GetNode()); // copy Node from parent particle!
-    }
-
-    std::string as_string() const { return fmt::format("node={}", fmt::ptr(GetNode())); }
-
-    void SetNode(BaseNodeType const* v) { GetStackData().SetNode(GetIndex(), v); }
-    BaseNodeType const* GetNode() const { return GetStackData().GetNode(GetIndex()); }
-  };
-
-  // definition of stack-data object to store geometry information
-  template <typename TEnvType>
-
-  /**
-   * @class GeometryData
-   *
-   * definition of stack-data object to store geometry information
-   */
-  class GeometryData {
-
-  public:
-    using BaseNodeType = typename TEnvType::BaseNodeType;
-
-    // these functions are needed for the Stack interface
-    void Clear() { fNode.clear(); }
-    unsigned int GetSize() const { return fNode.size(); }
-    unsigned int GetCapacity() const { return fNode.size(); }
-    void Copy(const int i1, const int i2) { fNode[i2] = fNode[i1]; }
-    void Swap(const int i1, const int i2) { std::swap(fNode[i1], fNode[i2]); }
-
-    // custom data access function
-    void SetNode(const int i, BaseNodeType const* v) { fNode[i] = v; }
-    BaseNodeType const* GetNode(const int i) const { return fNode[i]; }
-
-    // these functions are also needed by the Stack interface
-    void IncrementSize() { fNode.push_back(nullptr); }
-    void DecrementSize() {
-      if (fNode.size() > 0) { fNode.pop_back(); }
-    }
-
-    // custom private data section
-  private:
-    std::vector<const BaseNodeType*> fNode;
-  };
-
-  template <typename T, typename TEnv>
-  struct MakeGeometryDataInterface {
-    typedef GeometryDataInterface<T, TEnv> type;
-  };
-
-} // namespace corsika::stack::node
diff --git a/Stack/History/HistoryObservationPlane.cpp b/Stack/History/HistoryObservationPlane.cpp
deleted file mode 100644
index 6a8c94ec7d9e33686383dadf479ef56e413d5522..0000000000000000000000000000000000000000
--- a/Stack/History/HistoryObservationPlane.cpp
+++ /dev/null
@@ -1,85 +0,0 @@
-/*
- * (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.
- */
-
-#include <corsika/logging/Logging.h>
-#include <corsika/stack/history/HistoryObservationPlane.hpp>
-
-#include <boost/histogram/ostream.hpp>
-
-#include <iomanip>
-#include <iostream>
-
-using namespace corsika::units::si;
-using namespace corsika::history;
-using namespace corsika;
-
-HistoryObservationPlane::HistoryObservationPlane(setup::Stack const& stack,
-                                                 geometry::Plane const& obsPlane,
-                                                 bool deleteOnHit)
-    : stack_{stack}
-    , plane_{obsPlane}
-    , deleteOnHit_{deleteOnHit} {}
-
-corsika::process::EProcessReturn HistoryObservationPlane::DoContinuous(
-    setup::Stack::ParticleType const& particle, setup::Trajectory const& trajectory) {
-  TimeType const timeOfIntersection =
-      (plane_.GetCenter() - trajectory.GetLine().GetR0()).dot(plane_.GetNormal()) /
-      trajectory.GetLine().GetV0().dot(plane_.GetNormal());
-
-  if (timeOfIntersection < TimeType::zero()) { return process::EProcessReturn::eOk; }
-
-  if (plane_.IsAbove(trajectory.GetLine().GetR0()) ==
-      plane_.IsAbove(trajectory.GetPosition(1))) {
-    return process::EProcessReturn::eOk;
-  }
-
-  C8LOG_DEBUG(fmt::format("HistoryObservationPlane: Particle detected: pid={}",
-                          particle.GetPID()));
-
-  auto const pid = particle.GetPID();
-  if (particles::IsMuon(pid)) { fillHistoryHistogram(particle); }
-
-  if (deleteOnHit_) {
-    return process::EProcessReturn::eParticleAbsorbed;
-  } else {
-    return process::EProcessReturn::eOk;
-  }
-}
-
-LengthType HistoryObservationPlane::MaxStepLength(setup::Stack::ParticleType const&,
-                                                  setup::Trajectory const& trajectory) {
-  TimeType const timeOfIntersection =
-      (plane_.GetCenter() - trajectory.GetLine().GetR0()).dot(plane_.GetNormal()) /
-      trajectory.GetLine().GetV0().dot(plane_.GetNormal());
-
-  if (timeOfIntersection < TimeType::zero()) {
-    return std::numeric_limits<double>::infinity() * 1_m;
-  }
-
-  auto const pointOfIntersection = trajectory.GetLine().GetPosition(timeOfIntersection);
-  return (trajectory.GetLine().GetR0() - pointOfIntersection).norm() * 1.0001;
-}
-
-void HistoryObservationPlane::fillHistoryHistogram(
-    setup::Stack::ParticleType const& muon) {
-  double const muon_energy = muon.GetEnergy() / 1_GeV;
-
-  int genctr{0};
-  Event const* event = muon.GetEvent().get();
-  while (event) {
-    auto const projectile = stack_.cfirst() + event->projectileIndex();
-    if (event->eventType() == EventType::Interaction) {
-      genctr++;
-      double const projEnergy = projectile.GetEnergy() / 1_GeV;
-      int const pdg = static_cast<int>(particles::GetPDG(projectile.GetPID()));
-
-      histogram_(muon_energy, projEnergy, pdg);
-    }
-    event = event->parentEvent().get(); // projectile.GetEvent().get();
-  }
-}
diff --git a/Stack/History/HistoryObservationPlane.hpp b/Stack/History/HistoryObservationPlane.hpp
deleted file mode 100644
index fb67ca7f9b8897cb6d6f2b11c6ce02101a5ea653..0000000000000000000000000000000000000000
--- a/Stack/History/HistoryObservationPlane.hpp
+++ /dev/null
@@ -1,60 +0,0 @@
-/*
- * (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 <corsika/geometry/Plane.h>
-#include <corsika/process/ContinuousProcess.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <boost/histogram.hpp>
-
-#include <functional>
-
-namespace corsika::history {
-  namespace detail {
-    inline auto hist_factory() {
-      namespace bh = boost::histogram;
-      namespace bha = bh::axis;
-      auto h = bh::make_histogram(
-          bha::regular<float, bha::transform::log>{11 * 5, 1e0, 1e11, "muon energy"},
-          bha::regular<float, bha::transform::log>{11 * 5, 1e0, 1e11,
-                                                   "projectile energy"},
-          bha::category<int, bh::use_default, bha::option::growth_t>{
-              {211, -211, 2212, -2212}, "projectile PDG"});
-      return h;
-    }
-  } // namespace detail
-
-  class HistoryObservationPlane
-      : public corsika::process::ContinuousProcess<HistoryObservationPlane> {
-  public:
-    HistoryObservationPlane(setup::Stack const&, geometry::Plane const&, bool = true);
-
-    corsika::units::si::LengthType MaxStepLength(
-        corsika::setup::Stack::ParticleType const&,
-        corsika::setup::Trajectory const& vTrajectory);
-
-    corsika::process::EProcessReturn DoContinuous(
-        corsika::setup::Stack::ParticleType const& vParticle,
-        corsika::setup::Trajectory const& vTrajectory);
-
-    auto const& histogram() const { return histogram_; }
-
-  private:
-    void fillHistoryHistogram(setup::Stack::ParticleType const&);
-
-    setup::Stack const& stack_;
-    geometry::Plane const plane_;
-    bool const deleteOnHit_;
-
-    decltype(detail::hist_factory()) histogram_ = detail::hist_factory();
-  };
-} // namespace corsika::history
diff --git a/Stack/History/testHistoryView.cc b/Stack/History/testHistoryView.cc
deleted file mode 100644
index 588537198209b498af7358e27838fc3540c30590..0000000000000000000000000000000000000000
--- a/Stack/History/testHistoryView.cc
+++ /dev/null
@@ -1,221 +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/stack/history/Event.hpp>
-#include <corsika/stack/history/HistorySecondaryProducer.hpp>
-#include <corsika/stack/history/HistoryStackExtension.hpp>
-
-#include <corsika/stack/CombinedStack.h>
-#include <corsika/stack/dummy/DummyStack.h>
-#include <corsika/stack/nuclear_extension/NuclearStackExtension.h>
-
-#include <corsika/logging/Logging.h>
-
-#include <catch2/catch.hpp>
-
-using namespace corsika;
-using namespace corsika::geometry;
-using namespace corsika::units::si;
-
-/**
-   Need to replicate setup::SetupStack in a maximally simplified
-   way, but with real particle data
- */
-
-// combine dummy stack with geometry information for tracking
-template <typename TStackIter>
-using StackWithHistoryInterface = corsika::stack::CombinedParticleInterface<
-    stack::nuclear_extension::ParticleDataStack::MPIType,
-    history::HistoryEventDataInterface, TStackIter>;
-
-using TestStack = corsika::stack::CombinedStack<
-    typename stack::nuclear_extension::ParticleDataStack::StackImpl,
-    history::HistoryEventData, StackWithHistoryInterface>;
-
-/*
-    See Issue 161
-
-    unfortunately clang does not support this in the same way (yet) as
-    gcc, so we have to distinguish here. If clang cataches up, we
-    could remove the clang branch here and also in
-    corsika::Cascade. The gcc code is much more generic and
-    universal. If we could do the gcc version, we won't had to define
-    StackView globally, we could do it with MakeView whereever it is
-    actually needed. Keep an eye on this!
-  */
-#if defined(__clang__)
-using TheTestStackView = corsika::stack::SecondaryView<typename TestStack::StackImpl,
-                                                       StackWithHistoryInterface,
-                                                       history::HistorySecondaryProducer>;
-#elif defined(__GNUC__) || defined(__GNUG__)
-using TheTestStackView =
-    corsika::stack::MakeView<TestStack, history::HistorySecondaryProducer>::type;
-#endif
-
-using TestStackView = TheTestStackView;
-
-template <typename Event>
-int count_generations(Event const* event) {
-  int genCounter = 0;
-  while (event) {
-    event = event->parentEvent().get();
-    genCounter++;
-  }
-
-  return genCounter;
-}
-
-TEST_CASE("HistoryStackExtension", "[stack]") {
-
-  logging::SetLevel(logging::level::debug);
-
-  geometry::CoordinateSystem& dummyCS =
-      geometry::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-  // in this test we only use one singel stack !
-  TestStack stack;
-
-  // add primary particle
-  auto p0 = stack.AddParticle(
-      std::make_tuple(particles::Code::Electron, 1.5_GeV,
-                      corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                      Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
-
-  CHECK(stack.getEntries() == 1);
-  corsika::history::EventPtr evt = p0.GetEvent();
-  CHECK(evt == nullptr);
-  CHECK(count_generations(evt.get()) == 0);
-
-  SECTION("interface test, view") {
-
-    // add secondaries, 1st generation
-    TestStackView hview0(p0);
-
-    auto const ev0 = p0.GetEvent();
-    CHECK(ev0 == nullptr);
-
-    C8LOG_DEBUG("loop VIEW");
-
-    // add 5 secondaries
-    for (int i = 0; i < 5; ++i) {
-      auto sec = hview0.AddSecondary(
-          std::make_tuple(particles::Code::Electron, 1.5_GeV,
-                          corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                          Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
-
-      CHECK(sec.GetParentEventIndex() == i);
-      CHECK(sec.GetEvent() != nullptr);
-      CHECK(sec.GetEvent()->parentEvent() == nullptr);
-      CHECK(count_generations(sec.GetEvent().get()) == 1);
-    }
-
-    // read 1st genertion particle particle
-    auto p1 = stack.GetNextParticle();
-    CHECK(count_generations(p1.GetEvent().get()) == 1);
-
-    TestStackView hview1(p1);
-
-    auto const ev1 = p1.GetEvent();
-
-    // add second generation of secondaries
-    // add 10 secondaries
-    for (int i = 0; i < 10; ++i) {
-      auto sec = hview1.AddSecondary(
-          std::make_tuple(particles::Code::Electron, 1.5_GeV,
-                          corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                          Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
-
-      CHECK(sec.GetParentEventIndex() == i);
-      CHECK(sec.GetEvent()->parentEvent() == ev1);
-      CHECK(sec.GetEvent()->parentEvent()->parentEvent() == ev0);
-
-      CHECK(count_generations(sec.GetEvent().get()) == 2);
-
-      const auto org_projectile = stack.at(sec.GetEvent()->projectileIndex());
-      CHECK(org_projectile.GetEvent() == sec.GetEvent()->parentEvent());
-    }
-
-    // read 2nd genertion particle particle
-    auto p2 = stack.GetNextParticle();
-
-    TestStackView hview2(p2);
-
-    auto const ev2 = p2.GetEvent();
-
-    // add third generation of secondaries
-    // add 15 secondaries
-    for (int i = 0; i < 15; ++i) {
-      C8LOG_TRACE("loop, view: " + std::to_string(i));
-
-      auto sec = hview2.AddSecondary(
-          std::make_tuple(particles::Code::Electron, 1.5_GeV,
-                          corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                          Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
-      C8LOG_TRACE("loop, ---- ");
-
-      CHECK(sec.GetParentEventIndex() == i);
-      CHECK(sec.GetEvent()->parentEvent() == ev2);
-      CHECK(sec.GetEvent()->parentEvent()->parentEvent() == ev1);
-      CHECK(sec.GetEvent()->parentEvent()->parentEvent()->parentEvent() == ev0);
-
-      CHECK(count_generations(sec.GetEvent().get()) == 3);
-    }
-  }
-
-  SECTION("also test projectile access") {
-
-    C8LOG_TRACE("projectile test");
-
-    // add secondaries, 1st generation
-    TestStackView hview0(p0);
-    auto proj0 = hview0.GetProjectile();
-    auto const ev0 = p0.GetEvent();
-    CHECK(ev0 == nullptr);
-
-    C8LOG_TRACE("loop");
-
-    // add 5 secondaries
-    for (int i = 0; i < 5; ++i) {
-      C8LOG_TRACE("loop " + std::to_string(i));
-      auto sec = proj0.AddSecondary(
-          std::make_tuple(particles::Code::Electron, 1.5_GeV,
-                          corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                          Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
-
-      CHECK(sec.GetParentEventIndex() == i);
-      CHECK(sec.GetEvent() != nullptr);
-      CHECK(sec.GetEvent()->parentEvent() == nullptr);
-    }
-    CHECK(stack.getEntries() == 6);
-
-    // read 1st genertion particle particle
-    auto p1 = stack.GetNextParticle();
-
-    TestStackView hview1(p1);
-    auto proj1 = hview1.GetProjectile();
-    auto const ev1 = p1.GetEvent();
-
-    // add second generation of secondaries
-    // add 10 secondaries
-    for (int i = 0; i < 10; ++i) {
-      auto sec = proj1.AddSecondary(
-          std::make_tuple(particles::Code::Electron, 1.5_GeV,
-                          corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                          Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
-
-      CHECK(sec.GetParentEventIndex() == i);
-      CHECK(sec.GetEvent()->parentEvent() == ev1);
-      CHECK(sec.GetEvent()->secondaries().size() == i + 1);
-      CHECK(sec.GetEvent()->parentEvent()->parentEvent() == ev0);
-
-      const auto org_projectile = stack.at(sec.GetEvent()->projectileIndex());
-      CHECK(org_projectile.GetEvent() == sec.GetEvent()->parentEvent());
-    }
-    CHECK(stack.getEntries() == 16);
-  }
-}
diff --git a/Stack/NuclearStackExtension/CMakeLists.txt b/Stack/NuclearStackExtension/CMakeLists.txt
deleted file mode 100644
index 3e3c5e735317dbc76f9003a9a3d928df8f40a720..0000000000000000000000000000000000000000
--- a/Stack/NuclearStackExtension/CMakeLists.txt
+++ /dev/null
@@ -1,42 +0,0 @@
-set (NuclearStackExtension_HEADERS NuclearStackExtension.h)
-set (NuclearStackExtension_NAMESPACE corsika/stack/nuclear_extension)
-
-add_library (NuclearStackExtension INTERFACE)
-
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (NuclearStackExtension ${NuclearStackExtension_NAMESPACE} ${NuclearStackExtension_HEADERS})
-
-target_link_libraries (
-  NuclearStackExtension
-  INTERFACE
-  CORSIKAstackinterface
-  CORSIKAunits
-  CORSIKAparticles
-  CORSIKAgeometry
-  SuperStupidStack
-  )
-
-target_include_directories (
-  NuclearStackExtension
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include>
-  )
-
-install (
-  FILES
-  ${NuclearStackExtension_HEADERS}
-  DESTINATION
-  include/${NuclearStackExtension_NAMESPACE}
-  )
-
-# ----------------
-# code unit testing
-CORSIKA_ADD_TEST(testNuclearStackExtension)
-target_link_libraries (
-  testNuclearStackExtension
-  NuclearStackExtension
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAunits
-  CORSIKAtesting
-  )
diff --git a/Stack/NuclearStackExtension/NuclearStackExtension.h b/Stack/NuclearStackExtension/NuclearStackExtension.h
deleted file mode 100644
index c2ac33e2e0f25079dbd535ed52360bbb0c1e6244..0000000000000000000000000000000000000000
--- a/Stack/NuclearStackExtension/NuclearStackExtension.h
+++ /dev/null
@@ -1,363 +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/particles/ParticleProperties.h>
-
-#include <corsika/stack/Stack.h>
-#include <corsika/stack/super_stupid/SuperStupidStack.h>
-
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/Vector.h>
-
-#include <corsika/logging/Logging.h>
-
-#include <algorithm>
-#include <tuple>
-#include <vector>
-
-namespace corsika::stack {
-
-  /**
-   * @namespace nuclear_extension
-   *
-   * Add A and Z data to existing stack (currently SuperStupidStack) of particle
-   * properties. This is done via inheritance, not via CombinedStack since the nuclear
-   * data is stored ONLY when needed (for nuclei) and not for all particles. Thus, this is
-   * a new, derived Stack object.
-   *
-   * Only for Code::Nucleus particles A and Z are stored, not for all
-   * normal elementary particles.
-   *
-   * Thus in your code, make sure to always check <code>
-   * particle.GetPID()==Code::Nucleus </code> before attempting to
-   * read any nuclear information.
-   *
-   *
-   */
-
-  typedef corsika::geometry::Vector<corsika::units::si::hepmomentum_d> MomentumVector;
-
-  namespace nuclear_extension {
-
-    /**
-     * @class NuclearParticleInterface
-     *
-     * Define ParticleInterface for NuclearStackExtension Stack derived from
-     * ParticleInterface of Inner stack class
-     */
-    template <template <typename> typename InnerParticleInterface,
-              typename StackIteratorInterface>
-    class NuclearParticleInterface
-        : public InnerParticleInterface<StackIteratorInterface> {
-
-    protected:
-      using InnerParticleInterface<StackIteratorInterface>::GetStackData;
-      using InnerParticleInterface<StackIteratorInterface>::GetIndex;
-      using InnerParticleInterface<StackIteratorInterface>::as_string;
-
-    public:
-      void SetParticleData(
-          const std::tuple<corsika::particles::Code, corsika::units::si::HEPEnergyType,
-                           corsika::stack::MomentumVector, corsika::geometry::Point,
-                           corsika::units::si::TimeType>& v) {
-        if (std::get<0>(v) == corsika::particles::Code::Nucleus) {
-          std::ostringstream err;
-          err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
-          throw std::runtime_error(err.str());
-        }
-        InnerParticleInterface<StackIteratorInterface>::SetParticleData(v);
-        SetNucleusRef(-1); // this is not a nucleus
-      }
-
-      void SetParticleData(
-          const std::tuple<corsika::particles::Code, corsika::units::si::HEPEnergyType,
-                           corsika::stack::MomentumVector, corsika::geometry::Point,
-                           corsika::units::si::TimeType, unsigned short, unsigned short>&
-              v) {
-        const unsigned short A = std::get<5>(v);
-        const unsigned short Z = std::get<6>(v);
-        if (std::get<0>(v) != corsika::particles::Code::Nucleus || A == 0 || Z == 0) {
-          std::ostringstream err;
-          err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
-          throw std::runtime_error(err.str());
-        }
-        SetNucleusRef(GetStackData().GetNucleusNextRef()); // store this nucleus data ref
-        SetNuclearA(A);
-        SetNuclearZ(Z);
-        InnerParticleInterface<StackIteratorInterface>::SetParticleData(
-            std::tuple<corsika::particles::Code, corsika::units::si::HEPEnergyType,
-                       corsika::stack::MomentumVector, corsika::geometry::Point,
-                       corsika::units::si::TimeType>{std::get<0>(v), std::get<1>(v),
-                                                     std::get<2>(v), std::get<3>(v),
-                                                     std::get<4>(v)});
-      }
-
-      void SetParticleData(
-          InnerParticleInterface<StackIteratorInterface>& p,
-          const std::tuple<corsika::particles::Code, corsika::units::si::HEPEnergyType,
-                           corsika::stack::MomentumVector, corsika::geometry::Point,
-                           corsika::units::si::TimeType>& v) {
-        if (std::get<0>(v) == corsika::particles::Code::Nucleus) {
-          std::ostringstream err;
-          err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
-          throw std::runtime_error(err.str());
-        }
-        InnerParticleInterface<StackIteratorInterface>::SetParticleData(
-            p, std::tuple<corsika::particles::Code, corsika::units::si::HEPEnergyType,
-                          corsika::stack::MomentumVector, corsika::geometry::Point,
-                          corsika::units::si::TimeType>{std::get<0>(v), std::get<1>(v),
-                                                        std::get<2>(v), std::get<3>(v),
-                                                        std::get<4>(v)});
-        SetNucleusRef(-1); // this is not a nucleus
-      }
-
-      void SetParticleData(
-          InnerParticleInterface<StackIteratorInterface>& p,
-          const std::tuple<corsika::particles::Code, corsika::units::si::HEPEnergyType,
-                           corsika::stack::MomentumVector, corsika::geometry::Point,
-                           corsika::units::si::TimeType, unsigned short, unsigned short>&
-              v) {
-        const unsigned short A = std::get<5>(v);
-        const unsigned short Z = std::get<6>(v);
-        if (std::get<0>(v) != corsika::particles::Code::Nucleus || A == 0 || Z == 0) {
-          std::ostringstream err;
-          err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
-          throw std::runtime_error(err.str());
-        }
-        SetNucleusRef(GetStackData().GetNucleusNextRef()); // store this nucleus data ref
-        SetNuclearA(A);
-        SetNuclearZ(Z);
-        InnerParticleInterface<StackIteratorInterface>::SetParticleData(
-            p, std::tuple<corsika::particles::Code, corsika::units::si::HEPEnergyType,
-                          corsika::stack::MomentumVector, corsika::geometry::Point,
-                          corsika::units::si::TimeType>{std::get<0>(v), std::get<1>(v),
-                                                        std::get<2>(v), std::get<3>(v),
-                                                        std::get<4>(v)});
-      }
-
-      std::string as_string() const {
-        return fmt::format(
-            "{}, nuc({})", InnerParticleInterface<StackIteratorInterface>::as_string(),
-            (isNucleus() ? fmt::format("A={}, Z={}", GetNuclearA(), GetNuclearZ())
-                         : "n/a"));
-      }
-
-      /**
-       * @name individual setters
-       * @{
-       */
-      void SetNuclearA(const unsigned short vA) {
-        GetStackData().SetNuclearA(GetIndex(), vA);
-      }
-      void SetNuclearZ(const unsigned short vZ) {
-        GetStackData().SetNuclearZ(GetIndex(), vZ);
-      }
-      /// @}
-
-      /**
-       * @name individual getters
-       * @{
-       */
-      int GetNuclearA() const { return GetStackData().GetNuclearA(GetIndex()); }
-      int GetNuclearZ() const { return GetStackData().GetNuclearZ(GetIndex()); }
-      /// @}
-
-      /**
-       * Overwrite normal GetParticleMass function with nuclear version
-       */
-      corsika::units::si::HEPMassType GetMass() const {
-        if (InnerParticleInterface<StackIteratorInterface>::GetPID() ==
-            corsika::particles::Code::Nucleus)
-          return corsika::particles::GetNucleusMass(GetNuclearA(), GetNuclearZ());
-        return InnerParticleInterface<StackIteratorInterface>::GetMass();
-      }
-      /**
-       * Overwirte normal GetChargeNumber function with nuclear version
-       **/
-      int16_t GetChargeNumber() const {
-        if (InnerParticleInterface<StackIteratorInterface>::GetPID() ==
-            corsika::particles::Code::Nucleus)
-          return GetNuclearZ();
-        return InnerParticleInterface<StackIteratorInterface>::GetChargeNumber();
-      }
-
-      int GetNucleusRef() const {
-        return GetStackData().GetNucleusRef(GetIndex());
-      } // LCOV_EXCL_LINE
-
-    protected:
-      void SetNucleusRef(const int vR) { GetStackData().SetNucleusRef(GetIndex(), vR); }
-      bool isNucleus() const { return GetStackData().isNucleus(GetIndex()); }
-    };
-
-    /**
-     * @class NuclearStackExtension
-     *
-     * Memory implementation of adding nuclear inforamtion to the
-     * existing particle stack defined in class InnerStackImpl.
-     *
-     * Inside the NuclearStackExtension class there is a dedicated
-     * fNucleusRef index, where fNucleusRef[i] is referring to the
-     * correct A and Z for a specific particle index i. fNucleusRef[i]
-     * == -1 means that this is not a nucleus, and a subsequent call to
-     * GetNucleusA would produce an exception.
-     */
-    template <typename InnerStackImpl>
-    class NuclearStackExtensionImpl : public InnerStackImpl {
-
-    public:
-      void Init() { InnerStackImpl::Init(); }
-      void Dump() { InnerStackImpl::Dump(); }
-
-      void Clear() {
-        InnerStackImpl::Clear();
-        fNucleusRef.clear();
-        fNuclearA.clear();
-        fNuclearZ.clear();
-      }
-
-      unsigned int GetSize() const { return fNucleusRef.size(); }
-      unsigned int GetCapacity() const { return fNucleusRef.capacity(); }
-
-      void SetNuclearA(const unsigned int i, const unsigned short vA) {
-        fNuclearA[GetNucleusRef(i)] = vA;
-      }
-      void SetNuclearZ(const unsigned int i, const unsigned short vZ) {
-        fNuclearZ[GetNucleusRef(i)] = vZ;
-      }
-      void SetNucleusRef(const unsigned int i, const int v) { fNucleusRef[i] = v; }
-
-      int GetNuclearA(const unsigned int i) const { return fNuclearA[GetNucleusRef(i)]; }
-      int GetNuclearZ(const unsigned int i) const { return fNuclearZ[GetNucleusRef(i)]; }
-      // this function will create new storage for Nuclear Properties, and return the
-      // reference to it
-      int GetNucleusNextRef() {
-        fNuclearA.push_back(0);
-        fNuclearZ.push_back(0);
-        return fNuclearA.size() - 1;
-      }
-
-      int GetNucleusRef(const unsigned int i) const {
-        if (fNucleusRef[i] >= 0) return fNucleusRef[i];
-        std::ostringstream err;
-        err << "NuclearStackExtension: no nucleus at ref=" << i;
-        throw std::runtime_error(err.str());
-      }
-
-      bool isNucleus(const unsigned int i) const { return fNucleusRef[i] >= 0; }
-
-      /**
-       *   Function to copy particle at location i1 in stack to i2
-       */
-      void Copy(const unsigned int i1, const unsigned int i2) {
-        // index range check
-        if (i1 >= GetSize() || i2 >= GetSize()) {
-          std::ostringstream err;
-          err << "NuclearStackExtension: trying to access data beyond size of stack!";
-          throw std::runtime_error(err.str());
-        }
-        // copy internal particle data p[i2] = p[i1]
-        InnerStackImpl::Copy(i1, i2);
-        // check if any of p[i1] or p[i2] was a Code::Nucleus
-        const int ref1 = fNucleusRef[i1];
-        const int ref2 = fNucleusRef[i2];
-        if (ref2 < 0) {
-          if (ref1 >= 0) {
-            // i1 is nucleus, i2 is not
-            fNucleusRef[i2] = GetNucleusNextRef();
-            fNuclearA[fNucleusRef[i2]] = fNuclearA[ref1];
-            fNuclearZ[fNucleusRef[i2]] = fNuclearZ[ref1];
-          } else {
-            // neither i1 nor i2 are nuclei
-          }
-        } else {
-          if (ref1 >= 0) {
-            // both are nuclei, i2 is overwritten with nucleus i1
-            // fNucleusRef stays the same, but A and Z data is overwritten
-            fNuclearA[ref2] = fNuclearA[ref1];
-            fNuclearZ[ref2] = fNuclearZ[ref1];
-          } else {
-            // i2 is overwritten with non-nucleus i1
-            fNucleusRef[i2] = -1;                       // flag as non-nucleus
-            fNuclearA.erase(fNuclearA.cbegin() + ref2); // remove data for i2
-            fNuclearZ.erase(fNuclearZ.cbegin() + ref2); // remove data for i2
-            const int n = fNucleusRef.size(); // update fNucleusRef: indices above ref2
-                                              // must be decremented by 1
-            for (int i = 0; i < n; ++i) {
-              if (fNucleusRef[i] > ref2) { fNucleusRef[i] -= 1; }
-            }
-          }
-        }
-      }
-
-      /**
-       *   Function to copy particle at location i2 in stack to i1
-       */
-      void Swap(const unsigned int i1, const unsigned int i2) {
-        // index range check
-        if (i1 >= GetSize() || i2 >= GetSize()) {
-          std::ostringstream err;
-          err << "NuclearStackExtension: trying to access data beyond size of stack!";
-          throw std::runtime_error(err.str());
-        }
-        // swap original particle data
-        InnerStackImpl::Swap(i1, i2);
-        // swap corresponding nuclear reference data
-        std::swap(fNucleusRef[i2], fNucleusRef[i1]);
-      }
-
-      void IncrementSize() {
-        InnerStackImpl::IncrementSize();
-        fNucleusRef.push_back(-1);
-      }
-
-      void DecrementSize() {
-        InnerStackImpl::DecrementSize();
-        if (fNucleusRef.size() > 0) {
-          const int ref = fNucleusRef.back();
-          fNucleusRef.pop_back();
-          if (ref >= 0) {
-            fNuclearA.erase(fNuclearA.begin() + ref);
-            fNuclearZ.erase(fNuclearZ.begin() + ref);
-            const int n = fNucleusRef.size();
-            for (int i = 0; i < n; ++i) {
-              if (fNucleusRef[i] >= ref) { fNucleusRef[i] -= 1; }
-            }
-          }
-        }
-      }
-
-    private:
-      /// the actual memory to store particle data
-
-      std::vector<int> fNucleusRef;
-      std::vector<unsigned short> fNuclearA;
-      std::vector<unsigned short> fNuclearZ;
-
-    }; // end class NuclearStackExtensionImpl
-
-    template <typename InnerStack, template <typename> typename _PI>
-    using NuclearStackExtension =
-        Stack<NuclearStackExtensionImpl<typename InnerStack::StackImpl>, _PI>;
-
-    //
-    template <typename StackIter>
-    using ExtendedParticleInterfaceType =
-        corsika::stack::nuclear_extension::NuclearParticleInterface<
-            corsika::stack::super_stupid::SuperStupidStack::MPIType, StackIter>;
-
-    // the particle data stack with extra nuclear information:
-    using ParticleDataStack = corsika::stack::nuclear_extension::NuclearStackExtension<
-        corsika::stack::super_stupid::SuperStupidStack, ExtendedParticleInterfaceType>;
-
-  } // namespace nuclear_extension
-} // namespace corsika::stack
diff --git a/Stack/NuclearStackExtension/testNuclearStackExtension.cc b/Stack/NuclearStackExtension/testNuclearStackExtension.cc
deleted file mode 100644
index 72017c91a05c70d9c062fd72c20f0ff31cc4e246..0000000000000000000000000000000000000000
--- a/Stack/NuclearStackExtension/testNuclearStackExtension.cc
+++ /dev/null
@@ -1,258 +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/geometry/RootCoordinateSystem.h>
-#include <corsika/stack/nuclear_extension/NuclearStackExtension.h>
-#include <corsika/units/PhysicalUnits.h>
-
-using namespace corsika;
-using namespace corsika::stack::nuclear_extension;
-using namespace corsika::geometry;
-using namespace corsika::units::si;
-
-#include <catch2/catch.hpp>
-
-#include <iostream>
-using namespace std;
-
-TEST_CASE("NuclearStackExtension", "[stack]") {
-
-  geometry::CoordinateSystem& dummyCS =
-      geometry::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-  SECTION("write non nucleus") {
-    NuclearStackExtension<corsika::stack::super_stupid::SuperStupidStack,
-                          ExtendedParticleInterfaceType>
-        s;
-    s.AddParticle(
-        std::make_tuple(particles::Code::Electron, 1.5_GeV,
-                        corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
-    CHECK(s.getEntries() == 1);
-  }
-
-  SECTION("write nucleus") {
-    NuclearStackExtension<corsika::stack::super_stupid::SuperStupidStack,
-                          ExtendedParticleInterfaceType>
-        s;
-    s.AddParticle(std::make_tuple(
-        particles::Code::Nucleus, 1.5_GeV,
-        corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 10, 10));
-    CHECK(s.getEntries() == 1);
-  }
-
-  SECTION("write invalid nucleus") {
-    ParticleDataStack s;
-    CHECK_THROWS(s.AddParticle(
-        std::make_tuple(particles::Code::Nucleus, 1.5_GeV,
-                        corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 0, 0)));
-  }
-
-  SECTION("read non nucleus") {
-    ParticleDataStack s;
-    s.AddParticle(
-        std::make_tuple(particles::Code::Electron, 1.5_GeV,
-                        corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
-    const auto pout = s.GetNextParticle();
-    CHECK(pout.GetPID() == particles::Code::Electron);
-    CHECK(pout.GetEnergy() == 1.5_GeV);
-    CHECK(pout.GetTime() == 100_s);
-  }
-
-  SECTION("read nucleus") {
-    ParticleDataStack s;
-    s.AddParticle(
-        std::make_tuple(particles::Code::Nucleus, 1.5_GeV,
-                        corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 10, 9));
-    const auto pout = s.GetNextParticle();
-    CHECK(pout.GetPID() == particles::Code::Nucleus);
-    CHECK(pout.GetEnergy() == 1.5_GeV);
-    CHECK(pout.GetTime() == 100_s);
-    CHECK(pout.GetNuclearA() == 10);
-    CHECK(pout.GetNuclearZ() == 9);
-  }
-
-  SECTION("read invalid nucleus") {
-    ParticleDataStack s;
-    s.AddParticle(
-        std::make_tuple(particles::Code::Electron, 1.5_GeV,
-                        corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
-    const auto pout = s.GetNextParticle();
-    CHECK_THROWS(pout.GetNuclearA());
-    CHECK_THROWS(pout.GetNuclearZ());
-  }
-
-  SECTION("stack fill and cleanup") {
-
-    ParticleDataStack s;
-    // add 99 particles, each 10th particle is a nucleus with A=i and Z=A/2!
-    for (int i = 0; i < 99; ++i) {
-      if ((i + 1) % 10 == 0) {
-        s.AddParticle(std::make_tuple(
-            particles::Code::Nucleus, 1.5_GeV,
-            corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-            Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, i, i / 2));
-      } else {
-        s.AddParticle(std::make_tuple(
-            particles::Code::Electron, 1.5_GeV,
-            corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-            Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
-      }
-    }
-
-    CHECK(s.getEntries() == 99);
-    for (int i = 0; i < 99; ++i) s.GetNextParticle().Delete();
-    CHECK(s.getEntries() == 0);
-  }
-
-  SECTION("stack operations") {
-
-    ParticleDataStack s;
-    // add 99 particles, each 10th particle is a nucleus with A=i and Z=A/2!
-    // i=9, 19, 29, etc. are nuclei
-    for (int i = 0; i < 99; ++i) {
-      if ((i + 1) % 10 == 0) {
-        s.AddParticle(std::make_tuple(
-            particles::Code::Nucleus, i * 15_GeV,
-            corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-            Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, i, i / 2));
-      } else {
-        s.AddParticle(std::make_tuple(
-            particles::Code::Electron, i * 1.5_GeV,
-            corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-            Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
-      }
-    }
-
-    // copy
-    {
-      s.Copy(s.begin() + 9, s.begin() + 10); // nuclei to non-nuclei
-      const auto& p9 = s.cbegin() + 9;
-      const auto& p10 = s.cbegin() + 10;
-
-      CHECK(p9.GetPID() == particles::Code::Nucleus);
-      CHECK(p9.GetEnergy() == 9 * 15_GeV);
-      CHECK(p9.GetTime() == 100_s);
-      CHECK(p9.GetNuclearA() == 9);
-      CHECK(p9.GetNuclearZ() == 9 / 2);
-
-      CHECK(p10.GetPID() == particles::Code::Nucleus);
-      CHECK(p10.GetEnergy() == 9 * 15_GeV);
-      CHECK(p10.GetTime() == 100_s);
-      CHECK(p10.GetNuclearA() == 9);
-      CHECK(p10.GetNuclearZ() == 9 / 2);
-    }
-
-    // copy
-    {
-      s.Copy(s.begin() + 93, s.begin() + 9); // non-nuclei to nuclei
-      const auto& p93 = s.cbegin() + 93;
-      const auto& p9 = s.cbegin() + 9;
-
-      CHECK(p9.GetPID() == particles::Code::Electron);
-      CHECK(p9.GetEnergy() == 93 * 1.5_GeV);
-      CHECK(p9.GetTime() == 100_s);
-
-      CHECK(p93.GetPID() == particles::Code::Electron);
-      CHECK(p93.GetEnergy() == 93 * 1.5_GeV);
-      CHECK(p93.GetTime() == 100_s);
-    }
-
-    // copy
-    {
-      s.Copy(s.begin() + 89, s.begin() + 79); // nuclei to nuclei
-      const auto& p89 = s.cbegin() + 89;
-      const auto& p79 = s.cbegin() + 79;
-
-      CHECK(p89.GetPID() == particles::Code::Nucleus);
-      CHECK(p89.GetEnergy() == 89 * 15_GeV);
-      CHECK(p89.GetTime() == 100_s);
-
-      CHECK(p79.GetPID() == particles::Code::Nucleus);
-      CHECK(p79.GetEnergy() == 89 * 15_GeV);
-      CHECK(p79.GetTime() == 100_s);
-    }
-
-    // swap
-    {
-      s.Swap(s.begin() + 11, s.begin() + 10);
-      const auto& p11 = s.cbegin() + 11; // now: nucleus
-      const auto& p10 = s.cbegin() + 10; // now: electron
-
-      CHECK(p11.GetPID() == particles::Code::Nucleus);
-      CHECK(p11.GetEnergy() == 9 * 15_GeV);
-      CHECK(p11.GetTime() == 100_s);
-      CHECK(p11.GetNuclearA() == 9);
-      CHECK(p11.GetNuclearZ() == 9 / 2);
-
-      CHECK(p10.GetPID() == particles::Code::Electron);
-      CHECK(p10.GetEnergy() == 11 * 1.5_GeV);
-      CHECK(p10.GetTime() == 100_s);
-    }
-
-    // swap two nuclei
-    {
-      s.Swap(s.begin() + 29, s.begin() + 59);
-      const auto& p29 = s.cbegin() + 29;
-      const auto& p59 = s.cbegin() + 59;
-
-      CHECK(p29.GetPID() == particles::Code::Nucleus);
-      CHECK(p29.GetEnergy() == 59 * 15_GeV);
-      CHECK(p29.GetTime() == 100_s);
-      CHECK(p29.GetNuclearA() == 59);
-      CHECK(p29.GetNuclearZ() == 59 / 2);
-
-      CHECK(p59.GetPID() == particles::Code::Nucleus);
-      CHECK(p59.GetEnergy() == 29 * 15_GeV);
-      CHECK(p59.GetTime() == 100_s);
-      CHECK(p59.GetNuclearA() == 29);
-      CHECK(p59.GetNuclearZ() == 29 / 2);
-    }
-
-    for (int i = 0; i < 99; ++i) s.last().Delete();
-    CHECK(s.getEntries() == 0);
-  }
-
-  SECTION("not allowed") {
-    NuclearStackExtension<corsika::stack::super_stupid::SuperStupidStack,
-                          ExtendedParticleInterfaceType>
-        s;
-
-    // not valid:
-    CHECK_THROWS(s.AddParticle(std::make_tuple(
-        particles::Code::Oxygen, 1.5_GeV,
-        corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 16, 8)));
-
-    // valid
-    auto particle = s.AddParticle(
-        std::make_tuple(particles::Code::Nucleus, 1.5_GeV,
-                        corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 10, 9));
-
-    // not valid
-    CHECK_THROWS(particle.AddSecondary(std::make_tuple(
-        particles::Code::Oxygen, 1.5_GeV,
-        corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 16, 8)));
-
-    // add a another nucleus, so there are two now
-    s.AddParticle(
-        std::make_tuple(particles::Code::Nucleus, 1.5_GeV,
-                        corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 10, 9));
-
-    // not valid, since end() is not a valid entry
-    CHECK_THROWS(s.Swap(s.begin(), s.end()));
-  }
-}
diff --git a/Stack/SuperStupidStack/CMakeLists.txt b/Stack/SuperStupidStack/CMakeLists.txt
deleted file mode 100644
index 55b88c69c8c232a96c111e441426f4f23b8aca6a..0000000000000000000000000000000000000000
--- a/Stack/SuperStupidStack/CMakeLists.txt
+++ /dev/null
@@ -1,39 +0,0 @@
-set (SuperStupidStack_HEADERS SuperStupidStack.h)
-set (SuperStupidStack_NAMESPACE corsika/stack/super_stupid)
-
-add_library (SuperStupidStack INTERFACE)
-
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (SuperStupidStack ${SuperStupidStack_NAMESPACE} ${SuperStupidStack_HEADERS})
-
-target_link_libraries (
-  SuperStupidStack
-  INTERFACE
-  CORSIKAstackinterface
-  CORSIKAunits
-  CORSIKAparticles
-  CORSIKAgeometry
-  CORSIKAlogging
-  )
-
-target_include_directories (
-  SuperStupidStack
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include>
-  )
-
-install (
-  FILES
-  ${SuperStupidStack_HEADERS}
-  DESTINATION
-  include/${SuperStupidStack_NAMESPACE}
-  )
-
-# ----------------
-# code unit testing
-CORSIKA_ADD_TEST(testSuperStupidStack)
-target_link_libraries (
-  testSuperStupidStack
-  SuperStupidStack
-  CORSIKAtesting
-  )
diff --git a/Stack/SuperStupidStack/SuperStupidStack.h b/Stack/SuperStupidStack/SuperStupidStack.h
deleted file mode 100644
index eae265cc72d5329cc8d18cad7b762609bee06e9e..0000000000000000000000000000000000000000
--- a/Stack/SuperStupidStack/SuperStupidStack.h
+++ /dev/null
@@ -1,237 +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/particles/ParticleProperties.h>
-#include <corsika/stack/Stack.h>
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h> // remove
-#include <corsika/geometry/Vector.h>
-
-#include <string>
-#include <tuple>
-#include <vector>
-
-namespace corsika::stack {
-
-  typedef corsika::geometry::Vector<corsika::units::si::hepmomentum_d> MomentumVector;
-
-  namespace super_stupid {
-
-    /**
-     * Example of a particle object on the stack.
-     */
-
-    template <typename StackIteratorInterface>
-    class ParticleInterface : public ParticleBase<StackIteratorInterface> {
-
-    protected:
-      using corsika::stack::ParticleBase<StackIteratorInterface>::GetStack;
-      using corsika::stack::ParticleBase<StackIteratorInterface>::GetStackData;
-
-    public:
-      using corsika::stack::ParticleBase<StackIteratorInterface>::GetIndex;
-
-    public:
-      std::string as_string() const {
-        using namespace corsika::units::si;
-        return fmt::format("particle: i={}, PID={}, E={}GeV", GetIndex(),
-                           particles::GetName(GetPID()), GetEnergy() / 1_GeV);
-      }
-
-      void SetParticleData(
-          const std::tuple<corsika::particles::Code, corsika::units::si::HEPEnergyType,
-                           MomentumVector, corsika::geometry::Point,
-                           corsika::units::si::TimeType>& v) {
-        SetPID(std::get<0>(v));
-        SetEnergy(std::get<1>(v));
-        SetMomentum(std::get<2>(v));
-        SetPosition(std::get<3>(v));
-        SetTime(std::get<4>(v));
-      }
-
-      void SetParticleData(
-          ParticleInterface<StackIteratorInterface>&,
-          const std::tuple<corsika::particles::Code, corsika::units::si::HEPEnergyType,
-                           MomentumVector, corsika::geometry::Point,
-                           corsika::units::si::TimeType>& v) {
-        SetPID(std::get<0>(v));
-        SetEnergy(std::get<1>(v));
-        SetMomentum(std::get<2>(v));
-        SetPosition(std::get<3>(v));
-        SetTime(std::get<4>(v));
-      }
-
-      /// individual setters
-      void SetPID(const corsika::particles::Code id) {
-        GetStackData().SetPID(GetIndex(), id);
-      }
-      void SetEnergy(const corsika::units::si::HEPEnergyType& e) {
-        GetStackData().SetEnergy(GetIndex(), e);
-      }
-      void SetMomentum(const MomentumVector& v) {
-        GetStackData().SetMomentum(GetIndex(), v);
-      }
-      void SetPosition(const corsika::geometry::Point& v) {
-        GetStackData().SetPosition(GetIndex(), v);
-      }
-      void SetTime(const corsika::units::si::TimeType& v) {
-        GetStackData().SetTime(GetIndex(), v);
-      }
-
-      /// individual getters
-      corsika::particles::Code GetPID() const {
-        return GetStackData().GetPID(GetIndex());
-      }
-      corsika::units::si::HEPEnergyType GetEnergy() const {
-        return GetStackData().GetEnergy(GetIndex());
-      }
-      MomentumVector GetMomentum() const {
-        return GetStackData().GetMomentum(GetIndex());
-      }
-      corsika::geometry::Point GetPosition() const {
-        return GetStackData().GetPosition(GetIndex());
-      }
-      corsika::units::si::TimeType GetTime() const {
-        return GetStackData().GetTime(GetIndex());
-      }
-      /**
-       * @name derived quantities
-       *
-       * @{
-       */
-      corsika::geometry::Vector<corsika::units::si::dimensionless_d> GetDirection()
-          const {
-        return GetMomentum() / GetEnergy();
-      }
-      corsika::units::si::HEPMassType GetMass() const {
-        return corsika::particles::GetMass(GetPID());
-      }
-      int16_t GetChargeNumber() const {
-        return corsika::particles::GetChargeNumber(GetPID());
-      }
-      ///@}
-    };
-
-    /**
-     * Memory implementation of the most simple (stupid) particle stack object.
-     */
-
-    class SuperStupidStackImpl {
-
-    public:
-      void Init() {}
-      void Dump() const {}
-
-      void Clear() {
-        fDataPID.clear();
-        fDataE.clear();
-        fMomentum.clear();
-        fPosition.clear();
-        fTime.clear();
-      }
-
-      unsigned int GetSize() const { return fDataPID.size(); }
-      unsigned int GetCapacity() const { return fDataPID.size(); }
-
-      void SetPID(const unsigned int i, const corsika::particles::Code id) {
-        fDataPID[i] = id;
-      }
-      void SetEnergy(const unsigned int i, const corsika::units::si::HEPEnergyType e) {
-        fDataE[i] = e;
-      }
-      void SetMomentum(const unsigned int i, const MomentumVector& v) {
-        fMomentum[i] = v;
-      }
-      void SetPosition(const unsigned int i, const corsika::geometry::Point& v) {
-        fPosition[i] = v;
-      }
-      void SetTime(const unsigned int i, const corsika::units::si::TimeType& v) {
-        fTime[i] = v;
-      }
-
-      corsika::particles::Code GetPID(const unsigned int i) const { return fDataPID[i]; }
-      corsika::units::si::HEPEnergyType GetEnergy(const unsigned int i) const {
-        return fDataE[i];
-      }
-      MomentumVector GetMomentum(const unsigned int i) const { return fMomentum[i]; }
-      corsika::geometry::Point GetPosition(const unsigned int i) const {
-        return fPosition[i];
-      }
-      corsika::units::si::TimeType GetTime(const unsigned int i) const {
-        return fTime[i];
-      }
-
-      /**
-       *   Function to copy particle at location i2 in stack to i1
-       */
-      void Copy(const unsigned int i1, const unsigned int i2) {
-        fDataPID[i2] = fDataPID[i1];
-        fDataE[i2] = fDataE[i1];
-        fMomentum[i2] = fMomentum[i1];
-        fPosition[i2] = fPosition[i1];
-        fTime[i2] = fTime[i1];
-      }
-
-      /**
-       *   Function to copy particle at location i2 in stack to i1
-       */
-      void Swap(const unsigned int i1, const unsigned int i2) {
-        std::swap(fDataPID[i2], fDataPID[i1]);
-        std::swap(fDataE[i2], fDataE[i1]);
-        std::swap(fMomentum[i2], fMomentum[i1]);
-        std::swap(fPosition[i2], fPosition[i1]);
-        std::swap(fTime[i2], fTime[i1]);
-      }
-
-      void IncrementSize() {
-        using corsika::geometry::Point;
-        using corsika::particles::Code;
-        fDataPID.push_back(Code::Unknown);
-        fDataE.push_back(0 * corsika::units::si::electronvolt);
-        geometry::CoordinateSystem& dummyCS =
-            geometry::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-        fMomentum.push_back(MomentumVector(
-            dummyCS,
-            {0 * corsika::units::si::electronvolt, 0 * corsika::units::si::electronvolt,
-             0 * corsika::units::si::electronvolt}));
-        fPosition.push_back(
-            Point(dummyCS, {0 * corsika::units::si::meter, 0 * corsika::units::si::meter,
-                            0 * corsika::units::si::meter}));
-        fTime.push_back(0 * corsika::units::si::second);
-      }
-
-      void DecrementSize() {
-        if (fDataE.size() > 0) {
-          fDataPID.pop_back();
-          fDataE.pop_back();
-          fMomentum.pop_back();
-          fPosition.pop_back();
-          fTime.pop_back();
-        }
-      }
-
-    private:
-      /// the actual memory to store particle data
-
-      std::vector<corsika::particles::Code> fDataPID;
-      std::vector<corsika::units::si::HEPEnergyType> fDataE;
-      std::vector<MomentumVector> fMomentum;
-      std::vector<corsika::geometry::Point> fPosition;
-      std::vector<corsika::units::si::TimeType> fTime;
-
-    }; // end class SuperStupidStackImpl
-
-    typedef Stack<SuperStupidStackImpl, ParticleInterface> SuperStupidStack;
-
-  } // namespace super_stupid
-
-} // namespace corsika::stack
diff --git a/Stack/SuperStupidStack/testSuperStupidStack.cc b/Stack/SuperStupidStack/testSuperStupidStack.cc
deleted file mode 100644
index a9f4fadc90d9d21125a92173b13b652e5aa7af90..0000000000000000000000000000000000000000
--- a/Stack/SuperStupidStack/testSuperStupidStack.cc
+++ /dev/null
@@ -1,63 +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.
- */
-
-#define protected public // to also test the internal state of objects
-
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/stack/super_stupid/SuperStupidStack.h>
-#include <corsika/units/PhysicalUnits.h>
-
-using namespace corsika::geometry;
-using namespace corsika::units::si;
-
-#include <catch2/catch.hpp>
-
-using namespace corsika;
-using namespace corsika::stack::super_stupid;
-
-using namespace std;
-
-TEST_CASE("SuperStupidStack", "[stack]") {
-
-  geometry::CoordinateSystem& dummyCS =
-      geometry::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-  SECTION("read+write") {
-
-    SuperStupidStack s;
-    s.AddParticle(
-        std::make_tuple(particles::Code::Electron, 1.5_GeV,
-                        corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
-
-    // read
-    CHECK(s.getEntries() == 1);
-    CHECK(s.getSize() == 1);
-    auto pout = s.GetNextParticle();
-    CHECK(pout.GetPID() == particles::Code::Electron);
-    CHECK(pout.GetEnergy() == 1.5_GeV);
-    CHECK(pout.GetTime() == 100_s);
-  }
-
-  SECTION("write+delete") {
-
-    SuperStupidStack s;
-    for (int i = 0; i < 99; ++i)
-      s.AddParticle(
-          std::make_tuple(particles::Code::Electron, 1.5_GeV,
-                          corsika::stack::MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
-                          Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
-
-    CHECK(s.getSize() == 99);
-
-    for (int i = 0; i < 99; ++i) s.GetNextParticle().Delete();
-
-    CHECK(s.getEntries() == 0);
-    CHECK(s.getSize() == 1);
-  }
-}
diff --git a/ThirdParty/.gitignore b/ThirdParty/.gitignore
deleted file mode 100644
index 0d7fe279527897921a3d07f84e8f70c1dd05ffac..0000000000000000000000000000000000000000
--- a/ThirdParty/.gitignore
+++ /dev/null
@@ -1 +0,0 @@
-eigen-eigen-b3f3d4950030/
diff --git a/ThirdParty/CMakeLists.txt b/ThirdParty/CMakeLists.txt
deleted file mode 100644
index b1553b849d8fbf5d8c5c542b967b82ed663695b3..0000000000000000000000000000000000000000
--- a/ThirdParty/CMakeLists.txt
+++ /dev/null
@@ -1,292 +0,0 @@
-add_subdirectory (spdlog) # this is a git submodule 
-
-add_library (CORSIKAthirdparty INTERFACE)
-
-target_include_directories (CORSIKAthirdparty SYSTEM
-  INTERFACE
-  $<BUILD_INTERFACE:${PROJECT_SOURCE_DIR}/ThirdParty>
-  $<INSTALL_INTERFACE:include/ThirdParty>
-  )
-
-install (DIRECTORY phys DESTINATION include/ThirdParty/)
-install (DIRECTORY catch2 DESTINATION include/ThirdParty/)
-
-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 boost: either use C8 or system-level installation
-
-message ("***** Configuring boost version")
-
-set (USE_BOOST_C8 "C8" CACHE STRING
-    "Selection of boost package. Can be \'C8\' or \'SYSTEM\'. Default: \'C8\'.")
-set_property (CACHE USE_BOOST_C8 PROPERTY STRINGS ${ThirdPartyChoiceValues} )
-if (NOT (${USE_BOOST_C8} IN_LIST ThirdPartyChoiceValues))
-  message (SEND_ERROR "Illegal USE_BOOST_C8=\"${USE_BOOST_C8}\" can only be one of: ${ThirdPartyChoiceValues}")
-endif (NOT (${USE_BOOST_C8} IN_LIST ThirdPartyChoiceValues))
-message (STATUS "USE_BOOST_C8='${USE_BOOST_C8}'")
-
-add_library (C8::ext::boost INTERFACE IMPORTED GLOBAL)
-if ("x_${USE_BOOST_C8}" STREQUAL "x_SYSTEM")
-  find_package (Boost REQUIRED COMPONENTS mp11 iterator core format interval optional type_index histogram multi_array)
-
-  message (STATUS "Using system-level boost version ${Boost_VERSION} at ${Boost_INCLUDE_DIR}")
-  set_target_properties (
-    C8::ext::boost PROPERTIES
-    INTERFACE_LINK_LIBRARIES Boost::headers
-    )
-  set (Boost_FOUND 1 PARENT_SCOPE)
-  
-else ()
-
-  set (_C8_Boost_VERSION "107400")
-  message (STATUS "Building ThirdParty/boost using boost-${_C8_Boost_VERSION}.tar.bz2")
-  ExternalProject_Add (boost
-    URL ${CMAKE_CURRENT_SOURCE_DIR}/boost-${_C8_Boost_VERSION}.tar.bz2
-    URL_MD5 d55f45e662d101985353136b321ec624
-    SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/boost/install/boost 
-    INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/boost/install/boost 
-    CONFIGURE_COMMAND ""
-    BUILD_COMMAND ""
-    INSTALL_COMMAND ""
-    BUILD_IN_SOURCE ON
-    EXCLUDE_FROM_ALL FALSE
-    )
-  set (HAVE_Boost 1 CACHE BOOL "presence of boost, via external-project-add in ThirdParty folder")
-  set (Boost_FOUND 1 PARENT_SCOPE)
-  ExternalProject_Get_Property (boost INSTALL_DIR)
-  set (Boost_VERSION ${_C8_Boost_VERSION} CACHE STRING "Version of Boost")
-  set (Boost_PREFIX ${INSTALL_DIR}/..)
-  set (Boost_INCLUDE_DIR  ${Boost_PREFIX})
-  set (Boost_LIBRARY_DIR ${Boost_PREFIX})
-  add_dependencies (C8::ext::boost boost)
-  
-  # create include directory at config time
-  file (MAKE_DIRECTORY ${Boost_INCLUDE_DIR})
-  
-  set (Boost_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/externals/boost)
-  install (DIRECTORY ${Boost_PREFIX}/ DESTINATION ${Boost_INSTALL_DIR})
-  
-  message (STATUS "Use ThirdParty boost include dir ${Boost_INCLUDE_DIR}")
-  set_target_properties (
-    C8::ext::boost PROPERTIES
-    INTERFACE_INCLUDE_DIRECTORIES
-      $<BUILD_INTERFACE:${Boost_INCLUDE_DIR}>
-    )
- 
-endif ()
-
-
-##############################################################################
-# check for Eigen3: either use ThirdParty/eigen3 or system-level installation
-
-message ("***** Configuring eigen3 version")
-
-set (USE_EIGEN3_C8 "C8" CACHE STRING
-    "Selection of eigen3 package. Can be \'C8\' or \'SYSTEM\'. Default: \'C8\'.")
-set_property (CACHE USE_EIGEN3_C8 PROPERTY STRINGS ${ThirdPartyChoiceValues})
-if (NOT (${USE_EIGEN3_C8} IN_LIST ThirdPartyChoiceValues))
-  message (SEND_ERROR "Illegal USE_EIGEN3_C8=\"${USE_EIGEN3_C8}\" can only be one of: ${ThirdPartyChoiceValues}")
-endif (NOT (${USE_EIGEN3_C8} IN_LIST ThirdPartyChoiceValues))
-message (STATUS "USE_EIGEN3_C8='${USE_EIGEN3_C8}'")
-
-add_library (C8::ext::eigen3 INTERFACE IMPORTED GLOBAL)
-if ("x_${USE_EIGEN3_C8}" STREQUAL "x_SYSTEM")
-  
-  if (WITH_EIGEN3)
-    list (APPEND CMAKE_MODULE_PATH "${WITH_EIGEN3}/cmake")
-  endif (WITH_EIGEN3)
-  find_package (Eigen3 REQUIRED NO_MODULE)
-  message (STATUS "Using system-level eigen3 version ${Eigen3_VERSION} at ${Eigen3_INCLUDE_DIR}")
-  set_target_properties (
-    C8::ext::eigen3 PROPERTIES
-    INTERFACE_LINK_LIBRARIES Eigen3::Eigen
-  )
-set (Eigen3_FOUND 1 PARENT_SCOPE)
-
-else ()
-
-  set (_C8_Eigen3_VERSION "eigen-eigen-b3f3d4950030")
-
-  message (STATUS "Building ThirdParty/eigen3 using ${_C8_Eigen3_VERSION}.tar.bz2")
-  ExternalProject_Add (eigen3
-    URL ${CMAKE_CURRENT_SOURCE_DIR}/${_C8_Eigen3_VERSION}.tar.bz2
-    URL_MD5 e83549a79d1b721da0f8899ab34edf95
-    SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/eigen3/install/eigen
-    INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/eigen3/install/eigen
-    CONFIGURE_COMMAND ""
-    BUILD_COMMAND ""
-    INSTALL_COMMAND ""
-    BUILD_IN_SOURCE ON
-    EXCLUDE_FROM_ALL FALSE
-    )
-  set (Eigen3_FOUND 1 PARENT_SCOPE)
-  ExternalProject_Get_Property (eigen3 INSTALL_DIR)
-  set (Eigen3_PREFIX ${INSTALL_DIR}/..)
-  set (Eigen3_INCLUDE_DIR  ${Eigen3_PREFIX}/eigen)
-  add_dependencies (C8::ext::eigen3 eigen3)
-
-  # create include directory at config time
-  file (MAKE_DIRECTORY ${Eigen3_INCLUDE_DIR})
-  
-  set (Eigen3_INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/share/externals/eigen3)
-  install (DIRECTORY ${Eigen3_PREFIX}/ DESTINATION ${Eigen3_INSTALL_DIR})
-  
-  message (STATUS "Use ThirdParty eigen3 include dir ${Eigen3_INCLUDE_DIR}")
-  set_target_properties (
-    C8::ext::eigen3 PROPERTIES
-    INTERFACE_INCLUDE_DIRECTORIES
-      $<BUILD_INTERFACE:${Eigen3_INCLUDE_DIR}>
-    )
- 
-endif ()
-
-
-##############################################################################
-# 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}
-    IMPORTED_LINK_INTERFACE_LIBRARIES dl
-    INTERFACE_INCLUDE_DIRECTORIES ${Pythia8_INCLUDE_DIR}
-    )
-  set (Pythia8_FOUND 1 PARENT_SCOPE)
-
-else ()
-
-  set (_C8_Pythia8_VERSION "8235")
-  message (STATUS "Building ThirdParty/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}>
-    )
-
-endif ()
-
-
-
-##############################################################################
-# check for CxRoot/CONEX: either use ThirdParty/cxroot or system-level installation
-
-message ("***** Configuring CxRoot/CONEX version")
-
-set (USE_CONEX_C8 "C8" CACHE STRING
-    "Selection of conex package. Can be \'C8\' or \'SYSTEM\'. Default: \'C8\'.")
-set_property (CACHE USE_CONEX_C8 PROPERTY STRINGS ${ThirdPartyChoiceValues})
-if (NOT (${USE_CONEX_C8} IN_LIST ThirdPartyChoiceValues))
-  message (SEND_ERROR "Illegal USE_CONEX_C8=\"${USE_CONEX_C8}\" can only be one of: ${ThirdPartyChoiceValues}")
-endif (NOT (${USE_CONEX_C8} IN_LIST ThirdPartyChoiceValues))
-message (STATUS "USE_CONEX_C8='${USE_CONEX_C8}'")
-
-add_library (C8::ext::conex STATIC IMPORTED GLOBAL)
-if ("x_${USE_CONEX_C8}" STREQUAL "x_SYSTEM")
-  
-  find_package (CONEX REQUIRED) 
-  message (STATUS "Using system-level CONEX version at ${CONEX_INCLUDE_DIR}")
-  set (CONEX_FOUND 1 PARENT_SCOPE)
-
-else ()
-  
-  message (STATUS "Building conex obtained via git and compiled")
-  message (STATUS "This will take a bit.....")
-  if (${CMAKE_VERSION} VERSION_LESS "3.16.0")
-    message (WARNING "You need cmake >= 3.16 to support proper external git submodules. Right now, it will be more inefficient than necessary.")
-  else (${CMAKE_VERSION} VERSION_LESS "3.16.0")
-    cmake_policy (SET CMP0097 NEW) # avoid cloning of git submodules
-  endif (${CMAKE_VERSION} VERSION_LESS "3.16.0")
-  ExternalProject_Add (cxroot
-    GIT_REPOSITORY https://gitlab.ikp.kit.edu/AirShowerPhysics/cxroot.git
-    GIT_SUBMODULES ""
-    GIT_TAG origin/master
-    GIT_SHALLOW 5
-    GIT_PROGRESS 1
-    SOURCE_DIR ${CMAKE_CURRENT_BINARY_DIR}/cxroot/source
-    INSTALL_DIR ${CMAKE_CURRENT_BINARY_DIR}/cxroot/source
-    CONFIGURE_COMMAND ""
-    BUILD_COMMAND make CX_NO_ROOT=1 CORSIKA_8=1 CORSIKA_DATA=${CORSIKA_DATA} all
-    INSTALL_COMMAND ""
-    BUILD_IN_SOURCE ON
-    EXCLUDE_FROM_ALL TRUE
-    )
-  set (CONEX_FOUND 1 PARENT_SCOPE)
-  ExternalProject_Get_Property (cxroot INSTALL_DIR)
-  get_filename_component(INSTALL_DIR_ABS ${INSTALL_DIR} ABSOLUTE)
-  set (CONEX_PREFIX ${INSTALL_DIR_ABS})
-  set (CONEX_INCLUDE_DIR  ${CONEX_PREFIX}/src)
-  set (CONEX_INCLUDE_DIR  ${CONEX_PREFIX}/src PARENT_SCOPE)
-  add_dependencies (C8::ext::conex cxroot)
-
-  # create include directory at config time
-  file (MAKE_DIRECTORY ${CONEX_INCLUDE_DIR})
-    
-endif ()
-
-set_target_properties (
-  C8::ext::conex PROPERTIES
-  IMPORTED_LOCATION ${CONEX_PREFIX}/lib/${CMAKE_SYSTEM_NAME}/libCONEXsibyll.a
-  IMPORTED_NO_SONAME 1
-  SKIP_BUILD_RPATH FALSE
-  IMPORTED_LINK_INTERFACE_LIBRARIES bz2
-  INTERFACE_INCLUDE_DIRECTORIES
-  $<BUILD_INTERFACE:${CONEX_INCLUDE_DIR}>    
-  )
-
-
-# libz needed for cnpy, used for SaveHistograms
-find_package (ZLIB QUIET)
-
-if (ZLIB_FOUND)
-  message (STATUS "Found ZLIB. Build cnpy for SaveHistograms")  
-  add_subdirectory (cnpy)
-else (ZLIB_FOUND)
-  message (WARNING "Did not find ZLIB. Cannot build cnpy for SaveHistograms")  
-endif (ZLIB_FOUND)
diff --git a/ThirdParty/ThirdParty.dox b/ThirdParty/ThirdParty.dox
deleted file mode 100644
index a271a23fa0c7999bbabb889887ea44a515fc89e1..0000000000000000000000000000000000000000
--- a/ThirdParty/ThirdParty.dox
+++ /dev/null
@@ -1,70 +0,0 @@
-/**
-@page ThirdParty Third party software
-@tableofcontents
-
-In the directory ThirdParty we provide simple dependencies. This
-minimizes the need to install additional software for the user. Note
-the individual copyrights and licences here!
-
-
-@section PhysUnits
-
-The PhysUnits library is an external dependency included here just for
-convenience:
-
-Original source code from:
-https://github.com/martinmoene/PhysUnits-CT-Cpp11#references
-
-Licence: BSL-1.0
-(https://github.com/martinmoene/PhysUnits-CT-Cpp11/blob/master/LICENSE_1_0.txt)
-
-References: https://github.com/martinmoene/PhysUnits-CT-Cpp11#references
-
-
-@section catch2
- 
-The catch2 unit testing library is from:
-https://github.com/catchorg/Catch2
-
-Licence: BSL-1.0
-(https://github.com/martinmoene/PhysUnits-CT-Cpp11/blob/master/LICENSE_1_0.txt)
-
-References: https://github.com/catchorg/Catch2
-
-@section eigen3
- 
-eigen3 ....
-
-@section bitset2
-
-see https://github.com/ClaasBontus/bitset2, this package was obtained
-from a Boost Software License 1.0.
-
-@section Boost
-
-A subset of header-only Boost libraries. Boost is from:
-https://www.boost.org
-
-License: BSL-1.0
-(https://www.boost.org/LICENSE_1_0.txt)
-
-The subset was generated with the bcp tool:
-https://www.boost.org/doc/libs/1_73_0/tools/bcp/doc/html/index.html
-
-Instructions on how to update Boost with this tool:
-
-Download the latest tarball from www.boost.org.
-Unpack source code somewhere, go to into the folder.
-Run these commands:
-
-    ./bootstrap && ./b2 tools/bcp
-    ./dist/bin/bcp histogram multiarray math ./dist
-    mv ./dist/boost <CORSIKA-path>/ThirdParty/boost
-
-@section Pythia8
-
-There is a tar file provided with a default version of Pythia8. Some
-files have been removed from this with respect to the original
-distribution to save space.
-
-*/
diff --git a/ThirdParty/boost-107400.tar.bz2 b/ThirdParty/boost-107400.tar.bz2
deleted file mode 100644
index 61575a5e9105b8f383c96fe9928ee1be96fc4c80..0000000000000000000000000000000000000000
Binary files a/ThirdParty/boost-107400.tar.bz2 and /dev/null differ
diff --git a/ThirdParty/catch2/catch.hpp b/ThirdParty/catch2/catch.hpp
deleted file mode 100644
index 02302b8d3e97f3623ef075382a30ef72b7256b98..0000000000000000000000000000000000000000
--- a/ThirdParty/catch2/catch.hpp
+++ /dev/null
@@ -1,15372 +0,0 @@
-/*
- *  Catch v2.8.0
- *  Generated: 2019-05-26 21:29:22.235281
- *  ----------------------------------------------------------
- *  This file has been merged from multiple headers. Please don't edit it directly
- *  Copyright (c) 2019 Two Blue Cubes Ltd. All rights reserved.
- *
- *  Distributed under the Boost Software License, Version 1.0. (See accompanying
- *  file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- */
-#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-// start catch.hpp
-
-
-#define CATCH_VERSION_MAJOR 2
-#define CATCH_VERSION_MINOR 8
-#define CATCH_VERSION_PATCH 0
-
-#ifdef __clang__
-#    pragma clang system_header
-#elif defined __GNUC__
-#    pragma GCC system_header
-#endif
-
-// start catch_suppress_warnings.h
-
-#ifdef __clang__
-#   ifdef __ICC // icpc defines the __clang__ macro
-#       pragma warning(push)
-#       pragma warning(disable: 161 1682)
-#   else // __ICC
-#       pragma clang diagnostic push
-#       pragma clang diagnostic ignored "-Wpadded"
-#       pragma clang diagnostic ignored "-Wswitch-enum"
-#       pragma clang diagnostic ignored "-Wcovered-switch-default"
-#    endif
-#elif defined __GNUC__
-     // Because REQUIREs trigger GCC's -Wparentheses, and because still
-     // supported version of g++ have only buggy support for _Pragmas,
-     // Wparentheses have to be suppressed globally.
-#    pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details
-
-#    pragma GCC diagnostic push
-#    pragma GCC diagnostic ignored "-Wunused-variable"
-#    pragma GCC diagnostic ignored "-Wpadded"
-#endif
-// end catch_suppress_warnings.h
-#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER)
-#  define CATCH_IMPL
-#  define CATCH_CONFIG_ALL_PARTS
-#endif
-
-// In the impl file, we want to have access to all parts of the headers
-// Can also be used to sanely support PCHs
-#if defined(CATCH_CONFIG_ALL_PARTS)
-#  define CATCH_CONFIG_EXTERNAL_INTERFACES
-#  if defined(CATCH_CONFIG_DISABLE_MATCHERS)
-#    undef CATCH_CONFIG_DISABLE_MATCHERS
-#  endif
-#  if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
-#    define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
-#  endif
-#endif
-
-#if !defined(CATCH_CONFIG_IMPL_ONLY)
-// start catch_platform.h
-
-#ifdef __APPLE__
-# include <TargetConditionals.h>
-# if TARGET_OS_OSX == 1
-#  define CATCH_PLATFORM_MAC
-# elif TARGET_OS_IPHONE == 1
-#  define CATCH_PLATFORM_IPHONE
-# endif
-
-#elif defined(linux) || defined(__linux) || defined(__linux__)
-#  define CATCH_PLATFORM_LINUX
-
-#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__)
-#  define CATCH_PLATFORM_WINDOWS
-#endif
-
-// end catch_platform.h
-
-#ifdef CATCH_IMPL
-#  ifndef CLARA_CONFIG_MAIN
-#    define CLARA_CONFIG_MAIN_NOT_DEFINED
-#    define CLARA_CONFIG_MAIN
-#  endif
-#endif
-
-// start catch_user_interfaces.h
-
-namespace Catch {
-    unsigned int rngSeed();
-}
-
-// end catch_user_interfaces.h
-// start catch_tag_alias_autoregistrar.h
-
-// start catch_common.h
-
-// start catch_compiler_capabilities.h
-
-// Detect a number of compiler features - by compiler
-// The following features are defined:
-//
-// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported?
-// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported?
-// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported?
-// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled?
-// ****************
-// Note to maintainers: if new toggles are added please document them
-// in configuration.md, too
-// ****************
-
-// In general each macro has a _NO_<feature name> form
-// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature.
-// Many features, at point of detection, define an _INTERNAL_ macro, so they
-// can be combined, en-mass, with the _NO_ forms later.
-
-#ifdef __cplusplus
-
-#  if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L)
-#    define CATCH_CPP14_OR_GREATER
-#  endif
-
-#  if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
-#    define CATCH_CPP17_OR_GREATER
-#  endif
-
-#endif
-
-#if defined(CATCH_CPP17_OR_GREATER)
-#  define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
-#endif
-
-#ifdef __clang__
-
-#       define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
-            _Pragma( "clang diagnostic push" ) \
-            _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \
-            _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"")
-#       define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
-            _Pragma( "clang diagnostic pop" )
-
-#       define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
-            _Pragma( "clang diagnostic push" ) \
-            _Pragma( "clang diagnostic ignored \"-Wparentheses\"" )
-#       define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
-            _Pragma( "clang diagnostic pop" )
-
-#       define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
-            _Pragma( "clang diagnostic push" ) \
-            _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" )
-#       define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS \
-            _Pragma( "clang diagnostic pop" )
-
-#       define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
-            _Pragma( "clang diagnostic push" ) \
-            _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" )
-#       define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
-            _Pragma( "clang diagnostic pop" )
-
-#endif // __clang__
-
-////////////////////////////////////////////////////////////////////////////////
-// Assume that non-Windows platforms support posix signals by default
-#if !defined(CATCH_PLATFORM_WINDOWS)
-    #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// We know some environments not to support full POSIX signals
-#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__)
-    #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
-#endif
-
-#ifdef __OS400__
-#       define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS
-#       define CATCH_CONFIG_COLOUR_NONE
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Android somehow still does not support std::to_string
-#if defined(__ANDROID__)
-#    define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Not all Windows environments support SEH properly
-#if defined(__MINGW32__)
-#    define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// PS4
-#if defined(__ORBIS__)
-#    define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Cygwin
-#ifdef __CYGWIN__
-
-// Required for some versions of Cygwin to declare gettimeofday
-// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin
-#   define _BSD_SOURCE
-// some versions of cygwin (most) do not support std::to_string. Use the libstd check.
-// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813
-# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \
-	       && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF))
-
-#	define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING
-
-# endif
-#endif // __CYGWIN__
-
-////////////////////////////////////////////////////////////////////////////////
-// Visual C++
-#ifdef _MSC_VER
-
-#  if _MSC_VER >= 1900 // Visual Studio 2015 or newer
-#    define CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
-#  endif
-
-// Universal Windows platform does not support SEH
-// Or console colours (or console at all...)
-#  if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP)
-#    define CATCH_CONFIG_COLOUR_NONE
-#  else
-#    define CATCH_INTERNAL_CONFIG_WINDOWS_SEH
-#  endif
-
-// MSVC traditional preprocessor needs some workaround for __VA_ARGS__
-// _MSVC_TRADITIONAL == 0 means new conformant preprocessor
-// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor
-#  if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL)
-#    define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#  endif
-
-#endif // _MSC_VER
-
-////////////////////////////////////////////////////////////////////////////////
-// Check if we are compiled with -fno-exceptions or equivalent
-#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND)
-#  define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// DJGPP
-#ifdef __DJGPP__
-#  define CATCH_INTERNAL_CONFIG_NO_WCHAR
-#endif // __DJGPP__
-
-////////////////////////////////////////////////////////////////////////////////
-// Embarcadero C++Build
-#if defined(__BORLANDC__)
-    #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-
-// Use of __COUNTER__ is suppressed during code analysis in
-// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly
-// handled by it.
-// Otherwise all supported compilers support COUNTER macro,
-// but user still might want to turn it off
-#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L )
-    #define CATCH_INTERNAL_CONFIG_COUNTER
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Check if string_view is available and usable
-// The check is split apart to work around v140 (VS2015) preprocessor issue...
-#if defined(__has_include)
-#if __has_include(<string_view>) && defined(CATCH_CPP17_OR_GREATER)
-#    define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW
-#endif
-#endif
-
-////////////////////////////////////////////////////////////////////////////////
-// Check if optional is available and usable
-#if defined(__has_include)
-#  if __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
-#    define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL
-#  endif // __has_include(<optional>) && defined(CATCH_CPP17_OR_GREATER)
-#endif // __has_include
-
-////////////////////////////////////////////////////////////////////////////////
-// Check if variant is available and usable
-#if defined(__has_include)
-#  if __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
-#    if defined(__clang__) && (__clang_major__ < 8)
-       // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852
-       // fix should be in clang 8, workaround in libstdc++ 8.2
-#      include <ciso646>
-#      if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
-#        define CATCH_CONFIG_NO_CPP17_VARIANT
-#      else
-#        define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
-#      endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9)
-#    else
-#      define CATCH_INTERNAL_CONFIG_CPP17_VARIANT
-#    endif // defined(__clang__) && (__clang_major__ < 8)
-#  endif // __has_include(<variant>) && defined(CATCH_CPP17_OR_GREATER)
-#endif // __has_include
-
-#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER)
-#   define CATCH_CONFIG_COUNTER
-#endif
-#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH)
-#   define CATCH_CONFIG_WINDOWS_SEH
-#endif
-// This is set by default, because we assume that unix compilers are posix-signal-compatible by default.
-#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS)
-#   define CATCH_CONFIG_POSIX_SIGNALS
-#endif
-// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions.
-#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR)
-#   define CATCH_CONFIG_WCHAR
-#endif
-
-#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING)
-#    define CATCH_CONFIG_CPP11_TO_STRING
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL)
-#  define CATCH_CONFIG_CPP17_OPTIONAL
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_NO_CPP17_UNCAUGHT_EXCEPTIONS) && !defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
-#  define CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW)
-#  define CATCH_CONFIG_CPP17_STRING_VIEW
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT)
-#  define CATCH_CONFIG_CPP17_VARIANT
-#endif
-
-#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
-#  define CATCH_INTERNAL_CONFIG_NEW_CAPTURE
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE)
-#  define CATCH_CONFIG_NEW_CAPTURE
-#endif
-
-#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
-#  define CATCH_CONFIG_DISABLE_EXCEPTIONS
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN)
-#  define CATCH_CONFIG_POLYFILL_ISNAN
-#endif
-
-#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS)
-#   define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS
-#   define CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS
-#endif
-#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS)
-#   define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
-#   define CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
-#endif
-#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS)
-#   define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS
-#   define CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
-#endif
-#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS)
-#   define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS
-#   define CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS
-#endif
-
-#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
-#define CATCH_TRY if ((true))
-#define CATCH_CATCH_ALL if ((false))
-#define CATCH_CATCH_ANON(type) if ((false))
-#else
-#define CATCH_TRY try
-#define CATCH_CATCH_ALL catch (...)
-#define CATCH_CATCH_ANON(type) catch (type)
-#endif
-
-#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR)
-#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#endif
-
-// end catch_compiler_capabilities.h
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line
-#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line )
-#ifdef CATCH_CONFIG_COUNTER
-#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ )
-#else
-#  define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ )
-#endif
-
-#include <iosfwd>
-#include <string>
-#include <cstdint>
-
-// We need a dummy global operator<< so we can bring it into Catch namespace later
-struct Catch_global_namespace_dummy {};
-std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy);
-
-namespace Catch {
-
-    struct CaseSensitive { enum Choice {
-        Yes,
-        No
-    }; };
-
-    class NonCopyable {
-        NonCopyable( NonCopyable const& )              = delete;
-        NonCopyable( NonCopyable && )                  = delete;
-        NonCopyable& operator = ( NonCopyable const& ) = delete;
-        NonCopyable& operator = ( NonCopyable && )     = delete;
-
-    protected:
-        NonCopyable();
-        virtual ~NonCopyable();
-    };
-
-    struct SourceLineInfo {
-
-        SourceLineInfo() = delete;
-        SourceLineInfo( char const* _file, std::size_t _line ) noexcept
-        :   file( _file ),
-            line( _line )
-        {}
-
-        SourceLineInfo( SourceLineInfo const& other )            = default;
-        SourceLineInfo& operator = ( SourceLineInfo const& )     = default;
-        SourceLineInfo( SourceLineInfo&& )              noexcept = default;
-        SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default;
-
-        bool empty() const noexcept;
-        bool operator == ( SourceLineInfo const& other ) const noexcept;
-        bool operator < ( SourceLineInfo const& other ) const noexcept;
-
-        char const* file;
-        std::size_t line;
-    };
-
-    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info );
-
-    // Bring in operator<< from global namespace into Catch namespace
-    // This is necessary because the overload of operator<< above makes
-    // lookup stop at namespace Catch
-    using ::operator<<;
-
-    // Use this in variadic streaming macros to allow
-    //    >> +StreamEndStop
-    // as well as
-    //    >> stuff +StreamEndStop
-    struct StreamEndStop {
-        std::string operator+() const;
-    };
-    template<typename T>
-    T const& operator + ( T const& value, StreamEndStop ) {
-        return value;
-    }
-}
-
-#define CATCH_INTERNAL_LINEINFO \
-    ::Catch::SourceLineInfo( __FILE__, static_cast<std::size_t>( __LINE__ ) )
-
-// end catch_common.h
-namespace Catch {
-
-    struct RegistrarForTagAliases {
-        RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo );
-    };
-
-} // end namespace Catch
-
-#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \
-    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
-    namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \
-    CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
-
-// end catch_tag_alias_autoregistrar.h
-// start catch_test_registry.h
-
-// start catch_interfaces_testcase.h
-
-#include <vector>
-
-namespace Catch {
-
-    class TestSpec;
-
-    struct ITestInvoker {
-        virtual void invoke () const = 0;
-        virtual ~ITestInvoker();
-    };
-
-    class TestCase;
-    struct IConfig;
-
-    struct ITestCaseRegistry {
-        virtual ~ITestCaseRegistry();
-        virtual std::vector<TestCase> const& getAllTests() const = 0;
-        virtual std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const = 0;
-    };
-
-    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
-    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
-    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
-
-}
-
-// end catch_interfaces_testcase.h
-// start catch_stringref.h
-
-#include <cstddef>
-#include <string>
-#include <iosfwd>
-
-namespace Catch {
-
-    /// A non-owning string class (similar to the forthcoming std::string_view)
-    /// Note that, because a StringRef may be a substring of another string,
-    /// it may not be null terminated. c_str() must return a null terminated
-    /// string, however, and so the StringRef will internally take ownership
-    /// (taking a copy), if necessary. In theory this ownership is not externally
-    /// visible - but it does mean (substring) StringRefs should not be shared between
-    /// threads.
-    class StringRef {
-    public:
-        using size_type = std::size_t;
-
-    private:
-        friend struct StringRefTestAccess;
-
-        char const* m_start;
-        size_type m_size;
-
-        char* m_data = nullptr;
-
-        void takeOwnership();
-
-        static constexpr char const* const s_empty = "";
-
-    public: // construction/ assignment
-        StringRef() noexcept
-        :   StringRef( s_empty, 0 )
-        {}
-
-        StringRef( StringRef const& other ) noexcept
-        :   m_start( other.m_start ),
-            m_size( other.m_size )
-        {}
-
-        StringRef( StringRef&& other ) noexcept
-        :   m_start( other.m_start ),
-            m_size( other.m_size ),
-            m_data( other.m_data )
-        {
-            other.m_data = nullptr;
-        }
-
-        StringRef( char const* rawChars ) noexcept;
-
-        StringRef( char const* rawChars, size_type size ) noexcept
-        :   m_start( rawChars ),
-            m_size( size )
-        {}
-
-        StringRef( std::string const& stdString ) noexcept
-        :   m_start( stdString.c_str() ),
-            m_size( stdString.size() )
-        {}
-
-        ~StringRef() noexcept {
-            delete[] m_data;
-        }
-
-        auto operator = ( StringRef const &other ) noexcept -> StringRef& {
-            delete[] m_data;
-            m_data = nullptr;
-            m_start = other.m_start;
-            m_size = other.m_size;
-            return *this;
-        }
-
-        operator std::string() const;
-
-        void swap( StringRef& other ) noexcept;
-
-    public: // operators
-        auto operator == ( StringRef const& other ) const noexcept -> bool;
-        auto operator != ( StringRef const& other ) const noexcept -> bool;
-
-        auto operator[] ( size_type index ) const noexcept -> char;
-
-    public: // named queries
-        auto empty() const noexcept -> bool {
-            return m_size == 0;
-        }
-        auto size() const noexcept -> size_type {
-            return m_size;
-        }
-
-        auto numberOfCharacters() const noexcept -> size_type;
-        auto c_str() const -> char const*;
-
-    public: // substrings and searches
-        auto substr( size_type start, size_type size ) const noexcept -> StringRef;
-
-        // Returns the current start pointer.
-        // Note that the pointer can change when if the StringRef is a substring
-        auto currentData() const noexcept -> char const*;
-
-    private: // ownership queries - may not be consistent between calls
-        auto isOwned() const noexcept -> bool;
-        auto isSubstring() const noexcept -> bool;
-    };
-
-    auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string;
-    auto operator + ( StringRef const& lhs, char const* rhs ) -> std::string;
-    auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string;
-
-    auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&;
-    auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&;
-
-    inline auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef {
-        return StringRef( rawChars, size );
-    }
-
-} // namespace Catch
-
-inline auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef {
-    return Catch::StringRef( rawChars, size );
-}
-
-// end catch_stringref.h
-// start catch_type_traits.hpp
-
-
-#include <type_traits>
-
-namespace Catch{
-
-#ifdef CATCH_CPP17_OR_GREATER
-	template <typename...>
-	inline constexpr auto is_unique = std::true_type{};
-
-	template <typename T, typename... Rest>
-	inline constexpr auto is_unique<T, Rest...> = std::bool_constant<
-		(!std::is_same_v<T, Rest> && ...) && is_unique<Rest...>
-	>{};
-#else
-
-template <typename...>
-struct is_unique : std::true_type{};
-
-template <typename T0, typename T1, typename... Rest>
-struct is_unique<T0, T1, Rest...> : std::integral_constant
-<bool,
-     !std::is_same<T0, T1>::value
-     && is_unique<T0, Rest...>::value
-     && is_unique<T1, Rest...>::value
->{};
-
-#endif
-}
-
-// end catch_type_traits.hpp
-// start catch_preprocessor.hpp
-
-
-#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__
-#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__)))
-#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__)))
-
-#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__
-// MSVC needs more evaluations
-#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__)))
-#define CATCH_RECURSE(...)  CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__))
-#else
-#define CATCH_RECURSE(...)  CATCH_RECURSION_LEVEL5(__VA_ARGS__)
-#endif
-
-#define CATCH_REC_END(...)
-#define CATCH_REC_OUT
-
-#define CATCH_EMPTY()
-#define CATCH_DEFER(id) id CATCH_EMPTY()
-
-#define CATCH_REC_GET_END2() 0, CATCH_REC_END
-#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2
-#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1
-#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT
-#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0)
-#define CATCH_REC_NEXT(test, next)  CATCH_REC_NEXT1(CATCH_REC_GET_END test, next)
-
-#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST2(f, x, peek, ...)   f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ )
-
-#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ )
-#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...)   f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ )
-
-// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results,
-// and passes userdata as the first parameter to each invocation,
-// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c)
-#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
-
-#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))
-
-#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param)
-#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__
-#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__
-#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
-#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__)
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__
-#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param))
-#else
-// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF
-#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__)
-#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__
-#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1)
-#endif
-
-#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__
-#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name)
-
-#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__)
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>())
-#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))
-#else
-#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper<INTERNAL_CATCH_REMOVE_PARENS_GEN(__VA_ARGS__)>()))
-#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)))
-#endif
-
-#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\
-    CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__)
-
-#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0)
-#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1)
-#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2)
-#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3)
-#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4)
-#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5)
-#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _4, _5, _6)
-#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7)
-#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8)
-#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9)
-#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10)
-
-#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N
-
-#define INTERNAL_CATCH_TYPE_GEN\
-    template<typename...> struct TypeList {};\
-    template<typename...Ts>\
-    constexpr auto get_wrapper() noexcept -> TypeList<Ts...> { return {}; }\
-    \
-    template<template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2> \
-    constexpr auto append(L1<E1...>, L2<E2...>) noexcept -> L1<E1...,E2...> { return {}; }\
-    template< template<typename...> class L1, typename...E1, template<typename...> class L2, typename...E2, typename...Rest>\
-    constexpr auto append(L1<E1...>, L2<E2...>, Rest...) noexcept -> decltype(append(L1<E1...,E2...>{}, Rest{}...)) { return {}; }\
-    \
-    template< template<typename...> class Container, template<typename...> class List, typename...elems>\
-    constexpr auto rewrap(List<elems...>) noexcept -> TypeList<Container<elems...>> { return {}; }\
-    template< template<typename...> class Container, template<typename...> class List, class...Elems, typename...Elements>\
-    constexpr auto rewrap(List<Elems...>,Elements...) noexcept -> decltype(append(TypeList<Container<Elems...>>{}, rewrap<Container>(Elements{}...))) { return {}; }\
-    \
-    template<template <typename...> class Final, template< typename...> class...Containers, typename...Types>\
-    constexpr auto create(TypeList<Types...>) noexcept -> decltype(append(Final<>{}, rewrap<Containers>(Types{}...)...)) { return {}; }
-
-#define INTERNAL_CATCH_NTTP_1(signature, ...)\
-    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> struct Nttp{};\
-    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
-    constexpr auto get_wrapper() noexcept -> Nttp<__VA_ARGS__> { return {}; } \
-    \
-    template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature)>\
-    constexpr auto rewrap(List<__VA_ARGS__>) noexcept -> TypeList<Container<__VA_ARGS__>> { return {}; }\
-    template< template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class Container, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class List, INTERNAL_CATCH_REMOVE_PARENS(signature), typename...Elements>\
-    constexpr auto rewrap(List<__VA_ARGS__>,Elements...elems) noexcept -> decltype(append(TypeList<Container<__VA_ARGS__>>{}, rewrap<Container>(elems...))) { return {}; }\
-    template<template <typename...> class Final, template<INTERNAL_CATCH_REMOVE_PARENS(signature)> class...Containers, typename...Types>\
-    constexpr auto create(TypeList<Types...>) noexcept -> decltype(append(Final<>{}, rewrap<Containers>(Types{}...)...)) { return {}; }
-
-#define INTERNAL_CATCH_DECLARE_SIG_TEST0(TestName)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST1(TestName, signature)\
-    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
-    static void TestName()
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_X(TestName, signature, ...)\
-    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
-    static void TestName()
-
-#define INTERNAL_CATCH_DEFINE_SIG_TEST0(TestName)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST1(TestName, signature)\
-    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
-    static void TestName()
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_X(TestName, signature,...)\
-    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
-    static void TestName()
-
-#define INTERNAL_CATCH_NTTP_REGISTER0(TestFunc, signature)\
-    template<typename Type>\
-    void reg_test(TypeList<Type>, Catch::NameAndTags nameAndTags)\
-    {\
-        Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<Type>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
-    }
-
-#define INTERNAL_CATCH_NTTP_REGISTER(TestFunc, signature, ...)\
-    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
-    void reg_test(Nttp<__VA_ARGS__>, Catch::NameAndTags nameAndTags)\
-    {\
-        Catch::AutoReg( Catch::makeTestInvoker(&TestFunc<__VA_ARGS__>), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), nameAndTags);\
-    }
-
-#define INTERNAL_CATCH_NTTP_REGISTER_METHOD0(TestName, signature, ...)\
-    template<typename Type>\
-    void reg_test(TypeList<Type>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
-    {\
-        Catch::AutoReg( Catch::makeTestInvoker(&TestName<Type>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
-    }
-
-#define INTERNAL_CATCH_NTTP_REGISTER_METHOD(TestName, signature, ...)\
-    template<INTERNAL_CATCH_REMOVE_PARENS(signature)>\
-    void reg_test(Nttp<__VA_ARGS__>, Catch::StringRef className, Catch::NameAndTags nameAndTags)\
-    {\
-        Catch::AutoReg( Catch::makeTestInvoker(&TestName<__VA_ARGS__>::test), CATCH_INTERNAL_LINEINFO, className, nameAndTags);\
-    }
-
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0(TestName, ClassName)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1(TestName, ClassName, signature)\
-    template<typename TestType> \
-    struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<TestType> { \
-        void test();\
-    }
-
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X(TestName, ClassName, signature, ...)\
-    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
-    struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName)<__VA_ARGS__> { \
-        void test();\
-    }
-
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0(TestName)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1(TestName, signature)\
-    template<typename TestType> \
-    void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<TestType>::test()
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X(TestName, signature, ...)\
-    template<INTERNAL_CATCH_REMOVE_PARENS(signature)> \
-    void INTERNAL_CATCH_MAKE_NAMESPACE(TestName)::TestName<__VA_ARGS__>::test()
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define INTERNAL_CATCH_NTTP_0
-#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1(__VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_1( __VA_ARGS__),INTERNAL_CATCH_NTTP_1( __VA_ARGS__), INTERNAL_CATCH_NTTP_0)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__)
-#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__)
-#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__)
-#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__)
-#else
-#define INTERNAL_CATCH_NTTP_0(signature)
-#define INTERNAL_CATCH_NTTP_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_1,INTERNAL_CATCH_NTTP_1, INTERNAL_CATCH_NTTP_0)( __VA_ARGS__))
-#define INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD1, INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X,INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD_X, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD1, INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD0)(TestName, ClassName, __VA_ARGS__))
-#define INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD, INTERNAL_CATCH_NTTP_REGISTER_METHOD0, INTERNAL_CATCH_NTTP_REGISTER_METHOD0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_NTTP_REG_GEN(TestFunc, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER, INTERNAL_CATCH_NTTP_REGISTER0, INTERNAL_CATCH_NTTP_REGISTER0)(TestFunc, __VA_ARGS__))
-#define INTERNAL_CATCH_DEFINE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DEFINE_SIG_TEST1, INTERNAL_CATCH_DEFINE_SIG_TEST0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_DECLARE_SIG_TEST(TestName, ...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL( "dummy", __VA_ARGS__, INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DEFINE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X,INTERNAL_CATCH_DECLARE_SIG_TEST_X, INTERNAL_CATCH_DECLARE_SIG_TEST1, INTERNAL_CATCH_DECLARE_SIG_TEST0)(TestName, __VA_ARGS__))
-#define INTERNAL_CATCH_REMOVE_PARENS_GEN(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_VA_NARGS_IMPL(__VA_ARGS__, INTERNAL_CATCH_REMOVE_PARENS_11_ARG,INTERNAL_CATCH_REMOVE_PARENS_10_ARG,INTERNAL_CATCH_REMOVE_PARENS_9_ARG,INTERNAL_CATCH_REMOVE_PARENS_8_ARG,INTERNAL_CATCH_REMOVE_PARENS_7_ARG,INTERNAL_CATCH_REMOVE_PARENS_6_ARG,INTERNAL_CATCH_REMOVE_PARENS_5_ARG,INTERNAL_CATCH_REMOVE_PARENS_4_ARG,INTERNAL_CATCH_REMOVE_PARENS_3_ARG,INTERNAL_CATCH_REMOVE_PARENS_2_ARG,INTERNAL_CATCH_REMOVE_PARENS_1_ARG)(__VA_ARGS__))
-#endif
-
-// end catch_preprocessor.hpp
-// start catch_meta.hpp
-
-
-#include <type_traits>
-
-namespace Catch {
-    template<typename T>
-    struct always_false : std::false_type {};
-} // namespace Catch
-
-// end catch_meta.hpp
-namespace Catch {
-
-template<typename C>
-class TestInvokerAsMethod : public ITestInvoker {
-    void (C::*m_testAsMethod)();
-public:
-    TestInvokerAsMethod( void (C::*testAsMethod)() ) noexcept : m_testAsMethod( testAsMethod ) {}
-
-    void invoke() const override {
-        C obj;
-        (obj.*m_testAsMethod)();
-    }
-};
-
-auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker*;
-
-template<typename C>
-auto makeTestInvoker( void (C::*testAsMethod)() ) noexcept -> ITestInvoker* {
-    return new(std::nothrow) TestInvokerAsMethod<C>( testAsMethod );
-}
-
-struct NameAndTags {
-    NameAndTags( StringRef const& name_ = StringRef(), StringRef const& tags_ = StringRef() ) noexcept;
-    StringRef name;
-    StringRef tags;
-};
-
-struct AutoReg : NonCopyable {
-    AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept;
-    ~AutoReg();
-};
-
-} // end namespace Catch
-
-#if defined(CATCH_CONFIG_DISABLE)
-    #define INTERNAL_CATCH_TESTCASE_NO_REGISTRATION( TestName, ... ) \
-        static void TestName()
-    #define INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION( TestName, ClassName, ... ) \
-        namespace{                        \
-            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
-                void test();              \
-            };                            \
-        }                                 \
-        void TestName::test()
-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( TestName, TestFunc, Name, Tags, Signature, ... )  \
-        INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature))
-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... )    \
-        namespace{                                                                                  \
-            namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) {                                      \
-            INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
-        }                                                                                           \
-        }                                                                                           \
-        INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
-
-    #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
-            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )
-    #else
-        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(Name, Tags, ...) \
-            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
-    #endif
-
-    #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
-            INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )
-    #else
-        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(Name, Tags, Signature, ...) \
-            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
-    #endif
-
-    #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
-            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
-    #else
-        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION( ClassName, Name, Tags,... ) \
-            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
-    #endif
-
-    #ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
-            INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
-    #else
-        #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION( ClassName, Name, Tags, Signature, ... ) \
-            INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
-    #endif
-#endif
-
-    ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_TESTCASE2( TestName, ... ) \
-        static void TestName(); \
-        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
-        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &TestName ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
-        static void TestName()
-    #define INTERNAL_CATCH_TESTCASE( ... ) \
-        INTERNAL_CATCH_TESTCASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), __VA_ARGS__ )
-
-    ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_METHOD_AS_TEST_CASE( QualifiedMethod, ... ) \
-        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
-        namespace{ Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( &QualifiedMethod ), CATCH_INTERNAL_LINEINFO, "&" #QualifiedMethod, Catch::NameAndTags{ __VA_ARGS__ } ); } /* NOLINT */ \
-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
-
-    ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_TEST_CASE_METHOD2( TestName, ClassName, ... )\
-        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
-        namespace{ \
-            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName) { \
-                void test(); \
-            }; \
-            Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar ) ( Catch::makeTestInvoker( &TestName::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
-        } \
-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
-        void TestName::test()
-    #define INTERNAL_CATCH_TEST_CASE_METHOD( ClassName, ... ) \
-        INTERNAL_CATCH_TEST_CASE_METHOD2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), ClassName, __VA_ARGS__ )
-
-    ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_REGISTER_TESTCASE( Function, ... ) \
-        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
-        Catch::AutoReg INTERNAL_CATCH_UNIQUE_NAME( autoRegistrar )( Catch::makeTestInvoker( Function ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ __VA_ARGS__ } ); /* NOLINT */ \
-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
-
-    ///////////////////////////////////////////////////////////////////////////////
-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_2(TestName, TestFunc, Name, Tags, Signature, ... )\
-        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
-        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
-        INTERNAL_CATCH_DECLARE_SIG_TEST(TestFunc, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
-        namespace {\
-        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){\
-            INTERNAL_CATCH_TYPE_GEN\
-            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
-            INTERNAL_CATCH_NTTP_REG_GEN(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))\
-            template<typename...Types> \
-            struct TestName{\
-                TestName(){\
-                    int index = 0;                                    \
-                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
-                    using expander = int[];\
-                    (void)expander{(reg_test(Types{}, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++, 0)... };/* NOLINT */ \
-                }\
-            };\
-            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
-            TestName<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
-            return 0;\
-        }();\
-        }\
-        }\
-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
-        CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
-        INTERNAL_CATCH_DEFINE_SIG_TEST(TestFunc,INTERNAL_CATCH_REMOVE_PARENS(Signature))
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
-        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ )
-#else
-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE(Name, Tags, ...) \
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename TestType, __VA_ARGS__ ) )
-#endif
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
-        INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ )
-#else
-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG(Name, Tags, Signature, ...) \
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
-#endif
-
-    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(TestName, TestFuncName, Name, Tags, Signature, TmplTypes, TypesList) \
-        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS                      \
-        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS                \
-        template<typename TestType> static void TestFuncName();       \
-        namespace {\
-        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName) {                                     \
-            INTERNAL_CATCH_TYPE_GEN                                                  \
-            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))         \
-            template<typename... Types>                               \
-            struct TestName {                                         \
-                void reg_tests() {                                          \
-                    int index = 0;                                    \
-                    using expander = int[];                           \
-                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
-                    constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
-                    constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
-                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestFuncName<Types> ), CATCH_INTERNAL_LINEINFO, Catch::StringRef(), Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */\
-                }                                                     \
-            };                                                        \
-            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){ \
-                using TestInit = decltype(create<TestName, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>{})); \
-                TestInit t;                                           \
-                t.reg_tests();                                        \
-                return 0;                                             \
-            }();                                                      \
-        }                                                             \
-        }                                                             \
-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS                    \
-        CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS              \
-        template<typename TestType>                                   \
-        static void TestFuncName()
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
-        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T,__VA_ARGS__)
-#else
-    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE(Name, Tags, ...)\
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, typename T, __VA_ARGS__ ) )
-#endif
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
-        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__)
-#else
-    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG(Name, Tags, Signature, ...)\
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), Name, Tags, Signature, __VA_ARGS__ ) )
-#endif
-
-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( TestNameClass, TestName, ClassName, Name, Tags, Signature, ... ) \
-        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
-        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
-        namespace {\
-        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestName){ \
-            INTERNAL_CATCH_TYPE_GEN\
-            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
-            INTERNAL_CATCH_DECLARE_SIG_TEST_METHOD(TestName, ClassName, INTERNAL_CATCH_REMOVE_PARENS(Signature));\
-            INTERNAL_CATCH_NTTP_REG_METHOD_GEN(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))\
-            template<typename...Types> \
-            struct TestNameClass{\
-                TestNameClass(){\
-                    int index = 0;                                    \
-                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, __VA_ARGS__)};\
-                    using expander = int[];\
-                    (void)expander{(reg_test(Types{}, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index]), Tags } ), index++, 0)... };/* NOLINT */ \
-                }\
-            };\
-            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
-                TestNameClass<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(__VA_ARGS__)>();\
-                return 0;\
-        }();\
-        }\
-        }\
-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS\
-        CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS\
-        INTERNAL_CATCH_DEFINE_SIG_TEST_METHOD(TestName, INTERNAL_CATCH_REMOVE_PARENS(Signature))
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
-        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ )
-#else
-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( ClassName, Name, Tags,... ) \
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, typename T, __VA_ARGS__ ) )
-#endif
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
-        INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ )
-#else
-    #define INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... ) \
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____C_L_A_S_S____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ) , ClassName, Name, Tags, Signature, __VA_ARGS__ ) )
-#endif
-
-    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2(TestNameClass, TestName, ClassName, Name, Tags, Signature, TmplTypes, TypesList)\
-        CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
-        CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \
-        template<typename TestType> \
-            struct TestName : INTERNAL_CATCH_REMOVE_PARENS(ClassName <TestType>) { \
-                void test();\
-            };\
-        namespace {\
-        namespace INTERNAL_CATCH_MAKE_NAMESPACE(TestNameClass) {\
-            INTERNAL_CATCH_TYPE_GEN                  \
-            INTERNAL_CATCH_NTTP_GEN(INTERNAL_CATCH_REMOVE_PARENS(Signature))\
-            template<typename...Types>\
-            struct TestNameClass{\
-                void reg_tests(){\
-                    int index = 0;\
-                    using expander = int[];\
-                    constexpr char const* tmpl_types[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes))};\
-                    constexpr char const* types_list[] = {CATCH_REC_LIST(INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS, INTERNAL_CATCH_REMOVE_PARENS(TypesList))};\
-                    constexpr auto num_types = sizeof(types_list) / sizeof(types_list[0]);\
-                    (void)expander{(Catch::AutoReg( Catch::makeTestInvoker( &TestName<Types>::test ), CATCH_INTERNAL_LINEINFO, #ClassName, Catch::NameAndTags{ Name " - " + std::string(tmpl_types[index / num_types]) + "<" + std::string(types_list[index % num_types]) + ">", Tags } ), index++, 0)... };/* NOLINT */ \
-                }\
-            };\
-            static int INTERNAL_CATCH_UNIQUE_NAME( globalRegistrar ) = [](){\
-                using TestInit = decltype(create<TestNameClass, INTERNAL_CATCH_REMOVE_PARENS(TmplTypes)>(TypeList<INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(INTERNAL_CATCH_REMOVE_PARENS(TypesList))>{}));\
-                TestInit t;\
-                t.reg_tests();\
-                return 0;\
-            }(); \
-        }\
-        }\
-        CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
-        CATCH_INTERNAL_UNSUPPRESS_ZERO_VARIADIC_WARNINGS \
-        template<typename TestType> \
-        void TestName<TestType>::test()
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
-        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T, __VA_ARGS__ )
-#else
-    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( ClassName, Name, Tags, ... )\
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, typename T,__VA_ARGS__ ) )
-#endif
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
-        INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature, __VA_ARGS__ )
-#else
-    #define INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( ClassName, Name, Tags, Signature, ... )\
-        INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_2( INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____ ), INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_M_P_L_A_T_E____T_E_S_T____F_U_N_C____ ), ClassName, Name, Tags, Signature,__VA_ARGS__ ) )
-#endif
-
-// end catch_test_registry.h
-// start catch_capture.hpp
-
-// start catch_assertionhandler.h
-
-// start catch_assertioninfo.h
-
-// start catch_result_type.h
-
-namespace Catch {
-
-    // ResultWas::OfType enum
-    struct ResultWas { enum OfType {
-        Unknown = -1,
-        Ok = 0,
-        Info = 1,
-        Warning = 2,
-
-        FailureBit = 0x10,
-
-        ExpressionFailed = FailureBit | 1,
-        ExplicitFailure = FailureBit | 2,
-
-        Exception = 0x100 | FailureBit,
-
-        ThrewException = Exception | 1,
-        DidntThrowException = Exception | 2,
-
-        FatalErrorCondition = 0x200 | FailureBit
-
-    }; };
-
-    bool isOk( ResultWas::OfType resultType );
-    bool isJustInfo( int flags );
-
-    // ResultDisposition::Flags enum
-    struct ResultDisposition { enum Flags {
-        Normal = 0x01,
-
-        ContinueOnFailure = 0x02,   // Failures fail test, but execution continues
-        FalseTest = 0x04,           // Prefix expression with !
-        SuppressFail = 0x08         // Failures are reported but do not fail the test
-    }; };
-
-    ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs );
-
-    bool shouldContinueOnFailure( int flags );
-    inline bool isFalseTest( int flags ) { return ( flags & ResultDisposition::FalseTest ) != 0; }
-    bool shouldSuppressFailure( int flags );
-
-} // end namespace Catch
-
-// end catch_result_type.h
-namespace Catch {
-
-    struct AssertionInfo
-    {
-        StringRef macroName;
-        SourceLineInfo lineInfo;
-        StringRef capturedExpression;
-        ResultDisposition::Flags resultDisposition;
-
-        // We want to delete this constructor but a compiler bug in 4.8 means
-        // the struct is then treated as non-aggregate
-        //AssertionInfo() = delete;
-    };
-
-} // end namespace Catch
-
-// end catch_assertioninfo.h
-// start catch_decomposer.h
-
-// start catch_tostring.h
-
-#include <vector>
-#include <cstddef>
-#include <type_traits>
-#include <string>
-// start catch_stream.h
-
-#include <iosfwd>
-#include <cstddef>
-#include <ostream>
-
-namespace Catch {
-
-    std::ostream& cout();
-    std::ostream& cerr();
-    std::ostream& clog();
-
-    class StringRef;
-
-    struct IStream {
-        virtual ~IStream();
-        virtual std::ostream& stream() const = 0;
-    };
-
-    auto makeStream( StringRef const &filename ) -> IStream const*;
-
-    class ReusableStringStream {
-        std::size_t m_index;
-        std::ostream* m_oss;
-    public:
-        ReusableStringStream();
-        ~ReusableStringStream();
-
-        auto str() const -> std::string;
-
-        template<typename T>
-        auto operator << ( T const& value ) -> ReusableStringStream& {
-            *m_oss << value;
-            return *this;
-        }
-        auto get() -> std::ostream& { return *m_oss; }
-    };
-}
-
-// end catch_stream.h
-// start catch_interfaces_enum_values_registry.h
-
-#include <vector>
-
-namespace Catch {
-
-    namespace Detail {
-        struct EnumInfo {
-            StringRef m_name;
-            std::vector<std::pair<int, std::string>> m_values;
-
-            ~EnumInfo();
-
-            StringRef lookup( int value ) const;
-        };
-    } // namespace Detail
-
-    struct IMutableEnumValuesRegistry {
-        virtual ~IMutableEnumValuesRegistry();
-
-        virtual Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values ) = 0;
-
-        template<typename E>
-        Detail::EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::initializer_list<E> values ) {
-            std::vector<int> intValues;
-            intValues.reserve( values.size() );
-            for( auto enumValue : values )
-                intValues.push_back( static_cast<int>( enumValue ) );
-            return registerEnum( enumName, allEnums, intValues );
-        }
-    };
-
-} // Catch
-
-// end catch_interfaces_enum_values_registry.h
-
-#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
-#include <string_view>
-#endif
-
-#ifdef __OBJC__
-// start catch_objc_arc.hpp
-
-#import <Foundation/Foundation.h>
-
-#ifdef __has_feature
-#define CATCH_ARC_ENABLED __has_feature(objc_arc)
-#else
-#define CATCH_ARC_ENABLED 0
-#endif
-
-void arcSafeRelease( NSObject* obj );
-id performOptionalSelector( id obj, SEL sel );
-
-#if !CATCH_ARC_ENABLED
-inline void arcSafeRelease( NSObject* obj ) {
-    [obj release];
-}
-inline id performOptionalSelector( id obj, SEL sel ) {
-    if( [obj respondsToSelector: sel] )
-        return [obj performSelector: sel];
-    return nil;
-}
-#define CATCH_UNSAFE_UNRETAINED
-#define CATCH_ARC_STRONG
-#else
-inline void arcSafeRelease( NSObject* ){}
-inline id performOptionalSelector( id obj, SEL sel ) {
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Warc-performSelector-leaks"
-#endif
-    if( [obj respondsToSelector: sel] )
-        return [obj performSelector: sel];
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-    return nil;
-}
-#define CATCH_UNSAFE_UNRETAINED __unsafe_unretained
-#define CATCH_ARC_STRONG __strong
-#endif
-
-// end catch_objc_arc.hpp
-#endif
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4180) // We attempt to stream a function (address) by const&, which MSVC complains about but is harmless
-#endif
-
-namespace Catch {
-    namespace Detail {
-
-        extern const std::string unprintableString;
-
-        std::string rawMemoryToString( const void *object, std::size_t size );
-
-        template<typename T>
-        std::string rawMemoryToString( const T& object ) {
-          return rawMemoryToString( &object, sizeof(object) );
-        }
-
-        template<typename T>
-        class IsStreamInsertable {
-            template<typename SS, typename TT>
-            static auto test(int)
-                -> decltype(std::declval<SS&>() << std::declval<TT>(), std::true_type());
-
-            template<typename, typename>
-            static auto test(...)->std::false_type;
-
-        public:
-            static const bool value = decltype(test<std::ostream, const T&>(0))::value;
-        };
-
-        template<typename E>
-        std::string convertUnknownEnumToString( E e );
-
-        template<typename T>
-        typename std::enable_if<
-            !std::is_enum<T>::value && !std::is_base_of<std::exception, T>::value,
-        std::string>::type convertUnstreamable( T const& ) {
-            return Detail::unprintableString;
-        }
-        template<typename T>
-        typename std::enable_if<
-            !std::is_enum<T>::value && std::is_base_of<std::exception, T>::value,
-         std::string>::type convertUnstreamable(T const& ex) {
-            return ex.what();
-        }
-
-        template<typename T>
-        typename std::enable_if<
-            std::is_enum<T>::value
-        , std::string>::type convertUnstreamable( T const& value ) {
-            return convertUnknownEnumToString( value );
-        }
-
-#if defined(_MANAGED)
-        //! Convert a CLR string to a utf8 std::string
-        template<typename T>
-        std::string clrReferenceToString( T^ ref ) {
-            if (ref == nullptr)
-                return std::string("null");
-            auto bytes = System::Text::Encoding::UTF8->GetBytes(ref->ToString());
-            cli::pin_ptr<System::Byte> p = &bytes[0];
-            return std::string(reinterpret_cast<char const *>(p), bytes->Length);
-        }
-#endif
-
-    } // namespace Detail
-
-    // If we decide for C++14, change these to enable_if_ts
-    template <typename T, typename = void>
-    struct StringMaker {
-        template <typename Fake = T>
-        static
-        typename std::enable_if<::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
-            convert(const Fake& value) {
-                ReusableStringStream rss;
-                // NB: call using the function-like syntax to avoid ambiguity with
-                // user-defined templated operator<< under clang.
-                rss.operator<<(value);
-                return rss.str();
-        }
-
-        template <typename Fake = T>
-        static
-        typename std::enable_if<!::Catch::Detail::IsStreamInsertable<Fake>::value, std::string>::type
-            convert( const Fake& value ) {
-#if !defined(CATCH_CONFIG_FALLBACK_STRINGIFIER)
-            return Detail::convertUnstreamable(value);
-#else
-            return CATCH_CONFIG_FALLBACK_STRINGIFIER(value);
-#endif
-        }
-    };
-
-    namespace Detail {
-
-        // This function dispatches all stringification requests inside of Catch.
-        // Should be preferably called fully qualified, like ::Catch::Detail::stringify
-        template <typename T>
-        std::string stringify(const T& e) {
-            return ::Catch::StringMaker<typename std::remove_cv<typename std::remove_reference<T>::type>::type>::convert(e);
-        }
-
-        template<typename E>
-        std::string convertUnknownEnumToString( E e ) {
-            return ::Catch::Detail::stringify(static_cast<typename std::underlying_type<E>::type>(e));
-        }
-
-#if defined(_MANAGED)
-        template <typename T>
-        std::string stringify( T^ e ) {
-            return ::Catch::StringMaker<T^>::convert(e);
-        }
-#endif
-
-    } // namespace Detail
-
-    // Some predefined specializations
-
-    template<>
-    struct StringMaker<std::string> {
-        static std::string convert(const std::string& str);
-    };
-
-#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
-    template<>
-    struct StringMaker<std::string_view> {
-        static std::string convert(std::string_view str);
-    };
-#endif
-
-    template<>
-    struct StringMaker<char const *> {
-        static std::string convert(char const * str);
-    };
-    template<>
-    struct StringMaker<char *> {
-        static std::string convert(char * str);
-    };
-
-#ifdef CATCH_CONFIG_WCHAR
-    template<>
-    struct StringMaker<std::wstring> {
-        static std::string convert(const std::wstring& wstr);
-    };
-
-# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
-    template<>
-    struct StringMaker<std::wstring_view> {
-        static std::string convert(std::wstring_view str);
-    };
-# endif
-
-    template<>
-    struct StringMaker<wchar_t const *> {
-        static std::string convert(wchar_t const * str);
-    };
-    template<>
-    struct StringMaker<wchar_t *> {
-        static std::string convert(wchar_t * str);
-    };
-#endif
-
-    // TBD: Should we use `strnlen` to ensure that we don't go out of the buffer,
-    //      while keeping string semantics?
-    template<int SZ>
-    struct StringMaker<char[SZ]> {
-        static std::string convert(char const* str) {
-            return ::Catch::Detail::stringify(std::string{ str });
-        }
-    };
-    template<int SZ>
-    struct StringMaker<signed char[SZ]> {
-        static std::string convert(signed char const* str) {
-            return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
-        }
-    };
-    template<int SZ>
-    struct StringMaker<unsigned char[SZ]> {
-        static std::string convert(unsigned char const* str) {
-            return ::Catch::Detail::stringify(std::string{ reinterpret_cast<char const *>(str) });
-        }
-    };
-
-    template<>
-    struct StringMaker<int> {
-        static std::string convert(int value);
-    };
-    template<>
-    struct StringMaker<long> {
-        static std::string convert(long value);
-    };
-    template<>
-    struct StringMaker<long long> {
-        static std::string convert(long long value);
-    };
-    template<>
-    struct StringMaker<unsigned int> {
-        static std::string convert(unsigned int value);
-    };
-    template<>
-    struct StringMaker<unsigned long> {
-        static std::string convert(unsigned long value);
-    };
-    template<>
-    struct StringMaker<unsigned long long> {
-        static std::string convert(unsigned long long value);
-    };
-
-    template<>
-    struct StringMaker<bool> {
-        static std::string convert(bool b);
-    };
-
-    template<>
-    struct StringMaker<char> {
-        static std::string convert(char c);
-    };
-    template<>
-    struct StringMaker<signed char> {
-        static std::string convert(signed char c);
-    };
-    template<>
-    struct StringMaker<unsigned char> {
-        static std::string convert(unsigned char c);
-    };
-
-    template<>
-    struct StringMaker<std::nullptr_t> {
-        static std::string convert(std::nullptr_t);
-    };
-
-    template<>
-    struct StringMaker<float> {
-        static std::string convert(float value);
-        static int precision;
-    };
-
-    template<>
-    struct StringMaker<double> {
-        static std::string convert(double value);
-        static int precision;
-    };
-
-    template <typename T>
-    struct StringMaker<T*> {
-        template <typename U>
-        static std::string convert(U* p) {
-            if (p) {
-                return ::Catch::Detail::rawMemoryToString(p);
-            } else {
-                return "nullptr";
-            }
-        }
-    };
-
-    template <typename R, typename C>
-    struct StringMaker<R C::*> {
-        static std::string convert(R C::* p) {
-            if (p) {
-                return ::Catch::Detail::rawMemoryToString(p);
-            } else {
-                return "nullptr";
-            }
-        }
-    };
-
-#if defined(_MANAGED)
-    template <typename T>
-    struct StringMaker<T^> {
-        static std::string convert( T^ ref ) {
-            return ::Catch::Detail::clrReferenceToString(ref);
-        }
-    };
-#endif
-
-    namespace Detail {
-        template<typename InputIterator>
-        std::string rangeToString(InputIterator first, InputIterator last) {
-            ReusableStringStream rss;
-            rss << "{ ";
-            if (first != last) {
-                rss << ::Catch::Detail::stringify(*first);
-                for (++first; first != last; ++first)
-                    rss << ", " << ::Catch::Detail::stringify(*first);
-            }
-            rss << " }";
-            return rss.str();
-        }
-    }
-
-#ifdef __OBJC__
-    template<>
-    struct StringMaker<NSString*> {
-        static std::string convert(NSString * nsstring) {
-            if (!nsstring)
-                return "nil";
-            return std::string("@") + [nsstring UTF8String];
-        }
-    };
-    template<>
-    struct StringMaker<NSObject*> {
-        static std::string convert(NSObject* nsObject) {
-            return ::Catch::Detail::stringify([nsObject description]);
-        }
-
-    };
-    namespace Detail {
-        inline std::string stringify( NSString* nsstring ) {
-            return StringMaker<NSString*>::convert( nsstring );
-        }
-
-    } // namespace Detail
-#endif // __OBJC__
-
-} // namespace Catch
-
-//////////////////////////////////////////////////////
-// Separate std-lib types stringification, so it can be selectively enabled
-// This means that we do not bring in
-
-#if defined(CATCH_CONFIG_ENABLE_ALL_STRINGMAKERS)
-#  define CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
-#  define CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
-#  define CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
-#  define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
-#  define CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
-#endif
-
-// Separate std::pair specialization
-#if defined(CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER)
-#include <utility>
-namespace Catch {
-    template<typename T1, typename T2>
-    struct StringMaker<std::pair<T1, T2> > {
-        static std::string convert(const std::pair<T1, T2>& pair) {
-            ReusableStringStream rss;
-            rss << "{ "
-                << ::Catch::Detail::stringify(pair.first)
-                << ", "
-                << ::Catch::Detail::stringify(pair.second)
-                << " }";
-            return rss.str();
-        }
-    };
-}
-#endif // CATCH_CONFIG_ENABLE_PAIR_STRINGMAKER
-
-#if defined(CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_OPTIONAL)
-#include <optional>
-namespace Catch {
-    template<typename T>
-    struct StringMaker<std::optional<T> > {
-        static std::string convert(const std::optional<T>& optional) {
-            ReusableStringStream rss;
-            if (optional.has_value()) {
-                rss << ::Catch::Detail::stringify(*optional);
-            } else {
-                rss << "{ }";
-            }
-            return rss.str();
-        }
-    };
-}
-#endif // CATCH_CONFIG_ENABLE_OPTIONAL_STRINGMAKER
-
-// Separate std::tuple specialization
-#if defined(CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER)
-#include <tuple>
-namespace Catch {
-    namespace Detail {
-        template<
-            typename Tuple,
-            std::size_t N = 0,
-            bool = (N < std::tuple_size<Tuple>::value)
-            >
-            struct TupleElementPrinter {
-            static void print(const Tuple& tuple, std::ostream& os) {
-                os << (N ? ", " : " ")
-                    << ::Catch::Detail::stringify(std::get<N>(tuple));
-                TupleElementPrinter<Tuple, N + 1>::print(tuple, os);
-            }
-        };
-
-        template<
-            typename Tuple,
-            std::size_t N
-        >
-            struct TupleElementPrinter<Tuple, N, false> {
-            static void print(const Tuple&, std::ostream&) {}
-        };
-
-    }
-
-    template<typename ...Types>
-    struct StringMaker<std::tuple<Types...>> {
-        static std::string convert(const std::tuple<Types...>& tuple) {
-            ReusableStringStream rss;
-            rss << '{';
-            Detail::TupleElementPrinter<std::tuple<Types...>>::print(tuple, rss.get());
-            rss << " }";
-            return rss.str();
-        }
-    };
-}
-#endif // CATCH_CONFIG_ENABLE_TUPLE_STRINGMAKER
-
-#if defined(CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER) && defined(CATCH_CONFIG_CPP17_VARIANT)
-#include <variant>
-namespace Catch {
-    template<>
-    struct StringMaker<std::monostate> {
-        static std::string convert(const std::monostate&) {
-            return "{ }";
-        }
-    };
-
-    template<typename... Elements>
-    struct StringMaker<std::variant<Elements...>> {
-        static std::string convert(const std::variant<Elements...>& variant) {
-            if (variant.valueless_by_exception()) {
-                return "{valueless variant}";
-            } else {
-                return std::visit(
-                    [](const auto& value) {
-                        return ::Catch::Detail::stringify(value);
-                    },
-                    variant
-                );
-            }
-        }
-    };
-}
-#endif // CATCH_CONFIG_ENABLE_VARIANT_STRINGMAKER
-
-namespace Catch {
-    struct not_this_one {}; // Tag type for detecting which begin/ end are being selected
-
-    // Import begin/ end from std here so they are considered alongside the fallback (...) overloads in this namespace
-    using std::begin;
-    using std::end;
-
-    not_this_one begin( ... );
-    not_this_one end( ... );
-
-    template <typename T>
-    struct is_range {
-        static const bool value =
-            !std::is_same<decltype(begin(std::declval<T>())), not_this_one>::value &&
-            !std::is_same<decltype(end(std::declval<T>())), not_this_one>::value;
-    };
-
-#if defined(_MANAGED) // Managed types are never ranges
-    template <typename T>
-    struct is_range<T^> {
-        static const bool value = false;
-    };
-#endif
-
-    template<typename Range>
-    std::string rangeToString( Range const& range ) {
-        return ::Catch::Detail::rangeToString( begin( range ), end( range ) );
-    }
-
-    // Handle vector<bool> specially
-    template<typename Allocator>
-    std::string rangeToString( std::vector<bool, Allocator> const& v ) {
-        ReusableStringStream rss;
-        rss << "{ ";
-        bool first = true;
-        for( bool b : v ) {
-            if( first )
-                first = false;
-            else
-                rss << ", ";
-            rss << ::Catch::Detail::stringify( b );
-        }
-        rss << " }";
-        return rss.str();
-    }
-
-    template<typename R>
-    struct StringMaker<R, typename std::enable_if<is_range<R>::value && !::Catch::Detail::IsStreamInsertable<R>::value>::type> {
-        static std::string convert( R const& range ) {
-            return rangeToString( range );
-        }
-    };
-
-    template <typename T, int SZ>
-    struct StringMaker<T[SZ]> {
-        static std::string convert(T const(&arr)[SZ]) {
-            return rangeToString(arr);
-        }
-    };
-
-} // namespace Catch
-
-// Separate std::chrono::duration specialization
-#if defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
-#include <ctime>
-#include <ratio>
-#include <chrono>
-
-namespace Catch {
-
-template <class Ratio>
-struct ratio_string {
-    static std::string symbol();
-};
-
-template <class Ratio>
-std::string ratio_string<Ratio>::symbol() {
-    Catch::ReusableStringStream rss;
-    rss << '[' << Ratio::num << '/'
-        << Ratio::den << ']';
-    return rss.str();
-}
-template <>
-struct ratio_string<std::atto> {
-    static std::string symbol();
-};
-template <>
-struct ratio_string<std::femto> {
-    static std::string symbol();
-};
-template <>
-struct ratio_string<std::pico> {
-    static std::string symbol();
-};
-template <>
-struct ratio_string<std::nano> {
-    static std::string symbol();
-};
-template <>
-struct ratio_string<std::micro> {
-    static std::string symbol();
-};
-template <>
-struct ratio_string<std::milli> {
-    static std::string symbol();
-};
-
-    ////////////
-    // std::chrono::duration specializations
-    template<typename Value, typename Ratio>
-    struct StringMaker<std::chrono::duration<Value, Ratio>> {
-        static std::string convert(std::chrono::duration<Value, Ratio> const& duration) {
-            ReusableStringStream rss;
-            rss << duration.count() << ' ' << ratio_string<Ratio>::symbol() << 's';
-            return rss.str();
-        }
-    };
-    template<typename Value>
-    struct StringMaker<std::chrono::duration<Value, std::ratio<1>>> {
-        static std::string convert(std::chrono::duration<Value, std::ratio<1>> const& duration) {
-            ReusableStringStream rss;
-            rss << duration.count() << " s";
-            return rss.str();
-        }
-    };
-    template<typename Value>
-    struct StringMaker<std::chrono::duration<Value, std::ratio<60>>> {
-        static std::string convert(std::chrono::duration<Value, std::ratio<60>> const& duration) {
-            ReusableStringStream rss;
-            rss << duration.count() << " m";
-            return rss.str();
-        }
-    };
-    template<typename Value>
-    struct StringMaker<std::chrono::duration<Value, std::ratio<3600>>> {
-        static std::string convert(std::chrono::duration<Value, std::ratio<3600>> const& duration) {
-            ReusableStringStream rss;
-            rss << duration.count() << " h";
-            return rss.str();
-        }
-    };
-
-    ////////////
-    // std::chrono::time_point specialization
-    // Generic time_point cannot be specialized, only std::chrono::time_point<system_clock>
-    template<typename Clock, typename Duration>
-    struct StringMaker<std::chrono::time_point<Clock, Duration>> {
-        static std::string convert(std::chrono::time_point<Clock, Duration> const& time_point) {
-            return ::Catch::Detail::stringify(time_point.time_since_epoch()) + " since epoch";
-        }
-    };
-    // std::chrono::time_point<system_clock> specialization
-    template<typename Duration>
-    struct StringMaker<std::chrono::time_point<std::chrono::system_clock, Duration>> {
-        static std::string convert(std::chrono::time_point<std::chrono::system_clock, Duration> const& time_point) {
-            auto converted = std::chrono::system_clock::to_time_t(time_point);
-
-#ifdef _MSC_VER
-            std::tm timeInfo = {};
-            gmtime_s(&timeInfo, &converted);
-#else
-            std::tm* timeInfo = std::gmtime(&converted);
-#endif
-
-            auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
-            char timeStamp[timeStampSize];
-            const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
-
-#ifdef _MSC_VER
-            std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
-#else
-            std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
-#endif
-            return std::string(timeStamp);
-        }
-    };
-}
-#endif // CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
-
-#define INTERNAL_CATCH_REGISTER_ENUM( enumName, ... ) \
-namespace Catch { \
-    template<> struct StringMaker<enumName> { \
-        static std::string convert( enumName value ) { \
-            static const auto& enumInfo = ::Catch::getMutableRegistryHub().getMutableEnumValuesRegistry().registerEnum( #enumName, #__VA_ARGS__, { __VA_ARGS__ } ); \
-            return enumInfo.lookup( static_cast<int>( value ) ); \
-        } \
-    }; \
-}
-
-#define CATCH_REGISTER_ENUM( enumName, ... ) INTERNAL_CATCH_REGISTER_ENUM( enumName, __VA_ARGS__ )
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
-// end catch_tostring.h
-#include <iosfwd>
-
-#ifdef _MSC_VER
-#pragma warning(push)
-#pragma warning(disable:4389) // '==' : signed/unsigned mismatch
-#pragma warning(disable:4018) // more "signed/unsigned mismatch"
-#pragma warning(disable:4312) // Converting int to T* using reinterpret_cast (issue on x64 platform)
-#pragma warning(disable:4180) // qualifier applied to function type has no meaning
-#pragma warning(disable:4800) // Forcing result to true or false
-#endif
-
-namespace Catch {
-
-    struct ITransientExpression {
-        auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }
-        auto getResult() const -> bool { return m_result; }
-        virtual void streamReconstructedExpression( std::ostream &os ) const = 0;
-
-        ITransientExpression( bool isBinaryExpression, bool result )
-        :   m_isBinaryExpression( isBinaryExpression ),
-            m_result( result )
-        {}
-
-        // We don't actually need a virtual destructor, but many static analysers
-        // complain if it's not here :-(
-        virtual ~ITransientExpression();
-
-        bool m_isBinaryExpression;
-        bool m_result;
-
-    };
-
-    void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs );
-
-    template<typename LhsT, typename RhsT>
-    class BinaryExpr  : public ITransientExpression {
-        LhsT m_lhs;
-        StringRef m_op;
-        RhsT m_rhs;
-
-        void streamReconstructedExpression( std::ostream &os ) const override {
-            formatReconstructedExpression
-                    ( os, Catch::Detail::stringify( m_lhs ), m_op, Catch::Detail::stringify( m_rhs ) );
-        }
-
-    public:
-        BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )
-        :   ITransientExpression{ true, comparisonResult },
-            m_lhs( lhs ),
-            m_op( op ),
-            m_rhs( rhs )
-        {}
-
-        template<typename T>
-        auto operator && ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
-            static_assert(always_false<T>::value,
-            "chained comparisons are not supported inside assertions, "
-            "wrap the expression inside parentheses, or decompose it");
-        }
-
-        template<typename T>
-        auto operator || ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
-            static_assert(always_false<T>::value,
-            "chained comparisons are not supported inside assertions, "
-            "wrap the expression inside parentheses, or decompose it");
-        }
-
-        template<typename T>
-        auto operator == ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
-            static_assert(always_false<T>::value,
-            "chained comparisons are not supported inside assertions, "
-            "wrap the expression inside parentheses, or decompose it");
-        }
-
-        template<typename T>
-        auto operator != ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
-            static_assert(always_false<T>::value,
-            "chained comparisons are not supported inside assertions, "
-            "wrap the expression inside parentheses, or decompose it");
-        }
-
-        template<typename T>
-        auto operator > ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
-            static_assert(always_false<T>::value,
-            "chained comparisons are not supported inside assertions, "
-            "wrap the expression inside parentheses, or decompose it");
-        }
-
-        template<typename T>
-        auto operator < ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
-            static_assert(always_false<T>::value,
-            "chained comparisons are not supported inside assertions, "
-            "wrap the expression inside parentheses, or decompose it");
-        }
-
-        template<typename T>
-        auto operator >= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
-            static_assert(always_false<T>::value,
-            "chained comparisons are not supported inside assertions, "
-            "wrap the expression inside parentheses, or decompose it");
-        }
-
-        template<typename T>
-        auto operator <= ( T ) const -> BinaryExpr<LhsT, RhsT const&> const {
-            static_assert(always_false<T>::value,
-            "chained comparisons are not supported inside assertions, "
-            "wrap the expression inside parentheses, or decompose it");
-        }
-    };
-
-    template<typename LhsT>
-    class UnaryExpr : public ITransientExpression {
-        LhsT m_lhs;
-
-        void streamReconstructedExpression( std::ostream &os ) const override {
-            os << Catch::Detail::stringify( m_lhs );
-        }
-
-    public:
-        explicit UnaryExpr( LhsT lhs )
-        :   ITransientExpression{ false, static_cast<bool>(lhs) },
-            m_lhs( lhs )
-        {}
-    };
-
-    // Specialised comparison functions to handle equality comparisons between ints and pointers (NULL deduces as an int)
-    template<typename LhsT, typename RhsT>
-    auto compareEqual( LhsT const& lhs, RhsT const& rhs ) -> bool { return static_cast<bool>(lhs == rhs); }
-    template<typename T>
-    auto compareEqual( T* const& lhs, int rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }
-    template<typename T>
-    auto compareEqual( T* const& lhs, long rhs ) -> bool { return lhs == reinterpret_cast<void const*>( rhs ); }
-    template<typename T>
-    auto compareEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }
-    template<typename T>
-    auto compareEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) == rhs; }
-
-    template<typename LhsT, typename RhsT>
-    auto compareNotEqual( LhsT const& lhs, RhsT&& rhs ) -> bool { return static_cast<bool>(lhs != rhs); }
-    template<typename T>
-    auto compareNotEqual( T* const& lhs, int rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }
-    template<typename T>
-    auto compareNotEqual( T* const& lhs, long rhs ) -> bool { return lhs != reinterpret_cast<void const*>( rhs ); }
-    template<typename T>
-    auto compareNotEqual( int lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; }
-    template<typename T>
-    auto compareNotEqual( long lhs, T* const& rhs ) -> bool { return reinterpret_cast<void const*>( lhs ) != rhs; }
-
-    template<typename LhsT>
-    class ExprLhs {
-        LhsT m_lhs;
-    public:
-        explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
-
-        template<typename RhsT>
-        auto operator == ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { compareEqual( m_lhs, rhs ), m_lhs, "==", rhs };
-        }
-        auto operator == ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
-            return { m_lhs == rhs, m_lhs, "==", rhs };
-        }
-
-        template<typename RhsT>
-        auto operator != ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { compareNotEqual( m_lhs, rhs ), m_lhs, "!=", rhs };
-        }
-        auto operator != ( bool rhs ) -> BinaryExpr<LhsT, bool> const {
-            return { m_lhs != rhs, m_lhs, "!=", rhs };
-        }
-
-        template<typename RhsT>
-        auto operator > ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { static_cast<bool>(m_lhs > rhs), m_lhs, ">", rhs };
-        }
-        template<typename RhsT>
-        auto operator < ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { static_cast<bool>(m_lhs < rhs), m_lhs, "<", rhs };
-        }
-        template<typename RhsT>
-        auto operator >= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { static_cast<bool>(m_lhs >= rhs), m_lhs, ">=", rhs };
-        }
-        template<typename RhsT>
-        auto operator <= ( RhsT const& rhs ) -> BinaryExpr<LhsT, RhsT const&> const {
-            return { static_cast<bool>(m_lhs <= rhs), m_lhs, "<=", rhs };
-        }
-
-        template<typename RhsT>
-        auto operator && ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
-            static_assert(always_false<RhsT>::value,
-            "operator&& is not supported inside assertions, "
-            "wrap the expression inside parentheses, or decompose it");
-        }
-
-        template<typename RhsT>
-        auto operator || ( RhsT const& ) -> BinaryExpr<LhsT, RhsT const&> const {
-            static_assert(always_false<RhsT>::value,
-            "operator|| is not supported inside assertions, "
-            "wrap the expression inside parentheses, or decompose it");
-        }
-
-        auto makeUnaryExpr() const -> UnaryExpr<LhsT> {
-            return UnaryExpr<LhsT>{ m_lhs };
-        }
-    };
-
-    void handleExpression( ITransientExpression const& expr );
-
-    template<typename T>
-    void handleExpression( ExprLhs<T> const& expr ) {
-        handleExpression( expr.makeUnaryExpr() );
-    }
-
-    struct Decomposer {
-        template<typename T>
-        auto operator <= ( T const& lhs ) -> ExprLhs<T const&> {
-            return ExprLhs<T const&>{ lhs };
-        }
-
-        auto operator <=( bool value ) -> ExprLhs<bool> {
-            return ExprLhs<bool>{ value };
-        }
-    };
-
-} // end namespace Catch
-
-#ifdef _MSC_VER
-#pragma warning(pop)
-#endif
-
-// end catch_decomposer.h
-// start catch_interfaces_capture.h
-
-#include <string>
-
-namespace Catch {
-
-    class AssertionResult;
-    struct AssertionInfo;
-    struct SectionInfo;
-    struct SectionEndInfo;
-    struct MessageInfo;
-    struct MessageBuilder;
-    struct Counts;
-    struct BenchmarkInfo;
-    struct BenchmarkStats;
-    struct AssertionReaction;
-    struct SourceLineInfo;
-
-    struct ITransientExpression;
-    struct IGeneratorTracker;
-
-    struct IResultCapture {
-
-        virtual ~IResultCapture();
-
-        virtual bool sectionStarted(    SectionInfo const& sectionInfo,
-                                        Counts& assertions ) = 0;
-        virtual void sectionEnded( SectionEndInfo const& endInfo ) = 0;
-        virtual void sectionEndedEarly( SectionEndInfo const& endInfo ) = 0;
-
-        virtual auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& = 0;
-
-        virtual void benchmarkStarting( BenchmarkInfo const& info ) = 0;
-        virtual void benchmarkEnded( BenchmarkStats const& stats ) = 0;
-
-        virtual void pushScopedMessage( MessageInfo const& message ) = 0;
-        virtual void popScopedMessage( MessageInfo const& message ) = 0;
-
-        virtual void emplaceUnscopedMessage( MessageBuilder const& builder ) = 0;
-
-        virtual void handleFatalErrorCondition( StringRef message ) = 0;
-
-        virtual void handleExpr
-                (   AssertionInfo const& info,
-                    ITransientExpression const& expr,
-                    AssertionReaction& reaction ) = 0;
-        virtual void handleMessage
-                (   AssertionInfo const& info,
-                    ResultWas::OfType resultType,
-                    StringRef const& message,
-                    AssertionReaction& reaction ) = 0;
-        virtual void handleUnexpectedExceptionNotThrown
-                (   AssertionInfo const& info,
-                    AssertionReaction& reaction ) = 0;
-        virtual void handleUnexpectedInflightException
-                (   AssertionInfo const& info,
-                    std::string const& message,
-                    AssertionReaction& reaction ) = 0;
-        virtual void handleIncomplete
-                (   AssertionInfo const& info ) = 0;
-        virtual void handleNonExpr
-                (   AssertionInfo const &info,
-                    ResultWas::OfType resultType,
-                    AssertionReaction &reaction ) = 0;
-
-        virtual bool lastAssertionPassed() = 0;
-        virtual void assertionPassed() = 0;
-
-        // Deprecated, do not use:
-        virtual std::string getCurrentTestName() const = 0;
-        virtual const AssertionResult* getLastResult() const = 0;
-        virtual void exceptionEarlyReported() = 0;
-    };
-
-    IResultCapture& getResultCapture();
-}
-
-// end catch_interfaces_capture.h
-namespace Catch {
-
-    struct TestFailureException{};
-    struct AssertionResultData;
-    struct IResultCapture;
-    class RunContext;
-
-    class LazyExpression {
-        friend class AssertionHandler;
-        friend struct AssertionStats;
-        friend class RunContext;
-
-        ITransientExpression const* m_transientExpression = nullptr;
-        bool m_isNegated;
-    public:
-        LazyExpression( bool isNegated );
-        LazyExpression( LazyExpression const& other );
-        LazyExpression& operator = ( LazyExpression const& ) = delete;
-
-        explicit operator bool() const;
-
-        friend auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream&;
-    };
-
-    struct AssertionReaction {
-        bool shouldDebugBreak = false;
-        bool shouldThrow = false;
-    };
-
-    class AssertionHandler {
-        AssertionInfo m_assertionInfo;
-        AssertionReaction m_reaction;
-        bool m_completed = false;
-        IResultCapture& m_resultCapture;
-
-    public:
-        AssertionHandler
-            (   StringRef const& macroName,
-                SourceLineInfo const& lineInfo,
-                StringRef capturedExpression,
-                ResultDisposition::Flags resultDisposition );
-        ~AssertionHandler() {
-            if ( !m_completed ) {
-                m_resultCapture.handleIncomplete( m_assertionInfo );
-            }
-        }
-
-        template<typename T>
-        void handleExpr( ExprLhs<T> const& expr ) {
-            handleExpr( expr.makeUnaryExpr() );
-        }
-        void handleExpr( ITransientExpression const& expr );
-
-        void handleMessage(ResultWas::OfType resultType, StringRef const& message);
-
-        void handleExceptionThrownAsExpected();
-        void handleUnexpectedExceptionNotThrown();
-        void handleExceptionNotThrownAsExpected();
-        void handleThrowingCallSkipped();
-        void handleUnexpectedInflightException();
-
-        void complete();
-        void setCompleted();
-
-        // query
-        auto allowThrows() const -> bool;
-    };
-
-    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString );
-
-} // namespace Catch
-
-// end catch_assertionhandler.h
-// start catch_message.h
-
-#include <string>
-#include <vector>
-
-namespace Catch {
-
-    struct MessageInfo {
-        MessageInfo(    StringRef const& _macroName,
-                        SourceLineInfo const& _lineInfo,
-                        ResultWas::OfType _type );
-
-        StringRef macroName;
-        std::string message;
-        SourceLineInfo lineInfo;
-        ResultWas::OfType type;
-        unsigned int sequence;
-
-        bool operator == ( MessageInfo const& other ) const;
-        bool operator < ( MessageInfo const& other ) const;
-    private:
-        static unsigned int globalCount;
-    };
-
-    struct MessageStream {
-
-        template<typename T>
-        MessageStream& operator << ( T const& value ) {
-            m_stream << value;
-            return *this;
-        }
-
-        ReusableStringStream m_stream;
-    };
-
-    struct MessageBuilder : MessageStream {
-        MessageBuilder( StringRef const& macroName,
-                        SourceLineInfo const& lineInfo,
-                        ResultWas::OfType type );
-
-        template<typename T>
-        MessageBuilder& operator << ( T const& value ) {
-            m_stream << value;
-            return *this;
-        }
-
-        MessageInfo m_info;
-    };
-
-    class ScopedMessage {
-    public:
-        explicit ScopedMessage( MessageBuilder const& builder );
-        ScopedMessage( ScopedMessage& duplicate ) = delete;
-        ScopedMessage( ScopedMessage&& old );
-        ~ScopedMessage();
-
-        MessageInfo m_info;
-        bool m_moved;
-    };
-
-    class Capturer {
-        std::vector<MessageInfo> m_messages;
-        IResultCapture& m_resultCapture = getResultCapture();
-        size_t m_captured = 0;
-    public:
-        Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names );
-        ~Capturer();
-
-        void captureValue( size_t index, std::string const& value );
-
-        template<typename T>
-        void captureValues( size_t index, T const& value ) {
-            captureValue( index, Catch::Detail::stringify( value ) );
-        }
-
-        template<typename T, typename... Ts>
-        void captureValues( size_t index, T const& value, Ts const&... values ) {
-            captureValue( index, Catch::Detail::stringify(value) );
-            captureValues( index+1, values... );
-        }
-    };
-
-} // end namespace Catch
-
-// end catch_message.h
-#if !defined(CATCH_CONFIG_DISABLE)
-
-#if !defined(CATCH_CONFIG_DISABLE_STRINGIFICATION)
-  #define CATCH_INTERNAL_STRINGIFY(...) #__VA_ARGS__
-#else
-  #define CATCH_INTERNAL_STRINGIFY(...) "Disabled by CATCH_CONFIG_DISABLE_STRINGIFICATION"
-#endif
-
-#if defined(CATCH_CONFIG_FAST_COMPILE) || defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
-
-///////////////////////////////////////////////////////////////////////////////
-// Another way to speed-up compilation is to omit local try-catch for REQUIRE*
-// macros.
-#define INTERNAL_CATCH_TRY
-#define INTERNAL_CATCH_CATCH( capturer )
-
-#else // CATCH_CONFIG_FAST_COMPILE
-
-#define INTERNAL_CATCH_TRY try
-#define INTERNAL_CATCH_CATCH( handler ) catch(...) { handler.handleUnexpectedInflightException(); }
-
-#endif
-
-#define INTERNAL_CATCH_REACT( handler ) handler.complete();
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_TEST( macroName, resultDisposition, ... ) \
-    do { \
-        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
-        INTERNAL_CATCH_TRY { \
-            CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \
-            catchAssertionHandler.handleExpr( Catch::Decomposer() <= __VA_ARGS__ ); \
-            CATCH_INTERNAL_UNSUPPRESS_PARENTHESES_WARNINGS \
-        } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
-        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
-    } while( (void)0, (false) && static_cast<bool>( !!(__VA_ARGS__) ) ) // the expression here is never evaluated at runtime but it forces the compiler to give it a look
-    // The double negation silences MSVC's C4800 warning, the static_cast forces short-circuit evaluation if the type has overloaded &&.
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_IF( macroName, resultDisposition, ... ) \
-    INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
-    if( Catch::getResultCapture().lastAssertionPassed() )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_ELSE( macroName, resultDisposition, ... ) \
-    INTERNAL_CATCH_TEST( macroName, resultDisposition, __VA_ARGS__ ); \
-    if( !Catch::getResultCapture().lastAssertionPassed() )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_NO_THROW( macroName, resultDisposition, ... ) \
-    do { \
-        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition ); \
-        try { \
-            static_cast<void>(__VA_ARGS__); \
-            catchAssertionHandler.handleExceptionNotThrownAsExpected(); \
-        } \
-        catch( ... ) { \
-            catchAssertionHandler.handleUnexpectedInflightException(); \
-        } \
-        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
-    } while( false )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_THROWS( macroName, resultDisposition, ... ) \
-    do { \
-        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__), resultDisposition); \
-        if( catchAssertionHandler.allowThrows() ) \
-            try { \
-                static_cast<void>(__VA_ARGS__); \
-                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
-            } \
-            catch( ... ) { \
-                catchAssertionHandler.handleExceptionThrownAsExpected(); \
-            } \
-        else \
-            catchAssertionHandler.handleThrowingCallSkipped(); \
-        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
-    } while( false )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_THROWS_AS( macroName, exceptionType, resultDisposition, expr ) \
-    do { \
-        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(expr) ", " CATCH_INTERNAL_STRINGIFY(exceptionType), resultDisposition ); \
-        if( catchAssertionHandler.allowThrows() ) \
-            try { \
-                static_cast<void>(expr); \
-                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
-            } \
-            catch( exceptionType const& ) { \
-                catchAssertionHandler.handleExceptionThrownAsExpected(); \
-            } \
-            catch( ... ) { \
-                catchAssertionHandler.handleUnexpectedInflightException(); \
-            } \
-        else \
-            catchAssertionHandler.handleThrowingCallSkipped(); \
-        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
-    } while( false )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_MSG( macroName, messageType, resultDisposition, ... ) \
-    do { \
-        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::StringRef(), resultDisposition ); \
-        catchAssertionHandler.handleMessage( messageType, ( Catch::MessageStream() << __VA_ARGS__ + ::Catch::StreamEndStop() ).m_stream.str() ); \
-        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
-    } while( false )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_CAPTURE( varName, macroName, ... ) \
-    auto varName = Catch::Capturer( macroName, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info, #__VA_ARGS__ ); \
-    varName.captureValues( 0, __VA_ARGS__ )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_INFO( macroName, log ) \
-    Catch::ScopedMessage INTERNAL_CATCH_UNIQUE_NAME( scopedMessage )( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log );
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_UNSCOPED_INFO( macroName, log ) \
-    Catch::getResultCapture().emplaceUnscopedMessage( Catch::MessageBuilder( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, Catch::ResultWas::Info ) << log )
-
-///////////////////////////////////////////////////////////////////////////////
-// Although this is matcher-based, it can be used with just a string
-#define INTERNAL_CATCH_THROWS_STR_MATCHES( macroName, resultDisposition, matcher, ... ) \
-    do { \
-        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
-        if( catchAssertionHandler.allowThrows() ) \
-            try { \
-                static_cast<void>(__VA_ARGS__); \
-                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
-            } \
-            catch( ... ) { \
-                Catch::handleExceptionMatchExpr( catchAssertionHandler, matcher, #matcher##_catch_sr ); \
-            } \
-        else \
-            catchAssertionHandler.handleThrowingCallSkipped(); \
-        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
-    } while( false )
-
-#endif // CATCH_CONFIG_DISABLE
-
-// end catch_capture.hpp
-// start catch_section.h
-
-// start catch_section_info.h
-
-// start catch_totals.h
-
-#include <cstddef>
-
-namespace Catch {
-
-    struct Counts {
-        Counts operator - ( Counts const& other ) const;
-        Counts& operator += ( Counts const& other );
-
-        std::size_t total() const;
-        bool allPassed() const;
-        bool allOk() const;
-
-        std::size_t passed = 0;
-        std::size_t failed = 0;
-        std::size_t failedButOk = 0;
-    };
-
-    struct Totals {
-
-        Totals operator - ( Totals const& other ) const;
-        Totals& operator += ( Totals const& other );
-
-        Totals delta( Totals const& prevTotals ) const;
-
-        int error = 0;
-        Counts assertions;
-        Counts testCases;
-    };
-}
-
-// end catch_totals.h
-#include <string>
-
-namespace Catch {
-
-    struct SectionInfo {
-        SectionInfo
-            (   SourceLineInfo const& _lineInfo,
-                std::string const& _name );
-
-        // Deprecated
-        SectionInfo
-            (   SourceLineInfo const& _lineInfo,
-                std::string const& _name,
-                std::string const& ) : SectionInfo( _lineInfo, _name ) {}
-
-        std::string name;
-        std::string description; // !Deprecated: this will always be empty
-        SourceLineInfo lineInfo;
-    };
-
-    struct SectionEndInfo {
-        SectionInfo sectionInfo;
-        Counts prevAssertions;
-        double durationInSeconds;
-    };
-
-} // end namespace Catch
-
-// end catch_section_info.h
-// start catch_timer.h
-
-#include <cstdint>
-
-namespace Catch {
-
-    auto getCurrentNanosecondsSinceEpoch() -> uint64_t;
-    auto getEstimatedClockResolution() -> uint64_t;
-
-    class Timer {
-        uint64_t m_nanoseconds = 0;
-    public:
-        void start();
-        auto getElapsedNanoseconds() const -> uint64_t;
-        auto getElapsedMicroseconds() const -> uint64_t;
-        auto getElapsedMilliseconds() const -> unsigned int;
-        auto getElapsedSeconds() const -> double;
-    };
-
-} // namespace Catch
-
-// end catch_timer.h
-#include <string>
-
-namespace Catch {
-
-    class Section : NonCopyable {
-    public:
-        Section( SectionInfo const& info );
-        ~Section();
-
-        // This indicates whether the section should be executed or not
-        explicit operator bool() const;
-
-    private:
-        SectionInfo m_info;
-
-        std::string m_name;
-        Counts m_assertions;
-        bool m_sectionIncluded;
-        Timer m_timer;
-    };
-
-} // end namespace Catch
-
-#define INTERNAL_CATCH_SECTION( ... ) \
-    CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
-    if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, __VA_ARGS__ ) ) \
-    CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
-
-#define INTERNAL_CATCH_DYNAMIC_SECTION( ... ) \
-    CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \
-    if( Catch::Section const& INTERNAL_CATCH_UNIQUE_NAME( catch_internal_Section ) = Catch::SectionInfo( CATCH_INTERNAL_LINEINFO, (Catch::ReusableStringStream() << __VA_ARGS__).str() ) ) \
-    CATCH_INTERNAL_UNSUPPRESS_UNUSED_WARNINGS
-
-// end catch_section.h
-// start catch_benchmark.h
-
-#include <cstdint>
-#include <string>
-
-namespace Catch {
-
-    class BenchmarkLooper {
-
-        std::string m_name;
-        std::size_t m_count = 0;
-        std::size_t m_iterationsToRun = 1;
-        uint64_t m_resolution;
-        Timer m_timer;
-
-        static auto getResolution() -> uint64_t;
-    public:
-        // Keep most of this inline as it's on the code path that is being timed
-        BenchmarkLooper( StringRef name )
-        :   m_name( name ),
-            m_resolution( getResolution() )
-        {
-            reportStart();
-            m_timer.start();
-        }
-
-        explicit operator bool() {
-            if( m_count < m_iterationsToRun )
-                return true;
-            return needsMoreIterations();
-        }
-
-        void increment() {
-            ++m_count;
-        }
-
-        void reportStart();
-        auto needsMoreIterations() -> bool;
-    };
-
-} // end namespace Catch
-
-#define BENCHMARK( name ) \
-    for( Catch::BenchmarkLooper looper( name ); looper; looper.increment() )
-
-// end catch_benchmark.h
-// start catch_interfaces_exception.h
-
-// start catch_interfaces_registry_hub.h
-
-#include <string>
-#include <memory>
-
-namespace Catch {
-
-    class TestCase;
-    struct ITestCaseRegistry;
-    struct IExceptionTranslatorRegistry;
-    struct IExceptionTranslator;
-    struct IReporterRegistry;
-    struct IReporterFactory;
-    struct ITagAliasRegistry;
-    struct IMutableEnumValuesRegistry;
-
-    class StartupExceptionRegistry;
-
-    using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>;
-
-    struct IRegistryHub {
-        virtual ~IRegistryHub();
-
-        virtual IReporterRegistry const& getReporterRegistry() const = 0;
-        virtual ITestCaseRegistry const& getTestCaseRegistry() const = 0;
-        virtual ITagAliasRegistry const& getTagAliasRegistry() const = 0;
-        virtual IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const = 0;
-
-        virtual StartupExceptionRegistry const& getStartupExceptionRegistry() const = 0;
-    };
-
-    struct IMutableRegistryHub {
-        virtual ~IMutableRegistryHub();
-        virtual void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) = 0;
-        virtual void registerListener( IReporterFactoryPtr const& factory ) = 0;
-        virtual void registerTest( TestCase const& testInfo ) = 0;
-        virtual void registerTranslator( const IExceptionTranslator* translator ) = 0;
-        virtual void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) = 0;
-        virtual void registerStartupException() noexcept = 0;
-        virtual IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() = 0;
-    };
-
-    IRegistryHub const& getRegistryHub();
-    IMutableRegistryHub& getMutableRegistryHub();
-    void cleanUp();
-    std::string translateActiveException();
-
-}
-
-// end catch_interfaces_registry_hub.h
-#if defined(CATCH_CONFIG_DISABLE)
-    #define INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( translatorName, signature) \
-        static std::string translatorName( signature )
-#endif
-
-#include <exception>
-#include <string>
-#include <vector>
-
-namespace Catch {
-    using exceptionTranslateFunction = std::string(*)();
-
-    struct IExceptionTranslator;
-    using ExceptionTranslators = std::vector<std::unique_ptr<IExceptionTranslator const>>;
-
-    struct IExceptionTranslator {
-        virtual ~IExceptionTranslator();
-        virtual std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const = 0;
-    };
-
-    struct IExceptionTranslatorRegistry {
-        virtual ~IExceptionTranslatorRegistry();
-
-        virtual std::string translateActiveException() const = 0;
-    };
-
-    class ExceptionTranslatorRegistrar {
-        template<typename T>
-        class ExceptionTranslator : public IExceptionTranslator {
-        public:
-
-            ExceptionTranslator( std::string(*translateFunction)( T& ) )
-            : m_translateFunction( translateFunction )
-            {}
-
-            std::string translate( ExceptionTranslators::const_iterator it, ExceptionTranslators::const_iterator itEnd ) const override {
-                try {
-                    if( it == itEnd )
-                        std::rethrow_exception(std::current_exception());
-                    else
-                        return (*it)->translate( it+1, itEnd );
-                }
-                catch( T& ex ) {
-                    return m_translateFunction( ex );
-                }
-            }
-
-        protected:
-            std::string(*m_translateFunction)( T& );
-        };
-
-    public:
-        template<typename T>
-        ExceptionTranslatorRegistrar( std::string(*translateFunction)( T& ) ) {
-            getMutableRegistryHub().registerTranslator
-                ( new ExceptionTranslator<T>( translateFunction ) );
-        }
-    };
-}
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_TRANSLATE_EXCEPTION2( translatorName, signature ) \
-    static std::string translatorName( signature ); \
-    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \
-    namespace{ Catch::ExceptionTranslatorRegistrar INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionRegistrar )( &translatorName ); } \
-    CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS \
-    static std::string translatorName( signature )
-
-#define INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION2( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
-
-// end catch_interfaces_exception.h
-// start catch_approx.h
-
-#include <type_traits>
-
-namespace Catch {
-namespace Detail {
-
-    class Approx {
-    private:
-        bool equalityComparisonImpl(double other) const;
-        // Validates the new margin (margin >= 0)
-        // out-of-line to avoid including stdexcept in the header
-        void setMargin(double margin);
-        // Validates the new epsilon (0 < epsilon < 1)
-        // out-of-line to avoid including stdexcept in the header
-        void setEpsilon(double epsilon);
-
-    public:
-        explicit Approx ( double value );
-
-        static Approx custom();
-
-        Approx operator-() const;
-
-        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        Approx operator()( T const& value ) {
-            Approx approx( static_cast<double>(value) );
-            approx.m_epsilon = m_epsilon;
-            approx.m_margin = m_margin;
-            approx.m_scale = m_scale;
-            return approx;
-        }
-
-        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        explicit Approx( T const& value ): Approx(static_cast<double>(value))
-        {}
-
-        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator == ( const T& lhs, Approx const& rhs ) {
-            auto lhs_v = static_cast<double>(lhs);
-            return rhs.equalityComparisonImpl(lhs_v);
-        }
-
-        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator == ( Approx const& lhs, const T& rhs ) {
-            return operator==( rhs, lhs );
-        }
-
-        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator != ( T const& lhs, Approx const& rhs ) {
-            return !operator==( lhs, rhs );
-        }
-
-        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator != ( Approx const& lhs, T const& rhs ) {
-            return !operator==( rhs, lhs );
-        }
-
-        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator <= ( T const& lhs, Approx const& rhs ) {
-            return static_cast<double>(lhs) < rhs.m_value || lhs == rhs;
-        }
-
-        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator <= ( Approx const& lhs, T const& rhs ) {
-            return lhs.m_value < static_cast<double>(rhs) || lhs == rhs;
-        }
-
-        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator >= ( T const& lhs, Approx const& rhs ) {
-            return static_cast<double>(lhs) > rhs.m_value || lhs == rhs;
-        }
-
-        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        friend bool operator >= ( Approx const& lhs, T const& rhs ) {
-            return lhs.m_value > static_cast<double>(rhs) || lhs == rhs;
-        }
-
-        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        Approx& epsilon( T const& newEpsilon ) {
-            double epsilonAsDouble = static_cast<double>(newEpsilon);
-            setEpsilon(epsilonAsDouble);
-            return *this;
-        }
-
-        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        Approx& margin( T const& newMargin ) {
-            double marginAsDouble = static_cast<double>(newMargin);
-            setMargin(marginAsDouble);
-            return *this;
-        }
-
-        template <typename T, typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-        Approx& scale( T const& newScale ) {
-            m_scale = static_cast<double>(newScale);
-            return *this;
-        }
-
-        std::string toString() const;
-
-    private:
-        double m_epsilon;
-        double m_margin;
-        double m_scale;
-        double m_value;
-    };
-} // end namespace Detail
-
-namespace literals {
-    Detail::Approx operator "" _a(long double val);
-    Detail::Approx operator "" _a(unsigned long long val);
-} // end namespace literals
-
-template<>
-struct StringMaker<Catch::Detail::Approx> {
-    static std::string convert(Catch::Detail::Approx const& value);
-};
-
-} // end namespace Catch
-
-// end catch_approx.h
-// start catch_string_manip.h
-
-#include <string>
-#include <iosfwd>
-#include <vector>
-
-namespace Catch {
-
-    bool startsWith( std::string const& s, std::string const& prefix );
-    bool startsWith( std::string const& s, char prefix );
-    bool endsWith( std::string const& s, std::string const& suffix );
-    bool endsWith( std::string const& s, char suffix );
-    bool contains( std::string const& s, std::string const& infix );
-    void toLowerInPlace( std::string& s );
-    std::string toLower( std::string const& s );
-    std::string trim( std::string const& str );
-
-    // !!! Be aware, returns refs into original string - make sure original string outlives them
-    std::vector<StringRef> splitStringRef( StringRef str, char delimiter );
-    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis );
-
-    struct pluralise {
-        pluralise( std::size_t count, std::string const& label );
-
-        friend std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser );
-
-        std::size_t m_count;
-        std::string m_label;
-    };
-}
-
-// end catch_string_manip.h
-#ifndef CATCH_CONFIG_DISABLE_MATCHERS
-// start catch_capture_matchers.h
-
-// start catch_matchers.h
-
-#include <string>
-#include <vector>
-
-namespace Catch {
-namespace Matchers {
-    namespace Impl {
-
-        template<typename ArgT> struct MatchAllOf;
-        template<typename ArgT> struct MatchAnyOf;
-        template<typename ArgT> struct MatchNotOf;
-
-        class MatcherUntypedBase {
-        public:
-            MatcherUntypedBase() = default;
-            MatcherUntypedBase ( MatcherUntypedBase const& ) = default;
-            MatcherUntypedBase& operator = ( MatcherUntypedBase const& ) = delete;
-            std::string toString() const;
-
-        protected:
-            virtual ~MatcherUntypedBase();
-            virtual std::string describe() const = 0;
-            mutable std::string m_cachedToString;
-        };
-
-#ifdef __clang__
-#    pragma clang diagnostic push
-#    pragma clang diagnostic ignored "-Wnon-virtual-dtor"
-#endif
-
-        template<typename ObjectT>
-        struct MatcherMethod {
-            virtual bool match( ObjectT const& arg ) const = 0;
-        };
-
-#ifdef __clang__
-#    pragma clang diagnostic pop
-#endif
-
-        template<typename T>
-        struct MatcherBase : MatcherUntypedBase, MatcherMethod<T> {
-
-            MatchAllOf<T> operator && ( MatcherBase const& other ) const;
-            MatchAnyOf<T> operator || ( MatcherBase const& other ) const;
-            MatchNotOf<T> operator ! () const;
-        };
-
-        template<typename ArgT>
-        struct MatchAllOf : MatcherBase<ArgT> {
-            bool match( ArgT const& arg ) const override {
-                for( auto matcher : m_matchers ) {
-                    if (!matcher->match(arg))
-                        return false;
-                }
-                return true;
-            }
-            std::string describe() const override {
-                std::string description;
-                description.reserve( 4 + m_matchers.size()*32 );
-                description += "( ";
-                bool first = true;
-                for( auto matcher : m_matchers ) {
-                    if( first )
-                        first = false;
-                    else
-                        description += " and ";
-                    description += matcher->toString();
-                }
-                description += " )";
-                return description;
-            }
-
-            MatchAllOf<ArgT>& operator && ( MatcherBase<ArgT> const& other ) {
-                m_matchers.push_back( &other );
-                return *this;
-            }
-
-            std::vector<MatcherBase<ArgT> const*> m_matchers;
-        };
-        template<typename ArgT>
-        struct MatchAnyOf : MatcherBase<ArgT> {
-
-            bool match( ArgT const& arg ) const override {
-                for( auto matcher : m_matchers ) {
-                    if (matcher->match(arg))
-                        return true;
-                }
-                return false;
-            }
-            std::string describe() const override {
-                std::string description;
-                description.reserve( 4 + m_matchers.size()*32 );
-                description += "( ";
-                bool first = true;
-                for( auto matcher : m_matchers ) {
-                    if( first )
-                        first = false;
-                    else
-                        description += " or ";
-                    description += matcher->toString();
-                }
-                description += " )";
-                return description;
-            }
-
-            MatchAnyOf<ArgT>& operator || ( MatcherBase<ArgT> const& other ) {
-                m_matchers.push_back( &other );
-                return *this;
-            }
-
-            std::vector<MatcherBase<ArgT> const*> m_matchers;
-        };
-
-        template<typename ArgT>
-        struct MatchNotOf : MatcherBase<ArgT> {
-
-            MatchNotOf( MatcherBase<ArgT> const& underlyingMatcher ) : m_underlyingMatcher( underlyingMatcher ) {}
-
-            bool match( ArgT const& arg ) const override {
-                return !m_underlyingMatcher.match( arg );
-            }
-
-            std::string describe() const override {
-                return "not " + m_underlyingMatcher.toString();
-            }
-            MatcherBase<ArgT> const& m_underlyingMatcher;
-        };
-
-        template<typename T>
-        MatchAllOf<T> MatcherBase<T>::operator && ( MatcherBase const& other ) const {
-            return MatchAllOf<T>() && *this && other;
-        }
-        template<typename T>
-        MatchAnyOf<T> MatcherBase<T>::operator || ( MatcherBase const& other ) const {
-            return MatchAnyOf<T>() || *this || other;
-        }
-        template<typename T>
-        MatchNotOf<T> MatcherBase<T>::operator ! () const {
-            return MatchNotOf<T>( *this );
-        }
-
-    } // namespace Impl
-
-} // namespace Matchers
-
-using namespace Matchers;
-using Matchers::Impl::MatcherBase;
-
-} // namespace Catch
-
-// end catch_matchers.h
-// start catch_matchers_floating.h
-
-#include <type_traits>
-#include <cmath>
-
-namespace Catch {
-namespace Matchers {
-
-    namespace Floating {
-
-        enum class FloatingPointKind : uint8_t;
-
-        struct WithinAbsMatcher : MatcherBase<double> {
-            WithinAbsMatcher(double target, double margin);
-            bool match(double const& matchee) const override;
-            std::string describe() const override;
-        private:
-            double m_target;
-            double m_margin;
-        };
-
-        struct WithinUlpsMatcher : MatcherBase<double> {
-            WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType);
-            bool match(double const& matchee) const override;
-            std::string describe() const override;
-        private:
-            double m_target;
-            int m_ulps;
-            FloatingPointKind m_type;
-        };
-
-    } // namespace Floating
-
-    // The following functions create the actual matcher objects.
-    // This allows the types to be inferred
-    Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff);
-    Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff);
-    Floating::WithinAbsMatcher WithinAbs(double target, double margin);
-
-} // namespace Matchers
-} // namespace Catch
-
-// end catch_matchers_floating.h
-// start catch_matchers_generic.hpp
-
-#include <functional>
-#include <string>
-
-namespace Catch {
-namespace Matchers {
-namespace Generic {
-
-namespace Detail {
-    std::string finalizeDescription(const std::string& desc);
-}
-
-template <typename T>
-class PredicateMatcher : public MatcherBase<T> {
-    std::function<bool(T const&)> m_predicate;
-    std::string m_description;
-public:
-
-    PredicateMatcher(std::function<bool(T const&)> const& elem, std::string const& descr)
-        :m_predicate(std::move(elem)),
-        m_description(Detail::finalizeDescription(descr))
-    {}
-
-    bool match( T const& item ) const override {
-        return m_predicate(item);
-    }
-
-    std::string describe() const override {
-        return m_description;
-    }
-};
-
-} // namespace Generic
-
-    // The following functions create the actual matcher objects.
-    // The user has to explicitly specify type to the function, because
-    // inferring std::function<bool(T const&)> is hard (but possible) and
-    // requires a lot of TMP.
-    template<typename T>
-    Generic::PredicateMatcher<T> Predicate(std::function<bool(T const&)> const& predicate, std::string const& description = "") {
-        return Generic::PredicateMatcher<T>(predicate, description);
-    }
-
-} // namespace Matchers
-} // namespace Catch
-
-// end catch_matchers_generic.hpp
-// start catch_matchers_string.h
-
-#include <string>
-
-namespace Catch {
-namespace Matchers {
-
-    namespace StdString {
-
-        struct CasedString
-        {
-            CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity );
-            std::string adjustString( std::string const& str ) const;
-            std::string caseSensitivitySuffix() const;
-
-            CaseSensitive::Choice m_caseSensitivity;
-            std::string m_str;
-        };
-
-        struct StringMatcherBase : MatcherBase<std::string> {
-            StringMatcherBase( std::string const& operation, CasedString const& comparator );
-            std::string describe() const override;
-
-            CasedString m_comparator;
-            std::string m_operation;
-        };
-
-        struct EqualsMatcher : StringMatcherBase {
-            EqualsMatcher( CasedString const& comparator );
-            bool match( std::string const& source ) const override;
-        };
-        struct ContainsMatcher : StringMatcherBase {
-            ContainsMatcher( CasedString const& comparator );
-            bool match( std::string const& source ) const override;
-        };
-        struct StartsWithMatcher : StringMatcherBase {
-            StartsWithMatcher( CasedString const& comparator );
-            bool match( std::string const& source ) const override;
-        };
-        struct EndsWithMatcher : StringMatcherBase {
-            EndsWithMatcher( CasedString const& comparator );
-            bool match( std::string const& source ) const override;
-        };
-
-        struct RegexMatcher : MatcherBase<std::string> {
-            RegexMatcher( std::string regex, CaseSensitive::Choice caseSensitivity );
-            bool match( std::string const& matchee ) const override;
-            std::string describe() const override;
-
-        private:
-            std::string m_regex;
-            CaseSensitive::Choice m_caseSensitivity;
-        };
-
-    } // namespace StdString
-
-    // The following functions create the actual matcher objects.
-    // This allows the types to be inferred
-
-    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
-    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
-    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
-    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
-    StdString::RegexMatcher Matches( std::string const& regex, CaseSensitive::Choice caseSensitivity = CaseSensitive::Yes );
-
-} // namespace Matchers
-} // namespace Catch
-
-// end catch_matchers_string.h
-// start catch_matchers_vector.h
-
-#include <algorithm>
-
-namespace Catch {
-namespace Matchers {
-
-    namespace Vector {
-        template<typename T>
-        struct ContainsElementMatcher : MatcherBase<std::vector<T>> {
-
-            ContainsElementMatcher(T const &comparator) : m_comparator( comparator) {}
-
-            bool match(std::vector<T> const &v) const override {
-                for (auto const& el : v) {
-                    if (el == m_comparator) {
-                        return true;
-                    }
-                }
-                return false;
-            }
-
-            std::string describe() const override {
-                return "Contains: " + ::Catch::Detail::stringify( m_comparator );
-            }
-
-            T const& m_comparator;
-        };
-
-        template<typename T>
-        struct ContainsMatcher : MatcherBase<std::vector<T>> {
-
-            ContainsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
-
-            bool match(std::vector<T> const &v) const override {
-                // !TBD: see note in EqualsMatcher
-                if (m_comparator.size() > v.size())
-                    return false;
-                for (auto const& comparator : m_comparator) {
-                    auto present = false;
-                    for (const auto& el : v) {
-                        if (el == comparator) {
-                            present = true;
-                            break;
-                        }
-                    }
-                    if (!present) {
-                        return false;
-                    }
-                }
-                return true;
-            }
-            std::string describe() const override {
-                return "Contains: " + ::Catch::Detail::stringify( m_comparator );
-            }
-
-            std::vector<T> const& m_comparator;
-        };
-
-        template<typename T>
-        struct EqualsMatcher : MatcherBase<std::vector<T>> {
-
-            EqualsMatcher(std::vector<T> const &comparator) : m_comparator( comparator ) {}
-
-            bool match(std::vector<T> const &v) const override {
-                // !TBD: This currently works if all elements can be compared using !=
-                // - a more general approach would be via a compare template that defaults
-                // to using !=. but could be specialised for, e.g. std::vector<T> etc
-                // - then just call that directly
-                if (m_comparator.size() != v.size())
-                    return false;
-                for (std::size_t i = 0; i < v.size(); ++i)
-                    if (m_comparator[i] != v[i])
-                        return false;
-                return true;
-            }
-            std::string describe() const override {
-                return "Equals: " + ::Catch::Detail::stringify( m_comparator );
-            }
-            std::vector<T> const& m_comparator;
-        };
-
-        template<typename T>
-        struct ApproxMatcher : MatcherBase<std::vector<T>> {
-
-            ApproxMatcher(std::vector<T> const& comparator) : m_comparator( comparator ) {}
-
-            bool match(std::vector<T> const &v) const override {
-                if (m_comparator.size() != v.size())
-                    return false;
-                for (std::size_t i = 0; i < v.size(); ++i)
-                    if (m_comparator[i] != approx(v[i]))
-                        return false;
-                return true;
-            }
-            std::string describe() const override {
-                return "is approx: " + ::Catch::Detail::stringify( m_comparator );
-            }
-            template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-            ApproxMatcher& epsilon( T const& newEpsilon ) {
-                approx.epsilon(newEpsilon);
-                return *this;
-            }
-            template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-            ApproxMatcher& margin( T const& newMargin ) {
-                approx.margin(newMargin);
-                return *this;
-            }
-            template <typename = typename std::enable_if<std::is_constructible<double, T>::value>::type>
-            ApproxMatcher& scale( T const& newScale ) {
-                approx.scale(newScale);
-                return *this;
-            }
-
-            std::vector<T> const& m_comparator;
-            mutable Catch::Detail::Approx approx = Catch::Detail::Approx::custom();
-        };
-
-        template<typename T>
-        struct UnorderedEqualsMatcher : MatcherBase<std::vector<T>> {
-            UnorderedEqualsMatcher(std::vector<T> const& target) : m_target(target) {}
-            bool match(std::vector<T> const& vec) const override {
-                // Note: This is a reimplementation of std::is_permutation,
-                //       because I don't want to include <algorithm> inside the common path
-                if (m_target.size() != vec.size()) {
-                    return false;
-                }
-                return std::is_permutation(m_target.begin(), m_target.end(), vec.begin());
-            }
-
-            std::string describe() const override {
-                return "UnorderedEquals: " + ::Catch::Detail::stringify(m_target);
-            }
-        private:
-            std::vector<T> const& m_target;
-        };
-
-    } // namespace Vector
-
-    // The following functions create the actual matcher objects.
-    // This allows the types to be inferred
-
-    template<typename T>
-    Vector::ContainsMatcher<T> Contains( std::vector<T> const& comparator ) {
-        return Vector::ContainsMatcher<T>( comparator );
-    }
-
-    template<typename T>
-    Vector::ContainsElementMatcher<T> VectorContains( T const& comparator ) {
-        return Vector::ContainsElementMatcher<T>( comparator );
-    }
-
-    template<typename T>
-    Vector::EqualsMatcher<T> Equals( std::vector<T> const& comparator ) {
-        return Vector::EqualsMatcher<T>( comparator );
-    }
-
-    template<typename T>
-    Vector::ApproxMatcher<T> Approx( std::vector<T> const& comparator ) {
-        return Vector::ApproxMatcher<T>( comparator );
-    }
-
-    template<typename T>
-    Vector::UnorderedEqualsMatcher<T> UnorderedEquals(std::vector<T> const& target) {
-        return Vector::UnorderedEqualsMatcher<T>(target);
-    }
-
-} // namespace Matchers
-} // namespace Catch
-
-// end catch_matchers_vector.h
-namespace Catch {
-
-    template<typename ArgT, typename MatcherT>
-    class MatchExpr : public ITransientExpression {
-        ArgT const& m_arg;
-        MatcherT m_matcher;
-        StringRef m_matcherString;
-    public:
-        MatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString )
-        :   ITransientExpression{ true, matcher.match( arg ) },
-            m_arg( arg ),
-            m_matcher( matcher ),
-            m_matcherString( matcherString )
-        {}
-
-        void streamReconstructedExpression( std::ostream &os ) const override {
-            auto matcherAsString = m_matcher.toString();
-            os << Catch::Detail::stringify( m_arg ) << ' ';
-            if( matcherAsString == Detail::unprintableString )
-                os << m_matcherString;
-            else
-                os << matcherAsString;
-        }
-    };
-
-    using StringMatcher = Matchers::Impl::MatcherBase<std::string>;
-
-    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString  );
-
-    template<typename ArgT, typename MatcherT>
-    auto makeMatchExpr( ArgT const& arg, MatcherT const& matcher, StringRef const& matcherString  ) -> MatchExpr<ArgT, MatcherT> {
-        return MatchExpr<ArgT, MatcherT>( arg, matcher, matcherString );
-    }
-
-} // namespace Catch
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CHECK_THAT( macroName, matcher, resultDisposition, arg ) \
-    do { \
-        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(arg) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
-        INTERNAL_CATCH_TRY { \
-            catchAssertionHandler.handleExpr( Catch::makeMatchExpr( arg, matcher, #matcher##_catch_sr ) ); \
-        } INTERNAL_CATCH_CATCH( catchAssertionHandler ) \
-        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
-    } while( false )
-
-///////////////////////////////////////////////////////////////////////////////
-#define INTERNAL_CATCH_THROWS_MATCHES( macroName, exceptionType, resultDisposition, matcher, ... ) \
-    do { \
-        Catch::AssertionHandler catchAssertionHandler( macroName##_catch_sr, CATCH_INTERNAL_LINEINFO, CATCH_INTERNAL_STRINGIFY(__VA_ARGS__) ", " CATCH_INTERNAL_STRINGIFY(exceptionType) ", " CATCH_INTERNAL_STRINGIFY(matcher), resultDisposition ); \
-        if( catchAssertionHandler.allowThrows() ) \
-            try { \
-                static_cast<void>(__VA_ARGS__ ); \
-                catchAssertionHandler.handleUnexpectedExceptionNotThrown(); \
-            } \
-            catch( exceptionType const& ex ) { \
-                catchAssertionHandler.handleExpr( Catch::makeMatchExpr( ex, matcher, #matcher##_catch_sr ) ); \
-            } \
-            catch( ... ) { \
-                catchAssertionHandler.handleUnexpectedInflightException(); \
-            } \
-        else \
-            catchAssertionHandler.handleThrowingCallSkipped(); \
-        INTERNAL_CATCH_REACT( catchAssertionHandler ) \
-    } while( false )
-
-// end catch_capture_matchers.h
-#endif
-// start catch_generators.hpp
-
-// start catch_interfaces_generatortracker.h
-
-
-#include <memory>
-
-namespace Catch {
-
-    namespace Generators {
-        class GeneratorUntypedBase {
-        public:
-            GeneratorUntypedBase() = default;
-            virtual ~GeneratorUntypedBase();
-            // Attempts to move the generator to the next element
-             //
-             // Returns true iff the move succeeded (and a valid element
-             // can be retrieved).
-            virtual bool next() = 0;
-        };
-        using GeneratorBasePtr = std::unique_ptr<GeneratorUntypedBase>;
-
-    } // namespace Generators
-
-    struct IGeneratorTracker {
-        virtual ~IGeneratorTracker();
-        virtual auto hasGenerator() const -> bool = 0;
-        virtual auto getGenerator() const -> Generators::GeneratorBasePtr const& = 0;
-        virtual void setGenerator( Generators::GeneratorBasePtr&& generator ) = 0;
-    };
-
-} // namespace Catch
-
-// end catch_interfaces_generatortracker.h
-// start catch_enforce.h
-
-#include <stdexcept>
-
-namespace Catch {
-#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
-    template <typename Ex>
-    [[noreturn]]
-    void throw_exception(Ex const& e) {
-        throw e;
-    }
-#else // ^^ Exceptions are enabled //  Exceptions are disabled vv
-    [[noreturn]]
-    void throw_exception(std::exception const& e);
-#endif
-} // namespace Catch;
-
-#define CATCH_PREPARE_EXCEPTION( type, msg ) \
-    type( ( Catch::ReusableStringStream() << msg ).str() )
-#define CATCH_INTERNAL_ERROR( msg ) \
-    Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::logic_error, CATCH_INTERNAL_LINEINFO << ": Internal Catch error: " << msg))
-#define CATCH_ERROR( msg ) \
-    Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::domain_error, msg ))
-#define CATCH_RUNTIME_ERROR( msg ) \
-    Catch::throw_exception(CATCH_PREPARE_EXCEPTION( std::runtime_error, msg ))
-#define CATCH_ENFORCE( condition, msg ) \
-    do{ if( !(condition) ) CATCH_ERROR( msg ); } while(false)
-
-// end catch_enforce.h
-#include <memory>
-#include <vector>
-#include <cassert>
-
-#include <utility>
-#include <exception>
-
-namespace Catch {
-
-class GeneratorException : public std::exception {
-    const char* const m_msg = "";
-
-public:
-    GeneratorException(const char* msg):
-        m_msg(msg)
-    {}
-
-    const char* what() const noexcept override final;
-};
-
-namespace Generators {
-
-    // !TBD move this into its own location?
-    namespace pf{
-        template<typename T, typename... Args>
-        std::unique_ptr<T> make_unique( Args&&... args ) {
-            return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
-        }
-    }
-
-    template<typename T>
-    struct IGenerator : GeneratorUntypedBase {
-        virtual ~IGenerator() = default;
-
-        // Returns the current element of the generator
-        //
-        // \Precondition The generator is either freshly constructed,
-        // or the last call to `next()` returned true
-        virtual T const& get() const = 0;
-        using type = T;
-    };
-
-    template<typename T>
-    class SingleValueGenerator final : public IGenerator<T> {
-        T m_value;
-    public:
-        SingleValueGenerator(T const& value) : m_value( value ) {}
-        SingleValueGenerator(T&& value) : m_value(std::move(value)) {}
-
-        T const& get() const override {
-            return m_value;
-        }
-        bool next() override {
-            return false;
-        }
-    };
-
-    template<typename T>
-    class FixedValuesGenerator final : public IGenerator<T> {
-        std::vector<T> m_values;
-        size_t m_idx = 0;
-    public:
-        FixedValuesGenerator( std::initializer_list<T> values ) : m_values( values ) {}
-
-        T const& get() const override {
-            return m_values[m_idx];
-        }
-        bool next() override {
-            ++m_idx;
-            return m_idx < m_values.size();
-        }
-    };
-
-    template <typename T>
-    class GeneratorWrapper final {
-        std::unique_ptr<IGenerator<T>> m_generator;
-    public:
-        GeneratorWrapper(std::unique_ptr<IGenerator<T>> generator):
-            m_generator(std::move(generator))
-        {}
-        T const& get() const {
-            return m_generator->get();
-        }
-        bool next() {
-            return m_generator->next();
-        }
-    };
-
-    template <typename T>
-    GeneratorWrapper<T> value(T&& value) {
-        return GeneratorWrapper<T>(pf::make_unique<SingleValueGenerator<T>>(std::forward<T>(value)));
-    }
-    template <typename T>
-    GeneratorWrapper<T> values(std::initializer_list<T> values) {
-        return GeneratorWrapper<T>(pf::make_unique<FixedValuesGenerator<T>>(values));
-    }
-
-    template<typename T>
-    class Generators : public IGenerator<T> {
-        std::vector<GeneratorWrapper<T>> m_generators;
-        size_t m_current = 0;
-
-        void populate(GeneratorWrapper<T>&& generator) {
-            m_generators.emplace_back(std::move(generator));
-        }
-        void populate(T&& val) {
-            m_generators.emplace_back(value(std::move(val)));
-        }
-        template<typename U>
-        void populate(U&& val) {
-            populate(T(std::move(val)));
-        }
-        template<typename U, typename... Gs>
-        void populate(U&& valueOrGenerator, Gs... moreGenerators) {
-            populate(std::forward<U>(valueOrGenerator));
-            populate(std::forward<Gs>(moreGenerators)...);
-        }
-
-    public:
-        template <typename... Gs>
-        Generators(Gs... moreGenerators) {
-            m_generators.reserve(sizeof...(Gs));
-            populate(std::forward<Gs>(moreGenerators)...);
-        }
-
-        T const& get() const override {
-            return m_generators[m_current].get();
-        }
-
-        bool next() override {
-            if (m_current >= m_generators.size()) {
-                return false;
-            }
-            const bool current_status = m_generators[m_current].next();
-            if (!current_status) {
-                ++m_current;
-            }
-            return m_current < m_generators.size();
-        }
-    };
-
-    template<typename... Ts>
-    GeneratorWrapper<std::tuple<Ts...>> table( std::initializer_list<std::tuple<typename std::decay<Ts>::type...>> tuples ) {
-        return values<std::tuple<Ts...>>( tuples );
-    }
-
-    // Tag type to signal that a generator sequence should convert arguments to a specific type
-    template <typename T>
-    struct as {};
-
-    template<typename T, typename... Gs>
-    auto makeGenerators( GeneratorWrapper<T>&& generator, Gs... moreGenerators ) -> Generators<T> {
-        return Generators<T>(std::move(generator), std::forward<Gs>(moreGenerators)...);
-    }
-    template<typename T>
-    auto makeGenerators( GeneratorWrapper<T>&& generator ) -> Generators<T> {
-        return Generators<T>(std::move(generator));
-    }
-    template<typename T, typename... Gs>
-    auto makeGenerators( T&& val, Gs... moreGenerators ) -> Generators<T> {
-        return makeGenerators( value( std::forward<T>( val ) ), std::forward<Gs>( moreGenerators )... );
-    }
-    template<typename T, typename U, typename... Gs>
-    auto makeGenerators( as<T>, U&& val, Gs... moreGenerators ) -> Generators<T> {
-        return makeGenerators( value( T( std::forward<U>( val ) ) ), std::forward<Gs>( moreGenerators )... );
-    }
-
-    auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker&;
-
-    template<typename L>
-    // Note: The type after -> is weird, because VS2015 cannot parse
-    //       the expression used in the typedef inside, when it is in
-    //       return type. Yeah.
-    auto generate( SourceLineInfo const& lineInfo, L const& generatorExpression ) -> decltype(std::declval<decltype(generatorExpression())>().get()) {
-        using UnderlyingType = typename decltype(generatorExpression())::type;
-
-        IGeneratorTracker& tracker = acquireGeneratorTracker( lineInfo );
-        if (!tracker.hasGenerator()) {
-            tracker.setGenerator(pf::make_unique<Generators<UnderlyingType>>(generatorExpression()));
-        }
-
-        auto const& generator = static_cast<IGenerator<UnderlyingType> const&>( *tracker.getGenerator() );
-        return generator.get();
-    }
-
-} // namespace Generators
-} // namespace Catch
-
-#define GENERATE( ... ) \
-    Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [ ]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
-#define GENERATE_COPY( ... ) \
-    Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [=]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
-#define GENERATE_REF( ... ) \
-    Catch::Generators::generate( CATCH_INTERNAL_LINEINFO, [&]{ using namespace Catch::Generators; return makeGenerators( __VA_ARGS__ ); } )
-
-// end catch_generators.hpp
-// start catch_generators_generic.hpp
-
-namespace Catch {
-namespace Generators {
-
-    template <typename T>
-    class TakeGenerator : public IGenerator<T> {
-        GeneratorWrapper<T> m_generator;
-        size_t m_returned = 0;
-        size_t m_target;
-    public:
-        TakeGenerator(size_t target, GeneratorWrapper<T>&& generator):
-            m_generator(std::move(generator)),
-            m_target(target)
-        {
-            assert(target != 0 && "Empty generators are not allowed");
-        }
-        T const& get() const override {
-            return m_generator.get();
-        }
-        bool next() override {
-            ++m_returned;
-            if (m_returned >= m_target) {
-                return false;
-            }
-
-            const auto success = m_generator.next();
-            // If the underlying generator does not contain enough values
-            // then we cut short as well
-            if (!success) {
-                m_returned = m_target;
-            }
-            return success;
-        }
-    };
-
-    template <typename T>
-    GeneratorWrapper<T> take(size_t target, GeneratorWrapper<T>&& generator) {
-        return GeneratorWrapper<T>(pf::make_unique<TakeGenerator<T>>(target, std::move(generator)));
-    }
-
-    template <typename T, typename Predicate>
-    class FilterGenerator : public IGenerator<T> {
-        GeneratorWrapper<T> m_generator;
-        Predicate m_predicate;
-    public:
-        template <typename P = Predicate>
-        FilterGenerator(P&& pred, GeneratorWrapper<T>&& generator):
-            m_generator(std::move(generator)),
-            m_predicate(std::forward<P>(pred))
-        {
-            if (!m_predicate(m_generator.get())) {
-                // It might happen that there are no values that pass the
-                // filter. In that case we throw an exception.
-                auto has_initial_value = next();
-                if (!has_initial_value) {
-                    Catch::throw_exception(GeneratorException("No valid value found in filtered generator"));
-                }
-            }
-        }
-
-        T const& get() const override {
-            return m_generator.get();
-        }
-
-        bool next() override {
-            bool success = m_generator.next();
-            if (!success) {
-                return false;
-            }
-            while (!m_predicate(m_generator.get()) && (success = m_generator.next()) == true);
-            return success;
-        }
-    };
-
-    template <typename T, typename Predicate>
-    GeneratorWrapper<T> filter(Predicate&& pred, GeneratorWrapper<T>&& generator) {
-        return GeneratorWrapper<T>(std::unique_ptr<IGenerator<T>>(pf::make_unique<FilterGenerator<T, Predicate>>(std::forward<Predicate>(pred), std::move(generator))));
-    }
-
-    template <typename T>
-    class RepeatGenerator : public IGenerator<T> {
-        GeneratorWrapper<T> m_generator;
-        mutable std::vector<T> m_returned;
-        size_t m_target_repeats;
-        size_t m_current_repeat = 0;
-        size_t m_repeat_index = 0;
-    public:
-        RepeatGenerator(size_t repeats, GeneratorWrapper<T>&& generator):
-            m_generator(std::move(generator)),
-            m_target_repeats(repeats)
-        {
-            assert(m_target_repeats > 0 && "Repeat generator must repeat at least once");
-        }
-
-        T const& get() const override {
-            if (m_current_repeat == 0) {
-                m_returned.push_back(m_generator.get());
-                return m_returned.back();
-            }
-            return m_returned[m_repeat_index];
-        }
-
-        bool next() override {
-            // There are 2 basic cases:
-            // 1) We are still reading the generator
-            // 2) We are reading our own cache
-
-            // In the first case, we need to poke the underlying generator.
-            // If it happily moves, we are left in that state, otherwise it is time to start reading from our cache
-            if (m_current_repeat == 0) {
-                const auto success = m_generator.next();
-                if (!success) {
-                    ++m_current_repeat;
-                }
-                return m_current_repeat < m_target_repeats;
-            }
-
-            // In the second case, we need to move indices forward and check that we haven't run up against the end
-            ++m_repeat_index;
-            if (m_repeat_index == m_returned.size()) {
-                m_repeat_index = 0;
-                ++m_current_repeat;
-            }
-            return m_current_repeat < m_target_repeats;
-        }
-    };
-
-    template <typename T>
-    GeneratorWrapper<T> repeat(size_t repeats, GeneratorWrapper<T>&& generator) {
-        return GeneratorWrapper<T>(pf::make_unique<RepeatGenerator<T>>(repeats, std::move(generator)));
-    }
-
-    template <typename T, typename U, typename Func>
-    class MapGenerator : public IGenerator<T> {
-        // TBD: provide static assert for mapping function, for friendly error message
-        GeneratorWrapper<U> m_generator;
-        Func m_function;
-        // To avoid returning dangling reference, we have to save the values
-        T m_cache;
-    public:
-        template <typename F2 = Func>
-        MapGenerator(F2&& function, GeneratorWrapper<U>&& generator) :
-            m_generator(std::move(generator)),
-            m_function(std::forward<F2>(function)),
-            m_cache(m_function(m_generator.get()))
-        {}
-
-        T const& get() const override {
-            return m_cache;
-        }
-        bool next() override {
-            const auto success = m_generator.next();
-            if (success) {
-                m_cache = m_function(m_generator.get());
-            }
-            return success;
-        }
-    };
-
-#if defined(__cpp_lib_is_invocable) && __cpp_lib_is_invocable >= 201703
-    // std::result_of is deprecated in C++17 and removed in C++20. Hence, it is
-    // replaced with std::invoke_result here. Also *_t format is preferred over
-    // typename *::type format.
-    template <typename Func, typename U>
-    using MapFunctionReturnType = std::remove_reference_t<std::remove_cv_t<std::invoke_result_t<Func, U>>>;
-#else
-    template <typename Func, typename U>
-    using MapFunctionReturnType = typename std::remove_reference<typename std::remove_cv<typename std::result_of<Func(U)>::type>::type>::type;
-#endif
-
-    template <typename Func, typename U, typename T = MapFunctionReturnType<Func, U>>
-    GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
-        return GeneratorWrapper<T>(
-            pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
-        );
-    }
-
-    template <typename T, typename U, typename Func>
-    GeneratorWrapper<T> map(Func&& function, GeneratorWrapper<U>&& generator) {
-        return GeneratorWrapper<T>(
-            pf::make_unique<MapGenerator<T, U, Func>>(std::forward<Func>(function), std::move(generator))
-        );
-    }
-
-    template <typename T>
-    class ChunkGenerator final : public IGenerator<std::vector<T>> {
-        std::vector<T> m_chunk;
-        size_t m_chunk_size;
-        GeneratorWrapper<T> m_generator;
-        bool m_used_up = false;
-    public:
-        ChunkGenerator(size_t size, GeneratorWrapper<T> generator) :
-            m_chunk_size(size), m_generator(std::move(generator))
-        {
-            m_chunk.reserve(m_chunk_size);
-            m_chunk.push_back(m_generator.get());
-            for (size_t i = 1; i < m_chunk_size; ++i) {
-                if (!m_generator.next()) {
-                    Catch::throw_exception(GeneratorException("Not enough values to initialize the first chunk"));
-                }
-                m_chunk.push_back(m_generator.get());
-            }
-        }
-        std::vector<T> const& get() const override {
-            return m_chunk;
-        }
-        bool next() override {
-            m_chunk.clear();
-            for (size_t idx = 0; idx < m_chunk_size; ++idx) {
-                if (!m_generator.next()) {
-                    return false;
-                }
-                m_chunk.push_back(m_generator.get());
-            }
-            return true;
-        }
-    };
-
-    template <typename T>
-    GeneratorWrapper<std::vector<T>> chunk(size_t size, GeneratorWrapper<T>&& generator) {
-        return GeneratorWrapper<std::vector<T>>(
-            pf::make_unique<ChunkGenerator<T>>(size, std::move(generator))
-        );
-    }
-
-} // namespace Generators
-} // namespace Catch
-
-// end catch_generators_generic.hpp
-// start catch_generators_specific.hpp
-
-// start catch_context.h
-
-#include <memory>
-
-namespace Catch {
-
-    struct IResultCapture;
-    struct IRunner;
-    struct IConfig;
-    struct IMutableContext;
-
-    using IConfigPtr = std::shared_ptr<IConfig const>;
-
-    struct IContext
-    {
-        virtual ~IContext();
-
-        virtual IResultCapture* getResultCapture() = 0;
-        virtual IRunner* getRunner() = 0;
-        virtual IConfigPtr const& getConfig() const = 0;
-    };
-
-    struct IMutableContext : IContext
-    {
-        virtual ~IMutableContext();
-        virtual void setResultCapture( IResultCapture* resultCapture ) = 0;
-        virtual void setRunner( IRunner* runner ) = 0;
-        virtual void setConfig( IConfigPtr const& config ) = 0;
-
-    private:
-        static IMutableContext *currentContext;
-        friend IMutableContext& getCurrentMutableContext();
-        friend void cleanUpContext();
-        static void createContext();
-    };
-
-    inline IMutableContext& getCurrentMutableContext()
-    {
-        if( !IMutableContext::currentContext )
-            IMutableContext::createContext();
-        return *IMutableContext::currentContext;
-    }
-
-    inline IContext& getCurrentContext()
-    {
-        return getCurrentMutableContext();
-    }
-
-    void cleanUpContext();
-}
-
-// end catch_context.h
-// start catch_interfaces_config.h
-
-#include <iosfwd>
-#include <string>
-#include <vector>
-#include <memory>
-
-namespace Catch {
-
-    enum class Verbosity {
-        Quiet = 0,
-        Normal,
-        High
-    };
-
-    struct WarnAbout { enum What {
-        Nothing = 0x00,
-        NoAssertions = 0x01,
-        NoTests = 0x02
-    }; };
-
-    struct ShowDurations { enum OrNot {
-        DefaultForReporter,
-        Always,
-        Never
-    }; };
-    struct RunTests { enum InWhatOrder {
-        InDeclarationOrder,
-        InLexicographicalOrder,
-        InRandomOrder
-    }; };
-    struct UseColour { enum YesOrNo {
-        Auto,
-        Yes,
-        No
-    }; };
-    struct WaitForKeypress { enum When {
-        Never,
-        BeforeStart = 1,
-        BeforeExit = 2,
-        BeforeStartAndExit = BeforeStart | BeforeExit
-    }; };
-
-    class TestSpec;
-
-    struct IConfig : NonCopyable {
-
-        virtual ~IConfig();
-
-        virtual bool allowThrows() const = 0;
-        virtual std::ostream& stream() const = 0;
-        virtual std::string name() const = 0;
-        virtual bool includeSuccessfulResults() const = 0;
-        virtual bool shouldDebugBreak() const = 0;
-        virtual bool warnAboutMissingAssertions() const = 0;
-        virtual bool warnAboutNoTests() const = 0;
-        virtual int abortAfter() const = 0;
-        virtual bool showInvisibles() const = 0;
-        virtual ShowDurations::OrNot showDurations() const = 0;
-        virtual TestSpec const& testSpec() const = 0;
-        virtual bool hasTestFilters() const = 0;
-        virtual std::vector<std::string> const& getTestsOrTags() const = 0;
-        virtual RunTests::InWhatOrder runOrder() const = 0;
-        virtual unsigned int rngSeed() const = 0;
-        virtual int benchmarkResolutionMultiple() const = 0;
-        virtual UseColour::YesOrNo useColour() const = 0;
-        virtual std::vector<std::string> const& getSectionsToRun() const = 0;
-        virtual Verbosity verbosity() const = 0;
-    };
-
-    using IConfigPtr = std::shared_ptr<IConfig const>;
-}
-
-// end catch_interfaces_config.h
-#include <random>
-
-namespace Catch {
-namespace Generators {
-
-template <typename Float>
-class RandomFloatingGenerator final : public IGenerator<Float> {
-    // FIXME: What is the right seed?
-    std::minstd_rand m_rand;
-    std::uniform_real_distribution<Float> m_dist;
-    Float m_current_number;
-public:
-
-    RandomFloatingGenerator(Float a, Float b):
-        m_rand(getCurrentContext().getConfig()->rngSeed()),
-        m_dist(a, b) {
-        static_cast<void>(next());
-    }
-
-    Float const& get() const override {
-        return m_current_number;
-    }
-    bool next() override {
-        m_current_number = m_dist(m_rand);
-        return true;
-    }
-};
-
-template <typename Integer>
-class RandomIntegerGenerator final : public IGenerator<Integer> {
-    std::minstd_rand m_rand;
-    std::uniform_int_distribution<Integer> m_dist;
-    Integer m_current_number;
-public:
-
-    RandomIntegerGenerator(Integer a, Integer b):
-        m_rand(getCurrentContext().getConfig()->rngSeed()),
-        m_dist(a, b) {
-        static_cast<void>(next());
-    }
-
-    Integer const& get() const override {
-        return m_current_number;
-    }
-    bool next() override {
-        m_current_number = m_dist(m_rand);
-        return true;
-    }
-};
-
-// TODO: Ideally this would be also constrained against the various char types,
-//       but I don't expect users to run into that in practice.
-template <typename T>
-typename std::enable_if<std::is_integral<T>::value && !std::is_same<T, bool>::value,
-GeneratorWrapper<T>>::type
-random(T a, T b) {
-    return GeneratorWrapper<T>(
-        pf::make_unique<RandomIntegerGenerator<T>>(a, b)
-    );
-}
-
-template <typename T>
-typename std::enable_if<std::is_floating_point<T>::value,
-GeneratorWrapper<T>>::type
-random(T a, T b) {
-    return GeneratorWrapper<T>(
-        pf::make_unique<RandomFloatingGenerator<T>>(a, b)
-    );
-}
-
-template <typename T>
-class RangeGenerator final : public IGenerator<T> {
-    T m_current;
-    T m_end;
-    T m_step;
-    bool m_positive;
-
-public:
-    RangeGenerator(T const& start, T const& end, T const& step):
-        m_current(start),
-        m_end(end),
-        m_step(step),
-        m_positive(m_step > T(0))
-    {
-        assert(m_current != m_end && "Range start and end cannot be equal");
-        assert(m_step != T(0) && "Step size cannot be zero");
-        assert(((m_positive && m_current <= m_end) || (!m_positive && m_current >= m_end)) && "Step moves away from end");
-    }
-
-    RangeGenerator(T const& start, T const& end):
-        RangeGenerator(start, end, (start < end) ? T(1) : T(-1))
-    {}
-
-    T const& get() const override {
-        return m_current;
-    }
-
-    bool next() override {
-        m_current += m_step;
-        return (m_positive) ? (m_current < m_end) : (m_current > m_end);
-    }
-};
-
-template <typename T>
-GeneratorWrapper<T> range(T const& start, T const& end, T const& step) {
-    static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer");
-    return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end, step));
-}
-
-template <typename T>
-GeneratorWrapper<T> range(T const& start, T const& end) {
-    static_assert(std::is_integral<T>::value && !std::is_same<T, bool>::value, "Type must be an integer");
-    return GeneratorWrapper<T>(pf::make_unique<RangeGenerator<T>>(start, end));
-}
-
-} // namespace Generators
-} // namespace Catch
-
-// end catch_generators_specific.hpp
-
-// These files are included here so the single_include script doesn't put them
-// in the conditionally compiled sections
-// start catch_test_case_info.h
-
-#include <string>
-#include <vector>
-#include <memory>
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-
-namespace Catch {
-
-    struct ITestInvoker;
-
-    struct TestCaseInfo {
-        enum SpecialProperties{
-            None = 0,
-            IsHidden = 1 << 1,
-            ShouldFail = 1 << 2,
-            MayFail = 1 << 3,
-            Throws = 1 << 4,
-            NonPortable = 1 << 5,
-            Benchmark = 1 << 6
-        };
-
-        TestCaseInfo(   std::string const& _name,
-                        std::string const& _className,
-                        std::string const& _description,
-                        std::vector<std::string> const& _tags,
-                        SourceLineInfo const& _lineInfo );
-
-        friend void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags );
-
-        bool isHidden() const;
-        bool throws() const;
-        bool okToFail() const;
-        bool expectedToFail() const;
-
-        std::string tagsAsString() const;
-
-        std::string name;
-        std::string className;
-        std::string description;
-        std::vector<std::string> tags;
-        std::vector<std::string> lcaseTags;
-        SourceLineInfo lineInfo;
-        SpecialProperties properties;
-    };
-
-    class TestCase : public TestCaseInfo {
-    public:
-
-        TestCase( ITestInvoker* testCase, TestCaseInfo&& info );
-
-        TestCase withName( std::string const& _newName ) const;
-
-        void invoke() const;
-
-        TestCaseInfo const& getTestCaseInfo() const;
-
-        bool operator == ( TestCase const& other ) const;
-        bool operator < ( TestCase const& other ) const;
-
-    private:
-        std::shared_ptr<ITestInvoker> test;
-    };
-
-    TestCase makeTestCase(  ITestInvoker* testCase,
-                            std::string const& className,
-                            NameAndTags const& nameAndTags,
-                            SourceLineInfo const& lineInfo );
-}
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-// end catch_test_case_info.h
-// start catch_interfaces_runner.h
-
-namespace Catch {
-
-    struct IRunner {
-        virtual ~IRunner();
-        virtual bool aborting() const = 0;
-    };
-}
-
-// end catch_interfaces_runner.h
-
-#ifdef __OBJC__
-// start catch_objc.hpp
-
-#import <objc/runtime.h>
-
-#include <string>
-
-// NB. Any general catch headers included here must be included
-// in catch.hpp first to make sure they are included by the single
-// header for non obj-usage
-
-///////////////////////////////////////////////////////////////////////////////
-// This protocol is really only here for (self) documenting purposes, since
-// all its methods are optional.
-@protocol OcFixture
-
-@optional
-
--(void) setUp;
--(void) tearDown;
-
-@end
-
-namespace Catch {
-
-    class OcMethod : public ITestInvoker {
-
-    public:
-        OcMethod( Class cls, SEL sel ) : m_cls( cls ), m_sel( sel ) {}
-
-        virtual void invoke() const {
-            id obj = [[m_cls alloc] init];
-
-            performOptionalSelector( obj, @selector(setUp)  );
-            performOptionalSelector( obj, m_sel );
-            performOptionalSelector( obj, @selector(tearDown)  );
-
-            arcSafeRelease( obj );
-        }
-    private:
-        virtual ~OcMethod() {}
-
-        Class m_cls;
-        SEL m_sel;
-    };
-
-    namespace Detail{
-
-        inline std::string getAnnotation(   Class cls,
-                                            std::string const& annotationName,
-                                            std::string const& testCaseName ) {
-            NSString* selStr = [[NSString alloc] initWithFormat:@"Catch_%s_%s", annotationName.c_str(), testCaseName.c_str()];
-            SEL sel = NSSelectorFromString( selStr );
-            arcSafeRelease( selStr );
-            id value = performOptionalSelector( cls, sel );
-            if( value )
-                return [(NSString*)value UTF8String];
-            return "";
-        }
-    }
-
-    inline std::size_t registerTestMethods() {
-        std::size_t noTestMethods = 0;
-        int noClasses = objc_getClassList( nullptr, 0 );
-
-        Class* classes = (CATCH_UNSAFE_UNRETAINED Class *)malloc( sizeof(Class) * noClasses);
-        objc_getClassList( classes, noClasses );
-
-        for( int c = 0; c < noClasses; c++ ) {
-            Class cls = classes[c];
-            {
-                u_int count;
-                Method* methods = class_copyMethodList( cls, &count );
-                for( u_int m = 0; m < count ; m++ ) {
-                    SEL selector = method_getName(methods[m]);
-                    std::string methodName = sel_getName(selector);
-                    if( startsWith( methodName, "Catch_TestCase_" ) ) {
-                        std::string testCaseName = methodName.substr( 15 );
-                        std::string name = Detail::getAnnotation( cls, "Name", testCaseName );
-                        std::string desc = Detail::getAnnotation( cls, "Description", testCaseName );
-                        const char* className = class_getName( cls );
-
-                        getMutableRegistryHub().registerTest( makeTestCase( new OcMethod( cls, selector ), className, NameAndTags( name.c_str(), desc.c_str() ), SourceLineInfo("",0) ) );
-                        noTestMethods++;
-                    }
-                }
-                free(methods);
-            }
-        }
-        return noTestMethods;
-    }
-
-#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
-
-    namespace Matchers {
-        namespace Impl {
-        namespace NSStringMatchers {
-
-            struct StringHolder : MatcherBase<NSString*>{
-                StringHolder( NSString* substr ) : m_substr( [substr copy] ){}
-                StringHolder( StringHolder const& other ) : m_substr( [other.m_substr copy] ){}
-                StringHolder() {
-                    arcSafeRelease( m_substr );
-                }
-
-                bool match( NSString* const& str ) const override {
-                    return false;
-                }
-
-                NSString* CATCH_ARC_STRONG m_substr;
-            };
-
-            struct Equals : StringHolder {
-                Equals( NSString* substr ) : StringHolder( substr ){}
-
-                bool match( NSString* const& str ) const override {
-                    return  (str != nil || m_substr == nil ) &&
-                            [str isEqualToString:m_substr];
-                }
-
-                std::string describe() const override {
-                    return "equals string: " + Catch::Detail::stringify( m_substr );
-                }
-            };
-
-            struct Contains : StringHolder {
-                Contains( NSString* substr ) : StringHolder( substr ){}
-
-                bool match( NSString* const& str ) const override {
-                    return  (str != nil || m_substr == nil ) &&
-                            [str rangeOfString:m_substr].location != NSNotFound;
-                }
-
-                std::string describe() const override {
-                    return "contains string: " + Catch::Detail::stringify( m_substr );
-                }
-            };
-
-            struct StartsWith : StringHolder {
-                StartsWith( NSString* substr ) : StringHolder( substr ){}
-
-                bool match( NSString* const& str ) const override {
-                    return  (str != nil || m_substr == nil ) &&
-                            [str rangeOfString:m_substr].location == 0;
-                }
-
-                std::string describe() const override {
-                    return "starts with: " + Catch::Detail::stringify( m_substr );
-                }
-            };
-            struct EndsWith : StringHolder {
-                EndsWith( NSString* substr ) : StringHolder( substr ){}
-
-                bool match( NSString* const& str ) const override {
-                    return  (str != nil || m_substr == nil ) &&
-                            [str rangeOfString:m_substr].location == [str length] - [m_substr length];
-                }
-
-                std::string describe() const override {
-                    return "ends with: " + Catch::Detail::stringify( m_substr );
-                }
-            };
-
-        } // namespace NSStringMatchers
-        } // namespace Impl
-
-        inline Impl::NSStringMatchers::Equals
-            Equals( NSString* substr ){ return Impl::NSStringMatchers::Equals( substr ); }
-
-        inline Impl::NSStringMatchers::Contains
-            Contains( NSString* substr ){ return Impl::NSStringMatchers::Contains( substr ); }
-
-        inline Impl::NSStringMatchers::StartsWith
-            StartsWith( NSString* substr ){ return Impl::NSStringMatchers::StartsWith( substr ); }
-
-        inline Impl::NSStringMatchers::EndsWith
-            EndsWith( NSString* substr ){ return Impl::NSStringMatchers::EndsWith( substr ); }
-
-    } // namespace Matchers
-
-    using namespace Matchers;
-
-#endif // CATCH_CONFIG_DISABLE_MATCHERS
-
-} // namespace Catch
-
-///////////////////////////////////////////////////////////////////////////////
-#define OC_MAKE_UNIQUE_NAME( root, uniqueSuffix ) root##uniqueSuffix
-#define OC_TEST_CASE2( name, desc, uniqueSuffix ) \
-+(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Name_test_, uniqueSuffix ) \
-{ \
-return @ name; \
-} \
-+(NSString*) OC_MAKE_UNIQUE_NAME( Catch_Description_test_, uniqueSuffix ) \
-{ \
-return @ desc; \
-} \
--(void) OC_MAKE_UNIQUE_NAME( Catch_TestCase_test_, uniqueSuffix )
-
-#define OC_TEST_CASE( name, desc ) OC_TEST_CASE2( name, desc, __LINE__ )
-
-// end catch_objc.hpp
-#endif
-
-#ifdef CATCH_CONFIG_EXTERNAL_INTERFACES
-// start catch_external_interfaces.h
-
-// start catch_reporter_bases.hpp
-
-// start catch_interfaces_reporter.h
-
-// start catch_config.hpp
-
-// start catch_test_spec_parser.h
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-
-// start catch_test_spec.h
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wpadded"
-#endif
-
-// start catch_wildcard_pattern.h
-
-namespace Catch
-{
-    class WildcardPattern {
-        enum WildcardPosition {
-            NoWildcard = 0,
-            WildcardAtStart = 1,
-            WildcardAtEnd = 2,
-            WildcardAtBothEnds = WildcardAtStart | WildcardAtEnd
-        };
-
-    public:
-
-        WildcardPattern( std::string const& pattern, CaseSensitive::Choice caseSensitivity );
-        virtual ~WildcardPattern() = default;
-        virtual bool matches( std::string const& str ) const;
-
-    private:
-        std::string adjustCase( std::string const& str ) const;
-        CaseSensitive::Choice m_caseSensitivity;
-        WildcardPosition m_wildcard = NoWildcard;
-        std::string m_pattern;
-    };
-}
-
-// end catch_wildcard_pattern.h
-#include <string>
-#include <vector>
-#include <memory>
-
-namespace Catch {
-
-    class TestSpec {
-        struct Pattern {
-            virtual ~Pattern();
-            virtual bool matches( TestCaseInfo const& testCase ) const = 0;
-        };
-        using PatternPtr = std::shared_ptr<Pattern>;
-
-        class NamePattern : public Pattern {
-        public:
-            NamePattern( std::string const& name );
-            virtual ~NamePattern();
-            bool matches( TestCaseInfo const& testCase ) const override;
-        private:
-            WildcardPattern m_wildcardPattern;
-        };
-
-        class TagPattern : public Pattern {
-        public:
-            TagPattern( std::string const& tag );
-            virtual ~TagPattern();
-            bool matches( TestCaseInfo const& testCase ) const override;
-        private:
-            std::string m_tag;
-        };
-
-        class ExcludedPattern : public Pattern {
-        public:
-            ExcludedPattern( PatternPtr const& underlyingPattern );
-            virtual ~ExcludedPattern();
-            bool matches( TestCaseInfo const& testCase ) const override;
-        private:
-            PatternPtr m_underlyingPattern;
-        };
-
-        struct Filter {
-            std::vector<PatternPtr> m_patterns;
-
-            bool matches( TestCaseInfo const& testCase ) const;
-        };
-
-    public:
-        bool hasFilters() const;
-        bool matches( TestCaseInfo const& testCase ) const;
-
-    private:
-        std::vector<Filter> m_filters;
-
-        friend class TestSpecParser;
-    };
-}
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-// end catch_test_spec.h
-// start catch_interfaces_tag_alias_registry.h
-
-#include <string>
-
-namespace Catch {
-
-    struct TagAlias;
-
-    struct ITagAliasRegistry {
-        virtual ~ITagAliasRegistry();
-        // Nullptr if not present
-        virtual TagAlias const* find( std::string const& alias ) const = 0;
-        virtual std::string expandAliases( std::string const& unexpandedTestSpec ) const = 0;
-
-        static ITagAliasRegistry const& get();
-    };
-
-} // end namespace Catch
-
-// end catch_interfaces_tag_alias_registry.h
-namespace Catch {
-
-    class TestSpecParser {
-        enum Mode{ None, Name, QuotedName, Tag, EscapedName };
-        Mode m_mode = None;
-        bool m_exclusion = false;
-        std::size_t m_start = std::string::npos, m_pos = 0;
-        std::string m_arg;
-        std::vector<std::size_t> m_escapeChars;
-        TestSpec::Filter m_currentFilter;
-        TestSpec m_testSpec;
-        ITagAliasRegistry const* m_tagAliases = nullptr;
-
-    public:
-        TestSpecParser( ITagAliasRegistry const& tagAliases );
-
-        TestSpecParser& parse( std::string const& arg );
-        TestSpec testSpec();
-
-    private:
-        void visitChar( char c );
-        void startNewMode( Mode mode, std::size_t start );
-        void escape();
-        std::string subString() const;
-
-        template<typename T>
-        void addPattern() {
-            std::string token = subString();
-            for( std::size_t i = 0; i < m_escapeChars.size(); ++i )
-                token = token.substr( 0, m_escapeChars[i]-m_start-i ) + token.substr( m_escapeChars[i]-m_start-i+1 );
-            m_escapeChars.clear();
-            if( startsWith( token, "exclude:" ) ) {
-                m_exclusion = true;
-                token = token.substr( 8 );
-            }
-            if( !token.empty() ) {
-                TestSpec::PatternPtr pattern = std::make_shared<T>( token );
-                if( m_exclusion )
-                    pattern = std::make_shared<TestSpec::ExcludedPattern>( pattern );
-                m_currentFilter.m_patterns.push_back( pattern );
-            }
-            m_exclusion = false;
-            m_mode = None;
-        }
-
-        void addFilter();
-    };
-    TestSpec parseTestSpec( std::string const& arg );
-
-} // namespace Catch
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-// end catch_test_spec_parser.h
-// Libstdc++ doesn't like incomplete classes for unique_ptr
-
-#include <memory>
-#include <vector>
-#include <string>
-
-#ifndef CATCH_CONFIG_CONSOLE_WIDTH
-#define CATCH_CONFIG_CONSOLE_WIDTH 80
-#endif
-
-namespace Catch {
-
-    struct IStream;
-
-    struct ConfigData {
-        bool listTests = false;
-        bool listTags = false;
-        bool listReporters = false;
-        bool listTestNamesOnly = false;
-
-        bool showSuccessfulTests = false;
-        bool shouldDebugBreak = false;
-        bool noThrow = false;
-        bool showHelp = false;
-        bool showInvisibles = false;
-        bool filenamesAsTags = false;
-        bool libIdentify = false;
-
-        int abortAfter = -1;
-        unsigned int rngSeed = 0;
-        int benchmarkResolutionMultiple = 100;
-
-        Verbosity verbosity = Verbosity::Normal;
-        WarnAbout::What warnings = WarnAbout::Nothing;
-        ShowDurations::OrNot showDurations = ShowDurations::DefaultForReporter;
-        RunTests::InWhatOrder runOrder = RunTests::InDeclarationOrder;
-        UseColour::YesOrNo useColour = UseColour::Auto;
-        WaitForKeypress::When waitForKeypress = WaitForKeypress::Never;
-
-        std::string outputFilename;
-        std::string name;
-        std::string processName;
-#ifndef CATCH_CONFIG_DEFAULT_REPORTER
-#define CATCH_CONFIG_DEFAULT_REPORTER "console"
-#endif
-        std::string reporterName = CATCH_CONFIG_DEFAULT_REPORTER;
-#undef CATCH_CONFIG_DEFAULT_REPORTER
-
-        std::vector<std::string> testsOrTags;
-        std::vector<std::string> sectionsToRun;
-    };
-
-    class Config : public IConfig {
-    public:
-
-        Config() = default;
-        Config( ConfigData const& data );
-        virtual ~Config() = default;
-
-        std::string const& getFilename() const;
-
-        bool listTests() const;
-        bool listTestNamesOnly() const;
-        bool listTags() const;
-        bool listReporters() const;
-
-        std::string getProcessName() const;
-        std::string const& getReporterName() const;
-
-        std::vector<std::string> const& getTestsOrTags() const override;
-        std::vector<std::string> const& getSectionsToRun() const override;
-
-        TestSpec const& testSpec() const override;
-        bool hasTestFilters() const override;
-
-        bool showHelp() const;
-
-        // IConfig interface
-        bool allowThrows() const override;
-        std::ostream& stream() const override;
-        std::string name() const override;
-        bool includeSuccessfulResults() const override;
-        bool warnAboutMissingAssertions() const override;
-        bool warnAboutNoTests() const override;
-        ShowDurations::OrNot showDurations() const override;
-        RunTests::InWhatOrder runOrder() const override;
-        unsigned int rngSeed() const override;
-        int benchmarkResolutionMultiple() const override;
-        UseColour::YesOrNo useColour() const override;
-        bool shouldDebugBreak() const override;
-        int abortAfter() const override;
-        bool showInvisibles() const override;
-        Verbosity verbosity() const override;
-
-    private:
-
-        IStream const* openStream();
-        ConfigData m_data;
-
-        std::unique_ptr<IStream const> m_stream;
-        TestSpec m_testSpec;
-        bool m_hasTestFilters = false;
-    };
-
-} // end namespace Catch
-
-// end catch_config.hpp
-// start catch_assertionresult.h
-
-#include <string>
-
-namespace Catch {
-
-    struct AssertionResultData
-    {
-        AssertionResultData() = delete;
-
-        AssertionResultData( ResultWas::OfType _resultType, LazyExpression const& _lazyExpression );
-
-        std::string message;
-        mutable std::string reconstructedExpression;
-        LazyExpression lazyExpression;
-        ResultWas::OfType resultType;
-
-        std::string reconstructExpression() const;
-    };
-
-    class AssertionResult {
-    public:
-        AssertionResult() = delete;
-        AssertionResult( AssertionInfo const& info, AssertionResultData const& data );
-
-        bool isOk() const;
-        bool succeeded() const;
-        ResultWas::OfType getResultType() const;
-        bool hasExpression() const;
-        bool hasMessage() const;
-        std::string getExpression() const;
-        std::string getExpressionInMacro() const;
-        bool hasExpandedExpression() const;
-        std::string getExpandedExpression() const;
-        std::string getMessage() const;
-        SourceLineInfo getSourceInfo() const;
-        StringRef getTestMacroName() const;
-
-    //protected:
-        AssertionInfo m_info;
-        AssertionResultData m_resultData;
-    };
-
-} // end namespace Catch
-
-// end catch_assertionresult.h
-// start catch_option.hpp
-
-namespace Catch {
-
-    // An optional type
-    template<typename T>
-    class Option {
-    public:
-        Option() : nullableValue( nullptr ) {}
-        Option( T const& _value )
-        : nullableValue( new( storage ) T( _value ) )
-        {}
-        Option( Option const& _other )
-        : nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
-        {}
-
-        ~Option() {
-            reset();
-        }
-
-        Option& operator= ( Option const& _other ) {
-            if( &_other != this ) {
-                reset();
-                if( _other )
-                    nullableValue = new( storage ) T( *_other );
-            }
-            return *this;
-        }
-        Option& operator = ( T const& _value ) {
-            reset();
-            nullableValue = new( storage ) T( _value );
-            return *this;
-        }
-
-        void reset() {
-            if( nullableValue )
-                nullableValue->~T();
-            nullableValue = nullptr;
-        }
-
-        T& operator*() { return *nullableValue; }
-        T const& operator*() const { return *nullableValue; }
-        T* operator->() { return nullableValue; }
-        const T* operator->() const { return nullableValue; }
-
-        T valueOr( T const& defaultValue ) const {
-            return nullableValue ? *nullableValue : defaultValue;
-        }
-
-        bool some() const { return nullableValue != nullptr; }
-        bool none() const { return nullableValue == nullptr; }
-
-        bool operator !() const { return nullableValue == nullptr; }
-        explicit operator bool() const {
-            return some();
-        }
-
-    private:
-        T *nullableValue;
-        alignas(alignof(T)) char storage[sizeof(T)];
-    };
-
-} // end namespace Catch
-
-// end catch_option.hpp
-#include <string>
-#include <iosfwd>
-#include <map>
-#include <set>
-#include <memory>
-
-namespace Catch {
-
-    struct ReporterConfig {
-        explicit ReporterConfig( IConfigPtr const& _fullConfig );
-
-        ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream );
-
-        std::ostream& stream() const;
-        IConfigPtr fullConfig() const;
-
-    private:
-        std::ostream* m_stream;
-        IConfigPtr m_fullConfig;
-    };
-
-    struct ReporterPreferences {
-        bool shouldRedirectStdOut = false;
-        bool shouldReportAllAssertions = false;
-    };
-
-    template<typename T>
-    struct LazyStat : Option<T> {
-        LazyStat& operator=( T const& _value ) {
-            Option<T>::operator=( _value );
-            used = false;
-            return *this;
-        }
-        void reset() {
-            Option<T>::reset();
-            used = false;
-        }
-        bool used = false;
-    };
-
-    struct TestRunInfo {
-        TestRunInfo( std::string const& _name );
-        std::string name;
-    };
-    struct GroupInfo {
-        GroupInfo(  std::string const& _name,
-                    std::size_t _groupIndex,
-                    std::size_t _groupsCount );
-
-        std::string name;
-        std::size_t groupIndex;
-        std::size_t groupsCounts;
-    };
-
-    struct AssertionStats {
-        AssertionStats( AssertionResult const& _assertionResult,
-                        std::vector<MessageInfo> const& _infoMessages,
-                        Totals const& _totals );
-
-        AssertionStats( AssertionStats const& )              = default;
-        AssertionStats( AssertionStats && )                  = default;
-        AssertionStats& operator = ( AssertionStats const& ) = delete;
-        AssertionStats& operator = ( AssertionStats && )     = delete;
-        virtual ~AssertionStats();
-
-        AssertionResult assertionResult;
-        std::vector<MessageInfo> infoMessages;
-        Totals totals;
-    };
-
-    struct SectionStats {
-        SectionStats(   SectionInfo const& _sectionInfo,
-                        Counts const& _assertions,
-                        double _durationInSeconds,
-                        bool _missingAssertions );
-        SectionStats( SectionStats const& )              = default;
-        SectionStats( SectionStats && )                  = default;
-        SectionStats& operator = ( SectionStats const& ) = default;
-        SectionStats& operator = ( SectionStats && )     = default;
-        virtual ~SectionStats();
-
-        SectionInfo sectionInfo;
-        Counts assertions;
-        double durationInSeconds;
-        bool missingAssertions;
-    };
-
-    struct TestCaseStats {
-        TestCaseStats(  TestCaseInfo const& _testInfo,
-                        Totals const& _totals,
-                        std::string const& _stdOut,
-                        std::string const& _stdErr,
-                        bool _aborting );
-
-        TestCaseStats( TestCaseStats const& )              = default;
-        TestCaseStats( TestCaseStats && )                  = default;
-        TestCaseStats& operator = ( TestCaseStats const& ) = default;
-        TestCaseStats& operator = ( TestCaseStats && )     = default;
-        virtual ~TestCaseStats();
-
-        TestCaseInfo testInfo;
-        Totals totals;
-        std::string stdOut;
-        std::string stdErr;
-        bool aborting;
-    };
-
-    struct TestGroupStats {
-        TestGroupStats( GroupInfo const& _groupInfo,
-                        Totals const& _totals,
-                        bool _aborting );
-        TestGroupStats( GroupInfo const& _groupInfo );
-
-        TestGroupStats( TestGroupStats const& )              = default;
-        TestGroupStats( TestGroupStats && )                  = default;
-        TestGroupStats& operator = ( TestGroupStats const& ) = default;
-        TestGroupStats& operator = ( TestGroupStats && )     = default;
-        virtual ~TestGroupStats();
-
-        GroupInfo groupInfo;
-        Totals totals;
-        bool aborting;
-    };
-
-    struct TestRunStats {
-        TestRunStats(   TestRunInfo const& _runInfo,
-                        Totals const& _totals,
-                        bool _aborting );
-
-        TestRunStats( TestRunStats const& )              = default;
-        TestRunStats( TestRunStats && )                  = default;
-        TestRunStats& operator = ( TestRunStats const& ) = default;
-        TestRunStats& operator = ( TestRunStats && )     = default;
-        virtual ~TestRunStats();
-
-        TestRunInfo runInfo;
-        Totals totals;
-        bool aborting;
-    };
-
-    struct BenchmarkInfo {
-        std::string name;
-    };
-    struct BenchmarkStats {
-        BenchmarkInfo info;
-        std::size_t iterations;
-        uint64_t elapsedTimeInNanoseconds;
-    };
-
-    struct IStreamingReporter {
-        virtual ~IStreamingReporter() = default;
-
-        // Implementing class must also provide the following static methods:
-        // static std::string getDescription();
-        // static std::set<Verbosity> getSupportedVerbosities()
-
-        virtual ReporterPreferences getPreferences() const = 0;
-
-        virtual void noMatchingTestCases( std::string const& spec ) = 0;
-
-        virtual void testRunStarting( TestRunInfo const& testRunInfo ) = 0;
-        virtual void testGroupStarting( GroupInfo const& groupInfo ) = 0;
-
-        virtual void testCaseStarting( TestCaseInfo const& testInfo ) = 0;
-        virtual void sectionStarting( SectionInfo const& sectionInfo ) = 0;
-
-        // *** experimental ***
-        virtual void benchmarkStarting( BenchmarkInfo const& ) {}
-
-        virtual void assertionStarting( AssertionInfo const& assertionInfo ) = 0;
-
-        // The return value indicates if the messages buffer should be cleared:
-        virtual bool assertionEnded( AssertionStats const& assertionStats ) = 0;
-
-        // *** experimental ***
-        virtual void benchmarkEnded( BenchmarkStats const& ) {}
-
-        virtual void sectionEnded( SectionStats const& sectionStats ) = 0;
-        virtual void testCaseEnded( TestCaseStats const& testCaseStats ) = 0;
-        virtual void testGroupEnded( TestGroupStats const& testGroupStats ) = 0;
-        virtual void testRunEnded( TestRunStats const& testRunStats ) = 0;
-
-        virtual void skipTest( TestCaseInfo const& testInfo ) = 0;
-
-        // Default empty implementation provided
-        virtual void fatalErrorEncountered( StringRef name );
-
-        virtual bool isMulti() const;
-    };
-    using IStreamingReporterPtr = std::unique_ptr<IStreamingReporter>;
-
-    struct IReporterFactory {
-        virtual ~IReporterFactory();
-        virtual IStreamingReporterPtr create( ReporterConfig const& config ) const = 0;
-        virtual std::string getDescription() const = 0;
-    };
-    using IReporterFactoryPtr = std::shared_ptr<IReporterFactory>;
-
-    struct IReporterRegistry {
-        using FactoryMap = std::map<std::string, IReporterFactoryPtr>;
-        using Listeners = std::vector<IReporterFactoryPtr>;
-
-        virtual ~IReporterRegistry();
-        virtual IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const = 0;
-        virtual FactoryMap const& getFactories() const = 0;
-        virtual Listeners const& getListeners() const = 0;
-    };
-
-} // end namespace Catch
-
-// end catch_interfaces_reporter.h
-#include <algorithm>
-#include <cstring>
-#include <cfloat>
-#include <cstdio>
-#include <cassert>
-#include <memory>
-#include <ostream>
-
-namespace Catch {
-    void prepareExpandedExpression(AssertionResult& result);
-
-    // Returns double formatted as %.3f (format expected on output)
-    std::string getFormattedDuration( double duration );
-
-    std::string serializeFilters( std::vector<std::string> const& container );
-
-    template<typename DerivedT>
-    struct StreamingReporterBase : IStreamingReporter {
-
-        StreamingReporterBase( ReporterConfig const& _config )
-        :   m_config( _config.fullConfig() ),
-            stream( _config.stream() )
-        {
-            m_reporterPrefs.shouldRedirectStdOut = false;
-            if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) )
-                CATCH_ERROR( "Verbosity level not supported by this reporter" );
-        }
-
-        ReporterPreferences getPreferences() const override {
-            return m_reporterPrefs;
-        }
-
-        static std::set<Verbosity> getSupportedVerbosities() {
-            return { Verbosity::Normal };
-        }
-
-        ~StreamingReporterBase() override = default;
-
-        void noMatchingTestCases(std::string const&) override {}
-
-        void testRunStarting(TestRunInfo const& _testRunInfo) override {
-            currentTestRunInfo = _testRunInfo;
-        }
-
-        void testGroupStarting(GroupInfo const& _groupInfo) override {
-            currentGroupInfo = _groupInfo;
-        }
-
-        void testCaseStarting(TestCaseInfo const& _testInfo) override  {
-            currentTestCaseInfo = _testInfo;
-        }
-        void sectionStarting(SectionInfo const& _sectionInfo) override {
-            m_sectionStack.push_back(_sectionInfo);
-        }
-
-        void sectionEnded(SectionStats const& /* _sectionStats */) override {
-            m_sectionStack.pop_back();
-        }
-        void testCaseEnded(TestCaseStats const& /* _testCaseStats */) override {
-            currentTestCaseInfo.reset();
-        }
-        void testGroupEnded(TestGroupStats const& /* _testGroupStats */) override {
-            currentGroupInfo.reset();
-        }
-        void testRunEnded(TestRunStats const& /* _testRunStats */) override {
-            currentTestCaseInfo.reset();
-            currentGroupInfo.reset();
-            currentTestRunInfo.reset();
-        }
-
-        void skipTest(TestCaseInfo const&) override {
-            // Don't do anything with this by default.
-            // It can optionally be overridden in the derived class.
-        }
-
-        IConfigPtr m_config;
-        std::ostream& stream;
-
-        LazyStat<TestRunInfo> currentTestRunInfo;
-        LazyStat<GroupInfo> currentGroupInfo;
-        LazyStat<TestCaseInfo> currentTestCaseInfo;
-
-        std::vector<SectionInfo> m_sectionStack;
-        ReporterPreferences m_reporterPrefs;
-    };
-
-    template<typename DerivedT>
-    struct CumulativeReporterBase : IStreamingReporter {
-        template<typename T, typename ChildNodeT>
-        struct Node {
-            explicit Node( T const& _value ) : value( _value ) {}
-            virtual ~Node() {}
-
-            using ChildNodes = std::vector<std::shared_ptr<ChildNodeT>>;
-            T value;
-            ChildNodes children;
-        };
-        struct SectionNode {
-            explicit SectionNode(SectionStats const& _stats) : stats(_stats) {}
-            virtual ~SectionNode() = default;
-
-            bool operator == (SectionNode const& other) const {
-                return stats.sectionInfo.lineInfo == other.stats.sectionInfo.lineInfo;
-            }
-            bool operator == (std::shared_ptr<SectionNode> const& other) const {
-                return operator==(*other);
-            }
-
-            SectionStats stats;
-            using ChildSections = std::vector<std::shared_ptr<SectionNode>>;
-            using Assertions = std::vector<AssertionStats>;
-            ChildSections childSections;
-            Assertions assertions;
-            std::string stdOut;
-            std::string stdErr;
-        };
-
-        struct BySectionInfo {
-            BySectionInfo( SectionInfo const& other ) : m_other( other ) {}
-            BySectionInfo( BySectionInfo const& other ) : m_other( other.m_other ) {}
-            bool operator() (std::shared_ptr<SectionNode> const& node) const {
-                return ((node->stats.sectionInfo.name == m_other.name) &&
-                        (node->stats.sectionInfo.lineInfo == m_other.lineInfo));
-            }
-            void operator=(BySectionInfo const&) = delete;
-
-        private:
-            SectionInfo const& m_other;
-        };
-
-        using TestCaseNode = Node<TestCaseStats, SectionNode>;
-        using TestGroupNode = Node<TestGroupStats, TestCaseNode>;
-        using TestRunNode = Node<TestRunStats, TestGroupNode>;
-
-        CumulativeReporterBase( ReporterConfig const& _config )
-        :   m_config( _config.fullConfig() ),
-            stream( _config.stream() )
-        {
-            m_reporterPrefs.shouldRedirectStdOut = false;
-            if( !DerivedT::getSupportedVerbosities().count( m_config->verbosity() ) )
-                CATCH_ERROR( "Verbosity level not supported by this reporter" );
-        }
-        ~CumulativeReporterBase() override = default;
-
-        ReporterPreferences getPreferences() const override {
-            return m_reporterPrefs;
-        }
-
-        static std::set<Verbosity> getSupportedVerbosities() {
-            return { Verbosity::Normal };
-        }
-
-        void testRunStarting( TestRunInfo const& ) override {}
-        void testGroupStarting( GroupInfo const& ) override {}
-
-        void testCaseStarting( TestCaseInfo const& ) override {}
-
-        void sectionStarting( SectionInfo const& sectionInfo ) override {
-            SectionStats incompleteStats( sectionInfo, Counts(), 0, false );
-            std::shared_ptr<SectionNode> node;
-            if( m_sectionStack.empty() ) {
-                if( !m_rootSection )
-                    m_rootSection = std::make_shared<SectionNode>( incompleteStats );
-                node = m_rootSection;
-            }
-            else {
-                SectionNode& parentNode = *m_sectionStack.back();
-                auto it =
-                    std::find_if(   parentNode.childSections.begin(),
-                                    parentNode.childSections.end(),
-                                    BySectionInfo( sectionInfo ) );
-                if( it == parentNode.childSections.end() ) {
-                    node = std::make_shared<SectionNode>( incompleteStats );
-                    parentNode.childSections.push_back( node );
-                }
-                else
-                    node = *it;
-            }
-            m_sectionStack.push_back( node );
-            m_deepestSection = std::move(node);
-        }
-
-        void assertionStarting(AssertionInfo const&) override {}
-
-        bool assertionEnded(AssertionStats const& assertionStats) override {
-            assert(!m_sectionStack.empty());
-            // AssertionResult holds a pointer to a temporary DecomposedExpression,
-            // which getExpandedExpression() calls to build the expression string.
-            // Our section stack copy of the assertionResult will likely outlive the
-            // temporary, so it must be expanded or discarded now to avoid calling
-            // a destroyed object later.
-            prepareExpandedExpression(const_cast<AssertionResult&>( assertionStats.assertionResult ) );
-            SectionNode& sectionNode = *m_sectionStack.back();
-            sectionNode.assertions.push_back(assertionStats);
-            return true;
-        }
-        void sectionEnded(SectionStats const& sectionStats) override {
-            assert(!m_sectionStack.empty());
-            SectionNode& node = *m_sectionStack.back();
-            node.stats = sectionStats;
-            m_sectionStack.pop_back();
-        }
-        void testCaseEnded(TestCaseStats const& testCaseStats) override {
-            auto node = std::make_shared<TestCaseNode>(testCaseStats);
-            assert(m_sectionStack.size() == 0);
-            node->children.push_back(m_rootSection);
-            m_testCases.push_back(node);
-            m_rootSection.reset();
-
-            assert(m_deepestSection);
-            m_deepestSection->stdOut = testCaseStats.stdOut;
-            m_deepestSection->stdErr = testCaseStats.stdErr;
-        }
-        void testGroupEnded(TestGroupStats const& testGroupStats) override {
-            auto node = std::make_shared<TestGroupNode>(testGroupStats);
-            node->children.swap(m_testCases);
-            m_testGroups.push_back(node);
-        }
-        void testRunEnded(TestRunStats const& testRunStats) override {
-            auto node = std::make_shared<TestRunNode>(testRunStats);
-            node->children.swap(m_testGroups);
-            m_testRuns.push_back(node);
-            testRunEndedCumulative();
-        }
-        virtual void testRunEndedCumulative() = 0;
-
-        void skipTest(TestCaseInfo const&) override {}
-
-        IConfigPtr m_config;
-        std::ostream& stream;
-        std::vector<AssertionStats> m_assertions;
-        std::vector<std::vector<std::shared_ptr<SectionNode>>> m_sections;
-        std::vector<std::shared_ptr<TestCaseNode>> m_testCases;
-        std::vector<std::shared_ptr<TestGroupNode>> m_testGroups;
-
-        std::vector<std::shared_ptr<TestRunNode>> m_testRuns;
-
-        std::shared_ptr<SectionNode> m_rootSection;
-        std::shared_ptr<SectionNode> m_deepestSection;
-        std::vector<std::shared_ptr<SectionNode>> m_sectionStack;
-        ReporterPreferences m_reporterPrefs;
-    };
-
-    template<char C>
-    char const* getLineOfChars() {
-        static char line[CATCH_CONFIG_CONSOLE_WIDTH] = {0};
-        if( !*line ) {
-            std::memset( line, C, CATCH_CONFIG_CONSOLE_WIDTH-1 );
-            line[CATCH_CONFIG_CONSOLE_WIDTH-1] = 0;
-        }
-        return line;
-    }
-
-    struct TestEventListenerBase : StreamingReporterBase<TestEventListenerBase> {
-        TestEventListenerBase( ReporterConfig const& _config );
-
-        static std::set<Verbosity> getSupportedVerbosities();
-
-        void assertionStarting(AssertionInfo const&) override;
-        bool assertionEnded(AssertionStats const&) override;
-    };
-
-} // end namespace Catch
-
-// end catch_reporter_bases.hpp
-// start catch_console_colour.h
-
-namespace Catch {
-
-    struct Colour {
-        enum Code {
-            None = 0,
-
-            White,
-            Red,
-            Green,
-            Blue,
-            Cyan,
-            Yellow,
-            Grey,
-
-            Bright = 0x10,
-
-            BrightRed = Bright | Red,
-            BrightGreen = Bright | Green,
-            LightGrey = Bright | Grey,
-            BrightWhite = Bright | White,
-            BrightYellow = Bright | Yellow,
-
-            // By intention
-            FileName = LightGrey,
-            Warning = BrightYellow,
-            ResultError = BrightRed,
-            ResultSuccess = BrightGreen,
-            ResultExpectedFailure = Warning,
-
-            Error = BrightRed,
-            Success = Green,
-
-            OriginalExpression = Cyan,
-            ReconstructedExpression = BrightYellow,
-
-            SecondaryText = LightGrey,
-            Headers = White
-        };
-
-        // Use constructed object for RAII guard
-        Colour( Code _colourCode );
-        Colour( Colour&& other ) noexcept;
-        Colour& operator=( Colour&& other ) noexcept;
-        ~Colour();
-
-        // Use static method for one-shot changes
-        static void use( Code _colourCode );
-
-    private:
-        bool m_moved = false;
-    };
-
-    std::ostream& operator << ( std::ostream& os, Colour const& );
-
-} // end namespace Catch
-
-// end catch_console_colour.h
-// start catch_reporter_registrars.hpp
-
-
-namespace Catch {
-
-    template<typename T>
-    class ReporterRegistrar {
-
-        class ReporterFactory : public IReporterFactory {
-
-            IStreamingReporterPtr create( ReporterConfig const& config ) const override {
-                return std::unique_ptr<T>( new T( config ) );
-            }
-
-            std::string getDescription() const override {
-                return T::getDescription();
-            }
-        };
-
-    public:
-
-        explicit ReporterRegistrar( std::string const& name ) {
-            getMutableRegistryHub().registerReporter( name, std::make_shared<ReporterFactory>() );
-        }
-    };
-
-    template<typename T>
-    class ListenerRegistrar {
-
-        class ListenerFactory : public IReporterFactory {
-
-            IStreamingReporterPtr create( ReporterConfig const& config ) const override {
-                return std::unique_ptr<T>( new T( config ) );
-            }
-            std::string getDescription() const override {
-                return std::string();
-            }
-        };
-
-    public:
-
-        ListenerRegistrar() {
-            getMutableRegistryHub().registerListener( std::make_shared<ListenerFactory>() );
-        }
-    };
-}
-
-#if !defined(CATCH_CONFIG_DISABLE)
-
-#define CATCH_REGISTER_REPORTER( name, reporterType ) \
-    CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS          \
-    namespace{ Catch::ReporterRegistrar<reporterType> catch_internal_RegistrarFor##reporterType( name ); } \
-    CATCH_INTERNAL_UNSUPPRESS_GLOBALS_WARNINGS
-
-#define CATCH_REGISTER_LISTENER( listenerType ) \
-     CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS   \
-     namespace{ Catch::ListenerRegistrar<listenerType> catch_internal_RegistrarFor##listenerType; } \
-     CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS
-#else // CATCH_CONFIG_DISABLE
-
-#define CATCH_REGISTER_REPORTER(name, reporterType)
-#define CATCH_REGISTER_LISTENER(listenerType)
-
-#endif // CATCH_CONFIG_DISABLE
-
-// end catch_reporter_registrars.hpp
-// Allow users to base their work off existing reporters
-// start catch_reporter_compact.h
-
-namespace Catch {
-
-    struct CompactReporter : StreamingReporterBase<CompactReporter> {
-
-        using StreamingReporterBase::StreamingReporterBase;
-
-        ~CompactReporter() override;
-
-        static std::string getDescription();
-
-        ReporterPreferences getPreferences() const override;
-
-        void noMatchingTestCases(std::string const& spec) override;
-
-        void assertionStarting(AssertionInfo const&) override;
-
-        bool assertionEnded(AssertionStats const& _assertionStats) override;
-
-        void sectionEnded(SectionStats const& _sectionStats) override;
-
-        void testRunEnded(TestRunStats const& _testRunStats) override;
-
-    };
-
-} // end namespace Catch
-
-// end catch_reporter_compact.h
-// start catch_reporter_console.h
-
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
-                              // Note that 4062 (not all labels are handled
-                              // and default is missing) is enabled
-#endif
-
-namespace Catch {
-    // Fwd decls
-    struct SummaryColumn;
-    class TablePrinter;
-
-    struct ConsoleReporter : StreamingReporterBase<ConsoleReporter> {
-        std::unique_ptr<TablePrinter> m_tablePrinter;
-
-        ConsoleReporter(ReporterConfig const& config);
-        ~ConsoleReporter() override;
-        static std::string getDescription();
-
-        void noMatchingTestCases(std::string const& spec) override;
-
-        void assertionStarting(AssertionInfo const&) override;
-
-        bool assertionEnded(AssertionStats const& _assertionStats) override;
-
-        void sectionStarting(SectionInfo const& _sectionInfo) override;
-        void sectionEnded(SectionStats const& _sectionStats) override;
-
-        void benchmarkStarting(BenchmarkInfo const& info) override;
-        void benchmarkEnded(BenchmarkStats const& stats) override;
-
-        void testCaseEnded(TestCaseStats const& _testCaseStats) override;
-        void testGroupEnded(TestGroupStats const& _testGroupStats) override;
-        void testRunEnded(TestRunStats const& _testRunStats) override;
-        void testRunStarting(TestRunInfo const& _testRunInfo) override;
-    private:
-
-        void lazyPrint();
-
-        void lazyPrintWithoutClosingBenchmarkTable();
-        void lazyPrintRunInfo();
-        void lazyPrintGroupInfo();
-        void printTestCaseAndSectionHeader();
-
-        void printClosedHeader(std::string const& _name);
-        void printOpenHeader(std::string const& _name);
-
-        // if string has a : in first line will set indent to follow it on
-        // subsequent lines
-        void printHeaderString(std::string const& _string, std::size_t indent = 0);
-
-        void printTotals(Totals const& totals);
-        void printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row);
-
-        void printTotalsDivider(Totals const& totals);
-        void printSummaryDivider();
-        void printTestFilters();
-
-    private:
-        bool m_headerPrinted = false;
-    };
-
-} // end namespace Catch
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-
-// end catch_reporter_console.h
-// start catch_reporter_junit.h
-
-// start catch_xmlwriter.h
-
-#include <vector>
-
-namespace Catch {
-
-    class XmlEncode {
-    public:
-        enum ForWhat { ForTextNodes, ForAttributes };
-
-        XmlEncode( std::string const& str, ForWhat forWhat = ForTextNodes );
-
-        void encodeTo( std::ostream& os ) const;
-
-        friend std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode );
-
-    private:
-        std::string m_str;
-        ForWhat m_forWhat;
-    };
-
-    class XmlWriter {
-    public:
-
-        class ScopedElement {
-        public:
-            ScopedElement( XmlWriter* writer );
-
-            ScopedElement( ScopedElement&& other ) noexcept;
-            ScopedElement& operator=( ScopedElement&& other ) noexcept;
-
-            ~ScopedElement();
-
-            ScopedElement& writeText( std::string const& text, bool indent = true );
-
-            template<typename T>
-            ScopedElement& writeAttribute( std::string const& name, T const& attribute ) {
-                m_writer->writeAttribute( name, attribute );
-                return *this;
-            }
-
-        private:
-            mutable XmlWriter* m_writer = nullptr;
-        };
-
-        XmlWriter( std::ostream& os = Catch::cout() );
-        ~XmlWriter();
-
-        XmlWriter( XmlWriter const& ) = delete;
-        XmlWriter& operator=( XmlWriter const& ) = delete;
-
-        XmlWriter& startElement( std::string const& name );
-
-        ScopedElement scopedElement( std::string const& name );
-
-        XmlWriter& endElement();
-
-        XmlWriter& writeAttribute( std::string const& name, std::string const& attribute );
-
-        XmlWriter& writeAttribute( std::string const& name, bool attribute );
-
-        template<typename T>
-        XmlWriter& writeAttribute( std::string const& name, T const& attribute ) {
-            ReusableStringStream rss;
-            rss << attribute;
-            return writeAttribute( name, rss.str() );
-        }
-
-        XmlWriter& writeText( std::string const& text, bool indent = true );
-
-        XmlWriter& writeComment( std::string const& text );
-
-        void writeStylesheetRef( std::string const& url );
-
-        XmlWriter& writeBlankLine();
-
-        void ensureTagClosed();
-
-    private:
-
-        void writeDeclaration();
-
-        void newlineIfNecessary();
-
-        bool m_tagIsOpen = false;
-        bool m_needsNewline = false;
-        std::vector<std::string> m_tags;
-        std::string m_indent;
-        std::ostream& m_os;
-    };
-
-}
-
-// end catch_xmlwriter.h
-namespace Catch {
-
-    class JunitReporter : public CumulativeReporterBase<JunitReporter> {
-    public:
-        JunitReporter(ReporterConfig const& _config);
-
-        ~JunitReporter() override;
-
-        static std::string getDescription();
-
-        void noMatchingTestCases(std::string const& /*spec*/) override;
-
-        void testRunStarting(TestRunInfo const& runInfo) override;
-
-        void testGroupStarting(GroupInfo const& groupInfo) override;
-
-        void testCaseStarting(TestCaseInfo const& testCaseInfo) override;
-        bool assertionEnded(AssertionStats const& assertionStats) override;
-
-        void testCaseEnded(TestCaseStats const& testCaseStats) override;
-
-        void testGroupEnded(TestGroupStats const& testGroupStats) override;
-
-        void testRunEndedCumulative() override;
-
-        void writeGroup(TestGroupNode const& groupNode, double suiteTime);
-
-        void writeTestCase(TestCaseNode const& testCaseNode);
-
-        void writeSection(std::string const& className,
-                          std::string const& rootName,
-                          SectionNode const& sectionNode);
-
-        void writeAssertions(SectionNode const& sectionNode);
-        void writeAssertion(AssertionStats const& stats);
-
-        XmlWriter xml;
-        Timer suiteTimer;
-        std::string stdOutForSuite;
-        std::string stdErrForSuite;
-        unsigned int unexpectedExceptions = 0;
-        bool m_okToFail = false;
-    };
-
-} // end namespace Catch
-
-// end catch_reporter_junit.h
-// start catch_reporter_xml.h
-
-namespace Catch {
-    class XmlReporter : public StreamingReporterBase<XmlReporter> {
-    public:
-        XmlReporter(ReporterConfig const& _config);
-
-        ~XmlReporter() override;
-
-        static std::string getDescription();
-
-        virtual std::string getStylesheetRef() const;
-
-        void writeSourceInfo(SourceLineInfo const& sourceInfo);
-
-    public: // StreamingReporterBase
-
-        void noMatchingTestCases(std::string const& s) override;
-
-        void testRunStarting(TestRunInfo const& testInfo) override;
-
-        void testGroupStarting(GroupInfo const& groupInfo) override;
-
-        void testCaseStarting(TestCaseInfo const& testInfo) override;
-
-        void sectionStarting(SectionInfo const& sectionInfo) override;
-
-        void assertionStarting(AssertionInfo const&) override;
-
-        bool assertionEnded(AssertionStats const& assertionStats) override;
-
-        void sectionEnded(SectionStats const& sectionStats) override;
-
-        void testCaseEnded(TestCaseStats const& testCaseStats) override;
-
-        void testGroupEnded(TestGroupStats const& testGroupStats) override;
-
-        void testRunEnded(TestRunStats const& testRunStats) override;
-
-    private:
-        Timer m_testCaseTimer;
-        XmlWriter m_xml;
-        int m_sectionDepth = 0;
-    };
-
-} // end namespace Catch
-
-// end catch_reporter_xml.h
-
-// end catch_external_interfaces.h
-#endif
-
-#endif // ! CATCH_CONFIG_IMPL_ONLY
-
-#ifdef CATCH_IMPL
-// start catch_impl.hpp
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wweak-vtables"
-#endif
-
-// Keep these here for external reporters
-// start catch_test_case_tracker.h
-
-#include <string>
-#include <vector>
-#include <memory>
-
-namespace Catch {
-namespace TestCaseTracking {
-
-    struct NameAndLocation {
-        std::string name;
-        SourceLineInfo location;
-
-        NameAndLocation( std::string const& _name, SourceLineInfo const& _location );
-    };
-
-    struct ITracker;
-
-    using ITrackerPtr = std::shared_ptr<ITracker>;
-
-    struct ITracker {
-        virtual ~ITracker();
-
-        // static queries
-        virtual NameAndLocation const& nameAndLocation() const = 0;
-
-        // dynamic queries
-        virtual bool isComplete() const = 0; // Successfully completed or failed
-        virtual bool isSuccessfullyCompleted() const = 0;
-        virtual bool isOpen() const = 0; // Started but not complete
-        virtual bool hasChildren() const = 0;
-
-        virtual ITracker& parent() = 0;
-
-        // actions
-        virtual void close() = 0; // Successfully complete
-        virtual void fail() = 0;
-        virtual void markAsNeedingAnotherRun() = 0;
-
-        virtual void addChild( ITrackerPtr const& child ) = 0;
-        virtual ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) = 0;
-        virtual void openChild() = 0;
-
-        // Debug/ checking
-        virtual bool isSectionTracker() const = 0;
-        virtual bool isGeneratorTracker() const = 0;
-    };
-
-    class TrackerContext {
-
-        enum RunState {
-            NotStarted,
-            Executing,
-            CompletedCycle
-        };
-
-        ITrackerPtr m_rootTracker;
-        ITracker* m_currentTracker = nullptr;
-        RunState m_runState = NotStarted;
-
-    public:
-
-        ITracker& startRun();
-        void endRun();
-
-        void startCycle();
-        void completeCycle();
-
-        bool completedCycle() const;
-        ITracker& currentTracker();
-        void setCurrentTracker( ITracker* tracker );
-    };
-
-    class TrackerBase : public ITracker {
-    protected:
-        enum CycleState {
-            NotStarted,
-            Executing,
-            ExecutingChildren,
-            NeedsAnotherRun,
-            CompletedSuccessfully,
-            Failed
-        };
-
-        using Children = std::vector<ITrackerPtr>;
-        NameAndLocation m_nameAndLocation;
-        TrackerContext& m_ctx;
-        ITracker* m_parent;
-        Children m_children;
-        CycleState m_runState = NotStarted;
-
-    public:
-        TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
-
-        NameAndLocation const& nameAndLocation() const override;
-        bool isComplete() const override;
-        bool isSuccessfullyCompleted() const override;
-        bool isOpen() const override;
-        bool hasChildren() const override;
-
-        void addChild( ITrackerPtr const& child ) override;
-
-        ITrackerPtr findChild( NameAndLocation const& nameAndLocation ) override;
-        ITracker& parent() override;
-
-        void openChild() override;
-
-        bool isSectionTracker() const override;
-        bool isGeneratorTracker() const override;
-
-        void open();
-
-        void close() override;
-        void fail() override;
-        void markAsNeedingAnotherRun() override;
-
-    private:
-        void moveToParent();
-        void moveToThis();
-    };
-
-    class SectionTracker : public TrackerBase {
-        std::vector<std::string> m_filters;
-    public:
-        SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent );
-
-        bool isSectionTracker() const override;
-
-        bool isComplete() const override;
-
-        static SectionTracker& acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation );
-
-        void tryOpen();
-
-        void addInitialFilters( std::vector<std::string> const& filters );
-        void addNextFilters( std::vector<std::string> const& filters );
-    };
-
-} // namespace TestCaseTracking
-
-using TestCaseTracking::ITracker;
-using TestCaseTracking::TrackerContext;
-using TestCaseTracking::SectionTracker;
-
-} // namespace Catch
-
-// end catch_test_case_tracker.h
-
-// start catch_leak_detector.h
-
-namespace Catch {
-
-    struct LeakDetector {
-        LeakDetector();
-        ~LeakDetector();
-    };
-
-}
-// end catch_leak_detector.h
-// Cpp files will be included in the single-header file here
-// start catch_approx.cpp
-
-#include <cmath>
-#include <limits>
-
-namespace {
-
-// Performs equivalent check of std::fabs(lhs - rhs) <= margin
-// But without the subtraction to allow for INFINITY in comparison
-bool marginComparison(double lhs, double rhs, double margin) {
-    return (lhs + margin >= rhs) && (rhs + margin >= lhs);
-}
-
-}
-
-namespace Catch {
-namespace Detail {
-
-    Approx::Approx ( double value )
-    :   m_epsilon( std::numeric_limits<float>::epsilon()*100 ),
-        m_margin( 0.0 ),
-        m_scale( 0.0 ),
-        m_value( value )
-    {}
-
-    Approx Approx::custom() {
-        return Approx( 0 );
-    }
-
-    Approx Approx::operator-() const {
-        auto temp(*this);
-        temp.m_value = -temp.m_value;
-        return temp;
-    }
-
-    std::string Approx::toString() const {
-        ReusableStringStream rss;
-        rss << "Approx( " << ::Catch::Detail::stringify( m_value ) << " )";
-        return rss.str();
-    }
-
-    bool Approx::equalityComparisonImpl(const double other) const {
-        // First try with fixed margin, then compute margin based on epsilon, scale and Approx's value
-        // Thanks to Richard Harris for his help refining the scaled margin value
-        return marginComparison(m_value, other, m_margin) || marginComparison(m_value, other, m_epsilon * (m_scale + std::fabs(m_value)));
-    }
-
-    void Approx::setMargin(double newMargin) {
-        CATCH_ENFORCE(newMargin >= 0,
-            "Invalid Approx::margin: " << newMargin << '.'
-            << " Approx::Margin has to be non-negative.");
-        m_margin = newMargin;
-    }
-
-    void Approx::setEpsilon(double newEpsilon) {
-        CATCH_ENFORCE(newEpsilon >= 0 && newEpsilon <= 1.0,
-            "Invalid Approx::epsilon: " << newEpsilon << '.'
-            << " Approx::epsilon has to be in [0, 1]");
-        m_epsilon = newEpsilon;
-    }
-
-} // end namespace Detail
-
-namespace literals {
-    Detail::Approx operator "" _a(long double val) {
-        return Detail::Approx(val);
-    }
-    Detail::Approx operator "" _a(unsigned long long val) {
-        return Detail::Approx(val);
-    }
-} // end namespace literals
-
-std::string StringMaker<Catch::Detail::Approx>::convert(Catch::Detail::Approx const& value) {
-    return value.toString();
-}
-
-} // end namespace Catch
-// end catch_approx.cpp
-// start catch_assertionhandler.cpp
-
-// start catch_debugger.h
-
-namespace Catch {
-    bool isDebuggerActive();
-}
-
-#ifdef CATCH_PLATFORM_MAC
-
-    #define CATCH_TRAP() __asm__("int $3\n" : : ) /* NOLINT */
-
-#elif defined(CATCH_PLATFORM_LINUX)
-    // If we can use inline assembler, do it because this allows us to break
-    // directly at the location of the failing check instead of breaking inside
-    // raise() called from it, i.e. one stack frame below.
-    #if defined(__GNUC__) && (defined(__i386) || defined(__x86_64))
-        #define CATCH_TRAP() asm volatile ("int $3") /* NOLINT */
-    #else // Fall back to the generic way.
-        #include <signal.h>
-
-        #define CATCH_TRAP() raise(SIGTRAP)
-    #endif
-#elif defined(_MSC_VER)
-    #define CATCH_TRAP() __debugbreak()
-#elif defined(__MINGW32__)
-    extern "C" __declspec(dllimport) void __stdcall DebugBreak();
-    #define CATCH_TRAP() DebugBreak()
-#endif
-
-#ifdef CATCH_TRAP
-    #define CATCH_BREAK_INTO_DEBUGGER() []{ if( Catch::isDebuggerActive() ) { CATCH_TRAP(); } }()
-#else
-    #define CATCH_BREAK_INTO_DEBUGGER() []{}()
-#endif
-
-// end catch_debugger.h
-// start catch_run_context.h
-
-// start catch_fatal_condition.h
-
-// start catch_windows_h_proxy.h
-
-
-#if defined(CATCH_PLATFORM_WINDOWS)
-
-#if !defined(NOMINMAX) && !defined(CATCH_CONFIG_NO_NOMINMAX)
-#  define CATCH_DEFINED_NOMINMAX
-#  define NOMINMAX
-#endif
-#if !defined(WIN32_LEAN_AND_MEAN) && !defined(CATCH_CONFIG_NO_WIN32_LEAN_AND_MEAN)
-#  define CATCH_DEFINED_WIN32_LEAN_AND_MEAN
-#  define WIN32_LEAN_AND_MEAN
-#endif
-
-#ifdef __AFXDLL
-#include <AfxWin.h>
-#else
-#include <windows.h>
-#endif
-
-#ifdef CATCH_DEFINED_NOMINMAX
-#  undef NOMINMAX
-#endif
-#ifdef CATCH_DEFINED_WIN32_LEAN_AND_MEAN
-#  undef WIN32_LEAN_AND_MEAN
-#endif
-
-#endif // defined(CATCH_PLATFORM_WINDOWS)
-
-// end catch_windows_h_proxy.h
-#if defined( CATCH_CONFIG_WINDOWS_SEH )
-
-namespace Catch {
-
-    struct FatalConditionHandler {
-
-        static LONG CALLBACK handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo);
-        FatalConditionHandler();
-        static void reset();
-        ~FatalConditionHandler();
-
-    private:
-        static bool isSet;
-        static ULONG guaranteeSize;
-        static PVOID exceptionHandlerHandle;
-    };
-
-} // namespace Catch
-
-#elif defined ( CATCH_CONFIG_POSIX_SIGNALS )
-
-#include <signal.h>
-
-namespace Catch {
-
-    struct FatalConditionHandler {
-
-        static bool isSet;
-        static struct sigaction oldSigActions[];
-        static stack_t oldSigStack;
-        static char altStackMem[];
-
-        static void handleSignal( int sig );
-
-        FatalConditionHandler();
-        ~FatalConditionHandler();
-        static void reset();
-    };
-
-} // namespace Catch
-
-#else
-
-namespace Catch {
-    struct FatalConditionHandler {
-        void reset();
-    };
-}
-
-#endif
-
-// end catch_fatal_condition.h
-#include <string>
-
-namespace Catch {
-
-    struct IMutableContext;
-
-    ///////////////////////////////////////////////////////////////////////////
-
-    class RunContext : public IResultCapture, public IRunner {
-
-    public:
-        RunContext( RunContext const& ) = delete;
-        RunContext& operator =( RunContext const& ) = delete;
-
-        explicit RunContext( IConfigPtr const& _config, IStreamingReporterPtr&& reporter );
-
-        ~RunContext() override;
-
-        void testGroupStarting( std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount );
-        void testGroupEnded( std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount );
-
-        Totals runTest(TestCase const& testCase);
-
-        IConfigPtr config() const;
-        IStreamingReporter& reporter() const;
-
-    public: // IResultCapture
-
-        // Assertion handlers
-        void handleExpr
-                (   AssertionInfo const& info,
-                    ITransientExpression const& expr,
-                    AssertionReaction& reaction ) override;
-        void handleMessage
-                (   AssertionInfo const& info,
-                    ResultWas::OfType resultType,
-                    StringRef const& message,
-                    AssertionReaction& reaction ) override;
-        void handleUnexpectedExceptionNotThrown
-                (   AssertionInfo const& info,
-                    AssertionReaction& reaction ) override;
-        void handleUnexpectedInflightException
-                (   AssertionInfo const& info,
-                    std::string const& message,
-                    AssertionReaction& reaction ) override;
-        void handleIncomplete
-                (   AssertionInfo const& info ) override;
-        void handleNonExpr
-                (   AssertionInfo const &info,
-                    ResultWas::OfType resultType,
-                    AssertionReaction &reaction ) override;
-
-        bool sectionStarted( SectionInfo const& sectionInfo, Counts& assertions ) override;
-
-        void sectionEnded( SectionEndInfo const& endInfo ) override;
-        void sectionEndedEarly( SectionEndInfo const& endInfo ) override;
-
-        auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& override;
-
-        void benchmarkStarting( BenchmarkInfo const& info ) override;
-        void benchmarkEnded( BenchmarkStats const& stats ) override;
-
-        void pushScopedMessage( MessageInfo const& message ) override;
-        void popScopedMessage( MessageInfo const& message ) override;
-
-        void emplaceUnscopedMessage( MessageBuilder const& builder ) override;
-
-        std::string getCurrentTestName() const override;
-
-        const AssertionResult* getLastResult() const override;
-
-        void exceptionEarlyReported() override;
-
-        void handleFatalErrorCondition( StringRef message ) override;
-
-        bool lastAssertionPassed() override;
-
-        void assertionPassed() override;
-
-    public:
-        // !TBD We need to do this another way!
-        bool aborting() const final;
-
-    private:
-
-        void runCurrentTest( std::string& redirectedCout, std::string& redirectedCerr );
-        void invokeActiveTestCase();
-
-        void resetAssertionInfo();
-        bool testForMissingAssertions( Counts& assertions );
-
-        void assertionEnded( AssertionResult const& result );
-        void reportExpr
-                (   AssertionInfo const &info,
-                    ResultWas::OfType resultType,
-                    ITransientExpression const *expr,
-                    bool negated );
-
-        void populateReaction( AssertionReaction& reaction );
-
-    private:
-
-        void handleUnfinishedSections();
-
-        TestRunInfo m_runInfo;
-        IMutableContext& m_context;
-        TestCase const* m_activeTestCase = nullptr;
-        ITracker* m_testCaseTracker = nullptr;
-        Option<AssertionResult> m_lastResult;
-
-        IConfigPtr m_config;
-        Totals m_totals;
-        IStreamingReporterPtr m_reporter;
-        std::vector<MessageInfo> m_messages;
-        std::vector<ScopedMessage> m_messageScopes; /* Keeps owners of so-called unscoped messages. */
-        AssertionInfo m_lastAssertionInfo;
-        std::vector<SectionEndInfo> m_unfinishedSections;
-        std::vector<ITracker*> m_activeSections;
-        TrackerContext m_trackerContext;
-        bool m_lastAssertionPassed = false;
-        bool m_shouldReportUnexpected = true;
-        bool m_includeSuccessfulResults;
-    };
-
-} // end namespace Catch
-
-// end catch_run_context.h
-namespace Catch {
-
-    namespace {
-        auto operator <<( std::ostream& os, ITransientExpression const& expr ) -> std::ostream& {
-            expr.streamReconstructedExpression( os );
-            return os;
-        }
-    }
-
-    LazyExpression::LazyExpression( bool isNegated )
-    :   m_isNegated( isNegated )
-    {}
-
-    LazyExpression::LazyExpression( LazyExpression const& other ) : m_isNegated( other.m_isNegated ) {}
-
-    LazyExpression::operator bool() const {
-        return m_transientExpression != nullptr;
-    }
-
-    auto operator << ( std::ostream& os, LazyExpression const& lazyExpr ) -> std::ostream& {
-        if( lazyExpr.m_isNegated )
-            os << "!";
-
-        if( lazyExpr ) {
-            if( lazyExpr.m_isNegated && lazyExpr.m_transientExpression->isBinaryExpression() )
-                os << "(" << *lazyExpr.m_transientExpression << ")";
-            else
-                os << *lazyExpr.m_transientExpression;
-        }
-        else {
-            os << "{** error - unchecked empty expression requested **}";
-        }
-        return os;
-    }
-
-    AssertionHandler::AssertionHandler
-        (   StringRef const& macroName,
-            SourceLineInfo const& lineInfo,
-            StringRef capturedExpression,
-            ResultDisposition::Flags resultDisposition )
-    :   m_assertionInfo{ macroName, lineInfo, capturedExpression, resultDisposition },
-        m_resultCapture( getResultCapture() )
-    {}
-
-    void AssertionHandler::handleExpr( ITransientExpression const& expr ) {
-        m_resultCapture.handleExpr( m_assertionInfo, expr, m_reaction );
-    }
-    void AssertionHandler::handleMessage(ResultWas::OfType resultType, StringRef const& message) {
-        m_resultCapture.handleMessage( m_assertionInfo, resultType, message, m_reaction );
-    }
-
-    auto AssertionHandler::allowThrows() const -> bool {
-        return getCurrentContext().getConfig()->allowThrows();
-    }
-
-    void AssertionHandler::complete() {
-        setCompleted();
-        if( m_reaction.shouldDebugBreak ) {
-
-            // If you find your debugger stopping you here then go one level up on the
-            // call-stack for the code that caused it (typically a failed assertion)
-
-            // (To go back to the test and change execution, jump over the throw, next)
-            CATCH_BREAK_INTO_DEBUGGER();
-        }
-        if (m_reaction.shouldThrow) {
-#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
-            throw Catch::TestFailureException();
-#else
-            CATCH_ERROR( "Test failure requires aborting test!" );
-#endif
-        }
-    }
-    void AssertionHandler::setCompleted() {
-        m_completed = true;
-    }
-
-    void AssertionHandler::handleUnexpectedInflightException() {
-        m_resultCapture.handleUnexpectedInflightException( m_assertionInfo, Catch::translateActiveException(), m_reaction );
-    }
-
-    void AssertionHandler::handleExceptionThrownAsExpected() {
-        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
-    }
-    void AssertionHandler::handleExceptionNotThrownAsExpected() {
-        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
-    }
-
-    void AssertionHandler::handleUnexpectedExceptionNotThrown() {
-        m_resultCapture.handleUnexpectedExceptionNotThrown( m_assertionInfo, m_reaction );
-    }
-
-    void AssertionHandler::handleThrowingCallSkipped() {
-        m_resultCapture.handleNonExpr(m_assertionInfo, ResultWas::Ok, m_reaction);
-    }
-
-    // This is the overload that takes a string and infers the Equals matcher from it
-    // The more general overload, that takes any string matcher, is in catch_capture_matchers.cpp
-    void handleExceptionMatchExpr( AssertionHandler& handler, std::string const& str, StringRef const& matcherString  ) {
-        handleExceptionMatchExpr( handler, Matchers::Equals( str ), matcherString );
-    }
-
-} // namespace Catch
-// end catch_assertionhandler.cpp
-// start catch_assertionresult.cpp
-
-namespace Catch {
-    AssertionResultData::AssertionResultData(ResultWas::OfType _resultType, LazyExpression const & _lazyExpression):
-        lazyExpression(_lazyExpression),
-        resultType(_resultType) {}
-
-    std::string AssertionResultData::reconstructExpression() const {
-
-        if( reconstructedExpression.empty() ) {
-            if( lazyExpression ) {
-                ReusableStringStream rss;
-                rss << lazyExpression;
-                reconstructedExpression = rss.str();
-            }
-        }
-        return reconstructedExpression;
-    }
-
-    AssertionResult::AssertionResult( AssertionInfo const& info, AssertionResultData const& data )
-    :   m_info( info ),
-        m_resultData( data )
-    {}
-
-    // Result was a success
-    bool AssertionResult::succeeded() const {
-        return Catch::isOk( m_resultData.resultType );
-    }
-
-    // Result was a success, or failure is suppressed
-    bool AssertionResult::isOk() const {
-        return Catch::isOk( m_resultData.resultType ) || shouldSuppressFailure( m_info.resultDisposition );
-    }
-
-    ResultWas::OfType AssertionResult::getResultType() const {
-        return m_resultData.resultType;
-    }
-
-    bool AssertionResult::hasExpression() const {
-        return m_info.capturedExpression[0] != 0;
-    }
-
-    bool AssertionResult::hasMessage() const {
-        return !m_resultData.message.empty();
-    }
-
-    std::string AssertionResult::getExpression() const {
-        if( isFalseTest( m_info.resultDisposition ) )
-            return "!(" + m_info.capturedExpression + ")";
-        else
-            return m_info.capturedExpression;
-    }
-
-    std::string AssertionResult::getExpressionInMacro() const {
-        std::string expr;
-        if( m_info.macroName[0] == 0 )
-            expr = m_info.capturedExpression;
-        else {
-            expr.reserve( m_info.macroName.size() + m_info.capturedExpression.size() + 4 );
-            expr += m_info.macroName;
-            expr += "( ";
-            expr += m_info.capturedExpression;
-            expr += " )";
-        }
-        return expr;
-    }
-
-    bool AssertionResult::hasExpandedExpression() const {
-        return hasExpression() && getExpandedExpression() != getExpression();
-    }
-
-    std::string AssertionResult::getExpandedExpression() const {
-        std::string expr = m_resultData.reconstructExpression();
-        return expr.empty()
-                ? getExpression()
-                : expr;
-    }
-
-    std::string AssertionResult::getMessage() const {
-        return m_resultData.message;
-    }
-    SourceLineInfo AssertionResult::getSourceInfo() const {
-        return m_info.lineInfo;
-    }
-
-    StringRef AssertionResult::getTestMacroName() const {
-        return m_info.macroName;
-    }
-
-} // end namespace Catch
-// end catch_assertionresult.cpp
-// start catch_benchmark.cpp
-
-namespace Catch {
-
-    auto BenchmarkLooper::getResolution() -> uint64_t {
-        return getEstimatedClockResolution() * getCurrentContext().getConfig()->benchmarkResolutionMultiple();
-    }
-
-    void BenchmarkLooper::reportStart() {
-        getResultCapture().benchmarkStarting( { m_name } );
-    }
-    auto BenchmarkLooper::needsMoreIterations() -> bool {
-        auto elapsed = m_timer.getElapsedNanoseconds();
-
-        // Exponentially increasing iterations until we're confident in our timer resolution
-        if( elapsed < m_resolution ) {
-            m_iterationsToRun *= 10;
-            return true;
-        }
-
-        getResultCapture().benchmarkEnded( { { m_name }, m_count, elapsed } );
-        return false;
-    }
-
-} // end namespace Catch
-// end catch_benchmark.cpp
-// start catch_capture_matchers.cpp
-
-namespace Catch {
-
-    using StringMatcher = Matchers::Impl::MatcherBase<std::string>;
-
-    // This is the general overload that takes a any string matcher
-    // There is another overload, in catch_assertionhandler.h/.cpp, that only takes a string and infers
-    // the Equals matcher (so the header does not mention matchers)
-    void handleExceptionMatchExpr( AssertionHandler& handler, StringMatcher const& matcher, StringRef const& matcherString  ) {
-        std::string exceptionMessage = Catch::translateActiveException();
-        MatchExpr<std::string, StringMatcher const&> expr( exceptionMessage, matcher, matcherString );
-        handler.handleExpr( expr );
-    }
-
-} // namespace Catch
-// end catch_capture_matchers.cpp
-// start catch_commandline.cpp
-
-// start catch_commandline.h
-
-// start catch_clara.h
-
-// Use Catch's value for console width (store Clara's off to the side, if present)
-#ifdef CLARA_CONFIG_CONSOLE_WIDTH
-#define CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
-#undef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
-#endif
-#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CONFIG_CONSOLE_WIDTH-1
-
-#ifdef __clang__
-#pragma clang diagnostic push
-#pragma clang diagnostic ignored "-Wweak-vtables"
-#pragma clang diagnostic ignored "-Wexit-time-destructors"
-#pragma clang diagnostic ignored "-Wshadow"
-#endif
-
-// start clara.hpp
-// Copyright 2017 Two Blue Cubes Ltd. All rights reserved.
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// See https://github.com/philsquared/Clara for more details
-
-// Clara v1.1.5
-
-
-#ifndef CATCH_CLARA_CONFIG_CONSOLE_WIDTH
-#define CATCH_CLARA_CONFIG_CONSOLE_WIDTH 80
-#endif
-
-#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
-#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_CLARA_CONFIG_CONSOLE_WIDTH
-#endif
-
-#ifndef CLARA_CONFIG_OPTIONAL_TYPE
-#ifdef __has_include
-#if __has_include(<optional>) && __cplusplus >= 201703L
-#include <optional>
-#define CLARA_CONFIG_OPTIONAL_TYPE std::optional
-#endif
-#endif
-#endif
-
-// ----------- #included from clara_textflow.hpp -----------
-
-// TextFlowCpp
-//
-// A single-header library for wrapping and laying out basic text, by Phil Nash
-//
-// Distributed under the Boost Software License, Version 1.0. (See accompanying
-// file LICENSE.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
-//
-// This project is hosted at https://github.com/philsquared/textflowcpp
-
-
-#include <cassert>
-#include <ostream>
-#include <sstream>
-#include <vector>
-
-#ifndef CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH
-#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH 80
-#endif
-
-namespace Catch {
-namespace clara {
-namespace TextFlow {
-
-inline auto isWhitespace(char c) -> bool {
-	static std::string chars = " \t\n\r";
-	return chars.find(c) != std::string::npos;
-}
-inline auto isBreakableBefore(char c) -> bool {
-	static std::string chars = "[({<|";
-	return chars.find(c) != std::string::npos;
-}
-inline auto isBreakableAfter(char c) -> bool {
-	static std::string chars = "])}>.,:;*+-=&/\\";
-	return chars.find(c) != std::string::npos;
-}
-
-class Columns;
-
-class Column {
-	std::vector<std::string> m_strings;
-	size_t m_width = CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH;
-	size_t m_indent = 0;
-	size_t m_initialIndent = std::string::npos;
-
-public:
-	class iterator {
-		friend Column;
-
-		Column const& m_column;
-		size_t m_stringIndex = 0;
-		size_t m_pos = 0;
-
-		size_t m_len = 0;
-		size_t m_end = 0;
-		bool m_suffix = false;
-
-		iterator(Column const& column, size_t stringIndex)
-			: m_column(column),
-			m_stringIndex(stringIndex) {}
-
-		auto line() const -> std::string const& { return m_column.m_strings[m_stringIndex]; }
-
-		auto isBoundary(size_t at) const -> bool {
-			assert(at > 0);
-			assert(at <= line().size());
-
-			return at == line().size() ||
-				(isWhitespace(line()[at]) && !isWhitespace(line()[at - 1])) ||
-				isBreakableBefore(line()[at]) ||
-				isBreakableAfter(line()[at - 1]);
-		}
-
-		void calcLength() {
-			assert(m_stringIndex < m_column.m_strings.size());
-
-			m_suffix = false;
-			auto width = m_column.m_width - indent();
-			m_end = m_pos;
-			if (line()[m_pos] == '\n') {
-				++m_end;
-			}
-			while (m_end < line().size() && line()[m_end] != '\n')
-				++m_end;
-
-			if (m_end < m_pos + width) {
-				m_len = m_end - m_pos;
-			} else {
-				size_t len = width;
-				while (len > 0 && !isBoundary(m_pos + len))
-					--len;
-				while (len > 0 && isWhitespace(line()[m_pos + len - 1]))
-					--len;
-
-				if (len > 0) {
-					m_len = len;
-				} else {
-					m_suffix = true;
-					m_len = width - 1;
-				}
-			}
-		}
-
-		auto indent() const -> size_t {
-			auto initial = m_pos == 0 && m_stringIndex == 0 ? m_column.m_initialIndent : std::string::npos;
-			return initial == std::string::npos ? m_column.m_indent : initial;
-		}
-
-		auto addIndentAndSuffix(std::string const &plain) const -> std::string {
-			return std::string(indent(), ' ') + (m_suffix ? plain + "-" : plain);
-		}
-
-	public:
-		using difference_type = std::ptrdiff_t;
-		using value_type = std::string;
-		using pointer = value_type * ;
-		using reference = value_type & ;
-		using iterator_category = std::forward_iterator_tag;
-
-		explicit iterator(Column const& column) : m_column(column) {
-			assert(m_column.m_width > m_column.m_indent);
-			assert(m_column.m_initialIndent == std::string::npos || m_column.m_width > m_column.m_initialIndent);
-			calcLength();
-			if (m_len == 0)
-				m_stringIndex++; // Empty string
-		}
-
-		auto operator *() const -> std::string {
-			assert(m_stringIndex < m_column.m_strings.size());
-			assert(m_pos <= m_end);
-			return addIndentAndSuffix(line().substr(m_pos, m_len));
-		}
-
-		auto operator ++() -> iterator& {
-			m_pos += m_len;
-			if (m_pos < line().size() && line()[m_pos] == '\n')
-				m_pos += 1;
-			else
-				while (m_pos < line().size() && isWhitespace(line()[m_pos]))
-					++m_pos;
-
-			if (m_pos == line().size()) {
-				m_pos = 0;
-				++m_stringIndex;
-			}
-			if (m_stringIndex < m_column.m_strings.size())
-				calcLength();
-			return *this;
-		}
-		auto operator ++(int) -> iterator {
-			iterator prev(*this);
-			operator++();
-			return prev;
-		}
-
-		auto operator ==(iterator const& other) const -> bool {
-			return
-				m_pos == other.m_pos &&
-				m_stringIndex == other.m_stringIndex &&
-				&m_column == &other.m_column;
-		}
-		auto operator !=(iterator const& other) const -> bool {
-			return !operator==(other);
-		}
-	};
-	using const_iterator = iterator;
-
-	explicit Column(std::string const& text) { m_strings.push_back(text); }
-
-	auto width(size_t newWidth) -> Column& {
-		assert(newWidth > 0);
-		m_width = newWidth;
-		return *this;
-	}
-	auto indent(size_t newIndent) -> Column& {
-		m_indent = newIndent;
-		return *this;
-	}
-	auto initialIndent(size_t newIndent) -> Column& {
-		m_initialIndent = newIndent;
-		return *this;
-	}
-
-	auto width() const -> size_t { return m_width; }
-	auto begin() const -> iterator { return iterator(*this); }
-	auto end() const -> iterator { return { *this, m_strings.size() }; }
-
-	inline friend std::ostream& operator << (std::ostream& os, Column const& col) {
-		bool first = true;
-		for (auto line : col) {
-			if (first)
-				first = false;
-			else
-				os << "\n";
-			os << line;
-		}
-		return os;
-	}
-
-	auto operator + (Column const& other)->Columns;
-
-	auto toString() const -> std::string {
-		std::ostringstream oss;
-		oss << *this;
-		return oss.str();
-	}
-};
-
-class Spacer : public Column {
-
-public:
-	explicit Spacer(size_t spaceWidth) : Column("") {
-		width(spaceWidth);
-	}
-};
-
-class Columns {
-	std::vector<Column> m_columns;
-
-public:
-
-	class iterator {
-		friend Columns;
-		struct EndTag {};
-
-		std::vector<Column> const& m_columns;
-		std::vector<Column::iterator> m_iterators;
-		size_t m_activeIterators;
-
-		iterator(Columns const& columns, EndTag)
-			: m_columns(columns.m_columns),
-			m_activeIterators(0) {
-			m_iterators.reserve(m_columns.size());
-
-			for (auto const& col : m_columns)
-				m_iterators.push_back(col.end());
-		}
-
-	public:
-		using difference_type = std::ptrdiff_t;
-		using value_type = std::string;
-		using pointer = value_type * ;
-		using reference = value_type & ;
-		using iterator_category = std::forward_iterator_tag;
-
-		explicit iterator(Columns const& columns)
-			: m_columns(columns.m_columns),
-			m_activeIterators(m_columns.size()) {
-			m_iterators.reserve(m_columns.size());
-
-			for (auto const& col : m_columns)
-				m_iterators.push_back(col.begin());
-		}
-
-		auto operator ==(iterator const& other) const -> bool {
-			return m_iterators == other.m_iterators;
-		}
-		auto operator !=(iterator const& other) const -> bool {
-			return m_iterators != other.m_iterators;
-		}
-		auto operator *() const -> std::string {
-			std::string row, padding;
-
-			for (size_t i = 0; i < m_columns.size(); ++i) {
-				auto width = m_columns[i].width();
-				if (m_iterators[i] != m_columns[i].end()) {
-					std::string col = *m_iterators[i];
-					row += padding + col;
-					if (col.size() < width)
-						padding = std::string(width - col.size(), ' ');
-					else
-						padding = "";
-				} else {
-					padding += std::string(width, ' ');
-				}
-			}
-			return row;
-		}
-		auto operator ++() -> iterator& {
-			for (size_t i = 0; i < m_columns.size(); ++i) {
-				if (m_iterators[i] != m_columns[i].end())
-					++m_iterators[i];
-			}
-			return *this;
-		}
-		auto operator ++(int) -> iterator {
-			iterator prev(*this);
-			operator++();
-			return prev;
-		}
-	};
-	using const_iterator = iterator;
-
-	auto begin() const -> iterator { return iterator(*this); }
-	auto end() const -> iterator { return { *this, iterator::EndTag() }; }
-
-	auto operator += (Column const& col) -> Columns& {
-		m_columns.push_back(col);
-		return *this;
-	}
-	auto operator + (Column const& col) -> Columns {
-		Columns combined = *this;
-		combined += col;
-		return combined;
-	}
-
-	inline friend std::ostream& operator << (std::ostream& os, Columns const& cols) {
-
-		bool first = true;
-		for (auto line : cols) {
-			if (first)
-				first = false;
-			else
-				os << "\n";
-			os << line;
-		}
-		return os;
-	}
-
-	auto toString() const -> std::string {
-		std::ostringstream oss;
-		oss << *this;
-		return oss.str();
-	}
-};
-
-inline auto Column::operator + (Column const& other) -> Columns {
-	Columns cols;
-	cols += *this;
-	cols += other;
-	return cols;
-}
-}
-
-}
-}
-
-// ----------- end of #include from clara_textflow.hpp -----------
-// ........... back in clara.hpp
-
-#include <cctype>
-#include <string>
-#include <memory>
-#include <set>
-#include <algorithm>
-
-#if !defined(CATCH_PLATFORM_WINDOWS) && ( defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) )
-#define CATCH_PLATFORM_WINDOWS
-#endif
-
-namespace Catch { namespace clara {
-namespace detail {
-
-    // Traits for extracting arg and return type of lambdas (for single argument lambdas)
-    template<typename L>
-    struct UnaryLambdaTraits : UnaryLambdaTraits<decltype( &L::operator() )> {};
-
-    template<typename ClassT, typename ReturnT, typename... Args>
-    struct UnaryLambdaTraits<ReturnT( ClassT::* )( Args... ) const> {
-        static const bool isValid = false;
-    };
-
-    template<typename ClassT, typename ReturnT, typename ArgT>
-    struct UnaryLambdaTraits<ReturnT( ClassT::* )( ArgT ) const> {
-        static const bool isValid = true;
-        using ArgType = typename std::remove_const<typename std::remove_reference<ArgT>::type>::type;
-        using ReturnType = ReturnT;
-    };
-
-    class TokenStream;
-
-    // Transport for raw args (copied from main args, or supplied via init list for testing)
-    class Args {
-        friend TokenStream;
-        std::string m_exeName;
-        std::vector<std::string> m_args;
-
-    public:
-        Args( int argc, char const* const* argv )
-            : m_exeName(argv[0]),
-              m_args(argv + 1, argv + argc) {}
-
-        Args( std::initializer_list<std::string> args )
-        :   m_exeName( *args.begin() ),
-            m_args( args.begin()+1, args.end() )
-        {}
-
-        auto exeName() const -> std::string {
-            return m_exeName;
-        }
-    };
-
-    // Wraps a token coming from a token stream. These may not directly correspond to strings as a single string
-    // may encode an option + its argument if the : or = form is used
-    enum class TokenType {
-        Option, Argument
-    };
-    struct Token {
-        TokenType type;
-        std::string token;
-    };
-
-    inline auto isOptPrefix( char c ) -> bool {
-        return c == '-'
-#ifdef CATCH_PLATFORM_WINDOWS
-            || c == '/'
-#endif
-        ;
-    }
-
-    // Abstracts iterators into args as a stream of tokens, with option arguments uniformly handled
-    class TokenStream {
-        using Iterator = std::vector<std::string>::const_iterator;
-        Iterator it;
-        Iterator itEnd;
-        std::vector<Token> m_tokenBuffer;
-
-        void loadBuffer() {
-            m_tokenBuffer.resize( 0 );
-
-            // Skip any empty strings
-            while( it != itEnd && it->empty() )
-                ++it;
-
-            if( it != itEnd ) {
-                auto const &next = *it;
-                if( isOptPrefix( next[0] ) ) {
-                    auto delimiterPos = next.find_first_of( " :=" );
-                    if( delimiterPos != std::string::npos ) {
-                        m_tokenBuffer.push_back( { TokenType::Option, next.substr( 0, delimiterPos ) } );
-                        m_tokenBuffer.push_back( { TokenType::Argument, next.substr( delimiterPos + 1 ) } );
-                    } else {
-                        if( next[1] != '-' && next.size() > 2 ) {
-                            std::string opt = "- ";
-                            for( size_t i = 1; i < next.size(); ++i ) {
-                                opt[1] = next[i];
-                                m_tokenBuffer.push_back( { TokenType::Option, opt } );
-                            }
-                        } else {
-                            m_tokenBuffer.push_back( { TokenType::Option, next } );
-                        }
-                    }
-                } else {
-                    m_tokenBuffer.push_back( { TokenType::Argument, next } );
-                }
-            }
-        }
-
-    public:
-        explicit TokenStream( Args const &args ) : TokenStream( args.m_args.begin(), args.m_args.end() ) {}
-
-        TokenStream( Iterator it, Iterator itEnd ) : it( it ), itEnd( itEnd ) {
-            loadBuffer();
-        }
-
-        explicit operator bool() const {
-            return !m_tokenBuffer.empty() || it != itEnd;
-        }
-
-        auto count() const -> size_t { return m_tokenBuffer.size() + (itEnd - it); }
-
-        auto operator*() const -> Token {
-            assert( !m_tokenBuffer.empty() );
-            return m_tokenBuffer.front();
-        }
-
-        auto operator->() const -> Token const * {
-            assert( !m_tokenBuffer.empty() );
-            return &m_tokenBuffer.front();
-        }
-
-        auto operator++() -> TokenStream & {
-            if( m_tokenBuffer.size() >= 2 ) {
-                m_tokenBuffer.erase( m_tokenBuffer.begin() );
-            } else {
-                if( it != itEnd )
-                    ++it;
-                loadBuffer();
-            }
-            return *this;
-        }
-    };
-
-    class ResultBase {
-    public:
-        enum Type {
-            Ok, LogicError, RuntimeError
-        };
-
-    protected:
-        ResultBase( Type type ) : m_type( type ) {}
-        virtual ~ResultBase() = default;
-
-        virtual void enforceOk() const = 0;
-
-        Type m_type;
-    };
-
-    template<typename T>
-    class ResultValueBase : public ResultBase {
-    public:
-        auto value() const -> T const & {
-            enforceOk();
-            return m_value;
-        }
-
-    protected:
-        ResultValueBase( Type type ) : ResultBase( type ) {}
-
-        ResultValueBase( ResultValueBase const &other ) : ResultBase( other ) {
-            if( m_type == ResultBase::Ok )
-                new( &m_value ) T( other.m_value );
-        }
-
-        ResultValueBase( Type, T const &value ) : ResultBase( Ok ) {
-            new( &m_value ) T( value );
-        }
-
-        auto operator=( ResultValueBase const &other ) -> ResultValueBase & {
-            if( m_type == ResultBase::Ok )
-                m_value.~T();
-            ResultBase::operator=(other);
-            if( m_type == ResultBase::Ok )
-                new( &m_value ) T( other.m_value );
-            return *this;
-        }
-
-        ~ResultValueBase() override {
-            if( m_type == Ok )
-                m_value.~T();
-        }
-
-        union {
-            T m_value;
-        };
-    };
-
-    template<>
-    class ResultValueBase<void> : public ResultBase {
-    protected:
-        using ResultBase::ResultBase;
-    };
-
-    template<typename T = void>
-    class BasicResult : public ResultValueBase<T> {
-    public:
-        template<typename U>
-        explicit BasicResult( BasicResult<U> const &other )
-        :   ResultValueBase<T>( other.type() ),
-            m_errorMessage( other.errorMessage() )
-        {
-            assert( type() != ResultBase::Ok );
-        }
-
-        template<typename U>
-        static auto ok( U const &value ) -> BasicResult { return { ResultBase::Ok, value }; }
-        static auto ok() -> BasicResult { return { ResultBase::Ok }; }
-        static auto logicError( std::string const &message ) -> BasicResult { return { ResultBase::LogicError, message }; }
-        static auto runtimeError( std::string const &message ) -> BasicResult { return { ResultBase::RuntimeError, message }; }
-
-        explicit operator bool() const { return m_type == ResultBase::Ok; }
-        auto type() const -> ResultBase::Type { return m_type; }
-        auto errorMessage() const -> std::string { return m_errorMessage; }
-
-    protected:
-        void enforceOk() const override {
-
-            // Errors shouldn't reach this point, but if they do
-            // the actual error message will be in m_errorMessage
-            assert( m_type != ResultBase::LogicError );
-            assert( m_type != ResultBase::RuntimeError );
-            if( m_type != ResultBase::Ok )
-                std::abort();
-        }
-
-        std::string m_errorMessage; // Only populated if resultType is an error
-
-        BasicResult( ResultBase::Type type, std::string const &message )
-        :   ResultValueBase<T>(type),
-            m_errorMessage(message)
-        {
-            assert( m_type != ResultBase::Ok );
-        }
-
-        using ResultValueBase<T>::ResultValueBase;
-        using ResultBase::m_type;
-    };
-
-    enum class ParseResultType {
-        Matched, NoMatch, ShortCircuitAll, ShortCircuitSame
-    };
-
-    class ParseState {
-    public:
-
-        ParseState( ParseResultType type, TokenStream const &remainingTokens )
-        : m_type(type),
-          m_remainingTokens( remainingTokens )
-        {}
-
-        auto type() const -> ParseResultType { return m_type; }
-        auto remainingTokens() const -> TokenStream { return m_remainingTokens; }
-
-    private:
-        ParseResultType m_type;
-        TokenStream m_remainingTokens;
-    };
-
-    using Result = BasicResult<void>;
-    using ParserResult = BasicResult<ParseResultType>;
-    using InternalParseResult = BasicResult<ParseState>;
-
-    struct HelpColumns {
-        std::string left;
-        std::string right;
-    };
-
-    template<typename T>
-    inline auto convertInto( std::string const &source, T& target ) -> ParserResult {
-        std::stringstream ss;
-        ss << source;
-        ss >> target;
-        if( ss.fail() )
-            return ParserResult::runtimeError( "Unable to convert '" + source + "' to destination type" );
-        else
-            return ParserResult::ok( ParseResultType::Matched );
-    }
-    inline auto convertInto( std::string const &source, std::string& target ) -> ParserResult {
-        target = source;
-        return ParserResult::ok( ParseResultType::Matched );
-    }
-    inline auto convertInto( std::string const &source, bool &target ) -> ParserResult {
-        std::string srcLC = source;
-        std::transform( srcLC.begin(), srcLC.end(), srcLC.begin(), []( char c ) { return static_cast<char>( std::tolower(c) ); } );
-        if (srcLC == "y" || srcLC == "1" || srcLC == "true" || srcLC == "yes" || srcLC == "on")
-            target = true;
-        else if (srcLC == "n" || srcLC == "0" || srcLC == "false" || srcLC == "no" || srcLC == "off")
-            target = false;
-        else
-            return ParserResult::runtimeError( "Expected a boolean value but did not recognise: '" + source + "'" );
-        return ParserResult::ok( ParseResultType::Matched );
-    }
-#ifdef CLARA_CONFIG_OPTIONAL_TYPE
-    template<typename T>
-    inline auto convertInto( std::string const &source, CLARA_CONFIG_OPTIONAL_TYPE<T>& target ) -> ParserResult {
-        T temp;
-        auto result = convertInto( source, temp );
-        if( result )
-            target = std::move(temp);
-        return result;
-    }
-#endif // CLARA_CONFIG_OPTIONAL_TYPE
-
-    struct NonCopyable {
-        NonCopyable() = default;
-        NonCopyable( NonCopyable const & ) = delete;
-        NonCopyable( NonCopyable && ) = delete;
-        NonCopyable &operator=( NonCopyable const & ) = delete;
-        NonCopyable &operator=( NonCopyable && ) = delete;
-    };
-
-    struct BoundRef : NonCopyable {
-        virtual ~BoundRef() = default;
-        virtual auto isContainer() const -> bool { return false; }
-        virtual auto isFlag() const -> bool { return false; }
-    };
-    struct BoundValueRefBase : BoundRef {
-        virtual auto setValue( std::string const &arg ) -> ParserResult = 0;
-    };
-    struct BoundFlagRefBase : BoundRef {
-        virtual auto setFlag( bool flag ) -> ParserResult = 0;
-        virtual auto isFlag() const -> bool { return true; }
-    };
-
-    template<typename T>
-    struct BoundValueRef : BoundValueRefBase {
-        T &m_ref;
-
-        explicit BoundValueRef( T &ref ) : m_ref( ref ) {}
-
-        auto setValue( std::string const &arg ) -> ParserResult override {
-            return convertInto( arg, m_ref );
-        }
-    };
-
-    template<typename T>
-    struct BoundValueRef<std::vector<T>> : BoundValueRefBase {
-        std::vector<T> &m_ref;
-
-        explicit BoundValueRef( std::vector<T> &ref ) : m_ref( ref ) {}
-
-        auto isContainer() const -> bool override { return true; }
-
-        auto setValue( std::string const &arg ) -> ParserResult override {
-            T temp;
-            auto result = convertInto( arg, temp );
-            if( result )
-                m_ref.push_back( temp );
-            return result;
-        }
-    };
-
-    struct BoundFlagRef : BoundFlagRefBase {
-        bool &m_ref;
-
-        explicit BoundFlagRef( bool &ref ) : m_ref( ref ) {}
-
-        auto setFlag( bool flag ) -> ParserResult override {
-            m_ref = flag;
-            return ParserResult::ok( ParseResultType::Matched );
-        }
-    };
-
-    template<typename ReturnType>
-    struct LambdaInvoker {
-        static_assert( std::is_same<ReturnType, ParserResult>::value, "Lambda must return void or clara::ParserResult" );
-
-        template<typename L, typename ArgType>
-        static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
-            return lambda( arg );
-        }
-    };
-
-    template<>
-    struct LambdaInvoker<void> {
-        template<typename L, typename ArgType>
-        static auto invoke( L const &lambda, ArgType const &arg ) -> ParserResult {
-            lambda( arg );
-            return ParserResult::ok( ParseResultType::Matched );
-        }
-    };
-
-    template<typename ArgType, typename L>
-    inline auto invokeLambda( L const &lambda, std::string const &arg ) -> ParserResult {
-        ArgType temp{};
-        auto result = convertInto( arg, temp );
-        return !result
-           ? result
-           : LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( lambda, temp );
-    }
-
-    template<typename L>
-    struct BoundLambda : BoundValueRefBase {
-        L m_lambda;
-
-        static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
-        explicit BoundLambda( L const &lambda ) : m_lambda( lambda ) {}
-
-        auto setValue( std::string const &arg ) -> ParserResult override {
-            return invokeLambda<typename UnaryLambdaTraits<L>::ArgType>( m_lambda, arg );
-        }
-    };
-
-    template<typename L>
-    struct BoundFlagLambda : BoundFlagRefBase {
-        L m_lambda;
-
-        static_assert( UnaryLambdaTraits<L>::isValid, "Supplied lambda must take exactly one argument" );
-        static_assert( std::is_same<typename UnaryLambdaTraits<L>::ArgType, bool>::value, "flags must be boolean" );
-
-        explicit BoundFlagLambda( L const &lambda ) : m_lambda( lambda ) {}
-
-        auto setFlag( bool flag ) -> ParserResult override {
-            return LambdaInvoker<typename UnaryLambdaTraits<L>::ReturnType>::invoke( m_lambda, flag );
-        }
-    };
-
-    enum class Optionality { Optional, Required };
-
-    struct Parser;
-
-    class ParserBase {
-    public:
-        virtual ~ParserBase() = default;
-        virtual auto validate() const -> Result { return Result::ok(); }
-        virtual auto parse( std::string const& exeName, TokenStream const &tokens) const -> InternalParseResult  = 0;
-        virtual auto cardinality() const -> size_t { return 1; }
-
-        auto parse( Args const &args ) const -> InternalParseResult {
-            return parse( args.exeName(), TokenStream( args ) );
-        }
-    };
-
-    template<typename DerivedT>
-    class ComposableParserImpl : public ParserBase {
-    public:
-        template<typename T>
-        auto operator|( T const &other ) const -> Parser;
-
-		template<typename T>
-        auto operator+( T const &other ) const -> Parser;
-    };
-
-    // Common code and state for Args and Opts
-    template<typename DerivedT>
-    class ParserRefImpl : public ComposableParserImpl<DerivedT> {
-    protected:
-        Optionality m_optionality = Optionality::Optional;
-        std::shared_ptr<BoundRef> m_ref;
-        std::string m_hint;
-        std::string m_description;
-
-        explicit ParserRefImpl( std::shared_ptr<BoundRef> const &ref ) : m_ref( ref ) {}
-
-    public:
-        template<typename T>
-        ParserRefImpl( T &ref, std::string const &hint )
-        :   m_ref( std::make_shared<BoundValueRef<T>>( ref ) ),
-            m_hint( hint )
-        {}
-
-        template<typename LambdaT>
-        ParserRefImpl( LambdaT const &ref, std::string const &hint )
-        :   m_ref( std::make_shared<BoundLambda<LambdaT>>( ref ) ),
-            m_hint(hint)
-        {}
-
-        auto operator()( std::string const &description ) -> DerivedT & {
-            m_description = description;
-            return static_cast<DerivedT &>( *this );
-        }
-
-        auto optional() -> DerivedT & {
-            m_optionality = Optionality::Optional;
-            return static_cast<DerivedT &>( *this );
-        };
-
-        auto required() -> DerivedT & {
-            m_optionality = Optionality::Required;
-            return static_cast<DerivedT &>( *this );
-        };
-
-        auto isOptional() const -> bool {
-            return m_optionality == Optionality::Optional;
-        }
-
-        auto cardinality() const -> size_t override {
-            if( m_ref->isContainer() )
-                return 0;
-            else
-                return 1;
-        }
-
-        auto hint() const -> std::string { return m_hint; }
-    };
-
-    class ExeName : public ComposableParserImpl<ExeName> {
-        std::shared_ptr<std::string> m_name;
-        std::shared_ptr<BoundValueRefBase> m_ref;
-
-        template<typename LambdaT>
-        static auto makeRef(LambdaT const &lambda) -> std::shared_ptr<BoundValueRefBase> {
-            return std::make_shared<BoundLambda<LambdaT>>( lambda) ;
-        }
-
-    public:
-        ExeName() : m_name( std::make_shared<std::string>( "<executable>" ) ) {}
-
-        explicit ExeName( std::string &ref ) : ExeName() {
-            m_ref = std::make_shared<BoundValueRef<std::string>>( ref );
-        }
-
-        template<typename LambdaT>
-        explicit ExeName( LambdaT const& lambda ) : ExeName() {
-            m_ref = std::make_shared<BoundLambda<LambdaT>>( lambda );
-        }
-
-        // The exe name is not parsed out of the normal tokens, but is handled specially
-        auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
-            return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
-        }
-
-        auto name() const -> std::string { return *m_name; }
-        auto set( std::string const& newName ) -> ParserResult {
-
-            auto lastSlash = newName.find_last_of( "\\/" );
-            auto filename = ( lastSlash == std::string::npos )
-                    ? newName
-                    : newName.substr( lastSlash+1 );
-
-            *m_name = filename;
-            if( m_ref )
-                return m_ref->setValue( filename );
-            else
-                return ParserResult::ok( ParseResultType::Matched );
-        }
-    };
-
-    class Arg : public ParserRefImpl<Arg> {
-    public:
-        using ParserRefImpl::ParserRefImpl;
-
-        auto parse( std::string const &, TokenStream const &tokens ) const -> InternalParseResult override {
-            auto validationResult = validate();
-            if( !validationResult )
-                return InternalParseResult( validationResult );
-
-            auto remainingTokens = tokens;
-            auto const &token = *remainingTokens;
-            if( token.type != TokenType::Argument )
-                return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
-
-            assert( !m_ref->isFlag() );
-            auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
-
-            auto result = valueRef->setValue( remainingTokens->token );
-            if( !result )
-                return InternalParseResult( result );
-            else
-                return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
-        }
-    };
-
-    inline auto normaliseOpt( std::string const &optName ) -> std::string {
-#ifdef CATCH_PLATFORM_WINDOWS
-        if( optName[0] == '/' )
-            return "-" + optName.substr( 1 );
-        else
-#endif
-            return optName;
-    }
-
-    class Opt : public ParserRefImpl<Opt> {
-    protected:
-        std::vector<std::string> m_optNames;
-
-    public:
-        template<typename LambdaT>
-        explicit Opt( LambdaT const &ref ) : ParserRefImpl( std::make_shared<BoundFlagLambda<LambdaT>>( ref ) ) {}
-
-        explicit Opt( bool &ref ) : ParserRefImpl( std::make_shared<BoundFlagRef>( ref ) ) {}
-
-        template<typename LambdaT>
-        Opt( LambdaT const &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
-
-        template<typename T>
-        Opt( T &ref, std::string const &hint ) : ParserRefImpl( ref, hint ) {}
-
-        auto operator[]( std::string const &optName ) -> Opt & {
-            m_optNames.push_back( optName );
-            return *this;
-        }
-
-        auto getHelpColumns() const -> std::vector<HelpColumns> {
-            std::ostringstream oss;
-            bool first = true;
-            for( auto const &opt : m_optNames ) {
-                if (first)
-                    first = false;
-                else
-                    oss << ", ";
-                oss << opt;
-            }
-            if( !m_hint.empty() )
-                oss << " <" << m_hint << ">";
-            return { { oss.str(), m_description } };
-        }
-
-        auto isMatch( std::string const &optToken ) const -> bool {
-            auto normalisedToken = normaliseOpt( optToken );
-            for( auto const &name : m_optNames ) {
-                if( normaliseOpt( name ) == normalisedToken )
-                    return true;
-            }
-            return false;
-        }
-
-        using ParserBase::parse;
-
-        auto parse( std::string const&, TokenStream const &tokens ) const -> InternalParseResult override {
-            auto validationResult = validate();
-            if( !validationResult )
-                return InternalParseResult( validationResult );
-
-            auto remainingTokens = tokens;
-            if( remainingTokens && remainingTokens->type == TokenType::Option ) {
-                auto const &token = *remainingTokens;
-                if( isMatch(token.token ) ) {
-                    if( m_ref->isFlag() ) {
-                        auto flagRef = static_cast<detail::BoundFlagRefBase*>( m_ref.get() );
-                        auto result = flagRef->setFlag( true );
-                        if( !result )
-                            return InternalParseResult( result );
-                        if( result.value() == ParseResultType::ShortCircuitAll )
-                            return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
-                    } else {
-                        auto valueRef = static_cast<detail::BoundValueRefBase*>( m_ref.get() );
-                        ++remainingTokens;
-                        if( !remainingTokens )
-                            return InternalParseResult::runtimeError( "Expected argument following " + token.token );
-                        auto const &argToken = *remainingTokens;
-                        if( argToken.type != TokenType::Argument )
-                            return InternalParseResult::runtimeError( "Expected argument following " + token.token );
-                        auto result = valueRef->setValue( argToken.token );
-                        if( !result )
-                            return InternalParseResult( result );
-                        if( result.value() == ParseResultType::ShortCircuitAll )
-                            return InternalParseResult::ok( ParseState( result.value(), remainingTokens ) );
-                    }
-                    return InternalParseResult::ok( ParseState( ParseResultType::Matched, ++remainingTokens ) );
-                }
-            }
-            return InternalParseResult::ok( ParseState( ParseResultType::NoMatch, remainingTokens ) );
-        }
-
-        auto validate() const -> Result override {
-            if( m_optNames.empty() )
-                return Result::logicError( "No options supplied to Opt" );
-            for( auto const &name : m_optNames ) {
-                if( name.empty() )
-                    return Result::logicError( "Option name cannot be empty" );
-#ifdef CATCH_PLATFORM_WINDOWS
-                if( name[0] != '-' && name[0] != '/' )
-                    return Result::logicError( "Option name must begin with '-' or '/'" );
-#else
-                if( name[0] != '-' )
-                    return Result::logicError( "Option name must begin with '-'" );
-#endif
-            }
-            return ParserRefImpl::validate();
-        }
-    };
-
-    struct Help : Opt {
-        Help( bool &showHelpFlag )
-        :   Opt([&]( bool flag ) {
-                showHelpFlag = flag;
-                return ParserResult::ok( ParseResultType::ShortCircuitAll );
-            })
-        {
-            static_cast<Opt &>( *this )
-                    ("display usage information")
-                    ["-?"]["-h"]["--help"]
-                    .optional();
-        }
-    };
-
-    struct Parser : ParserBase {
-
-        mutable ExeName m_exeName;
-        std::vector<Opt> m_options;
-        std::vector<Arg> m_args;
-
-        auto operator|=( ExeName const &exeName ) -> Parser & {
-            m_exeName = exeName;
-            return *this;
-        }
-
-        auto operator|=( Arg const &arg ) -> Parser & {
-            m_args.push_back(arg);
-            return *this;
-        }
-
-        auto operator|=( Opt const &opt ) -> Parser & {
-            m_options.push_back(opt);
-            return *this;
-        }
-
-        auto operator|=( Parser const &other ) -> Parser & {
-            m_options.insert(m_options.end(), other.m_options.begin(), other.m_options.end());
-            m_args.insert(m_args.end(), other.m_args.begin(), other.m_args.end());
-            return *this;
-        }
-
-        template<typename T>
-        auto operator|( T const &other ) const -> Parser {
-            return Parser( *this ) |= other;
-        }
-
-        // Forward deprecated interface with '+' instead of '|'
-        template<typename T>
-        auto operator+=( T const &other ) -> Parser & { return operator|=( other ); }
-        template<typename T>
-        auto operator+( T const &other ) const -> Parser { return operator|( other ); }
-
-        auto getHelpColumns() const -> std::vector<HelpColumns> {
-            std::vector<HelpColumns> cols;
-            for (auto const &o : m_options) {
-                auto childCols = o.getHelpColumns();
-                cols.insert( cols.end(), childCols.begin(), childCols.end() );
-            }
-            return cols;
-        }
-
-        void writeToStream( std::ostream &os ) const {
-            if (!m_exeName.name().empty()) {
-                os << "usage:\n" << "  " << m_exeName.name() << " ";
-                bool required = true, first = true;
-                for( auto const &arg : m_args ) {
-                    if (first)
-                        first = false;
-                    else
-                        os << " ";
-                    if( arg.isOptional() && required ) {
-                        os << "[";
-                        required = false;
-                    }
-                    os << "<" << arg.hint() << ">";
-                    if( arg.cardinality() == 0 )
-                        os << " ... ";
-                }
-                if( !required )
-                    os << "]";
-                if( !m_options.empty() )
-                    os << " options";
-                os << "\n\nwhere options are:" << std::endl;
-            }
-
-            auto rows = getHelpColumns();
-            size_t consoleWidth = CATCH_CLARA_CONFIG_CONSOLE_WIDTH;
-            size_t optWidth = 0;
-            for( auto const &cols : rows )
-                optWidth = (std::max)(optWidth, cols.left.size() + 2);
-
-            optWidth = (std::min)(optWidth, consoleWidth/2);
-
-            for( auto const &cols : rows ) {
-                auto row =
-                        TextFlow::Column( cols.left ).width( optWidth ).indent( 2 ) +
-                        TextFlow::Spacer(4) +
-                        TextFlow::Column( cols.right ).width( consoleWidth - 7 - optWidth );
-                os << row << std::endl;
-            }
-        }
-
-        friend auto operator<<( std::ostream &os, Parser const &parser ) -> std::ostream& {
-            parser.writeToStream( os );
-            return os;
-        }
-
-        auto validate() const -> Result override {
-            for( auto const &opt : m_options ) {
-                auto result = opt.validate();
-                if( !result )
-                    return result;
-            }
-            for( auto const &arg : m_args ) {
-                auto result = arg.validate();
-                if( !result )
-                    return result;
-            }
-            return Result::ok();
-        }
-
-        using ParserBase::parse;
-
-        auto parse( std::string const& exeName, TokenStream const &tokens ) const -> InternalParseResult override {
-
-            struct ParserInfo {
-                ParserBase const* parser = nullptr;
-                size_t count = 0;
-            };
-            const size_t totalParsers = m_options.size() + m_args.size();
-            assert( totalParsers < 512 );
-            // ParserInfo parseInfos[totalParsers]; // <-- this is what we really want to do
-            ParserInfo parseInfos[512];
-
-            {
-                size_t i = 0;
-                for (auto const &opt : m_options) parseInfos[i++].parser = &opt;
-                for (auto const &arg : m_args) parseInfos[i++].parser = &arg;
-            }
-
-            m_exeName.set( exeName );
-
-            auto result = InternalParseResult::ok( ParseState( ParseResultType::NoMatch, tokens ) );
-            while( result.value().remainingTokens() ) {
-                bool tokenParsed = false;
-
-                for( size_t i = 0; i < totalParsers; ++i ) {
-                    auto&  parseInfo = parseInfos[i];
-                    if( parseInfo.parser->cardinality() == 0 || parseInfo.count < parseInfo.parser->cardinality() ) {
-                        result = parseInfo.parser->parse(exeName, result.value().remainingTokens());
-                        if (!result)
-                            return result;
-                        if (result.value().type() != ParseResultType::NoMatch) {
-                            tokenParsed = true;
-                            ++parseInfo.count;
-                            break;
-                        }
-                    }
-                }
-
-                if( result.value().type() == ParseResultType::ShortCircuitAll )
-                    return result;
-                if( !tokenParsed )
-                    return InternalParseResult::runtimeError( "Unrecognised token: " + result.value().remainingTokens()->token );
-            }
-            // !TBD Check missing required options
-            return result;
-        }
-    };
-
-    template<typename DerivedT>
-    template<typename T>
-    auto ComposableParserImpl<DerivedT>::operator|( T const &other ) const -> Parser {
-        return Parser() | static_cast<DerivedT const &>( *this ) | other;
-    }
-} // namespace detail
-
-// A Combined parser
-using detail::Parser;
-
-// A parser for options
-using detail::Opt;
-
-// A parser for arguments
-using detail::Arg;
-
-// Wrapper for argc, argv from main()
-using detail::Args;
-
-// Specifies the name of the executable
-using detail::ExeName;
-
-// Convenience wrapper for option parser that specifies the help option
-using detail::Help;
-
-// enum of result types from a parse
-using detail::ParseResultType;
-
-// Result type for parser operation
-using detail::ParserResult;
-
-}} // namespace Catch::clara
-
-// end clara.hpp
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-// Restore Clara's value for console width, if present
-#ifdef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
-#define CATCH_CLARA_TEXTFLOW_CONFIG_CONSOLE_WIDTH CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
-#undef CATCH_TEMP_CLARA_CONFIG_CONSOLE_WIDTH
-#endif
-
-// end catch_clara.h
-namespace Catch {
-
-    clara::Parser makeCommandLineParser( ConfigData& config );
-
-} // end namespace Catch
-
-// end catch_commandline.h
-#include <fstream>
-#include <ctime>
-
-namespace Catch {
-
-    clara::Parser makeCommandLineParser( ConfigData& config ) {
-
-        using namespace clara;
-
-        auto const setWarning = [&]( std::string const& warning ) {
-                auto warningSet = [&]() {
-                    if( warning == "NoAssertions" )
-                        return WarnAbout::NoAssertions;
-
-                    if ( warning == "NoTests" )
-                        return WarnAbout::NoTests;
-
-                    return WarnAbout::Nothing;
-                }();
-
-                if (warningSet == WarnAbout::Nothing)
-                    return ParserResult::runtimeError( "Unrecognised warning: '" + warning + "'" );
-                config.warnings = static_cast<WarnAbout::What>( config.warnings | warningSet );
-                return ParserResult::ok( ParseResultType::Matched );
-            };
-        auto const loadTestNamesFromFile = [&]( std::string const& filename ) {
-                std::ifstream f( filename.c_str() );
-                if( !f.is_open() )
-                    return ParserResult::runtimeError( "Unable to load input file: '" + filename + "'" );
-
-                std::string line;
-                while( std::getline( f, line ) ) {
-                    line = trim(line);
-                    if( !line.empty() && !startsWith( line, '#' ) ) {
-                        if( !startsWith( line, '"' ) )
-                            line = '"' + line + '"';
-                        config.testsOrTags.push_back( line + ',' );
-                    }
-                }
-                return ParserResult::ok( ParseResultType::Matched );
-            };
-        auto const setTestOrder = [&]( std::string const& order ) {
-                if( startsWith( "declared", order ) )
-                    config.runOrder = RunTests::InDeclarationOrder;
-                else if( startsWith( "lexical", order ) )
-                    config.runOrder = RunTests::InLexicographicalOrder;
-                else if( startsWith( "random", order ) )
-                    config.runOrder = RunTests::InRandomOrder;
-                else
-                    return clara::ParserResult::runtimeError( "Unrecognised ordering: '" + order + "'" );
-                return ParserResult::ok( ParseResultType::Matched );
-            };
-        auto const setRngSeed = [&]( std::string const& seed ) {
-                if( seed != "time" )
-                    return clara::detail::convertInto( seed, config.rngSeed );
-                config.rngSeed = static_cast<unsigned int>( std::time(nullptr) );
-                return ParserResult::ok( ParseResultType::Matched );
-            };
-        auto const setColourUsage = [&]( std::string const& useColour ) {
-                    auto mode = toLower( useColour );
-
-                    if( mode == "yes" )
-                        config.useColour = UseColour::Yes;
-                    else if( mode == "no" )
-                        config.useColour = UseColour::No;
-                    else if( mode == "auto" )
-                        config.useColour = UseColour::Auto;
-                    else
-                        return ParserResult::runtimeError( "colour mode must be one of: auto, yes or no. '" + useColour + "' not recognised" );
-                return ParserResult::ok( ParseResultType::Matched );
-            };
-        auto const setWaitForKeypress = [&]( std::string const& keypress ) {
-                auto keypressLc = toLower( keypress );
-                if( keypressLc == "start" )
-                    config.waitForKeypress = WaitForKeypress::BeforeStart;
-                else if( keypressLc == "exit" )
-                    config.waitForKeypress = WaitForKeypress::BeforeExit;
-                else if( keypressLc == "both" )
-                    config.waitForKeypress = WaitForKeypress::BeforeStartAndExit;
-                else
-                    return ParserResult::runtimeError( "keypress argument must be one of: start, exit or both. '" + keypress + "' not recognised" );
-            return ParserResult::ok( ParseResultType::Matched );
-            };
-        auto const setVerbosity = [&]( std::string const& verbosity ) {
-            auto lcVerbosity = toLower( verbosity );
-            if( lcVerbosity == "quiet" )
-                config.verbosity = Verbosity::Quiet;
-            else if( lcVerbosity == "normal" )
-                config.verbosity = Verbosity::Normal;
-            else if( lcVerbosity == "high" )
-                config.verbosity = Verbosity::High;
-            else
-                return ParserResult::runtimeError( "Unrecognised verbosity, '" + verbosity + "'" );
-            return ParserResult::ok( ParseResultType::Matched );
-        };
-        auto const setReporter = [&]( std::string const& reporter ) {
-            IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
-
-            auto lcReporter = toLower( reporter );
-            auto result = factories.find( lcReporter );
-
-            if( factories.end() != result )
-                config.reporterName = lcReporter;
-            else
-                return ParserResult::runtimeError( "Unrecognized reporter, '" + reporter + "'. Check available with --list-reporters" );
-            return ParserResult::ok( ParseResultType::Matched );
-        };
-
-        auto cli
-            = ExeName( config.processName )
-            | Help( config.showHelp )
-            | Opt( config.listTests )
-                ["-l"]["--list-tests"]
-                ( "list all/matching test cases" )
-            | Opt( config.listTags )
-                ["-t"]["--list-tags"]
-                ( "list all/matching tags" )
-            | Opt( config.showSuccessfulTests )
-                ["-s"]["--success"]
-                ( "include successful tests in output" )
-            | Opt( config.shouldDebugBreak )
-                ["-b"]["--break"]
-                ( "break into debugger on failure" )
-            | Opt( config.noThrow )
-                ["-e"]["--nothrow"]
-                ( "skip exception tests" )
-            | Opt( config.showInvisibles )
-                ["-i"]["--invisibles"]
-                ( "show invisibles (tabs, newlines)" )
-            | Opt( config.outputFilename, "filename" )
-                ["-o"]["--out"]
-                ( "output filename" )
-            | Opt( setReporter, "name" )
-                ["-r"]["--reporter"]
-                ( "reporter to use (defaults to console)" )
-            | Opt( config.name, "name" )
-                ["-n"]["--name"]
-                ( "suite name" )
-            | Opt( [&]( bool ){ config.abortAfter = 1; } )
-                ["-a"]["--abort"]
-                ( "abort at first failure" )
-            | Opt( [&]( int x ){ config.abortAfter = x; }, "no. failures" )
-                ["-x"]["--abortx"]
-                ( "abort after x failures" )
-            | Opt( setWarning, "warning name" )
-                ["-w"]["--warn"]
-                ( "enable warnings" )
-            | Opt( [&]( bool flag ) { config.showDurations = flag ? ShowDurations::Always : ShowDurations::Never; }, "yes|no" )
-                ["-d"]["--durations"]
-                ( "show test durations" )
-            | Opt( loadTestNamesFromFile, "filename" )
-                ["-f"]["--input-file"]
-                ( "load test names to run from a file" )
-            | Opt( config.filenamesAsTags )
-                ["-#"]["--filenames-as-tags"]
-                ( "adds a tag for the filename" )
-            | Opt( config.sectionsToRun, "section name" )
-                ["-c"]["--section"]
-                ( "specify section to run" )
-            | Opt( setVerbosity, "quiet|normal|high" )
-                ["-v"]["--verbosity"]
-                ( "set output verbosity" )
-            | Opt( config.listTestNamesOnly )
-                ["--list-test-names-only"]
-                ( "list all/matching test cases names only" )
-            | Opt( config.listReporters )
-                ["--list-reporters"]
-                ( "list all reporters" )
-            | Opt( setTestOrder, "decl|lex|rand" )
-                ["--order"]
-                ( "test case order (defaults to decl)" )
-            | Opt( setRngSeed, "'time'|number" )
-                ["--rng-seed"]
-                ( "set a specific seed for random numbers" )
-            | Opt( setColourUsage, "yes|no" )
-                ["--use-colour"]
-                ( "should output be colourised" )
-            | Opt( config.libIdentify )
-                ["--libidentify"]
-                ( "report name and version according to libidentify standard" )
-            | Opt( setWaitForKeypress, "start|exit|both" )
-                ["--wait-for-keypress"]
-                ( "waits for a keypress before exiting" )
-            | Opt( config.benchmarkResolutionMultiple, "multiplier" )
-                ["--benchmark-resolution-multiple"]
-                ( "multiple of clock resolution to run benchmarks" )
-
-            | Arg( config.testsOrTags, "test name|pattern|tags" )
-                ( "which test or tests to use" );
-
-        return cli;
-    }
-
-} // end namespace Catch
-// end catch_commandline.cpp
-// start catch_common.cpp
-
-#include <cstring>
-#include <ostream>
-
-namespace Catch {
-
-    bool SourceLineInfo::empty() const noexcept {
-        return file[0] == '\0';
-    }
-    bool SourceLineInfo::operator == ( SourceLineInfo const& other ) const noexcept {
-        return line == other.line && (file == other.file || std::strcmp(file, other.file) == 0);
-    }
-    bool SourceLineInfo::operator < ( SourceLineInfo const& other ) const noexcept {
-        // We can assume that the same file will usually have the same pointer.
-        // Thus, if the pointers are the same, there is no point in calling the strcmp
-        return line < other.line || ( line == other.line && file != other.file && (std::strcmp(file, other.file) < 0));
-    }
-
-    std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ) {
-#ifndef __GNUG__
-        os << info.file << '(' << info.line << ')';
-#else
-        os << info.file << ':' << info.line;
-#endif
-        return os;
-    }
-
-    std::string StreamEndStop::operator+() const {
-        return std::string();
-    }
-
-    NonCopyable::NonCopyable() = default;
-    NonCopyable::~NonCopyable() = default;
-
-}
-// end catch_common.cpp
-// start catch_config.cpp
-
-namespace Catch {
-
-    Config::Config( ConfigData const& data )
-    :   m_data( data ),
-        m_stream( openStream() )
-    {
-        TestSpecParser parser(ITagAliasRegistry::get());
-        if (!data.testsOrTags.empty()) {
-            m_hasTestFilters = true;
-            for( auto const& testOrTags : data.testsOrTags )
-                parser.parse( testOrTags );
-        }
-        m_testSpec = parser.testSpec();
-    }
-
-    std::string const& Config::getFilename() const {
-        return m_data.outputFilename ;
-    }
-
-    bool Config::listTests() const          { return m_data.listTests; }
-    bool Config::listTestNamesOnly() const  { return m_data.listTestNamesOnly; }
-    bool Config::listTags() const           { return m_data.listTags; }
-    bool Config::listReporters() const      { return m_data.listReporters; }
-
-    std::string Config::getProcessName() const { return m_data.processName; }
-    std::string const& Config::getReporterName() const { return m_data.reporterName; }
-
-    std::vector<std::string> const& Config::getTestsOrTags() const { return m_data.testsOrTags; }
-    std::vector<std::string> const& Config::getSectionsToRun() const { return m_data.sectionsToRun; }
-
-    TestSpec const& Config::testSpec() const { return m_testSpec; }
-    bool Config::hasTestFilters() const { return m_hasTestFilters; }
-
-    bool Config::showHelp() const { return m_data.showHelp; }
-
-    // IConfig interface
-    bool Config::allowThrows() const                   { return !m_data.noThrow; }
-    std::ostream& Config::stream() const               { return m_stream->stream(); }
-    std::string Config::name() const                   { return m_data.name.empty() ? m_data.processName : m_data.name; }
-    bool Config::includeSuccessfulResults() const      { return m_data.showSuccessfulTests; }
-    bool Config::warnAboutMissingAssertions() const    { return !!(m_data.warnings & WarnAbout::NoAssertions); }
-    bool Config::warnAboutNoTests() const              { return !!(m_data.warnings & WarnAbout::NoTests); }
-    ShowDurations::OrNot Config::showDurations() const { return m_data.showDurations; }
-    RunTests::InWhatOrder Config::runOrder() const     { return m_data.runOrder; }
-    unsigned int Config::rngSeed() const               { return m_data.rngSeed; }
-    int Config::benchmarkResolutionMultiple() const    { return m_data.benchmarkResolutionMultiple; }
-    UseColour::YesOrNo Config::useColour() const       { return m_data.useColour; }
-    bool Config::shouldDebugBreak() const              { return m_data.shouldDebugBreak; }
-    int Config::abortAfter() const                     { return m_data.abortAfter; }
-    bool Config::showInvisibles() const                { return m_data.showInvisibles; }
-    Verbosity Config::verbosity() const                { return m_data.verbosity; }
-
-    IStream const* Config::openStream() {
-        return Catch::makeStream(m_data.outputFilename);
-    }
-
-} // end namespace Catch
-// end catch_config.cpp
-// start catch_console_colour.cpp
-
-#if defined(__clang__)
-#    pragma clang diagnostic push
-#    pragma clang diagnostic ignored "-Wexit-time-destructors"
-#endif
-
-// start catch_errno_guard.h
-
-namespace Catch {
-
-    class ErrnoGuard {
-    public:
-        ErrnoGuard();
-        ~ErrnoGuard();
-    private:
-        int m_oldErrno;
-    };
-
-}
-
-// end catch_errno_guard.h
-#include <sstream>
-
-namespace Catch {
-    namespace {
-
-        struct IColourImpl {
-            virtual ~IColourImpl() = default;
-            virtual void use( Colour::Code _colourCode ) = 0;
-        };
-
-        struct NoColourImpl : IColourImpl {
-            void use( Colour::Code ) {}
-
-            static IColourImpl* instance() {
-                static NoColourImpl s_instance;
-                return &s_instance;
-            }
-        };
-
-    } // anon namespace
-} // namespace Catch
-
-#if !defined( CATCH_CONFIG_COLOUR_NONE ) && !defined( CATCH_CONFIG_COLOUR_WINDOWS ) && !defined( CATCH_CONFIG_COLOUR_ANSI )
-#   ifdef CATCH_PLATFORM_WINDOWS
-#       define CATCH_CONFIG_COLOUR_WINDOWS
-#   else
-#       define CATCH_CONFIG_COLOUR_ANSI
-#   endif
-#endif
-
-#if defined ( CATCH_CONFIG_COLOUR_WINDOWS ) /////////////////////////////////////////
-
-namespace Catch {
-namespace {
-
-    class Win32ColourImpl : public IColourImpl {
-    public:
-        Win32ColourImpl() : stdoutHandle( GetStdHandle(STD_OUTPUT_HANDLE) )
-        {
-            CONSOLE_SCREEN_BUFFER_INFO csbiInfo;
-            GetConsoleScreenBufferInfo( stdoutHandle, &csbiInfo );
-            originalForegroundAttributes = csbiInfo.wAttributes & ~( BACKGROUND_GREEN | BACKGROUND_RED | BACKGROUND_BLUE | BACKGROUND_INTENSITY );
-            originalBackgroundAttributes = csbiInfo.wAttributes & ~( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE | FOREGROUND_INTENSITY );
-        }
-
-        void use( Colour::Code _colourCode ) override {
-            switch( _colourCode ) {
-                case Colour::None:      return setTextAttribute( originalForegroundAttributes );
-                case Colour::White:     return setTextAttribute( FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
-                case Colour::Red:       return setTextAttribute( FOREGROUND_RED );
-                case Colour::Green:     return setTextAttribute( FOREGROUND_GREEN );
-                case Colour::Blue:      return setTextAttribute( FOREGROUND_BLUE );
-                case Colour::Cyan:      return setTextAttribute( FOREGROUND_BLUE | FOREGROUND_GREEN );
-                case Colour::Yellow:    return setTextAttribute( FOREGROUND_RED | FOREGROUND_GREEN );
-                case Colour::Grey:      return setTextAttribute( 0 );
-
-                case Colour::LightGrey:     return setTextAttribute( FOREGROUND_INTENSITY );
-                case Colour::BrightRed:     return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED );
-                case Colour::BrightGreen:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN );
-                case Colour::BrightWhite:   return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_GREEN | FOREGROUND_RED | FOREGROUND_BLUE );
-                case Colour::BrightYellow:  return setTextAttribute( FOREGROUND_INTENSITY | FOREGROUND_RED | FOREGROUND_GREEN );
-
-                case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
-
-                default:
-                    CATCH_ERROR( "Unknown colour requested" );
-            }
-        }
-
-    private:
-        void setTextAttribute( WORD _textAttribute ) {
-            SetConsoleTextAttribute( stdoutHandle, _textAttribute | originalBackgroundAttributes );
-        }
-        HANDLE stdoutHandle;
-        WORD originalForegroundAttributes;
-        WORD originalBackgroundAttributes;
-    };
-
-    IColourImpl* platformColourInstance() {
-        static Win32ColourImpl s_instance;
-
-        IConfigPtr config = getCurrentContext().getConfig();
-        UseColour::YesOrNo colourMode = config
-            ? config->useColour()
-            : UseColour::Auto;
-        if( colourMode == UseColour::Auto )
-            colourMode = UseColour::Yes;
-        return colourMode == UseColour::Yes
-            ? &s_instance
-            : NoColourImpl::instance();
-    }
-
-} // end anon namespace
-} // end namespace Catch
-
-#elif defined( CATCH_CONFIG_COLOUR_ANSI ) //////////////////////////////////////
-
-#include <unistd.h>
-
-namespace Catch {
-namespace {
-
-    // use POSIX/ ANSI console terminal codes
-    // Thanks to Adam Strzelecki for original contribution
-    // (http://github.com/nanoant)
-    // https://github.com/philsquared/Catch/pull/131
-    class PosixColourImpl : public IColourImpl {
-    public:
-        void use( Colour::Code _colourCode ) override {
-            switch( _colourCode ) {
-                case Colour::None:
-                case Colour::White:     return setColour( "[0m" );
-                case Colour::Red:       return setColour( "[0;31m" );
-                case Colour::Green:     return setColour( "[0;32m" );
-                case Colour::Blue:      return setColour( "[0;34m" );
-                case Colour::Cyan:      return setColour( "[0;36m" );
-                case Colour::Yellow:    return setColour( "[0;33m" );
-                case Colour::Grey:      return setColour( "[1;30m" );
-
-                case Colour::LightGrey:     return setColour( "[0;37m" );
-                case Colour::BrightRed:     return setColour( "[1;31m" );
-                case Colour::BrightGreen:   return setColour( "[1;32m" );
-                case Colour::BrightWhite:   return setColour( "[1;37m" );
-                case Colour::BrightYellow:  return setColour( "[1;33m" );
-
-                case Colour::Bright: CATCH_INTERNAL_ERROR( "not a colour" );
-                default: CATCH_INTERNAL_ERROR( "Unknown colour requested" );
-            }
-        }
-        static IColourImpl* instance() {
-            static PosixColourImpl s_instance;
-            return &s_instance;
-        }
-
-    private:
-        void setColour( const char* _escapeCode ) {
-            getCurrentContext().getConfig()->stream()
-                << '\033' << _escapeCode;
-        }
-    };
-
-    bool useColourOnPlatform() {
-        return
-#ifdef CATCH_PLATFORM_MAC
-            !isDebuggerActive() &&
-#endif
-#if !(defined(__DJGPP__) && defined(__STRICT_ANSI__))
-            isatty(STDOUT_FILENO)
-#else
-            false
-#endif
-            ;
-    }
-    IColourImpl* platformColourInstance() {
-        ErrnoGuard guard;
-        IConfigPtr config = getCurrentContext().getConfig();
-        UseColour::YesOrNo colourMode = config
-            ? config->useColour()
-            : UseColour::Auto;
-        if( colourMode == UseColour::Auto )
-            colourMode = useColourOnPlatform()
-                ? UseColour::Yes
-                : UseColour::No;
-        return colourMode == UseColour::Yes
-            ? PosixColourImpl::instance()
-            : NoColourImpl::instance();
-    }
-
-} // end anon namespace
-} // end namespace Catch
-
-#else  // not Windows or ANSI ///////////////////////////////////////////////
-
-namespace Catch {
-
-    static IColourImpl* platformColourInstance() { return NoColourImpl::instance(); }
-
-} // end namespace Catch
-
-#endif // Windows/ ANSI/ None
-
-namespace Catch {
-
-    Colour::Colour( Code _colourCode ) { use( _colourCode ); }
-    Colour::Colour( Colour&& rhs ) noexcept {
-        m_moved = rhs.m_moved;
-        rhs.m_moved = true;
-    }
-    Colour& Colour::operator=( Colour&& rhs ) noexcept {
-        m_moved = rhs.m_moved;
-        rhs.m_moved  = true;
-        return *this;
-    }
-
-    Colour::~Colour(){ if( !m_moved ) use( None ); }
-
-    void Colour::use( Code _colourCode ) {
-        static IColourImpl* impl = platformColourInstance();
-        impl->use( _colourCode );
-    }
-
-    std::ostream& operator << ( std::ostream& os, Colour const& ) {
-        return os;
-    }
-
-} // end namespace Catch
-
-#if defined(__clang__)
-#    pragma clang diagnostic pop
-#endif
-
-// end catch_console_colour.cpp
-// start catch_context.cpp
-
-namespace Catch {
-
-    class Context : public IMutableContext, NonCopyable {
-
-    public: // IContext
-        IResultCapture* getResultCapture() override {
-            return m_resultCapture;
-        }
-        IRunner* getRunner() override {
-            return m_runner;
-        }
-
-        IConfigPtr const& getConfig() const override {
-            return m_config;
-        }
-
-        ~Context() override;
-
-    public: // IMutableContext
-        void setResultCapture( IResultCapture* resultCapture ) override {
-            m_resultCapture = resultCapture;
-        }
-        void setRunner( IRunner* runner ) override {
-            m_runner = runner;
-        }
-        void setConfig( IConfigPtr const& config ) override {
-            m_config = config;
-        }
-
-        friend IMutableContext& getCurrentMutableContext();
-
-    private:
-        IConfigPtr m_config;
-        IRunner* m_runner = nullptr;
-        IResultCapture* m_resultCapture = nullptr;
-    };
-
-    IMutableContext *IMutableContext::currentContext = nullptr;
-
-    void IMutableContext::createContext()
-    {
-        currentContext = new Context();
-    }
-
-    void cleanUpContext() {
-        delete IMutableContext::currentContext;
-        IMutableContext::currentContext = nullptr;
-    }
-    IContext::~IContext() = default;
-    IMutableContext::~IMutableContext() = default;
-    Context::~Context() = default;
-}
-// end catch_context.cpp
-// start catch_debug_console.cpp
-
-// start catch_debug_console.h
-
-#include <string>
-
-namespace Catch {
-    void writeToDebugConsole( std::string const& text );
-}
-
-// end catch_debug_console.h
-#ifdef CATCH_PLATFORM_WINDOWS
-
-    namespace Catch {
-        void writeToDebugConsole( std::string const& text ) {
-            ::OutputDebugStringA( text.c_str() );
-        }
-    }
-
-#else
-
-    namespace Catch {
-        void writeToDebugConsole( std::string const& text ) {
-            // !TBD: Need a version for Mac/ XCode and other IDEs
-            Catch::cout() << text;
-        }
-    }
-
-#endif // Platform
-// end catch_debug_console.cpp
-// start catch_debugger.cpp
-
-#ifdef CATCH_PLATFORM_MAC
-
-#  include <assert.h>
-#  include <stdbool.h>
-#  include <sys/types.h>
-#  include <unistd.h>
-#  include <cstddef>
-#  include <ostream>
-
-#ifdef __apple_build_version__
-    // These headers will only compile with AppleClang (XCode)
-    // For other compilers (Clang, GCC, ... ) we need to exclude them
-#  include <sys/sysctl.h>
-#endif
-
-    namespace Catch {
-        #ifdef __apple_build_version__
-        // The following function is taken directly from the following technical note:
-        // https://developer.apple.com/library/archive/qa/qa1361/_index.html
-
-        // Returns true if the current process is being debugged (either
-        // running under the debugger or has a debugger attached post facto).
-        bool isDebuggerActive(){
-            int                 mib[4];
-            struct kinfo_proc   info;
-            std::size_t         size;
-
-            // Initialize the flags so that, if sysctl fails for some bizarre
-            // reason, we get a predictable result.
-
-            info.kp_proc.p_flag = 0;
-
-            // Initialize mib, which tells sysctl the info we want, in this case
-            // we're looking for information about a specific process ID.
-
-            mib[0] = CTL_KERN;
-            mib[1] = KERN_PROC;
-            mib[2] = KERN_PROC_PID;
-            mib[3] = getpid();
-
-            // Call sysctl.
-
-            size = sizeof(info);
-            if( sysctl(mib, sizeof(mib) / sizeof(*mib), &info, &size, nullptr, 0) != 0 ) {
-                Catch::cerr() << "\n** Call to sysctl failed - unable to determine if debugger is active **\n" << std::endl;
-                return false;
-            }
-
-            // We're being debugged if the P_TRACED flag is set.
-
-            return ( (info.kp_proc.p_flag & P_TRACED) != 0 );
-        }
-        #else
-        bool isDebuggerActive() {
-            // We need to find another way to determine this for non-appleclang compilers on macOS
-            return false;
-        }
-        #endif
-    } // namespace Catch
-
-#elif defined(CATCH_PLATFORM_LINUX)
-    #include <fstream>
-    #include <string>
-
-    namespace Catch{
-        // The standard POSIX way of detecting a debugger is to attempt to
-        // ptrace() the process, but this needs to be done from a child and not
-        // this process itself to still allow attaching to this process later
-        // if wanted, so is rather heavy. Under Linux we have the PID of the
-        // "debugger" (which doesn't need to be gdb, of course, it could also
-        // be strace, for example) in /proc/$PID/status, so just get it from
-        // there instead.
-        bool isDebuggerActive(){
-            // Libstdc++ has a bug, where std::ifstream sets errno to 0
-            // This way our users can properly assert over errno values
-            ErrnoGuard guard;
-            std::ifstream in("/proc/self/status");
-            for( std::string line; std::getline(in, line); ) {
-                static const int PREFIX_LEN = 11;
-                if( line.compare(0, PREFIX_LEN, "TracerPid:\t") == 0 ) {
-                    // We're traced if the PID is not 0 and no other PID starts
-                    // with 0 digit, so it's enough to check for just a single
-                    // character.
-                    return line.length() > PREFIX_LEN && line[PREFIX_LEN] != '0';
-                }
-            }
-
-            return false;
-        }
-    } // namespace Catch
-#elif defined(_MSC_VER)
-    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
-    namespace Catch {
-        bool isDebuggerActive() {
-            return IsDebuggerPresent() != 0;
-        }
-    }
-#elif defined(__MINGW32__)
-    extern "C" __declspec(dllimport) int __stdcall IsDebuggerPresent();
-    namespace Catch {
-        bool isDebuggerActive() {
-            return IsDebuggerPresent() != 0;
-        }
-    }
-#else
-    namespace Catch {
-       bool isDebuggerActive() { return false; }
-    }
-#endif // Platform
-// end catch_debugger.cpp
-// start catch_decomposer.cpp
-
-namespace Catch {
-
-    ITransientExpression::~ITransientExpression() = default;
-
-    void formatReconstructedExpression( std::ostream &os, std::string const& lhs, StringRef op, std::string const& rhs ) {
-        if( lhs.size() + rhs.size() < 40 &&
-                lhs.find('\n') == std::string::npos &&
-                rhs.find('\n') == std::string::npos )
-            os << lhs << " " << op << " " << rhs;
-        else
-            os << lhs << "\n" << op << "\n" << rhs;
-    }
-}
-// end catch_decomposer.cpp
-// start catch_enforce.cpp
-
-namespace Catch {
-#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS_CUSTOM_HANDLER)
-    [[noreturn]]
-    void throw_exception(std::exception const& e) {
-        Catch::cerr() << "Catch will terminate because it needed to throw an exception.\n"
-                      << "The message was: " << e.what() << '\n';
-        std::terminate();
-    }
-#endif
-} // namespace Catch;
-// end catch_enforce.cpp
-// start catch_enum_values_registry.cpp
-// start catch_enum_values_registry.h
-
-#include <vector>
-#include <memory>
-
-namespace Catch {
-
-    namespace Detail {
-
-        std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values );
-
-        class EnumValuesRegistry : public IMutableEnumValuesRegistry {
-
-            std::vector<std::unique_ptr<EnumInfo>> m_enumInfos;
-
-            EnumInfo const& registerEnum( StringRef enumName, StringRef allEnums, std::vector<int> const& values) override;
-        };
-
-        std::vector<std::string> parseEnums( StringRef enums );
-
-    } // Detail
-
-} // Catch
-
-// end catch_enum_values_registry.h
-
-#include <map>
-#include <cassert>
-
-namespace Catch {
-
-    IMutableEnumValuesRegistry::~IMutableEnumValuesRegistry() {}
-
-    namespace Detail {
-
-        std::vector<std::string> parseEnums( StringRef enums ) {
-            auto enumValues = splitStringRef( enums, ',' );
-            std::vector<std::string> parsed;
-            parsed.reserve( enumValues.size() );
-            for( auto const& enumValue : enumValues ) {
-                auto identifiers = splitStringRef( enumValue, ':' );
-                parsed.push_back( Catch::trim( identifiers.back() ) );
-            }
-            return parsed;
-        }
-
-        EnumInfo::~EnumInfo() {}
-
-        StringRef EnumInfo::lookup( int value ) const {
-            for( auto const& valueToName : m_values ) {
-                if( valueToName.first == value )
-                    return valueToName.second;
-            }
-            return "{** unexpected enum value **}";
-        }
-
-        std::unique_ptr<EnumInfo> makeEnumInfo( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
-            std::unique_ptr<EnumInfo> enumInfo( new EnumInfo );
-            enumInfo->m_name = enumName;
-            enumInfo->m_values.reserve( values.size() );
-
-            const auto valueNames = Catch::Detail::parseEnums( allValueNames );
-            assert( valueNames.size() == values.size() );
-            std::size_t i = 0;
-            for( auto value : values )
-                enumInfo->m_values.push_back({ value, valueNames[i++] });
-
-            return enumInfo;
-        }
-
-        EnumInfo const& EnumValuesRegistry::registerEnum( StringRef enumName, StringRef allValueNames, std::vector<int> const& values ) {
-            auto enumInfo = makeEnumInfo( enumName, allValueNames, values );
-            EnumInfo* raw = enumInfo.get();
-            m_enumInfos.push_back( std::move( enumInfo ) );
-            return *raw;
-        }
-
-    } // Detail
-} // Catch
-
-// end catch_enum_values_registry.cpp
-// start catch_errno_guard.cpp
-
-#include <cerrno>
-
-namespace Catch {
-        ErrnoGuard::ErrnoGuard():m_oldErrno(errno){}
-        ErrnoGuard::~ErrnoGuard() { errno = m_oldErrno; }
-}
-// end catch_errno_guard.cpp
-// start catch_exception_translator_registry.cpp
-
-// start catch_exception_translator_registry.h
-
-#include <vector>
-#include <string>
-#include <memory>
-
-namespace Catch {
-
-    class ExceptionTranslatorRegistry : public IExceptionTranslatorRegistry {
-    public:
-        ~ExceptionTranslatorRegistry();
-        virtual void registerTranslator( const IExceptionTranslator* translator );
-        std::string translateActiveException() const override;
-        std::string tryTranslators() const;
-
-    private:
-        std::vector<std::unique_ptr<IExceptionTranslator const>> m_translators;
-    };
-}
-
-// end catch_exception_translator_registry.h
-#ifdef __OBJC__
-#import "Foundation/Foundation.h"
-#endif
-
-namespace Catch {
-
-    ExceptionTranslatorRegistry::~ExceptionTranslatorRegistry() {
-    }
-
-    void ExceptionTranslatorRegistry::registerTranslator( const IExceptionTranslator* translator ) {
-        m_translators.push_back( std::unique_ptr<const IExceptionTranslator>( translator ) );
-    }
-
-#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
-    std::string ExceptionTranslatorRegistry::translateActiveException() const {
-        try {
-#ifdef __OBJC__
-            // In Objective-C try objective-c exceptions first
-            @try {
-                return tryTranslators();
-            }
-            @catch (NSException *exception) {
-                return Catch::Detail::stringify( [exception description] );
-            }
-#else
-            // Compiling a mixed mode project with MSVC means that CLR
-            // exceptions will be caught in (...) as well. However, these
-            // do not fill-in std::current_exception and thus lead to crash
-            // when attempting rethrow.
-            // /EHa switch also causes structured exceptions to be caught
-            // here, but they fill-in current_exception properly, so
-            // at worst the output should be a little weird, instead of
-            // causing a crash.
-            if (std::current_exception() == nullptr) {
-                return "Non C++ exception. Possibly a CLR exception.";
-            }
-            return tryTranslators();
-#endif
-        }
-        catch( TestFailureException& ) {
-            std::rethrow_exception(std::current_exception());
-        }
-        catch( std::exception& ex ) {
-            return ex.what();
-        }
-        catch( std::string& msg ) {
-            return msg;
-        }
-        catch( const char* msg ) {
-            return msg;
-        }
-        catch(...) {
-            return "Unknown exception";
-        }
-    }
-
-    std::string ExceptionTranslatorRegistry::tryTranslators() const {
-        if (m_translators.empty()) {
-            std::rethrow_exception(std::current_exception());
-        } else {
-            return m_translators[0]->translate(m_translators.begin() + 1, m_translators.end());
-        }
-    }
-
-#else // ^^ Exceptions are enabled // Exceptions are disabled vv
-    std::string ExceptionTranslatorRegistry::translateActiveException() const {
-        CATCH_INTERNAL_ERROR("Attempted to translate active exception under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
-    }
-
-    std::string ExceptionTranslatorRegistry::tryTranslators() const {
-        CATCH_INTERNAL_ERROR("Attempted to use exception translators under CATCH_CONFIG_DISABLE_EXCEPTIONS!");
-    }
-#endif
-
-}
-// end catch_exception_translator_registry.cpp
-// start catch_fatal_condition.cpp
-
-#if defined(__GNUC__)
-#    pragma GCC diagnostic push
-#    pragma GCC diagnostic ignored "-Wmissing-field-initializers"
-#endif
-
-#if defined( CATCH_CONFIG_WINDOWS_SEH ) || defined( CATCH_CONFIG_POSIX_SIGNALS )
-
-namespace {
-    // Report the error condition
-    void reportFatal( char const * const message ) {
-        Catch::getCurrentContext().getResultCapture()->handleFatalErrorCondition( message );
-    }
-}
-
-#endif // signals/SEH handling
-
-#if defined( CATCH_CONFIG_WINDOWS_SEH )
-
-namespace Catch {
-    struct SignalDefs { DWORD id; const char* name; };
-
-    // There is no 1-1 mapping between signals and windows exceptions.
-    // Windows can easily distinguish between SO and SigSegV,
-    // but SigInt, SigTerm, etc are handled differently.
-    static SignalDefs signalDefs[] = {
-        { static_cast<DWORD>(EXCEPTION_ILLEGAL_INSTRUCTION),  "SIGILL - Illegal instruction signal" },
-        { static_cast<DWORD>(EXCEPTION_STACK_OVERFLOW), "SIGSEGV - Stack overflow" },
-        { static_cast<DWORD>(EXCEPTION_ACCESS_VIOLATION), "SIGSEGV - Segmentation violation signal" },
-        { static_cast<DWORD>(EXCEPTION_INT_DIVIDE_BY_ZERO), "Divide by zero error" },
-    };
-
-    LONG CALLBACK FatalConditionHandler::handleVectoredException(PEXCEPTION_POINTERS ExceptionInfo) {
-        for (auto const& def : signalDefs) {
-            if (ExceptionInfo->ExceptionRecord->ExceptionCode == def.id) {
-                reportFatal(def.name);
-            }
-        }
-        // If its not an exception we care about, pass it along.
-        // This stops us from eating debugger breaks etc.
-        return EXCEPTION_CONTINUE_SEARCH;
-    }
-
-    FatalConditionHandler::FatalConditionHandler() {
-        isSet = true;
-        // 32k seems enough for Catch to handle stack overflow,
-        // but the value was found experimentally, so there is no strong guarantee
-        guaranteeSize = 32 * 1024;
-        exceptionHandlerHandle = nullptr;
-        // Register as first handler in current chain
-        exceptionHandlerHandle = AddVectoredExceptionHandler(1, handleVectoredException);
-        // Pass in guarantee size to be filled
-        SetThreadStackGuarantee(&guaranteeSize);
-    }
-
-    void FatalConditionHandler::reset() {
-        if (isSet) {
-            RemoveVectoredExceptionHandler(exceptionHandlerHandle);
-            SetThreadStackGuarantee(&guaranteeSize);
-            exceptionHandlerHandle = nullptr;
-            isSet = false;
-        }
-    }
-
-    FatalConditionHandler::~FatalConditionHandler() {
-        reset();
-    }
-
-bool FatalConditionHandler::isSet = false;
-ULONG FatalConditionHandler::guaranteeSize = 0;
-PVOID FatalConditionHandler::exceptionHandlerHandle = nullptr;
-
-} // namespace Catch
-
-#elif defined( CATCH_CONFIG_POSIX_SIGNALS )
-
-namespace Catch {
-
-    struct SignalDefs {
-        int id;
-        const char* name;
-    };
-
-    // 32kb for the alternate stack seems to be sufficient. However, this value
-    // is experimentally determined, so that's not guaranteed.
-    constexpr static std::size_t sigStackSize = 32768 >= MINSIGSTKSZ ? 32768 : MINSIGSTKSZ;
-
-    static SignalDefs signalDefs[] = {
-        { SIGINT,  "SIGINT - Terminal interrupt signal" },
-        { SIGILL,  "SIGILL - Illegal instruction signal" },
-        { SIGFPE,  "SIGFPE - Floating point error signal" },
-        { SIGSEGV, "SIGSEGV - Segmentation violation signal" },
-        { SIGTERM, "SIGTERM - Termination request signal" },
-        { SIGABRT, "SIGABRT - Abort (abnormal termination) signal" }
-    };
-
-    void FatalConditionHandler::handleSignal( int sig ) {
-        char const * name = "<unknown signal>";
-        for (auto const& def : signalDefs) {
-            if (sig == def.id) {
-                name = def.name;
-                break;
-            }
-        }
-        reset();
-        reportFatal(name);
-        raise( sig );
-    }
-
-    FatalConditionHandler::FatalConditionHandler() {
-        isSet = true;
-        stack_t sigStack;
-        sigStack.ss_sp = altStackMem;
-        sigStack.ss_size = sigStackSize;
-        sigStack.ss_flags = 0;
-        sigaltstack(&sigStack, &oldSigStack);
-        struct sigaction sa = { };
-
-        sa.sa_handler = handleSignal;
-        sa.sa_flags = SA_ONSTACK;
-        for (std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i) {
-            sigaction(signalDefs[i].id, &sa, &oldSigActions[i]);
-        }
-    }
-
-    FatalConditionHandler::~FatalConditionHandler() {
-        reset();
-    }
-
-    void FatalConditionHandler::reset() {
-        if( isSet ) {
-            // Set signals back to previous values -- hopefully nobody overwrote them in the meantime
-            for( std::size_t i = 0; i < sizeof(signalDefs)/sizeof(SignalDefs); ++i ) {
-                sigaction(signalDefs[i].id, &oldSigActions[i], nullptr);
-            }
-            // Return the old stack
-            sigaltstack(&oldSigStack, nullptr);
-            isSet = false;
-        }
-    }
-
-    bool FatalConditionHandler::isSet = false;
-    struct sigaction FatalConditionHandler::oldSigActions[sizeof(signalDefs)/sizeof(SignalDefs)] = {};
-    stack_t FatalConditionHandler::oldSigStack = {};
-    char FatalConditionHandler::altStackMem[sigStackSize] = {};
-
-} // namespace Catch
-
-#else
-
-namespace Catch {
-    void FatalConditionHandler::reset() {}
-}
-
-#endif // signals/SEH handling
-
-#if defined(__GNUC__)
-#    pragma GCC diagnostic pop
-#endif
-// end catch_fatal_condition.cpp
-// start catch_generators.cpp
-
-// start catch_random_number_generator.h
-
-#include <algorithm>
-#include <random>
-
-namespace Catch {
-
-    struct IConfig;
-
-    std::mt19937& rng();
-    void seedRng( IConfig const& config );
-    unsigned int rngSeed();
-
-}
-
-// end catch_random_number_generator.h
-#include <limits>
-#include <set>
-
-namespace Catch {
-
-IGeneratorTracker::~IGeneratorTracker() {}
-
-const char* GeneratorException::what() const noexcept {
-    return m_msg;
-}
-
-namespace Generators {
-
-    GeneratorUntypedBase::~GeneratorUntypedBase() {}
-
-    auto acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
-        return getResultCapture().acquireGeneratorTracker( lineInfo );
-    }
-
-} // namespace Generators
-} // namespace Catch
-// end catch_generators.cpp
-// start catch_interfaces_capture.cpp
-
-namespace Catch {
-    IResultCapture::~IResultCapture() = default;
-}
-// end catch_interfaces_capture.cpp
-// start catch_interfaces_config.cpp
-
-namespace Catch {
-    IConfig::~IConfig() = default;
-}
-// end catch_interfaces_config.cpp
-// start catch_interfaces_exception.cpp
-
-namespace Catch {
-    IExceptionTranslator::~IExceptionTranslator() = default;
-    IExceptionTranslatorRegistry::~IExceptionTranslatorRegistry() = default;
-}
-// end catch_interfaces_exception.cpp
-// start catch_interfaces_registry_hub.cpp
-
-namespace Catch {
-    IRegistryHub::~IRegistryHub() = default;
-    IMutableRegistryHub::~IMutableRegistryHub() = default;
-}
-// end catch_interfaces_registry_hub.cpp
-// start catch_interfaces_reporter.cpp
-
-// start catch_reporter_listening.h
-
-namespace Catch {
-
-    class ListeningReporter : public IStreamingReporter {
-        using Reporters = std::vector<IStreamingReporterPtr>;
-        Reporters m_listeners;
-        IStreamingReporterPtr m_reporter = nullptr;
-        ReporterPreferences m_preferences;
-
-    public:
-        ListeningReporter();
-
-        void addListener( IStreamingReporterPtr&& listener );
-        void addReporter( IStreamingReporterPtr&& reporter );
-
-    public: // IStreamingReporter
-
-        ReporterPreferences getPreferences() const override;
-
-        void noMatchingTestCases( std::string const& spec ) override;
-
-        static std::set<Verbosity> getSupportedVerbosities();
-
-        void benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) override;
-        void benchmarkEnded( BenchmarkStats const& benchmarkStats ) override;
-
-        void testRunStarting( TestRunInfo const& testRunInfo ) override;
-        void testGroupStarting( GroupInfo const& groupInfo ) override;
-        void testCaseStarting( TestCaseInfo const& testInfo ) override;
-        void sectionStarting( SectionInfo const& sectionInfo ) override;
-        void assertionStarting( AssertionInfo const& assertionInfo ) override;
-
-        // The return value indicates if the messages buffer should be cleared:
-        bool assertionEnded( AssertionStats const& assertionStats ) override;
-        void sectionEnded( SectionStats const& sectionStats ) override;
-        void testCaseEnded( TestCaseStats const& testCaseStats ) override;
-        void testGroupEnded( TestGroupStats const& testGroupStats ) override;
-        void testRunEnded( TestRunStats const& testRunStats ) override;
-
-        void skipTest( TestCaseInfo const& testInfo ) override;
-        bool isMulti() const override;
-
-    };
-
-} // end namespace Catch
-
-// end catch_reporter_listening.h
-namespace Catch {
-
-    ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig )
-    :   m_stream( &_fullConfig->stream() ), m_fullConfig( _fullConfig ) {}
-
-    ReporterConfig::ReporterConfig( IConfigPtr const& _fullConfig, std::ostream& _stream )
-    :   m_stream( &_stream ), m_fullConfig( _fullConfig ) {}
-
-    std::ostream& ReporterConfig::stream() const { return *m_stream; }
-    IConfigPtr ReporterConfig::fullConfig() const { return m_fullConfig; }
-
-    TestRunInfo::TestRunInfo( std::string const& _name ) : name( _name ) {}
-
-    GroupInfo::GroupInfo(  std::string const& _name,
-                           std::size_t _groupIndex,
-                           std::size_t _groupsCount )
-    :   name( _name ),
-        groupIndex( _groupIndex ),
-        groupsCounts( _groupsCount )
-    {}
-
-     AssertionStats::AssertionStats( AssertionResult const& _assertionResult,
-                                     std::vector<MessageInfo> const& _infoMessages,
-                                     Totals const& _totals )
-    :   assertionResult( _assertionResult ),
-        infoMessages( _infoMessages ),
-        totals( _totals )
-    {
-        assertionResult.m_resultData.lazyExpression.m_transientExpression = _assertionResult.m_resultData.lazyExpression.m_transientExpression;
-
-        if( assertionResult.hasMessage() ) {
-            // Copy message into messages list.
-            // !TBD This should have been done earlier, somewhere
-            MessageBuilder builder( assertionResult.getTestMacroName(), assertionResult.getSourceInfo(), assertionResult.getResultType() );
-            builder << assertionResult.getMessage();
-            builder.m_info.message = builder.m_stream.str();
-
-            infoMessages.push_back( builder.m_info );
-        }
-    }
-
-     AssertionStats::~AssertionStats() = default;
-
-    SectionStats::SectionStats(  SectionInfo const& _sectionInfo,
-                                 Counts const& _assertions,
-                                 double _durationInSeconds,
-                                 bool _missingAssertions )
-    :   sectionInfo( _sectionInfo ),
-        assertions( _assertions ),
-        durationInSeconds( _durationInSeconds ),
-        missingAssertions( _missingAssertions )
-    {}
-
-    SectionStats::~SectionStats() = default;
-
-    TestCaseStats::TestCaseStats(  TestCaseInfo const& _testInfo,
-                                   Totals const& _totals,
-                                   std::string const& _stdOut,
-                                   std::string const& _stdErr,
-                                   bool _aborting )
-    : testInfo( _testInfo ),
-        totals( _totals ),
-        stdOut( _stdOut ),
-        stdErr( _stdErr ),
-        aborting( _aborting )
-    {}
-
-    TestCaseStats::~TestCaseStats() = default;
-
-    TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo,
-                                    Totals const& _totals,
-                                    bool _aborting )
-    :   groupInfo( _groupInfo ),
-        totals( _totals ),
-        aborting( _aborting )
-    {}
-
-    TestGroupStats::TestGroupStats( GroupInfo const& _groupInfo )
-    :   groupInfo( _groupInfo ),
-        aborting( false )
-    {}
-
-    TestGroupStats::~TestGroupStats() = default;
-
-    TestRunStats::TestRunStats(   TestRunInfo const& _runInfo,
-                    Totals const& _totals,
-                    bool _aborting )
-    :   runInfo( _runInfo ),
-        totals( _totals ),
-        aborting( _aborting )
-    {}
-
-    TestRunStats::~TestRunStats() = default;
-
-    void IStreamingReporter::fatalErrorEncountered( StringRef ) {}
-    bool IStreamingReporter::isMulti() const { return false; }
-
-    IReporterFactory::~IReporterFactory() = default;
-    IReporterRegistry::~IReporterRegistry() = default;
-
-} // end namespace Catch
-// end catch_interfaces_reporter.cpp
-// start catch_interfaces_runner.cpp
-
-namespace Catch {
-    IRunner::~IRunner() = default;
-}
-// end catch_interfaces_runner.cpp
-// start catch_interfaces_testcase.cpp
-
-namespace Catch {
-    ITestInvoker::~ITestInvoker() = default;
-    ITestCaseRegistry::~ITestCaseRegistry() = default;
-}
-// end catch_interfaces_testcase.cpp
-// start catch_leak_detector.cpp
-
-#ifdef CATCH_CONFIG_WINDOWS_CRTDBG
-#include <crtdbg.h>
-
-namespace Catch {
-
-    LeakDetector::LeakDetector() {
-        int flag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG);
-        flag |= _CRTDBG_LEAK_CHECK_DF;
-        flag |= _CRTDBG_ALLOC_MEM_DF;
-        _CrtSetDbgFlag(flag);
-        _CrtSetReportMode(_CRT_WARN, _CRTDBG_MODE_FILE | _CRTDBG_MODE_DEBUG);
-        _CrtSetReportFile(_CRT_WARN, _CRTDBG_FILE_STDERR);
-        // Change this to leaking allocation's number to break there
-        _CrtSetBreakAlloc(-1);
-    }
-}
-
-#else
-
-    Catch::LeakDetector::LeakDetector() {}
-
-#endif
-
-Catch::LeakDetector::~LeakDetector() {
-    Catch::cleanUp();
-}
-// end catch_leak_detector.cpp
-// start catch_list.cpp
-
-// start catch_list.h
-
-#include <set>
-
-namespace Catch {
-
-    std::size_t listTests( Config const& config );
-
-    std::size_t listTestsNamesOnly( Config const& config );
-
-    struct TagInfo {
-        void add( std::string const& spelling );
-        std::string all() const;
-
-        std::set<std::string> spellings;
-        std::size_t count = 0;
-    };
-
-    std::size_t listTags( Config const& config );
-
-    std::size_t listReporters();
-
-    Option<std::size_t> list( std::shared_ptr<Config> const& config );
-
-} // end namespace Catch
-
-// end catch_list.h
-// start catch_text.h
-
-namespace Catch {
-    using namespace clara::TextFlow;
-}
-
-// end catch_text.h
-#include <limits>
-#include <algorithm>
-#include <iomanip>
-
-namespace Catch {
-
-    std::size_t listTests( Config const& config ) {
-        TestSpec testSpec = config.testSpec();
-        if( config.hasTestFilters() )
-            Catch::cout() << "Matching test cases:\n";
-        else {
-            Catch::cout() << "All available test cases:\n";
-        }
-
-        auto matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
-        for( auto const& testCaseInfo : matchedTestCases ) {
-            Colour::Code colour = testCaseInfo.isHidden()
-                ? Colour::SecondaryText
-                : Colour::None;
-            Colour colourGuard( colour );
-
-            Catch::cout() << Column( testCaseInfo.name ).initialIndent( 2 ).indent( 4 ) << "\n";
-            if( config.verbosity() >= Verbosity::High ) {
-                Catch::cout() << Column( Catch::Detail::stringify( testCaseInfo.lineInfo ) ).indent(4) << std::endl;
-                std::string description = testCaseInfo.description;
-                if( description.empty() )
-                    description = "(NO DESCRIPTION)";
-                Catch::cout() << Column( description ).indent(4) << std::endl;
-            }
-            if( !testCaseInfo.tags.empty() )
-                Catch::cout() << Column( testCaseInfo.tagsAsString() ).indent( 6 ) << "\n";
-        }
-
-        if( !config.hasTestFilters() )
-            Catch::cout() << pluralise( matchedTestCases.size(), "test case" ) << '\n' << std::endl;
-        else
-            Catch::cout() << pluralise( matchedTestCases.size(), "matching test case" ) << '\n' << std::endl;
-        return matchedTestCases.size();
-    }
-
-    std::size_t listTestsNamesOnly( Config const& config ) {
-        TestSpec testSpec = config.testSpec();
-        std::size_t matchedTests = 0;
-        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
-        for( auto const& testCaseInfo : matchedTestCases ) {
-            matchedTests++;
-            if( startsWith( testCaseInfo.name, '#' ) )
-               Catch::cout() << '"' << testCaseInfo.name << '"';
-            else
-               Catch::cout() << testCaseInfo.name;
-            if ( config.verbosity() >= Verbosity::High )
-                Catch::cout() << "\t@" << testCaseInfo.lineInfo;
-            Catch::cout() << std::endl;
-        }
-        return matchedTests;
-    }
-
-    void TagInfo::add( std::string const& spelling ) {
-        ++count;
-        spellings.insert( spelling );
-    }
-
-    std::string TagInfo::all() const {
-        std::string out;
-        for( auto const& spelling : spellings )
-            out += "[" + spelling + "]";
-        return out;
-    }
-
-    std::size_t listTags( Config const& config ) {
-        TestSpec testSpec = config.testSpec();
-        if( config.hasTestFilters() )
-            Catch::cout() << "Tags for matching test cases:\n";
-        else {
-            Catch::cout() << "All available tags:\n";
-        }
-
-        std::map<std::string, TagInfo> tagCounts;
-
-        std::vector<TestCase> matchedTestCases = filterTests( getAllTestCasesSorted( config ), testSpec, config );
-        for( auto const& testCase : matchedTestCases ) {
-            for( auto const& tagName : testCase.getTestCaseInfo().tags ) {
-                std::string lcaseTagName = toLower( tagName );
-                auto countIt = tagCounts.find( lcaseTagName );
-                if( countIt == tagCounts.end() )
-                    countIt = tagCounts.insert( std::make_pair( lcaseTagName, TagInfo() ) ).first;
-                countIt->second.add( tagName );
-            }
-        }
-
-        for( auto const& tagCount : tagCounts ) {
-            ReusableStringStream rss;
-            rss << "  " << std::setw(2) << tagCount.second.count << "  ";
-            auto str = rss.str();
-            auto wrapper = Column( tagCount.second.all() )
-                                                    .initialIndent( 0 )
-                                                    .indent( str.size() )
-                                                    .width( CATCH_CONFIG_CONSOLE_WIDTH-10 );
-            Catch::cout() << str << wrapper << '\n';
-        }
-        Catch::cout() << pluralise( tagCounts.size(), "tag" ) << '\n' << std::endl;
-        return tagCounts.size();
-    }
-
-    std::size_t listReporters() {
-        Catch::cout() << "Available reporters:\n";
-        IReporterRegistry::FactoryMap const& factories = getRegistryHub().getReporterRegistry().getFactories();
-        std::size_t maxNameLen = 0;
-        for( auto const& factoryKvp : factories )
-            maxNameLen = (std::max)( maxNameLen, factoryKvp.first.size() );
-
-        for( auto const& factoryKvp : factories ) {
-            Catch::cout()
-                    << Column( factoryKvp.first + ":" )
-                            .indent(2)
-                            .width( 5+maxNameLen )
-                    +  Column( factoryKvp.second->getDescription() )
-                            .initialIndent(0)
-                            .indent(2)
-                            .width( CATCH_CONFIG_CONSOLE_WIDTH - maxNameLen-8 )
-                    << "\n";
-        }
-        Catch::cout() << std::endl;
-        return factories.size();
-    }
-
-    Option<std::size_t> list( std::shared_ptr<Config> const& config ) {
-        Option<std::size_t> listedCount;
-        getCurrentMutableContext().setConfig( config );
-        if( config->listTests() )
-            listedCount = listedCount.valueOr(0) + listTests( *config );
-        if( config->listTestNamesOnly() )
-            listedCount = listedCount.valueOr(0) + listTestsNamesOnly( *config );
-        if( config->listTags() )
-            listedCount = listedCount.valueOr(0) + listTags( *config );
-        if( config->listReporters() )
-            listedCount = listedCount.valueOr(0) + listReporters();
-        return listedCount;
-    }
-
-} // end namespace Catch
-// end catch_list.cpp
-// start catch_matchers.cpp
-
-namespace Catch {
-namespace Matchers {
-    namespace Impl {
-
-        std::string MatcherUntypedBase::toString() const {
-            if( m_cachedToString.empty() )
-                m_cachedToString = describe();
-            return m_cachedToString;
-        }
-
-        MatcherUntypedBase::~MatcherUntypedBase() = default;
-
-    } // namespace Impl
-} // namespace Matchers
-
-using namespace Matchers;
-using Matchers::Impl::MatcherBase;
-
-} // namespace Catch
-// end catch_matchers.cpp
-// start catch_matchers_floating.cpp
-
-// start catch_polyfills.hpp
-
-namespace Catch {
-    bool isnan(float f);
-    bool isnan(double d);
-}
-
-// end catch_polyfills.hpp
-// start catch_to_string.hpp
-
-#include <string>
-
-namespace Catch {
-    template <typename T>
-    std::string to_string(T const& t) {
-#if defined(CATCH_CONFIG_CPP11_TO_STRING)
-        return std::to_string(t);
-#else
-        ReusableStringStream rss;
-        rss << t;
-        return rss.str();
-#endif
-    }
-} // end namespace Catch
-
-// end catch_to_string.hpp
-#include <cstdlib>
-#include <cstdint>
-#include <cstring>
-
-namespace Catch {
-namespace Matchers {
-namespace Floating {
-enum class FloatingPointKind : uint8_t {
-    Float,
-    Double
-};
-}
-}
-}
-
-namespace {
-
-template <typename T>
-struct Converter;
-
-template <>
-struct Converter<float> {
-    static_assert(sizeof(float) == sizeof(int32_t), "Important ULP matcher assumption violated");
-    Converter(float f) {
-        std::memcpy(&i, &f, sizeof(f));
-    }
-    int32_t i;
-};
-
-template <>
-struct Converter<double> {
-    static_assert(sizeof(double) == sizeof(int64_t), "Important ULP matcher assumption violated");
-    Converter(double d) {
-        std::memcpy(&i, &d, sizeof(d));
-    }
-    int64_t i;
-};
-
-template <typename T>
-auto convert(T t) -> Converter<T> {
-    return Converter<T>(t);
-}
-
-template <typename FP>
-bool almostEqualUlps(FP lhs, FP rhs, int maxUlpDiff) {
-    // Comparison with NaN should always be false.
-    // This way we can rule it out before getting into the ugly details
-    if (Catch::isnan(lhs) || Catch::isnan(rhs)) {
-        return false;
-    }
-
-    auto lc = convert(lhs);
-    auto rc = convert(rhs);
-
-    if ((lc.i < 0) != (rc.i < 0)) {
-        // Potentially we can have +0 and -0
-        return lhs == rhs;
-    }
-
-    auto ulpDiff = std::abs(lc.i - rc.i);
-    return ulpDiff <= maxUlpDiff;
-}
-
-}
-
-namespace Catch {
-namespace Matchers {
-namespace Floating {
-    WithinAbsMatcher::WithinAbsMatcher(double target, double margin)
-        :m_target{ target }, m_margin{ margin } {
-        CATCH_ENFORCE(margin >= 0, "Invalid margin: " << margin << '.'
-            << " Margin has to be non-negative.");
-    }
-
-    // Performs equivalent check of std::fabs(lhs - rhs) <= margin
-    // But without the subtraction to allow for INFINITY in comparison
-    bool WithinAbsMatcher::match(double const& matchee) const {
-        return (matchee + m_margin >= m_target) && (m_target + m_margin >= matchee);
-    }
-
-    std::string WithinAbsMatcher::describe() const {
-        return "is within " + ::Catch::Detail::stringify(m_margin) + " of " + ::Catch::Detail::stringify(m_target);
-    }
-
-    WithinUlpsMatcher::WithinUlpsMatcher(double target, int ulps, FloatingPointKind baseType)
-        :m_target{ target }, m_ulps{ ulps }, m_type{ baseType } {
-        CATCH_ENFORCE(ulps >= 0, "Invalid ULP setting: " << ulps << '.'
-            << " ULPs have to be non-negative.");
-    }
-
-#if defined(__clang__)
-#pragma clang diagnostic push
-// Clang <3.5 reports on the default branch in the switch below
-#pragma clang diagnostic ignored "-Wunreachable-code"
-#endif
-
-    bool WithinUlpsMatcher::match(double const& matchee) const {
-        switch (m_type) {
-        case FloatingPointKind::Float:
-            return almostEqualUlps<float>(static_cast<float>(matchee), static_cast<float>(m_target), m_ulps);
-        case FloatingPointKind::Double:
-            return almostEqualUlps<double>(matchee, m_target, m_ulps);
-        default:
-            CATCH_INTERNAL_ERROR( "Unknown FloatingPointKind value" );
-        }
-    }
-
-#if defined(__clang__)
-#pragma clang diagnostic pop
-#endif
-
-    std::string WithinUlpsMatcher::describe() const {
-        return "is within " + Catch::to_string(m_ulps) + " ULPs of " + ::Catch::Detail::stringify(m_target) + ((m_type == FloatingPointKind::Float)? "f" : "");
-    }
-
-}// namespace Floating
-
-Floating::WithinUlpsMatcher WithinULP(double target, int maxUlpDiff) {
-    return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Double);
-}
-
-Floating::WithinUlpsMatcher WithinULP(float target, int maxUlpDiff) {
-    return Floating::WithinUlpsMatcher(target, maxUlpDiff, Floating::FloatingPointKind::Float);
-}
-
-Floating::WithinAbsMatcher WithinAbs(double target, double margin) {
-    return Floating::WithinAbsMatcher(target, margin);
-}
-
-} // namespace Matchers
-} // namespace Catch
-
-// end catch_matchers_floating.cpp
-// start catch_matchers_generic.cpp
-
-std::string Catch::Matchers::Generic::Detail::finalizeDescription(const std::string& desc) {
-    if (desc.empty()) {
-        return "matches undescribed predicate";
-    } else {
-        return "matches predicate: \"" + desc + '"';
-    }
-}
-// end catch_matchers_generic.cpp
-// start catch_matchers_string.cpp
-
-#include <regex>
-
-namespace Catch {
-namespace Matchers {
-
-    namespace StdString {
-
-        CasedString::CasedString( std::string const& str, CaseSensitive::Choice caseSensitivity )
-        :   m_caseSensitivity( caseSensitivity ),
-            m_str( adjustString( str ) )
-        {}
-        std::string CasedString::adjustString( std::string const& str ) const {
-            return m_caseSensitivity == CaseSensitive::No
-                   ? toLower( str )
-                   : str;
-        }
-        std::string CasedString::caseSensitivitySuffix() const {
-            return m_caseSensitivity == CaseSensitive::No
-                   ? " (case insensitive)"
-                   : std::string();
-        }
-
-        StringMatcherBase::StringMatcherBase( std::string const& operation, CasedString const& comparator )
-        : m_comparator( comparator ),
-          m_operation( operation ) {
-        }
-
-        std::string StringMatcherBase::describe() const {
-            std::string description;
-            description.reserve(5 + m_operation.size() + m_comparator.m_str.size() +
-                                        m_comparator.caseSensitivitySuffix().size());
-            description += m_operation;
-            description += ": \"";
-            description += m_comparator.m_str;
-            description += "\"";
-            description += m_comparator.caseSensitivitySuffix();
-            return description;
-        }
-
-        EqualsMatcher::EqualsMatcher( CasedString const& comparator ) : StringMatcherBase( "equals", comparator ) {}
-
-        bool EqualsMatcher::match( std::string const& source ) const {
-            return m_comparator.adjustString( source ) == m_comparator.m_str;
-        }
-
-        ContainsMatcher::ContainsMatcher( CasedString const& comparator ) : StringMatcherBase( "contains", comparator ) {}
-
-        bool ContainsMatcher::match( std::string const& source ) const {
-            return contains( m_comparator.adjustString( source ), m_comparator.m_str );
-        }
-
-        StartsWithMatcher::StartsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "starts with", comparator ) {}
-
-        bool StartsWithMatcher::match( std::string const& source ) const {
-            return startsWith( m_comparator.adjustString( source ), m_comparator.m_str );
-        }
-
-        EndsWithMatcher::EndsWithMatcher( CasedString const& comparator ) : StringMatcherBase( "ends with", comparator ) {}
-
-        bool EndsWithMatcher::match( std::string const& source ) const {
-            return endsWith( m_comparator.adjustString( source ), m_comparator.m_str );
-        }
-
-        RegexMatcher::RegexMatcher(std::string regex, CaseSensitive::Choice caseSensitivity): m_regex(std::move(regex)), m_caseSensitivity(caseSensitivity) {}
-
-        bool RegexMatcher::match(std::string const& matchee) const {
-            auto flags = std::regex::ECMAScript; // ECMAScript is the default syntax option anyway
-            if (m_caseSensitivity == CaseSensitive::Choice::No) {
-                flags |= std::regex::icase;
-            }
-            auto reg = std::regex(m_regex, flags);
-            return std::regex_match(matchee, reg);
-        }
-
-        std::string RegexMatcher::describe() const {
-            return "matches " + ::Catch::Detail::stringify(m_regex) + ((m_caseSensitivity == CaseSensitive::Choice::Yes)? " case sensitively" : " case insensitively");
-        }
-
-    } // namespace StdString
-
-    StdString::EqualsMatcher Equals( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
-        return StdString::EqualsMatcher( StdString::CasedString( str, caseSensitivity) );
-    }
-    StdString::ContainsMatcher Contains( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
-        return StdString::ContainsMatcher( StdString::CasedString( str, caseSensitivity) );
-    }
-    StdString::EndsWithMatcher EndsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
-        return StdString::EndsWithMatcher( StdString::CasedString( str, caseSensitivity) );
-    }
-    StdString::StartsWithMatcher StartsWith( std::string const& str, CaseSensitive::Choice caseSensitivity ) {
-        return StdString::StartsWithMatcher( StdString::CasedString( str, caseSensitivity) );
-    }
-
-    StdString::RegexMatcher Matches(std::string const& regex, CaseSensitive::Choice caseSensitivity) {
-        return StdString::RegexMatcher(regex, caseSensitivity);
-    }
-
-} // namespace Matchers
-} // namespace Catch
-// end catch_matchers_string.cpp
-// start catch_message.cpp
-
-// start catch_uncaught_exceptions.h
-
-namespace Catch {
-    bool uncaught_exceptions();
-} // end namespace Catch
-
-// end catch_uncaught_exceptions.h
-#include <cassert>
-#include <stack>
-
-namespace Catch {
-
-    MessageInfo::MessageInfo(   StringRef const& _macroName,
-                                SourceLineInfo const& _lineInfo,
-                                ResultWas::OfType _type )
-    :   macroName( _macroName ),
-        lineInfo( _lineInfo ),
-        type( _type ),
-        sequence( ++globalCount )
-    {}
-
-    bool MessageInfo::operator==( MessageInfo const& other ) const {
-        return sequence == other.sequence;
-    }
-
-    bool MessageInfo::operator<( MessageInfo const& other ) const {
-        return sequence < other.sequence;
-    }
-
-    // This may need protecting if threading support is added
-    unsigned int MessageInfo::globalCount = 0;
-
-    ////////////////////////////////////////////////////////////////////////////
-
-    Catch::MessageBuilder::MessageBuilder( StringRef const& macroName,
-                                           SourceLineInfo const& lineInfo,
-                                           ResultWas::OfType type )
-        :m_info(macroName, lineInfo, type) {}
-
-    ////////////////////////////////////////////////////////////////////////////
-
-    ScopedMessage::ScopedMessage( MessageBuilder const& builder )
-    : m_info( builder.m_info ), m_moved()
-    {
-        m_info.message = builder.m_stream.str();
-        getResultCapture().pushScopedMessage( m_info );
-    }
-
-    ScopedMessage::ScopedMessage( ScopedMessage&& old )
-    : m_info( old.m_info ), m_moved()
-    {
-        old.m_moved = true;
-    }
-
-    ScopedMessage::~ScopedMessage() {
-        if ( !uncaught_exceptions() && !m_moved ){
-            getResultCapture().popScopedMessage(m_info);
-        }
-    }
-
-    Capturer::Capturer( StringRef macroName, SourceLineInfo const& lineInfo, ResultWas::OfType resultType, StringRef names ) {
-        auto trimmed = [&] (size_t start, size_t end) {
-            while (names[start] == ',' || isspace(names[start])) {
-                ++start;
-            }
-            while (names[end] == ',' || isspace(names[end])) {
-                --end;
-            }
-            return names.substr(start, end - start + 1);
-        };
-        auto skipq = [&] (size_t start, char quote) {
-            for (auto i = start + 1; i < names.size() ; ++i) {
-                if (names[i] == quote)
-                    return i;
-                if (names[i] == '\\')
-                    ++i;
-            }
-            CATCH_INTERNAL_ERROR("CAPTURE parsing encountered unmatched quote");
-        };
-
-        size_t start = 0;
-        std::stack<char> openings;
-        for (size_t pos = 0; pos < names.size(); ++pos) {
-            char c = names[pos];
-            switch (c) {
-            case '[':
-            case '{':
-            case '(':
-            // It is basically impossible to disambiguate between
-            // comparison and start of template args in this context
-//            case '<':
-                openings.push(c);
-                break;
-            case ']':
-            case '}':
-            case ')':
-//           case '>':
-                openings.pop();
-                break;
-            case '"':
-            case '\'':
-                pos = skipq(pos, c);
-                break;
-            case ',':
-                if (start != pos && openings.size() == 0) {
-                    m_messages.emplace_back(macroName, lineInfo, resultType);
-                    m_messages.back().message = trimmed(start, pos);
-                    m_messages.back().message += " := ";
-                    start = pos;
-                }
-            }
-        }
-        assert(openings.size() == 0 && "Mismatched openings");
-        m_messages.emplace_back(macroName, lineInfo, resultType);
-        m_messages.back().message = trimmed(start, names.size() - 1);
-        m_messages.back().message += " := ";
-    }
-    Capturer::~Capturer() {
-        if ( !uncaught_exceptions() ){
-            assert( m_captured == m_messages.size() );
-            for( size_t i = 0; i < m_captured; ++i  )
-                m_resultCapture.popScopedMessage( m_messages[i] );
-        }
-    }
-
-    void Capturer::captureValue( size_t index, std::string const& value ) {
-        assert( index < m_messages.size() );
-        m_messages[index].message += value;
-        m_resultCapture.pushScopedMessage( m_messages[index] );
-        m_captured++;
-    }
-
-} // end namespace Catch
-// end catch_message.cpp
-// start catch_output_redirect.cpp
-
-// start catch_output_redirect.h
-#ifndef TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
-#define TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
-
-#include <cstdio>
-#include <iosfwd>
-#include <string>
-
-namespace Catch {
-
-    class RedirectedStream {
-        std::ostream& m_originalStream;
-        std::ostream& m_redirectionStream;
-        std::streambuf* m_prevBuf;
-
-    public:
-        RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream );
-        ~RedirectedStream();
-    };
-
-    class RedirectedStdOut {
-        ReusableStringStream m_rss;
-        RedirectedStream m_cout;
-    public:
-        RedirectedStdOut();
-        auto str() const -> std::string;
-    };
-
-    // StdErr has two constituent streams in C++, std::cerr and std::clog
-    // This means that we need to redirect 2 streams into 1 to keep proper
-    // order of writes
-    class RedirectedStdErr {
-        ReusableStringStream m_rss;
-        RedirectedStream m_cerr;
-        RedirectedStream m_clog;
-    public:
-        RedirectedStdErr();
-        auto str() const -> std::string;
-    };
-
-    class RedirectedStreams {
-    public:
-        RedirectedStreams(RedirectedStreams const&) = delete;
-        RedirectedStreams& operator=(RedirectedStreams const&) = delete;
-        RedirectedStreams(RedirectedStreams&&) = delete;
-        RedirectedStreams& operator=(RedirectedStreams&&) = delete;
-
-        RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr);
-        ~RedirectedStreams();
-    private:
-        std::string& m_redirectedCout;
-        std::string& m_redirectedCerr;
-        RedirectedStdOut m_redirectedStdOut;
-        RedirectedStdErr m_redirectedStdErr;
-    };
-
-#if defined(CATCH_CONFIG_NEW_CAPTURE)
-
-    // Windows's implementation of std::tmpfile is terrible (it tries
-    // to create a file inside system folder, thus requiring elevated
-    // privileges for the binary), so we have to use tmpnam(_s) and
-    // create the file ourselves there.
-    class TempFile {
-    public:
-        TempFile(TempFile const&) = delete;
-        TempFile& operator=(TempFile const&) = delete;
-        TempFile(TempFile&&) = delete;
-        TempFile& operator=(TempFile&&) = delete;
-
-        TempFile();
-        ~TempFile();
-
-        std::FILE* getFile();
-        std::string getContents();
-
-    private:
-        std::FILE* m_file = nullptr;
-    #if defined(_MSC_VER)
-        char m_buffer[L_tmpnam] = { 0 };
-    #endif
-    };
-
-    class OutputRedirect {
-    public:
-        OutputRedirect(OutputRedirect const&) = delete;
-        OutputRedirect& operator=(OutputRedirect const&) = delete;
-        OutputRedirect(OutputRedirect&&) = delete;
-        OutputRedirect& operator=(OutputRedirect&&) = delete;
-
-        OutputRedirect(std::string& stdout_dest, std::string& stderr_dest);
-        ~OutputRedirect();
-
-    private:
-        int m_originalStdout = -1;
-        int m_originalStderr = -1;
-        TempFile m_stdoutFile;
-        TempFile m_stderrFile;
-        std::string& m_stdoutDest;
-        std::string& m_stderrDest;
-    };
-
-#endif
-
-} // end namespace Catch
-
-#endif // TWOBLUECUBES_CATCH_OUTPUT_REDIRECT_H
-// end catch_output_redirect.h
-#include <cstdio>
-#include <cstring>
-#include <fstream>
-#include <sstream>
-#include <stdexcept>
-
-#if defined(CATCH_CONFIG_NEW_CAPTURE)
-    #if defined(_MSC_VER)
-    #include <io.h>      //_dup and _dup2
-    #define dup _dup
-    #define dup2 _dup2
-    #define fileno _fileno
-    #else
-    #include <unistd.h>  // dup and dup2
-    #endif
-#endif
-
-namespace Catch {
-
-    RedirectedStream::RedirectedStream( std::ostream& originalStream, std::ostream& redirectionStream )
-    :   m_originalStream( originalStream ),
-        m_redirectionStream( redirectionStream ),
-        m_prevBuf( m_originalStream.rdbuf() )
-    {
-        m_originalStream.rdbuf( m_redirectionStream.rdbuf() );
-    }
-
-    RedirectedStream::~RedirectedStream() {
-        m_originalStream.rdbuf( m_prevBuf );
-    }
-
-    RedirectedStdOut::RedirectedStdOut() : m_cout( Catch::cout(), m_rss.get() ) {}
-    auto RedirectedStdOut::str() const -> std::string { return m_rss.str(); }
-
-    RedirectedStdErr::RedirectedStdErr()
-    :   m_cerr( Catch::cerr(), m_rss.get() ),
-        m_clog( Catch::clog(), m_rss.get() )
-    {}
-    auto RedirectedStdErr::str() const -> std::string { return m_rss.str(); }
-
-    RedirectedStreams::RedirectedStreams(std::string& redirectedCout, std::string& redirectedCerr)
-    :   m_redirectedCout(redirectedCout),
-        m_redirectedCerr(redirectedCerr)
-    {}
-
-    RedirectedStreams::~RedirectedStreams() {
-        m_redirectedCout += m_redirectedStdOut.str();
-        m_redirectedCerr += m_redirectedStdErr.str();
-    }
-
-#if defined(CATCH_CONFIG_NEW_CAPTURE)
-
-#if defined(_MSC_VER)
-    TempFile::TempFile() {
-        if (tmpnam_s(m_buffer)) {
-            CATCH_RUNTIME_ERROR("Could not get a temp filename");
-        }
-        if (fopen_s(&m_file, m_buffer, "w")) {
-            char buffer[100];
-            if (strerror_s(buffer, errno)) {
-                CATCH_RUNTIME_ERROR("Could not translate errno to a string");
-            }
-            CATCH_RUNTIME_ERROR("Could not open the temp file: '" << m_buffer << "' because: " << buffer);
-        }
-    }
-#else
-    TempFile::TempFile() {
-        m_file = std::tmpfile();
-        if (!m_file) {
-            CATCH_RUNTIME_ERROR("Could not create a temp file.");
-        }
-    }
-
-#endif
-
-    TempFile::~TempFile() {
-         // TBD: What to do about errors here?
-         std::fclose(m_file);
-         // We manually create the file on Windows only, on Linux
-         // it will be autodeleted
-#if defined(_MSC_VER)
-         std::remove(m_buffer);
-#endif
-    }
-
-    FILE* TempFile::getFile() {
-        return m_file;
-    }
-
-    std::string TempFile::getContents() {
-        std::stringstream sstr;
-        char buffer[100] = {};
-        std::rewind(m_file);
-        while (std::fgets(buffer, sizeof(buffer), m_file)) {
-            sstr << buffer;
-        }
-        return sstr.str();
-    }
-
-    OutputRedirect::OutputRedirect(std::string& stdout_dest, std::string& stderr_dest) :
-        m_originalStdout(dup(1)),
-        m_originalStderr(dup(2)),
-        m_stdoutDest(stdout_dest),
-        m_stderrDest(stderr_dest) {
-        dup2(fileno(m_stdoutFile.getFile()), 1);
-        dup2(fileno(m_stderrFile.getFile()), 2);
-    }
-
-    OutputRedirect::~OutputRedirect() {
-        Catch::cout() << std::flush;
-        fflush(stdout);
-        // Since we support overriding these streams, we flush cerr
-        // even though std::cerr is unbuffered
-        Catch::cerr() << std::flush;
-        Catch::clog() << std::flush;
-        fflush(stderr);
-
-        dup2(m_originalStdout, 1);
-        dup2(m_originalStderr, 2);
-
-        m_stdoutDest += m_stdoutFile.getContents();
-        m_stderrDest += m_stderrFile.getContents();
-    }
-
-#endif // CATCH_CONFIG_NEW_CAPTURE
-
-} // namespace Catch
-
-#if defined(CATCH_CONFIG_NEW_CAPTURE)
-    #if defined(_MSC_VER)
-    #undef dup
-    #undef dup2
-    #undef fileno
-    #endif
-#endif
-// end catch_output_redirect.cpp
-// start catch_polyfills.cpp
-
-#include <cmath>
-
-namespace Catch {
-
-#if !defined(CATCH_CONFIG_POLYFILL_ISNAN)
-    bool isnan(float f) {
-        return std::isnan(f);
-    }
-    bool isnan(double d) {
-        return std::isnan(d);
-    }
-#else
-    // For now we only use this for embarcadero
-    bool isnan(float f) {
-        return std::_isnan(f);
-    }
-    bool isnan(double d) {
-        return std::_isnan(d);
-    }
-#endif
-
-} // end namespace Catch
-// end catch_polyfills.cpp
-// start catch_random_number_generator.cpp
-
-namespace Catch {
-
-    std::mt19937& rng() {
-        static std::mt19937 s_rng;
-        return s_rng;
-    }
-
-    void seedRng( IConfig const& config ) {
-        if( config.rngSeed() != 0 ) {
-            std::srand( config.rngSeed() );
-            rng().seed( config.rngSeed() );
-        }
-    }
-
-    unsigned int rngSeed() {
-        return getCurrentContext().getConfig()->rngSeed();
-    }
-}
-// end catch_random_number_generator.cpp
-// start catch_registry_hub.cpp
-
-// start catch_test_case_registry_impl.h
-
-#include <vector>
-#include <set>
-#include <algorithm>
-#include <ios>
-
-namespace Catch {
-
-    class TestCase;
-    struct IConfig;
-
-    std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases );
-    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config );
-
-    void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions );
-
-    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config );
-    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config );
-
-    class TestRegistry : public ITestCaseRegistry {
-    public:
-        virtual ~TestRegistry() = default;
-
-        virtual void registerTest( TestCase const& testCase );
-
-        std::vector<TestCase> const& getAllTests() const override;
-        std::vector<TestCase> const& getAllTestsSorted( IConfig const& config ) const override;
-
-    private:
-        std::vector<TestCase> m_functions;
-        mutable RunTests::InWhatOrder m_currentSortOrder = RunTests::InDeclarationOrder;
-        mutable std::vector<TestCase> m_sortedFunctions;
-        std::size_t m_unnamedCount = 0;
-        std::ios_base::Init m_ostreamInit; // Forces cout/ cerr to be initialised
-    };
-
-    ///////////////////////////////////////////////////////////////////////////
-
-    class TestInvokerAsFunction : public ITestInvoker {
-        void(*m_testAsFunction)();
-    public:
-        TestInvokerAsFunction( void(*testAsFunction)() ) noexcept;
-
-        void invoke() const override;
-    };
-
-    std::string extractClassName( StringRef const& classOrQualifiedMethodName );
-
-    ///////////////////////////////////////////////////////////////////////////
-
-} // end namespace Catch
-
-// end catch_test_case_registry_impl.h
-// start catch_reporter_registry.h
-
-#include <map>
-
-namespace Catch {
-
-    class ReporterRegistry : public IReporterRegistry {
-
-    public:
-
-        ~ReporterRegistry() override;
-
-        IStreamingReporterPtr create( std::string const& name, IConfigPtr const& config ) const override;
-
-        void registerReporter( std::string const& name, IReporterFactoryPtr const& factory );
-        void registerListener( IReporterFactoryPtr const& factory );
-
-        FactoryMap const& getFactories() const override;
-        Listeners const& getListeners() const override;
-
-    private:
-        FactoryMap m_factories;
-        Listeners m_listeners;
-    };
-}
-
-// end catch_reporter_registry.h
-// start catch_tag_alias_registry.h
-
-// start catch_tag_alias.h
-
-#include <string>
-
-namespace Catch {
-
-    struct TagAlias {
-        TagAlias(std::string const& _tag, SourceLineInfo _lineInfo);
-
-        std::string tag;
-        SourceLineInfo lineInfo;
-    };
-
-} // end namespace Catch
-
-// end catch_tag_alias.h
-#include <map>
-
-namespace Catch {
-
-    class TagAliasRegistry : public ITagAliasRegistry {
-    public:
-        ~TagAliasRegistry() override;
-        TagAlias const* find( std::string const& alias ) const override;
-        std::string expandAliases( std::string const& unexpandedTestSpec ) const override;
-        void add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo );
-
-    private:
-        std::map<std::string, TagAlias> m_registry;
-    };
-
-} // end namespace Catch
-
-// end catch_tag_alias_registry.h
-// start catch_startup_exception_registry.h
-
-#include <vector>
-#include <exception>
-
-namespace Catch {
-
-    class StartupExceptionRegistry {
-    public:
-        void add(std::exception_ptr const& exception) noexcept;
-        std::vector<std::exception_ptr> const& getExceptions() const noexcept;
-    private:
-        std::vector<std::exception_ptr> m_exceptions;
-    };
-
-} // end namespace Catch
-
-// end catch_startup_exception_registry.h
-// start catch_singletons.hpp
-
-namespace Catch {
-
-    struct ISingleton {
-        virtual ~ISingleton();
-    };
-
-    void addSingleton( ISingleton* singleton );
-    void cleanupSingletons();
-
-    template<typename SingletonImplT, typename InterfaceT = SingletonImplT, typename MutableInterfaceT = InterfaceT>
-    class Singleton : SingletonImplT, public ISingleton {
-
-        static auto getInternal() -> Singleton* {
-            static Singleton* s_instance = nullptr;
-            if( !s_instance ) {
-                s_instance = new Singleton;
-                addSingleton( s_instance );
-            }
-            return s_instance;
-        }
-
-    public:
-        static auto get() -> InterfaceT const& {
-            return *getInternal();
-        }
-        static auto getMutable() -> MutableInterfaceT& {
-            return *getInternal();
-        }
-    };
-
-} // namespace Catch
-
-// end catch_singletons.hpp
-namespace Catch {
-
-    namespace {
-
-        class RegistryHub : public IRegistryHub, public IMutableRegistryHub,
-                            private NonCopyable {
-
-        public: // IRegistryHub
-            RegistryHub() = default;
-            IReporterRegistry const& getReporterRegistry() const override {
-                return m_reporterRegistry;
-            }
-            ITestCaseRegistry const& getTestCaseRegistry() const override {
-                return m_testCaseRegistry;
-            }
-            IExceptionTranslatorRegistry const& getExceptionTranslatorRegistry() const override {
-                return m_exceptionTranslatorRegistry;
-            }
-            ITagAliasRegistry const& getTagAliasRegistry() const override {
-                return m_tagAliasRegistry;
-            }
-            StartupExceptionRegistry const& getStartupExceptionRegistry() const override {
-                return m_exceptionRegistry;
-            }
-
-        public: // IMutableRegistryHub
-            void registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) override {
-                m_reporterRegistry.registerReporter( name, factory );
-            }
-            void registerListener( IReporterFactoryPtr const& factory ) override {
-                m_reporterRegistry.registerListener( factory );
-            }
-            void registerTest( TestCase const& testInfo ) override {
-                m_testCaseRegistry.registerTest( testInfo );
-            }
-            void registerTranslator( const IExceptionTranslator* translator ) override {
-                m_exceptionTranslatorRegistry.registerTranslator( translator );
-            }
-            void registerTagAlias( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) override {
-                m_tagAliasRegistry.add( alias, tag, lineInfo );
-            }
-            void registerStartupException() noexcept override {
-                m_exceptionRegistry.add(std::current_exception());
-            }
-            IMutableEnumValuesRegistry& getMutableEnumValuesRegistry() override {
-                return m_enumValuesRegistry;
-            }
-
-        private:
-            TestRegistry m_testCaseRegistry;
-            ReporterRegistry m_reporterRegistry;
-            ExceptionTranslatorRegistry m_exceptionTranslatorRegistry;
-            TagAliasRegistry m_tagAliasRegistry;
-            StartupExceptionRegistry m_exceptionRegistry;
-            Detail::EnumValuesRegistry m_enumValuesRegistry;
-        };
-    }
-
-    using RegistryHubSingleton = Singleton<RegistryHub, IRegistryHub, IMutableRegistryHub>;
-
-    IRegistryHub const& getRegistryHub() {
-        return RegistryHubSingleton::get();
-    }
-    IMutableRegistryHub& getMutableRegistryHub() {
-        return RegistryHubSingleton::getMutable();
-    }
-    void cleanUp() {
-        cleanupSingletons();
-        cleanUpContext();
-    }
-    std::string translateActiveException() {
-        return getRegistryHub().getExceptionTranslatorRegistry().translateActiveException();
-    }
-
-} // end namespace Catch
-// end catch_registry_hub.cpp
-// start catch_reporter_registry.cpp
-
-namespace Catch {
-
-    ReporterRegistry::~ReporterRegistry() = default;
-
-    IStreamingReporterPtr ReporterRegistry::create( std::string const& name, IConfigPtr const& config ) const {
-        auto it =  m_factories.find( name );
-        if( it == m_factories.end() )
-            return nullptr;
-        return it->second->create( ReporterConfig( config ) );
-    }
-
-    void ReporterRegistry::registerReporter( std::string const& name, IReporterFactoryPtr const& factory ) {
-        m_factories.emplace(name, factory);
-    }
-    void ReporterRegistry::registerListener( IReporterFactoryPtr const& factory ) {
-        m_listeners.push_back( factory );
-    }
-
-    IReporterRegistry::FactoryMap const& ReporterRegistry::getFactories() const {
-        return m_factories;
-    }
-    IReporterRegistry::Listeners const& ReporterRegistry::getListeners() const {
-        return m_listeners;
-    }
-
-}
-// end catch_reporter_registry.cpp
-// start catch_result_type.cpp
-
-namespace Catch {
-
-    bool isOk( ResultWas::OfType resultType ) {
-        return ( resultType & ResultWas::FailureBit ) == 0;
-    }
-    bool isJustInfo( int flags ) {
-        return flags == ResultWas::Info;
-    }
-
-    ResultDisposition::Flags operator | ( ResultDisposition::Flags lhs, ResultDisposition::Flags rhs ) {
-        return static_cast<ResultDisposition::Flags>( static_cast<int>( lhs ) | static_cast<int>( rhs ) );
-    }
-
-    bool shouldContinueOnFailure( int flags )    { return ( flags & ResultDisposition::ContinueOnFailure ) != 0; }
-    bool shouldSuppressFailure( int flags )      { return ( flags & ResultDisposition::SuppressFail ) != 0; }
-
-} // end namespace Catch
-// end catch_result_type.cpp
-// start catch_run_context.cpp
-
-#include <cassert>
-#include <algorithm>
-#include <sstream>
-
-namespace Catch {
-
-    namespace Generators {
-        struct GeneratorTracker : TestCaseTracking::TrackerBase, IGeneratorTracker {
-            GeneratorBasePtr m_generator;
-
-            GeneratorTracker( TestCaseTracking::NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
-            :   TrackerBase( nameAndLocation, ctx, parent )
-            {}
-            ~GeneratorTracker();
-
-            static GeneratorTracker& acquire( TrackerContext& ctx, TestCaseTracking::NameAndLocation const& nameAndLocation ) {
-                std::shared_ptr<GeneratorTracker> tracker;
-
-                ITracker& currentTracker = ctx.currentTracker();
-                if( TestCaseTracking::ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
-                    assert( childTracker );
-                    assert( childTracker->isGeneratorTracker() );
-                    tracker = std::static_pointer_cast<GeneratorTracker>( childTracker );
-                }
-                else {
-                    tracker = std::make_shared<GeneratorTracker>( nameAndLocation, ctx, &currentTracker );
-                    currentTracker.addChild( tracker );
-                }
-
-                if( !ctx.completedCycle() && !tracker->isComplete() ) {
-                    tracker->open();
-                }
-
-                return *tracker;
-            }
-
-            // TrackerBase interface
-            bool isGeneratorTracker() const override { return true; }
-            auto hasGenerator() const -> bool override {
-                return !!m_generator;
-            }
-            void close() override {
-                TrackerBase::close();
-                // Generator interface only finds out if it has another item on atual move
-                if (m_runState == CompletedSuccessfully && m_generator->next()) {
-                    m_children.clear();
-                    m_runState = Executing;
-                }
-            }
-
-            // IGeneratorTracker interface
-            auto getGenerator() const -> GeneratorBasePtr const& override {
-                return m_generator;
-            }
-            void setGenerator( GeneratorBasePtr&& generator ) override {
-                m_generator = std::move( generator );
-            }
-        };
-        GeneratorTracker::~GeneratorTracker() {}
-    }
-
-    RunContext::RunContext(IConfigPtr const& _config, IStreamingReporterPtr&& reporter)
-    :   m_runInfo(_config->name()),
-        m_context(getCurrentMutableContext()),
-        m_config(_config),
-        m_reporter(std::move(reporter)),
-        m_lastAssertionInfo{ StringRef(), SourceLineInfo("",0), StringRef(), ResultDisposition::Normal },
-        m_includeSuccessfulResults( m_config->includeSuccessfulResults() || m_reporter->getPreferences().shouldReportAllAssertions )
-    {
-        m_context.setRunner(this);
-        m_context.setConfig(m_config);
-        m_context.setResultCapture(this);
-        m_reporter->testRunStarting(m_runInfo);
-    }
-
-    RunContext::~RunContext() {
-        m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, aborting()));
-    }
-
-    void RunContext::testGroupStarting(std::string const& testSpec, std::size_t groupIndex, std::size_t groupsCount) {
-        m_reporter->testGroupStarting(GroupInfo(testSpec, groupIndex, groupsCount));
-    }
-
-    void RunContext::testGroupEnded(std::string const& testSpec, Totals const& totals, std::size_t groupIndex, std::size_t groupsCount) {
-        m_reporter->testGroupEnded(TestGroupStats(GroupInfo(testSpec, groupIndex, groupsCount), totals, aborting()));
-    }
-
-    Totals RunContext::runTest(TestCase const& testCase) {
-        Totals prevTotals = m_totals;
-
-        std::string redirectedCout;
-        std::string redirectedCerr;
-
-        auto const& testInfo = testCase.getTestCaseInfo();
-
-        m_reporter->testCaseStarting(testInfo);
-
-        m_activeTestCase = &testCase;
-
-        ITracker& rootTracker = m_trackerContext.startRun();
-        assert(rootTracker.isSectionTracker());
-        static_cast<SectionTracker&>(rootTracker).addInitialFilters(m_config->getSectionsToRun());
-        do {
-            m_trackerContext.startCycle();
-            m_testCaseTracker = &SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(testInfo.name, testInfo.lineInfo));
-            runCurrentTest(redirectedCout, redirectedCerr);
-        } while (!m_testCaseTracker->isSuccessfullyCompleted() && !aborting());
-
-        Totals deltaTotals = m_totals.delta(prevTotals);
-        if (testInfo.expectedToFail() && deltaTotals.testCases.passed > 0) {
-            deltaTotals.assertions.failed++;
-            deltaTotals.testCases.passed--;
-            deltaTotals.testCases.failed++;
-        }
-        m_totals.testCases += deltaTotals.testCases;
-        m_reporter->testCaseEnded(TestCaseStats(testInfo,
-                                  deltaTotals,
-                                  redirectedCout,
-                                  redirectedCerr,
-                                  aborting()));
-
-        m_activeTestCase = nullptr;
-        m_testCaseTracker = nullptr;
-
-        return deltaTotals;
-    }
-
-    IConfigPtr RunContext::config() const {
-        return m_config;
-    }
-
-    IStreamingReporter& RunContext::reporter() const {
-        return *m_reporter;
-    }
-
-    void RunContext::assertionEnded(AssertionResult const & result) {
-        if (result.getResultType() == ResultWas::Ok) {
-            m_totals.assertions.passed++;
-            m_lastAssertionPassed = true;
-        } else if (!result.isOk()) {
-            m_lastAssertionPassed = false;
-            if( m_activeTestCase->getTestCaseInfo().okToFail() )
-                m_totals.assertions.failedButOk++;
-            else
-                m_totals.assertions.failed++;
-        }
-        else {
-            m_lastAssertionPassed = true;
-        }
-
-        // We have no use for the return value (whether messages should be cleared), because messages were made scoped
-        // and should be let to clear themselves out.
-        static_cast<void>(m_reporter->assertionEnded(AssertionStats(result, m_messages, m_totals)));
-
-        if (result.getResultType() != ResultWas::Warning)
-            m_messageScopes.clear();
-
-        // Reset working state
-        resetAssertionInfo();
-        m_lastResult = result;
-    }
-    void RunContext::resetAssertionInfo() {
-        m_lastAssertionInfo.macroName = StringRef();
-        m_lastAssertionInfo.capturedExpression = "{Unknown expression after the reported line}"_sr;
-    }
-
-    bool RunContext::sectionStarted(SectionInfo const & sectionInfo, Counts & assertions) {
-        ITracker& sectionTracker = SectionTracker::acquire(m_trackerContext, TestCaseTracking::NameAndLocation(sectionInfo.name, sectionInfo.lineInfo));
-        if (!sectionTracker.isOpen())
-            return false;
-        m_activeSections.push_back(&sectionTracker);
-
-        m_lastAssertionInfo.lineInfo = sectionInfo.lineInfo;
-
-        m_reporter->sectionStarting(sectionInfo);
-
-        assertions = m_totals.assertions;
-
-        return true;
-    }
-    auto RunContext::acquireGeneratorTracker( SourceLineInfo const& lineInfo ) -> IGeneratorTracker& {
-        using namespace Generators;
-        GeneratorTracker& tracker = GeneratorTracker::acquire( m_trackerContext, TestCaseTracking::NameAndLocation( "generator", lineInfo ) );
-        assert( tracker.isOpen() );
-        m_lastAssertionInfo.lineInfo = lineInfo;
-        return tracker;
-    }
-
-    bool RunContext::testForMissingAssertions(Counts& assertions) {
-        if (assertions.total() != 0)
-            return false;
-        if (!m_config->warnAboutMissingAssertions())
-            return false;
-        if (m_trackerContext.currentTracker().hasChildren())
-            return false;
-        m_totals.assertions.failed++;
-        assertions.failed++;
-        return true;
-    }
-
-    void RunContext::sectionEnded(SectionEndInfo const & endInfo) {
-        Counts assertions = m_totals.assertions - endInfo.prevAssertions;
-        bool missingAssertions = testForMissingAssertions(assertions);
-
-        if (!m_activeSections.empty()) {
-            m_activeSections.back()->close();
-            m_activeSections.pop_back();
-        }
-
-        m_reporter->sectionEnded(SectionStats(endInfo.sectionInfo, assertions, endInfo.durationInSeconds, missingAssertions));
-        m_messages.clear();
-        m_messageScopes.clear();
-    }
-
-    void RunContext::sectionEndedEarly(SectionEndInfo const & endInfo) {
-        if (m_unfinishedSections.empty())
-            m_activeSections.back()->fail();
-        else
-            m_activeSections.back()->close();
-        m_activeSections.pop_back();
-
-        m_unfinishedSections.push_back(endInfo);
-    }
-    void RunContext::benchmarkStarting( BenchmarkInfo const& info ) {
-        m_reporter->benchmarkStarting( info );
-    }
-    void RunContext::benchmarkEnded( BenchmarkStats const& stats ) {
-        m_reporter->benchmarkEnded( stats );
-    }
-
-    void RunContext::pushScopedMessage(MessageInfo const & message) {
-        m_messages.push_back(message);
-    }
-
-    void RunContext::popScopedMessage(MessageInfo const & message) {
-        m_messages.erase(std::remove(m_messages.begin(), m_messages.end(), message), m_messages.end());
-    }
-
-    void RunContext::emplaceUnscopedMessage( MessageBuilder const& builder ) {
-        m_messageScopes.emplace_back( builder );
-    }
-
-    std::string RunContext::getCurrentTestName() const {
-        return m_activeTestCase
-            ? m_activeTestCase->getTestCaseInfo().name
-            : std::string();
-    }
-
-    const AssertionResult * RunContext::getLastResult() const {
-        return &(*m_lastResult);
-    }
-
-    void RunContext::exceptionEarlyReported() {
-        m_shouldReportUnexpected = false;
-    }
-
-    void RunContext::handleFatalErrorCondition( StringRef message ) {
-        // First notify reporter that bad things happened
-        m_reporter->fatalErrorEncountered(message);
-
-        // Don't rebuild the result -- the stringification itself can cause more fatal errors
-        // Instead, fake a result data.
-        AssertionResultData tempResult( ResultWas::FatalErrorCondition, { false } );
-        tempResult.message = message;
-        AssertionResult result(m_lastAssertionInfo, tempResult);
-
-        assertionEnded(result);
-
-        handleUnfinishedSections();
-
-        // Recreate section for test case (as we will lose the one that was in scope)
-        auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
-        SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
-
-        Counts assertions;
-        assertions.failed = 1;
-        SectionStats testCaseSectionStats(testCaseSection, assertions, 0, false);
-        m_reporter->sectionEnded(testCaseSectionStats);
-
-        auto const& testInfo = m_activeTestCase->getTestCaseInfo();
-
-        Totals deltaTotals;
-        deltaTotals.testCases.failed = 1;
-        deltaTotals.assertions.failed = 1;
-        m_reporter->testCaseEnded(TestCaseStats(testInfo,
-                                  deltaTotals,
-                                  std::string(),
-                                  std::string(),
-                                  false));
-        m_totals.testCases.failed++;
-        testGroupEnded(std::string(), m_totals, 1, 1);
-        m_reporter->testRunEnded(TestRunStats(m_runInfo, m_totals, false));
-    }
-
-    bool RunContext::lastAssertionPassed() {
-         return m_lastAssertionPassed;
-    }
-
-    void RunContext::assertionPassed() {
-        m_lastAssertionPassed = true;
-        ++m_totals.assertions.passed;
-        resetAssertionInfo();
-        m_messageScopes.clear();
-    }
-
-    bool RunContext::aborting() const {
-        return m_totals.assertions.failed >= static_cast<std::size_t>(m_config->abortAfter());
-    }
-
-    void RunContext::runCurrentTest(std::string & redirectedCout, std::string & redirectedCerr) {
-        auto const& testCaseInfo = m_activeTestCase->getTestCaseInfo();
-        SectionInfo testCaseSection(testCaseInfo.lineInfo, testCaseInfo.name);
-        m_reporter->sectionStarting(testCaseSection);
-        Counts prevAssertions = m_totals.assertions;
-        double duration = 0;
-        m_shouldReportUnexpected = true;
-        m_lastAssertionInfo = { "TEST_CASE"_sr, testCaseInfo.lineInfo, StringRef(), ResultDisposition::Normal };
-
-        seedRng(*m_config);
-
-        Timer timer;
-        CATCH_TRY {
-            if (m_reporter->getPreferences().shouldRedirectStdOut) {
-#if !defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT)
-                RedirectedStreams redirectedStreams(redirectedCout, redirectedCerr);
-
-                timer.start();
-                invokeActiveTestCase();
-#else
-                OutputRedirect r(redirectedCout, redirectedCerr);
-                timer.start();
-                invokeActiveTestCase();
-#endif
-            } else {
-                timer.start();
-                invokeActiveTestCase();
-            }
-            duration = timer.getElapsedSeconds();
-        } CATCH_CATCH_ANON (TestFailureException&) {
-            // This just means the test was aborted due to failure
-        } CATCH_CATCH_ALL {
-            // Under CATCH_CONFIG_FAST_COMPILE, unexpected exceptions under REQUIRE assertions
-            // are reported without translation at the point of origin.
-            if( m_shouldReportUnexpected ) {
-                AssertionReaction dummyReaction;
-                handleUnexpectedInflightException( m_lastAssertionInfo, translateActiveException(), dummyReaction );
-            }
-        }
-        Counts assertions = m_totals.assertions - prevAssertions;
-        bool missingAssertions = testForMissingAssertions(assertions);
-
-        m_testCaseTracker->close();
-        handleUnfinishedSections();
-        m_messages.clear();
-        m_messageScopes.clear();
-
-        SectionStats testCaseSectionStats(testCaseSection, assertions, duration, missingAssertions);
-        m_reporter->sectionEnded(testCaseSectionStats);
-    }
-
-    void RunContext::invokeActiveTestCase() {
-        FatalConditionHandler fatalConditionHandler; // Handle signals
-        m_activeTestCase->invoke();
-        fatalConditionHandler.reset();
-    }
-
-    void RunContext::handleUnfinishedSections() {
-        // If sections ended prematurely due to an exception we stored their
-        // infos here so we can tear them down outside the unwind process.
-        for (auto it = m_unfinishedSections.rbegin(),
-             itEnd = m_unfinishedSections.rend();
-             it != itEnd;
-             ++it)
-            sectionEnded(*it);
-        m_unfinishedSections.clear();
-    }
-
-    void RunContext::handleExpr(
-        AssertionInfo const& info,
-        ITransientExpression const& expr,
-        AssertionReaction& reaction
-    ) {
-        m_reporter->assertionStarting( info );
-
-        bool negated = isFalseTest( info.resultDisposition );
-        bool result = expr.getResult() != negated;
-
-        if( result ) {
-            if (!m_includeSuccessfulResults) {
-                assertionPassed();
-            }
-            else {
-                reportExpr(info, ResultWas::Ok, &expr, negated);
-            }
-        }
-        else {
-            reportExpr(info, ResultWas::ExpressionFailed, &expr, negated );
-            populateReaction( reaction );
-        }
-    }
-    void RunContext::reportExpr(
-            AssertionInfo const &info,
-            ResultWas::OfType resultType,
-            ITransientExpression const *expr,
-            bool negated ) {
-
-        m_lastAssertionInfo = info;
-        AssertionResultData data( resultType, LazyExpression( negated ) );
-
-        AssertionResult assertionResult{ info, data };
-        assertionResult.m_resultData.lazyExpression.m_transientExpression = expr;
-
-        assertionEnded( assertionResult );
-    }
-
-    void RunContext::handleMessage(
-            AssertionInfo const& info,
-            ResultWas::OfType resultType,
-            StringRef const& message,
-            AssertionReaction& reaction
-    ) {
-        m_reporter->assertionStarting( info );
-
-        m_lastAssertionInfo = info;
-
-        AssertionResultData data( resultType, LazyExpression( false ) );
-        data.message = message;
-        AssertionResult assertionResult{ m_lastAssertionInfo, data };
-        assertionEnded( assertionResult );
-        if( !assertionResult.isOk() )
-            populateReaction( reaction );
-    }
-    void RunContext::handleUnexpectedExceptionNotThrown(
-            AssertionInfo const& info,
-            AssertionReaction& reaction
-    ) {
-        handleNonExpr(info, Catch::ResultWas::DidntThrowException, reaction);
-    }
-
-    void RunContext::handleUnexpectedInflightException(
-            AssertionInfo const& info,
-            std::string const& message,
-            AssertionReaction& reaction
-    ) {
-        m_lastAssertionInfo = info;
-
-        AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
-        data.message = message;
-        AssertionResult assertionResult{ info, data };
-        assertionEnded( assertionResult );
-        populateReaction( reaction );
-    }
-
-    void RunContext::populateReaction( AssertionReaction& reaction ) {
-        reaction.shouldDebugBreak = m_config->shouldDebugBreak();
-        reaction.shouldThrow = aborting() || (m_lastAssertionInfo.resultDisposition & ResultDisposition::Normal);
-    }
-
-    void RunContext::handleIncomplete(
-            AssertionInfo const& info
-    ) {
-        m_lastAssertionInfo = info;
-
-        AssertionResultData data( ResultWas::ThrewException, LazyExpression( false ) );
-        data.message = "Exception translation was disabled by CATCH_CONFIG_FAST_COMPILE";
-        AssertionResult assertionResult{ info, data };
-        assertionEnded( assertionResult );
-    }
-    void RunContext::handleNonExpr(
-            AssertionInfo const &info,
-            ResultWas::OfType resultType,
-            AssertionReaction &reaction
-    ) {
-        m_lastAssertionInfo = info;
-
-        AssertionResultData data( resultType, LazyExpression( false ) );
-        AssertionResult assertionResult{ info, data };
-        assertionEnded( assertionResult );
-
-        if( !assertionResult.isOk() )
-            populateReaction( reaction );
-    }
-
-    IResultCapture& getResultCapture() {
-        if (auto* capture = getCurrentContext().getResultCapture())
-            return *capture;
-        else
-            CATCH_INTERNAL_ERROR("No result capture instance");
-    }
-}
-// end catch_run_context.cpp
-// start catch_section.cpp
-
-namespace Catch {
-
-    Section::Section( SectionInfo const& info )
-    :   m_info( info ),
-        m_sectionIncluded( getResultCapture().sectionStarted( m_info, m_assertions ) )
-    {
-        m_timer.start();
-    }
-
-    Section::~Section() {
-        if( m_sectionIncluded ) {
-            SectionEndInfo endInfo{ m_info, m_assertions, m_timer.getElapsedSeconds() };
-            if( uncaught_exceptions() )
-                getResultCapture().sectionEndedEarly( endInfo );
-            else
-                getResultCapture().sectionEnded( endInfo );
-        }
-    }
-
-    // This indicates whether the section should be executed or not
-    Section::operator bool() const {
-        return m_sectionIncluded;
-    }
-
-} // end namespace Catch
-// end catch_section.cpp
-// start catch_section_info.cpp
-
-namespace Catch {
-
-    SectionInfo::SectionInfo
-        (   SourceLineInfo const& _lineInfo,
-            std::string const& _name )
-    :   name( _name ),
-        lineInfo( _lineInfo )
-    {}
-
-} // end namespace Catch
-// end catch_section_info.cpp
-// start catch_session.cpp
-
-// start catch_session.h
-
-#include <memory>
-
-namespace Catch {
-
-    class Session : NonCopyable {
-    public:
-
-        Session();
-        ~Session() override;
-
-        void showHelp() const;
-        void libIdentify();
-
-        int applyCommandLine( int argc, char const * const * argv );
-    #if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
-        int applyCommandLine( int argc, wchar_t const * const * argv );
-    #endif
-
-        void useConfigData( ConfigData const& configData );
-
-        template<typename CharT>
-        int run(int argc, CharT const * const argv[]) {
-            if (m_startupExceptions)
-                return 1;
-            int returnCode = applyCommandLine(argc, argv);
-            if (returnCode == 0)
-                returnCode = run();
-            return returnCode;
-        }
-
-        int run();
-
-        clara::Parser const& cli() const;
-        void cli( clara::Parser const& newParser );
-        ConfigData& configData();
-        Config& config();
-    private:
-        int runInternal();
-
-        clara::Parser m_cli;
-        ConfigData m_configData;
-        std::shared_ptr<Config> m_config;
-        bool m_startupExceptions = false;
-    };
-
-} // end namespace Catch
-
-// end catch_session.h
-// start catch_version.h
-
-#include <iosfwd>
-
-namespace Catch {
-
-    // Versioning information
-    struct Version {
-        Version( Version const& ) = delete;
-        Version& operator=( Version const& ) = delete;
-        Version(    unsigned int _majorVersion,
-                    unsigned int _minorVersion,
-                    unsigned int _patchNumber,
-                    char const * const _branchName,
-                    unsigned int _buildNumber );
-
-        unsigned int const majorVersion;
-        unsigned int const minorVersion;
-        unsigned int const patchNumber;
-
-        // buildNumber is only used if branchName is not null
-        char const * const branchName;
-        unsigned int const buildNumber;
-
-        friend std::ostream& operator << ( std::ostream& os, Version const& version );
-    };
-
-    Version const& libraryVersion();
-}
-
-// end catch_version.h
-#include <cstdlib>
-#include <iomanip>
-
-namespace Catch {
-
-    namespace {
-        const int MaxExitCode = 255;
-
-        IStreamingReporterPtr createReporter(std::string const& reporterName, IConfigPtr const& config) {
-            auto reporter = Catch::getRegistryHub().getReporterRegistry().create(reporterName, config);
-            CATCH_ENFORCE(reporter, "No reporter registered with name: '" << reporterName << "'");
-
-            return reporter;
-        }
-
-        IStreamingReporterPtr makeReporter(std::shared_ptr<Config> const& config) {
-            if (Catch::getRegistryHub().getReporterRegistry().getListeners().empty()) {
-                return createReporter(config->getReporterName(), config);
-            }
-
-            // On older platforms, returning std::unique_ptr<ListeningReporter>
-            // when the return type is std::unique_ptr<IStreamingReporter>
-            // doesn't compile without a std::move call. However, this causes
-            // a warning on newer platforms. Thus, we have to work around
-            // it a bit and downcast the pointer manually.
-            auto ret = std::unique_ptr<IStreamingReporter>(new ListeningReporter);
-            auto& multi = static_cast<ListeningReporter&>(*ret);
-            auto const& listeners = Catch::getRegistryHub().getReporterRegistry().getListeners();
-            for (auto const& listener : listeners) {
-                multi.addListener(listener->create(Catch::ReporterConfig(config)));
-            }
-            multi.addReporter(createReporter(config->getReporterName(), config));
-            return ret;
-        }
-
-        Catch::Totals runTests(std::shared_ptr<Config> const& config) {
-            auto reporter = makeReporter(config);
-
-            RunContext context(config, std::move(reporter));
-
-            Totals totals;
-
-            context.testGroupStarting(config->name(), 1, 1);
-
-            TestSpec testSpec = config->testSpec();
-
-            auto const& allTestCases = getAllTestCasesSorted(*config);
-            for (auto const& testCase : allTestCases) {
-                bool matching = (!testSpec.hasFilters() && !testCase.isHidden()) ||
-                                 (testSpec.hasFilters() && matchTest(testCase, testSpec, *config));
-
-                if (!context.aborting() && matching)
-                    totals += context.runTest(testCase);
-                else
-                    context.reporter().skipTest(testCase);
-            }
-
-            if (config->warnAboutNoTests() && totals.testCases.total() == 0) {
-                ReusableStringStream testConfig;
-
-                bool first = true;
-                for (const auto& input : config->getTestsOrTags()) {
-                    if (!first) { testConfig << ' '; }
-                    first = false;
-                    testConfig << input;
-                }
-
-                context.reporter().noMatchingTestCases(testConfig.str());
-                totals.error = -1;
-            }
-
-            context.testGroupEnded(config->name(), totals, 1, 1);
-            return totals;
-        }
-
-        void applyFilenamesAsTags(Catch::IConfig const& config) {
-            auto& tests = const_cast<std::vector<TestCase>&>(getAllTestCasesSorted(config));
-            for (auto& testCase : tests) {
-                auto tags = testCase.tags;
-
-                std::string filename = testCase.lineInfo.file;
-                auto lastSlash = filename.find_last_of("\\/");
-                if (lastSlash != std::string::npos) {
-                    filename.erase(0, lastSlash);
-                    filename[0] = '#';
-                }
-
-                auto lastDot = filename.find_last_of('.');
-                if (lastDot != std::string::npos) {
-                    filename.erase(lastDot);
-                }
-
-                tags.push_back(std::move(filename));
-                setTags(testCase, tags);
-            }
-        }
-
-    } // anon namespace
-
-    Session::Session() {
-        static bool alreadyInstantiated = false;
-        if( alreadyInstantiated ) {
-            CATCH_TRY { CATCH_INTERNAL_ERROR( "Only one instance of Catch::Session can ever be used" ); }
-            CATCH_CATCH_ALL { getMutableRegistryHub().registerStartupException(); }
-        }
-
-        // There cannot be exceptions at startup in no-exception mode.
-#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
-        const auto& exceptions = getRegistryHub().getStartupExceptionRegistry().getExceptions();
-        if ( !exceptions.empty() ) {
-            config();
-            getCurrentMutableContext().setConfig(m_config);
-
-            m_startupExceptions = true;
-            Colour colourGuard( Colour::Red );
-            Catch::cerr() << "Errors occurred during startup!" << '\n';
-            // iterate over all exceptions and notify user
-            for ( const auto& ex_ptr : exceptions ) {
-                try {
-                    std::rethrow_exception(ex_ptr);
-                } catch ( std::exception const& ex ) {
-                    Catch::cerr() << Column( ex.what() ).indent(2) << '\n';
-                }
-            }
-        }
-#endif
-
-        alreadyInstantiated = true;
-        m_cli = makeCommandLineParser( m_configData );
-    }
-    Session::~Session() {
-        Catch::cleanUp();
-    }
-
-    void Session::showHelp() const {
-        Catch::cout()
-                << "\nCatch v" << libraryVersion() << "\n"
-                << m_cli << std::endl
-                << "For more detailed usage please see the project docs\n" << std::endl;
-    }
-    void Session::libIdentify() {
-        Catch::cout()
-                << std::left << std::setw(16) << "description: " << "A Catch test executable\n"
-                << std::left << std::setw(16) << "category: " << "testframework\n"
-                << std::left << std::setw(16) << "framework: " << "Catch Test\n"
-                << std::left << std::setw(16) << "version: " << libraryVersion() << std::endl;
-    }
-
-    int Session::applyCommandLine( int argc, char const * const * argv ) {
-        if( m_startupExceptions )
-            return 1;
-
-        auto result = m_cli.parse( clara::Args( argc, argv ) );
-        if( !result ) {
-            config();
-            getCurrentMutableContext().setConfig(m_config);
-            Catch::cerr()
-                << Colour( Colour::Red )
-                << "\nError(s) in input:\n"
-                << Column( result.errorMessage() ).indent( 2 )
-                << "\n\n";
-            Catch::cerr() << "Run with -? for usage\n" << std::endl;
-            return MaxExitCode;
-        }
-
-        if( m_configData.showHelp )
-            showHelp();
-        if( m_configData.libIdentify )
-            libIdentify();
-        m_config.reset();
-        return 0;
-    }
-
-#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(UNICODE)
-    int Session::applyCommandLine( int argc, wchar_t const * const * argv ) {
-
-        char **utf8Argv = new char *[ argc ];
-
-        for ( int i = 0; i < argc; ++i ) {
-            int bufSize = WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, NULL, 0, NULL, NULL );
-
-            utf8Argv[ i ] = new char[ bufSize ];
-
-            WideCharToMultiByte( CP_UTF8, 0, argv[i], -1, utf8Argv[i], bufSize, NULL, NULL );
-        }
-
-        int returnCode = applyCommandLine( argc, utf8Argv );
-
-        for ( int i = 0; i < argc; ++i )
-            delete [] utf8Argv[ i ];
-
-        delete [] utf8Argv;
-
-        return returnCode;
-    }
-#endif
-
-    void Session::useConfigData( ConfigData const& configData ) {
-        m_configData = configData;
-        m_config.reset();
-    }
-
-    int Session::run() {
-        if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeStart ) != 0 ) {
-            Catch::cout() << "...waiting for enter/ return before starting" << std::endl;
-            static_cast<void>(std::getchar());
-        }
-        int exitCode = runInternal();
-        if( ( m_configData.waitForKeypress & WaitForKeypress::BeforeExit ) != 0 ) {
-            Catch::cout() << "...waiting for enter/ return before exiting, with code: " << exitCode << std::endl;
-            static_cast<void>(std::getchar());
-        }
-        return exitCode;
-    }
-
-    clara::Parser const& Session::cli() const {
-        return m_cli;
-    }
-    void Session::cli( clara::Parser const& newParser ) {
-        m_cli = newParser;
-    }
-    ConfigData& Session::configData() {
-        return m_configData;
-    }
-    Config& Session::config() {
-        if( !m_config )
-            m_config = std::make_shared<Config>( m_configData );
-        return *m_config;
-    }
-
-    int Session::runInternal() {
-        if( m_startupExceptions )
-            return 1;
-
-        if (m_configData.showHelp || m_configData.libIdentify) {
-            return 0;
-        }
-
-        CATCH_TRY {
-            config(); // Force config to be constructed
-
-            seedRng( *m_config );
-
-            if( m_configData.filenamesAsTags )
-                applyFilenamesAsTags( *m_config );
-
-            // Handle list request
-            if( Option<std::size_t> listed = list( m_config ) )
-                return static_cast<int>( *listed );
-
-            auto totals = runTests( m_config );
-            // Note that on unices only the lower 8 bits are usually used, clamping
-            // the return value to 255 prevents false negative when some multiple
-            // of 256 tests has failed
-            return (std::min) (MaxExitCode, (std::max) (totals.error, static_cast<int>(totals.assertions.failed)));
-        }
-#if !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS)
-        catch( std::exception& ex ) {
-            Catch::cerr() << ex.what() << std::endl;
-            return MaxExitCode;
-        }
-#endif
-    }
-
-} // end namespace Catch
-// end catch_session.cpp
-// start catch_singletons.cpp
-
-#include <vector>
-
-namespace Catch {
-
-    namespace {
-        static auto getSingletons() -> std::vector<ISingleton*>*& {
-            static std::vector<ISingleton*>* g_singletons = nullptr;
-            if( !g_singletons )
-                g_singletons = new std::vector<ISingleton*>();
-            return g_singletons;
-        }
-    }
-
-    ISingleton::~ISingleton() {}
-
-    void addSingleton(ISingleton* singleton ) {
-        getSingletons()->push_back( singleton );
-    }
-    void cleanupSingletons() {
-        auto& singletons = getSingletons();
-        for( auto singleton : *singletons )
-            delete singleton;
-        delete singletons;
-        singletons = nullptr;
-    }
-
-} // namespace Catch
-// end catch_singletons.cpp
-// start catch_startup_exception_registry.cpp
-
-namespace Catch {
-void StartupExceptionRegistry::add( std::exception_ptr const& exception ) noexcept {
-        CATCH_TRY {
-            m_exceptions.push_back(exception);
-        } CATCH_CATCH_ALL {
-            // If we run out of memory during start-up there's really not a lot more we can do about it
-            std::terminate();
-        }
-    }
-
-    std::vector<std::exception_ptr> const& StartupExceptionRegistry::getExceptions() const noexcept {
-        return m_exceptions;
-    }
-
-} // end namespace Catch
-// end catch_startup_exception_registry.cpp
-// start catch_stream.cpp
-
-#include <cstdio>
-#include <iostream>
-#include <fstream>
-#include <sstream>
-#include <vector>
-#include <memory>
-
-namespace Catch {
-
-    Catch::IStream::~IStream() = default;
-
-    namespace detail { namespace {
-        template<typename WriterF, std::size_t bufferSize=256>
-        class StreamBufImpl : public std::streambuf {
-            char data[bufferSize];
-            WriterF m_writer;
-
-        public:
-            StreamBufImpl() {
-                setp( data, data + sizeof(data) );
-            }
-
-            ~StreamBufImpl() noexcept {
-                StreamBufImpl::sync();
-            }
-
-        private:
-            int overflow( int c ) override {
-                sync();
-
-                if( c != EOF ) {
-                    if( pbase() == epptr() )
-                        m_writer( std::string( 1, static_cast<char>( c ) ) );
-                    else
-                        sputc( static_cast<char>( c ) );
-                }
-                return 0;
-            }
-
-            int sync() override {
-                if( pbase() != pptr() ) {
-                    m_writer( std::string( pbase(), static_cast<std::string::size_type>( pptr() - pbase() ) ) );
-                    setp( pbase(), epptr() );
-                }
-                return 0;
-            }
-        };
-
-        ///////////////////////////////////////////////////////////////////////////
-
-        struct OutputDebugWriter {
-
-            void operator()( std::string const&str ) {
-                writeToDebugConsole( str );
-            }
-        };
-
-        ///////////////////////////////////////////////////////////////////////////
-
-        class FileStream : public IStream {
-            mutable std::ofstream m_ofs;
-        public:
-            FileStream( StringRef filename ) {
-                m_ofs.open( filename.c_str() );
-                CATCH_ENFORCE( !m_ofs.fail(), "Unable to open file: '" << filename << "'" );
-            }
-            ~FileStream() override = default;
-        public: // IStream
-            std::ostream& stream() const override {
-                return m_ofs;
-            }
-        };
-
-        ///////////////////////////////////////////////////////////////////////////
-
-        class CoutStream : public IStream {
-            mutable std::ostream m_os;
-        public:
-            // Store the streambuf from cout up-front because
-            // cout may get redirected when running tests
-            CoutStream() : m_os( Catch::cout().rdbuf() ) {}
-            ~CoutStream() override = default;
-
-        public: // IStream
-            std::ostream& stream() const override { return m_os; }
-        };
-
-        ///////////////////////////////////////////////////////////////////////////
-
-        class DebugOutStream : public IStream {
-            std::unique_ptr<StreamBufImpl<OutputDebugWriter>> m_streamBuf;
-            mutable std::ostream m_os;
-        public:
-            DebugOutStream()
-            :   m_streamBuf( new StreamBufImpl<OutputDebugWriter>() ),
-                m_os( m_streamBuf.get() )
-            {}
-
-            ~DebugOutStream() override = default;
-
-        public: // IStream
-            std::ostream& stream() const override { return m_os; }
-        };
-
-    }} // namespace anon::detail
-
-    ///////////////////////////////////////////////////////////////////////////
-
-    auto makeStream( StringRef const &filename ) -> IStream const* {
-        if( filename.empty() )
-            return new detail::CoutStream();
-        else if( filename[0] == '%' ) {
-            if( filename == "%debug" )
-                return new detail::DebugOutStream();
-            else
-                CATCH_ERROR( "Unrecognised stream: '" << filename << "'" );
-        }
-        else
-            return new detail::FileStream( filename );
-    }
-
-    // This class encapsulates the idea of a pool of ostringstreams that can be reused.
-    struct StringStreams {
-        std::vector<std::unique_ptr<std::ostringstream>> m_streams;
-        std::vector<std::size_t> m_unused;
-        std::ostringstream m_referenceStream; // Used for copy state/ flags from
-
-        auto add() -> std::size_t {
-            if( m_unused.empty() ) {
-                m_streams.push_back( std::unique_ptr<std::ostringstream>( new std::ostringstream ) );
-                return m_streams.size()-1;
-            }
-            else {
-                auto index = m_unused.back();
-                m_unused.pop_back();
-                return index;
-            }
-        }
-
-        void release( std::size_t index ) {
-            m_streams[index]->copyfmt( m_referenceStream ); // Restore initial flags and other state
-            m_unused.push_back(index);
-        }
-    };
-
-    ReusableStringStream::ReusableStringStream()
-    :   m_index( Singleton<StringStreams>::getMutable().add() ),
-        m_oss( Singleton<StringStreams>::getMutable().m_streams[m_index].get() )
-    {}
-
-    ReusableStringStream::~ReusableStringStream() {
-        static_cast<std::ostringstream*>( m_oss )->str("");
-        m_oss->clear();
-        Singleton<StringStreams>::getMutable().release( m_index );
-    }
-
-    auto ReusableStringStream::str() const -> std::string {
-        return static_cast<std::ostringstream*>( m_oss )->str();
-    }
-
-    ///////////////////////////////////////////////////////////////////////////
-
-#ifndef CATCH_CONFIG_NOSTDOUT // If you #define this you must implement these functions
-    std::ostream& cout() { return std::cout; }
-    std::ostream& cerr() { return std::cerr; }
-    std::ostream& clog() { return std::clog; }
-#endif
-}
-// end catch_stream.cpp
-// start catch_string_manip.cpp
-
-#include <algorithm>
-#include <ostream>
-#include <cstring>
-#include <cctype>
-#include <vector>
-
-namespace Catch {
-
-    namespace {
-        char toLowerCh(char c) {
-            return static_cast<char>( std::tolower( c ) );
-        }
-    }
-
-    bool startsWith( std::string const& s, std::string const& prefix ) {
-        return s.size() >= prefix.size() && std::equal(prefix.begin(), prefix.end(), s.begin());
-    }
-    bool startsWith( std::string const& s, char prefix ) {
-        return !s.empty() && s[0] == prefix;
-    }
-    bool endsWith( std::string const& s, std::string const& suffix ) {
-        return s.size() >= suffix.size() && std::equal(suffix.rbegin(), suffix.rend(), s.rbegin());
-    }
-    bool endsWith( std::string const& s, char suffix ) {
-        return !s.empty() && s[s.size()-1] == suffix;
-    }
-    bool contains( std::string const& s, std::string const& infix ) {
-        return s.find( infix ) != std::string::npos;
-    }
-    void toLowerInPlace( std::string& s ) {
-        std::transform( s.begin(), s.end(), s.begin(), toLowerCh );
-    }
-    std::string toLower( std::string const& s ) {
-        std::string lc = s;
-        toLowerInPlace( lc );
-        return lc;
-    }
-    std::string trim( std::string const& str ) {
-        static char const* whitespaceChars = "\n\r\t ";
-        std::string::size_type start = str.find_first_not_of( whitespaceChars );
-        std::string::size_type end = str.find_last_not_of( whitespaceChars );
-
-        return start != std::string::npos ? str.substr( start, 1+end-start ) : std::string();
-    }
-
-    bool replaceInPlace( std::string& str, std::string const& replaceThis, std::string const& withThis ) {
-        bool replaced = false;
-        std::size_t i = str.find( replaceThis );
-        while( i != std::string::npos ) {
-            replaced = true;
-            str = str.substr( 0, i ) + withThis + str.substr( i+replaceThis.size() );
-            if( i < str.size()-withThis.size() )
-                i = str.find( replaceThis, i+withThis.size() );
-            else
-                i = std::string::npos;
-        }
-        return replaced;
-    }
-
-    std::vector<StringRef> splitStringRef( StringRef str, char delimiter ) {
-        std::vector<StringRef> subStrings;
-        std::size_t start = 0;
-        for(std::size_t pos = 0; pos < str.size(); ++pos ) {
-            if( str[pos] == delimiter ) {
-                if( pos - start > 1 )
-                    subStrings.push_back( str.substr( start, pos-start ) );
-                start = pos+1;
-            }
-        }
-        if( start < str.size() )
-            subStrings.push_back( str.substr( start, str.size()-start ) );
-        return subStrings;
-    }
-
-    pluralise::pluralise( std::size_t count, std::string const& label )
-    :   m_count( count ),
-        m_label( label )
-    {}
-
-    std::ostream& operator << ( std::ostream& os, pluralise const& pluraliser ) {
-        os << pluraliser.m_count << ' ' << pluraliser.m_label;
-        if( pluraliser.m_count != 1 )
-            os << 's';
-        return os;
-    }
-
-}
-// end catch_string_manip.cpp
-// start catch_stringref.cpp
-
-#if defined(__clang__)
-#    pragma clang diagnostic push
-#    pragma clang diagnostic ignored "-Wexit-time-destructors"
-#endif
-
-#include <ostream>
-#include <cstring>
-#include <cstdint>
-
-namespace {
-    const uint32_t byte_2_lead = 0xC0;
-    const uint32_t byte_3_lead = 0xE0;
-    const uint32_t byte_4_lead = 0xF0;
-}
-
-namespace Catch {
-    StringRef::StringRef( char const* rawChars ) noexcept
-    : StringRef( rawChars, static_cast<StringRef::size_type>(std::strlen(rawChars) ) )
-    {}
-
-    StringRef::operator std::string() const {
-        return std::string( m_start, m_size );
-    }
-
-    void StringRef::swap( StringRef& other ) noexcept {
-        std::swap( m_start, other.m_start );
-        std::swap( m_size, other.m_size );
-        std::swap( m_data, other.m_data );
-    }
-
-    auto StringRef::c_str() const -> char const* {
-        if( !isSubstring() )
-            return m_start;
-
-        const_cast<StringRef *>( this )->takeOwnership();
-        return m_data;
-    }
-    auto StringRef::currentData() const noexcept -> char const* {
-        return m_start;
-    }
-
-    auto StringRef::isOwned() const noexcept -> bool {
-        return m_data != nullptr;
-    }
-    auto StringRef::isSubstring() const noexcept -> bool {
-        return m_start[m_size] != '\0';
-    }
-
-    void StringRef::takeOwnership() {
-        if( !isOwned() ) {
-            m_data = new char[m_size+1];
-            memcpy( m_data, m_start, m_size );
-            m_data[m_size] = '\0';
-        }
-    }
-    auto StringRef::substr( size_type start, size_type size ) const noexcept -> StringRef {
-        if( start < m_size )
-            return StringRef( m_start+start, size );
-        else
-            return StringRef();
-    }
-    auto StringRef::operator == ( StringRef const& other ) const noexcept -> bool {
-        return
-            size() == other.size() &&
-            (std::strncmp( m_start, other.m_start, size() ) == 0);
-    }
-    auto StringRef::operator != ( StringRef const& other ) const noexcept -> bool {
-        return !operator==( other );
-    }
-
-    auto StringRef::operator[](size_type index) const noexcept -> char {
-        return m_start[index];
-    }
-
-    auto StringRef::numberOfCharacters() const noexcept -> size_type {
-        size_type noChars = m_size;
-        // Make adjustments for uft encodings
-        for( size_type i=0; i < m_size; ++i ) {
-            char c = m_start[i];
-            if( ( c & byte_2_lead ) == byte_2_lead ) {
-                noChars--;
-                if (( c & byte_3_lead ) == byte_3_lead )
-                    noChars--;
-                if( ( c & byte_4_lead ) == byte_4_lead )
-                    noChars--;
-            }
-        }
-        return noChars;
-    }
-
-    auto operator + ( StringRef const& lhs, StringRef const& rhs ) -> std::string {
-        std::string str;
-        str.reserve( lhs.size() + rhs.size() );
-        str += lhs;
-        str += rhs;
-        return str;
-    }
-    auto operator + ( StringRef const& lhs, const char* rhs ) -> std::string {
-        return std::string( lhs ) + std::string( rhs );
-    }
-    auto operator + ( char const* lhs, StringRef const& rhs ) -> std::string {
-        return std::string( lhs ) + std::string( rhs );
-    }
-
-    auto operator << ( std::ostream& os, StringRef const& str ) -> std::ostream& {
-        return os.write(str.currentData(), str.size());
-    }
-
-    auto operator+=( std::string& lhs, StringRef const& rhs ) -> std::string& {
-        lhs.append(rhs.currentData(), rhs.size());
-        return lhs;
-    }
-
-} // namespace Catch
-
-#if defined(__clang__)
-#    pragma clang diagnostic pop
-#endif
-// end catch_stringref.cpp
-// start catch_tag_alias.cpp
-
-namespace Catch {
-    TagAlias::TagAlias(std::string const & _tag, SourceLineInfo _lineInfo): tag(_tag), lineInfo(_lineInfo) {}
-}
-// end catch_tag_alias.cpp
-// start catch_tag_alias_autoregistrar.cpp
-
-namespace Catch {
-
-    RegistrarForTagAliases::RegistrarForTagAliases(char const* alias, char const* tag, SourceLineInfo const& lineInfo) {
-        CATCH_TRY {
-            getMutableRegistryHub().registerTagAlias(alias, tag, lineInfo);
-        } CATCH_CATCH_ALL {
-            // Do not throw when constructing global objects, instead register the exception to be processed later
-            getMutableRegistryHub().registerStartupException();
-        }
-    }
-
-}
-// end catch_tag_alias_autoregistrar.cpp
-// start catch_tag_alias_registry.cpp
-
-#include <sstream>
-
-namespace Catch {
-
-    TagAliasRegistry::~TagAliasRegistry() {}
-
-    TagAlias const* TagAliasRegistry::find( std::string const& alias ) const {
-        auto it = m_registry.find( alias );
-        if( it != m_registry.end() )
-            return &(it->second);
-        else
-            return nullptr;
-    }
-
-    std::string TagAliasRegistry::expandAliases( std::string const& unexpandedTestSpec ) const {
-        std::string expandedTestSpec = unexpandedTestSpec;
-        for( auto const& registryKvp : m_registry ) {
-            std::size_t pos = expandedTestSpec.find( registryKvp.first );
-            if( pos != std::string::npos ) {
-                expandedTestSpec =  expandedTestSpec.substr( 0, pos ) +
-                                    registryKvp.second.tag +
-                                    expandedTestSpec.substr( pos + registryKvp.first.size() );
-            }
-        }
-        return expandedTestSpec;
-    }
-
-    void TagAliasRegistry::add( std::string const& alias, std::string const& tag, SourceLineInfo const& lineInfo ) {
-        CATCH_ENFORCE( startsWith(alias, "[@") && endsWith(alias, ']'),
-                      "error: tag alias, '" << alias << "' is not of the form [@alias name].\n" << lineInfo );
-
-        CATCH_ENFORCE( m_registry.insert(std::make_pair(alias, TagAlias(tag, lineInfo))).second,
-                      "error: tag alias, '" << alias << "' already registered.\n"
-                      << "\tFirst seen at: " << find(alias)->lineInfo << "\n"
-                      << "\tRedefined at: " << lineInfo );
-    }
-
-    ITagAliasRegistry::~ITagAliasRegistry() {}
-
-    ITagAliasRegistry const& ITagAliasRegistry::get() {
-        return getRegistryHub().getTagAliasRegistry();
-    }
-
-} // end namespace Catch
-// end catch_tag_alias_registry.cpp
-// start catch_test_case_info.cpp
-
-#include <cctype>
-#include <exception>
-#include <algorithm>
-#include <sstream>
-
-namespace Catch {
-
-    namespace {
-        TestCaseInfo::SpecialProperties parseSpecialTag( std::string const& tag ) {
-            if( startsWith( tag, '.' ) ||
-                tag == "!hide" )
-                return TestCaseInfo::IsHidden;
-            else if( tag == "!throws" )
-                return TestCaseInfo::Throws;
-            else if( tag == "!shouldfail" )
-                return TestCaseInfo::ShouldFail;
-            else if( tag == "!mayfail" )
-                return TestCaseInfo::MayFail;
-            else if( tag == "!nonportable" )
-                return TestCaseInfo::NonPortable;
-            else if( tag == "!benchmark" )
-                return static_cast<TestCaseInfo::SpecialProperties>( TestCaseInfo::Benchmark | TestCaseInfo::IsHidden );
-            else
-                return TestCaseInfo::None;
-        }
-        bool isReservedTag( std::string const& tag ) {
-            return parseSpecialTag( tag ) == TestCaseInfo::None && tag.size() > 0 && !std::isalnum( static_cast<unsigned char>(tag[0]) );
-        }
-        void enforceNotReservedTag( std::string const& tag, SourceLineInfo const& _lineInfo ) {
-            CATCH_ENFORCE( !isReservedTag(tag),
-                          "Tag name: [" << tag << "] is not allowed.\n"
-                          << "Tag names starting with non alphanumeric characters are reserved\n"
-                          << _lineInfo );
-        }
-    }
-
-    TestCase makeTestCase(  ITestInvoker* _testCase,
-                            std::string const& _className,
-                            NameAndTags const& nameAndTags,
-                            SourceLineInfo const& _lineInfo )
-    {
-        bool isHidden = false;
-
-        // Parse out tags
-        std::vector<std::string> tags;
-        std::string desc, tag;
-        bool inTag = false;
-        std::string _descOrTags = nameAndTags.tags;
-        for (char c : _descOrTags) {
-            if( !inTag ) {
-                if( c == '[' )
-                    inTag = true;
-                else
-                    desc += c;
-            }
-            else {
-                if( c == ']' ) {
-                    TestCaseInfo::SpecialProperties prop = parseSpecialTag( tag );
-                    if( ( prop & TestCaseInfo::IsHidden ) != 0 )
-                        isHidden = true;
-                    else if( prop == TestCaseInfo::None )
-                        enforceNotReservedTag( tag, _lineInfo );
-
-                    // Merged hide tags like `[.approvals]` should be added as
-                    // `[.][approvals]`. The `[.]` is added at later point, so
-                    // we only strip the prefix
-                    if (startsWith(tag, '.') && tag.size() > 1) {
-                        tag.erase(0, 1);
-                    }
-                    tags.push_back( tag );
-                    tag.clear();
-                    inTag = false;
-                }
-                else
-                    tag += c;
-            }
-        }
-        if( isHidden ) {
-            tags.push_back( "." );
-        }
-
-        TestCaseInfo info( nameAndTags.name, _className, desc, tags, _lineInfo );
-        return TestCase( _testCase, std::move(info) );
-    }
-
-    void setTags( TestCaseInfo& testCaseInfo, std::vector<std::string> tags ) {
-        std::sort(begin(tags), end(tags));
-        tags.erase(std::unique(begin(tags), end(tags)), end(tags));
-        testCaseInfo.lcaseTags.clear();
-
-        for( auto const& tag : tags ) {
-            std::string lcaseTag = toLower( tag );
-            testCaseInfo.properties = static_cast<TestCaseInfo::SpecialProperties>( testCaseInfo.properties | parseSpecialTag( lcaseTag ) );
-            testCaseInfo.lcaseTags.push_back( lcaseTag );
-        }
-        testCaseInfo.tags = std::move(tags);
-    }
-
-    TestCaseInfo::TestCaseInfo( std::string const& _name,
-                                std::string const& _className,
-                                std::string const& _description,
-                                std::vector<std::string> const& _tags,
-                                SourceLineInfo const& _lineInfo )
-    :   name( _name ),
-        className( _className ),
-        description( _description ),
-        lineInfo( _lineInfo ),
-        properties( None )
-    {
-        setTags( *this, _tags );
-    }
-
-    bool TestCaseInfo::isHidden() const {
-        return ( properties & IsHidden ) != 0;
-    }
-    bool TestCaseInfo::throws() const {
-        return ( properties & Throws ) != 0;
-    }
-    bool TestCaseInfo::okToFail() const {
-        return ( properties & (ShouldFail | MayFail ) ) != 0;
-    }
-    bool TestCaseInfo::expectedToFail() const {
-        return ( properties & (ShouldFail ) ) != 0;
-    }
-
-    std::string TestCaseInfo::tagsAsString() const {
-        std::string ret;
-        // '[' and ']' per tag
-        std::size_t full_size = 2 * tags.size();
-        for (const auto& tag : tags) {
-            full_size += tag.size();
-        }
-        ret.reserve(full_size);
-        for (const auto& tag : tags) {
-            ret.push_back('[');
-            ret.append(tag);
-            ret.push_back(']');
-        }
-
-        return ret;
-    }
-
-    TestCase::TestCase( ITestInvoker* testCase, TestCaseInfo&& info ) : TestCaseInfo( std::move(info) ), test( testCase ) {}
-
-    TestCase TestCase::withName( std::string const& _newName ) const {
-        TestCase other( *this );
-        other.name = _newName;
-        return other;
-    }
-
-    void TestCase::invoke() const {
-        test->invoke();
-    }
-
-    bool TestCase::operator == ( TestCase const& other ) const {
-        return  test.get() == other.test.get() &&
-                name == other.name &&
-                className == other.className;
-    }
-
-    bool TestCase::operator < ( TestCase const& other ) const {
-        return name < other.name;
-    }
-
-    TestCaseInfo const& TestCase::getTestCaseInfo() const
-    {
-        return *this;
-    }
-
-} // end namespace Catch
-// end catch_test_case_info.cpp
-// start catch_test_case_registry_impl.cpp
-
-#include <sstream>
-
-namespace Catch {
-
-    std::vector<TestCase> sortTests( IConfig const& config, std::vector<TestCase> const& unsortedTestCases ) {
-
-        std::vector<TestCase> sorted = unsortedTestCases;
-
-        switch( config.runOrder() ) {
-            case RunTests::InLexicographicalOrder:
-                std::sort( sorted.begin(), sorted.end() );
-                break;
-            case RunTests::InRandomOrder:
-                seedRng( config );
-                std::shuffle( sorted.begin(), sorted.end(), rng() );
-                break;
-            case RunTests::InDeclarationOrder:
-                // already in declaration order
-                break;
-        }
-        return sorted;
-    }
-    bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ) {
-        return testSpec.matches( testCase ) && ( config.allowThrows() || !testCase.throws() );
-    }
-
-    void enforceNoDuplicateTestCases( std::vector<TestCase> const& functions ) {
-        std::set<TestCase> seenFunctions;
-        for( auto const& function : functions ) {
-            auto prev = seenFunctions.insert( function );
-            CATCH_ENFORCE( prev.second,
-                    "error: TEST_CASE( \"" << function.name << "\" ) already defined.\n"
-                    << "\tFirst seen at " << prev.first->getTestCaseInfo().lineInfo << "\n"
-                    << "\tRedefined at " << function.getTestCaseInfo().lineInfo );
-        }
-    }
-
-    std::vector<TestCase> filterTests( std::vector<TestCase> const& testCases, TestSpec const& testSpec, IConfig const& config ) {
-        std::vector<TestCase> filtered;
-        filtered.reserve( testCases.size() );
-        for (auto const& testCase : testCases) {
-            if ((!testSpec.hasFilters() && !testCase.isHidden()) ||
-                (testSpec.hasFilters() && matchTest(testCase, testSpec, config))) {
-                filtered.push_back(testCase);
-            }
-        }
-        return filtered;
-    }
-    std::vector<TestCase> const& getAllTestCasesSorted( IConfig const& config ) {
-        return getRegistryHub().getTestCaseRegistry().getAllTestsSorted( config );
-    }
-
-    void TestRegistry::registerTest( TestCase const& testCase ) {
-        std::string name = testCase.getTestCaseInfo().name;
-        if( name.empty() ) {
-            ReusableStringStream rss;
-            rss << "Anonymous test case " << ++m_unnamedCount;
-            return registerTest( testCase.withName( rss.str() ) );
-        }
-        m_functions.push_back( testCase );
-    }
-
-    std::vector<TestCase> const& TestRegistry::getAllTests() const {
-        return m_functions;
-    }
-    std::vector<TestCase> const& TestRegistry::getAllTestsSorted( IConfig const& config ) const {
-        if( m_sortedFunctions.empty() )
-            enforceNoDuplicateTestCases( m_functions );
-
-        if(  m_currentSortOrder != config.runOrder() || m_sortedFunctions.empty() ) {
-            m_sortedFunctions = sortTests( config, m_functions );
-            m_currentSortOrder = config.runOrder();
-        }
-        return m_sortedFunctions;
-    }
-
-    ///////////////////////////////////////////////////////////////////////////
-    TestInvokerAsFunction::TestInvokerAsFunction( void(*testAsFunction)() ) noexcept : m_testAsFunction( testAsFunction ) {}
-
-    void TestInvokerAsFunction::invoke() const {
-        m_testAsFunction();
-    }
-
-    std::string extractClassName( StringRef const& classOrQualifiedMethodName ) {
-        std::string className = classOrQualifiedMethodName;
-        if( startsWith( className, '&' ) )
-        {
-            std::size_t lastColons = className.rfind( "::" );
-            std::size_t penultimateColons = className.rfind( "::", lastColons-1 );
-            if( penultimateColons == std::string::npos )
-                penultimateColons = 1;
-            className = className.substr( penultimateColons, lastColons-penultimateColons );
-        }
-        return className;
-    }
-
-} // end namespace Catch
-// end catch_test_case_registry_impl.cpp
-// start catch_test_case_tracker.cpp
-
-#include <algorithm>
-#include <cassert>
-#include <stdexcept>
-#include <memory>
-#include <sstream>
-
-#if defined(__clang__)
-#    pragma clang diagnostic push
-#    pragma clang diagnostic ignored "-Wexit-time-destructors"
-#endif
-
-namespace Catch {
-namespace TestCaseTracking {
-
-    NameAndLocation::NameAndLocation( std::string const& _name, SourceLineInfo const& _location )
-    :   name( _name ),
-        location( _location )
-    {}
-
-    ITracker::~ITracker() = default;
-
-    ITracker& TrackerContext::startRun() {
-        m_rootTracker = std::make_shared<SectionTracker>( NameAndLocation( "{root}", CATCH_INTERNAL_LINEINFO ), *this, nullptr );
-        m_currentTracker = nullptr;
-        m_runState = Executing;
-        return *m_rootTracker;
-    }
-
-    void TrackerContext::endRun() {
-        m_rootTracker.reset();
-        m_currentTracker = nullptr;
-        m_runState = NotStarted;
-    }
-
-    void TrackerContext::startCycle() {
-        m_currentTracker = m_rootTracker.get();
-        m_runState = Executing;
-    }
-    void TrackerContext::completeCycle() {
-        m_runState = CompletedCycle;
-    }
-
-    bool TrackerContext::completedCycle() const {
-        return m_runState == CompletedCycle;
-    }
-    ITracker& TrackerContext::currentTracker() {
-        return *m_currentTracker;
-    }
-    void TrackerContext::setCurrentTracker( ITracker* tracker ) {
-        m_currentTracker = tracker;
-    }
-
-    TrackerBase::TrackerBase( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
-    :   m_nameAndLocation( nameAndLocation ),
-        m_ctx( ctx ),
-        m_parent( parent )
-    {}
-
-    NameAndLocation const& TrackerBase::nameAndLocation() const {
-        return m_nameAndLocation;
-    }
-    bool TrackerBase::isComplete() const {
-        return m_runState == CompletedSuccessfully || m_runState == Failed;
-    }
-    bool TrackerBase::isSuccessfullyCompleted() const {
-        return m_runState == CompletedSuccessfully;
-    }
-    bool TrackerBase::isOpen() const {
-        return m_runState != NotStarted && !isComplete();
-    }
-    bool TrackerBase::hasChildren() const {
-        return !m_children.empty();
-    }
-
-    void TrackerBase::addChild( ITrackerPtr const& child ) {
-        m_children.push_back( child );
-    }
-
-    ITrackerPtr TrackerBase::findChild( NameAndLocation const& nameAndLocation ) {
-        auto it = std::find_if( m_children.begin(), m_children.end(),
-            [&nameAndLocation]( ITrackerPtr const& tracker ){
-                return
-                    tracker->nameAndLocation().location == nameAndLocation.location &&
-                    tracker->nameAndLocation().name == nameAndLocation.name;
-            } );
-        return( it != m_children.end() )
-            ? *it
-            : nullptr;
-    }
-    ITracker& TrackerBase::parent() {
-        assert( m_parent ); // Should always be non-null except for root
-        return *m_parent;
-    }
-
-    void TrackerBase::openChild() {
-        if( m_runState != ExecutingChildren ) {
-            m_runState = ExecutingChildren;
-            if( m_parent )
-                m_parent->openChild();
-        }
-    }
-
-    bool TrackerBase::isSectionTracker() const { return false; }
-    bool TrackerBase::isGeneratorTracker() const { return false; }
-
-    void TrackerBase::open() {
-        m_runState = Executing;
-        moveToThis();
-        if( m_parent )
-            m_parent->openChild();
-    }
-
-    void TrackerBase::close() {
-
-        // Close any still open children (e.g. generators)
-        while( &m_ctx.currentTracker() != this )
-            m_ctx.currentTracker().close();
-
-        switch( m_runState ) {
-            case NeedsAnotherRun:
-                break;
-
-            case Executing:
-                m_runState = CompletedSuccessfully;
-                break;
-            case ExecutingChildren:
-                if( m_children.empty() || m_children.back()->isComplete() )
-                    m_runState = CompletedSuccessfully;
-                break;
-
-            case NotStarted:
-            case CompletedSuccessfully:
-            case Failed:
-                CATCH_INTERNAL_ERROR( "Illogical state: " << m_runState );
-
-            default:
-                CATCH_INTERNAL_ERROR( "Unknown state: " << m_runState );
-        }
-        moveToParent();
-        m_ctx.completeCycle();
-    }
-    void TrackerBase::fail() {
-        m_runState = Failed;
-        if( m_parent )
-            m_parent->markAsNeedingAnotherRun();
-        moveToParent();
-        m_ctx.completeCycle();
-    }
-    void TrackerBase::markAsNeedingAnotherRun() {
-        m_runState = NeedsAnotherRun;
-    }
-
-    void TrackerBase::moveToParent() {
-        assert( m_parent );
-        m_ctx.setCurrentTracker( m_parent );
-    }
-    void TrackerBase::moveToThis() {
-        m_ctx.setCurrentTracker( this );
-    }
-
-    SectionTracker::SectionTracker( NameAndLocation const& nameAndLocation, TrackerContext& ctx, ITracker* parent )
-    :   TrackerBase( nameAndLocation, ctx, parent )
-    {
-        if( parent ) {
-            while( !parent->isSectionTracker() )
-                parent = &parent->parent();
-
-            SectionTracker& parentSection = static_cast<SectionTracker&>( *parent );
-            addNextFilters( parentSection.m_filters );
-        }
-    }
-
-    bool SectionTracker::isComplete() const {
-        bool complete = true;
-
-        if ((m_filters.empty() || m_filters[0] == "") ||
-             std::find(m_filters.begin(), m_filters.end(),
-                       m_nameAndLocation.name) != m_filters.end())
-            complete = TrackerBase::isComplete();
-        return complete;
-
-    }
-
-    bool SectionTracker::isSectionTracker() const { return true; }
-
-    SectionTracker& SectionTracker::acquire( TrackerContext& ctx, NameAndLocation const& nameAndLocation ) {
-        std::shared_ptr<SectionTracker> section;
-
-        ITracker& currentTracker = ctx.currentTracker();
-        if( ITrackerPtr childTracker = currentTracker.findChild( nameAndLocation ) ) {
-            assert( childTracker );
-            assert( childTracker->isSectionTracker() );
-            section = std::static_pointer_cast<SectionTracker>( childTracker );
-        }
-        else {
-            section = std::make_shared<SectionTracker>( nameAndLocation, ctx, &currentTracker );
-            currentTracker.addChild( section );
-        }
-        if( !ctx.completedCycle() )
-            section->tryOpen();
-        return *section;
-    }
-
-    void SectionTracker::tryOpen() {
-        if( !isComplete() && (m_filters.empty() || m_filters[0].empty() ||  m_filters[0] == m_nameAndLocation.name ) )
-            open();
-    }
-
-    void SectionTracker::addInitialFilters( std::vector<std::string> const& filters ) {
-        if( !filters.empty() ) {
-            m_filters.push_back(""); // Root - should never be consulted
-            m_filters.push_back(""); // Test Case - not a section filter
-            m_filters.insert( m_filters.end(), filters.begin(), filters.end() );
-        }
-    }
-    void SectionTracker::addNextFilters( std::vector<std::string> const& filters ) {
-        if( filters.size() > 1 )
-            m_filters.insert( m_filters.end(), ++filters.begin(), filters.end() );
-    }
-
-} // namespace TestCaseTracking
-
-using TestCaseTracking::ITracker;
-using TestCaseTracking::TrackerContext;
-using TestCaseTracking::SectionTracker;
-
-} // namespace Catch
-
-#if defined(__clang__)
-#    pragma clang diagnostic pop
-#endif
-// end catch_test_case_tracker.cpp
-// start catch_test_registry.cpp
-
-namespace Catch {
-
-    auto makeTestInvoker( void(*testAsFunction)() ) noexcept -> ITestInvoker* {
-        return new(std::nothrow) TestInvokerAsFunction( testAsFunction );
-    }
-
-    NameAndTags::NameAndTags( StringRef const& name_ , StringRef const& tags_ ) noexcept : name( name_ ), tags( tags_ ) {}
-
-    AutoReg::AutoReg( ITestInvoker* invoker, SourceLineInfo const& lineInfo, StringRef const& classOrMethod, NameAndTags const& nameAndTags ) noexcept {
-        CATCH_TRY {
-            getMutableRegistryHub()
-                    .registerTest(
-                        makeTestCase(
-                            invoker,
-                            extractClassName( classOrMethod ),
-                            nameAndTags,
-                            lineInfo));
-        } CATCH_CATCH_ALL {
-            // Do not throw when constructing global objects, instead register the exception to be processed later
-            getMutableRegistryHub().registerStartupException();
-        }
-    }
-
-    AutoReg::~AutoReg() = default;
-}
-// end catch_test_registry.cpp
-// start catch_test_spec.cpp
-
-#include <algorithm>
-#include <string>
-#include <vector>
-#include <memory>
-
-namespace Catch {
-
-    TestSpec::Pattern::~Pattern() = default;
-    TestSpec::NamePattern::~NamePattern() = default;
-    TestSpec::TagPattern::~TagPattern() = default;
-    TestSpec::ExcludedPattern::~ExcludedPattern() = default;
-
-    TestSpec::NamePattern::NamePattern( std::string const& name )
-    : m_wildcardPattern( toLower( name ), CaseSensitive::No )
-    {}
-    bool TestSpec::NamePattern::matches( TestCaseInfo const& testCase ) const {
-        return m_wildcardPattern.matches( toLower( testCase.name ) );
-    }
-
-    TestSpec::TagPattern::TagPattern( std::string const& tag ) : m_tag( toLower( tag ) ) {}
-    bool TestSpec::TagPattern::matches( TestCaseInfo const& testCase ) const {
-        return std::find(begin(testCase.lcaseTags),
-                         end(testCase.lcaseTags),
-                         m_tag) != end(testCase.lcaseTags);
-    }
-
-    TestSpec::ExcludedPattern::ExcludedPattern( PatternPtr const& underlyingPattern ) : m_underlyingPattern( underlyingPattern ) {}
-    bool TestSpec::ExcludedPattern::matches( TestCaseInfo const& testCase ) const { return !m_underlyingPattern->matches( testCase ); }
-
-    bool TestSpec::Filter::matches( TestCaseInfo const& testCase ) const {
-        // All patterns in a filter must match for the filter to be a match
-        for( auto const& pattern : m_patterns ) {
-            if( !pattern->matches( testCase ) )
-                return false;
-        }
-        return true;
-    }
-
-    bool TestSpec::hasFilters() const {
-        return !m_filters.empty();
-    }
-    bool TestSpec::matches( TestCaseInfo const& testCase ) const {
-        // A TestSpec matches if any filter matches
-        for( auto const& filter : m_filters )
-            if( filter.matches( testCase ) )
-                return true;
-        return false;
-    }
-}
-// end catch_test_spec.cpp
-// start catch_test_spec_parser.cpp
-
-namespace Catch {
-
-    TestSpecParser::TestSpecParser( ITagAliasRegistry const& tagAliases ) : m_tagAliases( &tagAliases ) {}
-
-    TestSpecParser& TestSpecParser::parse( std::string const& arg ) {
-        m_mode = None;
-        m_exclusion = false;
-        m_start = std::string::npos;
-        m_arg = m_tagAliases->expandAliases( arg );
-        m_escapeChars.clear();
-        for( m_pos = 0; m_pos < m_arg.size(); ++m_pos )
-            visitChar( m_arg[m_pos] );
-        if( m_mode == Name )
-            addPattern<TestSpec::NamePattern>();
-        return *this;
-    }
-    TestSpec TestSpecParser::testSpec() {
-        addFilter();
-        return m_testSpec;
-    }
-
-    void TestSpecParser::visitChar( char c ) {
-        if( m_mode == None ) {
-            switch( c ) {
-            case ' ': return;
-            case '~': m_exclusion = true; return;
-            case '[': return startNewMode( Tag, ++m_pos );
-            case '"': return startNewMode( QuotedName, ++m_pos );
-            case '\\': return escape();
-            default: startNewMode( Name, m_pos ); break;
-            }
-        }
-        if( m_mode == Name ) {
-            if( c == ',' ) {
-                addPattern<TestSpec::NamePattern>();
-                addFilter();
-            }
-            else if( c == '[' ) {
-                if( subString() == "exclude:" )
-                    m_exclusion = true;
-                else
-                    addPattern<TestSpec::NamePattern>();
-                startNewMode( Tag, ++m_pos );
-            }
-            else if( c == '\\' )
-                escape();
-        }
-        else if( m_mode == EscapedName )
-            m_mode = Name;
-        else if( m_mode == QuotedName && c == '"' )
-            addPattern<TestSpec::NamePattern>();
-        else if( m_mode == Tag && c == ']' )
-            addPattern<TestSpec::TagPattern>();
-    }
-    void TestSpecParser::startNewMode( Mode mode, std::size_t start ) {
-        m_mode = mode;
-        m_start = start;
-    }
-    void TestSpecParser::escape() {
-        if( m_mode == None )
-            m_start = m_pos;
-        m_mode = EscapedName;
-        m_escapeChars.push_back( m_pos );
-    }
-    std::string TestSpecParser::subString() const { return m_arg.substr( m_start, m_pos - m_start ); }
-
-    void TestSpecParser::addFilter() {
-        if( !m_currentFilter.m_patterns.empty() ) {
-            m_testSpec.m_filters.push_back( m_currentFilter );
-            m_currentFilter = TestSpec::Filter();
-        }
-    }
-
-    TestSpec parseTestSpec( std::string const& arg ) {
-        return TestSpecParser( ITagAliasRegistry::get() ).parse( arg ).testSpec();
-    }
-
-} // namespace Catch
-// end catch_test_spec_parser.cpp
-// start catch_timer.cpp
-
-#include <chrono>
-
-static const uint64_t nanosecondsInSecond = 1000000000;
-
-namespace Catch {
-
-    auto getCurrentNanosecondsSinceEpoch() -> uint64_t {
-        return std::chrono::duration_cast<std::chrono::nanoseconds>( std::chrono::high_resolution_clock::now().time_since_epoch() ).count();
-    }
-
-    namespace {
-        auto estimateClockResolution() -> uint64_t {
-            uint64_t sum = 0;
-            static const uint64_t iterations = 1000000;
-
-            auto startTime = getCurrentNanosecondsSinceEpoch();
-
-            for( std::size_t i = 0; i < iterations; ++i ) {
-
-                uint64_t ticks;
-                uint64_t baseTicks = getCurrentNanosecondsSinceEpoch();
-                do {
-                    ticks = getCurrentNanosecondsSinceEpoch();
-                } while( ticks == baseTicks );
-
-                auto delta = ticks - baseTicks;
-                sum += delta;
-
-                // If we have been calibrating for over 3 seconds -- the clock
-                // is terrible and we should move on.
-                // TBD: How to signal that the measured resolution is probably wrong?
-                if (ticks > startTime + 3 * nanosecondsInSecond) {
-                    return sum / ( i + 1u );
-                }
-            }
-
-            // We're just taking the mean, here. To do better we could take the std. dev and exclude outliers
-            // - and potentially do more iterations if there's a high variance.
-            return sum/iterations;
-        }
-    }
-    auto getEstimatedClockResolution() -> uint64_t {
-        static auto s_resolution = estimateClockResolution();
-        return s_resolution;
-    }
-
-    void Timer::start() {
-       m_nanoseconds = getCurrentNanosecondsSinceEpoch();
-    }
-    auto Timer::getElapsedNanoseconds() const -> uint64_t {
-        return getCurrentNanosecondsSinceEpoch() - m_nanoseconds;
-    }
-    auto Timer::getElapsedMicroseconds() const -> uint64_t {
-        return getElapsedNanoseconds()/1000;
-    }
-    auto Timer::getElapsedMilliseconds() const -> unsigned int {
-        return static_cast<unsigned int>(getElapsedMicroseconds()/1000);
-    }
-    auto Timer::getElapsedSeconds() const -> double {
-        return getElapsedMicroseconds()/1000000.0;
-    }
-
-} // namespace Catch
-// end catch_timer.cpp
-// start catch_tostring.cpp
-
-#if defined(__clang__)
-#    pragma clang diagnostic push
-#    pragma clang diagnostic ignored "-Wexit-time-destructors"
-#    pragma clang diagnostic ignored "-Wglobal-constructors"
-#endif
-
-// Enable specific decls locally
-#if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER)
-#define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER
-#endif
-
-#include <cmath>
-#include <iomanip>
-
-namespace Catch {
-
-namespace Detail {
-
-    const std::string unprintableString = "{?}";
-
-    namespace {
-        const int hexThreshold = 255;
-
-        struct Endianness {
-            enum Arch { Big, Little };
-
-            static Arch which() {
-                union _{
-                    int asInt;
-                    char asChar[sizeof (int)];
-                } u;
-
-                u.asInt = 1;
-                return ( u.asChar[sizeof(int)-1] == 1 ) ? Big : Little;
-            }
-        };
-    }
-
-    std::string rawMemoryToString( const void *object, std::size_t size ) {
-        // Reverse order for little endian architectures
-        int i = 0, end = static_cast<int>( size ), inc = 1;
-        if( Endianness::which() == Endianness::Little ) {
-            i = end-1;
-            end = inc = -1;
-        }
-
-        unsigned char const *bytes = static_cast<unsigned char const *>(object);
-        ReusableStringStream rss;
-        rss << "0x" << std::setfill('0') << std::hex;
-        for( ; i != end; i += inc )
-             rss << std::setw(2) << static_cast<unsigned>(bytes[i]);
-       return rss.str();
-    }
-}
-
-template<typename T>
-std::string fpToString( T value, int precision ) {
-    if (Catch::isnan(value)) {
-        return "nan";
-    }
-
-    ReusableStringStream rss;
-    rss << std::setprecision( precision )
-        << std::fixed
-        << value;
-    std::string d = rss.str();
-    std::size_t i = d.find_last_not_of( '0' );
-    if( i != std::string::npos && i != d.size()-1 ) {
-        if( d[i] == '.' )
-            i++;
-        d = d.substr( 0, i+1 );
-    }
-    return d;
-}
-
-//// ======================================================= ////
-//
-//   Out-of-line defs for full specialization of StringMaker
-//
-//// ======================================================= ////
-
-std::string StringMaker<std::string>::convert(const std::string& str) {
-    if (!getCurrentContext().getConfig()->showInvisibles()) {
-        return '"' + str + '"';
-    }
-
-    std::string s("\"");
-    for (char c : str) {
-        switch (c) {
-        case '\n':
-            s.append("\\n");
-            break;
-        case '\t':
-            s.append("\\t");
-            break;
-        default:
-            s.push_back(c);
-            break;
-        }
-    }
-    s.append("\"");
-    return s;
-}
-
-#ifdef CATCH_CONFIG_CPP17_STRING_VIEW
-std::string StringMaker<std::string_view>::convert(std::string_view str) {
-    return ::Catch::Detail::stringify(std::string{ str });
-}
-#endif
-
-std::string StringMaker<char const*>::convert(char const* str) {
-    if (str) {
-        return ::Catch::Detail::stringify(std::string{ str });
-    } else {
-        return{ "{null string}" };
-    }
-}
-std::string StringMaker<char*>::convert(char* str) {
-    if (str) {
-        return ::Catch::Detail::stringify(std::string{ str });
-    } else {
-        return{ "{null string}" };
-    }
-}
-
-#ifdef CATCH_CONFIG_WCHAR
-std::string StringMaker<std::wstring>::convert(const std::wstring& wstr) {
-    std::string s;
-    s.reserve(wstr.size());
-    for (auto c : wstr) {
-        s += (c <= 0xff) ? static_cast<char>(c) : '?';
-    }
-    return ::Catch::Detail::stringify(s);
-}
-
-# ifdef CATCH_CONFIG_CPP17_STRING_VIEW
-std::string StringMaker<std::wstring_view>::convert(std::wstring_view str) {
-    return StringMaker<std::wstring>::convert(std::wstring(str));
-}
-# endif
-
-std::string StringMaker<wchar_t const*>::convert(wchar_t const * str) {
-    if (str) {
-        return ::Catch::Detail::stringify(std::wstring{ str });
-    } else {
-        return{ "{null string}" };
-    }
-}
-std::string StringMaker<wchar_t *>::convert(wchar_t * str) {
-    if (str) {
-        return ::Catch::Detail::stringify(std::wstring{ str });
-    } else {
-        return{ "{null string}" };
-    }
-}
-#endif
-
-std::string StringMaker<int>::convert(int value) {
-    return ::Catch::Detail::stringify(static_cast<long long>(value));
-}
-std::string StringMaker<long>::convert(long value) {
-    return ::Catch::Detail::stringify(static_cast<long long>(value));
-}
-std::string StringMaker<long long>::convert(long long value) {
-    ReusableStringStream rss;
-    rss << value;
-    if (value > Detail::hexThreshold) {
-        rss << " (0x" << std::hex << value << ')';
-    }
-    return rss.str();
-}
-
-std::string StringMaker<unsigned int>::convert(unsigned int value) {
-    return ::Catch::Detail::stringify(static_cast<unsigned long long>(value));
-}
-std::string StringMaker<unsigned long>::convert(unsigned long value) {
-    return ::Catch::Detail::stringify(static_cast<unsigned long long>(value));
-}
-std::string StringMaker<unsigned long long>::convert(unsigned long long value) {
-    ReusableStringStream rss;
-    rss << value;
-    if (value > Detail::hexThreshold) {
-        rss << " (0x" << std::hex << value << ')';
-    }
-    return rss.str();
-}
-
-std::string StringMaker<bool>::convert(bool b) {
-    return b ? "true" : "false";
-}
-
-std::string StringMaker<signed char>::convert(signed char value) {
-    if (value == '\r') {
-        return "'\\r'";
-    } else if (value == '\f') {
-        return "'\\f'";
-    } else if (value == '\n') {
-        return "'\\n'";
-    } else if (value == '\t') {
-        return "'\\t'";
-    } else if ('\0' <= value && value < ' ') {
-        return ::Catch::Detail::stringify(static_cast<unsigned int>(value));
-    } else {
-        char chstr[] = "' '";
-        chstr[1] = value;
-        return chstr;
-    }
-}
-std::string StringMaker<char>::convert(char c) {
-    return ::Catch::Detail::stringify(static_cast<signed char>(c));
-}
-std::string StringMaker<unsigned char>::convert(unsigned char c) {
-    return ::Catch::Detail::stringify(static_cast<char>(c));
-}
-
-std::string StringMaker<std::nullptr_t>::convert(std::nullptr_t) {
-    return "nullptr";
-}
-
-int StringMaker<float>::precision = 5;
-
-std::string StringMaker<float>::convert(float value) {
-    return fpToString(value, precision) + 'f';
-}
-
-int StringMaker<double>::precision = 10;
-
-std::string StringMaker<double>::convert(double value) {
-    return fpToString(value, precision);
-}
-
-std::string ratio_string<std::atto>::symbol() { return "a"; }
-std::string ratio_string<std::femto>::symbol() { return "f"; }
-std::string ratio_string<std::pico>::symbol() { return "p"; }
-std::string ratio_string<std::nano>::symbol() { return "n"; }
-std::string ratio_string<std::micro>::symbol() { return "u"; }
-std::string ratio_string<std::milli>::symbol() { return "m"; }
-
-} // end namespace Catch
-
-#if defined(__clang__)
-#    pragma clang diagnostic pop
-#endif
-
-// end catch_tostring.cpp
-// start catch_totals.cpp
-
-namespace Catch {
-
-    Counts Counts::operator - ( Counts const& other ) const {
-        Counts diff;
-        diff.passed = passed - other.passed;
-        diff.failed = failed - other.failed;
-        diff.failedButOk = failedButOk - other.failedButOk;
-        return diff;
-    }
-
-    Counts& Counts::operator += ( Counts const& other ) {
-        passed += other.passed;
-        failed += other.failed;
-        failedButOk += other.failedButOk;
-        return *this;
-    }
-
-    std::size_t Counts::total() const {
-        return passed + failed + failedButOk;
-    }
-    bool Counts::allPassed() const {
-        return failed == 0 && failedButOk == 0;
-    }
-    bool Counts::allOk() const {
-        return failed == 0;
-    }
-
-    Totals Totals::operator - ( Totals const& other ) const {
-        Totals diff;
-        diff.assertions = assertions - other.assertions;
-        diff.testCases = testCases - other.testCases;
-        return diff;
-    }
-
-    Totals& Totals::operator += ( Totals const& other ) {
-        assertions += other.assertions;
-        testCases += other.testCases;
-        return *this;
-    }
-
-    Totals Totals::delta( Totals const& prevTotals ) const {
-        Totals diff = *this - prevTotals;
-        if( diff.assertions.failed > 0 )
-            ++diff.testCases.failed;
-        else if( diff.assertions.failedButOk > 0 )
-            ++diff.testCases.failedButOk;
-        else
-            ++diff.testCases.passed;
-        return diff;
-    }
-
-}
-// end catch_totals.cpp
-// start catch_uncaught_exceptions.cpp
-
-#include <exception>
-
-namespace Catch {
-    bool uncaught_exceptions() {
-#if defined(CATCH_CONFIG_CPP17_UNCAUGHT_EXCEPTIONS)
-        return std::uncaught_exceptions() > 0;
-#else
-        return std::uncaught_exception();
-#endif
-  }
-} // end namespace Catch
-// end catch_uncaught_exceptions.cpp
-// start catch_version.cpp
-
-#include <ostream>
-
-namespace Catch {
-
-    Version::Version
-        (   unsigned int _majorVersion,
-            unsigned int _minorVersion,
-            unsigned int _patchNumber,
-            char const * const _branchName,
-            unsigned int _buildNumber )
-    :   majorVersion( _majorVersion ),
-        minorVersion( _minorVersion ),
-        patchNumber( _patchNumber ),
-        branchName( _branchName ),
-        buildNumber( _buildNumber )
-    {}
-
-    std::ostream& operator << ( std::ostream& os, Version const& version ) {
-        os  << version.majorVersion << '.'
-            << version.minorVersion << '.'
-            << version.patchNumber;
-        // branchName is never null -> 0th char is \0 if it is empty
-        if (version.branchName[0]) {
-            os << '-' << version.branchName
-               << '.' << version.buildNumber;
-        }
-        return os;
-    }
-
-    Version const& libraryVersion() {
-        static Version version( 2, 8, 0, "", 0 );
-        return version;
-    }
-
-}
-// end catch_version.cpp
-// start catch_wildcard_pattern.cpp
-
-#include <sstream>
-
-namespace Catch {
-
-    WildcardPattern::WildcardPattern( std::string const& pattern,
-                                      CaseSensitive::Choice caseSensitivity )
-    :   m_caseSensitivity( caseSensitivity ),
-        m_pattern( adjustCase( pattern ) )
-    {
-        if( startsWith( m_pattern, '*' ) ) {
-            m_pattern = m_pattern.substr( 1 );
-            m_wildcard = WildcardAtStart;
-        }
-        if( endsWith( m_pattern, '*' ) ) {
-            m_pattern = m_pattern.substr( 0, m_pattern.size()-1 );
-            m_wildcard = static_cast<WildcardPosition>( m_wildcard | WildcardAtEnd );
-        }
-    }
-
-    bool WildcardPattern::matches( std::string const& str ) const {
-        switch( m_wildcard ) {
-            case NoWildcard:
-                return m_pattern == adjustCase( str );
-            case WildcardAtStart:
-                return endsWith( adjustCase( str ), m_pattern );
-            case WildcardAtEnd:
-                return startsWith( adjustCase( str ), m_pattern );
-            case WildcardAtBothEnds:
-                return contains( adjustCase( str ), m_pattern );
-            default:
-                CATCH_INTERNAL_ERROR( "Unknown enum" );
-        }
-    }
-
-    std::string WildcardPattern::adjustCase( std::string const& str ) const {
-        return m_caseSensitivity == CaseSensitive::No ? toLower( str ) : str;
-    }
-}
-// end catch_wildcard_pattern.cpp
-// start catch_xmlwriter.cpp
-
-#include <iomanip>
-
-using uchar = unsigned char;
-
-namespace Catch {
-
-namespace {
-
-    size_t trailingBytes(unsigned char c) {
-        if ((c & 0xE0) == 0xC0) {
-            return 2;
-        }
-        if ((c & 0xF0) == 0xE0) {
-            return 3;
-        }
-        if ((c & 0xF8) == 0xF0) {
-            return 4;
-        }
-        CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
-    }
-
-    uint32_t headerValue(unsigned char c) {
-        if ((c & 0xE0) == 0xC0) {
-            return c & 0x1F;
-        }
-        if ((c & 0xF0) == 0xE0) {
-            return c & 0x0F;
-        }
-        if ((c & 0xF8) == 0xF0) {
-            return c & 0x07;
-        }
-        CATCH_INTERNAL_ERROR("Invalid multibyte utf-8 start byte encountered");
-    }
-
-    void hexEscapeChar(std::ostream& os, unsigned char c) {
-        std::ios_base::fmtflags f(os.flags());
-        os << "\\x"
-            << std::uppercase << std::hex << std::setfill('0') << std::setw(2)
-            << static_cast<int>(c);
-        os.flags(f);
-    }
-
-} // anonymous namespace
-
-    XmlEncode::XmlEncode( std::string const& str, ForWhat forWhat )
-    :   m_str( str ),
-        m_forWhat( forWhat )
-    {}
-
-    void XmlEncode::encodeTo( std::ostream& os ) const {
-        // Apostrophe escaping not necessary if we always use " to write attributes
-        // (see: http://www.w3.org/TR/xml/#syntax)
-
-        for( std::size_t idx = 0; idx < m_str.size(); ++ idx ) {
-            uchar c = m_str[idx];
-            switch (c) {
-            case '<':   os << "&lt;"; break;
-            case '&':   os << "&amp;"; break;
-
-            case '>':
-                // See: http://www.w3.org/TR/xml/#syntax
-                if (idx > 2 && m_str[idx - 1] == ']' && m_str[idx - 2] == ']')
-                    os << "&gt;";
-                else
-                    os << c;
-                break;
-
-            case '\"':
-                if (m_forWhat == ForAttributes)
-                    os << "&quot;";
-                else
-                    os << c;
-                break;
-
-            default:
-                // Check for control characters and invalid utf-8
-
-                // Escape control characters in standard ascii
-                // see http://stackoverflow.com/questions/404107/why-are-control-characters-illegal-in-xml-1-0
-                if (c < 0x09 || (c > 0x0D && c < 0x20) || c == 0x7F) {
-                    hexEscapeChar(os, c);
-                    break;
-                }
-
-                // Plain ASCII: Write it to stream
-                if (c < 0x7F) {
-                    os << c;
-                    break;
-                }
-
-                // UTF-8 territory
-                // Check if the encoding is valid and if it is not, hex escape bytes.
-                // Important: We do not check the exact decoded values for validity, only the encoding format
-                // First check that this bytes is a valid lead byte:
-                // This means that it is not encoded as 1111 1XXX
-                // Or as 10XX XXXX
-                if (c <  0xC0 ||
-                    c >= 0xF8) {
-                    hexEscapeChar(os, c);
-                    break;
-                }
-
-                auto encBytes = trailingBytes(c);
-                // Are there enough bytes left to avoid accessing out-of-bounds memory?
-                if (idx + encBytes - 1 >= m_str.size()) {
-                    hexEscapeChar(os, c);
-                    break;
-                }
-                // The header is valid, check data
-                // The next encBytes bytes must together be a valid utf-8
-                // This means: bitpattern 10XX XXXX and the extracted value is sane (ish)
-                bool valid = true;
-                uint32_t value = headerValue(c);
-                for (std::size_t n = 1; n < encBytes; ++n) {
-                    uchar nc = m_str[idx + n];
-                    valid &= ((nc & 0xC0) == 0x80);
-                    value = (value << 6) | (nc & 0x3F);
-                }
-
-                if (
-                    // Wrong bit pattern of following bytes
-                    (!valid) ||
-                    // Overlong encodings
-                    (value < 0x80) ||
-                    (0x80 <= value && value < 0x800   && encBytes > 2) ||
-                    (0x800 < value && value < 0x10000 && encBytes > 3) ||
-                    // Encoded value out of range
-                    (value >= 0x110000)
-                    ) {
-                    hexEscapeChar(os, c);
-                    break;
-                }
-
-                // If we got here, this is in fact a valid(ish) utf-8 sequence
-                for (std::size_t n = 0; n < encBytes; ++n) {
-                    os << m_str[idx + n];
-                }
-                idx += encBytes - 1;
-                break;
-            }
-        }
-    }
-
-    std::ostream& operator << ( std::ostream& os, XmlEncode const& xmlEncode ) {
-        xmlEncode.encodeTo( os );
-        return os;
-    }
-
-    XmlWriter::ScopedElement::ScopedElement( XmlWriter* writer )
-    :   m_writer( writer )
-    {}
-
-    XmlWriter::ScopedElement::ScopedElement( ScopedElement&& other ) noexcept
-    :   m_writer( other.m_writer ){
-        other.m_writer = nullptr;
-    }
-    XmlWriter::ScopedElement& XmlWriter::ScopedElement::operator=( ScopedElement&& other ) noexcept {
-        if ( m_writer ) {
-            m_writer->endElement();
-        }
-        m_writer = other.m_writer;
-        other.m_writer = nullptr;
-        return *this;
-    }
-
-    XmlWriter::ScopedElement::~ScopedElement() {
-        if( m_writer )
-            m_writer->endElement();
-    }
-
-    XmlWriter::ScopedElement& XmlWriter::ScopedElement::writeText( std::string const& text, bool indent ) {
-        m_writer->writeText( text, indent );
-        return *this;
-    }
-
-    XmlWriter::XmlWriter( std::ostream& os ) : m_os( os )
-    {
-        writeDeclaration();
-    }
-
-    XmlWriter::~XmlWriter() {
-        while( !m_tags.empty() )
-            endElement();
-    }
-
-    XmlWriter& XmlWriter::startElement( std::string const& name ) {
-        ensureTagClosed();
-        newlineIfNecessary();
-        m_os << m_indent << '<' << name;
-        m_tags.push_back( name );
-        m_indent += "  ";
-        m_tagIsOpen = true;
-        return *this;
-    }
-
-    XmlWriter::ScopedElement XmlWriter::scopedElement( std::string const& name ) {
-        ScopedElement scoped( this );
-        startElement( name );
-        return scoped;
-    }
-
-    XmlWriter& XmlWriter::endElement() {
-        newlineIfNecessary();
-        m_indent = m_indent.substr( 0, m_indent.size()-2 );
-        if( m_tagIsOpen ) {
-            m_os << "/>";
-            m_tagIsOpen = false;
-        }
-        else {
-            m_os << m_indent << "</" << m_tags.back() << ">";
-        }
-        m_os << std::endl;
-        m_tags.pop_back();
-        return *this;
-    }
-
-    XmlWriter& XmlWriter::writeAttribute( std::string const& name, std::string const& attribute ) {
-        if( !name.empty() && !attribute.empty() )
-            m_os << ' ' << name << "=\"" << XmlEncode( attribute, XmlEncode::ForAttributes ) << '"';
-        return *this;
-    }
-
-    XmlWriter& XmlWriter::writeAttribute( std::string const& name, bool attribute ) {
-        m_os << ' ' << name << "=\"" << ( attribute ? "true" : "false" ) << '"';
-        return *this;
-    }
-
-    XmlWriter& XmlWriter::writeText( std::string const& text, bool indent ) {
-        if( !text.empty() ){
-            bool tagWasOpen = m_tagIsOpen;
-            ensureTagClosed();
-            if( tagWasOpen && indent )
-                m_os << m_indent;
-            m_os << XmlEncode( text );
-            m_needsNewline = true;
-        }
-        return *this;
-    }
-
-    XmlWriter& XmlWriter::writeComment( std::string const& text ) {
-        ensureTagClosed();
-        m_os << m_indent << "<!--" << text << "-->";
-        m_needsNewline = true;
-        return *this;
-    }
-
-    void XmlWriter::writeStylesheetRef( std::string const& url ) {
-        m_os << "<?xml-stylesheet type=\"text/xsl\" href=\"" << url << "\"?>\n";
-    }
-
-    XmlWriter& XmlWriter::writeBlankLine() {
-        ensureTagClosed();
-        m_os << '\n';
-        return *this;
-    }
-
-    void XmlWriter::ensureTagClosed() {
-        if( m_tagIsOpen ) {
-            m_os << ">" << std::endl;
-            m_tagIsOpen = false;
-        }
-    }
-
-    void XmlWriter::writeDeclaration() {
-        m_os << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
-    }
-
-    void XmlWriter::newlineIfNecessary() {
-        if( m_needsNewline ) {
-            m_os << std::endl;
-            m_needsNewline = false;
-        }
-    }
-}
-// end catch_xmlwriter.cpp
-// start catch_reporter_bases.cpp
-
-#include <cstring>
-#include <cfloat>
-#include <cstdio>
-#include <cassert>
-#include <memory>
-
-namespace Catch {
-    void prepareExpandedExpression(AssertionResult& result) {
-        result.getExpandedExpression();
-    }
-
-    // Because formatting using c++ streams is stateful, drop down to C is required
-    // Alternatively we could use stringstream, but its performance is... not good.
-    std::string getFormattedDuration( double duration ) {
-        // Max exponent + 1 is required to represent the whole part
-        // + 1 for decimal point
-        // + 3 for the 3 decimal places
-        // + 1 for null terminator
-        const std::size_t maxDoubleSize = DBL_MAX_10_EXP + 1 + 1 + 3 + 1;
-        char buffer[maxDoubleSize];
-
-        // Save previous errno, to prevent sprintf from overwriting it
-        ErrnoGuard guard;
-#ifdef _MSC_VER
-        sprintf_s(buffer, "%.3f", duration);
-#else
-        std::sprintf(buffer, "%.3f", duration);
-#endif
-        return std::string(buffer);
-    }
-
-    std::string serializeFilters( std::vector<std::string> const& container ) {
-        ReusableStringStream oss;
-        bool first = true;
-        for (auto&& filter : container)
-        {
-            if (!first)
-                oss << ' ';
-            else
-                first = false;
-
-            oss << filter;
-        }
-        return oss.str();
-    }
-
-    TestEventListenerBase::TestEventListenerBase(ReporterConfig const & _config)
-        :StreamingReporterBase(_config) {}
-
-    std::set<Verbosity> TestEventListenerBase::getSupportedVerbosities() {
-        return { Verbosity::Quiet, Verbosity::Normal, Verbosity::High };
-    }
-
-    void TestEventListenerBase::assertionStarting(AssertionInfo const &) {}
-
-    bool TestEventListenerBase::assertionEnded(AssertionStats const &) {
-        return false;
-    }
-
-} // end namespace Catch
-// end catch_reporter_bases.cpp
-// start catch_reporter_compact.cpp
-
-namespace {
-
-#ifdef CATCH_PLATFORM_MAC
-    const char* failedString() { return "FAILED"; }
-    const char* passedString() { return "PASSED"; }
-#else
-    const char* failedString() { return "failed"; }
-    const char* passedString() { return "passed"; }
-#endif
-
-    // Colour::LightGrey
-    Catch::Colour::Code dimColour() { return Catch::Colour::FileName; }
-
-    std::string bothOrAll( std::size_t count ) {
-        return count == 1 ? std::string() :
-               count == 2 ? "both " : "all " ;
-    }
-
-} // anon namespace
-
-namespace Catch {
-namespace {
-// Colour, message variants:
-// - white: No tests ran.
-// -   red: Failed [both/all] N test cases, failed [both/all] M assertions.
-// - white: Passed [both/all] N test cases (no assertions).
-// -   red: Failed N tests cases, failed M assertions.
-// - green: Passed [both/all] N tests cases with M assertions.
-void printTotals(std::ostream& out, const Totals& totals) {
-    if (totals.testCases.total() == 0) {
-        out << "No tests ran.";
-    } else if (totals.testCases.failed == totals.testCases.total()) {
-        Colour colour(Colour::ResultError);
-        const std::string qualify_assertions_failed =
-            totals.assertions.failed == totals.assertions.total() ?
-            bothOrAll(totals.assertions.failed) : std::string();
-        out <<
-            "Failed " << bothOrAll(totals.testCases.failed)
-            << pluralise(totals.testCases.failed, "test case") << ", "
-            "failed " << qualify_assertions_failed <<
-            pluralise(totals.assertions.failed, "assertion") << '.';
-    } else if (totals.assertions.total() == 0) {
-        out <<
-            "Passed " << bothOrAll(totals.testCases.total())
-            << pluralise(totals.testCases.total(), "test case")
-            << " (no assertions).";
-    } else if (totals.assertions.failed) {
-        Colour colour(Colour::ResultError);
-        out <<
-            "Failed " << pluralise(totals.testCases.failed, "test case") << ", "
-            "failed " << pluralise(totals.assertions.failed, "assertion") << '.';
-    } else {
-        Colour colour(Colour::ResultSuccess);
-        out <<
-            "Passed " << bothOrAll(totals.testCases.passed)
-            << pluralise(totals.testCases.passed, "test case") <<
-            " with " << pluralise(totals.assertions.passed, "assertion") << '.';
-    }
-}
-
-// Implementation of CompactReporter formatting
-class AssertionPrinter {
-public:
-    AssertionPrinter& operator= (AssertionPrinter const&) = delete;
-    AssertionPrinter(AssertionPrinter const&) = delete;
-    AssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
-        : stream(_stream)
-        , result(_stats.assertionResult)
-        , messages(_stats.infoMessages)
-        , itMessage(_stats.infoMessages.begin())
-        , printInfoMessages(_printInfoMessages) {}
-
-    void print() {
-        printSourceInfo();
-
-        itMessage = messages.begin();
-
-        switch (result.getResultType()) {
-        case ResultWas::Ok:
-            printResultType(Colour::ResultSuccess, passedString());
-            printOriginalExpression();
-            printReconstructedExpression();
-            if (!result.hasExpression())
-                printRemainingMessages(Colour::None);
-            else
-                printRemainingMessages();
-            break;
-        case ResultWas::ExpressionFailed:
-            if (result.isOk())
-                printResultType(Colour::ResultSuccess, failedString() + std::string(" - but was ok"));
-            else
-                printResultType(Colour::Error, failedString());
-            printOriginalExpression();
-            printReconstructedExpression();
-            printRemainingMessages();
-            break;
-        case ResultWas::ThrewException:
-            printResultType(Colour::Error, failedString());
-            printIssue("unexpected exception with message:");
-            printMessage();
-            printExpressionWas();
-            printRemainingMessages();
-            break;
-        case ResultWas::FatalErrorCondition:
-            printResultType(Colour::Error, failedString());
-            printIssue("fatal error condition with message:");
-            printMessage();
-            printExpressionWas();
-            printRemainingMessages();
-            break;
-        case ResultWas::DidntThrowException:
-            printResultType(Colour::Error, failedString());
-            printIssue("expected exception, got none");
-            printExpressionWas();
-            printRemainingMessages();
-            break;
-        case ResultWas::Info:
-            printResultType(Colour::None, "info");
-            printMessage();
-            printRemainingMessages();
-            break;
-        case ResultWas::Warning:
-            printResultType(Colour::None, "warning");
-            printMessage();
-            printRemainingMessages();
-            break;
-        case ResultWas::ExplicitFailure:
-            printResultType(Colour::Error, failedString());
-            printIssue("explicitly");
-            printRemainingMessages(Colour::None);
-            break;
-            // These cases are here to prevent compiler warnings
-        case ResultWas::Unknown:
-        case ResultWas::FailureBit:
-        case ResultWas::Exception:
-            printResultType(Colour::Error, "** internal error **");
-            break;
-        }
-    }
-
-private:
-    void printSourceInfo() const {
-        Colour colourGuard(Colour::FileName);
-        stream << result.getSourceInfo() << ':';
-    }
-
-    void printResultType(Colour::Code colour, std::string const& passOrFail) const {
-        if (!passOrFail.empty()) {
-            {
-                Colour colourGuard(colour);
-                stream << ' ' << passOrFail;
-            }
-            stream << ':';
-        }
-    }
-
-    void printIssue(std::string const& issue) const {
-        stream << ' ' << issue;
-    }
-
-    void printExpressionWas() {
-        if (result.hasExpression()) {
-            stream << ';';
-            {
-                Colour colour(dimColour());
-                stream << " expression was:";
-            }
-            printOriginalExpression();
-        }
-    }
-
-    void printOriginalExpression() const {
-        if (result.hasExpression()) {
-            stream << ' ' << result.getExpression();
-        }
-    }
-
-    void printReconstructedExpression() const {
-        if (result.hasExpandedExpression()) {
-            {
-                Colour colour(dimColour());
-                stream << " for: ";
-            }
-            stream << result.getExpandedExpression();
-        }
-    }
-
-    void printMessage() {
-        if (itMessage != messages.end()) {
-            stream << " '" << itMessage->message << '\'';
-            ++itMessage;
-        }
-    }
-
-    void printRemainingMessages(Colour::Code colour = dimColour()) {
-        if (itMessage == messages.end())
-            return;
-
-        // using messages.end() directly yields (or auto) compilation error:
-        std::vector<MessageInfo>::const_iterator itEnd = messages.end();
-        const std::size_t N = static_cast<std::size_t>(std::distance(itMessage, itEnd));
-
-        {
-            Colour colourGuard(colour);
-            stream << " with " << pluralise(N, "message") << ':';
-        }
-
-        for (; itMessage != itEnd; ) {
-            // If this assertion is a warning ignore any INFO messages
-            if (printInfoMessages || itMessage->type != ResultWas::Info) {
-                stream << " '" << itMessage->message << '\'';
-                if (++itMessage != itEnd) {
-                    Colour colourGuard(dimColour());
-                    stream << " and";
-                }
-            }
-        }
-    }
-
-private:
-    std::ostream& stream;
-    AssertionResult const& result;
-    std::vector<MessageInfo> messages;
-    std::vector<MessageInfo>::const_iterator itMessage;
-    bool printInfoMessages;
-};
-
-} // anon namespace
-
-        std::string CompactReporter::getDescription() {
-            return "Reports test results on a single line, suitable for IDEs";
-        }
-
-        ReporterPreferences CompactReporter::getPreferences() const {
-            return m_reporterPrefs;
-        }
-
-        void CompactReporter::noMatchingTestCases( std::string const& spec ) {
-            stream << "No test cases matched '" << spec << '\'' << std::endl;
-        }
-
-        void CompactReporter::assertionStarting( AssertionInfo const& ) {}
-
-        bool CompactReporter::assertionEnded( AssertionStats const& _assertionStats ) {
-            AssertionResult const& result = _assertionStats.assertionResult;
-
-            bool printInfoMessages = true;
-
-            // Drop out if result was successful and we're not printing those
-            if( !m_config->includeSuccessfulResults() && result.isOk() ) {
-                if( result.getResultType() != ResultWas::Warning )
-                    return false;
-                printInfoMessages = false;
-            }
-
-            AssertionPrinter printer( stream, _assertionStats, printInfoMessages );
-            printer.print();
-
-            stream << std::endl;
-            return true;
-        }
-
-        void CompactReporter::sectionEnded(SectionStats const& _sectionStats) {
-            if (m_config->showDurations() == ShowDurations::Always) {
-                stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
-            }
-        }
-
-        void CompactReporter::testRunEnded( TestRunStats const& _testRunStats ) {
-            printTotals( stream, _testRunStats.totals );
-            stream << '\n' << std::endl;
-            StreamingReporterBase::testRunEnded( _testRunStats );
-        }
-
-        CompactReporter::~CompactReporter() {}
-
-    CATCH_REGISTER_REPORTER( "compact", CompactReporter )
-
-} // end namespace Catch
-// end catch_reporter_compact.cpp
-// start catch_reporter_console.cpp
-
-#include <cfloat>
-#include <cstdio>
-
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
- // Note that 4062 (not all labels are handled
- // and default is missing) is enabled
-#endif
-
-namespace Catch {
-
-namespace {
-
-// Formatter impl for ConsoleReporter
-class ConsoleAssertionPrinter {
-public:
-    ConsoleAssertionPrinter& operator= (ConsoleAssertionPrinter const&) = delete;
-    ConsoleAssertionPrinter(ConsoleAssertionPrinter const&) = delete;
-    ConsoleAssertionPrinter(std::ostream& _stream, AssertionStats const& _stats, bool _printInfoMessages)
-        : stream(_stream),
-        stats(_stats),
-        result(_stats.assertionResult),
-        colour(Colour::None),
-        message(result.getMessage()),
-        messages(_stats.infoMessages),
-        printInfoMessages(_printInfoMessages) {
-        switch (result.getResultType()) {
-        case ResultWas::Ok:
-            colour = Colour::Success;
-            passOrFail = "PASSED";
-            //if( result.hasMessage() )
-            if (_stats.infoMessages.size() == 1)
-                messageLabel = "with message";
-            if (_stats.infoMessages.size() > 1)
-                messageLabel = "with messages";
-            break;
-        case ResultWas::ExpressionFailed:
-            if (result.isOk()) {
-                colour = Colour::Success;
-                passOrFail = "FAILED - but was ok";
-            } else {
-                colour = Colour::Error;
-                passOrFail = "FAILED";
-            }
-            if (_stats.infoMessages.size() == 1)
-                messageLabel = "with message";
-            if (_stats.infoMessages.size() > 1)
-                messageLabel = "with messages";
-            break;
-        case ResultWas::ThrewException:
-            colour = Colour::Error;
-            passOrFail = "FAILED";
-            messageLabel = "due to unexpected exception with ";
-            if (_stats.infoMessages.size() == 1)
-                messageLabel += "message";
-            if (_stats.infoMessages.size() > 1)
-                messageLabel += "messages";
-            break;
-        case ResultWas::FatalErrorCondition:
-            colour = Colour::Error;
-            passOrFail = "FAILED";
-            messageLabel = "due to a fatal error condition";
-            break;
-        case ResultWas::DidntThrowException:
-            colour = Colour::Error;
-            passOrFail = "FAILED";
-            messageLabel = "because no exception was thrown where one was expected";
-            break;
-        case ResultWas::Info:
-            messageLabel = "info";
-            break;
-        case ResultWas::Warning:
-            messageLabel = "warning";
-            break;
-        case ResultWas::ExplicitFailure:
-            passOrFail = "FAILED";
-            colour = Colour::Error;
-            if (_stats.infoMessages.size() == 1)
-                messageLabel = "explicitly with message";
-            if (_stats.infoMessages.size() > 1)
-                messageLabel = "explicitly with messages";
-            break;
-            // These cases are here to prevent compiler warnings
-        case ResultWas::Unknown:
-        case ResultWas::FailureBit:
-        case ResultWas::Exception:
-            passOrFail = "** internal error **";
-            colour = Colour::Error;
-            break;
-        }
-    }
-
-    void print() const {
-        printSourceInfo();
-        if (stats.totals.assertions.total() > 0) {
-            printResultType();
-            printOriginalExpression();
-            printReconstructedExpression();
-        } else {
-            stream << '\n';
-        }
-        printMessage();
-    }
-
-private:
-    void printResultType() const {
-        if (!passOrFail.empty()) {
-            Colour colourGuard(colour);
-            stream << passOrFail << ":\n";
-        }
-    }
-    void printOriginalExpression() const {
-        if (result.hasExpression()) {
-            Colour colourGuard(Colour::OriginalExpression);
-            stream << "  ";
-            stream << result.getExpressionInMacro();
-            stream << '\n';
-        }
-    }
-    void printReconstructedExpression() const {
-        if (result.hasExpandedExpression()) {
-            stream << "with expansion:\n";
-            Colour colourGuard(Colour::ReconstructedExpression);
-            stream << Column(result.getExpandedExpression()).indent(2) << '\n';
-        }
-    }
-    void printMessage() const {
-        if (!messageLabel.empty())
-            stream << messageLabel << ':' << '\n';
-        for (auto const& msg : messages) {
-            // If this assertion is a warning ignore any INFO messages
-            if (printInfoMessages || msg.type != ResultWas::Info)
-                stream << Column(msg.message).indent(2) << '\n';
-        }
-    }
-    void printSourceInfo() const {
-        Colour colourGuard(Colour::FileName);
-        stream << result.getSourceInfo() << ": ";
-    }
-
-    std::ostream& stream;
-    AssertionStats const& stats;
-    AssertionResult const& result;
-    Colour::Code colour;
-    std::string passOrFail;
-    std::string messageLabel;
-    std::string message;
-    std::vector<MessageInfo> messages;
-    bool printInfoMessages;
-};
-
-std::size_t makeRatio(std::size_t number, std::size_t total) {
-    std::size_t ratio = total > 0 ? CATCH_CONFIG_CONSOLE_WIDTH * number / total : 0;
-    return (ratio == 0 && number > 0) ? 1 : ratio;
-}
-
-std::size_t& findMax(std::size_t& i, std::size_t& j, std::size_t& k) {
-    if (i > j && i > k)
-        return i;
-    else if (j > k)
-        return j;
-    else
-        return k;
-}
-
-struct ColumnInfo {
-    enum Justification { Left, Right };
-    std::string name;
-    int width;
-    Justification justification;
-};
-struct ColumnBreak {};
-struct RowBreak {};
-
-class Duration {
-    enum class Unit {
-        Auto,
-        Nanoseconds,
-        Microseconds,
-        Milliseconds,
-        Seconds,
-        Minutes
-    };
-    static const uint64_t s_nanosecondsInAMicrosecond = 1000;
-    static const uint64_t s_nanosecondsInAMillisecond = 1000 * s_nanosecondsInAMicrosecond;
-    static const uint64_t s_nanosecondsInASecond = 1000 * s_nanosecondsInAMillisecond;
-    static const uint64_t s_nanosecondsInAMinute = 60 * s_nanosecondsInASecond;
-
-    uint64_t m_inNanoseconds;
-    Unit m_units;
-
-public:
-    explicit Duration(uint64_t inNanoseconds, Unit units = Unit::Auto)
-        : m_inNanoseconds(inNanoseconds),
-        m_units(units) {
-        if (m_units == Unit::Auto) {
-            if (m_inNanoseconds < s_nanosecondsInAMicrosecond)
-                m_units = Unit::Nanoseconds;
-            else if (m_inNanoseconds < s_nanosecondsInAMillisecond)
-                m_units = Unit::Microseconds;
-            else if (m_inNanoseconds < s_nanosecondsInASecond)
-                m_units = Unit::Milliseconds;
-            else if (m_inNanoseconds < s_nanosecondsInAMinute)
-                m_units = Unit::Seconds;
-            else
-                m_units = Unit::Minutes;
-        }
-
-    }
-
-    auto value() const -> double {
-        switch (m_units) {
-        case Unit::Microseconds:
-            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMicrosecond);
-        case Unit::Milliseconds:
-            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMillisecond);
-        case Unit::Seconds:
-            return m_inNanoseconds / static_cast<double>(s_nanosecondsInASecond);
-        case Unit::Minutes:
-            return m_inNanoseconds / static_cast<double>(s_nanosecondsInAMinute);
-        default:
-            return static_cast<double>(m_inNanoseconds);
-        }
-    }
-    auto unitsAsString() const -> std::string {
-        switch (m_units) {
-        case Unit::Nanoseconds:
-            return "ns";
-        case Unit::Microseconds:
-            return "us";
-        case Unit::Milliseconds:
-            return "ms";
-        case Unit::Seconds:
-            return "s";
-        case Unit::Minutes:
-            return "m";
-        default:
-            return "** internal error **";
-        }
-
-    }
-    friend auto operator << (std::ostream& os, Duration const& duration) -> std::ostream& {
-        return os << duration.value() << " " << duration.unitsAsString();
-    }
-};
-} // end anon namespace
-
-class TablePrinter {
-    std::ostream& m_os;
-    std::vector<ColumnInfo> m_columnInfos;
-    std::ostringstream m_oss;
-    int m_currentColumn = -1;
-    bool m_isOpen = false;
-
-public:
-    TablePrinter( std::ostream& os, std::vector<ColumnInfo> columnInfos )
-    :   m_os( os ),
-        m_columnInfos( std::move( columnInfos ) ) {}
-
-    auto columnInfos() const -> std::vector<ColumnInfo> const& {
-        return m_columnInfos;
-    }
-
-    void open() {
-        if (!m_isOpen) {
-            m_isOpen = true;
-            *this << RowBreak();
-            for (auto const& info : m_columnInfos)
-                *this << info.name << ColumnBreak();
-            *this << RowBreak();
-            m_os << Catch::getLineOfChars<'-'>() << "\n";
-        }
-    }
-    void close() {
-        if (m_isOpen) {
-            *this << RowBreak();
-            m_os << std::endl;
-            m_isOpen = false;
-        }
-    }
-
-    template<typename T>
-    friend TablePrinter& operator << (TablePrinter& tp, T const& value) {
-        tp.m_oss << value;
-        return tp;
-    }
-
-    friend TablePrinter& operator << (TablePrinter& tp, ColumnBreak) {
-        auto colStr = tp.m_oss.str();
-        // This takes account of utf8 encodings
-        auto strSize = Catch::StringRef(colStr).numberOfCharacters();
-        tp.m_oss.str("");
-        tp.open();
-        if (tp.m_currentColumn == static_cast<int>(tp.m_columnInfos.size() - 1)) {
-            tp.m_currentColumn = -1;
-            tp.m_os << "\n";
-        }
-        tp.m_currentColumn++;
-
-        auto colInfo = tp.m_columnInfos[tp.m_currentColumn];
-        auto padding = (strSize + 2 < static_cast<std::size_t>(colInfo.width))
-            ? std::string(colInfo.width - (strSize + 2), ' ')
-            : std::string();
-        if (colInfo.justification == ColumnInfo::Left)
-            tp.m_os << colStr << padding << " ";
-        else
-            tp.m_os << padding << colStr << " ";
-        return tp;
-    }
-
-    friend TablePrinter& operator << (TablePrinter& tp, RowBreak) {
-        if (tp.m_currentColumn > 0) {
-            tp.m_os << "\n";
-            tp.m_currentColumn = -1;
-        }
-        return tp;
-    }
-};
-
-ConsoleReporter::ConsoleReporter(ReporterConfig const& config)
-    : StreamingReporterBase(config),
-    m_tablePrinter(new TablePrinter(config.stream(),
-    {
-        { "benchmark name", CATCH_CONFIG_CONSOLE_WIDTH - 32, ColumnInfo::Left },
-        { "iters", 8, ColumnInfo::Right },
-        { "elapsed ns", 14, ColumnInfo::Right },
-        { "average", 14, ColumnInfo::Right }
-    })) {}
-ConsoleReporter::~ConsoleReporter() = default;
-
-std::string ConsoleReporter::getDescription() {
-    return "Reports test results as plain lines of text";
-}
-
-void ConsoleReporter::noMatchingTestCases(std::string const& spec) {
-    stream << "No test cases matched '" << spec << '\'' << std::endl;
-}
-
-void ConsoleReporter::assertionStarting(AssertionInfo const&) {}
-
-bool ConsoleReporter::assertionEnded(AssertionStats const& _assertionStats) {
-    AssertionResult const& result = _assertionStats.assertionResult;
-
-    bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
-
-    // Drop out if result was successful but we're not printing them.
-    if (!includeResults && result.getResultType() != ResultWas::Warning)
-        return false;
-
-    lazyPrint();
-
-    ConsoleAssertionPrinter printer(stream, _assertionStats, includeResults);
-    printer.print();
-    stream << std::endl;
-    return true;
-}
-
-void ConsoleReporter::sectionStarting(SectionInfo const& _sectionInfo) {
-    m_headerPrinted = false;
-    StreamingReporterBase::sectionStarting(_sectionInfo);
-}
-void ConsoleReporter::sectionEnded(SectionStats const& _sectionStats) {
-    m_tablePrinter->close();
-    if (_sectionStats.missingAssertions) {
-        lazyPrint();
-        Colour colour(Colour::ResultError);
-        if (m_sectionStack.size() > 1)
-            stream << "\nNo assertions in section";
-        else
-            stream << "\nNo assertions in test case";
-        stream << " '" << _sectionStats.sectionInfo.name << "'\n" << std::endl;
-    }
-    if (m_config->showDurations() == ShowDurations::Always) {
-        stream << getFormattedDuration(_sectionStats.durationInSeconds) << " s: " << _sectionStats.sectionInfo.name << std::endl;
-    }
-    if (m_headerPrinted) {
-        m_headerPrinted = false;
-    }
-    StreamingReporterBase::sectionEnded(_sectionStats);
-}
-
-void ConsoleReporter::benchmarkStarting(BenchmarkInfo const& info) {
-    lazyPrintWithoutClosingBenchmarkTable();
-
-    auto nameCol = Column( info.name ).width( static_cast<std::size_t>( m_tablePrinter->columnInfos()[0].width - 2 ) );
-
-    bool firstLine = true;
-    for (auto line : nameCol) {
-        if (!firstLine)
-            (*m_tablePrinter) << ColumnBreak() << ColumnBreak() << ColumnBreak();
-        else
-            firstLine = false;
-
-        (*m_tablePrinter) << line << ColumnBreak();
-    }
-}
-void ConsoleReporter::benchmarkEnded(BenchmarkStats const& stats) {
-    Duration average(stats.elapsedTimeInNanoseconds / stats.iterations);
-    (*m_tablePrinter)
-        << stats.iterations << ColumnBreak()
-        << stats.elapsedTimeInNanoseconds << ColumnBreak()
-        << average << ColumnBreak();
-}
-
-void ConsoleReporter::testCaseEnded(TestCaseStats const& _testCaseStats) {
-    m_tablePrinter->close();
-    StreamingReporterBase::testCaseEnded(_testCaseStats);
-    m_headerPrinted = false;
-}
-void ConsoleReporter::testGroupEnded(TestGroupStats const& _testGroupStats) {
-    if (currentGroupInfo.used) {
-        printSummaryDivider();
-        stream << "Summary for group '" << _testGroupStats.groupInfo.name << "':\n";
-        printTotals(_testGroupStats.totals);
-        stream << '\n' << std::endl;
-    }
-    StreamingReporterBase::testGroupEnded(_testGroupStats);
-}
-void ConsoleReporter::testRunEnded(TestRunStats const& _testRunStats) {
-    printTotalsDivider(_testRunStats.totals);
-    printTotals(_testRunStats.totals);
-    stream << std::endl;
-    StreamingReporterBase::testRunEnded(_testRunStats);
-}
-void ConsoleReporter::testRunStarting(TestRunInfo const& _testInfo) {
-    StreamingReporterBase::testRunStarting(_testInfo);
-    printTestFilters();
-}
-
-void ConsoleReporter::lazyPrint() {
-
-    m_tablePrinter->close();
-    lazyPrintWithoutClosingBenchmarkTable();
-}
-
-void ConsoleReporter::lazyPrintWithoutClosingBenchmarkTable() {
-
-    if (!currentTestRunInfo.used)
-        lazyPrintRunInfo();
-    if (!currentGroupInfo.used)
-        lazyPrintGroupInfo();
-
-    if (!m_headerPrinted) {
-        printTestCaseAndSectionHeader();
-        m_headerPrinted = true;
-    }
-}
-void ConsoleReporter::lazyPrintRunInfo() {
-    stream << '\n' << getLineOfChars<'~'>() << '\n';
-    Colour colour(Colour::SecondaryText);
-    stream << currentTestRunInfo->name
-        << " is a Catch v" << libraryVersion() << " host application.\n"
-        << "Run with -? for options\n\n";
-
-    if (m_config->rngSeed() != 0)
-        stream << "Randomness seeded to: " << m_config->rngSeed() << "\n\n";
-
-    currentTestRunInfo.used = true;
-}
-void ConsoleReporter::lazyPrintGroupInfo() {
-    if (!currentGroupInfo->name.empty() && currentGroupInfo->groupsCounts > 1) {
-        printClosedHeader("Group: " + currentGroupInfo->name);
-        currentGroupInfo.used = true;
-    }
-}
-void ConsoleReporter::printTestCaseAndSectionHeader() {
-    assert(!m_sectionStack.empty());
-    printOpenHeader(currentTestCaseInfo->name);
-
-    if (m_sectionStack.size() > 1) {
-        Colour colourGuard(Colour::Headers);
-
-        auto
-            it = m_sectionStack.begin() + 1, // Skip first section (test case)
-            itEnd = m_sectionStack.end();
-        for (; it != itEnd; ++it)
-            printHeaderString(it->name, 2);
-    }
-
-    SourceLineInfo lineInfo = m_sectionStack.back().lineInfo;
-
-    if (!lineInfo.empty()) {
-        stream << getLineOfChars<'-'>() << '\n';
-        Colour colourGuard(Colour::FileName);
-        stream << lineInfo << '\n';
-    }
-    stream << getLineOfChars<'.'>() << '\n' << std::endl;
-}
-
-void ConsoleReporter::printClosedHeader(std::string const& _name) {
-    printOpenHeader(_name);
-    stream << getLineOfChars<'.'>() << '\n';
-}
-void ConsoleReporter::printOpenHeader(std::string const& _name) {
-    stream << getLineOfChars<'-'>() << '\n';
-    {
-        Colour colourGuard(Colour::Headers);
-        printHeaderString(_name);
-    }
-}
-
-// if string has a : in first line will set indent to follow it on
-// subsequent lines
-void ConsoleReporter::printHeaderString(std::string const& _string, std::size_t indent) {
-    std::size_t i = _string.find(": ");
-    if (i != std::string::npos)
-        i += 2;
-    else
-        i = 0;
-    stream << Column(_string).indent(indent + i).initialIndent(indent) << '\n';
-}
-
-struct SummaryColumn {
-
-    SummaryColumn( std::string _label, Colour::Code _colour )
-    :   label( std::move( _label ) ),
-        colour( _colour ) {}
-    SummaryColumn addRow( std::size_t count ) {
-        ReusableStringStream rss;
-        rss << count;
-        std::string row = rss.str();
-        for (auto& oldRow : rows) {
-            while (oldRow.size() < row.size())
-                oldRow = ' ' + oldRow;
-            while (oldRow.size() > row.size())
-                row = ' ' + row;
-        }
-        rows.push_back(row);
-        return *this;
-    }
-
-    std::string label;
-    Colour::Code colour;
-    std::vector<std::string> rows;
-
-};
-
-void ConsoleReporter::printTotals( Totals const& totals ) {
-    if (totals.testCases.total() == 0) {
-        stream << Colour(Colour::Warning) << "No tests ran\n";
-    } else if (totals.assertions.total() > 0 && totals.testCases.allPassed()) {
-        stream << Colour(Colour::ResultSuccess) << "All tests passed";
-        stream << " ("
-            << pluralise(totals.assertions.passed, "assertion") << " in "
-            << pluralise(totals.testCases.passed, "test case") << ')'
-            << '\n';
-    } else {
-
-        std::vector<SummaryColumn> columns;
-        columns.push_back(SummaryColumn("", Colour::None)
-                          .addRow(totals.testCases.total())
-                          .addRow(totals.assertions.total()));
-        columns.push_back(SummaryColumn("passed", Colour::Success)
-                          .addRow(totals.testCases.passed)
-                          .addRow(totals.assertions.passed));
-        columns.push_back(SummaryColumn("failed", Colour::ResultError)
-                          .addRow(totals.testCases.failed)
-                          .addRow(totals.assertions.failed));
-        columns.push_back(SummaryColumn("failed as expected", Colour::ResultExpectedFailure)
-                          .addRow(totals.testCases.failedButOk)
-                          .addRow(totals.assertions.failedButOk));
-
-        printSummaryRow("test cases", columns, 0);
-        printSummaryRow("assertions", columns, 1);
-    }
-}
-void ConsoleReporter::printSummaryRow(std::string const& label, std::vector<SummaryColumn> const& cols, std::size_t row) {
-    for (auto col : cols) {
-        std::string value = col.rows[row];
-        if (col.label.empty()) {
-            stream << label << ": ";
-            if (value != "0")
-                stream << value;
-            else
-                stream << Colour(Colour::Warning) << "- none -";
-        } else if (value != "0") {
-            stream << Colour(Colour::LightGrey) << " | ";
-            stream << Colour(col.colour)
-                << value << ' ' << col.label;
-        }
-    }
-    stream << '\n';
-}
-
-void ConsoleReporter::printTotalsDivider(Totals const& totals) {
-    if (totals.testCases.total() > 0) {
-        std::size_t failedRatio = makeRatio(totals.testCases.failed, totals.testCases.total());
-        std::size_t failedButOkRatio = makeRatio(totals.testCases.failedButOk, totals.testCases.total());
-        std::size_t passedRatio = makeRatio(totals.testCases.passed, totals.testCases.total());
-        while (failedRatio + failedButOkRatio + passedRatio < CATCH_CONFIG_CONSOLE_WIDTH - 1)
-            findMax(failedRatio, failedButOkRatio, passedRatio)++;
-        while (failedRatio + failedButOkRatio + passedRatio > CATCH_CONFIG_CONSOLE_WIDTH - 1)
-            findMax(failedRatio, failedButOkRatio, passedRatio)--;
-
-        stream << Colour(Colour::Error) << std::string(failedRatio, '=');
-        stream << Colour(Colour::ResultExpectedFailure) << std::string(failedButOkRatio, '=');
-        if (totals.testCases.allPassed())
-            stream << Colour(Colour::ResultSuccess) << std::string(passedRatio, '=');
-        else
-            stream << Colour(Colour::Success) << std::string(passedRatio, '=');
-    } else {
-        stream << Colour(Colour::Warning) << std::string(CATCH_CONFIG_CONSOLE_WIDTH - 1, '=');
-    }
-    stream << '\n';
-}
-void ConsoleReporter::printSummaryDivider() {
-    stream << getLineOfChars<'-'>() << '\n';
-}
-
-void ConsoleReporter::printTestFilters() {
-    if (m_config->testSpec().hasFilters())
-        stream << Colour(Colour::BrightYellow) << "Filters: " << serializeFilters( m_config->getTestsOrTags() ) << '\n';
-}
-
-CATCH_REGISTER_REPORTER("console", ConsoleReporter)
-
-} // end namespace Catch
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-// end catch_reporter_console.cpp
-// start catch_reporter_junit.cpp
-
-#include <cassert>
-#include <sstream>
-#include <ctime>
-#include <algorithm>
-
-namespace Catch {
-
-    namespace {
-        std::string getCurrentTimestamp() {
-            // Beware, this is not reentrant because of backward compatibility issues
-            // Also, UTC only, again because of backward compatibility (%z is C++11)
-            time_t rawtime;
-            std::time(&rawtime);
-            auto const timeStampSize = sizeof("2017-01-16T17:06:45Z");
-
-#ifdef _MSC_VER
-            std::tm timeInfo = {};
-            gmtime_s(&timeInfo, &rawtime);
-#else
-            std::tm* timeInfo;
-            timeInfo = std::gmtime(&rawtime);
-#endif
-
-            char timeStamp[timeStampSize];
-            const char * const fmt = "%Y-%m-%dT%H:%M:%SZ";
-
-#ifdef _MSC_VER
-            std::strftime(timeStamp, timeStampSize, fmt, &timeInfo);
-#else
-            std::strftime(timeStamp, timeStampSize, fmt, timeInfo);
-#endif
-            return std::string(timeStamp);
-        }
-
-        std::string fileNameTag(const std::vector<std::string> &tags) {
-            auto it = std::find_if(begin(tags),
-                                   end(tags),
-                                   [] (std::string const& tag) {return tag.front() == '#'; });
-            if (it != tags.end())
-                return it->substr(1);
-            return std::string();
-        }
-    } // anonymous namespace
-
-    JunitReporter::JunitReporter( ReporterConfig const& _config )
-        :   CumulativeReporterBase( _config ),
-            xml( _config.stream() )
-        {
-            m_reporterPrefs.shouldRedirectStdOut = true;
-            m_reporterPrefs.shouldReportAllAssertions = true;
-        }
-
-    JunitReporter::~JunitReporter() {}
-
-    std::string JunitReporter::getDescription() {
-        return "Reports test results in an XML format that looks like Ant's junitreport target";
-    }
-
-    void JunitReporter::noMatchingTestCases( std::string const& /*spec*/ ) {}
-
-    void JunitReporter::testRunStarting( TestRunInfo const& runInfo )  {
-        CumulativeReporterBase::testRunStarting( runInfo );
-        xml.startElement( "testsuites" );
-    }
-
-    void JunitReporter::testGroupStarting( GroupInfo const& groupInfo ) {
-        suiteTimer.start();
-        stdOutForSuite.clear();
-        stdErrForSuite.clear();
-        unexpectedExceptions = 0;
-        CumulativeReporterBase::testGroupStarting( groupInfo );
-    }
-
-    void JunitReporter::testCaseStarting( TestCaseInfo const& testCaseInfo ) {
-        m_okToFail = testCaseInfo.okToFail();
-    }
-
-    bool JunitReporter::assertionEnded( AssertionStats const& assertionStats ) {
-        if( assertionStats.assertionResult.getResultType() == ResultWas::ThrewException && !m_okToFail )
-            unexpectedExceptions++;
-        return CumulativeReporterBase::assertionEnded( assertionStats );
-    }
-
-    void JunitReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
-        stdOutForSuite += testCaseStats.stdOut;
-        stdErrForSuite += testCaseStats.stdErr;
-        CumulativeReporterBase::testCaseEnded( testCaseStats );
-    }
-
-    void JunitReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
-        double suiteTime = suiteTimer.getElapsedSeconds();
-        CumulativeReporterBase::testGroupEnded( testGroupStats );
-        writeGroup( *m_testGroups.back(), suiteTime );
-    }
-
-    void JunitReporter::testRunEndedCumulative() {
-        xml.endElement();
-    }
-
-    void JunitReporter::writeGroup( TestGroupNode const& groupNode, double suiteTime ) {
-        XmlWriter::ScopedElement e = xml.scopedElement( "testsuite" );
-
-        TestGroupStats const& stats = groupNode.value;
-        xml.writeAttribute( "name", stats.groupInfo.name );
-        xml.writeAttribute( "errors", unexpectedExceptions );
-        xml.writeAttribute( "failures", stats.totals.assertions.failed-unexpectedExceptions );
-        xml.writeAttribute( "tests", stats.totals.assertions.total() );
-        xml.writeAttribute( "hostname", "tbd" ); // !TBD
-        if( m_config->showDurations() == ShowDurations::Never )
-            xml.writeAttribute( "time", "" );
-        else
-            xml.writeAttribute( "time", suiteTime );
-        xml.writeAttribute( "timestamp", getCurrentTimestamp() );
-
-        // Write properties if there are any
-        if (m_config->hasTestFilters() || m_config->rngSeed() != 0) {
-            auto properties = xml.scopedElement("properties");
-            if (m_config->hasTestFilters()) {
-                xml.scopedElement("property")
-                    .writeAttribute("name", "filters")
-                    .writeAttribute("value", serializeFilters(m_config->getTestsOrTags()));
-            }
-            if (m_config->rngSeed() != 0) {
-                xml.scopedElement("property")
-                    .writeAttribute("name", "random-seed")
-                    .writeAttribute("value", m_config->rngSeed());
-            }
-        }
-
-        // Write test cases
-        for( auto const& child : groupNode.children )
-            writeTestCase( *child );
-
-        xml.scopedElement( "system-out" ).writeText( trim( stdOutForSuite ), false );
-        xml.scopedElement( "system-err" ).writeText( trim( stdErrForSuite ), false );
-    }
-
-    void JunitReporter::writeTestCase( TestCaseNode const& testCaseNode ) {
-        TestCaseStats const& stats = testCaseNode.value;
-
-        // All test cases have exactly one section - which represents the
-        // test case itself. That section may have 0-n nested sections
-        assert( testCaseNode.children.size() == 1 );
-        SectionNode const& rootSection = *testCaseNode.children.front();
-
-        std::string className = stats.testInfo.className;
-
-        if( className.empty() ) {
-            className = fileNameTag(stats.testInfo.tags);
-            if ( className.empty() )
-                className = "global";
-        }
-
-        if ( !m_config->name().empty() )
-            className = m_config->name() + "." + className;
-
-        writeSection( className, "", rootSection );
-    }
-
-    void JunitReporter::writeSection(  std::string const& className,
-                        std::string const& rootName,
-                        SectionNode const& sectionNode ) {
-        std::string name = trim( sectionNode.stats.sectionInfo.name );
-        if( !rootName.empty() )
-            name = rootName + '/' + name;
-
-        if( !sectionNode.assertions.empty() ||
-            !sectionNode.stdOut.empty() ||
-            !sectionNode.stdErr.empty() ) {
-            XmlWriter::ScopedElement e = xml.scopedElement( "testcase" );
-            if( className.empty() ) {
-                xml.writeAttribute( "classname", name );
-                xml.writeAttribute( "name", "root" );
-            }
-            else {
-                xml.writeAttribute( "classname", className );
-                xml.writeAttribute( "name", name );
-            }
-            xml.writeAttribute( "time", ::Catch::Detail::stringify( sectionNode.stats.durationInSeconds ) );
-
-            writeAssertions( sectionNode );
-
-            if( !sectionNode.stdOut.empty() )
-                xml.scopedElement( "system-out" ).writeText( trim( sectionNode.stdOut ), false );
-            if( !sectionNode.stdErr.empty() )
-                xml.scopedElement( "system-err" ).writeText( trim( sectionNode.stdErr ), false );
-        }
-        for( auto const& childNode : sectionNode.childSections )
-            if( className.empty() )
-                writeSection( name, "", *childNode );
-            else
-                writeSection( className, name, *childNode );
-    }
-
-    void JunitReporter::writeAssertions( SectionNode const& sectionNode ) {
-        for( auto const& assertion : sectionNode.assertions )
-            writeAssertion( assertion );
-    }
-
-    void JunitReporter::writeAssertion( AssertionStats const& stats ) {
-        AssertionResult const& result = stats.assertionResult;
-        if( !result.isOk() ) {
-            std::string elementName;
-            switch( result.getResultType() ) {
-                case ResultWas::ThrewException:
-                case ResultWas::FatalErrorCondition:
-                    elementName = "error";
-                    break;
-                case ResultWas::ExplicitFailure:
-                    elementName = "failure";
-                    break;
-                case ResultWas::ExpressionFailed:
-                    elementName = "failure";
-                    break;
-                case ResultWas::DidntThrowException:
-                    elementName = "failure";
-                    break;
-
-                // We should never see these here:
-                case ResultWas::Info:
-                case ResultWas::Warning:
-                case ResultWas::Ok:
-                case ResultWas::Unknown:
-                case ResultWas::FailureBit:
-                case ResultWas::Exception:
-                    elementName = "internalError";
-                    break;
-            }
-
-            XmlWriter::ScopedElement e = xml.scopedElement( elementName );
-
-            xml.writeAttribute( "message", result.getExpandedExpression() );
-            xml.writeAttribute( "type", result.getTestMacroName() );
-
-            ReusableStringStream rss;
-            if( !result.getMessage().empty() )
-                rss << result.getMessage() << '\n';
-            for( auto const& msg : stats.infoMessages )
-                if( msg.type == ResultWas::Info )
-                    rss << msg.message << '\n';
-
-            rss << "at " << result.getSourceInfo();
-            xml.writeText( rss.str(), false );
-        }
-    }
-
-    CATCH_REGISTER_REPORTER( "junit", JunitReporter )
-
-} // end namespace Catch
-// end catch_reporter_junit.cpp
-// start catch_reporter_listening.cpp
-
-#include <cassert>
-
-namespace Catch {
-
-    ListeningReporter::ListeningReporter() {
-        // We will assume that listeners will always want all assertions
-        m_preferences.shouldReportAllAssertions = true;
-    }
-
-    void ListeningReporter::addListener( IStreamingReporterPtr&& listener ) {
-        m_listeners.push_back( std::move( listener ) );
-    }
-
-    void ListeningReporter::addReporter(IStreamingReporterPtr&& reporter) {
-        assert(!m_reporter && "Listening reporter can wrap only 1 real reporter");
-        m_reporter = std::move( reporter );
-        m_preferences.shouldRedirectStdOut = m_reporter->getPreferences().shouldRedirectStdOut;
-    }
-
-    ReporterPreferences ListeningReporter::getPreferences() const {
-        return m_preferences;
-    }
-
-    std::set<Verbosity> ListeningReporter::getSupportedVerbosities() {
-        return std::set<Verbosity>{ };
-    }
-
-    void ListeningReporter::noMatchingTestCases( std::string const& spec ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->noMatchingTestCases( spec );
-        }
-        m_reporter->noMatchingTestCases( spec );
-    }
-
-    void ListeningReporter::benchmarkStarting( BenchmarkInfo const& benchmarkInfo ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->benchmarkStarting( benchmarkInfo );
-        }
-        m_reporter->benchmarkStarting( benchmarkInfo );
-    }
-    void ListeningReporter::benchmarkEnded( BenchmarkStats const& benchmarkStats ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->benchmarkEnded( benchmarkStats );
-        }
-        m_reporter->benchmarkEnded( benchmarkStats );
-    }
-
-    void ListeningReporter::testRunStarting( TestRunInfo const& testRunInfo ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->testRunStarting( testRunInfo );
-        }
-        m_reporter->testRunStarting( testRunInfo );
-    }
-
-    void ListeningReporter::testGroupStarting( GroupInfo const& groupInfo ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->testGroupStarting( groupInfo );
-        }
-        m_reporter->testGroupStarting( groupInfo );
-    }
-
-    void ListeningReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->testCaseStarting( testInfo );
-        }
-        m_reporter->testCaseStarting( testInfo );
-    }
-
-    void ListeningReporter::sectionStarting( SectionInfo const& sectionInfo ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->sectionStarting( sectionInfo );
-        }
-        m_reporter->sectionStarting( sectionInfo );
-    }
-
-    void ListeningReporter::assertionStarting( AssertionInfo const& assertionInfo ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->assertionStarting( assertionInfo );
-        }
-        m_reporter->assertionStarting( assertionInfo );
-    }
-
-    // The return value indicates if the messages buffer should be cleared:
-    bool ListeningReporter::assertionEnded( AssertionStats const& assertionStats ) {
-        for( auto const& listener : m_listeners ) {
-            static_cast<void>( listener->assertionEnded( assertionStats ) );
-        }
-        return m_reporter->assertionEnded( assertionStats );
-    }
-
-    void ListeningReporter::sectionEnded( SectionStats const& sectionStats ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->sectionEnded( sectionStats );
-        }
-        m_reporter->sectionEnded( sectionStats );
-    }
-
-    void ListeningReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->testCaseEnded( testCaseStats );
-        }
-        m_reporter->testCaseEnded( testCaseStats );
-    }
-
-    void ListeningReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->testGroupEnded( testGroupStats );
-        }
-        m_reporter->testGroupEnded( testGroupStats );
-    }
-
-    void ListeningReporter::testRunEnded( TestRunStats const& testRunStats ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->testRunEnded( testRunStats );
-        }
-        m_reporter->testRunEnded( testRunStats );
-    }
-
-    void ListeningReporter::skipTest( TestCaseInfo const& testInfo ) {
-        for ( auto const& listener : m_listeners ) {
-            listener->skipTest( testInfo );
-        }
-        m_reporter->skipTest( testInfo );
-    }
-
-    bool ListeningReporter::isMulti() const {
-        return true;
-    }
-
-} // end namespace Catch
-// end catch_reporter_listening.cpp
-// start catch_reporter_xml.cpp
-
-#if defined(_MSC_VER)
-#pragma warning(push)
-#pragma warning(disable:4061) // Not all labels are EXPLICITLY handled in switch
-                              // Note that 4062 (not all labels are handled
-                              // and default is missing) is enabled
-#endif
-
-namespace Catch {
-    XmlReporter::XmlReporter( ReporterConfig const& _config )
-    :   StreamingReporterBase( _config ),
-        m_xml(_config.stream())
-    {
-        m_reporterPrefs.shouldRedirectStdOut = true;
-        m_reporterPrefs.shouldReportAllAssertions = true;
-    }
-
-    XmlReporter::~XmlReporter() = default;
-
-    std::string XmlReporter::getDescription() {
-        return "Reports test results as an XML document";
-    }
-
-    std::string XmlReporter::getStylesheetRef() const {
-        return std::string();
-    }
-
-    void XmlReporter::writeSourceInfo( SourceLineInfo const& sourceInfo ) {
-        m_xml
-            .writeAttribute( "filename", sourceInfo.file )
-            .writeAttribute( "line", sourceInfo.line );
-    }
-
-    void XmlReporter::noMatchingTestCases( std::string const& s ) {
-        StreamingReporterBase::noMatchingTestCases( s );
-    }
-
-    void XmlReporter::testRunStarting( TestRunInfo const& testInfo ) {
-        StreamingReporterBase::testRunStarting( testInfo );
-        std::string stylesheetRef = getStylesheetRef();
-        if( !stylesheetRef.empty() )
-            m_xml.writeStylesheetRef( stylesheetRef );
-        m_xml.startElement( "Catch" );
-        if( !m_config->name().empty() )
-            m_xml.writeAttribute( "name", m_config->name() );
-        if (m_config->testSpec().hasFilters())
-            m_xml.writeAttribute( "filters", serializeFilters( m_config->getTestsOrTags() ) );
-        if( m_config->rngSeed() != 0 )
-            m_xml.scopedElement( "Randomness" )
-                .writeAttribute( "seed", m_config->rngSeed() );
-    }
-
-    void XmlReporter::testGroupStarting( GroupInfo const& groupInfo ) {
-        StreamingReporterBase::testGroupStarting( groupInfo );
-        m_xml.startElement( "Group" )
-            .writeAttribute( "name", groupInfo.name );
-    }
-
-    void XmlReporter::testCaseStarting( TestCaseInfo const& testInfo ) {
-        StreamingReporterBase::testCaseStarting(testInfo);
-        m_xml.startElement( "TestCase" )
-            .writeAttribute( "name", trim( testInfo.name ) )
-            .writeAttribute( "description", testInfo.description )
-            .writeAttribute( "tags", testInfo.tagsAsString() );
-
-        writeSourceInfo( testInfo.lineInfo );
-
-        if ( m_config->showDurations() == ShowDurations::Always )
-            m_testCaseTimer.start();
-        m_xml.ensureTagClosed();
-    }
-
-    void XmlReporter::sectionStarting( SectionInfo const& sectionInfo ) {
-        StreamingReporterBase::sectionStarting( sectionInfo );
-        if( m_sectionDepth++ > 0 ) {
-            m_xml.startElement( "Section" )
-                .writeAttribute( "name", trim( sectionInfo.name ) );
-            writeSourceInfo( sectionInfo.lineInfo );
-            m_xml.ensureTagClosed();
-        }
-    }
-
-    void XmlReporter::assertionStarting( AssertionInfo const& ) { }
-
-    bool XmlReporter::assertionEnded( AssertionStats const& assertionStats ) {
-
-        AssertionResult const& result = assertionStats.assertionResult;
-
-        bool includeResults = m_config->includeSuccessfulResults() || !result.isOk();
-
-        if( includeResults || result.getResultType() == ResultWas::Warning ) {
-            // Print any info messages in <Info> tags.
-            for( auto const& msg : assertionStats.infoMessages ) {
-                if( msg.type == ResultWas::Info && includeResults ) {
-                    m_xml.scopedElement( "Info" )
-                            .writeText( msg.message );
-                } else if ( msg.type == ResultWas::Warning ) {
-                    m_xml.scopedElement( "Warning" )
-                            .writeText( msg.message );
-                }
-            }
-        }
-
-        // Drop out if result was successful but we're not printing them.
-        if( !includeResults && result.getResultType() != ResultWas::Warning )
-            return true;
-
-        // Print the expression if there is one.
-        if( result.hasExpression() ) {
-            m_xml.startElement( "Expression" )
-                .writeAttribute( "success", result.succeeded() )
-                .writeAttribute( "type", result.getTestMacroName() );
-
-            writeSourceInfo( result.getSourceInfo() );
-
-            m_xml.scopedElement( "Original" )
-                .writeText( result.getExpression() );
-            m_xml.scopedElement( "Expanded" )
-                .writeText( result.getExpandedExpression() );
-        }
-
-        // And... Print a result applicable to each result type.
-        switch( result.getResultType() ) {
-            case ResultWas::ThrewException:
-                m_xml.startElement( "Exception" );
-                writeSourceInfo( result.getSourceInfo() );
-                m_xml.writeText( result.getMessage() );
-                m_xml.endElement();
-                break;
-            case ResultWas::FatalErrorCondition:
-                m_xml.startElement( "FatalErrorCondition" );
-                writeSourceInfo( result.getSourceInfo() );
-                m_xml.writeText( result.getMessage() );
-                m_xml.endElement();
-                break;
-            case ResultWas::Info:
-                m_xml.scopedElement( "Info" )
-                    .writeText( result.getMessage() );
-                break;
-            case ResultWas::Warning:
-                // Warning will already have been written
-                break;
-            case ResultWas::ExplicitFailure:
-                m_xml.startElement( "Failure" );
-                writeSourceInfo( result.getSourceInfo() );
-                m_xml.writeText( result.getMessage() );
-                m_xml.endElement();
-                break;
-            default:
-                break;
-        }
-
-        if( result.hasExpression() )
-            m_xml.endElement();
-
-        return true;
-    }
-
-    void XmlReporter::sectionEnded( SectionStats const& sectionStats ) {
-        StreamingReporterBase::sectionEnded( sectionStats );
-        if( --m_sectionDepth > 0 ) {
-            XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResults" );
-            e.writeAttribute( "successes", sectionStats.assertions.passed );
-            e.writeAttribute( "failures", sectionStats.assertions.failed );
-            e.writeAttribute( "expectedFailures", sectionStats.assertions.failedButOk );
-
-            if ( m_config->showDurations() == ShowDurations::Always )
-                e.writeAttribute( "durationInSeconds", sectionStats.durationInSeconds );
-
-            m_xml.endElement();
-        }
-    }
-
-    void XmlReporter::testCaseEnded( TestCaseStats const& testCaseStats ) {
-        StreamingReporterBase::testCaseEnded( testCaseStats );
-        XmlWriter::ScopedElement e = m_xml.scopedElement( "OverallResult" );
-        e.writeAttribute( "success", testCaseStats.totals.assertions.allOk() );
-
-        if ( m_config->showDurations() == ShowDurations::Always )
-            e.writeAttribute( "durationInSeconds", m_testCaseTimer.getElapsedSeconds() );
-
-        if( !testCaseStats.stdOut.empty() )
-            m_xml.scopedElement( "StdOut" ).writeText( trim( testCaseStats.stdOut ), false );
-        if( !testCaseStats.stdErr.empty() )
-            m_xml.scopedElement( "StdErr" ).writeText( trim( testCaseStats.stdErr ), false );
-
-        m_xml.endElement();
-    }
-
-    void XmlReporter::testGroupEnded( TestGroupStats const& testGroupStats ) {
-        StreamingReporterBase::testGroupEnded( testGroupStats );
-        // TODO: Check testGroupStats.aborting and act accordingly.
-        m_xml.scopedElement( "OverallResults" )
-            .writeAttribute( "successes", testGroupStats.totals.assertions.passed )
-            .writeAttribute( "failures", testGroupStats.totals.assertions.failed )
-            .writeAttribute( "expectedFailures", testGroupStats.totals.assertions.failedButOk );
-        m_xml.endElement();
-    }
-
-    void XmlReporter::testRunEnded( TestRunStats const& testRunStats ) {
-        StreamingReporterBase::testRunEnded( testRunStats );
-        m_xml.scopedElement( "OverallResults" )
-            .writeAttribute( "successes", testRunStats.totals.assertions.passed )
-            .writeAttribute( "failures", testRunStats.totals.assertions.failed )
-            .writeAttribute( "expectedFailures", testRunStats.totals.assertions.failedButOk );
-        m_xml.endElement();
-    }
-
-    CATCH_REGISTER_REPORTER( "xml", XmlReporter )
-
-} // end namespace Catch
-
-#if defined(_MSC_VER)
-#pragma warning(pop)
-#endif
-// end catch_reporter_xml.cpp
-
-namespace Catch {
-    LeakDetector leakDetector;
-}
-
-#ifdef __clang__
-#pragma clang diagnostic pop
-#endif
-
-// end catch_impl.hpp
-#endif
-
-#ifdef CATCH_CONFIG_MAIN
-// start catch_default_main.hpp
-
-#ifndef __OBJC__
-
-#if defined(CATCH_CONFIG_WCHAR) && defined(WIN32) && defined(_UNICODE) && !defined(DO_NOT_USE_WMAIN)
-// Standard C/C++ Win32 Unicode wmain entry point
-extern "C" int wmain (int argc, wchar_t * argv[], wchar_t * []) {
-#else
-// Standard C/C++ main entry point
-int main (int argc, char * argv[]) {
-#endif
-
-    return Catch::Session().run( argc, argv );
-}
-
-#else // __OBJC__
-
-// Objective-C entry point
-int main (int argc, char * const argv[]) {
-#if !CATCH_ARC_ENABLED
-    NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
-#endif
-
-    Catch::registerTestMethods();
-    int result = Catch::Session().run( argc, (char**)argv );
-
-#if !CATCH_ARC_ENABLED
-    [pool drain];
-#endif
-
-    return result;
-}
-
-#endif // __OBJC__
-
-// end catch_default_main.hpp
-#endif
-
-#if !defined(CATCH_CONFIG_IMPL_ONLY)
-
-#ifdef CLARA_CONFIG_MAIN_NOT_DEFINED
-#  undef CLARA_CONFIG_MAIN
-#endif
-
-#if !defined(CATCH_CONFIG_DISABLE)
-//////
-// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
-#ifdef CATCH_CONFIG_PREFIX_ALL
-
-#define CATCH_REQUIRE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__ )
-#define CATCH_REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
-
-#define CATCH_REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "CATCH_REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )
-#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
-#define CATCH_REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
-#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
-#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr )
-#endif// CATCH_CONFIG_DISABLE_MATCHERS
-#define CATCH_REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ )
-
-#define CATCH_CHECK( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-#define CATCH_CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
-#define CATCH_CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CATCH_CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-#define CATCH_CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CATCH_CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-#define CATCH_CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CATCH_CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
-
-#define CATCH_CHECK_THROWS( ... )  INTERNAL_CATCH_THROWS( "CATCH_CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CATCH_CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
-#define CATCH_CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CATCH_CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
-#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
-#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CATCH_CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
-#endif // CATCH_CONFIG_DISABLE_MATCHERS
-#define CATCH_CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CATCH_CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-
-#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
-#define CATCH_CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
-
-#define CATCH_REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CATCH_REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
-#endif // CATCH_CONFIG_DISABLE_MATCHERS
-
-#define CATCH_INFO( msg ) INTERNAL_CATCH_INFO( "CATCH_INFO", msg )
-#define CATCH_UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "CATCH_UNSCOPED_INFO", msg )
-#define CATCH_WARN( msg ) INTERNAL_CATCH_MSG( "CATCH_WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
-#define CATCH_CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CATCH_CAPTURE",__VA_ARGS__ )
-
-#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
-#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#define CATCH_METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
-#define CATCH_REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
-#define CATCH_SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
-#define CATCH_DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
-#define CATCH_FAIL( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
-#define CATCH_FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "CATCH_FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-#define CATCH_SUCCEED( ... ) INTERNAL_CATCH_MSG( "CATCH_SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-
-#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE()
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
-#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
-#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
-#else
-#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
-#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
-#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
-#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
-#endif
-
-#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
-#define CATCH_STATIC_REQUIRE( ... )       static_assert(   __VA_ARGS__ ,      #__VA_ARGS__ );     CATCH_SUCCEED( #__VA_ARGS__ )
-#define CATCH_STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); CATCH_SUCCEED( #__VA_ARGS__ )
-#else
-#define CATCH_STATIC_REQUIRE( ... )       CATCH_REQUIRE( __VA_ARGS__ )
-#define CATCH_STATIC_REQUIRE_FALSE( ... ) CATCH_REQUIRE_FALSE( __VA_ARGS__ )
-#endif
-
-// "BDD-style" convenience wrappers
-#define CATCH_SCENARIO( ... ) CATCH_TEST_CASE( "Scenario: " __VA_ARGS__ )
-#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
-#define CATCH_GIVEN( desc )     INTERNAL_CATCH_DYNAMIC_SECTION( "    Given: " << desc )
-#define CATCH_AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc )
-#define CATCH_WHEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     When: " << desc )
-#define CATCH_AND_WHEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc )
-#define CATCH_THEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     Then: " << desc )
-#define CATCH_AND_THEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( "      And: " << desc )
-
-// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
-#else
-
-#define REQUIRE( ... ) INTERNAL_CATCH_TEST( "REQUIRE", Catch::ResultDisposition::Normal, __VA_ARGS__  )
-#define REQUIRE_FALSE( ... ) INTERNAL_CATCH_TEST( "REQUIRE_FALSE", Catch::ResultDisposition::Normal | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
-
-#define REQUIRE_THROWS( ... ) INTERNAL_CATCH_THROWS( "REQUIRE_THROWS", Catch::ResultDisposition::Normal, __VA_ARGS__ )
-#define REQUIRE_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "REQUIRE_THROWS_AS", exceptionType, Catch::ResultDisposition::Normal, expr )
-#define REQUIRE_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "REQUIRE_THROWS_WITH", Catch::ResultDisposition::Normal, matcher, expr )
-#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
-#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "REQUIRE_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::Normal, matcher, expr )
-#endif // CATCH_CONFIG_DISABLE_MATCHERS
-#define REQUIRE_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "REQUIRE_NOTHROW", Catch::ResultDisposition::Normal, __VA_ARGS__ )
-
-#define CHECK( ... ) INTERNAL_CATCH_TEST( "CHECK", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-#define CHECK_FALSE( ... ) INTERNAL_CATCH_TEST( "CHECK_FALSE", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::FalseTest, __VA_ARGS__ )
-#define CHECKED_IF( ... ) INTERNAL_CATCH_IF( "CHECKED_IF", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-#define CHECKED_ELSE( ... ) INTERNAL_CATCH_ELSE( "CHECKED_ELSE", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-#define CHECK_NOFAIL( ... ) INTERNAL_CATCH_TEST( "CHECK_NOFAIL", Catch::ResultDisposition::ContinueOnFailure | Catch::ResultDisposition::SuppressFail, __VA_ARGS__ )
-
-#define CHECK_THROWS( ... )  INTERNAL_CATCH_THROWS( "CHECK_THROWS", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-#define CHECK_THROWS_AS( expr, exceptionType ) INTERNAL_CATCH_THROWS_AS( "CHECK_THROWS_AS", exceptionType, Catch::ResultDisposition::ContinueOnFailure, expr )
-#define CHECK_THROWS_WITH( expr, matcher ) INTERNAL_CATCH_THROWS_STR_MATCHES( "CHECK_THROWS_WITH", Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
-#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
-#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) INTERNAL_CATCH_THROWS_MATCHES( "CHECK_THROWS_MATCHES", exceptionType, Catch::ResultDisposition::ContinueOnFailure, matcher, expr )
-#endif // CATCH_CONFIG_DISABLE_MATCHERS
-#define CHECK_NOTHROW( ... ) INTERNAL_CATCH_NO_THROW( "CHECK_NOTHROW", Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-
-#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
-#define CHECK_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "CHECK_THAT", matcher, Catch::ResultDisposition::ContinueOnFailure, arg )
-
-#define REQUIRE_THAT( arg, matcher ) INTERNAL_CHECK_THAT( "REQUIRE_THAT", matcher, Catch::ResultDisposition::Normal, arg )
-#endif // CATCH_CONFIG_DISABLE_MATCHERS
-
-#define INFO( msg ) INTERNAL_CATCH_INFO( "INFO", msg )
-#define UNSCOPED_INFO( msg ) INTERNAL_CATCH_UNSCOPED_INFO( "UNSCOPED_INFO", msg )
-#define WARN( msg ) INTERNAL_CATCH_MSG( "WARN", Catch::ResultWas::Warning, Catch::ResultDisposition::ContinueOnFailure, msg )
-#define CAPTURE( ... ) INTERNAL_CATCH_CAPTURE( INTERNAL_CATCH_UNIQUE_NAME(capturer), "CAPTURE",__VA_ARGS__ )
-
-#define TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE( __VA_ARGS__ )
-#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#define METHOD_AS_TEST_CASE( method, ... ) INTERNAL_CATCH_METHOD_AS_TEST_CASE( method, __VA_ARGS__ )
-#define REGISTER_TEST_CASE( Function, ... ) INTERNAL_CATCH_REGISTER_TESTCASE( Function, __VA_ARGS__ )
-#define SECTION( ... ) INTERNAL_CATCH_SECTION( __VA_ARGS__ )
-#define DYNAMIC_SECTION( ... ) INTERNAL_CATCH_DYNAMIC_SECTION( __VA_ARGS__ )
-#define FAIL( ... ) INTERNAL_CATCH_MSG( "FAIL", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::Normal, __VA_ARGS__ )
-#define FAIL_CHECK( ... ) INTERNAL_CATCH_MSG( "FAIL_CHECK", Catch::ResultWas::ExplicitFailure, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-#define SUCCEED( ... ) INTERNAL_CATCH_MSG( "SUCCEED", Catch::ResultWas::Ok, Catch::ResultDisposition::ContinueOnFailure, __VA_ARGS__ )
-#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE()
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
-#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ )
-#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
-#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ )
-#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ )
-#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ )
-#else
-#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ ) )
-#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG( __VA_ARGS__ ) )
-#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
-#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
-#define TEMPLATE_PRODUCT_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE( __VA_ARGS__ ) )
-#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( __VA_ARGS__ ) )
-#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, __VA_ARGS__ ) )
-#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, __VA_ARGS__ ) )
-#endif
-
-#if !defined(CATCH_CONFIG_RUNTIME_STATIC_REQUIRE)
-#define STATIC_REQUIRE( ... )       static_assert(   __VA_ARGS__,  #__VA_ARGS__ ); SUCCEED( #__VA_ARGS__ )
-#define STATIC_REQUIRE_FALSE( ... ) static_assert( !(__VA_ARGS__), "!(" #__VA_ARGS__ ")" ); SUCCEED( "!(" #__VA_ARGS__ ")" )
-#else
-#define STATIC_REQUIRE( ... )       REQUIRE( __VA_ARGS__ )
-#define STATIC_REQUIRE_FALSE( ... ) REQUIRE_FALSE( __VA_ARGS__ )
-#endif
-
-#endif
-
-#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION( signature )
-
-// "BDD-style" convenience wrappers
-#define SCENARIO( ... ) TEST_CASE( "Scenario: " __VA_ARGS__ )
-#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TEST_CASE_METHOD( className, "Scenario: " __VA_ARGS__ )
-
-#define GIVEN( desc )     INTERNAL_CATCH_DYNAMIC_SECTION( "    Given: " << desc )
-#define AND_GIVEN( desc ) INTERNAL_CATCH_DYNAMIC_SECTION( "And given: " << desc )
-#define WHEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     When: " << desc )
-#define AND_WHEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( " And when: " << desc )
-#define THEN( desc )      INTERNAL_CATCH_DYNAMIC_SECTION( "     Then: " << desc )
-#define AND_THEN( desc )  INTERNAL_CATCH_DYNAMIC_SECTION( "      And: " << desc )
-
-using Catch::Detail::Approx;
-
-#else // CATCH_CONFIG_DISABLE
-
-//////
-// If this config identifier is defined then all CATCH macros are prefixed with CATCH_
-#ifdef CATCH_CONFIG_PREFIX_ALL
-
-#define CATCH_REQUIRE( ... )        (void)(0)
-#define CATCH_REQUIRE_FALSE( ... )  (void)(0)
-
-#define CATCH_REQUIRE_THROWS( ... ) (void)(0)
-#define CATCH_REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0)
-#define CATCH_REQUIRE_THROWS_WITH( expr, matcher )     (void)(0)
-#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
-#define CATCH_REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
-#endif// CATCH_CONFIG_DISABLE_MATCHERS
-#define CATCH_REQUIRE_NOTHROW( ... ) (void)(0)
-
-#define CATCH_CHECK( ... )         (void)(0)
-#define CATCH_CHECK_FALSE( ... )   (void)(0)
-#define CATCH_CHECKED_IF( ... )    if (__VA_ARGS__)
-#define CATCH_CHECKED_ELSE( ... )  if (!(__VA_ARGS__))
-#define CATCH_CHECK_NOFAIL( ... )  (void)(0)
-
-#define CATCH_CHECK_THROWS( ... )  (void)(0)
-#define CATCH_CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
-#define CATCH_CHECK_THROWS_WITH( expr, matcher )     (void)(0)
-#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
-#define CATCH_CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
-#endif // CATCH_CONFIG_DISABLE_MATCHERS
-#define CATCH_CHECK_NOTHROW( ... ) (void)(0)
-
-#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
-#define CATCH_CHECK_THAT( arg, matcher )   (void)(0)
-
-#define CATCH_REQUIRE_THAT( arg, matcher ) (void)(0)
-#endif // CATCH_CONFIG_DISABLE_MATCHERS
-
-#define CATCH_INFO( msg )          (void)(0)
-#define CATCH_UNSCOPED_INFO( msg ) (void)(0)
-#define CATCH_WARN( msg )          (void)(0)
-#define CATCH_CAPTURE( msg )       (void)(0)
-
-#define CATCH_TEST_CASE( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
-#define CATCH_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
-#define CATCH_METHOD_AS_TEST_CASE( method, ... )
-#define CATCH_REGISTER_TEST_CASE( Function, ... ) (void)(0)
-#define CATCH_SECTION( ... )
-#define CATCH_DYNAMIC_SECTION( ... )
-#define CATCH_FAIL( ... ) (void)(0)
-#define CATCH_FAIL_CHECK( ... ) (void)(0)
-#define CATCH_SUCCEED( ... ) (void)(0)
-
-#define CATCH_ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
-#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
-#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
-#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#else
-#define CATCH_TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
-#define CATCH_TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
-#define CATCH_TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
-#define CATCH_TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) CATCH_TEMPLATE_TEST_CASE( __VA_ARGS__ )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#define CATCH_TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) CATCH_TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#endif
-
-// "BDD-style" convenience wrappers
-#define CATCH_SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
-#define CATCH_SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )
-#define CATCH_GIVEN( desc )
-#define CATCH_AND_GIVEN( desc )
-#define CATCH_WHEN( desc )
-#define CATCH_AND_WHEN( desc )
-#define CATCH_THEN( desc )
-#define CATCH_AND_THEN( desc )
-
-#define CATCH_STATIC_REQUIRE( ... )       (void)(0)
-#define CATCH_STATIC_REQUIRE_FALSE( ... ) (void)(0)
-
-// If CATCH_CONFIG_PREFIX_ALL is not defined then the CATCH_ prefix is not required
-#else
-
-#define REQUIRE( ... )       (void)(0)
-#define REQUIRE_FALSE( ... ) (void)(0)
-
-#define REQUIRE_THROWS( ... ) (void)(0)
-#define REQUIRE_THROWS_AS( expr, exceptionType ) (void)(0)
-#define REQUIRE_THROWS_WITH( expr, matcher ) (void)(0)
-#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
-#define REQUIRE_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
-#endif // CATCH_CONFIG_DISABLE_MATCHERS
-#define REQUIRE_NOTHROW( ... ) (void)(0)
-
-#define CHECK( ... ) (void)(0)
-#define CHECK_FALSE( ... ) (void)(0)
-#define CHECKED_IF( ... ) if (__VA_ARGS__)
-#define CHECKED_ELSE( ... ) if (!(__VA_ARGS__))
-#define CHECK_NOFAIL( ... ) (void)(0)
-
-#define CHECK_THROWS( ... )  (void)(0)
-#define CHECK_THROWS_AS( expr, exceptionType ) (void)(0)
-#define CHECK_THROWS_WITH( expr, matcher ) (void)(0)
-#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
-#define CHECK_THROWS_MATCHES( expr, exceptionType, matcher ) (void)(0)
-#endif // CATCH_CONFIG_DISABLE_MATCHERS
-#define CHECK_NOTHROW( ... ) (void)(0)
-
-#if !defined(CATCH_CONFIG_DISABLE_MATCHERS)
-#define CHECK_THAT( arg, matcher ) (void)(0)
-
-#define REQUIRE_THAT( arg, matcher ) (void)(0)
-#endif // CATCH_CONFIG_DISABLE_MATCHERS
-
-#define INFO( msg ) (void)(0)
-#define UNSCOPED_INFO( msg ) (void)(0)
-#define WARN( msg ) (void)(0)
-#define CAPTURE( msg ) (void)(0)
-
-#define TEST_CASE( ... )  INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
-#define TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
-#define METHOD_AS_TEST_CASE( method, ... )
-#define REGISTER_TEST_CASE( Function, ... ) (void)(0)
-#define SECTION( ... )
-#define DYNAMIC_SECTION( ... )
-#define FAIL( ... ) (void)(0)
-#define FAIL_CHECK( ... ) (void)(0)
-#define SUCCEED( ... ) (void)(0)
-#define ANON_TEST_CASE() INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ))
-
-#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR
-#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__)
-#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__)
-#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__)
-#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ )
-#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
-#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
-#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#else
-#define TEMPLATE_TEST_CASE( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_NO_REGISTRATION(__VA_ARGS__) )
-#define TEMPLATE_TEST_CASE_SIG( ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_SIG_NO_REGISTRATION(__VA_ARGS__) )
-#define TEMPLATE_TEST_CASE_METHOD( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_NO_REGISTRATION(className, __VA_ARGS__ ) )
-#define TEMPLATE_TEST_CASE_METHOD_SIG( className, ... ) INTERNAL_CATCH_EXPAND_VARGS( INTERNAL_CATCH_TEMPLATE_TEST_CASE_METHOD_SIG_NO_REGISTRATION(className, __VA_ARGS__ ) )
-#define TEMPLATE_PRODUCT_TEST_CASE( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
-#define TEMPLATE_PRODUCT_TEST_CASE_SIG( ... ) TEMPLATE_TEST_CASE( __VA_ARGS__ )
-#define TEMPLATE_PRODUCT_TEST_CASE_METHOD( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#define TEMPLATE_PRODUCT_TEST_CASE_METHOD_SIG( className, ... ) TEMPLATE_TEST_CASE_METHOD( className, __VA_ARGS__ )
-#endif
-
-#define STATIC_REQUIRE( ... )       (void)(0)
-#define STATIC_REQUIRE_FALSE( ... ) (void)(0)
-
-#endif
-
-#define CATCH_TRANSLATE_EXCEPTION( signature ) INTERNAL_CATCH_TRANSLATE_EXCEPTION_NO_REG( INTERNAL_CATCH_UNIQUE_NAME( catch_internal_ExceptionTranslator ), signature )
-
-// "BDD-style" convenience wrappers
-#define SCENARIO( ... ) INTERNAL_CATCH_TESTCASE_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ) )
-#define SCENARIO_METHOD( className, ... ) INTERNAL_CATCH_TESTCASE_METHOD_NO_REGISTRATION(INTERNAL_CATCH_UNIQUE_NAME( ____C_A_T_C_H____T_E_S_T____ ), className )
-
-#define GIVEN( desc )
-#define AND_GIVEN( desc )
-#define WHEN( desc )
-#define AND_WHEN( desc )
-#define THEN( desc )
-#define AND_THEN( desc )
-
-using Catch::Detail::Approx;
-
-#endif
-
-#endif // ! CATCH_CONFIG_IMPL_ONLY
-
-// start catch_reenable_warnings.h
-
-
-#ifdef __clang__
-#    ifdef __ICC // icpc defines the __clang__ macro
-#        pragma warning(pop)
-#    else
-#        pragma clang diagnostic pop
-#    endif
-#elif defined __GNUC__
-#    pragma GCC diagnostic pop
-#endif
-
-// end catch_reenable_warnings.h
-// end catch.hpp
-#endif // TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED
-
diff --git a/ThirdParty/cnpy/cnpy.cpp b/ThirdParty/cnpy/cnpy.cpp
deleted file mode 100644
index d2eee53d08820803eb14cc56490d699ede2f68ff..0000000000000000000000000000000000000000
--- a/ThirdParty/cnpy/cnpy.cpp
+++ /dev/null
@@ -1,340 +0,0 @@
-//Copyright (C) 2011  Carl Rogers
-//Released under MIT License
-//license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php
-
-#include "cnpy.hpp"
-#include <complex>
-#include <cstdlib>
-#include <algorithm>
-#include <cstring>
-#include <iomanip>
-#include <stdint.h>
-#include <stdexcept>
-#include <regex>
-
-char cnpy::BigEndianTest() {
-    int x = 1;
-    return (((char *)&x)[0]) ? '<' : '>';
-}
-
-char cnpy::map_type(const std::type_info& t)
-{
-    if(t == typeid(float) ) return 'f';
-    if(t == typeid(double) ) return 'f';
-    if(t == typeid(long double) ) return 'f';
-
-    if(t == typeid(int) ) return 'i';
-    if(t == typeid(char) ) return 'i';
-    if(t == typeid(short) ) return 'i';
-    if(t == typeid(long) ) return 'i';
-    if(t == typeid(long long) ) return 'i';
-
-    if(t == typeid(unsigned char) ) return 'u';
-    if(t == typeid(unsigned short) ) return 'u';
-    if(t == typeid(unsigned long) ) return 'u';
-    if(t == typeid(unsigned long long) ) return 'u';
-    if(t == typeid(unsigned int) ) return 'u';
-
-    if(t == typeid(bool) ) return 'b';
-
-    if(t == typeid(std::complex<float>) ) return 'c';
-    if(t == typeid(std::complex<double>) ) return 'c';
-    if(t == typeid(std::complex<long double>) ) return 'c';
-
-    else return '?';
-}
-
-template<> std::vector<char>& cnpy::operator+=(std::vector<char>& lhs, const std::string rhs) {
-    lhs.insert(lhs.end(),rhs.begin(),rhs.end());
-    return lhs;
-}
-
-template<> std::vector<char>& cnpy::operator+=(std::vector<char>& lhs, const char* rhs) {
-    //write in little endian
-    size_t len = strlen(rhs);
-    lhs.reserve(len);
-    for(size_t byte = 0; byte < len; byte++) {
-        lhs.push_back(rhs[byte]);
-    }
-    return lhs;
-}
-
-void cnpy::parse_npy_header(unsigned char* buffer,size_t& word_size, std::vector<size_t>& shape, bool& fortran_order) {
-    //std::string magic_string(buffer,6);
-    [[maybe_unused]] uint8_t major_version = *reinterpret_cast<uint8_t*>(buffer+6);
-    [[maybe_unused]] uint8_t minor_version = *reinterpret_cast<uint8_t*>(buffer+7);
-    uint16_t header_len = *reinterpret_cast<uint16_t*>(buffer+8);
-    std::string header(reinterpret_cast<char*>(buffer+9),header_len);
-
-    size_t loc1, loc2;
-
-    //fortran order
-    loc1 = header.find("fortran_order")+16;
-    fortran_order = (header.substr(loc1,4) == "True" ? true : false);
-
-    //shape
-    loc1 = header.find("(");
-    loc2 = header.find(")");
-
-    std::regex num_regex("[0-9][0-9]*");
-    std::smatch sm;
-    shape.clear();
-
-    std::string str_shape = header.substr(loc1+1,loc2-loc1-1);
-    while(std::regex_search(str_shape, sm, num_regex)) {
-        shape.push_back(std::stoi(sm[0].str()));
-        str_shape = sm.suffix().str();
-    }
-
-    //endian, word size, data type
-    //byte order code | stands for not applicable. 
-    //not sure when this applies except for byte array
-    loc1 = header.find("descr")+9;
-    bool littleEndian = (header[loc1] == '<' || header[loc1] == '|' ? true : false);
-    assert(littleEndian);
-
-    //char type = header[loc1+1];
-    //assert(type == map_type(T));
-
-    std::string str_ws = header.substr(loc1+2);
-    loc2 = str_ws.find("'");
-    word_size = atoi(str_ws.substr(0,loc2).c_str());
-}
-
-void cnpy::parse_npy_header(FILE* fp, size_t& word_size, std::vector<size_t>& shape, bool& fortran_order) {  
-    char buffer[256];
-    size_t res = fread(buffer,sizeof(char),11,fp);       
-    if(res != 11)
-        throw std::runtime_error("parse_npy_header: failed fread");
-    std::string header = fgets(buffer,256,fp);
-    assert(header[header.size()-1] == '\n');
-
-    size_t loc1, loc2;
-
-    //fortran order
-    loc1 = header.find("fortran_order");
-    if (loc1 == std::string::npos)
-        throw std::runtime_error("parse_npy_header: failed to find header keyword: 'fortran_order'");
-    loc1 += 16;
-    fortran_order = (header.substr(loc1,4) == "True" ? true : false);
-
-    //shape
-    loc1 = header.find("(");
-    loc2 = header.find(")");
-    if (loc1 == std::string::npos || loc2 == std::string::npos)
-        throw std::runtime_error("parse_npy_header: failed to find header keyword: '(' or ')'");
-
-    std::regex num_regex("[0-9][0-9]*");
-    std::smatch sm;
-    shape.clear();
-
-    std::string str_shape = header.substr(loc1+1,loc2-loc1-1);
-    while(std::regex_search(str_shape, sm, num_regex)) {
-        shape.push_back(std::stoi(sm[0].str()));
-        str_shape = sm.suffix().str();
-    }
-
-    //endian, word size, data type
-    //byte order code | stands for not applicable. 
-    //not sure when this applies except for byte array
-    loc1 = header.find("descr");
-    if (loc1 == std::string::npos)
-        throw std::runtime_error("parse_npy_header: failed to find header keyword: 'descr'");
-    loc1 += 9;
-    bool littleEndian = (header[loc1] == '<' || header[loc1] == '|' ? true : false);
-    assert(littleEndian);
-
-    //char type = header[loc1+1];
-    //assert(type == map_type(T));
-
-    std::string str_ws = header.substr(loc1+2);
-    loc2 = str_ws.find("'");
-    word_size = atoi(str_ws.substr(0,loc2).c_str());
-}
-
-void cnpy::parse_zip_footer(FILE* fp, uint16_t& nrecs, size_t& global_header_size, size_t& global_header_offset)
-{
-    std::vector<char> footer(22);
-    fseek(fp,-22,SEEK_END);
-    size_t res = fread(&footer[0],sizeof(char),22,fp);
-    if(res != 22)
-        throw std::runtime_error("parse_zip_footer: failed fread");
-
-    uint16_t disk_no, disk_start, nrecs_on_disk, comment_len;
-    disk_no = *(uint16_t*) &footer[4];
-    disk_start = *(uint16_t*) &footer[6];
-    nrecs_on_disk = *(uint16_t*) &footer[8];
-    nrecs = *(uint16_t*) &footer[10];
-    global_header_size = *(uint32_t*) &footer[12];
-    global_header_offset = *(uint32_t*) &footer[16];
-    comment_len = *(uint16_t*) &footer[20];
-
-    assert(disk_no == 0);
-    assert(disk_start == 0);
-    assert(nrecs_on_disk == nrecs);
-    assert(comment_len == 0);
-}
-
-cnpy::NpyArray load_the_npy_file(FILE* fp) {
-    std::vector<size_t> shape;
-    size_t word_size;
-    bool fortran_order;
-    cnpy::parse_npy_header(fp,word_size,shape,fortran_order);
-
-    cnpy::NpyArray arr(shape, word_size, fortran_order);
-    size_t nread = fread(arr.data<char>(),1,arr.num_bytes(),fp);
-    if(nread != arr.num_bytes())
-        throw std::runtime_error("load_the_npy_file: failed fread");
-    return arr;
-}
-
-cnpy::NpyArray load_the_npz_array(FILE* fp, uint32_t compr_bytes, uint32_t uncompr_bytes) {
-
-    std::vector<unsigned char> buffer_compr(compr_bytes);
-    std::vector<unsigned char> buffer_uncompr(uncompr_bytes);
-    size_t nread = fread(&buffer_compr[0],1,compr_bytes,fp);
-    if(nread != compr_bytes)
-        throw std::runtime_error("load_the_npy_file: failed fread");
-
-    [[maybe_unused]] int err;
-    z_stream d_stream;
-
-    d_stream.zalloc = Z_NULL;
-    d_stream.zfree = Z_NULL;
-    d_stream.opaque = Z_NULL;
-    d_stream.avail_in = 0;
-    d_stream.next_in = Z_NULL;
-    err = inflateInit2(&d_stream, -MAX_WBITS);
-
-    d_stream.avail_in = compr_bytes;
-    d_stream.next_in = &buffer_compr[0];
-    d_stream.avail_out = uncompr_bytes;
-    d_stream.next_out = &buffer_uncompr[0];
-
-    err = inflate(&d_stream, Z_FINISH);
-    err = inflateEnd(&d_stream);
-
-    std::vector<size_t> shape;
-    size_t word_size;
-    bool fortran_order;
-    cnpy::parse_npy_header(&buffer_uncompr[0],word_size,shape,fortran_order);
-
-    cnpy::NpyArray array(shape, word_size, fortran_order);
-
-    size_t offset = uncompr_bytes - array.num_bytes();
-    memcpy(array.data<unsigned char>(),&buffer_uncompr[0]+offset,array.num_bytes());
-
-    return array;
-}
-
-cnpy::npz_t cnpy::npz_load(std::string fname) {
-    FILE* fp = fopen(fname.c_str(),"rb");
-
-    if(!fp) {
-        throw std::runtime_error("npz_load: Error! Unable to open file "+fname+"!");
-    }
-
-    cnpy::npz_t arrays;  
-
-    while(1) {
-        std::vector<char> local_header(30);
-        size_t headerres = fread(&local_header[0],sizeof(char),30,fp);
-        if(headerres != 30)
-            throw std::runtime_error("npz_load: failed fread");
-
-        //if we've reached the global header, stop reading
-        if(local_header[2] != 0x03 || local_header[3] != 0x04) break;
-
-        //read in the variable name
-        uint16_t name_len = *(uint16_t*) &local_header[26];
-        std::string varname(name_len,' ');
-        size_t vname_res = fread(&varname[0],sizeof(char),name_len,fp);
-        if(vname_res != name_len)
-            throw std::runtime_error("npz_load: failed fread");
-
-        //erase the lagging .npy        
-        varname.erase(varname.end()-4,varname.end());
-
-        //read in the extra field
-        uint16_t extra_field_len = *(uint16_t*) &local_header[28];
-        if(extra_field_len > 0) {
-            std::vector<char> buff(extra_field_len);
-            size_t efield_res = fread(&buff[0],sizeof(char),extra_field_len,fp);
-            if(efield_res != extra_field_len)
-                throw std::runtime_error("npz_load: failed fread");
-        }
-
-        uint16_t compr_method = *reinterpret_cast<uint16_t*>(&local_header[0]+8);
-        uint32_t compr_bytes = *reinterpret_cast<uint32_t*>(&local_header[0]+18);
-        uint32_t uncompr_bytes = *reinterpret_cast<uint32_t*>(&local_header[0]+22);
-
-        if(compr_method == 0) {arrays[varname] = load_the_npy_file(fp);}
-        else {arrays[varname] = load_the_npz_array(fp,compr_bytes,uncompr_bytes);}
-    }
-
-    fclose(fp);
-    return arrays;  
-}
-
-cnpy::NpyArray cnpy::npz_load(std::string fname, std::string varname) {
-    FILE* fp = fopen(fname.c_str(),"rb");
-
-    if(!fp) throw std::runtime_error("npz_load: Unable to open file "+fname);
-
-    while(1) {
-        std::vector<char> local_header(30);
-        size_t header_res = fread(&local_header[0],sizeof(char),30,fp);
-        if(header_res != 30)
-            throw std::runtime_error("npz_load: failed fread");
-
-        //if we've reached the global header, stop reading
-        if(local_header[2] != 0x03 || local_header[3] != 0x04) break;
-
-        //read in the variable name
-        uint16_t name_len = *(uint16_t*) &local_header[26];
-        std::string vname(name_len,' ');
-        size_t vname_res = fread(&vname[0],sizeof(char),name_len,fp);      
-        if(vname_res != name_len)
-            throw std::runtime_error("npz_load: failed fread");
-        vname.erase(vname.end()-4,vname.end()); //erase the lagging .npy
-
-        //read in the extra field
-        uint16_t extra_field_len = *(uint16_t*) &local_header[28];
-        fseek(fp,extra_field_len,SEEK_CUR); //skip past the extra field
-        
-        uint16_t compr_method = *reinterpret_cast<uint16_t*>(&local_header[0]+8);
-        uint32_t compr_bytes = *reinterpret_cast<uint32_t*>(&local_header[0]+18);
-        uint32_t uncompr_bytes = *reinterpret_cast<uint32_t*>(&local_header[0]+22);
-
-        if(vname == varname) {
-            NpyArray array  = (compr_method == 0) ? load_the_npy_file(fp) : load_the_npz_array(fp,compr_bytes,uncompr_bytes);
-            fclose(fp);
-            return array;
-        }
-        else {
-            //skip past the data
-            uint32_t size = *(uint32_t*) &local_header[22];
-            fseek(fp,size,SEEK_CUR);
-        }
-    }
-
-    fclose(fp);
-
-    //if we get here, we haven't found the variable in the file
-    throw std::runtime_error("npz_load: Variable name "+varname+" not found in "+fname);
-}
-
-cnpy::NpyArray cnpy::npy_load(std::string fname) {
-
-    FILE* fp = fopen(fname.c_str(), "rb");
-
-    if(!fp) throw std::runtime_error("npy_load: Unable to open file "+fname);
-
-    NpyArray arr = load_the_npy_file(fp);
-
-    fclose(fp);
-    return arr;
-}
-
-
-
diff --git a/ThirdParty/cnpy/cnpy.hpp b/ThirdParty/cnpy/cnpy.hpp
deleted file mode 100644
index ffbcdb4b522b37b94f3d962bad9c9665d42b776b..0000000000000000000000000000000000000000
--- a/ThirdParty/cnpy/cnpy.hpp
+++ /dev/null
@@ -1,266 +0,0 @@
-//Copyright (C) 2011  Carl Rogers
-//Released under MIT License
-//license available in LICENSE file, or at http://www.opensource.org/licenses/mit-license.php
-
-#pragma once
-
-#include<string>
-#include<stdexcept>
-#include<sstream>
-#include<vector>
-#include<cstdio>
-#include<typeinfo>
-#include<iostream>
-#include<cassert>
-#include<zlib.h>
-#include<map>
-#include<memory>
-#include<stdint.h>
-#include<numeric>
-
-namespace cnpy {
-
-    struct NpyArray {
-        NpyArray(const std::vector<size_t>& _shape, size_t _word_size, bool _fortran_order) :
-            shape(_shape), word_size(_word_size), fortran_order(_fortran_order)
-        {
-            num_vals = 1;
-            for(size_t i = 0;i < shape.size();i++) num_vals *= shape[i];
-            data_holder = std::shared_ptr<std::vector<char>>(
-                new std::vector<char>(num_vals * word_size));
-        }
-
-        NpyArray() : shape(0), word_size(0), fortran_order(0), num_vals(0) { }
-
-        template<typename T>
-        T* data() {
-            return reinterpret_cast<T*>(&(*data_holder)[0]);
-        }
-
-        template<typename T>
-        const T* data() const {
-            return reinterpret_cast<T*>(&(*data_holder)[0]);
-        }
-
-        template<typename T>
-        std::vector<T> as_vec() const {
-            const T* p = data<T>();
-            return std::vector<T>(p, p+num_vals);
-        }
-
-        size_t num_bytes() const {
-            return data_holder->size();
-        }
-
-        std::shared_ptr<std::vector<char>> data_holder;
-        std::vector<size_t> shape;
-        size_t word_size;
-        bool fortran_order;
-        size_t num_vals;
-    };
-   
-    using npz_t = std::map<std::string, NpyArray>; 
-
-    char BigEndianTest();
-    char map_type(const std::type_info& t);
-    template<typename T> std::vector<char> create_npy_header(const std::vector<size_t>& shape);
-    void parse_npy_header(FILE* fp,size_t& word_size, std::vector<size_t>& shape, bool& fortran_order);
-    void parse_npy_header(unsigned char* buffer,size_t& word_size, std::vector<size_t>& shape, bool& fortran_order);
-    void parse_zip_footer(FILE* fp, uint16_t& nrecs, size_t& global_header_size, size_t& global_header_offset);
-    npz_t npz_load(std::string fname);
-    NpyArray npz_load(std::string fname, std::string varname);
-    NpyArray npy_load(std::string fname);
-
-    template<typename T> std::vector<char>& operator+=(std::vector<char>& lhs, const T rhs) {
-        //write in little endian
-        for(size_t byte = 0; byte < sizeof(T); byte++) {
-            char val = *((char*)&rhs+byte); 
-            lhs.push_back(val);
-        }
-        return lhs;
-    }
-
-    template<> std::vector<char>& operator+=(std::vector<char>& lhs, const std::string rhs);
-    template<> std::vector<char>& operator+=(std::vector<char>& lhs, const char* rhs);
-
-
-    template<typename T> void npy_save(std::string fname, const T* data, const std::vector<size_t> shape, std::string mode = "w") {
-        FILE* fp = NULL;
-        std::vector<size_t> true_data_shape; //if appending, the shape of existing + new data
-
-        if(mode == "a") fp = fopen(fname.c_str(),"r+b");
-
-        if(fp) {
-            //file exists. we need to append to it. read the header, modify the array size
-            size_t word_size;
-            bool fortran_order;
-            parse_npy_header(fp,word_size,true_data_shape,fortran_order);
-            assert(!fortran_order);
-
-            if(word_size != sizeof(T)) {
-                std::cout<<"libnpy error: "<<fname<<" has word size "<<word_size<<" but npy_save appending data sized "<<sizeof(T)<<"\n";
-                assert( word_size == sizeof(T) );
-            }
-            if(true_data_shape.size() != shape.size()) {
-                std::cout<<"libnpy error: npy_save attempting to append misdimensioned data to "<<fname<<"\n";
-                assert(true_data_shape.size() != shape.size());
-            }
-
-            for(size_t i = 1; i < shape.size(); i++) {
-                if(shape[i] != true_data_shape[i]) {
-                    std::cout<<"libnpy error: npy_save attempting to append misshaped data to "<<fname<<"\n";
-                    assert(shape[i] == true_data_shape[i]);
-                }
-            }
-            true_data_shape[0] += shape[0];
-        }
-        else {
-            fp = fopen(fname.c_str(),"wb");
-            true_data_shape = shape;
-        }
-
-        std::vector<char> header = create_npy_header<T>(true_data_shape);
-        size_t nels = std::accumulate(shape.begin(),shape.end(),1,std::multiplies<size_t>());
-
-        fseek(fp,0,SEEK_SET);
-        fwrite(&header[0],sizeof(char),header.size(),fp);
-        fseek(fp,0,SEEK_END);
-        fwrite(data,sizeof(T),nels,fp);
-        fclose(fp);
-    }
-
-    template<typename T> void npz_save(std::string zipname, std::string fname, const T* data, const std::vector<size_t>& shape, std::string mode = "w")
-    {
-        //first, append a .npy to the fname
-        fname += ".npy";
-
-        //now, on with the show
-        FILE* fp = NULL;
-        uint16_t nrecs = 0;
-        size_t global_header_offset = 0;
-        std::vector<char> global_header;
-
-        if(mode == "a") fp = fopen(zipname.c_str(),"r+b");
-
-        if(fp) {
-            //zip file exists. we need to add a new npy file to it.
-            //first read the footer. this gives us the offset and size of the global header
-            //then read and store the global header.
-            //below, we will write the the new data at the start of the global header then append the global header and footer below it
-            size_t global_header_size;
-            parse_zip_footer(fp,nrecs,global_header_size,global_header_offset);
-            fseek(fp,global_header_offset,SEEK_SET);
-            global_header.resize(global_header_size);
-            size_t res = fread(&global_header[0],sizeof(char),global_header_size,fp);
-            if(res != global_header_size){
-                throw std::runtime_error("npz_save: header read error while adding to existing zip");
-            }
-            fseek(fp,global_header_offset,SEEK_SET);
-        }
-        else {
-            fp = fopen(zipname.c_str(),"wb");
-        }
-
-        std::vector<char> npy_header = create_npy_header<T>(shape);
-
-        size_t nels = std::accumulate(shape.begin(),shape.end(),1,std::multiplies<size_t>());
-        size_t nbytes = nels*sizeof(T) + npy_header.size();
-
-        //get the CRC of the data to be added
-        uint32_t crc = crc32(0L,(uint8_t*)&npy_header[0],npy_header.size());
-        crc = crc32(crc,(uint8_t*)data,nels*sizeof(T));
-
-        //build the local header
-        std::vector<char> local_header;
-        local_header += "PK"; //first part of sig
-        local_header += (uint16_t) 0x0403; //second part of sig
-        local_header += (uint16_t) 20; //min version to extract
-        local_header += (uint16_t) 0; //general purpose bit flag
-        local_header += (uint16_t) 0; //compression method
-        local_header += (uint16_t) 0; //file last mod time
-        local_header += (uint16_t) 0;     //file last mod date
-        local_header += (uint32_t) crc; //crc
-        local_header += (uint32_t) nbytes; //compressed size
-        local_header += (uint32_t) nbytes; //uncompressed size
-        local_header += (uint16_t) fname.size(); //fname length
-        local_header += (uint16_t) 0; //extra field length
-        local_header += fname;
-
-        //build global header
-        global_header += "PK"; //first part of sig
-        global_header += (uint16_t) 0x0201; //second part of sig
-        global_header += (uint16_t) 20; //version made by
-        global_header.insert(global_header.end(),local_header.begin()+4,local_header.begin()+30);
-        global_header += (uint16_t) 0; //file comment length
-        global_header += (uint16_t) 0; //disk number where file starts
-        global_header += (uint16_t) 0; //internal file attributes
-        global_header += (uint32_t) 0; //external file attributes
-        global_header += (uint32_t) global_header_offset; //relative offset of local file header, since it begins where the global header used to begin
-        global_header += fname;
-
-        //build footer
-        std::vector<char> footer;
-        footer += "PK"; //first part of sig
-        footer += (uint16_t) 0x0605; //second part of sig
-        footer += (uint16_t) 0; //number of this disk
-        footer += (uint16_t) 0; //disk where footer starts
-        footer += (uint16_t) (nrecs+1); //number of records on this disk
-        footer += (uint16_t) (nrecs+1); //total number of records
-        footer += (uint32_t) global_header.size(); //nbytes of global headers
-        footer += (uint32_t) (global_header_offset + nbytes + local_header.size()); //offset of start of global headers, since global header now starts after newly written array
-        footer += (uint16_t) 0; //zip file comment length
-
-        //write everything
-        fwrite(&local_header[0],sizeof(char),local_header.size(),fp);
-        fwrite(&npy_header[0],sizeof(char),npy_header.size(),fp);
-        fwrite(data,sizeof(T),nels,fp);
-        fwrite(&global_header[0],sizeof(char),global_header.size(),fp);
-        fwrite(&footer[0],sizeof(char),footer.size(),fp);
-        fclose(fp);
-    }
-
-    template<typename T> void npy_save(std::string fname, const std::vector<T> data, std::string mode = "w") {
-        std::vector<size_t> shape;
-        shape.push_back(data.size());
-        npy_save(fname, &data[0], shape, mode);
-    }
-
-    template<typename T> void npz_save(std::string zipname, std::string fname, const std::vector<T> data, std::string mode = "w") {
-        std::vector<size_t> shape;
-        shape.push_back(data.size());
-        npz_save(zipname, fname, &data[0], shape, mode);
-    }
-
-    template<typename T> std::vector<char> create_npy_header(const std::vector<size_t>& shape) {  
-
-        std::vector<char> dict;
-        dict += "{'descr': '";
-        dict += BigEndianTest();
-        dict += map_type(typeid(T));
-        dict += std::to_string(sizeof(T));
-        dict += "', 'fortran_order': False, 'shape': (";
-        dict += std::to_string(shape[0]);
-        for(size_t i = 1;i < shape.size();i++) {
-            dict += ", ";
-            dict += std::to_string(shape[i]);
-        }
-        if(shape.size() == 1) dict += ",";
-        dict += "), }";
-        //pad with spaces so that preamble+dict is modulo 16 bytes. preamble is 10 bytes. dict needs to end with \n
-        int remainder = 16 - (10 + dict.size()) % 16;
-        dict.insert(dict.end(),remainder,' ');
-        dict.back() = '\n';
-
-        std::vector<char> header;
-        header += (char) 0x93;
-        header += "NUMPY";
-        header += (char) 0x01; //major version of numpy format
-        header += (char) 0x00; //minor version of numpy format
-        header += (uint16_t) dict.size();
-        header.insert(header.end(),dict.begin(),dict.end());
-
-        return header;
-    }
-
-
-}
diff --git a/ThirdParty/eigen-eigen-b3f3d4950030.tar.bz2 b/ThirdParty/eigen-eigen-b3f3d4950030.tar.bz2
deleted file mode 100644
index f7571b34f31468787f82a953029475b9896796a4..0000000000000000000000000000000000000000
Binary files a/ThirdParty/eigen-eigen-b3f3d4950030.tar.bz2 and /dev/null differ
diff --git a/ThirdParty/phys/units/quantity_io_symbols.hpp b/ThirdParty/phys/units/quantity_io_symbols.hpp
deleted file mode 100644
index 41dc6189b484a3824e8146d676025c996728cb50..0000000000000000000000000000000000000000
--- a/ThirdParty/phys/units/quantity_io_symbols.hpp
+++ /dev/null
@@ -1,57 +0,0 @@
-/**
- * \file quantity_io_symbols.hpp
- *
- * \brief   load all available unit names and symbols.
- * \author  Martin Moene
- * \date    7 September 2013
- * \since   1.0
- *
- * Copyright 2013 Universiteit Leiden. All rights reserved.
- * This code is provided as-is, with no warrantee of correctness.
- *
- * Distributed under the Boost Software License, Version 1.0. (See accompanying
- * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
- */
-
-#ifndef PHYS_UNITS_QUANTITY_IO_SYMBOLS_HPP_INCLUDED
-#define PHYS_UNITS_QUANTITY_IO_SYMBOLS_HPP_INCLUDED
-
-#include "phys/units/quantity_io_ampere.hpp"
-//prefer Hertz
-//#include "phys/units/quantity_io_becquerel.hpp"
-#include "phys/units/quantity_io_candela.hpp"
-//prefer kelvin
-//#include "phys/units/quantity_io_celsius.hpp"
-#include "phys/units/quantity_io_coulomb.hpp"
-#include "phys/units/quantity_io_dimensionless.hpp"
-#include "phys/units/quantity_io_farad.hpp"
-//prefer sievert
-//#include "phys/units/quantity_io_gray.hpp"
-#include "phys/units/quantity_io_joule.hpp"
-#include "phys/units/quantity_io_henry.hpp"
-#include "phys/units/quantity_io_hertz.hpp"
-#include "phys/units/quantity_io_kelvin.hpp"
-#include "phys/units/quantity_io_kilogram.hpp"
-//prefer Cd base unit
-//#include "phys/units/quantity_io_lumen.hpp"
-#include "phys/units/quantity_io_lux.hpp"
-#include "phys/units/quantity_io_meter.hpp"
-#include "phys/units/quantity_io_newton.hpp"
-#include "phys/units/quantity_io_ohm.hpp"
-#include "phys/units/quantity_io_pascal.hpp"
-#include "phys/units/quantity_io_radian.hpp"
-#include "phys/units/quantity_io_second.hpp"
-#include "phys/units/quantity_io_siemens.hpp"
-#include "phys/units/quantity_io_sievert.hpp"
-#include "phys/units/quantity_io_speed.hpp"
-#include "phys/units/quantity_io_steradian.hpp"
-#include "phys/units/quantity_io_tesla.hpp"
-#include "phys/units/quantity_io_volt.hpp"
-#include "phys/units/quantity_io_watt.hpp"
-#include "phys/units/quantity_io_weber.hpp"
-
-#endif // PHYS_UNITS_QUANTITY_IO_SYMBOLS_HPP_INCLUDED
-
-/*
- * end of file
- */
diff --git a/ThirdParty/spdlog b/ThirdParty/spdlog
deleted file mode 160000
index 58875bdcd790dd2f5cb8d95ef1da7970de0a4530..0000000000000000000000000000000000000000
--- a/ThirdParty/spdlog
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit 58875bdcd790dd2f5cb8d95ef1da7970de0a4530
diff --git a/Tools/CMakeLists.txt b/Tools/CMakeLists.txt
deleted file mode 100644
index 109b6d44abe7794a1f9ee39456ae24d883872e30..0000000000000000000000000000000000000000
--- a/Tools/CMakeLists.txt
+++ /dev/null
@@ -1,6 +0,0 @@
-set (TOOLS_FILES plot_tracks.sh plot_crossings.sh read_hist.py)
-
-install (
-  FILES ${TOOLS_FILES} 
-  DESTINATION share/tools
-  )
diff --git a/Tools/plot_tracks.py b/Tools/plot_tracks.py
deleted file mode 100644
index 2c9b063b3ec5675019a5934286848a389b6dd79e..0000000000000000000000000000000000000000
--- a/Tools/plot_tracks.py
+++ /dev/null
@@ -1,63 +0,0 @@
-#!/usr/bin/env python3
-
-import os
-import sys, getopt
-import re
-
-# (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.
-
-# with this script you can plot an animation of output of TrackWriter
-
-tracks_dat="$1"
-if [ -z "$tracks_dat" ]; then
-  echo "usage: $0 <tracks.dat> [output.gif]" >&2
-  exit 1
-fi
-
-directory=$(dirname "$tracks_dat")
-hadrons_dat="$directory/hadrons.dat"
-muons_dat="$directory/muons.dat"
-electrons_dat="$directory/electrons.dat"
-gammas_dat="$directory/gammas.dat"
-
-#if [ "$muons_dat" -ot "$tracks_dat" -o "$hadrons_dat" -ot "$muons_dat" ]; then
-  echo "splitting $tracks_dat into $muons_dat and $hadrons_dat."
-  cat "$tracks_dat" | egrep '^\s+-*13\s' > "$muons_dat"
-  cat "$tracks_dat" | egrep '^\s+-*11\s' > "$electrons_dat"
-  cat "$tracks_dat" | egrep '^\s+-*22\s' > "$gammas_dat"
-  cat "$tracks_dat" | egrep -v '^\s+-*13\s' | egrep -v '^\s+-*11\s' | egrep -v '^\s+-*22\s' > "$hadrons_dat"
-#fi
-
-output="$2"
-if [ -z "$output" ]; then
-  output="$tracks_dat.gif"
-fi
-
-echo "creating $output..."
-
-cat <<EOF | gnuplot
-set term gif animate size 900,900
-set output "$output"
-
-#set zrange [0:40e3]
-#set xrange [-10:10]
-#set yrange [-10:10]
-set xlabel "x / m"
-set ylabel "y / m"
-set zlabel "z / m"
-set title "CORSIKA 8 preliminary"
-
-do for [t=0:359:1] {
-# for separate file per angle:
-#	set output sprintf("%03d_$output", t)
-
-	set view 80, t
-        splot "$gammas_dat" u 3:4:5:6:7:8 w vectors nohead lt rgb "orange" t "", "$electrons_dat" u 3:4:5:6:7:8 w vectors nohead lt rgb "blue" t "", "$muons_dat" u 3:4:5:6:7:8 w vectors nohead lt rgb "red" t "", "$hadrons_dat" u 3:4:5:6:7:8 w vectors nohead  lc rgb "black" t ""
-}
-EOF
-
-exit $?
diff --git a/Tools/read_hist.py b/Tools/read_hist.py
deleted file mode 100755
index 53fbf0871679c5846ba4884125dab41855d58191..0000000000000000000000000000000000000000
--- a/Tools/read_hist.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import numpy as np
-import boost_histogram as bh
-import operator
-import functools
-
-def read_hist(filename):
-    """
-    read numpy file produced with CORSIKA 8's save_hist() function into
-    boost-histogram object.
-    """
-
-    d = np.load(filename)
-    axistypes = d['axistypes'].view('c')
-    overflow = d['overflow']
-    underflow = d['underflow']
-    
-    axes = []
-    for i, (at, has_overflow, has_underflow) in enumerate(zip(axistypes, overflow, underflow)):
-        if at == b'c':
-            axes.append(bh.axis.Variable(d[f'binedges_{i}'], overflow=has_overflow, underflow=has_underflow))
-        elif at == b'd':
-            axes.append(bh.axis.IntCategory(d[f'bins_{i}'], growth=(not has_overflow)))
-        
-    h = bh.Histogram(*axes)
-    h.view(flow=True)[:] = d['data']
-    
-    return h
-
diff --git a/CMakeModules/CodeCoverage b/cmake/CodeCoverage
similarity index 100%
rename from CMakeModules/CodeCoverage
rename to cmake/CodeCoverage
diff --git a/CMakeModules/CodeCoverage.cmake b/cmake/CodeCoverage.cmake
similarity index 100%
rename from CMakeModules/CodeCoverage.cmake
rename to cmake/CodeCoverage.cmake
diff --git a/cmake/CorsikaDefines.cmake b/cmake/CorsikaDefines.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..f2b08a67b68f9b92a0a809e01cf38ab5496611b0
--- /dev/null
+++ b/cmake/CorsikaDefines.cmake
@@ -0,0 +1,24 @@
+#
+# Floating point exception support - select implementation to use
+#
+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")
+endif ()
+
+
+#
+# General OS Detection
+#
+if (${CMAKE_SYSTEM_NAME} MATCHES "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")
+elseif (${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
+    set(CORSIKA_OS_MAC TRUE)  
+    set (CORSIKA_OS "Mac") 
+endif()
\ No newline at end of file
diff --git a/cmake/CorsikaUtilities.cmake b/cmake/CorsikaUtilities.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..c71e9bea31a13a80e195552f671ac89963af3759
--- /dev/null
+++ b/cmake/CorsikaUtilities.cmake
@@ -0,0 +1,122 @@
+#
+# (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+#
+# See file AUTHORS for a list of contributors.
+#
+# 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.
+#
+
+#################################################
+#
+# central macro to register unit tests in cmake
+#
+# 1) Simple use:
+# Pass the name of the test.cc file as the first
+# argument, without the ".cc" extention.
+#
+# Example: CORSIKA_ADD_TEST (testSomething)
+#
+# This generates target testSomething from file testSomething.cc.
+#
+# 2) Customize sources:
+# If 1) doesn't work, use the SOURCES keyword to explicitly
+# specify the sources.
+#
+# Example: CORSIKA_ADD_TEST (testSomething
+#              SOURCES source1.cc source2.cc someheader.h)
+#
+# 3) Customize sanitizers:
+# You can override which sanitizers are compiled into the
+# test, but only do this if the defaults do not work.
+#
+# Example: CORSIKA_ADD_TEST (testSomething SANITIZE undefined)
+#
+# Only uses the sanitizer for undefined behavior.
+#
+# In all cases, you can further customize the target with
+# target_link_libraries(testSomething ...) and so on.
+#
+# TEMPORARY: All sanitizers are currently globally disabled by default, to enable them,
+# set CORSIKA_SANITIZERS_ENABLED to TRUE.
+function (CORSIKA_ADD_TEST)
+  cmake_parse_arguments (PARSE_ARGV 1 C8_ADD_TEST "" "SANITIZE" "SOURCES")
+  set (name ${ARGV0})
+
+  if (NOT C8_ADD_TEST_SOURCES)
+    set (sources ${name}.cc)
+  else ()
+    set (sources ${C8_ADD_TEST_SOURCES})
+  endif ()
+
+  if (NOT C8_ADD_TEST_SANITIZE)
+    set(sanitize "address,undefined")
+  else ()
+    set(sanitize ${C8_ADD_TEST_SANITIZE})
+  endif ()
+
+  add_executable (${name} ${sources})
+  target_link_libraries (${name} CORSIKA8 CONAN_PKG::catch2 CorsikaTestingCommon)
+  target_compile_options (${name} PRIVATE -g) # do not skip asserts
+  target_include_directories (${name} PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
+  file (MAKE_DIRECTORY ${PROJECT_BINARY_DIR}/test_outputs/)
+  if (CORSIKA_SANITIZERS_ENABLED)
+    # -O1 is suggested in clang docs to get reasonable performance
+    target_compile_options (${name} PRIVATE -O1 -fno-omit-frame-pointer -fsanitize=${sanitize} -fno-sanitize-recover=all)
+    set_target_properties (${name} PROPERTIES LINK_FLAGS "-fsanitize=${sanitize}")
+  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
new file mode 100644
index 0000000000000000000000000000000000000000..5a18d9772d405ba09479ede5c6c8f6b69d6d37ad
--- /dev/null
+++ b/cmake/FindCORSIKA.cmake
@@ -0,0 +1,6 @@
+
+add_library(CORSIKA INTERFACE)
+
+target_compile_coptions(CORSIKA
+  INTERFACE
+  ${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/cmake/FindCORSIKA8.cmake b/cmake/FindCORSIKA8.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..39f936323ef79cb71ab0843ea41c7c85eca66526
--- /dev/null
+++ b/cmake/FindCORSIKA8.cmake
@@ -0,0 +1,9 @@
+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
new file mode 100644
index 0000000000000000000000000000000000000000..1a8549ce0c9170c05ef3d4bdce60150de486ee97
--- /dev/null
+++ b/cmake/FindCorsika8.cmake
@@ -0,0 +1,63 @@
+#
+# 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/FindPhysUnits.cmake b/cmake/FindPhysUnits.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..321ec043e882c5c98d611a6daf5c23438e8b4c81
--- /dev/null
+++ b/cmake/FindPhysUnits.cmake
@@ -0,0 +1,8 @@
+add_library (PhysUnits INTERFACE)
+
+target_compile_options (PhysUnits
+  INTERFACE
+  -I${CMAKE_SOURCE_DIR}/externals/phys_units
+  )
+
+set (PhysUnits_FOUND True)
diff --git a/cmake/FindPythia8.cmake b/cmake/FindPythia8.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..88535cede70317aa9c2c6a53f5e84e08f1924a34
--- /dev/null
+++ b/cmake/FindPythia8.cmake
@@ -0,0 +1,74 @@
+#
+# (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+#
+# See file AUTHORS for a list of contributors.
+#
+# 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.
+#
+
+#################################################
+#
+# run pythia8-config and interpret result
+#
+
+function (_Pythia8_CONFIG_ option variable type doc)
+  execute_process(COMMAND ${Pythia8_CONFIG} ${option}
+    OUTPUT_VARIABLE _local_out_
+    RESULT_VARIABLE _local_res_)
+  string (REGEX REPLACE "\n$" "" _local_out_ "${_local_out_}")
+  if (NOT ${_local_res_} EQUAL 0)
+    message ("Error in running ${Pythia8_CONFIG} ${option}")
+  else ()
+    set (${variable} "${_local_out_}" CACHE ${type} ${doc})
+  endif ()
+endfunction (_Pythia8_CONFIG_)
+  
+
+
+#################################################
+# 
+# Searched Pythia8 on system. Expect pythia8-config in PATH, or typical installation location
+#
+# This module defines
+# HAVE_Pythia8
+# Pythia8_INCLUDE_DIR   where to locate Pythia.h file
+# Pythia8_LIBRARY       where to find the libpythia8 library
+# Pythia8_LIBRARIES     (not cached) the libraries to link against to use Pythia8
+# Pythia8_VERSION       version of Pythia8 if found
+#
+
+set (_SEARCH_Pythia8_
+  ${PROJECT_BINARY_DIR}/ThirdParty/pythia8-install
+  ${PYTHIA8}
+  $ENV{PYTHIA8}
+  ${PYTHIA8DIR}
+  $ENV{PYTHIA8DIR}
+  ${PYTHIA8_ROOT}
+  $ENV{PYTHIA8_ROOT}
+  ${PYTHIA8_DIR}
+  $ENV{PYTHIA8_DIR}
+  /opt/pythia8
+  )
+
+find_program (Pythia8_CONFIG
+  NAME pythia8-config
+  PATHS ${_SEARCH_Pythia8_}
+  PATH_SUFFIXES "/bin"
+  DOC "The location of the pythia8-config script")
+
+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")
+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)
diff --git a/cmake/conan.cmake b/cmake/conan.cmake
new file mode 100644
index 0000000000000000000000000000000000000000..28d2126d4309c8f584eb59f295eb24f90adea25a
--- /dev/null
+++ b/cmake/conan.cmake
@@ -0,0 +1,516 @@
+# The MIT License (MIT)
+
+# Copyright (c) 2018 JFrog
+
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+
+# The above copyright notice and this permission notice shall be included in all
+# copies or substantial portions of the Software.
+
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+
+
+# This file comes from: https://github.com/conan-io/cmake-conan. Please refer
+# to this repository for issues and documentation.
+
+# Its purpose is to wrap and launch Conan C/C++ Package Manager when cmake is called.
+# It will take CMake current settings (os, compiler, compiler version, architecture)
+# and translate them to conan settings for installing and retrieving dependencies.
+
+# It is intended to facilitate developers building projects that have conan dependencies,
+# but it is only necessary on the end-user side. It is not necessary to create conan
+# packages, in fact it shouldn't be use for that. Check the project documentation.
+
+
+include(CMakeParseArguments)
+
+function(_get_msvc_ide_version result)
+    set(${result} "" PARENT_SCOPE)
+    if(NOT MSVC_VERSION VERSION_LESS 1400 AND MSVC_VERSION VERSION_LESS 1500)
+        set(${result} 8 PARENT_SCOPE)
+    elseif(NOT MSVC_VERSION VERSION_LESS 1500 AND MSVC_VERSION VERSION_LESS 1600)
+        set(${result} 9 PARENT_SCOPE)
+    elseif(NOT MSVC_VERSION VERSION_LESS 1600 AND MSVC_VERSION VERSION_LESS 1700)
+        set(${result} 10 PARENT_SCOPE)
+    elseif(NOT MSVC_VERSION VERSION_LESS 1700 AND MSVC_VERSION VERSION_LESS 1800)
+        set(${result} 11 PARENT_SCOPE)
+    elseif(NOT MSVC_VERSION VERSION_LESS 1800 AND MSVC_VERSION VERSION_LESS 1900)
+        set(${result} 12 PARENT_SCOPE)
+    elseif(NOT MSVC_VERSION VERSION_LESS 1900 AND MSVC_VERSION VERSION_LESS 1910)
+        set(${result} 14 PARENT_SCOPE)
+    elseif(NOT MSVC_VERSION VERSION_LESS 1910 AND MSVC_VERSION VERSION_LESS 1920)
+        set(${result} 15 PARENT_SCOPE)
+    else()
+        message(FATAL_ERROR "Conan: Unknown MSVC compiler version [${MSVC_VERSION}]")
+    endif()
+endfunction()
+
+function(conan_cmake_settings result)
+    #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER})
+    #message(STATUS "COMPILER " ${CMAKE_CXX_COMPILER_ID})
+    #message(STATUS "VERSION " ${CMAKE_CXX_COMPILER_VERSION})
+    #message(STATUS "FLAGS " ${CMAKE_LANG_FLAGS})
+    #message(STATUS "LIB ARCH " ${CMAKE_CXX_LIBRARY_ARCHITECTURE})
+    #message(STATUS "BUILD TYPE " ${CMAKE_BUILD_TYPE})
+    #message(STATUS "GENERATOR " ${CMAKE_GENERATOR})
+    #message(STATUS "GENERATOR WIN64 " ${CMAKE_CL_64})
+
+    message(STATUS "Conan: Automatic detection of conan settings from cmake")
+
+    parse_arguments(${ARGV})
+
+    if(ARGUMENTS_BUILD_TYPE)
+        set(_CONAN_SETTING_BUILD_TYPE ${ARGUMENTS_BUILD_TYPE})
+    elseif(CMAKE_BUILD_TYPE)
+        set(_CONAN_SETTING_BUILD_TYPE ${CMAKE_BUILD_TYPE})
+    else()
+        message(FATAL_ERROR "Please specify in command line CMAKE_BUILD_TYPE (-DCMAKE_BUILD_TYPE=Release)")
+    endif()
+    if(ARGUMENTS_ARCH)
+        set(_CONAN_SETTING_ARCH ${ARGUMENTS_ARCH})
+    endif()
+    #handle -s os setting
+    if(CMAKE_SYSTEM_NAME)
+        #use default conan os setting if CMAKE_SYSTEM_NAME is not defined
+        set(CONAN_SYSTEM_NAME ${CMAKE_SYSTEM_NAME})
+        if(${CMAKE_SYSTEM_NAME} STREQUAL "Darwin")
+            set(CONAN_SYSTEM_NAME Macos)
+        endif()
+        set(CONAN_SUPPORTED_PLATFORMS Windows Linux Macos Android iOS FreeBSD WindowsStore)
+        list (FIND CONAN_SUPPORTED_PLATFORMS "${CONAN_SYSTEM_NAME}" _index)
+        if (${_index} GREATER -1)
+            #check if the cmake system is a conan supported one
+            set(_CONAN_SETTING_OS ${CONAN_SYSTEM_NAME})
+        else()
+            message(FATAL_ERROR "cmake system ${CONAN_SYSTEM_NAME} is not supported by conan. Use one of ${CONAN_SUPPORTED_PLATFORMS}")
+        endif()
+    endif()
+
+    get_property(_languages GLOBAL PROPERTY ENABLED_LANGUAGES)
+    if (";${_languages};" MATCHES ";CXX;")
+        set(LANGUAGE CXX)
+        set(USING_CXX 1)
+    elseif (";${_languages};" MATCHES ";C;")
+        set(LANGUAGE C)
+        set(USING_CXX 0)
+    else ()
+        message(FATAL_ERROR "Conan: Neither C or C++ was detected as a language for the project. Unabled to detect compiler version.")
+    endif()
+
+    if (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL GNU)
+        # using GCC
+        # TODO: Handle other params
+        string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION})
+        list(GET VERSION_LIST 0 MAJOR)
+        list(GET VERSION_LIST 1 MINOR)
+        set(COMPILER_VERSION ${MAJOR}.${MINOR})
+        if(${MAJOR} GREATER 4)
+            set(COMPILER_VERSION ${MAJOR})
+        endif()
+        set(_CONAN_SETTING_COMPILER gcc)
+        set(_CONAN_SETTING_COMPILER_VERSION ${COMPILER_VERSION})
+        if (USING_CXX)
+            conan_cmake_detect_gnu_libcxx(_LIBCXX)
+            set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX})
+        endif ()
+    elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL AppleClang)
+        # using AppleClang
+        string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION})
+        list(GET VERSION_LIST 0 MAJOR)
+        list(GET VERSION_LIST 1 MINOR)
+        set(_CONAN_SETTING_COMPILER apple-clang)
+        set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR})
+        if (USING_CXX)
+            set(_CONAN_SETTING_COMPILER_LIBCXX libc++)
+        endif ()
+    elseif (${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL Clang)
+        string(REPLACE "." ";" VERSION_LIST ${CMAKE_${LANGUAGE}_COMPILER_VERSION})
+        list(GET VERSION_LIST 0 MAJOR)
+        list(GET VERSION_LIST 1 MINOR)
+        if(APPLE)
+            cmake_policy(GET CMP0025 APPLE_CLANG_POLICY_ENABLED)
+            if(NOT APPLE_CLANG_POLICY_ENABLED)
+                message(STATUS "Conan: APPLE and Clang detected. Assuming apple-clang compiler. Set CMP0025 to avoid it")
+                set(_CONAN_SETTING_COMPILER apple-clang)
+                set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR})
+            else()
+                set(_CONAN_SETTING_COMPILER clang)
+                set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR})
+            endif()
+            if (USING_CXX)
+                set(_CONAN_SETTING_COMPILER_LIBCXX libc++)
+            endif ()
+        else()
+            set(_CONAN_SETTING_COMPILER clang)
+            if(${MAJOR} GREATER 7)
+                set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR})
+            else()
+                set(_CONAN_SETTING_COMPILER_VERSION ${MAJOR}.${MINOR})
+            endif()
+            if (USING_CXX)
+                conan_cmake_detect_gnu_libcxx(_LIBCXX)
+                set(_CONAN_SETTING_COMPILER_LIBCXX ${_LIBCXX})
+            endif ()
+        endif()
+    elseif(${CMAKE_${LANGUAGE}_COMPILER_ID} STREQUAL MSVC)
+        set(_VISUAL "Visual Studio")
+        _get_msvc_ide_version(_VISUAL_VERSION)
+        if("${_VISUAL_VERSION}" STREQUAL "")
+            message(FATAL_ERROR "Conan: Visual Studio not recognized")
+        else()
+            set(_CONAN_SETTING_COMPILER ${_VISUAL})
+            set(_CONAN_SETTING_COMPILER_VERSION ${_VISUAL_VERSION})
+        endif()
+
+        if(NOT _CONAN_SETTING_ARCH)
+            if (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "64")
+                set(_CONAN_SETTING_ARCH x86_64)
+            elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "^ARM")
+                message(STATUS "Conan: Using default ARM architecture from MSVC")
+                set(_CONAN_SETTING_ARCH armv6)
+            elseif (MSVC_${LANGUAGE}_ARCHITECTURE_ID MATCHES "86")
+                set(_CONAN_SETTING_ARCH x86)
+            else ()
+                message(FATAL_ERROR "Conan: Unknown MSVC architecture [${MSVC_${LANGUAGE}_ARCHITECTURE_ID}]")
+            endif()
+        endif()
+
+        conan_cmake_detect_vs_runtime(_vs_runtime)
+        message(STATUS "Conan: Detected VS runtime: ${_vs_runtime}")
+        set(_CONAN_SETTING_COMPILER_RUNTIME ${_vs_runtime})
+
+        if (CMAKE_GENERATOR_TOOLSET)
+            set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET})
+        elseif(CMAKE_VS_PLATFORM_TOOLSET AND (CMAKE_GENERATOR STREQUAL "Ninja"))
+            set(_CONAN_SETTING_COMPILER_TOOLSET ${CMAKE_VS_PLATFORM_TOOLSET})
+        endif()
+    else()
+        message(FATAL_ERROR "Conan: compiler setup not recognized")
+    endif()
+
+    # If profile is defined it is used
+    if(CMAKE_BUILD_TYPE STREQUAL "Debug" AND ARGUMENTS_DEBUG_PROFILE)
+        set(_SETTINGS -pr ${ARGUMENTS_DEBUG_PROFILE})
+    elseif(CMAKE_BUILD_TYPE STREQUAL "Release" AND ARGUMENTS_RELEASE_PROFILE)
+        set(_SETTINGS -pr ${ARGUMENTS_RELEASE_PROFILE})
+    elseif(CMAKE_BUILD_TYPE STREQUAL "RelWithDebInfo" AND ARGUMENTS_RELWITHDEBINFO_PROFILE)
+        set(_SETTINGS -pr ${ARGUMENTS_RELWITHDEBINFO_PROFILE})
+    elseif(CMAKE_BUILD_TYPE STREQUAL "MinSizeRel" AND ARGUMENTS_MINSIZEREL_PROFILE)
+        set(_SETTINGS -pr ${ARGUMENTS_MINSIZEREL_PROFILE})
+    elseif(ARGUMENTS_PROFILE)
+        set(_SETTINGS -pr ${ARGUMENTS_PROFILE})
+    endif()
+
+    if(NOT _SETTINGS OR ARGUMENTS_PROFILE_AUTO STREQUAL "ALL")
+        set(ARGUMENTS_PROFILE_AUTO arch build_type compiler compiler.version
+                                   compiler.runtime compiler.libcxx compiler.toolset)
+    endif()
+
+    # Automatic from CMake
+    foreach(ARG ${ARGUMENTS_PROFILE_AUTO})
+        string(TOUPPER ${ARG} _arg_name)
+        string(REPLACE "." "_" _arg_name ${_arg_name})
+        if(_CONAN_SETTING_${_arg_name})
+            set(_SETTINGS ${_SETTINGS} -s ${ARG}=${_CONAN_SETTING_${_arg_name}})
+        endif()
+    endforeach()
+
+    foreach(ARG ${ARGUMENTS_SETTINGS})
+        set(_SETTINGS ${_SETTINGS} -s ${ARG})
+    endforeach()
+
+    message(STATUS "Conan: Settings= ${_SETTINGS}")
+
+    set(${result} ${_SETTINGS} PARENT_SCOPE)
+endfunction()
+
+
+function(conan_cmake_detect_gnu_libcxx result)
+    # Allow -D_GLIBCXX_USE_CXX11_ABI=ON/OFF as argument to cmake
+    if(DEFINED _GLIBCXX_USE_CXX11_ABI)
+        if(_GLIBCXX_USE_CXX11_ABI)
+            set(${result} libstdc++11 PARENT_SCOPE)
+            return()
+        else()
+            set(${result} libstdc++ PARENT_SCOPE)
+            return()
+        endif()
+    endif()
+
+    # Check if there's any add_definitions(-D_GLIBCXX_USE_CXX11_ABI=0)
+    get_directory_property(defines DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} COMPILE_DEFINITIONS)
+    foreach(define ${defines})
+        if(define STREQUAL "_GLIBCXX_USE_CXX11_ABI=0")
+            set(${result} libstdc++ PARENT_SCOPE)
+            return()
+        endif()
+    endforeach()
+
+    # Use C++11 stdlib as default if gcc is 5.1+
+    if(CMAKE_CXX_COMPILER_VERSION VERSION_LESS "5.1")
+      set(${result} libstdc++ PARENT_SCOPE)
+    else()
+      set(${result} libstdc++11 PARENT_SCOPE)
+    endif()
+endfunction()
+
+
+function(conan_cmake_detect_vs_runtime result)
+    string(TOUPPER ${CMAKE_BUILD_TYPE} build_type)
+    set(variables CMAKE_CXX_FLAGS_${build_type} CMAKE_C_FLAGS_${build_type} CMAKE_CXX_FLAGS CMAKE_C_FLAGS)
+    foreach(variable ${variables})
+        string(REPLACE " " ";" flags ${${variable}})
+        foreach (flag ${flags})
+            if(${flag} STREQUAL "/MD" OR ${flag} STREQUAL "/MDd" OR ${flag} STREQUAL "/MT" OR ${flag} STREQUAL "/MTd")
+                string(SUBSTRING ${flag} 1 -1 runtime)
+                set(${result} ${runtime} PARENT_SCOPE)
+                return()
+            endif()
+        endforeach()
+    endforeach()
+    if(${build_type} STREQUAL "DEBUG")
+        set(${result} "MDd" PARENT_SCOPE)
+    else()
+        set(${result} "MD" PARENT_SCOPE)
+    endif()
+endfunction()
+
+
+macro(parse_arguments)
+  set(options BASIC_SETUP CMAKE_TARGETS UPDATE KEEP_RPATHS NO_OUTPUT_DIRS OUTPUT_QUIET)
+  set(oneValueArgs CONANFILE DEBUG_PROFILE RELEASE_PROFILE RELWITHDEBINFO_PROFILE MINSIZEREL_PROFILE
+                   PROFILE ARCH BUILD_TYPE INSTALL_FOLDER CONAN_COMMAND)
+  set(multiValueArgs REQUIRES OPTIONS IMPORTS SETTINGS BUILD ENV GENERATORS PROFILE_AUTO)
+  cmake_parse_arguments(ARGUMENTS "${options}" "${oneValueArgs}" "${multiValueArgs}" ${ARGN})
+endmacro()
+
+function(conan_cmake_install)
+    # Calls "conan install"
+    # Argument BUILD is equivalant to --build={missing, PkgName,...} or
+    # --build when argument is 'BUILD all' (which builds all packages from source)
+    # Argument CONAN_COMMAND, to specify the conan path, e.g. in case of running from source
+    # cmake does not identify conan as command, even if it is +x and it is in the path
+    parse_arguments(${ARGV})
+
+    if(CONAN_CMAKE_MULTI)
+        set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake_multi)
+    else()
+        set(ARGUMENTS_GENERATORS ${ARGUMENTS_GENERATORS} cmake)
+    endif()
+
+    set(CONAN_BUILD_POLICY "")
+    foreach(ARG ${ARGUMENTS_BUILD})
+        if(${ARG} STREQUAL "all")
+            set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build)
+            break()
+        else()
+            set(CONAN_BUILD_POLICY ${CONAN_BUILD_POLICY} --build=${ARG})
+        endif()
+    endforeach()
+    if(ARGUMENTS_CONAN_COMMAND)
+       set(conan_command ${ARGUMENTS_CONAN_COMMAND})
+    else()
+      set(conan_command conan)
+    endif()
+    set(CONAN_OPTIONS "")
+    if(ARGUMENTS_CONANFILE)
+      set(CONANFILE ${CMAKE_CURRENT_SOURCE_DIR}/${ARGUMENTS_CONANFILE})
+      # A conan file has been specified - apply specified options as well if provided
+      foreach(ARG ${ARGUMENTS_OPTIONS})
+          set(CONAN_OPTIONS ${CONAN_OPTIONS} -o=${ARG})
+      endforeach()
+    else()
+      set(CONANFILE ".")
+    endif()
+    if(ARGUMENTS_UPDATE)
+      set(CONAN_INSTALL_UPDATE --update)
+    endif()
+    set(CONAN_INSTALL_FOLDER "")
+    if(ARGUMENTS_INSTALL_FOLDER)
+      set(CONAN_INSTALL_FOLDER -if=${ARGUMENTS_INSTALL_FOLDER})
+    endif()
+    foreach(ARG ${ARGUMENTS_GENERATORS})
+        set(CONAN_GENERATORS ${CONAN_GENERATORS} -g=${ARG})
+    endforeach()
+    foreach(ARG ${ARGUMENTS_ENV})
+        set(CONAN_ENV_VARS ${CONAN_ENV_VARS} -e=${ARG})
+    endforeach()
+    set(conan_args install ${CONANFILE} ${settings} ${CONAN_ENV_VARS} ${CONAN_GENERATORS} ${CONAN_BUILD_POLICY} ${CONAN_INSTALL_UPDATE} ${CONAN_OPTIONS} ${CONAN_INSTALL_FOLDER})
+
+    string (REPLACE ";" " " _conan_args "${conan_args}")
+    message(STATUS "Conan executing: ${conan_command} ${_conan_args}")
+
+    if(ARGUMENTS_OUTPUT_QUIET)
+      set(OUTPUT_CONTROL OUTPUT_QUIET)
+    endif()
+
+    execute_process(COMMAND ${conan_command} ${conan_args}
+                     RESULT_VARIABLE return_code
+                     OUTPUT_VARIABLE conan_output
+                     ERROR_VARIABLE conan_output					 
+                     WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR})
+
+    message(STATUS "${conan_output}")
+				 
+    if(NOT "${return_code}" STREQUAL "0")
+      message(FATAL_ERROR "Conan install failed='${return_code}'")
+    endif()
+
+endfunction()
+
+
+function(conan_cmake_setup_conanfile)
+  parse_arguments(${ARGV})
+  if(ARGUMENTS_CONANFILE)
+    # configure_file will make sure cmake re-runs when conanfile is updated
+    configure_file(${ARGUMENTS_CONANFILE} ${ARGUMENTS_CONANFILE}.junk)
+    file(REMOVE ${CMAKE_CURRENT_BINARY_DIR}/${ARGUMENTS_CONANFILE}.junk)
+  else()
+    conan_cmake_generate_conanfile(${ARGV})
+  endif()
+endfunction()
+
+function(conan_cmake_generate_conanfile)
+  # Generate, writing in disk a conanfile.txt with the requires, options, and imports
+  # specified as arguments
+  # This will be considered as temporary file, generated in CMAKE_CURRENT_BINARY_DIR)
+  parse_arguments(${ARGV})
+  set(_FN "${CMAKE_CURRENT_BINARY_DIR}/conanfile.txt")
+
+  file(WRITE ${_FN} "[generators]\ncmake\n\n[requires]\n")
+  foreach(ARG ${ARGUMENTS_REQUIRES})
+    file(APPEND ${_FN} ${ARG} "\n")
+  endforeach()
+
+  file(APPEND ${_FN} ${ARG} "\n[options]\n")
+  foreach(ARG ${ARGUMENTS_OPTIONS})
+    file(APPEND ${_FN} ${ARG} "\n")
+  endforeach()
+
+  file(APPEND ${_FN} ${ARG} "\n[imports]\n")
+  foreach(ARG ${ARGUMENTS_IMPORTS})
+    file(APPEND ${_FN} ${ARG} "\n")
+  endforeach()
+endfunction()
+
+
+macro(conan_load_buildinfo)
+    if(CONAN_CMAKE_MULTI)
+      set(_CONANBUILDINFO conanbuildinfo_multi.cmake)
+    else()
+      set(_CONANBUILDINFO conanbuildinfo.cmake)
+    endif()
+    # Checks for the existence of conanbuildinfo.cmake, and loads it
+    # important that it is macro, so variables defined at parent scope
+    if(EXISTS "${CMAKE_CURRENT_BINARY_DIR}/${_CONANBUILDINFO}")
+      message(STATUS "Conan: Loading ${_CONANBUILDINFO}")
+      include(${CMAKE_CURRENT_BINARY_DIR}/${_CONANBUILDINFO})
+    else()
+      message(FATAL_ERROR "${_CONANBUILDINFO} doesn't exist in ${CMAKE_CURRENT_BINARY_DIR}")
+    endif()
+endmacro()
+
+
+macro(conan_cmake_run)
+    parse_arguments(${ARGV})
+
+    if(CMAKE_CONFIGURATION_TYPES AND NOT CMAKE_BUILD_TYPE AND NOT CONAN_EXPORTED
+            AND NOT ARGUMENTS_BUILD_TYPE)
+        set(CONAN_CMAKE_MULTI ON)
+        message(STATUS "Conan: Using cmake-multi generator")
+    else()
+        set(CONAN_CMAKE_MULTI OFF)
+    endif()
+
+    if(NOT CONAN_EXPORTED)
+        conan_cmake_setup_conanfile(${ARGV})
+        if(CONAN_CMAKE_MULTI)
+            foreach(CMAKE_BUILD_TYPE "Release" "Debug")
+                set(ENV{CONAN_IMPORT_PATH} ${CMAKE_BUILD_TYPE})
+                conan_cmake_settings(settings ${ARGV})
+                conan_cmake_install(SETTINGS ${settings} ${ARGV})
+            endforeach()
+            set(CMAKE_BUILD_TYPE)
+        else()
+            conan_cmake_settings(settings ${ARGV})
+            conan_cmake_install(SETTINGS ${settings} ${ARGV})
+        endif()
+    endif()
+
+    conan_load_buildinfo()
+
+    if(ARGUMENTS_BASIC_SETUP)
+        foreach(_option CMAKE_TARGETS KEEP_RPATHS NO_OUTPUT_DIRS)
+            if(ARGUMENTS_${_option})
+                if(${_option} STREQUAL "CMAKE_TARGETS")
+                    list(APPEND _setup_options "TARGETS")
+                else()
+                    list(APPEND _setup_options ${_option})
+                endif()
+            endif()
+        endforeach()
+        conan_basic_setup(${_setup_options})
+    endif()
+endmacro()
+
+macro(conan_check)
+    # Checks conan availability in PATH
+    # Arguments REQUIRED and VERSION are optional
+    # Example usage:
+    #    conan_check(VERSION 1.0.0 REQUIRED)
+    message(STATUS "Conan: checking conan executable in path")
+    set(options REQUIRED)
+    set(oneValueArgs VERSION)
+    cmake_parse_arguments(CONAN "${options}" "${oneValueArgs}" "" ${ARGN})
+
+    find_program(CONAN_CMD conan)
+    if(NOT CONAN_CMD AND CONAN_REQUIRED)
+        message(FATAL_ERROR "Conan executable not found!")
+    endif()
+    message(STATUS "Conan: Found program ${CONAN_CMD}")
+    execute_process(COMMAND ${CONAN_CMD} --version
+                    OUTPUT_VARIABLE CONAN_VERSION_OUTPUT
+                    ERROR_VARIABLE CONAN_VERSION_OUTPUT)
+    message(STATUS "Conan: Version found ${CONAN_VERSION_OUTPUT}")
+
+    if(DEFINED CONAN_VERSION)
+        string(REGEX MATCH ".*Conan version ([0-9]+\.[0-9]+\.[0-9]+)" FOO
+            "${CONAN_VERSION_OUTPUT}")
+        if(${CMAKE_MATCH_1} VERSION_LESS ${CONAN_VERSION})
+            message(FATAL_ERROR "Conan outdated. Installed: ${CONAN_VERSION}, \
+                required: ${CONAN_VERSION_REQUIRED}. Consider updating via 'pip \
+                install conan --upgrade'.")
+        endif()
+    endif()
+endmacro()
+
+macro(conan_add_remote)
+    # Adds a remote
+    # Arguments URL and NAME are required, INDEX is optional
+    # Example usage:
+    #    conan_add_remote(NAME bincrafters INDEX 1
+    #       URL https://api.bintray.com/conan/bincrafters/public-conan)
+    set(oneValueArgs URL NAME INDEX)
+    cmake_parse_arguments(CONAN "" "${oneValueArgs}" "" ${ARGN})
+
+    if(DEFINED CONAN_INDEX)
+        set(CONAN_INDEX_ARG "-i ${CONAN_INDEX}")
+    endif()
+
+    message(STATUS "Conan: Adding ${CONAN_NAME} remote repositoy (${CONAN_URL})")
+    execute_process(COMMAND ${CONAN_CMD} remote add ${CONAN_NAME} ${CONAN_URL}
+      ${CONAN_INDEX_ARG} -f)
+endmacro()
+
diff --git a/conanfile.txt b/conanfile.txt
new file mode 100644
index 0000000000000000000000000000000000000000..85c1238b40b290ca181660b0a8bda665b1c88c5b
--- /dev/null
+++ b/conanfile.txt
@@ -0,0 +1,9 @@
+[requires]
+spdlog/1.8.0
+catch2/2.13.3
+eigen/3.3.8
+boost/1.74.0
+zlib/1.2.11
+
+[generators]
+cmake
diff --git a/corsika/corsika.hpp b/corsika/corsika.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..36fa90b6315e83504a74d20ba6c66f657929e409
--- /dev/null
+++ b/corsika/corsika.hpp
@@ -0,0 +1,55 @@
+/*
+ * (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
+
+/**
+  @file corsika.hpp
+
+  The CORSIKA 8 air shower simulation framework.
+
+  @mainpage Technical documentation of the CORSIKA 8 software framework
+
+  Software documentatin and reference guide for the CORSIKA 8
+  software framework for air shower simulations. CORSIKA 8 is developed
+  at <a href="https://gitlab.ikp.kit.edu/AirShowerPhysics">https://gitlab.ikp.kit.edu</a>.
+  If you got the code from somewhere else, consider to switch to the official development
+  repository. If you want to report bugs, or want to suggest features or future
+  development, please submit an "issue" on this gitlab server. We only accept Issues and
+  discussion via our central development server https://gitlab.ikp.kit.edu.
+
+  Write to corsika-devel@lists.kit.edu, or even register yourself at
+  https://www.lists.kit.edu/sympa/info/corsika-devel to get in contact
+  with other developers.
+
+  For more information about the project, see @ref md_README.
+  **/
+
+/*! Usage:
+ *  to get the version X.YY.Z,
+ *  set CORSIKA_VERSION X0YY0Z
+ */
+#define CORSIKA_VERSION 800000
+
+/*! \def CORSIKA_MAJOR_VERSION
+ *  \brief The preprocessor macro \p CORSIKA_MAJOR_VERSION encodes the
+ *         major version number of CORSIKA.
+ */
+#define CORSIKA_MAJOR_VERSION (CORSIKA_VERSION / 100000)
+
+/*! \def CORSIKA_MINOR_VERSION
+ *  \brief The preprocessor macro \p CORSIKA_MINOR_VERSION encodes the
+ *         minor version number of CORSIKA.
+ */
+#define CORSIKA_MINOR_VERSION (CORSIKA_VERSION / 100 % 1000)
+
+/*! \def CORSIKA_PATCH_NUMBER
+ *  \brief The preprocessor macro \p CORSIKA_PATCH_NUMBER encodes the
+ *         patch number of the CORSIKA library.
+ */
+#define CORSIKA_PATCH_NUMBER 0
diff --git a/corsika/detail/framework/analytics/ClassTimer.inl b/corsika/detail/framework/analytics/ClassTimer.inl
new file mode 100644
index 0000000000000000000000000000000000000000..dca6fb5ce128761146936929895d3ce8a65ddb00
--- /dev/null
+++ b/corsika/detail/framework/analytics/ClassTimer.inl
@@ -0,0 +1,95 @@
+/*
+ * (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.
+ */
+
+// Another possibility:
+// https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Execute-Around_Pointer
+// for a more global approach
+//
+// In this case here only a single function is measured via member function pointer.
+
+#pragma once
+
+#include <chrono>
+#include <utility>
+
+namespace corsika {
+
+  // Common
+
+  template <typename TClass, typename TRet, typename... TArgs,
+            TRet (TClass::*TFuncPtr)(TArgs...), typename TTimer>
+  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) {
+    this->start_ = ClassTimerImpl<TClass, TTimer>::clock_type::now();
+    auto tmp = (this->obj_.*TFuncPtr)(std::forward<TArgs>(args)...);
+    this->timeDiff_ = std::chrono::duration_cast<
+        typename ClassTimerImpl<TClass, TTimer>::duration_type>(
+        ClassTimerImpl<TClass, TTimer>::clock_type::now() - this->start_);
+    return tmp;
+  }
+
+  // Specialisation 1
+
+  template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...),
+            typename TTimer>
+  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) {
+    this->start_ = ClassTimerImpl<TClass, TTimer>::clock_type::now();
+    (this->obj_.*TFuncPtr)(std::forward<TArgs>(args)...);
+    this->timeDiff_ = std::chrono::duration_cast<
+        typename ClassTimerImpl<TClass, TTimer>::duration_type>(
+        ClassTimerImpl<TClass, TTimer>::clock_type::now() - this->start_);
+    return;
+  }
+
+  /// Specialisation 2
+
+  template <typename TClass, typename TRet, typename... TArgs,
+            TRet (TClass::*TFuncPtr)(TArgs...) const, typename TTimer>
+  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(
+      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<
+        typename ClassTimerImpl<TClass, TTimer>::duration_type>(
+        ClassTimerImpl<TClass, TTimer>::clock_type::now() - this->start_);
+    return tmp;
+  }
+
+  /// Specialisation 3
+  template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...) const,
+            typename TTimer>
+  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(
+      TArgs... args) {
+    this->start_ = ClassTimerImpl<TClass, TTimer>::clock_type::now();
+    (this->obj_.*TFuncPtr)(std::forward<TArgs>(args)...);
+    this->timeDiff_ = std::chrono::duration_cast<
+        typename ClassTimerImpl<TClass, TTimer>::duration_type>(
+        ClassTimerImpl<TClass, TTimer>::clock_type::now() - this->start_);
+    return;
+  }
+
+} // namespace corsika
\ No newline at end of file
diff --git a/corsika/detail/framework/analytics/FunctionTimer.inl b/corsika/detail/framework/analytics/FunctionTimer.inl
new file mode 100644
index 0000000000000000000000000000000000000000..e00bebc338db81acf099763e01adb7cc3a3d3bf5
--- /dev/null
+++ b/corsika/detail/framework/analytics/FunctionTimer.inl
@@ -0,0 +1,29 @@
+/*
+ * (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 <chrono>
+#include <utility>
+
+namespace corsika {
+
+  template <typename TFunc, typename TTime>
+  FunctionTimer<TFunc, TTime>::FunctionTimer(TFunc f)
+      : function_(f) {}
+
+  template <typename TFunc, typename TTime>
+  template <typename... TArgs>
+  auto FunctionTimer<TFunc, TTime>::operator()(TArgs&&... args)
+      -> std::invoke_result_t<TFunc, TArgs...> {
+    this->startTimer();
+    auto tmp = function_(std::forward<TArgs>(args)...);
+    this->stopTimer();
+    return tmp;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/core/Cascade.inl b/corsika/detail/framework/core/Cascade.inl
new file mode 100644
index 0000000000000000000000000000000000000000..8cd5b2e39f3f87e303ee51aecf9de64ecdda9131
--- /dev/null
+++ b/corsika/detail/framework/core/Cascade.inl
@@ -0,0 +1,292 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/process/ProcessReturn.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>
+#include <limits>
+#include <type_traits>
+
+namespace corsika {
+
+  template <typename TTracking, typename TProcessList, typename TStack,
+            typename TStackView>
+  void Cascade<TTracking, TProcessList, TStack, TStackView>::run() {
+    setNodes(); // put each particle on stack in correct environment volume
+
+    while (!stack_.isEmpty()) {
+      while (!stack_.isEmpty()) {
+        CORSIKA_LOG_TRACE("Stack: {}", stack_.asString());
+        count_++;
+
+        auto pNext = stack_.getNextParticle();
+
+        CORSIKA_LOG_TRACE(
+            "============== next particle : count={}, pid={}, "
+            ", stack entries={}"
+            ", stack deleted={}",
+            count_, pNext.getPID(), stack_.getEntries(), stack_.getErased());
+
+        step(pNext);
+        sequence_.doStack(stack_);
+      }
+      // do cascade equations, which can put new particles on Stack,
+      // thus, the double loop
+      // doCascadeEquations();
+    }
+  }
+
+  template <typename TTracking, typename TProcessList, typename TStack,
+            typename TStackView>
+  void Cascade<TTracking, TProcessList, TStack, TStackView>::forceInteraction() {
+    CORSIKA_LOG_TRACE("forced interaction!");
+    setNodes();
+    auto vParticle = stack_.getNextParticle();
+    TStackView secondaries(vParticle);
+    interaction(secondaries);
+    sequence_.doSecondaries(secondaries);
+    vParticle.erase(); // primary particle is done
+  }
+
+  template <typename TTracking, typename TProcessList, typename TStack,
+            typename TStackView>
+  void Cascade<TTracking, TProcessList, TStack, TStackView>::step(Particle& vParticle) {
+
+    // determine combined total interaction length (inverse)
+    InverseGrammageType const total_inv_lambda =
+        sequence_.getInverseInteractionLength(vParticle);
+
+    // sample random exponential step length in grammage
+    ExponentialDistribution expDist(1 / total_inv_lambda);
+    GrammageType const next_interact = expDist(rng_);
+
+    CORSIKA_LOG_DEBUG(
+        "total_lambda={} g/cm2, "
+        ", next_interact={} g/cm2",
+        double((1. / total_inv_lambda) / 1_g * 1_cm * 1_cm),
+        double(next_interact / 1_g * 1_cm * 1_cm));
+
+    auto const* currentLogicalNode = vParticle.getNode();
+
+    // assert that particle stays outside void Universe if it has no
+    // model properties set
+    assert((currentLogicalNode != &*environment_.getUniverse() ||
+            environment_.getUniverse()->hasModelProperties()) &&
+           "FATAL: The environment model has no valid properties set!");
+
+    // determine combined total inverse decay time
+    InverseTimeType const total_inv_lifetime = sequence_.getInverseLifetime(vParticle);
+
+    // sample random exponential decay time
+    ExponentialDistribution expDistDecay(1 / total_inv_lifetime);
+    TimeType const next_decay = expDistDecay(rng_);
+
+    CORSIKA_LOG_DEBUG(
+        "total_lifetime={} s"
+        ", next_decay={} s",
+        (1 / total_inv_lifetime) / 1_s, next_decay / 1_s);
+
+    // convert next_decay from time to length [m]
+    LengthType const distance_decay = next_decay * vParticle.getMomentum().getNorm() /
+                                      vParticle.getEnergy() * constants::c;
+
+    // determine geometric tracking
+    auto [step, nextVol] = tracking_.getTrack(vParticle);
+    auto geomMaxLength = step.getLength(1);
+
+    // convert next_step from grammage to length
+    LengthType const distance_interact =
+        currentLogicalNode->getModelProperties().getArclengthFromGrammage(step,
+                                                                          next_interact);
+
+    // determine the maximum geometric step length
+    LengthType const continuous_max_dist = sequence_.getMaxStepLength(vParticle, step);
+
+    // take minimum of geometry, interaction, decay for next step
+    auto const min_distance =
+        std::min({distance_interact, distance_decay, continuous_max_dist, geomMaxLength});
+
+    CORSIKA_LOG_DEBUG(
+        "transport particle by : {} m "
+        "Medium transition after: {} m "
+        "Decay after: {} m "
+        "Interaction after: {} m "
+        "Continuous limit: {} m ",
+        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);
+    step.setLength(min_distance);
+    vParticle.setPosition(step.getPosition(1));
+    // assumption: tracking does not change absolute momentum:
+    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) {
+      CORSIKA_LOG_DEBUG("Cascade: delete absorbed particle PID={} E={} GeV",
+                        vParticle.getPID(), vParticle.getEnergy() / 1_GeV);
+      if (!vParticle.isErased()) vParticle.erase();
+      return;
+    }
+
+    CORSIKA_LOG_DEBUG("sth. happening before geometric limit ? {}",
+                      ((min_distance < geomMaxLength) ? "yes" : "no"));
+
+    if (min_distance < geomMaxLength) { // interaction to happen within geometric 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)
+
+      TStackView secondaries(vParticle);
+
+      if (min_distance < continuous_max_dist) {
+        /*
+          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
+          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();
+      } else { // step-length limitation within volume
+
+        CORSIKA_LOG_DEBUG("step-length limitation");
+        // no further physics happens here. just proceed to next step.
+      }
+
+      [[maybe_unused]] auto const assertion = [&] {
+        auto const* numericalNodeAfterStep =
+            environment_.getUniverse()->getContainingNode(vParticle.getPosition());
+        CORSIKA_LOG_TRACE(
+            "Geometry check: numericalNodeAfterStep={} currentLogicalNode={}",
+            fmt::ptr(numericalNodeAfterStep), fmt::ptr(currentLogicalNode));
+        return numericalNodeAfterStep == currentLogicalNode;
+      };
+
+      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)
+
+          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.
+
+          \todo: this must be fixed.
+        */
+
+        sequence_.doBoundaryCrossing(vParticle, *currentLogicalNode, *nextVol);
+      }
+    }
+  }
+
+  template <typename TTracking, typename TProcessList, typename TStack,
+            typename TStackView>
+  ProcessReturn Cascade<TTracking, TProcessList, TStack, TStackView>::decay(
+      TStackView& view) {
+    CORSIKA_LOG_DEBUG("decay");
+    InverseTimeType const actual_decay_time = sequence_.getInverseLifetime(view.parent());
+
+    UniformRealDistribution<InverseTimeType> uniDist(actual_decay_time);
+    const auto sample_process = uniDist(rng_);
+
+    auto const returnCode = sequence_.selectDecay(view, sample_process);
+    if (returnCode != ProcessReturn::Decayed) {
+      CORSIKA_LOG_WARN("Particle did not decay!");
+    }
+    setEventType(view, history::EventType::Decay);
+    return returnCode;
+  }
+
+  template <typename TTracking, typename TProcessList, typename TStack,
+            typename TStackView>
+  ProcessReturn Cascade<TTracking, TProcessList, TStack, TStackView>::interaction(
+      TStackView& view) {
+    CORSIKA_LOG_DEBUG("collide");
+
+    InverseGrammageType const current_inv_length =
+        sequence_.getInverseInteractionLength(view.parent());
+
+    UniformRealDistribution<InverseGrammageType> uniDist(current_inv_length);
+
+    const auto sample_process = uniDist(rng_);
+    auto const returnCode = sequence_.selectInteraction(view, sample_process);
+    if (returnCode != ProcessReturn::Interacted) {
+      CORSIKA_LOG_WARN("Particle did not interace!");
+    }
+    setEventType(view, history::EventType::Interaction);
+    return returnCode;
+  }
+
+  template <typename TTracking, typename TProcessList, typename TStack,
+            typename TStackView>
+  void Cascade<TTracking, TProcessList, TStack, TStackView>::setNodes() {
+    std::for_each(stack_.begin(), stack_.end(), [&](auto& p) {
+      auto const* numericalNode =
+          environment_.getUniverse()->getContainingNode(p.getPosition());
+      p.setNode(numericalNode);
+    });
+  }
+
+  template <typename TTracking, typename TProcessList, typename TStack,
+            typename TStackView>
+  void Cascade<TTracking, TProcessList, TStack, TStackView>::setEventType(
+      TStackView& view, [[maybe_unused]] history::EventType eventType) {
+    if constexpr (TStackView::has_event) {
+      for (auto&& sec : view) { sec.getEvent()->setEventType(eventType); }
+    }
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/core/Logging.inl b/corsika/detail/framework/core/Logging.inl
new file mode 100644
index 0000000000000000000000000000000000000000..e99e788b8de6a6eaf2e3965fb10c51049e720058
--- /dev/null
+++ b/corsika/detail/framework/core/Logging.inl
@@ -0,0 +1,72 @@
+/*
+ * (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 <spdlog/fmt/ostr.h> // will output whenerver a streaming operator is found
+#include <spdlog/sinks/stdout_color_sinks.h>
+#include <spdlog/spdlog.h>
+
+namespace corsika {
+
+  // many of these free functions are special to the logging
+  // infrastructure so we hide them in the corsika::logging namespace.
+  namespace logging {
+
+    /*
+     * 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");
+    }
+
+    template <typename TLogger>
+    inline auto reset_pattern(TLogger& logger) -> void {
+      logger->set_pattern(default_pattern);
+    }
+
+  } // namespace logging
+
+  inline std::shared_ptr<spdlog::logger> create_logger(std::string const& name,
+                                                       bool const defaultlog) {
+
+    // create the logger
+    // this is currently a colorized multi-threading safe logger
+    auto logger = spdlog::stdout_color_mt(name);
+
+    // set the default C8 format
+    logger->set_pattern(logging::default_pattern);
+
+    // if defaultlog is True, we set this as the default spdlog logger.
+    if (defaultlog) { spdlog::set_default_logger(logger); }
+
+    return logger;
+  }
+
+  inline std::shared_ptr<spdlog::logger> get_logger(std::string const& name,
+                                                    bool const defaultlog) {
+
+    // attempt to get the logger from the registry
+    auto logger = spdlog::get(name);
+
+    // weg found the logger, so just return it
+    if (logger) {
+      return logger;
+    } else { // logger was not found so create it
+      return create_logger(name, defaultlog);
+    }
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/core/ParticleProperties.inl b/corsika/detail/framework/core/ParticleProperties.inl
new file mode 100644
index 0000000000000000000000000000000000000000..6fc0bea950f383c6e12e537eabcd95cf66cbe4ca
--- /dev/null
+++ b/corsika/detail/framework/core/ParticleProperties.inl
@@ -0,0 +1,101 @@
+/*
+ * (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 <cmath>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+namespace corsika {
+
+  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) {
+    return particle::detail::pdg_codes[static_cast<CodeIntType>(p)];
+  }
+
+  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) {
+    return get_charge_number(code) * constants::e;
+  }
+
+  std::string_view constexpr get_name(Code const code) {
+    return particle::detail::names[static_cast<CodeIntType>(code)];
+  }
+
+  TimeType constexpr get_lifetime(Code const p) {
+    return particle::detail::lifetime[static_cast<CodeIntType>(p)] * second;
+  }
+
+  bool constexpr is_hadron(Code const p) {
+    return particle::detail::isHadron[static_cast<CodeIntType>(p)];
+  }
+
+  bool constexpr is_em(Code c) {
+    return c == Code::Electron || c == Code::Positron || c == Code::Gamma;
+  }
+
+  bool constexpr is_muon(Code c) { return c == Code::MuPlus || c == Code::MuMinus; }
+
+  bool constexpr is_neutrino(Code 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) {
+    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) {
+    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) {
+    return (code == Code::Nucleus) || (get_nucleus_A(code) != 0);
+  }
+
+  inline std::ostream& operator<<(std::ostream& stream, corsika::Code const code) {
+    return stream << get_name(code);
+  }
+
+  inline Code convert_from_PDG(PDGCode 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};
+    auto const k = static_cast<PDGCodeType>(p);
+    if (std::abs(k) <= maxPDG) {
+      return particle::detail::conversionArray[k + maxPDG];
+    } else {
+      return particle::detail::conversionMap.at(p);
+    }
+  }
+
+  inline HEPMassType get_nucleus_mass(unsigned int const A, unsigned int const Z) {
+    return get_mass(Code::Proton) * Z + (A - Z) * get_mass(Code::Neutron);
+  }
+
+  std::initializer_list<Code> constexpr get_all_particles() {
+    return particle::detail::all_particles;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/geometry/BaseVector.inl b/corsika/detail/framework/geometry/BaseVector.inl
new file mode 100644
index 0000000000000000000000000000000000000000..50ec4b03b249ec3bfbe89f41c25fd4f33d04dc3f
--- /dev/null
+++ b/corsika/detail/framework/geometry/BaseVector.inl
@@ -0,0 +1,31 @@
+/*
+ * (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 <corsika/framework/geometry/CoordinateSystem.hpp>
+#include <corsika/framework/geometry/QuantityVector.hpp>
+
+namespace corsika {
+
+  template <typename TDimension>
+  CoordinateSystemPtr BaseVector<TDimension>::getCoordinateSystem() const {
+    return cs_;
+  }
+
+  template <typename TDimension>
+  QuantityVector<TDimension> const& BaseVector<TDimension>::getQuantityVector() const {
+    return quantityVector_;
+  }
+
+  template <typename TDimension>
+  QuantityVector<TDimension>& BaseVector<TDimension>::getQuantityVector() {
+    return quantityVector_;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/geometry/CoordinateSystem.inl b/corsika/detail/framework/geometry/CoordinateSystem.inl
new file mode 100644
index 0000000000000000000000000000000000000000..efcf716d3320487334713dd79bdf77ddd67f0a24
--- /dev/null
+++ b/corsika/detail/framework/geometry/CoordinateSystem.inl
@@ -0,0 +1,139 @@
+/*
+ * (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 <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/QuantityVector.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <Eigen/Dense>
+
+#include <memory>
+#include <stdexcept>
+
+namespace corsika {
+
+  CoordinateSystemPtr CoordinateSystem::getReferenceCS() const { return referenceCS_; }
+
+  EigenTransform const& CoordinateSystem::getTransform() const { return transf_; }
+
+  inline bool CoordinateSystem::operator==(CoordinateSystem const& cs) const {
+    return referenceCS_ == cs.referenceCS_ && transf_.matrix() == cs.transf_.matrix();
+  }
+
+  inline bool CoordinateSystem::operator!=(CoordinateSystem const& cs) const {
+    return !(cs == *this);
+  }
+
+  /// find transformation between two CS, using most optimal common base
+  inline EigenTransform get_transformation(CoordinateSystem const& pFrom,
+                                           CoordinateSystem const& pTo) {
+    CoordinateSystem const* a{&pFrom};
+    CoordinateSystem const* b{&pTo};
+
+    while (a != b && b) {
+
+      // traverse pFrom
+      a = &pFrom;
+      while (a != b && a) { a = a->getReferenceCS().get(); }
+
+      if (a == b) break;
+
+      b = b->getReferenceCS().get();
+    }
+
+    if (a != b || a == nullptr) {
+      throw std::runtime_error("no connection between coordinate systems found!");
+    }
+
+    CoordinateSystem const* commonBase = a;
+    CoordinateSystem const* p = &pFrom;
+    EigenTransform t = EigenTransform::Identity();
+    while ((*p) != (*commonBase)) {
+      t = p->getTransform() * t;
+      p = p->getReferenceCS().get();
+    }
+
+    p = &pTo;
+
+    while (*p != *commonBase) {
+      t = t * p->getTransform().inverse(Eigen::TransformTraits::Isometry);
+      p = p->getReferenceCS().get();
+    }
+
+    return t;
+  }
+
+  inline CoordinateSystemPtr make_translation(CoordinateSystemPtr const& cs,
+                                              QuantityVector<length_d> const& vector) {
+    EigenTransform const translation{EigenTranslation(vector.getEigenVector())};
+    return std::make_shared<CoordinateSystem const>(CoordinateSystem(cs, translation));
+  }
+
+  template <typename TDim>
+  inline CoordinateSystemPtr make_rotationToZ(CoordinateSystemPtr const& cs,
+                                              Vector<TDim> const& vVec) {
+    auto const a = vVec.normalized().getComponents(cs).getEigenVector();
+    auto const a1 = a(0), a2 = a(1), a3 = a(2);
+
+    Eigen::Matrix3d A, B;
+
+    if (a3 > 0) {
+      auto const c = 1 / (1 + a3);
+      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 {
+      auto const c = 1 / (1 - a3);
+      A << 1, 0, a1,                      // .
+          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;  // .
+    }
+
+    return std::make_shared<CoordinateSystem const>(
+        CoordinateSystem(cs, EigenTransform(A + B)));
+  }
+
+  template <typename TDim>
+  inline CoordinateSystemPtr make_rotation(CoordinateSystemPtr const& cs,
+                                           QuantityVector<TDim> const& axis,
+                                           double const angle) {
+    if (axis.getEigenVector().isZero()) {
+      throw std::runtime_error("null-vector given as axis parameter");
+    }
+
+    EigenTransform const rotation{
+        Eigen::AngleAxisd(angle, axis.getEigenVector().normalized())};
+
+    return std::make_shared<CoordinateSystem const>(CoordinateSystem(cs, rotation));
+  }
+
+  template <typename TDim>
+  inline CoordinateSystemPtr make_translationAndRotation(
+      CoordinateSystemPtr const& cs, QuantityVector<length_d> const& translation,
+      QuantityVector<TDim> const& axis, double const angle) {
+    if (axis.getEigenVector().isZero()) {
+      throw std::runtime_error("null-vector given as axis parameter");
+    }
+
+    EigenTransform const transf{
+        Eigen::AngleAxisd(angle, axis.getEigenVector().normalized()) *
+        EigenTranslation(translation.getEigenVector())};
+
+    return std::make_shared<CoordinateSystem const>(CoordinateSystem(cs, transf));
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/geometry/FourVector.inl b/corsika/detail/framework/geometry/FourVector.inl
new file mode 100644
index 0000000000000000000000000000000000000000..6c0a039e5466ef46d2d3014b7e91a6f0621c922c
--- /dev/null
+++ b/corsika/detail/framework/geometry/FourVector.inl
@@ -0,0 +1,124 @@
+/*
+ * (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 <type_traits>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+namespace corsika {
+
+  template <typename TTimeType, typename TSpaceVecType>
+  TTimeType FourVector<TTimeType, TSpaceVecType>::getTimeLikeComponent() const {
+    return timeLike_;
+  }
+
+  template <typename TTimeType, typename TSpaceVecType>
+  TSpaceVecType& FourVector<TTimeType, TSpaceVecType>::getSpaceLikeComponents() {
+    return spaceLike_;
+  }
+
+  template <typename TTimeType, typename TSpaceVecType>
+  TSpaceVecType const& FourVector<TTimeType, TSpaceVecType>::getSpaceLikeComponents()
+      const {
+    return spaceLike_;
+  }
+
+  template <typename TTimeType, typename TSpaceVecType>
+  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
+  FourVector<TTimeType, TSpaceVecType>::getNorm() const {
+
+    return sqrt(abs(getNormSqr()));
+  }
+
+  template <typename TTimeType, typename TSpaceVecType>
+  bool FourVector<TTimeType, TSpaceVecType>::isTimelike() const {
+    return getTimeSquared() < spaceLike_.getSquaredNorm();
+  }
+
+  template <typename TTimeType, typename TSpaceVecType>
+  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) {
+    timeLike_ += b.timeLike_;
+    spaceLike_ += b.spaceLike_;
+
+    return *this;
+  }
+
+  template <typename TTimeType, typename TSpaceVecType>
+  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) {
+    timeLike_ *= b;
+    spaceLike_ *= b;
+    return *this;
+  }
+
+  template <typename TTimeType, typename TSpaceVecType>
+  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) {
+    *this /= b;
+    return *this;
+  }
+
+  template <typename TTimeType, typename TSpaceVecType>
+  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();
+    else
+      return timeLike_ * timeLike_ - spaceLike_.norm();
+  }
+
+  template <typename TTimeType, typename TSpaceVecType>
+  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)
+      return timeLike_ * timeLike_ * constants::cSquared;
+    else
+      return timeLike_ * timeLike_;
+  }
+
+  template <typename TTimeType, typename TSpaceVecType>
+  inline std::ostream& operator<<(
+      std::ostream& os, corsika::FourVector<TTimeType, TSpaceVecType> const qv) {
+
+    os << '(' << qv.timeLike_ << ", " << qv.spaceLike_ << ") ";
+    return os;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/geometry/Helix.inl b/corsika/detail/framework/geometry/Helix.inl
new file mode 100644
index 0000000000000000000000000000000000000000..daf0c310fcd14762c3942d20221be35130431f06
--- /dev/null
+++ b/corsika/detail/framework/geometry/Helix.inl
@@ -0,0 +1,38 @@
+/*
+ * (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/Point.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <cmath>
+
+namespace corsika {
+
+  LengthType Helix::getRadius() const { return radius_; }
+
+  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 {
+    return getPosition(getTimeFromArclength(l));
+  }
+
+  LengthType Helix::getArcLength(TimeType const t1, TimeType const t2) const {
+    return (vPar_ + vPerp_).getNorm() * (t2 - t1);
+  }
+
+  TimeType Helix::getTimeFromArclength(LengthType const l) const {
+    return l / (vPar_ + vPerp_).getNorm();
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/geometry/LeapFrogTrajectory.inl b/corsika/detail/framework/geometry/LeapFrogTrajectory.inl
new file mode 100644
index 0000000000000000000000000000000000000000..9ff574321434aa6c7c495ea3fc8db432e4a47879
--- /dev/null
+++ b/corsika/detail/framework/geometry/LeapFrogTrajectory.inl
@@ -0,0 +1,61 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+
+namespace corsika {
+
+  inline Line LeapFrogTrajectory::getLine() const {
+    auto D = getPosition(1) - getPosition(0);
+    auto d = D.getNorm();
+    auto v = initialVelocity_;
+    if (d > 1_um) { // if trajectory is ultra-short, we do not
+                    // re-calculate velocity, just use initial
+                    // value. Otherwise, this is numerically unstable
+      v = D / d * getVelocity(0).getNorm();
+    }
+    return Line(getPosition(0), v);
+  }
+
+  inline Point LeapFrogTrajectory::getPosition(double const u) const {
+    Point position = initialPosition_ + initialVelocity_ * timeStep_ * u / 2;
+    VelocityVector velocity =
+        initialVelocity_ + initialVelocity_.cross(magneticfield_) * timeStep_ * u * k_;
+    return position + velocity * timeStep_ * u / 2;
+  }
+
+  inline VelocityVector LeapFrogTrajectory::getVelocity(double const u) const {
+    return initialVelocity_ + initialVelocity_.cross(magneticfield_) * timeStep_ * u * k_;
+  }
+
+  inline DirectionVector LeapFrogTrajectory::getDirection(double const u) const {
+    return getVelocity(u).normalized();
+  }
+
+  inline TimeType LeapFrogTrajectory::getDuration(double const u) const {
+    return u * timeStep_ *
+           (double(getVelocity(u).getNorm() / initialVelocity_.getNorm()) + 1.0) / 2;
+  }
+
+  inline LengthType LeapFrogTrajectory::getLength(double const u) const {
+    return timeStep_ * initialVelocity_.getNorm() * u;
+  }
+
+  inline void LeapFrogTrajectory::setLength(LengthType const limit) {
+    if (initialVelocity_.getNorm() == 0_m / 1_s) setDuration(0_s);
+    setDuration(limit / initialVelocity_.getNorm());
+  }
+
+  inline void LeapFrogTrajectory::setDuration(TimeType const limit) { timeStep_ = limit; }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/geometry/Line.inl b/corsika/detail/framework/geometry/Line.inl
new file mode 100644
index 0000000000000000000000000000000000000000..864aa72126d494c4e9ee2af64cb0582e5c8c7d10
--- /dev/null
+++ b/corsika/detail/framework/geometry/Line.inl
@@ -0,0 +1,43 @@
+/*
+ * (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/Point.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+namespace corsika {
+
+  inline Point Line::getPosition(TimeType const t) const {
+    return start_point_ + velocity_ * t;
+  }
+
+  inline VelocityVector const& Line::getVelocity(TimeType const) const {
+    return velocity_;
+  }
+
+  inline Point Line::getPositionFromArclength(LengthType const l) const {
+    return start_point_ + velocity_.normalized() * l;
+  }
+
+  inline LengthType Line::getArcLength(TimeType const t1, TimeType const t2) const {
+    return velocity_.getNorm() * (t2 - t1);
+  }
+
+  inline TimeType Line::getTimeFromArclength(LengthType const t) const {
+    return t / velocity_.getNorm();
+  }
+
+  inline Point const& Line::getStartPoint() const { return start_point_; }
+
+  inline DirectionVector Line::getDirection() const { return velocity_.normalized(); }
+
+  inline VelocityVector const& Line::getVelocity() const { return velocity_; }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/geometry/Plane.inl b/corsika/detail/framework/geometry/Plane.inl
new file mode 100644
index 0000000000000000000000000000000000000000..e158e04d36a59bbec08e1b3e2b7e6aebc76ee7ab
--- /dev/null
+++ b/corsika/detail/framework/geometry/Plane.inl
@@ -0,0 +1,29 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+namespace corsika {
+
+  inline bool Plane::isAbove(Point const& vP) const {
+    return normal_.dot(vP - center_) > LengthType::zero();
+  }
+
+  inline LengthType Plane::getDistanceTo(Point const& vP) const {
+    return (normal_ * (vP - center_).dot(normal_)).getNorm();
+  }
+
+  inline Point const& Plane::getCenter() const { return center_; }
+
+  inline Plane::DimLessVec const& Plane::getNormal() const { return normal_; }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/geometry/Point.inl b/corsika/detail/framework/geometry/Point.inl
new file mode 100644
index 0000000000000000000000000000000000000000..6c550c1ce716deb8e3004f775a81b9bebb182d75
--- /dev/null
+++ b/corsika/detail/framework/geometry/Point.inl
@@ -0,0 +1,98 @@
+/*
+ * (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 <corsika/framework/geometry/BaseVector.hpp>
+#include <corsika/framework/geometry/QuantityVector.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+namespace corsika {
+
+  QuantityVector<length_d> const& Point::getCoordinates() const {
+    return BaseVector<length_d>::getQuantityVector();
+  }
+
+  QuantityVector<length_d>& Point::getCoordinates() {
+    return BaseVector<length_d>::getQuantityVector();
+  }
+
+  inline LengthType Point::getX(CoordinateSystemPtr const& pCS) const {
+    CoordinateSystemPtr const& cs = BaseVector<length_d>::getCoordinateSystem();
+    if (*pCS == *cs) {
+      return BaseVector<length_d>::getQuantityVector().getX();
+    } else {
+      return QuantityVector<length_d>(
+                 get_transformation(*cs.get(), *pCS.get()) *
+                 BaseVector<length_d>::getQuantityVector().eigenVector_)
+          .getX();
+    }
+  }
+
+  inline LengthType Point::getY(CoordinateSystemPtr const& pCS) const {
+    CoordinateSystemPtr const& cs = BaseVector<length_d>::getCoordinateSystem();
+    if (*pCS == *cs) {
+      return BaseVector<length_d>::getQuantityVector().getY();
+    } else {
+      return QuantityVector<length_d>(
+                 get_transformation(*cs.get(), *pCS.get()) *
+                 BaseVector<length_d>::getQuantityVector().eigenVector_)
+          .getY();
+    }
+  }
+
+  inline LengthType Point::getZ(CoordinateSystemPtr const& pCS) const {
+    CoordinateSystemPtr const& cs = BaseVector<length_d>::getCoordinateSystem();
+    if (*pCS == *cs) {
+      return BaseVector<length_d>::getQuantityVector().getZ();
+    } else {
+      return QuantityVector<length_d>(
+                 get_transformation(*cs.get(), *pCS.get()) *
+                 BaseVector<length_d>::getQuantityVector().eigenVector_)
+          .getZ();
+    }
+  }
+
+  /// this always returns a QuantityVector as triple
+  QuantityVector<length_d> Point::getCoordinates(CoordinateSystemPtr const& pCS) const {
+    CoordinateSystemPtr const& cs = BaseVector<length_d>::getCoordinateSystem();
+    if (*pCS == *cs) {
+      return BaseVector<length_d>::getQuantityVector();
+    } else {
+      return QuantityVector<length_d>(
+          get_transformation(*cs.get(), *pCS.get()) *
+          BaseVector<length_d>::getQuantityVector().eigenVector_);
+    }
+  }
+
+  /// this always returns a QuantityVector as triple
+  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) {
+    BaseVector<length_d>::setQuantityVector(QuantityVector<length_d>(
+        get_transformation(*BaseVector<length_d>::getCoordinateSystem().get(),
+                           *pCS.get()) *
+        BaseVector<length_d>::getQuantityVector().eigenVector_));
+    BaseVector<length_d>::setCoordinateSystem(pCS);
+  }
+
+  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 {
+    CoordinateSystemPtr const& cs = BaseVector<length_d>::getCoordinateSystem();
+    return Vector<length_d>(cs, getCoordinates() - pB.getCoordinates(cs));
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/geometry/QuantityVector.inl b/corsika/detail/framework/geometry/QuantityVector.inl
new file mode 100644
index 0000000000000000000000000000000000000000..151b35daec64d4909ef3492e80ee3c0c57a64429
--- /dev/null
+++ b/corsika/detail/framework/geometry/QuantityVector.inl
@@ -0,0 +1,158 @@
+/*
+ * (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 <Eigen/Dense>
+
+#include <iostream>
+#include <utility>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+namespace corsika {
+
+  template <typename TDimension>
+  inline typename QuantityVector<TDimension>::quantity_type QuantityVector<TDimension>::
+  operator[](size_t const index) const {
+    return quantity_type(phys::units::detail::magnitude_tag, eigenVector_[index]);
+  }
+
+  template <typename TDimension>
+  inline typename QuantityVector<TDimension>::quantity_type
+  QuantityVector<TDimension>::getX() const {
+    return (*this)[0];
+  }
+
+  template <typename TDimension>
+  inline typename QuantityVector<TDimension>::quantity_type
+  QuantityVector<TDimension>::getY() const {
+    return (*this)[1];
+  }
+
+  template <typename TDimension>
+  inline typename QuantityVector<TDimension>::quantity_type
+  QuantityVector<TDimension>::getZ() const {
+    return (*this)[2];
+  }
+
+  template <typename TDimension>
+  inline typename QuantityVector<TDimension>::quantity_type
+  QuantityVector<TDimension>::getNorm() const {
+    return quantity_type(phys::units::detail::magnitude_tag, eigenVector_.norm());
+  }
+
+  template <typename TDimension>
+  inline typename QuantityVector<TDimension>::quantity_square_type
+  QuantityVector<TDimension>::getSquaredNorm() const {
+    using QuantitySquared =
+        decltype(std::declval<quantity_type>() * std::declval<quantity_type>());
+    return QuantitySquared(phys::units::detail::magnitude_tag,
+                           eigenVector_.squaredNorm());
+  }
+
+  template <typename TDimension>
+  inline QuantityVector<TDimension> QuantityVector<TDimension>::operator+(
+      QuantityVector<TDimension> const& pQVec) const {
+    return QuantityVector<TDimension>(eigenVector_ + pQVec.eigenVector_);
+  }
+
+  template <typename TDimension>
+  inline QuantityVector<TDimension> QuantityVector<TDimension>::operator-(
+      QuantityVector<TDimension> const& pQVec) const {
+    return QuantityVector<TDimension>(eigenVector_ - pQVec.eigenVector_);
+  }
+
+  template <typename TDimension>
+  template <typename TScalarDim>
+  inline auto QuantityVector<TDimension>::operator*(
+      phys::units::quantity<TScalarDim, double> const p) const {
+    using ResQuantity =
+        phys::units::detail::Product<TScalarDim, TDimension, double, double>;
+
+    if constexpr (std::is_same<ResQuantity, double>::value) // result dimensionless, not
+                                                            // a "quantity_type" anymore
+    {
+      return QuantityVector<phys::units::dimensionless_d>(eigenVector_ * p.magnitude());
+    } else {
+      return QuantityVector<typename ResQuantity::dimension_type>(eigenVector_ *
+                                                                  p.magnitude());
+    }
+  }
+
+  template <typename TDimension>
+  template <typename TScalarDim>
+  inline auto QuantityVector<TDimension>::operator/(
+      phys::units::quantity<TScalarDim, double> const p) const {
+    return (*this) * (1 / p);
+  }
+
+  template <typename TDimension>
+  inline auto QuantityVector<TDimension>::operator*(double const p) const {
+    return QuantityVector<TDimension>(eigenVector_ * p);
+  }
+
+  template <typename TDimension>
+  inline auto QuantityVector<TDimension>::operator/(double const p) const {
+    return QuantityVector<TDimension>(eigenVector_ / p);
+  }
+
+  template <typename TDimension>
+  inline auto& QuantityVector<TDimension>::operator/=(double const p) {
+    eigenVector_ /= p;
+    return *this;
+  }
+
+  template <typename TDimension>
+  inline auto& QuantityVector<TDimension>::operator*=(double const p) {
+    eigenVector_ *= p;
+    return *this;
+  }
+
+  template <typename TDimension>
+  inline auto& QuantityVector<TDimension>::operator+=(
+      QuantityVector<TDimension> const& pQVec) {
+    eigenVector_ += pQVec.eigenVector_;
+    return *this;
+  }
+
+  template <typename TDimension>
+  inline auto& QuantityVector<TDimension>::operator-=(
+      QuantityVector<TDimension> const& pQVec) {
+    eigenVector_ -= pQVec.eigenVector_;
+    return *this;
+  }
+
+  template <typename TDimension>
+  inline auto& QuantityVector<TDimension>::operator-() const {
+    return QuantityVector<TDimension>(-eigenVector_);
+  }
+
+  template <typename TDimension>
+  inline auto QuantityVector<TDimension>::normalized() const {
+    return QuantityVector<TDimension>(eigenVector_.normalized());
+  }
+
+  template <typename TDimension>
+  inline auto QuantityVector<TDimension>::operator==(
+      QuantityVector<TDimension> const& p) const {
+    return eigenVector_ == p.eigenVector_;
+  }
+
+  template <typename TDimension>
+  inline std::ostream& operator<<(std::ostream& os,
+                                  corsika::QuantityVector<TDimension> const qv) {
+    using quantity_type = phys::units::quantity<TDimension, double>;
+
+    os << '(' << qv.eigenVector_(0) << ' ' << qv.eigenVector_(1) << ' '
+       << qv.eigenVector_(2) << ") "
+       << phys::units::to_unit_symbol<TDimension, double>(
+              quantity_type(phys::units::detail::magnitude_tag, 1));
+    return os;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/geometry/Sphere.inl b/corsika/detail/framework/geometry/Sphere.inl
new file mode 100644
index 0000000000000000000000000000000000000000..be1ba2774919c3083dd083bea6489fce467e6c81
--- /dev/null
+++ b/corsika/detail/framework/geometry/Sphere.inl
@@ -0,0 +1,28 @@
+/*
+ * (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/Point.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+namespace corsika {
+
+  inline bool Sphere::contains(Point const& p) const {
+    return radius_ * radius_ > (center_ - p).getSquaredNorm();
+  }
+
+  inline Point const& Sphere::getCenter() const { return center_; }
+
+  inline void Sphere::setCenter(Point const& p) { center_ = p; }
+
+  inline LengthType Sphere::getRadius() const { return radius_; }
+
+  inline void Sphere::setRadius(LengthType const r) { radius_ = r; }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/geometry/StraightTrajectory.inl b/corsika/detail/framework/geometry/StraightTrajectory.inl
new file mode 100644
index 0000000000000000000000000000000000000000..a8fef9b035a38745095976a17f6a0644b720a2a5
--- /dev/null
+++ b/corsika/detail/framework/geometry/StraightTrajectory.inl
@@ -0,0 +1,70 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+
+namespace corsika {
+
+  inline VelocityVector StraightTrajectory::getVelocity(double const u) const {
+    return initialVelocity_ * (1 - u) + finalVelocity_ * u;
+  }
+
+  inline TimeType StraightTrajectory::getDuration(double const u) const {
+    return u * timeStep_;
+  }
+
+  inline LengthType StraightTrajectory::getLength(double const u) const {
+    if (timeLength_ == 0_s) return 0_m;
+    if (timeStep_ == std::numeric_limits<TimeType::value_type>::infinity() * 1_s)
+      return std::numeric_limits<LengthType::value_type>::infinity() * 1_m;
+    return getDistance(u) * timeStep_ / timeLength_;
+  }
+
+  inline void StraightTrajectory::setLength(LengthType const limit) {
+    setDuration(line_.getTimeFromArclength(limit));
+  }
+
+  inline void StraightTrajectory::setDuration(TimeType const limit) {
+    if (timeStep_ == 0_s) {
+      timeLength_ = 0_s;
+      setFinalVelocity(getVelocity(0));
+      timeStep_ = limit;
+    } else {
+      // for infinite steps there can't be a difference between
+      // curved and straight trajectory, this is fundamentally
+      // undefined: assume they are the same (which, i.e. is always correct for a
+      // straight line trajectory).
+      //
+      // Final note: only straight-line trajectories should have
+      // infinite steps! Everything else is ill-defined.
+      if (timeStep_ == std::numeric_limits<TimeType::value_type>::infinity() * 1_s ||
+          timeLength_ == std::numeric_limits<TimeType::value_type>::infinity() * 1_s) {
+        timeLength_ = limit;
+        timeStep_ = limit;
+        // ...and don't touch velocity
+      } else {
+        const double scale = limit / timeStep_;
+        timeLength_ *= scale;
+        setFinalVelocity(getVelocity(scale));
+        timeStep_ = limit;
+      }
+    }
+  }
+
+  inline LengthType StraightTrajectory::getDistance(double const u) const {
+    assert(u <= 1);
+    assert(u >= 0);
+    return line_.getArcLength(0 * second, u * timeLength_);
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/geometry/Vector.inl b/corsika/detail/framework/geometry/Vector.inl
new file mode 100644
index 0000000000000000000000000000000000000000..cbfd597b3e556fc04c03b0829b4f949bcb459ae8
--- /dev/null
+++ b/corsika/detail/framework/geometry/Vector.inl
@@ -0,0 +1,231 @@
+/*
+ * (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/BaseVector.hpp>
+#include <corsika/framework/geometry/QuantityVector.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+namespace corsika {
+
+  template <typename TDimension>
+  inline QuantityVector<TDimension> const& Vector<TDimension>::getComponents() const {
+    return BaseVector<TDimension>::getQuantityVector();
+  }
+
+  template <typename TDimension>
+  inline QuantityVector<TDimension>& Vector<TDimension>::getComponents() {
+    return BaseVector<TDimension>::getQuantityVector();
+  }
+
+  template <typename TDimension>
+  inline QuantityVector<TDimension> Vector<TDimension>::getComponents(
+      CoordinateSystemPtr const& pCS) const {
+    if (*pCS == *BaseVector<TDimension>::getCoordinateSystem()) {
+      return BaseVector<TDimension>::getQuantityVector();
+    } else {
+      return QuantityVector<TDimension>(
+          get_transformation(*BaseVector<TDimension>::getCoordinateSystem().get(),
+                             *pCS.get())
+              .linear() *
+          BaseVector<TDimension>::getQuantityVector().eigenVector_);
+    }
+  }
+
+  template <typename TDimension>
+  inline QuantityVector<TDimension>& Vector<TDimension>::getComponents(
+      CoordinateSystemPtr const& pCS) {
+    if (*pCS != *BaseVector<TDimension>::getCoordinateSystem()) { rebase(pCS); }
+    return BaseVector<TDimension>::getQuantityVector();
+  }
+
+  template <typename TDimension>
+  inline typename Vector<TDimension>::quantity_type Vector<TDimension>::getX(
+      CoordinateSystemPtr const& pCS) const {
+    if (*pCS == *BaseVector<TDimension>::getCoordinateSystem()) {
+      return BaseVector<TDimension>::getQuantityVector()[0];
+    } else {
+      return QuantityVector<TDimension>(
+          get_transformation(*BaseVector<TDimension>::getCoordinateSystem().get(),
+                             *pCS.get())
+              .linear() *
+          BaseVector<TDimension>::getQuantityVector().eigenVector_)[0];
+    }
+  }
+
+  template <typename TDimension>
+  inline typename Vector<TDimension>::quantity_type Vector<TDimension>::getY(
+      CoordinateSystemPtr const& pCS) const {
+    if (*pCS == *BaseVector<TDimension>::getCoordinateSystem()) {
+      return BaseVector<TDimension>::getQuantityVector()[1];
+    } else {
+      return QuantityVector<TDimension>(
+          get_transformation(*BaseVector<TDimension>::getCoordinateSystem().get(),
+                             *pCS.get())
+              .linear() *
+          BaseVector<TDimension>::getQuantityVector().eigenVector_)[1];
+    }
+  }
+
+  template <typename TDimension>
+  inline typename Vector<TDimension>::quantity_type Vector<TDimension>::getZ(
+      CoordinateSystemPtr const& pCS) const {
+    if (*pCS == *BaseVector<TDimension>::getCoordinateSystem()) {
+      return BaseVector<TDimension>::getQuantityVector()[2];
+    } else {
+      return QuantityVector<TDimension>(
+          get_transformation(*BaseVector<TDimension>::getCoordinateSystem().get(),
+                             *pCS.get())
+              .linear() *
+          BaseVector<TDimension>::getQuantityVector().eigenVector_)[2];
+    }
+  }
+
+  template <typename TDimension>
+  void Vector<TDimension>::rebase(CoordinateSystemPtr const& pCS) {
+    BaseVector<TDimension>::setQuantityVector(QuantityVector<TDimension>(
+        get_transformation(*BaseVector<TDimension>::getCoordinateSystem().get(),
+                           *pCS.get())
+            .linear() *
+        BaseVector<TDimension>::getQuantityVector().eigenVector_));
+    BaseVector<TDimension>::setCoordinateSystem(pCS);
+  }
+
+  template <typename TDimension>
+  inline typename Vector<TDimension>::quantity_type Vector<TDimension>::getNorm() const {
+    return BaseVector<TDimension>::getQuantityVector().getNorm();
+  }
+
+  template <typename TDimension>
+  inline typename Vector<TDimension>::quantity_square_type
+  Vector<TDimension>::getSquaredNorm() const {
+    return BaseVector<TDimension>::getQuantityVector().getSquaredNorm();
+  }
+
+  template <typename TDimension>
+  template <typename TDimension2>
+  auto Vector<TDimension>::getParallelProjectionOnto(
+      Vector<TDimension2> const& pVec, CoordinateSystemPtr const& pCS) const {
+    auto const ourCompVec = getComponents(pCS);
+    auto const otherCompVec = pVec.getComponents(pCS);
+    auto const& a = ourCompVec.eigenVector_;
+    auto const& b = otherCompVec.eigenVector_;
+
+    return Vector<TDimension>(
+        pCS, QuantityVector<TDimension>(b * ((a.dot(b)) / b.squaredNorm())));
+  }
+
+  template <typename TDimension>
+  template <typename TDimension2>
+  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 {
+    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 {
+    CoordinateSystemPtr const& cs = BaseVector<TDimension>::getCoordinateSystem();
+    return Vector<TDimension>(cs, getComponents() - pVec.getComponents(cs));
+  }
+
+  template <typename TDimension>
+  auto& Vector<TDimension>::operator*=(double const p) {
+    BaseVector<TDimension>::getQuantityVector() *= p;
+    return *this;
+  }
+
+  template <typename TDimension>
+  template <typename TScalarDim>
+  auto Vector<TDimension>::operator*(
+      phys::units::quantity<TScalarDim, double> const p) const {
+    using ProdDim = phys::units::detail::product_d<TDimension, TScalarDim>;
+
+    return Vector<ProdDim>(BaseVector<TDimension>::getCoordinateSystem(),
+                           BaseVector<TDimension>::getQuantityVector() * p);
+  }
+
+  template <typename TDimension>
+  template <typename TScalarDim>
+  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 {
+    return Vector<TDimension>(BaseVector<TDimension>::getCoordinateSystem(),
+                              BaseVector<TDimension>::getQuantityVector() * p);
+  }
+
+  template <typename TDimension>
+  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) {
+    BaseVector<TDimension>::getQuantityVector() +=
+        pVec.getComponents(BaseVector<TDimension>::getCoordinateSystem());
+    return *this;
+  }
+
+  template <typename TDimension>
+  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 {
+    return Vector<TDimension>(BaseVector<TDimension>::getCoordinateSystem(),
+                              -BaseVector<TDimension>::getQuantityVector());
+  }
+
+  template <typename TDimension>
+  auto Vector<TDimension>::normalized() const {
+    return (*this) * (1 / getNorm());
+  }
+
+  template <typename TDimension>
+  template <typename TDimension2>
+  auto Vector<TDimension>::cross(Vector<TDimension2> const& pV) const {
+    auto const c1 = getComponents().eigenVector_;
+    auto const c2 =
+        pV.getComponents(BaseVector<TDimension>::getCoordinateSystem()).eigenVector_;
+    auto const bareResult = c1.cross(c2);
+
+    using ProdDim = phys::units::detail::product_d<TDimension, TDimension2>;
+    return Vector<ProdDim>(BaseVector<TDimension>::getCoordinateSystem(), bareResult);
+  }
+
+  template <typename TDimension>
+  template <typename TDimension2>
+  auto Vector<TDimension>::dot(Vector<TDimension2> const& pV) const {
+    auto const c1 = getComponents().eigenVector_;
+    auto const c2 =
+        pV.getComponents(BaseVector<TDimension>::getCoordinateSystem()).eigenVector_;
+    auto const bareResult = c1.dot(c2);
+
+    using ProdDim = phys::units::detail::product_d<TDimension, TDimension2>;
+
+    return phys::units::quantity<ProdDim, double>(phys::units::detail::magnitude_tag,
+                                                  bareResult);
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/process/InteractionCounter.inl b/corsika/detail/framework/process/InteractionCounter.inl
new file mode 100644
index 0000000000000000000000000000000000000000..34c93ce43120bdd25f1c5824e6b0a561e3a7f06e
--- /dev/null
+++ b/corsika/detail/framework/process/InteractionCounter.inl
@@ -0,0 +1,52 @@
+/*
+ * (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 <corsika/framework/process/InteractionHistogram.hpp>
+
+namespace corsika {
+
+  template <class TCountedProcess>
+  InteractionCounter<TCountedProcess>::InteractionCounter(TCountedProcess& process)
+      : process_(process) {}
+
+  template <class TCountedProcess>
+  template <typename TSecondaryView>
+  inline void InteractionCounter<TCountedProcess>::doInteraction(TSecondaryView& view) {
+    auto const projectile = view.getProjectile();
+    auto const massNumber = projectile.getNode()
+                                ->getModelProperties()
+                                .getNuclearComposition()
+                                .getAverageMassNumber();
+    auto const massTarget = massNumber * constants::nucleonMass;
+
+    if (auto const projectile_id = projectile.getPID(); projectile_id == Code::Nucleus) {
+      auto const A = projectile.getNuclearA();
+      auto const Z = projectile.getNuclearZ();
+      histogram_.fill(projectile_id, projectile.getEnergy(), massTarget, A, Z);
+    } else {
+      histogram_.fill(projectile_id, projectile.getEnergy(), massTarget);
+    }
+    process_.doInteraction(view);
+  }
+
+  template <class TCountedProcess>
+  template <typename TParticle>
+  inline GrammageType InteractionCounter<TCountedProcess>::getInteractionLength(
+      TParticle const& particle) const {
+    return process_.getInteractionLength(particle);
+  }
+
+  template <class TCountedProcess>
+  inline InteractionHistogram const& InteractionCounter<TCountedProcess>::getHistogram()
+      const {
+    return histogram_;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/process/InteractionHistogram.hpp b/corsika/detail/framework/process/InteractionHistogram.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..eff1f264c8cf7278562aa5b4cb595bbcb6e160e1
--- /dev/null
+++ b/corsika/detail/framework/process/InteractionHistogram.hpp
@@ -0,0 +1,29 @@
+/*
+ * (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 <boost/histogram.hpp>
+
+namespace corsika {
+
+  namespace detail {
+    inline auto hist_factory(unsigned int const bin_number, double const e_low,
+                             double const e_high) {
+      namespace bh = boost::histogram;
+      namespace bha = bh::axis;
+
+      auto h = bh::make_histogram(
+          bha::category<int, bh::use_default, bha::option::growth_t>{{2212, 2112},
+                                                                     "projectile PDG"},
+          bha::regular<float, bha::transform::log>{bin_number, (float)e_low,
+                                                   (float)e_high, "energy/eV"});
+      return h;
+    }
+  } // namespace detail
+} // namespace corsika
diff --git a/corsika/detail/framework/process/InteractionHistogram.inl b/corsika/detail/framework/process/InteractionHistogram.inl
new file mode 100644
index 0000000000000000000000000000000000000000..ecb582368df13fc4efdb93347878148773b578f6
--- /dev/null
+++ b/corsika/detail/framework/process/InteractionHistogram.inl
@@ -0,0 +1,67 @@
+/*
+ * (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 <fstream>
+#include <string>
+
+#include <corsika/framework/process/InteractionHistogram.hpp>
+#include <corsika/detail/framework/process/InteractionHistogram.hpp> // for detail namespace
+
+namespace corsika {
+
+  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) {
+    auto constexpr inv_eV = 1 / 1_eV;
+    if (projectile_id == Code::Nucleus) {
+      auto const sqrtS = sqrt(A * A * (constants::nucleonMass * constants::nucleonMass) +
+                              mass_target * mass_target + 2 * lab_energy * mass_target);
+
+      int32_t const pdg = 1'000'000'000l + Z * 10'000l + A * 10l;
+
+      inthist_lab_(pdg, lab_energy * inv_eV);
+      inthist_cms_(pdg, sqrtS * inv_eV);
+    } else {
+      auto const projectile_mass = get_mass(projectile_id);
+      auto const sqrtS = sqrt(projectile_mass * projectile_mass +
+                              mass_target * mass_target + 2 * lab_energy * mass_target);
+
+      inthist_cms_(static_cast<int>(get_PDG(projectile_id)), sqrtS * inv_eV);
+      inthist_lab_(static_cast<int>(get_PDG(projectile_id)), lab_energy * inv_eV);
+    }
+  }
+
+  void InteractionHistogram::saveLab(std::string const& filename, SaveMode mode) const {
+    corsika::save_hist(inthist_lab_, filename, mode);
+  }
+
+  void InteractionHistogram::saveCMS(std::string const& filename, SaveMode mode) const {
+    corsika::save_hist(inthist_cms_, filename, mode);
+  }
+
+  InteractionHistogram& InteractionHistogram::operator+=(
+      InteractionHistogram const& other) {
+    inthist_lab_ += other.inthist_lab_;
+    inthist_cms_ += other.inthist_cms_;
+
+    return *this;
+  }
+
+  InteractionHistogram InteractionHistogram::operator+(InteractionHistogram other) const {
+    other.inthist_lab_ += inthist_lab_;
+    other.inthist_cms_ += inthist_cms_;
+
+    return other;
+  }
+} // namespace corsika
\ No newline at end of file
diff --git a/corsika/detail/framework/process/ProcessSequence.inl b/corsika/detail/framework/process/ProcessSequence.inl
new file mode 100644
index 0000000000000000000000000000000000000000..7759270ee0b0cfe0fc416d2c5923b3b9dc1498e3
--- /dev/null
+++ b/corsika/detail/framework/process/ProcessSequence.inl
@@ -0,0 +1,264 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/process/BaseProcess.hpp>
+#include <corsika/framework/process/BoundaryCrossingProcess.hpp>
+#include <corsika/framework/process/ContinuousProcess.hpp>
+#include <corsika/framework/process/DecayProcess.hpp>
+#include <corsika/framework/process/InteractionProcess.hpp>
+#include <corsika/framework/process/ProcessReturn.hpp>
+#include <corsika/framework/process/SecondariesProcess.hpp>
+#include <corsika/framework/process/StackProcess.hpp>
+
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+namespace corsika {
+
+  template <typename TProcess1, typename TProcess2>
+  template <typename TParticle>
+  ProcessReturn ProcessSequence<TProcess1, TProcess2>::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>,
+                                    process1_type> ||
+                  t1ProcSeq) {
+      ret |= A_.doBoundaryCrossing(particle, from, to);
+    }
+
+    if constexpr (std::is_base_of_v<BoundaryCrossingProcess<process2_type>,
+                                    process2_type> ||
+                  t2ProcSeq) {
+      ret |= B_.doBoundaryCrossing(particle, from, to);
+    }
+
+    return ret;
+  }
+
+  template <typename TProcess1, typename TProcess2>
+  template <typename TParticle, typename TTrack>
+  ProcessReturn ProcessSequence<TProcess1, TProcess2>::doContinuous(TParticle& particle,
+                                                                    TTrack& vT) {
+    ProcessReturn ret = ProcessReturn::Ok;
+    if constexpr (std::is_base_of_v<ContinuousProcess<process1_type>, process1_type> ||
+                  t1ProcSeq) {
+      ret |= A_.doContinuous(particle, vT);
+    }
+    if constexpr (std::is_base_of_v<ContinuousProcess<process2_type>, process2_type> ||
+                  t2ProcSeq) {
+      if (!isAbsorbed(ret)) { ret |= B_.doContinuous(particle, vT); }
+    }
+    return ret;
+  }
+
+  template <typename TProcess1, typename TProcess2>
+  template <typename TSecondaries>
+  void ProcessSequence<TProcess1, TProcess2>::doSecondaries(TSecondaries& vS) {
+    if constexpr (std::is_base_of_v<SecondariesProcess<process1_type>, process1_type> ||
+                  t1ProcSeq) {
+      A_.doSecondaries(vS);
+    }
+    if constexpr (std::is_base_of_v<SecondariesProcess<process2_type>, process2_type> ||
+                  t2ProcSeq) {
+      B_.doSecondaries(vS);
+    }
+  }
+
+  template <typename TProcess1, typename TProcess2>
+  bool ProcessSequence<TProcess1, TProcess2>::checkStep() {
+    bool ret = false;
+    if constexpr (std::is_base_of_v<StackProcess<process1_type>, process1_type> ||
+                  (t1ProcSeq && !t1SwitchProcSeq)) {
+      ret |= A_.checkStep();
+    }
+    if constexpr (std::is_base_of_v<StackProcess<process2_type>, process2_type> ||
+                  (t2ProcSeq && !t2SwitchProcSeq)) {
+      ret |= B_.checkStep();
+    }
+    return ret;
+  }
+
+  template <typename TProcess1, typename TProcess2>
+  template <typename TStack>
+  void ProcessSequence<TProcess1, TProcess2>::doStack(TStack& stack) {
+    if constexpr (std::is_base_of_v<StackProcess<process1_type>, process1_type> ||
+                  (t1ProcSeq && !t1SwitchProcSeq)) {
+      if (A_.checkStep()) { A_.doStack(stack); }
+    }
+    if constexpr (std::is_base_of_v<StackProcess<process2_type>, process2_type> ||
+                  (t2ProcSeq && !t2SwitchProcSeq)) {
+      if (B_.checkStep()) { B_.doStack(stack); }
+    }
+  }
+
+  template <typename TProcess1, typename TProcess2>
+  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;
+
+    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 (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 max_length;
+  }
+
+  template <typename TProcess1, typename TProcess2>
+  template <typename TParticle>
+  InverseGrammageType ProcessSequence<TProcess1, TProcess2>::getInverseInteractionLength(
+      TParticle&& particle) {
+
+    InverseGrammageType tot = 0 * meter * meter / gram; // default value
+
+    if constexpr (std::is_base_of_v<InteractionProcess<process1_type>, process1_type> ||
+                  t1ProcSeq) {
+      tot += A_.getInverseInteractionLength(particle);
+    }
+    if constexpr (std::is_base_of_v<InteractionProcess<process2_type>, process2_type> ||
+                  t2ProcSeq) {
+      tot += B_.getInverseInteractionLength(particle);
+    }
+    return tot;
+  }
+
+  template <typename TProcess1, typename TProcess2>
+  template <typename TSecondaryView>
+  inline ProcessReturn ProcessSequence<TProcess1, TProcess2>::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
+
+    if constexpr (t1ProcSeq) {
+      // if A is a process sequence --> check inside
+      ProcessReturn const ret =
+          A_.selectInteraction(view, lambda_inv_select, lambda_inv_sum);
+      // if A_ did succeed, stop routine. Not checking other static branch B_.
+      if (ret != ProcessReturn::Ok) { return ret; }
+    } else if constexpr (std::is_base_of_v<InteractionProcess<process1_type>,
+                                           process1_type>) {
+      // if this is not a ContinuousProcess --> evaluate probability
+      lambda_inv_sum += A_.getInverseInteractionLength(view.parent());
+      // check if we should execute THIS process and then EXIT
+      if (lambda_inv_select <= lambda_inv_sum) {
+        A_.doInteraction(view);
+        return ProcessReturn::Interacted;
+      }
+    } // end branch A
+
+    if constexpr (t2ProcSeq) {
+      // if B_ is a process sequence --> check inside
+      return B_.selectInteraction(view, lambda_inv_select, lambda_inv_sum);
+    } else if constexpr (std::is_base_of_v<InteractionProcess<process2_type>,
+                                           process2_type>) {
+      // if this is not a ContinuousProcess --> evaluate probability
+      lambda_inv_sum += B_.getInverseInteractionLength(view.parent());
+      // soon as SecondaryView::parent() is migrated!
+      // check if we should execute THIS process and then EXIT
+      if (lambda_inv_select <= lambda_inv_sum) {
+        B_.doInteraction(view);
+        return ProcessReturn::Interacted;
+      }
+    } // end branch B_
+    return ProcessReturn::Ok;
+  }
+
+  template <typename TProcess1, typename TProcess2>
+  template <typename TParticle>
+  inline InverseTimeType ProcessSequence<TProcess1, TProcess2>::getInverseLifetime(
+      TParticle&& particle) {
+
+    InverseTimeType tot = 0 / second; // default value
+
+    if constexpr (std::is_base_of_v<DecayProcess<process1_type>, process1_type> ||
+                  t1ProcSeq) {
+      tot += A_.getInverseLifetime(particle);
+    }
+    if constexpr (std::is_base_of_v<DecayProcess<process2_type>, process2_type> ||
+                  t2ProcSeq) {
+      tot += B_.getInverseLifetime(particle);
+    }
+    return tot;
+  }
+
+  template <typename TProcess1, typename TProcess2>
+  // 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) {
+
+    // TODO: add check for decay_inv_select>decay_inv_tot
+
+    if constexpr (t1ProcSeq) {
+      // if A_ is a process sequence --> check inside
+      ProcessReturn const ret = A_.selectDecay(view, decay_inv_select, decay_inv_sum);
+      // if A_ did succeed, stop routine here (not checking other static branch B_)
+      if (ret != ProcessReturn::Ok) { return ret; }
+    } else if constexpr (std::is_base_of_v<DecayProcess<process1_type>, process1_type>) {
+      // if this is not a ContinuousProcess --> evaluate probability
+      decay_inv_sum += A_.getInverseLifetime(view.parent());
+      // check if we should execute THIS process and then EXIT
+      if (decay_inv_select <= decay_inv_sum) { // more pedagogical: rndm_select <
+                                               // decay_inv_sum / decay_inv_tot
+        A_.doDecay(view);
+        return ProcessReturn::Decayed;
+      }
+    } // end branch A_
+
+    if constexpr (t2ProcSeq) {
+      // if B_ is a process sequence --> check inside
+      return B_.selectDecay(view, decay_inv_select, decay_inv_sum);
+    } else if constexpr (std::is_base_of_v<DecayProcess<process2_type>, process2_type>) {
+      // if this is not a ContinuousProcess --> evaluate probability
+      decay_inv_sum += B_.getInverseLifetime(view.parent());
+      // check if we should execute THIS process and then EXIT
+      if (decay_inv_select <= decay_inv_sum) {
+        B_.doDecay(view);
+        return ProcessReturn::Decayed;
+      }
+    } // end branch B_
+    return ProcessReturn::Ok;
+  }
+
+  /**
+   * traits marker to identify objects containing any StackProcesses
+   **/
+  namespace detail {
+    // need helper alias to achieve this:
+    template <typename TProcess1, typename TProcess2,
+              typename = typename std::enable_if_t<
+                  contains_stack_process_v<TProcess1> ||
+                      std::is_base_of_v<StackProcess<typename std::decay_t<TProcess1>>,
+                                        typename std::decay_t<TProcess1>> ||
+                      contains_stack_process_v<TProcess2> ||
+                      std::is_base_of_v<StackProcess<typename std::decay_t<TProcess2>>,
+                                        typename std::decay_t<TProcess2>>,
+                  int>>
+    using enable_if_stack = ProcessSequence<TProcess1, TProcess2>;
+  } // namespace detail
+
+  template <typename TProcess1, typename TProcess2>
+  struct contains_stack_process<detail::enable_if_stack<TProcess1, TProcess2>>
+      : std::true_type {};
+
+} // namespace corsika
diff --git a/corsika/detail/framework/process/SwitchProcessSequence.inl b/corsika/detail/framework/process/SwitchProcessSequence.inl
new file mode 100644
index 0000000000000000000000000000000000000000..62d33995afefe53aa3865df31969413d18a63a99
--- /dev/null
+++ b/corsika/detail/framework/process/SwitchProcessSequence.inl
@@ -0,0 +1,290 @@
+/*
+ * (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/process/BaseProcess.hpp>
+#include <corsika/framework/process/ProcessTraits.hpp>
+#include <corsika/framework/process/BoundaryCrossingProcess.hpp>
+#include <corsika/framework/process/ContinuousProcess.hpp>
+#include <corsika/framework/process/DecayProcess.hpp>
+#include <corsika/framework/process/InteractionProcess.hpp>
+#include <corsika/framework/process/ProcessReturn.hpp>
+#include <corsika/framework/process/SecondariesProcess.hpp>
+#include <corsika/framework/process/StackProcess.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+namespace corsika {
+
+  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TParticle, typename TVTNType>
+  ProcessReturn SwitchProcessSequence<TProcess1, TProcess2, TSelect>::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>,
+                                        process1_type> ||
+                      t1ProcSeq) {
+          return A_.doBoundaryCrossing(particle, from, to);
+        }
+        break;
+      }
+      case SwitchResult::Second: {
+        if constexpr (std::is_base_of_v<BoundaryCrossingProcess<process2_type>,
+                                        process2_type> ||
+                      t2ProcSeq) {
+          return B_.doBoundaryCrossing(particle, from, to);
+        }
+        break;
+      }
+    }
+    return ProcessReturn::Ok;
+  }
+
+  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TParticle, typename TTrack>
+  inline ProcessReturn SwitchProcessSequence<TProcess1, TProcess2, TSelect>::doContinuous(
+      TParticle& particle, TTrack& vT) {
+    switch (select_(particle)) {
+      case SwitchResult::First: {
+        if constexpr (std::is_base_of_v<ContinuousProcess<process1_type>,
+                                        process1_type> ||
+                      t1ProcSeq) {
+          return A_.doContinuous(particle, vT);
+        }
+        break;
+      }
+      case SwitchResult::Second: {
+        if constexpr (std::is_base_of_v<ContinuousProcess<process2_type>,
+                                        process2_type> ||
+                      t2ProcSeq) {
+          return B_.doContinuous(particle, vT);
+        }
+        break;
+      }
+    }
+    return ProcessReturn::Ok;
+  }
+
+  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TSecondaries>
+  inline void SwitchProcessSequence<TProcess1, TProcess2, TSelect>::doSecondaries(
+      TSecondaries& vS) {
+    const auto& particle = vS.parent();
+    switch (select_(particle)) {
+      case SwitchResult::First: {
+        if constexpr (std::is_base_of_v<SecondariesProcess<process1_type>,
+                                        process1_type> ||
+                      t1ProcSeq) {
+          A_.doSecondaries(vS);
+        }
+        break;
+      }
+      case SwitchResult::Second: {
+        if constexpr (std::is_base_of_v<SecondariesProcess<process2_type>,
+                                        process2_type> ||
+                      t2ProcSeq) {
+          B_.doSecondaries(vS);
+        }
+        break;
+      }
+    }
+  }
+
+  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TParticle, typename TTrack>
+  inline LengthType SwitchProcessSequence<TProcess1, TProcess2,
+                                          TSelect>::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);
+        }
+        break;
+      }
+      case SwitchResult::Second: {
+        if constexpr (std::is_base_of_v<ContinuousProcess<process2_type>,
+                                        process2_type> ||
+                      t2ProcSeq) {
+          return B_.getMaxStepLength(particle, vTrack);
+        }
+        break;
+      }
+    }
+
+    // if no other process in the sequence implements it
+    return std::numeric_limits<double>::infinity() * meter;
+  }
+
+  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TParticle>
+  inline InverseGrammageType
+  SwitchProcessSequence<TProcess1, TProcess2, TSelect>::getInverseInteractionLength(
+      TParticle&& particle) {
+
+    switch (select_(particle)) {
+      case SwitchResult::First: {
+        if constexpr (std::is_base_of_v<InteractionProcess<process1_type>,
+                                        process1_type> ||
+                      t1ProcSeq) {
+          return A_.getInverseInteractionLength(particle);
+        }
+        break;
+      }
+      case SwitchResult::Second: {
+        if constexpr (std::is_base_of_v<InteractionProcess<process2_type>,
+                                        process2_type> ||
+                      t2ProcSeq) {
+          return B_.getInverseInteractionLength(particle);
+        }
+        break;
+      }
+    }
+    return 0 * meter * meter / gram; // default value
+  }
+
+  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TSecondaryView>
+  inline ProcessReturn
+  SwitchProcessSequence<TProcess1, TProcess2, TSelect>::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) {
+          // if A_ is a process sequence --> check inside
+          ProcessReturn const ret =
+              A_.selectInteraction(view, lambda_inv_select, lambda_inv_sum);
+          // if A_ did succeed, stop routine. Not checking other static branch B_.
+          if (ret != ProcessReturn::Ok) { return ret; }
+        } else if constexpr (std::is_base_of_v<InteractionProcess<process1_type>,
+                                               process1_type>) {
+          // if this is not a ContinuousProcess --> evaluate probability
+          lambda_inv_sum += A_.getInverseInteractionLength(view.parent());
+          // check if we should execute THIS process and then EXIT
+          if (lambda_inv_select < lambda_inv_sum) {
+            A_.doInteraction(view);
+            return ProcessReturn::Interacted;
+          }
+        } // end branch A_
+        break;
+      }
+
+      case SwitchResult::Second: {
+
+        if constexpr (t2ProcSeq) {
+          // if B_ is a process sequence --> check inside
+          return B_.selectInteraction(view, lambda_inv_select, lambda_inv_sum);
+        } else if constexpr (std::is_base_of_v<InteractionProcess<process2_type>,
+                                               process2_type>) {
+          // if this is not a ContinuousProcess --> evaluate probability
+          lambda_inv_sum += B_.getInverseInteractionLength(view.parent());
+          // check if we should execute THIS process and then EXIT
+          if (lambda_inv_select < lambda_inv_sum) {
+            B_.doInteraction(view);
+            return ProcessReturn::Interacted;
+          }
+        } // end branch B_
+        break;
+      }
+    }
+    return ProcessReturn::Ok;
+  }
+
+  template <typename TProcess1, typename TProcess2, typename TSelect>
+  template <typename TParticle>
+  inline InverseTimeType
+  SwitchProcessSequence<TProcess1, TProcess2, TSelect>::getInverseLifetime(
+      TParticle&& particle) {
+
+    switch (select_(particle)) {
+      case SwitchResult::First: {
+        if constexpr (std::is_base_of_v<DecayProcess<process1_type>, process1_type> ||
+                      t1ProcSeq) {
+          return A_.getInverseLifetime(particle);
+        }
+        break;
+      }
+
+      case SwitchResult::Second: {
+        if constexpr (std::is_base_of_v<DecayProcess<process2_type>, process2_type> ||
+                      t2ProcSeq) {
+          return B_.getInverseLifetime(particle);
+        }
+        break;
+      }
+    }
+    return 0 / second; // default value
+  }
+
+  template <typename TProcess1, typename TProcess2, typename TSelect>
+  // 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) {
+    switch (select_(view.parent())) {
+      case SwitchResult::First: {
+        if constexpr (t1ProcSeq) {
+          // if A_ is a process sequence --> check inside
+          ProcessReturn const ret = A_.selectDecay(view, decay_inv_select, decay_inv_sum);
+          // if A_ did succeed, stop routine here (not checking other static branch B_)
+          if (ret != ProcessReturn::Ok) { return ret; }
+        } else if constexpr (std::is_base_of_v<DecayProcess<process1_type>,
+                                               process1_type>) {
+          // if this is not a ContinuousProcess --> evaluate probability
+          decay_inv_sum += A_.getInverseLifetime(view.parent());
+          // check if we should execute THIS process and then EXIT
+          if (decay_inv_select < decay_inv_sum) {
+            // more pedagogical: rndm_select < decay_inv_sum / decay_inv_tot
+            A_.doDecay(view);
+            return ProcessReturn::Decayed;
+          }
+        } // end branch A_
+        break;
+      }
+
+      case SwitchResult::Second: {
+
+        if constexpr (t2ProcSeq) {
+          // if B_ is a process sequence --> check inside
+          return B_.selectDecay(view, decay_inv_select, decay_inv_sum);
+        } else if constexpr (std::is_base_of_v<DecayProcess<process2_type>,
+                                               process2_type>) {
+          // if this is not a ContinuousProcess --> evaluate probability
+          decay_inv_sum += B_.getInverseLifetime(view.parent());
+          // check if we should execute THIS process and then EXIT
+          if (decay_inv_select < decay_inv_sum) {
+            B_.doDecay(view);
+            return ProcessReturn::Decayed;
+          }
+        } // end branch B_
+        break;
+      }
+    }
+    return ProcessReturn::Ok;
+  }
+
+  /// traits marker to identify objectas ProcessSequence
+  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>>
+      : std::true_type {};
+
+} // namespace corsika
diff --git a/corsika/detail/framework/random/RNGManager.inl b/corsika/detail/framework/random/RNGManager.inl
new file mode 100644
index 0000000000000000000000000000000000000000..575ab43b73e48efe733e2bde64a99503bce44199
--- /dev/null
+++ b/corsika/detail/framework/random/RNGManager.inl
@@ -0,0 +1,61 @@
+/*
+ * (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
+
+namespace corsika {
+
+  inline void RNGManager::registerRandomStream(string_type const& pStreamName) {
+    prng_type rng;
+
+    if (auto const& it = seeds_.find(pStreamName); it != seeds_.end()) {
+      rng.seed(it->second);
+    }
+
+    rngs_[pStreamName] = std::move(rng);
+  }
+
+  inline RNGManager::prng_type& RNGManager::getRandomStream(
+      string_type const& pStreamName) {
+    if (isRegistered(pStreamName)) {
+      return rngs_.at(pStreamName);
+    } else { // this stream name is not in the map
+      throw std::runtime_error("'" + pStreamName + "' is not a registered stream.");
+    }
+  }
+
+  inline bool RNGManager::isRegistered(string_type const& pStreamName) const {
+    return rngs_.count(pStreamName) > 0;
+  }
+
+  inline std::stringstream RNGManager::dumpState() const {
+    std::stringstream buffer;
+    for (auto const& [streamName, rng] : rngs_) {
+      buffer << '"' << streamName << "\" = \"" << rng << '"' << std::endl;
+    }
+
+    return buffer;
+  }
+
+  inline void RNGManager::seedAll(seed_type vSeed) {
+    for (auto& entry : rngs_) { entry.second.seed(vSeed++); }
+  }
+
+  inline void RNGManager::seedAll(void) {
+    std::random_device rd;
+    std::seed_seq sseq{rd(), rd(), rd(), rd(), rd(), rd()};
+    for (auto& entry : rngs_) {
+      std::vector<std::uint32_t> seeds(1);
+      sseq.generate(seeds.begin(), seeds.end());
+      std::uint32_t seed = seeds[0];
+      CORSIKA_LOG_TRACE("Random seed stream {} seed {}", entry.first, seed);
+      entry.second.seed(seed);
+    }
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/stack/CombinedStack.inl b/corsika/detail/framework/stack/CombinedStack.inl
new file mode 100644
index 0000000000000000000000000000000000000000..0c95ad15e8a8c57eabf1615e07b60c4bb54f3393
--- /dev/null
+++ b/corsika/detail/framework/stack/CombinedStack.inl
@@ -0,0 +1,111 @@
+/*
+ * (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 <corsika/framework/core/Logging.hpp>
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+namespace corsika {
+
+  template <template <typename> class TParticleInterfaceA,
+            template <typename> class TParticleInterfaceB, typename TStackIterator>
+  template <typename... TArgs1>
+  inline void CombinedParticleInterface<
+      TParticleInterfaceA, TParticleInterfaceB,
+      TStackIterator>::setParticleData(std::tuple<TArgs1...> const vA) {
+    pi_a_type::setParticleData(vA);
+    pi_b_type::setParticleData();
+  }
+
+  template <template <typename> class TParticleInterfaceA,
+            template <typename> class TParticleInterfaceB, typename TStackIterator>
+  template <typename... TArgs1, typename... TArgs2>
+  inline void CombinedParticleInterface<
+      TParticleInterfaceA, TParticleInterfaceB,
+      TStackIterator>::setParticleData(std::tuple<TArgs1...> const vA,
+                                       std::tuple<TArgs2...> const vB) {
+    pi_a_type::setParticleData(vA);
+    pi_b_type::setParticleData(vB);
+  }
+
+  template <template <typename> class TParticleInterfaceA,
+            template <typename> class TParticleInterfaceB, typename TStackIterator>
+  template <typename... TArgs1>
+  inline void CombinedParticleInterface<
+      TParticleInterfaceA, TParticleInterfaceB,
+      TStackIterator>::setParticleData(pi_a_type& p, std::tuple<TArgs1...> const vA) {
+    // static_assert(MT<I>::has_not, "error");
+    pi_a_type::setParticleData(static_cast<pi_a_type&>(p), vA); // original stack
+    pi_b_type::setParticleData(static_cast<pi_b_type&>(p));     // addon stack
+  }
+
+  template <template <typename> class TParticleInterfaceA,
+            template <typename> class TParticleInterfaceB, typename TStackIterator>
+  template <typename... TArgs1, typename... TArgs2>
+  inline void CombinedParticleInterface<
+      TParticleInterfaceA, TParticleInterfaceB,
+      TStackIterator>::setParticleData(pi_c_type& p, std::tuple<TArgs1...> const vA,
+                                       std::tuple<TArgs2...> const vB) {
+
+    pi_a_type::setParticleData(static_cast<pi_a_type&>(p), vA);
+    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,
+                                               TStackIterator>::asString() const {
+    return fmt::format("[[{}][{}]]", pi_a_type::asString(), pi_b_type::asString());
+  }
+
+  template <typename Stack1Impl, typename Stack2Impl>
+  inline void CombinedStackImpl<Stack1Impl, Stack2Impl>::clear() {
+    Stack1Impl::clear();
+    Stack2Impl::clear();
+  }
+  template <typename Stack1Impl, typename Stack2Impl>
+  inline void CombinedStackImpl<Stack1Impl, Stack2Impl>::copy(const unsigned int i1,
+                                                              const unsigned int i2) {
+    if (i1 >= getSize() || i2 >= getSize()) {
+      std::ostringstream err;
+      err << "CombinedStack: trying to access data beyond size of stack!";
+      throw std::runtime_error(err.str());
+    }
+    Stack1Impl::copy(i1, i2);
+    Stack2Impl::copy(i1, i2);
+  }
+
+  template <typename Stack1Impl, typename Stack2Impl>
+  inline void CombinedStackImpl<Stack1Impl, Stack2Impl>::swap(const unsigned int i1,
+                                                              const unsigned int i2) {
+    if (i1 >= getSize() || i2 >= getSize()) {
+      std::ostringstream err;
+      err << "CombinedStack: trying to access data beyond size of stack!";
+      throw std::runtime_error(err.str());
+    }
+    Stack1Impl::swap(i1, i2);
+    Stack2Impl::swap(i1, i2);
+  }
+
+  template <typename Stack1Impl, typename Stack2Impl>
+  inline void CombinedStackImpl<Stack1Impl, Stack2Impl>::incrementSize() {
+    Stack1Impl::incrementSize();
+    Stack2Impl::incrementSize();
+  }
+
+  template <typename Stack1Impl, typename Stack2Impl>
+  inline void CombinedStackImpl<Stack1Impl, Stack2Impl>::decrementSize() {
+    Stack1Impl::decrementSize();
+    Stack2Impl::decrementSize();
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/stack/SecondaryView.inl b/corsika/detail/framework/stack/SecondaryView.inl
new file mode 100644
index 0000000000000000000000000000000000000000..33160aa08b9b56f09d0929a3abc75a156c818309
--- /dev/null
+++ b/corsika/detail/framework/stack/SecondaryView.inl
@@ -0,0 +1,229 @@
+/*
+ * (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 <corsika/framework/stack/Stack.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <stdexcept>
+#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
+  SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::addSecondary(
+      const Args... v) {
+    CORSIKA_LOG_TRACE("SecondaryView::addSecondary(Args&&)");
+    stack_view_iterator proj = getProjectile(); // make this const
+    return addSecondary(proj, v...);
+  }
+
+  template <typename TStackDataType, template <typename> typename TParticleInterface,
+            template <typename T1, template <class> class T2> class MSecondaryProducer>
+  typename SecondaryView<TStackDataType, TParticleInterface,
+                         MSecondaryProducer>::stack_view_iterator
+  SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::begin() {
+    unsigned int i = 0;
+    for (; i < getSize(); ++i) {
+
+      if (!isErased(i)) break;
+    }
+    return stack_view_iterator(*this, i + 1);
+  }
+
+  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
+  SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::begin() const {
+    unsigned int i = 0;
+    for (; i < getSize(); ++i) {
+      if (!isErased(i)) break;
+    }
+
+    return const_stack_view_iterator(*this, i + 1);
+  }
+
+  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
+  SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::cbegin() const {
+    unsigned int i = 0;
+    for (; i < getSize(); ++i) {
+      if (!isErased(i)) break;
+    }
+
+    return const_stack_view_iterator(*this, i + 1);
+  }
+
+  template <typename TStackDataType, template <typename> typename TParticleInterface,
+            template <typename T1, template <class> class T2> class MSecondaryProducer>
+  typename SecondaryView<TStackDataType, TParticleInterface,
+                         MSecondaryProducer>::stack_view_iterator
+  SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::last() {
+    unsigned int i = 0;
+    for (; i < getSize(); ++i) {
+      if (!isErased(getSize() - 1 - i)) break;
+    }
+    return stack_view_iterator(*this, getSize() - 1 - i + 1);
+  }
+
+  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
+  SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::last() const {
+    unsigned int i = 0;
+    for (; i < getSize(); ++i) {
+      if (!isErased(getSize() - 1 - i)) break;
+    }
+    return stack_view_iterator(*this, getSize() - 1 - i + 1);
+  }
+
+  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
+  SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::clast() const {
+    unsigned int i = 0;
+    for (; i < getSize(); ++i) {
+
+      if (!isErased(getSize() - 1 - i)) break;
+    }
+    return const_stack_view_iterator(*this, getSize() - 1 - i + 1);
+  }
+
+  template <typename TStackDataType, template <typename> typename TParticleInterface,
+            template <typename T1, template <class> class T2> class MSecondaryProducer>
+  void SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::swap(
+      stack_view_iterator a, stack_view_iterator b) {
+
+    CORSIKA_LOG_TRACE("View::swap");
+    inner_stack_.swap(getIndexFromIterator(a.getIndex()),
+                      getIndexFromIterator(b.getIndex()));
+  }
+
+  template <typename TStackDataType, template <typename> typename TParticleInterface,
+            template <typename T1, template <class> class T2> class MSecondaryProducer>
+  void SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::copy(
+      stack_view_iterator a, stack_view_iterator b) {
+
+    CORSIKA_LOG_TRACE("View::copy");
+    inner_stack_.copy(getIndexFromIterator(a.getIndex()),
+                      getIndexFromIterator(b.getIndex()));
+  }
+
+  template <typename TStackDataType, template <typename> typename TParticleInterface,
+            template <typename T1, template <class> class T2> class MSecondaryProducer>
+  void SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::copy(
+      const_stack_view_iterator a, stack_view_iterator b) {
+
+    CORSIKA_LOG_TRACE("View::copy");
+    inner_stack_.copy(getIndexFromIterator(a.getIndex()),
+                      getIndexFromIterator(b.getIndex()));
+  }
+
+  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) {
+
+    CORSIKA_LOG_TRACE("SecondaryView::Delete");
+    if (isEmpty()) { /*error*/
+      throw std::runtime_error("Stack, cannot delete entry since size is zero");
+    }
+    if (isErased(p.getIndex() - 1)) { /*error*/
+      throw std::runtime_error("Stack, cannot delete entry since already deleted");
+    }
+    inner_stack_.erase(getIndexFromIterator(p.getIndex()));
+    inner_stack_reference_type::nDeleted_++; // also count in SecondaryView
+  }
+
+  template <typename TStackDataType, template <typename> typename TParticleInterface,
+            template <typename T1, template <class> class T2> class MSecondaryProducer>
+  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.
+    inner_stack_.purge(getIndexFromIterator(getSize()));
+    inner_stack_reference_type::nDeleted_--;
+    indices_.pop_back();
+    return true;
+  }
+
+  template <typename TStackDataType, template <typename> typename TParticleInterface,
+            template <typename T1, template <class> class T2> class MSecondaryProducer>
+  void SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::purge() {
+    unsigned int iStack = 0;
+    unsigned int size = getSize();
+    while (iStack < size) {
+
+      if (isErased(iStack)) {
+        inner_stack_.purge(iStack);
+        indices_.erase(indices_.begin() + iStack);
+      }
+      size = getSize();
+      iStack++;
+    }
+    inner_stack_reference_type::nDeleted_ = 0;
+  }
+
+  template <typename TStackDataType, template <typename> typename TParticleInterface,
+            template <typename T1, template <class> class T2> class MSecondaryProducer>
+  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 = "     ";
+    for (unsigned int iPart = 0; iPart != getSize(); ++iPart) {
+      const_stack_view_iterator itPart(*this, iPart);
+      str += fmt::format(
+          "{}{}{}", new_line, itPart.asString(),
+          (inner_stack_.deleted_[getIndexFromIterator(itPart.getIndex())] ? " [deleted]"
+                                                                          : ""));
+      new_line = "\n     ";
+    }
+    return str;
+  }
+
+  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
+  SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>::addSecondary(
+      stack_view_iterator& proj, const Args... v) {
+    CORSIKA_LOG_TRACE("SecondaryView::addSecondary(stack_view_iterator&, Args&&)");
+    // make space on stack
+    inner_stack_reference_type::getStackData().incrementSize();
+    inner_stack_.deleted_.push_back(false);
+    // get current number of secondaries on stack
+    const unsigned int idSec = getSize();
+    // determine index on (inner) stack where new particle will be located
+    const unsigned int index = inner_stack_reference_type::getStackData().getSize() - 1;
+    indices_.push_back(index);
+    // NOTE: "+1" is since "0" is special marker here for PROJECTILE, see
+    // getIndexFromIterator
+    auto sec = stack_view_iterator(*this, idSec + 1, proj, v...);
+    MSecondaryProducer<TStackDataType, TParticleInterface>::new_secondary(sec);
+    return sec;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/stack/Stack.inl b/corsika/detail/framework/stack/Stack.inl
new file mode 100644
index 0000000000000000000000000000000000000000..30c7c746095e89323e01bbfdae6b8735b99806a4
--- /dev/null
+++ b/corsika/detail/framework/stack/Stack.inl
@@ -0,0 +1,381 @@
+/*
+ * (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/core/Logging.hpp>
+#include <corsika/framework/stack/StackIteratorInterface.hpp>
+
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <utility>
+#include <type_traits>
+
+namespace corsika {
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  template <typename... TArgs>
+  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() {
+    unsigned int i = 0;
+    for (; i < getSize(); ++i) {
+      if (!deleted_[i]) break;
+    }
+    return stack_iterator_type(*this, i);
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  typename Stack<StackData, MParticleInterface>::stack_iterator_type
+  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() {
+    unsigned int i = 0;
+    for (; i < getSize(); ++i) {
+      if (!deleted_[getSize() - 1 - i]) break;
+    }
+    return stack_iterator_type(*this, getSize() - 1 - i);
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
+  Stack<StackData, MParticleInterface>::begin() const {
+    unsigned int i = 0;
+    for (; i < getSize(); ++i) {
+      if (!deleted_[i]) break;
+    }
+    return const_stack_iterator_type(*this, i);
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
+  Stack<StackData, MParticleInterface>::end() 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>::last() const {
+    unsigned int i = 0;
+    for (; i < getSize(); ++i) {
+      if (!deleted_[getSize() - 1 - i]) break;
+    }
+    return const_stack_iterator_type(*this, getSize() - 1 - i);
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
+  Stack<StackData, MParticleInterface>::cbegin() const {
+    unsigned int i = 0;
+    for (; i < getSize(); ++i) {
+      if (!deleted_[i]) break;
+    }
+    return const_stack_iterator_type(*this, i);
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  typename Stack<StackData, MParticleInterface>::const_stack_iterator_type
+  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 {
+    unsigned int i = 0;
+    for (; i < getSize(); ++i) {
+      if (!deleted_[getSize() - 1 - i]) break;
+    }
+
+    return const_stack_iterator_type(*this, getSize() - 1 - i);
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  typename Stack<StackData, MParticleInterface>::stack_iterator_type
+  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 {
+    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() {
+    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 {
+    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() {
+    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) {
+    CORSIKA_LOG_TRACE("Stack::AddParticle");
+    data_.incrementSize();
+    deleted_.push_back(false);
+    return stack_iterator_type(*this, getSize() - 1, v...);
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  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) {
+    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) {
+    CORSIKA_LOG_TRACE("Stack::Copy");
+    data_.copy(a.getIndex(), b.getIndex());
+    if (deleted_[b.getIndex()] && !deleted_[a.getIndex()]) nDeleted_--;
+    if (!deleted_[b.getIndex()] && deleted_[a.getIndex()]) nDeleted_++;
+    deleted_[b.getIndex()] = deleted_[a.getIndex()];
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  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");
+    }
+    if (deleted_[p.getIndex()]) { /*error*/
+      throw std::runtime_error("Stack, cannot delete entry since already deleted");
+    }
+    this->erase(p.getIndex());
+  }
+  /**
+   * delete this particle
+   */
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  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() {
+    return getEntries() == 0;
+  }
+
+  /**
+   * check if this particle was already deleted
+   */
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  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(
+      const const_stack_iterator_type& p) const {
+    return isErased(p.getIndex());
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  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() {
+    if (!deleted_.back())
+      return false; // the last particle is not marked for deletion. Do nothing.
+
+    CORSIKA_LOG_TRACE("Stack::purgeLastIfDeleted: yes");
+    data_.decrementSize();
+    nDeleted_--;
+    deleted_.pop_back();
+    return true;
+  }
+
+  /**
+   * Function to ultimatively remove all entries from the stack
+   * marked as deleted.
+   *
+   * Careful: this will re-order the entries on the stack, since
+   * "gaps" in the stack are filled with entries from the back
+   * (copied).
+   */
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  void Stack<StackData, MParticleInterface>::purge() {
+    unsigned int iStackFront = 0;
+    unsigned int iStackBack = getSize() - 1;
+
+    for (unsigned int iDeleted = 0; iDeleted < getErased(); ++iDeleted) {
+      // search first delete entry on stack
+      while (!deleted_[iStackFront]) { iStackFront++; }
+      // search for last non-deleted particle on stack
+      while (deleted_[iStackBack]) { iStackBack--; }
+      // copy entry from iStackBack to iStackFront
+      data_.copy(iStackBack, iStackFront);
+      data_.decrementSize();
+    }
+    deleted_.clear();
+    nDeleted_ = 0;
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  unsigned int Stack<StackData, MParticleInterface>::getSize() const {
+    return data_.getSize();
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  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
+    std::string new_line = "     ";
+    for (unsigned int iPart = 0; iPart != getSize(); ++iPart) {
+      const_stack_iterator_type itPart(*this, iPart);
+      str += fmt::format("{}{}{}", new_line, itPart.asString(),
+                         (deleted_[itPart.getIndex()] ? " [deleted]" : ""));
+      new_line = "\n     ";
+    }
+    return str;
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  template <typename... TArgs>
+  typename Stack<StackData, MParticleInterface>::stack_iterator_type
+  Stack<StackData, MParticleInterface>::addSecondary(stack_iterator_type& parent,
+                                                     const TArgs... v) {
+    CORSIKA_LOG_TRACE("Stack::AddSecondary");
+    data_.incrementSize();
+    deleted_.push_back(false);
+    return stack_iterator_type(*this, getSize() - 1, parent, v...);
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  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) {
+    CORSIKA_LOG_TRACE("Stack::Copy");
+    data_.copy(a, b);
+    if (deleted_[b] && !deleted_[a]) nDeleted_--;
+    if (!deleted_[b] && deleted_[a]) nDeleted_++;
+    deleted_[b] = deleted_[a];
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  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) {
+    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) {
+    unsigned int iStackBack = getSize() - 1;
+    // search for last non-deleted particle on stack
+    while (deleted_[iStackBack]) { iStackBack--; }
+    // copy entry from iStackBack to iStackFront
+    data_.copy(iStackBack, i);
+    if (deleted_[i]) nDeleted_--;
+    deleted_[i] = deleted_[iStackBack];
+    data_.decrementSize();
+    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(
+      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&
+  Stack<StackData, MParticleInterface>::getStackData() {
+    return data_;
+  }
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  const typename Stack<StackData, MParticleInterface>::value_type&
+  Stack<StackData, MParticleInterface>::getStackData() const {
+    return data_;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/framework/utility/COMBoost.inl b/corsika/detail/framework/utility/COMBoost.inl
new file mode 100644
index 0000000000000000000000000000000000000000..54d1162c327b8ba2f8be9dea485238a0c343ce46
--- /dev/null
+++ b/corsika/detail/framework/utility/COMBoost.inl
@@ -0,0 +1,113 @@
+/*
+ * (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 <cmath>
+
+#include <Eigen/Dense>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/CoordinateSystem.hpp>
+#include <corsika/framework/geometry/FourVector.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+namespace corsika {
+
+  inline COMBoost::COMBoost(FourVector<HEPEnergyType, MomentumVector> const& Pprojectile,
+                            HEPMassType const massTarget)
+      : originalCS_{Pprojectile.getSpaceLikeComponents().getCoordinateSystem()}
+      , rotatedCS_{
+            make_rotationToZ(Pprojectile.getSpaceLikeComponents().getCoordinateSystem(),
+                             Pprojectile.getSpaceLikeComponents())} {
+    auto const pProjectile = Pprojectile.getSpaceLikeComponents();
+    auto const pProjNormSquared = pProjectile.getSquaredNorm();
+    auto const pProjNorm = sqrt(pProjNormSquared);
+
+    auto const eProjectile = Pprojectile.getTimeLikeComponent();
+    auto const massProjectileSquared = eProjectile * eProjectile - pProjNormSquared;
+    auto const s =
+        massTarget * massTarget + massProjectileSquared + 2 * eProjectile * massTarget;
+
+    auto const sqrtS = sqrt(s);
+    auto const sinhEta = -pProjNorm / sqrtS;
+    auto const coshEta = sqrt(1 + pProjNormSquared / s);
+
+    setBoost(coshEta, sinhEta);
+
+    CORSIKA_LOG_TRACE("COMBoost (1-beta)={}, gamma={}, det={}", 1 - sinhEta / coshEta,
+                      coshEta, boost_.determinant() - 1);
+  }
+
+  inline COMBoost::COMBoost(MomentumVector const& momentum, HEPEnergyType mass)
+      : originalCS_{momentum.getCoordinateSystem()}
+      , rotatedCS_{make_rotationToZ(momentum.getCoordinateSystem(), momentum)} {
+    auto const squaredNorm = momentum.getSquaredNorm();
+    auto const norm = sqrt(squaredNorm);
+    auto const sinhEta = -norm / mass;
+    auto const coshEta = sqrt(1 + squaredNorm / (mass * mass));
+    setBoost(coshEta, sinhEta);
+  }
+
+  template <typename FourVector>
+  inline FourVector COMBoost::toCoM(FourVector const& p) const {
+    auto pComponents = p.getSpaceLikeComponents().getComponents(rotatedCS_);
+    Eigen::Vector3d eVecRotated = pComponents.getEigenVector();
+    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, MomentumVector(rotatedCS_, eVecRotated));
+  }
+
+  template <typename FourVector>
+  inline FourVector COMBoost::fromCoM(FourVector const& p) const {
+    auto pCM = p.getSpaceLikeComponents().getComponents(rotatedCS_);
+    auto const Ecm = p.getTimeLikeComponent();
+
+    Eigen::Vector2d com;
+    com << (Ecm * (1 / 1_GeV)), (pCM.getEigenVector()(2) * (1 / 1_GeV).magnitude());
+
+    CORSIKA_LOG_TRACE(
+        "COMBoost::fromCoM Ecm={} GeV"
+        " pcm={} GeV (norm = {} GeV), invariant mass={} GeV",
+        Ecm / 1_GeV, pCM / 1_GeV, pCM.getNorm() / 1_GeV, p.getNorm() / 1_GeV);
+
+    auto const boostedZ = inverseBoost_ * com;
+    auto const E_lab = boostedZ(0) * 1_GeV;
+
+    pCM.getEigenVector()[2] = boostedZ(1) * (1_GeV).magnitude();
+
+    Vector<typename decltype(pCM)::dimension_type> pLab{rotatedCS_, pCM};
+    pLab.rebase(originalCS_);
+
+    FourVector f(E_lab, pLab);
+
+    CORSIKA_LOG_TRACE("COMBoost::fromCoM --> Elab={} GeV",
+                      " plab={} GeV (norm={} GeV) "
+                      " GeV), invariant mass = {}",
+                      E_lab / 1_GeV, f.getNorm() / 1_GeV, pLab.getComponents(),
+                      pLab.getNorm() / 1_GeV);
+
+    return f;
+  }
+
+  inline void COMBoost::setBoost(double coshEta, double sinhEta) {
+    boost_ << coshEta, sinhEta, sinhEta, coshEta;
+    inverseBoost_ << coshEta, -sinhEta, -sinhEta, coshEta;
+  }
+
+  CoordinateSystemPtr COMBoost::getRotatedCS() const { return rotatedCS_; }
+
+} // namespace corsika
diff --git a/Framework/Utilities/CorsikaData.cc b/corsika/detail/framework/utility/CorsikaData.inl
similarity index 83%
rename from Framework/Utilities/CorsikaData.cc
rename to corsika/detail/framework/utility/CorsikaData.inl
index 5cb58ea7660a033262404a6433767871e264dd73..80a1b77caa404185bc618183590d48c67a730dbb 100644
--- a/Framework/Utilities/CorsikaData.cc
+++ b/corsika/detail/framework/utility/CorsikaData.inl
@@ -5,14 +5,13 @@
  * Licence version 3 (GPL Version 3). See file LICENSE for a full version of
  * the license.
  */
-
-#include <corsika/utl/CorsikaData.h>
+#pragma once
 
 #include <cstdlib>
 #include <stdexcept>
 #include <string>
 
-std::string corsika::utl::CorsikaData(std::string const& key) {
+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/CorsikaFenvFallback.inl b/corsika/detail/framework/utility/CorsikaFenvFallback.inl
new file mode 100644
index 0000000000000000000000000000000000000000..61e32daebf412a81c22f6bbda4ace51a34bcea9b
--- /dev/null
+++ b/corsika/detail/framework/utility/CorsikaFenvFallback.inl
@@ -0,0 +1,18 @@
+/*
+ * (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.
+ */
+
+#include <corsika/framework/utility/CorsikaFenv.hpp>
+#include <cfenv>
+
+extern "C" {
+#warning No enabling/disabling of floating point exceptions - platform needs better implementation
+
+inline int feenableexcept(int /*excepts*/) noexcept { return -1; }
+
+inline int fedisableexcept(int /*excepts*/) noexcept { return -1; }
+}
diff --git a/Framework/Utilities/CorsikaFenvOSX.cc b/corsika/detail/framework/utility/CorsikaFenvOSX.inl
similarity index 78%
rename from Framework/Utilities/CorsikaFenvOSX.cc
rename to corsika/detail/framework/utility/CorsikaFenvOSX.inl
index 1028c4b7e0988bb7ced605f71990c9c9f344fbbc..b57a6973da4b892e8da1f2f0ce68a6a3c2486292 100644
--- a/Framework/Utilities/CorsikaFenvOSX.cc
+++ b/corsika/detail/framework/utility/CorsikaFenvOSX.inl
@@ -1,3 +1,11 @@
+/*
+ * (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.
+ */
+
 /**
  * Import public domain code
  *
@@ -10,7 +18,6 @@
  *
  */
 
-#include <corsika/utl/CorsikaFenv.h>
 #include <cfenv>
 
 // Implementation for OS X on intel X64_86
@@ -21,7 +28,7 @@
 
 extern "C" {
 
-int feenableexcept(int excepts) {
+int feenableexcept(int excepts) noexcept {
   static fenv_t fenv;
   int new_excepts = excepts & FE_ALL_EXCEPT;
   // previous masks
@@ -37,7 +44,7 @@ int feenableexcept(int excepts) {
   return fesetenv(&fenv) ? -1 : old_excepts;
 }
 
-int fedisableexcept(int excepts) {
+int fedisableexcept(int excepts) noexcept {
   static fenv_t fenv;
   int new_excepts = excepts & FE_ALL_EXCEPT;
   // all previous masks
diff --git a/corsika/detail/framework/utility/QuarticSolver.inl b/corsika/detail/framework/utility/QuarticSolver.inl
new file mode 100644
index 0000000000000000000000000000000000000000..a82abf7ac022c3c99f6e342f88eb35f2ddaa6425
--- /dev/null
+++ b/corsika/detail/framework/utility/QuarticSolver.inl
@@ -0,0 +1,135 @@
+/*
+ * (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 <cmath>
+
+namespace corsika::quartic_solver {
+
+  //---------------------------------------------------------------------------
+  // solve cubic equation x^3 + a*x^2 + b*x + c = 0
+  // x - array of size 3
+  // In case 3 real roots: => x[0], x[1], x[2], return 3
+  //         2 real roots: x[0], x[1],          return 2
+  //         1 real root : x[0], x[1] ± i*x[2], return 1
+  inline unsigned int solveP3(double* x, double a, double b, double c) {
+    double a2 = a * a;
+    double q = (a2 - 3 * b) / 9;
+    double r = (a * (2 * a2 - 9 * b) + 27 * c) / 54;
+    double r2 = r * r;
+    double q3 = q * q * q;
+    double A, B;
+    if (r2 < q3) {
+      double t = r / sqrt(q3);
+      if (t < -1) t = -1;
+      if (t > 1) t = 1;
+      t = acos(t);
+      a /= 3;
+      q = -2 * sqrt(q);
+      x[0] = q * cos(t / 3) - a;
+      x[1] = q * cos((t + M_2PI) / 3) - a;
+      x[2] = q * cos((t - M_2PI) / 3) - a;
+      return 3;
+    } else {
+      A = -pow(fabs(r) + sqrt(r2 - q3), 1. / 3);
+      if (r < 0) A = -A;
+      B = (0 == A ? 0 : q / A);
+
+      a /= 3;
+      x[0] = (A + B) - a;
+      x[1] = -0.5 * (A + B) - a;
+      x[2] = 0.5 * sqrt(3.) * (A - B);
+      if (fabs(x[2]) < eps) {
+        x[2] = x[1];
+        return 2;
+      }
+
+      return 1;
+    }
+  }
+
+  //---------------------------------------------------------------------------
+  // solve quartic equation x^4 + a*x^3 + b*x^2 + c*x + d
+  // Attention - this function returns dynamically allocated array. It has to be released
+  // afterwards.
+  inline DComplex* solve_quartic(double a, double b, double c, double d) {
+    double a3 = -b;
+    double b3 = a * c - 4. * d;
+    double c3 = -a * a * d - c * c + 4. * b * d;
+
+    // cubic resolvent
+    // y^3 − b*y^2 + (ac−4d)*y − a^2*d−c^2+4*b*d = 0
+
+    double x3[3];
+    unsigned int iZeroes = solveP3(x3, a3, b3, c3);
+
+    double q1, q2, p1, p2, D, sqD, y;
+
+    y = x3[0];
+    // The essence - choosing Y with maximal absolute value.
+    if (iZeroes != 1) {
+      if (fabs(x3[1]) > fabs(y)) y = x3[1];
+      if (fabs(x3[2]) > fabs(y)) y = x3[2];
+    }
+
+    // h1+h2 = y && h1*h2 = d  <=>  h^2 -y*h + d = 0    (h === q)
+
+    D = y * y - 4 * d;
+    if (fabs(D) < eps) // in other words - D==0
+    {
+      q1 = q2 = y * 0.5;
+      // g1+g2 = a && g1+g2 = b-y   <=>   g^2 - a*g + b-y = 0    (p === g)
+      D = a * a - 4 * (b - y);
+      if (fabs(D) < eps) // in other words - D==0
+        p1 = p2 = a * 0.5;
+
+      else {
+        sqD = sqrt(D);
+        p1 = (a + sqD) * 0.5;
+        p2 = (a - sqD) * 0.5;
+      }
+    } else {
+      sqD = sqrt(D);
+      q1 = (y + sqD) * 0.5;
+      q2 = (y - sqD) * 0.5;
+      // g1+g2 = a && g1*h2 + g2*h1 = c       ( && g === p )  Krammer
+      p1 = (a * q1 - c) / (q1 - q2);
+      p2 = (c - a * q2) / (q1 - q2);
+    }
+
+    DComplex* retval = new DComplex[4];
+
+    // solving quadratic eq. - x^2 + p1*x + q1 = 0
+    D = p1 * p1 - 4 * q1;
+    if (D < 0.0) {
+      retval[0].real(-p1 * 0.5);
+      retval[0].imag(sqrt(-D) * 0.5);
+      retval[1] = std::conj(retval[0]);
+    } else {
+      sqD = sqrt(D);
+      retval[0].real((-p1 + sqD) * 0.5);
+      retval[1].real((-p1 - sqD) * 0.5);
+    }
+
+    // solving quadratic eq. - x^2 + p2*x + q2 = 0
+    D = p2 * p2 - 4 * q2;
+    if (D < 0.0) {
+      retval[2].real(-p2 * 0.5);
+      retval[2].imag(sqrt(-D) * 0.5);
+      retval[3] = std::conj(retval[2]);
+    } else {
+      sqD = sqrt(D);
+      retval[2].real((-p2 + sqD) * 0.5);
+      retval[3].real((-p2 - sqD) * 0.5);
+    }
+
+    return retval;
+  }
+
+} // namespace corsika::quartic_solver
\ No newline at end of file
diff --git a/Framework/Utilities/SaveBoostHistogram.hpp b/corsika/detail/framework/utility/SaveBoostHistogram.inl
similarity index 85%
rename from Framework/Utilities/SaveBoostHistogram.hpp
rename to corsika/detail/framework/utility/SaveBoostHistogram.inl
index 508c982aa4b0e06444eb0d95186d3e5138381a9d..94e5e1bbc8a383f99ef6a70776e622e6f0f2ad28 100644
--- a/Framework/Utilities/SaveBoostHistogram.hpp
+++ b/corsika/detail/framework/utility/SaveBoostHistogram.inl
@@ -6,7 +6,9 @@
  * the license.
  */
 
-#include <corsika/third_party/cnpy/cnpy.hpp>
+#pragma once
+
+#include <cnpy.hpp>
 
 #include <boost/histogram.hpp>
 
@@ -17,23 +19,11 @@
 #include <vector>
 #include <string>
 
-#pragma once
-
-namespace corsika::utl {
-
-  enum class SaveMode { overwrite, append };
+namespace corsika {
 
-  /**
-   * This functions saves a boost::histogram into a numpy file. Only rather basic axis
-   * types are supported: regular, variable, integer, category<int>. Only "ordinary" bin
-   * counts (i.e. a double or int) are supported, nothing fancy like profiles.
-   *
-   * Note that this function makes a temporary, dense copy of the histogram, which could
-   * be an issue for huge sizes (e.g. for high dimensions)
-   */
   template <class Axes, class Storage>
   inline void save_hist(boost::histogram::histogram<Axes, Storage> const& h,
-                        std::string const& filename, SaveMode mode = SaveMode::append) {
+                        std::string const& filename, SaveMode mode) {
     unsigned const rank = h.rank();
 
     // append vs. overwrite
@@ -111,5 +101,6 @@ namespace corsika::utl {
     cnpy::npz_save(filename, "data", temp.get(), axes_dims, mode_str);
     // In Python this array can directly be assigned to a histogram view if that
     // histogram has its axes correspondingly: hist.view(flow=True)[:] = file['data']
-  }
-} // namespace corsika::utl
+  } // namespace corsika
+
+} // namespace corsika
\ No newline at end of file
diff --git a/corsika/detail/media/BaseExponential.inl b/corsika/detail/media/BaseExponential.inl
new file mode 100644
index 0000000000000000000000000000000000000000..35b506610419a1a7ce5574b3ab16e7192505d785
--- /dev/null
+++ b/corsika/detail/media/BaseExponential.inl
@@ -0,0 +1,65 @@
+/*
+ * (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 <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+
+namespace corsika {
+
+  template <typename TDerived>
+  auto const& BaseExponential<TDerived>::getImplementation() const {
+    return *static_cast<TDerived const*>(this);
+  }
+
+  template <typename TDerived>
+  GrammageType BaseExponential<TDerived>::getIntegratedGrammage(
+      setup::Trajectory const& traj, LengthType vL, DirectionVector const& axis) const {
+    if (vL == LengthType::zero()) { return GrammageType::zero(); }
+
+    auto const uDotA = traj.getDirection(0).dot(axis).magnitude();
+    auto const rhoStart = getImplementation().getMassDensity(traj.getPosition(0));
+
+    if (uDotA == 0) {
+      return vL * rhoStart;
+    } else {
+      return rhoStart * (lambda_ / uDotA) * (exp(uDotA * vL * invLambda_) - 1);
+    }
+  }
+
+  template <typename TDerived>
+  LengthType BaseExponential<TDerived>::getArclengthFromGrammage(
+      setup::Trajectory const& traj, GrammageType grammage,
+      DirectionVector const& axis) const {
+    auto const uDotA = traj.getDirection(0).dot(axis).magnitude();
+    auto const rhoStart = getImplementation().getMassDensity(traj.getPosition(0));
+
+    if (uDotA == 0) {
+      return grammage / rhoStart;
+    } else {
+      auto const logArg = grammage * invLambda_ * uDotA / rhoStart + 1;
+      if (logArg > 0) {
+        return lambda_ / uDotA * log(logArg);
+      } else {
+        return std::numeric_limits<typename decltype(grammage)::value_type>::infinity() *
+               meter;
+      }
+    }
+  }
+
+  template <typename TDerived>
+  BaseExponential<TDerived>::BaseExponential(Point const& point, MassDensityType rho0,
+                                             LengthType lambda)
+      : rho0_(rho0)
+      , lambda_(lambda)
+      , invLambda_(1 / lambda)
+      , point_(point) {}
+
+} // namespace corsika
diff --git a/corsika/detail/media/Environment.inl b/corsika/detail/media/Environment.inl
new file mode 100644
index 0000000000000000000000000000000000000000..792182a9efee3d45fc07176524d6fdb017ed0dca
--- /dev/null
+++ b/corsika/detail/media/Environment.inl
@@ -0,0 +1,51 @@
+/*
+ * (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 <corsika/media/Environment.hpp>
+
+namespace corsika {
+
+  template <typename IEnvironmentModel>
+  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&
+  Environment<IEnvironmentModel>::getUniverse() {
+    return universe_;
+  }
+
+  template <typename IEnvironmentModel>
+  typename Environment<IEnvironmentModel>::BaseNodeType::VTNUPtr const&
+  Environment<IEnvironmentModel>::getUniverse() const {
+    return universe_;
+  }
+
+  template <typename IEnvironmentModel>
+  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) {
+    static_assert(std::is_base_of_v<IVolume, TVolumeType>,
+                  "unusable type provided, needs to be derived from "
+                  "\"Volume\"");
+
+    return std::make_unique<BaseNodeType>(
+        std::make_unique<TVolumeType>(std::forward<TVolumeArgs>(args)...));
+  }
+
+} // namespace corsika
\ No newline at end of file
diff --git a/corsika/detail/media/FlatExponential.inl b/corsika/detail/media/FlatExponential.inl
new file mode 100644
index 0000000000000000000000000000000000000000..bc782945fe67cc3a6ea21d778913f948a788e62a
--- /dev/null
+++ b/corsika/detail/media/FlatExponential.inl
@@ -0,0 +1,56 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/media/BaseExponential.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+
+namespace corsika {
+
+  template <typename T>
+  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 {
+    return BaseExponential<FlatExponential<T>>::getRho0() *
+           exp(BaseExponential<FlatExponential<T>>::getInvLambda() *
+               (point - BaseExponential<FlatExponential<T>>::getAnchorPoint())
+                   .dot(axis_));
+  }
+
+  template <typename T>
+  NuclearComposition const& FlatExponential<T>::getNuclearComposition() const {
+    return nuclComp_;
+  }
+
+  template <typename T>
+  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 {
+    return BaseExponential<FlatExponential<T>>::getArclengthFromGrammage(line, grammage,
+                                                                         axis_);
+  }
+
+} // namespace corsika
+
+#include <corsika/detail/media/BaseExponential.inl>
diff --git a/corsika/detail/media/HomogeneousMedium.inl b/corsika/detail/media/HomogeneousMedium.inl
new file mode 100644
index 0000000000000000000000000000000000000000..d73354aa680c085241c1f71c37daed4bb1221b47
--- /dev/null
+++ b/corsika/detail/media/HomogeneousMedium.inl
@@ -0,0 +1,44 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+
+namespace corsika {
+
+  template <typename T>
+  HomogeneousMedium<T>::HomogeneousMedium(MassDensityType density,
+                                          NuclearComposition const& nuclComp)
+      : density_(density)
+      , nuclComp_(nuclComp) {}
+
+  template <typename T>
+  MassDensityType HomogeneousMedium<T>::getMassDensity(Point const&) const {
+    return density_;
+  }
+  template <typename T>
+  NuclearComposition const& HomogeneousMedium<T>::getNuclearComposition() const {
+    return nuclComp_;
+  }
+
+  template <typename T>
+  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 {
+    return grammage / density_;
+  }
+} // namespace corsika
diff --git a/corsika/detail/media/InhomogeneousMedium.inl b/corsika/detail/media/InhomogeneousMedium.inl
new file mode 100644
index 0000000000000000000000000000000000000000..064e9610efe4ed04e7707cf626cae8f0a355c076
--- /dev/null
+++ b/corsika/detail/media/InhomogeneousMedium.inl
@@ -0,0 +1,49 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+
+namespace corsika {
+
+  template <typename T, typename TDensityFunction>
+  template <typename... TArgs>
+  InhomogeneousMedium<T, TDensityFunction>::InhomogeneousMedium(
+      NuclearComposition const& nuclComp, TArgs&&... rhoTArgs)
+      : nuclComp_(nuclComp)
+      , densityFunction_(rhoTArgs...) {}
+
+  template <typename T, typename TDensityFunction>
+  MassDensityType InhomogeneousMedium<T, TDensityFunction>::getMassDensity(
+      Point const& point) const {
+    return densityFunction_.evaluateAt(point);
+  }
+
+  template <typename T, typename TDensityFunction>
+  NuclearComposition const&
+  InhomogeneousMedium<T, TDensityFunction>::getNuclearComposition() const {
+    return nuclComp_;
+  }
+
+  template <typename T, typename TDensityFunction>
+  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(
+      setup::Trajectory const& line, GrammageType grammage) const {
+    return densityFunction_.getArclengthFromGrammage(line, grammage);
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/media/LayeredSphericalAtmosphereBuilder.hpp b/corsika/detail/media/LayeredSphericalAtmosphereBuilder.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6d288ee5a516924ccb33b6555928e20806a1a475
--- /dev/null
+++ b/corsika/detail/media/LayeredSphericalAtmosphereBuilder.hpp
@@ -0,0 +1,28 @@
+/*
+ * (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
+
+namespace corsika {
+
+  namespace detail {
+
+    struct NoExtraModelInner {};
+
+    template <typename M>
+    struct NoExtraModel {};
+
+    template <template <typename> typename M>
+    struct has_extra_models : std::true_type {};
+
+    template <>
+    struct has_extra_models<NoExtraModel> : std::false_type {};
+
+  } // namespace detail
+
+} // namespace corsika
diff --git a/corsika/detail/media/LayeredSphericalAtmosphereBuilder.inl b/corsika/detail/media/LayeredSphericalAtmosphereBuilder.inl
new file mode 100644
index 0000000000000000000000000000000000000000..e42310da2c7b13dc51480ff753b836a181557988
--- /dev/null
+++ b/corsika/detail/media/LayeredSphericalAtmosphereBuilder.inl
@@ -0,0 +1,141 @@
+/*
+ * (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 <corsika/framework/core/Logging.hpp>
+
+#include <corsika/media/FlatExponential.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/SlidingPlanarExponential.hpp>
+
+namespace corsika {
+
+  template <typename TMediumInterface, template <typename> typename TMediumModelExtra,
+            typename... TModelArgs>
+  void LayeredSphericalAtmosphereBuilder<TMediumInterface, TMediumModelExtra,
+                                         TModelArgs...>::checkRadius(LengthType r) const {
+    if (r <= previousRadius_) {
+      throw std::runtime_error("radius must be greater than previous");
+    }
+  }
+
+  template <typename TMediumInterface, template <typename> typename TMediumModelExtra,
+            typename... TModelArgs>
+  void LayeredSphericalAtmosphereBuilder<
+      TMediumInterface, TMediumModelExtra,
+      TModelArgs...>::setNuclearComposition(NuclearComposition const& composition) {
+    composition_ = std::make_unique<NuclearComposition>(composition);
+  }
+
+  template <typename TMediumInterface, template <typename> typename TMediumModelExtra,
+            typename... TModelArgs>
+  void LayeredSphericalAtmosphereBuilder<
+      TMediumInterface, TMediumModelExtra,
+      TModelArgs...>::addExponentialLayer(GrammageType b, LengthType c,
+                                          LengthType upperBoundary) {
+
+    auto const radius = earthRadius_ + upperBoundary;
+    checkRadius(radius);
+    previousRadius_ = radius;
+
+    auto node = std::make_unique<VolumeTreeNode<TMediumInterface>>(
+        std::make_unique<Sphere>(center_, radius));
+
+    auto const rho0 = b / c;
+
+    if constexpr (detail::has_extra_models<TMediumModelExtra>::value) {
+      // helper lambda in which the last 5 arguments to make_shared<...> are bound
+      auto lastBound = [&](auto... argPack) {
+        return std::make_shared<
+            TMediumModelExtra<SlidingPlanarExponential<TMediumInterface>>>(
+            argPack..., center_, rho0, -c, *composition_, earthRadius_);
+      };
+
+      // now unpack the additional arguments
+      auto model = std::apply(lastBound, additionalModelArgs_);
+      node->setModelProperties(std::move(model));
+    } else {
+      node->template setModelProperties<SlidingPlanarExponential<TMediumInterface>>(
+          center_, rho0, -c, *composition_, earthRadius_);
+    }
+
+    layers_.push(std::move(node));
+  }
+
+  template <typename TMediumInterface, template <typename> typename TMediumModelExtra,
+            typename... TModelArgs>
+  void LayeredSphericalAtmosphereBuilder<
+      TMediumInterface, TMediumModelExtra,
+      TModelArgs...>::addLinearLayer(LengthType c, LengthType upperBoundary) {
+    auto const radius = earthRadius_ + upperBoundary;
+    checkRadius(radius);
+    previousRadius_ = radius;
+
+    auto node = std::make_unique<VolumeTreeNode<TMediumInterface>>(
+        std::make_unique<Sphere>(center_, radius));
+
+    units::si::GrammageType constexpr b = 1 * 1_g / (1_cm * 1_cm);
+    auto const rho0 = b / c;
+
+    if constexpr (detail::has_extra_models<TMediumModelExtra>::value) {
+      // helper lambda in which the last 2 arguments to make_shared<...> are bound
+      auto lastBound = [&](auto... argPack) {
+        return std::make_shared<TMediumModelExtra<HomogeneousMedium<TMediumInterface>>>(
+            argPack..., rho0, *composition_);
+      };
+
+      // now unpack the additional arguments
+      auto model = std::apply(lastBound, additionalModelArgs_);
+
+      node->setModelProperties(std::move(model));
+    } else {
+      node->template setModelProperties<HomogeneousMedium<TMediumInterface>>(
+          rho0, *composition_);
+    }
+
+    layers_.push(std::move(node));
+  }
+
+  template <typename TMediumInterface, template <typename> typename TMediumModelExtra,
+            typename... TModelArgs>
+  Environment<TMediumInterface> LayeredSphericalAtmosphereBuilder<
+      TMediumInterface, TMediumModelExtra, TModelArgs...>::assemble() {
+    Environment<TMediumInterface> env;
+    assemble(env);
+    return env;
+  }
+
+  template <typename TMediumInterface, template <typename> typename TMediumModelExtra,
+            typename... TModelArgs>
+  void LayeredSphericalAtmosphereBuilder<
+      TMediumInterface, TMediumModelExtra,
+      TModelArgs...>::assemble(Environment<TMediumInterface>& env) {
+    auto& universe = env.getUniverse();
+    auto* outmost = universe.get();
+
+    while (!layers_.empty()) {
+      auto l = std::move(layers_.top());
+      auto* tmp = l.get();
+      outmost->addChild(std::move(l));
+      layers_.pop();
+      outmost = tmp;
+    }
+  }
+
+  template <typename TMediumInterface, template <typename> typename MExtraEnvirnoment>
+  struct make_layered_spherical_atmosphere_builder {
+    template <typename... TArgs>
+    static auto create(Point const& center, LengthType earthRadius, TArgs... args) {
+      return LayeredSphericalAtmosphereBuilder<TMediumInterface, MExtraEnvirnoment,
+                                               TArgs...>{std::forward<TArgs>(args)...,
+                                                         center, earthRadius};
+    }
+  };
+
+} // namespace corsika
diff --git a/corsika/detail/media/LinearApproximationIntegrator.inl b/corsika/detail/media/LinearApproximationIntegrator.inl
new file mode 100644
index 0000000000000000000000000000000000000000..7452ff31d3047b235aaf292fedb78dd0b0552ba5
--- /dev/null
+++ b/corsika/detail/media/LinearApproximationIntegrator.inl
@@ -0,0 +1,49 @@
+/*
+ * (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 <corsika/media/LinearApproximationIntegrator.hpp>
+
+namespace corsika {
+
+  template <typename TDerived>
+  auto const& LinearApproximationIntegrator<TDerived>::getImplementation() const {
+    return *static_cast<TDerived const*>(this);
+  }
+
+  template <typename TDerived>
+  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),
+                                                                line.getDirection(0));
+    return (c0 + 0.5 * c1 * length) * length;
+  }
+
+  template <typename TDerived>
+  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),
+                                                                line.getDirection(0));
+
+    return (1 - 0.5 * grammage * c1 / (c0 * c0)) * grammage / c0;
+  }
+
+  template <typename TDerived>
+  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));
+
+    // todo: provide a real, working implementation
+    return 1_m * std::numeric_limits<double>::infinity();
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/media/MediumPropertyModel.inl b/corsika/detail/media/MediumPropertyModel.inl
new file mode 100644
index 0000000000000000000000000000000000000000..90cdd9754774e9a8c55a6625f2172922fec83c76
--- /dev/null
+++ b/corsika/detail/media/MediumPropertyModel.inl
@@ -0,0 +1,31 @@
+/*
+ * (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 <corsika/media/IMediumPropertyModel.hpp>
+
+namespace corsika {
+
+  template <typename T>
+  template <typename... Args>
+  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 {
+    return medium_;
+  }
+
+  template <typename T>
+  void MediumPropertyModel<T>::setMedium(Medium const medium) {
+    medium_ = medium;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/media/NuclearComposition.inl b/corsika/detail/media/NuclearComposition.inl
new file mode 100644
index 0000000000000000000000000000000000000000..230dfe1a144fd7c82cc52b06b7d87f2cf40c8bd5
--- /dev/null
+++ b/corsika/detail/media/NuclearComposition.inl
@@ -0,0 +1,114 @@
+/*
+ * (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 <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <corsika/media/WeightProvider.hpp>
+
+#include <cassert>
+#include <functional>
+#include <numeric>
+#include <random>
+#include <stdexcept>
+#include <vector>
+
+namespace corsika {
+
+  NuclearComposition::NuclearComposition(std::vector<Code> const& pComponents,
+                                         std::vector<float> const& pFractions)
+      : numberFractions_(pFractions)
+      , components_(pComponents)
+      , avgMassNumber_(std::inner_product(
+            pComponents.cbegin(), pComponents.cend(), pFractions.cbegin(), 0.,
+            std::plus<double>(), [](auto const compID, auto const fraction) -> double {
+              if (is_nucleus(compID)) {
+                return get_nucleus_A(compID) * fraction;
+              } else {
+                return get_mass(compID) / convert_SI_to_HEP(constants::u) * fraction;
+              }
+            })) {
+    assert(pComponents.size() == pFractions.size());
+    auto const sumFractions =
+        std::accumulate(pFractions.cbegin(), pFractions.cend(), 0.f);
+
+    if (!(0.999f < sumFractions && sumFractions < 1.001f)) {
+      throw std::runtime_error("element fractions do not add up to 1");
+    }
+    this->updateHash();
+  }
+
+  template <typename TFunction>
+  inline auto NuclearComposition::getWeightedSum(TFunction const& func) const {
+    using ResultQuantity = decltype(func(*components_.cbegin()));
+
+    auto const prod = [&](auto const compID, auto const fraction) {
+      return func(compID) * fraction;
+    };
+
+    if constexpr (phys::units::is_quantity_v<ResultQuantity>) {
+      return std::inner_product(
+          components_.cbegin(), components_.cend(), numberFractions_.cbegin(),
+          ResultQuantity::zero(), // .zero() is defined for quantity types only
+          std::plus<ResultQuantity>(), prod);
+    } else {
+      return std::inner_product(
+          components_.cbegin(), components_.cend(), numberFractions_.cbegin(),
+          ResultQuantity(0), // in other cases we have to use a bare 0
+          std::plus<ResultQuantity>(), prod);
+    }
+  }
+
+  inline size_t NuclearComposition::getSize() const { return numberFractions_.size(); }
+
+  inline std::vector<float> const& NuclearComposition::getFractions() const {
+    return numberFractions_;
+  }
+
+  inline std::vector<Code> const& NuclearComposition::getComponents() const {
+    return components_;
+  }
+
+  inline double const NuclearComposition::getAverageMassNumber() const {
+    return avgMassNumber_;
+  }
+
+  template <class TRNG>
+  inline Code NuclearComposition::sampleTarget(std::vector<CrossSectionType> const& sigma,
+                                               TRNG& randomStream) const {
+
+    assert(sigma.size() == numberFractions_.size());
+
+    std::discrete_distribution channelDist(
+        WeightProviderIterator<decltype(numberFractions_.begin()),
+                               decltype(sigma.begin())>(numberFractions_.begin(),
+                                                        sigma.begin()),
+        WeightProviderIterator<decltype(numberFractions_.begin()), decltype(sigma.end())>(
+            numberFractions_.end(), sigma.end()));
+
+    auto const iChannel = channelDist(randomStream);
+    return components_[iChannel];
+  }
+
+  // Note: when this class ever modifies its internal data, the hash
+  // must be updated, too!
+  inline size_t NuclearComposition::getHash() const { return hash_; }
+
+  inline void NuclearComposition::updateHash() {
+    std::vector<std::size_t> hashes;
+    for (float ifrac : this->getFractions()) hashes.push_back(std::hash<float>{}(ifrac));
+    for (Code icode : this->getComponents())
+      hashes.push_back(std::hash<int>{}(static_cast<int>(icode)));
+    std::size_t h = std::hash<double>{}(this->getAverageMassNumber());
+    for (std::size_t ih : hashes) h = h ^ (ih << 1);
+    hash_ = h;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/media/ShowerAxis.inl b/corsika/detail/media/ShowerAxis.inl
new file mode 100644
index 0000000000000000000000000000000000000000..1be5d40a66c801299e57c95d1eee3b538839f953
--- /dev/null
+++ b/corsika/detail/media/ShowerAxis.inl
@@ -0,0 +1,117 @@
+/*
+ * (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.
+ */
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <string>
+
+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)
+      : pointStart_(pStart)
+      , length_(length)
+      , throw_(doThrow)
+      , max_length_(length_.getNorm())
+      , steplength_(max_length_ / steps)
+      , axis_normalized_(length / max_length_)
+      , X_(steps + 1) {
+    auto const* const universe = env.getUniverse().get();
+
+    auto rho = [pStart, length, universe](double x) {
+      auto const p = pStart + length * x;
+      auto const* node = universe->getContainingNode(p);
+      return node->getModelProperties().getMassDensity(p).magnitude();
+    };
+
+    double error;
+    int k = 0;
+    X_[0] = GrammageType::zero();
+    auto sum = GrammageType::zero();
+
+    for (int i = 1; i <= steps; ++i) {
+      auto const x_prev = (i - 1.) / steps;
+      auto const d_prev = max_length_ * x_prev;
+      auto const x = double(i) / steps;
+      auto const r = boost::math::quadrature::gauss_kronrod<double, 15>::integrate(
+          rho, x_prev, x, 15, 1e-9, &error);
+      auto const result =
+          MassDensityType(phys::units::detail::magnitude_tag, r) * max_length_;
+
+      sum += result;
+      X_[i] = sum;
+
+      for (; sum > k * X_binning_; ++k) {
+        d_.emplace_back(d_prev + k * X_binning_ * steplength_ / result);
+      }
+    }
+
+    assert(std::is_sorted(X_.cbegin(), X_.cend()));
+    assert(std::is_sorted(d_.cbegin(), d_.cend()));
+  }
+
+  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;
+    unsigned int const upper = lower + 1;
+
+    if (fractionalBin < 0) {
+      CORSIKA_LOG_ERROR("cannot extrapolate to points behind point of injection l={} m",
+                        l / 1_m);
+      if (throw_) {
+        throw std::runtime_error(
+            "cannot extrapolate to points behind point of injection");
+      }
+      return getMinimumX();
+    }
+
+    if (upper >= X_.size()) {
+      CORSIKA_LOG_ERROR(
+          "shower axis too short, cannot extrapolate (l / max_length_ = {} )",
+          l / max_length_);
+      if (throw_) {
+        const std::string err = fmt::format(
+            "shower axis too short, cannot extrapolate (l / max_length_ = {} )",
+            l / max_length_);
+        throw std::runtime_error(err.c_str());
+      }
+    }
+    CORSIKA_LOG_TRACE("showerAxis::X frac={}, fractionalBin={}, lower={}, upper={}",
+                      fraction, fractionalBin, lower, upper);
+
+    assert(0 <= fraction && fraction <= 1.);
+
+    CORSIKA_LOG_TRACE("ShowerAxis::getX l={} m, lower={}, fraction={}, upper={}", l / 1_m,
+                      lower, fraction, upper);
+
+    // linear interpolation between getX[lower] and X[upper]
+    return X_[upper] * fraction + X_[lower] * (1 - fraction);
+  }
+
+  LengthType ShowerAxis::getSteplength() const { return steplength_; }
+
+  GrammageType ShowerAxis::getMaximumX() const { return *X_.rbegin(); }
+
+  GrammageType ShowerAxis::getMinimumX() const { return GrammageType::zero(); }
+
+  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 {
+    return axis_normalized_;
+  }
+
+  Point const& ShowerAxis::getStart() const { return pointStart_; }
+
+} // namespace corsika
diff --git a/corsika/detail/media/SlidingPlanarExponential.inl b/corsika/detail/media/SlidingPlanarExponential.inl
new file mode 100644
index 0000000000000000000000000000000000000000..c5c517954bcc06858cd66e8142bcd1b30ebb097e
--- /dev/null
+++ b/corsika/detail/media/SlidingPlanarExponential.inl
@@ -0,0 +1,58 @@
+/*
+ * (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 <corsika/media/SlidingPlanarExponential.hpp>
+
+namespace corsika {
+
+  template <typename T>
+  SlidingPlanarExponential<T>::SlidingPlanarExponential(
+      Point const& p0, MassDensityType rho0, LengthType lambda,
+      NuclearComposition const& nuclComp, LengthType referenceHeight)
+      : BaseExponential<SlidingPlanarExponential<T>>(p0, rho0, lambda)
+      , nuclComp_(nuclComp)
+      , referenceHeight_(referenceHeight) {}
+
+  template <typename T>
+  MassDensityType SlidingPlanarExponential<T>::getMassDensity(Point const& point) const {
+    auto const height =
+        (point - BaseExponential<SlidingPlanarExponential<T>>::getAnchorPoint())
+            .getNorm() -
+        referenceHeight_;
+    return BaseExponential<SlidingPlanarExponential<T>>::getRho0() *
+           exp(BaseExponential<SlidingPlanarExponential<T>>::getInvLambda() * height);
+  }
+
+  template <typename T>
+  NuclearComposition const& SlidingPlanarExponential<T>::getNuclearComposition() const {
+    return nuclComp_;
+  }
+
+  template <typename T>
+  GrammageType SlidingPlanarExponential<T>::getIntegratedGrammage(
+      setup::Trajectory const& traj, LengthType l) const {
+    auto const axis = (traj.getPosition(0) -
+                       BaseExponential<SlidingPlanarExponential<T>>::getAnchorPoint())
+                          .normalized();
+    return BaseExponential<SlidingPlanarExponential<T>>::getIntegratedGrammage(traj, l,
+                                                                               axis);
+  }
+
+  template <typename T>
+  LengthType SlidingPlanarExponential<T>::getArclengthFromGrammage(
+      setup::Trajectory const& traj, GrammageType const grammage) const {
+    auto const axis = (traj.getPosition(0) -
+                       BaseExponential<SlidingPlanarExponential<T>>::getAnchorPoint())
+                          .normalized();
+    return BaseExponential<SlidingPlanarExponential<T>>::getArclengthFromGrammage(
+        traj, grammage, axis);
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/media/UniformRefractiveIndex.inl b/corsika/detail/media/UniformRefractiveIndex.inl
new file mode 100644
index 0000000000000000000000000000000000000000..1abea247066330608dfb4ace74eedddbbe91c7cc
--- /dev/null
+++ b/corsika/detail/media/UniformRefractiveIndex.inl
@@ -0,0 +1,31 @@
+/*
+ * (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 <corsika/media/IRefractiveIndexModel.hpp>
+
+namespace corsika {
+
+  template <typename T>
+  template <typename... Args>
+  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 {
+    return n_;
+  }
+
+  template <typename T>
+  void UniformRefractiveIndex<T>::setRefractiveIndex(double const& n) {
+    n_ = n;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/media/Universe.inl b/corsika/detail/media/Universe.inl
new file mode 100644
index 0000000000000000000000000000000000000000..b3f4d2541473ac7f3430ecd40bf607cbf4227464
--- /dev/null
+++ b/corsika/detail/media/Universe.inl
@@ -0,0 +1,21 @@
+/*
+ * (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 <corsika/media/Environment.hpp>
+
+namespace corsika {
+
+  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; }
+
+} // namespace corsika
diff --git a/corsika/detail/media/VolumeTreeNode.inl b/corsika/detail/media/VolumeTreeNode.inl
new file mode 100644
index 0000000000000000000000000000000000000000..bdae7cc12b3525ef932c3319a67e5e05c683121e
--- /dev/null
+++ b/corsika/detail/media/VolumeTreeNode.inl
@@ -0,0 +1,92 @@
+/*
+ * (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 <corsika/framework/geometry/IVolume.hpp>
+#include <corsika/media/IMediumModel.hpp>
+
+namespace corsika {
+
+  template <typename IModelProperties>
+  bool VolumeTreeNode<IModelProperties>::contains(Point const& p) const {
+    return geoVolume_->contains(p);
+  }
+
+  template <typename IModelProperties>
+  inline VolumeTreeNode<IModelProperties> const*
+  VolumeTreeNode<IModelProperties>::excludes(Point const& p) const {
+    auto exclContainsIter =
+        std::find_if(excludedNodes_.cbegin(), excludedNodes_.cend(),
+                     [&](auto const& s) { return bool(s->contains(p)); });
+
+    return exclContainsIter != excludedNodes_.cend() ? *exclContainsIter : nullptr;
+  }
+
+  /** 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.
+   */
+  template <typename IModelProperties>
+  VolumeTreeNode<IModelProperties> const*
+  VolumeTreeNode<IModelProperties>::getContainingNode(Point const& p) const {
+    if (!contains(p)) { return nullptr; }
+
+    if (auto const childContainsIter =
+            std::find_if(childNodes_.cbegin(), childNodes_.cend(),
+                         [&](auto const& s) { return bool(s->contains(p)); });
+        childContainsIter == childNodes_.cend()) // not contained in any of the children
+    {
+      if (auto const exclContainsIter = excludes(p)) // contained in any excluded nodes
+      {
+        return exclContainsIter->getContainingNode(p);
+      } else {
+        return this;
+      }
+    } else {
+      return (*childContainsIter)->getContainingNode(p);
+    }
+  }
+
+  template <typename IModelProperties>
+  template <typename TCallable, bool preorder>
+  void VolumeTreeNode<IModelProperties>::walk(TCallable func) {
+    if constexpr (preorder) { func(*this); }
+
+    std::for_each(childNodes_.begin(), childNodes_.end(),
+                  [&](auto& v) { v->walk(func); });
+
+    if constexpr (!preorder) { func(*this); };
+  }
+
+  template <typename IModelProperties>
+  void VolumeTreeNode<IModelProperties>::addChild(
+      typename VolumeTreeNode<IModelProperties>::VTNUPtr pChild) {
+    pChild->parentNode_ = this;
+    childNodes_.push_back(std::move(pChild));
+    // It is a bad idea to return an iterator to the inserted element
+    // because it might get invalidated when the vector needs to grow
+    // later and the caller won't notice.
+  }
+
+  template <typename IModelProperties>
+  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
new file mode 100644
index 0000000000000000000000000000000000000000..af9849aa3ca75b9bdaeefc661d56232f1ab895f4
--- /dev/null
+++ b/corsika/detail/media/WeightProvider.inl
@@ -0,0 +1,48 @@
+/*
+ * (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 <vector>
+
+namespace corsika {
+
+  template <class AConstIterator, class BConstIterator>
+  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 {
+    return ((*aIter_) * (*bIter_)).magnitude();
+  }
+
+  template <class AConstIterator, class BConstIterator>
+  WeightProviderIterator<AConstIterator, BConstIterator>&
+  WeightProviderIterator<AConstIterator,
+                         BConstIterator>::operator++() { // prefix ++
+    ++aIter_;
+    ++bIter_;
+    return *this;
+  }
+
+  template <class AConstIterator, class BConstIterator>
+  bool WeightProviderIterator<AConstIterator, BConstIterator>::operator==(
+      WeightProviderIterator other) {
+    return aIter_ == other.aIter_;
+  }
+
+  template <class AConstIterator, class BConstIterator>
+  bool WeightProviderIterator<AConstIterator, BConstIterator>::operator!=(
+      WeightProviderIterator other) {
+    return !(*this == other);
+  }
+
+} // namespace corsika
\ No newline at end of file
diff --git a/corsika/detail/modules/HadronicElasticModel.inl b/corsika/detail/modules/HadronicElasticModel.inl
new file mode 100644
index 0000000000000000000000000000000000000000..d0a54a711b7b1c62a48dfffbc8bcab60a8a3c5a0
--- /dev/null
+++ b/corsika/detail/modules/HadronicElasticModel.inl
@@ -0,0 +1,188 @@
+/*
+ * (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 <corsika/modules/HadronicElasticModel.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/framework/geometry/FourVector.hpp>
+#include <corsika/framework/random/ExponentialDistribution.hpp>
+#include <corsika/framework/utility/COMBoost.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+
+#include <iomanip>
+#include <iostream>
+
+namespace corsika {
+
+  HadronicElasticInteraction::HadronicElasticInteraction(CrossSectionType x,
+                                                         CrossSectionType y)
+      : parX_(x)
+      , parY_(y) {}
+
+  template <>
+  GrammageType HadronicElasticInteraction::getInteractionLength(SetupParticle const& p) {
+    if (p.getPID() == Code::Proton) {
+      auto const* currentNode = p.getNode();
+      auto const& mediumComposition =
+          currentNode->getModelProperties().getNuclearComposition();
+
+      auto const& components = mediumComposition.getComponents();
+      auto const& fractions = mediumComposition.getFractions();
+
+      auto const projectileMomentum = p.getMomentum();
+      auto const projectileMomentumSquaredNorm = projectileMomentum.getSquaredNorm();
+      auto const projectileEnergy = p.getEnergy();
+
+      auto const avgCrossSection = [&]() {
+        CrossSectionType avgCrossSection = 0_b;
+
+        for (size_t i = 0; i < fractions.size(); ++i) {
+          auto const targetMass = get_mass(components[i]);
+          auto const s = static_pow<2>(projectileEnergy + targetMass) -
+                         projectileMomentumSquaredNorm;
+          avgCrossSection += getCrossSection(s) * fractions[i];
+        }
+
+        std::cout << "avgCrossSection: " << avgCrossSection / 1_mb << " mb" << std::endl;
+
+        return avgCrossSection;
+      }();
+
+      auto const avgTargetMassNumber = mediumComposition.getAverageMassNumber();
+
+      GrammageType const interactionLength =
+          avgTargetMassNumber * constants::u / avgCrossSection;
+
+      return interactionLength;
+    } else {
+      return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
+    }
+  }
+
+  template <typename TParticle>
+  ProcessReturn HadronicElasticInteraction::doInteraction(TParticle& p) {
+    if (p.getPID() != Code::Proton) { return ProcessReturn::Ok; }
+
+    const auto* currentNode = p.getNode();
+    const auto& composition = currentNode->getModelProperties().getNuclearComposition();
+    const auto& components = composition.getComponents();
+
+    std::vector<CrossSectionType> cross_section_of_components(
+        composition.getComponents().size());
+
+    auto const projectileMomentum = p.getMomentum();
+    auto const projectileMomentumSquaredNorm = projectileMomentum.getSquaredNorm();
+    auto const projectileEnergy = p.getEnergy();
+
+    for (size_t i = 0; i < components.size(); ++i) {
+      auto const targetMass = get_mass(components[i]);
+      auto const s =
+          static_pow<2>(projectileEnergy + targetMass) - projectileMomentumSquaredNorm;
+      cross_section_of_components[i] = CrossSection(s);
+    }
+
+    const auto targetCode = composition.SampleTarget(cross_section_of_components, RNG_);
+
+    auto const targetMass = get_mass(targetCode);
+
+    std::uniform_real_distribution phiDist(0., 2 * M_PI);
+
+    FourVector const projectileLab(projectileEnergy, projectileMomentum);
+    FourVector const targetLab(
+        targetMass,
+        MomentumVector(projectileMomentum.getCoordinateSystem(), {0_eV, 0_eV, 0_eV}));
+    COMBoost const boost(projectileLab, targetMass);
+
+    auto const projectileCoM = boost.toCoM(projectileLab);
+    auto const targetCoM = boost.toCoM(targetLab);
+
+    auto const pProjectileCoMSqNorm =
+        projectileCoM.getSpaceLikeComponents().getSquaredNorm();
+    auto const pProjectileCoMNorm = sqrt(pProjectileCoMSqNorm);
+
+    auto const eProjectileCoM = projectileCoM.getTimeLikeComponent();
+    auto const eTargetCoM = targetCoM.getTimeLikeComponent();
+
+    auto const sqrtS = eProjectileCoM + eTargetCoM;
+    auto const s = static_pow<2>(sqrtS);
+
+    auto const B = this->B(s);
+    std::cout << B << std::endl;
+
+    ExponentialDistribution tDist(1 / B);
+    auto const absT = [&]() {
+      decltype(tDist(RNG_)) absT;
+      auto const maxT = 4 * pProjectileCoMSqNorm;
+
+      do {
+        // |t| cannot become arbitrarily large, max. given by GER eq. (4.16), so we just
+        // throw again until we have an acceptable value. Note that the formula holds in
+        // any frame despite of what is stated in the book.
+        absT = tDist(RNG_);
+      } while (absT >= maxT);
+
+      return absT;
+    }();
+
+    std::cout << "HadronicElasticInteraction: s = " << s * constants::invGeVsq
+              << " GeV²; absT = " << absT * constants::invGeVsq << " GeV² (max./GeV² = "
+              << 4 * constants::invGeVsq * projectileMomentumSquaredNorm << ')'
+              << std::endl;
+
+    auto const theta = 2 * asin(sqrt(absT / (4 * pProjectileCoMSqNorm)));
+    auto const phi = phiDist(RNG_);
+
+    auto const projectileScatteredLab =
+        boost.fromCoM(FourVector<HEPEnergyType, MomentumVector>(
+            eProjectileCoM, MomentumVector(projectileMomentum.getCoordinateSystem(),
+                                           {pProjectileCoMNorm * sin(theta) * cos(phi),
+                                            pProjectileCoMNorm * sin(theta) * sin(phi),
+                                            pProjectileCoMNorm * cos(theta)})));
+
+    p.setMomentum(projectileScatteredLab.getSpaceLikeComponents());
+    p.setEnergy(
+        sqrt(projectileScatteredLab.getSpaceLikeComponents().getSquaredNorm() +
+             static_pow<2>(get_mass(
+                 p.getPID())))); // Don't use energy from boost. It can be smaller than
+                                 // the momentum due to limited numerical accuracy.
+
+    return ProcessReturn::Ok;
+  }
+
+  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;
+    return result;
+  }
+
+  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) +
+                                        parY_ * pow(s * constants::invGeVsq, -gfEta);
+
+    // according to Schuler & Sjöstrand, PRD 49, 2257 (1994)
+    // (we ignore rho because rho^2 is just ~2 %)
+    auto const sigmaElastic =
+        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;
+    return sigmaElastic;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/modules/LongitudinalProfile.inl b/corsika/detail/modules/LongitudinalProfile.inl
new file mode 100644
index 0000000000000000000000000000000000000000..592f71464ed54d75cc7a20c30f43d08cd59384ad
--- /dev/null
+++ b/corsika/detail/modules/LongitudinalProfile.inl
@@ -0,0 +1,74 @@
+/*
+ * (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.
+ */
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <corsika/modules/LongitudinalProfile.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <cmath>
+#include <iomanip>
+#include <limits>
+
+namespace corsika {
+
+  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) {
+    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));
+
+    const int binStart = std::ceil(grammageStart / dX_);
+    const int binEnd = std::floor(grammageEnd / dX_);
+
+    for (int b = binStart; b <= binEnd; ++b) {
+      if (pid == Code::Gamma) {
+        profiles_.at(b)[ProfileIndex::Gamma]++;
+      } else if (pid == Code::Positron) {
+        profiles_.at(b)[ProfileIndex::Positron]++;
+      } else if (pid == Code::Electron) {
+        profiles_.at(b)[ProfileIndex::Electron]++;
+      } else if (pid == Code::MuPlus) {
+        profiles_.at(b)[ProfileIndex::MuPlus]++;
+      } else if (pid == Code::MuMinus) {
+        profiles_.at(b)[ProfileIndex::MuMinus]++;
+      } else if (is_hadron(pid)) {
+        profiles_.at(b)[ProfileIndex::Hadron]++;
+      }
+    }
+
+    return ProcessReturn::Ok;
+  }
+
+  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) {
+      f << std::setprecision(5) << std::setw(11) << b * (dX_ / (1_g / 1_cm / 1_cm));
+      for (auto const& N : profiles_.at(b)) {
+        f << std::setw(width) << std::setprecision(precision) << std::scientific << N;
+      }
+      f << std::endl;
+    }
+  }
+} // namespace corsika
diff --git a/corsika/detail/modules/ObservationPlane.inl b/corsika/detail/modules/ObservationPlane.inl
new file mode 100644
index 0000000000000000000000000000000000000000..e266a357f3c89351d6126de767bc09978717a34f
--- /dev/null
+++ b/corsika/detail/modules/ObservationPlane.inl
@@ -0,0 +1,158 @@
+/*
+ * (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.
+ */
+
+#include <corsika/modules/ObservationPlane.hpp>
+
+#include <fstream>
+
+namespace corsika {
+
+  ObservationPlane::ObservationPlane(Plane const& obsPlane, DirectionVector const& x_axis,
+                                     std::string const& filename, bool deleteOnHit)
+      : plane_(obsPlane)
+      , outputStream_(filename)
+      , deleteOnHit_(deleteOnHit)
+      , energy_ground_(0_GeV)
+      , count_ground_(0)
+      , xAxis_(x_axis.normalized())
+      , yAxis_(obsPlane.getNormal().cross(xAxis_)) {
+    outputStream_ << "#PDG code, energy / eV, x distance / m, y distance / m"
+                  << std::endl;
+  }
+
+  ProcessReturn ObservationPlane::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; }
+
+    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();
+
+    outputStream_ << static_cast<int>(get_PDG(particle.getPID())) << ' ' << energy / 1_eV
+                  << ' ' << displacement.dot(xAxis_) / 1_m << ' '
+                  << displacement.dot(yAxis_) / 1_m
+                  << (trajectory.getPosition(1) - plane_.getCenter()).getNorm() / 1_m
+                  << std::endl;
+
+    if (deleteOnHit_) {
+      count_ground_++;
+      energy_ground_ += energy;
+      particle.erase();
+      return ProcessReturn::ParticleAbsorbed;
+    } else {
+      return ProcessReturn::Ok;
+    }
+  }
+
+  LengthType ObservationPlane::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());
+
+    if (timeOfIntersection < TimeType::zero()) {
+      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);
+    return dist;
+  }
+
+  void ObservationPlane::showResults() const {
+    CORSIKA_LOG_INFO(
+        " ******************************\n"
+        " ObservationPlane: \n"
+        " energy in ground (GeV)     :  {}\n"
+        " no. of particles in ground :  {}\n"
+        " ******************************",
+        energy_ground_ / 1_GeV, count_ground_);
+  }
+
+  void ObservationPlane::reset() {
+    energy_ground_ = 0_GeV;
+    count_ground_ = 0;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/modules/OnShellCheck.inl b/corsika/detail/modules/OnShellCheck.inl
new file mode 100644
index 0000000000000000000000000000000000000000..c479ff66ed21a13cc05b8fafa9bb35d1882c38ca
--- /dev/null
+++ b/corsika/detail/modules/OnShellCheck.inl
@@ -0,0 +1,83 @@
+/*
+ * (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/FourVector.hpp>
+#include <corsika/modules/OnShellCheck.hpp>
+
+namespace corsika {
+
+  OnShellCheck::OnShellCheck(const double vMassTolerance, const double vEnergyTolerance,
+                             const bool vError)
+      : mass_tolerance_(vMassTolerance)
+      , energy_tolerance_(vEnergyTolerance)
+      , throw_error_(vError) {
+    std::cout << "OnShellCheck: mass tolerance is set to " << mass_tolerance_ * 100 << "%"
+              << std::endl
+              << "              energy tolerance is set to " << energy_tolerance_ * 100
+              << "%" << std::endl;
+  }
+
+  OnShellCheck::~OnShellCheck() {
+    std::cout << "OnShellCheck: summary" << std::endl
+              << " particles shifted: " << int(count_) << std::endl;
+    if (count_)
+      std::cout << " average energy shift (%): " << average_shift_ / count_ * 100.
+                << std::endl
+                << " max. energy shift (%): " << max_shift_ * 100. << std::endl;
+  }
+
+  template <typename TView>
+  void OnShellCheck::doSecondaries(TView& vS) {
+    for (auto& p : vS) {
+      auto const pid = p.getPID();
+      if (!is_hadron(pid) || is_nucleus(pid)) continue;
+      auto const e_original = p.getEnergy();
+      auto const p_original = p.getMomentum();
+      auto const Plab = FourVector(e_original, p_original);
+      auto const m_kinetic = Plab.getNorm();
+      auto const m_corsika = get_mass(pid);
+      auto const m_err_abs = abs(m_kinetic - m_corsika);
+      if (m_err_abs >= mass_tolerance_ * m_corsika) {
+        const HEPEnergyType e_shifted =
+            sqrt(p_original.getSquaredNorm() + m_corsika * m_corsika);
+        auto const e_shift_relative = (e_shifted / e_original - 1);
+        count_ = count_ + 1;
+        average_shift_ += abs(e_shift_relative);
+        if (abs(e_shift_relative) > max_shift_) max_shift_ = abs(e_shift_relative);
+        std::cout << "OnShellCheck: shift particle mass for " << pid << std::endl
+                  << std::setw(40) << std::setfill(' ')
+                  << "corsika mass (GeV): " << m_corsika / 1_GeV << std::endl
+                  << std::setw(40) << std::setfill(' ')
+                  << "kinetic mass (GeV): " << m_kinetic / 1_GeV << std::endl
+                  << std::setw(40) << std::setfill(' ')
+                  << "m_kin-m_cor (GeV): " << m_err_abs / 1_GeV << std::endl
+                  << std::setw(40) << std::setfill(' ')
+                  << "mass tolerance (GeV): " << (m_corsika * mass_tolerance_) / 1_GeV
+                  << std::endl;
+        /*
+          For now we warn if the necessary shift is larger than 1%.
+          we could promote this to an error.
+        */
+        if (abs(e_shift_relative) > energy_tolerance_) {
+          std::cout << "OnShellCheck: warning! shifted particle energy by "
+                    << e_shift_relative * 100 << " %" << std::endl;
+          if (throw_error_)
+            throw std::runtime_error(
+                "OnShellCheck: error! shifted energy by large amount!");
+        }
+
+        // reset energy
+        p.setEnergy(e_shifted);
+      } else
+        std::cout << "OnShellCheck: particle mass for " << pid << " OK" << std::endl;
+    }
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/modules/ParticleCut.inl b/corsika/detail/modules/ParticleCut.inl
new file mode 100644
index 0000000000000000000000000000000000000000..da19bce2727550a09a3ccede6b08a8ead7ca51ec
--- /dev/null
+++ b/corsika/detail/modules/ParticleCut.inl
@@ -0,0 +1,133 @@
+/*
+ * (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 <corsika/modules/ParticleCut.hpp>
+
+namespace corsika {
+
+  ParticleCut::ParticleCut(const HEPEnergyType eCut, bool em, bool inv)
+      : energy_cut_(eCut)
+      , doCutEm_(em)
+      , doCutInv_(inv)
+      , energy_(0_GeV)
+      , em_energy_(0_GeV)
+      , em_count_(0)
+      , inv_energy_(0_GeV)
+      , inv_count_(0) {}
+
+  template <typename TParticle>
+  bool ParticleCut::isBelowEnergyCut(TParticle const& vP) const {
+    auto const energyLab = vP.getEnergy();
+    // nuclei
+    if (vP.getPID() == Code::Nucleus) {
+      // calculate energy per nucleon
+      auto const ElabNuc = energyLab / vP.getNuclearA();
+      return (ElabNuc < energy_cut_);
+    } else {
+      return (energyLab < energy_cut_);
+    }
+  }
+
+  bool ParticleCut::isEmParticle(Code vCode) const {
+    // FOR NOW: switch
+    switch (vCode) {
+      case Code::Gamma:
+      case Code::Electron:
+      case Code::Positron:
+        return true;
+      default:
+        return false;
+    }
+  }
+
+  bool ParticleCut::isInvisible(Code vCode) const {
+    switch (vCode) {
+      case Code::NuE:
+      case Code::NuEBar:
+      case Code::NuMu:
+      case Code::NuMuBar:
+        return true;
+
+      default:
+        return false;
+    }
+  }
+
+  template <typename TParticle>
+  bool ParticleCut::checkCutParticle(const TParticle& particle) {
+
+    const Code pid = particle.getPID();
+    HEPEnergyType energy = particle.getEnergy();
+    CORSIKA_LOG_DEBUG(fmt::format("ParticleCut: checking {}, E= {} GeV, EcutTot={} GeV",
+                                  pid, energy / 1_GeV,
+                                  (em_energy_ + inv_energy_ + energy_) / 1_GeV));
+    if (doCutEm_ && isEmParticle(pid)) {
+      CORSIKA_LOG_DEBUG("removing em. particle...");
+      em_energy_ += energy;
+      em_count_ += 1;
+      return true;
+    } else if (doCutInv_ && isInvisible(pid)) {
+      CORSIKA_LOG_DEBUG("removing inv. particle...");
+      inv_energy_ += energy;
+      inv_count_ += 1;
+      return true;
+    } else if (isBelowEnergyCut(particle)) {
+      CORSIKA_LOG_DEBUG("removing low en. particle...");
+      energy_ += energy;
+      return true;
+    } else if (particle.getTime() > 10_ms) {
+      CORSIKA_LOG_DEBUG("removing OLD particle...");
+      energy_ += energy;
+      return true;
+    }
+    return false; // this particle will not be removed/cut
+  }
+
+  void ParticleCut::doSecondaries(corsika::setup::StackView& vS) {
+    auto particle = vS.begin();
+    while (particle != vS.end()) {
+      if (checkCutParticle(particle)) { particle.erase(); }
+      ++particle; // next entry in SecondaryView
+    }
+  }
+
+  ProcessReturn ParticleCut::doContinuous(corsika::setup::Stack::particle_type& particle,
+                                          corsika::setup::Trajectory 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::showResults() {
+    CORSIKA_LOG_INFO(
+        " ******************************\n"
+        " energy in em.  component (GeV): {} \n "
+        " no. of em.  particles injected: {} \n "
+        " energy in inv. component (GeV): {} \n "
+        " no. of inv. particles injected: {} \n "
+        " energy below particle cut (GeV): {} \n"
+        " ******************************",
+        em_energy_ / 1_GeV, em_count_, inv_energy_ / 1_GeV, inv_count_, energy_ / 1_GeV);
+  }
+
+  void ParticleCut::reset() {
+    em_energy_ = 0_GeV;
+    em_count_ = 0;
+    inv_energy_ = 0_GeV;
+    inv_count_ = 0;
+    energy_ = 0_GeV;
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/modules/StackInspector.inl b/corsika/detail/modules/StackInspector.inl
new file mode 100644
index 0000000000000000000000000000000000000000..517e017fceb9baf68c071399c5e67bed8a75b3b6
--- /dev/null
+++ b/corsika/detail/modules/StackInspector.inl
@@ -0,0 +1,79 @@
+/*
+ * (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 <corsika/modules/StackInspector.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <chrono>
+#include <iomanip>
+#include <iostream>
+#include <limits>
+
+namespace corsika {
+
+  template <typename TStack>
+  StackInspector<TStack>::StackInspector(const int vNStep, const bool vReportStack,
+                                         const HEPEnergyType vE0)
+      : StackProcess<StackInspector<TStack>>(vNStep)
+      , ReportStack_(vReportStack)
+      , E0_(vE0)
+      , StartTime_(std::chrono::system_clock::now()) {}
+
+  template <typename TStack>
+  StackInspector<TStack>::~StackInspector() {}
+
+  template <typename TStack>
+  void StackInspector<TStack>::doStack(const TStack& vS) {
+
+    [[maybe_unused]] int i = 0;
+    HEPEnergyType Etot = 0_GeV;
+
+    for (const auto& 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();
+        if (iterP.getPID() == Code::Nucleus)
+          std::cout << " nuc_ref=" << iterP.getNucleusRef();
+        std::cout << std::endl;
+      }
+    }
+
+    auto const now = std::chrono::system_clock::now();
+    const std::chrono::duration<double> 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;
+    double const progress = dE / E0_;
+
+    double const eta_seconds = elapsed_seconds.count() / progress;
+    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;
+  }
+
+} // namespace corsika
\ No newline at end of file
diff --git a/corsika/detail/modules/TrackWriter.inl b/corsika/detail/modules/TrackWriter.inl
new file mode 100644
index 0000000000000000000000000000000000000000..27b47cf6571c23975842fdec279e582ed30ce775
--- /dev/null
+++ b/corsika/detail/modules/TrackWriter.inl
@@ -0,0 +1,60 @@
+/*
+ * (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 <corsika/modules/TrackWriter.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <iomanip>
+#include <limits>
+
+namespace corsika {
+
+  TrackWriter::TrackWriter(std::string const& filename)
+      : filename_(filename) {
+    using namespace std::string_literals;
+
+    file_.open(filename_);
+    file_
+        << "# PID, E / eV, start coordinates / m, displacement vector to end / m, steplength / m "s
+        << '\n';
+  }
+
+  template <typename TParticle, typename TTrack>
+  ProcessReturn TrackWriter::doContinuous(const TParticle& vP, const TTrack& vT) {
+    auto const start = vT.getPosition(0).getCoordinates();
+    auto const delta = vT.getPosition(1).getCoordinates() - start;
+    auto const pdg = static_cast<int>(get_PDG(vP.getPID()));
+
+    // clang-format off
+    file_ << std::setw(7) << pdg
+          << std::setw(width_) << std::scientific << std::setprecision(precision_) << vP.getEnergy() / 1_eV
+          << std::setw(width_) << std::scientific << std::setprecision(precision_) << start[0] / 1_m 
+          << std::setw(width_) << std::scientific << std::setprecision(precision_) << start[1] / 1_m
+          << std::setw(width_) << std::scientific << std::setprecision(precision_) << start[2] / 1_m
+          << std::setw(width_) << std::scientific << std::setprecision(precision_) << delta[0] / 1_m
+          << std::setw(width_) << std::scientific << std::setprecision(precision_) << delta[1] / 1_m
+          << std::setw(width_) << std::scientific << std::setprecision(precision_) << delta[2] / 1_m 
+          << std::setw(width_) << std::scientific << std::setprecision(precision_) << delta.getNorm() / 1_m
+          << '\n';
+    // clang-format on
+
+    return ProcessReturn::Ok;
+  }
+
+  template <typename TParticle, typename TTrack>
+  LengthType TrackWriter::getMaxStepLength(const TParticle&, const TTrack&) {
+    return meter * std::numeric_limits<double>::infinity();
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/modules/TrackingLine.inl b/corsika/detail/modules/TrackingLine.inl
new file mode 100644
index 0000000000000000000000000000000000000000..52200c42d839d85c21fb8d6676d36c418f03c9e7
--- /dev/null
+++ b/corsika/detail/modules/TrackingLine.inl
@@ -0,0 +1,59 @@
+/*
+ * (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 <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/QuantityVector.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/media/Environment.hpp>
+
+#include <limits>
+#include <stdexcept>
+#include <utility>
+
+namespace corsika::tracking_line {
+
+  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();
+    auto const vSqNorm =
+        v.getSquaredNorm(); // todo: get rid of this by having V0 normalized always
+    auto const R = sphere.getRadius();
+
+    auto const vDotDelta = v.dot(delta);
+    auto const discriminant =
+        vDotDelta * vDotDelta - vSqNorm * (delta.getSquaredNorm() - R * R);
+
+    if (discriminant.magnitude() > 0) {
+      auto const sqDisc = sqrt(discriminant);
+      auto const invDenom = 1 / vSqNorm;
+      return std::make_pair((-vDotDelta - sqDisc) * invDenom,
+                            (-vDotDelta + sqDisc) * invDenom);
+    } else {
+      return {};
+    }
+  }
+
+  TimeType getTimeOfIntersection(Line const& vLine, Plane const& vPlane) {
+
+    auto const delta = vPlane.getCenter() - vLine.getStartPoint();
+    auto const v = vLine.getVelocity();
+    auto const n = vPlane.getNormal();
+    auto const c = n.dot(v);
+
+    if (c.magnitude() == 0) {
+      return std::numeric_limits<TimeType::value_type>::infinity() * 1_s;
+    } else {
+      return n.dot(delta) / c;
+    }
+  }
+
+} // namespace corsika::tracking_line
diff --git a/corsika/detail/modules/conex/CONEXhybrid.inl b/corsika/detail/modules/conex/CONEXhybrid.inl
new file mode 100644
index 0000000000000000000000000000000000000000..f722440d304e20a75c83a73e975fe06c1b15b36e
--- /dev/null
+++ b/corsika/detail/modules/conex/CONEXhybrid.inl
@@ -0,0 +1,294 @@
+/*
+ * (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.
+ */
+
+#include <corsika/framework/core/Logging.hpp>
+#include <corsika/modules/conex/CONEXhybrid.hpp>
+#include <corsika/modules/conex/CONEX_f.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/core/PhysicalConstants.hpp>
+
+#include <conexConfig.h>
+
+#include <algorithm>
+#include <fstream>
+#include <iomanip>
+#include <utility>
+
+namespace corsika {
+
+  CONEXhybrid::CONEXhybrid(Point center, ShowerAxis const& showerAxis,
+                           LengthType groundDist, LengthType injectionHeight,
+                           HEPEnergyType primaryEnergy, PDGCode primaryPDG)
+      : center_{center}
+      , showerAxis_{showerAxis}
+      , groundDist_{groundDist}
+      , showerCore_{showerAxis_.getStart() + showerAxis_.getDirection() * groundDist_}
+      , conexObservationCS_{std::invoke([&]() {
+        auto const& c8cs = center.getCoordinateSystem();
+        auto const translation = showerCore_ - center;
+        auto const intermediateCS =
+            make_translation(c8cs, translation.getComponents(c8cs));
+        auto const transformCS = make_rotationToZ(intermediateCS, translation);
+
+        std::cout << "translation C8/CONEX obs: " << translation.getComponents()
+                  << std::endl;
+
+        /*
+        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;
+      })}
+      , x_sf_{std::invoke([&]() {
+        Vector<length_d> const a{conexObservationCS_, 0._m, 0._m, 1._m};
+        auto b = a.cross(showerAxis_.getDirection());
+        auto const lengthB = b.getNorm();
+        if (lengthB < 1e-10_m) {
+          b = Vector<length_d>{conexObservationCS_, 1_m, 0_m, 0_m};
+        }
+
+        return b.normalized();
+      })}
+      , 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;
+
+    std::cout << "y_sf (conexObservationCS): " << y_sf_.getComponents(conexObservationCS_)
+              << std::endl;
+    std::cout << "y_sf (C8): " << y_sf_.getComponents(center.getCoordinateSystem())
+              << std::endl;
+
+    std::cout << "showerAxisDirection (conexObservationCS): "
+              << showerAxis_.getDirection().getComponents(conexObservationCS_)
+              << std::endl;
+    std::cout << "showerAxisDirection (C8): "
+              << showerAxis_.getDirection().getComponents(center.getCoordinateSystem())
+              << std::endl;
+
+    std::cout << "showerCore (conexObservationCS): "
+              << showerCore_.getCoordinates(conexObservationCS_) << std::endl;
+    std::cout << "showerCore (C8): "
+              << showerCore_.getCoordinates(center.getCoordinateSystem()) << std::endl;
+
+    int randomSeeds[3] = {1234, 0, 0}; // will be overwritten later??
+    int heModel = eSibyll23;
+
+    int nShower = 1; // large to avoid final stats.
+    int maxDetail = 0;
+#ifdef CONEX_EXTENSIONS
+    int particleListMode = 0;
+#endif
+
+    std::string configPath = CONEX_CONFIG_PATH;
+    ::conex::initconex_(nShower, randomSeeds, heModel, maxDetail,
+#ifdef CONEX_EXTENSIONS
+                        particleListMode,
+#endif
+                        configPath.c_str(), configPath.size());
+
+    double eprima = primaryEnergy / 1_GeV;
+
+    // set phi, theta
+    Vector<length_d> ez{conexObservationCS_, {0._m, 0._m, -1_m}};
+    auto const c = showerAxis_.getDirection().dot(ez) / 1_m;
+    double theta = std::acos(c) * 180 / M_PI;
+
+    auto const showerAxisConex =
+        showerAxis_.getDirection().getComponents(conexObservationCS_);
+    double phi = std::atan2(-showerAxisConex.getY().magnitude(),
+                            showerAxisConex.getX().magnitude()) *
+                 180 / M_PI;
+
+    std::cout << "theta (deg) = " << theta << "; phi (deg) = " << phi << std::endl;
+
+    int ipart = static_cast<int>(primaryPDG);
+    auto rng = RNGManager::getInstance().getRandomStream("cascade");
+
+    double dimpact = 0.; // valid only if shower core is fixed on the observation plane;
+                         // for skimming showers an offset is needed like in CONEX
+
+    std::array<int, 3> ioseed{static_cast<int>(rng()), static_cast<int>(rng()),
+                              static_cast<int>(rng())};
+
+    double xminp = injectionHeight / 1_m;
+
+    ::conex::conexrun_(ipart, eprima, theta, phi, xminp, dimpact, ioseed.data());
+  }
+
+  void CONEXhybrid::doSecondaries(setup::StackView& vS) {
+    auto p = vS.begin();
+    while (p != vS.end()) {
+      Code const pid = p.getPID();
+      if (addParticle(pid, p.getEnergy(), p.getMass(), p.getPosition(),
+                      p.getMomentum().normalized(), p.getTime())) {
+        p.erase();
+      }
+      ++p;
+    }
+  }
+
+  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; });
+    if (it == egs_em_codes_.cend()) { return false; }
+
+    // EM particle
+    auto const egs_pid = it->second;
+    std::cout << "position conexObs: " << position.getCoordinates(conexObservationCS_)
+              << std::endl;
+
+    auto const coords = position.getCoordinates(conexObservationCS_) / 1_m;
+    double const x = coords[0].magnitude();
+    double const y = coords[1].magnitude();
+
+    double const altitude = ((position - center_).getNorm() - conex::earthRadius) / 1_m;
+    auto const d = position - showerCore_;
+
+    // distance from core to particle projected along shower axis
+    double const slantDistance = -d.dot(showerAxis_.getDirection()) / 1_m;
+
+    // lateral coordinates in CONEX shower frame
+    auto const dShowerPlane = d - d.getParallelProjectionOnto(showerAxis_.getDirection());
+    double const lateralX = dShowerPlane.dot(x_sf_) / 1_m;
+    double const lateralY = dShowerPlane.dot(y_sf_) / 1_m;
+
+    double const slantX = showerAxis_.getProjectedX(position) * (1_cm * 1_cm / 1_g);
+
+    double const time = (t * constants::c - groundDist_) / 1_m;
+
+    // fill u,v,w momentum direction in EGS frame
+    double const u = direction.dot(y_sf_).magnitude();
+    double const v = direction.dot(x_sf_).magnitude();
+    double const w = direction.dot(showerAxis_.getDirection()).magnitude();
+
+    double const weight = 1; // NEEDS TO BE CHANGED WHEN WE HAVE WEIGHTS!
+
+    // generation, TO BE CHANGED WHEN WE HAVE THAT INFORMATION AVAILABLE
+    int const latchin = 1;
+
+    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;
+
+    ::conex::cxoptl_.dptl[10 - 1] = egs_pid;
+    ::conex::cxoptl_.dptl[4 - 1] = E;
+    ::conex::cxoptl_.dptl[5 - 1] = m;
+    ::conex::cxoptl_.dptl[6 - 1] = x;
+    ::conex::cxoptl_.dptl[7 - 1] = y;
+    ::conex::cxoptl_.dptl[8 - 1] = altitude;
+    ::conex::cxoptl_.dptl[9 - 1] = time;
+    ::conex::cxoptl_.dptl[11 - 1] = weight;
+    ::conex::cxoptl_.dptl[12 - 1] = latchin;
+    ::conex::cxoptl_.dptl[13 - 1] = slantX;
+    ::conex::cxoptl_.dptl[14 - 1] = lateralX;
+    ::conex::cxoptl_.dptl[15 - 1] = lateralY;
+    ::conex::cxoptl_.dptl[16 - 1] = slantDistance;
+    ::conex::cxoptl_.dptl[2 - 1] = u;
+    ::conex::cxoptl_.dptl[1 - 1] = v;
+    ::conex::cxoptl_.dptl[3 - 1] = w;
+
+    int n = 1, i = 1;
+    ::conex::cegs4_(n, i);
+
+    return true;
+  }
+
+  void CONEXhybrid::solveCE() {
+
+    ::conex::conexcascade_();
+
+    int nX = ::conex::get_number_of_depth_bins_(); // make sure this works!
+
+    int icut = 1;
+    int icutg = 2;
+    int icute = 3;
+    int icutm = 2;
+    int icuth = 3;
+    int iSec = 0;
+
+    const int maxX = nX;
+
+    auto X = std::make_unique<float[]>(maxX);
+    auto H = std::make_unique<float[]>(maxX);
+    auto D = std::make_unique<float[]>(maxX);
+    auto N = std::make_unique<float[]>(maxX);
+    auto dEdX = std::make_unique<float[]>(maxX);
+    auto Mu = std::make_unique<float[]>(maxX);
+    auto dMu = std::make_unique<float[]>(maxX);
+    auto Gamma = std::make_unique<float[]>(maxX);
+    auto Electrons = std::make_unique<float[]>(maxX);
+    auto Hadrons = std::make_unique<float[]>(maxX);
+
+    float EGround[3], fitpars[13];
+
+    ::conex::get_shower_data_(icut, iSec, nX, X[0], N[0], fitpars[0], H[0], D[0]);
+    ::conex::get_shower_edep_(icut, nX, dEdX[0], EGround[0]);
+    ::conex::get_shower_muon_(icutm, nX, Mu[0], dMu[0]);
+    ::conex::get_shower_gamma_(icutg, nX, Gamma[0]);
+    ::conex::get_shower_electron_(icute, nX, Electrons[0]);
+    ::conex::get_shower_hadron_(icuth, nX, Hadrons[0]);
+
+    std::ofstream file{"conex_output.txt"};
+    file << fmt::format("#{:>8} {:>13} {:>13} {:>13} {:>13} {:>13} {:>13} {:>13}\n", "X",
+                        "N", "dEdX", "Mu", "dMu", "Gamma", "El", "Had");
+    for (int i = 0; i < nX; ++i) {
+      file << fmt::format(
+          " {:>8.2f} {:>13.3} {:>13.3} {:>13.3} {:>13.3} {:>13.3} {:>13.3} {:>13.3}\n",
+          X[i], N[i], dEdX[i], Mu[i], dMu[i], Gamma[i], Electrons[i], Hadrons[i]);
+    }
+    file.close();
+
+    std::ofstream fitout{"conex_fit.txt"};
+    fitout << fitpars[1 - 1] << " # log10(eprima/eV)" << std::endl;
+    fitout << fitpars[2 - 1] << " # theta" << std::endl;
+    fitout << fitpars[3 - 1] << " # X1 (first interaction)" << std::endl;
+    fitout << fitpars[4 - 1] << " # Nmax" << std::endl;
+    fitout << fitpars[5 - 1] << " # X0" << std::endl;
+    fitout << fitpars[6 - 1] << " # P1" << std::endl;
+    fitout << fitpars[7 - 1] << " # P2" << std::endl;
+    fitout << fitpars[8 - 1] << " # P3" << std::endl;
+    fitout << fitpars[9 - 1] << " # chi^2 / sqrt(Nmax)" << std::endl;
+    fitout << fitpars[10 - 1] << " # Xmax" << std::endl;
+    fitout << fitpars[11 - 1] << " # phi" << std::endl;
+    fitout << fitpars[12 - 1] << " # inelasticity 1st int." << std::endl;
+    fitout << fitpars[13 - 1] << " # ???" << std::endl;
+    fitout.close();
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/modules/energy_loss/BetheBlochPDG.inl b/corsika/detail/modules/energy_loss/BetheBlochPDG.inl
new file mode 100644
index 0000000000000000000000000000000000000000..fbde1729cf7964c153674c193bded83c416ce03b
--- /dev/null
+++ b/corsika/detail/modules/energy_loss/BetheBlochPDG.inl
@@ -0,0 +1,261 @@
+/*
+ * (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 <corsika/framework/core/ParticleProperties.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <cmath>
+#include <fstream>
+#include <limits>
+
+namespace corsika {
+
+  auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) {
+    return sqrt((Elab - m) * (Elab + m));
+  };
+
+  BetheBlochPDG::BetheBlochPDG(ShowerAxis const& shower_axis, HEPEnergyType emCut)
+      : dX_(10_g / square(1_cm)) // profile binning
+      , dX_threshold_(0.0001_g / square(1_cm))
+      , shower_axis_(shower_axis)
+      , emCut_(emCut)
+      , profile_(int(shower_axis.getMaximumX() / dX_) + 1) {}
+
+  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
+    // 7 nitrogen_gas 82.0 0.49976 D E 0.0011653 0.0 1.7378 4.1323 0.15349 3.2125 10.54
+    auto Ieff = 82.0_eV;
+    [[maybe_unused]] auto Zmat = 7;
+    auto ZoverA = 0.49976_mol / 1_g;
+    const double x0 = 1.7378;
+    const double x1 = 4.1323;
+    const double Cbar = 10.54;
+    const double delta0 = 0.0;
+    const double aa = 0.15349;
+    const double sk = 3.2125;
+    // end of material constants
+
+    // this is the Bethe-Bloch coefficiet 4pi N_A r_e^2 m_e c^2
+    auto constexpr K = 0.307075_MeV / 1_mol * square(1_cm);
+    HEPEnergyType const E = p.getEnergy();
+    HEPMassType const m = p.getMass();
+    double const gamma = E / m;
+    int const Z = p.getChargeNumber();
+    int const Z2 = Z * Z;
+    HEPMassType constexpr me = Electron::mass;
+    auto const m2 = m * m;
+    auto constexpr me2 = me * me;
+    double const gamma2 = gamma * gamma;
+
+    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);
+    [[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);
+
+    // 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{}=",
+                      p.getMomentum().getNorm() / m);
+    double const x = log10(p.getMomentum().getNorm() / m);
+    double delta = 0;
+    if (x >= x1) {
+      delta = 2 * (log(10)) * x - Cbar;
+    } else if (x < x1 && x >= x0) {
+      delta = 2 * (log(10)) * x - Cbar + aa * pow((x1 - x), sk);
+    } else if (x < x0) { // and IF conductor (otherwise, this is 0)
+      delta = delta0 * pow(100, 2 * (x - x0));
+    }
+    CORSIKA_LOG_DEBUG("BetheBloch delta={}", delta);
+
+    // with further low energies correction, accurary ~1% down to beta~0.05 (1MeV for p)
+
+    // shell correction, <~100MeV
+    // need more clarity about formulas and units
+    const double Cadj = 0;
+    /*
+    // https://www.nap.edu/read/20066/chapter/8#104
+    HEPEnergyType Iadj = 12_eV * Z + 7_eV;  // Iadj<163eV
+    if (Iadj>=163_eV)
+      Iadj = 9.76_eV * Z + 58.8_eV * pow(Z, -0.19);  // Iadj>=163eV
+    double const Cadj = (0.422377/eta2 + 0.0304043/(eta2*eta2) -
+    0.00038106/(eta2*eta2*eta2)) * 1e-6 * Iadj*Iadj + (3.858019/eta2 -
+    0.1667989/(eta2*eta2) + 0.00157955/(eta2*eta2*eta2)) * 1e-9 * Iadj*Iadj*Iadj;
+    */
+
+    // Barkas correction O(Z3) higher-order Born approximation
+    // see Appl. Phys. 85 (1999) 1249
+    // double A = 1;
+    // if (p.getPID() == Code::Nucleus) A = p.getNuclearA();
+    // double const Erel = (p.getEnergy()-p.getMass()) / A / 1_keV;
+    // double const Llow = 0.01 * Erel;
+    // double const Lhigh = 1.5/pow(Erel, 0.4) + 45000./Zmat * pow(Erel, 1.6);
+    // double const barkas = Z * Llow*Lhigh/(Llow+Lhigh); // RU, I think the Z was
+    // missing...
+    double const barkas = 1; // does not work yet
+
+    // Bloch correction for O(Z4) higher-order Born approximation
+    // see Appl. Phys. 85 (1999) 1249
+    const double alpha = 1. / 137.035999173;
+    double const y2 = Z * Z * alpha * alpha / beta2;
+    double const bloch = -y2 * (1.202 - y2 * (1.042 - 0.855 * y2 + 0.343 * y2 * y2));
+
+    double const aux = 2 * me * c2 * beta2 * gamma2 * Wmax / (Ieff * Ieff);
+    return -K * Z2 * ZoverA / beta2 *
+           (0.5 * log(aux) - beta2 - Cadj / Z - delta / 2 + barkas + bloch) * dX;
+  }
+
+  // radiation losses according to PDG 2018, ch. 33 ref. [5]
+  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) {
+    return getBetheBloch(vP, vDX) + getRadiationLosses(vP, vDX);
+  }
+
+  ProcessReturn BetheBlochPDG::doContinuous(setup::Stack::particle_type& p,
+                                            setup::Trajectory const& t) {
+    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(),
+                      p.getChargeNumber(), dX / 1_g * square(1_cm));
+    HEPEnergyType dE = getTotalEnergyLoss(p, dX);
+    auto E = p.getEnergy();
+    const auto Ekin = E - p.getMass();
+    auto Enew = E + dE;
+    CORSIKA_LOG_DEBUG("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);
+    fillProfile(t, dE);
+    return ProcessReturn::Ok;
+  }
+
+  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();
+
+    // 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;
+
+    return vParticle.getNode()->getModelProperties().getArclengthFromGrammage(
+        vTrack, maxGrammage);
+  }
+
+  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) {
+
+    GrammageType const grammageStart = shower_axis_.getProjectedX(vTrack.getPosition(0));
+    GrammageType const grammageEnd = shower_axis_.getProjectedX(vTrack.getPosition(1));
+    const auto deltaX = grammageEnd - grammageStart;
+
+    int binStart = grammageStart / dX_;
+    if (binStart < 0) return;
+    int 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,
+                      grammageEnd);
+
+    auto energyCount = HEPEnergyType::zero();
+
+    auto fill = [&](const int bin, const double weight) {
+      auto const increment = -dE * weight;
+      profile_[bin] += increment;
+      energyCount += increment;
+
+      CORSIKA_LOG_DEBUG("filling bin {} with weight {} : {} ", bin, weight, increment);
+    };
+
+    // fill longitudinal profile
+    if (binStart == binEnd) {
+      fill(binStart, 1);
+    } else {
+      fill(binStart, ((1 + binStart) * dX_ - grammageStart) / deltaX);
+      fill(binEnd, (grammageEnd - binEnd * dX_) / deltaX);
+      for (int bin = binStart + 1; bin < binEnd; ++bin) { fill(bin, 1); }
+    }
+
+    CORSIKA_LOG_DEBUG("total energy added to histogram: {} ", energyCount);
+  }
+
+  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";
+    double const deltaX = dX_ / 1_g * square(1_cm);
+    for (size_t i = 0; i < profile_.size(); ++i) {
+      file << std::scientific << std::setw(15) << i * deltaX << std::setw(15)
+           << profile_.at(i) / (deltaX * 1_GeV) << '\n';
+    }
+    file.close();
+  }
+
+  HEPEnergyType BetheBlochPDG::getTotal() const {
+    return std::accumulate(profile_.cbegin(), profile_.cend(), HEPEnergyType::zero());
+  }
+
+  void BetheBlochPDG::showResults() const {
+    CORSIKA_LOG_INFO(
+        " ******************************\n"
+        " PROCESS::ContinuousProcess: \n"
+        " energy lost dE (GeV)      : {}\n  ",
+        energy_lost_ / 1_GeV);
+  }
+
+  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
new file mode 100644
index 0000000000000000000000000000000000000000..16d419b0be200bb0d65b8748770818491d5e53b2
--- /dev/null
+++ b/corsika/detail/modules/proposal/ContinuousProcess.inl
@@ -0,0 +1,155 @@
+/*
+ * (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.
+ */
+
+#include <PROPOSAL/PROPOSAL.h>
+
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/modules/proposal/ContinuousProcess.hpp>
+#include <corsika/modules/proposal/Interaction.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/utility/COMBoost.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <corsika/setup/SetupEnvironment.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <iostream>
+
+namespace corsika::proposal {
+
+  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())
+      throw std::runtime_error("PROPOSAL could not find corresponding builder");
+
+    // interpolate the crosssection for given media and energy cut. These may
+    // take some minutes if you have to build the tables and cannot read the
+    // from disk
+    auto c = p_cross->second(media.at(comp.getHash()), emCut_);
+
+    // Build displacement integral and scattering object and interpolate them too and
+    // saved in the calc map by a key build out of a hash of composed of the component and
+    // particle code.
+    auto disp = PROPOSAL::make_displacement(c, true);
+    auto scatter =
+        PROPOSAL::make_scattering("highland", particle[code], media.at(comp.getHash()));
+    calc[std::make_pair(comp.getHash(), code)] =
+        std::make_tuple(std::move(disp), std::move(scatter));
+  }
+
+  template <>
+  ContinuousProcess::ContinuousProcess(setup::Environment const& _env,
+                                       HEPEnergyType _emCut)
+      : ProposalProcessBase(_env, _emCut) {}
+
+  template <>
+  void ContinuousProcess::scatter(setup::Stack::particle_type& vP,
+                                  HEPEnergyType const& loss,
+                                  GrammageType const& grammage) {
+
+    // get or build corresponding calculators
+    auto c = getCalculator(vP, calc);
+
+    // Cast corsika vector to proposal vector
+    auto vP_dir = vP.getDirection();
+    auto d = vP_dir.getComponents();
+    auto direction = PROPOSAL::Vector3D(d.getX().magnitude(), d.getY().magnitude(),
+                                        d.getZ().magnitude());
+
+    auto E_f = vP.getEnergy() - loss;
+
+    // draw random numbers required for scattering process
+    std::uniform_real_distribution<double> distr(0., 1.);
+    auto rnd = array<double, 4>();
+    for (auto& it : rnd) it = distr(RNG_);
+
+    // calculate deflection based on particle energy, loss
+    auto [mean_dir, final_dir] = get<eSCATTERING>(c->second)->Scatter(
+        grammage / 1_g * square(1_cm), vP.getEnergy() / 1_MeV, E_f / 1_MeV, direction,
+        rnd);
+
+    // TODO: neglect mean direction deflection because Trajectory is a const ref
+    (void)mean_dir;
+
+    // update particle direction after continuous loss caused by multiple
+    // scattering
+    auto vec = QuantityVector(final_dir.GetX() * E_f, final_dir.GetY() * E_f,
+                              final_dir.GetZ() * E_f);
+    vP.setMomentum(MomentumVector(vP_dir.getCoordinateSystem(), vec));
+  }
+
+  template <>
+  ProcessReturn ContinuousProcess::doContinuous(setup::Stack::particle_type& vP,
+                                                setup::Trajectory const& vT) {
+
+    if (!canInteract(vP.getPID())) return ProcessReturn::Ok;
+    if (vT.getLength() == 0_m) return ProcessReturn::Ok;
+
+    // calculate passed grammage
+    auto dX =
+        vP.getNode()->getModelProperties().getIntegratedGrammage(vT, vT.getLength());
+
+    // get or build corresponding track integral calculator and solve the
+    // integral
+    auto c = getCalculator(vP, calc);
+    auto final_energy = get<eDISPLACEMENT>(c->second)->UpperLimitTrackIntegral(
+                            vP.getEnergy() / 1_MeV, dX / 1_g * 1_cm * 1_cm) *
+                        1_MeV;
+    auto dE = vP.getEnergy() - final_energy;
+    energy_lost_ += dE;
+
+    // if the particle has a charge take multiple scattering into account
+    if (vP.getChargeNumber() != 0) scatter(vP, dE, dX);
+    vP.setEnergy(final_energy);
+    vP.setMomentum(vP.getMomentum() * vP.getEnergy() / vP.getMomentum().getNorm());
+    return ProcessReturn::Ok;
+  }
+
+  template <>
+  LengthType ContinuousProcess::getMaxStepLength(setup::Stack::particle_type const& vP,
+                                                 setup::Trajectory const& vT) {
+
+    if (!canInteract(vP.getPID())) 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.
+    //
+    // 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_);
+
+    // 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) *
+                    1_g / square(1_cm);
+
+    // return it in distance aequivalent
+    auto dist = vP.getNode()->getModelProperties().getArclengthFromGrammage(vT, grammage);
+    CORSIKA_LOG_TRACE("PROPOSAL::getMaxStepLength X={} g/cm2, l={} m ",
+                      grammage / 1_g * square(1_cm), dist / 1_m);
+    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;
+  }
+
+  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
new file mode 100644
index 0000000000000000000000000000000000000000..853302967fcc0bd460f6181f593254c9335b10bb
--- /dev/null
+++ b/corsika/detail/modules/proposal/Interaction.inl
@@ -0,0 +1,113 @@
+/*
+ * (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.
+ */
+
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/modules/proposal/Interaction.hpp>
+#include <corsika/framework/utility/COMBoost.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <corsika/setup/SetupEnvironment.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <limits>
+#include <memory>
+#include <random>
+#include <tuple>
+
+namespace corsika::proposal {
+
+  template <>
+  Interaction::Interaction(setup::Environment const& _env, HEPEnergyType _emCut)
+      : ProposalProcessBase(_env, _emCut) {}
+
+  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())
+      throw std::runtime_error("PROPOSAL could not find corresponding builder");
+
+    // interpolate the crosssection for given media and energy cut. These may
+    // take some minutes if you have to build the tables and cannot read the
+    // from disk
+    auto c = p_cross->second(media.at(comp.getHash()), emCut_);
+
+    // Look which interactions take place and build the corresponding
+    // interaction and secondarie builder. The interaction integral will
+    // interpolated too and saved in the calc map by a key build out of a hash
+    // of composed of the component and particle code.
+    auto inter_types = PROPOSAL::CrossSectionVector::GetInteractionTypes(c);
+    calc[std::make_pair(comp.getHash(), code)] = std::make_tuple(
+        PROPOSAL::make_secondaries(inter_types, particle[code], media.at(comp.getHash())),
+        PROPOSAL::make_interaction(c, true));
+  }
+
+  template <>
+  ProcessReturn Interaction::doInteraction(setup::StackView& view) {
+
+    auto const projectile = view.getProjectile();
+
+    if (canInteract(projectile.getPID())) {
+
+      // get or build corresponding calculators
+      auto c = getCalculator(projectile, calc);
+
+      // get the rates of the interaction types for every component.
+      std::uniform_real_distribution<double> distr(0., 1.);
+
+      // sample a interaction-type, loss and component
+      auto rates = get<eINTERACTION>(c->second)->Rates(projectile.getEnergy() / 1_MeV);
+      auto [type, comp_ptr, v] = get<eINTERACTION>(c->second)->SampleLoss(
+          projectile.getEnergy() / 1_MeV, rates, distr(RNG_));
+
+      // Read how much random numbers are required to calculate the secondaries.
+      // Calculate the secondaries and deploy them on the corsika stack.
+      auto rnd =
+          vector<double>(get<eSECONDARIES>(c->second)->RequiredRandomNumbers(type));
+      for (auto& it : rnd) it = distr(RNG_);
+      Point const& place = projectile.getPosition();
+      CoordinateSystemPtr const& labCS = place.getCoordinateSystem();
+
+      auto point = PROPOSAL::Vector3D(place.getX(labCS) / 1_cm, place.getY(labCS) / 1_cm,
+                                      place.getZ(labCS) / 1_cm);
+      auto projectile_dir = projectile.getDirection();
+      auto d = projectile_dir.getComponents(labCS);
+      auto direction = PROPOSAL::Vector3D(d.getX().magnitude(), d.getY().magnitude(),
+                                          d.getZ().magnitude());
+      auto loss = make_tuple(static_cast<int>(type), point, direction,
+                             v * projectile.getEnergy() / 1_MeV, 0.);
+      auto sec = get<eSECONDARIES>(c->second)->CalculateSecondaries(
+          projectile.getEnergy() / 1_MeV, loss, *comp_ptr, rnd);
+      for (auto& s : sec) {
+        auto E = get<PROPOSAL::Loss::ENERGY>(s) * 1_MeV;
+        auto vecProposal = get<PROPOSAL::Loss::DIRECTION>(s);
+        auto vec = QuantityVector(vecProposal.GetX() * E, vecProposal.GetY() * E,
+                                  vecProposal.GetZ() * E);
+        auto p = MomentumVector(labCS, vec);
+        auto sec_code =
+            convert_from_PDG(static_cast<PDGCode>(get<PROPOSAL::Loss::TYPE>(s)));
+        view.addSecondary(
+            make_tuple(sec_code, E, p, projectile.getPosition(), projectile.getTime()));
+      }
+    }
+    return ProcessReturn::Ok;
+  }
+
+  template <>
+  GrammageType Interaction::getInteractionLength(
+      setup::Stack::particle_type const& projectile) {
+
+    if (canInteract(projectile.getPID())) {
+      auto c = getCalculator(projectile, calc);
+      return get<eINTERACTION>(c->second)->MeanFreePath(projectile.getEnergy() / 1_MeV) *
+             1_g / (1_cm * 1_cm);
+    }
+    return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
+  }
+} // namespace corsika::proposal
diff --git a/corsika/detail/modules/proposal/ProposalProcessBase.inl b/corsika/detail/modules/proposal/ProposalProcessBase.inl
new file mode 100644
index 0000000000000000000000000000000000000000..b71b0fa9b1f9aff2784796588fb4ea7017a17d1f
--- /dev/null
+++ b/corsika/detail/modules/proposal/ProposalProcessBase.inl
@@ -0,0 +1,80 @@
+/*
+ * (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.
+ */
+
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/modules/proposal/ProposalProcessBase.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/utility/COMBoost.hpp>
+
+#include <corsika/setup/SetupEnvironment.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <cstdlib>
+#include <iostream>
+#include <limits>
+#include <memory>
+#include <random>
+#include <tuple>
+
+namespace corsika::proposal {
+
+  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,
+                                           HEPEnergyType _emCut)
+      : emCut_(_emCut)
+      , RNG_(RNGManager::getInstance().getRandomStream("proposal")) {
+    _env.getUniverse()->walk([&](auto& vtn) {
+      if (vtn.hasModelProperties()) {
+        const auto& prop = vtn.getModelProperties();
+        const auto& medium = mediumData(
+            prop.getMedium(Point(_env.getCoordinateSystem(), 0_cm, 0_cm, 0_cm)));
+
+        auto comp_vec = std::vector<PROPOSAL::Components::Component>();
+        const auto& comp = prop.getNuclearComposition();
+        auto frac_iter = comp.getFractions().cbegin();
+        for (auto& pcode : comp.getComponents()) {
+          comp_vec.emplace_back(std::string(get_name(pcode)), get_nucleus_Z(pcode),
+                                get_nucleus_A(pcode), *frac_iter);
+          ++frac_iter;
+        }
+
+        media[comp.getHash()] = PROPOSAL::Medium(
+            medium.getName(), medium.getIeff(), -medium.getCbar(), medium.getAA(),
+            medium.getSK(), medium.getX0(), medium.getX1(), medium.getDlt0(),
+            medium.getCorrectedDensity(), comp_vec);
+      }
+    });
+
+    PROPOSAL::InterpolationDef::order_of_interpolation = 2;
+    PROPOSAL::InterpolationDef::nodes_cross_section = 100;
+    PROPOSAL::InterpolationDef::nodes_propagate = 1000;
+
+    //! If corsika data exist store interpolation tables to the corresponding
+    //! path, otherwise interpolation tables would only stored in main memory if
+    //! no explicit intrpolation def is specified.
+    if (auto data_path = std::getenv("CORSIKA_DATA")) {
+      PROPOSAL::InterpolationDef::path_to_tables = std::string(data_path) + "/PROPOSAL";
+    } else {
+      throw std::runtime_error(
+          "It is not recommended to run PROPOSAL without its tables in "
+          "$CORSIKA_DATA/PROPOSAL. This would be extremely slow. Please provide the "
+          "table directory. ");
+    }
+  }
+
+  size_t ProposalProcessBase::hash::operator()(const calc_key_t& p) const noexcept {
+    return p.first ^ std::hash<Code>{}(p.second);
+  }
+
+} // namespace corsika::proposal
diff --git a/corsika/detail/modules/pythia8/Decay.inl b/corsika/detail/modules/pythia8/Decay.inl
new file mode 100644
index 0000000000000000000000000000000000000000..662afa95ae2ff806122a2a47c8387d36f4ee4fe0
--- /dev/null
+++ b/corsika/detail/modules/pythia8/Decay.inl
@@ -0,0 +1,249 @@
+/*
+ * (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.
+ */
+
+#include <corsika/modules/pythia8/Pythia8.hpp>
+#include <corsika/modules/pythia8/Decay.hpp>
+#include <corsika/modules/pythia8/Random.hpp>
+
+#include <corsika/framework/utility/COMBoost.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+namespace corsika::pythia8 {
+
+  Decay::Decay(const bool print_listing)
+      : print_listing_(print_listing) {
+    init();
+  }
+
+  Decay::Decay(std::set<Code> const& those)
+      : handleAllDecays_(false)
+      , handledDecays_(those) {
+    init();
+  }
+
+  Decay::~Decay() { CORSIKA_LOG_INFO("Pythia::Decay n={}", count_); }
+
+  void Decay::init() {
+
+    // run this only once during construction
+
+    // set random number generator in pythia
+    Pythia8::RndmEngine* rndm = new corsika::pythia8::Random();
+    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");
+
+    pythia_.readString("Print:quiet = on");
+    pythia_.readString("Check:particleData = 0");
+
+    /*
+       switching off event check in pythia is needed to allow decays that are off-shell
+       according to the mass definition in pythia.
+       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");
+
+    pythia_.readString("ProcessLevel:all = off");
+    pythia_.readString("ProcessLevel:resonanceDecays = off");
+
+    // making sure
+    setStable(Code::Pi0);
+
+    //    pythia_.particleData.readString("59:m0 = 101.00");
+
+    if (!pythia_.init())
+      throw std::runtime_error("Pythia::Decay: Initialization failed!");
+  }
+
+  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 ||
+        vParticleCode == Code::NuTau || vParticleCode == Code::NuEBar ||
+        vParticleCode == Code::NuMuBar || vParticleCode == Code::NuTauBar ||
+        vParticleCode == Code::Electron || vParticleCode == Code::Positron)
+      return false;
+    else if (canDecay(vParticleCode)) // non-zero for particles known to sibyll
+      return true;
+    else
+      return false;
+  }
+
+  void Decay::setHandleDecay(Code const vParticleCode) {
+    handleAllDecays_ = false;
+    CORSIKA_LOG_INFO("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) {
+    handleAllDecays_ = false;
+    for (auto p : vParticleList) setHandleDecay(p);
+  }
+
+  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) {
+    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);
+  }
+
+  void Decay::setStable(Code const pCode) {
+    CORSIKA_LOG_INFO("Pythia::Decay: setting {} stable..", pCode);
+    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)));
+  }
+
+  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);
+    return ans;
+  }
+
+  void Decay::printDecayConfig(const Code vCode) {
+    CORSIKA_LOG_INFO("Decay: Pythia decay configuration:");
+    CORSIKA_LOG_INFO(" {} is {} ", vCode, (isStable(vCode) ? "stable" : "unstable"));
+  }
+
+  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!");
+    else
+      for (auto& pCode : handledDecays_)
+        CORSIKA_LOG_INFO("Decay of {} is handled by Pythia!", pCode);
+  }
+
+  template <typename TParticle>
+  TimeType Decay::getLifetime(TParticle const& particle) {
+
+    const auto pid = particle.getPID();
+    if (canDecay(pid)) {
+      HEPEnergyType E = particle.getEnergy();
+      HEPMassType m = particle.getMass();
+
+      const double gamma = E / m;
+
+      const TimeType 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);
+
+      return lifetime;
+    } else
+      return std::numeric_limits<double>::infinity() * 1_s;
+  }
+
+  template <typename TView>
+  void Decay::doDecay(TView& view) {
+
+    auto projectile = view.getProjectile();
+
+    auto const& decayPoint = projectile.getPosition();
+    auto const t0 = projectile.getTime();
+
+    auto const& labMomentum = projectile.getMomentum();
+    CoordinateSystemPtr const& labCS = labMomentum.getCoordinateSystem();
+
+    // define target kinematics in lab frame
+    // define boost to and from CoM frame
+    // CoM frame definition in Pythia projectile: +z
+    COMBoost const boost(labMomentum, projectile.getMass());
+    auto const& rotatedCS = boost.getRotatedCS();
+
+    count_++;
+
+    // pythia stack
+    Pythia8::Event& event = pythia_.event;
+    event.reset();
+
+    auto const particleId = projectile.getPID();
+
+    // set particle unstable
+    Decay::setUnstable(particleId);
+
+    // input particle PDG
+    auto const pdgCode = static_cast<int>(get_PDG(particleId));
+
+    double constexpr px = 0;
+    double constexpr py = 0;
+    double constexpr pz = 0;
+    double const en = projectile.getMass() / 1_GeV;
+    double const m = en;
+
+    // add particle to pythia stack
+    event.append(pdgCode, 1, 0, 0, px, py, pz, en, m);
+
+    if (!pythia_.next())
+      throw std::runtime_error("Pythia::Decay: decay failed!");
+    else
+      CORSIKA_LOG_INFO("Pythia::Decay: particles after decay: {} ", event.size());
+
+    if (print_listing_) {
+      // list final state
+      event.list();
+    }
+
+    // loop over final state
+    for (int i = 0; i < event.size(); ++i)
+      if (event[i].isFinal()) {
+        auto const pyId = convert_from_PDG(static_cast<PDGCode>(event[i].id()));
+        HEPEnergyType const Erest = event[i].e() * 1_GeV;
+        MomentumVector const pRest(
+            rotatedCS,
+            {event[i].px() * 1_GeV, event[i].py() * 1_GeV, event[i].pz() * 1_GeV});
+        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());
+
+        view.addSecondary(std::make_tuple(pyId, fourMomLab.getTimeLikeComponent(),
+                                          fourMomLab.getSpaceLikeComponents(), decayPoint,
+                                          t0));
+      }
+
+    // set particle stable
+    Decay::setStable(particleId);
+  }
+} // namespace corsika::pythia8
diff --git a/corsika/detail/modules/pythia8/Interaction.inl b/corsika/detail/modules/pythia8/Interaction.inl
new file mode 100644
index 0000000000000000000000000000000000000000..61bee857ca971c7895ca8bfa182df5c3fb82871d
--- /dev/null
+++ b/corsika/detail/modules/pythia8/Interaction.inl
@@ -0,0 +1,373 @@
+/*
+ * (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 <corsika/modules/pythia8/Interaction.hpp>
+
+#include <corsika/framework/geometry/FourVector.hpp>
+#include <corsika/framework/utility/COMBoost.hpp>
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+
+#include <tuple>
+
+namespace corsika::pythia8 {
+
+  Interaction::~Interaction() {
+    std::cout << "Pythia::Interaction n=" << count_ << std::endl;
+  }
+
+  Interaction::Interaction(const bool print_listing)
+      : print_listing_(print_listing) {
+
+    std::cout << "Pythia::Interaction n=" << count_ << std::endl;
+
+    // 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;
+    }
+  }
+
+  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);
+  }
+
+  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);
+  }
+
+  void Interaction::configureLabFrameCollision(const Code BeamId, const Code TargetId,
+                                               const HEPEnergyType BeamEnergy) {
+    // Pythia configuration of the current event
+    // very clumsy. I am sure this can be done better..
+
+    // set beam
+    // beam id for pythia
+    auto const pdgBeam = static_cast<int>(get_PDG(BeamId));
+    std::stringstream stBeam;
+    stBeam << "Beams:idA = " << pdgBeam;
+    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());
+    // set frame to lab. frame
+    pythia_.readString("Beams:frameType = 2");
+    // set beam energy
+    const double Elab = BeamEnergy / 1_GeV;
+    std::stringstream stEnergy;
+    stEnergy << "Beams:eA = " << Elab;
+    pythia_.readString(stEnergy.str());
+    // target at rest
+    pythia_.readString("Beams:eB = 0.");
+    // initialize this config
+    pythia_.init();
+  }
+
+  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) {
+    // 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;
+
+        // calculate cross section
+        sigma_.calc(pdgCodeBeam, pdgCodeTarget, ecm);
+        if (sigma_.hasSigmaTot()) {
+          const double sigEla = sigma_.sigmaEl();
+          const double sigProd = sigma_.sigmaTot() - sigEla;
+
+          return std::make_tuple(sigProd * (1_fm * 1_fm), sigEla * (1_fm * 1_fm));
+
+        } else
+          throw std::runtime_error("pythia cross section init failed");
+
+      } else {
+        return std::make_tuple(std::numeric_limits<double>::infinity() * 1_mb,
+                               std::numeric_limits<double>::infinity() * 1_mb);
+      }
+    } else {
+      throw std::runtime_error("invalid target for pythia");
+    }
+  }
+
+  //  template <>
+  GrammageType Interaction::getInteractionLength(
+      corsika::setup::Stack::particle_type const& particle) {
+
+    // coordinate system, get global frame of reference
+    MomentumVector const& pMomentum = particle.getMomentum();
+    CoordinateSystemPtr const& labCS = pMomentum.getCoordinateSystem();
+
+    Code const corsikaBeamId = particle.getPID();
+
+    // beam particles for pythia : 1, 2, 3 for p, pi, k
+    // read from cross section code table
+    bool const kInteraction = canInteract(corsikaBeamId);
+
+    // FOR NOW: assume target is at rest
+    MomentumVector pTarget(labCS, {0_GeV, 0_GeV, 0_GeV});
+
+    // total momentum and energy
+    HEPEnergyType Elab = particle.getEnergy() + constants::nucleonMass;
+    MomentumVector pTotLab(labCS, {0_GeV, 0_GeV, 0_GeV});
+    pTotLab += pMomentum;
+    pTotLab += pTarget;
+    auto const pTotLabNorm = pTotLab.getNorm();
+    // calculate cm. energy
+    const HEPEnergyType 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;
+
+    // TODO: move limits into variables
+    if (kInteraction && Elab >= 8.5_GeV && isValidCoMEnergy(ECoM)) {
+
+      // get target from environment
+      /*
+        the target should be defined by the Environment,
+        ideally as full particle object so that the four momenta
+        and the boosts can be defined..
+      */
+      const auto* currentNode = particle.getNode();
+      const auto mediumComposition =
+          currentNode->getModelProperties().getNuclearComposition();
+      // determine average interaction length
+
+      auto const weightedProdCrossSection =
+          mediumComposition.getWeightedSum([=](auto vTargetID) {
+            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;
+
+      // 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;
+
+      return int_length;
+    }
+
+    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) {
+
+    auto projectile = view.getProjectile();
+
+    const auto corsikaBeamId = projectile.getPID();
+    std::cout << "Pythia::Interaction: "
+              << "DoInteraction: " << corsikaBeamId << " interaction? "
+              << corsika::pythia8::Interaction::canInteract(corsikaBeamId) << std::endl;
+
+    if (is_nucleus(corsikaBeamId)) {
+      // nuclei handled by different process, this should not happen
+      throw std::runtime_error("Nuclear projectile are not handled by PYTHIA!");
+    }
+
+    if (corsika::pythia8::Interaction::canInteract(corsikaBeamId)) {
+
+      // define projectile
+      HEPEnergyType const eProjectileLab = projectile.getEnergy();
+      auto const pProjectileLab = projectile.getMomentum();
+      CoordinateSystemPtr const& labCS = pProjectileLab.getCoordinateSystem();
+
+      // position and time of interaction, not used in Sibyll
+      Point pOrig = projectile.getPosition();
+      TimeType tOrig = projectile.getTime();
+
+      // 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);
+
+      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;
+
+      const FourVector PprojLab(eProjectileLab, pProjectileLab);
+
+      // define target kinematics in lab frame
+      // define boost to and from CoM frame
+      // CoM frame definition in Pythia projectile: +z
+      COMBoost const boost(PprojLab, constants::nucleonMass);
+
+      // just for show:
+      // boost projecticle
+      auto const PprojCoM = boost.toCoM(PprojLab);
+
+      // 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;
+
+      std::cout << "Interaction: position of interaction: " << pOrig.getCoordinates()
+                << std::endl;
+      std::cout << "Interaction: time: " << tOrig << std::endl;
+
+      HEPEnergyType Etot = eProjectileLab + eTargetLab;
+      MomentumVector Ptot = projectile.getMomentum();
+      // invariant mass, i.e. cm. energy
+      HEPEnergyType Ecm = sqrt(Etot * Etot - Ptot.getSquaredNorm());
+
+      // sample target mass number
+      const auto* currentNode = projectile.getNode();
+      const auto& mediumComposition =
+          currentNode->getModelProperties().getNuclearComposition();
+      // get cross sections for target materials
+      /*
+        Here we read the cross section from the interaction model again,
+        should be passed from getInteractionLength if possible
+       */
+      //#warning reading interaction cross section again, should not be necessary
+      auto const& compVec = mediumComposition.getComponents();
+      std::vector<si::CrossSectionType> cross_section_of_components(compVec.size());
+
+      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;
+        cross_section_of_components[i] = sigProd;
+      }
+
+      const auto corsikaTargetId =
+          mediumComposition.sampleTarget(cross_section_of_components, RNG_);
+      std::cout << "Interaction: target selected: " << corsikaTargetId << std::endl;
+
+      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;
+
+      if (eProjectileLab < 8.5_GeV || !isValidCoMEnergy(Ecm)) {
+        std::cout << "Interaction: "
+                  << " DoInteraction: should have dropped particle.. "
+                  << "THIS IS AN ERROR" << std::endl;
+        throw std::runtime_error("energy too low for PYTHIA");
+
+      } else {
+        count_++;
+
+        configureLabFrameCollision(corsikaBeamId, corsikaTargetId, eProjectileLab);
+
+        // create event in pytia
+        if (!pythia_.next()) throw std::runtime_error("Pythia::DoInteraction: failed!");
+
+        // link to pythia stack
+        Pythia8::Event& event = pythia_.event;
+
+        if (print_listing_) {
+          // print final state
+          event.list();
+        }
+
+        MomentumVector Plab_final(labCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
+        HEPEnergyType Elab_final = 0_GeV;
+        for (int i = 0; i < event.size(); ++i) {
+          Pythia8::Particle& p8p = event[i];
+          // skip particles that have decayed in pythia
+          if (!p8p.isFinal()) continue;
+
+          auto const pyId = convert_from_PDG(static_cast<PDGCode>(p8p.id()));
+
+          const MomentumVector pyPlab(
+              labCS, {p8p.px() * 1_GeV, p8p.py() * 1_GeV, p8p.pz() * 1_GeV});
+          HEPEnergyType const pyEn = p8p.e() * 1_GeV;
+
+          // add to corsika stack
+          auto pnew =
+              projectile.addSecondary(std::make_tuple(pyId, pyEn, pyPlab, pOrig, tOrig));
+
+          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;
+      }
+    }
+  }
+
+} // namespace corsika::pythia8
diff --git a/corsika/detail/modules/pythia8/Random.inl b/corsika/detail/modules/pythia8/Random.inl
new file mode 100644
index 0000000000000000000000000000000000000000..c077e0bf732474bdfef413fac35885294f8b14f8
--- /dev/null
+++ b/corsika/detail/modules/pythia8/Random.inl
@@ -0,0 +1,17 @@
+/*
+ * (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 <corsika/modules/pythia8/Random.hpp>
+
+namespace corsika::pythia8 {
+
+  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
new file mode 100644
index 0000000000000000000000000000000000000000..afdafc99f528137d4bf8baa2b06c48594c1f478e
--- /dev/null
+++ b/corsika/detail/modules/qgsjetII/Interaction.inl
@@ -0,0 +1,394 @@
+/*
+ * (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.
+ */
+
+#include <corsika/modules/qgsjetII/Interaction.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/framework/geometry/QuantityVector.hpp>
+#include <corsika/framework/geometry/FourVector.hpp>
+#include <corsika/modules/qgsjetII/ParticleConversion.hpp>
+#include <corsika/modules/qgsjetII/QGSJetIIFragmentsStack.hpp>
+#include <corsika/modules/qgsjetII/QGSJetIIStack.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+#include <corsika/framework/utility/COMBoost.hpp>
+
+#include <sstream>
+#include <string>
+#include <tuple>
+
+#include <qgsjet-II-04.hpp>
+
+namespace corsika::qgsjetII {
+
+  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;
+      }
+    }
+
+    // initialize QgsjetII
+    static bool initialized = false;
+    if (!initialized) {
+      qgset_();
+      datadir DIR(data_path_);
+      qgaini_(DIR.data);
+      initialized = true;
+    }
+  }
+
+  Interaction::~Interaction() {
+    std::cout << "QgsjetII::Interaction n=" << count_ << std::endl;
+  }
+
+  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)) {
+
+      int const iBeam = static_cast<QgsjetIIXSClassIntType>(
+          corsika::qgsjetII::getQgsjetIIXSCode(beamId));
+      int iTarget = 1;
+      if (is_nucleus(targetId)) {
+        iTarget = targetA;
+        if (iTarget > maxMassNumber_ || iTarget <= 0) {
+          std::ostringstream txt;
+          txt << "QgsjetII target outside range. iTarget=" << iTarget;
+          throw std::runtime_error(txt.str().c_str());
+        }
+      }
+      int iProjectile = 1;
+      if (is_nucleus(beamId)) {
+        iProjectile = Abeam;
+        if (iProjectile > maxMassNumber_ || iProjectile <= 0)
+          throw std::runtime_error("QgsjetII target outside range. ");
+      }
+
+      std::cout << "QgsjetII::getCrossSection Elab=" << Elab << " iBeam=" << iBeam
+                << " iProjectile=" << iProjectile << " iTarget=" << iTarget << std::endl;
+      sigProd = qgsect_(Elab / 1_GeV, iBeam, iProjectile, iTarget);
+      std::cout << "QgsjetII::getCrossSection sigProd=" << sigProd << std::endl;
+    }
+
+    return sigProd * 1_mb;
+  }
+
+  template <typename TParticle>
+  GrammageType Interaction::getInteractionLength(const TParticle& vP) const {
+
+    // coordinate system, get global frame of reference
+    CoordinateSystemPtr const& rootCS = get_root_CoordinateSystem();
+
+    const Code corsikaBeamId = vP.getPID();
+
+    // beam particles for qgsjetII : 1, 2, 3 for p, pi, k
+    // read from cross section code table
+    const bool kInteraction = corsika::qgsjetII::canInteract(corsikaBeamId);
+
+    // FOR NOW: assume target is at rest
+    MomentumVector pTarget(rootCS, {0_GeV, 0_GeV, 0_GeV});
+
+    // 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;
+
+    if (kInteraction) {
+
+      int Abeam = 0;
+      if (is_nucleus(vP.getPID())) Abeam = vP.getNuclearA();
+
+      // get target from environment
+      /*
+        the target should be defined by the Environment,
+        ideally as full particle object so that the four momenta
+        and the boosts can be defined..
+      */
+
+      auto const* currentNode = vP.getNode();
+      const auto& mediumComposition =
+          currentNode->getModelProperties().getNuclearComposition();
+
+      CrossSectionType weightedProdCrossSection =
+          mediumComposition.getWeightedSum([=](Code targetID) -> CrossSectionType {
+            int targetA = 0;
+            if (is_nucleus(targetID)) targetA = get_nucleus_A(targetID);
+            return getCrossSection(corsikaBeamId, targetID, Elab, Abeam, targetA);
+          });
+
+      std::cout << "Interaction: "
+                << "IntLength: weighted CrossSection (mb): "
+                << weightedProdCrossSection / 1_mb << std::endl;
+
+      // 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;
+
+      return int_length;
+    }
+
+    return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
+  }
+
+  /**
+     In this function QGSJETII is called to produce one event. The
+     event is copied (and boosted) into the shower lab frame.
+   */
+
+  template <typename TView>
+  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;
+
+    if (corsika::qgsjetII::canInteract(corsikaBeamId)) {
+
+      CoordinateSystemPtr const& rootCS = get_root_CoordinateSystem();
+
+      // position and time of interaction, not used in QgsjetII
+      Point pOrig = projectile.getPosition();
+      TimeType tOrig = projectile.getTime();
+
+      // define target
+      // for QgsjetII is always a single nucleon
+      // FOR NOW: target is always at rest
+      auto const targetEnergyLab = 0_GeV + constants::nucleonMass;
+      auto const targetMomentumLab = MomentumVector(rootCS, 0_GeV, 0_GeV, 0_GeV);
+      FourVector const PtargLab(targetEnergyLab, targetMomentumLab);
+
+      // define projectile
+      HEPEnergyType const projectileEnergyLab = projectile.getEnergy();
+      auto const projectileMomentumLab = projectile.getMomentum();
+
+      int beamA = 0;
+      if (is_nucleus(corsikaBeamId)) beamA = projectile.getNuclearA();
+
+      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;
+
+      // sample target mass number
+      auto const* currentNode = projectile.getNode();
+      auto const& mediumComposition =
+          currentNode->getModelProperties().getNuclearComposition();
+      // get cross sections for target materials
+      /*
+        Here we read the cross section from the interaction model again,
+        should be passed from getInteractionLength if possible
+       */
+      auto const& compVec = mediumComposition.getComponents();
+      std::vector<CrossSectionType> cross_section_of_components(compVec.size());
+
+      for (size_t i = 0; i < compVec.size(); ++i) {
+        auto const targetId = compVec[i];
+        int targetA = 0;
+        if (is_nucleus(targetId)) targetA = get_nucleus_A(targetId);
+        const auto sigProd =
+            getCrossSection(corsikaBeamId, targetId, projectileEnergyLab, beamA, targetA);
+        cross_section_of_components[i] = sigProd;
+      }
+
+      const auto targetCode =
+          mediumComposition.sampleTarget(cross_section_of_components, rng_);
+      std::cout << "Interaction: target selected: " << targetCode << std::endl;
+
+      int targetMassNumber = 1;     // proton
+      if (is_nucleus(targetCode)) { // nucleus
+        targetMassNumber = get_nucleus_A(targetCode);
+        if (targetMassNumber > maxMassNumber_)
+          throw std::runtime_error("QgsjetII target mass outside range.");
+      } else {
+        if (targetCode != Proton::code)
+          throw std::runtime_error("QgsjetII Taget not possible.");
+      }
+      std::cout << "Interaction: target qgsjetII code/A: " << targetMassNumber
+                << std::endl;
+
+      int projectileMassNumber = 1; // "1" means "hadron"
+      QgsjetIIHadronType qgsjet_hadron_type =
+          qgsjetII::getQgsjetIIHadronType(corsikaBeamId);
+      if (qgsjet_hadron_type == QgsjetIIHadronType::NucleusType) {
+        projectileMassNumber = projectile.getNuclearA();
+        if (projectileMassNumber > maxMassNumber_)
+          throw std::runtime_error("QgsjetII projectile mass outside range.");
+        std::array<QgsjetIIHadronType, 2> constexpr nucleons = {
+            QgsjetIIHadronType::ProtonType, QgsjetIIHadronType::NeutronType};
+        std::uniform_int_distribution select(0, 1);
+        qgsjet_hadron_type = nucleons[select(rng_)];
+      } else {
+        // from conex: replace pi0 or rho0 with pi+/pi- in alternating sequence
+        if (qgsjet_hadron_type == QgsjetIIHadronType::NeutralLightMesonType) {
+          qgsjet_hadron_type = alternate_;
+          alternate_ = (alternate_ == QgsjetIIHadronType::PiPlusType
+                            ? QgsjetIIHadronType::PiMinusType
+                            : QgsjetIIHadronType::PiPlusType);
+        }
+      }
+
+      // beam id for qgsjetII
+      int kBeam = 2; // default: proton Shouldn't we randomize neutron/proton for nuclei?
+      if (corsikaBeamId != Code::Nucleus) {
+        kBeam = corsika::qgsjetII::convertToQgsjetIIRaw(corsikaBeamId);
+        // from conex
+        if (kBeam == 0) { // replace pi0 or rho0 with pi+/pi-
+          static int select = 1;
+          kBeam = select;
+          select *= -1;
+        }
+        // replace lambda by neutron
+        if (kBeam == 6)
+          kBeam = 3;
+        else if (kBeam == -6)
+          kBeam = -3;
+        // else if (abs(kBeam)>6) -> throw
+      }
+
+      std::cout << "Interaction: "
+                << " DoInteraction: E(GeV):" << projectileEnergyLab / 1_GeV << std::endl;
+      count_++;
+      int qgsjet_hadron_type_int = static_cast<QgsjetIICodeIntType>(qgsjet_hadron_type);
+      qgini_(projectileEnergyLab / 1_GeV, qgsjet_hadron_type_int, projectileMassNumber,
+             targetMassNumber);
+      qgconf_();
+
+      // bookkeeping
+      MomentumVector Plab_final(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
+      HEPEnergyType Elab_final = 0_GeV;
+
+      // to read the secondaries
+      // define rotation to and from CoM frame
+      // CoM frame definition in QgsjetII projectile: +z
+      auto const& originalCS = projectileMomentumLab.getCoordinateSystem();
+      CoordinateSystemPtr const zAxisFrame =
+          make_rotationToZ(originalCS, projectileMomentumLab);
+
+      // fragments
+      QGSJetIIFragmentsStack qfs;
+      for (auto& fragm : qfs) {
+        Code idFragm = Code::Nucleus;
+        int A = fragm.getFragmentSize();
+        int Z = 0;
+        switch (A) {
+          case 1: { // proton/neutron
+            std::uniform_real_distribution<double> select;
+            if (select(rng_) > 0.5) {
+              idFragm = Code::Proton;
+              Z = 1;
+            } else {
+              idFragm = Code::Neutron;
+              Z = 0;
+            }
+
+            const HEPMassType nucleonMass = get_mass(idFragm);
+
+            auto momentum = Vector(
+                zAxisFrame, corsika::QuantityVector<hepmomentum_d>{
+                                0.0_GeV, 0.0_GeV,
+                                sqrt((projectileEnergyLabPerNucleon + nucleonMass) *
+                                     (projectileEnergyLabPerNucleon - nucleonMass))});
+
+            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;
+            auto pnew = view.addSecondary(
+                std::make_tuple(idFragm, energy, momentum, pOrig, tOrig));
+            Plab_final += pnew.getMomentum();
+            Elab_final += pnew.getEnergy();
+          } break;
+          case 2: // deuterium
+            Z = 1;
+            break;
+          case 3: // tritium
+            Z = 1;
+            break;
+          case 4: // helium
+            Z = 2;
+            break;
+          default: // nucleus
+          {
+            Z = int(A / 2.15 + 0.7);
+          }
+        }
+
+        if (idFragm == Code::Nucleus) { // thus: not p or n
+          const HEPMassType nucleusMass = Proton::mass * Z + Neutron::mass * (A - Z);
+          auto momentum = Vector(
+              zAxisFrame, QuantityVector<hepmomentum_d>{
+                              0.0_GeV, 0.0_GeV,
+                              sqrt((projectileEnergyLabPerNucleon * A + nucleusMass) *
+                                   (projectileEnergyLabPerNucleon * A - nucleusMass))});
+
+          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;
+          auto pnew = view.addSecondary(
+              std::make_tuple(idFragm, energy, momentum, pOrig, tOrig, A, Z));
+          Plab_final += pnew.getMomentum();
+          Elab_final += pnew.getEnergy();
+        }
+      }
+
+      // secondaries
+      QGSJetIIStack qs;
+      for (auto& psec : qs) {
+
+        auto momentum = psec.getMomentum(zAxisFrame);
+        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;
+        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;
+    }
+  }
+
+} // namespace corsika::qgsjetII
diff --git a/Processes/QGSJetII/ParticleConversion.cc b/corsika/detail/modules/qgsjetII/ParticleConversion.inl
similarity index 62%
rename from Processes/QGSJetII/ParticleConversion.cc
rename to corsika/detail/modules/qgsjetII/ParticleConversion.inl
index c8057e9ad7c7677aebf6e96f1e3e89e998f62ad1..291bc7b873986dec5c7ba5ea573211b578918a26 100644
--- a/Processes/QGSJetII/ParticleConversion.cc
+++ b/corsika/detail/modules/qgsjetII/ParticleConversion.inl
@@ -6,7 +6,7 @@
  * the license.
  */
 
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/process/qgsjetII/ParticleConversion.h>
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/modules/qgsjetII/ParticleConversion.hpp>
 
-using namespace corsika::process::qgsjetII;
+using namespace corsika::qgsjetII;
diff --git a/corsika/detail/modules/qgsjetII/QGSJetIIStack.inl b/corsika/detail/modules/qgsjetII/QGSJetIIStack.inl
new file mode 100644
index 0000000000000000000000000000000000000000..60f86fb48130ba5df29da5d27c5741baa80c1c47
--- /dev/null
+++ b/corsika/detail/modules/qgsjetII/QGSJetIIStack.inl
@@ -0,0 +1,113 @@
+/*
+ * (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
+
+namespace corsika::qgsjetII {
+
+  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; }
+
+  void QGSJetIIStackData::setId(const unsigned int i, const int v) {
+    qgarr14_.ich[i] = v;
+  }
+  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) {
+    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 {
+    return qgarr14_.esp[i][0] * 1_GeV;
+  }
+  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) {
+    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) {
+    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() {
+    if (qgarr12_.nsp > 0) { qgarr12_.nsp--; }
+  }
+
+  template <typename StackIteratorInterface>
+  void ParticleInterface<StackIteratorInterface>::setParticleData(
+      const int vID, const HEPEnergyType vE, const MomentumVector& vP,
+      const HEPMassType) {
+    setPID(vID);
+    setEnergy(vE);
+    setMomentum(vP);
+  }
+
+  template <typename StackIteratorInterface>
+  void ParticleInterface<StackIteratorInterface>::setParticleData(
+      ParticleInterface<StackIteratorInterface>& /*parent*/, const int vID,
+      const HEPEnergyType vE, const MomentumVector& vP, const HEPMassType) {
+    setPID(vID);
+    setEnergy(vE);
+    setMomentum(vP);
+  }
+
+  template <typename StackIteratorInterface>
+  void ParticleInterface<StackIteratorInterface>::setEnergy(const HEPEnergyType v) {
+    getStackData().setEnergy(getIndex(), v);
+  }
+
+  template <typename StackIteratorInterface>
+  HEPEnergyType ParticleInterface<StackIteratorInterface>::getEnergy() const {
+    return getStackData().getEnergy(getIndex());
+  }
+
+  template <typename StackIteratorInterface>
+  void ParticleInterface<StackIteratorInterface>::setPID(const int v) {
+    getStackData().setId(getIndex(), v);
+  }
+
+  template <typename StackIteratorInterface>
+  corsika::qgsjetII::QgsjetIICode ParticleInterface<StackIteratorInterface>::getPID()
+      const {
+    return static_cast<corsika::qgsjetII::QgsjetIICode>(getStackData().getId(getIndex()));
+  }
+
+  template <typename StackIteratorInterface>
+  MomentumVector ParticleInterface<StackIteratorInterface>::getMomentum(
+      const CoordinateSystemPtr& CS) const {
+    return getStackData().getMomentum(getIndex(), CS);
+  }
+
+  template <typename StackIteratorInterface>
+  void ParticleInterface<StackIteratorInterface>::setMomentum(const MomentumVector& v) {
+    getStackData().setMomentum(getIndex(), v);
+  }
+
+} // namespace corsika::qgsjetII
diff --git a/Processes/QGSJetII/qgsjet-II-04.cc b/corsika/detail/modules/qgsjetII/qgsjet-II-04.inl
similarity index 67%
rename from Processes/QGSJetII/qgsjet-II-04.cc
rename to corsika/detail/modules/qgsjetII/qgsjet-II-04.inl
index a318d0a6cb774057203d34a93c977f290c70362b..130ac0173920a78bdd774914c5f285a90caa51ff 100644
--- a/Processes/QGSJetII/qgsjet-II-04.cc
+++ b/corsika/detail/modules/qgsjetII/qgsjet-II-04.inl
@@ -6,9 +6,11 @@
  * the license.
  */
 
-#include <corsika/process/qgsjetII/qgsjet-II-04.h>
+#pragma once
 
-#include <corsika/random/RNGManager.h>
+#include <corsika/modules/qgsjetII/qgsjet-II-04.hpp>
+
+#include <corsika/framework/random/RNGManager.hpp>
 
 #include <iostream>
 #include <random>
@@ -25,9 +27,13 @@ datadir::datadir(const std::string& dir) {
 }
 
 double qgran_(int&) {
-  static corsika::random::RNG& rng =
-      corsika::random::RNGManager::GetInstance().GetRandomStream("qgsjet");
+  static corsika::default_prng_type& rng =
+      corsika::RNGManager::getInstance().GetRandomStream("qgran");
 
   std::uniform_real_distribution<double> dist;
   return dist(rng);
 }
+
+void lzmaopenfile_(const char*, int) {}
+void lzmaclosefile_() {}
+void lzmafillarray_(const double&, const int&) {}
diff --git a/corsika/detail/modules/sibyll/Decay.inl b/corsika/detail/modules/sibyll/Decay.inl
new file mode 100644
index 0000000000000000000000000000000000000000..5ca2edb854bc24aaf32b04a9effeaae7c35b98cf
--- /dev/null
+++ b/corsika/detail/modules/sibyll/Decay.inl
@@ -0,0 +1,225 @@
+/*
+ * (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/modules/sibyll/Decay.hpp>
+#include <corsika/modules/sibyll/ParticleConversion.hpp>
+#include <corsika/modules/sibyll/SibStack.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <iostream>
+#include <vector>
+
+using SetupView = corsika::setup::StackView;
+using SetupProjectile = corsika::setup::StackView::particle_type;
+using SetupParticle = corsika::setup::Stack::particle_type;
+
+namespace corsika::sibyll {
+
+  inline Decay::Decay(const bool sibyll_printout_on)
+      : sibyll_listing_(sibyll_printout_on) {
+    // switch off decays to avoid internal decay chains
+    setAllStable();
+    // handle all decays by default
+    handleAllDecays_ = true;
+  }
+
+  inline Decay::Decay(std::set<Code> const& vHandled)
+      : handleAllDecays_(false)
+      , handledDecays_(vHandled) {
+    setAllStable();
+  }
+
+  inline Decay::~Decay() { CORSIKA_LOG_TRACE("Sibyll::Decay n={}", count_); }
+
+  inline bool Decay::canHandleDecay(const Code vParticleCode) {
+    // if known to sibyll and not proton or neutrino it can decay
+    if (vParticleCode == Code::Proton || vParticleCode == Code::AntiProton ||
+        vParticleCode == Code::NuE || vParticleCode == Code::NuMu ||
+        vParticleCode == Code::NuTau || vParticleCode == Code::NuEBar ||
+        vParticleCode == Code::NuMuBar || vParticleCode == Code::NuTauBar ||
+        vParticleCode == Code::Electron || vParticleCode == Code::Positron)
+      return false;
+    else if (corsika::sibyll::convertToSibyllRaw(
+                 vParticleCode)) // non-zero for particles known to sibyll
+      return true;
+    else
+      return false;
+  }
+
+  inline void Decay::setHandleDecay(const Code vParticleCode) {
+    handleAllDecays_ = false;
+    CORSIKA_LOG_DEBUG("Sibyll::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 sibyll!");
+  }
+
+  inline void Decay::setHandleDecay(std::vector<Code> const& vParticleList) {
+    handleAllDecays_ = false;
+    for (auto p : vParticleList) Decay::setHandleDecay(p);
+  }
+
+  inline bool Decay::isDecayHandled(corsika::Code const vParticleCode) {
+    if (handleAllDecays_ && Decay::canHandleDecay(vParticleCode))
+      return true;
+    else
+      return Decay::handledDecays_.find(vParticleCode) != Decay::handledDecays_.end()
+                 ? true
+                 : false;
+  }
+
+  inline void Decay::setStable(std::vector<Code> const& vParticleList) {
+    for (auto p : vParticleList) Decay::setStable(p);
+  }
+
+  inline void Decay::setUnstable(std::vector<Code> const& vParticleList) {
+    for (auto p : vParticleList) Decay::setUnstable(p);
+  }
+
+  inline bool Decay::isStable(Code const vCode) {
+    return abs(sibyll::convertToSibyllRaw(vCode)) <= 0 ? true : false;
+  }
+
+  inline bool Decay::isUnstable(Code const vCode) {
+    return abs(sibyll::convertToSibyllRaw(vCode)) > 0 ? true : false;
+  }
+
+  inline void Decay::setDecay(const Code vCode, const bool vMakeUnstable) {
+    vMakeUnstable ? setUnstable(vCode) : setStable(vCode);
+  }
+
+  inline void Decay::setUnstable(Code const vCode) {
+    CORSIKA_LOG_DEBUG("Sibyll::Decay: setting {} unstable. ", vCode);
+
+    const int s_id = abs(sibyll::convertToSibyllRaw(vCode));
+    s_csydec_.idb[s_id - 1] = abs(s_csydec_.idb[s_id - 1]);
+  }
+
+  inline void Decay::setStable(Code const vCode) {
+    CORSIKA_LOG_DEBUG("Sibyll::Decay: setting {} stable. ", vCode);
+
+    const int s_id = abs(sibyll::convertToSibyllRaw(vCode));
+    s_csydec_.idb[s_id - 1] = (-1) * abs(s_csydec_.idb[s_id - 1]);
+  }
+
+  inline void Decay::setAllStable() {
+    for (int i = 0; i < 99; ++i) s_csydec_.idb[i] = -1 * abs(s_csydec_.idb[i]);
+  }
+
+  inline void Decay::setAllUnstable() {
+    for (int i = 0; i < 99; ++i) s_csydec_.idb[i] = abs(s_csydec_.idb[i]);
+  }
+
+  inline void Decay::printDecayConfig([[maybe_unused]] const Code vCode) {
+    [[maybe_unused]] const int sibCode = corsika::sibyll::convertToSibyllRaw(vCode);
+    [[maybe_unused]] const int absSibCode = abs(sibCode);
+    CORSIKA_LOG_DEBUG("Decay: Sibyll decay configuration: {} is {}", vCode,
+                      (s_csydec_.idb[absSibCode - 1] <= 0) ? "stable" : "unstable");
+  }
+  inline void Decay::printDecayConfig() {
+    CORSIKA_LOG_DEBUG("Sibyll::Decay: decay configuration:");
+    if (handleAllDecays_) {
+      CORSIKA_LOG_DEBUG(
+          "     all particles known to Sibyll are handled by Sibyll::Decay!");
+
+    } else {
+      for ([[maybe_unused]] auto& pCode : handledDecays_) {
+        CORSIKA_LOG_DEBUG("      Decay of {}  is handled by Sibyll!", pCode);
+      }
+    }
+  }
+
+  template <typename TParticle>
+  inline TimeType Decay::getLifetime(TParticle const& projectile) {
+
+    const Code pid = projectile.getPID();
+    if (Decay::isDecayHandled(pid)) {
+      HEPEnergyType E = projectile.getEnergy();
+      HEPMassType m = projectile.getMass();
+      const double gamma = E / m;
+      const TimeType t0 = get_lifetime(projectile.getPID());
+      auto const lifetime = gamma * t0;
+      [[maybe_unused]] const auto mkin =
+          +(E * E - projectile.getMomentum()
+                        .getSquaredNorm()); // delta_mass(projectile.getMomentum(), E, m);
+      CORSIKA_LOG_DEBUG("Sibyll::Decay: code: {} ", projectile.getPID());
+      CORSIKA_LOG_DEBUG("Sibyll::Decay: MinStep: t0: {} ", t0);
+      CORSIKA_LOG_DEBUG("Sibyll::Decay: MinStep: energy: {} GeV ", E / 1_GeV);
+      CORSIKA_LOG_DEBUG("Sibyll::Decay: momentum: {} GeV ",
+                        projectile.getMomentum().getComponents() / 1_GeV);
+      CORSIKA_LOG_DEBUG("Sibyll::Decay: momentum: shell mass-kin. inv. mass {} {}",
+                        mkin / 1_GeV / 1_GeV, m / 1_GeV * m / 1_GeV);
+      [[maybe_unused]] auto sib_id =
+          corsika::sibyll::convertToSibyllRaw(projectile.getPID());
+      CORSIKA_LOG_DEBUG("Sibyll::Decay: sib mass: {}", get_sibyll_mass2(sib_id));
+      CORSIKA_LOG_DEBUG("Sibyll::Decay: MinStep: gamma:  {}", gamma);
+      CORSIKA_LOG_DEBUG("Sibyll::Decay: MinStep: tau {} s: ", lifetime / 1_s);
+      return lifetime;
+    }
+    return std::numeric_limits<double>::infinity() * 1_s;
+  } // namespace corsika::sibyll
+
+  template <typename TSecondaryView>
+  inline void Decay::doDecay(TSecondaryView& view) {
+
+    auto projectile = view.getProjectile();
+    const Code pCode = projectile.getPID();
+    // check if sibyll is configured to handle this decay!
+    if (!isDecayHandled(pCode))
+      throw std::runtime_error("STOP! Sibyll not configured to execute this decay!");
+
+    count_++;
+    SibStack ss;
+    ss.clear();
+    // copy particle to sibyll stack
+    ss.addParticle(sibyll::convertToSibyllRaw(pCode), projectile.getEnergy(),
+                   projectile.getMomentum(),
+                   // setting particle mass with Corsika values, may be inconsistent
+                   // with sibyll internal values
+                   get_mass(pCode));
+    // remember position
+    Point const decayPoint = projectile.getPosition();
+    TimeType const t0 = projectile.getTime();
+    // remember if particles is unstable
+    // auto const priorIsUnstable = isUnstable(pCode);
+    // switch on decay for this particle
+    setUnstable(pCode);
+    printDecayConfig(pCode);
+
+    // call sibyll decay
+    CORSIKA_LOG_DEBUG("Decay: calling Sibyll decay routine..");
+    decsib_();
+
+    if (sibyll_listing_) {
+      // print output
+      int print_unit = 6;
+      sib_list_(print_unit);
+    }
+
+    // reset to stable
+    setStable(pCode);
+
+    // copy particles from sibyll stack to corsika
+    for (auto const& psib : ss) {
+      // FOR NOW: skip particles that have decayed in Sibyll, move to iterator?
+      if (psib.hasDecayed()) continue;
+      // add to corsika stack
+      projectile.addSecondary(std::make_tuple(sibyll::convertFromSibyll(psib.getPID()),
+                                              psib.getEnergy(), psib.getMomentum(),
+                                              decayPoint, t0));
+    }
+    // empty sibyll stack
+    ss.clear();
+  }
+
+} // namespace corsika::sibyll
diff --git a/Processes/Sibyll/Interaction.cc b/corsika/detail/modules/sibyll/Interaction.inl
similarity index 52%
rename from Processes/Sibyll/Interaction.cc
rename to corsika/detail/modules/sibyll/Interaction.inl
index aa222b7f65ced91a80b5e6402a0ae5a45926c6dc..364ba19d15987c833b66d634e39ce890ce0e14b4 100644
--- a/Processes/Sibyll/Interaction.cc
+++ b/corsika/detail/modules/sibyll/Interaction.inl
@@ -6,72 +6,91 @@
  * the license.
  */
 
-#include <corsika/process/sibyll/Interaction.h>
-
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/geometry/FourVector.h>
-#include <corsika/process/sibyll/ParticleConversion.h>
-#include <corsika/process/sibyll/SibStack.h>
-#include <corsika/process/sibyll/sibyll2.3d.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-#include <corsika/utl/COMBoost.h>
+#pragma once
 
-#include <tuple>
+#include <corsika/modules/sibyll/Interaction.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/framework/geometry/FourVector.hpp>
+#include <corsika/modules/sibyll/ParticleConversion.hpp>
+#include <corsika/modules/sibyll/SibStack.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+#include <corsika/framework/utility/COMBoost.hpp>
+
+#include <sibyll2.3d.hpp>
 
-using std::make_tuple;
-using std::tuple;
+#include <tuple>
 
 using namespace corsika;
-using namespace corsika::setup;
-using Particle = setup::Stack::StackIterator;
+using SetupParticle = setup::Stack::stack_iterator_type;
 using SetupView = setup::StackView;
-using Track = Trajectory;
-
-namespace corsika::process::sibyll {
+using Track = setup::Trajectory;
 
-  bool Interaction::initialized_ = false;
+namespace corsika::sibyll {
 
-  Interaction::Interaction(const bool sibyll_printout_on)
+  inline Interaction::Interaction(const bool sibyll_printout_on)
       : sibyll_listing_(sibyll_printout_on) {
-    using random::RNGManager;
-
     // initialize Sibyll
-    if (!initialized_) {
+    static bool initialized = false;
+    if (!initialized) {
       sibyll_ini_();
-      initialized_ = true;
+      initialized = true;
     }
   }
 
-  Interaction::~Interaction() {
-    C8LOG_DEBUG(fmt::format("Sibyll::Interaction n={}, Nnuc={}", count_, nucCount_));
+  inline Interaction::~Interaction() {
+    CORSIKA_LOG_DEBUG(
+        fmt::format("Sibyll::Interaction n={}, Nnuc={}", count_, nucCount_));
   }
 
-  void Interaction::SetAllStable() {
+  inline void Interaction::setStable(std::vector<corsika::Code> const& vParticleList) {
+    for (auto p : vParticleList) Interaction::setStable(p);
+  }
+
+  inline void Interaction::setUnstable(std::vector<corsika::Code> const& vParticleList) {
+    for (auto p : vParticleList) Interaction::setUnstable(p);
+  }
+
+  inline void Interaction::setUnstable(const corsika::Code vCode) {
+    std::cout << "Sibyll::Interaction: setting " << vCode << " unstable.." << std::endl;
+    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;
+    const int s_id = abs(corsika::sibyll::convertToSibyllRaw(vCode));
+    s_csydec_.idb[s_id - 1] = (-1) * abs(s_csydec_.idb[s_id - 1]);
+  }
+
+  inline void Interaction::setAllUnstable() {
+    for (int i = 0; i < 99; ++i) s_csydec_.idb[i] = abs(s_csydec_.idb[i]);
+  }
+
+  inline void Interaction::setAllStable() {
     for (int i = 0; i < 99; ++i) s_csydec_.idb[i] = -1 * abs(s_csydec_.idb[i]);
   }
 
-  tuple<units::si::CrossSectionType, units::si::CrossSectionType>
-  Interaction::GetCrossSection(const particles::Code BeamId,
-                               const particles::Code TargetId,
-                               const units::si::HEPEnergyType CoMenergy) const {
-    using namespace units::si;
+  inline std::tuple<corsika::CrossSectionType, corsika::CrossSectionType>
+  Interaction::getCrossSection(const corsika::Code BeamId, const corsika::Code TargetId,
+                               const corsika::HEPEnergyType CoMenergy) const {
     double sigProd, sigEla, dummy, dum1, dum3, dum4;
     double dumdif[3];
-    const int iBeam = process::sibyll::GetSibyllXSCode(BeamId);
-    if (!IsValidCoMEnergy(CoMenergy)) {
+    const int iBeam = corsika::sibyll::getSibyllXSCode(BeamId);
+    if (!isValidCoMEnergy(CoMenergy)) {
       throw std::runtime_error(
-          "Interaction: GetCrossSection: CoM energy outside range for Sibyll!");
+          "Interaction: getCrossSection: CoM energy outside range for Sibyll!");
     }
     const double dEcm = CoMenergy / 1_GeV;
-    if (particles::IsNucleus(TargetId)) {
-      const int iTarget = particles::GetNucleusA(TargetId);
+    if (corsika::is_nucleus(TargetId)) {
+      const int iTarget = corsika::get_nucleus_A(TargetId);
       if (iTarget > maxTargetMassNumber_ || iTarget == 0)
         throw std::runtime_error(
             "Sibyll target outside range. Only nuclei with A<18 are allowed.");
       sib_sigma_hnuc_(iBeam, iTarget, dEcm, sigProd, dummy, sigEla);
-    } else if (TargetId == particles::Proton::GetCode()) {
+    } else if (TargetId == corsika::Code::Proton) {
       sib_sigma_hp_(iBeam, dEcm, dum1, sigEla, sigProd, dumdif, dum3, dum4);
     } else {
       // no interaction in sibyll possible, return infinite cross section? or throw?
@@ -82,45 +101,41 @@ namespace corsika::process::sibyll {
   }
 
   template <>
-  units::si::GrammageType Interaction::GetInteractionLength(Particle const& vP) const {
+  inline corsika::GrammageType Interaction::getInteractionLength(
+      SetupParticle const& projectile) const {
 
-    using namespace units;
-    using namespace units::si;
-    using namespace geometry;
+    const corsika::Code corsikaBeamId = projectile.getPID();
 
-    // coordinate system, get global frame of reference
-    CoordinateSystem& rootCS =
-        RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
-
-    const particles::Code corsikaBeamId = vP.GetPID();
-
-    // beam particles for sibyll : 1, 2, 3 for p, pi, k
+    // beam corsika for sibyll : 1, 2, 3 for p, pi, k
     // read from cross section code table
-    const bool kInteraction = process::sibyll::CanInteract(corsikaBeamId);
+    const bool kInteraction = corsika::sibyll::canInteract(corsikaBeamId);
+
+    MomentumVector const& pLab = projectile.getMomentum();
+    CoordinateSystemPtr const& labCS = pLab.getCoordinateSystem();
 
     // FOR NOW: assume target is at rest
-    MomentumVector pTarget(rootCS, {0_GeV, 0_GeV, 0_GeV});
+    MomentumVector pTarget(labCS, {0_GeV, 0_GeV, 0_GeV});
 
     // total momentum and energy
-    HEPEnergyType Elab = vP.GetEnergy() + constants::nucleonMass;
-    MomentumVector pTotLab(rootCS, {0_GeV, 0_GeV, 0_GeV});
-    pTotLab += vP.GetMomentum();
+    HEPEnergyType Elab = projectile.getEnergy() + constants::nucleonMass;
+    MomentumVector pTotLab(labCS, {0_GeV, 0_GeV, 0_GeV});
+    pTotLab += pLab;
     pTotLab += pTarget;
-    auto const pTotLabNorm = pTotLab.norm();
+    auto const pTotLabNorm = pTotLab.getNorm();
     // calculate cm. energy
     const HEPEnergyType ECoM = sqrt(
         (Elab + pTotLabNorm) * (Elab - pTotLabNorm)); // binomial for numerical accuracy
 
-    C8LOG_DEBUG(
-        fmt::format("Interaction: LambdaInt: \n"
-                    " input energy: {} GeV "
-                    " beam can interact: {} "
-                    " beam pid: {}",
-                    vP.GetEnergy() / 1_GeV, kInteraction, vP.GetPID()));
+    CORSIKA_LOG_DEBUG(
+        "Interaction: LambdaInt: \n"
+        " input energy: {} GeV "
+        " beam can interact: {} "
+        " beam pid: {}",
+        projectile.getEnergy() / 1_GeV, kInteraction, projectile.getPID());
 
     // TODO: move limits into variables
     // FR: removed && Elab >= 8.5_GeV
-    if (kInteraction && IsValidCoMEnergy(ECoM)) {
+    if (kInteraction && isValidCoMEnergy(ECoM)) {
 
       // get target from environment
       /*
@@ -129,24 +144,24 @@ namespace corsika::process::sibyll {
         and the boosts can be defined..
       */
 
-      auto const* currentNode = vP.GetNode();
+      auto const* currentNode = projectile.getNode();
       const auto& mediumComposition =
-          currentNode->GetModelProperties().GetNuclearComposition();
+          currentNode->getModelProperties().getNuclearComposition();
 
-      si::CrossSectionType weightedProdCrossSection = mediumComposition.WeightedSum(
-          [=](particles::Code targetID) -> si::CrossSectionType {
-            return std::get<0>(this->GetCrossSection(corsikaBeamId, targetID, ECoM));
+      si::CrossSectionType weightedProdCrossSection = mediumComposition.getWeightedSum(
+          [=](corsika::Code targetID) -> si::CrossSectionType {
+            return std::get<0>(this->getCrossSection(corsikaBeamId, targetID, ECoM));
           });
 
-      C8LOG_DEBUG(
+      CORSIKA_LOG_DEBUG(
           fmt::format("Interaction: "
                       "IntLength: weighted CrossSection (mb): {} ",
                       weightedProdCrossSection / 1_mb));
 
       // calculate interaction length in medium
-      GrammageType const int_length = mediumComposition.GetAverageMassNumber() *
-                                      units::constants::u / weightedProdCrossSection;
-      C8LOG_DEBUG(
+      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));
@@ -162,32 +177,27 @@ namespace corsika::process::sibyll {
      event is copied (and boosted) into the shower lab frame.
    */
 
-  template <>
-  process::EProcessReturn Interaction::DoInteraction(SetupView& view) {
-    using namespace utl;
-    using namespace units;
-    using namespace units::si;
-    using namespace geometry;
-
-    auto const projectile = view.GetProjectile();
+  template <typename TSecondaryView>
+  inline void Interaction::doInteraction(TSecondaryView& view) {
 
-    const auto corsikaBeamId = projectile.GetPID();
+    auto const projectile = view.getProjectile();
+    const auto corsikaBeamId = projectile.getPID();
 
-    if (particles::IsNucleus(corsikaBeamId)) {
+    if (corsika::is_nucleus(corsikaBeamId)) {
       // nuclei handled by different process, this should not happen
       throw std::runtime_error("Nuclear projectile are not handled by SIBYLL!");
     }
 
     // position and time of interaction, not used in Sibyll
-    Point const pOrig = projectile.GetPosition();
-    TimeType const tOrig = projectile.GetTime();
+    Point const pOrig = projectile.getPosition();
+    TimeType const tOrig = projectile.getTime();
 
     // define projectile
-    HEPEnergyType const eProjectileLab = projectile.GetEnergy();
-    auto const pProjectileLab = projectile.GetMomentum();
-    const CoordinateSystem& originalCS = pProjectileLab.GetCoordinateSystem();
+    HEPEnergyType const eProjectileLab = projectile.getEnergy();
+    auto const pProjectileLab = projectile.getMomentum();
+    CoordinateSystemPtr const& originalCS = pProjectileLab.getCoordinateSystem();
 
-    C8LOG_DEBUG(
+    CORSIKA_LOG_DEBUG(
         "ProcessSibyll: "
         "DoInteraction: pid {} interaction ",
         corsikaBeamId);
@@ -199,14 +209,14 @@ namespace corsika::process::sibyll {
     const auto pTargetLab = MomentumVector(originalCS, 0_GeV, 0_GeV, 0_GeV);
     const FourVector PtargLab(eTargetLab, pTargetLab);
 
-    C8LOG_DEBUG(
+    CORSIKA_LOG_DEBUG(
         "Interaction: ebeam lab: {} GeV"
         "Interaction: pbeam lab: {} GeV",
-        eProjectileLab / 1_GeV, pProjectileLab.GetComponents());
-    C8LOG_DEBUG(
+        eProjectileLab / 1_GeV, pProjectileLab.getComponents());
+    CORSIKA_LOG_DEBUG(
         "Interaction: etarget lab: {} GeV "
         "Interaction: ptarget lab: {} GeV",
-        eTargetLab / 1_GeV, pTargetLab.GetComponents() / 1_GeV);
+        eTargetLab / 1_GeV, pTargetLab.getComponents() / 1_GeV);
 
     const FourVector PprojLab(eProjectileLab, pProjectileLab);
 
@@ -214,7 +224,7 @@ namespace corsika::process::sibyll {
     // define boost to and from CoM frame
     // CoM frame definition in Sibyll projectile: +z
     COMBoost const boost(PprojLab, constants::nucleonMass);
-    auto const& csPrime = boost.GetRotatedCS();
+    auto const& csPrime = boost.getRotatedCS();
 
     // just for show:
     // boost projecticle
@@ -223,75 +233,76 @@ namespace corsika::process::sibyll {
     // boost target
     [[maybe_unused]] auto const PtargCoM = boost.toCoM(PtargLab);
 
-    C8LOG_DEBUG(
+    CORSIKA_LOG_DEBUG(
         "Interaction: ebeam CoM: {} GeV "
         "Interaction: pbeam CoM: {} GeV ",
-        PprojCoM.GetTimeLikeComponent() / 1_GeV,
-        PprojCoM.GetSpaceLikeComponents().GetComponents(csPrime) / 1_GeV);
-    C8LOG_DEBUG(
+        PprojCoM.getTimeLikeComponent() / 1_GeV,
+        PprojCoM.getSpaceLikeComponents().getComponents(csPrime) / 1_GeV);
+    CORSIKA_LOG_DEBUG(
         "Interaction: etarget CoM: {} GeV "
         "Interaction: ptarget CoM: {} GeV ",
-        PtargCoM.GetTimeLikeComponent() / 1_GeV,
-        PtargCoM.GetSpaceLikeComponents().GetComponents(csPrime) / 1_GeV);
+        PtargCoM.getTimeLikeComponent() / 1_GeV,
+        PtargCoM.getSpaceLikeComponents().getComponents(csPrime) / 1_GeV);
 
-    C8LOG_DEBUG("Interaction: position of interaction: {} ", pOrig.GetCoordinates());
-    C8LOG_DEBUG("Interaction: time: {} ", tOrig);
+    CORSIKA_LOG_DEBUG("Interaction: position of interaction: {} ",
+                      pOrig.getCoordinates());
+    CORSIKA_LOG_DEBUG("Interaction: time: {} ", tOrig);
 
     HEPEnergyType Etot = eProjectileLab + eTargetLab;
-    MomentumVector Ptot = projectile.GetMomentum();
+    MomentumVector Ptot = projectile.getMomentum();
     // invariant mass, i.e. cm. energy
-    HEPEnergyType Ecm = sqrt(Etot * Etot - Ptot.squaredNorm());
+    HEPEnergyType Ecm = sqrt(Etot * Etot - Ptot.getSquaredNorm());
 
     // sample target mass number
-    auto const* currentNode = projectile.GetNode();
+    auto const* currentNode = projectile.getNode();
     auto const& mediumComposition =
-        currentNode->GetModelProperties().GetNuclearComposition();
+        currentNode->getModelProperties().getNuclearComposition();
     // get cross sections for target materials
     /*
       Here we read the cross section from the interaction model again,
-      should be passed from GetInteractionLength if possible
+      should be passed from getInteractionLength if possible
      */
     //#warning reading interaction cross section again, should not be necessary
-    auto const& compVec = mediumComposition.GetComponents();
+    auto const& compVec = mediumComposition.getComponents();
     std::vector<CrossSectionType> cross_section_of_components(compVec.size());
 
     for (size_t i = 0; i < compVec.size(); ++i) {
       auto const targetId = compVec[i];
-      const auto [sigProd, sigEla] = GetCrossSection(corsikaBeamId, targetId, Ecm);
+      const auto [sigProd, sigEla] = getCrossSection(corsikaBeamId, targetId, Ecm);
       [[maybe_unused]] const auto& dummy_sigEla = sigEla;
       cross_section_of_components[i] = sigProd;
     }
 
     const auto targetCode =
-        mediumComposition.SampleTarget(cross_section_of_components, RNG_);
-    C8LOG_DEBUG("Interaction: target selected: {} ", targetCode);
+        mediumComposition.sampleTarget(cross_section_of_components, RNG_);
+    CORSIKA_LOG_DEBUG("Interaction: target selected: {} ", targetCode);
     /*
       FOR NOW: allow nuclei with A<18 or protons only.
       when medium composition becomes more complex, approximations will have to be
       allowed air in atmosphere also contains some Argon.
     */
     int targetSibCode = -1;
-    if (IsNucleus(targetCode)) targetSibCode = GetNucleusA(targetCode);
-    if (targetCode == particles::Proton::GetCode()) targetSibCode = 1;
-    C8LOG_DEBUG("Interaction: sibyll code: {}", targetSibCode);
+    if (is_nucleus(targetCode)) targetSibCode = get_nucleus_A(targetCode);
+    if (targetCode == Proton::code) targetSibCode = 1;
+    CORSIKA_LOG_DEBUG("Interaction: sibyll code: {}", targetSibCode);
     if (targetSibCode > maxTargetMassNumber_ || targetSibCode < 1)
       throw std::runtime_error(
           "Sibyll target outside range. Only nuclei with A<18 or protons are "
           "allowed.");
 
     // beam id for sibyll
-    const int kBeam = process::sibyll::ConvertToSibyllRaw(corsikaBeamId);
+    const int kBeam = corsika::sibyll::convertToSibyllRaw(corsikaBeamId);
 
-    C8LOG_DEBUG(
+    CORSIKA_LOG_DEBUG(
         "Interaction: "
         " DoInteraction: E(GeV): {} "
         " Ecm(GeV): {} ",
         eProjectileLab / 1_GeV, Ecm / 1_GeV);
-    if (Ecm > GetMaxEnergyCoM())
+    if (Ecm > getMaxEnergyCoM())
       throw std::runtime_error("Interaction::DoInteraction: CoM energy too high!");
     // FR: removed eProjectileLab < 8.5_GeV ||
-    if (Ecm < GetMinEnergyCoM()) {
-      C8LOG_DEBUG(
+    if (Ecm < getMinEnergyCoM()) {
+      CORSIKA_LOG_DEBUG(
           "Interaction: "
           " DoInteraction: should have dropped particle.. "
           "THIS IS AN ERROR");
@@ -319,28 +330,28 @@ namespace corsika::process::sibyll {
       for (auto& psib : ss) {
 
         // abort on particles that have decayed in Sibyll. Should not happen!
-        if (psib.HasDecayed())
+        if (psib.hasDecayed())
           throw std::runtime_error("found particle that decayed in SIBYLL!");
 
         // transform 4-momentum to lab. frame
         // note that the momentum needs to be rotated back
-        auto const tmp = psib.GetMomentum().GetComponents();
+        auto const tmp = psib.getMomentum().getComponents();
         auto const pCoM = Vector<hepmomentum_d>(csPrime, tmp);
-        HEPEnergyType const eCoM = psib.GetEnergy();
+        HEPEnergyType const eCoM = psib.getEnergy();
         auto const Plab = boost.fromCoM(FourVector(eCoM, pCoM));
-        auto const p3lab = Plab.GetSpaceLikeComponents();
-        assert(p3lab.GetCoordinateSystem() == originalCS); // just to be sure!
+        auto const p3lab = Plab.getSpaceLikeComponents();
+        assert(p3lab.getCoordinateSystem() == originalCS); // just to be sure!
 
         // add to corsika stack
-        auto pnew = view.AddSecondary(
-            make_tuple(process::sibyll::ConvertFromSibyll(psib.GetPID()),
-                       Plab.GetTimeLikeComponent(), p3lab, pOrig, tOrig));
+        auto pnew = view.addSecondary(
+            std::make_tuple(corsika::sibyll::convertFromSibyll(psib.getPID()),
+                            Plab.getTimeLikeComponent(), p3lab, pOrig, tOrig));
 
-        Plab_final += pnew.GetMomentum();
-        Elab_final += pnew.GetEnergy();
-        Ecm_final += psib.GetEnergy();
+        Plab_final += pnew.getMomentum();
+        Elab_final += pnew.getEnergy();
+        Ecm_final += psib.getEnergy();
       }
-      C8LOG_DEBUG(
+      CORSIKA_LOG_DEBUG(
           "conservation (all GeV):"
           "Ecm_initial(per nucleon)={}, Ecm_final(per nucleon)={}, "
           "Elab_initial={}, Elab_final={}, "
@@ -351,9 +362,8 @@ namespace corsika::process::sibyll {
           Ecm / 1_GeV, Ecm_final * 2. / (get_nwounded() + 1) / 1_GeV, Etot / 1_GeV,
           Elab_final / 1_GeV, (Elab_final / Etot / get_nwounded() - 1) * 100,
           constants::nucleonMass * get_nwounded() / 1_GeV,
-          (pProjectileLab / 1_GeV).GetComponents(), (Plab_final / 1_GeV).GetComponents());
+          (pProjectileLab / 1_GeV).getComponents(), (Plab_final / 1_GeV).getComponents());
     }
-    return process::EProcessReturn::eOk;
   }
 
-} // namespace corsika::process::sibyll
+} // namespace corsika::sibyll
diff --git a/corsika/detail/modules/sibyll/NuclearInteraction.inl b/corsika/detail/modules/sibyll/NuclearInteraction.inl
new file mode 100644
index 0000000000000000000000000000000000000000..4470ea28a8b1e2f504711346790b67bf2342370e
--- /dev/null
+++ b/corsika/detail/modules/sibyll/NuclearInteraction.inl
@@ -0,0 +1,608 @@
+/*
+ * (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/modules/sibyll/Interaction.hpp>
+#include <corsika/modules/sibyll/NuclearInteraction.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/framework/geometry/FourVector.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/utility/COMBoost.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <corsika/setup/SetupEnvironment.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <nuclib.hpp>
+
+namespace corsika::sibyll {
+
+  template <typename TEnvironment>
+  NuclearInteraction<TEnvironment>::NuclearInteraction(sibyll::Interaction& hadint,
+                                                       TEnvironment const& env)
+      : environment_(env)
+      , hadronicInteraction_(hadint) {
+
+    // initialize hadronic interaction module
+
+    // check compatibility of energy ranges, someone could try to use low-energy model..
+    if (!hadronicInteraction_.isValidCoMEnergy(getMinEnergyPerNucleonCoM()) ||
+        !hadronicInteraction_.isValidCoMEnergy(getMaxEnergyPerNucleonCoM()))
+      throw std::runtime_error(
+          "NuclearInteraction: hadronic interaction model incompatible!");
+
+    // initialize nuclib
+    // TODO: make sure this does not overlap with sibyll
+    nuc_nuc_ini_();
+
+    // initialize cross sections
+    initializeNuclearCrossSections();
+  }
+
+  template <typename TEnvironment>
+  NuclearInteraction<TEnvironment>::~NuclearInteraction() {
+    CORSIKA_LOG_DEBUG("Nuclib::NuclearInteraction n={} Nnuc={}", count_, nucCount_);
+  }
+
+  template <typename TEnvironment>
+  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};
+
+    std::ostringstream table;
+    table << "Nuclear CrossSectionTable pCode=" << pCode << " :\n en/A ";
+    for (auto& j : pNuclei) table << std::setw(9) << j;
+    table << "\n";
+
+    // loop over energy bins
+    for (unsigned int i = 0; i < getNEnergyBins(); ++i) {
+      table << " " << i << "  ";
+
+      for (auto& n : pNuclei) {
+        auto const j = get_nucleus_A(n);
+        table << " " << std::setprecision(5) << std::setw(8)
+              << cnucsignuc_.sigma[j - 1][k][i];
+      }
+      table << "\n";
+    }
+    CORSIKA_LOG_DEBUG(table.str());
+  }
+
+  template <typename TEnvironment>
+  void NuclearInteraction<TEnvironment>::initializeNuclearCrossSections() {
+
+    auto& universe = *(environment_.getUniverse());
+
+    auto const allElementsInUniverse = std::invoke([&]() {
+      std::set<Code> allElementsInUniverse;
+      auto collectElements = [&](auto& vtn) {
+        if (vtn.hasModelProperties()) {
+          auto const& comp =
+              vtn.getModelProperties().getNuclearComposition().getComponents();
+          for (auto const c : comp) allElementsInUniverse.insert(c);
+        }
+      };
+      universe.walk(collectElements);
+      return allElementsInUniverse;
+    });
+
+    CORSIKA_LOG_DEBUG("NuclearInteraction: initializing nuclear cross sections...");
+
+    // loop over target components, at most 4!!
+    int k = -1;
+    for (auto& ptarg : allElementsInUniverse) {
+      ++k;
+      CORSIKA_LOG_DEBUG("NuclearInteraction: init target component: {}", ptarg);
+      const int ib = get_nucleus_A(ptarg);
+      if (!hadronicInteraction_.isValidTarget(ptarg)) {
+        CORSIKA_LOG_DEBUG(
+            "NuclearInteraction::InitializeNuclearCrossSections: target nucleus? id={}",
+            ptarg);
+        throw std::runtime_error(
+            " target can not be handled by hadronic interaction model! ");
+      }
+      targetComponentsIndex_.insert(std::pair<Code, int>(ptarg, k));
+      // loop over energies, fNEnBins log. energy bins
+      for (unsigned int i = 0; i < getNEnergyBins(); ++i) {
+        // hard coded energy grid, has to be aligned to definition in signuc2!!, no
+        // comment..
+        const HEPEnergyType Ecm = pow(10., 1. + 1. * i) * 1_GeV;
+        // get p-p cross sections
+        auto const protonId = Code::Proton;
+        auto const [siginel, sigela] =
+            hadronicInteraction_.getCrossSection(protonId, protonId, Ecm);
+        const double dsig = siginel / 1_mb;
+        const double dsigela = sigela / 1_mb;
+        // loop over projectiles, mass numbers from 2 to fMaxNucleusAProjectile
+        for (unsigned int j = 1; j < gMaxNucleusAProjectile_; ++j) {
+          const int jj = j + 1;
+          double sig_out, dsig_out, sigqe_out, dsigqe_out;
+          sigma_mc_(jj, ib, dsig, dsigela, gNSample_, sig_out, dsig_out, sigqe_out,
+                    dsigqe_out);
+          // write to table
+          cnucsignuc_.sigma[j][k][i] = sig_out;
+          cnucsignuc_.sigqe[j][k][i] = sigqe_out;
+        }
+      }
+    }
+    CORSIKA_LOG_DEBUG(
+        "NuclearInteraction: cross sections for {} "
+        " components initialized!",
+        targetComponentsIndex_.size());
+    for (auto& ptarg : allElementsInUniverse) { printCrossSectionTable(ptarg); }
+  }
+
+  template <typename TEnvironment>
+  CrossSectionType NuclearInteraction<TEnvironment>::readCrossSectionTable(
+      const int ia, Code pTarget, HEPEnergyType elabnuc) {
+
+    const int ib = targetComponentsIndex_.at(pTarget) + 1; // table index in fortran
+    auto const ECoMNuc = sqrt(2. * constants::nucleonMass * elabnuc);
+    if (ECoMNuc < getMinEnergyPerNucleonCoM() || ECoMNuc > getMaxEnergyPerNucleonCoM())
+      throw std::runtime_error("NuclearInteraction: energy outside tabulated range!");
+    const double e0 = elabnuc / 1_GeV;
+    double sig;
+    CORSIKA_LOG_DEBUG("ReadCrossSectionTable: {} {} {}", ia, ib, e0);
+    signuc2_(ia, ib, e0, sig);
+    CORSIKA_LOG_DEBUG("ReadCrossSectionTable: sig={}", sig);
+    return sig * 1_mb;
+  }
+
+  // TODO: remove elastic cross section?
+  template <typename TEnvironment>
+  template <typename TParticle>
+  std::tuple<CrossSectionType, CrossSectionType>
+  NuclearInteraction<TEnvironment>::getCrossSection(TParticle const& projectile,
+                                                    Code const TargetId) {
+
+    if (projectile.getPID() != Code::Nucleus)
+      throw std::runtime_error(
+          "NuclearInteraction: getCrossSection: particle not a nucleus!");
+
+    unsigned int const iBeamA = projectile.getNuclearA();
+    HEPEnergyType LabEnergyPerNuc = projectile.getEnergy() / iBeamA;
+    CORSIKA_LOG_DEBUG(
+        "NuclearInteraction: getCrossSection: called with: beamNuclA={} "
+        " TargetId={} LabEnergyPerNuc={}GeV ",
+        iBeamA, TargetId, LabEnergyPerNuc / 1_GeV);
+
+    // use nuclib to calc. nuclear cross sections
+    // TODO: for now assumes air with hard coded composition
+    // extend to arbitrary mixtures, requires smarter initialization
+    // get nuclib projectile code: nucleon number
+    if (iBeamA > getMaxNucleusAProjectile() || iBeamA < 2) {
+      CORSIKA_LOG_DEBUG(
+          "NuclearInteraction: beam nucleus outside allowed range for NUCLIB!"
+          "A=" +
+          std::to_string(iBeamA));
+      throw std::runtime_error(
+          "NuclearInteraction: getCrossSection: beam nucleus outside allowed range for "
+          "NUCLIB!");
+    }
+
+    if (hadronicInteraction_.isValidTarget(TargetId)) {
+      auto const sigProd = readCrossSectionTable(iBeamA, TargetId, LabEnergyPerNuc);
+      CORSIKA_LOG_DEBUG("cross section (mb): " + std::to_string(sigProd / 1_mb));
+      return std::make_tuple(sigProd, 0_mb);
+    } else {
+      throw std::runtime_error("target outside range.");
+    }
+    return std::make_tuple(std::numeric_limits<double>::infinity() * 1_mb,
+                           std::numeric_limits<double>::infinity() * 1_mb);
+  }
+
+  template <typename TEnvironment>
+  template <typename TParticle>
+  GrammageType NuclearInteraction<TEnvironment>::getInteractionLength(
+      TParticle const& projectile) {
+
+    // coordinate system, get global frame of reference
+
+    const Code corsikaBeamId = projectile.getPID();
+
+    if (corsikaBeamId != Code::Nucleus) {
+      // check if target-style nucleus (enum), these are not allowed as projectile
+      if (is_nucleus(corsikaBeamId)) {
+        throw std::runtime_error(
+            "NuclearInteraction: getInteractionLength: Wrong nucleus type. Nuclear "
+            "projectiles should use NuclearStackExtension!");
+      } else {
+        // no nuclear interaction
+        return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
+      }
+    }
+
+    // read from cross section code table
+
+    MomentumVector pLab = projectile.getMomentum();
+    CoordinateSystemPtr const& labCS = pLab.getCoordinateSystem();
+
+    // FOR NOW: assume target is at rest
+    MomentumVector pTarget(labCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
+
+    // total momentum and energy
+    HEPEnergyType Elab = projectile.getEnergy() + constants::nucleonMass;
+    int const nuclA = projectile.getNuclearA();
+    auto const ElabNuc = projectile.getEnergy() / nuclA;
+
+    MomentumVector pTotLab(labCS, {0.0_GeV, 0.0_GeV, 0.0_GeV});
+    pTotLab += pLab;
+    pTotLab += pTarget;
+    auto const pTotLabNorm = pTotLab.getNorm();
+    // calculate cm. energy
+    HEPEnergyType const ECoM = sqrt(
+        (Elab + pTotLabNorm) * (Elab - pTotLabNorm)); // binomial for numerical accuracy
+    auto const ECoMNN = sqrt(2. * ElabNuc * constants::nucleonMass);
+    CORSIKA_LOG_DEBUG(
+        "NuclearInteraction: LambdaInt: \n"
+        " input energy: {}GeV\n"
+        " input energy CoM: {}GeV\n"
+        " beam pid: {}\n"
+        " beam A: {}\n"
+        " input energy per nucleon: {}GeV\n"
+        " input energy CoM per nucleon: {}GeV ",
+        Elab / 1_GeV, ECoM / 1_GeV, get_name(corsikaBeamId), nuclA, ElabNuc / 1_GeV,
+        ECoMNN / 1_GeV);
+
+    // energy limits
+    // TODO: values depend on hadronic interaction model !! this is sibyll specific
+    if (ElabNuc >= 8.5_GeV && ECoMNN >= gMinEnergyPerNucleonCoM_ &&
+        ECoMNN < gMaxEnergyPerNucleonCoM_) {
+
+      // get target from environment
+      /*
+        the target should be defined by the Environment,
+        ideally as full particle object so that the four momenta
+        and the boosts can be defined..
+      */
+      auto const* const currentNode = projectile.getNode();
+      auto const& mediumComposition =
+          currentNode->getModelProperties().getNuclearComposition();
+      // determine average interaction length
+      // weighted sum
+      int i = -1;
+      CrossSectionType weightedProdCrossSection = 0_mb;
+      // get weights of components from environment/medium
+      const auto& w = mediumComposition.getFractions();
+      // loop over components in medium
+      for (auto const targetId : mediumComposition.getComponents()) {
+        i++;
+        CORSIKA_LOG_DEBUG("NuclearInteraction: get interaction length for target: {}",
+                          get_name(targetId));
+        auto const [productionCrossSection, elaCrossSection] =
+            getCrossSection(projectile, targetId);
+        [[maybe_unused]] auto& dummy_elaCrossSection = elaCrossSection;
+
+        CORSIKA_LOG_DEBUG(
+            "NuclearInteraction: "
+            "IntLength: nuclib return (mb): " +
+            std::to_string(productionCrossSection / 1_mb));
+        weightedProdCrossSection += w[i] * productionCrossSection;
+      }
+      CORSIKA_LOG_DEBUG(
+          "NuclearInteraction: "
+          "IntLength: weighted CrossSection (mb): {} ",
+          weightedProdCrossSection / 1_mb);
+
+      // calculate interaction length in medium
+      GrammageType const int_length = mediumComposition.getAverageMassNumber() *
+                                      constants::u / weightedProdCrossSection;
+      CORSIKA_LOG_DEBUG(
+          "NuclearInteraction: "
+          "interaction length (g/cm2): {} ",
+          int_length * (1_cm * 1_cm / (0.001_kg)));
+
+      return int_length;
+    } else {
+      return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
+    }
+  }
+
+  template <typename TEnvironment>
+  template <typename TSecondaryView>
+  void NuclearInteraction<TEnvironment>::doInteraction(TSecondaryView& view) {
+
+    auto projectile = view.getProjectile();
+
+    // this routine superimposes different nucleon-nucleon interactions
+    // in a nucleus-nucleus interaction, based the SIBYLL routine SIBNUC
+
+    const auto ProjId = projectile.getPID();
+    // TODO: calculate projectile mass in nuclearStackExtension
+    //      const auto ProjMass = projectile.getMass();
+
+    CORSIKA_LOG_DEBUG("NuclearInteraction: DoInteraction: called with: {}",
+                      get_name(ProjId));
+
+    // check if target-style nucleus (enum)
+    if (ProjId != Code::Nucleus)
+      throw std::runtime_error(
+          "NuclearInteraction: DoInteraction: Wrong nucleus type. Nuclear projectiles "
+          "should use NuclearStackExtension!");
+
+    auto const ProjMass =
+        projectile.getNuclearZ() * Proton::mass +
+        (projectile.getNuclearA() - projectile.getNuclearZ()) * Neutron::mass;
+    CORSIKA_LOG_DEBUG("NuclearInteraction: projectile mass: {} ", ProjMass / 1_GeV);
+
+    count_++;
+
+    // position and time of interaction, not used in NUCLIB
+    Point pOrig = projectile.getPosition();
+    TimeType tOrig = projectile.getTime();
+
+    CORSIKA_LOG_DEBUG("Interaction: position of interaction: {}", pOrig.getCoordinates());
+    CORSIKA_LOG_DEBUG("Interaction: time: {} ", tOrig / 1_s);
+
+    // projectile nucleon number
+    const unsigned int kAProj = projectile.getNuclearA();
+    if (kAProj > getMaxNucleusAProjectile())
+      throw std::runtime_error("Projectile nucleus too large for NUCLIB!");
+
+    // kinematics
+    // define projectile nucleus
+    HEPEnergyType const eProjectileLab = projectile.getEnergy();
+    MomentumVector const pProjectileLab = projectile.getMomentum();
+    FourVector const PprojLab(eProjectileLab, pProjectileLab);
+    CoordinateSystemPtr const& labCS = pProjectileLab.getCoordinateSystem();
+
+    CORSIKA_LOG_DEBUG(
+        "NuclearInteraction: eProj lab: {} "
+        "pProj lab: {} ",
+        eProjectileLab / 1_GeV, pProjectileLab.getComponents() / 1_GeV);
+
+    // define projectile nucleon
+    HEPEnergyType const eProjectileNucLab = eProjectileLab / kAProj;
+    MomentumVector const pProjectileNucLab = pProjectileLab / kAProj;
+    FourVector const PprojNucLab(eProjectileNucLab, pProjectileNucLab);
+
+    CORSIKA_LOG_DEBUG(
+        "NuclearInteraction: eProjNucleon lab (GeV): {} "
+        "pProjNucleon lab (GeV): {} ",
+        eProjectileNucLab / 1_GeV, pProjectileNucLab.getComponents() / 1_GeV);
+
+    // define target
+    // always a nucleon
+    // target is always at rest
+    auto const eTargetNucLab = 0_GeV + constants::nucleonMass;
+    auto const pTargetNucLab = MomentumVector(labCS, 0_GeV, 0_GeV, 0_GeV);
+    FourVector const PtargNucLab(eTargetNucLab, pTargetNucLab);
+
+    CORSIKA_LOG_DEBUG(
+        "NuclearInteraction: etarget lab(GeV): {} "
+        "NuclearInteraction: ptarget lab(GeV): {} ",
+        eTargetNucLab / 1_GeV, pTargetNucLab.getComponents() / 1_GeV);
+
+    // center-of-mass energy in nucleon-nucleon frame
+    auto const PtotNN4 = PtargNucLab + PprojNucLab;
+    HEPEnergyType EcmNN = PtotNN4.getNorm();
+
+    CORSIKA_LOG_DEBUG("NuclearInteraction: nuc-nuc cm energy: {}", EcmNN / 1_GeV);
+
+    if (!hadronicInteraction_.isValidCoMEnergy(EcmNN)) {
+      CORSIKA_LOG_DEBUG(
+          "NuclearInteraction: nuc-nuc. CoM energy too low for hadronic "
+          "interaction model!");
+      throw std::runtime_error("NuclearInteraction: DoInteraction: energy too low!");
+    }
+
+    // define boost to NUCLEON-NUCLEON frame
+    COMBoost const boost(PprojNucLab, constants::nucleonMass);
+    // boost projecticle
+    auto const PprojNucCoM = boost.toCoM(PprojNucLab);
+
+    // boost target
+    auto const PtargNucCoM = boost.toCoM(PtargNucLab);
+
+    CORSIKA_LOG_DEBUG(
+        "Interaction: ebeam CoM: {} "
+        ", pbeam CoM: {} ",
+        PprojNucCoM.getTimeLikeComponent() / 1_GeV,
+        PprojNucCoM.getSpaceLikeComponents().getComponents() / 1_GeV);
+    CORSIKA_LOG_DEBUG(
+        "Interaction: etarget CoM: {}"
+        ", ptarget CoM: {}",
+        PtargNucCoM.getTimeLikeComponent() / 1_GeV,
+        PtargNucCoM.getSpaceLikeComponents().getComponents() / 1_GeV);
+
+    // sample target nucleon number
+    //
+    // proton stand-in for nucleon
+    const auto beamId = Code::Proton;
+    auto const* const currentNode = projectile.getNode();
+    const auto& mediumComposition =
+        currentNode->getModelProperties().getNuclearComposition();
+    CORSIKA_LOG_DEBUG("get nucleon-nucleus cross sections for target materials..");
+    // get cross sections for target materials
+    // using nucleon-target-nucleus cross section!!!
+    /*
+      Here we read the cross section from the interaction model again,
+      should be passed from getInteractionLength if possible
+    */
+    auto const& compVec = mediumComposition.getComponents();
+    std::vector<CrossSectionType> cross_section_of_components(compVec.size());
+
+    for (size_t i = 0; i < compVec.size(); ++i) {
+      auto const targetId = compVec[i];
+      CORSIKA_LOG_DEBUG("target component: {}", get_name(targetId));
+      CORSIKA_LOG_DEBUG("beam id: {}", get_name(beamId));
+      const auto [sigProd, sigEla] =
+          hadronicInteraction_.getCrossSection(beamId, targetId, EcmNN);
+      cross_section_of_components[i] = sigProd;
+      [[maybe_unused]] auto sigElaCopy = sigEla; // ONLY TO AVOID COMPILER WARNINGS
+    }
+
+    const auto targetCode =
+        mediumComposition.sampleTarget(cross_section_of_components, RNG_);
+    CORSIKA_LOG_DEBUG("Interaction: target selected: {}", get_name(targetCode));
+    /*
+      FOR NOW: allow nuclei with A<18 or protons only.
+      when medium composition becomes more complex, approximations will have to be
+      allowed air in atmosphere also contains some Argon.
+    */
+    int kATarget = -1;
+    if (is_nucleus(targetCode))
+      kATarget = get_nucleus_A(targetCode);
+    else if (targetCode == Code::Proton)
+      kATarget = 1;
+    CORSIKA_LOG_DEBUG("NuclearInteraction: nuclib target code: " +
+                      std::to_string(kATarget));
+    if (!hadronicInteraction_.isValidTarget(targetCode))
+      throw std::runtime_error("target outside range. ");
+    // end of target sampling
+
+    // superposition
+    CORSIKA_LOG_DEBUG(
+        "NuclearInteraction: sampling nuc. multiple interaction structure.. ");
+    // get nucleon-nucleon cross section
+    // (needed to determine number of nucleon-nucleon scatterings)
+    const auto protonId = Code::Proton;
+    const auto [prodCrossSection, elaCrossSection] =
+        hadronicInteraction_.getCrossSection(protonId, protonId, EcmNN);
+    const double sigProd = prodCrossSection / 1_mb;
+    const double sigEla = elaCrossSection / 1_mb;
+    // sample number of interactions (only input variables, output in common cnucms)
+    // nuclear multiple scattering according to glauber (r.i.p.)
+    int_nuc_(kATarget, kAProj, sigProd, sigEla);
+
+    CORSIKA_LOG_DEBUG(
+        "number of nucleons in target           : {}\n"
+        "number of wounded nucleons in target   : {}\n"
+        "number of nucleons in projectile       : {}\n"
+        "number of wounded nucleons in project. : {}\n"
+        "number of inel. nuc.-nuc. interactions : {}\n"
+        "number of elastic nucleons in target   : {}\n"
+        "number of elastic nucleons in project. : {}\n"
+        "impact parameter: {}",
+        kATarget, cnucms_.na, kAProj, cnucms_.nb, cnucms_.ni, cnucms_.nael, cnucms_.nbel,
+        cnucms_.b);
+
+    // calculate fragmentation
+    CORSIKA_LOG_DEBUG("calculating nuclear fragments..");
+    // number of interactions
+    // include elastic
+    const int nElasticNucleons = cnucms_.nbel;
+    const int nInelNucleons = cnucms_.nb;
+    const int nIntProj = nInelNucleons + nElasticNucleons;
+    const double impactPar = cnucms_.b; // only needed to avoid passing common var.
+    int nFragments = 0;
+    // number of fragments is limited to 60
+    int AFragments[60];
+    // call fragmentation routine
+    // input: target A, projectile A, number of int. nucleons in projectile, impact
+    // parameter (fm) output: nFragments, AFragments in addition the momenta ar stored
+    // in pf in common fragments, neglected
+    fragm_(kATarget, kAProj, nIntProj, impactPar, nFragments, AFragments);
+
+    // this should not occur but well :)
+    if (nFragments > (int)getMaxNFragments())
+      throw std::runtime_error("Number of nuclear fragments in NUCLIB exceeded!");
+
+    CORSIKA_LOG_DEBUG("number of fragments: " + std::to_string(nFragments));
+    for (int j = 0; j < nFragments; ++j)
+      CORSIKA_LOG_DEBUG("fragment {}: A={} px={} py={} pz={}", j, AFragments[j],
+                        fragments_.ppp[j][0], fragments_.ppp[j][1], fragments_.ppp[j][2]);
+
+    CORSIKA_LOG_DEBUG("adding nuclear fragments to particle stack..");
+    // put nuclear fragments on corsika stack
+    for (int j = 0; j < nFragments; ++j) {
+      Code specCode;
+      const auto nuclA = AFragments[j];
+      // get Z from stability line
+      const auto nuclZ = int(nuclA / 2.15 + 0.7);
+
+      // TODO: do we need to catch single nucleons??
+      if (nuclA == 1)
+        // TODO: sample neutron or proton
+        specCode = Code::Proton;
+      else
+        specCode = Code::Nucleus;
+
+      const HEPMassType mass = get_nucleus_mass(nuclA, nuclZ);
+
+      CORSIKA_LOG_DEBUG("NuclearInteraction: adding fragment: {}", get_name(specCode));
+      CORSIKA_LOG_DEBUG("NuclearInteraction: A,Z: {}, {}", nuclA, nuclZ);
+      CORSIKA_LOG_DEBUG("NuclearInteraction: mass: {} GeV", std::to_string(mass / 1_GeV));
+
+      // CORSIKA 7 way
+      // spectators inherit momentum from original projectile
+      const double mass_ratio = mass / ProjMass;
+
+      CORSIKA_LOG_DEBUG("NuclearInteraction: mass ratio " + std::to_string(mass_ratio));
+
+      auto const Plab = PprojLab * mass_ratio;
+
+      CORSIKA_LOG_DEBUG("NuclearInteraction: fragment momentum: {}",
+                        Plab.getSpaceLikeComponents().getComponents() / 1_GeV);
+
+      if (nuclA == 1)
+        // add nucleon
+        projectile.addSecondary(std::make_tuple(specCode, Plab.getTimeLikeComponent(),
+                                                Plab.getSpaceLikeComponents(), pOrig,
+                                                tOrig));
+      else
+        // add nucleus
+        projectile.addSecondary(std::make_tuple(specCode, Plab.getTimeLikeComponent(),
+                                                Plab.getSpaceLikeComponents(), pOrig,
+                                                tOrig, nuclA, nuclZ));
+    }
+
+    // add elastic nucleons to corsika stack
+    // TODO: the elastic interaction could be external like the inelastic interaction,
+    // e.g. use existing ElasticModel
+    CORSIKA_LOG_DEBUG("adding elastically scattered nucleons to particle stack..");
+    for (int j = 0; j < nElasticNucleons; ++j) {
+      // TODO: sample proton or neutron
+      auto const elaNucCode = Code::Proton;
+
+      // CORSIKA 7 way
+      // elastic nucleons inherit momentum from original projectile
+      // neglecting momentum transfer in interaction
+      const double mass_ratio = get_mass(elaNucCode) / ProjMass;
+      auto const Plab = PprojLab * mass_ratio;
+
+      projectile.addSecondary(std::make_tuple(elaNucCode, Plab.getTimeLikeComponent(),
+                                              Plab.getSpaceLikeComponents(), pOrig,
+                                              tOrig));
+    }
+
+    // add inelastic interactions
+    std::cout << "calculate inelastic nucleon-nucleon interactions.." << std::endl;
+    for (int j = 0; j < nInelNucleons; ++j) {
+      // TODO: sample neutron or proton
+      auto pCode = Code::Proton;
+      // temporarily add to stack, will be removed after interaction in DoInteraction
+      CORSIKA_LOG_DEBUG("inelastic interaction no. {}", j);
+      setup::Stack nucleonStack;
+      auto inelasticNucleon = nucleonStack.addParticle(
+          std::make_tuple(pCode, PprojNucLab.getTimeLikeComponent(),
+                          PprojNucLab.getSpaceLikeComponents(), pOrig, tOrig));
+      inelasticNucleon.setNode(projectile.getNode());
+      // create inelastic interaction for each nucleon
+      CORSIKA_LOG_TRACE("calling HadronicInteraction...");
+      // create new StackView for each of the nucleons
+      setup::StackView nucleon_secondaries(inelasticNucleon);
+      // all inner hadronic event generator
+      hadronicInteraction_.doInteraction(nucleon_secondaries);
+      // inelasticNucleon.Delete(); // this is just a temporary object
+      for (const auto& pSec : nucleon_secondaries) {
+        projectile.addSecondary(std::make_tuple(pSec.getPID(), pSec.getEnergy(),
+                                                pSec.getMomentum(), pSec.getPosition(),
+                                                pSec.getTime()));
+      }
+    }
+
+    CORSIKA_LOG_DEBUG("NuclearInteraction: DoInteraction: done");
+  }
+
+} // namespace corsika::sibyll
diff --git a/corsika/detail/modules/sibyll/ParticleConversion.inl b/corsika/detail/modules/sibyll/ParticleConversion.inl
new file mode 100644
index 0000000000000000000000000000000000000000..d4459a7ba026d804ee3eb6aecc90cdeb81c2feb0
--- /dev/null
+++ b/corsika/detail/modules/sibyll/ParticleConversion.inl
@@ -0,0 +1,26 @@
+/*
+ * (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/core/ParticleProperties.hpp>
+
+#include <sibyll2.3d.hpp>
+
+namespace corsika::sibyll {
+
+  inline HEPMassType getSibyllMass(Code const pCode) {
+    if (pCode == corsika::Code::Nucleus)
+      throw std::runtime_error("Cannot getMass() of particle::Nucleus -> unspecified");
+    auto sCode = convertToSibyllRaw(pCode);
+    if (sCode == 0)
+      throw std::runtime_error("getSibyllMass: unknown particle!");
+    else
+      return sqrt(get_sibyll_mass2(sCode)) * 1_GeV;
+  }
+} // namespace corsika::sibyll
\ No newline at end of file
diff --git a/corsika/detail/modules/tracking/Intersect.inl b/corsika/detail/modules/tracking/Intersect.inl
new file mode 100644
index 0000000000000000000000000000000000000000..fef16a955801992b636516605eec118d24fd3a04
--- /dev/null
+++ b/corsika/detail/modules/tracking/Intersect.inl
@@ -0,0 +1,111 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/core/Logging.hpp>
+#include <corsika/framework/geometry/Intersections.hpp>
+
+#include <limits>
+
+namespace corsika {
+
+  template <typename TDerived>
+  template <typename TParticle>
+  inline auto Intersect<TDerived>::nextIntersect(TParticle const& particle,
+                                                 TimeType const step_limit) const {
+
+    typedef typename std::remove_reference<decltype(*particle.getNode())>::type node_type;
+
+    node_type& volumeNode =
+        *particle.getNode(); // current "logical" node, from previous tracking step
+
+    CORSIKA_LOG_DEBUG("volumeNode={}, numericallyInside={} ", fmt::ptr(&volumeNode),
+                      volumeNode.getVolume().contains(particle.getPosition()));
+
+    // start values:
+    TimeType minTime = step_limit;
+    node_type* minNode = &volumeNode;
+
+    // determine the first geometric collision with any other Volume boundary
+
+    // 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 =
+        TDerived::intersect(particle, volumeNode);
+    CORSIKA_LOG_TRACE("curr node {}, parent node {}, hasIntersections={} ",
+                      fmt::ptr(&volumeNode), fmt::ptr(volumeNode.getParent()),
+                      time_intersections_curr.hasIntersections());
+    if (time_intersections_curr.hasIntersections()) {
+      CORSIKA_LOG_DEBUG("intersection times with currentLogicalVolumeNode: {} s and {} s",
+                        time_intersections_curr.getEntry() / 1_s,
+                        time_intersections_curr.getExit() / 1_s);
+      if (time_intersections_curr.getExit() <= minTime) {
+        minTime =
+            time_intersections_curr.getExit(); // we exit currentLogicalVolumeNode here
+        minNode = volumeNode.getParent();
+      }
+    }
+
+    // where do we collide with any of the next-tree-level volumes
+    // entirely contained by currentLogicalVolumeNode
+    for (const auto& node : volumeNode.getChildNodes()) {
+
+      const Intersections time_intersections = TDerived::intersect(particle, *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,
+                        time_intersections.getExit() / 1_s);
+
+      const auto t_entry = time_intersections.getEntry();
+      const auto 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
+      // KNOW we can't yet be in this volume yet, so we HAVE TO
+      // enter it IF exit point is not also in the "past", AND if
+      // extry point is [[much much]] closer than exit point
+      // (because we might have already numerically "exited" it)!
+      if (t_exit > 0_s && t_entry <= minTime &&
+          -t_entry < t_exit) { // protection agains numerical problem, when we already
+                               // _exited_ before
+                               // enter chile volume here
+        minTime = t_entry;
+        minNode = node.get();
+      }
+    }
+
+    // these are volumes from the previous tree-level that are cut-out partly from the
+    // current volume
+    for (node_type* node : volumeNode.getExcludedNodes()) {
+
+      const Intersections 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();
+      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
+      // KNOW we can't yet be in this volume yet, so we HAVE TO
+      // enter it IF exit point is not also in the "past"!
+      if (t_exit > 0_s && t_entry <= minTime) { // enter volumen child here
+        minTime = t_entry;
+        minNode = node;
+      }
+    }
+    CORSIKA_LOG_TRACE("t-intersect: {}, node {} ", minTime, fmt::ptr(minNode));
+    return std::make_tuple(minTime, minNode);
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/modules/tracking/TrackingLeapFrogCurved.inl b/corsika/detail/modules/tracking/TrackingLeapFrogCurved.inl
new file mode 100644
index 0000000000000000000000000000000000000000..8545d2bbdc434501070f6a1b11232dc93184c47d
--- /dev/null
+++ b/corsika/detail/modules/tracking/TrackingLeapFrogCurved.inl
@@ -0,0 +1,259 @@
+/*
+ * (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 <corsika/modules/tracking/TrackingStraight.hpp> // for neutral particles
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Plane.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+#include <corsika/framework/geometry/LeapFrogTrajectory.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/geometry/Intersections.hpp>
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/utility/QuarticSolver.hpp>
+#include <corsika/framework/core/Logging.hpp>
+#include <corsika/modules/tracking/Intersect.hpp>
+
+#include <type_traits>
+#include <utility>
+
+namespace corsika {
+
+  namespace tracking_leapfrog_curved {
+
+    template <typename TParticle>
+    inline auto make_LeapFrogStep(TParticle const& particle, LengthType steplength) {
+      if (particle.getMomentum().norm() == 0_GeV) {
+        return std::make_tuple(particle.getPosition(), particle.getMomentum() / 1_GeV,
+                               double(0));
+      } // charge of the particle
+      int const chargeNumber = particle.getChargeNumber();
+      auto const* currentLogicalVolumeNode = particle.getNode();
+      MagneticFieldVector const& magneticfield =
+          currentLogicalVolumeNode->getModelProperties().getMagneticField(
+              particle.getPosition());
+      VelocityVector velocity =
+          particle.getMomentum() / particle.getEnergy() * constants::c;
+      decltype(meter / (second * volt)) k =
+          chargeNumber * constants::cSquared * 1_eV /
+          (velocity.getNorm() * particle.getEnergy() * 1_V);
+      DirectionVector direction = velocity.normalized();
+      auto position = particle.getPosition(); // First Movement
+      // assuming magnetic field does not change during movement
+      position =
+          position + direction * steplength / 2; // Change of direction by magnetic field
+      direction =
+          direction + direction.cross(magneticfield) * steplength * k; // Second Movement
+      position = position + direction * steplength / 2;
+      auto const steplength_true = steplength * (1 + direction.getNorm()) / 2;
+      return std::make_tuple(position, direction.normalized(), steplength_true);
+    }
+
+    template <typename TParticle>
+    inline auto Tracking::getTrack(TParticle const& particle) {
+      VelocityVector const initialVelocity =
+          particle.getMomentum() / particle.getEnergy() * constants::c;
+
+      auto const position = particle.getPosition();
+      CORSIKA_LOG_DEBUG(
+          "Tracking pid: {}"
+          " , E = {} GeV",
+          particle.getPID(), particle.getEnergy() / 1_GeV);
+      CORSIKA_LOG_DEBUG("Tracking pos: {}", position.getCoordinates());
+      CORSIKA_LOG_DEBUG("Tracking   E: {} GeV", particle.getEnergy() / 1_GeV);
+      CORSIKA_LOG_DEBUG("Tracking   p: {} GeV",
+                        particle.getMomentum().getComponents() / 1_GeV);
+      CORSIKA_LOG_DEBUG("Tracking   v: {} ", initialVelocity.getComponents());
+
+      typedef
+          typename std::remove_reference<decltype(*particle.getNode())>::type node_type;
+      node_type& volumeNode = *particle.getNode();
+
+      // for the event of magnetic fields and curved trajectories, we need to limit
+      // maximum step-length since we need to follow curved
+      // trajectories segment-wise -- at least if we don't employ concepts as "Helix
+      // Trajectories" or similar
+      MagneticFieldVector const& magneticfield =
+          volumeNode.getModelProperties().getMagneticField(position);
+      MagneticFluxType const magnitudeB = magneticfield.getNorm();
+      int const chargeNumber = particle.getChargeNumber();
+      bool const no_deflection = chargeNumber == 0 || magnitudeB == 0_T;
+
+      if (no_deflection) { return getLinearTrajectory(particle); }
+
+      HEPMomentumType const pAlongB_delta =
+          (particle.getMomentum() -
+           particle.getMomentum().getParallelProjectionOnto(magneticfield))
+              .getNorm();
+
+      if (pAlongB_delta == 0_GeV) {
+        // particle travel along, parallel to magnetic field. Rg is
+        // "0", but for purpose of step limit we return infinity here.
+        CORSIKA_LOG_TRACE("pAlongB_delta is 0_GeV --> parallel");
+        return getLinearTrajectory(particle);
+      }
+
+      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();
+      CORSIKA_LOG_DEBUG("gyroradius {}, steplimit: {} = {}", gyroradius, steplimit,
+                        steplimit_time);
+
+      // traverse the environment volume tree and find next
+      // intersection
+      auto [minTime, minNode] = nextIntersect(particle, steplimit_time);
+
+      const auto k =
+          chargeNumber * constants::cSquared * 1_eV / (particle.getEnergy() * 1_V);
+      return std::make_tuple(
+          LeapFrogTrajectory(position, initialVelocity, magneticfield, k,
+                             minTime), // trajectory
+          minNode);                    // next volume node
+    }
+
+    template <typename TParticle, typename TMedium>
+    inline Intersections Tracking::intersect(const TParticle& particle,
+                                             const Sphere& sphere,
+                                             const TMedium& medium) {
+
+      if (sphere.getRadius() == 1_km * std::numeric_limits<double>::infinity()) {
+        return Intersections();
+      }
+
+      int const chargeNumber = particle.getChargeNumber();
+      auto const& position = particle.getPosition();
+      MagneticFieldVector const& magneticfield = medium.getMagneticField(position);
+
+      VelocityVector const velocity =
+          particle.getMomentum() / particle.getEnergy() * constants::c;
+      DirectionVector const directionBefore =
+          velocity.normalized(); // determine steplength to next volume
+
+      auto const projectedDirection = directionBefore.cross(magneticfield);
+      auto const projectedDirectionSqrNorm = projectedDirection.getSquaredNorm();
+      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);
+      }
+
+      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);
+      LengthType d_enter, d_exit;
+      int first = 0, first_entry = 0, first_exit = 0;
+      for (int i = 0; i < 4; i++) {
+        if (solutions[i].imag() == 0) {
+          LengthType const dist = solutions[i].real() * 1_m;
+          CORSIKA_LOG_TRACE("Solution (real) for current Volume: {} ", dist);
+          if (numericallyInside) {
+            // there must be an entry (negative) and exit (positive) solution
+            if (dist < -0.0001_m) { // security margin to assure transfer to next
+                                    // logical volume
+              if (first_entry == 0) {
+                d_enter = dist;
+              } else {
+                d_enter = std::max(d_enter, dist); // closest negative to zero (-1e-4) m
+              }
+              first_entry++;
+
+            } else { // thus, dist >= -0.0001_m
+
+              if (first_exit == 0) {
+                d_exit = dist;
+              } else {
+                d_exit = std::min(d_exit, dist); // closest positive to zero (-1e-4) m
+              }
+              first_exit++;
+            }
+            first = int(first_exit > 0) + int(first_entry > 0);
+
+          } else { // thus, numericallyInside == false
+
+            // both physical solutions (entry, exit) must be positive, and as small as
+            // possible
+            if (dist < -0.0001_m) { // need small numerical margin, to assure transport
+              // into next logical volume
+              continue;
+            }
+            if (first == 0) {
+              d_enter = dist;
+            } else {
+              if (dist < d_enter) {
+                d_exit = d_enter;
+                d_enter = dist;
+              } else {
+                d_exit = dist;
+              }
+            }
+            first++;
+          }
+        } // loop over solutions
+      }
+      delete[] solutions;
+
+      if (first != 2) { // entry and exit points found
+        CORSIKA_LOG_DEBUG("no intersection! count={}", first);
+        return Intersections();
+      }
+      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());
+      }
+      throw std::runtime_error(
+          "The Volume type provided is not supported in intersect(particle, node)");
+    }
+
+    template <typename TParticle>
+    inline auto Tracking::getLinearTrajectory(TParticle& particle) {
+
+      // perform simple linear tracking
+      auto [straightTrajectory, minNode] = straightTracking_.getTrack(particle);
+
+      // return as leap-frog trajectory
+      return std::make_tuple(
+          LeapFrogTrajectory(
+              straightTrajectory.getLine().getStartPoint(),
+              straightTrajectory.getLine().getVelocity(),
+              MagneticFieldVector(particle.getPosition().getCoordinateSystem(), 0_T, 0_T,
+                                  0_T),
+              square(0_m) / (square(1_s) * 1_V),
+              straightTrajectory.getDuration()), // trajectory
+          minNode);                              // next volume node
+    }
+
+  } // namespace tracking_leapfrog_curved
+
+} // namespace corsika
diff --git a/corsika/detail/modules/tracking/TrackingLeapFrogStraight.inl b/corsika/detail/modules/tracking/TrackingLeapFrogStraight.inl
new file mode 100644
index 0000000000000000000000000000000000000000..132a87e5d80abc129bc3375b4cab921c701a565c
--- /dev/null
+++ b/corsika/detail/modules/tracking/TrackingLeapFrogStraight.inl
@@ -0,0 +1,186 @@
+/*
+ * (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 <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>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <corsika/modules/tracking/TrackingStraight.hpp>
+
+#include <type_traits>
+#include <utility>
+
+namespace corsika {
+
+  namespace tracking_leapfrog_straight {
+
+    template <typename Particle>
+    inline auto Tracking::getTrack(Particle& particle) {
+      VelocityVector initialVelocity =
+          particle.getMomentum() / particle.getEnergy() * constants::c;
+
+      const Point initialPosition = particle.getPosition();
+      CORSIKA_LOG_DEBUG(
+          "TrackingB pid: {}"
+          " , E = {} GeV",
+          particle.getPID(), particle.getEnergy() / 1_GeV);
+      CORSIKA_LOG_DEBUG("TrackingB pos: {}", initialPosition.getCoordinates());
+      CORSIKA_LOG_DEBUG("TrackingB   E: {} GeV", particle.getEnergy() / 1_GeV);
+      CORSIKA_LOG_DEBUG("TrackingB   p: {} GeV",
+                        particle.getMomentum().getComponents() / 1_GeV);
+      CORSIKA_LOG_DEBUG("TrackingB   v: {} ", initialVelocity.getComponents());
+
+      typedef decltype(particle.getNode()) node_type;
+      node_type const volumeNode = particle.getNode();
+
+      // check if particle is moving at all
+      auto const absVelocity = initialVelocity.getNorm();
+      if (absVelocity * 1_s == 0_m) {
+        return std::make_tuple(
+            StraightTrajectory(Line(initialPosition, initialVelocity), 0_s), volumeNode);
+      }
+
+      // charge of the particle, and magnetic field
+      const int chargeNumber = particle.getChargeNumber();
+      auto magneticfield =
+          volumeNode->getModelProperties().getMagneticField(initialPosition);
+      const auto 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] =
+          tracking_line::Tracking::getTrack(particle);
+      { [[maybe_unused]] auto& initialTrackNextVolume_dum = initialTrackNextVolume; }
+      const auto initialTrackLength = initialTrack.getLength(1);
+
+      CORSIKA_LOG_DEBUG("initialTrack(0)={}, initialTrack(1)={}, initialTrackLength={}",
+                        initialTrack.getPosition(0).getCoordinates(),
+                        initialTrack.getPosition(1).getCoordinates(), initialTrackLength);
+
+      // if particle is non-deflectable, we are done:
+      if (no_deflection) {
+        CORSIKA_LOG_DEBUG("no deflection. tracking finished");
+        return std::make_tuple(initialTrack, initialTrackNextVolume);
+      }
+
+      HEPMomentumType const pAlongB_delta =
+          (particle.getMomentum() -
+           particle.getMomentum().getParallelProjectionOnto(magneticfield))
+              .getNorm();
+
+      if (pAlongB_delta == 0_GeV) {
+        // particle travel along, parallel to magnetic field. Rg is
+        // "0", but for purpose of step limit we return infinity here.
+        CORSIKA_LOG_TRACE("pAlongB_delta is 0_GeV --> parallel");
+        return std::make_tuple(initialTrack, initialTrackNextVolume);
+      }
+
+      LengthType const gyroradius =
+          (pAlongB_delta * 1_V / (constants::c * abs(chargeNumber) * magnitudeB * 1_eV));
+
+      // we need to limit maximum step-length since we absolutely
+      // 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;
+      CORSIKA_LOG_DEBUG("gyroradius {}, Steplimit: {}", gyroradius, steplimit);
+
+      // calculate first halve step for "steplimit"
+      auto const initialMomentum = particle.getMomentum();
+      auto const absMomentum = initialMomentum.getNorm();
+      DirectionVector const direction = initialVelocity.normalized();
+
+      // avoid any intersections within first halve steplength
+      LengthType const firstHalveSteplength =
+          std::min(steplimit, initialTrackLength * firstFraction_);
+
+      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 =
+          direction + direction.cross(magneticfield) * firstHalveSteplength * 2 * k;
+      const auto 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(),
+          new_direction_norm,
+          acos(std::min(1.0, direction.dot(new_direction) / new_direction_norm)) * 180 /
+              M_PI);
+
+      // check, where the second halve-step direction has geometric intersections
+      particle.setPosition(position_mid);
+      particle.setMomentum(new_direction * absMomentum);
+      const auto [finalTrack, finalTrackNextVolume] =
+          tracking_line::Tracking::getTrack(particle);
+      particle.setPosition(initialPosition); // this is not nice...
+      particle.setMomentum(initialMomentum); // this is not nice...
+
+      LengthType const finalTrackLength = finalTrack.getLength(1);
+      LengthType const secondLeapFrogLength = firstHalveSteplength * new_direction_norm;
+
+      // check if volume transition is obvious, OR
+      // for numerical reasons, particles slighly bend "away" from a
+      // volume boundary have a very hard time to cross the border,
+      // thus, if secondLeapFrogLength is just slighly shorter (1e-4m) than
+      // finalTrackLength we better just [extend the
+      // secondLeapFrogLength slightly and] force the volume
+      // crossing:
+      bool const switch_volume = finalTrackLength - 0.0001_m <= secondLeapFrogLength;
+      LengthType const secondHalveStepLength =
+          std::min(secondLeapFrogLength, finalTrackLength);
+
+      CORSIKA_LOG_DEBUG(
+          "finalTrack(0)={}, finalTrack(1)={}, finalTrackLength={}, "
+          "secondLeapFrogLength={}, secondHalveStepLength={}, "
+          "secondLeapFrogLength-finalTrackLength={}, "
+          "secondHalveStepLength-finalTrackLength={}, "
+          "nextVol={}, transition={}",
+          finalTrack.getPosition(0).getCoordinates(),
+          finalTrack.getPosition(1).getCoordinates(), finalTrackLength,
+          secondLeapFrogLength, secondHalveStepLength,
+          secondLeapFrogLength - finalTrackLength,
+          secondHalveStepLength - finalTrackLength, fmt::ptr(finalTrackNextVolume),
+          switch_volume);
+
+      // perform the second halve-step
+      auto const new_direction_normalized = new_direction.normalized();
+      const Point finalPosition =
+          position_mid + new_direction_normalized * secondHalveStepLength;
+
+      const LengthType totalStep = firstHalveSteplength + secondHalveStepLength;
+      const auto delta_pos = finalPosition - initialPosition;
+      const auto distance = delta_pos.getNorm();
+
+      return std::make_tuple(
+          StraightTrajectory(
+              Line(initialPosition,
+                   (distance == 0_m ? initialVelocity
+                                    : delta_pos.normalized() * absVelocity)),
+              distance / absVelocity,  // straight distance
+              totalStep / absVelocity, // bend distance
+              initialVelocity,
+              new_direction_normalized * absVelocity), // trajectory
+          (switch_volume ? finalTrackNextVolume : volumeNode));
+    }
+
+  } // namespace tracking_leapfrog_straight
+
+} // namespace corsika
diff --git a/corsika/detail/modules/tracking/TrackingStraight.inl b/corsika/detail/modules/tracking/TrackingStraight.inl
new file mode 100644
index 0000000000000000000000000000000000000000..2c284b4d01dddc3be3322dc305a84f6f836bd5e5
--- /dev/null
+++ b/corsika/detail/modules/tracking/TrackingStraight.inl
@@ -0,0 +1,100 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Plane.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/geometry/StraightTrajectory.hpp>
+#include <corsika/framework/geometry/Intersections.hpp>
+#include <corsika/framework/core/Logging.hpp>
+#include <corsika/modules/tracking/Intersect.hpp>
+
+#include <type_traits>
+#include <utility>
+
+namespace corsika::tracking_line {
+
+  template <typename TParticle>
+  inline auto Tracking::getTrack(TParticle const& particle) {
+    VelocityVector const initialVelocity =
+        particle.getMomentum() / particle.getEnergy() * constants::c;
+
+    auto const initialPosition = particle.getPosition();
+    CORSIKA_LOG_DEBUG(
+        "Tracking pid: {}"
+        " , E = {} GeV",
+        particle.getPID(), particle.getEnergy() / 1_GeV);
+    CORSIKA_LOG_DEBUG("Tracking pos: {}", initialPosition.getCoordinates());
+    CORSIKA_LOG_DEBUG("Tracking   E: {} GeV", particle.getEnergy() / 1_GeV);
+    CORSIKA_LOG_DEBUG("Tracking   p: {} GeV",
+                      particle.getMomentum().getComponents() / 1_GeV);
+    CORSIKA_LOG_DEBUG("Tracking   v: {} ", initialVelocity.getComponents());
+
+    // traverse the environment volume tree and find next
+    // intersection
+    auto [minTime, minNode] = nextIntersect(particle);
+
+    return std::make_tuple(StraightTrajectory(Line(initialPosition, initialVelocity),
+                                              minTime), // trajectory
+                           minNode);                    // next volume node
+  }
+
+  template <typename TParticle, typename TMedium>
+  Intersections Tracking::intersect(TParticle const& particle, Sphere const& sphere,
+                                    TMedium const&) {
+    auto const delta = particle.getPosition() - sphere.getCenter();
+    auto const velocity = particle.getMomentum() / particle.getEnergy() * constants::c;
+    auto const vSqNorm = velocity.getSquaredNorm();
+    auto const R = sphere.getRadius();
+
+    auto const vDotDelta = velocity.dot(delta);
+    auto const discriminant =
+        vDotDelta * vDotDelta - vSqNorm * (delta.getSquaredNorm() - R * R);
+
+    if (discriminant.magnitude() > 0) {
+      auto const sqDisc = sqrt(discriminant);
+      auto const invDenom = 1 / vSqNorm;
+      return Intersections((-vDotDelta - sqDisc) * invDenom,
+                           (-vDotDelta + sqDisc) * invDenom);
+    }
+    return Intersections();
+  }
+
+  template <typename TParticle, typename TBaseNodeType>
+  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());
+    }
+    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&) {
+    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);
+
+    return Intersections(c.magnitude() == 0
+                             ? std::numeric_limits<TimeType::value_type>::infinity() * 1_s
+                             : n.dot(delta) / c);
+  }
+
+} // namespace corsika::tracking_line
diff --git a/corsika/detail/modules/urqmd/UrQMD.inl b/corsika/detail/modules/urqmd/UrQMD.inl
new file mode 100644
index 0000000000000000000000000000000000000000..372d2d5925450b4798bd4650015e856e643d5de4
--- /dev/null
+++ b/corsika/detail/modules/urqmd/UrQMD.inl
@@ -0,0 +1,306 @@
+/*
+ * (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 <corsika/modules/urqmd/UrQMD.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/QuantityVector.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+#include <algorithm>
+#include <functional>
+#include <iostream>
+
+#include <urqmd.hpp>
+
+namespace corsika::urqmd {
+
+  UrQMD::UrQMD() { ::urqmd::iniurqmdc8_(); }
+
+  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 &&
+        !is_nucleus(vTargetCode)) { // both particles are "special"
+      auto const mProj = get_mass(vProjectileCode);
+      auto const mTar = get_mass(vTargetCode);
+      double sqrtS =
+          sqrt(static_pow<2>(mProj) + static_pow<2>(mTar) + 2 * vLabEnergy * mTar) *
+          (1 / 1_GeV);
+
+      // we must set some UrQMD globals first...
+      auto const [ityp, iso3] = convertToUrQMD(vProjectileCode);
+      ::urqmd::inputs_.spityp[0] = ityp;
+      ::urqmd::inputs_.spiso3[0] = iso3;
+
+      auto const [itypTar, iso3Tar] = convertToUrQMD(vTargetCode);
+      ::urqmd::inputs_.spityp[1] = itypTar;
+      ::urqmd::inputs_.spiso3[1] = iso3Tar;
+
+      int one = 1;
+      int two = 2;
+      return ::urqmd::sigtot_(one, two, sqrtS) * 1_mb;
+    } else {
+      int const Ap = vAProjectile;
+      int const At = is_nucleus(vTargetCode) ? get_nucleus_A(vTargetCode) : 1;
+
+      double const maxImpact = ::urqmd::nucrad_(Ap) + ::urqmd::nucrad_(At) +
+                               2 * ::urqmd::options_.CTParam[30 - 1];
+      return 10_mb * M_PI * static_pow<2>(maxImpact);
+      // is a constant cross-section really reasonable?
+    }
+  }
+
+  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 {
+    // TODO: return 0 for non-hadrons?
+
+    auto const projectileCode = vProjectile.getPID();
+    auto const projectileEnergyLab = vProjectile.getEnergy();
+
+    if (projectileCode == Code::K0Long) {
+      return 0.5 * (getCrossSection(Code::K0, vTargetCode, projectileEnergyLab) +
+                    getCrossSection(Code::K0Bar, vTargetCode, projectileEnergyLab));
+    }
+
+    int const Ap = (projectileCode == Code::Nucleus) ? vProjectile.getNuclearA() : 1;
+    return getCrossSection(projectileCode, vTargetCode, projectileEnergyLab, Ap);
+  }
+
+  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.
+    // TODO: Charmed mesons should be added to the list, too
+
+    static Code const validProjectileCodes[] = {
+        Code::Nucleus,     Code::Proton, Code::AntiProton, Code::Neutron,
+        Code::AntiNeutron, Code::PiPlus, Code::PiMinus,    Code::KPlus,
+        Code::KMinus,      Code::K0,     Code::K0Bar,      Code::K0Long};
+
+    return std::find(std::cbegin(validProjectileCodes), std::cend(validProjectileCodes),
+                     vCode) != std::cend(validProjectileCodes);
+  }
+
+  template <typename TParticle>
+  GrammageType UrQMD::getInteractionLength(TParticle const& vParticle) const {
+
+    if (!canInteract(vParticle.getPID())) {
+      // we could do the canInteract check in getCrossSection, too but if
+      // we do it here we have the advantage of avoiding the loop
+      return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm);
+    }
+
+    auto const& mediumComposition =
+        vParticle.getNode()->getModelProperties().getNuclearComposition();
+    using namespace std::placeholders;
+
+    CrossSectionType const weightedProdCrossSection = mediumComposition.getWeightedSum(
+        std::bind(&UrQMD::getCrossSection<decltype(vParticle)>, this, vParticle, _1));
+
+    return mediumComposition.getAverageMassNumber() * constants::u /
+           weightedProdCrossSection;
+  }
+
+  template <typename TView>
+  void UrQMD::doInteraction(TView& view) {
+
+    auto projectile = view.getProjectile();
+
+    auto projectileCode = projectile.getPID();
+    auto const projectileEnergyLab = projectile.getEnergy();
+    auto const& projectileMomentumLab = projectile.getMomentum();
+    auto const& projectilePosition = projectile.getPosition();
+    auto const projectileTime = projectile.getTime();
+
+    // sample target particle
+    auto const& mediumComposition =
+        projectile.getNode()->getModelProperties().getNuclearComposition();
+    auto const componentCrossSections = std::invoke([&]() {
+      auto const& components = mediumComposition.getComponents();
+      std::vector<CrossSectionType> crossSections;
+      crossSections.reserve(components.size());
+
+      for (auto const c : components) {
+        crossSections.push_back(getCrossSection(projectile, c));
+      }
+
+      return crossSections;
+    });
+
+    auto const targetCode = mediumComposition.sampleTarget(componentCrossSections, RNG_);
+    auto const targetA = get_nucleus_A(targetCode);
+    auto const targetZ = get_nucleus_Z(targetCode);
+
+    ::urqmd::inputs_.nevents = 1;
+    ::urqmd::sys_.eos = 0; // could be configurable in principle
+    ::urqmd::inputs_.outsteps = 1;
+    ::urqmd::sys_.nsteps = 1;
+
+    // initialization regarding projectile
+    if (Code::Nucleus == projectileCode) {
+      // is this everything?
+      ::urqmd::inputs_.prspflg = 0;
+
+      ::urqmd::sys_.Ap = projectile.getNuclearA();
+      ::urqmd::sys_.Zp = projectile.getNuclearZ();
+      ::urqmd::rsys_.ebeam = (projectileEnergyLab - projectile.getMass()) * (1 / 1_GeV) /
+                             projectile.getNuclearA();
+
+      ::urqmd::rsys_.bdist = ::urqmd::nucrad_(targetA) +
+                             ::urqmd::nucrad_(::urqmd::sys_.Ap) +
+                             2 * ::urqmd::options_.CTParam[30 - 1];
+
+      int const id = 1;
+      ::urqmd::cascinit_(::urqmd::sys_.Zp, ::urqmd::sys_.Ap, id);
+    } else {
+      ::urqmd::inputs_.prspflg = 1;
+      ::urqmd::sys_.Ap =
+          1; // even for non-baryons this has to be set, see vanilla UrQMD.f
+      ::urqmd::rsys_.bdist = ::urqmd::nucrad_(targetA) + ::urqmd::nucrad_(1) +
+                             2 * ::urqmd::options_.CTParam[30 - 1];
+      ::urqmd::rsys_.ebeam = (projectileEnergyLab - projectile.getMass()) * (1 / 1_GeV);
+
+      if (projectileCode == Code::K0Long) {
+        projectileCode = booleanDist_(RNG_) ? Code::K0 : Code::K0Bar;
+      } else if (projectileCode == Code::K0Short) {
+        throw std::runtime_error("K0Short should not interact");
+      }
+
+      auto const [ityp, iso3] = convertToUrQMD(projectileCode);
+      // todo: conversion of K_long/short into strong eigenstates;
+      ::urqmd::inputs_.spityp[0] = ityp;
+      ::urqmd::inputs_.spiso3[0] = iso3;
+    }
+
+    // initilazation regarding target
+    if (is_nucleus(targetCode)) {
+      ::urqmd::sys_.Zt = targetZ;
+      ::urqmd::sys_.At = targetA;
+      ::urqmd::inputs_.trspflg = 0; // nucleus as target
+      int const id = 2;
+      ::urqmd::cascinit_(::urqmd::sys_.Zt, ::urqmd::sys_.At, id);
+    } else {
+      ::urqmd::inputs_.trspflg = 1; // special particle as target
+      auto const [ityp, iso3] = convertToUrQMD(targetCode);
+      ::urqmd::inputs_.spityp[1] = ityp;
+      ::urqmd::inputs_.spiso3[1] = iso3;
+    }
+
+    int iflb = 0; // flag for retrying interaction in case of empty event, 0 means retry
+    ::urqmd::urqmd_(iflb);
+
+    // now retrieve secondaries from UrQMD
+    auto const& originalCS = projectileMomentumLab.getCoordinateSystem();
+    CoordinateSystemPtr const& zAxisFrame =
+        make_rotationToZ(originalCS, projectileMomentumLab);
+
+    for (int i = 0; i < ::urqmd::sys_.npart; ++i) {
+      auto code = convertFromUrQMD(::urqmd::isys_.ityp[i], ::urqmd::isys_.iso3[i]);
+      if (code == Code::K0 || code == Code::K0Bar) {
+        code = booleanDist_(RNG_) ? Code::K0Short : Code::K0Long;
+      }
+
+      // "coor_.p0[i] * 1_GeV" is likely off-shell as UrQMD doesn't preserve masses well
+      auto momentum =
+          Vector(zAxisFrame,
+                 QuantityVector<dimensionless_d>{
+                     ::urqmd::coor_.px[i], ::urqmd::coor_.py[i], ::urqmd::coor_.pz[i]} *
+                     1_GeV);
+
+      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;
+
+      projectile.addSecondary(
+          std::make_tuple(code, energy, momentum, projectilePosition, projectileTime));
+    }
+
+    std::cout << "UrQMD generated " << ::urqmd::sys_.npart << " secondaries!"
+              << std::endl;
+  }
+
+  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
+      throw std::runtime_error("UrQMD pdgid() returned 0");
+    }
+    auto const pdg = static_cast<PDGCode>(pdgInt);
+    return convert_from_PDG(pdg);
+  }
+
+  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
+        {111, {101, 0}},     // pi0
+        {211, {101, 2}},     // pi+
+        {-211, {101, -2}},   // pi-
+        {321, {106, 1}},     // K+
+        {-321, {-106, -1}},  // K-
+        {311, {106, -1}},    // K0
+        {-311, {-106, 1}},   // K0bar
+        {2212, {1, 1}},      // p
+        {2112, {1, -1}},     // n
+        {-2212, {-1, -1}},   // pbar
+        {-2112, {-1, 1}},    // nbar
+        {221, {102, 0}},     // eta
+        {213, {104, 2}},     // rho+
+        {-213, {104, -2}},   // rho-
+        {113, {104, 0}},     // rho0
+        {323, {108, 2}},     // K*+
+        {-323, {108, -2}},   // K*-
+        {313, {108, 0}},     // K*0
+        {-313, {-108, 0}},   // K*0-bar
+        {223, {103, 0}},     // omega
+        {333, {109, 0}},     // phi
+        {3222, {40, 2}},     // Sigma+
+        {3212, {40, 0}},     // Sigma0
+        {3112, {40, -2}},    // Sigma-
+        {3322, {49, 0}},     // Xi0
+        {3312, {49, -1}},    // Xi-
+        {3122, {27, 0}},     // Lambda0
+        {2224, {17, 4}},     // Delta++
+        {2214, {17, 2}},     // Delta+
+        {2114, {17, 0}},     // Delta0
+        {1114, {17, -2}},    // Delta-
+        {3224, {41, 2}},     // Sigma*+
+        {3214, {41, 0}},     // Sigma*0
+        {3114, {41, -2}},    // Sigma*-
+        {3324, {50, 0}},     // Xi*0
+        {3314, {50, -1}},    // Xi*-
+        {3334, {55, 0}},     // Omega-
+        {411, {133, 2}},     // D+
+        {-411, {133, -2}},   // D-
+        {421, {133, 0}},     // D0
+        {-421, {-133, 0}},   // D0-bar
+        {441, {107, 0}},     // etaC
+        {431, {138, 1}},     // Ds+
+        {-431, {138, -1}},   // Ds-
+        {433, {139, 1}},     // Ds*+
+        {-433, {139, -1}},   // Ds*-
+        {413, {134, 1}},     // D*+
+        {-413, {134, -1}},   // D*-
+        {10421, {134, 0}},   // D*0
+        {-10421, {-134, 0}}, // D*0-bar
+        {443, {135, 0}},     // jpsi
+    };
+
+    return mapPDGToUrQMD.at(static_cast<int>(get_PDG(code)));
+  }
+
+} // namespace corsika::urqmd
diff --git a/corsika/detail/setup/SetupEnvironment.inl b/corsika/detail/setup/SetupEnvironment.inl
new file mode 100644
index 0000000000000000000000000000000000000000..39b129bd18e1df946b807ec3c1b204d89522e9c1
--- /dev/null
+++ b/corsika/detail/setup/SetupEnvironment.inl
@@ -0,0 +1,14 @@
+/*
+ * (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 <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/CoordinateSystem.hpp>
+
+#include <limits>
diff --git a/corsika/detail/setup/SetupStack.hpp b/corsika/detail/setup/SetupStack.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d31e940f6371826772f0c29a4915663a00397f28
--- /dev/null
+++ b/corsika/detail/setup/SetupStack.hpp
@@ -0,0 +1,57 @@
+/*
+ * (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 <corsika/framework/stack/CombinedStack.hpp>
+#include <corsika/stack/GeometryNodeStackExtension.hpp>
+#include <corsika/stack/NuclearStackExtension.hpp>
+#include <corsika/stack/history/HistorySecondaryProducer.hpp>
+#include <corsika/stack/history/HistoryStackExtension.hpp>
+
+#include <corsika/setup/SetupEnvironment.hpp>
+
+namespace corsika {
+
+  namespace setup::detail {
+
+    // ------------------------------------------
+    // add geometry node tracking data to stack:
+
+    // the GeometryNode stack needs to know the type of geometry-nodes from the
+    // environment:
+    template <typename TStackIter>
+    using SetupGeometryDataInterface =
+        typename node::MakeGeometryDataInterface<TStackIter, setup::Environment>::type;
+
+    // combine particle data stack with geometry information for tracking
+    template <typename TStackIter>
+    using StackWithGeometryInterface =
+        CombinedParticleInterface<nuclear_stack::ParticleDataStack::pi_type,
+                                  SetupGeometryDataInterface, TStackIter>;
+
+    using StackWithGeometry = CombinedStack<
+        typename nuclear_stack::ParticleDataStack::stack_implementation_type,
+        node::GeometryData<setup::Environment>, StackWithGeometryInterface>;
+
+    // ------------------------------------------
+    // Add [optional] history data to stack, too:
+
+    // combine dummy stack with geometry information for tracking
+    template <typename TStackIter>
+    using StackWithHistoryInterface =
+        CombinedParticleInterface<StackWithGeometry::pi_type,
+                                  history::HistoryEventDataInterface, TStackIter>;
+
+    using StackWithHistory =
+        CombinedStack<typename StackWithGeometry::stack_implementation_type,
+                      history::HistoryEventData, StackWithHistoryInterface>;
+
+  } // namespace setup::detail
+
+} // namespace corsika
diff --git a/corsika/detail/setup/SetupStack.inl b/corsika/detail/setup/SetupStack.inl
new file mode 100644
index 0000000000000000000000000000000000000000..56fb4ff62c2172b386bc0efa9a452ccc8213947e
--- /dev/null
+++ b/corsika/detail/setup/SetupStack.inl
@@ -0,0 +1,7 @@
+/*
+ * (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.
+ */
diff --git a/corsika/detail/stack/NuclearStackExtension.inl b/corsika/detail/stack/NuclearStackExtension.inl
new file mode 100644
index 0000000000000000000000000000000000000000..728647ca0bfc5ea89738946ccdef9c36cda31ce2
--- /dev/null
+++ b/corsika/detail/stack/NuclearStackExtension.inl
@@ -0,0 +1,229 @@
+/*
+ * (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/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/stack/VectorStack.hpp>
+
+#include <algorithm>
+#include <tuple>
+#include <vector>
+
+namespace corsika::nuclear_stack {
+
+  template <template <typename> class InnerParticleInterface,
+            typename StackIteratorInterface>
+  inline void NuclearParticleInterface<InnerParticleInterface, StackIteratorInterface>::
+      setParticleData(particle_data_type const& v) {
+
+    if (std::get<0>(v) == Code::Nucleus) {
+      std::ostringstream err;
+      err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
+      throw std::runtime_error(err.str());
+    }
+
+    super_type::setParticleData(v);
+    setNucleusRef(-1); // this is not a nucleus
+  }
+
+  template <template <typename> class InnerParticleInterface,
+            typename StackIteratorInterface>
+  inline void NuclearParticleInterface<InnerParticleInterface, StackIteratorInterface>::
+      setParticleData(altenative_particle_data_type const& v) {
+    const unsigned short A = std::get<5>(v);
+    const unsigned short Z = std::get<6>(v);
+    if (std::get<0>(v) != Code::Nucleus || A == 0 || Z == 0) {
+      std::ostringstream err;
+      err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
+      throw std::runtime_error(err.str());
+    }
+    setNucleusRef(
+        super_type::getStackData().getNucleusNextRef()); // store this nucleus data ref
+    setNuclearA(A);
+    setNuclearZ(Z);
+    super_type::setParticleData(particle_data_type{
+        std::get<0>(v), std::get<1>(v), std::get<2>(v), std::get<3>(v), std::get<4>(v)});
+  }
+
+  template <template <typename> class InnerParticleInterface,
+            typename StackIteratorInterface>
+  inline void NuclearParticleInterface<InnerParticleInterface, StackIteratorInterface>::
+      setParticleData(super_type& p, particle_data_type const& v) {
+    if (std::get<0>(v) == Code::Nucleus) {
+      std::ostringstream err;
+      err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
+      throw std::runtime_error(err.str());
+    }
+
+    super_type::setParticleData(
+        p, particle_data_type{std::get<0>(v), std::get<1>(v), std::get<2>(v),
+                              std::get<3>(v), std::get<4>(v)});
+
+    setNucleusRef(-1); // this is not a nucleus
+  }
+
+  template <template <typename> class InnerParticleInterface,
+            typename StackIteratorInterface>
+  inline void NuclearParticleInterface<InnerParticleInterface, StackIteratorInterface>::
+      setParticleData(super_type& p, altenative_particle_data_type const& v) {
+
+    const unsigned short A = std::get<5>(v);
+    const unsigned short Z = std::get<6>(v);
+
+    if (std::get<0>(v) != Code::Nucleus || A == 0 || Z == 0) {
+      std::ostringstream err;
+      err << "NuclearStackExtension: no A and Z specified for new Nucleus!";
+      throw std::runtime_error(err.str());
+    }
+
+    setNucleusRef(
+        super_type::getStackData().getNucleusNextRef()); // store this nucleus data ref
+    setNuclearA(A);
+    setNuclearZ(Z);
+    super_type::setParticleData(
+        p, particle_data_type{std::get<0>(v), std::get<1>(v), std::get<2>(v),
+                              std::get<3>(v), std::get<4>(v)});
+  }
+
+  template <template <typename> class InnerParticleInterface,
+            typename StackIteratorInterface>
+  inline std::string NuclearParticleInterface<InnerParticleInterface,
+                                              StackIteratorInterface>::asString() const {
+    return fmt::format(
+        "{}, nuc({})", super_type::asString(),
+        (isNucleus() ? fmt::format("A={}, Z={}", getNuclearA(), getNuclearZ()) : "n/a"));
+  }
+
+  template <template <typename> class InnerParticleInterface,
+            typename StackIteratorInterface>
+  inline HEPMassType NuclearParticleInterface<InnerParticleInterface,
+                                              StackIteratorInterface>::getMass() const {
+    if (super_type::getPID() == Code::Nucleus)
+      return get_nucleus_mass(getNuclearA(), getNuclearZ());
+    return super_type::getMass();
+  }
+
+  template <template <typename> class InnerParticleInterface,
+            typename StackIteratorInterface>
+  inline int16_t NuclearParticleInterface<
+      InnerParticleInterface, StackIteratorInterface>::getChargeNumber() const {
+    if (super_type::getPID() == Code::Nucleus) return getNuclearZ();
+    return super_type::getChargeNumber();
+  }
+
+  template <typename InnerStackImpl>
+  inline int NuclearStackExtensionImpl<InnerStackImpl>::getNucleusNextRef() {
+    nuclearA_.push_back(0);
+    nuclearZ_.push_back(0);
+    return nuclearA_.size() - 1;
+  }
+
+  template <typename InnerStackImpl>
+  inline int NuclearStackExtensionImpl<InnerStackImpl>::getNucleusRef(
+      const unsigned int i) const {
+    if (nucleusRef_[i] >= 0) return nucleusRef_[i];
+    std::ostringstream err;
+    err << "NuclearStackExtension: no nucleus at ref=" << i;
+    throw std::runtime_error(err.str());
+  }
+
+  template <typename InnerStackImpl>
+  inline void NuclearStackExtensionImpl<InnerStackImpl>::copy(const unsigned int i1,
+                                                              const unsigned int i2) {
+    // index range check
+    if (i1 >= getSize() || i2 >= getSize()) {
+      std::ostringstream err;
+      err << "NuclearStackExtension: trying to access data beyond size of stack!";
+      throw std::runtime_error(err.str());
+    }
+    // copy internal particle data p[i2] = p[i1]
+    super_type::copy(i1, i2);
+    // check if any of p[i1] or p[i2] was a Code::Nucleus
+    const int ref1 = nucleusRef_[i1];
+    const int ref2 = nucleusRef_[i2];
+    if (ref2 < 0) {
+      if (ref1 >= 0) {
+        // i1 is nucleus, i2 is not
+        nucleusRef_[i2] = getNucleusNextRef();
+        nuclearA_[nucleusRef_[i2]] = nuclearA_[ref1];
+        nuclearZ_[nucleusRef_[i2]] = nuclearZ_[ref1];
+      } else {
+        // neither i1 nor i2 are nuclei
+      }
+    } else {
+      if (ref1 >= 0) {
+        // both are nuclei, i2 is overwritten with nucleus i1
+        // fNucleusRef stays the same, but A and Z data is overwritten
+        nuclearA_[ref2] = nuclearA_[ref1];
+        nuclearZ_[ref2] = nuclearZ_[ref1];
+      } else {
+        // i2 is overwritten with non-nucleus i1
+        nucleusRef_[i2] = -1;                       // flag as non-nucleus
+        nuclearA_.erase(nuclearA_.cbegin() + ref2); // remove data for i2
+        nuclearZ_.erase(nuclearZ_.cbegin() + ref2); // remove data for i2
+        const int n = nucleusRef_.size(); // update fNucleusRef: indices above ref2
+        // must be decremented by 1
+        for (int i = 0; i < n; ++i) {
+          if (nucleusRef_[i] > ref2) { nucleusRef_[i] -= 1; }
+        }
+      }
+    }
+  }
+
+  template <typename InnerStackImpl>
+  inline void NuclearStackExtensionImpl<InnerStackImpl>::clear() {
+    super_type::clear();
+    nucleusRef_.clear();
+    nuclearA_.clear();
+    nuclearZ_.clear();
+  }
+
+  template <typename InnerStackImpl>
+  inline void NuclearStackExtensionImpl<InnerStackImpl>::swap(const unsigned int i1,
+                                                              const unsigned int i2) {
+    // index range check
+    if (i1 >= getSize() || i2 >= getSize()) {
+      std::ostringstream err;
+      err << "NuclearStackExtension: trying to access data beyond size of stack!";
+      throw std::runtime_error(err.str());
+    }
+    // swap original particle data
+    super_type::swap(i1, i2);
+    // swap corresponding nuclear reference data
+    std::swap(nucleusRef_[i2], nucleusRef_[i1]);
+  }
+
+  template <typename InnerStackImpl>
+  inline void NuclearStackExtensionImpl<InnerStackImpl>::incrementSize() {
+    super_type::incrementSize();
+    nucleusRef_.push_back(-1);
+  }
+
+  template <typename InnerStackImpl>
+  inline void NuclearStackExtensionImpl<InnerStackImpl>::decrementSize() {
+    super_type::decrementSize();
+    if (nucleusRef_.size() > 0) {
+      const int ref = nucleusRef_.back();
+      nucleusRef_.pop_back();
+      if (ref >= 0) {
+        nuclearA_.erase(nuclearA_.begin() + ref);
+        nuclearZ_.erase(nuclearZ_.begin() + ref);
+        const int n = nucleusRef_.size();
+        for (int i = 0; i < n; ++i) {
+          if (nucleusRef_[i] >= ref) { nucleusRef_[i] -= 1; }
+        }
+      }
+    }
+  }
+
+} // namespace corsika::nuclear_stack
diff --git a/corsika/detail/stack/VectorStack.inl b/corsika/detail/stack/VectorStack.inl
new file mode 100644
index 0000000000000000000000000000000000000000..44384a131f8733711c52c72baff2d471af87da79
--- /dev/null
+++ b/corsika/detail/stack/VectorStack.inl
@@ -0,0 +1,93 @@
+/*
+ * (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/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+#include <string>
+#include <tuple>
+#include <vector>
+
+namespace corsika {
+
+  template <typename StackIteratorInterface>
+  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));
+    this->setMomentum(std::get<2>(v));
+    this->setPosition(std::get<3>(v));
+    this->setTime(std::get<4>(v));
+  }
+
+  template <typename StackIteratorInterface>
+  void ParticleInterface<StackIteratorInterface>::setParticleData(
+      ParticleInterface<StackIteratorInterface> const&,
+      std::tuple<Code, HEPEnergyType, MomentumVector, Point, TimeType> const& v) {
+    this->setPID(std::get<0>(v));
+    this->setEnergy(std::get<1>(v));
+    this->setMomentum(std::get<2>(v));
+    this->setPosition(std::get<3>(v));
+    this->setTime(std::get<4>(v));
+  }
+
+  inline void VectorStackImpl::clear() {
+    dataPID_.clear();
+    dataE_.clear();
+    momentum_.clear();
+    position_.clear();
+    time_.clear();
+  }
+
+  inline void VectorStackImpl::copy(size_t i1, size_t i2) {
+    dataPID_[i2] = dataPID_[i1];
+    dataE_[i2] = dataE_[i1];
+    momentum_[i2] = momentum_[i1];
+    position_[i2] = position_[i1];
+    time_[i2] = time_[i1];
+  }
+
+  inline void VectorStackImpl::swap(size_t i1, size_t i2) {
+    std::swap(dataPID_[i2], dataPID_[i1]);
+    std::swap(dataE_[i2], dataE_[i1]);
+    std::swap(momentum_[i2], momentum_[i1]);
+    std::swap(position_[i2], position_[i1]);
+    std::swap(time_[i2], time_[i1]);
+  }
+
+  inline void VectorStackImpl::incrementSize() {
+    dataPID_.push_back(Code::Unknown);
+    dataE_.push_back(0 * electronvolt);
+
+    CoordinateSystemPtr const& dummyCS = get_root_CoordinateSystem();
+
+    momentum_.push_back(
+        MomentumVector(dummyCS, {0 * electronvolt, 0 * electronvolt, 0 * electronvolt}));
+
+    position_.push_back(Point(dummyCS, {0 * meter, 0 * meter, 0 * meter}));
+    time_.push_back(0 * second);
+  }
+
+  inline void VectorStackImpl::decrementSize() {
+    if (dataE_.size() > 0) {
+      dataPID_.pop_back();
+      dataE_.pop_back();
+      momentum_.pop_back();
+      position_.pop_back();
+      time_.pop_back();
+    }
+  }
+
+} // namespace corsika
diff --git a/corsika/detail/stack/history/HistoryObservationPlane.hpp b/corsika/detail/stack/history/HistoryObservationPlane.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0b78f6a27f2b7517c54083ba5fac1ccd237d8aa9
--- /dev/null
+++ b/corsika/detail/stack/history/HistoryObservationPlane.hpp
@@ -0,0 +1,29 @@
+/*
+ * (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 <boost/histogram.hpp>
+
+namespace corsika::history {
+
+  namespace detail {
+    inline auto hist_factory() {
+      namespace bh = boost::histogram;
+      namespace bha = bh::axis;
+      auto h = bh::make_histogram(
+          bha::regular<float, bha::transform::log>{11 * 5, 1e0, 1e11, "muon energy"},
+          bha::regular<float, bha::transform::log>{11 * 5, 1e0, 1e11,
+                                                   "projectile energy"},
+          bha::category<int, bh::use_default, bha::option::growth_t>{
+              {211, -211, 2212, -2212}, "projectile PDG"});
+      return h;
+    }
+  } // namespace detail
+
+} // namespace corsika::history
diff --git a/corsika/detail/stack/history/HistoryObservationPlane.inl b/corsika/detail/stack/history/HistoryObservationPlane.inl
new file mode 100644
index 0000000000000000000000000000000000000000..0536eaf3ada84db7382461ec95d2ff7cc6ffac1b
--- /dev/null
+++ b/corsika/detail/stack/history/HistoryObservationPlane.inl
@@ -0,0 +1,80 @@
+/*
+ * (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.
+ */
+
+#include <corsika/logging/Logging.h>
+#include <corsika/stack/history/HistoryObservationPlane.hpp>
+
+#include <boost/histogram/ostream.hpp>
+
+namespace corsika::history {
+
+  HistoryObservationPlane::HistoryObservationPlane(setup::Stack const& stack,
+                                                   Plane const& obsPlane,
+                                                   bool deleteOnHit)
+      : stack_{stack}
+      , plane_{obsPlane}
+      , deleteOnHit_{deleteOnHit} {}
+
+  ProcessReturn HistoryObservationPlane::DoContinuous(
+      setup::Stack::ParticleType const& particle, setup::Trajectory const& trajectory) {
+    TimeType const timeOfIntersection =
+        (plane_.getCenter() - trajectory.getR0()).dot(plane_.getNormal()) /
+        trajectory.getV0().dot(plane_.getNormal());
+
+    if (timeOfIntersection < TimeType::zero()) { return ProcessReturn::Ok; }
+
+    if (plane_.isAbove(trajectory.getR0()) == plane_.isAbove(trajectory.getPosition(1))) {
+      return ProcessReturn::Ok;
+    }
+
+    CORSIKA_LOG_DEBUG(fmt::format("HistoryObservationPlane: Particle detected: pid={}",
+                                  particle.getPID()));
+
+    auto const pid = particle.getPID();
+    if (particles::isMuon(pid)) { fillHistoryHistogram(particle); }
+
+    if (deleteOnHit_) {
+      return ProcessReturn::ParticleAbsorbed;
+    } else {
+      return ProcessReturn::Ok;
+    }
+  }
+
+  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());
+
+    if (timeOfIntersection < TimeType::zero()) {
+      return std::numeric_limits<double>::infinity() * 1_m;
+    }
+
+    auto const pointOfIntersection = trajectory.getPosition(timeOfIntersection);
+    return (trajectory.getR0() - pointOfIntersection).norm() * 1.0001;
+  }
+
+  void HistoryObservationPlane::fillHistoryHistogram(
+      setup::Stack::ParticleType const& muon) {
+    double const muon_energy = muon.getEnergy() / 1_GeV;
+
+    int genctr{0};
+    Event const* event = muon.getEvent().get();
+    while (event) {
+      auto const projectile = stack_.cfirst() + event->projectileIndex();
+      if (event->eventType() == EventType::Interaction) {
+        genctr++;
+        double const projEnergy = projectile.getEnergy() / 1_GeV;
+        int const pdg = static_cast<int>(particles::getPDG(projectile.getPID()));
+
+        histogram_(muon_energy, projEnergy, pdg);
+      }
+      event = event->parentEvent().get(); // projectile.getEvent().get();
+    }
+  }
+} // namespace corsika::history
diff --git a/corsika/framework/analytics/ClassTimer.hpp b/corsika/framework/analytics/ClassTimer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5fed5b778af088bfa68ccb839efb2c1eba33f67c
--- /dev/null
+++ b/corsika/framework/analytics/ClassTimer.hpp
@@ -0,0 +1,118 @@
+/*
+ * (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.
+ */
+
+// Another possibility:
+// https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Execute-Around_Pointer
+// for a more global approach
+//
+// In this case here only a single function is measured via member function pointer.
+
+#pragma once
+
+#include <chrono>
+#include <utility>
+
+#include <corsika/framework/analytics/Timer.hpp>
+
+namespace corsika {
+
+  template <class TClass, typename TTimer>
+  class ClassTimerImpl : public TTimer {
+    static_assert(
+        is_timer_v<TTimer>,
+        "TTimer is not a timer!"); // Better
+                                   // https://en.cppreference.com/w/cpp/language/constraints
+                                   // but not available in C++17
+
+  protected:
+    /// Reference to the class object on which the function should be called
+    TClass& obj_;
+
+  public:
+    ClassTimerImpl(TClass& obj)
+        : obj_(obj){};
+  };
+
+  /// Measure the runtime of a single class function
+  /**
+   * @tparam TClassFunc Type of the member function pointer that should be wrapped
+   * @tparam TFunc Actual function of the type defined in TClass
+   */
+  template <typename TClassFunc, TClassFunc TFunc,
+            typename TTimer =
+                Timer<std::chrono::high_resolution_clock, std::chrono::microseconds>>
+  class ClassTimer;
+
+  /// Measure the runtime of a single class function
+  /** Specialisation to capture exact information about the composition of the member
+   * function pointer used.
+   *
+   *  This class wrapes a single function and allowes the measureing of its runtime if it
+   * called via the "call(...)" function
+   *
+   * @tparam TClass Class of the function that should be wrapped
+   * @tparam TRet   Return value of the wrapped function
+   * @tparam TArgs  Arguments passed to the wrapped function
+   * @tparam TFuncPtr Actual function of the type defined by TRet
+   * TClass::TFuncPtr(TArgs...)
+   */
+  template <typename TClass, typename TRet, typename... TArgs,
+            TRet (TClass::*TFuncPtr)(TArgs...), typename TTimer>
+  class ClassTimer<TRet (TClass::*)(TArgs...), TFuncPtr, TTimer>
+      : public ClassTimerImpl<TClass, TTimer> {
+  private:
+  public:
+    ClassTimer(TClass& obj);
+
+    /// Executes the wrapped function
+    /** This function executes and measure the runtime of the wrapped function with the
+     * highest precision available (high_resolution_clock).
+     *
+     * @param args Arguments are perfect forwarded to the wrapped function.
+     * @return Returns the return value of the wrapped function. This value get copied
+     * during the process and therefore must be copy constructible!
+     */
+    TRet call(TArgs... args);
+  };
+
+  /// Specialisation for member functions without return value
+  template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...),
+            typename TTimer>
+  class ClassTimer<void (TClass::*)(TArgs...), TFuncPtr, TTimer>
+      : public ClassTimerImpl<TClass, TTimer> {
+  public:
+    ClassTimer(TClass& obj);
+
+    void call(TArgs... args);
+  };
+
+  /// Specialisation for const member functions
+  template <typename TClass, typename TRet, typename... TArgs,
+            TRet (TClass::*TFuncPtr)(TArgs...) const, typename TTimer>
+  class ClassTimer<TRet (TClass::*)(TArgs...) const, TFuncPtr, TTimer>
+      : public ClassTimerImpl<TClass, TTimer> {
+  public:
+    ClassTimer(TClass& obj);
+
+    TRet call(TArgs... args);
+  };
+
+  /// Specialisation for const member functions without return value
+  template <typename TClass, typename... TArgs, void (TClass::*TFuncPtr)(TArgs...) const,
+            typename TTimer>
+  class ClassTimer<void (TClass::*)(TArgs...) const, TFuncPtr, TTimer>
+      : public ClassTimerImpl<TClass, TTimer> {
+  public:
+    ClassTimer(TClass& obj);
+
+    void call(TArgs... args);
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/analytics/ClassTimer.inl>
\ No newline at end of file
diff --git a/corsika/framework/analytics/FunctionTimer.hpp b/corsika/framework/analytics/FunctionTimer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b5aaae5c4ed6db978a86e2ec2dac8d928c1a2d59
--- /dev/null
+++ b/corsika/framework/analytics/FunctionTimer.hpp
@@ -0,0 +1,60 @@
+/*
+ * (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 <chrono>
+#include <utility>
+#include <type_traits>
+
+#include <corsika/framework/analytics/Timer.hpp>
+
+namespace corsika {
+
+  /// Wraps and measures the runtime of a single function type object
+  /**
+   *
+   * @tparam TFunc funtion pointer that should be wrapped
+   * @tparam TClock type of the clock that should be used for measurements, default is
+   * high_resolution_clock
+   * @tparam TDuration type of std::duration to measure the elapsed time, default is
+   * microseconds
+   */
+  template <typename TFunc, class TTimer = Timer<std::chrono::high_resolution_clock,
+                                                 std::chrono::microseconds>>
+  class FunctionTimer : public TTimer {
+    static_assert(
+        is_timer_v<TTimer>,
+        "TTimer is not a timer!"); // Better
+                                   // https://en.cppreference.com/w/cpp/language/constraints
+                                   // but not available in C++17
+  public:
+    /** Constructs the wrapper with the given functionpointer
+     *  @param f  Function or functor whose runtime should be measured
+     **/
+    FunctionTimer(TFunc f);
+
+    /** Functor for calling the wrapped function
+     *  This functor calls the wrapped function and measures the elapsed time between call
+     *and return. The return value needs to be copy constructible.
+     *  @tparam TArgs Parameter types that are forwarded to the function. The use of
+     *correct types is the responsibility of the user, no checks are done.
+     *  @param args Arguments are forwarded to the wrapped function. This method does not
+     *support overloaded function resolution.
+     *  @return The return value of the wrapped function is temporarily copied and then
+     *returned by value
+     **/
+    template <typename... TArgs>
+    auto operator()(TArgs&&... args) -> std::invoke_result_t<TFunc, TArgs...>;
+
+  private:
+    TFunc function_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/analytics/FunctionTimer.inl>
\ No newline at end of file
diff --git a/corsika/framework/analytics/Timer.hpp b/corsika/framework/analytics/Timer.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4df67f488dfaa0ebc998d67689d28238b6e2834b
--- /dev/null
+++ b/corsika/framework/analytics/Timer.hpp
@@ -0,0 +1,62 @@
+/*
+ * (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 <chrono>
+#include <utility>
+
+#include <type_traits>
+
+namespace corsika {
+
+  template <typename TClock = std::chrono::high_resolution_clock,
+            typename TDuration = std::chrono::microseconds>
+  class Timer {
+  protected:
+    /// Default clock used for time measurement
+    using clock_type = TClock;
+
+    /// Internal resolution of the time measurement
+    using duration_type = TDuration;
+
+    /// Startpoint of time measurement
+    typename clock_type::time_point start_;
+
+    /// Measured runtime of the function
+    duration_type timeDiff_;
+
+  public:
+    Timer()
+        : timeDiff_(0){};
+
+    /// Start the timer
+    inline void startTimer() { start_ = clock_type::now(); }
+
+    /// Stop the timer
+    inline void stopTimer() {
+      timeDiff_ = std::chrono::duration_cast<duration_type>(clock_type::now() - start_);
+    }
+
+    /**
+     * Returns the runtime of the last call to the wrapped function
+     * @return Returns the measured runtime of the wrapped function/functor in the unit
+     * given by TDuration
+     **/
+    inline duration_type getTime() const { return timeDiff_; }
+  };
+
+  std::false_type is_timer_impl(...);
+  template <typename T, typename U>
+  std::true_type is_timer_impl(Timer<T, U> const volatile&);
+
+  template <typename T>
+  inline 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
diff --git a/corsika/framework/core/Cascade.hpp b/corsika/framework/core/Cascade.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..64c4c065e01fd6bf08bf394605c04285a914c09a
--- /dev/null
+++ b/corsika/framework/core/Cascade.hpp
@@ -0,0 +1,159 @@
+/*
+ * (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 <corsika/framework/process/ProcessReturn.hpp>
+
+#include <corsika/framework/core/PhysicalUnits.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/framework/core/Logging.hpp>
+
+/*  see Issue 161, we need to include SetupStack only because we need
+    to globally define StackView. This is clearly not nice and should
+    be changed, when possible. It might be that StackView needs to be
+    templated in Cascade, but this would be even worse... so we don't
+    do that until it is really needed.
+ */
+#include <corsika/setup/SetupStack.hpp>
+
+#include <cassert>
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+/**
+ * The cascade namespace assembles all objects needed to simulate full particles cascades.
+ */
+
+namespace corsika {
+
+  /**
+   *
+   * The Cascade class is constructed from template arguments making
+   * it very versatile. Via the template arguments physics models are
+   * plugged into the cascade simulation.
+   *
+   * <b>TTracking</b> must be a class according to the
+   * TrackingInterface providing the functions:
+   *
+   * <code>
+   * auto getTrack(Particle const& p)</auto>,
+   * with the return type <code>geometry::Trajectory<Line>
+   * </code>
+   *
+   * <b>TProcessList</b> must be a ProcessSequence.   *
+   * <b>Stack</b> is the storage object for particle data, i.e. with
+   * Particle class type <code>Stack::ParticleType</code>
+   *
+   *
+   */
+  template <typename TTracking, typename TProcessList, typename TStack,
+            /*
+             TStackView is needed as explicit template parameter because
+             of issue 161 and the
+             inability of clang to understand "stack::MakeView" so far.
+            */
+            typename TStackView = corsika::setup::StackView>
+  class Cascade {
+
+    typedef typename TStack::particle_type Particle;
+    typedef std::remove_pointer_t<decltype(((Particle*)nullptr)->getNode())>
+        VolumeTreeNode;
+    typedef typename VolumeTreeNode::IModelProperties MediumInterface;
+
+  public:
+    /**
+     * \group constructors
+     * \{
+     * Cascade class cannot be default constructed, but needs a valid
+     * list of physics processes for configuration at construct time.
+     */
+    Cascade() = delete;
+    Cascade(Cascade const&) = default;
+    Cascade(Cascade&&) = default;
+    ~Cascade() = default;
+    Cascade& operator=(Cascade const&) = default;
+    Cascade(Environment<MediumInterface> const& env, TTracking& tr, TProcessList& pl,
+            TStack& stack)
+        : environment_(env)
+        , tracking_(tr)
+        , sequence_(pl)
+        , stack_(stack) {
+      CORSIKA_LOG_INFO(c8_ascii_);
+      if constexpr (TStackView::has_event) {
+        CORSIKA_LOG_INFO(" - With full cascade HISTORY.");
+      }
+    }
+    //! \}
+
+    /**
+     * set the nodes for all particles on the stack according to their numerical
+     * position
+     */
+    void setNodes();
+
+    /**
+     * The Run function is the main simulation loop, which processes
+     * particles from the Stack until the Stack is empty.
+     */
+    void run();
+
+    /**
+     * Force an interaction of the top particle of the stack at its current position.
+     * Note that setNodes() or an equivalent procedure needs to be called first if you
+     * want to call forceInteraction() for the primary interaction.
+     */
+    void forceInteraction();
+
+  private:
+    /**
+     * The Step function is executed for each particle from the
+     * stack. It will calcualte geometric transport of the particles,
+     * and apply continuous and stochastic processes to it, which may
+     * lead to energy losses, scattering, absorption, decays and the
+     * production of secondary particles.
+     *
+     * New particles produced in one step are subject to further
+     * processing, e.g. thinning, etc.
+     */
+    void step(Particle& vParticle);
+
+    ProcessReturn decay(TStackView& view);
+    ProcessReturn interaction(TStackView& view);
+    void setEventType(TStackView& view, history::EventType);
+
+    // data members
+    Environment<MediumInterface> const& environment_;
+    TTracking& tracking_;
+    TProcessList& sequence_;
+    TStack& stack_;
+    default_prng_type& rng_ = RNGManager::getInstance().getRandomStream("cascade");
+    unsigned int count_ = 0;
+
+    // but this here temporarily. Should go into dedicated file later:
+    const char* c8_ascii_ =
+        R"V0G0N(
+  ,ad8888ba,     ,ad8888ba,    88888888ba    ad88888ba   88  88      a8P          db              ad88888ba   
+ d8"'    `"8b   d8"'    `"8b   88      "8b  d8"     "8b  88  88    ,88'          d88b            d8"     "8b  
+d8'            d8'        `8b  88      ,8P  Y8,          88  88  ,88"           d8'`8b           Y8a     a8P  
+88             88          88  88aaaaaa8P'  `Y8aaaaa,    88  88,d88'           d8'  `8b           "Y8aaa8P"   
+88             88          88  88""""88'      `"""""8b,  88  8888"88,         d8YaaaaY8b          ,d8"""8b,   
+Y8,            Y8,        ,8P  88    `8b            `8b  88  88P   Y8b       d8""""""""8b        d8"     "8b  
+ Y8a.    .a8P   Y8a.    .a8P   88     `8b   Y8a     a8P  88  88     "88,    d8'        `8b       Y8a     a8P  
+  `"Y8888Y"'     `"Y8888Y"'    88      `8b   "Y88888P"   88  88       Y8b  d8'          `8b       "Y88888P"
+       )V0G0N";
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/core/Cascade.inl>
diff --git a/corsika/framework/core/Logging.hpp b/corsika/framework/core/Logging.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..4d4c43a7ba2e4f7e9d2c90e87a905d542b554df8
--- /dev/null
+++ b/corsika/framework/core/Logging.hpp
@@ -0,0 +1,150 @@
+/*
+ * (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.
+ */
+
+/**
+ *  @File Logging.hpp
+ *
+ * CORSIKA8 logging utilities.
+ *
+ * See testLogging.cpp for a complete set of examples for
+ * how the logging functions should be used.
+ */
+
+#pragma once
+
+// Configure some behaviour of sdlog.
+// This must be done before spdlog is included.
+
+// use the coarse system clock. This is *much* faster
+// but introduces a timestamp error of O(10 ms) which is fine for us.
+#define SPDLOG_CLOCK_COARSE
+
+// do not create a default logger (we provide our own "corsika" logger)
+#define SPDLOG_DISABLE_DEFAULT_LOGGER
+
+// use __PRETTY_FUNCTION__ instead of __FUNCTION__ where
+// printing function names in trace statements. This is much
+// nicer than __FUNCTION__ under GCC/clang.
+#define SPDLOG_FUNCTION __PRETTY_FUNCTION__
+
+// 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
+#else // otherwise, remove everything but "error" and worse messages
+#define SPDLOG_ACTIVE_LEVEL SPDLOG_LEVEL_INFO
+#endif
+
+#include <spdlog/fmt/ostr.h> // will output whenerver a streaming operator is found
+#include <spdlog/sinks/stdout_color_sinks.h>
+#include <spdlog/spdlog.h>
+
+namespace corsika {
+
+  /*
+   * The default pattern for CORSIKA8 loggers.
+   */
+  const std::string default_pattern{"[%n:%^%-8l%$] %v"};
+
+  /**
+   * Create a new C8-style logger.
+   *
+   * Use this if you are explicitly (and can guarantee) that you
+   * are creating a logger for the first time. It is recommended
+   * that for regular usage, the `get_logger` function is used instead
+   * as that will also create the logger if it has not yet been created.
+   *
+   * Calling `create_logger` twice to create the same logger will
+   * result in an spdlog duplicate exception.
+   *
+   * @param name           The unique name of the logger.
+   * @param defaultlog     If True, set this as the default logger.
+   * @returns              The constructed and formatted logger.
+   */
+  std::shared_ptr<spdlog::logger> create_logger(std::string const& name,
+                                                bool const defaultlog = false);
+
+  /**
+   * Get a smart pointer to an existing logger.
+   *
+   * This should be the default method for code to obtain a
+   * logger. If the logger *does not* exist, it is *created* and
+   * returned to the caller.
+   *
+   * This should be preferred over `create_logger`.
+   *
+   * @param name    The name of the logger to get.
+   * @param defaultlog   If True, make this the default logger.
+   * @returns              The constructed and formatted logger.
+   */
+  std::shared_ptr<spdlog::logger> get_logger(std::string const& name,
+                                             bool const defaultlog = false);
+
+  /**
+   * The default "corsika" logger.
+   */
+  static inline std::shared_ptr<spdlog::logger> corsika_logger =
+      get_logger("corsika", true);
+  // corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  // many of these free functions are special to the logging
+  // infrastructure so we hide them in the corsika::logging namespace.
+  namespace logging {
+
+    // bring spdlog into the corsika::logging namespace
+    using namespace spdlog;
+
+    /**
+     * Set the default log level for all *newly* created loggers.
+     *
+     *  @param name    The minimum log level required to print.
+     *
+     */
+    auto set_default_level(level::level_enum const minlevel) -> void;
+
+    /**
+     * Add the source (filename, line no) info to the logger.
+     *
+     * @param logger  The logger to set the level of.
+     *
+     */
+    template <typename TLogger>
+    auto add_source_info(TLogger& logger) -> void;
+
+    /**
+     * Reset the logging pattern to the default.
+     *
+     * @param logger  The logger to set the level of.
+     *
+     */
+    template <typename TLogger>
+    auto reset_pattern(TLogger& logger) -> void;
+
+  } // namespace logging
+
+// define our macro-style loggers
+// these use the default "corsika" logger
+#define CORSIKA_LOG_TRACE SPDLOG_TRACE
+#define CORSIKA_LOG_DEBUG SPDLOG_DEBUG
+#define CORSIKA_LOG_INFO SPDLOG_INFO
+#define CORSIKA_LOG_WARN SPDLOG_WARN
+#define CORSIKA_LOG_ERROR SPDLOG_ERROR
+#define CORSIKA_LOG_CRITICAL SPDLOG_CRITICAL
+
+// and the specific logger versions
+// these take a logger instance as their first argument
+#define CORSIKA_LOGGER_TRACE SPDLOG_LOGGER_TRACE
+#define CORSIKA_LOGGER_DEBUG SPDLOG_LOGGER_DEBUG
+#define CORSIKA_LOGGER_INFO SPDLOG_LOGGER_INFO
+#define CORSIKA_LOGGER_WARN SPDLOG_LOGGER_WARN
+#define CORSIKA_LOGGER_ERROR SPDLOG_LOGGER_ERROR
+#define CORSIKA_LOGGER_CRITICAL SPDLOG_LOGGER_CRITICAL
+
+} // namespace corsika
+
+#include <corsika/detail/framework/core/Logging.inl>
diff --git a/corsika/framework/core/ParticleProperties.hpp b/corsika/framework/core/ParticleProperties.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2deb03a865c6d32e37f4c274c83a604f72fecb04
--- /dev/null
+++ b/corsika/framework/core/ParticleProperties.hpp
@@ -0,0 +1,86 @@
+/*
+ * (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.
+ */
+
+/**
+   @file ParticleProperties.hpp
+
+   Interface to particle properties
+
+   The properties of all particles are saved in static and flat
+   arrays. There is a enum corsika::Code to identify each
+   particles, and each individual particles has its own static class,
+   which can be used to retrieve its physical properties.
+ */
+
+#pragma once
+
+#include <array>
+#include <cstdint>
+#include <cmath>
+#include <iosfwd>
+#include <string_view>
+#include <type_traits>
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+/**
+ * \file ParticleProperties.hpp
+ *
+ * The properties of all elementary particles are accessible here. The data
+ * are taken from the Pythia ParticleData.xml file.
+ *
+ */
+
+namespace corsika {
+  /**
+   * @enum Code
+   * The Code enum is the actual place to define CORSIKA 8 particle codes.
+   */
+  enum class Code : int16_t;
+  enum class PDGCode : int32_t;
+  using CodeIntType = std::underlying_type<Code>::type;
+  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
+
+  //! 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
+
+  //! 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
+
+  //! returns mass of (A,Z) nucleus, disregarding binding energy
+  inline HEPMassType get_nucleus_mass(unsigned int const, unsigned int const);
+
+  //! convert PDG code to CORSIKA 8 internal code
+  inline Code convert_from_PDG(PDGCode);
+
+  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);
+} // namespace corsika
+
+// data arrays, etc., as generated automatically
+#include <corsika/framework/core/GeneratedParticleProperties.inc>
+
+#include <corsika/detail/framework/core/ParticleProperties.inl>
+
+// constants in namespaces-like static classes, generated automatically
+#include <corsika/framework/core/GeneratedParticleClasses.inc>
diff --git a/Framework/Units/PhysicalConstants.h b/corsika/framework/core/PhysicalConstants.hpp
similarity index 79%
rename from Framework/Units/PhysicalConstants.h
rename to corsika/framework/core/PhysicalConstants.hpp
index f47b917e6919762d7b1c73a950192c086e7afe4b..026d93a115bdf369afaa1effdfff217593a1b88e 100644
--- a/Framework/Units/PhysicalConstants.h
+++ b/corsika/framework/core/PhysicalConstants.hpp
@@ -9,16 +9,25 @@
 
 #pragma once
 
-#include <phys/units/quantity.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
 
-namespace corsika::units::constants {
+/**
+ * \file PhysicalConstants.hpp
+ *
+ * Constants are defined with static units, based on the package
+ * (namespace) phys::units, imported in PhysicsUnits.hpp
+ *
+ * \namespace corsika::constants
+ *
+ * Physical and mathematical constants with units.
+ */
+
+namespace corsika::constants {
 
   using namespace phys::units;
 
   // acceleration of free-fall, standard
-  constexpr phys::units::quantity<phys::units::acceleration_d> g_sub_n{
-      phys::units::Rep(9.80665L) * phys::units::meter /
-      phys::units::square(phys::units::second)};
+  constexpr quantity<acceleration_d> g_sub_n{Rep(9.80665L) * meter / square(second)};
 
   // Avogadro constant
   constexpr quantity<dimensions<0, 0, 0, 0, 0, -1>> N_sub_A{Rep(6.02214199e+23L) / mole};
@@ -63,4 +72,4 @@ namespace corsika::units::constants {
 
   // etc.
 
-} // namespace corsika::units::constants
+} // namespace corsika::constants
diff --git a/corsika/framework/core/PhysicalGeometry.hpp b/corsika/framework/core/PhysicalGeometry.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..36b897f75ed37aa7f8acacb4c6a3bfef15ae307a
--- /dev/null
+++ b/corsika/framework/core/PhysicalGeometry.hpp
@@ -0,0 +1,25 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+/**
+ * \file PhysicalUnits.hpp
+ *
+ * Import and extend the phys::units package. The SI units are also imported into the
+ * `\namespace corsika`, since they are used everywhere as integral part of the framework.
+ */
+
+namespace corsika {
+
+  typedef Vector<hepmomentum_d> MomentumVector;
+
+} // namespace corsika
diff --git a/Framework/Units/PhysicalUnits.h b/corsika/framework/core/PhysicalUnits.hpp
similarity index 71%
rename from Framework/Units/PhysicalUnits.h
rename to corsika/framework/core/PhysicalUnits.hpp
index 36a835e7e9e072bcce1db01f8980099d864d9afe..82373acc9057817ccde077ad93adac8f3584360b 100644
--- a/Framework/Units/PhysicalUnits.h
+++ b/corsika/framework/core/PhysicalUnits.hpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
@@ -8,13 +8,21 @@
 
 #pragma once
 
-#include <corsika/units/PhysicalConstants.h>
+// the templated static-unit package we use:
+#include <corsika/framework/units/io.hpp>
+#include <corsika/framework/units/quantity.hpp>
 
-#include <phys/units/io.hpp>
-#include <phys/units/quantity.hpp>
+#include <corsika/framework/core/PhysicalConstants.hpp>
+
+/**
+ * \file PhysicalUnits.hpp
+ *
+ * Import and extend the phys::units package. The SI units are also imported into the
+ * `\namespace corsika`, since they are used everywhere as integral part of the framework.
+ */
 
 /*
-  It is essentially a bug of the phys/units package to define the
+  It is essentially a bug of the phys_units package to define the
   operator<< not in the same namespace as the types it is working
   on. This breaks ADL (argument-dependent lookup). Here we "fix" this:
  */
@@ -23,10 +31,9 @@ namespace phys::units {
 } // namespace phys::units
 
 /**
- * @file PhysicalUnits
+ * \namespace corsika::units
  *
- * Add new units and types we need. Units are compile-time. Literals are
- * used for optimal coding style.
+ * Extension of the phys::units package.
  *
  */
 
@@ -43,10 +50,19 @@ namespace corsika::units {
   }
 } // namespace corsika::units
 
+/**
+ * \namespace corsika::units::si
+ *
+ * SI units as used mainly in CORSIKA8 as basedline.
+ *
+ */
+
 namespace corsika::units::si {
+
   using namespace phys::units;
   using namespace phys::units::literals;
   using namespace phys::units::io;
+
   using phys::units::io::operator<<;
 
   /// defining momentum you suckers
@@ -58,6 +74,7 @@ namespace corsika::units::si {
   using sigma_d = phys::units::area_d;
 
   /// add the unit-types
+  using DimensionlessType = phys::units::quantity<phys::units::dimensionless_d, double>;
   using LengthType = phys::units::quantity<phys::units::length_d, double>;
   using TimeType = phys::units::quantity<phys::units::time_interval_d, double>;
   using SpeedType = phys::units::quantity<phys::units::speed_d, double>;
@@ -81,7 +98,7 @@ namespace corsika::units::si {
       phys::units::quantity<phys::units::magnetic_flux_density_d, double>;
 
   template <typename DimFrom, typename DimTo>
-  auto constexpr ConversionFactorHEPToSI() {
+  auto constexpr conversion_factor_HEP_to_SI() {
     static_assert(DimFrom::dim1 == 0 && DimFrom::dim2 == 0 && DimFrom::dim3 == 0 &&
                       DimFrom::dim4 == 0 && DimFrom::dim5 == 0 && DimFrom::dim6 == 0 &&
                       DimFrom::dim7 == 0,
@@ -101,14 +118,12 @@ namespace corsika::units::si {
     int constexpr q = -m - t;
     static_assert(q == l + e - 2 * m, "HEP/SI dimension mismatch!");
 
-    using namespace detail;
-    return static_pow<-e>(corsika::units::constants::hBarC) *
-           static_pow<p>(corsika::units::constants::hBar) *
-           static_pow<q>(corsika::units::constants::c);
+    return static_pow<-e>(constants::hBarC) * static_pow<p>(constants::hBar) *
+           static_pow<q>(constants::c);
   }
 
   template <typename DimFrom>
-  auto constexpr ConversionFactorSIToHEP() {
+  auto constexpr conversion_factor_SI_to_HEP() {
     static_assert(DimFrom::dim4 == 0 && DimFrom::dim5 == 0 && DimFrom::dim6 == 0 &&
                       DimFrom::dim7 == 0 && DimFrom::dim8 == 0,
                   "must be pure L, M, T type");
@@ -121,21 +136,20 @@ namespace corsika::units::si {
     int constexpr q = m + t;
     int constexpr e = m - t - l;
 
-    using namespace detail;
-    return static_pow<e>(corsika::units::constants::hBarC) *
-           static_pow<p>(corsika::units::constants::hBar) *
-           static_pow<q>(corsika::units::constants::c);
+    return static_pow<e>(constants::hBarC) * static_pow<p>(constants::hBar) *
+           static_pow<q>(constants::c);
   }
 
   template <typename DimTo, typename DimFrom>
-  auto constexpr ConvertHEPToSI(quantity<DimFrom> q) {
-    return ConversionFactorHEPToSI<DimFrom, DimTo>() * q;
+  auto constexpr convert_HEP_to_SI(quantity<DimFrom> q) {
+    return conversion_factor_HEP_to_SI<DimFrom, DimTo>() * q;
   }
 
   template <typename DimFrom>
-  auto constexpr ConvertSIToHEP(quantity<DimFrom> q) {
-    return ConversionFactorSIToHEP<DimFrom>() * q;
+  auto constexpr convert_SI_to_HEP(quantity<DimFrom> q) {
+    return conversion_factor_SI_to_HEP<DimFrom>() * q;
   }
+
 } // end namespace corsika::units::si
 
 /**
@@ -155,8 +169,18 @@ namespace phys {
       QUANTITY_DEFINE_SCALING_LITERALS(eV, hepenergy_d, 1)
 
       QUANTITY_DEFINE_SCALING_LITERALS(b, corsika::units::si::sigma_d,
-                                       magnitude(corsika::units::constants::barn))
+                                       magnitude(corsika::constants::barn))
 
     } // namespace literals
   }   // namespace units
 } // namespace phys
+
+// import into main \namespace corsika here:
+namespace corsika {
+  using namespace units;
+  using namespace units::si;
+  using namespace phys::units;
+  using namespace phys::units::literals;
+  using namespace phys::units::io;
+  using phys::units::io::operator<<;
+} // namespace corsika
diff --git a/corsika/framework/geometry/BaseVector.hpp b/corsika/framework/geometry/BaseVector.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f689d49dc49e9797e5056aee96e86d808c05d298
--- /dev/null
+++ b/corsika/framework/geometry/BaseVector.hpp
@@ -0,0 +1,55 @@
+/*
+ * (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 <corsika/framework/geometry/CoordinateSystem.hpp>
+#include <corsika/framework/geometry/QuantityVector.hpp>
+
+#include <memory>
+
+namespace corsika {
+
+  /*!
+   * Common base class for Vector and Point.
+   *
+   * This holds a QuantityVector and a CoordinateSystem. The
+   * BaseVector manages resources for many geometry objects.
+   *
+   */
+  template <typename TDimension>
+  class BaseVector {
+
+  public:
+    BaseVector(CoordinateSystemPtr const& pCS, QuantityVector<TDimension> const& pQVector)
+        : quantityVector_(pQVector)
+        , cs_(pCS) {}
+
+    BaseVector() = delete; // we only want to creat initialized
+                           // objects
+    BaseVector(BaseVector const&) = default;
+    BaseVector(BaseVector&& a) = default;
+    BaseVector& operator=(BaseVector const&) = default;
+    ~BaseVector() = default;
+
+    CoordinateSystemPtr getCoordinateSystem() const;
+    void setCoordinateSystem(CoordinateSystemPtr const& cs) { cs_ = cs; }
+
+  protected:
+    QuantityVector<TDimension> const& getQuantityVector() const;
+    QuantityVector<TDimension>& getQuantityVector();
+    void setQuantityVector(QuantityVector<TDimension> const& v) { quantityVector_ = v; }
+
+  private:
+    QuantityVector<TDimension> quantityVector_;
+    CoordinateSystemPtr cs_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/geometry/BaseVector.inl>
diff --git a/corsika/framework/geometry/CoordinateSystem.hpp b/corsika/framework/geometry/CoordinateSystem.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..ed9c8184853e61c9d19d102d2543a79d7806d7fc
--- /dev/null
+++ b/corsika/framework/geometry/CoordinateSystem.hpp
@@ -0,0 +1,179 @@
+/*
+ * (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
+
+/**
+ * \file CoordinateSystem.hpp
+ **/
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/QuantityVector.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <Eigen/Dense>
+#include <stdexcept>
+#include <memory>
+
+namespace corsika {
+
+  typedef Eigen::Transform<double, 3, Eigen::Affine> EigenTransform;
+  typedef Eigen::Translation<double, 3> EigenTranslation;
+
+  template <typename T>
+  class Vector; // fwd decl
+
+  class CoordinateSystem; // fwd decl
+  /**
+   * To refer to CoordinateSystems, only the CoordinateSystemPtr must be used.
+   */
+  using CoordinateSystemPtr = std::shared_ptr<CoordinateSystem const>;
+
+  /// this is the only way to create ONE unique root CS
+  static CoordinateSystemPtr& get_root_CoordinateSystem();
+
+  /**
+   * Creates new CoordinateSystemPtr by translation along \a vector
+   */
+  inline 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);
+
+  /**
+   * 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);
+
+  /**
+   * creates a new CoordinateSystem, translated by \a translation and rotated around \a
+   * axis by \a angle.
+   */
+  template <typename TDim>
+  inline CoordinateSystemPtr make_translationAndRotation(
+      CoordinateSystemPtr const& cs, QuantityVector<length_d> const& translation,
+      QuantityVector<TDim> const& axis, double const angle);
+
+  /**
+   * A class to store the reference coordinate system for a geometric object
+   *
+   * A CoordinateSystem can only be created in reference and relative
+   * to other CoordinateSystems. Thus, the geometric
+   * transformation between all CoordinateSystems is always known and stored.
+   *
+   * The static (singleton) function \ref make_root_CoordinateSystem is
+   * the only way to create and access the global top-level
+   * CoordinateSystem obect. CoordinateSystem objects should be
+   * *abosulte* *only* handled in their form of CoordinateSystemPtr,
+   * which are shared_ptr that handle the lifetime of the entire
+   * CoordinateSystem hirarchy.
+   *
+   * Thus, new CoordinateSystem are only be created (via
+   * CoordinateSystemPtr) by transforing existing CoordinateSystem
+   * using: \ref make_rotationToZ, \ref make_rotation, or \ref
+   * make_translationAndRotation, see below.
+   *
+   * Warning: As a consequence, never try to access, modify, copy, the raw
+   * CoordinateSystem objects directly, this will almost certainly result in undefined
+   * behaviour. Only access, copy, handle them via CoordinateSystemPtr.
+   */
+
+  class CoordinateSystem {
+
+    /**
+     * Constructor only from referenceCS, given the transformation matrix transf
+     */
+    CoordinateSystem(CoordinateSystemPtr const& referenceCS, EigenTransform const& transf)
+        : referenceCS_(referenceCS)
+        , transf_(transf) {}
+
+    /**
+     * for creating the root CS
+     */
+    CoordinateSystem()
+        : referenceCS_(nullptr)
+        , transf_(EigenTransform::Identity()) {}
+
+  public:
+    // default resource allocation
+    CoordinateSystem(CoordinateSystem const&) = default;
+    CoordinateSystem(CoordinateSystem&&) = default;
+    CoordinateSystem& operator=(CoordinateSystem const& pCS) =
+        delete; // avoid making copies
+    ~CoordinateSystem() = default;
+
+    /**
+     * Checks, if this is the unique ROOT CS
+     */
+    inline bool isRoot() const { return !referenceCS_; }
+
+    inline CoordinateSystemPtr getReferenceCS() const;
+
+    inline EigenTransform const& getTransform() const;
+
+    inline bool operator==(CoordinateSystem const&) const;
+    inline bool operator!=(CoordinateSystem const&) const;
+
+  protected:
+    static CoordinateSystem createCS() { return CoordinateSystem(); }
+
+    /**
+     * \name Friends
+     * Manipulation and creation functions.
+     * \{
+     **/
+
+    friend CoordinateSystemPtr& get_root_CoordinateSystem();
+
+    friend CoordinateSystemPtr make_translation(CoordinateSystemPtr const& cs,
+                                                QuantityVector<length_d> const& vector);
+    template <typename TDim>
+    friend CoordinateSystemPtr make_rotationToZ(CoordinateSystemPtr const& cs,
+                                                Vector<TDim> const& vVec);
+    template <typename TDim>
+    friend CoordinateSystemPtr make_rotation(CoordinateSystemPtr const& cs,
+                                             QuantityVector<TDim> const& axis,
+                                             double const angle);
+    template <typename TDim>
+    friend CoordinateSystemPtr make_translationAndRotation(
+        CoordinateSystemPtr const& cs, QuantityVector<length_d> const& translation,
+        QuantityVector<TDim> const& axis, double const angle);
+
+    /** \} **/
+
+  private:
+    CoordinateSystemPtr referenceCS_;
+    EigenTransform transf_;
+  };
+
+  /**
+   * Transformation matrix from one reference system to another.
+   *
+   * returns the transformation matrix necessary to transform primitives with coordinates
+   * in \a pFrom to \a pTo, e.g.
+   * \f$ \vec{v}^{\text{(to)}} = \mathcal{M} \vec{v}^{\text{(from)}} \f$
+   * (\f$ \vec{v}^{(.)} \f$ denotes the coordinates/components of the component in
+   * the indicated CoordinateSystem).
+   *
+   * \todo make this a protected member of CoordinateSystem
+   */
+  inline EigenTransform get_transformation(CoordinateSystem const& c1,
+                                           CoordinateSystem const& c2);
+
+} // namespace corsika
+
+#include <corsika/detail/framework/geometry/CoordinateSystem.inl>
diff --git a/corsika/framework/geometry/FourVector.hpp b/corsika/framework/geometry/FourVector.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..99932012d0ca5d4d99ae7427c62e957e0d37850c
--- /dev/null
+++ b/corsika/framework/geometry/FourVector.hpp
@@ -0,0 +1,198 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <type_traits>
+
+namespace corsika {
+
+  /**
+     Description of physical four-vectors
+
+     FourVector fully supports units, e.g. E in [GeV/c] and p in [GeV],
+     or also t in [s] and r in [m], etc.
+
+     However, for HEP applications it is also possible to use E and p
+     both in [GeV].
+
+     Thus, the input units of time-like and space-like coordinates
+     must either be idential (e.g. GeV) or scaled by "c" as in
+     [E/c]=[p].
+
+
+     The FourVector can return its squared-norm \ref getNormSqr and its
+     norm \ref getNorm, whereas norm is sqrt(abs(norm-squared)). The
+     physical units are always calculated and returned properly.
+
+     FourVector can also return if it is TimeLike, SpaceLike or PhotonLike.
+
+     When a FourVector is initialized with a lvalue references,
+     e.g. as `FourVector<TimeType&, Vector<length_d>&>`, references
+     are also used as internal data types, which should lead to
+     complete disappearance of the FourVector class during
+     optimization.
+   */
+
+  template <typename TTimeType, typename TSpaceVecType>
+  class FourVector {
+
+  public:
+    using space_vec_type = typename std::decay<TSpaceVecType>::type;
+    using space_type = typename space_vec_type::quantity_type;
+    using time_type = typename std::decay<TTimeType>::type;
+
+    // check the types and the physical units here:
+    static_assert(std::is_same<time_type, space_type>::value ||
+                      std::is_same<time_type, decltype(std::declval<space_type>() /
+                                                       meter * second)>::value,
+                  "Units of time-like and space-like coordinates must either be idential "
+                  "(e.g. GeV) or [E/c]=[p]");
+
+    using norm_type = space_type;
+    using norm_square_type =
+        decltype(std::declval<norm_type>() * std::declval<norm_type>());
+
+  public:
+    // resource management
+    FourVector() = default;
+    FourVector(FourVector&&) = default;
+    FourVector(FourVector const&) = default;
+    FourVector& operator=(const FourVector&) = default;
+    ~FourVector() = default;
+
+    FourVector(TTimeType const& eT, TSpaceVecType const& eS)
+        : timeLike_(eT)
+        , spaceLike_(eS) {}
+
+    /**
+     *
+     * @return timeLike_
+     */
+    TTimeType getTimeLikeComponent() const;
+
+    /**
+     *
+     * @return spaceLike_
+     */
+    TSpaceVecType& getSpaceLikeComponents();
+
+    /**
+     *
+     * @return spaceLike_;
+     */
+    TSpaceVecType const& getSpaceLikeComponents() const;
+
+    /**
+     *
+     * @return \f$p_0^2 - \vec{p}^2\f$
+     */
+    norm_square_type getNormSqr() const;
+
+    /**
+     *
+     * @return \f$\sqrt(p_0^2 - \vec{p}^2)\f$
+     */
+    norm_type getNorm() const;
+
+    /**
+     * \todo FIXME: a better alternative would be to define an enumeration
+     * enum { SpaceLike =-1, TimeLike, LightLike } V4R_Category;
+     * and a method called  V4R_Category GetCategory() const;
+     * RU: then you have to decide in the constructor which avoids "lazyness"
+     **/
+    ///\return if \f$|p_0|>|\vec{p}|\f$
+    bool isTimelike() const;
+    ///\return if \f$|p_0|<|\vec{p}|\f$
+    bool isSpacelike() const;
+
+    /**
+     * \name Math operators (class members)
+     * @{
+     */
+    FourVector& operator+=(FourVector const&);
+    FourVector& operator-=(FourVector const&);
+    FourVector& operator*=(double const);
+    FourVector& operator/=(double const);
+    FourVector& operator/(double const);
+
+    /**
+       Scalar product of two FourVectors
+
+       Note that the product between two 4-vectors assumes that you use
+       the same "c" convention for both. Only the LHS vector is checked
+       for this. You cannot mix different conventions due to
+       unit-checking.
+     */
+    norm_type operator*(FourVector const& b);
+
+    /** @} */
+
+  protected:
+    // the data members
+    TTimeType timeLike_;
+    TSpaceVecType spaceLike_;
+
+    /**
+     * \name Free math operators
+     * We need to define them inline here since we do not want to
+     * implement them as template functions. They are valid only for
+     * the specific types as defined right here.
+     *
+     * Note, these are "free function" (even if they don't look as
+     *  such). Thus, even if the input object uses internal references
+     *  for storage, the free math operators, of course, must provide
+     *  value-copies.
+     * @{
+     *
+     **/
+    friend FourVector<time_type, space_vec_type> operator+(FourVector const& a,
+                                                           FourVector const& b) {
+      return FourVector<time_type, space_vec_type>(a.timeLike_ + b.timeLike_,
+                                                   a.spaceLike_ + b.spaceLike_);
+    }
+
+    friend FourVector<time_type, space_vec_type> operator-(FourVector const& a,
+                                                           FourVector const& b) {
+      return FourVector<time_type, space_vec_type>(a.timeLike_ - b.timeLike_,
+                                                   a.spaceLike_ - b.spaceLike_);
+    }
+
+    friend FourVector<time_type, space_vec_type> operator*(FourVector const& a,
+                                                           double const b) {
+      return FourVector<time_type, space_vec_type>(a.timeLike_ * b, a.spaceLike_ * b);
+    }
+
+    friend FourVector<time_type, space_vec_type> operator/(FourVector const& a,
+                                                           double const b) {
+      return FourVector<time_type, space_vec_type>(a.timeLike_ / b, a.spaceLike_ / b);
+    }
+
+    /** @} */
+
+  private:
+    /**
+       This function is there to automatically remove the eventual
+       extra factor of "c" for the time-like quantity.
+     */
+    norm_square_type getTimeSquared() const;
+  };
+
+  /**
+   * streaming operator
+   **/
+
+  template <typename TTimeType, typename TSpaceVecType>
+  inline std::ostream& operator<<(
+      std::ostream& os, corsika::FourVector<TTimeType, TSpaceVecType> const& qv);
+
+} // namespace corsika
+
+#include <corsika/detail/framework/geometry/FourVector.inl>
diff --git a/corsika/framework/geometry/Helix.hpp b/corsika/framework/geometry/Helix.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5bd9fac77b71ec5e80ba54d26a19b6a3ab7b9d86
--- /dev/null
+++ b/corsika/framework/geometry/Helix.hpp
@@ -0,0 +1,70 @@
+/*
+ * (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 <cmath>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+namespace corsika {
+
+  /*!
+   * Defines a helical path
+   *
+   * A Helix is defined by the cyclotron frequency \f$ \omega_c \f$, the initial
+   * Point r0 and
+   * the velocity vectors \f$ \vec{v}_{\parallel} \f$ and \f$ \vec{v}_{\perp} \f$
+   * denoting the projections of the initial velocity \f$ \vec{v}_0 \f$ parallel
+   * and perpendicular to the axis \f$ \vec{B} \f$, respectively, i.e.
+   * \f{align*}{
+        \vec{v}_{\parallel} &= \frac{\vec{v}_0 \cdot \vec{B}}{\vec{B}^2} \vec{B} \\
+        \vec{v}_{\perp} &= \vec{v}_0 - \vec{v}_{\parallel}
+     \f}
+   */
+
+  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)
+        : r0_(pR0)
+        , omegaC_(pOmegaC)
+        , vPar_(pvPar)
+        , vPerp_(pvPerp)
+        , uPerp_(vPerp_.cross(vPar_.normalized()))
+        , radius_(pvPar.getNorm() / abs(pOmegaC)) {}
+
+    inline LengthType getRadius() const;
+
+    inline Point getPosition(TimeType const t) const;
+
+    VelocityVec getVelocity(TimeType const t) const;
+
+    inline Point getPositionFromArclength(LengthType const l) const;
+
+    inline LengthType getArcLength(TimeType const t1, TimeType const t2) const;
+
+    inline 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_;
+    LengthType radius_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/geometry/Helix.inl>
diff --git a/Framework/Geometry/Volume.h b/corsika/framework/geometry/IVolume.hpp
similarity index 50%
rename from Framework/Geometry/Volume.h
rename to corsika/framework/geometry/IVolume.hpp
index c1dfbd3130765f169344ccd421399eaeca2a93ac..1960dfdf2415b1f270f85e78c4d1b38e6eb5d8c2 100644
--- a/Framework/Geometry/Volume.h
+++ b/corsika/framework/geometry/IVolume.hpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
@@ -8,17 +8,17 @@
 
 #pragma once
 
-#include <corsika/geometry/Point.h>
+#include <corsika/framework/geometry/Point.hpp>
 
-namespace corsika::geometry {
+namespace corsika {
 
-  class Volume {
+  class IVolume {
 
   public:
     //! returns true if the Point p is within the volume
-    virtual bool Contains(Point const& p) const = 0;
+    virtual bool contains(Point const& p) const = 0;
 
-    virtual ~Volume() = default;
+    virtual ~IVolume() = default;
   };
 
-} // namespace corsika::geometry
+} // namespace corsika
diff --git a/Framework/Geometry/Intersections.hpp b/corsika/framework/geometry/Intersections.hpp
similarity index 61%
rename from Framework/Geometry/Intersections.hpp
rename to corsika/framework/geometry/Intersections.hpp
index 1d0ca566d8e16a4721619543ac4f024ee50d22ad..c9951213ef162fb273626f77dd8e3707f17403c2 100644
--- a/Framework/Geometry/Intersections.hpp
+++ b/corsika/framework/geometry/Intersections.hpp
@@ -8,14 +8,13 @@
 
 #pragma once
 
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/framework/core/PhysicalUnits.hpp>
 
 #include <map> // for pair
 
-namespace corsika::geometry {
+namespace corsika {
 
   /**
-   * \class Intersection
    *
    * Container to store and return a list of intersections of a
    * trajectory with a geometric volume objects in space.
@@ -31,25 +30,27 @@ namespace corsika::geometry {
   public:
     Intersections()
         : has_intersections_(false) {}
-    Intersections(corsika::units::si::TimeType&& t1, corsika::units::si::TimeType&& t2)
+
+    Intersections(TimeType&& t1, TimeType&& t2)
         : has_intersections_(true)
         , intersections_(std::make_pair(t1, t2)) {}
-    Intersections(corsika::units::si::TimeType&& t)
+
+    Intersections(TimeType&& t)
         : has_intersections_(true)
         , intersections_(std::make_pair(
-              t,
-              std::numeric_limits<corsika::units::si::TimeType::value_type>::infinity() *
-                  corsika::units::si::second)) {}
+              t, std::numeric_limits<TimeType::value_type>::infinity() * second)) {}
 
     bool hasIntersections() const { return has_intersections_; }
+
     ///! where did the trajectory currently enter the volume
-    corsika::units::si::TimeType getEntry() const { return intersections_.first; }
+    TimeType getEntry() const { return intersections_.first; }
+
     ///! where did the trajectory currently exit the volume
-    corsika::units::si::TimeType getExit() const { return intersections_.second; }
+    TimeType getExit() const { return intersections_.second; }
 
   private:
     bool has_intersections_;
-    std::pair<corsika::units::si::TimeType, corsika::units::si::TimeType> intersections_;
+    std::pair<TimeType, TimeType> intersections_;
   };
 
-} // namespace corsika::geometry
+} // namespace corsika
diff --git a/corsika/framework/geometry/LeapFrogTrajectory.hpp b/corsika/framework/geometry/LeapFrogTrajectory.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6b67bf6345a5c79400d72d9ec16e13afa380e13f
--- /dev/null
+++ b/corsika/framework/geometry/LeapFrogTrajectory.hpp
@@ -0,0 +1,83 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+
+namespace corsika {
+
+  /**
+   * The LeapFrogTrajectory stores information on one leap-frog step.
+   *
+   * The leap-frog algorithm uses a half-step and is used in magnetic
+   * field tracking. The LeapFrogTrajectory will solve the leap-frog
+   * algorithm equation for a given constant $k$ that has to be
+   * specified during construction (essentially fixing the magnetic
+   * field). Thus, different steps (length) can be dynamically
+   * generated here. The velocity vector will correctly point into the
+   * direction as calculated by the algorithm for any steplength, or
+   * intermediate position.
+   *
+   **/
+
+  class LeapFrogTrajectory {
+
+  public:
+    LeapFrogTrajectory() = delete;
+    LeapFrogTrajectory(LeapFrogTrajectory const&) = default;
+    LeapFrogTrajectory(LeapFrogTrajectory&&) = default;
+    LeapFrogTrajectory& operator=(LeapFrogTrajectory const&) = delete;
+
+    LeapFrogTrajectory(Point const& pos, VelocityVector const& initialVelocity,
+                       MagneticFieldVector const& Bfield,
+                       decltype(square(meter) / (square(second) * volt)) const k,
+                       TimeType const timeStep) // leap-from total length
+        : initialPosition_(pos)
+        , initialVelocity_(initialVelocity)
+        , initialDirection_(initialVelocity.normalized())
+        , magneticfield_(Bfield)
+        , k_(k)
+        , timeStep_(timeStep) {}
+
+    Line getLine() const;
+
+    Point getPosition(double const u) const;
+
+    VelocityVector getVelocity(double const u) const;
+
+    DirectionVector getDirection(double const u) const;
+
+    ///! duration along potentially bend trajectory
+    TimeType getDuration(double const u = 1) const;
+
+    ///! total length along potentially bend trajectory
+    LengthType getLength(double const u = 1) const;
+
+    ///! set new duration along potentially bend trajectory.
+    void setLength(LengthType const limit);
+
+    ///! set new duration along potentially bend trajectory.
+    //   Scale other properties by "limit/timeLength_"
+    void setDuration(TimeType const limit);
+
+  private:
+    Point initialPosition_;
+    VelocityVector initialVelocity_;
+    DirectionVector initialDirection_;
+    MagneticFieldVector magneticfield_;
+    decltype(square(meter) / (square(second) * volt)) k_;
+    TimeType timeStep_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/geometry/LeapFrogTrajectory.inl>
diff --git a/corsika/framework/geometry/Line.hpp b/corsika/framework/geometry/Line.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cdb03d6245945a421d31badeae339fa49d9ef71f
--- /dev/null
+++ b/corsika/framework/geometry/Line.hpp
@@ -0,0 +1,57 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+
+namespace corsika {
+
+  /**
+   *
+   * A Line describes a movement in three dimensional space. It
+   * consists of a Point `$\vec{p_0}$` and and a speed-Vector
+   * `$\vec{v}$`, so that it can return GetPosition as
+   * `$\vec{p_0}*\vec{v}*t$` for any value of time `$t$`.
+   *
+   **/
+
+  class Line {
+
+  public:
+    Line(Point const& pR0, VelocityVector const& pV0)
+        : start_point_(pR0)
+        , velocity_(pV0) {}
+
+    inline Point getPosition(TimeType const t) const;
+
+    inline VelocityVector const& getVelocity(TimeType const) const;
+
+    inline Point getPositionFromArclength(LengthType const l) const;
+
+    inline LengthType getArcLength(TimeType const t1, TimeType const t2) const;
+
+    inline TimeType getTimeFromArclength(LengthType const t) const;
+
+    inline Point const& getStartPoint() const;
+
+    inline DirectionVector getDirection() const;
+
+    inline VelocityVector const& getVelocity() const;
+
+  private:
+    Point start_point_;
+    VelocityVector velocity_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/geometry/Line.inl>
diff --git a/corsika/framework/geometry/PhysicalGeometry.hpp b/corsika/framework/geometry/PhysicalGeometry.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a0d9ba4236bf10be757786f036cb5a02f598a09e
--- /dev/null
+++ b/corsika/framework/geometry/PhysicalGeometry.hpp
@@ -0,0 +1,28 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+/**
+ * \file PhysicalUnits.hpp
+ *
+ * Import and extend the phys::units package. The SI units are also imported into the
+ * `\namespace corsika`, since they are used everywhere as integral part of the framework.
+ */
+
+namespace corsika {
+
+  typedef Vector<hepmomentum_d> MomentumVector;
+  typedef Vector<dimensionless_d> DirectionVector;
+  typedef Vector<magnetic_flux_density_d> MagneticFieldVector;
+  typedef Vector<SpeedType::dimension_type> VelocityVector;
+
+} // namespace corsika
diff --git a/corsika/framework/geometry/Plane.hpp b/corsika/framework/geometry/Plane.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9b2eacade48f5d9913d5b5311f1ae595cbcde039
--- /dev/null
+++ b/corsika/framework/geometry/Plane.hpp
@@ -0,0 +1,42 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+namespace corsika {
+
+  class Plane {
+
+    ///! \todo move to PhysicalUnits
+    using DimLessVec = Vector<dimensionless_d>;
+
+  public:
+    Plane(Point const& vCenter, DimLessVec const& vNormal)
+        : center_(vCenter)
+        , normal_(vNormal.normalized()) {}
+
+    bool isAbove(Point const& vP) const;
+
+    LengthType getDistanceTo(corsika::Point const& vP) const;
+
+    Point const& getCenter() const;
+
+    DimLessVec const& getNormal() const;
+
+  public:
+    Point const center_;
+    DimLessVec const normal_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/geometry/Plane.inl>
diff --git a/corsika/framework/geometry/Point.hpp b/corsika/framework/geometry/Point.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..82d783212b5f2309b1936b465c86434f5674e784
--- /dev/null
+++ b/corsika/framework/geometry/Point.hpp
@@ -0,0 +1,84 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/BaseVector.hpp>
+#include <corsika/framework/geometry/QuantityVector.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+namespace corsika {
+
+  /*!
+   * A Point represents a point in position space. It is defined by its
+   * coordinates with respect to some CoordinateSystem.
+   */
+  class Point : public BaseVector<length_d> {
+
+  public:
+    Point(CoordinateSystemPtr const& pCS, QuantityVector<length_d> const& pQVector)
+        : BaseVector<length_d>(pCS, pQVector) {}
+
+    Point(CoordinateSystemPtr const& cs, LengthType x, LengthType y, LengthType z)
+        : BaseVector<length_d>(cs, {x, y, z}) {}
+
+    /** \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();
+
+    /**
+       this always returns a QuantityVector as triple
+
+       \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;
+
+    /**
+     * this always returns a QuantityVector as triple
+     *
+     *  \returns A reference type QuantityVector&, but be aware, the underlying class data
+     *   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);
+
+    /**
+     * \defgroup access coordinate components
+     * \{
+     *
+     * Note, if you access components in a different CoordinateSystem
+     * pCS than the stored data, internally a temporary object will be
+     * 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;
+    /** \} **/
+
+    /*!
+     * transforms the Point into another CoordinateSystem by changing its
+     * coordinates interally
+     */
+    inline void rebase(CoordinateSystemPtr const& pCS);
+
+    inline Point operator+(Vector<length_d> const& pVec) const;
+
+    /*!
+     * returns the distance Vector between two points
+     */
+    inline Vector<length_d> operator-(Point const& pB) const;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/geometry/Point.inl>
diff --git a/corsika/framework/geometry/QuantityVector.hpp b/corsika/framework/geometry/QuantityVector.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..235237afe59b8ffbf19413e625e7bdd1178a18e8
--- /dev/null
+++ b/corsika/framework/geometry/QuantityVector.hpp
@@ -0,0 +1,121 @@
+/*
+ * (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 <Eigen/Dense>
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <ostream>
+#include <utility>
+
+namespace corsika {
+
+  class CoordinateSystem; // fwd decl
+  class Point;            // fwd decl
+  template <typename T>
+  class Vector; // fwd decl
+
+  /*!
+   * A QuantityVector is a three-component container based on Eigen::Vector3d
+   * with a phys::units::si::dimension. Arithmethic operators are defined that
+   * propagate the dimensions by dimensional analysis.
+   *
+   * \todo review QuantityVector: can this be a protected-only
+   * inheritance to other objects? We don't want to expose access to
+   * low-level Eigen objects anywhere.
+   */
+
+  template <typename TDimension>
+  class QuantityVector {
+  public:
+    using quantity_type =
+        phys::units::quantity<TDimension, double>; //< the phys::units::quantity
+                                                   // corresponding to the dimension
+    using quantity_square_type =
+        decltype(std::declval<quantity_type>() * std::declval<quantity_type>());
+
+    QuantityVector(Eigen::Vector3d const& pBareVector)
+        : eigenVector_(pBareVector) {}
+
+  public:
+    typedef TDimension dimension_type; //!< should be a phys::units::dimension
+
+    QuantityVector(quantity_type const a, quantity_type const b, quantity_type const c)
+        : eigenVector_{a.magnitude(), b.magnitude(), c.magnitude()} {}
+
+    QuantityVector(double const a, double const b, double const c)
+        : eigenVector_{a, b, c} {
+      static_assert(
+          std::is_same_v<TDimension, phys::units::dimensionless_d>,
+          "initialization of dimensionful QuantityVector with pure numbers not allowed!");
+    }
+
+    quantity_type operator[](size_t const index) const;
+    quantity_type getX() const;
+    quantity_type getY() const;
+    quantity_type getZ() const;
+    Eigen::Vector3d const& getEigenVector() const { return eigenVector_; }
+    Eigen::Vector3d& getEigenVector() { return eigenVector_; }
+
+    quantity_type getNorm() const;
+
+    quantity_square_type getSquaredNorm() const;
+
+    QuantityVector operator+(QuantityVector<TDimension> const& pQVec) const;
+
+    QuantityVector operator-(QuantityVector<TDimension> const& pQVec) const;
+
+    template <typename TScalarDim>
+    auto operator*(phys::units::quantity<TScalarDim, double> const p) const;
+
+    template <typename TScalarDim>
+    auto operator/(phys::units::quantity<TScalarDim, double> const p) const;
+
+    auto operator*(double const p) const;
+
+    auto operator/(double const p) const;
+
+    auto& operator/=(double const p);
+
+    auto& operator*=(double const p);
+
+    auto& operator+=(QuantityVector<TDimension> const& pQVec);
+
+    auto& operator-=(QuantityVector<TDimension> const& pQVec);
+
+    auto& operator-() const;
+
+    auto normalized() const;
+
+    auto operator==(QuantityVector<TDimension> const& p) const;
+
+    // friends:
+    friend class CoordinateSystem;
+    friend class Point;
+    template <typename T>
+    friend class corsika::Vector;
+    template <typename TDim>
+    friend std::ostream& operator<<(std::ostream& os, QuantityVector<TDim> qv);
+
+  protected:
+    Eigen::Vector3d
+        eigenVector_; //!< the actual container where the raw numbers are stored
+  };
+
+  /**
+   * streaming operator
+   **/
+
+  template <typename TDimension>
+  inline std::ostream& operator<<(std::ostream& os,
+                                  corsika::QuantityVector<TDimension> const qv);
+
+} // namespace corsika
+
+#include <corsika/detail/framework/geometry/QuantityVector.inl>
diff --git a/corsika/framework/geometry/RootCoordinateSystem.hpp b/corsika/framework/geometry/RootCoordinateSystem.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..41d2654d5973efedf63ca1ab1b0b640acd3e89b2
--- /dev/null
+++ b/corsika/framework/geometry/RootCoordinateSystem.hpp
@@ -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 <corsika/framework/geometry/CoordinateSystem.hpp>
+
+#include <memory>
+
+namespace corsika {
+
+  /**
+   * To refer to CoordinateSystems, only the CoordinateSystemPtr must be used.
+   */
+  using CoordinateSystemPtr = std::shared_ptr<CoordinateSystem const>;
+
+  /*!
+   * Singleton factory function to produce the root CoordinateSystem
+   *
+   * This is the only way to get a root-coordinate system, and it is a
+   * singleton. All other CoordinateSystems must be relative to the
+   * RootCoordinateSystem
+   */
+
+  static inline CoordinateSystemPtr& get_root_CoordinateSystem() {
+    static CoordinateSystemPtr rootCS(new CoordinateSystem); // THIS IS IT
+    return rootCS;
+  }
+
+} // namespace corsika
diff --git a/corsika/framework/geometry/Sphere.hpp b/corsika/framework/geometry/Sphere.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..81e5e0b15263f6430d912378650f900f5c74bd0c
--- /dev/null
+++ b/corsika/framework/geometry/Sphere.hpp
@@ -0,0 +1,48 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/IVolume.hpp>
+
+namespace corsika {
+
+  /**
+   * Describes a sphere in space
+   *
+   **/
+
+  class Sphere : public IVolume {
+
+  public:
+    Sphere(Point const& pCenter, LengthType const pRadius)
+        : center_(pCenter)
+        , radius_(pRadius) {}
+
+    //! returns true if the Point p is within the sphere
+    bool contains(Point const& p) const override;
+
+    Point const& getCenter() const;
+
+    void setCenter(Point const&);
+
+    LengthType getRadius() const;
+
+    void setRadius(LengthType const);
+
+  private:
+    Point center_;
+    LengthType radius_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/geometry/Sphere.inl>
diff --git a/corsika/framework/geometry/StraightTrajectory.hpp b/corsika/framework/geometry/StraightTrajectory.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b05746269b017f7a80fe5c5807ca93c7afe336ab
--- /dev/null
+++ b/corsika/framework/geometry/StraightTrajectory.hpp
@@ -0,0 +1,124 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+
+namespace corsika {
+
+  /**
+   *
+   * A Trajectory is a description of a momvement of an object in
+   * three-dimensional space that describes the trajectory (connection
+   * between two Points in space), as well as the direction of motion
+   * at any given point.
+   *
+   * A Trajectory has a start `0` and an end `1`, where
+   * e.g. getPosition(0) returns the start point and getDirection(1)
+   * the direction of motion at the end. Values outside 0...1 are not
+   * defined.
+   *
+   * A Trajectory has a length in [m], getLength, a duration in [s], getDuration.
+   *
+   * Note: so far it is assumed that the speed (d|vec{r}|/dt) between
+   * start and end does not change and is constant for the entire
+   * Trajectory.
+   *
+   **/
+
+  class StraightTrajectory {
+
+  public:
+    StraightTrajectory() = delete;
+    StraightTrajectory(StraightTrajectory const&) = default;
+    StraightTrajectory(StraightTrajectory&&) = default;
+    StraightTrajectory& operator=(StraightTrajectory const&) = delete;
+
+    /**
+     * \param theLine The geometric Line object that represents a straight-line
+     * connection
+     *
+     * \param timeLength The time duration to traverse the straight trajectory
+     * in units of TimeType
+     */
+    StraightTrajectory(Line const& theLine, TimeType timeLength)
+        : line_(theLine)
+        , timeLength_(timeLength)
+        , timeStep_(timeLength)
+        , initialVelocity_(theLine.getVelocity(TimeType::zero()))
+        , finalVelocity_(theLine.getVelocity(timeLength)) {}
+
+    /**
+     * \param theLine The geometric Line object that represents a straight-line
+     * connection
+     *
+     * \param timeLength The time duration to traverse the straight trajectory
+     * in units of TimeType
+     *
+     * \param timeStep Time duration to folow eventually curved
+     * trajectory in units of TimesType
+     *
+     * \param initialV Initial velocity vector at
+     * start of trajectory
+     *
+     * \param finalV Final velocity vector at start of trajectory
+     */
+    StraightTrajectory(Line const& theLine,
+                       TimeType const timeLength, // length of theLine (straight)
+                       TimeType const timeStep,   // length of bend step (curved)
+                       VelocityVector const& initialV, VelocityVector const& finalV)
+        : line_(theLine)
+        , timeLength_(timeLength)
+        , timeStep_(timeStep)
+        , initialVelocity_(initialV)
+        , finalVelocity_(finalV) {}
+
+    Line const& getLine() const { return line_; }
+
+    Point getPosition(double const u) const { return line_.getPosition(timeLength_ * u); }
+
+    VelocityVector getVelocity(double const u) const;
+
+    DirectionVector getDirection(double const u) const {
+      return getVelocity(u).normalized();
+    }
+
+    ///! duration along potentially bend trajectory
+    TimeType getDuration(double const u = 1) const;
+
+    ///! total length along potentially bend trajectory
+    LengthType getLength(double const u = 1) const;
+
+    ///! set new duration along potentially bend trajectory.
+    void setLength(LengthType const limit);
+
+    ///! set new duration along potentially bend trajectory.
+    //   Scale other properties by "limit/timeLength_"
+    void setDuration(TimeType const limit);
+
+  protected:
+    ///! total length along straight trajectory
+    LengthType getDistance(double const u) const;
+
+    void setFinalVelocity(VelocityVector const& v) { finalVelocity_ = v; }
+
+  private:
+    Line line_;
+    TimeType timeLength_; ///! length of straight step (shortest connecting line)
+    TimeType timeStep_;   ///! length of bend step (curved)
+    VelocityVector initialVelocity_;
+    VelocityVector finalVelocity_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/geometry/StraightTrajectory.inl>
diff --git a/corsika/framework/geometry/Vector.hpp b/corsika/framework/geometry/Vector.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0d37c66c7afb5460e09a8f0001ad248bbe59835c
--- /dev/null
+++ b/corsika/framework/geometry/Vector.hpp
@@ -0,0 +1,147 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/BaseVector.hpp>
+#include <corsika/framework/geometry/QuantityVector.hpp>
+
+namespace corsika {
+
+  /*!
+   * A Vector represents a 3-vector in Euclidean space.
+   *
+   * It is defined by components
+   * given in a specific CoordinateSystem. It has a physical dimension ("unit")
+   * as part of its type, so you cannot mix up e.g. electric with magnetic fields
+   * (but you could calculate their cross-product to get an energy flux vector).
+   *
+   * When transforming coordinate systems, a Vector is subject to the rotational
+   * part only and invariant under translations.
+   */
+
+  template <typename TDimension>
+  class Vector : public BaseVector<TDimension> {
+  public:
+    using quantity_type = phys::units::quantity<TDimension, double>;
+    using quantity_square_type =
+        decltype(std::declval<quantity_type>() * std::declval<quantity_type>());
+
+    Vector(CoordinateSystemPtr const& pCS, QuantityVector<TDimension> const& pQVector)
+        : BaseVector<TDimension>(pCS, pQVector) {}
+
+    Vector(CoordinateSystemPtr const& cs, quantity_type const x, quantity_type const y,
+           quantity_type const z)
+        : BaseVector<TDimension>(cs, QuantityVector<TDimension>(x, y, z)) {}
+
+    /*!
+     * \returns a QuantityVector with the components given in the "home"
+     * CoordinateSystem of the Vector
+     *
+     * \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();
+
+    /*!
+     * returns a QuantityVector with the components given in an arbitrary
+     * CoordinateSystem
+     */
+    inline QuantityVector<TDimension> getComponents(CoordinateSystemPtr const& pCS) const;
+
+    /**
+     * this always returns a QuantityVector as triple
+     *
+     *  \return A reference type QuantityVector&, but be aware, the underlying class data
+     *   is actually transformed to pCS, if needed. Thus, there may be an implicit call to
+     *   \ref rebase.
+     **/
+    inline QuantityVector<TDimension>& getComponents(CoordinateSystemPtr const& pCS);
+
+    /**
+     * \name Access coordinate components
+     *
+     * Note, if you access components in a different CoordinateSystem
+     * pCS than the stored data, internally a temporary object will be
+     * created and destroyed each call. This can be avoided by using
+     * \ref rebase first.
+     *
+     * \{
+     **/
+
+    inline quantity_type getX(CoordinateSystemPtr const& pCS) const;
+    inline quantity_type getY(CoordinateSystemPtr const& pCS) const;
+    inline quantity_type getZ(CoordinateSystemPtr const& pCS) const;
+    /** \} **/
+
+    /*!
+     * transforms the Vector into another CoordinateSystem by changing
+     * its components internally
+     */
+    inline 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;
+
+    /*!
+     * 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;
+
+    /*!
+     * returns a Vector \f$ \vec{v}_{\parallel} \f$ which is the parallel projection
+     * of this vector \f$ \vec{v}_1 \f$ along another Vector \f$ \vec{v}_2 \f$ given by
+     *   \f[
+     *     \vec{v}_{\parallel} = \frac{\vec{v}_1 \cdot \vec{v}_2}{\vec{v}_2^2} \vec{v}_2
+     *   \f]
+     */
+    template <typename TDimension2>
+    auto getParallelProjectionOnto(Vector<TDimension2> const& pVec,
+                                   CoordinateSystemPtr const& pCS) const;
+    template <typename TDimension2>
+    auto getParallelProjectionOnto(Vector<TDimension2> const& pVec) const;
+
+    Vector operator+(Vector<TDimension> const& pVec) const;
+
+    Vector operator-(Vector<TDimension> const& pVec) const;
+
+    auto& operator*=(double const p);
+
+    template <typename TScalarDim>
+    auto operator*(phys::units::quantity<TScalarDim, double> const p) const;
+    template <typename TScalarDim>
+    auto operator/(phys::units::quantity<TScalarDim, double> const p) const;
+
+    auto operator*(double const p) const;
+
+    auto operator/(double const p) const;
+
+    auto& operator+=(Vector<TDimension> const& pVec);
+
+    auto& operator-=(Vector<TDimension> const& pVec);
+
+    auto& operator-() const;
+
+    auto normalized() const;
+
+    template <typename TDimension2>
+    auto cross(Vector<TDimension2> const& pV) const;
+
+    template <typename TDimension2>
+    auto dot(Vector<TDimension2> const& pV) const;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/geometry/Vector.inl>
diff --git a/Framework/ProcessSequence/BaseProcess.h b/corsika/framework/process/BaseProcess.hpp
similarity index 59%
rename from Framework/ProcessSequence/BaseProcess.h
rename to corsika/framework/process/BaseProcess.hpp
index c1a4e232e0e0689b67aaca62af99b0abcfb47c8f..e91651d12ea99cafd40718ae288a869852a5cf35 100644
--- a/Framework/ProcessSequence/BaseProcess.h
+++ b/corsika/framework/process/BaseProcess.hpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
@@ -8,25 +8,26 @@
 
 #pragma once
 
-#include <corsika/process/ProcessReturn.h> // for convenience
-
-namespace corsika::process {
+namespace corsika {
 
   class TDerived; // fwd decl
 
   /**
-     \class BaseProcess
+     Each process in C8 must derive from BaseProcess
 
      The structural base type of a process object in a
      ProcessSequence. Both, the ProcessSequence and all its elements
      are of type BaseProcess<T>
 
      \todo rename BaseProcess into just Process
+     \todo rename _BaseProcess, or find better alternative in FIXME
+     ./Processes/AnalyticProcessors/ExecTime.h, see e.g. how this is done in
+     ProcessSequence.hpp/make_sequence
    */
   class _BaseProcess {};
 
   template <typename TDerived>
-  class BaseProcess : _BaseProcess {
+  struct BaseProcess : _BaseProcess {
   protected:
     friend TDerived;
 
@@ -34,12 +35,12 @@ namespace corsika::process {
                              // derived classes to be created, not
                              // BaseProcess itself
 
-    TDerived& GetRef() { return static_cast<TDerived&>(*this); }
-    const TDerived& GetRef() const { return static_cast<const TDerived&>(*this); }
+    TDerived& ref() { return static_cast<TDerived&>(*this); }
+    const TDerived& ref() const { return static_cast<const TDerived&>(*this); }
 
   public:
     // Base processor type for use in other template classes
-    using TProcessType = TDerived;
+    using process_type = TDerived;
   };
 
-} // namespace corsika::process
+} // namespace corsika
diff --git a/corsika/framework/process/BoundaryCrossingProcess.hpp b/corsika/framework/process/BoundaryCrossingProcess.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1e9a4bdee4eb3de6de009e7a1274bddb3e967d2a
--- /dev/null
+++ b/corsika/framework/process/BoundaryCrossingProcess.hpp
@@ -0,0 +1,45 @@
+/*
+ * (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 <corsika/framework/process/ProcessReturn.hpp>
+#include <corsika/media/Environment.hpp>
+
+#include <type_traits>
+
+namespace corsika {
+
+  /*
+  struct passepartout {
+    template <typename T>
+    operator T&();
+
+    template <typename T>
+    operator T &&();
+    };*/
+
+  template <typename TDerived>
+  class BoundaryCrossingProcess : public BaseProcess<TDerived> {
+
+    /*    static_assert(std::is_invocable_v<decltype(&TDerived<>::doBoundaryCrossing),
+       TDerived&, passepartout>, "BoundaryCrossingProcess needs
+       doBoundaryCrossing(TParticle, " "TParticle::node_type, TParticle::node_type)");*/
+
+  public:
+    /**
+     * This method is called when a particle crosses the boundary between the nodes
+     * \p from and \p to.
+     */
+    template <typename TParticle>
+    ProcessReturn doBoundaryCrossing(TParticle&,
+                                     typename TParticle::node_type const& from,
+                                     typename TParticle::node_type const& to);
+  };
+
+} // namespace corsika
diff --git a/Framework/ProcessSequence/ContinuousProcess.h b/corsika/framework/process/ContinuousProcess.hpp
similarity index 64%
rename from Framework/ProcessSequence/ContinuousProcess.h
rename to corsika/framework/process/ContinuousProcess.hpp
index c4138a755264be14c3551ddbb310dceb2495231a..1343c082272a8e0e49830e53c164f985abe5e860 100644
--- a/Framework/ProcessSequence/ContinuousProcess.h
+++ b/corsika/framework/process/ContinuousProcess.hpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
@@ -8,13 +8,13 @@
 
 #pragma once
 
-#include <corsika/process/BaseProcess.h>
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/framework/process/BaseProcess.hpp>
+#include <corsika/framework/process/ProcessReturn.hpp>
 
-namespace corsika::process {
+namespace corsika {
 
   /**
-     \class ContinuousProcess
+     Processes with continuous effects along a particle Trajectory
 
      The structural base type of a process object in a
      ProcessSequence. Both, the ProcessSequence and all its elements
@@ -30,11 +30,11 @@ namespace corsika::process {
     // here starts the interface part
     // -> enforce TDerived to implement DoContinuous...
     template <typename TParticle, typename TTrack>
-    EProcessReturn DoContinuous(TParticle&, TTrack const&) const;
+    ProcessReturn doContinuous(TParticle&, TTrack const&) const;
 
     // -> enforce TDerived to implement MaxStepLength...
     template <typename TParticle, typename TTrack>
-    units::si::LengthType MaxStepLength(TParticle const& p, TTrack const& track) const;
+    LengthType getMaxStepLength(TParticle const& p, TTrack const& track) const;
   };
 
-} // namespace corsika::process
+} // namespace corsika
diff --git a/corsika/framework/process/DecayProcess.hpp b/corsika/framework/process/DecayProcess.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..64298dc3442176149f6cd7747219f7d2a9bac482
--- /dev/null
+++ b/corsika/framework/process/DecayProcess.hpp
@@ -0,0 +1,44 @@
+/*
+ * (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 <corsika/framework/process/BaseProcess.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+namespace corsika {
+
+  /**
+     Process decribing the decay of particles
+
+     The structural base type of a process object in a
+     ProcessSequence. Both, the ProcessSequence and all its elements
+     are of type DecayProcess<T>
+
+   */
+
+  template <typename TDerived>
+  struct DecayProcess : BaseProcess<TDerived> {
+  public:
+    using BaseProcess<TDerived>::ref;
+
+    /// here starts the interface-definition part
+    // -> enforce TDerived to implement DoDecay...
+    template <typename TParticle>
+    void doDecay(TParticle&);
+
+    template <typename TParticle>
+    TimeType getLifetime(TParticle const&);
+
+    template <typename TParticle>
+    InverseTimeType getInverseLifetime(TParticle const& particle) {
+      return 1. / ref().getLifetime(particle);
+    }
+  };
+
+} // namespace corsika
diff --git a/corsika/framework/process/InteractionCounter.hpp b/corsika/framework/process/InteractionCounter.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..143fa430e79fea07937c67117646e79919f61f06
--- /dev/null
+++ b/corsika/framework/process/InteractionCounter.hpp
@@ -0,0 +1,43 @@
+/*
+ * (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 <corsika/framework/process/InteractionHistogram.hpp>
+#include <corsika/framework/process/InteractionProcess.hpp>
+
+namespace corsika {
+
+  /*!
+   * Wrapper around an InteractionProcess that fills histograms of the number
+   * of calls to DoInteraction() binned in projectile energy (both in
+   * lab and center-of-mass frame) and species
+   */
+  template <class TCountedProcess>
+  class InteractionCounter
+      : public InteractionProcess<InteractionCounter<TCountedProcess>> {
+
+  public:
+    InteractionCounter(TCountedProcess& process);
+
+    template <typename TSecondaryView>
+    void doInteraction(TSecondaryView& view);
+
+    template <typename TParticle>
+    GrammageType getInteractionLength(TParticle const& particle) const;
+
+    InteractionHistogram const& getHistogram() const;
+
+  private:
+    TCountedProcess& process_;
+    InteractionHistogram histogram_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/process/InteractionCounter.inl>
diff --git a/corsika/framework/process/InteractionHistogram.hpp b/corsika/framework/process/InteractionHistogram.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..667ba3b9820d29bfdbe32b7d7a5643a4699f1a95
--- /dev/null
+++ b/corsika/framework/process/InteractionHistogram.hpp
@@ -0,0 +1,55 @@
+/*
+ * (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 <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/utility/SaveBoostHistogram.hpp>
+
+#include <boost/histogram.hpp>
+
+#include <fstream>
+#include <functional>
+#include <map>
+#include <utility>
+
+#include <corsika/detail/framework/process/InteractionHistogram.hpp> // for detail namespace
+
+namespace corsika {
+
+  class InteractionHistogram {
+    static double constexpr lower_edge_cms = 1e3, upper_edge_cms = 1e17; // eV sqrt s
+    static double constexpr lower_edge_lab = 1e3, upper_edge_lab = 1e21; // eV lab
+    static unsigned int constexpr num_bins_lab = 18 * 10, num_bins_cms = 14 * 10;
+
+    using hist_type =
+        decltype(detail::hist_factory(num_bins_lab, lower_edge_lab, upper_edge_lab));
+
+    hist_type inthist_cms_, inthist_lab_;
+
+  public:
+    InteractionHistogram();
+
+    //! fill both CMS and lab histograms at the same time
+    void fill(Code projectile_id, HEPEnergyType lab_energy, HEPEnergyType mass_target,
+              int A = 0, int Z = 0);
+
+    hist_type const& CMSHist() const { return inthist_cms_; }
+    hist_type const& labHist() const { return inthist_lab_; }
+
+    void saveLab(std::string const& filename, SaveMode mode = SaveMode::append) const;
+    void saveCMS(std::string const& filename, SaveMode mode = SaveMode::append) const;
+
+    InteractionHistogram& operator+=(InteractionHistogram const& other);
+    InteractionHistogram operator+(InteractionHistogram other) const;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/process/InteractionHistogram.inl> // for implementation
diff --git a/Framework/ProcessSequence/InteractionProcess.h b/corsika/framework/process/InteractionProcess.hpp
similarity index 55%
rename from Framework/ProcessSequence/InteractionProcess.h
rename to corsika/framework/process/InteractionProcess.hpp
index e70729db8d2ae7ba3149b1f42ade39d78aaf29b5..f80480926c74b5f908e3fe7b4c85643a30cfeadd 100644
--- a/Framework/ProcessSequence/InteractionProcess.h
+++ b/corsika/framework/process/InteractionProcess.hpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
@@ -8,13 +8,13 @@
 
 #pragma once
 
-#include <corsika/process/BaseProcess.h>
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/framework/process/BaseProcess.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
 
-namespace corsika::process {
+namespace corsika {
 
   /**
-     \class InteractionProcess
+     Process describing the interaction of particles
 
      The structural base type of a process object in a
      ProcessSequence. Both, the ProcessSequence and all its elements
@@ -25,21 +25,20 @@ namespace corsika::process {
   template <typename TDerived>
   class InteractionProcess : public BaseProcess<TDerived> {
   public:
-    using BaseProcess<TDerived>::GetRef;
+    using BaseProcess<TDerived>::ref;
 
     /// here starts the interface-definition part
     // -> enforce TDerived to implement DoInteraction...
     template <typename TParticle>
-    EProcessReturn DoInteraction(TParticle&);
+    void doInteraction(TParticle&);
 
     template <typename TParticle>
-    corsika::units::si::GrammageType GetInteractionLength(const TParticle&);
+    GrammageType getInteractionLength(TParticle const&);
 
     template <typename TParticle>
-    corsika::units::si::InverseGrammageType GetInverseInteractionLength(
-        const TParticle& particle) {
-      return 1. / GetRef().GetInteractionLength(particle);
+    InverseGrammageType getInverseInteractionLength(TParticle const& particle) {
+      return 1. / ref().getInteractionLength(particle);
     }
   };
 
-} // namespace corsika::process
+} // namespace corsika
diff --git a/Framework/ProcessSequence/NullModel.h b/corsika/framework/process/NullModel.hpp
similarity index 63%
rename from Framework/ProcessSequence/NullModel.h
rename to corsika/framework/process/NullModel.hpp
index feb0debd90b9063cab60c48ae96e7c16ffbc5207..0e4746f31a924f4bf597f93eada980696d493efb 100644
--- a/Framework/ProcessSequence/NullModel.h
+++ b/corsika/framework/process/NullModel.hpp
@@ -8,15 +8,19 @@
 
 #pragma once
 
-#include <corsika/process/BaseProcess.h>
+#include <corsika/framework/process/BaseProcess.hpp>
 
-namespace corsika::process {
+namespace corsika {
 
-  class NullModel : public corsika::process::BaseProcess<NullModel> {
+  /**
+   * Process that does nothing
+   */
+
+  class NullModel : public BaseProcess<NullModel> {
 
   public:
     NullModel() = default;
     ~NullModel() = default;
   };
 
-} // namespace corsika::process
+} // namespace corsika
diff --git a/corsika/framework/process/ProcessReturn.hpp b/corsika/framework/process/ProcessReturn.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..eef7058d09c171df53686fb4850a77d9e92c91c5
--- /dev/null
+++ b/corsika/framework/process/ProcessReturn.hpp
@@ -0,0 +1,62 @@
+/*
+ * (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
+
+/**
+ * \file ProcessReturn.hpp
+ **/
+
+namespace corsika {
+
+  /**
+     since in a process sequence many status updates can accumulate
+     for a single particle, this enum should define only bit-flags
+     that can be accumulated easily with "|="
+   */
+
+  enum class ProcessReturn : int {
+    Ok = (1 << 0),
+    ParticleAbsorbed = (1 << 2),
+    Interacted = (1 << 3),
+    Decayed = (1 << 4),
+  };
+
+  inline ProcessReturn operator|(ProcessReturn a, ProcessReturn b) {
+    return static_cast<ProcessReturn>(static_cast<int>(a) | static_cast<int>(b));
+  }
+
+  inline ProcessReturn& operator|=(ProcessReturn& a, const ProcessReturn b) {
+    return a = a | b;
+  }
+
+  inline ProcessReturn operator&(const ProcessReturn a, const ProcessReturn b) {
+    return static_cast<ProcessReturn>(static_cast<int>(a) & static_cast<int>(b));
+  }
+
+  inline bool operator==(const ProcessReturn a, const ProcessReturn b) {
+    return (static_cast<int>(a) & static_cast<int>(b)) != 0;
+  }
+
+  inline bool isOk(const ProcessReturn a) {
+    return static_cast<int>(a & ProcessReturn::Ok);
+  }
+
+  inline bool isAbsorbed(const ProcessReturn a) {
+    return static_cast<int>(a & ProcessReturn::ParticleAbsorbed);
+  }
+
+  inline bool isDecayed(const ProcessReturn a) {
+    return static_cast<int>(a & ProcessReturn::Decayed);
+  }
+
+  inline bool isInteracted(const ProcessReturn a) {
+    return static_cast<int>(a & ProcessReturn::Interacted);
+  }
+
+} // namespace corsika
diff --git a/corsika/framework/process/ProcessSequence.hpp b/corsika/framework/process/ProcessSequence.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..70337e1099d09cd4945d7f557f2a1661dedc0d0b
--- /dev/null
+++ b/corsika/framework/process/ProcessSequence.hpp
@@ -0,0 +1,239 @@
+/*
+ * (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
+
+/**
+ * \file ProcessSequence.hpp
+ */
+
+#include <corsika/framework/process/BaseProcess.hpp>
+#include <corsika/framework/process/ProcessTraits.hpp>
+#include <corsika/framework/process/BoundaryCrossingProcess.hpp>
+#include <corsika/framework/process/ContinuousProcess.hpp>
+#include <corsika/framework/process/DecayProcess.hpp>
+#include <corsika/framework/process/InteractionProcess.hpp>
+#include <corsika/framework/process/ProcessReturn.hpp>
+#include <corsika/framework/process/SecondariesProcess.hpp>
+#include <corsika/framework/process/StackProcess.hpp>
+#include <corsika/framework/process/NullModel.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+namespace corsika {
+
+  /**
+   *
+   *  Definition of a static process list/sequence
+   *
+   *  A compile time static list of processes. The compiler will
+   *  generate a new type based on template logic containing all the
+   *  elements provided by the user.
+   *
+   *  TProcess1 and TProcess2 must both be derived from BaseProcess,
+   *  and are both references if possible (lvalue), otherwise (rvalue)
+   *  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.
+   *
+   *  \todo There are several FIXME's in the ProcessSequence.inl due to
+   *  outstanding migration of SecondaryView::parent()
+   **/
+
+  template <typename TProcess1, typename TProcess2 = NullModel>
+  class ProcessSequence : public BaseProcess<ProcessSequence<TProcess1, TProcess2>> {
+
+    using process1_type = typename std::decay_t<TProcess1>;
+    using process2_type = typename std::decay_t<TProcess2>;
+
+    static bool constexpr t1ProcSeq = is_process_sequence_v<process1_type>;
+    static bool constexpr t2ProcSeq = is_process_sequence_v<process2_type>;
+
+    static bool constexpr t1SwitchProcSeq = is_switch_process_sequence_v<process1_type>;
+    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>,
+                  "can only use process derived from BaseProcess in "
+                  "ProcessSequence, for Process 1");
+    static_assert(std::is_base_of_v<BaseProcess<process2_type>, 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
+    ProcessSequence(ProcessSequence const&) = default;
+    ProcessSequence(ProcessSequence&&) = default;
+    ProcessSequence& operator=(ProcessSequence const&) = default;
+    ~ProcessSequence() = default;
+
+    /**
+     * Only valid user constructor will create fully initialized object
+     *
+     * ProcessSequence supports and encourages move semantics. You can
+     * use object, l-value references or r-value references to
+     * construct sequences.
+     *
+     * \param in_A process/list A
+     * \param in_A process/list B
+     **/
+    ProcessSequence(TProcess1 in_A, TProcess2 in_B)
+        : A_(in_A)
+        , B_(in_B) {}
+
+    template <typename TParticle>
+    ProcessReturn doBoundaryCrossing(TParticle& particle,
+                                     typename TParticle::node_type const& from,
+                                     typename TParticle::node_type const& to);
+
+    template <typename TParticle, typename TTrack>
+    inline ProcessReturn doContinuous(TParticle& particle, TTrack& vT);
+
+    template <typename TSecondaries>
+    inline void doSecondaries(TSecondaries& vS);
+
+    /**
+       The processes of type StackProcess do have an internal counter,
+       so they can be exectuted only each N steps. Often these are
+       "maintenacne processes" that do not need to run after each
+       single step of the simulations. In the CheckStep function it is
+       tested if either A_ or B_ are StackProcess and if they are due
+       for execution.
+     */
+    inline bool checkStep();
+
+    /**
+       Execute the StackProcess-es in the ProcessSequence
+     */
+    template <typename TStack>
+    inline void doStack(TStack& stack);
+
+    template <typename TParticle, typename TTrack>
+    inline LengthType getMaxStepLength(TParticle& particle, TTrack& vTrack);
+
+    template <typename TParticle>
+    inline GrammageType getInteractionLength(TParticle&& particle) {
+      return 1. / getInverseInteractionLength(particle);
+    }
+
+    template <typename TParticle>
+    inline InverseGrammageType getInverseInteractionLength(TParticle&& particle);
+
+    template <typename TSecondaryView>
+    inline 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) {
+      return 1. / getInverseLifetime(particle);
+    }
+
+    template <typename TParticle>
+    inline InverseTimeType getInverseLifetime(TParticle&& particle);
+
+    // select decay process
+    template <typename TSecondaryView>
+    inline ProcessReturn selectDecay(
+        TSecondaryView& view, [[maybe_unused]] InverseTimeType decay_inv_select,
+        [[maybe_unused]] InverseTimeType decay_inv_sum = InverseTimeType::zero());
+  };
+
+  /**
+   * Factory function to create ProcessSequence
+   *
+   * to construct ProcessSequences in a flexible and dynamic way the
+   * `sequence` factory functions are provided
+   *
+   * Any objects of type
+   *  - BaseProcess,
+   *  - ContinuousProcess, and
+   *  - Interaction/DecayProcess,
+   *  - StackProcess,
+   *  - SecondariesProcess
+   * can be assembled into a ProcessSequence, all
+   * combinatorics are allowed.
+
+   * The sequence function checks that all its arguments are all of
+   * types derived from BaseProcess. Also the ProcessSequence itself
+   * is derived from type BaseProcess
+   *
+   * \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>>,
+      ProcessSequence<TProcess1, decltype(make_sequence(std::declval<TProcesses>()...))>>
+  make_sequence(TProcess1&& vA, TProcesses&&... vBs) {
+    return ProcessSequence<TProcess1,
+                           decltype(make_sequence(std::declval<TProcesses>()...))>(
+        vA, make_sequence(std::forward<TProcesses>(vBs)...));
+  }
+
+  /**
+   * Factory function to create ProcessSequence
+   *
+   * specialization for two input objects (no paramter pack in vB).
+   *
+   * \param vA needs to derive from BaseProcess or ProcessSequence
+   * \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>>
+  make_sequence(TProcess1&& vA, TProcess2&& vB) {
+    return ProcessSequence<TProcess1, TProcess2>(vA, vB);
+  }
+
+  /**
+   * Factory function to create ProcessSequence from a single BaseProcess
+   *
+   * also allow a single Process in ProcessSequence, accompany by
+   * `NullModel`
+   *
+   * \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>>
+  make_sequence(TProcess&& vA) {
+    return ProcessSequence<TProcess, NullModel>(vA, NullModel());
+  }
+
+  /**
+   * traits marker to identify objectas ProcessSequence
+   **/
+  template <typename TProcess1, typename TProcess2>
+  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_sequence() {}
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/process/ProcessSequence.inl>
diff --git a/Framework/ProcessSequence/ProcessTraits.h b/corsika/framework/process/ProcessTraits.hpp
similarity index 91%
rename from Framework/ProcessSequence/ProcessTraits.h
rename to corsika/framework/process/ProcessTraits.hpp
index e0cb00ebfde8827bbb098dc34d13df1c94a72eb2..7b303d2d3ce3a9ce1363d2433e08832e80032f46 100644
--- a/Framework/ProcessSequence/ProcessTraits.h
+++ b/corsika/framework/process/ProcessTraits.hpp
@@ -8,7 +8,13 @@
 
 #pragma once
 
-namespace corsika::process {
+/**
+ * \file ProcessTraits.hpp
+ */
+
+#include <type_traits>
+
+namespace corsika {
 
   /**
    *  A traits marker to track which BaseProcess is also a ProcessSequence
@@ -38,4 +44,4 @@ namespace corsika::process {
   template <typename TClass>
   bool constexpr contains_stack_process_v = contains_stack_process<TClass>::value;
 
-} // namespace corsika::process
+} // namespace corsika
diff --git a/Framework/ProcessSequence/SecondariesProcess.h b/corsika/framework/process/SecondariesProcess.hpp
similarity index 65%
rename from Framework/ProcessSequence/SecondariesProcess.h
rename to corsika/framework/process/SecondariesProcess.hpp
index a9bc3832a2e8a82949a6d03c3b9c1c5d44a49828..0d6db4783e48e6f6485f42a8e7b62e0fb285f8cf 100644
--- a/Framework/ProcessSequence/SecondariesProcess.h
+++ b/corsika/framework/process/SecondariesProcess.hpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
@@ -8,13 +8,13 @@
 
 #pragma once
 
-#include <corsika/process/BaseProcess.h>
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/framework/process/BaseProcess.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
 
-namespace corsika::process {
+namespace corsika {
 
   /**
-     \class SecondariesProcess
+     Process that modifies a list of secondaries of other processes
 
      The structural base type of a process object in a
      ProcessSequence. Both, the ProcessSequence and all its elements
@@ -28,7 +28,7 @@ namespace corsika::process {
     /// here starts the interface-definition part
     // -> enforce TDerived to implement DoSecondaries...
     template <typename TSecondaries>
-    inline void DoSecondaries(TSecondaries&);
+    inline void doSecondaries(TSecondaries&);
   };
 
-} // namespace corsika::process
+} // namespace corsika
diff --git a/Framework/ProcessSequence/StackProcess.h b/corsika/framework/process/StackProcess.hpp
similarity index 65%
rename from Framework/ProcessSequence/StackProcess.h
rename to corsika/framework/process/StackProcess.hpp
index ce5c0998df246ed5ceb20bed618b40814a79077f..77ca2a479cbc3404d372db701526a91895e6f2b6 100644
--- a/Framework/ProcessSequence/StackProcess.h
+++ b/corsika/framework/process/StackProcess.hpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
@@ -8,13 +8,13 @@
 
 #pragma once
 
-#include <corsika/process/BaseProcess.h>
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/framework/process/BaseProcess.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
 
-namespace corsika::process {
+namespace corsika {
 
   /**
-     \class StackProcess
+     Process to act on the entire particle stack
 
      The structural base type of a process object in a
      ProcessSequence. Both, the ProcessSequence and all its elements
@@ -29,15 +29,15 @@ namespace corsika::process {
   public:
     StackProcess() = delete;
     StackProcess(const unsigned int nStep)
-        : fNStep(nStep) {}
+        : nStep_(nStep) {}
 
     /// here starts the interface-definition part
     // -> enforce TDerived to implement DoStack...
     template <typename TStack>
-    inline void DoStack(TStack&);
+    inline void doStack(TStack&);
 
-    int GetStep() const { return fIStep; }
-    bool CheckStep() { return !((++fIStep) % fNStep); }
+    int getStep() const { return iStep_; }
+    bool checkStep() { return !((++iStep_) % nStep_); }
 
   private:
     /**
@@ -46,9 +46,9 @@ namespace corsika::process {
        "fIStep modulo fNStep"
        @{
      */
-    unsigned int fNStep = 0;
-    unsigned long int fIStep = 0;
+    unsigned int nStep_ = 0;
+    unsigned long int iStep_ = 0;
     //! @}
   };
 
-} // namespace corsika::process
+} // namespace corsika
diff --git a/corsika/framework/process/SwitchProcessSequence.hpp b/corsika/framework/process/SwitchProcessSequence.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..dab419ae5a5c66fda13eee9c0247c697e0cdcfd1
--- /dev/null
+++ b/corsika/framework/process/SwitchProcessSequence.hpp
@@ -0,0 +1,193 @@
+/*
+ * (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
+
+/**
+ * \file SwitchProcessSequence.hpp
+ **/
+
+#include <corsika/framework/process/BaseProcess.hpp>
+#include <corsika/framework/process/ProcessTraits.hpp>
+#include <corsika/framework/process/BoundaryCrossingProcess.hpp>
+#include <corsika/framework/process/ContinuousProcess.hpp>
+#include <corsika/framework/process/DecayProcess.hpp>
+#include <corsika/framework/process/InteractionProcess.hpp>
+#include <corsika/framework/process/ProcessReturn.hpp>
+#include <corsika/framework/process/SecondariesProcess.hpp>
+#include <corsika/framework/process/StackProcess.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <cmath>
+#include <limits>
+#include <type_traits>
+
+namespace corsika {
+
+  /**
+   * enum for the process switch selection: identify if First or
+   * Second process branch should be used.
+   **/
+  enum class SwitchResult { First, Second };
+
+  /**
+     Class to switch between two process branches
+
+     A compile-time static list of processes that uses an internal
+     TSelect class to switch between different versions of processes
+     (or process sequence).
+
+     TProcess1 and TProcess2 must be derived from BaseProcess and are
+     both references if possible (lvalue), otherwise (rvalue) they are
+     just classes. This allows us to handle both, rvalue as well as
+     lvalue Processes in the SwitchProcessSequence.
+
+     TSelect has to implement a `operator()(const Particle&)` and has to
+     return either SwitchResult::First or SwitchResult::Second. Note:
+     TSelect may absolutely also use random numbers to sample between
+     its results. This can be used to achieve arbitrarily smooth
+     transition or mixtures of processes.
+
+     Warning: do not put StackProcess into a SwitchProcessSequence
+     since this makes no sense. The StackProcess acts on an entire
+     particle stack and not on indiviidual particles.
+
+     See also class \sa ProcessSequence
+  **/
+
+  template <typename TProcess1, typename TProcess2, typename TSelect>
+  class SwitchProcessSequence
+      : public BaseProcess<SwitchProcessSequence<TProcess1, TProcess2, TSelect>> {
+
+    using process1_type = typename std::decay_t<TProcess1>;
+    using process2_type = typename std::decay_t<TProcess2>;
+
+    static bool constexpr t1ProcSeq = is_process_sequence_v<process1_type>;
+    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>,
+                  "can only use process derived from BaseProcess in "
+                  "SwitchProcessSequence, for Process 1");
+    static_assert(std::is_base_of_v<BaseProcess<process2_type>, process2_type>,
+                  "can only use process derived from BaseProcess in "
+                  "SwitchProcessSequence, for Process 2");
+
+    // make sure none of TProcess1/2 is a StackProcess
+    static_assert(!std::is_base_of_v<StackProcess<process1_type>, process1_type>,
+                  "cannot use StackProcess in SwitchProcessSequence, for Process 1");
+    static_assert(!std::is_base_of_v<StackProcess<process2_type>, process2_type>,
+                  "cannot use StackProcess in SwitchProcessSequence, for Process 2");
+
+    // if TProcess1/2 are already ProcessSequences, make sure they do not contain
+    // any StackProcess
+    static_assert(!contains_stack_process_v<process1_type>,
+                  "cannot use StackProcess in SwitchProcessSequence, remove from "
+                  "ProcessSequence 1");
+    static_assert(!contains_stack_process_v<process2_type>,
+                  "cannot use StackProcess in SwitchProcessSequence, remove from "
+                  "ProcessSequence 2");
+
+  public:
+    // resource management
+    SwitchProcessSequence() = delete; // only initialized objects
+    SwitchProcessSequence(SwitchProcessSequence const&) = default;
+    SwitchProcessSequence(SwitchProcessSequence&&) = default;
+    SwitchProcessSequence& operator=(SwitchProcessSequence const&) = default;
+    ~SwitchProcessSequence() = default;
+
+    /**
+     * Only valid user constructor will create fully initialized object
+     *
+     * SwitchProcessSequence supports and encourages move semantics. You can
+     * use object, l-value references or r-value references to
+     * construct sequences.
+     *
+     * \param in_A process branch A
+     * \param in_A process branch B
+     * \param sel functor to swtich between branch A and B
+     **/
+    SwitchProcessSequence(TProcess1 in_A, TProcess2 in_B, TSelect sel)
+        : select_(sel)
+        , A_(in_A)
+        , B_(in_B) {}
+
+    template <typename TParticle, typename TVTNType>
+    ProcessReturn doBoundaryCrossing(TParticle& particle, TVTNType const& from,
+                                     TVTNType const& to);
+
+    template <typename TParticle, typename TTrack>
+    inline ProcessReturn doContinuous(TParticle& particle, TTrack& vT);
+
+    template <typename TSecondaries>
+    inline void doSecondaries(TSecondaries& vS);
+
+    template <typename TParticle, typename TTrack>
+    inline LengthType getMaxStepLength(TParticle& particle, TTrack& vTrack);
+
+    template <typename TParticle>
+    inline GrammageType getInteractionLength(TParticle&& particle) {
+      return 1. / getInverseInteractionLength(particle);
+    }
+
+    template <typename TParticle>
+    inline InverseGrammageType getInverseInteractionLength(TParticle&& particle);
+
+    template <typename TSecondaryView>
+    inline 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) {
+      return 1. / getInverseLifetime(particle);
+    }
+
+    template <typename TParticle>
+    inline InverseTimeType getInverseLifetime(TParticle&& particle);
+
+    // select decay process
+    template <typename TSecondaryView>
+    inline ProcessReturn selectDecay(
+        TSecondaryView& view, [[maybe_unused]] InverseTimeType decay_inv_select,
+        [[maybe_unused]] InverseTimeType decay_inv_sum = InverseTimeType::zero());
+
+  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
+  };
+
+  /**
+   *
+   * the functin `make_select(proc1,proc1,selector)` assembles many
+   * BaseProcesses, and ProcessSequences into a SwitchProcessSequence,
+   * all combinatorics must be allowed, this is why we define a macro
+   * to define all combinations here:
+   *
+   *
+   * Both, Processes1 and Processes2, must derive from BaseProcesses
+   **/
+
+  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>>
+  make_select(TProcess1&& vA, TProcess2&& vB, TSelect selector) {
+    return SwitchProcessSequence<TProcess1, TProcess2, TSelect>(vA, vB, selector);
+  }
+
+} // namespace corsika
+
+#include <corsika/detail/framework/process/SwitchProcessSequence.inl>
diff --git a/corsika/framework/random/ExponentialDistribution.hpp b/corsika/framework/random/ExponentialDistribution.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7bd855632d3c6a4de0ceb4a10109c98f02472a9f
--- /dev/null
+++ b/corsika/framework/random/ExponentialDistribution.hpp
@@ -0,0 +1,79 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <random>
+
+namespace corsika {
+
+  template <typename Quantity>
+  class ExponentialDistribution {
+
+    typedef typename Quantity::value_type real_type;
+    typedef std::exponential_distribution<real_type> distribution_type;
+
+  public:
+    typedef Quantity value_type;
+
+    ExponentialDistribution() = delete;
+
+    ExponentialDistribution(value_type const& beta)
+        : beta_(beta) {}
+
+    ExponentialDistribution(ExponentialDistribution<value_type> const& other)
+        : beta_(other.getBeta()) {}
+
+    ExponentialDistribution<value_type>& operator=(
+        ExponentialDistribution<value_type> const& other) {
+      if (this == &other) return *this;
+      beta_ = other.getBeta();
+      return *this;
+    }
+
+    /**
+     * @fn value_type getBeta()const
+     * @brief Get parameter of exponential distribution \f[ \beta e^{-X}\f]
+     * @pre
+     * @post
+     * @return  value_type
+     */
+    value_type getBeta() const { return beta_; }
+
+    /**
+     * @fn void setBeta(value_type)
+     * @brief Set parameter of exponential distribution \f[ \beta e^{-X}\f]
+     *
+     * @pre
+     * @post
+     * @param vBeta
+     */
+    void setBeta(value_type const& beta) { beta_ = beta; }
+
+    /**
+     * @fn value_type operator ()(Generator&)
+     * @brief Generate a random number distributed like \f[ \beta e^{-X}\f]
+     *
+     * @pre
+     * @post
+     * @tparam Generator
+     * @param g
+     * @return
+     */
+    template <class Generator>
+    value_type operator()(Generator& g) {
+      return beta_ * dist_(g);
+    }
+
+  private:
+    distribution_type dist_{1.};
+    value_type beta_;
+  };
+
+} // namespace corsika
diff --git a/corsika/framework/random/RNGManager.hpp b/corsika/framework/random/RNGManager.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2dd6160e993d5a39b2aeb35f62be874047372cf3
--- /dev/null
+++ b/corsika/framework/random/RNGManager.hpp
@@ -0,0 +1,129 @@
+/*
+ * (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 <map>
+#include <cstdint>
+#include <random>
+#include <string>
+
+#include <corsika/framework/utility/Singleton.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+/*!
+ * With this class modules can register streams of random numbers.
+ */
+
+namespace corsika {
+
+  class RNGManager : public corsika::Singleton<RNGManager> {
+
+    friend class corsika::Singleton<RNGManager>;
+
+  public:
+    typedef std::mt19937_64 prng_type;
+    typedef std::uint64_t seed_type;
+    typedef std::string string_type;
+    typedef std::map<std::string, prng_type> streams_type;
+    typedef std::map<std::string, std::seed_seq> seeds_type;
+
+    RNGManager(RNGManager const&) = delete; // since it is a singleton
+
+    RNGManager& operator=(RNGManager const&) = delete;
+
+    /*!
+     * This function is to be called by a module requiring a random-number
+     * stream during its initialization.
+     *
+     * \throws sth. when stream \a pModuleName is already registered
+     */
+    inline 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);
+
+    /*!
+     * Check whether a stream has been registered.
+     */
+    inline 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;
+
+    /**
+     * Set explicit seeds for all currently registered streams. The actual seed values
+     * are incremented from \a vSeed.
+     */
+    inline void seedAll(seed_type seed);
+
+    /**
+     * Set seeds for all currently registered streams.
+     */
+    inline void seedAll(
+        void); //!< seed all currently registered streams with "real" randomness
+
+    /**
+     * @fn const streams_type getRngs&()const
+     * @brief Constant access to the streams.
+     *
+     * @pre
+     * @post
+     * @return RNGManager::streams_type
+     */
+    inline const streams_type& getRngs() const { return rngs_; }
+
+    /**
+     * @fn const seeds_type getSeeds&()const
+     * @brief Constant access to the seeds.
+     *
+     * @pre
+     * @post
+     * @return RNGManager::seeds_type
+     */
+    inline const seeds_type& getSeeds() const { return seeds_; }
+
+    /**
+     * @fn streams_type Rngs()
+     * @brief Non-constant access to the streams.
+     *
+     * @pre
+     * @post
+     * @return RNGManager::streams_type&
+     */
+    inline streams_type Rngs() { return rngs_; }
+
+    /**
+     * @fn seeds_type Seeds&()
+     * @brief Non-constant access to seeds.
+     *
+     * @pre
+     * @post
+     * @return RNGManager::seeds_type&
+     */
+    inline seeds_type& Seeds() { return seeds_; }
+
+  protected:
+    RNGManager() = default;
+
+  private:
+    streams_type rngs_;
+    seeds_type seeds_;
+  };
+
+  typedef typename RNGManager::prng_type default_prng_type;
+
+} // namespace corsika
+
+#include <corsika/detail/framework/random/RNGManager.inl>
diff --git a/corsika/framework/random/UniformRealDistribution.hpp b/corsika/framework/random/UniformRealDistribution.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..781a23717a6fef420ee8ed3a8e8d658fa4c5e768
--- /dev/null
+++ b/corsika/framework/random/UniformRealDistribution.hpp
@@ -0,0 +1,109 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <random>
+
+namespace corsika {
+
+  template <typename Quantity>
+  class UniformRealDistribution {
+
+    typedef typename Quantity::value_type real_type;
+    typedef std::uniform_real_distribution<real_type> distribution_type;
+
+  public:
+    typedef Quantity value_type;
+
+    UniformRealDistribution() = delete;
+
+    UniformRealDistribution(Quantity const& b)
+        : min_{value_type(phys::units::detail::magnitude_tag, 0)}
+        , max_(b) {}
+
+    UniformRealDistribution(value_type const& pmin, value_type const& pmax)
+        : min_(pmin)
+        , max_(pmax) {}
+
+    UniformRealDistribution(UniformRealDistribution<value_type> const& other)
+        : min_(other.getMin())
+        , max_(other.getMax()) {}
+
+    inline UniformRealDistribution<value_type>& operator=(
+        UniformRealDistribution<value_type> const& other) {
+      if (this == &other) return *this;
+      min_ = other.getMin();
+      max_ = other.getMax();
+      return *this;
+    }
+
+    /**
+     * @fn quantity_type getMax()const
+     * @brief Get the upper limit.
+     *
+     * @pre
+     * @post
+     * @return quantity_type
+     */
+    inline value_type getMax() const { return max_; }
+
+    /**
+     * @fn void setMax(quantity_type)
+     * @brief Set the upper limit.
+     *
+     * @pre
+     * @post
+     * @param vMax
+     */
+    inline void setMax(value_type const& pmax) { max_ = pmax; }
+
+    /**
+     * @fn quantity_type getMin()const
+     * @brief Get the lower limit.
+     *
+     * @pre
+     * @post
+     * @return
+     */
+    inline value_type getMin() const { return min_; }
+
+    /**
+     * @fn void setMin(quantity_type)
+     * @brief  Set the lower limit.
+     *
+     * @pre
+     * @post
+     * @param vMin
+     */
+    inline void setMin(value_type const& pmin) { min_ = pmin; }
+
+    /**
+     * @fn quantity_type operator ()(Generator&)
+     * @brief Generate a random numberin the range [min, max]
+     *
+     * @pre
+     * @post
+     * @tparam Generator
+     * @param g
+     * @return quantity_type
+     */
+    template <class Generator>
+    inline value_type operator()(Generator& g) {
+      return min_ + dist_(g) * (max_ - min_);
+    }
+
+  private:
+    distribution_type dist_{real_type(0.), real_type(1.)};
+
+    value_type min_;
+    value_type max_;
+  };
+
+} // namespace corsika
diff --git a/corsika/framework/stack/CombinedStack.hpp b/corsika/framework/stack/CombinedStack.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d452d8f8b469f7a79317036331d3c345f4d2b15e
--- /dev/null
+++ b/corsika/framework/stack/CombinedStack.hpp
@@ -0,0 +1,137 @@
+/*
+ * (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 <corsika/framework/core/Logging.hpp>
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+namespace corsika {
+
+  /**
+   * CombinedParticleInterface can be used to combine the data of several StackData
+   * objects.
+   *
+   * You may combine two StackData object, see class CombinedStackImpl
+   * below, into one Stack, using a combined StackIterator (aka
+   * CombinedParticleInterface) interface class.
+   *
+   * This allows to add specific information to a given Stack, could
+   * be special information on a subset of entries
+   * (e.g. NuclearStackExtension) or also (multi) thinning weights for
+   * all particles.
+   *
+   * Many Stacks can be combined into more complex object.
+   *
+   * The two sub-stacks must both provide their independent
+   * ParticleInterface classes.
+   *
+   */
+  template <template <typename> typename TParticleInterfaceA,
+            template <typename> class TParticleInterfaceB, typename TStackIterator>
+  struct CombinedParticleInterface
+      : public TParticleInterfaceB<TParticleInterfaceA<TStackIterator>> {
+
+    typedef CombinedParticleInterface<TParticleInterfaceA, TParticleInterfaceB,
+                                      TStackIterator>
+        pi_c_type;
+    typedef TParticleInterfaceA<TStackIterator> pi_a_type;
+    typedef TParticleInterfaceB<TParticleInterfaceA<TStackIterator>> pi_b_type;
+
+  protected:
+    using pi_b_type::getIndex;     // choose B, A would also work
+    using pi_b_type::getStackData; // choose B, A would also work
+
+  public:
+    /**
+     * @name wrapper for user functions
+     * @{
+     *
+     * In this set of functions we call the user-provide
+     * TParticleInterface setParticleData(...) methods, either with
+     * parent particle reference, or w/o.
+     *
+     * There is one implicit assumption here: if only one data tuple
+     * is provided for setParticleData, the data is passed on to
+     * TParticleInterfaceA and the TParticleInterfaceB is
+     * default-initialized. There are many occasions where this is the
+     * desired behaviour, e.g. for thinning etc.
+     *
+     */
+
+    template <typename... TArgs1>
+    inline 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);
+
+    template <typename... TArgs1>
+    inline 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);
+    ///@}
+
+    inline std::string asString() const;
+
+  protected:
+  };
+
+  /**
+   * @class CombinedStackImpl
+   *
+   * Memory implementation of a combined data stack.
+   *
+   * The two stack data user objects Stack1Impl and Stack2Impl are
+   * merged into one consistent Stack container object providing
+   * access to the combined number of data entries.
+   */
+  template <typename Stack1Impl, typename Stack2Impl>
+  struct CombinedStackImpl : public Stack1Impl, public Stack2Impl {
+
+  public:
+    inline void clear();
+
+    inline unsigned int getSize() const { return Stack1Impl::getSize(); }
+    inline 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);
+
+    /**
+     *   Function to copy particle at location i2 in stack to i1
+     */
+    inline void swap(const unsigned int i1, const unsigned int i2);
+
+    inline void incrementSize();
+
+    inline void decrementSize();
+
+  }; // end class CombinedStackImpl
+
+  /**
+   * Helper template alias `CombinedStack` to construct new combined
+   * stack from two stack data objects and a particle readout interface.
+   *
+   * Note that the Stack2Impl provides only /additional/ data to
+   * Stack1Impl. This is important (see above) since tuple data for
+   * initialization are forwarded to Stack1Impl (first).
+   */
+
+  template <typename Stack1Impl, typename Stack2Impl, template <typename> typename _PI>
+  using CombinedStack = Stack<CombinedStackImpl<Stack1Impl, Stack2Impl>, _PI>;
+
+} // namespace corsika
+
+#include <corsika/detail/framework/stack/CombinedStack.inl>
diff --git a/Framework/StackInterface/ParticleBase.h b/corsika/framework/stack/ParticleBase.hpp
similarity index 64%
rename from Framework/StackInterface/ParticleBase.h
rename to corsika/framework/stack/ParticleBase.hpp
index 1bf7d0ad8834eddd51971feffcd4db0fd23fabb6..c7b086c5cdfe3eb612049e73da194b0e1b4431bc 100644
--- a/Framework/StackInterface/ParticleBase.h
+++ b/corsika/framework/stack/ParticleBase.hpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
@@ -8,11 +8,11 @@
 
 #pragma once
 
-namespace corsika::stack {
+#include <cstdlib> // for size_t
 
-  /**
-   @class ParticleBase
+namespace corsika {
 
+  /**
    The base class to define the readout of particle properties from a
    particle stack. Every stack must implement this readout via the
    ParticleBase class.
@@ -42,29 +42,31 @@ namespace corsika::stack {
   class ParticleBase {
 
   public:
-    using StackIteratorType = StackIterator;
+    typedef StackIterator stack_iterator_type;
+
     ParticleBase() = default;
 
-  private:
     // those copy constructors and assigments should never be implemented
-    ParticleBase(ParticleBase&) = delete;
-    ParticleBase operator=(ParticleBase&) = delete;
+
     ParticleBase(ParticleBase&&) = delete;
+    ParticleBase(ParticleBase const&) = delete;
+
     ParticleBase operator=(ParticleBase&&) = delete;
-    ParticleBase(const ParticleBase&) = delete;
-    ParticleBase operator=(const ParticleBase&) = delete;
+    ParticleBase operator=(ParticleBase const&) = delete;
 
-  public:
     /**
      * Delete this particle on the stack. The corresponding iterator
      * will be invalidated by this operation
      */
-    void Delete() { GetIterator().GetStack().Delete(GetIterator()); }
+    inline void erase() { this->getIterator().getStack().erase(this->getIterator()); }
 
     /**
      * Method to retrieve the status of the Particle. Is it already deleted? Or not.
      */
-    bool isDeleted() const { return GetIterator().GetStack().isDeleted(GetIterator()); }
+
+    inline bool isErased() const {
+      return this->getIterator().getStack().isErased(this->getIterator());
+    }
 
     /**
      * Add a secondary particle based on *this on the stack @param
@@ -72,8 +74,9 @@ namespace corsika::stack {
      * function description in the user defined ParticleInterface::AddSecondary(...)
      */
     template <typename... TArgs>
-    StackIterator AddSecondary(const TArgs... args) {
-      return GetStack().AddSecondary(GetIterator(), args...);
+    inline stack_iterator_type addSecondary(const TArgs... args) {
+
+      return this->getStack().addSecondary(this->getIterator(), args...);
     }
 
     // protected: // todo should [MAY]be proteced, but don't now how to 'friend Stack'
@@ -81,28 +84,37 @@ namespace corsika::stack {
     /**
      * return the corresponding StackIterator for this particle
      */
-    StackIterator& GetIterator() { return static_cast<StackIterator&>(*this); }
-    const StackIterator& GetIterator() const {
-      return static_cast<const StackIterator&>(*this);
+    inline stack_iterator_type& getIterator() {
+      return static_cast<stack_iterator_type&>(*this);
+    }
+
+    inline const stack_iterator_type& getIterator() const {
+      return static_cast<const stack_iterator_type&>(*this);
     }
 
   protected:
     /**
         @name Access to underlying stack fData, these are service
-        function for user classes. User code can only rely on GetIndex
-        and GetStackData to retrieve data
+        function for user classes. User code can only rely on getIndex
+        and getStackData to retrieve data
         @{
     */
-    auto& GetStackData() { return GetIterator().GetStackData(); }
-    const auto& GetStackData() const { return GetIterator().GetStackData(); }
-    auto& GetStack() { return GetIterator().GetStack(); }
-    const auto& GetStack() const { return GetIterator().GetStack(); }
+    inline auto& getStackData() { return this->getIterator().getStackData(); }
+
+    inline const auto& getStackData() const { return this->getIterator().getStackData(); }
+
+    inline auto& getStack() { return this->getIterator().getStack(); }
+
+    inline const auto& getStack() const { return this->getIterator().getStack(); }
 
     /**
      * return the index number of the underlying iterator object
      */
-    unsigned int GetIndex() const { return GetIterator().GetIndexFromIterator(); }
+
+    inline std::size_t getIndex() const {
+      return this->getIterator().getIndexFromIterator();
+    }
     ///@}
   };
 
-} // namespace corsika::stack
+} // namespace corsika
diff --git a/corsika/framework/stack/SecondaryView.hpp b/corsika/framework/stack/SecondaryView.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e18531b3154e250ab44c33014a92355e0b9a877a
--- /dev/null
+++ b/corsika/framework/stack/SecondaryView.hpp
@@ -0,0 +1,432 @@
+/*
+ * (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 <corsika/framework/stack/Stack.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <stdexcept>
+#include <vector>
+
+namespace corsika {
+
+  // forward-decl:
+  template <class T1, template <class> class T2>
+  class DefaultSecondaryProducer;
+
+  /**
+   * @class SecondaryView
+   *
+   * SecondaryView can only be constructed by giving a valid
+   * Projectile particle, following calls to addSecondary will
+   * populate the original Stack, but will be directly accessible via
+   * the SecondaryView, e.g.
+
+     This allows to write code like
+     \verbatim
+     auto projectileInput = mainStack.getNextParticle();
+     const unsigned int nMain = mainStack.getSize();
+     SecondaryView<StackData, ParticleInterface> mainStackView(projectileInput);
+     mainStackView.addSecondary(...data...);
+     mainStackView.addSecondary(...data...);
+     mainStackView.addSecondary(...data...);
+     mainStackView.addSecondary(...data...);
+     assert(mainStackView.getSize() == 4);
+     assert(mainStack.getSize() = nMain+4);
+     \endverbatim
+
+     All operations possible on a Stack object are also possible on a
+     SecondaryView object. This means you can add, delete, copy, swap,
+     iterate, etc.
+
+     *Further information about implementation (for developers):* All
+     data is stored in the original stack privided at construction
+     time. The secondary particle (view) indices are stored in an
+     extra std::vector of SecondaryView class 'indices_' referring to
+     the original stack slot indices. The index of the primary
+     projectle particle is also explicitly stored in
+     'projectile_index_'. StackIterator indices
+     'i = StackIterator::getIndex()' are referring to those numbers,
+     where 'i==0' refers to the 'projectile_index_', and
+     'StackIterator::getIndex()>0' to 'indices_[i-1]', see function
+     getIndexFromIterator.
+   */
+
+  template <typename TStackDataType, template <typename> typename TParticleInterface,
+            template <typename T1, template <class> class T2> class MSecondaryProducer =
+                DefaultSecondaryProducer>
+  class SecondaryView : public Stack<TStackDataType&, TParticleInterface>,
+                        public MSecondaryProducer<TStackDataType, TParticleInterface> {
+
+    // using ViewType = SecondaryView<TStackDataType, TParticleInterface,
+    // MSecondaryProducer>;
+    typedef SecondaryView<TStackDataType, TParticleInterface, MSecondaryProducer>
+        view_type;
+    /**
+     * Helper type for inside this class
+     */
+    // using InnerStackTypeRef = Stack<TStackDataType&, TParticleInterface>;
+    typedef Stack<TStackDataType&, TParticleInterface> inner_stack_reference_type;
+
+    /**
+     * @name We need this "value" types with non-reference TStackData for
+     * the constructor of the SecondaryView class
+     * @{
+     */
+    // using InnerStackTypeValue = Stack<TStackDataType, TParticleInterface>;
+    typedef Stack<TStackDataType, TParticleInterface> inner_stack_value_type;
+
+  public:
+    typedef StackIteratorInterface<typename std::remove_reference<TStackDataType>::type,
+                                   TParticleInterface, inner_stack_value_type>
+        stack_value_iterator;
+
+    //    using ConstStackIteratorValue =
+    //        ConstStackIteratorInterface<typename
+    //        std::remove_reference<TStackDataType>::type,
+    //                                    TParticleInterface, inner_stack_value_type>;
+
+    typedef ConstStackIteratorInterface<
+        typename std::remove_reference<TStackDataType>::type, TParticleInterface,
+        inner_stack_value_type>
+        const_stack_value_iterator;
+    /// @}
+
+    //    using StackIterator =
+    //        StackIteratorInterface<typename std::remove_reference<TStackDataType>::type,
+    //                               TParticleInterface, view_type>;
+
+    typedef StackIteratorInterface<typename std::remove_reference<TStackDataType>::type,
+                                   TParticleInterface, view_type>
+        stack_view_iterator;
+
+    typedef ConstStackIteratorInterface<
+        typename std::remove_reference<TStackDataType>::type, TParticleInterface,
+        view_type>
+        const_stack_view_iterator;
+
+    /**
+     * this is the full type of the declared TParticleInterface:
+     */
+    using ParticleType = stack_view_iterator;
+    using ParticleInterfaceType = typename stack_view_iterator::particle_interface_type;
+
+    /**
+     * This is not accessible, since we don't want to allow creating a
+     * new stack.
+     */
+    template <typename... TArgs>
+    SecondaryView(TArgs... args) = delete;
+    SecondaryView() = delete;
+
+    /**
+        SecondaryView can only be constructed passing it a valid
+        stack_view_iterator to another Stack object (here: lvalue)
+      **/
+    SecondaryView(stack_value_iterator& particle)
+        : Stack<TStackDataType&, TParticleInterface>(particle.getStackData())
+        , MSecondaryProducer<TStackDataType, TParticleInterface>{particle}
+        , inner_stack_(particle.getStack())
+        , projectile_index_(particle.getIndex()) {
+      CORSIKA_LOG_TRACE("SecondaryView::SecondaryView(particle&)");
+    }
+    /**
+       SecondaryView can only be constructed passing it a valid
+       stack_view_iterator to another Stack object (here: rvalue)
+     **/
+    SecondaryView(stack_value_iterator&& particle)
+        : Stack<TStackDataType&, TParticleInterface>(particle.getStackData())
+        , MSecondaryProducer<TStackDataType, TParticleInterface>{particle}
+        , inner_stack_(particle.getStack())
+        , projectile_index_(particle.getIndex()) {
+      CORSIKA_LOG_TRACE("SecondaryView::SecondaryView(particle&&)");
+    }
+    /**
+     * Also allow to create a new View from a Projectile (stack_view_iterator on View)
+     *
+     * Note, the view generated this way will be equivalent to the orignal view in
+     * terms of reference to the underlying data stack. It is not a "view to a view".
+     */
+    SecondaryView(view_type& view, stack_view_iterator& projectile)
+        : Stack<TStackDataType&, TParticleInterface>{view.getStackData()}
+        , MSecondaryProducer<TStackDataType, TParticleInterface>{stack_value_iterator{
+              view.inner_stack_, view.getIndexFromIterator(projectile.getIndex())}}
+        , inner_stack_{view.inner_stack_}
+        , projectile_index_{view.getIndexFromIterator(projectile.getIndex())} {
+      CORSIKA_LOG_TRACE("SecondaryView::SecondaryView(view, projectile)");
+    }
+
+    /**
+     * This returns the projectile/parent in the original Stack, where this
+     * SecondaryView is derived from. This projectile should not be
+     * used to modify the Stack!
+     */
+    inline stack_value_iterator parent()
+        const { // todo: check if this can't be Conststack_value_iterator
+      return stack_value_iterator(inner_stack_, projectile_index_);
+    }
+
+    /**
+     * This returns the projectile/parent in the original Stack, where this
+     * SecondaryView is derived from. This projectile should not be
+     * used to modify the Stack!
+     */
+    inline stack_value_iterator asNewParent() const {
+      return stack_value_iterator(inner_stack_, projectile_index_);
+    }
+
+    /**
+     * This return a projectile of this SecondaryView, which can be
+     * used to modify the SecondaryView
+     */
+    inline stack_view_iterator getProjectile() {
+      // NOTE: 0 is special marker here for PROJECTILE, see getIndexFromIterator
+      return 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);
+
+    /**
+     * overwrite Stack::getSize to return actual number of secondaries
+     */
+    inline unsigned int getSize() const { return indices_.size(); }
+
+    inline unsigned int getEntries() const {
+      return getSize() - inner_stack_reference_type::getErased();
+    }
+
+    inline bool isEmpty() const { return getEntries() == 0; }
+
+    /**
+     * @name These are functions required by std containers and std loops
+     * The Stack-versions must be overwritten, since here we need the correct
+     * SecondaryView::getSize
+     * @{
+     */
+    // NOTE: the "+1" is since "0" is special marker here for PROJECTILE, see
+    // getIndexFromIterator
+    inline stack_view_iterator begin();
+
+    inline stack_view_iterator end() { return stack_view_iterator(*this, getSize() + 1); }
+
+    inline stack_view_iterator last();
+
+    inline const_stack_view_iterator begin() const;
+
+    inline const_stack_view_iterator end() const {
+      return const_stack_view_iterator(*this, getSize() + 1);
+    }
+
+    inline const_stack_view_iterator last() const;
+
+    inline const_stack_view_iterator cbegin() const;
+
+    inline const_stack_view_iterator cend() const {
+      return const_stack_view_iterator(*this, getSize());
+    }
+
+    inline const_stack_view_iterator clast() const;
+
+    inline stack_view_iterator at(unsigned int i) {
+      return stack_view_iterator(*this, i);
+    }
+
+    inline 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}; }
+
+    inline const_stack_view_iterator cfirst() const {
+      return const_stack_view_iterator{*this, 0};
+    }
+    /// @}
+
+    inline void swap(stack_view_iterator a, stack_view_iterator b);
+
+    inline void copy(stack_view_iterator a, stack_view_iterator b);
+
+    inline void copy(const_stack_view_iterator a, stack_view_iterator b);
+
+    /**
+     * need overwrite Stack::Delete, since we want to call
+     * SecondaryView::DeleteLast
+     *
+     * The particle is deleted on the underlying (internal) stack. The
+     * local references in SecondaryView in indices_ must be fixed,
+     * too.  The approach is to a) check if the particle 'p' is at the
+     * very end of the internal stack, b) if not: move it there by
+     * copying the last particle to the current particle location, c)
+     * remove the last particle.
+     *
+     */
+    inline 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() {
+      while (purgeLastIfDeleted()) {}
+      return last();
+    }
+
+    /**
+     * check if this particle was already deleted
+     *
+     * need to re-implement for SecondaryView since stack_view_iterator types are a bit
+     * different
+     */
+    inline bool isErased(const stack_view_iterator& p) const {
+      return isErased(p.getIndex() - 1);
+    }
+
+    inline bool isErased(const const_stack_view_iterator& p) const {
+      return isErased(p.getIndex() - 1);
+    }
+    /**
+     * delete this particle
+     */
+    inline bool isErased(const ParticleInterfaceType& p) const {
+      return isErased(p.getIterator());
+    }
+
+    inline bool isDeleted(const const_stack_view_iterator& p) const {
+      return isDeleted(p.getIndex() - 1);
+    }
+    /**
+     * 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();
+
+    /**
+     * Function to ultimatively remove all entries from the stack
+     * marked as deleted.
+     *
+     * Careful: this will re-order the entries on the stack, since
+     * "gaps" in the stack are filled with entries from the back
+     * (copied).
+     */
+    inline void purge();
+
+    inline std::string asString() const;
+
+  protected:
+    friend class StackIteratorInterface<
+        typename std::remove_reference<TStackDataType>::type, TParticleInterface,
+        view_type>;
+
+    friend class ConstStackIteratorInterface<
+        typename std::remove_reference<TStackDataType>::type, TParticleInterface,
+        view_type>;
+
+    friend class ParticleBase<stack_view_iterator>;
+
+    /**
+     * Overwrite of Stack::stack_view_iterator
+     *
+     * increase stack size, create new particle at end of stack,
+     * related to parent particle/projectile
+     *
+     * This should only get internally called from a
+     * stack_view_iterator::addSecondary via ParticleBase
+     */
+    template <typename... Args>
+    inline 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 {
+      if (i >= indices_.size()) return false;
+      return inner_stack_.isErased(getIndexFromIterator(i + 1));
+    }
+
+    /**
+     * We only want to 'see' secondaries indexed in indices_. In this
+     * function the conversion form iterator-index to stack-index is
+     * performed.
+     */
+    inline 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_;
+      return indices_[vI - 1];
+    }
+
+  private:
+    inner_stack_value_type& inner_stack_;
+    unsigned int projectile_index_;
+    std::vector<unsigned int> indices_;
+  };
+
+  /**
+   * Class to handle the generation of new secondaries. Used as default mix-in for
+   * SecondaryView.
+   */
+  template <class T1, template <class> class T2>
+  class DefaultSecondaryProducer {
+    using View = SecondaryView<T1, T2, DefaultSecondaryProducer>;
+
+  public:
+    static bool constexpr has_event{false};
+
+    /**
+     * Method is called after a new secondary has been created on the
+     * SecondaryView. Extra logic can be introduced here.
+     *
+     * The input Particle is the new secondary that was produced and
+     * is of course a reference into the SecondaryView itself.
+     */
+    template <typename Particle>
+    inline void new_secondary(Particle&&) const {
+      CORSIKA_LOG_TRACE("DefaultSecondaryProducer::new_secondary(Particle&&)");
+    }
+
+    /**
+     * Method is called when a new SecondaryView is being created
+     * created. Extra logic can be introduced here.
+     *
+     * The input Particle is a reference object into the original
+     * parent stack! It is not a reference into the SecondaryView
+     * itself.
+     */
+    template <typename Particle>
+    inline DefaultSecondaryProducer(Particle const&) {
+
+      CORSIKA_LOG_TRACE("DefaultSecondaryProducer::DefaultSecondaryProducer(Particle&)");
+    }
+  };
+
+  /*
+    See Issue 161
+
+    unfortunately clang does not support this in the same way (yet) as
+    gcc, so we have to distinguish here. If clang cataches up, we
+    could remove the #if here and elsewhere. The gcc code is much more
+    generic and universal.
+  */
+#if not defined(__clang__) && defined(__GNUC__) || defined(__GNUG__)
+  template <typename TStack,
+            template <class TStack_, template <class> class pi_type_>
+            class MSecondaryProducer = corsika::DefaultSecondaryProducer,
+            template <typename> typename pi_type_ = TStack::template pi_type>
+  struct MakeView {
+    using type = corsika::SecondaryView<typename TStack::stack_implementation_type,
+                                        pi_type_, MSecondaryProducer>;
+  };
+#endif
+
+} // namespace corsika
+
+#include <corsika/detail/framework/stack/SecondaryView.inl>
diff --git a/corsika/framework/stack/Stack.hpp b/corsika/framework/stack/Stack.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..e72dc94dbeb304258aab5206fb7e4552474725d4
--- /dev/null
+++ b/corsika/framework/stack/Stack.hpp
@@ -0,0 +1,316 @@
+/*
+ * (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/core/Logging.hpp>
+#include <corsika/framework/stack/StackIteratorInterface.hpp>
+
+#include <stdexcept>
+#include <string>
+#include <vector>
+#include <utility>
+#include <type_traits>
+
+/**
+  @file Stack.hpp
+
+  Description of particle stacks
+
+  In the CORSIKA 8 framework particle data is always stored in
+  particle stacks. A particle is, thus, always a reference (iterator)
+  to an entry on a stack, e.g.
+
+  \verbatim
+  ModelStack stack;
+  stack.begin(); // returns an iterator: StackIterator, ConstStackIterator
+
+  *stack.begin(); // return a reference to ParticleInterfaceType, which is the class
+provided by the user to read/write particle properties
+
+  \endverbatim
+
+  All functionality and algorithms for stack data access is located in the namespace
+corsika::stack
+
+  The minimal example of how to use this is shown in stack_example.cc
+**/
+
+namespace corsika {
+
+  /**
+     This is just a forward declatation for the user-defined
+     ParticleInterface, which is one of the essential template
+     parameters for the Stack.
+
+     <b>Important:</b> ParticleInterface must inherit from ParticleBase !
+   */
+
+  template <typename>
+  class ParticleInterface;
+
+  /**
+     The Stack class provides (and connects) the main particle data storage machinery.
+
+     The StackDataType type is the user-provided bare data storage
+     object. This can be of any complexity, from a simple struct
+     (fortran common block), to a combination of different and
+     distributed data sources.
+
+     The user-provided ParticleInterface template type is the base
+     class type of the StackIteratorInterface class (CRTP) and must
+     provide all functions to read single particle data from the
+     StackDataType, given an 'unsigned int' index.
+
+     The Stack implements the
+     std-type begin/end function to allow integration in normal for
+     loops, ranges, etc.
+   */
+
+  template <typename StackData, template <typename> typename MParticleInterface>
+  class Stack {
+
+    typedef typename std::remove_reference<StackData>::type value_type;
+
+  public:
+    typedef StackData stack_implementation_type; ///< this is the type of the
+                                                 ///< user-provided data structure
+
+    template <typename TSI>
+    using pi_type = MParticleInterface<TSI>;
+
+    /**
+     * Via the StackIteratorInterface and ConstStackIteratorInterface
+     * specialization, the type of the stack_iterator_type
+     * template class is declared for a particular stack data
+     * object. Using CRTP, this also determines the type of
+     * MParticleInterface template class simultaneously.
+     */
+    typedef StackIteratorInterface<value_type, MParticleInterface, Stack>
+        stack_iterator_type;
+
+    typedef ConstStackIteratorInterface<value_type, MParticleInterface, Stack>
+        const_stack_iterator_type;
+
+    /**
+     * this is the full type of the user-declared MParticleInterface
+     */
+    typedef typename stack_iterator_type::particle_interface_type particle_interface_type;
+    /**
+     * In all programming context, the object to access, copy, and
+     * transport particle data is via the stack_iterator_type
+     */
+    typedef stack_iterator_type particle_type;
+
+    /**
+     * create a new Stack, if there is already data associated prepare
+     * needed initialization.
+     */
+    Stack()
+        : nDeleted_(0)
+        , data_()
+        , deleted_(std::vector<bool>(data_.getSize(), false)) {}
+
+    Stack(Stack&) = delete; ///< since Stack can be very big, we don't want to copy it
+
+    Stack& operator=(Stack&) =
+        delete; ///< since Stack can be very big, we don't want to copy it
+
+    /**
+     * if StackData is a reference member we *HAVE* to initialize
+     * it in the constructor, this is typically needed for SecondaryView
+     */
+    template <typename UType = StackData,
+              typename = typename std::enable_if<std::is_reference<UType>::value>::type>
+    Stack(StackData vD)
+        : nDeleted_(0)
+        , data_(vD)
+        , deleted_(std::vector<bool>(data_.getSize(), false)) {}
+
+    /**
+     * This constructor takes any argument and passes it on to the
+     * StackData user class. If the user did not provide a suited
+     * constructor this will fail with an error message.
+     *
+     * Furthermore, this is disabled with enable_if for SecondaryView
+     * stacks, where the inner data container is always a reference
+     * and cannot be initialized here.
+     */
+    template <typename... TArgs, typename UType = StackData,
+              typename = typename std::enable_if<std::is_reference<UType>::value>::type>
+    Stack(TArgs... args)
+        : nDeleted_(0)
+        , data_(args...)
+        , deleted_(std::vector<bool>(data_.getSize(), false)) {}
+
+    /**
+     * @name Most generic proxy methods for StackData data_
+     * @{
+     */
+    inline unsigned int getCapacity() const { return data_.getCapacity(); }
+
+    inline unsigned int getErased() const { return nDeleted_; }
+
+    inline unsigned int getEntries() const { return getSize() - getErased(); }
+
+    template <typename... TArgs>
+    inline void clear(TArgs... args);
+    ///@}
+
+    /**
+     * @name These are functions required by std containers and std loops
+     * @{
+     */
+    inline stack_iterator_type begin();
+
+    inline stack_iterator_type end();
+
+    inline stack_iterator_type last();
+
+    inline const_stack_iterator_type begin() const;
+
+    inline const_stack_iterator_type end() const;
+
+    inline const_stack_iterator_type last() const;
+
+    inline const_stack_iterator_type cbegin() const;
+
+    inline const_stack_iterator_type cend() const;
+
+    inline const_stack_iterator_type clast() const;
+
+    inline stack_iterator_type at(unsigned int i);
+
+    inline const_stack_iterator_type at(unsigned int i) const;
+
+    inline stack_iterator_type first();
+
+    inline const_stack_iterator_type cfirst() const;
+
+    inline 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);
+
+    inline void swap(stack_iterator_type a, stack_iterator_type b);
+
+    inline void copy(stack_iterator_type a, stack_iterator_type b);
+
+    inline void copy(const_stack_iterator_type a, stack_iterator_type b);
+
+    inline void erase(stack_iterator_type p);
+    /**
+     * delete this particle
+     */
+
+    inline void erase(particle_interface_type p);
+
+    /**
+     * check if there are no further non-deleted particles on stack
+     */
+
+    inline bool isEmpty();
+
+    /**
+     * check if this particle was already deleted
+     */
+
+    inline bool isErased(const stack_iterator_type& p) const;
+
+    inline bool isErased(const const_stack_iterator_type& p) const;
+
+    inline 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();
+    /**
+     * Function to ultimatively remove all entries from the stack
+     * marked as deleted.
+     *
+     * Careful: this will re-order the entries on the stack, since
+     * "gaps" in the stack are filled with entries from the back
+     * (copied).
+     */
+    inline void purge();
+
+    inline unsigned int getSize() const;
+
+    inline std::string asString() const;
+
+  protected:
+    /**
+     * increase stack size, create new particle at end of stack, related to parent
+     * particle/projectile
+     *
+     * This should only get internally called from a
+     * StackIterator::AddSecondary via ParticleBase
+     */
+    template <typename... TArgs>
+    inline stack_iterator_type addSecondary(stack_iterator_type& parent,
+                                            const TArgs... v);
+
+    inline void swap(unsigned int const a, unsigned int const b);
+
+    inline void copy(unsigned int const a, unsigned int const b);
+
+    inline bool isErased(unsigned int const i) const;
+
+    inline 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);
+
+    /**
+     * 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.
+     */
+    inline unsigned int getIndexFromIterator(const unsigned int vI) const;
+    /**
+     * @name Return reference to StackData object data_ for data access
+     * @{
+     */
+
+    inline value_type& getStackData();
+
+    inline const value_type& getStackData() const;
+
+    friend class StackIteratorInterface<value_type, MParticleInterface, Stack>;
+    friend class ConstStackIteratorInterface<value_type, MParticleInterface, Stack>;
+    template <typename T1, //=StackData,
+              template <typename>
+              typename M1, //=MParticleInterface,
+                           //             template<typename>typename M2>
+              template <class T2, template <class> class T3> class MSecondaryProducer>
+    friend class SecondaryView; //<StackData,MParticleInterface,M>; // access for
+                                // SecondaryView
+
+    friend class ParticleBase<stack_iterator_type>;
+
+  protected:
+    unsigned int nDeleted_ = 0;
+
+  private:
+    StackData data_; ///< this in general holds all the data and can be quite big
+    std::vector<bool> deleted_; ///< bit field to flag deleted entries
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/framework/stack/Stack.inl>
diff --git a/Framework/StackInterface/StackIteratorInterface.h b/corsika/framework/stack/StackIteratorInterface.hpp
similarity index 56%
rename from Framework/StackInterface/StackIteratorInterface.h
rename to corsika/framework/stack/StackIteratorInterface.hpp
index 6c4901084843c568016450205435884d3e6bb972..f6240e87800d9232bb100587d68dde50850cb2a5 100644
--- a/Framework/StackInterface/StackIteratorInterface.h
+++ b/corsika/framework/stack/StackIteratorInterface.hpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
@@ -8,14 +8,14 @@
 
 #pragma once
 
-#include <corsika/stack/ParticleBase.h>
+#include <corsika/framework/stack/ParticleBase.hpp>
 
 namespace corsika::history {
-  template <typename T, template <typename> typename ParticleInterface>
+  template <typename T, template <typename> typename TParticleInterface>
   class HistorySecondaryProducer; // forward decl.
 }
 
-namespace corsika::stack {
+namespace corsika {
 
   template <typename TStackData, template <typename> typename TParticleInterface>
   class Stack; // forward decl
@@ -29,10 +29,10 @@ namespace corsika::stack {
   class ConstStackIteratorInterface; // forward decl
 
   /**
-     @class StackIteratorInterface
-
      The StackIteratorInterface is the main interface to iterator over
-     particles on a stack. At the same time StackIteratorInterface is a
+     particles on a stack.
+
+     At the same time StackIteratorInterface is a
      Particle object by itself, thus there is no difference between
      type and ref_type for convenience of the physicist.
 
@@ -52,59 +52,37 @@ namespace corsika::stack {
      for a particle entry at any index index_.
 
      The TParticleInterface class must be written and provided by the
-     user, it contains methods like <code> auto GetData() const {
-     return GetStackData().GetData(GetIndex()); }</code>, where
-     StackIteratorInterface::GetStackData() return a reference to the
+     user, it contains methods like <code> auto getData() const {
+     return getStackData().getData(getIndex()); }</code>, where
+     StackIteratorInterface::getStackData() return a reference to the
      object storing the particle data of type TStackData. And
-     StackIteratorInterface::GetIndex() provides the iterator index to
+     StackIteratorInterface::getIndex() provides the iterator index to
      be readout. The TStackData is another user-provided class to
      store data and must implement functions compatible with
-     TParticleInterface, in this example TStackData::GetData(const unsigned int
+     TParticleInterface, in this example TStackData::getData(const unsigned int
      vIndex).
 
      For two examples see stack_example.cc, or the
-     corsika::processes::sibyll::SibStack class
-  **/
+     corsikaes::sibyll::SibStack class
+  */
 
   template <typename TStackData, template <typename> typename TParticleInterface,
-            typename StackType = Stack<TStackData, TParticleInterface>>
+            typename TStackType = Stack<TStackData, TParticleInterface>>
   class StackIteratorInterface
       : public TParticleInterface<
-            StackIteratorInterface<TStackData, TParticleInterface, StackType>> {
+            StackIteratorInterface<TStackData, TParticleInterface, TStackType>> {
 
   public:
-    using ParticleInterfaceType =
-        TParticleInterface<corsika::stack::StackIteratorInterface<
-            TStackData, TParticleInterface, StackType>>;
-
-    // friends are needed for access to protected methods
-    friend class Stack<TStackData,
-                       TParticleInterface>; // for access to GetIndex for Stack
-    friend class Stack<TStackData&, TParticleInterface>; // for access to GetIndex
-                                                         // SecondaryView : public Stack
-    friend class ParticleBase<StackIteratorInterface>;   // for access to GetStackData
-
-    template <typename T1,                     // best fix this to: TStackData,
-              template <typename> typename M1, // best fix this to: TParticleInterface,
-              template <typename T, template <typename> typename T3> typename M2>
-    friend class SecondaryView; // access grant for SecondaryView
-
-    template <typename T, template <typename> typename ParticleInterface>
-    friend class corsika::history::HistorySecondaryProducer;
+    typedef TParticleInterface<
+        corsika::StackIteratorInterface<TStackData, TParticleInterface, TStackType>>
+        particle_interface_type;
 
-    friend class ConstStackIteratorInterface<TStackData, TParticleInterface, StackType>;
-
-  protected:
-    unsigned int index_ = 0;
-
-  private:
-    StackType* data_ = 0; // info: Particles and StackIterators become invalid when parent
-                          // Stack is copied or deleted!
+    // using ParticleInterfaceType = TParticleInterface<
+    //  corsika::StackIteratorInterface<TStackData, TParticleInterface, TStackType>>;
 
     // it is not allowed to create a "dangling" stack iterator
-    StackIteratorInterface() = delete;
+    StackIteratorInterface() = delete; //! \todo check rule of five
 
-  public:
     StackIteratorInterface(StackIteratorInterface&& rhs)
         : index_(std::move(rhs.index_))
         , data_(std::move(rhs.data_)) {}
@@ -114,16 +92,18 @@ namespace corsika::stack {
         , data_(vR.data_) {}
 
     StackIteratorInterface& operator=(StackIteratorInterface const& vR) {
-      index_ = vR.index_;
-      data_ = vR.data_;
+      if (&vR != this) {
+        index_ = vR.index_;
+        data_ = vR.data_;
+      }
       return *this;
     }
 
     /** iterator must always point to data, with an index:
           @param data reference to the stack [rw]
           @param index index on stack
-    **/
-    StackIteratorInterface(StackType& data, const unsigned int index)
+       */
+    StackIteratorInterface(TStackType& data, unsigned int const index)
         : index_(index)
         , data_(&data) {}
 
@@ -132,13 +112,14 @@ namespace corsika::stack {
         @param index index on stack
         @param args variadic list of data to initialize stack entry, this must be
        consistent with the definition of the user-provided
-       ParticleInterfaceType::SetParticleData(...) function
-    **/
-    template <typename... Args>
-    StackIteratorInterface(StackType& data, const unsigned int index, const Args... args)
+       particle_interface_type::SetParticleData(...) function
+     */
+    template <typename... TArgs>
+    StackIteratorInterface(TStackType& data, unsigned int const index,
+                           const TArgs... args)
         : index_(index)
         , data_(&data) {
-      (**this).SetParticleData(args...);
+      (**this).setParticleData(args...);
     }
 
     /** constructor that also sets new values on particle data object, including reference
@@ -149,67 +130,67 @@ namespace corsika::stack {
        counting, history, etc.
         @param args variadic list of data to initialize stack entry, this must be
        consistent with the definition of the user-provided
-       ParticleInterfaceType::SetParticleData(...) function
-    **/
+       particle_interface_type::SetParticleData(...) function
+    */
     template <typename... Args>
-    StackIteratorInterface(StackType& data, const unsigned int index,
+    StackIteratorInterface(TStackType& data, unsigned int const index,
                            StackIteratorInterface& parent, const Args... args)
         : index_(index)
         , data_(&data) {
-      (**this).SetParticleData(*parent, args...);
+      (**this).setParticleData(*parent, args...);
     }
 
-    bool isDeleted() const { return GetStack().isDeleted(*this); }
+    bool isErased() const { return getStack().isErased(*this); }
 
   public:
     /** @name Iterator interface
         @{
     **/
-    StackIteratorInterface& operator++() {
+    inline StackIteratorInterface& operator++() {
       do {
         ++index_;
       } while (
-          GetStack().isDeleted(*this)); // this also check the allowed bounds of index_
+          getStack().isErased(*this)); // this also check the allowed bounds of index_
       return *this;
     }
-    StackIteratorInterface operator++(int) {
+    inline StackIteratorInterface operator++(int) {
       StackIteratorInterface tmp(*this);
       do {
         ++index_;
       } while (
-          GetStack().isDeleted(*this)); // this also check the allowed bounds of index_
+          getStack().isErased(*this)); // this also check the allowed bounds of index_
       return tmp;
     }
-    StackIteratorInterface operator+(int delta) const {
+    inline StackIteratorInterface operator+(int delta) const {
       return StackIteratorInterface(*data_, index_ + delta);
     }
-    bool operator==(const StackIteratorInterface& rhs) const {
+    inline bool operator==(StackIteratorInterface const& rhs) const {
       return index_ == rhs.index_;
     }
-    bool operator!=(const StackIteratorInterface& rhs) const {
+    inline bool operator!=(StackIteratorInterface const& rhs) const {
       return index_ != rhs.index_;
     }
-    bool operator==(
-        const ConstStackIteratorInterface<TStackData, TParticleInterface, StackType>& rhs)
-        const; // implement below
-    bool operator!=(
-        const ConstStackIteratorInterface<TStackData, TParticleInterface, StackType>& rhs)
-        const; // implement below
+    inline bool operator==(
+        const ConstStackIteratorInterface<TStackData, TParticleInterface, TStackType>&
+            rhs) const; // implemented below
+    inline bool operator!=(
+        const ConstStackIteratorInterface<TStackData, TParticleInterface, TStackType>&
+            rhs) const; // implemented below
 
     /**
      * Convert iterator to value type, where value type is the user-provided particle
      * readout class
      **/
-    ParticleInterfaceType& operator*() {
-      return static_cast<ParticleInterfaceType&>(*this);
+    inline particle_interface_type& operator*() {
+      return static_cast<particle_interface_type&>(*this);
     }
 
     /**
      * Convert iterator to const value type, where value type is the user-provided
      * particle readout class
      **/
-    const ParticleInterfaceType& operator*() const {
-      return static_cast<const ParticleInterfaceType&>(*this);
+    inline particle_interface_type const& operator*() const {
+      return static_cast<particle_interface_type const&>(*this);
     }
     ///@}
 
@@ -219,99 +200,94 @@ namespace corsika::stack {
      * @{
      **/
     /// Get current particle index
-    inline unsigned int GetIndex() const { return index_; }
+    inline unsigned int getIndex() const { return index_; }
     /// Get current particle Stack object
-    inline StackType& GetStack() { return *data_; }
+    inline TStackType& getStack() { return *data_; }
     /// Get current particle const Stack object
-    inline const StackType& GetStack() const { return *data_; }
+    inline TStackType const& getStack() const { return *data_; }
     /// Get current user particle TStackData object
-    inline TStackData& GetStackData() { return data_->GetStackData(); }
+    inline TStackData& getStackData() { return data_->getStackData(); }
     /// Get current const user particle TStackData object
-    inline const TStackData& GetStackData() const { return data_->GetStackData(); }
+    inline TStackData const& getStackData() const { return data_->getStackData(); }
     /// Get data index as mapped in Stack class
-    inline unsigned int GetIndexFromIterator() const {
-      return data_->GetIndexFromIterator(index_);
+    inline unsigned int getIndexFromIterator() const {
+      return data_->getIndexFromIterator(index_);
     }
     ///@}
-  }; // end class StackIterator
-
-  /**
-     @class ConstStackIteratorInterface
-
-     This is the iterator class for const-access to stack data
-  **/
-
-  template <typename TStackData, template <typename> typename TParticleInterface,
-            typename StackType = Stack<TStackData, TParticleInterface>>
-  class ConstStackIteratorInterface
-      : public TParticleInterface<
-            ConstStackIteratorInterface<TStackData, TParticleInterface, StackType>> {
-
-  public:
-    typedef TParticleInterface<
-        ConstStackIteratorInterface<TStackData, TParticleInterface, StackType>>
-        ParticleInterfaceType;
 
     // friends are needed for access to protected methods
     friend class Stack<TStackData,
-                       TParticleInterface>; // for access to GetIndex for Stack
-    friend class Stack<TStackData&, TParticleInterface>; // for access to GetIndex
-
-    friend class ParticleBase<ConstStackIteratorInterface>; // for access to GetStackData
-
-    template <typename T1,                     // best fix to: TStackData,
-              template <typename> typename M1, // best fix to: TParticleInterface,
-              template <class T2, template <class> class T3> class MSecondaryProducer>
-    friend class SecondaryView; // access for SecondaryView
+                       TParticleInterface>; // for access to getIndex for Stack
+    friend class Stack<TStackData&, TParticleInterface>; // for access to getIndex
+                                                         // SecondaryView : public Stack
+    friend class ParticleBase<StackIteratorInterface>;   // for access to getStackDataType
 
-    friend class StackIteratorInterface<TStackData, TParticleInterface, StackType>;
+    template <typename T1,                     // best fix this to: TStackData,
+              template <typename> typename M1, // best fix this to: TParticleInterface,
+              template <typename T, template <typename> typename T3> typename M2>
+    friend class SecondaryView; // access grant for SecondaryView
 
-    template <typename T, template <typename> typename ParticleInterface>
+    template <typename T, template <typename> typename TParticleInterface_>
     friend class corsika::history::HistorySecondaryProducer;
 
+    friend class ConstStackIteratorInterface<TStackData, TParticleInterface, TStackType>;
+
   protected:
     unsigned int index_ = 0;
 
   private:
-    const StackType* data_ = 0; // info: Particles and StackIterators become invalid when
-                                // parent Stack is copied or deleted!
+    TStackType* data_ = 0; // info: Particles and StackIterators become invalid when
+                           // parent Stack is copied or deleted!
+
+  }; // end class StackIterator
+
+  /**
+     This is the iterator class for const-access to stack data.
+
+     The const counterpart of StackIteratorInterface, which is used
+     for read-only iterator access on particle stack:
+
+     \verbatim
+     for (auto const& p : theStack) { E += p.getEnergy(); }
+     \endverbatim
+
+     See documentation of StackIteratorInterface for more details:
+     \sa StackIteratorInterface
+   */
+
+  template <typename TStackData, template <typename> typename TParticleInterface,
+            typename TStackType = Stack<TStackData, TParticleInterface>>
+  class ConstStackIteratorInterface
+      : public TParticleInterface<
+            ConstStackIteratorInterface<TStackData, TParticleInterface, TStackType>> {
+
+  public:
+    typedef TParticleInterface<
+        ConstStackIteratorInterface<TStackData, TParticleInterface, TStackType>>
+        particle_interface_type;
 
     // we don't want to allow dangling iterators to exist
-    ConstStackIteratorInterface() = delete;
+    ConstStackIteratorInterface() = delete; //! \todo check rule of five
 
   public:
     ConstStackIteratorInterface(ConstStackIteratorInterface&& rhs)
         : index_(std::move(rhs.index_))
         , data_(std::move(rhs.data_)) {}
 
-    ConstStackIteratorInterface(const StackType& data, const unsigned int index)
+    ConstStackIteratorInterface(TStackType const& data, unsigned int const index)
         : index_(index)
         , data_(&data) {}
 
-    /**
-       @class ConstStackIteratorInterface
+    bool isErased() const { return getStack().isErased(*this); }
 
-       The const counterpart of StackIteratorInterface, which is used
-       for read-only iterator access on particle stack:
-
-       \verbatim
-       for (const auto& p : theStack) { E += p.GetEnergy(); }
-       \endverbatim
-
-       See documentation of StackIteratorInterface for more details.
-    **/
-
-    bool isDeleted() const { return GetStack().isDeleted(*this); }
-
-  public:
     /** @name Iterator interface
-     **/
-    ///@{
+        @{
+     */
     ConstStackIteratorInterface& operator++() {
       do {
         ++index_;
       } while (
-          GetStack().isDeleted(*this)); // this also check the allowed bounds of index_
+          getStack().isErased(*this)); // this also check the allowed bounds of index_
       return *this;
     }
     ConstStackIteratorInterface operator++(int) {
@@ -319,61 +295,70 @@ namespace corsika::stack {
       do {
         ++index_;
       } while (
-          GetStack().isDeleted(*this)); // this also check the allowed bounds of index_
+          getStack().isErased(*this)); // this also check the allowed bounds of index_
       return tmp;
     }
-    ConstStackIteratorInterface operator+(const int delta) const {
+    ConstStackIteratorInterface operator+(int const delta) const {
       return ConstStackIteratorInterface(*data_, index_ + delta);
     }
-    bool operator==(const ConstStackIteratorInterface& rhs) const {
+    bool operator==(ConstStackIteratorInterface const& rhs) const {
       return index_ == rhs.index_;
     }
-    bool operator!=(const ConstStackIteratorInterface& rhs) const {
+    bool operator!=(ConstStackIteratorInterface const& rhs) const {
       return index_ != rhs.index_;
     }
-    bool operator==(const StackIteratorInterface<TStackData, TParticleInterface,
-                                                 StackType>& rhs) const {
+    bool operator==(StackIteratorInterface<TStackData, TParticleInterface,
+                                           TStackType> const& rhs) const {
       return index_ == rhs.index_;
     }
-    bool operator!=(const StackIteratorInterface<TStackData, TParticleInterface,
-                                                 StackType>& rhs) const {
+    bool operator!=(StackIteratorInterface<TStackData, TParticleInterface,
+                                           TStackType> const& rhs) const {
       return index_ != rhs.index_;
     }
 
-    const ParticleInterfaceType& operator*() const {
-      return static_cast<const ParticleInterfaceType&>(*this);
+    particle_interface_type const& operator*() const {
+      return static_cast<particle_interface_type const&>(*this);
     }
     ///@}
 
   protected:
     /** @name Stack data access
         Only the const versions for read-only access
-    **/
-    ///@{
-    inline unsigned int GetIndex() const { return index_; }
-    inline const StackType& GetStack() const { return *data_; }
-    inline const TStackData& GetStackData() const { return data_->GetStackData(); }
+        @{
+     */
+    inline unsigned int getIndex() const { return index_; }
+    inline TStackType const& getStack() const { return *data_; }
+    inline TStackData const& getStackData() const { return data_->getStackData(); }
     /// Get data index as mapped in Stack class
-    inline unsigned int GetIndexFromIterator() const {
-      return data_->GetIndexFromIterator(index_);
+    inline unsigned int getIndexFromIterator() const {
+      return data_->getIndexFromIterator(index_);
     }
     ///@}
-  }; // end class ConstStackIterator
 
-  template <typename TStackData, template <typename> typename TParticleInterface,
-            typename StackType>
-  bool StackIteratorInterface<TStackData, TParticleInterface, StackType>::operator==(
-      const ConstStackIteratorInterface<TStackData, TParticleInterface, StackType>& rhs)
-      const {
-    return index_ == rhs.index_;
-  }
+    // friends are needed for access to protected methods
+    friend class Stack<TStackData,
+                       TParticleInterface>; // for access to GetIndex for Stack
+    friend class Stack<TStackData&, TParticleInterface>; // for access to GetIndex
 
-  template <typename TStackData, template <typename> typename TParticleInterface,
-            typename StackType>
-  bool StackIteratorInterface<TStackData, TParticleInterface, StackType>::operator!=(
-      const ConstStackIteratorInterface<TStackData, TParticleInterface, StackType>& rhs)
-      const {
-    return index_ != rhs.index_;
-  }
+    friend class ParticleBase<ConstStackIteratorInterface>; // for access to GetStackData
+
+    template <typename T1,                     // best fix to: TStackData,
+              template <typename> typename M1, // best fix to: TParticleInterface,
+              template <class T2, template <class> class T3> class MSecondaryProducer>
+    friend class SecondaryView; // access for SecondaryView
+
+    friend class StackIteratorInterface<TStackData, TParticleInterface, TStackType>;
+
+    template <typename T, template <typename> typename TParticleInterface_>
+    friend class corsika::history::HistorySecondaryProducer;
+
+  protected:
+    unsigned int index_ = 0;
+
+  private:
+    TStackType const* data_ = 0; // info: Particles and StackIterators become invalid when
+                                 // parent Stack is copied or deleted!
+
+  }; // end class ConstStackIterator
 
-} // namespace corsika::stack
+} // namespace corsika
diff --git a/ThirdParty/phys/units/io.hpp b/corsika/framework/units/io.hpp
similarity index 76%
rename from ThirdParty/phys/units/io.hpp
rename to corsika/framework/units/io.hpp
index 9bb53384206c6676cc0c546477e1bdb57c791fe5..f250a36af1a2103fcfb07928eef0faa5ec6a2c52 100644
--- a/ThirdParty/phys/units/io.hpp
+++ b/corsika/framework/units/io.hpp
@@ -16,9 +16,9 @@
 #ifndef PHYS_UNITS_IO_HPP_INCLUDED
 #define PHYS_UNITS_IO_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
-#include "phys/units/quantity_io_engineering.hpp"
-#include "phys/units/quantity_io_symbols.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io_engineering.hpp"
+#include "corsika/framework/units/quantity_io_symbols.hpp"
 
 #endif // PHYS_UNITS_IO_HPP_INCLUDED
 
diff --git a/ThirdParty/phys/units/io_output.hpp b/corsika/framework/units/io_output.hpp
similarity index 91%
rename from ThirdParty/phys/units/io_output.hpp
rename to corsika/framework/units/io_output.hpp
index f9095a92dbb982c9bbbac73925ad768ec0836868..c2786ad1581c32ed7611ffde3d6b0d14706f57d8 100644
--- a/ThirdParty/phys/units/io_output.hpp
+++ b/corsika/framework/units/io_output.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_IO_OUTPUT_HPP_INCLUDED
 #define PHYS_UNITS_IO_OUTPUT_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 #endif // PHYS_UNITS_IO_OUTPUT_HPP_INCLUDED
 
diff --git a/ThirdParty/phys/units/io_output_eng.hpp b/corsika/framework/units/io_output_eng.hpp
similarity index 90%
rename from ThirdParty/phys/units/io_output_eng.hpp
rename to corsika/framework/units/io_output_eng.hpp
index 47bc2e2e81311ca4e07a7c933abb204902ee5887..d4f709acd281cc191b54787a0c3cce08d547fc38 100644
--- a/ThirdParty/phys/units/io_output_eng.hpp
+++ b/corsika/framework/units/io_output_eng.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_IO_ENG_HPP_INCLUDED
 #define PHYS_UNITS_IO_ENG_HPP_INCLUDED
 
-#include "phys/units/quantity_io_engineering.hpp"
+#include "corsika/framework/units/quantity_io_engineering.hpp"
 
 #endif // PHYS_UNITS_IO_ENG_HPP_INCLUDED
 
diff --git a/ThirdParty/phys/units/io_symbols.hpp b/corsika/framework/units/io_symbols.hpp
similarity index 90%
rename from ThirdParty/phys/units/io_symbols.hpp
rename to corsika/framework/units/io_symbols.hpp
index b91666ee9f99e65ab8320e236546e4d139e40909..adeaf7f2ae3dfc4c815da3db10c3589a539044bf 100644
--- a/ThirdParty/phys/units/io_symbols.hpp
+++ b/corsika/framework/units/io_symbols.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_IO_SYMBOLS_HPP_INCLUDED
 #define PHYS_UNITS_IO_SYMBOLS_HPP_INCLUDED
 
-#include "phys/units/quantity_io_symbols.hpp"
+#include "corsika/framework/units/quantity_io_symbols.hpp"
 
 #endif // PHYS_UNITS_IO_SYMBOLS_HPP_INCLUDED
 
diff --git a/ThirdParty/phys/units/other_units.hpp b/corsika/framework/units/other_units.hpp
similarity index 99%
rename from ThirdParty/phys/units/other_units.hpp
rename to corsika/framework/units/other_units.hpp
index 3e6c35540afbf7f79ffcb1a1d8045a88240b24b9..4ba5df3d8919e06d8cae67add482cb761ae77229 100644
--- a/ThirdParty/phys/units/other_units.hpp
+++ b/corsika/framework/units/other_units.hpp
@@ -29,7 +29,7 @@
 #ifndef PHYS_UNITS_OTHER_UNITS_HPP_INCLUDED
 #define PHYS_UNITS_OTHER_UNITS_HPP_INCLUDED
 
-#include "phys/units/quantity.hpp"
+#include "corsika/framework/units/quantity.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/physical_constants.hpp b/corsika/framework/units/physical_constants.hpp
similarity index 97%
rename from ThirdParty/phys/units/physical_constants.hpp
rename to corsika/framework/units/physical_constants.hpp
index 131dfb03c1ee8497254f7d39f50bc3373136331d..ae098428cd1ee18ef94cced81ca53c008585982e 100644
--- a/ThirdParty/phys/units/physical_constants.hpp
+++ b/corsika/framework/units/physical_constants.hpp
@@ -22,7 +22,7 @@
 #ifndef PHYS_UNITS_PHYSICAL_CONSTANTS_HPP_INCLUDED
 #define PHYS_UNITS_PHYSICAL_CONSTANTS_HPP_INCLUDED
 
-#include "phys/units/quantity.hpp"
+#include "corsika/framework/units/quantity.hpp"
 
 namespace phys {
   namespace units {
diff --git a/ThirdParty/phys/units/quantity.hpp b/corsika/framework/units/quantity.hpp
similarity index 98%
rename from ThirdParty/phys/units/quantity.hpp
rename to corsika/framework/units/quantity.hpp
index 0751923b0ca4eb07a75a0d712523bb69b569b904..3e897cfc80151beebdd2fb700af35fd27ae94e47 100644
--- a/ThirdParty/phys/units/quantity.hpp
+++ b/corsika/framework/units/quantity.hpp
@@ -31,7 +31,7 @@
 #include <cmath>
 #include <cstdlib>
 #include <utility> // std::declval
-#include <type_traits> // std::enable_if
+#include <type_traits>
 
 /// namespace phys.
 
@@ -359,10 +359,21 @@ namespace phys {
       static constexpr quantity zero() { return quantity{value_type(0.0)}; }
       //    static constexpr quantity zero = quantity{ value_type( 0.0 ) };
 
-      // RU, added conversion to T (often: double) for dimensionless_d
-      template <typename DIM=Dims, std::enable_if_t<std::is_same_v<DIM, dimensionless_d>, int> = 0>
-      operator T() { return m_value; }
-      
+      /**
+       * We also define "infinity" for each type.
+       *
+       * RU, Do 7. Jan 02:10:18 CET 2021
+       */
+      static constexpr quantity infinity() {
+        return quantity{value_type(std::numeric_limits<value_type>::infinity())};
+      }
+
+      template <typename _dim = dimension_type,
+                std::enable_if_t<std::is_same_v<_dim, dimensionless_d>, bool> = true>
+      operator double() const {
+        return m_value;
+      }
+
     private:
       /**
        * private initializing constructor.
@@ -476,7 +487,7 @@ namespace phys {
 
       template <typename D, typename X, typename Y>
       friend constexpr bool operator>=(quantity<D, X> const& x, quantity<D, Y> const& y);
-    };
+    }; // namespace units
 
     // helper to check whether some type is a quantity
     template <typename TNonQuantityType>
diff --git a/ThirdParty/phys/units/quantity_io.hpp b/corsika/framework/units/quantity_io.hpp
similarity index 99%
rename from ThirdParty/phys/units/quantity_io.hpp
rename to corsika/framework/units/quantity_io.hpp
index e94ffab36c1d0c84556cd326353aa1c6eb5d6881..db32e911b85e64feacb7df6428db5e8e30de60d5 100644
--- a/ThirdParty/phys/units/quantity_io.hpp
+++ b/corsika/framework/units/quantity_io.hpp
@@ -22,7 +22,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_HPP_INCLUDED
 
-#include "phys/units/quantity.hpp"
+#include "corsika/framework/units/quantity.hpp"
 
 #include <algorithm>
 #include <iosfwd>
diff --git a/ThirdParty/phys/units/quantity_io_ampere.hpp b/corsika/framework/units/quantity_io_ampere.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_ampere.hpp
rename to corsika/framework/units/quantity_io_ampere.hpp
index a21e8c255546f36170859ba42cdd038a9866274c..98477e1f324260c6c3da41d66c7b3391215df900 100644
--- a/ThirdParty/phys/units/quantity_io_ampere.hpp
+++ b/corsika/framework/units/quantity_io_ampere.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_AMPERE_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_AMPERE_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_becquerel.hpp b/corsika/framework/units/quantity_io_becquerel.hpp
similarity index 95%
rename from ThirdParty/phys/units/quantity_io_becquerel.hpp
rename to corsika/framework/units/quantity_io_becquerel.hpp
index deaf47c3fe4010e170a32dab3bcb94464e89dcd4..cab3454760b918f8fd1fa27518be7fc63be6ffc9 100644
--- a/ThirdParty/phys/units/quantity_io_becquerel.hpp
+++ b/corsika/framework/units/quantity_io_becquerel.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_BECQUEREL_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_BECQUEREL_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_candela.hpp b/corsika/framework/units/quantity_io_candela.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_candela.hpp
rename to corsika/framework/units/quantity_io_candela.hpp
index 8814dbb2d15a3a39aa932ce022ad125af9e763ed..cdb7694a309e023073d6657dcd1c121ddc1b73f6 100644
--- a/ThirdParty/phys/units/quantity_io_candela.hpp
+++ b/corsika/framework/units/quantity_io_candela.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_CANDELA_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_CANDELA_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_celsius.hpp b/corsika/framework/units/quantity_io_celsius.hpp
similarity index 95%
rename from ThirdParty/phys/units/quantity_io_celsius.hpp
rename to corsika/framework/units/quantity_io_celsius.hpp
index fc769a4d465bd535bf05e95e61c1ec615d79ffa1..cc145ecdfe073814c089e8a746c357a8ee189813 100644
--- a/ThirdParty/phys/units/quantity_io_celsius.hpp
+++ b/corsika/framework/units/quantity_io_celsius.hpp
@@ -17,7 +17,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_CELSIUS_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_CELSIUS_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_coulomb.hpp b/corsika/framework/units/quantity_io_coulomb.hpp
similarity index 95%
rename from ThirdParty/phys/units/quantity_io_coulomb.hpp
rename to corsika/framework/units/quantity_io_coulomb.hpp
index 0f140d78ddcd5188c349ef351b483fde385296a5..8fcc4af30c771196fc6034affc5fdbeaccc2cea6 100644
--- a/ThirdParty/phys/units/quantity_io_coulomb.hpp
+++ b/corsika/framework/units/quantity_io_coulomb.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_COULOMB_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_COULOMB_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_dimensionless.hpp b/corsika/framework/units/quantity_io_dimensionless.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_dimensionless.hpp
rename to corsika/framework/units/quantity_io_dimensionless.hpp
index 1d1ca57e7816c934e243614b25d14df491c0a1b4..254dac4d1934e0590fb5832046cf42e565ee4523 100644
--- a/ThirdParty/phys/units/quantity_io_dimensionless.hpp
+++ b/corsika/framework/units/quantity_io_dimensionless.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_DIMENSIONLESS_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_DIMENSIONLESS_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_engineering.hpp b/corsika/framework/units/quantity_io_engineering.hpp
similarity index 98%
rename from ThirdParty/phys/units/quantity_io_engineering.hpp
rename to corsika/framework/units/quantity_io_engineering.hpp
index 0423f5acad4a7305fd28aba4115eef94a2edca1d..16b4be8f59ddc8f9676604063054029fb894913e 100644
--- a/ThirdParty/phys/units/quantity_io_engineering.hpp
+++ b/corsika/framework/units/quantity_io_engineering.hpp
@@ -23,7 +23,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_ENGINEERING_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_ENGINEERING_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 #include <cmath>
 #include <iomanip>
diff --git a/ThirdParty/phys/units/quantity_io_farad.hpp b/corsika/framework/units/quantity_io_farad.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_farad.hpp
rename to corsika/framework/units/quantity_io_farad.hpp
index fcd0c445b9c97535f289424657c510f38398b5d4..aa56f6f6c088fb1f6999d6f5040be868aa5f71cb 100644
--- a/ThirdParty/phys/units/quantity_io_farad.hpp
+++ b/corsika/framework/units/quantity_io_farad.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_FARAD_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_FARAD_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_gray.hpp b/corsika/framework/units/quantity_io_gray.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_gray.hpp
rename to corsika/framework/units/quantity_io_gray.hpp
index 769110dd996ac24baad837a03deab21d7d4ed12b..5e5300b26e37f179303a813d0bc751023c18d03c 100644
--- a/ThirdParty/phys/units/quantity_io_gray.hpp
+++ b/corsika/framework/units/quantity_io_gray.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_GRAY_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_GRAY_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_henry.hpp b/corsika/framework/units/quantity_io_henry.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_henry.hpp
rename to corsika/framework/units/quantity_io_henry.hpp
index d1e733637f4fc3918b3427634f502bded79f2620..34e8989e0983ac9cb13f53bd3e2d2f0de8ead106 100644
--- a/ThirdParty/phys/units/quantity_io_henry.hpp
+++ b/corsika/framework/units/quantity_io_henry.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_HENRY_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_HENRY_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_hertz.hpp b/corsika/framework/units/quantity_io_hertz.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_hertz.hpp
rename to corsika/framework/units/quantity_io_hertz.hpp
index 3695e5435be67ce0fcbd7cdc8d96dcdd000522e0..a3ffff728392d6f768988f234e0ec33c89baee91 100644
--- a/ThirdParty/phys/units/quantity_io_hertz.hpp
+++ b/corsika/framework/units/quantity_io_hertz.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_HERTZ_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_HERTZ_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_joule.hpp b/corsika/framework/units/quantity_io_joule.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_joule.hpp
rename to corsika/framework/units/quantity_io_joule.hpp
index 33f26c5847f7710b0bbf10e781ad2af159dc5734..817f2fe05487f231efdeaa035a4f8091f0865791 100644
--- a/ThirdParty/phys/units/quantity_io_joule.hpp
+++ b/corsika/framework/units/quantity_io_joule.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_JOULE_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_JOULE_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_kelvin.hpp b/corsika/framework/units/quantity_io_kelvin.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_kelvin.hpp
rename to corsika/framework/units/quantity_io_kelvin.hpp
index 24dfa48b2462761c9b73882349c2529420af0224..164206219b04f25df356c9c56a43d164e69c5478 100644
--- a/ThirdParty/phys/units/quantity_io_kelvin.hpp
+++ b/corsika/framework/units/quantity_io_kelvin.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_KELVIN_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_KELVIN_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_kilogram.hpp b/corsika/framework/units/quantity_io_kilogram.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_kilogram.hpp
rename to corsika/framework/units/quantity_io_kilogram.hpp
index 375cdc51841044dd8513bbb3b351448975018395..fdb333229b440a5df0584ad1bf8254383658f064 100644
--- a/ThirdParty/phys/units/quantity_io_kilogram.hpp
+++ b/corsika/framework/units/quantity_io_kilogram.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_KILOGRAM_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_KILOGRAM_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_lumen.hpp b/corsika/framework/units/quantity_io_lumen.hpp
similarity index 95%
rename from ThirdParty/phys/units/quantity_io_lumen.hpp
rename to corsika/framework/units/quantity_io_lumen.hpp
index f5443b5003be3a881540d9fb2aa134ebf4871b5f..f4d53d3fbb19bdd4f12b70ade4aeda829819abd0 100644
--- a/ThirdParty/phys/units/quantity_io_lumen.hpp
+++ b/corsika/framework/units/quantity_io_lumen.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_LUMEN_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_LUMEN_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_lux.hpp b/corsika/framework/units/quantity_io_lux.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_lux.hpp
rename to corsika/framework/units/quantity_io_lux.hpp
index 98a8d9e4b208e7775635158114ed0dd278953cf9..cbaf2986764663a1e040eb763aba6b25452732e7 100644
--- a/ThirdParty/phys/units/quantity_io_lux.hpp
+++ b/corsika/framework/units/quantity_io_lux.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_LUX_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_LUX_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_meter.hpp b/corsika/framework/units/quantity_io_meter.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_meter.hpp
rename to corsika/framework/units/quantity_io_meter.hpp
index 4b537d5020af4d0b1d0bd978a851bf3ad4ef1ee8..3a5ebd9cf948cab0acf68a9315c6930d13398d3b 100644
--- a/ThirdParty/phys/units/quantity_io_meter.hpp
+++ b/corsika/framework/units/quantity_io_meter.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_METER_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_METER_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_mole.hpp b/corsika/framework/units/quantity_io_mole.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_mole.hpp
rename to corsika/framework/units/quantity_io_mole.hpp
index cd3b184dc778a10a0e684e10089f7f79d547c07a..4007e4328c2faf1a9e82272e31dbb98fa2d3ab83 100644
--- a/ThirdParty/phys/units/quantity_io_mole.hpp
+++ b/corsika/framework/units/quantity_io_mole.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_MOLE_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_MOLE_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_newton.hpp b/corsika/framework/units/quantity_io_newton.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_newton.hpp
rename to corsika/framework/units/quantity_io_newton.hpp
index 4a7505e6cfdaffa7c1355cd0ea2ab231ac01023c..7599315b5299ba640ae6f6e5cf53bc1e342165e2 100644
--- a/ThirdParty/phys/units/quantity_io_newton.hpp
+++ b/corsika/framework/units/quantity_io_newton.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_NEWTON_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_NEWTON_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_ohm.hpp b/corsika/framework/units/quantity_io_ohm.hpp
similarity index 95%
rename from ThirdParty/phys/units/quantity_io_ohm.hpp
rename to corsika/framework/units/quantity_io_ohm.hpp
index 84d33c7eb04f2881a4c47b863419482d4f1364ec..2bfce23d0c0841696344dc0c398154412077ad16 100644
--- a/ThirdParty/phys/units/quantity_io_ohm.hpp
+++ b/corsika/framework/units/quantity_io_ohm.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_OHM_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_OHM_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_pascal.hpp b/corsika/framework/units/quantity_io_pascal.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_pascal.hpp
rename to corsika/framework/units/quantity_io_pascal.hpp
index 011e3da402bd1f86b8421e223cecfee7135c2ee9..a8b8968367d1382a93f71ef9b2b9dc5338fceef5 100644
--- a/ThirdParty/phys/units/quantity_io_pascal.hpp
+++ b/corsika/framework/units/quantity_io_pascal.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_PASCAL_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_PASCAL_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_radian.hpp b/corsika/framework/units/quantity_io_radian.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_radian.hpp
rename to corsika/framework/units/quantity_io_radian.hpp
index 780e1bcea63edc83d70fff365b87e9b29df75e26..56f69f3b13ab587303b5673ef4817221fac56fbe 100644
--- a/ThirdParty/phys/units/quantity_io_radian.hpp
+++ b/corsika/framework/units/quantity_io_radian.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_RADIAN_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_RADIAN_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_second.hpp b/corsika/framework/units/quantity_io_second.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_second.hpp
rename to corsika/framework/units/quantity_io_second.hpp
index 31edd4dea8706dd3070e34b58232b577d89b3464..6f4b8dca3304b504af997c342585e354b3dda227 100644
--- a/ThirdParty/phys/units/quantity_io_second.hpp
+++ b/corsika/framework/units/quantity_io_second.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_SECOND_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_SECOND_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_siemens.hpp b/corsika/framework/units/quantity_io_siemens.hpp
similarity index 95%
rename from ThirdParty/phys/units/quantity_io_siemens.hpp
rename to corsika/framework/units/quantity_io_siemens.hpp
index 34f145de25e1995a6450dd7cb907e0e1ede8691b..13e9d4e3417ee9095c8420677ff52a23673c8664 100644
--- a/ThirdParty/phys/units/quantity_io_siemens.hpp
+++ b/corsika/framework/units/quantity_io_siemens.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_SIEMENS_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_SIEMENS_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_sievert.hpp b/corsika/framework/units/quantity_io_sievert.hpp
similarity index 95%
rename from ThirdParty/phys/units/quantity_io_sievert.hpp
rename to corsika/framework/units/quantity_io_sievert.hpp
index 971ca1559daef7cf9363235b3e3a31972aa665ae..ef7c0b9946b94440f9d260b1b734577258959a5d 100644
--- a/ThirdParty/phys/units/quantity_io_sievert.hpp
+++ b/corsika/framework/units/quantity_io_sievert.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_SIEVERT_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_SIEVERT_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_speed.hpp b/corsika/framework/units/quantity_io_speed.hpp
similarity index 95%
rename from ThirdParty/phys/units/quantity_io_speed.hpp
rename to corsika/framework/units/quantity_io_speed.hpp
index 7a6f631812cefeafe28e661482706a2925985f75..544cd27f64c40ce63ee6e3b37646a7279f23705d 100644
--- a/ThirdParty/phys/units/quantity_io_speed.hpp
+++ b/corsika/framework/units/quantity_io_speed.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_SPEED_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_SPEED_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_steradian.hpp b/corsika/framework/units/quantity_io_steradian.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_steradian.hpp
rename to corsika/framework/units/quantity_io_steradian.hpp
index 87a977e5acd94c6ea67004df40878a20fd51a4ec..037bd0d300efc34267e557138ef3c7c6c42b0886 100644
--- a/ThirdParty/phys/units/quantity_io_steradian.hpp
+++ b/corsika/framework/units/quantity_io_steradian.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_STERADIAN_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_STERADIAN_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/corsika/framework/units/quantity_io_symbols.hpp b/corsika/framework/units/quantity_io_symbols.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7b302dec0b50523932cc8cc74fed6345bef32613
--- /dev/null
+++ b/corsika/framework/units/quantity_io_symbols.hpp
@@ -0,0 +1,57 @@
+/**
+ * \file quantity_io_symbols.hpp
+ *
+ * \brief   load all available unit names and symbols.
+ * \author  Martin Moene
+ * \date    7 September 2013
+ * \since   1.0
+ *
+ * Copyright 2013 Universiteit Leiden. All rights reserved.
+ * This code is provided as-is, with no warrantee of correctness.
+ *
+ * Distributed under the Boost Software License, Version 1.0. (See accompanying
+ * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
+ */
+
+#ifndef PHYS_UNITS_QUANTITY_IO_SYMBOLS_HPP_INCLUDED
+#define PHYS_UNITS_QUANTITY_IO_SYMBOLS_HPP_INCLUDED
+
+#include "corsika/framework/units/quantity_io_ampere.hpp"
+//prefer Hertz
+//#include "corsika/framework/units/quantity_io_becquerel.hpp"
+#include "corsika/framework/units/quantity_io_candela.hpp"
+//prefer kelvin
+//#include "corsika/framework/units/quantity_io_celsius.hpp"
+#include "corsika/framework/units/quantity_io_coulomb.hpp"
+#include "corsika/framework/units/quantity_io_dimensionless.hpp"
+#include "corsika/framework/units/quantity_io_farad.hpp"
+//prefer sievert
+//#include "corsika/framework/units/quantity_io_gray.hpp"
+#include "corsika/framework/units/quantity_io_joule.hpp"
+#include "corsika/framework/units/quantity_io_henry.hpp"
+#include "corsika/framework/units/quantity_io_hertz.hpp"
+#include "corsika/framework/units/quantity_io_kelvin.hpp"
+#include "corsika/framework/units/quantity_io_kilogram.hpp"
+//prefer Cd base unit
+//#include "corsika/framework/units/quantity_io_lumen.hpp"
+#include "corsika/framework/units/quantity_io_lux.hpp"
+#include "corsika/framework/units/quantity_io_meter.hpp"
+#include "corsika/framework/units/quantity_io_newton.hpp"
+#include "corsika/framework/units/quantity_io_ohm.hpp"
+#include "corsika/framework/units/quantity_io_pascal.hpp"
+#include "corsika/framework/units/quantity_io_radian.hpp"
+#include "corsika/framework/units/quantity_io_second.hpp"
+#include "corsika/framework/units/quantity_io_siemens.hpp"
+#include "corsika/framework/units/quantity_io_sievert.hpp"
+#include "corsika/framework/units/quantity_io_speed.hpp"
+#include "corsika/framework/units/quantity_io_steradian.hpp"
+#include "corsika/framework/units/quantity_io_tesla.hpp"
+#include "corsika/framework/units/quantity_io_volt.hpp"
+#include "corsika/framework/units/quantity_io_watt.hpp"
+#include "corsika/framework/units/quantity_io_weber.hpp"
+
+#endif // PHYS_UNITS_QUANTITY_IO_SYMBOLS_HPP_INCLUDED
+
+/*
+ * end of file
+ */
diff --git a/ThirdParty/phys/units/quantity_io_tesla.hpp b/corsika/framework/units/quantity_io_tesla.hpp
similarity index 95%
rename from ThirdParty/phys/units/quantity_io_tesla.hpp
rename to corsika/framework/units/quantity_io_tesla.hpp
index fc4e651c621c0044081fc49b85c248cdbf29e570..04ea10fcd3e6fb89263ea946e34978293bfe3ad0 100644
--- a/ThirdParty/phys/units/quantity_io_tesla.hpp
+++ b/corsika/framework/units/quantity_io_tesla.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_TESLA_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_TESLA_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_volt.hpp b/corsika/framework/units/quantity_io_volt.hpp
similarity index 95%
rename from ThirdParty/phys/units/quantity_io_volt.hpp
rename to corsika/framework/units/quantity_io_volt.hpp
index baacf39074fe062925c8b69f4ae9c7793186dd76..0957b513ba5f4155f277dd63e8c54dcc21634fdc 100644
--- a/ThirdParty/phys/units/quantity_io_volt.hpp
+++ b/corsika/framework/units/quantity_io_volt.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_VOLT_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_VOLT_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_watt.hpp b/corsika/framework/units/quantity_io_watt.hpp
similarity index 94%
rename from ThirdParty/phys/units/quantity_io_watt.hpp
rename to corsika/framework/units/quantity_io_watt.hpp
index 7e616c706490e274ae413fd18a5f1f8f200394a8..8dd317e605a6f839256e240f4fd0cf58bb797270 100644
--- a/ThirdParty/phys/units/quantity_io_watt.hpp
+++ b/corsika/framework/units/quantity_io_watt.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_WATT_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_WATT_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/ThirdParty/phys/units/quantity_io_weber.hpp b/corsika/framework/units/quantity_io_weber.hpp
similarity index 95%
rename from ThirdParty/phys/units/quantity_io_weber.hpp
rename to corsika/framework/units/quantity_io_weber.hpp
index 95ab616758e31f0443c6b5891af4300893427f21..600c3f8f55d58d727d9c17502261c59f25e2f90f 100644
--- a/ThirdParty/phys/units/quantity_io_weber.hpp
+++ b/corsika/framework/units/quantity_io_weber.hpp
@@ -16,7 +16,7 @@
 #ifndef PHYS_UNITS_QUANTITY_IO_WEBER_HPP_INCLUDED
 #define PHYS_UNITS_QUANTITY_IO_WEBER_HPP_INCLUDED
 
-#include "phys/units/quantity_io.hpp"
+#include "corsika/framework/units/quantity_io.hpp"
 
 namespace phys { namespace units {
 
diff --git a/corsika/framework/utility/COMBoost.hpp b/corsika/framework/utility/COMBoost.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..63b8f0a4420f258569bebcc44a2e3f361f6ba6fe
--- /dev/null
+++ b/corsika/framework/utility/COMBoost.hpp
@@ -0,0 +1,57 @@
+/*
+ * (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 <corsika/framework/geometry/CoordinateSystem.hpp>
+#include <corsika/framework/geometry/FourVector.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <Eigen/Dense>
+
+namespace corsika {
+
+  /**
+     This utility class handles Lorentz boost between different
+     referenence frames, using FourVector.
+   */
+
+  class COMBoost {
+
+  public:
+    //! construct a COMBoost given four-vector of projectile and mass of target
+    COMBoost(FourVector<HEPEnergyType, MomentumVector> const& Pprojectile,
+             HEPEnergyType const massTarget);
+
+    //! construct a COMBoost to boost into the rest frame given a 3-momentum and mass
+    COMBoost(MomentumVector const& momentum, HEPEnergyType mass);
+
+    //! transforms a 4-momentum from lab frame to the center-of-mass frame
+    template <typename FourVector>
+    inline 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;
+
+    inline CoordinateSystemPtr getRotatedCS() const;
+
+  protected:
+    inline void setBoost(double coshEta, double sinhEta);
+
+  private:
+    Eigen::Matrix2d boost_;
+    Eigen::Matrix2d inverseBoost_;
+    CoordinateSystemPtr originalCS_;
+    CoordinateSystemPtr rotatedCS_;
+  };
+} // namespace corsika
+
+#include <corsika/detail/framework/utility/COMBoost.inl>
diff --git a/Framework/Utilities/CorsikaData.h b/corsika/framework/utility/CorsikaData.hpp
similarity index 64%
rename from Framework/Utilities/CorsikaData.h
rename to corsika/framework/utility/CorsikaData.hpp
index ed635f806c7cc5d8b6ad872c2cab28243183d091..d117db8da59e3399e77ff7304335c7e9eaa21711 100644
--- a/Framework/Utilities/CorsikaData.h
+++ b/corsika/framework/utility/CorsikaData.hpp
@@ -6,16 +6,15 @@
  * the license.
  */
 
-#ifndef CORSIKA_CORSIKADATA_H
-#define CORSIKA_CORSIKADATA_H
+#pragma once
 
 #include <string>
 
-namespace corsika::utl {
+namespace corsika {
   /**
    * returns the full path of the file \p filename within the CORSIKA_DATA directory
    */
-  std::string CorsikaData(std::string const& filename);
-} // namespace corsika::utl
+  std::string corsika_data(std::string const& filename);
+} // namespace corsika
 
-#endif // CORSIKA_CORSIKADATA_H
+#include <corsika/detail/framework/utility/CorsikaData.inl>
diff --git a/corsika/framework/utility/CorsikaFenv.hpp b/corsika/framework/utility/CorsikaFenv.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..858f3e7e37c3cb47ec1bcadb1e99dde6fe6a9d45
--- /dev/null
+++ b/corsika/framework/utility/CorsikaFenv.hpp
@@ -0,0 +1,31 @@
+/*
+ * (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 <cfenv>
+
+/*
+ * Same declaration of function as provided in GLIBC
+ * Repetition allowed in the case where cfenv defines the functions already, no clash.
+ */
+extern "C" {
+
+int feenableexcept(int excepts) noexcept;
+int fedisableexcept(int excepts) noexcept;
+}
+
+#ifdef CORSIKA_HAS_FEENABLEEXCEPT
+// Nothing to do, OS privides the functions
+#else
+#ifdef CORSIKA_OS_MAC
+#include <corsika/detail/framework/utility/CorsikaFenvOSX.inl>
+#else
+#include <corsika/detail/framework/utility/CorsikaFenvFallback.inl>
+#endif
+#endif
diff --git a/corsika/framework/utility/QuarticSolver.hpp b/corsika/framework/utility/QuarticSolver.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0bcdfcedf1a9a9055306acacfd756012726cf8b0
--- /dev/null
+++ b/corsika/framework/utility/QuarticSolver.hpp
@@ -0,0 +1,61 @@
+/*
+ * (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 <complex>
+
+/**
+ * \todo convert to class
+ */
+
+namespace corsika::quartic_solver {
+
+  const double PI = 3.141592653589793238463L;
+  const double M_2PI = 2 * PI;
+  const double eps = 1e-12;
+
+  typedef std::complex<double> DComplex;
+
+  //---------------------------------------------------------------------------
+  // useful for testing
+  inline DComplex polinom_2(DComplex x, double a, double b) {
+    // Horner's scheme for x*x + a*x + b
+    return x * (x + a) + b;
+  }
+
+  //---------------------------------------------------------------------------
+  // useful for testing
+  inline DComplex polinom_3(DComplex x, double a, double b, double c) {
+    // Horner's scheme for x*x*x + a*x*x + b*x + c;
+    return x * (x * (x + a) + b) + c;
+  }
+
+  //---------------------------------------------------------------------------
+  // useful for testing
+  inline DComplex polinom_4(DComplex x, double a, double b, double c, double d) {
+    // Horner's scheme for x*x*x*x + a*x*x*x + b*x*x + c*x + d;
+    return x * (x * (x * (x + a) + b) + c) + d;
+  }
+
+  //---------------------------------------------------------------------------
+  // x - array of size 3
+  // In case 3 real roots: => x[0], x[1], x[2], return 3
+  //         2 real roots: x[0], x[1],          return 2
+  //         1 real root : x[0], x[1] ± i*x[2], return 1
+  unsigned int solveP3(double* x, double a, double b, double c);
+
+  //---------------------------------------------------------------------------
+  // solve quartic equation x^4 + a*x^3 + b*x^2 + c*x + d
+  // Attention - this function returns dynamically allocated array. It has to be released
+  // afterwards.
+  DComplex* solve_quartic(double a, double b, double c, double d);
+
+} // namespace corsika::quartic_solver
+
+#include <corsika/detail/framework/utility/QuarticSolver.inl>
diff --git a/corsika/framework/utility/SaveBoostHistogram.hpp b/corsika/framework/utility/SaveBoostHistogram.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..13a079410e7c4735e88eb0ec56f5346f96ad2407
--- /dev/null
+++ b/corsika/framework/utility/SaveBoostHistogram.hpp
@@ -0,0 +1,30 @@
+/*
+ * (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 <boost/histogram.hpp>
+
+namespace corsika {
+
+  enum class SaveMode { overwrite, append };
+
+  /**
+   * This functions saves a boost::histogram into a numpy file. Only rather basic axis
+   * types are supported: regular, variable, integer, category<int>. Only "ordinary" bin
+   * counts (i.e. a double or int) are supported, nothing fancy like profiles.
+   *
+   * Note that this function makes a temporary, dense copy of the histogram, which could
+   * be an issue for huge sizes (e.g. for high dimensions)
+   */
+  template <class Axes, class Storage>
+  inline void save_hist(boost::histogram::histogram<Axes, Storage> const& h,
+                        std::string const& filename, SaveMode mode = SaveMode::append);
+} // namespace corsika
+
+#include <corsika/detail/framework/utility/SaveBoostHistogram.inl>
diff --git a/Framework/Utilities/Singleton.h b/corsika/framework/utility/Singleton.hpp
similarity index 71%
rename from Framework/Utilities/Singleton.h
rename to corsika/framework/utility/Singleton.hpp
index 68667df8d82a0090639b528daac1f19bf392c4ce..78a7df97b6c0aa3c221a5404eaa68dcfde6afce6 100644
--- a/Framework/Utilities/Singleton.h
+++ b/corsika/framework/utility/Singleton.hpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
@@ -10,7 +10,7 @@
 
 //#define OFFLINE_USE_GAMMA_SINGLETON
 
-namespace corsika::utl {
+namespace corsika {
   /**
    * \class Singleton Singleton.h utl/Singleton.h
    *
@@ -18,7 +18,7 @@ namespace corsika::utl {
    *
    * The singleton class is implemented as follows
    * \code
-   * #include <utl/Singleton.h>
+   * #include <utl/Singleton.hpp>
    *
    * class SomeClass : public utl::Singleton<SomeClass> {
    *   ...
@@ -41,13 +41,16 @@ namespace corsika::utl {
   template <typename T>
   class Singleton {
   public:
-    static T& GetInstance() {
+    static T& getInstance() {
       static T instance;
       return instance;
     }
 
-    Singleton(const Singleton&) = delete;
-    Singleton& operator=(const Singleton&) = delete;
+    Singleton(const Singleton&) =
+        delete; // Singleton Classes should not be copied. Removes move constructor and
+                // move assignment as well
+    Singleton& operator=(const Singleton&) =
+        delete; // Singleton Classes should not be copied.
 
   protected:
     // derived class can call ctor and dtor
@@ -55,4 +58,4 @@ namespace corsika::utl {
     ~Singleton() {}
   };
 
-} // namespace corsika::utl
+} // namespace corsika
diff --git a/corsika/framework/utility/detail/COMBoost.inl b/corsika/framework/utility/detail/COMBoost.inl
new file mode 100644
index 0000000000000000000000000000000000000000..aa6af48349ed06df019e709eaadf2d8693b56289
--- /dev/null
+++ b/corsika/framework/utility/detail/COMBoost.inl
@@ -0,0 +1,125 @@
+/*
+ * (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/Framework/Utilities/CorsikaFenvFallback.cc b/corsika/framework/utility/detail/CorsikaFenvFallback.inl
similarity index 89%
rename from Framework/Utilities/CorsikaFenvFallback.cc
rename to corsika/framework/utility/detail/CorsikaFenvFallback.inl
index 64327b2c7b7500b6925a86a4ec8997aebeec6f15..ba847a3a04dd63dc980b95148113f440e8d70aaf 100644
--- a/Framework/Utilities/CorsikaFenvFallback.cc
+++ b/corsika/framework/utility/detail/CorsikaFenvFallback.inl
@@ -6,7 +6,7 @@
  * the license.
  */
 
-#include <corsika/utl/CorsikaFenv.h>
+#include <corsika/framework/utility/CorsikaFenv.hpp>
 #include <cfenv>
 
 extern "C" {
diff --git a/corsika/media/BaseExponential.hpp b/corsika/media/BaseExponential.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c992d7807d9f94a1a64f9a68d880ba35092b9f03
--- /dev/null
+++ b/corsika/media/BaseExponential.hpp
@@ -0,0 +1,85 @@
+/*
+ * (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 <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+#include <limits>
+
+namespace corsika {
+
+  /**
+   * This class provides the grammage/length conversion functionality for
+   * (locally) flat exponential atmospheres.
+   */
+  template <typename TDerived>
+  class BaseExponential {
+  protected:
+    auto const& getImplementation() const;
+
+    // clang-format off
+    /**
+     * For a (normalized) axis \f$ \vec{a} \f$, the grammage along a non-orthogonal line with (normalized)
+     * direction \f$ \vec{u} \f$ is given by
+     * \f[
+     *   X = \frac{\varrho_0 \lambda}{\vec{u} \cdot \vec{a}} \left( \exp\left( \vec{u} \cdot \vec{a} \frac{l}{\lambda} \right) - 1 \right)
+     * \f], where \f$ \varrho_0 \f$ is the density at the starting point.
+     *
+     * If \f$ \vec{u} \cdot \vec{a} = 0 \f$, the calculation is just like with a homogeneous density:
+     * \f[
+     *   X = \varrho_0 l;
+     * \f]
+     */
+    // clang-format on
+    GrammageType getIntegratedGrammage(setup::Trajectory const& line, LengthType vL,
+                                       DirectionVector const& axis) const;
+
+    // clang-format off
+    /**
+     * For a (normalized) axis \f$ \vec{a} \f$, the length of a non-orthogonal line with (normalized)
+     * direction \f$ \vec{u} \f$ corresponding to grammage \f$ X \f$ is given by
+     * \f[
+     *   l = \begin{cases}
+     *   \frac{\lambda}{\vec{u} \cdot \vec{a}} \log\left(Y \right), & \text{if} Y :=  0 > 1 +
+     *     \vec{u} \cdot \vec{a} \frac{X}{\rho_0 \lambda}
+     *   \infty & \text{else,}
+     *   \end{cases}
+     * \f] where \f$ \varrho_0 \f$ is the density at the starting point.
+     *
+     * If \f$ \vec{u} \cdot \vec{a} = 0 \f$, the calculation is just like with a homogeneous density:
+     * \f[
+     *   l =  \frac{X}{\varrho_0}
+     * \f]
+     */
+    // clang-format on
+    LengthType getArclengthFromGrammage(setup::Trajectory const& line,
+                                        GrammageType grammage,
+                                        DirectionVector const& axis) const;
+
+  public:
+    BaseExponential(Point const& point, MassDensityType rho0, LengthType lambda);
+
+    Point const& getAnchorPoint() const { return point_; }
+    MassDensityType getRho0() const { return rho0_; }
+    InverseLengthType getInvLambda() const { return invLambda_; }
+
+  private:
+    MassDensityType const rho0_;
+    LengthType const lambda_;
+    InverseLengthType const invLambda_;
+    Point const point_;
+
+  }; // class BaseExponential
+
+} // namespace corsika
+
+#include <corsika/detail/media/BaseExponential.inl>
diff --git a/Environment/DensityFunction.h b/corsika/media/DensityFunction.hpp
similarity index 53%
rename from Environment/DensityFunction.h
rename to corsika/media/DensityFunction.hpp
index 3ac33c3f21ebc0a8fbe792d9641b3a37c4dec7c4..6242b3d787ec507a0ac7db7a2e3e0d3b225d6813 100644
--- a/Environment/DensityFunction.h
+++ b/corsika/media/DensityFunction.hpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
@@ -8,11 +8,11 @@
 
 #pragma once
 
-#include <corsika/environment/LinearApproximationIntegrator.h>
-#include <corsika/geometry/Line.h>
-#include <corsika/geometry/Point.h>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/media/LinearApproximationIntegrator.hpp>
 
-namespace corsika::environment {
+namespace corsika {
 
   template <class TDerivableRho,
             template <typename> class TIntegrator = LinearApproximationIntegrator>
@@ -20,15 +20,13 @@ namespace corsika::environment {
       : public TIntegrator<DensityFunction<TDerivableRho, TIntegrator>> {
     friend class TIntegrator<DensityFunction<TDerivableRho, TIntegrator>>;
 
-    TDerivableRho fRho; //!< functor for density
+    TDerivableRho rho_; //!< functor for density
 
   public:
     DensityFunction(TDerivableRho rho)
-        : fRho(rho) {}
+        : rho_(rho) {}
 
-    corsika::units::si::MassDensityType EvaluateAt(
-        corsika::geometry::Point const& p) const {
-      return fRho(p);
-    }
+    MassDensityType evaluateAt(Point const& p) const { return rho_(p); }
   };
-} // namespace corsika::environment
+
+} // namespace corsika
diff --git a/corsika/media/Environment.hpp b/corsika/media/Environment.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a52aeaebe3c33548f52db8aeddcc096940ff9307
--- /dev/null
+++ b/corsika/media/Environment.hpp
@@ -0,0 +1,67 @@
+/*
+ * (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 <corsika/media/IMediumModel.hpp>
+#include <corsika/media/VolumeTreeNode.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+
+#include <corsika/media/Universe.hpp>
+
+#include <limits>
+
+namespace corsika {
+
+  /** Base Evnironment class
+   *  Describes the Environment in which the shower is propagated
+   **/
+  template <typename IEnvironmentModel>
+  class Environment {
+  public:
+    using BaseNodeType = VolumeTreeNode<IEnvironmentModel>;
+
+    Environment();
+
+    /** Getters for the universe stored in the Environment
+     *
+     * @retval Retuns reference to a Universe object with infinite size
+     **/
+    ///@{
+    //* Get non const universe */
+    typename BaseNodeType::VTNUPtr& getUniverse();
+    //* Get const universe */
+    typename BaseNodeType::VTNUPtr const& getUniverse() const;
+    ///@}
+
+    /** Getter for the CoordinateSystem used in the Environment
+     *
+     * @retval Retuns a const reference to the CoordinateSystem used
+     **/
+    CoordinateSystemPtr const& getCoordinateSystem() const;
+
+    /** Factory method for creation of VolumeTreeNodes
+     * @tparam TVolumeType Type of volume to be created
+     * @tparam TVolumeArgs Types to forward to the constructor
+     * @param args Parameter forwarded to the constructor of TVolumeType
+     * @retval Retuns unique pointer to a VolumeTreeNode with the same EnvitonmentModel as
+     *this class
+     **/
+    template <class TVolumeType, typename... TVolumeArgs>
+    static std::unique_ptr<BaseNodeType> createNode(TVolumeArgs&&... args);
+
+  private:
+    CoordinateSystemPtr const coordinateSystem_;
+    typename BaseNodeType::VTNUPtr universe_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/media/Environment.inl>
diff --git a/corsika/media/FlatExponential.hpp b/corsika/media/FlatExponential.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a33c76496fb89d80b9a0e7a5747b6578bb5c9698
--- /dev/null
+++ b/corsika/media/FlatExponential.hpp
@@ -0,0 +1,57 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/media/BaseExponential.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+namespace corsika {
+
+  // clang-format off
+  /**
+   * flat exponential density distribution with
+   * \f[
+   *  \varrho(r) = \varrho_0 \exp\left( \frac{1}{\lambda} (r - p) \cdot
+   *    \vec{a} \right).
+   * \f]
+   * \f$ \vec{a} \f$ denotes the axis and should be normalized to avoid degeneracy
+   * with the scale parameter \f$ \lambda \f$.
+   */
+  // clang-format on
+  template <typename T>
+  class FlatExponential : public BaseExponential<FlatExponential<T>>, public T {
+    using base_type = BaseExponential<FlatExponential<T>>;
+
+  public:
+    FlatExponential(Point const& point, Vector<dimensionless_d> const& axis,
+                    MassDensityType rho, LengthType lambda,
+                    NuclearComposition const& nuclComp);
+
+    MassDensityType getMassDensity(Point const& point) const override;
+
+    NuclearComposition const& getNuclearComposition() const override;
+
+    GrammageType getIntegratedGrammage(setup::Trajectory const& line,
+                                       LengthType to) const override;
+
+    LengthType getArclengthFromGrammage(setup::Trajectory const& line,
+                                        GrammageType grammage) const override;
+
+  private:
+    DirectionVector const axis_;
+    NuclearComposition const nuclComp_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/media/FlatExponential.inl>
diff --git a/corsika/media/HomogeneousMedium.hpp b/corsika/media/HomogeneousMedium.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a659be2288aef59492c9ab84922e6495c01a46d6
--- /dev/null
+++ b/corsika/media/HomogeneousMedium.hpp
@@ -0,0 +1,47 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+
+#include <corsika/setup/SetupTrajectory.hpp>
+
+/**
+ * a homogeneous medium
+ */
+
+namespace corsika {
+
+  template <typename T>
+  class HomogeneousMedium : public T {
+
+  public:
+    HomogeneousMedium(MassDensityType density, NuclearComposition const& nuclComp);
+
+    MassDensityType getMassDensity(Point const&) const override;
+
+    NuclearComposition const& getNuclearComposition() const override;
+
+    GrammageType getIntegratedGrammage(setup::Trajectory const&,
+                                       LengthType to) const override;
+
+    LengthType getArclengthFromGrammage(setup::Trajectory const&,
+                                        GrammageType grammage) const override;
+
+  private:
+    MassDensityType const density_;
+    NuclearComposition const nuclComp_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/media/HomogeneousMedium.inl>
diff --git a/Environment/IEmpty.hpp b/corsika/media/IEmpty.hpp
similarity index 52%
rename from Environment/IEmpty.hpp
rename to corsika/media/IEmpty.hpp
index 6d45cd03c271868118d02d744fb4d6091e31cedf..1912f90fc5e09ce403ef9c4e33c163e63f96309f 100644
--- a/Environment/IEmpty.hpp
+++ b/corsika/media/IEmpty.hpp
@@ -8,15 +8,13 @@
 
 #pragma once
 
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/geometry/Trajectory.h>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
 
-namespace corsika::environment {
+namespace corsika {
 
   /**
-   * \class IEmpty
-   *
-   * intended for usage as default template argument for environments
+   * Intended for usage as default template argument for environments
    * with no properties.  For now, the ArclengthFromGrammage is
    * mandatory, since it is used even in the most simple Cascade code.
    *
@@ -27,9 +25,8 @@ namespace corsika::environment {
 
   class IEmpty {
   public:
-    virtual corsika::units::si::LengthType ArclengthFromGrammage(
-        corsika::geometry::LineTrajectory const&,
-        corsika::units::si::GrammageType) const = 0;
+    virtual LengthType getArclengthFromGrammage(setup::Trajectory const&,
+                                                GrammageType) const = 0;
 
     virtual ~IEmpty() {}
   };
@@ -37,11 +34,9 @@ namespace corsika::environment {
   template <typename TModel = IEmpty>
   class Empty : public TModel {
   public:
-    corsika::units::si::LengthType ArclengthFromGrammage(
-        corsika::geometry::LineTrajectory const&,
-        corsika::units::si::GrammageType) const {
-      return 0. * corsika::units::si::meter;
+    LengthType getArclengthFromGrammage(setup::Trajectory const&, GrammageType) const {
+      return 0. * meter;
     }
   };
 
-} // namespace corsika::environment
+} // namespace corsika
diff --git a/Environment/IMagneticFieldModel.h b/corsika/media/IMagneticFieldModel.hpp
similarity index 68%
rename from Environment/IMagneticFieldModel.h
rename to corsika/media/IMagneticFieldModel.hpp
index d27eda38ca03d1c0c6a35c6318e7dda7bb32da46..3efd34546e8a34285f8fb4b51defba93ec86b761 100644
--- a/Environment/IMagneticFieldModel.h
+++ b/corsika/media/IMagneticFieldModel.hpp
@@ -8,11 +8,11 @@
 
 #pragma once
 
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
 
-namespace corsika::environment {
+namespace corsika {
 
   /**
    * An interface for magnetic field models.
@@ -24,8 +24,7 @@ namespace corsika::environment {
   class IMagneticFieldModel : public Model {
 
     // a type-alias for a magnetic field vector
-    using MagneticFieldVector =
-        corsika::geometry::Vector<corsika::units::si::magnetic_flux_density_d>;
+    using MagneticFieldVector = Vector<magnetic_flux_density_d>;
 
   public:
     /**
@@ -34,8 +33,7 @@ namespace corsika::environment {
      * @param  point    The location to evaluate the field at.
      * @returns    The magnetic field vector at that point.
      */
-    virtual auto GetMagneticField(corsika::geometry::Point const&) const
-        -> MagneticFieldVector = 0;
+    virtual auto getMagneticField(Point const&) const -> MagneticFieldVector = 0;
 
     /**
      * A virtual default destructor.
@@ -44,4 +42,4 @@ namespace corsika::environment {
 
   }; // END: class MagneticField
 
-} // namespace corsika::environment
+} // namespace corsika
diff --git a/corsika/media/IMediumModel.hpp b/corsika/media/IMediumModel.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2dcacffbbf1cbfc80e634eea8a171e95b5bf31c6
--- /dev/null
+++ b/corsika/media/IMediumModel.hpp
@@ -0,0 +1,35 @@
+/*
+ * (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/media/NuclearComposition.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+namespace corsika {
+
+  class IMediumModel {
+  public:
+    virtual ~IMediumModel() = default; // LCOV_EXCL_LINE
+
+    virtual MassDensityType getMassDensity(Point const&) const = 0;
+
+    // todo: think about the mixin inheritance of the trajectory vs the BaseTrajectory
+    // approach; for now, only lines are supported
+    virtual GrammageType getIntegratedGrammage(setup::Trajectory const&,
+                                               LengthType) const = 0;
+
+    virtual LengthType getArclengthFromGrammage(setup::Trajectory const&,
+                                                GrammageType) const = 0;
+
+    virtual NuclearComposition const& getNuclearComposition() const = 0;
+  };
+
+} // namespace corsika
diff --git a/Environment/IMediumPropertyModel.h b/corsika/media/IMediumPropertyModel.hpp
similarity index 68%
rename from Environment/IMediumPropertyModel.h
rename to corsika/media/IMediumPropertyModel.hpp
index 759517af71ce73a95bb9fedecf19cbdf4b0ac6ab..4455a6242be0ecf5a5098ed19da7685879597565 100644
--- a/Environment/IMediumPropertyModel.h
+++ b/corsika/media/IMediumPropertyModel.hpp
@@ -8,12 +8,12 @@
 
 #pragma once
 
-#include <corsika/environment/MediumProperties.h>
+#include <corsika/media/MediumProperties.hpp>
 
-#include <corsika/geometry/Point.h>
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
 
-namespace corsika::environment {
+namespace corsika {
 
   /**
    * An interface for type of media, needed e.g. to determine energy losses
@@ -21,8 +21,8 @@ namespace corsika::environment {
    * This is the base interface for media types.
    *
    */
-  template <typename Model>
-  class IMediumPropertyModel : public Model {
+  template <typename TModel>
+  class IMediumPropertyModel : public TModel {
 
   public:
     /**
@@ -31,7 +31,7 @@ namespace corsika::environment {
      * @param  point    The location to evaluate at.
      * @returns    The media type
      */
-    virtual Medium medium(corsika::geometry::Point const&) const = 0;
+    virtual Medium getMedium(Point const&) const = 0;
 
     /**
      * A virtual default destructor.
@@ -40,4 +40,4 @@ namespace corsika::environment {
 
   }; // END: class IMediumTypeModel
 
-} // namespace corsika::environment
+} // namespace corsika
diff --git a/Environment/IRefractiveIndexModel.h b/corsika/media/IRefractiveIndexModel.hpp
similarity index 70%
rename from Environment/IRefractiveIndexModel.h
rename to corsika/media/IRefractiveIndexModel.hpp
index 29037aca77256377ead6c8e79ce3e9a2652fa415..b042540fb3fba6e58b639467b050e9478f693ab8 100644
--- a/Environment/IRefractiveIndexModel.h
+++ b/corsika/media/IRefractiveIndexModel.hpp
@@ -8,10 +8,9 @@
 
 #pragma once
 
-#include <corsika/geometry/Point.h>
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/framework/geometry/Point.hpp>
 
-namespace corsika::environment {
+namespace corsika {
 
   /**
    * An interface for refractive index models.
@@ -19,8 +18,8 @@ namespace corsika::environment {
    * This is the base interface for refractive index mixins.
    *
    */
-  template <typename Model>
-  class IRefractiveIndexModel : public Model {
+  template <typename TModel>
+  class IRefractiveIndexModel : public TModel {
 
   public:
     /**
@@ -29,7 +28,7 @@ namespace corsika::environment {
      * @param  point    The location to evaluate at.
      * @returns    The refractive index at this point.
      */
-    virtual double GetRefractiveIndex(corsika::geometry::Point const&) const = 0;
+    virtual double getRefractiveIndex(Point const&) const = 0;
 
     /**
      * A virtual default destructor.
@@ -38,4 +37,4 @@ namespace corsika::environment {
 
   }; // END: class IRefractiveIndexModel
 
-} // namespace corsika::environment
+} // namespace corsika
diff --git a/corsika/media/InhomogeneousMedium.hpp b/corsika/media/InhomogeneousMedium.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7c9f664aaf589a311de7207d79a214125c8cdd15
--- /dev/null
+++ b/corsika/media/InhomogeneousMedium.hpp
@@ -0,0 +1,48 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+/**
+ * A general inhomogeneous medium. The mass density distribution TDensityFunction must be
+ * a \f$C^2\f$-function.
+ */
+
+namespace corsika {
+
+  template <typename T, typename TDensityFunction>
+  class InhomogeneousMedium : public T {
+
+  public:
+    template <typename... TArgs>
+    InhomogeneousMedium(NuclearComposition const& nuclComp, TArgs&&... rhoTArgs);
+
+    MassDensityType getMassDensity(Point const& point) const override;
+
+    NuclearComposition const& getNuclearComposition() const override;
+
+    GrammageType getIntegratedGrammage(setup::Trajectory const& line,
+                                       LengthType to) const override;
+
+    LengthType getArclengthFromGrammage(setup::Trajectory const& pLine,
+                                        GrammageType grammage) const override;
+
+  private:
+    NuclearComposition const nuclComp_;
+    TDensityFunction const densityFunction_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/media/InhomogeneousMedium.inl>
diff --git a/corsika/media/LayeredSphericalAtmosphereBuilder.hpp b/corsika/media/LayeredSphericalAtmosphereBuilder.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5de160d136e54f61537d9c49943cc863137bb7dd
--- /dev/null
+++ b/corsika/media/LayeredSphericalAtmosphereBuilder.hpp
@@ -0,0 +1,107 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/media/VolumeTreeNode.hpp>
+
+// for detail namespace, NoExtraModelInner, NoExtraModel and traits
+#include <corsika/detail/media/LayeredSphericalAtmosphereBuilder.hpp>
+
+#include <memory>
+#include <stack>
+#include <tuple>
+#include <type_traits>
+
+namespace corsika {
+
+  /**
+   * \class make_layered_spherical_atmosphere_builder
+   *
+   * Helper class to create LayeredSphericalAtmosphereBuilder, the
+   * extra environment models have to be passed as template-template
+   * argument to make_layered_spherical_atmosphere_builder, the member
+   * function `create` does then take an unspecified number of extra
+   * parameters to internalize those models for all layers later
+   * produced.
+   **/
+  template <typename TMediumInterface = IMediumModel,
+            template <typename> typename MExtraEnvirnoment = detail::NoExtraModel>
+  struct make_layered_spherical_atmosphere_builder;
+
+  /**
+   * Helper class to setup concentric spheres of layered atmosphere
+   * with spcified density profiles (exponential, linear, ...).
+   *
+   * This can be used most importantly to replicate CORSIKA7
+   * atmospheres.
+   *
+   * Each layer by definition has a density profile and a (constant)
+   * nuclear composition model.
+   *
+   */
+
+  template <typename TMediumInterface = IMediumModel,
+            template <typename> typename TMediumModelExtra = detail::NoExtraModel,
+            typename... TModelArgs>
+  class LayeredSphericalAtmosphereBuilder {
+
+    LayeredSphericalAtmosphereBuilder() = delete;
+    LayeredSphericalAtmosphereBuilder(const LayeredSphericalAtmosphereBuilder&) = delete;
+    LayeredSphericalAtmosphereBuilder(const LayeredSphericalAtmosphereBuilder&&) = delete;
+    LayeredSphericalAtmosphereBuilder& operator=(
+        const LayeredSphericalAtmosphereBuilder&) = delete;
+
+    // friend, to allow construction
+    template <typename, template <typename> typename>
+    friend struct make_layered_spherical_atmosphere_builder;
+
+  protected:
+    LayeredSphericalAtmosphereBuilder(TModelArgs... args, Point const& center,
+                                      LengthType earthRadius)
+        : center_(center)
+        , earthRadius_(earthRadius)
+        , additionalModelArgs_{args...} {}
+
+  public:
+    void setNuclearComposition(NuclearComposition const& composition);
+    void addExponentialLayer(GrammageType b, LengthType c, LengthType upperBoundary);
+    void addLinearLayer(LengthType c, LengthType upperBoundary);
+
+    int getSize() const { return layers_.size(); }
+
+    void assemble(Environment<TMediumInterface>& env);
+    Environment<TMediumInterface> assemble();
+
+    /**
+     * Get the current Earth radius.
+     */
+    LengthType getEarthRadius() const { return earthRadius_; }
+
+  private:
+    void checkRadius(LengthType r) const;
+
+    std::unique_ptr<NuclearComposition> composition_;
+    Point center_;
+    LengthType previousRadius_{LengthType::zero()};
+    LengthType earthRadius_;
+    std::tuple<TModelArgs...> const additionalModelArgs_;
+
+    std::stack<typename VolumeTreeNode<TMediumInterface>::VTNUPtr>
+        layers_; // innermost layer first
+
+  }; // end class LayeredSphericalAtmosphereBuilder
+
+} // namespace corsika
+
+#include <corsika/detail/media/LayeredSphericalAtmosphereBuilder.inl>
diff --git a/corsika/media/LinearApproximationIntegrator.hpp b/corsika/media/LinearApproximationIntegrator.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..dfd9f0037476ea9d4e8dc8ed5ae3be6acccf8943
--- /dev/null
+++ b/corsika/media/LinearApproximationIntegrator.hpp
@@ -0,0 +1,34 @@
+/*
+ * (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 <limits>
+
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+namespace corsika {
+
+  template <typename TDerived>
+  class LinearApproximationIntegrator {
+    auto const& getImplementation() const;
+
+  public:
+    auto getIntegrateGrammage(setup::Trajectory const& line, LengthType length) const;
+
+    auto getArclengthFromGrammage(setup::Trajectory const& line,
+                                  GrammageType grammage) const;
+
+    auto getMaximumLength(setup::Trajectory const& line,
+                          [[maybe_unused]] double relError) const;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/media/LinearApproximationIntegrator.inl>
diff --git a/Environment/MediumProperties.h b/corsika/media/MediumProperties.hpp
similarity index 53%
rename from Environment/MediumProperties.h
rename to corsika/media/MediumProperties.hpp
index 969f34c0c51734e96e75a1c8a218269789b3afdc..76434f1f710e3799b5fc74e25ad83334015ac834 100644
--- a/Environment/MediumProperties.h
+++ b/corsika/media/MediumProperties.hpp
@@ -8,7 +8,7 @@
 
 #pragma once
 
-namespace corsika::environment {
+namespace corsika {
 
   /**
    * Medium types are useful most importantly for effective models
@@ -33,7 +33,7 @@ namespace corsika::environment {
   enum class Medium : int16_t;
   using MediumIntType = std::underlying_type<Medium>::type;
 
-  /**
+  /** \todo documentation needs update ...
    * \struct MediumData
    *
    * Simple object to group together a number of properties
@@ -59,34 +59,34 @@ namespace corsika::environment {
     double sk_;
     double dlt0_;
 
-    std::string name() const { return name_; }
-    std::string pretty_name() const { return pretty_name_; }
-    double weight() const { return weight_; }
-    int weight_significant_figure() const { return weight_significant_figure_; }
-    int weight_error_last_digit() const { return weight_error_last_digit_; }
-    double Z_over_A() const { return Z_over_A_; }
-    double sternheimer_density() const { return sternheimer_density_; }
-    double corrected_density() const { return corrected_density_; }
-    State state() const { return state_; }
-    MediumType type() const { return type_; }
-    std::string symbol() const { return symbol_; }
-    double Ieff() const { return Ieff_; }
-    double Cbar() const { return Cbar_; }
-    double x0() const { return x0_; }
-    double x1() const { return x1_; }
-    double aa() const { return aa_; }
-    double sk() const { return sk_; }
-    double dlt0() const { return dlt0_; }
+    std::string getName() const { return name_; }
+    std::string getPrettyName() const { return pretty_name_; }
+    double getWeight() const { return weight_; }
+    const int& weight_significant_figure() const { return weight_significant_figure_; }
+    const int& weight_error_last_digit() const { return weight_error_last_digit_; }
+    const double& Z_over_A() const { return Z_over_A_; }
+    double getSternheimerDensity() const { return sternheimer_density_; }
+    double getCorrectedDensity() const { return corrected_density_; }
+    State getState() const { return state_; }
+    MediumType getType() const { return type_; }
+    std::string getSymbol() const { return symbol_; }
+    double getIeff() const { return Ieff_; }
+    double getCbar() const { return Cbar_; }
+    double getX0() const { return x0_; }
+    double getX1() const { return x1_; }
+    double getAA() const { return aa_; }
+    double getSK() const { return sk_; }
+    double getDlt0() const { return dlt0_; }
   };
 
-} // namespace corsika::environment
+} // namespace corsika
 
-#include <corsika/environment/GeneratedMediaProperties.inc>
+#include <corsika/media/GeneratedMediaProperties.inc>
 
-namespace corsika::environment {
+namespace corsika {
 
   constexpr MediumData const& mediumData(Medium const m) {
-    return detail::medium_data[static_cast<MediumIntType>(m)];
+    return corsika::detail::medium_data[static_cast<MediumIntType>(m)];
   }
 
-} // namespace corsika::environment
+} // namespace corsika
diff --git a/corsika/media/MediumPropertyModel.hpp b/corsika/media/MediumPropertyModel.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7833599c63b9806f39b19e581b1846bc5f2b58ec
--- /dev/null
+++ b/corsika/media/MediumPropertyModel.hpp
@@ -0,0 +1,53 @@
+/*
+ * (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 <corsika/media/IMediumPropertyModel.hpp>
+
+namespace corsika {
+
+  /**
+   * A model for the energy loss property of a medium.
+   *
+   */
+  template <typename T>
+  class MediumPropertyModel : public T {
+
+    Medium medium_; ///< The medium that is used for this medium.
+
+  public:
+    /**
+     * Construct a MediumPropertyModel.
+     *
+     * @param field    The refractive index to return everywhere.
+     */
+    template <typename... Args>
+    MediumPropertyModel(Medium const medium, Args&&... args);
+
+    /**
+     * Evaluate the medium type at a given location.
+     *
+     * @param  point    The location to evaluate at.
+     * @returns    The medium type as enum environment::Medium
+     */
+    Medium getMedium(Point const&) const override;
+
+    /**
+     * Set the medium type.
+     *
+     * @param  medium    The medium to store.
+     * @returns    The medium type as enum environment::Medium
+     */
+    void setMedium(Medium const medium);
+
+  }; // END: class MediumPropertyModel
+
+} // namespace corsika
+
+#include <corsika/detail/media/MediumPropertyModel.inl>
diff --git a/Environment/NameModel.h b/corsika/media/NameModel.hpp
similarity index 70%
rename from Environment/NameModel.h
rename to corsika/media/NameModel.hpp
index d5b6ea4fe42d5dd69cd5bd9c56bb5721f09b06a8..8fe9698ed0bf9ca9684e03491b5ecb9d5122c5d9 100644
--- a/Environment/NameModel.h
+++ b/corsika/media/NameModel.hpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
@@ -9,9 +9,9 @@
 #pragma once
 
 #include <string>
-#include <utility>
+//#include <utility>
 
-namespace corsika::environment {
+namespace corsika {
 
   template <typename T>
   struct NameModel : public T {
@@ -19,4 +19,4 @@ namespace corsika::environment {
     virtual ~NameModel() = default;
   };
 
-} // namespace corsika::environment
+} // namespace corsika
diff --git a/corsika/media/NuclearComposition.hpp b/corsika/media/NuclearComposition.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f8f08704d3bc1f97deba819c886d6d2abd4b5ca9
--- /dev/null
+++ b/corsika/media/NuclearComposition.hpp
@@ -0,0 +1,83 @@
+/*
+ * (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 <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <cassert>
+#include <functional>
+#include <numeric>
+#include <random>
+#include <stdexcept>
+#include <vector>
+
+namespace corsika {
+
+  /** Describes the composition of matter
+   *  Allowes and handles the creation of custom matter compositions
+   **/
+  class NuclearComposition {
+  public:
+    /** Constructor
+     *  The constructore takes a list of elements and a list which describe the relative
+     *  amount. Booth lists need to have the same length and the sum all of fractions
+     *  should be 1. Otherwise an exception is thrown
+     *  @param pComponents List of particle types
+     *  @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);
+
+    /** Sum all all relative composition weighted by func(element)
+     *  This function sums all relative compositions given during this classes
+     *construction. Each entry is weighted by the user defined function func given to this
+     *function.
+     *  @tparam TFunction Type of functions for the weights. The type should be
+     *          Code -> float
+     *  @param func Functions for reweighting specific elements
+     *  @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;
+
+    /** Number of elements in the composition array
+     *  @retval returns the number of elements in the composition array
+     **/
+    inline size_t getSize() const;
+
+    /// Returns a const reference to the fraction
+    inline 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;
+
+    template <class TRNG>
+    inline 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;
+
+  private:
+    inline void updateHash();
+
+    std::vector<float> const numberFractions_; //!< relative fractions of number density
+    std::vector<Code> const components_;       //!< particle codes of consitutents
+
+    double const avgMassNumber_;
+
+    std::size_t hash_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/media/NuclearComposition.inl>
diff --git a/corsika/media/ShowerAxis.hpp b/corsika/media/ShowerAxis.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f5e726de8ba4c8966737093261cacbdbf3d8176d
--- /dev/null
+++ b/corsika/media/ShowerAxis.hpp
@@ -0,0 +1,79 @@
+/*
+ * (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 <corsika/media/Environment.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <cassert>
+#include <cstdlib>
+#include <fstream>
+#include <functional>
+#include <iterator>
+#include <memory>
+#include <stdexcept>
+#include <vector>
+
+#include <iostream>
+
+#include <boost/math/quadrature/gauss_kronrod.hpp>
+
+namespace corsika {
+
+  /**
+   * \class ShowerAxis
+   *
+   * The environment::ShowerAxis is created from a Point and
+   * a Vector and inside an Environment. It internally uses
+   * a table with steps=10000 (default) rows for interpolation.
+   *
+   * The shower axis can convert location in the shower into a
+   * projected grammage along the shower axis.
+   *
+   **/
+
+  ///\todo documentation needs update ...
+  class ShowerAxis {
+  public:
+    template <typename TEnvModel>
+    ShowerAxis(Point const& pStart, Vector<length_d> const& length,
+               Environment<TEnvModel> const& env, bool const doThrow = false,
+               int const steps = 10'000);
+
+    LengthType getSteplength() const;
+
+    GrammageType getMaximumX() const;
+
+    GrammageType getMinimumX() const;
+
+    GrammageType getProjectedX(Point const& p) const;
+
+    GrammageType getX(LengthType) const;
+
+    DirectionVector const& getDirection() const;
+
+    Point const& getStart() const;
+
+  private:
+    Point const pointStart_;
+    Vector<length_d> const length_;
+    bool throw_ = false;
+    LengthType const max_length_, steplength_;
+    DirectionVector const axis_normalized_;
+    std::vector<GrammageType> X_;
+
+    // for storing the lengths corresponding to equidistant X values
+    GrammageType const X_binning_ = 1_g / 1_cm / 1_cm;
+    std::vector<LengthType> d_;
+  };
+} // namespace corsika
+
+#include <corsika/detail/media/ShowerAxis.inl>
diff --git a/corsika/media/SlidingPlanarExponential.hpp b/corsika/media/SlidingPlanarExponential.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9060dc8765abc90e05147503462f7a9b1de5afe9
--- /dev/null
+++ b/corsika/media/SlidingPlanarExponential.hpp
@@ -0,0 +1,62 @@
+/*
+ * (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 <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/media/FlatExponential.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+namespace corsika {
+
+  // clang-format off
+  /**
+   * The SlidingPlanarExponential models mass density as
+   * \f[
+   *   \varrho(r) = \varrho_0 \exp\left( \frac{|p_0 - r|}{\lambda} \right).
+   * \f]
+   * For grammage/length conversion, the density distribution is approximated as
+   * locally flat at the starting point \f$ r_0 \f$ of the trajectory with the axis pointing
+   * from \f$ p_0 \f$ to \f$ r_0 \f$.
+   */
+  // clang-format on
+
+  template <typename T>
+  class SlidingPlanarExponential : public BaseExponential<SlidingPlanarExponential<T>>,
+                                   public T {
+
+    using Base = BaseExponential<SlidingPlanarExponential<T>>;
+
+  public:
+    SlidingPlanarExponential(Point const& p0, MassDensityType rho0, LengthType lambda,
+                             NuclearComposition const& nuclComp,
+                             LengthType referenceHeight = LengthType::zero());
+
+    MassDensityType getMassDensity(Point const& point) const override;
+
+    NuclearComposition const& getNuclearComposition() const override;
+
+    GrammageType getIntegratedGrammage(setup::Trajectory const& line,
+                                       LengthType l) const override;
+
+    LengthType getArclengthFromGrammage(setup::Trajectory const& line,
+                                        GrammageType grammage) const override;
+
+  private:
+    NuclearComposition const nuclComp_;
+    LengthType const referenceHeight_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/media/SlidingPlanarExponential.inl>
diff --git a/Environment/UniformMagneticField.h b/corsika/media/UniformMagneticField.hpp
similarity index 74%
rename from Environment/UniformMagneticField.h
rename to corsika/media/UniformMagneticField.hpp
index f649afeb55d4507052dea5a9f4bdc9702687e581..2f08499dc7d91dc6366e3ca69fda9dc92baa5d40 100644
--- a/Environment/UniformMagneticField.h
+++ b/corsika/media/UniformMagneticField.hpp
@@ -8,9 +8,11 @@
 
 #pragma once
 
-#include <corsika/environment/IMagneticFieldModel.h>
+#include <corsika/media/IMagneticFieldModel.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
 
-namespace corsika::environment {
+namespace corsika {
 
   /**
    * A uniform (constant) magnetic field.
@@ -22,12 +24,6 @@ namespace corsika::environment {
   template <typename T>
   class UniformMagneticField : public T {
 
-    // a type-alias for a magnetic field vector
-    using MagneticFieldVector =
-        corsika::geometry::Vector<corsika::units::si::magnetic_flux_density_d>;
-
-    MagneticFieldVector B_; ///< The constant magnetic field we use.
-
   public:
     /**
      * Construct a UniformMagneticField.
@@ -48,10 +44,7 @@ namespace corsika::environment {
      * @param  point    The location to evaluate the field at.
      * @returns    The magnetic field vector.
      */
-    MagneticFieldVector GetMagneticField(
-        corsika::geometry::Point const&) const final override {
-      return B_;
-    }
+    MagneticFieldVector getMagneticField(Point const&) const final override { return B_; }
 
     /**
      * Set the magnetic field returned by this instance.
@@ -59,8 +52,11 @@ namespace corsika::environment {
      * @param  point    The location to evaluate the field at.
      * @returns    The magnetic field vector.
      */
-    auto SetMagneticField(MagneticFieldVector const& Bfield) -> void { B_ = Bfield; }
+    auto setMagneticField(MagneticFieldVector const& Bfield) -> void { B_ = Bfield; }
+
+  private:
+    MagneticFieldVector B_; ///< The constant magnetic field we use.
 
   }; // END: class MagneticField
 
-} // namespace corsika::environment
+} // namespace corsika
diff --git a/Environment/UniformRefractiveIndex.h b/corsika/media/UniformRefractiveIndex.hpp
similarity index 72%
rename from Environment/UniformRefractiveIndex.h
rename to corsika/media/UniformRefractiveIndex.hpp
index 84a43daabe1b26158aa17f201728863daa9574e1..2dc5c790a2006893da59dcbfc87c82b814d1456f 100644
--- a/Environment/UniformRefractiveIndex.h
+++ b/corsika/media/UniformRefractiveIndex.hpp
@@ -8,9 +8,9 @@
 
 #pragma once
 
-#include <corsika/environment/IRefractiveIndexModel.h>
+#include <corsika/media/IRefractiveIndexModel.hpp>
 
-namespace corsika::environment {
+namespace corsika {
 
   /**
    * A uniform refractive index.
@@ -20,7 +20,7 @@ namespace corsika::environment {
    *
    */
   template <typename T>
-  class UniformRefractiveIndex : public T {
+  class UniformRefractiveIndex final : public T {
 
     double n_; ///< The constant refractive index that we use.
 
@@ -34,9 +34,7 @@ namespace corsika::environment {
      * @param field    The refractive index to return everywhere.
      */
     template <typename... Args>
-    UniformRefractiveIndex(double const n, Args&&... args)
-        : T(std::forward<Args>(args)...)
-        , n_(n) {}
+    UniformRefractiveIndex(double const n, Args&&... args);
 
     /**
      * Evaluate the refractive index at a given location.
@@ -44,9 +42,7 @@ namespace corsika::environment {
      * @param  point    The location to evaluate at.
      * @returns    The refractive index at this point.
      */
-    double GetRefractiveIndex(corsika::geometry::Point const&) const final override {
-      return n_;
-    }
+    double getRefractiveIndex(Point const&) const override;
 
     /**
      * Set the refractive index returned by this instance.
@@ -54,8 +50,10 @@ namespace corsika::environment {
      * @param  point    The location to evaluate at.
      * @returns    The refractive index at this location.
      */
-    void SetRefractiveIndex(double const& n) { n_ = n; }
+    void setRefractiveIndex(double const& n);
 
   }; // END: class RefractiveIndex
 
-} // namespace corsika::environment
+} // namespace corsika
+
+#include <corsika/detail/media/UniformRefractiveIndex.inl>
diff --git a/corsika/media/Universe.hpp b/corsika/media/Universe.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..cdd5c2bd297d5bc39ac27f91e8eeecf7b9b7bf95
--- /dev/null
+++ b/corsika/media/Universe.hpp
@@ -0,0 +1,23 @@
+/*
+ * (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 <corsika/framework/geometry/Sphere.hpp>
+#include <limits>
+
+namespace corsika {
+
+  struct Universe : public corsika::Sphere {
+    inline Universe(corsika::CoordinateSystemPtr const& pCS);
+    inline bool contains(corsika::Point const&) const override;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/media/Universe.inl>
diff --git a/corsika/media/VolumeTreeNode.hpp b/corsika/media/VolumeTreeNode.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fba07bc585043bf592bd7cfdfe1b41cddb10332a
--- /dev/null
+++ b/corsika/media/VolumeTreeNode.hpp
@@ -0,0 +1,89 @@
+/*
+ * (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 <corsika/framework/geometry/IVolume.hpp>
+#include <corsika/media/IEmpty.hpp>
+#include <memory>
+#include <vector>
+
+namespace corsika {
+
+  template <typename TModelProperties = IEmpty>
+  class VolumeTreeNode {
+
+  public:
+    using IModelProperties = TModelProperties;
+    using VTN_type = VolumeTreeNode<IModelProperties>;
+    using VTNUPtr = std::unique_ptr<VTN_type>;
+    using IMPSharedPtr = std::shared_ptr<IModelProperties>;
+    using VolUPtr = std::unique_ptr<IVolume>;
+
+    VolumeTreeNode(VolUPtr pVolume = nullptr)
+        : geoVolume_(std::move(pVolume)) {}
+
+    //! convenience function equivalent to Volume::isInside
+    inline bool contains(Point const& p) const;
+
+    inline 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;
+
+    /**
+     * Traverses the VolumeTree pre- or post-order and calls the functor  \p func for each
+     * node. \p func takes a reference to VolumeTreeNode as argument. The return value \p
+     * func is ignored.
+     */
+    template <typename TCallable, bool preorder = true>
+    inline void walk(TCallable func);
+
+    inline void addChild(VTNUPtr pChild);
+
+    inline void excludeOverlapWith(VTNUPtr const& pNode);
+
+    inline VTN_type const* getParent() const { return parentNode_; };
+
+    inline auto const& getChildNodes() const { return childNodes_; }
+
+    inline auto const& getExcludedNodes() const { return excludedNodes_; }
+
+    inline auto const& getVolume() const { return *geoVolume_; }
+
+    inline auto const& getModelProperties() const { return *modelProperties_; }
+
+    inline bool hasModelProperties() const { return modelProperties_.get() != nullptr; }
+
+    template <typename ModelProperties, typename... Args>
+    inline 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; }
+
+    // template <class MediumType, typename... Args>
+    // static auto createMedium(Args&&... args);
+
+  private:
+    std::vector<VTNUPtr> childNodes_;
+    std::vector<VTN_type const*> excludedNodes_;
+    VTN_type const* parentNode_ = nullptr;
+    VolUPtr geoVolume_;
+    IMPSharedPtr modelProperties_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/media/VolumeTreeNode.inl>
diff --git a/corsika/media/WeightProvider.hpp b/corsika/media/WeightProvider.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8f68926a7392b1ff354bd03a1a90540997f271cd
--- /dev/null
+++ b/corsika/media/WeightProvider.hpp
@@ -0,0 +1,49 @@
+/*
+ * (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 <vector>
+
+namespace corsika {
+
+  /** Double Iterator
+   * Iterator that allowes the iteration of two individual lists at the same time. The
+   *user needs to take care that booth lists have the same length.
+   *  @tparam AConstIterator Iterator Type of the first list
+   *  @tparam BConstIterator Iterator Type of the second list
+
+    \todo TODO: replace with
+   https://www.boost.org/doc/libs/1_74_0/libs/iterator/doc/zip_iterator.html or ranges zip
+    \todo check resource allocation
+   **/
+  template <class AConstIterator, class BConstIterator>
+  class WeightProviderIterator {
+    AConstIterator aIter_;
+    BConstIterator bIter_;
+
+  public:
+    using value_type = double;
+    using iterator_category = std::input_iterator_tag;
+    using pointer = value_type*;
+    using reference = value_type&;
+    using difference_type = ptrdiff_t;
+
+    WeightProviderIterator(AConstIterator a, BConstIterator b);
+
+    value_type operator*() const;
+
+    WeightProviderIterator& operator++();
+
+    bool operator==(WeightProviderIterator other);
+
+    bool operator!=(WeightProviderIterator other);
+  };
+} // namespace corsika
+
+#include <corsika/detail/media/WeightProvider.inl>
diff --git a/corsika/modules/BetheBlochPDG.hpp b/corsika/modules/BetheBlochPDG.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f5f34a8050c958b1b6aacf627b92216c1bfa669d
--- /dev/null
+++ b/corsika/modules/BetheBlochPDG.hpp
@@ -0,0 +1,11 @@
+/*
+ * (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 <corsika/modules/energy_loss/BetheBlochPDG.hpp>
diff --git a/corsika/modules/CONEX.hpp b/corsika/modules/CONEX.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8d90673ca687015a35b22650c21af75d50138944
--- /dev/null
+++ b/corsika/modules/CONEX.hpp
@@ -0,0 +1,11 @@
+/*
+ * (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 <corsika/modules/conex/CONEXhybrid.hpp>
diff --git a/corsika/modules/HadronicElasticModel.hpp b/corsika/modules/HadronicElasticModel.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0d5a77473e41fee63e67ca4a47e3244962dac777
--- /dev/null
+++ b/corsika/modules/HadronicElasticModel.hpp
@@ -0,0 +1,63 @@
+/*
+ * (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 <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+
+#include <corsika/framework/core/PhysicalConstants.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+namespace corsika {
+
+  /**
+   * A simple model for elastic hadronic interactions based on the formulas
+   * in Gaisser, Engel, Resconi, Cosmic Rays and Particle Physics (Cambridge Univ. Press,
+   * 2016), section 4.2 and Donnachie, Landshoff, Phys. Lett. B 296, 227 (1992)
+   *
+   * Currently only \f$p\f$ projectiles are supported and cross-sections are assumed to be
+   * \f$pp\f$-like even for nuclei.
+   *
+   * \todo add unit test
+   */
+  class HadronicElasticInteraction
+      : public InteractionProcess<HadronicElasticInteraction> {
+  private:
+    using SquaredHEPEnergyType = decltype(HEPEnergyType() * HEPEnergyType());
+
+    using eV2 = decltype(square(electronvolt));
+    using inveV2 = decltype(1 / square(electronvolt));
+
+    inveV2 B(eV2 s) const;
+    CrossSectionType getCrossSection(SquaredHEPEnergyType s) const;
+
+  public:
+    HadronicElasticInteraction( // x & y values taken from DL for pp collisions
+        CrossSectionType x = 0.0217 * barn, CrossSectionType y = 0.05608 * barn);
+
+    template <typename TParticle>
+    GrammageType getInteractionLength(TParticle const& p);
+
+    template <typename TParticle>
+    ProcessReturn doInteraction(TParticle&);
+
+  private:
+    CrossSectionType parX_, parY_;
+
+    static double constexpr gfEpsilon = 0.0808;
+    static double constexpr gfEta = 0.4525;
+    // Froissart-Martin is not violated up for sqrt s < 10^32 eV with these values [DL].
+
+    default_prng_type& RNG_ =
+        RNGManager::getInstance().getRandomStream("HadronicElasticModel");
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/modules/HadronicElasticModel.inl>
diff --git a/Processes/LongitudinalProfile/LongitudinalProfile.h b/corsika/modules/LongitudinalProfile.hpp
similarity index 53%
rename from Processes/LongitudinalProfile/LongitudinalProfile.h
rename to corsika/modules/LongitudinalProfile.hpp
index c4d2bf0c6e6461d89f66fc53dcd41810a6f7dd7f..baaa5046824c142694097a18ba662ba97647799c 100644
--- a/Processes/LongitudinalProfile/LongitudinalProfile.h
+++ b/corsika/modules/LongitudinalProfile.hpp
@@ -1,7 +1,6 @@
 /*
  * (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.
@@ -9,23 +8,24 @@
 
 #pragma once
 
-#include <corsika/environment/ShowerAxis.h>
-#include <corsika/process/ContinuousProcess.h>
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/media/ShowerAxis.hpp>
+#include <corsika/framework/process/ContinuousProcess.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
 
 #include <array>
 #include <fstream>
 #include <limits>
 #include <string>
 
-namespace corsika::process::longitudinal_profile {
+namespace corsika {
 
   /**
    * \class LongitudinalProfile
    *
+   * \todo test missing
+   *
    * is a ContinuousProcess, which is constructed from an environment::ShowerAxis
-   * object, and a dX in units of g/cm2
-   * (corsika::units::si::GrammageType).
+   * object, and a dX in units of g/cm2  (GrammageType).
    *
    * LongitudinalProfile does then convert each single Track of the
    * simulation into a projected grammage range and counts for
@@ -34,30 +34,25 @@ namespace corsika::process::longitudinal_profile {
    *
    **/
 
-  class LongitudinalProfile
-      : public corsika::process::ContinuousProcess<LongitudinalProfile> {
+  class LongitudinalProfile : public ContinuousProcess<LongitudinalProfile> {
 
   public:
-    LongitudinalProfile(environment::ShowerAxis const&,
-                        units::si::GrammageType dX = std::invoke([]() {
-                          using namespace units::si;
-                          return 10_g / square(1_cm);
-                        })); // profile binning);
+    LongitudinalProfile(ShowerAxis const&,
+                        GrammageType dX = 10_g / square(1_cm)); // profile binning);
 
     template <typename TParticle, typename TTrack>
-    corsika::process::EProcessReturn DoContinuous(TParticle const&, TTrack const&);
+    ProcessReturn doContinuous(TParticle const&, TTrack const&);
 
     template <typename TParticle, typename TTrack>
-    corsika::units::si::LengthType MaxStepLength(TParticle const&, TTrack const&) {
-      return units::si::meter * std::numeric_limits<double>::infinity();
+    LengthType getMaxStepLength(TParticle const&, TTrack const&) {
+      return meter * std::numeric_limits<double>::infinity();
     }
 
     void save(std::string const&, int const width = 14, int const precision = 6);
 
   private:
-    units::si::GrammageType const dX_;
-
-    environment::ShowerAxis const& shower_axis_;
+    GrammageType const dX_;
+    ShowerAxis const& shower_axis_;
     using ProfileEntry = std::array<uint32_t, 6>;
     enum ProfileIndex {
       Gamma = 0,
@@ -70,4 +65,6 @@ namespace corsika::process::longitudinal_profile {
     std::vector<ProfileEntry> profiles_; // longitudinal profile
   };
 
-} // namespace corsika::process::longitudinal_profile
+} // namespace corsika
+
+#include <corsika/detail/modules/LongitudinalProfile.inl>
diff --git a/corsika/modules/ObservationPlane.hpp b/corsika/modules/ObservationPlane.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7effcd93b6abfda4ae1cc8b2adca07b38cb6cdc6
--- /dev/null
+++ b/corsika/modules/ObservationPlane.hpp
@@ -0,0 +1,54 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Plane.hpp>
+#include <corsika/framework/process/ContinuousProcess.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <fstream>
+#include <string>
+
+namespace corsika {
+
+  /**
+   * The ObservationPlane writes PDG codes, energies, and distances of particles to the
+   * central point of the plane into its output file. The particles are considered
+   * "absorbed" afterwards.
+   */
+  class ObservationPlane : public ContinuousProcess<ObservationPlane> {
+
+  public:
+    ObservationPlane(Plane const&, DirectionVector const&, std::string const&,
+                     bool = true);
+
+    ProcessReturn doContinuous(corsika::setup::Stack::particle_type& vParticle,
+                               corsika::setup::Trajectory& vTrajectory);
+
+    LengthType getMaxStepLength(corsika::setup::Stack::particle_type const&,
+                                corsika::setup::Trajectory const& vTrajectory);
+
+    void showResults() const;
+    void reset();
+    HEPEnergyType getEnergyGround() const { return energy_ground_; }
+
+  private:
+    Plane const plane_;
+    std::ofstream outputStream_;
+    bool const deleteOnHit_;
+    HEPEnergyType energy_ground_;
+    unsigned int count_ground_;
+    DirectionVector const xAxis_;
+    DirectionVector const yAxis_;
+  };
+} // namespace corsika
+
+#include <corsika/detail/modules/ObservationPlane.inl>
diff --git a/corsika/modules/OnShellCheck.hpp b/corsika/modules/OnShellCheck.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a7efa4e26241fee9c0036137a38c6f73c02f3e6a
--- /dev/null
+++ b/corsika/modules/OnShellCheck.hpp
@@ -0,0 +1,41 @@
+/*
+ * (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/core/ParticleProperties.hpp>
+#include <corsika/framework/process/SecondariesProcess.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+namespace corsika {
+
+  class OnShellCheck : public SecondariesProcess<OnShellCheck> {
+
+  public:
+    OnShellCheck(const double vMassTolerance, const double vEnergyTolerance,
+                 const bool vError);
+
+    ~OnShellCheck();
+
+    template <typename TView>
+    void doSecondaries(TView&);
+
+  private:
+    // data members
+    double average_shift_ = 0;
+    double max_shift_ = 0;
+    double count_ = 0;
+
+    double mass_tolerance_;
+    double energy_tolerance_;
+    bool throw_error_;
+  };
+} // namespace corsika
+
+#include <corsika/detail/modules/OnShellCheck.inl>
diff --git a/corsika/modules/PROPOSAL.hpp b/corsika/modules/PROPOSAL.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bccfce90ce40e60e2e18e86dcffa416322a4bf8a
--- /dev/null
+++ b/corsika/modules/PROPOSAL.hpp
@@ -0,0 +1,12 @@
+/*
+ * (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 <corsika/modules/proposal/Interaction.hpp>
+#include <corsika/modules/proposal/ContinuousProcess.hpp>
diff --git a/corsika/modules/ParticleCut.hpp b/corsika/modules/ParticleCut.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7abc7d6be02517cf78c8bc19749387cbf7055a23
--- /dev/null
+++ b/corsika/modules/ParticleCut.hpp
@@ -0,0 +1,67 @@
+/*
+ * (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 <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/process/SecondariesProcess.hpp>
+#include <corsika/framework/process/ContinuousProcess.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+namespace corsika {
+
+  class ParticleCut : public SecondariesProcess<ParticleCut>,
+                      public ContinuousProcess<ParticleCut> {
+
+  public:
+    ParticleCut(const HEPEnergyType eCut, bool em, bool inv);
+
+    void doSecondaries(corsika::setup::StackView&);
+    ProcessReturn doContinuous(corsika::setup::Stack::particle_type& vParticle,
+                               corsika::setup::Trajectory const& vTrajectory);
+    LengthType getMaxStepLength(corsika::setup::Stack::particle_type const&,
+                                corsika::setup::Trajectory const&) {
+      return meter * std::numeric_limits<double>::infinity();
+    }
+
+    void showResults();
+    void reset();
+
+    HEPEnergyType getECut() const { return energy_cut_; }
+    HEPEnergyType getInvEnergy() const { return inv_energy_; }
+    HEPEnergyType getCutEnergy() const { return energy_; }
+    HEPEnergyType getEmEnergy() const { return em_energy_; }
+    unsigned int getNumberEmParticles() const { return em_count_; }
+    unsigned int getNumberInvParticles() const { return inv_count_; }
+
+  private:
+    template <typename TParticle>
+    bool checkCutParticle(const TParticle& p);
+
+    template <typename TParticle>
+    bool isBelowEnergyCut(TParticle const&) const;
+    bool isEmParticle(Code) const;
+    bool isInvisible(Code) const;
+
+  private:
+    HEPEnergyType energy_cut_;
+    bool doCutEm_;
+    bool doCutInv_;
+    HEPEnergyType energy_ = 0 * electronvolt;
+    HEPEnergyType em_energy_ = 0 * electronvolt;
+    unsigned int em_count_ = 0;
+    HEPEnergyType inv_energy_ = 0 * electronvolt;
+    unsigned int inv_count_ = 0;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/modules/ParticleCut.inl>
diff --git a/corsika/modules/Pythia8.hpp b/corsika/modules/Pythia8.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9fdf773ad7a10d8376d59db3694a5d9193f395b7
--- /dev/null
+++ b/corsika/modules/Pythia8.hpp
@@ -0,0 +1,12 @@
+/*
+ * (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 <corsika/modules/pythia8/Decay.hpp>
+#include <corsika/modules/pythia8/Interaction.hpp>
diff --git a/corsika/modules/QGSJetII.hpp b/corsika/modules/QGSJetII.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9540c4ad116471d54a5ef178124bd97b979d9758
--- /dev/null
+++ b/corsika/modules/QGSJetII.hpp
@@ -0,0 +1,11 @@
+/*
+ * (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 <corsika/modules/qgsjetII/Interaction.hpp>
diff --git a/corsika/modules/Sibyll.hpp b/corsika/modules/Sibyll.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..44bebf0ff174d4dab8234e2d17d0d67b1ff58416
--- /dev/null
+++ b/corsika/modules/Sibyll.hpp
@@ -0,0 +1,14 @@
+/*
+ * (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 <corsika/modules/sibyll/ParticleConversion.hpp>
+#include <corsika/modules/sibyll/Interaction.hpp>
+#include <corsika/modules/sibyll/Decay.hpp>
+#include <corsika/modules/sibyll/NuclearInteraction.hpp>
diff --git a/corsika/modules/StackInspector.hpp b/corsika/modules/StackInspector.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9616079540a21792148f54186bbe5890a1400b43
--- /dev/null
+++ b/corsika/modules/StackInspector.hpp
@@ -0,0 +1,45 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/process/StackProcess.hpp>
+
+#include <chrono>
+
+namespace corsika {
+
+  template <typename TStack>
+  class StackInspector : public StackProcess<StackInspector<TStack>> {
+
+    typedef typename TStack::particle_type Particle;
+
+    using StackProcess<StackInspector<TStack>>::getStep;
+
+  public:
+    StackInspector(const int vNStep, const bool vReportStack, const HEPEnergyType vE0);
+    ~StackInspector();
+
+    void doStack(const TStack&);
+
+    /**
+     * To set a new E0, for example when a new shower event is started
+     */
+    void setE0(const HEPEnergyType vE0) { E0_ = vE0; }
+
+  private:
+    bool ReportStack_;
+    HEPEnergyType E0_;
+    const HEPEnergyType dE_threshold_ = 1_eV;
+    decltype(std::chrono::system_clock::now()) StartTime_;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/modules/StackInspector.inl>
diff --git a/corsika/modules/TrackWriter.hpp b/corsika/modules/TrackWriter.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..c437bc7d15dfe4ada3ef269e874cccf2ce5a36a7
--- /dev/null
+++ b/corsika/modules/TrackWriter.hpp
@@ -0,0 +1,40 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/process/ContinuousProcess.hpp>
+
+#include <fstream>
+#include <string>
+
+namespace corsika {
+
+  class TrackWriter : public ContinuousProcess<TrackWriter> {
+
+  public:
+    TrackWriter(std::string const& filename);
+
+    template <typename TParticle, typename TTrack>
+    ProcessReturn doContinuous(TParticle const&, TTrack const&);
+
+    template <typename TParticle, typename TTrack>
+    LengthType getMaxStepLength(TParticle const&, TTrack const&);
+
+  private:
+    std::string const filename_;
+    std::ofstream file_;
+
+    int width_ = 14;
+    int precision_ = 6;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/modules/TrackWriter.inl>
diff --git a/corsika/modules/Tracking.hpp b/corsika/modules/Tracking.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fb12412fbb899835050263e674719d546d5ce689
--- /dev/null
+++ b/corsika/modules/Tracking.hpp
@@ -0,0 +1,18 @@
+/*
+ * (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 <corsika/modules/tracking/TrackingStraight.hpp>
+#include <corsika/modules/tracking/TrackingLeapFrogStraight.hpp> // simple leap-frog implementation with two straight lines
+#include <corsika/modules/tracking/TrackingLeapFrogCurved.hpp> // // more complete, curved, leap-frog
+
+/**
+ * \todo add TrackingCurved, with simple circular trajectories
+   \todo add Boris algorithm
+ */
diff --git a/corsika/modules/UrQMD.hpp b/corsika/modules/UrQMD.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a29165307efa5c5c5b3df6d14e9ec3c6badd79f1
--- /dev/null
+++ b/corsika/modules/UrQMD.hpp
@@ -0,0 +1,11 @@
+/*
+ * (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 <corsika/modules/urqmd/UrQMD.hpp>
diff --git a/Processes/CONEXSourceCut/CONEX_f.h b/corsika/modules/conex/CONEX_f.hpp
similarity index 100%
rename from Processes/CONEXSourceCut/CONEX_f.h
rename to corsika/modules/conex/CONEX_f.hpp
diff --git a/corsika/modules/conex/CONEXhybrid.hpp b/corsika/modules/conex/CONEXhybrid.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b8927f6356bea06dc89845b011517f594a48b928
--- /dev/null
+++ b/corsika/modules/conex/CONEXhybrid.hpp
@@ -0,0 +1,58 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/process/SecondariesProcess.hpp>
+#include <corsika/media/ShowerAxis.hpp>
+#include <corsika/setup/SetupStack.hpp>
+
+#include <corsika/modules/conex/CONEX_f.hpp>
+
+namespace corsika {
+
+  namespace conex {
+    LengthType constexpr earthRadius{6371315 * meter};
+  } // namespace conex
+
+  class CONEXhybrid : public SecondariesProcess<CONEXhybrid> {
+
+  public:
+    CONEXhybrid(Point center, ShowerAxis const& showerAxis, LengthType groundDist,
+                LengthType injectionHeight, HEPEnergyType primaryEnergy, PDGCode pdg);
+    void doSecondaries(setup::StackView&);
+
+    void solveCE();
+
+    bool addParticle(Code pid, HEPEnergyType energy, HEPEnergyType mass,
+                     Point const& position, Vector<dimensionless_d> const& direction,
+                     TimeType t);
+
+    CoordinateSystemPtr const& getObserverCS() const { return conexObservationCS_; }
+
+  private:
+    // data members
+    //! CONEX e.m. particle codes
+    static std::array<std::pair<Code, int>, 3> constexpr egs_em_codes_{
+        {{Code::Gamma, 0}, {Code::Electron, -1}, {Code::Positron, -1}}};
+
+    Point const center_; //!< center of CONEX Earth
+    ShowerAxis const& showerAxis_;
+    LengthType groundDist_;  //!< length from injection point to shower core
+    Point const showerCore_; //!< shower core
+    CoordinateSystemPtr const conexObservationCS_; //!< CONEX observation frame
+    DirectionVector const x_sf_,
+        y_sf_; //!< unit vectors of CONEX shower frame, z_sf is shower axis direction
+  };
+} // namespace corsika
+
+#include <corsika/detail/modules/conex/CONEXhybrid.inl>
diff --git a/corsika/modules/energy_loss/BetheBlochPDG.hpp b/corsika/modules/energy_loss/BetheBlochPDG.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3e64567b974c9dfdb71b6f67c99e7877dbd6d39d
--- /dev/null
+++ b/corsika/modules/energy_loss/BetheBlochPDG.hpp
@@ -0,0 +1,76 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/process/ContinuousProcess.hpp>
+#include <corsika/media/ShowerAxis.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <map>
+
+namespace corsika {
+
+  /**
+   *   PDG2018, passage of particles through matter
+   *
+   * Note, that \f$I_{\mathrm{eff}}\f$ of composite media a determined from \f$ \ln I =
+   * \sum_i a_i \ln(I_i) \f$ where \f$ a_i \f$ is the fraction of the electron population
+   * (\f$\sim Z_i\f$) of the \f$i\f$-th element. This can also be used for shell
+   * corrections or density effects.
+   *
+   * The \f$I_{\mathrm{eff}}\f$ of compounds is not better than a few percent, if not
+   * measured explicitly.
+   *
+   * For shell correction, see Sec 6 of https://www.nap.edu/read/20066/chapter/8#115
+   *
+   */
+
+  class BetheBlochPDG : public ContinuousProcess<BetheBlochPDG> {
+
+    using MeVgcm2 = decltype(1e6 * electronvolt / gram * square(1e-2 * meter));
+
+  public:
+    BetheBlochPDG(ShowerAxis const& showerAxis, HEPEnergyType emCut);
+
+    ProcessReturn doContinuous(setup::Stack::particle_type&, setup::Trajectory const&);
+    LengthType getMaxStepLength(setup::Stack::particle_type const&,
+                                setup::Trajectory const&) const;
+    static HEPEnergyType getBetheBloch(setup::Stack::particle_type const&,
+                                       const GrammageType);
+    static HEPEnergyType getRadiationLosses(setup::Stack::particle_type const&,
+                                            const GrammageType);
+    static HEPEnergyType getTotalEnergyLoss(setup::Stack::particle_type const&,
+                                            const GrammageType);
+
+    void showResults() const;
+    void reset();
+    HEPEnergyType getEnergyLost() const { return energy_lost_; }
+    void printProfile() const;
+    HEPEnergyType getTotal() const;
+
+  private:
+    void updateMomentum(corsika::setup::Stack::particle_type&, HEPEnergyType Enew);
+    void fillProfile(setup::Trajectory const&, HEPEnergyType);
+
+    GrammageType const dX_ = 10_g / square(1_cm); // profile binning
+    GrammageType const dX_threshold_ = 0.0001_g / square(1_cm);
+    ShowerAxis const& shower_axis_;
+    HEPEnergyType emCut_;
+    HEPEnergyType energy_lost_ = HEPEnergyType::zero();
+    std::vector<HEPEnergyType> profile_; // longitudinal profile
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/modules/energy_loss/BetheBlochPDG.inl>
diff --git a/Processes/EnergyLoss/Properties8.dat b/corsika/modules/energy_loss/Properties8.dat
similarity index 100%
rename from Processes/EnergyLoss/Properties8.dat
rename to corsika/modules/energy_loss/Properties8.dat
diff --git a/Processes/EnergyLoss/ReadData.py b/corsika/modules/energy_loss/ReadData.py
similarity index 100%
rename from Processes/EnergyLoss/ReadData.py
rename to corsika/modules/energy_loss/ReadData.py
diff --git a/Processes/EnergyLoss/SummaryPropTable.dat b/corsika/modules/energy_loss/SummaryPropTable.dat
similarity index 100%
rename from Processes/EnergyLoss/SummaryPropTable.dat
rename to corsika/modules/energy_loss/SummaryPropTable.dat
diff --git a/Processes/Proposal/ContinuousProcess.h b/corsika/modules/proposal/ContinuousProcess.hpp
similarity index 59%
rename from Processes/Proposal/ContinuousProcess.h
rename to corsika/modules/proposal/ContinuousProcess.hpp
index 6d6641e529134d8ad6aa80a048caaba3acbc50af..3bf53b145246392848f6ba09cbd293ac722c0a80 100644
--- a/Processes/Proposal/ContinuousProcess.h
+++ b/corsika/modules/proposal/ContinuousProcess.hpp
@@ -9,23 +9,28 @@
 #pragma once
 
 #include <PROPOSAL/PROPOSAL.h>
-#include <corsika/environment/Environment.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/process/ContinuousProcess.h>
-#include <corsika/process/proposal/ProposalProcessBase.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/random/UniformRealDistribution.h>
+
+#include <corsika/media/Environment.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/process/ContinuousProcess.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/random/UniformRealDistribution.hpp>
+
+#include <corsika/modules/proposal/ProposalProcessBase.hpp>
+
 #include <unordered_map>
 
-namespace corsika::process::proposal {
+namespace corsika::proposal {
 
   //!
   //! Electro-magnetic and gamma continous losses produced by proposal. It makes
   //! use of interpolation tables which are runtime intensive calculation, but can be
   //! reused by setting the \param PROPOSAL::InterpolationDef::path_to_tables variable.
   //!
-  class ContinuousProcess : public process::ContinuousProcess<ContinuousProcess>,
-                            ProposalProcessBase {
+  class ContinuousProcess
+      : public corsika::ContinuousProcess<proposal::ContinuousProcess>,
+        ProposalProcessBase {
 
     enum { eDISPLACEMENT, eSCATTERING };
     using calc_t = std::tuple<std::unique_ptr<PROPOSAL::Displacement>,
@@ -34,12 +39,12 @@ namespace corsika::process::proposal {
     std::unordered_map<calc_key_t, calc_t, hash>
         calc; //!< Stores the displacement and scattering calculators.
 
-    units::si::HEPEnergyType energy_lost_ = 0 * units::si::electronvolt;
+    HEPEnergyType energy_lost_ = 0 * electronvolt;
 
     //!
     //! Build the displacement and scattering calculators and add it to calc.
     //!
-    void BuildCalculator(particles::Code, environment::NuclearComposition const&) final;
+    void buildCalculator(Code, NuclearComposition const&) final;
 
   public:
     //!
@@ -47,33 +52,34 @@ namespace corsika::process::proposal {
     //! compositions and stochastic description limited by the particle cut.
     //!
     template <typename TEnvironment>
-    ContinuousProcess(TEnvironment const&, corsika::units::si::HEPEnergyType _emCut);
+    ContinuousProcess(TEnvironment const&, HEPEnergyType _emCut);
 
     //!
     //! Multiple Scattering of the lepton. Stochastic deflection is not yet taken into
     //! account. Displacment of the track due to multiple scattering is not possible
     //! because of the constant referernce. The final direction will be updated anyway.
     //!
-    template <typename Particle>
-    void Scatter(Particle&, corsika::units::si::HEPEnergyType const&,
-                 corsika::units::si::GrammageType const&);
+    template <typename TParticle>
+    void scatter(TParticle&, HEPEnergyType const&, GrammageType const&);
 
     //!
     //! Produces the loss and deflection after given distance for the particle.
     //! If the particle if below the given energy threshold where it will be
     //! considered stochastically, it will be absorbed.
     //!
-    template <typename Particle, typename Track>
-    EProcessReturn DoContinuous(Particle&, Track const&);
+    template <typename TParticle, typename TTrack>
+    ProcessReturn doContinuous(TParticle&, TTrack const&);
 
     //!
     //! Calculates maximal step length of process.
     //!
-    template <typename Particle, typename Track>
-    corsika::units::si::LengthType MaxStepLength(Particle const&, Track const&);
+    template <typename TParticle, typename TTrack>
+    LengthType getMaxStepLength(TParticle const&, TTrack const&);
 
     void showResults() const;
     void reset();
-    corsika::units::si::HEPEnergyType energyLost() const { return energy_lost_; }
+    HEPEnergyType getEnergyLost() const { return energy_lost_; }
   };
-} // namespace corsika::process::proposal
+} // namespace corsika::proposal
+
+#include <corsika/detail/modules/proposal/ContinuousProcess.inl>
diff --git a/Processes/Proposal/Interaction.h b/corsika/modules/proposal/Interaction.hpp
similarity index 70%
rename from Processes/Proposal/Interaction.h
rename to corsika/modules/proposal/Interaction.hpp
index 7617e8c27d229068ed95e7afefc975f5b957941b..69f27cca42b728cd52a77883cd377c6c3ccafc36 100644
--- a/Processes/Proposal/Interaction.h
+++ b/corsika/modules/proposal/Interaction.hpp
@@ -10,15 +10,18 @@
 
 #include <PROPOSAL/PROPOSAL.h>
 
-#include <corsika/environment/Environment.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/process/InteractionProcess.h>
-#include <corsika/process/proposal/ProposalProcessBase.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/random/UniformRealDistribution.h>
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/process/InteractionProcess.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/random/UniformRealDistribution.hpp>
+
+#include <corsika/media/Environment.hpp>
+
+#include <corsika/modules/proposal/ProposalProcessBase.hpp>
+
 #include <array>
 
-namespace corsika::process::proposal {
+namespace corsika::proposal {
 
   //!
   //! Electro-magnetic and gamma stochastic losses produced by proposal. It makes
@@ -37,7 +40,7 @@ namespace corsika::process::proposal {
     //!
     //! Build the secondaries and interaction calculators and add it to calc.
     //!
-    void BuildCalculator(particles::Code, environment::NuclearComposition const&) final;
+    void buildCalculator(Code, NuclearComposition const&) final;
 
   public:
     //!
@@ -45,7 +48,7 @@ namespace corsika::process::proposal {
     //! compositions and stochastic description limited by the particle cut.
     //!
     template <typename TEnvironment>
-    Interaction(TEnvironment const& env, corsika::units::si::HEPEnergyType emCut);
+    Interaction(TEnvironment const& env, HEPEnergyType emCut);
 
     //!
     //! Calculate the rates for the different targets and interactions. Sample a
@@ -53,12 +56,14 @@ namespace corsika::process::proposal {
     //! produce the corresponding secondaries and store them on the particle stack.
     //!
     template <typename TSecondaryView>
-    corsika::process::EProcessReturn DoInteraction(TSecondaryView&);
+    ProcessReturn doInteraction(TSecondaryView&);
 
     //!
     //! Calculates the  mean free path length
     //!
     template <typename TParticle>
-    corsika::units::si::GrammageType GetInteractionLength(TParticle const& p);
+    GrammageType getInteractionLength(TParticle const& p);
   };
-} // namespace corsika::process::proposal
+} // namespace corsika::proposal
+
+#include <corsika/detail/modules/proposal/Interaction.inl>
diff --git a/Processes/Proposal/ProposalProcessBase.h b/corsika/modules/proposal/ProposalProcessBase.hpp
similarity index 51%
rename from Processes/Proposal/ProposalProcessBase.h
rename to corsika/modules/proposal/ProposalProcessBase.hpp
index 6346721e77428db40666ad1765c0fd9052cb549d..89037f9fead31d255b76e40180a265aaa00d3cb7 100644
--- a/Processes/Proposal/ProposalProcessBase.h
+++ b/corsika/modules/proposal/ProposalProcessBase.hpp
@@ -10,21 +10,22 @@
 
 #include <PROPOSAL/PROPOSAL.h>
 
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/setup/SetupEnvironment.h>
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+
+#include <corsika/setup/SetupEnvironment.hpp>
+
 #include <array>
 
-namespace corsika::process::proposal {
+namespace corsika::proposal {
 
   //!
   //! Particles which can be handled by proposal. That means they can be
   //! propagated and decayed if they decays.
   //!
-  static constexpr std::array<particles::Code, 7> tracked{
-      particles::Code::Gamma,    particles::Code::Electron, particles::Code::Positron,
-      particles::Code::MuMinus,  particles::Code::MuPlus,   particles::Code::TauPlus,
-      particles::Code::TauMinus,
+  static constexpr std::array<Code, 7> tracked{
+      Code::Gamma,  Code::Electron, Code::Positron, Code::MuMinus,
+      Code::MuPlus, Code::TauPlus,  Code::TauMinus,
   };
 
   //!
@@ -32,14 +33,11 @@ namespace corsika::process::proposal {
   //! crosssections, decay and scattering algorithms. In the future the
   //! particles may be created by reading out the Corsica constants.
   //!
-  static std::map<particles::Code, PROPOSAL::ParticleDef> particle = {
-      {particles::Code::Gamma, PROPOSAL::GammaDef()},
-      {particles::Code::Electron, PROPOSAL::EMinusDef()},
-      {particles::Code::Positron, PROPOSAL::EPlusDef()},
-      {particles::Code::MuMinus, PROPOSAL::MuMinusDef()},
-      {particles::Code::MuPlus, PROPOSAL::MuPlusDef()},
-      {particles::Code::TauMinus, PROPOSAL::TauMinusDef()},
-      {particles::Code::TauPlus, PROPOSAL::TauPlusDef()}};
+  static std::map<Code, PROPOSAL::ParticleDef> particle = {
+      {Code::Gamma, PROPOSAL::GammaDef()},    {Code::Electron, PROPOSAL::EMinusDef()},
+      {Code::Positron, PROPOSAL::EPlusDef()}, {Code::MuMinus, PROPOSAL::MuMinusDef()},
+      {Code::MuPlus, PROPOSAL::MuPlusDef()},  {Code::TauMinus, PROPOSAL::TauMinusDef()},
+      {Code::TauPlus, PROPOSAL::TauPlusDef()}};
 
   //!
   //! Crosssection factories for different particle types.
@@ -58,17 +56,16 @@ namespace corsika::process::proposal {
   //! PROPOSAL default crosssections are maped to corresponding corsika particle
   //! code.
   //!
-  static std::map<particles::Code,
-                  std::function<PROPOSAL::crosssection_list_t<PROPOSAL::ParticleDef,
-                                                              PROPOSAL::Medium>(
-                      PROPOSAL::Medium&, corsika::units::si::HEPEnergyType)>>
-      cross = {{particles::Code::Gamma, cross_builder<PROPOSAL::GammaDef>},
-               {particles::Code::Electron, cross_builder<PROPOSAL::EMinusDef>},
-               {particles::Code::Positron, cross_builder<PROPOSAL::EPlusDef>},
-               {particles::Code::MuMinus, cross_builder<PROPOSAL::MuMinusDef>},
-               {particles::Code::MuPlus, cross_builder<PROPOSAL::MuPlusDef>},
-               {particles::Code::TauMinus, cross_builder<PROPOSAL::TauMinusDef>},
-               {particles::Code::TauPlus, cross_builder<PROPOSAL::TauPlusDef>}};
+  static std::map<Code, std::function<PROPOSAL::crosssection_list_t<PROPOSAL::ParticleDef,
+                                                                    PROPOSAL::Medium>(
+                            PROPOSAL::Medium&, corsika::units::si::HEPEnergyType)>>
+      cross = {{Code::Gamma, cross_builder<PROPOSAL::GammaDef>},
+               {Code::Electron, cross_builder<PROPOSAL::EMinusDef>},
+               {Code::Positron, cross_builder<PROPOSAL::EPlusDef>},
+               {Code::MuMinus, cross_builder<PROPOSAL::MuMinusDef>},
+               {Code::MuPlus, cross_builder<PROPOSAL::MuPlusDef>},
+               {Code::TauMinus, cross_builder<PROPOSAL::TauMinusDef>},
+               {Code::TauPlus, cross_builder<PROPOSAL::TauPlusDef>}};
 
   //!
   //! PROPOSAL base process which handels mapping of particle codes to
@@ -76,10 +73,9 @@ namespace corsika::process::proposal {
   //!
   class ProposalProcessBase {
   protected:
-    corsika::units::si::HEPEnergyType
-        emCut_;                 //!< Stochastic losses smaller than the given cut
+    HEPEnergyType emCut_;       //!< Stochastic losses smaller than the given cut
                                 //!< will be handeled continuously.
-    corsika::random::RNG& fRNG; //!< random number generator used by proposal
+    RNGManager::prng_type RNG_; //!< random number generator used by proposal
 
     std::unordered_map<std::size_t, PROPOSAL::Medium>
         media; //!< maps nuclear composition from univers to media to produce
@@ -89,15 +85,14 @@ namespace corsika::process::proposal {
     //! Store cut and  nuclear composition of the whole universe in media which are
     //! required for creating crosssections by proposal.
     //!
-    ProposalProcessBase(corsika::setup::Environment const& _env,
-                        corsika::units::si::HEPEnergyType _emCut);
+    ProposalProcessBase(corsika::setup::Environment const& _env, HEPEnergyType _emCut);
 
     //!
     //! Checks if a particle can be processed by proposal
     //!
-    bool CanInteract(particles::Code pcode) const;
+    bool canInteract(Code pcode) const;
 
-    using calc_key_t = std::pair<std::size_t, particles::Code>;
+    using calc_key_t = std::pair<std::size_t, Code>;
 
     //!
     //! Hash to store interpolation tables related to a pair of particle and nuclear
@@ -110,8 +105,7 @@ namespace corsika::process::proposal {
     //!
     //! Builds the calculator to the corresponding class
     //!
-    virtual void BuildCalculator(particles::Code,
-                                 environment::NuclearComposition const&) = 0;
+    virtual void buildCalculator(Code, NuclearComposition const&) = 0;
 
     //!
     //! Searches the particle dependet calculator dependent of actuall medium composition
@@ -119,12 +113,14 @@ namespace corsika::process::proposal {
     //! built and then returned.
     //!
     template <typename Particle, typename Calculators>
-    auto GetCalculator(Particle& vP, Calculators& calc) {
-      const auto& comp = vP.GetNode()->GetModelProperties().GetNuclearComposition();
-      auto calc_it = calc.find(std::make_pair(comp.hash(), vP.GetPID()));
+    auto getCalculator(Particle& vP, Calculators& calc) {
+      const auto& comp = vP.getNode()->getModelProperties().getNuclearComposition();
+      auto calc_it = calc.find(std::make_pair(comp.getHash(), vP.getPID()));
       if (calc_it != calc.end()) return calc_it;
-      BuildCalculator(vP.GetPID(), comp);
-      return GetCalculator(vP, calc);
+      buildCalculator(vP.getPID(), comp);
+      return getCalculator(vP, calc);
     }
   };
-} // namespace corsika::process::proposal
+} // namespace corsika::proposal
+
+#include <corsika/detail/modules/proposal/ProposalProcessBase.inl>
diff --git a/corsika/modules/pythia8/Decay.hpp b/corsika/modules/pythia8/Decay.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..2bdfccb3c82a2a0aa8b653d7c117f062ee9bd1e6
--- /dev/null
+++ b/corsika/modules/pythia8/Decay.hpp
@@ -0,0 +1,71 @@
+/*
+ * (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 <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/process/DecayProcess.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <corsika/modules/pythia8/Pythia8.hpp>
+
+namespace corsika::pythia8 {
+
+  class Decay : public DecayProcess<Decay> {
+
+  public:
+    Decay(bool const print_listing = false);
+    Decay(std::set<Code> const&);
+    ~Decay();
+
+    // is Pythia::Decay set to handle the decay of this particle?
+    bool isDecayHandled(Code const);
+
+    //! is decay possible in principle?
+    bool canHandleDecay(Code const);
+
+    //! set Pythia::Decay to handle the decay of this particle!
+    void setHandleDecay(Code const);
+    //! set Pythia::Decay to handle the decay of this list of particles!
+    void setHandleDecay(std::vector<Code> const&);
+    //! set Pythia::Decay to handle all particle decays
+    void setHandleAllDecays();
+
+    //! print internal configuration for this particle
+    void printDecayConfig(Code const);
+    //! print configuration of decays in corsika
+    void printDecayConfig();
+
+    bool canDecay(Code const);
+
+    template <typename TParticle>
+    TimeType getLifetime(TParticle const&);
+
+    template <typename TView>
+    void doDecay(TView&);
+
+  private:
+    void init();
+
+    bool isStable(Code const vCode);
+    void setStable(std::vector<Code> const&);
+    void setUnstable(Code const);
+    void setStable(Code const);
+
+    // data members
+    Pythia8::Pythia pythia_;
+    int count_ = 0;
+    bool handleAllDecays_ = true;
+    std::set<Code> handledDecays_;
+    bool print_listing_ = false;
+  };
+
+} // namespace corsika::pythia8
+
+#include <corsika/detail/modules/pythia8/Decay.inl>
diff --git a/corsika/modules/pythia8/Interaction.hpp b/corsika/modules/pythia8/Interaction.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..08c1d429bebc47887a7b18f224f00936c9c4f6d7
--- /dev/null
+++ b/corsika/modules/pythia8/Interaction.hpp
@@ -0,0 +1,65 @@
+/*
+ * (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 <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/process/InteractionProcess.hpp>
+#include <corsika/modules/pythia8/Pythia8.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+
+#include <tuple>
+
+namespace corsika::pythia8 {
+
+  class Interaction : public InteractionProcess<Interaction> {
+
+  public:
+    Interaction(const bool print_listing = false);
+    ~Interaction();
+
+    void setStable(std::vector<Code> const&);
+    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);
+    void configureLabFrameCollision(const Code, const Code, const HEPEnergyType);
+
+    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;
+  };
+
+} // namespace corsika::pythia8
+
+#include <corsika/detail/modules/pythia8/Interaction.inl>
diff --git a/Framework/Utilities/CorsikaFenvDefault.cc b/corsika/modules/pythia8/Pythia8.hpp
similarity index 61%
rename from Framework/Utilities/CorsikaFenvDefault.cc
rename to corsika/modules/pythia8/Pythia8.hpp
index 4d3426ad6b6c2b3c40e51d7103cec38b5ec12b87..5b42ac148b97a9bc54059d27d3a34c61f4d8b87a 100644
--- a/Framework/Utilities/CorsikaFenvDefault.cc
+++ b/corsika/modules/pythia8/Pythia8.hpp
@@ -1,9 +1,11 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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.
  */
 
-// do nothing, functions exist in system libraries
+#pragma once
+
+#include <Pythia8/Pythia.h>
diff --git a/corsika/modules/pythia8/Random.hpp b/corsika/modules/pythia8/Random.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0e04cdb6ba49801610b9903cd2ce152f9735ddd8
--- /dev/null
+++ b/corsika/modules/pythia8/Random.hpp
@@ -0,0 +1,27 @@
+/*
+ * (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 <Pythia8/Pythia.h>
+
+#include <corsika/framework/random/RNGManager.hpp>
+
+namespace corsika::pythia8 {
+
+  class Random : public Pythia8::RndmEngine {
+    double flat();
+
+  private:
+    std::uniform_real_distribution<double> Dist_;
+    default_prng_type& RNG_ = RNGManager::getInstance().getRandomStream("pythia");
+  };
+
+} // namespace corsika::pythia8
+
+#include <corsika/detail/modules/pythia8/Random.inl>
diff --git a/corsika/modules/qgsjetII/Interaction.hpp b/corsika/modules/qgsjetII/Interaction.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..004788f138980e9a07ffe2c1fe8d86444970180c
--- /dev/null
+++ b/corsika/modules/qgsjetII/Interaction.hpp
@@ -0,0 +1,66 @@
+/*
+ * (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 <corsika/framework/core/ParticleProperties.hpp>
+#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>
+
+#include <string>
+
+namespace corsika::qgsjetII {
+
+  class Interaction : public corsika::InteractionProcess<Interaction> {
+
+    std::string data_path_;
+    int count_ = 0;
+    bool initialized_ = false;
+    QgsjetIIHadronType alternate_ =
+        QgsjetIIHadronType::PiPlusType; // for pi0, rho0 projectiles
+
+  public:
+    Interaction(const std::string& dataPath = "");
+    ~Interaction();
+
+    bool wasInitialized() { return initialized_; }
+    int getMaxTargetMassNumber() const { return maxMassNumber_; }
+    bool isValidTarget(corsika::Code TargetId) const {
+      return is_nucleus(TargetId) && (get_nucleus_A(TargetId) < maxMassNumber_);
+    }
+
+    CrossSectionType getCrossSection(const Code, const Code, const HEPEnergyType,
+                                     const unsigned int Abeam = 0,
+                                     const unsigned int Atarget = 0) const;
+
+    template <typename TParticle>
+    GrammageType getInteractionLength(TParticle const&) const;
+
+    /**
+       In this function QGSJETII is called to produce one event. The
+       event is copied (and boosted) into the shower lab frame.
+     */
+
+    template <typename TSecondaryView>
+    void doInteraction(TSecondaryView&);
+
+  private:
+    corsika::default_prng_type& rng_ =
+        corsika::RNGManager::getInstance().getRandomStream("qgsjet");
+    const int maxMassNumber_ = 208;
+  };
+
+} // namespace corsika::qgsjetII
+
+#include <corsika/detail/modules/qgsjetII/Interaction.inl>
diff --git a/Processes/QGSJetII/ParticleConversion.h b/corsika/modules/qgsjetII/ParticleConversion.hpp
similarity index 59%
rename from Processes/QGSJetII/ParticleConversion.h
rename to corsika/modules/qgsjetII/ParticleConversion.hpp
index 9171706b331e6a388c486e038f6c982d94c2f71d..473365f126a54e2249198d6ad1efc912363a7e96 100644
--- a/Processes/QGSJetII/ParticleConversion.h
+++ b/corsika/modules/qgsjetII/ParticleConversion.hpp
@@ -8,11 +8,11 @@
 
 #pragma once
 
-#include <corsika/particles/ParticleProperties.h>
+#include <corsika/framework/core/ParticleProperties.hpp>
 
 #include <string>
 
-namespace corsika::process::qgsjetII {
+namespace corsika::qgsjetII {
 
   /**
      These are the possible secondaries produced by QGSJetII
@@ -51,17 +51,21 @@ namespace corsika::process::qgsjetII {
     NeutralLightMesonType = 101,
   };
   using QgsjetIIHadronTypeIntType = std::underlying_type<QgsjetIIHadronType>::type;
+} // namespace corsika::qgsjetII
 
-#include <corsika/process/qgsjetII/Generated.inc>
+// include automatically generated code:
+#include <corsika/modules/qgsjetII/Generated.inc>
 
-  QgsjetIICode constexpr ConvertToQgsjetII(corsika::particles::Code pCode) {
-    return corsika2qgsjetII[static_cast<corsika::particles::CodeIntType>(pCode)];
+namespace corsika::qgsjetII {
+
+  QgsjetIICode constexpr convertToQgsjetII(Code pCode) {
+    return corsika2qgsjetII[static_cast<CodeIntType>(pCode)];
   }
 
-  corsika::particles::Code constexpr ConvertFromQgsjetII(QgsjetIICode pCode) {
+  Code constexpr convertFromQgsjetII(QgsjetIICode pCode) {
     auto const pCodeInt = static_cast<QgsjetIICodeIntType>(pCode);
     auto const corsikaCode = qgsjetII2corsika[pCodeInt - minQgsjetII];
-    if (corsikaCode == corsika::particles::Code::Unknown) {
+    if (corsikaCode == Code::Unknown) {
       throw std::runtime_error(std::string("QGSJETII/CORSIKA conversion of pCodeInt=")
                                    .append(std::to_string(pCodeInt))
                                    .append(" impossible"));
@@ -69,27 +73,26 @@ namespace corsika::process::qgsjetII {
     return corsikaCode;
   }
 
-  QgsjetIICodeIntType constexpr ConvertToQgsjetIIRaw(corsika::particles::Code pCode) {
-    return static_cast<QgsjetIICodeIntType>(ConvertToQgsjetII(pCode));
+  QgsjetIICodeIntType constexpr convertToQgsjetIIRaw(Code pCode) {
+    return static_cast<QgsjetIICodeIntType>(convertToQgsjetII(pCode));
   }
 
-  QgsjetIIXSClass constexpr GetQgsjetIIXSCode(corsika::particles::Code pCode) {
+  QgsjetIIXSClass constexpr getQgsjetIIXSCode(Code pCode) {
     // if (pCode == corsika::particles::Code::Nucleus)
     // static_cast(QgsjetIIXSClassIntType>();
-    return corsika2qgsjetIIXStype[static_cast<corsika::particles::CodeIntType>(pCode)];
+    return corsika2qgsjetIIXStype[static_cast<CodeIntType>(pCode)];
   }
 
-  QgsjetIIXSClassIntType constexpr GetQgsjetIIXSCodeRaw(corsika::particles::Code pCode) {
-    return static_cast<QgsjetIIXSClassIntType>(GetQgsjetIIXSCode(pCode));
+  QgsjetIIXSClassIntType constexpr getQgsjetIIXSCodeRaw(Code pCode) {
+    return static_cast<QgsjetIIXSClassIntType>(getQgsjetIIXSCode(pCode));
   }
 
-  bool constexpr CanInteract(corsika::particles::Code pCode) {
-    return GetQgsjetIIXSCode(pCode) != QgsjetIIXSClass::CannotInteract;
+  bool constexpr canInteract(Code pCode) {
+    return getQgsjetIIXSCode(pCode) != QgsjetIIXSClass::CannotInteract;
   }
 
-  QgsjetIIHadronType constexpr GetQgsjetIIHadronType(corsika::particles::Code pCode) {
-    return corsika2qgsjetIIHadronType[static_cast<corsika::particles::CodeIntType>(
-        pCode)];
+  QgsjetIIHadronType constexpr getQgsjetIIHadronType(Code pCode) {
+    return corsika2qgsjetIIHadronType[static_cast<CodeIntType>(pCode)];
   }
 
-} // namespace corsika::process::qgsjetII
+} // namespace corsika::qgsjetII
diff --git a/corsika/modules/qgsjetII/QGSJetIIFragmentsStack.hpp b/corsika/modules/qgsjetII/QGSJetIIFragmentsStack.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5539ec807a99804c5c66fe343c16bc7948a0c3a4
--- /dev/null
+++ b/corsika/modules/qgsjetII/QGSJetIIFragmentsStack.hpp
@@ -0,0 +1,77 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+#include <corsika/modules/qgsjetII/ParticleConversion.hpp>
+
+#include <qgsjet-II-04.hpp>
+
+namespace corsika::qgsjetII {
+
+  class QGSJetIIFragmentsStackData {
+
+  public:
+    void dump() const {}
+
+    void clear() {
+      qgarr13_.nsf = 0;
+      qgarr55_.nwt = 0;
+    }
+    unsigned int getSize() const { return qgarr13_.nsf; }
+    unsigned int getCapacity() const { return iapmax; }
+
+    static unsigned int getWoundedNucleonsTarget() { return qgarr55_.nwt; }
+    static unsigned int getWoundedNucleonsProjectile() { return qgarr55_.nwp; }
+
+    int getFragmentSize(const unsigned int i) const { return qgarr13_.iaf[i]; }
+    void setFragmentSize(const unsigned int i, const int v) { qgarr13_.iaf[i] = v; }
+
+    void copy(const unsigned int i1, const unsigned int i2) {
+      qgarr13_.iaf[i2] = qgarr13_.iaf[i1];
+    }
+
+    void swap(const unsigned int i1, const unsigned int i2) {
+      std::swap(qgarr13_.iaf[i1], qgarr13_.iaf[i2]);
+    }
+
+    void incrementSize() { qgarr13_.nsf++; }
+    void decrementSize() {
+      if (qgarr13_.nsf > 0) { qgarr13_.nsf--; }
+    }
+  };
+
+  template <typename StackIteratorInterface>
+  class FragmentsInterface : public corsika::ParticleBase<StackIteratorInterface> {
+
+    using corsika::ParticleBase<StackIteratorInterface>::getStackData;
+    using corsika::ParticleBase<StackIteratorInterface>::getIndex;
+
+  public:
+    void setParticleData(const int vSize) { setFragmentSize(vSize); }
+
+    void setParticleData(FragmentsInterface<StackIteratorInterface>& /*parent*/,
+                         const int vSize) {
+      setFragmentSize(vSize);
+    }
+
+    void setFragmentSize(const int v) { getStackData().setFragmentSize(getIndex(), v); }
+
+    double getFragmentSize() const { return getStackData().getFragmentSize(getIndex()); }
+  };
+
+  typedef corsika::Stack<QGSJetIIFragmentsStackData, FragmentsInterface>
+      QGSJetIIFragmentsStack;
+
+} // end namespace corsika::qgsjetII
+
+//#include <corsika/detail/modules/qgsjetII/QGSJetIIFragmentsStack.inl>
diff --git a/corsika/modules/qgsjetII/QGSJetIIStack.hpp b/corsika/modules/qgsjetII/QGSJetIIStack.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bde5bdeacdc219f8ff2080f4ab23c2a59ebe2eb4
--- /dev/null
+++ b/corsika/modules/qgsjetII/QGSJetIIStack.hpp
@@ -0,0 +1,74 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/CoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+#include <corsika/modules/qgsjetII/ParticleConversion.hpp>
+
+#include <qgsjet-II-04.hpp>
+
+namespace corsika::qgsjetII {
+
+  class QGSJetIIStackData {
+
+  public:
+    void dump() const {}
+
+    void clear();
+    unsigned int getSize() const;
+    unsigned int getCapacity() const;
+
+    void setId(const unsigned int i, const int v);
+    void setEnergy(const unsigned int i, const HEPEnergyType v);
+
+    void setMomentum(const unsigned int i, const MomentumVector& v);
+
+    int getId(const unsigned int i) const;
+    HEPEnergyType getEnergy(const int i) const;
+    MomentumVector getMomentum(const unsigned int i, const CoordinateSystemPtr& CS) const;
+
+    void copy(const unsigned int i1, const unsigned int i2);
+    void swap(const unsigned int i1, const unsigned int i2);
+
+    void incrementSize();
+    void decrementSize();
+  };
+
+  template <typename StackIteratorInterface>
+  class ParticleInterface : public corsika::ParticleBase<StackIteratorInterface> {
+
+    using corsika::ParticleBase<StackIteratorInterface>::getStackData;
+    using corsika::ParticleBase<StackIteratorInterface>::getIndex;
+
+  public:
+    void setParticleData(const int vID, const HEPEnergyType vE, const MomentumVector& vP,
+                         const HEPMassType);
+    void setParticleData(ParticleInterface<StackIteratorInterface>& /*parent*/,
+                         const int vID, const HEPEnergyType vE, const MomentumVector& vP,
+                         const HEPMassType);
+
+    void setEnergy(const HEPEnergyType v);
+    HEPEnergyType getEnergy() const;
+
+    void setPID(const int v);
+    corsika::qgsjetII::QgsjetIICode getPID() const;
+
+    MomentumVector getMomentum(const CoordinateSystemPtr& CS) const;
+    void setMomentum(const MomentumVector& v);
+  };
+
+  typedef corsika::Stack<QGSJetIIStackData, ParticleInterface> QGSJetIIStack;
+
+} // end namespace corsika::qgsjetII
+
+#include <corsika/detail/modules/qgsjetII/QGSJetIIStack.inl>
diff --git a/corsika/modules/qgsjetII/Random.hpp b/corsika/modules/qgsjetII/Random.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1ba33351dff63470f1bdd2fe299999df33e1e374
--- /dev/null
+++ b/corsika/modules/qgsjetII/Random.hpp
@@ -0,0 +1,23 @@
+/*
+ * (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 <corsika/framework/random/RNGManager.hpp>
+#include <random>
+
+namespace qgsjetII {
+
+  double rndm_interface() {
+    static corsika::default_prng_type& rng =
+        corsika::RNGManager::getInstance().getRandomStream("qgsjet");
+    std::uniform_real_distribution<double> dist;
+    return dist(rng);
+  }
+
+} // namespace qgsjetII
diff --git a/Processes/QGSJetII/qgsjet-II-04.h b/corsika/modules/qgsjetII/qgsjet-II-04.hpp
similarity index 97%
rename from Processes/QGSJetII/qgsjet-II-04.h
rename to corsika/modules/qgsjetII/qgsjet-II-04.hpp
index f9f75d826371873fc9985fb05478c48c81ab17dc..f95d011ae1c1a7784a40aef7012eb2d05e32a6cf 100644
--- a/Processes/QGSJetII/qgsjet-II-04.h
+++ b/corsika/modules/qgsjetII/qgsjet-II-04.hpp
@@ -98,3 +98,5 @@ double qgsect_(const double& e0n, const int& icz, const int& iap0, const int& ia
  */
 double qgran_(int&);
 }
+
+#include <corsika/detail/modules/qgsjetII/qgsjet-II-04.inl>
diff --git a/corsika/modules/sibyll/Decay.hpp b/corsika/modules/sibyll/Decay.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..35b96a5df098eab78bfbe83e078289a604ac1762
--- /dev/null
+++ b/corsika/modules/sibyll/Decay.hpp
@@ -0,0 +1,82 @@
+/*
+ * (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/core/ParticleProperties.hpp>
+#include <corsika/framework/process/DecayProcess.hpp>
+
+#include <set>
+#include <vector>
+
+namespace corsika::sibyll {
+
+  class Decay : public DecayProcess<Decay> {
+
+  public:
+    Decay(const bool sibyll_listing = false);
+    Decay(std::set<Code> const&);
+    ~Decay();
+
+    void printDecayConfig(const Code);
+    void printDecayConfig();
+    void setHadronsUnstable();
+
+    // is Sibyll::Decay set to handle the decay of this particle?
+    bool isDecayHandled(const Code);
+
+    // is decay possible in principle?
+    bool canHandleDecay(const Code);
+
+    // set Sibyll::Decay to handle the decay of this particle!
+    void setHandleDecay(const Code);
+    // set Sibyll::Decay to handle the decay of this list of particles!
+    void setHandleDecay(std::vector<Code> const&);
+    // set Sibyll::Decay to handle all particle decays
+    void setHandleAllDecay();
+
+    template <typename TParticle>
+    TimeType getLifetime(TParticle const&);
+
+    /**
+     In this function SIBYLL is called to produce to decay the input particle.
+   */
+
+    template <typename TSecondaryView>
+    void doDecay(TSecondaryView&);
+
+  private:
+    // internal routines to set particles stable and unstable in the COMMON blocks in
+    // sibyll
+    void setStable(std::vector<Code> const&);
+    void setUnstable(std::vector<Code> const&);
+
+    void setStable(Code const);
+    void setUnstable(Code const);
+
+    // internally set all particles to decay/not to decay
+    void setAllUnstable();
+    void setAllStable();
+
+    // will this particle be stable in sibyll ?
+    bool isStable(Code const);
+    // will this particle decay in sibyll ?
+    bool isUnstable(Code const);
+    // set particle with input code to decay or not
+    void setDecay(Code const, bool const);
+
+    // data members
+    int count_ = 0;
+    bool handleAllDecays_ = true;
+    bool sibyll_listing_ = false;
+    std::set<Code> handledDecays_;
+  };
+
+} // namespace corsika::sibyll
+
+#include <corsika/detail/modules/sibyll/Decay.inl>
diff --git a/corsika/modules/sibyll/Interaction.hpp b/corsika/modules/sibyll/Interaction.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9b983eb018724c208faaffbbcb6d86fa4641cbd2
--- /dev/null
+++ b/corsika/modules/sibyll/Interaction.hpp
@@ -0,0 +1,73 @@
+/*
+ * (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/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/process/InteractionProcess.hpp>
+#include <tuple>
+
+namespace corsika::sibyll {
+
+  class Interaction : public InteractionProcess<Interaction> {
+
+  public:
+    Interaction(bool const sibyll_printout_on = false);
+    ~Interaction();
+
+    bool isValidCoMEnergy(HEPEnergyType const ecm) const {
+      return (minEnergyCoM_ <= ecm) && (ecm <= maxEnergyCoM_);
+    }
+    bool isValidTarget(Code const TargetId) const {
+      return is_nucleus(TargetId) && (get_nucleus_A(TargetId) < maxTargetMassNumber_);
+    }
+
+    std::tuple<CrossSectionType, CrossSectionType> getCrossSection(
+        Code const, Code const, HEPEnergyType const) const;
+
+    template <typename TParticle>
+    GrammageType getInteractionLength(TParticle const&) const;
+
+    /**
+       In this function SIBYLL is called to produce one event. The
+       event is copied (and boosted) into the shower lab frame.
+     */
+
+    template <typename TSecondaries>
+    void doInteraction(TSecondaries&);
+
+  private:
+    void setStable(std::vector<Code> const&);
+    void setUnstable(std::vector<Code> const&);
+
+    void setUnstable(Code const);
+    void setStable(Code const);
+    void setAllUnstable();
+    void setAllStable();
+
+    int getMaxTargetMassNumber() const { return maxTargetMassNumber_; }
+    HEPEnergyType getMinEnergyCoM() const { return minEnergyCoM_; }
+    HEPEnergyType getMaxEnergyCoM() const { return maxEnergyCoM_; }
+
+    default_prng_type& RNG_ = RNGManager::getInstance().getRandomStream("sibyll");
+    const bool internalDecays_ = true;
+    const HEPEnergyType minEnergyCoM_ = 10. * 1e9 * electronvolt;
+    const HEPEnergyType maxEnergyCoM_ = 1.e6 * 1e9 * electronvolt;
+    const int maxTargetMassNumber_ = 18;
+
+    // data members
+    int count_ = 0;
+    int nucCount_ = 0;
+    bool sibyll_listing_;
+  };
+
+} // namespace corsika::sibyll
+
+#include <corsika/detail/modules/sibyll/Interaction.inl>
diff --git a/corsika/modules/sibyll/NuclearInteraction.hpp b/corsika/modules/sibyll/NuclearInteraction.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..22a70bcb2f98389dabd60af44ecbbc5c3c0cab69
--- /dev/null
+++ b/corsika/modules/sibyll/NuclearInteraction.hpp
@@ -0,0 +1,70 @@
+/*
+ * (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/core/ParticleProperties.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/process/InteractionProcess.hpp>
+
+namespace corsika::sibyll {
+
+  class Interaction; // fwd-decl
+
+  /**
+   *
+   *
+   **/
+  template <class TEnvironment>
+  class NuclearInteraction : public InteractionProcess<NuclearInteraction<TEnvironment>> {
+
+  public:
+    NuclearInteraction(sibyll::Interaction&, TEnvironment const&);
+    ~NuclearInteraction();
+
+    void initializeNuclearCrossSections();
+    void printCrossSectionTable(Code);
+    CrossSectionType readCrossSectionTable(int const, Code const, HEPEnergyType const);
+    HEPEnergyType getMinEnergyPerNucleonCoM() { return gMinEnergyPerNucleonCoM_; }
+    HEPEnergyType getMaxEnergyPerNucleonCoM() { return gMaxEnergyPerNucleonCoM_; }
+    unsigned int constexpr getMaxNucleusAProjectile() { return gMaxNucleusAProjectile_; }
+    unsigned int constexpr getMaxNFragments() { return gMaxNFragments_; }
+    unsigned int constexpr getNEnergyBins() { return gNEnBins_; }
+
+    template <typename Particle>
+    std::tuple<CrossSectionType, CrossSectionType> getCrossSection(Particle const& p,
+                                                                   const Code TargetId);
+
+    template <typename Particle>
+    GrammageType getInteractionLength(Particle const&);
+
+    template <typename TSecondaryView>
+    void doInteraction(TSecondaryView&);
+
+  private:
+    int count_ = 0;
+    int nucCount_ = 0;
+
+    TEnvironment const& environment_;
+    sibyll::Interaction& hadronicInteraction_;
+    std::map<Code, int> targetComponentsIndex_;
+    default_prng_type& RNG_ = RNGManager::getInstance().getRandomStream("sibyll");
+    static unsigned int constexpr gNSample_ =
+        500; // number of samples in MC estimation of cross section
+    static unsigned int constexpr gMaxNucleusAProjectile_ = 56;
+    static unsigned int constexpr gNEnBins_ = 6;
+    static unsigned int constexpr gMaxNFragments_ = 60;
+    // energy limits defined by table used for cross section in signuc.f
+    // 10**1 GeV to 10**6 GeV
+    static HEPEnergyType constexpr gMinEnergyPerNucleonCoM_ = 10. * 1e9 * electronvolt;
+    static HEPEnergyType constexpr gMaxEnergyPerNucleonCoM_ = 1.e6 * 1e9 * electronvolt;
+  };
+
+} // namespace corsika::sibyll
+
+#include <corsika/detail/modules/sibyll/NuclearInteraction.inl>
diff --git a/Processes/Sibyll/ParticleConversion.h b/corsika/modules/sibyll/ParticleConversion.hpp
similarity index 51%
rename from Processes/Sibyll/ParticleConversion.h
rename to corsika/modules/sibyll/ParticleConversion.hpp
index 143e15e63aac36287de7f05d918cae8f2c1065a4..5351280f91a57f40f56805b2cb7d6e3c3f48e6c4 100644
--- a/Processes/Sibyll/ParticleConversion.h
+++ b/corsika/modules/sibyll/ParticleConversion.hpp
@@ -8,13 +8,14 @@
 
 #pragma once
 
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/process/sibyll/sibyll2.3d.h>
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <sibyll2.3d.hpp>
 
 #include <string>
 
-namespace corsika::process::sibyll {
+namespace corsika::sibyll {
 
   enum class SibyllCode : int8_t;
   using SibyllCodeIntType = std::underlying_type<SibyllCode>::type;
@@ -30,16 +31,16 @@ namespace corsika::process::sibyll {
   };
   using SibyllXSClassIntType = std::underlying_type<SibyllXSClass>::type;
 
-#include <corsika/process/sibyll/Generated.inc>
+#include <corsika/modules/sibyll/Generated.inc>
 
-  SibyllCode constexpr ConvertToSibyll(corsika::particles::Code pCode) {
-    return corsika2sibyll[static_cast<corsika::particles::CodeIntType>(pCode)];
+  SibyllCode constexpr convertToSibyll(corsika::Code pCode) {
+    return corsika2sibyll[static_cast<corsika::CodeIntType>(pCode)];
   }
 
-  corsika::particles::Code constexpr ConvertFromSibyll(SibyllCode pCode) {
+  corsika::Code constexpr convertFromSibyll(SibyllCode pCode) {
     auto const s = static_cast<SibyllCodeIntType>(pCode);
     auto const corsikaCode = sibyll2corsika[s - minSibyll];
-    if (corsikaCode == corsika::particles::Code::Unknown) {
+    if (corsikaCode == corsika::Code::Unknown) {
       throw std::runtime_error(std::string("SIBYLL/CORSIKA conversion of ")
                                    .append(std::to_string(s))
                                    .append(" impossible"));
@@ -47,19 +48,19 @@ namespace corsika::process::sibyll {
     return corsikaCode;
   }
 
-  int constexpr ConvertToSibyllRaw(corsika::particles::Code pCode) {
-    return static_cast<int>(ConvertToSibyll(pCode));
+  int constexpr convertToSibyllRaw(corsika::Code pCode) {
+    return static_cast<int>(convertToSibyll(pCode));
   }
 
-  int constexpr GetSibyllXSCode(corsika::particles::Code pCode) {
+  int constexpr getSibyllXSCode(corsika::Code pCode) {
     return static_cast<SibyllXSClassIntType>(
-        corsika2sibyllXStype[static_cast<corsika::particles::CodeIntType>(pCode)]);
+        corsika2sibyllXStype[static_cast<corsika::CodeIntType>(pCode)]);
   }
 
-  bool constexpr CanInteract(corsika::particles::Code pCode) {
-    return GetSibyllXSCode(pCode) > 0;
-  }
+  bool constexpr canInteract(corsika::Code pCode) { return getSibyllXSCode(pCode) > 0; }
+
+  HEPMassType getSibyllMass(corsika::Code const);
 
-  corsika::units::si::HEPMassType GetSibyllMass(corsika::particles::Code const);
+} // namespace corsika::sibyll
 
-} // namespace corsika::process::sibyll
+#include <corsika/detail/modules/sibyll/ParticleConversion.inl>
diff --git a/corsika/modules/sibyll/Random.hpp b/corsika/modules/sibyll/Random.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b3f3d86ba899e1dce4ac18687c2d1e4aaa47853a
--- /dev/null
+++ b/corsika/modules/sibyll/Random.hpp
@@ -0,0 +1,31 @@
+/*
+ * (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/random/RNGManager.hpp>
+#include <random>
+
+/**
+ * \file sibyll/Random.hpp
+ *
+ * This file is an integral part of the sibyll interface. It must be
+ * linked to the executable linked to sibyll exactly once
+ *
+ */
+
+namespace sibyll {
+
+  double rndm_interface() {
+    static corsika::default_prng_type& rng =
+        corsika::RNGManager::getInstance().getRandomStream("sibyll");
+    std::uniform_real_distribution<double> dist;
+    return dist(rng);
+  }
+
+} // namespace sibyll
diff --git a/corsika/modules/sibyll/SibStack.hpp b/corsika/modules/sibyll/SibStack.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..68e2d63d54c2314d03888276c123cc01129c15aa
--- /dev/null
+++ b/corsika/modules/sibyll/SibStack.hpp
@@ -0,0 +1,122 @@
+/*
+ * (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/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+#include <corsika/modules/sibyll/ParticleConversion.hpp>
+
+#include <sibyll2.3d.hpp>
+
+namespace corsika::sibyll {
+
+  typedef corsika::Vector<hepmomentum_d> MomentumVector;
+
+  class SibStackData {
+
+  public:
+    void dump() const {}
+
+    void clear() { s_plist_.np = 0; }
+    unsigned int getSize() const { return s_plist_.np; }
+    unsigned int getCapacity() const { return 8000; }
+
+    void setId(const unsigned int i, const int v) { s_plist_.llist[i] = v; }
+    void setEnergy(const unsigned int i, const HEPEnergyType v) {
+      s_plist_.p[3][i] = v / 1_GeV;
+    }
+    void setMass(const unsigned int i, const HEPMassType v) {
+      s_plist_.p[4][i] = v / 1_GeV;
+    }
+    void setMomentum(const unsigned int i, const MomentumVector& v) {
+      auto tmp = v.getComponents();
+      for (int idx = 0; idx < 3; ++idx) s_plist_.p[idx][i] = tmp[idx] / 1_GeV;
+    }
+
+    int getId(const unsigned int i) const { return s_plist_.llist[i]; }
+    HEPEnergyType getEnergy(const int i) const { return s_plist_.p[3][i] * 1_GeV; }
+    HEPEnergyType getMass(const unsigned int i) const { return s_plist_.p[4][i] * 1_GeV; }
+    MomentumVector getMomentum(const unsigned int i) const {
+      CoordinateSystemPtr const& rootCS = get_root_CoordinateSystem();
+      QuantityVector<hepmomentum_d> components = {
+          s_plist_.p[0][i] * 1_GeV, s_plist_.p[1][i] * 1_GeV, s_plist_.p[2][i] * 1_GeV};
+      return MomentumVector(rootCS, components);
+    }
+
+    void copy(const unsigned int i1, const unsigned int i2) {
+      s_plist_.llist[i2] = s_plist_.llist[i1];
+      for (unsigned int i = 0; i < 5; ++i) s_plist_.p[i][i2] = s_plist_.p[i][i1];
+    }
+
+    void swap(const unsigned int i1, const unsigned int i2) {
+      std::swap(s_plist_.llist[i1], s_plist_.llist[i2]);
+      for (unsigned int i = 0; i < 5; ++i)
+        std::swap(s_plist_.p[i][i1], s_plist_.p[i][i2]);
+    }
+
+    void incrementSize() { s_plist_.np++; }
+    void decrementSize() {
+      if (s_plist_.np > 0) { s_plist_.np--; }
+    }
+  };
+
+  template <typename StackIteratorInterface>
+  class ParticleInterface : public corsika::ParticleBase<StackIteratorInterface> {
+
+    using corsika::ParticleBase<StackIteratorInterface>::getStackData;
+    using corsika::ParticleBase<StackIteratorInterface>::getIndex;
+
+  public:
+    void setParticleData(const int vID, // corsika::sibyll::SibyllCode vID,
+                         const HEPEnergyType vE, const MomentumVector& vP,
+                         const HEPMassType vM) {
+      setPID(vID);
+      setEnergy(vE);
+      setMomentum(vP);
+      setMass(vM);
+    }
+
+    void setParticleData(ParticleInterface<StackIteratorInterface>& /*parent*/,
+                         const int vID, //  corsika::sibyll::SibyllCode vID,
+                         const HEPEnergyType vE, const MomentumVector& vP,
+                         const HEPMassType vM) {
+      setPID(vID);
+      setEnergy(vE);
+      setMomentum(vP);
+      setMass(vM);
+    }
+
+    void setEnergy(const HEPEnergyType v) { getStackData().setEnergy(getIndex(), v); }
+
+    HEPEnergyType getEnergy() const { return getStackData().getEnergy(getIndex()); }
+
+    bool hasDecayed() const { return abs(getStackData().getId(getIndex())) > 100; }
+
+    void setMass(const HEPMassType v) { getStackData().setMass(getIndex(), v); }
+
+    HEPEnergyType getMass() const { return getStackData().getMass(getIndex()); }
+
+    void setPID(const int v) { getStackData().setId(getIndex(), v); }
+
+    corsika::sibyll::SibyllCode getPID() const {
+      return static_cast<corsika::sibyll::SibyllCode>(getStackData().getId(getIndex()));
+    }
+
+    MomentumVector getMomentum() const { return getStackData().getMomentum(getIndex()); }
+
+    void setMomentum(const MomentumVector& v) {
+      getStackData().setMomentum(getIndex(), v);
+    }
+  };
+
+  typedef corsika::Stack<SibStackData, ParticleInterface> SibStack;
+
+} // end namespace corsika::sibyll
diff --git a/corsika/modules/tracking/Intersect.hpp b/corsika/modules/tracking/Intersect.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..1e7a07d84dbee68f4094089a23a99f15cf4fbb69
--- /dev/null
+++ b/corsika/modules/tracking/Intersect.hpp
@@ -0,0 +1,54 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/core/Logging.hpp>
+#include <corsika/framework/geometry/Intersections.hpp>
+
+#include <limits>
+
+namespace corsika {
+
+  /**
+   *
+   * This is a CRTP class to provide a generic volume-tree
+   * intersection for the purpose of tracking.
+   *
+   * It return the closest distance in time to the next geometric
+   * intersection, as well as a pointer to the corresponding new
+   * volume.
+   *
+   * User may provide an optional global step-length limit as
+   * parameter. This may be needd for (simpler) algorithms in magnetic
+   * fields, where tracking errors grow linearly with step-length.
+   * Obviously, in the case of the step-length limit, the returend
+   * "next" volume is just the current one.
+   *
+   **/
+
+  template <typename TDerived>
+  class Intersect {
+
+  protected:
+    /**
+     * Determines next intersection with any of the geometry volumes.
+     *
+     *
+     */
+    template <typename TParticle>
+    auto nextIntersect(TParticle const& particle,
+                       TimeType const step_limit =
+                           std::numeric_limits<TimeType::value_type>::infinity() *
+                           second) const;
+  };
+
+} // namespace corsika
+
+#include <corsika/detail/modules/tracking/Intersect.inl>
diff --git a/corsika/modules/tracking/TrackingLeapFrogCurved.hpp b/corsika/modules/tracking/TrackingLeapFrogCurved.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..dff4bc18ea42ee6ab0d399ad1b2dc7d3b9e185aa
--- /dev/null
+++ b/corsika/modules/tracking/TrackingLeapFrogCurved.hpp
@@ -0,0 +1,91 @@
+/*
+ * (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 <corsika/modules/tracking/TrackingStraight.hpp> // for neutral particles
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Plane.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+#include <corsika/framework/geometry/LeapFrogTrajectory.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/geometry/Intersections.hpp>
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/utility/QuarticSolver.hpp>
+#include <corsika/framework/core/Logging.hpp>
+#include <corsika/modules/tracking/Intersect.hpp>
+
+#include <type_traits>
+#include <utility>
+
+namespace corsika {
+
+  namespace tracking_leapfrog_curved {
+
+    /**
+     * \file TrackingLeapFrogCurved.hpp
+     * \function LeapFrogStep
+     *
+     * Performs one leap-frog step consistent of two halve-steps with steplength/2
+     * The step is caluculated analytically precisely to reach to the next volume
+     *boundary.
+     **/
+    template <typename TParticle>
+    auto make_LeapFrogStep(TParticle const& particle, LengthType steplength);
+
+    /**
+     *
+     * The class tracking_leapfrog_curved::Tracking is based on the
+     * Bachelor thesis of Andre Schmidt (KIT). It implements a
+     * two-step leap-frog algorithm, but with analytically exact geometric
+     * intersections between leap-frog steps and geometric volumes
+     * (spheres, planes).
+     *
+     **/
+
+    class Tracking : public Intersect<Tracking> {
+
+      using Intersect<Tracking>::nextIntersect;
+
+    public:
+      Tracking()
+          : straightTracking_{tracking_line::Tracking()} {}
+
+      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);
+
+      template <typename TParticle, typename TBaseNodeType>
+      static Intersections intersect(const TParticle& particle,
+                                     const TBaseNodeType& volumeNode);
+
+    protected:
+      /**
+       * Use internally stored class tracking_line::Tracking to
+       * perform a straight line tracking, if no magnetic bendig was
+       * detected.
+       *
+       */
+      template <typename TParticle>
+      auto getLinearTrajectory(TParticle& particle);
+
+    protected:
+      tracking_line::Tracking
+          straightTracking_; ///! we want this for neutral and B=0T tracks
+
+    }; // namespace tracking_leapfrog_curved
+
+  } // namespace tracking_leapfrog_curved
+
+} // namespace corsika
+
+#include <corsika/detail/modules/tracking/TrackingLeapFrogCurved.inl>
diff --git a/corsika/modules/tracking/TrackingLeapFrogStraight.hpp b/corsika/modules/tracking/TrackingLeapFrogStraight.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..f54e355ba3ca568e75caff18be305bf665f73557
--- /dev/null
+++ b/corsika/modules/tracking/TrackingLeapFrogStraight.hpp
@@ -0,0 +1,72 @@
+/*
+ * (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 <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>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <corsika/modules/tracking/TrackingStraight.hpp>
+
+#include <type_traits>
+#include <utility>
+
+namespace corsika {
+
+  namespace tracking_leapfrog_straight {
+
+    /**
+     *
+     * The class tracking_leapfrog_straight::Tracking inherits from
+     * tracking_line::Tracking and adds a (two-step) Leap-Frog
+     * algorithms with two halve-steps and magnetic deflection.
+     *
+     * The two halve steps are implemented as two straight explicit
+     * `tracking_line::Tracking`s and all geometry intersections are,
+     * thus, based on those two straight line elements.
+     *
+     * As a precaution for numerical instability, the steplength is
+     * limited to correspond to a straight line distance to the next
+     * volume intersection. In typical situations this leads to about
+     * (at least) one full leap-frog step to the next volume boundary.
+     *
+     **/
+
+    class Tracking : public tracking_line::Tracking {
+
+    public:
+      /**
+       * \param firstFraction fraction of first leap-frog halve step
+       * relative to full linear step to next volume boundary. This
+       * should not be less than 0.5, otherwise you risk that
+       * particles will never travel from one volume to the next
+       * one. A crossing must be possible (even likely). However, if
+       * firstFraction is too big (~1) the resulting calculated
+       * numerical error will be largest.
+       *
+       */
+      Tracking(double const firstFraction = 0.55)
+          : firstFraction_(firstFraction) {}
+
+      template <typename Particle>
+      auto getTrack(Particle& particle);
+
+    protected:
+      double firstFraction_;
+    };
+
+  } // namespace tracking_leapfrog_straight
+
+} // namespace corsika
+
+#include <corsika/detail/modules/tracking/TrackingLeapFrogStraight.inl>
diff --git a/corsika/modules/tracking/TrackingStraight.hpp b/corsika/modules/tracking/TrackingStraight.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b77f8fd211b6c2459e012389b6fa6939088494da
--- /dev/null
+++ b/corsika/modules/tracking/TrackingStraight.hpp
@@ -0,0 +1,61 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Plane.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/geometry/StraightTrajectory.hpp>
+#include <corsika/framework/geometry/Intersections.hpp>
+#include <corsika/framework/core/Logging.hpp>
+#include <corsika/modules/tracking/Intersect.hpp>
+
+#include <type_traits>
+#include <utility>
+
+namespace corsika::tracking_line {
+
+  /**
+   * Tracking of particles without charge or in no magnetic fields.
+   *
+   * This will simply follow traight lines, but intersect them with
+   * the geometry volume model of the environment.
+   *
+   **/
+
+  class Tracking : public Intersect<Tracking> {
+
+    using Intersect<Tracking>::nextIntersect;
+
+  public:
+    //! Determine track of particle
+    template <typename TParticle>
+    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&);
+
+    //! find intersection of Volume with Track
+    template <typename TParticle, typename TBaseNodeType>
+    static Intersections intersect(TParticle const& particle,
+                                   TBaseNodeType const& volumeNode);
+
+    //! find intersection of Plane with Track
+    template <typename TParticle, typename TMedium>
+    static Intersections intersect(TParticle const& particle, Plane const& plane,
+                                   TMedium const&);
+  };
+
+} // namespace corsika::tracking_line
+
+#include <corsika/detail/modules/tracking/TrackingStraight.inl>
diff --git a/corsika/modules/urqmd/Random.hpp b/corsika/modules/urqmd/Random.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..aa8f555e022b06eba90aaf0e97bb277f4d3a730d
--- /dev/null
+++ b/corsika/modules/urqmd/Random.hpp
@@ -0,0 +1,26 @@
+/*
+ * (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/random/RNGManager.hpp>
+#include <random>
+
+namespace urqmd {
+
+  /**
+   * the random number generator function of UrQMD
+   */
+  double rndm_interface() {
+    static corsika::default_prng_type& rng =
+        corsika::RNGManager::getInstance().getRandomStream("urqmd");
+    static std::uniform_real_distribution<double> dist;
+    return dist(rng);
+  }
+
+} // namespace urqmd
diff --git a/corsika/modules/urqmd/UrQMD.hpp b/corsika/modules/urqmd/UrQMD.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..52439d7234a287718c06a471859e8e35b31fe8fd
--- /dev/null
+++ b/corsika/modules/urqmd/UrQMD.hpp
@@ -0,0 +1,57 @@
+/*
+ * (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 <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/process/InteractionProcess.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+
+#include <array>
+#include <utility>
+
+namespace corsika::urqmd {
+
+  class UrQMD : public InteractionProcess<UrQMD> {
+  public:
+    UrQMD();
+
+    template <typename TParticle>
+    GrammageType getInteractionLength(TParticle const&) const;
+
+    template <typename TParticle>
+    CrossSectionType getCrossSection(TParticle const&, Code) const;
+
+    template <typename TView>
+    void doInteraction(TView&);
+
+    bool canInteract(Code) const;
+
+  private:
+    static CrossSectionType getCrossSection(Code, Code, HEPEnergyType, int);
+
+    // data members
+    default_prng_type& RNG_ = RNGManager::getInstance().getRandomStream("urqmd");
+
+    std::uniform_int_distribution<int> booleanDist_{0, 1};
+  };
+
+  /**
+   * convert CORSIKA code to UrQMD code tuple
+   *
+   * In the current implementation a detour via the PDG code is made.
+   */
+  std::pair<int, int> convertToUrQMD(Code);
+  Code convertFromUrQMD(int vItyp, int vIso3);
+
+} // namespace corsika::urqmd
+
+#include <corsika/detail/modules/urqmd/UrQMD.inl>
diff --git a/corsika/setup/SetupEnvironment.hpp b/corsika/setup/SetupEnvironment.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b7f59b31b6aa34d914206285f37463ce4981cb37
--- /dev/null
+++ b/corsika/setup/SetupEnvironment.hpp
@@ -0,0 +1,28 @@
+/*
+ * (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 <corsika/media/Environment.hpp>
+#include <corsika/media/IMagneticFieldModel.hpp>
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/IMediumPropertyModel.hpp>
+#include <corsika/media/IRefractiveIndexModel.hpp>
+
+namespace corsika::setup {
+
+  /**
+     Definition of the default environemnt model interface. Each model
+     interface provides properties of the environment in a position
+     bdependent way.
+   */
+
+  using EnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>;
+  using Environment = Environment<EnvironmentInterface>;
+
+} // end namespace corsika::setup
diff --git a/corsika/setup/SetupLogger.hpp b/corsika/setup/SetupLogger.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..562f93c929a1b85ac96bdc98e79633ad2b0a14d5
--- /dev/null
+++ b/corsika/setup/SetupLogger.hpp
@@ -0,0 +1,19 @@
+/*
+ * (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
+
+namespace corsika::setup {
+
+  /**
+   * The default "corsika" logger.
+   */
+  static std::shared_ptr<spdlog::logger> corsika_logger = get_logger("corsika", true);
+  // corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+} // namespace corsika::setup
diff --git a/corsika/setup/SetupStack.hpp b/corsika/setup/SetupStack.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6c9696d899be01c7ae4eff98000b5ebe394b293f
--- /dev/null
+++ b/corsika/setup/SetupStack.hpp
@@ -0,0 +1,77 @@
+/*
+ * (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.
+ */
+
+#include <corsika/detail/setup/SetupStack.hpp>
+
+#include <array>
+#include <memory>
+
+namespace corsika::setup {
+
+  // ---------------------------------------
+  // this is the stack we use in C8 executables:
+
+#ifdef WITH_HISTORY
+
+  /*
+   * the version with history
+   */
+  using Stack = detail::StackWithHistory;
+  template <typename T1, template <typename> typename M2>
+  using StackViewProducer = HistorySecondaryProducer<T1, M2>;
+
+#else // WITH_HISTORY
+
+  /*
+   * the version without history
+   */
+  using Stack = detail::StackWithGeometry;
+  template <typename T1, template <typename> typename M2>
+  using StackViewProducer = corsika::DefaultSecondaryProducer<T1, M2>;
+
+#endif
+
+  // ---------------------------------------
+  // this is the stackitertor (particle type) we use in C8 executables:
+
+  /*
+    See Issue 161
+
+    unfortunately clang does not support this in the same way (yet) as
+    gcc, so we have to distinguish here. If clang cataches up, we
+    could remove the clang branch here and also in
+    corsika::Cascade. The gcc code is much more generic and
+    universal. If we could do the gcc version, we won't had to define
+    StackView globally, we could do it with MakeView whereever it is
+    actually needed. Keep an eye on this!
+  */
+
+#ifdef WITH_HISTORY
+
+#if defined(__clang__)
+  using StackView = SecondaryView<typename Stack::stack_implementation_type,
+                                  // CHECK with CLANG: setup::Stack::MPIType>;
+                                  detail::StackWithHistoryInterface, StackViewProducer>;
+#elif defined(__GNUC__) || defined(__GNUG__)
+  using StackView = make_view<setup::Stack, StackViewProducer>::type;
+#endif
+
+#else // WITH_HISTORY
+
+#if defined(__clang__)
+  using StackView = SecondaryView<typename setup::Stack::stack_implementation_type,
+                                  // CHECK with CLANG:
+                                  // setup::Stack::MPIType>;
+                                  setup::detail::StackWithGeometryInterface>;
+#elif defined(__GNUC__) || defined(__GNUG__)
+  using StackView = corsika::MakeView<setup::Stack>::type;
+#endif
+
+#endif // WITH_HISTORY
+
+} // namespace corsika::setup
diff --git a/corsika/setup/SetupTrajectory.hpp b/corsika/setup/SetupTrajectory.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fdbfcbeaa4d0a678a15583c5ceef93d3b5d3c31f
--- /dev/null
+++ b/corsika/setup/SetupTrajectory.hpp
@@ -0,0 +1,52 @@
+/*
+ * (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 <corsika/framework/geometry/StraightTrajectory.hpp>
+#include <corsika/framework/geometry/LeapFrogTrajectory.hpp>
+#include <corsika/modules/Tracking.hpp>
+
+namespace corsika::setup {
+
+  /**
+    \file SetupTrajectory.hpp
+
+    Note/Warning:     Tracking and Trajectory must fit together !
+
+    tracking_leapfrog_curved::Tracking is the result of the Bachelor
+    thesis of Andre Schmidt, KIT. This is a leap-frog algorithm with
+    an analytical, precise calculation of volume intersections. This
+    algorithm needs a LeapFrogTrajectory.
+
+    tracking_leapfrog_straight::Tracking is a more simple and direct
+    leap-frog implementation. The two halve steps are coded explicitly
+    as two straight segments. Intersections with other volumes are
+    calculate only on the straight segments. This algorithm is based
+    on LineTrajectory.
+
+    tracking_line::Tracking is a pure straight tracker. It is based on
+    LineTrajectory.
+   */
+
+  /**
+     The default tracking algorithm.
+   */
+
+  // typedef corsika::process::tracking_leapfrog_curved::Tracking Tracking;
+  // typedef corsika::process::tracking_leapfrog_straight::Tracking Tracking;
+  typedef corsika::tracking_line::Tracking Tracking;
+
+  /**
+   The default trajectory.
+  */
+  /// definition of Trajectory base class, to be used in tracking and cascades
+  typedef StraightTrajectory Trajectory;
+  // typedef corsika::geometry::LeapFrogTrajectory Trajectory;
+
+} // namespace corsika::setup
diff --git a/corsika/stack/DummyStack.hpp b/corsika/stack/DummyStack.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..bbbfff91a46e1c2a3348040105f0e0afdb0a5855
--- /dev/null
+++ b/corsika/stack/DummyStack.hpp
@@ -0,0 +1,87 @@
+/*
+ * (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/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+
+#include <string>
+#include <tuple>
+
+namespace corsika::dummy_stack {
+
+  /**
+   * Example of a particle object on the stack, with NO DATA.
+   */
+
+  /**
+     however, conceptually we need to provide fake data. A stack without data does not
+     work...
+   */
+
+  struct NoData { /* nothing */
+    int nothing = 0;
+  };
+
+  template <typename StackIteratorInterface>
+  struct ParticleInterface : public corsika::ParticleBase<StackIteratorInterface> {
+
+    typedef corsika::ParticleBase<StackIteratorInterface> super_type;
+
+  public:
+    void setParticleData(const std::tuple<NoData>& /*v*/) {}
+    void setParticleData(super_type& /*parent*/, const std::tuple<NoData>& /*v*/) {}
+
+    std::string asString() const { return "dummy-data"; }
+  };
+
+  /**
+   *
+   * Memory implementation of the most simple (no-data) particle stack object.
+   */
+
+  class DummyStackImpl {
+
+  public:
+    DummyStackImpl() = default;
+
+    DummyStackImpl(DummyStackImpl const&) = default;
+
+    DummyStackImpl(DummyStackImpl&&) = default;
+
+    DummyStackImpl& operator=(DummyStackImpl const&) = default;
+    DummyStackImpl& operator=(DummyStackImpl&&) = default;
+
+    void init() { entries_ = 0; }
+
+    inline void clear() { entries_ = 0; }
+
+    inline int getSize() const { return entries_; }
+    inline 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*/) {}
+
+    inline void incrementSize() { entries_++; }
+    inline void decrementSize() { entries_--; }
+
+    inline int getEntries() const { return entries_; }
+    inline void setEntries(int entries = 0) { entries_ = entries; }
+
+  private:
+    int entries_ = 0;
+
+  }; // end class DummyStackImpl
+
+  typedef Stack<DummyStackImpl, ParticleInterface> DummyStack;
+
+} // namespace corsika::dummy_stack
diff --git a/corsika/stack/GeometryNodeStackExtension.hpp b/corsika/stack/GeometryNodeStackExtension.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..b3a15854f58af04c8bae6ceecb0ba9e25644343e
--- /dev/null
+++ b/corsika/stack/GeometryNodeStackExtension.hpp
@@ -0,0 +1,126 @@
+/*
+ * (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/core/Logging.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+
+#include <tuple>
+#include <utility>
+#include <vector>
+
+namespace corsika::node {
+
+  /**
+   * Describe "volume node" data on a Stack.
+   *
+   * Corresponding defintion of a stack-readout object, the iteractor
+   * dereference operator will deliver access to these function
+   * defintion of a stack-readout object, the iteractor dereference
+   * operator will deliver access to these function
+   */
+
+  /**
+   * \todo fixme: Document type T
+   */
+  template <typename T, typename TEnvType>
+  struct GeometryDataInterface : public T {
+
+    typedef T super_type;
+
+  public:
+    typedef typename TEnvType::BaseNodeType node_type;
+
+    // default version for particle-creation from input data
+    inline 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*>) {
+      setNode(parent.getNode()); // copy Node from parent particle!
+    }
+    inline void setParticleData() { setNode(nullptr); }
+    inline void setParticleData(GeometryDataInterface& parent) {
+      setNode(parent.getNode()); // copy Node from parent particle!
+    }
+
+    inline std::string asString() const {
+      return fmt::format("node={}", fmt::ptr(getNode()));
+    }
+
+    inline void setNode(node_type const* v) {
+
+      super_type::getStackData().setNode(super_type::getIndex(), v);
+    }
+
+    inline node_type const* getNode() const {
+      return super_type::getStackData().getNode(super_type::getIndex());
+    }
+  };
+
+  // definition of stack-data object to store geometry information
+
+  /**
+   * @class GeometryData
+   *
+   * definition of stack-data object to store geometry information
+   */
+  template <typename TEnvType>
+  class GeometryData {
+
+  public:
+    typedef typename TEnvType::BaseNodeType node_type;
+    typedef std::vector<const node_type*> node_vector_type;
+
+    GeometryData() = default;
+
+    GeometryData(GeometryData<TEnvType> const&) = default;
+
+    GeometryData(GeometryData<TEnvType>&&) = default;
+
+    GeometryData<TEnvType>& operator=(GeometryData<TEnvType> const&) = default;
+
+    GeometryData<TEnvType>& operator=(GeometryData<TEnvType>&&) = default;
+
+    // these functions are needed for the Stack interface
+    inline void clear() { node_vector_.clear(); }
+
+    inline unsigned int getSize() const { return node_vector_.size(); }
+
+    inline unsigned int getCapacity() const { return node_vector_.size(); }
+
+    inline void copy(const int i1, const int i2) { node_vector_[i2] = node_vector_[i1]; }
+
+    inline 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; }
+
+    inline 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); }
+
+    inline void decrementSize() {
+      if (node_vector_.size() > 0) { node_vector_.pop_back(); }
+    }
+
+    // custom private data section
+  private:
+    node_vector_type node_vector_;
+  };
+
+  template <typename T, typename TEnv>
+  struct MakeGeometryDataInterface {
+    typedef GeometryDataInterface<T, TEnv> type;
+  };
+
+} // namespace corsika::node
diff --git a/corsika/stack/NuclearStackExtension.hpp b/corsika/stack/NuclearStackExtension.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..5d8d573ed35b35b79fa6770461c922c61150a1ea
--- /dev/null
+++ b/corsika/stack/NuclearStackExtension.hpp
@@ -0,0 +1,221 @@
+/*
+ * (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/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+#include <corsika/stack/VectorStack.hpp>
+
+#include <algorithm>
+#include <tuple>
+#include <vector>
+
+namespace corsika::nuclear_stack {
+
+  /**
+   *
+   * Define ParticleInterface for NuclearStackExtension Stack derived from
+   * ParticleInterface of Inner stack class
+   *
+   * Add A and Z data to existing stack (currently VectorStack) of particle
+   * properties. This is done via inheritance, not via CombinedStack since the nuclear
+   * data is stored ONLY when needed (for nuclei) and not for all particles. Thus, this is
+   * a new, derived Stack object.
+   *
+   * Only for Code::Nucleus particles A and Z are stored, not for all
+   * normal elementary particles.
+   *
+   * Thus in your code, make sure to always check <code>
+   * particle.getPID()==Code::Nucleus </code> before attempting to
+   * read any nuclear information.
+   */
+  template <template <typename> class InnerParticleInterface,
+            typename StackIteratorInterface>
+  struct NuclearParticleInterface
+      : public InnerParticleInterface<StackIteratorInterface> {
+
+    typedef InnerParticleInterface<StackIteratorInterface> super_type;
+
+  public:
+    typedef std::tuple<Code, HEPEnergyType, MomentumVector, Point, TimeType>
+        particle_data_type;
+
+    typedef std::tuple<Code, HEPEnergyType, MomentumVector, Point, TimeType,
+                       unsigned short, unsigned short>
+        altenative_particle_data_type;
+
+    inline void setParticleData(particle_data_type const& v);
+
+    inline void setParticleData(altenative_particle_data_type const& v);
+
+    inline void setParticleData(super_type& p, particle_data_type const& v);
+
+    inline void setParticleData(super_type& p, altenative_particle_data_type const& v);
+
+    inline std::string asString() const;
+
+    /**
+     * @name individual setters
+     * @{
+     */
+    inline void setNuclearA(const unsigned short vA) {
+      super_type::getStackData().setNuclearA(super_type::getIndex(), vA);
+    }
+    inline void setNuclearZ(const unsigned short vZ) {
+      super_type::getStackData().setNuclearZ(super_type::getIndex(), vZ);
+    }
+    /// @}
+
+    /**
+     * @name individual getters
+     * @{
+     */
+    inline int getNuclearA() const {
+      return super_type::getStackData().getNuclearA(super_type::getIndex());
+    }
+    inline int getNuclearZ() const {
+      return super_type::getStackData().getNuclearZ(super_type::getIndex());
+    }
+    /// @}
+
+    /**
+     * Overwrite normal getParticleMass function with nuclear version
+     */
+    inline HEPMassType getMass() const;
+    /**
+     * Overwirte normal getChargeNumber function with nuclear version
+     **/
+    inline int16_t getChargeNumber() const;
+
+    inline int getNucleusRef() const {
+      return super_type::getStackData().getNucleusRef(super_type::getIndex());
+    } // LCOV_EXCL_LINE
+
+  protected:
+    inline void setNucleusRef(const int vR) {
+      super_type::getStackData().setNucleusRef(super_type::getIndex(), vR);
+    }
+
+    inline bool isNucleus() const {
+      return super_type::getStackData().isNucleus(super_type::getIndex());
+    }
+  };
+
+  /**
+   * @class NuclearStackExtension
+   *
+   * Memory implementation of adding nuclear inforamtion to the
+   * existing particle stack defined in class InnerStackImpl.
+   *
+   * Inside the NuclearStackExtension class there is a dedicated
+   * fNucleusRef index, where fNucleusRef[i] is referring to the
+   * correct A and Z for a specific particle index i. fNucleusRef[i]
+   * == -1 means that this is not a nucleus, and a subsequent call to
+   * getNucleusA would produce an exception.
+   */
+  template <typename InnerStackImpl>
+  class NuclearStackExtensionImpl : public InnerStackImpl {
+
+    typedef InnerStackImpl super_type;
+
+  public:
+    typedef std::vector<int> nucleus_ref_type;
+    typedef std::vector<unsigned short> nuclear_a_type;
+    typedef std::vector<unsigned short> nuclear_z_type;
+
+    NuclearStackExtensionImpl() = default;
+
+    NuclearStackExtensionImpl(NuclearStackExtensionImpl<InnerStackImpl> const&) = default;
+
+    NuclearStackExtensionImpl(NuclearStackExtensionImpl<InnerStackImpl>&&) = default;
+
+    NuclearStackExtensionImpl<InnerStackImpl>& operator=(
+        NuclearStackExtensionImpl<InnerStackImpl> const&) = default;
+
+    NuclearStackExtensionImpl<InnerStackImpl>& operator=(
+        NuclearStackExtensionImpl<InnerStackImpl>&&) = default;
+
+    void init() { super_type::init(); }
+
+    void dump() { super_type::dump(); }
+
+    inline 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) {
+      nuclearA_[getNucleusRef(i)] = vA;
+    }
+
+    inline 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; }
+
+    inline int getNuclearA(const unsigned int i) const {
+      return nuclearA_[getNucleusRef(i)];
+    }
+
+    inline 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();
+
+    inline int getNucleusRef(const unsigned int i) const;
+
+    inline 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);
+    /**
+     *   Function to copy particle at location i2 in stack to i1
+     */
+    inline void swap(const unsigned int i1, const unsigned int i2);
+
+    inline void incrementSize();
+
+    inline void decrementSize();
+
+  private:
+    /// the actual memory to store particle data
+
+    nucleus_ref_type nucleusRef_;
+    nuclear_a_type nuclearA_;
+    nuclear_z_type nuclearZ_;
+
+  }; // end class NuclearStackExtensionImpl
+
+  template <typename TInnerStack, template <typename> typename PI_>
+  using NuclearStackExtension =
+      Stack<NuclearStackExtensionImpl<typename TInnerStack::stack_implementation_type>,
+            PI_>;
+
+  //
+  template <typename TStackIter>
+  using ExtendedParticleInterfaceType =
+      NuclearParticleInterface<VectorStack::pi_type, TStackIter>;
+
+  // the particle data stack with extra nuclear information:
+  using ParticleDataStack =
+      NuclearStackExtension<VectorStack, ExtendedParticleInterfaceType>;
+
+} // namespace corsika::nuclear_stack
+
+#include <corsika/detail/stack/NuclearStackExtension.inl>
diff --git a/corsika/stack/VectorStack.hpp b/corsika/stack/VectorStack.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7877469ee39fa4d3f8f3326bd56c112e61da0267
--- /dev/null
+++ b/corsika/stack/VectorStack.hpp
@@ -0,0 +1,176 @@
+/*
+ * (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/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+
+#include <string>
+#include <tuple>
+#include <vector>
+
+namespace corsika {
+
+  /**
+   * Example of a particle object on the stack.
+   */
+
+  template <typename StackIteratorInterface>
+  class ParticleInterface : public ParticleBase<StackIteratorInterface> {
+
+  private:
+    typedef ParticleBase<StackIteratorInterface> super_type;
+
+  public:
+    std::string asString() const {
+      return fmt::format("particle: i={}, PID={}, E={}GeV", super_type::getIndex(),
+                         get_name(this->getPID()), this->getEnergy() / 1_GeV);
+    }
+
+    void setParticleData(
+        std::tuple<Code, HEPEnergyType, MomentumVector, Point, TimeType> const& v);
+
+    void setParticleData(
+        ParticleInterface<StackIteratorInterface> const&,
+        std::tuple<Code, HEPEnergyType, MomentumVector, Point, TimeType> const& v);
+
+    /// individual setters
+    void setPID(Code const id) {
+      super_type::getStackData().setPID(super_type::getIndex(), id);
+    }
+    void setEnergy(HEPEnergyType const& e) {
+      super_type::getStackData().setEnergy(super_type::getIndex(), e);
+    }
+    void setMomentum(MomentumVector const& v) {
+      super_type::getStackData().setMomentum(super_type::getIndex(), v);
+    }
+    void setPosition(Point const& v) {
+      super_type::getStackData().setPosition(super_type::getIndex(), v);
+    }
+    void setTime(TimeType const& v) {
+      super_type::getStackData().setTime(super_type::getIndex(), v);
+    }
+
+    /// individual getters
+    Code getPID() const {
+      return super_type::getStackData().getPID(super_type::getIndex());
+    }
+    HEPEnergyType getEnergy() const {
+      return super_type::getStackData().getEnergy(super_type::getIndex());
+    }
+    MomentumVector getMomentum() const {
+      return super_type::getStackData().getMomentum(super_type::getIndex());
+    }
+    Point getPosition() const {
+      return super_type::getStackData().getPosition(super_type::getIndex());
+    }
+    TimeType getTime() const {
+      return super_type::getStackData().getTime(super_type::getIndex());
+    }
+    /**
+     * @name derived quantities
+     *
+     * @{
+     */
+    DirectionVector getDirection() const {
+      return this->getMomentum() / this->getEnergy();
+    }
+
+    HEPMassType getMass() const { return get_mass(this->getPID()); }
+
+    int16_t getChargeNumber() const { return get_charge_number(this->getPID()); }
+    ///@}
+  };
+
+  /**
+   * Memory implementation of the most simple (stupid) particle stack object.
+   *
+   */
+
+  class VectorStackImpl {
+
+  public:
+    typedef std::vector<Code> code_vector_type;
+    typedef std::vector<HEPEnergyType> energy_vector_type;
+    typedef std::vector<Point> point_vector_type;
+    typedef std::vector<TimeType> time_vector_type;
+    typedef std::vector<MomentumVector> momentum_vector_type;
+
+    VectorStackImpl() = default;
+
+    VectorStackImpl(VectorStackImpl const& other) = default;
+
+    VectorStackImpl(VectorStackImpl&& other) = default;
+
+    VectorStackImpl& operator=(VectorStackImpl const& other) = default;
+
+    VectorStackImpl& operator=(VectorStackImpl&& other) = default;
+
+    void dump() const {}
+
+    inline void clear();
+
+    unsigned int getSize() const { return dataPID_.size(); }
+    unsigned int getCapacity() const { return dataPID_.size(); }
+
+    void setPID(size_t i, Code const id) { dataPID_[i] = id; }
+    void setEnergy(size_t i, HEPEnergyType const& e) { dataE_[i] = e; }
+    void setMomentum(size_t i, MomentumVector const& v) { momentum_[i] = v; }
+    void setPosition(size_t i, Point const& v) { position_[i] = v; }
+    void setTime(size_t i, TimeType const& v) { time_[i] = v; }
+
+    Code getPID(size_t i) const { return dataPID_[i]; }
+
+    HEPEnergyType getEnergy(size_t i) const { return dataE_[i]; }
+
+    MomentumVector getMomentum(size_t i) const { return momentum_[i]; }
+
+    Point getPosition(size_t i) const { return position_[i]; }
+    TimeType getTime(size_t i) const { return time_[i]; }
+
+    HEPEnergyType getDataE(size_t i) const { return dataE_[i]; }
+
+    void setDataE(size_t i, HEPEnergyType const& dataE) { dataE_[i] = dataE; }
+
+    Code getDataPid(size_t i) const { return dataPID_[i]; }
+
+    void setDataPid(size_t i, Code const& dataPid) { dataPID_[i] = dataPid; }
+    /**
+     *   Function to copy particle at location i2 in stack to i1
+     */
+    inline 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);
+
+    inline void incrementSize();
+    inline void decrementSize();
+
+  private:
+    /// the actual memory to store particle data
+    code_vector_type dataPID_;
+    energy_vector_type dataE_;
+    momentum_vector_type momentum_;
+    point_vector_type position_;
+    time_vector_type time_;
+
+  }; // end class VectorStackImpl
+
+  typedef Stack<VectorStackImpl, ParticleInterface> VectorStack;
+
+} // namespace corsika
+
+#include <corsika/detail/stack/VectorStack.inl>
diff --git a/Stack/History/CMakeLists.txt b/corsika/stack/history/CMakeLists.txt
similarity index 100%
rename from Stack/History/CMakeLists.txt
rename to corsika/stack/history/CMakeLists.txt
diff --git a/Stack/History/Event.hpp b/corsika/stack/history/Event.hpp
similarity index 80%
rename from Stack/History/Event.hpp
rename to corsika/stack/history/Event.hpp
index 901d9db8f7c327f1ff4dab9f931114a5a1dc7b69..4ac1ccdc8107f383ff1f0788e596d6e7f64df8ad 100644
--- a/Stack/History/Event.hpp
+++ b/corsika/stack/history/Event.hpp
@@ -8,8 +8,8 @@
 
 #pragma once
 
-#include <corsika/logging/Logging.h>
-#include <corsika/particles/ParticleProperties.h>
+#include <corsika/framework/core/Logging.hpp>
+#include <corsika/framework/core/ParticleProperties.hpp>
 #include <corsika/stack/history/EventType.hpp>
 #include <corsika/stack/history/SecondaryParticle.hpp>
 
@@ -30,7 +30,7 @@ namespace corsika::history {
 
     EventType type_ = EventType::Uninitialized;
 
-    std::optional<corsika::particles::Code> targetCode_;
+    std::optional<Code> targetCode_;
 
   public:
     Event() = default;
@@ -44,16 +44,15 @@ namespace corsika::history {
     void setProjectileIndex(size_t i) { projectile_index_ = i; }
     size_t projectileIndex() const { return projectile_index_; }
 
-    size_t addSecondary(units::si::HEPEnergyType energy,
-                        geometry::Vector<units::si::hepmomentum_d> const& momentum,
-                        particles::Code pid) {
+    size_t addSecondary(HEPEnergyType energy, Vector<hepmomentum_d> const& momentum,
+                        Code pid) {
       secondaries_.emplace_back(energy, momentum, pid);
       return secondaries_.size() - 1;
     }
 
     std::vector<SecondaryParticle> const& secondaries() const { return secondaries_; }
 
-    void setTargetCode(const particles::Code t) { targetCode_ = t; }
+    void setTargetCode(const Code t) { targetCode_ = t; }
 
     std::string as_string() const {
       return fmt::format("hasParent={}, projIndex={}, Nsec={}", hasParentEvent(),
diff --git a/Stack/History/EventType.hpp b/corsika/stack/history/EventType.hpp
similarity index 100%
rename from Stack/History/EventType.hpp
rename to corsika/stack/history/EventType.hpp
diff --git a/corsika/stack/history/HistoryObservationPlane.hpp b/corsika/stack/history/HistoryObservationPlane.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9c97f8722218f24449ef32538be81ea2ac5c1056
--- /dev/null
+++ b/corsika/stack/history/HistoryObservationPlane.hpp
@@ -0,0 +1,50 @@
+/*
+ * (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 <corsika/geometry/Plane.h>
+#include <corsika/process/ContinuousProcess.h>
+#include <corsika/setup/SetupStack.h>
+#include <corsika/setup/SetupTrajectory.h>
+#include <corsika/units/PhysicalUnits.h>
+
+#include <boost/histogram.hpp>
+
+#include <functional>
+
+// the detail namespace: here the histrograms are defined
+//! \todo add options/parameters to 'detail::hist_factory()'
+#include <corsika/detail/stack/history/HistoryObservationPlane.hpp>
+
+namespace corsika::history {
+
+  class HistoryObservationPlane : public ContinuousProcess<HistoryObservationPlane> {
+  public:
+    HistoryObservationPlane(setup::Stack const&, Plane const&, bool = true);
+
+    LengthType getMaxStepLength(setup::Stack::particle_type const&,
+                                setup::Trajectory const& vTrajectory);
+
+    ProcessReturn doContinuous(setup::Stack::particle_type const& vParticle,
+                               setup::Trajectory const& vTrajectory);
+
+    auto const& histogram() const { return histogram_; }
+
+  private:
+    void fillHistoryHistogram(setup::Stack::particle_type const&);
+
+    setup::Stack const& stack_;
+    Plane const plane_;
+    bool const deleteOnHit_;
+
+    decltype(detail::hist_factory()) histogram_ = detail::hist_factory();
+  };
+} // namespace corsika::history
+
+#include <corsika/detail/stack/history/HistoryObservationPlane.inl>
diff --git a/Stack/History/HistorySecondaryProducer.hpp b/corsika/stack/history/HistorySecondaryProducer.hpp
similarity index 72%
rename from Stack/History/HistorySecondaryProducer.hpp
rename to corsika/stack/history/HistorySecondaryProducer.hpp
index 792a83d9538ee742c75b4474b1399edfd6139645..cf7ef2e4dfddd5dc6d582b53880b7ccc56656ec6 100644
--- a/Stack/History/HistorySecondaryProducer.hpp
+++ b/corsika/stack/history/HistorySecondaryProducer.hpp
@@ -8,10 +8,10 @@
 
 #pragma once
 
-#include <corsika/stack/SecondaryView.h>
+#include <corsika/framework/stack/SecondaryView.hpp>
 #include <corsika/stack/history/Event.hpp>
 
-#include <corsika/logging/Logging.h>
+#include <corsika/framework/core/Logging.hpp>
 
 #include <boost/type_index.hpp>
 
@@ -32,9 +32,9 @@ namespace corsika::history {
     template <typename Particle>
     HistorySecondaryProducer(Particle const& p)
         : event_{std::make_shared<Event>()} {
-      C8LOG_TRACE("HistorySecondaryProducer::HistorySecondaryProducer");
-      event_->setProjectileIndex(p.GetIndex());
-      event_->setParentEvent(p.GetEvent());
+      CORSIKA_LOG_TRACE("HistorySecondaryProducer::HistorySecondaryProducer");
+      event_->setProjectileIndex(p.getIndex());
+      event_->setParentEvent(p.getEvent());
     }
 
     /**
@@ -46,13 +46,13 @@ namespace corsika::history {
      */
     template <typename Particle>
     auto new_secondary(Particle& sec) {
-      C8LOG_TRACE("HistorySecondaryProducer::new_secondary(sec)");
+      CORSIKA_LOG_TRACE("HistorySecondaryProducer::new_secondary(sec)");
 
       // store particles at production time in Event here
       auto const sec_index =
-          event_->addSecondary(sec.GetEnergy(), sec.GetMomentum(), sec.GetPID());
-      sec.SetParentEventIndex(sec_index);
-      sec.SetEvent(event_);
+          event_->addSecondary(sec.getEnergy(), sec.getMomentum(), sec.getPID());
+      sec.setParentEventIndex(sec_index);
+      sec.setEvent(event_);
     }
   };
 
diff --git a/Stack/History/HistoryStackExtension.hpp b/corsika/stack/history/HistoryStackExtension.hpp
similarity index 60%
rename from Stack/History/HistoryStackExtension.hpp
rename to corsika/stack/history/HistoryStackExtension.hpp
index 67a38b79617b8a5a5dca62fa6485ff17926f927a..9d517b0acae18d541be59f86d3540238a28b8c9a 100644
--- a/Stack/History/HistoryStackExtension.hpp
+++ b/corsika/stack/history/HistoryStackExtension.hpp
@@ -8,8 +8,8 @@
 
 #pragma once
 
-#include <corsika/logging/Logging.h>
-#include <corsika/stack/Stack.h>
+#include <corsika/framework/core/Logging.hpp>
+#include <corsika/framework/stack/Stack.hpp>
 
 #include <memory>
 #include <utility>
@@ -33,29 +33,29 @@ namespace corsika::history {
 
   public:
     // these functions are needed for the Stack interface
-    void Clear() { historyData_.clear(); }
-    unsigned int GetSize() const { return historyData_.size(); }
-    unsigned int GetCapacity() const { return historyData_.size(); }
-    void Copy(const int i1, const int i2) { historyData_[i2] = historyData_[i1]; }
-    void Swap(const int i1, const int i2) {
+    void clear() { historyData_.clear(); }
+    unsigned int getSize() const { return historyData_.size(); }
+    unsigned int getCapacity() const { return historyData_.size(); }
+    void copy(const int i1, const int i2) { historyData_[i2] = historyData_[i1]; }
+    void swap(const int i1, const int i2) {
       std::swap(historyData_[i1], historyData_[i2]);
     }
 
     // custom data access function
-    void SetEvent(const int i, EventPtr v) { historyData_[i].first = std::move(v); }
-    const EventPtr& GetEvent(const int i) const { return historyData_[i].first; }
-    EventPtr& GetEvent(const int i) { return historyData_[i].first; }
+    void setEvent(const int i, EventPtr v) { historyData_[i].first = std::move(v); }
+    const EventPtr& getEvent(const int i) const { return historyData_[i].first; }
+    EventPtr& getEvent(const int i) { return historyData_[i].first; }
 
-    void SetParentEventIndex(const int i, ParentEventIndex v) {
+    void setParentEventIndex(const int i, ParentEventIndex v) {
       historyData_[i].second = std::move(v);
     }
-    ParentEventIndex GetParentEventIndex(const int i) const {
+    ParentEventIndex getParentEventIndex(const int i) const {
       return historyData_[i].second;
     }
 
     // these functions are also needed by the Stack interface
-    void IncrementSize() { historyData_.push_back(DataType{}); }
-    void DecrementSize() {
+    void incrementSize() { historyData_.push_back(DataType{}); }
+    void decrementSize() {
       if (historyData_.size() > 0) { historyData_.pop_back(); }
     }
 
@@ -75,44 +75,44 @@ namespace corsika::history {
   template <typename T, typename TEvent>
   class HistoryDataInterface : public T {
   protected:
-    using T::GetStack;
-    using T::GetStackData;
+    using T::getStack;
+    using T::getStackData;
 
   public:
-    using T::GetIndex;
+    using T::getIndex;
 
   public:
     // create a new particle from scratch
-    void SetParticleData() {
-      C8LOG_TRACE("HistoyDatatInterface::SetParticleData()");
-      GetStackData().SetParentEventIndex(GetIndex(), -1);
+    void setParticleData() {
+      CORSIKA_LOG_TRACE("HistoyDatatInterface::setParticleData()");
+      getStackData().setParentEventIndex(getIndex(), -1);
     }
 
     // create a new particle as secondary of a parent
-    void SetParticleData(HistoryDataInterface& /*parent*/) {
-      C8LOG_TRACE("HistoyDatatInterface::SetParticleData(parnt)");
-      SetParticleData();
+    void setParticleData(HistoryDataInterface& /*parent*/) {
+      CORSIKA_LOG_TRACE("HistoyDatatInterface::setParticleData(parnt)");
+      setParticleData();
     }
 
-    void SetEvent(const std::shared_ptr<TEvent>& v) {
-      GetStackData().SetEvent(GetIndex(), v);
+    void setEvent(const std::shared_ptr<TEvent>& v) {
+      getStackData().setEvent(getIndex(), v);
     }
 
-    void SetParentEventIndex(int index) {
-      GetStackData().SetParentEventIndex(GetIndex(), index);
+    void setParentEventIndex(int index) {
+      getStackData().setParentEventIndex(getIndex(), index);
     }
 
-    std::shared_ptr<TEvent> GetEvent() const {
-      return GetStackData().GetEvent(GetIndex());
+    std::shared_ptr<TEvent> getEvent() const {
+      return getStackData().getEvent(getIndex());
     }
 
-    int GetParentEventIndex() const {
-      return GetStackData().GetParentEventIndex(GetIndex());
+    int getParentEventIndex() const {
+      return getStackData().getParentEventIndex(getIndex());
     }
 
-    std::string as_string() const {
-      return fmt::format("i_parent={}, [evt: {}]", GetParentEventIndex(),
-                         (bool(GetEvent()) ? GetEvent()->as_string() : "n/a"));
+    std::string asString() const {
+      return fmt::format("i_parent={}, [evt: {}]", getParentEventIndex(),
+                         (bool(getEvent()) ? getEvent()->as_string() : "n/a"));
     }
   };
 
diff --git a/Stack/History/SecondaryParticle.hpp b/corsika/stack/history/SecondaryParticle.hpp
similarity index 59%
rename from Stack/History/SecondaryParticle.hpp
rename to corsika/stack/history/SecondaryParticle.hpp
index 249ea95bf97dec8313621e7d69a05ca4646db115..4a36abf37f50eb5ce61f5f7e5cd8f16720ea7893 100644
--- a/Stack/History/SecondaryParticle.hpp
+++ b/corsika/stack/history/SecondaryParticle.hpp
@@ -8,9 +8,9 @@
 
 #pragma once
 
-#include <corsika/geometry/Vector.h>
-#include <corsika/particles/ParticleProperties.h>
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
 
 #include <vector>
 
@@ -21,14 +21,12 @@ namespace corsika::history {
    * other (common) properties are available via the event itself or its projectile.
    */
   struct SecondaryParticle {
-    units::si::HEPEnergyType const energy_;
-    geometry::Vector<units::si::hepmomentum_d> const momentum_;
-    particles::Code const pid_;
+    HEPEnergyType const energy_;
+    Vector<hepmomentum_d> const momentum_;
+    Code const pid_;
 
   public:
-    SecondaryParticle(units::si::HEPEnergyType energy,
-                      geometry::Vector<units::si::hepmomentum_d> momentum,
-                      particles::Code pid)
+    SecondaryParticle(HEPEnergyType energy, Vector<hepmomentum_d> momentum, Code pid)
         : energy_{energy}
         , momentum_{momentum}
         , pid_{pid} {}
diff --git a/do-clang-format.py b/do-clang-format.py
index e3406502846f41ffed9f73aba88e7d828851bb8a..076a5ccdf4c207ff448aef2453fa835233c0ff28 100755
--- a/do-clang-format.py
+++ b/do-clang-format.py
@@ -8,6 +8,7 @@ import argparse
 import subprocess as subp
 import os
 import sys
+import re
 
 parser = argparse.ArgumentParser(description=__doc__)
 parser.add_argument('--apply', action="store_true",
@@ -17,18 +18,20 @@ parser.add_argument("--all", action="store_true",
 
 args = parser.parse_args()
 
+excludeDirs = [r"^(\./)?modules/", r"^(\./)?externals/", r"^(\./)?build", r"^(\./)?install", r"(\./)?\.git", r"^(\./)?corsika/framework/units",]
+
 filelist = []
 if args.all:
     for dirpath, dirnames, filenames in os.walk("."):
-        doExclude = False
-        for exclude in ["ThirdParty", "PROPOSAL", "include", "build"]:
-            if exclude in dirpath:
-                doExclude = True
+        excl = False
+        for excl_dir in excludeDirs:
+            if re.findall(excl_dir, dirpath):
+                excl = True
                 break
-        if doExclude:
+        if excl:
             continue
         for f in filenames:
-            if f.endswith(".h") or f.endswith(".cc") or f.endswith(".hpp") or f.endswith(".cpp") or f.endswith(".cxx"):
+            if (f.endswith(".hpp") or f.endswith(".cpp") or f.endswith(".inl")):
                 filename = os.path.join(dirpath, f)
                 if not os.path.islink(filename):
                     filelist.append(filename)
@@ -45,17 +48,33 @@ else:
             filelist.append(line[1:].lstrip())
 
     cmd = "git ls-files --exclude-standard --others"
-    filelist += subp.check_output(cmd, shell=True).decode("utf8").strip().split("\n")
-    filelist = [x for x in filelist
-                if "ThirdParty" not in x and (x.endswith(".h") or x.endswith(".cc"))]
+    filelist2 = subp.check_output(cmd, shell=True).decode("utf8").strip().split("\n")
+    filelist += filelist2
+    # some cleanup
+    filelist_clean = []
+    for f in filelist:
+        if not (f.endswith(".hpp") or f.endswith(".cpp") or f.endswith(".inl")):
+            continue
+        if os.path.islink(f):
+            continue
+        excl = False
+        for excl_dir in excludeDirs:
+            if re.findall(excl_dir, f):
+                excl = True
+                break
+        if excl:
+            continue
+        filelist_clean.append(f)
+    filelist = filelist_clean
 
 cmd = "clang-format"
 if "CLANG_FORMAT" in os.environ:
   cmd = os.environ["CLANG_FORMAT"]
 cmd +=  " -style=file"
 if args.apply:
-    for filename in filelist:
+    for filename in filelist:        
         subp.check_call(cmd.split() + ["-i", filename])
+
 else:
     # only print files which need formatting
     files_need_formatting = 0
diff --git a/do-copyright.py b/do-copyright.py
index adc278d1124eafdc943d65937ecdb9f552933a40..7e0caf2d12af351f224dacb91a3cb0f45516ead5 100755
--- a/do-copyright.py
+++ b/do-copyright.py
@@ -21,10 +21,10 @@ Debug settings are 0: nothing, 1: checking, 2: filesystem
 """
 Debug = 0 
 
-excludeDirs = ["modules", "git", "build", "install", "externals"]
+excludeDirs = ["./modules", "./externals", "build", "install", "git", "framework/units"]
 excludeFiles = ['PhysicalConstants.h','CorsikaFenvOSX.cc', 'sgn.h', 'quartic.h']
 
-extensions = [".cpp", ".hpp"]
+extensions = [".cpp", ".inl", ".hpp"]
 
 """
 justCheck: T: only checking, F: also changing files 
@@ -194,7 +194,7 @@ def next_file(dir_name, files, justCheck, forYear, updateMessage):
     for check in excludeDirs :
         if check in dir_name:
             if Debug>1:
-                print ("exclude-dir: " + check)
+                print ("exclude-dir: " + check, dir_name)
             return True
     for check in files :
         if (os.path.isdir(check)):
diff --git a/Documentation/Doxygen/CMakeLists.txt b/documentation/CMakeLists.txt
similarity index 100%
rename from Documentation/Doxygen/CMakeLists.txt
rename to documentation/CMakeLists.txt
diff --git a/Documentation/Doxygen/Doxyfile.in b/documentation/Doxyfile.in
similarity index 59%
rename from Documentation/Doxygen/Doxyfile.in
rename to documentation/Doxyfile.in
index b733d09258adfa7e7200033acda18823a8c8ca9d..1378999e9fd140e792d576fdceefce4556bff5e4 100644
--- a/Documentation/Doxygen/Doxyfile.in
+++ b/documentation/Doxyfile.in
@@ -1,17 +1,18 @@
 PROJECT_NAME           = CORSIKA 8
 PROJECT_NUMBER         = 0.0.0
+PROJECT_BRIEF          = "The framework to simulate particle cascades for astroparticle physics"
 
 GENERATE_HTML          = YES
 GENERATE_LATEX         = YES
 GENERATE_XML           = YES
 
 OUTPUT_DIRECTORY       = @CMAKE_CURRENT_BINARY_DIR@/
-INPUT                  = @PROJECT_SOURCE_DIR@ @PROJECT_BINARY_DIR@/Framework
-EXCLUDE_PATTERNS       = */ThirdParty/*/* */build*/corsika/*
+INPUT                  = @PROJECT_SOURCE_DIR@/corsika @PROJECT_SOURCE_DIR@/src
+EXCLUDE_PATTERNS       = *.inl
 FULL_PATH_NAMES        = YES
 STRIP_FROM_PATH        = @PROJECT_SOURCE_DIR@
 
-FILE_PATTERNS          = *.cc *.cpp *.cxx *.h *.hpp *.dox *.inc *.md
+FILE_PATTERNS          = *.cpp *.hpp *.dox *.md
 EXTENSION_MAPPING      = inc=C++
 RECURSIVE              = YES
 
@@ -29,6 +30,17 @@ INCLUDE_GRAPH          = YES
 GRAPHICAL_HIERARCHY    = YES
 DIRECTORY_GRAPH        = YES
 GENERATE_LEGEND        = YES
+
 USE_MATHJAX            = YES
+MATHJAX_EXTENSIONS     = TeX/AMSmath TeX/AMSsymbols
+
+#JAVADOC_BANNER         = YES 
+JAVADOC_AUTOBRIEF      = YES
+REPEAT_BRIEF          = YES
+ABBREVIATE_BRIEF       = YES
+MULTILINE_CPP_IS_BRIEF  = YES
+MARKDOWN_SUPPORT = YES
+BUILTIN_STL_SUPPORT = YES
+CLANG_ASSISTED_PARSING = YES
 
 SEARCHENGINE           = YES
diff --git a/corsika.dox b/documentation/corsika.dox
similarity index 100%
rename from corsika.dox
rename to documentation/corsika.dox
diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..a9a16a6e7f1d2a66020f709a26f4c4488d1ab2c4
--- /dev/null
+++ b/examples/CMakeLists.txt
@@ -0,0 +1,52 @@
+add_executable (helix_example helix_example.cpp)
+target_link_libraries (helix_example CORSIKA8)
+CORSIKA_REGISTER_EXAMPLE (helix_example)
+
+add_executable (geometry_example geometry_example.cpp)
+target_link_libraries (geometry_example 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)
+CORSIKA_REGISTER_EXAMPLE (stack_example)
+
+add_executable (cascade_example cascade_example.cpp)
+target_link_libraries (cascade_example CORSIKA8)
+CORSIKA_REGISTER_EXAMPLE (cascade_example)
+
+add_executable (boundary_example boundary_example.cpp)
+target_link_libraries (boundary_example CORSIKA8)
+CORSIKA_REGISTER_EXAMPLE (boundary_example)
+
+add_executable (cascade_proton_example cascade_proton_example.cpp)
+target_link_libraries (cascade_proton_example 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.)
+
+add_executable (stopping_power stopping_power.cpp)
+target_link_libraries (stopping_power CORSIKA8)
+CORSIKA_REGISTER_EXAMPLE (stopping_power)
+
+add_executable (staticsequence_example staticsequence_example.cpp)
+target_link_libraries (staticsequence_example CORSIKA8)
+CORSIKA_REGISTER_EXAMPLE (staticsequence_example)
+
+add_executable (particle_list_example particle_list_example.cpp)
+target_link_libraries (particle_list_example CORSIKA8)
+CORSIKA_REGISTER_EXAMPLE (particle_list_example)
+
+add_executable (em_shower em_shower.cpp)
+target_link_libraries (em_shower CORSIKA8)
+CORSIKA_REGISTER_EXAMPLE (em_shower RUN_OPTIONS "100.")
+
+add_executable (hybrid_MC hybrid_MC.cpp)
+target_link_libraries (hybrid_MC  CORSIKA8)
+CORSIKA_REGISTER_EXAMPLE (hybrid_MC RUN_OPTIONS 4 2 10000.)
diff --git a/examples/boundary_example.cpp b/examples/boundary_example.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..bca45481a3841f690e80ea5d936063a4ffe91acf
--- /dev/null
+++ b/examples/boundary_example.cpp
@@ -0,0 +1,177 @@
+/*
+ * (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/core/Cascade.hpp>
+#include <corsika/framework/process/ProcessSequence.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/utility/CorsikaFenv.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <corsika/setup/SetupEnvironment.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/media/UniformMagneticField.hpp>
+#include <corsika/media/MediumPropertyModel.hpp>
+
+#include <corsika/modules/Sibyll.hpp>
+#include <corsika/modules/TrackWriter.hpp>
+#include <corsika/modules/ParticleCut.hpp>
+
+/*
+  NOTE, WARNING, ATTENTION
+
+  The .../Random.hpp implement the hooks of external modules to the C8 random
+  number generator. It has to occur excatly ONCE per linked
+  executable. If you include the header below multiple times and
+  link this togehter, it will fail.
+ */
+#include <corsika/modules/sibyll/Random.hpp>
+#include <corsika/modules/urqmd/Random.hpp>
+
+#include <iostream>
+#include <limits>
+#include <typeinfo>
+
+using namespace corsika;
+using namespace std;
+
+template <bool deleteParticle>
+struct MyBoundaryCrossingProcess
+    : public BoundaryCrossingProcess<MyBoundaryCrossingProcess<deleteParticle>> {
+
+  MyBoundaryCrossingProcess(std::string const& filename) { file_.open(filename); }
+
+  template <typename Particle>
+  ProcessReturn doBoundaryCrossing(Particle& p, typename Particle::node_type const& from,
+                                   typename Particle::node_type const& to) {
+
+    CORSIKA_LOG_INFO("MyBoundaryCrossingProcess: crossing! from: {} to: {} ",
+                     fmt::ptr(&from), fmt::ptr(&to));
+
+    auto const& name = get_name(p.getPID());
+    auto const start = p.getPosition().getCoordinates();
+
+    file_ << name << "    " << start[0] / 1_m << ' ' << start[1] / 1_m << ' '
+          << start[2] / 1_m << '\n';
+
+    if constexpr (deleteParticle) { p.erase(); }
+
+    return ProcessReturn::Ok;
+  }
+
+private:
+  std::ofstream file_;
+};
+
+//
+// The example main program for a particle cascade
+//
+int main() {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  CORSIKA_LOG_INFO("boundary_example");
+
+  feenableexcept(FE_INVALID);
+  // initialize random number sequence(s)
+  RNGManager::getInstance().registerRandomStream("cascade");
+
+  // setup environment, geometry
+  using EnvType = setup::Environment;
+  EnvType env;
+  auto& universe = *(env.getUniverse());
+
+  CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
+
+  // create "world" as infinite sphere filled with protons
+  auto world = EnvType::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 100_km);
+
+  using MyHomogeneousModel = MediumPropertyModel<
+      UniformMagneticField<HomogeneousMedium<setup::EnvironmentInterface>>>;
+
+  auto const props = world->setModelProperties<MyHomogeneousModel>(
+      Medium::AirDry1Atm, Vector(rootCS, 0_T, 0_T, 0_T), 1_kg / (1_m * 1_m * 1_m),
+      NuclearComposition(std::vector<Code>{Code::Proton}, std::vector<float>{1.f}));
+
+  // add a "target" sphere with 5km readius at 0,0,0
+  auto target = EnvType::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 5_km);
+  target->setModelProperties(props);
+
+  world->addChild(std::move(target));
+  universe.addChild(std::move(world));
+
+  // setup processes, decays and interactions
+  setup::Tracking tracking;
+
+  RNGManager::getInstance().registerRandomStream("sibyll");
+  corsika::sibyll::Interaction sibyll;
+  corsika::sibyll::Decay decay;
+
+  ParticleCut cut(50_GeV, true, true);
+
+  TrackWriter trackWriter("boundary_tracks.dat");
+  MyBoundaryCrossingProcess<true> boundaryCrossing("crossings.dat");
+
+  // assemble all processes into an ordered process list
+  auto sequence = make_sequence(sibyll, decay, cut, boundaryCrossing, trackWriter);
+
+  // setup particle stack, and add primary particles
+  setup::Stack stack;
+  stack.clear();
+  const Code beamCode = Code::MuPlus;
+  const HEPMassType mass = get_mass(beamCode);
+  const HEPEnergyType E0 = 100_GeV;
+
+  std::uniform_real_distribution distTheta(0., 180.);
+  std::uniform_real_distribution distPhi(0., 360.);
+  std::mt19937 rng;
+
+  for (int i = 0; i < 100; ++i) {
+    double const theta = distTheta(rng);
+    double const phi = distPhi(rng);
+
+    auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) {
+      return sqrt((Elab - m) * (Elab + m));
+    };
+    HEPMomentumType P0 = elab2plab(E0, mass);
+    auto momentumComponents = [](double theta, double phi, HEPMomentumType ptot) {
+      return std::make_tuple(ptot * sin(theta) * cos(phi), ptot * sin(theta) * sin(phi),
+                             -ptot * cos(theta));
+    };
+    auto const [px, py, pz] =
+        momentumComponents(theta / 180. * M_PI, phi / 180. * M_PI, P0);
+    auto plab = MomentumVector(rootCS, {px, py, pz});
+    CORSIKA_LOG_INFO(
+        "input particle: {} "
+        "input angles: theta={} phi={}"
+        "input momentum: {} GeV",
+        beamCode, theta, phi, plab.getComponents() / 1_GeV);
+    // shoot particles from inside target out
+    Point pos(rootCS, 0_m, 0_m, 0_m);
+    stack.addParticle(std::make_tuple(beamCode, E0, plab, pos, 0_ns));
+  }
+
+  // define air shower object, run simulation
+  Cascade EAS(env, tracking, sequence, stack);
+
+  EAS.run();
+
+  CORSIKA_LOG_INFO("Result: E0={}GeV", E0 / 1_GeV);
+  cut.showResults();
+  [[maybe_unused]] const HEPEnergyType Efinal =
+      (cut.getCutEnergy() + cut.getInvEnergy() + cut.getEmEnergy());
+  CORSIKA_LOG_INFO("Total energy (GeV): {} relative difference (%): {}", Efinal / 1_GeV,
+                   (Efinal / E0 - 1.) * 100);
+}
diff --git a/examples/cascade_example.cpp b/examples/cascade_example.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d14843c51730d8c05dd2e8d46c7374ba5e4224b7
--- /dev/null
+++ b/examples/cascade_example.cpp
@@ -0,0 +1,166 @@
+/*
+ * (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/core/Cascade.hpp>
+#include <corsika/framework/process/ProcessSequence.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+
+#include <corsika/framework/utility/CorsikaFenv.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/media/ShowerAxis.hpp>
+#include <corsika/media/MediumPropertyModel.hpp>
+#include <corsika/media/UniformMagneticField.hpp>
+
+#include <corsika/setup/SetupEnvironment.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <corsika/modules/BetheBlochPDG.hpp>
+#include <corsika/modules/StackInspector.hpp>
+#include <corsika/modules/Sibyll.hpp>
+#include <corsika/modules/ParticleCut.hpp>
+#include <corsika/modules/TrackWriter.hpp>
+
+/*
+  NOTE, WARNING, ATTENTION
+
+  The .../Random.hpp implement the hooks of external modules to the C8 random
+  number generator. It has to occur excatly ONCE per linked
+  executable. If you include the header below multiple times and
+  link this togehter, it will fail.
+ */
+#include <corsika/modules/sibyll/Random.hpp>
+#include <corsika/modules/urqmd/Random.hpp>
+
+#include <iostream>
+#include <limits>
+
+using namespace corsika;
+using namespace std;
+
+//
+// The example main program for a particle cascade
+//
+int main() {
+
+  // logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  logging::set_level(logging::level::trace);
+
+  std::cout << "cascade_example" << std::endl;
+
+  const LengthType height_atmosphere = 112.8_km;
+
+  feenableexcept(FE_INVALID);
+  // initialize random number sequence(s)
+  RNGManager::getInstance().registerRandomStream("cascade");
+
+  // setup environment, geometry
+  setup::Environment env;
+  auto& universe = *(env.getUniverse());
+
+  CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
+
+  auto world =
+      setup::Environment::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 150_km);
+
+  using MyHomogeneousModel = MediumPropertyModel<
+      UniformMagneticField<HomogeneousMedium<setup::EnvironmentInterface>>>;
+
+  // fraction of oxygen
+  float const fox = 0.20946;
+  auto const props = world->setModelProperties<MyHomogeneousModel>(
+      Medium::AirDry1Atm, MagneticFieldVector(rootCS, 0_T, 0_T, 0_T),
+      1_kg / (1_m * 1_m * 1_m),
+      NuclearComposition(std::vector<Code>{Code::Nitrogen, Code::Oxygen},
+                         std::vector<float>{1.f - fox, fox}));
+
+  auto innerMedium =
+      setup::Environment::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 5000_m);
+
+  innerMedium->setModelProperties(props);
+  world->addChild(std::move(innerMedium));
+  universe.addChild(std::move(world));
+
+  // setup particle stack, and add primary particle
+  setup::Stack stack;
+  stack.clear();
+  const Code beamCode = Code::Nucleus;
+  const int nuclA = 4;
+  const int nuclZ = int(nuclA / 2.15 + 0.7);
+  const HEPMassType mass = get_nucleus_mass(nuclA, nuclZ);
+  const HEPEnergyType E0 = nuclA * 1_TeV;
+  double theta = 0.;
+  double phi = 0.;
+
+  Point const injectionPos(
+      rootCS, 0_m, 0_m,
+      height_atmosphere); // this is the CORSIKA 7 start of atmosphere/universe
+
+  ShowerAxis const showerAxis{injectionPos, Vector{rootCS, 0_m, 0_m, -100_km}, env};
+
+  {
+    auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) {
+      return sqrt((Elab - m) * (Elab + m));
+    };
+    HEPMomentumType P0 = elab2plab(E0, mass);
+    auto momentumComponents = [](double theta, double phi, HEPMomentumType ptot) {
+      return std::make_tuple(ptot * sin(theta) * cos(phi), ptot * sin(theta) * sin(phi),
+                             -ptot * cos(theta));
+    };
+    auto const [px, py, pz] =
+        momentumComponents(theta / 180. * M_PI, phi / 180. * M_PI, P0);
+    auto plab = MomentumVector(rootCS, {px, py, pz});
+    cout << "input particle: " << beamCode << endl;
+    cout << "input angles: theta=" << theta << " phi=" << phi << endl;
+    cout << "input momentum: " << plab.getComponents() / 1_GeV << endl;
+    stack.addParticle(
+        std::make_tuple(beamCode, E0, plab, injectionPos, 0_ns, nuclA, nuclZ));
+  }
+
+  // setup processes, decays and interactions
+  setup::Tracking tracking;
+  StackInspector<setup::Stack> stackInspect(1, true, E0);
+
+  RNGManager::getInstance().registerRandomStream("sibyll");
+  RNGManager::getInstance().registerRandomStream("pythia");
+  corsika::sibyll::Interaction sibyll;
+  corsika::sibyll::NuclearInteraction sibyllNuc(sibyll, env);
+  corsika::sibyll::Decay decay;
+  // cascade with only HE model ==> HE cut
+  ParticleCut cut(80_GeV, true, true);
+
+  TrackWriter trackWriter("tracks.dat");
+  BetheBlochPDG eLoss{showerAxis, cut.getECut()};
+
+  // assemble all processes into an ordered process list
+  auto sequence =
+      make_sequence(stackInspect, sibyll, sibyllNuc, decay, eLoss, cut, trackWriter);
+
+  // define air shower object, run simulation
+  Cascade EAS(env, tracking, sequence, stack);
+
+  EAS.run();
+
+  eLoss.printProfile(); // print longitudinal profile
+
+  cut.showResults();
+  const HEPEnergyType Efinal =
+      cut.getCutEnergy() + cut.getInvEnergy() + cut.getEmEnergy();
+  cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl
+       << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl;
+  cout << "total dEdX energy (GeV): " << eLoss.getTotal() / 1_GeV << endl
+       << "relative difference (%): " << eLoss.getTotal() / E0 * 100 << endl;
+  cut.reset();
+}
diff --git a/examples/cascade_proton_example.cpp b/examples/cascade_proton_example.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..003dc85584ebbe6911348b74109471fda5b3c096
--- /dev/null
+++ b/examples/cascade_proton_example.cpp
@@ -0,0 +1,151 @@
+/*
+ * (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/core/Cascade.hpp>
+#include <corsika/framework/process/ProcessSequence.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+
+#include <corsika/framework/utility/CorsikaFenv.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/media/ShowerAxis.hpp>
+#include <corsika/media/MediumPropertyModel.hpp>
+#include <corsika/media/UniformMagneticField.hpp>
+
+#include <corsika/setup/SetupEnvironment.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <corsika/modules/BetheBlochPDG.hpp>
+#include <corsika/modules/StackInspector.hpp>
+#include <corsika/modules/Sibyll.hpp>
+#include <corsika/modules/ParticleCut.hpp>
+#include <corsika/modules/TrackWriter.hpp>
+#include <corsika/modules/HadronicElasticModel.hpp>
+#include <corsika/modules/Pythia8.hpp>
+
+/*
+  NOTE, WARNING, ATTENTION
+
+  The .../Random.hpppp implement the hooks of external modules to the C8 random
+  number generator. It has to occur excatly ONCE per linked
+  executable. If you include the header below multiple times and
+  link this togehter, it will fail.
+ */
+#include <corsika/modules/sibyll/Random.hpp>
+#include <corsika/modules/urqmd/Random.hpp>
+
+#include <iostream>
+#include <limits>
+#include <typeinfo>
+
+using namespace corsika;
+using namespace std;
+
+//
+// The example main program for a particle cascade
+//
+int main() {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  std::cout << "cascade_proton_example" << std::endl;
+
+  feenableexcept(FE_INVALID);
+  // initialize random number sequence(s)
+  RNGManager::getInstance().registerRandomStream("cascade");
+
+  // setup environment, geometry
+  using EnvType = setup::Environment;
+  EnvType env;
+  auto& universe = *(env.getUniverse());
+  CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
+
+  auto world = EnvType::createNode<Sphere>(Point{rootCS, 0_m, 0_m, 0_m}, 150_km);
+
+  using MyHomogeneousModel = MediumPropertyModel<
+      UniformMagneticField<HomogeneousMedium<setup::EnvironmentInterface>>>;
+
+  world->setModelProperties<MyHomogeneousModel>(
+      Medium::AirDry1Atm, MagneticFieldVector(rootCS, 0_T, 0_T, 1_T),
+      1_kg / (1_m * 1_m * 1_m),
+      NuclearComposition(std::vector<Code>{Code::Hydrogen},
+                         std::vector<float>{(float)1.}));
+
+  universe.addChild(std::move(world));
+
+  // setup particle stack, and add primary particle
+  setup::Stack stack;
+  stack.clear();
+  const Code beamCode = Code::Proton;
+  const HEPMassType mass = Proton::mass;
+  const HEPEnergyType E0 = 100_GeV;
+  double theta = 0.;
+  double phi = 0.;
+
+  Point injectionPos(rootCS, 0_m, 0_m, 0_m);
+  {
+    auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) {
+      return sqrt(Elab * Elab - m * m);
+    };
+    HEPMomentumType P0 = elab2plab(E0, mass);
+    auto momentumComponents = [](double theta, double phi, HEPMomentumType ptot) {
+      return std::make_tuple(ptot * sin(theta) * cos(phi), ptot * sin(theta) * sin(phi),
+                             -ptot * cos(theta));
+    };
+    auto const [px, py, pz] =
+        momentumComponents(theta / 180. * M_PI, phi / 180. * M_PI, P0);
+    auto plab = MomentumVector(rootCS, {px, py, pz});
+    cout << "input particle: " << beamCode << endl;
+    cout << "input angles: theta=" << theta << " phi=" << phi << endl;
+    cout << "input momentum: " << plab.getComponents() / 1_GeV << endl;
+    stack.addParticle(std::make_tuple(beamCode, E0, plab, injectionPos, 0_ns));
+  }
+
+  // setup processes, decays and interactions
+  setup::Tracking tracking;
+  StackInspector<setup::Stack> stackInspect(1, true, E0);
+
+  RNGManager::getInstance().registerRandomStream("sibyll");
+  RNGManager::getInstance().registerRandomStream("pythia");
+  //  sibyll::Interaction sibyll(env);
+  corsika::pythia8::Interaction pythia;
+  //  sibyll::NuclearInteraction sibyllNuc(env, sibyll);
+  //  sibyll::Decay decay;
+  corsika::pythia8::Decay decay;
+  ParticleCut cut(60_GeV, true, true);
+
+  // RNGManager::getInstance().registerRandomStream("HadronicElasticModel");
+  // HadronicElasticModel::HadronicElasticInteraction
+  // hadronicElastic(env);
+
+  TrackWriter trackWriter("tracks.dat");
+  ShowerAxis const showerAxis{injectionPos, Vector{rootCS, 0_m, 0_m, -100_km}, env};
+  BetheBlochPDG eLoss{showerAxis, cut.getECut()};
+
+  // assemble all processes into an ordered process list
+  // auto sequence = sibyll << decay << hadronicElastic << cut << trackWriter;
+  auto sequence = make_sequence(pythia, decay, eLoss, cut, trackWriter, stackInspect);
+
+  // define air shower object, run simulation
+  Cascade EAS(env, tracking, sequence, stack);
+  EAS.run();
+
+  cout << "Result: E0=" << E0 / 1_GeV << endl;
+  cut.showResults();
+  const HEPEnergyType Efinal =
+      cut.getCutEnergy() + cut.getInvEnergy() + cut.getEmEnergy();
+  cout << "total energy (GeV): " << Efinal / 1_GeV << endl
+       << "relative difference (%): " << (Efinal / E0 - 1.) * 100 << endl;
+}
diff --git a/examples/em_shower.cpp b/examples/em_shower.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..855fc5a388ed59e7e1b037cf077c6ca580feac5e
--- /dev/null
+++ b/examples/em_shower.cpp
@@ -0,0 +1,186 @@
+/*
+ * (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.
+ */
+
+#include <corsika/framework/core/Cascade.hpp>
+#include <corsika/framework/geometry/Plane.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+#include <corsika/framework/process/ProcessSequence.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/utility/CorsikaFenv.hpp>
+#include <corsika/framework/process/InteractionCounter.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/LayeredSphericalAtmosphereBuilder.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/media/ShowerAxis.hpp>
+#include <corsika/media/MediumPropertyModel.hpp>
+#include <corsika/media/UniformMagneticField.hpp>
+
+#include <corsika/modules/LongitudinalProfile.hpp>
+#include <corsika/modules/ObservationPlane.hpp>
+#include <corsika/modules/ParticleCut.hpp>
+#include <corsika/modules/TrackWriter.hpp>
+#include <corsika/modules/PROPOSAL.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <iomanip>
+#include <iostream>
+#include <limits>
+#include <string>
+#include <typeinfo>
+
+/*
+  NOTE, WARNING, ATTENTION
+
+  The .../Random.hpppp implement the hooks of external modules to the C8 random
+  number generator. It has to occur excatly ONCE per linked
+  executable. If you include the header below multiple times and
+  link this togehter, it will fail.
+ */
+#include <corsika/modules/sibyll/Random.hpp>
+#include <corsika/modules/urqmd/Random.hpp>
+
+using namespace corsika;
+using namespace std;
+
+void registerRandomStreams() {
+  RNGManager::getInstance().registerRandomStream("cascade");
+  RNGManager::getInstance().registerRandomStream("proposal");
+  RNGManager::getInstance().seedAll();
+}
+
+template <typename T>
+using MyExtraEnv = MediumPropertyModel<UniformMagneticField<T>>;
+
+int main(int argc, char** argv) {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  if (argc != 2) {
+    std::cerr << "usage: em_shower <energy/GeV>" << std::endl;
+    return 1;
+  }
+  feenableexcept(FE_INVALID);
+  // initialize random number sequence(s)
+  registerRandomStreams();
+
+  // setup environment, geometry
+  using EnvType = setup::Environment;
+  EnvType env;
+  CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
+  Point const center{rootCS, 0_m, 0_m, 0_m};
+  auto builder = make_layered_spherical_atmosphere_builder<
+      setup::EnvironmentInterface, MyExtraEnv>::create(center,
+                                                       constants::EarthRadius::Mean,
+                                                       Medium::AirDry1Atm,
+                                                       Vector{rootCS, 0_T, 50_uT, 0_T});
+  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);
+  builder.assemble(env);
+
+  // setup particle stack, and add primary particle
+  setup::Stack stack;
+  stack.clear();
+  const Code beamCode = Code::Electron;
+  auto const mass = get_mass(beamCode);
+  const HEPEnergyType E0 = 1_GeV * std::stof(std::string(argv[1]));
+  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 = 1.4_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;
+
+  stack.addParticle(std::make_tuple(beamCode, E0, plab, injectionPos, 0_ns));
+
+  std::cout << "shower axis length: " << (showerCore - injectionPos).getNorm() * 1.02
+            << std::endl;
+
+  ShowerAxis const showerAxis{injectionPos, (showerCore - injectionPos) * 1.02, env};
+
+  // setup processes, decays and interactions
+
+  // PROPOSAL processs proposal{...};
+  ParticleCut cut(10_GeV, false, true);
+  corsika::proposal::Interaction proposal(env, cut.getECut());
+  corsika::proposal::ContinuousProcess em_continuous(env, cut.getECut());
+  InteractionCounter proposalCounted(proposal);
+
+  TrackWriter trackWriter("tracks.dat");
+
+  // long. profile; columns for gamma, e+, e- still need to be added
+  LongitudinalProfile longprof{showerAxis};
+
+  Plane const obsPlane(showerCore, DirectionVector(rootCS, {0., 0., 1.}));
+  ObservationPlane observationLevel(obsPlane, DirectionVector(rootCS, {1., 0., 0.}),
+                                    "particles.dat");
+
+  auto sequence = make_sequence(proposalCounted, em_continuous, longprof, cut,
+                                observationLevel, trackWriter);
+  // define air shower object, run simulation
+  setup::Tracking tracking;
+  Cascade EAS(env, tracking, sequence, stack);
+
+  // to fix the point of first interaction, uncomment the following two lines:
+  //  EAS.setNodes();
+  //  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 = proposalCounted.getHistogram();
+  hists.saveLab("inthist_lab_emShower.npz");
+  hists.saveCMS("inthist_cms_emShower.npz");
+  longprof.save("longprof_emShower.txt");
+}
diff --git a/examples/geometry_example.cpp b/examples/geometry_example.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7a25708a8d434b8b35e89a8cd23b1e4ad35e0f1f
--- /dev/null
+++ b/examples/geometry_example.cpp
@@ -0,0 +1,84 @@
+/*
+ * (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/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <cstdlib>
+#include <typeinfo>
+
+using namespace corsika;
+
+int main() {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  CORSIKA_LOG_INFO("geometry_example");
+
+  // define the root coordinate system
+  CoordinateSystemPtr const& root = get_root_CoordinateSystem();
+
+  // another CS defined by a translation relative to the root CS
+  CoordinateSystemPtr cs2 = make_translation(root, {0_m, 0_m, 1_m});
+
+  // rotations are possible, too; parameters are axis vector and angle
+  CoordinateSystemPtr cs3 =
+      make_rotation(root, QuantityVector<length_d>{1_m, 0_m, 0_m}, 90 * degree_angle);
+
+  // now let's define some geometrical objects:
+  Point const p1(root, {0_m, 0_m, 0_m}); // the origin of the root CS
+  Point const p2(cs2, {0_m, 0_m, 0_m});  // the origin of cs2
+
+  Vector<length_d> const diff =
+      p2 -
+      p1; // the distance between the points, basically the translation vector given above
+  auto const norm = diff.getSquaredNorm(); // squared length with the right dimension
+
+  // print the components of the vector as given in the different CS
+  CORSIKA_LOG_INFO(
+      "p2-p1 components in root: {} \n"
+      "p2-p1 components in cs2: {} \n"
+      "p2-p1 components in cs3: {}\n"
+      "p2-p1 norm^2: {} \n",
+      diff.getComponents(root), diff.getComponents(cs2), diff.getComponents(cs3), norm);
+  assert(norm == 1 * meter * meter);
+
+  Sphere s(p1, 10_m); // define a sphere around a point with a radius
+  CORSIKA_LOG_INFO("p1 inside s:{} ", s.contains(p2));
+  assert(s.contains(p2) == 1);
+
+  Sphere s2(p1, 3_um); // another sphere
+  CORSIKA_LOG_INFO("p1 inside s2: {}", s2.contains(p2));
+  assert(s2.contains(p2) == 0);
+
+  // let's try parallel projections:
+  auto const v1 = Vector<length_d>(root, {1_m, 1_m, 0_m});
+  auto const v2 = Vector<length_d>(root, {1_m, 0_m, 0_m});
+
+  auto const v3 = v1.getParallelProjectionOnto(v2);
+
+  // cross product
+  auto const cross =
+      v1.cross(v2).normalized(); // normalized() returns dimensionless, normalized vectors
+
+  // if a CS is not given as parameter for getComponents(), the components
+  // in the "home" CS are returned
+  CORSIKA_LOG_INFO(
+      "v1: {} \n"
+      "v2: {}\n "
+      "parallel projection of v1 onto v2:  {} \n"
+      "normalized cross product of v1 x v2 {} \n",
+      v1.getComponents(), v2.getComponents(), v3.getComponents(), cross.getComponents());
+
+  return EXIT_SUCCESS;
+}
diff --git a/Documentation/Examples/helix_example.cc b/examples/helix_example.cpp
similarity index 61%
rename from Documentation/Examples/helix_example.cc
rename to examples/helix_example.cpp
index 9f3eba67e610c87f63f193b3db3aad5978eda4fc..cf58531b4170aa5b74a3693033b71c700cde102e 100644
--- a/Documentation/Examples/helix_example.cc
+++ b/examples/helix_example.cpp
@@ -6,25 +6,26 @@
  * the license.
  */
 
-#include <corsika/geometry/Helix.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/framework/geometry/Helix.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
 #include <array>
 #include <cstdlib>
 #include <iostream>
 
 using namespace corsika;
-using namespace corsika::geometry;
-using namespace corsika::units::si;
 
 int main() {
 
-  std::cout << "helix_example" << std::endl;
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  CORSIKA_LOG_INFO("helix_example");
 
-  geometry::CoordinateSystem& root =
-      geometry::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem();
+  CoordinateSystemPtr const& root = get_root_CoordinateSystem();
 
   Point const r0(root, {0_m, 0_m, 0_m});
   auto const omegaC = 2 * M_PI * 1_Hz;
@@ -42,7 +43,7 @@ int main() {
   auto& positions = *arr;
 
   for (auto [t, i] = std::tuple{t0, 0}; t < t1; t += dt, ++i) {
-    auto const r = h.GetPosition(t).GetCoordinates();
+    auto const r = h.getPosition(t).getCoordinates();
 
     positions[i][0] = t / 1_s;
     positions[i][1] = r[0] / 1_m;
@@ -50,8 +51,8 @@ int main() {
     positions[i][3] = r[2] / 1_m;
   }
 
-  std::cout << positions[n - 2][0] << " " << positions[n - 2][1] << " "
-            << positions[n - 2][2] << " " << positions[n - 2][3] << std::endl;
+  CORSIKA_LOG_INFO("test: {} {} {} {} ", positions[n - 2][0], positions[n - 2][1],
+                   positions[n - 2][2], positions[n - 2][3]);
 
   return EXIT_SUCCESS;
 }
diff --git a/examples/hybrid_MC.cpp b/examples/hybrid_MC.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b4eab698c94feb5a182ea7c068272eb050b88289
--- /dev/null
+++ b/examples/hybrid_MC.cpp
@@ -0,0 +1,280 @@
+/*
+ * (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.
+ */
+
+/* clang-format off */
+// InteractionCounter used boost/histogram, which
+// fails if boost/type_traits have been included before. Thus, we have
+// to include it first...
+#include <corsika/framework/process/InteractionCounter.hpp>
+/* clang-format on */
+#include <corsika/framework/geometry/Plane.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+#include <corsika/framework/core/Logging.hpp>
+#include <corsika/framework/process/ProcessSequence.hpp>
+#include <corsika/framework/process/SwitchProcessSequence.hpp>
+#include <corsika/framework/process/InteractionCounter.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/utility/CorsikaFenv.hpp>
+#include <corsika/framework/core/Cascade.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/FlatExponential.hpp>
+#include <corsika/media/LayeredSphericalAtmosphereBuilder.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/media/MediumPropertyModel.hpp>
+#include <corsika/media/UniformMagneticField.hpp>
+#include <corsika/media/ShowerAxis.hpp>
+
+#include <corsika/modules/BetheBlochPDG.hpp>
+#include <corsika/modules/LongitudinalProfile.hpp>
+#include <corsika/modules/ObservationPlane.hpp>
+#include <corsika/modules/OnShellCheck.hpp>
+#include <corsika/modules/ParticleCut.hpp>
+#include <corsika/modules/Pythia8.hpp>
+#include <corsika/modules/Sibyll.hpp>
+#include <corsika/modules/UrQMD.hpp>
+#include <corsika/modules/PROPOSAL.hpp>
+#include <corsika/modules/CONEX.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <iomanip>
+#include <iostream>
+#include <limits>
+#include <string>
+
+/*
+  NOTE, WARNING, ATTENTION
+
+  The .../Random.hpppp implement the hooks of external modules to the C8 random
+  number generator. It has to occur excatly ONCE per linked
+  executable. If you include the header below multiple times and
+  link this togehter, it will fail.
+ */
+#include <corsika/modules/sibyll/Random.hpp>
+#include <corsika/modules/urqmd/Random.hpp>
+
+using namespace corsika;
+using namespace std;
+
+void registerRandomStreams(const int seed) {
+  RNGManager::getInstance().registerRandomStream("cascade");
+  RNGManager::getInstance().registerRandomStream("qgsjet");
+  RNGManager::getInstance().registerRandomStream("sibyll");
+  RNGManager::getInstance().registerRandomStream("pythia");
+  RNGManager::getInstance().registerRandomStream("urqmd");
+  RNGManager::getInstance().registerRandomStream("proposal");
+
+  if (seed == 0)
+    RNGManager::getInstance().seedAll();
+  else
+    RNGManager::getInstance().seedAll(seed);
+}
+
+template <typename T>
+using MyExtraEnv = MediumPropertyModel<UniformMagneticField<T>>;
+
+int main(int argc, char** argv) {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  CORSIKA_LOG_INFO("hybrid_MC");
+
+  if (argc < 4) {
+    std::cerr << "usage: hybrid_MC <A> <Z> <energy/GeV> [seed]" << std::endl;
+    std::cerr << "       if no seed is given, a random seed is chosen" << std::endl;
+    return 1;
+  }
+  feenableexcept(FE_INVALID);
+
+  int seed = 0;
+  if (argc > 4) seed = std::stoi(std::string(argv[4]));
+  // initialize random number sequence(s)
+  registerRandomStreams(seed);
+
+  // setup environment, geometry
+  using EnvType = setup::Environment;
+  EnvType env;
+  CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
+  Point const center{rootCS, 0_m, 0_m, 0_m};
+  auto builder = make_layered_spherical_atmosphere_builder<
+      setup::EnvironmentInterface, MyExtraEnv>::create(center,
+                                                       constants::EarthRadius::Mean,
+                                                       Medium::AirDry1Atm,
+                                                       Vector{rootCS, 0_T, 50_uT, 0_T});
+  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);
+  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 +
+      Vector<dimensionless_d>{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 {
+    stack.addParticle(std::make_tuple(Code::Proton, E0, plab, injectionPos, 0_ns));
+  }
+
+  std::cout << "shower axis length: " << (showerCore - injectionPos).getNorm() * 1.02
+            << std::endl;
+
+  ShowerAxis const showerAxis{injectionPos, (showerCore - injectionPos) * 1.02, 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();
+
+  ParticleCut cut{60_GeV, false, true};
+  BetheBlochPDG eLoss{showerAxis, cut.getECut()};
+
+  CONEXhybrid conex_model(center, showerAxis, t, injectionHeight, E0,
+                          get_PDG(Code::Proton));
+
+  OnShellCheck reset_particle_mass(1.e-3, 1.e-1, false);
+
+  LongitudinalProfile longprof{showerAxis};
+
+  Plane const obsPlane(showerCore, DirectionVector(rootCS, {0., 0., 1.}));
+  ObservationPlane observationLevel(obsPlane, DirectionVector(rootCS, {1., 0., 0.}),
+                                    "particles.dat");
+
+  corsika::urqmd::UrQMD urqmd_model;
+  InteractionCounter urqmdCounted{urqmd_model};
+
+  // assemble all processes into an ordered process list
+  struct EnergySwitch {
+    HEPEnergyType cutE_;
+    EnergySwitch(HEPEnergyType cutE)
+        : cutE_(cutE) {}
+    SwitchResult operator()(const setup::Stack::particle_type& 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(hadronSequence, reset_particle_mass, decaySequence, eLoss,
+                                cut, conex_model, longprof, observationLevel);
+
+  // define air shower object, run simulation
+  setup::Tracking tracking;
+  Cascade EAS(env, tracking, sequence, stack);
+
+  // to fix the point of first interaction, uncomment the following two lines:
+  //  EAS.SetNodes();
+  //  EAS.forceInteraction();
+
+  EAS.run();
+
+  cut.showResults();
+  eLoss.showResults();
+  observationLevel.showResults();
+  const HEPEnergyType Efinal = cut.getCutEnergy() + cut.getInvEnergy() +
+                               cut.getEmEnergy() + eLoss.getEnergyLost() +
+                               observationLevel.getEnergyGround();
+  cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl
+       << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl;
+  observationLevel.reset();
+  cut.reset();
+  eLoss.reset();
+
+  conex_model.solveCE();
+
+  auto const hists = sibyllCounted.getHistogram() + sibyllNucCounted.getHistogram() +
+                     urqmdCounted.getHistogram();
+
+  hists.saveLab("inthist_lab.txt");
+  hists.saveCMS("inthist_cms.txt");
+
+  hists.saveLab("inthist_lab.txt");
+  hists.saveCMS("inthist_cms.txt");
+
+  longprof.save("longprof.txt");
+
+  std::ofstream finish("finished");
+  finish << "run completed without error" << std::endl;
+}
diff --git a/examples/particle_list_example.cpp b/examples/particle_list_example.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4b8d1d75770743d92e7ad2051ce05bb25d1156c9
--- /dev/null
+++ b/examples/particle_list_example.cpp
@@ -0,0 +1,71 @@
+/*
+ * (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/core/ParticleProperties.hpp>
+#include <corsika/modules/QGSJetII.hpp>
+#include <corsika/modules/Sibyll.hpp>
+#include <corsika/setup/SetupEnvironment.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+/*
+  NOTE, WARNING, ATTENTION
+
+  The .../Random.hpp implement the hooks of external modules to the C8 random
+  number generator. It has to occur excatly ONCE per linked
+  executable. If you include the header below multiple times and
+  link this togehter, it will fail.
+ */
+#include <corsika/modules/sibyll/Random.hpp>
+#include <corsika/modules/urqmd/Random.hpp>
+
+#include <iomanip>
+#include <string>
+
+using namespace corsika;
+using namespace std;
+
+//
+// The example main program for a particle list
+//
+int main() {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  logging::info(
+      "------------------------------------------\n"
+      "particles in CORSIKA\n"
+      "------------------------------------------\n"
+      "Name                | "
+      "PDG-id     | "
+      "SIBYLL-id  | "
+      "QGSJETII-id| "
+      "PDG-mass (GeV)   | "
+      "SIBYLL-mass (GeV)|\n"
+      "{:-}",
+      "", 104);
+  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,
+                    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}", "");
+}
diff --git a/examples/stack_example.cpp b/examples/stack_example.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..30187c520a4d0badbef4126f5e192ffd5906702f
--- /dev/null
+++ b/examples/stack_example.cpp
@@ -0,0 +1,54 @@
+/*
+ * (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/core/ParticleProperties.hpp>
+#include <corsika/stack/VectorStack.hpp>
+
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+
+#include <cassert>
+#include <iomanip>
+#include <iostream>
+
+using namespace corsika;
+using namespace std;
+
+void fill(VectorStack& s) {
+  CoordinateSystemPtr const& rootCS = get_root_CoordinateSystem();
+  for (int i = 0; i < 11; ++i) {
+    s.addParticle(std::make_tuple(Code::Electron, 1.5_GeV * i,
+                                  MomentumVector(rootCS, {0_GeV, 0_GeV, 1_GeV}),
+                                  Point(rootCS, 0_m, 0_m, 0_m), 0_ns));
+  }
+}
+
+void read(VectorStack& s) {
+  assert(s.getEntries() == 11); // stack has 11 particles
+
+  HEPEnergyType total_energy;
+  int i = 0;
+  for (auto& p : s) {
+    total_energy += p.getEnergy();
+    // particles are electrons with 1.5 GeV energy times i
+    assert(p.getPID() == Code::Electron);
+    assert(p.getEnergy() == 1.5_GeV * (i++));
+  }
+}
+
+int main() {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  std::cout << "stack_example" << std::endl;
+  VectorStack s;
+  fill(s);
+  read(s);
+  return 0;
+}
diff --git a/Documentation/Examples/staticsequence_example.cc b/examples/staticsequence_example.cpp
similarity index 71%
rename from Documentation/Examples/staticsequence_example.cc
rename to examples/staticsequence_example.cpp
index 659a3d6c2d13e9f3840f80d162cb8dff3feadfac..d7b9e06b5051d8b8a721b87bba5c9407f3090a70 100644
--- a/Documentation/Examples/staticsequence_example.cc
+++ b/examples/staticsequence_example.cpp
@@ -10,15 +10,12 @@
 #include <iomanip>
 #include <iostream>
 
-#include <corsika/process/ProcessSequence.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
+#include <corsika/framework/process/ProcessSequence.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
 
 using namespace corsika;
-using namespace corsika::units::si;
-using namespace corsika::process;
 using namespace std;
 
 const int nData = 10;
@@ -27,9 +24,9 @@ class Process1 : public ContinuousProcess<Process1> {
 public:
   Process1() {}
   template <typename D, typename T>
-  EProcessReturn DoContinuous(D& d, T&) const {
+  ProcessReturn doContinuous(D& d, T&) const {
     for (int i = 0; i < nData; ++i) d.p[i] += 1;
-    return EProcessReturn::eOk;
+    return ProcessReturn::Ok;
   }
 };
 
@@ -38,9 +35,9 @@ public:
   Process2() {}
 
   template <typename D, typename T>
-  inline EProcessReturn DoContinuous(D& d, T&) const {
+  inline ProcessReturn doContinuous(D& d, T&) const {
     for (int i = 0; i < nData; ++i) d.p[i] -= 0.1 * i;
-    return EProcessReturn::eOk;
+    return ProcessReturn::Ok;
   }
 };
 
@@ -49,8 +46,8 @@ public:
   Process3() {}
 
   template <typename D, typename T>
-  inline EProcessReturn DoContinuous(D&, T&) const {
-    return EProcessReturn::eOk;
+  inline ProcessReturn doContinuous(D&, T&) const {
+    return ProcessReturn::Ok;
   }
 };
 
@@ -59,9 +56,9 @@ public:
   Process4(const double v)
       : fV(v) {}
   template <typename D, typename T>
-  inline EProcessReturn DoContinuous(D& d, T&) const {
+  inline ProcessReturn doContinuous(D& d, T&) const {
     for (int i = 0; i < nData; ++i) d.p[i] *= fV;
-    return EProcessReturn::eOk;
+    return ProcessReturn::Ok;
   }
 
 private:
@@ -84,7 +81,7 @@ void modular() {
   Process3 m3;      // * 1.0
   Process4 m4(1.5); // * 1.5
 
-  auto sequence = process::sequence(m1, m2, m3, m4);
+  auto sequence = make_sequence(m1, m2, m3, m4);
 
   DummyData particle;
   DummyTrajectory track;
@@ -93,7 +90,7 @@ void modular() {
 
   const int nEv = 10;
   for (int iEv = 0; iEv < nEv; ++iEv) {
-    sequence.DoContinuous(particle, track);
+    sequence.doContinuous(particle, track);
     for (int i = 0; i < nData; ++i) {
       check[i] += 1. - 0.1 * i;
       check[i] *= 1.5;
@@ -107,6 +104,9 @@ void modular() {
 
 int main() {
 
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
   std::cout << "staticsequence_example" << std::endl;
 
   modular();
diff --git a/Documentation/Examples/stopping_power.cc b/examples/stopping_power.cpp
similarity index 51%
rename from Documentation/Examples/stopping_power.cc
rename to examples/stopping_power.cpp
index 100594f6a0060a5b58ea5561586a0ee13af4d678..ef8252501703205734ed361e4667df9fabdcbbed 100644
--- a/Documentation/Examples/stopping_power.cc
+++ b/examples/stopping_power.cpp
@@ -6,53 +6,50 @@
  * the license.
  */
 
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/HomogeneousMedium.h>
-#include <corsika/environment/IMediumModel.h>
-#include <corsika/environment/ShowerAxis.h>
-#include <corsika/geometry/Sphere.h>
-#include <corsika/process/energy_loss/EnergyLoss.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/units/PhysicalUnits.h>
-#include <corsika/utl/CorsikaFenv.h>
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/ShowerAxis.hpp>
+
+#include <corsika/framework/geometry/Sphere.hpp>
+#include <corsika/modules/BetheBlochPDG.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/utility/CorsikaFenv.hpp>
 
 #include <fstream>
 #include <iostream>
 #include <limits>
 
 using namespace corsika;
-using namespace corsika::process;
-using namespace corsika::particles;
-using namespace corsika::geometry;
-using namespace corsika::environment;
-
 using namespace std;
-using namespace corsika::units::si;
 
 //
 // This example demonstrates the energy loss of muons as function of beta*gamma (=p/m)
 //
 int main() {
 
-  std::cout << "stopping_power" << std::endl;
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  CORSIKA_LOG_INFO("stopping_power");
 
   feenableexcept(FE_INVALID);
 
   // setup environment, geometry
   using EnvType = Environment<IMediumModel>;
   EnvType env;
-  env.GetUniverse()->SetModelProperties<HomogeneousMedium<IMediumModel>>(
-      1_g / cube(1_cm), NuclearComposition{{particles::Code::Unknown}, {1.f}});
+  env.getUniverse()->setModelProperties<HomogeneousMedium<IMediumModel>>(
+      1_g / cube(1_cm), NuclearComposition{{Code::Unknown}, {1.f}});
 
-  const CoordinateSystem& rootCS = env.GetCoordinateSystem();
+  CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
 
   Point const injectionPos(
       rootCS, 0_m, 0_m,
       112.8_km); // this is the CORSIKA 7 start of atmosphere/universe
 
-  environment::ShowerAxis showerAxis{injectionPos,
-                                     Vector<length_d>{rootCS, 0_m, 0_m, 1_m}, env};
-  process::energy_loss::EnergyLoss eLoss{showerAxis, 300_MeV};
+  ShowerAxis showerAxis{injectionPos, Vector<length_d>{rootCS, 0_m, 0_m, 1_m}, env};
+  BetheBlochPDG eLoss{showerAxis, 300_MeV};
 
   setup::Stack stack;
 
@@ -60,9 +57,9 @@ int main() {
   file << "# beta*gamma, dE/dX / eV/(g/cm²)" << std::endl;
 
   for (HEPEnergyType E0 = 300_MeV; E0 < 1_PeV; E0 *= 1.05) {
-    stack.Clear();
+    stack.clear();
     const Code beamCode = Code::MuPlus;
-    const HEPMassType mass = GetMass(beamCode);
+    const HEPMassType mass = get_mass(beamCode);
     double theta = 0.;
     double phi = 0.;
 
@@ -76,18 +73,15 @@ int main() {
     };
     auto const [px, py, pz] =
         momentumComponents(theta / 180. * M_PI, phi / 180. * M_PI, P0);
-    auto plab = corsika::stack::MomentumVector(rootCS, {px, py, pz});
+    auto plab = MomentumVector(rootCS, {px, py, pz});
     cout << "input particle: " << beamCode << endl;
     cout << "input angles: theta=" << theta << " phi=" << phi << endl;
-    cout << "input momentum: " << plab.GetComponents() / 1_GeV << endl;
+    cout << "input momentum: " << plab.getComponents() / 1_GeV << endl;
 
-    stack.AddParticle(
-        std::tuple<particles::Code, units::si::HEPEnergyType,
-                   corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
-            beamCode, E0, plab, injectionPos, 0_ns});
+    stack.addParticle(std::make_tuple(beamCode, E0, plab, injectionPos, 0_ns));
 
-    auto const p = stack.GetNextParticle();
-    HEPEnergyType dE = eLoss.TotalEnergyLoss(p, 1_g / square(1_cm));
+    auto const p = stack.getNextParticle();
+    HEPEnergyType dE = eLoss.getTotalEnergyLoss(p, 1_g / square(1_cm));
     file << P0 / mass << "\t" << -dE / 1_eV << std::endl;
   }
 }
diff --git a/examples/vertical_EAS.cpp b/examples/vertical_EAS.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7d0a97d062940867454349e88f9520769f4ad0fd
--- /dev/null
+++ b/examples/vertical_EAS.cpp
@@ -0,0 +1,287 @@
+/*
+ * (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.
+ */
+
+/* clang-format off */
+// InteractionCounter used boost/histogram, which
+// fails if boost/type_traits have been included before. Thus, we have
+// to include it first...
+#include <corsika/framework/process/InteractionCounter.hpp>
+/* clang-format on */
+#include <corsika/framework/geometry/Plane.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+#include <corsika/framework/core/Logging.hpp>
+#include <corsika/framework/process/ProcessSequence.hpp>
+#include <corsika/framework/process/SwitchProcessSequence.hpp>
+#include <corsika/framework/process/InteractionCounter.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/utility/CorsikaFenv.hpp>
+#include <corsika/framework/core/Cascade.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/FlatExponential.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/IMagneticFieldModel.hpp>
+#include <corsika/media/LayeredSphericalAtmosphereBuilder.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/media/MediumPropertyModel.hpp>
+#include <corsika/media/UniformMagneticField.hpp>
+#include <corsika/media/ShowerAxis.hpp>
+#include <corsika/media/SlidingPlanarExponential.hpp>
+
+#include <corsika/modules/BetheBlochPDG.hpp>
+#include <corsika/modules/LongitudinalProfile.hpp>
+#include <corsika/modules/ObservationPlane.hpp>
+#include <corsika/modules/OnShellCheck.hpp>
+#include <corsika/modules/StackInspector.hpp>
+#include <corsika/modules/TrackWriter.hpp>
+#include <corsika/modules/ParticleCut.hpp>
+#include <corsika/modules/Pythia8.hpp>
+#include <corsika/modules/Sibyll.hpp>
+#include <corsika/modules/UrQMD.hpp>
+#include <corsika/modules/PROPOSAL.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <iomanip>
+#include <iostream>
+#include <limits>
+#include <string>
+
+/*
+  NOTE, WARNING, ATTENTION
+
+  The .../Random.hpppp implement the hooks of external modules to the C8 random
+  number generator. It has to occur excatly ONCE per linked
+  executable. If you include the header below multiple times and
+  link this togehter, it will fail.
+ */
+#include <corsika/modules/sibyll/Random.hpp>
+#include <corsika/modules/urqmd/Random.hpp>
+
+using namespace corsika;
+using namespace std;
+
+using Particle = setup::Stack::particle_type;
+
+void registerRandomStreams(const int seed) {
+  RNGManager::getInstance().registerRandomStream("cascade");
+  RNGManager::getInstance().registerRandomStream("qgsjet");
+  RNGManager::getInstance().registerRandomStream("sibyll");
+  RNGManager::getInstance().registerRandomStream("pythia");
+  RNGManager::getInstance().registerRandomStream("urqmd");
+  RNGManager::getInstance().registerRandomStream("proposal");
+
+  if (seed == 0)
+    RNGManager::getInstance().seedAll();
+  else
+    RNGManager::getInstance().seedAll(seed);
+}
+
+template <typename T>
+using MyExtraEnv = MediumPropertyModel<UniformMagneticField<T>>;
+
+int main(int argc, char** argv) {
+
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %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;
+    return 1;
+  }
+  feenableexcept(FE_INVALID);
+
+  int seed = 0;
+  if (argc > 4) seed = std::stoi(std::string(argv[4]));
+  // initialize random number sequence(s)
+  registerRandomStreams(seed);
+
+  // setup environment, geometry
+  using EnvType = setup::Environment;
+  EnvType env;
+  CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
+  Point const center{rootCS, 0_m, 0_m, 0_m};
+  auto builder = make_layered_spherical_atmosphere_builder<
+      setup::EnvironmentInterface, MyExtraEnv>::create(center,
+                                                       constants::EarthRadius::Mean,
+                                                       Medium::AirDry1Atm,
+                                                       MagneticFieldVector{rootCS, 0_T,
+                                                                           50_uT, 0_T});
+  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);
+  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));
+    } else {
+      std::cerr << "illegal parameters" << std::endl;
+      return EXIT_FAILURE;
+    }
+  }
+
+  // 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();
+
+  ParticleCut cut{60_GeV, false, true};
+  corsika::proposal::Interaction proposal(env, cut.getECut());
+  corsika::proposal::ContinuousProcess em_continuous(env, cut.getECut());
+  InteractionCounter proposalCounted(proposal);
+
+  OnShellCheck reset_particle_mass(1.e-3, 1.e-1, false);
+  TrackWriter trackWriter("tracks.dat");
+
+  LongitudinalProfile longprof{showerAxis};
+
+  Plane const obsPlane(showerCore, DirectionVector(rootCS, {0., 0., 1.}));
+  ObservationPlane observationLevel(obsPlane, DirectionVector(rootCS, {1., 0., 0.}),
+                                    "particles.dat");
+
+  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;
+    }
+  };
+  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, 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();
+
+  hists.saveLab("inthist_lab_verticalEAS.npz");
+  hists.saveCMS("inthist_cms_verticalEAS.npz");
+  longprof.save("longprof_verticalEAS.txt");
+}
diff --git a/ThirdParty/cnpy/CMakeLists.txt b/externals/cnpy/CMakeLists.txt
similarity index 52%
rename from ThirdParty/cnpy/CMakeLists.txt
rename to externals/cnpy/CMakeLists.txt
index 4a115728d660361d0159c1c309efdabb332b51c3..0fa38d3bd16804b024f83700a2ba6c7c2c316c08 100644
--- a/ThirdParty/cnpy/CMakeLists.txt
+++ b/externals/cnpy/CMakeLists.txt
@@ -8,33 +8,20 @@ set (
   cnpy.hpp
   )
 
-set (
-  CNPY_NAMESPACE
-  corsika/third_party/cnpy
-  )
-
 add_library (cnpy STATIC ${CNPY_SOURCES})
-CORSIKA_COPY_HEADERS_TO_NAMESPACE (cnpy ${CNPY_NAMESPACE} ${CNPY_HEADERS})
-
-set_target_properties (
-  cnpy
-  PROPERTIES
-  VERSION ${PROJECT_VERSION}
-  SOVERSION 1
-  )
 
 # target dependencies on other libraries (also the header onlys)
 target_link_libraries (
   cnpy
   PUBLIC
-  ZLIB::ZLIB
+  CONAN_PKG::zlib
   )
 
 target_include_directories (
   cnpy 
   INTERFACE 
-  $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include>
-  $<INSTALL_INTERFACE:include/include>
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+  $<INSTALL_INTERFACE:include/externals/cnpy>
   )
 
 install (
diff --git a/externals/cnpy/cnpy.cpp b/externals/cnpy/cnpy.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b32ed761799ba7bb2c2856bea6054bc29def283c
--- /dev/null
+++ b/externals/cnpy/cnpy.cpp
@@ -0,0 +1,342 @@
+// Copyright (C) 2011  Carl Rogers
+// Released under MIT License
+// license available in LICENSE file, or at
+// http://www.opensource.org/licenses/mit-license.php
+
+#include "cnpy.hpp"
+#include <complex>
+#include <cstdlib>
+#include <algorithm>
+#include <cstring>
+#include <iomanip>
+#include <stdint.h>
+#include <stdexcept>
+#include <regex>
+
+char cnpy::BigEndianTest() {
+  int x = 1;
+  return (((char*)&x)[0]) ? '<' : '>';
+}
+
+char cnpy::map_type(const std::type_info& t) {
+  if (t == typeid(float)) return 'f';
+  if (t == typeid(double)) return 'f';
+  if (t == typeid(long double)) return 'f';
+
+  if (t == typeid(int)) return 'i';
+  if (t == typeid(char)) return 'i';
+  if (t == typeid(short)) return 'i';
+  if (t == typeid(long)) return 'i';
+  if (t == typeid(long long)) return 'i';
+
+  if (t == typeid(unsigned char)) return 'u';
+  if (t == typeid(unsigned short)) return 'u';
+  if (t == typeid(unsigned long)) return 'u';
+  if (t == typeid(unsigned long long)) return 'u';
+  if (t == typeid(unsigned int)) return 'u';
+
+  if (t == typeid(bool)) return 'b';
+
+  if (t == typeid(std::complex<float>)) return 'c';
+  if (t == typeid(std::complex<double>)) return 'c';
+  if (t == typeid(std::complex<long double>))
+    return 'c';
+
+  else
+    return '?';
+}
+
+template <>
+std::vector<char>& cnpy::operator+=(std::vector<char>& lhs, const std::string rhs) {
+  lhs.insert(lhs.end(), rhs.begin(), rhs.end());
+  return lhs;
+}
+
+template <>
+std::vector<char>& cnpy::operator+=(std::vector<char>& lhs, const char* rhs) {
+  // write in little endian
+  size_t len = strlen(rhs);
+  lhs.reserve(len);
+  for (size_t byte = 0; byte < len; byte++) { lhs.push_back(rhs[byte]); }
+  return lhs;
+}
+
+void cnpy::parse_npy_header(unsigned char* buffer, size_t& word_size,
+                            std::vector<size_t>& shape, bool& fortran_order) {
+  // std::string magic_string(buffer,6);
+  [[maybe_unused]] uint8_t major_version = *reinterpret_cast<uint8_t*>(buffer + 6);
+  [[maybe_unused]] uint8_t minor_version = *reinterpret_cast<uint8_t*>(buffer + 7);
+  uint16_t header_len = *reinterpret_cast<uint16_t*>(buffer + 8);
+  std::string header(reinterpret_cast<char*>(buffer + 9), header_len);
+
+  size_t loc1, loc2;
+
+  // fortran order
+  loc1 = header.find("fortran_order") + 16;
+  fortran_order = (header.substr(loc1, 4) == "True" ? true : false);
+
+  // shape
+  loc1 = header.find("(");
+  loc2 = header.find(")");
+
+  std::regex num_regex("[0-9][0-9]*");
+  std::smatch sm;
+  shape.clear();
+
+  std::string str_shape = header.substr(loc1 + 1, loc2 - loc1 - 1);
+  while (std::regex_search(str_shape, sm, num_regex)) {
+    shape.push_back(std::stoi(sm[0].str()));
+    str_shape = sm.suffix().str();
+  }
+
+  // endian, word size, data type
+  // byte order code | stands for not applicable.
+  // not sure when this applies except for byte array
+  loc1 = header.find("descr") + 9;
+  bool littleEndian = (header[loc1] == '<' || header[loc1] == '|' ? true : false);
+  assert(littleEndian);
+
+  // char type = header[loc1+1];
+  // assert(type == map_type(T));
+
+  std::string str_ws = header.substr(loc1 + 2);
+  loc2 = str_ws.find("'");
+  word_size = atoi(str_ws.substr(0, loc2).c_str());
+}
+
+void cnpy::parse_npy_header(FILE* fp, size_t& word_size, std::vector<size_t>& shape,
+                            bool& fortran_order) {
+  char buffer[256];
+  size_t res = fread(buffer, sizeof(char), 11, fp);
+  if (res != 11) throw std::runtime_error("parse_npy_header: failed fread");
+  std::string header = fgets(buffer, 256, fp);
+  assert(header[header.size() - 1] == '\n');
+
+  size_t loc1, loc2;
+
+  // fortran order
+  loc1 = header.find("fortran_order");
+  if (loc1 == std::string::npos)
+    throw std::runtime_error(
+        "parse_npy_header: failed to find header keyword: 'fortran_order'");
+  loc1 += 16;
+  fortran_order = (header.substr(loc1, 4) == "True" ? true : false);
+
+  // shape
+  loc1 = header.find("(");
+  loc2 = header.find(")");
+  if (loc1 == std::string::npos || loc2 == std::string::npos)
+    throw std::runtime_error(
+        "parse_npy_header: failed to find header keyword: '(' or ')'");
+
+  std::regex num_regex("[0-9][0-9]*");
+  std::smatch sm;
+  shape.clear();
+
+  std::string str_shape = header.substr(loc1 + 1, loc2 - loc1 - 1);
+  while (std::regex_search(str_shape, sm, num_regex)) {
+    shape.push_back(std::stoi(sm[0].str()));
+    str_shape = sm.suffix().str();
+  }
+
+  // endian, word size, data type
+  // byte order code | stands for not applicable.
+  // not sure when this applies except for byte array
+  loc1 = header.find("descr");
+  if (loc1 == std::string::npos)
+    throw std::runtime_error("parse_npy_header: failed to find header keyword: 'descr'");
+  loc1 += 9;
+  bool littleEndian = (header[loc1] == '<' || header[loc1] == '|' ? true : false);
+  assert(littleEndian);
+
+  // char type = header[loc1+1];
+  // assert(type == map_type(T));
+
+  std::string str_ws = header.substr(loc1 + 2);
+  loc2 = str_ws.find("'");
+  word_size = atoi(str_ws.substr(0, loc2).c_str());
+}
+
+void cnpy::parse_zip_footer(FILE* fp, uint16_t& nrecs, size_t& global_header_size,
+                            size_t& global_header_offset) {
+  std::vector<char> footer(22);
+  fseek(fp, -22, SEEK_END);
+  size_t res = fread(&footer[0], sizeof(char), 22, fp);
+  if (res != 22) throw std::runtime_error("parse_zip_footer: failed fread");
+
+  uint16_t disk_no, disk_start, nrecs_on_disk, comment_len;
+  disk_no = *(uint16_t*)&footer[4];
+  disk_start = *(uint16_t*)&footer[6];
+  nrecs_on_disk = *(uint16_t*)&footer[8];
+  nrecs = *(uint16_t*)&footer[10];
+  global_header_size = *(uint32_t*)&footer[12];
+  global_header_offset = *(uint32_t*)&footer[16];
+  comment_len = *(uint16_t*)&footer[20];
+
+  assert(disk_no == 0);
+  assert(disk_start == 0);
+  assert(nrecs_on_disk == nrecs);
+  assert(comment_len == 0);
+}
+
+cnpy::NpyArray load_the_npy_file(FILE* fp) {
+  std::vector<size_t> shape;
+  size_t word_size;
+  bool fortran_order;
+  cnpy::parse_npy_header(fp, word_size, shape, fortran_order);
+
+  cnpy::NpyArray arr(shape, word_size, fortran_order);
+  size_t nread = fread(arr.data<char>(), 1, arr.num_bytes(), fp);
+  if (nread != arr.num_bytes())
+    throw std::runtime_error("load_the_npy_file: failed fread");
+  return arr;
+}
+
+cnpy::NpyArray load_the_npz_array(FILE* fp, uint32_t compr_bytes,
+                                  uint32_t uncompr_bytes) {
+
+  std::vector<unsigned char> buffer_compr(compr_bytes);
+  std::vector<unsigned char> buffer_uncompr(uncompr_bytes);
+  size_t nread = fread(&buffer_compr[0], 1, compr_bytes, fp);
+  if (nread != compr_bytes) throw std::runtime_error("load_the_npy_file: failed fread");
+
+  [[maybe_unused]] int err;
+  z_stream d_stream;
+
+  d_stream.zalloc = Z_NULL;
+  d_stream.zfree = Z_NULL;
+  d_stream.opaque = Z_NULL;
+  d_stream.avail_in = 0;
+  d_stream.next_in = Z_NULL;
+  err = inflateInit2(&d_stream, -MAX_WBITS);
+
+  d_stream.avail_in = compr_bytes;
+  d_stream.next_in = &buffer_compr[0];
+  d_stream.avail_out = uncompr_bytes;
+  d_stream.next_out = &buffer_uncompr[0];
+
+  err = inflate(&d_stream, Z_FINISH);
+  err = inflateEnd(&d_stream);
+
+  std::vector<size_t> shape;
+  size_t word_size;
+  bool fortran_order;
+  cnpy::parse_npy_header(&buffer_uncompr[0], word_size, shape, fortran_order);
+
+  cnpy::NpyArray array(shape, word_size, fortran_order);
+
+  size_t offset = uncompr_bytes - array.num_bytes();
+  memcpy(array.data<unsigned char>(), &buffer_uncompr[0] + offset, array.num_bytes());
+
+  return array;
+}
+
+cnpy::npz_t cnpy::npz_load(std::string fname) {
+  FILE* fp = fopen(fname.c_str(), "rb");
+
+  if (!fp) {
+    throw std::runtime_error("npz_load: Error! Unable to open file " + fname + "!");
+  }
+
+  cnpy::npz_t arrays;
+
+  while (1) {
+    std::vector<char> local_header(30);
+    size_t headerres = fread(&local_header[0], sizeof(char), 30, fp);
+    if (headerres != 30) throw std::runtime_error("npz_load: failed fread");
+
+    // if we've reached the global header, stop reading
+    if (local_header[2] != 0x03 || local_header[3] != 0x04) break;
+
+    // read in the variable name
+    uint16_t name_len = *(uint16_t*)&local_header[26];
+    std::string varname(name_len, ' ');
+    size_t vname_res = fread(&varname[0], sizeof(char), name_len, fp);
+    if (vname_res != name_len) throw std::runtime_error("npz_load: failed fread");
+
+    // erase the lagging .npy
+    varname.erase(varname.end() - 4, varname.end());
+
+    // read in the extra field
+    uint16_t extra_field_len = *(uint16_t*)&local_header[28];
+    if (extra_field_len > 0) {
+      std::vector<char> buff(extra_field_len);
+      size_t efield_res = fread(&buff[0], sizeof(char), extra_field_len, fp);
+      if (efield_res != extra_field_len)
+        throw std::runtime_error("npz_load: failed fread");
+    }
+
+    uint16_t compr_method = *reinterpret_cast<uint16_t*>(&local_header[0] + 8);
+    uint32_t compr_bytes = *reinterpret_cast<uint32_t*>(&local_header[0] + 18);
+    uint32_t uncompr_bytes = *reinterpret_cast<uint32_t*>(&local_header[0] + 22);
+
+    if (compr_method == 0) {
+      arrays[varname] = load_the_npy_file(fp);
+    } else {
+      arrays[varname] = load_the_npz_array(fp, compr_bytes, uncompr_bytes);
+    }
+  }
+
+  fclose(fp);
+  return arrays;
+}
+
+cnpy::NpyArray cnpy::npz_load(std::string fname, std::string varname) {
+  FILE* fp = fopen(fname.c_str(), "rb");
+
+  if (!fp) throw std::runtime_error("npz_load: Unable to open file " + fname);
+
+  while (1) {
+    std::vector<char> local_header(30);
+    size_t header_res = fread(&local_header[0], sizeof(char), 30, fp);
+    if (header_res != 30) throw std::runtime_error("npz_load: failed fread");
+
+    // if we've reached the global header, stop reading
+    if (local_header[2] != 0x03 || local_header[3] != 0x04) break;
+
+    // read in the variable name
+    uint16_t name_len = *(uint16_t*)&local_header[26];
+    std::string vname(name_len, ' ');
+    size_t vname_res = fread(&vname[0], sizeof(char), name_len, fp);
+    if (vname_res != name_len) throw std::runtime_error("npz_load: failed fread");
+    vname.erase(vname.end() - 4, vname.end()); // erase the lagging .npy
+
+    // read in the extra field
+    uint16_t extra_field_len = *(uint16_t*)&local_header[28];
+    fseek(fp, extra_field_len, SEEK_CUR); // skip past the extra field
+
+    uint16_t compr_method = *reinterpret_cast<uint16_t*>(&local_header[0] + 8);
+    uint32_t compr_bytes = *reinterpret_cast<uint32_t*>(&local_header[0] + 18);
+    uint32_t uncompr_bytes = *reinterpret_cast<uint32_t*>(&local_header[0] + 22);
+
+    if (vname == varname) {
+      NpyArray array = (compr_method == 0)
+                           ? load_the_npy_file(fp)
+                           : load_the_npz_array(fp, compr_bytes, uncompr_bytes);
+      fclose(fp);
+      return array;
+    } else {
+      // skip past the data
+      uint32_t size = *(uint32_t*)&local_header[22];
+      fseek(fp, size, SEEK_CUR);
+    }
+  }
+
+  fclose(fp);
+
+  // if we get here, we haven't found the variable in the file
+  throw std::runtime_error("npz_load: Variable name " + varname + " not found in " +
+                           fname);
+}
+
+cnpy::NpyArray cnpy::npy_load(std::string fname) {
+
+  FILE* fp = fopen(fname.c_str(), "rb");
+
+  if (!fp) throw std::runtime_error("npy_load: Unable to open file " + fname);
+
+  NpyArray arr = load_the_npy_file(fp);
+
+  fclose(fp);
+  return arr;
+}
diff --git a/externals/cnpy/cnpy.hpp b/externals/cnpy/cnpy.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..51668d3280d12fa84414b56beeee2088f308f567
--- /dev/null
+++ b/externals/cnpy/cnpy.hpp
@@ -0,0 +1,294 @@
+// Copyright (C) 2011  Carl Rogers
+// Released under MIT License
+// license available in LICENSE file, or at
+// http://www.opensource.org/licenses/mit-license.php
+
+#pragma once
+
+#include <string>
+#include <stdexcept>
+#include <sstream>
+#include <vector>
+#include <cstdio>
+#include <typeinfo>
+#include <iostream>
+#include <cassert>
+#include <zlib.h>
+#include <map>
+#include <memory>
+#include <stdint.h>
+#include <numeric>
+
+namespace cnpy {
+
+  struct NpyArray {
+    NpyArray(const std::vector<size_t>& _shape, size_t _word_size, bool _fortran_order)
+        : shape(_shape)
+        , word_size(_word_size)
+        , fortran_order(_fortran_order) {
+      num_vals = 1;
+      for (size_t i = 0; i < shape.size(); i++) num_vals *= shape[i];
+      data_holder =
+          std::shared_ptr<std::vector<char>>(new std::vector<char>(num_vals * word_size));
+    }
+
+    NpyArray()
+        : shape(0)
+        , word_size(0)
+        , fortran_order(0)
+        , num_vals(0) {}
+
+    template <typename T>
+    T* data() {
+      return reinterpret_cast<T*>(&(*data_holder)[0]);
+    }
+
+    template <typename T>
+    const T* data() const {
+      return reinterpret_cast<T*>(&(*data_holder)[0]);
+    }
+
+    template <typename T>
+    std::vector<T> as_vec() const {
+      const T* p = data<T>();
+      return std::vector<T>(p, p + num_vals);
+    }
+
+    size_t num_bytes() const { return data_holder->size(); }
+
+    std::shared_ptr<std::vector<char>> data_holder;
+    std::vector<size_t> shape;
+    size_t word_size;
+    bool fortran_order;
+    size_t num_vals;
+  };
+
+  using npz_t = std::map<std::string, NpyArray>;
+
+  char BigEndianTest();
+  char map_type(const std::type_info& t);
+  template <typename T>
+  std::vector<char> create_npy_header(const std::vector<size_t>& shape);
+  void parse_npy_header(FILE* fp, size_t& word_size, std::vector<size_t>& shape,
+                        bool& fortran_order);
+  void parse_npy_header(unsigned char* buffer, size_t& word_size,
+                        std::vector<size_t>& shape, bool& fortran_order);
+  void parse_zip_footer(FILE* fp, uint16_t& nrecs, size_t& global_header_size,
+                        size_t& global_header_offset);
+  npz_t npz_load(std::string fname);
+  NpyArray npz_load(std::string fname, std::string varname);
+  NpyArray npy_load(std::string fname);
+
+  template <typename T>
+  std::vector<char>& operator+=(std::vector<char>& lhs, const T rhs) {
+    // write in little endian
+    for (size_t byte = 0; byte < sizeof(T); byte++) {
+      char val = *((char*)&rhs + byte);
+      lhs.push_back(val);
+    }
+    return lhs;
+  }
+
+  template <>
+  std::vector<char>& operator+=(std::vector<char>& lhs, const std::string rhs);
+  template <>
+  std::vector<char>& operator+=(std::vector<char>& lhs, const char* rhs);
+
+  template <typename T>
+  void npy_save(std::string fname, const T* data, const std::vector<size_t> shape,
+                std::string mode = "w") {
+    FILE* fp = NULL;
+    std::vector<size_t> true_data_shape; // if appending, the shape of existing + new data
+
+    if (mode == "a") fp = fopen(fname.c_str(), "r+b");
+
+    if (fp) {
+      // file exists. we need to append to it. read the header, modify the array size
+      size_t word_size;
+      bool fortran_order;
+      parse_npy_header(fp, word_size, true_data_shape, fortran_order);
+      assert(!fortran_order);
+
+      if (word_size != sizeof(T)) {
+        std::cout << "libnpy error: " << fname << " has word size " << word_size
+                  << " but npy_save appending data sized " << sizeof(T) << "\n";
+        assert(word_size == sizeof(T));
+      }
+      if (true_data_shape.size() != shape.size()) {
+        std::cout << "libnpy error: npy_save attempting to append misdimensioned data to "
+                  << fname << "\n";
+        assert(true_data_shape.size() != shape.size());
+      }
+
+      for (size_t i = 1; i < shape.size(); i++) {
+        if (shape[i] != true_data_shape[i]) {
+          std::cout << "libnpy error: npy_save attempting to append misshaped data to "
+                    << fname << "\n";
+          assert(shape[i] == true_data_shape[i]);
+        }
+      }
+      true_data_shape[0] += shape[0];
+    } else {
+      fp = fopen(fname.c_str(), "wb");
+      true_data_shape = shape;
+    }
+
+    std::vector<char> header = create_npy_header<T>(true_data_shape);
+    size_t nels =
+        std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<size_t>());
+
+    fseek(fp, 0, SEEK_SET);
+    fwrite(&header[0], sizeof(char), header.size(), fp);
+    fseek(fp, 0, SEEK_END);
+    fwrite(data, sizeof(T), nels, fp);
+    fclose(fp);
+  }
+
+  template <typename T>
+  void npz_save(std::string zipname, std::string fname, const T* data,
+                const std::vector<size_t>& shape, std::string mode = "w") {
+    // first, append a .npy to the fname
+    fname += ".npy";
+
+    // now, on with the show
+    FILE* fp = NULL;
+    uint16_t nrecs = 0;
+    size_t global_header_offset = 0;
+    std::vector<char> global_header;
+
+    if (mode == "a") fp = fopen(zipname.c_str(), "r+b");
+
+    if (fp) {
+      // zip file exists. we need to add a new npy file to it.
+      // first read the footer. this gives us the offset and size of the global header
+      // then read and store the global header.
+      // below, we will write the the new data at the start of the global header then
+      // append the global header and footer below it
+      size_t global_header_size;
+      parse_zip_footer(fp, nrecs, global_header_size, global_header_offset);
+      fseek(fp, global_header_offset, SEEK_SET);
+      global_header.resize(global_header_size);
+      size_t res = fread(&global_header[0], sizeof(char), global_header_size, fp);
+      if (res != global_header_size) {
+        throw std::runtime_error(
+            "npz_save: header read error while adding to existing zip");
+      }
+      fseek(fp, global_header_offset, SEEK_SET);
+    } else {
+      fp = fopen(zipname.c_str(), "wb");
+    }
+
+    std::vector<char> npy_header = create_npy_header<T>(shape);
+
+    size_t nels =
+        std::accumulate(shape.begin(), shape.end(), 1, std::multiplies<size_t>());
+    size_t nbytes = nels * sizeof(T) + npy_header.size();
+
+    // get the CRC of the data to be added
+    uint32_t crc = crc32(0L, (uint8_t*)&npy_header[0], npy_header.size());
+    crc = crc32(crc, (uint8_t*)data, nels * sizeof(T));
+
+    // build the local header
+    std::vector<char> local_header;
+    local_header += "PK";                   // first part of sig
+    local_header += (uint16_t)0x0403;       // second part of sig
+    local_header += (uint16_t)20;           // min version to extract
+    local_header += (uint16_t)0;            // general purpose bit flag
+    local_header += (uint16_t)0;            // compression method
+    local_header += (uint16_t)0;            // file last mod time
+    local_header += (uint16_t)0;            // file last mod date
+    local_header += (uint32_t)crc;          // crc
+    local_header += (uint32_t)nbytes;       // compressed size
+    local_header += (uint32_t)nbytes;       // uncompressed size
+    local_header += (uint16_t)fname.size(); // fname length
+    local_header += (uint16_t)0;            // extra field length
+    local_header += fname;
+
+    // build global header
+    global_header += "PK";             // first part of sig
+    global_header += (uint16_t)0x0201; // second part of sig
+    global_header += (uint16_t)20;     // version made by
+    global_header.insert(global_header.end(), local_header.begin() + 4,
+                         local_header.begin() + 30);
+    global_header += (uint16_t)0; // file comment length
+    global_header += (uint16_t)0; // disk number where file starts
+    global_header += (uint16_t)0; // internal file attributes
+    global_header += (uint32_t)0; // external file attributes
+    global_header +=
+        (uint32_t)global_header_offset; // relative offset of local file header, since it
+                                        // begins where the global header used to begin
+    global_header += fname;
+
+    // build footer
+    std::vector<char> footer;
+    footer += "PK";                           // first part of sig
+    footer += (uint16_t)0x0605;               // second part of sig
+    footer += (uint16_t)0;                    // number of this disk
+    footer += (uint16_t)0;                    // disk where footer starts
+    footer += (uint16_t)(nrecs + 1);          // number of records on this disk
+    footer += (uint16_t)(nrecs + 1);          // total number of records
+    footer += (uint32_t)global_header.size(); // nbytes of global headers
+    footer += (uint32_t)(
+        global_header_offset + nbytes +
+        local_header.size()); // offset of start of global headers, since global header
+                              // now starts after newly written array
+    footer += (uint16_t)0; // zip file comment length
+
+    // write everything
+    fwrite(&local_header[0], sizeof(char), local_header.size(), fp);
+    fwrite(&npy_header[0], sizeof(char), npy_header.size(), fp);
+    fwrite(data, sizeof(T), nels, fp);
+    fwrite(&global_header[0], sizeof(char), global_header.size(), fp);
+    fwrite(&footer[0], sizeof(char), footer.size(), fp);
+    fclose(fp);
+  }
+
+  template <typename T>
+  void npy_save(std::string fname, const std::vector<T> data, std::string mode = "w") {
+    std::vector<size_t> shape;
+    shape.push_back(data.size());
+    npy_save(fname, &data[0], shape, mode);
+  }
+
+  template <typename T>
+  void npz_save(std::string zipname, std::string fname, const std::vector<T> data,
+                std::string mode = "w") {
+    std::vector<size_t> shape;
+    shape.push_back(data.size());
+    npz_save(zipname, fname, &data[0], shape, mode);
+  }
+
+  template <typename T>
+  std::vector<char> create_npy_header(const std::vector<size_t>& shape) {
+
+    std::vector<char> dict;
+    dict += "{'descr': '";
+    dict += BigEndianTest();
+    dict += map_type(typeid(T));
+    dict += std::to_string(sizeof(T));
+    dict += "', 'fortran_order': False, 'shape': (";
+    dict += std::to_string(shape[0]);
+    for (size_t i = 1; i < shape.size(); i++) {
+      dict += ", ";
+      dict += std::to_string(shape[i]);
+    }
+    if (shape.size() == 1) dict += ",";
+    dict += "), }";
+    // pad with spaces so that preamble+dict is modulo 16 bytes. preamble is 10 bytes.
+    // dict needs to end with \n
+    int remainder = 16 - (10 + dict.size()) % 16;
+    dict.insert(dict.end(), remainder, ' ');
+    dict.back() = '\n';
+
+    std::vector<char> header;
+    header += (char)0x93;
+    header += "NUMPY";
+    header += (char)0x01; // major version of numpy format
+    header += (char)0x00; // minor version of numpy format
+    header += (uint16_t)dict.size();
+    header.insert(header.end(), dict.begin(), dict.end());
+
+    return header;
+  }
+
+} // namespace cnpy
diff --git a/ThirdParty/lcov/.version b/externals/lcov/.version
similarity index 100%
rename from ThirdParty/lcov/.version
rename to externals/lcov/.version
diff --git a/ThirdParty/lcov/CHANGES b/externals/lcov/CHANGES
similarity index 100%
rename from ThirdParty/lcov/CHANGES
rename to externals/lcov/CHANGES
diff --git a/ThirdParty/lcov/CONTRIBUTING b/externals/lcov/CONTRIBUTING
similarity index 100%
rename from ThirdParty/lcov/CONTRIBUTING
rename to externals/lcov/CONTRIBUTING
diff --git a/ThirdParty/lcov/COPYING b/externals/lcov/COPYING
similarity index 100%
rename from ThirdParty/lcov/COPYING
rename to externals/lcov/COPYING
diff --git a/ThirdParty/lcov/Makefile b/externals/lcov/Makefile
similarity index 100%
rename from ThirdParty/lcov/Makefile
rename to externals/lcov/Makefile
diff --git a/ThirdParty/lcov/README b/externals/lcov/README
similarity index 100%
rename from ThirdParty/lcov/README
rename to externals/lcov/README
diff --git a/ThirdParty/lcov/bin/copy_dates.sh b/externals/lcov/bin/copy_dates.sh
similarity index 100%
rename from ThirdParty/lcov/bin/copy_dates.sh
rename to externals/lcov/bin/copy_dates.sh
diff --git a/ThirdParty/lcov/bin/gendesc b/externals/lcov/bin/gendesc
similarity index 100%
rename from ThirdParty/lcov/bin/gendesc
rename to externals/lcov/bin/gendesc
diff --git a/ThirdParty/lcov/bin/genhtml b/externals/lcov/bin/genhtml
similarity index 100%
rename from ThirdParty/lcov/bin/genhtml
rename to externals/lcov/bin/genhtml
diff --git a/ThirdParty/lcov/bin/geninfo b/externals/lcov/bin/geninfo
similarity index 100%
rename from ThirdParty/lcov/bin/geninfo
rename to externals/lcov/bin/geninfo
diff --git a/ThirdParty/lcov/bin/genpng b/externals/lcov/bin/genpng
similarity index 100%
rename from ThirdParty/lcov/bin/genpng
rename to externals/lcov/bin/genpng
diff --git a/ThirdParty/lcov/bin/get_changes.sh b/externals/lcov/bin/get_changes.sh
similarity index 100%
rename from ThirdParty/lcov/bin/get_changes.sh
rename to externals/lcov/bin/get_changes.sh
diff --git a/ThirdParty/lcov/bin/get_version.sh b/externals/lcov/bin/get_version.sh
similarity index 100%
rename from ThirdParty/lcov/bin/get_version.sh
rename to externals/lcov/bin/get_version.sh
diff --git a/ThirdParty/lcov/bin/install.sh b/externals/lcov/bin/install.sh
similarity index 100%
rename from ThirdParty/lcov/bin/install.sh
rename to externals/lcov/bin/install.sh
diff --git a/ThirdParty/lcov/bin/lcov b/externals/lcov/bin/lcov
similarity index 100%
rename from ThirdParty/lcov/bin/lcov
rename to externals/lcov/bin/lcov
diff --git a/ThirdParty/lcov/bin/updateversion.pl b/externals/lcov/bin/updateversion.pl
similarity index 100%
rename from ThirdParty/lcov/bin/updateversion.pl
rename to externals/lcov/bin/updateversion.pl
diff --git a/ThirdParty/lcov/example/Makefile b/externals/lcov/example/Makefile
similarity index 100%
rename from ThirdParty/lcov/example/Makefile
rename to externals/lcov/example/Makefile
diff --git a/ThirdParty/lcov/example/README b/externals/lcov/example/README
similarity index 100%
rename from ThirdParty/lcov/example/README
rename to externals/lcov/example/README
diff --git a/ThirdParty/lcov/example/descriptions.txt b/externals/lcov/example/descriptions.txt
similarity index 100%
rename from ThirdParty/lcov/example/descriptions.txt
rename to externals/lcov/example/descriptions.txt
diff --git a/ThirdParty/lcov/example/example.c b/externals/lcov/example/example.c
similarity index 100%
rename from ThirdParty/lcov/example/example.c
rename to externals/lcov/example/example.c
diff --git a/ThirdParty/lcov/example/gauss.h b/externals/lcov/example/gauss.h
similarity index 100%
rename from ThirdParty/lcov/example/gauss.h
rename to externals/lcov/example/gauss.h
diff --git a/ThirdParty/lcov/example/iterate.h b/externals/lcov/example/iterate.h
similarity index 100%
rename from ThirdParty/lcov/example/iterate.h
rename to externals/lcov/example/iterate.h
diff --git a/ThirdParty/lcov/example/methods/gauss.c b/externals/lcov/example/methods/gauss.c
similarity index 100%
rename from ThirdParty/lcov/example/methods/gauss.c
rename to externals/lcov/example/methods/gauss.c
diff --git a/ThirdParty/lcov/example/methods/iterate.c b/externals/lcov/example/methods/iterate.c
similarity index 100%
rename from ThirdParty/lcov/example/methods/iterate.c
rename to externals/lcov/example/methods/iterate.c
diff --git a/ThirdParty/lcov/lcovrc b/externals/lcov/lcovrc
similarity index 100%
rename from ThirdParty/lcov/lcovrc
rename to externals/lcov/lcovrc
diff --git a/ThirdParty/lcov/man/gendesc.1 b/externals/lcov/man/gendesc.1
similarity index 100%
rename from ThirdParty/lcov/man/gendesc.1
rename to externals/lcov/man/gendesc.1
diff --git a/ThirdParty/lcov/man/genhtml.1 b/externals/lcov/man/genhtml.1
similarity index 100%
rename from ThirdParty/lcov/man/genhtml.1
rename to externals/lcov/man/genhtml.1
diff --git a/ThirdParty/lcov/man/geninfo.1 b/externals/lcov/man/geninfo.1
similarity index 100%
rename from ThirdParty/lcov/man/geninfo.1
rename to externals/lcov/man/geninfo.1
diff --git a/ThirdParty/lcov/man/genpng.1 b/externals/lcov/man/genpng.1
similarity index 100%
rename from ThirdParty/lcov/man/genpng.1
rename to externals/lcov/man/genpng.1
diff --git a/ThirdParty/lcov/man/lcov.1 b/externals/lcov/man/lcov.1
similarity index 100%
rename from ThirdParty/lcov/man/lcov.1
rename to externals/lcov/man/lcov.1
diff --git a/ThirdParty/lcov/man/lcovrc.5 b/externals/lcov/man/lcovrc.5
similarity index 100%
rename from ThirdParty/lcov/man/lcovrc.5
rename to externals/lcov/man/lcovrc.5
diff --git a/ThirdParty/lcov/rpm/lcov.spec b/externals/lcov/rpm/lcov.spec
similarity index 100%
rename from ThirdParty/lcov/rpm/lcov.spec
rename to externals/lcov/rpm/lcov.spec
diff --git a/ThirdParty/lcov/test/Makefile b/externals/lcov/test/Makefile
similarity index 100%
rename from ThirdParty/lcov/test/Makefile
rename to externals/lcov/test/Makefile
diff --git a/ThirdParty/lcov/test/bin/common b/externals/lcov/test/bin/common
similarity index 100%
rename from ThirdParty/lcov/test/bin/common
rename to externals/lcov/test/bin/common
diff --git a/ThirdParty/lcov/test/bin/mkinfo b/externals/lcov/test/bin/mkinfo
similarity index 100%
rename from ThirdParty/lcov/test/bin/mkinfo
rename to externals/lcov/test/bin/mkinfo
diff --git a/ThirdParty/lcov/test/bin/norminfo b/externals/lcov/test/bin/norminfo
similarity index 100%
rename from ThirdParty/lcov/test/bin/norminfo
rename to externals/lcov/test/bin/norminfo
diff --git a/ThirdParty/lcov/test/bin/test_run b/externals/lcov/test/bin/test_run
similarity index 100%
rename from ThirdParty/lcov/test/bin/test_run
rename to externals/lcov/test/bin/test_run
diff --git a/ThirdParty/lcov/test/bin/test_skip b/externals/lcov/test/bin/test_skip
similarity index 100%
rename from ThirdParty/lcov/test/bin/test_skip
rename to externals/lcov/test/bin/test_skip
diff --git a/ThirdParty/lcov/test/bin/testsuite_exit b/externals/lcov/test/bin/testsuite_exit
similarity index 100%
rename from ThirdParty/lcov/test/bin/testsuite_exit
rename to externals/lcov/test/bin/testsuite_exit
diff --git a/ThirdParty/lcov/test/bin/testsuite_init b/externals/lcov/test/bin/testsuite_init
similarity index 100%
rename from ThirdParty/lcov/test/bin/testsuite_init
rename to externals/lcov/test/bin/testsuite_init
diff --git a/ThirdParty/lcov/test/common.mak b/externals/lcov/test/common.mak
similarity index 100%
rename from ThirdParty/lcov/test/common.mak
rename to externals/lcov/test/common.mak
diff --git a/ThirdParty/lcov/test/genhtml_output/Makefile b/externals/lcov/test/genhtml_output/Makefile
similarity index 100%
rename from ThirdParty/lcov/test/genhtml_output/Makefile
rename to externals/lcov/test/genhtml_output/Makefile
diff --git a/ThirdParty/lcov/test/genhtml_output/genhtml_test b/externals/lcov/test/genhtml_output/genhtml_test
similarity index 100%
rename from ThirdParty/lcov/test/genhtml_output/genhtml_test
rename to externals/lcov/test/genhtml_output/genhtml_test
diff --git a/ThirdParty/lcov/test/lcov_add_files/Makefile b/externals/lcov/test/lcov_add_files/Makefile
similarity index 100%
rename from ThirdParty/lcov/test/lcov_add_files/Makefile
rename to externals/lcov/test/lcov_add_files/Makefile
diff --git a/ThirdParty/lcov/test/lcov_add_files/add_test b/externals/lcov/test/lcov_add_files/add_test
similarity index 100%
rename from ThirdParty/lcov/test/lcov_add_files/add_test
rename to externals/lcov/test/lcov_add_files/add_test
diff --git a/ThirdParty/lcov/test/lcov_diff/Makefile b/externals/lcov/test/lcov_diff/Makefile
similarity index 100%
rename from ThirdParty/lcov/test/lcov_diff/Makefile
rename to externals/lcov/test/lcov_diff/Makefile
diff --git a/ThirdParty/lcov/test/lcov_diff/diff_test b/externals/lcov/test/lcov_diff/diff_test
similarity index 100%
rename from ThirdParty/lcov/test/lcov_diff/diff_test
rename to externals/lcov/test/lcov_diff/diff_test
diff --git a/ThirdParty/lcov/test/lcov_diff/new/Makefile b/externals/lcov/test/lcov_diff/new/Makefile
similarity index 100%
rename from ThirdParty/lcov/test/lcov_diff/new/Makefile
rename to externals/lcov/test/lcov_diff/new/Makefile
diff --git a/ThirdParty/lcov/test/lcov_diff/new/prog.c b/externals/lcov/test/lcov_diff/new/prog.c
similarity index 100%
rename from ThirdParty/lcov/test/lcov_diff/new/prog.c
rename to externals/lcov/test/lcov_diff/new/prog.c
diff --git a/ThirdParty/lcov/test/lcov_diff/old/Makefile b/externals/lcov/test/lcov_diff/old/Makefile
similarity index 100%
rename from ThirdParty/lcov/test/lcov_diff/old/Makefile
rename to externals/lcov/test/lcov_diff/old/Makefile
diff --git a/ThirdParty/lcov/test/lcov_diff/old/prog.c b/externals/lcov/test/lcov_diff/old/prog.c
similarity index 100%
rename from ThirdParty/lcov/test/lcov_diff/old/prog.c
rename to externals/lcov/test/lcov_diff/old/prog.c
diff --git a/ThirdParty/lcov/test/lcov_misc/Makefile b/externals/lcov/test/lcov_misc/Makefile
similarity index 100%
rename from ThirdParty/lcov/test/lcov_misc/Makefile
rename to externals/lcov/test/lcov_misc/Makefile
diff --git a/ThirdParty/lcov/test/lcov_summary/Makefile b/externals/lcov/test/lcov_summary/Makefile
similarity index 100%
rename from ThirdParty/lcov/test/lcov_summary/Makefile
rename to externals/lcov/test/lcov_summary/Makefile
diff --git a/ThirdParty/lcov/test/lcov_summary/check_counts b/externals/lcov/test/lcov_summary/check_counts
similarity index 100%
rename from ThirdParty/lcov/test/lcov_summary/check_counts
rename to externals/lcov/test/lcov_summary/check_counts
diff --git a/ThirdParty/lcov/test/lcovrc b/externals/lcov/test/lcovrc
similarity index 100%
rename from ThirdParty/lcov/test/lcovrc
rename to externals/lcov/test/lcovrc
diff --git a/ThirdParty/lcov/test/profiles/large b/externals/lcov/test/profiles/large
similarity index 100%
rename from ThirdParty/lcov/test/profiles/large
rename to externals/lcov/test/profiles/large
diff --git a/ThirdParty/lcov/test/profiles/medium b/externals/lcov/test/profiles/medium
similarity index 100%
rename from ThirdParty/lcov/test/profiles/medium
rename to externals/lcov/test/profiles/medium
diff --git a/ThirdParty/lcov/test/profiles/small b/externals/lcov/test/profiles/small
similarity index 100%
rename from ThirdParty/lcov/test/profiles/small
rename to externals/lcov/test/profiles/small
diff --git a/modules/CMakeLists.txt b/modules/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..ed2ead3f69b33185506f3cd720e30820f7c551f1
--- /dev/null
+++ b/modules/CMakeLists.txt
@@ -0,0 +1,32 @@
+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/Processes/Proposal/CMakeLists_PROPOSAL.txt b/modules/CMakeLists_PROPOSAL.txt
similarity index 96%
rename from Processes/Proposal/CMakeLists_PROPOSAL.txt
rename to modules/CMakeLists_PROPOSAL.txt
index 7045ae65db2b0a81b82bfd38ba643d1a9056590e..a752d8f7be5a72349f908c2721461a69f3665e58 100644
--- a/Processes/Proposal/CMakeLists_PROPOSAL.txt
+++ b/modules/CMakeLists_PROPOSAL.txt
@@ -63,22 +63,26 @@ target_include_directories(
     $<INSTALL_INTERFACE:include>
 )
 target_compile_options(PROPOSAL PRIVATE -Wall -Wextra -Wnarrowing -Wpedantic -fdiagnostics-show-option -Wno-format-security)
+if (NOT IN_CORSIKA8)
 install(
     TARGETS PROPOSAL
     EXPORT PROPOSALTargets
     LIBRARY DESTINATION ${CMAKE_INSTALL_LIBDIR}
     ARCHIVE DESTINATION ${CMAKE_INSTALL_LIBDIR}
 )
+endif (NOT IN_CORSIKA8)
 
 # input version from the project call, so the library knows its own version
 configure_file(
     "${PROJECT_SOURCE_DIR}/include/PROPOSAL/version.h.in"
     "${PROJECT_BINARY_DIR}/include/PROPOSAL/version.h"
 )
+if (NOT IN_CORSIKA8)
 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>
@@ -86,12 +90,14 @@ target_include_directories(
 )
 
 # install header files
+if (NOT IN_CORSIKA8)
 file(GLOB_RECURSE INC_FILES ${PROJECT_SOURCE_DIR}/include/*)
 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()
+endif (NOT IN_CORSIKA8)
 
 
 if(NOT IS_SYMLINK ${CMAKE_BINARY_DIR}/resources)
@@ -108,8 +114,10 @@ endif()
 if (NOT IN_CORSIKA8)
 set(CMAKE_POSITION_INDEPENDENT_CODE ON)
 add_subdirectory("vendor/spdlog")
-endif (NOT IN_CORSIKA8)
 target_link_libraries(PROPOSAL PRIVATE spdlog::spdlog)
+else (NOT IN_CORSIKA8)
+target_link_libraries(PROPOSAL PRIVATE CONAN_PKG::spdlog)
+endif (NOT IN_CORSIKA8)
 
 
 #################################################################
diff --git a/modules/conex b/modules/conex
new file mode 160000
index 0000000000000000000000000000000000000000..73e3d1fa850d2d3602a15b3b948ac1789fbc968d
--- /dev/null
+++ b/modules/conex
@@ -0,0 +1 @@
+Subproject commit 73e3d1fa850d2d3602a15b3b948ac1789fbc968d
diff --git a/modules/data b/modules/data
new file mode 160000
index 0000000000000000000000000000000000000000..afe83dc19c1464deb176f38c3ddab80a64e081f4
--- /dev/null
+++ b/modules/data
@@ -0,0 +1 @@
+Subproject commit afe83dc19c1464deb176f38c3ddab80a64e081f4
diff --git a/Processes/Proposal/PROPOSAL b/modules/proposal
similarity index 100%
rename from Processes/Proposal/PROPOSAL
rename to modules/proposal
diff --git a/modules/pythia/CMakeLists.txt b/modules/pythia/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..980e42375a04347de5b8fd96cad79f6526d66a8a
--- /dev/null
+++ b/modules/pythia/CMakeLists.txt
@@ -0,0 +1,79 @@
+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/ThirdParty/pythia8235-stripped.tar.bz2 b/modules/pythia/pythia8235-stripped.tar.bz2
similarity index 100%
rename from ThirdParty/pythia8235-stripped.tar.bz2
rename to modules/pythia/pythia8235-stripped.tar.bz2
diff --git a/modules/qgsjetII/CMakeLists.txt b/modules/qgsjetII/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..2ddb6b023f7e4d0320a7f827027c165d504b6a48
--- /dev/null
+++ b/modules/qgsjetII/CMakeLists.txt
@@ -0,0 +1,42 @@
+cmake_minimum_required (VERSION 3.1)
+
+project (libQGSJetII)
+
+set (
+  MODEL_SOURCES
+  qgsjet-II-04-mod1.f
+  qgsjet-II-04.cpp
+  )
+
+set (
+  MODEL_HEADERS
+  qgsjet-II-04.hpp
+  )
+
+enable_language (Fortran)
+add_library (QGSJetII SHARED ${MODEL_SOURCES})
+set_target_properties (QGSJetII PROPERTIES POSITION_INDEPENDENT_CODE 1)
+
+target_include_directories (
+  QGSJetII
+  PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+  $<INSTALL_INTERFACE:include/include>
+  )
+target_link_libraries (QGSJetII CorsikaData)
+
+add_library (QGSJetII_static STATIC ${MODEL_SOURCES})
+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>
+  )
+target_link_libraries (QGSJetII_static CorsikaData)
+
+
+# add qgsjetII to corsika8 build
+add_dependencies (CORSIKA8 QGSJetII_static)
+target_link_libraries (CORSIKA8 INTERFACE QGSJetII_static)
diff --git a/modules/qgsjetII/README.md b/modules/qgsjetII/README.md
new file mode 100644
index 0000000000000000000000000000000000000000..65850ba9263edb0c63f5b84d50f7c8021f318cbd
--- /dev/null
+++ b/modules/qgsjetII/README.md
@@ -0,0 +1,20 @@
+Note, the "qgsjet-II-04-mod1.f" is differnt from the "qgsjet-II-04-org.f"
+in the introduction of additional file-handling routines:
+
+```
+  void cordataopenfile_(const char* name);
+  void cordatafillarray_(double* data, const int& length);
+  void cordataclosefile_();
+  double cordatanextnumber_();
+  void cordatanexttext_(char*, int);
+  int cordatacandecompress_();
+```
+
+Those functions need to be provided at link time. If provided, this
+can be used to read compressed input files (gzip, lzma, etc) as
+provided by the corsika-data git project. 
+
+The reference implementation for reading compressed files used
+boost-iostreams, but this should be changed since using boost here is
+too invasive.
+g
diff --git a/Processes/QGSJetII/qgsjet-II-04.f b/modules/qgsjetII/qgsjet-II-04-mod1.f
similarity index 100%
rename from Processes/QGSJetII/qgsjet-II-04.f
rename to modules/qgsjetII/qgsjet-II-04-mod1.f
diff --git a/modules/qgsjetII/qgsjet-II-04-org.f b/modules/qgsjetII/qgsjet-II-04-org.f
new file mode 100644
index 0000000000000000000000000000000000000000..40b2a4ccbc8da8506f5a6bafec36f190bec9b7bb
--- /dev/null
+++ b/modules/qgsjetII/qgsjet-II-04-org.f
@@ -0,0 +1,17733 @@
+C======================================================================C
+C                                                                      C
+C     QQQ        GGG      SSSS    JJJJJJJ   EEEEEEE   TTTTTTT     I I  C
+C    Q   Q      G   G    S    S         J   E            T        I I  C
+C   Q     Q    G         S              J   E            T        I I  C
+C   Q     Q    G   GGG    SSSS          J   EEEEE        T    ==  I I  C
+C   Q   Q Q    G     G        S         J   E            T        I I  C
+C    Q   Q      G   G    S    S    J   J    E            T        I I  C
+C     QQQQQ      GGG      SSSS      JJJ     EEEEEEE      T        I I  C
+C                                                                      C
+C                                                                      C
+C----------------------------------------------------------------------C
+C                                                                      C
+C                  QUARK - GLUON - STRING - JET - II MODEL             C
+C                                                                      C
+C                HIGH ENERGY HADRON INTERACTION PROGRAM                C
+C                                                                      C
+C                                  BY                                  C
+C                                                                      C
+C                           S. OSTAPCHENKO                             C
+C                                                                      C
+C Institute for Physics, Norwegian University for Science & Tech       C
+C D.V. Skobeltsyn Institute of Nuclear Physics, Moscow State UniversityC
+C                  e-mail: sergei@tf.phys.ntnu.no                      C
+C----------------------------------------------------------------------C
+C         Publication to be cited when using this program:             C
+C         S. Ostapchenko, Phys. Rev. D 83 (2011) 014018                C
+C----------------------------------------------------------------------C
+C                        LIST OF MODIFICATIONS                         C
+C                                                                      C
+C (Any modification of this program has to be approved by the author)  C
+C                                                                      C
+C 24.01.2005 - beta-version completed (qgsjet-II-01)                   C
+C 12.04.2005 - final version (qgsjet-II-02)                            C
+C 12.12.2005 - technical update -  version II-03:                      C
+C    improved treatment of Pomeron cuts (all "net" cuts included);     C
+C    improved treatment of nuclear config. (more consistent diffr.);   C
+C    "baryon junction" mechanism included (motivated by RHIC data);    C
+C    better parameter calibration, e.g. including RHIC data            C
+C 21.02.2006 - some commons enlarged to avoid frequent rejects  D.H.   C
+C 26.04.2006 - reduce unnecessary looping in qgsha              D.H.   C
+C                                                                      C
+C 01.10.2010 - new version  (qgsjet-II-04, not released):              C
+C   treating all enhanced diagrams (incuding 'Pomeron loops');         C
+C   calibration to LHC data on multiparticle production;               C
+C   a number of cosmetic improvements,                                 C
+C   e.g. more efficient simulation procedure (a factor of ~10 win)     C
+C                                                                      C
+C 26.06.2012 - final version (qgsjet-II-04):                           C
+C additional parameter retuning applied                                C
+C (mainly to TOTEM data on total/elastic pp cross sections);           C
+C remnant treatment for pion-hadron/nucleus collisions improved        C
+C                                                                      C
+C 18.03.2013 - small corrections for A-p cross-section calculation     C
+C 09.04.2013 - diffractive flag correction (no physics change)         C
+C 30.09.2013 - small bug correction to avoid rare momentum inbalance   C
+C              (no physics change)                                     C
+C                                                                      C
+C                 last modification:  09.04.2013                       C
+C                 Version qgsjet-II-04 (for CONEX)                     C
+C                                                                      C
+C small corrections to adapt to CORSIKA : 25.07.2012 by T.Pierog       C
+C=======================================================================
+
+
+
+c=============================================================================
+      subroutine qgset
+c-----------------------------------------------------------------------------
+c common model parameters setting
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      character*7 ty
+      character*2 tyq
+      parameter(iapmax=208)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr2/  scm,wp0,wm0
+      common /qgarr3/  rmin,emax,eev
+      common /qgarr6/  pi,bm,amws
+      common /qgarr7/  xa(iapmax,3),xb(iapmax,3),b
+      common /qgarr8/  wwm,be(4),dc(5),deta,almpt,ptdif,ptndi
+      common /qgarr10/ am0,amn,amk,amc,amlamc,amlam,ameta,ammu
+      common /qgarr11/ b10
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr19/ ahl(3)
+      common /qgarr20/ spmax
+      common /qgarr21/ dmmin(3),wex(3),dmres(3),wdres(3)
+      common /qgarr28/ arr(5)
+      common /qgarr26/ factk,fqscal
+      common /qgarr41/ ty(6)
+      common /qgarr42/ tyq(16)
+      common /qgarr43/ moniou
+      common /qgarr51/ epsxmn
+      common /opt/     jopt
+      common /qgdebug/ debug
+      common /qgsIInex1/xan(iapmax,3),xbn(iapmax,3)  !used to link with nexus
+     *,bqgs,bmaxqgs,bmaxnex,bminnex
+
+      moniou=6             !output channel for debugging
+      debug=0              !debugging level
+                           !(0 - no debugging, 1 - very geheral,
+                           !2 - more detailed, 3 - all function calls,
+                           !4 - all returned values, 5 - technical)
+      if(debug.ge.1)write (moniou,210)
+
+      bqgs=0.d0            !used to link with nexus
+      bmaxqgs=0.d0         !used to link with nexus
+      bmaxnex=-1.d0        !used to link with nexus
+      bminnex=0.d0         !used to link with nexus
+
+      jopt=1               !parameter option
+
+      if(jopt.eq.1)then       !tunable parameters
+c soft Pomeron parameters
+       dels=.165d0            !overcriticality
+       alfp=.135d0            !trajectory slope
+       sigs=1.01d0            !soft parton cross section
+c coupling to DGLAP
+       qt0=3.d0               !q**2 cutoff
+       betp=2.2d0             !gluon distribution hardness for soft Pomeron
+       dgqq=.16d0             !sea quark/gluon relative weight
+c multi-Pomeron vertex parameters
+       r3p=.0076d0            !triple-Pomeron coupling (/4/pi)
+       g3p=.35d0              !factor for multu-Pomeron couplings
+       sgap=exp(1.5d0)        !minimal rap-gap between 3P-vertices
+c Pomeron-hadron coupling
+       rq(1,1)=1.d0           !pion: vertex slope for 1st diffr. eigenst.
+       rq(2,1)=.15d0          !pion: vertex slope for 2nd diffr. eigenst.
+       cd(1,1)=1.75d0         !pion: relat. strenth for 1st diffr. eigenst.
+       rq(1,2)=2.52d0         !proton: vertex slope for 1st diffr. eigenst.
+       rq(2,2)=.2d0           !proton: vertex slope for 2nd diffr. eigenst.
+       cd(1,2)=1.58d0         !proton: relat. strenth for 1st diffr. eigenst.
+       rq(1,3)=.75d0          !kaon: vertex slope for 1st diffr. eigenst.
+       rq(2,3)=.15d0          !kaon: vertex slope for 2nd diffr. eigenst.
+       cd(1,3)=1.75d0         !kaon: relat. strenth for 1st diffr. eigenst.
+
+c parameters for soft/hard fragmentation:
+
+       qtf=.15d0              !q**2 cutoff for timelike cascades
+       almpt=1.5d0            !string fragmentation parameter
+       wwm=1.d0               !switching to 2-particle string decay (threshold)
+c leading state exponents
+       ahl(1)=0.d0            !pion
+       ahl(2)=1.3d0           !proton
+       ahl(3)=-0.5            !kaon
+c remnant excitation probabilities
+       wex(1)=.5d0            !pion
+       wex(2)=.4d0            !proton
+       wex(3)=.5d0            !kaon
+c dc(i) - relative probabilities for qq~(qqq~q~)-pair creation from vacuum
+       dc(1)=.077d0           !udu~d~
+       dc(2)=.08d0            !ss~
+       dc(4)=.4d0             !ss~ (intrinsic)
+c be(i) - parameters for pt-distributions
+       be(1)=.225d0           !uu~(dd~)
+       be(2)=.43d0            !qqq~q~
+       be(3)=.48d0            !ss~
+       ptdif=.15d0            !diffractive momentum transfer
+       ptndi=.19d0            !non-diffractive momentum transfer
+
+c parameters for nuclear spectator part fragmentation:
+
+       rmin=3.35d0    !coupling radius squared (fm^2)
+       emax=.11d0     !relative critical energy ( / <E_ex>, <E_ex>~12.5 MeV )
+       eev=.25d0      !relative evaporation energy ( / <E_ex>, <E_ex>~12.5 MeV )
+
+      else
+       stop'wrong option!!!'
+      endif
+
+      do i=1,3         !relative strenth of 2nd diffr. eigenst. [2-CD(1,icz)]
+       cd(2,i)=2.d0-cd(1,i)
+      enddo
+
+!other parameters and constants:
+
+      spmax=1.d11             !max energy squared for tabulations
+      delh=0.25d0             !effective exponent for weighting (technical)
+      epsxmn=.01d0            !pt-resolution scale (technical)
+      alm=.04d0               !lambda_qcd squared
+      factk=1.5d0             !k-factor value
+      fqscal=4.d0             !factor for fact. scale (Mf^2=p_t^2/fqscal)
+      deta=.11111d0           !ratio of etas production to all pions (1/9)
+      dc(3)=.000d0            !to switch off charmed particles set to 0.000
+      dc(5)=.0d0              !to switch off charmed particles set to 0.000
+c weigts for diffractive eigenstates
+      cc(1,1)=.5d0            !pion
+      cc(2,1)=.5d0
+      cc(1,2)=.5d0            !proton
+      cc(2,2)=.5d0
+      cc(1,3)=.5d0            !kaon
+      cc(2,3)=.5d0
+c auxiliary constants
+      b10=.43876194d0         !initial value of the pseudorandom sequence
+      pi=3.1416d0             !pi-value
+      amws=.523d0             !diffusive radius for saxon-wood density
+c regge intercepts for the uu~, qqq~q~, us~, uc~ trajectories
+      arr(1)=0.5d0            !qq~-trajectory
+      arr(2)=-0.5d0           !qqq~q~-trajectory
+      arr(3)=0.d0             !us~-trajectory
+c lowest resonance masses for low-mass excitations
+      dmmin(1)=.76d0          !rho
+      dmmin(2)=1.23d0         !delta
+      dmmin(3)=.89d0          !K*
+c mass and width for resonance contribution to low mass diffraction
+      dmres(1)=1.23d0         !pion
+      dmres(2)=1.44d0         !proton
+      dmres(3)=1.27d0         !kaon
+      wdres(1)=.3d0           !pion
+      wdres(2)=.3d0           !proton
+      wdres(3)=.1d0           !kaon
+c proton, kaon, pion, d-meson, lambda, lambda_c, eta masses
+      amn=0.93827999
+      amk=.496d0
+      am0=.14d0
+      amc=1.868d0
+      amlam=1.116d0
+      amlamc=2.27d0
+      ameta=.548d0
+      ammu=.1057d0
+c initial particle classes
+      ty(1)='pion   '
+      ty(2)='nucleon'
+      ty(3)='kaon   '
+c parton types
+      tyq(1)='DD'
+      tyq(2)='UU'
+      tyq(3)='C '
+      tyq(4)='S '
+      tyq(5)='UD'
+      tyq(6)='D '
+      tyq(7)='U '
+      tyq(8)='g '
+      tyq(9)='u '
+      tyq(10)='d '
+      tyq(11)='ud'
+      tyq(12)='s '
+      tyq(13)='c '
+      tyq(14)='uu'
+      tyq(15)='dd'
+      if(debug.ge.2)write (moniou,202)
+
+210   format(2x,'qgset - common model parameters setting')
+202   format(2x,'qgset - end')
+      return
+      end
+
+c=============================================================================
+      subroutine qgaini( DATDIR )
+c-----------------------------------------------------------------------------
+c common initialization procedure
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      CHARACTER DATDIR*(132)
+      real qggamfun
+      integer debug
+      character *7 ty
+      logical lcalc
+      parameter(iapmax=208)
+      dimension mij(40,40,4),nij(40,40,4),cs1(40,40,160)
+     *,evs(40,100,3,2),ixemax(40,3,2),gz0(5),gz1(3)
+     *,qfan0(11,14),qfan2(11,11,3),fann(14)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr2/  scm,wp0,wm0
+      common /qgarr5/  rnuc(2),wsnuc(2),wbnuc(2),anorm
+     *,cr1(2),cr2(2),cr3(2)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr10/ am(7),ammu
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr19/ ahl(3)
+      common /qgarr20/ spmax
+      common /qgarr24/ qpomr(11,11,216,12,2)
+      common /qgarr25/ ahv(3)
+      common /qgarr26/ factk,fqscal
+      common /qgarr27/ qlegi(51,11,2,3,7),qfanu(51,11,11,6,2)
+     *,qfanc(51,11,11,39,18),qdfan(21,11,11,2,3),qrev(11,11,66,219,2)
+      common /qgarr28/ arr(5)
+      common /qgarr29/ cstot(40,40,160)
+      common /qgarr30/ cs0(40,40,160)
+      common /qgarr31/ csborn(40,160)
+      common /qgarr33/ fsud(10,2)
+      common /qgarr34/ qrt(10,101,2)
+      common /qgarr35/ qlegc0(51,10,11,6,8),qlegc(51,10,11,11,30)
+      common /qgarr38/ qpomc(11,100,11,11,48)
+      common /qgarr39/ qpomi(51,11,15),qpomis(51,11,11,11,9)
+      common /qgarr41/ ty(6)
+      common /qgarr43/ moniou
+      common /qgarr47/ gsect(10,5,6)
+      common /qgarr48/ qgsasect(10,6,6)
+      common /qgarr51/ epsxmn
+      common /qgarr52/ evk(40,40,100,3,2)
+c auxiliary common blocks to calculate hadron-nucleus cross-sections
+      common /arr1/   trnuc(56),twsnuc(56),twbnuc(56)
+      common /arr3/   x1(7),a1(7)
+      common /opt/    jopt
+      common /qgdebug/debug
+      character*500 fnIIdat,fnIIncs                        !used to link with nexus
+      common /version/ version                             !used to link with nexus
+      common/qgsIIfname/fnIIdat, fnIIncs, ifIIdat, ifIIncs !used to link with nexus
+      common/qgsIInfname/ nfnIIdat, nfnIIncs               !used to link with nexus
+      common/producetab/ producetables              !used to link with CRMC
+      logical producetables
+
+      if(debug.ge.1)write (moniou,210)
+      version = 204
+
+c-------------------------------------------------
+      write(*,100)
+ 100  format(' ',
+     *           '====================================================',
+     *     /,' ','|                                                  |',
+     *     /,' ','|         QUARK GLUON STRING JET -II MODEL         |',
+     *     /,' ','|                                                  |',
+     *     /,' ','|         HADRONIC INTERACTION MONTE CARLO         |',
+     *     /,' ','|                        BY                        |',
+     *     /,' ','|                 S. OSTAPCHENKO                   |',
+     *     /,' ','|                                                  |',
+     *     /,' ','|             e-mail: sergei@tf.phys.ntnu.no       |',
+     *     /,' ','|                                                  |',
+     *     /,' ','|                   Version II-04                  |',
+     *     /,' ','|                                                  |',
+     *     /,' ','| Publication to be cited when using this program: |',
+     *     /,' ','| S.Ostapchenko, PRD 83 (2011) 014018              |',
+     *     /,' ','|                                                  |',
+     *     /,' ','| last modification:  09.04.2013                   |',
+     *     /,' ','|                                                  |',
+     *     /,' ','| Any modification has to be approved by the author|',
+     *     /,' ','====================================================',
+     *     /)
+
+
+c-----------------------------------------------------------------------------
+c normalization of parton density in the soft pomeron
+      rr=qggamfun(real(2.d0+betp-dels))/qggamfun(real(1.d0-dels))
+     */qggamfun(real(1.d0+betp))/4.d0/pi
+
+      ahv(1)=.383d0+.624d0*dlog(dlog(qt0/.204d0**2)
+     */dlog(.26d0/.204d0**2))
+      ahv(3)=ahv(1)
+      sq=dlog(dlog(qt0/.232d0**2)/dlog(.23d0/.232d0**2))
+      ahv(2)=2.997d0+.753d0*sq-.076d0*sq*sq
+c valence quark momentum share
+      qnorm1=0.d0
+      do i=1,7
+      do m=1,2
+       tp=1.d0-(.5d0+x1(i)*(m-1.5d0))**(2.d0/3.d0)
+       xp=1.d0-tp**(1.d0/(1.d0+ahv(1)))
+       qnorm1=qnorm1+a1(i)*(qggrv(xp,qt0,1,1)+qggrv(xp,qt0,1,2))
+     * /dsqrt(1.d0-tp)
+      enddo
+      enddo
+      qnorm1=qnorm1/(1.d0+ahv(1))/3.d0
+      qnorm2=0.d0
+      do i=1,7
+      do m=1,2
+       tp=1.d0-(.5d0+x1(i)*(m-1.5d0))**(2.d0/3.d0)
+       xp=1.d0-tp**(1.d0/(1.d0+ahv(2)))
+       qnorm2=qnorm2+a1(i)*(qggrv(xp,qt0,2,1)+qggrv(xp,qt0,2,2))
+     * /dsqrt(1.d0-tp)
+      enddo
+      enddo
+      qnorm2=qnorm2/(1.d0+ahv(2))/3.d0
+c fp(i) - pomeron vertex constant (i=icz)
+      fp(2)=(1.d0-qnorm2)*(2.d0+ahl(2))*(1.d0+ahl(2))
+
+      gnorm=0.d0
+      seanrm=0.d0
+      do i=1,7
+      do m=1,2
+       xxg=(.5d0+x1(i)*(m-1.5d0))**(1.d0/(1.d0-dels))
+       gnorm=gnorm+a1(i)*qgftld(xxg,2)
+       seanrm=seanrm+a1(i)*qgftle(xxg,2)
+      enddo
+      enddo
+      gnorm=gnorm/(1.d0-dels)*fp(2)*rr*2.d0*pi
+      seanrm=seanrm/(1.d0-dels)*fp(2)*rr*2.d0*pi
+      if(debug.ge.1)write (moniou,*)'rr,fp,norm,qnorm2,gnorm,seanrm'
+     *,rr,fp(2),qnorm2+gnorm+seanrm,qnorm2,gnorm,seanrm
+
+      do icz=1,3,2
+       fp(icz)=(1.d0-qnorm1)*(2.d0+ahl(icz))*(1.d0+ahl(icz))
+       gnorm=0.d0
+       seanrm=0.d0
+       do i=1,7
+       do m=1,2
+        xxg=(.5d0+x1(i)*(m-1.5d0))**(1.d0/(1.d0-dels))
+        gnorm=gnorm+a1(i)*qgftld(xxg,icz)
+        seanrm=seanrm+a1(i)*qgftle(xxg,icz)
+       enddo
+       enddo
+       gnorm=gnorm/(1.d0-dels)*fp(icz)*rr*2.d0*pi
+       seanrm=seanrm/(1.d0-dels)*fp(icz)*rr*2.d0*pi
+
+       if(debug.ge.1)write (moniou,*)'fp,norm,qnorm1,gnorm,seanrm'
+     * ,fp(icz),qnorm1+gnorm+seanrm,qnorm1,gnorm,seanrm
+      enddo
+
+      do icz=1,3
+       gsoft(icz)=fp(icz)*fp(2)*sigs*4.d0*.0389d0
+     * *qggamfun(real(1.d0+dels))**2*qggamfun(real(1.d0+ahl(icz)))
+     * *qggamfun(real(1.d0+ahl(2)))/qggamfun(real(2.d0+dels+ahl(icz)))
+     * /qggamfun(real(2.d0+dels+ahl(2)))
+      enddo
+
+c-----------------------------------------------------------------------------
+c reading cross sections from the file
+      if(ifIIdat.ne.1)then
+       inquire(file=DATDIR(1:INDEX(DATDIR,' ')-1)//'qgsdat-II-04'
+     *        ,exist=lcalc)
+      else
+       inquire(file=fnIIdat(1:nfnIIdat),exist=lcalc)       !used to link with nexus
+      endif
+      lzmaUse=0
+      if(lcalc)then
+         if(ifIIdat.ne.1)then
+            open(1,file=DATDIR(1:INDEX(DATDIR,' ')-1)//'qgsdat-II-04'
+     *           ,status='old')
+         else                   !used to link with nexus
+            if (LEN(fnIIdat).gt.6.and.
+     *           fnIIdat(nfnIIdat-4:nfnIIdat) .eq. ".lzma") then
+               lzmaUse=1
+               call LzmaOpenFile(fnIIdat(1:nfnIIdat))
+            else
+               open(ifIIdat,file=fnIIdat(1:nfnIIdat),status='old')
+            endif
+         endif
+
+       if (lzmaUse.ne.0) then
+
+          if(debug.ge.0)write (moniou,214) 'qgsdat-II-04.lzma'
+
+          call LzmaFillArray(csborn,size(csborn))
+          call LzmaFillArray(cs0,size(cs0))
+          call LzmaFillArray(cstot,size(cstot))
+          call LzmaFillArray(evk,size(evk))
+          call LzmaFillArray(qpomi,size(qpomi))
+          call LzmaFillArray(qpomis,size(qpomis))
+          call LzmaFillArray(qlegi,size(qlegi))
+          call LzmaFillArray(qfanu,size(qfanu))
+          call LzmaFillArray(qfanc,size(qfanc))
+          call LzmaFillArray(qdfan,size(qdfan))
+          call LzmaFillArray(qpomr,size(qpomr))
+          call LzmaFillArray(gsect,size(gsect))
+          call LzmaFillArray(qlegc0,size(qlegc0))
+          call LzmaFillArray(qlegc,size(qlegc))
+          call LzmaFillArray(qpomc,size(qpomc))
+          call LzmaFillArray(fsud,size(fsud))
+          call LzmaFillArray(qrt,size(qrt))
+          call LzmaFillArray(qrev,size(qrev))
+          call LzmaFillArray(fsud,size(fsud))
+          call LzmaFillArray(qrt,size(qrt))
+          call LzmaCloseFile()
+       else
+          if(debug.ge.0)write (moniou,214) 'qgsdat-II-04'
+          read (1,*)csborn,cs0,cstot,evk,qpomi,qpomis,qlegi,qfanu,qfanc
+     *         ,qdfan,qpomr,gsect,qlegc0,qlegc,qpomc,fsud,qrt,qrev,fsud,
+     *         qrt
+          close(1)
+       endif
+
+       if(debug.ge.0)write (moniou,*)'done'
+       goto 10
+      elseif(.not.producetables)then
+        write(moniou,*) "Missing QGSDAT-II-04 file !"
+        write(moniou,*) "Please correct the defined path ",
+     &"or force production ..."
+        stop
+      endif
+
+c--------------------------------------------------
+c qcd evolution and qcd ladder cross sections
+c--------------------------------------------------
+      if(debug.ge.0)write (moniou,201)
+      do i=1,40
+      do m=1,3
+      do k=1,2
+       ixemax(i,m,k)=99
+      do j=1,40
+      do l=1,100
+       evk(i,j,l,m,k)=0.d0
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+
+      n=1
+1     n=n+1
+      do m=1,3
+      do k=1,2
+       if(m.ne.3.or.k.ne.1)then
+        do i=1,39
+         if(ixemax(i,m,k).gt.0)then
+          qi=spmax**((i-1)/39.d0)
+          qq=qi*(spmax/qi)**(1.d0/39.d0)
+          do l=1,99
+           if(l.le.37)then
+            xx=.1d0/(.1d0*spmax)**((37.d0-l)/36.d0)
+           elseif(l.le.69)then
+            xx=.1d0+.8d0*(l-37.d0)/32.d0
+           else
+            xx=1.d0-.1d0*(10.d0*epsxmn)**((l-69.d0)/31.d0)
+           endif
+
+           ev=qgev(qi,qq,qq,xx,m,k)/qgfap(xx,m,k)
+           if(m.eq.1.and.k.eq.1.or.m.ne.1.and.k.ne.1)then
+            evs(i,l,m,k)=dlog(1.d0+ev*4.5d0*qgsudx(qi,m)/qgsudx(qq,m)
+     *      /dlog(dlog(qq/alm)/dlog(qi/alm)))
+           else
+            evs(i,l,m,k)=dlog(1.d0+ev/.3d0*(dlog(epsxmn)+.75d0)
+     *      /(qgsudx(qq,1)/qgsudx(qi,1)-qgsudx(qq,2)/qgsudx(qi,2)))
+           endif
+          enddo
+         endif
+        enddo
+       endif
+      enddo
+      enddo
+
+      jec=0
+      do m=1,3
+      do k=1,2
+       if(m.ne.3.or.k.ne.1)then
+        do i=1,39
+         if(ixemax(i,m,k).gt.0)then
+          qi=spmax**((i-1)/39.d0)
+          qq=qi*(spmax/qi)**(1.d0/39.d0)
+          imx=ixemax(i,m,k)
+          do l=imx,1,-1
+           if(l.le.37)then
+            xx=.1d0/(.1d0*spmax)**((37.d0-l)/36.d0)
+           elseif(l.le.69)then
+            xx=.1d0+.8d0*(l-37.d0)/32.d0
+           else
+            xx=1.d0-.1d0*(10.d0*epsxmn)**((l-69.d0)/31.d0)
+           endif
+
+           if(abs(evs(i,l,m,k)-evk(i,2,l,m,k)).gt.1.d-3)then
+            evk(i,2,l,m,k)=evs(i,l,m,k)
+            jec=1
+           elseif(ixemax(i,m,k).eq.l)then
+            ixemax(i,m,k)=l-1
+           endif
+          enddo
+         endif
+        enddo
+       endif
+      enddo
+      enddo
+
+      do i=1,39
+       qi=spmax**((i-1)/39.d0)
+       qj=qi*(spmax/qi)**(1.d0/39.d0)
+       qq=qi*(spmax/qi)**(2.d0/39.d0)
+       do l=99,1,-1
+        if(l.le.37)then
+         xx=.1d0/(.1d0*spmax)**((37.d0-l)/36.d0)
+        elseif(l.le.69)then
+         xx=.1d0+.8d0*(l-37.d0)/32.d0
+        else
+         xx=1.d0-.1d0*(10.d0*epsxmn)**((l-69.d0)/31.d0)
+        endif
+        do m=1,3
+        do k=1,2
+         if(m.ne.3.or.k.ne.1)then
+          ev=(qgev(qi,qj,qq,xx,m,k)
+     *    +qgevi(qi,qj,xx,m,k)*qgsudx(qq,k)/qgsudx(qj,k)
+     *    +qgevi(qj,qq,xx,m,k)*qgsudx(qj,m)/qgsudx(qi,m))/qgfap(xx,m,k)
+          if(m.eq.1.and.k.eq.1.or.m.ne.1.and.k.ne.1)then
+           evk(i,3,l,m,k)=dlog(ev*4.5d0*qgsudx(qi,m)/qgsudx(qq,m)
+     *     /dlog(dlog(qq/alm)/dlog(qi/alm)))
+          else
+           evk(i,3,l,m,k)=dlog(ev/.3d0*(dlog(epsxmn)+.75d0)
+     *     /(qgsudx(qq,1)/qgsudx(qi,1)-qgsudx(qq,2)/qgsudx(qi,2)))
+          endif
+         endif
+        enddo
+        enddo
+       enddo
+      enddo
+      if(jec.ne.0)goto 1
+
+      do i=1,39
+       qi=spmax**((i-1)/39.d0)
+      do j=4,40
+       qj=qi*(spmax/qi)**((j-2)/39.d0)
+       qq=qi*(spmax/qi)**((j-1)/39.d0)
+       do l=99,1,-1
+        if(l.le.37)then
+         xx=.1d0/(.1d0*spmax)**((37.d0-l)/36.d0)
+        elseif(l.le.69)then
+         xx=.1d0+.8d0*(l-37.d0)/32.d0
+        else
+         xx=1.d0-.1d0*(10.d0*epsxmn)**((l-69.d0)/31.d0)
+        endif
+        do m=1,3
+        do k=1,2
+         if(m.ne.3.or.k.ne.1)then
+          ev=(qgev(qi,qj,qq,xx,m,k)
+     *    +qgevi(qi,qj,xx,m,k)*qgsudx(qq,k)/qgsudx(qj,k)
+     *    +qgevi(qj,qq,xx,m,k)*qgsudx(qj,m)/qgsudx(qi,m))/qgfap(xx,m,k)
+          if(m.eq.1.and.k.eq.1.or.m.ne.1.and.k.ne.1)then
+           evk(i,j,l,m,k)=dlog(ev*4.5d0*qgsudx(qi,m)/qgsudx(qq,m)
+     *     /dlog(dlog(qq/alm)/dlog(qi/alm)))
+          else
+           evk(i,j,l,m,k)=dlog(ev/.3d0*(dlog(epsxmn)+.75d0)
+     *     /(qgsudx(qq,1)/qgsudx(qi,1)-qgsudx(qq,2)/qgsudx(qi,2)))
+          endif
+         endif
+        enddo
+        enddo
+       enddo
+      enddo
+      enddo
+
+c--------------------------------------------------
+c qcd ladder cross sections
+      do i=1,40
+       qi=(spmax/4.d0/fqscal)**((i-1)/39.d0)  !q^2 cutoff for born process
+       s2min=qi*4.d0*fqscal          !energy threshold for 2->2 subprocess
+      do m=1,2                                !parton types (1-g, 2-q)
+      do l=1,2                                !parton types (1-g, 2-q)
+       l1=2*l-1
+      do k=1,40
+       sk=s2min*(spmax/s2min)**((k-1)/39.d0)  !c.m. energy squared
+       k1=k+40*(m-1)+80*(l-1)
+       csborn(i,k1)=dlog(qgborn(qi,qi,sk,m-1,l1-1)) !born cross-section (2->2)
+       if(.not.(csborn(i,k1).ge.0.d0.or.csborn(i,k1).lt.0.d0))stop
+      enddo
+      enddo
+      enddo
+      enddo
+
+      do i=1,40
+       qi=(spmax/4.d0/fqscal)**((i-1)/39.d0)
+      do j=1,40
+       qj=qi*(spmax/4.d0/fqscal/qi)**((j-1)/39.d0)
+       s2min=qj*4.d0*fqscal
+       smin=s2min/(1.d0-epsxmn)
+      do m=1,2
+      do l=1,2
+       l1=2*l-1
+       ml=m+2*l-2
+      do k=1,40
+       sk=s2min*(spmax/s2min)**((k-1)/39.d0)
+       k1=k+40*(m-1)+80*(l-1)
+
+       tmin=qj*fqscal/(.5d0+dsqrt(max(0.d0,.25d0-qj*fqscal/sk)))
+       sjtot=qgjett(qi,qj,sk,m-1,l-1)
+       sjord1=qgjeto(qi,qj,sk,m-1,l-1)
+       sjord2=qgjeto(qj,qi,sk,l-1,m-1)
+       born=qgborn(qi,qj,sk,m-1,l1-1)
+       if(k.eq.1.or.j.eq.40.or.i.eq.40.or.sk.le.smin)then
+        cstot(i,j,k1)=dlog(born)
+        cs0(i,j,k1)=cstot(i,j,k1)
+       else
+        cstot(i,j,k1)=dlog(born+(sjtot+sjord1+sjord2)
+     *  /(1.d0/tmin-2.d0/sk))
+        cs0(i,j,k1)=dlog(born+sjord1/(1.d0/tmin-2.d0/sk))
+       endif
+       if(.not.(cstot(i,j,k1).ge.0.d0.or.cstot(i,j,k1).lt.0.d0))stop
+       if(.not.(cs0(i,j,k1).ge.0.d0.or.cs0(i,j,k1).lt.0.d0))stop
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      goto 3
+
+c--------------------------------------------------
+c alternative calculation (not used)
+      do i=1,40
+       qi=(spmax/4.d0/fqscal)**((i-1)/39.d0)
+      do j=1,40
+       qj=qi*(spmax/4.d0/fqscal/qi)**((j-1)/39.d0)
+       s2min=qj*4.d0*fqscal
+      do m=1,2
+      do l=1,2
+       l1=2*l-1
+       ml=m+2*l-2
+      do k=1,40
+       sk=s2min*(spmax/s2min)**((k-1)/39.d0)
+       k1=k+40*(m-1)+80*(l-1)
+       cstot(i,j,k1)=dlog(qgborn(qi,qj,sk,m-1,l1-1))
+       cs0(i,j,k1)=cstot(i,j,k1)
+       mij(i,j,ml)=2
+       nij(i,j,ml)=2
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+
+      n=2                             !number of ladder rungs considered
+2     if(debug.ge.1)write (moniou,202)n,mij(1,1,1),nij(1,1,1)
+      do i=1,39
+       qi=(spmax/4.d0/fqscal)**((i-1)/39.d0)       !q^2 for upper parton
+      do j=1,39
+       qj=qi*(spmax/4.d0/fqscal/qi)**((j-1)/39.d0) !q^2 for downer parton
+       s2min=qj*4.d0*fqscal                !energy threshold for 2->2 subprocess
+       smin=s2min/(1.d0-epsxmn)            !energy threshold for 2->3 subprocess
+      do m=1,2                                     !parton types (1-g, 2-q)
+      do l=1,2                                     !parton types (1-g, 2-q)
+       l1=2*l-1
+       ml=m+2*l-2
+       kmin=nij(i,j,ml)                  !lowest energy bin for another rung
+       if(kmin.le.40)then
+        do k=kmin,40
+         sk=s2min*(spmax/s2min)**((k-1)/39.d0)
+         if(sk.le.smin)then
+          nij(i,j,ml)=nij(i,j,ml)+1
+         else
+          k1=k+40*(m-1)+80*(l-1)
+          tmin=qj*fqscal/(.5d0+dsqrt(max(0.d0,.25d0-qj*fqscal/sk)))
+          cs1(i,j,k1)=dlog(qgjet1(qi,qj,sk,s2min,m,l)
+     *    /(1.d0/tmin-2.d0/sk)+qgborn(qi,qj,sk,m-1,l1-1))
+         endif
+        enddo
+       endif
+      enddo
+      enddo
+      enddo
+      enddo
+
+      do i=1,39
+       qi=(spmax/4.d0/fqscal)**((i-1)/39.d0)
+      do j=1,39
+       qj=qi*(spmax/4.d0/fqscal/qi)**((j-1)/39.d0)
+       s2min=qj*4.d0*fqscal
+      do m=1,2
+      do l=1,2
+       ml=m+2*l-2
+       kmin=nij(i,j,ml)
+       if(kmin.le.40)then
+        do k=40,kmin,-1
+         sk=s2min*(spmax/s2min)**((k-1)/39.d0)
+         tmin=qj*fqscal/(.5d0+dsqrt(max(0.d0,.25d0-qj*fqscal/sk)))
+         k1=k+40*(m-1)+80*(l-1)
+         if(abs(cs1(i,j,k1)-cs0(i,j,k1)).gt.1.d-2)then
+          cs0(i,j,k1)=cs1(i,j,k1)
+         elseif(k.eq.nij(i,j,ml))then
+          nij(i,j,ml)=nij(i,j,ml)+1
+         endif
+        enddo
+       endif
+      enddo
+      enddo
+      enddo
+      enddo
+
+      do i=1,39
+       qi=(spmax/4.d0/fqscal)**((i-1)/39.d0)
+      do j=1,39
+       qj=qi*(spmax/4.d0/fqscal/qi)**((j-1)/39.d0)
+       s2min=qj*4.d0*fqscal         !min energy squared for 2->2 subprocess
+       smin=s2min/(1.d0-epsxmn)     !min energy squared for 2->3 subprocess
+      do m=1,2
+      do l=1,2
+       ml=m+2*l-2
+       kmin=mij(i,j,ml)             !min energy bin for more ladder rungs
+       if(kmin.le.40)then
+        do k=kmin,40
+         sk=s2min*(spmax/s2min)**((k-1)/39.d0)
+         if(sk.le.smin)then
+          mij(i,j,ml)=mij(i,j,ml)+1
+         else
+          k1=k+40*(m-1)+80*(l-1)
+          tmin=qj*fqscal/(.5d0+dsqrt(max(0.d0,.25d0-qj*fqscal/sk)))
+          cs1(i,j,k1)=dlog((qgjet(qi,qj,sk,s2min,m,l)
+     *    +qgjit1(qj,qi,sk,l,m))/(1.d0/tmin-2.d0/sk))
+         endif
+        enddo
+       endif
+      enddo
+      enddo
+      enddo
+      enddo
+
+c--------------------------------------------------
+c check convergence
+      do i=1,39
+       qi=(spmax/4.d0/fqscal)**((i-1)/39.d0)
+      do j=1,39
+       qj=qi*(spmax/4.d0/fqscal/qi)**((j-1)/39.d0)
+       s2min=qj*4.d0*fqscal
+      do m=1,2
+      do l=1,2
+       ml=m+2*l-2
+       kmin=mij(i,j,ml)             !min energy bin for more ladder rungs
+       if(kmin.le.40)then
+        do k=40,kmin,-1
+         sk=s2min*(spmax/s2min)**((k-1)/39.d0)
+         tmin=qj*fqscal/(.5d0+dsqrt(max(0.d0,.25d0-qj*fqscal/sk)))
+         k1=k+40*(m-1)+80*(l-1)
+         if(abs(cs1(i,j,k1)-cstot(i,j,k1)).gt.1.d-2)then
+          cstot(i,j,k1)=cs1(i,j,k1)
+         elseif(k.eq.mij(i,j,ml))then
+          mij(i,j,ml)=mij(i,j,ml)+1
+         endif
+        enddo
+       endif
+      enddo
+      enddo
+      enddo
+      enddo
+
+      n=n+1                         !one more rung
+      do i=1,39
+      do j=1,39
+      do l=1,4
+       if(mij(i,j,l).le.40.or.nij(i,j,l).le.40)goto 2
+      enddo
+      enddo
+      enddo
+
+3     if(debug.ge.2)write (moniou,205)
+c-------------------------------------------------
+c itermediate Pomeron
+      if(debug.ge.1)write (moniou,210)
+      s2min=4.d0*fqscal*qt0
+      do iy=1,51
+       sy=sgap*(spmax/sgap)**((iy-1)/50.d0)
+       rp=alfp*log(sy)*4.d0*.0389d0
+      do iz=1,11
+       if(iz.gt.6)then
+        z=.2d0*(iz-6)
+        b=sqrt(-log(z)*rp)
+       else
+        b=sqrt(-rp*(log(0.2d0)+2.d0*(iz-7)))
+        z=exp(-b*b/rp)
+       endif
+
+       qpomi(iy,iz,1)=dlog(qgpint(sy,b*b)
+     * /sy**dels/sigs/z*rp/4.d0/.0389d0+1.d0)
+      enddo
+      enddo
+
+c-------------------------------------------------
+c loop contribution
+      do iy=1,51
+       sy=sgap*(spmax/sgap)**((iy-1)/50.d0)
+       rp=alfp*log(sy)*4.d0*.0389d0
+      do iz=1,11
+       if(iz.gt.6)then
+        z=.2d0*(iz-6)
+        b=sqrt(-log(z)*rp)
+       else
+        b=sqrt(-rp*(log(0.2d0)+2.d0*(iz-7)))
+        z=exp(-b*b/rp)
+       endif
+      do iqq=2,4
+       qpomi(iy,iz,iqq)=qpomi(iy,iz,1)
+      enddo
+      enddo
+      enddo
+
+      do iy=2,51
+       sy=sgap*(spmax/sgap)**((iy-1)/50.d0)
+       rp=alfp*log(sy)*4.d0*.0389d0
+       do iz=1,11
+       do iqq=2,4
+        qpomi(iy,iz,iqq)=qpomi(iy-1,iz,iqq)
+       enddo
+       enddo
+       n=0
+4      n=n+1
+       nrep=0
+       do iz=1,11
+        if(iz.gt.6)then
+         z=.2d0*(iz-6)
+         b=sqrt(-log(z)*rp)
+        else
+         b=sqrt(-rp*(log(0.2d0)+2.d0*(iz-7)))
+         z=exp(-b*b/rp)
+        endif
+        call qgloop(sy,b*b,fann,1)
+        do iqq=1,3
+         if(fann(iqq).gt.0.d0)then
+          qfan0(iz,iqq)=dlog(fann(iqq)/z/sigs/sy**dels*rp/g3p**2
+     *    /4.d0/.0389d0)
+         elseif(iy.gt.2)then
+          qfan0(iz,iqq)=min(2.d0*qpomi(iy-1,iz,iqq+1)
+     *    -qpomi(iy-2,iz,iqq+1),qpomi(iy-1,iz,iqq+1))
+         else
+          stop'loop<0: iy=2'
+         endif
+         if(qfan0(iz,iqq).lt.-20.d0)then
+          qfan0(iz,iqq)=-20.d0
+         endif
+         if(abs(qfan0(iz,iqq)-qpomi(iy,iz,iqq+1)).gt.1.d-3)nrep=1
+        enddo
+       enddo
+       do iz=1,11
+        if(iz.gt.6)then
+         z=.2d0*(iz-6)
+         b=sqrt(-log(z)*rp)
+        else
+         b=sqrt(-rp*(log(0.2d0)+2.d0*(iz-7)))
+         z=exp(-b*b/rp)
+        endif
+       do iqq=2,4
+        qpomi(iy,iz,iqq)=qfan0(iz,iqq-1)
+        if(.not.(qpomi(iy,iz,iqq).le.0.d0
+     *  .or.qpomi(iy,iz,iqq).gt.0.d0))stop'qpom-nan'
+       enddo
+       enddo
+       if(nrep.eq.1.and.n.lt.100)goto 4
+      enddo
+
+c-------------------------------------------------
+c cut loops
+      do iy=1,51
+      do iz=1,11
+       do iqq=5,7
+        qpomi(iy,iz,iqq)=qpomi(iy,iz,iqq-3)
+       enddo
+       qpomi(iy,iz,8)=qpomi(iy,iz,2)
+       do iqq=9,10
+        qpomi(iy,iz,iqq)=qpomi(iy,iz,iqq-7)
+        qpomi(iy,iz,iqq+2)=qpomi(iy,iz,iqq-7)
+       enddo
+       do iqq=13,15
+        qpomi(iy,iz,iqq)=qpomi(iy,iz,iqq-11)
+       enddo
+      enddo
+      enddo
+
+      do iy=2,51
+       sy=sgap*(spmax/sgap)**((iy-1)/50.d0)
+       rp=alfp*log(sy)*4.d0*.0389d0
+       do iz=1,11
+       do iqq=5,15
+        qpomi(iy,iz,iqq)=qpomi(iy-1,iz,iqq)
+       enddo
+       enddo
+       n=0
+5      n=n+1
+       nrep=0
+       do iz=1,11
+        if(iz.gt.6)then
+         z=.2d0*(iz-6)
+         b=sqrt(-log(z)*rp)
+        else
+         b=sqrt(-rp*(log(0.2d0)+2.d0*(iz-7)))
+         z=exp(-b*b/rp)
+        endif
+        call qgloop(sy,b*b,fann,2)
+        do iqq=4,14
+         if(fann(iqq).gt.0.d0)then
+          qfan0(iz,iqq)=dlog(fann(iqq)/z/sigs/sy**dels*rp/g3p**2
+     *    /4.d0/.0389d0)
+         elseif(iy.gt.2)then
+          qfan0(iz,iqq)=min(2.d0*qpomi(iy-1,iz,iqq+1)
+     *    -qpomi(iy-2,iz,iqq+1),qpomi(iy-1,iz,iqq+1))
+         else
+          stop'loop<0: iy=2'
+         endif
+         if(qfan0(iz,iqq).lt.-20.d0)then
+          qfan0(iz,iqq)=-20.d0
+         endif
+         if(abs(qfan0(iz,iqq)-qpomi(iy,iz,iqq+1)).gt.1.d-3)nrep=1
+        enddo
+       enddo
+       do iz=1,11
+        if(iz.gt.6)then
+         z=.2d0*(iz-6)
+         b=sqrt(-log(z)*rp)
+        else
+         b=sqrt(-rp*(log(0.2d0)+2.d0*(iz-7)))
+         z=exp(-b*b/rp)
+        endif
+       do iqq=5,15
+        qpomi(iy,iz,iqq)=qfan0(iz,iqq-1)
+        if(.not.(qpomi(iy,iz,iqq).le.0.d0
+     *  .or.qpomi(iy,iz,iqq).gt.0.d0))stop'qpomi-nan'
+       enddo
+       enddo
+       if(nrep.eq.1.and.n.lt.50)goto 5
+      enddo
+
+c-------------------------------------------------
+c cut loops with proj/targ screening corrections
+      do iv=1,11
+       vvx=dble(iv-1)/10.d0
+      do iv1=1,11
+       vvxt=dble(iv1-1)/10.d0
+
+       do iz=1,11
+        do iqq=1,8
+         qpomis(1,iz,iv,iv1,iqq)=0.d0
+        enddo
+        qpomis(1,iz,iv,iv1,1)=qpomi(1,iz,1)
+        qpomis(1,iz,iv,iv1,4)=qpomi(1,iz,1)
+       enddo
+
+       do iy=2,51
+        sy=sgap*(spmax/sgap)**((iy-1)/50.d0)
+        rp=alfp*log(sy)*4.d0*.0389d0
+        do iz=1,11
+        do iqq=1,8
+         qpomis(iy,iz,iv,iv1,iqq)=qpomis(iy-1,iz,iv,iv1,iqq)
+        enddo
+        enddo
+
+        n=0
+6       n=n+1
+        nrep=0
+        do iz=1,11
+         if(iz.gt.6)then
+          z=.2d0*(iz-6)
+          b=sqrt(-log(z)*rp)
+         else
+          b=sqrt(-rp*(log(0.2d0)+2.d0*(iz-7)))
+          z=exp(-b*b/rp)
+         endif
+         call qgloos(sy,b*b,vvx,vvxt,fann)
+         vi=qgpini(sy,b*b,0.d0,0.d0,2)
+         vic=min(vi,qgpini(sy,b*b,0.d0,0.d0,8))
+         vicng=min(vic,qgpini(sy,b*b,0.d0,0.d0,11))
+         do iqq=1,8
+          if(fann(iqq).gt.0.d0)then
+           if(iqq.eq.1.or.iqq.eq.4)then
+            qfan0(iz,iqq)=dlog(fann(iqq)/z/sigs/sy**dels*rp/g3p**2
+     *      /4.d0/.0389d0)
+           elseif(iqq.eq.3)then
+            qfan0(iz,iqq)=dlog(fann(iqq)/(.5d0*max(0.d0,1.d0
+     *      -exp(-2.d0*vic)*(1.d0+2.d0*vic))+vicng*exp(-2.d0*vic)))
+           elseif(iqq.gt.6)then
+            qfan0(iz,iqq)=dlog(fann(iqq)*2.d0/((1.d0-exp(-vi))**2
+     *      +(exp(2.d0*(vi-vic))-1.d0)*exp(-2.d0*vi)))
+           else
+            qfan0(iz,iqq)=dlog(fann(iqq)/(1.d0-exp(-vi)))
+           endif
+          elseif(iy.gt.2)then
+           qfan0(iz,iqq)=min(2.d0*qpomis(iy-1,iz,iv,iv1,iqq)
+     *     -qpomis(iy-2,iz,iv,iv1,iqq),qpomis(iy-1,iz,iv,iv1,iqq))
+          else
+           qfan0(iz,iqq)=qpomis(iy-1,iz,iv,iv1,iqq)
+          endif
+          if(iqq.gt.5)qfan0(iz,iqq)=min(qfan0(iz,iqq),qfan0(iz,iqq-1))
+          qfan0(iz,iqq)=max(qfan0(iz,iqq),-20.d0)
+          if(abs(qfan0(iz,iqq)-qpomis(iy,iz,iv,iv1,iqq)).gt.1.d-3)
+     *    nrep=1
+         enddo
+        enddo
+
+        do iz=1,11
+         if(iz.gt.6)then
+          z=.2d0*(iz-6)
+          b=sqrt(-log(z)*rp)
+         else
+          b=sqrt(-rp*(log(0.2d0)+2.d0*(iz-7)))
+          z=exp(-b*b/rp)
+         endif
+         do iqq=1,8
+          qpomis(iy,iz,iv,iv1,iqq)=qfan0(iz,iqq)
+          if(iqq.eq.1.or.iqq.eq.4)then
+           dpx=exp(qpomis(iy,iz,iv,iv1,iqq))*g3p**2*sigs
+     *     *sy**dels*z/rp*4.d0*.0389d0
+          else
+           dpx=exp(qpomis(iy,iz,iv,iv1,iqq))
+          endif
+         enddo
+        enddo
+        if(nrep.eq.1.and.n.lt.50)goto 6
+       enddo
+      enddo
+      enddo
+
+c-------------------------------------------------
+c integrated Pomeron leg eikonals
+      if(debug.ge.1)write (moniou,212)
+      do icz=1,3
+      do icdp=1,2
+       if(cd(icdp,icz).ne.0.d0)then
+        do iy=1,51
+         sy=sgap*(spmax/sgap)**((iy-1)/50.d0)
+         rp=(rq(icdp,icz)+alfp*log(sy))*4.d0*.0389d0
+        do iz=1,11
+         if(iz.gt.6)then
+          z=.2d0*(iz-6)
+          b=sqrt(-log(z)*rp)
+         else
+          b=sqrt(-rp*(log(0.2d0)+2.d0*(iz-7)))
+          z=exp(-b*b/rp)
+         endif
+
+         qxl=qgleg(sy,b*b,icdp,icz)
+         qlegi(iy,iz,icdp,icz,1)=log(qxl/z)
+        enddo
+        enddo
+       endif
+      enddo
+      enddo
+
+c-------------------------------------------------
+c loop-legs
+      do icz=1,3
+      do icdp=1,2
+       if(cd(icdp,icz).ne.0.d0)then
+        do iy=1,51
+         sy=sgap*(spmax/sgap)**((iy-1)/50.d0)
+         rp=(rq(icdp,icz)+alfp*log(sy))*4.d0*.0389d0
+        do iz=1,11
+         if(iz.gt.6)then
+          z=.2d0*(iz-6)
+          b=sqrt(-log(z)*rp)
+         else
+          b=sqrt(-rp*(log(0.2d0)+2.d0*(iz-7)))
+          z=exp(-b*b/rp)
+         endif
+         if(iy.eq.1)then
+          do iqq=2,7
+           qlegi(iy,iz,icdp,icz,iqq)=qlegi(iy,iz,icdp,icz,1)
+          enddo
+         else
+          call qglool(sy,b*b,icdp,icz,fann)
+          do iqq=2,7
+           if(fann(iqq-1).gt.0.d0)then
+            qlegi(iy,iz,icdp,icz,iqq)=log(fann(iqq-1)/z)
+           else
+            qlegi(iy,iz,icdp,icz,iqq)=2.d0*qlegi(iy-1,iz,icdp,icz,iqq)
+     *      -qlegi(iy-2,iz,icdp,icz,iqq)
+           endif
+           qlegi(iy,iz,icdp,icz,iqq)=max(qlegi(iy,iz,icdp,icz,iqq)
+     *     ,-20.d0)
+           if(.not.(qlegi(iy,iz,icdp,icz,iqq).le.0.d0
+     *     .or.qlegi(iy,iz,icdp,icz,iqq).gt.0.d0))stop'leg-nan'
+          enddo
+         endif
+        enddo
+        enddo
+       endif
+      enddo
+      enddo
+
+c-------------------------------------------------
+c uncut fan-contributions
+      if(debug.ge.1)write (moniou,213)
+      do icz=1,3
+      do iv=1,11
+       vvx=dble(iv-1)/10.d0
+      do icdp=1,2
+       if(cd(icdp,icz).ne.0.d0)then
+        do iy=1,51
+        do iz=1,11
+        do iqq=1,2
+         qfanu(iy,iz,iv,icdp+2*(icz-1),iqq)=qlegi(iy,iz,icdp,icz,iqq+1)
+        enddo
+        enddo
+        enddo
+
+        do iy=2,51
+         sy=sgap*(spmax/sgap)**((iy-1)/50.d0)
+         rp=(rq(icdp,icz)+alfp*dlog(sy))*4.d0*.0389d0
+         do iz=1,11
+         do iqq=1,2
+          qfanu(iy,iz,iv,icdp+2*(icz-1),iqq)
+     *    =qfanu(iy-1,iz,iv,icdp+2*(icz-1),iqq)
+         enddo
+         enddo
+
+         n=1
+7        n=n+1
+         nrep=0
+         do iz=1,11
+          if(iz.gt.6)then
+           z=.2d0*dble(iz-6)
+           b=dsqrt(-dlog(z)*rp)
+          else
+           b=dsqrt(-rp*(dlog(0.2d0)+2.d0*dble(iz-7)))
+           z=dexp(-b*b/rp)
+          endif
+          call qgfan(sy,b*b,vvx,icdp,icz,fann)
+          do iqq=1,2
+           if(fann(iqq).gt.0.d0)then
+            qfan0(iz,iqq)=dlog(fann(iqq)/z)
+           else
+            qfan0(iz,iqq)=min(qfanu(iy-1,iz,iv,icdp+2*(icz-1),iqq)
+     *      ,2.d0*qfanu(iy-1,iz,iv,icdp+2*(icz-1),iqq)
+     *      -qfanu(iy-2,iz,iv,icdp+2*(icz-1),iqq))
+           endif
+           qfan0(iz,iqq)=max(qfan0(iz,iqq),-20.d0)
+           if(abs(qfan0(iz,iqq)-qfanu(iy,iz,iv,icdp+2*(icz-1),iqq))
+     *     .gt.1.d-3)nrep=1
+          enddo
+         enddo
+
+         do iz=1,11
+         do iqq=1,2
+          qfanu(iy,iz,iv,icdp+2*(icz-1),iqq)=qfan0(iz,iqq)
+         enddo
+         enddo
+         if(nrep.eq.1)goto 7
+
+         do iz=1,11
+         do iqq=1,2
+          if(iz.gt.6)then
+           z=.2d0*dble(iz-6)
+           b=dsqrt(-dlog(z)*rp)
+          else
+           b=dsqrt(-rp*(dlog(0.2d0)+2.d0*dble(iz-7)))
+           z=dexp(-b*b/rp)
+          endif
+          if(.not.(qfanu(iy,iz,iv,icdp+2*(icz-1),iqq).le.0.d0
+     *    .or.qfanu(iy,iz,iv,icdp+2*(icz-1),iqq).gt.0.d0))stop'fan-nn'
+         enddo
+         enddo
+        enddo
+       endif
+      enddo
+      enddo
+      enddo
+
+c-------------------------------------------------
+c cut fan contributions
+      if(debug.ge.1)write (moniou,215)
+      do icz=1,3                                !hadron class
+      do icdp=1,2                                 !diffractive eigenstate
+       if(cd(icdp,icz).ne.0.d0)then
+c vvx,vvxp,vvxpl - screening corrections from targ. and nuclear proj. fans
+        do iv=1,11
+         vvx=dble(iv-1)/10.d0
+        do iv1=1,1+5*(icz-1)*(3-icz)
+         vvxp=dble(iv1-1)/5.d0
+        do iv2=1,1+5*(icz-1)*(3-icz)
+         vvxpl=vvx*dble(iv2-1)/5.d0
+        do iy=1,51                                !initialization
+        do iz=1,11
+         do iqq=1,9
+          qfanc(iy,iz,iv,icz+(icz-1)*(3-icz)*(iv1+1+6*(iv2-1)),icdp
+     *    +2*(iqq-1))=qfanu(iy,iz,iv,icdp+2*(icz-1),1)
+         enddo
+        enddo
+        enddo
+
+        do iy=2,51
+         sy=sgap*(spmax/sgap)**((iy-1)/50.d0)
+         rp=(rq(icdp,icz)+alfp*dlog(sy))*4.d0*.0389d0
+         do iz=1,11
+         do iqq=1,9
+          qfanc(iy,iz,iv,icz+(icz-1)*(3-icz)*(iv1+1+6*(iv2-1)),icdp
+     *    +2*(iqq-1))=qfanc(iy-1,iz,iv,icz+(icz-1)*(3-icz)*(iv1+1
+     *    +6*(iv2-1)),icdp+2*(iqq-1))
+         enddo
+         enddo
+
+         n=1
+8        n=n+1                          !number of t-channel iterations
+         nrep=0
+         do iz=1,11
+          if(iz.gt.6)then
+           z=.2d0*dble(iz-6)
+           b=dsqrt(-dlog(z)*rp)
+          else
+           b=dsqrt(-rp*(dlog(0.2d0)+2.d0*dble(iz-7)))
+           z=dexp(-b*b/rp)
+          endif
+          call qgfanc(sy,b*b,vvx,vvxp,vvxpl,icdp,icz,fann)
+          fann(7)=min(fann(7),fann(8))
+          do iqq=1,9
+           if(fann(iqq).gt.0.d0)then
+            qfan0(iz,iqq)=dlog(fann(iqq)/z)
+           else
+            qfan0(iz,iqq)=min(2.d0*qfanc(iy-1,iz,iv,icz
+     *      +(icz-1)*(3-icz)*(iv1+1+6*(iv2-1)),icdp+2*(iqq-1))
+     *      -qfanc(iy-2,iz,iv,icz+(icz-1)*(3-icz)*(iv1+1+6*(iv2-1))
+     *      ,icdp+2*(iqq-1)),qfanc(iy-1,iz,iv,icz
+     *      +(icz-1)*(3-icz)*(iv1+1+6*(iv2-1)),icdp+2*(iqq-1)))
+           endif
+           qfan0(iz,iqq)=max(qfan0(iz,iqq),-20.d0)
+          enddo
+         enddo
+
+         do iz=1,11
+         do iqq=1,9
+          if(abs(qfan0(iz,iqq)-qfanc(iy,iz,iv,icz+(icz-1)*(3-icz)
+     *    *(iv1+1+6*(iv2-1)),icdp+2*(iqq-1))).gt.1.d-3)nrep=1
+          qfanc(iy,iz,iv,icz+(icz-1)*(3-icz)*(iv1+1+6*(iv2-1))
+     *    ,icdp+2*(iqq-1))=qfan0(iz,iqq)
+         enddo
+         enddo
+         if(nrep.eq.1.and.n.lt.50)goto 8
+
+         do iz=1,11
+          if(iz.gt.6)then
+           z=.2d0*dble(iz-6)
+           b=dsqrt(-dlog(z)*rp)
+          else
+           b=dsqrt(-rp*(dlog(0.2d0)+2.d0*dble(iz-7)))
+           z=dexp(-b*b/rp)
+          endif
+         do iqq=1,9
+          if(.not.(qfanc(iy,iz,iv,icz+(icz-1)*(3-icz)*(iv1+1+6*(iv2-1))
+     *    ,icdp+2*(iqq-1)).le.0.d0.or.qfanc(iy,iz,iv,icz+(icz-1)
+     *    *(3-icz)*(iv1+1+6*(iv2-1)),icdp+2*(iqq-1)).gt.0.d0))
+     *    stop'fanc-nan'
+         enddo
+         enddo
+        enddo
+        enddo
+        enddo
+        enddo
+       endif
+      enddo
+      enddo
+
+c-------------------------------------------------
+c zigzag fans
+      do icz=1,3                                  !hadron class
+      do icdp=1,2                                 !diffractive eigenstate
+       if(cd(icdp,icz).ne.0.d0)then
+        do iy=1,11
+         sy=sgap**2*(spmax/sgap**2)**((iy-1)/10.d0)
+         rp=(rq(icdp,icz)+alfp*dlog(sy))*4.d0*.0389d0
+        do iz=1,11
+         if(iz.gt.6)then
+          z=.2d0*dble(iz-6)
+          bb=-dlog(z)*rp
+         else
+          bb=-rp*(dlog(0.2d0)+2.d0*dble(iz-7))
+          z=dexp(-bb/rp)
+         endif
+        do iv=1,11
+         vvxt0=dble(iv-1)/10.d0
+        do iv1=1,6
+         vvxt=vvxt0+(1.d0-vvxt0)*dble(iv1-1)/5.d0
+        do iv2=1,1+5*(icz-1)*(3-icz)
+         vvxpt=dble(iv2-1)/5.d0
+        do iv3=1,1+5*(icz-1)*(3-icz)
+         vvxp0=vvxpt*dble(iv3-1)/5.d0
+        do iv4=1,1+5*(icz-1)*(3-icz)
+         vvxpl=dble(iv4-1)/5.d0
+
+         dfan=qgrev(sy,bb,vvxt0,vvxt,vvxpt,vvxp0,vvxpl,icdp,icz)
+         if(dfan.gt.0.d0)then
+          qrev(iy,iz,iv+11*(iv1-1),icz+(icz-1)*(3-icz)
+     *    *(iv2+1+6*(iv3-1)+36*(iv4-1)),icdp)=dlog(dfan/z)
+         else
+          qrev(iy,iz,iv+11*(iv1-1),icz+(icz-1)*(3-icz)*(iv2+1
+     *    +6*(iv3-1)+36*(iv4-1)),icdp)=2.d0*qrev(iy-1,iz,iv+11*(iv1-1)
+     *    ,icz+(icz-1)*(3-icz)*(iv2+1+6*(iv3-1)+36*(iv4-1)),icdp)
+     *    -qrev(iy-2,iz,iv+11*(iv1-1),icz+(icz-1)*(3-icz)*(iv2+1
+     *    +6*(iv3-1)+36*(iv4-1)),icdp)
+         endif
+         qrev(iy,iz,iv+11*(iv1-1),icz+(icz-1)*(3-icz)*(iv2+1+6*(iv3-1)
+     *   +36*(iv4-1)),icdp)=max(qrev(iy,iz,iv+11*(iv1-1),icz
+     *   +(icz-1)*(3-icz)*(iv2+1+6*(iv3-1)+36*(iv4-1)),icdp),-20.d0)
+
+         if(.not.(qrev(iy,iz,iv+11*(iv1-1),icz+(icz-1)*(3-icz)
+     *   *(iv2+1+6*(iv3-1)+36*(iv4-1)),icdp).le.0.d0.or.qrev(iy,iz
+     *   ,iv+11*(iv1-1),icz+(icz-1)*(3-icz)*(iv2+1+6*(iv3-1)
+     *   +36*(iv4-1)),icdp).gt.0.d0))stop'fanc-nan'
+        enddo
+        enddo
+        enddo
+        enddo
+        enddo
+        enddo
+        enddo
+       endif
+      enddo
+      enddo
+
+c-------------------------------------------------
+c diffractive fans
+      icz=2
+      do icdp=1,2
+       if(cd(icdp,icz).ne.0.d0)then
+        do iy=1,21
+         xpomr=(1.d5/sgap**2)**(-dble(iy-1)/20.d0)/sgap**2
+          rp=(rq(icdp,icz)-alfp*dlog(xpomr))*2.d0*.0389d0
+         if(iy.gt.1)then
+          do iy1=1,11
+          do iz=1,11
+          do iqq=1,3
+           qdfan(iy,iy1,iz,icdp,iqq)=qdfan(iy-1,iy1,iz,icdp,iqq)
+          enddo
+          enddo
+          enddo
+         endif
+
+         n=0
+9        n=n+1
+         nrep=0
+         do iy1=1,11
+          xpomr1=(xpomr*sgap**2)**(dble(11-iy1)/10.d0)/sgap
+         do iz=1,11
+          if(iz.gt.6)then
+           z=.2d0*dble(iz-6)
+           b=dsqrt(-dlog(z)*rp)
+          else
+           b=dsqrt(-rp*(dlog(0.2d0)+2.d0*dble(iz-7)))
+           z=dexp(-b*b/rp)
+          endif
+          call qgdfan(xpomr,xpomr1,b*b,icdp,fann,n)
+          do iqq=1,3
+           if(fann(iqq).gt.0.d0)then
+            qfan2(iy1,iz,iqq)=dlog(fann(iqq)/z)
+           else
+            qfan2(iy1,iz,iqq)=qfan2(iy1-1,iz,iqq)
+           endif
+           if(n.gt.1.and.abs(qfan2(iy1,iz,iqq)
+     *     -qdfan(iy,iy1,iz,icdp,iqq)).gt.1.d-3)nrep=1
+          enddo
+         enddo
+         enddo
+
+         do iy1=1,11
+         do iz=1,11
+         do iqq=1,3
+          qdfan(iy,iy1,iz,icdp,iqq)=qfan2(iy1,iz,iqq)
+         enddo
+         enddo
+         enddo
+         if((n.eq.1.or.nrep.eq.1).and.iy.gt.1)goto 9
+
+         do iy1=1,11
+          xpomr1=(xpomr*sgap**2)**(dble(11-iy1)/10.d0)/sgap
+          do iz=1,11
+           if(iz.gt.6)then
+            z=.2d0*dble(iz-6)
+            b=dsqrt(-dlog(z)*rp)
+           else
+            b=dsqrt(-rp*(dlog(0.2d0)+2.d0*dble(iz-7)))
+            z=dexp(-b*b/rp)
+           endif
+           do iqq=1,3
+            if(iqq.ne.3)then
+             dpx=dexp(qdfan(iy,iy1,iz,icdp,iqq))*z
+            else
+             dpx=dexp(qdfan(iy,iy1,iz,icdp,iqq))*z
+     *       *dlog(xpomr1/xpomr/sgap)
+            endif
+            if(.not.(qdfan(iy,iy1,iz,icdp,iqq).le.0.d0
+     *      .or.qdfan(iy,iy1,iz,icdp,iqq).gt.0.d0))stop'qdfan-nan'
+           enddo
+          enddo
+         enddo
+        enddo
+       endif
+      enddo
+
+c-------------------------------------------------
+c integrated Pomeron eikonals
+      do icz=1,3
+      do icdp=1,2
+      do icdt=1,2
+       if(cd(icdp,icz).ne.0.d0.and.cd(icdt,2).ne.0.d0)then
+        do iy=1,11
+         e0n=10.d0**iy
+         sy=2.d0*e0n*am(2)+am(2)**2+am(icz)**2
+         rp=(rq(icdp,icz)+rq(icdt,2)+alfp*log(sy))*4.d0*.0389d0
+        do iz=1,11
+         if(iz.gt.6)then
+          z=.2d0*(iz-6)
+          b=sqrt(-log(z)*rp)
+         else
+          b=sqrt(-rp*(log(0.2d0)+2.d0*(iz-7)))
+          z=exp(-b*b/rp)
+         endif
+
+         vsoft=gsoft(icz)*sy**dels/rp*cd(icdp,icz)*cd(icdt,2)
+         vgg=qgfsh(sy,b*b,icdp,icdt,icz,0)
+         vqg=qgfsh(sy,b*b,icdp,icdt,icz,1)
+         vgq=qgfsh(sy,b*b,icdp,icdt,icz,2)
+         vqq=qghard(sy,b*b,icdp,icdt,icz)
+
+         qxp=vsoft*z+vgg+vqg+vgq+vqq
+         do iv=1,6
+          vvx=(iv-1)/5.d0
+         do iv1=1,1+5*(icz-1)*(3-icz)
+          vvxp=(iv1-1)/5.d0
+         do iv2=1,6
+          vvxt=(iv2-1)/5.d0
+
+          v3p=qg3pom(sy,b,vvx,vvxp,vvxt,icdp,icdt,icz)
+          v1p=qgpcut(sy,b,vvx,vvxp,vvxt,icdp,icdt,icz)
+          qxp1=qxp+v3p
+          qxpc=qxp+v1p
+          if(qxp1.gt.0.d0)then
+           qpomr(iy,iz,iv+6*(iv1-1)+36*(iv2-1)
+     *     ,icdp+2*(icdt-1)+4*(icz-1),1)=log(qxp1/z)
+          else
+           qpomr(iy,iz,iv+6*(iv1-1)+36*(iv2-1),icdp+2*(icdt-1)
+     *     +4*(icz-1),1)=min(2.d0*qpomr(iy-1,iz,iv+6*(iv1-1)
+     *     +36*(iv2-1),icdp+2*(icdt-1)+4*(icz-1),1)-qpomr(iy-2,iz
+     *     ,iv+6*(iv1-1)+36*(iv2-1),icdp+2*(icdt-1)+4*(icz-1),1)
+     *     ,qpomr(iy-1,iz,iv+6*(iv1-1)+36*(iv2-1)
+     *     ,icdp+2*(icdt-1)+4*(icz-1),1))
+          endif
+          if(qxpc.gt.0.d0)then
+           qpomr(iy,iz,iv+6*(iv1-1)+36*(iv2-1)
+     *     ,icdp+2*(icdt-1)+4*(icz-1),2)=log(qxpc/z)
+          else
+           qpomr(iy,iz,iv+6*(iv1-1)+36*(iv2-1),icdp+2*(icdt-1)
+     *     +4*(icz-1),2)=min(2.d0*qpomr(iy-1,iz,iv+6*(iv1-1)
+     *     +36*(iv2-1),icdp+2*(icdt-1)+4*(icz-1),2)-qpomr(iy-2,iz
+     *     ,iv+6*(iv1-1)+36*(iv2-1),icdp+2*(icdt-1)+4*(icz-1),2)
+     *     ,qpomr(iy-1,iz,iv+6*(iv1-1)+36*(iv2-1)
+     *     ,icdp+2*(icdt-1)+4*(icz-1),2))
+          endif
+
+          do iqq=1,2
+           qpomr(iy,iz,iv+6*(iv1-1)+36*(iv2-1),icdp+2*(icdt-1)
+     *     +4*(icz-1),iqq)=max(qpomr(iy,iz,iv+6*(iv1-1)+36*(iv2-1)
+     *     ,icdp+2*(icdt-1)+4*(icz-1),iqq),-20.d0)
+
+           if(.not.(qpomr(iy,iz,iv+6*(iv1-1)+36*(iv2-1),icdp+2*(icdt-1)
+     *     +4*(icz-1),iqq).le.0.d0.or.qpomr(iy,iz,iv+6*(iv1-1)
+     *     +36*(iv2-1),icdp+2*(icdt-1)+4*(icz-1),iqq).gt.0.d0))
+     *     stop'qpomr-nan'
+          enddo
+         enddo
+         enddo
+         enddo
+        enddo
+        enddo
+       endif
+      enddo
+      enddo
+      enddo
+
+c-------------------------------------------------
+c interaction cross sections
+      ia(1)=1
+      do iy=1,10
+       e0n=10.d0**iy                               !interaction energy
+       scm=2.d0*e0n*am(2)+am(2)**2+am(icz)**2
+
+       do iiz=1,3
+        icz=iiz                                    !hadron class
+        rp=(rq(1,icz)+rq(1,2)+alfp*log(scm))*4.d0*.0389d0 !slope (in fm^2)
+        g0=pi*rp*10.d0                  !factor for cross-sections (in mb)
+
+        do iia=1,6
+         if(iia.le.4)then
+          ia(2)=4**(iia-1)                         !target mass number
+         elseif(iia.eq.5)then
+          ia(2)=14
+         else
+          ia(2)=40
+         endif
+         if(debug.ge.1)write (moniou,206)e0n,ty(icz),ia(2)
+c-------------------------------------------------
+c nuclear densities
+         if(ia(2).lt.10)then                       !light nuclei - gaussian
+          rnuc(2)=.9d0*float(ia(2))**.3333         !nuclear radius
+          wsnuc(2)=amws                            !not used
+          wbnuc(2)=0.d0                            !not used
+         elseif(ia(2).le.56)then                   !3-parameter Fermi
+          rnuc(2)=trnuc(ia(2))                     !nuclear radius
+          wsnuc(2)=twsnuc(ia(2))                   !diffuseness
+          wbnuc(2)=twbnuc(ia(2))                   !'wine-bottle' parameter
+         else                                      !2-parameter Fermi
+c rnuc - wood-saxon density radius (fit to the data of murthy et al.)
+          rnuc(2)=1.19d0*dble(ia(2))**(1.d0/3.d0)
+     *    -1.38d0*dble(ia(2))**(-1.d0/3.d0)        !nuclear radius
+          wsnuc(2)=amws                            !diffuseness
+          wbnuc(2)=0.d0                            !not used
+         endif
+
+         if(ia(2).eq.1)then               !hadron-proton interaction
+          call qgfz(0.d0,gz0,0,0)
+          gtot=gz0(1)                     !total cross-section
+          gin=(gz0(2)+gz0(3)+gz0(4))*.5d0 !inelastic cross section
+          bel=gz0(5)                      !elastic scattering slope
+          gel=gtot-gin                    !elastic cross section
+          gdp=gz0(3)*.5d0     !projectile low mass diffr. (+double LMD)
+          gdt=gz0(4)*.5d0                 !target low mass diffraction
+          if(iy.le.10)gsect(iy,icz,iia)=log(gin)
+
+          if(debug.ge.1)write (moniou,225)gtot,gin,gel,gdp,gdt,bel
+         else                             !hadron-nucleus interaction
+          bm=rnuc(2)+dlog(29.d0)*wsnuc(2) !for numerical integration
+          anorm=qganrm(rnuc(2),wsnuc(2),wbnuc(2))*rp !density normalization
+          call qggau(gz1)                 !integration over b<bm
+          call qggau1(gz1)                !integration over b>bm
+          gin=gz1(1)+gz1(2)+gz1(3)        !inelastic cross section
+          if(iy.le.10)gsect(iy,icz,iia)=log(gin*10.d0)
+
+          if(debug.ge.1)write (moniou,224)
+     *    gin*10.d0,gz1(3)*10.d0,gz1(2)*10.d0
+         endif
+         if(.not.(gsect(iy,icz,iia).le.0.d0
+     *   .or.gsect(iy,icz,iia).gt.0.d0))stop'qpomr-nan'
+        enddo
+       enddo
+      enddo
+
+c-------------------------------------------------
+c cut Pomeron leg eikonals
+      if(debug.ge.1)write (moniou,223)
+      do icz=1,3                                    !hadron class
+      do icdp=1,2
+       if(cd(icdp,icz).ne.0.d0)then
+        do iy=1,51
+         sy=sgap**2*(spmax/sgap**2)**((iy-1)/50.d0)
+         rp=(rq(icdp,icz)+alfp*log(sy))*4.d0*.0389d0
+        do iz=1,11
+         if(iz.gt.6)then
+          z=.2d0*(iz-6)
+          bb=-log(z)*rp                             !impact parameter^2
+         else
+          bb=-rp*(log(0.2d0)+2.d0*(iz-7))
+          z=exp(-bb/rp)
+         endif
+        do ix=1,10
+         if(ix.le.5)then
+          xp=.2d0*(5.d0*sgap/sy)**((6-ix)/5.d0)     !Pomeron LC+ momentum
+         else
+          xp=.2d0*(ix-5)
+         endif
+         sys=xp*sy
+
+         vs=qgls(sys,xp,bb,icdp,icz)
+         vg=qglsh(sys,xp,bb,icdp,icz,0)
+         if(xp.lt..99d0)then
+          vq=qglsh(sys,xp,bb,icdp,icz,1)
+     *    /dsqrt(xp)*(1.d0-xp)**(ahv(icz)-ahl(icz))
+         else
+          vq=0.d0
+         endif
+         qlegc0(iy,ix,iz,icdp+2*(icz-1),1)=dlog((vs+vg+vq)/vs)
+         qlegc0(iy,ix,iz,icdp+2*(icz-1),2)=dlog((vs+vg)/vs)
+        enddo
+        enddo
+        enddo
+       endif
+      enddo
+      enddo
+
+      do icz=1,3                                    !hadron class
+      do icdp=1,2
+       if(cd(icdp,icz).ne.0.d0)then
+        do iy=1,51
+         sy=sgap**2*(spmax/sgap**2)**((iy-1)/50.d0)
+         rp=(rq(icdp,icz)+alfp*log(sy))*4.d0*.0389d0
+        do iz=1,11
+         if(iz.gt.6)then
+          z=.2d0*(iz-6)
+          bb=-log(z)*rp                             !impact parameter^2
+         else
+          bb=-rp*(log(0.2d0)+2.d0*(iz-7))
+          z=exp(-bb/rp)
+         endif
+        do ix=1,10
+         if(ix.le.5)then
+          xp=.2d0*(5.d0*sgap/sy)**((6-ix)/5.d0)     !Pomeron LC+ momentum
+         else
+          xp=.2d0*(ix-5)
+         endif
+         sys=xp*sy
+
+         do iqq=1,3
+          call qgloolc(sys,xp,bb,icdp,icz,iqq,fann(2*iqq-1)
+     *    ,fann(2*iqq))
+         enddo
+         do iqq=1,6
+          if(fann(iqq).gt.0.d0)then
+           qlegc0(iy,ix,iz,icdp+2*(icz-1),iqq+2)
+     *     =dlog(fann(iqq)/qgls(sys,xp,bb,icdp,icz))
+          else
+           qlegc0(iy,ix,iz,icdp+2*(icz-1),iqq+2)
+     *     =min(2.d0*qlegc0(iy-1,ix,iz,icdp+2*(icz-1),iqq+2)
+     *     -qlegc0(iy-2,ix,iz,icdp+2*(icz-1),iqq+2)
+     *     ,qlegc0(iy-1,ix,iz,icdp+2*(icz-1),iqq+2))
+          endif
+          qlegc0(iy,ix,iz,icdp+2*(icz-1),iqq+2)
+     *     =max(qlegc0(iy,ix,iz,icdp+2*(icz-1),iqq+2),-20.d0)
+         enddo
+        enddo
+        enddo
+        enddo
+       endif
+      enddo
+      enddo
+
+      do icz=1,3                                    !hadron class
+      do icdp=1,2                                   !diffr. eigenstate
+       if(cd(icdp,icz).ne.0.d0)then
+        do iv=1,11
+         vvx=dble(iv-1)/10.d0
+         do iy=1,51                                 !initialization
+         do ix=1,10
+         do iz=1,11
+          do iqq=1,3
+           qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))
+     *     =qlegc0(iy,ix,iz,icdp+2*(icz-1),2*iqq+1)
+          enddo
+         enddo
+         enddo
+         enddo
+
+         do iy=2,51
+          sy=sgap**2*(spmax/sgap**2)**((iy-1)/50.d0)
+          rp=(rq(icdp,icz)+alfp*log(sy))*4.d0*.0389d0
+
+          do ix=1,10
+          do iz=1,11
+          do iqq=1,3
+           qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))
+     *     =qlegc(iy-1,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))
+          enddo
+          enddo
+          enddo
+
+          n=1
+43        n=n+1                          !number of t-channel iterations
+          nrep=0
+          do iz=1,11
+           if(iz.gt.6)then
+            z=.2d0*(iz-6)
+            bb=-log(z)*rp                           !impact parameter^2
+           else
+            bb=-rp*(log(0.2d0)+2.d0*(iz-7))
+            z=exp(-bb/rp)
+           endif
+          do ix=1,10
+           if(ix.le.5)then
+            xp=.2d0*(5.d0*sgap/sy)**((6-ix)/5.d0)   !Pomeron LC+ momentum
+           else
+            xp=.2d0*(ix-5)
+           endif
+           sys=xp*sy
+
+           do iqq=1,3
+            fann(iqq)=qglscr(sys,xp,bb,vvx,icdp,icz,iqq)
+            if(fann(iqq).gt.0.d0)then
+             qfan2(ix,iz,iqq)=dlog(fann(iqq)/qgls(sys,xp,bb,icdp,icz))
+            elseif(iy.gt.2)then
+             qfan2(ix,iz,iqq)
+     *       =min(2.d0*qlegc(iy-1,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))
+     *       -qlegc(iy-2,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))
+     *       ,qlegc(iy-1,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1)))
+            else
+             qfan2(ix,iz,iqq)
+     *       =qlegc(iy-1,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))
+            endif
+            qfan2(ix,iz,iqq)=max(qfan2(ix,iz,iqq),-20.d0)
+            if(abs(qfan2(ix,iz,iqq)-qlegc(iy,ix,iv,iz
+     *      ,icdp+2*(icz-1)+6*(iqq-1))).gt.1.d-3)nrep=1
+           enddo
+          enddo
+          enddo
+
+          do iz=1,11
+           if(iz.gt.6)then
+            z=.2d0*(iz-6)
+            bb=-log(z)*rp                           !impact parameter
+           else
+            bb=-rp*(log(0.2d0)+2.d0*(iz-7))
+            z=exp(-bb/rp)
+           endif
+          do ix=1,10
+           if(ix.le.5)then
+            xp=.2d0*(5.d0*sgap/sy)**((6-ix)/5.d0)   !Pomeron LC+ momentum
+           else
+            xp=.2d0*(ix-5)
+           endif
+           sys=xp*sy
+
+          do iqq=1,3
+           qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))=qfan2(ix,iz,iqq)
+
+           if(.not.(qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1)).le.0.d0
+     *     .or.qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1)).gt.0.d0))
+     *     stop'qlegc-nan'
+          enddo
+          enddo
+          enddo
+          if(nrep.eq.1.and.n.lt.50)goto 43
+         enddo
+        enddo
+       endif
+      enddo
+      enddo
+
+c soft pre-evolution
+      do icz=1,3                                    !hadron class
+      do icdp=1,2                                   !diffr. eigenstate
+       if(cd(icdp,icz).ne.0.d0)then
+        do iv=1,11
+         vvx=dble(iv-1)/10.d0
+        do iy=1,51
+         sy=sgap*(spmax/sgap)**((iy-1)/50.d0)
+         rp=(rq(icdp,icz)+alfp*log(sy))*4.d0*.0389d0
+        do iz=1,11
+         if(iz.gt.6)then
+          z=.2d0*(iz-6)
+          bb=-log(z)*rp                             !impact parameter
+         else
+          bb=-rp*(log(0.2d0)+2.d0*(iz-7))
+          z=exp(-bb/rp)
+         endif
+        do ix=1,10
+         if(ix.le.5)then
+          xp=.2d0*(sgap/sy)**((6-ix)/5.d0)          !Pomeron LC+ momentum
+         else
+          xp=.2d0*(ix-5)
+         endif
+         sys=xp*sy
+
+         if(iy.eq.1)then
+          qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+18)=0.d0
+          qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+24)=0.d0
+         else
+          do iqq=4,5
+           fann(iqq)=qglh(sys,xp,bb,vvx,icdp,icz,iqq-4)
+           if(fann(iqq).gt.0.d0)then
+            qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))
+     *      =dlog(fann(iqq))
+           elseif(iy.gt.2)then
+            qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))
+     *      =min(2.d0*qlegc(iy-1,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))
+     *      -qlegc(iy-2,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))
+     *      ,qlegc(iy-1,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1)))
+           else
+            qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))
+     *      =qlegc(iy-1,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))
+           endif
+           qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1))
+     *     =max(qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1)),-20.d0)
+
+           if(.not.(qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1)).le.0.d0
+     *     .or.qlegc(iy,ix,iv,iz,icdp+2*(icz-1)+6*(iqq-1)).gt.0.d0))
+     *     stop'qlegc-nan'
+          enddo
+         endif
+        enddo
+        enddo
+        enddo
+        enddo
+       endif
+      enddo
+      enddo
+
+c-------------------------------------------------
+c cut Pomeron eikonals
+      if(debug.ge.1)write (moniou,226)
+      do icz=1,3                                    !proj. class
+      do icdp=1,2
+      do icdt=1,2
+       if(cd(icdp,icz).ne.0.d0.and.cd(icdt,2).ne.0.d0)then
+        do iy=1,11
+         sy=sgap**2*(spmax/sgap**2)**((iy-1)/10.d0)
+         rp=(rq(icdp,icz)+rq(icdt,2)+alfp*log(sy))*4.d0*.0389d0
+        do iz=1,11
+         if(iz.gt.6)then
+          z=.2d0*(iz-6)
+          bb=-log(z)*rp                             !impact parameter^2
+         else
+          bb=-rp*(log(0.2d0)+2.d0*(iz-7))
+          z=exp(-bb/rp)
+         endif
+        do iv=1,11
+         vvx=(iv-1)/10.d0                           !relative scr. strenth
+
+        do ix1=1,10
+         if(ix1.le.5)then
+          xp=.2d0*(5.d0*sgap/sy)**((6-ix1)/5.d0)    !Pomeron LC+ momentum
+         else
+          xp=.2d0*(ix1-5)
+         endif
+        do ix2=1,10
+         if(ix2.le.5)then
+          xm=.2d0*(sgap/sy/xp)**((6-ix2)/5.d0)      !Pomeron LC- momentum
+         else
+          xm=.2d0*(ix2-5)
+         endif
+         sys=xp*xm*sy
+
+         do iqq=1,4
+          vv=qgcutp(sys,xp,xm,bb,vvx,icdp,icdt,icz,iqq)
+          if(vv.gt.0.d0)then
+           qpomc(iy,ix1+10*(ix2-1),iz,iv,icdp+2*(icdt-1)+4*(icz-1)
+     *     +12*(iqq-1))=dlog(vv/z)
+          elseif(iy.gt.2)then
+           qpomc(iy,ix1+10*(ix2-1),iz,iv,icdp+2*(icdt-1)+4*(icz-1)
+     *     +12*(iqq-1))=min(2.d0*qpomc(iy-1,ix1+10*(ix2-1),iz,iv
+     *     ,icdp+2*(icdt-1)+4*(icz-1)+12*(iqq-1))-qpomc(iy-2
+     *     ,ix1+10*(ix2-1),iz,iv,icdp+2*(icdt-1)+4*(icz-1)+12*(iqq-1))
+     *     ,qpomc(iy-1,ix1+10*(ix2-1),iz,iv,icdp+2*(icdt-1)+4*(icz-1)
+     *     +12*(iqq-1)))
+          else
+           qpomc(iy,ix1+10*(ix2-1),iz,iv,icdp+2*(icdt-1)+4*(icz-1)
+     *     +12*(iqq-1))=qpomc(iy-1,ix1+10*(ix2-1),iz,iv,icdp+2*(icdt-1)
+     *     +4*(icz-1)+12*(iqq-1))
+          endif
+           qpomc(iy,ix1+10*(ix2-1),iz,iv,icdp+2*(icdt-1)+4*(icz-1)
+     *     +12*(iqq-1))=max(qpomc(iy,ix1+10*(ix2-1),iz,iv,icdp
+     *     +2*(icdt-1)+4*(icz-1)+12*(iqq-1)),-20.d0)
+
+          if(.not.(qpomc(iy,ix1+10*(ix2-1),iz,iv,icdp+2*(icdt-1)+4*(icz
+     *    -1)+12*(iqq-1)).le.0.d0.or.qpomc(iy,ix1+10*(ix2-1),iz,iv,icdp
+     *    +2*(icdt-1)+4*(icz-1)+12*(iqq-1)).gt.0.d0))stop'qpomc-nan'
+         enddo
+        enddo
+        enddo
+        enddo
+        enddo
+        enddo
+       endif
+      enddo
+      enddo
+      enddo
+
+c-----------------------------------------------------------------------------
+c timelike Sudakov formfactor
+      if(debug.ge.1)write (moniou,221)
+      do m=1,2                     !parton type (1-g, 2-q)
+       fsud(1,m)=0.d0
+      do k=2,10
+       qmax=qtf*4.d0**(1.d0+k)     !effective virtuality (qt**2/z**2/(1-z)**2)
+       fsud(k,m)=qgsudt(qmax,m)
+      enddo
+      enddo
+c-----------------------------------------------------------------------------
+c effective virtuality (used for inversion in timelike branching)
+      if(debug.ge.1)write (moniou,222)
+      do m=1,2                     !parton type (1-g, 2-q)
+      do k=1,10
+       qlmax=1.38629d0*(k-1)
+       qrt(k,1,m)=0.d0
+       qrt(k,101,m)=qlmax
+      do i=1,99                    !bins in Sudakov formfactor
+       if(k.eq.1)then
+        qrt(k,i+1,m)=0.d0
+       else
+        qrt(k,i+1,m)=qgroot(qlmax,.01d0*i,m)
+       endif
+      enddo
+      enddo
+      enddo
+
+c-----------------------------------------------------------------------------
+c writing cross sections to the file
+      if(debug.ge.1)write (moniou,220)
+      if(ifIIdat.ne.1)then
+       open(1,file=DATDIR(1:INDEX(DATDIR,' ')-1)//'qgsdat-II-04'
+     * ,status='unknown')
+      else                                              !used to link with nexus
+       open(ifIIdat,file=fnIIdat(1:nfnIIdat),status='unknown')
+      endif
+      write (1,*)csborn,cs0,cstot,evk,qpomi,qpomis,qlegi,qfanu,qfanc
+     *,qdfan,qpomr,gsect,qlegc0,qlegc,qpomc,fsud,qrt,qrev,fsud,qrt
+      close(1)
+
+10    continue
+c-----------------------------------------------------------------------------
+c nuclear cross sections
+      if(ifIIncs.ne.2)then
+       inquire(file=DATDIR(1:INDEX(DATDIR,' ')-1)//'sectnu-II-04'
+     * ,exist=lcalc)
+      else                                                  !ctp
+       inquire(file=fnIIncs(1:nfnIIncs),exist=lcalc)
+      endif
+
+      if(lcalc)then
+       if(debug.ge.0)write (moniou,207)
+       if(ifIIncs.ne.2)then
+        open(2,file=DATDIR(1:INDEX(DATDIR,' ')-1)//'sectnu-II-04'
+     *  ,status='old')
+       else                                                  !ctp
+        open(ifIIncs,file=fnIIncs(1:nfnIIncs),status='old')
+       endif
+       read (2,*)qgsasect
+       close(2)
+
+      elseif(.not.producetables)then
+        write(moniou,*) "Missing sectnu-II-04 file !"
+        write(moniou,*) "Please correct the defined path ",
+     &"or force production ..."
+        stop
+
+      else
+       niter=5000                   !number of iterations
+       do ie=1,10
+        e0n=10.d0**ie               !interaction energy (per nucleon)
+       do iia1=1,6
+        iap=2**iia1                 !proj. mass number
+       do iia2=1,6
+        if(iia2.le.4)then
+         iat=4**(iia2-1)            !targ. mass number
+        elseif(iia2.eq.5)then
+         iat=14
+        else
+         iat=40
+        endif
+        if(debug.ge.1)write (moniou,208)e0n,iap,iat
+
+        call qgini(e0n,2,iap,iat)
+        call qgcrossc(niter,gtot,gprod,gabs,gdd,gqel,gcoh)
+        if(debug.ge.1)write (moniou,209)gtot,gprod,gabs,gdd,gqel,gcoh
+        qgsasect(ie,iia1,iia2)=log(gprod)
+       enddo
+       enddo
+       enddo
+       if(ifIIncs.ne.2)then
+        open(2,file=DATDIR(1:INDEX(DATDIR,' ')-1)//'sectnu-II-04'
+     *  ,status='unknown')
+       else                                                  !ctp
+        open(ifIIncs,file=fnIIncs(1:nfnIIncs),status='unknown')
+       endif
+       write (2,*)qgsasect
+       close(2)
+      endif
+
+      if(debug.ge.3)write (moniou,218)
+201   format(2x,'qgaini: hard cross sections calculation')
+202   format(2x,'qgaini: number of rungs considered:',i2
+     */4x,'starting energy bin for ordered and general ladders:',3i4)
+205   format(2x,'qgaini: pretabulation of the interaction eikonals')
+206   format(2x,'qgaini: initial particle energy:',e10.3,2x
+     *,'its type:',a7,2x,'target mass number:',i2)
+207   format(2x,'qgaini: nuclear cross sections readout from the file'
+     *,' sectnu-II-04')
+208   format(2x,'qgaini: initial nucleus energy:',e10.3,2x
+     *,'projectile mass:',i2,2x,'target mass:',i2)
+209   format(2x,'gtot',d10.3,'  gprod',d10.3,'  gabs',d10.3
+     */2x,'gdd',d10.3,'  gqel',d10.3,' gcoh',d10.3)
+210   format(2x,'qgaini - main initialization procedure')
+212   format(2x,'qgaini: integrated Pomeron leg eikonals')
+213   format(2x,'qgaini: integrated fan contributions')
+214   format(2x,'qgaini: cross sections readout from the file: ', A,2x)
+c     *,' qgsdat-II-04')
+215   format(2x,'qgaini: integrated cut fan contributions')
+c216   format(2x,'qgaini: integrated cut Pomeron eikonals')
+218   format(2x,'qgaini - end')
+220   format(2x,'qgaini: cross sections are written to the file'
+     *,' qgsdat-II-04')
+221   format(2x,'qgaini: timelike Sudakov formfactor')
+222   format(2x,'qgaini: effective virtuality for inversion')
+223   format(2x,'qgaini: cut Pomeron leg eikonals')
+224   format(2x,'qgaini: hadron-nucleus cross sections:'
+     */4x,'gin=',e10.3,2x,'gdifr_targ=',e10.3,2x
+     *,'gdifr_proj=',e10.3)
+225   format(2x,'qgaini: hadron-proton cross sections:'
+     */4x,'gtot=',e10.3,2x,'gin=',e10.3,2x,'gel=',e10.3/4x
+     *,'gdifrp=',e10.3,2x,'gdifrt=',e10.3,2x,'b_el=',e10.3,2x)
+226   format(2x,'qgaini: cut Pomeron eikonals (semi-hard)')
+      return
+      end
+
+c=============================================================================
+      subroutine qgini(e0n,icp0,iap,iat)
+c-----------------------------------------------------------------------------
+c additional initialization procedure
+c e0n  - interaction energy (per hadron/nucleon),
+c icp0 - hadron type (+-1 - pi+-, +-2 - p(p~), +-3 - n(n~),
+c                     +-4 - K+-, +-5 - K_l/s),
+c iap  - projectile mass number (1 - for a hadron),
+c iat  - target mass number
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr2/  scm,wp0,wm0
+      common /qgarr4/  ey0(3)
+      common /qgarr5/  rnuc(2),wsnuc(2),wbnuc(2),anorm
+     *,cr1(2),cr2(2),cr3(2)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr7/  xa(iapmax,3),xb(iapmax,3),b
+      common /qgarr10/ am(7),ammu
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr43/ moniou
+      common /arr1/    trnuc(56),twsnuc(56),twbnuc(56)
+      common /qgdebug/ debug
+      common /qgsIInex1/xan(iapmax,3),xbn(iapmax,3)  !used to link with nexus
+     *,bqgs,bmaxqgs,bmaxnex,bminnex
+
+      if(debug.ge.1)write (moniou,201)icp0,iap,iat,e0n
+      icp=icp0
+      ia(1)=iap
+      ia(2)=iat
+
+      icz=iabs(icp)/2+1  !!!!!particle class (1 - pion, 2 - nucleon, 3 - kaon)
+
+      scm=2.d0*e0n*am(2)+am(2)**2+am(icz)**2   !c.m. energy squared
+      ey0(1)=dsqrt(scm)/(e0n+am(2)+dsqrt(e0n-am(icz))
+     **dsqrt(e0n+am(icz)))                     !Lorentz boost to lab. frame
+      ey0(2)=1.d0
+      ey0(3)=1.d0
+      wp0=dsqrt(scm)                           !initial LC+ mometum
+      wm0=wp0                                  !initial LC- mometum
+
+c-------------------------------------------------
+c nuclear radii and weights for nuclear configurations - procedure qggea
+      do i=1,2
+       if(ia(i).lt.10.and.ia(i).ne.1)then      !gaussian density
+        rnuc(i)=.9d0*float(ia(i))**.3333       !nuclear radius
+        if(ia(i).eq.2)rnuc(i)=3.16d0
+c rnuc -> rnuc * a / (a-1) - to use van-hove method (in qggea)
+        rnuc(i)=rnuc(i)*dsqrt(2.d0*ia(i)/(ia(i)-1.d0))
+                           !rnuc -> rnuc*a/(a-1) - to use Van-Hove method
+      elseif(ia(i).ne.1)then
+        if(ia(i).le.56)then                    !3-parameter Fermi
+         rnuc(i)=trnuc(ia(i))                  !nuclear radius
+         wsnuc(i)=twsnuc(ia(i))                !diffuseness
+         wbnuc(i)=twbnuc(ia(i))                !'wine-bottle' parameter
+        else
+         rnuc(i)=1.19*float(ia(i))**(1./3.)-1.38*float(ia(i))**(-1./3.)
+         wsnuc(i)=amws                         !diffuseness
+         wbnuc(i)=0.d0
+        endif
+        cr1(i)=1.d0+3.d0/rnuc(i)*wsnuc(i)+6.d0/(rnuc(i)/wsnuc(i))**2
+     *  +6.d0/(rnuc(i)/wsnuc(i))**3
+        cr2(i)=3.d0/rnuc(i)*wsnuc(i)
+        cr3(i)=3.d0/rnuc(i)*wsnuc(i)+6.d0/(rnuc(i)/wsnuc(i))**2
+       endif
+      enddo
+
+      if(ia(1).ne.1.and.ia(2).ne.1)then      !primary nucleus       !so18032013-beg
+       bm=rnuc(1)+rnuc(2)+5.d0*max(wsnuc(1),wsnuc(2))
+     &   +dsqrt(alfp*log(scm))                               !b-cutoff
+      elseif(ia(2).ne.1)then                 !hadron-nucleus
+       bm=rnuc(2)+5.d0*wsnuc(2)+dsqrt(rq(1,2)+alfp*log(scm)) !b-cutoff
+      elseif(ia(1).ne.1)then                 !nucleus-proton
+       bm=rnuc(1)+5.d0*wsnuc(1)+dsqrt(rq(1,2)+alfp*log(scm)) !b-cutoff
+      else                                   !hadron-proton
+       bm=3.d0*dsqrt((rq(1,icz)+rq(1,2)+alfp*log(scm))*4.d0*.0398d0)
+      endif                                                         !so18032013-end
+
+      bmaxqgs=bm                                      !used to link with nexus
+
+      if(debug.ge.3)write (moniou,202)
+201   format(2x,'qgini - miniinitialization: particle type icp0=',
+     *i2,2x,'projectile mass number iap=',i2/4x,
+     *'target mass number iat=',i2,' interaction energy e0n=',e10.3)
+202   format(2x,'qgini - end')
+      return
+      end
+
+c=============================================================================
+      double precision function qgpint(sy,bb)
+c-----------------------------------------------------------------------------
+c qgpint - interm. Pomeron eikonal
+c sy  - pomeron mass squared,
+c bb  - impact parameter squared
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      if(debug.ge.2)write (moniou,201)sy,bb
+
+      qgpint=0.d0
+      s2min=4.d0*fqscal*qt0
+      xmin=s2min/sy
+      if(xmin.ge.1.d0)return
+
+      xmin=xmin**(delh-dels)
+c numerical integration over z1
+      do i=1,7
+      do m=1,2
+       z1=(.5d0*(1.d0+xmin-(2*m-3)*x1(i)*(1.d0-xmin)))
+     * **(1.d0/(delh-dels))
+       ww=z1*sy
+       sjqq=qgjit(qt0,qt0,ww,2,2)  !inclusive qq cross-section
+       sjqg=qgjit(qt0,qt0,ww,1,2)  !inclusive qg cross-section
+       sjgg=qgjit(qt0,qt0,ww,1,1)  !inclusive gg cross-section
+
+       st2=0.d0
+       do j=1,7
+       do k=1,2
+        xx=.5d0*(1.d0+x1(j)*(2*k-3))
+        xp=z1**xx
+        xm=z1/xp
+        glu1=qgppdi(xp,0)
+        sea1=qgppdi(xp,1)
+        glu2=qgppdi(xm,0)
+        sea2=qgppdi(xm,1)
+        st2=st2+a1(j)*(glu1*glu2*sjgg+(glu1*sea2+glu2*sea1)*sjqg
+     *  +sea1*sea2*sjqq)
+       enddo
+       enddo
+       rh=-alfp*dlog(z1)
+       qgpint=qgpint-a1(i)*dlog(z1)/z1**delh*st2
+     * *exp(-bb/(4.d0*.0389d0*rh))/rh
+      enddo
+      enddo
+      qgpint=qgpint*(1.d0-xmin)/(delh-dels)*factk*rr**2/2.d0*pi
+
+      if(debug.ge.3)write (moniou,202)qgpint
+201   format(2x,'qgpint - interm. Pomeron eikonal:'
+     */4x,'sy=',e10.3,2x,'bb=',e10.3)
+202   format(2x,'qgpint=',e10.3)
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgpini(sy,bb,vvx,vvxt,iqq)
+c-----------------------------------------------------------------------
+c qgpini - intermediate gg-Pomeron eikonal
+c sy   - pomeron mass squared,
+c bb   - impact parameter squared,
+c vvx  - total / projectile screening factor,
+c vvxt - target screening factor
+c vvx  - total/projectile screening factor:
+c vvx  = 0                                                    (iqq=1,...15)
+c vvx  = 1 - exp[-2*sum_{i} chi_proj(i)-2*sum_j chi_targ(j)]  (iqq=16)
+c vvx  = 1 + exp[-2*sum_{i} chi_proj(i)-2*sum_j chi_targ(j)]
+c          - exp[-2*sum_{i} chi_proj(i)-sum_j chi_targ(j)]
+c          - exp[-sum_{i} chi_proj(i)-2*sum_j chi_targ(j)]    (iqq=17 uncut)
+c vvx  = 1 - exp[-sum_{i} chi_proj(i)-2*sum_j chi_targ(j)]    (iqq=17,...19)
+c vvx  = 1 - exp[-sum_{i} chi_proj(i)]                        (iqq=20,...23)
+c vvxt - target screening factor:
+c vvxt = 0                                                    (iqq=1,...19)
+c vvxt = 1 - exp[-sum_j chi_targ(j)]                          (iqq=20,...23)
+c uncut eikonals:
+c iqq=0  - single soft Pomeron
+c iqq=1  - single Pomeron
+c iqq=2  - general loop contribution
+c iqq=3  - single Pomeron end on one side
+c iqq=4  - single Pomeron ends on both sides
+c cut eikonals:
+c iqq=5  - single cut Pomeron
+c iqq=6  - single cut Pomeron with single end
+c iqq=7  - single cut Pomeron with 2 single ends
+c iqq=8  - any cuts except the complete rap-gap
+c iqq=9  - single cut Pomeron end at one side
+c iqq=10 - single cut Pomeron end at one side and single Pomeron on the other
+c iqq=11 - no rap-gap at one side
+c iqq=12 - no rap-gap at one side and single Pomeron on the other
+c iqq=13 - single cut soft Pomeron
+c iqq=14 - single cut soft Pomeron with single end
+c iqq=15 - single cut soft Pomeron with 2 single ends
+c  with proj/targ screening corrections:
+c iqq=16 - single cut Pomeron
+c iqq=17 - uncut / cut end / loop sequence
+c iqq=18 - no rap-gap at the end
+c iqq=19 - single cut Pomeron end
+c iqq=20 - diffractive cut, Puu
+c iqq=21 - diffractive cut, Puu-Puc
+c iqq=22 - diffractive cut, Pcc
+c iqq=23 - diffractive cut, Pcc+Pcu
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wk(3),wz(3),wi(3),wj(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr20/ spmax
+      common /qgarr26/ factk,fqscal
+      common /qgarr39/ qpomi(51,11,15),qpomis(51,11,11,11,9)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      qgpini=0.d0
+      pinm=0.d0
+      s2min=4.d0*fqscal*qt0
+      rp=alfp*dlog(sy)*4.d0*.0389d0
+      z=exp(-bb/rp)
+      if(iqq.le.1.and.(sy.le.s2min.or.iqq.eq.0))goto 1
+
+      yl=log(sy/sgap)/log(spmax/sgap)*50.d0+1.d0
+      k=max(1,int(1.00001d0*yl-1.d0))
+      k=min(k,49)
+      wk(2)=yl-k
+      if(yl.le.2.d0)then
+       iymax=2
+       wk(1)=1.d0-wk(2)
+      else
+       wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+       wk(1)=1.d0-wk(2)+wk(3)
+       wk(2)=wk(2)-2.d0*wk(3)
+       iymax=3
+      endif
+
+      if(z.gt..2d0)then
+       zz=5.d0*z+6.d0
+      else
+       zz=(-bb/rp-dlog(0.2d0))/2.d0+7.d0
+      endif
+      jz=min(9,int(zz))
+      jz=max(1,jz)
+      if(zz.lt.1.d0)then
+       wz(2)=zz-jz
+       wz(1)=1.d0-wz(2)
+       izmax=2
+      else
+       if(jz.eq.6)jz=5
+       wz(2)=zz-jz
+       wz(3)=wz(2)*(wz(2)-1.d0)*.5d0
+       wz(1)=1.d0-wz(2)+wz(3)
+       wz(2)=wz(2)-2.d0*wz(3)
+       izmax=3
+      endif
+
+      if(iqq.le.15)then
+       iqr=iqq
+       if(sy.le.sgap**2.and.iqq.le.12)iqr=1
+       do l1=1,izmax
+        l2=jz+l1-1
+       do k1=1,iymax
+        k2=k+k1-1
+        qgpini=qgpini+qpomi(k2,l2,iqr)*wk(k1)*wz(l1)
+       enddo
+       enddo
+       if(zz.lt.1.d0)then
+        do k1=1,iymax
+         k2=k+k1-1
+         pinm=pinm+qpomi(k2,1,iqr)*wk(k1)
+        enddo
+        qgpini=min(qgpini,pinm)
+       endif
+
+      else
+       vi=vvx*10.d0+1.d0
+       i=max(1,int(vi))
+       i=min(i,9)
+       wi(2)=vi-i
+       wi(3)=wi(2)*(wi(2)-1.d0)*.5d0
+       wi(1)=1.d0-wi(2)+wi(3)
+       wi(2)=wi(2)-2.d0*wi(3)
+
+       if(iqq.le.19)then
+        do i1=1,3
+         i2=i+i1-1
+        do l1=1,izmax
+         l2=jz+l1-1
+        do k1=1,iymax
+         k2=k+k1-1
+         qgpini=qgpini+qpomis(k2,l2,i2,1,iqq-15)*wk(k1)*wz(l1)*wi(i1)
+        enddo
+        enddo
+        enddo
+        if(zz.lt.1.d0)then
+         do i1=1,3
+          i2=i+i1-1
+         do k1=1,iymax
+          k2=k+k1-1
+          pinm=pinm+qpomis(k2,1,i2,1,iqq-15)*wk(k1)*wi(i1)
+         enddo
+         enddo
+         qgpini=min(qgpini,pinm)
+        endif
+
+       else
+        vj=vvxt*10.d0+1.d0
+        j=max(1,int(vj))
+        j=min(j,9)
+        wj(2)=vj-j
+        wj(3)=wj(2)*(wj(2)-1.d0)*.5d0
+        wj(1)=1.d0-wj(2)+wj(3)
+        wj(2)=wj(2)-2.d0*wj(3)
+        jmax=3
+
+        do j1=1,jmax
+         j2=j+j1-1
+        do i1=1,3
+         i2=i+i1-1
+        do l1=1,izmax
+         l2=jz+l1-1
+        do k1=1,iymax
+         k2=k+k1-1
+         qgpini=qgpini+qpomis(k2,l2,i2,j2,iqq-15)
+     *   *wk(k1)*wz(l1)*wi(i1)*wj(j1)
+        enddo
+        enddo
+        enddo
+        enddo
+        if(zz.lt.1.d0)then
+         do j1=1,jmax
+          j2=j+j1-1
+         do i1=1,3
+          i2=i+i1-1
+         do k1=1,iymax
+          k2=k+k1-1
+          pinm=pinm+qpomis(k2,1,i2,j2,iqq-15)*wk(k1)*wi(i1)*wj(j1)
+         enddo
+         enddo
+         enddo
+         qgpini=min(qgpini,pinm)
+        endif
+       endif
+      endif
+1     qgpini=exp(qgpini)
+      if(iqq.le.16.or.iqq.eq.19)qgpini=qgpini
+     **sy**dels*sigs*g3p**2*z/rp*4.d0*.0389d0
+      return
+      end
+
+c=============================================================================
+      double precision function qgleg(sy,bb,icdp,icz)
+c-----------------------------------------------------------------------------
+c qgleg - integrated Pomeron leg eikonal
+c sy  - pomeron mass squared,
+c bb  - impact parameter squared,
+c icz - hadron class
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr19/ ahl(3)
+      common /qgarr25/ ahv(3)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      if(debug.ge.2)write (moniou,201)sy,bb,icz
+
+      qgleg=0.d0
+      if(sy.lt.1.001d0)then
+       tmin=1.d0
+      else
+       tmin=(1.d0-(1.d0-1.d0/sy)**(1.+ahl(icz)))**(1.+dels)
+      endif
+      if(debug.ge.3)write (moniou,203)tmin
+      do i1=1,7
+      do m1=1,2
+       tp=1.d0-(.5d0*(1.d0+tmin)+x1(i1)*(m1-1.5d0)*(1.d0-tmin))
+     * **(1./(1.+dels))
+       if(tp.gt.1.d-9)then
+        xp=1.d0-tp**(1.d0/(1.d0+ahl(icz)))
+       else
+        xp=1.d0
+       endif
+
+       ws=qgls(xp*sy,xp,bb,icdp,icz)
+       wg=qglsh(xp*sy,xp,bb,icdp,icz,0)
+       wq=qglsh(xp*sy,xp,bb,icdp,icz,1)/dsqrt(xp)
+     * *(1.d0-xp)**(ahv(icz)-ahl(icz))
+
+       qgleg=qgleg+a1(i1)*(ws+wg+wq)/(1.d0-tp)**dels
+      enddo
+      enddo
+      qgleg=qgleg/2.d0/(1.+ahl(icz))/(1.d0+dels)
+
+      if(debug.ge.3)write (moniou,202)qgleg
+201   format(2x,'qgleg - Pomeron leg eikonal:'
+     */4x,'s=',e10.3,2x,'b^2=',e10.3,2x,'icz=',i1)
+202   format(2x,'qgleg=',e10.3)
+203   format(2x,'qgleg:',2x,'tmin=',e10.3)
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qglegi(sy,bb,icdp,icz,iqq)
+c-----------------------------------------------------------------------
+c qglegi - integrated Pomeron leg eikonal
+c sy   - pomeron mass squared,
+c bb   - impact parameter squared,
+c icdp - diffractive state for the hadron,
+c icz  - hadron class
+c iqq=1 - single leg Pomeron
+c iqq=2 - all loops
+c iqq=3 - single Pomeron end
+c iqq=4 - single cut Pomeron
+c iqq=5 - single cut Pomeron with single Pomeron end
+c iqq=6 - single cut Pomeron end
+c iqq=7 - no rap-gap at the end
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wk(3),wz(3)
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr19/ ahl(3)
+      common /qgarr20/ spmax
+      common /qgarr27/ qlegi(51,11,2,3,7),qfanu(51,11,11,6,2)
+     *,qfanc(51,11,11,39,18),qdfan(21,11,11,2,3),qrev(11,11,66,219,2)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      qglegi=0.d0
+      xlegm=0.d0
+      rp=(rq(icdp,icz)+alfp*log(sy))*4.d0*.0389d0
+      z=exp(-bb/rp)
+      if(z.gt..2d0)then
+       zz=5.d0*z+6.d0
+      else
+       zz=(-bb/rp-dlog(0.2d0))/2.d0+7.d0
+      endif
+      jz=min(9,int(zz))
+      jz=max(1,jz)
+      if(zz.lt.1.d0)then
+       wz(2)=zz-jz
+       wz(1)=1.d0-wz(2)
+       izmax=2
+      else
+       if(jz.eq.6)jz=5
+       wz(2)=zz-jz
+       wz(3)=wz(2)*(wz(2)-1.d0)*.5d0
+       wz(1)=1.d0-wz(2)+wz(3)
+       wz(2)=wz(2)-2.d0*wz(3)
+       izmax=3
+      endif
+
+      yl=log(sy/sgap)/log(spmax/sgap)*50.d0+1.d0
+      k=max(1,int(yl))
+      k=min(k,49)
+      wk(2)=yl-k
+      wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+      wk(1)=1.d0-wk(2)+wk(3)
+      wk(2)=wk(2)-2.d0*wk(3)
+      iymax=3
+
+      iqr=iqq
+      if(sy.le.sgap**2)iqr=1
+      do l1=1,izmax
+       l2=jz+l1-1
+      do k1=1,iymax
+       k2=k+k1-1
+       qglegi=qglegi+qlegi(k2,l2,icdp,icz,iqr)*wk(k1)*wz(l1)
+      enddo
+      enddo
+      if(zz.lt.1.d0)then
+       do k1=1,iymax
+        k2=k+k1-1
+        xlegm=xlegm+qlegi(k2,1,icdp,icz,iqr)*wk(k1)
+       enddo
+       qglegi=min(qglegi,xlegm)
+      endif
+      qglegi=exp(qglegi)*z
+     **(1.d0-(1.d0-(1.d0-1.d0/sy)**(1.+ahl(icz)))**(1.+dels))
+      return
+      end
+
+c=============================================================================
+      double precision function qgls(sy,xp,bb,icdp,icz)
+c-----------------------------------------------------------------------------
+c qgls - soft pomeron leg eikonal
+c sy   - pomeron mass squared,
+c xp   - pomeron light cone momentum,
+c bb   - impact parameter squared,
+c icdp - diffractive state for the connected hadron,
+c icz  - hadron class
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)sy,bb,icz
+
+      rp=rq(icdp,icz)+alfp*log(sy/xp)
+      qgls=sy**dels*fp(icz)*g3p*sigs/rp*exp(-bb/(4.d0*.0389d0*rp))
+     **cd(icdp,icz)
+
+      if(debug.ge.3)write (moniou,202)qgls
+201   format(2x,'qgls - soft pomeron leg eikonal:'
+     */4x,'sy=',e10.3,2x,'b^2=',e10.3,2x,'icz=',i1)
+202   format(2x,'qgls=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qglsh(sy,xp,bb,icdp,icz,iqq)
+c-----------------------------------------------------------------------------
+c qglsh - unintegrated Pomeron leg eikonal
+c sy  - pomeron mass squared,
+c xp  - light cone momentum share,
+c bb  - impact parameter squared,
+c icz - hadron class
+c iqq=0 - gluon/sea quark contribution,
+c iqq=1 - valence quark contribution
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr19/ ahl(3)
+      common /qgarr25/ ahv(3)
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      if(debug.ge.2)write (moniou,201)sy,bb,icz
+
+      qglsh=0.d0
+      s2min=4.d0*fqscal*qt0
+      if(sy.lt.1.001d0*s2min)return
+
+      xmin=(s2min/sy)**(delh-dels)
+c numerical integration over zh
+      do i1=1,7
+      do m1=1,2
+       zh=(.5d0*(1.d0+xmin-(2*m1-3)*x1(i1)*(1.d0-xmin)))
+     * **(1.d0/(delh-dels))
+       ww=zh*sy         !c.m. energy squared for hard interaction
+       sjqq=qgjit(qt0,qt0,ww,2,2)
+       sjqg=qgjit(qt0,qt0,ww,1,2)
+       sjgg=qgjit(qt0,qt0,ww,1,1)
+       if(debug.ge.3)write (moniou,203)ww,sjqq+sjqg+sjgg
+
+       if(iqq.eq.0)then
+        stg=0.d0
+        do i2=1,7
+        do m2=1,2
+         xx=.5d0*(1.d0+x1(i2)*(2*m2-3))
+         xph=zh**xx
+         xmh=zh/xph
+         glu1=qgppdi(xph,0)
+         sea1=qgppdi(xph,1)
+         glu2=qgppdi(xmh,0)
+         sea2=qgppdi(xmh,1)
+         rh=rq(icdp,icz)-alfp*dlog(zh*xp)
+
+         stsum=(glu1*glu2*sjgg+(glu1*sea2+glu2*sea1)*sjqg
+     *   +sea1*sea2*sjqq)*exp(-bb/(4.d0*.0389d0*rh))/rh
+         stg=stg+a1(i2)*stsum
+        enddo
+        enddo
+        qglsh=qglsh-a1(i1)*dlog(zh)/zh**delh*stg
+
+       elseif(iqq.eq.1)then
+        xmh=zh
+        glu2=qgppdi(xmh,0)
+        sea2=qgppdi(xmh,1)
+        rh=rq(icdp,icz)-alfp*dlog(zh)
+
+        stq=(glu2*sjqg+sea2*sjqq)*exp(-bb/(4.d0*.0389d0*rh))/rh
+        qglsh=qglsh+a1(i1)/zh**delh*stq
+     *  *(qggrv(xp,qt0,icz,1)+qggrv(xp,qt0,icz,2))/dsqrt(xp)
+       endif
+      enddo
+      enddo
+      if(iqq.eq.0)then
+       qglsh=qglsh*rr**2*(1.d0-xmin)/(delh-dels)*fp(icz)*g3p*factk
+     * /2.d0*pi*cd(icdp,icz)
+      elseif(iqq.eq.1)then
+       qglsh=qglsh*rr*(1.d0-xmin)/(delh-dels)*g3p*factk/4.d0
+     * *cd(icdp,icz)
+      endif
+
+      if(debug.ge.3)write (moniou,202)qglsh
+201   format(2x,'qglsh - unintegrated Pomeron leg eikonal:'
+     */4x,'s=',e10.3,2x,'b^2=',e10.3,2x,'icz=',i1)
+202   format(2x,'qglsh=',e10.3)
+203   format(2x,'qglsh:',2x,'s_hard=',e10.3,2x,'sigma_hard=',e10.3)
+      return
+      end
+
+c------------------------------------------------------------------------
+      subroutine qgloop(sy,bb,fann,jj)
+c-----------------------------------------------------------------------
+c qgloop - intermediate Pomeron eikonal with loops
+c sy   - pomeron mass squared,
+c bb   - impact parameter squared,
+c jj=1 - uncut loops (iqq=1,...3)
+c jj=2 - cut loops (iqq=4,...11)
+c iqq=1  - general loop contribution
+c iqq=2  - single Pomeron end on one side
+c iqq=3  - single Pomeron ends on both sides
+c iqq=4  - single cut Pomeron
+c iqq=5  - single cut Pomeron with single end
+c iqq=6  - single cut Pomeron with 2 single ends
+c iqq=7  - any cuts except the complete rap-gap
+c iqq=8  - single cut Pomeron at one side
+c iqq=9  - single cut Pomeron at one side and single Pomeron on the other
+c iqq=10 - no rap-gap at one side
+c iqq=11 - no rap-gap at one side and single Pomeron on the other
+c iqq=12 - single cut soft Pomeron
+c iqq=13 - single cut soft Pomeron with single end
+c iqq=14 - single cut soft Pomeron with 2 single ends
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension fann(14)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      do iqq=1,14
+       fann(iqq)=0.d0
+      enddo
+      if(sy.le.sgap**2)goto 1
+      do ix1=1,7
+      do mx1=1,2
+       xpomr=(sy/sgap**2)**(-.5d0-x1(ix1)*(mx1-1.5d0))/sgap
+       rp=-alfp*log(xpomr)*4.d0*.0389d0
+       rp1=alfp*log(xpomr*sy)*4.d0*.0389d0
+       rp2=rp*rp1/(rp+rp1)
+      do ix2=1,7
+      do mx2=1,2
+       z=.5d0+x1(ix2)*(mx2-1.5d0)
+       bb0=-rp2*log(z)
+      do ix3=1,7
+      do mx3=1,2
+       phi=pi*(.5d0+x1(ix3)*(mx3-1.5d0))
+       bb1=(dsqrt(bb)*rp1/(rp+rp1)-dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+       bb2=(dsqrt(bb)*rp/(rp+rp1)+dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+
+       vi=qgpini(xpomr*sy,bb1,0.d0,0.d0,1)
+       vit=min(vi,qgpini(xpomr*sy,bb1,0.d0,0.d0,2))
+       v1i0=qgpini(1.d0/xpomr,bb2,0.d0,0.d0,4)
+       v1i1=min(v1i0,qgpini(1.d0/xpomr,bb2,0.d0,0.d0,3))
+       v1i=min(v1i1,qgpini(1.d0/xpomr,bb2,0.d0,0.d0,2))
+       if(jj.eq.1)then
+        do iqq=1,3
+         if(iqq.eq.1)then
+          dpx=vi*(min(0.d0,1.d0-exp(-v1i)-v1i)+v1i-v1i1)
+     *    +min(0.d0,1.d0-exp(-vit)-vit)*(1.d0-exp(-v1i))
+         elseif(iqq.eq.2)then
+          dpx=vi*(min(0.d0,1.d0-exp(-v1i)-v1i)+v1i-v1i1)
+         elseif(iqq.eq.3)then
+          dpx=vi*(v1i1-v1i0)
+         else
+          dpx=0.d0
+         endif
+         fann(iqq)=fann(iqq)+a1(ix1)*a1(ix2)*a1(ix3)*dpx/z*rp2
+        enddo
+
+       else
+        v1ic0=min(v1i0,qgpini(1.d0/xpomr,bb2,0.d0,0.d0,7))
+        v1ic1=min(v1ic0,qgpini(1.d0/xpomr,bb2,0.d0,0.d0,6))
+        v1ic=min(v1ic1,qgpini(1.d0/xpomr,bb2,0.d0,0.d0,5))
+        v1icn=min(v1i,qgpini(1.d0/xpomr,bb2,0.d0,0.d0,8))
+        vict=min(vit,qgpini(xpomr*sy,bb1,0.d0,0.d0,5))
+        victn=min(vit,qgpini(xpomr*sy,bb1,0.d0,0.d0,8))
+        victg=min(victn,qgpini(xpomr*sy,bb1,0.d0,0.d0,11))
+        vict1=min(victg,qgpini(xpomr*sy,bb1,0.d0,0.d0,9))
+
+        vis=min(vi,qgpini(xpomr*sy,bb1,0.d0,0.d0,0))
+        v1ic0s=min(v1ic0,qgpini(1.d0/xpomr,bb2,0.d0,0.d0,15))
+        v1ic1s=min(v1ic0s,qgpini(1.d0/xpomr,bb2,0.d0,0.d0,14))
+        v1ics=min(v1ic1s,qgpini(1.d0/xpomr,bb2,0.d0,0.d0,13))
+        victs=min(vict,qgpini(xpomr*sy,bb1,0.d0,0.d0,13))
+        do iqq=4,14
+         if(iqq.eq.4)then
+          dpx=vi*(v1ic*exp(-2.d0*v1icn)-v1ic1)
+     *    +vict*(exp(-2.d0*victn)-1.d0)*v1ic*exp(-2.d0*v1icn)
+         elseif(iqq.eq.5)then
+          dpx=vi*(v1ic*exp(-2.d0*v1icn)-v1ic1)
+         elseif(iqq.eq.6)then
+          dpx=vi*(v1ic1-v1ic0)
+         elseif(iqq.eq.7)then
+          dpx=vi*(min(0.d0,1.d0-exp(-v1i)-v1i)+v1i-v1i1)
+     *    +.5d0*min(0.d0,1.d0-exp(-vit)-vit)*(1.d0-exp(-2.d0*v1icn))
+     *    +.5d0*min(0.d0,1.d0-exp(-2.d0*victn)-2.d0*victn)
+     *    *max(0.d0,1.d0-exp(-v1i)-.5d0*(1.d0-exp(-2.d0*v1icn)))
+         elseif(iqq.eq.8)then
+          dpx=vi*(min(0.d0,1.d0-exp(-v1i)-v1i)+v1i-v1i1)
+     *    +vict1*(exp(-2.d0*victn)-1.d0)*(1.d0-exp(-v1i))
+         elseif(iqq.eq.9)then
+          dpx=vi*(v1i1-v1i0)
+     *    +vict1*(exp(-2.d0*victn)-1.d0)*v1i1
+         elseif(iqq.eq.10)then
+          dpx=vi*(min(0.d0,1.d0-exp(-v1i)-v1i)+v1i-v1i1)
+     *    +(.5d0*max(0.d0,1.d0-exp(-2.d0*victn)-2.d0*victn
+     *    *exp(-2.d0*victn))+victg*(exp(-2.d0*victn)-1.d0))
+     *    *(1.d0-exp(-v1i))
+         elseif(iqq.eq.11)then
+          dpx=vi*(v1i1-v1i0)
+     *    +(.5d0*max(0.d0,1.d0-exp(-2.d0*victn)-2.d0*victn
+     *    *exp(-2.d0*victn))+victg*(exp(-2.d0*victn)-1.d0))*v1i1
+         elseif(iqq.eq.12)then
+          dpx=vis*(v1ics*exp(-2.d0*v1icn)-v1ic1s)
+     *    +victs*(exp(-2.d0*victn)-1.d0)*v1ics*exp(-2.d0*v1icn)
+         elseif(iqq.eq.13)then
+          dpx=vis*(v1ics*exp(-2.d0*v1icn)-v1ic1s)
+         elseif(iqq.eq.14)then
+          dpx=vis*(v1ic1s-v1ic0s)
+         else
+          dpx=0.d0
+         endif
+         fann(iqq)=fann(iqq)+a1(ix1)*a1(ix2)*a1(ix3)*dpx/z*rp2
+        enddo
+       endif
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+1     dpin=qgpini(sy,bb,0.d0,0.d0,1)
+      do iqq=1,11
+       fann(iqq)=fann(iqq)*log(sy/sgap**2)/8.d0*pi*r3p/.0389d0/g3p**3
+     * +dpin
+      enddo
+      dpins=min(dpin,qgpini(sy,bb,0.d0,0.d0,0))
+      do iqq=12,14
+       fann(iqq)=fann(iqq)*log(sy/sgap**2)/8.d0*pi*r3p/.0389d0/g3p**3
+     * +dpins
+      enddo
+      return
+      end
+
+c------------------------------------------------------------------------
+      subroutine qgloos(sy,bb,vvx,vvxt,fann)
+c-----------------------------------------------------------------------
+c qgloos - intermediate Pomeron eikonal with screening corrections
+c sy   - pomeron mass squared,
+c bb   - impact parameter squared,
+c vvx  - total/projectile screening factor:
+c vvx  = 1 - exp[-2*sum_{i} chi_proj(i)-2*sum_j chi_targ(j)]  (iqq=1)
+c vvx  = 1 + exp[-2*sum_{i} chi_proj(i)-2*sum_j chi_targ(j)]
+c          - exp[-2*sum_{i} chi_proj(i)-sum_j chi_targ(j)]
+c          - exp[-sum_{i} chi_proj(i)-2*sum_j chi_targ(j)]    (iqq=2 uncut)
+c vvx  = 1 - exp[-sum_{i} chi_proj(i)-2*sum_j chi_targ(j)]    (iqq=2,...4)
+c vvx  = 1 - exp[-sum_{i} chi_proj(i)]                        (iqq=5,...8)
+c vvxt - target screening factor:
+c vvxt = 0                                                    (iqq=1,...4)
+c vvxt = 1 - exp[-sum_j chi_targ(j)]                          (iqq=5,...8)
+c iqq=1  - single cut Pomeron
+c iqq=2  - uncut / cut end / loop sequence
+c iqq=3  - no rap-gap at the end
+c iqq=4  - single cut Pomeron end
+c iqq=5  - diffractive cut, Puu
+c iqq=6  - diffractive cut, Puu-Puc
+c iqq=7  - diffractive cut, Pcc
+c iqq=8  - diffractive cut, Pcc+Pcu
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension fann(14)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      do iqq=1,8
+       fann(iqq)=0.d0
+      enddo
+      if(sy.le.sgap**2)goto 1
+
+      do ix1=1,7
+      do mx1=1,2
+       xpomr=(sy/sgap**2)**(-.5d0-x1(ix1)*(mx1-1.5d0))/sgap
+       rp=-alfp*log(xpomr)*4.d0*.0389d0
+       rp1=alfp*log(xpomr*sy)*4.d0*.0389d0
+       rp2=rp*rp1/(rp+rp1)
+      do ix2=1,7
+      do mx2=1,2
+       z=.5d0+x1(ix2)*(mx2-1.5d0)
+       bb0=-rp2*log(z)
+      do ix3=1,7
+      do mx3=1,2
+       phi=pi*(.5d0+x1(ix3)*(mx3-1.5d0))
+       bb1=(dsqrt(bb)*rp1/(rp+rp1)-dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+       bb2=(dsqrt(bb)*rp/(rp+rp1)+dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+
+       vit=qgpini(xpomr*sy,bb1,0.d0,0.d0,2)
+       vicn=min(vit,qgpini(xpomr*sy,bb1,0.d0,0.d0,8))
+       vicng=min(vicn,qgpini(xpomr*sy,bb1,0.d0,0.d0,11))
+       vicpe=min(vicng,qgpini(xpomr*sy,bb1,0.d0,0.d0,9))
+       vic1=min(vicpe,qgpini(xpomr*sy,bb1,0.d0,0.d0,5))
+
+       viu=qgpini(1.d0/xpomr,bb2,0.d0,0.d0,2)
+       v1icn=min(viu,qgpini(1.d0/xpomr,bb2,0.d0,0.d0,8))
+       v1i=qgpini(1.d0/xpomr,bb2,vvx,0.d0,16)*exp(-2.d0*v1icn)
+       vi=qgpini(1.d0/xpomr,bb2,vvx,0.d0,17)*(1.d0-exp(-viu))
+       vduu=qgpini(1.d0/xpomr,bb2,vvx,vvxt,20)*(1.d0-exp(-viu))
+       vduc=max(0.d0,vduu-qgpini(1.d0/xpomr,bb2,vvx,vvxt,21)
+     * *(1.d0-exp(-viu)))
+       vdcc=qgpini(1.d0/xpomr,bb2,vvx,vvxt,22)*((1.d0-exp(-viu))**2
+     * +(exp(2.d0*(viu-v1icn))-1.d0)*exp(-2.d0*viu))/2.d0
+       vdcu=max(0.d0,qgpini(1.d0/xpomr,bb2,vvx,vvxt,23)
+     * *((1.d0-exp(-viu))**2+(exp(2.d0*(viu-v1icn))-1.d0)
+     * *exp(-2.d0*viu))/2.d0-vdcc)
+
+       do iqq=1,8
+        if(iqq.eq.1)then       !single cut Pomeron
+         dpx=-vvx*v1i*vic1*exp(-2.d0*vicn)
+        elseif(iqq.eq.2)then   !uncut / cut end / loop sequence
+         dpx=-(1.d0-exp(-vit))*vi*vvx
+        elseif(iqq.eq.3)then   !no rap-gap at the end
+         dpx=-(.5d0*max(0.d0,1.d0-exp(-2.d0*vicn)*(1.d0+2.d0*vicn))
+     *   +vicng*exp(-2.d0*vicn))*vi*vvx
+        elseif(iqq.eq.4)then   !single cut Pomeron end
+         dpx=-vicpe*exp(-2.d0*vicn)*vi*vvx
+        elseif(iqq.eq.5)then   !Puu
+         dpx=(1.d0-exp(-vit))
+     *   *(vduu*((1.d0-vvx)*(1.d0-vvxt)*(1.d0-vvx*vvxt)-1.d0)
+     *   -vdcu*(1.d0-vvx)**2*(1.d0-vvxt)*vvxt)
+        elseif(iqq.eq.6)then   !Puu-Puc
+         dpx=(1.d0-exp(-vit))
+     *   *((vduu-vduc)*((1.d0-vvx)*(1.d0-vvxt)*(1.d0-vvx*vvxt)-1.d0)
+     *   -(vdcc+vdcu)*(1.d0-vvx)**2*(1.d0-vvxt)*vvxt)
+        elseif(iqq.eq.7)then   !Pcc
+         dpx=.5d0*((1.d0-exp(-vit))**2
+     *   +(exp(2.d0*(vit-vicn))-1.d0)*exp(-2.d0*vit))
+     *   *(vdcc*((1.d0-vvx)**2*(1.d0-vvxt)**2-1.d0)
+     *   -vduc*(1.d0-vvx)*(1.d0-vvxt)**2*vvx)
+        elseif(iqq.eq.8)then   !Pcc+Pcu
+         dpx=.5d0*((1.d0-exp(-vit))**2
+     *   +(exp(2.d0*(vit-vicn))-1.d0)*exp(-2.d0*vit))
+     *   *((vdcc+vdcu)*((1.d0-vvx)**2*(1.d0-vvxt)**2-1.d0)
+     *   +(vduu-vduc)*(1.d0-vvx)*(1.d0-vvxt)**2*vvx)
+        else
+         dpx=0.d0
+        endif
+        fann(iqq)=fann(iqq)+a1(ix1)*a1(ix2)*a1(ix3)*dpx/z*rp2
+       enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+1     vit=qgpini(sy,bb,0.d0,0.d0,2)
+      vicn=min(vit,qgpini(sy,bb,0.d0,0.d0,8))
+      vicng=min(vicn,qgpini(sy,bb,0.d0,0.d0,11))
+      vicpe=min(vicng,qgpini(sy,bb,0.d0,0.d0,9))
+      vic1=min(vicpe,qgpini(sy,bb,0.d0,0.d0,5))
+      do iqq=1,8
+       fann(iqq)=fann(iqq)*log(sy/sgap**2)/8.d0*pi*r3p/.0389d0/g3p**3
+       if(iqq.eq.1)then
+        fann(iqq)=fann(iqq)*exp(2.d0*vicn)+vic1
+       elseif(iqq.eq.3)then
+        fann(iqq)=fann(iqq)+vicng*exp(-2.d0*vicn)
+     *  +.5d0*max(0.d0,1.d0-exp(-2.d0*vicn)*(1.d0+2.d0*vicn))
+       elseif(iqq.eq.4)then
+        fann(iqq)=fann(iqq)*exp(2.d0*vicn)+vicpe
+       elseif(iqq.lt.7)then
+        fann(iqq)=fann(iqq)+(1.d0-exp(-vit))
+       else
+        fann(iqq)=fann(iqq)+.5d0*((1.d0-exp(-vit))**2
+     *  +(exp(2.d0*(vit-vicn))-1.d0)*exp(-2.d0*vit))
+       endif
+      enddo
+      return
+      end
+
+c------------------------------------------------------------------------
+      subroutine qglool(sy,bb,icdp,icz,fann)
+c-----------------------------------------------------------------------
+c qglool - integrated Pomeron leg eikonal with loops
+c sy   - pomeron mass squared,
+c bb   - impact parameter squared,
+c icz  - hadron class
+c iqq=1 - all
+c iqq=2 - single Pomeron end
+c iqq=3 - single cut Pomeron
+c iqq=4 - single cut Pomeron with single Pomeron end
+c iqq=5 - single cut Pomeron end
+c iqq=6 - no rap-gap at the end
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension fann(14)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr19/ ahl(3)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      do iqq=1,6
+       fann(iqq)=0.d0
+      enddo
+      if(sy.le.sgap**2)goto 1
+
+      do ix1=1,7
+      do mx1=1,2
+       xpomr=(sy/sgap**2)**(-.5d0-x1(ix1)*(mx1-1.5d0))/sgap
+       rp=(rq(icdp,icz)-alfp*log(xpomr))*4.d0*.0389d0
+       rp1=alfp*log(xpomr*sy)*4.d0*.0389d0
+       rp2=rp*rp1/(rp+rp1)
+      do ix2=1,7
+      do mx2=1,2
+       z=.5d0+x1(ix2)*(mx2-1.5d0)
+       bb0=-rp2*log(z)
+      do ix3=1,7
+      do mx3=1,2
+       phi=pi*(.5d0+x1(ix3)*(mx3-1.5d0))
+       bb1=(dsqrt(bb)*rp1/(rp+rp1)-dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+       bb2=(dsqrt(bb)*rp/(rp+rp1)+dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+
+       vpl=qglegi(1.d0/xpomr,bb2,icdp,icz,1)
+       v1i0=qgpini(xpomr*sy,bb1,0.d0,0.d0,4)
+       v1i1=min(v1i0,qgpini(xpomr*sy,bb1,0.d0,0.d0,3))
+       v1i=min(v1i1,qgpini(xpomr*sy,bb1,0.d0,0.d0,2))
+       v1ic0=min(v1i0,qgpini(xpomr*sy,bb1,0.d0,0.d0,7))
+       v1ic1=min(v1ic0,qgpini(xpomr*sy,bb1,0.d0,0.d0,6))
+       v1ic=min(v1ic1,qgpini(xpomr*sy,bb1,0.d0,0.d0,5))
+       v1icn=min(v1i,qgpini(xpomr*sy,bb1,0.d0,0.d0,8))
+       vicn0=min(v1i1,qgpini(xpomr*sy,bb1,0.d0,0.d0,12))
+       vicn=min(vicn0,qgpini(xpomr*sy,bb1,0.d0,0.d0,11))
+       vic0=min(vicn0,qgpini(xpomr*sy,bb1,0.d0,0.d0,10))
+       vic1=min(vic0,qgpini(xpomr*sy,bb1,0.d0,0.d0,9))
+       vicn=min(vicn,v1icn)
+       vic1=min(vicn,vic1)
+       do iqq=1,6
+        if(iqq.eq.1)then
+         dpx=vpl*(min(0.d0,1.d0-exp(-v1i)-v1i)+v1i-v1i1)
+        elseif(iqq.eq.2)then
+         dpx=vpl*(v1i1-v1i0)
+        elseif(iqq.eq.3)then
+         dpx=vpl*(v1ic*exp(-2.d0*v1icn)-v1ic1)
+        elseif(iqq.eq.4)then
+         dpx=vpl*(v1ic1-v1ic0)
+        elseif(iqq.eq.5)then
+         dpx=vpl*(vic1*exp(-2.d0*v1icn)-vic0)
+        elseif(iqq.eq.6)then
+         dpx=vpl*(.5d0*max(0.d0,1.d0-exp(-2.d0*v1icn)-2.d0*v1icn
+     *   *exp(-2.d0*v1icn))+vicn*exp(-2.d0*v1icn)-vicn0)
+        else
+         dpx=0.d0
+        endif
+        fann(iqq)=fann(iqq)+a1(ix1)*a1(ix2)*a1(ix3)*dpx/z*rp2
+       enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+1     dlool=qglegi(sy,bb,icdp,icz,1)
+      do iqq=1,6
+       fann(iqq)=(fann(iqq)*log(sy/sgap**2)/8.d0*pi*r3p/.0389d0/g3p**3
+     * +dlool)/(1.d0-(1.d0-(1.d0-1.d0/sy)**(1.+ahl(icz)))**(1.+dels))
+      enddo
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgrev(sy,bb,vvxt0,vvxt,vvxpt,vvxp0
+     *,vvxpl,icdp,icz)
+c-----------------------------------------------------------------------
+c qgrev - zigzag contribution
+c sy    - c.m. energy squared,
+c bb    - impact parameter squared,
+c icdp  - diffractive state for the projectile,
+c icz   - hadron class,
+c vvxt0 = 1 - exp[-sum_j chi^(3)_targ(j)]
+c vvxt  = 1 - exp[-sum_j chi_targ(j)]
+c vvxpt = 1 - exp[-sum_{i>I} chi^(6)_proj(i)]
+c vvxp0 = 1 - exp[-sum_{i>I} chi^(3)_proj(i)]
+c vvxpl = 1 - exp[-sum_{i<I} chi_proj(i)]
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr19/ ahl(3)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      qgrev=0.d0
+      if(sy.lt..999d0*sgap**2)return
+
+      do ix1=1,7
+      do mx1=1,2
+       xpomr=(sy/sgap**2)**(-.5d0-x1(ix1)*(mx1-1.5d0))/sgap
+       rp=(rq(icdp,icz)-alfp*log(xpomr))*4.d0*.0389d0
+       rp1=alfp*log(xpomr*sy)*4.d0*.0389d0
+       rp2=rp*rp1/(rp+rp1)
+      do ix2=1,7
+      do mx2=1,2
+       z=.5d0+x1(ix2)*(mx2-1.5d0)
+       bb0=-rp2*log(z)
+      do ix3=1,7
+      do mx3=1,2
+       phi=pi*(.5d0+x1(ix3)*(mx3-1.5d0))
+       bb1=(dsqrt(bb)*rp1/(rp+rp1)-dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+       bb2=(dsqrt(bb)*rp/(rp+rp1)+dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+
+       vvx=1.d0-(1.d0-vvxt)*(1.d0-vvxpl)
+       vpf=qgfani(1.d0/xpomr,bb2,vvx,0.d0,0.d0,icdp,icz,1)
+
+       viu=qgpini(xpomr*sy,bb1,0.d0,0.d0,2)
+       viloop=(1.d0-exp(-viu))
+       vim=2.d0*min(viu,qgpini(xpomr*sy,bb1,0.d0,0.d0,8))
+
+       if(vvxt.eq.0.d0)then
+        vvxpin=1.d0-(1.d0-vvxp0)*(1.d0-vvxpl)*exp(-vpf)
+        vvxtin=0.d0
+        vi=max(0.d0,qgpini(xpomr*sy,bb1,vvxpin,vvxtin,21)*viloop
+     *  -qgpini(xpomr*sy,bb1,vvxpin,vvxtin,23)
+     *  *(viloop**2+(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))/2.d0)
+
+        dpx=vi*(1.d0-exp(-vpf))
+       else
+        vpf0=min(vpf,qgfani(1.d0/xpomr,bb2,vvx,vvxp0,vvxpl,icdp,icz,3))
+        vpft=max(vpf,qgfani(1.d0/xpomr,bb2,vvx,vvxpt,vvxpl,icdp,icz,6))
+        vvxpin=1.d0-(1.d0-vvxp0)*(1.d0-vvxpl)*exp(-vpf0)
+        vvxtin=vvxt0
+        vi=max(0.d0,qgpini(xpomr*sy,bb1,vvxpin,vvxtin,21)*viloop
+     *  -qgpini(xpomr*sy,bb1,vvxpin,vvxtin,23)
+     *  *(viloop**2+(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))/2.d0)
+        if(vvxpt.eq.1.d0)then
+         dpx=vi*(1.d0-exp(-vpft))
+        else
+         dpx=vi*(1.d0-exp(-vpft)+((1.d0-vvxt)**2*(max(0.d0
+     *   ,1.d0-exp(-vpft)*(1.d0+vpft))-max(0.d0,1.d0-exp(-vpf0)
+     *   *(1.d0+vpf0))*(1.d0-vvxp0)/(1.d0-vvxpt))
+     *   +vpft*((1.d0-vvxt)**2*exp(-vpft)-exp(-vpf0)*(1.d0-vvxpl)
+     *   *(1.d0-vvxp0)/(1.d0-vvxpt)*(1.d0-vvxt0)**2)
+     *   -vpf0*exp(-vpf0)*(1.d0-vvxp0)/(1.d0-vvxpt)*((1.d0-vvxt)**2
+     *   -(1.d0-vvxpl)*(1.d0-vvxt0)**2))/(1.d0-(1.d0-vvxt)**2))
+         if(dpx.le.0.d0)dpx=vi*(1.d0-exp(-vpft))
+        endif
+       endif
+
+       qgrev=qgrev+a1(ix1)*a1(ix2)*a1(ix3)*dpx/z*rp2
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      qgrev=qgrev/8.d0*pi*r3p/.0389d0/g3p**3
+      if(.not.(qgrev.gt.0.d0.and.qgrev.lt.1.d10))stop'qgrev=NAN'
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgrevi(sy,bb,vvxt0,vvxt,vvxpt,vvxp0
+     *,vvxpl,icdp,icz)
+c-----------------------------------------------------------------------
+c qgrevi - zigzag contribution (interpolation)
+c sy    - c.m. energy squared,
+c bb    - impact parameter squared,
+c icdp  - diffractive state for the projectile,
+c icz   - hadron class,
+c vvxt0 = 1 - exp[-sum_j chi^(3)_targ(j)]
+c vvxt  = 1 - exp[-sum_j chi_targ(j)
+c vvxpt = 1 - exp[-sum_{i>I} chi^(6)_proj(i)]
+c vvxp0 = 1 - exp[-sum_{i>I} chi^(3)_proj(i)]
+c vvxpl = 1 - exp[-sum_{i<I} chi_proj(i)]
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wk(3),wz(3),wj(3),wi(3),wm2(3),wm3(3),wm4(3)
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr19/ ahl(3)
+      common /qgarr20/ spmax
+      common /qgarr27/ qlegi(51,11,2,3,7),qfanu(51,11,11,6,2)
+     *,qfanc(51,11,11,39,18),qdfan(21,11,11,2,3),qrev(11,11,66,219,2)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      qgrevi=0.d0
+      revm=0.d0
+      if(sy.le.sgap**2)return
+
+      rp=(rq(icdp,icz)+alfp*dlog(sy))*4.d0*.0389d0
+      z=dexp(-bb/rp)
+      if(z.gt..2d0)then
+       zz=5.d0*z+6.d0
+      else
+       zz=(-bb/rp-dlog(0.2d0))/2.d0+7.d0
+      endif
+      jz=min(9,int(zz))
+      jz=max(1,jz)
+      if(zz.lt.1.d0)then
+       wz(2)=zz-jz
+       wz(1)=1.d0-wz(2)
+       izmax=2
+      else
+       if(jz.eq.6)jz=5
+       wz(2)=zz-jz
+       wz(3)=wz(2)*(wz(2)-1.d0)*.5d0
+       wz(1)=1.d0-wz(2)+wz(3)
+       wz(2)=wz(2)-2.d0*wz(3)
+       izmax=3
+      endif
+
+      yl=dlog(sy/sgap**2)/dlog(spmax/sgap**2)*10.d0+1.d0
+      k=max(1,int(1.00001d0*yl-1.d0))
+      k=min(k,9)
+      wk(2)=yl-k
+      if(yl.le.2.d0)then
+       iymax=2
+       wk(1)=1.d0-wk(2)
+      else
+       wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+       wk(1)=1.d0-wk(2)+wk(3)
+       wk(2)=wk(2)-2.d0*wk(3)
+       iymax=3
+      endif
+
+      if(vvxt0.gt..99d0)then
+       j=11
+       wj(1)=1.d0
+       ivmax=1
+       i=1
+       wi(1)=1.d0
+       iv1max=1
+      else
+       vl=max(1.d0,vvxt0*10.d0+1.d0)
+       j=min(int(vl),9)
+       wj(2)=vl-dble(j)
+       wj(3)=wj(2)*(wj(2)-1.d0)*.5d0
+       wj(1)=1.d0-wj(2)+wj(3)
+       wj(2)=wj(2)-2.d0*wj(3)
+       ivmax=3
+
+       vl1=max(1.d0,(vvxt-vvxt0)/(1.d0-vvxt0)*5.d0+1.d0)
+       i=min(int(vl1),4)
+       wi(2)=vl1-dble(i)
+       wi(3)=wi(2)*(wi(2)-1.d0)*.5d0
+       wi(1)=1.d0-wi(2)+wi(3)
+       wi(2)=wi(2)-2.d0*wi(3)
+       iv1max=3
+      endif
+
+      if(icz.ne.2.or.vvxpt+vvxp0+vvxpl.eq.0.d0)then !hadron (no proj. nucl. corr.)
+       ll=icz+(icz-1)*(3-icz)*2
+       do i1=1,iv1max
+        i2=i+i1-2
+       do j1=1,ivmax
+        j2=j+j1-1
+       do l1=1,izmax
+        l2=jz+l1-1
+       do k1=1,iymax
+        k2=k+k1-1
+        qgrevi=qgrevi+qrev(k2,l2,j2+11*i2,ll,icdp)
+     *  *wk(k1)*wz(l1)*wj(j1)*wi(i1)
+       enddo
+       enddo
+       enddo
+       enddo
+       if(zz.lt.1.d0)then
+        do i1=1,iv1max
+         i2=i+i1-2
+        do j1=1,ivmax
+         j2=j+j1-1
+        do k1=1,iymax
+         k2=k+k1-1
+         revm=revm+qrev(k2,1,j2+11*i2,ll,icdp)*wk(k1)*wj(j1)*wi(i1)
+        enddo
+        enddo
+        enddo
+        qgrevi=min(qgrevi,revm)
+       endif
+
+      else
+       vm2=max(1.d0,vvxpt*5.d0+1.d0)
+       m2=min(int(vm2),5)
+       wm2(2)=vm2-dble(m2)
+       wm2(1)=1.d0-wm2(2)
+       im2max=2
+
+       if(vvxpt.lt.1.d-2)then
+        m3=1
+        wm3(1)=1.d0
+        im3max=1
+       else
+        vm3=max(1.d0,vvxp0/vvxpt*5.d0+1.d0)
+        m3=min(int(vm3),5)
+        wm3(2)=vm3-dble(m3)
+        wm3(1)=1.d0-wm3(2)
+        im3max=2
+       endif
+
+       vm4=max(1.d0,vvxpl*5.d0+1.d0)
+       m4=min(int(vm4),5)
+       wm4(2)=vm4-dble(m4)
+       wm4(1)=1.d0-wm4(2)
+       im4max=2
+
+       do mn4=1,im4max
+       do mn3=1,im3max
+       do mn2=1,im2max
+        mn=icz+m2+mn2+6*(m3+mn3-2)+36*(m4+mn4-2)
+       do i1=1,iv1max
+        i2=i+i1-2
+       do j1=1,ivmax
+        j2=j+j1-1
+       do l1=1,izmax
+        l2=jz+l1-1
+       do k1=1,iymax
+        k2=k+k1-1
+        qgrevi=qgrevi+qrev(k2,l2,j2+11*i2,mn,icdp)
+     *  *wk(k1)*wz(l1)*wj(j1)*wi(i1)*wm2(mn2)*wm3(mn3)*wm4(mn4)
+       enddo
+       enddo
+       enddo
+       enddo
+       enddo
+       enddo
+       enddo
+       if(zz.lt.1.d0)then
+        do mn4=1,im4max
+        do mn3=1,im3max
+        do mn2=1,im2max
+         mn=icz+m2+mn2+6*(m3+mn3-2)+36*(m4+mn4-2)
+        do i1=1,iv1max
+         i2=i+i1-2
+        do j1=1,ivmax
+         j2=j+j1-1
+        do k1=1,iymax
+         k2=k+k1-1
+         revm=revm+qrev(k2,1,j2+11*i2,mn,icdp)
+     *   *wk(k1)*wj(j1)*wi(i1)*wm2(mn2)*wm3(mn3)*wm4(mn4)
+        enddo
+        enddo
+        enddo
+        enddo
+        enddo
+        enddo
+        qgrevi=min(qgrevi,revm)
+       endif
+      endif
+      qgrevi=dexp(qgrevi)*z*dlog(sy/sgap**2)
+     **(1.d0-(1.d0-vvxt)**2)*(1.d0-vvxpt)
+      return
+      end
+
+c------------------------------------------------------------------------
+      subroutine qgfan(sy,bb,vvx,icdp,icz,fann)
+c-----------------------------------------------------------------------
+c qgfan - integrated fan-contributions
+c sy    - c.m. energy squared,
+c bb    - impact parameter squared,
+c icdp  - diffractive state for the projectile,
+c icz   - hadron class
+c vvx  = 1 - exp[-sum_j chi_targ(j) - sum_{i<I} chi_proj(i)]
+c iqq=1  - general fan with loops
+c iqq=2  - general fan with single pomeron end
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension fann(14)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr19/ ahl(3)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      do iqq=1,2
+       fann(iqq)=0.d0
+      enddo
+      if(sy.le.sgap**2)goto 1
+
+      do ix1=1,7
+      do mx1=1,2
+       xpomr1=(sy/sgap**2)**(-.5d0-x1(ix1)*(mx1-1.5d0))/sgap
+       rp=(rq(icdp,icz)-alfp*log(xpomr1))*4.d0*.0389d0
+       rp1=alfp*log(xpomr1*sy)*4.d0*.0389d0
+       rp2=rp*rp1/(rp+rp1)
+       do ix2=1,7
+       do mx2=1,2
+        z=.5d0+x1(ix2)*(mx2-1.5d0)
+        bb0=-rp2*log(z)
+       do ix3=1,7
+       do mx3=1,2
+        phi=pi*(.5d0+x1(ix3)*(mx3-1.5d0))
+        bb1=(dsqrt(bb)*rp1/(rp+rp1)-dsqrt(bb0)*cos(phi))**2
+     *  +bb0*sin(phi)**2
+        bb2=(dsqrt(bb)*rp/(rp+rp1)+dsqrt(bb0)*cos(phi))**2
+     *  +bb0*sin(phi)**2
+
+        vpf1=qgfani(1.d0/xpomr1,bb2,vvx,0.d0,0.d0,icdp,icz,2)
+        vpf=min(vpf1,qgfani(1.d0/xpomr1,bb2,vvx,0.d0,0.d0,icdp,icz,1))
+        v1i1=qgpini(xpomr1*sy,bb1,0.d0,0.d0,3)
+        v1i=min(v1i1,qgpini(xpomr1*sy,bb1,0.d0,0.d0,2))
+        do iqq=1,2
+         if(iqq.eq.1)then
+          dpx=(1.d0-exp(-v1i))*(min(0.d0,1.d0-exp(-vpf)-vpf)
+     *    *(1.d0-vvx)-vpf*vvx)
+         else
+          dpx=v1i1*(min(0.d0,1.d0-exp(-vpf)-vpf)*(1.d0-vvx)-vpf*vvx)
+         endif
+         fann(iqq)=fann(iqq)+a1(ix1)*a1(ix2)*a1(ix3)*dpx/z*rp2
+        enddo
+       enddo
+       enddo
+       enddo
+       enddo
+      enddo
+      enddo
+1     continue
+      do iqq=1,2
+       fann(iqq)=(fann(iqq)*dlog(sy/sgap**2)/8.d0*pi*r3p/.0389d0/g3p**3
+     * +qglegi(sy,bb,icdp,icz,iqq+1))
+     * /(1.d0-(1.d0-(1.d0-1.d0/sy)**(1.+ahl(icz)))**(1.+dels))
+      enddo
+      return
+      end
+
+c------------------------------------------------------------------------
+      subroutine qgfanc(sy,bb,vvx,vvxp,vvxpl,icdp,icz,fann)
+c-----------------------------------------------------------------------
+c qgfan - cut fan-contributions
+c sy    - c.m. energy squared,
+c bb    - impact parameter squared,
+c icdp  - diffractive state for the projectile,
+c icz   - hadron class,
+c vvx   = 1 - exp[-sum_j chi_targ(j) - sum_{i<I} chi_proj(i)]
+c vvxp  = 1 - exp[-sum_{i>I} chi^(3)_proj(i)] (iqq=1,2,3)
+c vvxp  = 1 - exp[-sum_{i>I} chi^(6)_proj(i)] (iqq=4)
+c vvxp  = 1 - exp[-sum_{i>I} chi_proj(i)]     (iqq=5-9)
+c vvxpl = 1 - exp[-sum_{i<I} chi_proj(i)]
+c iqq=1 - cut handle fan
+c iqq=2 - no rap-gap at the end
+c iqq=3 - single cut Pomeron end
+c iqq=4 - total fan-like contribution
+c iqq=5 - leg-like cut
+c iqq=6 - leg-like cut with cut handle
+c iqq=7 - single Pomeron cut
+c iqq=8 - leg-like cut with single cut Pomeron end
+c iqq=9 - leg-like cut without a rap-gap at the end
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension fann(14)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr19/ ahl(3)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      do iqq=1,9
+       fann(iqq)=0.d0
+      enddo
+      if(sy.le.sgap**2)goto 1
+
+      if(vvx.gt..999d0)then
+       vvxs=0.d0
+      else
+       vvxs=(1.d0-vvx)**2/(1.d0-vvxpl)
+      endif
+
+      do ix1=1,7
+      do mx1=1,2
+       xpomr1=(sy/sgap**2)**(-.5d0-x1(ix1)*(mx1-1.5d0))/sgap
+       rp=(rq(icdp,icz)-alfp*log(xpomr1))*4.d0*.0389d0
+       rp1=alfp*log(xpomr1*sy)*4.d0*.0389d0
+       rp2=rp*rp1/(rp+rp1)
+       do ix2=1,7
+       do mx2=1,2
+        z=.5d0+x1(ix2)*(mx2-1.5d0)
+        bb0=-rp2*log(z)
+       do ix3=1,7
+       do mx3=1,2
+        phi=pi*(.5d0+x1(ix3)*(mx3-1.5d0))
+        bb1=(dsqrt(bb)*rp1/(rp+rp1)-dsqrt(bb0)*cos(phi))**2
+     *  +bb0*sin(phi)**2
+        bb2=(dsqrt(bb)*rp/(rp+rp1)+dsqrt(bb0)*cos(phi))**2
+     *  +bb0*sin(phi)**2
+
+        vi=qgpini(xpomr1*sy,bb1,0.d0,0.d0,2)
+        vicn=min(vi,qgpini(xpomr1*sy,bb1,0.d0,0.d0,8))
+        vicgap=min(vicn,qgpini(xpomr1*sy,bb1,0.d0,0.d0,11))
+        vic1p=min(vicgap,qgpini(xpomr1*sy,bb1,0.d0,0.d0,9))
+        vic1=min(vic1p,qgpini(xpomr1*sy,bb1,0.d0,0.d0,5))
+
+        vpf=qgfani(1.d0/xpomr1,bb2,vvx,0.d0,0.d0,icdp,icz,1)
+        vpfc0=min(vpf
+     *  ,qgfani(1.d0/xpomr1,bb2,vvx,vvxp,vvxpl,icdp,icz,3))
+        vpfct=max(vpf
+     *  ,qgfani(1.d0/xpomr1,bb2,vvx,vvxp,vvxpl,icdp,icz,6))
+        vpf1p=min(vpf
+     *  ,qgfani(1.d0/xpomr1,bb2,vvx,vvxp,vvxpl,icdp,icz,7))
+        vpf1p0=min(vpf1p
+     *  ,qgfani(1.d0/xpomr1,bb2,vvx,vvxp,vvxpl,icdp,icz,8))
+        vpfc1=min(vpf1p0
+     *  ,qgfani(1.d0/xpomr1,bb2,vvx,vvxp,vvxpl,icdp,icz,9))
+        do iqq=1,9
+         if(iqq.eq.1)then      !cut handle
+          dpx=(1.d0-exp(-vi))
+     *    *(vvxs*(min(0.d0,1.d0-exp(-vpfc0)-vpfc0)
+     *    +vvxp*(exp(-vpfc0)-exp(-vpf)))+vpfc0*(vvxs-1.d0))
+         elseif(iqq.eq.2)then  !no rap-gap at the end
+          dpx=(.5d0*max(0.d0,1.d0-exp(-2.d0*vicn)*(1.d0+2.d0*vicn))
+     *    +vicgap*exp(-2.d0*vicn))
+     *    *(vvxs*(min(0.d0,1.d0-exp(-vpfc0)-vpfc0)
+     *    +vvxp*(exp(-vpfc0)-exp(-vpf)))+vpfc0*(vvxs-1.d0))
+         elseif(iqq.eq.3)then  !single cut Pomeron end
+          dpx=vic1p*exp(-2.d0*vicn)
+     *    *(vvxs*(min(0.d0,1.d0-exp(-vpfc0)-vpfc0)
+     *    +vvxp*(exp(-vpfc0)-exp(-vpf)))+vpfc0*(vvxs-1.d0))
+         elseif(iqq.eq.4)then  !total fan-like contribution
+          dpx=(1.d0-exp(-vi))
+     *    *((1.d0-vvxpl)*(min(0.d0,1.d0-exp(-vpfct)-vpfct)
+     *    +vvxp*(exp(-vpfct)-exp(-vpf)))-vpfct*vvxpl)
+         elseif(iqq.eq.5)then  !leg-like cut
+          dpx=(1.d0-exp(-vi))*vpf1p
+     *    *((1.d0-vvx)*(1.d0-vvxpl)*(1.d0-vvxp)**2*exp(-2.d0*vpf)-1.d0)
+         elseif(iqq.eq.6)then  !leg-like cut with cut handle
+          dpx=(1.d0-exp(-vi))
+     *    *(vpf1p0*((1.d0-vvx)**2*(1.d0-vvxp)**2*exp(-2.d0*vpf)-1.d0)
+     *    -(vpf1p-vpf1p0)*vvxs*(1.d0-vvxp)*exp(-vpf)
+     *    *(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpf)))
+         elseif(iqq.eq.7)then  !single Pomeron cut
+          dpx=vic1*exp(-2.d0*vicn)
+     *    *vpfc1*((1.d0-vvx)**2*(1.d0-vvxp)**2*exp(-2.d0*vpf)-1.d0)
+         elseif(iqq.eq.8)then  !leg-like cut with single cut Pomeron end
+          dpx=vic1p*exp(-2.d0*vicn)
+     *    *(vpf1p0*((1.d0-vvx)**2*(1.d0-vvxp)**2*exp(-2.d0*vpf)-1.d0)
+     *    -(vpf1p-vpf1p0)*vvxs*(1.d0-vvxp)*exp(-vpf)
+     *    *(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpf)))
+         elseif(iqq.eq.9)then  !leg-like cut without a rap-gap at the end
+          dpx=(.5d0*max(0.d0,1.d0-exp(-2.d0*vicn)*(1.d0+2.d0*vicn))
+     *    +vicgap*exp(-2.d0*vicn))
+     *    *(vpf1p0*((1.d0-vvx)**2*(1.d0-vvxp)**2*exp(-2.d0*vpf)-1.d0)
+     *    -(vpf1p-vpf1p0)*vvxs*(1.d0-vvxp)*exp(-vpf)
+     *    *(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpf)))
+         else
+          dpx=0.d0
+         endif
+         fann(iqq)=fann(iqq)+a1(ix1)*a1(ix2)*a1(ix3)*dpx/z*rp2
+        enddo
+       enddo
+       enddo
+       enddo
+       enddo
+      enddo
+      enddo
+1     continue
+      dfan=qglegi(sy,bb,icdp,icz,2)
+      dfangap=min(dfan,qglegi(sy,bb,icdp,icz,7))
+      dfan1p=min(dfangap,qglegi(sy,bb,icdp,icz,6))
+      dfanc1=min(dfan1p,qglegi(sy,bb,icdp,icz,4))
+      do iqq=1,9
+       fann(iqq)=fann(iqq)*dlog(sy/sgap**2)/8.d0*pi*r3p/.0389d0/g3p**3
+       if(iqq.eq.2.or.iqq.eq.9)then
+        fann(iqq)=fann(iqq)+dfangap
+       elseif(iqq.eq.3.or.iqq.eq.8)then
+        fann(iqq)=fann(iqq)+dfan1p
+       elseif(iqq.eq.7)then
+        fann(iqq)=fann(iqq)+dfanc1
+       else
+        fann(iqq)=fann(iqq)+dfan
+       endif
+       fann(iqq)=fann(iqq)
+     * /(1.d0-(1.d0-(1.d0-1.d0/sy)**(1.+ahl(icz)))**(1.+dels))
+      enddo
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgfani(sy,bb,vvx,vvxp,vvxpl
+     *,icdp,icz,iqq)
+c-----------------------------------------------------------------------
+c qgfani - integrated fan-contributions
+c sy   - c.m. energy squared,
+c bb   - impact parameter squared,
+c icdp - diffractive state for the projectile,
+c icz  - hadron class,
+c vvx   = 1 - exp[-sum_j chi_targ(j) - sum_{i<I} chi_proj(i)]
+c vvxp=vvxpl=0                                (iqq=1,2)
+c vvxp  = 1 - exp[-sum_{i>I} chi^(3)_proj(i)] (iqq=3,4,5)
+c vvxp  = 1 - exp[-sum_{i>I} chi^(6)_proj(i)] (iqq=6)
+c vvxp  = 1 - exp[-sum_{i>I} chi_proj(i)]     (iqq=7-11)
+c vvxpl = 1 - exp[-sum_{i<I} chi_proj(i)]
+c uncut fans:
+c iqq=1  - general fan with loops
+c iqq=2  - general fan with single pomeron end
+c cut fans:
+c iqq=3  - cut handle fan
+c iqq=4  - no rap-gap at the end
+c iqq=5  - single cut Pomeron end
+c iqq=6  - total fan-like contribution
+c iqq=7  - leg-like cut
+c iqq=8  - leg-like cut with cut handle
+c iqq=9  - single Pomeron cut
+c iqq=10 - leg-like cut with single cut Pomeron end
+c iqq=11 - leg-like cut without a rap-gap at the end
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wk(3),wz(3),wj(3),wi(3),wn(3)
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr19/ ahl(3)
+      common /qgarr20/ spmax
+      common /qgarr27/ qlegi(51,11,2,3,7),qfanu(51,11,11,6,2)
+     *,qfanc(51,11,11,39,18),qdfan(21,11,11,2,3),qrev(11,11,66,219,2)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      qgfani=0.d0
+      fanm=0.d0
+
+      if(sy.le.sgap**2)then
+       qgfani=qglegi(sy,bb,icdp,icz,1)
+       return
+      endif
+
+      rp=(rq(icdp,icz)+alfp*dlog(sy))*4.d0*.0389d0
+      z=dexp(-bb/rp)
+      if(z.gt..2d0)then
+       zz=5.d0*z+6.d0
+      else
+       zz=(-bb/rp-dlog(0.2d0))/2.d0+7.d0
+      endif
+      jz=min(9,int(zz))
+      jz=max(1,jz)
+      if(zz.lt.1.d0)then
+       wz(2)=zz-jz
+       wz(1)=1.d0-wz(2)
+       izmax=2
+      else
+       if(jz.eq.6)jz=5
+       wz(2)=zz-jz
+       wz(3)=wz(2)*(wz(2)-1.d0)*.5d0
+       wz(1)=1.d0-wz(2)+wz(3)
+       wz(2)=wz(2)-2.d0*wz(3)
+       izmax=3
+      endif
+
+      yl=dlog(sy/sgap)/dlog(spmax/sgap)*50.d0+1.d0
+      k=max(1,int(1.00001d0*yl-1.d0))
+      k=min(k,49)
+      wk(2)=yl-k
+      if(yl.le.2.d0)then
+       iymax=2
+       wk(1)=1.d0-wk(2)
+      else
+       wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+       wk(1)=1.d0-wk(2)+wk(3)
+       wk(2)=wk(2)-2.d0*wk(3)
+       iymax=3
+      endif
+
+      vl=max(1.d0,vvx*10.d0+1.d0)
+      if(vvx.eq.0.d0)then
+       ivmax=1
+       j=1
+       wj(1)=1.d0
+      else
+       j=min(int(vl),9)
+       wj(2)=vl-dble(j)
+       wj(3)=wj(2)*(wj(2)-1.d0)*.5d0
+       wj(1)=1.d0-wj(2)+wj(3)
+       wj(2)=wj(2)-2.d0*wj(3)
+       ivmax=3
+      endif
+
+      if(iqq.le.2)then
+       ii=icdp+2*(icz-1)
+       do j1=1,ivmax
+        j2=j+j1-1
+       do l1=1,izmax
+        l2=jz+l1-1
+       do k1=1,iymax
+        k2=k+k1-1
+        qgfani=qgfani+qfanu(k2,l2,j2,ii,iqq)
+     *  *wk(k1)*wz(l1)*wj(j1)
+       enddo
+       enddo
+       enddo
+       if(zz.lt.1.d0)then
+        do j1=1,ivmax
+         j2=j+j1-1
+        do k1=1,iymax
+         k2=k+k1-1
+         fanm=fanm+qfanu(k2,1,j2,ii,iqq)*wk(k1)*wj(j1)
+        enddo
+        enddo
+        qgfani=min(qgfani,fanm)
+       endif
+
+      elseif(icz.ne.2.or.vvxp+vvxpl.eq.0.d0)then  !hadron (no proj. nucl. corr.)
+       ii=icdp+2*(iqq-3)
+       ll=icz+(icz-1)*(3-icz)*2
+       do j1=1,ivmax
+        j2=j+j1-1
+       do l1=1,izmax
+        l2=jz+l1-1
+       do k1=1,iymax
+        k2=k+k1-1
+        qgfani=qgfani+qfanc(k2,l2,j2,ll,ii)*wk(k1)*wz(l1)*wj(j1)
+       enddo
+       enddo
+       enddo
+       if(zz.lt.1.d0)then
+        do j1=1,ivmax
+         j2=j+j1-1
+        do k1=1,iymax
+         k2=k+k1-1
+         fanm=fanm+qfanc(k2,1,j2,ll,ii)*wk(k1)*wj(j1)
+        enddo
+        enddo
+        qgfani=min(qgfani,fanm)
+       endif
+
+      else
+       iv1max=2
+       vl1=max(1.d0,vvxp*5.d0+1.d0)
+       i=min(int(vl1),5)
+       wi(2)=vl1-i
+       wi(1)=1.d0-wi(2)
+
+       if(vvx.lt..01d0)then                 !weak (no) screening
+        iv2max=1
+         n=1
+        wn(1)=1.d0
+       else                                    !nuclear effects
+        iv2max=2
+        vl2=max(1.d0,vvxpl/vvx*5.d0+1.d0)
+        n=min(int(vl2),5)
+        wn(2)=vl2-n
+        wn(1)=1.d0-wn(2)
+       endif
+
+       ii=icdp+2*(iqq-3)
+       do n1=1,iv2max
+        n2=n+n1-2
+       do i1=1,iv1max
+        i2=i+i1+2
+       do j1=1,ivmax
+        j2=j+j1-1
+       do l1=1,izmax
+        l2=jz+l1-1
+       do k1=1,iymax
+        k2=k+k1-1
+        qgfani=qgfani+qfanc(k2,l2,j2,i2+6*n2,ii)
+     *  *wk(k1)*wz(l1)*wj(j1)*wi(i1)*wn(n1)
+       enddo
+       enddo
+       enddo
+       enddo
+       enddo
+       if(zz.lt.1.d0)then
+        do n1=1,iv2max
+         n2=n+n1-2
+        do i1=1,iv1max
+         i2=i+i1+2
+        do j1=1,ivmax
+         j2=j+j1-1
+        do k1=1,iymax
+         k2=k+k1-1
+         fanm=fanm+qfanc(k2,1,j2,i2+6*n2,ii)
+     *   *wk(k1)*wj(j1)*wi(i1)*wn(n1)
+        enddo
+        enddo
+        enddo
+        enddo
+        qgfani=min(qgfani,fanm)
+       endif
+      endif
+      qgfani=dexp(qgfani)*z
+     **(1.d0-(1.d0-(1.d0-1.d0/sy)**(1.+ahl(icz)))**(1.+dels))
+      return
+      end
+
+c------------------------------------------------------------------------
+      subroutine qgdfan(xpomr,xpomr1,bb,icdp,fann,nn)
+c-----------------------------------------------------------------------
+c qgdfan - diffractive fans
+c xpomr - pomeron lc momentum,
+c xpomr1 - rapgap,
+c bb    - impact parameter squared,
+c icdp - diffractive state for the projectile,
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension fann(14),dps(3)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr19/ ahl(3)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      icz=2
+      do iqq=1,3
+       fann(iqq)=0.d0
+      enddo
+
+      rp=(rq(icdp,icz)-alfp*log(xpomr1))*2.d0*.0389d0
+      rp1=alfp*log(xpomr1/xpomr)*4.d0*.0389d0
+      rp2=rp*rp1/(rp+rp1)
+      do ix2=1,7
+      do mx2=1,2
+       z=.5d0+x1(ix2)*(mx2-1.5d0)
+       bb0=-rp2*log(z)
+      do ix3=1,7
+      do mx3=1,2
+       phi=pi*(.5d0+x1(ix3)*(mx3-1.5d0))
+       bb1=(dsqrt(bb)*rp1/(rp+rp1)-dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+       bb2=(dsqrt(bb)*rp/(rp+rp1)+dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+
+       vpf=qgfani(1.d0/xpomr1,bb2,0.d0,0.d0,0.d0,icdp,icz,1)
+       v1i1=qgpini(xpomr1/xpomr,bb1,0.d0,0.d0,3)
+       v1i=min(v1i1,qgpini(xpomr1/xpomr,bb1,0.d0,0.d0,2))
+
+       do iqq=1,2
+        if(iqq.eq.1)then
+         dpx=(1.d0-exp(-v1i))*(1.d0-exp(-vpf))**2
+        else
+         dpx=v1i1*(1.d0-exp(-vpf))**2
+        endif
+        fann(iqq)=fann(iqq)+a1(ix2)*a1(ix3)*dpx/z*rp2
+       enddo
+      enddo
+      enddo
+      enddo
+      enddo
+
+      do ix1=1,7
+      do mx1=1,2
+       xpomr2=xpomr1*(xpomr/xpomr1*sgap)**(.5d0+x1(ix1)*(mx1-1.5d0))
+       rp=(rq(icdp,icz)-alfp*log(xpomr2))*2.d0*.0389d0
+       rp1=alfp*log(xpomr2/xpomr)*4.d0*.0389d0
+       rp2=rp*rp1/(rp+rp1)
+       do ix2=1,7
+       do mx2=1,2
+        z=.5d0+x1(ix2)*(mx2-1.5d0)
+        bb0=-rp2*log(z)
+       do ix3=1,7
+       do mx3=1,2
+        phi=pi*(.5d0+x1(ix3)*(mx3-1.5d0))
+        bb1=(dsqrt(bb)*rp1/(rp+rp1)-dsqrt(bb0)*cos(phi))**2
+     *  +bb0*sin(phi)**2
+        bb2=(dsqrt(bb)*rp/(rp+rp1)+dsqrt(bb0)*cos(phi))**2
+     *  +bb0*sin(phi)**2
+
+        vpf=qgfani(1.d0/xpomr2,bb2,0.d0,0.d0,0.d0,icdp,icz,1)
+        v1i=qgpini(xpomr2/xpomr,bb1,0.d0,0.d0,2)
+        dpx=(1.d0-exp(-v1i))*(1.d0-exp(-vpf))**2/2.d0
+        fann(3)=fann(3)+a1(ix1)*a1(ix2)*a1(ix3)*dpx/z*rp2
+       enddo
+       enddo
+       enddo
+       enddo
+      enddo
+      enddo
+      do iqq=1,3
+       fann(iqq)=fann(iqq)*(r3p*pi/.0389d0)/g3p**3/8.d0
+      enddo
+
+      if(nn.gt.1.and.xpomr1/xpomr.gt.sgap**2)then
+       do iqq=1,3
+        dps(iqq)=0.d0
+       enddo
+       do ix1=1,7
+       do mx1=1,2
+        xpomr2=xpomr1/sgap*(xpomr/xpomr1*sgap**2)
+     *  **(.5d0+x1(ix1)*(mx1-1.5d0))
+        rp=(rq(icdp,icz)-alfp*log(xpomr2))*2.d0*.0389d0
+        rp1=alfp*log(xpomr2/xpomr)*4.d0*.0389d0
+        rp2=rp*rp1/(rp+rp1)
+        do ix2=1,7
+        do mx2=1,2
+         z=.5d0+x1(ix2)*(mx2-1.5d0)
+         bb0=-rp2*log(z)
+        do ix3=1,7
+        do mx3=1,2
+         phi=pi*(.5d0+x1(ix3)*(mx3-1.5d0))
+          bb1=(dsqrt(bb)*rp1/(rp+rp1)-dsqrt(bb0)*cos(phi))**2
+     *   +bb0*sin(phi)**2
+         bb2=(dsqrt(bb)*rp/(rp+rp1)+dsqrt(bb0)*cos(phi))**2
+     *   +bb0*sin(phi)**2
+
+         vpf=qgfani(1.d0/xpomr2,bb2,0.d0,0.d0,0.d0,icdp,icz,1)
+         v1i1=qgpini(xpomr2/xpomr,bb1,0.d0,0.d0,3)
+         v1i=min(v1i1,qgpini(xpomr2/xpomr,bb1,0.d0,0.d0,2))
+         vpdf=qgdfani(xpomr2,xpomr1,bb2,icdp,1)
+         vpdfi=qgdfani(xpomr2,xpomr1,bb2,icdp,3)
+         do iqq=1,3
+          if(iqq.eq.1)then
+           dpx=(1.d0-exp(-v1i))*vpdf*(exp(2.d0*(vpdfi-vpf))-1.d0)
+          elseif(iqq.eq.2)then
+           dpx=v1i1*vpdf*(exp(2.d0*(vpdfi-vpf))-1.d0)
+          elseif(iqq.eq.3)then
+           dpx=(1.d0-exp(-v1i))*((exp(2.d0*vpdfi)-1.d0)*exp(-2.d0*vpf)
+     *     -2.d0*vpdfi)/2.d0/dlog(xpomr1/xpomr/sgap)
+          endif
+          dps(iqq)=dps(iqq)+a1(ix1)*a1(ix2)*a1(ix3)*dpx/z*rp2
+         enddo
+        enddo
+        enddo
+        enddo
+        enddo
+       enddo
+       enddo
+       do iqq=1,3
+        fann(iqq)=fann(iqq)+dps(iqq)*dlog(xpomr1/xpomr/sgap**2)
+     *  *(r3p*pi/.0389d0)/g3p**3/8.d0
+       enddo
+      endif
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgdfani(xpomr,xpomr1,bb,icdp,iqq)
+c-----------------------------------------------------------------------
+c qgfani - integrated fan-contributions
+c xpomr - pomeron lc momentum,
+c xpomr1 - rapgap,
+c bb    - impact parameter squared,
+c icdp - diffractive state for the projectile,
+c icz  - hadron class
+c iqq=1 - total unintegrated,
+c iqq=2 - single end unintegrated,
+c iqq=3 - total integrated
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wk(3),wz(3),wj(3)
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr19/ ahl(3)
+      common /qgarr20/ spmax
+      common /qgarr27/ qlegi(51,11,2,3,7),qfanu(51,11,11,6,2)
+     *,qfanc(51,11,11,39,18),qdfan(21,11,11,2,3),qrev(11,11,66,219,2)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      qgdfani=0.d0
+      dfanm=0.d0
+      if(xpomr*sgap**2.gt.1.d0)return
+
+      icz=2
+      rp=(rq(icdp,icz)-alfp*dlog(xpomr))*2.d0*.0389d0
+      z=dexp(-bb/rp)
+      if(z.gt..2d0)then
+       zz=5.d0*z+6.d0
+      else
+       zz=(-bb/rp-dlog(0.2d0))/2.d0+7.d0
+      endif
+      jz=min(9,int(zz))
+      jz=max(1,jz)
+      if(zz.lt.1.d0)then
+       wz(2)=zz-jz
+       wz(1)=1.d0-wz(2)
+       izmax=2
+      else
+       if(jz.eq.6)jz=5
+       wz(2)=zz-jz
+       wz(3)=wz(2)*(wz(2)-1.d0)*.5d0
+       wz(1)=1.d0-wz(2)+wz(3)
+       wz(2)=wz(2)-2.d0*wz(3)
+       izmax=3
+      endif
+
+      if(xpomr*sgap**2.gt..9999d0)then
+       k=1
+       j=1
+       wk(1)=1.d0
+       wj(1)=1.d0
+       iymax=1
+       iy1max=1
+      else
+       yl=-dlog(xpomr*sgap**2)/dlog(1.d5/sgap**2)*20.d0+1.d0
+       k=max(1,int(1.00001d0*yl-1.d0))
+       k=min(k,19)
+       wk(2)=yl-k
+       if(yl.le.2.d0)then
+        iymax=2
+        wk(1)=1.d0-wk(2)
+       else
+        wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+        wk(1)=1.d0-wk(2)+wk(3)
+        wk(2)=wk(2)-2.d0*wk(3)
+        iymax=3
+       endif
+
+       yl1=11.d0-dlog(xpomr1*sgap)/dlog(xpomr*sgap**2)*10.d0
+       j=max(1,int(yl1))
+       j=min(j,9)
+       wj(2)=yl1-dble(j)
+       wj(3)=wj(2)*(wj(2)-1.d0)*.5d0
+       wj(1)=1.d0-wj(2)+wj(3)
+       wj(2)=wj(2)-2.d0*wj(3)
+       iy1max=3
+      endif
+
+      do l1=1,izmax
+       l2=jz+l1-1
+      do j1=1,iy1max
+       j2=j+j1-1
+      do k1=1,iymax
+       k2=k+k1-1
+       qgdfani=qgdfani+qdfan(k2,j2,l2,icdp,iqq)
+     * *wk(k1)*wz(l1)*wj(j1)
+      enddo
+      enddo
+      enddo
+      if(zz.lt.1.d0)then
+       do j1=1,iy1max
+        j2=j+j1-1
+       do k1=1,iymax
+        k2=k+k1-1
+        dfanm=dfanm+qdfan(k2,j2,1,icdp,iqq)*wk(k1)*wj(j1)
+       enddo
+       enddo
+       qgdfani=min(qgdfani,dfanm)
+      endif
+      qgdfani=dexp(qgdfani)*z
+      if(iqq.eq.3)qgdfani=qgdfani*max(0.d0,dlog(xpomr1/xpomr/sgap))
+      return
+      end
+
+c=============================================================================
+      double precision function qg3pom(sy,b,vvx,vvxp,vvxt
+     *,icdp,icdt,icz)
+c-----------------------------------------------------------------------
+c qg3pom - integrated 3p-contributions to the interaction eikonal
+c sy   - pomeron mass squared,
+c b    - impact parameter,
+c icdp - diffractive state for the projectile,
+c icdt - diffractive state for the target,
+c icz  - hadron class
+c vvx  = 1 - exp[-sum_{j<J} chi_targ(j) - sum_{i<I} chi_proj(i)]
+c vvxp = 1 - exp[-sum_{i>I} chi_proj(i)]
+c vvxt = 1 - exp[-sum_{j>J} chi_targ(j)]
+c------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgdebug/  debug
+      common /qgarr43/ moniou
+      common /arr3/   x1(7),a1(7)
+
+      qg3pom=0.d0
+      if(sy.le.sgap**2)return
+
+      do ix1=1,7
+      do mx1=1,2
+       xpomr1=(sy/sgap**2)**(-(.5+x1(ix1)*(mx1-1.5)))/sgap
+       rp1=(rq(icdp,icz)-alfp*log(xpomr1))*4.d0*.0389d0
+       rp2=(rq(icdt,2)+alfp*log(xpomr1*sy))*4.d0*.0389d0
+       rp=rp1*rp2/(rp1+rp2)
+      do ib1=1,7
+      do mb1=1,2
+       z=.5d0+x1(ib1)*(mb1-1.5d0)
+       bb0=-rp*dlog(z)
+      do ib2=1,7
+      do mb2=1,2
+       phi=pi*(.5d0+x1(ib2)*(mb2-1.5d0))
+       bb1=(b*rp1/(rp1+rp2)+dsqrt(bb0)*cos(phi))**2+bb0*sin(phi)**2
+       bb2=(b*rp2/(rp1+rp2)-dsqrt(bb0)*cos(phi))**2+bb0*sin(phi)**2
+
+       v1p0=qglegi(1.d0/xpomr1,bb1,icdp,icz,1)
+       v1t0=qglegi(xpomr1*sy,bb2,icdt,2,1)
+       v1p1=min(v1p0,qglegi(1.d0/xpomr1,bb1,icdp,icz,3))
+       v1t1=min(v1t0,qglegi(xpomr1*sy,bb2,icdt,2,3))
+       v1p=min(v1p1,qglegi(1.d0/xpomr1,bb1,icdp,icz,2))
+       v1t=min(v1t1,qglegi(xpomr1*sy,bb2,icdt,2,2))
+
+       vpf0=min(v1p,qgfani(1.d0/xpomr1,bb1
+     * ,1.d0-(1.d0-vvx)*(1.d0-vvxt),0.d0,0.d0,icdp,icz,1))
+       vtf0=min(v1t,qgfani(xpomr1*sy,bb2
+     * ,1.d0-(1.d0-vvx)*(1.d0-vvxp),0.d0,0.d0,icdt,2,1))
+
+       n=1
+1      n=n+1
+       vpf=qgfani(1.d0/xpomr1,bb1
+     * ,1.d0-(1.d0-vvx)*(1.d0-vvxt)*exp(-vtf0),0.d0,0.d0,icdp,icz,1)
+       vtf=qgfani(xpomr1*sy,bb2
+     * ,1.d0-(1.d0-vvx)*(1.d0-vvxp)*exp(-vpf0),0.d0,0.d0,icdt,2,1)
+       if(abs(1.d0-vpf/vpf0)+abs(1.d0-vtf/vtf0).gt.1.d-2.and.n.lt.100)
+     * then
+        vpf0=vpf
+        vtf0=vtf
+        goto 1
+       endif
+
+       dpx=(1.d0-vvx)*(min(0.d0,1.d0-exp(-vpf)-vpf)
+     * *min(0.d0,1.d0-exp(-vtf)-vtf)
+     * +vpf*min(0.d0,1.d0-exp(-vtf)-vtf)
+     * +vtf*min(0.d0,1.d0-exp(-vpf)-vpf))-vvx*vpf*vtf
+     * -.5d0*(vtf-v1t)*(min(0.d0,1.d0-exp(-vpf)-vpf)
+     * *(1.d0-vvx)*(1.d0-vvxt)*exp(-vtf)
+     * -vpf*(1.d0-(1.d0-vvx)*(1.d0-vvxt)*exp(-vtf)))
+     * -.5d0*(vpf-v1p)*(min(0.d0,1.d0-exp(-vtf)-vtf)
+     * *(1.d0-vvx)*(1.d0-vvxp)*exp(-vpf)
+     * -vtf*(1.d0-(1.d0-vvx)*(1.d0-vvxp)*exp(-vpf)))
+     * +.5d0*(v1t-v1t1)*v1p0+.5d0*(v1p-v1p1)*v1t0
+       dpx=min(1.d0,dpx)
+
+       qg3pom=qg3pom+a1(ib1)*a1(ib2)*a1(ix1)/z*rp*dpx
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      qg3pom=qg3pom/8.d0*log(sy/sgap**2)*(r3p*pi/.0389d0)/g3p**3
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgpcut(sy,b,vvx,vvxp,vvxt
+     *,icdp,icdt,icz)
+c-----------------------------------------------------------------------
+c qglool - integrated Pomeron leg eikonal with loops
+c sy   - pomeron mass squared,
+c bb   - impact parameter squared,
+c vvx  = 1 - exp[-sum_{j<J} chi_targ(j) - sum_{i<I} chi_proj(i)]
+c vvxp = 1 - exp[-sum_{i>I} chi_proj(i)]
+c vvxt = 1 - exp[-sum_{j>J} chi_targ(j)]
+c icz  - hadron class
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr19/ ahl(3)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      qgpcut=0.d0
+      if(sy.le.sgap**2)return
+
+      do ix1=1,7
+      do mx1=1,2
+       xpomr1=(sy/sgap**2)**(-(.5+x1(ix1)*(mx1-1.5)))/sgap
+       rp1=(rq(icdp,icz)-alfp*log(xpomr1))*4.d0*.0389d0
+       rp2=(rq(icdt,2)+alfp*log(xpomr1*sy))*4.d0*.0389d0
+       rp=rp1*rp2/(rp1+rp2)
+      do ib1=1,7
+      do mb1=1,2
+       z=.5d0+x1(ib1)*(mb1-1.5d0)
+       bb0=-rp*dlog(z)
+      do ib2=1,7
+      do mb2=1,2
+       phi=pi*(.5d0+x1(ib2)*(mb2-1.5d0))
+       bb1=(b*rp1/(rp1+rp2)+dsqrt(bb0)*cos(phi))**2+bb0*sin(phi)**2
+       bb2=(b*rp2/(rp1+rp2)-dsqrt(bb0)*cos(phi))**2+bb0*sin(phi)**2
+
+       vpl=qglegi(1.d0/xpomr1,bb1,icdp,icz,1)
+       vtl=qglegi(xpomr1*sy,bb2,icdt,2,1)
+       vpf0=qgfani(1.d0/xpomr1,bb1,1.d0-(1.d0-vvx)*(1.d0-vvxt)
+     * ,0.d0,0.d0,icdp,icz,1)
+       vtf0=qgfani(xpomr1*sy,bb2,1.d0-(1.d0-vvx)*(1.d0-vvxp)
+     * ,0.d0,0.d0,icdt,2,1)
+
+       n=1
+1      n=n+1
+       vpf=qgfani(1.d0/xpomr1,bb1,1.d0-(1.d0-vvx)*(1.d0-vvxt)
+     * *exp(-vtf0),0.d0,0.d0,icdp,icz,1)
+       vtf=qgfani(xpomr1*sy,bb2,1.d0-(1.d0-vvx)*(1.d0-vvxp)*exp(-vpf0)
+     * ,0.d0,0.d0,icdt,2,1)
+       if(abs(1.d0-vpf/vpf0)+abs(1.d0-vtf/vtf0).gt.1.d-2.and.n.lt.100)
+     * then
+        vpf0=vpf
+        vtf0=vtf
+        goto 1
+       endif
+
+       vpls=qgfani(1.d0/xpomr1,bb1,1.d0-(1.d0-vvx)*(1.d0-vvxt)
+     * *exp(-vtf),vvxp,0.d0,icdp,icz,9)
+       vtls=qgfani(xpomr1*sy,bb2,1.d0-(1.d0-vvx)*(1.d0-vvxp)*exp(-vpf)
+     * ,vvxt,0.d0,icdt,2,9)
+       vploop0=qglegi(1.d0/xpomr1,bb1,icdp,icz,5)
+       vploop=min(vploop0,qglegi(1.d0/xpomr1,bb1,icdp,icz,4))
+       vtloop0=qglegi(xpomr1*sy,bb2,icdt,2,5)
+       vtloop=min(vtloop0,qglegi(xpomr1*sy,bb2,icdt,2,4))
+
+       dpx=(vpls*vtloop+vtls*vploop)*(((1.d0-vvx)*(1.d0-vvxp)
+     * *(1.d0-vvxt))**2*exp(-2.d0*vpf-2.d0*vtf)-1.d0)
+     * +vpl*(vtloop-vtloop0)+vtl*(vploop-vploop0)
+
+       qgpcut=qgpcut+a1(ib1)*a1(ib2)*a1(ix1)/z*rp*dpx
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      qgpcut=qgpcut/16.d0*log(sy/sgap**2)*(r3p*pi/.0389d0)/g3p**3
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgpomi(sy,bb,vvx,vvxp,vvxt
+     *,icdp,icdt,icz,iqq)
+c-----------------------------------------------------------------------
+c qgpomi - integrated  eikonal contributions
+c sy   - pomeron mass squared,
+c bb   - impact parameter squared,
+c icdp - diffractive state for the projectile,
+c icdt - diffractive state for the target,
+c icz  - projectile class
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wk(3),wz(3),wi(3),wj(3),wm(3)
+      common /qgarr10/ am(7),ammu
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr24/ qpomr(11,11,216,12,2)
+      common /qgdebug/  debug
+      common /qgarr43/ moniou
+
+      qgpomi=0.d0
+      pomm=0.d0
+      if(cd(icdp,icz).eq.0.d0.or.cd(icdt,2).eq.0.d0)return
+
+      rp=(rq(icdp,icz)+rq(icdt,2)+alfp*log(sy))*4.d0*.0389d0
+      z=exp(-bb/rp)
+      if(z.gt..2d0)then
+       zz=5.d0*z+6.d0
+      else
+       zz=(-bb/rp-log(0.2d0))/2.d0+7.d0
+      endif
+      jz=min(9,int(zz))
+      jz=max(1,jz)
+      if(zz.lt.1.d0)then
+       wz(2)=zz-jz
+       wz(1)=1.d0-wz(2)
+       izmax=2
+      else
+       if(jz.eq.6)jz=5
+       wz(2)=zz-jz
+       wz(3)=wz(2)*(wz(2)-1.d0)*.5d0
+       wz(1)=1.d0-wz(2)+wz(3)
+       wz(2)=wz(2)-2.d0*wz(3)
+       izmax=3
+      endif
+
+      yl=dlog10((sy-am(2)**2-am(icz)**2)/2.d0/am(2))
+      k=max(1,int(yl))
+      k=min(k,9)
+      wk(2)=yl-k
+      wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+      wk(1)=1.d0-wk(2)+wk(3)
+      wk(2)=wk(2)-2.d0*wk(3)
+
+      ml=icdp+2*(icdt-1)+4*(icz-1)
+      if(vvx+vvxp+vvxt.eq.0.d0)then  !hadron-proton (no nucl. screening)
+       do l1=1,izmax
+        l2=jz+l1-1
+       do k1=1,3
+        k2=k+k1-1
+        qgpomi=qgpomi+qpomr(k2,l2,1,ml,iqq)*wk(k1)*wz(l1)
+       enddo
+       enddo
+       if(zz.lt.1.d0)then
+        do k1=1,3
+         k2=k+k1-1
+         pomm=pomm+qpomr(k2,1,1,ml,iqq)*wk(k1)
+        enddo
+        qgpomi=min(qgpomi,pomm)
+       endif
+      else
+       vl=max(1.d0,vvx*5.d0+1.d0)
+       j=min(int(vl),4)
+       wj(2)=vl-j
+       wj(3)=wj(2)*(wj(2)-1.d0)*.5d0
+       wj(1)=1.d0-wj(2)+wj(3)
+       wj(2)=wj(2)-2.d0*wj(3)
+
+       if(icz.ne.2.or.vvxp.eq.0.d0)then   !hadron-nucleus (no proj. nucl. scr.)
+        i1max=1
+        i=1
+        wi(1)=1.d0
+       else
+        i1max=3
+        vl1=max(1.d0,vvxp*5.d0+1.d0)
+        i=min(int(vl1),4)
+        wi(2)=vl1-i
+        wi(3)=wi(2)*(wi(2)-1.d0)*.5d0
+        wi(1)=1.d0-wi(2)+wi(3)
+        wi(2)=wi(2)-2.d0*wi(3)
+       endif
+
+       vl2=max(1.d0,vvxt*5.d0+1.d0)
+       m=min(int(vl2),4)
+       wm(2)=vl2-m
+       wm(3)=wm(2)*(wm(2)-1.d0)*.5d0
+       wm(1)=1.d0-wm(2)+wm(3)
+       wm(2)=wm(2)-2.d0*wm(3)
+
+       do m1=1,3
+        m2=m+m1-2
+       do i1=1,i1max
+        i2=i+i1-2
+       do j1=1,3
+        j2=j+j1-1
+        mij=j2+6*i2+36*m2
+       do l1=1,izmax
+        l2=jz+l1-1
+       do k1=1,3
+        k2=k+k1-1
+        qgpomi=qgpomi+qpomr(k2,l2,mij,ml,iqq)
+     *  *wk(k1)*wz(l1)*wj(j1)*wi(i1)*wm(m1)
+       enddo
+       enddo
+       enddo
+       enddo
+       enddo
+       if(zz.lt.1.d0)then
+        do m1=1,3
+         m2=m+m1-2
+        do i1=1,i1max
+         i2=i+i1-2
+        do j1=1,3
+         j2=j+j1-1
+         mij=j2+6*i2+36*m2
+        do k1=1,3
+         k2=k+k1-1
+         pomm=pomm+qpomr(k2,1,mij,ml,iqq)*wk(k1)*wj(j1)*wi(i1)*wm(m1)
+        enddo
+        enddo
+        enddo
+        enddo
+        qgpomi=min(qgpomi,pomm)
+       endif
+      endif
+      qgpomi=exp(qgpomi)*z
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgppdi(xp,iqq)
+c-----------------------------------------------------------------------
+c qgppdi - parton distributions in the Pomeron
+c xp    - parton LC momentum share,
+c iqq=0 - gluon
+c iqq=1 - sea quark
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr20/ spmax
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.3)write (moniou,201)xp,iqq
+      if(xp.ge..9999999d0)then
+       qgppdi=0.d0
+      else
+       if(iqq.eq.0)then                             !gluon
+        qgppdi=(1.d0-xp)**betp*(1.d0-dgqq)
+       elseif(iqq.eq.1)then                         !quark
+        qgppdi=qgftlf(xp)*dgqq
+       endif
+      endif
+      if(debug.ge.4)write (moniou,202)qgppdi
+
+201   format(2x,'qgppdi - parton distr. in the Pomeron (interpol.):'
+     */4x,'xp=',e10.3,2x,'iqq=',i1)
+202   format(2x,'qgppdi=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgvpdf(x,icz)
+c-----------------------------------------------------------------------------
+c qgvpdf - valence quark structure function
+c x   - Feinman x,
+c icz - hadron class
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr25/ ahv(3)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      qgvpdf=(qggrv(x,qt0,icz,1)+qggrv(x,qt0,icz,2))*(1.d0-x)**ahv(icz)
+      return
+      end
+
+c=============================================================================
+      double precision function qgspdf(x,icz)
+c-----------------------------------------------------------------------------
+c qgspdf - sea quark structure function
+c x   - Feinman x,
+c icz - hadron class
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      qgspdf=0.d0
+      if(x*sgap.ge.1.d0)goto 1
+
+      do icdp=1,2
+       rp=(rq(icdp,icz)-alfp*log(x))*2.d0*.0389d0
+       if(cd(icdp,icz).ne.0.d0)then
+        dps=0.d0
+        do ix=1,7
+        do mx=1,2
+         xpomr=(x*sgap)**(.5d0+x1(ix)*(mx-1.5d0))/sgap
+        do ib=1,7
+        do mb=1,2
+         z=.5d0+x1(ib)*(mb-1.5d0)
+         bb=-rp*log(z)
+
+         v1p1=qgfani(1.d0/xpomr,bb,0.d0,0.d0,0.d0,icdp,icz,2)
+         v1p=min(v1p1,qgfani(1.d0/xpomr,bb,0.d0,0.d0,0.d0,icdp,icz,1))
+         dps=dps+a1(ix)*a1(ib)*(min(0.d0,1.d0-exp(-v1p)-v1p)+v1p-v1p1)
+     *   *qgftlf(x/xpomr)*(xpomr/x)**dels/z
+        enddo
+        enddo
+        enddo
+        enddo
+        qgspdf=qgspdf-dps*dlog(x*sgap)*rp/g3p**2*pi*rr*(r3p*pi/.0389d0)
+     *  *dgqq*cc(icdp,icz)
+       endif
+      enddo
+
+1     qgspdf=qgspdf+4.*pi*rr*fp(icz)*qgftle(x,icz)/x**dels
+      return
+      end
+
+c=============================================================================
+      double precision function qggpdf(x,icz)
+c-----------------------------------------------------------------------------
+c qggpdf - gluon structure function (xg(x,qt0))
+c x   - Feinman x,
+c icz - hadron class
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      qggpdf=0.d0
+      if(x*sgap.ge.1.d0)goto 1
+
+      do icdp=1,2
+       rp=(rq(icdp,icz)-alfp*log(x))*2.d0*.0389d0
+       if(cd(icdp,icz).ne.0.d0)then
+        dps=0.d0
+        do ix=1,7
+        do mx=1,2
+         xpomr=(x*sgap)**(.5d0+x1(ix)*(mx-1.5d0))/sgap
+        do ib=1,7
+        do mb=1,2
+         z=.5d0+x1(ib)*(mb-1.5d0)
+         bb=-rp*log(z)
+
+         v1p1=qgfani(1.d0/xpomr,bb,0.d0,0.d0,0.d0,icdp,icz,2)
+         v1p=min(v1p1,qgfani(1.d0/xpomr,bb,0.d0,0.d0,0.d0,icdp,icz,1))
+         dps=dps+a1(ix)*a1(ib)*(min(0.d0,1.d0-exp(-v1p)-v1p)+v1p-v1p1)
+     *   *(1.d0-x/xpomr)**betp*(xpomr/x)**dels/z
+        enddo
+        enddo
+        enddo
+        enddo
+        qggpdf=qggpdf-dps*dlog(x*sgap)*rp/g3p**2*pi*rr*(r3p*pi/.0389d0)
+     *  *(1.d0-dgqq)*cc(icdp,icz)
+       endif
+      enddo
+
+1     qggpdf=qggpdf+4.*pi*rr*fp(icz)*qgftld(x,icz)/x**dels
+      return
+      end
+
+c=============================================================================
+      double precision function qgpdfb(x,bb,icz,jj)
+c-----------------------------------------------------------------------------
+c qgpdfb - b-dependent parton momentum distributions (xf(x,b,qt0))
+c x   - Feinman x,
+c icz - hadron class
+c jj=0 - g,
+c jj=1 - q
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      qgpdfb=0.d0
+      if(x*sgap.lt.1.d0)then
+       do icdp=1,2
+        if(cd(icdp,icz).ne.0.d0)then
+         dps=0.d0
+         do ix=1,7
+         do mx=1,2
+          xpomr=(x*sgap)**(.5d0+x1(ix)*(mx-1.5d0))/sgap
+          rp=(rq(icdp,icz)-alfp*log(xpomr))*2.d0*.0389d0
+          rp1=alfp*dlog(xpomr/x)*4.d0*.0389d0
+          rp2=rp1*rp/(rp1+rp)
+         do ix2=1,7
+         do mx2=1,2
+          bb0=-rp2*log(.5d0+x1(ix2)*(mx2-1.5d0))
+         do ix3=1,7
+         do mx3=1,2
+          phi=pi*(.5d0+x1(ix3)*(mx3-1.5d0))
+          bb1=(dsqrt(bb)*rp1/(rp+rp1)-dsqrt(bb0)*cos(phi))**2
+     *    +bb0*sin(phi)**2
+          bb2=(dsqrt(bb)*rp/(rp+rp1)+dsqrt(bb0)*cos(phi))**2
+     *    +bb0*sin(phi)**2
+
+          if(jj.eq.0)then
+           v1i=(1.d0-x/xpomr)**betp*(1.d0-dgqq)
+          else
+           v1i=qgftlf(x/xpomr)*dgqq
+          endif
+          v1p1=qgfani(1.d0/xpomr,bb2,0.d0,0.d0,0.d0,icdp,icz,2)
+          v1p=min(v1p1,qgfani(1.d0/xpomr,bb2,0.d0,0.d0,0.d0,icdp,icz,1))
+
+          dps=dps+a1(ix)*a1(ix2)*a1(ix3)*v1i
+     *    *(min(0.d0,1.d0-exp(-v1p)-v1p)+v1p-v1p1)
+     *    *(xpomr/x)**dels*rp/(rp1+rp)*exp(bb2/rp-bb/(rp1+rp))
+         enddo
+         enddo
+         enddo
+         enddo
+         enddo
+         enddo
+         qgpdfb=qgpdfb-dps*dlog(x*sgap)*pi*rr*r3p/g3p**2/.0389d0/2.d0
+     *   *cc(icdp,icz)
+        endif
+       enddo
+
+       do icdp=1,2
+        rp=(rq(icdp,icz)-alfp*dlog(x))*4.d0*.0389d0
+        if(jj.eq.0)then
+         qgpdfb=qgpdfb+4.d0*rr*fp(icz)*qgftld(x,icz)/x**dels
+     *   /rp*exp(-bb/rp)*cc(icdp,icz)
+        else
+         qgpdfb=qgpdfb+4.d0*rr*fp(icz)*qgftle(x,icz)/x**dels
+     *   /rp*exp(-bb/rp)*cc(icdp,icz)
+        endif
+       enddo
+      endif
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgpdfi(x,bb,icz,jj)
+c-----------------------------------------------------------------------
+c qgpdfi - b-dependent parton momentum distributions
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wk(3),wz(3)
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr20/ spmax
+      common /qgarr53/ qpdfb(51,11,3,2)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      qgpdfi=0.d0
+      rp=(rq(1,icz)-alfp*dlog(x))*4.d0*.0389d0
+      if(rp.le.1.d-10)then
+       z=1.d0
+      else
+       z=exp(-bb/rp)
+      endif
+      if(z.lt..2d0*exp(-10.d0))then
+       izmax=2
+       jz=1
+       wz(2)=5.d0*z*exp(10.d0)
+       wz(1)=1.d0-wz(2)
+      else
+       if(z.gt..2d0)then
+        zz=5.d0*z+6.d0
+       else
+        zz=(-bb/rp-log(0.2d0))/2.d0+7.d0
+       endif
+       jz=min(9,int(zz))
+       jz=max(2,jz)
+       if(jz.eq.6)jz=5
+       wz(2)=zz-jz
+       wz(3)=wz(2)*(wz(2)-1.d0)*.5d0
+       wz(1)=1.d0-wz(2)+wz(3)
+       wz(2)=wz(2)-2.d0*wz(3)
+       izmax=3
+      endif
+
+      yl=-dlog(x)/log(spmax)*50.d0+1.d0
+      k=max(1,int(yl))
+      k=min(k,49)
+      wk(2)=yl-k
+      wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+      wk(1)=1.d0-wk(2)+wk(3)
+      wk(2)=wk(2)-2.d0*wk(3)
+
+      do j1=1,izmax
+       j2=jz+j1-1
+      do k1=1,3
+       k2=k+k1-1
+       qgpdfi=qgpdfi+qpdfb(k2,j2,icz,jj+1)*wk(k1)*wz(j1)
+      enddo
+      enddo
+      qgpdfi=exp(qgpdfi)*z*4.d0*rr*fp(icz)/x**dels/rp
+      if(jj.eq.0)then
+       qgpdfi=qgpdfi*qgftld(x,icz)
+      else
+       qgpdfi=qgpdfi*qgftle(x,icz)
+      endif
+      return
+      end
+
+c=============================================================================
+      double precision function qgdgdf(x,xpomr,icz,jj)
+c-----------------------------------------------------------------------------
+c qgdgdf - diffractive gluon pdf xpomr*g_d^3(x,xpomr,qt0)
+c x   - Feinman x,
+c icz - hadron class
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      qgdgdf=0.d0
+      do icdp=1,2
+      if(cd(icdp,icz).ne.0.d0)then
+       dps=0.d0
+       if(jj.eq.1)then
+        rp=(rq(icdp,icz)-alfp*log(xpomr))*2.d0*.0389d0
+        do ib=1,7
+        do mb=1,2
+         z=.5d0+x1(ib)*(mb-1.5d0)
+         bb=-rp*log(z)
+
+         v1p=qgfani(1.d0/xpomr,bb,0.d0,0.d0,0.d0,icdp,icz,1)
+         dps=dps+a1(ib)*(1.d0-exp(-v1p))**2/z
+        enddo
+        enddo
+        dps=dps*rp*pi*rr*(r3p*pi/.0389d0)*cc(icdp,icz)/g3p**2
+     *  *(1.d0-x/xpomr)**betp*(1.d0-dgqq)*(xpomr/x)**dels
+
+       elseif(jj.eq.2.and.xpomr/x.gt.sgap)then
+        do ix1=1,7
+        do mx1=1,2
+         xpomr1=(x/xpomr*sgap)**(.5d0+x1(ix1)*(mx1-1.5d0))*xpomr/sgap
+         rp=(rq(icdp,icz)-alfp*log(xpomr1))*2.d0*.0389d0
+         do ib=1,7
+         do mb=1,2
+          z=.5d0+x1(ib)*(mb-1.5d0)
+          bb=-rp*log(z)
+
+          vpf=qgfani(1.d0/xpomr1,bb,0.d0,0.d0,0.d0,icdp,icz,1)
+          vpdf1=qgdfani(xpomr1,xpomr,bb,icdp,2)
+          vpdf=min(vpdf1,qgdfani(xpomr1,xpomr,bb,icdp,1))
+          vpdfi=qgdfani(xpomr1,xpomr,bb,icdp,3)
+           dpx=vpdf*exp(2.d0*vpdfi-2.d0*vpf)-vpdf1
+
+          dps=dps+a1(ix1)*a1(ib)*dpx/z*rp
+     *    *(1.d0-x/xpomr1)**betp*(xpomr1/x)**dels
+         enddo
+         enddo
+        enddo
+        enddo
+        dps=dps*rr*pi*(r3p*pi/.0389d0)*dlog(xpomr/x/sgap)/g3p**2
+     *  *(1.d0-dgqq)*cc(icdp,icz)
+       endif
+       qgdgdf=qgdgdf+dps
+      endif
+      enddo
+      return
+      end
+
+c=============================================================================
+      double precision function qgdpdf(x,xpomr,icz,jj)
+c-----------------------------------------------------------------------------
+c qgdpdf - diffractive structure function
+c x   - Feinman x,
+c icz - hadron class
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      qgdpdf=0.d0
+      do icdp=1,2
+      if(cd(icdp,icz).ne.0.d0)then
+       dps=0.d0
+       if(jj.eq.1)then
+        rp=(rq(icdp,icz)-alfp*log(xpomr))*2.d0*.0389d0
+        do ib=1,7
+        do mb=1,2
+         z=.5d0+x1(ib)*(mb-1.5d0)
+         bb=-rp*log(z)
+
+         v1p=qgfani(1.d0/xpomr,bb,0.d0,0.d0,0.d0,icdp,icz,1)
+         dps=dps+a1(ib)*(1.d0-exp(-v1p))**2/z
+        enddo
+        enddo
+        dps=dps*rp*pi*rr*(r3p*pi/.0389d0)*cc(icdp,icz)/g3p**2
+     *  *qgftlf(x/xpomr)*dgqq*(xpomr/x)**dels
+
+       elseif(jj.eq.2.and.xpomr/x.gt.sgap)then
+        do ix1=1,7
+        do mx1=1,2
+         xpomr1=(x/xpomr*sgap)**(.5d0+x1(ix1)*(mx1-1.5d0))*xpomr/sgap
+         rp=(rq(icdp,icz)-alfp*log(xpomr1))*2.d0*.0389d0
+         do ib=1,7
+         do mb=1,2
+          z=.5d0+x1(ib)*(mb-1.5d0)
+          bb=-rp*log(z)
+
+          vpf=qgfani(1.d0/xpomr1,bb,0.d0,0.d0,0.d0,icdp,icz,1)
+          vpdf1=qgdfani(xpomr1,xpomr,bb,icdp,2)
+          vpdf=min(vpdf1,qgdfani(xpomr1,xpomr,bb,icdp,1))
+          vpdfi=qgdfani(xpomr1,xpomr,bb,icdp,3)
+           dpx=vpdf*exp(2.d0*vpdfi-2.d0*vpf)-vpdf1
+
+          dps=dps+a1(ix1)*a1(ib)*dpx/z*rp
+     *    *qgftlf(x/xpomr1)*(xpomr1/x)**dels
+         enddo
+         enddo
+        enddo
+        enddo
+        dps=dps*rr*pi*(r3p*pi/.0389d0)*dlog(xpomr/x/sgap)/g3p**2
+     *  *dgqq*cc(icdp,icz)
+       endif
+       qgdpdf=qgdpdf+dps
+      endif
+      enddo
+      qgdpdf=qgdpdf/4.5d0
+      return
+      end
+
+c=============================================================================
+      double precision function qgfsh(sy,bb,icdp,icdt,icz,iqq)
+c-----------------------------------------------------------------------------
+c qgfsh - semihard interaction eikonal
+c sy  - pomeron mass squared,
+c bb  - impact parameter squared,
+c icz - hadron class
+c iqq - type of the hard interaction (0-gg, 1-q_vg, 2-gq_v)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr25/ ahv(3)
+      common /qgarr26/ factk,fqscal
+      common /arr3/   x1(7),a1(7)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)sy,bb,iqq,icz
+
+      qgfsh=0.d0
+      s2min=4.d0*fqscal*qt0
+      xmin=s2min/sy
+      if(xmin.ge.1.d0)return
+      xmin=xmin**(delh-dels)
+      if(iqq.eq.1)then
+       icv=icz
+       icq=2
+      elseif(iqq.eq.2)then
+       icv=2
+       icq=icz
+      endif
+      if(debug.ge.3)write (moniou,205)xmin,iqq
+
+c numerical integration over z1
+      do i=1,7
+      do m=1,2
+       z1=(.5d0*(1.d0+xmin-(2*m-3)*x1(i)*(1.d0-xmin)))
+     * **(1.d0/(delh-dels))
+       ww=z1*sy
+       sjqq=qgjit(qt0,qt0,ww,2,2)
+       sjqg=qgjit(qt0,qt0,ww,1,2)
+       sjgg=qgjit(qt0,qt0,ww,1,1)
+       if(debug.ge.3)write (moniou,203)ww,sjqq+sjqg+sjgg
+
+       if(iqq.eq.0)then
+        st2=0.d0
+        do j=1,7
+        do k=1,2
+         xx=.5d0*(1.d0+x1(j)*(2*k-3))
+         xp=z1**xx
+         xm=z1/xp
+         glu1=qgftld(xp,icz)
+         sea1=qgftle(xp,icz)
+         glu2=qgftld(xm,2)
+         sea2=qgftle(xm,2)
+         st2=st2+a1(j)*(glu1*glu2*sjgg+(glu1*sea2+glu2*sea1)*sjqg
+     *   +sea1*sea2*sjqq)
+        enddo
+        enddo
+        rh=rq(icdp,icz)+rq(icdt,2)-alfp*dlog(z1)
+        qgfsh=qgfsh-a1(i)*dlog(z1)/z1**delh*st2
+     *  *exp(-bb/(4.d0*.0389d0*rh))/rh
+
+       else
+        st2=0.d0
+        alh=.5d0+dels
+        xam=z1**alh
+
+        do j=1,7
+        do k=1,2
+         xp=(.5d0*(1.d0+xam+x1(j)*(2*k-3)*(1.d0-xam)))**(1.d0/alh)
+         xm=z1/xp
+         glu=qgftld(xm,icq)
+         sea=qgftle(xm,icq)
+         rh=rq(icdp,icz)+rq(icdt,2)-alfp*dlog(xm)
+
+         fst=(glu*sjqg+sea*sjqq)*(1.d0-xp)**ahv(icv)
+     *   *(qggrv(xp,qt0,icv,1)+qggrv(xp,qt0,icv,2))/dsqrt(xp)
+     *   *exp(-bb/(4.d0*.0389d0*rh))/rh
+         st2=st2+a1(j)*fst
+        enddo
+        enddo
+        st2=st2*(1.d0-xam)/alh
+        qgfsh=qgfsh+a1(i)/z1**delh*st2
+       endif
+      enddo
+      enddo
+
+      if(iqq.eq.0)then
+       qgfsh=qgfsh*rr**2*(1.d0-xmin)/(delh-dels)*fp(icz)*fp(2)*factk
+     * /2.d0*pi*cd(icdp,icz)*cd(icdt,2)
+      else
+       qgfsh=qgfsh*rr*fp(icq)*(1.d0-xmin)/(delh-dels)*factk/8.d0
+     * *cd(icdp,icz)*cd(icdt,2)
+      endif
+
+      if(debug.ge.3)write (moniou,202)qgfsh
+201   format(2x,'qgfsh - semihard interaction eikonal:'
+     */4x,'sy=',e10.3,2x,'bb=',e10.3,2x,'iqq=',i1,2x,'icz=',i1)
+202   format(2x,'qgfsh=',e10.3)
+203   format(2x,'qgfsh:',2x,'s_hard=',e10.3,2x,'sigma_hard=',e10.3)
+205   format(2x,'qgfsh:',2x,'xmin=',e10.3,2x,'iqq=',i3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgftld(z,icz)
+c-----------------------------------------------------------------------------
+c qgftld - auxilliary function for semihard eikonals calculation -
+c (proportional to gluon sf: g(z)*z^(1+dels)) -
+c integration over semihard block light cone momentum share x
+c z - x-cutoff from below,
+c icz - type of the hadron to which the semihard block is connected
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr19/ ahl(3)
+      common /qgarr43/ moniou
+      common /qgdebug/    debug
+      common /arr3/     x1(7),a1(7)
+
+      if(debug.ge.2)write (moniou,201)z,icz
+
+      qgftld=0.d0
+      xpmin=z**(1.d0+dels)
+      do i1=1,7
+      do m1=1,2
+       tp=1.d0-(1.d0-xpmin)*(.5d0+x1(i1)*(m1-1.5d0))
+     * **(1.d0/(1.d0+ahl(icz)))
+       xp=tp**(1.d0/(1.d0+dels))
+       qgftld=qgftld+a1(i1)*((1.d0-xp)/(1.d0-tp))**ahl(icz)
+     * *(1.d0-z/xp)**betp
+      enddo
+      enddo
+      qgftld=qgftld*.5d0*(1.d0-xpmin)**(ahl(icz)+1.d0)
+     */(ahl(icz)+1.d0)/(1.d0+dels)*(1.d0-dgqq)
+
+      if(debug.ge.3)write (moniou,202)qgftld
+201   format(2x,'qgftld:',2x,'z=',e10.3,2x,'icz=',i1)
+202   format(2x,'qgftld=',e10.3)
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgftle(z,icz)
+c-----------------------------------------------------------------------
+c qgftle - auxilliary function for semihard eikonals calculation
+c (proportional to sea quark sf: q_s(z)*z^(1+dels)) -
+c integration over semihard pomeron light cone momentum share x
+c z - light cone x of the quark,
+c icz - type of the hadron to which the semihard block is connected
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr19/ ahl(3)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      if(debug.ge.2)write (moniou,201)z,icz
+
+      qgftle=0.d0
+      xpmin=z**(1.d0+dels)
+      do i1=1,7
+      do m1=1,2
+       tp=1.d0-(1.d0-xpmin)*(.5d0+x1(i1)*(m1-1.5d0))
+     * **(1.d0/(1.d0+ahl(icz)))
+       xp=tp**(1.d0/(1.d0+dels))
+       qgftle=qgftle+a1(i1)*((1.d0-xp)/(1.d0-tp))**ahl(icz)
+     * *qgftlf(z/xp)
+      enddo
+      enddo
+      qgftle=qgftle*.5d0*(1.d0-xpmin)**(ahl(icz)+1.d0)
+     */(ahl(icz)+1.d0)/(1.d0+dels)*dgqq
+
+      if(debug.ge.3)write (moniou,202)qgftle
+201   format(2x,'qgftle:',2x,'z=',e10.3,2x,'icz=',i1)
+202   format(2x,'qgftle=',e10.3)
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgftlf(zz)
+c-----------------------------------------------------------------------
+c qgftlf - auxilliary function for semihard eikonals calculation
+c zz - ratio of the quark and pomeron light cone x (zz=x_G/x_P)
+c integration over quark to gluon light cone momentum ratio (z=x/x_G):
+c qgftlf=int(dz) z^dels * (1-zz/z)^betp * P_qG(z)
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      if(debug.ge.2)write (moniou,201)zz
+201   format(2x,'qgftlf:',2x,'zz=',e10.3)
+
+      qgftlf=0.d0
+      zmin=zz**(1.d0+dels)
+      do i=1,7
+      do m=1,2
+        z=(.5d0*(1.d0+zmin+(2*m-3)*x1(i)*(1.d0-zmin)))**(1.d0/
+     *  (1.d0+dels))
+        qgftlf=qgftlf+a1(i)*max(1.d-9,(1.d0-zz/z))**betp
+     *  *(z**2+(1.d0-z)**2)
+      enddo
+      enddo
+      qgftlf=qgftlf*1.5d0*(1.d0-zmin)/(1.d0+dels)   !1.5=naflav/2 at Q0
+
+      if(debug.ge.3)write (moniou,202)qgftlf
+202   format(2x,'qgftlf=',e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qgfz(b,gz,iddp1,iddp2)
+c----------------------------------------------------------------------------
+c hadron-hadron and hadron-nucleus cross sections calculation
+c----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208)
+      dimension gz(5),wt1(3),wt2(3)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr2/  scm,wp0,wm0
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr43/ moniou
+      common /arr3/   x1(7),a1(7)
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)b,iddp1,iddp2
+      do l=1,5
+       gz(l)=0.d0
+      enddo
+      rp=(rq(1,icz)+rq(1,2)+alfp*log(scm))*4.d0*.0389d0
+      g0=0.d0
+      if(ia(2).eq.1.and.iddp1.eq.0.and.iddp2.eq.0)then
+       g0=pi*rp*10.d0                     !normalization factor (in mb)
+       bm=2.d0*dsqrt(rp)                  !impact parameter for exp. fall-down
+      endif
+
+      do i1=1,7
+      do m=1,2
+       z=.5d0+x1(i1)*(m-1.5d0)
+       bb1=rp*z
+       bb2=rp*(1.d0-dlog(z))
+
+       do l=1,3
+        wt1(l)=0.d0
+        wt2(l)=0.d0
+       enddo
+
+       if(ia(2).eq.1)then
+        do idd1=1,2
+        do idd2=1,2
+         vv1=exp(-qgpomi(scm,bb1,0.d0,0.d0,0.d0,idd1,idd2,icz,1))
+         vv2=exp(-qgpomi(scm,bb2,0.d0,0.d0,0.d0,idd1,idd2,icz,1))
+
+         do l=1,2
+          wt1(l)=wt1(l)+cc(idd1,icz)*cc(idd2,2)*vv1**l
+          wt2(l)=wt2(l)+cc(idd1,icz)*cc(idd2,2)*vv2**l
+         enddo
+         do idd3=1,2
+          wt1(3)=wt1(3)+cc(idd1,icz)*cc(idd2,2)*cc(idd3,icz)*vv1
+     *    *exp(-qgpomi(scm,bb1,0.d0,0.d0,0.d0,idd3,idd2,icz,1))
+          wt2(3)=wt2(3)+cc(idd1,icz)*cc(idd2,2)*cc(idd3,icz)*vv2
+     *    *exp(-qgpomi(scm,bb2,0.d0,0.d0,0.d0,idd3,idd2,icz,1))
+         enddo
+        enddo
+        enddo
+        do l=1,2
+         gz(l)=gz(l)+a1(i1)*((1.d0-wt1(l))+(1.d0-wt2(l))/z)
+        enddo
+        gz(3)=gz(3)+a1(i1)*((wt1(2)-wt1(3))+(wt2(2)-wt2(3))/z)
+        gz(4)=gz(4)+a1(i1)*((wt1(3)-wt1(1)**2)+(wt2(3)-wt2(1)**2)/z)
+        gz(5)=gz(5)+a1(i1)*((1.d0-wt1(1))*z+(1.d0-wt2(1))/z*(1.-log(z)))
+
+       else
+        do idd1=1,2
+        do idd2=1,2
+         vv1=exp(-qgpomi(scm,bb1,0.d0,0.d0,0.d0,iddp1,idd1,icz,1)
+     *   -qgpomi(scm,bb1,0.d0,0.d0,0.d0,iddp2,idd2,icz,1))
+         vv2=exp(-qgpomi(scm,bb2,0.d0,0.d0,0.d0,iddp1,idd1,icz,1)
+     *   -qgpomi(scm,bb2,0.d0,0.d0,0.d0,iddp2,idd2,icz,1))
+
+         if(idd1.eq.idd2)then
+          wt1(1)=wt1(1)+cc(idd1,2)*vv1
+          wt2(1)=wt2(1)+cc(idd1,2)*vv2
+         endif
+         wt1(2)=wt1(2)+cc(idd1,2)*cc(idd2,2)*vv1
+         wt2(2)=wt2(2)+cc(idd1,2)*cc(idd2,2)*vv2
+        enddo
+        enddo
+        cg1=qgrot(b,dsqrt(bb1))
+        cg2=qgrot(b,dsqrt(bb2))
+        do l=1,2
+         gz(l)=gz(l)+a1(i1)*(cg1*(1.d0-wt1(l))+cg2*(1.d0-wt2(l))/z)
+        enddo
+       endif
+      enddo
+      enddo
+      if(ia(2).eq.1.and.iddp1.eq.0.and.iddp2.eq.0)then     !hadron-proton
+       do l=1,5
+        gz(l)=gz(l)*g0
+       enddo
+       gz(5)=gz(5)/gz(1)*(rq(1,icz)+rq(1,2)+alfp*log(scm))*2.d0
+      endif
+
+      if(debug.ge.2)write (moniou,203)gz
+      if(debug.ge.3)write (moniou,202)
+201   format(2x,'qgfz - hadronic cross-sections calculation'
+     */4x,'b=',e10.3,2x,'iddp=',2i3)
+202   format(2x,'qgfz - end')
+203   format(2x,'qgfz: gz=',5e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qghard(sy,bb,icdp,icdt,icz)
+c-----------------------------------------------------------------------------
+c qghard - hard quark-quark interaction cross-section
+c s - energy squared for the interaction (hadron-hadron),
+c icz - type of the primaty hadron (nucleon)
+c----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr25/ ahv(3)
+      common /qgarr26/ factk,fqscal
+      common /arr3/   x1(7),a1(7)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)sy,icz
+
+      qghard=0.d0
+      s2min=4.d0*fqscal*qt0
+      xmin=s2min/sy
+      if(xmin.ge.1.d0)return
+      xmin=xmin**(delh+.5d0)
+
+c numerical integration over z1
+      do i=1,7
+      do m=1,2
+       z1=(.5d0*(1.d0+xmin-(2*m-3)*x1(i)*(1.d0-xmin)))
+     * **(1.d0/(delh+.5d0))
+
+       st2=0.d0
+       do j=1,7
+       do k=1,2
+        xx=.5d0*(1.d0+x1(j)*(2*k-3))
+        xp=z1**xx
+        xm=z1/xp
+        st2=st2+a1(j)*(1.d0-xp)**ahv(icz)*(1.d0-xm)**ahv(2)
+     *  *(qggrv(xp,qt0,icz,1)+qggrv(xp,qt0,icz,2))
+     *  *(qggrv(xm,qt0,2,1)+qggrv(xm,qt0,2,2))/dsqrt(z1)
+       enddo
+       enddo
+       sj=qgjit(qt0,qt0,z1*sy,2,2)
+       st2=-st2*dlog(z1)*sj
+       if(debug.ge.3)write (moniou,203)z1*sy,sj
+
+       qghard=qghard+a1(i)/z1**delh*st2
+      enddo
+      enddo
+      qghard=qghard*(1.d0-xmin)/(.5d0+delh)*.25d0*factk
+      rh=rq(icdp,icz)+rq(icdt,2)
+      qghard=qghard/(8.d0*pi*rh)*exp(-bb/(4.d0*.0389d0*rh))
+     **cd(icdp,icz)*cd(icdt,2)
+
+      if(debug.ge.2)write (moniou,202)qghard
+201   format(2x,'qghard - hard quark-quark interaction eikonal:'
+     */2x,'s=',e10.3,2x,'icz=',i1)
+202   format(2x,'qghard=',e10.3)
+203   format(2x,'qghard:',2x,'s_hard=',e10.3,2x,'sigma_hard=',e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qgbdef(bba,bbb,xxa,yya,xxb,yyb,xxp,yyp,jb)
+c-----------------------------------------------------------------------
+c qgbdef - defines coordinates (xxp,yyp) of a multi-pomeron vertex
+c------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+
+      xx=xxa-xxb
+      yy=yya-yyb
+      bb=xx**2+yy**2
+      if(bb.lt.1.d-5)then
+       xxp=xxb+dsqrt(bba)
+       yyp=yyb
+      elseif(abs(yy).lt.1.d-8)then
+       xxp=(bba-bbb+xxb**2-xxa**2)/2.d0/(xxb-xxa)
+       yyp=yyb+(2*jb-3)*dsqrt(max(0.d0,bbb-(xxb-xxp)**2))
+      else
+       bbd=bb+bbb-bba
+       discr=max(0.d0,4.d0*bb*bbb-bbd**2)
+       xxp=(xx*bbd+(2*jb-3)*abs(yy)*dsqrt(discr))/2.d0/bb
+       yyp=(bbd-2.d0*xx*xxp)/2.d0/yy
+       xxp=xxp+xxb
+       yyp=yyp+yyb
+      endif
+      return
+      end
+
+c=============================================================================
+      subroutine qgv(x,y,xb,vin,vdd,vabs)
+c xxv - eikonal dependent factor for hadron-nucleus interaction
+c (used for total and diffractive hadron-nucleus cross-sections calculation)
+c----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208)
+      dimension xb(iapmax,3),vabs(2)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr2/  scm,wp0,wm0
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)x,y
+
+      vin=0.d0
+      vdd=0.d0
+      do iddp1=1,2
+       dv=0.d0
+       do m=1,ia(2)
+        bb=(x-xb(m,1))**2+(y-xb(m,2))**2
+        dv=dv+qgpomi(scm,bb,0.d0,0.d0,0.d0,iddp1,iddt(m),icz,1)
+       enddo
+       dv=exp(-dv)
+       vabs(iddp1)=1.d0-dv**2       !1-exp(-2 * chi_i)
+       vdd=vdd+cc(iddp1,icz)*dv**2  !sum_i cc(i) exp(-2 * chi_i)
+       vin=vin+cc(iddp1,icz)*dv     !sum_i cc(i) exp(-chi_i)
+      enddo
+      vin=1.d0-vin**2               !1-sum_ij cc(i) cc(j) exp(-chi_i-chi_j)
+      vdd=vdd+vin-1.d0
+          !sum_i cc(i) exp(-2*chi_i) - sum_ij cc(i) cc(j) exp(-chi_i-chi_j)
+
+      if(debug.ge.3)write (moniou,202)vin,vdd,vabs
+201   format(2x,'qgv - eikonal factor: nucleon coordinates x='
+     *  ,e10.3,2x,'y=',e10.3)
+202   format(2x,'vin=',e10.3,2x,'vdd=',e10.3,2x,'vabs=',2e10.3)
+      return
+      end
+
+
+c=============================================================================
+      subroutine qgfdf(xxp,yyp,xpomr,vpac,vtac,vvx,vvxp,vvxt
+     *  ,vvxpl,vvxtl,ip,it)
+c-----------------------------------------------------------------------
+c qgfdf - configuration of fan contributions (cut and uncut fans)
+c xxp, yyp -  coordinates of the multi-Pomeron vertex,
+c xpomr    - LC momentum share of the multi-Pomeron vertex,
+c ip       - proj. index,
+c it       - targ. index
+c vvx   = 1 - exp[-sum_{j<J} chi_targ(j) - sum_{i<I} chi_proj(i)]
+c vvxp  = 1 - exp[-sum_{i>I} chi_proj(i)]
+c vvxt  = 1 - exp[-sum_{j>J} chi_targ(j)]
+c vvxpl = 1 - exp[-sum_{i<I} chi_proj(i)]
+c vvxtl = 1 - exp[-sum_{j<J} chi_targ(j)]
+c------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208)
+      dimension vpac(iapmax),vtac(iapmax)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr2/  scm,wp0,wm0
+      common /qgarr7/  xa(iapmax,3),xb(iapmax,3),b
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr43/ moniou
+      common /qgarr46/ iconab(iapmax,iapmax),icona(iapmax)
+     *,iconb(iapmax)
+      common /qgdebug/   debug
+
+      if(debug.ge.3)write (moniou,201)xxp,yyp,xpomr,ip,it
+
+      vvx=0.d0
+      vvxp=0.d0
+      vvxt=0.d0
+      vvxpl=0.d0
+      vvxtl=0.d0
+      if(scm.le.sgap**2)return
+
+      sumup0=0.d0                      !proj. fans without targ. screening
+      do ipp=1,ia(1)
+       if(iconab(ipp,it).eq.0)then    !no connection
+                                      !(nucleon too far from the vertex)
+        vpac(ipp)=0.d0
+       else
+        bbp=(xa(ipp,1)+b-xxp)**2+(xa(ipp,2)-yyp)**2
+        vpac(ipp)=qgfani(1.d0/xpomr,bbp,1.d0-exp(-sumup0),0.d0,0.d0
+     *  ,iddp(ipp),icz,1)
+        sumup0=sumup0+vpac(ipp)
+       endif
+      enddo
+
+      sumut0=0.d0                      !targ. fans without proj. screening
+      do itt=1,ia(2)
+       if(iconab(ip,itt).eq.0)then     !no connection
+        vtac(itt)=0.d0
+       else
+        bbt=(xb(itt,1)-xxp)**2+(xb(itt,2)-yyp)**2
+        vtac(itt)=qgfani(xpomr*scm,bbt,1.d0-exp(-sumut0),0.d0,0.d0
+     *  ,iddt(itt),2,1)
+        sumut0=sumut0+vtac(itt)
+       endif
+      enddo
+
+      nn=0
+1     nn=nn+1
+      sumup=0.d0                       !proj. fans with targ. screening
+      do ipp=1,ia(1)
+       if(iconab(ipp,it).eq.0)then    !no connection
+        vpac(ipp)=0.d0
+       else
+        bbp=(xa(ipp,1)+b-xxp)**2+(xa(ipp,2)-yyp)**2
+        vpac(ipp)=qgfani(1.d0/xpomr,bbp,1.d0-exp(-sumup-sumut0)
+     *  ,0.d0,0.d0,iddp(ipp),icz,1)
+        sumup=sumup+vpac(ipp)
+       endif
+      enddo
+
+      sumut=0.d0                      !targ. uncut fans with proj. screening
+      do itt=1,ia(2)
+       if(iconab(ip,itt).eq.0)then
+        vtac(itt)=0.d0
+       else
+        bbt=(xb(itt,1)-xxp)**2+(xb(itt,2)-yyp)**2
+        vtac(itt)=qgfani(xpomr*scm,bbt,1.d0-exp(-sumut-sumup0)
+     *  ,0.d0,0.d0,iddt(itt),2,1)
+        sumut=sumut+vtac(itt)
+       endif
+      enddo
+
+      if((abs(sumup-sumup0).gt..01d0.or.abs(sumut-sumut0).gt..01d0)
+     *.and.nn.lt.100)then
+       sumup0=sumup
+       sumut0=sumut
+       goto 1
+      endif
+
+      if(ia(1).gt.1)then
+       do ipp=1,ia(1)
+        if(ipp.lt.ip)then
+         vvxpl=vvxpl+vpac(ipp)
+        elseif(ipp.gt.ip)then
+         vvxp=vvxp+vpac(ipp)
+        endif
+       enddo
+      endif
+
+      if(ia(2).gt.1)then
+       do itt=1,ia(2)
+        if(itt.lt.it)then
+         vvxtl=vvxtl+vtac(itt)
+        elseif(itt.gt.it)then
+         vvxt=vvxt+vtac(itt)
+        endif
+       enddo
+      endif
+      vvx=1.d0-exp(-vvxpl-vvxtl)
+      vvxp=1.d0-exp(-vvxp)
+      vvxpl=1.d0-exp(-vvxpl)
+      vvxt=1.d0-exp(-vvxt)
+      vvxtl=1.d0-exp(-vvxtl)
+      if(debug.ge.4)write (moniou,202)
+
+201   format(2x,'qgfdf - configuration of fan contributions:'
+     */2x,'xxp=',e10.3,2x,'yyp=',e10.3,2x,'xpomr=',e10.3
+     *,2x,'ip=',i3,2x,'it=',i3)
+202   format(2x,'qgfdf - end')
+      return
+      end
+
+c=============================================================================
+      subroutine qgconf
+c-----------------------------------------------------------------------------
+c interaction (cut Pomeron) configuration:
+c b - impact parameter,
+c xa(1-iap,3), xb(1-iat,3) - proj. and targ. nucleon coordinates,
+c iddp(1-iap), iddt(1-iat) - proj. and targ. nucleon diffractive eigenstates,
+c icona(1-iap) - connection for proj. nucleons (0 if too far from the target),
+c iconab(1-iap,1-iat) - connection for proj.-targ. nucleons (0 if too far from
+c each other),
+c nwp, nwt - numbers of wounded proj. and targ. nucleons (inelastic or diff.),
+c iwp(1-iap), iwt(1-iat) - indexes for wounded proj. and targ. nucleons
+c (0 - intact, 1 - inel., 2,3 - diffr., -1 - recoiled from diffraction),
+c ncola(1-iap), ncolb(1-iat) - index for inel.-wounded proj. and targ. nucleons,
+c nbpom  - total number of Pomeron blocks,
+c ias(k) (ibs(k)) - index of the proj. (targ.) nucleon for k-th Pomeron block,
+c bbpom(k) - squared impact parameter (between proj. and targ.) for k-th block,
+c vvxpom(k) - relative strenth of A-screening corrections for k-th block,
+c nqs(k) - number of single Pomerons in k-th block (without cut 3P-vertexes),
+c npompr(k) - number of proj. leg Pomerons in k-th block,
+c npomtg(k) - number of targ. leg Pomerons in k-th block,
+c npomin(k) - number of interm. Pomerons (between 2 3P-vertexes) in k-th block,
+c xpopin(n,k) - LC momentum of the upper 3P-vertex for n-th interm. Pomeron
+c in k-th block,
+c xpomin(n,k) - LC momentum of the lower 3P-vertex for n-th interm. Pomeron
+c in k-th block,
+c nnpr(i,k) - proj. participant index for i-th single Pomeron in k-th block,
+c nntg(i,k) - targ. participant index for i-th single Pomeron in k-th block,
+c ilpr(i,k) - proj. index for i-th proj. leg Pomeron in k-th block,
+c iltg(i,k) - proj. index for i-th targ. leg Pomeron in k-th block,
+c lnpr(i,k) - proj. participant index for i-th proj. leg Pomeron in k-th block,
+c lntg(i,k) - targ. participant index for i-th targ. leg Pomeron in k-th block,
+c lqa(ip) - number of cut Pomerons connected to ip-th proj. nucleon (hadron),
+c lqb(it) - number of cut Pomerons connected to it-th targ. nucleon (hadron),
+c nbpi(n,ip) - block index for n-th Pomeron connected to ip-th proj. nucleon,
+c nbti(n,it) - block index for n-th Pomeron connected to it-th targ. nucleon,
+c idnpi(n,ip) - type of n-th Pomeron (0 - single, 1 - leg) connected to ip-th
+c proj. nucleon,
+c idnti(n,it) - type of n-th Pomeron (0 - single, 1 - leg) connected to it-th
+c targ. nucleon,
+c nppi(n,ip) - index in the block of n-th Pomeron connected to ip-th proj.
+c nucleon (for single Pomerons),
+c npti(n,it) - index in the block of n-th Pomeron connected to it-th targ.
+c nucleon (for single Pomerons),
+c nlpi(n,ip) - index in the block of n-th Pomeron connected to ip-th proj.
+c nucleon (for leg Pomerons),
+c nlti(n,it) - index in the block of n-th Pomeron connected to it-th targ.
+c nucleon (for leg Pomerons),
+c iprcn(ip) - index of the recoiled targ. nucleon for ip-th proj. nucleon
+c (undergoing diffraction),
+c itgcn(it) - index of the recoiled proj. nucleon for it-th targ. nucleon
+c (undergoing diffraction),
+c bpompr(n,ip) - squared impact parameter for n-th leg Pomeron connected
+c to ip-th proj. nucleon,
+c bpomtg(n,it) - squared impact parameter for n-th leg Pomeron connected
+c to it-th targ. nucleon,
+c vvxpr(n,ip) - relative strenth of A-screening corrections for n-th leg
+c Pomeron connected to ip-th proj. nucleon,
+c vvxtg(n,it) - relative strenth of A-screening corrections for n-th leg
+c Pomeron connected to it-th targ. nucleon,
+c xpompr(n,ip) - LC momentum of the 3P-vertex for n-th leg Pomeron connected
+c to ip-th proj. nucleon,
+c xpomtg(n,it) - LC momentum of the 3P-vertex for n-th leg Pomeron connected
+c to it-th targ. nucleon
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208,npbmax=1000,npnmax=900,npmax=900,legmax=900)
+      dimension xas(iapmax,3),vabs(2),vabsi(2,iapmax),wdifi(iapmax)
+     *,vpac(iapmax),vtac(iapmax),xpomip(npmax),xpomim(npmax)
+     *,vvxim(npmax),bpomim(npmax),xpompi(legmax),xpomti(legmax)
+     *,vvxpi(legmax),vvxti(legmax),bpompi(legmax),bpomti(legmax)
+     *,ipompi(legmax),ipomti(legmax),ncola(iapmax),ncolb(iapmax)
+     *,wdp(2,iapmax),wdt(2,iapmax),wabs(2,2),xrapmin(100),xrapmax(100)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr2/  scm,wp0,wm0
+      common /qgarr4/  ey0(3)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr7/  xa(iapmax,3),xb(iapmax,3),b
+      common /qgarr9/  iwp(iapmax),iwt(iapmax),lqa(iapmax),lqb(iapmax)
+     *,iprcn(iapmax),itgcn(iapmax),ias(npbmax),ibs(npbmax),nqs(npbmax)
+     *,npompr(npbmax),npomtg(npbmax),npomin(npbmax),nnpr(npmax,npbmax)
+     *,nntg(npmax,npbmax),ilpr(legmax,npbmax),iltg(legmax,npbmax)
+     *,lnpr(legmax,npbmax),lntg(legmax,npbmax)
+     *,nbpi(npnmax,iapmax),nbti(npnmax,iapmax),idnpi(npnmax,iapmax)
+     *,idnti(npnmax,iapmax),nppi(npnmax,iapmax),npti(npnmax,iapmax)
+     *,nlpi(npnmax,iapmax),nlti(npnmax,iapmax)
+      common /qgarr10/ am(7),ammu
+      common /qgarr11/ b10
+      common /qgarr12/ nsp
+      common /qgarr13/ nsf,iaf(iapmax)
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr23/ bbpom(npbmax),vvxpom(npbmax)
+     *,bpompr(npnmax,iapmax),bpomtg(npnmax,iapmax)
+     *,vvxpr(npnmax,iapmax),vvxtg(npnmax,iapmax)
+     *,xpompr(npnmax,iapmax),xpomtg(npnmax,iapmax)
+     *,xpopin(npmax,npbmax),xpomin(npmax,npbmax),vvxin(npmax,npbmax)
+     *,bpomin(npmax,npbmax)
+      common /qgarr43/ moniou
+      common /qgarr46/ iconab(iapmax,iapmax),icona(iapmax)
+     *,iconb(iapmax)
+      common /qgarr55/ nwt,nwp       !N of wounded targ.(proj.) nucleons
+      common /qgarr56/ nspec,nspect  !N of spectators targ.(proj.) nucleons
+      common /qgdebug/  debug
+      common /qgsIInex1/xan(iapmax,3),xbn(iapmax,3) !used to link with nexus
+     *,bqgs,bmaxqgs,bmaxnex,bminnex
+      common/jdiff/bdiff,jdiff     !for external use: impact parameter
+                                   !for diffraction, diffraction type
+ctp from epos
+      integer ng1evt,ng2evt,ikoevt
+      real    rglevt,sglevt,eglevt,fglevt,typevt
+      common/c2evt/ng1evt,ng2evt,rglevt,sglevt,eglevt,fglevt,ikoevt
+     *,typevt            !in epos.inc
+
+      external qgran
+
+      if(debug.ge.1)write (moniou,201)
+      nsp=0
+      nsf=0
+      nsp0=nsp
+
+c initialization
+1     continue
+      do i=1,ia(1)
+       iddp(i)=1+int(qgran(b10)+cc(2,icz)) !diffractive eigenstates for proj.
+      enddo
+      do i=1,ia(2)
+       iddt(i)=1+int(qgran(b10)+cc(2,2))   !diffractive eigenstates for targ.
+      enddo
+
+c-------------------------------------------------
+c squared impact parameter is sampled uniformly (b**2<bm**2)
+      b=bm*dsqrt(qgran(b10))
+      if(debug.ge.1)write (moniou,202)b
+
+      if(bmaxnex.ge.0.d0)then              !used to link with nexus
+       b1=bminnex
+       b2=min(bm,bmaxnex)
+       if(b1.gt.b2)stop'bmin > bmax in qgsjet'
+       b=dsqrt(b1*b1+(b2*b2-b1*b1)*qgran(b10))
+       bqgs=b
+      endif
+
+c-------------------------------------------------
+c nuclear configurations
+      if(debug.ge.1)write (moniou,203)
+      if(ia(1).gt.1)then          !projectile nucleon coordinates
+       call qggea(ia(1),xa,1)     !xa(n,i), i=1,2,3 - x,y,z for n-th nucleon
+      else
+       do i=1,3
+        xa(1,i)=0.d0              !projectile hadron
+       enddo
+      endif
+      if(ia(2).gt.1)then          !target nucleon coordinates
+       call qggea(ia(2),xb,2)     !xb(n,i), i=1,2,3 - x,y,z for n-th nucleon
+      else
+       do i=1,3
+        xb(1,i)=0.d0              !target proton
+       enddo
+      endif
+
+c-------------------------------------------------
+c check connections
+      if(debug.ge.1)write (moniou,204)
+      do it=1,ia(2)
+       iconb(it)=0
+      enddo
+
+      do ip=1,ia(1)
+       icdp=iddp(ip)
+       icona(ip)=0
+       do it=1,ia(2)
+        icdt=iddt(it)
+        bbp=(xa(ip,1)+b-xb(it,1))**2+(xa(ip,2)-xb(it,2))**2
+        vv1p=qgpomi(scm,bbp,0.d0,0.d0,0.d0,icdp,icdt,icz,1)
+        if(vv1p.gt.1.d-3)then
+         if(debug.ge.2)write (moniou,205)ip,it
+         iconab(ip,it)=1
+         icona(ip)=1
+         iconb(it)=1
+         if(debug.ge.2)write (moniou,206)ip
+         if(debug.ge.2)write (moniou,207)it
+        else
+         iconab(ip,it)=0
+        endif
+       enddo
+      enddo
+
+      nrej=0
+2     nrej=nrej+1
+      if(debug.ge.2)write (moniou,208)nrej
+      if(nrej.gt.10)then
+       if(debug.ge.1)write (moniou,209)
+       goto 1
+      endif
+      nsp=nsp0
+      nbpom=0
+      nwp=0
+      nwt=0
+      do i=1,ia(1)
+       lqa(i)=0
+       iwp(i)=0
+       ncola(i)=0
+       wdp(1,i)=0.d0
+       wdp(2,i)=0.d0
+      enddo
+      do i=1,ia(2)
+       lqb(i)=0
+       iwt(i)=0
+       ncolb(i)=0
+       wdt(1,i)=0.d0
+       wdt(2,i)=0.d0
+      enddo
+      nqs(1)=0
+      npomin(1)=0
+      npompr(1)=0
+      npomtg(1)=0
+
+c-------------------------------------------------
+c Pomeron configuration
+      if(debug.ge.1)write (moniou,210)
+      do 4 ip=1,ia(1)             !loop over all projectile nucleons
+       if(debug.ge.2)write (moniou,211)ip
+       if(icona(ip).eq.0)goto 4
+       x=xa(ip,1)+b               !proj. x is shifted by the impact parameter b
+       y=xa(ip,2)
+       icdp=iddp(ip)              !diffr. eigenstate for ip
+
+       do 3 it=1,ia(2)            !loop over all target nucleons
+        if(debug.ge.2)write (moniou,212)it
+        if(iconab(ip,it).eq.0)goto 3
+        icdt=iddt(it)                         !diffr. eigenstate for it
+        bbp=(x-xb(it,1))**2+(y-xb(it,2))**2   !distance squared between ip, it
+
+c calculate nuclear screening factors for "middle point" -> eikonals
+        xpomr=1.d0/dsqrt(scm)
+        xxp=.5d0*(x+xb(it,1))
+        yyp=.5d0*(y+xb(it,2))
+        call qgfdf(xxp,yyp,xpomr,vpac,vtac,vvx,vvxp,vvxt,vvxpl,vvxtl
+     *  ,ip,it)
+        vv=qgpomi(scm,bbp,vvx,vvxp,vvxt,icdp,icdt,icz,1)        !total eikonal
+        vv1p=min(vv,qgpomi(scm,bbp,vvx,vvxp,vvxt,icdp,icdt,icz,2)) !1P-eikonal
+        if(debug.ge.2)write (moniou,213)vv,vv1p
+
+        if(qgran(b10).gt.1.d0-exp(-2.d0*vv))goto 3 !1.-exp(-2*vv) - probability
+                                                   !for inelastic interaction
+        iwt(it)=1
+        iwp(ip)=1
+        ncola(ip)=ncola(ip)+1                   !N of binary collisions for ip
+        ncolb(it)=ncolb(it)+1                   !N of binary collisions for it
+
+        n=npgen(2.d0*vv,1,50) !number of elem. inter. for (ip-it) collision
+        nbpom=nbpom+1         !new Pomeron block
+        if(nbpom.gt.npbmax)then
+         goto 2
+        endif
+        ias(nbpom)=ip         !proj. index for current elementary interaction
+        ibs(nbpom)=it         !targ. index for current elementary interaction
+        bbpom(nbpom)=bbp      !distance squared between ip, it
+        vvxpom(nbpom)=1.d0-(1.d0-vvx)*(1.d0-vvxp)*(1.d0-vvxt)
+        if(debug.ge.2)write (moniou,214)nbpom,ip,it,n
+
+        nqs(nbpom)=0
+        npomin(nbpom)=0
+        npompr(nbpom)=0
+        npomtg(nbpom)=0
+        do i=1,n
+         if(qgran(b10).lt.vv1p/vv.or.scm.le.sgap**2)then  !single Pomeron
+          if(debug.ge.2)write (moniou,215)i
+          np=nqs(nbpom)+1
+          if(np.gt.legmax)then
+           goto 2
+          endif
+          nqs(nbpom)=np                  !update Pomeron number in the block
+          l0=lqa(ip)+1
+          if(l0.gt.npnmax)then
+           goto 2
+          endif
+          lqa(ip)=l0                     !update number of connections for proj.
+          nnpr(np,nbpom)=l0              !index for connected proj. participant
+          nbpi(l0,ip)=nbpom
+          idnpi(l0,ip)=0
+          nppi(l0,ip)=np
+          l0=lqb(it)+1
+          if(l0.gt.npnmax)then
+           goto 2
+          endif
+          lqb(it)=l0
+          nntg(np,nbpom)=l0              !index for connected targ. participant
+          nbti(l0,it)=nbpom
+          idnti(l0,it)=0
+          npti(l0,it)=np
+
+         else                            !multi-Pomeron vertex
+          if(debug.ge.2)write (moniou,219)
+          call qg3pdf(vvxpi,vvxti,xpompi,xpomti,bpompi,bpomti,xpomip
+     *    ,xpomim,vvxim,bpomim,npompi,npomti,npin,ipompi,ipomti
+     *    ,wdp,wdt,ip,it,iret)
+          if(iret.ne.0)goto 2
+
+          if(npin.ne.0)then
+           if(debug.ge.2)write (moniou,220)i,npin
+           npomin(nbpom)=npomin(nbpom)+npin
+           if(npomin(nbpom).gt.npmax)then
+            goto 2
+           endif
+           do l=1,npin
+            l1=npomin(nbpom)+l-npin
+            xpopin(l1,nbpom)=xpomip(l)
+            xpomin(l1,nbpom)=xpomim(l)
+            vvxin(l1,nbpom)=vvxim(l)
+            bpomin(l1,nbpom)=bpomim(l)
+           enddo
+          endif
+          if(npompi.ne.0)then
+           if(debug.ge.2)write (moniou,221)i,npompi
+           do m=1,npompi
+            np=npompr(nbpom)+1
+            if(np.gt.legmax)then
+             goto 2
+            endif
+            npompr(nbpom)=np
+            ipp=ipompi(m)
+            iwp(ipp)=1
+            ilpr(np,nbpom)=ipp
+            l0=lqa(ipp)+1
+            if(l0.gt.npnmax)then
+             goto 2
+            endif
+            lqa(ipp)=l0
+            lnpr(np,nbpom)=l0
+            nbpi(l0,ipp)=nbpom
+            idnpi(l0,ipp)=1
+            nlpi(l0,ipp)=np
+            vvxpr(l0,ipp)=vvxpi(m)
+            xpompr(l0,ipp)=1.d0/xpompi(m)/scm
+            bpompr(l0,ipp)=bpompi(m)
+           enddo
+          endif
+          if(npomti.ne.0)then
+           if(debug.ge.2)write (moniou,222)i,npomti
+           do m=1,npomti
+            np=npomtg(nbpom)+1
+            if(np.gt.legmax)then
+             goto 2
+            endif
+            npomtg(nbpom)=np
+            itt=ipomti(m)
+            iwt(itt)=1
+            iltg(np,nbpom)=itt
+            l0=lqb(itt)+1
+            if(l0.gt.npnmax)then
+             goto 2
+            endif
+            lqb(itt)=l0
+            lntg(np,nbpom)=l0
+            nbti(l0,itt)=nbpom
+            idnti(l0,itt)=1
+            nlti(l0,itt)=np
+            vvxtg(l0,itt)=vvxti(m)
+            xpomtg(l0,itt)=xpomti(m)
+            bpomtg(l0,itt)=bpomti(m)
+           enddo
+          endif
+         endif
+        enddo                   !end of Pomeron loop
+3      continue                 !end of it-loop
+4     continue                  !end of ip-loop
+
+c-------------------------------------------------
+c   diffraction (hadron-hadron case)
+      if(ia(1).eq.1.and.ia(2).eq.1.and.iwp(1).eq.0.and.iwt(1).eq.0)then
+       wel=0.d0
+       winel=0.d0
+       do icdp=1,2
+       do icdt=1,2
+        vv=qgpomi(scm,b*b,0.d0,0.d0,0.d0,icdp,icdt,icz,1)   !total eikonal
+        wabs(icdp,icdt)=exp(-vv)
+        wel=wel+cc(icdp,icz)*cc(icdt,2)*wabs(icdp,icdt)
+        winel=winel+cc(icdp,icz)*cc(icdt,2)*wabs(icdp,icdt)**2
+       enddo
+       enddo
+       if(qgran(b10).le.wel**2/winel)then
+        if(debug.ge.1)write (moniou,231)
+        goto 1
+       endif
+
+       wdifp=cc(1,icz)*cc(2,icz)*(cc(1,2)**2*(wabs(1,1)-wabs(2,1))**2
+     * +cc(2,2)**2*(wabs(1,2)-wabs(2,2))**2+2.d0*cc(1,2)*cc(2,2)
+     * *(wabs(1,1)-wabs(2,1))*(wabs(1,2)-wabs(2,2)))
+       wdift=cc(1,2)*cc(2,2)*(cc(1,icz)**2*(wabs(1,1)-wabs(1,2))**2
+     * +cc(2,icz)**2*(wabs(2,1)-wabs(2,2))**2+2.d0*cc(1,icz)*cc(2,icz)
+     * *(wabs(1,1)-wabs(1,2))*(wabs(2,1)-wabs(2,2)))
+       wdifd=cc(1,icz)*cc(2,icz)*cc(1,2)*cc(2,2)
+     * *(wabs(1,1)+wabs(2,2)-wabs(1,2)-wabs(2,1))**2
+       aks=(wdifp+wdift+wdifd)*qgran(b10)
+       if(aks.lt.wdifp)then
+        nwp=nwp+1
+        iwp(1)=2
+        iprcn(1)=1
+        iwt(1)=-1
+       elseif(aks.lt.wdifp+wdift)then
+        nwt=nwt+1
+        iwt(1)=2
+        itgcn(1)=1
+        iwp(1)=-1
+       else
+        nwp=nwp+1
+        nwt=nwt+1
+        iwp(1)=2
+        iwt(1)=2
+        iprcn(1)=1
+        itgcn(1)=1
+       endif
+       goto 9
+      endif
+
+c-------------------------------------------------
+c   diffraction (hadron-nucleus & nucleus-nucleus)
+      do ip=1,ia(1)             !loop over all projectile nucleons
+       x=xa(ip,1)+b             !proj. x is shifted by b
+       y=xa(ip,2)
+       if(iwp(ip).ne.0)then
+        nwp=nwp+1               !one more wounded proj. nucleon
+        if(lqa(ip).eq.0.and.(wdp(1,ip).ne.0.d0.or.wdp(2,ip).ne.0.d0))
+     *  then
+         icdps=iddp(ip)
+         xpomr=1.d0/dsqrt(scm)
+         do it=1,ia(2)
+          if(iconab(ip,it).ne.0)then
+            bbp=(x-xb(it,1))**2+(y-xb(it,2))**2
+           xxp=.5d0*(x+xb(it,1))
+           yyp=.5d0*(y+xb(it,2))
+           icdt=iddt(it)
+           do icdp=1,2
+            iddp(ip)=icdp
+            call qgfdf(xxp,yyp,xpomr,vpac,vtac
+     *      ,vvx,vvxp,vvxt,vvxpl,vvxtl,ip,it)
+            vv=qgpomi(scm,bbp,vvx,vvxp,vvxt,icdp,icdt,icz,1)   !total eikonal
+            wdp(icdp,ip)=wdp(icdp,ip)*exp(-vv)
+           enddo
+          endif
+         enddo
+         iddp(ip)=icdps
+         wdifr=cc(1,icz)*cc(2,icz)*(wdp(1,ip)-wdp(2,ip))**2
+     *   /(cc(1,icz)*wdp(1,ip)**2+cc(2,icz)*wdp(2,ip)**2)
+         if(qgran(b10).lt.wdifr)iwp(ip)=3                     !LMD excitation
+        endif
+
+       elseif(icona(ip).ne.0)then
+        if(debug.ge.2)write (moniou,223)ip
+        vabs(1)=0.d0
+        vabs(2)=0.d0
+        icdps=iddp(ip)
+        do it=1,ia(2)
+          bbp=(x-xb(it,1))**2+(y-xb(it,2))**2
+         icdt=iddt(it)
+         do icdp=1,2
+          if(iconab(ip,it).eq.0)then
+           vabsi(icdp,it)=0.d0
+          else
+           iddp(ip)=icdp
+           xpomr=1.d0/dsqrt(scm)
+           xxp=.5d0*(x+xb(it,1))
+           yyp=.5d0*(y+xb(it,2))
+           call qgfdf(xxp,yyp,xpomr,vpac,vtac,vvx,vvxp,vvxt,vvxpl,vvxtl
+     *     ,ip,it)
+           vv=qgpomi(scm,bbp,vvx,vvxp,vvxt,icdp,icdt,icz,1)   !total eikonal
+           vabsi(icdp,it)=vv
+           vabs(icdp)=vabs(icdp)+vv
+          endif
+         enddo
+        enddo
+        iddp(ip)=icdps
+        wdifr=cc(1,icz)*cc(2,icz)*(exp(-vabs(1))-exp(-vabs(2)))**2
+     *  /(cc(1,icz)*exp(-2.d0*vabs(1))+cc(2,icz)*exp(-2.d0*vabs(2)))
+
+        if(qgran(b10).lt.wdifr)then       !projectile diffraction
+         wdift=0.d0
+         do it=1,ia(2)
+          if(iwt(it).ne.-1)then
+           wdifi(it)=cc(1,icz)*cc(2,icz)*(exp(-vabsi(1,it))
+     *     -exp(-vabsi(2,it)))**2/(cc(1,icz)*exp(-2.d0*vabsi(1,it))
+     *     +cc(2,icz)*exp(-2.d0*vabsi(2,it)))
+           wdift=wdift+wdifi(it)
+          else
+           wdifi(it)=0.d0
+          endif
+         enddo
+         if(wdift.ne.0.d0)then
+          nwp=nwp+1
+          iwp(ip)=2
+          aks=qgran(b10)*wdift
+          do it=1,ia(2)
+           aks=aks-wdifi(it)
+           if(aks.lt.0.d0)goto 5
+          enddo
+5          continue
+          iprcn(ip)=it
+          if(iwt(it).eq.0)iwt(it)=-1
+          if(debug.ge.2)write (moniou,224)ip,it
+         endif
+        endif
+       endif
+      enddo                            !end of ip-loop
+
+      do 8 it=1,ia(2)                     !check target diffraction
+       if(iwt(it).gt.0)then
+        nwt=nwt+1                         !one more wounded targ. nucleon
+        if(lqb(it).eq.0.and.(wdt(1,it).ne.0.d0.or.wdt(2,it).ne.0.d0))
+     *  then
+         icdts=iddt(it)
+         xpomr=1.d0/dsqrt(scm)
+         do ip=1,ia(1)
+          if(iconab(ip,it).ne.0)then
+           bbp=(xa(ip,1)+b-xb(it,1))**2+(xa(ip,2)-xb(it,2))**2
+           xxp=.5d0*(xa(ip,1)+b+xb(it,1))
+           yyp=.5d0*(xa(ip,2)+xb(it,2))
+           icdp=iddp(ip)
+           do icdt=1,2
+            iddt(it)=icdt
+            call qgfdf(xxp,yyp,xpomr,vpac,vtac
+     *      ,vvx,vvxp,vvxt,vvxpl,vvxtl,ip,it)
+            vv=qgpomi(scm,bbp,vvx,vvxp,vvxt,icdp,icdt,icz,1)   !total eikonal
+             wdt(icdt,it)=wdt(icdt,it)*exp(-vv)
+           enddo
+          endif
+         enddo
+         iddt(it)=icdts
+         wdifr=cc(1,2)*cc(2,2)*(wdt(1,it)-wdt(2,it))**2
+     *   /(cc(1,2)*wdt(1,it)**2+cc(2,2)*wdt(2,it)**2)
+         if(qgran(b10).lt.wdifr)iwt(it)=3
+        endif
+
+       elseif(iconb(it).ne.0)then
+        if(debug.ge.2)write (moniou,225)it
+        vabs(1)=0.d0
+        vabs(2)=0.d0
+        icdts=iddt(it)
+        do ip=1,ia(1)
+         bbp=(xa(ip,1)+b-xb(it,1))**2+(xa(ip,2)-xb(it,2))**2
+         icdp=iddp(ip)
+         do icdt=1,2
+          if(iconab(ip,it).eq.0)then
+           vabsi(icdt,ip)=0.d0
+          else
+           iddt(it)=icdt
+           xpomr=1.d0/dsqrt(scm)
+           xxp=.5d0*(xa(ip,1)+b+xb(it,1))
+           yyp=.5d0*(xa(ip,2)+xb(it,2))
+           call qgfdf(xxp,yyp,xpomr,vpac,vtac,vvx,vvxp,vvxt,vvxpl,vvxtl
+     *     ,ip,it)
+           vv=qgpomi(scm,bbp,vvx,vvxp,vvxt,icdp,icdt,icz,1)   !total eikonal
+           vabsi(icdt,ip)=vv
+           vabs(icdt)=vabs(icdt)+vv
+          endif
+         enddo
+        enddo
+        iddt(it)=icdts
+        wdifr=cc(1,2)*cc(2,2)*(exp(-vabs(1))-exp(-vabs(2)))**2
+     *  /(cc(1,2)*exp(-2.d0*vabs(1))+cc(2,2)*exp(-2.d0*vabs(2)))
+
+        if(qgran(b10).lt.wdifr)then       !target diffraction
+         wdift=0.d0
+         do ip=1,ia(1)
+          if(iwp(ip).eq.-1)then
+           wdifi(ip)=0.d0
+          else
+           if(iwp(ip).eq.2)then
+            itt=iprcn(ip)
+            if(itt.eq.it)goto 7
+            if(iwt(itt).eq.2)then
+             wdifi(ip)=0.d0
+             goto 6
+            endif
+           endif
+           wdifi(ip)=cc(1,2)*cc(2,2)*(exp(-vabsi(1,ip))
+     *     -exp(-vabsi(2,ip)))**2/(cc(1,2)*exp(-2.d0*vabsi(1,ip))
+     *     +cc(2,2)*exp(-2.d0*vabsi(2,ip)))
+          endif
+6          wdift=wdift+wdifi(ip)
+         enddo
+         if(wdift.eq.0.d0)goto 8
+         nwt=nwt+1
+         iwt(it)=2
+         aks=qgran(b10)*wdift
+         do ip=1,ia(1)
+          aks=aks-wdifi(ip)
+          if(aks.lt.0.d0)goto 7
+         enddo
+7         continue
+         itgcn(it)=ip
+         if(debug.ge.2)write (moniou,226)it,ip
+         if(iwp(ip).eq.0)then
+          iwp(ip)=-1
+         elseif(iwp(ip).eq.2)then
+          itt=iprcn(ip)
+          iprcn(ip)=it
+          if(itt.ne.it.and.iwt(itt).eq.-1)iwt(itt)=0
+         endif
+        endif
+       endif
+8     continue
+c check diffractive cross sections          !so060413-beg
+9     jdiff=0                             !non-diffractive
+      nqst=0
+      nint=0
+      if(nbpom.ne.0)then
+       do i=1,nbpom
+        nqst=nqst+nqs(i)
+        nint=nint+npomin(i)
+       enddo
+      endif
+      if((nwp.ne.0.or.nwt.ne.0).and.nqst.eq.0)then   !not elastic nor ND
+       lqat=0
+       do ip=1,ia(1)
+        lqat=lqat+lqa(ip)
+       enddo
+       lqbt=0
+       do it=1,ia(2)
+        lqbt=lqbt+lqb(it)
+       enddo
+       iwpt=0
+       do ip=1,ia(1)
+        if(iwp(ip).eq.1)then
+         iwpt=1
+         goto 10
+        elseif(iwp(ip).ge.2)then
+         iwpt=2
+        endif
+       enddo
+10     continue
+       iwtt=0
+       do it=1,ia(2)
+        if(iwt(it).eq.1)then
+         iwtt=1
+         goto 11
+        elseif(iwt(it).ge.2)then
+         iwtt=2
+        endif
+       enddo
+11     continue
+       if(lqat.eq.0.and.lqbt.eq.0)then
+        if(nbpom.eq.0.or.nint.eq.0)then
+         if(iwpt.eq.2.and.iwtt.ne.2)then
+          jdiff=6                         !SD(LM)-proj
+         elseif(iwpt.ne.2.and.iwtt.eq.2)then
+          jdiff=7                         !SD(LM)-targ
+         elseif(iwpt.eq.2.and.iwtt.eq.2)then
+          jdiff=8                         !DD(LM)
+         else
+          goto 14
+         endif
+        else
+         if(iwpt.ne.2.and.iwtt.ne.2)then
+          jdiff=9                         !CD(DPE)
+         else
+          jdiff=10                        !CD+LMD
+         endif
+        endif
+       elseif(lqat.gt.0.and.lqbt.eq.0.and.iwtt.ne.2)then
+        jdiff=1                          !SD(HM)-proj
+       elseif(lqat.eq.0.and.lqbt.gt.0.and.iwpt.ne.2)then
+        jdiff=2                          !SD(HM)-targ
+       elseif(lqat.gt.0.and.lqbt.eq.0.and.iwtt.eq.2)then
+        jdiff=3                          !DD(LHM)-proj
+       elseif(lqat.eq.0.and.lqbt.gt.0.and.iwpt.eq.2)then
+        jdiff=4                          !DD(LHM)-targ
+
+       elseif(lqat.gt.0.and.lqbt.gt.0)then
+        if(nbpom.eq.0)stop'problem with nbpom!!!'
+        xrapmax(1)=1.d0
+        xrapmin(1)=1.d0/scm
+        do ibpom=1,nbpom
+         if(npompr(ibpom).gt.0)then
+          do i=1,npompr(ibpom)
+           ip=ilpr(i,ibpom)
+           lpom=lnpr(i,ibpom)
+           xrapmax(1)=min(xrapmax(1),1.d0/xpompr(lpom,ip)/scm)
+          enddo
+         endif
+         if(npomtg(ibpom).gt.0)then
+          do i=1,npomtg(ibpom)
+           it=iltg(i,ibpom)
+           lpom=lntg(i,ibpom)
+           xrapmin(1)=max(xrapmin(1),xpomtg(lpom,it))
+          enddo
+         endif
+        enddo
+        if(xrapmin(1).gt..999d0*xrapmax(1))goto 14
+        nraps=1
+12      if(nraps.gt.90)stop'nraps>90'
+        do ibpom=1,nbpom
+         if(npomin(ibpom).gt.0)then
+          do i=1,npomin(ibpom)
+           if(nraps.eq.1)then
+            if(1.d0/scm/xpomin(i,ibpom).lt..999d0*xrapmax(1)
+     *      .and.xpopin(i,ibpom).gt.1.001d0*xrapmin(1))then !rap-gaps changed
+             if(1.d0/scm/xpomin(i,ibpom).lt.1.001d0*xrapmin(1)
+     *       .and.xpopin(i,ibpom).gt..999d0*xrapmax(1))then !no rap-gap (filled)
+               goto 14
+             elseif(xpopin(i,ibpom).gt..999d0*xrapmax(1))then
+              xrapmax(1)=1.d0/scm/xpomin(i,ibpom)
+             elseif(1.d0/scm/xpomin(i,ibpom).lt.1.001d0*xrapmin(1))then
+              xrapmin(1)=xpopin(i,ibpom)
+             else
+              xrapmin(2)=xrapmin(1)
+              xrapmin(1)=xpopin(i,ibpom)
+              xrapmax(2)=1.d0/scm/xpomin(i,ibpom)
+              nraps=2
+              goto 12
+             endif
+            endif
+           else
+            if(1.d0/scm/xpomin(i,ibpom).lt..999d0*xrapmax(1)
+     *      .and.xpopin(i,ibpom).gt.1.001d0*xrapmin(nraps))then !rap-gaps changed
+             if(1.d0/scm/xpomin(i,ibpom).lt.1.001d0*xrapmin(nraps)
+     *       .and.xpopin(i,ibpom).gt..999d0*xrapmax(1))then !no rap-gaps (filled)
+              goto 14
+             else
+              do irap=1,nraps
+               if(xpopin(i,ibpom).gt..999d0*xrapmax(irap).and.1.d0/scm
+     *         /xpomin(i,ibpom).lt.1.001d0*xrapmin(irap))then !gap filled
+                if(irap.lt.nraps)then
+                 do j=irap,nraps-1
+                  xrapmax(j)=xrapmax(j+1)
+                  xrapmin(j)=xrapmin(j+1)
+                 enddo
+                endif
+                nraps=nraps-1
+                goto 12
+               elseif(xpopin(i,ibpom).gt..999d0*xrapmax(irap))then
+                xrapmax(irap)=min(1.d0/scm/xpomin(i,ibpom)
+     *          ,xrapmax(irap))
+               elseif(1.d0/scm/xpomin(i,ibpom)
+     *         .lt.1.001d0*xrapmin(irap))then
+                xrapmin(irap)=max(xpopin(i,ibpom),xrapmin(irap))
+               elseif(1.d0/scm/xpomin(i,ibpom).gt.xrapmin(irap)
+     *         .and.xpopin(i,ibpom).lt.xrapmax(irap))then
+                xrapmin(irap)=max(xpopin(i,ibpom),xrapmin(irap))
+                if(irap.lt.nraps)then
+                 do j=1,nraps-irap
+                  xrapmax(nraps-j+2)=xrapmax(nraps-j+1)
+                  xrapmin(nraps-j+2)=xrapmin(nraps-j+1)
+                 enddo
+                endif
+                xrapmin(irap+1)=xrapmin(irap)
+                xrapmin(irap)=xpopin(i,ibpom)
+                xrapmax(irap+1)=1.d0/scm/xpomin(i,ibpom)
+                nraps=nraps+1
+                goto 12
+               endif
+              enddo                       !end of irap-loop
+             endif
+            endif
+           endif
+          enddo                           !end of npin-loop
+         endif
+        enddo                             !end of ibpom-loop
+        jdiff=5                          !DD(HM)
+       endif
+      endif                              !end of diffr. check
+14    bdiff=b
+
+ctp define collision type
+      typevt=0                      !no interaction
+      if(nwp.gt.0.or.nwt.gt.0)then         !so060413-end
+       if(jdiff.eq.0)then                                  !ND (no rap-gaps)
+        typevt=1
+       elseif(jdiff.eq.8.or.jdiff.eq.10.or.
+     *       (jdiff.gt.2.and.jdiff.lt.6))then !DD + (CD+LMD)
+        typevt=2
+       elseif(jdiff.eq.1.or.jdiff.eq.6)then                  !SD pro
+        typevt=4
+       elseif(jdiff.eq.2.or.jdiff.eq.7)then                  !SD tar
+        typevt=-4
+       elseif(jdiff.eq.9)then                                !CD
+        typevt=3
+       else
+        stop'problem with typevt!'
+       endif
+      endif
+
+
+c form projectile spectator part
+      if(debug.ge.1)write (moniou,227)
+      nspec=0
+      do ip=1,ia(1)
+       if(iwp(ip).eq.0)then
+        if(debug.ge.2)write (moniou,228)ip
+        nspec=nspec+1
+        do l=1,3
+         xas(nspec,l)=xa(ip,l)
+        enddo
+       endif
+      enddo
+
+      nspect=0
+      do it=1,ia(2)
+       if(iwt(it).eq.0)nspect=nspect+1
+      enddo
+
+c inelastic interaction: energy sharing and particle production
+      if(nwp.ne.0.or.nwt.ne.0)then
+       if(ia(1).eq.nspec.or.ia(2).eq.nspect)stop'ia(1)=nspec!!!'
+       if(debug.ge.1)write (moniou,229)
+
+       call qgsha(nbpom,ncola,ncolb,iret)
+       if(iret.ne.0)goto 1
+       if(nsp.le.nsp0+2)then
+        if(debug.ge.1)write (moniou,230)
+        goto 1
+       endif
+      else                                 !no interaction
+       if(debug.ge.1)write (moniou,231)
+       goto 1
+      endif
+      if(debug.ge.1)write (moniou,232)nsp
+
+c fragmentation of the projectile spectator part
+      if(debug.ge.1)write (moniou,233)
+      call qgfrgm(nspec,xas)
+      if(debug.ge.1)write (moniou,234)nsf
+      if(debug.ge.1)write (moniou,235)
+
+201   format(2x,'qgconf - configuration of the interaction')
+202   format(2x,'qgconf: impact parameter b=',e10.3,' fm')
+203   format(2x,'qgconf: nuclear configurations')
+204   format(2x,'qgconf: check connections')
+205   format(2x,'qgconf: ',i3,'-th proj. nucleon may interact with '
+     *,i3,'-th target nucleon')
+206   format(2x,'qgconf: ',i3,'-th projectile nucleon may interact')
+207   format(2x,'qgconf: ',i3,'-th target nucleon may interact')
+208   format(2x,'qgconf: ',i3,'-th rejection,'
+     *,' redo Pomeron configuration')
+209   format(2x,'qgconf: too many rejections,'
+     *,' redo nuclear configuartions')
+210   format(2x,'qgconf: Pomeron configuration')
+211   format(2x,'qgconf: check ',i3,'-th projectile nucleon')
+212   format(2x,'qgconf: interaction with ',i3,'-th target nucleon?')
+213   format(2x,'qgconf: eikonals - total: ',e10.3,2x,'single: ',e10.3)
+214   format(2x,'qgconf: ',i4,'-th Pomeron block connected to ',i3
+     *,'-th proj. nucleon and'/4x,i3,'-th targ. nucleon;'
+     *,' number of element. processes in the block: ',i3)
+215   format(2x,'qgconf: ',i3
+     *,'-th process in the block is single cut Pomeron')
+219   format(2x,'qgconf: configuration of multi-Pomeron vertexes')
+220   format(2x,'qgconf: ',i3,'-th process in the block contains '
+     *,i3,' interm. Pomerons')
+221   format(2x,'qgconf: ',i3,'-th process in the block contains '
+     *,i3,' proj. legs')
+222   format(2x,'qgconf: ',i3,'-th process in the block contains '
+     *,i3,' targ. legs')
+223   format(2x,'qgconf: check diffraction for ',i3,'-th proj. nucleon')
+224   format(2x,'qgconf: diffr. of ',i3,'-th proj. nucleon,'
+     *,' recoil of ',i3,'-th targ. nucleon')
+225   format(2x,'qgconf: check diffraction for ',i3,'-th targ. nucleon')
+226   format(2x,'qgconf: diffr. of ',i3,'-th targ. nucleon,'
+     *,' recoil of ',i3,'-th proj. nucleon')
+227   format(2x,'qgconf: projectile spectator part')
+228   format(2x,'qgconf: ',i3,'-th proj. nucleon stays idle')
+229   format(2x,'qgconf: inelastic interaction: energy sharing'
+     *,' and particle production')
+230   format(2x,'qgconf: no particle produced - rejection')
+231   format(2x,'qgconf: no interaction - rejection')
+232   format(2x,'qgconf: ',i5,' particles have been produced')
+233   format(2x,'qgconf: fragmentation of the proj. spectator part')
+234   format(2x,'qgconf: ',i3,' proj. fragments have been produced')
+235   format(2x,'qgconf - end')
+      return
+      end
+
+c=============================================================================
+      subroutine qg3pdf(vvxpi,vvxti,xpompi,xpomti,bpompi,bpomti
+     *,xpomip,xpomim,vvxim,bpomim,nppr,nptg,npin,ipompi,ipomti
+     *,wdp,wdt,ip,it,iret)
+c-----------------------------------------------------------------------
+c qg3pdf - configuration for multi-Pomeron/diffractive contributions
+c ip,it - indexes of proj. and targ. nucleons for current collision
+c to determine:
+c nppr - number of proj. leg Pomerons in the process,
+c nptg - number of targ. leg Pomerons in the process,
+c npin - number of interm. Pomerons (between 2 3P-vertexes) in the process,
+c xpomip(i) - LC momentum of the upper 3P-vertex for i-th interm. Pomeron
+c in the process,
+c xpomim(i) - LC momentum of the lower 3P-vertex for i-th interm. Pomeron
+c in the process,
+c ipompi(i) - proj. index for i-th proj. leg Pomeron in the process,
+c ipomti(i) - proj. index for i-th targ. leg Pomeron in the process,
+c bpompi(i) - squared impact param. for i-th proj. leg Pomeron in the process,
+c bpomti(i) - squared impact param. for i-th targ. leg Pomeron in the process,
+c vvxpi(i) - relative strenth of scr. corrections for i-th proj. leg Pomeron,
+c vvxti(i) - relative strenth of scr. corrections for i-th targ. leg Pomeron,
+c xpompi(i) - LC momentum of the 3P-vertex for i-th proj. leg Pomeron,
+c xpomti(i) - LC momentum of the 3P-vertex for i-th targ. leg Pomeron
+c iret=1 - reject configuration
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208,npbmax=1000,npnmax=900,npmax=900
+     *,levmax=20,legmax=900)
+      dimension vpac(iapmax),vtac(iapmax)
+     *,vpac0(iapmax),vtac0(iapmax),vpact(iapmax),vtact(iapmax)
+     *,xpomip(npmax),xpomim(npmax),vvxim(npmax),bpomim(npmax)
+     *,xpompi(legmax),xpomti(legmax)
+     *,vvxpi(legmax),vvxti(legmax),bpompi(legmax),bpomti(legmax)
+     *,ipompi(legmax),ipomti(legmax),ippr0(legmax),iptg0(legmax)
+     *,nppm(levmax),ippm(legmax,levmax),ii(levmax),xpomm(levmax)
+     *,wgpm(levmax),xxm(levmax),yym(levmax)
+     *,itypr0(legmax),itytg0(legmax),itypm(legmax,levmax),vv(12)
+     *,wdp(2,iapmax),wdt(2,iapmax)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr2/  scm,wp0,wm0
+      common /qgarr6/  pi,bm,amws
+      common /qgarr7/  xa(iapmax,3),xb(iapmax,3),b
+      common /qgarr9/  iwp(iapmax),iwt(iapmax),lqa(iapmax),lqb(iapmax)
+     *,iprcn(iapmax),itgcn(iapmax),ias(npbmax),ibs(npbmax),nqs(npbmax)
+     *,npompr(npbmax),npomtg(npbmax),npomin(npbmax),nnpr(npmax,npbmax)
+     *,nntg(npmax,npbmax),ilpr(legmax,npbmax),iltg(legmax,npbmax)
+     *,lnpr(legmax,npbmax),lntg(legmax,npbmax)
+     *,nbpi(npnmax,iapmax),nbti(npnmax,iapmax),idnpi(npnmax,iapmax)
+     *,idnti(npnmax,iapmax),nppi(npnmax,iapmax),npti(npnmax,iapmax)
+     *,nlpi(npnmax,iapmax),nlti(npnmax,iapmax)
+      common /qgarr11/ b10
+      common /qgarr12/ nsp
+      common /qgarr13/ nsf,iaf(iapmax)
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr19/ ahl(3)
+      common /qgarr23/ bbpom(npbmax),vvxpom(npbmax)
+     *,bpompr(npnmax,iapmax),bpomtg(npnmax,iapmax)
+     *,vvxpr(npnmax,iapmax),vvxtg(npnmax,iapmax)
+     *,xpompr(npnmax,iapmax),xpomtg(npnmax,iapmax)
+     *,xpopin(npmax,npbmax),xpomin(npmax,npbmax),vvxin(npmax,npbmax)
+     *,bpomin(npmax,npbmax)
+      common /qgarr43/ moniou
+      common /qgarr46/ iconab(iapmax,iapmax),icona(iapmax)
+     *,iconb(iapmax)
+      common /qgdebug/  debug
+      external qgran
+
+      if(debug.ge.2)write (moniou,201)ip,it
+
+      if(scm.le.sgap**2)stop'qg3pdf: scm<sgap**2!'
+      iret=0
+      vpacng=0.d0
+      vtacng=0.d0
+      vpacpe=0.d0
+      vtacpe=0.d0
+      vimp=0.d0
+      viuc=0.d0
+      viuu=0.d0
+      vip=0.d0
+      vicc=0.d0
+      vicu=0.d0
+c normalization of rejection function
+      xpomr=1.d0/dsqrt(scm)
+      bpt=dsqrt((xa(ip,1)+b-xb(it,1))**2+(xa(ip,2)-xb(it,2))**2)
+      rp1=(rq(iddp(ip),icz)-alfp*dlog(xpomr))*4.d0*.0389d0
+      rp2=(rq(iddt(it),2)+alfp*dlog(xpomr*scm))*4.d0*.0389d0
+      rp0=rp1*rp2/(rp1+rp2)
+      bbpr=(bpt*rp1/(rp1+rp2))**2
+      bbtg=(bpt*rp2/(rp1+rp2))**2
+      call qgbdef(bbpr,bbtg,xa(ip,1)+b,xa(ip,2),xb(it,1),xb(it,2)
+     *,xxp,yyp,1)
+
+      rpmax=max(rq(iddp(ip),icz),rq(iddt(it),2))*4.d0*.0389d0
+      rpmin=min(rq(iddp(ip),icz),rq(iddt(it),2))*4.d0*.0389d0
+      if(rpmax.eq.rpmin)then
+       rpmax=rpmax+alfp*dlog(scm)*2.d0*.0389d0
+       rpmin=rpmin+alfp*dlog(scm)*2.d0*.0389d0
+      else
+       rpmin=rpmin+alfp*dlog(scm/sgap)*4.d0*.0389d0
+      endif
+      rp0=rpmax*rpmin/(rpmax+rpmin)
+
+      call qgfdf(xxp,yyp,xpomr,vpac,vtac,vvx,vvxp,vvxt,vvxpl,vvxtl
+     *,ip,it)
+      vvxts=1.d0-(1.d0-vvx)*(1.d0-vvxt)*exp(-vtac(it))
+      vpl=qglegi(1.d0/xpomr,bbpr,iddp(ip),icz,2)
+      vplc=min(vpl
+     *,qgfani(1.d0/xpomr,bbpr,vvxts,vvxp,vvxpl,iddp(ip),icz,7))
+      vplc0=min(vplc
+     *,qgfani(1.d0/xpomr,bbpr,vvxts,vvxp,vvxpl,iddp(ip),icz,8))
+      vplcpe=min(vplc0
+     *,qgfani(1.d0/xpomr,bbpr,vvxts,vvxp,vvxpl,iddp(ip),icz,10))
+      vplcp=min(vplcpe
+     *,qgfani(1.d0/xpomr,bbpr,vvxts,vvxp,vvxpl,iddp(ip),icz,9))
+
+      vvxps=1.d0-(1.d0-vvx)*(1.d0-vvxp)*exp(-vpac(ip))
+      vtl=qglegi(xpomr*scm,bbtg,iddt(it),2,2)
+      vtlc=min(vtl
+     *,qgfani(xpomr*scm,bbtg,vvxps,vvxt,vvxtl,iddt(it),2,7))
+      vtlc0=min(vtlc
+     *,qgfani(xpomr*scm,bbtg,vvxps,vvxt,vvxtl,iddt(it),2,8))
+      vtlcpe=min(vtlc0
+     *,qgfani(xpomr*scm,bbtg,vvxps,vvxt,vvxtl,iddt(it),2,10))
+      vtlcp=min(vtlcpe
+     *,qgfani(xpomr*scm,bbtg,vvxps,vvxt,vvxtl,iddt(it),2,9))
+
+      sumcp0=0.d0
+      sumup=0.d0
+      do i=1,ia(1)
+       sumup=sumup+vpac(i)
+      enddo
+      vvxs=(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+      do i=1,ia(1)-ip+1
+       ipp=ia(1)-i+1
+       bbp=(xa(ipp,1)+b-xxp)**2+(xa(ipp,2)-yyp)**2
+       sumup=sumup-vpac(ipp)
+       vpac0(ipp)=min(vpac(ipp)
+     * ,qgfani(1.d0/xpomr,bbp,1.d0-vvxs*exp(-sumup)
+     * ,1.d0-exp(-sumcp0),1.d0-exp(-sumup),iddp(ipp),icz,3))
+       if(ipp.gt.ip)sumcp0=sumcp0+vpac0(ipp)
+      enddo
+      sumct0=0.d0
+      sumut=0.d0
+      do i=1,ia(2)
+       sumut=sumut+vtac(i)
+      enddo
+      vvxs=(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+      do i=1,ia(2)-it+1
+       itt=ia(2)-i+1
+       bbt=(xb(itt,1)-xxp)**2+(xb(itt,2)-yyp)**2
+       sumut=sumut-vtac(itt)
+       vtac0(itt)=min(vtac(itt)
+     * ,qgfani(xpomr*scm,bbt,1.d0-vvxs*exp(-sumut)
+     * ,1.d0-exp(-sumct0),1.d0-exp(-sumut),iddt(itt),2,3))
+       if(itt.gt.it)sumct0=sumct0+vtac0(itt)
+      enddo
+      vvxp0=1.d0-exp(-sumcp0)
+      vvxt0=1.d0-exp(-sumct0)
+
+c weights for vertex contributions:
+c vv(1): >1 proj. legs and >1 targ. legs
+      vv(1)=(max(0.d0,1.d0-exp(-2.d0*vpac(ip))*(1.d0+2.d0*vpac(ip)))
+     *+2.d0*vpac(ip)*exp(-2.d0*vpac(ip))*(1.d0-(1.d0-vvxp)**2))
+     **(max(0.d0,1.d0-exp(-2.d0*vtac(it))*(1.d0+2.d0*vtac(it)))
+     *+2.d0*vtac(it)*exp(-2.d0*vtac(it))*(1.d0-(1.d0-vvxt)**2))
+     **(1.d0-vvx)**2
+     *-2.d0*(max(0.d0,exp(vpac(ip)-vpac0(ip))-1.d0
+     *-(vpac(ip)-vpac0(ip)))
+     **(1.d0-vvxp0)+(vpac(ip)-vpac0(ip))*(vvxp-vvxp0))*exp(-vpac(ip))
+     **(max(0.d0,1.d0-exp(-2.d0*vtac(it))*(1.d0+2.d0*vtac(it)))
+     *+2.d0*vtac(it)*exp(-2.d0*vtac(it))*(1.d0-(1.d0-vvxt)**2))
+     **(1.d0-vvx)*(1.d0-vvxtl)
+     *-2.d0*(max(0.d0,1.d0-exp(-2.d0*vpac(ip))*(1.d0+2.d0*vpac(ip)))
+     *+2.d0*vpac(ip)*exp(-2.d0*vpac(ip))*(1.d0-(1.d0-vvxp)**2))
+     **(max(0.d0,exp(vtac(it)-vtac0(it))-1.d0-(vtac(it)-vtac0(it)))
+     **(1.d0-vvxt0)+(vtac(it)-vtac0(it))*(vvxt-vvxt0))*exp(-vtac(it))
+     **(1.d0-vvx)*(1.d0-vvxpl)
+c vv(2): 0 proj. legs and 0 targ. legs
+      vv(2)=((1.d0-exp(-vpac(ip)))**2*(1.d0-vvxpl)
+     *+2.d0*(1.d0-exp(-vpac(ip)))*vvxpl)
+     **((1.d0-exp(-vtac(it)))**2*(1.d0-vvxtl)
+     *+2.d0*(1.d0-exp(-vtac(it)))*vvxtl)*(1.d0-vvx)
+c vv(3): 0 proj. legs and >1 targ. legs
+      vv(3)=((1.d0-exp(-vpac(ip)))**2*(1.d0-vvxpl)
+     *+2.d0*(1.d0-exp(-vpac(ip)))*vvxpl)*(1.d0-vvx)
+     **((max(0.d0,1.d0-exp(-2.d0*vtac(it))*(1.d0+2.d0*vtac(it)))
+     *+2.d0*vtac(it)*exp(-2.d0*vtac(it))*(1.d0-(1.d0-vvxt)**2))
+     **(1.d0-vvxtl)
+     *-2.d0*(max(0.d0,exp(vtac(it)-vtac0(it))-1.d0
+     *-(vtac(it)-vtac0(it)))
+     **(1.d0-vvxt0)+(vtac(it)-vtac0(it))*(vvxt-vvxt0))*exp(-vtac(it)))
+c vv(4): >1 proj. legs and 0 targ. legs
+      vv(4)=((max(0.d0,1.d0-exp(-2.d0*vpac(ip))*(1.d0+2.d0*vpac(ip)))
+     *+2.d0*vpac(ip)*exp(-2.d0*vpac(ip))*(1.d0-(1.d0-vvxp)**2))
+     **(1.d0-vvxpl)
+     *-2.d0*(max(0.d0,exp(vpac(ip)-vpac0(ip))-1.d0
+     *-(vpac(ip)-vpac0(ip)))
+     **(1.d0-vvxp0)+(vpac(ip)-vpac0(ip))*(vvxp-vvxp0))*exp(-vpac(ip)))
+     **((1.d0-exp(-vtac(it)))**2*(1.d0-vvxtl)
+     *+2.d0*(1.d0-exp(-vtac(it)))*vvxtl)*(1.d0-vvx)
+c vv(5): 0 proj. legs and >1 targ. (handle) legs
+      vv(5)=4.d0*(1.d0-exp(-vpac(ip)))*(1.d0-vvx)
+     **(max(0.d0,exp(vtac(it)-vtac0(it))-1.d0-(vtac(it)-vtac0(it)))
+     **(1.d0-vvxt0)+(vtac(it)-vtac0(it))*(vvxt-vvxt0))*exp(-vtac(it))
+      if(xpomr*scm.lt.1.1d0*sgap**2)vv(5)=0.d0
+c vv(6): >1 proj. (handle) legs and 0 targ. legs
+      vv(6)=4.d0*(max(0.d0,exp(vpac(ip)-vpac0(ip))-1.d0
+     *-(vpac(ip)-vpac0(ip)))*(1.d0-vvxp0)
+     *+(vpac(ip)-vpac0(ip))*(vvxp-vvxp0))*exp(-vpac(ip))
+     **(1.d0-exp(-vtac(it)))*(1.d0-vvx)
+      if(xpomr*sgap**2.gt..9d0)vv(6)=0.d0
+c vv(7): >1 proj. legs and 1 targ. leg
+      vv(7)=(max(0.d0,1.d0-exp(-2.d0*vpac(ip))*(1.d0+2.d0*vpac(ip)))
+     *+2.d0*vpac(ip)*exp(-2.d0*vpac(ip))*(1.d0-(1.d0-vvxp)**2))
+     **((vtac0(it)+vtlc0)*exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+     *-(vtac(it)+vtlc-vtac0(it)-vtlc0)
+     **(1.d0-exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)))*exp(-vtac(it))
+     **(1.d0-vvx)*(1.d0-vvxpl)*(1.d0-vvxt)
+     *-2.d0*(max(0.d0,exp(vpac(ip)-vpac0(ip))-1.d0
+     *-(vpac(ip)-vpac0(ip)))
+     **(1.d0-vvxp0)+(vpac(ip)-vpac0(ip))*(vvxp-vvxp0))
+     **(vtac(it)+vtlc)*exp(-vpac(ip)-2.d0*vtac(it))
+     **(1.d0-vvx)*(1.d0-vvxt)**2*(1.d0-vvxtl)
+c vv(8): 1 proj. leg and >1 targ. legs
+      vv(8)=((vpac0(ip)+vplc0)*exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+     *-(vpac(ip)+vplc-vpac0(ip)-vplc0)
+     **(1.d0-exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)))*exp(-vpac(ip))
+     **(max(0.d0,1.d0-exp(-2.d0*vtac(it))*(1.d0+2.d0*vtac(it)))
+     *+2.d0*vtac(it)*exp(-2.d0*vtac(it))*(1.d0-(1.d0-vvxt)**2))
+     **(1.d0-vvx)*(1.d0-vvxp)*(1.d0-vvxtl)
+     *-2.d0*(vpac(ip)+vplc)*exp(-2.d0*vpac(ip)-vtac(it))
+     **(max(0.d0,exp(vtac(it)-vtac0(it))-1.d0-(vtac(it)-vtac0(it)))
+     **(1.d0-vvxt0)+(vtac(it)-vtac0(it))*(vvxt-vvxt0))
+     **(1.d0-vvx)*(1.d0-vvxp)**2*(1.d0-vvxpl)
+c vv(9): 0 proj. legs and 1 targ. leg
+      vv(9)=((1.d0-exp(-vpac(ip)))**2*(1.d0-vvxpl)
+     *+2.d0*(1.d0-exp(-vpac(ip)))*vvxpl)*(1.d0-vvx)*(1.d0-vvxt)
+     **((vtac0(it)+vtlc0)*exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+     *-(vtac(it)+vtlc-vtac0(it)-vtlc0)
+     **(1.d0-exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)))*exp(-vtac(it))
+c vv(10): 1 proj. leg and 0 targ. legs
+      vv(10)=((vpac0(ip)+vplc0)*exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+     *-(vpac(ip)+vplc-vpac0(ip)-vplc0)
+     **(1.d0-exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)))*exp(-vpac(ip))
+     **((1.d0-exp(-vtac(it)))**2*(1.d0-vvxtl)
+     *+2.d0*(1.d0-exp(-vtac(it)))*vvxtl)*(1.d0-vvx)*(1.d0-vvxp)
+c vv(11): 1 cut proj. leg and 1 targ. leg
+      vv(11)=2.d0*vplcp*((vtlc0-vtlcpe)
+     **exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+     *-(vtlc-vtlc0)*(1.d0-exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)))
+     **exp(-2.d0*vpac(ip)-vtac(it))
+     **(1.d0-vvx)*(1.d0-vvxp)**2*(1.d0-vvxpl)*(1.d0-vvxt)
+      if(xpomr*scm.lt.1.1d0*sgap**2)vv(11)=0.d0
+c vv(12): 1 proj. leg and 1 cut targ. leg
+      vv(12)=2.d0*vtlcp*((vplc0-vplcpe)
+     **exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+     *-(vplc-vplc0)*(1.d0-exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)))
+     **exp(-2.d0*vtac(it)-vpac(ip))
+     **(1.d0-vvx)*(1.d0-vvxp)*(1.d0-vvxt)**2*(1.d0-vvxtl)
+      if(xpomr*sgap**2.gt..9d0)vv(12)=0.d0
+
+      gb0=0.d0
+      do i=1,12
+       gb0=gb0+max(0.d0,vv(i))/4.d0
+      enddo
+
+      if(gb0.le.0.d0)then      !so170712
+       if(debug.ge.3)write (moniou,202)
+       iret=1
+       goto 31
+      endif
+      if(debug.ge.3)write (moniou,203)gb0
+
+1     continue
+      xpomr=(scm/sgap**2)**(-qgran(b10))/sgap   !proposed LC momentum for 3P-vertex
+      rp1=(rq(iddp(ip),icz)-alfp*dlog(xpomr))*4.d0*.0389d0
+      rp2=(rq(iddt(it),2)+alfp*dlog(xpomr*scm))*4.d0*.0389d0
+      rp=rp1*rp2/(rp1+rp2)
+      z=qgran(b10)
+      phi=pi*qgran(b10)
+      b0=dsqrt(-rp*dlog(z))
+      bbpr=(bpt*rp1/(rp1+rp2)+b0*cos(phi))**2+(b0*sin(phi))**2
+      bbtg=(bpt*rp2/(rp1+rp2)-b0*cos(phi))**2+(b0*sin(phi))**2
+      call qgbdef(bbpr,bbtg,xa(ip,1)+b,xa(ip,2),xb(it,1),xb(it,2)
+     *,xxp,yyp,int(1.5d0+qgran(b10)))   !determine coordinates for the vertex
+
+      call qgfdf(xxp,yyp,xpomr,vpac,vtac,vvx,vvxp,vvxt,vvxpl,vvxtl
+     *,ip,it)
+      vvxts=1.d0-(1.d0-vvx)*(1.d0-vvxt)*exp(-vtac(it))
+      vpl=qglegi(1.d0/xpomr,bbpr,iddp(ip),icz,2)
+      vplc=min(vpl
+     *,qgfani(1.d0/xpomr,bbpr,vvxts,vvxp,vvxpl,iddp(ip),icz,7))
+      vplc0=min(vplc
+     *,qgfani(1.d0/xpomr,bbpr,vvxts,vvxp,vvxpl,iddp(ip),icz,8))
+      vplcpe=min(vplc0
+     *,qgfani(1.d0/xpomr,bbpr,vvxts,vvxp,vvxpl,iddp(ip),icz,10))
+      vplcp=min(vplcpe
+     *,qgfani(1.d0/xpomr,bbpr,vvxts,vvxp,vvxpl,iddp(ip),icz,9))
+
+      vvxps=1.d0-(1.d0-vvx)*(1.d0-vvxp)*exp(-vpac(ip))
+      vtl=qglegi(xpomr*scm,bbtg,iddt(it),2,2)
+      vtlc=min(vtl
+     *,qgfani(xpomr*scm,bbtg,vvxps,vvxt,vvxtl,iddt(it),2,7))
+      vtlc0=min(vtlc
+     *,qgfani(xpomr*scm,bbtg,vvxps,vvxt,vvxtl,iddt(it),2,8))
+      vtlcpe=min(vtlc0
+     *,qgfani(xpomr*scm,bbtg,vvxps,vvxt,vvxtl,iddt(it),2,10))
+      vtlcp=min(vtlcpe
+     *,qgfani(xpomr*scm,bbtg,vvxps,vvxt,vvxtl,iddt(it),2,9))
+
+      sumcp0=0.d0
+      sumup=0.d0
+      do i=1,ia(1)
+       sumup=sumup+vpac(i)
+      enddo
+      vvxs=(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+      do i=1,ia(1)-ip+1
+       ipp=ia(1)-i+1
+       bbp=(xa(ipp,1)+b-xxp)**2+(xa(ipp,2)-yyp)**2
+       sumup=sumup-vpac(ipp)
+       vpac0(ipp)=min(vpac(ipp)
+     * ,qgfani(1.d0/xpomr,bbp,1.d0-vvxs*exp(-sumup)
+     * ,1.d0-exp(-sumcp0),1.d0-exp(-sumup),iddp(ipp),icz,3))
+       if(ipp.gt.ip)sumcp0=sumcp0+vpac0(ipp)
+      enddo
+      sumct0=0.d0
+      sumut=0.d0
+      do i=1,ia(2)
+       sumut=sumut+vtac(i)
+      enddo
+      vvxs=(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+      do i=1,ia(2)-it+1
+       itt=ia(2)-i+1
+       bbt=(xb(itt,1)-xxp)**2+(xb(itt,2)-yyp)**2
+       sumut=sumut-vtac(itt)
+       vtac0(itt)=min(vtac(itt)
+     * ,qgfani(xpomr*scm,bbt,1.d0-vvxs*exp(-sumut)
+     * ,1.d0-exp(-sumct0),1.d0-exp(-sumut),iddt(itt),2,3))
+       if(itt.gt.it)sumct0=sumct0+vtac0(itt)
+      enddo
+      vvxp0=1.d0-exp(-sumcp0)
+      vvxt0=1.d0-exp(-sumct0)
+
+c weights for vertex contributions:
+c vv(1): >1 proj. legs and >1 targ. legs
+      vv(1)=(max(0.d0,1.d0-exp(-2.d0*vpac(ip))*(1.d0+2.d0*vpac(ip)))
+     *+2.d0*vpac(ip)*exp(-2.d0*vpac(ip))*(1.d0-(1.d0-vvxp)**2))
+     **(max(0.d0,1.d0-exp(-2.d0*vtac(it))*(1.d0+2.d0*vtac(it)))
+     *+2.d0*vtac(it)*exp(-2.d0*vtac(it))*(1.d0-(1.d0-vvxt)**2))
+     **(1.d0-vvx)**2
+     *-2.d0*(max(0.d0,exp(vpac(ip)-vpac0(ip))-1.d0
+     *-(vpac(ip)-vpac0(ip)))
+     **(1.d0-vvxp0)+(vpac(ip)-vpac0(ip))*(vvxp-vvxp0))*exp(-vpac(ip))
+     **(max(0.d0,1.d0-exp(-2.d0*vtac(it))*(1.d0+2.d0*vtac(it)))
+     *+2.d0*vtac(it)*exp(-2.d0*vtac(it))*(1.d0-(1.d0-vvxt)**2))
+     **(1.d0-vvx)*(1.d0-vvxtl)
+     *-2.d0*(max(0.d0,1.d0-exp(-2.d0*vpac(ip))*(1.d0+2.d0*vpac(ip)))
+     *+2.d0*vpac(ip)*exp(-2.d0*vpac(ip))*(1.d0-(1.d0-vvxp)**2))
+     **(max(0.d0,exp(vtac(it)-vtac0(it))-1.d0-(vtac(it)-vtac0(it)))
+     **(1.d0-vvxt0)+(vtac(it)-vtac0(it))*(vvxt-vvxt0))*exp(-vtac(it))
+     **(1.d0-vvx)*(1.d0-vvxpl)
+c vv(2): 0 proj. legs and 0 targ. legs
+      vv(2)=((1.d0-exp(-vpac(ip)))**2*(1.d0-vvxpl)
+     *+2.d0*(1.d0-exp(-vpac(ip)))*vvxpl)
+     **((1.d0-exp(-vtac(it)))**2*(1.d0-vvxtl)
+     *+2.d0*(1.d0-exp(-vtac(it)))*vvxtl)*(1.d0-vvx)
+c vv(3): 0 proj. legs and >1 targ. legs
+      vv(3)=((1.d0-exp(-vpac(ip)))**2*(1.d0-vvxpl)
+     *+2.d0*(1.d0-exp(-vpac(ip)))*vvxpl)*(1.d0-vvx)
+     **((max(0.d0,1.d0-exp(-2.d0*vtac(it))*(1.d0+2.d0*vtac(it)))
+     *+2.d0*vtac(it)*exp(-2.d0*vtac(it))*(1.d0-(1.d0-vvxt)**2))
+     **(1.d0-vvxtl)
+     *-2.d0*(max(0.d0,exp(vtac(it)-vtac0(it))-1.d0
+     *-(vtac(it)-vtac0(it)))
+     **(1.d0-vvxt0)+(vtac(it)-vtac0(it))*(vvxt-vvxt0))*exp(-vtac(it)))
+c vv(4): >1 proj. legs and 0 targ. legs
+      vv(4)=((max(0.d0,1.d0-exp(-2.d0*vpac(ip))*(1.d0+2.d0*vpac(ip)))
+     *+2.d0*vpac(ip)*exp(-2.d0*vpac(ip))*(1.d0-(1.d0-vvxp)**2))
+     **(1.d0-vvxpl)
+     *-2.d0*(max(0.d0,exp(vpac(ip)-vpac0(ip))-1.d0
+     *-(vpac(ip)-vpac0(ip)))
+     **(1.d0-vvxp0)+(vpac(ip)-vpac0(ip))*(vvxp-vvxp0))*exp(-vpac(ip)))
+     **((1.d0-exp(-vtac(it)))**2*(1.d0-vvxtl)
+     *+2.d0*(1.d0-exp(-vtac(it)))*vvxtl)*(1.d0-vvx)
+c vv(5): 0 proj. legs and >1 targ. (handle) legs
+      vv(5)=4.d0*(1.d0-exp(-vpac(ip)))*(1.d0-vvx)
+     **(max(0.d0,exp(vtac(it)-vtac0(it))-1.d0-(vtac(it)-vtac0(it)))
+     **(1.d0-vvxt0)+(vtac(it)-vtac0(it))*(vvxt-vvxt0))*exp(-vtac(it))
+      if(xpomr*scm.le.sgap**2)vv(5)=0.d0
+c vv(6): >1 proj. (handle) legs and 0 targ. legs
+      vv(6)=4.d0*(max(0.d0,exp(vpac(ip)-vpac0(ip))-1.d0
+     *-(vpac(ip)-vpac0(ip)))*(1.d0-vvxp0)
+     *+(vpac(ip)-vpac0(ip))*(vvxp-vvxp0))*exp(-vpac(ip))
+     **(1.d0-exp(-vtac(it)))*(1.d0-vvx)
+      if(xpomr*sgap**2.ge.1.d0)vv(6)=0.d0
+c vv(7): >1 proj. legs and 1 targ. leg
+      vv(7)=(max(0.d0,1.d0-exp(-2.d0*vpac(ip))*(1.d0+2.d0*vpac(ip)))
+     *+2.d0*vpac(ip)*exp(-2.d0*vpac(ip))*(1.d0-(1.d0-vvxp)**2))
+     **((vtac0(it)+vtlc0)*exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+     *-(vtac(it)+vtlc-vtac0(it)-vtlc0)
+     **(1.d0-exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)))*exp(-vtac(it))
+     **(1.d0-vvx)*(1.d0-vvxpl)*(1.d0-vvxt)
+     *-2.d0*(max(0.d0,exp(vpac(ip)-vpac0(ip))-1.d0
+     *-(vpac(ip)-vpac0(ip)))
+     **(1.d0-vvxp0)+(vpac(ip)-vpac0(ip))*(vvxp-vvxp0))
+     **(vtac(it)+vtlc)*exp(-vpac(ip)-2.d0*vtac(it))
+     **(1.d0-vvx)*(1.d0-vvxt)**2*(1.d0-vvxtl)
+c vv(8): 1 proj. leg and >1 targ. legs
+      vv(8)=((vpac0(ip)+vplc0)*exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+     *-(vpac(ip)+vplc-vpac0(ip)-vplc0)
+     **(1.d0-exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)))*exp(-vpac(ip))
+     **(max(0.d0,1.d0-exp(-2.d0*vtac(it))*(1.d0+2.d0*vtac(it)))
+     *+2.d0*vtac(it)*exp(-2.d0*vtac(it))*(1.d0-(1.d0-vvxt)**2))
+     **(1.d0-vvx)*(1.d0-vvxp)*(1.d0-vvxtl)
+     *-2.d0*(vpac(ip)+vplc)*exp(-2.d0*vpac(ip)-vtac(it))
+     **(max(0.d0,exp(vtac(it)-vtac0(it))-1.d0-(vtac(it)-vtac0(it)))
+     **(1.d0-vvxt0)+(vtac(it)-vtac0(it))*(vvxt-vvxt0))
+     **(1.d0-vvx)*(1.d0-vvxp)**2*(1.d0-vvxpl)
+c vv(9): 0 proj. legs and 1 targ. leg
+      vv(9)=((1.d0-exp(-vpac(ip)))**2*(1.d0-vvxpl)
+     *+2.d0*(1.d0-exp(-vpac(ip)))*vvxpl)*(1.d0-vvx)*(1.d0-vvxt)
+     **((vtac0(it)+vtlc0)*exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+     *-(vtac(it)+vtlc-vtac0(it)-vtlc0)
+     **(1.d0-exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)))*exp(-vtac(it))
+c vv(10): 1 proj. leg and 0 targ. legs
+      vv(10)=((vpac0(ip)+vplc0)*exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+     *-(vpac(ip)+vplc-vpac0(ip)-vplc0)
+     **(1.d0-exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)))*exp(-vpac(ip))
+     **((1.d0-exp(-vtac(it)))**2*(1.d0-vvxtl)
+     *+2.d0*(1.d0-exp(-vtac(it)))*vvxtl)*(1.d0-vvx)*(1.d0-vvxp)
+c vv(11): 1 cut proj. leg and 1 targ. leg
+      vv(11)=2.d0*vplcp*((vtlc0-vtlcpe)
+     **exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+     *-(vtlc-vtlc0)*(1.d0-exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)))
+     **exp(-2.d0*vpac(ip)-vtac(it))
+     **(1.d0-vvx)*(1.d0-vvxp)**2*(1.d0-vvxpl)*(1.d0-vvxt)
+      if(xpomr*scm.lt.1.1d0*sgap**2)vv(11)=0.d0
+c vv(12): 1 proj. leg and 1 cut targ. leg
+      vv(12)=2.d0*vtlcp*((vplc0-vplcpe)
+     **exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+     *-(vplc-vplc0)*(1.d0-exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)))
+     **exp(-2.d0*vtac(it)-vpac(ip))
+     **(1.d0-vvx)*(1.d0-vvxp)*(1.d0-vvxt)**2*(1.d0-vvxtl)
+      if(xpomr*sgap**2.gt..9d0)vv(12)=0.d0
+
+      gb=0.d0
+      do i=1,12
+       vv(i)=max(0.d0,vv(i))
+       gb=gb+vv(i)/4.d0
+      enddo
+      gb=gb/gb0/z*rp/rp0  /max(2.d0,dlog10(scm)-1.d0)  /2.
+      if(debug.ge.5)write (moniou,204)xpomr,bbpr,bbtg,gb
+
+      if(qgran(b10).gt.gb)goto 1
+      if(debug.ge.3)write (moniou,205)xpomr,bbpr,bbtg,xxp,yyp
+
+      vplcng=min(vplc0
+     *,qgfani(1.d0/xpomr,bbpr,vvxts,vvxp,vvxpl,iddp(ip),icz,11))
+      vtlcng=min(vtlc0
+     *,qgfani(xpomr*scm,bbtg,vvxps,vvxt,vvxtl,iddt(it),2,11))
+
+      sumcpt=0.d0
+      sumcp0=0.d0
+      sumup=0.d0
+      vvxp0l=0.d0
+      do i=1,ia(1)
+       sumup=sumup+vpac(i)
+      enddo
+      vvxs=(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+      do i=1,ia(1)
+       ipp=ia(1)-i+1
+       bbp=(xa(ipp,1)+b-xxp)**2+(xa(ipp,2)-yyp)**2
+       sumup=sumup-vpac(ipp)
+       if(ipp.ge.ip)vpact(ipp)=max(vpac(ipp)
+     * ,qgfani(1.d0/xpomr,bbp,1.d0-vvxs*exp(-sumup)
+     * ,1.d0-exp(-sumcpt),1.d0-exp(-sumup),iddp(ipp),icz,6))
+       vpac0(ipp)=min(vpac(ipp)
+     * ,qgfani(1.d0/xpomr,bbp,1.d0-vvxs*exp(-sumup)
+     * ,1.d0-exp(-sumcp0),1.d0-exp(-sumup),iddp(ipp),icz,3))
+       if(ipp.gt.ip)then
+        sumcpt=sumcpt+vpact(ipp)
+       elseif(ipp.lt.ip)then
+        vvxp0l=vvxp0l+vpac0(ipp)
+       endif
+       sumcp0=sumcp0+vpac0(ipp)
+      enddo
+      sumctt=0.d0
+      sumct0=0.d0
+      sumut=0.d0
+      vvxt0l=0.d0
+      do i=1,ia(2)
+       sumut=sumut+vtac(i)
+      enddo
+      vvxs=(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+      do i=1,ia(2)
+       itt=ia(2)-i+1
+       bbt=(xb(itt,1)-xxp)**2+(xb(itt,2)-yyp)**2
+       sumut=sumut-vtac(itt)
+       if(itt.ge.it)vtact(itt)=max(vtac(itt)
+     * ,qgfani(xpomr*scm,bbt,1.d0-vvxs*exp(-sumut)
+     * ,1.d0-exp(-sumctt),1.d0-exp(-sumut),iddt(itt),2,6))
+       vtac0(itt)=min(vtac(itt)
+     * ,qgfani(xpomr*scm,bbt,1.d0-vvxs*exp(-sumut)
+     * ,1.d0-exp(-sumct0),1.d0-exp(-sumut),iddt(itt),2,3))
+       if(itt.gt.it)then
+        sumctt=sumctt+vtact(itt)
+       elseif(itt.lt.it)then
+        vvxt0l=vvxt0l+vtac0(itt)
+       endif
+       sumct0=sumct0+vtac0(itt)
+      enddo
+      vvxpt=1.d0-exp(-sumcpt)
+      vvxtt=1.d0-exp(-sumctt)
+      vvxp0l=1.d0-exp(-vvxp0l)
+      vvxt0l=1.d0-exp(-vvxt0l)
+
+      vvt=0.d0
+      do i=1,12
+       vvt=vvt+vv(i)
+      enddo
+      if(.not.(vvt.gt.0.d0))stop'vvt<0'
+
+      aks=qgran(b10)*vvt
+      do jt=1,12
+       aks=aks-vv(jt)
+       if(aks.lt.0.d0)goto 2
+      enddo
+      stop'jt>12!'
+
+2     continue
+      if(xpomr*scm.gt.sgap**2)then
+       wzgp=-2.d0*(1.d0-exp(-2.d0*vpac(ip)))*(1.d0-vvxpl)**2
+     * *(max(0.d0,1.d0-exp(-vtact(it))*(1.d0+vtact(it)))*(1.d0-vvxtt)
+     * -max(0.d0,1.d0-exp(-vtac0(it))*(1.d0+vtac0(it)))*(1.d0-vvxt0)
+     * +vtact(it)*exp(-vtact(it))*(1.d0-vvxtt
+     * -exp(vtact(it)-vtac(it))*(1.d0-vvxtl)*(1.d0-vvxt))
+     * -vtac0(it)*exp(-vtac0(it))*(1.d0-vvxt0
+     * -exp(vtac0(it)-vtac(it))*(1.d0-vvxtl)*(1.d0-vvxt)))
+      else
+       wzgp=0.d0
+      endif
+      if(xpomr*sgap**2.lt.1.d0)then
+       wzgt=-2.d0*(1.d0-exp(-2.d0*vtac(it)))*(1.d0-vvxtl)**2
+     * *(max(0.d0,1.d0-exp(-vpact(ip))*(1.d0+vpact(ip)))*(1.d0-vvxpt)
+     * -max(0.d0,1.d0-exp(-vpac0(ip))*(1.d0+vpac0(ip)))*(1.d0-vvxp0)
+     * +vpact(ip)*exp(-vpact(ip))*(1.d0-vvxpt
+     * -exp(vpact(ip)-vpac(ip))*(1.d0-vvxpl)*(1.d0-vvxp))
+     * -vpac0(ip)*exp(-vpac0(ip))*(1.d0-vvxp0
+     * -exp(vpac0(ip)-vpac(ip))*(1.d0-vvxpl)*(1.d0-vvxp)))
+      else
+       wzgt=0.d0
+      endif
+
+      nppr0=0
+      nptg0=0
+      npprh0=0
+      nptgh0=0
+      wgpr0=0.d0
+      wgtg0=0.d0
+      if(jt.eq.1.or.jt.eq.4.or.jt.eq.7)then
+       ntry=0
+3      ntry=ntry+1
+       npprh0=0
+       if(ip.eq.ia(1).or.ntry.gt.100)then
+        nppr0=npgen(2.d0*vpac(ip),2,20)
+        do i=1,nppr0
+         if(qgran(b10).le.vpac0(ip)/vpac(ip).or.xpomr*sgap**2.ge.1.d0)
+     *   then
+          itypr0(i)=0
+         else
+          npprh0=npprh0+1
+          itypr0(i)=1
+         endif
+         ippr0(i)=ip
+        enddo
+        wh=(vpac(ip)/vpac0(ip)-1.d0)/nppr0
+       else
+        nppr0=npgen(2.d0*vpac(ip),1,20)
+        do i=1,nppr0
+         if(qgran(b10).le.vpac0(ip)/vpac(ip).or.xpomr*sgap**2.ge.1.d0)
+     *   then
+          itypr0(i)=0
+         else
+          npprh0=npprh0+1
+          itypr0(i)=1
+         endif
+         ippr0(i)=ip
+        enddo
+        wh=(vpac(ip)/vpac0(ip)-1.d0)/nppr0
+        do ipp=ip+1,ia(1)
+         ninc=npgen(2.d0*vpac(ipp),0,20)
+         if(ninc.ne.0)then
+          nppr0=nppr0+ninc
+          nh0=npprh0
+          if(nppr0.gt.legmax)then
+           iret=1
+           goto 31
+          endif
+          do i=nppr0-ninc+1,nppr0
+           if(qgran(b10).le.vpac0(ipp)/vpac(ipp)
+     *     .or.xpomr*sgap**2.ge.1.d0)then
+            itypr0(i)=0
+           else
+            npprh0=npprh0+1
+            itypr0(i)=1
+           endif
+           ippr0(i)=ipp
+          enddo
+          if(ninc.gt.npprh0-nh0)wh=(vpac(ipp)/vpac0(ipp)-1.d0)/ninc
+         endif
+        enddo
+        if(nppr0.eq.1)goto 3
+       endif
+       if(nppr0.le.npprh0+1)then
+        if(jt.ne.7)then
+         wh0=1.d0-exp(vpac(ip)+(1.d0-nppr0)*dlog(2.d0))
+     *   /(1.d0-vvxp)/(1.d0-vvxpl)
+        else
+         wh0=1.d0-exp(vpac(ip)+(1.d0-nppr0)*dlog(2.d0))
+     *   /(1.d0-vvxp)/(1.d0-vvxpl)
+     *   *(vtac(it)+vtlc)*exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+     *   /((vtac0(it)+vtlc0)*exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+     *   -(vtac(it)+vtlc-vtac0(it)-vtlc0)
+     *   *(1.d0-exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)))
+        endif
+        if(nppr0.eq.npprh0.and.wh0.lt.0.d0
+     *  .or.nppr0.eq.npprh0+1.and.qgran(b10).gt.1.d0+wh*wh0)goto 3
+       endif
+      endif
+
+      if(jt.eq.1.or.jt.eq.3.or.jt.eq.8)then
+       ntry=0
+4      ntry=ntry+1
+       nptgh0=0
+       if(it.eq.ia(2).or.ntry.gt.100)then
+        nptg0=npgen(2.d0*vtac(it),2,20)
+        do i=1,nptg0
+         if(qgran(b10).le.vtac0(it)/vtac(it).or.xpomr*scm.le.sgap**2)
+     *   then
+          itytg0(i)=0
+         else
+          nptgh0=nptgh0+1
+          itytg0(i)=1
+         endif
+         iptg0(i)=it
+        enddo
+        wh=(vtac(it)/vtac0(it)-1.d0)/nptg0
+       else
+        nptg0=npgen(2.d0*vtac(it),1,20)
+        do i=1,nptg0
+         if(qgran(b10).le.vtac0(it)/vtac(it).or.xpomr*scm.le.sgap**2)
+     *   then
+          itytg0(i)=0
+         else
+          nptgh0=nptgh0+1
+          itytg0(i)=1
+         endif
+         iptg0(i)=it
+        enddo
+        wh=(vtac(it)/vtac0(it)-1.d0)/nptg0
+        do itt=it+1,ia(2)
+         ninc=npgen(2.d0*vtac(itt),0,20)
+         if(ninc.ne.0)then
+          nptg0=nptg0+ninc
+          nh0=nptgh0
+          if(nptg0.gt.legmax)then
+           iret=1
+           goto 31
+          endif
+          do i=nptg0-ninc+1,nptg0
+           if(qgran(b10).le.vtac0(itt)/vtac(itt)
+     *     .or.xpomr*scm.le.sgap**2) then
+            itytg0(i)=0
+           else
+            nptgh0=nptgh0+1
+            itytg0(i)=1
+           endif
+           iptg0(i)=itt
+          enddo
+          if(ninc.gt.nptgh0-nh0)wh=(vtac(itt)/vtac0(itt)-1.d0)/ninc
+         endif
+        enddo
+        if(nptg0.eq.1)goto 4
+       endif
+       if(nptg0.le.nptgh0+1)then
+        if(jt.ne.8)then
+         wh0=1.d0-exp(vtac(it)+(1.d0-nptg0)*dlog(2.d0))
+     *   /(1.d0-vvxt)/(1.d0-vvxtl)
+        else
+         wh0=1.d0-exp(vtac(it)+(1.d0-nptg0)*dlog(2.d0))
+     *   /(1.d0-vvxt)/(1.d0-vvxtl)
+     *   *(vpac(ip)+vplc)*exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+     *   /((vpac0(ip)+vplc0)*exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+     *   -(vpac(ip)+vplc-vpac0(ip)-vplc0)
+     *   *(1.d0-exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)))
+        endif
+        if(nptg0.eq.nptgh0.and.wh0.lt.0.d0
+     *  .or.nptg0.eq.nptgh0+1.and.qgran(b10).gt.1.d0+wh*wh0)goto 4
+       endif
+      endif
+
+      if(jt.eq.6)then
+       ntry=0
+5      ntry=ntry+1
+       if(ip.eq.ia(1).or.ntry.gt.100)then
+        nppr0=npgen(vpac(ip)-vpac0(ip),2,20)
+        do i=1,nppr0
+         itypr0(i)=1
+         ippr0(i)=ip
+        enddo
+       else
+        nppr0=npgen(vpac(ip)-vpac0(ip),1,20)
+        do i=1,nppr0
+         itypr0(i)=1
+         ippr0(i)=ip
+        enddo
+        do ipp=ip+1,ia(1)
+         ninc=npgen(vpac(ipp)-vpac0(ipp),0,20)
+         if(ninc.ne.0)then
+          nppr0=nppr0+ninc
+          if(nppr0.gt.legmax)then
+           iret=1
+           goto 31
+          endif
+          do i=nppr0-ninc+1,nppr0
+           itypr0(i)=1
+           ippr0(i)=ipp
+          enddo
+         endif
+        enddo
+        if(nppr0.eq.1)goto 5
+       endif
+      endif
+
+      if(jt.eq.5)then
+       ntry=0
+6      ntry=ntry+1
+       if(it.eq.ia(2).or.ntry.gt.100)then
+        nptg0=npgen(vtac(it)-vtac0(it),2,20)
+        do i=1,nptg0
+         itytg0(i)=1
+         iptg0(i)=it
+        enddo
+       else
+        nptg0=npgen(vtac(it)-vtac0(it),1,20)
+        do i=1,nptg0
+         itytg0(i)=1
+         iptg0(i)=it
+        enddo
+        do itt=it+1,ia(2)
+         ninc=npgen(vtac(itt)-vtac0(itt),0,20)
+         if(ninc.ne.0)then
+          nptg0=nptg0+ninc
+          if(nptg0.gt.legmax)then
+           iret=1
+           goto 31
+          endif
+          do i=nptg0-ninc+1,nptg0
+           itytg0(i)=1
+           iptg0(i)=itt
+          enddo
+         endif
+        enddo
+        if(nptg0.eq.1)goto 6
+       endif
+      endif
+
+      gbt=1.d0
+      if((jt.eq.1.and.nptgh0.lt.nptg0.or.jt.eq.4)
+     *.and.npprh0.eq.nppr0)then
+       gbt=1.d0-exp(vpac(ip)+(1.d0-nppr0)*dlog(2.d0))
+     * /(1.d0-vvxp)/(1.d0-vvxpl)
+      elseif((jt.eq.1.and.npprh0.lt.nppr0.or.jt.eq.3)
+     *.and.nptgh0.eq.nptg0)then
+       gbt=1.d0-exp(vtac(it)+(1.d0-nptg0)*dlog(2.d0))
+     * /(1.d0-vvxt)/(1.d0-vvxtl)
+      elseif(jt.eq.1.and.nptgh0.eq.nptg0.and.npprh0.eq.nppr0)then
+       gbt=1.d0-exp(vpac(ip)+(1.d0-nppr0)*dlog(2.d0))
+     * /(1.d0-vvxp)/(1.d0-vvxpl)
+     * -exp(vtac(it)+(1.d0-nptg0)*dlog(2.d0))/(1.d0-vvxt)/(1.d0-vvxtl)
+      elseif(jt.eq.7.and.npprh0.eq.nppr0)then
+       gbt=1.d0-exp(vpac(ip)+(1.d0-nppr0)*dlog(2.d0))
+     * /(1.d0-vvxp)/(1.d0-vvxpl)
+     * *(vtac(it)+vtlc)*exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+     * /((vtac0(it)+vtlc0)*exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+     * -(vtac(it)+vtlc-vtac0(it)-vtlc0)
+     * *(1.d0-exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)))
+      elseif(jt.eq.8.and.nptgh0.eq.nptg0)then
+       gbt=1.d0-exp(vtac(it)+(1.d0-nptg0)*dlog(2.d0))
+     * /(1.d0-vvxt)/(1.d0-vvxtl)
+     * *(vpac(ip)+vplc)*exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+     * /((vpac0(ip)+vplc0)*exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+     * -(vpac(ip)+vplc-vpac0(ip)-vplc0)
+     * *(1.d0-exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)))
+      endif
+      if(qgran(b10).gt.gbt)goto 2
+
+c less important part of 'zigzag' cuts - commented out (sub-per cent effect)
+c      if((jt.eq.1.or.jt.eq.8)
+c     *  .and.qgran(b10).lt.max(0.d0,wzgp/(vv(1)+vv(8))))nppr0=0
+c      if((jt.eq.1.or.jt.eq.7)
+c     *  .and.qgran(b10).lt.max(0.d0,wzgt/(vv(1)+vv(7))))nptg0=0
+
+      if(jt.eq.7.or.jt.eq.9.or.jt.eq.11.or.jt.eq.12)then
+       nptg0=1
+       iptg0(1)=it
+      endif
+      if(jt.eq.8.or.jt.eq.10.or.jt.eq.11.or.jt.eq.12)then
+       nppr0=1
+       ippr0(1)=ip
+      endif
+
+      if(jt.eq.8.and.nptgh0.lt.nptg0.or.jt.eq.10)then !'fan' from cut vertex
+       vpacng=min(vpac0(ip)
+     * ,qgfani(1.d0/xpomr,bbpr,vvxts,vvxp0,vvxpl,iddp(ip),icz,4))
+
+       factor=exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+       wng=(vpacng+vplcng)*factor
+       wgap=max(0.d0,(vpac0(ip)+vplc0)*factor
+     * -(vpac(ip)+vplc-vpac0(ip)-vplc0)*(1.d0-factor)-wng)
+       if(qgran(b10).ge.wgap/(wgap+wng).or.xpomr*sgap**2.gt..9d0)then
+        if(qgran(b10).lt.vpacng/(vpacng+vplcng)
+     *  .and.xpomr*sgap**2.lt..9d0)then
+         itypr0(1)=2            !cut 'fan' (no gap at the end)
+        else
+         itypr0(1)=4            !cut 'leg' (no gap at the end)
+        endif
+       else
+        wfg=max(0.d0,(vpac0(ip)-vpacng)*factor
+     *         -(vpac(ip)-vpac0(ip))*(1.d0-factor))
+        wlg=max(0.d0,(vplc0-vplcng)*factor-(vplc-vplc0)*(1.d0-factor))
+        if(qgran(b10).lt.wfg/(wfg+wlg))then
+         itypr0(1)=3            !cut 'fan' (gap at the end)
+        else
+         itypr0(1)=5            !cut 'leg' (gap at the end)
+        endif
+        wgpr0=(1.d0-factor)/factor
+       endif
+
+      elseif(jt.eq.8.and.nptgh0.eq.nptg0)then !'fan' from cut/uncut vertex
+       vpacng=min(vpac0(ip)
+     * ,qgfani(1.d0/xpomr,bbpr,vvxts,vvxp0,vvxpl,iddp(ip),icz,4))
+
+       factor=exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+       wng=(vpacng+vplcng)*factor*(1.d0-exp(vtac(it)
+     * +(1.d0-nptg0)*dlog(2.d0))/(1.d0-vvxt)/(1.d0-vvxtl))
+       wgap=max(0.d0,(vpac0(ip)+vplc0)*factor
+     * -(vpac(ip)+vplc-vpac0(ip)-vplc0)*(1.d0-factor)
+     * -exp(vtac(it)+(1.d0-nptg0)*dlog(2.d0))/(1.d0-vvxt)/(1.d0-vvxtl)
+     * *(vpac(ip)+vplc)*factor-wng)
+       if(qgran(b10).ge.wgap/(wgap+wng).or.xpomr*sgap**2.gt..9d0)then
+        if(qgran(b10).lt.vpacng/(vpacng+vplcng)
+     *  .and.xpomr*sgap**2.lt..9d0)then
+         itypr0(1)=2            !cut 'fan' (no gap at the end)
+        else
+         itypr0(1)=4            !cut 'leg' (no gap at the end)
+        endif
+       else
+        wfg=max(0.d0,(vpac0(ip)-vpacng)*factor
+     *         -(vpac(ip)-vpac0(ip))*(1.d0-factor)
+     *  -exp(vtac(it)+(1.d0-nptg0)*dlog(2.d0))
+     *  /(1.d0-vvxt)/(1.d0-vvxtl)*(vpac(ip)-vpacng)*factor)
+        wlg=max(0.d0,(vplc0-vplcng)*factor-(vplc-vplc0)*(1.d0-factor)
+     *  -exp(vtac(it)+(1.d0-nptg0)*dlog(2.d0))
+     *  /(1.d0-vvxt)/(1.d0-vvxtl)*(vplc-vplcng)*factor)
+        if(qgran(b10).lt.wfg/(wfg+wlg))then
+         itypr0(1)=3            !cut 'fan' (gap at the end)
+        else
+         itypr0(1)=5            !cut 'leg' (gap at the end)
+        endif
+        wgpr0=1.d0/factor/(1.d0-exp(vtac(it)+(1.d0-nptg0)*dlog(2.d0))
+     *  /(1.d0-vvxt)/(1.d0-vvxtl))-1.d0
+       endif
+
+      elseif(jt.eq.11)then
+       itypr0(1)=6
+      elseif(jt.eq.12)then
+       factor=exp(-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+       wng=max(0.d0,vplcng-vplcpe)*factor
+     * /((vplc0-vplcpe)*factor-(vplc-vplc0)*(1.d0-factor))
+       if(qgran(b10).le.wng)then
+        itypr0(1)=7            !cut 'leg' (>1 cut Poms at the end)
+       else
+        itypr0(1)=5            !cut 'leg' (gap at the end)
+        wgpr0=(1.d0-factor)/factor
+       endif
+      endif
+
+      if(jt.eq.7.and.npprh0.lt.nppr0.or.jt.eq.9)then !'fan' from cut vertex
+       vtacng=min(vtac0(it)
+     * ,qgfani(xpomr*scm,bbtg,vvxps,vvxt0,vvxtl,iddt(it),2,4))
+
+       factor=exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+       wng=(vtacng+vtlcng)*factor
+       wgap=max(0.d0,(vtac0(it)+vtlc0)*factor
+     * -(vtac(it)+vtlc-vtac0(it)-vtlc0)*(1.d0-factor)-wng)
+       if(qgran(b10).ge.wgap/(wgap+wng)
+     * .or.xpomr*scm.lt.1.1d0*sgap**2)then
+        if(qgran(b10).lt.vtacng/(vtacng+vtlcng)
+     *  .and.xpomr*scm.gt.1.1d0*sgap**2)then
+         itytg0(1)=2            !cut 'fan' (no gap at the end)
+        else
+         itytg0(1)=4            !cut 'leg' (no gap at the end)
+        endif
+       else
+        wfg=max(0.d0,(vtac0(it)-vtacng)*factor
+     *         -(vtac(it)-vtac0(it))*(1.d0-factor))
+        wlg=max(0.d0,(vtlc0-vtlcng)*factor-(vtlc-vtlc0)*(1.d0-factor))
+        if(qgran(b10).lt.wfg/(wfg+wlg))then
+         itytg0(1)=3            !cut 'fan' (gap at the end)
+        else
+         itytg0(1)=5            !cut 'leg' (gap at the end)
+        endif
+        wgtg0=(1.d0-factor)/factor
+       endif
+
+      elseif(jt.eq.7.and.npprh0.eq.nppr0)then !'fan' from cut/uncut vertex
+       vtacng=min(vtac0(it)
+     * ,qgfani(xpomr*scm,bbtg,vvxps,vvxt0,vvxtl,iddt(it),2,4))
+
+       factor=exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+       wng=(vtacng+vtlcng)*factor*(1.d0-exp(vpac(ip)
+     * +(1.d0-nppr0)*dlog(2.d0))/(1.d0-vvxp)/(1.d0-vvxpl))
+       wgap=max(0.d0,(vtac0(it)+vtlc0)*factor
+     * -(vtac(it)+vtlc-vtac0(it)-vtlc0)*(1.d0-factor)
+     * -exp(vpac(ip)+(1.d0-nppr0)*dlog(2.d0))/(1.d0-vvxp)/(1.d0-vvxpl)
+     * *(vtac(it)+vtlc)*factor-wng)
+       if(qgran(b10).ge.wgap/(wgap+wng)
+     * .or.xpomr*scm.lt.1.1d0*sgap**2)then
+        if(qgran(b10).lt.vtacng/(vtacng+vtlcng)
+     *  .and.xpomr*scm.gt.1.1d0*sgap**2)then
+         itytg0(1)=2            !cut 'fan' (no gap at the end)
+        else
+         itytg0(1)=4            !cut 'leg' (no gap at the end)
+        endif
+       else
+        wfg=max(0.d0,(vtac0(it)-vtacng)*factor
+     *         -(vtac(it)-vtac0(it))*(1.d0-factor)
+     *  -exp(vpac(ip)+(1.d0-nppr0)*dlog(2.d0))
+     *  /(1.d0-vvxp)/(1.d0-vvxpl)*(vtac(it)-vtacng)*factor)
+        wlg=max(0.d0,(vtlc0-vtlcng)*factor-(vtlc-vtlc0)*(1.d0-factor)
+     *  -exp(vpac(ip)+(1.d0-nppr0)*dlog(2.d0))
+     *  /(1.d0-vvxp)/(1.d0-vvxpl)*(vtlc-vtlcng)*factor)
+        if(qgran(b10).lt.wfg/(wfg+wlg))then
+         itytg0(1)=3            !cut 'fan' (gap at the end)
+        else
+         itytg0(1)=5            !cut 'leg' (gap at the end)
+        endif
+        wgtg0=1.d0/factor/(1.d0-exp(vpac(ip)+(1.d0-nppr0)*dlog(2.d0))
+     *  /(1.d0-vvxp)/(1.d0-vvxpl))-1.d0
+       endif
+
+      elseif(jt.eq.12)then
+       itytg0(1)=6
+      elseif(jt.eq.11)then
+       factor=exp(-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+       wng=max(0.d0,vtlcng-vtlcpe)*factor
+     * /((vtlc0-vtlcpe)*factor-(vtlc-vtlc0)*(1.d0-factor))
+       if(qgran(b10).le.wng)then
+        itytg0(1)=7            !cut 'leg' (>1 cut Poms at the end)
+       else
+        itytg0(1)=5            !cut 'leg' (gap at the end)
+        wgtg0=(1.d0-factor)/factor
+       endif
+      endif
+      if(debug.ge.3)write (moniou,206)nppr0,nptg0
+
+      nppr=0
+      nptg=0
+      npin=0
+
+      if(nppr0.eq.1.and.itypr0(1).eq.6)then     !single cut Pomeron
+       nppr=1
+       xpompi(nppr)=xpomr
+       vvxpi(nppr)=1.d0-(1.d0-vvx)*(1.d0-vvxp)*(1.d0-vvxt)
+     * *exp(-vtac(it))
+       ipompi(nppr)=ip
+       bpompi(nppr)=bbpr
+       if(debug.ge.4)write (moniou,209)nppr,ip,bbpr,xpompi(nppr)
+     * ,vvxpi(nppr)
+       nppr0=0
+      endif
+      if(nptg0.eq.1.and.itytg0(1).eq.6)then     !single cut Pomeron
+       nptg=1
+       xpomti(nptg)=xpomr
+       vvxti(nptg)=1.d0-(1.d0-vvx)*(1.d0-vvxp)*(1.d0-vvxt)
+     * *exp(-vpac(ip))
+       ipomti(nptg)=it
+       bpomti(nptg)=bbtg
+       if(debug.ge.4)write (moniou,217)nptg,it,bbtg,xpomti(nptg)
+     * ,vvxti(nptg)
+       nptg0=0
+      endif
+
+      vvxps=vvxp
+      vvxpls=vvxpl
+      vvxp0s=vvxp0
+      if(nppr0.ne.0)then
+       i=0
+7      i=i+1
+       ityp=itypr0(i)
+       if(ityp.eq.0.or.ityp.eq.2.or.ityp.eq.4)then
+        ipp=ippr0(i)
+        bbp=(xa(ipp,1)+b-xxp)**2+(xa(ipp,2)-yyp)**2
+        vvxp=0.d0
+        vvxpl=0.d0
+        vvxp0=0.d0
+        if(ia(1).gt.1)then
+         do l=1,ia(1)
+          if(l.lt.ipp)then
+           vvxpl=vvxpl+vpac(l)
+          elseif(l.gt.ipp)then
+           vvxp=vvxp+vpac(l)
+           vvxp0=vvxp0+vpac0(l)
+          endif
+         enddo
+        endif
+        vvxp=1.d0-exp(-vvxp)
+        vvxpl=1.d0-exp(-vvxpl)
+        vvxp0=1.d0-exp(-vvxp0)
+        vvxts=1.d0-(1.d0-vvxt)*(1.d0-vvxtl)*(1.d0-vvxpl)*exp(-vtac(it))
+        if(ityp.ne.4)then
+         vpacng=min(vpac0(ipp)
+     *   ,qgfani(1.d0/xpomr,bbp,vvxts,vvxp0,vvxpl,iddp(ipp),icz,4))
+         vpacpe=min(vpacng
+     *   ,qgfani(1.d0/xpomr,bbp,vvxts,vvxp0,vvxpl,iddp(ipp),icz,5))
+         vplcp=min(vpacpe
+     *   ,qgfani(1.d0/xpomr,bbp,vvxts,vvxp,vvxpl,iddp(ipp),icz,9))
+        else
+         vplcng=min(vpac0(ipp)
+     *   ,qgfani(1.d0/xpomr,bbp,vvxts,vvxp,vvxpl,iddp(ipp),icz,11))
+         vplcpe=min(vplcng
+     *   ,qgfani(1.d0/xpomr,bbp,vvxts,vvxp,vvxpl,iddp(ipp),icz,10))
+         vplcp=min(vplcpe
+     *   ,qgfani(1.d0/xpomr,bbp,vvxts,vvxp,vvxpl,iddp(ipp),icz,9))
+        endif
+
+        if(ityp.eq.0)then
+         aks=qgran(b10)*vpac0(ipp)
+         if(aks.le.vplcp.or.xpomr*sgap**2.gt..9d0)then
+          itypr0(i)=6        !single cut Pomeron
+         elseif(aks.lt.vpacpe)then
+          itypr0(i)=-1       !'fan' (cut Pomeron end)
+         elseif(aks.lt.vpacng)then
+          itypr0(i)=2        !'fan' (>1 cut Poms at the end)
+         endif
+        elseif(ityp.eq.2)then
+         aks=qgran(b10)*vpacng
+         if(aks.le.vplcp.or.xpomr*sgap**2.gt..9d0)then
+          itypr0(i)=6        !single cut Pomeron
+         elseif(aks.lt.vpacpe)then
+          itypr0(i)=-1       !'fan' (cut Pomeron end)
+         endif
+        elseif(ityp.eq.4)then
+         aks=qgran(b10)*vplcng
+         if(aks.le.vplcp.or.xpomr*sgap**2.gt..9d0)then
+          itypr0(i)=6        !single cut Pomeron
+         elseif(aks.gt.vplcpe.or.xpomr*sgap**3.gt..9d0)then
+          itypr0(i)=7        !'leg' (>1 cut Poms at the end)
+         endif
+        endif
+
+        if(itypr0(i).eq.6)then        !single cut Pomeron
+         nppr=nppr+1
+         xpompi(nppr)=xpomr
+         vvxpi(nppr)=1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*(1.d0-vvxt)
+     *   *(1.d0-vvxtl)*exp(-vtac(it))
+         ipompi(nppr)=ipp
+         bpompi(nppr)=bbp
+         if(debug.ge.4)write (moniou,209)nppr,ipp,bbp,xpompi(nppr)
+     *   ,vvxpi(nppr)
+         nppr0=nppr0-1
+         if(nppr0.ge.i)then
+          do l=i,nppr0
+           ippr0(l)=ippr0(l+1)
+           itypr0(l)=itypr0(l+1)
+          enddo
+         endif
+         i=i-1
+        endif
+       endif
+       if(i.lt.nppr0)goto 7
+      endif
+
+      vvxp=vvxps
+      vvxpl=vvxpls
+      vvxp0=vvxp0s
+      vvxts=vvxt
+      vvxtls=vvxtl
+      vvxt0s=vvxt0
+      if(nptg0.ne.0)then
+       i=0
+8      i=i+1
+       ityt=itytg0(i)
+       if(ityt.eq.0.or.ityt.eq.2.or.ityt.eq.4)then
+        itt=iptg0(i)
+        bbt=(xb(itt,1)-xxp)**2+(xb(itt,2)-yyp)**2
+        vvxt=0.d0
+        vvxtl=0.d0
+        vvxt0=0.d0
+        if(ia(2).gt.1)then
+         do l=1,ia(2)
+          if(l.lt.itt)then
+           vvxtl=vvxtl+vtac(l)
+          elseif(l.gt.itt)then
+           vvxt=vvxt+vtac(l)
+           vvxt0=vvxt0+vtac0(l)
+          endif
+         enddo
+        endif
+        vvxt=1.d0-exp(-vvxt)
+        vvxtl=1.d0-exp(-vvxtl)
+        vvxt0=1.d0-exp(-vvxt0)
+        vvxps=1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*(1.d0-vvxtl)*exp(-vpac(ip))
+        if(ityt.ne.4)then
+         vtacng=min(vtac0(itt)
+     *   ,qgfani(xpomr*scm,bbt,vvxps,vvxt0,vvxtl,iddt(itt),2,4))
+         vtacpe=min(vtacng
+     *   ,qgfani(xpomr*scm,bbt,vvxps,vvxt0,vvxtl,iddt(itt),2,5))
+         vtlcp=min(vtacpe
+     *   ,qgfani(xpomr*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,9))
+        else
+         vtlcng=min(vtac0(itt)
+     *   ,qgfani(xpomr*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,11))
+         vtlcpe=min(vtlcng
+     *   ,qgfani(xpomr*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,10))
+         vtlcp=min(vtlcpe
+     *   ,qgfani(xpomr*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,9))
+        endif
+
+        if(ityt.eq.0)then
+         aks=qgran(b10)*vtac0(itt)
+         if(aks.le.vtlcp.or.xpomr*scm.lt.1.1d0*sgap**2)then
+          itytg0(i)=6        !single cut Pomeron
+         elseif(aks.lt.vtacpe)then
+          itytg0(i)=-1       !'fan' (cut Pomeron end)
+         elseif(aks.lt.vtacng)then
+          itytg0(i)=2        !'fan' (>1 cut Poms at the end)
+         endif
+        elseif(ityt.eq.2)then
+         aks=qgran(b10)*vtacng
+         if(aks.le.vtlcp.or.xpomr*scm.lt.1.1d0*sgap**2)then
+          itytg0(i)=6        !single cut Pomeron
+         elseif(aks.lt.vtacpe)then
+          itytg0(i)=-1       !'fan' (cut Pomeron end)
+         endif
+        elseif(ityt.eq.4)then
+         aks=qgran(b10)*vtlcng
+         if(aks.le.vtlcp.or.xpomr*scm.lt.1.1d0*sgap**2)then
+          itytg0(i)=6
+         elseif(aks.gt.vtlcpe.or.xpomr*scm.lt.1.1d0*sgap**3)then
+          itytg0(i)=7        !'leg' (>1 cut Poms at the end)
+         endif
+        endif
+
+        if(itytg0(i).eq.6)then        !single cut Pomeron
+         nptg=nptg+1
+         xpomti(nptg)=xpomr
+         vvxti(nptg)=1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*(1.d0-vvxt)
+     *   *(1.d0-vvxtl)*exp(-vpac(ip))
+         ipomti(nptg)=itt
+         bpomti(nptg)=bbt
+         if(debug.ge.4)write (moniou,217)nptg,itt,bbt,xpomti(nptg)
+     *   ,vvxti(nptg)
+         nptg0=nptg0-1
+         if(nptg0.ge.i)then
+          do l=i,nptg0
+           iptg0(l)=iptg0(l+1)
+           itytg0(l)=itytg0(l+1)
+          enddo
+         endif
+         i=i-1
+        endif
+       endif
+       if(i.lt.nptg0)goto 8
+      endif
+      vvxt=vvxts
+      vvxtl=vvxtls
+      vvxt0=vvxt0s
+
+      if((jt-1)*(jt-4)*(jt-7).eq.0.and.xpomr*sgap**2.lt..9d0)then
+       vvxts=1.d0-(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+       vvxt0s=1.d0-(1.d0-vvxt0)*(1.d0-vvxt0l)*exp(-vtac0(it))
+       vvxs=((1.d0-vvxp)*(1.d0-vvxpl))**2*exp(-2.d0*vpac(ip))
+       vvx0s=((1.d0-vvxp0)*(1.d0-vvxp0l))**2*exp(-2.d0*vpac0(ip))
+
+       wzzp=2.d0*qgrevi(1.d0/xpomr,bbpr,vvxt0s,vvxts
+     * ,vvxpt,vvxp0,vvxpl,iddp(ip),icz)
+     * *((1.d0-exp(-vtact(it)))*(1.d0-vvxtt)*(1.d0-vvxs)
+     * +vvxs*(max(0.d0,1.d0-exp(-vtact(it))*(1.d0+vtact(it)))
+     * *(1.d0-vvxtt)
+     * -max(0.d0,1.d0-exp(-vtac0(it))*(1.d0+vtac0(it)))*(1.d0-vvxt0))
+     * +vtact(it)*exp(-vtact(it))*((1.d0-vvxtt)*vvxs
+     * -exp(vtact(it)-vtac0(it))*(1.d0-vvxt0)*(1.d0-vvxt0l)*vvx0s)
+     * -vtac0(it)*exp(-vtac0(it))*(1.d0-vvxt0)
+     * *(vvxs-vvx0s+vvxt0l*vvx0s))
+       wzzp=max(0.d0,wzzp)
+       nzzp=npgen(wzzp/(vv(1)+vv(4)+vv(7)),0,50)
+      else
+       nzzp=0
+      endif
+
+      if((jt-1)*(jt-3)*(jt-8).eq.0.and.xpomr*scm.gt.1.1d0*sgap**2)then
+       vvxps=1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+       vvxp0s=1.d0-(1.d0-vvxp0)*(1.d0-vvxp0l)*exp(-vpac0(ip))
+       vvxs=((1.d0-vvxt)*(1.d0-vvxtl))**2*exp(-2.d0*vtac(it))
+       vvx0s=((1.d0-vvxt0)*(1.d0-vvxt0l))**2*exp(-2.d0*vtac0(it))
+       wzzt=2.d0*qgrevi(xpomr*scm,bbtg,vvxp0s,vvxps
+     * ,vvxtt,vvxt0,vvxtl,iddt(it),2)
+     * *((1.d0-exp(-vpact(ip)))*(1.d0-vvxpt)*(1.d0-vvxs)
+     * +vvxs*(max(0.d0,1.d0-exp(-vpact(ip))*(1.d0+vpact(ip)))
+     * *(1.d0-vvxpt)
+     * -max(0.d0,1.d0-exp(-vpac0(ip))*(1.d0+vpac0(ip)))*(1.d0-vvxp0))
+     * +vpact(ip)*exp(-vpact(ip))*((1.d0-vvxpt)*vvxs
+     * -exp(vpact(ip)-vpac0(ip))*(1.d0-vvxp0)*(1.d0-vvxp0l)*vvx0s)
+     * -vpac0(ip)*exp(-vpac0(ip))*(1.d0-vvxp0)
+     * *(vvxs-vvx0s+vvxp0l*vvx0s))
+       wzzt=max(0.d0,wzzt)
+       nzzt=npgen(wzzt/(vv(1)+vv(3)+vv(8)),0,50)
+      else
+       nzzt=0
+      endif
+
+      if(nzzp.ne.0)then
+       bpm=(xa(ip,1)+b-xxp)**2+(xa(ip,2)-yyp)**2
+       xpomr0=min(dsqrt(xpomr),1.d0/sgap)
+       xpomr0=max(xpomr0,xpomr*sgap)
+       rp1=(rq(iddp(ip),icz)-alfp*dlog(xpomr0))*4.d0*.0389d0
+       rp2=alfp*dlog(xpomr0/xpomr)*4.d0*.0389d0
+       rp0=rp1*rp2/(rp1+rp2)
+       bbp=bpm*(rp1/(rp1+rp2))**2
+       bbi=bpm*(rp2/(rp1+rp2))**2
+       call qgbdef(bbp,bbi,xa(ip,1)+b,xa(ip,2),xxp,yyp,xxp0,yyp0,1)
+       call qgfdf(xxp0,yyp0,xpomr0,vpac,vtac,vvx,vvxp,vvxt
+     * ,vvxpl,vvxtl,ip,it)
+
+       sumcp0=0.d0
+       sumcpt=0.d0
+       sumup=0.d0
+       vvxp0=0.d0
+       vvxp0l=0.d0
+       do i=1,ia(1)
+        sumup=sumup+vpac(i)
+       enddo
+       vvxs=(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+       do i=1,ia(1)
+        ipp=ia(1)-i+1
+        bbpi=(xa(ipp,1)+b-xxp0)**2+(xa(ipp,2)-yyp0)**2
+        sumup=sumup-vpac(ipp)
+        vpac0(ipp)=min(vpac(ipp)
+     *  ,qgfani(1.d0/xpomr0,bbpi,1.d0-vvxs*exp(-sumup)
+     *  ,1.d0-exp(-sumcp0),1.d0-exp(-sumup),iddp(ipp),icz,3))
+        if(ipp.ge.ip)vpact(ipp)=max(vpac(ipp)
+     *  ,qgfani(1.d0/xpomr0,bbpi,1.d0-vvxs*exp(-sumup)
+     *  ,1.d0-exp(-sumcpt),1.d0-exp(-sumup),iddp(ipp),icz,6))
+        if(ipp.gt.ip)then
+         vvxp0=vvxp0+vpac0(ipp)
+         sumcpt=sumcpt+vpact(ipp)
+        elseif(ipp.lt.ip)then
+         vvxp0l=vvxp0l+vpac0(ipp)
+        endif
+        sumcp0=sumcp0+vpac0(ipp)
+       enddo
+       vvxpt=1.d0-exp(-sumcpt)
+       vvxp0=1.d0-exp(-vvxp0)
+       vvxp0l=1.d0-exp(-vvxp0l)
+
+       sumut=0.d0
+       sumct0=0.d0
+       vvxt0=0.d0
+       vvxt0l=0.d0
+       do i=1,ia(2)
+        sumut=sumut+vtac(i)
+       enddo
+       vvxs=(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+       do i=1,ia(2)
+        itt=ia(2)-i+1
+        bbti=(xb(itt,1)-xxp0)**2+(xb(itt,2)-yyp0)**2
+        sumut=sumut-vtac(itt)
+        vtac0(itt)=min(vtac(itt)
+     *  ,qgfani(xpomr0*scm,bbti,1.d0-vvxs*exp(-sumut)
+     *  ,1.d0-exp(-sumct0),1.d0-exp(-sumut),iddt(itt),2,3))
+        if(itt.gt.it)then
+         vvxt0=vvxt0+vtac0(itt)
+        elseif(itt.lt.it)then
+        vvxt0l=vvxt0l+vtac0(itt)
+        endif
+        sumct0=sumct0+vtac0(itt)
+       enddo
+       vvxt0=1.d0-exp(-vvxt0)
+       vvxt0l=1.d0-exp(-vvxt0l)
+
+       viu=qgpini(xpomr0/xpomr,bbi,0.d0,0.d0,2)
+       vim=2.d0*min(viu,qgpini(xpomr0/xpomr,bbi,0.d0,0.d0,8))
+       vvxpin=1.d0-(1.d0-vvxp0)*(1.d0-vvxp0l)*exp(-vpac0(ip))
+       vvxtin=1.d0-(1.d0-vvxt0)*(1.d0-vvxt0l)*exp(-vtac0(it))
+       vi=qgpini(xpomr0/xpomr,bbi,vvxpin,vvxtin,21)*(1.d0-exp(-viu))
+     * -qgpini(xpomr0/xpomr,bbi,vvxpin,vvxtin,23)*((1.d0-exp(-viu))**2
+     * +(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))/2.d0
+
+       vvx0s=(1.d0-vvxtin)**2
+       vvxs=((1.d0-vvxt)*(1.d0-vvxtl))**2*exp(-2.d0*vtac(it))
+
+       gb0=vi                        *15.
+     * *((1.d0-exp(-vpact(ip)))*(1.d0-vvxpt)*(1.d0-vvxs)
+     * +vvxs*(max(0.d0,1.d0-exp(-vpact(ip))*(1.d0+vpact(ip)))
+     * *(1.d0-vvxpt)
+     * -max(0.d0,1.d0-exp(-vpac0(ip))*(1.d0+vpac0(ip)))*(1.d0-vvxp0))
+     * +vpact(ip)*exp(-vpact(ip))*((1.d0-vvxpt)*vvxs
+     * -exp(vpact(ip)-vpac0(ip))*(1.d0-vvxp0)*(1.d0-vvxp0l)*vvx0s)
+     * -vpac0(ip)*exp(-vpac0(ip))*(1.d0-vvxp0)
+     * *(vvxs-vvx0s+vvxp0l*vvx0s))
+
+       do in=1,nzzp
+        nrej=0
+32      xpomri=(xpomr*sgap**2)**qgran(b10)/sgap
+        rp1=(rq(iddp(ip),icz)-alfp*dlog(xpomri))*4.d0*.0389d0
+        rp2=alfp*dlog(xpomri/xpomr)*4.d0*.0389d0
+        rp=rp1*rp2/(rp1+rp2)
+        z=qgran(b10)
+        phi=pi*qgran(b10)
+        b0=dsqrt(-rp*dlog(z))
+        bbp=(dsqrt(bpm)*rp1/(rp1+rp2)+b0*cos(phi))**2+(b0*sin(phi))**2
+        bbi=(dsqrt(bpm)*rp2/(rp1+rp2)-b0*cos(phi))**2+(b0*sin(phi))**2
+        call qgbdef(bbp,bbi,xa(ip,1)+b,xa(ip,2),xxp,yyp
+     *  ,xxi,yyi,int(1.5d0+qgran(b10)))   !coordinates for the vertex
+        call qgfdf(xxi,yyi,xpomri,vpac,vtac
+     *  ,vvx,vvxp,vvxt,vvxpl,vvxtl,ip,it)
+
+        sumcp0=0.d0
+        sumcpt=0.d0
+        sumup=0.d0
+        vvxp0=0.d0
+        vvxp0l=0.d0
+        do i=1,ia(1)
+         sumup=sumup+vpac(i)
+        enddo
+        vvxs=(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+        do i=1,ia(1)
+         ipp=ia(1)-i+1
+         bbpi=(xa(ipp,1)+b-xxi)**2+(xa(ipp,2)-yyi)**2
+         sumup=sumup-vpac(ipp)
+         vpac0(ipp)=min(vpac(ipp)
+     *   ,qgfani(1.d0/xpomri,bbpi,1.d0-vvxs*exp(-sumup)
+     *   ,1.d0-exp(-sumcp0),1.d0-exp(-sumup),iddp(ipp),icz,3))
+         if(ipp.ge.ip)vpact(ipp)=max(vpac(ipp)
+     *   ,qgfani(1.d0/xpomri,bbpi,1.d0-vvxs*exp(-sumup)
+     *   ,1.d0-exp(-sumcpt),1.d0-exp(-sumup),iddp(ipp),icz,6))
+         if(ipp.gt.ip)then
+          vvxp0=vvxp0+vpac0(ipp)
+          sumcpt=sumcpt+vpact(ipp)
+         elseif(ipp.lt.ip)then
+          vvxp0l=vvxp0l+vpac0(ipp)
+         endif
+         sumcp0=sumcp0+vpac0(ipp)
+        enddo
+        vvxpt=1.d0-exp(-sumcpt)
+        vvxp0=1.d0-exp(-vvxp0)
+        vvxp0l=1.d0-exp(-vvxp0l)
+
+        sumut=0.d0
+        sumct0=0.d0
+        vvxt0=0.d0
+        vvxt0l=0.d0
+        do i=1,ia(2)
+         sumut=sumut+vtac(i)
+        enddo
+        vvxs=(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+        do i=1,ia(2)
+         itt=ia(2)-i+1
+         bbti=(xb(itt,1)-xxi)**2+(xb(itt,2)-yyi)**2
+         sumut=sumut-vtac(itt)
+         vtac0(itt)=min(vtac(itt)
+     *   ,qgfani(xpomri*scm,bbti,1.d0-vvxs*exp(-sumut)
+     *   ,1.d0-exp(-sumct0),1.d0-exp(-sumut),iddt(itt),2,3))
+         if(itt.gt.it)then
+          vvxt0=vvxt0+vtac0(itt)
+         elseif(itt.lt.it)then
+          vvxt0l=vvxt0l+vtac0(itt)
+         endif
+         sumct0=sumct0+vtac0(itt)
+        enddo
+        vvxt0=1.d0-exp(-vvxt0)
+        vvxt0l=1.d0-exp(-vvxt0l)
+
+        viu=qgpini(xpomri/xpomr,bbi,0.d0,0.d0,2)
+        vim=2.d0*min(viu,qgpini(xpomri/xpomr,bbi,0.d0,0.d0,8))
+        vvxpin=1.d0-(1.d0-vvxp0)*(1.d0-vvxp0l)*exp(-vpac0(ip))
+        vvxtin=1.d0-(1.d0-vvxt0)*(1.d0-vvxt0l)*exp(-vtac0(it))
+        vi=qgpini(xpomri/xpomr,bbi,vvxpin,vvxtin,21)*(1.d0-exp(-viu))
+     *  -qgpini(xpomri/xpomr,bbi,vvxpin,vvxtin,23)*((1.d0-exp(-viu))**2
+     *  +(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))/2.d0
+
+        vvx0s=(1.d0-vvxtin)**2
+        vvxs=((1.d0-vvxt)*(1.d0-vvxtl))**2*exp(-2.d0*vtac(it))
+
+        gb=vi
+     *  *((1.d0-exp(-vpact(ip)))*(1.d0-vvxpt)*(1.d0-vvxs)
+     *  +vvxs*(max(0.d0,1.d0-exp(-vpact(ip))*(1.d0+vpact(ip)))
+     *  *(1.d0-vvxpt)
+     *  -max(0.d0,1.d0-exp(-vpac0(ip))*(1.d0+vpac0(ip)))*(1.d0-vvxp0))
+     *  +vpact(ip)*exp(-vpact(ip))*((1.d0-vvxpt)*vvxs
+     *  -exp(vpact(ip)-vpac0(ip))*(1.d0-vvxp0)*(1.d0-vvxp0l)*vvx0s)
+     *  -vpac0(ip)*exp(-vpac0(ip))*(1.d0-vvxp0)
+     *  *(vvxs-vvx0s+vvxp0l*vvx0s))
+
+        gb=gb/gb0/z*rp/rp0
+        nrej=nrej+1
+        if(qgran(b10).gt.gb.and.nrej.lt.10000)goto 32
+
+        vi1p=qgpini(xpomri/xpomr,bbi,1.d0-(1.d0-vvxpin)**2*vvx0s
+     *  ,0.d0,16)*exp(-vim)
+        vimp=max(0.d0,(1.d0-exp(-vim)*(1.d0+vim)))/2.d0
+
+        if(qgran(b10).le.(vi1p+vimp)/vi
+     *  .or.xpomri/xpomr.lt.1.1d0*sgap**2)then
+         if(qgran(b10).le.vi1p/(vi1p+vimp))then   !single cut Pomeron
+          npin=npin+1
+          if(npin.gt.npmax)then
+           iret=1
+           goto 31
+          endif
+          xpomim(npin)=1.d0/xpomr/scm
+          xpomip(npin)=xpomri
+          vvxim(npin)=1.d0-(1.d0-vvxpin)**2*vvx0s
+          bpomim(npin)=bbi
+          if(debug.ge.4)write (moniou,211)npin,xpomip(npin)
+     *    ,xpomim(npin),vvxim(npin),bpomim(npin)
+         else                                     !more than 1 cut Pomeron
+          ninc=npgen(vim,2,20)
+          npin=npin+ninc
+          if(npin.gt.npmax)then
+           iret=1
+           goto 31
+          endif
+          do i=npin-ninc+1,npin
+           xpomim(i)=1.d0/xpomr/scm
+           xpomip(i)=xpomri
+           vvxim(i)=0.d0
+           bpomim(i)=bbi
+           if(debug.ge.4)write (moniou,211)i,xpomip(i),xpomim(i)
+     *     ,vvxim(i),bpomim(i)
+          enddo
+         endif
+
+        else                                      !additional vertices
+         xpomz0=dsqrt(xpomr*xpomri)
+         rp0=alfp*dlog(xpomri/xpomr)*.0389d0
+         xxz0=.5d0*(xxp+xxi)
+         yyz0=.5d0*(yyp+yyi)
+         bbzp=.25d0*bbi
+         bbzt=bbzp
+         call qgfdf(xxz0,yyz0,xpomz0,vpac,vtac,vvx,vvxp,vvxt
+     *   ,vvxpl,vvxtl,ip,it)
+
+         vvxp0=0.d0
+         sumup=0.d0
+         do i=1,ia(1)
+          sumup=sumup+vpac(i)
+         enddo
+         vvxs=(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+         do i=1,ia(1)
+          ipp=ia(1)-i+1
+          bbpi=(xa(ipp,1)+b-xxz0)**2+(xa(ipp,2)-yyz0)**2
+          sumup=sumup-vpac(ipp)
+          vpac0(ipp)=min(vpac(ipp)
+     *    ,qgfani(1.d0/xpomz0,bbpi,1.d0-vvxs*exp(-sumup)
+     *    ,1.d0-exp(-vvxp0),1.d0-exp(-sumup),iddp(ipp),icz,3))
+          vvxp0=vvxp0+vpac0(ipp)
+         enddo
+         vvxp0=1.d0-exp(-vvxp0)
+
+         sumut=0.d0
+         vvxt0=0.d0
+         do i=1,ia(2)
+          sumut=sumut+vtac(i)
+         enddo
+         vvxs=(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+         do i=1,ia(2)
+          itt=ia(2)-i+1
+          bbti=(xb(itt,1)-xxz0)**2+(xb(itt,2)-yyz0)**2
+          sumut=sumut-vtac(itt)
+          vtac0(itt)=min(vtac(itt)
+     *    ,qgfani(xpomz0*scm,bbti,1.d0-vvxs*exp(-sumut)
+     *    ,1.d0-exp(-vvxt0),1.d0-exp(-sumut),iddt(itt),2,3))
+          vvxt0=vvxt0+vtac0(itt)
+         enddo
+         vvxt0=1.d0-exp(-vvxt0)
+
+         viu=qgpini(xpomri/xpomz0,bbzp,0.d0,0.d0,2)
+         vilu=1.d0-exp(-viu)
+         vimu=2.d0*min(viu,qgpini(xpomri/xpomz0,bbzp,0.d0,0.d0,8))
+         vimpu=max(0.d0,(1.d0-exp(-vimu)*(1.d0+vimu)))/2.d0
+         vid=qgpini(xpomz0/xpomr,bbzt,0.d0,0.d0,2)
+         vild=1.d0-exp(-vid)
+         vimd=2.d0*min(vid,qgpini(xpomz0/xpomr,bbzt,0.d0,0.d0,8))
+         vimpd=max(0.d0,(1.d0-exp(-vimd)*(1.d0+vimd)))/2.d0
+
+         vi1pu=qgpini(xpomri/xpomz0,bbzp
+     *   ,1.d0-((1.d0-vvxp0)*(1.d0-vvxt0))**2,0.d0,16)*exp(-vimu)
+         vguu=qgpini(xpomri/xpomz0,bbzp,vvxp0,vvxt0,21)*vilu      !uu+uc
+         vgcu=qgpini(xpomri/xpomz0,bbzp,vvxp0,vvxt0,23)
+     *   *(vilu**2+(exp(2.d0*viu-vimu)-1.d0)*exp(-2.d0*viu))/2.d0 !cc+cu
+         vi1pd=qgpini(xpomz0/xpomr,bbzt
+     *   ,1.d0-((1.d0-vvxp0)*(1.d0-vvxt0))**2,0.d0,16)*exp(-vimd)
+         vgud=qgpini(xpomz0/xpomr,bbzt,vvxt0,vvxp0,21)*vild       !uu+uc
+         vgcd=qgpini(xpomz0/xpomr,bbzt,vvxt0,vvxp0,23)
+     *   *(vild**2+(exp(2.d0*vid-vimd)-1.d0)*exp(-2.d0*vid))/2.d0 !cc+cu
+
+         gbz0=(vimpu*vimpd+vimpu*vi1pd+vi1pu*vimpd+vimpu*vgcd
+     *   +vgcu*vimpd+vi1pu*vgcd+vgcu*vi1pd)*(1.d0-vvxp0)*(1.d0-vvxt0)
+     *   +(vimpu+vi1pu)*vgud*(1.d0-vvxp0)*vvxt0
+     *   +(vimpd+vi1pd)*vguu*(1.d0-vvxt0)*vvxp0
+
+         nrej=0
+34       xpomz=xpomr*sgap*(xpomri/xpomr/sgap**2)**qgran(b10)
+         rpp=alfp*dlog(xpomri/xpomz)*4.d0*.0389d0
+         rpt=alfp*dlog(xpomz/xpomr)*4.d0*.0389d0
+         rp=rpp*rpt/(rpp+rpt)
+         z=qgran(b10)
+         phi=pi*qgran(b10)
+         b0=dsqrt(-rp*dlog(z))
+         bbzp=(dsqrt(bbi)*rpp/(rpp+rpt)+b0*cos(phi))**2
+     *   +(b0*sin(phi))**2
+         bbzt=(dsqrt(bbi)*rpt/(rpp+rpt)-b0*cos(phi))**2
+     *   +(b0*sin(phi))**2
+         call qgbdef(bbzp,bbzt,xxi,yyi,xxp,yyp,xxz,yyz
+     *   ,int(1.5d0+qgran(b10)))               !coordinates for the vertex
+         call qgfdf(xxz,yyz,xpomz,vpac,vtac
+     *   ,vvx,vvxp,vvxt,vvxpl,vvxtl,ip,it)
+
+         vvxp0=0.d0
+         sumup=0.d0
+         do i=1,ia(1)
+          sumup=sumup+vpac(i)
+         enddo
+         vvxs=(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+         do i=1,ia(1)
+          ipp=ia(1)-i+1
+          bbpi=(xa(ipp,1)+b-xxz)**2+(xa(ipp,2)-yyz)**2
+          sumup=sumup-vpac(ipp)
+          vpac0(ipp)=min(vpac(ipp)
+     *    ,qgfani(1.d0/xpomz,bbpi,1.d0-vvxs*exp(-sumup)
+     *    ,1.d0-exp(-vvxp0),1.d0-exp(-sumup),iddp(ipp),icz,3))
+          vvxp0=vvxp0+vpac0(ipp)
+         enddo
+         vvxp0=1.d0-exp(-vvxp0)
+
+         sumut=0.d0
+         vvxt0=0.d0
+         do i=1,ia(2)
+          sumut=sumut+vtac(i)
+         enddo
+         vvxs=(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+         do i=1,ia(2)
+          itt=ia(2)-i+1
+          bbti=(xb(itt,1)-xxz)**2+(xb(itt,2)-yyz)**2
+          sumut=sumut-vtac(itt)
+          vtac0(itt)=min(vtac(itt)
+     *    ,qgfani(xpomz*scm,bbti,1.d0-vvxs*exp(-sumut)
+     *    ,1.d0-exp(-vvxt0),1.d0-exp(-sumut),iddt(itt),2,3))
+          vvxt0=vvxt0+vtac0(itt)
+         enddo
+         vvxt0=1.d0-exp(-vvxt0)
+
+         viu=qgpini(xpomri/xpomz,bbzp,0.d0,0.d0,2)
+         vilu=1.d0-exp(-viu)
+         vimu=2.d0*min(viu,qgpini(xpomri/xpomz,bbzp,0.d0,0.d0,8))
+         vimpu=max(0.d0,(1.d0-exp(-vimu)*(1.d0+vimu)))/2.d0
+         vid=qgpini(xpomz/xpomr,bbzt,0.d0,0.d0,2)
+         vild=1.d0-exp(-vid)
+         vimd=2.d0*min(vid,qgpini(xpomz/xpomr,bbzt,0.d0,0.d0,8))
+         vimpd=max(0.d0,(1.d0-exp(-vimd)*(1.d0+vimd)))/2.d0
+
+         vi1pu=qgpini(xpomri/xpomz,bbzp
+     *   ,1.d0-((1.d0-vvxp0)*(1.d0-vvxt0))**2,0.d0,16)*exp(-vimu)
+         vguu=qgpini(xpomri/xpomz,bbzp,vvxp0,vvxt0,21)*vilu       !uu+uc
+         vgcu=qgpini(xpomri/xpomz,bbzp,vvxp0,vvxt0,23)
+     *   *(vilu**2+(exp(2.d0*viu-vimu)-1.d0)*exp(-2.d0*viu))/2.d0 !cc+cu
+         vi1pd=qgpini(xpomz/xpomr,bbzt
+     *   ,1.d0-((1.d0-vvxp0)*(1.d0-vvxt0))**2,0.d0,16)*exp(-vimd)
+         vgud=qgpini(xpomz/xpomr,bbzt,vvxt0,vvxp0,21)*vild        !uu+uc
+         vgcd=qgpini(xpomz/xpomr,bbzt,vvxt0,vvxp0,23)
+     *   *(vild**2+(exp(2.d0*vid-vimd)-1.d0)*exp(-2.d0*vid))/2.d0 !cc+cu
+
+         vvcc=vimpu*vimpd+vimpu*vi1pd+vi1pu*vimpd+vimpu*vgcd+vgcu*vimpd
+     *   +vi1pu*vgcd+vgcu*vi1pd
+         vvt=vvcc*(1.d0-vvxp0)*(1.d0-vvxt0)
+     *   +(vimpu+vi1pu)*vgud*(1.d0-vvxp0)*vvxt0
+     *   +(vimpd+vi1pd)*vguu*(1.d0-vvxt0)*vvxp0
+
+         gbz=vvt/gbz0/z*rp/rp0  /1.4d0
+         nrej=nrej+1
+         if(qgran(b10).gt.gbz.and.nrej.lt.10000)goto 34
+
+         aks=vvt*qgran(b10)
+         if(aks.gt.vvcc*(1.d0-vvxp0)*(1.d0-vvxt0)
+     *   +(vimpu+vi1pu)*vgud*(1.d0-vvxp0)*vvxt0)then
+          jtu=0
+          if(qgran(b10).lt.vimpd/(vimpd+vi1pd))then
+           jtd=2
+          else
+           jtd=1
+          endif
+         elseif(aks.gt.vvcc*(1.d0-vvxp0)*(1.d0-vvxt0))then
+          jtd=0
+          if(qgran(b10).lt.vimpu/(vimpu+vi1pu))then
+           jtu=2
+          else
+           jtu=1
+          endif
+         else
+          aks=vvcc*qgran(b10)
+          if(aks.lt.vimpu*vimpd)then
+           jtu=2
+           jtd=2
+          elseif(aks.lt.vimpu*vimpd+vimpu*vi1pd)then
+           jtu=2
+           jtd=1
+          elseif(aks.lt.vimpu*vimpd+vimpu*vi1pd+vi1pu*vimpd)then
+           jtu=1
+           jtd=2
+          elseif(aks.lt.vimpu*vimpd+vimpu*vi1pd+vi1pu*vimpd
+     *    +vimpu*vgcd)then
+           jtu=2
+           jtd=0
+          elseif(aks.lt.vimpu*vimpd+vimpu*vi1pd+vi1pu*vimpd
+     *    +vimpu*vgcd+vgcu*vimpd)then
+           jtu=0
+           jtd=2
+          elseif(aks.lt.vimpu*vimpd+vimpu*vi1pd+vi1pu*vimpd
+     *    +vimpu*vgcd+vgcu*vimpd+vi1pu*vgcd)then
+           jtu=1
+           jtd=0
+          else
+           jtu=0
+           jtd=1
+          endif
+         endif
+
+         if(jtu.eq.1)then                         !single cut Pomeron
+          npin=npin+1
+          if(npin.gt.npmax)then
+           iret=1
+           goto 31
+          endif
+          xpomim(npin)=1.d0/xpomz/scm
+          xpomip(npin)=xpomri
+          vvxim(npin)=1.d0-((1.d0-vvxp0)*(1.d0-vvxt0))**2
+          bpomim(npin)=bbzp
+          if(debug.ge.4)write (moniou,211)npin,xpomip(npin)
+     *    ,xpomim(npin),vvxim(npin),bpomim(npin)
+         elseif(jtu.eq.2)then                     !more than 1 cut Pomeron
+          ninc=npgen(vimu,2,20)
+          npin=npin+ninc
+          if(npin.gt.npmax)then
+           iret=1
+           goto 31
+          endif
+          do i=npin-ninc+1,npin
+           xpomim(i)=1.d0/xpomz/scm
+           xpomip(i)=xpomri
+           vvxim(i)=0.d0
+           bpomim(i)=bbzp
+           if(debug.ge.4)write (moniou,211)i,xpomip(i),xpomim(i)
+     *     ,vvxim(i),bpomim(i)
+          enddo
+         endif
+
+         if(jtd.eq.1)then                         !single cut Pomeron
+          npin=npin+1
+          if(npin.gt.npmax)then
+           iret=1
+           goto 31
+          endif
+          xpomim(npin)=1.d0/xpomr/scm
+          xpomip(npin)=xpomz
+          vvxim(npin)=1.d0-((1.d0-vvxp0)*(1.d0-vvxt0))**2
+          bpomim(npin)=bbzt
+          if(debug.ge.4)write (moniou,211)npin,xpomip(npin)
+     *    ,xpomim(npin),vvxim(npin),bpomim(npin)
+         elseif(jtu.eq.2)then                     !more than 1 cut Pomeron
+          ninc=npgen(vimd,2,20)
+          npin=npin+ninc
+          if(npin.gt.npmax)then
+           iret=1
+           goto 31
+          endif
+          do i=npin-ninc+1,npin
+           xpomim(i)=1.d0/xpomr/scm
+           xpomip(i)=xpomz
+           vvxim(i)=0.d0
+           bpomim(i)=bbzt
+           if(debug.ge.4)write (moniou,211)i,xpomip(i),xpomim(i)
+     *     ,vvxim(i),bpomim(i)
+          enddo
+         endif
+        endif
+       enddo          !end of the zigzag-loop
+      endif           !nzzp.ne.0
+
+      if(nzzt.ne.0)then
+       btm=(xb(it,1)-xxp)**2+(xb(it,2)-yyp)**2
+       xpomr0=max(dsqrt(xpomr/scm),sgap/scm)
+       xpomr0=min(xpomr0,xpomr/sgap)
+       rp1=(rq(iddt(it),2)+alfp*dlog(xpomr0*scm))*4.d0*.0389d0
+       rp2=alfp*dlog(xpomr/xpomr0)*4.d0*.0389d0
+       rp0=rp1*rp2/(rp1+rp2)
+       bbt=btm*(rp1/(rp1+rp2))**2
+       bbi=btm*(rp2/(rp1+rp2))**2
+       call qgbdef(bbt,bbi,xb(it,1),xb(it,2),xxp,yyp,xxp0,yyp0,1)
+       call qgfdf(xxp0,yyp0,xpomr0,vpac,vtac
+     * ,vvx,vvxp,vvxt,vvxpl,vvxtl,ip,it)
+
+       sumct0=0.d0
+       sumctt=0.d0
+       sumut=0.d0
+       vvxt0=0.d0
+       vvxt0l=0.d0
+       do i=1,ia(2)
+        sumut=sumut+vtac(i)
+       enddo
+       vvxs=(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+       do i=1,ia(2)
+        itt=ia(2)-i+1
+        bbti=(xb(itt,1)-xxp0)**2+(xb(itt,2)-yyp0)**2
+        sumut=sumut-vtac(itt)
+        vtac0(itt)=min(vtac(itt)
+     *  ,qgfani(xpomr0*scm,bbti,1.d0-vvxs*exp(-sumut)
+     *  ,1.d0-exp(-sumct0),1.d0-exp(-sumut),iddt(itt),2,3))
+        if(itt.ge.it)vtact(itt)=max(vtac(itt)
+     *  ,qgfani(xpomr0*scm,bbti,1.d0-vvxs*exp(-sumut)
+     *  ,1.d0-exp(-sumctt),1.d0-exp(-sumut),iddt(itt),2,6))
+        if(itt.gt.it)then
+         vvxt0=vvxt0+vtac0(itt)
+         sumctt=sumctt+vtact(itt)
+        elseif(itt.lt.it)then
+         vvxt0l=vvxt0l+vtac0(itt)
+        endif
+        sumct0=sumct0+vtac0(itt)
+       enddo
+       vvxtt=1.d0-exp(-sumctt)
+       vvxt0=1.d0-exp(-vvxt0)
+       vvxt0l=1.d0-exp(-vvxt0l)
+
+       sumcp0=0.d0
+       sumup=0.d0
+       vvxp0=0.d0
+       vvxp0l=0.d0
+       do i=1,ia(1)
+        sumup=sumup+vpac(i)
+       enddo
+       vvxs=(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+       do i=1,ia(1)
+        ipp=ia(1)-i+1
+        bbpi=(xa(ipp,1)+b-xxp0)**2+(xa(ipp,2)-yyp0)**2
+        sumup=sumup-vpac(ipp)
+        vpac0(ipp)=min(vpac(ipp)
+     *  ,qgfani(1.d0/xpomr0,bbpi,1.d0-vvxs*exp(-sumup)
+     *  ,1.d0-exp(-sumcp0),1.d0-exp(-sumup),iddp(ipp),icz,3))
+        if(ipp.gt.ip)then
+         vvxp0=vvxp0+vpac0(ipp)
+        elseif(ipp.lt.ip)then
+         vvxp0l=vvxp0l+vpac0(ipp)
+        endif
+        sumcp0=sumcp0+vpac0(ipp)
+       enddo
+       vvxp0=1.d0-exp(-vvxp0)
+       vvxp0l=1.d0-exp(-vvxp0l)
+
+       viu=qgpini(xpomr/xpomr0,bbi,0.d0,0.d0,2)
+       vim=2.d0*min(viu,qgpini(xpomr/xpomr0,bbi,0.d0,0.d0,8))
+       vvxpin=1.d0-(1.d0-vvxp0)*(1.d0-vvxp0l)*exp(-vpac0(ip))
+       vvxtin=1.d0-(1.d0-vvxt0)*(1.d0-vvxt0l)*exp(-vtac0(it))
+       vi=qgpini(xpomr/xpomr0,bbi,vvxtin,vvxpin,21)*(1.d0-exp(-viu))
+     * -qgpini(xpomr/xpomr0,bbi,vvxtin,vvxpin,23)*((1.d0-exp(-viu))**2
+     * +(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))/2.d0
+
+       vvx0s=(1.d0-vvxpin)**2
+       vvxs=((1.d0-vvxp)*(1.d0-vvxpl))**2*exp(-2.d0*vpac(ip))
+
+       gb0=vi                      *15.
+     * *((1.d0-exp(-vtact(it)))*(1.d0-vvxtt)*(1.d0-vvxs)
+     * +vvxs*(max(0.d0,1.d0-exp(-vtact(it))*(1.d0+vtact(it)))
+     * *(1.d0-vvxtt)
+     * -max(0.d0,1.d0-exp(-vtac0(it))*(1.d0+vtac0(it)))*(1.d0-vvxt0))
+     * +vtact(it)*exp(-vtact(it))*((1.d0-vvxtt)*vvxs
+     * -exp(vtact(it)-vtac0(it))*(1.d0-vvxt0)*(1.d0-vvxt0l)*vvx0s)
+     * -vtac0(it)*exp(-vtac0(it))*(1.d0-vvxt0)
+     * *(vvxs-vvx0s+vvxt0l*vvx0s))
+
+       do in=1,nzzt
+        nrej=0
+33      xpomri=xpomr/sgap/(xpomr*scm/sgap**2)**qgran(b10)
+        rp1=(rq(iddt(it),2)+alfp*dlog(xpomri*scm))*4.d0*.0389d0
+        rp2=alfp*dlog(xpomr/xpomri)*4.d0*.0389d0
+        rp=rp1*rp2/(rp1+rp2)
+        z=qgran(b10)
+        phi=pi*qgran(b10)
+        b0=dsqrt(-rp*dlog(z))
+        bbt=(dsqrt(btm)*rp1/(rp1+rp2)+b0*cos(phi))**2+(b0*sin(phi))**2
+        bbi=(dsqrt(btm)*rp2/(rp1+rp2)-b0*cos(phi))**2+(b0*sin(phi))**2
+        call qgbdef(bbt,bbi,xb(it,1),xb(it,2),xxp,yyp,xxi,yyi
+     *  ,int(1.5d0+qgran(b10)))   !coordinates for the vertex
+        call qgfdf(xxi,yyi,xpomri,vpac,vtac
+     *  ,vvx,vvxp,vvxt,vvxpl,vvxtl,ip,it)
+
+        sumct0=0.d0
+        sumctt=0.d0
+        sumut=0.d0
+        vvxt0=0.d0
+        vvxt0l=0.d0
+        do i=1,ia(2)
+         sumut=sumut+vtac(i)
+        enddo
+        vvxs=(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+        do i=1,ia(2)
+         itt=ia(2)-i+1
+         bbti=(xb(itt,1)-xxi)**2+(xb(itt,2)-yyi)**2
+         sumut=sumut-vtac(itt)
+         vtac0(itt)=min(vtac(itt)
+     *   ,qgfani(xpomri*scm,bbti,1.d0-vvxs*exp(-sumut)
+     *   ,1.d0-exp(-sumct0),1.d0-exp(-sumut),iddt(itt),2,3))
+         if(itt.ge.it)vtact(itt)=max(vtac(itt)
+     *   ,qgfani(xpomri*scm,bbti,1.d0-vvxs*exp(-sumut)
+     *   ,1.d0-exp(-sumctt),1.d0-exp(-sumut),iddt(itt),2,6))
+         if(itt.gt.it)then
+          vvxt0=vvxt0+vtac0(itt)
+          sumctt=sumctt+vtact(itt)
+         elseif(itt.lt.it)then
+          vvxt0l=vvxt0l+vtac0(itt)
+         endif
+         sumct0=sumct0+vtac0(itt)
+        enddo
+        vvxtt=1.d0-exp(-sumctt)
+        vvxt0=1.d0-exp(-vvxt0)
+        vvxt0l=1.d0-exp(-vvxt0l)
+
+        sumcp0=0.d0
+        sumup=0.d0
+        vvxp0=0.d0
+        vvxp0l=0.d0
+        do i=1,ia(1)
+         sumup=sumup+vpac(i)
+        enddo
+        vvxs=(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+        do i=1,ia(1)
+         ipp=ia(1)-i+1
+         bbpi=(xa(ipp,1)+b-xxi)**2+(xa(ipp,2)-yyi)**2
+         sumup=sumup-vpac(ipp)
+         vpac0(ipp)=min(vpac(ipp)
+     *   ,qgfani(1.d0/xpomri,bbpi,1.d0-vvxs*exp(-sumup)
+     *   ,1.d0-exp(-sumcp0),1.d0-exp(-sumup),iddp(ipp),icz,3))
+         if(ipp.gt.ip)then
+          vvxp0=vvxp0+vpac0(ipp)
+         elseif(ipp.lt.ip)then
+          vvxp0l=vvxp0l+vpac0(ipp)
+         endif
+         sumcp0=sumcp0+vpac0(ipp)
+        enddo
+        vvxp0=1.d0-exp(-vvxp0)
+        vvxp0l=1.d0-exp(-vvxp0l)
+
+        viu=qgpini(xpomr/xpomri,bbi,0.d0,0.d0,2)
+        vim=2.d0*min(viu,qgpini(xpomr/xpomri,bbi,0.d0,0.d0,8))
+        vvxpin=1.d0-(1.d0-vvxp0)*(1.d0-vvxp0l)*exp(-vpac0(ip))
+        vvxtin=1.d0-(1.d0-vvxt0)*(1.d0-vvxt0l)*exp(-vtac0(it))
+        vi=qgpini(xpomr/xpomri,bbi,vvxtin,vvxpin,21)*(1.d0-exp(-viu))
+     *  -qgpini(xpomr/xpomri,bbi,vvxtin,vvxpin,23)*((1.d0-exp(-viu))**2
+     *  +(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))/2.d0
+
+        vvx0s=(1.d0-vvxpin)**2
+        vvxs=((1.d0-vvxp)*(1.d0-vvxpl))**2*exp(-2.d0*vpac(ip))
+
+        gb=vi
+     *  *((1.d0-exp(-vtact(it)))*(1.d0-vvxtt)*(1.d0-vvxs)
+     *  +vvxs*(max(0.d0,1.d0-exp(-vtact(it))*(1.d0+vtact(it)))
+     *  *(1.d0-vvxtt)
+     *  -max(0.d0,1.d0-exp(-vtac0(it))*(1.d0+vtac0(it)))*(1.d0-vvxt0))
+     *  +vtact(it)*exp(-vtact(it))*((1.d0-vvxtt)*vvxs
+     *  -exp(vtact(it)-vtac0(it))*(1.d0-vvxt0)*(1.d0-vvxt0l)*vvx0s)
+     *  -vtac0(it)*exp(-vtac0(it))*(1.d0-vvxt0)
+     *  *(vvxs-vvx0s+vvxt0l*vvx0s))
+
+        gb=gb/gb0/z*rp/rp0
+        nrej=nrej+1
+        if(qgran(b10).gt.gb.and.nrej.lt.10000)goto 33
+
+        vi1p=qgpini(xpomr/xpomri,bbi,1.d0-(1.d0-vvxtin)**2*vvx0s
+     *  ,0.d0,16)*exp(-vim)
+        vimp=max(0.d0,(1.d0-exp(-vim)*(1.d0+vim)))/2.d0
+
+        if(qgran(b10).le.(vi1p+vimp)/vi
+     *  .or.xpomr/xpomri.lt.1.1d0*sgap**2)then
+         if(qgran(b10).le.vi1p/(vi1p+vimp))then   !single cut Pomeron
+          npin=npin+1
+          if(npin.gt.npmax)then
+           iret=1
+           goto 31
+          endif
+          xpomim(npin)=1.d0/xpomri/scm
+          xpomip(npin)=xpomr
+          vvxim(npin)=1.d0-(1.d0-vvxtin)**2*vvx0s
+          bpomim(npin)=bbi
+          if(debug.ge.4)write (moniou,211)npin,xpomip(npin)
+     *    ,xpomim(npin),vvxim(npin),bpomim(npin)
+         else                                     !more than 1 cut pomeron
+          ninc=npgen(vim,2,20)
+          npin=npin+ninc
+          if(npin.gt.npmax)then
+           iret=1
+           goto 31
+          endif
+          do i=npin-ninc+1,npin
+           xpomim(i)=1.d0/xpomri/scm
+           xpomip(i)=xpomr
+           vvxim(i)=0.d0
+           bpomim(i)=bbi
+           if(debug.ge.4)write (moniou,211)i,xpomip(i),xpomim(i)
+     *     ,vvxim(i),bpomim(i)
+          enddo
+         endif
+
+        else                                      !additional vertices
+         xpomz0=dsqrt(xpomr*xpomri)
+         rp0=alfp*dlog(xpomr/xpomri)*.0389d0
+         xxz0=.5d0*(xxp+xxi)
+         yyz0=.5d0*(yyp+yyi)
+         bbzp=.25d0*bbi
+         bbzt=bbzp
+         call qgfdf(xxz0,yyz0,xpomz0,vpac,vtac,vvx,vvxp,vvxt
+     *   ,vvxpl,vvxtl,ip,it)
+
+         vvxp0=0.d0
+         sumup=0.d0
+         do i=1,ia(1)
+          sumup=sumup+vpac(i)
+         enddo
+         vvxs=(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+         do i=1,ia(1)
+          ipp=ia(1)-i+1
+          bbpi=(xa(ipp,1)+b-xxz0)**2+(xa(ipp,2)-yyz0)**2
+          sumup=sumup-vpac(ipp)
+          vpac0(ipp)=min(vpac(ipp)
+     *    ,qgfani(1.d0/xpomz0,bbpi,1.d0-vvxs*exp(-sumup)
+     *    ,1.d0-exp(-vvxp0),1.d0-exp(-sumup),iddp(ipp),icz,3))
+          vvxp0=vvxp0+vpac0(ipp)
+         enddo
+         vvxp0=1.d0-exp(-vvxp0)
+
+         sumut=0.d0
+         vvxt0=0.d0
+         do i=1,ia(2)
+          sumut=sumut+vtac(i)
+         enddo
+         vvxs=(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+         do i=1,ia(2)
+          itt=ia(2)-i+1
+          bbti=(xb(itt,1)-xxz0)**2+(xb(itt,2)-yyz0)**2
+          sumut=sumut-vtac(itt)
+          vtac0(itt)=min(vtac(itt)
+     *    ,qgfani(xpomz0*scm,bbti,1.d0-vvxs*exp(-sumut)
+     *    ,1.d0-exp(-vvxt0),1.d0-exp(-sumut),iddt(itt),2,3))
+          vvxt0=vvxt0+vtac0(itt)
+         enddo
+         vvxt0=1.d0-exp(-vvxt0)
+
+         viu=qgpini(xpomr/xpomz0,bbzp,0.d0,0.d0,2)
+         vilu=1.d0-exp(-viu)
+         vimu=2.d0*min(viu,qgpini(xpomr/xpomz0,bbzp,0.d0,0.d0,8))
+         vimpu=max(0.d0,(1.d0-exp(-vimu)*(1.d0+vimu)))/2.d0
+         vid=qgpini(xpomz0/xpomri,bbzt,0.d0,0.d0,2)
+         vild=1.d0-exp(-vid)
+         vimd=2.d0*min(vid,qgpini(xpomz0/xpomri,bbzt,0.d0,0.d0,8))
+         vimpd=max(0.d0,(1.d0-exp(-vimd)*(1.d0+vimd)))/2.d0
+
+         vi1pu=qgpini(xpomr/xpomz0,bbzp
+     *   ,1.d0-((1.d0-vvxp0)*(1.d0-vvxt0))**2,0.d0,16)*exp(-vimu)
+         vguu=qgpini(xpomr/xpomz0,bbzp,vvxp0,vvxt0,21)*vilu       !uu+uc
+         vgcu=qgpini(xpomr/xpomz0,bbzp,vvxp0,vvxt0,23)
+     *   *(vilu**2+(exp(2.d0*viu-vimu)-1.d0)*exp(-2.d0*viu))/2.d0 !cc+cu
+         vi1pd=qgpini(xpomz0/xpomri,bbzt
+     *   ,1.d0-((1.d0-vvxp0)*(1.d0-vvxt0))**2,0.d0,16)*exp(-vimd)
+         vgud=qgpini(xpomz0/xpomri,bbzt,vvxt0,vvxp0,21)*vild      !uu+uc
+         vgcd=qgpini(xpomz0/xpomri,bbzt,vvxt0,vvxp0,23)
+     *   *(vild**2+(exp(2.d0*vid-vimd)-1.d0)*exp(-2.d0*vid))/2.d0 !cc+cu
+
+         gbz0=(vimpu*vimpd+vimpu*vi1pd+vi1pu*vimpd+vimpu*vgcd
+     *   +vgcu*vimpd+vi1pu*vgcd+vgcu*vi1pd)*(1.d0-vvxp0)*(1.d0-vvxt0)
+     *   +(vimpu+vi1pu)*vgud*(1.d0-vvxp0)*vvxt0
+     *   +(vimpd+vi1pd)*vguu*(1.d0-vvxt0)*vvxp0
+
+         nrej=0
+35       xpomz=xpomri*sgap*(xpomr/xpomri/sgap**2)**qgran(b10)
+         rpt=alfp*dlog(xpomz/xpomri)*4.d0*.0389d0
+         rpp=alfp*dlog(xpomr/xpomz)*4.d0*.0389d0
+         rp=rpp*rpt/(rpp+rpt)
+         z=qgran(b10)
+         phi=pi*qgran(b10)
+         b0=dsqrt(-rp*dlog(z))
+         bbzt=(dsqrt(bbi)*rpt/(rpp+rpt)-b0*cos(phi))**2
+     *   +(b0*sin(phi))**2
+         bbzp=(dsqrt(bbi)*rpp/(rpp+rpt)+b0*cos(phi))**2
+     *   +(b0*sin(phi))**2
+         call qgbdef(bbzt,bbzp,xxi,yyi,xxp,yyp,xxz,yyz
+     *   ,int(1.5d0+qgran(b10)))               !coordinates for the vertex
+         call qgfdf(xxz,yyz,xpomz,vpac,vtac
+     *   ,vvx,vvxp,vvxt,vvxpl,vvxtl,ip,it)
+
+         vvxp0=0.d0
+         sumup=0.d0
+         do i=1,ia(1)
+          sumup=sumup+vpac(i)
+         enddo
+         vvxs=(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+         do i=1,ia(1)
+          ipp=ia(1)-i+1
+          bbpi=(xa(ipp,1)+b-xxz)**2+(xa(ipp,2)-yyz)**2
+          sumup=sumup-vpac(ipp)
+          vpac0(ipp)=min(vpac(ipp)
+     *    ,qgfani(1.d0/xpomz,bbpi,1.d0-vvxs*exp(-sumup)
+     *    ,1.d0-exp(-vvxp0),1.d0-exp(-sumup),iddp(ipp),icz,3))
+          vvxp0=vvxp0+vpac0(ipp)
+         enddo
+         vvxp0=1.d0-exp(-vvxp0)
+
+         sumut=0.d0
+         vvxt0=0.d0
+         do i=1,ia(2)
+          sumut=sumut+vtac(i)
+         enddo
+         vvxs=(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+         do i=1,ia(2)
+          itt=ia(2)-i+1
+          bbti=(xb(itt,1)-xxz)**2+(xb(itt,2)-yyz)**2
+          sumut=sumut-vtac(itt)
+          vtac0(itt)=min(vtac(itt)
+     *    ,qgfani(xpomz*scm,bbti,1.d0-vvxs*exp(-sumut)
+     *    ,1.d0-exp(-vvxt0),1.d0-exp(-sumut),iddt(itt),2,3))
+          vvxt0=vvxt0+vtac0(itt)
+         enddo
+         vvxt0=1.d0-exp(-vvxt0)
+
+         viu=qgpini(xpomr/xpomz,bbzp,0.d0,0.d0,2)
+         vilu=1.d0-exp(-viu)
+         vimu=2.d0*min(viu,qgpini(xpomr/xpomz,bbzp,0.d0,0.d0,8))
+         vimpu=max(0.d0,(1.d0-exp(-vimu)*(1.d0+vimu)))/2.d0
+         vid=qgpini(xpomz/xpomri,bbzt,0.d0,0.d0,2)
+         vild=1.d0-exp(-vid)
+         vimd=2.d0*min(vid,qgpini(xpomz/xpomri,bbzt,0.d0,0.d0,8))
+         vimpd=max(0.d0,(1.d0-exp(-vimd)*(1.d0+vimd)))/2.d0
+
+         vi1pu=qgpini(xpomr/xpomz,bbzp
+     *   ,1.d0-((1.d0-vvxp0)*(1.d0-vvxt0))**2,0.d0,16)*exp(-vimu)
+         vguu=qgpini(xpomr/xpomz,bbzp,vvxp0,vvxt0,21)*vilu        !uu+uc
+         vgcu=qgpini(xpomr/xpomz,bbzp,vvxp0,vvxt0,23)
+     *   *(vilu**2+(exp(2.d0*viu-vimu)-1.d0)*exp(-2.d0*viu))/2.d0 !cc+cu
+         vi1pd=qgpini(xpomz/xpomri,bbzt
+     *   ,1.d0-((1.d0-vvxp0)*(1.d0-vvxt0))**2,0.d0,16)*exp(-vimd)
+         vgud=qgpini(xpomz/xpomri,bbzt,vvxt0,vvxp0,21)*vild       !uu+uc
+         vgcd=qgpini(xpomz/xpomri,bbzt,vvxt0,vvxp0,23)
+     *   *(vild**2+(exp(2.d0*vid-vimd)-1.d0)*exp(-2.d0*vid))/2.d0 !cc+cu
+
+         vvcc=vimpu*vimpd+vimpu*vi1pd+vi1pu*vimpd+vimpu*vgcd+vgcu*vimpd
+     *   +vi1pu*vgcd+vgcu*vi1pd
+         vvt=vvcc*(1.d0-vvxp0)*(1.d0-vvxt0)
+     *   +(vimpu+vi1pu)*vgud*(1.d0-vvxp0)*vvxt0
+     *   +(vimpd+vi1pd)*vguu*(1.d0-vvxt0)*vvxp0
+
+         gbz=vvt/gbz0/z*rp/rp0    /1.4d0
+         nrej=nrej+1
+         if(qgran(b10).gt.gbz.and.nrej.lt.10000)goto 35
+
+         aks=vvt*qgran(b10)
+         if(aks.gt.vvcc*(1.d0-vvxp0)*(1.d0-vvxt0)
+     *   +(vimpu+vi1pu)*vgud*(1.d0-vvxp0)*vvxt0)then
+          jtu=0
+          if(qgran(b10).lt.vimpd/(vimpd+vi1pd))then
+           jtd=2
+          else
+           jtd=1
+          endif
+         elseif(aks.gt.vvcc*(1.d0-vvxp0)*(1.d0-vvxt0))then
+          jtd=0
+          if(qgran(b10).lt.vimpu/(vimpu+vi1pu))then
+           jtu=2
+          else
+           jtu=1
+          endif
+         else
+          aks=vvcc*qgran(b10)
+          if(aks.lt.vimpu*vimpd)then
+           jtu=2
+           jtd=2
+          elseif(aks.lt.vimpu*vimpd+vimpu*vi1pd)then
+           jtu=2
+           jtd=1
+          elseif(aks.lt.vimpu*vimpd+vimpu*vi1pd+vi1pu*vimpd)then
+           jtu=1
+           jtd=2
+          elseif(aks.lt.vimpu*vimpd+vimpu*vi1pd+vi1pu*vimpd
+     *    +vimpu*vgcd)then
+           jtu=2
+           jtd=0
+          elseif(aks.lt.vimpu*vimpd+vimpu*vi1pd+vi1pu*vimpd
+     *    +vimpu*vgcd+vgcu*vimpd)then
+           jtu=0
+           jtd=2
+          elseif(aks.lt.vimpu*vimpd+vimpu*vi1pd+vi1pu*vimpd
+     *    +vimpu*vgcd+vgcu*vimpd+vi1pu*vgcd)then
+           jtu=1
+           jtd=0
+          else
+           jtu=0
+           jtd=1
+          endif
+         endif
+
+         if(jtu.eq.1)then                         !single cut Pomeron
+          npin=npin+1
+          if(npin.gt.npmax)then
+           iret=1
+           goto 31
+          endif
+          xpomim(npin)=1.d0/xpomz/scm
+          xpomip(npin)=xpomr
+          vvxim(npin)=1.d0-((1.d0-vvxp0)*(1.d0-vvxt0))**2
+          bpomim(npin)=bbzp
+          if(debug.ge.4)write (moniou,211)npin,xpomip(npin)
+     *    ,xpomim(npin),vvxim(npin),bpomim(npin)
+         elseif(jtu.eq.2)then                     !more than 1 cut Pomeron
+          ninc=npgen(vimu,2,20)
+          npin=npin+ninc
+          if(npin.gt.npmax)then
+           iret=1
+           goto 31
+          endif
+          do i=npin-ninc+1,npin
+           xpomim(i)=1.d0/xpomz/scm
+           xpomip(i)=xpomr
+           vvxim(i)=0.d0
+           bpomim(i)=bbzp
+           if(debug.ge.4)write (moniou,211)i,xpomip(i),xpomim(i)
+     *     ,vvxim(i),bpomim(i)
+          enddo
+         endif
+
+         if(jtd.eq.1)then                         !single cut Pomeron
+          npin=npin+1
+          if(npin.gt.npmax)then
+           iret=1
+           goto 31
+          endif
+          xpomim(npin)=1.d0/xpomri/scm
+          xpomip(npin)=xpomz
+          vvxim(npin)=1.d0-((1.d0-vvxp0)*(1.d0-vvxt0))**2
+          bpomim(npin)=bbzt
+          if(debug.ge.4)write (moniou,211)npin,xpomip(npin)
+     *    ,xpomim(npin),vvxim(npin),bpomim(npin)
+         elseif(jtu.eq.2)then                     !more than 1 cut Pomeron
+          ninc=npgen(vimd,2,20)
+          npin=npin+ninc
+          if(npin.gt.npmax)then
+           iret=1
+           goto 31
+          endif
+          do i=npin-ninc+1,npin
+           xpomim(i)=1.d0/xpomri/scm
+           xpomip(i)=xpomz
+           vvxim(i)=0.d0
+           bpomim(i)=bbzt
+           if(debug.ge.4)write (moniou,211)i,xpomip(i),xpomim(i)
+     *     ,vvxim(i),bpomim(i)
+          enddo
+         endif
+        endif
+       enddo          !end of the zigzag-loop
+      endif           !nzzt.ne.0
+
+      call qgfdf(xxp,yyp,xpomr,vpac,vtac,vvx,vvxp,vvxt,vvxpl,vvxtl
+     *,ip,it)
+      if((jt.eq.2.or.jt.eq.3.or.jt.eq.9)
+     *.and.qgran(b10).lt.(1.d0-exp(-vpac(ip)))*(1.d0-vvxpl)
+     */((1.d0-exp(-vpac(ip)))*(1.d0-vvxpl)+2.d0*vvxpl))then
+       icdps=iddp(ip)
+       do icdp=1,2
+        iddp(ip)=icdp
+        call qgfdf(xxp,yyp,xpomr,vpac,vtac,vvx,vvxp,vvxt,vvxpl,vvxtl
+     *  ,ip,it)
+        wdp(icdp,ip)=(1.d0-exp(-vpac(ip)))*(1.d0-vvxpl)
+       enddo
+       iddp(ip)=icdps
+      endif
+      call qgfdf(xxp,yyp,xpomr,vpac,vtac,vvx,vvxp,vvxt,vvxpl,vvxtl
+     *,ip,it)
+      if((jt.eq.2.or.jt.eq.4.or.jt.eq.10)
+     *.and.qgran(b10).lt.(1.d0-exp(-vtac(it)))*(1.d0-vvxtl)
+     */((1.d0-exp(-vtac(it)))*(1.d0-vvxtl)+2.d0*vvxtl))then
+       icdts=iddt(it)
+       do icdt=1,2
+        iddt(it)=icdt
+        call qgfdf(xxp,yyp,xpomr,vpac,vtac,vvx,vvxp,vvxt,vvxpl,vvxtl
+     *  ,ip,it)
+        wdt(icdt,it)=(1.d0-exp(-vtac(it)))*(1.d0-vvxtl)
+       enddo
+       iddt(it)=icdts
+      endif
+
+      if(nppr0.eq.0)goto 20
+
+c projectile 'fans'
+      m=0
+      nppm(1)=nppr0
+      xpomm(1)=xpomr
+      wgpm(1)=wgpr0
+      xxm(1)=xxp
+      yym(1)=yyp
+      do i=1,nppr0
+       ippm(i,1)=ippr0(i)
+       itypm(i,1)=itypr0(i)
+      enddo
+
+9     m=m+1                                 !next level multi-Pomeron vertex
+      if(m.gt.levmax)then
+       iret=1
+       goto 31
+      endif
+      ii(m)=0
+10    ii(m)=ii(m)+1                         !next cut fan in the vertex
+      if(ii(m).gt.nppm(m))then              !all fans at the level considered
+       m=m-1                                !one level down
+       if(m.eq.0)goto 20                    !all proj. fans considered
+       goto 10
+      endif
+      l=ii(m)
+      ipp=ippm(l,m)                         !proj. index for the leg
+      itypom=itypm(l,m)                     !type of the cut
+      bpm=(xa(ipp,1)+b-xxm(m))**2+(xa(ipp,2)-yym(m))**2      !b^2 for the leg
+      if(debug.ge.4)write (moniou,208)ii(m),m,ipp,bpm
+      if(xpomm(m)*sgap**2.gt.1.d0)stop'xpomm(m)*sgap**2>1!'
+      if(itypom.eq.4.and.xpomm(m)*sgap**3.gt.1.d0)
+     *stop'4:xpomm(m)*sgap**3>1!'
+
+      if(debug.ge.4)write (moniou,210)m
+      xpomr0=min(dsqrt(xpomm(m)),1.d0/sgap)
+      xpomr0=max(xpomr0,xpomm(m)*sgap)
+      if(itypom.eq.4)xpomr0=min(xpomr0,dsqrt(xpomm(m)/sgap))
+      rp1=(rq(iddp(ipp),icz)-alfp*dlog(xpomr0))*4.d0*.0389d0
+      rp2=alfp*dlog(xpomr0/xpomm(m))*4.d0*.0389d0
+      rp0=rp1*rp2/(rp1+rp2)
+      bbp=bpm*(rp1/(rp1+rp2))**2
+      bbi=bpm*(rp2/(rp1+rp2))**2
+      call qgbdef(bbp,bbi,xa(ipp,1)+b,xa(ipp,2),xxm(m),yym(m)
+     *,xxp0,yyp0,1)
+
+      call qgfdf(xxp0,yyp0,xpomr0,vpac,vtac,vvx,vvxp,vvxt
+     *,vvxpl,vvxtl,ipp,it)
+      vvxts=1.d0-(1.d0-vvx)*(1.d0-vvxt)*exp(-vtac(it))
+      viu=qgpini(xpomr0/xpomm(m),bbi,0.d0,0.d0,2)
+      vim=2.d0*min(viu,qgpini(xpomr0/xpomm(m),bbi,0.d0,0.d0,8))
+      if(itypom.eq.-1.or.itypom.eq.4)then         !single cut Pomeron at the end
+       vvxi=1.d0-((1.d0-vvx)*(1.d0-vvxp)*(1.d0-vvxt))**2
+     * *exp(-2.d0*vpac(ipp)-2.d0*vtac(it))
+       vip=qgpini(xpomr0/xpomm(m),bbi,vvxi,0.d0,16)*exp(-vim)
+      elseif(itypom.eq.2.or.itypom.eq.7)then       !>1 cut Poms at the end
+       vimp=max(0.d0,1.d0-exp(-vim)*(1.d0+vim))
+      else                                         !rap-gap
+       vvxpin=1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ipp))
+       vvxtin=1.d0-(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+       viuu=qgpini(xpomr0/xpomm(m),bbi,vvxpin,vvxtin,20)
+     * *(1.d0-exp(-viu))
+       viuc=max(0.d0,viuu
+     * -qgpini(xpomr0/xpomm(m),bbi,vvxpin,vvxtin,21)*(1.d0-exp(-viu)))
+       vicc=qgpini(xpomr0/xpomm(m),bbi,vvxpin,vvxtin,22)*.5d0
+     * *((1.d0-exp(-viu))**2+(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))
+       vicu=max(0.d0,qgpini(xpomr0/xpomm(m),bbi,vvxpin,vvxtin,23)*.5d0
+     * *((1.d0-exp(-viu))**2+(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))
+     * -vicc)
+      endif
+
+      if(itypom.le.3)then
+       sumup=0.d0
+       vvxp0=0.d0
+       do i=1,ia(1)
+        sumup=sumup+vpac(i)
+       enddo
+       vvxs=(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+       do i=1,ia(1)-ipp+1
+        ipi=ia(1)-i+1
+        bbl=(xa(ipi,1)+b-xxp0)**2+(xa(ipi,2)-yyp0)**2
+        sumup=sumup-vpac(ipi)
+        vpac0(ipi)=min(vpac(ipi)
+     *  ,qgfani(1.d0/xpomr0,bbl,1.d0-vvxs*exp(-sumup)
+     *  ,1.d0-exp(-vvxp0),1.d0-exp(-sumup),iddp(ipi),icz,3))
+        if(ipi.gt.ipp)vvxp0=vvxp0+vpac0(ipi)
+       enddo
+       vvxp0=1.d0-exp(-vvxp0)
+       vpacng=min(vpac0(ipp)
+     * ,qgfani(1.d0/xpomr0,bbp,vvxts,vvxp0,vvxpl,iddp(ipp),icz,4))
+       vpacpe=min(vpacng
+     * ,qgfani(1.d0/xpomr0,bbp,vvxts,vvxp0,vvxpl,iddp(ipp),icz,5))
+      else
+       vplc=qgfani(1.d0/xpomr0,bbp,vvxts,vvxp,vvxpl,iddp(ipp),icz,7)
+       vplc0=min(vplc
+     * ,qgfani(1.d0/xpomr0,bbp,vvxts,vvxp,vvxpl,iddp(ipp),icz,8))
+       vplcng=min(vplc0
+     * ,qgfani(1.d0/xpomr0,bbp,vvxts,vvxp,vvxpl,iddp(ipp),icz,11))
+       vplcpe=min(vplcng
+     * ,qgfani(1.d0/xpomr0,bbp,vvxts,vvxp,vvxpl,iddp(ipp),icz,10))
+      endif
+
+      if(itypom.eq.-1)then          !'fan' (single cut Pomeron at the end)
+       gb0=vip*((max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp))
+     * +((1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl)
+     * +2.d0*((vpac0(ipp)-vpacpe)*exp(-vpac(ipp))*(1.d0-vvxp)
+     * *(1.d0-vvxpl)-(vpac(ipp)-vpac0(ipp))*(1.d0-exp(-vpac(ipp))
+     * *(1.d0-vvxp)*(1.d0-vvxpl)))*exp(-vpac(ipp))*(1.d0-vvxp))
+     * *(1.d0-vvx)*(1.d0-vvxt)**2*(1.d0-vvxtl)*exp(-2.d0*vtac(it))
+       gb0=gb0*40.d0
+      elseif(itypom.eq.0)then      !'fan' (cut loop at the end - rapgap)
+       gb0=((max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp))
+     * +((1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl)
+     * +2.d0*vpacng*exp(-2.d0*vpac(ipp))*(1.d0-vvxp)**2*(1.d0-vvxpl))
+     * *(vicc*(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+     * -vicu*(1.d0-(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))))
+     * *(1.d0-vvx)*(1.d0-vvxt)*exp(-vtac(it))
+     * -2.d0*vicu*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp)-vtac(it))
+     * *(1.d0-vvx)*(1.d0-vvxt)
+      elseif(itypom.eq.1)then      !'fan' (uncut end - rapgap)
+       gb0=((max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp))
+     * +((1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl)
+     * +2.d0*vpacng*exp(-2.d0*vpac(ipp))*(1.d0-vvxp)**2*(1.d0-vvxpl))
+     * *(viuc*(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+     * +viuu*(1.d0-(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))))
+     * *(1.d0-vvx)*(1.d0-vvxt)*exp(-vtac(it))
+     * +2.d0*viuu*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp)-vtac(it))
+     * *(1.d0-vvx)*(1.d0-vvxt)
+      elseif(itypom.eq.2)then        !'fan' (>1 cut Poms at the end)
+       gb0=vimp*((max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp))
+     * +((1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl)
+     * +2.d0*(vpac0(ipp)*exp(-vpac(ipp))*(1.d0-vvxp)
+     * *(1.d0-vvxpl)-(vpac(ipp)-vpac0(ipp))*(1.d0-exp(-vpac(ipp))
+     * *(1.d0-vvxp)*(1.d0-vvxpl)))*exp(-vpac(ipp))*(1.d0-vvxp))
+     * *(1.d0-vvx)*(1.d0-vvxt)**2*(1.d0-vvxtl)*exp(-2.d0*vtac(it))
+      elseif(itypom.eq.3)then      !'fan' (cut/uncut end - rapgap)
+       gb0=((max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp))
+     * +((1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl)
+     * +2.d0*vpacng*exp(-2.d0*vpac(ipp))*(1.d0-vvxp)**2*(1.d0-vvxpl))
+     * *(vicc-wgpm(m)*viuc)*(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+     * *(1.d0-vvx)*(1.d0-vvxt)*exp(-vtac(it))
+      elseif(itypom.eq.4)then          !'leg' (single cut Pomeron at the end)
+       gb0=vip*((vplc0-vplcpe)*exp(-vpac(ipp))*(1.d0-vvxp)
+     * *(1.d0-vvxpl))*exp(-vpac(ipp)-2.d0*vtac(it))*(1.d0-vvxp)
+     * *(1.d0-vvx)*(1.d0-vvxt)**2*(1.d0-vvxtl)
+       if(gb0.le.0.d0)then
+        gb0=vip*vplc0*.01d0*exp(-vpac(ipp))*(1.d0-vvxp)
+     *  *(1.d0-vvxpl)*exp(-vpac(ipp)-2.d0*vtac(it))*(1.d0-vvxp)
+     *  *(1.d0-vvx)*(1.d0-vvxt)**2*(1.d0-vvxtl)
+       endif
+      elseif(itypom.eq.5)then      !'leg' (cut/uncut end - rapgap)
+       gb0=vplcng*exp(-2.d0*vpac(ipp)-vtac(it))
+     * *(1.d0-vvxp)**2*(1.d0-vvxpl)*(1.d0-vvx)*(1.d0-vvxt)
+     * *(vicc-wgpm(m)*viuc)*(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+      elseif(itypom.eq.7)then      !'leg' (>1 cut Poms at the end)
+       gb0=vimp*(vplc0*exp(-vpac(ipp))*(1.d0-vvxp)*(1.d0-vvxpl)
+     * -(vplc-vplc0)*(1.d0-exp(-vpac(ipp))*(1.d0-vvxp)*(1.d0-vvxpl)))
+     * *exp(-vpac(ipp)-2.d0*vtac(it))*(1.d0-vvxp)
+     * *(1.d0-vvx)*(1.d0-vvxt)**2*(1.d0-vvxtl)
+      endif
+      if(gb0.le.0.d0)then      !so170712
+       iret=1
+       goto 31
+      endif
+      nrej=0
+
+11    xpomm(m+1)=(xpomm(m)*sgap**2)**qgran(b10)/sgap
+      if(itypom.eq.4)xpomm(m+1)=(xpomm(m)*sgap**3)**qgran(b10)/sgap**2
+      rp1=(rq(iddp(ipp),icz)-alfp*dlog(xpomm(m+1)))*4.d0*.0389d0
+      rp2=alfp*dlog(xpomm(m+1)/xpomm(m))*4.d0*.0389d0
+      rp=rp1*rp2/(rp1+rp2)
+      z=qgran(b10)
+      phi=pi*qgran(b10)
+      b0=dsqrt(-rp*dlog(z))
+      bbp=(dsqrt(bpm)*rp1/(rp1+rp2)+b0*cos(phi))**2+(b0*sin(phi))**2
+      bbi=(dsqrt(bpm)*rp2/(rp1+rp2)-b0*cos(phi))**2+(b0*sin(phi))**2
+      call qgbdef(bbp,bbi,xa(ipp,1)+b,xa(ipp,2),xxm(m),yym(m)
+     *,xxm(m+1),yym(m+1),int(1.5d0+qgran(b10)))   !coordinates for the vertex
+
+      call qgfdf(xxm(m+1),yym(m+1),xpomm(m+1),vpac,vtac
+     *,vvx,vvxp,vvxt,vvxpl,vvxtl,ipp,it)
+      vvxts=1.d0-(1.d0-vvx)*(1.d0-vvxt)*exp(-vtac(it))
+      viu=qgpini(xpomm(m+1)/xpomm(m),bbi,0.d0,0.d0,2)
+      vim=2.d0*min(viu,qgpini(xpomm(m+1)/xpomm(m),bbi,0.d0,0.d0,8))
+      if(itypom.eq.-1.or.itypom.eq.4)then         !single cut Pomeron at the end
+       vvxi=1.d0-((1.d0-vvx)*(1.d0-vvxp)*(1.d0-vvxt))**2
+     * *exp(-2.d0*vpac(ipp)-2.d0*vtac(it))
+       vip=qgpini(xpomm(m+1)/xpomm(m),bbi,vvxi,0.d0,16)*exp(-vim)
+      elseif(itypom.eq.2.or.itypom.eq.7)then       !>1 cut Poms at the end
+       vimp=max(0.d0,1.d0-exp(-vim)*(1.d0+vim))
+      else                                         !rap-gap
+       vvxpin=1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ipp))
+       vvxtin=1.d0-(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+       viuu=qgpini(xpomm(m+1)/xpomm(m),bbi,vvxpin,vvxtin,20)
+     * *(1.d0-exp(-viu))
+       viuc=max(0.d0,viuu-qgpini(xpomm(m+1)/xpomm(m),bbi
+     * ,vvxpin,vvxtin,21)*(1.d0-exp(-viu)))
+       vicc=qgpini(xpomm(m+1)/xpomm(m),bbi,vvxpin,vvxtin,22)*.5d0
+     * *((1.d0-exp(-viu))**2+(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))
+       vicu=max(0.d0,qgpini(xpomm(m+1)/xpomm(m),bbi,vvxpin,vvxtin,23)
+     * *((1.d0-exp(-viu))**2+(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))
+     * /2.d0-vicc)
+      endif
+
+      if(itypom.le.3)then
+       sumup=0.d0
+       vvxp0=0.d0
+       do i=1,ia(1)
+        sumup=sumup+vpac(i)
+       enddo
+       vvxs=(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+       do i=1,ia(1)-ipp+1
+        ipi=ia(1)-i+1
+        bbl=(xa(ipi,1)+b-xxm(m+1))**2+(xa(ipi,2)-yym(m+1))**2
+        sumup=sumup-vpac(ipi)
+        vpac0(ipi)=min(vpac(ipi)
+     *  ,qgfani(1.d0/xpomm(m+1),bbl,1.d0-vvxs*exp(-sumup)
+     *  ,1.d0-exp(-vvxp0),1.d0-exp(-sumup),iddp(ipi),icz,3))
+        if(ipi.gt.ipp)vvxp0=vvxp0+vpac0(ipi)
+       enddo
+       vvxp0=1.d0-exp(-vvxp0)
+
+       vpacng=min(vpac0(ipp)
+     * ,qgfani(1.d0/xpomm(m+1),bbp,vvxts,vvxp0,vvxpl,iddp(ipp),icz,4))
+       vpacpe=min(vpacng
+     * ,qgfani(1.d0/xpomm(m+1),bbp,vvxts,vvxp0,vvxpl,iddp(ipp),icz,5))
+      else
+       vplc=qgfani(1.d0/xpomm(m+1),bbp,vvxts,vvxp,vvxpl,iddp(ipp)
+     * ,icz,7)
+       vplc0=min(vplc
+     * ,qgfani(1.d0/xpomm(m+1),bbp,vvxts,vvxp,vvxpl,iddp(ipp),icz,8))
+       vplcng=min(vplc0
+     * ,qgfani(1.d0/xpomm(m+1),bbp,vvxts,vvxp,vvxpl,iddp(ipp),icz,11))
+       vplcpe=min(vplcng
+     * ,qgfani(1.d0/xpomm(m+1),bbp,vvxts,vvxp,vvxpl,iddp(ipp),icz,10))
+      endif
+
+      if(itypom.eq.-1)then          !'fan' (single cut Pomeron at the end)
+       gb=vip*((max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp))
+     * +((1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl)
+     * +2.d0*((vpac0(ipp)-vpacpe)*exp(-vpac(ipp))*(1.d0-vvxp)
+     * *(1.d0-vvxpl)-(vpac(ipp)-vpac0(ipp))*(1.d0-exp(-vpac(ipp))
+     * *(1.d0-vvxp)*(1.d0-vvxpl)))*exp(-vpac(ipp))*(1.d0-vvxp))
+     * *(1.d0-vvx)*(1.d0-vvxt)**2*(1.d0-vvxtl)*exp(-2.d0*vtac(it))
+      elseif(itypom.eq.0)then      !'fan' (cut loop at the end - rapgap)
+       gb=((max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp))
+     * +((1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl)
+     * +2.d0*vpacng*exp(-2.d0*vpac(ipp))*(1.d0-vvxp)**2*(1.d0-vvxpl))
+     * *(vicc*(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+     * -vicu*(1.d0-(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))))
+     * *(1.d0-vvx)*(1.d0-vvxt)*exp(-vtac(it))
+     * -2.d0*vicu*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp)-vtac(it))
+     * *(1.d0-vvx)*(1.d0-vvxt)
+      elseif(itypom.eq.1)then      !'fan' (uncut end - rapgap)
+       gb=((max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp))
+     * +((1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl)
+     * +2.d0*vpacng*exp(-2.d0*vpac(ipp))*(1.d0-vvxp)**2*(1.d0-vvxpl))
+     * *(viuc*(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+     * +viuu*(1.d0-(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))))
+     * *(1.d0-vvx)*(1.d0-vvxt)*exp(-vtac(it))
+     * +2.d0*viuu*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp)-vtac(it))
+     * *(1.d0-vvx)*(1.d0-vvxt)
+      elseif(itypom.eq.2)then        !'fan' (>1 cut Poms at the end)
+       gb=vimp*((max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp))
+     * +((1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl)
+     * +2.d0*(vpac0(ipp)*exp(-vpac(ipp))*(1.d0-vvxp)
+     * *(1.d0-vvxpl)-(vpac(ipp)-vpac0(ipp))*(1.d0-exp(-vpac(ipp))
+     * *(1.d0-vvxp)*(1.d0-vvxpl)))*exp(-vpac(ipp))*(1.d0-vvxp))
+     * *(1.d0-vvx)*(1.d0-vvxt)**2*(1.d0-vvxtl)*exp(-2.d0*vtac(it))
+      elseif(itypom.eq.3)then      !'fan' (cut/uncut end - rapgap)
+       gb=((max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp))
+     * +((1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl)
+     * +2.d0*vpacng*exp(-2.d0*vpac(ipp))*(1.d0-vvxp)**2*(1.d0-vvxpl))
+     * *((vicc-wgpm(m)*viuc)*(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+     * -(vicu+wgpm(m)*viuu)*(1.d0-(1.d0-vvxt)*(1.d0-vvxtl)
+     * *exp(-vtac(it))))*(1.d0-vvx)*(1.d0-vvxt)*exp(-vtac(it))
+     * -2.d0*(vicu+wgpm(m)*viuu)*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))
+     * -1.d0-(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp)-vtac(it))
+     * *(1.d0-vvx)*(1.d0-vvxt)
+      elseif(itypom.eq.4)then          !'leg' (single cut Pomeron at the end)
+       gb=vip*((vplc0-vplcpe)*exp(-vpac(ipp))*(1.d0-vvxp)
+     * *(1.d0-vvxpl)-(vplc-vplc0)*(1.d0-exp(-vpac(ipp))*(1.d0-vvxp)
+     * *(1.d0-vvxpl)))*exp(-vpac(ipp)-2.d0*vtac(it))*(1.d0-vvxp)
+     * *(1.d0-vvx)*(1.d0-vvxt)**2*(1.d0-vvxtl)
+      elseif(itypom.eq.5)then      !'leg' (cut/uncut end - rapgap)
+       gb=vplcng*exp(-2.d0*vpac(ipp)-vtac(it))
+     * *(1.d0-vvxp)**2*(1.d0-vvxpl)*(1.d0-vvx)*(1.d0-vvxt)
+     * *((vicc-wgpm(m)*viuc)*(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+     * -(vicu+wgpm(m)*viuu)*(1.d0-(1.d0-vvxt)*(1.d0-vvxtl)
+     * *exp(-vtac(it))))
+      elseif(itypom.eq.7)then      !'leg' (>1 cut Poms at the end)
+       gb=vimp*(vplc0*exp(-vpac(ipp))*(1.d0-vvxp)*(1.d0-vvxpl)
+     * -(vplc-vplc0)*(1.d0-exp(-vpac(ipp))*(1.d0-vvxp)*(1.d0-vvxpl)))
+     * *exp(-vpac(ipp)-2.d0*vtac(it))*(1.d0-vvxp)
+     * *(1.d0-vvx)*(1.d0-vvxt)**2*(1.d0-vvxtl)
+      endif
+      gb=gb/gb0/z*rp/rp0  /10.d0
+      nrej=nrej+1
+      if(qgran(b10).gt.gb.and.nrej.le.1000)goto 11
+
+      if(itypom.eq.-1.or.itypom.eq.4)then  !'single cut Pomeron in the handle
+       npin=npin+1
+       if(npin.gt.npmax)then
+        iret=1
+        goto 31
+       endif
+       xpomim(npin)=1.d0/xpomm(m)/scm
+       xpomip(npin)=xpomm(m+1)
+       vvxim(npin)=vvxi
+       bpomim(npin)=bbi
+       if(debug.ge.4)write (moniou,211)npin,xpomip(npin),xpomim(npin)
+     * ,vvxim(npin),bpomim(npin)
+      elseif(itypom.eq.2.or.itypom.eq.7)then   !>1 cut Pomerons in the handle
+       ninc=npgen(vim,2,20)
+       npin=npin+ninc
+       if(npin.gt.npmax)then
+        iret=1
+        goto 31
+       endif
+       do i=npin-ninc+1,npin
+        xpomim(i)=1.d0/xpomm(m)/scm
+        xpomip(i)=xpomm(m+1)
+        vvxim(i)=0.d0
+        bpomim(i)=bbi
+        if(debug.ge.4)write (moniou,211)i,xpomip(i),xpomim(i)
+     *  ,vvxim(i),bpomim(i)
+       enddo
+      endif
+
+      if(itypom.eq.-1)then      !single cut Pomeron in the 'handle'
+       vv1=(max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp))
+       vv2=(1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl
+       vv3=2.d0*((vpac0(ipp)-vpacpe)*exp(-vpac(ipp))*(1.d0-vvxp)
+     * *(1.d0-vvxpl)-(vpac(ipp)-vpac0(ipp))*(1.d0-exp(-vpac(ipp))
+     * *(1.d0-vvxp)*(1.d0-vvxpl)))*exp(-vpac(ipp))*(1.d0-vvxp)
+       if(xpomm(m+1)*sgap**2.gt..9d0.or.vv3.lt.0.d0)vv3=0.d0
+       aks=(vv1+vv2+vv3)*qgran(b10)
+       if(aks.lt.vv1)then
+        jt=1                     !>1 cut fans
+       elseif(aks.lt.vv1+vv2)then
+        jt=2                     !diffr. cut
+       else
+        jt=3                     !1 cut fan
+       endif
+      elseif(itypom.eq.0)then    !cut 'loop' in the 'handle' (rap-gap)
+       vv1=(max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp)-vtac(it))
+     * *(1.d0-vvxt)*(1.d0-vvxtl)*(vicc+vicu)
+     * /(vicc*(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+     * -vicu*(1.d0-(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))))
+       vv2=(1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl
+       vv3=2.d0*vpacng*exp(-2.d0*vpac(ipp))*(1.d0-vvxp)**2*(1.d0-vvxpl)
+       aks=(vv1+vv2+vv3)*qgran(b10)
+       if(aks.lt.vv1)then
+        jt=1                     !>1 cut fans
+       elseif(aks.lt.vv1+vv2)then
+        jt=2                     !diffr. cut
+       else
+        jt=3                     !1 cut fan
+       endif
+      elseif(itypom.eq.1)then    !uncut 'handle' (rap-gap)
+       vv1=(max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp))
+       vv2=(1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl
+       vv3=2.d0*vpacng*exp(-2.d0*vpac(ipp))*(1.d0-vvxp)**2*(1.d0-vvxpl)
+       vv4=2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0-(vpac(ipp)
+     * -vpac0(ipp)))*(1.d0-vvxp0)+(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))
+     * *exp(-vpac(ipp))*viuu/(viuu*(1.d0-(1.d0-vvxt)*(1.d0-vvxtl)
+     * *exp(-vtac(it)))+viuc*(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it)))
+       if(xpomm(m+1)*sgap**2.gt..9d0.or.vv4.lt.0.d0)vv4=0.d0
+       aks=(vv1+vv2+vv3+vv4)*qgran(b10)
+       if(aks.lt.vv1)then
+        jt=1                     !>1 cut fans
+       elseif(aks.lt.vv1+vv2)then
+        jt=2                     !diffr. cut
+       elseif(aks.lt.vv1+vv2+vv3)then
+        jt=3                     !1 cut fan
+       else
+        jt=4                     !>1 cut 'handle' fans
+       endif
+      elseif(itypom.eq.2)then    !>1 cut Pomerons in the 'handle'
+       vv1=(max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))*exp(-vpac(ipp))
+       vv2=(1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl
+       vv3=2.d0*(vpac0(ipp)*exp(-vpac(ipp))*(1.d0-vvxp)
+     * *(1.d0-vvxpl)-(vpac(ipp)-vpac0(ipp))*(1.d0-exp(-vpac(ipp))
+     * *(1.d0-vvxp)*(1.d0-vvxpl)))*exp(-vpac(ipp))*(1.d0-vvxp)
+       aks=(vv1+vv2+vv3)*qgran(b10)
+       if(aks.lt.vv1)then
+        jt=1                     !>1 cut fans
+       elseif(aks.lt.vv1+vv2)then
+        jt=2                     !diffr. cut
+       else
+        jt=3                     !1 cut fan
+       endif
+
+      elseif(itypom.eq.3)then    !rap-gap in the 'handle'
+       vv1=(max(0.d0,1.d0-exp(-2.d0*vpac(ipp))
+     * *(1.d0+2.d0*vpac(ipp)))+2.d0*vpac(ipp)*exp(-2.d0*vpac(ipp))
+     * *(1.d0-(1.d0-vvxp)**2))*(1.d0-vvxpl)
+     * -2.d0*(max(0.d0,exp(vpac(ipp)-vpac0(ipp))-1.d0
+     * -(vpac(ipp)-vpac0(ipp)))*(1.d0-vvxp0)
+     * +(vpac(ipp)-vpac0(ipp))*(vvxp-vvxp0))
+     * *exp(-vpac(ipp)-vtac(it))*(1.d0-vvxt)*(1.d0-vvxtl)
+     * *(vicc+vicu+wgpm(m)*(viuu-viuc))
+     * /((vicc-wgpm(m)*viuc)*(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+     * -(vicu+wgpm(m)*viuu)*(1.d0-(1.d0-vvxt)*(1.d0-vvxtl)
+     * *exp(-vtac(it))))
+       vv2=(1.d0-exp(-vpac(ipp)))**2*(1.d0-vvxpl)
+     * +2.d0*(1.d0-exp(-vpac(ipp)))*vvxpl
+       vv3=2.d0*vpacng*exp(-2.d0*vpac(ipp))*(1.d0-vvxp)**2
+     * *(1.d0-vvxpl)
+       aks=(vv1+vv2+vv3)*qgran(b10)
+       if(aks.lt.vv1)then
+        jt=1                     !>1 cut fans
+       elseif(aks.lt.vv1+vv2)then
+        jt=2                     !diffr. cut
+       else
+        jt=3                     !1 cut fan
+       endif
+      else
+       jt=5                      !cut leg
+      endif
+
+      nppm(m+1)=0
+      wgpm(m+1)=0.d0
+      if(jt.eq.1)then                        !>1 cut fans
+       ntry=0
+12     ntry=ntry+1
+       nphm=0
+       if(ipp.eq.ia(1).or.ntry.gt.100)then
+        nppm(m+1)=npgen(2.d0*vpac(ipp),2,20)
+        do i=1,nppm(m+1)
+         if(qgran(b10).le.vpac0(ipp)/vpac(ipp)
+     *   .or.xpomm(m+1)*sgap**2.gt..9d0)then
+          itypm(i,m+1)=0
+         else
+          itypm(i,m+1)=1
+          nphm=nphm+1
+         endif
+         ippm(i,m+1)=ipp
+        enddo
+        wh=(vpac(ipp)/vpac0(ipp)-1.d0)/nppm(m+1)
+       else
+        nppm(m+1)=npgen(2.d0*vpac(ipp),1,20)
+        do i=1,nppm(m+1)
+         if(qgran(b10).le.vpac0(ipp)/vpac(ipp)
+     *   .or.xpomm(m+1)*sgap**2.gt..9d0)then
+          itypm(i,m+1)=0
+         else
+          itypm(i,m+1)=1
+          nphm=nphm+1
+         endif
+         ippm(i,m+1)=ipp
+        enddo
+        wh=(vpac(ipp)/vpac0(ipp)-1.d0)/nppm(m+1)
+        do ipi=ipp+1,ia(1)
+         ninc=npgen(2.d0*vpac(ipi),0,20)
+         if(ninc.ne.0)then
+          nppm(m+1)=nppm(m+1)+ninc
+          nh0=nphm
+          if(nppm(m+1).gt.legmax)then
+           iret=1
+           goto 31
+          endif
+          do i=nppm(m+1)-ninc+1,nppm(m+1)
+           if(qgran(b10).le.vpac0(ipi)/vpac(ipi)
+     *     .or.xpomm(m+1)*sgap**2.gt..9d0)then
+            itypm(i,m+1)=0
+           else
+            itypm(i,m+1)=1
+            nphm=nphm+1
+           endif
+           ippm(i,m+1)=ipi
+          enddo
+          if(ninc.gt.nphm-nh0)wh=(vpac(ipi)/vpac0(ipi)-1.d0)/ninc
+         endif
+        enddo
+        if(nppm(m+1).eq.1)goto 12
+       endif
+
+       if(nphm+1.ge.nppm(m+1))then
+        if(itypom.eq.-1.or.itypom.eq.1.or.itypom.eq.2)then
+         gbt=1.d0-exp(vpac(ipp)+(1.d0-nphm)*dlog(2.d0))
+     *   /(1.d0-vvxp)/(1.d0-vvxpl)
+        elseif(itypom.eq.0)then
+         gbt=1.d0-(vicc+vicu)*(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+     *   /(vicc*(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+     *   -vicu*(1.d0-(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))))
+     *   *exp(vpac(ipp)+(1.d0-nphm)*dlog(2.d0))
+     *   /(1.d0-vvxp)/(1.d0-vvxpl)
+        elseif(itypom.eq.3)then
+         gbt=1.d0-(vicc+vicu+wgpm(m)*(viuu-viuc))
+     *   *(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))
+     *   /((vicc-wgpm(m)*viuc)*(1.d0-vvxt)*(1.d0-vvxtl)
+     *   *exp(-vtac(it))-(vicu+wgpm(m)*viuu)
+     *   *(1.d0-(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(it))))
+     *   *exp(vpac(ipp)+(1.d0-nphm)*dlog(2.d0))
+     *   /(1.d0-vvxp)/(1.d0-vvxpl)
+        else
+         stop'unknown itypom'
+        endif
+        if(nphm.eq.nppm(m+1).and.qgran(b10).gt.gbt
+     *  .or.nphm+1.eq.nppm(m+1).and.qgran(b10).gt.1.d0+wh*gbt)then
+         ntry=0
+          goto 12
+        endif
+       endif
+
+      elseif(jt.eq.4)then                    !>1 cut 'handle' fans
+       ntry=0
+14     ntry=ntry+1
+       if(ipp.eq.ia(1).or.ntry.gt.100)then
+        nppm(m+1)=npgen(vpac(ipp)-vpac0(ipp),2,20)
+        do i=1,nppm(m+1)
+          itypm(i,m+1)=1
+         ippm(i,m+1)=ipp
+        enddo
+       else
+        nppm(m+1)=npgen(vpac(ipp)-vpac0(ipp),1,20)
+        do i=1,nppm(m+1)
+         itypm(i,m+1)=1
+         ippm(i,m+1)=ipp
+        enddo
+        do ipi=ipp+1,ia(1)
+         ninc=npgen(vpac(ipi)-vpac0(ipi),0,20)
+         if(ninc.ne.0)then
+          nppm(m+1)=nppm(m+1)+ninc
+          if(nppm(m+1).gt.legmax)then
+           iret=1
+           goto 31
+          endif
+          do i=nppm(m+1)-ninc+1,nppm(m+1)
+           itypm(i,m+1)=1
+           ippm(i,m+1)=ipi
+          enddo
+         endif
+        enddo
+        if(nppm(m+1).eq.1)goto 14
+       endif
+
+      elseif(jt.eq.3)then                    !1 cut fan
+       nppm(m+1)=1
+       ippm(1,m+1)=ipp
+       if(itypom.eq.-1)then             !single cut Pomeron in the 'handle'
+        factor=exp(-vpac(ipp))*(1.d0-vvxp)*(1.d0-vvxpl)
+        wng=(vpacng-vpacpe)*factor/((vpac0(ipp)-vpacpe)*factor
+     *  -(vpac(ipp)-vpac0(ipp))*(1.d0-factor))
+        if(qgran(b10).le.wng.or.wng.lt.0.d0
+     *  .or.xpomm(m+1)*sgap**2.gt..9d0)then
+         itypm(1,m+1)=2          !>1 cut Pomerons in the 'handle'
+        else
+         itypm(1,m+1)=3          !rap-gap in the 'handle'
+         wgpm(m+1)=(1.d0-factor)/factor
+        endif
+       elseif(itypom.eq.2)then          !>1 cut Pomerons in the 'handle'
+        factor=exp(-vpac(ipp))*(1.d0-vvxp)*(1.d0-vvxpl)
+        wng=vpacng*factor/(vpac0(ipp)*factor
+     *  -(vpac(ipp)-vpac0(ipp))*(1.d0-factor))
+        if(qgran(b10).le.wng.or.wng.lt.0.d0
+     *  .or.xpomm(m+1)*sgap**2.gt..9d0)then
+         if(qgran(b10).le.vpacpe/vpacng
+     *   .or.xpomm(m+1)*sgap**2.gt..9d0)then
+          itypm(1,m+1)=-1        !single cut Pomeron in the 'handle'
+         else
+          itypm(1,m+1)=2         !>1 cut Pomerons in the 'handle'
+         endif
+        else
+         itypm(1,m+1)=3          !rap-gap in the 'handle'
+         wgpm(m+1)=(1.d0-factor)/factor
+        endif
+       else                             !rap-gap in the 'handle'
+        if(qgran(b10).le.vpacpe/vpacng
+     *  .or.xpomm(m+1)*sgap**2.gt..9d0)then
+         itypm(1,m+1)=-1         !single cut Pomeron in the 'handle'
+        else
+         itypm(1,m+1)=2          !>1 cut Pomerons in the 'handle'
+        endif
+       endif
+
+       if(itypm(1,m+1).eq.-1)then     !single cut Pomeron in the 'handle'
+        vplcp=min(vpacpe
+     *  ,qgfani(1.d0/xpomm(m+1),bbp,vvxts,vvxp,vvxpl,iddp(ipp),icz,9))
+        if(qgran(b10).le.vplcp/vpacpe
+     *  .or.xpomm(m+1)*sgap**2.gt..9d0)itypm(1,m+1)=6 !single cut Pomeron
+       endif
+
+      elseif(jt.eq.5)then                    !cut 'leg'
+       nppm(m+1)=1
+       ippm(1,m+1)=ipp
+       if(itypom.eq.4)then              !single cut Pomeron at the end
+        if(xpomm(m+1)*sgap**2.ge.1.d0)stop'=4:xpomm(m+1)*sgap**2>1'
+        factor=exp(-vpac(ipp))*(1.d0-vvxp)*(1.d0-vvxpl)
+        wng=(vplcng-vplcpe)*factor/((vplc0-vplcpe)*factor
+     *  -(vplc-vplc0)*(1.d0-factor))
+        if(qgran(b10).le.wng.or.wng.lt.0.d0)then
+         itypm(1,m+1)=7          !>1 cut Pomerons at the end
+        else
+         itypm(1,m+1)=5          !rap-gap at the end
+         wgpm(m+1)=(1.d0-factor)/factor
+        endif
+       elseif(itypom.eq.5)then          !rap-gap at the end (cut or uncut loop)
+        if(qgran(b10).le.vplcpe/vplcng
+     *  .or.xpomm(m+1)*sgap**2.gt..9d0)then
+         itypm(1,m+1)=4          !single cut Pomeron at the end
+        else
+         itypm(1,m+1)=7          !>1 cut Pomerons at the end
+        endif
+       elseif(itypom.eq.7)then          !>1 cut Pomerons at the end
+        factor=exp(-vpac(ipp))*(1.d0-vvxp)*(1.d0-vvxpl)
+        wng=vplcng*factor/(vplc0*factor-(vplc-vplc0)*(1.d0-factor))
+        if(qgran(b10).le.wng.or.wng.lt.0.d0
+     *  .or.xpomm(m+1)*sgap**2.gt..9d0)then
+         if(qgran(b10).le.vplcpe/vplcng
+     *   .or.xpomm(m+1)*sgap**2.gt..9d0)then
+          itypm(1,m+1)=4         !single cut Pomeron at the end
+         else
+          itypm(1,m+1)=7         !>1 cut Pomerons at the end
+         endif
+        else
+         itypm(1,m+1)=5          !rap-gap at the end
+         wgpm(m+1)=(1.d0-factor)/factor
+        endif
+       endif
+
+       if(itypm(1,m+1).eq.4)then        !single cut Pomeron at the end
+        vplcp=min(vplcpe
+     *  ,qgfani(1.d0/xpomm(m+1),bbp,vvxts,vvxp,vvxpl,iddp(ipp),icz,9))
+        if(qgran(b10).le.vplcp/vplcpe
+     *  .or.xpomm(m+1)*sgap**3.gt..9d0)itypm(1,m+1)=6 !single cut Pomeron
+       endif
+      endif
+
+      if(nppm(m+1).eq.1.and.itypm(1,m+1).eq.6)then  !record single cut Pomeron
+       nppr=nppr+1
+       if(nppr.gt.legmax)then
+        iret=1
+        goto 31
+       endif
+       xpompi(nppr)=xpomm(m+1)
+       vvxpi(nppr)=1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*(1.d0-vvxt)
+     * *(1.d0-vvxtl)*exp(-vtac(it))
+       ipompi(nppr)=ipp
+       bpompi(nppr)=bbp
+       nppm(m+1)=0
+       if(debug.ge.4)write (moniou,209)nppr,ipp,bbp,xpompi(nppr)
+     * ,vvxpi(nppr)
+
+      elseif(nppm(m+1).gt.1)then
+       i=0
+15     i=i+1
+       ityp=itypm(i,m+1)
+       if(ityp.eq.0)then
+        ipi=ippm(i,m+1)
+        bbi=(xa(ipi,1)+b-xxm(m+1))**2+(xa(ipi,2)-yym(m+1))**2
+        vvxp=0.d0
+        vvxpl=0.d0
+        vvxp0=0.d0
+        if(ia(1).gt.1)then
+         do l=1,ia(1)
+          if(l.lt.ipi)then
+           vvxpl=vvxpl+vpac(l)
+          elseif(l.gt.ipi)then
+           vvxp=vvxp+vpac(l)
+           vvxp0=vvxp0+vpac0(l)
+          endif
+         enddo
+        endif
+        vvxp=1.d0-exp(-vvxp)
+        vvxpl=1.d0-exp(-vvxpl)
+        vvxp0=1.d0-exp(-vvxp0)
+
+        vpacng=min(vpac0(ipi)
+     *  ,qgfani(1.d0/xpomm(m+1),bbi,vvxts,vvxp0,vvxpl,iddp(ipi),icz,4))
+        vpacpe=min(vpacng
+     *  ,qgfani(1.d0/xpomm(m+1),bbi,vvxts,vvxp0,vvxpl,iddp(ipi),icz,5))
+        vplcp=min(vpacpe
+     *  ,qgfani(1.d0/xpomm(m+1),bbi,vvxts,vvxp,vvxpl,iddp(ipi),icz,9))
+
+        aks=qgran(b10)*vpac0(ipi)
+        if(aks.le.vplcp.or.xpomm(m+1)*sgap**2.gt..9d0)then
+         itypm(i,m+1)=6          !single cut Pomeron
+        elseif(aks.lt.vpacpe)then
+         itypm(i,m+1)=-1         !single cut Pomeron in the 'handle'
+        elseif(aks.lt.vpacng)then
+         itypm(i,m+1)=2          !>1 cut Pomerons in the 'handle'
+        endif
+
+        if(itypm(i,m+1).eq.6)then      !record single cut Pomeron
+         nppr=nppr+1
+         if(nppr.gt.legmax)then
+          iret=1
+          goto 31
+         endif
+         xpompi(nppr)=xpomm(m+1)
+         vvxpi(nppr)=1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*(1.d0-vvxt)
+     *   *(1.d0-vvxtl)*exp(-vtac(it))
+         ipompi(nppr)=ipi
+         bpompi(nppr)=bbi
+         if(debug.ge.4)write (moniou,209)nppr,ipi,bbi,xpompi(nppr)
+     *   ,vvxpi(nppr)
+         nppm(m+1)=nppm(m+1)-1
+         if(nppm(m+1).ge.i)then
+          do l=i,nppm(m+1)
+           ippm(l,m+1)=ippm(l+1,m+1)
+           itypm(l,m+1)=itypm(l+1,m+1)
+          enddo
+         endif
+         i=i-1
+        endif
+       endif
+       if(i.lt.nppm(m+1))goto 15
+      endif
+
+      if(jt.eq.2.and.qgran(b10).lt.(1.d0-exp(-vpac(ipp)))*(1.d0-vvxpl)
+     */((1.d0-exp(-vpac(ipp)))*(1.d0-vvxpl)+2.d0*vvxpl))then
+       if(debug.ge.4)write (moniou,212)
+       icdps=iddp(ipp)
+       do icdp=1,2
+        iddp(ipp)=icdp
+        call qgfdf(xxm(m+1),yym(m+1),xpomm(m+1),vpac,vtac
+     *  ,vvx,vvxp,vvxt,vvxpl,vvxtl,ipp,it)
+        wdp(icdp,ipp)=(1.d0-exp(-vpac(ipp)))*(1.d0-vvxpl)
+       enddo
+       iddp(ipp)=icdps
+      endif
+
+      if(nppm(m+1).ne.0)then
+       goto 9
+      else
+       goto 10
+      endif
+
+20    continue
+      if(debug.ge.3)write (moniou,214)nppr
+      if(nptg0.eq.0)goto 31
+
+c target 'fans'
+      m=0
+      nppm(1)=nptg0
+      xpomm(1)=xpomr
+      wgpm(1)=wgtg0
+      xxm(1)=xxp
+      yym(1)=yyp
+      do i=1,nptg0
+       ippm(i,1)=iptg0(i)
+       itypm(i,1)=itytg0(i)
+      enddo
+
+21    m=m+1                                   !next level multi-Pomeron vertex
+      if(m.gt.levmax)then
+       iret=1
+       goto 31
+      endif
+      ii(m)=0
+22    ii(m)=ii(m)+1                           !next cut fan in the vertex
+      if(ii(m).gt.nppm(m))then                !all fans at the level considered
+       m=m-1                                  !one level down
+       if(m.eq.0)goto 31                      !all targ. fans considered
+       goto 22
+      endif
+      l=ii(m)
+      itt=ippm(l,m)                           !targ. index for the leg
+      itypom=itypm(l,m)                       !type of the cut
+      btm=(xb(itt,1)-xxm(m))**2+(xb(itt,2)-yym(m))**2  !b^2 for the leg
+      if(debug.ge.4)write (moniou,216)ii(m),m,itt,btm
+      if(xpomm(m)*scm.lt.sgap**2)stop'xpomm(m)*scm<sgap**2!'
+
+      if(debug.ge.4)write (moniou,210)m
+      xpomr0=min(dsqrt(xpomm(m)/scm),xpomm(m)/sgap)
+      xpomr0=max(xpomr0,sgap/scm)
+      if(itypom.eq.4)xpomr0=max(xpomr0,dsqrt(xpomm(m)*sgap/scm))
+      rp1=(rq(iddt(itt),2)+alfp*dlog(xpomr0*scm))*4.d0*.0389d0
+      rp2=alfp*dlog(xpomm(m)/xpomr0)*4.d0*.0389d0
+      rp0=rp1*rp2/(rp1+rp2)
+      bbt=btm*(rp1/(rp1+rp2))**2
+      bbi=btm*(rp2/(rp1+rp2))**2
+      call qgbdef(bbt,bbi,xb(itt,1),xb(itt,2),xxm(m),yym(m)
+     *,xxp0,yyp0,1)
+
+      call qgfdf(xxp0,yyp0,xpomr0,vpac,vtac
+     *,vvx,vvxp,vvxt,vvxpl,vvxtl,ip,itt)
+      vvxps=1.d0-(1.d0-vvx)*(1.d0-vvxp)*exp(-vpac(ip))
+      viu=qgpini(xpomm(m)/xpomr0,bbi,0.d0,0.d0,2)
+      vim=2.d0*min(viu,qgpini(xpomm(m)/xpomr0,bbi,0.d0,0.d0,8))
+      if(itypom.eq.-1.or.itypom.eq.4)then      !single cut Pomeron at the end
+       vvxi=1.d0-((1.d0-vvx)*(1.d0-vvxp)*(1.d0-vvxt))**2
+     * *exp(-2.d0*vpac(ip)-2.d0*vtac(itt))
+       vip=qgpini(xpomm(m)/xpomr0,bbi,vvxi,0.d0,16)*exp(-vim)
+      elseif(itypom.eq.2.or.itypom.eq.7)then   !>1 cut Pomerons at the end
+       vimp=max(0.d0,1.d0-exp(-vim)*(1.d0+vim))
+      else                                     !rap-gap at the end
+       vvxpin=1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+       vvxtin=1.d0-(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(itt))
+       viuu=qgpini(xpomm(m)/xpomr0,bbi,vvxtin,vvxpin,20)
+     * *(1.d0-exp(-viu))
+       viuc=max(0.d0,viuu-qgpini(xpomm(m)/xpomr0,bbi
+     * ,vvxtin,vvxpin,21)*(1.d0-exp(-viu)))
+       vicc=qgpini(xpomm(m)/xpomr0,bbi,vvxtin,vvxpin,22)*.5d0
+     * *((1.d0-exp(-viu))**2+(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))
+       vicu=max(0.d0,qgpini(xpomm(m)/xpomr0,bbi,vvxtin,vvxpin,23)*.5d0
+     * *((1.d0-exp(-viu))**2+(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))
+     * -vicc)
+      endif
+
+      if(itypom.le.3)then                         !cut 'fan'
+       sumut=0.d0
+       vvxt0=0.d0
+       do i=1,ia(2)
+        sumut=sumut+vtac(i)
+       enddo
+       vvxs=(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+       do i=1,ia(2)-itt+1
+        iti=ia(2)-i+1
+        bbl=(xb(iti,1)-xxp0)**2+(xb(iti,2)-yyp0)**2
+        sumut=sumut-vtac(iti)
+        vtac0(iti)=min(vtac(iti)
+     *  ,qgfani(xpomr0*scm,bbl,1.d0-vvxs*exp(-sumut)
+     *  ,1.d0-exp(-vvxt0),1.d0-exp(-sumut),iddt(iti),2,3))
+        if(iti.gt.itt)vvxt0=vvxt0+vtac0(iti)
+       enddo
+       vvxt0=1.d0-exp(-vvxt0)
+       vtacng=min(vtac0(itt)
+     * ,qgfani(xpomr0*scm,bbt,vvxps,vvxt0,vvxtl,iddt(itt),2,4))
+       vtacpe=min(vtacng
+     * ,qgfani(xpomr0*scm,bbt,vvxps,vvxt0,vvxtl,iddt(itt),2,5))
+      else                                        !cut 'leg'
+       vtlc=qgfani(xpomr0*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,7)
+       vtlc0=min(vtlc
+     * ,qgfani(xpomr0*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,8))
+       vtlcng=min(vtlc0
+     * ,qgfani(xpomr0*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,11))
+       vtlcpe=min(vtlcng
+     * ,qgfani(xpomr0*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,10))
+      endif
+
+      if(itypom.eq.-1)then         !'fan' (single cut Pomeron at the end)
+       gb0=vip*((max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt))
+     * +((1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl)
+     * +2.d0*((vtac0(itt)-vtacpe)*exp(-vtac(itt))*(1.d0-vvxt)
+     * *(1.d0-vvxtl)-(vtac(itt)-vtac0(itt))*(1.d0-exp(-vtac(itt))
+     * *(1.d0-vvxt)*(1.d0-vvxtl)))*exp(-vtac(itt))*(1.d0-vvxt))
+     * *(1.d0-vvx)*(1.d0-vvxp)**2*(1.d0-vvxpl)*exp(-2.d0*vpac(ip))
+       gb0=gb0*40.d0
+      elseif(itypom.eq.0)then      !'fan' (cut loop at the end - rapgap)
+       gb0=((max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt))
+     * +((1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl)
+     * +2.d0*vtacng*exp(-2.d0*vtac(itt))*(1.d0-vvxt)**2*(1.d0-vvxtl))
+     * *(vicc*(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+     * -vicu*(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))))
+     * *(1.d0-vvx)*(1.d0-vvxp)*exp(-vpac(ip))
+     * -2.d0*vicu*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt)-vpac(ip))
+     * *(1.d0-vvx)*(1.d0-vvxp)
+      elseif(itypom.eq.1)then      !'fan' (uncut end - rapgap)
+       gb0=((max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt))
+     * +((1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl)
+     * +2.d0*vtacng*exp(-2.d0*vtac(itt))*(1.d0-vvxt)**2*(1.d0-vvxtl))
+     * *(viuc*(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+     * +viuu*(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))))
+     * *(1.d0-vvx)*(1.d0-vvxp)*exp(-vpac(ip))
+     * +2.d0*viuu*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt)-vpac(ip))
+     * *(1.d0-vvx)*(1.d0-vvxp)
+      elseif(itypom.eq.2)then      !'fan' (>1 cut Poms at the end)
+       gb0=vimp*((max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt))
+     * +((1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl)
+     * +2.d0*(vtac0(itt)*exp(-vtac(itt))*(1.d0-vvxt)
+     * *(1.d0-vvxtl)-(vtac(itt)-vtac0(itt))*(1.d0-exp(-vtac(itt))
+     * *(1.d0-vvxt)*(1.d0-vvxtl)))*exp(-vtac(itt))*(1.d0-vvxt))
+     * *(1.d0-vvx)*(1.d0-vvxp)**2*(1.d0-vvxpl)*exp(-2.d0*vpac(ip))
+      elseif(itypom.eq.3)then      !'fan' (cut/uncut end - rapgap)
+       gb0=((max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt))
+     * +((1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl)
+     * +2.d0*vtacng*exp(-2.d0*vtac(itt))*(1.d0-vvxt)**2*(1.d0-vvxtl))
+     * *(vicc-wgpm(m)*viuc)*(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+     * *(1.d0-vvx)*(1.d0-vvxp)*exp(-vpac(ip))
+      elseif(itypom.eq.4)then      !'leg' (single cut Pomeron at the end)
+       gb0=vip*((vtlc0-vtlcpe)*exp(-vtac(itt))*(1.d0-vvxt)
+     * *(1.d0-vvxtl))*exp(-vtac(itt)-2.d0*vpac(ip))*(1.d0-vvxt)
+     * *(1.d0-vvx)*(1.d0-vvxp)**2*(1.d0-vvxpl)
+       if(gb0.eq.0.d0)then
+        gb0=vip*vtlc0*exp(-vtac(itt))*(1.d0-vvxt)
+     * *(1.d0-vvxtl)*exp(-vtac(itt)-2.d0*vpac(ip))*(1.d0-vvxt)
+     * *(1.d0-vvx)*(1.d0-vvxp)**2*(1.d0-vvxpl)  *.01d0
+       endif
+      elseif(itypom.eq.5)then      !'leg' (cut/uncut end - rapgap)
+       gb0=vtlcng*exp(-2.d0*vtac(itt)-vpac(ip))
+     * *(1.d0-vvxt)**2*(1.d0-vvxtl)*(1.d0-vvx)*(1.d0-vvxp)
+     * *(vicc-wgpm(m)*viuc)*(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+      elseif(itypom.eq.7)then      !'leg' (>1 cut Poms at the end)
+       gb0=vimp*(vtlc0*exp(-vtac(itt))*(1.d0-vvxt)*(1.d0-vvxtl)
+     * -(vtlc-vtlc0)*(1.d0-exp(-vtac(itt))*(1.d0-vvxt)*(1.d0-vvxtl)))
+     * *exp(-vtac(itt)-2.d0*vpac(ip))*(1.d0-vvxt)
+     * *(1.d0-vvx)*(1.d0-vvxp)**2*(1.d0-vvxpl)
+      endif
+      if(gb0.le.0.d0)then      !so170712
+       iret=1
+       goto 31
+      endif
+      nrej=0
+
+23    xpomm(m+1)=xpomm(m)/sgap/(xpomm(m)*scm/sgap**2)**qgran(b10)
+      if(itypom.eq.4)xpomm(m+1)=xpomm(m)/sgap
+     */(xpomm(m)*scm/sgap**3)**qgran(b10)
+      rp1=(rq(iddt(itt),2)+alfp*dlog(xpomm(m+1)*scm))*4.d0*.0389d0
+      rp2=alfp*dlog(xpomm(m)/xpomm(m+1))*4.d0*.0389d0
+      rp=rp1*rp2/(rp1+rp2)
+      z=qgran(b10)
+      phi=pi*qgran(b10)
+      b0=dsqrt(-rp*dlog(z))
+      bbt=(dsqrt(btm)*rp1/(rp1+rp2)+b0*cos(phi))**2+(b0*sin(phi))**2
+      bbi=(dsqrt(btm)*rp2/(rp1+rp2)-b0*cos(phi))**2+(b0*sin(phi))**2
+      call qgbdef(bbt,bbi,xb(itt,1),xb(itt,2),xxm(m),yym(m)
+     *,xxm(m+1),yym(m+1),int(1.5d0+qgran(b10)))   !coordinates for the vertex
+
+      call qgfdf(xxm(m+1),yym(m+1),xpomm(m+1),vpac,vtac
+     *,vvx,vvxp,vvxt,vvxpl,vvxtl,ip,itt)
+      vvxps=1.d0-(1.d0-vvx)*(1.d0-vvxp)*exp(-vpac(ip))
+      viu=qgpini(xpomm(m)/xpomm(m+1),bbi,0.d0,0.d0,2)
+      vim=2.d0*min(viu,qgpini(xpomm(m)/xpomm(m+1),bbi,0.d0,0.d0,8))
+      if(itypom.eq.-1.or.itypom.eq.4)then      !single cut Pomeron at the end
+       vvxi=1.d0-((1.d0-vvx)*(1.d0-vvxp)*(1.d0-vvxt))**2
+     * *exp(-2.d0*vpac(ip)-2.d0*vtac(itt))
+       vip=qgpini(xpomm(m)/xpomm(m+1),bbi,vvxi,0.d0,16)*exp(-vim)
+      elseif(itypom.eq.2.or.itypom.eq.7)then   !>1 cut Pomerons at the end
+        vimp=max(0.d0,1.d0-exp(-vim)*(1.d0+vim))
+      else                                     !rap-gap at the end
+       vvxpin=1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+       vvxtin=1.d0-(1.d0-vvxt)*(1.d0-vvxtl)*exp(-vtac(itt))
+       viuu=qgpini(xpomm(m)/xpomm(m+1),bbi,vvxtin,vvxpin,20)
+     * *(1.d0-exp(-viu))
+       viuc=max(0.d0,viuu-qgpini(xpomm(m)/xpomm(m+1),bbi
+     * ,vvxtin,vvxpin,21)*(1.d0-exp(-viu)))
+       vicc=qgpini(xpomm(m)/xpomm(m+1),bbi,vvxtin,vvxpin,22)*.5d0
+     * *((1.d0-exp(-viu))**2+(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))
+       vicu=max(0.d0,qgpini(xpomm(m)/xpomm(m+1),bbi,vvxtin,vvxpin,23)
+     * *((1.d0-exp(-viu))**2+(exp(2.d0*viu-vim)-1.d0)*exp(-2.d0*viu))
+     * /2.d0-vicc)
+      endif
+
+      if(itypom.le.3)then                         !cut 'fan'
+       sumut=0.d0
+       vvxt0=0.d0
+       do i=1,ia(2)
+        sumut=sumut+vtac(i)
+       enddo
+       vvxs=(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+       do i=1,ia(2)-itt+1
+        iti=ia(2)-i+1
+        bbl=(xb(iti,1)-xxm(m+1))**2+(xb(iti,2)-yym(m+1))**2
+        sumut=sumut-vtac(iti)
+        vtac0(iti)=min(vtac(iti)
+     *  ,qgfani(xpomm(m+1)*scm,bbl,1.d0-vvxs*exp(-sumut)
+     *  ,1.d0-exp(-vvxt0),1.d0-exp(-sumut),iddt(iti),2,3))
+        if(iti.gt.itt)vvxt0=vvxt0+vtac0(iti)
+       enddo
+       vvxt0=1.d0-exp(-vvxt0)
+
+       vtacng=min(vtac0(itt)
+     * ,qgfani(xpomm(m+1)*scm,bbt,vvxps,vvxt0,vvxtl,iddt(itt),2,4))
+       vtacpe=min(vtacng
+     * ,qgfani(xpomm(m+1)*scm,bbt,vvxps,vvxt0,vvxtl,iddt(itt),2,5))
+      else                                        !cut 'leg'
+       vtlc=qgfani(xpomm(m+1)*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,7)
+       vtlc0=min(vtlc
+     * ,qgfani(xpomm(m+1)*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,8))
+       vtlcng=min(vtlc0
+     * ,qgfani(xpomm(m+1)*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,11))
+       vtlcpe=min(vtlcng
+     * ,qgfani(xpomm(m+1)*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,10))
+      endif
+
+      if(itypom.eq.-1)then         !'fan' (single cut Pomeron at the end)
+       gb=vip*((max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt))
+     * +((1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl)
+     * +2.d0*((vtac0(itt)-vtacpe)*exp(-vtac(itt))*(1.d0-vvxt)
+     * *(1.d0-vvxtl)-(vtac(itt)-vtac0(itt))*(1.d0-exp(-vtac(itt))
+     * *(1.d0-vvxt)*(1.d0-vvxtl)))*exp(-vtac(itt))*(1.d0-vvxt))
+     * *(1.d0-vvx)*(1.d0-vvxp)**2*(1.d0-vvxpl)*exp(-2.d0*vpac(ip))
+      elseif(itypom.eq.0)then      !'fan' (cut loop at the end - rapgap)
+       gb=((max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt))
+     * +((1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl)
+     * +2.d0*vtacng*exp(-2.d0*vtac(itt))*(1.d0-vvxt)**2*(1.d0-vvxtl))
+     * *(vicc*(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+     * -vicu*(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))))
+     * *(1.d0-vvx)*(1.d0-vvxp)*exp(-vpac(ip))
+     * -2.d0*vicu*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt)-vpac(ip))
+     * *(1.d0-vvx)*(1.d0-vvxp)
+      elseif(itypom.eq.1)then      !'fan' (uncut end - rapgap)
+       gb=((max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt))
+     * +((1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl)
+     * +2.d0*vtacng*exp(-2.d0*vtac(itt))*(1.d0-vvxt)**2*(1.d0-vvxtl))
+     * *(viuc*(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+     * +viuu*(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))))
+     * *(1.d0-vvx)*(1.d0-vvxp)*exp(-vpac(ip))
+     * +2.d0*viuu*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt)-vpac(ip))
+     * *(1.d0-vvx)*(1.d0-vvxp)
+      elseif(itypom.eq.2)then      !'fan' (>1 cut Poms at the end)
+       gb=vimp*((max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt))
+     * +((1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl)
+     * +2.d0*(vtac0(itt)*exp(-vtac(itt))*(1.d0-vvxt)
+     * *(1.d0-vvxtl)-(vtac(itt)-vtac0(itt))*(1.d0-exp(-vtac(itt))
+     * *(1.d0-vvxt)*(1.d0-vvxtl)))*exp(-vtac(itt))*(1.d0-vvxt))
+     * *(1.d0-vvx)*(1.d0-vvxp)**2*(1.d0-vvxpl)*exp(-2.d0*vpac(ip))
+      elseif(itypom.eq.3)then      !'fan' (cut/uncut end - rapgap)
+       gb=((max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt))
+     * +((1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl)
+     * +2.d0*vtacng*exp(-2.d0*vtac(itt))*(1.d0-vvxt)**2*(1.d0-vvxtl))
+     * *((vicc-wgpm(m)*viuc)*(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+     * -(vicu+wgpm(m)*viuu)*(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)
+     * *exp(-vpac(ip))))*(1.d0-vvx)*(1.d0-vvxp)*exp(-vpac(ip))
+     * -2.d0*(vicu+wgpm(m)*viuu)*(max(0.d0,exp(vtac(itt)-vtac0(itt))
+     * -1.d0-(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt)-vpac(ip))
+     * *(1.d0-vvx)*(1.d0-vvxp)
+      elseif(itypom.eq.4)then      !'leg' (single cut Pomeron at the end)
+       gb=vip*((vtlc0-vtlcpe)*exp(-vtac(itt))*(1.d0-vvxt)
+     * *(1.d0-vvxtl)-(vtlc-vtlc0)*(1.d0-exp(-vtac(itt))*(1.d0-vvxt)
+     * *(1.d0-vvxtl)))*exp(-vtac(itt)-2.d0*vpac(ip))*(1.d0-vvxt)
+     * *(1.d0-vvx)*(1.d0-vvxp)**2*(1.d0-vvxpl)
+      elseif(itypom.eq.5)then      !'leg' (cut/uncut end - rapgap)
+       gb=vtlcng*exp(-2.d0*vtac(itt)-vpac(ip))
+     * *(1.d0-vvxt)**2*(1.d0-vvxtl)*(1.d0-vvx)*(1.d0-vvxp)
+     * *((vicc-wgpm(m)*viuc)*(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+     * -(vicu+wgpm(m)*viuu)*(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)
+     * *exp(-vpac(ip))))
+      elseif(itypom.eq.7)then      !'leg' (>1 cut Poms at the end)
+       gb=vimp*(vtlc0*exp(-vtac(itt))*(1.d0-vvxt)*(1.d0-vvxtl)
+     * -(vtlc-vtlc0)*(1.d0-exp(-vtac(itt))*(1.d0-vvxt)*(1.d0-vvxtl)))
+     * *exp(-vtac(itt)-2.d0*vpac(ip))*(1.d0-vvxt)
+     * *(1.d0-vvx)*(1.d0-vvxp)**2*(1.d0-vvxpl)
+      endif
+      nrej=nrej+1
+      gb=gb/gb0/z*rp/rp0  /10.d0
+      if(qgran(b10).gt.gb.and.nrej.le.1000)goto 23
+
+      if(itypom.eq.-1.or.itypom.eq.4)then    !'single cut Pomeron in the handle
+       npin=npin+1
+       if(npin.gt.npmax)then
+        iret=1
+        goto 31
+       endif
+       xpomim(npin)=1.d0/xpomm(m+1)/scm
+       xpomip(npin)=xpomm(m)
+       vvxim(npin)=vvxi
+       bpomim(npin)=bbi
+       if(debug.ge.4)write (moniou,211)npin,xpomip(npin),xpomim(npin)
+     * ,vvxim(npin),bpomim(npin)
+      elseif(itypom.eq.2.or.itypom.eq.7)then !>1 cut Pomerons in the handle
+       ninc=npgen(vim,2,20)
+       npin=npin+ninc
+       if(npin.gt.npmax)then
+        iret=1
+        goto 31
+       endif
+       do i=npin-ninc+1,npin
+        xpomim(i)=1.d0/xpomm(m+1)/scm
+        xpomip(i)=xpomm(m)
+        vvxim(i)=0.d0
+        bpomim(i)=bbi
+        if(debug.ge.4)write (moniou,211)i,xpomip(i),xpomim(i)
+     *  ,vvxim(i),bpomim(i)
+       enddo
+      endif
+
+      if(itypom.eq.-1)then      !single cut Pomeron in the 'handle'
+       vv1=(max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt))
+       vv2=(1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl
+       vv3=2.d0*((vtac0(itt)-vtacpe)*exp(-vtac(itt))*(1.d0-vvxt)
+     * *(1.d0-vvxtl)-(vtac(itt)-vtac0(itt))*(1.d0-exp(-vtac(itt))
+     * *(1.d0-vvxt)*(1.d0-vvxtl)))*exp(-vtac(itt))*(1.d0-vvxt)
+       if(xpomm(m+1)*scm.lt.1.1d0*sgap**2.or.vv3.lt.0.d0)vv3=0.d0
+       aks=(vv1+vv2+vv3)*qgran(b10)
+       if(aks.lt.vv1)then
+        jt=1                     !>1 cut fans
+       elseif(aks.lt.vv1+vv2)then
+        jt=2                     !diffr. cut
+       else
+        jt=3                     !1 cut fan
+       endif
+      elseif(itypom.eq.0)then      !cut 'loop' in the 'handle'
+       vv1=(max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt)-vpac(ip))
+     * *(1.d0-vvxp)*(1.d0-vvxpl)*(vicc+vicu)
+     * /(vicc*(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+     * -vicu*(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))))
+       vv2=(1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl
+       vv3=2.d0*vtacng*exp(-2.d0*vtac(itt))*(1.d0-vvxt)**2*(1.d0-vvxtl)
+       aks=(vv1+vv2+vv3)*qgran(b10)
+       if(aks.lt.vv1)then
+        jt=1                     !>1 cut fans
+       elseif(aks.lt.vv1+vv2)then
+        jt=2                     !diffr. cut
+       else
+        jt=3                     !1 cut fan
+       endif
+      elseif(itypom.eq.1)then    !uncut 'handle' (rap-gap)
+       vv1=(max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt))
+       vv2=(1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl
+       vv3=2.d0*vtacng*exp(-2.d0*vtac(itt))*(1.d0-vvxt)**2*(1.d0-vvxtl)
+       vv4=2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0-(vtac(itt)
+     * -vtac0(itt)))*(1.d0-vvxt0)+(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))
+     * *exp(-vtac(itt))*viuu/(viuu*(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)
+     * *exp(-vpac(ip)))+viuc*(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip)))
+       if(xpomm(m+1)*scm.lt.1.1d0*sgap**2.or.vv4.lt.0.d0)vv4=0.d0
+       aks=(vv1+vv2+vv3+vv4)*qgran(b10)
+       if(aks.lt.vv1)then
+        jt=1                     !>1 cut fans
+       elseif(aks.lt.vv1+vv2)then
+        jt=2                     !diffr. cut
+       elseif(aks.lt.vv1+vv2+vv3)then
+        jt=3                     !1 cut fan
+       else
+        jt=4                     !>1 cut 'handle' fans
+       endif
+      elseif(itypom.eq.2)then    !>1 cut Pomerons in the 'handle'
+       vv1=(max(0.d0,1.d0-exp(-2.d0*vtac(itt))
+     * *(1.d0+2.d0*vtac(itt)))+2.d0*vtac(itt)*exp(-2.d0*vtac(itt))
+     * *(1.d0-(1.d0-vvxt)**2))*(1.d0-vvxtl)
+     * -2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)
+     * +(vtac(itt)-vtac0(itt))*(vvxt-vvxt0))*exp(-vtac(itt))
+       vv2=(1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl
+       vv3=2.d0*(vtac0(itt)*exp(-vtac(itt))*(1.d0-vvxt)
+     * *(1.d0-vvxtl)-(vtac(itt)-vtac0(itt))*(1.d0-exp(-vtac(itt))
+     * *(1.d0-vvxt)*(1.d0-vvxtl)))*exp(-vtac(itt))*(1.d0-vvxt)
+       aks=(vv1+vv2+vv3)*qgran(b10)
+       if(aks.lt.vv1)then
+        jt=1                     !>1 cut fans
+       elseif(aks.lt.vv1+vv2)then
+        jt=2                     !diffr. cut
+       else
+        jt=3                     !1 cut fan
+       endif
+      elseif(itypom.eq.3)then    !rap-gap in the 'handle'
+       vv1=(max(0.d0,1.d0-exp(-2.d0*vtac(itt))*(1.d0+2.d0*vtac(itt)))
+     * +2.d0*vtac(itt)*exp(-2.d0*vtac(itt))*(1.d0-(1.d0-vvxt)**2))
+     * *(1.d0-vvxtl)-2.d0*(max(0.d0,exp(vtac(itt)-vtac0(itt))-1.d0
+     * -(vtac(itt)-vtac0(itt)))*(1.d0-vvxt0)+(vtac(itt)-vtac0(itt))
+     * *(vvxt-vvxt0))*exp(-vtac(itt)-vpac(ip))*(1.d0-vvxp)*(1.d0-vvxpl)
+     * *(vicc+vicu+wgpm(m)*(viuu-viuc))
+     * /((vicc-wgpm(m)*viuc)*(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+     * -(vicu+wgpm(m)*viuu)*(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)
+     * *exp(-vpac(ip))))
+       vv2=(1.d0-exp(-vtac(itt)))**2*(1.d0-vvxtl)
+     * +2.d0*(1.d0-exp(-vtac(itt)))*vvxtl
+       vv3=2.d0*vtacng*exp(-2.d0*vtac(itt))*(1.d0-vvxt)**2
+     * *(1.d0-vvxtl)
+       aks=(vv1+vv2+vv3)*qgran(b10)
+       if(aks.lt.vv1)then
+        jt=1                     !>1 cut fans
+       elseif(aks.lt.vv1+vv2)then
+        jt=2                     !diffr. cut
+       else
+        jt=3                     !1 cut fan
+       endif
+      else
+       jt=5                      !cut leg
+      endif
+
+      nppm(m+1)=0
+      wgpm(m+1)=0.d0
+      if(jt.eq.1)then                        !>1 cut fans
+       ntry=0
+24     ntry=ntry+1
+       nphm=0
+       if(itt.eq.ia(2).or.ntry.gt.100)then
+        nppm(m+1)=npgen(2.d0*vtac(itt),2,20)
+        do i=1,nppm(m+1)
+         if(qgran(b10).le.vtac0(itt)/vtac(itt)
+     *   .or.xpomm(m+1)*scm.lt.1.1d0*sgap**2)then
+          itypm(i,m+1)=0
+         else
+          nphm=nphm+1
+          itypm(i,m+1)=1
+         endif
+         ippm(i,m+1)=itt
+        enddo
+        wh=(vtac(itt)/vtac0(itt)-1.d0)/nppm(m+1)
+       else
+        nppm(m+1)=npgen(2.d0*vtac(itt),1,20)
+        do i=1,nppm(m+1)
+         if(qgran(b10).le.vtac0(itt)/vtac(itt)
+     *   .or.xpomm(m+1)*scm.lt.1.1d0*sgap**2)then
+          itypm(i,m+1)=0
+         else
+          nphm=nphm+1
+          itypm(i,m+1)=1
+         endif
+         ippm(i,m+1)=itt
+        enddo
+        wh=(vtac(itt)/vtac0(itt)-1.d0)/nppm(m+1)
+        do iti=itt+1,ia(2)
+         ninc=npgen(2.d0*vtac(iti),0,20)
+         if(ninc.ne.0)then
+          nppm(m+1)=nppm(m+1)+ninc
+          nh0=nphm
+          if(nppm(m+1).gt.legmax)then
+           iret=1
+           goto 31
+          endif
+          do i=nppm(m+1)-ninc+1,nppm(m+1)
+           if(qgran(b10).le.vtac0(iti)/vtac(iti)
+     *     .or.xpomm(m+1)*scm.lt.1.1d0*sgap**2)then
+            itypm(i,m+1)=0
+           else
+            nphm=nphm+1
+            itypm(i,m+1)=1
+           endif
+           ippm(i,m+1)=iti
+          enddo
+          if(ninc.gt.nphm-nh0)wh=(vtac(iti)/vtac0(iti)-1.d0)/ninc
+         endif
+        enddo
+        if(nppm(m+1).eq.1)goto 24
+       endif
+
+       if(nphm+1.ge.nppm(m+1))then
+        if(itypom.eq.-1.or.itypom.eq.1.or.itypom.eq.2)then
+         gbt=1.d0-exp(vtac(itt)+(1.d0-nphm)*dlog(2.d0))
+     *   /(1.d0-vvxt)/(1.d0-vvxtl)
+        elseif(itypom.eq.0)then
+         gbt=1.d0-(vicc+vicu)*(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+     *   /(vicc*(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+     *   -vicu*(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))))
+     *   *exp(vtac(itt)+(1.d0-nphm)*dlog(2.d0))
+     *   /(1.d0-vvxt)/(1.d0-vvxtl)
+        elseif(itypom.eq.3)then
+         gbt=1.d0-(vicc+vicu+wgpm(m)*(viuu-viuc))
+     *   *(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))
+     *   /((vicc-wgpm(m)*viuc)*(1.d0-vvxp)*(1.d0-vvxpl)
+     *   *exp(-vpac(ip))-(vicu+wgpm(m)*viuu)
+     *   *(1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*exp(-vpac(ip))))
+     *   *exp(vtac(itt)+(1.d0-nphm)*dlog(2.d0))
+     *   /(1.d0-vvxt)/(1.d0-vvxtl)
+        else
+         stop'unknown itypom'
+        endif
+        if(nphm.eq.nppm(m+1).and.qgran(b10).gt.gbt
+     *  .or.nphm+1.eq.nppm(m+1).and.qgran(b10).gt.1.d0+wh*gbt)then
+         ntry=0
+          goto 24
+        endif
+       endif
+
+      elseif(jt.eq.4)then                    !>1 cut 'handle' fans
+       ntry=0
+25     ntry=ntry+1
+       if(itt.eq.ia(2).or.ntry.gt.100)then
+        nppm(m+1)=npgen(vtac(itt)-vtac0(itt),2,20)
+        do i=1,nppm(m+1)
+         itypm(i,m+1)=1
+         ippm(i,m+1)=itt
+        enddo
+       else
+        nppm(m+1)=npgen(vtac(itt)-vtac0(itt),1,20)
+        do i=1,nppm(m+1)
+         itypm(i,m+1)=1
+         ippm(i,m+1)=itt
+        enddo
+        do iti=itt+1,ia(2)
+         ninc=npgen(vtac(iti)-vtac0(iti),0,20)
+         if(ninc.ne.0)then
+          nppm(m+1)=nppm(m+1)+ninc
+          if(nppm(m+1).gt.legmax)then
+           iret=1
+           goto 31
+          endif
+          do i=nppm(m+1)-ninc+1,nppm(m+1)
+           itypm(i,m+1)=1
+           ippm(i,m+1)=iti
+          enddo
+         endif
+        enddo
+        if(nppm(m+1).eq.1)goto 25
+       endif
+
+      elseif(jt.eq.3)then                    !1 cut fan
+       nppm(m+1)=1
+       ippm(1,m+1)=itt
+       if(itypom.eq.-1)then             !single cut Pomeron in the 'handle'
+        factor=exp(-vtac(itt))*(1.d0-vvxt)*(1.d0-vvxtl)
+        wng=(vtacng-vtacpe)*factor/((vtac0(itt)-vtacpe)*factor
+     *  -(vtac(itt)-vtac0(itt))*(1.d0-factor))
+        if(qgran(b10).le.wng.or.wng.lt.0.d0
+     *  .or.xpomm(m+1)*scm.lt.1.1d0*sgap**2)then
+         itypm(1,m+1)=2          !>1 cut Pomerons in the 'handle'
+        else
+         itypm(1,m+1)=3          !rap-gap in the 'handle'
+         wgpm(m+1)=(1.d0-factor)/factor
+        endif
+       elseif(itypom.eq.2)then          !>1 cut Pomerons in the 'handle'
+        factor=exp(-vtac(itt))*(1.d0-vvxt)*(1.d0-vvxtl)
+        wng=vtacng*factor/(vtac0(itt)*factor
+     *  -(vtac(itt)-vtac0(itt))*(1.d0-factor))
+        if(qgran(b10).le.wng.or.wng.lt.0.d0
+     *  .or.xpomm(m+1)*scm.lt.1.1d0*sgap**2)then
+         if(qgran(b10).le.vtacpe/vtacng
+     *   .or.xpomm(m+1)*scm.lt.1.1d0*sgap**2)then
+          itypm(1,m+1)=-1        !single cut Pomeron in the 'handle'
+         else
+          itypm(1,m+1)=2         !>1 cut Pomerons in the 'handle'
+         endif
+        else
+         itypm(1,m+1)=3          !rap-gap in the 'handle'
+         wgpm(m+1)=(1.d0-factor)/factor
+        endif
+       else                             !rap-gap in the 'handle'
+        if(qgran(b10).le.vtacpe/vtacng
+     *  .or.xpomm(m+1)*scm.lt.1.1d0*sgap**2)then
+         itypm(1,m+1)=-1         !single cut Pomeron in the 'handle'
+        else
+         itypm(1,m+1)=2          !>1 cut Pomerons in the 'handle'
+        endif
+       endif
+
+       if(itypm(1,m+1).eq.-1)then     !single cut Pomeron in the 'handle'
+        vtlcp=min(vtacpe
+     *  ,qgfani(xpomm(m+1)*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,9))
+        if(qgran(b10).le.vtlcp/vtacpe
+     *  .or.xpomm(m+1)*scm.lt.1.1d0*sgap**2)itypm(1,m+1)=6 !single cut Pomeron
+       endif
+
+      elseif(jt.eq.5)then                    !cut 'leg'
+       nppm(m+1)=1
+       ippm(1,m+1)=itt
+       if(itypom.eq.4)then              !single cut Pomeron at the end
+        if(xpomm(m+1)*scm.le.sgap**2)stop'=4:xpomm(m+1)*scm<sgap**2'
+        factor=exp(-vtac(itt))*(1.d0-vvxt)*(1.d0-vvxtl)
+        wng=(vtlcng-vtlcpe)*factor/((vtlc0-vtlcpe)*factor
+     *  -(vtlc-vtlc0)*(1.d0-factor))
+        if(qgran(b10).le.wng.or.wng.lt.0.d0)then
+         itypm(1,m+1)=7          !>1 cut Pomerons at the end
+        else
+         itypm(1,m+1)=5          !rap-gap at the end
+         wgpm(m+1)=(1.d0-factor)/factor
+        endif
+       elseif(itypom.eq.5)then          !rap-gap at the end (cut or uncut loop)
+        if(qgran(b10).le.vtlcpe/vtlcng
+     *  .or.xpomm(m+1)*scm.lt.1.1d0*sgap**2)then
+         itypm(1,m+1)=4          !single cut Pomeron at the end
+        else
+         itypm(1,m+1)=7          !>1 cut Pomerons at the end
+        endif
+       elseif(itypom.eq.7)then          !>1 cut Pomerons at the end
+        factor=exp(-vtac(itt))*(1.d0-vvxt)*(1.d0-vvxtl)
+        wng=vtlcng*factor/(vtlc0*factor-(vtlc-vtlc0)*(1.d0-factor))
+        if(qgran(b10).le.wng.or.wng.lt.0.d0
+     *  .or.xpomm(m+1)*scm.lt.1.1d0*sgap**2)then
+         if(qgran(b10).le.vtlcpe/vtlcng
+     *   .or.xpomm(m+1)*scm.lt.1.1d0*sgap**2)then
+          itypm(1,m+1)=4         !single cut Pomeron at the end
+         else
+          itypm(1,m+1)=7         !>1 cut Pomerons at the end
+         endif
+        else
+         itypm(1,m+1)=5          !rap-gap at the end
+         wgpm(m+1)=(1.d0-factor)/factor
+        endif
+       endif
+
+       if(itypm(1,m+1).eq.4)then        !single cut Pomeron at the end
+        vtlcp=min(vtlcpe
+     *  ,qgfani(xpomm(m+1)*scm,bbt,vvxps,vvxt,vvxtl,iddt(itt),2,9))
+        if(qgran(b10).le.vtlcp/vtlcpe
+     *  .or.xpomm(m+1)*scm.lt.1.1d0*sgap**3)itypm(1,m+1)=6 !single cut Pomeron
+       endif
+      endif
+
+      if(nppm(m+1).eq.1.and.itypm(1,m+1).eq.6)then  !record single cut Pomeron
+       nptg=nptg+1
+       if(nptg.gt.legmax)then
+        iret=1
+        goto 31
+       endif
+       xpomti(nptg)=xpomm(m+1)
+       vvxti(nptg)=1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*(1.d0-vvxt)
+     * *(1.d0-vvxtl)*exp(-vpac(ip))
+       ipomti(nptg)=itt
+       bpomti(nptg)=bbt
+       nppm(m+1)=0
+       if(debug.ge.4)write (moniou,217)nptg,itt,bbt,xpomti(nptg)
+     * ,vvxti(nptg)
+
+      elseif(nppm(m+1).gt.1)then
+       i=0
+26     i=i+1
+       ityp=itypm(i,m+1)
+       if(ityp.eq.0)then
+        iti=ippm(i,m+1)
+        bbi=(xb(iti,1)-xxm(m+1))**2+(xb(iti,2)-yym(m+1))**2
+        vvxt=0.d0
+        vvxtl=0.d0
+        vvxt0=0.d0
+        if(ia(2).gt.1)then
+         do l=1,ia(2)
+          if(l.lt.iti)then
+           vvxtl=vvxtl+vtac(l)
+          elseif(l.gt.iti)then
+           vvxt=vvxt+vtac(l)
+           vvxt0=vvxt0+vtac0(l)
+          endif
+         enddo
+        endif
+        vvxt=1.d0-exp(-vvxt)
+        vvxtl=1.d0-exp(-vvxtl)
+        vvxt0=1.d0-exp(-vvxt0)
+
+        vtacng=min(vtac0(iti)
+     *  ,qgfani(xpomm(m+1)*scm,bbi,vvxps,vvxt0,vvxtl,iddt(iti),2,4))
+        vtacpe=min(vtacng
+     *  ,qgfani(xpomm(m+1)*scm,bbi,vvxps,vvxt0,vvxtl,iddt(iti),2,5))
+        vtlcp=min(vtacpe
+     *  ,qgfani(xpomm(m+1)*scm,bbi,vvxps,vvxt,vvxtl,iddt(iti),2,9))
+
+        aks=qgran(b10)*vtac0(iti)
+        if(aks.le.vtlcp.or.xpomm(m+1)*scm.lt.1.1d0*sgap**2)then
+         itypm(i,m+1)=6          !single cut Pomeron
+        elseif(aks.lt.vtacpe)then
+         itypm(i,m+1)=-1         !single cut Pomeron in the 'handle'
+        elseif(aks.lt.vtacng)then
+         itypm(i,m+1)=2          !>1 cut Pomerons in the 'handle'
+        endif
+
+        if(itypm(i,m+1).eq.6)then      !record single cut Pomeron
+         nptg=nptg+1
+         if(nptg.gt.legmax)then
+          iret=1
+          goto 31
+         endif
+         xpomti(nptg)=xpomm(m+1)
+         vvxti(nptg)=1.d0-(1.d0-vvxp)*(1.d0-vvxpl)*(1.d0-vvxt)
+     *   *(1.d0-vvxtl)*exp(-vpac(ip))
+         ipomti(nptg)=iti
+         bpomti(nptg)=bbi
+         if(debug.ge.4)write (moniou,217)nptg,iti,bbi,xpomti(nptg)
+     *   ,vvxti(nptg)
+         nppm(m+1)=nppm(m+1)-1
+         if(nppm(m+1).ge.i)then
+          do l=i,nppm(m+1)
+           ippm(l,m+1)=ippm(l+1,m+1)
+           itypm(l,m+1)=itypm(l+1,m+1)
+          enddo
+         endif
+         i=i-1
+        endif
+       endif
+       if(i.lt.nppm(m+1))goto 26
+      endif
+
+      if(jt.eq.2.and.qgran(b10).lt.(1.d0-exp(-vtac(itt)))*(1.d0-vvxtl)
+     */((1.d0-exp(-vtac(itt)))*(1.d0-vvxtl)+2.d0*vvxtl))then
+       if(debug.ge.4)write (moniou,212)
+       icdts=iddt(itt)
+       do icdt=1,2
+        iddt(itt)=icdt
+        call qgfdf(xxm(m+1),yym(m+1),xpomm(m+1),vpac,vtac
+     *  ,vvx,vvxp,vvxt,vvxpl,vvxtl,ip,itt)
+        wdt(icdt,itt)=(1.d0-exp(-vtac(itt)))*(1.d0-vvxtl)
+       enddo
+       iddt(itt)=icdts
+      endif
+
+      if(nppm(m+1).ne.0)then
+       goto 21
+      else
+       goto 22
+      endif
+31    continue
+      if(debug.ge.2)write (moniou,219)nppr,nptg,npin,iret
+
+201   format(2x,'qg3pdf - configuration for multi-Pomeron'
+     *,'/diffractive contributions'
+     */4x,i2,'-th proj. nucleon',2x,i2,'-th targ. nucleon')
+202   format(2x,'qg3pdf: problem with initial normalization'
+     *,' -> rejection')
+203   format(2x,'qg3pdf: normalization of rejection function - ',e10.3)
+204   format(2x,'qg3pdf: xpomr=',e10.3,2x,'bbpr=',e10.3,2x,'bbtg=',e10.3
+     *,2x,'gb=',e10.3)
+205   format(2x,'qg3pdf: xpomr=',e10.3,2x,'bbpr=',e10.3,2x,'bbtg=',e10.3
+     *,2x,'xxp=',e10.3,2x,'yyp=',e10.3)
+206   format(2x,'qg3pdf: main vertex, nppr0=',i3,2x,'nptg0=',i3)
+208   format(2x,'qg3pdf: check',i3,'-th cut fan at ',i2,'-th level,'
+     *,' proj. index - ',i3,2x,'b^2=',e10.3)
+209   format(2x,'qg3pdf: ',i3,'-th proj. leg, proj. index - ',i3
+     *,2x,'b^2=',e10.3,2x,'xpomr=',e10.3,2x,'vvx=',e10.3)
+210   format(2x,'qg3pdf: new vertex at ',i3,'-th level')
+211   format(2x,'qg3pdf: ',i3,'-th interm. Pomeron'
+     */4x,'xpomip=',e10.3,2x,'xpomim=',e10.3
+     *,2x,'vvxim=',e10.3,2x,'bpomim=',e10.3)
+212   format(2x,'qg3pdf: diffractive cut')
+214   format(2x,'qg3pdf: total number of proj. legs - ',i3)
+216   format(2x,'qg3pdf: check',i3,'-th cut fan at ',i2,'-th level,'
+     *,' targ. index - ',i3,2x,'b^2=',e10.3)
+217   format(2x,'qg3pdf: ',i3,'-th targ. leg, targ. index - ',i3
+     *,2x,'b^2=',e10.3,2x,'xpomr=',e10.3,2x,'vvx=',e10.3)
+219   format(2x,'qg3pdf - end',2x,'number of proj. legs:',i3
+     *,2x,'number of targ. legs:',i3
+     */4x,'number of interm. Pomerons:',i3,'return flag:',i2)
+      return
+      end
+
+c------------------------------------------------------------------------
+      subroutine qgloolc(sy,xp,bb,icdp,icz,iqq,fan1,fan0)
+c-----------------------------------------------------------------------
+c qgloolc - unintegrated Pomeron leg eikonal with loops
+c sy   - Pomeron mass squared,
+c xp   - Pomeron LC momentum,
+c bb   - impact parameter squared,
+c icz  - hadron class
+c iqq=1 - tot
+c iqq=2 - soft Pomeron
+c iqq=3 - (soft+g)-Pomeron
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      fan0=0.d0
+      fan1=0.d0
+      if(sy.le.sgap*max(1.d0,xp*sgap))goto 1
+
+      do ix1=1,7
+      do mx1=1,2
+       xpomr=min(xp,1.d0/sgap)/(sy/sgap/max(1.d0,xp*sgap))
+     * **(.5d0+x1(ix1)*(mx1-1.5d0))
+       rp=(rq(icdp,icz)-alfp*log(xpomr))*4.d0*.0389d0
+       rp1=alfp*log(xpomr*sy/xp)*4.d0*.0389d0
+       rp2=rp*rp1/(rp+rp1)
+      do ix2=1,7
+      do mx2=1,2
+       z=.5d0+x1(ix2)*(mx2-1.5d0)
+       bb0=-rp2*log(z)
+      do ix3=1,7
+      do mx3=1,2
+       phi=pi*(.5d0+x1(ix3)*(mx3-1.5d0))
+       bb1=(dsqrt(bb)*rp1/(rp+rp1)-dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+       bb2=(dsqrt(bb)*rp/(rp+rp1)+dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+
+       v1icn=qgpini(xpomr*sy/xp,bb1,0.d0,0.d0,8)
+       if(iqq.eq.1)then
+        vpl=qglegc(xp/xpomr,xp,bb2,0.d0,icdp,icz,1)
+        v1ic0=qgpini(xpomr*sy/xp,bb1,0.d0,0.d0,7)
+        v1ic1=min(v1ic0,qgpini(xpomr*sy/xp,bb1,0.d0,0.d0,6))
+        v1ic=min(v1ic1,qgpini(xpomr*sy/xp,bb1,0.d0,0.d0,5))
+       elseif(iqq.eq.2)then
+        vpl=qglegc(xp/xpomr,xp,bb2,0.d0,icdp,icz,0)
+        v1ic0=qgpini(xpomr*sy/xp,bb1,0.d0,0.d0,15)
+        v1ic1=min(v1ic0,qgpini(xpomr*sy/xp,bb1,0.d0,0.d0,14))
+        v1ic=min(v1ic1,qgpini(xpomr*sy/xp,bb1,0.d0,0.d0,13))
+       elseif(iqq.eq.3)then
+        vpl=qglegc(xp/xpomr,xp,bb2,0.d0,icdp,icz,2)
+        v1ic0=qgpini(xpomr*sy/xp,bb1,0.d0,0.d0,7)
+        v1ic1=min(v1ic0,qgpini(xpomr*sy/xp,bb1,0.d0,0.d0,6))
+        v1ic=min(v1ic1,qgpini(xpomr*sy/xp,bb1,0.d0,0.d0,5))
+       else
+         vpl=0.d0
+         v1ic0=0.d0
+         v1ic1=0.d0
+         v1ic=0.d0
+         stop 'Should no happen in qgloolc !'
+       endif
+       fan1=fan1+a1(ix1)*a1(ix2)*a1(ix3)/z*rp2
+     * *vpl*(v1ic*exp(-2.d0*v1icn)-v1ic1)
+       fan0=fan0+a1(ix1)*a1(ix2)*a1(ix3)/z*rp2*vpl*(v1ic1-v1ic0)
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      fan0=fan0/8.d0*pi*r3p/.0389d0/g3p**3
+     **dlog(sy/sgap/max(1.d0,xp*sgap))
+      fan1=fan1/8.d0*pi*r3p/.0389d0/g3p**3
+     **dlog(sy/sgap/max(1.d0,xp*sgap))
+1     continue
+      if(iqq.eq.1)then
+       dleg=qglegc(sy,xp,bb,0.d0,icdp,icz,1)
+      elseif(iqq.eq.2)then
+       dleg=qglegc(sy,xp,bb,0.d0,icdp,icz,0)
+      elseif(iqq.eq.3)then
+       dleg=qglegc(sy,xp,bb,0.d0,icdp,icz,2)
+      else
+       dleg=0.d0
+       stop 'Should no happen in qgloolc !'
+      endif
+      fan0=fan0+dleg
+      fan1=fan1+dleg
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qglscr(sy,xp,bb,vvx,icdp,icz,iqq)
+c-----------------------------------------------------------------------
+c vvx  = 1 - exp[-sum_j chi_targ(j) - sum_{i.ne.I} chi_proj(i)]
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      qglscr=0.d0
+      if(sy.le.sgap*max(1.d0,xp*sgap))goto 1
+
+      do ix1=1,7
+      do mx1=1,2
+       xpomr1=min(xp,1.d0/sgap)/(sy/sgap/max(1.d0,xp*sgap))
+     * **(.5d0+x1(ix1)*(mx1-1.5d0))
+       rp=(rq(icdp,icz)-alfp*log(xpomr1))*4.d0*.0389d0
+       rp1=alfp*log(xpomr1*sy/xp)*4.d0*.0389d0
+       rp2=rp*rp1/(rp+rp1)
+       do ix2=1,7
+       do mx2=1,2
+        z=.5d0+x1(ix2)*(mx2-1.5d0)
+        bb0=-rp2*log(z)
+       do ix3=1,7
+       do mx3=1,2
+        phi=pi*(.5d0+x1(ix3)*(mx3-1.5d0))
+        bb1=(dsqrt(bb)*rp1/(rp+rp1)-dsqrt(bb0)*cos(phi))**2
+     *  +bb0*sin(phi)**2
+        bb2=(dsqrt(bb)*rp/(rp+rp1)+dsqrt(bb0)*cos(phi))**2
+     *  +bb0*sin(phi)**2
+
+        vicn=qgpini(xpomr1*sy/xp,bb1,0.d0,0.d0,8)
+        vpf=qgfani(1.d0/xpomr1,bb2,vvx,0.d0,0.d0,icdp,icz,1)
+        if(iqq.eq.1)then
+         vpl=qglegc(xp/xpomr1,xp,bb2,vvx,icdp,icz,9)
+         vi=qgpini(xpomr1*sy/xp,bb1,0.d0,0.d0,5)
+        elseif(iqq.eq.2)then
+         vpl=qglegc(xp/xpomr1,xp,bb2,vvx,icdp,icz,10)
+         vi=qgpini(xpomr1*sy/xp,bb1,0.d0,0.d0,13)
+        elseif(iqq.eq.3)then
+         vpl=qglegc(xp/xpomr1,xp,bb2,vvx,icdp,icz,11)
+         vi=qgpini(xpomr1*sy/xp,bb1,0.d0,0.d0,5)
+        else
+         vpl=0.d0
+         vi=0.d0
+         stop 'Should no happen in qglscr !'
+        endif
+
+        dpx=vpl*vi*exp(-2.d0*vicn)
+     *  *((1.d0-vvx)**2*exp(-2.d0*vpf)-1.d0)
+        qglscr=qglscr+a1(ix1)*a1(ix2)*a1(ix3)*dpx/z*rp2
+       enddo
+       enddo
+       enddo
+       enddo
+      enddo
+      enddo
+      qglscr=qglscr/8.d0*pi*r3p/.0389d0/g3p**3
+     **dlog(sy/sgap/max(1.d0,xp*sgap))
+1     continue
+      if(iqq.eq.1)then
+       qglscr=qglscr+qglegc(sy,xp,bb,0.d0,icdp,icz,3)
+      elseif(iqq.eq.2)then
+       qglscr=qglscr+qglegc(sy,xp,bb,0.d0,icdp,icz,5)
+      elseif(iqq.eq.3)then
+       qglscr=qglscr+qglegc(sy,xp,bb,0.d0,icdp,icz,7)
+      endif
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qglh(sy,xp,bb,vvx,icdp,icz,iqq)
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr19/ ahl(3)
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgdebug/    debug
+      common /arr3/     x1(7),a1(7)
+
+      qglh=0.d0
+      if(sy.le.max(1.d0,xp*sgap))goto 1
+
+      do ix1=1,7
+      do mx1=1,2
+       xpomr1=min(xp,1.d0/sgap)/(sy/max(1.d0,xp*sgap))
+     * **(.5d0+x1(ix1)*(mx1-1.5d0))
+       rp=(rq(icdp,icz)-alfp*log(xpomr1))*4.d0*.0389d0
+       rp1=alfp*log(xpomr1*sy/xp)*4.d0*.0389d0
+       rp2=rp*rp1/(rp+rp1)
+       do ix2=1,7
+       do mx2=1,2
+        z=.5d0+x1(ix2)*(mx2-1.5d0)
+        bb0=-rp2*log(z)
+       do ix3=1,7
+       do mx3=1,2
+        phi=pi*(.5d0+x1(ix3)*(mx3-1.5d0))
+        bb1=(dsqrt(bb)*rp1/(rp+rp1)-dsqrt(bb0)*cos(phi))**2
+     *  +bb0*sin(phi)**2
+        bb2=(dsqrt(bb)*rp/(rp+rp1)+dsqrt(bb0)*cos(phi))**2
+     *  +bb0*sin(phi)**2
+
+        vi=qgppdi(xp/xpomr1/sy,iqq)
+        vpf=qgfani(1.d0/xpomr1,bb2,vvx,0.d0,0.d0,icdp,icz,1)
+        vpl=qglegc(xp/xpomr1,xp,bb2,vvx,icdp,icz,10)
+
+        dpx=vpl*vi*((1.d0-vvx)**2*exp(-2.d0*vpf)-1.d0)
+     *  *(xpomr1/xp)**dels*exp(bb2/rp)*rp
+        qglh=qglh+a1(ix1)*a1(ix2)*a1(ix3)*dpx
+       enddo
+       enddo
+       enddo
+       enddo
+      enddo
+      enddo
+      qglh=qglh/8.d0*pi*r3p/.0389d0/g3p**2*dlog(sy/max(1.d0,xp*sgap))
+     */fp(icz)/cd(icdp,icz)/qgppdi(1.d0/sy,iqq)
+
+1     qglh=qglh+1.d0
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgcutp(sy,xp,xm,bb,vvx
+     *,icdp,icdt,icz,iqq)
+c-----------------------------------------------------------------------
+c qgcutp - unintegrated cut Pomeron eikonal
+c sy         - Pomeron mass squared,
+c xp,xm      - Pomeron light cone momenta,
+c b          - squared impact parameter,
+c vvx        - relative strenth of nuclear screening corrections,
+c icdp, icdt - proj. and targ. diffractive eigenstates,
+c icz        - hadron class
+c iqq=1 - total,
+c iqq=2 - soft contribution,
+c iqq=3  - (soft+gg+gq+qq) contribution
+c iqq=4  - (soft+gg+qq) contribution
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr19/ ahl(3)
+      common /qgarr25/ ahv(3)
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      qgcutp=0.d0
+      if(sy.le.max(1.d0,xp*sgap)*max(1.d0,xm*sgap))goto 2
+
+      do ix1=1,7
+      do mx1=1,2
+       xpomr1=xp/max(1.d0,xp*sgap)/(sy/max(1.d0,xp*sgap)
+     * /max(1.d0,xm*sgap))**(.5+x1(ix1)*(mx1-1.5))
+       rp1=(rq(icdp,icz)-alfp*log(xpomr1))*4.d0*.0389d0
+       rp2=(rq(icdt,2)+alfp*log(xpomr1*sy/xp/xm))*4.d0*.0389d0
+       rp=rp1*rp2/(rp1+rp2)
+      do ib1=1,7
+      do mb1=1,2
+       z=.5d0+x1(ib1)*(mb1-1.5d0)
+       bb0=-rp*dlog(z)
+      do ib2=1,7
+      do mb2=1,2
+       phi=pi*(.5d0+x1(ib2)*(mb2-1.5d0))
+       bb1=(dsqrt(bb)*rp1/(rp1+rp2)+dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+       bb2=(dsqrt(bb)*rp2/(rp1+rp2)-dsqrt(bb0)*cos(phi))**2
+     * +bb0*sin(phi)**2
+
+       vpf0=qgfani(1.d0/xpomr1,bb1,vvx,0.d0,0.d0,icdp,icz,1)
+       vtf0=qgfani(xpomr1*sy/xp/xm,bb2,vvx,0.d0,0.d0,icdt,2,1)
+       n=1
+1      n=n+1
+       vpf=qgfani(1.d0/xpomr1,bb1,1.d0-(1.d0-vvx)*exp(-vtf0)
+     * ,0.d0,0.d0,icdp,icz,1)
+       vtf=qgfani(xpomr1*sy/xp/xm,bb2,1.d0-(1.d0-vvx)*exp(-vpf0)
+     * ,0.d0,0.d0,icdt,2,1)
+       if(abs(1.d0-vpf/vpf0)+abs(1.d0-vtf/vtf0).gt.1.d-2.and.n.le.50)
+     * then
+        vpf0=vpf
+        vtf0=vtf
+        goto 1
+       endif
+
+       if(iqq.eq.1)then
+        vplt=qglegc(xp/xpomr1,xp,bb1,0.d0,icdp,icz,1)
+        vtlt=qglegc(xpomr1*sy/xp,xm,bb2,0.d0,icdt,2,1)
+        vpltloop0=min(vplt,qglegc(xp/xpomr1,xp,bb1,0.d0,icdp,icz,4))
+        vpltloop=min(vpltloop0,qglegc(xp/xpomr1,xp,bb1,0.d0,icdp,icz,3))
+        vtltloop0=min(vtlt,qglegc(xpomr1*sy/xp,xm,bb2,0.d0,icdt,2,4))
+        vtltloop=min(vtltloop0,qglegc(xpomr1*sy/xp,xm,bb2,0.d0
+     *  ,icdt,2,3))
+        vpltscr=min(vpltloop,qglegc(xp/xpomr1,xp,bb1
+     *  ,1.d0-(1.d0-vvx)*exp(-vtf),icdp,icz,9))
+        vtltscr=min(vtltloop,qglegc(xpomr1*sy/xp,xm,bb2
+     *  ,1.d0-(1.d0-vvx)*exp(-vpf),icdt,2,9))
+
+        dpx=(vpltscr*vtltloop+vtltscr*vpltloop)
+     *  *((1.d0-vvx)**2*exp(-2.d0*vpf-2.d0*vtf)-1.d0)
+     *  +vplt*(vtltloop-vtltloop0)+vtlt*(vpltloop-vpltloop0)
+       elseif(iqq.eq.2)then
+        vpls=qglegc(xp/xpomr1,xp,bb1,0.d0,icdp,icz,0)
+        vtls=qglegc(xpomr1*sy/xp,xm,bb2,0.d0,icdt,2,0)
+        vplsloop0=min(vpls,qglegc(xp/xpomr1,xp,bb1,0.d0,icdp,icz,6))
+        vplsloop=min(vplsloop0,qglegc(xp/xpomr1,xp,bb1,0.d0,icdp,icz,5))
+        vtlsloop0=min(vtls,qglegc(xpomr1*sy/xp,xm,bb2,0.d0,icdt,2,6))
+        vtlsloop=min(vtlsloop0,qglegc(xpomr1*sy/xp,xm,bb2,0.d0
+     *  ,icdt,2,5))
+        vplsscr=min(vplsloop,qglegc(xp/xpomr1,xp,bb1
+     *  ,1.d0-(1.d0-vvx)*exp(-vtf),icdp,icz,10))
+        vtlsscr=min(vtlsloop,qglegc(xpomr1*sy/xp,xm,bb2
+     *  ,1.d0-(1.d0-vvx)*exp(-vpf),icdt,2,10))
+
+        dpx=(vplsscr*vtlsloop+vtlsscr*vplsloop)
+     *  *((1.d0-vvx)**2*exp(-2.d0*vpf-2.d0*vtf)-1.d0)
+     *  +vpls*(vtlsloop-vtlsloop0)+vtls*(vplsloop-vplsloop0)
+       elseif(iqq.eq.3)then
+        vplq=qglegc(xp/xpomr1,xp,bb1,0.d0,icdp,icz,2)
+        vtlt=qglegc(xpomr1*sy/xp,xm,bb2,0.d0,icdt,2,1)
+        vplqloop0=min(vplq,qglegc(xp/xpomr1,xp,bb1,0.d0,icdp,icz,8))
+        vplqloop=min(vplqloop0
+     *  ,qglegc(xp/xpomr1,xp,bb1,0.d0,icdp,icz,7))
+        vtltloop0=min(vtlt,qglegc(xpomr1*sy/xp,xm,bb2,0.d0,icdt,2,4))
+        vtltloop=min(vtltloop0,qglegc(xpomr1*sy/xp,xm,bb2,0.d0
+     *  ,icdt,2,3))
+        vplqscr=min(vplqloop,qglegc(xp/xpomr1,xp,bb1
+     *  ,1.d0-(1.d0-vvx)*exp(-vtf),icdp,icz,11))
+        vtltscr=min(vtltloop,qglegc(xpomr1*sy/xp,xm,bb2
+     *  ,1.d0-(1.d0-vvx)*exp(-vpf),icdt,2,9))
+
+        dpx=(vplqscr*vtltloop+vtltscr*vplqloop)
+     *  *((1.d0-vvx)**2*exp(-2.d0*vpf-2.d0*vtf)-1.d0)
+     *  +vplq*(vtltloop-vtltloop0)+vtlt*(vplqloop-vplqloop0)
+       elseif(iqq.eq.4)then
+        vplq=qglegc(xp/xpomr1,xp,bb1,0.d0,icdp,icz,2)
+        vtlq=qglegc(xpomr1*sy/xp,xm,bb2,0.d0,icdt,2,2)
+        vplqloop0=min(vplq,qglegc(xp/xpomr1,xp,bb1,0.d0,icdp,icz,8))
+        vplqloop=min(vplqloop0
+     *  ,qglegc(xp/xpomr1,xp,bb1,0.d0,icdp,icz,7))
+        vtlqloop0=min(vtlq,qglegc(xpomr1*sy/xp,xm,bb2,0.d0,icdt,2,8))
+        vtlqloop=min(vtlqloop0,qglegc(xpomr1*sy/xp,xm,bb2,0.d0
+     *  ,icdt,2,7))
+        vplqscr=min(vplqloop,qglegc(xp/xpomr1,xp,bb1
+     *  ,1.d0-(1.d0-vvx)*exp(-vtf),icdp,icz,11))
+        vtlqscr=min(vtlqloop,qglegc(xpomr1*sy/xp,xm,bb2
+     *  ,1.d0-(1.d0-vvx)*exp(-vpf),icdt,2,11))
+
+        dpx=(vplqscr*vtlqloop+vtlqscr*vplqloop)
+     *  *((1.d0-vvx)**2*exp(-2.d0*vpf-2.d0*vtf)-1.d0)
+     *  +vplq*(vtlqloop-vtlqloop0)+vtlq*(vplqloop-vplqloop0)
+       else
+        dpx=0.d0
+       endif
+       qgcutp=qgcutp+a1(ib1)*a1(ib2)*a1(ix1)/z*rp*dpx
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      enddo
+      qgcutp=qgcutp/16.d0*(r3p*pi/.0389d0)/g3p**3
+     **dlog(sy/max(1.d0,xp*sgap)/max(1.d0,xm*sgap))
+
+2     continue
+      rp=(rq(icdp,icz)+rq(icdt,2)+alfp*log(sy/xp/xm))
+      vs=sy**dels*fp(icz)*fp(2)*sigs/rp
+     **exp(-bb/rp/4.d0/.0389d0)*cd(icdp,icz)*cd(icdt,2)
+      vgg=qgpsh(sy,xp,xm,bb,icdp,icdt,icz,0)
+      vqq=qgpomc(sy,xp,xm,bb,0.d0,icdp,icdt,icz,5)
+      vqg=qgpsh(sy,xp,xm,bb,icdp,icdt,icz,1)
+     */dsqrt(xp)*(1.d0-xp)**(ahv(icz)-ahl(icz))
+      vgq=qgpsh(sy,xp,xm,bb,icdp,icdt,icz,2)
+     */dsqrt(xm)*(1.d0-xm)**(ahv(2)-ahl(2))
+      if(iqq.eq.1)then
+       qgcutp=qgcutp+vs+vgg+vqg+vgq+vqq
+      elseif(iqq.eq.2)then
+       qgcutp=qgcutp+vs
+      elseif(iqq.eq.3)then
+       qgcutp=qgcutp+vs+vgg+vgq+vqq
+      elseif(iqq.eq.4)then
+       qgcutp=qgcutp+vs+vgg+vqq
+      endif
+      return
+      end
+
+c=============================================================================
+      double precision function qgpsh(sy,xpp,xpm,bb,icdp,icdt,icz,iqq)
+c-----------------------------------------------------------------------------
+c qgpsh - unintegrated semihard Pomeron eikonal
+c sy         - Pomeron mass squared,
+c xpp, xpm   - Pomeron LC momenta,
+c b          - impact parameter,
+c icdp, icdt - proj. and targ. diffractive eigenstates,
+c icz        - hadron class,
+c iqq        - type of the hard interaction (0-gg, 1-q_vg, 2-gq_v)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /arr3/   x1(7),a1(7)
+      common /qgdebug/  debug
+
+      if(debug.ge.3)write (moniou,201)sy,xpp,xpm,b,vvx0,icdp,icdt
+     *,icz,iqq
+      qgpsh=0.d0
+      s2min=4.d0*fqscal*qt0               !energy threshold for hard interaction
+      if(s2min/sy.ge.1.d0)then
+       if(debug.ge.4)write (moniou,202)qgpsh
+       return
+      endif
+
+      if(iqq.ne.2)then
+       icv=icz
+       icq=2
+       xp=xpp
+       xm=xpm
+       icdv=icdp
+       icdq=icdt
+      else
+       icv=2
+       icq=icz
+       xp=xpm
+       xm=xpp
+       icdq=icdp
+       icdv=icdt
+      endif
+
+      xmin=(s2min/sy)**(delh-dels)
+      do i=1,7
+      do m=1,2
+       z1=(.5d0*(1.d0+xmin-(2*m-3)*x1(i)*(1.d0-xmin)))
+     * **(1.d0/(delh-dels))
+       ww=z1*sy
+       sjqq=qgjit(qt0,qt0,ww,2,2)
+       sjqg=qgjit(qt0,qt0,ww,1,2)
+       sjgg=qgjit(qt0,qt0,ww,1,1)
+
+       if(iqq.eq.0)then                                !gg-Pomeron
+        st2=0.d0
+        do j=1,7
+        do k=1,2
+         xx=.5d0*(1.d0+x1(j)*(2*k-3))
+         xph=z1**xx
+         xmh=z1/xph
+
+         glu1=qgppdi(xph,0)
+         sea1=qgppdi(xph,1)
+         glu2=qgppdi(xmh,0)
+         sea2=qgppdi(xmh,1)
+         st2=st2+a1(j)*(glu1*glu2*sjgg+(glu1*sea2+glu2*sea1)*sjqg
+     *   +sea1*sea2*sjqq)
+        enddo
+        enddo
+        rh=rq(icdp,icz)+rq(icdt,2)-alfp*dlog(xpp*xpm*z1)
+        qgpsh=qgpsh-a1(i)*dlog(z1)/z1**delh*st2
+     *  *exp(-bb/rh/4.d0/.0389d0)/rh
+
+       else                                !qg-Pomeron
+        xmh=z1
+        glu=qgppdi(xmh,0)
+        sea=qgppdi(xmh,1)
+        rh=rq(icdp,icz)+rq(icdt,2)-alfp*dlog(xm*xmh)
+
+        fst=(glu*sjqg+sea*sjqq)
+     *  *(qggrv(xp,qt0,icv,1)+qggrv(xp,qt0,icv,2))/dsqrt(xp)
+     *  *exp(-bb/rh/4.d0/.0389d0)/rh
+        qgpsh=qgpsh+a1(i)/z1**delh*fst
+       endif
+      enddo
+      enddo
+      qgpsh=qgpsh*(1.d0-xmin)/(delh-dels)
+      if(iqq.eq.0)then
+       qgpsh=qgpsh*rr**2*fp(icz)*fp(2)*factk/2.d0*pi
+     * *cd(icdp,icz)*cd(icdt,2)
+      else
+       qgpsh=qgpsh*rr*fp(icq)*factk/4.d0
+     * *cd(icdp,icz)*cd(icdt,2)
+      endif
+      if(debug.ge.4)write (moniou,202)qgpsh
+
+201   format(2x,'qgpsh - unintegrated semihard Pomeron eikonal:'
+     */4x,'sy=',e10.3,2x,'xpp=',e10.3,2x,'xpm=',e10.3,2x,'b=',e10.3
+     */4x,'vvx0=',e10.3,2x,'icdp=',i1,2x,'icdt=',i1,2x,'icz=',i1
+     *,2x,'iqq=',i1)
+202   format(2x,'qgpsh=',e10.3)
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qglegc(sy,xp,bb,vvx,icdp,icz,iqq)
+c-----------------------------------------------------------------------
+c qglegc - interpolation of cut Pomeron leg eikonal
+c sy   - Pomeron mass squared,
+c xp   - Pomeron LC momentum,
+c bb   - squared impact parameter,
+c vvx - relative strenth of screening corrections (0<vvx<1),
+c icdp - diffractive eigenstate for the hadron,
+c icz  - hadron class
+c iqq=0  - soft Pomeron,
+c iqq=1  - total Pomeron,
+c iqq=2  - (soft+g)-Pomeron,
+c iqq=3  - total loop,
+c iqq=4  - total loop with single Pomeron end,
+c iqq=5  - soft loop,
+c iqq=6  - soft loop with single Pomeron end,
+c iqq=7  - (soft+g)-loop,
+c iqq=8  - (soft+g)-loop with single Pomeron end,
+c iqq=9  - total screened,
+c iqq=10 - soft screened,
+c iqq=11 - (soft+g)-screened
+c iqq=12 - g-distribution,
+c iqq=13 - q-distribution
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wk(3),wj(3),wi(3),wz(3)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr19/ ahl(3)
+      common /qgarr20/ spmax
+      common /qgarr25/ ahv(3)
+      common /qgarr26/ factk,fqscal
+      common /qgarr35/ qlegc0(51,10,11,6,8),qlegc(51,10,11,11,30)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.3)write (moniou,201)sy,xp,bb,vvx,icdp,icz,iqq
+
+      qglegc=0.d0
+      clegm=0.d0
+      rp=(rq(icdp,icz)+alfp*log(max(1.d0,sy/xp)))*4.d0*.0389d0
+      z=exp(-bb/rp)
+      if(iqq.eq.0.or.iqq.le.11.and.sy.le.sgap*max(1.d0,xp*sgap)
+     *  .or.iqq.gt.11.and.sy.le.max(1.d0,xp*sgap))then
+       if(iqq.le.11)then
+        qglegc=sy**dels*fp(icz)*sigs*g3p/rp*4.d0*.0389d0*z*cd(icdp,icz)
+       else
+        qglegc=qgppdi(1.d0/sy,iqq-12)
+       endif
+       if(debug.ge.4)write (moniou,202)qglegc
+       return
+      endif
+
+      if(z.gt..2d0)then
+       zz=5.d0*z+6.d0
+      else
+       zz=(-bb/rp-dlog(0.2d0))/2.d0+7.d0
+      endif
+      jz=min(9,int(zz))
+      jz=max(1,jz)
+      if(zz.lt.1.d0)then
+       wz(2)=zz-jz
+       wz(1)=1.d0-wz(2)
+       izmax=2
+      else
+       if(jz.eq.6)jz=5
+       wz(2)=zz-jz
+       wz(3)=wz(2)*(wz(2)-1.d0)*.5d0
+       wz(1)=1.d0-wz(2)+wz(3)
+       wz(2)=wz(2)-2.d0*wz(3)
+       izmax=3
+      endif
+
+      if(iqq.le.11)then
+       yl=max(0.d0,dlog(sy/xp/sgap**2)/dlog(spmax/sgap**2))*50.d0+1.d0
+      else
+       yl=max(0.d0,dlog(sy/xp/sgap)/dlog(spmax/sgap))*50.d0+1.d0
+      endif
+      k=max(1,int(yl))
+      k=min(k,49)
+      wk(2)=yl-k
+      wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+      wk(1)=1.d0-wk(2)+wk(3)
+      wk(2)=wk(2)-2.d0*wk(3)
+      iymax=3
+
+      if(xp.lt..2d0)then
+       if(iqq.le.11)then
+        xl=6.d0-5.d0*log(5.d0*xp)/log(5.d0*xp*sgap/sy)
+       elseif(sy.gt.1.01d0*xp*sgap)then
+        xl=6.d0-5.d0*log(5.d0*xp)/log(xp*sgap/sy)
+       else
+        xl=1.d0
+       endif
+      else
+       xl=5.d0*xp+5.d0
+      endif
+      i=min(8,int(xl))
+      i=max(1,i)
+      if(i.eq.5)i=4
+      wi(2)=xl-i
+      wi(3)=wi(2)*(wi(2)-1.d0)*.5d0
+      wi(1)=1.d0-wi(2)+wi(3)
+      wi(2)=wi(2)-2.d0*wi(3)
+      ixmax=3
+
+      if(iqq.lt.9)then
+       do k1=1,iymax
+        k2=k+k1-1
+       do i1=1,ixmax
+        i2=i+i1-1
+       do l1=1,izmax
+        l2=jz+l1-1
+        qglegc=qglegc+qlegc0(k2,i2,l2,icdp+2*(icz-1),iqq)
+     *  *wk(k1)*wi(i1)*wz(l1)
+       enddo
+       enddo
+       enddo
+       if(zz.lt.1.d0)then
+        do k1=1,iymax
+         k2=k+k1-1
+        do i1=1,ixmax
+         i2=i+i1-1
+         clegm=clegm+qlegc0(k2,i2,1,icdp+2*(icz-1),iqq)*wk(k1)*wi(i1)
+        enddo
+        enddo
+        qglegc=min(qglegc,clegm)
+       endif
+      else
+       vl=max(1.d0,vvx*10.d0+1.d0)
+       if(vl.lt.2.d0)then
+        j=1
+        wj(2)=vl-j
+        wj(3)=wj(2)*(wj(2)-1.d0)*.5d0
+        wj(1)=1.d0-wj(2)+wj(3)
+        wj(2)=wj(2)-2.d0*wj(3)
+        ivmax=3
+       else
+        j=min(int(vl),10)
+        wj(2)=vl-j
+        wj(1)=1.d0-wj(2)
+        ivmax=2
+       endif
+
+       do l1=1,izmax
+        l2=jz+l1-1
+       do j1=1,ivmax
+        j2=j+j1-1
+       do i1=1,ixmax
+        i2=i+i1-1
+       do k1=1,iymax
+        k2=k+k1-1
+        qglegc=qglegc+qlegc(k2,i2,j2,l2,icdp+2*(icz-1)+6*(iqq-9))
+     *  *wk(k1)*wi(i1)*wz(l1)*wj(j1)
+       enddo
+       enddo
+       enddo
+       enddo
+       if(zz.lt.1.d0)then
+        do j1=1,ivmax
+         j2=j+j1-1
+        do i1=1,ixmax
+         i2=i+i1-1
+        do k1=1,iymax
+         k2=k+k1-1
+         clegm=clegm+qlegc(k2,i2,j2,1,icdp+2*(icz-1)+6*(iqq-9))
+     *   *wk(k1)*wi(i1)*wj(j1)
+        enddo
+        enddo
+        enddo
+        qglegc=min(qglegc,clegm)
+       endif
+      endif
+      if(iqq.le.11)then
+       qglegc=exp(qglegc)*qgls(sy,xp,bb,icdp,icz)
+      else
+       qglegc=exp(qglegc)*qgppdi(1.d0/sy,iqq-12)
+      endif
+      if(debug.ge.4)write (moniou,202)qglegc
+
+201   format(2x,'qglegc - interpolation of Pomeron leg eikonal:'
+     */4x,'sy=',e10.3,2x,'xp=',e10.3,2x,'b^2=',e10.3,2x,'vvx=',e10.3
+     *,2x,'icdp=',i1,2x,'icz=',i1,2x,'iqq=',i1)
+202   format(2x,'qglegc=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgpomc(sy,xp,xm,bb,vvx
+     *,icdp,icdt,icz,iqq)
+c-----------------------------------------------------------------------
+c qgpomc - unintegrated cut Pomeron eikonal
+c sy         - Pomeron mass squared,
+c xp,xm      - Pomeron light cone momenta,
+c bb         - squared impact parameter,
+c vvx        - relative strenth of nuclear screening corrections,
+c icdp, icdt - proj. and targ. diffractive eigenstates,
+c icz        - hadron class
+c iqq=1 - total,
+c iqq=2 - soft contribution,
+c iqq=3 - qg contribution
+c iqq=4 - gq contribution
+c iqq=5 - qq contribution
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wk(3),wi(3),wj(3),wz(3),wm(3)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr19/ ahl(3)
+      common /qgarr20/ spmax
+      common /qgarr25/ ahv(3)
+      common /qgarr26/ factk,fqscal
+      common /qgarr38/ qpomc(11,100,11,11,48)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.3)write (moniou,201)sy,xp,xm,bb,vvx
+     *,icdp,icdt,icz,iqq
+
+      qgpomc=0.d0
+      pomm=0.d0
+      if(iqq.eq.5)then                          !qq contribution
+       s2min=4.d0*fqscal*qt0
+       if(sy.gt.1.001d0*s2min.and.xp.lt..99d0.and.xm.lt..99d0)then
+        sj=qgjit(qt0,qt0,sy,2,2)
+        qgpomc=sj*factk*(qggrv(xp,qt0,icz,1)+qggrv(xp,qt0,icz,2))
+     *  *(qggrv(xm,qt0,2,1)+qggrv(xm,qt0,2,2))/xp/xm
+     *  *(1.d0-xp)**(ahv(icz)-ahl(icz))*(1.d0-xm)**(ahv(2)-ahl(2))
+     *  *exp(-bb/(4.d0*.0389d0*(rq(icdp,icz)+rq(icdt,2))))
+     *  /(8.d0*pi*(rq(icdp,icz)+rq(icdt,2)))*cd(icdp,icz)*cd(icdt,2)
+       endif
+       if(debug.ge.4)write (moniou,202)qgpomc
+       return
+      endif
+
+      rp=(rq(icdp,icz)+rq(icdt,2)+alfp*log(sy/xp/xm))*4.d0*.0389d0
+      z=exp(-bb/rp)
+      if(sy.le.max(1.d0,xp*sgap)*max(1.d0,xm*sgap)*1.01d0)then
+       qgpomc=sy**dels*fp(icz)*fp(2)*sigs*z/rp
+     * *4.d0*.0389d0*cd(icdp,icz)*cd(icdt,2)
+       return
+      endif
+
+      if(z.gt..2d0)then
+       zz=5.d0*z+6.d0
+      else
+       zz=(-bb/rp-dlog(0.2d0))/2.d0+7.d0
+      endif
+      jz=min(9,int(zz))
+      jz=max(1,jz)
+      if(zz.lt.1.d0)then
+       wz(2)=zz-jz
+       wz(1)=1.d0-wz(2)
+       izmax=2
+      else
+       if(jz.eq.6)jz=5
+       wz(2)=zz-jz
+       wz(3)=wz(2)*(wz(2)-1.d0)*.5d0
+       wz(1)=1.d0-wz(2)+wz(3)
+       wz(2)=wz(2)-2.d0*wz(3)
+       izmax=3
+      endif
+
+      yl=max(0.d0,dlog(sy/xp/xm/sgap**2)
+     */dlog(spmax/sgap**2))*10.d0+1.d0
+      k=max(1,int(yl))
+      k=min(k,9)
+      wk(2)=yl-k
+      wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+      wk(1)=1.d0-wk(2)+wk(3)
+      wk(2)=wk(2)-2.d0*wk(3)
+      iymax=3
+
+      if(xp.lt..2d0)then
+       xl1=6.d0-5.d0*log(5.d0*xp)/log(5.d0*sgap*xp*xm/sy)
+      else
+       xl1=5.d0*xp+5.d0
+      endif
+      i=min(8,int(xl1))
+      i=max(1,i)
+      if(i.eq.5)i=4
+      wi(2)=xl1-i
+      wi(3)=wi(2)*(wi(2)-1.d0)*.5d0
+      wi(1)=1.d0-wi(2)+wi(3)
+      wi(2)=wi(2)-2.d0*wi(3)
+      ix1max=3
+
+      if(sgap/sy*xm.gt..99d0)then
+       j=1
+       wj(1)=1.d0
+       ix2max=1
+      else
+       if(xm.lt..2d0)then
+        xl2=6.d0-5.d0*log(5.d0*xm)/log(sgap/sy*xm)
+       else
+        xl2=5.d0*xm+5.d0
+       endif
+       j=min(8,int(xl2))
+       j=max(1,j)
+       if(j.eq.5)j=4
+       wj(2)=xl2-j
+       wj(3)=wj(2)*(wj(2)-1.d0)*.5d0
+       wj(1)=1.d0-wj(2)+wj(3)
+       wj(2)=wj(2)-2.d0*wj(3)
+       ix2max=3
+      endif
+
+      ml=icdp+2*(icdt-1)+4*(icz-1)+12*(iqq-1)
+      if(vvx.eq.0.d0)then                     !hadron-proton collision
+       do l1=1,izmax
+        l2=jz+l1-1
+       do j1=1,ix2max
+        j2=j+j1-2
+       do i1=1,ix1max
+        i2=i+i1-1
+       do k1=1,iymax
+        k2=k+k1-1
+        qgpomc=qgpomc+qpomc(k2,i2+10*j2,l2,1,ml)
+     *  *wk(k1)*wi(i1)*wj(j1)*wz(l1)
+       enddo
+       enddo
+       enddo
+       enddo
+       if(zz.lt.1.d0)then
+        do j1=1,ix2max
+         j2=j+j1-2
+        do i1=1,ix1max
+         i2=i+i1-1
+        do k1=1,iymax
+         k2=k+k1-1
+         pomm=pomm+qpomc(k2,i2+10*j2,1,1,ml)*wk(k1)*wi(i1)*wj(j1)
+        enddo
+        enddo
+        enddo
+        qgpomc=min(qgpomc,pomm)
+       endif
+
+      else                                    !hA (AA) collision
+       vl=max(1.d0,vvx*10.d0+1.d0)
+       if(vl.lt.2.d0)then
+        m=1
+        wm(2)=vl-m
+        wm(3)=wm(2)*(wm(2)-1.d0)*.5d0
+        wm(1)=1.d0-wm(2)+wm(3)
+        wm(2)=wm(2)-2.d0*wm(3)
+        ivmax=3
+       else
+        m=min(int(vl),10)
+        wm(2)=vl-m
+        wm(1)=1.d0-wm(2)
+        ivmax=2
+       endif
+
+       do m1=1,ivmax
+        m2=m+m1-1
+       do l1=1,izmax
+        l2=jz+l1-1
+       do j1=1,ix2max
+        j2=j+j1-2
+       do i1=1,ix1max
+        i2=i+i1-1
+       do k1=1,iymax
+        k2=k+k1-1
+        qgpomc=qgpomc+qpomc(k2,i2+10*j2,l2,m2,ml)
+     *  *wk(k1)*wi(i1)*wj(j1)*wz(l1)*wm(m1)
+       enddo
+       enddo
+       enddo
+       enddo
+       enddo
+       if(zz.lt.1.d0)then
+        do m1=1,ivmax
+         m2=m+m1-1
+        do j1=1,ix2max
+         j2=j+j1-2
+        do i1=1,ix1max
+         i2=i+i1-1
+        do k1=1,iymax
+         k2=k+k1-1
+         pomm=pomm+qpomc(k2,i2+10*j2,1,m2,ml)
+     *   *wk(k1)*wi(i1)*wj(j1)*wm(m1)
+        enddo
+        enddo
+        enddo
+        enddo
+        qgpomc=min(qgpomc,pomm)
+       endif
+      endif
+      qgpomc=exp(qgpomc)*z
+      if(debug.ge.4)write (moniou,202)qgpomc
+
+201   format(2x,'qgpomc - unintegrated cut Pomeron eikonal:'
+     */4x,'sy=',e10.3,2x,'xp=',e10.3,2x,'xm=',e10.3,2x,'b^2=',e10.3
+     */4x,'vvx=',e10.3,2x,'icdp=',i1,2x,'icdt=',i1,2x,'icz=',i1
+     *,2x,'iqq=',i1)
+202   format(2x,'qgpomc=',e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qgsha(nbpom,ncola,ncolb,iret)
+c-----------------------------------------------------------------------------
+c qgsha - inelastic interaction (energy sharing and particle production)
+c nbpom - number of Pomeron blocks (nucleon(hadron)-nucleon collisions),
+c ncola - number of inel.-wounded proj. nucleons,
+c ncolb - number of inel.-wounded targ. nucleons
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208,npbmax=1000,npnmax=900,npmax=900
+     *,legmax=900,njmax=50000)
+      dimension wppr0(iapmax),wmtg0(iapmax),wppr1(iapmax),wmtg1(iapmax)
+     *,wppr2(iapmax),wmtg2(iapmax),izp(iapmax),izt(iapmax)
+     *,ila(iapmax),ilb(iapmax),lva(iapmax),lvb(iapmax)
+     *,lqa0(iapmax),lqb0(iapmax),ncola(iapmax),ncolb(iapmax)
+     *,ncola0(iapmax),ncolb0(iapmax)
+     *,xpomp0(npnmax,iapmax),xpomt0(npnmax,iapmax)
+     *,xpopin0(npmax,npbmax),xpomin0(npmax,npbmax)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr2/  scm,wp0,wm0
+      common /qgarr6/  pi,bm,amws
+      common /qgarr7/  xa(iapmax,3),xb(iapmax,3),b
+      common /qgarr9/  iwp(iapmax),iwt(iapmax),lqa(iapmax),lqb(iapmax)
+     *,iprcn(iapmax),itgcn(iapmax),ias(npbmax),ibs(npbmax),nqs(npbmax)
+     *,npompr(npbmax),npomtg(npbmax),npomin(npbmax),nnpr(npmax,npbmax)
+     *,nntg(npmax,npbmax),ilpr(legmax,npbmax),iltg(legmax,npbmax)
+     *,lnpr(legmax,npbmax),lntg(legmax,npbmax)
+     *,nbpi(npnmax,iapmax),nbti(npnmax,iapmax),idnpi(npnmax,iapmax)
+     *,idnti(npnmax,iapmax),nppi(npnmax,iapmax),npti(npnmax,iapmax)
+     *,nlpi(npnmax,iapmax),nlti(npnmax,iapmax)
+      common /qgarr11/ b10
+      common /qgarr12/ nsp
+      common /qgarr13/ nsf,iaf(iapmax)
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr23/ bbpom(npbmax),vvxpom(npbmax)
+     *,bpompr(npnmax,iapmax),bpomtg(npnmax,iapmax)
+     *,vvxpr(npnmax,iapmax),vvxtg(npnmax,iapmax)
+     *,xpompr(npnmax,iapmax),xpomtg(npnmax,iapmax)
+     *,xpopin(npmax,npbmax),xpomin(npmax,npbmax),vvxin(npmax,npbmax)
+     *,bpomin(npmax,npbmax)
+      common /qgarr26/ factk,fqscal
+      common /qgarr37/ eqj(4,njmax),iqj(njmax),ncj(2,njmax),nj
+      common /qgarr40/ xppr(npnmax,iapmax),xmtg(npnmax,iapmax)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      external qgran
+
+      if(debug.ge.1)write (moniou,201)nbpom             !so161205
+      nsp0=nsp
+
+      do j=1,ia(1)
+       if(lqa(j).ne.0)then
+        do i=1,lqa(j)
+         if(idnpi(i,j).ne.0)xpomp0(i,j)=xpompr(i,j)
+        enddo
+       endif
+      enddo
+      do j=1,ia(2)
+       if(lqb(j).ne.0)then
+        do i=1,lqb(j)
+         if(idnti(i,j).ne.0)xpomt0(i,j)=xpomtg(i,j)
+        enddo
+       endif
+      enddo
+      if(nbpom.ne.0)then
+       do nb=1,nbpom                            !loop over collisions
+        if(npomin(nb).ne.0)then
+         do np=1,npomin(nb)         !loop over interm. Pomerons in the collision
+          xpopin0(np,nb)=xpopin(np,nb)
+          xpomin0(np,nb)=xpomin(np,nb)
+         enddo
+        endif
+       enddo
+      endif
+      iret=0
+      nret=0
+
+1     nsp=nsp0
+      nj=0
+
+      if(iret.ne.0)then             !rejection during energy-sharing
+       nret=nret+1
+       if(nret.gt.100)return        !too many rejections -> redo configuration
+      endif
+
+      do j=1,ia(1)
+       if(lqa(j).ne.0)then
+        do i=1,lqa(j)
+         if(idnpi(i,j).ne.0)xpompr(i,j)=xpomp0(i,j)
+        enddo
+       endif
+      enddo
+      do j=1,ia(2)
+       if(lqb(j).ne.0)then
+        do i=1,lqb(j)
+         if(idnti(i,j).ne.0)xpomtg(i,j)=xpomt0(i,j)
+        enddo
+       endif
+      enddo
+      if(nbpom.ne.0)then
+       do nb=1,nbpom                            !loop over collisions
+        if(npomin(nb).ne.0)then
+         do np=1,npomin(nb)         !loop over interm. Pomerons in the collision
+          xpopin(np,nb)=xpopin0(np,nb)
+          xpomin(np,nb)=xpomin0(np,nb)
+         enddo
+        endif
+       enddo
+      endif
+
+c-------------------------------------------------
+c initial nucleon (hadron) types
+      if(ia(1).ne.1)then
+       do i=1,ia(1)
+        izp(i)=int(2.5d0+qgran(b10))   !i-th projectile nucleon type
+       enddo
+      else
+       izp(1)=icp                      !projectile hadron type
+      endif
+      if(ia(2).ne.1)then
+       do i=1,ia(2)
+        izt(i)=int(2.5d0+qgran(b10))   !i-th target nucleon type
+       enddo
+      else
+       izt(1)=2                        !target proton
+      endif
+
+      do i=1,ia(1)
+       lqa0(i)=lqa(i)
+       lva(i)=0
+       ncola0(i)=ncola(i)
+      enddo
+      do i=1,ia(2)
+       lqb0(i)=lqb(i)
+       lvb(i)=0
+       ncolb0(i)=ncolb(i)
+      enddo
+
+c-------------------------------------------------
+c energy-momentum sharing between Pomerons
+      if(nbpom.ne.0)then
+       if(debug.ge.1)write (moniou,202)
+       call qgprox(0)        !initial x-configuration
+       gbl0=qgweix(nbpom)    !log-weight for the initial x-configuration
+       nrej=0
+       nchange=0
+       gbnorm=.1d0
+       gbhmax=-1000.d0
+
+2      continue
+       call qgprox(1)        !proposed x-configuration
+       gbl=qgweix(nbpom)     !log-weight for the proposed x-configuration
+       gbh=gbl-gbl0-gbnorm   !log of acceptance probability
+       gbhmax=max(gbhmax,gbh)
+
+       if(debug.ge.5)write (moniou,203)gbh,nrej,nchange
+       if(gbh.lt.-50.d0.or.qgran(b10).gt.exp(gbh))then
+        nrej=nrej+1
+        if(nrej.gt.100)then               !too many rejections
+         nrej=0
+         nchange=nchange+1
+         gbnorm=gbnorm+gbhmax+.5d0        !new normalization of acceptance
+         gbhmax=-1000.d0
+         if(debug.ge.4)write (moniou,204)nchange
+        endif
+        goto 2                            !rejection
+       endif
+      endif
+
+c-------------------------------------------------
+c leading remnant LC momenta
+      if(debug.ge.1)write (moniou,205)
+      do i=1,ia(1)                        !loop over proj. nucleons
+       wppr0(i)=wp0
+       wppr1(i)=0.d0
+       wppr2(i)=0.d0
+       if(lqa(i).ne.0)then
+        do l=1,lqa(i)                     !loop over constituent partons
+         wppr0(i)=wppr0(i)-wp0*xppr(l,i)  !subtract Pomeron LC momentum
+         if(wppr0(i).lt.0.d0)then
+          wppr0(i)=0.d0
+         endif
+        enddo
+       endif
+      enddo
+      do i=1,ia(2)                        !loop over targ. nucleons
+       wmtg0(i)=wm0
+       wmtg1(i)=0.d0
+       wmtg2(i)=0.d0
+       if(lqb(i).ne.0)then
+        do l=1,lqb(i)                     !loop over constituent partons
+         wmtg0(i)=wmtg0(i)-wm0*xmtg(l,i)  !subtract Pomeron LC momentum
+         if(wmtg0(i).lt.-1.d-15)stop'w^-<0!!!'
+         wmtg0(i)=max(0.d0,wmtg0(i))
+        enddo
+       endif
+      enddo
+
+c-------------------------------------------------
+c momentum conservation (correction for 3p-vertexes)
+      if(debug.ge.1)write (moniou,206)
+      if(nbpom.ne.0)then
+       do nb=1,nbpom                            !loop over collisions
+        ip=ias(nb)                              !proj. index
+        it=ibs(nb)                              !targ. index
+        if(nqs(nb).ne.0)then
+         do np=1,nqs(nb)             !loop over single Pomerons in the collision
+          lnp=nnpr(np,nb)                       !proj. constituent parton index
+          lnt=nntg(np,nb)                       !targ. constituent parton index
+          wppr1(ip)=wppr1(ip)+xppr(lnp,ip)*wp0  !count Pomeron LC momentum
+          wmtg1(it)=wmtg1(it)+xmtg(lnt,it)*wm0  !count Pomeron LC momentum
+         enddo
+        endif
+        if(npomin(nb).ne.0)then
+         do np=1,npomin(nb)         !loop over interm. Pomerons in the collision
+          xpp=xpopin(np,nb)
+          xpm=xpomin(np,nb)
+          if(xpp*xpm*scm.gt.1.d0)then
+           wppr2(ip)=wppr2(ip)+xpp*wp0          !count Pomeron LC momentum
+           wmtg2(it)=wmtg2(it)+xpm*wm0          !count Pomeron LC momentum
+          else
+           xpopin(np,nb)=0.d0
+           xpomin(np,nb)=0.d0
+          endif
+         enddo
+        endif
+        if(npompr(nb).ne.0)then
+         do np=1,npompr(nb)       !loop over proj. leg Pomerons in the collision
+          ipp=ilpr(np,nb)                       !proj. index
+          lnp=lnpr(np,nb)                       !proj. constituent parton index
+          xpp=xppr(lnp,ipp)
+          xpm=xpompr(lnp,ipp)
+          if(xpp*xpm*scm.gt.1.d0)then
+           wppr1(ipp)=wppr1(ipp)+xpp*wp0        !count Pomeron LC momentum
+           wmtg2(it)=wmtg2(it)+xpm*wm0          !count Pomeron LC momentum
+          else
+           xppr(lnp,ipp)=0.d0
+           xpompr(lnp,ipp)=0.d0
+          endif
+         enddo
+        endif
+        if(npomtg(nb).ne.0)then
+         do np=1,npomtg(nb)       !loop over targ. leg Pomerons in the collision
+          itt=iltg(np,nb)                       !targ. index
+          lnt=lntg(np,nb)                       !targ. constituent parton index
+          xpp=xpomtg(lnt,itt)
+          xpm=xmtg(lnt,itt)
+          if(xpp*xpm*scm.gt.1.d0)then
+           wppr2(ip)=wppr2(ip)+xpp*wp0                !count Pomeron LC momentum
+           wmtg1(itt)=wmtg1(itt)+xpm*wm0        !count Pomeron LC momentum
+          else
+           xmtg(lnt,itt)=0.d0
+           xpomtg(lnt,itt)=0.d0
+          endif
+         enddo
+        endif
+       enddo
+      endif
+
+      do ip=1,ia(1)
+       if(wppr1(ip)+wppr2(ip).ne.0.d0)then
+        if(lqa(ip).ne.0)then
+         do i=1,lqa(ip)
+          xppr(i,ip)=xppr(i,ip)*(wp0-wppr0(ip)) !renorm. for const. partons
+     *    /(wppr1(ip)+wppr2(ip))
+         enddo
+
+         do nb=1,nbpom
+          if(ias(nb).eq.ip.and.npomtg(nb)+npomin(nb).ne.0)then
+           if(npomin(nb).ne.0)then
+            do np=1,npomin(nb)
+             xpopin(np,nb)=xpopin(np,nb)*(wp0-wppr0(ip))
+     *       /(wppr1(ip)+wppr2(ip))
+            enddo
+           endif
+           if(npomtg(nb).ne.0)then
+            do np=1,npomtg(nb)
+             itt=iltg(np,nb)
+             lnt=lntg(np,nb)
+             xpomtg(lnt,itt)=xpomtg(lnt,itt)*(wp0-wppr0(ip))
+     *       /(wppr1(ip)+wppr2(ip))
+            enddo
+           endif
+          endif
+         enddo
+
+        elseif(wppr2(ip).gt.wp0)then
+         wpt=wp0/sgap/2.d0*4.d0**qgran(b10)
+         do nb=1,nbpom
+          if(ias(nb).eq.ip.and.npomtg(nb)+npomin(nb).ne.0)then
+           if(npomin(nb).ne.0)then
+            do np=1,npomin(nb)
+             xpopin(np,nb)=xpopin(np,nb)*wpt/wppr2(ip)
+            enddo
+           endif
+           if(npomtg(nb).ne.0)then
+            do np=1,npomtg(nb)
+             itt=iltg(np,nb)
+             lnt=lntg(np,nb)
+             xpomtg(lnt,itt)=xpomtg(lnt,itt)*wpt/wppr2(ip)
+            enddo
+           endif
+          endif
+         enddo
+         wppr0(ip)=wp0-wpt
+        else
+         wppr0(ip)=wp0-wppr2(ip)
+        endif
+       else                             !so230913
+        wppr0(ip)=wp0                   !so230913
+       endif
+      enddo
+
+      do it=1,ia(2)
+       if(wmtg1(it)+wmtg2(it).ne.0.d0)then
+        if(lqb(it).ne.0)then
+         do i=1,lqb(it)
+          xmtg(i,it)=xmtg(i,it)*(wm0-wmtg0(it))/(wmtg1(it)+wmtg2(it))
+         enddo
+
+         do nb=1,nbpom
+          if(ibs(nb).eq.it.and.npompr(nb)+npomin(nb).ne.0)then
+           if(npomin(nb).ne.0)then
+            do np=1,npomin(nb)
+             xpomin(np,nb)=xpomin(np,nb)*(wm0-wmtg0(it))
+     *       /(wmtg1(it)+wmtg2(it))
+            enddo
+           endif
+           if(npompr(nb).ne.0)then
+            do np=1,npompr(nb)
+             ipp=ilpr(np,nb)
+             lnp=lnpr(np,nb)
+             xpompr(lnp,ipp)=xpompr(lnp,ipp)*(wm0-wmtg0(it))
+     *       /(wmtg1(it)+wmtg2(it))
+            enddo
+           endif
+          endif
+         enddo
+
+        elseif(wmtg2(it).gt.wm0)then
+         wmt=wm0/sgap/2.d0*4.d0**qgran(b10)
+         do nb=1,nbpom
+          if(ibs(nb).eq.it.and.npompr(nb)+npomin(nb).ne.0)then
+           if(npomin(nb).ne.0)then
+            do np=1,npomin(nb)
+             xpomin(np,nb)=xpomin(np,nb)*wmt/wmtg2(it)
+            enddo
+           endif
+           if(npompr(nb).ne.0)then
+            do np=1,npompr(nb)
+             ipp=ilpr(np,nb)
+             lnp=lnpr(np,nb)
+             xpompr(lnp,ipp)=xpompr(lnp,ipp)*wmt/wmtg2(it)
+            enddo
+           endif
+          endif
+         enddo
+         wmtg0(it)=wm0-wmt
+        else
+         wmtg0(it)=wm0-wmtg2(it)
+        endif
+       else                             !so230913
+        wmtg0(it)=wm0                   !so230913
+       endif
+      enddo
+
+c-------------------------------------------------
+c treatment of low mass diffraction
+      if(debug.ge.1)write (moniou,207)
+      do ip=1,ia(1)                        !loop over proj. nucleons
+       if(iwp(ip).eq.2)then                !diffraction dissociation
+        it=iprcn(ip)
+        if(debug.ge.2)write (moniou,208)ip,it
+        if(iwt(it).eq.2)then
+         call qgdifr(wppr0(ip),wmtg0(it),izp(ip),izt(it),-2,-2,iret)
+        elseif(iwt(it).eq.-1)then
+         call qgdifr(wppr0(ip),wmtg0(it),izp(ip),izt(it),-2,0,iret)
+        elseif(iwt(it).gt.0)then
+         call qgdifr(wppr0(ip),wmtg0(it),izp(ip),izt(it),-2,-1,iret)
+        else
+         stop'wrong connection for diffraction'
+        endif
+        if(iret.eq.1)goto 1
+       endif
+      enddo
+
+      do it=1,ia(2)                        !loop over targ. nucleons
+       if(iwt(it).eq.2)then                !diffraction dissociation
+        ip=itgcn(it)
+        if(debug.ge.2)write (moniou,209)it,ip
+        if(iwp(ip).eq.-1)then
+         call qgdifr(wppr0(ip),wmtg0(it),izp(ip),izt(it),0,-2,iret)
+        elseif(iwp(ip).gt.0.and.iwp(ip).ne.2)then
+         call qgdifr(wppr0(ip),wmtg0(it),izp(ip),izt(it),-1,-2,iret)
+        endif
+        if(iret.eq.1)goto 1
+       endif
+      enddo
+
+c-------------------------------------------------
+c particle production for all cut Pomerons
+      s2min=4.d0*fqscal*qt0       !threshold energy for a hard process
+      if(nbpom.ne.0)then
+       if(debug.ge.1)write (moniou,210)
+       do npb=1,nbpom                            !loop over collisions
+        ip=ias(npb)                              !proj. index
+        it=ibs(npb)                              !targ. index
+        icdp=iddp(ip)                            !proj. diffr. eigenstate
+        icdt=iddt(it)                            !targ. diffr. eigenstate
+        bbp=bbpom(npb)                           !b^2 between proj. and targ.
+        vvx=vvxpom(npb)                          !nuclear screening factor
+        if(debug.ge.1)write (moniou,211)npb,ip,it,bbp,vvx,nqs(npb)
+     *  ,npomin(npb),npompr(npb),npomtg(npb)
+
+        if(npomin(npb).ne.0)then
+         do n=1,npomin(npb)                      !loop over interm. Pomerons
+          wpi=xpopin(n,npb)*wp0                  !LC+ for the Pomeron
+          wmi=xpomin(n,npb)*wm0                  !LC- for the Pomeron
+          if(debug.ge.2)write (moniou,212)n,wpi,wmi
+          if(wpi*wmi.ne.0.d0)then
+           ic11=0
+           ic12=0
+           ic21=0
+           ic22=0
+           call qgstr(wpi,wmi,wppr0(ip),wmtg0(it)
+     *     ,ic11,ic12,ic22,ic21,0,0)             !string hadronization
+          endif
+         enddo
+        endif
+
+        if(nqs(npb).ne.0)then
+         do n=1,nqs(npb)                         !loop over single Pomerons
+          lnp=nnpr(n,npb)                        !index for proj. constituent
+          lnt=nntg(n,npb)                        !index for targ. constituent
+          lqa0(ip)=lqa0(ip)-1
+          lqb0(it)=lqb0(it)-1
+          xpi=xppr(lnp,ip)
+          xmi=xmtg(lnt,it)
+          wpi=wp0*xpi                            !LC+ for the Pomeron
+          wmi=wm0*xmi                            !LC- for the Pomeron
+          sy=wpi*wmi
+          wtot=qgpomc(sy,xpi,xmi,bbp,vvx,icdp,icdt,icz,1) !total
+          wsoft=qgpomc(sy,xpi,xmi,bbp,vvx,icdp,icdt,icz,2)!soft interaction
+          wqg=qgpomc(sy,xpi,xmi,bbp,vvx,icdp,icdt,icz,3)  !qg-hard interaction
+          wgq=qgpomc(sy,xpi,xmi,bbp,vvx,icdp,icdt,icz,4)  !gq-hard interaction
+          wqq=qgpomc(sy,xpi,xmi,bbp,vvx,icdp,icdt,icz,5)  !qq-hard interaction
+          aks=qgran(b10)*wtot
+          if(debug.ge.2)write (moniou,213)n,wpi,wmi
+
+          if(aks.lt.wsoft.or.sy.lt.2.d0*s2min)then !soft string hadronization
+           if(lqa0(ip).eq.0.and.lva(ip).eq.0)then
+            call qgixxd(izp(ip),ic11,ic12,icz)
+           else
+            ic11=0
+            ic12=0
+           endif
+           if(lqb0(it).eq.0.and.lvb(it).eq.0)then
+            call qgixxd(izt(it),ic21,ic22,2)
+           else
+            ic21=0
+            ic22=0
+           endif
+           call qgstr(wpi,wmi,wppr0(ip),wmtg0(it),ic11,ic12,ic22,ic21
+     *     ,1,1)
+          else            !QCD evolution and hadronization for semi-hard Pomeron
+           if(lva(ip).eq.0.and.lvb(it).eq.0.and.aks.lt.wsoft+wqq)then
+            iqq=3
+            lva(ip)=1
+            lvb(it)=1
+           elseif(lva(ip).eq.0.and.aks.gt.wqg)then
+            iqq=1
+            lva(ip)=1
+           elseif(lvb(it).eq.0.and.aks.gt.wgq)then
+            iqq=2
+            lvb(it)=1
+           else
+            iqq=0
+           endif
+
+           call qghot(wpi,wmi,dsqrt(bbp),vvx,nva,nvb,izp(ip),izt(it)
+     *     ,icdp,icdt,icz,iqq,0)            !QCD evolution + jet hadronization
+           if(iqq.eq.1.or.iqq.eq.3)ila(ip)=nva
+           if(iqq.eq.2.or.iqq.eq.3)ilb(it)=nvb
+          endif
+         enddo
+        endif
+
+        if(npompr(npb).ne.0)then
+         do l=1,npompr(npb)                 !loop over proj. leg Pomerons
+          ipp=ilpr(l,npb)                  !proj. index
+          lnp=lnpr(l,npb)                  !index for proj. constituent
+          bbpr=bpompr(lnp,ipp)             !b^2 for the Pomeron
+          vvxp=vvxpr(lnp,ipp)              !screening factor
+          lqa0(ipp)=lqa0(ipp)-1
+          xpi=xppr(lnp,ipp)
+          xmi=xpompr(lnp,ipp)
+          wpi=wp0*xpi                      !LC+ for the Pomeron
+          wmi=wm0*xmi                      !LC- for the Pomeron
+          sy=wpi*wmi
+          if(sy.ne.0.d0)then
+           wtot=qglegc(sy,xpi,bbpr,vvxp,iddp(ipp),icz,9)   !total
+           wsoft=qglegc(sy,xpi,bbpr,vvxp,iddp(ipp),icz,10) !soft interaction
+           wqg=qglegc(sy,xpi,bbpr,vvxp,iddp(ipp),icz,11)   !qg-hard interaction
+          else
+           wsoft=1.d0
+           wtot=1.d0
+           wqg=0.d0
+          endif
+          aks=qgran(b10)*wtot
+          if(debug.ge.2)write (moniou,214)l,wpi,wmi
+
+          if(aks.le.wsoft.or.sy.lt.2.d0*s2min)then  !soft string hadronization
+           if(lqa0(ipp).eq.0.and.lva(ipp).eq.0.and.sy.ne.0.d0)then
+            call qgixxd(izp(ipp),ic11,ic12,icz)
+           else
+            ic11=0
+            ic12=0
+           endif
+           ic21=0
+           ic22=0
+           call qgstr(wpi,wmi,wppr0(ipp),wmtg0(it),ic11,ic12,ic22,ic21
+     *     ,1,0)
+
+          else        !QCD evolution and hadronization for semi-hard Pomeron
+           if(lva(ipp).eq.0.and.aks.gt.wqg)then
+            iqq=1
+            lva(ipp)=1
+           else
+            iqq=0
+           endif
+
+           call qghot(wpi,wmi,dsqrt(bbpr),vvxp,nva,nvb,izp(ipp),izt(it)
+     *     ,iddp(ipp),icdt,icz,iqq,1)         !QCD evolution + jet hadronization
+           if(iqq.eq.1)ila(ipp)=nva
+          endif
+          call qglead(wppr0(ipp),wmtg0(it),lqa(ipp)+1-iwp(ipp)
+     *    ,lqb(it)+1-iwt(it),lqa0(ipp)+ncola0(ipp),lqb0(it)+ncolb0(it)
+     *    ,lva(ipp),lvb(it),izp(ipp),izt(it),ila(ipp),ilb(it),iret)  !remnants
+          if(iret.ne.0)goto 1
+         enddo
+        endif
+
+        if(npomtg(npb).ne.0)then
+         do l=1,npomtg(npb)                !loop over targ. leg Pomerons
+          itt=iltg(l,npb)                  !targ. index
+          lnt=lntg(l,npb)                  !index for targ. constituent
+          bbtg=bpomtg(lnt,itt)             !b^2 for the Pomeron
+          vvxt=vvxtg(lnt,itt)              !screening factor
+          lqb0(itt)=lqb0(itt)-1
+          xmi=xmtg(lnt,itt)
+          wmi=wm0*xmi                      !LC- for the Pomeron
+          wpi=wp0*xpomtg(lnt,itt)          !LC+ for the Pomeron
+          sy=wpi*wmi
+          if(sy.ne.0.d0)then
+           wtot=qglegc(sy,xmi,bbtg,vvxt,iddt(itt),2,9)  !tot
+           wsoft=qglegc(sy,xmi,bbtg,vvxt,iddt(itt),2,10)!soft interaction
+           wqg=qglegc(sy,xmi,bbtg,vvxt,iddt(itt),2,11)  !qg-hard interaction
+          else
+           wtot=1.d0
+           wsoft=1.d0
+           wqg=0.d0
+          endif
+          aks=qgran(b10)*wtot
+          if(debug.ge.2)write (moniou,215)l,wpi,wmi
+
+          if(aks.le.wsoft.or.sy.lt.2.d0*s2min)then  !soft string hadronization
+           ic11=0
+           ic12=0
+           if(lqb0(itt).eq.0.and.lvb(itt).eq.0.and.sy.ne.0.d0)then
+            call qgixxd(izt(itt),ic21,ic22,2)
+           else
+            ic21=0
+            ic22=0
+           endif
+           call qgstr(wpi,wmi,wppr0(ip),wmtg0(itt),ic11,ic12,ic22,ic21
+     *     ,0,1)
+
+          else         !QCD evolution and hadronization for semi-hard Pomeron
+           if(lvb(itt).eq.0.and.aks.gt.wqg)then
+            iqq=2
+            lvb(itt)=1
+           else
+            iqq=0
+           endif
+
+           call qghot(wpi,wmi,dsqrt(bbtg),vvxt,nva,nvb,izp(ip),izt(itt)
+     *     ,icdp,iddt(itt),icz,iqq,2)         !QCD evolution + jet hadronization
+           if(iqq.eq.2)ilb(itt)=nvb
+          endif
+          call qglead(wppr0(ip),wmtg0(itt),lqa(ip)+1-iwp(ip),lqb(itt)
+     *    +1-iwt(itt),lqa0(ip)+ncola0(ip),lqb0(itt)+ncolb0(itt)
+     *    ,lva(ip),lvb(itt),izp(ip),izt(itt),ila(ip),ilb(itt),iret) !remnants
+          if(iret.ne.0)goto 1
+         enddo
+        endif
+        ncola0(ip)=ncola0(ip)-1
+        ncolb0(it)=ncolb0(it)-1
+        call qglead(wppr0(ip),wmtg0(it),lqa(ip)+1-iwp(ip),lqb(it)
+     *  +1-iwt(it),lqa0(ip)+ncola0(ip),lqb0(it)+ncolb0(it)
+     *  ,lva(ip),lvb(it),izp(ip),izt(it),ila(ip),ilb(it),iret) !remnants
+        if(iret.ne.0)goto 1
+       enddo                                           !end of collision loop
+      endif
+
+      if(nj.ne.0)then                   !arrangement of parton color connections
+       if(debug.ge.1)write (moniou,216)nj
+       call qgjarr(jfl)
+       if(jfl.eq.0)then
+        iret=1
+        goto 1
+       endif
+       if(debug.ge.1)write (moniou,217)
+       call qgxjet                      !jet hadronization
+      endif
+      if(debug.ge.1)write (moniou,218)
+
+201   format(2x,'qgsha - inelastic interaction, N of Pomeron blocks:'
+     *,i4)
+202   format(2x,'qgsha: energy-momentum sharing between Pomerons')
+203   format(2x,'qgsha: log of acceptance probability - ',e10.3
+     */4x,'N of rejections - ',i4,2x,'N of renorm. - ',i3)
+204   format(2x,'qgsha:  new normalization of acceptance,'
+     *,' N of renorm. - ',i3)
+205   format(2x,'qgsha: leading remnant LC momenta')
+206   format(2x,'qgsha: momentum conservation '
+     *,'(correction for 3p-vertexes)')
+207   format(2x,'qgsha: treatment of low mass diffraction')
+208   format(2x,'qgsha: diffraction of ',i3,'-th proj. nucleon,'
+     *,' recoil of ',i3,'-th targ. nucleon')
+209   format(2x,'qgsha: diffraction of ',i3,'-th targ. nucleon,'
+     *,' recoil of ',i3,'-th proj. nucleon')
+210   format(2x,'qgsha: particle production for all cut Pomerons')
+211   format(2x,'qgsha: ',i4,'-th collision,  proj. index - ',i3,2x
+     *,'targ. index - ',i3
+     */4x,'b^2=',e10.3,2x,'vvx=',e10.3,2x,'N of single Pomerons - ',i3
+     *,2x,' N of interm. Pomerons - ',i3
+     */4x,'N of proj. legs - ',i3,2x,'N of targ. legs - ',i3)
+212   format(2x,'qgsha: particle production for '
+     *,i3,'-th interm. Pomeron'
+     */4x,'light cone momenta for the Pomeron:',2e10.3)
+213   format(2x,'qgsha: particle production for '
+     *,i3,'-th single Pomeron'
+     */4x,'light cone momenta for the Pomeron:',2e10.3)
+214   format(2x,'qgsha: particle production for '
+     *,i3,'-th proj. leg Pomeron'
+     */4x,'light cone momenta for the Pomeron:',2e10.3)
+215   format(2x,'qgsha: particle production for '
+     *,i3,'-th targ. leg Pomeron'
+     */4x,'light cone momenta for the Pomeron:',2e10.3)
+216   format(2x,'qgsha: arrangement of color connections for '
+     *,i5,' final partons')
+217   format(2x,'qgsha: jet hadronization')
+218   format(2x,'qgsha - end')
+      return
+      end
+
+c=============================================================================
+      subroutine qgprox(imode)
+c-------------------------------------------------------------------------
+c qgprox - propose Pomeron end LC momenta
+c imod = 0 - to define normalization
+c imod = 1 - propose values according to x^delf * (1 - sum_i x_i)^ahl
+c-------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208,npbmax=1000,npnmax=900,npmax=900,legmax=900)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr2/  scm,wp0,wm0
+      common /qgarr6/  pi,bm,amws
+      common /qgarr9/  iwp(iapmax),iwt(iapmax),lqa(iapmax),lqb(iapmax)
+     *,iprcn(iapmax),itgcn(iapmax),ias(npbmax),ibs(npbmax),nqs(npbmax)
+     *,npompr(npbmax),npomtg(npbmax),npomin(npbmax),nnpr(npmax,npbmax)
+     *,nntg(npmax,npbmax),ilpr(legmax,npbmax),iltg(legmax,npbmax)
+     *,lnpr(legmax,npbmax),lntg(legmax,npbmax)
+     *,nbpi(npnmax,iapmax),nbti(npnmax,iapmax),idnpi(npnmax,iapmax)
+     *,idnti(npnmax,iapmax),nppi(npnmax,iapmax),npti(npnmax,iapmax)
+     *,nlpi(npnmax,iapmax),nlti(npnmax,iapmax)
+      common /qgarr11/ b10
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr19/ ahl(3)
+      common /qgarr23/ bbpom(npbmax),vvxpom(npbmax)
+     *,bpompr(npnmax,iapmax),bpomtg(npnmax,iapmax)
+     *,vvxpr(npnmax,iapmax),vvxtg(npnmax,iapmax)
+     *,xpompr(npnmax,iapmax),xpomtg(npnmax,iapmax)
+     *,xpopin(npmax,npbmax),xpomin(npmax,npbmax),vvxin(npmax,npbmax)
+     *,bpomin(npmax,npbmax)
+      common /qgarr40/ xppr(npnmax,iapmax),xmtg(npnmax,iapmax)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      external qgran
+
+      if(debug.ge.3)write (moniou,201)imode
+
+      delf=dels
+      if(imode.eq.0)then                    !0-configuration (for normalization)
+       do ip=1,ia(1)                        !loop over proj. nucleons
+        if(lqa(ip).ne.0)then
+         do n=1,lqa(ip)                     !loop over proj. constituents
+          if(idnpi(n,ip).eq.0)then
+           xppr(n,ip)=1.d0/wp0              !LC+ for single Pomeron
+          else
+           xppr(n,ip)=1.d0/xpompr(n,ip)/scm !LC+ for leg Pomeron
+          endif
+          enddo
+        endif
+       enddo
+       do it=1,ia(2)                        !loop over targ. nucleons
+        if(lqb(it).ne.0)then
+         do n=1,lqb(it)                     !loop over targ. constituents
+          if(idnti(n,it).eq.0)then
+           xmtg(n,it)=1.d0/wm0              !LC- for single Pomeron
+          else
+           xmtg(n,it)=1.d0/xpomtg(n,it)/scm !LC- for leg Pomeron
+          endif
+         enddo
+        endif
+       enddo
+
+      else                                  !proposed configuration
+       do ip=1,ia(1)                        !loop over proj. nucleons
+        if(lqa(ip).ne.0)then
+         xpt=1.d0
+         do n=1,lqa(ip)                     !loop over proj. constituents
+          nrej=0
+          alfl=ahl(icz)+(lqa(ip)-n)*(1.d0+delf)
+c          if(icz.eq.2)alfl=alfl-float(lqa(ip)-1)/lqa(ip)  !baryon "junction"
+          gb0=(1.d0-.11d0**(1.d0/(1.d0+delf)))**alfl
+     *    *exp(alfl*(1.d0+delf)*.11d0)*2.d0
+1         continue
+c proposal functions are chosen depending on the parameters
+c to assure an efficient procedure
+          if(delf.ge.0.d0.and.alfl.ge.0.d0
+     *    .or.delf.lt.0.d0.and.alfl.le.0.d0)then
+           up=1.d0-qgran(b10)**(1.d0/(1.d0+delf))
+           if(1.d0-up.lt.1.d-20)goto 1
+           tp=1.d0-up**(1.d0/(1.d0+alfl))
+           gb=(tp/(1.d0-up))**delf
+          elseif(delf.lt.0.d0.and.alfl.gt.0.d0)then
+           up=-log(1.d0-qgran(b10)*(1.d0-exp(-alfl*(1.d0+delf))))
+     *     /alfl/(1.d0+delf)
+           tp=up**(1.d0/(1.d0+delf))
+           gb=(1.d0-tp)**alfl*exp(alfl*(1.d0+delf)*up)/gb0
+          else
+           tp=1.d0-qgran(b10)**(1.d0/(1.d0+alfl))
+           gb=tp**delf
+          endif
+          if(qgran(b10).gt.gb)then
+           nrej=nrej+1
+           goto 1
+          endif
+          xppr(n,ip)=tp*xpt                 !proposed LC+ for the constituent
+          xpt=xpt-xppr(n,ip)                !LC+ of the remnant
+          enddo
+        endif
+       enddo
+
+       do it=1,ia(2)                        !loop over targ. nucleons
+        if(lqb(it).ne.0)then
+         xmt=1.d0
+         do n=1,lqb(it)                     !loop over targ. constituents
+          nrej=0
+          alfl=ahl(2)+(lqb(it)-n)*(1.d0+delf)
+c     *    -float(lqb(it)-1)/lqb(it)                       !baryon "junction"
+          gb0=(1.d0-.11d0**(1.d0/(1.d0+delf)))**alfl
+     *    *exp(alfl*(1.d0+delf)*.11d0)*2.d0
+2         continue
+          if(delf.ge.0.d0.and.alfl.ge.0.d0
+     *    .or.delf.lt.0.d0.and.alfl.le.0.d0)then
+           up=1.d0-qgran(b10)**(1.d0/(1.d0+delf))
+           if(1.d0-up.lt.1.d-20)goto 2
+           tp=1.d0-up**(1.d0/(1.d0+alfl))
+           gb=(tp/(1.d0-up))**delf
+          elseif(delf.lt.0.d0.and.alfl.gt.0.d0)then
+           up=-log(1.d0-qgran(b10)*(1.d0-exp(-alfl*(1.d0+delf))))
+     *     /alfl/(1.d0+delf)
+           tp=up**(1.d0/(1.d0+delf))
+           gb=(1.d0-tp)**alfl*exp(alfl*(1.d0+delf)*up)/gb0
+          else
+           tp=1.d0-qgran(b10)**(1.d0/(1.d0+alfl))
+           gb=tp**delf
+          endif
+          if(qgran(b10).gt.gb)then
+           nrej=nrej+1
+           goto 2
+          endif
+          if(qgran(b10).gt.gb)goto 2
+          xmtg(n,it)=tp*xmt                 !proposed LC- for the constituent
+          xmt=xmt-xmtg(n,it)                !LC- of the remnant
+          enddo
+        endif
+       enddo
+      endif
+      if(debug.ge.4)write (moniou,202)
+
+201   format(2x,'qgprox - propose Pomeron end LC momenta, imode=',i2)
+202   format(2x,'qgprox - end')
+      return
+      end
+
+c=============================================================================
+      double precision function qgweix(nbpom)
+c-------------------------------------------------------------------------
+c qgweix - log-weight of x-configuration
+c imod = 0 - to define normalization
+c imod = 1 - propose values according to x^delf * (1 - sum_i x_i)^ahl
+c-------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208,npbmax=1000,npnmax=900,npmax=900,legmax=900)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr2/  scm,wp0,wm0
+      common /qgarr6/  pi,bm,amws
+      common /qgarr9/  iwp(iapmax),iwt(iapmax),lqa(iapmax),lqb(iapmax)
+     *,iprcn(iapmax),itgcn(iapmax),ias(npbmax),ibs(npbmax),nqs(npbmax)
+     *,npompr(npbmax),npomtg(npbmax),npomin(npbmax),nnpr(npmax,npbmax)
+     *,nntg(npmax,npbmax),ilpr(legmax,npbmax),iltg(legmax,npbmax)
+     *,lnpr(legmax,npbmax),lntg(legmax,npbmax)
+     *,nbpi(npnmax,iapmax),nbti(npnmax,iapmax),idnpi(npnmax,iapmax)
+     *,idnti(npnmax,iapmax),nppi(npnmax,iapmax),npti(npnmax,iapmax)
+     *,nlpi(npnmax,iapmax),nlti(npnmax,iapmax)
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr23/ bbpom(npbmax),vvxpom(npbmax)
+     *,bpompr(npnmax,iapmax),bpomtg(npnmax,iapmax)
+     *,vvxpr(npnmax,iapmax),vvxtg(npnmax,iapmax)
+     *,xpompr(npnmax,iapmax),xpomtg(npnmax,iapmax)
+     *,xpopin(npmax,npbmax),xpomin(npmax,npbmax),vvxin(npmax,npbmax)
+     *,bpomin(npmax,npbmax)
+      common /qgarr40/ xppr(npnmax,iapmax),xmtg(npnmax,iapmax)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.3)write (moniou,201)nbpom
+
+      delf=dels
+      qgweix=0.d0
+      do npb=1,nbpom                              !loop over collisions
+       ip=ias(npb)                                !proj. index
+       it=ibs(npb)                                !targ. index
+       icdp=iddp(ip)                              !proj. diffr. eigenstate
+       icdt=iddt(it)                              !targ. diffr. eigenstate
+       bbp=bbpom(npb)                             !b^2 between proj. and targ.
+       vvx=vvxpom(npb)                            !nuclear screening factor
+       if(nqs(npb).ne.0)then
+        do n=1,nqs(npb)                           !loop over single Pomerons
+         lnp=nnpr(n,npb)                          !proj. constituent index
+         lnt=nntg(n,npb)                          !targ. constituent index
+         xpp=xppr(lnp,ip)                         !LC+ for the Pomeron
+         xpm=xmtg(lnt,it)                         !LC- for the Pomeron
+         qgweix=qgweix+dlog(qgpomc(scm*xpp*xpm,xpp,xpm,bbp,vvx
+     *   ,icdp,icdt,icz,1)/(xpp*xpm)**delf)       !add single Pomeron contrib.
+        enddo
+       endif
+       if(npompr(npb).ne.0)then
+        do l=1,npompr(npb)                         !loop over proj. leg Pomerons
+         ipp=ilpr(l,npb)                          !proj. index
+         lnp=lnpr(l,npb)                          !proj. constituent index
+         xpp=xppr(lnp,ipp)                        !LC+ for the Pomeron
+         xpomr=1.d0/xpompr(lnp,ipp)/scm           !LC+ for the 3P vertex
+         vvxp=vvxpr(lnp,ipp)                      !screening factor
+         bbpr=bpompr(lnp,ipp)                          !b^2 for the Pomeron
+         qgweix=qgweix+dlog(qglegc(xpp/xpomr,xpp,bbpr,vvxp
+     *   ,iddp(ipp),icz,9)/xpp**delf)             !add leg Pomeron contrib.
+        enddo
+       endif
+       if(npomtg(npb).ne.0)then
+        do l=1,npomtg(npb)                        !loop over targ. leg Pomerons
+         itt=iltg(l,npb)                          !targ. index
+         lnt=lntg(l,npb)                          !targ. constituent index
+         xpm=xmtg(lnt,itt)                        !LC- for the Pomeron
+         xpomr=xpomtg(lnt,itt)                    !LC+ for the 3P vertex
+         vvxt=vvxtg(lnt,itt)                      !screening factor
+         bbtg=bpomtg(lnt,itt)                          !b^2 for the Pomeron
+         qgweix=qgweix+dlog(qglegc(xpomr*scm*xpm,xpm,bbtg,vvxt
+     *   ,iddt(itt),2,9)/xpm**delf)               !add leg Pomeron contrib.
+        enddo
+       endif
+      enddo
+      if(debug.ge.4)write (moniou,202)qgweix
+
+201   format(2x,'qgweix - log-weight of x-configuration,'
+     *,' N of collisions - ',i4)
+202   format(2x,'qgweix=',e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qghot(wpp,wpm,b,vvx,nva,nvb,izp,izt,icdp,icdt,icz,iqq
+     *,jpt)
+c---------------------------------------------------------------------------
+c qghot - semi-hard process
+c wpp,wpm   - LC momenta for the constituent partons,
+c b         - impact parameter for the semi-hard Pomeron,
+c izp, izt  - types of proj. and targ. remnants,
+c icdp,icdt - proj. and targ.  diffractive eigenstates,
+c iqq - type of the semi-hard process: 0 - gg, 1 - q_vg, 2 - gq_v, 3 - q_vq_v
+c jpt=0 - single Pomeron,
+c jpt=1 - proj. leg Pomeron,
+c jpt=2 - targ. leg Pomeron
+c---------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      character*2 tyq
+      parameter(njmax=50000)
+      dimension ept(4),ep3(4),ey(3),ebal(4),
+     *qmin(2),wp(2),iqc(2),iqp(2),nqc(2),ncc(2,2),
+     *qv1(30,50),zv1(30,50),qm1(30,50),iqv1(30,50),
+     *ldau1(30,49),lpar1(30,50),
+     *qv2(30,50),zv2(30,50),qm2(30,50),iqv2(30,50),
+     *ldau2(30,49),lpar2(30,50)
+      parameter(iapmax=208,npbmax=1000,npnmax=900,npmax=900,legmax=900)
+      common /qgarr2/  scm,wp0,wm0
+      common /qgarr6/  pi,bm,amws
+      common /qgarr8/  wwm,be(4),dc(5),deta,almpt,ptdif,ptndi
+      common /qgarr10/ am(7),ammu
+      common /qgarr11/ b10
+      common /qgarr12/ nsp
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr26/ factk,fqscal
+      common /qgarr37/ eqj(4,njmax),iqj(njmax),ncj(2,njmax),nj
+      common /qgarr42/ tyq(16)
+      common /qgarr43/ moniou
+      common /qgarr51/ epsxmn
+      common /qgdebug/ debug
+      external qgran
+
+      if(debug.ge.1)write (moniou,201)iqq,wpp,wpm,izp,izt,icdp,icdt
+     *,icz,jpt,nj
+
+      wwgg=0.d0
+      wwqg=0.d0
+      wwgq=0.d0
+      wwqq=0.d0
+      wpi=0.d0
+      wmi=0.d0
+      sjqg=0.d0
+      sjqq=0.d0
+      sea1=0.d0
+      sea2=0.d0
+      glu1=0.d0
+      glu2=0.d0
+      nj0=nj                       !store number of final partons
+      nsp0=nsp                     !store number of final particles
+
+1     sy=wpp*wpm  !energy squared for semi-hard inter. (including preevolution)
+      nj=nj0
+      nsp=nsp0
+      s2min=4.d0*fqscal*qt0       !threshold energy
+      if(sy.lt.s2min)stop'qghot: sy<s2min!!!'
+
+      if(iqq.eq.3)then             !q_vq_v-ladder
+       wpi=wpp                     !LC+ for the hard interaction
+       wmi=wpm                     !LC- for the hard interaction
+      else
+
+c-------------------------------------------------
+c normalization of acceptance
+       xmin=s2min/sy
+       iq=(iqq+1)/2+1              !auxilliary type of parton (1 - g, 2 - q(q~))
+       sj=qgjit(qt0,qt0,sy,1,iq)   !inclusive parton-parton cross-sections
+       if(iqq.eq.0)then
+        gb0=-dlog(xmin)*(1.d0-dsqrt(xmin))**(2.d0*betp)*sj
+       else
+        gb0=(1.d0-xmin)**betp*sj
+       endif
+       if(jpt.eq.0)then            !single Pomeron
+        if(iqq.eq.0)then
+         rp0=(rq(icdp,icz)+rq(icdt,2)+alfp*dlog(scm/s2min))
+     *   *4.d0*.0389d0
+         gb0=gb0/(rq(icdp,icz)+rq(icdt,2)+alfp*dlog(scm/sy))
+     *   *exp(-b*b/rp0)
+        elseif(iqq.eq.1)then
+         rp0=(rq(icdp,icz)+rq(icdt,2)+alfp*dlog(wpp*wm0/s2min))
+     *   *4.d0*.0389d0
+         gb0=gb0/(rq(icdp,icz)+rq(icdt,2)+alfp*dlog(wm0/wpm))
+     *   *exp(-b*b/rp0)
+        elseif(iqq.eq.2)then
+         rp0=(rq(icdp,icz)+rq(icdt,2)+alfp*dlog(wpm*wp0/s2min))
+     *   *4.d0*.0389d0
+         gb0=gb0/(rq(icdp,icz)+rq(icdt,2)+alfp*dlog(wp0/wpp))
+     *   *exp(-b*b/rp0)
+        endif
+       elseif(jpt.eq.1)then        !proj. leg Pomeron
+        if(iqq.eq.0)then
+         rp0=(rq(icdp,icz)+alfp*dlog(wp0*wpm/s2min))*4.d0*.0389d0
+         gb0=gb0/(rq(icdp,icz)+alfp*dlog(wp0/wpp))*exp(-b*b/rp0)
+        elseif(iqq.eq.1)then
+         rp0=(rq(icdp,icz)+alfp*dlog(sy/s2min))*4.d0*.0389d0
+         gb0=gb0/rq(icdp,icz)*exp(-b*b/rp0)
+        endif
+       elseif(jpt.eq.2)then        !targ. leg Pomeron
+        if(iqq.eq.0)then
+         rp0=(rq(icdt,2)+alfp*dlog(wm0*wpp/s2min))*4.d0*.0389d0
+         gb0=gb0/(rq(icdt,2)+alfp*dlog(wm0/wpm))*exp(-b*b/rp0)
+        elseif(iqq.eq.2)then
+         rp0=(rq(icdt,2)+alfp*dlog(sy/s2min))*4.d0*.0389d0
+         gb0=gb0/rq(icdt,2)*exp(-b*b/rp0)
+        endif
+       endif
+
+c-------------------------------------------------
+c sharing of LC momenta between soft preevolution and hard ladder
+2      zpm=(1.d0-qgran(b10)*(1.d0-xmin**(delh-dels)))
+     * **(1.d0/(delh-dels))
+       sjqq=qgjit(qt0,qt0,zpm*sy,2,2)  !inclusive qq cross-section
+       sjqg=qgjit(qt0,qt0,zpm*sy,1,2)  !inclusive qg cross-section
+       sjgg=qgjit(qt0,qt0,zpm*sy,1,1)  !inclusive gg cross-section
+
+       if(iqq.eq.0)then              !gg-ladder
+        xp=zpm**qgran(b10)           !LC+ momentum share
+        xm=zpm/xp                    !LC- momentum share
+        wpi=wpp*xp                   !LC+ for the hard interaction
+        wmi=wpm*xm                   !LC- for the hard interaction
+        if(jpt.eq.0)then             !single Pomeron
+         rp1=(rq(icdp,icz)+alfp*dlog(wp0/wpi))*4.d0*.0389d0
+         rp2=(rq(icdt,2)+alfp*dlog(wm0/wmi))*4.d0*.0389d0
+         rp=rp1*rp2/(rp1+rp2)
+         z=qgran(b10)
+         phi=pi*qgran(b10)
+         b0=dsqrt(-rp*dlog(z))
+         bb1=(b*rp1/(rp1+rp2)+b0*cos(phi))**2+(b0*sin(phi))**2
+         bb2=(b*rp2/(rp1+rp2)-b0*cos(phi))**2+(b0*sin(phi))**2
+
+         xpomr=wpi/wp0
+         if(xpomr*sgap.ge.1.d0.or.xpomr*scm.le.sgap)then
+          vvx1=0.d0
+         else
+          v1pnu0=qgfani(1.d0/xpomr,bb1,vvx,0.d0,0.d0,icdp,icz,1)
+          v1tnu0=qgfani(xpomr*scm,bb2,vvx,0.d0,0.d0,icdt,2,1)
+          nn=0
+21        nn=nn+1
+          vvxt=1.d0-exp(-v1pnu0)*(1.d0-vvx)
+          vvxp=1.d0-exp(-v1tnu0)*(1.d0-vvx)
+          v1pnu=qgfani(1.d0/xpomr,bb1,vvxp,0.d0,0.d0,icdp,icz,1)
+          v1tnu=qgfani(xpomr*scm,bb2,vvxt,0.d0,0.d0,icdt,2,1)
+          if((abs(v1pnu0-v1pnu).gt.1.d-1.or.abs(v1tnu0-v1tnu).gt.1.d-1)
+     *    .and.nn.lt.100)then
+           v1pnu0=v1pnu
+           v1tnu0=v1tnu
+           goto 21
+          endif
+          vvx1=1.d0-exp(-v1tnu)*(1.d0-vvx)
+         endif
+
+         xpomr=wm0/wmi/scm
+         if(xpomr*sgap.ge.1.d0.or.xpomr*scm.le.sgap)then
+          vvx2=0.d0
+         else
+          v1pnu0=qgfani(1.d0/xpomr,bb1,vvx,0.d0,0.d0,icdp,icz,1)
+          v1tnu0=qgfani(xpomr*scm,bb2,vvx,0.d0,0.d0,icdt,2,1)
+          nn=0
+22        nn=nn+1
+          vvxt=1.d0-exp(-v1pnu0)*(1.d0-vvx)
+          vvxp=1.d0-exp(-v1tnu0)*(1.d0-vvx)
+          v1pnu=qgfani(1.d0/xpomr,bb1,vvxp,0.d0,0.d0,icdp,icz,1)
+          v1tnu=qgfani(xpomr*scm,bb2,vvxt,0.d0,0.d0,icdt,2,1)
+          if((abs(v1pnu0-v1pnu).gt.1.d-1.or.abs(v1tnu0-v1tnu).gt.1.d-1)
+     *    .and.nn.lt.100)then
+           v1pnu0=v1pnu
+           v1tnu0=v1tnu
+           goto 22
+          endif
+          vvx2=1.d0-exp(-v1pnu)*(1.d0-vvx)
+         endif
+
+         glu1=qglegc(1.d0/xp,wpp/wp0,bb1,vvx1,icdp,icz,12) !upper gluon PDF
+         sea1=qglegc(1.d0/xp,wpp/wp0,bb1,vvx1,icdp,icz,13) !upper quark PDF
+         glu2=qglegc(1.d0/xm,wpm/wm0,bb2,vvx2,icdt,2,12)   !lower gluon PDF
+         sea2=qglegc(1.d0/xm,wpm/wm0,bb2,vvx2,icdt,2,13)   !lower quark PDF
+        elseif(jpt.eq.1)then                         !proj. leg Pomeron
+         rp1=(rq(icdp,icz)+alfp*dlog(wp0/wpi))*4.d0*.0389d0
+         rp2=-alfp*dlog(xm)*4.d0*.0389d0
+         rp=rp1*rp2/(rp1+rp2)
+         z=qgran(b10)
+         phi=pi*qgran(b10)
+         b0=dsqrt(-rp*dlog(z))
+         bb1=(b*rp1/(rp1+rp2)+b0*cos(phi))**2+(b0*sin(phi))**2
+         bb2=(b*rp2/(rp1+rp2)-b0*cos(phi))**2+(b0*sin(phi))**2
+
+         glu1=qglegc(1.d0/xp,wpp/wp0,bb1,vvx,icdp,icz,12) !upper gluon PDF
+         sea1=qglegc(1.d0/xp,wpp/wp0,bb1,vvx,icdp,icz,13) !upper quark PDF
+         glu2=qgppdi(xm,0)
+         sea2=qgppdi(xm,1)
+        elseif(jpt.eq.2)then                         !proj. leg Pomeron
+         rp1=(rq(icdt,2)+alfp*dlog(wm0/wmi))*4.d0*.0389d0
+         rp2=-alfp*dlog(xp)*4.d0*.0389d0
+         rp=rp1*rp2/(rp1+rp2)
+         z=qgran(b10)
+         phi=pi*qgran(b10)
+         b0=dsqrt(-rp*dlog(z))
+         bb1=(b*rp1/(rp1+rp2)+b0*cos(phi))**2+(b0*sin(phi))**2
+         bb2=(b*rp2/(rp1+rp2)-b0*cos(phi))**2+(b0*sin(phi))**2
+
+         glu1=qglegc(1.d0/xm,wpm/wm0,bb1,vvx,icdt,2,12) !upper gluon PDF
+         sea1=qglegc(1.d0/xm,wpm/wm0,bb1,vvx,icdt,2,13) !upper quark PDF
+         glu2=qgppdi(xp,0)
+         sea2=qgppdi(xp,1)
+        endif
+        wwgg=glu1*glu2*sjgg
+        wwqg=sea1*glu2*sjqg
+        wwgq=glu1*sea2*sjqg
+        wwqq=sea1*sea2*sjqq
+        gbyj=-dlog(zpm)*(wwgg+wwqg+wwgq+wwqq)
+        if(jpt.eq.0)then
+         rh=rq(icdp,icz)+rq(icdt,2)-alfp*dlog(zpm*sy/scm)
+        elseif(jpt.eq.1)then
+         rh=rq(icdp,icz)-alfp*dlog(wpp/wp0*zpm)
+        elseif(jpt.eq.2)then
+         rh=rq(icdt,2)-alfp*dlog(wpm/wm0*zpm)
+        else
+         rh=0.d0
+         stop 'Should not happen in qghot'
+        endif
+        gbyj=gbyj/rh*exp(-b*b/(4.d0*.0389d0*rh))
+
+       else                          !q_vg-(gq_v-)ladder
+        if(iqq.eq.1)then             !q_vg-ladder
+         wpi=wpp
+         wmi=wpm*zpm
+         xm=zpm
+         if(jpt.eq.0)then            !single Pomeron
+          rp1=rq(icdp,icz)*4.d0*.0389d0
+          rp2=(rq(icdt,2)+alfp*dlog(wm0/wmi))*4.d0*.0389d0
+          rp=rp1*rp2/(rp1+rp2)
+          z=qgran(b10)
+          phi=pi*qgran(b10)
+          b0=dsqrt(-rp*dlog(z))
+          bb1=(b*rp1/(rp1+rp2)+b0*cos(phi))**2+(b0*sin(phi))**2
+          bb2=(b*rp2/(rp1+rp2)-b0*cos(phi))**2+(b0*sin(phi))**2
+
+          xpomr=wm0/wmi/scm
+          if(xpomr*sgap.ge.1.d0.or.xpomr*scm.le.sgap)then
+           vvx2=0.d0
+          else
+           v1pnu0=qgfani(1.d0/xpomr,bb1,vvx,0.d0,0.d0,icdp,icz,1)
+           v1tnu0=qgfani(xpomr*scm,bb2,vvx,0.d0,0.d0,icdt,2,1)
+           nn=0
+23         nn=nn+1
+           vvxt=1.d0-exp(-v1pnu0)*(1.d0-vvx)
+           vvxp=1.d0-exp(-v1tnu0)*(1.d0-vvx)
+           v1pnu=qgfani(1.d0/xpomr,bb1,vvxp,0.d0,0.d0,icdp,icz,1)
+           v1tnu=qgfani(xpomr*scm,bb2,vvxt,0.d0,0.d0,icdt,2,1)
+           if((abs(v1pnu0-v1pnu).gt.1.d-1.or.abs(v1tnu0-v1tnu).gt.1.d-1)
+     *     .and.nn.lt.100)then
+            v1pnu0=v1pnu
+            v1tnu0=v1tnu
+            goto 23
+           endif
+           vvx2=1.d0-exp(-v1pnu)*(1.d0-vvx)
+          endif
+
+          glu2=qglegc(1.d0/xm,wpm/wm0,bb2,vvx2,icdt,2,12) !upper gluon PDF
+          sea2=qglegc(1.d0/xm,wpm/wm0,bb2,vvx2,icdt,2,13) !upper quark PDF
+          wwqg=glu2*sjqg
+          wwqq=sea2*sjqq
+         else                        !leg Pomeron
+          wwqg=qgppdi(xm,0)*sjqg
+          wwqq=qgppdi(xm,1)*sjqq
+         endif
+        elseif(iqq.eq.2)then         !gq_v-ladder
+         wpi=wpp*zpm
+         wmi=wpm
+         xp=zpm
+         if(jpt.eq.0)then            !single Pomeron
+          rp1=(rq(icdp,icz)+alfp*dlog(wp0/wpi))*4.d0*.0389d0
+          rp2=rq(icdt,2)*4.d0*.0389d0
+          rp=rp1*rp2/(rp1+rp2)
+          z=qgran(b10)
+          phi=pi*qgran(b10)
+          b0=dsqrt(-rp*dlog(z))
+          bb1=(b*rp1/(rp1+rp2)+b0*cos(phi))**2+(b0*sin(phi))**2
+          bb2=(b*rp2/(rp1+rp2)-b0*cos(phi))**2+(b0*sin(phi))**2
+
+          xpomr=wpi/wp0
+          if(xpomr*sgap.ge.1.d0.or.xpomr*scm.le.sgap)then
+           vvx1=0.d0
+          else
+           v1pnu0=qgfani(1.d0/xpomr,bb1,vvx,0.d0,0.d0,icdp,icz,1)
+           v1tnu0=qgfani(xpomr*scm,bb2,vvx,0.d0,0.d0,icdt,2,1)
+           nn=0
+24         nn=nn+1
+           vvxt=1.d0-exp(-v1pnu0)*(1.d0-vvx)
+           vvxp=1.d0-exp(-v1tnu0)*(1.d0-vvx)
+           v1pnu=qgfani(1.d0/xpomr,bb1,vvxp,0.d0,0.d0,icdp,icz,1)
+           v1tnu=qgfani(xpomr*scm,bb2,vvxt,0.d0,0.d0,icdt,2,1)
+           if((abs(v1pnu0-v1pnu).gt.1.d-1.or.abs(v1tnu0-v1tnu).gt.1.d-1)
+     *     .and.nn.lt.100)then
+            v1pnu0=v1pnu
+            v1tnu0=v1tnu
+            goto 24
+           endif
+           vvx1=1.d0-exp(-v1tnu)*(1.d0-vvx)
+          endif
+
+          glu1=qglegc(1.d0/xp,wpp/wp0,bb1,vvx1,icdp,icz,12) !upper gluon PDF
+          sea1=qglegc(1.d0/xp,wpp/wp0,bb1,vvx1,icdp,icz,13) !upper quark PDF
+          wwqg=glu1*sjqg
+          wwqq=sea1*sjqq
+         else                        !leg Pomeron
+          wwqg=qgppdi(xp,0)*sjqg
+          wwqq=qgppdi(xp,1)*sjqq
+         endif
+        endif
+        gbyj=wwqg+wwqq
+        if(jpt.eq.0)then
+         if(iqq.eq.1)then
+          rh=rq(icdp,icz)+rq(icdt,2)-alfp*dlog(wpm/wm0*zpm)
+         else
+          rh=rq(icdp,icz)+rq(icdt,2)-alfp*dlog(wpp/wp0*zpm)
+         endif
+        elseif(jpt.eq.1)then
+         rh=rq(icdp,icz)-alfp*dlog(zpm)
+        elseif(jpt.eq.2)then
+         rh=rq(icdt,2)-alfp*dlog(zpm)
+        else
+         rh=0.d0
+         stop 'Should not happen in qghot'
+        endif
+        gbyj=gbyj/rh*exp(-b*b/(4.d0*.0389d0*rh))
+       endif
+
+       gbyj=gbyj/gb0/zpm**delh
+       if(qgran(b10).gt.gbyj)goto 2
+      endif
+      if(debug.ge.2)write (moniou,202)wpi*wmi
+
+11    wpi1=wpi
+      wmi1=wmi
+      wpq=0.d0
+      wmq=0.d0
+      nj=nj0                     !initialization for the number of final partons
+      rrr=qgran(b10)
+      jqq=0                                  !gg-ladder
+      if(iqq.eq.1.or.iqq.eq.2)then
+       if(rrr.lt.wwqq/(wwqg+wwqq))jqq=1      !q_vq_s-laddder
+      elseif(iqq.eq.0)then
+       if(rrr.lt.wwqg/(wwgg+wwqg+wwgq+wwqq))then
+        jqq=1                                !q_sg-ladder
+       elseif(rrr.lt.(wwqg+wwgq)/(wwgg+wwqg+wwgq+wwqq))then
+        jqq=2                                !gq_s-ladder
+       elseif(rrr.lt.(wwqg+wwgq+wwqq)/(wwgg+wwqg+wwgq+wwqq))then
+        jqq=3                                !q_sq_s-ladder
+       endif
+      endif
+
+c-------------------------------------------------
+c parton types for the ladder legs and for the leading jets
+c iqc(1) - flavor for the upper quark (0 in case of gluon),
+c iqc(2) - the same for the lower one
+      if(iqq.ne.0.and.iqq.ne.2)then          !q_v from the proj.
+       call qgvdef(izp,ic1,ic2,icz)          !leading state flavor
+       iqc(1)=ic1                            !upper leg parton
+       nj=nj+1
+       if(nj.gt.njmax)stop'increase njmax!!!'
+       nva=nj
+       iqj(nj)=ic2                           !leading jet parton
+       ncc(1,1)=nj                           !color connection with leading jet
+       ncc(2,1)=0
+      else                                   !g(q_s) from the proj.
+       nj=nj+1
+       if(nj.gt.njmax)stop'increase njmax!!!'
+       if(qgran(b10).lt.dc(2))then
+        iqj(nj)=-4
+       else
+        iqj(nj)=-int(2.d0*qgran(b10)+1.d0)
+       endif
+       iqj(nj+1)=-iqj(nj)
+       wp1=wpp-wpi
+       wp2=wp1*qgran(b10)
+       wp1=wp1-wp2
+       eqj(1,nj)=.5d0*wp1
+       eqj(2,nj)=eqj(1,nj)
+       eqj(3,nj)=0.d0
+       eqj(4,nj)=0.d0
+       eqj(1,nj+1)=.5d0*wp2
+       eqj(2,nj+1)=eqj(1,nj+1)
+       eqj(3,nj+1)=0.d0
+       eqj(4,nj+1)=0.d0
+       if(jqq.eq.0.or.iqq.eq.0.and.jqq.eq.2)then
+        iqc(1)=0
+        ncc(1,1)=nj
+        ncc(2,1)=nj+1
+        nj=nj+1
+        if(nj.gt.njmax)stop'increase njmax!!!'
+       else
+        if(qgran(b10).lt..3333d0)then
+         iqc(1)=3*(2.d0*int(.5d0+qgran(b10))-1.d0)
+        else
+         iqc(1)=int(2.d0*qgran(b10)+1.d0)
+     *   *(2.d0*int(.5d0+qgran(b10))-1.d0)
+        endif
+12      zg=xp+qgran(b10)*(1.d0-xp)           !gluon splitting into qq~
+        if(qgran(b10).gt.zg**dels*((1.d0-xp/zg)/ (1.d0-xp))**betp)
+     *  goto 12
+        xg=xp/zg
+        wpq0=wpp*(xg-xp)
+        wmq=1.d0/wpq0
+        wmi1=wmi1-wmq
+        if(wmi1*wpi1.le.s2min)goto 11
+        nj=nj+2
+        if(nj.gt.njmax)stop'increase njmax!!!'
+        iqj(nj)=-iqc(1)
+        if(iabs(iqc(1)).eq.3)iqj(nj)=iqj(nj)*4/3
+        eqj(1,nj)=.5d0*wmq
+        eqj(2,nj)=-.5d0*wmq
+        eqj(3,nj)=0.d0
+        eqj(4,nj)=0.d0
+        if(iqc(1).gt.0)then
+         ncj(1,nj)=nj-1
+         ncj(1,nj-1)=nj
+         ncj(2,nj)=0
+         ncj(2,nj-1)=0
+         ncc(1,1)=nj-2
+         ncc(2,1)=0
+        else
+         ncj(1,nj)=nj-2
+         ncj(1,nj-2)=nj
+         ncj(2,nj)=0
+         ncj(2,nj-2)=0
+         ncc(1,1)=nj-1
+         ncc(2,1)=0
+        endif
+       endif
+      endif
+
+      if((iqq-2)*(iqq-3)*(iqq-4).eq.0)then     !q_v from the targ.
+       call qgvdef(izt,ic1,ic2,2)              !leading state flavor
+       iqc(2)=ic1                              !lower leg parton
+       nj=nj+1
+       if(nj.gt.njmax)stop'increase njmax!!!'
+       nvb=nj
+       iqj(nj)=ic2
+       ncc(1,2)=nj
+       ncc(2,2)=0
+      else
+       nj=nj+1
+       if(nj.gt.njmax)stop'increase njmax!!!'
+       if(qgran(b10).lt.dc(2))then
+        iqj(nj)=-4
+       else
+        iqj(nj)=-int(2.d0*qgran(b10)+1.d0)
+       endif
+       iqj(nj+1)=-iqj(nj)
+       wm1=wpm-wmi
+       wm2=wm1*qgran(b10)
+       wm1=wm1-wm2
+       eqj(1,nj)=.5d0*wm1
+       eqj(2,nj)=-eqj(1,nj)
+       eqj(3,nj)=0.d0
+       eqj(4,nj)=0.d0
+       eqj(1,nj+1)=.5d0*wm2
+       eqj(2,nj+1)=-eqj(1,nj+1)
+       eqj(3,nj+1)=0.d0
+       eqj(4,nj+1)=0.d0
+       if(jqq.eq.0.or.iqq.eq.0.and.jqq.eq.1)then
+        iqc(2)=0
+        ncc(1,2)=nj
+        ncc(2,2)=nj+1
+        nj=nj+1
+        if(nj.gt.njmax)stop'increase njmax!!!'
+       else
+        if(qgran(b10).lt..3333d0)then
+         iqc(2)=3*(2.d0*int(.5d0+qgran(b10))-1.d0)
+        else
+         iqc(2)=int(2.d0*qgran(b10)+1.d0)
+     *   *(2.d0*int(.5d0+qgran(b10))-1.d0)
+        endif
+14      zg=xm+qgran(b10)*(1.d0-xm)           !gluon splitting into qq~
+        if(qgran(b10).gt.zg**dels*((1.d0-xm/zg)/ (1.d0-xm))**betp)
+     *  goto 14
+        xg=xm/zg
+        wmq0=wpm*(xg-xm)
+        wpq=1.d0/wmq0
+        wpi1=wpi1-wpq
+        if(wmi1*wpi1.le.s2min)goto 11
+        nj=nj+2
+        if(nj.gt.njmax)stop'increase njmax!!!'
+        iqj(nj)=-iqc(2)
+        if(iabs(iqc(2)).eq.3)iqj(nj)=iqj(nj)*4/3
+        eqj(1,nj)=.5d0*wpq
+        eqj(2,nj)=.5d0*wpq
+        eqj(3,nj)=0.d0
+        eqj(4,nj)=0.d0
+        if(iqc(2).gt.0)then
+         ncj(1,nj)=nj-1
+         ncj(1,nj-1)=nj
+         ncj(2,nj)=0
+         ncj(2,nj-1)=0
+         ncc(1,2)=nj-2
+         ncc(2,2)=0
+        else
+         ncj(1,nj)=nj-2
+         ncj(1,nj-2)=nj
+         ncj(2,nj)=0
+         ncj(2,nj-2)=0
+         ncc(1,2)=nj-1
+         ncc(2,2)=0
+        endif
+       endif
+      endif
+
+      if(jqq.ne.0)then
+       if(iqq.ne.0.or.iqq.eq.0.and.jqq.eq.3)then
+        sjqq1=qgjit(qt0,qt0,wpi1*wmi1,2,2)
+        gbs=sjqq1/sjqq
+       else
+        sjqg1=qgjit(qt0,qt0,wpi1*wmi1,1,2)
+        gbs=sjqg1/sjqg
+       endif
+       if(qgran(b10).gt.gbs)goto 11
+      endif
+      wpi=wpi1
+      wmi=wmi1
+
+      ept(1)=.5d0*(wpi+wmi)      !ladder 4-momentum
+      ept(2)=.5d0*(wpi-wmi)
+      ept(3)=0.d0
+      ept(4)=0.d0
+      qmin(1)=qt0                !q^2 cutoff for the upper leg
+      qmin(2)=qt0                !q^2 cutoff for the downer leg
+      qminn=max(qmin(1),qmin(2)) !overall q^2 cutoff
+      si=qgnrm(ept)
+      jini=1
+      jj=int(1.5d0+qgran(b10)) !1st parton at upper (jj=1) or downer (jj=2) leg
+
+3     continue
+
+      aaa=qgnrm(ept)             !ladder mass squared
+      if(debug.ge.3)write (moniou,203)si,iqc,ept,aaa
+
+      pt2=ept(3)**2+ept(4)**2
+      pt=dsqrt(pt2)
+      ww=si+pt2
+
+      iqp(1)=min(1,iabs(iqc(1)))+1
+      iqp(2)=min(1,iabs(iqc(2)))+1
+      wp(1)=ept(1)+ept(2)                 !LC+ for the ladder
+      wp(2)=ept(1)-ept(2)                 !LC- for the ladder
+      s2min=4.d0*fqscal*qminn   !minimal energy squared for 2-parton production
+      if(jini.eq.1)then                   !general ladder
+       sj=qgjit(qmin(jj),qmin(3-jj),si,iqp(jj),iqp(3-jj))   !total ladder contribution
+       sj1=qgjit1(qmin(3-jj),qmin(jj),si,iqp(3-jj),iqp(jj)) !one-way ordered
+       sjb=qgbit(qmin(1),qmin(2),si,iqp(1),iqp(2))          !born contribution
+       aks=qgran(b10)
+       if(aks.lt.sjb/sj)then
+        goto 6      !born process sampled
+       elseif(aks.lt.sj1/sj)then       !change to one-way ordered ladder
+        jj=3-jj
+        sj=sj1
+        jini=0
+       endif
+      else                                !one-way ordered ladder
+       sj=qgjit1(qmin(jj),qmin(3-jj),si,iqp(jj),iqp(3-jj)) !one-way ordered
+       sjb=qgbit(qmin(1),qmin(2),si,iqp(1),iqp(2))         !born contribution
+       if(qgran(b10).lt.sjb/sj)goto 6      !born process sampled
+      endif
+      wwmin=(s2min+qmin(jj)+pt2-2.d0*pt*dsqrt(qmin(jj)*epsxmn))
+     */(1.d0-epsxmn)           !minimal energy squared for 3-parton production
+
+      if(debug.ge.3)write (moniou,204)s2min,wwmin,sj,sjb
+
+      if(ww.lt.1.1d0*wwmin)goto 6         !energy too low -> born process
+
+      xxx=pt*dsqrt(qmin(jj))/ww
+      xmin=(s2min+qmin(jj)+pt2)/ww
+      xmin=xmin-2.d0*xxx*(xxx+dsqrt(xxx**2+1.d0-xmin))
+
+      xmax=1.d0-epsxmn
+      if(debug.ge.3)write (moniou,205)xmin,xmax
+
+      qqmax=(pt*dsqrt(epsxmn)+dsqrt(max(0.d0,pt2*epsxmn
+     *+(1.d0+4.d0*fqscal)*(xmax*ww-pt2))))/(1.d0+4.d0*fqscal)
+      qqmin=qmin(jj)        !minimal parton virtuality in the current rung
+      if(debug.ge.3)write (moniou,206)qqmin,qqmax
+
+      qm0=qqmin
+      xm0=xmax
+      s2max=xm0*ww
+
+      if(jini.eq.1)then
+       sj0=qgjit(qm0,qmin(3-jj),s2max,1,iqp(3-jj))*qgfap(xm0,iqp(jj),1)
+     * +qgjit(qm0,qmin(3-jj),s2max,2,iqp(3-jj))*qgfap(xm0,iqp(jj),2)
+      else
+       sj0=qgjit1(qm0,qmin(3-jj),s2max,1,iqp(3-jj))
+     * *qgfap(xm0,iqp(jj),1)
+     * +qgjit1(qm0,qmin(3-jj),s2max,2,iqp(3-jj))*qgfap(xm0,iqp(jj),2)
+      endif
+
+      gb0=sj0*qm0*qgalf(qm0/alm)*qgsudx(qm0,iqp(jj)) *4.5d0  !normal. of accept.
+      if(xm0.le..5d0)then
+       gb0=gb0*xm0**(1.d0-delh)
+      else
+       gb0=gb0*(1.d0-xm0)*2.d0**delh
+      endif
+      if(debug.ge.3)write (moniou,208)xm0,xmin,xmax,gb0
+
+      xmin2=max(.5d0,xmin)
+      xmin1=xmin**delh
+      xmax1=min(xmax,.5d0)**delh
+      if(xmin.ge..5d0)then                             !choose proposal function
+       djl=1.d0
+      elseif(xmax.lt..5d0)then
+       djl=0.d0
+      else
+       djl=1.d0/(1.d0+((2.d0*xmin)**delh-1.d0)/delh
+     * /dlog(2.d0*(1.d0-xmax)))
+      endif
+
+c-------------------------------------------------
+c propose x, q^2
+4     continue
+      if(qgran(b10).gt.djl)then
+       x=(xmin1+qgran(b10)*(xmax1-xmin1))**(1.d0/delh) !parton LC share
+      else
+       x=1.d0-(1.d0-xmin2)*((1.d0-xmax)/(1.d0-xmin2))**qgran(b10)
+      endif
+      qq=qqmin/(1.d0+qgran(b10)*(qqmin/qqmax-1.d0))    !parton virtuality
+      qt2=qq*(1.d0-x)                                  !parton p_t^2
+      if(debug.ge.4)write (moniou,209)qq,qqmin,qqmax,x,qt2
+
+      if(qq.gt.qminn)then                  !update virtuality cutoff
+       qmin2=qq
+      else
+       qmin2=qminn
+      endif
+      qt=dsqrt(qt2)
+      call qgcs(c,s)
+      ep3(3)=qt*c                          !final parton p_x, p_y
+      ep3(4)=qt*s
+      pt2new=(ept(3)-ep3(3))**2+(ept(4)-ep3(4))**2!p_t^2 for the remained ladder
+      s2min2=max(s2min,4.d0*fqscal*qmin2)  !new ladder kinematic limit
+      s2=x*ww-qt2*x/(1.d0-x)-pt2new        !mass squared for the remained ladder
+      if(s2.lt.s2min2)goto 4           !ladder mass below threshold -> rejection
+
+      if(jini.eq.1)then                    !weights for g- and q-legs
+       sj1=qgjit(qq,qmin(3-jj),s2,1,iqp(3-jj))*qgfap(x,iqp(jj),1)
+       sj2=qgjit(qq,qmin(3-jj),s2,2,iqp(3-jj))*qgfap(x,iqp(jj),2)
+      else
+       sj1=qgjit1(qq,qmin(3-jj),s2,1,iqp(3-jj))*qgfap(x,iqp(jj),1)
+       sj2=qgjit1(qq,qmin(3-jj),s2,2,iqp(3-jj))*qgfap(x,iqp(jj),2)
+      endif
+      gb7=(sj1+sj2)*qgalf(qq/alm)*qq*qgsudx(qq,iqp(jj))/gb0  /2.d0
+                               !acceptance probability for x and q**2 simulation
+      if(x.le..5d0)then
+       gb7=gb7*x**(1.d0-delh)
+      else
+       gb7=gb7*(1.d0-x)*2.d0**delh
+      endif
+      if(debug.ge.4)write (moniou,210)gb7,s2,sj1,sj2,jj,jini
+      if(qgran(b10).gt.gb7)goto 4          !rejection
+
+c-------------------------------------------------
+c define color flow for the emitted jet; perform final state emission
+      nqc(2)=0
+      if(qgran(b10).lt.sj1/(sj1+sj2))then         !new gluon-leg ladder
+       if(iqc(jj).eq.0)then                       !g -> gg
+        jt=1
+        jq=int(1.5d0+qgran(b10))
+        nqc(1)=ncc(jq,jj)                         !color connection for the jet
+        nqc(2)=0
+       else                                       !q -> qg
+        jt=2
+        if(iqc(jj).gt.0)then                      !orientation of color flow
+         jq=1
+        else
+         jq=2
+        endif
+        nqc(1)=0
+        ncc(jq,jj)=ncc(1,jj)                      !color connection for the jet
+       endif
+       iq1=iqc(jj)                                !jet flavor (type)
+       iqc(jj)=0                                  !new ladder leg flavor (type)
+
+      else                                        !new quark-leg ladder
+       if(iqc(jj).ne.0)then                       !q -> gq
+        iq1=0
+        jt=3
+        if(iqc(jj).gt.0)then                      !orientation of color flow
+         jq=1
+        else
+         jq=2
+        endif
+        nqc(1)=ncc(1,jj)                          !color connection for the jet
+        nqc(2)=0
+
+       else                                       !g -> qq~
+        jq=int(1.5d0+qgran(b10))                  !orientation of color flow
+        iq1=int(3.d0*qgran(b10)+1.d0)*(3-2*jq)    !jet flavor (type)
+        iqc(jj)=-iq1                              !new ladder leg flavor (type)
+        jt=4
+        nqc(1)=ncc(jq,jj)                         !color connections for the jet
+        ncc(1,jj)=ncc(3-jq,jj)
+       endif
+      endif
+      if(debug.ge.3)write (moniou,211)jt
+
+      call qgcjet(qt2,iq1,qv1,zv1,qm1,iqv1,ldau1,lpar1,jq) !final state emission
+      si=x*ww-(qt2+qm1(1,1))*x/(1.d0-x)-pt2new  !mass squared for the new ladder
+      if(si.gt.s2min2)then
+       iq=min(1,iabs(iqc(jj)))+1
+       if(jini.eq.1)then
+        gb=qgjit(qq,qmin(3-jj),si,iq,iqp(3-jj))
+     *  /qgjit(qq,qmin(3-jj),s2,iq,iqp(3-jj))
+       else
+        gb=qgjit1(qq,qmin(3-jj),si,iq,iqp(3-jj))
+     *  /qgjit1(qq,qmin(3-jj),s2,iq,iqp(3-jj))
+       endif
+       if(qgran(b10).gt.gb)goto 1        !jet mass correction for the acceptance
+      else                                        !below threshold -> rejection
+       goto 1
+      endif
+
+      wp3=wp(jj)*(1.d0-x)
+      wm3=(qt2+qm1(1,1))/wp3
+      ep3(1)=.5d0*(wp3+wm3)                       !jet 4-momentum
+      ep3(2)=.5d0*(wp3-wm3)*(3-2*jj)
+      call qgrec(ep3,nqc,qv1,zv1,qm1,iqv1,ldau1,lpar1,jq)
+                               !reconstruction of 4-momenta of all final partons
+c-------------------------------------------------
+c define color connections for the new ladder
+      if(jt.eq.1)then
+       if(ncc(1,jj).eq.0.and.ncc(2,jj).eq.0)ncc(3-jq,jj)=nqc(1)
+       ncc(jq,jj)=nqc(2)
+      elseif(jt.eq.2)then
+       ncc(3-jq,jj)=nqc(1)
+      elseif(jt.eq.3)then
+       ncc(1,jj)=nqc(2)
+      elseif(jt.eq.4.and.ncc(1,jj).eq.0.and.ncc(2,jj).eq.0)then
+       ncc(1,jj)=nqc(1)
+      endif
+
+      if(iabs(iq1).eq.3)then
+       iqqq=8+iq1/3*4
+      else
+       iqqq=8+iq1
+      endif
+      if(debug.ge.3)write (moniou,212)tyq(iqqq),qt2,ep3
+      do i=1,4
+       ept(i)=ept(i)-ep3(i)                       !new ladder 4-momentum
+      enddo
+      qmin(jj)=qq                                 !new virtuality cutoffs
+      qminn=qmin2
+      goto 3                                      !consider next parton emission
+
+c------------------------------------------------
+c born process - last parton pair production in the ladder
+6     continue
+      if(debug.ge.2)write (moniou,214)si,qminn,iqc
+      tmin=qminn*fqscal/(.5d0+dsqrt(max(0.d0,.25d0-qminn*fqscal/si)))
+      qtmin=tmin*(1.d0-tmin/si)
+      if(iqc(1).ne.0.or.iqc(2).ne.0)then
+       gb0=tmin**2*qgalf(qtmin/fqscal/alm)**2
+     * *qgfbor(si,tmin,iqc(1),iqc(2),1)    *1.1d0
+      else
+       gb0=.25d0*si**2*qgalf(qtmin/fqscal/alm)**2
+     * *qgfbor(si,.5d0*si,iqc(1),iqc(2),1)
+      endif
+      gb0=gb0*qgsudx(qtmin/fqscal,iqp(1))*qgsudx(qtmin/fqscal,iqp(2))
+                                                    !normalization of acceptance
+      if(debug.ge.3)write (moniou,215)gb0
+
+7     q2=tmin/(1.d0-qgran(b10)*(1.d0-2.d0*tmin/si))   !proposed q^2
+      z=q2/si                                         !parton LC momentum share
+      qt2=q2*(1.d0-z)                                 !parton p_t^2
+      if(qgran(b10).lt..5d0)then
+       jm=2
+       tq=si-q2
+      else
+       jm=1
+       tq=q2
+      endif
+      gb=q2**2*qgalf(qt2/fqscal/alm)**2*qgfbor(si,tq,iqc(1),iqc(2),1)
+     **qgsudx(qt2/fqscal,iqp(1))*qgsudx(qt2/fqscal,iqp(2))/gb0
+                                                      !acceptance probabilty
+      if(debug.ge.4)write (moniou,216)gb,q2,z,qt2
+      if(qgran(b10).gt.gb)goto 7                      !rejection
+
+c-------------------------------------------------
+c define color connections for the 1st emitted jet
+      nqc(2)=0
+      if(iqc(1).eq.0.and.iqc(2).eq.0)then             !gg-process
+       jq=int(1.5d0+qgran(b10))                       !orientation of color flow
+       nqc(1)=ncc(jq,jm)
+
+       if(qgran(b10).lt..5d0)then
+        jt=1                                          !gg -> gg
+        nqc(2)=0
+        njc1=ncc(3-jq,jm)                         !color connections for 1st jet
+        njc2=ncc(jq,3-jm)
+        if(ncc(1,1).eq.0.and.ncc(2,1).eq.0)then
+         if(jm.eq.1)nqc(1)=njc2
+        else
+         if(iqj(njc1).ne.0)then
+          ncj(1,njc1)=njc2
+         else
+          ncj(jq,njc1)=njc2
+         endif
+         if(iqj(njc2).ne.0)then
+          ncj(1,njc2)=njc1
+         else
+          ncj(3-jq,njc2)=njc1
+         endif
+        endif
+       else                                 !gg -> gg (inverse color connection)
+        jt=2
+        nqc(2)=ncc(3-jq,3-jm)
+       endif
+
+      elseif(iqc(1)*iqc(2).eq.0)then                  !qg -> qg
+       if(iqc(1)+iqc(2).gt.0)then                     !orientation of color flow
+        jq=1
+       else
+        jq=2
+       endif
+       if(qgran(b10).lt..5d0)then
+        if(iqc(jm).eq.0)then
+         jt=3
+         nqc(1)=ncc(jq,jm)
+         nqc(2)=0
+         njc1=ncc(3-jq,jm)
+         njc2=ncc(1,3-jm)
+         if(ncc(1,jm).eq.0.and.ncc(2,jm).eq.0)then
+          nqc(1)=njc2
+         else
+          if(iqj(njc1).ne.0)then
+           ncj(1,njc1)=njc2
+          else
+           ncj(jq,njc1)=njc2
+          endif
+          if(iqj(njc2).ne.0)then
+           ncj(1,njc2)=njc1
+          else
+           ncj(3-jq,njc2)=njc1
+          endif
+         endif
+        else
+         jt=4
+         nqc(1)=0
+         njc1=ncc(1,jm)
+         njc2=ncc(3-jq,3-jm)
+         if(njc2.ne.0)then
+          if(iqj(njc1).ne.0)then
+           ncj(1,njc1)=njc2
+          else
+           ncj(3-jq,njc1)=njc2
+          endif
+          if(iqj(njc2).ne.0)then
+           ncj(1,njc2)=njc1
+          else
+           ncj(jq,njc2)=njc1
+          endif
+         endif
+        endif
+       else
+        if(iqc(jm).eq.0)then
+         jt=5
+         nqc(2)=ncc(3-jq,jm)
+         nqc(1)=ncc(1,3-jm)
+        else
+         jt=6
+         nqc(1)=ncc(jq,3-jm)
+        endif
+       endif
+
+      elseif(iqc(1)*iqc(2).gt.0)then                  !qq (q~q~) -> qq (q~q~)
+       jt=7
+       if(iqc(1).gt.0)then
+        jq=1
+       else
+        jq=2
+       endif
+       nqc(1)=ncc(1,3-jm)
+      else                                            !qq~ -> qq~
+       jt=8
+       if(iqc(jm).gt.0)then
+        jq=1
+       else
+        jq=2
+       endif
+       nqc(1)=0
+       njc1=ncc(1,jm)
+       njc2=ncc(1,3-jm)
+       if(iqj(njc1).ne.0)then
+        ncj(1,njc1)=njc2
+       else
+        ncj(3-jq,njc1)=njc2
+       endif
+       if(iqj(njc2).ne.0)then
+        ncj(1,njc2)=njc1
+       else
+        ncj(jq,njc2)=njc1
+       endif
+      endif
+      if(jt.ne.8)then
+       jq2=jq
+      else
+       jq2=3-jq
+      endif
+      if(debug.ge.3)write (moniou,211)jt
+      call qgcjet(qt2,iqc(jm),qv1,zv1,qm1,iqv1,ldau1,lpar1,jq)!final state emis.
+      call qgcjet(qt2,iqc(3-jm),qv2,zv2,qm2,iqv2,ldau2,lpar2,jq2)
+      amt1=qt2+qm1(1,1)
+      amt2=qt2+qm2(1,1)
+      if(dsqrt(si).gt.dsqrt(amt1)+dsqrt(amt2))then
+       z=qgtwd(si,amt1,amt2)
+      else
+       if(debug.ge.4)write (moniou,217)dsqrt(si),dsqrt(amt1),dsqrt(amt2)
+       goto 1                                      !below threshold -> rejection
+      endif
+
+      call qgdeft(si,ept,ey)
+      wp3=z*dsqrt(si)
+      wm3=(qt2+qm1(1,1))/wp3
+      ep3(1)=.5d0*(wp3+wm3)                        !1st jet 4-momentum
+      ep3(2)=.5d0*(wp3-wm3)
+      qt=dsqrt(qt2)
+      call qgcs(c,s)
+      ep3(3)=qt*c
+      ep3(4)=qt*s
+
+      call qgtran(ep3,ey,1)
+      call qgrec(ep3,nqc,qv1,zv1,qm1,iqv1,ldau1,lpar1,jq)
+                               !reconstruction of 4-momenta of all final partons
+      if(iabs(iqc(jm)).eq.3)then
+       iqqq=8+iqc(jm)/3*4
+      else
+       iqqq=8+iqc(jm)
+      endif
+      if(debug.ge.3)write (moniou,212)tyq(iqqq),qt2,ep3
+
+      wp3=(1.d0-z)*dsqrt(si)
+      wm3=(qt2+qm2(1,1))/wp3
+      ep3(1)=.5d0*(wp3+wm3)                        !2nd jet 4-momentum
+      ep3(2)=.5d0*(wp3-wm3)
+      ep3(3)=-qt*c
+      ep3(4)=-qt*s
+      call qgtran(ep3,ey,1)
+
+c-------------------------------------------------
+c define color connections for the 2nd emitted jet
+      if(jt.eq.1)then
+       nqc(1)=nqc(2)
+       if(ncc(1,3-jm).eq.0.and.ncc(2,3-jm).eq.0)then
+        nqc(2)=ncc(3-jq,jm)
+       else
+        nqc(2)=ncc(3-jq,3-jm)
+       endif
+      elseif(jt.eq.2)then
+       if(ncc(1,1).eq.0.and.ncc(2,1).eq.0)then
+        if(jm.eq.1)then
+         nqc(2)=nqc(1)
+         nqc(1)=ncc(jq,3-jm)
+        else
+         nqc(1)=nqc(2)
+         nqc(2)=ncc(3-jq,jm)
+        endif
+       else
+        nqc(2)=ncc(3-jq,jm)
+        nqc(1)=ncc(jq,3-jm)
+       endif
+      elseif(jt.eq.3)then
+       nqc(1)=nqc(2)
+      elseif(jt.eq.4)then
+       nqc(2)=nqc(1)
+       if(ncc(1,1).eq.0.and.ncc(2,1).eq.0)then
+        nqc(1)=ncc(1,jm)
+       else
+        nqc(1)=ncc(jq,3-jm)
+       endif
+      elseif(jt.eq.5)then
+       if(ncc(1,jm).eq.0.and.ncc(2,jm).eq.0)then
+        nqc(1)=nqc(2)
+       else
+        nqc(1)=ncc(jq,jm)
+       endif
+      elseif(jt.eq.6)then
+       if(ncc(1,3-jm).eq.0.and.ncc(2,3-jm).eq.0)then
+        nqc(2)=nqc(1)
+       else
+        nqc(2)=ncc(3-jq,3-jm)
+       endif
+       nqc(1)=ncc(1,jm)
+      elseif(jt.eq.7)then
+       nqc(1)=ncc(1,jm)
+      endif
+      call qgrec(ep3,nqc,qv2,zv2,qm2,iqv2,ldau2,lpar2,jq2)
+                               !reconstruction of 4-momenta of all final partons
+      if(iabs(iqc(3-jm)).eq.3)then
+       iqqq=8+iqc(3-jm)/3*4
+      else
+       iqqq=8+iqc(3-jm)
+      endif
+      if(debug.ge.3)write (moniou,212)tyq(iqqq),qt2,ep3
+
+      ebal(1)=.5d0*(wpp+wpm)                          !balans of 4-momentum
+      ebal(2)=.5d0*(wpp-wpm)
+      ebal(3)=0.d0
+      ebal(4)=0.d0
+      do i=nj0+1,nj
+       if(iqq.eq.0.or.iqq.eq.1.and.i.ne.nva.or.iqq.eq.2
+     * .and.i.ne.nvb.or.iqq.eq.3.and.i.ne.nva.and.i.ne.nvb)then
+        do j=1,4
+         ebal(j)=ebal(j)-eqj(j,i)
+        enddo
+       endif
+      enddo
+      if(debug.ge.2)write (moniou,218)nj
+      if(debug.ge.5)write (moniou,219)ebal
+      if(debug.ge.1)write (moniou,220)
+
+201   format(2x,'qghot - semihard interaction:'/
+     *4x,'type of the interaction - ',i2/
+     *4x,'initial light cone momenta - ',2e10.3/
+     *4x,'remnant types - ',2i3,2x,'diffr. eigenstates - ',2i2/
+     *4x,'proj. class - ',i2,2x,'Pomeron type - ',i2/
+     *4x,'initial number of final partons - ',i4)
+202   format(2x,'qghot: mass squared for parton ladder - ',e10.3)
+203   format(2x,'qghot: ',' mass squared for the laddder:',e10.3/
+     *4x,'ladder end flavors:',2i3/4x,'ladder 5-momentum: ',5e10.3)
+204   format(2x,'qghot: kinematic bounds s2min=',e10.3,
+     *2x,'wwmin=',e10.3/4x,'jet cross section sj=',e10.3,
+     *2x,'born cross section sjb=',e10.3)
+205   format(2x,'qghot: xmin=',e10.3,2x,'xmax=',e10.3)
+206   format(2x,'qghot: qqmin=',e10.3,2x,'qqmax=',e10.3)
+208   format(2x,'qghot: xm0=',e10.3,2x,'xmin=',e10.3,2x,
+     *'xmax=',e10.3,2x,'gb0=',e10.3)
+209   format(2x,'qghot: qq=',e10.3,2x,'qqmin=',e10.3,2x,
+     *'qqmax=',e10.3,2x,'x=',e10.3,2x,'qt2=',e10.3)
+210   format(2x,'qghot: gb7=',e10.3,2x,'s2=',e10.3,2x,'sj1=',e10.3
+     *,2x,'sj2=',e10.3,2x,'jj=',i2,2x,'jini=',i2)
+211   format(2x,'qghot: colour connection jt=:',i1)
+212   format(2x,'qghot: new jet flavor:',a2,
+     *' pt squared for the jet:',e10.3/4x,'jet 4-momentum:',4e10.3)
+214   format(2x,'qghot - highest virtuality subprocess in the ladder:'/
+     *4x,'mass squared for the process:',e10.3/4x,'q^2-cutoff:',e10.3
+     *,2x,'iqc=',2i3)
+215   format(2x,'qghot - normalization of acceptance:',' gb0=',e10.3)
+216   format(2x,'qghot - acceptance probabilty:'/
+     *4x,'gb=',e10.3,2x,'q2=',e10.3,2x,'z=',e10.3,2x,'qt2=',e10.3)
+217   format(2x,'qghot: ecm=',e10.3,2x,'mt1=',e10.3,2x,'mt2=',e10.3)
+218   format(2x,'qghot: total number of jets - ',i4)
+219   format(2x,'qghot: 4-momentum balans - ',4e10.3)
+220   format(2x,'qghot - end')
+      return
+      end
+
+c------------------------------------------------------------------------
+      function npgen(vv,npmin,npmax)
+c-----------------------------------------------------------------------
+c npgen -  Poisson distribution
+c vv    - average number
+c npmin - minimal number
+c npmax - maximal number
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr11/ b10
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      EXTERNAL qgran
+
+      if(npmin.eq.0)then
+       aks=qgran(b10)
+       vvn=exp(-vv)
+       do n=1,npmax
+         aks=aks-vvn
+        if(aks.lt.0.d0)goto 1
+         vvn=vvn*vv/dble(n)
+       enddo
+      elseif(npmin.eq.1)then
+       aks=qgran(b10)*(1.d0-exp(-vv))
+       vvn=exp(-vv)
+       do n=1,npmax
+         vvn=vvn*vv/dble(n)
+         aks=aks-vvn
+        if(aks.lt.0.d0)goto 2
+       enddo
+      elseif(npmin.eq.2)then
+       aks=qgran(b10)*(1.d0-exp(-vv)*(1.d0+vv))
+       vvn=vv*exp(-vv)
+       do n=2,npmax
+         vvn=vvn*vv/dble(n)
+         aks=aks-vvn
+        if(aks.lt.0.d0)goto 2
+       enddo
+      else
+       stop'npgen'
+      endif
+1     n=n-1
+2     npgen=n
+      return
+      end
+
+c=============================================================================
+      subroutine qglead(wppr0,wmtg0,lqa,lqb,lqa0,lqb0,lva,lvb
+     *,izp,izt,ila,ilb,iret)
+c-------------------------------------------------------------------------
+c qglead-treatment of leading hadron states
+c-------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(njmax=50000)
+      common /qgdebug/ debug
+      common /qgarr37/ eqj(4,njmax),iqj(njmax),ncj(2,njmax),nj
+
+      iret=0
+      if(lqa0.eq.0.and.lqb0.eq.0)then
+       if(lva.eq.0.and.lvb.eq.0)then
+        call qgdifr(wppr0,wmtg0,izp,izt,lqa,lqb,iret)
+       elseif(lva.eq.0)then
+        call qgdifr(wppr0,wmtg0,izp,izt,lqa,-1,iret)
+       elseif(lvb.eq.0)then
+        call qgdifr(wppr0,wmtg0,izp,izt,-1,lqb,iret)
+       endif
+       if(lva.eq.1)then
+        eqj(1,ila)=.5d0*wppr0
+        eqj(2,ila)=eqj(1,ila)
+        eqj(3,ila)=0.d0
+        eqj(4,ila)=0.d0
+       endif
+       if(lvb.eq.1)then
+        eqj(1,ilb)=.5d0*wmtg0
+        eqj(2,ilb)=-eqj(1,ilb)
+        eqj(3,ilb)=0.d0
+        eqj(4,ilb)=0.d0
+       endif
+      elseif(lqa0.eq.0)then
+       if(lva.eq.0)then
+        call qgdifr(wppr0,wmtg0,izp,izt,lqa,-1,iret)
+       else
+        eqj(1,ila)=.5d0*wppr0
+        eqj(2,ila)=eqj(1,ila)
+        eqj(3,ila)=0.d0
+        eqj(4,ila)=0.d0
+       endif
+      elseif(lqb0.eq.0)then
+       if(lvb.eq.0)then
+        call qgdifr(wppr0,wmtg0,izp,izt,-1,lqb,iret)
+       else
+        eqj(1,ilb)=.5d0*wmtg0
+        eqj(2,ilb)=-eqj(1,ilb)
+        eqj(3,ilb)=0.d0
+        eqj(4,ilb)=0.d0
+       endif
+      endif
+      return
+      end
+
+c=============================================================================
+      double precision function qgbit(qi,qj,s,m,l)
+c------------------------------------------------------------------------
+c qgbit - born cross-section interpolation
+c qi,qj - effective momentum cutoffs for the scattering,
+c s - total c.m. energy squared for the scattering,
+c m - parton type at current end of the ladder (1 - g, 2 - q)
+c l - parton type at opposite end of the ladder (1 - g, 2 - q)
+c------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wi(3),wk(3)
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr20/ spmax
+      common /qgarr26/ factk,fqscal
+      common /qgarr31/ csj(40,160)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)qi,qj,s,m,l
+      qgbit=0.d0
+      qq=max(qi,qj)
+      s2min=qq*4.d0*fqscal
+      if(s.le..99d0*s2min)then
+       if(debug.ge.3)write (moniou,202)qgbit
+       return
+      endif
+
+      tmin=qq*fqscal/(.5d0+dsqrt(max(0.d0,.25d0-qq*fqscal/s)))
+      ml=40*(m-1)+80*(l-1)
+      qli=dlog(qq)/dlog(spmax/4.d0/fqscal)*39.d0+1.d0
+      sl=dlog(s/s2min)/dlog(spmax/s2min)*39.d0+1.d0
+      i=min(38,int(qli))
+      k=min(38,int(sl))
+
+      wk(2)=sl-k
+      wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+      wk(1)=1.d0-wk(2)+wk(3)
+      wk(2)=wk(2)-2.d0*wk(3)
+      wi(2)=qli-i
+      wi(3)=wi(2)*(wi(2)-1.d0)*.5d0
+      wi(1)=1.d0-wi(2)+wi(3)
+      wi(2)=wi(2)-2.d0*wi(3)
+      do k1=1,3
+       k2=k+k1-1+ml
+      do i1=1,3
+       qgbit=qgbit+csj(i+i1-1,k2)*wi(i1)*wk(k1)
+      enddo
+      enddo
+      qgbit=exp(qgbit)*(1.d0/tmin-2.d0/s)
+      if(qi.lt.qq)qgbit=qgbit*qgsudx(qq,m)/qgsudx(qi,m)
+      if(qj.lt.qq)qgbit=qgbit*qgsudx(qq,l)/qgsudx(qj,l)
+
+      if(debug.ge.3)write (moniou,202)qgbit
+201   format(2x,'qgbit: qi=',e10.3,2x,'qj=',e10.3
+     *,2x,'s= ',e10.3,2x,'m= ',i1,2x,'l= ',i1)
+202   format(2x,'qgbit=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgfbor(s,t,iq1,iq2,n)
+c---------------------------------------------------------------------------
+c qgfbor - integrand for the born cross-section (matrix element squared)
+c s - total c.m. energy squared for the scattering,
+c t - invariant variable for the scattering abs[(p1-p3)**2],
+c iq1 - parton type at current end of the ladder (0 - g, 1,2 - q)
+c iq2 - parton type at opposite end of the ladder (0 - g, 1,2 - q)
+c---------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)s,t,iq1,iq2
+
+      u=s-t
+      if(n.eq.1)then
+       if(iq1.eq.0.and.iq2.eq.0)then        !gluon-gluon
+        qgfbor=(3.d0-t*u/s**2+s*u/t**2+s*t/u**2)*4.5d0
+       elseif(iq1*iq2.eq.0)then             !gluon-quark
+        qgfbor=(s**2+u**2)/t**2+(s/u+u/s)/2.25d0
+       elseif(iq1.eq.iq2)then               !quark-quark (same flavor)
+        qgfbor=((s**2+u**2)/t**2+(s**2+t**2)/u**2)/2.25d0
+     *  -s**2/t/u/3.375d0
+       elseif(iq1+iq2.eq.0)then             !quark-antiquark (same flavor)
+        qgfbor=((s**2+u**2)/t**2+(u**2+t**2)/s**2)/2.25d0
+     *  +u**2/t/s/3.375d0
+       else                                 !quark-antiquark (different flavors)
+        qgfbor=(s**2+u**2)/t**2/2.25d0
+       endif
+      elseif(n.eq.2)then
+       if(iq1.eq.0.and.iq2.eq.0)then        !gluon-gluon->quark-antiquark
+        qgfbor=.5d0*(t/u+u/t)-1.125d0*(t*t+u*u)/s**2
+       elseif(iq1+iq2.eq.0)then             !quark-antiquark->quark-antiquark
+        qgfbor=(t*t+u*u)/s**2/1.125d0       !(different flavor)
+       else
+        qgfbor=0.d0
+       endif
+      elseif(n.eq.3)then
+       if(iq1.ne.0.and.iq1+iq2.eq.0)then    !quark-antiquark->gluon-gluon
+        qgfbor=32.d0/27.d0*(t/u+u/t)-(t*t+u*u)/s**2/.375d0
+       else
+        qgfbor=0.d0
+       endif
+      endif
+
+      if(debug.ge.2)write (moniou,202)qgfbor
+201   format(2x,'qgfbor - hard scattering matrix element squared:'/
+     *4x,'s=',e10.3,2x,'|t|=',e10.3,2x,'iq1=',i1,2x,'iq2=',i1)
+202   format(2x,'qgfbor=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgborn(qi,qj,s,iq1,iq2)
+c-----------------------------------------------------------------------------
+c qgborn - hard 2->2 parton scattering born cross-section
+c s is the c.m. energy square for the scattering process,
+c iq1 - parton type at current end of the ladder (0 - g, 1,2 etc. - q)
+c iq2 - parton type at opposite end of the ladder (0 - g, 1,2 etc. - q)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      common /arr3/  x1(7),a1(7)
+
+      if(debug.ge.2)write (moniou,201)qi,qj,s,iq1,iq2
+
+      qgborn=0.d0
+      qq=max(qi,qj)
+      tmin=qq*fqscal/(.5d0+dsqrt(max(0.d0,.25d0-qq*fqscal/s)))
+      do i=1,7
+      do m=1,2
+       t=2.d0*tmin/(1.d0+2.d0*tmin/s-x1(i)*(2*m-3)*(1.d0-2.d0*tmin/s))
+       qt=t*(1.d0-t/s)
+
+       fb=0.d0
+       do n=1,3
+        fb=fb+qgfbor(s,t,iq1,iq2,n)+qgfbor(s,s-t,iq1,iq2,n)
+       enddo
+       fb=fb*qgsudx(qt/fqscal,iabs(iq1)+1)
+     * *qgsudx(qt/fqscal,iabs(iq2)+1)
+
+       qgborn=qgborn+a1(i)*fb*qgalf(qt/fqscal/alm)**2*t**2
+      enddo
+      enddo
+      qgborn=qgborn*2.d0*pi**3/s**2
+
+      qgborn=qgborn/qgsudx(qi,iabs(iq1)+1)/qgsudx(qj,iabs(iq2)+1)
+      if(iq1.eq.iq2)qgborn=qgborn*.5d0
+
+      if(debug.ge.3)write (moniou,202)qgborn
+201   format(2x,'qgborn: qi=',e10.3,2x,'qj=',e10.3,2x,
+     *'s= ',e10.3,2x,'iq1= ',i1,2x,'iq2= ',i1)
+202   format(2x,'qgborn=',e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qgcjet(qq,iq1,qv,zv,qm,iqv,ldau,lpar,jq)
+c-----------------------------------------------------------------------------
+c final state emission process (all branchings as well as parton masses
+c are determined)
+c qq - maximal effective momentum transfer for the first branching
+c iq1 - initial jet flavour (0 - for gluon)
+c qv(i,j) - effective momentum for the branching of the parton in i-th row
+c on j-th level (0 - in case of no branching)  - to be determined
+c zv(i,j) - z-value for the branching of the parton in i-th row
+c on j-th level - to be determined
+c qm(i,j) - mass squared for the parton in i-th row
+c on j-th level - to be determined
+c iqv(i,j) - flavour for the parton in i-th row on j-th level
+c - to be determined
+c ldau(i,j) - first daughter row for the branching of the parton in i-th row
+c on j-th level - to be determined
+c lpar(i,j) - the parent row for the parton in i-th row
+c on j-th level - to be determined
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension qmax(30,50),iqm(2),lnv(50),
+     *qv(30,50),zv(30,50),qm(30,50),iqv(30,50),
+     *ldau(30,49),lpar(30,50)
+      common /qgarr11/ b10
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      EXTERNAL qgran
+
+      if(debug.ge.2)write (moniou,201)qq,iq1,jq
+
+      do i=2,50
+       lnv(i)=0
+      enddo
+      lnv(1)=1
+      qmax(1,1)=qq
+      iqv(1,1)=iq1
+      nlev=1
+      nrow=1
+
+2     qlmax=dlog(qmax(nrow,nlev)/qtf/16.d0)
+      iq=min(1,iabs(iqv(nrow,nlev)))+1
+
+      if(qgran(b10).gt.qgsudi(qlmax,iq))then
+       q=qgqint(qlmax,qgran(b10),iq)
+       z=qgzsim(q,iq)
+       ll=lnv(nlev+1)+1
+       ldau(nrow,nlev)=ll
+       lpar(ll,nlev+1)=nrow
+       lpar(ll+1,nlev+1)=nrow
+       lnv(nlev+1)=ll+1
+
+       if(iq.ne.1)then
+        if((3-2*jq)*iqv(nrow,nlev).gt.0)then
+         iqm(1)=0
+         iqm(2)=iqv(nrow,nlev)
+        else
+         iqm(2)=0
+         iqm(1)=iqv(nrow,nlev)
+         z=1.d0-z
+        endif
+       else
+        wg=qgfap(z,1,1)
+        wg=wg/(wg+qgfap(z,1,2))
+        if(qgran(b10).lt.wg)then
+         iqm(1)=0
+         iqm(2)=0
+        else
+         iqm(1)=int(3.d0*qgran(b10)+1.d0)*(3-2*jq)
+         iqm(2)=-iqm(1)
+        endif
+        if(qgran(b10).lt..5d0)z=1.d0-z
+       endif
+       qv(nrow,nlev)=q
+       zv(nrow,nlev)=z
+       nrow=ll
+       nlev=nlev+1
+       qmax(nrow,nlev)=q*z**2
+       qmax(nrow+1,nlev)=q*(1.d0-z)**2
+       iqv(nrow,nlev)=iqm(1)
+       iqv(nrow+1,nlev)=iqm(2)
+       if(debug.ge.3)write (moniou,203)nlev,nrow,q,z
+       goto 2
+      else
+       qv(nrow,nlev)=0.d0
+       zv(nrow,nlev)=0.d0
+       qm(nrow,nlev)=0.d0
+       if(debug.ge.3)write (moniou,204)nlev,nrow
+      endif
+
+3     continue
+      if(nlev.eq.1)then
+       if(debug.ge.3)write (moniou,202)
+       return
+      endif
+
+      lprow=lpar(nrow,nlev)
+      if(ldau(lprow,nlev-1).eq.nrow)then
+       nrow=nrow+1
+       goto 2
+      else
+       z=zv(lprow,nlev-1)
+       qm(lprow,nlev-1)=z*(1.d0-z)*qv(lprow,nlev-1)
+     * +qm(nrow-1,nlev)/z+qm(nrow,nlev)/(1.d0-z)
+       nrow=lprow
+       nlev=nlev-1
+       if(debug.ge.3)write (moniou,205)nlev,nrow,qm(lprow,nlev)
+       goto 3
+      endif
+
+201   format(2x,'qgcjet: qq=',e10.3,2x,'iq1= ',i1,2x,'jq=',i1)
+202   format(2x,'qgcjet - end')
+203   format(2x,'qgcjet: new branching at level nlev=',i2,' nrow=',i2
+     */4x,' effective momentum q=',e10.3,2x,' z=',e10.3)
+204   format(2x,'qgcjet: new final jet at level nlev=',i2,' nrow=',i2)
+205   format(2x,'qgcjet: jet mass at level nlev=',i2,' nrow=',i2
+     *,' - qm=',e10.3)
+      end
+
+c===========================================================================
+      subroutine qgcs(c,s)
+c---------------------------------------------------------------------------
+c c,s - cos and sin generation for uniformly distributed angle 0<fi<2*pi
+c---------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr11/ b10
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      EXTERNAL qgran
+
+      if(debug.ge.2)write (moniou,201)
+1     s1=2.d0*qgran(b10)-1.d0
+      s2=2.d0*qgran(b10)-1.d0
+      s3=s1*s1+s2*s2
+      if(s3.gt.1.d0)goto 1
+      s3=dsqrt(s3)
+      c=s1/s3
+      s=s2/s3
+
+      if(debug.ge.3)write (moniou,202)c,s
+201   format(2x,'qgcs - cos(fi) and sin(fi) are generated',
+     *' (0<fi<2*pi)')
+202   format(2x,'qgcs: c=',e10.3,2x,'s=',e10.3)
+      return
+      end
+
+c===========================================================================
+      subroutine qgdeft(s,ep,ey)
+c---------------------------------------------------------------------------
+c determination of the parameters for the lorentz transform to the rest frame
+c system for 4-vector ep
+c---------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension ey(3),ep(4)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)ep,s
+
+      do i=1,3
+       if(ep(i+1).eq.0.d0)then
+        ey(i)=1.d0
+       else
+        wp=ep(1)+ep(i+1)
+        wm=ep(1)-ep(i+1)
+        if(wm/wp.lt.1.d-8)then
+         ww=s
+         do l=1,3
+          if(l.ne.i)ww=ww+ep(l+1)**2
+         enddo
+         wm=ww/wp
+        endif
+        ey(i)=dsqrt(wm/wp)
+        ep(1)=wp*ey(i)
+        ep(i+1)=0.d0
+       endif
+      enddo
+
+      if(debug.ge.3)write (moniou,202)ey
+201   format(2x,'qgdeft - lorentz boost parameters:'
+     */4x,'4-vector ep=',4e10.3/4x,'4-vector squared s=',e10.3)
+202   format(2x,'qgdeft: lorentz boost parameters ey(i)=',2x,3e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qgdefr(ep,s0x,c0x,s0,c0)
+c-----------------------------------------------------------------------------
+c determination of the parameters the spacial rotation to the lab. system
+c for 4-vector ep
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension ep(4)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)ep
+
+c transverse momentum square for the current parton (ep)
+      pt2=ep(3)**2+ep(4)**2
+      if(pt2.ne.0.d0)then
+       pt=dsqrt(pt2)
+c system rotation to get pt=0 - euler angles are determined (c0x = cos theta,
+c s0x = sin theta, c0 = cos phi, s0 = sin phi)
+       c0x=ep(3)/pt
+       s0x=ep(4)/pt
+c total momentum for the gluon
+       pl=dsqrt(pt2+ep(2)**2)
+       s0=pt/pl
+       c0=ep(2)/pl
+      else
+       c0x=1.d0
+       s0x=0.d0
+       pl=abs(ep(2))
+       s0=0.d0
+       c0=ep(2)/pl
+      endif
+      ep(2)=pl
+      ep(3)=0.d0
+      ep(4)=0.d0
+
+      if(debug.ge.3)write (moniou,202)s0x,c0x,s0,c0,ep
+201   format(2x,'qgdefr - spacial rotation parameters'/4x,
+     *'4-vector ep=',2x,4(e10.3,1x))
+202   format(2x,'qgdefr: spacial rotation parameters'/
+     *4x,'s0x=',e10.3,2x,'c0x=',e10.3,2x,'s0=',e10.3,2x,'c0=',e10.3/
+     *4x,'rotated 4-vector ep=',4(e10.3,1x))
+      return
+      end
+
+c=============================================================================
+      double precision function qgfap(x,j,l)
+c------------------------------------------------------------------------
+c qgfap - altarelli-parisi function (multiplied by x)
+c x - light cone momentum share value,
+c j - type of the parent parton (1-g,2-q)
+c l - type of the daughter parton (1-g,2-q)
+c------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)x,j,l
+
+      if(j.eq.1)then
+       if(l.eq.1)then
+        qgfap=((1.d0-x)/x+x/(1.d0-x)+x*(1.d0-x))*6.d0
+       else
+        qgfap=(x**2+(1.d0-x)**2)*3.d0
+       endif
+      else
+       if(l.eq.1)then
+        qgfap=(1.d0+(1.d0-x)**2)/x/.75d0
+       else
+        qgfap=(x**2+1.d0)/(1.d0-x)/.75d0
+       endif
+      endif
+
+      if(debug.ge.3)write (moniou,202)qgfap
+201   format(2x,'qgfap - altarelli-parisi function:'
+     *,2x,'x=',e10.3,2x,'j=',i1,2x,'l=',i1)
+202   format(2x,'qgfap=',e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qggea(ia,xa,jj)
+c-----------------------------------------------------------------------------
+c qggea - nuclear configuration simulation (nucleons positions)
+c ia - number of nucleons to be considered
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208)
+      dimension xa(iapmax,3)
+      common /qgarr5/  rnuc(2),wsnuc(2),wbnuc(2),anorm
+     *,cr1(2),cr2(2),cr3(2)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr11/ b10
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      EXTERNAL qgran
+
+      if(debug.ge.2)write (moniou,201)jj,ia
+
+      if(ia.ge.10)then
+       do i=1,ia
+1       zuk=qgran(b10)*cr1(jj)-1.d0
+c        if(zuk)2,2,3
+        if(zuk.le.0.d0)then
+         tt=rnuc(jj)/wsnuc(jj)*(qgran(b10)**.3333d0-1.d0)
+         goto 6
+        else
+         if(zuk.gt.cr2(jj))goto 4
+         tt=-dlog(qgran(b10))
+         goto 6
+4        if(zuk.gt.cr3(jj))goto 5
+         tt=-dlog(qgran(b10))-dlog(qgran(b10))
+         goto 6
+5        tt=-dlog(qgran(b10))-dlog(qgran(b10))-dlog(qgran(b10))
+        endif
+6       rim=tt*wsnuc(jj)+rnuc(jj)
+        if(qgran(b10).gt.(1.d0+wbnuc(jj)*rim**2/rnuc(jj)**2)
+     *  /(1.d0+exp(-abs(tt))))goto 1
+        z=rim*(2.d0*qgran(b10)-1.d0)
+        rim=dsqrt(rim*rim-z*z)
+        xa(i,3)=z
+        call qgcs(c,s)
+        xa(i,1)=rim*c
+        xa(i,2)=rim*s
+       enddo
+      else
+       do l=1,3
+        summ=0.d0
+        do i=1,ia-1
+         j=ia-i
+         aks=rnuc(jj)*(qgran(b10)+qgran(b10)+qgran(b10)-1.5d0)
+         k=j+1
+         xa(k,l)=summ-aks*sqrt(float(j)/k)
+         summ=summ+aks/sqrt(float(j*k))
+        enddo
+        xa(1,l)=summ
+       enddo
+      endif
+
+      if(debug.ge.3)then
+       write (moniou,203)
+       do i=1,ia
+        write (moniou,204)i,(xa(i,l),l=1,3)
+       enddo
+       write (moniou,202)
+      endif
+201   format(2x,'qggea - configuration of the nucleus ',i1,';',2x,
+     *'coordinates for ',i2,' nucleons')
+202   format(2x,'qggea - end')
+203   format(2x,'qggea:  positions of the nucleons')
+204   format(2x,'qggea: ',i2,' - ',3(e10.3,1x))
+      return
+      end
+
+c=============================================================================
+      double precision function qgapi(x,j,l)
+c-----------------------------------------------------------------------------
+c qgapi - integrated altarelli-parisi function
+c x - light cone momentum share value,
+c j - type of initial parton (1 - g, 2 - q)
+c l - type of final parton (1 - g, 2 - q)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)x,j,l
+
+      if(j.eq.1)then
+       if(l.eq.1)then
+        qgapi=6.d0*(dlog(x/(1.d0-x))-x**3/3.d0+x**2/2.d0-2.d0*x)
+       else
+        qgapi=3.d0*(x+x**3/1.5d0-x*x)
+       endif
+      else
+       if(l.eq.1)then
+        qgapi=(dlog(x)-x+.25d0*x*x)/.375d0
+       else
+        z=1.d0-x
+        qgapi=-(dlog(z)-z+.25d0*z*z)/.375d0
+       endif
+      endif
+
+      if(debug.ge.2)write (moniou,202)qgapi
+201   format(2x,'qgapi: x=',e10.3,2x,'j= ',i1,2x,'l= ',i1)
+202   format(2x,'qgapi=',e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qgjarr(jfl)
+c-----------------------------------------------------------------------------
+c final jets rearrangement according to their colour connections
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(njmax=50000)
+      dimension mark(njmax),ept(4)
+      common /qgarr10/ am(7),ammu
+      common /qgarr36/ epjet(4,njmax),ipjet(njmax),njtot
+      common /qgarr37/ eqj(4,njmax),iqj(njmax),ncj(2,njmax),nj
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)nj
+      if(debug.ge.2.and.nj.ne.0)then
+       do i=1,nj
+        write (moniou,203)i,iqj(i),(eqj(l,i),l=1,4)
+        if(iqj(i).eq.0)then
+         write (moniou,204)ncj(1,i),ncj(2,i)
+        else
+         ncdum=0
+         write (moniou,204)ncj(1,i),ncdum
+        endif
+       enddo
+      endif
+
+      njpar=0
+      jfl=0
+      do i=1,nj
+       mark(i)=1
+      enddo
+      njtot=0
+
+2     continue
+      do ij=1,nj
+       if(mark(ij).ne.0.and.iqj(ij).ne.0)goto 4
+      enddo
+4     continue
+
+      jfirst=1
+      if(iabs(iqj(ij)).le.2)then
+       am1=am(1)
+      elseif(iabs(iqj(ij)).eq.4)then
+       am1=am(3)
+      else
+       am1=am(2)
+      endif
+      do i=1,4
+       ept(i)=0.d0
+      enddo
+
+6     mark(ij)=0
+      njtot=njtot+1
+      ipjet(njtot)=iqj(ij)
+      do i=1,4
+       ept(i)=ept(i)+eqj(i,ij)
+       epjet(i,njtot)=eqj(i,ij)
+      enddo
+
+      if(iqj(ij).ne.0)then
+       if(jfirst.ne.1)then
+        if(iabs(iqj(ij)).le.2)then
+         am2=am(1)
+        elseif(iabs(iqj(ij)).eq.4)then
+         am2=am(3)
+        else
+         am2=am(2)
+        endif
+        amj=(am1+am2)**2
+        if(amj.gt.qgnrm(ept))then
+         if(debug.ge.3)write (moniou,202)jfl
+         return
+        endif
+
+        if(njtot.lt.nj)then
+         goto 2
+        else
+         jfl=1
+         nj=0
+         if(debug.ge.3)write (moniou,202)jfl
+         return
+        endif
+       else
+        jfirst=0
+        njpar=ij
+        ij=ncj(1,ij)
+        goto 6
+       endif
+      else
+       if(ncj(1,ij).eq.njpar)then
+        njdau=ncj(2,ij)
+       else
+        njdau=ncj(1,ij)
+       endif
+       njpar=ij
+       ij=njdau
+       goto 6
+      endif
+
+201   format(2x,'qgjarr: total number of jets nj=',i4)
+202   format(2x,'qgjarr - end,jfl=',i2)
+203   format(2x,'qgjarr: ij=',i3,2x,'iqj=',i2,2x,'eqj=',4e10.3)
+204   format(2x,'qgjarr: ncj=',2i3)
+      end
+
+c=============================================================================
+      double precision function qgjet(q1,q2,s,s2min,j,l)
+c-----------------------------------------------------------------------------
+c qgjet - inclusive hard cross-section calculation (one more run is added
+c to the ladder) - for any ordering
+c q1 - effective momentum cutoff for current end of the ladder,
+c q2 - effective momentum cutoff for opposide end of the ladder,
+c s - total c.m. energy squared for the ladder,
+c s2min - minimal c.m. energy squared for born process (above q1 and q2)
+c j - parton type at current end of the ladder (1 - g, 2 - q)
+c l - parton type at opposite end of the ladder (1 - g, 2 - q)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgarr51/ epsxmn
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      if(debug.ge.2)write (moniou,201)s,q1,q2,s2min,j,l
+
+      qgjet=0.d0
+      qmax=s/4.d0/fqscal*(1.d0-epsxmn)
+      qmin=q1
+      if(debug.ge.3)write (moniou,203)qmin,qmax
+
+      if(qmax.gt.qmin)then
+c numerical integration over transverse momentum square;
+c gaussian integration is used
+       do i=1,7
+       do m=1,2
+        qi=2.d0*qmin/(1.d0+qmin/qmax+(2*m-3)*x1(i)*(1.d0-qmin/qmax))
+        zmax=(1.d0-epsxmn)**delh
+        zmin=(max(4.d0*fqscal*qi,s2min)/s)**delh
+        fsj=0.d0
+        if(debug.ge.3)write (moniou,204)qi,zmin,zmax
+
+        if(zmax.gt.zmin)then
+         do i1=1,7
+         do m1=1,2
+          z=(.5d0*(zmax+zmin+(2*m1-3)*x1(i1)*(zmax-zmin)))**(1.d0/delh)
+          s2=z*s
+
+          sj=0.d0
+          do k=1,2
+           sj=sj+qgjit(qi,q2,s2,k,l)*qgfap(z,j,k)*z
+          enddo
+          fsj=fsj+a1(i1)*sj/z**delh
+         enddo
+         enddo
+         fsj=fsj*(zmax-zmin)
+        endif
+        qgjet=qgjet+a1(i)*fsj*qi*qgsudx(qi,j)*qgalf(qi/alm)
+       enddo
+       enddo
+       qgjet=qgjet*(1.d0/qmin-1.d0/qmax)/qgsudx(q1,j)/delh/4.d0
+      endif
+
+      if(debug.ge.3)write (moniou,202)qgjet
+201   format(2x,'qgjet - unordered ladder cross section:'
+     */4x,'s=',e10.3,2x,'q1=',e10.3,2x,'q2=',e10.3,2x,'s2min=',
+     *e10.3,2x,'j=',i1,2x,'l=',i1)
+202   format(2x,'qgjet=',e10.3)
+203   format(2x,'qgjet:',2x,'qmin=',e10.3,2x,'qmax=',e10.3)
+204   format(2x,'qgjet:',2x,'qi=',e10.3,2x,'zmin=',e10.3
+     *,2x,'zmax=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgjet1(q1,q2,s,s2min,j,l)
+c-----------------------------------------------------------------------------
+c qgjet1 - inclusive hard cross-section calculation (one more run is added
+c to the ladder) - for strict ordering
+c q1 - effective momentum cutoff for current end of the ladder,
+c q2 - effective momentum cutoff for opposide end of the ladder,
+c s - total c.m. energy squared for the ladder,
+c s2min - minimal c.m. energy squared for born process (above q1 and q2)
+c j - parton type at current end of the ladder (1 - g, 2 - q)
+c l - parton type at opposite end of the ladder (1 - g, 2 - q)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgarr51/ epsxmn
+      common /qgdebug/  debug
+      common /arr3/   x1(7),a1(7)
+
+      if(debug.ge.2)write (moniou,201)s,q1,q2,s2min,j,l
+
+      qgjet1=0.d0
+      qmax=s/4.d0/fqscal*(1.d0-epsxmn)
+      qmin=q1
+      if(debug.ge.3)write (moniou,203)qmin,qmax
+
+      if(qmax.gt.qmin)then
+c numerical integration over transverse momentum square;
+c gaussian integration is used
+       do i=1,7
+       do m=1,2
+        qi=2.d0*qmin/(1.d0+qmin/qmax+(2*m-3)*x1(i)*(1.d0-qmin/qmax))
+        zmax=(1.d0-epsxmn)**delh
+        zmin=(max(4.d0*fqscal*qi,s2min)/s)**delh
+        fsj=0.d0
+        if(debug.ge.3)write (moniou,204)qi,zmin,zmax
+
+        if(zmax.gt.zmin)then
+         do i1=1,7
+         do m1=1,2
+          z=(.5d0*(zmax+zmin+(2*m1-3)*x1(i1)*(zmax-zmin)))**(1.d0/delh)
+          s2=z*s
+
+          sj=0.d0
+          do k=1,2
+           sj=sj+qgjit1(qi,q2,s2,k,l)*qgfap(z,j,k)*z
+          enddo
+          fsj=fsj+a1(i1)*sj/z**delh
+         enddo
+         enddo
+         fsj=fsj*(zmax-zmin)
+        endif
+        qgjet1=qgjet1+a1(i)*fsj*qi*qgsudx(qi,j)*qgalf(qi/alm)
+       enddo
+       enddo
+       qgjet1=qgjet1*(1.d0/qmin-1.d0/qmax)/qgsudx(q1,j)/delh/4.d0
+      endif
+
+      if(debug.ge.3)write (moniou,202)qgjet1
+201   format(2x,'qgjet1 - strictly ordered ladder cross section:'
+     */4x,'s=',e10.3,2x,'q1=',e10.3,2x,'q2=',e10.3,2x,'s2min=',
+     *e10.3,2x,'j=',i1,2x,'l=',i1)
+202   format(2x,'qgjet1=',e10.3)
+203   format(2x,'qgjet1:',2x,'qmin=',e10.3,2x,'qmax=',e10.3)
+204   format(2x,'qgjet1:',2x,'qi=',e10.3,2x,'zmin=',e10.3
+     *,2x,'zmax=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgjit(q1,q2,s,m,l)
+c-----------------------------------------------------------------------------
+c qgjit - inclusive hard cross-section interpolation - for any ordering
+c in the ladder
+c q1 - effective momentum cutoff for current end of the ladder,
+c q2 - effective momentum cutoff for opposide end of the ladder,
+c s - total c.m. energy squared for the ladder,
+c m - parton type at current end of the ladder (1 - g, 2 - q)
+c l - parton type at opposite end of the ladder (1 - g, 2 - q)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wi(3),wj(3),wk(3)
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr20/ spmax
+      common /qgarr26/ factk,fqscal
+      common /qgarr29/ csj(40,40,160)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)s,q1,q2,m,l
+
+      qgjit=0.d0
+      qq=max(q1,q2)
+      s2min=qq*4.d0*fqscal
+      if(s.le..99d0*s2min)then
+       if(debug.ge.3)write (moniou,202)qgjit
+       return
+      endif
+
+      if(q1.le.q2)then
+       qi=q1
+       qj=q2
+       ml=40*(m-1)+80*(l-1)
+      else
+       qi=q2
+       qj=q1
+       ml=40*(l-1)+80*(m-1)
+      endif
+
+      tmin=qq*fqscal/(.5d0+dsqrt(max(0.d0,.25d0-qq*fqscal/s)))
+      qli=dlog(qi)/dlog(spmax/4.d0/fqscal)*39.d0+1.d0
+      if(qi.lt..99d0*spmax/4.d0/fqscal)then
+       qlj=dlog(qj/qi)/dlog(spmax/4.d0/fqscal/qi)*39.d0+1.d0
+      else
+       qlj=1.d0
+      endif
+      sl=dlog(s/s2min)/dlog(spmax/s2min)*39.d0+1.d0
+      i=min(38,int(qli))
+      j=min(38,int(qlj))
+      k=min(38,int(sl))
+
+      wk(2)=sl-k
+      wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+      wk(1)=1.d0-wk(2)+wk(3)
+      wk(2)=wk(2)-2.d0*wk(3)
+      wi(2)=qli-i
+      wi(3)=wi(2)*(wi(2)-1.d0)*.5d0
+      wi(1)=1.d0-wi(2)+wi(3)
+      wi(2)=wi(2)-2.d0*wi(3)
+      wj(2)=qlj-j
+      wj(3)=wj(2)*(wj(2)-1.d0)*.5d0
+      wj(1)=1.d0-wj(2)+wj(3)
+      wj(2)=wj(2)-2.d0*wj(3)
+      do k1=1,3
+       k2=k+k1-1+ml
+      do i1=1,3
+      do j1=1,3
+       qgjit=qgjit+csj(i+i1-1,j+j1-1,k2)*wi(i1)*wj(j1)*wk(k1)
+      enddo
+      enddo
+      enddo
+      qgjit=exp(qgjit)*(1.d0/tmin-2.d0/s)
+
+      if(debug.ge.3)write (moniou,202)qgjit
+201   format(2x,'qgjit - unordered ladder cross section interpol.:'/4x,
+     *'s=',e10.3,2x,'q1=',e10.3,2x,'q2=',e10.3,2x,2x,'m=',i1,2x,'l=',i1)
+202   format(2x,'qgjit=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgjit1(q1,q2,s,m,l)
+c-----------------------------------------------------------------------------
+c qgjit1 - inclusive hard cross-section interpolation - for strict ordering
+c in the ladder
+c q1 - effective momentum cutoff for current end of the ladder,
+c q2 - effective momentum cutoff for opposide end of the ladder,
+c s - total c.m. energy squared for the ladder,
+c m - parton type at current end of the ladder (1 - g, 2 - q)
+c l - parton type at opposite end of the ladder (1 - g, 2 - q)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wi(3),wj(3),wk(3)
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr20/ spmax
+      common /qgarr26/ factk,fqscal
+      common /qgarr30/ csj(40,40,160)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)s,q1,q2,m,l
+
+      qgjit1=0.d0
+      qq=max(q1,q2)
+      s2min=qq*4.d0*fqscal
+      if(s.le.s2min)then
+       if(debug.ge.3)write (moniou,202)qgjit1
+       return
+      endif
+
+      tmin=qq*fqscal/(.5d0+dsqrt(max(0.d0,.25d0-qq*fqscal/s)))
+      ml=40*(m-1)+80*(l-1)
+      qli=dlog(q1)/dlog(spmax/4.d0/fqscal)*39.d0+1.d0
+      if(q1.lt..99d0*spmax/4.d0/fqscal)then
+       qlj=dlog(qq/q1)/dlog(spmax/4.d0/fqscal/q1)*39.d0+1.d0
+      else
+       qlj=1.d0
+      endif
+      sl=dlog(s/s2min)/dlog(spmax/s2min)*39.d0+1.d0
+      i=min(38,int(qli))
+      j=min(38,int(qlj))
+      k=min(38,int(sl))
+      wk(2)=sl-k
+      wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+      wk(1)=1.d0-wk(2)+wk(3)
+      wk(2)=wk(2)-2.d0*wk(3)
+      wi(2)=qli-i
+      wi(3)=wi(2)*(wi(2)-1.d0)*.5d0
+      wi(1)=1.d0-wi(2)+wi(3)
+      wi(2)=wi(2)-2.d0*wi(3)
+      wj(2)=qlj-j
+      wj(3)=wj(2)*(wj(2)-1.d0)*.5d0
+      wj(1)=1.d0-wj(2)+wj(3)
+      wj(2)=wj(2)-2.d0*wj(3)
+
+      do k1=1,3
+       k2=k+k1-1+ml
+      do i1=1,3
+      do j1=1,3
+       qgjit1=qgjit1+csj(i+i1-1,j+j1-1,k2)*wi(i1)*wj(j1)*wk(k1)
+      enddo
+      enddo
+      enddo
+      qgjit1=exp(qgjit1)*(1.d0/tmin-2.d0/s)
+      if(q2.lt.q1)qgjit1=qgjit1*qgsudx(q1,l)/qgsudx(q2,l)
+
+      if(debug.ge.3)write (moniou,202)qgjit1
+201   format(2x,'qgjit1 - ordered ladder cross section interpol.:'/4x,
+     *'s=',e10.3,2x,'q1=',e10.3,2x,'q2=',e10.3,2x,2x,'m=',i1,2x,'l=',i1)
+202   format(2x,'qgjit1=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qglam(s,a,b)
+c-----------------------------------------------------------------------------
+c kinematical function for two particle decay - maximal pt-value
+c a - first particle mass squared,
+c b - second particle mass squared,
+c s - two particle invariant mass
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)s,a,b
+
+      qglam=max(0.d0,.25d0/s*(s+a-b)**2-a)
+
+      if(debug.ge.3)write (moniou,202)qglam
+201   format(2x,'qglam - kinematical function s=',e10.3,2x,'a='
+     *,e10.3,2x,'b=',e10.3)
+202   format(2x,'qglam=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgnrm(ep)
+c-----------------------------------------------------------------------------
+c 4-vector squared calculation
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension ep(4)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)ep
+      qgnrm=(ep(1)-ep(2))*(ep(1)+ep(2))-ep(3)**2-ep(4)**2
+
+      if(debug.ge.3)write (moniou,202)qgnrm
+201   format(2x,'qgnrm - 4-vector squared for ','ep=',4(e10.3,1x))
+202   format(2x,'qgnrm=',e10.3)
+      return
+      end
+
+c===========================================================================
+      subroutine qgrec(ep,nqc,qv,zv,qm,iqv,ldau,lpar,jq)
+c---------------------------------------------------------------------------
+c jet reconstructuring procedure - 4-momenta for all final jets are determ.
+c ep(i) - jet 4-momentum
+c---------------------------------------------------------------------------
+c qv(i,j) - effective momentum for the branching of the parton in i-th row
+c on j-th level (0 - in case of no branching)
+c zv(i,j) - z-value for the branching of the parton in i-th row
+c on j-th level
+c qm(i,j) - mass squared for the parton in i-th row
+c on j-th level
+c iqv(i,j) - flavours for the parton in i-th row on j-th level
+c ldau(i,j) - first daughter row for the branching of the parton in i-th row
+c on j-th level
+c lpar(i,j) - the parent row for the parton in i-th row on j-th level
+c----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(njmax=50000)
+      dimension ep(4),ep3(4),epv(4,30,50),nqc(2),ncc(2,30,50),
+     *qv(30,50),zv(30,50),qm(30,50),iqv(30,50),
+     *ldau(30,49),lpar(30,50)
+c eqj(i,nj) - 4-momentum for the final jet nj
+c iqj(nj) - flavour for the final jet nj
+c ncj(m,nj) - colour connections for the final jet nj
+      common /qgarr37/ eqj(4,njmax),iqj(njmax),ncj(2,njmax),nj
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)jq,ep,iqv(1,1),nqc
+
+      do i=1,4
+       epv(i,1,1)=ep(i)
+      enddo
+      ncc(1,1,1)=nqc(1)
+      if(iqv(1,1).eq.0)ncc(2,1,1)=nqc(2)
+      nlev=1
+      nrow=1
+
+2     continue
+      if(qv(nrow,nlev).eq.0.d0)then
+       nj=nj+1
+       do i=1,4
+        eqj(i,nj)=epv(i,nrow,nlev)
+       enddo
+       iqj(nj)=iqv(nrow,nlev)
+       if(iabs(iqj(nj)).eq.3)iqj(nj)=iqj(nj)*4/3
+
+       if(iqj(nj).ne.0)then
+        njc=ncc(1,nrow,nlev)
+        if(njc.ne.0)then
+         ncj(1,nj)=njc
+         iqc=iqj(njc)
+         if(iqc.ne.0)then
+          ncj(1,njc)=nj
+         else
+          if(iqj(nj).gt.0)then
+           ncj(2,njc)=nj
+          else
+           ncj(1,njc)=nj
+          endif
+         endif
+        else
+         ncc(1,nrow,nlev)=nj
+        endif
+       else
+
+        do m=1,2
+         if(jq.eq.1)then
+          m1=m
+         else
+          m1=3-m
+         endif
+         njc=ncc(m1,nrow,nlev)
+         if(njc.ne.0)then
+          ncj(m,nj)=njc
+          iqc=iqj(njc)
+          if(iqc.ne.0)then
+           ncj(1,njc)=nj
+          else
+           ncj(3-m,njc)=nj
+          endif
+         else
+          ncc(m1,nrow,nlev)=nj
+         endif
+        enddo
+       endif
+       if(debug.ge.3)write (moniou,204)
+     * nj,nlev,nrow,iqj(nj),(eqj(i,nj),i=1,4)
+
+      else
+       do i=1,4
+         ep3(i)=epv(i,nrow,nlev)
+       enddo
+       call qgdefr(ep3,s0x,c0x,s0,c0)
+       z=zv(nrow,nlev)
+       qt2=(z*(1.d0-z))**2*qv(nrow,nlev)
+       ldrow=ldau(nrow,nlev)
+
+       wp0=ep3(1)+ep3(2)
+       wpi=z*wp0
+       wmi=(qt2+qm(ldrow,nlev+1))/wpi
+       ep3(1)=.5d0*(wpi+wmi)
+       ep3(2)=.5d0*(wpi-wmi)
+       qt=dsqrt(qt2)
+       call qgcs(c,s)
+       ep3(3)=qt*c
+       ep3(4)=qt*s
+       call qgrota(ep3,s0x,c0x,s0,c0)
+       do i=1,4
+        epv(i,ldrow,nlev+1)=ep3(i)
+       enddo
+       if(debug.ge.3)write (moniou,206)nlev+1,ldrow,ep3
+
+       wpi=(1.d0-z)*wp0
+       wmi=(qt2+qm(ldrow+1,nlev+1))/wpi
+       ep3(1)=.5d0*(wpi+wmi)
+       ep3(2)=.5d0*(wpi-wmi)
+       ep3(3)=-qt*c
+       ep3(4)=-qt*s
+       call qgrota(ep3,s0x,c0x,s0,c0)
+       do i=1,4
+        epv(i,ldrow+1,nlev+1)=ep3(i)
+       enddo
+       if(debug.ge.3)write (moniou,206)nlev+1,ldrow+1,ep3
+
+       if(iqv(nrow,nlev).eq.0)then
+        if(iqv(ldrow,nlev+1).ne.0)then
+         ncc(1,ldrow,nlev+1)=ncc(1,nrow,nlev)
+         ncc(1,ldrow+1,nlev+1)=ncc(2,nrow,nlev)
+        else
+         ncc(1,ldrow,nlev+1)=ncc(1,nrow,nlev)
+         ncc(2,ldrow,nlev+1)=0
+         ncc(1,ldrow+1,nlev+1)=0
+         ncc(2,ldrow+1,nlev+1)=ncc(2,nrow,nlev)
+        endif
+       else
+        if(iqv(ldrow,nlev+1).eq.0)then
+         ncc(1,ldrow,nlev+1)=ncc(1,nrow,nlev)
+         ncc(2,ldrow,nlev+1)=0
+         ncc(1,ldrow+1,nlev+1)=0
+        else
+         ncc(1,ldrow,nlev+1)=0
+         ncc(1,ldrow+1,nlev+1)=0
+         ncc(2,ldrow+1,nlev+1)=ncc(1,nrow,nlev)
+        endif
+       endif
+
+       nrow=ldrow
+       nlev=nlev+1
+       goto 2
+      endif
+
+8     continue
+      if(nlev.eq.1)then
+       if(nqc(1).eq.0)nqc(1)=ncc(1,1,1)
+       if(iqv(1,1).eq.0.and.nqc(2).eq.0)nqc(2)=ncc(2,1,1)
+       if(debug.ge.3)write (moniou,202)
+       return
+      endif
+
+      lprow=lpar(nrow,nlev)
+      if(ldau(lprow,nlev-1).eq.nrow)then
+       if(iqv(nrow,nlev).eq.0)then
+        if(ncc(1,lprow,nlev-1).eq.0)ncc(1,lprow,nlev-1)=ncc(1,nrow,nlev)
+        ncc(1,nrow+1,nlev)=ncc(2,nrow,nlev)
+       else
+        if(iqv(lprow,nlev-1).eq.0)then
+         if(ncc(1,lprow,nlev-1).eq.0)
+     *   ncc(1,lprow,nlev-1)=ncc(1,nrow,nlev)
+        else
+         ncc(1,nrow+1,nlev)=ncc(1,nrow,nlev)
+        endif
+       endif
+       nrow=nrow+1
+       goto 2
+      else
+       if(iqv(nrow,nlev).eq.0)then
+        if(iqv(lprow,nlev-1).eq.0)then
+         if(ncc(2,lprow,nlev-1).eq.0)
+     *   ncc(2,lprow,nlev-1)=ncc(2,nrow,nlev)
+        else
+         if(ncc(1,lprow,nlev-1).eq.0)
+     *   ncc(1,lprow,nlev-1)=ncc(2,nrow,nlev)
+        endif
+       else
+        if(iqv(lprow,nlev-1).eq.0.and.ncc(2,lprow,nlev-1).eq.0)
+     *  ncc(2,lprow,nlev-1)=ncc(1,nrow,nlev)
+       endif
+       nrow=lprow
+       nlev=nlev-1
+       goto 8
+      endif
+
+201   format(2x,'qgrec - jet reconstructuring: jq=',i1
+     */4x,'jet 4-momentum ep=',4(e10.3,1x)
+     */4x,'jet flavor: ',i2,2x,'colour connections: ',2i3)
+202   format(2x,'qgrec - end')
+204   format(2x,'qgrec: ',i3,'-th final jet at level nlev=',i2,' nrow='
+     *,i2/4x,'jet flavor: ',i3,2x,'jet 4-momentum:',4(e10.3,1x))
+206   format(2x,'qgrec: jet at level nlev='
+     *,i2,' nrow=',i2/4x,'jet 4-momentum:',4(e10.3,1x))
+      end
+
+c=============================================================================
+      double precision function qgroot(qlmax,g,j)
+c-----------------------------------------------------------------------------
+c qgroot - effective momentum tabulation for given set of random number
+c values and maximal effective momentum qmax values - according to the
+c probability of branching: (1 - timelike sudakov formfactor)
+c qlmax - ln qmax/16/qtf,
+c g - dzeta number (some function of ksi)
+c j - type of the parton (1-g,2-q)
+c------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)qlmax,g,j
+
+      ql0=0.d0
+      ql1=qlmax
+      f0=-g
+      f1=1.d0-g
+      sud0=-dlog(qgsudi(qlmax,j))
+
+1     ql2=ql1-(ql1-ql0)*f1/(f1-f0)
+      if(ql2.lt.0.d0)then
+       ql2=0.d0
+       f2=-g
+      elseif(ql2.gt.qlmax)then
+       ql2=qlmax
+       f2=1.d0-g
+      else
+       f2=-dlog(qgsudi(ql2,j))/sud0-g
+      endif
+      if(abs(f2).gt.1.d-3)then
+       if(f1*f2.lt.0.d0)then
+        ql0=ql1
+        f0=f1
+       endif
+       ql1=ql2
+       f1=f2
+       goto 1
+      else
+       qgroot=ql2
+      endif
+
+      if(debug.ge.3)write (moniou,202)qgroot
+201   format(2x,'qgqint - branching momentum tabulation:'
+     */4x,'qlmax=',e10.3,2x,'g=',e10.3,2x,'j=',i1)
+202   format(2x,'qgroot=',e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qgrota(ep,s0x,c0x,s0,c0)
+c-----------------------------------------------------------------------------
+c spacial rotation to the lab. system for 4-vector ep
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension ep(4),ep1(3)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)ep,s0x,c0x,s0,c0
+
+      ep1(3)=ep(4)
+      ep1(2)=ep(2)*s0+ep(3)*c0
+      ep1(1)=ep(2)*c0-ep(3)*s0
+      ep(2)=ep1(1)
+      ep(4)=ep1(2)*s0x+ep1(3)*c0x
+      ep(3)=ep1(2)*c0x-ep1(3)*s0x
+
+      if(debug.ge.3)write (moniou,202)ep
+201   format(2x,'qgrota - spacial rotation:'/4x,'4-vector ep=',4(e10.3
+     *,1x)/4x,'s0x=',e10.3,'c0x=',e10.3,2x,'s0=',e10.3,'c0=',e10.3)
+202   format(2x,'qgrota: rotated 4-vector ep=',2x,4e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgqint(qlmax,g,j)
+c-----------------------------------------------------------------------------
+c qgqint - effective momentum interpolation for given random number g
+c and maximal effective momentum qmax
+c qlmax - ln qmax/16/qtf,
+c g - random number (0<g<1)
+c j - type of the parton (1-g,2-q)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wi(3),wk(3)
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr34/ qrt(10,101,2)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)qlmax,g,j
+
+      qli=qlmax/1.38629d0
+      sud0=1.d0/qgsudi(qlmax,j)
+      sl=100.d0*dlog(1.d0-g*(1.d0-sud0))/dlog(sud0)
+      i=int(qli)
+      k=int(sl)
+      if(k.gt.98)k=98
+      wk(2)=sl-k
+      wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+      wk(1)=1.d0-wk(2)+wk(3)
+      wk(2)=wk(2)-2.d0*wk(3)
+      qgqint=0.d0
+      if(i.gt.7)i=7
+      wi(2)=qli-i
+      wi(3)=wi(2)*(wi(2)-1.d0)*.5d0
+      wi(1)=1.d0-wi(2)+wi(3)
+      wi(2)=wi(2)-2.d0*wi(3)
+      do k1=1,3
+      do i1=1,3
+       qgqint=qgqint+qrt(i+i1,k+k1,j)*wi(i1)*wk(k1)
+      enddo
+      enddo
+      if(qgqint.le.0.d0)qgqint=0.d0
+      qgqint=16.d0*qtf*exp(qgqint)
+
+      if(debug.ge.3)write (moniou,202)qgqint
+201   format(2x,'qgqint - branching momentum interpolation:'
+     */4x,'qlmax=',e10.3,2x,'g=',e10.3,2x,'j=',i1)
+202   format(2x,'qgqint=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgalf(qq)
+c-----------------------------------------------------------------------------
+c qgalf - alpha_s(qq)/2/pi
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      qgalf=2.d0/9.d0/dlog(qq)
+      return
+      end
+
+c=============================================================================
+      subroutine qgtran(ep,ey,jj)
+c-----------------------------------------------------------------------------
+c lorentz transform according to parameters ey ( determining lorentz shift
+c along the z,x,y-axis respectively (ey(1),ey(2),ey(3)))
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension ey(3),ep(4)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)ep,ey
+
+      if(jj.eq.1)then
+c lorentz transform to lab. system according to 1/ey(i) parameters
+       do i=1,3
+        if(ey(4-i).ne.1.d0)then
+         wp=(ep(1)+ep(5-i))/ey(4-i)
+         wm=(ep(1)-ep(5-i))*ey(4-i)
+         ep(1)=.5d0*(wp+wm)
+         ep(5-i)=.5d0*(wp-wm)
+        endif
+       enddo
+      else
+c lorentz transform to lab. system according to ey(i) parameters
+       do i=1,3
+        if(ey(i).ne.1.d0)then
+         wp=(ep(1)+ep(i+1))*ey(i)
+         wm=(ep(1)-ep(i+1))/ey(i)
+         ep(1)=.5d0*(wp+wm)
+         ep(i+1)=.5d0*(wp-wm)
+        endif
+       enddo
+      endif
+
+      if(debug.ge.3)write (moniou,202)ep
+201   format(2x,'qgtran - lorentz boost for 4-vector'/4x,'ep='
+     *,2x,4(e10.3,1x)/4x,'boost parameters ey=',3e10.3)
+202   format(2x,'qgtran: transformed 4-vector ep=',2x,4(e10.3,1x))
+      return
+      end
+
+c=============================================================================
+      double precision function qgsudi(qlmax,j)
+c-----------------------------------------------------------------------------
+c qgsudi - timelike sudakov formfactor interpolation
+c qlmax - ln qmax/16/qtf,
+c j - type of the parton (1-g,2-q)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wk(3)
+      common /qgarr33/ fsud(10,2)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)j,qlmax
+
+      ql=qlmax/1.38629d0
+      if(ql.le.0.d0)then
+       qgsudi=1.d0
+      else
+       k=int(ql)
+       if(k.gt.7)k=7
+       wk(2)=ql-k
+       wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+       wk(1)=1.d0-wk(2)+wk(3)
+       wk(2)=wk(2)-2.d0*wk(3)
+
+       qgsudi=0.d0
+       do k1=1,3
+        qgsudi=qgsudi+fsud(k+k1,j)*wk(k1)
+       enddo
+       if(qgsudi.le.0.d0)qgsudi=0.d0
+       qgsudi=exp(-qgsudi)
+      endif
+
+      if(debug.ge.3)write (moniou,202)qgsudi
+201   format(2x,'qgsudi - spacelike form factor interpolation:'
+     */4x,'parton type j=',i1,2x,'momentum logarithm qlmax=',e10.3)
+202   format(2x,'qgsudi=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgsudx(q,j)
+c-----------------------------------------------------------------------------
+c qgsudx - spacelike sudakov formfactor
+c q - maximal value of the effective momentum,
+c j - type of parton (1 - g, 2 - q)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr43/ moniou
+      common /qgarr51/ epsxmn
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)j,q
+
+      if(q.gt.1.d0)then
+       qgsudx=dlog(dlog(q/alm)/dlog(1.d0/alm))*(.75d0+dlog(epsxmn))
+       if(j.eq.1)then
+        qgsudx=exp(qgsudx/.75d0)
+       else
+        qgsudx=exp(qgsudx*16.d0/27.d0)
+       endif
+      else
+       qgsudx=1.d0
+      endif
+
+      if(debug.ge.3)write (moniou,202)qgsudx
+201   format(2x,'qgsudx - spacelike form factor: parton type j='
+     *,i1,2x,'momentum q=',e10.3)
+202   format(2x,'qgsudx=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgsudt(qmax,j)
+c-----------------------------------------------------------------------------
+c qgsudt - timelike sudakov formfactor
+c qmax - maximal value of the effective momentum,
+c j - type of parton (1 - g, 2 - q)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common/arr3/x1(7),a1(7)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)j,qmax
+
+      qgsudt=0.d0
+      qlmax=dlog(dlog(qmax/16.d0/alm))
+      qfl=dlog(dlog(qtf/alm))
+c numerical integration over transverse momentum square;
+c gaussian integration is used
+      do i=1,7
+      do m=1,2
+       qtl=.5d0*(qlmax+qfl+(2*m-3)*x1(i)*(qlmax-qfl))
+       qt=alm*exp(exp(qtl))
+       if(qt.ge.qmax/16.d0)qt=qmax/16.0001d0
+       zmin=.5d0-dsqrt((.25d0-dsqrt(qt/qmax)))
+       zmax=1.d0-zmin
+
+       if(j.eq.1)then
+        ap=(qgapi(zmax,1,1)-qgapi(zmin,1,1)+
+     *  qgapi(zmax,1,2)-qgapi(zmin,1,2))*.5d0
+       else
+        ap=qgapi(zmax,2,1)-qgapi(zmin,2,1)
+       endif
+       qgsudt=qgsudt+a1(i)*ap
+      enddo
+      enddo
+      qgsudt=qgsudt*(qlmax-qfl)/9.d0
+
+      if(debug.ge.3)write (moniou,202)qgsudt
+201   format(2x,'qgsudt - timelike form factor: parton type j='
+     *,i1,2x,'momentum qmax=',e10.3)
+202   format(2x,'qgsudt=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgtwd(s,a,b)
+c-----------------------------------------------------------------------------
+c kinematical function for two particle decay - light cone momentum share
+c for the particle of mass squared a,
+c b - partner's mass squared,
+c s - two particle invariant mass
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)s,a,b
+
+      x=.5d0*(1.d0+(a-b)/s)
+      dx=x-dsqrt(a/s)
+      if(dx.gt.0.d0)then
+       x=x+dsqrt(dx)*dsqrt(x+dsqrt(a/s))
+      else
+       x=dsqrt(a/s)
+      endif
+      qgtwd=x
+
+      if(debug.ge.3)write (moniou,202)qgtwd
+201   format(2x,'qgtwd: s=',e10.3,2x,'a=',e10.3,2x,'b=',e10.3)
+202   format(2x,'qgtwd=',e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qgvdef(ich,ic1,ic2,icz)
+c-----------------------------------------------------------------------------
+c determination of valence quark flavour -
+c for valence quark hard scattering
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr11/ b10
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      EXTERNAL qgran
+
+      if(debug.ge.2)write (moniou,201)ich,icz
+
+      is=iabs(ich)/ich
+      if(icz.eq.1)then
+       ic1=ich*(1-3*int(.5d0+qgran(b10)))
+       ic2=-ic1-ich
+      elseif(icz.eq.2)then
+       if(qgran(b10).gt..33333d0.or.ich.lt.0)then
+        ic1=ich-is
+        ic2=3*is
+       else
+        ic1=4*is-ich
+        ic2=ich+4*is
+       endif
+      elseif(icz.eq.3)then
+       ic1=ich-3*is
+       ic2=-4*is
+      elseif(icz.eq.4)then
+       ic1=ich-9*is
+       ic2=5*is
+      endif
+
+      if(debug.ge.3)write (moniou,202)ic1,ic2
+201   format(2x,'qgvdef: hadron type ich=',i2,' auxilliary type icz='
+     *,i1)
+202   format(2x,'qgvdef-end: parton flavors ic1=',i2,
+     *'ic2=',i2)
+      return
+      end
+
+c=============================================================================
+      double precision function qgzsim(qq,j)
+c-----------------------------------------------------------------------------
+c qgzsim - light cone momentum share simulation (for the timelike
+c branching)
+c qq - effective momentum value,
+c j - type of the parent parton (1-g,2-q)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr11/ b10
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      EXTERNAL qgran
+
+      if(debug.ge.2)write (moniou,201)qq,j
+
+      zmin=.5d0-dsqrt(.25d0-dsqrt(qtf/qq))
+      qlf=dlog(qtf/alm)
+1     continue
+      if(j.eq.1)then
+       qgzsim=.5d0*(2.d0*zmin)**qgran(b10)
+       gb=qgzsim*(qgfap(qgzsim,1,1)+qgfap(qgzsim,1,2))/7.5d0
+      else
+       qgzsim=zmin*((1.d0-zmin)/zmin)**qgran(b10)
+       gb=qgzsim*qgfap(qgzsim,2,1)*.375d0
+      endif
+      qt=qq*(qgzsim*(1.d0-qgzsim))**2
+      gb=gb/dlog(qt/alm)*qlf
+      if(debug.ge.3)write (moniou,203)qt,gb
+      if(qgran(b10).gt.gb)goto 1
+
+      if(debug.ge.3)write (moniou,202)qgzsim
+201   format(2x,'qgzsim - z-share simulation: qq=',e10.3,2x,'j=',i1)
+202   format(2x,'qgzsim=',e10.3)
+203   format(2x,'qgzsim: qt=',e10.3,2x,'gb=',e10.3)
+      return
+      end
+
+c===========================================================================
+      subroutine qgixxd(ich,ic1,ic2,icz)
+c---------------------------------------------------------------------------
+c determination of parton flavours for valence quark soft interaction
+c (charge exchange)
+c---------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr8/  wwm,be(4),dc(5),deta,almpt,ptdif,ptndi
+      common /qgarr11/ b10
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      EXTERNAL qgran
+
+      if(debug.ge.2)write (moniou,201)ich,icz
+
+      is=iabs(ich)/ich
+      if(icz.eq.1)then                      !pion
+       ic1=ich*(1-3*int(.5d0+qgran(b10)))
+       if(qgran(b10).lt.dc(2))then
+        ic2=-4*ic1/iabs(ic1)
+        if(iabs(ic1).eq.1)then
+         ich1=-5*is
+        else
+         ich1=4*is
+        endif
+       else
+        ich1=ich*int(.5d0+qgran(b10))
+        ic2=-ic1*iabs(ich1)-(ich+ic1)*iabs(ich-ich1)
+       endif
+      elseif(icz.eq.2)then
+c valence quark type simulation ( for proton )
+       ic1=int(1.3333d0+qgran(b10))
+c leading nucleon type simulation ( flavors combinatorics )
+       if(ic1.eq.1)then
+        ich1=int(qgran(b10)+.5d0)+2
+        ic2=1-ich1
+       elseif(qgran(b10).lt..5d0)then
+        ich1=2
+        ic2=-2
+       else
+        ich1=7                   !uuu
+        ic2=-1
+       endif
+       if(iabs(ich).eq.3)then    !neutron
+        ic1=3-ic1
+        ic2=-3-ic2
+        if(ich1.eq.7)then
+         ich1=8                  !ddd
+        else
+         ich1=5-ich1
+        endif
+       endif
+       if(ich.lt.0)then
+        ic1=-ic1
+        ic2=-ic2
+        ich1=-ich1
+       endif
+      elseif(icz.eq.3)then
+       ic1=ich-3*is
+       ic2=-is*int(1.5d0+qgran(b10))
+       ich1=3*is-ic2
+      elseif(icz.eq.4)then
+       ic1=ich-9*is
+       ic2=is*int(1.5d0+qgran(b10))
+       ich1=9*is-ic2
+      elseif(icz.eq.5)then
+       ic1=is*int(1.5d0+qgran(b10))
+       ic2=-ic1
+       ich1=ich
+      else
+       ich1=0
+       stop 'Should not happen in qgixxd !'
+      endif
+      ich=ich1
+
+      if(debug.ge.3)write (moniou,202)ic1,ic2,ich
+201   format(2x,'qgixxd: hadron type ich=',i2,' auxilliary type icz='
+     *,i1)
+202   format(2x,'qgixxd-end: parton flavors ic1=',i2,' ic2='
+     *,i2,'new hadron type ich=',i2)
+      return
+      end
+
+c=============================================================================
+      subroutine qgdifr(wppr,wmtg,izp,izt,jexpr,jextg,iret)
+c-----------------------------------------------------------------------------
+c qgdifr - treatment of diffraction dissociation / leading hadron states
+c wppr - LC momentum for projectile remnant;
+c wptg - LC momentum for target remnant;
+c izp  - projectile remnant type;
+c izt  - target remnant type;
+c jexpr/jextg = -2 - low mass diffraction;
+c             = -1 - more collisions to follow;
+c             =  0 - no excitation;
+c             >  0 - low mass excitation
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension ey(3),ep(4)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr2/  scm,wp0,wm0
+      common /qgarr6/  pi,bm,amws
+      common /qgarr8/  wwm,be(4),dc(5),deta,almpt,ptdif,ptndi
+      common /qgarr10/ am(7),ammu
+      common /qgarr11/ b10
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr21/ dmmin(3),wex(3),dmres(3),wdres(3)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      EXTERNAL qgran
+
+      if(debug.ge.2)write (moniou,201)izp,izt,wppr,wmtg
+
+      iret=0
+      jexip=0
+      jexit=0
+      ddmin1=0.d0
+      ddmax1=0.d0
+c check if remnants are excited to low mass states
+      if(jexpr.eq.-2.or.jexpr.gt.0.and.qgran(b10)
+     *.lt.1.d0-(1.d0-wex(icz))**dble(jexpr).and.iabs(izp).lt.7)jexip=1
+      if(jextg.eq.-2.or.jextg.gt.0.and.qgran(b10)
+     *.lt.1.d0-(1.d0-wex(2))**dble(jextg).and.iabs(izt).lt.7)jexit=1
+c add low mass excitations if no particles produced before
+      if(wppr.ge.wp0.and.jexpr.gt.0.and.jexip.eq.0.and.iabs(izp).lt.7)
+     *jexip=1
+      if(wmtg.ge.wm0.and.jextg.gt.0.and.jexit.eq.0.and.iabs(izt).lt.7)
+     *jexit=1
+
+      sd0=wppr*wmtg                          !energy squared available
+      if(jextg.eq.-1)then                    !more collisions to follow
+       dmass2=0.d0
+       ddmin2=0.d0
+      elseif(jexit.eq.0)then                 !no excitation
+       if(iabs(izt).eq.7.or.iabs(izt).eq.8)then  !delta++/-
+        dmass2=dmmin(2)
+       else
+        dmass2=am(2)
+       endif
+       ddmin2=dmass2
+      else                                   !low mass excitation
+       ddmin2=dmmin(2)
+       if(jextg.eq.-2)ddmin2=dmres(2)        !low mass diffraction
+      endif
+      if(jexpr.eq.-1)then                    !more collisions to follow
+       dmass1=0.d0
+      elseif(jexip.eq.0)then                 !no excitation
+       if(iabs(izp).eq.7.or.iabs(izp).eq.8)then  !delta++/-
+        dmass1=dmmin(2)
+       elseif(izp.eq.0)then                      !rho0
+        dmass1=dmmin(1)
+        izp=-10
+       else
+        dmass1=am(icz)
+       endif
+      else                                   !low mass excitation
+       ddmin1=dmmin(icz)
+       if(jexpr.eq.-2)ddmin1=dmres(icz)        !low mass diffraction
+       ddmax1=dsqrt(sd0)-ddmin2
+      endif
+
+
+c determine mass for projectile excited remnant
+      if(jexip.eq.1)then
+       if(jexpr.ne.-2)then                   !low mass excitation (dM/M^2)
+        if(ddmax1.gt.ddmin1)then
+         dmass1=ddmin1/(1.d0-qgran(b10)*(1.d0-ddmin1/ddmax1))
+        else
+         dmass1=ddmin1
+        endif
+       else                                  !low mass diffraction (res. + PPR)
+        ddmin=dmmin(icz)+am(1)
+        ddmax=min(ddmax1,dmres(icz)+.5d0*wdres(icz))
+        ddmax=max(ddmax,ddmin)
+        wres=1.d0/(1.d0+.5d0*(1.d0+2.d0*dmres(icz)/wdres(icz))
+     *  *(1.d0-(dmres(icz)+.5d0*wdres(icz))
+     *  /max(ddmax1,dmres(icz)+.5d0*wdres(icz)))
+     *  /(.25d0*pi+atan(2.d0*(dmres(icz)-ddmin)/wdres(icz))))
+        if(qgran(b10).gt.wres)then           !PPR contribution
+         dmass1=ddmax/(1.d0-qgran(b10)*(1.d0-ddmax/ddmax1))
+        else                                 !resonance contribution
+         dmass1=dmres(icz)+.5d0*wdres(icz)
+     *   *tan(atan(2.d0*(ddmax-dmres(icz))/wdres(icz))
+     *   -qgran(b10)*(atan(2.d0*(ddmax-dmres(icz))/wdres(icz))
+     *   +atan(2.d0*(dmres(icz)-ddmin)/wdres(icz))))
+         jexip=0
+         izp=izp+10*izp/iabs(izp)
+        endif
+       endif
+      endif
+
+c determine mass for target excited remnant
+      if(jexit.eq.1)then
+       ddmax2=dsqrt(sd0)-dmass1
+       if(jextg.ne.-2)then                   !low mass excitation (dM/M^2)
+        if(ddmax2.gt.ddmin2)then
+         dmass2=ddmin2/(1.d0-qgran(b10)*(1.d0-ddmin2/ddmax2))
+        else                                  !low mass diffraction
+         dmass2=ddmin2
+        endif
+       else                                  !low mass diffraction (res. + PPR)
+        ddmin=dmmin(2)+am(1)
+        ddmax=min(ddmax2,dmres(2)+.5d0*wdres(2))
+        ddmax=max(ddmax,ddmin)
+        wres=1.d0/(1.d0+.5d0*(1.d0+2.d0*dmres(2)/wdres(2))
+     *  *(1.d0-(dmres(2)+.5d0*wdres(2))/max(ddmax2,dmres(2)+.5d0
+     *  *wdres(2)))/(.25d0*pi+atan(2.d0*(dmres(2)-ddmin)/wdres(2))))
+        if(qgran(b10).gt.wres)then           !PPR contribution
+         dmass2=ddmax/(1.d0-qgran(b10)*(1.d0-ddmax/ddmax2))
+        else                                 !resonance contribution
+         dmass2=dmres(2)+.5d0*wdres(2)*tan(atan(2.d0*(ddmax-dmres(2))
+     *   /wdres(2))-qgran(b10)*(atan(2.d0*(ddmax-dmres(2))/wdres(2))
+     *   +atan(2.d0*(dmres(2)-ddmin)/wdres(2))))
+         izt=izt+10*izt/iabs(izt)
+         jexit=0
+        endif
+       endif
+      endif
+
+      wpp=wppr
+      wpm=wmtg
+      if(sd0.lt.(dmass1+dmass2)**2)then
+       iret=1
+       return
+      endif
+      dmass1=dmass1**2
+      dmass2=dmass2**2
+
+      if(jexpr.ne.-1.and.jextg.ne.-1)then
+       ptmax=max(0.d0,qglam(sd0,dmass1,dmass2))
+       if(jexpr.eq.-2.or.jextg.eq.-2)then
+        ptmean=ptdif
+       else
+        ptmean=ptndi*dsqrt(dble(max(jexpr,jextg)))
+       endif
+       if(ptmax.lt.ptmean**2)then
+1       pti=ptmax*qgran(b10)
+        if(qgran(b10).gt.exp(-dsqrt(pti)/ptmean))goto 1
+       else
+2       pti=(ptmean*dlog(qgran(b10)*qgran(b10)))**2
+        if(pti.gt.ptmax)goto 2
+       endif
+      else
+       pti=0.d0
+      endif
+      amt1=dmass1+pti
+      amt2=dmass2+pti
+      wpd1=wpp*qgtwd(sd0,amt1,amt2)
+      if(wpd1.gt.0.d0)then
+       wmd1=amt1/wpd1
+      else
+       wmd1=0.d0
+      endif
+      wmd2=wpm-wmd1
+      if(wmd2.gt.0.d0)then
+       wpd2=amt2/wmd2
+      else
+       wpd2=0.d0
+      endif
+      pt=dsqrt(pti)
+      call qgcs(c,s)
+
+      if(jexpr.eq.-1)then
+       wppr=wpd1
+       if(wmd1.ne.0.d0)stop'wmd1.ne.0!!!'
+      else
+       ep(1)=.5d0*(wpd1+wmd1)
+       ep(2)=.5d0*(wpd1-wmd1)
+       ep(3)=pt*c
+       ep(4)=pt*s
+       wppr=0.d0
+       if(jexip.eq.0)then
+        call qgreg(ep,izp)
+       else
+        is=0
+        if(izp.ne.0)is=iabs(izp)/izp
+        if(icz.eq.1)then
+         if(iabs(izp).ge.4)then
+          ic2=-4*is
+          ic1=izp-3*is
+         elseif(izp.ne.0)then
+          ic1=izp*(1-3*int(.5d0+qgran(b10)))
+          ic2=-izp-ic1
+         else
+          ic1=int(1.5d0+qgran(b10))*(2*int(.5d0+qgran(b10))-1)
+          ic2=-ic1
+         endif
+        elseif(icz.eq.2)then
+         if(qgran(b10).gt..33333d0)then
+          ic1=3*is
+          ic2=izp-is
+         else
+          ic1=izp+4*is
+          ic2=4*is-izp
+         endif
+        elseif(icz.eq.3)then
+         ic1=-4*is
+         ic2=izp-3*is
+        endif
+        call qgdeft(dmass1,ep,ey)
+        call qggene(dsqrt(dmass1),dsqrt(dmass1),ey
+     *  ,0.d0,1.d0,0.d0,1.d0,ic1,ic2)
+       endif
+      endif
+
+      if(jextg.eq.-1)then
+       wmtg=wmd2
+       if(wpd2.ne.0.d0)stop'wpd2.ne.0!!!'
+      else
+       ep(1)=.5d0*(wpd2+wmd2)
+       ep(2)=.5d0*(wpd2-wmd2)
+       ep(3)=-pt*c
+       ep(4)=-pt*s
+       wmtg=0.d0
+       if(jexit.eq.0)then
+        call qgreg(ep,izt)
+       else
+        is=iabs(izt)/izt
+        if(qgran(b10).gt..33333d0)then
+         ic1=3*is
+         ic2=izt-is
+        else
+         ic1=izt+4*is
+         ic2=4*is-izt
+        endif
+        call qgdeft(dmass2,ep,ey)
+        call qggene(dsqrt(dmass2),dsqrt(dmass2),ey
+     *  ,0.d0,1.d0,0.d0,1.d0,ic2,ic1)
+       endif
+      endif
+
+      if(debug.ge.3)write (moniou,202)
+201   format(2x,'qgdifr - leading clusters hadronization:'
+     */4x,'cluster types izp=',i2,2x,
+     *'izt=',i2/4x,'available light cone momenta: wppr=',e10.3,
+     *' wmtg=',e10.3)
+202   format(2x,'qgdifr - end')
+      return
+      end
+
+c=============================================================================
+      subroutine qgfau(b,gz)
+c-----------------------------------------------------------------------------
+c integrands for hadron-hadron and hadron-nucleus cross-sections calculation
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208)
+      dimension gz(3),gz0(5)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /qgarr5/  rnuc(2),wsnuc(2),wbnuc(2),anorm
+     *,cr1(2),cr2(2),cr3(2)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)b
+
+      do l=1,3
+       gz(l)=0.d0
+      enddo
+
+      ab=float(ia(2))
+      do iddp1=1,2
+      do iddp2=1,2
+       call qgfz(b,gz0,iddp1,iddp2)
+       if(iddp1.eq.iddp2)gz(1)=gz(1)+(1.d0-gz0(1)*anorm)**ab
+     * *cc(iddp1,icz)
+       do l=2,3
+        gz(l)=gz(l)+(1.d0-gz0(l-1)*anorm)**ab
+     *  *cc(iddp1,icz)*cc(iddp2,icz)
+       enddo
+      enddo
+      enddo
+
+      gz(3)=gz(2)-gz(3)
+      gz(2)=gz(1)-gz(2)
+      gz(1)=1.d0-gz(1)
+
+      if(debug.ge.2)write (moniou,203)gz
+      if(debug.ge.3)write (moniou,202)
+201   format(2x,'qgfau - integrands for hadron-hadron and hadron'
+     *,'-nucleus cross-sections calculation'/4x,'b=',e10.3)
+202   format(2x,'qgfau - end')
+203   format(2x,'qgfau: gz=',3e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qgfrag(sa,na,rc)
+c-----------------------------------------------------------------------------
+c connected nucleon clasters extraction - used for the nuclear spectator part
+c multifragmentation
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(iapmax=208)
+      dimension sa(iapmax,3)
+      common /qgarr13/ nsf,iaf(iapmax)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)na
+      if(debug.ge.3)then
+       write (moniou,203)
+       do i=1,na
+        write (moniou,204)(sa(i,l),l=1,3)
+       enddo
+      endif
+
+      ni=1
+      ng=1
+      j=0
+1     j=j+1
+      j1=ni+1
+
+      do 4 i=j1,na
+       ri=0.d0
+       do m=1,3
+        ri=ri+(sa(j,m)-sa(i,m))**2
+       enddo
+       if(ri.gt.rc)goto 4
+
+       ni=ni+1
+       ng=ng+1
+       if(i.eq.ni)goto 4
+       do m=1,3
+        s0=sa(ni,m)
+        sa(ni,m)=sa(i,m)
+        sa(i,m)=s0
+       enddo
+4     continue
+
+      if(j.lt.ni.and.na-ni.gt.0)goto 1
+      nsf=nsf+1
+      iaf(nsf)=ng
+      if(debug.ge.3)write (moniou,206)nsf,iaf(nsf)
+
+      ng=1
+      j=ni
+      ni=ni+1
+      if(na.eq.ni)then
+       nsf=nsf+1
+       iaf(nsf)=1
+       if(debug.ge.3)write (moniou,206)nsf,iaf(nsf)
+      elseif(na.gt.ni)then
+       goto 1
+      endif
+
+      if(debug.ge.3)write (moniou,202)
+201   format(2x,'qgfrag-multifragmentation: nucleus mass number: na='
+     *,i2)
+202   format(2x,'qgfrag - end')
+203   format(2x,'nucleons coordinates:')
+204   format(2x,3e10.3)
+206   format(2x,'qgfrag: fragment n',i2,2x,'fragment mass - ',i2)
+      return
+      end
+
+c=============================================================================
+      subroutine qgfrgm(ns,xa)
+c-----------------------------------------------------------------------------
+c fragmentation of the spectator part of the nucleus
+c xa - array for spectator nucleons positions
+c ns - total number of spectators
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      parameter(iapmax=208)
+      dimension xa(iapmax,3)
+      integer debug
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr3/  rmin,emax,eev
+      common /qgarr11/ b10
+c nsf - number of secondary fragments;
+c iaf(i) - mass of the i-th fragment
+      common /qgarr13/ nsf,iaf(iapmax)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      EXTERNAL qgran
+
+      if(debug.ge.2)write (moniou,201)ns
+
+      nsf=0
+      if(ns.eq.0)then                  !no fragments
+       return
+      elseif(ns.eq.1)then              !single spectator nucleon recorded
+       nsf=nsf+1
+       iaf(nsf)=1
+       if(debug.ge.3)write (moniou,205)
+       return
+      endif
+
+      eex=0.d0                         !excitation energy for spectator part
+           !sum of excitations due to wounded nucleons (including diffractive)
+      do i=1,ia(1)-ns
+c partial excitation according to f(e) ~ 1/sqrt(e) * exp(-e/(2*<e>))
+       eex=eex+(qgran(b10)+qgran(b10)+qgran(b10)+
+     * qgran(b10)+qgran(b10)-2.5d0)**2*2.4d0
+      enddo
+      if(debug.ge.3)write (moniou,203)eex
+
+      if(eex/ns.gt.emax)then    !if eex>emax -> multifragmentation
+       call qgfrag(xa,ns,rmin)  !multifragmentation (percolation algorithm)
+      else                      !otherwise eveporation
+       nf=npgen(eex/eev,0,ns-1) !number of eveporated nucleons (mean=eex/eev)
+       nsf=nsf+1
+       iaf(nsf)=ns-nf           !recording of the fragment produced
+       if(debug.ge.3)write (moniou,206)iaf(nsf)
+
+       nal=nf/4                 !number of evapotared alphas (taken as nf/4)
+       if(nal.ne.0)then
+        do i=1,nal              !recording the evaporated alphas
+         nsf=nsf+1
+         iaf(nsf)=4
+        enddo
+       endif
+       nf=nf-4*nal
+
+       if(nf.ne.0)then
+        do i=1,nf               !recording the evaporated nucleons
+         nsf=nsf+1
+         iaf(nsf)=1
+        enddo
+       endif
+       if(debug.ge.3)write (moniou,204)nf,nal
+      endif
+c6     continue
+
+      if(debug.ge.3)write (moniou,202)
+201   format(2x,'qgfrgm: number of spectators: ns=',i2)
+202   format(2x,'qgfrgm - end')
+203   format(2x,'qgfrgm: excitation energy: eex=',e10.3)
+204   format(2x,'qgfrgm - evaporation: number of nucleons nf='
+     *,i2,'number of alphas nal=',i2)
+205   format(2x,'qgfrgm - single spectator')
+206   format(2x,'qgfrgm - evaporation: mass number of the fragment:',i2)
+      return
+      end
+
+c=============================================================================
+      subroutine qggau(gz)
+c-----------------------------------------------------------------------------
+c impact parameter integration for impact parameters <bm -
+c for hadron-hadron and hadron-nucleus cross-sections calculation
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension gz(3),gz0(3)
+      common /qgarr5/  rnuc(2),wsnuc(2),wbnuc(2),anorm
+     *,cr1(2),cr2(2),cr3(2)
+      common /qgarr6/  pi,bm,amws
+      common /arr3/   x1(7),a1(7)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)
+
+      do i=1,3
+       gz(i)=0.d0
+      enddo
+      do i=1,7
+      do m=1,2
+       b=bm*dsqrt(.5d0+x1(i)*(m-1.5d0))
+       call qgfau(b,gz0)
+       do l=1,3
+        gz(l)=gz(l)+gz0(l)*a1(i)
+       enddo
+      enddo
+      enddo
+
+      do l=1,3
+       gz(l)=gz(l)*bm**2*pi*.5d0
+      enddo
+
+      if(debug.ge.3)write (moniou,202)
+201   format(2x,'qggau - nuclear cross-sections calculation')
+202   format(2x,'qggau - end')
+      return
+      end
+
+c=============================================================================
+      subroutine qggau1(gz)
+c-----------------------------------------------------------------------------
+c impact parameter integration for impact parameters >bm -
+c for hadron-hadron and hadron-nucleus cross-sections calculation
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension gz(3),gz0(3)
+      common /qgarr5/  rnuc(2),wsnuc(2),wbnuc(2),anorm
+     *,cr1(2),cr2(2),cr3(2)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr43/ moniou
+      common /arr3/   x1(7),a1(7)
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)
+
+      do i=1,7
+      do m=1,2
+       b=bm-wsnuc(2)*dlog(.5d0+x1(i)*(m-1.5d0))
+       call qgfau(b,gz0)
+       do l=1,3
+        gz(l)=gz(l)+gz0(l)*a1(i)*exp((b-bm)/wsnuc(2))*b*pi*wsnuc(2)
+       enddo
+      enddo
+      enddo
+
+      if(debug.ge.3)write (moniou,202)
+201   format(2x,'qggau1 - nuclear cross-sections calculation')
+202   format(2x,'qggau1 - end')
+      return
+      end
+
+c=============================================================================
+      double precision function qganrm(rnuc,wsnuc,wbnuc)
+c-----------------------------------------------------------------------------
+c impact parameter integration for impact parameters <bm -
+c for hadron-hadron and hadron-nucleus cross-sections calculation
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /arr3/   x1(7),a1(7)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)
+
+      qganrm=0.d0
+      do i=1,7
+      do m=1,2
+       r=rnuc*(.5d0+x1(i)*(m-1.5d0))**(1.d0/3.d0)
+       quq=(r-rnuc)/wsnuc
+       if(quq.lt.1.d80)qganrm=qganrm+a1(i)/(1.d0+exp(quq))
+     * *(1.d0+wbnuc*(r/rnuc)**2)
+      enddo
+      enddo
+      qganrm=qganrm*rnuc**3*pi/1.5d0
+
+      dnrm=0.d0
+      do i=1,7
+      do m=1,2
+       t=.5d0+x1(i)*(m-1.5d0)
+       r=rnuc-wsnuc*log(t)
+       dnrm=dnrm+a1(i)/(1.d0+t)*r*r
+     * *(1.d0+wbnuc*(r/rnuc)**2)
+      enddo
+      enddo
+      qganrm=1.d0/(qganrm+dnrm*2.d0*pi*wsnuc)
+
+      if(debug.ge.3)write (moniou,202)qganrm
+201   format(2x,'qganrm - nuclear density normalization')
+202   format(2x,'qganrm=',e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qggene(wp0,wm0,ey0,s0x,c0x,s0,c0,ic1,ic2)
+c-----------------------------------------------------------------------------
+c to simulate the fragmentation of the string into secondary hadrons
+c the algorithm conserves energy-momentum;
+c wp0, wm0 are initial longitudinal momenta ( e+p, e-p ) of the quarks
+c at the ends of the string; ic1, ic2 - their types
+c the following partons types are used: 1 - u, -1 - U, 2 - d, -2 - D,
+c 3 - ud, -3 - UD, 4 - s, -4 - S, 6 - uu, -6 - UU, 7 - dd, -7 - DD,
+c 8 - us, -8 - US
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      character *2 tyq
+      dimension wp(2),ic(2),ept(4),ep(4),ey(3),ey0(3)
+c wp(1), wp(2) - current longitudinal momenta of the partons at the string
+c ends, ic(1), ic(2) - their types
+      common /qgarr8/  wwm,bep,ben,bek,bec,dc(5),deta,almpt,ptdif
+     *,ptndi
+      common /qgarr10/ am0,amn,amk,amc,amlamc,amlam,ameta,ammu
+      common /qgarr11/ b10
+      common /qgarr19/ ahl(3)
+      common /qgarr28/ arr(5)
+      common /qgarr42/ tyq(16)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      external qgran
+
+      if(debug.ge.2)write (moniou,201)tyq(8+ic1),tyq(8+ic2)
+     *,wp0,wm0,ey0,s0x,c0x,s0,c0
+
+      ww=wp0*wm0                              !mass squared for the string
+      ept(1)=.5d0*(wp0+wm0)                   !4-momentum for the string
+      ept(2)=.5d0*(wp0-wm0)
+      ept(3)=0.d0
+      ept(4)=0.d0
+
+      if(iabs(ic1).eq.5.or.iabs(ic2).eq.5.or.iabs(ic1).gt.8
+     *.or.iabs(ic2).gt.8)stop'qggene: problem with parton types'
+
+      ic(1)=ic1                               !parton types at string ends
+      ic(2)=ic2
+
+1     sww=dsqrt(ww)
+      call qgdeft(ww,ept,ey)                  !boost to c.m.  for the string
+      j=int(2.d0*qgran(b10))+1                !choose string end to start
+
+      if(debug.ge.3)then
+       iqt=8+ic(j)
+       write (moniou,203)j,tyq(iqt),ww
+      endif
+
+      iab=iabs(ic(j))
+      is=ic(j)/iab
+      if(iab.eq.8)then
+       iab=6
+      elseif(iab.gt.5)then
+       iab=3
+      endif
+      iaj=iabs(ic(3-j))
+      if(iaj.eq.8)then
+       iaj=6
+      elseif(iaj.gt.5)then
+       iaj=3
+      endif
+      if(iab.eq.5)stop'no charm anymore!'
+
+      if(iaj.eq.3)then
+       restm=amn
+      elseif(iaj.eq.4)then
+       restm=amk
+      elseif(iaj.eq.5)then
+       stop'no charm anymore!'
+      elseif(iaj.eq.6)then
+       restm=amlam
+      else
+       restm=am0
+      endif
+
+      if(iab.le.2.and.sww.gt.restm+2.d0*am0+wwm
+     *.or.iab.eq.3.and.sww.gt.restm+am0+amn+wwm
+     *.or.iab.eq.4.and.sww.gt.restm+am0+amk+wwm
+     *.or.iab.eq.6.and.sww.gt.restm+am0+amlam+wwm)then !more than 2 particles
+       blf=0.d0
+       bet=0.d0
+       alf=0.d0
+       if(iab.le.2)then                                !light quark string end
+        if(iab.eq.2.and.iabs(ic(3-j)).ne.7
+     *  .and.sww.gt.restm+2.d0*amlam.and.qgran(b10).lt.dc(1)*dc(2))then
+c lambda generation
+         restm=(restm+amlam)**2
+         bet=ben
+         ami=amlam**2
+         alf=almpt-arr(2)+arr(1)-arr(3)
+         blf=1.d0-arr(2)-arr(3)
+         ic0=6*is                                      !(anti-)lambda
+         ic(j)=-8*is                                   !US(us)
+        elseif(sww.gt.restm+2.d0*amn.and.qgran(b10).lt.dc(1))then
+c nucleon generation
+         restm=(restm+amn)**2
+         bet=ben
+         ami=amn**2
+         alf=almpt-arr(2)
+         blf=1.d0-arr(1)-arr(2)
+         ic0=ic(j)+is
+         ic(j)=-3*is
+        elseif(sww.gt.restm+2.d0*amk.and.qgran(b10).lt.dc(2))then
+c kaon generation
+         restm=(restm+amk)**2
+         bet=bek
+         ami=amk**2
+         alf=almpt-arr(3)
+         blf=1.d0-arr(1)-arr(3)
+         ic0=ic(j)+3*is
+         ic(j)=4*is
+        elseif(sww.gt.restm+ameta+am0.and.qgran(b10).lt.deta)then
+c eta generation
+         restm=(restm+am0)**2
+         bet=bek
+         ami=ameta**2
+         alf=almpt-arr(1)
+         blf=1.d0-2.d0*arr(1)
+         ic0=10
+        else
+c pion generation
+         restm=(restm+am0)**2
+         bet=bep
+         ami=am0**2
+         alf=almpt-arr(1)
+         blf=1.d0-2.d0*arr(1)
+         if(qgran(b10).lt..3333d0)then
+          ic0=0
+         else
+          ic0=3*is-2*ic(j)
+          ic(j)=3*is-ic(j)
+         endif
+        endif
+
+       elseif(iab.eq.3)then
+        if(sww.gt.restm+amk+amlam.and.qgran(b10).lt.dc(4)
+     *  .and.iabs(ic(j)).eq.3)then
+c lambda generation
+         restm=(restm+amk)**2
+         bet=bek
+         ami=amlam**2
+         alf=almpt-arr(3)
+         blf=1.d0-arr(2)-arr(3)
+         ic0=6*is
+         ic(j)=-4*is
+        else
+c nucleon generation
+         restm=(restm+am0)**2
+         bet=ben
+         ami=amn**2
+         alf=almpt-arr(1)
+         blf=1.d0-arr(1)-arr(2)
+         if(iabs(ic(j)).eq.3)then
+          ic0=is*int(2.5d0+qgran(b10))
+          ic(j)=is-ic0
+         else
+          ic0=ic(j)-4*is
+          ic(j)=ic0-4*is
+         endif
+        endif
+
+       elseif(iab.eq.4)then
+        if(sww.gt.restm+amn+amlam.and.qgran(b10).lt.dc(1))then
+c lambda generation
+         restm=(restm+amn)**2
+         bet=ben
+         ami=amlam**2
+         alf=almpt-arr(2)
+         blf=1.d0-arr(2)-arr(3)
+         ic0=6*is
+         ic(j)=-3*is
+        else
+c kaon generation
+         restm=(restm+am0)**2
+         bet=bep
+         ami=amk**2
+         alf=almpt-arr(1)
+         blf=1.d0-arr(1)-arr(3)
+         ic(j)=is*int(1.5d0+qgran(b10))
+         ic0=-3*is-ic(j)
+        endif
+
+       elseif(iab.eq.6)then
+c lambda generation
+        restm=(restm+am0)**2
+        bet=bep
+        ami=amlam**2
+        alf=almpt-arr(1)
+        blf=1.d0-arr(2)-arr(3)
+        ic0=6*is
+        ic(j)=-2*is
+       endif
+
+       ptmax=qglam(ww,restm,ami)
+       if(ptmax.lt.0.)ptmax=0.
+
+       if(ptmax.lt.bet**2)then
+2       pti=ptmax*qgran(b10)
+        if(qgran(b10).gt.exp(-dsqrt(pti)/bet))goto 2
+       else
+3       pti=(bet*dlog(qgran(b10)*qgran(b10)))**2
+        if(pti.gt.ptmax)goto 3
+       endif
+
+       amt=ami+pti
+       restm1=restm+pti
+       zmin=1.d0-qgtwd(ww,restm1,amt)
+       zmax=qgtwd(ww,amt,restm1)
+
+       z1=(1.d0-zmax)**alf
+       z2=(1.d0-zmin)**alf
+4      z=1.-(z1+(z2-z1)*qgran(b10))**(1./alf)
+       if(qgran(b10).gt.(z/zmax)**blf)goto 4
+       wp(j)=z*sww
+       wp(3-j)=amt/wp(j)
+       ep(1)=.5d0*(wp(1)+wp(2))
+       ep(2)=.5d0*(wp(1)-wp(2))
+       pti=dsqrt(pti)
+       call qgcs(c,s)
+       ep(3)=pti*c
+       ep(4)=pti*s
+       ept(1)=sww-ep(1)
+       do i=2,4
+        ept(i)=-ep(i)
+       enddo
+       ww=qgnrm(ept)
+       if(ww.lt.restm)goto 4
+
+       call qgtran(ep,ey,1)
+       call qgtran(ept,ey,1)
+       if(s0x.ne.0.d0.or.s0.ne.0.d0)then
+        call qgrota(ep,s0x,c0x,s0,c0)
+       endif
+       if(ey0(1)*ey0(2)*ey0(3).ne.1.d0)then
+        call qgtran(ep,ey0,1)
+       endif
+       call qgreg(ep,ic0)
+
+      else
+       ami2=restm**2
+       bet=bep
+       if(iab.eq.6.or.iaj.eq.6)then
+        if(iab.eq.6)then
+         ami=amlam**2
+         ic(j)=6*is
+         if(iaj.eq.6)then
+          ic(3-j)=-6*is
+         elseif(iaj.eq.4)then
+          ic(3-j)=-5*is
+         elseif(iaj.le.2)then
+          ic(3-j)=2*is-ic(3-j)
+         else
+          if(iabs(ic(3-j)).eq.3)then
+           ic(3-j)=-3*is
+          elseif(iabs(ic(3-j)).eq.6)then
+           ic(3-j)=-2*is
+          else
+           stop'wrong parton types'
+          endif
+         endif
+        elseif(iab.eq.4)then
+         ami=amk**2
+         ic(j)=-5*is
+         ic(3-j)=6*is
+        elseif(iab.le.2)then
+         ami=am0**2
+         ic(j)=2*is-ic(j)
+         ic(3-j)=6*is
+        else
+         ami=amn**2
+         ic(3-j)=-6*is
+         if(iabs(ic(j)).eq.3)then
+          ic(j)=3*is
+         elseif(iabs(ic(j)).eq.6)then
+          ic(j)=2*is
+         else
+          stop'wrong parton types'
+         endif
+        endif
+
+       elseif(iab.le.2.and.iaj.le.2)then
+        if(sww.gt.2.d0*amk.and.qgran(b10).lt.dc(2))then
+         bet=bek
+         ami=amk**2
+         ami2=ami
+         ic(j)=ic(j)+3*is
+         ic(3-j)=ic(3-j)-3*is
+        else
+         ami=am0**2
+         ic0=-ic(1)-ic(2)
+         if(ic0.ne.0)then
+          ic(j)=ic0*int(.5d0+qgran(b10))
+          ic(3-j)=ic0-ic(j)
+         else
+          if(qgran(b10).lt..2d0)then
+           ic(j)=0
+           ic(3-j)=0
+          else
+           ic(j)=3*is-2*ic(j)
+           ic(3-j)=-ic(j)
+          endif
+         endif
+        endif
+
+       elseif(iab.eq.3.or.iaj.eq.3)then
+        if(iab.eq.3)then
+         ami=amn**2
+         if(iabs(ic(j)).eq.3)then
+          if(iaj.eq.3)then
+           if(iabs(ic(3-j)).eq.3)then
+            if(sww.gt.2.d0*amlam.and.qgran(b10).lt.dc(4))then
+             bet=bek
+             ami=amlam**2
+             ami2=ami
+             ic(j)=6*is
+             ic(3-j)=-6*is
+            else
+             ic(j)=is*int(2.5d0+qgran(b10))
+             ic(3-j)=-ic(j)
+            endif
+           else
+            ic(3-j)=ic(3-j)+4*is
+            ic(j)=5*is+ic(3-j)
+           endif
+          elseif(iaj.lt.3)then
+           if(sww.gt.amlam+amk.and.qgran(b10).lt.dc(4))then
+            bet=bek
+            ami=amlam**2
+            ami2=amk**2
+            ic(j)=6*is
+            ic(3-j)=ic(3-j)+3*is
+           else
+            if(qgran(b10).lt..3333d0)then
+             ic(j)=ic(3-j)+is
+             ic(3-j)=0
+            else
+             ic(j)=is*(4-iaj)
+             ic(3-j)=is*(3-2*iaj)
+            endif
+           endif
+          elseif(iaj.eq.4)then
+           ic(j)=is*int(2.5d0+qgran(b10))
+           ic(3-j)=-ic(j)-2*is
+          endif
+         else
+          if(iabs(ic(3-j)).gt.4)stop'qggene: problem with parton types'
+          ic(j)=ic(j)-4*is
+          ic0=ic(j)-4*is
+          if(iaj.eq.3)then
+           ic(3-j)=ic0-is
+          elseif(iaj.lt.3)then
+           ic(3-j)=-ic(3-j)-ic0
+          elseif(iaj.eq.4)then
+           ic(3-j)=ic0-3*is
+          endif
+         endif
+        else
+         if(iabs(ic(3-j)).eq.3)then
+          if(iab.lt.3)then
+           if(sww.gt.amlam+amk.and.qgran(b10).lt.dc(4))then
+            bet=bek
+            ami2=amlam**2
+            ami=amk**2
+            ic(j)=ic(j)+3*is
+            ic(3-j)=6*is
+           else
+            ami=am0**2
+            if(qgran(b10).lt..3333d0)then
+             ic(3-j)=ic(j)+is
+             ic(j)=0
+            else
+             ic(3-j)=is*(4-iab)
+             ic(j)=is*(3-2*iab)
+            endif
+           endif
+          elseif(iab.eq.4)then
+           ami=amk**2
+           ic(3-j)=is*int(2.5d0+qgran(b10))
+           ic(j)=-ic(3-j)-2*is
+          endif
+         else
+          ic(3-j)=ic(3-j)-4*is
+          ic0=ic(3-j)-4*is
+          if(iab.lt.3)then
+           ami=am0**2
+           ic(j)=-ic0-ic(j)
+          elseif(iab.eq.4)then
+           ami=amk**2
+           ic(j)=ic0-3*is
+          endif
+         endif
+        endif
+       elseif(iab.eq.4.or.iaj.eq.4)then
+        if(iab.eq.4)then
+         ami=amk**2
+         if(iaj.eq.4)then
+          ic(j)=-is*int(4.5d0+qgran(b10))
+          ic(3-j)=-ic(j)
+         else
+          ic0=ic(3-j)+int(.6667d0+qgran(b10))*(-3*is-2*ic(3-j))
+          ic(j)=ic0-3*is
+          ic(3-j)=ic0-ic(3-j)
+         endif
+        else
+         ami=am0**2
+         ic0=ic(j)+int(.6667d0+qgran(b10))*(3*is-2*ic(j))
+         ic(j)=ic0-ic(j)
+         ic(3-j)=ic0+3*is
+        endif
+       endif
+
+       ptmax=qglam(ww,ami2,ami)
+       if(ptmax.lt.0.)ptmax=0.
+       if(ptmax.lt.bet**2)then
+5       pti=ptmax*qgran(b10)
+        if(qgran(b10).gt.exp(-dsqrt(pti)/bet))goto 5
+       else
+6       pti=(bet*dlog(qgran(b10)*qgran(b10)))**2
+        if(pti.gt.ptmax)goto 6
+       endif
+       amt1=ami+pti
+       amt2=ami2+pti
+       z=qgtwd(ww,amt1,amt2)
+       wp(j)=z*sww
+       wp(3-j)=amt1/wp(j)
+       ep(1)=.5d0*(wp(1)+wp(2))
+       ep(2)=.5d0*(wp(1)-wp(2))
+       pti=dsqrt(pti)
+       call qgcs(c,s)
+       ep(3)=pti*c
+       ep(4)=pti*s
+       ept(1)=sww-ep(1)
+       do i=2,4
+        ept(i)=-ep(i)
+       enddo
+       call qgtran(ep,ey,1)
+       call qgtran(ept,ey,1)
+       if(s0x.ne.0.d0.or.s0.ne.0.d0)then
+        call qgrota(ep,s0x,c0x,s0,c0)
+        call qgrota(ept,s0x,c0x,s0,c0)
+       endif
+       if(ey0(1)*ey0(2)*ey0(3).ne.1.d0)then
+        call qgtran(ep,ey0,1)
+        call qgtran(ept,ey0,1)
+       endif
+
+       call qgreg(ep,ic(j))
+       call qgreg(ept,ic(3-j))
+       if(debug.ge.3)write (moniou,202)
+       return
+      endif
+      goto 1
+
+201   format(2x,'qggene: parton flavors at the ends of the string:'
+     *,2x,a2,2x,a2/4x,'light cone momenta of the string: ',e10.3
+     *,2x,e10.3/4x,'ey0=',3e10.3/4x,'s0x=',e10.3,2x,'c0x=',e10.3
+     *,2x,'s0=',e10.3,2x,'c0=',e10.3)
+202   format(2x,'qggene - end')
+203   format(2x,'qggene: current parton flavor at the end '
+     *,i1,' of the string: ',a2/4x,' string mass: ',e10.3)
+      end
+
+c=============================================================================
+      subroutine qgxjet
+c-----------------------------------------------------------------------------
+c procedure for jet hadronization
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(njmax=50000)
+      dimension ep(4),ept(4),ept1(4),ey(3)
+     *,epj(4,2,2*njmax),ipj(2,2*njmax)
+      common /qgarr8/  wwm,be(4),dc(5),deta,almpt,ptdif,ptndi
+      common /qgarr10/ am(7),ammu
+      common /qgarr11/ b10
+      common /qgarr36/ epjet(4,njmax),ipjet(njmax),njtot
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      external qgran
+
+      if(debug.ge.2)write (moniou,201)njtot
+201   format(2x,'qgxjet: total number of jets njtot=',i4)
+
+      nj0=1
+      njet0=0
+      nrej=0
+
+1     njet=njet0
+      do i=1,4
+       ept(i)=epjet(i,nj0)
+       epj(i,1,njet+1)=ept(i)
+      enddo
+      iq1=ipjet(nj0)
+      ipj(1,njet+1)=iq1
+
+      if(iabs(iq1).le.2)then
+       am1=am(1)
+       if(iq1.gt.0)then
+        jq=1
+       else
+        jq=2
+       endif
+      elseif(iabs(iq1).eq.4)then
+       am1=am(3)
+       if(iq1.gt.0)then
+        jq=1
+       else
+        jq=2
+       endif
+      else
+       am1=am(2)
+       if(iq1.gt.0)then
+        jq=2
+       else
+        jq=1
+       endif
+      endif
+
+      ij=nj0
+2     ij=ij+1
+      njet=njet+1
+      iq2=ipjet(ij)
+
+      if(iq2.eq.0)then
+       aks=qgran(b10)
+       do i=1,4
+        epi=epjet(i,ij)*aks
+        epj(i,2,njet)=epi
+        ept(i)=ept(i)+epi
+       enddo
+       if(qgran(b10).lt.dc(2))then
+        ipj(2,njet)=4*(2*jq-3)
+        amj=am(3)
+       else
+        ipj(2,njet)=int(1.5d0+qgran(b10))*(2*jq-3)
+        amj=am(1)
+       endif
+
+       if(qgnrm(ept).gt.(am1+amj)**2)then
+        if(debug.ge.3)write (moniou,211)njet,ipj(1,njet),ipj(2,njet)
+     *  ,qgnrm(ept),ept
+
+        ipj(1,njet+1)=-ipj(2,njet)
+        do i=1,4
+         ept(i)=epjet(i,ij)-epj(i,2,njet)
+         epj(i,1,njet+1)=ept(i)
+        enddo
+        am1=amj
+        goto 2
+       elseif(nrej.lt.100000)then
+        nrej=nrej+1
+        goto 1
+       else
+3       continue
+        do i=1,4
+         ept(i)=epjet(i,ij)+epjet(i,ij-1)+epjet(i,ij+1)
+         ep(i)=epjet(i,ij-1)
+         ept1(i)=ept(i)
+        enddo
+        ww=qgnrm(ept1)
+        if(ww.le.0.)then
+         if(ij.gt.nj0+1)then
+          ij=ij-1
+          goto 3
+         else
+          ij=ij+1
+          goto 3
+         endif
+        endif
+        ipjet(ij)=ipjet(ij+1)
+        sww=sqrt(ww)
+        call qgdeft(ww,ept1,ey)
+        call qgtran(ep,ey,-1)
+        call qgdefr(ep,s0x,c0x,s0,c0)
+        ep(1)=.5d0*sww
+        ep(2)=.5d0*sww
+        ep(3)=0.d0
+        ep(4)=0.d0
+        call qgrota(ep,s0x,c0x,s0,c0)
+        call qgtran(ep,ey,1)
+        do i=1,4
+         epjet(i,ij-1)=ep(i)
+         epjet(i,ij)=ept(i)-ep(i)
+        enddo
+
+        if(njtot.gt.ij+1)then
+         do j=ij+1,njtot-1
+          ipjet(j)=ipjet(j+1)
+         do i=1,4
+          epjet(i,j)=epjet(i,j+1)
+         enddo
+         enddo
+        endif
+        nrej=0
+        njtot=njtot-1
+        goto 1
+       endif
+
+      else
+       ipj(2,njet)=iq2
+       do i=1,4
+        epi=epjet(i,ij)
+        epj(i,2,njet)=epi
+        ept(i)=ept(i)+epi
+       enddo
+
+       if(iabs(iq2).le.2)then
+        am2=am(1)
+       elseif(iabs(iq2).eq.4)then
+        am2=am(3)
+       else
+        am2=am(2)
+       endif
+
+       if(qgnrm(ept).gt.(am1+am2)**2)then
+        if(debug.ge.3)write (moniou,211)njet,ipj(1,njet),ipj(2,njet)
+     *  ,qgnrm(ept),ept
+
+        nj0=ij+1
+        njet0=njet
+        nrej=0
+        if(ij.lt.njtot)then
+         goto 1
+        else
+         goto 5
+        endif
+       elseif(nrej.lt.100000)then
+        nrej=nrej+1
+        goto 1
+       else
+4       continue
+        do i=1,4
+         ept(i)=epjet(i,ij)+epjet(i,ij-1)+epjet(i,ij-2)
+         ep(i)=epjet(i,ij-2)
+         ept1(i)=ept(i)
+        enddo
+        ww=qgnrm(ept1)
+        if(ww.lt.0.d0)then
+         ij=ij-1
+         goto 4
+        endif
+        ipjet(ij-1)=ipjet(ij)
+        sww=sqrt(ww)
+        call qgdeft(ww,ept1,ey)
+        call qgtran(ep,ey,-1)
+        call qgdefr(ep,s0x,c0x,s0,c0)
+        ep(1)=.5d0*sww
+        ep(2)=.5d0*sww
+        ep(3)=0.d0
+        ep(4)=0.d0
+        call qgrota(ep,s0x,c0x,s0,c0)
+        call qgtran(ep,ey,1)
+        do i=1,4
+         epjet(i,ij-2)=ep(i)
+         epjet(i,ij-1)=ept(i)-ep(i)
+        enddo
+
+        if(ij.lt.njtot)then
+         do j=ij,njtot-1
+          ipjet(j)=ipjet(j+1)
+         do i=1,4
+          epjet(i,j)=epjet(i,j+1)
+         enddo
+         enddo
+        endif
+
+        nrej=0
+        njtot=njtot-1
+        goto 1
+       endif
+      endif
+
+5     continue
+      do ij=1,njet
+       do i=1,4
+        ep(i)=epj(i,1,ij)
+        ept(i)=ep(i)+epj(i,2,ij)
+       enddo
+c invariant mass squared for the jet
+       ww=qgnrm(ept)
+
+       if(debug.ge.3)write (moniou,208)
+     * ij,njet,ww,ipj(1,ij),ipj(2,ij)
+
+       sww=dsqrt(ww)
+       call qgdeft(ww,ept,ey)
+       call qgtran(ep,ey,-1)
+       call qgdefr(ep,s0x,c0x,s0,c0)
+       call qggene(sww,sww,ey,s0x,c0x,s0,c0,ipj(1,ij),ipj(2,ij))
+      enddo
+
+      if(debug.ge.3)write (moniou,202)
+202   format(2x,'qgxjet - end')
+208   format(2x,'qgxjet: ij=',i2,2x,'njet=',i3,2x,'ww=',e10.3
+     *,2x,'ic=',2i3)
+211   format(2x,'qgxjet: njet=',i3,2x,'ic=',2i2,2x,'mass=',e10.3
+     *,2x,'ep=',4e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgrot(b,s)
+c-----------------------------------------------------------------------------
+c convolution of nuclear profile functions (axial angle integration)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /arr8/  x2(4),a2
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)b,s
+
+      qgrot=0.d0
+      do i=1,4
+       sb1=b**2+s**2-2.*b*s*(2.*x2(i)-1.)
+       sb2=b**2+s**2-2.*b*s*(1.-2.*x2(i))
+       qgrot=qgrot+(qgt(sb1)+qgt(sb2))
+      enddo
+      qgrot=qgrot*a2
+
+      if(debug.ge.2)write (moniou,202)qgrot
+201   format(2x,'qgrot - axial angle integration of the ',
+     *'nuclear profile function'/4x,
+     *'impact parameter b=',e10.3,2x,'nucleon coordinate s=',e10.3)
+202   format(2x,'qgrot=',e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qgstr(wpi0,wmi0,wp0,wm0,ic10,ic120,ic210,ic20,jp,jt)
+c-----------------------------------------------------------------------------
+c fragmentation process for the pomeron ( quarks and antiquarks types at the
+c ends of the two strings are determined, energy-momentum is shared
+c between them and strings fragmentation is simulated )
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension ey(3)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr8/  wwm,be(4),dc(5),deta,almpt,ptdif,ptndi
+      common /qgarr10/ am(7),ammu
+      common /qgarr11/ b10
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      EXTERNAL qgran
+
+      if(debug.ge.2)write (moniou,201)wpi0,wmi0,wp0,wm0
+
+      do i=1,3
+       ey(i)=1.d0
+      enddo
+      wpi=wpi0
+      wmi=wmi0
+c quark-antiquark types (1 - u, 2 - d, -1 - u~, -2 - d~); s- and d- quarks are
+c taken into consideration at the fragmentation step
+      if(ic10.eq.0)then
+       if(qgran(b10).lt.dc(2))then
+        ic1=4
+        ic12=-4
+       else
+        ic1=int(1.5+qgran(b10))
+        ic12=-ic1
+       endif
+      elseif(ic10.gt.0)then
+       ic1=ic10
+       ic12=ic120
+      else
+       ic1=ic120
+       ic12=ic10
+      endif
+
+      if(ic20.eq.0)then
+       if(qgran(b10).lt.dc(2))then
+        ic2=4
+        ic21=-4
+       else
+        ic2=int(1.5+qgran(b10))
+        ic21=-ic2
+       endif
+      elseif(ic20.gt.0)then
+       ic2=ic20
+       ic21=ic210
+      else
+       ic2=ic210
+       ic21=ic20
+      endif
+
+c longitudinal momenta for the strings
+      if(jp.eq.0)then
+       wp1=wpi*cos(pi*qgran(b10))**2
+      else
+1      xp=.5d0*qgran(b10)**2
+       if(qgran(b10).gt.(2.d0*(1.d0-xp))**(-.5d0))goto 1
+       wp1=wpi*xp
+       if(qgran(b10).lt..5d0)wp1=wpi-wp1
+      endif
+      if(jt.eq.0)then
+       wm1=wmi*cos(pi*qgran(b10))**2
+      else
+2      xm=.5d0*qgran(b10)**2
+       if(qgran(b10).gt.(2.d0*(1.d0-xm))**(-.5d0))goto 2
+       wm1=wmi*xm
+       if(qgran(b10).lt..5d0)wm1=wmi-wm1
+      endif
+      wpi=wpi-wp1
+      wmi=wmi-wm1
+c string masses
+      sm1=wp1*wm1
+      sm2=wpi*wmi
+
+c mass thresholds
+      if(iabs(ic1).le.2)then
+       am1=am(1)
+      elseif(iabs(ic1).eq.3)then
+       am1=am(2)
+      elseif(iabs(ic1).eq.4)then
+       am1=am(3)
+      else
+       am1=0.d0
+       stop 'should not happen in qgstr 1 !'
+      endif
+      if(iabs(ic2).le.2)then
+       am2=am(1)
+      elseif(iabs(ic2).eq.3)then
+       am2=am(2)
+      elseif(iabs(ic2).eq.4)then
+       am2=am(3)
+      else
+       am2=0.d0
+       stop 'should not happen in qgstr 2 !'
+      endif
+      if(iabs(ic12).le.2)then
+       am12=am(1)
+      elseif(iabs(ic12).eq.3)then
+       am12=am(2)
+      elseif(iabs(ic12).eq.4)then
+       am12=am(3)
+      else
+       am12=0.d0
+       stop 'should not happen in qgstr 3 !'
+      endif
+      if(iabs(ic21).le.2)then
+       am21=am(1)
+      elseif(iabs(ic21).eq.3)then
+       am21=am(2)
+      elseif(iabs(ic21).eq.4)then
+       am21=am(3)
+      else
+       am21=0.d0
+       stop 'should not happen in qgstr 4 !'
+      endif
+
+c too short strings are neglected (energy is given to partner string
+c or to the hadron (nucleon) to which the pomeron is connected)
+      if(sm1.gt.am1+am21.and.sm2.gt.am2+am12)then
+c strings fragmentation is simulated - gener
+       call qggene(wp1,wm1,ey,0.d0,1.d0,0.d0,1.d0,ic1,ic21)
+       call qggene(wpi,wmi,ey,0.d0,1.d0,0.d0,1.d0,ic12,ic2)
+      elseif((wpi+wp1)*(wmi+wm1).gt.am1+am21)then
+       call qggene(wp1+wpi,wm1+wmi,ey,0.d0,1.d0,0.d0,1.d0,ic1,ic21)
+      elseif((wpi+wp1)*(wmi+wm1).gt.am2+am12)then
+       call qggene(wp1+wpi,wm1+wmi,ey,0.d0,1.d0,0.d0,1.d0,ic12,ic2)
+      else
+       wp0=wp0+wp1+wpi
+       wm0=wm0+wm1+wmi
+      endif
+
+      if(debug.ge.3)write (moniou,202)wp0,wm0
+201   format(2x,'qgstr: wpi0=',e10.3,2x,'wmi0=',e10.3
+     *,2x,'wp0=',e10.3,2x,'wm0=',e10.3)
+202   format(2x,'qgstr - returned light cone momenta:'
+     *,2x,'wp0=',e10.3,2x,'wm0=',e10.3)
+      return
+      end
+
+c===========================================================================
+      double precision function qgt(b)
+c---------------------------------------------------------------------------
+c nuclear profile function value at impact parameter squared b
+c---------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr5/  rnuc(2),wsnuc(2),wbnuc(2),anorm
+     *,cr1(2),cr2(2),cr3(2)
+      common /qgarr6/  pi,bm,amws
+      common /arr3/   x1(7),a1(7)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+
+      if(debug.ge.2)write (moniou,201)b
+
+      qgt=0.
+      zm=rnuc(2)**2-b
+      if(zm.gt.4.*b)then
+       zm=dsqrt(zm)
+      else
+       zm=2.*dsqrt(b)
+      endif
+
+      do i=1,7
+      do m=1,2
+       z1=zm*(.5d0+x1(i)*(m-1.5d0))
+       r=dsqrt(b+z1**2)
+       quq=(r-rnuc(2))/wsnuc(2)
+       if (quq.lt.85.)qgt=qgt+a1(i)/(1.+exp(quq))
+     * *(1.d0+wbnuc(2)*(r/rnuc(2))**2)
+      enddo
+      enddo
+      qgt=qgt*zm*0.5d0
+
+      dt=0.
+      do i=1,7
+      do m=1,2
+       z1=zm-wsnuc(2)*log(.5d0+x1(i)*(m-1.5d0))
+       r=dsqrt(b+z1**2)
+       quq=(r-rnuc(2)-z1+zm)/wsnuc(2)
+       if (quq.lt.85.)dt=dt+a1(i)/(exp((zm-z1)/wsnuc(2))+exp(quq))
+     * *(1.d0+wbnuc(2)*(r/rnuc(2))**2)
+      enddo
+      enddo
+      qgt=qgt+dt*wsnuc(2)/2.d0
+
+      if(debug.ge.3)write (moniou,202)qgt
+201   format(2x,'qgt - nuclear profile function value at impact'
+     *,' parameter squared b=',e10.3)
+202   format(2x,'qgt=',e10.3)
+      return
+      end
+
+c=============================================================================
+      block data qgdata
+c-----------------------------------------------------------------------------
+c constants for numerical integration (gaussian weights)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      common /arr1/ trnuc(56),twsnuc(56),twbnuc(56)
+      common /arr3/ x1(7),a1(7)
+      common /arr4/ x4(2),a4(2)
+      common /arr5/ x5(2),a5(2)
+      common /arr8/ x2(4),a2
+      common /arr9/ x9(3),a9(3)
+      data x1/.9862838d0,.9284349d0,.8272013d0,.6872929d0,.5152486d0,
+     *.3191124d0,.1080549d0/
+      data a1/.03511946d0,.08015809d0,.1215186d0,.1572032d0,
+     *.1855384d0,.2051985d0,.2152639d0/
+      data x2/.00960736d0,.0842652d0,.222215d0,.402455d0/
+      data a2/.392699d0/
+      data x4/ 0.339981,0.861136/
+      data a4/ 0.652145,0.347855/
+      data x5/.585786d0,3.41421d0/
+      data a5/.853553d0,.146447d0/
+      data x9/.93247d0,.661209d0,.238619d0/
+      data a9/.171324d0,.360762d0,.467914d0/
+      data trnuc/0.69d0,1.71d0,1.53d0,1.37d0,1.37d0,2.09d0,1.95d0
+     *,1.95d0,2.06d0,1.76d0,1.67d0,1.74d0,1.66d0,2.57d0,2.334d0
+     *,2.608d0,2.201d0,2.331d0,2.58d0,2.791d0,2.791d0,2.782d0,2.74d0
+     *,3.192d0,3.22d0,3.05d0,3.07d0,3.34d0,3.338d0,3.252d0
+     *,3.369d0,3.244d0,3.244d0,3.313d0,3.476d0,3.54d0,3.554d0
+     *,3.554d0,3.743d0,3.73d0,3.744d0,3.759d0,3.774d0,3.788d0
+     *,3.802d0,3.815d0,3.829d0,3.843d0,3.855d0,3.941d0
+     *,3.94d0,3.984d0,4.d0,4.074d0,3.89d0,4.111d0/
+      data twsnuc/0.d0,0.d0,0.d0,0.d0,0.d0,0.d0,0.d0,0.d0,0.d0
+     *,0.55d0,0.55d0,0.56d0,0.56d0,0.5052d0,0.498d0,0.513d0
+     *,0.55d0,0.55d0,0.567d0,0.698d0,0.698d0,0.549d0,0.55d0
+     *,0.604d0,0.58d0,0.523d0,0.519d0,0.58d0,0.547d0,0.553d0
+     *,0.582d0,0.55d0,0.55d0,0.7d0,0.599d0,0.507d0,0.588d0
+     *,0.588d0,0.585d0,0.62d0,0.55d0,0.55d0,0.55d0,0.55d0
+     *,0.55d0,0.55d0,0.55d0,0.588d0,0.588d0
+     *,0.566d0,0.505d0,0.542d0,0.557d0,0.536d0,0.567d0,0.558d0/
+      data twbnuc/0.d0,0.d0,0.d0,0.d0,0.d0,0.d0,0.d0,0.d0,0.d0
+     *,0.d0,0.d0,0.d0,0.d0,-0.18d0,0.139d0,-0.051d0,0.d0,0.d0
+     *,0.d0,-0.168d0,0.d0,0.d0,0.d0,-0.249d0,-0.236d0,0.d0,0.d0
+     *,0.233d0,-0.203d0,-0.078d0,-0.173d0,0.d0,0.d0,0.d0,-0.1d0
+     *,0.d0,-0.13d0,-0.13d0,-0.201d0,-0.19d0,0.d0,0.d0,0.d0,0.d0
+     *,0.d0,0.d0,0.d0,0.d0,0.d0,0.d0,0.d0,0.d0,0.d0,0.d0
+     *,0.d0,0.d0/
+      end
+
+c-----------------------------------------------------------------------
+      real function qggamfun(x)
+c-----------------------------------------------------------------------
+c     gamma fctn
+c-----------------------------------------------------------------------
+      dimension c(13)
+      data c
+     1/ 0.00053 96989 58808, 0.00261 93072 82746, 0.02044 96308 23590,
+     2  0.07309 48364 14370, 0.27964 36915 78538, 0.55338 76923 85769,
+     3  0.99999 99999 99998,-0.00083 27247 08684, 0.00469 86580 79622,
+     4  0.02252 38347 47260,-0.17044 79328 74746,-0.05681 03350 86194,
+     5  1.13060 33572 86556/
+      qggamfun=0
+      z=x
+      if(x .gt. 0.0) goto1
+      if(x .eq. aint(x)) goto5
+      z=1.0-z
+    1 f=1.0/z
+      if(z .le. 1.0) goto4
+      f=1.0
+    2 continue
+      if(z .lt. 2.0) goto3
+      z=z-1.0
+      f=f*z
+      goto2
+    3 z=z-1.0
+    4 qggamfun=
+     1 f*((((((c(1)*z+c(2))*z+c(3))*z+c(4))*z+c(5))*z+c(6))*z+c(7))/
+     2   ((((((c(8)*z+c(9))*z+c(10))*z+c(11))*z+c(12))*z+c(13))*z+1.0)
+      if(x .gt. 0.0) return
+      qggamfun=3.141592653589793/(sin(3.141592653589793*x)*qggamfun)
+      return
+    5 write(*,10)x
+   10 format(1x,'argument of gamma fctn = ',e20.5)
+      stop
+      end
+
+c-------------------------------------------------------------------------------
+      subroutine qgcrossc(niter,gtot,gprod,gabs,gdd,gqel,gcoh)
+c-------------------------------------------------------------------------------
+c nucleus-nucleus (nucleus-hydrogen) interaction cross sections
+c gtot  - total cross section
+c gprod - production cross section (projectile diffraction included)
+c gabs  - cut pomerons cross section
+c gdd   - projectile diffraction cross section
+c gqel  - quasielastic (projectile nucleon knock-out) cross section
+c gcoh  - coherent (elastic with respect to the projectile) cross section
+c (target diffraction is not treated explicitely and contributes to
+c gdd, gqel, gcoh).
+c-------------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      parameter(iapmax=208)
+      dimension wabs(28),wdd(28),wqel(28),wcoh(28)
+     *,wprod(28),b0(28),ai(28),xa(iapmax,3),xb(iapmax,3)
+      common /qgarr1/  ia(2),icz,icp
+      common /qgarr5/  rnuc(2),wsnuc(2),wbnuc(2),anorm
+     *,cr1(2),cr2(2),cr3(2)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr11/ b10
+      common /qgarr16/ cc(2,3),iddp(iapmax),iddt(iapmax)
+      common /arr3/   x1(7),a1(7)
+      EXTERNAL qgran
+
+      e1=exp(-1.d0)
+
+      do i=1,7
+       b0(15-i)=bm*sqrt((1.d0+x1(i))/2.d0)
+       b0(i)=bm*sqrt((1.d0-x1(i))/2.d0)
+       ai(i)=a1(i)*bm**2*5.d0*pi
+       ai(15-i)=ai(i)
+      enddo
+
+      do i=1,7
+       tp=(1.d0+x1(i))/2.d0
+       tm=(1.d0-x1(i))/2.d0
+       b0(14+i)=bm-log(tp)*max(wsnuc(1),wsnuc(2))
+       b0(29-i)=bm-log(tm)*max(wsnuc(1),wsnuc(2))
+       ai(14+i)=a1(i)*b0(14+i)/tp*10.d0*max(wsnuc(1),wsnuc(2))*pi
+       ai(29-i)=a1(i)*b0(29-i)/tm*10.d0*max(wsnuc(1),wsnuc(2))*pi
+      enddo
+
+      do i=1,28
+       wabs(i)=0.
+       wdd(i)=0.
+       wqel(i)=0.
+       wcoh(i)=0.
+      enddo
+
+      do nc=1,niter
+       do i=1,ia(2)
+        iddt(i)=1+int(qgran(b10)+cc(2,2))
+       enddo
+
+       if(ia(1).eq.1)then
+        xa(1,1)=0.d0
+        xa(1,2)=0.d0
+        xa(1,3)=0.d0
+       else
+        call qggea(ia(1),xa,1)
+       endif
+       if(ia(2).eq.1)then
+        xb(1,1)=0.d0
+        xb(1,2)=0.d0
+        xb(1,3)=0.d0
+       else
+        call qggea(ia(2),xb,2)
+       endif
+
+       do i=1,28
+        call qggcr(b0(i),gabs,gdd,gqel,gcoh,xa,xb,ia(1))
+        wabs(i)=wabs(i)+gabs
+        wdd(i)=wdd(i)+gdd
+        wqel(i)=wqel(i)+gqel
+        wcoh(i)=wcoh(i)+gcoh
+       enddo
+      enddo
+
+      gabs=0.
+      gdd=0.
+      gqel=0.
+      gcoh=0.
+      do i=1,28
+       wabs(i)=wabs(i)/niter
+       wdd(i)=wdd(i)/niter
+       wqel(i)=wqel(i)/niter
+       wcoh(i)=wcoh(i)/niter
+       wprod(i)=wabs(i)+wdd(i)
+       gabs=gabs+ai(i)*wabs(i)
+       gdd=gdd+ai(i)*wdd(i)
+       gqel=gqel+ai(i)*wqel(i)
+       gcoh=gcoh+ai(i)*wcoh(i)
+      enddo
+      gprod=gabs+gdd
+      gtot=gprod+gqel+gcoh
+      return
+      end
+
+c-------------------------------------------------------------------------------
+      subroutine qggcr(b,gabs,gdd,gqel,gcoh,xa,xb,ia)
+c-------------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      parameter(iapmax=208)
+      dimension xa(iapmax,3),xb(iapmax,3),vabs(2)
+
+      gabs=1.
+      gdd=1.
+      gqel=1.
+      gcoh=1.
+      do n=1,ia
+       call qgv(xa(n,1)+b,xa(n,2),xb,vin,vdd,vabs)
+       gabs=gabs*(vdd-vin+1.d0)          !prod_n^A [sum_i c_i exp(-2chi_i(n))]
+       gdd=gdd*(1.-vin)                  !prod_n^A [sum_i c_i exp(-chi_i(n))]^2
+       gqel=gqel*(2.d0*dsqrt(1.d0-vin)-1.d0)
+                                       !prod_n^A [sum_i c_i exp(-chi_i(n)) - 1]
+       gcoh=gcoh*dsqrt(1.d0-vin)
+      enddo
+      gcoh=1.-2.*gcoh+gqel
+      gqel=gdd-gqel
+      gdd=gabs-gdd
+      gabs=1.-gabs
+      return
+      end
+
+c-------------------------------------------------------------------------------
+      double precision function qgsect(e0n,icz,iap0,iat0)    !so18032013
+c-------------------------------------------------------------------------------
+c qgsect - hadron-nucleus (hadron-nucleus) particle production cross section
+c e0n - lab. energy per projectile nucleon (hadron),
+c icz - hadron class,
+c iap - projectile mass number (1=<iap<=iapmax),
+c iat - target mass number     (1=<iat<=iapmax)
+c-------------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension wk(3),wa(3),wb(3)
+      common /qgarr47/ gsect(10,5,6)
+      common /qgarr48/ qgsasect(10,6,6)
+      common /qgarr43/ moniou
+      common /qgdebug/    debug
+
+      if(debug.ge.3)write (moniou,201)e0n,icz,iap0,iat0
+      qgsect=0.d0
+
+      iap=iap0                                              !so18032013-beg
+      iat=iat0
+      if(iat.eq.1.and.iap.ne.1)then
+       iap=iat0
+       iat=iap0
+      endif                                                 !so18032013-end
+
+      ye=dlog10(e0n)
+      if(ye.lt.1.d0)ye=1.d0
+      je=int(ye)
+      if(je.gt.8)je=8
+
+      wk(2)=ye-je
+      wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+      wk(1)=1.d0-wk(2)+wk(3)
+      wk(2)=wk(2)-2.d0*wk(3)
+
+      yb=iat
+      yb=dlog(yb)/1.38629d0+1.d0
+      jb=min(int(yb),2)
+      wb(2)=yb-jb
+      wb(3)=wb(2)*(wb(2)-1.d0)*.5d0
+      wb(1)=1.d0-wb(2)+wb(3)
+      wb(2)=wb(2)-2.d0*wb(3)
+
+      if(iap.eq.1)then
+       if(iat.eq.14)then
+        do i=1,3
+         qgsect=qgsect+gsect(je+i-1,icz,5)*wk(i)
+        enddo
+       elseif(iat.eq.40)then
+        do i=1,3
+         qgsect=qgsect+gsect(je+i-1,icz,6)*wk(i)
+        enddo
+       else
+        do i=1,3
+        do l=1,3
+         qgsect=qgsect+gsect(je+i-1,icz,jb+l-1)*wk(i)*wb(l)
+        enddo
+        enddo
+       endif
+      else
+       ya=iap
+       ya=dlog(ya/2.d0)/.69315d0+1.d0
+       ja=min(int(ya),4)
+       wa(2)=ya-ja
+       wa(3)=wa(2)*(wa(2)-1.d0)*.5d0
+       wa(1)=1.d0-wa(2)+wa(3)
+       wa(2)=wa(2)-2.d0*wa(3)
+       if(iat.eq.14)then
+        do i=1,3
+        do m=1,3
+         qgsect=qgsect+qgsasect(je+i-1,ja+m-1,5)*wk(i)*wa(m)
+        enddo
+        enddo
+       elseif(iat.eq.40)then
+        do i=1,3
+        do m=1,3
+         qgsect=qgsect+qgsasect(je+i-1,ja+m-1,6)*wk(i)*wa(m)
+        enddo
+        enddo
+       else
+        do i=1,3
+        do m=1,3
+        do l=1,3
+         qgsect=qgsect+qgsasect(je+i-1,ja+m-1,jb+l-1)*wk(i)*wa(m)*wb(l)
+        enddo
+        enddo
+        enddo
+       endif
+      endif
+      qgsect=exp(qgsect)
+      if(debug.ge.4)write (moniou,202)
+
+201   format(2x,'qgsect - nucleus-nucleus production cross section'
+     */4x,'lab. energy per nucleon - ',e10.3,2x,'hadron class - ',i2
+     */4x,'proj. mass N - ',i3,2x,'targ. mass N - ',i3)
+202   format(2x,'qgsect=',e10.3)
+      return
+      end
+
+c=============================================================================
+      subroutine qgreg(ep0,ic)
+c-----------------------------------------------------------------------
+c qgreg - registration of produced hadron
+c ep0 - 4-momentum,
+c ic  - hadron type
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      parameter(nptmax=95000)
+      dimension ep(4),ep0(4),ep1(4),ep2(4),ep3(4)
+      common /qgarr4/  ey0(3)
+      common /qgarr10/ am0,amn,amk,amc,amlamc,amlam,ameta,ammu
+      common /qgarr11/ b10
+      common /qgarr12/ nsh
+      common /qgarr14/ esp(4,nptmax),ich(nptmax)
+      common /qgarr21/ dmmin(3),wex(3),dmres(3),wdres(3)
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      external qgran
+
+      if(debug.ge.3)write (moniou,201)ic,ep0,nsh
+      nsh=nsh+1
+
+      nstprev = nsh
+
+      if(nsh.gt.nptmax)stop'increase nptmax!!!'
+      iab=iabs(ic)
+      do i=1,4
+       ep(i)=ep0(i)
+      enddo
+
+c       call qgtran(ep,ey0,1)
+
+      if(iab.eq.7.or.iab.eq.8)then         !delta++(-)
+       call qgdec2(ep,ep1,ep2,dmmin(2)**2,amn**2,am0**2)
+       ich(nsh)=ic-5*ic/iab
+       do i=1,4
+        esp(i,nsh)=ep1(i)
+        ep(i)=ep2(i)
+       enddo
+       nsh=nsh+1
+       ich(nsh)=15*ic/iab-2*ic
+
+ctp      elseif(iab.eq.-10)then                   !rho0 -> pi+ + pi-
+ctp       call qgdec2(ep,ep1,ep2,dmmin(1)**2,am0**2,am0**2)
+ctp       ich(nsh)=2*int(.5d0+qgran(b10))-1
+ctp       do i=1,4
+ctp        esp(i,nsh)=ep1(i)
+ctp        ep(i)=ep2(i)
+ctp       enddo
+ctp       nsh=nsh+1
+ctp       ich(nsh)=-ich(nsh-1)
+
+      elseif(iab.eq.11)then                  !pi* -> rho + pi
+       am2=qgnrm(ep)
+       call qgdec2(ep,ep1,ep2,am2,dmmin(1)**2,am0**2)
+ctp       call qgdec2(ep1,ep3,ep,dmmin(1)**2,am0**2,am0**2)
+       if(qgran(b10).lt..5d0)then  !rho0 + pi+/-
+        ich(nsh)=-10
+        ich(nsh+1)=ic/iab
+ctp        ich(nsh+1)=2*int(.5d0+qgran(b10))-1
+ctp        ich(nsh+2)=-ich(nsh+1)
+        do i=1,4
+          esp(i,nsh)=ep1(i)
+          ep(i)=ep2(i)
+        enddo
+        nsh=nsh+1
+       else      !rho+/- + pi0 -> pi+/- + 2 pi0
+        call qgdec2(ep1,ep3,ep,dmmin(1)**2,am0**2,am0**2)
+        ich(nsh)=0
+        ich(nsh+1)=ic/iab
+        ich(nsh+2)=0
+        do i=1,4
+          esp(i,nsh)=ep2(i)
+          esp(i,nsh+1)=ep3(i)
+        enddo
+        nsh=nsh+2
+       endif
+ctp       do i=1,4
+ctp        esp(i,nsh)=ep2(i)
+ctp        esp(i,nsh+1)=ep3(i)
+ctp       enddo
+ctp       nsh=nsh+2
+
+      elseif(iab.eq.12.or.iab.eq.13)then       !N*
+       am2=qgnrm(ep)
+       if(6.d0*qgran(b10).lt.1.d0)then         !delta + pi
+        call qgdec2(ep,ep1,ep2,am2,dmmin(2)**2,am0**2)
+        call qgdec2(ep1,ep3,ep,dmmin(2)**2,amn**2,am0**2)
+        ich(nsh)=2*ic-25*ic/iab
+        ich(nsh+1)=ic-10*ic/iab
+        ich(nsh+2)=-ich(nsh)
+        do i=1,4
+         esp(i,nsh)=ep2(i)
+         esp(i,nsh+1)=ep3(i)
+        enddo
+        nsh=nsh+2
+       else                                    !N + pi
+        call qgdec2(ep,ep1,ep2,am2,amn**2,am0**2)
+        do i=1,4
+         esp(i,nsh)=ep1(i)
+         ep(i)=ep2(i)
+        enddo
+        if(qgran(b10).lt..4d0)then
+         ich(nsh)=ic-10*ic/iab
+         ich(nsh+1)=0
+        else
+         ich(nsh)=15*ic/iab-ic
+         ich(nsh+1)=25*ic/iab-2*ic
+        endif
+        nsh=nsh+1
+       endif
+
+      elseif(iab.eq.14.or.iab.eq.15)then       !K1
+       am2=qgnrm(ep)
+       if(dsqrt(am2).gt.dmmin(1)+amk)then      !rho + K
+        call qgdec2(ep,ep1,ep2,am2,dmmin(1)**2,amk**2)
+ctp        call qgdec2(ep1,ep3,ep,dmmin(1)**2,am0**2,am0**2)
+        if(3.d0*qgran(b10).lt.1.d0)then  !rho0
+         ich(nsh)=ic-10*ic/iab
+         ich(nsh+1)=-10
+c         ich(nsh+1)=2*int(.5d0+qgran(b10))-1
+c         ich(nsh+2)=-ich(nsh+1)
+         do i=1,4
+           esp(i,nsh)=ep2(i)
+           ep(i)=ep1(i)
+         enddo
+         nsh=nsh+1
+        else                             !rho+/-
+         call qgdec2(ep1,ep3,ep,dmmin(1)**2,am0**2,am0**2)
+         ich(nsh)=19*ic/iab-ic
+         ich(nsh+1)=29*ic/iab-2*ic
+         ich(nsh+2)=0
+         do i=1,4
+          esp(i,nsh)=ep2(i)
+          esp(i,nsh+1)=ep3(i)
+         enddo
+         nsh=nsh+2
+        endif
+       else                                    !K* + pi
+        call qgdec2(ep,ep1,ep2,am2,dmmin(3)**2,am0**2)
+        call qgdec2(ep1,ep3,ep,dmmin(3)**2,amk**2,am0**2)
+        if(3.d0*qgran(b10).lt.1.d0)then
+         ich(nsh)=0
+         if(3.d0*qgran(b10).lt.1.d0)then
+          ich(nsh+1)=ic-10*ic/iab
+          ich(nsh+2)=0
+         else
+          ich(nsh+1)=19*ic/iab-ic
+          ich(nsh+2)=29*ic/iab-2*ic
+         endif
+        else
+         ich(nsh)=29*ic/iab-2*ic
+         if(3.d0*qgran(b10).lt.1.d0)then
+          ich(nsh+1)=19*ic/iab-ic
+          ich(nsh+2)=0
+         else
+          ich(nsh+1)=ic-10*ic/iab
+          ich(nsh+2)=2*ic-29*ic/iab
+         endif
+        endif
+        do i=1,4
+         esp(i,nsh)=ep2(i)
+         esp(i,nsh+1)=ep3(i)
+        enddo
+        nsh=nsh+2
+       endif
+ctp       do i=1,4
+ctp        esp(i,nsh)=ep2(i)
+ctp        esp(i,nsh+1)=ep3(i)
+ctp       enddo
+ctp       nsh=nsh+2
+
+      elseif(iab.eq.5)then                     !K0,K0~
+       ich(nsh)=10*int(.5d0+qgran(b10))-5
+
+c      elseif(iab.eq.6)then                !lambda decay (switch on in CONEX!)
+c       ic2=-ic/iab*int(.64d0+qgran(b10))
+c       ic1=3*ic/iab+ic2
+c       call qgdec2(ep,ep1,ep2,amlam**2,amn**2,am0**2)
+c       do i=1,4
+c        esp(i,nsh)=ep1(i)
+c        ep(i)=ep2(i)
+c       enddo
+c       ich(nsh)=ic1
+c       ich(nsh+1)=ic2
+c       nsh=nsh+1
+
+      else
+       ich(nsh)=ic
+      endif
+
+      do i=1,4
+       esp(i,nsh)=ep(i)
+      enddo
+
+      do n=nstprev,nsh
+        do i=1,4
+          ep(i)=esp(i,n)
+        enddo
+        call qgtran(ep,ey0,1)
+        do i=1,4
+          esp(i,n)=ep(i)
+        enddo
+      enddo
+
+      if(debug.ge.4)write (moniou,202)
+
+201   format(2x,'qgreg: ic=',i2,2x,'c.m. 4-momentum:',2x,4(e10.3,1x)/
+     * 4x,'number of particles in the storage: ',i5)
+202   format(2x,'qgreg - end')
+      return
+      end
+
+c-----------------------------------------------------------------------------
+      subroutine qgdec2(ep,ep1,ep2,ww,a,b)
+c two particle decay
+      implicit double precision (a-h,o-z)
+      integer debug
+      dimension ep(4),ep1(4),ep2(4),ey(3)
+      common /qgarr11/ b10
+      common /qgarr43/ moniou
+      common /qgdebug/  debug
+      EXTERNAL qgran
+
+      if(debug.ge.2)write (moniou,201)ep,ww,a,b
+201   format(2x,'qgdec2: 4-momentum:',2x,4(e10.3,1x)
+     */4x,'ww=',e10.3,2x,'a=',e10.3,2x,'b=',e10.3)
+
+      pl=qglam(ww,a,b)
+      ep1(1)=dsqrt(pl+a)
+      ep2(1)=dsqrt(pl+b)
+      pl=dsqrt(pl)
+      cosz=2.d0*qgran(b10)-1.d0
+      pt=pl*dsqrt(1.d0-cosz**2)
+      ep1(2)=pl*cosz
+      call qgcs(c,s)
+      ep1(3)=pt*c
+      ep1(4)=pt*s
+      do i=2,4
+       ep2(i)=-ep1(i)
+      enddo
+      call qgdeft(ww,ep,ey)
+      call qgtran(ep1,ey,1)
+      call qgtran(ep2,ey,1)
+      if(debug.ge.3)write (moniou,203)
+203   format(2x,'qgdec2 - end')
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qggrv(x,qqs,icq,iq)
+c------------------------------------------------------------------------
+c qggrv - GRV structure functions
+c------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr25/ ahv(3)
+
+      qggrv=0.
+      if(x.gt..99999d0.and.(qqs.ne.qt0.or.iq.ne.1.and.iq.ne.2))return
+
+      if(icq.eq.2)then
+       sq=dlog(dlog(qqs/.232d0**2)/dlog(.23d0/.232d0**2))
+       if(iq.eq.0)then                                 !gluon
+        alg=.524d0
+        betg=1.088d0
+        aag=1.742d0-.93d0*sq
+        bbg=-.399d0*sq**2
+        ag=7.486d0-2.185d0*sq
+        bg=16.69d0-22.74d0*sq+5.779d0*sq*sq
+        cg=-25.59d0+29.71d0*sq-7.296d0*sq*sq
+        dg=2.792d0+2.215d0*sq+.422d0*sq*sq-.104d0*sq*sq*sq
+        eg=.807d0+2.005d0*sq
+        eeg=3.841d0+.361d0*sq
+        qggrv=(1.d0-x)**dg*(x**aag*(ag+bg*x+cg*x**2)*log(1.d0/x)**bbg
+     *  +sq**alg*exp(-eg+sqrt(eeg*sq**betg*log(1.d0/x))))
+       elseif(iq.eq.1.or.iq.eq.2)then                  !u_v or d_v
+        aau=.59d0-.024d0*sq
+        bbu=.131d0+.063d0*sq
+        auu=2.284d0+.802d0*sq+.055d0*sq*sq
+        au=-.449d0-.138d0*sq-.076d0*sq*sq
+        bu=.213d0+2.669d0*sq-.728d0*sq*sq
+        cu=8.854d0-9.135d0*sq+1.979d0*sq*sq
+        du=2.997d0+.753d0*sq-.076d0*sq*sq
+        uv=auu*x**aau*(1.d0+au*x**bbu+bu*x+cu*x**1.5d0)
+        if(qqs.ne.qt0)uv=uv*(1.d0-x)**du
+
+        aad=.376d0
+        bbd=.486d0+.062d0*sq
+        add=.371d0+.083d0*sq+.039d0*sq*sq
+        ad=-.509d0+3.31d0*sq-1.248d0*sq*sq
+        bd=12.41d0-10.52d0*sq+2.267d0*sq*sq
+        ccd=6.373d0-6.208d0*sq+1.418d0*sq*sq
+        dd=3.691d0+.799d0*sq-.071d0*sq*sq
+        dv=add*x**aad*(1.d0+ad*x**bbd+bd*x+ccd*x**1.5d0)
+        if(qqs.ne.qt0)then
+         dv=dv*(1.d0-x)**dd
+        elseif(x.gt..99999d0)then
+         dv=0.d0
+        else
+         dv=dv*(1.d0-x)**(dd-ahv(2))
+        endif
+        if(iq.eq.1)then                              !u_v
+         qggrv=uv
+        elseif(iq.eq.2)then                          !d_v
+         qggrv=dv
+        endif
+
+       elseif(iq.eq.-3)then                           !s_sea
+        als=.914
+        bets=.577
+        aas=1.798-.596*sq
+        as=-5.548+3.669*sqrt(sq)-.616*sq
+        bs=18.92-16.73*sqrt(sq)+5.168*sq
+        ds=6.379-.35*sq+.142*sq*sq
+        es=3.981+1.638*sq
+        ees=6.402
+        qggrv=(1.-x)**ds*sq**als/log(1./x)**aas*(1.+as*sqrt(x)
+     *  +bs*x)*exp(-es+sqrt(ees*sq**bets*log(1./x)))
+       elseif(iabs(iq).lt.3)then                      !u_sea or d_sea
+        aadel=.409-.005*sq
+        bbdel=.799+.071*sq
+        addel=.082+.014*sq+.008*sq*sq
+        adel=-38.07+36.13*sq-.656*sq*sq
+        bdel=90.31-74.15*sq+7.645*sq*sq
+        ccdel=0.
+        ddel=7.486+1.217*sq-.159*sq*sq
+        delv=addel*x**aadel*(1.-x)**ddel
+     *  *(1.+adel*x**bbdel+bdel*x+ccdel*x**1.5)
+
+        alud=1.451
+        betud=.271
+        aaud=.41-.232*sq
+        bbud=.534-.457*sq
+        aud=.89-.14*sq
+        bud=-.981
+        cud=.32+.683*sq
+        dud=4.752+1.164*sq+.286*sq*sq
+        eud=4.119+1.713*sq
+        eeud=.682+2.978*sq
+        udsea=(1.-x)**dud*(x**aaud*(aud+bud*x+cud*x**2)
+     *  *log(1./x)**bbud+sq**alud*exp(-eud+sqrt(eeud*sq**betud
+     *  *log(1./x))))
+
+        if(iq.eq.-1)then                           !u_sea
+         qggrv=(udsea-delv)/2.
+        elseif(iq.eq.-2)then                       !d_sea
+         qggrv=(udsea+delv)/2.
+        endif
+       else
+        qggrv=0.
+       endif
+
+      elseif(icq.eq.1.or.icq.eq.3)then
+       sq=dlog(dlog(qqs/.204d0**2)/dlog(.26d0/.204d0**2))
+       if(iq.eq.1.or.iq.eq.2)then
+        aapi=.517-.02*sq
+        api=-.037-.578*sq
+        bpi=.241+.251*sq
+        dpi=.383+.624*sq
+        anorm=1.212+.498*sq+.009*sq**2
+        qggrv=.5*anorm*x**aapi*(1.+api*sqrt(x)+bpi*x)
+        if(qqs.ne.qt0)qggrv=qggrv*(1.d0-x)**dpi
+       elseif(iq.eq.0)then
+          alfpi=.504
+          betpi=.226
+          aapi=2.251-1.339*sqrt(sq)
+          api=2.668-1.265*sq+.156*sq**2
+          bbpi=0.
+          bpi=-1.839+.386*sq
+          cpi=-1.014+.92*sq-.101*sq**2
+          dpi=-.077+1.466*sq
+          epi=1.245+1.833*sq
+          eppi=.51+3.844*sq
+          qggrv=(1.-x)**dpi*(x**aapi*(api+bpi*sqrt(x)+cpi*x)*
+     *    log(1./x)**bbpi+sq**alfpi*
+     *    exp(-epi+sqrt(eppi*sq**betpi*log(1./x))))
+        elseif(iq.eq.-3)then
+          alfpi=.823
+          betpi=.65
+          aapi=1.036-.709*sq
+          api=-1.245+.713*sq
+          bpi=5.58-1.281*sq
+          dpi=2.746-.191*sq
+          epi=5.101+1.294*sq
+          eppi=4.854-.437*sq
+          qggrv=sq**alfpi/log(1./x)**aapi*(1.-x)**dpi*
+     *    (1.+api*sqrt(x)+bpi*x)*
+     *    exp(-epi+sqrt(eppi*sq**betpi*log(1./x)))
+        elseif(iabs(iq).lt.3)then
+          alfpi=1.147
+          betpi=1.241
+          aapi=.309-.134*sqrt(sq)
+          api=.219-.054*sq
+          bbpi=.893-.264*sqrt(sq)
+          bpi=-.593+.24*sq
+          cpi=1.1-.452*sq
+          dpi=3.526+.491*sq
+          epi=4.521+1.583*sq
+          eppi=3.102
+          qggrv=(1.-x)**dpi*(x**aapi*(api+bpi*sqrt(x)+cpi*x)*
+     *    log(1./x)**bbpi+sq**alfpi*
+     *    exp(-epi+sqrt(eppi*sq**betpi*log(1./x))))
+        else
+          qggrv=0.
+        endif
+      else
+       qggrv=0.
+      endif
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgev(q1,qj,qq,xx,j,l)
+c------------------------------------------------------------------------
+c qgev - PDF evolution
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr51/ epsxmn
+      common /arr3/   x1(7),a1(7)
+
+      qgev=0.d0
+      zmax=1.d0-epsxmn
+      zmin=xx/zmax
+      if(zmin.ge.zmax)return
+
+      if(qj.eq.qq)then
+       do i1=1,7
+       do m1=1,2
+        qi=q1*(qq/q1)**(.5d0+x1(i1)*(m1-1.5d0))
+
+        fz1=0.d0
+        fz2=0.d0
+        fz3=0.d0
+        zmin1=max(.2d0,zmin)
+        zmax1=min(.2d0,zmax)
+        zmax1=min(5.d0*xx,zmax1)
+        zmax2=min(zmin1,zmax)
+        zmin2=max(zmax1,zmin)
+
+        if(zmax1.gt.zmin)then
+         do i=1,7
+         do m=1,2
+          z=xx+(zmin-xx)*((zmax1-xx)/(zmin-xx))**(.5d0+(m-1.5d0)*x1(i))
+          do k=1,2
+           if(j.ne.3.or.k.ne.1)then
+            fz1=fz1+a1(i)*qgevi(q1,qi,xx/z,j,k)*qgfap(z,k,l)*(1.d0-xx/z)
+           endif
+          enddo
+         enddo
+         enddo
+         fz1=fz1*dlog((zmax1-xx)/(zmin-xx))
+        endif
+        if(zmin1.lt.zmax)then
+         do i=1,7
+         do m=1,2
+          z=1.d0-(1.d0-zmax)*((1.d0-zmin1)/(1.d0-zmax))
+     *    **(.5d0+x1(i)*(m-1.5d0))
+          do k=1,2
+           if(j.ne.3.or.k.ne.1)then
+            fz2=fz2+a1(i)*qgevi(q1,qi,xx/z,j,k)*qgfap(z,k,l)
+     *      *(1.d0/z-1.d0)
+           endif
+          enddo
+         enddo
+         enddo
+         fz2=fz2*dlog((1.d0-zmin1)/(1.d0-zmax))
+        endif
+        if(zmax2.gt.zmin2)then
+         do i=1,7
+         do m=1,2
+          z=zmin2*(zmax2/zmin2)**(.5d0+x1(i)*(m-1.5d0))
+          do k=1,2
+           if(j.ne.3.or.k.ne.1)then
+            fz3=fz3+a1(i)*qgevi(q1,qi,xx/z,j,k)*qgfap(z,k,l)
+           endif
+          enddo
+         enddo
+         enddo
+         fz3=fz3*dlog(zmax2/zmin2)
+        endif
+        qgev=qgev+a1(i1)*(fz1+fz2+fz3)/qgsudx(qi,l)*qgalf(qi/alm)
+       enddo
+       enddo
+       qgev=qgev*dlog(qq/q1)/4.d0*qgsudx(qq,l)
+
+      else
+       fz1=0.d0
+       fz2=0.d0
+       fz3=0.d0
+       zmin1=max(.2d0,zmin)
+       zmax1=min(.2d0,zmax)
+       zmax1=min(5.d0*xx,zmax1)
+       zmax2=min(zmin1,zmax)
+       zmin2=max(zmax1,zmin)
+
+       if(zmax1.gt.zmin)then
+        do i=1,7
+        do m=1,2
+         z=xx+(zmin-xx)*((zmax1-xx)/(zmin-xx))**(.5d0+(m-1.5d0)*x1(i))
+         do k=1,2
+          if(j.ne.3)then
+           fz1=fz1+a1(i)*qgevi(q1,qj,xx/z,j,k)*qgevi(qj,qq,z,k,l)
+     *     *(1.d0-xx/z)
+          elseif(k.ne.1)then
+           fz1=fz1+a1(i)*qgevi(q1,qj,xx/z,3,2)*qgevi(qj,qq,z,3,2)
+     *     *(1.d0-xx/z)
+          endif
+         enddo
+        enddo
+        enddo
+        fz1=fz1*dlog((zmax1-xx)/(zmin-xx))
+       endif
+       if(zmin1.lt.zmax)then
+        do i=1,7
+        do m=1,2
+         z=1.d0-(1.d0-zmax)*((1.d0-zmin1)/(1.d0-zmax))
+     *   **(.5d0+x1(i)*(m-1.5d0))
+         do k=1,2
+          if(j.ne.3)then
+           fz2=fz2+a1(i)*qgevi(q1,qj,xx/z,j,k)*qgevi(qj,qq,z,k,l)
+     *     *(1.d0/z-1.d0)
+          elseif(k.ne.1)then
+           fz2=fz2+a1(i)*qgevi(q1,qj,xx/z,3,2)*qgevi(qj,qq,z,3,2)
+     *     *(1.d0/z-1.d0)
+          endif
+         enddo
+        enddo
+        enddo
+        fz2=fz2*dlog((1.d0-zmin1)/(1.d0-zmax))
+       endif
+       if(zmax2.gt.zmin2)then
+        do i=1,7
+        do m=1,2
+         z=zmin2*(zmax2/zmin2)**(.5d0+x1(i)*(m-1.5d0))
+         do k=1,2
+          if(j.ne.3)then
+           fz2=fz2+a1(i)*qgevi(q1,qj,xx/z,j,k)*qgevi(qj,qq,z,k,l)
+          elseif(k.ne.1)then
+           fz2=fz2+a1(i)*qgevi(q1,qj,xx/z,3,2)*qgevi(qj,qq,z,3,2)
+          endif
+         enddo
+        enddo
+        enddo
+        fz3=fz3*dlog(zmax2/zmin2)
+       endif
+       qgev=(fz1+fz2+fz3)/2.d0
+      endif
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgevi(q1,qq,xx,m,l)
+c------------------------------------------------------------------------
+c qgevi - PDF evolution - interpolation
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      dimension wi(3),wj(3),wk(3)
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr20/ spmax
+      common /qgarr51/ epsxmn
+      common /qgarr52/ evk(40,40,100,3,2)
+
+      qgevi=0.d0
+      if(q1.ge..9999d0*spmax)goto 1
+
+      if(xx.le..1d0)then
+       yx=37.d0-dlog(.1d0/xx)/dlog(.1d0*spmax)*36.d0
+       k=max(1,int(yx))
+       k=min(k,35)
+      elseif(xx.le..9d0)then
+       yx=(xx-.1d0)*40.d0+37.d0
+       k=max(37,int(yx))
+       k=min(k,67)
+      else
+       yx=dlog(10.d0*(1.d0-xx))/log(10.d0*epsxmn)*31.d0+69.d0
+       k=max(69,int(yx))
+       k=min(k,98)
+      endif
+      wk(2)=yx-k
+      wk(3)=wk(2)*(wk(2)-1.d0)*.5d0
+      wk(1)=1.d0-wk(2)+wk(3)
+      wk(2)=wk(2)-2.d0*wk(3)
+
+      qli=log(q1)/dlog(spmax)*39.d0+1.d0
+      qlj=log(qq/q1)/dlog(spmax/q1)*39.d0+1.d0
+      i=max(1,int(1.0001d0*qli))
+      i=min(i,38)
+      wi(2)=qli-i
+      wi(3)=wi(2)*(wi(2)-1.d0)*.5d0
+      wi(1)=1.d0-wi(2)+wi(3)
+      wi(2)=wi(2)-2.d0*wi(3)
+
+      j=max(1,int(1.0001d0*qlj))
+      j=min(j,38)
+      wj(2)=qlj-j
+      wj(3)=wj(2)*(wj(2)-1.d0)*.5d0
+      wj(1)=1.d0-wj(2)+wj(3)
+      wj(2)=wj(2)-2.d0*wj(3)
+
+      do i1=1,3
+      do j1=1,3
+      do k1=1,3
+       k2=k+k1-1
+       qgevi=qgevi+evk(i+i1-1,j+j1-1,k2,m,l)*wi(i1)*wj(j1)*wk(k1)
+      enddo
+      enddo
+      enddo
+1     qgevi=exp(qgevi)*qgfap(xx,m,l)
+      if(m.eq.1.and.l.eq.1.or.m.ne.1.and.l.ne.1)then
+       qgevi=qgevi/4.5d0/qgsudx(q1,m)*qgsudx(qq,m)
+     * *dlog(dlog(qq/alm)/dlog(q1/alm))
+      else
+       qgevi=qgevi*.3d0/(dlog(epsxmn)+.75d0)
+     * *(qgsudx(qq,1)/qgsudx(q1,1)-qgsudx(qq,2)/qgsudx(q1,2))
+      endif
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgpdf(xx,qq,icz,jj)
+c-----------------------------------------------------------------------
+c qgpdf - parton distribution function for proton
+c qq  - virtuality scale,
+c xx  - light cone x,
+c icz - hadron type,
+c jj  - parton type (0 - gluon, 1 - u_v, 2 - d_v, -1 - q_sea)
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr25/ ahv(3)
+      common /qgarr51/ epsxmn
+      common /arr3/   x1(7),a1(7)
+
+      if(jj.eq.0)then
+       qgpdf=qggpdf(xx,icz)
+      elseif(jj.eq.1.or.jj.eq.2)then
+       qgpdf=qggrv(xx,qt0,icz,jj)*(1.d0-xx)**ahv(icz)
+      else
+       qgpdf=qgspdf(xx,icz)
+      endif
+      qgpdf=qgpdf*qgsudx(qq,iabs(jj)+1)/qgsudx(qt0,iabs(jj)+1)
+
+      xmin=xx/(1.d0-epsxmn)
+      if(xmin.lt.1.d0.and.qq.gt.qt0)then
+       dpd1=0.d0
+       dpd2=0.d0
+       xm=max(xmin,.3d0)
+       do i=1,7         !numerical integration over zx
+       do m=1,2
+        zx=1.d0-(1.d0-xm)*(.5d0+(m-1.5d0)*x1(i))**.25d0
+        z=xx/zx
+
+        gl=qggpdf(zx,icz)
+        uv=qggrv(zx,qt0,icz,1)*(1.d0-zx)**ahv(icz)
+        dv=qggrv(zx,qt0,icz,2)*(1.d0-zx)**ahv(icz)
+        sea=qgspdf(zx,icz)
+        if(jj.eq.0)then
+         fz=qgevi(qt0,qq,z,1,1)*gl+qgevi(qt0,qq,z,2,1)*(uv+dv+sea)
+        elseif(jj.eq.1)then
+         fz=qgevi(qt0,qq,z,3,2)*uv
+        elseif(jj.eq.2)then
+         fz=qgevi(qt0,qq,z,3,2)*dv
+        else
+         akns=qgevi(qt0,qq,z,3,2)              !nonsinglet contribution
+         aks=(qgevi(qt0,qq,z,2,2)-akns)        !singlet contribution
+         fz=(qgevi(qt0,qq,z,1,2)*gl+aks*(uv+dv+sea)+akns*sea)
+        endif
+        dpd1=dpd1+a1(i)*fz/zx**2/(1.d0-zx)**3
+       enddo
+       enddo
+       dpd1=dpd1*(1.d0-xm)**4/8.d0*xx
+
+       if(xm.gt.xmin)then
+        do i=1,7         !numerical integration
+        do m=1,2
+         zx=xx+(xm-xx)*((xmin-xx)/(xm-xx))**(.5d0-(m-1.5d0)*x1(i))
+         z=xx/zx
+
+         gl=qggpdf(zx,icz)
+         uv=qggrv(zx,qt0,icz,1)*(1.d0-zx)**ahv(icz)
+         dv=qggrv(zx,qt0,icz,2)*(1.d0-zx)**ahv(icz)
+         sea=qgspdf(zx,icz)
+         if(jj.eq.0)then
+          fz=qgevi(qt0,qq,z,1,1)*gl+qgevi(qt0,qq,z,2,1)*(uv+dv+sea)
+         elseif(jj.eq.1)then
+          fz=qgevi(qt0,qq,z,3,2)*uv
+         elseif(jj.eq.2)then
+          fz=qgevi(qt0,qq,z,3,2)*dv
+         else
+          akns=qgevi(qt0,qq,z,3,2)              !nonsinglet contribution
+          aks=(qgevi(qt0,qq,z,2,2)-akns)        !singlet contribution
+          fz=(qgevi(qt0,qq,z,1,2)*gl+aks*(uv+dv+sea)+akns*sea)
+         endif
+         dpd2=dpd2+a1(i)*fz*(1.d0-xx/zx)/zx
+        enddo
+        enddo
+        dpd2=dpd2*dlog((xm-xx)/(xmin-xx))*.5d0*xx
+       endif
+       qgpdf=qgpdf+dpd2+dpd1
+      endif
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgpdfd(xx,xpomr,qq,icz)
+c-----------------------------------------------------------------------
+c qgpdfd - diffractive sf f2_d^(3)
+c qq    - virtuality scale,
+c xx    - parton light cone x,
+c xpomr - pomeron lc x,
+c icz   - hadron type
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      common /qgarr6/  pi,bm,amws
+      common /qgarr15/ fp(3),rq(2,3),cd(2,3),gsoft(3)
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr25/ ahv(3)
+      common /qgarr51/ epsxmn
+      common /arr3/   x1(7),a1(7)
+
+      qgpdfd=(qgdpdf(xx,xpomr,icz,1)+qgdpdf(xx,xpomr,icz,2))
+     **qgsudx(qq,2)/qgsudx(qt0,2)
+      xmin=xx/(1.d0-epsxmn)
+      if(xmin.lt.xpomr.and.qq.gt.qt0)then
+       dpd1=0.d0
+       dpd2=0.d0
+       xm=max(xmin,.3d0)
+       if(xm.lt.xpomr)then
+        do i=1,7         !numerical integration over zx
+        do m=1,2
+         zx=1.d0-(1.d0-xm)*(1.d0-(.5d0+(m-1.5d0)*x1(i))
+     *   *(1.d0-((1.d0-xpomr)/(1.d0-xm))**4))**.25d0
+         z=xx/zx
+
+         glu=(qgdgdf(zx,xpomr,icz,1)+qgdgdf(zx,xpomr,icz,2))/4.5d0
+         sea=qgdpdf(zx,xpomr,icz,1)+qgdpdf(zx,xpomr,icz,2)
+         fz=qgevi(qt0,qq,z,1,2)*glu+qgevi(qt0,qq,z,2,2)*sea
+         dpd1=dpd1+a1(i)*fz/zx**2/(1.d0-zx)**3
+        enddo
+        enddo
+        dpd1=dpd1*((1.d0-xm)**4-(1.d0-xpomr)**4)/8.d0*xx
+       endif
+
+       xm=min(xm,xpomr)
+       if(xm.gt.xmin)then
+        do i=1,7         !numerical integration
+        do m=1,2
+         zx=xx+(xm-xx)*((xmin-xx)/(xm-xx))**(.5d0-(m-1.5d0)*x1(i))
+         z=xx/zx
+
+         glu=(qgdgdf(zx,xpomr,icz,1)+qgdgdf(zx,xpomr,icz,2))/4.5d0
+         sea=qgdpdf(zx,xpomr,icz,1)+qgdpdf(zx,xpomr,icz,2)
+         fz=qgevi(qt0,qq,z,1,2)*glu+qgevi(qt0,qq,z,2,2)*sea
+         dpd2=dpd2+a1(i)*fz*(1.d0-xx/zx)/zx
+        enddo
+        enddo
+        dpd2=dpd2*dlog((xm-xx)/(xmin-xx))*.5d0*xx
+       endif
+       qgpdfd=qgpdfd+dpd2+dpd1
+      endif
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgf2c(xx,qq,icz)
+c-----------------------------------------------------------------------
+c qgf2c - c-quark contribution to f2
+c qq  - virtuality scale,
+c xx  - light cone x,
+c icz - hadron type,
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      common /arr3/   x1(7),a1(7)
+
+      qgf2c=0.d0
+      qcmass=1.3d0
+      s2min=4.*qcmass**2+qq
+      xmin=s2min*xx/qq
+
+      if(xmin.lt.1.d0)then
+       do i=1,7          !numerical integration over z1
+       do m=1,2
+        z1=xmin**(.5d0+x1(i)*(m-1.5d0))
+        sdc=qgdbor(qq,xx/z1,qcmass**2)
+        glu=qgpdf(z1,s2min-qq,icz,0)
+        qgf2c=qgf2c+a1(i)*sdc*glu
+       enddo
+       enddo
+       qgf2c=-qgf2c*dlog(xmin)*.5d0
+      endif
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgf2cd(xx,xpomr,qq,icz)
+c-----------------------------------------------------------------------
+c qgf2cd - c-quark contribution to diffractive sf
+c qq  - virtuality scale,
+c xx  - light cone x,
+c icz - hadron type,
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      common /arr3/   x1(7),a1(7)
+
+      qgf2cd=0.d0
+      qcmass=1.3d0
+      s2min=4.*qcmass**2+qq
+      xmin=s2min*xx/qq
+
+      if(xmin.lt.xpomr)then
+       do i=1,7          !numerical integration over z1
+       do m=1,2
+        z1=xpomr*(xmin/xpomr)**(.5d0+x1(i)*(m-1.5d0))
+        sdc=qgdbor(qq,xx/z1,qcmass**2)
+        glu=qgdgdf(z1,xpomr,icz,1)+qgdgdf(z1,xpomr,icz,2)
+        qgf2cd=qgf2cd+a1(i)*sdc*glu
+       enddo
+       enddo
+       qgf2cd=qgf2cd*dlog(xpomr/xmin)*.5d0
+      endif
+      return
+      end
+
+c------------------------------------------------------------------------
+      double precision function qgdbor(qq,zz,q2mass)
+c-----------------------------------------------------------------------
+c qgdbor - DIS c-quark cross-section
+c qq      - photon virtuality
+c s=2(pq) - s_true + qq,
+c-----------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+
+      qgdbor=0.
+      qtq=4.d0*q2mass*zz/qq/(1.d0-zz)
+      if(qtq.ge.1.d0)return
+      bet=dsqrt(1.d0-qtq)
+
+      qgdbor=qgalf(4.d0*q2mass/alm)/2.25d0*zz
+     **(dlog((1.d0+bet)/(1.d0-bet))*(1.d0-2.d0*zz*(1.d0-zz)
+     *-8.d0*(zz*q2mass/qq)**2+4.d0*zz*(1.d0-3.d0*zz)*q2mass/qq)
+     *+bet*(-1.d0-4.d0*zz*(1.d0-zz)*q2mass/qq+8.d0*zz*(1.d0-zz)))
+      return
+      end
+
+c=============================================================================
+      double precision function qgjeto(qi,qj,s,iq1,iq2)
+c-----------------------------------------------------------------------------
+c qgjeto - hard 2->2 parton scattering born cross-section
+c s is the c.m. energy square for the scattering process,
+c iq1 - parton type at current end of the ladder (0 - g, 1,2 etc. - q)
+c iq2 - parton type at opposite end of the ladder (0 - g, 1,2 etc. - q)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgarr51/ epsxmn
+      common /qgdebug/    debug
+      common /arr3/     x1(7),a1(7)
+
+      if(debug.ge.2)write (moniou,201)qi,qj,s,iq1,iq2
+
+      qgjeto=0.d0
+      qq=max(qi,qj)
+
+      zmin=qq*fqscal*4.d0/s
+      zmax=1.d0-epsxmn
+      if(zmin.ge.zmax)return
+
+      dpx1=0.d0
+      zmin1=min(.2d0,1.d0-zmin)
+      do i1=1,7
+      do m1=1,2
+       z=1.d0-epsxmn*(zmin1/epsxmn)**(.5d0+x1(i1)*(m1-1.5d0))
+
+       si=z*s
+       fb=qgjeti(qi,qj,si,z,1.d0,iq1,iq2,1)
+       dpx1=dpx1+a1(i1)*fb*(1.d0-z)
+      enddo
+      enddo
+      dpx1=dpx1*dlog(zmin1/epsxmn)
+
+      dpx2=0.d0
+      if(zmin.lt..8d0)then
+       zmin1=zmin**(-delh)
+       zmax1=.8d0**(-delh)
+       do i1=1,7
+       do m1=1,2
+        z=(.5d0*(zmax1+zmin1+(zmax1-zmin1)*x1(i1)*(2*m1-3)))
+     *  **(-1.d0/delh)
+
+        si=z*s
+        fb=qgjeti(qi,qj,si,z,1.d0,iq1,iq2,1)
+        dpx2=dpx2+a1(i1)*fb*z**(1.d0+delh)
+       enddo
+       enddo
+       dpx2=dpx2*(zmin1-zmax1)/delh
+      endif
+      qgjeto=(dpx1+dpx2)/qgsudx(qj,iabs(iq2)+1)*pi**3
+
+      if(debug.ge.3)write (moniou,202)qgjeto
+201   format(2x,'qgjeto: qi=',e10.3,2x,'qj=',e10.3,2x,
+     *'s= ',e10.3,2x,'iq1= ',i1,2x,'iq2= ',i1)
+202   format(2x,'qgjeto=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgjett(qi,qj,s,iq1,iq2)
+c-----------------------------------------------------------------------------
+c qgjett - hard 2->2 parton scattering born cross-section
+c s is the c.m. energy square for the scattering process,
+c iq1 - parton type at current end of the ladder (0 - g, 1,2 etc. - q)
+c iq2 - parton type at opposite end of the ladder (0 - g, 1,2 etc. - q)
+c-----------------------------------------------------------------------------
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr17/ dels,alfp,sigs,rr,r3p,g3p,delh,sgap
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgarr51/ epsxmn
+      common /qgdebug/    debug
+      common /arr3/     x1(7),a1(7)
+
+      if(debug.ge.2)write (moniou,201)qi,qj,s,iq1,iq2
+
+      qgjett=0.d0
+      qq=max(qi,qj)
+
+      zmin=qq*fqscal*4.d0/s
+      zmax=(1.d0-epsxmn)**2
+      if(zmin.ge.zmax)return
+      zmin1=zmin**(-delh)
+      zmax1=zmax**(-delh)
+      do i1=1,7
+      do m1=1,2
+       z=(.5d0*(zmax1+zmin1+(zmax1-zmin1)*x1(i1)*(2*m1-3)))
+     * **(-1.d0/delh)
+
+       si=z*s
+       fb1=0.d0
+       zmin2=min(.2d0,1.d0-dsqrt(z))
+       do i2=1,7
+       do m2=1,2
+        z1=1.d0-epsxmn*(zmin2/epsxmn)**(.5d0+x1(i2)*(m2-1.5d0))
+        z2=z/z1
+
+        fb1=fb1+a1(i2)*(qgjeti(qi,qj,si,z1,z2,iq1,iq2,2)
+     *  +qgjeti(qi,qj,si,z2,z1,iq1,iq2,2))*(1.d0/z1-1.d0)
+       enddo
+       enddo
+       fb1=fb1*dlog(zmin2/epsxmn)
+
+       fb2=0.d0
+       if(z.lt..64d0)then
+        do i2=1,7
+        do m2=1,2
+         z1=.8d0*(dsqrt(z)/.8d0)**(.5d0+x1(i2)*(m2-1.5d0))
+          z2=z/z1
+
+         fb2=fb2+a1(i2)*(qgjeti(qi,qj,si,z1,z2,iq1,iq2,2)
+     *   +qgjeti(qi,qj,si,z2,z1,iq1,iq2,2))
+        enddo
+        enddo
+        fb2=fb2*dlog(.64d0/z)/2.d0
+       endif
+
+       qgjett=qgjett+a1(i1)*(fb1+fb2)*z**(1.d0+delh)
+      enddo
+      enddo
+      qgjett=qgjett*(zmin1-zmax1)/delh*pi**3/2.d0
+
+      if(debug.ge.3)write (moniou,202)qgjett
+201   format(2x,'qgjett: qi=',e10.3,2x,'qj=',e10.3,2x,
+     *'s= ',e10.3,2x,'iq1= ',i1,2x,'iq2= ',i1)
+202   format(2x,'qgjett=',e10.3)
+      return
+      end
+
+c=============================================================================
+      double precision function qgjeti(qi,qj,si,z1,z2,iq1,iq2,jj)
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgarr51/ epsxmn
+      common /qgdebug/    debug
+      common /arr3/     x1(7),a1(7)
+
+      qgjeti=0.d0
+      qq=max(qi,qj)
+      tmin=qq*fqscal/(.5d0+dsqrt(max(0.d0,.25d0-qq*fqscal/si)))
+      if(tmin.ge.si/2.d0)return
+      do i=1,7
+      do m=1,2
+       t=2.d0*tmin/(1.d0+2.d0*tmin/si
+     *   -x1(i)*(2*m-3)*(1.d0-2.d0*tmin/si))
+       qt=t*(1.d0-t/si)
+
+       fb=0.d0
+       if(jj.eq.1)then
+        do iql=1,2
+         iq=2*iql-2
+         dfb=0.d0
+         do n=1,3
+          dfb=dfb+qgfbor(si,t,iq,iq2,n)+qgfbor(si,si-t,iq,iq2,n)
+         enddo
+         if(iq.eq.iq2)dfb=dfb/2.d0
+         fb=fb+dfb*qgevi(qi,qt/fqscal,z1,iabs(iq1)+1,iql)
+        enddo
+        fb=fb*qgsudx(qt/fqscal,iabs(iq2)+1)
+       else
+        do iql=1,2
+         iq=2*iql-2
+        do iqr=1,2
+         dfb=0.d0
+         do n=1,3
+          dfb=dfb+qgfbor(si,t,iq,iqr-1,n)+qgfbor(si,si-t,iq,iqr-1,n)
+         enddo
+         if(iq.eq.iqr-1)dfb=dfb/2.d0
+         fb=fb+dfb*qgevi(qi,qt/fqscal,z1,iabs(iq1)+1,iql)
+     *   *qgevi(qj,qt/fqscal,z2,iabs(iq2)+1,iqr)
+        enddo
+        enddo
+       endif
+
+       qgjeti=qgjeti+a1(i)*fb*qgalf(qt/fqscal/alm)**2*t**2
+      enddo
+      enddo
+      qgjeti=qgjeti*(1.d0/tmin-2.d0/si)/si**2
+      return
+      end
+
+c=============================================================================
+      double precision function qgptj(s,pt,y0,sigin)
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr6/  pi,bm,amws
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgarr51/ epsxmn
+      common /qgdebug/    debug
+      common /arr3/     x1(7),a1(7)
+
+      qgptj=0.d0
+      zmin=4.d0*pt**2/s
+      xt=2.d0*pt*exp(y0)/dsqrt(s)
+      zmax=min(1.d0,xt**2/(2.d0*xt-zmin))
+      if(zmax.le.zmin)return
+
+      qq=pt**2/fqscal
+      do i1=1,7
+      do m1=1,2
+       z=zmax*(zmin/zmax)**(.5d0+x1(i1)*(m1-1.5d0))
+       si=z*s
+       t=2.d0*pt**2/(1.d0+dsqrt(max(0.d0,1.d0-zmin/z)))
+
+       xmax=min(1.d0,xt/(1.d0+dsqrt(max(0.d0,1.d0-zmin/z))))
+       xmin=max(z,xmax*exp(-2.d0*y0))
+       do i2=1,7
+       do m2=1,2
+        xp=xmax*(xmin/xmax)**(.5d0+x1(i2)*(m2-1.5d0))
+        xm=z/xp
+
+        glu1=qgpdf(xp,qq,2,0)
+        glu2=qgpdf(xm,qq,2,0)
+        seav2=qgpdf(xm,qq,2,-1)+qgpdf(xm,qq,2,1)+qgpdf(xm,qq,2,2)
+
+        qgptj=qgptj+a1(i1)*a1(i2)*(qgptjb(si,pt**2,t,1)*glu1*glu2
+     *  +qgptjb(si,pt**2,t,2)*glu1*seav2)
+     *  *dlog(xmax/xmin)/(1.d0-2.d0*t/si)
+       enddo
+       enddo
+      enddo
+      enddo
+      qgptj=qgptj*dlog(zmax/zmin)*pi**3*.39d0/sigin  *2.  !2 jets
+      return
+      end
+
+c=============================================================================
+      double precision function qgptjb(si,qt,t,jj)
+      implicit double precision (a-h,o-z)
+      integer debug
+      common /qgarr18/ alm,qt0,qtf,betp,dgqq
+      common /qgarr26/ factk,fqscal
+      common /qgarr43/ moniou
+      common /qgdebug/    debug
+
+      if(jj.eq.1)then
+       qgptjb=qgfbor(si,t,0,0,1)
+      else       !if(jj.eq.2)then
+       qgptjb=qgfbor(si,t,0,1,1)
+      endif
+      qgptjb=qgptjb*qgalf(qt/fqscal/alm)**2/si**2
+      return
+      end
diff --git a/modules/qgsjetII/qgsjet-II-04.cpp b/modules/qgsjetII/qgsjet-II-04.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..eea2724a88c07f1b028aba0703173a1ebc1f0504
--- /dev/null
+++ b/modules/qgsjetII/qgsjet-II-04.cpp
@@ -0,0 +1,23 @@
+#include <qgsjet-II-04.hpp>
+
+#include <iostream>
+
+datadir::datadir(const std::string& dir) {
+  if (dir.length() > 130) {
+    std::cerr << "QGSJetII error, will cut datadir \"" << dir
+              << "\" to 130 characters: " << std::endl;
+  }
+  int i = 0;
+  for (i = 0; i < std::min(130, int(dir.length())); ++i) data[i] = dir[i];
+  data[i + 0] = ' ';
+  data[i + 1] = '\0';
+}
+
+
+/**
+   @function qgran
+
+   link to random number generation
+ */
+double qgran_(int&) { return qgsjetII::rndm_interface(); } 
+
diff --git a/modules/qgsjetII/qgsjet-II-04.hpp b/modules/qgsjetII/qgsjet-II-04.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7b41f01ed1413874a27a36b1f8a0b41f4ba2f5f2
--- /dev/null
+++ b/modules/qgsjetII/qgsjet-II-04.hpp
@@ -0,0 +1,113 @@
+/*
+ * (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>
+
+namespace qgsjetII {
+
+  /**
+   * \function qgsjetII::rndm_interface
+   *
+   * this is the random number hook to external packages.
+   *
+   * CORSIKA8, for example, has to provide an implementation of this.
+   **/
+  extern double rndm_interface();
+
+} // namespace sibyll
+
+//----------------------------------------------
+//  C++ interface for the QGSJetII event generator
+//----------------------------------------------
+// wrapper
+
+extern "C" {
+
+// data memory layout
+
+extern struct { int nsp; } qgarr12_;
+
+const int nptmax = 95000;
+const int iapmax = 208;
+
+extern struct {
+  double esp[nptmax][4];
+  int ich[nptmax];
+} qgarr14_;
+
+extern struct {
+  // c nsf - number of secondary fragments;
+  // c iaf(i) - mass of the i-th fragment
+  int nsf;
+  int iaf[iapmax];
+} qgarr13_;
+
+extern struct {
+  int nwt;
+  int nwp;
+} qgarr55_;
+
+/**
+   Small helper class to provide a data-directory name in the format qgsjetII expects
+ */
+class datadir {
+private:
+  datadir operator=(const std::string& dir);
+  datadir operator=(const datadir&);
+
+public:
+  datadir(const std::string& dir);
+  char data[132];
+};
+
+// functions
+void qgset_();
+void qgaini_(
+    const char* datdir); // Note: there is a length limiation 132 from fortran-qgsjet here
+
+/**
+   @function qgini_
+
+   additional initialization procedure per event
+
+   @parameter e0n  - interaction energy (per hadron/nucleon),
+   @parameter icp0 - hadron type (+-1 - pi+-, +-2 - p(p~), +-3 - n(n~), +-4 - K+-, +-5 -
+   K_l/s),
+   @parameter iap  - projectile mass number (1 - for a hadron),
+   @parameter iat  - target mass number
+*/
+void qgini_(const double& e0n, const int& icp0, const int& iap, const int& iat);
+
+/**
+   @function qgconf_
+
+   generate one event configuration
+*/
+void qgconf_();
+
+/**
+   @function qgsect_
+
+   hadron-nucleus (hadron-nucleus) particle production cross section
+
+   @parameter e0n lab. energy per projectile nucleon (hadron)
+   @parameter icz hadron class (1 - pion, 2 - nucleon, 3 - kaon)
+   @parameter iap projectile mass number (1=<iap<=iapmax),
+   @parameter iat target mass number     (1=<iat<=iapmax)
+ */
+double qgsect_(const double& e0n, const int& icz, const int& iap0, const int& iat0);
+
+/**
+   @function qgran
+
+   link to random number generation
+ */
+double qgran_(int&);
+}
diff --git a/modules/sibyll/CMakeLists.txt b/modules/sibyll/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..6dfe2d5c3f739d4494024adfe04bff1d3b861749
--- /dev/null
+++ b/modules/sibyll/CMakeLists.txt
@@ -0,0 +1,45 @@
+set (
+  MODEL_SOURCES
+  sibyll2.3d.cpp
+  sibyll2.3d.f
+  nuclib.f
+  signuc.f
+  gasdev.f
+  )
+
+set (
+  MODEL_HEADERS
+  sibyll2.3d.hpp
+  nuclib.hpp
+  )
+
+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)
+
+target_include_directories (
+  Sibyll
+  PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+  $<INSTALL_INTERFACE:include/include>
+  )
+
+target_include_directories (
+  Sibyll_static
+  PUBLIC
+  $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+  $<INSTALL_INTERFACE:include/include>
+  )
+
+target_link_libraries (
+  Sibyll_static
+  PUBLIC
+  gfortran
+  )
+
+# add sibyll to corsika8 build
+add_dependencies (CORSIKA8 Sibyll_static)
+target_link_libraries (CORSIKA8 INTERFACE Sibyll_static)
diff --git a/Processes/Sibyll/gasdev.f b/modules/sibyll/gasdev.f
similarity index 100%
rename from Processes/Sibyll/gasdev.f
rename to modules/sibyll/gasdev.f
diff --git a/Processes/Sibyll/nuclib.f b/modules/sibyll/nuclib.f
similarity index 100%
rename from Processes/Sibyll/nuclib.f
rename to modules/sibyll/nuclib.f
diff --git a/Processes/Sibyll/nuclib.h b/modules/sibyll/nuclib.hpp
similarity index 100%
rename from Processes/Sibyll/nuclib.h
rename to modules/sibyll/nuclib.hpp
diff --git a/Framework/Utilities/sgn.h b/modules/sibyll/sibyll2.3d.cpp
similarity index 52%
rename from Framework/Utilities/sgn.h
rename to modules/sibyll/sibyll2.3d.cpp
index 2cc3a936b2f326835e46ad1ed4a96fb63080975f..48da5882c59bdee70dc15af77e7dee9bf89d2dc3 100644
--- a/Framework/Utilities/sgn.h
+++ b/modules/sibyll/sibyll2.3d.cpp
@@ -1,20 +1,16 @@
 /*
  * (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 <sibyll2.3d.hpp>
 
-namespace corsika::utl {
+#include <cmath>
 
-  //! sign function without branches
-  template <typename T>
-  static int sgn(T val) {
-    return (T(0) < val) - (val < T(0));
-  }
+int get_nwounded() { return s_chist_.nwd; }
+double get_sibyll_mass2(int& id) { return s_mass1_.am2[std::abs(id) - 1]; }
 
-} // namespace corsika::utl
+double s_rndm_(int&) { return ::sibyll::rndm_interface(); }
diff --git a/Processes/Sibyll/sibyll2.3d.f b/modules/sibyll/sibyll2.3d.f
similarity index 100%
rename from Processes/Sibyll/sibyll2.3d.f
rename to modules/sibyll/sibyll2.3d.f
diff --git a/Processes/Sibyll/sibyll2.3d.h b/modules/sibyll/sibyll2.3d.hpp
similarity index 79%
rename from Processes/Sibyll/sibyll2.3d.h
rename to modules/sibyll/sibyll2.3d.hpp
index 9721e1747dc9fa7c6fa41cecb96a794d7aa7b479..741d45775bf00a4503e242cb4245605b8448d89a 100644
--- a/Processes/Sibyll/sibyll2.3d.h
+++ b/modules/sibyll/sibyll2.3d.hpp
@@ -7,6 +7,27 @@
  */
 
 #pragma once
+
+/**
+ * \file sibyll2.3d.hpp
+ *
+ * Interface definition to link to sibyll library.
+ *
+ */
+
+namespace sibyll {
+
+  /**
+   * \function sibyll::rndm_interface
+   *
+   * this is the random number hook to external packages.
+   *
+   * CORSIKA8, for example, has to provide an implementation of this.
+   **/
+  double rndm_interface();
+
+} // namespace sibyll
+
 //----------------------------------------------
 //  C++ interface for the SIBYLL event generator
 //----------------------------------------------
@@ -16,10 +37,14 @@ extern "C" {
 
 typedef char s_name[6];
 
-// SIBYLL particle stack (FORTRAN COMMON)
-// variables are: np : numer of particles on stack
-//                 p : 4momentum + mass of particles on stack
-//             llist : id of particles on stack
+/**
+   \struct s_plist_
+
+  SIBYLL particle stack (FORTRAN COMMON)
+  variables are: np : numer of particles on stack
+                  p : 4momentum + mass of particles on stack
+              llist : id of particles on stack
+ **/
 extern struct {
   double p[5][8000];
   int llist[8000];
diff --git a/Processes/Sibyll/signuc.f b/modules/sibyll/signuc.f
similarity index 100%
rename from Processes/Sibyll/signuc.f
rename to modules/sibyll/signuc.f
diff --git a/modules/urqmd/CMakeLists.txt b/modules/urqmd/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..4c37ed1e0cd735cc3dca3364570e84dee550b5c0
--- /dev/null
+++ b/modules/urqmd/CMakeLists.txt
@@ -0,0 +1,57 @@
+cmake_minimum_required (VERSION 3.1)
+project (libUrQMD)
+
+set (
+  MODEL_SOURCES
+  urqmd.cpp
+  urqmdInterface.F
+  addpart.f
+  angdis.f
+  anndec.f
+  blockres.f
+  boxprg.f
+  cascinit.f
+  coload.f
+  dectim.f
+  delpart.f
+  detbal.f
+  dwidth.f
+  error.f
+  getmass.f
+  getspin.f
+  init.f
+  iso.f
+  ityp2pdg.f
+  jdecay2.f
+  make22.f
+  numrec.f
+  output.f
+  paulibl.f
+  proppot.f
+  saveinfo.f
+  scatter.f
+  siglookup.f
+  string.f
+  tabinit.f
+  urqmd.f
+  whichres.f
+)
+
+set (
+  MODEL_HEADERS
+  urqmd.hpp
+  )
+
+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)
+
+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)
+
+
+# add UrQMD to CORSIKA8 build
+add_dependencies (CORSIKA8 UrQMD_static)
+target_link_libraries (CORSIKA8 INTERFACE UrQMD_static)
diff --git a/Processes/UrQMD/Copyright b/modules/urqmd/Copyright
similarity index 100%
rename from Processes/UrQMD/Copyright
rename to modules/urqmd/Copyright
diff --git a/Processes/UrQMD/README b/modules/urqmd/README
similarity index 100%
rename from Processes/UrQMD/README
rename to modules/urqmd/README
diff --git a/Processes/UrQMD/addpart.f b/modules/urqmd/addpart.f
similarity index 100%
rename from Processes/UrQMD/addpart.f
rename to modules/urqmd/addpart.f
diff --git a/Processes/UrQMD/angdis.f b/modules/urqmd/angdis.f
similarity index 100%
rename from Processes/UrQMD/angdis.f
rename to modules/urqmd/angdis.f
diff --git a/Processes/UrQMD/anndec.f b/modules/urqmd/anndec.f
similarity index 100%
rename from Processes/UrQMD/anndec.f
rename to modules/urqmd/anndec.f
diff --git a/Processes/UrQMD/blockres.f b/modules/urqmd/blockres.f
similarity index 100%
rename from Processes/UrQMD/blockres.f
rename to modules/urqmd/blockres.f
diff --git a/Processes/UrQMD/boxinc.f b/modules/urqmd/boxinc.f
similarity index 100%
rename from Processes/UrQMD/boxinc.f
rename to modules/urqmd/boxinc.f
diff --git a/Processes/UrQMD/boxprg.f b/modules/urqmd/boxprg.f
similarity index 100%
rename from Processes/UrQMD/boxprg.f
rename to modules/urqmd/boxprg.f
diff --git a/Processes/UrQMD/cascinit.f b/modules/urqmd/cascinit.f
similarity index 100%
rename from Processes/UrQMD/cascinit.f
rename to modules/urqmd/cascinit.f
diff --git a/Processes/UrQMD/colltab.f b/modules/urqmd/colltab.f
similarity index 100%
rename from Processes/UrQMD/colltab.f
rename to modules/urqmd/colltab.f
diff --git a/Processes/UrQMD/coload.f b/modules/urqmd/coload.f
similarity index 100%
rename from Processes/UrQMD/coload.f
rename to modules/urqmd/coload.f
diff --git a/Processes/UrQMD/comnorm.f b/modules/urqmd/comnorm.f
similarity index 100%
rename from Processes/UrQMD/comnorm.f
rename to modules/urqmd/comnorm.f
diff --git a/Processes/UrQMD/comres.f b/modules/urqmd/comres.f
similarity index 100%
rename from Processes/UrQMD/comres.f
rename to modules/urqmd/comres.f
diff --git a/Processes/UrQMD/coms.f b/modules/urqmd/coms.f
similarity index 100%
rename from Processes/UrQMD/coms.f
rename to modules/urqmd/coms.f
diff --git a/Processes/UrQMD/comstr.f b/modules/urqmd/comstr.f
similarity index 100%
rename from Processes/UrQMD/comstr.f
rename to modules/urqmd/comstr.f
diff --git a/Processes/UrQMD/comwid.f b/modules/urqmd/comwid.f
similarity index 100%
rename from Processes/UrQMD/comwid.f
rename to modules/urqmd/comwid.f
diff --git a/Processes/UrQMD/dectim.f b/modules/urqmd/dectim.f
similarity index 100%
rename from Processes/UrQMD/dectim.f
rename to modules/urqmd/dectim.f
diff --git a/Processes/UrQMD/delpart.f b/modules/urqmd/delpart.f
similarity index 100%
rename from Processes/UrQMD/delpart.f
rename to modules/urqmd/delpart.f
diff --git a/Processes/UrQMD/detbal.f b/modules/urqmd/detbal.f
similarity index 100%
rename from Processes/UrQMD/detbal.f
rename to modules/urqmd/detbal.f
diff --git a/Processes/UrQMD/dwidth.f b/modules/urqmd/dwidth.f
similarity index 100%
rename from Processes/UrQMD/dwidth.f
rename to modules/urqmd/dwidth.f
diff --git a/Processes/UrQMD/error.f b/modules/urqmd/error.f
similarity index 100%
rename from Processes/UrQMD/error.f
rename to modules/urqmd/error.f
diff --git a/Processes/UrQMD/freezeout.f b/modules/urqmd/freezeout.f
similarity index 100%
rename from Processes/UrQMD/freezeout.f
rename to modules/urqmd/freezeout.f
diff --git a/Processes/UrQMD/getmass.f b/modules/urqmd/getmass.f
similarity index 100%
rename from Processes/UrQMD/getmass.f
rename to modules/urqmd/getmass.f
diff --git a/Processes/UrQMD/getspin.f b/modules/urqmd/getspin.f
similarity index 100%
rename from Processes/UrQMD/getspin.f
rename to modules/urqmd/getspin.f
diff --git a/Processes/UrQMD/init.f b/modules/urqmd/init.f
similarity index 100%
rename from Processes/UrQMD/init.f
rename to modules/urqmd/init.f
diff --git a/Processes/UrQMD/inputs.f b/modules/urqmd/inputs.f
similarity index 100%
rename from Processes/UrQMD/inputs.f
rename to modules/urqmd/inputs.f
diff --git a/Processes/UrQMD/iso.f b/modules/urqmd/iso.f
similarity index 100%
rename from Processes/UrQMD/iso.f
rename to modules/urqmd/iso.f
diff --git a/Processes/UrQMD/ityp2pdg.f b/modules/urqmd/ityp2pdg.f
similarity index 100%
rename from Processes/UrQMD/ityp2pdg.f
rename to modules/urqmd/ityp2pdg.f
diff --git a/Processes/UrQMD/jdecay2.f b/modules/urqmd/jdecay2.f
similarity index 100%
rename from Processes/UrQMD/jdecay2.f
rename to modules/urqmd/jdecay2.f
diff --git a/Processes/UrQMD/make22.f b/modules/urqmd/make22.f
similarity index 100%
rename from Processes/UrQMD/make22.f
rename to modules/urqmd/make22.f
diff --git a/Processes/UrQMD/newpart.f b/modules/urqmd/newpart.f
similarity index 100%
rename from Processes/UrQMD/newpart.f
rename to modules/urqmd/newpart.f
diff --git a/Processes/UrQMD/numrec.f b/modules/urqmd/numrec.f
similarity index 100%
rename from Processes/UrQMD/numrec.f
rename to modules/urqmd/numrec.f
diff --git a/Processes/UrQMD/options.f b/modules/urqmd/options.f
similarity index 100%
rename from Processes/UrQMD/options.f
rename to modules/urqmd/options.f
diff --git a/Processes/UrQMD/outcom.f b/modules/urqmd/outcom.f
similarity index 100%
rename from Processes/UrQMD/outcom.f
rename to modules/urqmd/outcom.f
diff --git a/Processes/UrQMD/output.f b/modules/urqmd/output.f
similarity index 100%
rename from Processes/UrQMD/output.f
rename to modules/urqmd/output.f
diff --git a/Processes/UrQMD/paulibl.f b/modules/urqmd/paulibl.f
similarity index 100%
rename from Processes/UrQMD/paulibl.f
rename to modules/urqmd/paulibl.f
diff --git a/Processes/UrQMD/proppot.f b/modules/urqmd/proppot.f
similarity index 100%
rename from Processes/UrQMD/proppot.f
rename to modules/urqmd/proppot.f
diff --git a/Processes/UrQMD/saveinfo.f b/modules/urqmd/saveinfo.f
similarity index 100%
rename from Processes/UrQMD/saveinfo.f
rename to modules/urqmd/saveinfo.f
diff --git a/Processes/UrQMD/scatter.f b/modules/urqmd/scatter.f
similarity index 100%
rename from Processes/UrQMD/scatter.f
rename to modules/urqmd/scatter.f
diff --git a/Processes/UrQMD/siglookup.f b/modules/urqmd/siglookup.f
similarity index 100%
rename from Processes/UrQMD/siglookup.f
rename to modules/urqmd/siglookup.f
diff --git a/Processes/UrQMD/string.f b/modules/urqmd/string.f
similarity index 100%
rename from Processes/UrQMD/string.f
rename to modules/urqmd/string.f
diff --git a/Processes/UrQMD/tabinit.f b/modules/urqmd/tabinit.f
similarity index 100%
rename from Processes/UrQMD/tabinit.f
rename to modules/urqmd/tabinit.f
diff --git a/Framework/Utilities/try_feenableexcept.cc b/modules/urqmd/urqmd.cpp
similarity index 71%
rename from Framework/Utilities/try_feenableexcept.cc
rename to modules/urqmd/urqmd.cpp
index d70dbebadff7a8ba3841daa65a7f51c333a5cafa..c810dfdec6d7ecffd1c5a8339242fd578e3659e1 100644
--- a/Framework/Utilities/try_feenableexcept.cc
+++ b/modules/urqmd/urqmd.cpp
@@ -6,9 +6,9 @@
  * the license.
  */
 
-#include <cfenv>
+#include <urqmd.hpp>
+
+namespace urqmd {
+  double ranf_(int&) { return ::urqmd::rndm_interface(); }
+}
 
-int main() {
-  feenableexcept(FE_ALL_EXCEPT);
-  return 0;
-}
\ No newline at end of file
diff --git a/Processes/UrQMD/urqmd.f b/modules/urqmd/urqmd.f
similarity index 100%
rename from Processes/UrQMD/urqmd.f
rename to modules/urqmd/urqmd.f
diff --git a/modules/urqmd/urqmd.hpp b/modules/urqmd/urqmd.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a9dbd4a0aec39564aa5ebddea751708f5014f413
--- /dev/null
+++ b/modules/urqmd/urqmd.hpp
@@ -0,0 +1,115 @@
+/*
+ * (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 <array>
+
+/**
+ * \file urqmd.hpp
+ *
+ * Interface file for the urqmd library.
+ */
+
+namespace urqmd {
+  /**
+   * \function urqmd::rndm_interface
+   *
+   * this is the random number hook to external packages.
+   *
+   * CORSIKA8, for example, has to provide an implementation of this.
+   **/
+  extern double rndm_interface();
+} // namespace urqmd
+
+namespace urqmd {
+
+  namespace constants {
+    // from coms.f
+    int constexpr nmax = 500;
+
+    // from options.f
+    int constexpr numcto = 400;
+    int constexpr numctp = 400;
+
+    // from inputs.f
+    int constexpr aamax = 300;
+
+  } // namespace constants
+
+  template <typename T>
+  using nmaxArray = std::array<T, constants::nmax>;
+  using nmaxIntArray = nmaxArray<int>;
+  using nmaxDoubleArray = nmaxArray<double>;
+
+  extern "C" {
+
+  void urqmd_(int&);
+  void iniurqmdc8_();
+  double ranf_(int&);
+  void cascinit_(int const&, int const&, int const&);
+  double nucrad_(int const&);
+  int pdgid_(int&, int&);
+  double sigtot_(int&, int&, double&);
+
+  // defined in coms.f
+  extern struct {
+    int npart, nbar, nmes, ctag, nsteps, uid_cnt, ranseed, event;
+    int Ap; // projectile mass number (in case of nucleus)
+    int At; // target mass number (in case of nucleus)
+    int Zp; // projectile charge number (in case of nucleus)
+    int Zt; // target charge number (in case of nucleus)
+    int eos, dectag, NHardRes, NSoftRes, NDecRes, NElColl, NBlColl;
+  } sys_;
+
+  extern struct {
+    double time, acttime, bdist, bimp, bmin;
+    double ebeam; // lab-frame energy of projectile
+    double ecm;
+  } rsys_;
+
+  // defined in coms.f
+  extern struct {
+    nmaxIntArray spin, ncoll, charge, ityp, lstcoll, iso3, origin, strid, uid;
+  } isys_;
+
+  // defined in coor.f
+  extern struct {
+    nmaxDoubleArray r0, rx, ry, rz, p0, px, py, pz, fmass, rww, dectime;
+  } coor_;
+
+  // defined in inputs.f
+  extern struct {
+    int nevents;
+    std::array<int, 2> spityp; // particle codes of: [0]: projectile, [1]: target
+    int prspflg;               // projectile special flag
+    int trspflg; // target special flag, set to 1 unless target is nucleus > H
+    std::array<int, 2> spiso3; // particle codes of: [0]: projectile, [1]: target
+    int outsteps, bflag, srtflag, efuncflag, nsrt, npb, firstev;
+  } inputs_;
+
+  // defined in inputs.f
+  extern struct {
+    double srtmin, srtmax, pbeam, betann, betatar, betapro, pbmin, pbmax;
+  } input2_;
+
+  // defined in options.f
+  extern struct {
+    std::array<double, constants::numcto> CTOption;
+    std::array<double, constants::numctp> CTParam;
+  } options_;
+
+  extern struct {
+    int fixedseed, bf13, bf14, bf15, bf16, bf17, bf18, bf19, bf20;
+  } loptions_;
+
+  // defined in urqmdInterface.F
+  extern struct { std::array<double, 3> xs, bim; } cxs_u2_;
+  }
+
+} // namespace urqmd
diff --git a/Processes/UrQMD/urqmdInterface.F b/modules/urqmd/urqmdInterface.F
similarity index 100%
rename from Processes/UrQMD/urqmdInterface.F
rename to modules/urqmd/urqmdInterface.F
diff --git a/modules/urqmd/urqmd_xs.cc b/modules/urqmd/urqmd_xs.cc
new file mode 100644
index 0000000000000000000000000000000000000000..d85c56e78fd36cade6ccf9119828815721e69929
--- /dev/null
+++ b/modules/urqmd/urqmd_xs.cc
@@ -0,0 +1,39 @@
+/*
+ * (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.
+ */
+
+// a little helper to dump UrQMD cross-sections
+
+#include <corsika/process/urqmd/UrQMD.h>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+
+#include <cstdlib>
+#include <fstream>
+#include <iostream>
+
+using namespace corsika;
+using namespace corsika::units::si;
+
+int main() {
+  random::RNGManager::getInstance().registerRandomStream("UrQMD");
+  corsika::UrQMD::UrQMD urqmd;
+
+  std::vector<Code> const projectiles{{Code::Proton, Code::AntiProton, Code::Neutron,
+                                       Code::AntiNeutron, Code::PiPlus, Code::PiMinus,
+                                       Code::KPlus, Code::KMinus, Code::K0Short}};
+
+  for (auto const& p : projectiles) {
+    std::ofstream file(std::string("xs_") + GetName(p) + ".dat");
+    for (auto Elab = GetMass(p) + 200_MeV; Elab <= 10_TeV; Elab *= 1.02) {
+      file << Elab / 1_GeV << '\t'
+           << urqmd.GetTabulatedCrossSection(p, Code::Nitrogen, Elab) / 1_mb << std::endl;
+    }
+  }
+
+  return EXIT_SUCCESS;
+}
diff --git a/Processes/UrQMD/whichres.f b/modules/urqmd/whichres.f
similarity index 100%
rename from Processes/UrQMD/whichres.f
rename to modules/urqmd/whichres.f
diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..dbd3a90a263705a01b40a6c6160b39258604bc67
--- /dev/null
+++ b/src/CMakeLists.txt
@@ -0,0 +1,4 @@
+add_subdirectory (framework/core) 
+add_subdirectory (media)
+add_subdirectory (modules/sibyll) 
+add_subdirectory (modules/qgsjetII) 
diff --git a/src/framework/core/CMakeLists.txt b/src/framework/core/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..68423b1aa03bddcfd9ff4328e8e3b0b7be91987b
--- /dev/null
+++ b/src/framework/core/CMakeLists.txt
@@ -0,0 +1,26 @@
+set (input_dir ${PROJECT_SOURCE_DIR}/src/framework/core)
+set (output_dir ${PROJECT_BINARY_DIR}/corsika/framework/core)
+
+file (MAKE_DIRECTORY ${output_dir})
+
+add_custom_command (
+  OUTPUT  ${output_dir}/GeneratedParticleProperties.inc
+  OUTPUT  ${output_dir}/GeneratedParticleClasses.inc
+          ${output_dir}/particle_db.pkl
+  COMMAND ${input_dir}/pdxml_reader.py ${input_dir}/ParticleData.xml
+                                       ${input_dir}/NuclearData.xml
+                                       ${input_dir}/ParticleClassNames.xml
+  DEPENDS ${input_dir}/pdxml_reader.py
+          ${input_dir}/ParticleData.xml
+          ${input_dir}/NuclearData.xml
+          ${input_dir}/ParticleClassNames.xml
+  WORKING_DIRECTORY
+          ${output_dir}
+  COMMENT "Read PYTHIA8 particle data and produce C++ source code GeneratedParticle[...].inc"
+  VERBATIM
+  )
+
+add_custom_target (GenParticlesHeaders
+  DEPENDS ${output_dir}/GeneratedParticleProperties.inc
+          ${output_dir}/GeneratedParticleClasses.inc)
+add_dependencies (CORSIKA8 GenParticlesHeaders)
diff --git a/Framework/Particles/NuclearData.xml b/src/framework/core/NuclearData.xml
similarity index 100%
rename from Framework/Particles/NuclearData.xml
rename to src/framework/core/NuclearData.xml
diff --git a/Framework/Particles/ParticleClassNames.xml b/src/framework/core/ParticleClassNames.xml
similarity index 100%
rename from Framework/Particles/ParticleClassNames.xml
rename to src/framework/core/ParticleClassNames.xml
diff --git a/Framework/Particles/ParticleData.xml b/src/framework/core/ParticleData.xml
similarity index 100%
rename from Framework/Particles/ParticleData.xml
rename to src/framework/core/ParticleData.xml
diff --git a/Framework/Particles/pdxml_reader.py b/src/framework/core/pdxml_reader.py
similarity index 91%
rename from Framework/Particles/pdxml_reader.py
rename to src/framework/core/pdxml_reader.py
index 15dfd81ed22820b73f38e61d87c6013df0fb2fad..fcd0b134187647d81bf747c3634e4dce2eb76a57 100755
--- a/Framework/Particles/pdxml_reader.py
+++ b/src/framework/core/pdxml_reader.py
@@ -12,6 +12,7 @@ c_speed_of_light = 29.9792458e10  # mm / s
 mneutron = 0.9395654133 # GeV
 mproton = 0.9382720813 # GeV
 
+namespace = "corsika"
 
 ##############################################################
 # 
@@ -337,7 +338,7 @@ def gen_properties(particle_db):
     string += "};\n"
     
     # name string table
-    string += "static const std::array<std::string const, size> names = {\n"
+    string += "static constexpr std::array<std::string_view, size> names = {\n"
     for p in particle_db.values():
         string += "  \"{name:s}\",\n".format(name = p['name'])            
     string += "};\n"
@@ -345,7 +346,7 @@ def gen_properties(particle_db):
     # electric charges table
     string += "static constexpr std::array<int16_t, size> electric_charges = {\n"
     for p in particle_db.values():
-        string += "  {charge:d},\n".format(charge = p['electric_charge'])
+        string += "  {charge:d},\n".format(charge = p['electric_charge'] // 3)
     string += "};\n"
 
     # anti-particle table
@@ -403,6 +404,9 @@ def gen_classes(particle_db):
     string = "// list of C++ classes to access particle properties"
     
     for cname in particle_db:
+        if cname == "Nucleus":
+            string += "// skipping Nucleus"
+            continue
 
         antiP = 'Unknown'
         for cname_anti in particle_db:
@@ -415,7 +419,7 @@ def gen_classes(particle_db):
         string += " * Particle properties are taken from the PYTHIA8 ParticleData.xml file:<br>\n"
         string += " *  - pdg=" + str(particle_db[cname]['pdg']) +"\n"
         string += " *  - mass=" + str(particle_db[cname]['mass']) + " GeV \n"
-        string += " *  - charge= " + str(particle_db[cname]['electric_charge']/3) + " \n"
+        string += " *  - charge= " + str(particle_db[cname]['electric_charge'] // 3) + " \n"
         string += " *  - name=" + str(cname) + "\n"
         string += " *  - anti=" + str(antiP) + "\n"
         if (particle_db[cname]['isNucleus']):
@@ -424,19 +428,19 @@ def gen_classes(particle_db):
         string += "*/\n\n"
         string += "class " + cname + " {\n"
         string += "  public:\n"
-        string += "   static constexpr Code GetCode() { return Type; }\n"
-        string += "   static constexpr corsika::units::si::HEPMassType GetMass() { return corsika::particles::GetMass(Type); }\n"
-        string += "   static constexpr corsika::units::si::ElectricChargeType GetCharge() { return corsika::particles::GetCharge(Type); }\n"
-        string += "   static constexpr int16_t GetChargeNumber() { return corsika::particles::GetChargeNumber(Type); }\n"
-        string += "   static std::string const& GetName() { return corsika::particles::GetName(Type); }\n"
-        string += "   static constexpr Code GetAntiParticle() { return AntiType; }\n"
-        string += "   static constexpr bool IsNucleus() { return corsika::particles::IsNucleus(Type); }\n"
-        string += "   static constexpr int16_t GetNucleusA() { return corsika::particles::GetNucleusA(Type); }\n"
-        string += "   static constexpr int16_t GetNucleusZ() { return corsika::particles::GetNucleusZ(Type); }\n"
-        string += "   static constexpr Code Type = Code::" + cname + ";\n"
-        string += "   static constexpr Code AntiType = Code::" + antiP + ";\n"
+        string += "   " + cname + "() = delete;\n"
+        string += "   static constexpr Code code{Code::" + cname + "};\n"
+        string += "   static constexpr Code anti_code{Code::" + antiP + "};\n"
+        string += "   static constexpr HEPMassType mass{corsika::get_mass(code)};\n"
+        string += "   static constexpr ElectricChargeType charge{corsika::get_charge(code)};\n"
+        string += "   static constexpr int charge_number{corsika::get_charge_number(code)};\n"
+        string += "   static constexpr std::string_view name{corsika::get_name(code)};\n"
+        string += "   static constexpr bool is_nucleus{corsika::is_nucleus(code)};\n"
+        if particle_db[cname]['isNucleus']:
+            string += "   static constexpr int nucleus_A{corsika::get_nucleus_A(code)};\n"
+            string += "   static constexpr int nucleus_Z{corsika::get_nucleus_Z(code)};\n"
         string += " private:\n"
-        string += "   static constexpr CodeIntType TypeIndex = static_cast<CodeIntType const>(Type);\n"
+        string += "   static constexpr CodeIntType TypeIndex = static_cast<CodeIntType>(code);\n"
         string += "};\n"
 
     return string
@@ -448,7 +452,8 @@ def gen_classes(particle_db):
 def inc_start():
     string = ('// generated by pdxml_reader.py\n'
               '// MANUAL EDITS ON OWN RISK. THEY WILL BE OVERWRITTEN. \n'
-              'namespace corsika::particles {\n')
+              '\n'
+              'namespace corsika {\n')
     return string
 
 
@@ -456,7 +461,7 @@ def inc_start():
 # 
 # 
 def detail_start():
-    string = ('namespace detail {\n\n')
+    string = ('namespace particle::detail {\n\n')
     return string
 
 
@@ -464,14 +469,14 @@ def detail_start():
 # 
 # 
 def detail_end():
-    string = "\n} // end namespace detail\n"
+    string = "\n}//end namespace particle::detail\n"
     return string
 
 ###############################################################
 # 
 # 
 def inc_end():
-    string = "\n} // end namespace corsika::particles\n"
+    string = "} // end namespace corsika"
     return string
 
 
@@ -508,8 +513,12 @@ if __name__ == "__main__":
         print(gen_properties(particle_db), file=f)
         print(gen_conversion_PDG_ngc(particle_db), file=f)
         print(detail_end(), file=f) 
+        print(inc_end(), file=f)
+
+    with open("GeneratedParticleClasses.inc", "w") as f:
+        print(inc_start(), file=f)
         print(gen_classes(particle_db), file=f)
-        print(inc_end(), file=f) 
+        print(inc_end(), file=f)
     
     with open("particle_db.pkl", "wb") as f:
         serialize_particle_db(particle_db, f)
diff --git a/Main/shower.cc b/src/main/shower.cc
similarity index 100%
rename from Main/shower.cc
rename to src/main/shower.cc
diff --git a/src/media/CMakeLists.txt b/src/media/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..94b578560d71feacf38c2abec26f37446762e313
--- /dev/null
+++ b/src/media/CMakeLists.txt
@@ -0,0 +1,18 @@
+set (input_dir ${PROJECT_SOURCE_DIR}/src/media)
+set (output_dir ${PROJECT_BINARY_DIR}/corsika/media)
+
+file (MAKE_DIRECTORY ${output_dir})
+
+add_custom_command (       
+  OUTPUT  ${output_dir}/GeneratedMediaProperties.inc         
+  COMMAND ${input_dir}/readProperties.py ${input_dir}/properties8.dat                                       
+  DEPENDS ${input_dir}/readProperties.py
+          ${input_dir}/properties8.dat         
+  WORKING_DIRECTORY
+          ${output_dir}
+  COMMENT "Read NIST properties8 data file and produce C++ source code GeneratedMediaProperties.inc"
+  VERBATIM
+  )
+
+add_custom_target (GenMediaProperties DEPENDS ${output_dir}/GeneratedMediaProperties.inc)
+add_dependencies (CORSIKA8 GenMediaProperties)
diff --git a/Environment/properties8.dat b/src/media/properties8.dat
similarity index 100%
rename from Environment/properties8.dat
rename to src/media/properties8.dat
diff --git a/Environment/readProperties.py b/src/media/readProperties.py
similarity index 93%
rename from Environment/readProperties.py
rename to src/media/readProperties.py
index 552c70d70b64f58ee479562aa8102c357d4181a8..44eb3f839b068d7d26ca457f5d327cabec6b9591 100755
--- a/Environment/readProperties.py
+++ b/src/media/readProperties.py
@@ -288,25 +288,25 @@ def gen_classes(media_db):
     public:
      static constexpr Medium medium() {{ return Medium::{cname}; }}
 
-     static std::string const name() {{ return data_.name(); }}
-     static std::string const pretty_name() {{ return data_.pretty_name(); }}
-     static double weight() {{ return data_.weight (); }}
+     static std::string const getName() {{ return data_.getName(); }}
+     static std::string const getPrettyName() {{ return data_.getPrettyName(); }}
+     static double getWeight() {{ return data_.getWeight (); }}
      static int weight_significant_figure() {{ return data_.weight_significant_figure (); }}
      static int weight_error_last_digit() {{ return data_.weight_error_last_digit(); }}
      static double Z_over_A() {{ return data_.Z_over_A(); }}
-     static double sternheimer_density() {{ return data_.sternheimer_density(); }}
-     static double corrected_density() {{ return data_.corrected_density(); }}
-     static State state() {{ return data_.state(); }}
-     static MediumType type() {{ return data_.type(); }}
-     static std::string const symbol() {{ return data_.symbol(); }}
-
-     static double Ieff() {{ return data_.Ieff(); }}
-     static double Cbar() {{ return data_.Cbar(); }}
-     static double x0() {{ return data_.x0(); }}
-     static double x1() {{ return data_.x1(); }}
-     static double aa() {{ return data_.aa(); }}
-     static double sk() {{ return data_.sk(); }}
-     static double dlt0() {{ return data_.dlt0(); }}
+     static double getSternheimerDensity() {{ return data_.getSternheimerDensity(); }}
+     static double getCorrectedDensity() {{ return data_.getCorrectedDensity(); }}
+     static State getState() {{ return data_.getState(); }}
+     static MediumType getType() {{ return data_.getType(); }}
+     static std::string const getSymbol() {{ return data_.getSymbol(); }}
+
+     static double getIeff() {{ return data_.getIeff(); }}
+     static double getCbar() {{ return data_.getCbar(); }}
+     static double getX0() {{ return data_.getX0(); }}
+     static double getX1() {{ return data_.getX1(); }}
+     static double getAA() {{ return data_.getAA(); }}
+     static double getSK() {{ return data_.getSK(); }}
+     static double getDlt0() {{ return data_.getDlt0(); }}
 
      //static constexpr Constituents constituents() {{ return {constituents}; }}
      //static constexpr Properties properties() {{ return {properties}; }}
@@ -416,7 +416,9 @@ def inc_start():
 // generated by readProperties.py
 // MANUAL EDITS ON OWN RISK. THEY WILL BE OVERWRITTEN. 
 // since this is automatic code, we don't attempt to generate automatic unit testing, too: LCOV_EXCL_START 
-namespace corsika::environment {
+
+#pragma once
+namespace corsika {
 
 """
     return string
@@ -442,7 +444,7 @@ def detail_end():
 # 
 def inc_end():
     string = """
-\n} // end namespace corsika::environment
+\n} // end namespace corsika
 // since this was automatic code, we didn't attempt to generate automatic unit testing, too: LCOV_EXCL_STOP
 """
     return string
diff --git a/src/modules/qgsjetII/CMakeLists.txt b/src/modules/qgsjetII/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..297e2690ddf4119494eb2736aa68293109699c69
--- /dev/null
+++ b/src/modules/qgsjetII/CMakeLists.txt
@@ -0,0 +1,22 @@
+set (input_dir ${PROJECT_SOURCE_DIR}/src/modules/qgsjetII)
+set (output_dir ${PROJECT_BINARY_DIR}/corsika/modules/qgsjetII)
+
+file (MAKE_DIRECTORY ${output_dir})
+
+add_custom_command (
+  OUTPUT  ${output_dir}/Generated.inc
+  COMMAND ${input_dir}/code_generator.py 
+          ${PROJECT_BINARY_DIR}/corsika/framework/core/particle_db.pkl
+          ${input_dir}/qgsjet-II-04-codes.dat
+  DEPENDS ${input_dir}/code_generator.py
+          ${input_dir}/qgsjet-II-04-codes.dat
+          GenParticlesHeaders # for particle_db.pkl
+  WORKING_DIRECTORY
+          ${output_dir}
+  COMMENT "Generate conversion tables for particle codes QGSJetII <-> CORSIKA"
+  VERBATIM
+  )
+
+add_custom_target (SourceDirLinkQgs DEPENDS ${output_dir}/Generated.inc)
+add_dependencies (CORSIKA8 SourceDirLinkQgs)
+
diff --git a/Processes/QGSJetII/code_generator.py b/src/modules/qgsjetII/code_generator.py
similarity index 93%
rename from Processes/QGSJetII/code_generator.py
rename to src/modules/qgsjetII/code_generator.py
index 6fd456083b4b669c046c93b2e4dfe57eebed0e49..ce7fcae263f6e2b9a64b08c5ef56fc85aa99e9b7 100755
--- a/Processes/QGSJetII/code_generator.py
+++ b/src/modules/qgsjetII/code_generator.py
@@ -2,8 +2,6 @@
 
 # (c) Copyright 2020 CORSIKA Project, corsika-project@lists.kit.edu
 #
-# See file AUTHORS for a list of contributors.
-#
 # 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.
@@ -172,16 +170,27 @@ def generate_qgsjetII2corsika(particle_db) :
             pDict[model_code] = identifier
     
     nPart = max(pDict.keys()) - min(pDict.keys()) + 1
-    string += "std::array<corsika::particles::Code, {:d}> constexpr qgsjetII2corsika = {{\n".format(nPart)
+    string += "std::array<corsika::Code, {:d}> constexpr qgsjetII2corsika = {{\n".format(nPart)
     
     for iPart in range(nPart) :
         identifier = pDict.get(iPart, "Unknown")
         qgsID = iPart + minID
-        string += "  corsika::particles::Code::{:s}, // {:d} \n".format(identifier, qgsID)
+        string += "  corsika::Code::{:s}, // {:d} \n".format(identifier, qgsID)
     
     string += "};\n"
     return string
 
+def generate_qgsjetII_start():
+    string = "// This file is auto-generated. Do not edit!\n"
+    string += "#pragma once\n"
+    string += "namespace corsika::qgsjetII {\n"
+    return string
+
+def generate_qgsjetII_end():
+    string = "}\n"
+    return string
+
+
 
 if __name__ == "__main__":
     if len(sys.argv) != 3:
@@ -196,8 +205,10 @@ if __name__ == "__main__":
     
     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)
         print(generate_qgsjetII_enum(particle_db), file=f)
         print(generate_corsika2qgsjetII(particle_db), file=f)
         print(generate_qgsjetII2corsika(particle_db), file=f)
         print(generate_corsika2qgsjetII_xsType(particle_db), file=f)
         print(generate_corsika2qgsjetII_hadronType(particle_db), file=f)
+        print(generate_qgsjetII_end(), file=f)
diff --git a/Processes/QGSJetII/qgsjet-II-04-codes.dat b/src/modules/qgsjetII/qgsjet-II-04-codes.dat
similarity index 100%
rename from Processes/QGSJetII/qgsjet-II-04-codes.dat
rename to src/modules/qgsjetII/qgsjet-II-04-codes.dat
diff --git a/src/modules/sibyll/CMakeLists.txt b/src/modules/sibyll/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c2e6dd0bfc4840f59f4ce33a981186bdcff3b248
--- /dev/null
+++ b/src/modules/sibyll/CMakeLists.txt
@@ -0,0 +1,22 @@
+set (input_dir ${PROJECT_SOURCE_DIR}/src/modules/sibyll)
+set (output_dir ${PROJECT_BINARY_DIR}/corsika/modules/sibyll)
+
+file (MAKE_DIRECTORY ${output_dir})
+
+add_custom_command (
+  OUTPUT  ${output_dir}/Generated.inc
+  COMMAND ${input_dir}/code_generator.py 
+          ${PROJECT_BINARY_DIR}/corsika/framework/core/particle_db.pkl
+          ${input_dir}/sibyll_codes.dat
+  DEPENDS ${input_dir}/code_generator.py
+          ${input_dir}/sibyll_codes.dat
+          GenParticlesHeaders # for particle_db.pkl
+  WORKING_DIRECTORY
+          ${output_dir}/
+  COMMENT "Generate conversion tables for particle codes SIBYLL <-> CORSIKA"
+  VERBATIM
+  )
+
+add_custom_target (SourceDirLinkSib DEPENDS ${output_dir}/Generated.inc)
+add_dependencies (CORSIKA8 SourceDirLinkSib)
+
diff --git a/Processes/Sibyll/code_generator.py b/src/modules/sibyll/code_generator.py
similarity index 96%
rename from Processes/Sibyll/code_generator.py
rename to src/modules/sibyll/code_generator.py
index 7044c95e01df1c5507f8b1dd45b89d4634189ffa..22cb11022741597b6807c844766dcf0158f24365 100755
--- a/Processes/Sibyll/code_generator.py
+++ b/src/modules/sibyll/code_generator.py
@@ -106,14 +106,14 @@ def generate_sibyll2corsika(particle_db) :
             pDict[sib_code] = identifier
     
     nPart = max(pDict.keys()) - min(pDict.keys()) + 1
-    string += "std::array<corsika::particles::Code, {:d}> constexpr sibyll2corsika = {{\n".format(nPart)
+    string += "std::array<corsika::Code, {:d}> constexpr sibyll2corsika = {{\n".format(nPart)
     
     for iPart in range(nPart) :
         if iPart in pDict:
             identifier = pDict[iPart]
         else:
             identifier = "Unknown"
-        string += "  corsika::particles::Code::{:s}, \n".format(identifier)
+        string += "  corsika::Code::{:s}, \n".format(identifier)
     
     string += "};\n"
     return string
diff --git a/Processes/Sibyll/sibyll_codes.dat b/src/modules/sibyll/sibyll_codes.dat
similarity index 100%
rename from Processes/Sibyll/sibyll_codes.dat
rename to src/modules/sibyll/sibyll_codes.dat
diff --git a/tests/CMakeLists.txt b/tests/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..3a12d7cc7401177052d3374be42c583073b3ae20
--- /dev/null
+++ b/tests/CMakeLists.txt
@@ -0,0 +1,5 @@
+add_subdirectory (common)
+add_subdirectory (framework)
+add_subdirectory (media)
+add_subdirectory (stack)
+add_subdirectory (modules)
diff --git a/tests/common/CMakeLists.txt b/tests/common/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..eb3d1be55ccc2031dc28bdfbe76038d992360538
--- /dev/null
+++ b/tests/common/CMakeLists.txt
@@ -0,0 +1,2 @@
+add_library (CorsikaTestingCommon INTERFACE)
+target_include_directories (CorsikaTestingCommon INTERFACE ${CMAKE_SOURCE_DIR}/tests/common/) 
diff --git a/tests/common/PhysicalUnitsCatch2.hpp b/tests/common/PhysicalUnitsCatch2.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3fdc58bfeb8ddb4845bbf3b760aacb9e2fef6422
--- /dev/null
+++ b/tests/common/PhysicalUnitsCatch2.hpp
@@ -0,0 +1,29 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <catch2/catch.hpp>
+
+namespace corsika::testing {
+
+  /**
+   * comparion of DimensionlessType quantities with the Catch2 Approx
+   * class.
+
+   * This allows code/checks of this type
+   * `CHECK(v.normalize().norm() == Approx(1),margin(0)) `
+   *
+   **/
+  inline bool operator==(DimensionlessType const a, Catch::Detail::Approx const& b) {
+    return a.magnitude() == b;
+  }
+
+} // namespace corsika::testing
diff --git a/tests/common/SetupTestEnvironment.hpp b/tests/common/SetupTestEnvironment.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..fa48ec023fc37358198582f7f15139604d4cfa36
--- /dev/null
+++ b/tests/common/SetupTestEnvironment.hpp
@@ -0,0 +1,60 @@
+/*
+ * (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 <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/CoordinateSystem.hpp>
+
+#include <corsika/setup/SetupEnvironment.hpp>
+#include <corsika/media/UniformMagneticField.hpp>
+#include <corsika/media/MediumPropertyModel.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+
+#include <limits>
+
+namespace corsika::setup::testing {
+
+  /**
+   * \function setup_environment
+   *
+   * standard environment for unit testing.
+   *
+   */
+
+  inline std::tuple<std::unique_ptr<setup::Environment>, CoordinateSystemPtr const*,
+                    setup::Environment::BaseNodeType*>
+  setup_environment(Code const vTargetCode,
+                    MagneticFluxType const& BfieldZ = MagneticFluxType::zero()) {
+
+    auto env = std::make_unique<setup::Environment>();
+    auto& universe = *(env->getUniverse());
+    CoordinateSystemPtr const& cs = env->getCoordinateSystem();
+
+    /**
+     * our world is a sphere at 0,0,0 with R=infty
+     */
+    auto world = setup::Environment::createNode<Sphere>(Point{cs, 0_m, 0_m, 0_m}, 100_km);
+
+    /**
+     * construct suited environment medium model:
+     */
+    using MyHomogeneousModel = MediumPropertyModel<
+        UniformMagneticField<HomogeneousMedium<setup::EnvironmentInterface>>>;
+
+    world->setModelProperties<MyHomogeneousModel>(
+        Medium::AirDry1Atm, Vector(cs, 0_T, 0_T, BfieldZ), 1_kg / (1_m * 1_m * 1_m),
+        NuclearComposition(std::vector<Code>{vTargetCode}, std::vector<float>{1.}));
+
+    setup::Environment::BaseNodeType* nodePtr = world.get();
+    universe.addChild(std::move(world));
+
+    return std::make_tuple(std::move(env), &cs, nodePtr);
+  }
+
+} // namespace corsika::setup::testing
diff --git a/tests/common/SetupTestStack.hpp b/tests/common/SetupTestStack.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..3d5322d4a79f5a4be0d397348daff9bbf5530c87
--- /dev/null
+++ b/tests/common/SetupTestStack.hpp
@@ -0,0 +1,66 @@
+/*
+ * (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 <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+
+/**
+ * \file SetupTestStack
+ *
+ * standard stack setup for unit tests.
+ **/
+
+namespace corsika::setup::testing {
+
+  /**
+   * \function setup_stack
+   *
+   * standard stack setup for unit tests.
+   *
+   *
+   *
+   *
+   * \return a tuple with element 0 being a Stack object filled with
+   * one particle, and element 1 the StackView on it.
+   **/
+
+  inline std::tuple<std::unique_ptr<setup::Stack>, std::unique_ptr<setup::StackView>>
+  setup_stack(Code vProjectileType, int vA, int vZ, HEPEnergyType vMomentum,
+              setup::Environment::BaseNodeType* const vNodePtr,
+              CoordinateSystemPtr const& cs) {
+
+    auto stack = std::make_unique<setup::Stack>();
+
+    Point const origin(cs, {0_m, 0_m, 0_m});
+    MomentumVector const pLab(cs, {vMomentum, 0_GeV, 0_GeV});
+
+    if (vProjectileType == Code::Nucleus) {
+      auto constexpr mN = constants::nucleonMass;
+      HEPEnergyType const E0 = sqrt(static_pow<2>(mN * vA) + pLab.getSquaredNorm());
+      auto particle = stack->addParticle(
+          std::make_tuple(Code::Nucleus, E0, pLab, origin, 0_ns, vA, vZ));
+      particle.setNode(vNodePtr);
+      return std::make_tuple(std::move(stack),
+                             std::make_unique<setup::StackView>(particle));
+    } else { // not a nucleus
+      HEPEnergyType const E0 =
+          sqrt(static_pow<2>(get_mass(vProjectileType)) + pLab.getSquaredNorm());
+      auto particle =
+          stack->addParticle(std::make_tuple(vProjectileType, E0, pLab, origin, 0_ns));
+      particle.setNode(vNodePtr);
+      return std::make_tuple(std::move(stack),
+                             std::make_unique<setup::StackView>(particle));
+    }
+  }
+
+} // namespace corsika::setup::testing
diff --git a/tests/common/SetupTestTrajectory.hpp b/tests/common/SetupTestTrajectory.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0c06d7f39aea76fd2b466ca1974d95a616bcfefb
--- /dev/null
+++ b/tests/common/SetupTestTrajectory.hpp
@@ -0,0 +1,45 @@
+/*
+ * (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 <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#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
+// implementation
+
+namespace corsika::setup::testing {
+
+  template <typename TTrack>
+  TTrack make_track(Line const& line, TimeType const tEnd);
+
+  template <>
+  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);
+    }
+  */
+} // namespace corsika::setup::testing
diff --git a/Framework/StackInterface/testTestStack.h b/tests/common/testTestStack.hpp
similarity index 50%
rename from Framework/StackInterface/testTestStack.h
rename to tests/common/testTestStack.hpp
index 9765dda8b5cf449844ff0e44a6b922007d8f0cb2..70f4d5f147dd7f8d2499f89a1b4809057f77edaa 100644
--- a/Framework/StackInterface/testTestStack.h
+++ b/tests/common/testTestStack.hpp
@@ -8,7 +8,7 @@
 
 #pragma once
 
-#include <corsika/stack/Stack.h>
+#include <corsika/framework/stack/Stack.hpp>
 #include <tuple>
 #include <vector>
 
@@ -16,35 +16,35 @@
  * definition of stack-data object for unit tests
  *
  * TestStackData contain only a single variable "Data" stored in fData
- * with Get/SetData functions.
+ * with get/setData functions.
  */
 class TestStackData {
 
 public:
   // these functions are needed for the Stack interface
-  void Clear() { fData.clear(); }
-  unsigned int GetSize() const { return fData.size(); }
-  unsigned int GetCapacity() const { return fData.size(); }
-  void Copy(const unsigned int i1, const unsigned int i2) { fData[i2] = fData[i1]; }
-  void Swap(const unsigned int i1, const unsigned int i2) {
-    double tmp0 = fData[i1];
-    fData[i1] = fData[i2];
-    fData[i2] = tmp0;
+  void clear() { data_.clear(); }
+  unsigned int getSize() const { return data_.size(); }
+  unsigned int getCapacity() const { return data_.size(); }
+  void copy(const unsigned int i1, const unsigned int i2) { data_[i2] = data_[i1]; }
+  void swap(const unsigned int i1, const unsigned int i2) {
+    double tmp0 = data_[i1];
+    data_[i1] = data_[i2];
+    data_[i2] = tmp0;
   }
 
   // custom data access function
-  void SetData(const unsigned int i, const double v) { fData[i] = v; }
-  double GetData(const unsigned int i) const { return fData[i]; }
+  void setData(const unsigned int i, const double v) { data_[i] = v; }
+  double getData(const unsigned int i) const { return data_[i]; }
 
   // these functions are also needed by the Stack interface
-  void IncrementSize() { fData.push_back(0.); }
-  void DecrementSize() {
-    if (fData.size() > 0) { fData.pop_back(); }
+  void incrementSize() { data_.push_back(0.); }
+  void decrementSize() {
+    if (data_.size() > 0) { data_.pop_back(); }
   }
 
   // custom private data section
 private:
-  std::vector<double> fData;
+  std::vector<double> data_;
 };
 
 /**
@@ -54,18 +54,16 @@ private:
  *
  * It provides Get/Set methods to read and write data to the "Data"
  * storage of TestStackData obtained via
- * "StackIteratorInterface::GetStackData()", given the index of the
- * iterator "StackIteratorInterface::GetIndex()"
+ * "StackIteratorInterface::getStackData()", given the index of the
+ * iterator "StackIteratorInterface::getIndex()"
  *
  */
 template <typename StackIteratorInterface>
-class TestParticleInterface
-    : public corsika::stack::ParticleBase<StackIteratorInterface> {
+class TestParticleInterface : public corsika::ParticleBase<StackIteratorInterface> {
 
-public:
-  using corsika::stack::ParticleBase<StackIteratorInterface>::GetStackData;
-  using corsika::stack::ParticleBase<StackIteratorInterface>::GetIndex;
+  typedef corsika::ParticleBase<StackIteratorInterface> super_type;
 
+public:
   /*
      The SetParticleData methods are called for creating new entries
      on the stack. You can specifiy various parametric versions to
@@ -73,13 +71,17 @@ public:
   */
 
   // default version for particle-creation from input data
-  void SetParticleData(const std::tuple<double> v) { SetData(std::get<0>(v)); }
-  void SetParticleData(TestParticleInterface<StackIteratorInterface>& /*parent*/,
+  void setParticleData(const std::tuple<double> v) { setData(std::get<0>(v)); }
+  void setParticleData(TestParticleInterface<StackIteratorInterface>& /*parent*/,
                        std::tuple<double> v) {
-    SetData(std::get<0>(v));
+    setData(std::get<0>(v));
   }
 
   // here are the fundamental methods for access to TestStackData data
-  void SetData(const double v) { GetStackData().SetData(GetIndex(), v); }
-  double GetData() const { return GetStackData().GetData(GetIndex()); }
+  void setData(const double v) {
+    super_type::getStackData().setData(super_type::getIndex(), v);
+  }
+  double getData() const {
+    return super_type::getStackData().getData(super_type::getIndex());
+  }
 };
diff --git a/tests/framework/CMakeCache.txt b/tests/framework/CMakeCache.txt
new file mode 100644
index 0000000000000000000000000000000000000000..792df53c17d42a1b2a9f008c4c4450be089dbfec
--- /dev/null
+++ b/tests/framework/CMakeCache.txt
@@ -0,0 +1,342 @@
+# This is the CMakeCache file.
+# For build in directory: /home/rulrich/work/corsika/corsika/tests/framework
+# It was generated by CMake: /usr/bin/cmake
+# You can edit this file to change values found and used by cmake.
+# If you do not want to change any of the values, simply exit the editor.
+# If you do want to change a value, simply edit, save, and exit the editor.
+# The syntax for the file is as follows:
+# KEY:TYPE=VALUE
+# KEY is the name of a variable in the cache.
+# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!.
+# VALUE is the current value for the KEY.
+
+########################
+# EXTERNAL cache entries
+########################
+
+//Path to a program.
+CMAKE_AR:FILEPATH=/usr/bin/ar
+
+//For backwards compatibility, what version of CMake commands and
+// syntax should this version of CMake try to support.
+CMAKE_BACKWARDS_COMPATIBILITY:STRING=2.4
+
+//Choose the type of build, options are: None(CMAKE_CXX_FLAGS or
+// CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel.
+CMAKE_BUILD_TYPE:STRING=
+
+//Enable/Disable color output during build.
+CMAKE_COLOR_MAKEFILE:BOOL=ON
+
+//CXX compiler
+CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/c++
+
+//A wrapper around 'ar' adding the appropriate '--plugin' option
+// for the GCC compiler
+CMAKE_CXX_COMPILER_AR:FILEPATH=/usr/bin/gcc-ar-7
+
+//A wrapper around 'ranlib' adding the appropriate '--plugin' option
+// for the GCC compiler
+CMAKE_CXX_COMPILER_RANLIB:FILEPATH=/usr/bin/gcc-ranlib-7
+
+//Flags used by the compiler during all build types.
+CMAKE_CXX_FLAGS:STRING=
+
+//Flags used by the compiler during debug builds.
+CMAKE_CXX_FLAGS_DEBUG:STRING=-g
+
+//Flags used by the compiler during release builds for minimum
+// size.
+CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
+
+//Flags used by the compiler during release builds.
+CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
+
+//Flags used by the compiler during release builds with debug info.
+CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
+
+//C compiler
+CMAKE_C_COMPILER:FILEPATH=/usr/bin/cc
+
+//A wrapper around 'ar' adding the appropriate '--plugin' option
+// for the GCC compiler
+CMAKE_C_COMPILER_AR:FILEPATH=/usr/bin/gcc-ar-7
+
+//A wrapper around 'ranlib' adding the appropriate '--plugin' option
+// for the GCC compiler
+CMAKE_C_COMPILER_RANLIB:FILEPATH=/usr/bin/gcc-ranlib-7
+
+//Flags used by the compiler during all build types.
+CMAKE_C_FLAGS:STRING=
+
+//Flags used by the compiler during debug builds.
+CMAKE_C_FLAGS_DEBUG:STRING=-g
+
+//Flags used by the compiler during release builds for minimum
+// size.
+CMAKE_C_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG
+
+//Flags used by the compiler during release builds.
+CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG
+
+//Flags used by the compiler during release builds with debug info.
+CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG
+
+//Flags used by the linker.
+CMAKE_EXE_LINKER_FLAGS:STRING=
+
+//Flags used by the linker during debug builds.
+CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING=
+
+//Flags used by the linker during release minsize builds.
+CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING=
+
+//Flags used by the linker during release builds.
+CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING=
+
+//Flags used by the linker during Release with Debug Info builds.
+CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
+
+//Enable/Disable output of compile commands during generation.
+CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=OFF
+
+//Install path prefix, prepended onto install directories.
+CMAKE_INSTALL_PREFIX:PATH=/usr/local
+
+//Path to a program.
+CMAKE_LINKER:FILEPATH=/usr/bin/ld
+
+//Path to a program.
+CMAKE_MAKE_PROGRAM:FILEPATH=/usr/bin/make
+
+//Flags used by the linker during the creation of modules.
+CMAKE_MODULE_LINKER_FLAGS:STRING=
+
+//Flags used by the linker during debug builds.
+CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING=
+
+//Flags used by the linker during release minsize builds.
+CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING=
+
+//Flags used by the linker during release builds.
+CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING=
+
+//Flags used by the linker during Release with Debug Info builds.
+CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING=
+
+//Path to a program.
+CMAKE_NM:FILEPATH=/usr/bin/nm
+
+//Path to a program.
+CMAKE_OBJCOPY:FILEPATH=/usr/bin/objcopy
+
+//Path to a program.
+CMAKE_OBJDUMP:FILEPATH=/usr/bin/objdump
+
+//Value Computed by CMake
+CMAKE_PROJECT_NAME:STATIC=Project
+
+//Path to a program.
+CMAKE_RANLIB:FILEPATH=/usr/bin/ranlib
+
+//Flags used by the linker during the creation of dll's.
+CMAKE_SHARED_LINKER_FLAGS:STRING=
+
+//Flags used by the linker during debug builds.
+CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING=
+
+//Flags used by the linker during release minsize builds.
+CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING=
+
+//Flags used by the linker during release builds.
+CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING=
+
+//Flags used by the linker during Release with Debug Info builds.
+CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING=
+
+//If set, runtime paths are not added when installing shared libraries,
+// but are added when building.
+CMAKE_SKIP_INSTALL_RPATH:BOOL=NO
+
+//If set, runtime paths are not added when using shared libraries.
+CMAKE_SKIP_RPATH:BOOL=NO
+
+//Flags used by the linker during the creation of static libraries.
+CMAKE_STATIC_LINKER_FLAGS:STRING=
+
+//Flags used by the linker during debug builds.
+CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING=
+
+//Flags used by the linker during release minsize builds.
+CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING=
+
+//Flags used by the linker during release builds.
+CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING=
+
+//Flags used by the linker during Release with Debug Info builds.
+CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING=
+
+//Path to a program.
+CMAKE_STRIP:FILEPATH=/usr/bin/strip
+
+//If this value is on, makefiles will be generated without the
+// .SILENT directive, and all commands will be echoed to the console
+// during the make.  This is useful for debugging only. With Visual
+// Studio IDE projects all commands are done without /nologo.
+CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE
+
+//Single output directory for building all executables.
+EXECUTABLE_OUTPUT_PATH:PATH=
+
+//Single output directory for building all libraries.
+LIBRARY_OUTPUT_PATH:PATH=
+
+//Value Computed by CMake
+Project_BINARY_DIR:STATIC=/home/rulrich/work/corsika/corsika/tests/framework
+
+//Value Computed by CMake
+Project_SOURCE_DIR:STATIC=/home/rulrich/work/corsika/corsika/tests
+
+
+########################
+# INTERNAL cache entries
+########################
+
+//ADVANCED property for variable: CMAKE_AR
+CMAKE_AR-ADVANCED:INTERNAL=1
+//This is the directory where this CMakeCache.txt was created
+CMAKE_CACHEFILE_DIR:INTERNAL=/home/rulrich/work/corsika/corsika/tests/framework
+//Major version of cmake used to create the current loaded cache
+CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3
+//Minor version of cmake used to create the current loaded cache
+CMAKE_CACHE_MINOR_VERSION:INTERNAL=10
+//Patch version of cmake used to create the current loaded cache
+CMAKE_CACHE_PATCH_VERSION:INTERNAL=2
+//ADVANCED property for variable: CMAKE_COLOR_MAKEFILE
+CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1
+//Path to CMake executable.
+CMAKE_COMMAND:INTERNAL=/usr/bin/cmake
+//Path to cpack program executable.
+CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack
+//Path to ctest program executable.
+CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest
+//ADVANCED property for variable: CMAKE_CXX_COMPILER
+CMAKE_CXX_COMPILER-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_CXX_COMPILER_AR
+CMAKE_CXX_COMPILER_AR-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_CXX_COMPILER_RANLIB
+CMAKE_CXX_COMPILER_RANLIB-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_CXX_FLAGS
+CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG
+CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL
+CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE
+CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO
+CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_COMPILER
+CMAKE_C_COMPILER-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_COMPILER_AR
+CMAKE_C_COMPILER_AR-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_COMPILER_RANLIB
+CMAKE_C_COMPILER_RANLIB-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_FLAGS
+CMAKE_C_FLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_FLAGS_DEBUG
+CMAKE_C_FLAGS_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_FLAGS_MINSIZEREL
+CMAKE_C_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_FLAGS_RELEASE
+CMAKE_C_FLAGS_RELEASE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_C_FLAGS_RELWITHDEBINFO
+CMAKE_C_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
+//Path to cache edit program executable.
+CMAKE_EDIT_COMMAND:INTERNAL=/usr/bin/ccmake
+//Executable file format
+CMAKE_EXECUTABLE_FORMAT:INTERNAL=ELF
+//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS
+CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG
+CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL
+CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE
+CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO
+CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS
+CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1
+//Name of external makefile project generator.
+CMAKE_EXTRA_GENERATOR:INTERNAL=
+//Name of generator.
+CMAKE_GENERATOR:INTERNAL=Unix Makefiles
+//Name of generator platform.
+CMAKE_GENERATOR_PLATFORM:INTERNAL=
+//Name of generator toolset.
+CMAKE_GENERATOR_TOOLSET:INTERNAL=
+//Source directory with the top level CMakeLists.txt file for this
+// project
+CMAKE_HOME_DIRECTORY:INTERNAL=/home/rulrich/work/corsika/corsika/tests
+//Install .so files without execute permission.
+CMAKE_INSTALL_SO_NO_EXE:INTERNAL=1
+//ADVANCED property for variable: CMAKE_LINKER
+CMAKE_LINKER-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_MAKE_PROGRAM
+CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS
+CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG
+CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL
+CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE
+CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO
+CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_NM
+CMAKE_NM-ADVANCED:INTERNAL=1
+//number of local generators
+CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=3
+//ADVANCED property for variable: CMAKE_OBJCOPY
+CMAKE_OBJCOPY-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_OBJDUMP
+CMAKE_OBJDUMP-ADVANCED:INTERNAL=1
+//Platform information initialized
+CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_RANLIB
+CMAKE_RANLIB-ADVANCED:INTERNAL=1
+//Path to CMake installation.
+CMAKE_ROOT:INTERNAL=/usr/share/cmake-3.10
+//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS
+CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG
+CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL
+CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE
+CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO
+CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH
+CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_SKIP_RPATH
+CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS
+CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG
+CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL
+CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE
+CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO
+CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1
+//ADVANCED property for variable: CMAKE_STRIP
+CMAKE_STRIP-ADVANCED:INTERNAL=1
+//uname command
+CMAKE_UNAME:INTERNAL=/bin/uname
+//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE
+CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1
+
diff --git a/tests/framework/CMakeLists.txt b/tests/framework/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..35f61737b13dfedef24d6746cf92e56a26658ebe
--- /dev/null
+++ b/tests/framework/CMakeLists.txt
@@ -0,0 +1,31 @@
+
+set (test_framework_sources  
+  TestMain.cpp
+  testFourVector.cpp
+  testSaveBoostHistogram.cpp
+  testClassTimer.cpp
+  testLogging.cpp
+  testParticles.cpp
+  testStackInterface.cpp
+  testProcessSequence.cpp
+  testCOMBoost.cpp
+  # testCorsikaFenv.cpp # does not work because of use of exceptions in catch2
+  testFunctionTimer.cpp
+  testSecondaryView.cpp
+  testGeometry.cpp
+  testCombinedStack.cpp
+  testUnits.cpp
+  testCascade.cpp 
+  testRandom.cpp
+  testNullModel.cpp  
+  testHelix.cpp
+  testInteractionCounter.cpp
+  )
+
+CORSIKA_ADD_TEST (testFramework SOURCES ${test_framework_sources})
+
+target_compile_definitions (
+  testFramework
+  PRIVATE
+  REFDATADIR="${CMAKE_CURRENT_SOURCE_DIR}"
+)
diff --git a/Framework/Testing/TestMain.cc b/tests/framework/TestMain.cpp
similarity index 84%
rename from Framework/Testing/TestMain.cc
rename to tests/framework/TestMain.cpp
index fa52fa804f307c546f628b7ccd5d14401b6f0adb..51532584b8e03b35d79301543ac8f80b598ba544 100644
--- a/Framework/Testing/TestMain.cc
+++ b/tests/framework/TestMain.cpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2019 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
diff --git a/tests/framework/testCOMBoost.cpp b/tests/framework/testCOMBoost.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..caf6f08965bf4468294df23dee13e4f265dd4c47
--- /dev/null
+++ b/tests/framework/testCOMBoost.cpp
@@ -0,0 +1,358 @@
+/*
+ * (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.
+ */
+
+#include <catch2/catch.hpp>
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/FourVector.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+#include <corsika/framework/utility/COMBoost.hpp>
+
+using namespace corsika;
+
+double constexpr absMargin = 1e-6;
+
+CoordinateSystemPtr rootCS = get_root_CoordinateSystem();
+
+/**
+ * \todo such helper functions should be moved to the FourVector class:
+ **/
+// helper function for energy-momentum
+// relativistic energy
+auto const energy = [](HEPMassType m, MomentumVector const& p) {
+  return sqrt(m * m + p.getSquaredNorm());
+};
+
+auto const momentum = [](HEPEnergyType E, HEPMassType m) { return sqrt(E * E - m * m); };
+
+// helper function for mandelstam-s
+auto const s = [](HEPEnergyType E, QuantityVector<hepmomentum_d> const& p) {
+  return E * E - p.getSquaredNorm();
+};
+
+TEST_CASE("rotation") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  // define projectile kinematics in lab frame
+  HEPMassType const projectileMass = 1_GeV;
+  HEPMassType const targetMass = 1.0e300_eV;
+  MomentumVector pProjectileLab{rootCS, {0_GeV, 0_PeV, 1_GeV}};
+  HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab);
+  FourVector const PprojLab(eProjectileLab, pProjectileLab);
+
+  MomentumVector e1(rootCS, {1_GeV, 0_GeV, 0_GeV});
+  MomentumVector e2(rootCS, {0_GeV, 1_GeV, 0_GeV});
+  MomentumVector e3(rootCS, {0_GeV, 0_GeV, 1_GeV});
+
+  // define boost to com frame
+  SECTION("pos. z-axis") {
+    COMBoost boost({eProjectileLab, {rootCS, {0_GeV, 0_GeV, 1_GeV}}}, targetMass);
+    CoordinateSystemPtr rotCS = boost.getRotatedCS();
+
+    e1.rebase(rotCS);
+    e2.rebase(rotCS);
+    e3.rebase(rotCS);
+
+    // length of e1, e2 and e3 must all be 1_GeV in rotated CS (not boosted!)
+    CHECK(e1.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+    CHECK(e2.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+    CHECK(e3.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+
+    // z-axis is along z-boost
+    CHECK(e3.getX(rotCS) / 1_GeV == Approx(0).margin(absMargin));
+    CHECK(e3.getY(rotCS) / 1_GeV == Approx(0).margin(absMargin));
+    CHECK(e3.getZ(rotCS) / 1_GeV == Approx(1).margin(absMargin));
+  }
+
+  SECTION("y-axis in upper half") {
+    COMBoost boost({eProjectileLab, {rootCS, {0_GeV, 1_GeV, 1_meV}}}, targetMass);
+    CoordinateSystemPtr rotCS = boost.getRotatedCS();
+
+    e1.rebase(rotCS);
+    e2.rebase(rotCS);
+    e3.rebase(rotCS);
+
+    // length of e1, e2 and e3 must all be 1_GeV in rotated CS (not boosted!)
+    CHECK(e1.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+    CHECK(e2.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+    CHECK(e3.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+
+    // z-axis is along y-boost
+    CHECK(e2.getX(rotCS) / 1_GeV == Approx(0).margin(absMargin));
+    CHECK(e2.getY(rotCS) / 1_GeV == Approx(0).margin(absMargin));
+    CHECK(e2.getZ(rotCS) / 1_GeV == Approx(1).margin(absMargin));
+  }
+
+  SECTION("x-axis in upper half") {
+    COMBoost boost({eProjectileLab, {rootCS, {1_GeV, 0_GeV, 1_meV}}}, targetMass);
+    CoordinateSystemPtr rotCS = boost.getRotatedCS();
+
+    e1.rebase(rotCS);
+    e2.rebase(rotCS);
+    e3.rebase(rotCS);
+
+    // length of e1, e2 and e3 must all be 1_GeV in rotated CS (not boosted!)
+    CHECK(e1.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+    CHECK(e2.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+    CHECK(e3.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+
+    // z-axis is along x-boost
+    CHECK(e1.getX(rotCS) / 1_GeV == Approx(0).margin(absMargin));
+    CHECK(e1.getY(rotCS) / 1_GeV == Approx(0).margin(absMargin));
+    CHECK(e1.getZ(rotCS) / 1_GeV == Approx(1).margin(absMargin));
+  }
+
+  SECTION("neg. z-axis") {
+    COMBoost boost({eProjectileLab, {rootCS, {0_GeV, 0_GeV, -1_GeV}}}, targetMass);
+    CoordinateSystemPtr rotCS = boost.getRotatedCS();
+
+    e1.rebase(rotCS);
+    e2.rebase(rotCS);
+    e3.rebase(rotCS);
+
+    // length of e1, e2 and e3 must all be 1_GeV in rotated CS (not boosted!)
+    CHECK(e1.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+    CHECK(e2.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+    CHECK(e3.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+
+    // z-axis is along -z-boost
+    CHECK(-e3.getX(rotCS) / 1_GeV == Approx(0).margin(absMargin));
+    CHECK(-e3.getY(rotCS) / 1_GeV == Approx(0).margin(absMargin));
+    CHECK(-e3.getZ(rotCS) / 1_GeV == Approx(1).margin(absMargin));
+  }
+
+  SECTION("x-axis lower half") {
+    COMBoost boost({eProjectileLab, {rootCS, {1_GeV, 0_GeV, -1_meV}}}, targetMass);
+    CoordinateSystemPtr rotCS = boost.getRotatedCS();
+
+    e1.rebase(rotCS);
+    e2.rebase(rotCS);
+    e3.rebase(rotCS);
+
+    // length of e1, e2 and e3 must all be 1_GeV in rotated CS (not boosted!)
+    CHECK(e1.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+    CHECK(e2.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+    CHECK(e3.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+
+    // z-axis is along x-boost
+    CHECK(e1.getX(rotCS) / 1_GeV == Approx(0).margin(absMargin));
+    CHECK(e1.getY(rotCS) / 1_GeV == Approx(0).margin(absMargin));
+    CHECK(e1.getZ(rotCS) / 1_GeV == Approx(1).margin(absMargin));
+  }
+
+  SECTION("y-axis lower half") {
+    COMBoost boost({eProjectileLab, {rootCS, {0_GeV, 1_GeV, -1_meV}}}, targetMass);
+    CoordinateSystemPtr rotCS = boost.getRotatedCS();
+
+    e1.rebase(rotCS);
+    e2.rebase(rotCS);
+    e3.rebase(rotCS);
+
+    // length of e1, e2 and e3 must all be 1_GeV in rotated CS (not boosted!)
+    CHECK(e1.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+    CHECK(e2.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+    CHECK(e3.getNorm() / 1_GeV == Approx(1).margin(absMargin));
+
+    // z-axis is along y-boost
+    CHECK(e2.getX(rotCS) / 1_GeV == Approx(0).margin(absMargin));
+    CHECK(e2.getY(rotCS) / 1_GeV == Approx(0).margin(absMargin));
+    CHECK(e2.getZ(rotCS) / 1_GeV == Approx(1).margin(absMargin));
+  }
+}
+
+TEST_CASE("boosts") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  // define target kinematics in lab frame
+  HEPMassType const targetMass = 1_GeV;
+  MomentumVector pTargetLab{rootCS, {0_eV, 0_eV, 0_eV}};
+  HEPEnergyType const eTargetLab = energy(targetMass, pTargetLab);
+
+  /*
+    General tests check the interface and basic operation
+   */
+
+  SECTION("General tests") {
+
+    // define projectile kinematics in lab frame
+    HEPMassType const projectileMass = 1_GeV;
+    MomentumVector pProjectileLab{rootCS, {0_GeV, 1_PeV, 0_GeV}};
+    HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab);
+    FourVector const PprojLab(eProjectileLab, pProjectileLab);
+
+    // define boost to com frame
+    COMBoost boost(PprojLab, targetMass);
+
+    // boost projecticle
+    auto const PprojCoM = boost.toCoM(PprojLab);
+
+    // boost target
+    auto const PtargCoM = boost.toCoM(FourVector(targetMass, pTargetLab));
+
+    // sum of momenta in CoM, should be 0
+    auto const sumPCoM =
+        PprojCoM.getSpaceLikeComponents() + PtargCoM.getSpaceLikeComponents();
+    CHECK(sumPCoM.getNorm() / 1_GeV == Approx(0).margin(absMargin));
+
+    // mandelstam-s should be invariant under transformation
+    CHECK(s(eProjectileLab + eTargetLab,
+            pProjectileLab.getComponents() + pTargetLab.getComponents()) /
+              1_GeV / 1_GeV ==
+          Approx(s(PprojCoM.getTimeLikeComponent() + PtargCoM.getTimeLikeComponent(),
+                   PprojCoM.getSpaceLikeComponents().getComponents() +
+                       PtargCoM.getSpaceLikeComponents().getComponents()) /
+                 1_GeV / 1_GeV));
+
+    // boost back...
+    auto const PprojBack = boost.fromCoM(PprojCoM);
+
+    // ...should yield original values before the boosts
+    CHECK(PprojBack.getTimeLikeComponent() / PprojLab.getTimeLikeComponent() ==
+          Approx(1));
+    CHECK((PprojBack.getSpaceLikeComponents() - PprojLab.getSpaceLikeComponents())
+                  .getNorm() /
+              PprojLab.getSpaceLikeComponents().getNorm() ==
+          Approx(0).margin(absMargin));
+  }
+
+  /*
+    special case: projectile along -z
+   */
+
+  SECTION("Test boost along z-axis") {
+
+    // define projectile kinematics in lab frame
+    HEPMassType const projectileMass = 1_GeV;
+    MomentumVector pProjectileLab{rootCS, {0_GeV, 0_PeV, -1_PeV}};
+    HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab);
+    FourVector const PprojLab(eProjectileLab, pProjectileLab);
+
+    auto const sqrt_s_lab =
+        sqrt(s(eProjectileLab + targetMass, pProjectileLab.getComponents(rootCS)));
+
+    // define boost to com frame
+    COMBoost boost(PprojLab, targetMass);
+
+    // boost projecticle
+    auto const PprojCoM = boost.toCoM(PprojLab);
+    auto const a = PprojCoM.getSpaceLikeComponents().getComponents(boost.getRotatedCS());
+    CHECK(a.getX() / 1_GeV == Approx(0));
+    CHECK(a.getY() / 1_GeV == Approx(0));
+    CHECK(a.getZ() / (momentum(sqrt_s_lab / 2, projectileMass)) == Approx(1));
+
+    // boost target
+    auto const PtargCoM = boost.toCoM(FourVector(targetMass, pTargetLab));
+    CHECK(PtargCoM.getTimeLikeComponent() / sqrt_s_lab == Approx(.5));
+
+    // sum of momenta in CoM, should be 0
+    auto const sumPCoM =
+        PprojCoM.getSpaceLikeComponents() + PtargCoM.getSpaceLikeComponents();
+    CHECK(sumPCoM.getNorm() / 1_GeV == Approx(0).margin(absMargin));
+  }
+
+  /*
+    special case: projectile with arbitrary direction
+   */
+
+  SECTION("Test boost along tilted axis") {
+
+    HEPMomentumType const P0 = 1_PeV;
+    double theta = 33.;
+    double phi = -10.;
+    auto momentumComponents = [](double theta, double phi, HEPMomentumType ptot) {
+      return std::make_tuple(ptot * sin(theta) * cos(phi), ptot * sin(theta) * sin(phi),
+                             -ptot * cos(theta));
+    };
+    auto const [px, py, pz] =
+        momentumComponents(theta / 180. * M_PI, phi / 180. * M_PI, P0);
+
+    // define projectile kinematics in lab frame
+    HEPMassType const projectileMass = 1_GeV;
+    MomentumVector pProjectileLab(rootCS, {px, py, pz});
+    HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab);
+    FourVector const PprojLab(eProjectileLab, pProjectileLab);
+
+    // define boost to com frame
+    COMBoost boost(PprojLab, targetMass);
+
+    // boost projecticle
+    auto const PprojCoM = boost.toCoM(PprojLab);
+
+    // boost target
+    auto const PtargCoM = boost.toCoM(FourVector(targetMass, pTargetLab));
+
+    // sum of momenta in CoM, should be 0
+    auto const sumPCoM =
+        PprojCoM.getSpaceLikeComponents() + PtargCoM.getSpaceLikeComponents();
+    CHECK(sumPCoM.getNorm() / 1_GeV == Approx(0).margin(absMargin));
+  }
+
+  /*
+    test the ultra-high energy behaviour: E=ZeV
+   */
+
+  SECTION("High energy") {
+    // define projectile kinematics in lab frame
+    HEPMassType const projectileMass = 1_GeV;
+    HEPMomentumType P0 = 1_ZeV;
+    MomentumVector pProjectileLab{rootCS, {0_GeV, 0_PeV, -P0}};
+    HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab);
+    FourVector const PprojLab(eProjectileLab, pProjectileLab);
+
+    // define boost to com frame
+    COMBoost boost(PprojLab, targetMass);
+
+    // boost projecticle
+    auto const PprojCoM = boost.toCoM(PprojLab);
+
+    // boost target
+    auto const PtargCoM = boost.toCoM(FourVector(targetMass, pTargetLab));
+
+    // sum of momenta in CoM, should be 0
+    auto const sumPCoM =
+        PprojCoM.getSpaceLikeComponents() + PtargCoM.getSpaceLikeComponents();
+    CHECK(sumPCoM.getNorm() / P0 == Approx(0).margin(absMargin)); // MAKE RELATIVE CHECK
+  }
+}
+
+TEST_CASE("rest frame") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  HEPMassType const projectileMass = 1_GeV;
+  HEPMomentumType const P0 = 1_TeV;
+  MomentumVector pProjectileLab{rootCS, {0_GeV, P0, 0_GeV}};
+  HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab);
+  const FourVector PprojLab(eProjectileLab, pProjectileLab);
+
+  COMBoost boostRest(pProjectileLab, projectileMass);
+  auto const& csPrime = boostRest.getRotatedCS();
+  FourVector const rest4Mom = boostRest.toCoM(PprojLab);
+
+  CHECK(rest4Mom.getTimeLikeComponent() / 1_GeV == Approx(projectileMass / 1_GeV));
+  CHECK(rest4Mom.getSpaceLikeComponents().getNorm() / 1_GeV ==
+        Approx(0).margin(absMargin));
+
+  FourVector const a{0_eV, Vector{csPrime, 0_eV, 5_GeV, 0_eV}};
+  FourVector const b{0_eV, Vector{rootCS, 3_GeV, 0_eV, 0_eV}};
+  auto const aLab = boostRest.fromCoM(a);
+  auto const bLab = boostRest.fromCoM(b);
+
+  CHECK(aLab.getNorm() / a.getNorm() == Approx(1));
+  CHECK(aLab.getSpaceLikeComponents().getComponents(csPrime)[1].magnitude() ==
+        Approx((5_GeV).magnitude()));
+  CHECK(bLab.getSpaceLikeComponents().getComponents(rootCS)[0].magnitude() ==
+        Approx((3_GeV).magnitude()));
+}
diff --git a/tests/framework/testCascade.cpp b/tests/framework/testCascade.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8a7978b8446677132529b41c2c905abe7dab9cef
--- /dev/null
+++ b/tests/framework/testCascade.cpp
@@ -0,0 +1,185 @@
+/*
+ * (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.
+ */
+
+#include <testCascade.hpp>
+
+#include <corsika/framework/core/Cascade.hpp>
+
+#include <corsika/framework/process/ProcessSequence.hpp>
+#include <corsika/framework/process/NullModel.hpp>
+#include <corsika/modules/StackInspector.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+
+#include <limits>
+using namespace std;
+
+/**
+ * testCascade implements an e.m. Heitler model with energy splitting
+ * and a critical energy.
+ *
+ * It resembles one of the most simple cascades you can simulate with CORSIKA8.
+ **/
+
+/*
+  The dummy env (here) doesn't need to have any propoerties
+ */
+
+auto make_dummy_env() {
+  TestEnvironmentType env; // dummy environment
+  auto& universe = *(env.getUniverse());
+
+  auto world = TestEnvironmentType::createNode<Sphere>(
+      Point{env.getCoordinateSystem(), 0_m, 0_m, 0_m},
+      1_km * std::numeric_limits<double>::infinity());
+
+  using MyEmptyModel = Empty<IEmpty>;
+  world->setModelProperties<MyEmptyModel>();
+
+  universe.addChild(std::move(world));
+  return env;
+}
+
+/**
+ *
+ * For the Heitler model we don't need particle transport.
+ **/
+class DummyTracking {
+
+public:
+  template <typename TParticle>
+  auto getTrack(TParticle const& particle) {
+    VelocityVector const initialVelocity =
+        particle.getMomentum() / particle.getEnergy() * constants::c;
+    return std::make_tuple(
+        StraightTrajectory(
+            Line(particle.getPosition(), initialVelocity),
+            std::numeric_limits<TimeType::value_type>::infinity() * 1_s), // trajectory,
+                                                                          // just
+                                                                          // go
+                                                                          // ahead
+                                                                          // forever
+        particle.getNode()); // next volume node
+  }
+};
+
+class ProcessSplit : public InteractionProcess<ProcessSplit> {
+
+  int calls_ = 0;
+
+public:
+  template <typename Particle>
+  GrammageType getInteractionLength(Particle const&) const {
+    return 0_g / square(1_cm);
+  }
+
+  template <typename TView>
+  ProcessReturn doInteraction(TView& view) {
+    calls_++;
+    auto vP = view.getProjectile();
+    const HEPEnergyType E = vP.getEnergy();
+    vP.addSecondary(std::make_tuple(vP.getPID(), E / 2, vP.getMomentum(),
+                                    vP.getPosition(), vP.getTime()));
+    vP.addSecondary(std::make_tuple(vP.getPID(), E / 2, vP.getMomentum(),
+                                    vP.getPosition(), vP.getTime()));
+    return ProcessReturn::Interacted;
+  }
+
+  int getCalls() const { return calls_; }
+};
+
+class ProcessCut : public SecondariesProcess<ProcessCut> {
+
+  int count_ = 0;
+  int calls_ = 0;
+  HEPEnergyType fEcrit;
+
+public:
+  ProcessCut(HEPEnergyType e)
+      : fEcrit(e) {}
+
+  template <typename TStack>
+  void doSecondaries(TStack& vS) {
+    calls_++;
+    auto p = vS.begin();
+    while (p != vS.end()) {
+      HEPEnergyType E = p.getEnergy();
+      if (E < fEcrit) {
+        p.erase();
+        count_++;
+      }
+      ++p; // next particle
+    }
+    CORSIKA_LOG_INFO(fmt::format("ProcessCut::doSecondaries size={} count={}",
+                                 vS.getEntries(), count_));
+  }
+
+  int getCount() const { return count_; }
+  int getCalls() const { return calls_; }
+};
+
+TEST_CASE("Cascade", "[Cascade]") {
+
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  logging::set_level(logging::level::trace);
+
+  HEPEnergyType E0 = 100_GeV;
+
+  RNGManager& rmng = RNGManager::getInstance();
+  rmng.registerRandomStream("cascade");
+
+  auto env = make_dummy_env();
+  auto const& rootCS = env.getCoordinateSystem();
+
+  StackInspector<TestCascadeStack> stackInspect(1, true, E0);
+  NullModel nullModel;
+
+  const HEPEnergyType Ecrit = 85_MeV;
+  ProcessSplit split;
+  ProcessCut cut(Ecrit);
+  auto sequence = make_sequence(nullModel, stackInspect, split, cut);
+  TestCascadeStack stack;
+  stack.clear();
+  stack.addParticle(std::make_tuple(
+      Code::Electron, E0,
+      MomentumVector(rootCS, {0_GeV, 0_GeV,
+                              -sqrt(E0 * E0 - static_pow<2>(get_mass(Code::Electron)))}),
+      Point(rootCS, {0_m, 0_m, 10_km}), 0_ns));
+
+  DummyTracking tracking;
+  Cascade<DummyTracking, decltype(sequence), TestCascadeStack, TestCascadeStackView> EAS(
+      env, tracking, sequence, stack);
+
+  SECTION("full cascade") {
+    EAS.run();
+
+    CHECK(cut.getCount() == 2048);
+    CHECK(cut.getCalls() == 2047); // final particle is still on stack and not yet deleted
+    CHECK(split.getCalls() == 2047);
+  }
+
+  SECTION("forced interaction") {
+    EAS.setNodes();
+    EAS.forceInteraction();
+    CHECK(stack.getEntries() == 2);
+    CHECK(stack.getSize() == 3);
+    CHECK(split.getCalls() == 1);
+  }
+}
diff --git a/tests/framework/testCascade.hpp b/tests/framework/testCascade.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..8e6b06bd5d5b1f4fabc6b6f6f0f11b40187fdd83
--- /dev/null
+++ b/tests/framework/testCascade.hpp
@@ -0,0 +1,45 @@
+/*
+ * (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 <corsika/media/Environment.hpp>
+#include <corsika/media/IEmpty.hpp>
+
+#include <corsika/framework/stack/CombinedStack.hpp>
+#include <corsika/framework/stack/SecondaryView.hpp>
+#include <corsika/stack/GeometryNodeStackExtension.hpp>
+#include <corsika/stack/NuclearStackExtension.hpp>
+
+using TestEnvironmentInterface = corsika::IEmpty;
+using TestEnvironmentType = corsika::Environment<TestEnvironmentInterface>;
+
+template <typename T>
+using SetupGeometryDataInterface =
+    corsika::node::GeometryDataInterface<T, TestEnvironmentType>;
+
+// combine particle data stack with geometry information for tracking
+template <typename StackIter>
+using StackWithGeometryInterface =
+    corsika::CombinedParticleInterface<corsika::nuclear_stack::ParticleDataStack::pi_type,
+                                       SetupGeometryDataInterface, StackIter>;
+
+using TestCascadeStack = corsika::CombinedStack<
+    typename corsika::nuclear_stack::ParticleDataStack::stack_implementation_type,
+    corsika::node::GeometryData<TestEnvironmentType>, StackWithGeometryInterface>;
+
+/*
+  See also Issue 161
+*/
+#if defined(__clang__)
+using TestCascadeStackView =
+    corsika::SecondaryView<typename TestCascadeStack::stack_implementation_type,
+                           StackWithGeometryInterface>;
+#elif defined(__GNUC__) || defined(__GNUG__)
+using TestCascadeStackView = corsika::MakeView<TestCascadeStack>::type;
+#endif
diff --git a/Framework/Analytics/testClassTimer.cc b/tests/framework/testClassTimer.cpp
similarity index 82%
rename from Framework/Analytics/testClassTimer.cc
rename to tests/framework/testClassTimer.cpp
index 6185036b5ab36b1a2a203759fa18a06120020bc5..d51a56b1a254146847f742bfc0283ee9f6e44be2 100644
--- a/Framework/Analytics/testClassTimer.cc
+++ b/tests/framework/testClassTimer.cpp
@@ -6,7 +6,8 @@
  * the license.
  */
 
-#include <corsika/analytics/ClassTimer.h>
+#include <corsika/framework/analytics/ClassTimer.hpp>
+#include <corsika/framework/core/Logging.hpp>
 
 #include <catch2/catch.hpp>
 
@@ -46,7 +47,7 @@ public:
   }
 
   int inside() {
-    auto tc = corsika::analytics::ClassTimer<int (_foo1::*)(int), &_foo1::inside>(*this);
+    auto tc = corsika::ClassTimer<int (_foo1::*)(int), &_foo1::inside>(*this);
 
     auto r = tc.call(1);
 
@@ -102,11 +103,15 @@ public:
   }
 };
 
-TEST_CASE("Analytics", "[Timer]") {
+TEST_CASE("ClassTimer", "[Timer]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
   SECTION("Measure runtime of a function without arguments") {
 
     auto test = foo();
-    auto tc = corsika::analytics::ClassTimer<decltype(&foo::bar), &foo::bar>(test);
+    auto tc = corsika::ClassTimer<decltype(&foo::bar), &foo::bar>(test);
 
     tc.call();
 
@@ -116,7 +121,7 @@ TEST_CASE("Analytics", "[Timer]") {
   SECTION("Measure runtime of a function with arguments") {
 
     auto test = foo();
-    auto tc = corsika::analytics::ClassTimer<decltype(&foo::bar2), &foo::bar2>(test);
+    auto tc = corsika::ClassTimer<decltype(&foo::bar2), &foo::bar2>(test);
 
     tc.call(1);
 
@@ -126,8 +131,7 @@ TEST_CASE("Analytics", "[Timer]") {
   SECTION("Measure runtime of a const function without arguments") {
 
     auto test = foo();
-    auto tc =
-        corsika::analytics::ClassTimer<decltype(&foo::bar_const), &foo::bar_const>(test);
+    auto tc = corsika::ClassTimer<decltype(&foo::bar_const), &foo::bar_const>(test);
 
     tc.call();
 
diff --git a/tests/framework/testCombinedStack.cpp b/tests/framework/testCombinedStack.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..7d01f3fcd3139507c41617a2fd276ddfedb46138
--- /dev/null
+++ b/tests/framework/testCombinedStack.cpp
@@ -0,0 +1,402 @@
+/*
+ * (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.
+ */
+
+#define protected public // to also test the internal state of objects
+
+#include <corsika/framework/stack/CombinedStack.hpp>
+#include <corsika/framework/stack/SecondaryView.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <testTestStack.hpp> // for testing: simple stack. This is a
+// test-build, and inluce file is obtained from CMAKE_CURRENT_SOURCE_DIR
+
+#include <iomanip>
+#include <vector>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+using namespace std;
+
+////////////////////////////////////////////////////////////
+// first level test: combine two stacks:
+//                   StackTest = (TestStackData + TestStackData2)
+
+// definition of stack-data object
+class TestStackData2 {
+
+public:
+  // these functions are needed for the Stack interface
+  void clear() { data2_.clear(); }
+  unsigned int getSize() const { return data2_.size(); }
+  unsigned int getCapacity() const { return data2_.size(); }
+  void copy(const int i1, const int i2) { data2_[i2] = data2_[i1]; }
+  void swap(const int i1, const int i2) {
+    double tmp0 = data2_[i1];
+    data2_[i1] = data2_[i2];
+    data2_[i2] = tmp0;
+  }
+
+  // custom data access function
+  void setData2(const int i, const double v) { data2_[i] = v; }
+  double getData2(const int i) const { return data2_[i]; }
+
+  // these functions are also needed by the Stack interface
+  void incrementSize() { data2_.push_back(0.); }
+  void decrementSize() {
+    if (data2_.size() > 0) { data2_.pop_back(); }
+  }
+
+  // custom private data section
+private:
+  std::vector<double> data2_;
+};
+
+// defintion of a stack-readout object, the iteractor dereference
+// operator will deliver access to these function
+template <typename T>
+class TestParticleInterface2 : public T {
+
+public:
+  using T::getIndex;
+  using T::getStackData;
+  using T::setParticleData;
+
+  // default version for particle-creation from input data
+  void setParticleData(std::tuple<double> const v = {0.}) { setData2(std::get<0>(v)); }
+  void setParticleData(TestParticleInterface2<T>& parent,
+                       std::tuple<double> const v = {0.}) {
+    setData2(parent.getData2() + std::get<0>(v));
+  }
+  void setData2(const double v) { getStackData().setData2(getIndex(), v); }
+  double getData2() const { return getStackData().getData2(getIndex()); }
+};
+
+// combined stack: StackTest = (TestStackData + TestStackData2)
+template <typename TStackIter>
+using CombinedTestInterfaceType =
+    corsika::CombinedParticleInterface<TestParticleInterface, TestParticleInterface2,
+                                       TStackIter>;
+
+using StackTest = CombinedStack<TestStackData, TestStackData2, CombinedTestInterfaceType>;
+
+TEST_CASE("Combined Stack", "[stack]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  // helper function for sum over stack data
+  auto sum = [](const StackTest& stack) {
+    double v = 0;
+    for (const auto& p : stack) v += p.getData();
+    return v;
+  };
+  auto sum2 = [](const StackTest& stack) {
+    double v = 0;
+    for (const auto& p : stack) v += p.getData2();
+    return v;
+  };
+
+  SECTION("StackInterface") {
+
+    // construct a valid Stack object
+    StackTest s;
+    s.clear();
+    s.addParticle(std::tuple{0.});
+    s.copy(s.cbegin(), s.begin());
+    s.swap(s.begin(), s.begin());
+    CHECK(s.getSize() == 1);
+  }
+
+  SECTION("construct") {
+
+    // construct a valid, empty Stack object
+    StackTest s;
+  }
+
+  SECTION("write and read") {
+
+    StackTest s;
+    s.addParticle(std::tuple{9.9});
+    CHECK(sum2(s) == 0.);
+    CHECK(sum(s) == 9.9);
+  }
+
+  SECTION("delete from stack") {
+
+    StackTest s;
+    CHECK(s.getSize() == 0);
+    StackTest::stack_iterator_type p =
+        s.addParticle(std::tuple{0.}); // valid way to access particle data
+    p.setData(8.9);
+    p.setData2(3.);
+    CHECK(sum2(s) == 3.);
+    CHECK(sum(s) == 8.9);
+    CHECK(s.getSize() == 1);
+    CHECK(s.getEntries() == 1);
+    s.erase(p);
+    CHECK(s.getSize() == 1);
+    CHECK(s.getEntries() == 0);
+  }
+
+  SECTION("delete particle") {
+
+    StackTest s;
+    CHECK(s.getSize() == 0);
+    auto p = s.addParticle(
+        std::tuple{9.9}); // also valid way to access particle data, identical to above
+    CHECK(s.getSize() == 1);
+    CHECK(s.getEntries() == 1);
+    p.erase();
+    CHECK(s.getSize() == 1);
+    CHECK(s.getEntries() == 0);
+  }
+
+  SECTION("create secondaries") {
+    StackTest s;
+    CHECK(s.getSize() == 0);
+    auto iter = s.addParticle(std::tuple{9.9});
+    iter.setData2(2);
+    CHECK(s.getSize() == 1);
+    CHECK(s.getEntries() == 1);
+    iter.addSecondary(std::tuple{4.4});
+    CHECK(s.getSize() == 2);
+    CHECK(s.getEntries() == 2);
+    // p.addSecondary(3.3, 2.2, 1.);
+    // CHECK(s.getSize() == 3);
+    double v = 0;
+    for (const auto& i : s) {
+      v += i.getData();
+      CHECK(i.getData2() == 2);
+    }
+    CHECK(v == 9.9 + 4.4);
+  }
+
+  SECTION("get next particle") {
+    StackTest s;
+    CHECK(s.getSize() == 0);
+    CHECK(s.getEntries() == 0);
+    CHECK(s.isEmpty());
+
+    auto p1 = s.addParticle(std::tuple{9.9});
+    auto p2 = s.addParticle(std::tuple{8.8});
+    p1.setData2(20.2);
+    p2.setData2(20.3);
+    CHECK(s.getSize() == 2);
+    CHECK(s.getEntries() == 2);
+    CHECK(!s.isEmpty());
+
+    auto particle = s.getNextParticle(); // first particle
+    CHECK(particle.getData() == 8.8);
+    CHECK(particle.getData2() == 20.3);
+
+    particle.erase(); // only marks (last) particle as "deleted"
+    CHECK(s.getSize() == 2);
+    CHECK(s.getEntries() == 1);
+    CHECK(!s.isEmpty());
+
+    /*
+      This following call to GetNextParticle will realize that the
+      current last particle on the stack was marked "deleted" and will
+      purge it: stack size is reduced by one.
+     */
+    auto particle2 = s.getNextParticle(); // first particle
+    CHECK(s.getSize() == 1);
+    CHECK(s.getEntries() == 1);
+    CHECK(!s.isEmpty());
+    CHECK(particle2.getData() == 9.9);
+    CHECK(particle2.getData2() == 20.2);
+
+    particle2.erase(); // also mark this particle as "deleted"
+    CHECK(s.getSize() == 1);
+    CHECK(s.getEntries() == 0);
+    CHECK(s.isEmpty());
+  }
+}
+
+////////////////////////////////////////////////////////////
+// next level: combine three stacks:
+// combined stack: StackTest2 = ((TestStackData + TestStackData2) + TestStackData3)
+
+// definition of stack-data object
+class TestStackData3 {
+
+public:
+  // these functions are needed for the Stack interface
+  void clear() { data3_.clear(); }
+  unsigned int getSize() const { return data3_.size(); }
+  unsigned int getCapacity() const { return data3_.size(); }
+  void copy(const int i1, const int i2) { data3_[i2] = data3_[i1]; }
+  void swap(const int i1, const int i2) {
+    double tmp0 = data3_[i1];
+    data3_[i1] = data3_[i2];
+    data3_[i2] = tmp0;
+  }
+
+  // custom data access function
+  void setData3(const int i, const double v) { data3_[i] = v; }
+  double getData3(const int i) const { return data3_[i]; }
+
+  // these functions are also needed by the Stack interface
+  void incrementSize() { data3_.push_back(0.); }
+  void decrementSize() {
+    if (data3_.size() > 0) { data3_.pop_back(); }
+  }
+
+  // custom private data section
+private:
+  std::vector<double> data3_;
+};
+
+// ---------------------------------------
+// defintion of a stack-readout object, the iteractor dereference
+// operator will deliver access to these function
+template <typename T>
+class TestParticleInterface3 : public T {
+
+public:
+  using T::getIndex;
+  using T::getStackData;
+  using T::setParticleData;
+
+  // default version for particle-creation from input data
+  void setParticleData(std::tuple<double> const v = {0.}) { setData3(std::get<0>(v)); }
+  void setParticleData(TestParticleInterface3<T>& parent,
+                       std::tuple<double> const v = {0.}) {
+    setData3(parent.getData3() + std::get<0>(v));
+  }
+  void setData3(const double v) { getStackData().setData3(getIndex(), v); }
+  double getData3() const { return getStackData().getData3(getIndex()); }
+};
+
+// double combined stack:
+// combined stack
+template <typename TStackIter>
+using CombinedTestInterfaceType2 =
+    corsika::CombinedParticleInterface<StackTest::pi_type, TestParticleInterface3,
+                                       TStackIter>;
+
+using StackTest2 = CombinedStack<typename StackTest::stack_implementation_type,
+                                 TestStackData3, CombinedTestInterfaceType2>;
+
+TEST_CASE("Combined Stack - multi", "[stack]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  SECTION("create secondaries") {
+
+    StackTest2 s;
+    CHECK(s.getSize() == 0);
+    CHECK(s.isEmpty()); // size = entries = 0
+
+    // add new particle, only provide tuple data for StackTest
+    auto p1 = s.addParticle(std::tuple{9.9});
+    // add new particle, provide tuple data for both StackTest and TestStackData3
+    auto p2 = s.addParticle(std::tuple{8.8}, std::tuple{0.1});
+
+    CHECK(s.getSize() == 2);
+    CHECK(!s.isEmpty()); // size = entries = 2
+
+    // examples to explicitly change data on stack
+    p2.setData2(0.1); // not clear why this is needed, need to check
+                      // SetParticleData workflow for more complicated
+                      // settings
+    p1.setData3(20.2);
+    p2.setData3(10.3);
+
+    CHECK(p1.getData() == 9.9);
+    CHECK(p1.getData2() == 0.);
+    p1.setData2(10.2);
+    CHECK(p1.getData2() == 10.2);
+    CHECK(p1.getData3() == 20.2);
+
+    CHECK(p2.getData() == 8.8);
+    CHECK(p2.getData2() == 0.1);
+    CHECK(p2.getData3() == 10.3);
+
+    auto particle = s.getNextParticle(); // first particle
+    CHECK(particle.getData() == 8.8);
+    CHECK(particle.getData2() == 0.1);
+    CHECK(particle.getData3() == 10.3);
+
+    auto sec = particle.addSecondary(std::tuple{4.4});
+    CHECK(s.getSize() == 3);
+    CHECK(s.getEntries() == 3);
+    CHECK(sec.getData() == 4.4);
+    CHECK(sec.getData2() == 0.1);
+    CHECK(sec.getData3() == 10.3);
+
+    sec.erase(); // mark for deletion: size=3, entries=2
+    CHECK(s.getSize() == 3);
+    CHECK(s.getEntries() == 2);
+    CHECK(!s.isEmpty());
+
+    s.last().erase(); // mark for deletion: size=3, entries=1
+    CHECK(s.getSize() == 3);
+    CHECK(s.getEntries() == 1);
+    CHECK(!s.isEmpty());
+
+    /*
+       GetNextParticle will find two entries marked as "deleted" and
+       will purge this from the end of the stack: size = 1
+    */
+    s.getNextParticle().erase(); // mark for deletion: size=3, entries=0
+    CHECK(s.getSize() == 1);
+    CHECK(s.getEntries() == 0);
+    CHECK(s.isEmpty());
+  }
+}
+
+////////////////////////////////////////////////////////////
+
+// final level test, create SecondaryView on StackTest2
+
+/*
+  See Issue 161
+
+  unfortunately clang does not support this in the same way (yet) as
+  gcc, so we have to distinguish here. If clang cataches up, we could
+  remove the clang branch here and also in corsika::Cascade. The gcc
+  code is much more generic and universal.
+ */
+template <typename TStackIter>
+using CombinedTestInterfaceType2 =
+    corsika::CombinedParticleInterface<StackTest::pi_type, TestParticleInterface3,
+                                       TStackIter>;
+
+using StackTest2 = CombinedStack<typename StackTest::stack_implementation_type,
+                                 TestStackData3, CombinedTestInterfaceType2>;
+
+#if defined(__clang__)
+using StackTestView = SecondaryView<typename StackTest2::stack_implementation_type,
+                                    CombinedTestInterfaceType2>;
+#elif defined(__GNUC__) || defined(__GNUG__)
+using StackTestView = corsika::MakeView<StackTest2>::type;
+#endif
+
+using Particle2 = typename StackTest2::particle_type;
+
+TEST_CASE("Combined Stack - secondary view") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  SECTION("create secondaries via secondaryview") {
+
+    StackTest2 stack;
+    auto particle = stack.addParticle(std::tuple{9.9});
+    StackTestView view(particle);
+
+    auto projectile = view.getProjectile();
+    projectile.addSecondary(std::tuple{8.8});
+
+    CHECK(stack.getSize() == 2);
+  }
+}
diff --git a/tests/framework/testCorsikaFenv.cpp b/tests/framework/testCorsikaFenv.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..2b6cdb58034d06c82cf336c2826cdb2e4f4248f6
--- /dev/null
+++ b/tests/framework/testCorsikaFenv.cpp
@@ -0,0 +1,38 @@
+/*
+ * (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.
+ */
+
+#include <catch2/catch.hpp>
+#include <corsika/framework/utility/CorsikaFenv.hpp>
+
+#include <cmath>
+#include <csignal>
+#include <iostream>
+
+int gRESULT = 1;
+
+extern "C" {
+static void handle_fpe(int /*signo*/) { gRESULT = 0; }
+}
+
+TEST_CASE("CorsikaFenv", "[fenv]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  SECTION("Enable all exceptions") { feenableexcept(FE_ALL_EXCEPT); }
+
+  signal(SIGFPE, handle_fpe);
+
+  SECTION("exception") {
+
+    [[maybe_unused]] auto trigger = std::log(0.);
+    std::cout << "trigger: " << trigger << std::endl;
+    CHECK(gRESULT == 0);
+  }
+}
+}
diff --git a/tests/framework/testFourVector.cpp b/tests/framework/testFourVector.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..09b458295ef0b118684ed2707389a0a5f0fcfd9c
--- /dev/null
+++ b/tests/framework/testFourVector.cpp
@@ -0,0 +1,182 @@
+/*
+ * (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.
+ */
+
+#include <catch2/catch.hpp>
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/CoordinateSystem.hpp>
+#include <corsika/framework/geometry/FourVector.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+#include <cmath>
+
+using namespace corsika;
+
+TEST_CASE("four vectors") {
+
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  logging::set_level(logging::level::info);
+
+  // this is just needed as a baseline
+  CoordinateSystemPtr rootCS = get_root_CoordinateSystem();
+
+  /*
+    Test: P2 = E2 - p2 all in [GeV]
+    This is the typical HEP application
+   */
+  SECTION("Energy momentum in hep-units") {
+
+    HEPEnergyType E0 = 10_GeV;
+    Vector<hepmomentum_d> P0(rootCS, {10_GeV, 10_GeV, 10_GeV});
+
+    FourVector p0(E0, P0);
+
+    CHECK(p0.getNormSqr() == -200_GeV * 1_GeV);
+    CHECK(p0.getNorm() == sqrt(200_GeV * 1_GeV));
+  }
+
+  /*
+    Check space/time-like
+   */
+  SECTION("Space/time likeness") {
+
+    HEPEnergyType E0 = 20_GeV;
+    Vector<hepmomentum_d> P0(rootCS, {10_GeV, 0_GeV, 0_GeV});
+    Vector<hepmomentum_d> P1(rootCS, {10_GeV, 10_GeV, 20_GeV});
+    Vector<hepmomentum_d> P2(rootCS, {0_GeV, 20_GeV, 0_GeV});
+
+    FourVector p0(E0, P0);
+    FourVector p1(E0, P1);
+    FourVector p2(E0, P2);
+
+    CHECK(p0.isSpacelike());
+    CHECK(!p0.isTimelike());
+
+    CHECK(!p1.isSpacelike());
+    CHECK(p1.isTimelike());
+
+    CHECK(!p2.isSpacelike());
+    CHECK(!p2.isTimelike());
+  }
+
+  /*
+    Test: P2 = E2/c2 - p2 with E in [GeV/c] and P in [GeV]
+    This requires additional factors of c
+   */
+  SECTION("Energy momentum in SI-units") {
+
+    auto E1 = 100_GeV / constants::c;
+    Vector<hepmomentum_d> P1(rootCS, {10_GeV, 5_GeV, 15_GeV});
+
+    FourVector p1(E1, P1);
+
+    double const check = 100 * 100 - 10 * 10 - 5 * 5 - 15 * 15; // for dummies...
+
+    CHECK(p1.getNormSqr() / 1_GeV / 1_GeV == Approx(check));
+    CHECK(p1.getNorm() / 1_GeV == Approx(sqrt(check)));
+  }
+
+  /**
+    Test: P2 = T2/c2 - r2 with T in [s] and r in [m]
+    This requires additional factors of c
+   */
+  SECTION("Spacetime in SI-units") {
+
+    TimeType T2 = 10_m / constants::c;
+    Vector<length_d> P2(rootCS, {10_m, 5_m, 5_m});
+
+    double const check = 10 * 10 - 10 * 10 - 5 * 5 - 5 * 5; // for dummies...
+
+    FourVector p2(T2, P2);
+
+    CHECK(p2.getNormSqr() == check * 1_m * 1_m);
+    CHECK(p2.getNorm() == sqrt(abs(check)) * 1_m);
+  }
+
+  /**
+     Testing the math operators
+   */
+
+  SECTION("Operators and comutions") {
+
+    HEPEnergyType E1 = 100_GeV;
+    Vector<hepmomentum_d> P1(rootCS, {0_GeV, 0_GeV, 0_GeV});
+
+    HEPEnergyType E2 = 0_GeV;
+    Vector<hepmomentum_d> P2(rootCS, {10_GeV, 0_GeV, 0_GeV});
+
+    FourVector const p1(E1, P1);
+    FourVector const p2(E2, P2);
+
+    CHECK(p1.getNorm() / 1_GeV == Approx(100.));
+    CHECK(p2.getNorm() / 1_GeV == Approx(10.));
+
+    SECTION("product") {
+      FourVector p3 = p1 + p2;
+      CHECK(p3.getNorm() / 1_GeV == Approx(sqrt(100. * 100. - 100.)));
+      p3 -= p2;
+      CHECK(p3.getNorm() / 1_GeV == Approx(100.));
+      CHECK(p1.getNorm() / 1_GeV == Approx(100.));
+      CHECK(p2.getNorm() / 1_GeV == Approx(10.));
+    }
+
+    SECTION("difference") {
+      FourVector p3 = p1 - p2;
+      CHECK(p3.getNorm() / 1_GeV == Approx(sqrt(100. * 100. - 100.)));
+      p3 += p2;
+      CHECK(p3.getNorm() / 1_GeV == Approx(100.));
+      CHECK(p1.getNorm() / 1_GeV == Approx(100.));
+      CHECK(p2.getNorm() / 1_GeV == Approx(10.));
+    }
+
+    SECTION("scale") {
+      double s = 10;
+      FourVector p3 = p1 * s;
+      CHECK(p3.getNorm() / 1_GeV == Approx(sqrt(100. * 100. * s * s)));
+      p3 /= 10;
+      CHECK(p3.getNorm() / 1_GeV == Approx(sqrt(100. * 100.)));
+      CHECK(p1.getNorm() / 1_GeV == Approx(100.));
+      CHECK(p2.getNorm() / 1_GeV == Approx(10.));
+    }
+  }
+
+  /**
+     The FourVector class can be used with reference template
+     arguments. In this configuration it does not hold any data
+     itself, but rather just refers to data located elsewhere. Thus,
+     it merely provides the physical/mathematical wrapper around the
+     data.
+   */
+
+  SECTION("Use as wrapper") {
+
+    TimeType T = 10_m / constants::c;
+    Vector<length_d> P(rootCS, {10_m, 5_m, 5_m});
+
+    TimeType const T_c = 10_m / constants::c;
+    Vector<length_d> const P_c(rootCS, {10_m, 5_m, 5_m});
+
+    /*
+      this does not compile, and it shoudn't!
+      FourVector<TimeType&, Vector<length_d>&> p0(T_c, P_c);
+    */
+    FourVector<TimeType&, Vector<length_d>&> p1(T, P);
+    FourVector<TimeType const&, Vector<length_d> const&> p2(T, P);
+    FourVector<TimeType const&, Vector<length_d> const&> p3(T_c, P_c);
+
+    p1 *= 10;
+    // p2 *= 10; // this does not compile, and it shoudn't !
+    // p3 *= 10; // this does not compile, and it shoudn't !!
+
+    double const check = 10 * 10 - 10 * 10 - 5 * 5 - 5 * 5; // for dummies...
+    CHECK(p1.getNormSqr() / (1_m * 1_m) == Approx(10. * 10. * check));
+    CHECK(p2.getNorm() / 1_m == Approx(10 * sqrt(abs(check))));
+    CHECK(p3.getNorm() / 1_m == Approx(sqrt(abs(check))));
+  }
+}
diff --git a/Framework/Analytics/testFunctionTimer.cc b/tests/framework/testFunctionTimer.cpp
similarity index 72%
rename from Framework/Analytics/testFunctionTimer.cc
rename to tests/framework/testFunctionTimer.cpp
index f703d7cd47226f157bc03e447619127223c02d13..f0bc38e67ec68a7aa85c898f007e1fd405b4dc1c 100644
--- a/Framework/Analytics/testFunctionTimer.cc
+++ b/tests/framework/testFunctionTimer.cpp
@@ -6,7 +6,8 @@
  * the license.
  */
 
-#include <corsika/analytics/FunctionTimer.h>
+#include <corsika/framework/analytics/FunctionTimer.hpp>
+#include <corsika/framework/core/Logging.hpp>
 
 #include <catch2/catch.hpp>
 
@@ -29,10 +30,14 @@ public:
   }
 };
 
-TEST_CASE("Analytics", "[Timer]") {
+TEST_CASE("FunctionTimer", "[Timer]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
   SECTION("Measure runtime of a free function") {
 
-    auto test = corsika::analytics::FunctionTimer(testFunc);
+    auto test = corsika::FunctionTimer(testFunc);
 
     std::cout << test() << std::endl;
     std::cout << test.getTime().count() << std::endl;
@@ -40,7 +45,7 @@ TEST_CASE("Analytics", "[Timer]") {
 
   SECTION("Measure runtime of a class functor") {
     TestClass testC;
-    auto test = corsika::analytics::FunctionTimer(testC);
+    auto test = corsika::FunctionTimer(testC);
 
     std::cout << test() << std::endl;
     std::cout << test.getTime().count() << std::endl;
diff --git a/tests/framework/testGeometry.cpp b/tests/framework/testGeometry.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a39201c04de97d13ccc24343cd5b4a0148a2c5a5
--- /dev/null
+++ b/tests/framework/testGeometry.cpp
@@ -0,0 +1,311 @@
+/*
+ * (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.
+ */
+
+#include <catch2/catch.hpp>
+
+#include <cmath>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/CoordinateSystem.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Helix.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+#include <corsika/framework/geometry/StraightTrajectory.hpp>
+#include <corsika/framework/geometry/LeapFrogTrajectory.hpp>
+
+#include <PhysicalUnitsCatch2.hpp> // namespace corsike::testing
+
+using namespace corsika;
+using namespace corsika::testing;
+
+double constexpr absMargin = 1.0e-8;
+
+TEST_CASE("transformations between CoordinateSystems") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  CoordinateSystemPtr rootCS = get_root_CoordinateSystem();
+
+  QuantityVector<length_d> const coordinates{0_m, 0_m, 0_m};
+  Point p1(rootCS, coordinates);
+
+  QuantityVector<magnetic_flux_density_d> components{1. * tesla, 0. * tesla, 0. * tesla};
+  Vector<magnetic_flux_density_d> v1(rootCS, components);
+
+  CHECK((p1.getCoordinates() - coordinates).getNorm().magnitude() ==
+        Approx(0).margin(absMargin));
+  CHECK((p1.getCoordinates(rootCS) - coordinates).getNorm().magnitude() ==
+        Approx(0).margin(absMargin));
+
+  SECTION("translations") {
+    QuantityVector<length_d> const translationVector{0_m, 4_m, 0_m};
+
+    CoordinateSystemPtr translatedCS = make_translation(rootCS, translationVector);
+
+    CHECK(translatedCS->getReferenceCS() == rootCS);
+
+    CHECK((p1.getCoordinates(translatedCS) + translationVector).getNorm().magnitude() ==
+          Approx(0).margin(absMargin));
+
+    // Vectors are not subject to translations
+    CHECK((v1.getComponents(rootCS) - v1.getComponents(translatedCS))
+              .getNorm()
+              .magnitude() == Approx(0).margin(absMargin));
+
+    Point p2(translatedCS, {0_m, 0_m, 0_m});
+    CHECK(((p2 - p1).getComponents() - translationVector).getNorm().magnitude() ==
+          Approx(0).margin(absMargin));
+  }
+
+  SECTION("multiple translations") {
+    QuantityVector<length_d> const tv1{0_m, 5_m, 0_m};
+    CoordinateSystemPtr cs2 = make_translation(rootCS, tv1);
+
+    QuantityVector<length_d> const tv2{3_m, 0_m, 0_m};
+    CoordinateSystemPtr cs3 = make_translation(rootCS, tv2);
+
+    QuantityVector<length_d> const tv3{0_m, 0_m, 2_m};
+    CoordinateSystemPtr cs4 = make_translation(cs3, tv3);
+
+    CHECK(cs4->getReferenceCS()->getReferenceCS() == rootCS);
+
+    CHECK(get_transformation(*cs3.get(), *cs2.get())
+              .isApprox(make_translation(rootCS, {3_m, -5_m, 0_m})->getTransform()));
+    CHECK(get_transformation(*cs2.get(), *cs3.get())
+              .isApprox(make_translation(rootCS, {-3_m, +5_m, 0_m})->getTransform()));
+  }
+
+  SECTION("rotations") {
+    QuantityVector<length_d> const axis{0_m, 0_m, 1_km};
+    double const angle = 90. / 180. * M_PI;
+
+    CoordinateSystemPtr rotatedCS = make_rotation(rootCS, axis, angle);
+    CHECK(rotatedCS->getReferenceCS() == rootCS);
+
+    CHECK(v1.getComponents(rotatedCS)[1].magnitude() ==
+          Approx((-1. * tesla).magnitude()));
+
+    // vector norm invariant under rotation
+    CHECK(v1.getComponents(rotatedCS).getNorm().magnitude() ==
+          Approx(v1.getComponents(rootCS).getNorm().magnitude()));
+  }
+
+  SECTION("multiple rotations") {
+    QuantityVector<length_d> const zAxis{0_m, 0_m, 1_km};
+    QuantityVector<length_d> const yAxis{0_m, 7_nm, 0_m};
+    QuantityVector<length_d> const xAxis{2_m, 0_nm, 0_m};
+
+    QuantityVector<magnetic_flux_density_d> components{1. * tesla, 2. * tesla,
+                                                       3. * tesla};
+    Vector<magnetic_flux_density_d> v1(rootCS, components);
+
+    double const angle = 90. / 180. * M_PI;
+
+    CoordinateSystemPtr rotated1 = make_rotation(rootCS, zAxis, angle);
+    CoordinateSystemPtr rotated2 = make_rotation(rotated1, yAxis, angle);
+    CoordinateSystemPtr rotated3 = make_rotation(rotated2, zAxis, -angle);
+
+    CoordinateSystemPtr combined = make_rotation(rootCS, xAxis, -angle);
+
+    auto comp1 = v1.getComponents(rotated3);
+    auto comp3 = v1.getComponents(combined);
+    CHECK((comp1 - comp3).getNorm().magnitude() == Approx(0).margin(absMargin));
+  }
+
+  SECTION("RotateToZ positive") {
+    Vector const v{rootCS, 0_m, 1_m, 1_m};
+    auto const csPrime = make_rotationToZ(rootCS, v);
+    Vector const zPrime{csPrime, 0_m, 0_m, 5_m};
+    Vector const xPrime{csPrime, 5_m, 0_m, 0_m};
+    Vector const yPrime{csPrime, 0_m, 5_m, 0_m};
+
+    CHECK(xPrime.dot(v).magnitude() == Approx(0).margin(absMargin));
+    CHECK(yPrime.dot(v).magnitude() == Approx(0).margin(absMargin));
+    CHECK((zPrime.dot(v) / 1_m).magnitude() == Approx(5 * sqrt(2)));
+
+    CHECK(zPrime.getComponents(rootCS)[1].magnitude() ==
+          Approx(zPrime.getComponents(rootCS)[2].magnitude()));
+    CHECK(zPrime.getComponents(rootCS)[0].magnitude() == Approx(0));
+
+    CHECK(xPrime.getComponents(rootCS).getEigenVector().dot(
+              yPrime.getComponents(rootCS).getEigenVector()) == Approx(0));
+    CHECK(zPrime.getComponents(rootCS).getEigenVector().dot(
+              xPrime.getComponents(rootCS).getEigenVector()) == Approx(0));
+    CHECK(yPrime.getComponents(rootCS).getEigenVector().dot(
+              zPrime.getComponents(rootCS).getEigenVector()) == Approx(0));
+
+    CHECK(yPrime.getComponents(rootCS).getEigenVector().dot(
+              yPrime.getComponents(rootCS).getEigenVector()) ==
+          Approx((5_m * 5_m).magnitude()));
+    CHECK(xPrime.getComponents(rootCS).getEigenVector().dot(
+              xPrime.getComponents(rootCS).getEigenVector()) ==
+          Approx((5_m * 5_m).magnitude()));
+    CHECK(zPrime.getComponents(rootCS).getEigenVector().dot(
+              zPrime.getComponents(rootCS).getEigenVector()) ==
+          Approx((5_m * 5_m).magnitude()));
+  }
+
+  SECTION("RotateToZ negative") {
+    Vector const v{rootCS, 0_m, 0_m, -1_m};
+    auto const csPrime = make_rotationToZ(rootCS, v);
+    Vector const zPrime{csPrime, 0_m, 0_m, 5_m};
+    Vector const xPrime{csPrime, 5_m, 0_m, 0_m};
+    Vector const yPrime{csPrime, 0_m, 5_m, 0_m};
+
+    CHECK(zPrime.dot(v).magnitude() > 0);
+    CHECK(xPrime.getComponents(rootCS).getEigenVector().dot(
+              v.getComponents().getEigenVector()) == Approx(0));
+    CHECK(yPrime.getComponents(rootCS).getEigenVector().dot(
+              v.getComponents().getEigenVector()) == Approx(0));
+
+    CHECK(xPrime.getComponents(rootCS).getEigenVector().dot(
+              yPrime.getComponents(rootCS).getEigenVector()) == Approx(0));
+    CHECK(zPrime.getComponents(rootCS).getEigenVector().dot(
+              xPrime.getComponents(rootCS).getEigenVector()) == Approx(0));
+    CHECK(yPrime.getComponents(rootCS).getEigenVector().dot(
+              zPrime.getComponents(rootCS).getEigenVector()) == Approx(0));
+
+    CHECK(yPrime.getComponents(rootCS).getEigenVector().dot(
+              yPrime.getComponents(rootCS).getEigenVector()) ==
+          Approx((5_m * 5_m).magnitude()));
+    CHECK(xPrime.getComponents(rootCS).getEigenVector().dot(
+              xPrime.getComponents(rootCS).getEigenVector()) ==
+          Approx((5_m * 5_m).magnitude()));
+    CHECK(zPrime.getComponents(rootCS).getEigenVector().dot(
+              zPrime.getComponents(rootCS).getEigenVector()) ==
+          Approx((5_m * 5_m).magnitude()));
+  }
+}
+
+TEST_CASE("CoordinateSystem hirarchy") {
+
+  CoordinateSystemPtr rootCS = get_root_CoordinateSystem();
+
+  CHECK(get_transformation(*rootCS.get(), *rootCS.get())
+            .isApprox(EigenTransform::Identity()));
+
+  // define the root coordinate system
+  CoordinateSystemPtr root = get_root_CoordinateSystem();
+  Point const p1(root, {0_m, 0_m, 0_m}); // the origin of the root CS
+
+  // root -> cs2
+  CoordinateSystemPtr cs2 = make_translation(root, {0_m, 0_m, 1_m});
+  Point const p2(cs2, {0_m, 0_m, -1_m});
+
+  // root -> cs2 -> cs3
+  CoordinateSystemPtr cs3 = make_translation(cs2, {0_m, 0_m, -1_m});
+  Point const p3(cs3, {0_m, 0_m, 0_m});
+
+  // root -> cs2 -> cs4
+  CoordinateSystemPtr cs4 = make_translation(cs2, {0_m, 0_m, -1_m});
+  Point const p4(cs4, {0_m, 0_m, 0_m});
+
+  // root -> cs2 -> cs4 -> cs5
+  CoordinateSystemPtr cs5 =
+      make_rotation(cs4, QuantityVector<length_d>{1_m, 0_m, 0_m}, 90 * degree_angle);
+  Point const p5(cs5, {0_m, 0_m, 0_m});
+
+  // root -> cs6
+  CoordinateSystemPtr cs6 =
+      make_rotation(root, QuantityVector<length_d>{1_m, 0_m, 0_m}, 90 * degree_angle);
+  Point const p6(cs6, {0_m, 0_m, 0_m}); // the origin of the root CS
+
+  // all points should be on top of each other
+
+  CHECK_FALSE(
+      get_transformation(*root.get(), *cs2.get()).isApprox(EigenTransform::Identity()));
+  CHECK(get_transformation(*root.get(), *cs3.get()).isApprox(EigenTransform::Identity()));
+  CHECK(get_transformation(*root.get(), *cs4.get()).isApprox(EigenTransform::Identity()));
+  CHECK(get_transformation(*cs5.get(), *cs6.get()).isApprox(EigenTransform::Identity()));
+
+  CHECK((p1 - p2).getNorm().magnitude() == Approx(0).margin(absMargin));
+  CHECK((p1 - p3).getNorm().magnitude() == Approx(0).margin(absMargin));
+  CHECK((p1 - p4).getNorm().magnitude() == Approx(0).margin(absMargin));
+  CHECK((p1 - p5).getNorm().magnitude() == Approx(0).margin(absMargin));
+  CHECK((p1 - p6).getNorm().magnitude() == Approx(0).margin(absMargin));
+}
+
+TEST_CASE("Sphere") {
+  CoordinateSystemPtr const& rootCS = get_root_CoordinateSystem();
+  Point center(rootCS, {0_m, 3_m, 4_m});
+  Sphere sphere(center, 5_m);
+
+  SECTION("getCenter") {
+    CHECK((sphere.getCenter().getCoordinates(rootCS) -
+           QuantityVector<length_d>(0_m, 3_m, 4_m))
+              .getNorm()
+              .magnitude() == Approx(0).margin(absMargin));
+    CHECK(sphere.getRadius() / 5_m == Approx(1));
+  }
+
+  SECTION("isInside") {
+    CHECK_FALSE(sphere.contains(Point(rootCS, {100_m, 0_m, 0_m})));
+    CHECK(sphere.contains(Point(rootCS, {2_m, 3_m, 4_m})));
+  }
+}
+
+TEST_CASE("Trajectories") {
+  CoordinateSystemPtr rootCS = get_root_CoordinateSystem();
+  Point r0(rootCS, {0_m, 0_m, 0_m});
+
+  SECTION("Line") {
+    VelocityVector v0(rootCS, {3_m / second, 0_m / second, 0_m / second});
+
+    Line const line(r0, v0);
+    CHECK(
+        (line.getPosition(2_s).getCoordinates() - QuantityVector<length_d>(6_m, 0_m, 0_m))
+            .getNorm()
+            .magnitude() == Approx(0).margin(absMargin));
+
+    CHECK((line.getPositionFromArclength(4_m).getCoordinates() -
+           QuantityVector<length_d>(4_m, 0_m, 0_m))
+              .getNorm()
+              .magnitude() == Approx(0).margin(absMargin));
+
+    CHECK((line.getPosition(7_s) -
+           line.getPositionFromArclength(line.getArcLength(0_s, 7_s)))
+              .getNorm()
+              .magnitude() == Approx(0).margin(absMargin));
+
+    auto const t = 1_s;
+    StraightTrajectory base(line, t);
+    CHECK(line.getPosition(t).getCoordinates() == base.getPosition(1.).getCoordinates());
+
+    CHECK((base.getDirection(0).getComponents(rootCS) -
+           QuantityVector<dimensionless_d>{1, 0, 0})
+              .getNorm() == Approx(0).margin(absMargin));
+  }
+
+  SECTION("Helix") {
+    VelocityVector const vPar(rootCS, {0_m / second, 0_m / second, 4_m / second});
+
+    VelocityVector const vPerp(rootCS, {3_m / second, 0_m / second, 0_m / second});
+
+    auto const T = 1_s;
+    auto const omegaC = 2 * M_PI / T;
+
+    Helix const helix(r0, omegaC, vPar, vPerp);
+
+    CHECK((helix.getPosition(1_s).getCoordinates() -
+           QuantityVector<length_d>(0_m, 0_m, 4_m))
+              .getNorm()
+              .magnitude() == Approx(0).margin(absMargin));
+
+    CHECK((helix.getPosition(0.25_s).getCoordinates() -
+           QuantityVector<length_d>(-3_m / (2 * M_PI), -3_m / (2 * M_PI), 1_m))
+              .getNorm()
+              .magnitude() == Approx(0).margin(absMargin));
+
+    CHECK((helix.getPosition(7_s) -
+           helix.getPositionFromArclength(helix.getArcLength(0_s, 7_s)))
+              .getNorm()
+              .magnitude() == Approx(0).margin(absMargin));
+  }
+}
diff --git a/tests/framework/testHelix.cpp b/tests/framework/testHelix.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..cbe997d203a02705001f9304bc767cfedce848db
--- /dev/null
+++ b/tests/framework/testHelix.cpp
@@ -0,0 +1,66 @@
+/*
+ * (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.
+ */
+
+#include <catch2/catch.hpp>
+
+#include <corsika/framework/geometry/Helix.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/CoordinateSystem.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+
+using namespace corsika;
+
+double constexpr absMargin = 1.0e-8;
+
+TEST_CASE("Helix class") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  const CoordinateSystemPtr rootCS = get_root_CoordinateSystem();
+  Point r0(rootCS, {0_m, 0_m, 0_m});
+
+  SECTION("Helix") {
+    Vector<SpeedType::dimension_type> const vPar(
+        rootCS, {0_m / second, 0_m / second, 4_m / second});
+
+    Vector<SpeedType::dimension_type> const vPerp(
+        rootCS, {3_m / second, 0_m / second, 0_m / second});
+
+    auto const T = 1_s;
+    auto const omegaC = 2 * M_PI / T;
+
+    Helix const helix(r0, omegaC, vPar, vPerp);
+
+    CHECK((helix.getPosition(1_s).getCoordinates() -
+           QuantityVector<length_d>(0_m, 0_m, 4_m))
+              .getNorm()
+              .magnitude() == Approx(0).margin(absMargin));
+
+    CHECK((helix.getPosition(0.25_s).getCoordinates() -
+           QuantityVector<length_d>(-3_m / (2 * M_PI), -3_m / (2 * M_PI), 1_m))
+              .getNorm()
+              .magnitude() == Approx(0).margin(absMargin));
+
+    CHECK((helix.getPosition(7_s) -
+           helix.getPositionFromArclength(helix.getArcLength(0_s, 7_s)))
+              .getNorm()
+              .magnitude() == Approx(0).margin(absMargin));
+
+    /*
+    // we have to consider this, if we need it
+    auto const t = 1234_s;
+    Trajectory<Helix> const base(helix, t);
+    CHECK(helix.getPosition(t).GetCoordinates() == base.GetPosition(1.).GetCoordinates());
+
+    CHECK(base.ArcLength(0_s, 1_s) / 1_m == Approx(5));
+    */
+  }
+}
diff --git a/tests/framework/testInteractionCounter.cpp b/tests/framework/testInteractionCounter.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b9cb8b013bbc94dd883367c6bafd4c41b91ae7f1
--- /dev/null
+++ b/tests/framework/testInteractionCounter.cpp
@@ -0,0 +1,133 @@
+/*
+ * (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/process/InteractionCounter.hpp>
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <SetupTestStack.hpp>
+#include <SetupTestEnvironment.hpp>
+
+#include <catch2/catch.hpp>
+
+#include <numeric>
+
+using namespace corsika;
+
+const std::string refDataDir = std::string(REFDATADIR); // from cmake
+
+struct DummyProcess {
+  template <typename TParticle>
+  GrammageType getInteractionLength([[maybe_unused]] TParticle const& particle) {
+    return 100_g / 1_cm / 1_cm;
+  }
+
+  template <typename TParticle>
+  void doInteraction([[maybe_unused]] TParticle& projectile) {}
+};
+
+TEST_CASE("InteractionCounter", "[process]") {
+
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  logging::set_level(logging::level::info);
+
+  DummyProcess d;
+  InteractionCounter countedProcess(d);
+
+  SECTION("getInteractionLength") {
+    CHECK(countedProcess.getInteractionLength(nullptr) == 100_g / 1_cm / 1_cm);
+  }
+
+  auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Oxygen);
+  [[maybe_unused]] auto& env_dummy = env;
+
+  SECTION("DoInteraction nucleus") {
+    unsigned short constexpr A = 14, Z = 7;
+    auto [stackPtr, secViewPtr] = setup::testing::setup_stack(
+        Code::Nucleus, A, Z, 105_TeV, (setup::Environment::BaseNodeType* const)nodePtr,
+        *csPtr);
+    CHECK(stackPtr->getEntries() == 1);
+    CHECK(secViewPtr->getEntries() == 0);
+
+    countedProcess.doInteraction(*secViewPtr);
+
+    auto const& h = countedProcess.getHistogram().labHist();
+    CHECK(h.at(h.axis(0).index(1'000'070'140), h.axis(1).index(1.05e14)) == 1);
+    CHECK(std::accumulate(h.cbegin(), h.cend(), 0) == 1);
+
+    auto const& h2 = countedProcess.getHistogram().CMSHist();
+    CHECK(h2.at(h2.axis(0).index(1'000'070'140), h2.axis(1).index(1.6e12)) == 1);
+    CHECK(std::accumulate(h2.cbegin(), h2.cend(), 0) == 1);
+
+    countedProcess.getHistogram().saveLab("testInteractionCounter_file1.npz",
+                                          SaveMode::overwrite);
+    countedProcess.getHistogram().saveCMS("testInteractionCounter_file2.npz",
+                                          SaveMode::overwrite);
+  }
+
+  SECTION("DoInteraction Lambda") {
+    auto constexpr code = Code::Lambda0;
+    auto [stackPtr, secViewPtr] = setup::testing::setup_stack(
+        code, 0, 0, 105_TeV, (setup::Environment::BaseNodeType* const)nodePtr, *csPtr);
+    CHECK(stackPtr->getEntries() == 1);
+    CHECK(secViewPtr->getEntries() == 0);
+
+    countedProcess.doInteraction(*secViewPtr);
+
+    auto const& h = countedProcess.getHistogram().labHist();
+    CHECK(h.at(h.axis(0).index(3122), h.axis(1).index(1.05e14)) == 1);
+    CHECK(std::accumulate(h.cbegin(), h.cend(), 0) == 1);
+
+    auto const& h2 = countedProcess.getHistogram().CMSHist();
+    CHECK(h2.at(h2.axis(0).index(3122), h2.axis(1).index(1.6e12)) == 1);
+    CHECK(std::accumulate(h2.cbegin(), h2.cend(), 0) == 1);
+  }
+}
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <fstream>
+
+TEST_CASE("InteractionCounterOutput", "[output validation]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  auto file = GENERATE(as<std::string>{}, "testInteractionCounter_file1",
+                       "testInteractionCounter_file2");
+
+  SECTION(std::string("check saved data, ") + file + ".npz") {
+
+    std::cout << file + ".npz vs " << refDataDir + "/" + file + "_REF.npz" << std::endl;
+
+    // compare to binary reference data
+    std::ifstream file1(file + ".npz");
+    std::ifstream file1ref(refDataDir + "/" + file + "_REF.npz");
+
+    std::istreambuf_iterator<char> begin1(file1);
+    std::istreambuf_iterator<char> begin1ref(file1ref);
+
+    std::istreambuf_iterator<char> end;
+
+    while (begin1 != end && begin1ref != end) {
+      CHECK(*begin1 == *begin1ref);
+      ++begin1;
+      ++begin1ref;
+    }
+    CHECK(begin1 == end);
+    CHECK(begin1ref == end);
+    file1.close();
+    file1ref.close();
+  }
+}
diff --git a/Processes/InteractionCounter/testInteractionCounter_file1_REF.npz b/tests/framework/testInteractionCounter_file1_REF.npz
similarity index 100%
rename from Processes/InteractionCounter/testInteractionCounter_file1_REF.npz
rename to tests/framework/testInteractionCounter_file1_REF.npz
diff --git a/Processes/InteractionCounter/testInteractionCounter_file2_REF.npz b/tests/framework/testInteractionCounter_file2_REF.npz
similarity index 100%
rename from Processes/InteractionCounter/testInteractionCounter_file2_REF.npz
rename to tests/framework/testInteractionCounter_file2_REF.npz
diff --git a/tests/framework/testLogging.cpp b/tests/framework/testLogging.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..b737d7dfa778b0cdf78b235479177e51534311b2
--- /dev/null
+++ b/tests/framework/testLogging.cpp
@@ -0,0 +1,120 @@
+/*
+ * (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.
+ */
+
+#include <corsika/framework/core/Logging.hpp>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+
+TEST_CASE("Logging", "[Logging]") {
+
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  SECTION("top level functions using default corsika logger") {
+    logging::info("(1) This is an info message!");
+    logging::warn("(1) This is a warning message!");
+    logging::debug("(1) This is a debug message!");
+    logging::error("(1) This is an error message!");
+    logging::critical("(1) This is a critical error message!");
+  }
+
+  SECTION("create a specific logger") {
+
+    // create a logger manually
+    auto logger = create_logger("loggerA");
+
+    // set a custom pattern for this logger
+    logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+    // and make sure we can log with this created object
+    logger->info("(2) This is an info message!");
+    logger->warn("(2) This is a warning message!");
+    logger->debug("(2) This is a debug message!");
+    logger->error("(2) This is an error message!");
+    logger->critical("(2) This is a critical error message!");
+
+    // get a reference to the logger using Get
+    auto other = get_logger("loggerA");
+
+    // and make sure we can use this other reference to log
+    other->info("(3) This is an info message!");
+    other->warn("(3) This is a warning message!");
+    other->debug("(3) This is a debug message!");
+    other->error("(3) This is an error message!");
+    other->critical("(3) This is a critical error message!");
+  }
+
+  SECTION("get a new logger") {
+
+    // get a reference to an unknown logger
+    auto logger = get_logger("loggerB");
+
+    // and make sure we can log with this created object
+    logger->info("(4) This is an info message!");
+    logger->warn("(4) This is a warning message!");
+    logger->debug("(4) This is a debug message!");
+    logger->error("(4) This is an error message!");
+    logger->critical("(4) This is a critical error message!");
+  }
+
+  SECTION("test log level") {
+
+    // set the default log level
+    logging::set_default_level(logging::level::critical);
+
+    // and make sure we can log with this created object
+    logging::info("(5) This should NOT be printed!");
+    logging::warn("(5) This should NOT be printed!");
+    logging::debug("(5) This should NOT be printed!");
+    logging::error("(5) This should NOT be printed!");
+    logging::critical("(5) This SHOULD BE printed!!");
+
+    // get a reference to an unknown logger
+    auto logger = get_logger("loggerD");
+
+    // now set the default log level for this logger
+    logger->set_level(logging::level::critical);
+
+    // now try the various logging functions
+    logger->info("(6) This should NOT be printed!");
+    logger->warn("(6) This should NOT be printed!");
+    logger->debug("(6) This should NOT be printed!");
+    logger->error("(6) This should NOT be printed!");
+    logger->critical("(6) This SHOULD BE printed!!");
+
+    // and reset it for the next tests
+    logging::set_default_level(logging::level::debug);
+    logger->set_level(logging::level::critical);
+  }
+
+  SECTION("test macro style logging") {
+
+    // these print with the "corsika" logger
+    CORSIKA_LOG_INFO("(7) test macro style logging");
+    CORSIKA_LOG_DEBUG("(7) test macro style logging");
+    CORSIKA_LOG_ERROR("(7) test macro style logging");
+    CORSIKA_LOG_CRITICAL("(7) test macro style logging");
+
+    // get a reference to an unknown logger
+    auto logger = get_logger("loggerE");
+
+    // add the filename, source information to this logger
+    logging::add_source_info(logger);
+
+    // these print with the "loggerE" logger
+    CORSIKA_LOGGER_INFO(logger, "(8) test macro style logging");
+    CORSIKA_LOGGER_WARN(logger, "(8) test macro style logging");
+
+    // reset the logging pattern
+    logging::reset_pattern(logger);
+
+    // these trace macros should not print file, function, and line
+    CORSIKA_LOGGER_TRACE(logger, "(9) test macro style logging:");
+  }
+}
diff --git a/Framework/ProcessSequence/testNullModel.cc b/tests/framework/testNullModel.cpp
similarity index 53%
rename from Framework/ProcessSequence/testNullModel.cc
rename to tests/framework/testNullModel.cpp
index 33bfda3d01cda939f6205271cb2d00d3e6d8cb7e..0589b1782a85d3385f4bd666a598d655f8f93872 100644
--- a/Framework/ProcessSequence/testNullModel.cc
+++ b/tests/framework/testNullModel.cpp
@@ -6,24 +6,22 @@
  * the license.
  */
 
-#include <catch2/catch.hpp>
-
-#include <corsika/process/NullModel.h>
-
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
+#include <corsika/framework/process/NullModel.hpp>
+#include <corsika/framework/core/Logging.hpp>
 
-#include <corsika/units/PhysicalUnits.h>
-
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
+#include <catch2/catch.hpp>
 
-using namespace corsika::units::si;
-using namespace corsika::process;
 using namespace corsika;
 
+/*
+ * The NullModel can do really nothing, so we can basically test
+ * nothing.
+ */
+
 TEST_CASE("NullModel", "[processes]") {
 
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
   SECTION("interface") { [[maybe_unused]] NullModel model; }
 }
diff --git a/tests/framework/testParticles.cpp b/tests/framework/testParticles.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..dd999ecf8c6be288546fd7b32ec5770b414c39b4
--- /dev/null
+++ b/tests/framework/testParticles.cpp
@@ -0,0 +1,150 @@
+/*
+ * (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.
+ */
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+
+TEST_CASE("ParticleProperties", "[Particles]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  SECTION("Types") {
+    CHECK(Electron::code == Code::Electron);
+    CHECK(Positron::code == Code::Positron);
+    CHECK(Proton::code == Code::Proton);
+    CHECK(Neutron::code == Code::Neutron);
+    CHECK(Gamma::code == Code::Gamma);
+    CHECK(PiPlus::code == Code::PiPlus);
+  }
+
+  SECTION("Masses") {
+    CHECK(Electron::mass / (511_keV) == Approx(1));
+    CHECK(Electron::mass / get_mass(Code::Electron) == 1.);
+
+    CHECK((Proton::mass + Neutron::mass) / constants::nucleonMass == Approx(2));
+  }
+
+  SECTION("Charges") {
+    CHECK(Electron::charge / constants::e == Approx(-1));
+    CHECK(Positron::charge / constants::e == Approx(+1));
+    CHECK(get_charge(Positron::anti_code) / constants::e == Approx(-1));
+  }
+
+  SECTION("Names") {
+    CHECK(Electron::name == "e-");
+    CHECK(get_name(Code::Electron) == "e-");
+    CHECK(PiMinus::name == "pi-");
+    CHECK(Iron::name == "iron");
+  }
+
+  SECTION("PDG") {
+    CHECK(get_PDG(Code::PiPlus) == PDGCode::PiPlus);
+    CHECK(get_PDG(Code::DPlus) == PDGCode::DPlus);
+    CHECK(get_PDG(Code::NuMu) == PDGCode::NuMu);
+    CHECK(get_PDG(Code::NuE) == PDGCode::NuE);
+    CHECK(get_PDG(Code::MuMinus) == PDGCode::MuMinus);
+
+    CHECK(static_cast<int>(get_PDG(Code::PiPlus)) == 211);
+    CHECK(static_cast<int>(get_PDG(Code::DPlus)) == 411);
+    CHECK(static_cast<int>(get_PDG(Code::NuMu)) == 14);
+    CHECK(static_cast<int>(get_PDG(Code::NuEBar)) == -12);
+    CHECK(static_cast<int>(get_PDG(Code::MuMinus)) == 13);
+  }
+
+  SECTION("Conversion PDG -> internal") {
+    CHECK(convert_from_PDG(PDGCode::KStarMinus) == Code::KStarMinus);
+    CHECK(convert_from_PDG(PDGCode::MuPlus) == Code::MuPlus);
+    CHECK(convert_from_PDG(PDGCode::SigmaStarCMinusBar) == Code::SigmaStarCMinusBar);
+  }
+
+  SECTION("Lifetimes") {
+    CHECK(get_lifetime(Code::Electron) ==
+          std::numeric_limits<double>::infinity() * si::second);
+    CHECK(get_lifetime(Code::DPlus) < get_lifetime(Code::Gamma));
+    CHECK(get_lifetime(Code::RhoPlus) / si::second ==
+          (Approx(4.414566727909413e-24).epsilon(1e-3)));
+    CHECK(get_lifetime(Code::SigmaMinusBar) / si::second ==
+          (Approx(8.018880848563575e-11).epsilon(1e-5)));
+    CHECK(get_lifetime(Code::MuPlus) / si::second ==
+          (Approx(2.1970332555864364e-06).epsilon(1e-5)));
+  }
+
+  SECTION("Particle groups: electromagnetic") {
+    CHECK(is_em(Code::Gamma));
+    CHECK(is_em(Code::Electron));
+    CHECK_FALSE(is_em(Code::MuPlus));
+    CHECK_FALSE(is_em(Code::NuE));
+    CHECK_FALSE(is_em(Code::Proton));
+    CHECK_FALSE(is_em(Code::PiPlus));
+    CHECK_FALSE(is_em(Code::Oxygen));
+  }
+
+  SECTION("Particle groups: hadrons") {
+    CHECK_FALSE(is_hadron(Code::Gamma));
+    CHECK_FALSE(is_hadron(Code::Electron));
+    CHECK_FALSE(is_hadron(Code::MuPlus));
+    CHECK_FALSE(is_hadron(Code::NuE));
+    CHECK(is_hadron(Code::Proton));
+    CHECK(is_hadron(Code::PiPlus));
+    CHECK(is_hadron(Code::Oxygen));
+    CHECK(is_hadron(Code::Nucleus));
+  }
+
+  SECTION("Particle groups: muons") {
+    CHECK_FALSE(is_muon(Code::Gamma));
+    CHECK_FALSE(is_muon(Code::Electron));
+    CHECK(is_muon(Code::MuPlus));
+    CHECK(is_muon(Code::MuMinus));
+    CHECK_FALSE(is_muon(Code::NuE));
+    CHECK_FALSE(is_muon(Code::Proton));
+    CHECK_FALSE(is_muon(Code::PiPlus));
+    CHECK_FALSE(is_muon(Code::Oxygen));
+  }
+
+  SECTION("Particle groups: neutrinos") {
+    CHECK_FALSE(is_neutrino(Code::Gamma));
+    CHECK_FALSE(is_neutrino(Code::Electron));
+    CHECK_FALSE(is_neutrino(Code::MuPlus));
+    CHECK_FALSE(is_neutrino(Code::Proton));
+    CHECK_FALSE(is_neutrino(Code::PiPlus));
+    CHECK_FALSE(is_neutrino(Code::Oxygen));
+
+    CHECK(is_neutrino(Code::NuE));
+    CHECK(is_neutrino(Code::NuMu));
+    CHECK(is_neutrino(Code::NuTau));
+    CHECK(is_neutrino(Code::NuEBar));
+    CHECK(is_neutrino(Code::NuMuBar));
+    CHECK(is_neutrino(Code::NuTauBar));
+  }
+
+  SECTION("Nuclei") {
+    CHECK_FALSE(is_nucleus(Code::Gamma));
+    CHECK(is_nucleus(Code::Argon));
+    CHECK_FALSE(is_nucleus(Code::Proton));
+    CHECK(is_nucleus(Code::Hydrogen));
+    CHECK(Argon::is_nucleus);
+    CHECK_FALSE(EtaC::is_nucleus);
+
+    CHECK(get_nucleus_A(Code::Hydrogen) == 1);
+    CHECK(get_nucleus_A(Code::Tritium) == 3);
+    CHECK(Hydrogen::nucleus_Z == 1);
+    CHECK(Tritium::nucleus_A == 3);
+
+    // Nucleus is a generic object, it has no specific properties
+    CHECK_THROWS(get_nucleus_Z(Code::Nucleus));
+    CHECK_THROWS(get_nucleus_A(Code::Nucleus));
+    CHECK_THROWS(get_mass(Code::Nucleus));
+    CHECK_THROWS(get_charge(Code::Nucleus));
+  }
+}
diff --git a/Framework/ProcessSequence/testProcessSequence.cc b/tests/framework/testProcessSequence.cpp
similarity index 64%
rename from Framework/ProcessSequence/testProcessSequence.cc
rename to tests/framework/testProcessSequence.cpp
index 875cbc5821fe5e67da0ece3977f13446f2a0659a..ac80847cabbd24913175b44a6a78e62af7e9db2c 100644
--- a/Framework/ProcessSequence/testProcessSequence.cc
+++ b/tests/framework/testProcessSequence.cpp
@@ -1,11 +1,15 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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.
  */
 
+#include <corsika/framework/process/ProcessSequence.hpp>
+#include <corsika/framework/process/SwitchProcessSequence.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
 #include <catch2/catch.hpp>
 
 #include <array>
@@ -13,13 +17,7 @@
 #include <iostream>
 #include <typeinfo>
 
-#include <corsika/process/ProcessSequence.h>
-#include <corsika/process/SwitchProcessSequence.h>
-#include <corsika/units/PhysicalUnits.h>
-
 using namespace corsika;
-using namespace corsika::units::si;
-using namespace corsika::process;
 using namespace std;
 
 static const int nData = 10;
@@ -32,161 +30,162 @@ int checkSec = 0;      // use this as a bit field
 int checkCont = 0;     // use this as a bit field
 
 class ContinuousProcess1 : public ContinuousProcess<ContinuousProcess1> {
-  int fV = 0;
-
 public:
   ContinuousProcess1(const int v)
-      : fV(v) {
+      : v_(v) {
 
-    cout << "globalCount: " << globalCount << ", fV: " << fV << std::endl;
+    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
     globalCount++;
   }
 
   template <typename D, typename T>
-  inline EProcessReturn DoContinuous(D& d, T&) const {
+  inline ProcessReturn doContinuous(D& d, T&) const {
     cout << "ContinuousProcess1::DoContinuous" << endl;
     checkCont |= 1;
     for (int i = 0; i < nData; ++i) d.data_[i] += 0.933;
-    return EProcessReturn::eOk;
+    return ProcessReturn::Ok;
   }
+
+private:
+  int v_ = 0;
 };
 
 class ContinuousProcess2 : public ContinuousProcess<ContinuousProcess2> {
-  int fV = 0;
-
 public:
   ContinuousProcess2(const int v)
-      : fV(v) {
-    cout << "globalCount: " << globalCount << ", fV: " << fV << std::endl;
+      : v_(v) {
+    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
     globalCount++;
   }
 
   template <typename D, typename T>
-  inline EProcessReturn DoContinuous(D& d, T&) const {
+  inline ProcessReturn doContinuous(D& d, T&) const {
     cout << "ContinuousProcess2::DoContinuous" << endl;
     checkCont |= 2;
     for (int i = 0; i < nData; ++i) d.data_[i] += 0.111;
-    return EProcessReturn::eOk;
+    return ProcessReturn::Ok;
   }
+
+private:
+  int v_ = 0;
 };
 
 class ContinuousProcess3 : public ContinuousProcess<ContinuousProcess3> {
-  int fV = 0;
-
 public:
   ContinuousProcess3(const int v)
-      : fV(v) {
-    cout << "globalCount: " << globalCount << ", fV: " << fV << std::endl;
+      : v_(v) {
+    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
     globalCount++;
   }
 
   template <typename D, typename T>
-  inline EProcessReturn DoContinuous(D& d, T&) const {
+  inline ProcessReturn doContinuous(D& d, T&) const {
     cout << "ContinuousProcess3::DoContinuous" << endl;
     checkCont |= 4;
     for (int i = 0; i < nData; ++i) d.data_[i] += 0.333;
-    return EProcessReturn::eOk;
+    return ProcessReturn::Ok;
   }
+
+private:
+  int v_ = 0;
 };
 
 class Process1 : public InteractionProcess<Process1> {
 public:
   Process1(const int v)
-      : fV(v) {
-    cout << "globalCount: " << globalCount << ", fV: " << fV << std::endl;
+      : v_(v) {
+    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
     globalCount++;
   }
 
   template <typename TView>
-  inline EProcessReturn DoInteraction(TView& v) const {
+  inline void doInteraction(TView& v) const {
     checkInteract |= 1;
     for (int i = 0; i < nData; ++i) v.parent().data_[i] += 1 + i;
-    return EProcessReturn::eOk;
   }
 
   template <typename TParticle>
-  corsika::units::si::GrammageType GetInteractionLength(TParticle&) const {
+  GrammageType getInteractionLength(TParticle&) const {
     return 10_g / square(1_cm);
   }
 
 private:
-  int fV;
+  int v_;
 };
 
 class Process2 : public InteractionProcess<Process2> {
-  int fV = 0;
-
 public:
   Process2(const int v)
-      : fV(v) {
-    cout << "globalCount: " << globalCount << ", fV: " << fV << std::endl;
+      : v_(v) {
+    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
     globalCount++;
   }
 
   template <typename TView>
-  inline EProcessReturn DoInteraction(TView& v) const {
+  inline void doInteraction(TView& v) const {
     checkInteract |= 2;
     for (int i = 0; i < nData; ++i) v.parent().data_[i] /= 1.1;
-    cout << "Process2::DoInteraction" << endl;
-    return EProcessReturn::eOk;
+    cout << "Process2::doInteraction" << endl;
   }
   template <typename Particle>
-  GrammageType GetInteractionLength(Particle&) const {
+  GrammageType getInteractionLength(Particle&) const {
     cout << "Process2::GetInteractionLength" << endl;
     return 20_g / (1_cm * 1_cm);
   }
+
+private:
+  int v_ = 0;
 };
 
 class Process3 : public InteractionProcess<Process3> {
-  int fV = 0;
-
 public:
   Process3(const int v)
-      : fV(v) {
-    cout << "globalCount: " << globalCount << ", fV: " << fV << std::endl;
+      : v_(v) {
+    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
     globalCount++;
   }
 
   template <typename TView>
-  inline EProcessReturn DoInteraction(TView& v) const {
+  inline void doInteraction(TView& v) const {
     checkInteract |= 4;
     for (int i = 0; i < nData; ++i) v.parent().data_[i] *= 1.01;
-    cout << "Process3::DoInteraction" << endl;
-    return EProcessReturn::eOk;
+    cout << "Process3::doInteraction" << endl;
   }
   template <typename Particle>
-  GrammageType GetInteractionLength(Particle&) const {
+  GrammageType getInteractionLength(Particle&) const {
     cout << "Process3::GetInteractionLength" << endl;
     return 30_g / (1_cm * 1_cm);
   }
+
+private:
+  int v_ = 0;
 };
 
 class Process4 : public BaseProcess<Process4> {
-  int fV = 0;
-
 public:
   Process4(const int v)
-      : fV(v) {
-    cout << "globalCount: " << globalCount << ", fV: " << fV << std::endl;
+      : v_(v) {
+    cout << "globalCount: " << globalCount << ", v_: " << v_ << std::endl;
     globalCount++;
   }
 
   template <typename D, typename T>
-  inline EProcessReturn DoContinuous(D& d, T&) const {
-    std::cout << "Base::DoContinuous" << std::endl;
+  inline ProcessReturn doContinuous(D& d, T&) const {
+    std::cout << "Base::doContinuous" << std::endl;
     checkCont |= 8;
     for (int i = 0; i < nData; ++i) { d.data_[i] /= 1.2; }
-    return EProcessReturn::eOk;
+    return ProcessReturn::Ok;
   }
   template <typename TView>
-  EProcessReturn DoInteraction(TView&) const {
+  void doInteraction(TView&) const {
     checkInteract |= 8;
-    return EProcessReturn::eOk;
   }
+
+private:
+  int v_ = 0;
 };
 
 class Decay1 : public DecayProcess<Decay1> {
-
 public:
   Decay1(const int) {
     cout << "Decay1()" << endl;
@@ -194,18 +193,16 @@ public:
   }
 
   template <typename Particle>
-  TimeType GetLifetime(Particle&) const {
+  TimeType getLifetime(Particle&) const {
     return 1_s;
   }
   template <typename TView>
-  EProcessReturn DoDecay(TView&) const {
+  void doDecay(TView&) const {
     checkDecay |= 1;
-    return EProcessReturn::eOk;
   }
 };
 
 class Decay2 : public DecayProcess<Decay2> {
-
 public:
   Decay2(const int) {
     cout << "Decay2()" << endl;
@@ -213,28 +210,28 @@ public:
   }
 
   template <typename Particle>
-  TimeType GetLifetime(Particle&) const {
+  TimeType getLifetime(Particle&) const {
     return 2_s;
   }
   template <typename TView>
-  EProcessReturn DoDecay(TView&) const {
+  void doDecay(TView&) const {
     checkDecay |= 2;
-    return EProcessReturn::eOk;
   }
 };
 
 class Stack1 : public StackProcess<Stack1> {
-  int fCount = 0;
-
 public:
   Stack1(const int n)
       : StackProcess(n) {}
   template <typename TStack>
-  EProcessReturn DoStack(TStack&) {
-    fCount++;
-    return EProcessReturn::eOk;
+  ProcessReturn doStack(TStack&) {
+    count_++;
+    return ProcessReturn::Ok;
   }
-  int GetCount() const { return fCount; }
+  int getCount() const { return count_; }
+
+private:
+  int count_ = 0;
 };
 
 struct DummyStack {};
@@ -252,6 +249,9 @@ struct DummyView {
 
 TEST_CASE("Process Sequence", "[Process Sequence]") {
 
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
   SECTION("Check construction") {
     globalCount = 0;
     Process1 m1(0);
@@ -263,16 +263,16 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
     Process4 m4(3);
     CHECK(globalCount == 4);
 
-    auto sequence1 = process::sequence(m1, m2, m3, m4);
+    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);
 
-    auto sequence2 = process::sequence(m1, m2, m3);
+    auto sequence2 = make_sequence(m1, m2, m3);
     CHECK(is_process_sequence_v<decltype(sequence2)> == true);
 
-    auto sequence3 = process::sequence(m4);
+    auto sequence3 = make_sequence(m4);
     CHECK(is_process_sequence_v<decltype(sequence3)> == true);
   }
 
@@ -284,9 +284,9 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
 
     DummyData particle;
 
-    auto sequence2 = sequence(cp1, m2, m3);
-    GrammageType const tot = sequence2.GetInteractionLength(particle);
-    InverseGrammageType const tot_inv = sequence2.GetInverseInteractionLength(particle);
+    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;
 
     CHECK(tot / 1_g * square(1_cm) == 12);
@@ -303,9 +303,9 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
 
     DummyData particle;
 
-    auto sequence2 = sequence(cp1, m2, m3, d3);
-    TimeType const tot = sequence2.GetLifetime(particle);
-    InverseTimeType const tot_inv = sequence2.GetInverseLifetime(particle);
+    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;
 
     CHECK(tot / 1_s == 1);
@@ -320,7 +320,7 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
     Process2 m2(2);            //  /= 1.1
     Process3 m3(3);            //  *= 1.01
 
-    auto sequence2 = sequence(cp1, m2, m3, cp2);
+    auto sequence2 = make_sequence(cp1, m2, m3, cp2);
 
     DummyData particle;
     DummyTrajectory track;
@@ -336,7 +336,7 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
     cout << "Running loop with n=" << nLoop << endl;
     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);
     }
     for (int i = 0; i < nData; i++) {
       cout << "data_[" << i << "]=" << particle.data_[i] << endl;
@@ -351,20 +351,20 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
     Stack1 s1(1);
     Stack1 s2(2);
 
-    auto sequence = process::sequence(s1, s2);
+    auto sequence1 = make_sequence(s1, s2);
 
     DummyStack stack;
 
     const int nLoop = 20;
-    for (int i = 0; i < nLoop; ++i) { sequence.DoStack(stack); }
+    for (int i = 0; i < nLoop; ++i) { sequence1.doStack(stack); }
 
-    CHECK(s1.GetCount() == 20);
-    CHECK(s2.GetCount() == 10);
+    CHECK(s1.getCount() == 20);
+    CHECK(s2.getCount() == 10);
 
     ContinuousProcess2 cp2(1); // += 0.111
     Process2 m2(2);            //  /= 1.1
-    auto sequence2 = process::sequence(cp2, m2);
-    auto sequence3 = process::sequence(cp2, m2, s1);
+    auto sequence2 = make_sequence(cp2, m2);
+    auto sequence3 = make_sequence(cp2, m2, s1);
 
     CHECK(is_process_sequence_v<decltype(sequence2)> == true);
     CHECK(is_process_sequence_v<decltype(sequence3)> == true);
@@ -375,35 +375,37 @@ TEST_CASE("Process Sequence", "[Process Sequence]") {
 
 TEST_CASE("Switch Process Sequence", "[Process Sequence]") {
 
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
   SECTION("Check construction") {
 
     struct TestSelect {
-      corsika::process::SwitchResult operator()(const DummyData& p) const {
+      SwitchResult operator()(const DummyData& p) const {
         std::cout << "TestSelect data=" << p.data_[0] << std::endl;
-        if (p.data_[0] > 0) return corsika::process::SwitchResult::First;
-        return corsika::process::SwitchResult::Second;
+        if (p.data_[0] > 0) return SwitchResult::First;
+        return SwitchResult::Second;
       }
     };
-    TestSelect select;
+    TestSelect select1;
 
-    auto sequence1 = process::sequence(Process1(0), ContinuousProcess2(0), Decay1(0));
-    auto sequence2 = process::sequence(ContinuousProcess3(0), Process2(0), Decay2(0));
+    auto sequence1 = make_sequence(Process1(0), ContinuousProcess2(0), Decay1(0));
+    auto sequence2 = make_sequence(ContinuousProcess3(0), Process2(0), Decay2(0));
 
-    auto sequence =
-        process::sequence(ContinuousProcess1(0), Process3(0),
-                          SwitchProcessSequence(sequence1, sequence2, select));
+    auto sequence3 = make_sequence(ContinuousProcess1(0), Process3(0),
+                                   SwitchProcessSequence(sequence1, sequence2, select1));
 
-    auto sequence_alt = process::sequence(
+    auto sequence_alt = make_sequence(
         ContinuousProcess1(0), Process3(0),
-        process::select(process::sequence(Process1(0), ContinuousProcess2(0), Decay1(0)),
-                        process::sequence(ContinuousProcess3(0), Process2(0), Decay2(0)),
-                        select));
+        make_select(make_sequence(Process1(0), ContinuousProcess2(0), Decay1(0)),
+                    make_sequence(ContinuousProcess3(0), Process2(0), Decay2(0)),
+                    select1));
 
     // check that same process sequence can be build in different ways
-    CHECK(typeid(sequence) == typeid(sequence_alt));
-    CHECK(is_process_sequence_v<decltype(sequence)> == true);
+    CHECK(typeid(sequence3) == typeid(sequence_alt));
+    CHECK(is_process_sequence_v<decltype(sequence3)> == true);
     CHECK(is_process_sequence_v<decltype(
-              SwitchProcessSequence(sequence1, sequence2, select))> == true);
+              SwitchProcessSequence(sequence1, sequence2, select1))> == true);
 
     DummyData particle;
     DummyTrajectory track;
@@ -414,7 +416,7 @@ TEST_CASE("Switch Process Sequence", "[Process Sequence]") {
     checkSec = 0;
     checkCont = 0;
     particle.data_[0] = 100; // data positive
-    sequence.DoContinuous(particle, track);
+    sequence3.doContinuous(particle, track);
     CHECK(checkInteract == 0);
     CHECK(checkDecay == 0);
     CHECK(checkCont == 0b011);
@@ -425,30 +427,30 @@ TEST_CASE("Switch Process Sequence", "[Process Sequence]") {
     checkSec = 0;
     checkCont = 0;
     particle.data_[0] = -100; // data negative
-    sequence_alt.DoContinuous(particle, track);
+    sequence_alt.doContinuous(particle, track);
     CHECK(checkInteract == 0);
     CHECK(checkDecay == 0);
     CHECK(checkCont == 0b101);
     CHECK(checkSec == 0);
 
     // 1/(30g/cm2) is Process3
-    corsika::units::si::InverseGrammageType lambda_select = .9 / 30. * square(1_cm) / 1_g;
-    corsika::units::si::InverseTimeType time_select = 0.1 / second;
+    InverseGrammageType lambda_select = .9 / 30. * square(1_cm) / 1_g;
+    InverseTimeType time_select = 0.1 / second;
 
     checkDecay = 0;
     checkInteract = 0;
     checkSec = 0;
     checkCont = 0;
     particle.data_[0] = 100; // data positive
-    sequence.SelectInteraction(view, lambda_select);
-    sequence.SelectDecay(view, time_select);
+    sequence3.selectInteraction(view, lambda_select);
+    sequence3.selectDecay(view, time_select);
     CHECK(checkInteract == 0b100); // this is Process3
     CHECK(checkDecay == 0b001);    // this is Decay1
     CHECK(checkCont == 0);
     CHECK(checkSec == 0);
     lambda_select = 1.01 / 30. * square(1_cm) / 1_g;
     checkInteract = 0;
-    sequence.SelectInteraction(view, lambda_select);
+    sequence3.selectInteraction(view, lambda_select);
     CHECK(checkInteract == 0b001); // this is Process1
 
     checkDecay = 0;
@@ -456,8 +458,8 @@ TEST_CASE("Switch Process Sequence", "[Process Sequence]") {
     checkSec = 0;
     checkCont = 0;
     particle.data_[0] = -100; // data negative
-    sequence.SelectInteraction(view, lambda_select);
-    sequence.SelectDecay(view, time_select);
+    sequence3.selectInteraction(view, lambda_select);
+    sequence3.selectDecay(view, time_select);
     CHECK(checkInteract == 0b010); // this is Process2
     CHECK(checkDecay == 0b010);    // this is Decay2
     CHECK(checkCont == 0);
@@ -468,9 +470,9 @@ TEST_CASE("Switch Process Sequence", "[Process Sequence]") {
     checkSec = 0;
     checkCont = 0;
     particle.data_[0] = -100; // data negative
-    sequence.DoSecondaries(view);
+    sequence3.doSecondaries(view);
     Stack1 stack(0);
-    sequence.DoStack(stack);
+    sequence3.doStack(stack);
     CHECK(checkInteract == 0);
     CHECK(checkDecay == 0);
     CHECK(checkCont == 0);
diff --git a/Framework/Random/testRandom.cc b/tests/framework/testRandom.cpp
similarity index 62%
rename from Framework/Random/testRandom.cc
rename to tests/framework/testRandom.cpp
index d9053f74d63b49a39e24470c052b97970f573b02..5774575e6844754289cdcd2130313d3b03d95359 100644
--- a/Framework/Random/testRandom.cc
+++ b/tests/framework/testRandom.cpp
@@ -1,5 +1,5 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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
@@ -8,40 +8,40 @@
 
 #include <catch2/catch.hpp>
 
-#include <corsika/random/ExponentialDistribution.h>
-#include <corsika/random/RNGManager.h>
-#include <corsika/random/UniformRealDistribution.h>
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/random/ExponentialDistribution.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/random/UniformRealDistribution.hpp>
 #include <iostream>
 #include <limits>
 #include <random>
 
-using namespace corsika::random;
+using namespace corsika;
 
 SCENARIO("random-number streams can be registered and retrieved") {
   GIVEN("a RNGManager") {
-    RNGManager& rngManager = RNGManager::GetInstance();
+    RNGManager& rngManager = RNGManager::getInstance();
 
     WHEN("the sequence name is not registered") {
-      REQUIRE(rngManager.IsRegistered("stream_A") == false);
+      CHECK(rngManager.isRegistered("stream_A") == false);
 
       THEN("a sequence is registered by name") {
-        rngManager.RegisterRandomStream("stream_A");
+        rngManager.registerRandomStream("stream_A");
 
         THEN("the sequence can be retrieved") {
-          REQUIRE_NOTHROW(rngManager.GetRandomStream("stream_A"));
+          CHECK_NOTHROW(rngManager.getRandomStream("stream_A"));
 
           THEN("we can check that the sequence exists") {
-            REQUIRE_NOTHROW(rngManager.GetRandomStream("stream_A"));
+            CHECK_NOTHROW(rngManager.getRandomStream("stream_A"));
 
             THEN("an unknown sequence cannot be retrieved") {
-              REQUIRE(rngManager.IsRegistered("stream_A") == true);
+              CHECK(rngManager.isRegistered("stream_A") == true);
 
               THEN("an unknown sequence cannot be retrieved") {
-                REQUIRE_THROWS(rngManager.GetRandomStream("stream_UNKNOWN"));
+                CHECK_THROWS(rngManager.getRandomStream("stream_UNKNOWN"));
 
                 THEN("an unknown sequence is not registered") {
-                  REQUIRE(rngManager.IsRegistered("stream_UNKNOWN") == false);
+                  CHECK(rngManager.isRegistered("stream_UNKNOWN") == false);
                 }
               }
             }
@@ -53,13 +53,16 @@ SCENARIO("random-number streams can be registered and retrieved") {
 }
 
 TEST_CASE("UniformRealDistribution") {
-  using namespace corsika::units::si;
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
   std::mt19937 rng;
 
-  corsika::random::UniformRealDistribution<LengthType> dist(1_m, 2_m);
+  corsika::UniformRealDistribution<LengthType> dist(1_m, 2_m);
 
   SECTION("range") {
-    corsika::random::UniformRealDistribution<LengthType> dist(1_m, 2_m);
+    corsika::UniformRealDistribution<LengthType> dist(1_m, 2_m);
 
     LengthType min =
         +1_m * std::numeric_limits<typename LengthType::value_type>::infinity();
@@ -77,7 +80,7 @@ TEST_CASE("UniformRealDistribution") {
   }
 
   SECTION("range") {
-    corsika::random::UniformRealDistribution<LengthType> dist(18_cm);
+    corsika::UniformRealDistribution<LengthType> dist(18_cm);
 
     LengthType min =
         +1_m * std::numeric_limits<typename LengthType::value_type>::infinity();
@@ -96,12 +99,15 @@ TEST_CASE("UniformRealDistribution") {
 }
 
 TEST_CASE("ExponentialDistribution") {
-  using namespace corsika::units::si;
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
   std::mt19937 rng;
 
   auto const beta = 15_m;
 
-  corsika::random::ExponentialDistribution dist(beta);
+  corsika::ExponentialDistribution dist(beta);
 
   SECTION("mean") {
     std::remove_const<decltype(beta)>::type mean = beta * 0;
diff --git a/Framework/Utilities/testSaveBoostHistogram.cc b/tests/framework/testSaveBoostHistogram.cpp
similarity index 78%
rename from Framework/Utilities/testSaveBoostHistogram.cc
rename to tests/framework/testSaveBoostHistogram.cpp
index aa05a1947a9c989724dee5244f41859fa634f59f..91bac8b82120b0ce2595a0f8d91a5f503706403c 100644
--- a/Framework/Utilities/testSaveBoostHistogram.cc
+++ b/tests/framework/testSaveBoostHistogram.cpp
@@ -7,13 +7,18 @@
  */
 
 #include <catch2/catch.hpp>
-#include <corsika/utl/SaveBoostHistogram.hpp>
+#include <corsika/framework/utility/SaveBoostHistogram.hpp>
+#include <corsika/framework/core/Logging.hpp>
 
 #include <random>
 
 namespace bh = boost::histogram;
 
 TEST_CASE("SaveHistogram") {
+
+  corsika::corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  corsika::logging::set_level(corsika::logging::level::info);
+
   std::mt19937 rng;
   std::normal_distribution n1{2., 6.};
   std::exponential_distribution e{1.};
@@ -35,5 +40,5 @@ TEST_CASE("SaveHistogram") {
     h(a, b, c, d);
   }
 
-  corsika::utl::save_hist(h, "hist.npz");
+  corsika::save_hist(h, "hist.npz");
 }
diff --git a/Framework/StackInterface/testSecondaryView.cc b/tests/framework/testSecondaryView.cpp
similarity index 57%
rename from Framework/StackInterface/testSecondaryView.cc
rename to tests/framework/testSecondaryView.cpp
index 9cd1e46667e08696e81bc44d25d79b6a634500d3..87cfd91c0c74cb3b9c38cc8bda31af7a96e52c8c 100644
--- a/Framework/StackInterface/testSecondaryView.cc
+++ b/tests/framework/testSecondaryView.cpp
@@ -8,10 +8,11 @@
 
 #define protected public // to also test the internal state of objects
 
-#include <corsika/stack/SecondaryView.h>
-#include <corsika/stack/Stack.h>
+#include <corsika/framework/stack/SecondaryView.hpp>
+#include <corsika/framework/stack/Stack.hpp>
+#include <corsika/framework/core/Logging.hpp>
 
-#include <testTestStack.h> // for testing: simple stack. This is a
+#include <testTestStack.hpp> // for testing: simple stack. This is a
 // test-build, and inluce file is obtained from CMAKE_CURRENT_SOURCE_DIR
 
 #include <iomanip>
@@ -20,7 +21,6 @@
 #include <catch2/catch.hpp>
 
 using namespace corsika;
-using namespace corsika::stack;
 using namespace std;
 
 typedef Stack<TestStackData, TestParticleInterface> StackTest;
@@ -39,69 +39,70 @@ using StackTestView = SecondaryView<TestStackData, TestParticleInterface>;
 using StackTestView = MakeView<StackTest>::type;
 #endif
 
-using Particle = typename StackTest::ParticleType;
+using Particle = typename StackTest::particle_type;
 
 TEST_CASE("SecondaryStack", "[stack]") {
 
-  logging::SetLevel(logging::level::debug);
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
 
   // helper function for sum over stack data
   auto sum = [](const StackTest& stack) {
     double v = 0;
-    for (const auto& p : stack) { v += p.GetData(); }
+    for (const auto& p : stack) { v += p.getData(); }
     return v;
   };
 
   auto sumView = [](const StackTestView& stack) {
     double value = 0;
-    for (const auto& p : stack) { value += p.GetData(); }
+    for (const auto& p : stack) { value += p.getData(); }
     return value;
   };
 
   SECTION("secondary view") {
     StackTest stack;
     CHECK(stack.getSize() == 0);
-    CHECK(stack.IsEmpty());
+    CHECK(stack.isEmpty());
 
-    stack.AddParticle(std::tuple{9.9});
-    stack.AddParticle(std::tuple{8.8});
+    stack.addParticle(std::tuple{9.9});
+    stack.addParticle(std::tuple{8.8});
     const double sumS = 9.9 + 8.8; // helper, see below
     CHECK(stack.getSize() == 2);
     CHECK(stack.getEntries() == 2);
-    CHECK(!stack.IsEmpty());
+    CHECK(!stack.isEmpty());
 
-    auto particle = stack.GetNextParticle();
+    auto particle = stack.getNextParticle();
 
     StackTestView view(particle);
     CHECK(view.getSize() == 0);
     CHECK(view.getEntries() == 0);
-    CHECK(view.IsEmpty());
+    CHECK(view.isEmpty());
 
     {
-      auto proj = view.GetProjectile();
-      CHECK(proj.GetData() == particle.GetData());
-      proj.AddSecondary(std::tuple{4.4});
+      auto proj = view.getProjectile();
+      CHECK(proj.getData() == particle.getData());
+      proj.addSecondary(std::tuple{4.4});
     }
     CHECK(view.getSize() == 1);
     CHECK(view.getEntries() == 1);
-    CHECK(!view.IsEmpty());
+    CHECK(!view.isEmpty());
     CHECK(stack.getSize() == 3);
     CHECK(stack.getEntries() == 3);
-    CHECK(!stack.IsEmpty());
+    CHECK(!stack.isEmpty());
 
-    view.AddSecondary(std::tuple{4.5});
-    view.AddSecondary(std::tuple{4.6});
+    view.addSecondary(std::tuple{4.5});
+    view.addSecondary(std::tuple{4.6});
     CHECK(view.getSize() == 3);
     CHECK(view.getEntries() == 3);
-    CHECK(!view.IsEmpty());
+    CHECK(!view.isEmpty());
     CHECK(stack.getSize() == 5);
     CHECK(stack.getEntries() == 5);
-    CHECK(!stack.IsEmpty());
+    CHECK(!stack.isEmpty());
 
     CHECK(sum(stack) == sumS + 4.4 + 4.5 + 4.6);
     CHECK(sumView(view) == 4.4 + 4.5 + 4.6);
 
-    view.last().Delete();
+    view.last().erase();
     CHECK(view.getSize() == 3);
     CHECK(view.getEntries() == 2);
     CHECK(stack.getSize() == 5);
@@ -110,22 +111,22 @@ TEST_CASE("SecondaryStack", "[stack]") {
     CHECK(sum(stack) == sumS + 4.4 + 4.5);
     CHECK(sumView(view) == 4.4 + 4.5);
 
-    auto pDel = view.GetNextParticle();
-    view.Delete(pDel);
+    auto pDel = view.getNextParticle();
+    view.erase(pDel);
     CHECK(view.getSize() == 2);
     CHECK(stack.getSize() == 4);
 
-    CHECK(sum(stack) == sumS + 4.4 + 4.5 - pDel.GetData());
-    CHECK(sumView(view) == 4.4 + 4.5 - pDel.GetData());
+    CHECK(sum(stack) == sumS + 4.4 + 4.5 - pDel.getData());
+    CHECK(sumView(view) == 4.4 + 4.5 - pDel.getData());
 
-    view.Delete(view.GetNextParticle());
+    view.erase(view.getNextParticle());
     CHECK(sum(stack) == sumS);
     CHECK(sumView(view) == 0);
-    CHECK(view.IsEmpty());
+    CHECK(view.isEmpty());
 
     {
-      auto proj = view.GetProjectile();
-      CHECK(proj.GetData() == particle.GetData());
+      auto proj = view.getProjectile();
+      CHECK(proj.getData() == particle.getData());
       CHECK(particle == view.parent());
     }
 
@@ -138,34 +139,34 @@ TEST_CASE("SecondaryStack", "[stack]") {
   SECTION("secondary view, construct from ParticleType") {
     StackTest stack;
     CHECK(stack.getSize() == 0);
-    stack.AddParticle(std::tuple{9.9});
-    stack.AddParticle(std::tuple{8.8});
+    stack.addParticle(std::tuple{9.9});
+    stack.addParticle(std::tuple{8.8});
 
-    auto iterator = stack.GetNextParticle();
-    typename StackTest::ParticleType& particle = iterator; // as in corsika::Cascade
+    auto iterator = stack.getNextParticle();
+    typename StackTest::particle_type& particle = iterator; // as in corsika::Cascade
 
     StackTestView view(particle);
     CHECK(view.getSize() == 0);
 
-    view.AddSecondary(std::tuple{4.4});
+    view.addSecondary(std::tuple{4.4});
 
     CHECK(view.getSize() == 1);
   }
 
   SECTION("deletion") {
     StackTest stack;
-    stack.AddParticle(std::tuple{-99.});
-    stack.AddParticle(std::tuple{0.});
+    stack.addParticle(std::tuple{-99.});
+    stack.addParticle(std::tuple{0.});
 
     {
-      auto particle = stack.GetNextParticle();
+      auto particle = stack.getNextParticle();
       StackTestView view(particle);
 
-      auto proj = view.GetProjectile();
-      proj.AddSecondary(std::tuple{-2.});
-      proj.AddSecondary(std::tuple{-1.});
-      proj.AddSecondary(std::tuple{1.});
-      proj.AddSecondary(std::tuple{2.});
+      auto proj = view.getProjectile();
+      proj.addSecondary(std::tuple{-2.});
+      proj.addSecondary(std::tuple{-1.});
+      proj.addSecondary(std::tuple{1.});
+      proj.addSecondary(std::tuple{2.});
 
       CHECK(stack.getSize() == 6); // -99, 0, -2, -1, 1, 2
       CHECK(view.getSize() == 4);  // -2, -1, 1, 2
@@ -173,8 +174,8 @@ TEST_CASE("SecondaryStack", "[stack]") {
       // now delete all negative entries, i.e. -1 and -2
       auto p = view.begin();
       while (p != view.end()) {
-        auto data = p.GetData();
-        if (data < 0) { p.Delete(); }
+        auto data = p.getData();
+        if (data < 0) { p.erase(); }
         ++p;
       }
       CHECK(stack.getSize() == 6);
@@ -186,21 +187,21 @@ TEST_CASE("SecondaryStack", "[stack]") {
     // repeat
 
     {
-      auto particle = stack.GetNextParticle();
+      auto particle = stack.getNextParticle();
       StackTestView view(particle);
 
       // put -2,...,+2 on stack
-      auto proj = view.GetProjectile();
-      proj.AddSecondary(std::tuple{-2.});
-      proj.AddSecondary(std::tuple{-1.});
-      proj.AddSecondary(std::tuple{1.});
-      proj.AddSecondary(std::tuple{2.});
+      auto proj = view.getProjectile();
+      proj.addSecondary(std::tuple{-2.});
+      proj.addSecondary(std::tuple{-1.});
+      proj.addSecondary(std::tuple{1.});
+      proj.addSecondary(std::tuple{2.});
       // stack should contain -99, 0, 2, 1, [-2, -1, 1, 2]
 
       auto p = view.begin();
       while (p != view.end()) {
-        auto data = p.GetData();
-        if (data < 0) { p.Delete(); }
+        auto data = p.getData();
+        if (data < 0) { p.erase(); }
         ++p;
       }
 
@@ -214,43 +215,43 @@ TEST_CASE("SecondaryStack", "[stack]") {
 
   SECTION("swap particle") {
     StackTest stack;
-    stack.AddParticle(std::tuple{-99.});
+    stack.addParticle(std::tuple{-99.});
 
     StackTestView view(stack.first());
-    view.AddSecondary(std::tuple{-2.});
-    view.AddSecondary(std::tuple{-1.});
-    view.AddSecondary(std::tuple{1.});
+    view.addSecondary(std::tuple{-2.});
+    view.addSecondary(std::tuple{-1.});
+    view.addSecondary(std::tuple{1.});
 
     auto p1 = view.begin();
     auto p2 = p1 + 1;
 
-    CHECK(p1.GetData() == -2.);
-    CHECK(p2.GetData() == -1.);
+    CHECK(p1.getData() == -2.);
+    CHECK(p2.getData() == -1.);
 
-    view.Swap(p1, p2);
+    view.swap(p1, p2);
 
-    CHECK(p1.GetData() == -1);
-    CHECK(p2.GetData() == -2);
+    CHECK(p1.getData() == -1);
+    CHECK(p2.getData() == -2);
   }
 
   SECTION("copy particle") {
     StackTest stack;
-    stack.AddParticle(std::tuple{-99.});
+    stack.addParticle(std::tuple{-99.});
 
     StackTestView view(stack.first());
-    view.AddSecondary(std::tuple{-2.});
-    view.AddSecondary(std::tuple{-1.});
-    view.AddSecondary(std::tuple{1.});
+    view.addSecondary(std::tuple{-2.});
+    view.addSecondary(std::tuple{-1.});
+    view.addSecondary(std::tuple{1.});
 
     auto p1 = view.begin();
     auto p2 = p1 + 1;
 
-    CHECK(p1.GetData() == -2.);
-    CHECK(p2.GetData() == -1.);
+    CHECK(p1.getData() == -2.);
+    CHECK(p2.getData() == -1.);
 
-    view.Copy(p1, p2);
+    view.copy(p1, p2);
 
-    CHECK(p1.GetData() == -2);
-    CHECK(p2.GetData() == -2);
+    CHECK(p1.getData() == -2);
+    CHECK(p2.getData() == -2);
   }
 }
diff --git a/Framework/StackInterface/testStackInterface.cc b/tests/framework/testStackInterface.cpp
similarity index 58%
rename from Framework/StackInterface/testStackInterface.cc
rename to tests/framework/testStackInterface.cpp
index af1f217ad361f8b4fa41c62da12b156a205e1c76..b37dc495d7b03850ce135dd87942f8227531963d 100644
--- a/Framework/StackInterface/testStackInterface.cc
+++ b/tests/framework/testStackInterface.cpp
@@ -8,11 +8,10 @@
 
 #define protected public // to also test the internal state of objects
 
-#include <corsika/stack/Stack.h>
+#include <corsika/framework/stack/Stack.hpp>
+#include <corsika/framework/core/Logging.hpp>
 
-#include <testTestStack.h> // simple test-stack for testing. This is
-                           // for testing only: include from
-                           // CMAKE_CURRENT_SOURCE_DIR
+#include <testTestStack.hpp> // from tests/common
 
 #include <iomanip>
 #include <tuple>
@@ -21,17 +20,19 @@
 #include <catch2/catch.hpp>
 
 using namespace corsika;
-using namespace corsika::stack;
 using namespace std;
 
 typedef Stack<TestStackData, TestParticleInterface> StackTest;
 
 TEST_CASE("Stack", "[Stack]") {
 
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
   // helper function for sum over stack data
   auto sum = [](const StackTest& stack) {
     double v = 0;
-    for (const auto& p : stack) v += p.GetData();
+    for (const auto& p : stack) v += p.getData();
     return v;
   };
 
@@ -39,15 +40,15 @@ TEST_CASE("Stack", "[Stack]") {
 
     // construct a valid Stack object
     StackTest stack;
-    stack.Clear();
+    stack.clear();
     CHECK(stack.getSize() == 0);
-    CHECK(stack.IsEmpty());                          // stack empty here
-    auto pTest0 = stack.AddParticle(std::tuple{0.}); // [0]
+    CHECK(stack.isEmpty());                          // stack empty here
+    auto pTest0 = stack.addParticle(std::tuple{0.}); // [0]
     CHECK(stack.getSize() == 1);
-    CHECK(!stack.IsEmpty());
-    auto pTest1 = stack.AddParticle(std::tuple{1.}); // [0,1]
+    CHECK(!stack.isEmpty());
+    auto pTest1 = stack.addParticle(std::tuple{1.}); // [0,1]
     CHECK(stack.getSize() == 2);
-    CHECK(pTest1.GetData() == 1.);
+    CHECK(pTest1.getData() == 1.);
     auto pTestAt = stack.at(1); // -> 1
     CHECK(pTestAt == pTest1);
     auto pTestFirst = stack.first(); // -> 0
@@ -63,7 +64,7 @@ TEST_CASE("Stack", "[Stack]") {
   SECTION("write and read") {
 
     StackTest stack;
-    stack.AddParticle(std::tuple{9.9});
+    stack.addParticle(std::tuple{9.9});
     const double v = sum(stack);
     CHECK(v == 9.9);
   }
@@ -72,12 +73,12 @@ TEST_CASE("Stack", "[Stack]") {
 
     StackTest stack;
     CHECK(stack.getSize() == 0);
-    StackTest::StackIterator p =
-        stack.AddParticle(std::tuple{0.}); // valid way to access particle data
-    p.SetData(9.9);
+    StackTest::stack_iterator_type p =
+        stack.addParticle(std::tuple{0.}); // valid way to access particle data
+    p.setData(9.9);
     CHECK(stack.getSize() == 1);
     CHECK(stack.getEntries() == 1);
-    stack.Delete(p);
+    stack.erase(p);
     CHECK(stack.getSize() == 1);
     CHECK(stack.getEntries() == 0);
   }
@@ -86,44 +87,44 @@ TEST_CASE("Stack", "[Stack]") {
 
     StackTest stack;
     CHECK(stack.getSize() == 0);
-    stack.AddParticle(std::tuple{8.9});
-    stack.AddParticle(std::tuple{7.9});
-    auto p = stack.AddParticle(
+    stack.addParticle(std::tuple{8.9});
+    stack.addParticle(std::tuple{7.9});
+    auto p = stack.addParticle(
         std::tuple{9.9}); // also valid way to access particle data, identical to above
 
     CHECK(stack.getSize() == 3);
     CHECK(stack.getEntries() == 3);
-    CHECK(!stack.IsEmpty());
+    CHECK(!stack.isEmpty());
 
-    p.Delete(); // mark for deletion: size=3, entries=2
+    p.erase(); // mark for deletion: size=3, entries=2
     CHECK(stack.getSize() == 3);
     CHECK(stack.getEntries() == 2);
-    CHECK(!stack.IsEmpty());
+    CHECK(!stack.isEmpty());
 
-    stack.last().Delete(); // mark for deletion: size=3, entries=1
+    stack.last().erase(); // mark for deletion: size=3, entries=1
     CHECK(stack.getSize() == 3);
     CHECK(stack.getEntries() == 1);
-    CHECK(!stack.IsEmpty());
+    CHECK(!stack.isEmpty());
 
     /*
        GetNextParticle will find two entries marked as "deleted" and
        will purge this from the end of the stack: size = 1
     */
-    stack.GetNextParticle().Delete(); // mark for deletion: size=3, entries=0
+    stack.getNextParticle().erase(); // mark for deletion: size=3, entries=0
     CHECK(stack.getSize() == 1);
     CHECK(stack.getEntries() == 0);
-    CHECK(stack.IsEmpty());
+    CHECK(stack.isEmpty());
   }
 
   SECTION("create secondaries") {
 
     StackTest stack;
     CHECK(stack.getSize() == 0);
-    auto iter = stack.AddParticle(std::tuple{9.9});
-    StackTest::ParticleInterfaceType& p =
+    auto iter = stack.addParticle(std::tuple{9.9});
+    StackTest::particle_interface_type& p =
         *iter; // also this is valid to access particle data
     CHECK(stack.getSize() == 1);
-    p.AddSecondary(std::tuple{4.4});
+    p.addSecondary(std::tuple{4.4});
     CHECK(stack.getSize() == 2);
   }
 
@@ -131,85 +132,85 @@ TEST_CASE("Stack", "[Stack]") {
     StackTest stack;
     CHECK(stack.getSize() == 0);
     CHECK(stack.getEntries() == 0);
-    CHECK(stack.IsEmpty());
+    CHECK(stack.isEmpty());
 
-    stack.AddParticle(std::tuple{9.9});
-    stack.AddParticle(std::tuple{8.8});
+    stack.addParticle(std::tuple{9.9});
+    stack.addParticle(std::tuple{8.8});
     CHECK(stack.getSize() == 2);
     CHECK(stack.getEntries() == 2);
-    CHECK(!stack.IsEmpty());
+    CHECK(!stack.isEmpty());
 
-    auto particle = stack.GetNextParticle(); // first particle
-    CHECK(particle.GetData() == 8.8);
+    auto particle = stack.getNextParticle(); // first particle
+    CHECK(particle.getData() == 8.8);
 
-    particle.Delete(); // only marks (last) particle as deleted
+    particle.erase(); // only marks (last) particle as deleted
     CHECK(stack.getSize() == 2);
     CHECK(stack.getEntries() == 1);
-    CHECK(!stack.IsEmpty());
+    CHECK(!stack.isEmpty());
 
     /*
       This following call to GetNextParticle will realize that the
       current last particle on the stack was marked "deleted" and will
       purge it: stack size is reduced by one.
      */
-    auto particle2 = stack.GetNextParticle(); // first particle
-    CHECK(particle2.GetData() == 9.9);
+    auto particle2 = stack.getNextParticle(); // first particle
+    CHECK(particle2.getData() == 9.9);
     CHECK(stack.getSize() == 1);
     CHECK(stack.getEntries() == 1);
-    CHECK(!stack.IsEmpty());
+    CHECK(!stack.isEmpty());
 
-    particle2.Delete(); // also mark this particle as deleted
+    particle2.erase(); // also mark this particle as deleted
 
     CHECK(stack.getSize() == 1);
     CHECK(stack.getEntries() == 0);
-    CHECK(stack.IsEmpty());
+    CHECK(stack.isEmpty());
   }
 
   SECTION("swap particle") {
     StackTest stack;
     CHECK(stack.getSize() == 0);
     CHECK(stack.getEntries() == 0);
-    CHECK(stack.IsEmpty());
+    CHECK(stack.isEmpty());
 
-    stack.AddParticle(std::tuple{9.888});
-    stack.AddParticle(std::tuple{8.999});
+    stack.addParticle(std::tuple{9.888});
+    stack.addParticle(std::tuple{8.999});
     CHECK(stack.getSize() == 2);
     CHECK(stack.getEntries() == 2);
-    CHECK(!stack.IsEmpty());
+    CHECK(!stack.isEmpty());
 
     auto p1 = stack.begin();
     auto p2 = p1 + 1;
 
-    CHECK(p1.GetData() == 9.888);
-    CHECK(p2.GetData() == 8.999);
+    CHECK(p1.getData() == 9.888);
+    CHECK(p2.getData() == 8.999);
 
-    stack.Swap(p1, p2);
+    stack.swap(p1, p2);
 
-    CHECK(p1.GetData() == 8.999);
-    CHECK(p2.GetData() == 9.888);
+    CHECK(p1.getData() == 8.999);
+    CHECK(p2.getData() == 9.888);
   }
 
   SECTION("copy particle") {
     StackTest stack;
     CHECK(stack.getSize() == 0);
     CHECK(stack.getEntries() == 0);
-    CHECK(stack.IsEmpty());
+    CHECK(stack.isEmpty());
 
-    stack.AddParticle(std::tuple{9.888});
-    stack.AddParticle(std::tuple{8.999});
+    stack.addParticle(std::tuple{9.888});
+    stack.addParticle(std::tuple{8.999});
     CHECK(stack.getSize() == 2);
     CHECK(stack.getEntries() == 2);
-    CHECK(!stack.IsEmpty());
+    CHECK(!stack.isEmpty());
 
     auto p1 = stack.begin();
     auto p2 = p1 + 1;
 
-    CHECK(p1.GetData() == 9.888);
-    CHECK(p2.GetData() == 8.999);
+    CHECK(p1.getData() == 9.888);
+    CHECK(p2.getData() == 8.999);
 
-    stack.Copy(p1, p2);
+    stack.copy(p1, p2);
 
-    CHECK(p1.GetData() == 9.888);
-    CHECK(p2.GetData() == 9.888);
+    CHECK(p1.getData() == 9.888);
+    CHECK(p2.getData() == 9.888);
   }
 }
diff --git a/tests/framework/testUnits.cpp b/tests/framework/testUnits.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..67db321d8734fc488a833348dca02c120a831b52
--- /dev/null
+++ b/tests/framework/testUnits.cpp
@@ -0,0 +1,158 @@
+/*
+ * (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.
+ */
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/core/Logging.hpp>
+
+#include <array>
+#include <sstream>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+
+TEST_CASE("PhysicalUnits", "[Units]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  SECTION("Consistency") {
+    CHECK(1_m / 1_m == Approx(1));
+    // CHECK_FALSE( 1_m/1_s == 1 ); // static assert
+  }
+
+  SECTION("Constructors") {
+    [[maybe_unused]] auto E1 = 10_GeV;
+    CHECK(E1 == 10_GeV);
+
+    LengthType l1 = 10_nm;
+    [[maybe_unused]] auto l2 = l1;
+    CHECK(l2 == l1);
+
+    LengthType arr0[5];
+    arr0[0] = 5_m;
+
+    [[maybe_unused]] LengthType arr1[2] = {{1_mm}, {2_cm}};
+
+    [[maybe_unused]] std::array<HEPEnergyType, 4> arr2; // empty array
+
+    [[maybe_unused]] std::array<HEPEnergyType, 4> arr3 = {1_GeV, 1_eV, 5_MeV};
+
+    auto p1 = 10_s * newton;
+    CHECK(p1 == 10_s * newton);
+  }
+
+  SECTION("Powers in literal units") {
+    CHECK(1_s / 1_ds == Approx(1e1));
+    CHECK(1_m / 1_cm == Approx(1e2));
+    CHECK(1_m / 1_mm == Approx(1e3));
+    CHECK(1_V / 1_uV == Approx(1e6));
+    CHECK(1_s / 1_ns == Approx(1e9));
+    CHECK(1_eV / 1_peV == Approx(1e12));
+    CHECK(1_A / 1_fA == Approx(1e15));
+    CHECK(1_mol / 1_amol == Approx(1e18));
+    CHECK(1_K / 1_zK == Approx(1e21));
+    CHECK(1_K / 1_yK == Approx(1e24));
+    CHECK(1_b / 1_mb == Approx(1e3));
+
+    CHECK(1_A / 1_hA == Approx(1e-2));
+    CHECK(1_m / 1_km == Approx(1e-3));
+    CHECK(1_m / 1_Mm == Approx(1e-6));
+    CHECK(1_V / 1_GV == Approx(1e-9));
+    CHECK(1_s / 1_Ts == Approx(1e-12));
+    CHECK(1_eV / 1_PeV == Approx(1e-15));
+    CHECK(1_A / 1_EA == Approx(1e-18));
+    CHECK(1_K / 1_ZK == Approx(1e-21));
+    CHECK(1_mol / 1_Ymol == Approx(1e-24));
+
+    CHECK(std::min(1_A, 2_A) == 1_A);
+  }
+
+  SECTION("Powers and units") {
+    CHECK(1 * ampere / 1_A == Approx(1e0));
+    CHECK(mega * bar / bar == Approx(1e6));
+  }
+
+  SECTION("Formulas") {
+    const HEPEnergyType E2 = 20_GeV * 2;
+    CHECK(E2 == 40_GeV);
+    CHECK(E2 / 1_GeV == Approx(40));
+
+    const MassType m = 1_kg;
+    const SpeedType v = 1_m / 1_s;
+    CHECK(m * v == 1_s * newton);
+
+    const double lgE = log10(E2 / 1_GeV);
+    CHECK(lgE == Approx(log10(40.)));
+
+    const auto E3 = E2 + 100_GeV + pow(10, lgE) * 1_GeV;
+    CHECK(E3 == 180_GeV);
+  }
+
+  SECTION("Output") {
+    {
+      const HEPEnergyType E = 5_eV;
+      std::stringstream stream;
+      stream << E;
+      CHECK(stream.str() == std::string("5 eV"));
+    }
+    {
+      const HEPEnergyType E = 5_EeV;
+      std::stringstream stream;
+      stream << E;
+      CHECK(stream.str() == std::string("5e+18 eV"));
+    }
+  }
+
+  SECTION("Special") {
+
+    const LengthType farAway = std::numeric_limits<double>::infinity() * meter;
+    CHECK(farAway > 100000_m);
+    CHECK_FALSE(farAway < 1e19 * meter);
+  }
+
+  SECTION("static_pow") {
+    double x = 235.7913;
+    CHECK(1 == static_pow<0, double>(x));
+    CHECK(x == static_pow<1, double>(x));
+    CHECK(x * x == static_pow<2, double>(x));
+    CHECK(1 / x == static_pow<-1, double>(x));
+    CHECK(1 / x / x == static_pow<-2, double>(x));
+  }
+
+  SECTION("HEP/SI conversion") {
+    auto const invEnergy = 1 / 197.326978_MeV; // should be convertible to length or time
+
+    LengthType const length = convert_HEP_to_SI<LengthType::dimension_type>(invEnergy);
+    CHECK((length / 1_fm) == Approx(1));
+
+    TimeType const time = convert_HEP_to_SI<TimeType::dimension_type>(invEnergy);
+    CHECK((time / (1_fm / constants::c)) == Approx(1));
+
+    auto const protonMass = 938.272'081'3_MeV; // convertible to mass or SI energy
+    MassType protonMassSI = convert_HEP_to_SI<MassType::dimension_type>(protonMass);
+    CHECK((protonMassSI / 1.672'621'898e-27_kg) == Approx(1));
+    CHECK((protonMassSI / (1.007'276 * constants::u)) == Approx(1));
+  }
+
+  SECTION("SI/HEP conversion") {
+    CHECK(convert_SI_to_HEP(constants::c) == Approx(1));
+    CHECK(convert_SI_to_HEP(constants::hBar) == Approx(1));
+
+    {
+      auto const invLength = 1 / 197.326978_fm; // should be convertible to HEPEnergy
+      HEPEnergyType const energy = convert_SI_to_HEP(invLength);
+      CHECK(energy / 1_MeV == Approx(1));
+    }
+
+    CHECK(convert_SI_to_HEP(6.5823e-25_s) * 1_GeV == Approx(1).epsilon(1e-4));
+
+    CHECK(convert_SI_to_HEP(3.8938e-32 * meter * meter) * 1_GeV * 1_GeV ==
+          Approx(1).epsilon(1e-4));
+  }
+}
diff --git a/tests/media/CMakeLists.txt b/tests/media/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..292b5793e02fc47efa55b9089f9ebf1efe77dbc4
--- /dev/null
+++ b/tests/media/CMakeLists.txt
@@ -0,0 +1,12 @@
+set (test_media_sources
+  TestMain.cpp
+  testEnvironment.cpp
+  testShowerAxis.cpp
+  testMedium.cpp
+  testRefractiveIndex.cpp
+  testMagneticField.cpp
+  )
+
+CORSIKA_ADD_TEST (testMedia SOURCES ${test_media_sources})
+
+target_link_libraries (testMedia CONAN_PKG::boost) # for math/quadrature/gauss_kronrod.hpp
diff --git a/tests/media/TestMain.cpp b/tests/media/TestMain.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..51532584b8e03b35d79301543ac8f80b598ba544
--- /dev/null
+++ b/tests/media/TestMain.cpp
@@ -0,0 +1,11 @@
+/*
+ * (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.
+ */
+
+#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one
+                          // cpp file
+#include <catch2/catch.hpp>
diff --git a/tests/media/testEnvironment.cpp b/tests/media/testEnvironment.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..d9e42cf0cdcb9b6cf22c6e6b4a7611ffa3c2f8b6
--- /dev/null
+++ b/tests/media/testEnvironment.cpp
@@ -0,0 +1,303 @@
+/*
+ * (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.
+ */
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/media/DensityFunction.hpp>
+#include <corsika/media/FlatExponential.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/MediumPropertyModel.hpp>
+#include <corsika/media/UniformMagneticField.hpp>
+#include <corsika/media/UniformRefractiveIndex.hpp>
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/IMediumPropertyModel.hpp>
+#include <corsika/media/IMagneticFieldModel.hpp>
+#include <corsika/media/IRefractiveIndexModel.hpp>
+#include <corsika/media/InhomogeneousMedium.hpp>
+#include <corsika/media/LayeredSphericalAtmosphereBuilder.hpp>
+#include <corsika/media/LinearApproximationIntegrator.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/media/SlidingPlanarExponential.hpp>
+#include <corsika/media/VolumeTreeNode.hpp>
+
+#include <SetupTestTrajectory.hpp>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+
+CoordinateSystemPtr const& gCS = get_root_CoordinateSystem();
+
+Point const gOrigin(gCS, {0_m, 0_m, 0_m});
+
+TEST_CASE("HomogeneousMedium") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
+                                             std::vector<float>{1.f});
+  HomogeneousMedium<IMediumModel> const medium(19.2_g / cube(1_cm), protonComposition);
+}
+
+TEST_CASE("FlatExponential") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
+                                             std::vector<float>{1.f});
+
+  Vector const axis(gCS, QuantityVector<dimensionless_d>(0, 0, 1));
+  LengthType const lambda = 3_m;
+  auto const rho0 = 1_g / static_pow<3>(1_cm);
+  FlatExponential<IMediumModel> const medium(gOrigin, axis, rho0, lambda,
+                                             protonComposition);
+  auto const tEnd = 5_s;
+
+  SECTION("horizontal") {
+    Line const line(gOrigin, Vector<SpeedType::dimension_type>(
+                                 gCS, {20_cm / second, 0_m / second, 0_m / second}));
+    setup::Trajectory const trajectory =
+        setup::testing::make_track<setup::Trajectory>(line, tEnd);
+
+    CHECK((medium.getIntegratedGrammage(trajectory, 2_m) / (rho0 * 2_m)) == Approx(1));
+    CHECK((medium.getArclengthFromGrammage(trajectory, rho0 * 5_m) / 5_m) == Approx(1));
+  }
+
+  SECTION("vertical") {
+    Line const line(gOrigin, Vector<SpeedType::dimension_type>(
+                                 gCS, {0_m / second, 0_m / second, 5_m / second}));
+    setup::Trajectory const trajectory =
+        setup::testing::make_track<setup::Trajectory>(line, tEnd);
+    LengthType const length = 2 * lambda;
+    GrammageType const exact = rho0 * lambda * (exp(length / lambda) - 1);
+
+    CHECK((medium.getIntegratedGrammage(trajectory, length) / exact) == Approx(1));
+    CHECK((medium.getArclengthFromGrammage(trajectory, exact) / length) == Approx(1));
+  }
+
+  SECTION("escape grammage") {
+    Line const line(gOrigin, Vector<SpeedType::dimension_type>(
+                                 gCS, {0_m / second, 0_m / second, -5_m / second}));
+    setup::Trajectory const trajectory =
+        setup::testing::make_track<setup::Trajectory>(line, tEnd);
+    GrammageType const escapeGrammage = rho0 * lambda;
+
+    CHECK(trajectory.getDirection(0).dot(axis).magnitude() < 0);
+    CHECK(medium.getArclengthFromGrammage(trajectory, 1.2 * escapeGrammage) ==
+          std::numeric_limits<typename GrammageType::value_type>::infinity() * 1_m);
+  }
+
+  SECTION("inclined") {
+    Line const line(gOrigin, Vector<SpeedType::dimension_type>(
+                                 gCS, {0_m / second, 5_m / second, 5_m / second}));
+    setup::Trajectory const trajectory =
+        setup::testing::make_track<setup::Trajectory>(line, tEnd);
+    double const cosTheta = M_SQRT1_2;
+    LengthType const length = 2 * lambda;
+    GrammageType const exact =
+        rho0 * lambda * (exp(cosTheta * length / lambda) - 1) / cosTheta;
+    CHECK((medium.getIntegratedGrammage(trajectory, length) / exact) == Approx(1));
+    CHECK((medium.getArclengthFromGrammage(trajectory, exact) / length) == Approx(1));
+  }
+}
+
+TEST_CASE("SlidingPlanarExponential") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
+                                             std::vector<float>{1.f});
+
+  LengthType const lambda = 3_m;
+  auto const rho0 = 1_g / static_pow<3>(1_cm);
+  auto const tEnd = 5_s;
+
+  SlidingPlanarExponential<IMediumModel> const medium(gOrigin, rho0, lambda,
+                                                      protonComposition);
+
+  SECTION("density") {
+    CHECK(medium.getMassDensity({gCS, {0_m, 0_m, 3_m}}) /
+              medium.getMassDensity({gCS, {0_m, 3_m, 0_m}}) ==
+          Approx(1));
+  }
+
+  SECTION("vertical") {
+    Vector const axis(gCS, QuantityVector<dimensionless_d>(0, 0, 1));
+    FlatExponential<IMediumModel> const flat(gOrigin, axis, rho0, lambda,
+                                             protonComposition);
+    Line const line({gCS, {0_m, 0_m, 1_m}},
+                    Vector<SpeedType::dimension_type>(
+                        gCS, {0_m / second, 0_m / second, 5_m / second}));
+    setup::Trajectory const trajectory =
+        setup::testing::make_track<setup::Trajectory>(line, tEnd);
+
+    CHECK(medium.getMassDensity({gCS, {0_mm, 0_m, 3_m}}).magnitude() ==
+          flat.getMassDensity({gCS, {0_mm, 0_m, 3_m}}).magnitude());
+    CHECK(medium.getIntegratedGrammage(trajectory, 2_m).magnitude() ==
+          flat.getIntegratedGrammage(trajectory, 2_m).magnitude());
+    CHECK(medium.getArclengthFromGrammage(trajectory, rho0 * 5_m).magnitude() ==
+          flat.getArclengthFromGrammage(trajectory, rho0 * 5_m).magnitude());
+  }
+}
+
+auto constexpr rho0 = 1_kg / 1_m / 1_m / 1_m;
+
+struct Exponential {
+  auto operator()(Point const& p) const {
+    return exp(p.getCoordinates()[0] / 1_m) * rho0;
+  }
+
+  template <int N>
+  auto getDerivative(Point const& p, DirectionVector const& v) const {
+    return v.getComponents()[0] * (*this)(p) / static_pow<N>(1_m);
+  }
+
+  auto getFirstDerivative(Point const& p, DirectionVector const& v) const {
+    return getDerivative<1>(p, v);
+  }
+
+  auto getSecondDerivative(Point const& p, DirectionVector const& v) const {
+    return getDerivative<2>(p, v);
+  }
+};
+
+TEST_CASE("InhomogeneousMedium") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  Vector direction(gCS, QuantityVector<dimensionless_d>(1, 0, 0));
+
+  Line line(gOrigin, Vector<SpeedType::dimension_type>(
+                         gCS, {20_m / second, 0_m / second, 0_m / second}));
+
+  auto const tEnd = 5_s;
+  setup::Trajectory const trajectory =
+      setup::testing::make_track<setup::Trajectory>(line, tEnd);
+
+  Exponential const e;
+  DensityFunction<decltype(e), LinearApproximationIntegrator> const rho(e);
+
+  SECTION("DensityFunction") {
+    CHECK(e.getDerivative<1>(gOrigin, direction) / (1_kg / 1_m / 1_m / 1_m / 1_m) ==
+          Approx(1));
+    CHECK(rho.evaluateAt(gOrigin) == e(gOrigin));
+  }
+
+  auto const exactGrammage = [](auto l) { return 1_m * rho0 * (exp(l / 1_m) - 1); };
+  auto const exactLength = [](auto X) { return 1_m * log(1 + X / (rho0 * 1_m)); };
+
+  auto constexpr l = 15_cm;
+
+  NuclearComposition const composition{{Code::Proton}, {1.f}};
+  InhomogeneousMedium<IMediumModel, decltype(rho)> const inhMedium(composition, rho);
+
+  SECTION("Integration") {
+    CHECK(rho.getIntegrateGrammage(trajectory, l) / exactGrammage(l) ==
+          Approx(1).epsilon(1e-2));
+    CHECK(rho.getArclengthFromGrammage(trajectory, exactGrammage(l)) /
+              exactLength(exactGrammage(l)) ==
+          Approx(1).epsilon(1e-2));
+    CHECK(rho.getMaximumLength(trajectory, 1e-2) >
+          l); // todo: write reasonable test when implementation is working
+
+    CHECK(rho.getIntegrateGrammage(trajectory, l) ==
+          inhMedium.getIntegratedGrammage(trajectory, l));
+    CHECK(rho.getArclengthFromGrammage(trajectory, 20_g / (1_cm * 1_cm)) ==
+          inhMedium.getArclengthFromGrammage(trajectory, 20_g / (1_cm * 1_cm)));
+  }
+}
+
+TEST_CASE("LayeredSphericalAtmosphereBuilder") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  LayeredSphericalAtmosphereBuilder builder =
+      make_layered_spherical_atmosphere_builder<>::create(gOrigin,
+                                                          constants::EarthRadius::Mean);
+
+  builder.setNuclearComposition({{{Code::Nitrogen, Code::Oxygen}}, {{.6, .4}}});
+
+  builder.addLinearLayer(1_km, 10_km);
+  builder.addLinearLayer(2_km, 20_km);
+  builder.addExponentialLayer(540.1778_g / (1_cm * 1_cm), 772170.16_cm, 30_km);
+
+  CHECK(builder.getSize() == 3);
+
+  auto const builtEnv = builder.assemble();
+  auto const& univ = builtEnv.getUniverse();
+
+  CHECK(builder.getSize() == 0);
+
+  auto const R = builder.getEarthRadius();
+
+  CHECK(univ->getChildNodes().size() == 1);
+
+  CHECK(univ->getContainingNode(Point(gCS, 0_m, 0_m, R + 35_km)) == univ.get());
+  CHECK(dynamic_cast<Sphere const&>(
+            univ->getContainingNode(Point(gCS, 0_m, 0_m, R + 8_km))->getVolume())
+            .getRadius() == R + 10_km);
+  CHECK(dynamic_cast<Sphere const&>(
+            univ->getContainingNode(Point(gCS, 0_m, 0_m, R + 12_km))->getVolume())
+            .getRadius() == R + 20_km);
+  CHECK(dynamic_cast<Sphere const&>(
+            univ->getContainingNode(Point(gCS, 0_m, 0_m, R + 24_km))->getVolume())
+            .getRadius() == R + 30_km);
+}
+
+TEST_CASE("LayeredSphericalAtmosphereBuilder w/ magnetic field") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  // setup our interface types
+  using ModelInterface = IMagneticFieldModel<IMediumModel>;
+
+  // the composition we use for the homogenous medium
+  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
+                                             std::vector<float>{1.f});
+
+  // create magnetic field vectors
+  Vector B0(gCS, 0_T, 0_T, 1_T);
+
+  LayeredSphericalAtmosphereBuilder builder = make_layered_spherical_atmosphere_builder<
+      ModelInterface, UniformMagneticField>::create(gOrigin, constants::EarthRadius::Mean,
+                                                    B0);
+
+  builder.setNuclearComposition({{{Code::Nitrogen, Code::Oxygen}}, {{.6, .4}}});
+  builder.addLinearLayer(1_km, 10_km);
+  builder.addExponentialLayer(1222.6562_g / (1_cm * 1_cm), 994186.38_cm, 20_km);
+
+  CHECK(builder.getSize() == 2);
+
+  auto const builtEnv = builder.assemble();
+  auto const& univ = builtEnv.getUniverse();
+
+  CHECK(builder.getSize() == 0);
+  CHECK(univ->getChildNodes().size() == 1);
+  auto const R = builder.getEarthRadius();
+
+  // check magnetic field at several locations
+  const Point pTest(gCS, -10_m, 4_m, R + 35_m);
+  CHECK(B0.getComponents(gCS) == univ->getContainingNode(pTest)
+                                     ->getModelProperties()
+                                     .getMagneticField(pTest)
+                                     .getComponents(gCS));
+  const Point pTest2(gCS, 10_m, -4_m, R + 15_km);
+  CHECK(B0.getComponents(gCS) == univ->getContainingNode(pTest2)
+                                     ->getModelProperties()
+                                     .getMagneticField(pTest2)
+                                     .getComponents(gCS));
+}
diff --git a/tests/media/testMagneticField.cpp b/tests/media/testMagneticField.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..73827dd92fbfafe5e0a697b3ce2e6f13757af5c6
--- /dev/null
+++ b/tests/media/testMagneticField.cpp
@@ -0,0 +1,91 @@
+/*
+ * (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.
+ */
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/UniformMagneticField.hpp>
+#include <corsika/media/IMagneticFieldModel.hpp>
+#include <corsika/media/VolumeTreeNode.hpp>
+
+#include <SetupTestTrajectory.hpp>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+
+TEST_CASE("UniformMagneticField w/ Homogeneous Medium") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  CoordinateSystemPtr const& gCS = get_root_CoordinateSystem();
+  Point const gOrigin(gCS, {0_m, 0_m, 0_m});
+
+  // setup our interface types
+  using IModelInterface = IMagneticFieldModel<IMediumModel>;
+  using AtmModel = UniformMagneticField<HomogeneousMedium<IModelInterface>>;
+
+  // the composition we use for the homogenous medium
+  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
+                                             std::vector<float>{1.f});
+
+  // create a magnetic field vector
+  Vector B0(gCS, 0_T, 0_T, 0_T);
+
+  // the constant density
+  const auto density{19.2_g / cube(1_cm)};
+
+  // create our atmospheric model
+  AtmModel medium(B0, density, protonComposition);
+
+  // and test at several locations
+  CHECK(B0.getComponents(gCS) ==
+        medium.getMagneticField(Point(gCS, -10_m, 4_m, 35_km)).getComponents(gCS));
+  CHECK(
+      B0.getComponents(gCS) ==
+      medium.getMagneticField(Point(gCS, 1000_km, -1000_km, 1000_km)).getComponents(gCS));
+  CHECK(B0.getComponents(gCS) ==
+        medium.getMagneticField(Point(gCS, 0_m, 0_m, 0_m)).getComponents(gCS));
+
+  // create a new magnetic field vector
+  Vector B1(gCS, 23_T, 57_T, -4_T);
+
+  // and update this atmospheric model
+  medium.setMagneticField(B1);
+
+  // and test at several locations
+  CHECK(B1.getComponents(gCS) ==
+        medium.getMagneticField(Point(gCS, -10_m, 4_m, 35_km)).getComponents(gCS));
+  CHECK(
+      B1.getComponents(gCS) ==
+      medium.getMagneticField(Point(gCS, 1000_km, -1000_km, 1000_km)).getComponents(gCS));
+  CHECK(B1.getComponents(gCS) ==
+        medium.getMagneticField(Point(gCS, 0_m, 0_m, 0_m)).getComponents(gCS));
+
+  // check the density and nuclear composition
+  CHECK(density == medium.getMassDensity(Point(gCS, 0_m, 0_m, 0_m)));
+  medium.getNuclearComposition();
+
+  // create a line of length 1 m
+  Line const line(gOrigin, Vector<SpeedType::dimension_type>(
+                               gCS, {1_m / second, 0_m / second, 0_m / second}));
+
+  // the end time of our line
+  auto const tEnd = 1_s;
+
+  // and the associated trajectory
+  setup::Trajectory const track =
+      setup::testing::make_track<setup::Trajectory>(line, tEnd);
+
+  // and check the integrated grammage
+  CHECK((medium.getIntegratedGrammage(track, 3_m) / (density * 3_m)) == Approx(1));
+  CHECK((medium.getArclengthFromGrammage(track, density * 5_m) / 5_m) == Approx(1));
+}
diff --git a/tests/media/testMedium.cpp b/tests/media/testMedium.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..670c25d9246e3f1ffacef09cde5275f85500242d
--- /dev/null
+++ b/tests/media/testMedium.cpp
@@ -0,0 +1,109 @@
+/*
+ * (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.
+ */
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/media/FlatExponential.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/InhomogeneousMedium.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/media/MediumPropertyModel.hpp>
+#include <corsika/media/MediumProperties.hpp>
+
+#include <catch2/catch.hpp>
+
+#include <SetupTestTrajectory.hpp>
+
+using namespace corsika;
+
+TEST_CASE("MediumProperties") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  // test access of medium properties via enum and class types
+
+  const Medium type = Medium::AirDry1Atm;
+  const MediumData& air = mediumData(type);
+  CHECK(air.getIeff() == 85.7);
+  CHECK(air.getCbar() == 10.5961);
+  CHECK(air.getX0() == 1.7418);
+  CHECK(air.getX1() == 4.2759);
+  CHECK(air.getAA() == 0.10914);
+  CHECK(air.getSK() == 3.3994);
+  CHECK(air.getDlt0() == 0.0);
+}
+
+TEST_CASE("MediumPropertyModel w/ Homogeneous") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  CoordinateSystemPtr gCS = get_root_CoordinateSystem();
+
+  Point const gOrigin(gCS, {0_m, 0_m, 0_m});
+
+  // setup our interface types
+  using IModelInterface = IMediumPropertyModel<IMediumModel>;
+  using AtmModel = MediumPropertyModel<HomogeneousMedium<IModelInterface>>;
+
+  // the constant density
+  const auto density{19.2_g / cube(1_cm)};
+
+  // the composition we use for the homogenous medium
+  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
+                                             std::vector<float>{1.f});
+
+  // the refrative index that we use
+  const Medium type = corsika::Medium::AirDry1Atm;
+
+  // create the atmospheric model
+  AtmModel medium(type, density, protonComposition);
+
+  // and require that it is constant
+  CHECK(type == medium.getMedium(Point(gCS, -10_m, 4_m, 35_km)));
+  CHECK(type == medium.getMedium(Point(gCS, +210_m, 0_m, 7_km)));
+  CHECK(type == medium.getMedium(Point(gCS, 0_m, 0_m, 0_km)));
+  CHECK(type == medium.getMedium(Point(gCS, 100_km, 400_km, 350_km)));
+
+  // a new refractive index
+  const Medium type2 = corsika::Medium::StandardRock;
+
+  // update the refractive index of this atmospheric model
+  medium.setMedium(type2);
+
+  // check that the returned refractive index is correct
+  CHECK(type2 == medium.getMedium(Point(gCS, -10_m, 4_m, 35_km)));
+  CHECK(type2 == medium.getMedium(Point(gCS, +210_m, 0_m, 7_km)));
+  CHECK(type2 == medium.getMedium(Point(gCS, 0_m, 0_m, 0_km)));
+  CHECK(type2 == medium.getMedium(Point(gCS, 100_km, 400_km, 350_km)));
+
+  // define our axis vector
+  Vector const axis(gCS, QuantityVector<dimensionless_d>(0, 0, 1));
+
+  // check the density and nuclear composition
+  CHECK(density == medium.getMassDensity(Point(gCS, 0_m, 0_m, 0_m)));
+  medium.getNuclearComposition();
+
+  // create a line of length 1 m
+  Line const line(gOrigin,
+                  VelocityVector(gCS, {1_m / second, 0_m / second, 0_m / second}));
+
+  // the end time of our line
+  auto const tEnd = 1_s;
+
+  // and the associated trajectory
+  setup::Trajectory const trajectory(line, tEnd);
+
+  // and check the integrated grammage
+  CHECK((medium.getIntegratedGrammage(trajectory, 3_m) / (density * 3_m)) == Approx(1));
+  CHECK((medium.getArclengthFromGrammage(trajectory, density * 5_m) / 5_m) == Approx(1));
+}
diff --git a/tests/media/testRefractiveIndex.cpp b/tests/media/testRefractiveIndex.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..fb1e71fec7b914fcb449f8a2bd2d06dce952222e
--- /dev/null
+++ b/tests/media/testRefractiveIndex.cpp
@@ -0,0 +1,91 @@
+/*
+ * (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.
+ */
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/media/FlatExponential.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/InhomogeneousMedium.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/media/UniformRefractiveIndex.hpp>
+
+#include <SetupTestTrajectory.hpp>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+
+TEST_CASE("UniformRefractiveIndex w/ Homogeneous") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  CoordinateSystemPtr const& gCS = get_root_CoordinateSystem();
+
+  Point const gOrigin(gCS, {0_m, 0_m, 0_m});
+
+  // setup our interface types
+  using IModelInterface = IRefractiveIndexModel<IMediumModel>;
+  using AtmModel = UniformRefractiveIndex<HomogeneousMedium<IModelInterface>>;
+
+  // the constant density
+  const auto density{19.2_g / cube(1_cm)};
+
+  // the composition we use for the homogenous medium
+  NuclearComposition const protonComposition(std::vector<Code>{Code::Proton},
+                                             std::vector<float>{1.f});
+
+  // the refrative index that we use
+  const double n{1.000327};
+
+  // create the atmospheric model
+  AtmModel medium(n, density, protonComposition);
+
+  // and require that it is constant
+  CHECK(n == medium.getRefractiveIndex(Point(gCS, -10_m, 4_m, 35_km)));
+  CHECK(n == medium.getRefractiveIndex(Point(gCS, +210_m, 0_m, 7_km)));
+  CHECK(n == medium.getRefractiveIndex(Point(gCS, 0_m, 0_m, 0_km)));
+  CHECK(n == medium.getRefractiveIndex(Point(gCS, 100_km, 400_km, 350_km)));
+
+  // a new refractive index
+  const double n2{2.3472123};
+
+  // update the refractive index of this atmospheric model
+  medium.setRefractiveIndex(n2);
+
+  // check that the returned refractive index is correct
+  CHECK(n2 == medium.getRefractiveIndex(Point(gCS, -10_m, 4_m, 35_km)));
+  CHECK(n2 == medium.getRefractiveIndex(Point(gCS, +210_m, 0_m, 7_km)));
+  CHECK(n2 == medium.getRefractiveIndex(Point(gCS, 0_m, 0_m, 0_km)));
+  CHECK(n2 == medium.getRefractiveIndex(Point(gCS, 100_km, 400_km, 350_km)));
+
+  // define our axis vector
+  Vector const axis(gCS, QuantityVector<dimensionless_d>(0, 0, 1));
+
+  // check the density and nuclear composition
+  CHECK(density == medium.getMassDensity(Point(gCS, 0_m, 0_m, 0_m)));
+  medium.getNuclearComposition();
+
+  // create a line of length 1 m
+  Line const line(gOrigin,
+                  VelocityVector(gCS, {1_m / second, 0_m / second, 0_m / second}));
+
+  // the end time of our line
+  auto const tEnd = 1_s;
+
+  // and the associated trajectory
+  setup::Trajectory const track =
+      setup::testing::make_track<setup::Trajectory>(line, tEnd);
+
+  // and check the integrated grammage
+  CHECK((medium.getIntegratedGrammage(track, 3_m) / (density * 3_m)) == Approx(1));
+  CHECK((medium.getArclengthFromGrammage(track, density * 5_m) / 5_m) == Approx(1));
+}
diff --git a/tests/media/testShowerAxis.cpp b/tests/media/testShowerAxis.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..8ba454c342eb3ba1f972c4c6129ab0876fb1bcf9
--- /dev/null
+++ b/tests/media/testShowerAxis.cpp
@@ -0,0 +1,83 @@
+/*
+ * (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/media/DensityFunction.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/IMediumModel.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/media/ShowerAxis.hpp>
+#include <corsika/media/VolumeTreeNode.hpp>
+#include <corsika/framework/geometry/Line.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <catch2/catch.hpp>
+
+// using namespace
+using namespace corsika;
+
+const auto density = 1_kg / (1_m * 1_m * 1_m);
+
+auto setupEnvironment(Code vTargetCode) {
+  // setup environment, geometry
+  auto env = std::make_unique<Environment<IMediumModel>>();
+  auto& universe = *(env->getUniverse());
+  const CoordinateSystemPtr& cs = env->getCoordinateSystem();
+
+  auto theMedium = Environment<IMediumModel>::createNode<Sphere>(
+      Point{cs, 0_m, 0_m, 0_m}, 1_km * std::numeric_limits<double>::infinity());
+
+  using MyHomogeneousModel = HomogeneousMedium<IMediumModel>;
+  theMedium->setModelProperties<MyHomogeneousModel>(
+      density,
+      NuclearComposition(std::vector<Code>{vTargetCode}, std::vector<float>{1.}));
+
+  auto const* nodePtr = theMedium.get();
+  universe.addChild(std::move(theMedium));
+
+  return std::make_tuple(std::move(env), &cs, nodePtr);
+}
+
+TEST_CASE("Homogeneous Density") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  auto [env, csPtr, nodePtr] = setupEnvironment(Code::Nitrogen);
+  auto const& cs = *csPtr;
+  [[maybe_unused]] auto const& env_dummy = env;
+  [[maybe_unused]] auto const& node_dummy = nodePtr;
+
+  auto const observationHeight = 0_km;
+  auto const injectionHeight = 10_km;
+  auto const t = -observationHeight + injectionHeight;
+  Point const showerCore{cs, 0_m, 0_m, observationHeight};
+  Point const injectionPos = showerCore + Vector<dimensionless_d>{cs, {0, 0, 1}} * t;
+
+  ShowerAxis const showerAxis{injectionPos, (showerCore - injectionPos), *env,
+                              false, // -> do not throw exceptions
+                              20};   // -> number of bins
+
+  CHECK(showerAxis.getSteplength() == 500_m);
+
+  CHECK(showerAxis.getMaximumX() / (10_km * density) == Approx(1).epsilon(1e-8));
+
+  CHECK(showerAxis.getMinimumX() == 0_g / square(1_cm));
+
+  const Point p{cs, 10_km, 20_km, 8.3_km};
+  CHECK(showerAxis.getProjectedX(p) / (1.7_km * density) == Approx(1).epsilon(1e-8));
+
+  const LengthType d = 6.789_km;
+  CHECK(showerAxis.getX(d) / (d * density) == Approx(1).epsilon(1e-8));
+
+  const Vector<dimensionless_d> dir{cs, {0, 0, -1}};
+  CHECK(showerAxis.getDirection().getComponents(cs) == dir.getComponents(cs));
+  CHECK(showerAxis.getStart().getCoordinates() == injectionPos.getCoordinates());
+}
diff --git a/tests/modules/CMakeLists.txt b/tests/modules/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..d72de5b5314c8af20d58e8b04adead3994e5db95
--- /dev/null
+++ b/tests/modules/CMakeLists.txt
@@ -0,0 +1,22 @@
+set (test_modules_sources
+  TestMain.cpp
+  testStackInspector.cpp
+  testTracking.cpp
+  # testExecTime.cpp --> needs to be fixed, see #326
+  testObservationPlane.cpp
+  testQGSJetII.cpp
+  testPythia8.cpp
+  testUrQMD.cpp
+  testCONEX.cpp
+  testOnShellCheck.cpp
+  testParticleCut.cpp
+  testSibyll.cpp
+  )
+
+CORSIKA_ADD_TEST (testModules SOURCES ${test_modules_sources})
+
+target_compile_definitions (
+  testModules
+  PRIVATE
+  REFDATADIR="${CMAKE_CURRENT_SOURCE_DIR}"
+)
diff --git a/tests/modules/TestMain.cpp b/tests/modules/TestMain.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..51532584b8e03b35d79301543ac8f80b598ba544
--- /dev/null
+++ b/tests/modules/TestMain.cpp
@@ -0,0 +1,11 @@
+/*
+ * (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.
+ */
+
+#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one
+                          // cpp file
+#include <catch2/catch.hpp>
diff --git a/Processes/CONEXSourceCut/conex_fit_REF.txt b/tests/modules/conex_fit_REF.txt
similarity index 100%
rename from Processes/CONEXSourceCut/conex_fit_REF.txt
rename to tests/modules/conex_fit_REF.txt
diff --git a/tests/modules/conex_output_REF.txt b/tests/modules/conex_output_REF.txt
new file mode 100644
index 0000000000000000000000000000000000000000..c609d74bdfa35897498dd1ba46e68a502079ee78
--- /dev/null
+++ b/tests/modules/conex_output_REF.txt
@@ -0,0 +1,208 @@
+#       X             N          dEdX            Mu           dMu         Gamma            El           Had
+     0.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+    10.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+    20.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+    30.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+    40.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+    50.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+    60.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+    70.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+    80.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+    90.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   100.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   110.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   120.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   130.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   140.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   150.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   160.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   170.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   180.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   190.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   200.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   210.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   220.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   230.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   240.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   250.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   260.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   270.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   280.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   290.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   300.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   310.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   320.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   330.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   340.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   350.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   360.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   370.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   380.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   390.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   400.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   410.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   420.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   430.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   440.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   450.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   460.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   470.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   480.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   490.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   500.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   510.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   520.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   530.00           0.00           0.00           0.00           0.00           0.00           0.00           0.00
+   540.00           0.00      0.000620           0.00           0.00          1.00           0.00           0.00
+   550.00         0.512       0.00246      0.000784      0.000802          1.69         0.272       0.00518
+   560.00          1.45       0.00598       0.00326       0.00256          4.23         0.787       0.00985
+   570.00          3.23        0.0123       0.00719       0.00411          9.26          1.78        0.0145
+   580.00          6.30        0.0227        0.0126       0.00569          18.0          3.51        0.0195
+   590.00          11.2        0.0385        0.0194       0.00734          32.0          6.27        0.0248
+   600.00          18.4        0.0613        0.0278       0.00906          53.3          10.4        0.0304
+   610.00          28.6        0.0925        0.0376        0.0108          84.1          16.2        0.0364
+   620.00          42.3         0.134        0.0489        0.0125          127.0          24.1        0.0425
+   630.00          60.1         0.186        0.0615        0.0142          184.0          34.5        0.0489
+   640.00          82.5         0.251        0.0753        0.0158          258.0          47.5        0.0553
+   650.00          110.0         0.328        0.0902        0.0173          350.0          63.4        0.0618
+   660.00          142.0         0.416         0.106        0.0186          464.0          82.5        0.0681
+   670.00          180.0         0.518         0.123        0.0198          600.0          105.0        0.0744
+   680.00          223.0         0.632         0.140        0.0208          759.0          130.0        0.0803
+   690.00          270.0         0.757         0.157        0.0216          942.0          158.0        0.0860
+   700.00          322.0         0.892         0.175        0.0223      1.15e+03          189.0        0.0913
+   710.00          378.0          1.04         0.193        0.0227      1.38e+03          222.0        0.0962
+   720.00          437.0          1.19         0.210        0.0230      1.62e+03          257.0         0.101
+   730.00          497.0          1.34         0.227        0.0230      1.89e+03          294.0         0.104
+   740.00          559.0          1.50         0.244        0.0229      2.17e+03          331.0         0.108
+   750.00          621.0          1.65         0.260        0.0227      2.46e+03          369.0         0.111
+   760.00          683.0          1.81         0.275        0.0223      2.76e+03          406.0         0.113
+   770.00          742.0          1.96         0.289        0.0218      3.06e+03          443.0         0.114
+   780.00          799.0          2.10         0.303        0.0212      3.36e+03          478.0         0.116
+   790.00          853.0          2.23         0.315        0.0205      3.66e+03          511.0         0.116
+   800.00          902.0          2.35         0.327        0.0197      3.94e+03          542.0         0.116
+   810.00          947.0          2.45         0.337        0.0189      4.21e+03          570.0         0.116
+   820.00          986.0          2.55         0.347        0.0180      4.47e+03          595.0         0.115
+   830.00      1.02e+03          2.63         0.355        0.0171      4.70e+03          616.0         0.113
+   840.00      1.05e+03          2.69         0.362        0.0162      4.92e+03          634.0         0.112
+   850.00      1.07e+03          2.74         0.369        0.0153      5.10e+03          648.0         0.109
+   860.00      1.08e+03          2.77         0.374        0.0144      5.27e+03          659.0         0.107
+   870.00      1.09e+03          2.79         0.379        0.0135      5.40e+03          666.0         0.104
+   880.00      1.10e+03          2.79         0.382        0.0126      5.50e+03          669.0         0.101
+   890.00      1.09e+03          2.78         0.385        0.0117      5.58e+03          669.0        0.0983
+   900.00      1.08e+03          2.76         0.387        0.0109      5.63e+03          665.0        0.0950
+   910.00      1.07e+03          2.72         0.388        0.0101      5.64e+03          658.0        0.0916
+   920.00      1.05e+03          2.67         0.388       0.00933      5.64e+03          649.0        0.0881
+   930.00      1.03e+03          2.62         0.388       0.00861      5.60e+03          636.0        0.0845
+   940.00      1.01e+03          2.55         0.388       0.00793      5.54e+03          622.0        0.0809
+   950.00          978.0          2.48         0.387       0.00729      5.46e+03          605.0        0.0773
+   960.00          947.0          2.39         0.385       0.00669      5.36e+03          587.0        0.0737
+   970.00          913.0          2.31         0.383       0.00613      5.24e+03          567.0        0.0702
+   980.00          878.0          2.22         0.380       0.00561      5.11e+03          546.0        0.0667
+   990.00          841.0          2.12         0.378       0.00512      4.96e+03          524.0        0.0633
+  1000.00          803.0          2.03         0.375       0.00467      4.80e+03          501.0        0.0599
+  1010.00          765.0          1.93         0.371       0.00426      4.63e+03          478.0        0.0566
+  1020.00          726.0          1.83         0.368       0.00387      4.45e+03          455.0        0.0535
+  1030.00          688.0          1.73         0.364       0.00352      4.26e+03          431.0        0.0504
+  1040.00          650.0          1.64         0.360       0.00320      4.07e+03          408.0        0.0475
+  1050.00          612.0          1.54         0.356       0.00290      3.88e+03          385.0        0.0447
+  1060.00          575.0          1.45         0.352       0.00263      3.69e+03          362.0        0.0420
+  1070.00          539.0          1.36         0.348       0.00239      3.50e+03          340.0        0.0394
+  1080.00          504.0          1.27         0.344       0.00216      3.31e+03          319.0        0.0370
+  1090.00          470.0          1.19         0.340       0.00196      3.12e+03          298.0        0.0346
+  1100.00          438.0          1.10         0.336       0.00177      2.94e+03          278.0        0.0324
+  1110.00          407.0          1.03         0.331       0.00160      2.76e+03          258.0        0.0303
+  1120.00          377.0         0.952         0.327       0.00145      2.58e+03          240.0        0.0283
+  1130.00          349.0         0.881         0.323       0.00131      2.41e+03          222.0        0.0265
+  1140.00          323.0         0.814         0.319       0.00119      2.25e+03          206.0        0.0247
+  1150.00          298.0         0.751         0.314       0.00108      2.10e+03          190.0        0.0230
+  1160.00          274.0         0.692         0.310      0.000973      1.95e+03          175.0        0.0215
+  1170.00          252.0         0.636         0.306      0.000881      1.81e+03          161.0        0.0200
+  1180.00          231.0         0.584         0.302      0.000798      1.67e+03          148.0        0.0186
+  1190.00          212.0         0.535         0.298      0.000722      1.55e+03          136.0        0.0173
+  1200.00          194.0         0.490         0.294      0.000655      1.43e+03          125.0        0.0161
+  1210.00          177.0         0.447         0.290      0.000593      1.32e+03          114.0        0.0150
+  1220.00          161.0         0.408         0.286      0.000538      1.21e+03          104.0        0.0140
+  1230.00          147.0         0.372         0.283      0.000489      1.11e+03          94.8        0.0130
+  1240.00          134.0         0.338         0.279      0.000444      1.02e+03          86.3        0.0121
+  1250.00          121.0         0.307         0.275      0.000403          933.0          78.5        0.0112
+  1260.00          110.0         0.279         0.272      0.000367          853.0          71.3        0.0104
+  1270.00          99.8         0.253         0.268      0.000335          779.0          64.7       0.00970
+  1280.00          90.3         0.229         0.265      0.000305          710.0          58.6       0.00901
+  1290.00          81.6         0.207         0.262      0.000278          647.0          53.0       0.00838
+  1300.00          73.7         0.187         0.258      0.000254          588.0          47.9       0.00779
+  1310.00          66.5         0.169         0.255      0.000232          534.0          43.2       0.00724
+  1320.00          59.9         0.152         0.252      0.000212          485.0          39.0       0.00673
+  1330.00          53.9         0.137         0.249      0.000194          439.0          35.1       0.00626
+  1340.00          48.5         0.123         0.246      0.000178          397.0          31.6       0.00582
+  1350.00          43.5         0.111         0.243      0.000163          359.0          28.4       0.00541
+  1360.00          39.1        0.0995         0.240      0.000150          324.0          25.5       0.00503
+  1370.00          35.0        0.0892         0.237      0.000138          293.0          22.9       0.00469
+  1380.00          31.4        0.0800         0.234      0.000127          264.0          20.5       0.00436
+  1390.00          28.1        0.0716         0.231      0.000117          237.0          18.4       0.00406
+  1400.00          25.1        0.0641         0.229      0.000108          213.0          16.4       0.00379
+  1410.00          22.4        0.0573         0.226      9.93e-05          192.0          14.7       0.00353
+  1420.00          20.0        0.0512         0.223      9.17e-05          172.0          13.1       0.00329
+  1430.00          17.9        0.0457         0.221      8.48e-05          154.0          11.7       0.00307
+  1440.00          16.0        0.0408         0.218      7.85e-05          138.0          10.4       0.00287
+  1450.00          14.2        0.0364         0.216      7.28e-05          124.0          9.29       0.00268
+  1460.00          12.7        0.0325         0.214      6.75e-05          111.0          8.27       0.00250
+  1470.00          11.3        0.0289         0.211      6.27e-05          98.9          7.36       0.00234
+  1480.00          10.0        0.0258         0.209      5.83e-05          88.3          6.54       0.00219
+  1490.00          8.93        0.0229         0.207      5.42e-05          78.7          5.81       0.00205
+  1500.00          7.94        0.0204         0.204      5.05e-05          70.2          5.16       0.00192
+  1510.00          7.05        0.0182         0.202      4.70e-05          62.5          4.58       0.00180
+  1520.00          6.27        0.0161         0.200      4.39e-05          55.7          4.06       0.00169
+  1530.00          5.57        0.0144         0.198      4.10e-05          49.5          3.60       0.00159
+  1540.00          4.95        0.0128         0.196      3.83e-05          44.0          3.18       0.00150
+  1550.00          4.40        0.0114         0.194      3.58e-05          39.1          2.82       0.00141
+  1560.00          3.91        0.0101         0.192      3.35e-05          34.7          2.49       0.00133
+  1570.00          3.47       0.00899         0.190      3.14e-05          30.8          2.20       0.00125
+  1580.00          3.09       0.00800         0.188      2.94e-05          27.3          1.95       0.00118
+  1590.00          2.75       0.00712         0.186      2.76e-05          24.2          1.72       0.00112
+  1600.00          2.44       0.00635         0.185      2.59e-05          21.4          1.52       0.00106
+  1610.00          2.18       0.00566         0.183      2.44e-05          19.0          1.34       0.00100
+  1620.00          1.94       0.00505         0.181      2.29e-05          16.8          1.19      0.000948
+  1630.00          1.73       0.00451         0.179      2.16e-05          14.9          1.05      0.000900
+  1640.00          1.55       0.00404         0.177      2.04e-05          13.1         0.923      0.000855
+  1650.00          1.38       0.00362         0.176      1.92e-05          11.6         0.814      0.000814
+  1660.00          1.24       0.00324         0.174      1.81e-05          10.3         0.718      0.000775
+  1670.00          1.11       0.00292         0.173      1.71e-05          9.07         0.634      0.000740
+  1680.00         0.998       0.00263         0.171      1.62e-05          8.01         0.559      0.000706
+  1690.00         0.900       0.00237         0.169      1.53e-05          7.08         0.494      0.000675
+  1700.00         0.812       0.00214         0.168      1.45e-05          6.25         0.436      0.000647
+  1710.00         0.736       0.00195         0.166      1.38e-05          5.52         0.385      0.000620
+  1720.00         0.668       0.00177         0.165      1.31e-05          4.87         0.340      0.000595
+  1730.00         0.608       0.00161         0.163      1.24e-05          4.30         0.301      0.000572
+  1740.00         0.556       0.00148         0.162      1.18e-05          3.80         0.266      0.000550
+  1750.00         0.509       0.00136         0.161      1.12e-05          3.36         0.236      0.000530
+  1760.00         0.469       0.00125         0.159      1.07e-05          2.97         0.209      0.000512
+  1770.00         0.433       0.00116         0.158      1.02e-05          2.63         0.185      0.000494
+  1780.00         0.401       0.00108         0.156      9.71e-06          2.33         0.165      0.000478
+  1790.00         0.373       0.00100         0.155      9.27e-06          2.06         0.147      0.000463
+  1800.00         0.349      0.000939         0.154      8.86e-06          1.83         0.131      0.000448
+  1810.00         0.327      0.000882         0.152      8.48e-06          1.62         0.117      0.000435
+  1820.00         0.308      0.000831         0.151      8.12e-06          1.44         0.105      0.000423
+  1830.00         0.291      0.000787         0.150      7.78e-06          1.28        0.0941      0.000411
+  1840.00         0.276      0.000747         0.149      7.46e-06          1.14        0.0847      0.000400
+  1850.00         0.262      0.000712         0.147      7.16e-06          1.02        0.0764      0.000390
+  1860.00         0.251      0.000681         0.146      6.88e-06         0.914        0.0692      0.000381
+  1870.00         0.240      0.000653         0.145      6.62e-06         0.820        0.0629      0.000372
+  1880.00         0.231      0.000629         0.144      6.37e-06         0.737        0.0573      0.000364
+  1890.00         0.222      0.000607         0.143      6.14e-06         0.665        0.0524      0.000356
+  1900.00         0.215      0.000587         0.142      5.92e-06         0.601        0.0481      0.000349
+  1910.00         0.208      0.000569         0.141      5.72e-06         0.545        0.0443      0.000342
+  1920.00         0.202      0.000553         0.139      5.52e-06         0.495        0.0410      0.000335
+  1930.00         0.197      0.000539         0.138      5.34e-06         0.452        0.0381      0.000329
+  1940.00         0.192      0.000526         0.137      5.17e-06         0.414        0.0355      0.000324
+  1950.00         0.188      0.000514         0.136      5.01e-06         0.381        0.0332      0.000318
+  1960.00         0.184      0.000503         0.135      4.85e-06         0.351        0.0312      0.000313
+  1970.00         0.180      0.000493         0.134      4.71e-06         0.325        0.0295      0.000309
+  1980.00         0.177      0.000484         0.133      4.57e-06         0.303        0.0279      0.000304
+  1990.00         0.174      0.000476         0.132      4.44e-06         0.283        0.0265      0.000300
+  2000.00         0.171      0.000469         0.131      4.32e-06         0.265        0.0252      0.000296
+  2010.00         0.168      0.000461         0.130      4.20e-06         0.249        0.0241      0.000292
+  2020.00         0.166      0.000455         0.129      4.10e-06         0.235        0.0232      0.000289
+  2030.00         0.164      0.000449         0.128      3.99e-06         0.223        0.0223      0.000285
+  2040.00         0.162      0.000443         0.127      3.89e-06         0.212        0.0215      0.000282
+  2050.00         0.160      0.000437         0.127      3.80e-06         0.202        0.0208      0.000279
+  2060.00         0.158           0.00         0.126      3.71e-06         0.193        0.0201      0.000276
diff --git a/tests/modules/testCONEX.cpp b/tests/modules/testCONEX.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..621ae626a95b5fe6b1e73953b5d0d94c5a71c4ba
--- /dev/null
+++ b/tests/modules/testCONEX.cpp
@@ -0,0 +1,149 @@
+/*
+ * (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.
+ */
+
+#include <corsika/setup/SetupEnvironment.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/LayeredSphericalAtmosphereBuilder.hpp>
+#include <corsika/media/MediumPropertyModel.hpp>
+#include <corsika/media/UniformMagneticField.hpp>
+
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+
+#include <corsika/modules/CONEX.hpp>
+#include <corsika/modules/Sibyll.hpp>
+
+#include <corsika/framework/random/RNGManager.hpp>
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/utility/CorsikaFenv.hpp>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+
+const std::string refDataDir = std::string(REFDATADIR); // from cmake
+
+template <typename T>
+using MExtraEnvirnoment = MediumPropertyModel<UniformMagneticField<T>>;
+
+TEST_CASE("CONEXSourceCut") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  RNGManager::getInstance().registerRandomStream("cascade");
+  RNGManager::getInstance().registerRandomStream("sibyll");
+
+  feenableexcept(FE_INVALID);
+
+  // setup environment, geometry
+  setup::Environment env;
+  CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
+  Point const center{rootCS, 0_m, 0_m, 0_m};
+
+  auto builder = make_layered_spherical_atmosphere_builder<
+      setup::EnvironmentInterface, MExtraEnvirnoment>::create(center,
+                                                              corsika::conex::earthRadius,
+                                                              Medium::AirDry1Atm,
+                                                              Vector{rootCS, 0_T, 50_mT,
+                                                                     0_T});
+
+  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);
+
+  builder.assemble(env);
+
+  const HEPEnergyType E0 = 1_PeV;
+  double thetaDeg = 60.;
+  auto const thetaRad = thetaDeg / 180. * M_PI;
+
+  auto const observationHeight = 1.4_km + corsika::conex::earthRadius;
+  auto const injectionHeight = 112.75_km + corsika::conex::earthRadius;
+  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 +
+      Vector<dimensionless_d>{rootCS, {-sin(thetaRad), 0, cos(thetaRad)}} * t;
+
+  ShowerAxis const showerAxis{injectionPos, (showerCore - injectionPos) * 1.02, env};
+
+  // need to initialize Sibyll, done in constructor:
+  corsika::sibyll::Interaction sibyll;
+  [[maybe_unused]] corsika::sibyll::NuclearInteraction sibyllNuc(sibyll, env);
+
+  CONEXhybrid conex(center, showerAxis, t, injectionHeight, E0, get_PDG(Code::Proton));
+
+  HEPEnergyType const Eem{1_PeV};
+  auto const momentum = showerAxis.getDirection() * Eem;
+
+  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;
+
+  conex.addParticle(Code::Proton, Eem, 0_eV, emPosition, momentum.normalized(), 0_s);
+  // supperimpose a photon
+  auto const momentumPhoton = showerAxis.getDirection() * 1_TeV;
+  conex.addParticle(Code::Gamma, 1_TeV, 0_eV, emPosition, momentumPhoton.normalized(),
+                    0_s);
+  conex.solveCE();
+}
+
+#include <algorithm>
+#include <iterator>
+#include <string>
+#include <fstream>
+
+TEST_CASE("ConexOutput", "[output validation]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  auto file = GENERATE(as<std::string>{}, "conex_fit", "conex_output");
+
+  SECTION(std::string("check saved data, ") + file + ".txt") {
+
+    // compare to reference data
+    std::ifstream file1(file + ".txt");
+    std::ifstream file1ref(refDataDir + "/" + file + "_REF.txt");
+
+    std::istreambuf_iterator<char> begin1(file1);
+    std::istreambuf_iterator<char> begin1ref(file1ref);
+
+    std::istreambuf_iterator<char> end;
+
+    while (begin1 != end && begin1ref != end) {
+      CHECK(*begin1 == *begin1ref);
+      ++begin1;
+      ++begin1ref;
+    }
+    CHECK(begin1 == end);
+    CHECK(begin1ref == end);
+    file1.close();
+    file1ref.close();
+  }
+}
diff --git a/Processes/AnalyticProcessors/testExecTime.cc b/tests/modules/testExecTime.cpp
similarity index 96%
rename from Processes/AnalyticProcessors/testExecTime.cc
rename to tests/modules/testExecTime.cpp
index e2fc710e7a201096184edac399befe01a8d04356..68a85d81b2af7343ed323777daef39f55286a69e 100644
--- a/Processes/AnalyticProcessors/testExecTime.cc
+++ b/tests/modules/testExecTime.cpp
@@ -6,17 +6,15 @@
  * the license.
  */
 
-#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one
-                          // cpp file
 #include <catch2/catch.hpp>
 
-#include <corsika/process/analytic_processors/ExecTime.h>
+#include <corsika/modules/ExecTime.hpp>
 
-#include <corsika/process/example_processors/DummyBoundaryCrossingProcess.h>
-#include <corsika/process/example_processors/DummyContinuousProcess.h>
-#include <corsika/process/example_processors/DummyDecayProcess.h>
-#include <corsika/process/example_processors/DummyInteractionProcess.h>
-#include <corsika/process/example_processors/DummySecondariesProcess.h>
+#include <corsika/process/example_processors/DummyBoundaryCrossingProcess.hpp>
+#include <corsika/process/example_processors/DummyContinuousProcess.hpp>
+#include <corsika/process/example_processors/DummyDecayProcess.hpp>
+#include <corsika/process/example_processors/DummyInteractionProcess.hpp>
+#include <corsika/process/example_processors/DummySecondariesProcess.hpp>
 
 #include <cmath>
 #include <random>
@@ -28,6 +26,9 @@ using namespace corsika::process::example_processors;
 
 TEST_CASE("Timing process", "[proccesses][analytic_processors ExecTime]") {
 
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
   int tmp = 0;
 
   SECTION("BoundaryCrossing") {
diff --git a/Processes/InteractionCounter/testInteractionCounter.cc b/tests/modules/testInteractionCounter.cpp
similarity index 65%
rename from Processes/InteractionCounter/testInteractionCounter.cc
rename to tests/modules/testInteractionCounter.cpp
index 5417f3e3ec90ea7955e705e6fb836efd22198f8f..350e1dc1de38a96a69d91941a37f0fb371eaccc3 100644
--- a/Processes/InteractionCounter/testInteractionCounter.cc
+++ b/tests/modules/testInteractionCounter.cpp
@@ -1,31 +1,28 @@
 /*
- * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu
+ * (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.
  */
 
-#include <corsika/process/interaction_counter/InteractionCounter.hpp>
+#include <corsika/modules/InteractionCounter.hpp>
 
-#include <corsika/environment/Environment.h>
-#include <corsika/environment/HomogeneousMedium.h>
-#include <corsika/environment/NuclearComposition.h>
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/RootCoordinateSystem.h>
-#include <corsika/geometry/Vector.h>
-#include <corsika/units/PhysicalUnits.h>
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
 
-#include <corsika/setup/SetupStack.h>
+#include <corsika/setup/SetupStack.hpp>
 
 #include <catch2/catch.hpp>
 
 #include <numeric>
 
 using namespace corsika;
-using namespace corsika::process::interaction_counter;
-using namespace corsika::units;
-using namespace corsika::units::si;
 
 const std::string refDataDir = std::string(REFDATADIR); // from cmake
 
@@ -43,13 +40,14 @@ struct DummyProcess {
 
 TEST_CASE("InteractionCounter", "[process]") {
 
-  logging::SetLevel(logging::level::debug);
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
 
   DummyProcess d;
   InteractionCounter countedProcess(d);
 
   SECTION("GetInteractionLength") {
-    REQUIRE(countedProcess.GetInteractionLength(nullptr) == 100_g / 1_cm / 1_cm);
+    CHECK(countedProcess.GetInteractionLength(nullptr) == 100_g / 1_cm / 1_cm);
   }
 
   auto [env, csPtr, nodePtr] = setup::testing::setupEnvironment(particles::Code::Oxygen);
@@ -59,19 +57,19 @@ TEST_CASE("InteractionCounter", "[process]") {
     unsigned short constexpr A = 14, Z = 7;
     auto [stackPtr, secViewPtr] = setup::testing::setupStack(particles::Code::Nucleus, A,
                                                              Z, 105_TeV, nodePtr, *csPtr);
-    REQUIRE(stackPtr->getEntries() == 1);
-    REQUIRE(secViewPtr->getEntries() == 0);
+    CHECK(stackPtr->getEntries() == 1);
+    CHECK(secViewPtr->getEntries() == 0);
 
     auto const ret = countedProcess.DoInteraction(*secViewPtr);
-    REQUIRE(ret == nullptr);
+    CHECK(ret == nullptr);
 
     auto const& h = countedProcess.GetHistogram().labHist();
-    REQUIRE(h.at(h.axis(0).index(1'000'070'140), h.axis(1).index(1.05e14)) == 1);
-    REQUIRE(std::accumulate(h.cbegin(), h.cend(), 0) == 1);
+    CHECK(h.at(h.axis(0).index(1'000'070'140), h.axis(1).index(1.05e14)) == 1);
+    CHECK(std::accumulate(h.cbegin(), h.cend(), 0) == 1);
 
     auto const& h2 = countedProcess.GetHistogram().CMSHist();
-    REQUIRE(h2.at(h2.axis(0).index(1'000'070'140), h2.axis(1).index(1.6e12)) == 1);
-    REQUIRE(std::accumulate(h2.cbegin(), h2.cend(), 0) == 1);
+    CHECK(h2.at(h2.axis(0).index(1'000'070'140), h2.axis(1).index(1.6e12)) == 1);
+    CHECK(std::accumulate(h2.cbegin(), h2.cend(), 0) == 1);
 
     countedProcess.GetHistogram().saveLab("testInteractionCounter_file1.npz",
                                           utl::SaveMode::overwrite);
@@ -83,19 +81,19 @@ TEST_CASE("InteractionCounter", "[process]") {
     auto constexpr code = particles::Code::Lambda0;
     auto [stackPtr, secViewPtr] =
         setup::testing::setupStack(code, 0, 0, 105_TeV, nodePtr, *csPtr);
-    REQUIRE(stackPtr->getEntries() == 1);
-    REQUIRE(secViewPtr->getEntries() == 0);
+    CHECK(stackPtr->getEntries() == 1);
+    CHECK(secViewPtr->getEntries() == 0);
 
     auto const ret = countedProcess.DoInteraction(*secViewPtr);
-    REQUIRE(ret == nullptr);
+    CHECK(ret == nullptr);
 
     auto const& h = countedProcess.GetHistogram().labHist();
-    REQUIRE(h.at(h.axis(0).index(3122), h.axis(1).index(1.05e14)) == 1);
-    REQUIRE(std::accumulate(h.cbegin(), h.cend(), 0) == 1);
+    CHECK(h.at(h.axis(0).index(3122), h.axis(1).index(1.05e14)) == 1);
+    CHECK(std::accumulate(h.cbegin(), h.cend(), 0) == 1);
 
     auto const& h2 = countedProcess.GetHistogram().CMSHist();
-    REQUIRE(h2.at(h2.axis(0).index(3122), h2.axis(1).index(1.6e12)) == 1);
-    REQUIRE(std::accumulate(h2.cbegin(), h2.cend(), 0) == 1);
+    CHECK(h2.at(h2.axis(0).index(3122), h2.axis(1).index(1.6e12)) == 1);
+    CHECK(std::accumulate(h2.cbegin(), h2.cend(), 0) == 1);
   }
 }
 
@@ -106,6 +104,9 @@ TEST_CASE("InteractionCounter", "[process]") {
 
 TEST_CASE("InteractionCounterOutput", "[output validation]") {
 
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
   auto file = GENERATE(as<std::string>{}, "testInteractionCounter_file1",
                        "testInteractionCounter_file2");
 
diff --git a/tests/modules/testNullModel.cpp b/tests/modules/testNullModel.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a24433b080b4272955f01dfb2d116b43e0d5a2d6
--- /dev/null
+++ b/tests/modules/testNullModel.cpp
@@ -0,0 +1,23 @@
+/*
+ * (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.
+ */
+
+#include <catch2/catch.hpp>
+
+#include <corsika/framework/process/NullModel.hpp>
+
+using namespace corsika;
+
+TEST_CASE("NullModel", "[processes]") {
+
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  logging::set_level(logging::level::trace);
+
+  SECTION("interface") {
+    [[maybe_unused]] NullModel model; // nothing to test...
+  }
+}
diff --git a/tests/modules/testObservationPlane.cpp b/tests/modules/testObservationPlane.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9b9be4656d447b76552d63ae8e6b83657effa3d2
--- /dev/null
+++ b/tests/modules/testObservationPlane.cpp
@@ -0,0 +1,81 @@
+/*
+ * (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.
+ */
+
+#include <catch2/catch.hpp>
+
+#include <corsika/modules/ObservationPlane.hpp>
+
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <SetupTestEnvironment.hpp>
+#include <SetupTestStack.hpp>
+#include <SetupTestTrajectory.hpp>
+
+using namespace corsika;
+
+TEST_CASE("ContinuousProcess interface", "[proccesses][observation_plane]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Oxygen);
+  auto const& cs = *csPtr;
+  [[maybe_unused]] auto const& env_dummy = env;
+  [[maybe_unused]] auto const& node_dummy = nodePtr;
+
+  /*
+    Test with downward going 1_GeV neutrino, starting at 0,1_m,10m
+
+    ObservationPlane has origin at 0,0,0
+   */
+  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();
+
+  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::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",
+                         true);
+
+    LengthType const length = obs.getMaxStepLength(particle, track);
+    ProcessReturn const ret = obs.doContinuous(particle, track);
+
+    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",
+                         false);
+
+    LengthType const length = obs.getMaxStepLength(particle, track);
+    ProcessReturn const ret = obs.doContinuous(particle, track);
+
+    CHECK(length / 10_m == Approx(1).margin(1e-4));
+    CHECK(ret == ProcessReturn::Ok);
+  }
+}
diff --git a/tests/modules/testOnShellCheck.cpp b/tests/modules/testOnShellCheck.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..f7577569c8188afd5d79fd90490d703911ba4943
--- /dev/null
+++ b/tests/modules/testOnShellCheck.cpp
@@ -0,0 +1,81 @@
+/*
+ * (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/modules/OnShellCheck.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/framework/geometry/FourVector.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/utility/CorsikaFenv.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+
+TEST_CASE("OnShellCheck", "[processes]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  feenableexcept(FE_INVALID);
+  using EnvType = setup::Environment;
+  EnvType env;
+  CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
+
+  // setup empty particle stack
+  setup::Stack stack;
+  stack.clear();
+  // two energies
+  const HEPEnergyType E = 10_GeV;
+  // list of arbitrary particles
+  std::array const particleList{Code::PiPlus, Code::PiMinus, Code::Helium, Code::Gamma};
+
+  std::array const mass_shifts{1.1, 1.001, 1.0, 1.0};
+
+  SECTION("check particle masses") {
+
+    OnShellCheck check(1.e-2, 0.01, false);
+
+    // add primary particle to stack
+    auto particle = stack.addParticle(
+        std::make_tuple(Code::Proton, E, MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
+                        Point(rootCS, 0_m, 0_m, 0_m), 0_ns));
+    // view on secondary particles
+    setup::StackView view{particle};
+    // ref. to primary particle through the secondary view.
+    // only this way the secondary view is populated
+    auto projectile = view.getProjectile();
+    // add secondaries, all with energies above the threshold
+    // only cut is by species
+    int count = -1;
+    for (auto proType : particleList) {
+      count++;
+      const auto pz = sqrt((E - get_mass(proType) * mass_shifts[count]) *
+                           (E + get_mass(proType) * mass_shifts[count]));
+      auto const momentum = MomentumVector(rootCS, {0_GeV, 0_GeV, pz});
+      projectile.addSecondary(
+          std::make_tuple(proType, E, momentum, Point(rootCS, 0_m, 0_m, 0_m), 0_ns));
+    }
+    check.doSecondaries(view);
+    int i = -1;
+    for (auto& p : view) {
+      i++;
+      auto const Plab = FourVector(p.getEnergy(), p.getMomentum());
+      auto const m_kinetic = Plab.getNorm();
+      if (i == 0)
+        CHECK(m_kinetic / PiPlus::mass == Approx(1));
+      else if (i == 1)
+        CHECK_FALSE(m_kinetic / PiMinus::mass == Approx(1));
+    }
+  }
+}
diff --git a/tests/modules/testParticleCut.cpp b/tests/modules/testParticleCut.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..a8894230196e5041482cd53d267d1786a5a05a21
--- /dev/null
+++ b/tests/modules/testParticleCut.cpp
@@ -0,0 +1,187 @@
+/*
+ * (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.
+ */
+
+#include <corsika/modules/ParticleCut.hpp>
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/utility/CorsikaFenv.hpp>
+#include <corsika/media/Environment.hpp>
+
+#include <SetupTestStack.hpp>
+#include <SetupTestTrajectory.hpp>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+
+TEST_CASE("ParticleCut", "[processes]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  feenableexcept(FE_INVALID);
+  using EnvType = setup::Environment;
+
+  EnvType env;
+  CoordinateSystemPtr const& rootCS = env.getCoordinateSystem();
+
+  // setup empty particle stack
+  setup::Stack stack;
+  stack.clear();
+  // two energies
+  HEPEnergyType const Eabove = 1_TeV;
+  HEPEnergyType const Ebelow = 10_GeV;
+  // list of arbitrary particles
+  std::vector<Code> const particleList = {Code::PiPlus,   Code::PiMinus, Code::KPlus,
+                                          Code::KMinus,   Code::K0Long,  Code::K0Short,
+                                          Code::Electron, Code::MuPlus,  Code::NuE,
+                                          Code::Neutron,  Code::NuMu};
+
+  // common stating point
+  const Point point0(rootCS, 0_m, 0_m, 0_m);
+
+  SECTION("cut on particle type: inv") {
+
+    ParticleCut cut(20_GeV, false, true);
+    CHECK(cut.getECut() == 20_GeV);
+
+    // add primary particle to stack
+    auto particle = stack.addParticle(std::make_tuple(
+        Code::Proton, Eabove, MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
+        Point(rootCS, 0_m, 0_m, 0_m), 0_ns));
+    // view on secondary particles
+    setup::StackView view(particle);
+    // ref. to primary particle through the secondary view.
+    // only this way the secondary view is populated
+    auto projectile = view.getProjectile();
+    // add secondaries, all with energies above the threshold
+    // only cut is by species
+    for (auto proType : particleList)
+      projectile.addSecondary(std::make_tuple(
+          proType, Eabove, MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), point0, 0_ns));
+    CHECK(view.getEntries() == 11);
+    CHECK(stack.getEntries() == 12);
+
+    cut.doSecondaries(view);
+
+    CHECK(view.getEntries() == 9);
+    CHECK(cut.getNumberInvParticles() == 2);
+    CHECK(cut.getInvEnergy() / 1_GeV == 2000);
+  }
+
+  SECTION("cut on particle type: em") {
+
+    ParticleCut cut(20_GeV, true, false);
+
+    // add primary particle to stack
+    auto particle = stack.addParticle(
+        std::make_tuple(Code::Proton, Eabove,
+                        MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), point0, 0_ns));
+    // view on secondary particles
+    setup::StackView view(particle);
+    // ref. to primary particle through the secondary view.
+    // only this way the secondary view is populated
+    auto projectile = view.getProjectile();
+    // add secondaries, all with energies above the threshold
+    // only cut is by species
+    for (auto proType : particleList) {
+      projectile.addSecondary(std::make_tuple(
+          proType, Eabove, MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), point0, 0_ns));
+    }
+    cut.doSecondaries(view);
+
+    CHECK(view.getEntries() == 10);
+    CHECK(cut.getNumberEmParticles() == 1);
+    CHECK(cut.getEmEnergy() / 1_GeV == 1000);
+  }
+
+  SECTION("cut low energy") {
+    ParticleCut cut(20_GeV, true, true);
+
+    // add primary particle to stack
+    auto particle = stack.addParticle(
+        std::make_tuple(Code::Proton, Eabove,
+                        MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), point0, 0_ns));
+    // view on secondary particles
+    setup::StackView view(particle);
+    // ref. to primary particle through the secondary view.
+    // only this way the secondary view is populated
+    auto projectile = view.getProjectile();
+    // add secondaries, all with energies below the threshold
+    // only cut is by species
+    for (auto proType : particleList)
+      projectile.addSecondary(std::make_tuple(
+          proType, Ebelow, MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), point0, 0_ns));
+    unsigned short A = 18;
+    unsigned short Z = 8;
+    projectile.addSecondary(std::make_tuple(Code::Nucleus, Eabove * A,
+                                            MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
+                                            point0, 0_ns, A, Z));
+    projectile.addSecondary(std::make_tuple(Code::Nucleus, Ebelow * A,
+                                            MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
+                                            point0, 0_ns, A, Z));
+
+    cut.doSecondaries(view);
+
+    CHECK(view.getEntries() == 1);
+    CHECK(view.getSize() == 13);
+  }
+
+  SECTION("cut on time") {
+    ParticleCut cut(20_GeV, false, false);
+    const TimeType too_late = 1_s;
+
+    // add primary particle to stack
+    auto particle = stack.addParticle(
+        std::make_tuple(Code::Proton, Eabove,
+                        MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), point0, 1_ns));
+    // view on secondary particles
+    setup::StackView view(particle);
+    // ref. to primary particle through the secondary view.
+    // only this way the secondary view is populated
+    auto projectile = view.getProjectile();
+    // add secondaries, all with energies above the threshold
+    // only cut is by species
+    for (auto proType : particleList) {
+      projectile.addSecondary(
+          std::make_tuple(proType, Eabove, MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}),
+                          point0, too_late));
+    }
+    cut.doSecondaries(view);
+
+    CHECK(view.getEntries() == 0);
+    CHECK(cut.getCutEnergy() / 1_GeV == 11000);
+    cut.reset();
+    CHECK(cut.getCutEnergy() == 0_GeV);
+  }
+
+  setup::Trajectory const track = setup::testing::make_track<setup::Trajectory>(
+      Line{point0, VelocityVector{rootCS, {0_m / second, 0_m / second, -constants::c}}},
+      12_m / constants::c);
+
+  SECTION("cut on DoContinous, just invisibles") {
+
+    ParticleCut cut(20_GeV, false, true);
+    CHECK(cut.getECut() == 20_GeV);
+
+    // add particles, all with energies above the threshold
+    // only cut is by species
+    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);
+    }
+
+    CHECK(stack.getEntries() == 9);
+    CHECK(cut.getNumberInvParticles() == 2);
+    CHECK(cut.getInvEnergy() / 1_GeV == 2000);
+  }
+}
diff --git a/tests/modules/testPythia8.cpp b/tests/modules/testPythia8.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..4f93280f513ffdf243259d99dc4ea675bf60cb83
--- /dev/null
+++ b/tests/modules/testPythia8.cpp
@@ -0,0 +1,172 @@
+/*
+ * (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.
+ */
+
+#include <corsika/modules/Pythia8.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+
+#include <SetupTestEnvironment.hpp>
+#include <SetupTestStack.hpp>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+
+TEST_CASE("Pythia", "[processes]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  SECTION("linking pythia") {
+    using namespace Pythia8;
+    using std::cout;
+    using std::endl;
+
+    // Generator. Process selection. LHC initialization. Histogram.
+    Pythia pythia;
+
+    pythia.readString("Next:numberShowInfo = 0");
+    pythia.readString("Next:numberShowProcess = 0");
+    pythia.readString("Next:numberShowEvent = 0");
+
+    pythia.readString("ProcessLevel:all = off");
+
+    pythia.init();
+
+    Event& event = pythia.event;
+    event.reset();
+
+    pythia.particleData.mayDecay(321, true);
+    double pz = 100.;
+    double m = 0.49368;
+    event.append(321, 1, 0, 0, 0., 0., 100., sqrt(pz * pz + m * m), m);
+
+    if (!pythia.next())
+      cout << "decay failed!" << endl;
+    else
+      cout << "particles after decay: " << event.size() << endl;
+    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;
+      }
+  }
+
+  SECTION("pythia interface") {
+
+    std::set<Code> const particleList = {Code::PiPlus, Code::PiMinus, Code::KPlus,
+                                         Code::KMinus, Code::K0Long,  Code::K0Short};
+    RNGManager::getInstance().registerRandomStream("pythia");
+    corsika::pythia8::Decay model(particleList);
+  }
+}
+
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+
+using namespace corsika;
+
+template <typename TStackView>
+auto sumMomentum(TStackView const& view, CoordinateSystemPtr const& vCS) {
+  MomentumVector sum{vCS, 0_eV, 0_eV, 0_eV};
+  for (auto const& p : view) { sum += p.getMomentum(); }
+  return sum;
+}
+
+TEST_CASE("pythia process") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Proton);
+  auto const& cs = *csPtr;
+  [[maybe_unused]] auto const& env_dummy = env;
+  [[maybe_unused]] auto const& node_dummy = nodePtr;
+
+  SECTION("pythia decay") {
+    HEPEnergyType const P0 = 10_GeV;
+    // HEPMomentumType const E0 = sqrt(P0*P0 + PiPlus::mass*PiPlus::mass);
+
+    // feenableexcept(FE_INVALID); \todo how does this work nowadays...???
+    auto [stackPtr, secViewPtr] = setup::testing::setup_stack(
+        Code::PiPlus, 0, 0, P0, (setup::Environment::BaseNodeType* const)nodePtr, *csPtr);
+    auto& stack = *stackPtr;
+    auto& view = *secViewPtr;
+
+    auto const& particle = stack.getNextParticle();
+    auto const plab = MomentumVector(cs, {P0, 0_GeV, 0_GeV});
+
+    std::set<Code> const particleList = {Code::PiPlus, Code::PiMinus, Code::KPlus,
+                                         Code::KMinus, Code::K0Long,  Code::K0Short};
+
+    RNGManager::getInstance().registerRandomStream("pythia");
+
+    corsika::pythia8::Decay model(particleList);
+
+    [[maybe_unused]] const TimeType time = model.getLifetime(particle);
+    model.doDecay(*secViewPtr);
+    CHECK(stack.getEntries() == 3);
+    auto const pSum = sumMomentum(view, cs);
+    CHECK((pSum - plab).getNorm() / 1_GeV == Approx(0).margin(1e-4));
+    CHECK((pSum.getNorm() - plab.getNorm()) / 1_GeV == Approx(0).margin(1e-4));
+  }
+
+  SECTION("pythia decay config") {
+    corsika::pythia8::Decay model({Code::PiPlus, Code::PiMinus});
+    CHECK(model.isDecayHandled(Code::PiPlus));
+    CHECK(model.isDecayHandled(Code::PiMinus));
+    CHECK_FALSE(model.isDecayHandled(Code::KPlus));
+
+    const std::vector<Code> particleTestList = {Code::PiPlus, Code::PiMinus, Code::KPlus,
+                                                Code::Lambda0Bar, Code::D0Bar};
+
+    // setup decays
+    model.setHandleDecay(particleTestList);
+    for (auto& pCode : particleTestList) CHECK(model.isDecayHandled(pCode));
+
+    // individually
+    model.setHandleDecay(Code::KMinus);
+
+    // possible decays
+    CHECK_FALSE(model.canHandleDecay(Code::Proton));
+    CHECK_FALSE(model.canHandleDecay(Code::Electron));
+    CHECK(model.canHandleDecay(Code::PiPlus));
+    CHECK(model.canHandleDecay(Code::MuPlus));
+  }
+
+  SECTION("pythia interaction") {
+
+    // feenableexcept(FE_INVALID); \todo how does this work nowadays
+    auto [stackPtr, secViewPtr] = setup::testing::setup_stack(
+        Code::PiPlus, 0, 0, 100_GeV, (setup::Environment::BaseNodeType* const)nodePtr,
+        *csPtr);
+    auto& view = *secViewPtr;
+    auto particle = stackPtr->first();
+
+    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));
+  }
+}
diff --git a/tests/modules/testQGSJetII.cpp b/tests/modules/testQGSJetII.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..3ddbb254c133050e07b5ec257683767df8508be7
--- /dev/null
+++ b/tests/modules/testQGSJetII.cpp
@@ -0,0 +1,163 @@
+/*
+ * (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.
+ */
+
+#include <corsika/modules/qgsjetII/Interaction.hpp>
+#include <corsika/modules/qgsjetII/ParticleConversion.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+
+#include <catch2/catch.hpp>
+
+#include <string>
+#include <cstdlib>
+#include <experimental/filesystem>
+#include <iostream>
+
+using namespace corsika;
+
+template <typename TStackView>
+auto sumCharge(TStackView const& view) {
+  int totalCharge = 0;
+  for (auto const& p : view) { totalCharge += get_charge_number(p.getPID()); }
+  return totalCharge;
+}
+
+template <typename TStackView>
+auto sumMomentum(TStackView const& view, CoordinateSystemPtr const& vCS) {
+  Vector<hepenergy_d> sum{vCS, 0_eV, 0_eV, 0_eV};
+  for (auto const& p : view) { sum += p.getMomentum(); }
+  return sum;
+}
+
+TEST_CASE("CORSIKA_DATA", "[processes]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  SECTION("check CORSIKA_DATA") {
+
+    const char* data = std::getenv("CORSIKA_DATA");
+    // these CHECKS are needed:
+    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;
+  }
+}
+
+TEST_CASE("QgsjetII", "[processes]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  SECTION("Corsika -> QgsjetII") {
+    CHECK(corsika::qgsjetII::convertToQgsjetII(PiMinus::code) ==
+          corsika::qgsjetII::QgsjetIICode::PiMinus);
+    CHECK(corsika::qgsjetII::convertToQgsjetIIRaw(Proton::code) == 2);
+  }
+
+  SECTION("QgsjetII -> Corsika") {
+    CHECK(Code::PiPlus == corsika::qgsjetII::convertFromQgsjetII(
+                              corsika::qgsjetII::QgsjetIICode::PiPlus));
+  }
+
+  SECTION("Corsika -> QgsjetII") {
+    CHECK(corsika::qgsjetII::convertToQgsjetII(Code::PiMinus) ==
+          corsika::qgsjetII::QgsjetIICode::PiMinus);
+    CHECK(corsika::qgsjetII::convertToQgsjetIIRaw(Code::Proton) == 2);
+  }
+
+  SECTION("canInteractInQgsjetII") {
+
+    CHECK(corsika::qgsjetII::canInteract(Code::Proton));
+    CHECK(corsika::qgsjetII::canInteract(Code::KPlus));
+    CHECK(corsika::qgsjetII::canInteract(Code::Nucleus));
+    // CHECK(corsika::qgsjetII::canInteract(Helium::getCode()));
+
+    CHECK_FALSE(corsika::qgsjetII::canInteract(Code::EtaC));
+    CHECK_FALSE(corsika::qgsjetII::canInteract(Code::SigmaC0));
+  }
+
+  SECTION("cross-section type") {
+
+    CHECK(corsika::qgsjetII::getQgsjetIIXSCode(Code::Neutron) ==
+          corsika::qgsjetII::QgsjetIIXSClass::Baryons);
+    CHECK(corsika::qgsjetII::getQgsjetIIXSCode(Code::K0Long) ==
+          corsika::qgsjetII::QgsjetIIXSClass::Kaons);
+    CHECK(corsika::qgsjetII::getQgsjetIIXSCode(Code::Proton) ==
+          corsika::qgsjetII::QgsjetIIXSClass::Baryons);
+    CHECK(corsika::qgsjetII::getQgsjetIIXSCode(Code::PiMinus) ==
+          corsika::qgsjetII::QgsjetIIXSClass::LightMesons);
+  }
+}
+
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <corsika/setup/SetupStack.hpp>
+#include <corsika/setup/SetupTrajectory.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+
+#include <SetupTestEnvironment.hpp>
+#include <SetupTestStack.hpp>
+
+TEST_CASE("QgsjetIIInterface", "[processes]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Oxygen);
+  [[maybe_unused]] auto const& env_dummy = env;
+  [[maybe_unused]] auto const& node_dummy = nodePtr;
+
+  RNGManager::getInstance().registerRandomStream("qgsjet");
+
+  SECTION("InteractionInterface") {
+
+    auto [stackPtr, secViewPtr] = setup::testing::setup_stack(
+        Code::Proton, 0, 0, 110_GeV, (setup::Environment::BaseNodeType* const)nodePtr,
+        *csPtr);
+    setup::StackView& view = *(secViewPtr.get());
+    auto particle = stackPtr->first();
+    auto projectile = secViewPtr->getProjectile();
+    auto const projectileMomentum = projectile.getMomentum();
+
+    corsika::qgsjetII::Interaction model;
+    model.doInteraction(view);
+    [[maybe_unused]] const GrammageType length = model.getInteractionLength(particle);
+
+    CHECK(length / (1_g / square(1_cm)) == Approx(93.04).margin(0.1));
+
+    /***********************************
+     It as turned out already two times (#291 and #307) that the detailed output of
+    QGSJetII event generation depends on the gfortran version used. This is not reliable
+    and cannot be tested in a unit test here. One related problem was already found (#291)
+    and is realted to undefined behaviour in the evaluation of functions in logical
+    expressions. It is not clear if #307 is the same issue.
+
+     CHECK(view.getSize() == 14);
+     CHECK(sumCharge(view) == 2);
+    ************************************/
+    auto const secMomSum = sumMomentum(view, projectileMomentum.getCoordinateSystem());
+    CHECK((secMomSum - projectileMomentum).getNorm() / projectileMomentum.getNorm() ==
+          Approx(0).margin(1e-2));
+  }
+}
diff --git a/tests/modules/testSibyll.cpp b/tests/modules/testSibyll.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6e35c6a1d0bd84249717303dd7eb908f7d28c48c
--- /dev/null
+++ b/tests/modules/testSibyll.cpp
@@ -0,0 +1,258 @@
+/*
+ * (c) Copyright 2019 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/modules/Sibyll.hpp>
+#include <corsika/modules/sibyll/ParticleConversion.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+
+#include <catch2/catch.hpp>
+#include <tuple>
+
+/*
+  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/sibyll/Random.hpp>
+
+using namespace corsika;
+using namespace corsika::sibyll;
+
+TEST_CASE("Sibyll", "[processes]") {
+
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  logging::set_level(logging::level::trace);
+
+  SECTION("Sibyll -> Corsika") {
+    CHECK(Code::Electron ==
+          corsika::sibyll::convertFromSibyll(corsika::sibyll::SibyllCode::Electron));
+  }
+
+  SECTION("Corsika -> Sibyll") {
+    CHECK(corsika::sibyll::convertToSibyll(Electron::code) ==
+          corsika::sibyll::SibyllCode::Electron);
+    CHECK(corsika::sibyll::convertToSibyllRaw(Proton::code) == 13);
+    CHECK(corsika::sibyll::convertToSibyll(XiStarC0::code) ==
+          corsika::sibyll::SibyllCode::XiStarC0);
+  }
+
+  SECTION("canInteractInSibyll") {
+
+    CHECK(corsika::sibyll::canInteract(Code::Proton));
+    CHECK(corsika::sibyll::canInteract(Code::XiCPlus));
+
+    CHECK_FALSE(corsika::sibyll::canInteract(Code::Electron));
+    CHECK_FALSE(corsika::sibyll::canInteract(Code::SigmaC0));
+
+    CHECK_FALSE(corsika::sibyll::canInteract(Code::Nucleus));
+    CHECK_FALSE(corsika::sibyll::canInteract(Code::Helium));
+  }
+
+  SECTION("cross-section type") {
+
+    CHECK(corsika::sibyll::getSibyllXSCode(Code::Electron) == 0);
+    CHECK(corsika::sibyll::getSibyllXSCode(Code::K0Long) == 3);
+    CHECK(corsika::sibyll::getSibyllXSCode(Code::SigmaPlus) == 1);
+    CHECK(corsika::sibyll::getSibyllXSCode(Code::PiMinus) == 2);
+  }
+
+  SECTION("sibyll mass") {
+
+    CHECK_FALSE(corsika::sibyll::getSibyllMass(Code::Electron) == 0_GeV);
+  }
+}
+
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+
+#include <SetupTestEnvironment.hpp>
+#include <SetupTestStack.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+#include <corsika/media/UniformMagneticField.hpp>
+
+template <typename TStackView>
+auto sumMomentum(TStackView const& view, CoordinateSystemPtr const& vCS) {
+  Vector<hepenergy_d> sum{vCS, 0_eV, 0_eV, 0_eV};
+  for (auto const& p : view) { sum += p.getMomentum(); }
+  return sum;
+}
+
+TEST_CASE("SibyllInterface", "[processes]") {
+
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+  logging::set_level(logging::level::trace);
+
+  auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Oxygen);
+  auto const& cs = *csPtr;
+  [[maybe_unused]] auto const& env_dummy = env;
+
+  RNGManager::getInstance().registerRandomStream("sibyll");
+
+  SECTION("InteractionInterface - low energy") {
+
+    const HEPEnergyType P0 = 60_GeV;
+    auto [stack, viewPtr] = setup::testing::setup_stack(
+        Code::Proton, 0, 0, P0, (setup::Environment::BaseNodeType* const)nodePtr, cs);
+    MomentumVector plab =
+        MomentumVector(cs, {P0, 0_eV, 0_eV}); // this is secret knowledge about setupStack
+    setup::StackView& view = *viewPtr;
+
+    auto particle = stack->first();
+
+    Interaction model;
+    model.doInteraction(view);
+    auto const pSum = sumMomentum(view, cs);
+
+    /*
+      Interactions between hadrons (h) and nuclei (A) in Sibyll are treated in the
+      hadron-nucleon center-of-mass frame (hnCoM). The incoming hadron (h) and
+      nucleon (N) are assumed massless, such that the energy and momentum in the hnCoM are
+      : E_i_cm = 0.5 * SQS and P_i_cm = +- 0.5 * SQS  where i is either the projectile
+      hadron or the target nucleon and SQS is the hadron-nucleon center-of-mass energy.
+
+      The true energies and momenta, accounting for the hadron masses, are: E_i = ( S +
+      m_i**2 - m_j**2 ) / (2 * SQS) and Pcm = +-
+      sqrt( (S-(m_j+m_i)**2) * (s-(m_j-m_i)**2) ) / (2*SQS) where m_i is the projectiles
+      mass and m_j is the target particles mass. In terms of lab. frame variables Pcm =
+      m_j * Plab_i / SQS, where Plab_i is the momentum of the projectile (i) in the lab.
+      and m_j is the mass of the target, i.e. the particle at rest (usually a nucleon).
+
+      Any hadron-nucleus event can contain several nucleon interactions. In case of Nw
+      (number of wounded nucleons) nucleons interacting in the hadron-nucleus interaction,
+      the total energy and momentum in the hadron(i)-nucleon(N) center-of-mass frame are:
+      momentum: p_projectile + p_nucleon_1 + p_nucleon_2 + .... p_nucleon_Nw = -(Nw-1) *
+      Pcm with center-of-mass momentum Pcm = p_projectile = - p_nucleon_i. For the energy:
+      E_projectile + E_nucleon_1 + ... E_nucleon_Nw = E_projectile + Nw * E_nucleon.
+
+      Using the above definitions of center-of-mass energies and momenta this leads to the
+      total energy: E_tot = SQS/2 * (1+Nw) + (m_N**2-m_i**2)/(2*SQS) * (Nw-1) and P_tot
+      = -m_N * Plab_i / SQS * (Nw-1).
+
+      A Lorentztransformation of these quantities to the lab. frame recovers Plab_i for
+      the total momentum, so momentum is exactly conserved, and Elab_i + Nw * m_N for the
+      total energy. Not surprisingly the total energy differs from the total energy before
+      the collision by the mass of the additional nucleons (Nw-1)*m_N. In relative terms
+      the additional energy is entirely negligible and as it is not kinetic energy there
+      is zero influence on the shower development.
+
+      Due to the ommission of the hadron masses in Sibyll, the total energy and momentum
+      in the center-of-mass system after the collision are just: E_tot = SQS/2 * (1+Nw)
+      and P_tot = SQS/2 * (1-Nw). After the Lorentztransformation the total momentum in
+      the lab. thus differs from the initial value by (1-Nw)/2 * ( m_N + m_i**2 / (2 *
+      Plab_i) ) and momentum is NOT conserved. Note however that the second term quickly
+      vanishes as the lab. momentum of the projectile increases. The first term is fixed
+      as it depends only on the number of additional nucleons, in relative terms it is
+      always small at high energies.
+
+      For this reason the numerical precision in these tests is limited to 5% to still
+      pass at low energies and no absolute check is implemented, e.g.
+
+          CHECK(pSum.getComponents(cs).getX() / P0 == Approx(1).margin(0.05));
+          CHECK((pSum - plab).norm()/1_GeV == Approx(0).margin(plab.norm() * 0.05/1_GeV));
+
+      /FR'2020
+
+      See also:
+
+      Issue 272 / MR 204
+      https://gitlab.ikp.kit.edu/AirShowerPhysics/corsika/-/merge_requests/204
+
+    */
+
+    CHECK(pSum.getComponents(cs).getX() / P0 == Approx(1).margin(0.05));
+    CHECK(pSum.getComponents(cs).getY() / 1_GeV == Approx(0).margin(1e-4));
+    CHECK(pSum.getComponents(cs).getZ() / 1_GeV == Approx(0).margin(1e-4));
+
+    CHECK((pSum - plab).getNorm() / 1_GeV ==
+          Approx(0).margin(plab.getNorm() * 0.05 / 1_GeV));
+    CHECK(pSum.getNorm() / P0 == Approx(1).margin(0.05));
+    [[maybe_unused]] const GrammageType length = model.getInteractionLength(particle);
+    CHECK(length / 1_g * 1_cm * 1_cm == Approx(88.7).margin(0.1));
+    // CHECK(view.getEntries() == 9); //! \todo: this was 20 before refactory-2020: check
+    //                                           also sibyll not stable wrt. to compiler
+    //                                           changes
+  }
+
+  SECTION("NuclearInteractionInterface") {
+
+    auto [stack, viewPtr] =
+        setup::testing::setup_stack(Code::Nucleus, 4, 2, 500_GeV,
+                                    (setup::Environment::BaseNodeType* const)nodePtr, cs);
+    setup::StackView& view = *viewPtr;
+    auto particle = stack->first();
+
+    Interaction hmodel;
+    NuclearInteraction model(hmodel, *env);
+
+    model.doInteraction(view);
+    [[maybe_unused]] const GrammageType length = model.getInteractionLength(particle);
+    // Felix, are those changes OK? Below are the checks before refactory-2020
+    // CHECK(length / 1_g * 1_cm * 1_cm == Approx(44.2).margin(.1));
+    // CHECK(view.getSize() == 11);
+    CHECK(length / 1_g * 1_cm * 1_cm == Approx(42.8).margin(.1));
+    // CHECK(view.getSize() == 20); // also sibyll not stable wrt. to compiler changes
+  }
+
+  SECTION("DecayInterface") {
+
+    auto [stackPtr, viewPtr] =
+        setup::testing::setup_stack(Code::Lambda0, 0, 0, 10_GeV,
+                                    (setup::Environment::BaseNodeType* const)nodePtr, cs);
+    setup::StackView& view = *viewPtr;
+    auto& stack = *stackPtr;
+    auto particle = stack.first();
+
+    Decay model;
+    model.printDecayConfig();
+    [[maybe_unused]] const TimeType time = model.getLifetime(particle);
+
+    model.doDecay(view);
+    // run checks
+    // lambda decays into proton and pi- or neutron and pi+
+    CHECK(stack.getEntries() == 3);
+  }
+
+  SECTION("DecayConfiguration") {
+
+    Decay model({Code::PiPlus, Code::PiMinus});
+    CHECK(model.isDecayHandled(Code::PiPlus));
+    CHECK(model.isDecayHandled(Code::PiMinus));
+    CHECK_FALSE(model.isDecayHandled(Code::KPlus));
+
+    const std::vector<Code> particleTestList = {Code::PiPlus, Code::PiMinus, Code::KPlus,
+                                                Code::Lambda0Bar, Code::D0Bar};
+
+    // setup decays
+    model.setHandleDecay(particleTestList);
+    for (auto& pCode : particleTestList) CHECK(model.isDecayHandled(pCode));
+
+    // individually
+    model.setHandleDecay(Code::KMinus);
+
+    // possible decays
+    CHECK_FALSE(model.canHandleDecay(Code::Proton));
+    CHECK_FALSE(model.canHandleDecay(Code::Electron));
+    CHECK(model.canHandleDecay(Code::PiPlus));
+    CHECK(model.canHandleDecay(Code::MuPlus));
+  }
+}
diff --git a/tests/modules/testStackInspector.cpp b/tests/modules/testStackInspector.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..00187ed9addbf6076641540dd494beccbd85758b
--- /dev/null
+++ b/tests/modules/testStackInspector.cpp
@@ -0,0 +1,47 @@
+/*
+ * (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.
+ */
+
+#include <catch2/catch.hpp>
+
+#include <corsika/modules/StackInspector.hpp>
+
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/geometry/PhysicalGeometry.hpp>
+
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <../framework/testCascade.hpp> //! \todo fix this
+
+using namespace corsika;
+
+TEST_CASE("StackInspector", "[processes]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  auto const& rootCS = get_root_CoordinateSystem();
+  Point const origin(rootCS, {0_m, 0_m, 0_m});
+  VelocityVector v(rootCS, 0_m / second, 0_m / second, 1_m / second);
+  Line line(origin, v);
+  StraightTrajectory track(line, 10_s);
+
+  TestCascadeStack stack;
+  stack.clear();
+  HEPEnergyType E0 = 100_GeV;
+  stack.addParticle(std::make_tuple(Code::Electron, E0,
+                                    MomentumVector(rootCS, {0_GeV, 0_GeV, -1_GeV}),
+                                    Point(rootCS, {0_m, 0_m, 10_km}), 0_ns));
+
+  SECTION("interface") {
+
+    StackInspector<TestCascadeStack> model(1, true, E0);
+    model.doStack(stack);
+  }
+}
diff --git a/Processes/Tracking/testTracking.cc b/tests/modules/testTracking.cpp
similarity index 52%
rename from Processes/Tracking/testTracking.cc
rename to tests/modules/testTracking.cpp
index aabd87603fdc888fbc4d5a2a3a39fde50a26adb1..87cd51e93baf36dd89430c230b0d97e9c5ee9357 100644
--- a/Processes/Tracking/testTracking.cc
+++ b/tests/modules/testTracking.cpp
@@ -6,40 +6,31 @@
  * the license.
  */
 
-#include <corsika/process/tracking_leapfrog_curved/Tracking.h>
-#include <corsika/process/tracking_leapfrog_straight/Tracking.h>
-#include <corsika/process/tracking_line/Tracking.h>
+#include <corsika/modules/Tracking.hpp>
 
-#include <corsika/particles/ParticleProperties.h>
+#include <corsika/framework/core/ParticleProperties.hpp>
 
-#include <corsika/geometry/Point.h>
-#include <corsika/geometry/Sphere.h>
-#include <corsika/geometry/Vector.h>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/Sphere.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
 
-#include <corsika/setup/SetupEnvironment.h>
-#include <corsika/setup/SetupStack.h>
-#include <corsika/setup/SetupTrajectory.h>
-using corsika::setup::Trajectory;
+#include <SetupTestEnvironment.hpp>
+#include <SetupTestStack.hpp>
+#include <SetupTestTrajectory.hpp>
 
 #include <catch2/catch.hpp>
 
 using namespace corsika;
-using namespace corsika::particles;
-using namespace corsika::process;
-using namespace corsika::units;
-using namespace corsika::geometry;
-using namespace corsika::units::si;
-
-typedef corsika::geometry::Vector<corsika::units::si::magnetic_flux_density_d>
-    MagneticFieldVector;
 
 template <typename T>
 int sgn(T val) {
   return (T(0) < val) - (val < T(0));
 }
 
-/*
-  This is the unified and commond unit test for Tracking:
+/**
+ \file testTracking.cpp
+
+  This is the unified and commond unit test for all Tracking algorithms:
 
   - tracking_leapfrog_curved::Tracking
   - tracking_leapfrog_straight::Tracking
@@ -51,7 +42,10 @@ TEMPLATE_TEST_CASE("TrackingLeapfrog_Curved", "tracking",
                    tracking_leapfrog_curved::Tracking,
                    tracking_leapfrog_straight::Tracking, tracking_line::Tracking) {
 
-  logging::SetLevel(logging::level::trace);
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  logging::set_level(logging::level::trace);
 
   const HEPEnergyType P0 = 10_GeV;
 
@@ -74,12 +68,12 @@ TEMPLATE_TEST_CASE("TrackingLeapfrog_Curved", "tracking",
   SECTION(fmt::format("Tracking PID={}, Bfield={} uT, from outside={}", PID,
                       Bfield / 1_uT, outer)) {
 
-    C8LOG_DEBUG(
+    CORSIKA_LOG_DEBUG(
         "********************\n                          TEST section PID={}, Bfield={} "
         "uT, outer (?)={}",
         PID, Bfield / 1_uT, outer);
 
-    const int chargeNumber = GetChargeNumber(PID);
+    const int chargeNumber = get_charge_number(PID);
     LengthType radius = 10_m;
     int deflect = 0;
     if (chargeNumber != 0 and Bfield != 0_T) {
@@ -89,27 +83,26 @@ TEMPLATE_TEST_CASE("TrackingLeapfrog_Curved", "tracking",
       radius = gyroradius;
     }
 
-    auto [env, csPtr, worldPtr] = setup::testing::setupEnvironment(Code::Oxygen, Bfield);
+    auto [env, csPtr, worldPtr] =
+        corsika::setup::testing::setup_environment(Code::Oxygen, Bfield);
     { [[maybe_unused]] const auto& env_dummy = env; }
     auto const& cs = *csPtr;
 
     TestType tracking;
     Point const center(cs, {0_m, 0_m, 0_m});
-    auto target = setup::Environment::CreateNode<geometry::Sphere>(center, radius);
+    auto target = setup::Environment::createNode<Sphere>(center, radius);
 
-    using MyHomogeneousModel =
-        environment::MediumPropertyModel<environment::UniformMagneticField<
-            environment::HomogeneousMedium<setup::EnvironmentInterface>>>;
+    using MyHomogeneousModel = MediumPropertyModel<
+        UniformMagneticField<HomogeneousMedium<setup::EnvironmentInterface>>>;
 
     MagneticFieldVector magneticfield(cs, 0_T, 0_T, Bfield);
-    target->SetModelProperties<MyHomogeneousModel>(
-        environment::Medium::AirDry1Atm, magneticfield, 1_g / (1_m * 1_m * 1_m),
-        environment::NuclearComposition(std::vector<Code>{Code::Oxygen},
-                                        std::vector<float>{1.}));
+    target->setModelProperties<MyHomogeneousModel>(
+        Medium::AirDry1Atm, magneticfield, 1_g / (1_m * 1_m * 1_m),
+        NuclearComposition(std::vector<Code>{Code::Oxygen}, std::vector<float>{1.}));
     auto* targetPtr = target.get();
-    worldPtr->AddChild(std::move(target));
+    worldPtr->addChild(std::move(target));
 
-    auto [stack, viewPtr] = setup::testing::setupStack(PID, 0, 0, P0, targetPtr, cs);
+    auto [stack, viewPtr] = setup::testing::setup_stack(PID, 0, 0, P0, targetPtr, cs);
     { [[maybe_unused]] auto& viewPtr_dum = viewPtr; }
     auto particle = stack->first();
     // Note: momentum in X-direction
@@ -118,38 +111,39 @@ TEMPLATE_TEST_CASE("TrackingLeapfrog_Curved", "tracking",
     //       expect intersections somewere in +-y_start
 
     if (outer) {
-      particle.SetNode(worldPtr); // set particle inside "target" volume
+      particle.setNode(worldPtr); // set particle inside "target" volume
     } else {
-      particle.SetNode(targetPtr); // set particle outside "target" volume
+      particle.setNode(targetPtr); // set particle outside "target" volume
     }
-    particle.SetPosition(Point(cs, -radius, 0_m, 0_m));
+    particle.setPosition(Point(cs, -radius, 0_m, 0_m));
 
-    auto [traj, nextVol] = tracking.GetTrack(particle);
-    particle.SetNode(nextVol);
-    particle.SetPosition(traj.GetPosition(1));
-    particle.SetMomentum(traj.GetDirection(1) * particle.GetMomentum().norm());
+    auto [traj, nextVol] = tracking.getTrack(particle);
+    particle.setNode(nextVol);
+    particle.setPosition(traj.getPosition(1));
+    particle.setMomentum(traj.getDirection(1) * particle.getMomentum().getNorm());
     if (outer) {
       // now we know we are in target volume, depending on "outer"
-      CHECK(traj.GetLength(1) == 0_m);
+      CHECK(traj.getLength(1) == 0_m);
       CHECK(nextVol == targetPtr);
     }
     // move forward, until we leave target volume
     while (nextVol == targetPtr) {
-      const auto [traj2, nextVol2] = tracking.GetTrack(particle);
+      const auto [traj2, nextVol2] = tracking.getTrack(particle);
       nextVol = nextVol2;
-      particle.SetNode(nextVol);
-      particle.SetPosition(traj2.GetPosition(1));
-      particle.SetMomentum(traj2.GetDirection(1) * particle.GetMomentum().norm());
+      particle.setNode(nextVol);
+      particle.setPosition(traj2.getPosition(1));
+      particle.setMomentum(traj2.getDirection(1) * particle.getMomentum().getNorm());
     }
     CHECK(nextVol == worldPtr);
 
     Point pointCheck(cs, (deflect == 0 ? radius : 0_m), (deflect * radius), 0_m);
 
-    C8LOG_DEBUG("testTrackingLineStack: deflect={}, momentum={}, pos={}, pos_check={}",
-                deflect, particle.GetMomentum().GetComponents(),
-                particle.GetPosition().GetCoordinates(), pointCheck.GetCoordinates());
+    CORSIKA_LOG_DEBUG(
+        "testTrackingLineStack: deflect={}, momentum={}, pos={}, pos_check={}", deflect,
+        particle.getMomentum().getComponents(), particle.getPosition().getCoordinates(),
+        pointCheck.getCoordinates());
 
-    CHECK((particle.GetPosition() - pointCheck).norm() / radius ==
+    CHECK((particle.getPosition() - pointCheck).getNorm() / radius ==
           Approx(0).margin(1e-3));
   }
 }
diff --git a/tests/modules/testUrQMD.cpp b/tests/modules/testUrQMD.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..28ed39a159c9ceb8bb0960d8adac735fc67a95a2
--- /dev/null
+++ b/tests/modules/testUrQMD.cpp
@@ -0,0 +1,174 @@
+/*
+ * (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.
+ */
+
+#include <corsika/modules/urqmd/UrQMD.hpp>
+
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/PhysicalConstants.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/geometry/Point.hpp>
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/framework/geometry/Vector.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
+#include <corsika/framework/utility/CorsikaFenv.hpp>
+
+#include <SetupTestStack.hpp>
+#include <SetupTestEnvironment.hpp>
+
+#include <corsika/media/Environment.hpp>
+#include <corsika/media/HomogeneousMedium.hpp>
+#include <corsika/media/NuclearComposition.hpp>
+
+#include <tuple>
+#include <utility>
+
+#include <catch2/catch.hpp>
+
+/*
+  NOTE, WARNING, ATTENTION
+
+  The urqmd/Random.hpp implements the hook of urqmd 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/urqmd/Random.hpp>
+
+using namespace corsika;
+using namespace corsika::urqmd;
+
+template <typename TStackView>
+auto sumCharge(TStackView const& view) {
+  int totalCharge = 0;
+  for (auto const& p : view) { totalCharge += get_charge_number(p.getPID()); }
+  return totalCharge;
+}
+
+template <typename TStackView>
+auto sumMomentum(TStackView const& view, CoordinateSystemPtr const& vCS) {
+  MomentumVector sum{vCS, 0_eV, 0_eV, 0_eV};
+  for (auto const& p : view) { sum += p.getMomentum(); }
+  return sum;
+}
+
+TEST_CASE("UrQMD") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  SECTION("conversion") {
+    CHECK_THROWS(corsika::urqmd::convertFromUrQMD(106, 0));
+    CHECK(corsika::urqmd::convertFromUrQMD(101, 0) == Code::Pi0);
+    CHECK(corsika::urqmd::convertToUrQMD(Code::PiPlus) ==
+          std::make_pair<int, int>(101, 2));
+  }
+
+  feenableexcept(FE_INVALID);
+  RNGManager::getInstance().registerRandomStream("urqmd");
+  UrQMD urqmd;
+
+  SECTION("interaction length") {
+    auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Nitrogen);
+    auto const& cs = *csPtr;
+    { [[maybe_unused]] auto const& env_dummy = env; }
+
+    Code validProjectileCodes[] = {Code::PiPlus,  Code::PiMinus, Code::Proton,
+                                   Code::Neutron, Code::KPlus,   Code::KMinus,
+                                   Code::K0,      Code::K0Bar,   Code::K0Long};
+
+    for (auto code : validProjectileCodes) {
+      auto [stack, view] = setup::testing::setup_stack(
+          code, 0, 0, 100_GeV, (setup::Environment::BaseNodeType* const)nodePtr, cs);
+      CHECK(stack->getEntries() == 1);
+      CHECK(view->getEntries() == 0);
+
+      // simple check whether the cross-section is non-vanishing
+      CHECK(urqmd.getCrossSection(view->getProjectile(), Code::Proton) / 1_mb > 0);
+      CHECK(urqmd.getCrossSection(view->getProjectile(), Code::Nitrogen) / 1_mb > 0);
+      CHECK(urqmd.getCrossSection(view->getProjectile(), Code::Oxygen) / 1_mb > 0);
+      CHECK(urqmd.getCrossSection(view->getProjectile(), Code::Argon) / 1_mb > 0);
+    }
+  }
+
+  SECTION("nucleus projectile") {
+    auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Oxygen);
+    [[maybe_unused]] auto const& env_dummy = env;      // against warnings
+    [[maybe_unused]] auto const& node_dummy = nodePtr; // against warnings
+
+    unsigned short constexpr A = 14, Z = 7;
+    auto [stackPtr, secViewPtr] = setup::testing::setup_stack(
+        Code::Nucleus, A, Z, 40_GeV, (setup::Environment::BaseNodeType* const)nodePtr,
+        *csPtr);
+    CHECK(stackPtr->getEntries() == 1);
+    CHECK(secViewPtr->getEntries() == 0);
+
+    // must be assigned to variable, cannot be used as rvalue?!
+    auto projectile = secViewPtr->getProjectile();
+    auto const projectileMomentum = projectile.getMomentum();
+    urqmd.doInteraction(*secViewPtr);
+
+    CHECK(sumCharge(*secViewPtr) == Z + get_charge_number(Code::Oxygen));
+
+    auto const secMomSum =
+        sumMomentum(*secViewPtr, projectileMomentum.getCoordinateSystem());
+    CHECK((secMomSum - projectileMomentum).getNorm() / projectileMomentum.getNorm() ==
+          Approx(0).margin(1e-2));
+  }
+
+  SECTION("\"special\" projectile") {
+    auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Oxygen);
+    [[maybe_unused]] auto const& env_dummy = env;      // against warnings
+    [[maybe_unused]] auto const& node_dummy = nodePtr; // against warnings
+
+    auto [stackPtr, secViewPtr] = setup::testing::setup_stack(
+        Code::PiPlus, 0, 0, 40_GeV, (setup::Environment::BaseNodeType* const)nodePtr,
+        *csPtr);
+    CHECK(stackPtr->getEntries() == 1);
+    CHECK(secViewPtr->getEntries() == 0);
+
+    // must be assigned to variable, cannot be used as rvalue?!
+    auto projectile = secViewPtr->getProjectile();
+    auto const projectileMomentum = projectile.getMomentum();
+
+    urqmd.doInteraction(*secViewPtr);
+
+    CHECK(sumCharge(*secViewPtr) ==
+          get_charge_number(Code::PiPlus) + get_charge_number(Code::Oxygen));
+
+    auto const secMomSum =
+        sumMomentum(*secViewPtr, projectileMomentum.getCoordinateSystem());
+    CHECK((secMomSum - projectileMomentum).getNorm() / projectileMomentum.getNorm() ==
+          Approx(0).margin(1e-2));
+  }
+
+  SECTION("K0Long projectile") {
+    auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Oxygen);
+    [[maybe_unused]] auto const& env_dummy = env;      // against warnings
+    [[maybe_unused]] auto const& node_dummy = nodePtr; // against warnings
+
+    auto [stackPtr, secViewPtr] = setup::testing::setup_stack(
+        Code::K0Long, 0, 0, 40_GeV, (setup::Environment::BaseNodeType* const)nodePtr,
+        *csPtr);
+    CHECK(stackPtr->getEntries() == 1);
+    CHECK(secViewPtr->getEntries() == 0);
+
+    // must be assigned to variable, cannot be used as rvalue?!
+    auto projectile = secViewPtr->getProjectile();
+    auto const projectileMomentum = projectile.getMomentum();
+
+    urqmd.doInteraction(*secViewPtr);
+
+    CHECK(sumCharge(*secViewPtr) ==
+          get_charge_number(Code::K0Long) + get_charge_number(Code::Oxygen));
+
+    auto const secMomSum =
+        sumMomentum(*secViewPtr, projectileMomentum.getCoordinateSystem());
+    CHECK((secMomSum - projectileMomentum).getNorm() / projectileMomentum.getNorm() ==
+          Approx(0).margin(1e-2));
+  }
+}
diff --git a/tests/stack/CMakeLists.txt b/tests/stack/CMakeLists.txt
new file mode 100644
index 0000000000000000000000000000000000000000..8fe1070f199e6cf0ca529d267b6189dc6a990cc6
--- /dev/null
+++ b/tests/stack/CMakeLists.txt
@@ -0,0 +1,11 @@
+set (test_stack_sources
+  TestMain.cpp
+  testHistoryStack.cpp
+  testHistoryView.cpp
+  testGeometryNodeStackExtension.cpp
+  testDummyStack.cpp
+  testVectorStack.cpp
+  testNuclearStackExtension.cpp
+  )
+
+CORSIKA_ADD_TEST (testStack SOURCES ${test_stack_sources})
diff --git a/tests/stack/TestMain.cpp b/tests/stack/TestMain.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..51532584b8e03b35d79301543ac8f80b598ba544
--- /dev/null
+++ b/tests/stack/TestMain.cpp
@@ -0,0 +1,11 @@
+/*
+ * (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.
+ */
+
+#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one
+                          // cpp file
+#include <catch2/catch.hpp>
diff --git a/Stack/DummyStack/testDummyStack.cc b/tests/stack/testDummyStack.cpp
similarity index 58%
rename from Stack/DummyStack/testDummyStack.cc
rename to tests/stack/testDummyStack.cpp
index 41a5731589d0909405f11de46188784eb9fa710b..d68b0c093f0ffb6e8ce5a2a5ce57bc7aeea00544 100644
--- a/Stack/DummyStack/testDummyStack.cc
+++ b/tests/stack/testDummyStack.cpp
@@ -6,10 +6,9 @@
  * the license.
  */
 
-#include <corsika/stack/dummy/DummyStack.h>
+#include <corsika/stack/DummyStack.hpp>
 
 using namespace corsika;
-using namespace corsika::stack;
 
 #include <catch2/catch.hpp>
 
@@ -17,14 +16,17 @@ using namespace corsika::stack;
 
 TEST_CASE("DummyStack", "[stack]") {
 
-  using TestStack = dummy::DummyStack;
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
 
-  dummy::NoData noData;
+  using TestStack = dummy_stack::DummyStack;
+
+  dummy_stack::NoData noData;
 
   SECTION("write node") {
 
     TestStack s;
-    s.AddParticle(std::tuple<dummy::NoData>{noData});
+    s.addParticle(std::tuple<dummy_stack::NoData>{noData});
     CHECK(s.getEntries() == 1);
   }
 
@@ -32,10 +34,13 @@ TEST_CASE("DummyStack", "[stack]") {
 
     TestStack s;
     // add 99 particles, each 10th particle is a nucleus with A=i and Z=A/2!
-    for (int i = 0; i < 99; ++i) { s.AddParticle(std::tuple<dummy::NoData>{noData}); }
+
+    for (int i = 0; i < 99; ++i) {
+      s.addParticle(std::tuple<dummy_stack::NoData>{noData});
+    }
 
     CHECK(s.getEntries() == 99);
-    for (int i = 0; i < 99; ++i) s.GetNextParticle().Delete();
+    for (int i = 0; i < 99; ++i) s.getNextParticle().erase();
     CHECK(s.getEntries() == 0);
   }
 }
diff --git a/Stack/GeometryNodeStackExtension/testGeometryNodeStackExtension.cc b/tests/stack/testGeometryNodeStackExtension.cpp
similarity index 57%
rename from Stack/GeometryNodeStackExtension/testGeometryNodeStackExtension.cc
rename to tests/stack/testGeometryNodeStackExtension.cpp
index b566c2c8ebed2848b243a28bd0b21b28219fcfc1..8256927d92ed44f57d84a0ddfa370244630c9483 100644
--- a/Stack/GeometryNodeStackExtension/testGeometryNodeStackExtension.cc
+++ b/tests/stack/testGeometryNodeStackExtension.cpp
@@ -6,12 +6,11 @@
  * the license.
  */
 
-#include <corsika/stack/CombinedStack.h>
-#include <corsika/stack/dummy/DummyStack.h>
-#include <corsika/stack/node/GeometryNodeStackExtension.h>
+#include <corsika/framework/stack/CombinedStack.hpp>
+#include <corsika/stack/DummyStack.hpp>
+#include <corsika/stack/GeometryNodeStackExtension.hpp>
 
 using namespace corsika;
-using namespace corsika::stack;
 
 #include <catch2/catch.hpp>
 
@@ -27,29 +26,31 @@ public:
 // the GeometryNode stack needs to know the type of geometry-nodes from the DummyEnv:
 template <typename TStackIter>
 using DummyGeometryDataInterface =
-    typename corsika::stack::node::MakeGeometryDataInterface<TStackIter, DummyEnv>::type;
+    typename node::MakeGeometryDataInterface<TStackIter, DummyEnv>::type;
 
 // combine dummy stack with geometry information for tracking
 template <typename TStackIter>
 using StackWithGeometryInterface =
-    corsika::stack::CombinedParticleInterface<dummy::DummyStack::MPIType,
-                                              DummyGeometryDataInterface, TStackIter>;
+    CombinedParticleInterface<dummy_stack::DummyStack::pi_type,
+                              DummyGeometryDataInterface, TStackIter>;
 
 using TestStack =
-    corsika::stack::CombinedStack<typename stack::dummy::DummyStack::StackImpl,
-                                  stack::node::GeometryData<DummyEnv>,
-                                  StackWithGeometryInterface>;
+    CombinedStack<typename dummy_stack::DummyStack::stack_implementation_type,
+                  node::GeometryData<DummyEnv>, StackWithGeometryInterface>;
 
 TEST_CASE("GeometryNodeStackExtension", "[stack]") {
 
-  dummy::NoData noData;
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  dummy_stack::NoData noData;
 
   SECTION("write node") {
 
     const int data = 5;
 
     TestStack s;
-    s.AddParticle(std::make_tuple(noData), std::tuple<const int*>{&data});
+    s.addParticle(std::make_tuple(noData), std::tuple<const int*>{&data});
 
     CHECK(s.getEntries() == 1);
   }
@@ -58,12 +59,12 @@ TEST_CASE("GeometryNodeStackExtension", "[stack]") {
     const int data = 15;
 
     TestStack s;
-    auto p = s.AddParticle(std::make_tuple(noData));
-    p.SetNode(&data);
+    auto p = s.addParticle(std::make_tuple(noData));
+    p.setNode(&data);
     CHECK(s.getEntries() == 1);
 
-    const auto pout = s.GetNextParticle();
-    CHECK(*(pout.GetNode()) == 15);
+    const auto pout = s.getNextParticle();
+    CHECK(*(pout.getNode()) == 15);
   }
 
   SECTION("stack fill and cleanup") {
@@ -73,16 +74,16 @@ TEST_CASE("GeometryNodeStackExtension", "[stack]") {
     TestStack s;
     // add 99 particles, each 10th particle is a nucleus with A=i and Z=A/2!
     for (int i = 0; i < 99; ++i) {
-      auto p = s.AddParticle(std::tuple<dummy::NoData>{noData});
-      p.SetNode(&data);
+      auto p = s.addParticle(std::tuple<dummy_stack::NoData>{noData});
+      p.setNode(&data);
     }
 
     CHECK(s.getEntries() == 99);
     double v = 0;
     for (int i = 0; i < 99; ++i) {
-      auto p = s.GetNextParticle();
-      v += *(p.GetNode());
-      p.Delete();
+      auto p = s.getNextParticle();
+      v += *(p.getNode());
+      p.erase();
     }
     CHECK(v == 99 * data);
     CHECK(s.getEntries() == 0);
diff --git a/Stack/History/testHistoryStack.cc b/tests/stack/testHistoryStack.cpp
similarity index 65%
rename from Stack/History/testHistoryStack.cc
rename to tests/stack/testHistoryStack.cpp
index c068bdb6942ed57bcd705b8b001d8293267ce383..b393291bfe3598e3a65f848864d62f7610ba7a71 100644
--- a/Stack/History/testHistoryStack.cc
+++ b/tests/stack/testHistoryStack.cpp
@@ -6,14 +6,13 @@
  * the license.
  */
 
-#include <corsika/stack/CombinedStack.h>
-#include <corsika/stack/dummy/DummyStack.h>
+#include <corsika/framework/stack/CombinedStack.hpp>
+#include <corsika/stack/DummyStack.hpp>
 #include <corsika/stack/history/HistoryStackExtension.hpp>
 
 #include <catch2/catch.hpp>
 
 using namespace corsika;
-using namespace corsika::stack;
 
 // this is our dummy environment, it only knows its trivial BaseNodeType
 class DummyEvent {
@@ -38,29 +37,31 @@ using DummyHistoryDataInterface =
 // combine dummy stack with geometry information for tracking
 template <typename TStackIter>
 using StackWithHistoryInterface =
-    corsika::stack::CombinedParticleInterface<dummy::DummyStack::MPIType,
-                                              DummyHistoryDataInterface, TStackIter>;
+    CombinedParticleInterface<dummy_stack::DummyStack::pi_type, DummyHistoryDataInterface,
+                              TStackIter>;
 
 using TestStack =
-    corsika::stack::CombinedStack<typename stack::dummy::DummyStack::StackImpl,
-                                  history::HistoryData<DummyEvent>,
-                                  StackWithHistoryInterface>;
+    CombinedStack<typename dummy_stack::DummyStack::stack_implementation_type,
+                  history::HistoryData<DummyEvent>, StackWithHistoryInterface>;
 
 using EvtPtr = std::shared_ptr<DummyEvent>;
 
 TEST_CASE("HistoryStackExtension", "[stack]") {
 
-  logging::SetLevel(logging::level::debug);
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
 
-  const dummy::NoData noData;
+  [[maybe_unused]] CoordinateSystemPtr const& dummyCS = get_root_CoordinateSystem();
+
+  const dummy_stack::NoData noData;
   TestStack s;
 
-  auto p = s.AddParticle(std::tuple<dummy::NoData>{noData});
+  auto p = s.addParticle(std::tuple<dummy_stack::NoData>{noData});
 
   SECTION("add lone particle") {
     CHECK(s.getEntries() == 1);
 
-    EvtPtr evt = p.GetEvent();
+    EvtPtr evt = p.getEvent();
     CHECK(evt == nullptr);
   }
 }
diff --git a/tests/stack/testHistoryView.cpp b/tests/stack/testHistoryView.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..6ee9300c3ab4fff3c74b834bb4cbfca8df828c0f
--- /dev/null
+++ b/tests/stack/testHistoryView.cpp
@@ -0,0 +1,212 @@
+/*
+ * (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/stack/history/Event.hpp>
+#include <corsika/stack/history/HistorySecondaryProducer.hpp>
+#include <corsika/stack/history/HistoryStackExtension.hpp>
+
+#include <corsika/framework/stack/CombinedStack.hpp>
+#include <corsika/stack/DummyStack.hpp>
+#include <corsika/stack/NuclearStackExtension.hpp>
+
+#include <corsika/framework/core/Logging.hpp>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+
+/**
+   Need to replicate setup::SetupStack in a maximally simplified
+   way, but with real particle data
+ */
+
+// combine dummy stack with geometry information for tracking
+template <typename TStackIter>
+using StackWithHistoryInterface =
+    CombinedParticleInterface<nuclear_stack::ParticleDataStack::pi_type,
+                              history::HistoryEventDataInterface, TStackIter>;
+
+using TestStack =
+    CombinedStack<typename nuclear_stack::ParticleDataStack::stack_implementation_type,
+                  history::HistoryEventData, StackWithHistoryInterface>;
+
+/*
+    See Issue 161
+
+    unfortunately clang does not support this in the same way (yet) as
+    gcc, so we have to distinguish here. If clang cataches up, we
+    could remove the clang branch here and also in
+    corsika::Cascade. The gcc code is much more generic and
+    universal. If we could do the gcc version, we won't had to define
+    StackView globally, we could do it with MakeView whereever it is
+    actually needed. Keep an eye on this!
+  */
+#if defined(__clang__)
+using TheTestStackView =
+    SecondaryView<typename TestStack::stack_implementation_type,
+                  StackWithHistoryInterface, history::HistorySecondaryProducer>;
+#elif defined(__GNUC__) || defined(__GNUG__)
+using TheTestStackView = MakeView<TestStack, history::HistorySecondaryProducer>::type;
+#endif
+
+using TestStackView = TheTestStackView;
+
+template <typename Event>
+int count_generations(Event const* event) {
+  int genCounter = 0;
+  while (event) {
+    event = event->parentEvent().get();
+    genCounter++;
+  }
+
+  return genCounter;
+}
+
+TEST_CASE("HistoryStackExtensionView", "[stack]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  CoordinateSystemPtr const& dummyCS = get_root_CoordinateSystem();
+
+  // in this test we only use one singel stack !
+  TestStack stack;
+
+  // add primary particle
+  auto p0 = stack.addParticle(std::make_tuple(
+      Code::Electron, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+      Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
+
+  CHECK(stack.getEntries() == 1);
+  corsika::history::EventPtr evt = p0.getEvent();
+  CHECK(evt == nullptr);
+  CHECK(count_generations(evt.get()) == 0);
+
+  SECTION("interface test, view") {
+
+    // add secondaries, 1st generation
+    TestStackView hview0(p0);
+
+    auto const ev0 = p0.getEvent();
+    CHECK(ev0 == nullptr);
+
+    CORSIKA_LOG_DEBUG("loop VIEW");
+
+    // add 5 secondaries
+    for (int i = 0; i < 5; ++i) {
+      auto sec = hview0.addSecondary(std::make_tuple(
+          Code::Electron, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+          Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
+
+      CHECK(sec.getParentEventIndex() == i);
+      CHECK(sec.getEvent() != nullptr);
+      CHECK(sec.getEvent()->parentEvent() == nullptr);
+      CHECK(count_generations(sec.getEvent().get()) == 1);
+    }
+
+    // read 1st genertion particle particle
+    auto p1 = stack.getNextParticle();
+    CHECK(count_generations(p1.getEvent().get()) == 1);
+
+    TestStackView hview1(p1);
+
+    auto const ev1 = p1.getEvent();
+
+    // add second generation of secondaries
+    // add 10 secondaries
+    for (int i = 0; i < 10; ++i) {
+      auto sec = hview1.addSecondary(std::make_tuple(
+          Code::Electron, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+          Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
+
+      CHECK(sec.getParentEventIndex() == i);
+      CHECK(sec.getEvent()->parentEvent() == ev1);
+      CHECK(sec.getEvent()->parentEvent()->parentEvent() == ev0);
+
+      CHECK(count_generations(sec.getEvent().get()) == 2);
+
+      const auto org_projectile = stack.at(sec.getEvent()->projectileIndex());
+      CHECK(org_projectile.getEvent() == sec.getEvent()->parentEvent());
+    }
+
+    // read 2nd genertion particle particle
+    auto p2 = stack.getNextParticle();
+
+    TestStackView hview2(p2);
+
+    auto const ev2 = p2.getEvent();
+
+    // add third generation of secondaries
+    // add 15 secondaries
+    for (int i = 0; i < 15; ++i) {
+      CORSIKA_LOG_TRACE("loop, view: " + std::to_string(i));
+
+      auto sec = hview2.addSecondary(std::make_tuple(
+          Code::Electron, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+          Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
+      CORSIKA_LOG_TRACE("loop, ---- ");
+
+      CHECK(sec.getParentEventIndex() == i);
+      CHECK(sec.getEvent()->parentEvent() == ev2);
+      CHECK(sec.getEvent()->parentEvent()->parentEvent() == ev1);
+      CHECK(sec.getEvent()->parentEvent()->parentEvent()->parentEvent() == ev0);
+
+      CHECK(count_generations(sec.getEvent().get()) == 3);
+    }
+  }
+
+  SECTION("also test projectile access") {
+
+    CORSIKA_LOG_TRACE("projectile test");
+
+    // add secondaries, 1st generation
+    TestStackView hview0(p0);
+    auto proj0 = hview0.getProjectile();
+    auto const ev0 = p0.getEvent();
+    CHECK(ev0 == nullptr);
+
+    CORSIKA_LOG_TRACE("loop");
+
+    // add 5 secondaries
+    for (int i = 0; i < 5; ++i) {
+      CORSIKA_LOG_TRACE("loop " + std::to_string(i));
+      auto sec = proj0.addSecondary(std::make_tuple(
+          Code::Electron, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+          Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
+
+      CHECK(sec.getParentEventIndex() == i);
+      CHECK(sec.getEvent() != nullptr);
+      CHECK(sec.getEvent()->parentEvent() == nullptr);
+    }
+    CHECK(stack.getEntries() == 6);
+
+    // read 1st genertion particle particle
+    auto p1 = stack.getNextParticle();
+
+    TestStackView hview1(p1);
+    auto proj1 = hview1.getProjectile();
+    auto const ev1 = p1.getEvent();
+
+    // add second generation of secondaries
+    // add 10 secondaries
+    for (unsigned int i = 0; i < 10; ++i) {
+      auto sec = proj1.addSecondary(std::make_tuple(
+          Code::Electron, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+          Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
+
+      CHECK(sec.getParentEventIndex() == int(i));
+      CHECK(sec.getEvent()->parentEvent() == ev1);
+      CHECK(sec.getEvent()->secondaries().size() == i + 1);
+      CHECK(sec.getEvent()->parentEvent()->parentEvent() == ev0);
+
+      const auto org_projectile = stack.at(sec.getEvent()->projectileIndex());
+      CHECK(org_projectile.getEvent() == sec.getEvent()->parentEvent());
+    }
+    CHECK(stack.getEntries() == 16);
+  }
+}
diff --git a/tests/stack/testNuclearStackExtension.cpp b/tests/stack/testNuclearStackExtension.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..1e33f6c0570230bed7fc31baa13f0f2de1cb0c91
--- /dev/null
+++ b/tests/stack/testNuclearStackExtension.cpp
@@ -0,0 +1,245 @@
+/*
+ * (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/geometry/RootCoordinateSystem.hpp>
+#include <corsika/stack/NuclearStackExtension.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+using namespace corsika;
+
+#include <catch2/catch.hpp>
+
+#include <iostream>
+using namespace std;
+
+TEST_CASE("NuclearStackExtension", "[stack]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  CoordinateSystemPtr const& dummyCS = get_root_CoordinateSystem();
+
+  SECTION("write non nucleus") {
+    nuclear_stack::NuclearStackExtension<VectorStack,
+                                         nuclear_stack::ExtendedParticleInterfaceType>
+        s;
+    s.addParticle(std::make_tuple(
+        Code::Electron, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
+    CHECK(s.getEntries() == 1);
+  }
+
+  SECTION("write nucleus") {
+
+    nuclear_stack::NuclearStackExtension<VectorStack,
+                                         nuclear_stack::ExtendedParticleInterfaceType>
+        s;
+    s.addParticle(std::make_tuple(
+        Code::Nucleus, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 10, 10));
+    CHECK(s.getEntries() == 1);
+  }
+
+  SECTION("write invalid nucleus") {
+    nuclear_stack::ParticleDataStack s;
+    CHECK_THROWS(s.addParticle(std::make_tuple(
+        Code::Nucleus, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 0, 0)));
+  }
+
+  SECTION("read non nucleus") {
+    nuclear_stack::ParticleDataStack s;
+    s.addParticle(std::make_tuple(
+        Code::Electron, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
+    const auto pout = s.getNextParticle();
+    CHECK(pout.getPID() == Code::Electron);
+    CHECK(pout.getEnergy() == 1.5_GeV);
+    CHECK(pout.getTime() == 100_s);
+  }
+
+  SECTION("read nucleus") {
+    nuclear_stack::ParticleDataStack s;
+    s.addParticle(std::make_tuple(
+        Code::Nucleus, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 10, 9));
+    const auto pout = s.getNextParticle();
+    CHECK(pout.getPID() == Code::Nucleus);
+    CHECK(pout.getEnergy() == 1.5_GeV);
+    CHECK(pout.getTime() == 100_s);
+    CHECK(pout.getNuclearA() == 10);
+    CHECK(pout.getNuclearZ() == 9);
+  }
+
+  SECTION("read invalid nucleus") {
+    nuclear_stack::ParticleDataStack s;
+    s.addParticle(std::make_tuple(
+        Code::Electron, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
+    const auto pout = s.getNextParticle();
+    CHECK_THROWS(pout.getNuclearA());
+    CHECK_THROWS(pout.getNuclearZ());
+  }
+
+  SECTION("stack fill and cleanup") {
+
+    nuclear_stack::ParticleDataStack s;
+    // add 99 particles, each 10th particle is a nucleus with A=i and Z=A/2!
+    for (int i = 0; i < 99; ++i) {
+      if ((i + 1) % 10 == 0) {
+        s.addParticle(std::make_tuple(
+            Code::Nucleus, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+            Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, i, i / 2));
+      } else {
+        s.addParticle(std::make_tuple(
+            Code::Electron, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+            Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
+      }
+    }
+
+    CHECK(s.getEntries() == 99);
+    for (int i = 0; i < 99; ++i) s.getNextParticle().erase();
+    CHECK(s.getEntries() == 0);
+  }
+
+  SECTION("stack operations") {
+
+    nuclear_stack::ParticleDataStack s;
+    // add 99 particles, each 10th particle is a nucleus with A=i and Z=A/2!
+    // i=9, 19, 29, etc. are nuclei
+    for (int i = 0; i < 99; ++i) {
+      if ((i + 1) % 10 == 0) {
+        s.addParticle(std::make_tuple(
+            Code::Nucleus, i * 15_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+            Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, i, i / 2));
+      } else {
+        s.addParticle(std::make_tuple(
+            Code::Electron, i * 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+            Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
+      }
+    }
+
+    // copy
+    {
+      s.copy(s.begin() + 9, s.begin() + 10); // nuclei to non-nuclei
+      const auto& p9 = s.cbegin() + 9;
+      const auto& p10 = s.cbegin() + 10;
+
+      CHECK(p9.getPID() == Code::Nucleus);
+      CHECK(p9.getEnergy() == 9 * 15_GeV);
+      CHECK(p9.getTime() == 100_s);
+      CHECK(p9.getNuclearA() == 9);
+      CHECK(p9.getNuclearZ() == 9 / 2);
+
+      CHECK(p10.getPID() == Code::Nucleus);
+      CHECK(p10.getEnergy() == 9 * 15_GeV);
+      CHECK(p10.getTime() == 100_s);
+      CHECK(p10.getNuclearA() == 9);
+      CHECK(p10.getNuclearZ() == 9 / 2);
+    }
+
+    // copy
+    {
+      s.copy(s.begin() + 93, s.begin() + 9); // non-nuclei to nuclei
+      const auto& p93 = s.cbegin() + 93;
+      const auto& p9 = s.cbegin() + 9;
+
+      CHECK(p9.getPID() == Code::Electron);
+      CHECK(p9.getEnergy() == 93 * 1.5_GeV);
+      CHECK(p9.getTime() == 100_s);
+
+      CHECK(p93.getPID() == Code::Electron);
+      CHECK(p93.getEnergy() == 93 * 1.5_GeV);
+      CHECK(p93.getTime() == 100_s);
+    }
+
+    // copy
+    {
+      s.copy(s.begin() + 89, s.begin() + 79); // nuclei to nuclei
+      const auto& p89 = s.cbegin() + 89;
+      const auto& p79 = s.cbegin() + 79;
+
+      CHECK(p89.getPID() == Code::Nucleus);
+      CHECK(p89.getEnergy() == 89 * 15_GeV);
+      CHECK(p89.getTime() == 100_s);
+
+      CHECK(p79.getPID() == Code::Nucleus);
+      CHECK(p79.getEnergy() == 89 * 15_GeV);
+      CHECK(p79.getTime() == 100_s);
+    }
+
+    // swap
+    {
+      s.swap(s.begin() + 11, s.begin() + 10);
+      const auto& p11 = s.cbegin() + 11; // now: nucleus
+      const auto& p10 = s.cbegin() + 10; // now: electron
+
+      CHECK(p11.getPID() == Code::Nucleus);
+      CHECK(p11.getEnergy() == 9 * 15_GeV);
+      CHECK(p11.getTime() == 100_s);
+      CHECK(p11.getNuclearA() == 9);
+      CHECK(p11.getNuclearZ() == 9 / 2);
+
+      CHECK(p10.getPID() == Code::Electron);
+      CHECK(p10.getEnergy() == 11 * 1.5_GeV);
+      CHECK(p10.getTime() == 100_s);
+    }
+
+    // swap two nuclei
+    {
+      s.swap(s.begin() + 29, s.begin() + 59);
+      const auto& p29 = s.cbegin() + 29;
+      const auto& p59 = s.cbegin() + 59;
+
+      CHECK(p29.getPID() == Code::Nucleus);
+      CHECK(p29.getEnergy() == 59 * 15_GeV);
+      CHECK(p29.getTime() == 100_s);
+      CHECK(p29.getNuclearA() == 59);
+      CHECK(p29.getNuclearZ() == 59 / 2);
+
+      CHECK(p59.getPID() == Code::Nucleus);
+      CHECK(p59.getEnergy() == 29 * 15_GeV);
+      CHECK(p59.getTime() == 100_s);
+      CHECK(p59.getNuclearA() == 29);
+      CHECK(p59.getNuclearZ() == 29 / 2);
+    }
+
+    for (int i = 0; i < 99; ++i) s.last().erase();
+    CHECK(s.getEntries() == 0);
+  }
+
+  SECTION("not allowed") {
+
+    nuclear_stack::NuclearStackExtension<VectorStack,
+                                         nuclear_stack::ExtendedParticleInterfaceType>
+        s;
+
+    // not valid:
+    CHECK_THROWS(s.addParticle(std::make_tuple(
+        Code::Oxygen, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 16, 8)));
+
+    // valid
+    auto particle = s.addParticle(std::make_tuple(
+        Code::Nucleus, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 10, 9));
+
+    // not valid
+    CHECK_THROWS(particle.addSecondary(std::make_tuple(
+        Code::Oxygen, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 16, 8)));
+
+    // add a another nucleus, so there are two now
+    s.addParticle(std::make_tuple(
+        Code::Nucleus, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s, 10, 9));
+
+    // not valid, since end() is not a valid entry
+    CHECK_THROWS(s.swap(s.begin(), s.end()));
+  }
+}
diff --git a/tests/stack/testVectorStack.cpp b/tests/stack/testVectorStack.cpp
new file mode 100644
index 0000000000000000000000000000000000000000..9767b6c847beff9097e873aed8a390ccb62a45fd
--- /dev/null
+++ b/tests/stack/testVectorStack.cpp
@@ -0,0 +1,59 @@
+/*
+ * (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.
+ */
+
+#define protected public // to also test the internal state of objects
+
+#include <corsika/framework/geometry/RootCoordinateSystem.hpp>
+#include <corsika/stack/VectorStack.hpp>
+#include <corsika/framework/core/PhysicalUnits.hpp>
+
+#include <catch2/catch.hpp>
+
+using namespace corsika;
+using namespace std;
+
+TEST_CASE("VectorStack", "[stack]") {
+
+  logging::set_level(logging::level::info);
+  corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v");
+
+  CoordinateSystemPtr const& dummyCS = get_root_CoordinateSystem();
+
+  SECTION("read+write") {
+
+    VectorStack s;
+    s.addParticle(std::make_tuple(
+        Code::Electron, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+        Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
+
+    // read
+    CHECK(s.getEntries() == 1);
+    CHECK(s.getSize() == 1);
+    auto pout = s.getNextParticle();
+    CHECK(pout.getPID() == Code::Electron);
+    CHECK(pout.getEnergy() == 1.5_GeV);
+    CHECK(pout.getTime() == 100_s);
+  }
+
+  SECTION("write+delete") {
+
+    VectorStack s;
+    for (int i = 0; i < 99; ++i)
+
+      s.addParticle(std::make_tuple(
+          Code::Electron, 1.5_GeV, MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}),
+          Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s));
+
+    CHECK(s.getSize() == 99);
+
+    for (int i = 0; i < 99; ++i) s.getNextParticle().erase();
+
+    CHECK(s.getEntries() == 0);
+    CHECK(s.getSize() == 1);
+  }
+}
diff --git a/Tools/plot_crossings.sh b/tools/plot_crossings.sh
similarity index 100%
rename from Tools/plot_crossings.sh
rename to tools/plot_crossings.sh
diff --git a/Tools/plot_tracks.sh b/tools/plot_tracks.sh
similarity index 100%
rename from Tools/plot_tracks.sh
rename to tools/plot_tracks.sh