diff --git a/corsika/detail/media/LayeredSphericalAtmosphereBuilder.hpp b/corsika/detail/media/LayeredSphericalAtmosphereBuilder.hpp new file mode 100644 index 0000000000000000000000000000000000000000..0bf88ff6743b9fc90271d509730858563d9d58d3 --- /dev/null +++ b/corsika/detail/media/LayeredSphericalAtmosphereBuilder.hpp @@ -0,0 +1,20 @@ +#pragma once + +namespace corsika { + + namespace detail { + + struct NoExtraModelInner {}; + + template <typename M> + struct NoExtraModel {}; + + template <template <typename> typename M> + struct has_extra_models : std::true_type {}; + + template <> + struct has_extra_models<NoExtraModel> : std::false_type {}; + + } // namespace detail + +} diff --git a/corsika/detail/modules/qgsjetII/QGSJetIIStack.inl b/corsika/detail/modules/qgsjetII/QGSJetIIStack.inl new file mode 100644 index 0000000000000000000000000000000000000000..8405888ccf2717f196ec3d4736fe50713970af6e --- /dev/null +++ b/corsika/detail/modules/qgsjetII/QGSJetIIStack.inl @@ -0,0 +1,105 @@ +#pragma once + +namespace corsika::qgsjetII { + + void QGSJetIIStackData::clear() { + qgarr12_.nsp = 0; + qgarr13_.nsf = 0; + qgarr55_.nwt = 0; + } + unsigned int QGSJetIIStackData::getSize() const { return qgarr12_.nsp; } + unsigned int QGSJetIIStackData::getCapacity() const { return nptmax; } + + void QGSJetIIStackData::setId(const unsigned int i, const int v) { + qgarr14_.ich[i] = v; + } + void QGSJetIIStackData::setEnergy(const unsigned int i, const HEPEnergyType v) { + qgarr14_.esp[i][0] = v / 1_GeV; + } + + void QGSJetIIStackData::setMomentum(const unsigned int i, const MomentumVector& v) { + auto tmp = v.getComponents(); + qgarr14_.esp[i][2] = tmp[0] / 1_GeV; + qgarr14_.esp[i][3] = tmp[1] / 1_GeV; + qgarr14_.esp[i][1] = tmp[2] / 1_GeV; + } + + int QGSJetIIStackData::getId(const unsigned int i) const { return qgarr14_.ich[i]; } + HEPEnergyType QGSJetIIStackData::getEnergy(const int i) const { + return qgarr14_.esp[i][0] * 1_GeV; + } + MomentumVector QGSJetIIStackData::getMomentum( + const unsigned int i, const CoordinateSystemPtr& CS) const { + QuantityVector<hepmomentum_d> components = {qgarr14_.esp[i][2] * 1_GeV, + qgarr14_.esp[i][3] * 1_GeV, + qgarr14_.esp[i][1] * 1_GeV}; + return MomentumVector(CS, components); + } + + void QGSJetIIStackData::copy(const unsigned int i1, const unsigned int i2) { + qgarr14_.ich[i2] = qgarr14_.ich[i1]; + for (unsigned int i = 0; i < 4; ++i) qgarr14_.esp[i2][i] = qgarr14_.esp[i1][i]; + } + + void QGSJetIIStackData::swap(const unsigned int i1, const unsigned int i2) { + std::swap(qgarr14_.ich[i1], qgarr14_.ich[i2]); + for (unsigned int i = 0; i < 4; ++i) + std::swap(qgarr14_.esp[i1][i], qgarr14_.esp[i2][i]); + } + + void QGSJetIIStackData::incrementSize() { qgarr12_.nsp++; } + void QGSJetIIStackData::decrementSize() { + if (qgarr12_.nsp > 0) { qgarr12_.nsp--; } + } + + template <typename StackIteratorInterface> + void ParticleInterface<StackIteratorInterface>::setParticleData( + const int vID, const HEPEnergyType vE, const MomentumVector& vP, + const HEPMassType) { + setPID(vID); + setEnergy(vE); + setMomentum(vP); + } + + template <typename StackIteratorInterface> + void ParticleInterface<StackIteratorInterface>::setParticleData( + ParticleInterface<StackIteratorInterface>& /*parent*/, const int vID, + const HEPEnergyType vE, const MomentumVector& vP, const HEPMassType) { + setPID(vID); + setEnergy(vE); + setMomentum(vP); + } + + template <typename StackIteratorInterface> + void ParticleInterface<StackIteratorInterface>::setEnergy(const HEPEnergyType v) { + getStackData().setEnergy(getIndex(), v); + } + + template <typename StackIteratorInterface> + HEPEnergyType ParticleInterface<StackIteratorInterface>::getEnergy() const { + return getStackData().getEnergy(getIndex()); + } + + template <typename StackIteratorInterface> + void ParticleInterface<StackIteratorInterface>::setPID(const int v) { + getStackData().setId(getIndex(), v); + } + + template <typename StackIteratorInterface> + corsika::qgsjetII::QgsjetIICode ParticleInterface<StackIteratorInterface>::getPID() + const { + return static_cast<corsika::qgsjetII::QgsjetIICode>(getStackData().getId(getIndex())); + } + + template <typename StackIteratorInterface> + MomentumVector ParticleInterface<StackIteratorInterface>::getMomentum( + const CoordinateSystemPtr& CS) const { + return getStackData().getMomentum(getIndex(), CS); + } + + template <typename StackIteratorInterface> + void ParticleInterface<StackIteratorInterface>::setMomentum(const MomentumVector& v) { + getStackData().setMomentum(getIndex(), v); + } + +} // namespace corsika::qgsjetII diff --git a/corsika/framework/core/PhysicalGeometry.hpp b/corsika/framework/core/PhysicalGeometry.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fb1645786db242dc0625c2324e2574dedb57eccf --- /dev/null +++ b/corsika/framework/core/PhysicalGeometry.hpp @@ -0,0 +1,26 @@ +/* + * (c) Copyright 2020 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/PhysicalUnits.hpp> +#include <corsika/framework/geometry/Vector.hpp> + +/** + * \file PhysicalUnits.hpp + * + * Import and extend the phys::units package. The SI units are also imported into the + * `\namespace corsika`, since they are used everywhere as integral part of the framework. + */ + +namespace corsika { + + typedef Vector<hepmomentum_d> MomentumVector; + + +} // namespace corsika diff --git a/corsika/framework/geometry/PhysicalGeometry.hpp b/corsika/framework/geometry/PhysicalGeometry.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fb1645786db242dc0625c2324e2574dedb57eccf --- /dev/null +++ b/corsika/framework/geometry/PhysicalGeometry.hpp @@ -0,0 +1,26 @@ +/* + * (c) Copyright 2020 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/PhysicalUnits.hpp> +#include <corsika/framework/geometry/Vector.hpp> + +/** + * \file PhysicalUnits.hpp + * + * Import and extend the phys::units package. The SI units are also imported into the + * `\namespace corsika`, since they are used everywhere as integral part of the framework. + */ + +namespace corsika { + + typedef Vector<hepmomentum_d> MomentumVector; + + +} // namespace corsika diff --git a/corsika/stack/history/CMakeLists.txt b/corsika/stack/history/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..2a47b86189bef946269e8f2815a51b2cfcf171f6 --- /dev/null +++ b/corsika/stack/history/CMakeLists.txt @@ -0,0 +1,69 @@ +set ( + HISTORY_HEADERS + EventType.hpp + Event.hpp + HistorySecondaryProducer.hpp + HistoryObservationPlane.hpp + HistoryStackExtension.hpp + SecondaryParticle.hpp + ) + +set ( + HISTORY_NAMESPACE + corsika/stack/history + ) + +if (WITH_HISTORY) + set ( + HISTORY_SOURCES + HistoryObservationPlane.cpp + ) + add_library (CORSIKAhistory STATIC ${HISTORY_SOURCES}) + set (IS_INTERFACE "") +else (WITH_HISTORY) + add_library (CORSIKAhistory INTERFACE) + set (IS_INTERFACE "INTERFACE") +endif (WITH_HISTORY) + +CORSIKA_COPY_HEADERS_TO_NAMESPACE (CORSIKAhistory ${HISTORY_NAMESPACE} ${HISTORY_HEADERS}) + +target_link_libraries ( + CORSIKAhistory + ${IS_INTERFACE} + CORSIKAparticles + CORSIKAgeometry + CORSIKAprocesssequence # for HistoryObservationPlane + CORSIKAsetup # for HistoryObservationPlane + CORSIKAlogging + SuperStupidStack + NuclearStackExtension # for testHistoryView.cc + C8::ext::boost # for HistoryObservationPlane + ) + +target_include_directories ( + CORSIKAhistory + INTERFACE + $<BUILD_INTERFACE:${PROJECT_BINARY_DIR}/include> + $<INSTALL_INTERFACE:include/include> + ) + +install ( + FILES ${HISTORY_HEADERS} + DESTINATION include/${HISTORY_NAMESPACE} + ) + +# ---------------- +# code unit testing +CORSIKA_ADD_TEST(testHistoryStack) +target_link_libraries ( + testHistoryStack + CORSIKAhistory + CORSIKAtesting + DummyStack + ) +CORSIKA_ADD_TEST(testHistoryView) +target_link_libraries ( + testHistoryView + CORSIKAhistory + CORSIKAtesting + ) diff --git a/corsika/stack/history/Event.hpp b/corsika/stack/history/Event.hpp new file mode 100644 index 0000000000000000000000000000000000000000..c00f5511b3ce91f748cdcb372d345f6d4da8ff13 --- /dev/null +++ b/corsika/stack/history/Event.hpp @@ -0,0 +1,68 @@ +/* + * (c) Copyright 2020 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/logging/Logging.hpp> +#include <corsika/framework/core/ParticleProperties.hpp> +#include <corsika/stack/history/EventType.hpp> +#include <corsika/stack/history/SecondaryParticle.hpp> + +#include <memory> +#include <optional> +#include <vector> + +namespace corsika::history { + + class Event; + using EventPtr = std::shared_ptr<history::Event>; + + class Event { + + size_t projectile_index_ = 0; //!< index of projectile on stack + std::vector<SecondaryParticle> secondaries_; + EventPtr parent_event_; + + EventType type_ = EventType::Uninitialized; + + std::optional<Code> targetCode_; + + public: + Event() = default; + + void setParentEvent(EventPtr const& evt) { parent_event_ = evt; } + + bool hasParentEvent() const { return bool(parent_event_); } + EventPtr& parentEvent() { return parent_event_; } + EventPtr const& parentEvent() const { return parent_event_; } + + void setProjectileIndex(size_t i) { projectile_index_ = i; } + size_t projectileIndex() const { return projectile_index_; } + + size_t addSecondary(HEPEnergyType energy, + Vector<hepmomentum_d> const& momentum, + Code pid) { + secondaries_.emplace_back(energy, momentum, pid); + return secondaries_.size() - 1; + } + + std::vector<SecondaryParticle> const& secondaries() const { return secondaries_; } + + void setTargetCode(const Code t) { targetCode_ = t; } + + std::string as_string() const { + return fmt::format("hasParent={}, projIndex={}, Nsec={}", hasParentEvent(), + projectile_index_, secondaries_.size()); + } + + EventType eventType() const { return type_; } + + void setEventType(EventType t) { type_ = t; } + }; + +} // namespace corsika::history diff --git a/corsika/stack/history/EventType.hpp b/corsika/stack/history/EventType.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4781d5c64c085684a8ea139abda2aa856b56cd66 --- /dev/null +++ b/corsika/stack/history/EventType.hpp @@ -0,0 +1,13 @@ +/* + * (c) Copyright 2020 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 + +namespace corsika::history { + enum class EventType { Uninitialized, Interaction, Decay }; +} diff --git a/corsika/stack/history/HistoryObservationPlane.cpp b/corsika/stack/history/HistoryObservationPlane.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b1425f18be3ff20fe68f8ffbaf2f6ff9244d6e78 --- /dev/null +++ b/corsika/stack/history/HistoryObservationPlane.cpp @@ -0,0 +1,84 @@ +/* + * (c) Copyright 2020 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 <corsika/logging/Logging.h> +#include <corsika/stack/history/HistoryObservationPlane.hpp> + +#include <boost/histogram/ostream.hpp> + +#include <iomanip> +#include <iostream> + +using namespace corsika::units::si; +using namespace corsika::history; +using namespace corsika; + +HistoryObservationPlane::HistoryObservationPlane(setup::Stack const& stack, + geometry::Plane const& obsPlane, + bool deleteOnHit) + : stack_{stack} + , plane_{obsPlane} + , deleteOnHit_{deleteOnHit} {} + +corsika::process::EProcessReturn HistoryObservationPlane::DoContinuous( + setup::Stack::ParticleType const& particle, setup::Trajectory const& trajectory) { + TimeType const timeOfIntersection = + (plane_.GetCenter() - trajectory.GetR0()).dot(plane_.GetNormal()) / + trajectory.GetV0().dot(plane_.GetNormal()); + + if (timeOfIntersection < TimeType::zero()) { return process::EProcessReturn::eOk; } + + if (plane_.IsAbove(trajectory.GetR0()) == plane_.IsAbove(trajectory.GetPosition(1))) { + return process::EProcessReturn::eOk; + } + + C8LOG_DEBUG(fmt::format("HistoryObservationPlane: Particle detected: pid={}", + particle.GetPID())); + + auto const pid = particle.GetPID(); + if (particles::IsMuon(pid)) { fillHistoryHistogram(particle); } + + if (deleteOnHit_) { + return process::EProcessReturn::eParticleAbsorbed; + } else { + return process::EProcessReturn::eOk; + } +} + +LengthType HistoryObservationPlane::MaxStepLength(setup::Stack::ParticleType const&, + setup::Trajectory const& trajectory) { + 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; +} + +void HistoryObservationPlane::fillHistoryHistogram( + setup::Stack::ParticleType const& muon) { + double const muon_energy = muon.GetEnergy() / 1_GeV; + + int genctr{0}; + Event const* event = muon.GetEvent().get(); + while (event) { + auto const projectile = stack_.cfirst() + event->projectileIndex(); + if (event->eventType() == EventType::Interaction) { + genctr++; + double const projEnergy = projectile.GetEnergy() / 1_GeV; + int const pdg = static_cast<int>(particles::GetPDG(projectile.GetPID())); + + histogram_(muon_energy, projEnergy, pdg); + } + event = event->parentEvent().get(); // projectile.GetEvent().get(); + } +} diff --git a/corsika/stack/history/HistoryObservationPlane.hpp b/corsika/stack/history/HistoryObservationPlane.hpp new file mode 100644 index 0000000000000000000000000000000000000000..fb67ca7f9b8897cb6d6f2b11c6ce02101a5ea653 --- /dev/null +++ b/corsika/stack/history/HistoryObservationPlane.hpp @@ -0,0 +1,60 @@ +/* + * (c) Copyright 2020 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/geometry/Plane.h> +#include <corsika/process/ContinuousProcess.h> +#include <corsika/setup/SetupStack.h> +#include <corsika/setup/SetupTrajectory.h> +#include <corsika/units/PhysicalUnits.h> + +#include <boost/histogram.hpp> + +#include <functional> + +namespace corsika::history { + namespace detail { + inline auto hist_factory() { + namespace bh = boost::histogram; + namespace bha = bh::axis; + auto h = bh::make_histogram( + bha::regular<float, bha::transform::log>{11 * 5, 1e0, 1e11, "muon energy"}, + bha::regular<float, bha::transform::log>{11 * 5, 1e0, 1e11, + "projectile energy"}, + bha::category<int, bh::use_default, bha::option::growth_t>{ + {211, -211, 2212, -2212}, "projectile PDG"}); + return h; + } + } // namespace detail + + class HistoryObservationPlane + : public corsika::process::ContinuousProcess<HistoryObservationPlane> { + public: + HistoryObservationPlane(setup::Stack const&, geometry::Plane const&, bool = true); + + corsika::units::si::LengthType MaxStepLength( + corsika::setup::Stack::ParticleType const&, + corsika::setup::Trajectory const& vTrajectory); + + corsika::process::EProcessReturn DoContinuous( + corsika::setup::Stack::ParticleType const& vParticle, + corsika::setup::Trajectory const& vTrajectory); + + auto const& histogram() const { return histogram_; } + + private: + void fillHistoryHistogram(setup::Stack::ParticleType const&); + + setup::Stack const& stack_; + geometry::Plane const plane_; + bool const deleteOnHit_; + + decltype(detail::hist_factory()) histogram_ = detail::hist_factory(); + }; +} // namespace corsika::history diff --git a/corsika/stack/history/HistorySecondaryProducer.hpp b/corsika/stack/history/HistorySecondaryProducer.hpp new file mode 100644 index 0000000000000000000000000000000000000000..f7317a22b0c290203880af3110b07a63fb4b2379 --- /dev/null +++ b/corsika/stack/history/HistorySecondaryProducer.hpp @@ -0,0 +1,59 @@ +/* + * (c) Copyright 2018 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/stack/SecondaryView.hpp> +#include <corsika/stack/history/Event.hpp> + +#include <corsika/framework/logging/Logging.hpp> + +#include <boost/type_index.hpp> + +#include <memory> +#include <type_traits> +#include <utility> + +namespace corsika::history { + + //! mix-in class for SecondaryView that fills secondaries into an \class Event + template <class T1, template <class> class T2> + class HistorySecondaryProducer { + public: + EventPtr event_; + static bool constexpr has_event{true}; + + public: + template <typename Particle> + HistorySecondaryProducer(Particle const& p) + : event_{std::make_shared<Event>()} { + CORSIKA_LOG_TRACE("HistorySecondaryProducer::HistorySecondaryProducer"); + event_->setProjectileIndex(p.getIndex()); + event_->setParentEvent(p.getEvent()); + } + + /** + * Method is called after a new Secondary has been created on the + * SecondaryView. Extra logic can be introduced here. + * + * The input Particle is the new secondary that was produced and + * is of course a reference into the SecondaryView itself. + */ + template <typename Particle> + auto new_secondary(Particle& sec) { + CORSIKA_LOG_TRACE("HistorySecondaryProducer::new_secondary(sec)"); + + // store particles at production time in Event here + auto const sec_index = + event_->addSecondary(sec.getEnergy(), sec.getMomentum(), sec.getPID()); + sec.setParentEventIndex(sec_index); + sec.setEvent(event_); + } + }; + +} // namespace corsika::history diff --git a/corsika/stack/history/HistoryStackExtension.hpp b/corsika/stack/history/HistoryStackExtension.hpp new file mode 100644 index 0000000000000000000000000000000000000000..2d79af4c4fa12a009a019e1fe27e576e130a184c --- /dev/null +++ b/corsika/stack/history/HistoryStackExtension.hpp @@ -0,0 +1,139 @@ +/* + * (c) Copyright 2020 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/logging/Logging.hpp> +#include <corsika/framework/stack/Stack.hpp> + +#include <memory> +#include <utility> +#include <vector> + +namespace corsika::history { + + /** + * @class HistoryData + * + * definition of stack-data object to store history information this + * is vector with shared_ptr<TEvent>, where TEvent is a free + * template parameter for customization. + */ + template <typename TEvent> + class HistoryData { + using EventPtr = + std::shared_ptr<TEvent>; //!< Pointer to the event where this particle was created + using ParentEventIndex = int; //!< index to TEvent::secondaries_ + using DataType = std::pair<EventPtr, ParentEventIndex>; + + public: + // these functions are needed for the Stack interface + void clear() { historyData_.clear(); } + unsigned int getSize() const { return historyData_.size(); } + unsigned int getCapacity() const { return historyData_.size(); } + void copy(const int i1, const int i2) { historyData_[i2] = historyData_[i1]; } + void swap(const int i1, const int i2) { + std::swap(historyData_[i1], historyData_[i2]); + } + + // custom data access function + void setEvent(const int i, EventPtr v) { historyData_[i].first = std::move(v); } + const EventPtr& getEvent(const int i) const { return historyData_[i].first; } + EventPtr& getEvent(const int i) { return historyData_[i].first; } + + void setParentEventIndex(const int i, ParentEventIndex v) { + historyData_[i].second = std::move(v); + } + ParentEventIndex getParentEventIndex(const int i) const { + return historyData_[i].second; + } + + // these functions are also needed by the Stack interface + void incrementSize() { historyData_.push_back(DataType{}); } + void decrementSize() { + if (historyData_.size() > 0) { historyData_.pop_back(); } + } + + // custom private data section + private: + std::vector<DataType> historyData_; + }; + + /** + * @class HistoryDataInterface + * + * corresponding defintion of a stack-readout object, the iteractor + * dereference operator will deliver access to these function + // defintion of a stack-readout object, the iteractor dereference + // operator will deliver access to these function + */ + template <typename T, typename TEvent> + class HistoryDataInterface : public T { + protected: + using T::getStack; + using T::getStackData; + + public: + using T::getIndex; + + public: + // create a new particle from scratch + void setParticleData() { + CORSIKA_LOG_TRACE("HistoyDatatInterface::setParticleData()"); + getStackData().setParentEventIndex(getIndex(), -1); + } + + // create a new particle as secondary of a parent + void setParticleData(HistoryDataInterface& /*parent*/) { + CORSIKA_LOG_TRACE("HistoyDatatInterface::setParticleData(parnt)"); + setParticleData(); + } + + void setEvent(const std::shared_ptr<TEvent>& v) { + getStackData().setEvent(getIndex(), v); + } + + void setParentEventIndex(int index) { + getStackData().setParentEventIndex(getIndex(), index); + } + + std::shared_ptr<TEvent> getEvent() const { + return getStackData().getEvent(getIndex()); + } + + int getParentEventIndex() const { + return getStackData().getParentEventIndex(getIndex()); + } + + std::string asString() const { + return fmt::format("i_parent={}, [evt: {}]", getParentEventIndex(), + (bool(getEvent()) ? getEvent()->as_string() : "n/a")); + } + }; + + template <typename T, typename TEvent> + struct MakeHistoryDataInterface { + typedef HistoryDataInterface<T, TEvent> type; + }; + +} // namespace corsika::history + +// for user-friendlyness we create the HistoryDataInterface type +// with the histoy::Event data content right here: + +#include <corsika/stack/history/Event.hpp> + +namespace corsika::history { + + template <typename TStackIter> + using HistoryEventDataInterface = + typename history::MakeHistoryDataInterface<TStackIter, history::Event>::type; + + using HistoryEventData = history::HistoryData<history::Event>; + +} // namespace corsika::history diff --git a/corsika/stack/history/SecondaryParticle.hpp b/corsika/stack/history/SecondaryParticle.hpp new file mode 100644 index 0000000000000000000000000000000000000000..4a36abf37f50eb5ce61f5f7e5cd8f16720ea7893 --- /dev/null +++ b/corsika/stack/history/SecondaryParticle.hpp @@ -0,0 +1,35 @@ +/* + * (c) Copyright 2020 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/geometry/Vector.hpp> +#include <corsika/framework/core/ParticleProperties.hpp> +#include <corsika/framework/core/PhysicalUnits.hpp> + +#include <vector> + +namespace corsika::history { + + /** + * This class stores the non-common properties of secondaries in an event. All + * other (common) properties are available via the event itself or its projectile. + */ + struct SecondaryParticle { + HEPEnergyType const energy_; + Vector<hepmomentum_d> const momentum_; + Code const pid_; + + public: + SecondaryParticle(HEPEnergyType energy, Vector<hepmomentum_d> momentum, Code pid) + : energy_{energy} + , momentum_{momentum} + , pid_{pid} {} + }; + +} // namespace corsika::history diff --git a/examples/em_shower.cpp b/examples/em_shower.cpp new file mode 100644 index 0000000000000000000000000000000000000000..594a95845c9a2e8d8640aeda4c7847ed591d9420 --- /dev/null +++ b/examples/em_shower.cpp @@ -0,0 +1,185 @@ +/* + * (c) Copyright 2020 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 <corsika/cascade/Cascade.h> +#include <corsika/environment/Environment.h> +#include <corsika/environment/LayeredSphericalAtmosphereBuilder.h> +#include <corsika/environment/NuclearComposition.h> +#include <corsika/environment/ShowerAxis.h> +#include <corsika/geometry/Plane.h> +#include <corsika/geometry/Sphere.h> +#include <corsika/process/ProcessSequence.h> +#include <corsika/process/StackProcess.h> +#include <corsika/process/longitudinal_profile/LongitudinalProfile.h> +#include <corsika/process/observation_plane/ObservationPlane.h> +#include <corsika/process/particle_cut/ParticleCut.h> +#include <corsika/process/proposal/ContinuousProcess.h> +#include <corsika/process/proposal/Interaction.h> +#include <corsika/process/track_writer/TrackWriter.h> +#include <corsika/process/tracking_line/TrackingLine.h> +#include <corsika/random/RNGManager.h> +#include <corsika/setup/SetupStack.h> +#include <corsika/setup/SetupTrajectory.h> +#include <corsika/units/PhysicalUnits.h> +#include <corsika/utl/CorsikaFenv.h> +#include <corsika/process/interaction_counter/InteractionCounter.hpp> + +#include <corsika/logging/Logging.h> + +#include <iomanip> +#include <iostream> +#include <limits> +#include <string> +#include <typeinfo> + +using namespace corsika; +using namespace corsika::process; +using namespace corsika::units; +using namespace corsika::particles; +using namespace corsika::random; +using namespace corsika::geometry; +using namespace corsika::environment; + +using namespace std; +using namespace corsika::units::si; + +void registerRandomStreams() { + random::RNGManager::GetInstance().RegisterRandomStream("cascade"); + random::RNGManager::GetInstance().RegisterRandomStream("proposal"); + random::RNGManager::GetInstance().SeedAll(); +} + +template <typename T> +using MyExtraEnv = environment::MediumPropertyModel<environment::UniformMagneticField<T>>; + +int main(int argc, char** argv) { + + logging::SetLevel(logging::level::info); + + if (argc != 2) { + std::cerr << "usage: em_shower <energy/GeV>" << std::endl; + return 1; + } + feenableexcept(FE_INVALID); + // initialize random number sequence(s) + registerRandomStreams(); + + // setup environment, geometry + using EnvType = setup::Environment; + EnvType env; + const CoordinateSystem& rootCS = env.GetCoordinateSystem(); + Point const center{rootCS, 0_m, 0_m, 0_m}; + auto builder = environment::make_layered_spherical_atmosphere_builder< + setup::EnvironmentInterface, + MyExtraEnv>::create(center, units::constants::EarthRadius::Mean, + environment::Medium::AirDry1Atm, + geometry::Vector{rootCS, 0_T, 0_T, 1_T}); + builder.setNuclearComposition( + {{particles::Code::Nitrogen, particles::Code::Oxygen}, + {0.7847f, 1.f - 0.7847f}}); // values taken from AIRES manual, Ar removed for now + + builder.addExponentialLayer(1222.6562_g / (1_cm * 1_cm), 994186.38_cm, 4_km); + builder.addExponentialLayer(1144.9069_g / (1_cm * 1_cm), 878153.55_cm, 10_km); + builder.addExponentialLayer(1305.5948_g / (1_cm * 1_cm), 636143.04_cm, 40_km); + builder.addExponentialLayer(540.1778_g / (1_cm * 1_cm), 772170.16_cm, 100_km); + builder.addLinearLayer(1e9_cm, 112.8_km); + builder.assemble(env); + + // setup particle stack, and add primary particle + setup::Stack stack; + stack.Clear(); + const Code beamCode = Code::Electron; + auto const mass = particles::GetMass(beamCode); + const HEPEnergyType E0 = 1_GeV * std::stof(std::string(argv[1])); + double theta = 0.; + auto const thetaRad = theta / 180. * M_PI; + + auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) { + return sqrt((Elab - m) * (Elab + m)); + }; + HEPMomentumType P0 = elab2plab(E0, mass); + auto momentumComponents = [](double thetaRad, HEPMomentumType ptot) { + return std::make_tuple(ptot * sin(thetaRad), 0_eV, -ptot * cos(thetaRad)); + }; + + auto const [px, py, pz] = momentumComponents(thetaRad, P0); + auto plab = corsika::stack::MomentumVector(rootCS, {px, py, pz}); + cout << "input particle: " << beamCode << endl; + cout << "input angles: theta=" << theta << endl; + cout << "input momentum: " << plab.GetComponents() / 1_GeV << ", norm = " << plab.norm() + << endl; + + auto const observationHeight = 1.4_km + builder.getEarthRadius(); + auto const injectionHeight = 112.75_km + builder.getEarthRadius(); + auto const t = -observationHeight * cos(thetaRad) + + sqrt(-static_pow<2>(sin(thetaRad) * observationHeight) + + static_pow<2>(injectionHeight)); + Point const showerCore{rootCS, 0_m, 0_m, observationHeight}; + Point const injectionPos = + showerCore + + Vector<dimensionless_d>{rootCS, {-sin(thetaRad), 0, cos(thetaRad)}} * t; + + std::cout << "point of injection: " << injectionPos.GetCoordinates() << std::endl; + + stack.AddParticle( + std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{ + beamCode, E0, plab, injectionPos, 0_ns}); + + std::cout << "shower axis length: " << (showerCore - injectionPos).norm() * 1.02 + << std::endl; + + environment::ShowerAxis const showerAxis{injectionPos, + (showerCore - injectionPos) * 1.02, env}; + + // setup processes, decays and interactions + + // PROPOSAL processs proposal{...}; + process::particle_cut::ParticleCut cut(10_GeV, false, true); + process::proposal::Interaction proposal(env, cut.GetECut()); + process::proposal::ContinuousProcess em_continuous(env, cut.GetECut()); + process::interaction_counter::InteractionCounter proposalCounted(proposal); + + process::track_writer::TrackWriter trackWriter("tracks.dat"); + + // long. profile; columns for gamma, e+, e- still need to be added + process::longitudinal_profile::LongitudinalProfile longprof{showerAxis}; + + Plane const obsPlane(showerCore, Vector<dimensionless_d>(rootCS, {0., 0., 1.})); + process::observation_plane::ObservationPlane observationLevel(obsPlane, + "particles.dat"); + + auto sequence = process::sequence(proposalCounted, em_continuous, longprof, cut, + observationLevel, trackWriter); + // define air shower object, run simulation + tracking_line::TrackingLine tracking; + cascade::Cascade EAS(env, tracking, sequence, stack); + + // to fix the point of first interaction, uncomment the following two lines: + // EAS.SetNodes(); + // EAS.forceInteraction(); + + EAS.Run(); + + cut.ShowResults(); + em_continuous.showResults(); + observationLevel.ShowResults(); + const HEPEnergyType Efinal = cut.GetCutEnergy() + cut.GetInvEnergy() + + cut.GetEmEnergy() + em_continuous.energyLost() + + observationLevel.GetEnergyGround(); + cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl + << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl; + observationLevel.Reset(); + cut.Reset(); + em_continuous.reset(); + + auto const hists = proposalCounted.GetHistogram(); + hists.saveLab("inthist_lab_emShower.npz"); + hists.saveCMS("inthist_cms_emShower.npz"); + longprof.save("longprof_emShower.txt"); +} diff --git a/examples/hybrid_MC.cpp b/examples/hybrid_MC.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c6647209bb13d4843020c2bce4d0354486f97753 --- /dev/null +++ b/examples/hybrid_MC.cpp @@ -0,0 +1,281 @@ +/* + * (c) Copyright 2018 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. + */ + +/* clang-format off */ +// InteractionCounter used boost/histogram, which +// fails if boost/type_traits have been included before. Thus, we have +// to include it first... +#include <corsika/process/interaction_counter/InteractionCounter.hpp> +/* clang-format on */ +#include <corsika/cascade/Cascade.h> +#include <corsika/environment/Environment.h> +#include <corsika/environment/FlatExponential.h> +#include <corsika/environment/LayeredSphericalAtmosphereBuilder.h> +#include <corsika/environment/NuclearComposition.h> +#include <corsika/environment/ShowerAxis.h> +#include <corsika/geometry/Plane.h> +#include <corsika/geometry/Sphere.h> +#include <corsika/logging/Logging.h> +#include <corsika/process/ProcessSequence.h> +#include <corsika/process/SwitchProcessSequence.h> +#include <corsika/process/StackProcess.h> +#include <corsika/process/conex_source_cut/CONEXSourceCut.h> +#include <corsika/process/energy_loss/EnergyLoss.h> +#include <corsika/process/longitudinal_profile/LongitudinalProfile.h> +#include <corsika/process/observation_plane/ObservationPlane.h> +#include <corsika/process/on_shell_check/OnShellCheck.h> +#include <corsika/process/particle_cut/ParticleCut.h> +#include <corsika/process/pythia/Decay.h> +#include <corsika/process/sibyll/Decay.h> +#include <corsika/process/sibyll/Interaction.h> +#include <corsika/process/sibyll/NuclearInteraction.h> +#include <corsika/process/tracking_line/TrackingLine.h> +#include <corsika/process/urqmd/UrQMD.h> +#include <corsika/random/RNGManager.h> +#include <corsika/setup/SetupStack.h> +#include <corsika/setup/SetupTrajectory.h> +#include <corsika/units/PhysicalUnits.h> +#include <corsika/utl/CorsikaFenv.h> + +#include <iomanip> +#include <iostream> +#include <limits> +#include <string> + +using namespace corsika; +using namespace corsika::process; +using namespace corsika::units; +using namespace corsika::particles; +using namespace corsika::random; +using namespace corsika::geometry; +using namespace corsika::environment; + +using namespace std; +using namespace corsika::units::si; + +void registerRandomStreams(const int seed) { + random::RNGManager::GetInstance().RegisterRandomStream("cascade"); + random::RNGManager::GetInstance().RegisterRandomStream("qgsjet"); + random::RNGManager::GetInstance().RegisterRandomStream("sibyll"); + random::RNGManager::GetInstance().RegisterRandomStream("pythia"); + random::RNGManager::GetInstance().RegisterRandomStream("urqmd"); + random::RNGManager::GetInstance().RegisterRandomStream("proposal"); + + if (seed == 0) + random::RNGManager::GetInstance().SeedAll(); + else + random::RNGManager::GetInstance().SeedAll(seed); +} + +template <typename T> +using MyExtraEnv = environment::MediumPropertyModel<environment::UniformMagneticField<T>>; + +int main(int argc, char** argv) { + + logging::SetLevel(logging::level::info); + + C8LOG_INFO("hybrid_MC"); + + if (argc < 4) { + std::cerr << "usage: hybrid_MC <A> <Z> <energy/GeV> [seed]" << std::endl; + std::cerr << " if no seed is given, a random seed is chosen" << std::endl; + return 1; + } + feenableexcept(FE_INVALID); + + int seed = 0; + if (argc > 4) seed = std::stoi(std::string(argv[4])); + // initialize random number sequence(s) + registerRandomStreams(seed); + + // setup environment, geometry + using EnvType = setup::Environment; + EnvType env; + const CoordinateSystem& rootCS = env.GetCoordinateSystem(); + Point const center{rootCS, 0_m, 0_m, 0_m}; + auto builder = environment::make_layered_spherical_atmosphere_builder< + setup::EnvironmentInterface, + MyExtraEnv>::create(center, units::constants::EarthRadius::Mean, + environment::Medium::AirDry1Atm, + geometry::Vector{rootCS, 0_T, 0_T, 1_T}); + builder.setNuclearComposition( + {{particles::Code::Nitrogen, particles::Code::Oxygen}, + {0.7847f, 1.f - 0.7847f}}); // values taken from AIRES manual, Ar removed for now + + builder.addExponentialLayer(1222.6562_g / (1_cm * 1_cm), 994186.38_cm, 4_km); + builder.addExponentialLayer(1144.9069_g / (1_cm * 1_cm), 878153.55_cm, 10_km); + builder.addExponentialLayer(1305.5948_g / (1_cm * 1_cm), 636143.04_cm, 40_km); + builder.addExponentialLayer(540.1778_g / (1_cm * 1_cm), 772170.16_cm, 100_km); + builder.addLinearLayer(1e9_cm, 112.8_km); + builder.assemble(env); + + // setup particle stack, and add primary particle + setup::Stack stack; + stack.Clear(); + const Code beamCode = Code::Nucleus; + unsigned short const A = std::stoi(std::string(argv[1])); + unsigned short Z = std::stoi(std::string(argv[2])); + auto const mass = particles::GetNucleusMass(A, Z); + const HEPEnergyType E0 = 1_GeV * std::stof(std::string(argv[3])); + double theta = 0.; + auto const thetaRad = theta / 180. * M_PI; + + auto elab2plab = [](HEPEnergyType Elab, HEPMassType m) { + return sqrt((Elab - m) * (Elab + m)); + }; + HEPMomentumType P0 = elab2plab(E0, mass); + auto momentumComponents = [](double thetaRad, HEPMomentumType ptot) { + return std::make_tuple(ptot * sin(thetaRad), 0_eV, -ptot * cos(thetaRad)); + }; + + auto const [px, py, pz] = momentumComponents(thetaRad, P0); + auto plab = corsika::stack::MomentumVector(rootCS, {px, py, pz}); + cout << "input particle: " << beamCode << endl; + cout << "input angles: theta=" << theta << endl; + cout << "input momentum: " << plab.GetComponents() / 1_GeV << ", norm = " << plab.norm() + << endl; + + auto const observationHeight = 0_km + builder.getEarthRadius(); + auto const injectionHeight = 112.75_km + builder.getEarthRadius(); + auto const t = -observationHeight * cos(thetaRad) + + sqrt(-units::static_pow<2>(sin(thetaRad) * observationHeight) + + units::static_pow<2>(injectionHeight)); + Point const showerCore{rootCS, 0_m, 0_m, observationHeight}; + Point const injectionPos = + showerCore + + Vector<dimensionless_d>{rootCS, {-sin(thetaRad), 0, cos(thetaRad)}} * t; + + std::cout << "point of injection: " << injectionPos.GetCoordinates() << std::endl; + + if (A != 1) { + stack.AddParticle(std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, + units::si::TimeType, unsigned short, unsigned short>{ + beamCode, E0, plab, injectionPos, 0_ns, A, Z}); + + } else { + stack.AddParticle( + std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, units::si::TimeType>{ + particles::Code::Proton, E0, plab, injectionPos, 0_ns}); + } + + std::cout << "shower axis length: " << (showerCore - injectionPos).norm() * 1.02 + << std::endl; + + environment::ShowerAxis const showerAxis{injectionPos, + (showerCore - injectionPos) * 1.02, env}; + + // setup processes, decays and interactions + + process::sibyll::Interaction sibyll; + process::interaction_counter::InteractionCounter sibyllCounted(sibyll); + + process::sibyll::NuclearInteraction sibyllNuc(sibyll, env); + process::interaction_counter::InteractionCounter sibyllNucCounted(sibyllNuc); + + process::pythia::Decay decayPythia; + + // use sibyll decay routine for decays of particles unknown to pythia + process::sibyll::Decay decaySibyll{{ + Code::N1440Plus, + Code::N1440MinusBar, + Code::N1440_0, + Code::N1440_0Bar, + Code::N1710Plus, + Code::N1710MinusBar, + Code::N1710_0, + Code::N1710_0Bar, + + Code::Pi1300Plus, + Code::Pi1300Minus, + Code::Pi1300_0, + + Code::KStar0_1430_0, + Code::KStar0_1430_0Bar, + Code::KStar0_1430_Plus, + Code::KStar0_1430_MinusBar, + }}; + + decaySibyll.PrintDecayConfig(); + + process::particle_cut::ParticleCut cut{60_GeV, false, true}; + process::energy_loss::EnergyLoss eLoss{showerAxis, cut.GetECut()}; + + corsika::process::conex_source_cut::CONEXSourceCut conex( + center, showerAxis, t, injectionHeight, E0, + particles::GetPDG(particles::Code::Proton)); + + process::on_shell_check::OnShellCheck reset_particle_mass(1.e-3, 1.e-1, false); + + process::longitudinal_profile::LongitudinalProfile longprof{showerAxis}; + + Plane const obsPlane(showerCore, Vector<dimensionless_d>(rootCS, {0., 0., 1.})); + process::observation_plane::ObservationPlane observationLevel(obsPlane, + "particles.dat"); + + process::UrQMD::UrQMD urqmd; + process::interaction_counter::InteractionCounter urqmdCounted{urqmd}; + + // assemble all processes into an ordered process list + struct EnergySwitch { + HEPEnergyType cutE_; + EnergySwitch(HEPEnergyType cutE) + : cutE_(cutE) {} + process::SwitchResult operator()(const setup::Stack::ParticleType& p) { + if (p.GetEnergy() < cutE_) + return process::SwitchResult::First; + else + return process::SwitchResult::Second; + } + }; + auto hadronSequence = + process::select(urqmdCounted, process::sequence(sibyllNucCounted, sibyllCounted), + EnergySwitch(55_GeV)); + auto decaySequence = process::sequence(decayPythia, decaySibyll); + auto sequence = process::sequence(hadronSequence, reset_particle_mass, decaySequence, + eLoss, cut, conex, longprof, observationLevel); + + // define air shower object, run simulation + tracking_line::TrackingLine tracking; + cascade::Cascade EAS(env, tracking, sequence, stack); + + // to fix the point of first interaction, uncomment the following two lines: + // EAS.SetNodes(); + // EAS.forceInteraction(); + + EAS.Run(); + + cut.ShowResults(); + eLoss.showResults(); + observationLevel.ShowResults(); + const HEPEnergyType Efinal = cut.GetCutEnergy() + cut.GetInvEnergy() + + cut.GetEmEnergy() + eLoss.energyLost() + + observationLevel.GetEnergyGround(); + cout << "total cut energy (GeV): " << Efinal / 1_GeV << endl + << "relative difference (%): " << (Efinal / E0 - 1) * 100 << endl; + observationLevel.Reset(); + cut.Reset(); + eLoss.reset(); + + conex.SolveCE(); + + auto const hists = sibyllCounted.GetHistogram() + sibyllNucCounted.GetHistogram() + + urqmdCounted.GetHistogram(); + + hists.saveLab("inthist_lab.txt"); + hists.saveCMS("inthist_cms.txt"); + + hists.saveLab("inthist_lab.txt"); + hists.saveCMS("inthist_cms.txt"); + + longprof.save("longprof.txt"); + + std::ofstream finish("finished"); + finish << "run completed without error" << std::endl; +} diff --git a/examples/particle_list_example.cpp b/examples/particle_list_example.cpp new file mode 100644 index 0000000000000000000000000000000000000000..1280398ab6cafd8aabcae8785be75432124f93d3 --- /dev/null +++ b/examples/particle_list_example.cpp @@ -0,0 +1,59 @@ +/* + * (c) Copyright 2018 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 <corsika/framework/core/ParticleProperties.hpp> +#include <corsika/modules/QGSJetII.hpp> +#include <corsika/modules/Sibyll.hpp> +#include <corsika/setup/SetupEnvironment.hpp> +#include <corsika/framework/core/PhysicalUnits.hpp> + +#include <iomanip> +#include <string> + +using namespace corsika; +using namespace std; + +// +// The example main program for a particle list +// +int main() { + + logging::set_level(spdlog::level::info); + + logging::info( + "------------------------------------------\n" + "particles in CORSIKA\n" + "------------------------------------------\n" + "Name | " + "PDG-id | " + "SIBYLL-id | " + "QGSJETII-id| " + "PDG-mass (GeV) | " + "SIBYLL-mass (GeV)|\n" + "{:-}", + "", 104); + for (auto p : get_all_particles()) { + if (!is_nucleus(p)) { + corsika::sibyll::SibyllCode sib_id = corsika::sibyll::convertToSibyll(p); + auto const sib_mass = (sib_id != corsika::sibyll::SibyllCode::Unknown + ? to_string(corsika::sibyll::getSibyllMass(p) / 1_GeV) + : "--"); + auto const qgs_id = corsika::qgsjetII::convertToQgsjetII(p); + logging::info("{:20} | {:10} | {:10} | {:10.5} | {:18.5}", p, + static_cast<int>(get_PDG(p)), + (sib_id != corsika::sibyll::SibyllCode::Unknown + ? to_string(static_cast<int>(sib_id)) + : "--"), + (qgs_id != corsika::qgsjetII::QgsjetIICode::Unknown + ? to_string(static_cast<int>(qgs_id)) + : "--"), + get_mass(p) / 1_GeV, sib_mass); + } + } + logging::info("{:-104}", ""); +} diff --git a/tests/common/SetupEnvironment.hpp b/tests/common/SetupEnvironment.hpp new file mode 100644 index 0000000000000000000000000000000000000000..69d1f6233cb390cb074347bcdd7a3a1cbbdcb61e --- /dev/null +++ b/tests/common/SetupEnvironment.hpp @@ -0,0 +1,45 @@ +#include <corsika/media/HomogeneousMedium.hpp> +#include <corsika/media/InhomogeneousMedium.hpp> +#include <corsika/media/MediumPropertyModel.hpp> +#include <corsika/media/UniformMagneticField.hpp> + +#include <tuple> +#include <memory> + +/** + * \function setup_environment + * + * standard environment for unit testing. + * + */ +namespace corsika::setup::testing { + + inline std::tuple<std::unique_ptr<setup::Environment>, CoordinateSystem const*, + setup::Environment::BaseNodeType const*> + setup_environment(Code vTargetCode) { + auto env = std::make_unique<setup::Environment>(); + auto& universe = *(env->getUniverse()); + CoordinateSystemPtr const& cs = env->getCoordinateSystem(); + + /** + * our world is a sphere at 0,0,0 with R=infty + */ + auto world = setup::Environment::createNode<Sphere>( + Point{cs, 0_m, 0_m, 0_m}, 1_km * std::numeric_limits<double>::infinity()); + + /** + * construct suited environment medium model: + */ + using MyHomogeneousModel = MediumPropertyModel< + UniformMagneticField<HomogeneousMedium<setup::EnvironmentInterface>>>; + + world->setModelProperties<MyHomogeneousModel>( + Medium::AirDry1Atm, Vector(cs, 0_T, 0_T, 1_T), 1_kg / (1_m * 1_m * 1_m), + NuclearComposition(std::vector<Code>{vTargetCode}, std::vector<float>{1.})); + + setup::Environment::BaseNodeType const* nodePtr = world.get(); + universe.addChild(std::move(world)); + + return std::make_tuple(std::move(env), &cs, nodePtr); + } +} // namespace corsika::setup::testing diff --git a/tests/common/SetupTestEnvironment.hpp b/tests/common/SetupTestEnvironment.hpp new file mode 100644 index 0000000000000000000000000000000000000000..50d5561dc38021bfeed55f3f2ae0a69613069785 --- /dev/null +++ b/tests/common/SetupTestEnvironment.hpp @@ -0,0 +1,45 @@ +#pragma once + +#include <corsika/framework/geometry/Point.hpp> +#include <corsika/framework/geometry/CoordinateSystem.hpp> + +#include <corsika/setup/SetupEnvironment.hpp> +#include <corsika/media/UniformMagneticField.hpp> +#include <corsika/media/MediumPropertyModel.hpp> +#include <corsika/media/HomogeneousMedium.hpp> + +#include <limits> + +namespace corsika::setup::testing { + + inline std::tuple<std::unique_ptr<setup::Environment>, CoordinateSystemPtr const*, + setup::Environment::BaseNodeType const*> + setup_environment(Code vTargetCode) { + + auto env = std::make_unique<setup::Environment>(); + auto& universe = *(env->getUniverse()); + CoordinateSystemPtr const& cs = env->getCoordinateSystem(); + + /** + * our world is a sphere at 0,0,0 with R=infty + */ + auto world = setup::Environment::createNode<Sphere>( + Point{cs, 0_m, 0_m, 0_m}, 1_km * std::numeric_limits<double>::infinity()); + + /** + * construct suited environment medium model: + */ + using MyHomogeneousModel = MediumPropertyModel< + UniformMagneticField<HomogeneousMedium<setup::EnvironmentInterface>>>; + + world->setModelProperties<MyHomogeneousModel>( + Medium::AirDry1Atm, Vector(cs, 0_T, 0_T, 1_T), 1_kg / (1_m * 1_m * 1_m), + NuclearComposition(std::vector<Code>{vTargetCode}, std::vector<float>{1.})); + + setup::Environment::BaseNodeType const* nodePtr = world.get(); + universe.addChild(std::move(world)); + + return std::make_tuple(std::move(env), &cs, nodePtr); + } + +} // namespace corsika::setup::testing diff --git a/tests/common/SetupTestStack.hpp b/tests/common/SetupTestStack.hpp new file mode 100644 index 0000000000000000000000000000000000000000..35103012b2865aeab9f8af8a4a2bbc4ec43881a9 --- /dev/null +++ b/tests/common/SetupTestStack.hpp @@ -0,0 +1,56 @@ +#pragma once + +#include <corsika/framework/geometry/Point.hpp> +#include <corsika/framework/geometry/RootCoordinateSystem.hpp> +#include <corsika/framework/geometry/Vector.hpp> + +/** + * \file SetupTestStack + * + * standard stack setup for unit tests. + **/ + +namespace corsika::setup::testing { + + /** + * \function setup_stack + * + * standard stack setup for unit tests. + * + * + * + * + * \return a tuple with element 0 being a Stack object filled with + * one particle, and element 1 the StackView on it. + **/ + + inline std::tuple<std::unique_ptr<setup::Stack>, std::unique_ptr<setup::StackView>> + setup_stack(Code vProjectileType, int vA, int vZ, HEPEnergyType vMomentum, + setup::Environment::BaseNodeType* const vNodePtr, + CoordinateSystemPtr const& cs) { + + auto stack = std::make_unique<setup::Stack>(); + + Point const origin(cs, {0_m, 0_m, 0_m}); + MomentumVector const pLab(cs, {vMomentum, 0_GeV, 0_GeV}); + + if (vProjectileType == Code::Nucleus) { + auto constexpr mN = constants::nucleonMass; + HEPEnergyType const E0 = sqrt(static_pow<2>(mN * vA) + pLab.getSquaredNorm()); + auto particle = stack->addParticle( + std::make_tuple(Code::Nucleus, E0, pLab, origin, 0_ns, vA, vZ)); + particle.setNode(vNodePtr); + return std::make_tuple(std::move(stack), + std::make_unique<setup::StackView>(particle)); + } else { // not a nucleus + HEPEnergyType const E0 = + sqrt(static_pow<2>(get_mass(vProjectileType)) + pLab.getSquaredNorm()); + auto particle = + stack->addParticle(std::make_tuple(vProjectileType, E0, pLab, origin, 0_ns)); + particle.setNode(vNodePtr); + return std::make_tuple(std::move(stack), + std::make_unique<setup::StackView>(particle)); + } + } + +} // namespace corsika::setup::testing diff --git a/tests/framework/CMakeCache.txt b/tests/framework/CMakeCache.txt new file mode 100644 index 0000000000000000000000000000000000000000..792df53c17d42a1b2a9f008c4c4450be089dbfec --- /dev/null +++ b/tests/framework/CMakeCache.txt @@ -0,0 +1,342 @@ +# This is the CMakeCache file. +# For build in directory: /home/rulrich/work/corsika/corsika/tests/framework +# It was generated by CMake: /usr/bin/cmake +# You can edit this file to change values found and used by cmake. +# If you do not want to change any of the values, simply exit the editor. +# If you do want to change a value, simply edit, save, and exit the editor. +# The syntax for the file is as follows: +# KEY:TYPE=VALUE +# KEY is the name of a variable in the cache. +# TYPE is a hint to GUIs for the type of VALUE, DO NOT EDIT TYPE!. +# VALUE is the current value for the KEY. + +######################## +# EXTERNAL cache entries +######################## + +//Path to a program. +CMAKE_AR:FILEPATH=/usr/bin/ar + +//For backwards compatibility, what version of CMake commands and +// syntax should this version of CMake try to support. +CMAKE_BACKWARDS_COMPATIBILITY:STRING=2.4 + +//Choose the type of build, options are: None(CMAKE_CXX_FLAGS or +// CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel. +CMAKE_BUILD_TYPE:STRING= + +//Enable/Disable color output during build. +CMAKE_COLOR_MAKEFILE:BOOL=ON + +//CXX compiler +CMAKE_CXX_COMPILER:FILEPATH=/usr/bin/c++ + +//A wrapper around 'ar' adding the appropriate '--plugin' option +// for the GCC compiler +CMAKE_CXX_COMPILER_AR:FILEPATH=/usr/bin/gcc-ar-7 + +//A wrapper around 'ranlib' adding the appropriate '--plugin' option +// for the GCC compiler +CMAKE_CXX_COMPILER_RANLIB:FILEPATH=/usr/bin/gcc-ranlib-7 + +//Flags used by the compiler during all build types. +CMAKE_CXX_FLAGS:STRING= + +//Flags used by the compiler during debug builds. +CMAKE_CXX_FLAGS_DEBUG:STRING=-g + +//Flags used by the compiler during release builds for minimum +// size. +CMAKE_CXX_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG + +//Flags used by the compiler during release builds. +CMAKE_CXX_FLAGS_RELEASE:STRING=-O3 -DNDEBUG + +//Flags used by the compiler during release builds with debug info. +CMAKE_CXX_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG + +//C compiler +CMAKE_C_COMPILER:FILEPATH=/usr/bin/cc + +//A wrapper around 'ar' adding the appropriate '--plugin' option +// for the GCC compiler +CMAKE_C_COMPILER_AR:FILEPATH=/usr/bin/gcc-ar-7 + +//A wrapper around 'ranlib' adding the appropriate '--plugin' option +// for the GCC compiler +CMAKE_C_COMPILER_RANLIB:FILEPATH=/usr/bin/gcc-ranlib-7 + +//Flags used by the compiler during all build types. +CMAKE_C_FLAGS:STRING= + +//Flags used by the compiler during debug builds. +CMAKE_C_FLAGS_DEBUG:STRING=-g + +//Flags used by the compiler during release builds for minimum +// size. +CMAKE_C_FLAGS_MINSIZEREL:STRING=-Os -DNDEBUG + +//Flags used by the compiler during release builds. +CMAKE_C_FLAGS_RELEASE:STRING=-O3 -DNDEBUG + +//Flags used by the compiler during release builds with debug info. +CMAKE_C_FLAGS_RELWITHDEBINFO:STRING=-O2 -g -DNDEBUG + +//Flags used by the linker. +CMAKE_EXE_LINKER_FLAGS:STRING= + +//Flags used by the linker during debug builds. +CMAKE_EXE_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during release minsize builds. +CMAKE_EXE_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during release builds. +CMAKE_EXE_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Enable/Disable output of compile commands during generation. +CMAKE_EXPORT_COMPILE_COMMANDS:BOOL=OFF + +//Install path prefix, prepended onto install directories. +CMAKE_INSTALL_PREFIX:PATH=/usr/local + +//Path to a program. +CMAKE_LINKER:FILEPATH=/usr/bin/ld + +//Path to a program. +CMAKE_MAKE_PROGRAM:FILEPATH=/usr/bin/make + +//Flags used by the linker during the creation of modules. +CMAKE_MODULE_LINKER_FLAGS:STRING= + +//Flags used by the linker during debug builds. +CMAKE_MODULE_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during release minsize builds. +CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during release builds. +CMAKE_MODULE_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Path to a program. +CMAKE_NM:FILEPATH=/usr/bin/nm + +//Path to a program. +CMAKE_OBJCOPY:FILEPATH=/usr/bin/objcopy + +//Path to a program. +CMAKE_OBJDUMP:FILEPATH=/usr/bin/objdump + +//Value Computed by CMake +CMAKE_PROJECT_NAME:STATIC=Project + +//Path to a program. +CMAKE_RANLIB:FILEPATH=/usr/bin/ranlib + +//Flags used by the linker during the creation of dll's. +CMAKE_SHARED_LINKER_FLAGS:STRING= + +//Flags used by the linker during debug builds. +CMAKE_SHARED_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during release minsize builds. +CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during release builds. +CMAKE_SHARED_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//If set, runtime paths are not added when installing shared libraries, +// but are added when building. +CMAKE_SKIP_INSTALL_RPATH:BOOL=NO + +//If set, runtime paths are not added when using shared libraries. +CMAKE_SKIP_RPATH:BOOL=NO + +//Flags used by the linker during the creation of static libraries. +CMAKE_STATIC_LINKER_FLAGS:STRING= + +//Flags used by the linker during debug builds. +CMAKE_STATIC_LINKER_FLAGS_DEBUG:STRING= + +//Flags used by the linker during release minsize builds. +CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL:STRING= + +//Flags used by the linker during release builds. +CMAKE_STATIC_LINKER_FLAGS_RELEASE:STRING= + +//Flags used by the linker during Release with Debug Info builds. +CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO:STRING= + +//Path to a program. +CMAKE_STRIP:FILEPATH=/usr/bin/strip + +//If this value is on, makefiles will be generated without the +// .SILENT directive, and all commands will be echoed to the console +// during the make. This is useful for debugging only. With Visual +// Studio IDE projects all commands are done without /nologo. +CMAKE_VERBOSE_MAKEFILE:BOOL=FALSE + +//Single output directory for building all executables. +EXECUTABLE_OUTPUT_PATH:PATH= + +//Single output directory for building all libraries. +LIBRARY_OUTPUT_PATH:PATH= + +//Value Computed by CMake +Project_BINARY_DIR:STATIC=/home/rulrich/work/corsika/corsika/tests/framework + +//Value Computed by CMake +Project_SOURCE_DIR:STATIC=/home/rulrich/work/corsika/corsika/tests + + +######################## +# INTERNAL cache entries +######################## + +//ADVANCED property for variable: CMAKE_AR +CMAKE_AR-ADVANCED:INTERNAL=1 +//This is the directory where this CMakeCache.txt was created +CMAKE_CACHEFILE_DIR:INTERNAL=/home/rulrich/work/corsika/corsika/tests/framework +//Major version of cmake used to create the current loaded cache +CMAKE_CACHE_MAJOR_VERSION:INTERNAL=3 +//Minor version of cmake used to create the current loaded cache +CMAKE_CACHE_MINOR_VERSION:INTERNAL=10 +//Patch version of cmake used to create the current loaded cache +CMAKE_CACHE_PATCH_VERSION:INTERNAL=2 +//ADVANCED property for variable: CMAKE_COLOR_MAKEFILE +CMAKE_COLOR_MAKEFILE-ADVANCED:INTERNAL=1 +//Path to CMake executable. +CMAKE_COMMAND:INTERNAL=/usr/bin/cmake +//Path to cpack program executable. +CMAKE_CPACK_COMMAND:INTERNAL=/usr/bin/cpack +//Path to ctest program executable. +CMAKE_CTEST_COMMAND:INTERNAL=/usr/bin/ctest +//ADVANCED property for variable: CMAKE_CXX_COMPILER +CMAKE_CXX_COMPILER-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_COMPILER_AR +CMAKE_CXX_COMPILER_AR-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_COMPILER_RANLIB +CMAKE_CXX_COMPILER_RANLIB-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS +CMAKE_CXX_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_DEBUG +CMAKE_CXX_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_MINSIZEREL +CMAKE_CXX_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELEASE +CMAKE_CXX_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_CXX_FLAGS_RELWITHDEBINFO +CMAKE_CXX_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_COMPILER +CMAKE_C_COMPILER-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_COMPILER_AR +CMAKE_C_COMPILER_AR-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_COMPILER_RANLIB +CMAKE_C_COMPILER_RANLIB-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS +CMAKE_C_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_DEBUG +CMAKE_C_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_MINSIZEREL +CMAKE_C_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_RELEASE +CMAKE_C_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_C_FLAGS_RELWITHDEBINFO +CMAKE_C_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//Path to cache edit program executable. +CMAKE_EDIT_COMMAND:INTERNAL=/usr/bin/ccmake +//Executable file format +CMAKE_EXECUTABLE_FORMAT:INTERNAL=ELF +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS +CMAKE_EXE_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_DEBUG +CMAKE_EXE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_MINSIZEREL +CMAKE_EXE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELEASE +CMAKE_EXE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_EXPORT_COMPILE_COMMANDS +CMAKE_EXPORT_COMPILE_COMMANDS-ADVANCED:INTERNAL=1 +//Name of external makefile project generator. +CMAKE_EXTRA_GENERATOR:INTERNAL= +//Name of generator. +CMAKE_GENERATOR:INTERNAL=Unix Makefiles +//Name of generator platform. +CMAKE_GENERATOR_PLATFORM:INTERNAL= +//Name of generator toolset. +CMAKE_GENERATOR_TOOLSET:INTERNAL= +//Source directory with the top level CMakeLists.txt file for this +// project +CMAKE_HOME_DIRECTORY:INTERNAL=/home/rulrich/work/corsika/corsika/tests +//Install .so files without execute permission. +CMAKE_INSTALL_SO_NO_EXE:INTERNAL=1 +//ADVANCED property for variable: CMAKE_LINKER +CMAKE_LINKER-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MAKE_PROGRAM +CMAKE_MAKE_PROGRAM-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS +CMAKE_MODULE_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_DEBUG +CMAKE_MODULE_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL +CMAKE_MODULE_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELEASE +CMAKE_MODULE_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_NM +CMAKE_NM-ADVANCED:INTERNAL=1 +//number of local generators +CMAKE_NUMBER_OF_MAKEFILES:INTERNAL=3 +//ADVANCED property for variable: CMAKE_OBJCOPY +CMAKE_OBJCOPY-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_OBJDUMP +CMAKE_OBJDUMP-ADVANCED:INTERNAL=1 +//Platform information initialized +CMAKE_PLATFORM_INFO_INITIALIZED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_RANLIB +CMAKE_RANLIB-ADVANCED:INTERNAL=1 +//Path to CMake installation. +CMAKE_ROOT:INTERNAL=/usr/share/cmake-3.10 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS +CMAKE_SHARED_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_DEBUG +CMAKE_SHARED_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL +CMAKE_SHARED_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELEASE +CMAKE_SHARED_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SKIP_INSTALL_RPATH +CMAKE_SKIP_INSTALL_RPATH-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_SKIP_RPATH +CMAKE_SKIP_RPATH-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS +CMAKE_STATIC_LINKER_FLAGS-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_DEBUG +CMAKE_STATIC_LINKER_FLAGS_DEBUG-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL +CMAKE_STATIC_LINKER_FLAGS_MINSIZEREL-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELEASE +CMAKE_STATIC_LINKER_FLAGS_RELEASE-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO +CMAKE_STATIC_LINKER_FLAGS_RELWITHDEBINFO-ADVANCED:INTERNAL=1 +//ADVANCED property for variable: CMAKE_STRIP +CMAKE_STRIP-ADVANCED:INTERNAL=1 +//uname command +CMAKE_UNAME:INTERNAL=/bin/uname +//ADVANCED property for variable: CMAKE_VERBOSE_MAKEFILE +CMAKE_VERBOSE_MAKEFILE-ADVANCED:INTERNAL=1 + diff --git a/tests/framework/testSwitchProcessSequence.cpp b/tests/framework/testSwitchProcessSequence.cpp new file mode 100644 index 0000000000000000000000000000000000000000..a778f2efb5dd587b3fc2341dbb7cb5e205c671ab --- /dev/null +++ b/tests/framework/testSwitchProcessSequence.cpp @@ -0,0 +1,247 @@ +/* + * (c) Copyright 2018 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 <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 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>> { + + 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") { + CHECK(switchProcess.GetInteractionLength(p) / kgMSq == Approx(1)); + CHECK(seq.GetInteractionLength(p) / kgMSq == Approx(3. / 4)); + } + } + + SECTION("high energy") { + stack.AddParticle(std::tuple<HEPEnergyType>{4_TeV}); + auto p = stack.GetNextParticle(); + + // high energy process returns 2 kg/m² + SECTION("interaction length") { + CHECK(switchProcess.GetInteractionLength(p) / kgMSq == Approx(2)); + CHECK(seq.GetInteractionLength(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); + + CHECK(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") { + CHECK(switchProcess.GetInteractionLength(p) / kgMSq == Approx(2. / 3)); + CHECK(completeSeq.GetInteractionLength(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(); + CHECK(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") { + CHECK(switchProcess.GetInteractionLength(p) / kgMSq == Approx(3)); + CHECK(completeSeq.GetInteractionLength(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(); + CHECK(mean == Approx(24. / 7.).margin(0.01)); + } + } +} diff --git a/tests/media/testMagneticField.cpp b/tests/media/testMagneticField.cpp new file mode 100644 index 0000000000000000000000000000000000000000..da0a1a5d80e8702d0533c4d132db95c16a984d4a --- /dev/null +++ b/tests/media/testMagneticField.cpp @@ -0,0 +1,87 @@ +/* + * (c) Copyright 2020 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 <corsika/framework/core/ParticleProperties.hpp> +#include <corsika/framework/core/PhysicalUnits.hpp> +#include <corsika/framework/geometry/RootCoordinateSystem.hpp> +#include <corsika/media/HomogeneousMedium.hpp> +#include <corsika/media/IMediumModel.hpp> +#include <corsika/media/UniformMagneticField.hpp> +#include <corsika/media/IMagneticFieldModel.hpp> +#include <corsika/media/VolumeTreeNode.hpp> + +#include <catch2/catch.hpp> + +using namespace corsika; + + +TEST_CASE("UniformMagneticField w/ Homogeneous Medium") { + + CoordinateSystemPtr const& gCS = get_root_CoordinateSystem(); + Point const gOrigin(gCS, {0_m, 0_m, 0_m}); + + + // setup our interface types + using IModelInterface = IMagneticFieldModel<IMediumModel>; + using AtmModel = UniformMagneticField<HomogeneousMedium<IModelInterface>>; + + // the composition we use for the homogenous medium + NuclearComposition const protonComposition(std::vector<Code>{Code::Proton}, + std::vector<float>{1.f}); + + // create a magnetic field vector + Vector B0(gCS, 0_T, 0_T, 0_T); + + // the constant density + const auto density{19.2_g / cube(1_cm)}; + + // create our atmospheric model + AtmModel medium(B0, density, protonComposition); + + // and test at several locations + CHECK(B0.getComponents(gCS) == + medium.getMagneticField(Point(gCS, -10_m, 4_m, 35_km)).getComponents(gCS)); + CHECK( + B0.getComponents(gCS) == + medium.getMagneticField(Point(gCS, 1000_km, -1000_km, 1000_km)).getComponents(gCS)); + CHECK(B0.getComponents(gCS) == + medium.getMagneticField(Point(gCS, 0_m, 0_m, 0_m)).getComponents(gCS)); + + // create a new magnetic field vector + Vector B1(gCS, 23_T, 57_T, -4_T); + + // and update this atmospheric model + medium.setMagneticField(B1); + + // and test at several locations + CHECK(B1.getComponents(gCS) == + medium.getMagneticField(Point(gCS, -10_m, 4_m, 35_km)).getComponents(gCS)); + CHECK( + B1.getComponents(gCS) == + medium.getMagneticField(Point(gCS, 1000_km, -1000_km, 1000_km)).getComponents(gCS)); + CHECK(B1.getComponents(gCS) == + medium.getMagneticField(Point(gCS, 0_m, 0_m, 0_m)).getComponents(gCS)); + + // check the density and nuclear composition + CHECK(density == medium.getMassDensity(Point(gCS, 0_m, 0_m, 0_m))); + medium.getNuclearComposition(); + + // create a line of length 1 m + Line const line(gOrigin, Vector<SpeedType::dimension_type>( + gCS, {1_m / second, 0_m / second, 0_m / second})); + + // the end time of our line + auto const tEnd = 1_s; + + // and the associated trajectory + Trajectory<Line> const trajectory(line, tEnd); + + // and check the integrated grammage + CHECK((medium.getIntegratedGrammage(trajectory, 3_m) / (density * 3_m)) == Approx(1)); + CHECK((medium.getArclengthFromGrammage(trajectory, density * 5_m) / 5_m) == Approx(1)); +} diff --git a/tests/modules/testCONEXSourceCut.cpp b/tests/modules/testCONEXSourceCut.cpp new file mode 100644 index 0000000000000000000000000000000000000000..378cd9259488e8ded32678929e987ba507387e07 --- /dev/null +++ b/tests/modules/testCONEXSourceCut.cpp @@ -0,0 +1,150 @@ +/* + * (c) Copyright 2020 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 <corsika/setup/SetupEnvironment.h> + +#include <corsika/environment/Environment.h> +#include <corsika/environment/LayeredSphericalAtmosphereBuilder.h> +#include <corsika/environment/MediumPropertyModel.h> +#include <corsika/environment/UniformMagneticField.h> + +#include <corsika/geometry/Point.h> +#include <corsika/geometry/RootCoordinateSystem.h> +#include <corsika/geometry/Vector.h> + +#include <corsika/particles/ParticleProperties.h> + +#include <corsika/process/conex_source_cut/CONEXSourceCut.h> +#include <corsika/process/sibyll/Interaction.h> +#include <corsika/process/sibyll/NuclearInteraction.h> + +#include <corsika/random/RNGManager.h> + +#include <corsika/units/PhysicalUnits.h> +#include <corsika/utl/CorsikaFenv.h> + +#include <catch2/catch.hpp> + +using namespace corsika; +using namespace corsika::environment; +using namespace corsika::geometry; +using namespace corsika::units::si; + +const std::string refDataDir = std::string(REFDATADIR); // from cmake + +template <typename T> +using MExtraEnvirnoment = + environment::MediumPropertyModel<environment::UniformMagneticField<T>>; + +TEST_CASE("CONEXSourceCut") { + random::RNGManager::GetInstance().RegisterRandomStream("cascade"); + random::RNGManager::GetInstance().RegisterRandomStream("sibyll"); + + feenableexcept(FE_INVALID); + + // setup environment, geometry + setup::Environment env; + const CoordinateSystem& rootCS = env.GetCoordinateSystem(); + Point const center{rootCS, 0_m, 0_m, 0_m}; + + auto builder = environment::make_layered_spherical_atmosphere_builder< + setup::EnvironmentInterface, + MExtraEnvirnoment>::create(center, conex::earthRadius, + environment::Medium::AirDry1Atm, + geometry::Vector{rootCS, 0_T, 50_mT, 0_T}); + + builder.setNuclearComposition( + {{particles::Code::Nitrogen, particles::Code::Oxygen}, + {0.7847f, 1.f - 0.7847f}}); // values taken from AIRES manual, Ar removed for now + + builder.addExponentialLayer(1222.6562_g / (1_cm * 1_cm), 994186.38_cm, 4_km); + builder.addExponentialLayer(1144.9069_g / (1_cm * 1_cm), 878153.55_cm, 10_km); + builder.addExponentialLayer(1305.5948_g / (1_cm * 1_cm), 636143.04_cm, 40_km); + builder.addExponentialLayer(540.1778_g / (1_cm * 1_cm), 772170.16_cm, 100_km); + builder.addLinearLayer(1e9_cm, 112.8_km); + + builder.assemble(env); + + const HEPEnergyType E0 = 1_PeV; + double thetaDeg = 60.; + auto const thetaRad = thetaDeg / 180. * M_PI; + + auto const observationHeight = 1.4_km + conex::earthRadius; + auto const injectionHeight = 112.75_km + conex::earthRadius; + auto const t = -observationHeight * cos(thetaRad) + + sqrt(-units::static_pow<2>(sin(thetaRad) * observationHeight) + + units::static_pow<2>(injectionHeight)); + Point const showerCore{rootCS, 0_m, 0_m, observationHeight}; + Point const injectionPos = + showerCore + + Vector<dimensionless_d>{rootCS, {-sin(thetaRad), 0, cos(thetaRad)}} * t; + + environment::ShowerAxis const showerAxis{injectionPos, + (showerCore - injectionPos) * 1.02, env}; + + // need to initialize Sibyll, done in constructor: + process::sibyll::Interaction sibyll; + [[maybe_unused]] process::sibyll::NuclearInteraction sibyllNuc(sibyll, env); + + corsika::process::conex_source_cut::CONEXSourceCut conex( + center, showerAxis, t, injectionHeight, E0, + particles::GetPDG(particles::Code::Proton)); + + HEPEnergyType const Eem{1_PeV}; + auto const momentum = showerAxis.GetDirection() * Eem; + + auto const emPosition = showerCore + showerAxis.GetDirection() * (-20_km); + + std::cout << "position injection: " + << injectionPos.GetCoordinates(conex.GetObserverCS()) << " " + << injectionPos.GetCoordinates(rootCS) << std::endl; + std::cout << "position core: " << showerCore.GetCoordinates(conex.GetObserverCS()) + << " " << showerCore.GetCoordinates(rootCS) << std::endl; + std::cout << "position EM: " << emPosition.GetCoordinates(conex.GetObserverCS()) << " " + << emPosition.GetCoordinates(rootCS) << std::endl; + + conex.addParticle(particles::Code::Proton, Eem, 0_eV, emPosition, momentum.normalized(), + 0_s); + // supperimpose a photon + auto const momentumPhoton = showerAxis.GetDirection() * 1_TeV; + conex.addParticle(particles::Code::Gamma, 1_TeV, 0_eV, emPosition, + momentumPhoton.normalized(), 0_s); + conex.SolveCE(); +} + +#include <algorithm> +#include <iterator> +#include <string> +#include <fstream> + +TEST_CASE("ConexOutput", "[output validation]") { + + auto file = GENERATE(as<std::string>{}, "conex_fit", "conex_output"); + + SECTION(std::string("check saved data, ") + file + ".txt") { + + // compare to binary reference data + std::ifstream file1(file + ".txt"); + std::ifstream file1ref(refDataDir + "/" + file + "_REF.txt"); + + std::istreambuf_iterator<char> begin1(file1); + std::istreambuf_iterator<char> begin1ref(file1ref); + + std::istreambuf_iterator<char> end; + + while (begin1 != end && begin1ref != end) { + CHECK(*begin1 == *begin1ref); + ++begin1; + ++begin1ref; + } + CHECK(begin1 == end); + CHECK(begin1ref == end); + file1.close(); + file1ref.close(); + } +} diff --git a/tests/modules/testExecTime.cpp b/tests/modules/testExecTime.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e2fc710e7a201096184edac399befe01a8d04356 --- /dev/null +++ b/tests/modules/testExecTime.cpp @@ -0,0 +1,168 @@ +/* + * (c) Copyright 2019 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. + */ + +#define CATCH_CONFIG_MAIN // This tells Catch to provide a main() - only do this in one + // cpp file +#include <catch2/catch.hpp> + +#include <corsika/process/analytic_processors/ExecTime.h> + +#include <corsika/process/example_processors/DummyBoundaryCrossingProcess.h> +#include <corsika/process/example_processors/DummyContinuousProcess.h> +#include <corsika/process/example_processors/DummyDecayProcess.h> +#include <corsika/process/example_processors/DummyInteractionProcess.h> +#include <corsika/process/example_processors/DummySecondariesProcess.h> + +#include <cmath> +#include <random> +#include <vector> + +using namespace corsika::process; +using namespace corsika::process::analytic_processors; +using namespace corsika::process::example_processors; + +TEST_CASE("Timing process", "[proccesses][analytic_processors ExecTime]") { + + int tmp = 0; + + SECTION("BoundaryCrossing") { + ExecTime<DummyBoundaryCrossingProcess<10>> execTime; + auto start = std::chrono::steady_clock::now(); + CHECK(execTime.DoBoundaryCrossing(tmp, 0, 0) == EProcessReturn::eOk); + auto end = std::chrono::steady_clock::now(); + CHECK(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() == + Approx(10).margin(5)); + + for (int i = 0; i < 100; i++) execTime.DoBoundaryCrossing(tmp, 0, 0); + + CHECK(execTime.mean() == Approx(10 * 1000).margin(2 * 1000)); + + CHECK(execTime.sumTime() == Approx(10 * 100 * 1000).margin((10 * 100) * 1000)); + + CHECK(fabs(execTime.var()) < 100000); + } + + SECTION("Continuous") { + ExecTime<DummyContinuousProcess<50>> execTime; + auto start = std::chrono::steady_clock::now(); + CHECK(execTime.DoContinuous(tmp, tmp) == EProcessReturn::eOk); + auto end = std::chrono::steady_clock::now(); + CHECK(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() == + Approx(50).margin(5)); + + for (int i = 0; i < 100; i++) execTime.DoContinuous(tmp, tmp); + + CHECK(execTime.mean() == Approx(50 * 1000).margin(2 * 1000)); + + CHECK(execTime.sumTime() == Approx(50 * 100 * 1000).margin((10 * 100) * 1000)); + + CHECK(fabs(execTime.var()) < 100000); + } + + SECTION("Decay") { + ExecTime<DummyDecayProcess<10>> execTime; + auto start = std::chrono::steady_clock::now(); + CHECK(execTime.DoDecay(tmp) == EProcessReturn::eOk); + auto end = std::chrono::steady_clock::now(); + CHECK(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() == + Approx(10).margin(5)); + + for (int i = 0; i < 100; i++) execTime.DoDecay(tmp); + + CHECK(execTime.mean() == Approx(10 * 1000).margin(2 * 1000)); + + CHECK(execTime.sumTime() == Approx(10 * 100 * 100).margin((10 * 100) * 1000)); + + CHECK(fabs(execTime.var()) < 100000); + } + + SECTION("Interaction") { + ExecTime<DummyInteractionProcess<10>> execTime; + auto start = std::chrono::steady_clock::now(); + CHECK(execTime.DoInteraction(tmp) == EProcessReturn::eOk); + auto end = std::chrono::steady_clock::now(); + CHECK(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() == + Approx(10).margin(5)); + + for (int i = 0; i < 100; i++) execTime.DoInteraction(tmp); + + CHECK(execTime.mean() == Approx(10 * 1000).margin(2 * 1000)); + + CHECK(execTime.sumTime() == Approx(10 * 100 * 1000).margin((10 * 100) * 1000)); + + CHECK(fabs(execTime.var()) < 100000); + } + + SECTION("Secondaries") { + ExecTime<DummySecondariesProcess<10>> execTime; + auto start = std::chrono::steady_clock::now(); + CHECK(execTime.DoSecondaries(tmp) == EProcessReturn::eOk); + auto end = std::chrono::steady_clock::now(); + CHECK(std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count() == + Approx(10).margin(5)); + + for (int i = 0; i < 100; i++) execTime.DoSecondaries(tmp); + + CHECK(execTime.mean() == Approx(10 * 1000).margin(2 * 1000)); + + CHECK(execTime.sumTime() == Approx(10 * 100 * 1000).margin((10 * 100) * 1000)); + + CHECK(fabs(execTime.var()) < 100000); + } + + SECTION("TestMeanAlgo") { + std::default_random_engine generator; + std::normal_distribution<double> distribution(10000.0, 200.0); + + double fElapsedSum = 0; + double fMean = 0; + double fMean2 = 0; + long long fMin = std::numeric_limits<long long>::max(); + long long fMax = std::numeric_limits<long long>::min(); + int fN = 0; + + std::vector<double> elems; + + for (int i = 0; i < 1000; i++) { + double timeDiv = distribution(generator); + + elems.push_back(timeDiv); + + fElapsedSum += timeDiv; + fN = fN + 1; + + if (fMax < timeDiv) fMax = timeDiv; + + if (timeDiv < fMin) fMin = timeDiv; + + double delta = timeDiv - fMean; + fMean += delta / static_cast<double>(fN); + + double delta2 = timeDiv - fMean; + + fMean2 += delta * delta2; + } + + CHECK(fN == 1000); + + double mean = 0; + std::for_each(elems.begin(), elems.end(), [&](double i) { mean += i; }); + mean = mean / fN; + + double var = 0; + std::for_each(elems.begin(), elems.end(), + [&](double i) { var += (mean - i) * (mean - i); }); + var = var / fN; + + CHECK(mean == Approx(10000.0).margin(10)); + CHECK(var == Approx(200.0 * 200).margin(2000)); + + CHECK(fMean2 / fN == Approx(200 * 200).margin(2000)); // Varianz + CHECK(fMean == Approx(10000).margin(10)); + } +} diff --git a/tests/modules/testInteractionCounter.cpp b/tests/modules/testInteractionCounter.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5417f3e3ec90ea7955e705e6fb836efd22198f8f --- /dev/null +++ b/tests/modules/testInteractionCounter.cpp @@ -0,0 +1,135 @@ +/* + * (c) Copyright 2018 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 <corsika/process/interaction_counter/InteractionCounter.hpp> + +#include <corsika/environment/Environment.h> +#include <corsika/environment/HomogeneousMedium.h> +#include <corsika/environment/NuclearComposition.h> +#include <corsika/geometry/Point.h> +#include <corsika/geometry/RootCoordinateSystem.h> +#include <corsika/geometry/Vector.h> +#include <corsika/units/PhysicalUnits.h> + +#include <corsika/setup/SetupStack.h> + +#include <catch2/catch.hpp> + +#include <numeric> + +using namespace corsika; +using namespace corsika::process::interaction_counter; +using namespace corsika::units; +using namespace corsika::units::si; + +const std::string refDataDir = std::string(REFDATADIR); // from cmake + +struct DummyProcess { + template <typename TParticle> + GrammageType GetInteractionLength([[maybe_unused]] TParticle const& particle) { + return 100_g / 1_cm / 1_cm; + } + + template <typename TParticle> + auto DoInteraction([[maybe_unused]] TParticle& projectile) { + return nullptr; + } +}; + +TEST_CASE("InteractionCounter", "[process]") { + + logging::SetLevel(logging::level::debug); + + DummyProcess d; + InteractionCounter countedProcess(d); + + SECTION("GetInteractionLength") { + REQUIRE(countedProcess.GetInteractionLength(nullptr) == 100_g / 1_cm / 1_cm); + } + + auto [env, csPtr, nodePtr] = setup::testing::setupEnvironment(particles::Code::Oxygen); + [[maybe_unused]] auto& env_dummy = env; + + SECTION("DoInteraction nucleus") { + unsigned short constexpr A = 14, Z = 7; + auto [stackPtr, secViewPtr] = setup::testing::setupStack(particles::Code::Nucleus, A, + Z, 105_TeV, nodePtr, *csPtr); + REQUIRE(stackPtr->getEntries() == 1); + REQUIRE(secViewPtr->getEntries() == 0); + + auto const ret = countedProcess.DoInteraction(*secViewPtr); + REQUIRE(ret == nullptr); + + auto const& h = countedProcess.GetHistogram().labHist(); + REQUIRE(h.at(h.axis(0).index(1'000'070'140), h.axis(1).index(1.05e14)) == 1); + REQUIRE(std::accumulate(h.cbegin(), h.cend(), 0) == 1); + + auto const& h2 = countedProcess.GetHistogram().CMSHist(); + REQUIRE(h2.at(h2.axis(0).index(1'000'070'140), h2.axis(1).index(1.6e12)) == 1); + REQUIRE(std::accumulate(h2.cbegin(), h2.cend(), 0) == 1); + + countedProcess.GetHistogram().saveLab("testInteractionCounter_file1.npz", + utl::SaveMode::overwrite); + countedProcess.GetHistogram().saveCMS("testInteractionCounter_file2.npz", + utl::SaveMode::overwrite); + } + + SECTION("DoInteraction Lambda") { + auto constexpr code = particles::Code::Lambda0; + auto [stackPtr, secViewPtr] = + setup::testing::setupStack(code, 0, 0, 105_TeV, nodePtr, *csPtr); + REQUIRE(stackPtr->getEntries() == 1); + REQUIRE(secViewPtr->getEntries() == 0); + + auto const ret = countedProcess.DoInteraction(*secViewPtr); + REQUIRE(ret == nullptr); + + auto const& h = countedProcess.GetHistogram().labHist(); + REQUIRE(h.at(h.axis(0).index(3122), h.axis(1).index(1.05e14)) == 1); + REQUIRE(std::accumulate(h.cbegin(), h.cend(), 0) == 1); + + auto const& h2 = countedProcess.GetHistogram().CMSHist(); + REQUIRE(h2.at(h2.axis(0).index(3122), h2.axis(1).index(1.6e12)) == 1); + REQUIRE(std::accumulate(h2.cbegin(), h2.cend(), 0) == 1); + } +} + +#include <algorithm> +#include <iterator> +#include <string> +#include <fstream> + +TEST_CASE("InteractionCounterOutput", "[output validation]") { + + auto file = GENERATE(as<std::string>{}, "testInteractionCounter_file1", + "testInteractionCounter_file2"); + + SECTION(std::string("check saved data, ") + file + ".npz") { + + std::cout << file + ".npz vs " << refDataDir + "/" + file + "_REF.npz" << std::endl; + + // compare to binary reference data + std::ifstream file1(file + ".npz"); + std::ifstream file1ref(refDataDir + "/" + file + "_REF.npz"); + + std::istreambuf_iterator<char> begin1(file1); + std::istreambuf_iterator<char> begin1ref(file1ref); + + std::istreambuf_iterator<char> end; + + while (begin1 != end && begin1ref != end) { + CHECK(*begin1 == *begin1ref); + ++begin1; + ++begin1ref; + } + CHECK(begin1 == end); + CHECK(begin1ref == end); + file1.close(); + file1ref.close(); + } +} diff --git a/tests/modules/testOnShellCheck.cpp b/tests/modules/testOnShellCheck.cpp new file mode 100644 index 0000000000000000000000000000000000000000..77c64e178da2726fabcd7c3792af60d270417c25 --- /dev/null +++ b/tests/modules/testOnShellCheck.cpp @@ -0,0 +1,88 @@ +/* + * (c) Copyright 2018 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 <corsika/process/on_shell_check/OnShellCheck.h> + +#include <corsika/environment/Environment.h> +#include <corsika/geometry/FourVector.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::on_shell_check; +using namespace corsika::units; +using namespace corsika::units::si; + +TEST_CASE("OnShellCheck", "[processes]") { + feenableexcept(FE_INVALID); + using EnvType = setup::Environment; + EnvType env; + const geometry::CoordinateSystem& rootCS = env.GetCoordinateSystem(); + + // setup empty particle stack + setup::Stack stack; + stack.Clear(); + // two energies + const HEPEnergyType E = 10_GeV; + // list of arbitrary particles + std::array const particleList{particles::Code::PiPlus, particles::Code::PiMinus, + particles::Code::Helium, particles::Code::Gamma}; + + std::array const mass_shifts{1.1, 1.001, 1.0, 1.0}; + + SECTION("check particle masses") { + + OnShellCheck check(1.e-2, 0.01, false); + + check.Init(); + + // 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, E, + 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 + setup::StackView 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 + int count = -1; + for (auto proType : particleList) { + count++; + const auto pz = sqrt((E - particles::GetMass(proType) * mass_shifts[count]) * + (E + particles::GetMass(proType) * mass_shifts[count])); + auto const momentum = corsika::stack::MomentumVector(rootCS, {0_GeV, 0_GeV, pz}); + projectile.AddSecondary(std::tuple<particles::Code, units::si::HEPEnergyType, + corsika::stack::MomentumVector, geometry::Point, + units::si::TimeType>{ + proType, E, momentum, geometry::Point(rootCS, 0_m, 0_m, 0_m), 0_ns}); + } + check.DoSecondaries(view); + int i = -1; + for (auto& p : view) { + i++; + auto const Plab = corsika::geometry::FourVector(p.GetEnergy(), p.GetMomentum()); + auto const m_kinetic = Plab.GetNorm(); + if (i == 0) + CHECK(m_kinetic / particles::PiPlus::GetMass() == Approx(1)); + else if (i == 1) + CHECK_FALSE(m_kinetic / particles::PiMinus::GetMass() == Approx(1)); + } + } +} diff --git a/tests/stack/testHistoryStack.cpp b/tests/stack/testHistoryStack.cpp new file mode 100644 index 0000000000000000000000000000000000000000..e636208300aafb24883817fa9d619898636c9389 --- /dev/null +++ b/tests/stack/testHistoryStack.cpp @@ -0,0 +1,64 @@ +/* + * (c) Copyright 2018 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 <corsika/framework/stack/CombinedStack.hpp> +#include <corsika/stack/DummyStack.hpp> +#include <corsika/stack/history/HistoryStackExtension.hpp> + +#include <catch2/catch.hpp> + +using namespace corsika; + +// this is our dummy environment, it only knows its trivial BaseNodeType +class DummyEvent { +private: + size_t parent_; + std::vector<int> secondaries_; + +public: + DummyEvent() {} + DummyEvent(const size_t parent) { parent_ = parent; } + + size_t getParentIndex() { return parent_; } + void addSecondary(const int particle) { secondaries_.push_back(particle); } + int multiplicity() const { return secondaries_.size(); } +}; + +// the GeometryNode stack needs to know the type of geometry-nodes from the DummyEnv: +template <typename TStackIter> +using DummyHistoryDataInterface = + typename history::MakeHistoryDataInterface<TStackIter, DummyEvent>::type; + +// combine dummy stack with geometry information for tracking +template <typename TStackIter> +using StackWithHistoryInterface = + CombinedParticleInterface<dummy_stack::DummyStack::pi_type, DummyHistoryDataInterface, + TStackIter>; + +using TestStack = + CombinedStack<typename dummy_stack::DummyStack::stack_implementation_type, + history::HistoryData<DummyEvent>, StackWithHistoryInterface>; + +using EvtPtr = std::shared_ptr<DummyEvent>; + +TEST_CASE("HistoryStackExtension", "[stack]") { + + [[maybe_unused]] CoordinateSystemPtr const& dummyCS = get_root_CoordinateSystem(); + + const dummy_stack::NoData noData; + TestStack s; + + auto p = s.addParticle(std::tuple<dummy_stack::NoData>{noData}); + + SECTION("add lone particle") { + CHECK(s.getEntries() == 1); + + EvtPtr evt = p.getEvent(); + CHECK(evt == nullptr); + } +} diff --git a/tests/stack/testHistoryView.cpp b/tests/stack/testHistoryView.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c702f4c8ae8adf111c06821b1d0178b6f3355b74 --- /dev/null +++ b/tests/stack/testHistoryView.cpp @@ -0,0 +1,216 @@ +/* + * (c) Copyright 2018 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 <corsika/stack/history/Event.hpp> +#include <corsika/stack/history/HistorySecondaryProducer.hpp> +#include <corsika/stack/history/HistoryStackExtension.hpp> + +#include <corsika/framework/stack/CombinedStack.hpp> +#include <corsika/stack/DummyStack.hpp> +#include <corsika/stack/NuclearStackExtension.hpp> + +#include <corsika/framework/logging/Logging.hpp> + +#include <catch2/catch.hpp> + +using namespace corsika; + +/** + Need to replicate setup::SetupStack in a maximally simplified + way, but with real particle data + */ + +// combine dummy stack with geometry information for tracking +template <typename TStackIter> +using StackWithHistoryInterface = CombinedParticleInterface< + nuclear_stack::ParticleDataStack::pi_type, + history::HistoryEventDataInterface, TStackIter>; + +using TestStack = CombinedStack< + typename nuclear_stack::ParticleDataStack::stack_implementation_type, + history::HistoryEventData, StackWithHistoryInterface>; + +/* + See Issue 161 + + unfortunately clang does not support this in the same way (yet) as + gcc, so we have to distinguish here. If clang cataches up, we + could remove the clang branch here and also in + corsika::Cascade. The gcc code is much more generic and + universal. If we could do the gcc version, we won't had to define + StackView globally, we could do it with MakeView whereever it is + actually needed. Keep an eye on this! + */ +#if defined(__clang__) +using TheTestStackView = SecondaryView<typename TestStack::StackImpl, + StackWithHistoryInterface, + history::HistorySecondaryProducer>; +#elif defined(__GNUC__) || defined(__GNUG__) +using TheTestStackView = + MakeView<TestStack, history::HistorySecondaryProducer>::type; +#endif + +using TestStackView = TheTestStackView; + +template <typename Event> +int count_generations(Event const* event) { + int genCounter = 0; + while (event) { + event = event->parentEvent().get(); + genCounter++; + } + + return genCounter; +} + +TEST_CASE("HistoryStackExtensionView", "[stack]") { + + CoordinateSystemPtr const& dummyCS = get_root_CoordinateSystem(); + + // in this test we only use one singel stack ! + TestStack stack; + + // add primary particle + auto p0 = stack.addParticle( + std::make_tuple(Code::Electron, 1.5_GeV, + MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}), + Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s)); + + CHECK(stack.getEntries() == 1); + corsika::history::EventPtr evt = p0.getEvent(); + CHECK(evt == nullptr); + CHECK(count_generations(evt.get()) == 0); + + SECTION("interface test, view") { + + // add secondaries, 1st generation + TestStackView hview0(p0); + + auto const ev0 = p0.getEvent(); + CHECK(ev0 == nullptr); + + CORSIKA_LOG_DEBUG("loop VIEW"); + + // add 5 secondaries + for (int i = 0; i < 5; ++i) { + auto sec = hview0.addSecondary( + std::make_tuple(Code::Electron, 1.5_GeV, + MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}), + Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s)); + + CHECK(sec.getParentEventIndex() == i); + CHECK(sec.getEvent() != nullptr); + CHECK(sec.getEvent()->parentEvent() == nullptr); + CHECK(count_generations(sec.getEvent().get()) == 1); + } + + // read 1st genertion particle particle + auto p1 = stack.getNextParticle(); + CHECK(count_generations(p1.getEvent().get()) == 1); + + TestStackView hview1(p1); + + auto const ev1 = p1.getEvent(); + + // add second generation of secondaries + // add 10 secondaries + for (int i = 0; i < 10; ++i) { + auto sec = hview1.addSecondary( + std::make_tuple(Code::Electron, 1.5_GeV, + MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}), + Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s)); + + CHECK(sec.getParentEventIndex() == i); + CHECK(sec.getEvent()->parentEvent() == ev1); + CHECK(sec.getEvent()->parentEvent()->parentEvent() == ev0); + + CHECK(count_generations(sec.getEvent().get()) == 2); + + const auto org_projectile = stack.at(sec.getEvent()->projectileIndex()); + CHECK(org_projectile.getEvent() == sec.getEvent()->parentEvent()); + } + + // read 2nd genertion particle particle + auto p2 = stack.getNextParticle(); + + TestStackView hview2(p2); + + auto const ev2 = p2.getEvent(); + + // add third generation of secondaries + // add 15 secondaries + for (int i = 0; i < 15; ++i) { + CORSIKA_LOG_TRACE("loop, view: " + std::to_string(i)); + + auto sec = hview2.addSecondary( + std::make_tuple(Code::Electron, 1.5_GeV, + MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}), + Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s)); + CORSIKA_LOG_TRACE("loop, ---- "); + + CHECK(sec.getParentEventIndex() == i); + CHECK(sec.getEvent()->parentEvent() == ev2); + CHECK(sec.getEvent()->parentEvent()->parentEvent() == ev1); + CHECK(sec.getEvent()->parentEvent()->parentEvent()->parentEvent() == ev0); + + CHECK(count_generations(sec.getEvent().get()) == 3); + } + } + + SECTION("also test projectile access") { + + CORSIKA_LOG_TRACE("projectile test"); + + // add secondaries, 1st generation + TestStackView hview0(p0); + auto proj0 = hview0.getProjectile(); + auto const ev0 = p0.getEvent(); + CHECK(ev0 == nullptr); + + CORSIKA_LOG_TRACE("loop"); + + // add 5 secondaries + for (int i = 0; i < 5; ++i) { + CORSIKA_LOG_TRACE("loop " + std::to_string(i)); + auto sec = proj0.addSecondary( + std::make_tuple(Code::Electron, 1.5_GeV, + MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}), + Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s)); + + CHECK(sec.getParentEventIndex() == i); + CHECK(sec.getEvent() != nullptr); + CHECK(sec.getEvent()->parentEvent() == nullptr); + } + CHECK(stack.getEntries() == 6); + + // read 1st genertion particle particle + auto p1 = stack.getNextParticle(); + + TestStackView hview1(p1); + auto proj1 = hview1.getProjectile(); + auto const ev1 = p1.getEvent(); + + // add second generation of secondaries + // add 10 secondaries + for (unsigned int i = 0; i < 10; ++i) { + auto sec = proj1.addSecondary( + std::make_tuple(Code::Electron, 1.5_GeV, + MomentumVector(dummyCS, {1_GeV, 1_GeV, 1_GeV}), + Point(dummyCS, {1 * meter, 1 * meter, 1 * meter}), 100_s)); + + CHECK(sec.getParentEventIndex() == int(i)); + CHECK(sec.getEvent()->parentEvent() == ev1); + CHECK(sec.getEvent()->secondaries().size() == i + 1); + CHECK(sec.getEvent()->parentEvent()->parentEvent() == ev0); + + const auto org_projectile = stack.at(sec.getEvent()->projectileIndex()); + CHECK(org_projectile.getEvent() == sec.getEvent()->parentEvent()); + } + CHECK(stack.getEntries() == 16); + } +}