diff --git a/Documentation/Examples/vertical_EAS.cc b/Documentation/Examples/vertical_EAS.cc index 5ceb0de05c8fd9d401289d30aa7a80858facabbf..042e64bc29f468afa32c45b654b9082adedc33dd 100644 --- a/Documentation/Examples/vertical_EAS.cc +++ b/Documentation/Examples/vertical_EAS.cc @@ -252,11 +252,8 @@ int main(int argc, char** argv) { auto const hists = sibyllCounted.GetHistogram() + sibyllNucCounted.GetHistogram() + urqmdCounted.GetHistogram() + proposalCounted.GetHistogram(); - hists.saveLab("inthist_lab.txt"); - hists.saveCMS("inthist_cms.txt"); - - hists.saveLab("inthist_lab.txt"); - hists.saveCMS("inthist_cms.txt"); + hists.saveLab("inthist_lab.npz"); + hists.saveCMS("inthist_cms.npz"); longprof.save("longprof.txt"); diff --git a/Framework/Utilities/SaveBoostHistogram.hpp b/Framework/Utilities/SaveBoostHistogram.hpp index 8418457f0ab8c9fe86412a77352cca278e919f35..813099c4c3a13f9ba3623dedf6e0590e8b12597e 100644 --- a/Framework/Utilities/SaveBoostHistogram.hpp +++ b/Framework/Utilities/SaveBoostHistogram.hpp @@ -1,3 +1,11 @@ +/* + * (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/third_party/cnpy/cnpy.hpp> #include <boost/histogram.hpp> @@ -8,97 +16,96 @@ #include <utility> #include <vector> -namespace corsika { - namespace utl { - - /** - * This functions saves a boost::histogram into a numpy file. Only rather basic axis - * types are supported: regular, variable, integer, category<int>. Only "ordinary" bin - * counts (i.e. a double or int) are supported, nothing fancy like profiles. - * - * Note that this function makes a temporary, dense copy of the histogram, which could - * be an issue for huge sizes (e.g. for high dimensions) - */ - template <class Axes, class Storage> - void save_hist(boost::histogram::histogram<Axes, Storage> const& h, - std::string const& filename) { - int const rank = h.rank(); - - std::vector<size_t> axes_dims; - axes_dims.reserve(rank); +#pragma once - auto overflow = std::make_unique<bool[]>(rank); - auto underflow = std::make_unique<bool[]>(rank); +namespace corsika::utl { + /** + * This functions saves a boost::histogram into a numpy file. Only rather basic axis + * types are supported: regular, variable, integer, category<int>. Only "ordinary" bin + * counts (i.e. a double or int) are supported, nothing fancy like profiles. + * + * Note that this function makes a temporary, dense copy of the histogram, which could + * be an issue for huge sizes (e.g. for high dimensions) + */ + template <class Axes, class Storage> + inline void save_hist(boost::histogram::histogram<Axes, Storage> const& h, + std::string const& filename) { + int const rank = h.rank(); - std::vector<char> axis_types; - axis_types.reserve(rank); + std::vector<size_t> axes_dims; + axes_dims.reserve(rank); - for (int i = 0; i < rank; ++i) { - auto const& ax = h.axis(i); - unsigned const has_underflow = (ax.options() & 0x01) ? 1 : 0; - unsigned const has_overflow = (ax.options() & 0x02) ? 1 : 0; + auto overflow = std::make_unique<bool[]>(rank); + auto underflow = std::make_unique<bool[]>(rank); - underflow[i] = has_underflow; - overflow[i] = has_overflow; + std::vector<char> axis_types; + axis_types.reserve(rank); - axes_dims.emplace_back(ax.size() + has_underflow + has_overflow); + for (int i = 0; i < rank; ++i) { + auto const& ax = h.axis(i); + unsigned const has_underflow = (ax.options() & 0x01) ? 1 : 0; + unsigned const has_overflow = (ax.options() & 0x02) ? 1 : 0; - if (ax.continuous()) { - axis_types.push_back('c'); - std::vector<double> ax_edges; - ax_edges.reserve(ax.size() + 1); + underflow[i] = has_underflow; + overflow[i] = has_overflow; - for (int j = 0; j < ax.size(); ++j) { ax_edges.push_back(ax.bin(j).lower()); } + axes_dims.emplace_back(ax.size() + has_underflow + has_overflow); - ax_edges.push_back(ax.bin(ax.size() - 1).upper()); + if (ax.continuous()) { + axis_types.push_back('c'); + std::vector<double> ax_edges; + ax_edges.reserve(ax.size() + 1); - cnpy::npz_save(filename, std::string{"binedges_"} + std::to_string(i), - ax_edges.data(), {ax_edges.size()}, "a"); - } else { - axis_types.push_back('d'); - std::vector<int64_t> bins; // we assume that discrete axes have integer bins - bins.reserve(ax.size()); + for (int j = 0; j < ax.size(); ++j) { ax_edges.push_back(ax.bin(j).lower()); } - for (int j = 0; j < ax.size(); ++j) { bins.push_back(ax.bin(j).lower()); } + ax_edges.push_back(ax.bin(ax.size() - 1).upper()); - cnpy::npz_save(filename, std::string{"bins_"} + std::to_string(i), bins.data(), - {bins.size()}, "a"); - } + cnpy::npz_save(filename, std::string{"binedges_"} + std::to_string(i), + ax_edges.data(), {ax_edges.size()}, "a"); + } else { + axis_types.push_back('d'); + std::vector<int64_t> bins; // we assume that discrete axes have integer bins + bins.reserve(ax.size()); - cnpy::npz_save(filename, std::string{"axistypes"}, axis_types.data(), - {axis_types.size()}, "a"); + for (int j = 0; j < ax.size(); ++j) { bins.push_back(ax.bin(j).lower()); } - cnpy::npz_save(filename, std::string{"overflow"}, overflow.get(), - {axis_types.size()}, "a"); - cnpy::npz_save(filename, std::string{"underflow"}, underflow.get(), - {axis_types.size()}, "a"); + cnpy::npz_save(filename, std::string{"bins_"} + std::to_string(i), bins.data(), + {bins.size()}, "a"); } - auto const prod_axis_size = std::accumulate(axes_dims.cbegin(), axes_dims.cend(), - unsigned{1}, std::multiplies<>()); - auto temp = std::make_unique<float[]>(prod_axis_size); + cnpy::npz_save(filename, std::string{"axistypes"}, axis_types.data(), + {axis_types.size()}, "a"); - assert(prod_axis_size == h.size()); + cnpy::npz_save(filename, std::string{"overflow"}, overflow.get(), + {axis_types.size()}, "a"); + cnpy::npz_save(filename, std::string{"underflow"}, underflow.get(), + {axis_types.size()}, "a"); + } + + auto const prod_axis_size = std::accumulate(axes_dims.cbegin(), axes_dims.cend(), + unsigned{1}, std::multiplies<>()); + auto temp = std::make_unique<float[]>(prod_axis_size); - // reduce multi-dim. to 1-dim, row-major (i.e., last axis index is contiguous in - // memory) take special care of underflow bins which have -1 as index and thus need - // to be shifted by +1 + assert(prod_axis_size == h.size()); - for (auto&& x : indexed(h, boost::histogram::coverage::all)) { - int p = 0; + // reduce multi-dim. to 1-dim, row-major (i.e., last axis index is contiguous in + // memory) take special care of underflow bins which have -1 as index and thus need + // to be shifted by +1 - for (int axis_index = 0; axis_index < rank; ++axis_index) { - int const offset_underflow = (h.axis(axis_index).options() & 0x01) ? 1 : 0; - auto k = x.index(axis_index) + offset_underflow; - p = k + p * axes_dims.at(axis_index); - } + for (auto&& x : indexed(h, boost::histogram::coverage::all)) { + int p = 0; - temp[p] = *x; + for (int axis_index = 0; axis_index < rank; ++axis_index) { + int const offset_underflow = (h.axis(axis_index).options() & 0x01) ? 1 : 0; + auto k = x.index(axis_index) + offset_underflow; + p = k + p * axes_dims.at(axis_index); } - cnpy::npz_save(filename, "data", temp.get(), axes_dims, "a"); - // In Python this array can directly be assigned to a histogram view if that - // histogram has its axes correspondingly: hist.view(flow=True)[:] = file['data'] + temp[p] = *x; } - } // namespace utl -} // namespace corsika + + cnpy::npz_save(filename, "data", temp.get(), axes_dims, "a"); + // In Python this array can directly be assigned to a histogram view if that + // histogram has its axes correspondingly: hist.view(flow=True)[:] = file['data'] + } +} // namespace corsika::utl diff --git a/Processes/InteractionCounter/CMakeLists.txt b/Processes/InteractionCounter/CMakeLists.txt index 7602d4e7c8250d8960d1686290846a5e826d4283..e7f374dbf345b888659c7b035b03ec1f24cf7494 100644 --- a/Processes/InteractionCounter/CMakeLists.txt +++ b/Processes/InteractionCounter/CMakeLists.txt @@ -28,6 +28,7 @@ set_target_properties ( target_link_libraries ( ProcessInteractionCounter CORSIKAunits + CORSIKAutilities CORSIKAprocesssequence CORSIKAthirdparty C8::ext::boost diff --git a/Processes/InteractionCounter/InteractionHistogram.cc b/Processes/InteractionCounter/InteractionHistogram.cc index a54b63bd185cbc0d2892bc564f1c294889d5e9eb..8ae626ffe57d1b0dd1cc016667b409a26b697f9a 100644 --- a/Processes/InteractionCounter/InteractionHistogram.cc +++ b/Processes/InteractionCounter/InteractionHistogram.cc @@ -13,10 +13,15 @@ using namespace corsika::process::interaction_counter; +InteractionHistogram::InteractionHistogram() + : inthist_cms_{detail::hist_factory(num_bins_cms, lower_edge_cms, upper_edge_cms)} + , inthist_lab_{detail::hist_factory(num_bins_lab, lower_edge_lab, upper_edge_lab)} {} + void InteractionHistogram::fill(particles::Code projectile_id, units::si::HEPEnergyType lab_energy, units::si::HEPEnergyType mass_target, int A, int Z) { using namespace units::si; + auto constexpr inv_eV = 1 / 1_eV; if (projectile_id == particles::Code::Nucleus) { auto const sqrtS = sqrt(A * A * (units::constants::nucleonMass * units::constants::nucleonMass) + @@ -24,123 +29,37 @@ void InteractionHistogram::fill(particles::Code projectile_id, int32_t const pdg = 1'000'000'000l + Z * 10'000l + A * 10l; - if (nuclear_inthist_cms_.count(pdg) == 0) { - nuclear_inthist_lab_.emplace( - pdg, - boost::histogram::make_histogram( - boost::histogram::axis::regular<double, - boost::histogram::axis::transform::log>( - num_bins_lab, lower_edge_lab, upper_edge_lab))); - - nuclear_inthist_cms_.emplace( - pdg, - boost::histogram::make_histogram( - boost::histogram::axis::regular<double, - boost::histogram::axis::transform::log>( - num_bins_cms, lower_edge_cms, upper_edge_cms))); - } - - nuclear_inthist_lab_[pdg](lab_energy / 1_GeV); - nuclear_inthist_cms_[pdg](sqrtS / 1_GeV); + inthist_lab_(pdg, lab_energy * inv_eV); + inthist_cms_(pdg, sqrtS * inv_eV); } else { auto const projectile_mass = particles::GetMass(projectile_id); auto const sqrtS = sqrt(projectile_mass * projectile_mass + mass_target * mass_target + 2 * lab_energy * mass_target); - interaction_histogram_cms_( - static_cast<corsika::particles::CodeIntType>(projectile_id), sqrtS / 1_GeV); - interaction_histogram_lab_( - static_cast<corsika::particles::CodeIntType>(projectile_id), lab_energy / 1_GeV); + inthist_cms_(static_cast<int>(particles::GetPDG(projectile_id)), sqrtS * inv_eV); + inthist_lab_(static_cast<int>(particles::GetPDG(projectile_id)), lab_energy * inv_eV); } } void InteractionHistogram::saveLab(std::string const& filename) const { - save(interaction_histogram_lab_, nuclear_inthist_lab_, filename, "lab system"); + corsika::utl::save_hist(inthist_lab_, filename); } void InteractionHistogram::saveCMS(std::string const& filename) const { - save(interaction_histogram_lab_, nuclear_inthist_cms_, filename, - "center-of-mass system"); + corsika::utl::save_hist(inthist_cms_, filename); } InteractionHistogram& InteractionHistogram::operator+=( InteractionHistogram const& other) { - interaction_histogram_lab_ += other.interaction_histogram_lab_; - interaction_histogram_cms_ += other.interaction_histogram_cms_; - - nuclear_inthist_lab_ += other.nuclear_inthist_lab_; - nuclear_inthist_cms_ += other.nuclear_inthist_cms_; + inthist_lab_ += other.inthist_lab_; + inthist_cms_ += other.inthist_cms_; return *this; } InteractionHistogram InteractionHistogram::operator+(InteractionHistogram other) const { - other.nuclear_inthist_lab_ += nuclear_inthist_lab_; - other.nuclear_inthist_cms_ += nuclear_inthist_cms_; - - other.interaction_histogram_lab_ += interaction_histogram_lab_; - other.interaction_histogram_cms_ += interaction_histogram_cms_; + other.inthist_lab_ += inthist_lab_; + other.inthist_cms_ += inthist_cms_; return other; } - -void InteractionHistogram::saveHist(hist_type const& hist, std::string const& filename, - std::string const& comment) { - std::ofstream file; - file.open(filename); - saveHist(hist, file, comment); -} - -void InteractionHistogram::saveHist(hist_type const& hist, std::ofstream& file, - std::string const& comment) { - auto const& energy_axis = hist.axis(1); - - file << "# interaction count histogram (" << comment << ")" << std::endl - << "# " << energy_axis.size() << " bins between " << energy_axis.bin(0).lower() - << " and " << energy_axis.bin(energy_axis.size() - 1).upper() << " GeV" - << std::endl; - - for (particles::CodeIntType p = 0; - p < static_cast<particles::CodeIntType>(particles::Code::LastParticle); ++p) { - - if (auto pdg = static_cast<particles::PDGCodeType>( - particles::GetPDG(static_cast<particles::Code>(p))); - pdg < 1'000'000'000l) { - file << "# " << static_cast<particles::Code>(p) << std::endl; - file << pdg; - for (int i = 0; i < energy_axis.size(); ++i) { file << ' ' << hist.at(p, i); } - file << std::endl; - } - } -} - -void InteractionHistogram::saveHistMap(nuclear_hist_type const& histMap, - std::ofstream& file) { - file << "# nuclei" << std::endl; - for (auto const& [pdg, hist] : histMap) { - auto const num_ebins_nucl = hist.axis(0).size(); - - file << pdg << ' '; - for (int i = 0; i < num_ebins_nucl; ++i) { file << ' ' << hist.at(i); } - file << std::endl; - } -} - -void InteractionHistogram::save(hist_type const& hist, nuclear_hist_type const& histMap, - std::string const& filename, std::string const& comment) { - std::ofstream file; - file.open(filename); - - saveHist(hist, file, comment); - saveHistMap(histMap, file); -} - -InteractionHistogram::InteractionHistogram() - : interaction_histogram_cms_{boost::histogram::make_histogram( - boost::histogram::axis::integer<short>(0, numIds), - boost::histogram::axis::regular<double, boost::histogram::axis::transform::log>( - num_bins_cms, lower_edge_cms, upper_edge_cms))} - , interaction_histogram_lab_{boost::histogram::make_histogram( - boost::histogram::axis::integer<short>(0, numIds), - boost::histogram::axis::regular<double, boost::histogram::axis::transform::log>( - num_bins_lab, lower_edge_lab, upper_edge_lab))} {} diff --git a/Processes/InteractionCounter/InteractionHistogram.hpp b/Processes/InteractionCounter/InteractionHistogram.hpp index ec9124e491d2be28eab999561fde2c7c831e5224..96d243f1223505498d211d675fc8cba6ec620fb6 100644 --- a/Processes/InteractionCounter/InteractionHistogram.hpp +++ b/Processes/InteractionCounter/InteractionHistogram.hpp @@ -10,47 +10,38 @@ #include <corsika/particles/ParticleProperties.h> #include <corsika/units/PhysicalUnits.h> +#include <corsika/utl/SaveBoostHistogram.hpp> #include <boost/histogram.hpp> -#include <boost/histogram/ostream.hpp> #include <fstream> #include <functional> #include <map> #include <utility> namespace corsika::process::interaction_counter { - using hist_type = decltype(boost::histogram::make_histogram( - std::declval<boost::histogram::axis::integer<particles::CodeIntType>>(), - std::declval<boost::histogram::axis::regular< - double, boost::histogram::axis::transform::log>>())); - - using nucl_hist_type = decltype(boost::histogram::make_histogram( - std::declval<boost::histogram::axis::regular< - double, boost::histogram::axis::transform::log>>())); - - using nuclear_hist_type = std::map<int32_t, nucl_hist_type>; - - template <typename TKey, typename TVal> - auto operator+=(std::map<TKey, TVal>& a, std::map<TKey, TVal> b) { - a.merge(b); - for (auto const& [id, hist] : b) { a[id] += hist; } - return a; - } + namespace detail { + inline auto hist_factory(unsigned int bin_number, float e_low, float e_high) { + namespace bh = boost::histogram; + namespace bha = bh::axis; + + auto h = bh::make_histogram( + bha::category<int, bh::use_default, bha::option::growth_t>{{2212, 2112}, + "projectile PDG"}, + bha::regular<float, bha::transform::log>{bin_number, e_low, e_high, + "energy/eV"}); + return h; + } + } // namespace detail class InteractionHistogram { - static auto constexpr lower_edge_cms = 1., upper_edge_cms = 1e8; // GeV sqrt s - static auto constexpr lower_edge_lab = 1., upper_edge_lab = 1e12; // GeV lab - static auto constexpr num_bins_lab = 120, num_bins_cms = 80; - static auto constexpr numIds = - static_cast<particles::CodeIntType>(particles::Code::LastParticle); + static double constexpr lower_edge_cms = 1e3, upper_edge_cms = 1e17; // eV sqrt s + static double constexpr lower_edge_lab = 1e3, upper_edge_lab = 1e21; // eV lab + static unsigned int constexpr num_bins_lab = 18 * 10, num_bins_cms = 14 * 10; - hist_type interaction_histogram_cms_; - hist_type interaction_histogram_lab_; + using hist_type = + decltype(detail::hist_factory(num_bins_lab, lower_edge_lab, upper_edge_lab)); - /*! - * These maps map PDG nuclei codes to their corresponding interaction histograms - */ - nuclear_hist_type nuclear_inthist_lab_, nuclear_inthist_cms_; + hist_type inthist_cms_, inthist_lab_; public: InteractionHistogram(); @@ -59,17 +50,9 @@ namespace corsika::process::interaction_counter { void fill(particles::Code projectile_id, units::si::HEPEnergyType lab_energy, units::si::HEPEnergyType mass_target, int A = 0, int Z = 0); - auto CMSHists() const { - return std::pair<decltype(interaction_histogram_cms_) const&, - decltype(nuclear_inthist_cms_) const&>{interaction_histogram_cms_, - nuclear_inthist_cms_}; - } + hist_type const& CMSHist() const { return inthist_cms_; } - auto labHists() const { - return std::pair<decltype(interaction_histogram_lab_) const&, - decltype(nuclear_inthist_lab_) const&>(interaction_histogram_lab_, - nuclear_inthist_lab_); - } + hist_type const& labHist() const { return inthist_lab_; } void saveLab(std::string const& filename) const; @@ -78,30 +61,6 @@ namespace corsika::process::interaction_counter { InteractionHistogram& operator+=(InteractionHistogram const& other); InteractionHistogram operator+(InteractionHistogram other) const; - - private: - /*! - * Save a histogram into a text file. The \arg comment string is written - * into the header of the text file. - * This method is static so that you can sum the histograms of multiple - * InteractionProcesses before saving them into the same file. - */ - - static void saveHist(hist_type const& hist, std::string const& filename, - std::string const& comment = ""); - - static void saveHist(hist_type const& hist, std::ofstream& file, - std::string const& comment = ""); - - static void saveHistMap(nuclear_hist_type const& histMap, std::ofstream& file); - - /*! - * save both the "normal" particle histograms as well as the "nuclear" histograms - * into the same file - */ - - static void save(hist_type const& hist, nuclear_hist_type const& histMap, - std::string const& filename, std::string const& comment = ""); }; } // namespace corsika::process::interaction_counter diff --git a/Processes/InteractionCounter/testInteractionCounter.cc b/Processes/InteractionCounter/testInteractionCounter.cc index 08fc95113c2955b5782247d55133336d15e01d5b..af43fe63eae05e941998fef7e180a47ca020e032 100644 --- a/Processes/InteractionCounter/testInteractionCounter.cc +++ b/Processes/InteractionCounter/testInteractionCounter.cc @@ -126,12 +126,13 @@ TEST_CASE("InteractionCounter") { auto const ret = countedProcess.DoInteraction(*secViewPtr); REQUIRE(ret == nullptr); - auto const& h = countedProcess.GetHistogram().labHists().second.at(1'000'070'140); - REQUIRE(h.at(50) == 1); + 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().CMSHists().second.at(1'000'070'140); - REQUIRE(h2.at(32) == 1); // bin 1.584 .. 1.995 TeV √s + 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(h2.at(1'000'070'140, 92) == 1); // bin 1.584 .. 1.995 TeV √s REQUIRE(std::accumulate(h2.cbegin(), h2.cend(), 0) == 1); } @@ -145,12 +146,12 @@ TEST_CASE("InteractionCounter") { auto const ret = countedProcess.DoInteraction(*secViewPtr); REQUIRE(ret == nullptr); - auto const& h = countedProcess.GetHistogram().labHists().first; - REQUIRE(h.at(codeInt, 50) == 1); + 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().CMSHists().first; - REQUIRE(h2.at(codeInt, 32) == 1); // bin 1.584 .. 1.995 TeV √s + 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); } }