diff --git a/corsika/detail/modules/qgsjetII/Interaction.inl b/corsika/detail/modules/qgsjetII/Interaction.inl index 828b9fcfeba8ae193ac622249a74ef317eaacc13..217fe30b72c3fa27d1ad3794a4a6b1ec8852ffd5 100644 --- a/corsika/detail/modules/qgsjetII/Interaction.inl +++ b/corsika/detail/modules/qgsjetII/Interaction.inl @@ -239,10 +239,14 @@ namespace corsika::qgsjetII { if (is_nucleus(targetCode)) { // nucleus targetMassNumber = get_nucleus_A(targetCode); if (targetMassNumber > maxMassNumber_) - throw std::runtime_error("QgsjetII target mass outside range."); + throw std::runtime_error( + "QgsjetII target mass outside range."); // LCOV_EXCL_LINE there is no + // allowed path here } else { - if (targetCode != Proton::code) - throw std::runtime_error("QgsjetII Taget not possible."); + if (targetCode != Proton::code) // LCOV_EXCL_LINE there is no allowed path here + throw std::runtime_error( + "QgsjetII Taget not possible."); // LCOV_EXCL_LINE there is no allowed path + // here } CORSIKA_LOG_DEBUG("Interaction: target qgsjetII code/A: {}", targetMassNumber); @@ -252,7 +256,9 @@ namespace corsika::qgsjetII { if (qgsjet_hadron_type == QgsjetIIHadronType::NucleusType) { projectileMassNumber = projectile.getNuclearA(); if (projectileMassNumber > maxMassNumber_) - throw std::runtime_error("QgsjetII projectile mass outside range."); + throw std::runtime_error( + "QgsjetII projectile mass outside range."); // LCOV_EXCL_LINE there is no + // allowed path here std::array<QgsjetIIHadronType, 2> constexpr nucleons = { QgsjetIIHadronType::ProtonType, QgsjetIIHadronType::NeutronType}; std::uniform_int_distribution select(0, 1); diff --git a/corsika/detail/modules/urqmd/UrQMD.inl b/corsika/detail/modules/urqmd/UrQMD.inl index e85a6abc302824e80f1188e57d7fda4cd56216a8..ee29642e00bbd408c355a5c2c645abef312564ec 100644 --- a/corsika/detail/modules/urqmd/UrQMD.inl +++ b/corsika/detail/modules/urqmd/UrQMD.inl @@ -86,9 +86,9 @@ namespace corsika::urqmd { case Code::K0Bar: projectileIndex = 8; break; - default: { + default: { // LCOV_EXCL_START since this can never happen due to canInteract CORSIKA_LOG_WARN("UrQMD cross-section not tabulated for {}", projectileCode); - return CrossSectionType::zero(); + return CrossSectionType::zero(); // LCOV_EXCL_STOP } } @@ -400,7 +400,9 @@ namespace corsika::urqmd { boost::filesystem::ifstream file(filename, std::ios::in); if (!file.is_open()) { - throw std::runtime_error(filename.native() + " could not be opened."); + throw std::runtime_error( + filename.native() + + " could not be opened."); // LCOV_EXCL_LINE since this is pointless to test } std::string line; diff --git a/corsika/framework/utility/COMBoost.hpp b/corsika/framework/utility/COMBoost.hpp index befd3025035105a184f33c1c2a872c750364665e..43a123cf18fa62fe62baded114b319525b856d55 100644 --- a/corsika/framework/utility/COMBoost.hpp +++ b/corsika/framework/utility/COMBoost.hpp @@ -19,14 +19,36 @@ namespace corsika { /** + @defgroup Utilities + + Collection of classes and methods to perform recurring tasks. + **/ + + /** + @class + @ingroup Utilities + This utility class handles Lorentz boost between different referenence frames, using FourVector. + + The class is initialized with projectile and optionally target + energy/momentum data. During initialization, a rotation matrix is + calculated to represent the projectile movement (and thus the + boost) along the z-axis. Also the inverse of this rotation is + calculated. The Lorentz boost matrix and its inverse are + determined as 2x2 matrices considering the energy and + pz-momentum. + + Different constructors are offered with different specialization + for the cases of collisions (projectile-target) or just decays + (projectile only). */ class COMBoost { public: - //! construct a COMBoost given four-vector of projectile and mass of target + //! construct a COMBoost given four-vector of projectile and mass of target (target at + //! rest) COMBoost(FourVector<HEPEnergyType, MomentumVector> const& Pprojectile, HEPEnergyType const massTarget); @@ -41,9 +63,11 @@ namespace corsika { template <typename FourVector> FourVector fromCoM(FourVector const& p) const; + //! returns the rotated coordinate system CoordinateSystemPtr getRotatedCS() const; protected: + //! internal method void setBoost(double coshEta, double sinhEta); private: diff --git a/corsika/framework/utility/CorsikaData.hpp b/corsika/framework/utility/CorsikaData.hpp index 9f24d5036a34f96ab10a3422c96b735b6338f0df..2ab2dec7dabcf7a81bcbe976d689bd92f72df1b3 100644 --- a/corsika/framework/utility/CorsikaData.hpp +++ b/corsika/framework/utility/CorsikaData.hpp @@ -12,9 +12,14 @@ namespace corsika { /** + * @file CorsikaData.hpp + * @ingroup Utilities + * @{ * returns the full path of the file \p filename within the CORSIKA_DATA directory */ boost::filesystem::path corsika_data(boost::filesystem::path const& filename); + + //! @} } // namespace corsika #include <corsika/detail/framework/utility/CorsikaData.inl> diff --git a/corsika/framework/utility/QuarticSolver.hpp b/corsika/framework/utility/QuarticSolver.hpp index 0bcdfcedf1a9a9055306acacfd756012726cf8b0..ad9484939d10691e38c7e74d9e2632b3d2e60463 100644 --- a/corsika/framework/utility/QuarticSolver.hpp +++ b/corsika/framework/utility/QuarticSolver.hpp @@ -11,6 +11,9 @@ #include <complex> /** + * @file QuarticSolver.hpp + * @ingroup Utilities + * @{ * \todo convert to class */ @@ -56,6 +59,8 @@ namespace corsika::quartic_solver { // afterwards. DComplex* solve_quartic(double a, double b, double c, double d); + //! @} + } // namespace corsika::quartic_solver #include <corsika/detail/framework/utility/QuarticSolver.inl> diff --git a/corsika/framework/utility/SaveBoostHistogram.hpp b/corsika/framework/utility/SaveBoostHistogram.hpp index 14e0379966c447cd0c2f8cfa7ccffcf3c29a0478..30cbdda2a6c9e95ce63dbfbaf7c7a120d4bbe727 100644 --- a/corsika/framework/utility/SaveBoostHistogram.hpp +++ b/corsika/framework/utility/SaveBoostHistogram.hpp @@ -13,6 +13,8 @@ namespace corsika { /** + * @ingroup Utilities + * * This functions saves a boost::histogram into a numpy file. Only rather basic axis * types are supported: regular, variable, integer, category<int>. Only "ordinary" bin * counts (i.e. a double or int) are supported, nothing fancy like profiles. diff --git a/documentation/index.rst b/documentation/index.rst index c1be80a37ee4a481cd3c67638772b11309c5a5d3..79440c503bd0b500d8235d5c1edd77c2bf459822 100644 --- a/documentation/index.rst +++ b/documentation/index.rst @@ -13,6 +13,7 @@ Welcome to the CORSIKA 8 air shower simulation framework. units environment stack + utilities api diff --git a/documentation/utilities.rst b/documentation/utilities.rst new file mode 100644 index 0000000000000000000000000000000000000000..37c289fde6a68f36548ff2fcdab0ce0dbda2114b --- /dev/null +++ b/documentation/utilities.rst @@ -0,0 +1,9 @@ +Utilities +========== + +.. doxygengroup:: Utilities + :project: CORSIKA8 + :members: + + + diff --git a/tests/modules/testQGSJetII.cpp b/tests/modules/testQGSJetII.cpp index 477dedc6ca15a123b26f5524cbeb888fb2700cd7..4a604fd8bbf32f440db2eb0e073aed684b412fe4 100644 --- a/tests/modules/testQGSJetII.cpp +++ b/tests/modules/testQGSJetII.cpp @@ -175,7 +175,7 @@ TEST_CASE("QgsjetIIInterface", "[processes]") { SECTION("InteractionInterface Nuclei") { auto [stackPtr, secViewPtr] = setup::testing::setup_stack( - Code::Nucleus, 20, 10, 10100_GeV, + Code::Nucleus, 60, 30, 20100_GeV, (setup::Environment::BaseNodeType* const)nodePtr, *csPtr); setup::StackView& view = *(secViewPtr.get()); auto particle = stackPtr->first(); @@ -183,10 +183,15 @@ TEST_CASE("QgsjetIIInterface", "[processes]") { auto const projectileMomentum = projectile.getMomentum(); corsika::qgsjetII::Interaction model; - model.doInteraction(view); + model.doInteraction(view); // this also should produce some fragments + CHECK(view.getSize() == Approx(188).margin(2)); // this is not physics validation + int countFragments = 0; + for (auto const& sec : view) { countFragments += (sec.getPID() == Code::Nucleus); } + CHECK(countFragments == Approx(2).margin(1)); // this is not physics validation [[maybe_unused]] const GrammageType length = model.getInteractionLength(particle); - CHECK(length / (1_g / square(1_cm)) == Approx(20.13).margin(0.1)); + CHECK(length / (1_g / square(1_cm)) == + Approx(12).margin(2)); // this is not physics validation } SECTION("Heavy nuclei") { @@ -210,17 +215,50 @@ TEST_CASE("QgsjetIIInterface", "[processes]") { } SECTION("Allowed Particles") { - - auto [stackPtr, secViewPtr] = setup::testing::setup_stack( - Code::Electron, 0, 0, 1100_GeV, (setup::Environment::BaseNodeType* const)nodePtr, - *csPtr); - auto particle = stackPtr->first(); - auto projectile = secViewPtr->getProjectile(); - auto const projectileMomentum = projectile.getMomentum(); - - corsika::qgsjetII::Interaction model; - - GrammageType const length = model.getInteractionLength(particle); - CHECK(length / (1_g / square(1_cm)) == std::numeric_limits<double>::infinity()); + { // electron + auto [stackPtr, secViewPtr] = setup::testing::setup_stack( + Code::Electron, 0, 0, 100_GeV, (setup::Environment::BaseNodeType* const)nodePtr, + *csPtr); + auto particle = stackPtr->first(); + corsika::qgsjetII::Interaction model; + GrammageType const length = model.getInteractionLength(particle); + CHECK(length / (1_g / square(1_cm)) == std::numeric_limits<double>::infinity()); + } + { // pi0 is internally converted into pi+/pi- + auto [stackPtr, secViewPtr] = setup::testing::setup_stack( + Code::Pi0, 0, 0, 1000_GeV, (setup::Environment::BaseNodeType* const)nodePtr, + *csPtr); + setup::StackView& view = *(secViewPtr.get()); + corsika::qgsjetII::Interaction model; + model.doInteraction(view); + CHECK(view.getSize() == Approx(18).margin(2)); // this is not physics validation + } + { // rho0 is internally converted into pi-/pi+ + auto [stackPtr, secViewPtr] = setup::testing::setup_stack( + Code::Rho0, 0, 0, 1000_GeV, (setup::Environment::BaseNodeType* const)nodePtr, + *csPtr); + setup::StackView& view = *(secViewPtr.get()); + corsika::qgsjetII::Interaction model; + model.doInteraction(view); + CHECK(view.getSize() == Approx(7).margin(2)); // this is not physics validation + } + { // Lambda is internally converted into neutron + auto [stackPtr, secViewPtr] = setup::testing::setup_stack( + Code::Lambda0, 0, 0, 100_GeV, (setup::Environment::BaseNodeType* const)nodePtr, + *csPtr); + setup::StackView& view = *(secViewPtr.get()); + corsika::qgsjetII::Interaction model; + model.doInteraction(view); + CHECK(view.getSize() == Approx(25).margin(3)); // this is not physics validation + } + { // AntiLambda is internally converted into anti neutron + auto [stackPtr, secViewPtr] = setup::testing::setup_stack( + Code::Lambda0Bar, 0, 0, 100_GeV, + (setup::Environment::BaseNodeType* const)nodePtr, *csPtr); + setup::StackView& view = *(secViewPtr.get()); + corsika::qgsjetII::Interaction model; + model.doInteraction(view); + CHECK(view.getSize() == Approx(25).margin(3)); // this is not physics validation + } } } diff --git a/tests/modules/testUrQMD.cpp b/tests/modules/testUrQMD.cpp index 92a544932db254827fb2cd70518214d00b4d7db6..da788b797ffc8785b2fdf0275075d33d28b1abc8 100644 --- a/tests/modules/testUrQMD.cpp +++ b/tests/modules/testUrQMD.cpp @@ -84,15 +84,34 @@ TEST_CASE("UrQMD") { for (auto code : validProjectileCodes) { auto [stack, view] = setup::testing::setup_stack(code, 0, 0, 100_GeV, nodePtr, cs); - REQUIRE(stack->getEntries() == 1); - REQUIRE(view->getEntries() == 0); + CHECK(stack->getEntries() == 1); + CHECK(view->getEntries() == 0); // simple check whether the cross-section is non-vanishing // only nuclei with available tabluated data so far - REQUIRE(urqmd.getInteractionLength(stack->getNextParticle()) > 1_g / square(1_cm)); + CHECK(urqmd.getInteractionLength(stack->getNextParticle()) > 1_g / square(1_cm)); } } + SECTION("targets options") { + auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Argon); + auto const& cs = *csPtr; + { [[maybe_unused]] auto const& env_dummy = env; } + auto [stack, view] = + setup::testing::setup_stack(Code::Proton, 0, 0, 100_GeV, nodePtr, cs); + CHECK(urqmd.getInteractionLength(stack->getNextParticle()) / 1_g * square(1_cm) == + Approx(105).margin(5)); + } + + SECTION("invalid targets options") { + auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Omega); + auto const& cs = *csPtr; + { [[maybe_unused]] auto const& env_dummy = env; } + auto [stack, view] = + setup::testing::setup_stack(Code::Neutron, 0, 0, 100_GeV, nodePtr, cs); + CHECK_THROWS(urqmd.getInteractionLength(stack->getNextParticle())); + } + SECTION("nucleus projectile") { auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Oxygen); [[maybe_unused]] auto const& env_dummy = env; // against warnings @@ -144,6 +163,32 @@ TEST_CASE("UrQMD") { Approx(0).margin(1e-2)); } + SECTION("\"special\" projectile and target") { + auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Proton); + [[maybe_unused]] auto const& env_dummy = env; // against warnings + [[maybe_unused]] auto const& node_dummy = nodePtr; // against warnings + + auto [stackPtr, secViewPtr] = setup::testing::setup_stack( + Code::PiPlus, 0, 0, 40_GeV, (setup::Environment::BaseNodeType* const)nodePtr, + *csPtr); + CHECK(stackPtr->getEntries() == 1); + CHECK(secViewPtr->getEntries() == 0); + + // must be assigned to variable, cannot be used as rvalue?! + auto projectile = secViewPtr->getProjectile(); + auto const projectileMomentum = projectile.getMomentum(); + + urqmd.doInteraction(*secViewPtr); + + CHECK(sumCharge(*secViewPtr) == + get_charge_number(Code::PiPlus) + get_charge_number(Code::Oxygen)); + + auto const secMomSum = + sumMomentum(*secViewPtr, projectileMomentum.getCoordinateSystem()); + CHECK((secMomSum - projectileMomentum).getNorm() / projectileMomentum.getNorm() == + Approx(0).margin(1e-2)); + } + SECTION("K0Long projectile") { auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Oxygen); [[maybe_unused]] auto const& env_dummy = env; // against warnings @@ -169,4 +214,22 @@ TEST_CASE("UrQMD") { CHECK((secMomSum - projectileMomentum).getNorm() / projectileMomentum.getNorm() == Approx(0).margin(1e-2)); } + + SECTION("K0Short projectile") { + auto [env, csPtr, nodePtr] = setup::testing::setup_environment(Code::Argon); + [[maybe_unused]] auto const& env_dummy = env; // against warnings + [[maybe_unused]] auto const& node_dummy = nodePtr; // against warnings + + auto [stackPtr, secViewPtr] = setup::testing::setup_stack( + Code::K0Short, 0, 0, 40_GeV, (setup::Environment::BaseNodeType* const)nodePtr, + *csPtr); + CHECK(stackPtr->getEntries() == 1); + CHECK(secViewPtr->getEntries() == 0); + + // must be assigned to variable, cannot be used as rvalue?! + auto projectile = secViewPtr->getProjectile(); + auto const projectileMomentum = projectile.getMomentum(); + + CHECK_THROWS(urqmd.doInteraction(*secViewPtr)); + } }