REST-for-Physics  v2.3
Rare Event Searches ToolKit for Physics
TRestGeant4VetoAnalysisProcess.cxx
1 /*************************************************************************
2  * This file is part of the REST software framework. *
3  * *
4  * Copyright (C) 2020 GIFNA/TREX (University of Zaragoza) *
5  * For more information see http://gifna.unizar.es/trex *
6  * *
7  * REST is free software: you can redistribute it and/or modify *
8  * it under the terms of the GNU General Public License as published by *
9  * the Free Software Foundation, either version 3 of the License, or *
10  * (at your option) any later version. *
11  * *
12  * REST is distributed in the hope that it will be useful, *
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15  * GNU General Public License for more details. *
16  * *
17  * You should have a copy of the GNU General Public License along with *
18  * REST in $REST_PATH/LICENSE. *
19  * If not, see http://www.gnu.org/licenses/. *
20  * For the list of contributors see $REST_PATH/CREDITS. *
21  *************************************************************************/
22 
65 
66 #include "TRestGeant4VetoAnalysisProcess.h"
67 
68 #include <TObjArray.h>
69 #include <TObjString.h>
70 #include <TString.h>
71 
72 #include <iostream>
73 #include <unordered_map>
74 
75 using namespace std;
76 
78 
79 int findMostFrequent(const vector<int>& nums) {
80  if (nums.empty()) {
81  return 0;
82  }
83 
84  unordered_map<int, int> freqMap;
85 
86  // Count the frequency of each element
87  for (int num : nums) {
88  freqMap[num]++;
89  }
90 
91  int mostFrequent = nums[0];
92  int maxFrequency = 0;
93 
94  // Find the most frequent element
95  for (const auto& entry : freqMap) {
96  if (entry.second > maxFrequency) {
97  mostFrequent = entry.first;
98  maxFrequency = entry.second;
99  }
100  }
101 
102  return mostFrequent;
103 }
104 
105 Veto TRestGeant4VetoAnalysisProcess::GetVetoFromString(const string& input) {
106  // example input:
107  // VetoSystem_vetoSystemEast_vetoLayerEast2_assembly-13.veto2_scintillatorVolume-1500.0mm-f1a5df68
108 
109  Veto veto;
110  veto.name = input;
111 
112  TObjArray* parts = TString(input).Tokenize("_");
113 
114  if (parts->GetEntries() >= 5) {
115  TString group = ((TObjString*)parts->At(1))->GetString();
116  // remove leading "vetoSystem" from group if present
117  if (group.Index("vetoSystem") == 0) {
118  group = group(10, group.Length() - 10);
119  }
120 
121  TString layer = ((TObjString*)parts->At(2))->GetString();
122  // layer is last character of layer string
123  layer = layer(layer.Length() - 1, 1);
124 
125  TString numberAndRest = ((TObjString*)parts->At(parts->GetEntries() - 2))->GetString();
126  Ssiz_t vetoPos = numberAndRest.Index("veto");
127  if (vetoPos != kNPOS) {
128  TString number = numberAndRest(vetoPos + 4, numberAndRest.Length() - vetoPos - 4);
129 
130  TString lengthAndRest = ((TObjString*)parts->At(parts->GetEntries() - 1))->GetString();
131  Ssiz_t mmPos = lengthAndRest.Index("mm");
132  if (mmPos != kNPOS) {
133  TString length = lengthAndRest(0, mmPos);
134  // if "scintillatorVolume" is present, remove it
135  if (length.Index("scintillatorVolume") == 0) {
136  length = length(19, length.Length() - 19);
137  }
138 
139  veto.group = group.Data();
140  veto.layer = layer.Atoi();
141  veto.number = number.Atoi();
142  veto.length = length.Atof();
143 
144  } else {
145  cout << "No match found." << endl;
146  }
147  } else {
148  cout << "No match found." << endl;
149  }
150  } else {
151  cout << "No match found." << endl;
152  }
153 
154  delete parts;
155 
156  veto.alias = veto.group + "_L" + veto.layer + "_N" + veto.number;
157  return veto;
158 }
159 
160 TRestGeant4VetoAnalysisProcess::TRestGeant4VetoAnalysisProcess() { Initialize(); }
161 
162 TRestGeant4VetoAnalysisProcess::TRestGeant4VetoAnalysisProcess(const char* configFilename) {
164  if (LoadConfigFromFile(configFilename)) {
165  LoadDefaultConfig();
166  }
167 }
168 
169 TRestGeant4VetoAnalysisProcess::~TRestGeant4VetoAnalysisProcess() { delete fOutputEvent; }
170 
174 void TRestGeant4VetoAnalysisProcess::LoadDefaultConfig() { SetTitle("Default config"); }
175 
181  SetSectionName(this->ClassName());
182  SetLibraryVersion(LIBRARY_VERSION);
183 
184  fOutputEvent = new TRestGeant4Event();
185 }
186 
199 void TRestGeant4VetoAnalysisProcess::LoadConfig(const string& configFilename, const string& name) {
200  if (LoadConfigFromFile(configFilename, name)) {
201  LoadDefaultConfig();
202  }
203 }
204 
209  // CAREFUL THIS METHOD IS CALLED TWICE!
210  fVetoVolumes.clear();
211 
212  if (fGeant4Metadata == nullptr) {
213  // maybe it was manually initialized
214  fGeant4Metadata = GetMetadata<TRestGeant4Metadata>();
215  }
216  if (fGeant4Metadata == nullptr) {
217  cerr << "TRestGeant4VetoAnalysisProcess::InitProcess: Geant4 metadata not found" << endl;
218  exit(1);
219  }
220 
221  RESTDebug << "Expression: " << fVetoVolumesExpression << RESTendl;
222  const auto& geometryInfo = fGeant4Metadata->GetGeant4GeometryInfo();
223 
224  auto vetoVolumes = geometryInfo.GetAllPhysicalVolumesMatchingExpression(fVetoVolumesExpression);
225  if (vetoVolumes.empty()) {
226  const auto logicalVolumes =
227  geometryInfo.GetAllLogicalVolumesMatchingExpression(fVetoVolumesExpression);
228  for (const auto& logicalVolume : logicalVolumes) {
229  for (const auto& physicalVolume : geometryInfo.GetAllPhysicalVolumesFromLogical(logicalVolume)) {
230  const string name =
231  geometryInfo.GetAlternativeNameFromGeant4PhysicalName(physicalVolume).Data();
232  Veto veto = GetVetoFromString(name);
233  veto.position = geometryInfo.GetPosition(name);
234  fVetoVolumes.push_back(veto);
235  }
236  }
237  }
238 
239  // PrintMetadata();
240 }
241 
246  fInputEvent = (TRestGeant4Event*)inputEvent;
247  *fOutputEvent = *((TRestGeant4Event*)inputEvent);
248 
249  map<string, double> vetoEnergyMap;
250  map<string, double> vetoGroupEnergyMap;
251  map<pair<string, int>, double> vetoGroupLayerEnergyMap;
252  double totalVetoEnergy = 0;
253 
254  for (const auto& veto : fVetoVolumes) {
255  const double energy = fInputEvent->GetEnergyInVolume(veto.name);
256 
257  totalVetoEnergy += energy;
258  vetoEnergyMap[veto.name] = energy;
259  vetoGroupEnergyMap[veto.group] += energy;
260  vetoGroupLayerEnergyMap[make_pair(veto.group, veto.layer)] += energy;
261 
262  SetObservableValue("EnergyVeto_" + veto.alias, energy);
263  }
264 
265  for (const auto& [group, energy] : vetoGroupEnergyMap) {
266  SetObservableValue("EnergyGroup_" + group, energy);
267  }
268 
269  for (const auto& [groupLayer, energy] : vetoGroupLayerEnergyMap) {
270  SetObservableValue("EnergyGroupLayer_" + groupLayer.first + "_" + to_string(groupLayer.second),
271  energy);
272  }
273 
274  SetObservableValue("EnergyTotal", totalVetoEnergy);
275 
276  // compute max energy
277  double maxEnergy = 0;
278  for (const auto& [name, energy] : vetoEnergyMap) {
279  if (energy > maxEnergy) {
280  maxEnergy = energy;
281  }
282  }
283  SetObservableValue("EnergyMax", maxEnergy);
284 
285  // compute max energy for each group
286  map<string, double> vetoGroupMaxEnergyMap;
287  for (const auto& [name, energy] : vetoEnergyMap) {
288  const auto veto = GetVetoFromString(name);
289  if (vetoGroupMaxEnergyMap[veto.group] < energy) {
290  vetoGroupMaxEnergyMap[veto.group] = energy;
291  }
292  }
293  for (const auto& [group, energy] : vetoGroupMaxEnergyMap) {
294  SetObservableValue("EnergyGroupMax_" + group, energy);
295  }
296 
297  // compute max energy for each group layer
298  map<pair<string, int>, double> vetoGroupLayerMaxEnergyMap;
299  for (const auto& [name, energy] : vetoEnergyMap) {
300  const auto veto = GetVetoFromString(name);
301  if (vetoGroupLayerMaxEnergyMap[make_pair(veto.group, veto.layer)] < energy) {
302  vetoGroupLayerMaxEnergyMap[make_pair(veto.group, veto.layer)] = energy;
303  }
304  }
305 
306  for (const auto& [groupLayer, energy] : vetoGroupLayerMaxEnergyMap) {
307  SetObservableValue("EnergyGroupLayerMax_" + groupLayer.first + "_" + to_string(groupLayer.second),
308  energy);
309  }
310 
311  // compute max energy for each layer (all groups)
312  map<int, double> vetoLayerMaxEnergyMap;
313  for (const auto& [name, energy] : vetoEnergyMap) {
314  const auto veto = GetVetoFromString(name);
315  if (vetoLayerMaxEnergyMap[veto.layer] < energy) {
316  vetoLayerMaxEnergyMap[veto.layer] = energy;
317  }
318  }
319  for (const auto& [layer, energy] : vetoLayerMaxEnergyMap) {
320  SetObservableValue("EnergyLayerMax_" + to_string(layer), energy);
321  }
322 
323  // compute neutron capture observables
324  int nCaptures = 0;
325  int nCapturesInCaptureVolumes = 0;
326  int nCapturesInVetoVolumes = 0;
327 
328  vector<float> nCapturesInCaptureVolumesTimes;
329  vector<vector<float>> nCapturesInCaptureVolumesChildGammaEnergies;
330  // for each child gamma, how much energy was deposited in all vetoes
331  vector<float> nCapturesInCaptureVolumesChildGammasEnergyInVetos;
332  vector<vector<float>> nCapturesInCaptureVolumesEnergyInVetoesForCapture;
333 
334  set<int> nCapturesInCaptureVolumesNeutronTrackIds;
335 
336  vector<float> nCapturesInCaptureVolumesPositionX;
337  vector<float> nCapturesInCaptureVolumesPositionY;
338  vector<float> nCapturesInCaptureVolumesPositionZ;
339 
340  for (const auto& track : fInputEvent->GetTracks()) {
341  if (track.GetParticleName() == "neutron") {
342  const auto hits = track.GetHits();
343  for (size_t hitIndex = 0; hitIndex < hits.GetNumberOfHits(); hitIndex++) {
344  const auto processName = hits.GetProcessName(hitIndex);
345  if (processName != "nCapture") {
346  continue;
347  }
348  nCaptures++;
349  const string volumeName = hits.GetVolumeName(hitIndex).Data();
350  const double time = hits.GetTime(hitIndex);
351 
352  if (volumeName.find("captureLayerVolume") != string::npos) {
353  nCapturesInCaptureVolumes++;
354  nCapturesInCaptureVolumesNeutronTrackIds.insert(track.GetTrackID());
355  nCapturesInCaptureVolumesTimes.push_back(time);
356  const TVector3& position = hits.GetPosition(hitIndex);
357  nCapturesInCaptureVolumesPositionX.push_back(position.X());
358  nCapturesInCaptureVolumesPositionY.push_back(position.Y());
359  nCapturesInCaptureVolumesPositionZ.push_back(position.Z());
360  vector<float> childrenEnergy;
361  const auto children = track.GetChildrenTracks();
362  for (const auto& child : children) {
363  if (child->GetParticleName() != "gamma") {
364  continue;
365  }
366  if (child->GetCreatorProcess() != "nCapture") {
367  continue;
368  }
369  childrenEnergy.push_back(child->GetInitialKineticEnergy());
370  double energyInVeto = 0;
371  for (const auto& veto : fVetoVolumes) {
372  energyInVeto += child->GetEnergyInVolume(veto.name, true);
373  }
374  nCapturesInCaptureVolumesChildGammasEnergyInVetos.push_back(energyInVeto);
375  }
376  nCapturesInCaptureVolumesChildGammaEnergies.push_back(childrenEnergy);
377 
378  vector<float> energyInVetoesForCapture;
379  for (const auto& veto : fVetoVolumes) {
380  const double energyInVeto = track.GetEnergyInVolume(veto.name, true);
381  if (energyInVeto > 0) { // soft limit of 100 keV
382  energyInVetoesForCapture.push_back(energyInVeto);
383  }
384  }
385  nCapturesInCaptureVolumesEnergyInVetoesForCapture.push_back(energyInVetoesForCapture);
386  }
387  if (volumeName.find("scintillatorVolume") != string::npos) {
388  nCapturesInVetoVolumes++;
389  }
390  }
391  }
392  }
393 
394  SetObservableValue("nCapturesInCaptureVolumesPositionX", nCapturesInCaptureVolumesPositionX);
395  SetObservableValue("nCapturesInCaptureVolumesPositionY", nCapturesInCaptureVolumesPositionY);
396  SetObservableValue("nCapturesInCaptureVolumesPositionZ", nCapturesInCaptureVolumesPositionZ);
397 
398  vector<float> nCapturesInCaptureVolumesPositionOriginX;
399  vector<float> nCapturesInCaptureVolumesPositionOriginY;
400  vector<float> nCapturesInCaptureVolumesPositionOriginZ;
401 
402  // compute observables for neutrons in capture volumes
403  vector<double> nCapturesInCaptureVolumesNeutronInitialEnergy;
404  vector<int> nCapturesInCaptureVolumesNeutronGeneration; // Number of inelastic interactions leading to
405  // neutron (primary neutron has generation 0)
406  for (const auto& neutronCaptureTrackId : nCapturesInCaptureVolumesNeutronTrackIds) {
407  auto track = fInputEvent->GetTrackByID(neutronCaptureTrackId);
408  if (track->GetParticleName() != "neutron") {
409  cerr << "TRestGeant4VetoAnalysisProcess::ProcessEvent: track is not a neutron" << endl;
410  exit(1);
411  }
412 
413  int generation = 0;
414  double energy = track->GetInitialKineticEnergy();
415 
416  while (track->GetParentTrack() != nullptr) {
417  track = track->GetParentTrack();
418  if (track->GetParticleName() == "neutron") {
419  generation++;
420  }
421  }
422 
423  const TVector3& origin = track->GetTrackOrigin();
424  nCapturesInCaptureVolumesPositionOriginX.push_back(origin.X());
425  nCapturesInCaptureVolumesPositionOriginY.push_back(origin.Y());
426  nCapturesInCaptureVolumesPositionOriginZ.push_back(origin.Z());
427 
428  nCapturesInCaptureVolumesNeutronInitialEnergy.push_back(energy);
429  nCapturesInCaptureVolumesNeutronGeneration.push_back(generation);
430  }
431 
432  SetObservableValue("nCapturesInCaptureVolumesPositionOriginX", nCapturesInCaptureVolumesPositionOriginX);
433  SetObservableValue("nCapturesInCaptureVolumesPositionOriginY", nCapturesInCaptureVolumesPositionOriginY);
434  SetObservableValue("nCapturesInCaptureVolumesPositionOriginZ", nCapturesInCaptureVolumesPositionOriginZ);
435 
436  SetObservableValue("nCapturesInCaptureVolumesNeutronInitialEnergy",
437  nCapturesInCaptureVolumesNeutronInitialEnergy);
438  SetObservableValue("nCapturesInCaptureVolumesNeutronGeneration",
439  nCapturesInCaptureVolumesNeutronGeneration);
440 
441  // Set capture observables
442  SetObservableValue("nCaptures", nCaptures);
443  SetObservableValue("nCapturesInCaptureVolumes", nCapturesInCaptureVolumes);
444  SetObservableValue("nCapturesInVetoVolumes", nCapturesInVetoVolumes);
445 
446  // Set capture time observables
447  SetObservableValue("nCapturesInCaptureVolumesTimes", nCapturesInCaptureVolumesTimes);
448  // cannot insert a vector of vectors, need to flatten it
449  vector<float> nCapturesInCaptureVolumesChildGammaEnergiesFlat;
450  vector<int> nCapturesInCaptureVolumesChildGammaCount;
451  for (const auto& v : nCapturesInCaptureVolumesChildGammaEnergies) {
452  nCapturesInCaptureVolumesChildGammaEnergiesFlat.insert(
453  nCapturesInCaptureVolumesChildGammaEnergiesFlat.end(), v.begin(), v.end());
454  nCapturesInCaptureVolumesChildGammaCount.push_back(v.size());
455  }
456 
457  float nCapturesInCaptureVolumesChildGammaCountAverage = 0;
458  for (const auto& v : nCapturesInCaptureVolumesChildGammaCount) {
459  nCapturesInCaptureVolumesChildGammaCountAverage +=
460  float(v) / nCapturesInCaptureVolumesChildGammaCount.size();
461  }
462 
463  SetObservableValue("nCapturesInCaptureVolumesChildGammaEnergies",
464  nCapturesInCaptureVolumesChildGammaEnergiesFlat);
465  SetObservableValue("nCapturesInCaptureVolumesChildGammaCount", nCapturesInCaptureVolumesChildGammaCount);
466  SetObservableValue("nCapturesInCaptureVolumesChildGammaCountAverage",
467  nCapturesInCaptureVolumesChildGammaCountAverage);
468  SetObservableValue("nCapturesInCaptureVolumesChildGammaCountMostFrequent",
469  findMostFrequent(nCapturesInCaptureVolumesChildGammaCount));
470  SetObservableValue("nCapturesInCaptureVolumesChildGammaCountTotal",
471  nCapturesInCaptureVolumesChildGammaEnergiesFlat.size());
472 
473  SetObservableValue("nCapturesInCaptureVolumesChildGammasEnergyInVetos",
474  nCapturesInCaptureVolumesChildGammasEnergyInVetos);
475 
476  auto nCapturesInCaptureVolumesChildGammasEnergyInVetosTotal = 0;
477  for (const auto& energy : nCapturesInCaptureVolumesChildGammasEnergyInVetos) {
478  nCapturesInCaptureVolumesChildGammasEnergyInVetosTotal += energy;
479  }
480 
481  SetObservableValue("nCapturesInCaptureVolumesChildGammasEnergyInVetosTotal",
482  nCapturesInCaptureVolumesChildGammasEnergyInVetosTotal);
483 
484  SetObservableValue("nCapturesInCaptureVolumesEnergyInVetoesForCapture",
485  nCapturesInCaptureVolumesEnergyInVetoesForCapture);
486 
487  const vector<float> energyInVetoesThresholds = {0, 100, 250, 500, 1000, 1500};
488  for (const auto& threshold : energyInVetoesThresholds) {
489  vector<vector<int>> nCapturesInCaptureVolumesEnergyInVetoesForCaptureThreshold;
490  for (const auto& captureForNeutronEnergies : nCapturesInCaptureVolumesEnergyInVetoesForCapture) {
491  int nCapturesInCaptureVolumesVetoesAboveThresholdForCapture = 0;
492  for (const auto& energy : captureForNeutronEnergies) {
493  if (energy > threshold) {
494  nCapturesInCaptureVolumesVetoesAboveThresholdForCapture++;
495  }
496  }
497  nCapturesInCaptureVolumesEnergyInVetoesForCaptureThreshold.push_back(
498  {nCapturesInCaptureVolumesVetoesAboveThresholdForCapture});
499  }
500  SetObservableValue(
501  "nCapturesInCaptureVolumesVetoesAboveThresholdForCapture" + to_string(int(threshold)) + "keV",
502  nCapturesInCaptureVolumesEnergyInVetoesForCaptureThreshold);
503  }
504  return fOutputEvent;
505 }
506 
512  // Function to be executed once at the end of the process
513  // (after all events have been processed)
514 
515  // Start by calling the EndProcess function of the abstract class.
516  // Comment this if you don't want it.
517  // TRestEventProcess::EndProcess();
518 }
519 
525  fVetoVolumesExpression = GetParameter("vetoVolumesExpression", fVetoVolumesExpression);
526 }
527 
529  BeginPrintProcess();
530 
531  cout << "Number of veto volumes: " << fVetoVolumes.size() << endl;
532  for (const auto& veto : fVetoVolumes) {
533  cout << "Veto: " << veto.name << endl;
534  cout << " - Alias: " << veto.alias << endl;
535  cout << " - Group: " << veto.group << endl;
536  cout << " - Layer: " << veto.layer << endl;
537  cout << " - Number: " << veto.number << endl;
538  cout << " - Position: (" << veto.position.x() << ", " << veto.position.y() << ", "
539  << veto.position.z() << ")" << endl;
540  cout << " - Length: " << veto.length << endl;
541  }
542 }
A base class for any REST event.
Definition: TRestEvent.h:38
An event class to store geant4 generated event information.
void LoadConfig(const std::string &configFilename, const std::string &name="")
Function to load the configuration from an external configuration file.
TRestEvent * ProcessEvent(TRestEvent *inputEvent) override
The main processing event function.
void PrintMetadata() override
Implemented it in the derived metadata class to print out specific metadata information.
void LoadDefaultConfig()
Function to load the default config in absence of RML input.
void EndProcess() override
Function to include required actions after all events have been processed.
void InitFromConfigFile() override
Function to read input parameters from the RML TRestGeant4VetoAnalysisProcess metadata section.
void Initialize() override
Function to initialize input/output event members and define the section name.
void InitProcess() override
Process initialization.