diff --git a/corsika/detail/modules/pythia8/Interaction.inl b/corsika/detail/modules/pythia8/Interaction.inl index 38ca6094df214d15d5c1da0853358314cdd10e5a..65f909b01fe06208a1344510426c5b018f0544f6 100644 --- a/corsika/detail/modules/pythia8/Interaction.inl +++ b/corsika/detail/modules/pythia8/Interaction.inl @@ -10,6 +10,7 @@ #include <tuple> +#include <boost/filesystem/path.hpp> #include <fmt/core.h> #include <corsika/modules/pythia8/Interaction.hpp> @@ -25,100 +26,109 @@ namespace corsika::pythia8 { CORSIKA_LOG_INFO("Pythia::Interaction n= {}", count_); } - inline Interaction::Interaction(bool const print_listing) - : Pythia8::Pythia(CORSIKA_Pythia8_XML_DIR) - , print_listing_(print_listing) { - - CORSIKA_LOG_INFO("Configuring Pythia8 from: {}", CORSIKA_Pythia8_XML_DIR); - - // initialize Pythia - - // reduce output from pythia if set to "on" - Pythia8::Pythia::readString("Print:quiet = on"); - // check if data in particle data file is minimally consistent. Very verbose! set to - // "off"! we do not change the basic file provided by pythia. - Pythia8::Pythia::readString("Check:particleData = off"); - Pythia8::Pythia::readString("Check:event = on"); // default: on - Pythia8::Pythia::readString("Check:levelParticleData = 12"); // 1 is default - readString("SoftQCD:all = on"); - readString("LowEnergyQCD:all = on"); - readString("MultipartonInteraction:reuseInit = 3"); - readString(fmt::format("MultipartonInteraction:initFile = {}", corsika_data("pythia8/MPI_init_file"))); - readString("Beam:allowVariableEnergy = on"); - readString("Beam:allowIDASwitch = on"); - readString("Beam:frameType = 3"); // no special frame, need to specify full 3-mom - readString("Check:epTolErr = 0.1"); // relax energy-monetum conservation, copied from Pythia 8 main183.cc - - // we can't test this block, LCOV_EXCL_START - if (!Pythia8::Pythia::init()) { - throw std::runtime_error("Pythia::Interaction: Initialization failed!"); - } - // LCOV_EXCL_STOP - - // any decays in pythia? if yes need to define which particles - if (internalDecays_) { - // define which particles are passed to corsika, i.e. which particles make it into - // history even very shortlived particles like charm or pi0 are of interest here - std::vector<Code> const HadronsWeWantTrackedByCorsika = { - Code::PiPlus, Code::PiMinus, Code::Pi0, Code::KMinus, Code::KPlus, - Code::K0Long, Code::K0Short, Code::SigmaPlus, Code::SigmaMinus, Code::Lambda0, - Code::Xi0, Code::XiMinus, Code::OmegaMinus, Code::DPlus, Code::DMinus, - Code::D0, Code::D0Bar}; - - Interaction::setStable(HadronsWeWantTrackedByCorsika); - } + inline Interaction::Interaction(boost::filesystem::path const& mpiInitFile, bool const print_listing) + : print_listing_(print_listing) { + // Main Pythia object for managing the cascade evolution. + // Can also do decays, but no hard processes. + + + pythiaMain_.readString("ProcessLevel:all = off"); + pythiaMain_.readString("211:mayDecay = on"); // TODO: probably not necessary, but ask Torjbörn maybe + pythiaMain_.readString("13:mayDecay = on"); + pythiaMain_.readString("321:mayDecay = on"); + pythiaMain_.readString("130:mayDecay = on"); + + // Redure statistics printout to relevant ones. + pythiaMain_.readString("Stat:showProcessLevel = off"); + pythiaMain_.readString("Stat:showPartonLevel = off"); + + // Add Argon, since not in Particle data. id:all = name antiName + // spinType chargeType colType m0 mWidth mMin mMax tau0. + pythiaMain_.readString("1000180400:all = 40Ar 40Arbar 1 54 0 " + "37.22474 0. 0. 0. 0."); + + if (!pythiaMain_.init()) + throw std::runtime_error("Pythia::Interaction: Initialization failed!"); + + + // TODO: do we need this? + //~ CORSIKA_LOG_INFO("Configuring Pythia8 from: {}", CORSIKA_Pythia8_XML_DIR); + + // Secondary Pythia object for performing individual collisions. + // Variable incoming beam type and energy. + pythiaColl_.readString("Beams:allowVariableEnergy = on"); + pythiaColl_.readString("Beams:allowIDAswitch = on"); + + // Set up for fixed-target collisions. + pythiaColl_.readString("Beams:frameType = 3"); // arbitrary frame, need to define full 4-momenta + pythiaColl_.settings.parm("Beams:pzA", eMaxLab_ / 1_GeV); + pythiaColl_.readString("Beams:pzB = 0."); + + // Must use the soft and low-energy QCD processes. + pythiaColl_.readString("SoftQCD:all = on"); + pythiaColl_.readString("LowEnergyQCD:all = on"); + + // Decays to be done by pythiaMain_. + pythiaColl_.readString("HadronLevel:Decay = off"); + + // Reduce printout and relax energy-momentum conservation. + pythiaColl_.readString("Print:quiet = on"); + pythiaColl_.readString("Check:epTolErr = 0.1"); + + // Redure statistics printout to relevant ones. + pythiaColl_.readString("Stat:showProcessLevel = off"); + pythiaColl_.readString("Stat:showPartonLevel = off"); + + + bool const reuse = true; // TODO: make this more flexible + // Reuse MPI initialization file if it exists; else create a new one. + if (reuse) pythiaColl_.readString("MultipartonInteractions:reuseInit = 3"); + else pythiaColl_.readString("MultipartonInteractions:reuseInit = 1"); + pythiaColl_.settings.word("MultipartonInteractions:initFile", mpiInitFile.native()); + + // initialize + // we can't test this block, LCOV_EXCL_START + if (!pythiaColl_.init()) + throw std::runtime_error("Pythia::Interaction: Initialization failed!"); + // LCOV_EXCL_STOP + + + //~ // any decays in pythia? if yes need to define which particles + //~ if (internalDecays_) { + //~ // define which particles are passed to corsika, i.e. which particles make it into + //~ // history even very shortlived particles like charm or pi0 are of interest here + //~ std::vector<Code> const HadronsWeWantTrackedByCorsika = { + //~ Code::PiPlus, Code::PiMinus, Code::Pi0, Code::KMinus, Code::KPlus, + //~ Code::K0Long, Code::K0Short, Code::SigmaPlus, Code::SigmaMinus, Code::Lambda0, + //~ Code::Xi0, Code::XiMinus, Code::OmegaMinus, Code::DPlus, Code::DMinus, + //~ Code::D0, Code::D0Bar}; + + //~ Interaction::setStable(HadronsWeWantTrackedByCorsika); + //~ } } - inline void Interaction::setStable(std::vector<Code> const& particleList) { - for (auto p : particleList) Interaction::setStable(p); - } + //~ inline void Interaction::setStable(std::vector<Code> const& particleList) { + //~ for (auto p : particleList) Interaction::setStable(p); + //~ } - inline void Interaction::setUnstable(Code const pCode) { - CORSIKA_LOG_DEBUG("Pythia::Interaction: setting {} unstable..", pCode); - Pythia8::Pythia::particleData.mayDecay(static_cast<int>(get_PDG(pCode)), true); - } + //~ inline void Interaction::setUnstable(Code const pCode) { + //~ CORSIKA_LOG_DEBUG("Pythia::Interaction: setting {} unstable..", pCode); + //~ Pythia8::Pythia::particleData.mayDecay(static_cast<int>(get_PDG(pCode)), true); + //~ } - inline void Interaction::setStable(Code const pCode) { - CORSIKA_LOG_DEBUG("Pythia::Interaction: setting {} stable..", pCode); - Pythia8::Pythia::particleData.mayDecay(static_cast<int>(get_PDG(pCode)), false); - } + //~ inline void Interaction::setStable(Code const pCode) { + //~ CORSIKA_LOG_DEBUG("Pythia::Interaction: setting {} stable..", pCode); + //~ Pythia8::Pythia::particleData.mayDecay(static_cast<int>(get_PDG(pCode)), false); + //~ } inline bool Interaction::isValid(Code const projectileId, Code const targetId, HEPEnergyType const sqrtS) const { - // TODO: rethink this function. Pythia can do much more now. - return true; - } - - inline void Interaction::configureLabFrameCollision(Code const projectileId, - Code const targetId, - MomentumVector const& projectileMom, - MomentumVector const& targetMom) { - // Pythia configuration of the current event - // very clumsy. I am sure this can be done better.. - - // set beam - // beam id for pythia - auto const pdgBeam = static_cast<int>(get_PDG(projectileId)); - - // set target - auto pdgTarget = static_cast<int>(get_PDG(targetId)); - // replace hydrogen with proton, otherwise pythia goes into heavy ion mode! - if (targetId == Code::Hydrogen) - pdgTarget = static_cast<int>(get_PDG(Code::Proton)); + if (is_nucleus(projectileId)) // not yet possible with Pythia + return false; + + // TODO: check sqrtS for validity - setBeamIDs(pdgBeam, pdgTarget); - - auto const projMomGeV = projectileMom.getComponents() * (1/1_GeV); - auto const tarMomGeV = targetMom.getComponents() * (1/1_GeV); - - setKinematics(projMomGeV[0], projMomGeV[1], projMomGeV[2], - tarMomGeV[0], tarMomGeV[1], tarMomGeV[2]); - - // initialize this config - // we can't test this block, LCOV_EXCL_START - if (!Pythia8::Pythia::init()) - throw std::runtime_error("Pythia::Interaction: Initialization failed!"); - // LCOV_EXCL_STOP + return std::find(validTargets_.begin(), validTargets_.end(), targetId) != validTargets_.end(); } inline bool Interaction::canInteract(Code const pCode) const { @@ -128,12 +138,14 @@ namespace corsika::pythia8 { CrossSectionType Interaction::getCrossSection(Code const projectileId, Code const targetId, FourMomentum const& projectileP4, - FourMomentum const& targetP4) const { + FourMomentum const& targetP4) const + { - HEPEnergyType const CoMenergy = (projectileP4 + targetP4).getNorm(); + HEPEnergyType const CoMenergy = is_nucleus(targetId) ? + (projectileP4 + targetP4 / get_nucleus_A(targetId)).getNorm() : (projectileP4 + targetP4).getNorm(); if (!isValid(projectileId, targetId, CoMenergy)) { - return {CrossSectionType::zero(), CrossSectionType::zero()}; + return CrossSectionType::zero(); } // input particle PDG @@ -141,21 +153,36 @@ namespace corsika::pythia8 { auto const pdgCodeTarget = static_cast<int>(get_PDG(targetId)); double const ecm_GeV = CoMenergy * (1 / 1_GeV); - auto const sigTot = pythia.getSigmaTotal(pdgCodeBeam, 2212, ecm_GeV) * 1_mb; - auto const sigEla = pythia.getSigmaPartial(pdgCodeBeam, 2212, evm_GeV, 2) * 1_mb; - auto const sigProd = sigTot - sigEla; - - if (is_nucleus(targetId)) { - // avg. no. of sub-collisions, from 2108.03481, for Nitrogen target... - // TODO: generalize - double const nCollAvg = (sigTot < 31._mb) ? 1. + (0.017 / 1_mb) * sigTot - : 1.2 + (0.0105 / 1_mb) * sigTot; - - // TODO: is this valid also for production XS? Paper says total XS... - return get_nucleus_A(targetId) * sigProd / nCollAvg; + auto const sigTot = pythiaColl_.getSigmaTotal(pdgCodeBeam, 2212, ecm_GeV) * 1_mb; + auto const sigEla = pythiaColl_.getSigmaPartial(pdgCodeBeam, 2212, ecm_GeV, 2) * 1_mb; + //~ auto const sigProd = sigTot - sigEla; + + // TODO: handle hydrogen as proton + if (!is_nucleus(targetId)) { + return sigTot; } else { - return sigProd; + return sigTot * get_nucleus_A(targetId) / getAverageSubcollisions(targetId, sigTot); + } + } + + double Interaction::getAverageSubcollisions(Code targetId, CrossSectionType sigTot) const { + auto const Z = get_nucleus_Z(targetId); + auto const A = get_nucleus_A(targetId); + + double nCollAvg; + + if (Z == 7 && A == 14) nCollAvg = (sigTot < 31._mb) // this is from the paper + ? 1. + 0.017/1_mb * sigTot : 1.2 + 0.0105/1_mb * sigTot; + else if (Z == 8 && A == 16) nCollAvg = (sigTot < 16._mb) // provided by Torbjörn Sjöstrand + ? 1. + 0.0245/1_mb * sigTot : 1.2 + 0.012/1_mb * sigTot; + else if (Z == 18 && A == 40) nCollAvg = (sigTot < 10._mb) + ? 1. + 0.050/1_mb * sigTot : 1.28 + 0.022/1_mb * sigTot; + else { + CORSIKA_LOG_ERROR(fmt::format("Pythia8 cross-sections not defined for ({},{}) nucleus", A, Z)); + nCollAvg = 0; } + + return nCollAvg; } template <class TView> @@ -187,7 +214,6 @@ namespace corsika::pythia8 { TimeType const tOrig = projectile.getTime(); CORSIKA_LOG_DEBUG("Interaction: ebeam lab: {} GeV", eProjectileLab / 1_GeV); - CORSIKA_LOG_DEBUG("Interaction: position of interaction: ", pOrig.getCoordinates()); CORSIKA_LOG_DEBUG("Interaction: time: {}", tOrig); @@ -198,36 +224,144 @@ namespace corsika::pythia8 { eProjectileLab / 1_GeV, sqrtS / 1_GeV); count_++; - configureLabFrameCollision(projectileId, targetId, projectileP4.getSpacelikeComponents(), targetP4.getSpacelikeComponents()); - - CrossSectionType const sigmaHp = pythiaColl.getSigmaTotal(idNow, 2212, eCMNow); - - // create event in pytia. LCOV_EXCL_START: we don't validate pythia8 internals - if (!Pythia8::Pythia::next()) - throw std::runtime_error("Pythia::DoInteraction: failed!"); - // LCOV_EXCL_STOP - - // LCOV_EXCL_START, we don't validate pythia8 internals - if (print_listing_) { - // print final state - event.list(); - } - // LCOV_EXCL_STOP - - MomentumVector Plab_final(labCS, {0.0_GeV, 0.0_GeV, 0.0_GeV}); - HEPEnergyType Elab_final = 0_GeV; - for (int i = 0; i < event.size(); ++i) { - Pythia8::Particle const& p8p = event[i]; + + int const idNow = static_cast<int>(get_PDG(projectileId)); + + // References to the two event records. Clear main event record. + Pythia8::Event& eventMain = pythiaMain_.event; + Pythia8::Event& eventColl = pythiaColl_.event; + eventMain.clear(); + + COMBoost const labFrameBoost{targetP4.getSpaceLikeComponents(), get_mass(targetId)}; + auto const proj4MomLab = labFrameBoost.toCoM(projectileP4); + auto const& rotCS = labFrameBoost.getRotatedCS(); + auto const pProjLab = proj4MomLab.getSpaceLikeComponents().getComponents(rotCS); + + auto constexpr invGeV = 1 / 1_GeV; + + Pythia8::Vec4 const pNow{pProjLab.getX() * invGeV, pProjLab.getY() * invGeV, pProjLab.getZ() * invGeV, proj4MomLab.getTimeLikeComponent() * invGeV}; + + // Insert incoming particle in cleared main event record. + eventMain.append(90, -11, 0, 0, 1, 1, 0, 0, pNow, pNow.mCalc()); + int const iHad = eventMain.append(idNow, 12, 0, 0, 0, 0, 0, 0, pNow, projectile.getMass() * (1/1_GeV)); + + Pythia8::Vec4 const vNow{}; // production vertex; useless but necessary (?) TODO: ask TS + + eventMain[iHad].vProd(vNow); + + auto const [Anow, Znow] = std::invoke([targetId]() { + if (targetId == Code::Proton) { + return std::make_pair(1, 1); + } else if (targetId == Code::Neutron) { + return std::make_pair(1, 0); + } else if (is_nucleus(targetId)) { + return std::make_pair<int, int>(get_nucleus_A(targetId), get_nucleus_Z(targetId)); + } else { + return std::make_pair(0, 0); //TODO: what to do in this case? + } + }); + + // Set up for collisions on a nucleus. + int np = Znow; + int nn = Anow - Znow; + int sizeOld = 0; + int sizeNew = 0; + Pythia8::Vec4 const dirNow = pNow / pNow.pAbs(); + Pythia8::Rndm& rndm = pythiaMain_.rndm; + + double constexpr mp = get_mass(Code::Proton) / 1_GeV; + double const sqrtSNN_GeV = (pNow + Pythia8::Vec4{0, 0, 0, mp}).mCalc(); + + auto const nCollAvg = getAverageSubcollisions(targetId, pythiaColl_.getSigmaTotal(idNow, 2212, sqrtSNN_GeV) * 1_mb); + double const probMore = 1. - 1. / nCollAvg; + + // Loop over varying number of hit nucleons in target nucleus. + for (int iColl = 1; iColl <= Anow; ++iColl) { + if (iColl > 1 && rndm.flat() > probMore) break; + + // Pick incoming projectile: trivial for first subcollision, else ... + int iProj = iHad; + int procType = 0; + + // ... find highest-pLongitudinal particle from latest subcollision. + if (iColl > 1) { + iProj = 0; + double pMax = 0.; + for (int i = sizeOld; i < sizeNew; ++i) + if ( eventMain[i].isFinal() && eventMain[i].isHadron()) { + double const pp = Pythia8::dot3(dirNow, eventMain[i].p()); + if (pp > pMax) { + iProj = i; + pMax = pp; + } + } + + // No further subcollision if no particle with enough energy. + if ( iProj == 0 || eventMain[iProj].e() - eventMain[iProj].m() + < eKinMinLab_/1_GeV) break; + + // Choose process; only SD or ND at perturbative energies. + double const eCMSub = (eventMain[iProj].p() + Pythia8::Vec4{0, 0, 0, mp}).mCalc(); + if (eCMSub > 10.) procType = (rndm.flat() < probSD_) ? 4 : 1; + } + + // Pick one p or n from target. + int const idProj = eventMain[iProj].id(); + bool const doProton = rndm.flat() < (np / double(np + nn)); + if (doProton) np--; + else nn--; + int const idNuc = doProton ? 2212 : 2112; + + // Perform the projectile-nucleon subcollision. + pythiaColl_.setBeamIDs(idProj, idNuc); + pythiaColl_.setKinematics(eventMain[iProj].p(), Pythia8::Vec4()); + pythiaColl_.next(procType); + + // Insert target nucleon. Mothers are (0,iProj) to mark who it + // interacted with. Always use proton mass for simplicity. + int const statusNuc = (iColl == 1) ? -181 : -182; + int const iNuc = eventMain.append( idNuc, statusNuc, 0, iProj, 0, 0, 0, 0, + 0., 0., 0., mp, mp); + eventMain[iNuc].vProdAdd(vNow); + + // Insert secondary produced particles (but skip intermediate partons) + // into main event record and shift to correct production vertex. + sizeOld = eventMain.size(); + for (int iSub = 3; iSub < eventColl.size(); ++iSub) { + if (!eventColl[iSub].isFinal()) continue; + int iNew = eventMain.append(eventColl[iSub]); + eventMain[iNew].mothers(iNuc, iProj); + eventMain[iNew].vProdAdd(vNow); + } + sizeNew = eventMain.size(); + + // Update daughters of colliding hadrons and other history. + eventMain[iProj].daughters(sizeOld, sizeNew - 1); + eventMain[iNuc].daughters(sizeOld, sizeNew - 1); + eventMain[iProj].statusNeg(); + double dTau = (iColl == 1) ? (vNow.e() - eventMain[iHad].tProd()) + * eventMain[iHad].m() / eventMain[iHad].e() : 0.; + eventMain[iProj].tau(dTau); + + // End of loop over interactions in a nucleus. + } + + + MomentumVector Plab_final{labFrameBoost.getOriginalCS()}; + auto Elab_final = HEPEnergyType::zero(); + + for (Pythia8::Particle const& p8p: eventColl) { // skip particles that have decayed in pythia if (!p8p.isFinal()) continue; auto const pyId = convert_from_PDG(static_cast<PDGCode>(p8p.id())); - MomentumVector const pyPlab(labCS, + MomentumVector const pyPlab(rotCS, {p8p.px() * 1_GeV, p8p.py() * 1_GeV, p8p.pz() * 1_GeV}); + auto const pyP = labFrameBoost.fromCoM(FourVector{p8p.e() * 1_GeV, pyPlab}); HEPEnergyType const mass = get_mass(pyId); - HEPEnergyType const Ekin = sqrt(pyPlab.getSquaredNorm() + mass * mass) - mass; + HEPEnergyType const Ekin = sqrt(pyP.getSpaceLikeComponents().getSquaredNorm() + mass * mass) - mass; // add to corsika stack auto pnew = @@ -236,6 +370,8 @@ namespace corsika::pythia8 { Plab_final += pnew.getMomentum(); Elab_final += pnew.getEnergy(); } + + eventMain.clear(); CORSIKA_LOG_DEBUG( "conservation (all GeV): " diff --git a/corsika/modules/pythia8/Interaction.hpp b/corsika/modules/pythia8/Interaction.hpp index 6385430f9a0046dab9e4284b41ec7a3688f47545..c9ebe4965fd1a0cd170020e8aca4d0227f3a9233 100644 --- a/corsika/modules/pythia8/Interaction.hpp +++ b/corsika/modules/pythia8/Interaction.hpp @@ -8,54 +8,57 @@ #pragma once +#include <tuple> +#include <boost/filesystem/path.hpp> + +#include <corsika/framework/utility/CorsikaData.hpp> #include <corsika/framework/core/ParticleProperties.hpp> #include <corsika/framework/core/PhysicalUnits.hpp> #include <corsika/framework/random/RNGManager.hpp> #include <corsika/framework/process/InteractionProcess.hpp> #include <corsika/modules/pythia8/Pythia8.hpp> -#include <tuple> - namespace corsika::pythia8 { - class Interaction : public InteractionProcess<Interaction>, public Pythia8::Pythia { + class Interaction : public InteractionProcess<Interaction> { public: - Interaction(bool const print_listing = false); + Interaction(boost::filesystem::path const& mpiInitFile = corsika_data("Pythia/main184.mpi"), + bool const print_listing = false); ~Interaction(); - void setStable(std::vector<Code> const&); - void setUnstable(Code const); - void setStable(Code const); + //~ void setStable(std::vector<Code> const&); + //~ void setUnstable(Code const); + //~ void setStable(Code const); bool isValidCoMEnergy(HEPEnergyType const ecm) const { return (10_GeV < ecm) && (ecm < 1_PeV); } bool canInteract(Code const) const; - void configureLabFrameCollision(Code const, Code const, HEPEnergyType const); + //~ void configureLabFrameCollision(Code const, Code const, HEPEnergyType const); bool isValid(Code const projectileId, Code const targetId, HEPEnergyType const sqrtS) const; - /** - * Returns inelastic AND elastic cross sections. - * - * These cross sections must correspond to the process described in doInteraction - * AND elastic scattering (sigma_tot = sigma_inel + sigma_el). Allowed targets are: - * nuclei or single nucleons (p,n,hydrogen). This "InelEla" method is used since - * Sibyll must be useful inside the NuclearInteraction model, which requires that. - * - * @param projectile is the Code of the projectile - * @param target is the Code of the target - * @param sqrtSnn is the center-of-mass energy (per nucleon pair) - * @param Aprojectil is the mass number of the projectils, if it is a nucleus - * @param Atarget is the mass number of the target, if it is a nucleus - * - * @return a tuple of: inelastic cross section, elastic cross section - */ - std::tuple<CrossSectionType, CrossSectionType> getCrossSectionInelEla( - Code const projectile, Code const target, FourMomentum const& projectileP4, - FourMomentum const& targetP4) const; + //~ /** + //~ * Returns inelastic AND elastic cross sections. + //~ * + //~ * These cross sections must correspond to the process described in doInteraction + //~ * AND elastic scattering (sigma_tot = sigma_inel + sigma_el). Allowed targets are: + //~ * nuclei or single nucleons (p,n,hydrogen). This "InelEla" method is used since + //~ * Sibyll must be useful inside the NuclearInteraction model, which requires that. + //~ * + //~ * @param projectile is the Code of the projectile + //~ * @param target is the Code of the target + //~ * @param sqrtSnn is the center-of-mass energy (per nucleon pair) + //~ * @param Aprojectil is the mass number of the projectils, if it is a nucleus + //~ * @param Atarget is the mass number of the target, if it is a nucleus + //~ * + //~ * @return a tuple of: inelastic cross section, elastic cross section + //~ */ + //~ std::tuple<CrossSectionType, CrossSectionType> getCrossSectionInelEla( + //~ Code const projectile, Code const target, FourMomentum const& projectileP4, + //~ FourMomentum const& targetP4) const; /** * Returns inelastic (production) cross section. @@ -74,7 +77,7 @@ namespace corsika::pythia8 { */ CrossSectionType getCrossSection(Code const projectile, Code const target, FourMomentum const& projectileP4, - FourMomentum const& targetP4) const; + FourMomentum const& targetP4) const; // non-const for now /** * In this function PYTHIA is called to produce one event. The @@ -83,12 +86,30 @@ namespace corsika::pythia8 { template <typename TView> void doInteraction(TView& output, Code const projectileId, Code const targetId, FourMomentum const& projectileP4, FourMomentum const& targetP4); + + + /** + * return average number of sub-collisions in a nucleus, using the parameterizations of + * Sjöstrand and Utheim, EPJ C 82 (2022) 1, 21, arXiv:2108.03481 [hep-ph] + * + * @param targetId target (nucleus + * @param sigTot projectile-nucleon (=proton) cross-secion + */ + double getAverageSubcollisions(Code targetId, CrossSectionType sigTot) const; + + static std::array constexpr validTargets_{Code::Oxygen, Code::Nitrogen, Code::Argon, Code::Hydrogen, Code::Proton, Code::Neutron}; private: default_prng_type& RNG_ = RNGManager<>::getInstance().getRandomStream("pythia"); - bool const internalDecays_ = true; + //~ bool const internalDecays_ = true; int count_ = 0; - bool print_listing_ = false; + bool const print_listing_ = false; + Pythia8::Pythia pythiaMain_; + Pythia8::Pythia mutable pythiaColl_; // Pythia's getSigma...() are not marked const... + double const probSD_ = 0.3; // Fraction of single diffractive events beyond first collision in nucleus. + HEPEnergyType const eMaxLab_ = 1e18_eV; + HEPEnergyType const eKinMinLab_ = 0.2_GeV; + }; } // namespace corsika::pythia8 diff --git a/modules/pythia8/CMakeLists.txt b/modules/pythia8/CMakeLists.txt index 06edb532098c0d54e8c48e48793b63e4b29ab781..1f6449a81f29e45b55ff0d1d5e001d9e25a33481 100644 --- a/modules/pythia8/CMakeLists.txt +++ b/modules/pythia8/CMakeLists.txt @@ -24,7 +24,7 @@ file (MAKE_DIRECTORY ${CORSIKA_Pythia8_MODULE_DIR}) add_library (C8::ext::pythia8 STATIC IMPORTED GLOBAL) if ("x_${USE_Pythia8_C8}" STREQUAL "x_SYSTEM") - find_package (Pythia8 8245 EXACT REQUIRED) + find_package (Pythia8 8307 EXACT REQUIRED) message (STATUS "Using system-level Pythia8 version ${Pythia8_VERSION} at ${Pythia8_PREFIX}") set (Pythia8_INCLUDE_DIRS ${Pythia8_INCLUDE_DIR}) set_target_properties ( @@ -50,14 +50,14 @@ if ("x_${USE_Pythia8_C8}" STREQUAL "x_SYSTEM") else () - set (_C8_Pythia8_VERSION "8245") + set (_C8_Pythia8_VERSION "8307") message (STATUS "Building modules/pythia8 using pythia${_C8_Pythia8_VERSION}-stripped.tar.bz2") message (STATUS "This will take a bit.....") # this is not a full Pythia8 install, it is a bit simplified, e.g. no pythia8-config, no examples # and also, it is fitted into the normal CORSIKA8 install "include/corsika_modules/Pythia8, lib/corsika" ExternalProject_Add (pythia8 - URL ${CMAKE_CURRENT_SOURCE_DIR}/pythia${_C8_Pythia8_VERSION}-stripped.tar.bz2 - URL_MD5 d3e951a2f101e8cfec26405cb61db83b + URL ${CMAKE_CURRENT_SOURCE_DIR}/pythia${_C8_Pythia8_VERSION}.tgz + URL_MD5 4ee7aea85cc66db13e92722f150c51c5 SOURCE_DIR "${CMAKE_CURRENT_BINARY_DIR}/pythia8/source" INSTALL_DIR "${CMAKE_CURRENT_BINARY_DIR}/pythia8/install" CONFIGURE_COMMAND ./configure --prefix=${CMAKE_CURRENT_BINARY_DIR}/pythia8/install diff --git a/modules/pythia8/pythia8245-stripped.tar.bz2 b/modules/pythia8/pythia8245-stripped.tar.bz2 deleted file mode 100644 index 0ad96435f488cac464393fee13b4d5f4a38794fd..0000000000000000000000000000000000000000 Binary files a/modules/pythia8/pythia8245-stripped.tar.bz2 and /dev/null differ diff --git a/modules/pythia8/pythia8307.tgz b/modules/pythia8/pythia8307.tgz new file mode 120000 index 0000000000000000000000000000000000000000..c0a689fb4c46b5ddc653e4d08bc6ac3245ff8c06 --- /dev/null +++ b/modules/pythia8/pythia8307.tgz @@ -0,0 +1 @@ +/cr/users/reininghaus/Downloads/pythia8307.tgz \ No newline at end of file diff --git a/tests/modules/testPythia8.cpp b/tests/modules/testPythia8.cpp index f1b9892a51412ca963d0636726c97a449a1e00c7..6f43f003cc79ee342553162158d03092f9db098f 100644 --- a/tests/modules/testPythia8.cpp +++ b/tests/modules/testPythia8.cpp @@ -89,7 +89,7 @@ using namespace corsika; template <typename TStackView> auto sumMomentum(TStackView const& view, CoordinateSystemPtr const& vCS) { - MomentumVector sum{vCS, 0_eV, 0_eV, 0_eV}; + MomentumVector sum{vCS}; for (auto const& p : view) { sum += p.getMomentum(); } return sum; } @@ -182,21 +182,21 @@ TEST_CASE("Pythia8Interface", "modules") { CHECK(collision.canInteract(Code::PiPlus)); CHECK_FALSE(collision.canInteract(Code::Electron)); - // nuclei not supported - std::tuple<CrossSectionType, CrossSectionType> xs_test = - collision.getCrossSectionInelEla( - Code::Proton, Code::Hydrogen, - {sqrt(static_pow<2>(Proton::mass) + static_pow<2>(100_GeV)), - {rootCS, {0_eV, 0_eV, 100_GeV}}}, - {Hydrogen::mass, {rootCS, {0_eV, 0_eV, 0_eV}}}); - CHECK(std::get<0>(xs_test) / 1_mb == Approx(314).margin(2)); - CHECK(std::get<1>(xs_test) / 1_mb == Approx(69).margin(2)); - - collision.doInteraction(view, Code::Proton, Code::Hydrogen, + //~ // nuclei not supported + //~ std::tuple<CrossSectionType, CrossSectionType> xs_test = + //~ collision.getCrossSectionInelEla( + //~ Code::Proton, Code::Hydrogen, + //~ {sqrt(static_pow<2>(Proton::mass) + static_pow<2>(100_GeV)), + //~ {rootCS, {0_eV, 0_eV, 100_GeV}}}, + //~ {Hydrogen::mass, {rootCS, {0_eV, 0_eV, 0_eV}}}); + //~ CHECK(std::get<0>(xs_test) / 1_mb == Approx(314).margin(2)); + //~ CHECK(std::get<1>(xs_test) / 1_mb == Approx(69).margin(2)); + + collision.doInteraction(view, Code::Proton, Code::Nitrogen, {sqrt(static_pow<2>(Proton::mass) + static_pow<2>(100_GeV)), {rootCS, {0_eV, 0_eV, 100_GeV}}}, - {Hydrogen::mass, {rootCS, {0_eV, 0_eV, 0_eV}}}); - CHECK(view.getSize() == 12); + {Nitrogen::mass, {rootCS, {0_eV, 0_eV, 0_eV}}}); + CHECK(view.getSize() > 0); } SECTION("pythia too low energy") {