Belle II Software  release-05-02-19
FullSimTimingModule.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2015 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Martin Ritter *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 
11 #include <simulation/modules/fullsim/FullSimTimingModule.h>
12 #include <simulation/kernel/RunManager.h>
13 #include <framework/gearbox/Unit.h>
14 #include <G4EventManager.hh>
15 #include <G4VSteppingVerbose.hh>
16 #include <G4RegionStore.hh>
17 #include <framework/logging/Logger.h>
18 
19 #include <TProfile.h>
20 #include <TFile.h>
21 
22 using namespace Belle2;
23 
24 //-----------------------------------------------------------------
25 // Register the Module
26 //-----------------------------------------------------------------
27 REG_MODULE(FullSimTiming)
28 
29 //-----------------------------------------------------------------
30 // Implementation
31 //-----------------------------------------------------------------
32 
34 {
35  // Set module properties
36  setDescription("Provide more detailed timing info for FullSim module");
37 
38  //So, we have to get rid of the old instance first if we want to get our
39  //timing instance in there, otherwise we get an G4Exception
40  G4VSteppingVerbose* old = G4VSteppingVerbose::GetInstance();
41  if (old) {
42  delete old;
43  G4VSteppingVerbose::SetInstance(nullptr);
44  }
45  //Now we create our timing class and set it as the class to be used. Set the callback to our processStep member
46  m_timing = new Simulation::StepTiming([&](G4Track * track, G4LogicalVolume * volume, double time) {
47  processStep(track, volume, time);
48  });
49  G4VSteppingVerbose::SetInstance(m_timing);
50 
51  // Parameter definitions
52  addParam("rootFile", m_rootFileName, "Name of the output root file. If none "
53  " is given, no file will be created", std::string(""));
54 }
55 
56 
58 {
59  m_isInitialized = false;
60 
61  //There are two (useful) scenarios: either this module is in front or after
62  //the FullSim module. In both cases we want it to work correctly. As the
63  //FullSim module overrides the verbosity of the SteppingManager we have to be
64  //careful when to initialize, it must be after the FullSim::initialize(). So
65  //either here if we are after the FullSim module or in the first event() call
66  //if we are before
67 
69  //So, we check if the Simulation has been intialized already. If so we can
70  //initialize the StepTiming now, otherwise we do it in the first call to the
71  //event function. For this we just check if the PrimaryGeneratorAction has
72  //been set
73  if (runManager.GetUserPrimaryGeneratorAction()) {
74  //initialization is done by just calling the event function once
75  event();
76  }
77 }
78 
80 {
81  if (!m_isInitialized) {
82  //Override verbosity of the Stepping manager to make sure that the
83  //TrackingVerbose instance is called
84  G4SteppingManager* stepping = G4EventManager::GetEventManager()->GetTrackingManager()->GetSteppingManager();
85  //Set the timing class
86  stepping->SetVerbose(m_timing);
87  //Make sure the SteppingManager is set to verbose otherwise the timing class will not be called
88  stepping->SetVerboseLevel(1);
89  //Make sure the timing class knows about the stepping manager
90  m_timing->SetManager(stepping);
91  //And set it to silent so the TrackingManager does not print the TrackBanner
92  m_timing->SetSilent(1);
93  m_timing->SetSilentStepInfo(1);
94  //Ok, we want to plot the regions in the order they were created so we need
95  //to map from region to index in the store
96  int index {0};
97  for (G4Region* region : * (G4RegionStore::GetInstance())) {
98  //We don't care about regions wihtout volumes
99  if (region->GetNumberOfRootVolumes() == 0) continue;
100  //Increase the index otherwise
101  m_regionIndices[region] = index++;
102  }
103  //create profile to store timings
104  m_timingProfile = new TProfile(
105  "SimulationTiming", "Simulation Timing;region;stepping time per event / ms",
106  index, 0, index, "s"
107  );
108  //Set counter variables
109  m_totalTime = 0;
110  m_eventCount = 0;
111  m_eventTime = 0;
112  //And we only do this once
113  m_isInitialized = true;
114  }
115  //If the eventTime is >0 we recorded at least one step, so let's update the
116  //profile and reset the counters
117  if (m_eventTime > 0) {
118  ++m_eventCount;
119  m_eventTime = 0;
120  for (auto& it : m_regionCache) {
121  int index = m_regionIndices[it.first];
122  m_timingProfile->Fill(index, it.second);
123  it.second = 0;
124  }
125  }
126 }
127 
128 void FullSimTimingModule::processStep(G4Track*, G4LogicalVolume* volume, double time)
129 {
130  const G4Region* const region = volume->GetRegion();
131  //Check if we are still in the same region to speed up the lookup
132  if (m_lastRegion == end(m_regionCache) || m_lastRegion->first != region) {
133  //Apparently not, so find or create the region in the Cache
134  m_lastRegion = m_regionCache.insert(std::make_pair(region, 0.0)).first;
135  }
136  //Record times
137  m_lastRegion->second += time;
138  m_totalTime += time;
139  m_eventTime += time;
140 }
141 
143 {
144  //Call event one last time to get the last timing in case we were before the FullSim module
145  event();
146  //Ok, now finish the results
147  B2RESULT("FullSimTiming: Total simulation stepping time: " << (m_totalTime / Unit::s) << " s");
148  B2RESULT("FullSimTiming: Number of simulated events: " << m_eventCount);
149  //Remove stat box
150  m_timingProfile->SetStats(0);
151  //Convert to ms
152  m_timingProfile->Scale(1. / Unit::ms);
153  //Set the labels of the profile to the region names and check the maximum length
154  size_t regionWidth {6};
155  for (auto& it : m_regionIndices) {
156  m_timingProfile->GetXaxis()->SetBinLabel(it.second + 1, it.first->GetName().c_str());
157  regionWidth = std::max(regionWidth, it.first->GetName().size());
158  }
159  //Do we want a root file?
160  if (!m_rootFileName.empty()) {
161  TFile* file = new TFile(m_rootFileName.c_str(), "RECREATE");
162  file->cd();
163  m_timingProfile->Write();
164  file->Close();
165  }
166  if (m_totalTime > 0) {
167  //Now print the result as a table. But first we need the total average
168  double totalAvg {0};
169  size_t maxContentWidth {4};
170  size_t maxErrorWidth {4};
171  for (int i = 0; i < m_timingProfile->GetNbinsX(); ++i) {
172  //We want to calculate the total avg per event and the width required to
173  //show all times and their errors. So we need times and errors in ms
174  const double content = m_timingProfile->GetBinContent(i + 1);
175  const double error = m_timingProfile->GetBinError(i + 1);
176  //add up the averages.
177  totalAvg += content;
178  //and calculate the number of digits
179  const size_t contentWidth = std::floor(std::log10(content > 0 ? content : 1) + 1);
180  const size_t errorWidth = std::floor(std::log10(error > 0 ? error : 1) + 1);
181  //and get the maximum, including dot and 2 digits
182  maxContentWidth = std::max(maxContentWidth, contentWidth + 3);
183  maxErrorWidth = std::max(maxErrorWidth, errorWidth + 3);
184  }
185  //We want percentages ...
186  totalAvg /= 100;
187  //And finally we print the table
188  B2RESULT("FullSimTiming: Simulation stepping time per event and region");
189  for (int i = 0; i < m_timingProfile->GetNbinsX(); ++i) {
190  B2RESULT(" "
191  << std::setw(regionWidth + 1) << std::left << m_timingProfile->GetXaxis()->GetBinLabel(i + 1) << " "
192  << std::right << std::fixed << std::setprecision(2)
193  << std::setw(maxContentWidth) << m_timingProfile->GetBinContent(i + 1) << " ms +- "
194  << std::setw(maxErrorWidth) << m_timingProfile->GetBinError(i + 1) << " ms ("
195  << std::setw(6) << (m_timingProfile->GetBinContent(i + 1) / totalAvg) << " %)"
196  );
197  }
198  }
199 }
Belle2::FullSimTimingModule::m_eventCount
unsigned int m_eventCount
number of events with m_eventTime>0
Definition: FullSimTimingModule.h:85
Belle2::Unit::s
static const double s
[second]
Definition: Unit.h:105
Belle2::Unit::ms
static const double ms
[millisecond]
Definition: Unit.h:106
Belle2::FullSimTimingModule::m_timing
Simulation::StepTiming * m_timing
instance of the StepTiming class which records the step times in geant4
Definition: FullSimTimingModule.h:71
REG_MODULE
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:652
Belle2::FullSimTimingModule::initialize
virtual void initialize() override
Setup the timing structure.
Definition: FullSimTimingModule.cc:57
Belle2::FullSimTimingModule
Provide more detailled timing info for FullSim module.
Definition: FullSimTimingModule.h:39
Belle2::FullSimTimingModule::m_rootFileName
std::string m_rootFileName
filename of the root file to store the timing profile
Definition: FullSimTimingModule.h:69
Belle2::FullSimTimingModule::m_lastRegion
std::map< const G4Region *, double >::iterator m_lastRegion
iterator pointing to the region of the last step to speed up lookup
Definition: FullSimTimingModule.h:75
Belle2::Simulation::RunManager
The run manager controls the flow of the Geant4 program and manages the event loop(s) within a run.
Definition: RunManager.h:40
Belle2::FullSimTimingModule::processStep
void processStep(G4Track *track, G4LogicalVolume *volume, double time)
store the time it took a Geant4 track for one step inside a volume
Definition: FullSimTimingModule.cc:128
Belle2::Module
Base class for Modules.
Definition: Module.h:74
Belle2::FullSimTimingModule::m_regionCache
std::map< const G4Region *, double > m_regionCache
map containing the amount of time spent in each region
Definition: FullSimTimingModule.h:73
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::Simulation::RunManager::Instance
static RunManager & Instance()
Static method to get a reference to the RunManager instance.
Definition: RunManager.cc:31
Belle2::Simulation::StepTiming
Class to perform more detailed timing studies of the Geant4 simulation.
Definition: StepTiming.h:36
Belle2::FullSimTimingModule::terminate
virtual void terminate() override
Save the output.
Definition: FullSimTimingModule.cc:142
Belle2::FullSimTimingModule::m_eventTime
double m_eventTime
stepping time of the current event
Definition: FullSimTimingModule.h:83
Belle2::FullSimTimingModule::m_totalTime
double m_totalTime
total time spent stepping through the simulation geometry
Definition: FullSimTimingModule.h:81
Belle2::FullSimTimingModule::m_regionIndices
std::map< const G4Region *, int > m_regionIndices
map between region and and bin indices in the TProfile
Definition: FullSimTimingModule.h:77
Belle2::FullSimTimingModule::event
virtual void event() override
Save the simulation time for each region in a profile.
Definition: FullSimTimingModule.cc:79
Belle2::FullSimTimingModule::m_timingProfile
TProfile * m_timingProfile
profile of the time spent per event in each region
Definition: FullSimTimingModule.h:79
Belle2::FullSimTimingModule::m_isInitialized
bool m_isInitialized
remember whether we aleady initialized everything
Definition: FullSimTimingModule.h:87