diff --git a/corsika/detail/modules/radio/RadioProcess.inl b/corsika/detail/modules/radio/RadioProcess.inl index 0548b71a4226e554ddb20b59ed4fa13b0b69ff2a..ff8e570100ce3ae30fd215390b90ad127d8719b2 100644 --- a/corsika/detail/modules/radio/RadioProcess.inl +++ b/corsika/detail/modules/radio/RadioProcess.inl @@ -36,13 +36,13 @@ namespace corsika { TPropagator>::doContinuous(const Step<Particle>& step, const bool) { // we want the following particles: - // Code::Electron & Code::Positron & Code::Gamma + // Code::Electron & Code::Positron // we wrap Simulate() in doContinuous as the plan is to add particle level // filtering or thinning for calculation of the radio emission. This is // important for controlling the runtime of radio (by ignoring particles // that aren't going to contribute i.e. heavy hadrons) - // if (valid(particle, track)) { + // if (valid(step)) { auto const particleID_{step.getParticlePre().getPID()}; if ((particleID_ == Code::Electron) || (particleID_ == Code::Positron)) { CORSIKA_LOG_DEBUG("Particle for radio calculation: {} ", particleID_); @@ -62,17 +62,28 @@ namespace corsika { return meter * std::numeric_limits<double>::infinity(); } - // this should all be moved at a separate radio output function - // LCOV_EXCL_START template <typename TAntennaCollection, typename TRadioImpl, typename TPropagator> inline void RadioProcess<TAntennaCollection, TRadioImpl, TPropagator>::startOfLibrary( const boost::filesystem::path& directory) { - // loop over every antenna and set the initial path - // this also writes the time-bins to disk. - for (auto& antenna : antennas_.getAntennas()) { - antenna.startOfLibrary(directory, this->implementation().algorithm); - } + // setup the streamer + output_.initStreamer((directory / ("antennas.parquet")).string()); + // LCOV_EXCL_START + // build the schema + output_.addField("Time", parquet::Repetition::REQUIRED, parquet::Type::DOUBLE, + parquet::ConvertedType::NONE); + + output_.addField("Ex", parquet::Repetition::REQUIRED, parquet::Type::DOUBLE, + parquet::ConvertedType::NONE); + + output_.addField("Ey", parquet::Repetition::REQUIRED, parquet::Type::DOUBLE, + parquet::ConvertedType::NONE); + + output_.addField("Ez", parquet::Repetition::REQUIRED, parquet::Type::DOUBLE, + parquet::ConvertedType::NONE); + // LCOV_EXCL_STOP + // and build the streamer + output_.buildStreamer(); } template <typename TAntennaCollection, typename TRadioImpl, typename TPropagator> @@ -83,13 +94,55 @@ namespace corsika { // flush data to disk, and then reset the antenna // before the next event for (auto& antenna : antennas_.getAntennas()) { - antenna.endOfShower(event_, this->implementation().algorithm, - antenna.getSampleRate() * 1_s); + + auto const sampleRate = antenna.getSampleRate() * 1_s; + auto const radioImplementation = + static_cast<std::string>(this->implementation().algorithm); + + // get the axis labels for this antenna and write the first row. + axistype axis = antenna.implementation().getAxis(); + + // get the copy of the waveform data for this event + std::vector<double> const& dataX = antenna.implementation().getWaveformX(); + std::vector<double> const& dataY = antenna.implementation().getWaveformY(); + std::vector<double> const& dataZ = antenna.implementation().getWaveformZ(); + + // check for the axis name + std::string label = "Unknown"; + if (antenna.getDomainLabel() == "Time") { + label = "Time"; + } + // LCOV_EXCL_START + else if (antenna.getDomainLabel() == "Frequency") { + label = "Frequency"; + } + // LCOV_EXCL_STOP + if (radioImplementation == "ZHS" && label == "Time") { + for (size_t i = 0; i < axis.size() - 1; i++) { + auto time = (axis.at(i + 1) + axis.at(i)) / 2.; + auto Ex = -(dataX.at(i + 1) - dataX.at(i)) * sampleRate; + auto Ey = -(dataY.at(i + 1) - dataY.at(i)) * sampleRate; + auto Ez = -(dataZ.at(i + 1) - dataZ.at(i)) * sampleRate; + + *(output_.getWriter()) + << showerId_ << static_cast<double>(time) << static_cast<double>(Ex) + << static_cast<double>(Ey) << static_cast<double>(Ez) << parquet::EndRow; + } + } else if (radioImplementation == "CoREAS" && label == "Time") { + for (size_t i = 0; i < axis.size() - 1; i++) { + *(output_.getWriter()) + << showerId_ << static_cast<double>(axis[i]) + << static_cast<double>(dataX[i]) << static_cast<double>(dataY[i]) + << static_cast<double>(dataZ[i]) << parquet::EndRow; + } + } + antenna.reset(); } + output_.closeStreamer(); // increment our event counter - event_++; + showerId_++; } template <typename TAntennaCollection, typename TRadioImpl, typename TPropagator> @@ -123,6 +176,5 @@ namespace corsika { return config; } - // LCOV_EXCL_STOP } // namespace corsika \ No newline at end of file diff --git a/corsika/detail/modules/radio/ZHS.inl b/corsika/detail/modules/radio/ZHS.inl index 5c9e4aacc824b06499b65b988883d9bc1f3cbb00..6b8b4907aa37d98965daf67f8611d279ce66f3bd 100644 --- a/corsika/detail/modules/radio/ZHS.inl +++ b/corsika/detail/modules/radio/ZHS.inl @@ -214,7 +214,7 @@ namespace corsika { VectorPotential Vp = betaPerp * sign * constants * f / denominator / midPaths[i].R_distance_; antenna.receive(detectionTime1, betaPerp, Vp); - // intermidiate contributions + // intermediate contributions for (int it{1}; it < numberOfBins; ++it) { Vp = betaPerp * sign * constants / denominator / midPaths[i].R_distance_; antenna.receive( diff --git a/corsika/detail/modules/radio/antennas/Antenna.inl b/corsika/detail/modules/radio/antennas/Antenna.inl index 89d511257c5d9b9069df9036a243956e17d9e06c..cb2967f639932ca46660d001a19435ce12abd2d0 100644 --- a/corsika/detail/modules/radio/antennas/Antenna.inl +++ b/corsika/detail/modules/radio/antennas/Antenna.inl @@ -28,85 +28,6 @@ namespace corsika { return name_; } - template <typename TAntennaImpl> - inline void Antenna<TAntennaImpl>::startOfLibrary( - const boost::filesystem::path& directory, const std::string radioImplementation) { - - // calculate and save our filename - filename_ = (directory / this->getName()).string() + ".npz"; - - // get the axis labels for this antenna and write the first row. - axistype axis = this->implementation().getAxis(); - - // check for the axis name - std::string label = "Unknown"; - if constexpr (TAntennaImpl::is_time_domain) { - label = "Time"; - } else if constexpr (TAntennaImpl::is_freq_domain) { - label = "Frequency"; - } - - if (radioImplementation == "ZHS" && TAntennaImpl::is_time_domain) { - for (size_t i = 0; i < axis.size() - 1; i++) { - axis.at(i) = (axis.at(i + 1) + axis.at(i)) / 2.; - } - // explicitly convert the arrays to the needed type for cnpy - axis.pop_back(); - long double const* raw_data = axis.data(); - std::vector<size_t> N = { - axis.size()}; // cnpy needs a vector here -- this should be axis.size() - 1 -- - // write the labels to the first row of the NumPy file - cnpy::npz_save(filename_, label, raw_data, N, "w"); - } else { - // explicitly convert the arrays to the needed type for cnpy - long double const* raw_data = axis.data(); - std::vector<size_t> N = {axis.size()}; // cnpy needs a vector here - // write the labels to the first row of the NumPy file - cnpy::npz_save(filename_, label, raw_data, N, "w"); - } - } - - template <typename TAntennaImpl> - inline void Antenna<TAntennaImpl>::endOfShower(const int event, - std::string const& radioImplementation, - const double sampleRate) { - - // get the copy of the waveform data for this event - // we transpose it so that we can match dimensions with the - // time array that is already in the output file - std::vector<double> const& dataX = this->implementation().getWaveformX(); - std::vector<double> const& dataY = this->implementation().getWaveformY(); - std::vector<double> const& dataZ = this->implementation().getWaveformZ(); - - if (radioImplementation == "ZHS") { - std::vector<double> electricFieldX(dataX.size() - 1, - 0); // num_bins_, std::vector<double>(3, 0) - std::vector<double> electricFieldY(dataY.size() - 1, 0); - std::vector<double> electricFieldZ(dataZ.size() - 1, 0); - for (size_t i = 0; i < electricFieldX.size(); i++) { - electricFieldX.at(i) = -(dataX.at(i + 1) - dataX.at(i)) * sampleRate; - electricFieldY.at(i) = -(dataY.at(i + 1) - dataY.at(i)) * sampleRate; - electricFieldZ.at(i) = -(dataZ.at(i + 1) - dataZ.at(i)) * sampleRate; - } - // cnpy needs a vector for the shape - cnpy::npz_save(filename_, std::to_string(event) + "X", electricFieldX.data(), - {electricFieldX.size()}, "a"); - cnpy::npz_save(filename_, std::to_string(event) + "Y", electricFieldY.data(), - {electricFieldY.size()}, "a"); - cnpy::npz_save(filename_, std::to_string(event) + "Z", electricFieldZ.data(), - {electricFieldZ.size()}, "a"); - } else { - // cnpy needs a vector for the shape - // and write this event to the .npz archive - cnpy::npz_save(filename_, std::to_string(event) + "X", dataX.data(), {dataX.size()}, - "a"); - cnpy::npz_save(filename_, std::to_string(event) + "Y", dataY.data(), {dataY.size()}, - "a"); - cnpy::npz_save(filename_, std::to_string(event) + "Z", dataZ.data(), {dataZ.size()}, - "a"); - } - } - template <typename TAntennaImpl> inline TAntennaImpl& Antenna<TAntennaImpl>::implementation() { return static_cast<TAntennaImpl&>(*this); diff --git a/corsika/detail/modules/radio/antennas/TimeDomainAntenna.inl b/corsika/detail/modules/radio/antennas/TimeDomainAntenna.inl index c32f51657d745f2cd7f4ee0f5ace92979f98ca88..0fa54f21e0354d652ec957138f5a0074dc774476 100644 --- a/corsika/detail/modules/radio/antennas/TimeDomainAntenna.inl +++ b/corsika/detail/modules/radio/antennas/TimeDomainAntenna.inl @@ -97,6 +97,8 @@ namespace corsika { inline auto const& TimeDomainAntenna::getWaveformZ() const { return waveformEZ_; } + inline std::string const TimeDomainAntenna::getDomainLabel() { return "Time"; } + inline std::vector<long double> TimeDomainAntenna::createTimeAxis() const { // create a 1-D xtensor to store time values so we can print them later. @@ -115,7 +117,7 @@ namespace corsika { return times; } - inline auto const& TimeDomainAntenna::getAxis() const { return time_axis_; } + inline auto const TimeDomainAntenna::getAxis() const { return time_axis_; } inline InverseTimeType const& TimeDomainAntenna::getSampleRate() const { return sample_rate_; diff --git a/corsika/modules/radio/RadioProcess.hpp b/corsika/modules/radio/RadioProcess.hpp index 9ae9087bde8e81b06c143f9fff320cacd7fe279f..43b7a3453a74978e1dddf3721901a67758dfd229 100644 --- a/corsika/modules/radio/RadioProcess.hpp +++ b/corsika/modules/radio/RadioProcess.hpp @@ -7,11 +7,8 @@ */ #pragma once -#include <istream> -#include <fstream> -#include <iostream> -#include <string> #include <corsika/output/BaseOutput.hpp> +#include <corsika/output/ParquetStreamer.hpp> #include <corsika/framework/process/ContinuousProcess.hpp> #include <corsika/framework/core/Step.hpp> #include <corsika/setup/SetupStack.hpp> @@ -49,9 +46,11 @@ namespace corsika { protected: TAntennaCollection& antennas_; ///< The radio antennas we store into. TPropagator propagator_; ///< The propagator implementation. - int event_{0}; ///< The current event ID. + unsigned int showerId_{0}; ///< The current event ID. + ParquetStreamer output_; //!< The parquet streamer for this process. public: + using axistype = std::vector<long double>; /** * Construct a new RadioProcess. */ diff --git a/corsika/modules/radio/antennas/Antenna.hpp b/corsika/modules/radio/antennas/Antenna.hpp index b5aeb8fb99be1dd69e0ce57536a2e0e731921d09..3ae084e71d12a0e484c92064f063fd4977cf2901 100644 --- a/corsika/modules/radio/antennas/Antenna.hpp +++ b/corsika/modules/radio/antennas/Antenna.hpp @@ -7,10 +7,10 @@ */ #pragma once -#include <cnpy.hpp> #include <boost/filesystem.hpp> #include <corsika/framework/geometry/Point.hpp> #include <corsika/framework/core/PhysicalGeometry.hpp> +#include <corsika/output/ParquetStreamer.hpp> namespace corsika { @@ -28,7 +28,6 @@ namespace corsika { std::string const name_; ///< The name/identifier of this antenna. Point const location_; ///< The location of this antenna. CoordinateSystemPtr const coordinateSystem_; ///< The coordinate system of the antenna - std::string filename_ = ""; ///< The filename for the output file for this antenna. public: using axistype = std::vector<long double>; @@ -102,19 +101,6 @@ namespace corsika { */ std::vector<double> const& getWaveformZ() const; - /** - * Prepare for the start of the library. - */ - void startOfLibrary(boost::filesystem::path const& directory, - std::string const radioImplementation); - - /** - * Flush the data from this shower to disk. - */ - void endOfShower(int const event, std::string const& radioImplementation, - double const sampleRate); - - protected: /** * Get a reference to the underlying radio implementation. */ diff --git a/corsika/modules/radio/antennas/TimeDomainAntenna.hpp b/corsika/modules/radio/antennas/TimeDomainAntenna.hpp index 85fd9dd100743f674fef937f0aff8c69570b5d9c..8c4ee2147c9861bdafae6e8a8f0a93c9c1c3d5e1 100644 --- a/corsika/modules/radio/antennas/TimeDomainAntenna.hpp +++ b/corsika/modules/radio/antennas/TimeDomainAntenna.hpp @@ -33,10 +33,6 @@ namespace corsika { public: // import the methods from the antenna - - // label this as a time-domain antenna. - static constexpr bool is_time_domain{true}; - using Antenna<TimeDomainAntenna>::getName; using Antenna<TimeDomainAntenna>::getLocation; @@ -98,6 +94,14 @@ namespace corsika { */ auto const& getWaveformZ() const; + /** + * Return a label that indicates that this is a time + * domain antenna + * + * This returns the string "Time". + */ + std::string const getDomainLabel(); + /** * Creates time-units of each waveform. * @@ -110,7 +114,7 @@ namespace corsika { * * This returns them in nanoseconds for ease of use. */ - auto const& getAxis() const; + auto const getAxis() const; /** * Returns the sampling rate of the time domain antenna. diff --git a/corsika/modules/writers/ParticleWriterParquet.hpp b/corsika/modules/writers/ParticleWriterParquet.hpp index d28abdd1c7a483ace5e7e28ec2a2fbdb1b7a26d8..2a431bc0975a4617199bfc2f8be81ae2fd161de6 100644 --- a/corsika/modules/writers/ParticleWriterParquet.hpp +++ b/corsika/modules/writers/ParticleWriterParquet.hpp @@ -71,7 +71,7 @@ namespace corsika { double countHadrons_ = 0; ///< count hadrons hitting plane double countMuons_ = 0; ///< count muons hitting plane double countEM_ = 0; ///< count EM particles hitting plane. - double countOthers_ = 0; ///< count othe types of particles hitting plane + double countOthers_ = 0; ///< count other types of particles hitting plane HEPEnergyType totalEnergy_; ///< energy absorbed in ground. diff --git a/examples/clover_leaf.cpp b/examples/clover_leaf.cpp index ccb8c7e79aed238d9bfeabe5994dac0efe41cbff..0a93e1d9e5416cd467374536bbc52cf8cd61d0bc 100644 --- a/examples/clover_leaf.cpp +++ b/examples/clover_leaf.cpp @@ -1,5 +1,5 @@ /* - * (c) Copyright 2018 CORSIKA Project, corsika-project@lists.kit.edu + * (c) Copyright 2022 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 @@ -37,8 +37,6 @@ #include <corsika/modules/radio/detectors/AntennaCollection.hpp> #include <corsika/modules/radio/propagators/StraightPropagator.hpp> #include <corsika/modules/radio/propagators/SimplePropagator.hpp> -#include <corsika/modules/radio/propagators/SignalPath.hpp> -#include <corsika/modules/radio/propagators/RadioPropagator.hpp> #include <corsika/modules/TrackWriter.hpp> #include <corsika/modules/StackInspector.hpp> @@ -47,13 +45,13 @@ //#include <corsika/modules/TrackWriter.hpp> /* - NOTE, WARNING, ATTENTION + NOTE, WARNING, ATTENTION - The .../Random.hpppp implement the hooks of external modules to the C8 random - number generator. It has to occur excatly ONCE per linked - executable. If you include the header below multiple times and - link this together, it will fail. - */ + The .../Random.hpppp implement the hooks of external modules to the C8 random + number generator. It has to occur excatly ONCE per linked + executable. If you include the header below multiple times and + link this together, it will fail. +*/ #include <corsika/modules/sibyll/Random.hpp> #include <corsika/modules/urqmd/Random.hpp> @@ -72,7 +70,7 @@ using namespace std; // int main() { - logging::set_level(logging::level::warn); + logging::set_level(logging::level::info); corsika_logger->set_pattern("[%n:%^%-8l%$] custom pattern: %v"); CORSIKA_LOG_INFO("Synchrotron radiation"); @@ -210,11 +208,10 @@ int main() { // assemble all processes into an ordered process list auto sequence = make_sequence(coreas, cut); + output.startOfLibrary(); // define air shower object, run simulation Cascade EAS(env, tracking, sequence, output, stack); - output.startOfShower(); EAS.run(); - output.endOfShower(); CORSIKA_LOG_INFO("|p| electron = {} and E electron = {}", plab.getNorm(), Elab); CORSIKA_LOG_INFO("period: {}", period); @@ -225,4 +222,4 @@ int main() { CORSIKA_LOG_INFO("gamma: {}", gammaP); output.endOfLibrary(); -} +} \ No newline at end of file diff --git a/examples/radio_em_shower.cpp b/examples/radio_em_shower.cpp index 93eb14208a22b5dd3a010400d0043119d826df62..a088288def47dd9a0036d1bc821759a227d00a94 100644 --- a/examples/radio_em_shower.cpp +++ b/examples/radio_em_shower.cpp @@ -51,8 +51,6 @@ #include <corsika/modules/radio/detectors/AntennaCollection.hpp> #include <corsika/modules/radio/propagators/StraightPropagator.hpp> #include <corsika/modules/radio/propagators/SimplePropagator.hpp> -#include <corsika/modules/radio/propagators/SignalPath.hpp> -#include <corsika/modules/radio/propagators/RadioPropagator.hpp> #include <corsika/setup/SetupStack.hpp> #include <corsika/setup/SetupTrajectory.hpp> diff --git a/examples/synchrotron_test_C8tracking.cpp b/examples/synchrotron_test_C8tracking.cpp index cf1792188e4e463c33c2f5a9078b8121bdfb9ef3..9654d6ef207aef5c7c353c6a571b5c986ef2d6e1 100644 --- a/examples/synchrotron_test_C8tracking.cpp +++ b/examples/synchrotron_test_C8tracking.cpp @@ -158,11 +158,10 @@ int main() { // assemble all processes into an ordered process list auto sequence = make_sequence(coreas, zhs, cut); + output.startOfLibrary(); // define air shower object, run simulation Cascade EAS(env, tracking, sequence, output, stack); - output.startOfShower(); EAS.run(); - output.endOfShower(); CORSIKA_LOG_INFO("|p| = {} and E = {}", plab.getNorm(), Elab); CORSIKA_LOG_INFO("period: {}", period); diff --git a/tests/modules/testRadio.cpp b/tests/modules/testRadio.cpp index e31434fd4be10d403b5bab43ac0d7373d9e71739..58742fb830ccc95e4ed3b36b764b8e75889b3192 100644 --- a/tests/modules/testRadio.cpp +++ b/tests/modules/testRadio.cpp @@ -7,6 +7,7 @@ */ #include <catch2/catch.hpp> +#include <corsika/modules/radio/RadioProcess.hpp> #include <corsika/modules/radio/ZHS.hpp> #include <corsika/modules/radio/CoREAS.hpp> #include <corsika/modules/radio/antennas/TimeDomainAntenna.hpp> @@ -65,9 +66,10 @@ TEST_CASE("Radio", "[processes]") { SECTION("CoREAS process") { - // This serves as a compiler test for any changes in the CoREAS algorithm - // Environment + // This serves as a compiler test for any changes in the CoREAS algorithm and + // check the radio process output + // Environment using EnvironmentInterface = IRefractiveIndexModel<IMediumPropertyModel<IMagneticFieldModel<IMediumModel>>>; @@ -178,6 +180,23 @@ TEST_CASE("Radio", "[processes]") { REQUIRE(total < (1e-12 * ant.getWaveformX().size())); } + // coreas output check + std::string const implemencoreas{"CoREAS"}; + + boost::filesystem::path const tempPathC{boost::filesystem::temp_directory_path() / + ("test_corsika_radio_" + implemencoreas)}; + if (boost::filesystem::exists(tempPathC)) { + boost::filesystem::remove_all(tempPathC); + } + + boost::filesystem::create_directory(tempPathC); + coreas.startOfLibrary(tempPathC); + auto const outputFileC = tempPathC / ("antennas.parquet"); + CHECK(boost::filesystem::exists(outputFileC)); + // run end of shower and make sure that something extra was added + auto const fileSizeC = boost::filesystem::file_size(outputFileC); + coreas.endOfShower(0); + CHECK(boost::filesystem::file_size(outputFileC) > fileSizeC); coreas.endOfLibrary(); } // END: SECTION("CoREAS process") @@ -374,6 +393,25 @@ TEST_CASE("Radio", "[processes]") { // check doContinuous and simulate methods zhs.doContinuous(step, true); + // zhs output check + std::string const implemenzhs{"ZHS"}; + + boost::filesystem::path const tempPathZ{boost::filesystem::temp_directory_path() / + ("test_corsika_radio_" + implemenzhs)}; + if (boost::filesystem::exists(tempPathZ)) { + boost::filesystem::remove_all(tempPathZ); + } + + boost::filesystem::create_directory(tempPathZ); + zhs.startOfLibrary(tempPathZ); + auto const outputFileZ = tempPathZ / ("antennas.parquet"); + CHECK(boost::filesystem::exists(outputFileZ)); + // run end of shower and make sure that something extra was added + auto const fileSizeZ = boost::filesystem::file_size(outputFileZ); + zhs.endOfShower(0); + CHECK(boost::filesystem::file_size(outputFileZ) > fileSizeZ); + zhs.endOfLibrary(); + } // END: SECTION("ZHS process") SECTION("Radio extreme cases") { @@ -399,6 +437,7 @@ TEST_CASE("Radio", "[processes]") { Point const point_1(rootCSRadio, {1_m, 1_m, 0_m}); Point const point_2(rootCSRadio, {2_km, 1_km, 0_m}); Point const point_4(rootCSRadio, {0_m, 1_m, 0_m}); + const auto point_b{Point(rootCSRadio, 30000_m, 0_m, 0_m)}; // create times for the antenna const TimeType start{0_s}; @@ -407,17 +446,26 @@ TEST_CASE("Radio", "[processes]") { const TimeType duration_dummy{2_s}; const InverseTimeType sample_dummy{1_Hz}; + // create specific times for antenna to do timebin check + const TimeType start_b{0.994e-4_s}; + const TimeType duration_b{1.07e-4_s - 0.994e-4_s}; + const InverseTimeType sampleRate_b{5e+11_Hz}; + // check that I can create an antenna at (1, 2, 3) TimeDomainAntenna ant1("antenna_name", point1, rootCSRadio, start, duration, sample, start); TimeDomainAntenna ant2("dummy", point1, rootCSRadio, start, duration_dummy, sample_dummy, start); + TimeDomainAntenna ant_b("timebin", point_b, rootCSRadio, start_b, duration_b, + sampleRate_b, start_b); // construct a radio detector instance to store our antennas AntennaCollection<TimeDomainAntenna> detector; AntennaCollection<TimeDomainAntenna> detector_dummy; + AntennaCollection<TimeDomainAntenna> detector_b; // add the antennas to the detector detector.addAntenna(ant1); detector_dummy.addAntenna(ant2); + detector_b.addAntenna(ant_b); // create a new stack for each trial setup::Stack<EnvType> stack; @@ -428,7 +476,7 @@ TEST_CASE("Radio", "[processes]") { const auto pmass{get_mass(particle)}; // construct an energy const HEPEnergyType E0{1_TeV}; - // compute the necessary momentumn + // compute the necessary momentum const HEPMomentumType P0{sqrt(E0 * E0 - pmass * pmass)}; // and create the momentum vector const auto plab{MomentumVector(rootCSRadio, {P0, 0_GeV, 0_GeV})}; @@ -455,7 +503,18 @@ TEST_CASE("Radio", "[processes]") { VelocityVector vh{(point_4 - point1) / th}; Line lh{point1, vh}; StraightTrajectory track_h{lh, th}; + StraightTrajectory track_h_neg_time{lh, -th}; Step step_h(particle_stack, track_h); + Step step_h_neg_time(particle_stack, track_h_neg_time); + + // feed radio with an electron track that ends in a different antenna bin. + Point const point_start(rootCSRadio, {100_m, 0_m, 0_m}); + Point const point_end(rootCSRadio, {100_m, 0.00628319_m, 0_m}); + TimeType tb{(point_end - point_start).getNorm() / (0.999 * constants::c)}; + VelocityVector vb{(point_end - point_start) / tb}; + Line lb{point_start, vb}; + StraightTrajectory track_b{lb, tb}; + Step step_b(particle_stack, track_b); // create radio process instances RadioProcess<decltype(detector), @@ -467,13 +526,12 @@ TEST_CASE("Radio", "[processes]") { ZHS<decltype(detector), decltype(SimplePropagator(envRadio))>, decltype(SimplePropagator(envRadio))> zhs(detector, envRadio); - coreas.doContinuous(step_proton, true); zhs.doContinuous(step_proton, true); coreas.doContinuous(step_h, true); zhs.doContinuous(step_h, true); + zhs.doContinuous(step_h_neg_time, true); - // create radio processes with "dummy" antenna to trigger extreme time-binning RadioProcess<decltype(detector_dummy), CoREAS<decltype(detector_dummy), decltype(SimplePropagator(envRadio))>, decltype(SimplePropagator(envRadio))> @@ -483,10 +541,24 @@ TEST_CASE("Radio", "[processes]") { ZHS<decltype(detector_dummy), decltype(SimplePropagator(envRadio))>, decltype(SimplePropagator(envRadio))> zhs_dummy(detector_dummy, envRadio); - + coreas_dummy.doContinuous(step_proton, true); + zhs_dummy.doContinuous(step_proton, true); coreas_dummy.doContinuous(step_h, true); zhs_dummy.doContinuous(step_h, true); + // create radio process instances + RadioProcess<decltype(detector_b), + CoREAS<decltype(detector_b), decltype(SimplePropagator(envRadio))>, + decltype(SimplePropagator(envRadio))> + coreas_b(detector_b, envRadio); + + RadioProcess<decltype(detector_b), + ZHS<decltype(detector_b), decltype(SimplePropagator(envRadio))>, + decltype(SimplePropagator(envRadio))> + zhs_b(detector_b, envRadio); + coreas_b.doContinuous(step_b, true); + zhs_b.doContinuous(step_b, true); + } // END: SECTION("Radio extreme cases") SECTION("Process Library") { @@ -574,7 +646,7 @@ TEST_CASE("Radio", "[processes]") { CHECK(config["antennas"]["antenna_name2"]["location"][0].as<double>() == 4); CHECK(config["antennas"]["antenna_name2"]["location"][1].as<double>() == 80); CHECK(config["antennas"]["antenna_name2"]["location"][2].as<double>() == 6); - } + } // END: SECTION("Process Library") } // END: TEST_CASE("Radio", "[processes]") @@ -827,7 +899,7 @@ TEST_CASE("Antennas") { } // END: SECTION("TimeDomainAntenna AntennaCollection") - SECTION("TimeDomainAntenna Library") { + SECTION("TimeDomainAntenna Config File") { // Runs checks that the file readers are working properly Environment<IRefractiveIndexModel<IMediumModel>> env; const auto rootCS = env.getCoordinateSystem(); @@ -837,36 +909,24 @@ TEST_CASE("Antennas") { InverseTimeType const sampleRate(1_GHz); TimeType const groundHitTime(1e3_ns); - TimeDomainAntenna antenna("test_antenna", antPos, rootCS, tStart, duration, - sampleRate, groundHitTime); - - // Run the start of lib and end of shower functions on the antenna - std::vector<std::string> implementations{"CoREAS", "ZHS"}; - for (auto const implemen : implementations) { - // For each implementation type, save a file - boost::filesystem::path const tempPath{boost::filesystem::temp_directory_path() / - ("test_corsika_radio_" + implemen)}; - if (boost::filesystem::exists(tempPath)) { - boost::filesystem::remove_all(tempPath); - } - boost::filesystem::create_directory(tempPath); - antenna.startOfLibrary(tempPath, implemen); - auto const outputFile = tempPath / (antenna.getName() + ".npz"); - CHECK(boost::filesystem::exists(outputFile)); - - // run end of shower and make sure that something extra was added - auto const fileSize = boost::filesystem::file_size(outputFile); - antenna.endOfShower(0, implemen, sampleRate / 1_Hz); - CHECK(boost::filesystem::file_size(outputFile) > fileSize); - } + TimeDomainAntenna antennaC("test_antennaCoREAS", antPos, rootCS, tStart, duration, + sampleRate, groundHitTime); + TimeDomainAntenna antennaZ("test_antennaZHS", antPos, rootCS, tStart, duration, + sampleRate, groundHitTime); // Check the YAML file output - auto const config = antenna.getConfig(); - CHECK(config["type"].as<std::string>() == "TimeDomainAntenna"); - CHECK(config["start_time"].as<double>() == tStart / 1_ns); - CHECK(config["duration"].as<double>() == duration / 1_ns); - CHECK(config["sample_rate"].as<double>() == sampleRate / 1_GHz); - } // END: SECTION("TimeDomainAntenna Library") + auto const configC = antennaC.getConfig(); + CHECK(configC["type"].as<std::string>() == "TimeDomainAntenna"); + CHECK(configC["start_time"].as<double>() == tStart / 1_ns); + CHECK(configC["duration"].as<double>() == duration / 1_ns); + CHECK(configC["sample_rate"].as<double>() == sampleRate / 1_GHz); + + auto const configZ = antennaZ.getConfig(); + CHECK(configZ["type"].as<std::string>() == "TimeDomainAntenna"); + CHECK(configZ["start_time"].as<double>() == tStart / 1_ns); + CHECK(configZ["duration"].as<double>() == duration / 1_ns); + CHECK(configZ["sample_rate"].as<double>() == sampleRate / 1_GHz); + } // END: SECTION("TimeDomainAntenna Config File") } // END: TEST_CASE("Antennas") @@ -1156,7 +1216,7 @@ TEST_CASE("Propagators") { Approx(0).margin(absMargin)); CHECK(path.average_refractive_index_ == Approx(0.210275935)); CHECK(path.refractive_index_source_ == Approx(2)); - // CHECK(path.refractive_index_destination_ == Approx(0.0000000041)); + CHECK(path.refractive_index_destination_ == Approx(4.12231e-09)); CHECK(path.emit_.getComponents() == vvv1.getComponents()); CHECK(path.receive_.getComponents() == vvv2.getComponents()); CHECK(path.R_distance_ == 10_m);