REST-for-Physics  v2.3
Rare Event Searches ToolKit for Physics
TRestGeant4ParticleSourceCosmics.cxx
1
2#include "TRestGeant4ParticleSourceCosmics.h"
3
4#include <TFile.h>
5#include <TH2D.h>
6
7using namespace std;
8
9mutex TRestGeant4ParticleSourceCosmics::fMutex;
10unique_ptr<TRandom3> TRestGeant4ParticleSourceCosmics::fRandom = nullptr;
11
12const map<string, string> geant4ParticleNames = {
13 {"neutron", "neutron"},
14 {"proton", "proton"},
15 {"gamma", "gamma"},
16 {"electron_minus", "e-"},
17 {"electron_plus", "e+"},
18 {"muon_minus", "mu-"},
19 {"muon_plus", "mu+"},
20 //
21 {"neutron_below_1MeV", "neutron"},
22 {"neutron_above_10GeV", "neutron"},
23 {"neutron_between_1MeV_and_10GeV", "neutron"},
24};
25
26TRestGeant4ParticleSourceCosmics::TRestGeant4ParticleSourceCosmics() = default;
27
29 lock_guard<mutex> lock(fMutex);
30
31 cout << "TRestGeant4ParticleSourceCosmics::InitFromConfigFile" << endl;
32 fFilename = GetParameter("filename");
33 const auto particles =
34 GetParameter("particles", "neutron,proton,gamma,electron_minus,electron_plus,muon_minus,muon_plus");
35
36 const TVector2 energy = Get2DVectorParameterWithUnits("energy", {0, 0});
37 // sort it so that the lower energy is first
38 if (energy.X() > energy.Y()) {
39 fEnergyRange = {energy.Y(), energy.X()};
40 } else {
41 fEnergyRange = {energy.X(), energy.Y()};
42 }
43 // convert energy to MeV
44 fEnergyRange = {fEnergyRange.first / 1000, fEnergyRange.second / 1000};
45
46 fDirection = Get3DVectorParameterWithUnits("direction", {0, -1, 0});
47
48 std::istringstream iss(particles);
49 std::string name;
50 while (std::getline(iss, name, ',')) {
51 fParticleNames.insert(name);
52 }
53
54 for (const auto& particle : fParticleNames) {
55 if (geant4ParticleNames.find(particle) == geant4ParticleNames.end()) {
56 cerr << "Particle name '" << particle << "' is not allowed." << endl;
57 exit(1);
58 }
59 }
60
61 auto file = TFile::Open(fFilename.c_str(), "READ");
62 if (file == nullptr) {
63 cerr << "File '" << fFilename << "' not found." << endl;
64 exit(1);
65 }
66 for (const auto& particle : fParticleNames) {
67 auto histogramFromFile = file->Get<TH2D>(string(particle + "_energy_zenith").c_str());
68 if (histogramFromFile == nullptr) {
69 cerr << "Histogram '" << particle << "' not found in file '" << fFilename << "'." << endl;
70 exit(1);
71 }
72 fHistograms[particle] = new TH2D(*histogramFromFile);
73 const auto& histogram = fHistograms.at(particle);
74 TH2D* hist = new TH2D(*histogram);
75 // same as original but Y axis is multiplied by 1/cos(zenith). Integral is also scaled properly when
76 // computing the time of the simulation.
77 for (int i = 1; i <= hist->GetNbinsX(); i++) {
78 for (int j = 1; j <= hist->GetNbinsY(); j++) {
79 const double zenith = hist->GetYaxis()->GetBinCenter(j);
80 const double value = hist->GetBinContent(i, j) / TMath::Cos(zenith * TMath::DegToRad());
81 hist->SetBinContent(i, j, value);
82 }
83 }
84 fHistogramsTransformed[particle] = hist;
85 }
86
87 double sum = 0;
88 for (const auto& particle : fParticleNames) {
89 auto hist = fHistograms.at(particle);
90 fParticleWeights[particle] += hist->GetEntries();
91 sum += fParticleWeights.at(particle);
92 }
93 for (auto& entry : fParticleWeights) {
94 entry.second /= sum;
95 cout << "TRestGeant4ParticleSourceCosmics::InitFromConfigFile: particle: " << entry.first
96 << " sampling weight: " << entry.second << endl;
97 }
98
99 // file->Close();
100}
101
102void TRestGeant4ParticleSourceCosmics::Update() {
103 lock_guard<mutex> lock(fMutex);
104
105 RemoveParticles();
106
107 string particleName;
108 if (fParticleNames.size() == 1) {
109 particleName = *fParticleNames.begin();
110 } else {
111 const auto random = fRandom->Uniform(0, 1);
112 // choose a random particle based on the weights
113 double sum = 0;
114 for (const auto& entry : fParticleWeights) {
115 sum += entry.second;
116 if (random <= sum) {
117 particleName = entry.first;
118 break;
119 }
120 }
121 }
122
123 auto hist = fHistogramsTransformed.at(particleName);
124
125 double energy, zenith;
126 if (abs(fEnergyRange.first - fEnergyRange.second) <
127 1e-12) { // user has not defined a range TODO: improve how we check for this...
128 hist->GetRandom2(energy, zenith, fRandom.get());
129 } else {
130 // attempt to get a value in range, then use the counters to update simulation time
131 while (true) {
132 hist->GetRandom2(energy, zenith, fRandom.get());
133 // check counter does not overflow. If counter is at limit, stop updating it, we should have
134 // enough stats by now
135 if (fCounterEnergyTotal < std::numeric_limits<unsigned long long>::max()) {
136 fCounterEnergyTotal++;
137 }
138
139 if (energy >= fEnergyRange.first && energy <= fEnergyRange.second) {
140 if (fCounterEnergyTotal < std::numeric_limits<unsigned long long>::max()) {
141 fCounterEnergyAccepted++;
142 }
143 break;
144 }
145 }
146 }
147
148 particleName = geant4ParticleNames.at(particleName);
149
150 TRestGeant4Particle particle;
151 particle.SetParticleName(particleName);
152
153 particle.SetEnergy(energy * 1000); // Convert from MeV to keV
154
155 double phi = fRandom->Uniform(0, 1) * TMath::TwoPi();
156 double zenithRad = zenith * TMath::DegToRad();
157
158 // direction towards -y (can be rotated later)
159 const TVector3 direction = {TMath::Sin(zenithRad) * TMath::Cos(phi), -TMath::Cos(zenithRad),
160 TMath::Sin(zenithRad) * TMath::Sin(phi)};
161
162 particle.SetDirection(direction);
163
164 AddParticle(particle);
165}
166
167void TRestGeant4ParticleSourceCosmics::SetSeed(unsigned int seed) {
168 cout << "TRestGeant4ParticleSourceCosmics::SetSeed: " << seed << endl;
169 fRandom = std::make_unique<TRandom3>(seed);
170}
171
172double TRestGeant4ParticleSourceCosmics::GetEnergyRangeScalingFactor() const {
173 if (fCounterEnergyTotal == 0) {
174 return 1;
175 }
176
177 if (fParticleNames.size() > 1) {
178 throw std::runtime_error(
179 "TRestGeant4ParticleSourceCosmics::GetEnergyRangeScalingFactor is not implemented for multiple "
180 "particles.");
181 }
182
183 return double(fCounterEnergyAccepted) / double(fCounterEnergyTotal);
184}
void InitFromConfigFile() override
To make settings from rml file. This method must be implemented in the derived class.
A class used to store particle properties.
std::string GetParameter(std::string parName, TiXmlElement *e, TString defaultValue=PARAMETER_NOT_FOUND_STR)
Returns the value for the parameter named parName in the given section.