Belle II Software  release-05-02-19
TOPDoublePulseGeneratorModule.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2017 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Marko Staric *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 
11 // Own include
12 #include <top/modules/TOPDoublePulseGenerator/TOPDoublePulseGeneratorModule.h>
13 #include <top/geometry/TOPGeometryPar.h>
14 
15 // framework - DataStore
16 #include <framework/datastore/StoreArray.h>
17 #include <framework/datastore/StoreObjPtr.h>
18 
19 // framework aux
20 #include <framework/logging/Logger.h>
21 
22 // datastore classes
23 #include <framework/dataobjects/EventMetaData.h>
24 #include <top/dataobjects/TOPDigit.h>
25 
26 // root
27 #include <TRandom.h>
28 #include <TFile.h>
29 #include <TH1F.h>
30 
31 
32 using namespace std;
33 
34 namespace Belle2 {
40  using namespace TOP;
41 
42  //-----------------------------------------------------------------
43  // Register module
44  //-----------------------------------------------------------------
45 
46  REG_MODULE(TOPDoublePulseGenerator)
47 
48  //-----------------------------------------------------------------
49  // Implementation
50  //-----------------------------------------------------------------
51 
53 
54  {
55  // set module description
56  setDescription("Generator of calibration double pulses");
57  setPropertyFlags(c_ParallelProcessingCertified);
58 
59  // Add parameters
60  addParam("moduleIDs", m_moduleIDs,
61  "list of slots for which to generate cal pulse, empty list means all slots.",
62  m_moduleIDs);
63  m_asicChannels.push_back(0);
64  addParam("asicChannels", m_asicChannels,
65  "ASIC calibration channels (0 - 7), empty list means all channels.",
66  m_asicChannels);
67  addParam("timeDifference", m_timeDifference,
68  "time difference between first and second pulse [ns].", 21.87);
69  addParam("timeResolution", m_timeResolution,
70  "sigma of time difference [ns].", 40.0e-3);
71  addParam("sampleTimeIntervals", m_sampleTimeIntervals,
72  "vector of 256 sample time intervals to construct sample times. "
73  "If empty, equidistant intervals will be used.", m_sampleTimeIntervals);
74  addParam("useDatabase", m_useDatabase,
75  "if true, use sample times from database instead of sampleTimeIntervals.",
76  false);
77  addParam("storageWindows", m_storageWindows,
78  "number of storage windows (old FW used 64 out of 512)", (unsigned) 512);
79  addParam("outputFileName", m_outputFileName,
80  "if given, sample times will be saved as root histograms in this file",
81  std::string(""));
82 
83  }
84 
85  TOPDoublePulseGeneratorModule::~TOPDoublePulseGeneratorModule()
86  {
87  if (m_timebase) delete m_timebase;
88  }
89 
90  void TOPDoublePulseGeneratorModule::initialize()
91  {
92  // Output
93 
94  StoreArray<TOPDigit> digits;
95  digits.registerInDataStore();
96 
97  // prepare vectors to loop on
98 
99  const auto* geo = TOPGeometryPar::Instance()->getGeometry();
100 
101  if (m_moduleIDs.empty()) {
102  for (const auto& module : geo->getModules()) {
103  m_moduleIDs.push_back(module.getModuleID());
104  }
105  } else {
106  for (auto moduleID : m_moduleIDs) {
107  if (!geo->isModuleIDValid(moduleID))
108  B2ERROR("Invalid module ID found in input list: " << moduleID);
109  }
110  }
111 
112  if (m_asicChannels.empty()) {
113  for (unsigned ch = 0; ch < 8; ch++) m_asicChannels.push_back(ch);
114  } else {
115  for (unsigned ch : m_asicChannels) {
116  if (ch > 7)
117  B2ERROR("Invalid ASIC channel found in input list: " << ch);
118  }
119  }
120 
121  // set sample times
122 
123  double syncTimeBase = geo->getNominalTDC().getSyncTimeBase();
124  m_sampleTimes.setTimeAxis(syncTimeBase); // equidistant
125  m_sampleDivisions = (0x1 << geo->getNominalTDC().getSubBits());
126 
127  if (m_useDatabase) {
128  m_timebase = new DBObjPtr<TOPCalTimebase>;
129  } else if (m_sampleTimeIntervals.empty()) {
130  B2INFO("TOPDoublePulseGenerator: using equidistant sample times");
131  } else if (m_sampleTimeIntervals.size() == 256) {
132  std::vector<double> timeAxis;
133  timeAxis.push_back(0);
134  for (auto dt : m_sampleTimeIntervals) timeAxis.push_back(dt + timeAxis.back());
135  double rescale = 2 * syncTimeBase / timeAxis.back();
136  for (auto& t : timeAxis) t *= rescale;
137  m_sampleTimes.setTimeAxis(timeAxis, syncTimeBase); // given by steering
138  B2INFO("TOPDoublePulseGenerator: using sample times from steering");
139  } else {
140  B2ERROR("sampleTimeIntervals: size must be 256 or empty");
141  }
142 
143  if (!m_outputFileName.empty()) storeSampleTimes(m_outputFileName);
144 
145  }
146 
147 
148  void TOPDoublePulseGeneratorModule::beginRun()
149  {
150  StoreObjPtr<EventMetaData> evtMetaData;
151  if (m_useDatabase) {
152  if (!(*m_timebase).isValid()) {
153  B2FATAL("Sample time calibration requested but not available for run "
154  << evtMetaData->getRun()
155  << " of experiment " << evtMetaData->getExperiment());
156  }
157  }
158 
159  }
160 
161 
162  void TOPDoublePulseGeneratorModule::event()
163  {
164 
165  StoreArray<TOPDigit> digits;
166 
167  const auto& chMapper = TOPGeometryPar::Instance()->getChannelMapper();
168  const auto& feMapper = TOPGeometryPar::Instance()->getFrontEndMapper();
169 
170  double timeError = m_timeResolution / sqrt(2.0); // for single pulse
171 
172  for (auto moduleID : m_moduleIDs) {
173  for (unsigned asic = 0; asic < 64; asic++) {
174  for (auto asicChannel : m_asicChannels) {
175  unsigned channel = asic * 8 + asicChannel;
176  auto pixelID = chMapper.getPixelID(channel);
177 
178  // sample times: from steering or database
179  const TOPSampleTimes* sampleTimes = &m_sampleTimes;
180  if (m_useDatabase) {
181  unsigned scrodID = 0;
182  int bs = asic / 16;
183  const auto* feMap = feMapper.getMap(moduleID, bs);
184  if (feMap) scrodID = feMap->getScrodID();
185  sampleTimes = (*m_timebase)->getSampleTimes(scrodID, channel % 128);
186  if (!sampleTimes->isCalibrated()) {
187  B2WARNING("No sample time calibration available for SCROD " << scrodID
188  << " channel " << channel % 128 << " - equidistant will be used");
189  }
190  }
191 
192  // first calpulse
193  double time = gRandom->Rndm() * sampleTimes->getTimeRange();
194  unsigned window = gRandom->Rndm() * m_storageWindows;
195  double sample = sampleTimes->getSample(window, time);
196  auto* digit = digits.appendNew(moduleID, pixelID, sample);
197  digit->setFirstWindow(window);
198  digit->setTime(time);
199  digit->setTimeError(timeError);
200  digit->setChannel(channel);
201  digit->setHitQuality(TOPDigit::c_CalPulse);
202 
203  // second calpulse
204  time += gRandom->Gaus(m_timeDifference, m_timeResolution);
205  sample = sampleTimes->getSample(window, time);
206  digit = digits.appendNew(moduleID, pixelID, sample);
207  digit->setFirstWindow(window);
208  digit->setTime(time);
209  digit->setTimeError(timeError);
210  digit->setChannel(channel);
211  digit->setHitQuality(TOPDigit::c_CalPulse);
212  }
213  }
214  }
215 
216  }
217 
218 
219  void TOPDoublePulseGeneratorModule::endRun()
220  {
221  }
222 
223  void TOPDoublePulseGeneratorModule::terminate()
224  {
225  }
226 
227 
228  void TOPDoublePulseGeneratorModule::storeSampleTimes(std::string fileName)
229  {
230  if (fileName.empty()) return;
231 
232  TFile* fout = TFile::Open(fileName.c_str(), "recreate");
233  if (!fout) {
234  B2ERROR("Can't open the output file " << fileName);
235  return;
236  }
237 
238  const auto& feMapper = TOPGeometryPar::Instance()->getFrontEndMapper();
239 
240  TH1F scrods("scrodID", "scrod ID's mapped to slots/boardstacks", 64, -0.5, 63.5);
241  scrods.SetXTitle("(slot - 1) * 4 + boardstack");
242  scrods.SetYTitle("scrod ID");
243 
244  for (auto moduleID : m_moduleIDs) {
245  for (int bs = 0; bs < 4; bs++) {
246  const auto* feMap = feMapper.getMap(moduleID, bs);
247  if (!feMap) {
248  B2ERROR("No front-end mapping available for slot " << moduleID
249  << " boardstack " << bs);
250  continue;
251  }
252  unsigned scrodID = feMap->getScrodID();
253  std::string subdir = "scrod" + std::to_string(scrodID);
254  int i = (moduleID - 1) * 4 + bs;
255  scrods.SetBinContent(i + 1, scrodID);
256  fout->mkdir(subdir.c_str());
257  }
258  }
259  scrods.Write();
260 
261  for (auto moduleID : m_moduleIDs) {
262  for (unsigned asic = 0; asic < 64; asic++) {
263  int bs = asic / 16;
264  const auto* feMap = feMapper.getMap(moduleID, bs);
265  if (!feMap) continue;
266  unsigned scrodID = feMap->getScrodID();
267  std::string subdir = "scrod" + std::to_string(scrodID);
268  fout->cd(subdir.c_str());
269  for (auto asicChannel : m_asicChannels) {
270  unsigned channel = (asic * 8 + asicChannel) % 128;
271  const TOPSampleTimes* sampleTimes = &m_sampleTimes;
272  if (m_useDatabase) {
273  sampleTimes = (*m_timebase)->getSampleTimes(scrodID, channel);
274  }
275  string forWhat = "scrod " + to_string(scrodID) +
276  " channel " + to_string(channel) +
277  " (slot" + to_string(moduleID) + ", as" + to_string(asic) +
278  ", ch" + to_string(asicChannel) + ")";
279  auto timeAxis = sampleTimes->getTimeAxis();
280  saveAsHistogram(timeAxis, "sampleTimes_ch" + to_string(channel),
281  "Generator input: sample times for " + forWhat,
282  "sample number", "t [ns]");
283  std::vector<double> dt;
284  for (unsigned i = 1; i < timeAxis.size(); i++) {
285  dt.push_back(timeAxis[i] - timeAxis[i - 1]);
286  }
287  saveAsHistogram(dt, "dt_ch" + to_string(channel),
288  "Generator input: sample time bins for " + forWhat,
289  "sample number", "#Delta t [ns]");
290  }
291  }
292  }
293 
294  fout->Close();
295 
296  }
297 
298 
299  void TOPDoublePulseGeneratorModule::saveAsHistogram(const std::vector<double>& vec,
300  const std::string& name,
301  const std::string& title,
302  const std::string& xTitle,
303  const std::string& yTitle) const
304  {
305  if (vec.empty()) return;
306 
307  TH1F h(name.c_str(), title.c_str(), vec.size(), 0, vec.size());
308  h.SetXTitle(xTitle.c_str());
309  h.SetYTitle(yTitle.c_str());
310  if (name.find("Fit") != string::npos) h.SetLineColor(2);
311 
312  for (unsigned i = 0; i < vec.size(); i++) h.SetBinContent(i + 1, vec[i]);
313 
314  h.Write();
315  }
316 
318 } // end Belle2 namespace
319 
REG_MODULE
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:652
Belle2::TOPSampleTimes::getTimeAxis
std::vector< double > getTimeAxis() const
Returns time axis (sample times)
Definition: TOPSampleTimes.cc:50
Belle2::DBObjPtr
Class for accessing objects in the database.
Definition: DBObjPtr.h:31
Belle2::TOPSampleTimes::getTimeRange
double getTimeRange() const
Returns time axis range (time interval corresponding to 4 asic windows)
Definition: TOPSampleTimes.h:118
Belle2::Module
Base class for Modules.
Definition: Module.h:74
Belle2::TOPSampleTimes::getSample
double getSample(int window, double time) const
Returns sample with respect to sample 0 of the specified ASIC window (inverse of getTime).
Definition: TOPSampleTimes.cc:82
Belle2::TOPSampleTimes
Calibration constants of a singe ASIC channel: time axis (sample times)
Definition: TOPSampleTimes.h:34
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::StoreObjPtr
Type-safe access to single objects in the data store.
Definition: ParticleList.h:33
Belle2::asicChannel
record to be used to store ASIC info
Definition: CDCCrossTalkClasses.h:23
Belle2::TOPSampleTimes::isCalibrated
bool isCalibrated() const
Returns calibration status.
Definition: TOPSampleTimes.h:197
Belle2::StoreArray
Accessor to arrays stored in the data store.
Definition: ECLMatchingPerformanceExpertModule.h:33
Belle2::TOPSampleTimes::getScrodID
unsigned getScrodID() const
Returns scrod ID.
Definition: TOPSampleTimes.h:106
Belle2::TOPDoublePulseGeneratorModule
Generator of double calibration pulses Output to TOPDigits.
Definition: TOPDoublePulseGeneratorModule.h:40