diff --git a/Environment/NuclearComposition.h b/Environment/NuclearComposition.h index 5f89bd2cadb50e48f01931abafbc4ee94305cf8b..dee18a4463c340698bbff372cc504d4744748375 100644 --- a/Environment/NuclearComposition.h +++ b/Environment/NuclearComposition.h @@ -35,15 +35,15 @@ namespace corsika::environment { public: using value_type = double; using iterator_category = std::input_iterator_tag; - using pointer = double*; - using reference = double&; + using pointer = value_type*; + using reference = value_type&; using difference_type = ptrdiff_t; WeightProviderIterator(AConstIterator a, BConstIterator b) : fAIter(a) , fBIter(b) {} - double operator*() const { return ((*fAIter) * (*fBIter)).magnitude(); } + value_type operator*() const { return ((*fAIter) * (*fBIter)).magnitude(); } WeightProviderIterator& operator++() { // prefix ++ ++fAIter; diff --git a/Environment/VolumeTreeNode.h b/Environment/VolumeTreeNode.h index 3331c41bf5e060bb904f5a78f78485af0440d04b..9cd3be1a9e534c5f6b74bb4e584dac1c78e0d9bf 100644 --- a/Environment/VolumeTreeNode.h +++ b/Environment/VolumeTreeNode.h @@ -66,6 +66,24 @@ namespace corsika::environment { } } + /** + * 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); }); + } else { + std::for_each(fChildNodes.begin(), fChildNodes.end(), + [&](auto& v) { v->walk(func); }); + func(*this); + } + } + void AddChild(VTNUPtr pChild) { pChild->fParentNode = this; fChildNodes.push_back(std::move(pChild)); @@ -88,6 +106,8 @@ namespace corsika::environment { auto const& GetModelProperties() const { return *fModelProperties; } + IMPSharedPtr GetModelPropertiesPtr() const { return fModelProperties; } + template <typename TModelProperties, typename... Args> auto SetModelProperties(Args&&... args) { static_assert(std::is_base_of_v<IModelProperties, TModelProperties>, diff --git a/Processes/Sibyll/Interaction.cc b/Processes/Sibyll/Interaction.cc index 5ccc1b743ce35f1e72a1ebd1e5cccba95a8d25e9..0e65c074bba270dcd4db98866e079893d80bf622 100644 --- a/Processes/Sibyll/Interaction.cc +++ b/Processes/Sibyll/Interaction.cc @@ -53,7 +53,7 @@ namespace corsika::process::sibyll { } } - tuple<units::si::CrossSectionType, units::si::CrossSectionType, int> + tuple<units::si::CrossSectionType, units::si::CrossSectionType> Interaction::GetCrossSection(const particles::Code BeamId, const particles::Code TargetId, const units::si::HEPEnergyType CoMenergy) { @@ -61,23 +61,24 @@ namespace corsika::process::sibyll { double sigProd, sigEla, dummy, dum1, dum3, dum4; double dumdif[3]; const int iBeam = process::sibyll::GetSibyllXSCode(BeamId); + if( !IsValidCoMEnergy(CoMenergy) ){ + throw std::runtime_error("Interaction: GetCrossSection: CoM energy outside range for Sibyll!"); + } const double dEcm = CoMenergy / 1_GeV; - int iTarget = -1; if (particles::IsNucleus(TargetId)) { - iTarget = particles::GetNucleusA(TargetId); - if (iTarget > 18 || iTarget == 0) + const int iTarget = particles::GetNucleusA(TargetId); + if (iTarget > fMaxTargetMassNumber || 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()) { sib_sigma_hp_(iBeam, dEcm, dum1, sigEla, sigProd, dumdif, dum3, dum4); - iTarget = 1; } else { // no interaction in sibyll possible, return infinite cross section? or throw? sigProd = std::numeric_limits<double>::infinity(); sigEla = std::numeric_limits<double>::infinity(); } - return std::make_tuple(sigProd * 1_mbarn, sigEla * 1_mbarn, iTarget); + return std::make_tuple(sigProd * 1_mbarn, sigEla * 1_mbarn); } template <> @@ -97,14 +98,11 @@ namespace corsika::process::sibyll { // read from cross section code table const bool kInteraction = process::sibyll::CanInteract(corsikaBeamId); - const HEPMassType nucleon_mass = - 0.5 * (particles::Proton::GetMass() + particles::Neutron::GetMass()); - // FOR NOW: assume target is at rest MomentumVector pTarget(rootCS, {0_GeV, 0_GeV, 0_GeV}); // total momentum and energy - HEPEnergyType Elab = p.GetEnergy() + nucleon_mass; + HEPEnergyType Elab = p.GetEnergy() + constants::nucleonMass; MomentumVector pTotLab(rootCS, {0_GeV, 0_GeV, 0_GeV}); pTotLab += p.GetMomentum(); pTotLab += pTarget; @@ -119,7 +117,8 @@ namespace corsika::process::sibyll { << " beam pid:" << p.GetPID() << endl; // TODO: move limits into variables - if (kInteraction && Elab >= 8.5_GeV && ECoM >= 10_GeV) { + // FR: removed && Elab >= 8.5_GeV + if (kInteraction && IsValidCoMEnergy(ECoM)) { // get target from environment /* @@ -134,7 +133,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(); @@ -143,7 +141,7 @@ namespace corsika::process::sibyll { i++; cout << "Interaction: get interaction length for target: " << targetId << endl; - auto const [productionCrossSection, elaCrossSection, numberOfNucleons] = + auto const [productionCrossSection, elaCrossSection ] = GetCrossSection(corsikaBeamId, targetId, ECoM); [[maybe_unused]] auto elaCrossSectionCopy = elaCrossSection; // ONLY TO AVOID COMPILER WARNING @@ -152,7 +150,6 @@ namespace corsika::process::sibyll { << " IntLength: sibyll return (mb): " << productionCrossSection / 1_mbarn << endl; weightedProdCrossSection += w[i] * productionCrossSection; - avgTargetMassNumber += w[i] * numberOfNucleons; } cout << "Interaction: " << "IntLength: weighted CrossSection (mb): " @@ -161,7 +158,7 @@ namespace corsika::process::sibyll { // calculate interaction length in medium //#warning check interaction length units GrammageType const int_length = - avgTargetMassNumber * units::constants::u / weightedProdCrossSection; + mediumComposition.GetAverageMassNumber() * units::constants::u / weightedProdCrossSection; cout << "Interaction: " << "interaction length (g/cm2): " << int_length / (0.001_kg) * 1_cm * 1_cm << endl; @@ -205,10 +202,8 @@ namespace corsika::process::sibyll { // define target // for Sibyll is always a single nucleon - auto constexpr nucleon_mass = - 0.5 * (particles::Proton::GetMass() + particles::Neutron::GetMass()); // FOR NOW: target is always at rest - const auto eTargetLab = 0_GeV + nucleon_mass; + const auto eTargetLab = 0_GeV + constants::nucleonMass; const auto pTargetLab = MomentumVector(rootCS, 0_GeV, 0_GeV, 0_GeV); const FourVector PtargLab(eTargetLab, pTargetLab); @@ -227,7 +222,7 @@ namespace corsika::process::sibyll { // define target kinematics in lab frame // define boost to and from CoM frame // CoM frame definition in Sibyll projectile: +z - COMBoost const boost(PprojLab, nucleon_mass); + COMBoost const boost(PprojLab, constants::nucleonMass); // just for show: // boost projecticle @@ -268,11 +263,9 @@ namespace corsika::process::sibyll { for (size_t i = 0; i < compVec.size(); ++i) { auto const targetId = compVec[i]; - const auto [sigProd, sigEla, nNuc] = + const auto [sigProd, sigEla ] = GetCrossSection(corsikaBeamId, targetId, Ecm); cross_section_of_components[i] = sigProd; - [[maybe_unused]] int ideleteme = - nNuc; // to avoid not used warning in array binding [[maybe_unused]] auto sigElaCopy = sigEla; // to avoid not used warning in array binding } @@ -289,7 +282,7 @@ namespace corsika::process::sibyll { if (IsNucleus(targetCode)) targetSibCode = GetNucleusA(targetCode); if (targetCode == particles::Proton::GetCode()) targetSibCode = 1; cout << "Interaction: sibyll code: " << targetSibCode << endl; - if (targetSibCode > 18 || targetSibCode < 1) + if (targetSibCode > fMaxTargetMassNumber || targetSibCode < 1) throw std::runtime_error( "Sibyll target outside range. Only nuclei with A<18 or protons are " "allowed."); @@ -300,7 +293,10 @@ namespace corsika::process::sibyll { cout << "Interaction: " << " DoInteraction: E(GeV):" << eProjectileLab / 1_GeV << " Ecm(GeV): " << Ecm / 1_GeV << endl; - if (eProjectileLab < 8.5_GeV || Ecm < 10_GeV) { + if( Ecm > GetMaxEnergyCoM() ) + throw std::runtime_error("Interaction::DoInteraction: CoM energy too high!"); + // FR: removed eProjectileLab < 8.5_GeV || + if ( Ecm < GetMinEnergyCoM() ) { cout << "Interaction: " << " DoInteraction: should have dropped particle.. " << "THIS IS AN ERROR" << endl; diff --git a/Processes/Sibyll/Interaction.h b/Processes/Sibyll/Interaction.h index 40463f6657c2b6e877be5a018b7cfcf67ae6f884..509f50a61861d30e0797e86d5190abcf469cb5b8 100644 --- a/Processes/Sibyll/Interaction.h +++ b/Processes/Sibyll/Interaction.h @@ -37,13 +37,20 @@ namespace corsika::process::sibyll { void Init(); bool WasInitialized() { return fInitialized; } - bool ValidCoMEnergy(corsika::units::si::HEPEnergyType ecm) { - using namespace corsika::units::si; - return (10_GeV < ecm) && (ecm < 1_PeV); + bool IsValidCoMEnergy(corsika::units::si::HEPEnergyType ecm) { + return (fMinEnergyCoM <= ecm) && (ecm <= fMaxEnergyCoM); } - - std::tuple<corsika::units::si::CrossSectionType, corsika::units::si::CrossSectionType, - int> + int GetMaxTargetMassNumber() { return fMaxTargetMassNumber; } + corsika::units::si::HEPEnergyType GetMinEnergyCoM() { return fMinEnergyCoM; } + corsika::units::si::HEPEnergyType GetMaxEnergyCoM() { return fMaxEnergyCoM; } + bool IsValidTarget(corsika::particles::Code TargetId) + { + return ( corsika::particles::GetNucleusA(TargetId) < fMaxTargetMassNumber ) + && corsika::particles::IsNucleus( TargetId ); + } + + + 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); @@ -63,6 +70,11 @@ namespace corsika::process::sibyll { corsika::environment::Environment const& fEnvironment; corsika::random::RNG& fRNG = corsika::random::RNGManager::GetInstance().GetRandomStream("s_rndm"); + const corsika::units::si::HEPEnergyType fMinEnergyCoM = + 10. * 1e9 * corsika::units::si::electronvolt; + const corsika::units::si::HEPEnergyType fMaxEnergyCoM = + 1.e6 * 1e9 * corsika::units::si::electronvolt; + const int fMaxTargetMassNumber = 18; }; } // namespace corsika::process::sibyll diff --git a/Processes/Sibyll/NuclearInteraction.cc b/Processes/Sibyll/NuclearInteraction.cc index 6a37363235f66c455206e16c0134a9b623cf4ff4..0e65121d478942af665824f1348777a3764e21da 100644 --- a/Processes/Sibyll/NuclearInteraction.cc +++ b/Processes/Sibyll/NuclearInteraction.cc @@ -22,6 +22,8 @@ #include <corsika/setup/SetupStack.h> #include <corsika/setup/SetupTrajectory.h> +#include <set> + using std::cout; using std::endl; using std::tuple; @@ -41,6 +43,7 @@ namespace corsika::process::sibyll { NuclearInteraction::~NuclearInteraction() { cout << "Nuclib::NuclearInteraction n=" << fCount << " Nnuc=" << fNucCount << endl; + fTargetComponentsIndex.clear(); } void NuclearInteraction::Init() { @@ -51,25 +54,136 @@ namespace corsika::process::sibyll { // TODO: safe to run multiple initializations? if (!fHadronicInteraction.WasInitialized()) fHadronicInteraction.Init(); + // check compatibility of energy ranges, someone could try to use low-energy model.. + if(!fHadronicInteraction.IsValidCoMEnergy(GetMinEnergyPerNucleonCoM())|| + !fHadronicInteraction.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(); + } + + void NuclearInteraction::InitializeNuclearCrossSections() + { + using namespace corsika::particles; + using namespace units::si; + + auto& universe = *(fEnvironment.GetUniverse()); + + auto const allElementsInUniverse = std::invoke([&]() { + std::set<particles::Code> allElementsInUniverse; + auto collectElements = [&](auto& vtn) { + if (auto const mp = vtn.GetModelPropertiesPtr(); + mp != nullptr) { // do not query Universe it self, it has no ModelProperties + auto const& comp = mp->GetNuclearComposition().GetComponents(); + for (auto const c : comp) + allElementsInUniverse.insert(c); + // std::for_each(comp.cbegin(), comp.cend(), + // [&](particles::Code c) { allElementsInUniverse.insert(c); }); + } + }; + universe.walk(collectElements); + return allElementsInUniverse; + }); + + + cout << "NuclearInteraction: initializing nuclear cross sections..." << endl; + + // loop over target components, at most 4!! + int k =-1; + for(auto &ptarg: allElementsInUniverse){ + ++k; + cout << "NuclearInteraction: init target component: " << ptarg << endl; + const int ib = GetNucleusA( ptarg ); + if(!fHadronicInteraction.IsValidTarget( ptarg )){ + cout << "NuclearInteraction::InitializeNuclearCrossSections: target nucleus? id=" + << ptarg << endl; + throw std::runtime_error(" target can not be handled by hadronic interaction model! "); + } + fTargetComponentsIndex.insert( std::pair<Code,int>(ptarg, k) ); + // loop over energies, fNEnBins log. energy bins + for(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 ] = + fHadronicInteraction.GetCrossSection(protonId, protonId, Ecm); + const double dsig = siginel / 1_mbarn; + const double dsigela = sigela / 1_mbarn; + // loop over projectiles, mass numbers from 2 to fMaxNucleusAProjectile + for(int j=1; j<fMaxNucleusAProjectile; ++j){ + const int jj = j+1; + double sig_out, dsig_out, sigqe_out, dsigqe_out; + sigma_mc_(jj,ib,dsig,dsigela,fNSample,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; + } + } + } + cout << "NuclearInteraction: cross sections for " << fTargetComponentsIndex.size() << " components initialized!" << endl; + for(auto &ptarg: allElementsInUniverse){ + cout << "cross section table: " << ptarg << endl; + PrintCrossSectionTable( ptarg ); + } + } - // TODO: remove number of nucleons, avg target mass is available in environment + void NuclearInteraction::PrintCrossSectionTable( corsika::particles::Code pCode) + { + using namespace corsika::particles; + const int k = fTargetComponentsIndex.at( pCode ); + Code pNuclei [] = {Code::Helium, Code::Lithium7, Code::Oxygen, + Code::Neon, Code::Argon, Code::Iron}; + cout << "en/A "; + for(auto &j: pNuclei) + cout << std::setw(9) << j; + cout << endl; + + // loop over energy bins + for(int i=0; i<GetNEnergyBins(); ++i){ + cout << " " << i << " "; + for(auto &n: pNuclei){ + auto const j= GetNucleusA( n ); + cout << " " << std::setprecision(5) << std::setw(8) << cnucsignuc_.sigma[j-1][k][i]; + } + cout << endl; + } + } + + units::si::CrossSectionType NuclearInteraction::ReadCrossSectionTable(const int ia, particles::Code pTarget, units::si::HEPEnergyType elabnuc) + { + using namespace corsika::particles; + using namespace units::si; + const int ib = fTargetComponentsIndex.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; + cout << "ReadCrossSectionTable: " << ia << " " << ib << " " << e0 << endl; + signuc2_(ia,ib,e0,sig); + cout << "ReadCrossSectionTable: sig=" << sig << endl; + return sig * 1_mbarn; + } + + // TODO: remove elastic cross section? template <> tuple<units::si::CrossSectionType, units::si::CrossSectionType> NuclearInteraction::GetCrossSection(Particle& p, const particles::Code TargetId) { using namespace units::si; - double sigProd; - auto const pCode = p.GetPID(); - if (pCode != particles::Code::Nucleus) + if ( p.GetPID() != particles::Code::Nucleus) throw std::runtime_error( "NuclearInteraction: GetCrossSection: particle not a nucleus!"); - auto const iBeam = p.GetNuclearA(); - HEPEnergyType LabEnergyPerNuc = p.GetEnergy() / iBeam; - cout << "NuclearInteraction: GetCrossSection: called with: beamNuclA= " << iBeam + auto const iBeamA = p.GetNuclearA(); + HEPEnergyType LabEnergyPerNuc = p.GetEnergy() / iBeamA; + cout << "NuclearInteraction: GetCrossSection: called with: beamNuclA= " << iBeamA << " TargetId= " << TargetId << " LabEnergyPerNuc= " << LabEnergyPerNuc / 1_GeV << endl; @@ -77,38 +191,20 @@ namespace corsika::process::sibyll { // TODO: for now assumes air with hard coded composition // extend to arbitrary mixtures, requires smarter initialization // get nuclib projectile code: nucleon number - if (iBeam > 56 || iBeam < 2) { + if (iBeamA > GetMaxNucleusAProjectile() || iBeamA < 2) { cout << "NuclearInteraction: beam nucleus outside allowed range for NUCLIB!" << endl - << "A=" << iBeam << endl; + << "A=" << iBeamA << endl; throw std::runtime_error( "NuclearInteraction: GetCrossSection: beam nucleus outside allowed range for " "NUCLIB!"); } - 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. - // available target nuclei depends on the hadronic interaction model and the - // initialization - if (particles::IsNucleus(TargetId)) { - const int iTarget = particles::GetNucleusA(TargetId); - if (iTarget > 18 || iTarget == 0) - throw std::runtime_error( - "Sibyll target outside range. Only nuclei with A<18 are allowed."); - cout << "NuclearInteraction: calling signuc.." << endl; - cout << "WARNING: using hard coded cross section for Nucleus-Air with " - "SIBYLL! (fix me!)" - << endl; - // TODO: target id is not used because cross section is still hard coded and fixed - // to air. - signuc_(iBeam, dElabNuc, sigProd); - cout << "cross section: " << sigProd << endl; - return std::make_tuple(sigProd * 1_mbarn, 0_mbarn); + if (fHadronicInteraction.IsValidTarget(TargetId)) { + auto const sigProd = ReadCrossSectionTable( iBeamA, TargetId, LabEnergyPerNuc ); + cout << "cross section (mb): " << sigProd / 1_mbarn << endl; + return std::make_tuple(sigProd, 0_mbarn); + } else { + throw std::runtime_error( "target outside range."); } return std::make_tuple(std::numeric_limits<double>::infinity() * 1_mbarn, std::numeric_limits<double>::infinity() * 1_mbarn); @@ -137,14 +233,12 @@ namespace corsika::process::sibyll { "projectiles should use NuclearStackExtension!"); // read from cross section code table - const HEPMassType nucleon_mass = - 0.5 * (particles::Proton::GetMass() + particles::Neutron::GetMass()); // 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 = p.GetEnergy() + nucleon_mass; + HEPEnergyType Elab = p.GetEnergy() + constants::nucleonMass; int const nuclA = p.GetNuclearA(); auto const ElabNuc = p.GetEnergy() / nuclA; @@ -155,7 +249,7 @@ 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); + auto const ECoMNN = sqrt(2. * ElabNuc * constants::nucleonMass); cout << "NuclearInteraction: LambdaInt: \n" << " input energy: " << Elab / 1_GeV << endl << " input energy CoM: " << ECoM / 1_GeV << endl @@ -167,7 +261,7 @@ namespace corsika::process::sibyll { // energy limits // TODO: values depend on hadronic interaction model !! this is sibyll specific - if (ElabNuc >= 8.5_GeV && ECoMNN >= 10_GeV) { + if (ElabNuc >= 8.5_GeV && ECoMNN >= fMinEnergyPerNucleonCoM && ECoMNN < fMaxEnergyPerNucleonCoM) { // get target from environment /* @@ -264,7 +358,8 @@ namespace corsika::process::sibyll { // projectile nucleon number const int kAProj = p.GetNuclearA(); // GetNucleusA(ProjId); - if (kAProj > 56) throw std::runtime_error("Projectile nucleus too large for NUCLIB!"); + if (kAProj > GetMaxNucleusAProjectile() ) + throw std::runtime_error("Projectile nucleus too large for NUCLIB!"); // kinematics // define projectile nucleus @@ -287,10 +382,8 @@ namespace corsika::process::sibyll { // define target // always a nucleon - auto constexpr nucleon_mass = - 0.5 * (particles::Proton::GetMass() + particles::Neutron::GetMass()); // target is always at rest - const auto eTargetNucLab = 0_GeV + nucleon_mass; + 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); @@ -304,7 +397,7 @@ namespace corsika::process::sibyll { HEPEnergyType EcmNN = PtotNN4.GetNorm(); cout << "NuclearInteraction: nuc-nuc cm energy: " << EcmNN / 1_GeV << endl; - if (!fHadronicInteraction.ValidCoMEnergy(EcmNN)) { + if (!fHadronicInteraction.IsValidCoMEnergy(EcmNN)) { cout << "NuclearInteraction: nuc-nuc. CoM energy too low for hadronic " "interaction model!" << endl; @@ -312,7 +405,7 @@ namespace corsika::process::sibyll { } // define boost to NUCLEON-NUCLEON frame - COMBoost const boost(PprojNucLab, nucleon_mass); + COMBoost const boost(PprojNucLab, constants::nucleonMass); // boost projecticle auto const PprojNucCoM = boost.toCoM(PprojNucLab); @@ -349,11 +442,10 @@ namespace corsika::process::sibyll { auto const targetId = compVec[i]; cout << "target component: " << targetId << endl; cout << "beam id: " << beamId << endl; - const auto [sigProd, sigEla, nNuc] = + const auto [sigProd, sigEla] = 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 } const auto targetCode = mediumComposition.SampleTarget(cross_section_of_components, fRNG); @@ -367,10 +459,8 @@ namespace corsika::process::sibyll { if (IsNucleus(targetCode)) kATarget = GetNucleusA(targetCode); if (targetCode == particles::Proton::GetCode()) kATarget = 1; cout << "NuclearInteraction: nuclib target code: " << kATarget << endl; - if (kATarget > 18 || kATarget < 1) - throw std::runtime_error( - "Sibyll target outside range. Only nuclei with A<18 or protons are " - "allowed."); + if(!fHadronicInteraction.IsValidTarget(targetCode)) + throw std::runtime_error("target outside range. "); // end of target sampling // superposition @@ -378,9 +468,8 @@ namespace corsika::process::sibyll { // get nucleon-nucleon cross section // (needed to determine number of nucleon-nucleon scatterings) const auto protonId = particles::Proton::GetCode(); - const auto [prodCrossSection, elaCrossSection, dum] = + const auto [prodCrossSection, elaCrossSection] = fHadronicInteraction.GetCrossSection(protonId, protonId, EcmNN); - [[maybe_unused]] auto dumCopy = dum; // ONLY TO AVOID COMPILER WARNING const double sigProd = prodCrossSection / 1_mbarn; const double sigEla = elaCrossSection / 1_mbarn; // sample number of interactions (only input variables, output in common cnucms) @@ -414,7 +503,7 @@ namespace corsika::process::sibyll { fragm_(kATarget, kAProj, nIntProj, impactPar, nFragments, AFragments); // this should not occur but well :) - if (nFragments > 60) + if (nFragments > GetMaxNFragments()) throw std::runtime_error("Number of nuclear fragments in NUCLIB exceeded!"); cout << "number of fragments: " << nFragments << endl; diff --git a/Processes/Sibyll/NuclearInteraction.h b/Processes/Sibyll/NuclearInteraction.h index cf1f6593330fddc72e867dd7d40387747d6ee9be..ad1d0bc7e06e064d859f0817d0128d6b1d73170e 100644 --- a/Processes/Sibyll/NuclearInteraction.h +++ b/Processes/Sibyll/NuclearInteraction.h @@ -40,7 +40,14 @@ namespace corsika::process::sibyll { corsika::process::sibyll::Interaction& hadint); ~NuclearInteraction(); void Init(); - + 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 fMinEnergyPerNucleonCoM; } + corsika::units::si::HEPEnergyType GetMaxEnergyPerNucleonCoM() { return fMaxEnergyPerNucleonCoM; } + int GetMaxNucleusAProjectile() {return fMaxNucleusAProjectile; } + int GetMaxNFragments() { return fMaxNFragments; } + int GetNEnergyBins() { return fNEnBins; } template <typename Particle> std::tuple<corsika::units::si::CrossSectionType, corsika::units::si::CrossSectionType> GetCrossSection(Particle& p, const corsika::particles::Code TargetId); @@ -54,8 +61,19 @@ namespace corsika::process::sibyll { private: corsika::environment::Environment const& fEnvironment; corsika::process::sibyll::Interaction& fHadronicInteraction; + std::map<corsika::particles::Code,int> fTargetComponentsIndex; corsika::random::RNG& fRNG = corsika::random::RNGManager::GetInstance().GetRandomStream("s_rndm"); + const int fNSample = 500; // number of samples in MC estimation of cross section + const int fMaxNucleusAProjectile = 56; + const int fNEnBins = 6; + const int fMaxNFragments = 60; + // energy limits defined by table used for cross section in signuc.f + // 10**1 GeV to 10**6 GeV + const corsika::units::si::HEPEnergyType fMinEnergyPerNucleonCoM = + 10. * 1e9 * corsika::units::si::electronvolt; + const corsika::units::si::HEPEnergyType fMaxEnergyPerNucleonCoM = + 1.e6 * 1e9 * corsika::units::si::electronvolt; }; } // namespace corsika::process::sibyll diff --git a/Processes/Sibyll/nuclib.h b/Processes/Sibyll/nuclib.h index e4b89b52bed8bab3f1898183b2398c5ef3c2d3b3..ddd1856cd2eea972e257ba2958f6705ae15bcf96 100644 --- a/Processes/Sibyll/nuclib.h +++ b/Processes/Sibyll/nuclib.h @@ -34,6 +34,13 @@ extern struct { */ extern struct { double ppp[60][3]; } fragments_; + // COMMON /cnucsignuc/SIGMA(6,4,56), SIGQE(6,4,56) + extern struct + { + double sigma[56][4][6]; + double sigqe[56][4][6]; + } cnucsignuc_; + // NUCLIB // subroutine to initiate nuclib @@ -46,5 +53,9 @@ void int_nuc_(const int&, const int&, const double&, const double&); void fragm_(const int&, const int&, const int&, const double&, int&, int*); void signuc_(const int&, const double&, double&); + +void signuc2_(const int&, const int&, const double&, double&); + +void sigma_mc_(const int&, const int&, const double&, const double&, const int&, double&, double&, double&, double&); } #endif diff --git a/Processes/Sibyll/signuc.f b/Processes/Sibyll/signuc.f index b21bce8cbb4893c90b1bfca4f5c1a0ca617fcb4f..b88f2ae0bd37f76bcb693692ac0fea0911b053bf 100644 --- a/Processes/Sibyll/signuc.f +++ b/Processes/Sibyll/signuc.f @@ -274,3 +274,46 @@ C ADD INELASTIC AND QUASI-ELASTIC CROSS-SECTIONS RETURN END + + + SUBROUTINE SIGNUC2(IA,IB,E0,SSIGNUC) + +C----------------------------------------------------------------------- +C SIG(MA) NUC(LEUS) 2 +C INPUT: IA : projectile mass number +C IB : position in cross section table for target nucleus +C specified when tables are filled +C E0 : Energy per nucleon in GeV in the lab. +C OUTPUT: inelastic cross section +C----------------------------------------------------------------------- + IMPLICIT NONE + DOUBLE PRECISION SIGMA,SIGQE + COMMON /cnucsignuc/SIGMA(6,4,56), SIGQE(6,4,56) + DIMENSION AA(6) + DOUBLE PRECISION AA,DA,AMIN,ABEAM,S1,S2,ASQS + DOUBLE PRECISION E0,SSIGNUC + INTEGER IA,IB,J,JE,NE + DOUBLE PRECISION QUAD_INT + EXTERNAL QUAD_INT + SAVE + DATA NE /6/, AMIN /1.D0/, DA /1.D0/ + DATA AA /1.D0,2.D0,3.D0,4.D0,5.D0,6.D0/ + +C ENERGY E0 IN GEV + ASQS = 0.5D0*LOG10(1.876D0*E0) + JE = MIN( INT( (ASQS-AMIN)/DA )+1, NE-2 ) + ABEAM = DBLE(IA) +C INELASTIC CROSS-SECTION + S1 = QUAD_INT( ASQS, AA(JE),AA(JE+1),AA(JE+2), + + SIGMA(JE,IB,IA),SIGMA(JE+1,IB,IA), + + SIGMA(JE+2,IB,IA) ) +C QUASI ELASTIC CROSS-SECTION + S2 = QUAD_INT( ASQS, AA(JE),AA(JE+1),AA(JE+2), + + SIGQE(JE,IB,IA),SIGQE(JE+1,IB,IA), + + SIGQE(JE+2,IB,IA) ) +C ADD INELASTIC AND QUASI-ELASTIC CROSS-SECTIONS + SSIGNUC = S1 + S2 + + RETURN + END +