From 78e2efea175a164e494625faae2944f218a89f8b Mon Sep 17 00:00:00 2001
From: Maximilian Reininghaus <maximilian.reininghaus@kit.edu>
Date: Wed, 24 Apr 2019 19:04:11 -0300
Subject: [PATCH] primitive unit test for charge conservation

---
 Processes/UrQMD/UrQMD.cc     | 68 +++++++++++--------------------
 Processes/UrQMD/testUrQMD.cc | 79 ++++++++++++++++++++++++++++--------
 2 files changed, 86 insertions(+), 61 deletions(-)

diff --git a/Processes/UrQMD/UrQMD.cc b/Processes/UrQMD/UrQMD.cc
index 293d0c478..2f48668b7 100644
--- a/Processes/UrQMD/UrQMD.cc
+++ b/Processes/UrQMD/UrQMD.cc
@@ -50,50 +50,46 @@ corsika::process::EProcessReturn UrQMD::DoInteraction(SetupProjectile& projectil
   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(projectileCode, c, projectileEnergyLab));
+    }
+
+    return crossSections;
+  });
+
+  auto const targetCode = mediumComposition.SampleTarget(componentCrossSections, fRNG);
+  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;
 
-  // todo: sample target
-
-  int const Atarget = 14;
-  int tableIndex = 0; // 0: nitrogen, 1: oxygen, 2: argon target
-
-  /*
-  // corsika 7
-        bmin    = 0.d0
-        CTOption(5) = 1
-        if ( iflbmax.eq.1 ) then
-          bdist       = BIM(LIT)
-        else
-          bdist=nucrad(Ap)+nucrad(At)+2*CTParam(30)
-        endif
-
-  // conex
-        CTOption(5)=1
-        if ( prspflg.eq.0 ) then
-          bdist = BIM(LT)
-        else
-          bdist = xsbmax
-        endif
-  */
-
   // initialization regarding projectile
   if (particles::Code::Nucleus == projectileCode) {
     // is this everything?
     inputs_.prspflg = 0;
-    rsys_.bdist = cxs_u2_.bim[tableIndex];
 
     sys_.Ap = projectile.GetNuclearA();
     sys_.Zp = projectile.GetNuclearZ();
 
+    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_(Atarget) + nucrad_(1) + 2 * options_.CTParam[30 - 1];
+    rsys_.bdist = nucrad_(targetA) + nucrad_(1) + 2 * options_.CTParam[30 - 1];
 
     auto const [ityp, iso3] = ConvertToUrQMD(projectileCode);
     // todo: conversion of K_long/short into strong eigenstates;
@@ -104,25 +100,9 @@ corsika::process::EProcessReturn UrQMD::DoInteraction(SetupProjectile& projectil
   rsys_.ebeam = (projectileEnergyLab - projectile.GetMass()) * (1 / 1_GeV);
 
   // initilazation regarding target
-  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(projectileCode, c, projectileEnergyLab));
-    }
-
-    return crossSections;
-  });
-
-  auto const targetCode = mediumComposition.SampleTarget(componentCrossSections, fRNG);
-
   if (particles::IsNucleus(targetCode)) {
-    sys_.Zt = particles::GetNucleusZ(targetCode);
-    sys_.At = particles::GetNucleusA(targetCode);
+    sys_.Zt = targetZ;
+    sys_.At = targetA;
     inputs_.trspflg = 0; // nucleus as target
     int const id = 2;
     cascinit_(sys_.Zt, sys_.At, id);
diff --git a/Processes/UrQMD/testUrQMD.cc b/Processes/UrQMD/testUrQMD.cc
index 2dad439de..c715f1180 100644
--- a/Processes/UrQMD/testUrQMD.cc
+++ b/Processes/UrQMD/testUrQMD.cc
@@ -18,6 +18,8 @@
 #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>
@@ -34,7 +36,23 @@ using namespace corsika;
 using namespace corsika::process::UrQMD;
 using namespace corsika::units::si;
 
+template <typename TStack>
+auto sumCharge(TStack& stack) {
+  int totalCharge = 0;
+  int count = 0;
+  for (auto& p : stack) {
+    count++;
+    totalCharge += particles::GetChargeNumber(p.GetPID());
+    std::cout << p.GetPID() << " " << particles::GetChargeNumber(p.GetPID()) << std::endl;
+  }
+
+  std::cout << count << " particles on stack" << std::endl;
+
+  return totalCharge;
+}
+
 TEST_CASE("UrQMD") {
+  feenableexcept(FE_INVALID);
   corsika::random::RNGManager::GetInstance().RegisterRandomStream("UrQMD");
   UrQMD urqmd;
 
@@ -63,21 +81,48 @@ TEST_CASE("UrQMD") {
   geometry::Line line(origin, v);
   geometry::Trajectory<geometry::Line> track(line, 10_s);
 
-  auto constexpr mN = corsika::units::constants::nucleonMass;
-
-  setup::Stack stack;
-  const HEPEnergyType P0 = 50_GeV;
-  unsigned short constexpr A = 56, Z = 26;
-  HEPMomentumType E0 = sqrt(A * A * mN * mN + P0 * P0);
-  auto plab = corsika::stack::MomentumVector(cs, {P0, 0_GeV, 0_GeV});
-  auto particle =
-      stack.AddParticle(std::tuple<particles::Code, units::si::HEPEnergyType,
-                                   corsika::stack::MomentumVector, geometry::Point,
-                                   units::si::TimeType, unsigned short, unsigned short>{
-          particles::Code::Nucleus, E0, plab, origin, 0_ns, A, Z}); // iron
-  particle.SetNode(nodePtr);
-  corsika::stack::SecondaryView view(particle);
-  auto projectile = view.GetProjectile();
-
-  [[maybe_unused]] const process::EProcessReturn ret = urqmd.DoInteraction(projectile);
+  const HEPEnergyType P0 = 1000_GeV;
+  auto pLab = corsika::stack::MomentumVector(cs, {P0, 0_GeV, 0_GeV});
+
+  SECTION("nucleon projectile") {
+    setup::Stack stack;
+
+    unsigned short constexpr A = 16, Z = 8;
+    auto constexpr mN = corsika::units::constants::nucleonMass;
+    HEPMomentumType E0 = sqrt(A * A * mN * mN + P0 * P0);
+    auto particle =
+        stack.AddParticle(std::tuple<particles::Code, units::si::HEPEnergyType,
+                                     corsika::stack::MomentumVector, geometry::Point,
+                                     units::si::TimeType, unsigned short, unsigned short>{
+            particles::Code::Nucleus, E0, pLab, origin, 0_ns, A, Z});
+
+    particle.SetNode(nodePtr);
+    corsika::stack::SecondaryView view(particle);
+    auto projectile = view.GetProjectile();
+
+    [[maybe_unused]] process::EProcessReturn const ret = urqmd.DoInteraction(projectile);
+
+    REQUIRE(sumCharge(stack) == Z + particles::GetChargeNumber(particles::Code::Oxygen));
+  }
+
+  SECTION("\"special\" projectile") {
+
+    setup::Stack stack;
+
+    auto constexpr code = particles::Code::Neutron;
+    auto constexpr mass = particles::GetMass(code);
+    HEPMomentumType E0 = sqrt(mass * mass + pLab.squaredNorm());
+    auto particle = stack.AddParticle(
+        std::tuple<particles::Code, units::si::HEPEnergyType,
+                   corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{
+            code, E0, pLab, origin, 0_ns});
+    particle.SetNode(nodePtr);
+    corsika::stack::SecondaryView view(particle);
+    auto projectile = view.GetProjectile();
+
+    [[maybe_unused]] process::EProcessReturn const ret = urqmd.DoInteraction(projectile);
+
+    REQUIRE(sumCharge(stack) == particles::GetChargeNumber(particles::Code::PiPlus) +
+                                    particles::GetChargeNumber(particles::Code::Oxygen));
+  }
 }
-- 
GitLab