From 1c170b3b3f520d1e6c5a58cf010419184eb76139 Mon Sep 17 00:00:00 2001 From: Alan Coleman <alanc@udel.edu> Date: Fri, 8 Mar 2024 10:38:33 +0100 Subject: [PATCH] first draft of interaction writer --- .../modules/writers/InteractionWriter.inl | 119 ++++++++++++++++++ corsika/modules/writers/InteractionWriter.hpp | 61 +++++++++ tests/output/CMakeLists.txt | 3 +- tests/output/testInteractionWriter.cpp | 119 ++++++++++++++++++ 4 files changed, 301 insertions(+), 1 deletion(-) create mode 100644 corsika/detail/modules/writers/InteractionWriter.inl create mode 100644 corsika/modules/writers/InteractionWriter.hpp create mode 100644 tests/output/testInteractionWriter.cpp diff --git a/corsika/detail/modules/writers/InteractionWriter.inl b/corsika/detail/modules/writers/InteractionWriter.inl new file mode 100644 index 000000000..61784ae1c --- /dev/null +++ b/corsika/detail/modules/writers/InteractionWriter.inl @@ -0,0 +1,119 @@ +/* + * (c) Copyright 2024 CORSIKA Project, corsika-project@lists.kit.edu + * + * 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/core/Logging.hpp> + +namespace corsika { + + inline InteractionWriter::InteractionWriter(ShowerAxis const& axis) + : showerAxis_(axis) + , interactionCounter_(0) + , showerId_(0) + , nSecondaries_(0) + {} + + template <typename TStackView> + inline void InteractionWriter::doSecondaries(TStackView& vS) { + if (interactionCounter_++) { + return; + } + + auto primary = vS.getProjectile(); + auto dX = showerAxis_.getProjectedX(primary.getPosition()); + CORSIKA_LOG_INFO("First interaction at dX {}", dX); + CORSIKA_LOG_INFO("Primary: {}, E {}", primary.getPID(), primary.getKineticEnergy()); + + *(output_.getWriter()) << showerId_ << true + << static_cast<int>(primary.getPID()) + << static_cast<float>(primary.getKineticEnergy() / 1_GeV) + << static_cast<float>(1.0) + << static_cast<float>(0.0) + << static_cast<float>(0.0) + << static_cast<double>(primary.getTime() / 1_ns) + << static_cast<float>(dX / (1_g / 1_cm / 1_cm)) + << parquet::EndRow; + + // Loop through secondaries + auto particle = vS.begin(); + while (particle != vS.end()) { + + *(output_.getWriter()) << showerId_ << false + << static_cast<int>(particle.getPID()) + << static_cast<float>(particle.getKineticEnergy() / 1_GeV) + << static_cast<float>(1.0) + << static_cast<float>(0.0) + << static_cast<float>(0.0) + << static_cast<double>(particle.getTime() / 1_ns) + << static_cast<float>(dX / (1_g / 1_cm / 1_cm)) + << parquet::EndRow; + + CORSIKA_LOG_INFO(" 2ndary: {}, E {}", particle.getPID(), particle.getKineticEnergy()); + ++particle; + ++nSecondaries_; + } + } + + inline void InteractionWriter::startOfLibrary(boost::filesystem::path const& directory) { + output_.initStreamer((directory / ("interactions.parquet")).string()); + + output_.addField("primary", parquet::Repetition::REQUIRED, parquet::Type::BOOLEAN, + parquet::ConvertedType::NONE); + output_.addField("pdg", parquet::Repetition::REQUIRED, parquet::Type::INT32, + parquet::ConvertedType::INT_32); + output_.addField("kinetic_energy", parquet::Repetition::REQUIRED, + parquet::Type::FLOAT, parquet::ConvertedType::NONE); + output_.addField("nx", parquet::Repetition::REQUIRED, parquet::Type::FLOAT, + parquet::ConvertedType::NONE); + output_.addField("ny", parquet::Repetition::REQUIRED, parquet::Type::FLOAT, + parquet::ConvertedType::NONE); + output_.addField("nz", parquet::Repetition::REQUIRED, parquet::Type::FLOAT, + parquet::ConvertedType::NONE); + output_.addField("time", parquet::Repetition::REQUIRED, parquet::Type::DOUBLE, + parquet::ConvertedType::NONE); + output_.addField("dX", parquet::Repetition::REQUIRED, parquet::Type::FLOAT, + parquet::ConvertedType::NONE); + + output_.buildStreamer(); + + showerId_ = 0; + interactionCounter_ = 0; + nSecondaries_ = 0; + summary_ = YAML::Node(); + } + + inline void InteractionWriter::startOfShower(unsigned int const showerId) { + showerId_ = showerId; + interactionCounter_ = 0; + nSecondaries_ = 0; + } + + inline void InteractionWriter::endOfShower(unsigned int const) { + summary_["shower_" + std::to_string(showerId_)]["n_secondaries"] = nSecondaries_; + } + + inline void InteractionWriter::endOfLibrary() { + output_.closeStreamer(); + } + + inline YAML::Node InteractionWriter::getConfig() const { + YAML::Node node; + node["type"] = "Interactions"; + node["units"]["energy"] = "GeV"; + node["units"]["length"] = "m"; + node["units"]["time"] = "ns"; + node["units"]["grammage"] = "g/cm^2"; + return node; + } + + inline YAML::Node InteractionWriter::getSummary() const { + return summary_; + } + +} // namespace corsika diff --git a/corsika/modules/writers/InteractionWriter.hpp b/corsika/modules/writers/InteractionWriter.hpp new file mode 100644 index 000000000..6ba3fee30 --- /dev/null +++ b/corsika/modules/writers/InteractionWriter.hpp @@ -0,0 +1,61 @@ +/* + * (c) Copyright 2024 CORSIKA Project, corsika-project@lists.kit.edu + * + * 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/process/SecondariesProcess.hpp> + +#include <corsika/media/ShowerAxis.hpp> + +#include <corsika/output/BaseOutput.hpp> +#include <corsika/output/ParquetStreamer.hpp> + +namespace corsika { + + class InteractionWriter : public SecondariesProcess<InteractionWriter>, BaseOutput{ + + public: + /** + * + * @param axis - axis used for calculating grammage + */ + InteractionWriter(ShowerAxis const& axis); + + /** + * + * @tparam TStackView + */ + template <typename TStackView> + void doSecondaries(TStackView&); + + void startOfLibrary(boost::filesystem::path const&) override; + void endOfLibrary() override; + + void startOfShower(unsigned int const) override; + void endOfShower(unsigned int const) override; + + + YAML::Node getConfig() const override; + YAML::Node getSummary() const override; + + auto getInteractionCounter() { return interactionCounter_; } + + private: + ShowerAxis const& showerAxis_; + unsigned int interactionCounter_; + unsigned int showerId_; + unsigned int nSecondaries_; + + ParquetStreamer output_; + YAML::Node summary_; + + }; // namespace corsika + +} // namespace corsika + +#include <corsika/detail/modules/writers/InteractionWriter.inl> diff --git a/tests/output/CMakeLists.txt b/tests/output/CMakeLists.txt index f85f95aa7..3b954de5f 100644 --- a/tests/output/CMakeLists.txt +++ b/tests/output/CMakeLists.txt @@ -1,7 +1,8 @@ set (test_output_sources + testDummyOutputManager.cpp + testInteractionWriter.cpp TestMain.cpp testOutputManager.cpp - testDummyOutputManager.cpp testParquetStreamer.cpp testWriterObservationPlane.cpp testWriterTracks.cpp diff --git a/tests/output/testInteractionWriter.cpp b/tests/output/testInteractionWriter.cpp new file mode 100644 index 000000000..302c0ccd6 --- /dev/null +++ b/tests/output/testInteractionWriter.cpp @@ -0,0 +1,119 @@ +/* + * (c) Copyright 2024 CORSIKA Project, corsika-project@lists.kit.edu + * + * 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 <boost/filesystem.hpp> + +#include <catch2/catch.hpp> + +#include <corsika/modules/writers/InteractionWriter.hpp> + +#include <corsika/framework/core/PhysicalUnits.hpp> +#include <corsika/framework/geometry/Point.hpp> +#include <corsika/framework/geometry/RootCoordinateSystem.hpp> +#include <corsika/framework/geometry/Vector.hpp> + +#include <corsika/media/Environment.hpp> +#include <corsika/media/HomogeneousMedium.hpp> + +#include <SetupTestStack.hpp> +#include <SetupTestTrajectory.hpp> + + +using namespace corsika; + +using DummyEnvironmentInterface = IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>; +using DummyEnvironment = Environment<DummyEnvironmentInterface>; + +TEST_CASE("InteractionWriter", "process") { + logging::set_level(logging::level::info); + + auto env = std::make_unique<Environment<IMediumModel>>(); + auto& universe = *(env->getUniverse()); + CoordinateSystemPtr const& rootCS = env->getCoordinateSystem(); + auto theMedium = Environment<IMediumModel>::createNode<Sphere>( + Point{rootCS, 0_m, 0_m, 0_m}, 1_km * std::numeric_limits<double>::infinity()); + using MyHomogeneousModel = HomogeneousMedium<IMediumModel>; + const auto density = 1_kg / (1_m * 1_m * 1_m); + theMedium->setModelProperties<MyHomogeneousModel>( + density, NuclearComposition({Code::Nitrogen}, {1.})); + universe.addChild(std::move(theMedium)); + + + std::string const outputDir = "./output_interactions"; + + // preparation + if (boost::filesystem::exists(outputDir)) { boost::filesystem::remove_all(outputDir); } + boost::filesystem::create_directory(outputDir); + + // setup empty particle stack + test::Stack stack; + stack.clear(); + + // Make shower axis + auto start = Point(rootCS, -1_m, 0_m, 0_m); + auto stop = Point(rootCS, 1_m, 0_m, 0_m); + auto length = stop - start; + ShowerAxis axis(start, length, *env); + + + InteractionWriter writer(axis); + writer.startOfLibrary(outputDir); + writer.startOfShower(0); + + CHECK(!writer.getInteractionCounter()); + + // add primary particle to stack + auto particle = stack.addParticle( + std::make_tuple(Code::Proton, 10_GeV, DirectionVector(rootCS, {1, 0, 0}), + Point(rootCS, 0_m, 0_m, 0_m), 0_ns)); + + + test::StackView view(particle); + auto projectile = view.getProjectile(); + + std::vector<Code> const particleList = {Code::PiPlus, Code::PiMinus, Code::KPlus, + Code::KMinus, Code::K0Long, Code::K0Short, + Code::Electron, Code::MuPlus, Code::NuE, + Code::Neutron, Code::NuMu + }; + + for (auto proType : particleList) + projectile.addSecondary( + std::make_tuple(proType, 1_GeV, DirectionVector(rootCS, {1, 0, 0}))); + + // pre-check on stack size + CHECK(view.getEntries() == 11); + CHECK(stack.getEntries() == 12); + + // correctly initialized counter + CHECK(!writer.getInteractionCounter()); + + // run the process and check for consistency + writer.doSecondaries(view); + CHECK(1 == writer.getInteractionCounter()); + CHECK(view.getEntries() == 11); + CHECK(stack.getEntries() == 12); + + // ensure the counter bumps + writer.doSecondaries(view); + CHECK(2 == writer.getInteractionCounter()); + + writer.endOfShower(0); + writer.endOfLibrary(); + + CHECK(boost::filesystem::exists(outputDir + "/interactions.parquet")); + + auto const config = writer.getConfig(); + CHECK(config["type"].as<std::string>() == "Interactions"); + + auto const summary = writer.getSummary(); + CHECK(summary["shower_0"]["n_secondaries"].as<int>() == 11); + + // clean up + if (boost::filesystem::exists(outputDir)) { boost::filesystem::remove_all(outputDir); } +} -- GitLab