diff --git a/Framework/Geometry/oCoordinateSystem.h b/Framework/Geometry/oCoordinateSystem.h new file mode 100644 index 0000000000000000000000000000000000000000..614c76da9d2f32925fa9d8c18828196fbbe8f19f --- /dev/null +++ b/Framework/Geometry/oCoordinateSystem.h @@ -0,0 +1,125 @@ +/* + * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu + * + * See file AUTHORS for a list of contributors. + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ + +#ifndef _include_COORDINATESYSTEM_H_ +#define _include_COORDINATESYSTEM_H_ + +#include <corsika/geometry/QuantityVector.h> +#include <corsika/units/PhysicalUnits.h> +#include <corsika/utl/sgn.h> +#include <Eigen/Dense> +#include <stdexcept> + +typedef Eigen::Transform<double, 3, Eigen::Affine> EigenTransform; +typedef Eigen::Translation<double, 3> EigenTranslation; + +namespace corsika::geometry { + + class RootCoordinateSystem; + template <typename T> + class Vector; + + using corsika::units::si::length_d; + + class CoordinateSystem { + CoordinateSystem const* reference = nullptr; + EigenTransform transf; + + CoordinateSystem(CoordinateSystem const& reference, EigenTransform const& transf) + : reference(&reference) + , transf(transf) {} + + CoordinateSystem() + : // for creating the root CS + transf(EigenTransform::Identity()) {} + + protected: + static auto CreateCS() { return CoordinateSystem(); } + friend corsika::geometry::RootCoordinateSystem; /// this is the only class that can + /// create ONE unique root CS + + public: + static EigenTransform GetTransformation(CoordinateSystem const& c1, + CoordinateSystem const& c2); + + auto& operator=(const CoordinateSystem& pCS) { + reference = pCS.reference; + transf = pCS.transf; + return *this; + } + + auto translate(QuantityVector<length_d> vector) const { + EigenTransform const translation{EigenTranslation(vector.eVector)}; + + return CoordinateSystem(*this, translation); + } + + template <typename TDim> + auto RotateToZ(Vector<TDim> vVec) const { + auto const a = vVec.normalized().GetComponents(*this).eVector; + auto const a1 = a(0), a2 = a(1); + + auto const s = utl::sgn(a(2)); + auto const c = 1 / (1 + s * a(2)); + + Eigen::Matrix3d A, B; + + if (s > 0) { + A << 1, 0, a1, // comment to prevent clang-format + 0, 1, a2, // . + -a1, -a2, 1; // . + B << -a1 * a1 * c, -a1 * a2 * c, 0, // . + -a1 * a2 * c, -a2 * a2 * c, 0, // . + 0, 0, -(a1 * a1 + a2 * a2) * c; // . + + } else { + A << 1, 0, a1, // . + 0, -1, a2, // . + a1, -a2, -1; // . + B << -a1 * a1 * c, +a1 * a2 * c, 0, // . + -a1 * a2 * c, +a2 * a2 * c, 0, // . + 0, 0, (a1 * a1 + a2 * a2) * c; // . + } + + return CoordinateSystem(*this, EigenTransform(A + B)); + } + + template <typename TDim> + auto rotate(QuantityVector<TDim> axis, double angle) const { + if (axis.eVector.isZero()) { + throw std::runtime_error("null-vector given as axis parameter"); + } + + EigenTransform const rotation{Eigen::AngleAxisd(angle, axis.eVector.normalized())}; + + return CoordinateSystem(*this, rotation); + } + + template <typename TDim> + auto translateAndRotate(QuantityVector<phys::units::length_d> translation, + QuantityVector<TDim> axis, double angle) { + if (axis.eVector.isZero()) { + throw std::runtime_error("null-vector given as axis parameter"); + } + + EigenTransform const transf{Eigen::AngleAxisd(angle, axis.eVector.normalized()) * + EigenTranslation(translation.eVector)}; + + return CoordinateSystem(*this, transf); + } + + auto const* GetReference() const { return reference; } + + auto const& GetTransform() const { return transf; } + }; + +} // namespace corsika::geometry + +#endif diff --git a/Framework/Utilities/otestCOMBoost.cc b/Framework/Utilities/otestCOMBoost.cc new file mode 100644 index 0000000000000000000000000000000000000000..572839296b07634b219f518eac5192e086cda5e4 --- /dev/null +++ b/Framework/Utilities/otestCOMBoost.cc @@ -0,0 +1,213 @@ +/* + * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu + * + * See file AUTHORS for a list of contributors. + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ + +#include <catch2/catch.hpp> + +#include <corsika/geometry/FourVector.h> +#include <corsika/geometry/RootCoordinateSystem.h> +#include <corsika/geometry/Vector.h> +#include <corsika/units/PhysicalUnits.h> +#include <corsika/utl/COMBoost.h> + +#include <iostream> + +using namespace corsika::geometry; +using namespace corsika::utl; +using namespace corsika::units::si; +using corsika::units::constants::c; +using corsika::units::constants::cSquared; + +double constexpr absMargin = 1e-6; + +CoordinateSystem const& rootCS = + RootCoordinateSystem::GetInstance().GetRootCoordinateSystem(); + +// helper function for energy-momentum +// relativistic energy +auto const energy = [](HEPMassType m, Vector<hepmomentum_d> const& p) { + return sqrt(m * m + p.squaredNorm()); +}; + +// helper function for mandelstam-s +auto const s = [](HEPEnergyType E, QuantityVector<hepmomentum_d> const& p) { + return E * E - p.squaredNorm(); +}; + +TEST_CASE("boosts") { + // define target kinematics in lab frame + HEPMassType const targetMass = 1_GeV; + Vector<hepmomentum_d> pTargetLab{rootCS, {0_eV, 0_eV, 0_eV}}; + HEPEnergyType const eTargetLab = energy(targetMass, pTargetLab); + + /* + General tests check the interface and basic operation + */ + + SECTION("General tests") { + + // define projectile kinematics in lab frame + HEPMassType const projectileMass = 1._GeV; + Vector<hepmomentum_d> pProjectileLab{rootCS, {0_GeV, 1_PeV, 0_GeV}}; + HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab); + const FourVector PprojLab(eProjectileLab, pProjectileLab); + + // define boost to com frame + COMBoost boost(PprojLab, targetMass); + + // boost projecticle + auto const PprojCoM = boost.toCoM(PprojLab); + + // boost target + auto const PtargCoM = boost.toCoM(FourVector(targetMass, pTargetLab)); + + // sum of momenta in CoM, should be 0 + auto const sumPCoM = + PprojCoM.GetSpaceLikeComponents() + PtargCoM.GetSpaceLikeComponents(); + CHECK(sumPCoM.norm() / 1_GeV == Approx(0).margin(absMargin)); + + // mandelstam-s should be invariant under transformation + CHECK(s(eProjectileLab + eTargetLab, + pProjectileLab.GetComponents() + pTargetLab.GetComponents()) / + 1_GeV / 1_GeV == + Approx(s(PprojCoM.GetTimeLikeComponent() + PtargCoM.GetTimeLikeComponent(), + PprojCoM.GetSpaceLikeComponents().GetComponents() + + PtargCoM.GetSpaceLikeComponents().GetComponents()) / + 1_GeV / 1_GeV)); + + // boost back... + auto const PprojBack = boost.fromCoM(PprojCoM); + + // ...should yield original values before the boosts + CHECK(PprojBack.GetTimeLikeComponent() / PprojLab.GetTimeLikeComponent() == + Approx(1)); + CHECK( + (PprojBack.GetSpaceLikeComponents() - PprojLab.GetSpaceLikeComponents()).norm() / + PprojLab.GetSpaceLikeComponents().norm() == + Approx(0).margin(absMargin)); + } + + /* + special case: projectile along -z + */ + + SECTION("Test boost along z-axis") { + + // define projectile kinematics in lab frame + HEPMassType const projectileMass = 1_GeV; + Vector<hepmomentum_d> pProjectileLab{rootCS, {0_GeV, 0_PeV, -1_PeV}}; + HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab); + const FourVector PprojLab(eProjectileLab, pProjectileLab); + + // define boost to com frame + COMBoost boost(PprojLab, targetMass); + + // boost projecticle + auto const PprojCoM = boost.toCoM(PprojLab); + + // boost target + auto const PtargCoM = boost.toCoM(FourVector(targetMass, pTargetLab)); + + // sum of momenta in CoM, should be 0 + auto const sumPCoM = + PprojCoM.GetSpaceLikeComponents() + PtargCoM.GetSpaceLikeComponents(); + CHECK(sumPCoM.norm() / 1_GeV == Approx(0).margin(absMargin)); + } + + /* + special case: projectile with arbitrary direction + */ + + SECTION("Test boost along tilted axis") { + + const HEPMomentumType P0 = 1_PeV; + double theta = 33.; + double phi = -10.; + 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)); + }; + auto const [px, py, pz] = + momentumComponents(theta / 180. * M_PI, phi / 180. * M_PI, P0); + + // define projectile kinematics in lab frame + HEPMassType const projectileMass = 1_GeV; + Vector<hepmomentum_d> pProjectileLab(rootCS, {px, py, pz}); + HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab); + const FourVector PprojLab(eProjectileLab, pProjectileLab); + + // define boost to com frame + COMBoost boost(PprojLab, targetMass); + + // boost projecticle + auto const PprojCoM = boost.toCoM(PprojLab); + + // boost target + auto const PtargCoM = boost.toCoM(FourVector(targetMass, pTargetLab)); + + // sum of momenta in CoM, should be 0 + auto const sumPCoM = + PprojCoM.GetSpaceLikeComponents() + PtargCoM.GetSpaceLikeComponents(); + CHECK(sumPCoM.norm() / 1_GeV == Approx(0).margin(absMargin)); + } + + /* + test the ultra-high energy behaviour: E=ZeV + */ + + SECTION("High energy") { + // define projectile kinematics in lab frame + HEPMassType const projectileMass = 1_GeV; + HEPMomentumType P0 = 1_ZeV; + Vector<hepmomentum_d> pProjectileLab{rootCS, {0_GeV, 0_PeV, -P0}}; + HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab); + const FourVector PprojLab(eProjectileLab, pProjectileLab); + + // define boost to com frame + COMBoost boost(PprojLab, targetMass); + + // boost projecticle + auto const PprojCoM = boost.toCoM(PprojLab); + + // boost target + auto const PtargCoM = boost.toCoM(FourVector(targetMass, pTargetLab)); + + // sum of momenta in CoM, should be 0 + auto const sumPCoM = + PprojCoM.GetSpaceLikeComponents() + PtargCoM.GetSpaceLikeComponents(); + CHECK(sumPCoM.norm() / P0 == Approx(0).margin(absMargin)); // MAKE RELATIVE CHECK + } + + SECTION("rest frame") { + HEPMassType const projectileMass = 1_GeV; + HEPMomentumType const P0 = 1_TeV; + Vector<hepmomentum_d> pProjectileLab{rootCS, {0_GeV, P0, 0_GeV}}; + HEPEnergyType const eProjectileLab = energy(projectileMass, pProjectileLab); + const FourVector PprojLab(eProjectileLab, pProjectileLab); + + COMBoost boostRest(pProjectileLab, projectileMass); + auto const& csPrime = boostRest.GetRotatedCS(); + FourVector const rest4Mom = boostRest.toCoM(PprojLab); + + CHECK(rest4Mom.GetTimeLikeComponent() / 1_GeV == Approx(projectileMass / 1_GeV)); + CHECK(rest4Mom.GetSpaceLikeComponents().norm() / 1_GeV == + Approx(0).margin(absMargin)); + + FourVector const a{0_eV, Vector{csPrime, 0_eV, 5_GeV, 0_eV}}; + FourVector const b{0_eV, Vector{rootCS, 3_GeV, 0_eV, 0_eV}}; + auto const aLab = boostRest.fromCoM(a); + auto const bLab = boostRest.fromCoM(b); + + CHECK(aLab.GetNorm() / a.GetNorm() == Approx(1)); + CHECK(aLab.GetSpaceLikeComponents().GetComponents(csPrime)[1].magnitude() == + Approx((5_GeV).magnitude())); + CHECK(bLab.GetSpaceLikeComponents().GetComponents(rootCS)[0].magnitude() == + Approx((3_GeV).magnitude())); + } +} diff --git a/Processes/Sibyll/oInteraction.cc b/Processes/Sibyll/oInteraction.cc new file mode 100644 index 0000000000000000000000000000000000000000..916ea2ec0829f6de41b4055428bac17b78f0fb62 --- /dev/null +++ b/Processes/Sibyll/oInteraction.cc @@ -0,0 +1,339 @@ +/* + * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu + * + * See file AUTHORS for a list of contributors. + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ + +#include <corsika/process/sibyll/Interaction.h> + +#include <corsika/environment/Environment.h> +#include <corsika/environment/NuclearComposition.h> +#include <corsika/geometry/FourVector.h> +#include <corsika/process/sibyll/ParticleConversion.h> +#include <corsika/process/sibyll/SibStack.h> +#include <corsika/process/sibyll/sibyll2.3d.h> +#include <corsika/setup/SetupStack.h> +#include <corsika/setup/SetupTrajectory.h> +#include <corsika/utl/COMBoost.h> + +#include <tuple> + +using std::cout; +using std::endl; +using std::tuple; + +using namespace corsika; +using namespace corsika::setup; +using SetupParticle = setup::Stack::StackIterator; +using SetupProjectile = setup::StackView::StackIterator; +using Track = Trajectory; + +namespace corsika::process::sibyll { + + Interaction::Interaction() {} + + Interaction::~Interaction() { + cout << "Sibyll::Interaction n=" << count_ << " Nnuc=" << nucCount_ << endl; + } + + void Interaction::Init() { + + using random::RNGManager; + + // initialize Sibyll + if (!initialized_) { + sibyll_ini_(); + initialized_ = true; + } + } + + void Interaction::SetAllStable() { + for (int i = 0; i < 99; ++i) s_csydec_.idb[i] = -1 * abs(s_csydec_.idb[i]); + } + + tuple<units::si::CrossSectionType, units::si::CrossSectionType> + Interaction::GetCrossSection(const particles::Code BeamId, + const particles::Code TargetId, + const units::si::HEPEnergyType CoMenergy) const { + using namespace units::si; + 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; + if (particles::IsNucleus(TargetId)) { + const int iTarget = particles::GetNucleusA(TargetId); + if (iTarget > maxTargetMassNumber_ || 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); + } 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_mb, sigEla * 1_mb); + } + + template <> + units::si::GrammageType Interaction::GetInteractionLength( + SetupParticle const& vP) const { + + using namespace units; + using namespace units::si; + using namespace geometry; + + // coordinate system, get global frame of reference + CoordinateSystem& rootCS = + RootCoordinateSystem::GetInstance().GetRootCoordinateSystem(); + + const particles::Code corsikaBeamId = vP.GetPID(); + + // beam particles for sibyll : 1, 2, 3 for p, pi, k + // read from cross section code table + const bool kInteraction = process::sibyll::CanInteract(corsikaBeamId); + + // FOR NOW: assume target is at rest + MomentumVector pTarget(rootCS, {0_GeV, 0_GeV, 0_GeV}); + + // total momentum and energy + HEPEnergyType Elab = vP.GetEnergy() + constants::nucleonMass; + MomentumVector pTotLab(rootCS, {0_GeV, 0_GeV, 0_GeV}); + pTotLab += vP.GetMomentum(); + pTotLab += pTarget; + auto const pTotLabNorm = pTotLab.norm(); + // calculate cm. energy + const HEPEnergyType ECoM = sqrt( + (Elab + pTotLabNorm) * (Elab - pTotLabNorm)); // binomial for numerical accuracy + + cout << "Interaction: LambdaInt: \n" + << " input energy: " << vP.GetEnergy() / 1_GeV << endl + << " beam can interact:" << kInteraction << endl + << " beam pid:" << vP.GetPID() << endl; + + // TODO: move limits into variables + // FR: removed && Elab >= 8.5_GeV + if (kInteraction && IsValidCoMEnergy(ECoM)) { + + // get target from environment + /* + the target should be defined by the Environment, + ideally as full particle object so that the four momenta + and the boosts can be defined.. + */ + + auto const* currentNode = vP.GetNode(); + const auto& mediumComposition = + currentNode->GetModelProperties().GetNuclearComposition(); + + si::CrossSectionType weightedProdCrossSection = mediumComposition.WeightedSum( + [=](particles::Code targetID) -> si::CrossSectionType { + return std::get<0>(this->GetCrossSection(corsikaBeamId, targetID, ECoM)); + }); + + cout << "Interaction: " + << "IntLength: weighted CrossSection (mb): " << weightedProdCrossSection / 1_mb + << endl; + + // calculate interaction length in medium + GrammageType const int_length = mediumComposition.GetAverageMassNumber() * + units::constants::u / weightedProdCrossSection; + cout << "Interaction: " + << "interaction length (g/cm2): " << int_length / (0.001_kg) * 1_cm * 1_cm + << endl; + + return int_length; + } + + return std::numeric_limits<double>::infinity() * 1_g / (1_cm * 1_cm); + } + + /** + In this function SIBYLL is called to produce one event. The + event is copied (and boosted) into the shower lab frame. + */ + + template <> + process::EProcessReturn Interaction::DoInteraction(SetupProjectile& vP) { + + using namespace units; + using namespace utl; + using namespace units::si; + using namespace geometry; + + const auto corsikaBeamId = vP.GetPID(); + cout << "ProcessSibyll: " + << "DoInteraction: " << corsikaBeamId << " interaction? " + << process::sibyll::CanInteract(corsikaBeamId) << endl; + + if (particles::IsNucleus(corsikaBeamId)) { + // nuclei handled by different process, this should not happen + throw std::runtime_error("Nuclear projectile are not handled by SIBYLL!"); + } + + if (process::sibyll::CanInteract(corsikaBeamId)) { + const CoordinateSystem& rootCS = + RootCoordinateSystem::GetInstance().GetRootCoordinateSystem(); + + // position and time of interaction, not used in Sibyll + Point pOrig = vP.GetPosition(); + TimeType tOrig = vP.GetTime(); + + // define target + // for Sibyll is always a single nucleon + // FOR NOW: target is always at rest + const auto eTargetLab = 0_GeV + constants::nucleonMass; + const auto pTargetLab = MomentumVector(rootCS, 0_GeV, 0_GeV, 0_GeV); + const FourVector PtargLab(eTargetLab, pTargetLab); + + // define projectile + HEPEnergyType const eProjectileLab = vP.GetEnergy(); + auto const pProjectileLab = vP.GetMomentum(); + + cout << "Interaction: ebeam lab: " << eProjectileLab / 1_GeV << endl + << "Interaction: pbeam lab: " << pProjectileLab.GetComponents() / 1_GeV + << endl; + cout << "Interaction: etarget lab: " << eTargetLab / 1_GeV << endl + << "Interaction: ptarget lab: " << pTargetLab.GetComponents() / 1_GeV << endl; + + const FourVector PprojLab(eProjectileLab, pProjectileLab); + + // define target kinematics in lab frame + // define boost to and from CoM frame + // CoM frame definition in Sibyll projectile: +z + COMBoost const boost(PprojLab, constants::nucleonMass); + + // just for show: + // boost projecticle + auto const PprojCoM = boost.toCoM(PprojLab); + + // boost target + auto const PtargCoM = boost.toCoM(PtargLab); + + cout << "Interaction: ebeam CoM: " << PprojCoM.GetTimeLikeComponent() / 1_GeV + << endl + << "Interaction: pbeam CoM: " + << PprojCoM.GetSpaceLikeComponents().GetComponents() / 1_GeV << endl; + cout << "Interaction: etarget CoM: " << PtargCoM.GetTimeLikeComponent() / 1_GeV + << endl + << "Interaction: ptarget CoM: " + << PtargCoM.GetSpaceLikeComponents().GetComponents() / 1_GeV << endl; + + cout << "Interaction: position of interaction: " << pOrig.GetCoordinates() << endl; + cout << "Interaction: time: " << tOrig << endl; + + HEPEnergyType Etot = eProjectileLab + eTargetLab; + MomentumVector Ptot = vP.GetMomentum(); + // invariant mass, i.e. cm. energy + HEPEnergyType Ecm = sqrt(Etot * Etot - Ptot.squaredNorm()); + + // sample target mass number + auto const* currentNode = vP.GetNode(); + auto const& mediumComposition = + currentNode->GetModelProperties().GetNuclearComposition(); + // get cross sections for target materials + /* + Here we read the cross section from the interaction model again, + should be passed from GetInteractionLength if possible + */ + //#warning reading interaction cross section again, should not be necessary + auto const& compVec = mediumComposition.GetComponents(); + std::vector<si::CrossSectionType> cross_section_of_components(compVec.size()); + + for (size_t i = 0; i < compVec.size(); ++i) { + auto const targetId = compVec[i]; + const auto [sigProd, sigEla] = GetCrossSection(corsikaBeamId, targetId, Ecm); + [[maybe_unused]] const auto& dummy_sigEla = sigEla; + cross_section_of_components[i] = sigProd; + } + + const auto targetCode = + mediumComposition.SampleTarget(cross_section_of_components, RNG_); + cout << "Interaction: target selected: " << targetCode << endl; + /* + FOR NOW: allow nuclei with A<18 or protons only. + when medium composition becomes more complex, approximations will have to be + allowed air in atmosphere also contains some Argon. + */ + int targetSibCode = -1; + if (IsNucleus(targetCode)) targetSibCode = GetNucleusA(targetCode); + if (targetCode == particles::Proton::GetCode()) targetSibCode = 1; + cout << "Interaction: sibyll code: " << targetSibCode << endl; + if (targetSibCode > maxTargetMassNumber_ || targetSibCode < 1) + throw std::runtime_error( + "Sibyll target outside range. Only nuclei with A<18 or protons are " + "allowed."); + + // beam id for sibyll + const int kBeam = process::sibyll::ConvertToSibyllRaw(corsikaBeamId); + + cout << "Interaction: " + << " DoInteraction: E(GeV):" << eProjectileLab / 1_GeV + << " Ecm(GeV): " << Ecm / 1_GeV << endl; + 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; + throw std::runtime_error("energy too low for SIBYLL"); + } else { + count_++; + // Sibyll does not know about units.. + const double sqs = Ecm / 1_GeV; + // running sibyll, filling stack + sibyll_(kBeam, targetSibCode, sqs); + + // print final state + int print_unit = 6; + sib_list_(print_unit); + nucCount_ += get_nwounded() - 1; + + // add particles from sibyll to stack + // link to sibyll stack + SibStack ss; + + MomentumVector Plab_final(rootCS, {0.0_GeV, 0.0_GeV, 0.0_GeV}); + HEPEnergyType Elab_final = 0_GeV, Ecm_final = 0_GeV; + for (auto& psib : ss) { + + // abort on particles that have decayed in Sibyll. Should not happen! + if (psib.HasDecayed()) + throw std::runtime_error("found particle that decayed in SIBYLL!"); + + // transform energy to lab. frame + auto const pCoM = psib.GetMomentum(); + HEPEnergyType const eCoM = psib.GetEnergy(); + auto const Plab = boost.fromCoM(FourVector(eCoM, pCoM)); + + // add to corsika stack + auto pnew = vP.AddSecondary( + tuple<particles::Code, units::si::HEPEnergyType, stack::MomentumVector, + geometry::Point, units::si::TimeType>{ + process::sibyll::ConvertFromSibyll(psib.GetPID()), + Plab.GetTimeLikeComponent(), Plab.GetSpaceLikeComponents(), pOrig, + tOrig}); + + Plab_final += pnew.GetMomentum(); + Elab_final += pnew.GetEnergy(); + Ecm_final += psib.GetEnergy(); + } + cout << "conservation (all GeV): Ecm_final=" << Ecm_final / 1_GeV << endl + << "Elab_final=" << Elab_final / 1_GeV + << ", Plab_final=" << (Plab_final / 1_GeV).GetComponents() << endl; + } + } + return process::EProcessReturn::eOk; + } + +} // namespace corsika::process::sibyll diff --git a/Processes/Sibyll/otestSibyll.cc b/Processes/Sibyll/otestSibyll.cc new file mode 100644 index 0000000000000000000000000000000000000000..779529e765e42aa97e10d4ec64ea7c992979f38d --- /dev/null +++ b/Processes/Sibyll/otestSibyll.cc @@ -0,0 +1,216 @@ +/* + * (c) Copyright 2019 CORSIKA Project, corsika-project@lists.kit.edu + * + * See file AUTHORS for a list of contributors. + * + * This software is distributed under the terms of the GNU General Public + * Licence version 3 (GPL Version 3). See file LICENSE for a full version of + * the license. + */ + +#include <corsika/process/sibyll/Decay.h> +#include <corsika/process/sibyll/Interaction.h> +#include <corsika/process/sibyll/NuclearInteraction.h> +#include <corsika/process/sibyll/ParticleConversion.h> + +#include <corsika/random/RNGManager.h> + +#include <corsika/particles/ParticleProperties.h> + +#include <corsika/geometry/Point.h> +#include <corsika/units/PhysicalUnits.h> + +#include <catch2/catch.hpp> + +using namespace corsika; +using namespace corsika::process::sibyll; +using namespace corsika::units; +using namespace corsika::units::si; + +TEST_CASE("Sibyll", "[processes]") { + + SECTION("Sibyll -> Corsika") { + REQUIRE(particles::Electron::GetCode() == + process::sibyll::ConvertFromSibyll(process::sibyll::SibyllCode::Electron)); + } + + SECTION("Corsika -> Sibyll") { + REQUIRE(process::sibyll::ConvertToSibyll(particles::Electron::GetCode()) == + process::sibyll::SibyllCode::Electron); + REQUIRE(process::sibyll::ConvertToSibyllRaw(particles::Proton::GetCode()) == 13); + REQUIRE(process::sibyll::ConvertToSibyll(particles::XiStarC0::GetCode()) == + process::sibyll::SibyllCode::XiStarC0); + } + + SECTION("canInteractInSibyll") { + + REQUIRE(process::sibyll::CanInteract(particles::Proton::GetCode())); + REQUIRE(process::sibyll::CanInteract(particles::Code::XiCPlus)); + + REQUIRE_FALSE(process::sibyll::CanInteract(particles::Electron::GetCode())); + REQUIRE_FALSE(process::sibyll::CanInteract(particles::SigmaC0::GetCode())); + + REQUIRE_FALSE(process::sibyll::CanInteract(particles::Nucleus::GetCode())); + REQUIRE_FALSE(process::sibyll::CanInteract(particles::Helium::GetCode())); + } + + SECTION("cross-section type") { + + REQUIRE(process::sibyll::GetSibyllXSCode(particles::Code::Electron) == 0); + REQUIRE(process::sibyll::GetSibyllXSCode(particles::Code::K0Long) == 3); + REQUIRE(process::sibyll::GetSibyllXSCode(particles::Code::SigmaPlus) == 1); + REQUIRE(process::sibyll::GetSibyllXSCode(particles::Code::PiMinus) == 2); + } + + SECTION("sibyll mass") { + + REQUIRE_FALSE(process::sibyll::GetSibyllMass(particles::Code::Electron) == 0_GeV); + } +} + +#include <corsika/geometry/Point.h> +#include <corsika/geometry/RootCoordinateSystem.h> +#include <corsika/geometry/Vector.h> + +#include <corsika/units/PhysicalUnits.h> + +#include <corsika/particles/ParticleProperties.h> +#include <corsika/setup/SetupStack.h> +#include <corsika/setup/SetupTrajectory.h> + +#include <corsika/environment/Environment.h> +#include <corsika/environment/HomogeneousMedium.h> +#include <corsika/environment/NuclearComposition.h> +#include <corsika/process/sibyll/sibyll2.3d.h> + +using namespace corsika::units::si; +using namespace corsika::units; + +TEST_CASE("SibyllInterface", "[processes]") { + + // setup environment, geometry + environment::Environment<environment::IMediumModel> env; + auto& universe = *(env.GetUniverse()); + + auto theMedium = + environment::Environment<environment::IMediumModel>::CreateNode<geometry::Sphere>( + geometry::Point{env.GetCoordinateSystem(), 0_m, 0_m, 0_m}, + 1_km * std::numeric_limits<double>::infinity()); + + using MyHomogeneousModel = environment::HomogeneousMedium<environment::IMediumModel>; + theMedium->SetModelProperties<MyHomogeneousModel>( + 1_kg / (1_m * 1_m * 1_m), + environment::NuclearComposition( + std::vector<particles::Code>{particles::Code::Oxygen}, std::vector<float>{1.})); + + auto const* nodePtr = theMedium.get(); + universe.AddChild(std::move(theMedium)); + + const geometry::CoordinateSystem& cs = env.GetCoordinateSystem(); + + random::RNGManager::GetInstance().RegisterRandomStream("s_rndm"); + + SECTION("InteractionInterface") { + + setup::Stack stack; + const HEPEnergyType E0 = 100_GeV; + HEPMomentumType P0 = + sqrt(E0 * E0 - particles::Proton::GetMass() * particles::Proton::GetMass()); + auto plab = corsika::stack::MomentumVector(cs, {0_GeV, 0_GeV, -P0}); + geometry::Point pos(cs, 0_m, 0_m, 0_m); + auto particle = stack.AddParticle( + std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{ + particles::Code::Proton, E0, plab, pos, 0_ns}); + particle.SetNode(nodePtr); + corsika::stack::SecondaryView view(particle); + auto projectile = view.GetProjectile(); + + Interaction model; + + model.Init(); + [[maybe_unused]] const process::EProcessReturn ret = model.DoInteraction(projectile); + [[maybe_unused]] const GrammageType length = model.GetInteractionLength(particle); + } + + SECTION("NuclearInteractionInterface") { + + setup::Stack stack; + const HEPEnergyType E0 = 400_GeV; + HEPMomentumType P0 = + sqrt(E0 * E0 - particles::Proton::GetMass() * particles::Proton::GetMass()); + auto plab = corsika::stack::MomentumVector(cs, {0_GeV, 0_GeV, -P0}); + geometry::Point pos(cs, 0_m, 0_m, 0_m); + + auto particle = + stack.AddParticle(std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, + units::si::TimeType, unsigned short, unsigned short>{ + particles::Code::Nucleus, E0, plab, pos, 0_ns, 4, 2}); + particle.SetNode(nodePtr); + corsika::stack::SecondaryView view(particle); + auto projectile = view.GetProjectile(); + + Interaction hmodel; + NuclearInteraction model(hmodel, env); + + model.Init(); + [[maybe_unused]] const process::EProcessReturn ret = model.DoInteraction(projectile); + [[maybe_unused]] const GrammageType length = model.GetInteractionLength(particle); + } + + SECTION("DecayInterface") { + + setup::Stack stack; + const HEPEnergyType E0 = 10_GeV; + HEPMomentumType P0 = + sqrt(E0 * E0 - particles::Proton::GetMass() * particles::Proton::GetMass()); + auto plab = corsika::stack::MomentumVector(cs, {0_GeV, 0_GeV, -P0}); + geometry::Point pos(cs, 0_m, 0_m, 0_m); + auto particle = stack.AddParticle( + std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{ + particles::Code::Lambda0, E0, plab, pos, 0_ns}); + corsika::stack::SecondaryView view(particle); + auto projectile = view.GetProjectile(); + + Decay model; + + model.PrintDecayConfig(); + + model.Init(); + + [[maybe_unused]] const TimeType time = model.GetLifetime(particle); + + /*[[maybe_unused]] const process::EProcessReturn ret =*/model.DoDecay(projectile); + + // run checks + // lambda decays into proton and pi- or neutron and pi+ + REQUIRE(stack.GetSize() == 3); + } + + SECTION("DecayConfiguration") { + + Decay model({particles::Code::PiPlus, particles::Code::PiMinus}); + REQUIRE(model.IsDecayHandled(particles::Code::PiPlus)); + REQUIRE(model.IsDecayHandled(particles::Code::PiMinus)); + REQUIRE_FALSE(model.IsDecayHandled(particles::Code::KPlus)); + + const std::vector<particles::Code> particleTestList = { + particles::Code::PiPlus, particles::Code::PiMinus, particles::Code::KPlus, + particles::Code::Lambda0Bar, particles::Code::D0Bar}; + + // setup decays + model.SetHandleDecay(particleTestList); + for (auto& pCode : particleTestList) REQUIRE(model.IsDecayHandled(pCode)); + + // individually + model.SetHandleDecay(particles::Code::KMinus); + + // possible decays + REQUIRE_FALSE(model.CanHandleDecay(particles::Code::Proton)); + REQUIRE_FALSE(model.CanHandleDecay(particles::Code::Electron)); + REQUIRE(model.CanHandleDecay(particles::Code::PiPlus)); + REQUIRE(model.CanHandleDecay(particles::Code::MuPlus)); + } +}