diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 879e1ac58766a03b0afd06f012e3bb207c4e8db8..c83d5488bb8ccdb2ad14b619b74bf39328d88ab9 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -18,8 +18,10 @@ build: - cmake .. - cmake --build . - ctest -j4 -V >& test.log - - gzip -9 -S .gz test.log + after_script: + - cd build - ls + - gzip -v -9 -S .gz test.log - pwd artifacts: expire_in: 1 week diff --git a/Documentation/Examples/cascade_example.cc b/Documentation/Examples/cascade_example.cc index 772853db877c6f099370b3b8f35ee949b2129c45..67b6f4d9160b18cfea3ed42b5564d9f24b688098 100644 --- a/Documentation/Examples/cascade_example.cc +++ b/Documentation/Examples/cascade_example.cc @@ -71,12 +71,22 @@ public: template <typename Particle> bool isBelowEnergyCut(Particle& p) const { - // FOR NOW: center-of-mass energy hard coded - const HEPEnergyType Ecm = sqrt(2. * p.GetEnergy() * 0.93827_GeV); - if (p.GetEnergy() < fECut || Ecm < 10_GeV) - return true; - else - return false; + // nuclei + if (p.GetPID() == corsika::particles::Code::Nucleus) { + auto const ElabNuc = p.GetEnergy() / p.GetNuclearA(); + auto const EcmNN = sqrt(2. * ElabNuc * 0.93827_GeV); + if (ElabNuc < fECut || EcmNN < 10_GeV) + return true; + else + return false; + } else { + // TODO: center-of-mass energy hard coded + const HEPEnergyType Ecm = sqrt(2. * p.GetEnergy() * 0.93827_GeV); + if (p.GetEnergy() < fECut || Ecm < 10_GeV) + return true; + else + return false; + } } bool isEmParticle(Code pCode) const { @@ -244,7 +254,6 @@ int main() { corsika::random::RNGManager::GetInstance().RegisterRandomStream("s_rndm"); corsika::process::sibyll::Interaction sibyll(env); - // corsika::process::sibyll::NuclearInteraction sibyllNuc(env); corsika::process::sibyll::NuclearInteraction sibyllNuc(env, sibyll); corsika::process::sibyll::Decay decay; ProcessCut cut(20_GeV); @@ -265,9 +274,14 @@ int main() { // setup particle stack, and add primary particle setup::Stack stack; stack.Clear(); - const Code beamCode = Code::Proton; + const Code beamCode = Code::Nucleus; + const int nuclA = 56; + const int nuclZ = int(nuclA / 2.15 + 0.7); + const HEPMassType mass = corsika::particles::Proton::GetMass() * nuclZ + + (nuclA - nuclZ) * corsika::particles::Neutron::GetMass(); const HEPEnergyType E0 = - 100_TeV; // 1_PeV crashes with bad COMboost in second interaction (crash later) + nuclA * + 100_GeV; // 1_PeV crashes with bad COMboost in second interaction (crash later) double theta = 0.; double phi = 0.; @@ -275,7 +289,7 @@ int main() { auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) { return sqrt(Elab * Elab - m * m); }; - HEPMomentumType P0 = elab2plab(E0, corsika::particles::GetMass(beamCode)); + 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)); @@ -287,7 +301,7 @@ int main() { cout << "input angles: theta=" << theta << " phi=" << phi << endl; cout << "input momentum: " << plab.GetComponents() / 1_GeV << endl; Point pos(rootCS, 0_m, 0_m, 0_m); - stack.AddParticle(beamCode, E0, plab, pos, 0_ns); + stack.AddParticle(beamCode, E0, plab, pos, 0_ns, nuclA, nuclZ); } // define air shower object, run simulation diff --git a/Framework/Particles/NuclearData.xml b/Framework/Particles/NuclearData.xml index 0fff50c33839093fc8bfacb9a7632f36110a2492..73eda0ef2350aa6849df60c61a34231dd9fff869 100644 --- a/Framework/Particles/NuclearData.xml +++ b/Framework/Particles/NuclearData.xml @@ -18,19 +18,19 @@ <particle id="1000020030" name="helium3" A="3" Z="2" > </particle> -<particle id="1000020030" name="lithium" A="6" Z="3" > +<particle id="1000030070" name="lithium7" A="7" Z="3" > </particle> -<particle id="1000020030" name="beryllium" A="9" Z="4" > +<particle id="1000040090" name="beryllium9" A="9" Z="4" > </particle> -<particle id="1000020030" name="boron" A="10" Z="5" > +<particle id="1000110050" name="boron11" A="11" Z="5" > </particle> <particle id="1000060120" name="carbon" A="12" Z="6" > </particle> -<particle id="1000060120" name="carbon13" A="13" Z="6" > +<particle id="1000060130" name="carbon13" A="13" Z="6" > </particle> <particle id="1000070140" name="nitrogen" A="14" Z="7" > @@ -39,10 +39,10 @@ <particle id="1000080160" name="oxygen" A="16" Z="8" > </particle> -<particle id="1000080160" name="fluor" A="18" Z="9" > +<particle id="1000080180" name="fluor" A="18" Z="9" > </particle> -<particle id="1000100220" name="neon21" A="21" Z="10" > +<particle id="1000100210" name="neon21" A="21" Z="10" > </particle> <particle id="1000100220" name="neon" A="22" Z="10" > diff --git a/Framework/Particles/ParticleProperties.cc b/Framework/Particles/ParticleProperties.cc index 4932492ddf33028b456670e9ca2da0a5b8d0efe2..b90a744bd38f5dc2755a2f10753af2e7c7386081 100644 --- a/Framework/Particles/ParticleProperties.cc +++ b/Framework/Particles/ParticleProperties.cc @@ -27,5 +27,16 @@ 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); + auto constexpr maxPDG{(detail::conversionArray.size() - 1) >> 1}; + auto k = static_cast<PDGCodeType>(p); + if (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 index ad1a14e620899089e94093722c630e218b8db4c2..7b2b6f7e5594efa6aa4c5014f002bcc78d328701 100644 --- a/Framework/Particles/ParticleProperties.h +++ b/Framework/Particles/ParticleProperties.h @@ -23,13 +23,12 @@ #include <iosfwd> #include <type_traits> -#include <corsika/units/PhysicalConstants.h> #include <corsika/units/PhysicalUnits.h> /** * * The properties of all elementary particles is stored here. The data - * is taken from the Pythia ParticleData.xml file. + * are taken from the Pythia ParticleData.xml file. * */ @@ -40,14 +39,15 @@ namespace corsika::particles { * 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 = int32_t; + using PDGCodeType = std::underlying_type<PDGCode>::type; // forward declarations to be used in GeneratedParticleProperties int16_t constexpr GetElectricChargeNumber(Code const); corsika::units::si::ElectricChargeType constexpr GetElectricCharge(Code const); corsika::units::si::HEPMassType constexpr GetMass(Code const); - PDGCodeType constexpr GetPDG(Code const); + PDGCode constexpr GetPDG(Code const); constexpr std::string const& GetName(Code const); corsika::units::si::TimeType constexpr GetLifetime(Code const); @@ -58,46 +58,52 @@ namespace corsika::particles { #include <corsika/particles/GeneratedParticleProperties.inc> /*! - * returns mass of particle + * returns mass of particle in natural units */ corsika::units::si::HEPMassType constexpr GetMass(Code const p) { - return detail::masses[static_cast<CodeIntType const>(p)]; + return detail::masses[static_cast<CodeIntType>(p)]; } - PDGCodeType constexpr GetPDG(Code const p) { - return detail::pdg_codes[static_cast<CodeIntType const>(p)]; + /*! + * returns PDG id + */ + PDGCode constexpr GetPDG(Code const p) { + return detail::pdg_codes[static_cast<CodeIntType>(p)]; } /*! - * returns electric charge of particle / (e/3). + * returns electric charge of particle / (e/3), e.g. return 3 for a proton. */ int16_t constexpr GetElectricChargeNumber(Code const p) { - return detail::electric_charges[static_cast<CodeIntType const>(p)]; + return detail::electric_charges[static_cast<CodeIntType>(p)]; } + /*! + * returns electric charge of particle, e.g. return 1.602e-19_C for a proton. + */ corsika::units::si::ElectricChargeType constexpr GetElectricCharge(Code const p) { - return GetElectricChargeNumber(p) * (corsika::units::constants::e / 3.); + return GetElectricChargeNumber(p) * (corsika::units::constants::e * (1. / 3.)); } constexpr std::string const& GetName(Code const p) { - return detail::names[static_cast<CodeIntType const>(p)]; + return detail::names[static_cast<CodeIntType>(p)]; } corsika::units::si::TimeType constexpr GetLifetime(Code const p) { - return detail::lifetime[static_cast<CodeIntType const>(p)] * + return detail::lifetime[static_cast<CodeIntType>(p)] * corsika::units::si::second; } bool constexpr IsNucleus(Code const p) { - return detail::isNucleus[static_cast<CodeIntType const>(p)]; + return detail::isNucleus[static_cast<CodeIntType>(p)]; } int constexpr GetNucleusA(Code const p) { - return detail::nucleusA[static_cast<CodeIntType const>(p)]; + return detail::nucleusA[static_cast<CodeIntType>(p)]; } int constexpr GetNucleusZ(Code const p) { - return detail::nucleusZ[static_cast<CodeIntType const>(p)]; + return detail::nucleusZ[static_cast<CodeIntType>(p)]; } /** @@ -105,7 +111,8 @@ namespace corsika::particles { **/ std::ostream& operator<<(std::ostream& stream, corsika::particles::Code const p); - + + Code ConvertFromPDG(PDGCode); } // namespace corsika::particles #endif diff --git a/Framework/Particles/pdxml_reader.py b/Framework/Particles/pdxml_reader.py index 99544cf5029c933ef9a43478dae73d28c2efbb56..946940f7dcf54551e2888e438c6eeeae0e1659e1 100755 --- a/Framework/Particles/pdxml_reader.py +++ b/Framework/Particles/pdxml_reader.py @@ -4,6 +4,7 @@ import sys, math, itertools, re, csv, pprint import xml.etree.ElementTree as ET from collections import OrderedDict import pickle +import io GeVfm = 0.19732696312541853 c_speed_of_light = 29.9792458e10 # mm / s @@ -39,7 +40,7 @@ def parsePythia(filename): ctau = 0. else: print ("missing lifetime: " + str(pdg_id) + " " + str(name)) - sys.exit(0) + sys.exit(1) yield (pdg_id, name, mass, electric_charge, antiName, ctau/c_speed_of_light) @@ -93,42 +94,7 @@ def class_names(filename): pdg_id = int(particle.attrib["pdgID"]) map[pdg_id] = name - return map - - - -############################################################## -# -# Automatically produce a string qualifying as C++ class name -# -# This function produces names of type "DELTA_PLUS_PLUS" -# -def c_identifier(name): - orig = name - name = name.upper() - for c in "() /": - name = name.replace(c, "_") - - name = name.replace("BAR", "_BAR") - name = name.replace("0", "_0") - name = name.replace("*", "_STAR") - name = name.replace("'", "_PRIME") - name = name.replace("+", "_PLUS") - name = name.replace("-", "_MINUS") - - while True: - tmp = name.replace("__", "_") - if tmp == name: - break - else: - name = tmp - - pattern = re.compile(r'^[A-Z_][A-Z_0-9]*$') - if pattern.match(name): - return name.strip("_") - else: - raise Exception("could not generate C identifier for '{:s}'".format(orig)) - + return map ############################################################## # @@ -163,7 +129,7 @@ def c_identifier_camel(name): name = tmp name.strip("_") - # remove all "_", if this does not by accident concatenate two number + # remove all "_", if this does not by accident concatenate two numbers istart = 0 while True: i = name.find('_', istart) @@ -253,6 +219,47 @@ def read_nuclei_db(filename, particle_db, classnames): return particle_db +############################################################### +# +# build conversion table PDG -> ngc +# +def gen_conversion_PDG_ngc(particle_db): + # todo: find a optimum value, think about cache miss with array vs lookup time with map + P_MAX = 500 # the maximum PDG code that is still filled into the table + + conversionDict = dict() + conversionTable = [None] * (2*P_MAX + 1) + for cId, p in particle_db.items(): + pdg = p['pdg'] + + if abs(pdg) < P_MAX: + if conversionTable[pdg + P_MAX]: + raise Exception("table entry already occupied") + else: + conversionTable[pdg + P_MAX] = cId + else: + if pdg in conversionDict.keys(): + raise Exception(f"map entry {pdg} already occupied") + else: + conversionDict[pdg] = cId + + output = io.StringIO() + def oprint(*args, **kwargs): + print(*args, **kwargs, file=output) + + oprint(f"static std::array<Code, {len(conversionTable)}> constexpr conversionArray {{") + for ngc in conversionTable: + oprint(" Code::{0},".format(ngc if ngc else "Unknown")) + oprint("};") + oprint() + + oprint("static std::map<PDGCode, Code> const conversionMap {") + for ngc in conversionDict.values(): + oprint(f" {{PDGCode::{ngc}, Code::{ngc}}},") + oprint("};") + oprint() + + return output.getvalue() ############################################################### @@ -260,7 +267,7 @@ def read_nuclei_db(filename, particle_db, classnames): # return string with enum of all internal particle codes # def gen_internal_enum(particle_db): - string = ("enum class Code : int16_t {\n" + string = ("enum class Code : CodeIntType {\n" " FirstParticle = 1, // if you want to loop over particles, you want to start with \"1\" \n") # identifier for eventual loops... @@ -277,6 +284,20 @@ def gen_internal_enum(particle_db): return string +############################################################### +# +# return string with enum of all PDG particle codes +# +def gen_pdg_enum(particle_db): + string = "enum class PDGCode : PDGCodeType {\n" + + for cId in particle_db: + pdgCode = particle_db[cId]['pdg'] + string += " {key:s} = {code:d},\n".format(key = cId, code = pdgCode) + + string += " };\n" + + return string ############################################################### @@ -296,9 +317,9 @@ def gen_properties(particle_db): string += "};\n\n" # PDG code table - string += "static constexpr std::array<PDGCodeType const, size> pdg_codes = {\n" - for p in particle_db.values(): - string += " {pdg:d}, // {name:s}\n".format(pdg = p['pdg'], name = p['name']) + string += "static constexpr std::array<PDGCode, size> pdg_codes = {\n" + for p in particle_db.keys(): + string += f" PDGCode::{p},\n" string += "};\n" # name string table @@ -473,8 +494,10 @@ if __name__ == "__main__": with open("GeneratedParticleProperties.inc", "w") as f: print(inc_start(), file=f) print(gen_internal_enum(particle_db), file=f) + print(gen_pdg_enum(particle_db), file=f) print(detail_start(), file=f) print(gen_properties(particle_db), file=f) + print(gen_conversion_PDG_ngc(particle_db), file=f) print(detail_end(), file=f) print(gen_classes(particle_db), file=f) print(inc_end(), file=f) diff --git a/Framework/Particles/testParticles.cc b/Framework/Particles/testParticles.cc index 1ca8b2188b5004c22c3168b5ad90290aa10a8ffb..cba91b476f42e6ee540ebfbf64b5990c4e03ea81 100644 --- a/Framework/Particles/testParticles.cc +++ b/Framework/Particles/testParticles.cc @@ -34,6 +34,8 @@ TEST_CASE("ParticleProperties", "[Particles]") { SECTION("Masses") { REQUIRE(Electron::GetMass() / (511_keV) == Approx(1)); REQUIRE(Electron::GetMass() / GetMass(Code::Electron) == Approx(1)); + + REQUIRE((Proton::GetMass() + Neutron::GetMass()) / corsika::units::constants::nucleonMass == Approx(2)); } SECTION("Charges") { @@ -50,11 +52,23 @@ TEST_CASE("ParticleProperties", "[Particles]") { } SECTION("PDG") { - REQUIRE(GetPDG(Code::PiPlus) == 211); - REQUIRE(GetPDG(Code::DPlus) == 411); - REQUIRE(GetPDG(Code::NuMu) == 14); - REQUIRE(GetPDG(Code::NuE) == 12); - REQUIRE(GetPDG(Code::MuMinus) == 13); + REQUIRE(GetPDG(Code::PiPlus) == PDGCode::PiPlus); + REQUIRE(GetPDG(Code::DPlus) == PDGCode::DPlus); + REQUIRE(GetPDG(Code::NuMu) == PDGCode::NuMu); + REQUIRE(GetPDG(Code::NuE) == PDGCode::NuE); + REQUIRE(GetPDG(Code::MuMinus) == PDGCode::MuMinus); + + REQUIRE(static_cast<int>(GetPDG(Code::PiPlus)) == 211); + REQUIRE(static_cast<int>(GetPDG(Code::DPlus)) == 411); + REQUIRE(static_cast<int>(GetPDG(Code::NuMu)) == 14); + REQUIRE(static_cast<int>(GetPDG(Code::NuEBar)) == -12); + REQUIRE(static_cast<int>(GetPDG(Code::MuMinus)) == 13); + } + + SECTION("Conversion PDG -> internal") { + REQUIRE(ConvertFromPDG(PDGCode::KStarMinus) == Code::KStarMinus); + REQUIRE(ConvertFromPDG(PDGCode::MuPlus) == Code::MuPlus); + REQUIRE(ConvertFromPDG(PDGCode::SigmaStarCMinusBar) == Code::SigmaStarCMinusBar); } SECTION("Lifetimes") { @@ -70,12 +84,12 @@ TEST_CASE("ParticleProperties", "[Particles]") { } SECTION("Nuclei") { - REQUIRE(IsNucleus(Code::Gamma) == false); - REQUIRE(IsNucleus(Code::Argon) == true); - REQUIRE(IsNucleus(Code::Proton) == false); - REQUIRE(IsNucleus(Code::Hydrogen) == true); - REQUIRE(Argon::IsNucleus() == true); - REQUIRE(EtaC::IsNucleus() == false); + REQUIRE_FALSE(IsNucleus(Code::Gamma)); + REQUIRE(IsNucleus(Code::Argon)); + REQUIRE_FALSE(IsNucleus(Code::Proton)); + REQUIRE(IsNucleus(Code::Hydrogen)); + REQUIRE(Argon::IsNucleus()); + REQUIRE_FALSE(EtaC::IsNucleus()); REQUIRE(GetNucleusA(Code::Hydrogen) == 1); REQUIRE(GetNucleusA(Code::Tritium) == 3); diff --git a/Framework/Units/PhysicalConstants.h b/Framework/Units/PhysicalConstants.h index 38c59992fe928228754e3a16658043308e71ab68..1bf9872a03ed3abee9bcb03ef93fd24250f14a50 100644 --- a/Framework/Units/PhysicalConstants.h +++ b/Framework/Units/PhysicalConstants.h @@ -61,7 +61,9 @@ namespace corsika::units::constants { // unified atomic mass unit constexpr quantity<mass_d> u{Rep(1.6605402e-27L) * kilogram}; - + + auto constexpr nucleonMass = 0.5 * (0.93827 + 0.93957) * 1e9 * electronvolt; + // etc. } // namespace corsika::units::constants diff --git a/Framework/Units/PhysicalUnits.h b/Framework/Units/PhysicalUnits.h index 90b98e170f2a912018e1486cac26b7da1c85f3ff..af4dbb966aaaf5dfe5bfe7ace9619c9d299e94c7 100644 --- a/Framework/Units/PhysicalUnits.h +++ b/Framework/Units/PhysicalUnits.h @@ -18,28 +18,11 @@ namespace phys::units { /** * @file PhysicalUnits * - * Add new units and types we need. Units are compile-time. We support - * different system of units in parallel. Literals are used for - * optimal coding style. + * Add new units and types we need. Units are compile-time. Literals are + * used for optimal coding style. * */ -/* -namespace corsika::units::hep { - using namespace phys::units; - using namespace phys::units::literals; - using namespace phys::units::io; - - /// defining HEP energy, mass, momentum - using energy_hep_d = phys::units::energy_d; - constexpr phys::units::quantity<energy_hep_d> GeV{corsika::units::constants::eV}; - // corsika::units::constants::e / phys::units::coulomb * phys::units::joule }; - - using MassType = phys::units::quantity<energy_hep_d, double>; - using MomentumType = phys::units::quantity<energy_hep_d, double>; - using EnergyType = phys::units::quantity<energy_hep_d, double>; -} // namespace corsika::units::hep -*/ namespace corsika::units::si { using namespace phys::units; using namespace phys::units::literals; diff --git a/Processes/Sibyll/Interaction.h b/Processes/Sibyll/Interaction.h index 966c2f7f03afd4a60dd1dfc7dab66540d1f636b8..69755f00eb2e2923bf43e28935f0617b1ccce860 100644 --- a/Processes/Sibyll/Interaction.h +++ b/Processes/Sibyll/Interaction.h @@ -56,6 +56,11 @@ namespace corsika::process::sibyll { bool WasInitialized() { return fInitialized; } + bool ValidCoMEnergy(corsika::units::si::HEPEnergyType ecm) { + using namespace corsika::units::si; + return (10_GeV < ecm) && (ecm < 1_PeV); + } + std::tuple<corsika::units::si::CrossSectionType, corsika::units::si::CrossSectionType, int> GetCrossSection(const corsika::particles::Code BeamId, @@ -124,6 +129,7 @@ namespace corsika::process::sibyll { << " beam can interact:" << kInteraction << endl << " beam pid:" << p.GetPID() << endl; + // TODO: move limits into variables if (kInteraction && Elab >= 8.5_GeV && ECoM >= 10_GeV) { // get target from environment diff --git a/Processes/Sibyll/NuclearInteraction.h b/Processes/Sibyll/NuclearInteraction.h index 50384ddeba4657bcc6988dc50acd4a0614e22a6c..7b7492d4c335477e196116f003e405d3829177bd 100644 --- a/Processes/Sibyll/NuclearInteraction.h +++ b/Processes/Sibyll/NuclearInteraction.h @@ -47,7 +47,7 @@ namespace corsika::process::sibyll { using std::endl; // initialize hadronic interaction module - // TODO: save to run multiple initializations? + // TODO: safe to run multiple initializations? if (!fHadronicInteraction.WasInitialized()) fHadronicInteraction.Init(); // initialize nuclib @@ -56,38 +56,40 @@ namespace corsika::process::sibyll { } // TODO: remove number of nucleons, avg target mass is available in environment - std::tuple<corsika::units::si::CrossSectionType, corsika::units::si::CrossSectionType, - int> - GetCrossSection(const corsika::particles::Code BeamId, - const corsika::particles::Code TargetId, - const corsika::units::si::HEPEnergyType CoMenergy) { + template <typename Particle> + std::tuple<corsika::units::si::CrossSectionType, corsika::units::si::CrossSectionType> + GetCrossSection(Particle const& p, const corsika::particles::Code TargetId) { using namespace corsika::units::si; double sigProd; + auto const pCode = p.GetPID(); + if (pCode != corsika::particles::Code::Nucleus) + throw std::runtime_error( + "NuclearInteraction: GetCrossSection: particle not a nucleus!"); - std::cout << "NuclearInteraction: GetCrossSection: called with: beamId= " << BeamId - << " TargetId= " << TargetId << " CoMenergy= " << CoMenergy / 1_GeV - << std::endl; - - if (!corsika::particles::IsNucleus(BeamId)) { - // ask hadronic interaction model for hadron cross section - return fHadronicInteraction.GetCrossSection(BeamId, TargetId, CoMenergy); - } + auto const iBeam = p.GetNuclearA(); + HEPEnergyType LabEnergyPerNuc = p.GetEnergy() / iBeam; + std::cout << "NuclearInteraction: GetCrossSection: called with: beamNuclA= " + << iBeam << " TargetId= " << TargetId + << " LabEnergyPerNuc= " << LabEnergyPerNuc / 1_GeV << std::endl; // use nuclib to calc. nuclear cross sections // TODO: for now assumes air with hard coded composition // extend to arbitrary mixtures, requires smarter initialization - - const int iBeam = corsika::particles::GetNucleusA(BeamId); + // get nuclib projectile code: nucleon number if (iBeam > 56 || iBeam < 2) { - // std::cout<<"NuclearInteraction: beam nucleus outside allowed range for NUCLIB!" - // << std::endl; + std::cout << "NuclearInteraction: beam nucleus outside allowed range for NUCLIB!" + << std::endl + << "A=" << iBeam << std::endl; throw std::runtime_error( "NuclearInteraction: GetCrossSection: beam nucleus outside allowed range for " "NUCLIB!"); } - const double dEcm = CoMenergy / 1_GeV; - if (dEcm < 10.) + const double dElabNuc = LabEnergyPerNuc / 1_GeV; + // TODO: these limitations are still sibyll specific. + // available target nuclei depends on the hadronic interaction model and the + // initialization + if (dElabNuc < 10.) throw std::runtime_error("NuclearInteraction: GetCrossSection: energy too low!"); // TODO: these limitations are still sibyll specific. @@ -99,12 +101,17 @@ namespace corsika::process::sibyll { throw std::runtime_error( "Sibyll target outside range. Only nuclei with A<18 are allowed."); std::cout << "NuclearInteraction: calling signuc.." << std::endl; - signuc_(iBeam, dEcm, sigProd); + std::cout << "WARNING: using hard coded cross section for Nucleus-Air with " + "SIBYLL! (fix me!)" + << std::endl; + // TODO: target id is not used because cross section is still hard coded and fixed + // to air. + signuc_(iBeam, dElabNuc, sigProd); std::cout << "cross section: " << sigProd << std::endl; - return std::make_tuple(sigProd * 1_mbarn, 0_mbarn, iTarget); + return std::make_tuple(sigProd * 1_mbarn, 0_mbarn); } return std::make_tuple(std::numeric_limits<double>::infinity() * 1_mbarn, - std::numeric_limits<double>::infinity() * 1_mbarn, 0); + std::numeric_limits<double>::infinity() * 1_mbarn); } template <typename Particle, typename Track> @@ -121,11 +128,15 @@ namespace corsika::process::sibyll { RootCoordinateSystem::GetInstance().GetRootCoordinateSystem(); const particles::Code corsikaBeamId = p.GetPID(); - if (!corsika::particles::IsNucleus(corsikaBeamId)) { // no nuclear interaction return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm); } + // check if target-style nucleus (enum) + if (corsikaBeamId != corsika::particles::Code::Nucleus) + throw std::runtime_error( + "NuclearInteraction: GetInteractionLength: Wrong nucleus type. Nuclear " + "projectiles should use NuclearStackExtension!"); // read from cross section code table const HEPMassType nucleon_mass = 0.5 * (corsika::particles::Proton::GetMass() + @@ -136,6 +147,9 @@ namespace corsika::process::sibyll { // total momentum and energy HEPEnergyType Elab = p.GetEnergy() + nucleon_mass; + int const nuclA = p.GetNuclearA(); + auto const ElabNuc = p.GetEnergy() / nuclA; + MomentumVector pTotLab(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV}); pTotLab += p.GetMomentum(); pTotLab += pTarget; @@ -143,13 +157,19 @@ namespace corsika::process::sibyll { // calculate cm. energy const HEPEnergyType ECoM = sqrt( (Elab + pTotLabNorm) * (Elab - pTotLabNorm)); // binomial for numerical accuracy - + auto const ECoMNN = sqrt(2. * ElabNuc * nucleon_mass); std::cout << "NuclearInteraction: LambdaInt: \n" << " input energy: " << Elab / 1_GeV << endl << " input energy CoM: " << ECoM / 1_GeV << endl - << " beam pid:" << corsikaBeamId << endl; + << " beam pid:" << corsikaBeamId << endl + << " beam A: " << nuclA << endl + << " input energy per nucleon: " << ElabNuc / 1_GeV << endl + << " input energy CoM per nucleon: " << ECoMNN / 1_GeV << endl; + // throw std::runtime_error("stop here"); - if (Elab >= 8.5_GeV && ECoM >= 10_GeV) { + // energy limits + // TODO: values depend on hadronic interaction model !! this is sibyll specific + if (ElabNuc >= 8.5_GeV && ECoMNN >= 10_GeV) { // get target from environment /* @@ -164,7 +184,6 @@ namespace corsika::process::sibyll { // determine average interaction length // weighted sum int i = -1; - double avgTargetMassNumber = 0.; si::CrossSectionType weightedProdCrossSection = 0_mbarn; // get weights of components from environment/medium const auto w = mediumComposition.GetFractions(); @@ -173,24 +192,23 @@ namespace corsika::process::sibyll { i++; cout << "NuclearInteraction: get interaction length for target: " << targetId << endl; - // TODO: remove number of nucleons, avg target mass is available in environment - auto const [productionCrossSection, elaCrossSection, numberOfNucleons] = - GetCrossSection(corsikaBeamId, targetId, ECoM); + auto const [productionCrossSection, elaCrossSection] = + GetCrossSection(p, targetId); [[maybe_unused]] auto elaCrossSectionCopy = elaCrossSection; std::cout << "NuclearInteraction: " << "IntLength: nuclib return (mb): " << productionCrossSection / 1_mbarn << std::endl; weightedProdCrossSection += w[i] * productionCrossSection; - avgTargetMassNumber += w[i] * numberOfNucleons; } cout << "NuclearInteraction: " << "IntLength: weighted CrossSection (mb): " << weightedProdCrossSection / 1_mbarn << endl; // calculate interaction length in medium - GrammageType const int_length = - avgTargetMassNumber * corsika::units::constants::u / weightedProdCrossSection; + GrammageType const int_length = mediumComposition.GetAverageMassNumber() * + corsika::units::constants::u / + weightedProdCrossSection; std::cout << "NuclearInteraction: " << "interaction length (g/cm2): " << int_length / (0.001_kg) * 1_cm * 1_cm << std::endl; @@ -207,9 +225,6 @@ namespace corsika::process::sibyll { // this routine superimposes different nucleon-nucleon interactions // in a nucleus-nucleus interaction, based the SIBYLL routine SIBNUC - // this routine superimposes different nucleon-nucleon interactions - // in a nucleus-nucleus interaction, based the SIBYLL routine SIBNUC - using namespace corsika::units; using namespace corsika::utl; using namespace corsika::units::si; @@ -217,18 +232,29 @@ namespace corsika::process::sibyll { using std::cout; using std::endl; - const auto corsikaProjId = p.GetPID(); - cout << "NuclearInteraction: DoInteraction: called with:" << corsikaProjId << endl - << "NuclearInteraction: projectile mass: " - << corsika::particles::GetMass(corsikaProjId) / 1_GeV << endl; + const auto ProjId = p.GetPID(); + // TODO: calculate projectile mass in nuclearStackExtension + // const auto ProjMass = p.GetMass(); + cout << "NuclearInteraction: DoInteraction: called with:" << ProjId << endl; - if (!IsNucleus(corsikaProjId)) { + if (!IsNucleus(ProjId)) { cout << "WARNING: non nuclear projectile in NUCLIB!" << endl; // this should not happen // throw std::runtime_error("Non nuclear projectile in NUCLIB!"); return process::EProcessReturn::eOk; } + // check if target-style nucleus (enum) + if (ProjId != corsika::particles::Code::Nucleus) + throw std::runtime_error( + "NuclearInteraction: DoInteraction: Wrong nucleus type. Nuclear projectiles " + "should use NuclearStackExtension!"); + + auto const ProjMass = + p.GetNuclearZ() * corsika::particles::Proton::GetMass() + + (p.GetNuclearA() - p.GetNuclearZ()) * corsika::particles::Neutron::GetMass(); + cout << "NuclearInteraction: projectile mass: " << ProjMass / 1_GeV << endl; + fCount++; const CoordinateSystem& rootCS = @@ -242,7 +268,7 @@ namespace corsika::process::sibyll { cout << "Interaction: time: " << tOrig << endl; // projectile nucleon number - const int kAProj = GetNucleusA(corsikaProjId); + const int kAProj = p.GetNuclearA(); // GetNucleusA(ProjId); if (kAProj > 56) throw std::runtime_error("Projectile nucleus too large for NUCLIB!"); @@ -284,6 +310,13 @@ namespace corsika::process::sibyll { HEPEnergyType EcmNN = PtotNN4.GetNorm(); cout << "NuclearInteraction: nuc-nuc cm energy: " << EcmNN / 1_GeV << endl; + if (!fHadronicInteraction.ValidCoMEnergy(EcmNN)) { + cout << "NuclearInteraction: nuc-nuc. CoM energy too low for hadronic " + "interaction model!" + << endl; + throw std::runtime_error("NuclearInteraction: DoInteraction: energy too low!"); + } + // define boost to NUCLEON-NUCLEON frame COMBoost const boost(PprojNucLab, nucleon_mass); // boost projecticle @@ -308,8 +341,9 @@ namespace corsika::process::sibyll { const auto currentNode = fEnvironment.GetUniverse()->GetContainingNode(pOrig); const auto& mediumComposition = currentNode->GetModelProperties().GetNuclearComposition(); - cout << "get cross sections for target materials.." << endl; + cout << "get nucleon-nucleus cross sections for target materials.." << endl; // 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 @@ -320,8 +354,9 @@ namespace corsika::process::sibyll { for (size_t i = 0; i < compVec.size(); ++i) { auto const targetId = compVec[i]; cout << "target component: " << targetId << endl; - cout << "beam id: " << targetId << endl; - const auto [sigProd, sigEla, nNuc] = GetCrossSection(beamId, targetId, EcmNN); + cout << "beam id: " << beamId << endl; + const auto [sigProd, sigEla, nNuc] = + fHadronicInteraction.GetCrossSection(beamId, targetId, EcmNN); cross_section_of_components[i] = sigProd; [[maybe_unused]] auto sigElaCopy = sigEla; // ONLY TO AVOID COMPILER WARNINGS [[maybe_unused]] auto sigNucCopy = nNuc; // ONLY TO AVOID COMPILER WARNINGS @@ -396,97 +431,90 @@ namespace corsika::process::sibyll { << " px=" << fragments_.ppp[j][0] << " py=" << fragments_.ppp[j][1] << " pz=" << fragments_.ppp[j][2] << endl; - auto Nucleus = [](int iA) { - // int znew = iA / 2.15 + 0.7; - corsika::particles::Code pCode; - switch (iA) { - case 2: - pCode = corsika::particles::Deuterium::GetCode(); - break; - - case 3: - pCode = corsika::particles::Tritium::GetCode(); - break; - - case 4: - pCode = corsika::particles::Helium::GetCode(); - break; - - case 12: - pCode = corsika::particles::Carbon::GetCode(); - break; - - case 14: - pCode = corsika::particles::Nitrogen::GetCode(); - break; - - case 16: - pCode = corsika::particles::Oxygen::GetCode(); - break; - - case 33: - pCode = corsika::particles::Sulphur::GetCode(); - break; - - case 40: - pCode = corsika::particles::Argon::GetCode(); - break; - - default: - pCode = corsika::particles::Proton::GetCode(); - } - return pCode; - }; - + cout << "adding nuclear fragments to particle stack.." << endl; // put nuclear fragments on corsika stack for (int j = 0; j < nFragments; ++j) { - // here we need the nucleonNumber to corsika Id conversion - auto pCode = Nucleus(AFragments[j]); + corsika::particles::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 = corsika::particles::Code::Proton; + else + specCode = corsika::particles::Code::Nucleus; + + // TODO: mass of nuclei? + const HEPMassType mass = + corsika::particles::Proton::GetMass() * nuclZ + + (nuclA - nuclZ) * + corsika::particles::Neutron::GetMass(); // this neglects binding energy + + cout << "NuclearInteraction: adding fragment: " << specCode << endl; + cout << "NuclearInteraction: A,Z: " << nuclA << "," << nuclZ << endl; + cout << "NuclearInteraction: mass: " << mass / 1_GeV << endl; // CORSIKA 7 way // spectators inherit momentum from original projectile - const double mass_ratio = corsika::particles::GetMass(pCode) / - corsika::particles::GetMass(corsikaProjId); + const double mass_ratio = mass / ProjMass; + + cout << "NuclearInteraction: mass ratio " << mass_ratio << endl; + auto const Plab = PprojLab * mass_ratio; - p.AddSecondary(pCode, Plab.GetTimeLikeComponent(), Plab.GetSpaceLikeComponents(), - pOrig, tOrig); + cout << "NuclearInteraction: fragment momentum: " + << Plab.GetSpaceLikeComponents().GetComponents() / 1_GeV << endl; + + if (nuclA == 1) + // add nucleon + p.AddSecondary(specCode, Plab.GetTimeLikeComponent(), + Plab.GetSpaceLikeComponents(), pOrig, tOrig); + else + // add nucleus + p.AddSecondary(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 + cout << "adding elastically scattered nucleons to particle stack.." << endl; for (int j = 0; j < nElasticNucleons; ++j) { // TODO: sample proton or neutron - auto pCode = corsika::particles::Proton::GetCode(); + auto const elaNucCode = corsika::particles::Code::Proton; // CORSIKA 7 way // elastic nucleons inherit momentum from original projectile // neglecting momentum transfer in interaction - const double mass_ratio = corsika::particles::GetMass(pCode) / - corsika::particles::GetMass(corsikaProjId); + const double mass_ratio = corsika::particles::GetMass(elaNucCode) / ProjMass; auto const Plab = PprojLab * mass_ratio; - p.AddSecondary(pCode, Plab.GetTimeLikeComponent(), Plab.GetSpaceLikeComponents(), - pOrig, tOrig); + p.AddSecondary(elaNucCode, Plab.GetTimeLikeComponent(), + Plab.GetSpaceLikeComponents(), pOrig, tOrig); } // add inelastic interactions + cout << "calculate inelastic nucleon-nucleon interactions.." << endl; for (int j = 0; j < nInelNucleons; ++j) { - // TODO: sample neutron or proton auto pCode = corsika::particles::Proton::GetCode(); // temporarily add to stack, will be removed after interaction in DoInteraction + cout << "inelastic interaction no. " << j << endl; auto inelasticNucleon = p.AddSecondary(pCode, PprojNucLab.GetTimeLikeComponent(), PprojNucLab.GetSpaceLikeComponents(), pOrig, tOrig); // create inelastic interaction + cout << "calling HadronicInteraction..." << endl; fHadronicInteraction.DoInteraction(inelasticNucleon, s); } // delete parent particle p.Delete(); + cout << "NuclearInteraction: DoInteraction: done" << endl; + return process::EProcessReturn::eOk; } diff --git a/Processes/Sibyll/SibStack.h b/Processes/Sibyll/SibStack.h index 9b168f6eb97957b369f87a5c2066b9c601d039f5..fd847124abbcd34b42f2acd5ddb1bc3e5cef2e79 100644 --- a/Processes/Sibyll/SibStack.h +++ b/Processes/Sibyll/SibStack.h @@ -27,6 +27,7 @@ namespace corsika::process::sibyll { public: void Init(); + void Dump() const {} void Clear() { s_plist_.np = 0; } unsigned int GetSize() const { return s_plist_.np; } @@ -65,8 +66,7 @@ namespace corsika::process::sibyll { 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}; - MomentumVector v1(rootCS, components); - return v1; + return MomentumVector(rootCS, components); } void Copy(const unsigned int i1, const unsigned int i2) { diff --git a/Processes/Sibyll/testSibyll.cc b/Processes/Sibyll/testSibyll.cc index 6251aab11ed5efc5d8b620824ba2ba45847cfff5..8f53c6e12d687dcbfede62a31c3cc43917704ae4 100644 --- a/Processes/Sibyll/testSibyll.cc +++ b/Processes/Sibyll/testSibyll.cc @@ -136,13 +136,13 @@ TEST_CASE("SibyllInterface", "[processes]") { SECTION("NuclearInteractionInterface") { setup::Stack stack; - const HEPEnergyType E0 = 100_GeV; + const HEPEnergyType E0 = 400_GeV; HEPMomentumType P0 = sqrt(E0 * E0 - particles::Proton::GetMass() * particles::Proton::GetMass()); auto plab = stack::super_stupid::MomentumVector(cs, {0_GeV, 0_GeV, -P0}); geometry::Point pos(cs, 0_m, 0_m, 0_m); - auto particle = stack.AddParticle(particles::Code::Proton, E0, plab, pos, 0_ns); + auto particle = stack.AddParticle(particles::Code::Nucleus, E0, plab, pos, 0_ns, 4, 2); Interaction hmodel(env); NuclearInteraction model(env, hmodel); diff --git a/Processes/StackInspector/StackInspector.cc b/Processes/StackInspector/StackInspector.cc index 312fe40ec82dfd4f1faf12870e397ef4fbe794bc..fb749c0b8c7e6430c7283f079055979d099ee454 100644 --- a/Processes/StackInspector/StackInspector.cc +++ b/Processes/StackInspector/StackInspector.cc @@ -51,7 +51,10 @@ process::EProcessReturn StackInspector<Stack>::DoContinuous(Particle&, setup::Tr 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 << endl; + << " pos=" << pos; + // if (iterP.GetPID()==Code::Nucleus) + // cout << " nuc_ref=" << iterP.GetNucleusRef(); + cout << endl; } fCountStep++; cout << "StackInspector: nStep=" << fCountStep << " stackSize=" << s.GetSize() diff --git a/Stack/NuclearStackExtension/NuclearStackExtension.h b/Stack/NuclearStackExtension/NuclearStackExtension.h index 4b3fa4ded55584a4daaff8c7fec0e1525aca48fb..355e9821d806fb3138cb36913b988e07ede4b234 100644 --- a/Stack/NuclearStackExtension/NuclearStackExtension.h +++ b/Stack/NuclearStackExtension/NuclearStackExtension.h @@ -24,11 +24,28 @@ namespace corsika::stack { + /** + * @namespace nuclear_extension + * + * Add A and Z data to existing stack of particle properties. + * + * 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. + * + * + */ + namespace nuclear_extension { typedef corsika::geometry::Vector<corsika::units::si::hepmomentum_d> MomentumVector; /** + * @class NuclearParticleInterface + * * Define ParticleInterface for NuclearStackExtension Stack derived from * ParticleInterface of Inner stack class */ @@ -58,12 +75,12 @@ namespace corsika::stack { err << "NuclearStackExtension: no A and Z specified for new Nucleus!"; throw std::runtime_error(err.str()); } - SetNuclearRef( + SetNucleusRef( GetStackData().GetNucleusNextRef()); // store this nucleus data ref SetNuclearA(vA); SetNuclearZ(vZ); } else { - SetNuclearRef(-1); // this is not a nucleus + SetNucleusRef(-1); // this is not a nucleus } InnerParticleInterface<StackIteratorInterface>::SetParticleData( vDataPID, vDataE, vMomentum, vPosition, vTime); @@ -99,31 +116,40 @@ namespace corsika::stack { int GetNuclearZ() const { return GetStackData().GetNuclearZ(GetIndex()); } /// @} + // int GetNucleusRef() const { return GetStackData().GetNucleusRef(GetIndex()); } + protected: - void SetNuclearRef(const int vR) { GetStackData().SetNuclearRef(GetIndex(), vR); } + void SetNucleusRef(const int vR) { GetStackData().SetNucleusRef(GetIndex(), vR); } }; /** - * @class NuclearStackExtensionImpl + * @class NuclearStackExtension * - * Memory implementation of the extension of particle stack of - * type InnerStackImpl with nuclear data + * 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(); - fNuclearRef.clear(); + fNucleusRef.clear(); fNuclearA.clear(); fNuclearZ.clear(); } - unsigned int GetSize() const { return fNuclearRef.size(); } - unsigned int GetCapacity() const { return fNuclearRef.size(); } + 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; @@ -131,7 +157,7 @@ namespace corsika::stack { void SetNuclearZ(const unsigned int i, const unsigned short vZ) { fNuclearZ[GetNucleusRef(i)] = vZ; } - void SetNuclearRef(const unsigned int i, const int v) { fNuclearRef[i] = v; } + 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)]; } @@ -144,7 +170,7 @@ namespace corsika::stack { } int GetNucleusRef(const unsigned int i) const { - if (fNuclearRef[i] >= 0) return fNuclearRef[i]; + if (fNucleusRef[i] >= 0) return fNucleusRef[i]; std::ostringstream err; err << "NuclearStackExtension: no nucleus at ref=" << i; throw std::runtime_error(err.str()); @@ -154,35 +180,41 @@ namespace corsika::stack { * 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); - const int ref1 = fNuclearRef[i1]; - const int ref2 = fNuclearRef[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 - fNuclearRef[i2] = GetNucleusNextRef(); - fNuclearA[fNuclearRef[i2]] = fNuclearA[ref1]; - fNuclearZ[fNuclearRef[i2]] = fNuclearZ[ref1]; + 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 - fNuclearA.erase(fNuclearA.cbegin() + ref2); - fNuclearZ.erase(fNuclearZ.cbegin() + ref2); - const int n = fNuclearRef.size(); + 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 (fNuclearRef[i] >= ref2) { fNuclearRef[i] -= 1; } + if (fNucleusRef[i] > ref2) { fNucleusRef[i] -= 1; } } } } @@ -192,48 +224,34 @@ namespace corsika::stack { * 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); - const int ref1 = fNuclearRef[i1]; - const int ref2 = fNuclearRef[i2]; - if (ref2 < 0) { - if (ref1 >= 0) { - // i1 is nucleus, i2 is not - std::swap(fNuclearRef[i2], fNuclearRef[i1]); - } else { - // neither i1 nor i2 are nuclei - } - } else { - if (ref1 >= 0) { - // both are nuclei, i2 is overwritten with nucleus i1 - std::swap(fNuclearRef[i2], fNuclearRef[i1]); - } else { - // i2 is overwritten with non-nucleus i1 - std::swap(fNuclearRef[i2], fNuclearRef[i1]); - } - } + // swap corresponding nuclear reference data + std::swap(fNucleusRef[i2], fNucleusRef[i1]); } void IncrementSize() { InnerStackImpl::IncrementSize(); - fNuclearRef.push_back(-1); + fNucleusRef.push_back(-1); } void DecrementSize() { InnerStackImpl::DecrementSize(); - if (fNuclearRef.size() > 0) { - const int ref = fNuclearRef.back(); - fNuclearRef.pop_back(); + 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 = fNuclearRef.size(); + const int n = fNucleusRef.size(); for (int i = 0; i < n; ++i) { - if (fNuclearRef[i] >= ref) { fNuclearRef[i] -= 1; } + if (fNucleusRef[i] >= ref) { fNucleusRef[i] -= 1; } } } } @@ -242,7 +260,7 @@ namespace corsika::stack { private: /// the actual memory to store particle data - std::vector<int> fNuclearRef; + std::vector<int> fNucleusRef; std::vector<unsigned short> fNuclearA; std::vector<unsigned short> fNuclearZ; diff --git a/Stack/SuperStupidStack/SuperStupidStack.h b/Stack/SuperStupidStack/SuperStupidStack.h index 61ef97a96d3a3305156894f13cfd69b1f59b5a0e..cb63b03360b9df5c4c5593468258de900218b1bb 100644 --- a/Stack/SuperStupidStack/SuperStupidStack.h +++ b/Stack/SuperStupidStack/SuperStupidStack.h @@ -114,6 +114,7 @@ namespace corsika::stack { public: void Init() {} + void Dump() const {} void Clear() { fDataPID.clear();