GGS(GenericGEANT4Simulation)Software  2.99.0
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Macros
GGSRootFileService.cpp
1 /*
2  * GGSRootFileService.cpp
3  *
4  * Created on: 03 Jun 2011
5  * Author: Nicola Mori
6  */
7 
12 #include "montecarlo/dataobjs/GGSTHitVolInfo.h"
13 #include "montecarlo/dataobjs/GGSTParameters.h"
15 #include "utils/GGSSmartLog.h"
16 
17 #include "G4LogicalVolume.hh"
18 #include "G4Run.hh"
19 #include "G4SystemOfUnits.hh"
20 
21 #include "ROOT/TBufferMerger.hxx"
22 #include "TTree.h"
23 
24 #include <sstream>
25 
26 // Definition of the std::hash<GGSRootFileService::VolumeKey> specialization
27 namespace std {
28 // The standard library does not provide an equivalent of boost::hash_combine, so
29 // the implementation from Boost 1.75.0 for 64 bit machines is copy-pasted here.
30 inline void hash_combine_impl(uint64_t &h, uint64_t k) {
31  constexpr uint64_t m = UINT64_C(0xc6a4a7935bd1e995);
32  constexpr int r = 47;
33 
34  k *= m;
35  k ^= k >> r;
36  k *= m;
37 
38  h ^= k;
39  h *= m;
40 
41  // Completely arbitrary number, to prevent 0's
42  // from hashing to 0.
43  h += 0xe6546b64;
44 }
45 template <class T> inline void hash_combine(uint64_t &seed, T const &v) {
46  std::hash<T> hasher;
47  hash_combine_impl(seed, hasher(v));
48 }
49 
50 // With the above definition of hash_combine, define the hash struct for GGSRootFileService::VolumeKey
51 template <> struct hash<GGSRootFileService::VolumeKey> {
52  uint64_t operator()(GGSRootFileService::VolumeKey const &key) const noexcept {
53  uint64_t seed = 0;
54  hash_combine(seed, key.name);
55  hash_combine(seed, key.physVol);
56  hash_combine(seed, key.position[0]);
57  hash_combine(seed, key.position[1]);
58  hash_combine(seed, key.position[2]);
59  return seed;
60  }
61 };
62 } // namespace std
63 
64 // Variables used for performance in StoreVolume
65 G4ThreadLocal TFile *GGSRootFileService::_currVolStorageTFile = nullptr;
66 G4ThreadLocal std::string GGSRootFileService::_currDetVolName = "";
67 G4ThreadLocal GGSRootFileService::FileInfoContainer::iterator GGSRootFileService::_currVolStorageFileInfo;
68 G4ThreadLocal GGSRootFileService::HitDetMap::iterator GGSRootFileService::_currDetector;
69 G4ThreadLocal GGSTHitDetInfo *GGSRootFileService::_currPersDetector = nullptr;
70 
72  static GGSRootFileService instance;
73  return instance;
74 }
75 
76 GGSRootFileService::GGSRootFileService()
77  : _suffix(""), _defaultOutBase("GGSRootOutput"), _singleFileForMT(true), _messenger(this, "/GGS/rootOutput/") {
78  _messenger.DeclareMethod("setMultiFileForMT", &GGSRootFileService::_SetMultiFileMode,
79  "Set the output mode for MT mode to one file per thread");
80 }
81 
82 namespace {
83 G4Mutex mutexForGetFile = G4MUTEX_INITIALIZER;
84 }
85 TFile *GGSRootFileService::GetFileForThisRun(const std::filesystem::path &baseName, const G4Run *run) {
86 
87  G4AutoLock lock(mutexForGetFile);
88 
89  std::filesystem::path absPath;
90 
91  // Set default if no base name has been provided
92  if (baseName.string() == "") {
93  absPath = _defaultOutBase;
94  } else {
95  absPath = baseName;
96  }
97 
98  // Retrieve absolute path
99  absPath = _GetAbsolutePath(absPath);
100 
101  // Strip extension and append suffix and extension
102  absPath = _AppendSuffixAndExt(absPath, run);
103 
104  // Lambda that creates a new TFile for current thread and for the given file info
105  auto CreateNewTFile = [this, &absPath](FileInfoContainer::iterator fileInfoIter) -> TFile * {
106  std::shared_ptr<TFile> newTFile = nullptr;
107  if (G4Threading::IsMultithreadedApplication() && _singleFileForMT) {
108  // Create the TFile with TBufferMerger in MT single-file mode
109  if (!fileInfoIter->bufferMerger) {
110  fileInfoIter->bufferMerger = std::make_unique<ROOT::TBufferMerger>(absPath.string().c_str());
111  }
112  newTFile = fileInfoIter->bufferMerger->GetFile();
113  } else {
114  // Create the TFile normally in ST mode or in MT multi-file mode
115  if (fileInfoIter->tFilesInfo.size() != 0) {
116  G4Exception("GGSRootFileService::GetFileForThisRun", "TFile clash", G4ExceptionSeverity::FatalException,
117  ("A TFile is already existing for file " + absPath.string() + " in ST mode; can't create it again")
118  .c_str());
119  }
120  newTFile = std::make_shared<TFile>(absPath.string().c_str(), "RECREATE");
121  }
122  fileInfoIter->tFilesInfo.emplace_back();
123  auto &tFileInfo = fileInfoIter->tFilesInfo.back();
124  tFileInfo.tFile = newTFile;
125  tFileInfo.defaultEventsTree = nullptr;
126  tFileInfo.threadNo = G4Threading::G4GetThreadId();
127  tFileInfo.nRequests = 1;
128 
129  return newTFile.get();
130  };
131 
132  // Check if the file info is already present
133  int iThread = G4Threading::G4GetThreadId();
134  auto fileInfoIter = _filesInfo.begin();
135  for (; fileInfoIter != _filesInfo.end(); ++fileInfoIter) {
136  if (absPath == fileInfoIter->absPath) {
137  // File info found. Check for the TFile
138  for (auto &tFileInfo : fileInfoIter->tFilesInfo) {
139  if (tFileInfo.threadNo == iThread) {
140  ++(tFileInfo.nRequests);
141  return tFileInfo.tFile.get();
142  }
143  }
144  // TFile not found. Create and return it
145  return CreateNewTFile(fileInfoIter);
146  }
147  }
148 
149  // File info has not been found, so create it
150  _filesInfo.emplace_back();
151  fileInfoIter = --(_filesInfo.end());
152  fileInfoIter->absPath = absPath;
153 
154  // Create the TFile
155  TFile *newTFile = CreateNewTFile(fileInfoIter);
156 
157  // Set storage of detector to nullptr values to force proper initialization in StoreVolume
158  _currVolStorageTFile = nullptr;
159  _currDetVolName = "";
160 
161  return newTFile;
162 }
163 
164 namespace {
165 G4Mutex mutexForCloseFile = G4MUTEX_INITIALIZER;
166 }
167 void GGSRootFileService::CloseFileForThisRun(const std::filesystem::path &baseName, const G4Run *run) {
168 
169  G4AutoLock lock(mutexForCloseFile);
170 
171  std::filesystem::path absPath;
172 
173  // Set default if no base name has been provided
174  if (baseName.string() == "") {
175  absPath = _defaultOutBase;
176  } else
177  absPath = baseName;
178  // Retrieve absolute path
179  absPath = _GetAbsolutePath(absPath);
180 
181  // Strip extension and append suffix and extension
182  absPath = _AppendSuffixAndExt(absPath, run);
183 
184  // Helper lambda that writes the general simulation information on the output file
185  auto WriteGeneralSimInfo = [this](TFile *outFile) {
186  // Write simulation informations
187  outFile->WriteObjectAny(&_simInfo, "GGSTSimInfo", _simInfo.GetName());
188 
189  // Write geometry informations
191  GGSTGeoParams geoParams;
192  geoParams.SetIntGeoParams(geoCons->GetIntParameters());
193  geoParams.SetBoolGeoParams(geoCons->GetBoolParameters());
194  geoParams.SetRealGeoParams(geoCons->GetRealParameters());
195  geoParams.SetStringGeoParams(geoCons->GetStringParameters());
196  geoParams.SetVectIntGeoParams(geoCons->GetVectIntParameters());
197  geoParams.SetVectBoolGeoParams(geoCons->GetVectBoolParameters());
198  geoParams.SetVectRealGeoParams(geoCons->GetVectRealParameters());
199  geoParams.SetVectStringGeoParams(geoCons->GetVectStringParameters());
200  outFile->WriteObject(&geoParams, "GGSGeoParams");
201 
202  // Write generator informations
203  // These are written only for GGS generators that export a parameter object containing the "generator" name
204  // setting.
205  auto *genAction =
206  dynamic_cast<const GGSGeneratorAction *>(GGSRunManager::GetRunManager()->GetUserPrimaryGeneratorAction());
207  if (genAction) {
208  GGSTParameters generatorParams(genAction->GetParameters());
209  try {
210  generatorParams.GetParam<std::string>("generator"); // Throws if "generator" parameter is not present
211  outFile->WriteObject(&generatorParams, "GGSGenParams");
212  } catch (...) {
213  }
214  }
215  };
216 
217  int iThread = G4Threading::G4GetThreadId();
218  for (auto fileInfoIter = _filesInfo.begin(); fileInfoIter != _filesInfo.end(); ++fileInfoIter) {
219  if (fileInfoIter->absPath == absPath) {
220  for (auto tFileInfoIter = fileInfoIter->tFilesInfo.begin(); tFileInfoIter != fileInfoIter->tFilesInfo.end();
221  ++tFileInfoIter) {
222  if (tFileInfoIter->threadNo == iThread) {
223  --(tFileInfoIter->nRequests);
224  if (tFileInfoIter->nRequests == 0) {
225  if (tFileInfoIter->defaultEventsTree) {
226  tFileInfoIter->defaultEventsTree->Write(); // The tree is already associated to the right TFile
227  }
228  // Write the detectors array and the general simulation information in the file, but if there is a buffer
229  // merger then do it later just on the merged file
230  if (!(fileInfoIter->bufferMerger)) {
231  if (fileInfoIter->detectorsArray) {
232  tFileInfoIter->tFile->WriteObject(fileInfoIter->detectorsArray, "GGSHitDetInfo", "SingleKey");
233  }
234  WriteGeneralSimInfo(tFileInfoIter->tFile.get());
235  }
236  // Mute due to this:
237  // https://root-forum.cern.ch/t/warning-message-when-merging-files-with-tbuffermerger/42349
238  // WARNING: this will suppress any output message from other threads occurring at the same time.
239  GGSSmartLog::MuteOutput();
240  tFileInfoIter->tFile->Write();
241  GGSSmartLog::UnmuteOutput();
242  tFileInfoIter = --(fileInfoIter->tFilesInfo.erase(tFileInfoIter));
243  } else {
244  return;
245  }
246  }
247  }
248  if (fileInfoIter->tFilesInfo.size() == 0) {
249  if (fileInfoIter->bufferMerger) {
250  // Delete the buffer merger
251  fileInfoIter->bufferMerger.reset();
252  // Open the file for writing global objects
253  auto outFile = std::unique_ptr<TFile>(TFile::Open(fileInfoIter->absPath.string().c_str(), "UPDATE"));
254  if (fileInfoIter->detectorsArray) {
255  outFile->WriteObject(fileInfoIter->detectorsArray, "GGSHitDetInfo", "SingleKey");
256  }
257  WriteGeneralSimInfo(outFile.get());
258  outFile->Close();
259  }
260  fileInfoIter = --(_filesInfo.erase(fileInfoIter));
261  }
262  }
263  }
264 }
265 
267  // Don't allow other threads to ask for a file in the meantime since this might invalidate the references in the
268  // loops
269  G4AutoLock lock(mutexForGetFile);
270 
271  for (auto &fileInfo : _filesInfo) {
272  for (auto &tFileInfo : fileInfo.tFilesInfo) {
273  if (file == tFileInfo.tFile.get()) {
274  if (tFileInfo.defaultEventsTree == nullptr) {
275  tFileInfo.defaultEventsTree = new TTree("GGSEventsTree", "GGS events tree. Info: ", 99, file);
276  }
277  return tFileInfo.defaultEventsTree;
278  }
279  }
280  }
281  return nullptr;
282 }
283 
284 std::filesystem::path GGSRootFileService::_GetAbsolutePath(const std::filesystem::path &baseName) {
285 
286  std::filesystem::path absPath(baseName);
287 
288  absPath = absolute(baseName);
289 
290  return absPath;
291 }
292 
293 std::filesystem::path GGSRootFileService::_AppendSuffixAndExt(const std::filesystem::path &baseName, const G4Run *run) {
294 
295  std::filesystem::path absPath(baseName);
296  absPath.replace_extension(); // Remove the extension
297 
298  // Append thread no. in MT multi-file mode
299  if (G4Threading::IsMultithreadedApplication() && !_singleFileForMT) {
300  absPath += "_Thread" + std::to_string(G4Threading::G4GetThreadId());
301  }
302 
303  // if runID > 0 append _RunID
304  int runID = run->GetRunID();
305  if (runID > 0) {
306  absPath += "_Run" + std::to_string(runID);
307  }
308 
309  // Append suffix
310  if (_suffix != "") {
311  absPath += "_" + _suffix;
312  }
313 
314  // Append extension
315  absPath += ".root";
316 
317  return absPath;
318 }
319 
320 void GGSRootFileService::_SetMultiFileMode() {
321  const std::string routineName("GGSRootFileService::_SetMultiFileMode");
322  if (G4Threading::IsMultithreadedApplication()) {
323  _singleFileForMT = false;
324  } else {
325  GGSCOUT(WARNING) << "ST application: ignoring multi-file output setting" << GGSENDL;
326  }
327 }
328 
329 void GGSRootFileService::_FillDefaultEventsTrees() {
330  // No need to acquire a lock since there's always one TFile (either a plain TFile or a TBufferMergerFile) per
331  // thread.
332  for (auto &fileInfo : _filesInfo) {
333  for (auto &tFileInfo : fileInfo.tFilesInfo) {
334  if (tFileInfo.tFile && tFileInfo.defaultEventsTree && tFileInfo.threadNo == G4Threading::G4GetThreadId()) {
335  tFileInfo.tFile->cd();
336  tFileInfo.defaultEventsTree->Fill();
337  }
338  }
339  }
340 }
341 
342 namespace {
343 G4Mutex mutexForStoreVolume = G4MUTEX_INITIALIZER;
344 }
345 int GGSRootFileService::StoreVolume(TFile *tFilePtr, const std::string &detector, const G4VPhysicalVolume *volume,
346  const G4ThreeVector &position, int id) {
347  static const std::string routineName("GGSRootFileService::StoreVolume");
348 
349  G4AutoLock lock(mutexForStoreVolume);
350  std::string detVolName = detector.substr(0, detector.find_first_of('.'));
351 
352  // 1. Retrieve file info structure
353  bool fileChanged = false, found = false;
354  if (tFilePtr != _currVolStorageTFile) {
355  fileChanged = true;
356  _currVolStorageTFile = tFilePtr;
357 
358  for (_currVolStorageFileInfo = _filesInfo.begin(); _currVolStorageFileInfo != _filesInfo.end();
359  ++_currVolStorageFileInfo) {
360  for (auto &tFileInfo : _currVolStorageFileInfo->tFilesInfo) {
361  if (tFileInfo.tFile.get() == tFilePtr) {
362  found = true;
363  break;
364  }
365  }
366  if (found) {
367  break;
368  }
369  }
370  if (!found) {
371  G4Exception("GGSRootFileService::StoreVolume", "TFile not found", G4ExceptionSeverity::FatalException,
372  (std::string("TFile for ") + tFilePtr->GetName() + " is not managed by GGSRootFileService").c_str());
373  }
374  }
375 
376  // 2. Find the detector in the index
377  if (fileChanged || _currDetVolName != detVolName) {
378  _currDetVolName = detVolName;
379  int currPersDetectorIndex = 0;
380  for (_currDetector = _currVolStorageFileInfo->detectorsMap.begin();
381  _currDetector != _currVolStorageFileInfo->detectorsMap.end(); ++_currDetector, ++currPersDetectorIndex) {
382  if (_currDetector->first == _currDetVolName) {
383  break;
384  }
385  }
386  if (_currDetector == _currVolStorageFileInfo->detectorsMap.end()) {
387  // 2.1 Detector not found. Create it both in index and persistence array
388  _currVolStorageFileInfo->detectorsMap.push_back(
389  std::make_pair<std::string, HitVolMap>(std::string(_currDetVolName), HitVolMap()));
390  _currDetector = _currVolStorageFileInfo->detectorsMap.end(); // Past-the-end iterator
391  --_currDetector; // Set the iterator to the last element
392  if (_currVolStorageFileInfo->detectorsArray == nullptr) {
393  _currVolStorageFileInfo->detectorsArray = new TClonesArray("GGSTHitDetInfo");
394  }
395  currPersDetectorIndex = _currVolStorageFileInfo->detectorsArray->GetEntries(); // Redundant
396  _currPersDetector = new ((*(_currVolStorageFileInfo->detectorsArray))[currPersDetectorIndex]) GGSTHitDetInfo;
397  _currPersDetector->detectorName = _currDetVolName.data();
398  } else {
399  // 2.2 Detector found. Set the current persistent detector
400  _currPersDetector = (GGSTHitDetInfo *)(_currVolStorageFileInfo->detectorsArray->At(currPersDetectorIndex));
401  }
402  }
403 
404  // 3. Insert the volume in the index and in the persistency structure
405 
406  // Append volume ID to volume name to avoid placing all the replicated volumes at the same place in the index
407  std::string volAndId = volume->GetName() + std::to_string(id);
408 
409  auto insertResult =
410  _currDetector->second.emplace(VolumeKey(volAndId, volume, position), _currPersDetector->volumes.GetEntries());
411  if (insertResult.second) {
412  // 3.1 No hash collision: this is a new volume. Add it also to the volumes array in detector object...
413  GGSTHitVolInfo *volInfo =
414  new ((_currPersDetector->volumes)[_currPersDetector->volumes.GetEntries()]) GGSTHitVolInfo;
415  volInfo->volumeName = volume->GetName().data();
416  for (int i = 0; i < 3; i++) {
417  volInfo->volumePos[i] = position[i] / cm;
418  }
419  volInfo->id = id;
420  // ... then return its position in the array
421  return _currPersDetector->volumes.GetEntries() - 1;
422  } else {
423  // 3.2 A hash collision has happened: the volume is already present. So take its position from the index
424  return insertResult.first->second;
425  }
426 }
Float_t volumePos[3]
Position of the touchable in world volume coordinates [cm].
Int_t id
ID of the volume.
const std::map< std::string, std::vector< bool > > & GetVectBoolParameters()
Getter method for vector-of-booleans geometry parameters.
void SetVectBoolGeoParams(const std::map< std::string, std::vector< bool >> &vectBoolGeoParams)
Sets the vector-of-booleans geometry parameters.
Class to store detector informations.
const std::map< std::string, int > & GetIntParameters()
Getter method for integer geometry parameters.
int StoreVolume(TFile *tFilePtr, const std::string &detector, const G4VPhysicalVolume *volume, const G4ThreeVector &position, int id)
Set persistence for the specified volume.
const std::map< std::string, double > & GetRealParameters()
Getter method for real geometry parameters.
Base class for GGS generator actions.
Abstract class needed to load GGS geometry.
TFile * GetFileForThisRun(const std::filesystem::path &baseName, const G4Run *run)
Opens a file for a given run and returns a pointer to it.
static GGSGeoPluginManager & GetInstance()
Get the singleton instance.
#define GGSENDL
Definition: GGSSmartLog.h:131
T GetParam(const std::string &name) const
Gets a parameter.
void SetStringGeoParams(const std::map< std::string, std::string > &stringGeoParams)
Sets the string geometry parameters.
const std::map< std::string, std::vector< std::string > > & GetVectStringParameters()
Getter method for vector-of-strings geometry parameters.
void CloseFileForThisRun(const std::filesystem::path &baseName, const G4Run *run)
Closes the ROOT output file.
void SetRealGeoParams(const std::map< std::string, double > &realGeoParams)
Sets the real geometry parameters.
const std::map< std::string, std::vector< int > > & GetVectIntParameters()
Getter method for vector-of-integers geometry parameters.
const std::map< std::string, std::string > & GetStringParameters()
Getter method for string geometry parameters.
TClonesArray volumes
Array of GGSTHitVolInfo objects.
Singleton for a centralized ROOT files management.
const std::map< std::string, std::vector< double > > & GetVectRealParameters()
Getter method for vector-of-reals geometry parameters.
void SetVectRealGeoParams(const std::map< std::string, std::vector< double >> &vectRealGeoParams)
Sets the vector-of-reals geometry parameters.
void SetVectStringGeoParams(const std::map< std::string, std::vector< std::string >> &vectStringGeoParams)
Sets the vector-of-strings geometry parameters.
const std::map< std::string, bool > & GetBoolParameters()
Getter method for boolean geometry parameters.
Class for storing the geometry parameters on Root output file.
Definition: GGSTGeoParams.h:18
TTree * GetDefaultTree(TFile *file)
Gets the default tree for this file.
static GGSRootFileService & GetInstance()
Get reference to GGSRootFileService unique instance.
void SetVectIntGeoParams(const std::map< std::string, std::vector< int >> &vectIntGeoParams)
Sets the vector-of-integers geometry parameters.
TString volumeName
Name of the physical volume.
GGSVGeometryConstruction * GetGeoConstruction()
Returns the geometry construction object.
void SetBoolGeoParams(const std::map< std::string, bool > &boolGeoParams)
Sets the boolean geometry parameters.
GGSTHitVolInfo.h GGSTHitVolInfo class declaration.
TString detectorName
Name of detector associated to integrated hits.
Class for writing parameters into the output Root file.
void SetIntGeoParams(const std::map< std::string, int > &intGeoParams)
Sets the integer geometry parameters.