diff --git a/Framework/Cascade/Cascade.h b/Framework/Cascade/Cascade.h index f4b8e92d2c35be48f95d99b294f48d508756c79a..df9ee0e1820891ce8a59fe2817815cdd6992c1a0 100644 --- a/Framework/Cascade/Cascade.h +++ b/Framework/Cascade/Cascade.h @@ -36,6 +36,26 @@ #include <boost/type_index.hpp> using boost::typeindex::type_id_with_cvr; +#include <fstream> +#include <boost/histogram.hpp> +#include <boost/histogram/ostream.hpp> +#include <corsika/process/tracking_line/dump_bh.hpp> +using namespace boost::histogram; +static auto histL2 = make_histogram(axis::regular<>(100, 0, 60000, "L'")); +static auto histS2 = make_histogram(axis::regular<>(100, 0, 60000, "S")); +static auto histB2 = make_histogram(axis::regular<>(100, 0, 60000, "Bogenlänge")); +static auto histLB2 = make_histogram(axis::regular<>(100, 0, 0.01, "L - B")); +static auto histLS2 = make_histogram(axis::regular<>(100, 0, 0.01, "L - S")); +static auto histLBrel2 = make_histogram(axis::regular<double, axis::transform::log> (20,1e-11,1e-6,"L/B -1")); +static auto histLSrel2 = make_histogram(axis::regular<double, axis::transform::log>(20,1e-11,1e-6, "L/S -1")); +static auto histELSrel2 = make_histogram(axis::regular<double, axis::transform::log>(20,1e-11,1e-6, "L/S -1"),axis::regular<double, axis::transform::log>(20, 0.1, 1e4, "E / GeV")); +static auto histBS2 = make_histogram(axis::regular<>(100, 0, 0.01, "B - S")); +static auto histLp2 = make_histogram(axis::regular<>(100, 0, 60000, "L' für Protonen")); +static auto histLpi2 = make_histogram(axis::regular<>(100, 0, 60000, "L' für Pionen")); +static auto histLmu2 = make_histogram(axis::regular<>(100, 0, 60000, "L' für Myonen")); +//static auto histLe = make_histogram(axis::regular<>(100, 0, 60000, "L' für Elektronen")); +//static auto histLy = make_histogram(axis::regular<>(100, 0, 60000, "L' für Photonen")); + /** * The cascade namespace assembles all objects needed to simulate full particles cascades. */ @@ -107,6 +127,72 @@ namespace corsika::cascade { } } + ~Cascade(){ + std::ofstream myfile; + myfile.open ("histograms2.txt"); + myfile << histLB2 << std::endl; + myfile << histLBrel2 << std::endl; + myfile << histLS2 << std::endl; + myfile << histLSrel2 << std::endl; + myfile << histELSrel2 << std::endl; + myfile.close(); + + /*std::cout << histLBrel << std::endl; + std::cout << histLSrel << std::endl;*/ + + + std::ofstream file1("histL2.json"); + dump_bh(file1, histL2); + file1.close(); + std::ofstream file2("histS2.json"); + dump_bh(file2, histS2); + file2.close(); + std::ofstream file3("histB2.json"); + dump_bh(file3, histB2); + file3.close(); + /*std::ofstream file4("histLB.json"); + dump_bh(file4, histLB); + file4.close(); + std::ofstream file5("histLS.json"); + dump_bh(file5, histLS); + file5.close(); + std::ofstream file6("histBS.json"); + dump_bh(file6, histBS); + file6.close(); + std::ofstream file7("histLBrel.json"); + dump_bh(file7, histLBrel); + file7.close(); + std::ofstream file8("histLSrel.json"); + dump_bh(file8, histLSrel); + file8.close(); + + std::ofstream file10("histELSrel.json"); + dump_bh(file10, histELSrel); + file10.close();*/ + std::ofstream file11("histLmu2.json"); + dump_bh(file11, histLmu2); + file11.close(); + std::ofstream file12("histLpi2.json"); + dump_bh(file12, histLpi2); + file12.close(); + std::ofstream file13("histLp2.json"); + dump_bh(file13, histLp2); + file13.close(); + + }; + + /** + * set the nodes for all particles on the stack according to their numerical + * position + */ + void SetNodes() { + std::for_each(fStack.begin(), fStack.end(), [&](auto& p) { + auto const* numericalNode = + fEnvironment.GetUniverse()->GetContainingNode(p.GetPosition()); + p.SetNode(numericalNode); + }); + } + /** * The Run function is the main simulation loop, which processes * particles from the Stack until the Stack is empty. @@ -214,7 +300,7 @@ namespace corsika::cascade { vParticle.GetEnergy() * units::constants::c; // determine geometric tracking - auto [lineWithoutB, stepWithoutB, stepWithB, geomMaxLength, nextVol] = fTracking.GetTrack(vParticle); + auto [stepWithoutB, stepWithB, geomMaxLength, magMaxLength, nextVol] = fTracking.GetTrack(vParticle); [[maybe_unused]] auto const& dummy_nextVol = nextVol; // convert next_step from grammage to length @@ -228,20 +314,59 @@ namespace corsika::cascade { // take minimum of geometry, interaction, decay for next step auto min_distance = std::min( - {distance_interact, distance_decay, distance_max, geomMaxLength}); - - C8LOG_DEBUG("transport particle by : {} m", min_distance / 1_m); - - // determine displacement by the magnetic field - auto [line, position, directionAfter] = fTracking.MagneticStep(vParticle, lineWithoutB, min_distance); + {distance_interact, distance_decay, distance_max, geomMaxLength, magMaxLength}); + + C8LOG_DEBUG("transport particle by : {} m " + "Max Displacement after: {} m " + "Medium transition after: {} m " + "Decay after: {} m " + "Interaction after: {} m", + min_distance/1_m, magMaxLength/1_m, geomMaxLength/1_m, distance_decay/1_m, distance_interact/1_m); + + // determine displacement by the magnetic field + /* + int chargeNumber; + if (corsika::particles::IsNucleus(vParticle.GetPID())) { + chargeNumber = vParticle.GetNuclearZ(); + } else { + chargeNumber = corsika::particles::GetChargeNumber(vParticle.GetPID()); + } + auto const* currentLogicalVolumeNode = vParticle.GetNode(); + auto magneticfield = currentLogicalVolumeNode->GetModelProperties().GetMagneticField(vParticle.GetPosition()); + auto k = chargeNumber * corsika::units::constants::cSquared * 1_eV / + ((vParticle.GetMomentum() / vParticle.GetEnergy() * + corsika::units::constants::c).norm() * vParticle.GetEnergy() * 1_V); + geometry::Vector<dimensionless_d> const directionBefore = vParticle.GetMomentum().normalized(); + LengthType Steplength = min_distance; + if (chargeNumber != 0) { + Steplength = (-sqrt(2 * k * min_distance * (directionBefore.cross(magneticfield)).norm() + 1) - 1) + / ((directionBefore.cross(magneticfield)).norm() * k); + std::cout << "Steplength2 " << Steplength << std::endl; + + Steplength = (sqrt(2 * k * min_distance * (directionBefore.cross(magneticfield)).norm() + 1) - 1) + / ((directionBefore.cross(magneticfield)).norm() * k); + std::cout << "Steplength1 " << Steplength << std::endl; + // not totally sure if it is always the positive solution + } + */ + // This formula has an error or doesnt work for specific conditions + // Steplength should not be min_distance + + auto [position, direction] = fTracking.MagneticStep(vParticle, min_distance); auto distance = position - vParticle.GetPosition(); - // distance.norm() != min_distance if q != 0 - // small error can be neglected + + //Building Trajectory for Continuous processes + geometry::Vector<SpeedType::dimension_type> velocity = + vParticle.GetMomentum() / vParticle.GetEnergy() * corsika::units::constants::c; + if (distance.norm() != 0_m) { + velocity = distance.normalized() * velocity.norm(); + } + geometry::Line line(vParticle.GetPosition(), velocity); + geometry::Trajectory<geometry::Line> stepNew(line, distance.norm() / line.GetV0().norm()); // here the particle is actually moved along the trajectory to new position: // std::visit(setup::ParticleUpdate<Particle>{vParticle}, step); - vParticle.SetMomentum(directionAfter.normalized() * vParticle.GetMomentum().norm()); - geometry::Trajectory<geometry::Line> stepNew(line, distance.norm() / line.GetV0().norm()); + vParticle.SetMomentum(direction * vParticle.GetMomentum().norm()); vParticle.SetPosition(position); vParticle.SetTime(vParticle.GetTime() + distance.norm() / units::constants::c); std::cout << "New Position: " << vParticle.GetPosition().GetCoordinates() << std::endl; @@ -268,7 +393,7 @@ namespace corsika::cascade { TStackView secondaries(vParticle); - if (min_distance != distance_max) { + if (min_distance != distance_max && min_distance != magMaxLength) { /* Create SecondaryView object on Stack. The data container remains untouched and identical, and 'projectil' is identical diff --git a/Processes/TrackWriter/TrackWriter.cc b/Processes/TrackWriter/TrackWriter.cc index a3d3edbf39eb27f4aaaf66c6255f6cb879572a9a..1bef877c3a48d327e56de4a70b6f13df5154f117 100644 --- a/Processes/TrackWriter/TrackWriter.cc +++ b/Processes/TrackWriter/TrackWriter.cc @@ -28,7 +28,7 @@ namespace corsika::process::track_writer { using namespace std::string_literals; fFile.open(fFilename); - fFile << "# PID, E / eV, start coordinates / m, displacement vector to end / m "s + fFile << "# PID, E / eV, start coordinates / m, displacement vector to end / m, steplength / m "s << '\n'; } @@ -47,7 +47,9 @@ namespace corsika::process::track_writer { << std::setw(width) << std::scientific << std::setprecision(precision) << start[2] / 1_m << std::setw(width) << std::scientific << std::setprecision(precision) << delta[0] / 1_m << std::setw(width) << std::scientific << std::setprecision(precision) << delta[1] / 1_m - << std::setw(width) << std::scientific << std::setprecision(precision) << delta[2] / 1_m << '\n'; + << std::setw(width) << std::scientific << std::setprecision(precision) << delta[2] / 1_m + << std::setw(width) << std::scientific << std::setprecision(precision) << delta.norm() / 1_m + << '\n'; // clang-format on return process::EProcessReturn::eOk; diff --git a/Processes/TrackingLine/CMakeLists.txt b/Processes/TrackingLine/CMakeLists.txt index dd626f8778c5a4139fe042fc651bb5e20eb315fe..36d53e30c3b1dceccc83e079a52499d5e73734fa 100644 --- a/Processes/TrackingLine/CMakeLists.txt +++ b/Processes/TrackingLine/CMakeLists.txt @@ -1,6 +1,7 @@ set ( MODEL_HEADERS TrackingLine.h + dump_bh.hpp ) set ( diff --git a/Processes/TrackingLine/TrackingLine.h b/Processes/TrackingLine/TrackingLine.h index d68a96d65c80c9f308c7a4694dc9f6faf8a60fda..875569079fa4a65c86c0dd5041eeb0b7f0e2f7a9 100644 --- a/Processes/TrackingLine/TrackingLine.h +++ b/Processes/TrackingLine/TrackingLine.h @@ -20,6 +20,12 @@ #include <type_traits> #include <utility> +#include <fstream> +#include <iostream> +#include <boost/histogram.hpp> +#include <boost/histogram/ostream.hpp> +#include <corsika/process/tracking_line/dump_bh.hpp> + namespace corsika::environment { template <typename IEnvironmentModel> class Environment; @@ -27,6 +33,23 @@ namespace corsika::environment { class VolumeTreeNode; } // namespace corsika::environment +using namespace boost::histogram; +static auto histL = make_histogram(axis::regular<>(100, 0, 60000, "L'")); +static auto histS = make_histogram(axis::regular<>(100, 0, 60000, "S")); +static auto histB = make_histogram(axis::regular<>(100, 0, 60000, "Bogenlänge")); +static auto histLB = make_histogram(axis::regular<>(100, 0, 0.01, "L - B")); +static auto histLS = make_histogram(axis::regular<>(100, 0, 0.01, "L - S")); +static auto histLBrel = make_histogram(axis::regular<double, axis::transform::log> (20,1e-11,1e-6,"L/B -1")); +static auto histLSrel = make_histogram(axis::regular<double, axis::transform::log>(20,1e-11,1e-6, "L/S -1")); +static auto histELSrel = make_histogram(axis::regular<double, axis::transform::log>(20,1e-11,1e-6, "L/S -1"),axis::regular<double, axis::transform::log>(20, 0.1, 1e4, "E / GeV")); +static auto histBS = make_histogram(axis::regular<>(100, 0, 0.01, "B - S")); +static auto histLp = make_histogram(axis::regular<>(100, 0, 60000, "L' für Protonen")); +static auto histLpi = make_histogram(axis::regular<>(100, 0, 60000, "L' für Pionen")); +static auto histLmu = make_histogram(axis::regular<>(100, 0, 60000, "L' für Myonen")); +//static auto histLe = make_histogram(axis::regular<>(100, 0, 60000, "L' für Elektronen")); +//static auto histLy = make_histogram(axis::regular<>(100, 0, 60000, "L' für Photonen")); +static double L = 0; + namespace corsika::process { namespace tracking_line { @@ -36,11 +59,64 @@ namespace corsika::process { corsika::units::si::TimeType TimeOfIntersection(geometry::Line const&, geometry::Plane const&); - + class TrackingLine { public: TrackingLine() = default; + ~TrackingLine(){ + std::ofstream myfile; + myfile.open ("histograms.txt"); + myfile << histLB << std::endl; + myfile << histLBrel << std::endl; + myfile << histLS << std::endl; + myfile << histLSrel << std::endl; + myfile << histELSrel << std::endl; + myfile.close(); + + /*std::cout << histLBrel << std::endl; + std::cout << histLSrel << std::endl;*/ + + + std::ofstream file1("histL.json"); + dump_bh(file1, histL); + file1.close(); + std::ofstream file2("histS.json"); + dump_bh(file2, histS); + file2.close(); + std::ofstream file3("histB.json"); + dump_bh(file3, histB); + file3.close(); + std::ofstream file4("histLB.json"); + dump_bh(file4, histLB); + file4.close(); + std::ofstream file5("histLS.json"); + dump_bh(file5, histLS); + file5.close(); + std::ofstream file6("histBS.json"); + dump_bh(file6, histBS); + file6.close(); + std::ofstream file7("histLBrel.json"); + dump_bh(file7, histLBrel); + file7.close(); + std::ofstream file8("histLSrel.json"); + dump_bh(file8, histLSrel); + file8.close(); + + std::ofstream file10("histELSrel.json"); + dump_bh(file10, histELSrel); + file10.close(); + std::ofstream file11("histLmu.json"); + dump_bh(file11, histLmu); + file11.close(); + std::ofstream file12("histLpi.json"); + dump_bh(file12, histLpi); + file12.close(); + std::ofstream file13("histLp.json"); + dump_bh(file13, histLp); + file13.close(); + + }; template <typename Particle> // was Stack previously, and argument was // Stack::StackIterator @@ -88,7 +164,10 @@ namespace corsika::process { std::cout << " Magnetic Field: " << magneticfield.GetComponents() / 1_uT << " uT " << std::endl; auto k = chargeNumber * corsika::units::constants::cSquared * 1_eV / (velocity.GetNorm() * p.GetEnergy() * 1_V); geometry::Vector<dimensionless_d> const directionBefore = velocity.normalized(); - LengthType Steplength = 10_m; // length irrelevant if q=0 and else it gets changed again + LengthType Steplength1 = 10_m; // length irrelevant if q=0 and else it gets changed again + LengthType Steplength2 = 10_m; + LengthType Steplimit = 1_m / 0; + //infinite kann man auch anders schreiben // for entering from outside auto addIfIntersects = [&](auto const& vtn) { @@ -98,7 +177,7 @@ namespace corsika::process { // everything is a sphere, crashes with exception if not // creating Line with magnetic field - if (chargeNumber != 0) { + if (chargeNumber != 0) { // determine steplength to next volume double a = ((directionBefore.cross(magneticfield)).dot(currentPosition - sphere.GetCenter()) * k + 1) * 4 / (1_m * 1_m * (directionBefore.cross(magneticfield)).GetSquaredNorm() * k * k); @@ -116,18 +195,24 @@ namespace corsika::process { } } if (tmp.size() > 0) { - Steplength = 1_m * *std::min_element(tmp.begin(),tmp.end()); - std::cout << "Steplength to next volume = " << Steplength << std::endl; + Steplength1 = 1_m * *std::min_element(tmp.begin(),tmp.end()); + std::cout << "Steplength to next volume = " << Steplength1 << std::endl; + std::cout << "Time to next volume = " << Steplength1 / velocity.norm() << std::endl; } else { std::cout << "no intersection (1)!" << std::endl; // what to do when this happens? (very unlikely) } delete [] solutions; + geometry::Vector<SpeedType::dimension_type> const velocityVerticalMag = velocity - + velocity.parallelProjectionOnto(magneticfield); + LengthType const gyroradius = p.GetEnergy() * velocityVerticalMag.GetNorm() * 1_V / + (corsika::units::constants::cSquared * abs(chargeNumber) * + magneticfield.GetNorm() * 1_eV); + Steplimit = 2 * sin(0.1) * gyroradius; } - auto [line1, position, direction] = MagneticStep(p, line, Steplength); - // new particle position and direction not needed in this case + auto line1 = MagneticStep(p, line, Steplength1, false); // instead of changing the line with magnetic field, the TimeOfIntersection() could be changed // using line has some errors for huge steps @@ -168,15 +253,15 @@ namespace corsika::process { std::cout << "Solutions for current Volume: " << solutions[i].real() << std::endl; } } - LengthType Steplength; if (tmp.size() > 0) { - Steplength = 1_m * *std::min_element(tmp.begin(),tmp.end()); - if (numericallyInside == false) { - int p = std::min_element(tmp.begin(),tmp.end()) - tmp.begin(); - tmp.erase(tmp.begin() + p); - Steplength = 1_m * *std::min_element(tmp.begin(),tmp.end()); - } - std::cout << "steplength out of current volume = " << Steplength << std::endl; + Steplength2 = 1_m * *std::min_element(tmp.begin(),tmp.end()); + if (numericallyInside == false) { + int p = std::min_element(tmp.begin(),tmp.end()) - tmp.begin(); + tmp.erase(tmp.begin() + p); + Steplength2 = 1_m * *std::min_element(tmp.begin(),tmp.end()); + } + std::cout << "steplength out of current volume = " << Steplength2 << std::endl; + std::cout << "Time out of current volume = " << Steplength2 / velocity.norm() << std::endl; } else { std::cout << "no intersection (2)!" << std::endl; // what to do when this happens? (very unlikely) @@ -184,8 +269,7 @@ namespace corsika::process { delete [] solutions; } - auto [line2, position, direction] = MagneticStep(p, line, Steplength); - // new particle position and direction not needed in this case + auto line2 = MagneticStep(p, line, Steplength2, false); // instead of changing the line with magnetic field, the TimeOfIntersection() could be changed // using line has some errors for huge steps @@ -212,22 +296,55 @@ namespace corsika::process { << min // << " " << minIter->second->GetModelProperties().GetName() << std::endl; - - auto [lineWithB, position, direction] = MagneticStep(p, line, velocity.norm() * min); - // new particle position and direction not needed in this case + + auto lineWithB = MagneticStep(p, line, velocity.norm() * min, true); + + if(min * velocity.norm() < 1000000_m) { + histL(L); + histS(lineWithB.ArcLength(0_s,min) / 1_m); + histLS(L-lineWithB.ArcLength(0_s,min) / 1_m); + + if(chargeNumber != 0) { + histLSrel(L * 1_m/lineWithB.ArcLength(0_s,min) -1); + histELSrel(L * 1_m/lineWithB.ArcLength(0_s,min) -1, p.GetEnergy() / 1_GeV); + auto magneticfield = currentLogicalVolumeNode->GetModelProperties().GetMagneticField(currentPosition); + geometry::Vector<SpeedType::dimension_type> const velocityVerticalMag = velocity - + velocity.parallelProjectionOnto(magneticfield); + + + LengthType const gyroradius = p.GetEnergy() * velocityVerticalMag.GetNorm() * 1_V / + (corsika::units::constants::cSquared * abs(chargeNumber) * + magneticfield.GetNorm() * 1_eV);; + LengthType B = 2 * gyroradius * asin ( lineWithB.ArcLength(0_s,min) / 2 / gyroradius); + histB(B / 1_m); + histLB(L-B/1_m); + histBS(B/1_m - lineWithB.ArcLength(0_s,min) / 1_m); + histLBrel(L * 1_m/B-1); + } + + int pdg = static_cast<int>(particles::GetPDG(p.GetPID())); + if (abs(pdg) == 13) + histLmu(L); + if (abs(pdg) == 211 || abs(pdg) == 111) + histLpi(L); + if (abs(pdg) == 2212 || abs(pdg) == 2112) + histLp(L); + } + - return std::make_tuple(line, geometry::Trajectory<geometry::Line>(line, min), + return std::make_tuple(geometry::Trajectory<geometry::Line>(line, min), geometry::Trajectory<geometry::Line>(lineWithB, min), - velocity.norm() * min, minIter->second); + velocity.norm() * min, Steplimit, minIter->second); } template <typename Particle> // was Stack previously, and argument was // Stack::StackIterator // determine direction of the particle after adding magnetic field - auto MagneticStep(Particle const& p, corsika::geometry::Line line, corsika::units::si::LengthType Steplength) { + corsika::geometry::Line MagneticStep( + Particle const& p, corsika::geometry::Line line, corsika::units::si::LengthType Steplength, bool i) { using namespace corsika::units::si; - + //charge of the particle int chargeNumber; if (corsika::particles::IsNucleus(p.GetPID())) { @@ -235,26 +352,69 @@ namespace corsika::process { } else { chargeNumber = corsika::particles::GetChargeNumber(p.GetPID()); } + auto const* currentLogicalVolumeNode = p.GetNode(); auto magneticfield = currentLogicalVolumeNode->GetModelProperties().GetMagneticField(p.GetPosition()); auto k = chargeNumber * corsika::units::constants::cSquared * 1_eV / (line.GetV0().norm() * p.GetEnergy() * 1_V); - geometry::Vector<dimensionless_d> const directionBefore = line.GetV0().normalized(); - + geometry::Vector<dimensionless_d> direction = line.GetV0().normalized(); + auto position = p.GetPosition(); + // First Movement // assuming magnetic field does not change during movement - auto position = p.GetPosition() + directionBefore * Steplength / 2; + position = position + direction * Steplength / 2; // Change of direction by magnetic field - geometry::Vector<dimensionless_d> const directionAfter = directionBefore + directionBefore.cross(magneticfield) * - Steplength * k; + direction = direction + direction.cross(magneticfield) * Steplength * k; // Second Movement - position = position + directionAfter * Steplength / 2; - auto distance = position - p.GetPosition(); + position = position + direction * Steplength / 2; + if(i) { + //if(chargeNumber != 0) { + if(Steplength < 1000000_m) { + L = direction.norm() * Steplength / 2_m + Steplength / 2_m; + } + //} + } + + geometry::Vector<LengthType::dimension_type> const distance = position - p.GetPosition(); if(distance.norm() == 0_m) { - return std::make_tuple(line, position, directionAfter); + return line; } geometry::Line newLine(p.GetPosition(), distance.normalized() * line.GetV0().norm()); - return std::make_tuple(newLine, position, directionAfter); + return newLine; + } + + template <typename Particle> // was Stack previously, and argument was + // Stack::StackIterator + + // determine direction of the particle after adding magnetic field + auto MagneticStep(Particle const& p, corsika::units::si::LengthType Steplength) { + using namespace corsika::units::si; + + //charge of the particle + int chargeNumber; + if (corsika::particles::IsNucleus(p.GetPID())) { + chargeNumber = p.GetNuclearZ(); + } else { + chargeNumber = corsika::particles::GetChargeNumber(p.GetPID()); + } + + auto const* currentLogicalVolumeNode = p.GetNode(); + auto magneticfield = currentLogicalVolumeNode->GetModelProperties().GetMagneticField(p.GetPosition()); + geometry::Vector<SpeedType::dimension_type> velocity = + p.GetMomentum() / p.GetEnergy() * corsika::units::constants::c; + auto k = chargeNumber * corsika::units::constants::cSquared * 1_eV / (velocity.norm() * p.GetEnergy() * 1_V); + geometry::Vector<dimensionless_d> direction = velocity.normalized(); + auto position = p.GetPosition(); + + // First Movement + // assuming magnetic field does not change during movement + position = position + direction * Steplength / 2; + // Change of direction by magnetic field + direction = direction + direction.cross(magneticfield) * Steplength * k; + // Second Movement + position = position + direction * Steplength / 2; + return std::make_tuple(position, direction.normalized()); + } }; diff --git a/Processes/TrackingLine/dump_bh.hpp b/Processes/TrackingLine/dump_bh.hpp new file mode 100644 index 0000000000000000000000000000000000000000..01e0dac20726b2d2b4787fad3bb3a9f0fff27b09 --- /dev/null +++ b/Processes/TrackingLine/dump_bh.hpp @@ -0,0 +1,186 @@ +#pragma once + +#include <boost/histogram.hpp> +#include <sstream> +#include <string> +#include <vector> + +/* + To save one 1D boost::histogram in C++, include this file and run: + + dump_bh_1d("file_name.json", hist); + + Note that the file_name.json will be overwritten, so use a new file for each histogram! or use: + + std::ofstream file1("test_hist.json"); + dump_bh(file1, h1); + file << ",\n"; + dump_bh(file1, h2); + file << ",\n"; + dump_bh(file1, h3); + file1.close(); + + + In python, just read the json and plot this histogram with matplotib + + for 1D histogram: + + import json + import matplotlib.pyplot as plt + + h1=json.load(open("test_hist_direct.json")) + print (h1) + plt.hist(h1['bins'][:-1], bins=h1['bins'], weights=h1['data']) + plt.show() + + + for 2D histogram (needs some list/array gymnastics): + + import json + import matplotlib.pyplot as plt + import numpy as np + + h2=json.load(open("test_hist_2D_direct.json")) + print (h2) + xx,yy = np.meshgrid(h2['xbins'][:-1], h2['ybins'][:-1]) + plt.hist2d([i for s in xx for i in s], [i for s in yy for i in s], + bins=[h2['xbins'],h2['ybins']], weights=h2['data']) + plt.show() + + + Do not forget to add axis titles, legend, etc. + */ + + +template<typename T> +void dump_bh_2d(std::ostream& os, T& h) +{ + // determine number of axes (rank) and axes sizes + const int rank = 2; + std::vector<int> axis_size(rank); + for (unsigned int i=0; i<rank; ++i) + axis_size[i] = h.axis(i).size(); + + // start dump json object + os << "{\n"; + os << " \"rank\" : " << rank << ",\n"; + os << " \"size\" : ["; + bool first = true; + for (auto s : axis_size) { + os << (first?"":", ") << s; + first = false; + } + os << "],\n"; + + // bins-x + os << " \"xbins\" : ["; + double lastx = 0; + for (unsigned int i=0; i<h.axis(0).size(); ++i) { + os << h.axis(0).bin(i).lower() << ", "; + lastx = h.axis(0).bin(i).upper(); + } + os << lastx << "],\n"; + + // bins-y + os << " \"ybins\" : ["; + double lasty = 0; + for (unsigned int i=0; i<h.axis(1).size(); ++i) { + os << h.axis(1).bin(i).lower() << ", "; + lasty = h.axis(1).bin(i).upper(); + } + os << lasty << "],\n"; + + // binned data + os << " \"data\" : ["; + first = true; + for (auto x : boost::histogram::indexed(h)) { + os << (first?"":", ") << *x; + first = false; + } + os << " ]\n"; + os << "}\n"; +} + + + + +template<typename T> +void dump_bh_1d(std::ostream& os, T& h) +{ + const int rank = 1; + // determine number of axes (rank) and axes sizes + std::vector<int> axis_size(rank); + for (unsigned int i=0; i<rank; ++i) + axis_size[i] = h.axis(i).size(); + + // start dump json object + os << "{\n"; + os << " \"rank\" : 1,\n"; + os << " \"size\" : ["; + bool first = true; + for (auto s : axis_size) { + os << (first?"":", ") << s << "],\n"; + first = false; + } + + // underflow data + os << " \"underflow\" : ["; + os << h.at(boost::histogram::axis::option::underflow); + os << "],\n"; + + // overflow data + os << " \"overflow\" : ["; + os << h.at(boost::histogram::axis::option::overflow); + os << "],\n"; + + // bins + os << " \"bins\" : ["; + double last = 0; + for (auto x : boost::histogram::indexed(h)) { + os << x.bin().lower() << ", "; + last = x.bin().upper(); + } + os << last << "],\n"; + + // data of bins + os << " \"data\" : ["; + first = true; + for (auto x : boost::histogram::indexed(h)) { + os << (first?"":", ") << *x; + first = false; + } + os << " ]\n"; + os << "}\n"; +} + + + + +template<typename T> +void dump_bh(std::ostream& os, T& h) +{ + // determine number of axes (rank) and axes sizes + const int rank = h.rank(); + switch (rank) { + case 1: + dump_bh_1d(os, h); + break; + case 2: + dump_bh_2d(os, h); + break; + default: + { + throw std::runtime_error("multi dim hist not implemented"); + } + break; + } +} + +template<typename T> +void dump_bh(const char* fname, T& h) +{ + std::ofstream file(fname); + dump_bh(file, h); + file.close(); +} +