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