From 3b40b8cc9800cafe57e99774fac6ad97d4a78c39 Mon Sep 17 00:00:00 2001
From: Maximilian Reininghaus <maximilian.reininghaus@kit.edu>
Date: Thu, 9 Mar 2023 16:20:25 +0100
Subject: [PATCH] event generation, test, linking

---
 .../detail/modules/fluka/InteractionModel.inl | 50 ++++++++++-------
 corsika/modules/FLUKA.hpp                     |  4 +-
 corsika/modules/Random.hpp                    |  1 +
 corsika/modules/fluka/InteractionModel.hpp    |  5 +-
 corsika/modules/fluka/ParticleConversion.hpp  |  9 +--
 corsika/modules/fluka/Random.hpp              | 26 +++++++++
 modules/fluka/CMakeLists.txt                  | 47 ++++++++++++++--
 modules/fluka/FLUKA.hpp                       | 16 +++++-
 modules/fluka/flrndm.cpp                      | 31 +++++++++++
 src/modules/fluka/CMakeLists.txt              |  2 +-
 src/modules/fluka/strip_flukahp.sh            | 28 ++++++++++
 tests/modules/testFluka.cpp                   | 55 ++++++++++++++-----
 12 files changed, 223 insertions(+), 51 deletions(-)
 create mode 100644 corsika/modules/fluka/Random.hpp
 create mode 100644 modules/fluka/flrndm.cpp
 create mode 100755 src/modules/fluka/strip_flukahp.sh

diff --git a/corsika/detail/modules/fluka/InteractionModel.inl b/corsika/detail/modules/fluka/InteractionModel.inl
index 2798d9fb5..627e64ba3 100644
--- a/corsika/detail/modules/fluka/InteractionModel.inl
+++ b/corsika/detail/modules/fluka/InteractionModel.inl
@@ -21,6 +21,8 @@
 #include <corsika/media/Environment.hpp>
 #include <corsika/media/NuclearComposition.hpp>
 #include <corsika/framework/geometry/FourVector.hpp>
+#include <corsika/framework/core/ParticleProperties.hpp>
+#include <corsika/framework/core/EnergyMomentumOperations.hpp>
 
 #include <corsika/framework/core/PhysicalUnits.hpp>
 
@@ -90,8 +92,6 @@ namespace corsika::fluka {
 
     auto const plab = projectileLab4mom.getSpaceLikeComponents();
 
-    std::cout << targetRestBoost.boost_ << '\n';
-    std::cout << targetRestBoost.rotatedCS_->getTransform().matrix() << '\n';
     CORSIKA_LOGGER_DEBUG(logger_, fmt::format("Elab = {} GeV", Elab * invGeV));
     CORSIKA_LOGGER_DEBUG(logger_, fmt::format("EkinLab = {} GeV", EkinLab * invGeV));
 
@@ -139,24 +139,32 @@ namespace corsika::fluka {
                      cumsgx_.get() + materials_.size(),
                      cumsgx_.get() + materials_.size() * 2);
 
-    //~ extern struct {
-    //~ int nevhep;                  // event number
-    //~ int nhep;                    // number of entries
-    //~ hepmc_array<int> isthep;     // status code
-    //~ hepmc_array<int> idhep;      // PDG particle id
-    //~ hepmc_array<int[2]> jmohep;  // position of first, second mother
-    //~ hepmc_array<int[2]> jdahep;  // position of first, last daughter
-    //~ hepmc_array<double[5]> phep; // 4-momemtum, mass (GeV)
-    //~ hepmc_array<double[4]> vhep; // vertex, production time in mm
-    //~ } hepevt_;
-
     for (int i = 0; i < ::fluka::hepevt_.nhep; ++i) {
-      int const pdg = ::fluka::hepevt_.idhep[i];
       int const status = ::fluka::hepevt_.isthep[i];
-      auto const mom = QuantityVector<hepenergy_d>{
-          Eigen::Map<Eigen::Vector3d>(&::fluka::hepevt_.phep[i][0]) * invGeV.magnitude()};
+      // TODO: enable this when FLUKA writes status code
+      //~ if (status != 1) // skip non-final-state particles
+      //~ continue;
 
-      std::cout << pdg << '\t' << status << '\t' << mom << std::endl;
+      auto const pdg = static_cast<corsika::PDGCode>(::fluka::hepevt_.idhep[i]);
+      auto const c8code = corsika::convert_from_PDG(pdg);
+      auto const mom = QuantityVector<hepenergy_d>{
+          Eigen::Map<Eigen::Vector3d>(&::fluka::hepevt_.phep[i][0]) *
+          (1_GeV).magnitude()};
+      auto const pPrime = mom.getNorm();
+      auto const c8mass = corsika::get_mass(c8code);
+      auto const flMass = ::fluka::hepevt_.phep[i][5 - 1];
+
+      auto const fourMomCollisionFrame =
+          FourVector{calculate_total_energy(pPrime, c8mass), MomentumVector{cs, mom}};
+      auto const fourMomOrigFrame = targetRestBoost.fromCoM(fourMomCollisionFrame);
+      auto const momOrigFrame = fourMomOrigFrame.getSpaceLikeComponents();
+      auto const p = momOrigFrame.getNorm();
+
+      view.addSecondary(std::tuple{c8code, corsika::calculate_kinetic_energy(p, c8mass),
+                                   momOrigFrame / p});
+      std::cout << static_cast<int>(pdg) << '\t' << get_name(c8code, full_name{}) << '\t'
+                << momOrigFrame / 1_GeV << '\t' << flMass << ' ' << c8mass / 1_GeV << " "
+                << std::endl;
     }
   }
 
@@ -195,8 +203,9 @@ namespace corsika::fluka {
     double const df2dp3 = -1;   // default
     bool const lprint = true;
     auto mtflka = std::make_unique<int[]>(mxelfl);
-    char crvrck[8 + 1] =
-        "76466879"; // magic number that FLUKA uses to see if it's the right version
+    // magic number that FLUKA uses to see if it's the right version
+    char crvrck[] = "76466879";
+    int const size = 8;
 
     std::fill(&nelmfl[0], &nelmfl[nElements], 1);
     std::fill(&wfelml[0], &wfelml[nElements], 1.);
@@ -210,7 +219,8 @@ namespace corsika::fluka {
 
     // call FLUKA
     ::fluka::stpxyz_(&nElements, nelmfl.get(), izelfl.data(), wfelml.get(), &mxelfl,
-                     &pptmax, &ef2dp3, &df2dp3, &iflxyz_, &lprint, mtflka.get(), crvrck);
+                     &pptmax, &ef2dp3, &df2dp3, &iflxyz_, &lprint, mtflka.get(), crvrck,
+                     &size);
 
     // now create & fill vector of (C8 Code, FLUKA mat. no.) pairs
     std::vector<std::pair<Code, int>> mapping;
diff --git a/corsika/modules/FLUKA.hpp b/corsika/modules/FLUKA.hpp
index 02555ea5e..2b75999cf 100644
--- a/corsika/modules/FLUKA.hpp
+++ b/corsika/modules/FLUKA.hpp
@@ -13,9 +13,9 @@
 #include <corsika/framework/process/InteractionProcess.hpp>
 
 /**
- * @file Epos.hpp
+ * @file FLUKA.hpp
  *
- * Includes all the parts of the EPOS model. Defines the InteractionProcess<TModel>
+ * Includes all the parts of the FLUKA model. Defines the InteractionProcess<TModel>
  * classes needed for the ProcessSequence.
  */
 
diff --git a/corsika/modules/Random.hpp b/corsika/modules/Random.hpp
index a81bc1ad2..60d8b9a9a 100644
--- a/corsika/modules/Random.hpp
+++ b/corsika/modules/Random.hpp
@@ -22,3 +22,4 @@
 #include <corsika/modules/urqmd/Random.hpp>
 #include <corsika/modules/qgsjetII/Random.hpp>
 #include <corsika/modules/conex/Random.hpp>
+#include <corsika/modules/fluka/Random.hpp>
diff --git a/corsika/modules/fluka/InteractionModel.hpp b/corsika/modules/fluka/InteractionModel.hpp
index f662040bc..206319f85 100644
--- a/corsika/modules/fluka/InteractionModel.hpp
+++ b/corsika/modules/fluka/InteractionModel.hpp
@@ -18,6 +18,7 @@
 #include <corsika/framework/utility/COMBoost.hpp>
 #include <corsika/framework/core/Logging.hpp>
 #include <corsika/framework/core/PhysicalUnits.hpp>
+#include <corsika/framework/random/RNGManager.hpp>
 
 namespace corsika::fluka {
   class InteractionModel {
@@ -39,14 +40,14 @@ namespace corsika::fluka {
                        FourMomentum const& projectileP4, FourMomentum const& targetP4);
 
   private:
+    default_prng_type& RNG_ = RNGManager<>::getInstance().getRandomStream("fluka");
     std::vector<std::pair<Code, int>> const
         materials_; //!< map target Code to FLUKA material no.
     std::shared_ptr<spdlog::logger> logger_ = get_logger("corsika_FLUKA_Interaction");
-    std::unique_ptr<double[]> cumsgx_; //!< dump for evtxyz cumsg*
+    std::unique_ptr<double[]> cumsgx_; //!< dump for evtxyz cumsg*, never read again
 
     template <typename TEnvironment>
     static std::vector<std::pair<Code, int>> genFlukaMaterials(TEnvironment const&);
-    // TODO: random number stream
   };
 
   inline static int const iflxyz_ = 1;
diff --git a/corsika/modules/fluka/ParticleConversion.hpp b/corsika/modules/fluka/ParticleConversion.hpp
index 64e97dea0..770cc8c53 100644
--- a/corsika/modules/fluka/ParticleConversion.hpp
+++ b/corsika/modules/fluka/ParticleConversion.hpp
@@ -27,13 +27,14 @@ namespace corsika::fluka {
 
   FLUKACode constexpr convertToFluka(Code const c8id) {
     if (is_nucleus(c8id)) {
-		throw std::runtime_error{"nucleus conversion to FLUKA not implemented"};
-	}
+      throw std::runtime_error{"nucleus conversion to FLUKA not implemented"};
+    }
 
     FLUKACode const flukaID = corsika2fluka[static_cast<corsika::CodeIntType>(c8id)];
     if (flukaID == FLUKACode::Unknown) {
-		throw std::runtime_error{fmt::format("no correspondig FLUKA id for {}", get_name(c8id)).c_str()};
-	}
+      throw std::runtime_error{
+          fmt::format("no correspondig FLUKA id for {}", get_name(c8id)).c_str()};
+    }
 
     return flukaID;
   }
diff --git a/corsika/modules/fluka/Random.hpp b/corsika/modules/fluka/Random.hpp
new file mode 100644
index 000000000..73cf5377a
--- /dev/null
+++ b/corsika/modules/fluka/Random.hpp
@@ -0,0 +1,26 @@
+/*
+ * (c) Copyright 2023 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * This software is distributed under the 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>
+
+#include <corsika/framework/random/RNGManager.hpp>
+
+namespace fluka {
+  /**
+   * the random number generator function for FLUKA
+   */
+  double rndm_interface() {
+    static corsika::default_prng_type& rng =
+        corsika::RNGManager<>::getInstance().getRandomStream("fluka");
+    static std::uniform_real_distribution<double> dist;
+    return dist(rng);
+  }
+
+} // namespace fluka
diff --git a/modules/fluka/CMakeLists.txt b/modules/fluka/CMakeLists.txt
index 626901998..2d56bdc85 100644
--- a/modules/fluka/CMakeLists.txt
+++ b/modules/fluka/CMakeLists.txt
@@ -1,16 +1,51 @@
 set(C8_FLUKALIB CACHE STRING "path to libflukahp.a")
 
 if (DEFINED C8_FLUKALIB)
-    add_library(flukahp STATIC IMPORTED)
-    set_property(TARGET flukahp PROPERTY IMPORTED_LOCATION ${C8_FLUKALIB})
+    set (input_dir ${PROJECT_SOURCE_DIR}/src/modules/fluka)
+    
+    # we remove flrndm_.o from the original libflukahp.a and save the result as libflukahp-norndm.a
+    add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/libflukahp-norndm.a COMMAND ${input_dir}/strip_flukahp.sh ${C8_FLUKALIB} ${CMAKE_BINARY_DIR})
+    add_custom_target(generate_libfluka-norndm DEPENDS ${CMAKE_BINARY_DIR}/libflukahp-norndm.a)
+
+    add_library(flukahp-norndm STATIC IMPORTED)
+    add_dependencies(flukahp-norndm generate_libfluka-norndm)
+    set_property(TARGET flukahp-norndm PROPERTY IMPORTED_LOCATION ${CMAKE_BINARY_DIR}/libflukahp-norndm.a)
     find_library(MATH_LIBRARY m)
-    target_link_libraries(flukahp INTERFACE gfortran ${MATH_LIBRARY})
-    target_include_directories (flukahp INTERFACE
+    target_link_libraries(flukahp-norndm INTERFACE gfortran ${MATH_LIBRARY})
+    target_include_directories (flukahp-norndm INTERFACE
         $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
         $<INSTALL_INTERFACE:include/corsika_modules/fluka>
     )
     
+    # this contains our own implementation of flrndm_()
+    add_library(flukaRndm SHARED flrndm.cpp)
+    target_link_libraries(flukaRndm PRIVATE flukahp-norndm)
+
+    target_include_directories (flukaRndm PUBLIC
+      $<BUILD_INTERFACE:${CMAKE_CURRENT_SOURCE_DIR}>
+      $<INSTALL_INTERFACE:include/corsika_modules/fluka>
+      )
+    
+    set_target_properties (
+      flukaRndm
+      PROPERTIES
+      POSITION_INDEPENDENT_CODE 1
+      )
+    
+    install (
+      FILES
+      fluka.hpp
+      DESTINATION include/corsika_modules/urqmd
+      )
+    
+    install (
+      TARGETS flukaRndm
+      EXPORT CORSIKA8PublicTargets
+      ARCHIVE DESTINATION lib/corsika
+      INCLUDES DESTINATION include/corsika_modules/fluka
+      )
+    
     # add fluka to corsika8 build
-    add_dependencies (CORSIKA8 flukahp)
-    target_link_libraries (CORSIKA8 INTERFACE flukahp)
+    add_dependencies (CORSIKA8 flukaRndm)
+    target_link_libraries (CORSIKA8 INTERFACE flukaRndm)
 endif()
diff --git a/modules/fluka/FLUKA.hpp b/modules/fluka/FLUKA.hpp
index e24f6d22e..25b55f904 100644
--- a/modules/fluka/FLUKA.hpp
+++ b/modules/fluka/FLUKA.hpp
@@ -8,7 +8,19 @@
 
 #pragma once
 
+#include <cstddef>
+#include <array>
+
 namespace fluka {
+  /**
+   * \fluka fluka::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();
+
   size_t constexpr nmxhep = 10000;
   template <typename T>
   using hepmc_array = std::array<T, nmxhep>;
@@ -116,7 +128,7 @@ namespace fluka {
   void stpxyz_(int const* NMATFL, int const NELMFL[], int const IZELFL[],
                double const WFELFL[], int const* MXELFL, double const* PPTMAX,
                double const* EF2DP3, double const* DF2DP3, int const* IFLXYZ,
-               bool const* LPRINT, int* MTFLKA, char CRVRCK[8]);
+               bool const* LPRINT, int* MTFLKA, char const* CRVRCK, int const*);
 
   /*----------------------------------------------------------------------*
    *                                                                      *
@@ -191,5 +203,7 @@ namespace fluka {
    *                                                                      *
    *----------------------------------------------------------------------*/
   void fllhep_();
+  
+  double flrndm();
   }
 } // namespace fluka
diff --git a/modules/fluka/flrndm.cpp b/modules/fluka/flrndm.cpp
new file mode 100644
index 000000000..79f3a82ae
--- /dev/null
+++ b/modules/fluka/flrndm.cpp
@@ -0,0 +1,31 @@
+/*
+ * (c) Copyright 2023 CORSIKA Project, corsika-project@lists.kit.edu
+ *
+ * This software is distributed 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 <FLUKA.hpp>
+
+namespace fluka {
+    
+    /**
+     * The following (function) pointers make sure the corresponding objects in libflukahp.a
+     * won't get dropped from the final file during linking.
+     */
+
+    auto* const hepevt_ptr = &hepevt_;
+    auto* const stpxyc_ptr = &stpxyz_;
+    auto* const evtxyz_ptr = &evtxyz_;
+    auto* const sgmxyz_ptr = &sgmxyz_;
+    auto* const fllhep_ptr = &fllhep_;
+
+extern "C" {
+  double flrndm_() { return ::fluka::rndm_interface(); }
+  void bla() {
+      fllhep_ptr();
+  }
+}
+
+}
diff --git a/src/modules/fluka/CMakeLists.txt b/src/modules/fluka/CMakeLists.txt
index 8ce30ed94..2d2d26b44 100644
--- a/src/modules/fluka/CMakeLists.txt
+++ b/src/modules/fluka/CMakeLists.txt
@@ -23,4 +23,4 @@ add_dependencies (CORSIKA8 SourceDirLinkFLUKA)
 install (
   FILES ${output_dir}/Generated.inc
   DESTINATION include/corsika/modules/fluka
-  )
+)
diff --git a/src/modules/fluka/strip_flukahp.sh b/src/modules/fluka/strip_flukahp.sh
new file mode 100755
index 000000000..0599ca987
--- /dev/null
+++ b/src/modules/fluka/strip_flukahp.sh
@@ -0,0 +1,28 @@
+#!/bin/sh
+
+# (c) Copyright 2023 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.
+
+# This script strips off flrndm() from the libflukahp.a so that we can provide our own
+# implementation.
+
+flukalibOrig="$1"
+target="$2"
+
+tmpdir=`mktemp -d fluka_objectsXXXXXX`
+
+echo "extracting objects from $1 into `realpath $tmpdir`..."
+ar --output "$tmpdir" x "$flukalibOrig"
+rm "$tmpdir/flrndm.o"
+
+[ -f "libflukahp-norndm.a" ] && rm "libflukahp-norndm.a"
+
+echo "creating libflukahp-norndm.a..."
+ar -rcs "$target/libflukahp-norndm.a" "$tmpdir"/*.o
+
+rm -r "$tmpdir"
diff --git a/tests/modules/testFluka.cpp b/tests/modules/testFluka.cpp
index 0770bf06c..6ddcd96c8 100644
--- a/tests/modules/testFluka.cpp
+++ b/tests/modules/testFluka.cpp
@@ -7,6 +7,7 @@
  */
 
 #include <corsika/modules/FLUKA.hpp>
+#include <corsika/modules/fluka/Random.hpp>
 //~ #include <SetupTestEnvironment.hpp>
 
 #include <corsika/framework/core/EnergyMomentumOperations.hpp>
@@ -29,6 +30,13 @@
 
 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("FLUKACodeConversion") {
   REQUIRE(corsika::fluka::convertToFluka(Code::PiPlus) ==
           corsika::fluka::FLUKACode::PiPlus);
@@ -44,6 +52,8 @@ TEST_CASE("FLUKA") {
   using MyHomogeneousModel = MediumPropertyModel<
       UniformMagneticField<HomogeneousMedium<DummyEnvironmentInterface>>>;
 
+  RNGManager<>::getInstance().registerRandomStream("fluka");
+
   DummyEnvironment env;
   auto& universe = *env.getUniverse();
   CoordinateSystemPtr const& cs = env.getCoordinateSystem();
@@ -99,20 +109,35 @@ TEST_CASE("FLUKA") {
   {
     auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Proton);
     auto const& cs = *csPtr;
-    auto [stackPtr, secViewPtr] = setup::testing::setup_stack(
-        Code::Hydrogen, 1_GeV, (DummyEnvironment::BaseNodeType* const)nodePtr, *csPtr);
-    { [[maybe_unused]] auto const& dummy_StackPtr = stackPtr; }
-
-    auto const projectileCode = Code::PiPlus;
-    auto const targetCode = Code::Nitrogen;
-    auto const p = 20_GeV;
-    auto const projectile4mom =
-        FourVector{calculate_total_energy(p, get_mass(projectileCode)),
-                   MomentumVector{cs, 0_eV, 0_eV, p}};
-    auto const target4mom =
-        FourVector{get_mass(targetCode), MomentumVector{cs, 0_eV, 0_eV, 0_eV}};
-
-    flukaModel.doInteraction(*secViewPtr, projectileCode, targetCode, projectile4mom,
-                             target4mom);
+
+    auto const projectiles = std::array{Code::PiPlus, Code::PiMinus, Code::KPlus,
+                                        Code::K0Long, Code::Lambda0, Code::SigmaPlus};
+    auto const momenta = std::array{100_MeV, 1_GeV, 20_GeV, 100_GeV, 1_TeV};
+
+    for (auto const p : momenta) {
+      for (auto const projectileCode : projectiles) {
+        std::cout << "===== " << projectileCode << " @ " << p << "\n";
+
+        auto [stackPtr, secViewPtr] = setup::testing::setup_stack(
+            Code::Hydrogen, 1_GeV, (DummyEnvironment::BaseNodeType* const)nodePtr,
+            *csPtr);
+        { [[maybe_unused]] auto const& dummy_StackPtr = stackPtr; }
+
+        auto const targetCode = Code::Oxygen;
+        auto const projectile4mom =
+            FourVector{calculate_total_energy(p, get_mass(projectileCode)),
+                       MomentumVector{cs, 0_eV, 0_eV, p}};
+        auto const target4mom =
+            FourVector{get_mass(targetCode), MomentumVector{cs, 0_eV, 0_eV, 0_eV}};
+
+        flukaModel.doInteraction(*secViewPtr, projectileCode, targetCode, projectile4mom,
+                                 target4mom);
+        auto const pSum = sumMomentum(*secViewPtr, cs);
+        std::cout << "psum = " << pSum << '\n';
+        CHECK((pSum - projectile4mom.getSpaceLikeComponents()).getNorm() / p ==
+              Approx(0).margin(1e-4));
+        CHECK((pSum.getNorm() - p) / p == Approx(0).margin(1e-4));
+      }
+    }
   }
 }
-- 
GitLab