diff --git a/corsika/detail/framework/geometry/Plane.inl b/corsika/detail/framework/geometry/Plane.inl index f2170b48ea1cebd0076f13bee6fcc513c06ebf3f..d1a41c3eafebcdf6a8f62c15a6734030a39b1163 100644 --- a/corsika/detail/framework/geometry/Plane.inl +++ b/corsika/detail/framework/geometry/Plane.inl @@ -22,7 +22,7 @@ namespace corsika { return fNormal.dot(vP - fCenter) > corsika::units::si::LengthType::zero(); } - units::si::LengthType Plane::DistanceTo(geometry::Point const& vP) const + units::si::LengthType Plane::DistanceTo(corsika::Point const& vP) const { return (fNormal * (vP - fCenter).dot(fNormal)).norm(); } diff --git a/corsika/detail/media/Universe.inl b/corsika/detail/media/Universe.inl index d1f16c4e5f0773bf75192bc2cc520c99568ea8a5..c48d97b887c2b8a35c3c081612c0a329d928aa28 100644 --- a/corsika/detail/media/Universe.inl +++ b/corsika/detail/media/Universe.inl @@ -14,18 +14,12 @@ namespace corsika { - Universe::Universe(corsika::CoordinateSystem const& pCS): - corsika::Sphere( - corsika::Point{pCS, - 0 * corsika::units::si::meter, - 0 * corsika::units::si::meter, - 0 * corsika::units::si::meter}, - corsika::units::si::meter * std::numeric_limits<double>::infinity()) - {} + Universe::Universe(corsika::CoordinateSystem const& pCS) + : corsika::Sphere( + corsika::Point{pCS, 0 * corsika::units::si::meter, + 0 * corsika::units::si::meter, 0 * corsika::units::si::meter}, + corsika::units::si::meter * std::numeric_limits<double>::infinity()) {} - bool Universe::Contains(corsika::Point const&) const - { - return true; - } + bool Universe::Contains(corsika::Point const&) const { return true; } -} \ No newline at end of file +} // namespace corsika \ No newline at end of file diff --git a/corsika/detail/modules/null_model/NullModel.inl b/corsika/detail/modules/null_model/NullModel.inl new file mode 100644 index 0000000000000000000000000000000000000000..87115f4e5748ca959e915d46d67e1423abb0a96a --- /dev/null +++ b/corsika/detail/modules/null_model/NullModel.inl @@ -0,0 +1,36 @@ +/* + * (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. + */ + +#pragma once + +#include <corsika/modules/null_model/NullModel.hpp> +#include <corsika/setup/SetupStack.hpp> +#include <corsika/setup/SetupTrajectory.hpp> + +namespace corsika::null_model { + + void NullModel::Init() {} + + NullModel::NullModel(corsika::units::si::LengthType maxStepLength) + : fMaxStepLength(maxStepLength) {} + + template <> + corsika::EProcessReturn NullModel::DoContinuous(corsika::setup::Stack::ParticleType&, + corsika::setup::Trajectory&) const { + return corsika::EProcessReturn::eOk; + } + + template <> + units::si::LengthType NullModel::MaxStepLength(corsika::setup::Stack::ParticleType&, + corsika::setup::Trajectory&) const { + return fMaxStepLength; + } + +} // namespace corsika::null_model diff --git a/corsika/detail/modules/observation_plane/ObservationPlane.inl b/corsika/detail/modules/observation_plane/ObservationPlane.inl new file mode 100644 index 0000000000000000000000000000000000000000..87a7a0410fa70edd6bf68761d49550a20c2eae1b --- /dev/null +++ b/corsika/detail/modules/observation_plane/ObservationPlane.inl @@ -0,0 +1,68 @@ +/* + * (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/modules/observation_plane/ObservationPlane.hpp> + +#include <fstream> + +namespace corsika::observation_plane { + +ObservationPlane::ObservationPlane(corsika::Plane const& obsPlane, + std::string const& filename, bool deleteOnHit) + : plane_(obsPlane) + , outputStream_(filename) + , deleteOnHit_(deleteOnHit) { + outputStream_ << "#PDG code, energy / eV, distance to center / m" << std::endl; +} + +corsika::EProcessReturn ObservationPlane::DoContinuous( + corsika::setup::Stack::ParticleType const& particle, corsika::setup::Trajectory const& trajectory) { + using namespace corsika::units::si; + + TimeType const timeOfIntersection = + (plane_.GetCenter() - trajectory.GetR0()).dot(plane_.GetNormal()) / + trajectory.GetV0().dot(plane_.GetNormal()); + + if (timeOfIntersection < TimeType::zero()) { return corsika::EProcessReturn::eOk; } + + if (plane_.IsAbove(trajectory.GetR0()) == plane_.IsAbove(trajectory.GetPosition(1))) { + return corsika::EProcessReturn::eOk; + } + + outputStream_ << static_cast<int>(corsika::GetPDG(particle.GetPID())) << ' ' + << particle.GetEnergy() * (1 / 1_eV) << ' ' + << (trajectory.GetPosition(1) - plane_.GetCenter()).norm() / 1_m + << std::endl; + + if (deleteOnHit_) { + return corsika::EProcessReturn::eParticleAbsorbed; + } else { + return corsika::EProcessReturn::eOk; + } +} + +corsika::LengthType ObservationPlane::MaxStepLength(corsika::setup::Stack::ParticleType const&, + corsika::setup::Trajectory const& trajectory) { + + using namespace corsika::units::si; + +TimeType const timeOfIntersection = + (plane_.GetCenter() - trajectory.GetR0()).dot(plane_.GetNormal()) / + trajectory.GetV0().dot(plane_.GetNormal()); + + if (timeOfIntersection < TimeType::zero()) { + return std::numeric_limits<double>::infinity() * 1_m; + } + + auto const pointOfIntersection = trajectory.GetPosition(timeOfIntersection); + return (trajectory.GetR0() - pointOfIntersection).norm() * 1.0001; +} + +} \ No newline at end of file diff --git a/corsika/framework/geometry/Plane.hpp b/corsika/framework/geometry/Plane.hpp index 6a0d04f4c0c8e776b1ef6a8e324978897be5c0ae..fed4dab83983d44d94f940124893a2f8ef8b5df9 100644 --- a/corsika/framework/geometry/Plane.hpp +++ b/corsika/framework/geometry/Plane.hpp @@ -29,7 +29,7 @@ namespace corsika { bool IsAbove(Point const& vP) const ; - units::si::LengthType DistanceTo(geometry::Point const& vP) const ; + units::si::LengthType DistanceTo(corsika::Point const& vP) const ; Point const& GetCenter() const ; diff --git a/corsika/media/Universe.hpp b/corsika/media/Universe.hpp index 7241b794b05ed91f06595f39eb9cb27e70cdd2dd..fe61ba0feeb13530e5ccaca1b9666c36e8cc5103 100644 --- a/corsika/media/Universe.hpp +++ b/corsika/media/Universe.hpp @@ -17,7 +17,7 @@ namespace corsika { struct Universe : public corsika::Sphere { - Universe(corsika::CoordinateSystem const& pCS); + inline Universe(corsika::CoordinateSystem const& pCS); inline bool Contains(corsika::Point const&) const override; }; diff --git a/corsika/modules/null_model/NullModel.hpp b/corsika/modules/null_model/NullModel.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dd754b734a099008ae227d3f77d432c28ffbe879 --- /dev/null +++ b/corsika/modules/null_model/NullModel.hpp @@ -0,0 +1,36 @@ +/* + * (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. + */ + +#pragma once + +#include <corsika/framework/sequence/BaseProcess.hpp> +#include <corsika/framework/core/PhysicalUnits.hpp> + +namespace corsika::null_model { + + class NullModel : public corsika::BaseProcess<NullModel> { + corsika::units::si::LengthType const fMaxStepLength; + + public: + NullModel(corsika::units::si::LengthType maxStepLength = + corsika::units::si::meter * std::numeric_limits<double>::infinity()); + + void Init(); + + template <typename Particle, typename Track> + corsika::EProcessReturn DoContinuous(Particle&, Track&) const; + + template <typename Particle, typename Track> + corsika::units::si::LengthType MaxStepLength(Particle&, Track&) const; + }; + +} // namespace corsika::process::null_model + +#include <corsika/detail/modules/null_model/NullModel.inl> diff --git a/corsika/modules/observation_plane/ObservationPlane.hpp b/corsika/modules/observation_plane/ObservationPlane.hpp new file mode 100644 index 0000000000000000000000000000000000000000..dc3410647ec1ca8f8605542a5d562c9760becf03 --- /dev/null +++ b/corsika/modules/observation_plane/ObservationPlane.hpp @@ -0,0 +1,49 @@ +/* + * (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. + */ + +#pragma once + +#include <corsika/framework/geometry/Plane.hpp> +#include <corsika/framework/sequence/ContinuousProcess.hpp> +#include <corsika/setup/SetupStack.hpp> +#include <corsika/setup/SetupTrajectory.hpp> +#include <corsika/framework/core/PhysicalUnits.hpp> + +#include <fstream> + +namespace corsika::observation_plane { + + /** + * The ObservationPlane writes PDG codes, energies, and distances of particles to the + * central point of the plane into its output file. The particles are considered + * "absorbed" afterwards. + */ + class ObservationPlane : public corsika::ContinuousProcess<ObservationPlane> { + + public: + ObservationPlane(corsika::Plane const&, std::string const&, bool = true); + void Init() {} + + corsika::EProcessReturn DoContinuous( + corsika::setup::Stack::ParticleType const& vParticle, + corsika::setup::Trajectory const& vTrajectory); + + corsika::units::si::LengthType MaxStepLength( + corsika::setup::Stack::ParticleType const&, + corsika::setup::Trajectory const& vTrajectory); + + private: + corsika::Plane const plane_; + std::ofstream outputStream_; + bool const deleteOnHit_; + }; +} // namespace corsika::process::observation_plane + +#include <corsika/detail/modules/observation_plane/ObservationPlane.inl> diff --git a/corsika/modules/sibyll/Random.hpp b/corsika/modules/sibyll/Random.hpp index 3d82be78bc0100e40e41c867a04f3a48cc6c6eb0..8c0ddcf91523a0e392bc4a1c4e636ec6f271e05f 100644 --- a/corsika/modules/sibyll/Random.hpp +++ b/corsika/modules/sibyll/Random.hpp @@ -13,10 +13,9 @@ namespace sibyll { double rndm_interface() { static corsika::RNG& rng = - corsika::RNGManager::GetInstance().GetRandomStream("s_rndm"); + corsika::RNGManager::GetInstance().GetRandomStream("s_rndm"); std::uniform_real_distribution<double> dist; return dist(rng); } } - diff --git a/tests/modules/CMakeLists.txt b/tests/modules/CMakeLists.txt index f4c2fea50aeeea3e3e8d54bcb295f89a2287a19e..e4449b878d8affe9577cbc2723eba46304c74289 100644 --- a/tests/modules/CMakeLists.txt +++ b/tests/modules/CMakeLists.txt @@ -2,9 +2,18 @@ set (test_modules_sources TestMain.cpp testSibyll.cpp + testNullModel.cpp + testObservationPlane.cpp + #testParticleCut.cpp + #testPythia.cpp + #testQGSJetII.cpp + #testSibyll.cpp + #testStackInspector.cpp + #testSwitchProcess.cpp + #testTrackingLine.cpp + #testUrQMD.cpp ) - add_executable (testModules ${test_modules_sources}) target_link_libraries (testModules CORSIKA8 Sibyll_static Catch2) diff --git a/tests/modules/testNullModel.cpp b/tests/modules/testNullModel.cpp new file mode 100644 index 0000000000000000000000000000000000000000..349f193470f7dd96286413afd14fe4354a91872e --- /dev/null +++ b/tests/modules/testNullModel.cpp @@ -0,0 +1,57 @@ +/* + * (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/modules/null_model/NullModel.hpp> + +#include <corsika/framework/geometry/Point.hpp> +#include <corsika/framework/geometry/RootCoordinateSystem.hpp> +#include <corsika/framework/geometry/Vector.hpp> + +#include <corsika/framework/core/PhysicalUnits.hpp> + +#include <corsika/setup/SetupStack.hpp> +#include <corsika/setup/SetupTrajectory.hpp> + +using namespace corsika::units::si; +using namespace corsika::null_model; +using namespace corsika; + +TEST_CASE("NullModel", "[processes]") { + + auto const& dummyCS = + corsika::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem(); + corsika::Point const origin(dummyCS, {0_m, 0_m, 0_m}); + corsika::Vector<SpeedType::dimension_type> v(dummyCS, 0_m / second, + 0_m / second, 1_m / second); + corsika::Line line(origin, v); + corsika::Trajectory<corsika::Line> track(line, 10_s); + + setup::Stack stack; + setup::Stack::ParticleType particle = stack.AddParticle( + std::tuple<Code, units::si::HEPEnergyType, + corsika::MomentumVector, corsika::Point, units::si::TimeType>{ + Code::Electron, 100_GeV, + corsika::MomentumVector(dummyCS, {0_GeV, 0_GeV, -1_GeV}), + corsika::Point(dummyCS, {0_m, 0_m, 10_km}), 0_ns}); + + SECTION("interface") { + + NullModel model(10_m); + + model.Init(); + [[maybe_unused]] const EProcessReturn ret = + model.DoContinuous(particle, track); + LengthType const length = model.MaxStepLength(particle, track); + + CHECK((length / 10_m) == Approx(1)); + } +} diff --git a/tests/modules/testObservationPlane.cpp b/tests/modules/testObservationPlane.cpp new file mode 100644 index 0000000000000000000000000000000000000000..ba0733da78d7ae1c6042731b0b1c88746d71363e --- /dev/null +++ b/tests/modules/testObservationPlane.cpp @@ -0,0 +1,94 @@ +/* + * (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 <catch2/catch.hpp> + +#include <corsika/modules/observation_plane/ObservationPlane.hpp> + +#include <corsika/framework/geometry/Point.hpp> +#include <corsika/framework/geometry/RootCoordinateSystem.hpp> +#include <corsika/framework/geometry/Vector.hpp> + +#include <corsika/framework/core/ParticleProperties.hpp> +#include <corsika/framework/core/PhysicalUnits.hpp> + +using namespace corsika::units::si; +using namespace corsika::observation_plane; +using namespace corsika; + +TEST_CASE("ContinuousProcess interface", "[proccesses][observation_plane]") { + + auto const& rootCS = RootCoordinateSystem::GetInstance().GetRootCoordinateSystem(); + + /* + Test with downward going 1_GeV neutrino, starting at 0,1_m,10m + + ObservationPlane has origin at 0,0,0 + */ + + Point const start(rootCS, {0_m, 1_m, 10_m}); + Vector<units::si::SpeedType::dimension_type> vec(rootCS, 0_m / second, 0_m / second, + -units::constants::c); + Line line(start, vec); + Trajectory<Line> track(line, 12_m / units::constants::c); + + // setup particle stack, and add primary particle + setup::Stack stack; + stack.Clear(); + { + auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) { + return sqrt((Elab - m) * (Elab + m)); + }; + stack.AddParticle( + std::tuple<Code, units::si::HEPEnergyType, corsika::MomentumVector, Point, + units::si::TimeType>{ + Code::NuMu, 1_GeV, + corsika::MomentumVector( + rootCS, {0_GeV, 0_GeV, -elab2plab(1_GeV, NuMu::GetMass())}), + Point(rootCS, {1_m, 1_m, 10_m}), 0_ns}); + } + auto particle = stack.GetNextParticle(); + + SECTION("horizontal plane") { + + Plane const obsPlane(Point(rootCS, {0_m, 0_m, 0_m}), + Vector<dimensionless_d>(rootCS, {0., 0., 1.})); + ObservationPlane obs(obsPlane, "particles.dat", true); + + obs.Init(); + const LengthType length = obs.MaxStepLength(particle, track); + const EProcessReturn ret = obs.DoContinuous(particle, track); + + REQUIRE(length / 10_m == Approx(1).margin(1e-4)); + REQUIRE(ret == EProcessReturn::eParticleAbsorbed); + + /* + SECTION("horizontal plane") { + REQUIRE(true); // todo: we have to check content of output file... + + } + */ + } + + SECTION("inclined plane") {} + + SECTION("transparent plane") { + Plane const obsPlane(Point(rootCS, {0_m, 0_m, 0_m}), + Vector<dimensionless_d>(rootCS, {0., 0., 1.})); + ObservationPlane obs(obsPlane, "particles.dat", false); + + obs.Init(); + const LengthType length = obs.MaxStepLength(particle, track); + const EProcessReturn ret = obs.DoContinuous(particle, track); + + REQUIRE(length / 10_m == Approx(1).margin(1e-4)); + REQUIRE(ret == EProcessReturn::eOk); + } +} diff --git a/tests/modules/testParticleCut.cpp b/tests/modules/testParticleCut.cpp new file mode 100644 index 0000000000000000000000000000000000000000..2375600c8bacb5908950dbeab0efdd0b46c19b3f --- /dev/null +++ b/tests/modules/testParticleCut.cpp @@ -0,0 +1,107 @@ + +/* + * (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/particle_cut/ParticleCut.h> + +#include <corsika/environment/Environment.h> +#include <corsika/geometry/Point.h> +#include <corsika/geometry/RootCoordinateSystem.h> +#include <corsika/geometry/Vector.h> +#include <corsika/units/PhysicalUnits.h> +#include <corsika/utl/CorsikaFenv.h> + +#include <corsika/setup/SetupStack.h> + +#include <catch2/catch.hpp> + +using namespace corsika; +using namespace corsika::process::particle_cut; +using namespace corsika::units; +using namespace corsika::units::si; + +TEST_CASE("ParticleCut", "[processes]") { + feenableexcept(FE_INVALID); + using EnvType = environment::Environment<setup::IEnvironmentModel>; + EnvType env; + const geometry::CoordinateSystem& rootCS = env.GetCoordinateSystem(); + + // setup empty particle stack + setup::Stack stack; + stack.Clear(); + // two energies + const HEPEnergyType Eabove = 1_TeV; + const HEPEnergyType Ebelow = 10_GeV; + // list of arbitrary particles + std::vector<particles::Code> particleList = { + particles::Code::PiPlus, particles::Code::PiMinus, particles::Code::KPlus, + particles::Code::KMinus, particles::Code::K0Long, particles::Code::K0Short, + particles::Code::Electron, particles::Code::MuPlus, particles::Code::NuE, + particles::Code::Neutron}; + + SECTION("cut on particle type") { + + ParticleCut cut(20_GeV); + + // add primary particle to stack + auto particle = stack.AddParticle( + std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{ + particles::Code::Proton, Eabove, + corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), + geometry::Point(rootCS, 0_m, 0_m, 0_m), 0_ns}); + // view on secondary particles + corsika::stack::SecondaryView view(particle); + // ref. to primary particle through the secondary view. + // only this way the secondary view is populated + auto projectile = view.GetProjectile(); + // add secondaries, all with energies above the threshold + // only cut is by species + for (auto proType : particleList) + projectile.AddSecondary(std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, + units::si::TimeType>{ + proType, Eabove, corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), + geometry::Point(rootCS, 0_m, 0_m, 0_m), 0_ns}); + + cut.DoSecondaries(view); + + REQUIRE(view.GetSize() == 8); + } + + SECTION("cut low energy") { + ParticleCut cut(20_GeV); + + // add primary particle to stack + auto particle = stack.AddParticle( + std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{ + particles::Code::Proton, Eabove, + corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), + geometry::Point(rootCS, 0_m, 0_m, 0_m), 0_ns}); + // view on secondary particles + corsika::stack::SecondaryView view(particle); + // ref. to primary particle through the secondary view. + // only this way the secondary view is populated + auto projectile = view.GetProjectile(); + // add secondaries, all with energies below the threshold + // only cut is by species + for (auto proType : particleList) + projectile.AddSecondary(std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, + units::si::TimeType>{ + proType, Ebelow, corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, 0_GeV}), + geometry::Point(rootCS, 0_m, 0_m, 0_m), 0_ns}); + + cut.DoSecondaries(view); + + REQUIRE(view.GetSize() == 0); + } +} diff --git a/tests/modules/testPythia.cpp b/tests/modules/testPythia.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f229b907f92968d7c26ff7bed70b4476ce56282d --- /dev/null +++ b/tests/modules/testPythia.cpp @@ -0,0 +1,165 @@ +/* + * (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 <Pythia8/Pythia.h> +#include <corsika/process/pythia/Decay.h> +#include <corsika/process/pythia/Interaction.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> + +TEST_CASE("Pythia", "[processes]") { + + SECTION("linking pythia") { + using namespace Pythia8; + using std::cout; + using std::endl; + + // Generator. Process selection. LHC initialization. Histogram. + Pythia pythia; + + pythia.readString("Next:numberShowInfo = 0"); + pythia.readString("Next:numberShowProcess = 0"); + pythia.readString("Next:numberShowEvent = 0"); + + pythia.readString("ProcessLevel:all = off"); + + pythia.init(); + + Event& event = pythia.event; + event.reset(); + + pythia.particleData.mayDecay(321, true); + double pz = 100.; + double m = 0.49368; + event.append(321, 1, 0, 0, 0., 0., 100., sqrt(pz * pz + m * m), m); + + if (!pythia.next()) + cout << "decay failed!" << endl; + else + cout << "particles after decay: " << event.size() << endl; + event.list(); + + // loop over final state + for (int i = 0; i < pythia.event.size(); ++i) + if (pythia.event[i].isFinal()) { + cout << "particle: id=" << pythia.event[i].id() << endl; + } + } + + SECTION("pythia interface") { + using namespace corsika; + + const std::vector<particles::Code> particleList = { + particles::Code::PiPlus, particles::Code::PiMinus, particles::Code::KPlus, + particles::Code::KMinus, particles::Code::K0Long, particles::Code::K0Short}; + + random::RNGManager::GetInstance().RegisterRandomStream("pythia"); + + process::pythia::Decay model(particleList); + + model.Init(); + } +} + +#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> + +using namespace corsika; +using namespace corsika::units::si; + +TEST_CASE("pythia process") { + + // setup environment, geometry + environment::Environment<environment::IMediumModel> env; + + geometry::CoordinateSystem const& cs = env.GetCoordinateSystem(); + + auto theMedium = + environment::Environment<environment::IMediumModel>::CreateNode<geometry::Sphere>( + geometry::Point{cs, 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::Hydrogen}, + std::vector<float>{1.})); + + auto const* nodePtr = theMedium.get(); // save the medium for later use before moving it + + SECTION("pythia decay") { + + setup::Stack stack; + const HEPEnergyType E0 = 10_GeV; + HEPMomentumType P0 = + sqrt(E0 * E0 - particles::PiPlus::GetMass() * particles::PiPlus::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::PiPlus, E0, plab, pos, 0_ns}); + + const std::vector<particles::Code> particleList = { + particles::Code::PiPlus, particles::Code::PiMinus, particles::Code::KPlus, + particles::Code::KMinus, particles::Code::K0Long, particles::Code::K0Short}; + + random::RNGManager::GetInstance().RegisterRandomStream("pythia"); + + corsika::stack::SecondaryView view(particle); + auto projectile = view.GetProjectile(); + + process::pythia::Decay model(particleList); + model.Init(); + model.DoDecay(projectile); + [[maybe_unused]] const TimeType time = model.GetLifetime(particle); + } + + SECTION("pythia interaction") { + + setup::Stack stack; + const HEPEnergyType E0 = 100_GeV; + HEPMomentumType P0 = + sqrt(E0 * E0 - particles::PiPlus::GetMass() * particles::PiPlus::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::PiPlus, E0, plab, pos, 0_ns}); + particle.SetNode(nodePtr); + corsika::stack::SecondaryView view(particle); + auto projectile = view.GetProjectile(); + + process::pythia::Interaction model; + model.Init(); + [[maybe_unused]] const process::EProcessReturn ret = model.DoInteraction(projectile); + [[maybe_unused]] const GrammageType length = model.GetInteractionLength(particle); + } +} diff --git a/tests/modules/testQGSJetII.cpp b/tests/modules/testQGSJetII.cpp new file mode 100644 index 0000000000000000000000000000000000000000..4f284e056992a9987ae4cd70b4a5f471f5caca6a --- /dev/null +++ b/tests/modules/testQGSJetII.cpp @@ -0,0 +1,126 @@ +/* + * (c) Copyright 2020 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/qgsjetII/Interaction.h> +#include <corsika/process/qgsjetII/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::qgsjetII; + +TEST_CASE("QgsjetII", "[processes]") { + + SECTION("QgsjetII -> Corsika") { + REQUIRE(particles::PiPlus::GetCode() == process::qgsjetII::ConvertFromQgsjetII( + process::qgsjetII::QgsjetIICode::PiPlus)); + } + + SECTION("Corsika -> QgsjetII") { + REQUIRE(process::qgsjetII::ConvertToQgsjetII(particles::PiMinus::GetCode()) == + process::qgsjetII::QgsjetIICode::PiMinus); + REQUIRE(process::qgsjetII::ConvertToQgsjetIIRaw(particles::Proton::GetCode()) == 2); + } + + SECTION("canInteractInQgsjetII") { + + REQUIRE(process::qgsjetII::CanInteract(particles::Proton::GetCode())); + REQUIRE(process::qgsjetII::CanInteract(particles::Code::KPlus)); + REQUIRE(process::qgsjetII::CanInteract(particles::Nucleus::GetCode())); + // REQUIRE(process::qgsjetII::CanInteract(particles::Helium::GetCode())); + + REQUIRE_FALSE(process::qgsjetII::CanInteract(particles::EtaC::GetCode())); + REQUIRE_FALSE(process::qgsjetII::CanInteract(particles::SigmaC0::GetCode())); + } + + SECTION("cross-section type") { + + REQUIRE(process::qgsjetII::GetQgsjetIIXSCode(particles::Code::Neutron) == 2); + REQUIRE(process::qgsjetII::GetQgsjetIIXSCode(particles::Code::K0Long) == 3); + REQUIRE(process::qgsjetII::GetQgsjetIIXSCode(particles::Code::Proton) == 2); + REQUIRE(process::qgsjetII::GetQgsjetIIXSCode(particles::Code::PiMinus) == 1); + } +} + +#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/qgsjetII/qgsjet-II-04.h> + +using namespace corsika::units::si; +using namespace corsika::units; + +TEST_CASE("QgsjetIIInterface", "[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("qgran"); + + 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, unsigned int, unsigned int>{ + particles::Code::Nucleus, E0, plab, pos, 0_ns, 16, 8}); + // corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{ + // particles::Code::PiPlus, 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); + } +} diff --git a/tests/modules/testStackInspector.cpp b/tests/modules/testStackInspector.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1371f8bfaf48bd8a582cfca0abca152510b515d1 --- /dev/null +++ b/tests/modules/testStackInspector.cpp @@ -0,0 +1,55 @@ +/* + * (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/process/stack_inspector/StackInspector.h> + +#include <corsika/geometry/Point.h> +#include <corsika/geometry/RootCoordinateSystem.h> +#include <corsika/geometry/Vector.h> + +#include <corsika/units/PhysicalUnits.h> + +#include <corsika/cascade/testCascade.h> + +using namespace corsika::units::si; +using namespace corsika::process::stack_inspector; +using namespace corsika; +using namespace corsika::geometry; + +TEST_CASE("StackInspector", "[processes]") { + + auto const& rootCS = + geometry::RootCoordinateSystem::GetInstance().GetRootCoordinateSystem(); + geometry::Point const origin(rootCS, {0_m, 0_m, 0_m}); + geometry::Vector<units::si::SpeedType::dimension_type> v(rootCS, 0_m / second, + 0_m / second, 1_m / second); + geometry::Line line(origin, v); + geometry::Trajectory<geometry::Line> track(line, 10_s); + + TestCascadeStack stack; + stack.Clear(); + HEPEnergyType E0 = 100_GeV; + stack.AddParticle( + std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{ + particles::Code::Electron, E0, + corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, -1_GeV}), + Point(rootCS, {0_m, 0_m, 10_km}), 0_ns}); + + SECTION("interface") { + + StackInspector<TestCascadeStack> model(1, true, E0); + + model.Init(); + [[maybe_unused]] const process::EProcessReturn ret = model.DoStack(stack); + } +} diff --git a/tests/modules/testSwitchProcess.cpp b/tests/modules/testSwitchProcess.cpp new file mode 100644 index 0000000000000000000000000000000000000000..510c731ff633331b8996326d3c940c68025b4796 --- /dev/null +++ b/tests/modules/testSwitchProcess.cpp @@ -0,0 +1,264 @@ +/* + * (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/switch_process/SwitchProcess.h> +#include <corsika/stack/SecondaryView.h> +#include <corsika/stack/Stack.h> +#include <corsika/units/PhysicalUnits.h> + +#include <catch2/catch.hpp> + +#include <algorithm> +#include <random> + +using namespace corsika; +using namespace corsika::process; +using namespace corsika::units::si; + +class TestStackData { + +public: + // these functions are needed for the Stack interface + void Init() {} + void Clear() { fData.clear(); } + unsigned int GetSize() const { return fData.size(); } + unsigned int GetCapacity() const { return fData.size(); } + void Copy(int i1, int i2) { fData[i2] = fData[i1]; } + void Swap(int i1, int i2) { std::swap(fData[i1], fData[i2]); } + + // custom data access function + void SetData(unsigned int i, HEPEnergyType v) { fData[i] = v; } + HEPEnergyType GetData(const unsigned int i) const { return fData[i]; } + + // these functions are also needed by the Stack interface + void IncrementSize() { fData.resize(fData.size() + 1); } + void DecrementSize() { + if (fData.size() > 0) { fData.pop_back(); } + } + + // custom private data section +private: + std::vector<HEPEnergyType> fData; +}; + +/** + * From static_cast of a StackIterator over entries in the + * TestStackData class you get and object of type + * TestParticleInterface defined here + * + * It provides Get/Set methods to read and write data to the "Data" + * storage of TestStackData obtained via + * "StackIteratorInterface::GetStackData()", given the index of the + * iterator "StackIteratorInterface::GetIndex()" + * + */ +template <typename StackIteratorInterface> +class TestParticleInterface + : public corsika::stack::ParticleBase<StackIteratorInterface> { + +public: + using corsika::stack::ParticleBase<StackIteratorInterface>::GetStackData; + using corsika::stack::ParticleBase<StackIteratorInterface>::GetIndex; + + /* + The SetParticleData methods are called for creating new entries + on the stack. You can specifiy various parametric versions to + perform this task: + */ + + // default version for particle-creation from input data + void SetParticleData(const std::tuple<HEPEnergyType> v) { SetEnergy(std::get<0>(v)); } + void SetParticleData(TestParticleInterface<StackIteratorInterface>& /*parent*/, + std::tuple<HEPEnergyType> v) { + SetEnergy(std::get<0>(v)); + } + + // here are the fundamental methods for access to TestStackData data + void SetEnergy(HEPEnergyType v) { GetStackData().SetData(GetIndex(), v); } + HEPEnergyType GetEnergy() const { return GetStackData().GetData(GetIndex()); } +}; + +using SimpleStack = corsika::stack::Stack<TestStackData, TestParticleInterface>; + +// see issue 161 +#if defined(__clang__) +using StackTestView = corsika::stack::SecondaryView<TestStackData, TestParticleInterface>; +#elif defined(__GNUC__) || defined(__GNUG__) +using StackTestView = corsika::stack::MakeView<SimpleStack>::type; +#endif + +auto constexpr kgMSq = 1_kg / (1_m * 1_m); + +template <int N> +struct DummyProcess : InteractionProcess<DummyProcess<N>> { + void Init() {} + + template <typename TParticle> + corsika::units::si::GrammageType GetInteractionLength(TParticle const&) const { + return N * kgMSq; + } + + template <typename TSecondaries> + corsika::process::EProcessReturn DoInteraction(TSecondaries& vSec) { + // to figure out which process was selected in the end, we produce N + // secondaries for DummyProcess<N> + + for (int i = 0; i < N; ++i) { + vSec.AddSecondary(std::tuple<HEPEnergyType>{vSec.GetEnergy() / N}); + } + + return EProcessReturn::eOk; + } +}; + +using DummyLowEnergyProcess = DummyProcess<1>; +using DummyHighEnergyProcess = DummyProcess<2>; +using DummyAdditionalProcess = DummyProcess<3>; + +TEST_CASE("SwitchProcess from InteractionProcess") { + DummyLowEnergyProcess low; + DummyHighEnergyProcess high; + DummyAdditionalProcess proc; + + switch_process::SwitchProcess switchProcess(low, high, 1_TeV); + auto seq = switchProcess << proc; + + SimpleStack stack; + + SECTION("low energy") { + stack.AddParticle(std::tuple<HEPEnergyType>{0.5_TeV}); + auto p = stack.GetNextParticle(); + + // low energy process returns 1 kg/m² + SECTION("interaction length") { + REQUIRE(switchProcess.GetInteractionLength(p) / kgMSq == Approx(1)); + REQUIRE(seq.GetTotalInteractionLength(p) / kgMSq == Approx(3. / 4)); + } + + // low energy process creates 1 secondary + //~ SECTION("SelectInteraction") { + //~ typename SimpleStack::ParticleType theParticle = + //~ stack.GetNextParticle(); // as in corsika::Cascade + //~ StackTestView view(theParticle); + //~ auto projectile = view.GetProjectile(); + + //~ InverseGrammageType invLambda = 0 / kgMSq; + //~ switchProcess.SelectInteraction(p, projectile, 0.01 / kgMSq, invLambda); + + //~ REQUIRE(view.GetSize() == 1); + //~ } + } + + SECTION("high energy") { + stack.AddParticle(std::tuple<HEPEnergyType>{4_TeV}); + auto p = stack.GetNextParticle(); + + // high energy process returns 2 kg/m² + SECTION("interaction length") { + REQUIRE(switchProcess.GetInteractionLength(p) / kgMSq == Approx(2)); + REQUIRE(seq.GetTotalInteractionLength(p) / kgMSq == Approx(6. / 5)); + } + + // high energy process creates 2 secondaries + SECTION("SelectInteraction") { + typename SimpleStack::ParticleType theParticle = + stack.GetNextParticle(); // as in corsika::Cascade + StackTestView view(theParticle); + auto projectile = view.GetProjectile(); + + InverseGrammageType invLambda = 0 / kgMSq; + switchProcess.SelectInteraction(p, projectile, 0.01 / kgMSq, invLambda); + + REQUIRE(view.GetSize() == 2); + } + } +} + +TEST_CASE("SwitchProcess from ProcessSequence") { + DummyProcess<1> innerA; + DummyProcess<2> innerB; + DummyProcess<3> outer; + DummyProcess<4> additional; + + auto seq = innerA << innerB; + + switch_process::SwitchProcess switchProcess(seq, outer, 1_TeV); + auto completeSeq = switchProcess << additional; + + SimpleStack stack; + + SECTION("low energy") { + stack.AddParticle(std::tuple<HEPEnergyType>{0.5_TeV}); + auto p = stack.GetNextParticle(); + + SECTION("interaction length") { + REQUIRE(switchProcess.GetInteractionLength(p) / kgMSq == Approx(2. / 3)); + REQUIRE(completeSeq.GetTotalInteractionLength(p) / kgMSq == Approx(4. / 7)); + } + + SECTION("SelectInteraction") { + std::vector<int> numberOfSecondaries; + + for (int i = 0; i < 1000; ++i) { + typename SimpleStack::ParticleType theParticle = + stack.GetNextParticle(); // as in corsika::Cascade + StackTestView view(theParticle); + auto projectile = view.GetProjectile(); + + double r = i / 1000.; + InverseGrammageType invLambda = r * 7. / 4 / kgMSq; + + InverseGrammageType accumulator = 0 / kgMSq; + completeSeq.SelectInteraction(p, projectile, invLambda, accumulator); + + numberOfSecondaries.push_back(view.GetSize()); + } + + auto const mean = + std::accumulate(numberOfSecondaries.cbegin(), numberOfSecondaries.cend(), 0.) / + numberOfSecondaries.size(); + REQUIRE(mean == Approx(12. / 7.).margin(0.01)); + } + } + + SECTION("high energy") { + stack.AddParticle(std::tuple<HEPEnergyType>{3.0_TeV}); + auto p = stack.GetNextParticle(); + + SECTION("interaction length") { + REQUIRE(switchProcess.GetInteractionLength(p) / kgMSq == Approx(3)); + REQUIRE(completeSeq.GetTotalInteractionLength(p) / kgMSq == Approx(12. / 7.)); + } + + SECTION("SelectInteraction") { + std::vector<int> numberOfSecondaries; + + for (int i = 0; i < 1000; ++i) { + typename SimpleStack::ParticleType theParticle = + stack.GetNextParticle(); // as in corsika::Cascade + StackTestView view(theParticle); + auto projectile = view.GetProjectile(); + + double r = i / 1000.; + InverseGrammageType invLambda = r * 7. / 12. / kgMSq; + + InverseGrammageType accumulator = 0 / kgMSq; + completeSeq.SelectInteraction(p, projectile, invLambda, accumulator); + + numberOfSecondaries.push_back(view.GetSize()); + } + + auto const mean = + std::accumulate(numberOfSecondaries.cbegin(), numberOfSecondaries.cend(), 0.) / + numberOfSecondaries.size(); + REQUIRE(mean == Approx(24. / 7.).margin(0.01)); + } + } +} diff --git a/tests/modules/testTrackingLine.cpp b/tests/modules/testTrackingLine.cpp new file mode 100644 index 0000000000000000000000000000000000000000..638e43bc7f83f8f45d1446b75dbfdb1d5092dee2 --- /dev/null +++ b/tests/modules/testTrackingLine.cpp @@ -0,0 +1,100 @@ +/* + * (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/tracking_line/TrackingLine.h> +#include <testTrackingLineStack.h> // test-build, and include file is obtained from CMAKE_CURRENT_SOURCE_DIR + +#include <corsika/environment/Environment.h> +#include <corsika/particles/ParticleProperties.h> + +#include <corsika/geometry/Point.h> +#include <corsika/geometry/Sphere.h> +#include <corsika/geometry/Vector.h> + +#include <corsika/setup/SetupTrajectory.h> +using corsika::setup::Trajectory; + +#include <catch2/catch.hpp> + +using namespace corsika; +using namespace corsika::process; +using namespace corsika::units; +using namespace corsika::geometry; + +#include <iostream> +using namespace std; +using namespace corsika::units::si; + +TEST_CASE("TrackingLine") { + environment::Environment<environment::Empty> env; // dummy environment + auto const& cs = env.GetCoordinateSystem(); + + tracking_line::TrackingLine tracking; + + SECTION("intersection with sphere") { + Point const origin(cs, {0_m, 0_m, -5_m}); + Point const center(cs, {0_m, 0_m, 10_m}); + Sphere const sphere(center, 1_m); + Vector<corsika::units::si::SpeedType::dimension_type> v(cs, 0_m / second, + 0_m / second, 1_m / second); + Line line(origin, v); + + setup::Trajectory traj(line, 12345_s); + + auto const opt = + tracking_line::TimeOfIntersection(traj, Sphere(Point(cs, {0_m, 0_m, 10_m}), 1_m)); + REQUIRE(opt.has_value()); + + auto [t1, t2] = opt.value(); + REQUIRE(t1 / 14_s == Approx(1)); + REQUIRE(t2 / 16_s == Approx(1)); + + auto const optNoIntersection = + tracking_line::TimeOfIntersection(traj, Sphere(Point(cs, {5_m, 0_m, 10_m}), 1_m)); + REQUIRE_FALSE(optNoIntersection.has_value()); + } + + SECTION("maximally possible propagation") { + auto& universe = *(env.GetUniverse()); + + auto const radius = 20_m; + + auto theMedium = environment::Environment<environment::Empty>::CreateNode<Sphere>( + Point{env.GetCoordinateSystem(), 0_m, 0_m, 0_m}, radius); + auto const* theMediumPtr = theMedium.get(); + universe.AddChild(std::move(theMedium)); + + TestTrackingLineStack stack; + stack.AddParticle( + std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{ + particles::Code::MuPlus, + 1_GeV, + {cs, {0_GeV, 0_GeV, 1_GeV}}, + {cs, {0_m, 0_m, 0_km}}, + 0_ns}); + auto p = stack.GetNextParticle(); + p.SetNode(theMediumPtr); + + Point const origin(cs, {0_m, 0_m, 0_m}); + Vector<corsika::units::si::SpeedType::dimension_type> v(cs, 0_m / second, + 0_m / second, 1_m / second); + Line line(origin, v); + + auto const [traj, geomMaxLength, nextVol] = tracking.GetTrack(p); + [[maybe_unused]] auto dummy_geomMaxLength = geomMaxLength; + [[maybe_unused]] auto dummy_nextVol = nextVol; + + REQUIRE((traj.GetPosition(1.) - Point(cs, 0_m, 0_m, radius)) + .GetComponents(cs) + .norm() + .magnitude() == Approx(0).margin(1e-4)); + } +} diff --git a/tests/modules/testUrQMD.cpp b/tests/modules/testUrQMD.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3339fb9d32cc84939a3d9c4b0bbb6dbc031c4012 --- /dev/null +++ b/tests/modules/testUrQMD.cpp @@ -0,0 +1,227 @@ +/* + * (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/urqmd/UrQMD.h> +#include <corsika/random/RNGManager.h> + +#include <corsika/geometry/Point.h> +#include <corsika/geometry/RootCoordinateSystem.h> +#include <corsika/geometry/Vector.h> + +#include <corsika/units/PhysicalConstants.h> +#include <corsika/units/PhysicalUnits.h> + +#include <corsika/utl/CorsikaFenv.h> + +#include <corsika/particles/ParticleProperties.h> +#include <corsika/setup/SetupStack.h> +#include <corsika/setup/SetupTrajectory.h> + +#include <corsika/environment/Environment.h> +#include <corsika/environment/HomogeneousMedium.h> +#include <corsika/environment/NuclearComposition.h> + +#include <tuple> +#include <utility> + +#include <catch2/catch.hpp> + +using namespace corsika; +using namespace corsika::process::UrQMD; +using namespace corsika::units::si; + +template <typename TStackView> +auto sumCharge(TStackView const& view) { + int totalCharge = 0; + + for (auto const& p : view) { totalCharge += particles::GetChargeNumber(p.GetPID()); } + + return totalCharge; +} + +template <typename TStackView> +auto sumMomentum(TStackView const& view, geometry::CoordinateSystem const& vCS) { + geometry::Vector<hepenergy_d> sum{vCS, 0_eV, 0_eV, 0_eV}; + + for (auto const& p : view) { sum += p.GetMomentum(); } + + return sum; +} + +auto setupEnvironment(particles::Code vTargetCode) { + // setup environment, geometry + auto env = std::make_unique<environment::Environment<environment::IMediumModel>>(); + auto& universe = *(env->GetUniverse()); + const geometry::CoordinateSystem& cs = env->GetCoordinateSystem(); + + auto theMedium = + environment::Environment<environment::IMediumModel>::CreateNode<geometry::Sphere>( + geometry::Point{cs, 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>{vTargetCode}, + std::vector<float>{1.})); + + auto const* nodePtr = theMedium.get(); + universe.AddChild(std::move(theMedium)); + + return std::make_tuple(std::move(env), &cs, nodePtr); +} + +template <typename TNodeType> +auto setupStack(int vA, int vZ, HEPEnergyType vMomentum, TNodeType* vNodePtr, + geometry::CoordinateSystem const& cs) { + auto stack = std::make_unique<setup::Stack>(); + auto constexpr mN = corsika::units::constants::nucleonMass; + + geometry::Point const origin(cs, {0_m, 0_m, 0_m}); + corsika::stack::MomentumVector const pLab(cs, {vMomentum, 0_GeV, 0_GeV}); + + HEPEnergyType const E0 = + sqrt(units::si::detail::static_pow<2>(mN * vA) + pLab.squaredNorm()); + auto particle = + stack->AddParticle(std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, + units::si::TimeType, unsigned short, unsigned short>{ + particles::Code::Nucleus, E0, pLab, origin, 0_ns, vA, vZ}); + + particle.SetNode(vNodePtr); + return std::make_tuple( + std::move(stack), + std::make_unique<decltype(corsika::stack::SecondaryView(particle))>(particle)); +} + +template <typename TNodeType> +auto setupStack(particles::Code vProjectileType, HEPEnergyType vMomentum, + TNodeType* vNodePtr, geometry::CoordinateSystem const& cs) { + auto stack = std::make_unique<setup::Stack>(); + + geometry::Point const origin(cs, {0_m, 0_m, 0_m}); + corsika::stack::MomentumVector const pLab(cs, {vMomentum, 0_GeV, 0_GeV}); + + HEPEnergyType const E0 = + sqrt(units::si::detail::static_pow<2>(particles::GetMass(vProjectileType)) + + pLab.squaredNorm()); + auto particle = stack->AddParticle( + std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{ + vProjectileType, E0, pLab, origin, 0_ns}); + + particle.SetNode(vNodePtr); + return std::make_tuple( + std::move(stack), + std::make_unique<decltype(corsika::stack::SecondaryView(particle))>(particle)); +} + +TEST_CASE("UrQMD") { + SECTION("conversion") { + REQUIRE_THROWS(process::UrQMD::ConvertFromUrQMD(106, 0)); + REQUIRE(process::UrQMD::ConvertFromUrQMD(101, 0) == particles::Code::Pi0); + REQUIRE(process::UrQMD::ConvertToUrQMD(particles::Code::PiPlus) == + std::make_pair<int, int>(101, 2)); + } + + feenableexcept(FE_INVALID); + corsika::random::RNGManager::GetInstance().RegisterRandomStream("UrQMD"); + UrQMD urqmd; + + SECTION("cross sections") { + auto [env, csPtr, nodePtr] = setupEnvironment(particles::Code::Unknown); + auto const& cs = *csPtr; + + particles::Code validProjectileCodes[] = { + particles::Code::PiPlus, particles::Code::PiMinus, particles::Code::Proton, + particles::Code::Neutron, particles::Code::KPlus, particles::Code::KMinus, + particles::Code::K0, particles::Code::K0Bar, particles::Code::K0Long}; + + for (auto code : validProjectileCodes) { + auto [stack, view] = setupStack(code, 100_GeV, nodePtr, cs); + REQUIRE(stack->GetSize() == 1); + + // simple check whether the cross-section is non-vanishing + REQUIRE(urqmd.GetCrossSection(view->GetProjectile(), particles::Code::Proton) / + 1_mb > + 0); + REQUIRE(urqmd.GetCrossSection(view->GetProjectile(), particles::Code::Nitrogen) / + 1_mb > + 0); + REQUIRE(urqmd.GetCrossSection(view->GetProjectile(), particles::Code::Oxygen) / + 1_mb > + 0); + REQUIRE(urqmd.GetCrossSection(view->GetProjectile(), particles::Code::Argon) / + 1_mb > + 0); + } + } + + SECTION("nucleon projectile") { + auto [env, csPtr, nodePtr] = setupEnvironment(particles::Code::Oxygen); + unsigned short constexpr A = 14, Z = 7; + auto [stackPtr, secViewPtr] = setupStack(A, Z, 400_GeV, nodePtr, *csPtr); + + // must be assigned to variable, cannot be used as rvalue?! + auto projectile = secViewPtr->GetProjectile(); + auto const projectileMomentum = projectile.GetMomentum(); + [[maybe_unused]] process::EProcessReturn const ret = urqmd.DoInteraction(projectile); + + REQUIRE(sumCharge(*secViewPtr) == + Z + particles::GetChargeNumber(particles::Code::Oxygen)); + + auto const secMomSum = + sumMomentum(*secViewPtr, projectileMomentum.GetCoordinateSystem()); + REQUIRE((secMomSum - projectileMomentum).norm() / projectileMomentum.norm() == + Approx(0).margin(1e-2)); + } + + SECTION("\"special\" projectile") { + auto [env, csPtr, nodePtr] = setupEnvironment(particles::Code::Oxygen); + auto [stackPtr, secViewPtr] = + setupStack(particles::Code::PiPlus, 400_GeV, nodePtr, *csPtr); + + // must be assigned to variable, cannot be used as rvalue?! + auto projectile = secViewPtr->GetProjectile(); + auto const projectileMomentum = projectile.GetMomentum(); + + [[maybe_unused]] process::EProcessReturn const ret = urqmd.DoInteraction(projectile); + + REQUIRE(sumCharge(*secViewPtr) == + particles::GetChargeNumber(particles::Code::PiPlus) + + particles::GetChargeNumber(particles::Code::Oxygen)); + + auto const secMomSum = + sumMomentum(*secViewPtr, projectileMomentum.GetCoordinateSystem()); + REQUIRE((secMomSum - projectileMomentum).norm() / projectileMomentum.norm() == + Approx(0).margin(1e-2)); + } + + SECTION("K0Long projectile") { + auto [env, csPtr, nodePtr] = setupEnvironment(particles::Code::Oxygen); + auto [stackPtr, secViewPtr] = + setupStack(particles::Code::K0Long, 400_GeV, nodePtr, *csPtr); + + // must be assigned to variable, cannot be used as rvalue?! + auto projectile = secViewPtr->GetProjectile(); + auto const projectileMomentum = projectile.GetMomentum(); + + [[maybe_unused]] process::EProcessReturn const ret = urqmd.DoInteraction(projectile); + + REQUIRE(sumCharge(*secViewPtr) == + particles::GetChargeNumber(particles::Code::K0Long) + + particles::GetChargeNumber(particles::Code::Oxygen)); + + auto const secMomSum = + sumMomentum(*secViewPtr, projectileMomentum.GetCoordinateSystem()); + REQUIRE((secMomSum - projectileMomentum).norm() / projectileMomentum.norm() == + Approx(0).margin(1e-2)); + } +}