Belle II Software  release-06-02-00
TOPDigitizerModule.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 // Own include
9 #include <top/modules/TOPDigitizer/TOPDigitizerModule.h>
10 #include <top/geometry/TOPGeometryPar.h>
11 #include <top/modules/TOPDigitizer/TimeDigitizer.h>
12 
13 // framework - DataStore
14 #include <framework/datastore/DataStore.h>
15 #include <framework/datastore/StoreArray.h>
16 #include <framework/datastore/StoreObjPtr.h>
17 
18 // framework aux
19 #include <framework/logging/Logger.h>
20 
21 // ROOT
22 #include <TRandom.h>
23 
24 #include <map>
25 
26 using namespace std;
27 
28 namespace Belle2 {
34  using namespace TOP;
35 
36  //-----------------------------------------------------------------
37  // Register the Module
38  //-----------------------------------------------------------------
39 
40  REG_MODULE(TOPDigitizer)
41 
42 
43  //-----------------------------------------------------------------
44  // Implementation
45  //-----------------------------------------------------------------
46 
48  {
49  // Set description()
50  setDescription("Digitize TOPSimHits");
51  setPropertyFlags(c_ParallelProcessingCertified);
52 
53  // Add parameters
54  addParam("timeZeroJitter", m_timeZeroJitter,
55  "r.m.s of T0 jitter [ns]", 15e-3);
56  addParam("electronicJitter", m_electronicJitter,
57  "r.m.s of electronic jitter [ns], "
58  "if negative the one from TOPNominalTDC is used. "
59  "This parameter is ignored in the full waveform digitization.", -1.0);
60  addParam("darkNoise", m_darkNoise,
61  "uniformly distributed dark noise (hits per module)", 0.0);
62  addParam("ADCx0", m_ADCx0,
63  "pulse height distribution parameter [ADC counts]", 204.1);
64  addParam("ADCp1", m_ADCp1,
65  "pulse height distribution parameter (must be non-negative)", 1.0);
66  addParam("ADCp2", m_ADCp2,
67  "pulse height distribution parameter (must be non-negative)", 1.025);
68  addParam("ADCmax", m_ADCmax,
69  "pulse height distribution upper bound [ADC counts]", 2000.0);
70  addParam("rmsNoise", m_rmsNoise,
71  "r.m.s of noise [ADC counts]", 9.7);
72  addParam("threshold", m_threshold,
73  "pulse height threshold [ADC counts]", 40);
74  addParam("hysteresis", m_hysteresis,
75  "pulse height threshold hysteresis [ADC counts]", 10);
76  addParam("thresholdCount", m_thresholdCount,
77  "minimal number of samples above threshold", 3);
78  addParam("useWaveforms", m_useWaveforms,
79  "if true, use full waveform digitization", true);
80  addParam("allChannels", m_allChannels,
81  "if true, make waveforms for all 8192 channels "
82  "(note: this will slow-down digitization)", false);
83  addParam("useDatabase", m_useDatabase,
84  "if true, use channel dependent constants from database (incl. time base)",
85  true);
86  addParam("useSampleTimeCalibration", m_useSampleTimeCalibration,
87  "if true, use only time base calibration from database "
88  "(has no effect if useDatabase = True)", false);
89  addParam("simulateTTS", m_simulateTTS,
90  "if true, simulate time transition spread. "
91  "Should be always switched ON, except for some dedicated timing studies.",
92  true);
93  addParam("minWidthXheight", m_minWidthXheight,
94  "minimal product of width and height [ns * ADC counts]", 100.0);
95  addParam("lookBackWindows", m_lookBackWindows,
96  "number of look back windows, if positive override the number from database", 0);
97  }
98 
99 
100  void TOPDigitizerModule::initialize()
101  {
102  // input from datastore
103  m_simHits.isRequired();
104  m_simCalPulses.isOptional();
105  m_mcParticles.isOptional();
106  m_simClockState.isOptional();
107 
108  // output to datastore
109  m_rawDigits.registerInDataStore();
110  m_digits.registerInDataStore();
111  m_digits.registerRelationTo(m_simHits);
112  m_digits.registerRelationTo(m_mcParticles);
113  m_digits.registerRelationTo(m_rawDigits);
114  m_waveforms.registerInDataStore(DataStore::c_DontWriteOut);
115  m_rawDigits.registerRelationTo(m_waveforms, DataStore::c_Event,
116  DataStore::c_DontWriteOut);
117 
118  // geometry and nominal data
119  const auto* geo = TOPGeometryPar::Instance()->getGeometry();
120 
121  if (m_electronicJitter < 0) {
122  m_electronicJitter = geo->getNominalTDC().getTimeJitter();
123  }
124 
125  // set pile-up and double hit resolution times (needed for BG overlay)
126  TOPDigit::setDoubleHitResolution(geo->getNominalTDC().getDoubleHitResolution());
127  TOPDigit::setPileupTime(geo->getNominalTDC().getPileupTime());
128 
129  // default sample times (equidistant)
130  m_syncTimeBase = geo->getNominalTDC().getSyncTimeBase();
131  m_sampleTimes.setTimeAxis(m_syncTimeBase); // equidistant time base
132 
133  // default pulse height generator
134  m_pulseHeightGenerator = PulseHeightGenerator(m_ADCx0, m_ADCp1, m_ADCp2, m_ADCmax);
135 
136  }
137 
138 
139  void TOPDigitizerModule::beginRun()
140  {
141  StoreObjPtr<EventMetaData> evtMetaData;
142 
143  // check availability of mappers
144  const auto& channelMapper = TOPGeometryPar::Instance()->getChannelMapper();
145  if (not channelMapper.isValid()) {
146  B2FATAL("No valid channel mapper found for run "
147  << evtMetaData->getRun()
148  << " of experiment " << evtMetaData->getExperiment());
149  }
150  const auto& frontEndMapper = TOPGeometryPar::Instance()->getFrontEndMapper();
151  if (not frontEndMapper.isValid()) {
152  B2FATAL("No valid front-end mapper found for run "
153  << evtMetaData->getRun()
154  << " of experiment " << evtMetaData->getExperiment());
155  }
156 
157  // check availability of constants in database
158  if (m_useDatabase) {
159  if (not m_timebases.isValid()) {
160  B2FATAL("Sample time calibration constants requested but not available for run "
161  << evtMetaData->getRun()
162  << " of experiment " << evtMetaData->getExperiment());
163  }
164  if (not m_channelT0.isValid()) {
165  B2FATAL("Channel T0 calibration constants requested but not available for run "
166  << evtMetaData->getRun()
167  << " of experiment " << evtMetaData->getExperiment());
168  }
169  if (not m_asicShift.isValid()) {
170  B2FATAL("ASIC shifts calibration requested but not available for run "
171  << evtMetaData->getRun()
172  << " of experiment " << evtMetaData->getExperiment());
173  }
174  if (not m_moduleT0.isValid()) {
175  B2FATAL("Module T0 calibration constants requested but not available for run "
176  << evtMetaData->getRun()
177  << " of experiment " << evtMetaData->getExperiment());
178  }
179  if (not m_commonT0.isValid()) {
180  B2FATAL("Common T0 calibration constants requested but not available for run "
181  << evtMetaData->getRun()
182  << " of experiment " << evtMetaData->getExperiment());
183  }
184  if (not m_pulseHeights.isValid()) {
185  B2FATAL("Pulse height calibration constants requested but not available for run "
186  << evtMetaData->getRun()
187  << " of experiment " << evtMetaData->getExperiment());
188  }
189  if (not m_thresholds.isValid()) {
190  B2FATAL("Channel thresholds requested but not available for run "
191  << evtMetaData->getRun()
192  << " of experiment " << evtMetaData->getExperiment());
193  }
194  if (not m_noises.isValid()) {
195  B2FATAL("Channel noise levels requested but not available for run "
196  << evtMetaData->getRun()
197  << " of experiment " << evtMetaData->getExperiment());
198  }
199  if (m_timeWalk.isValid()) {
200  TimeDigitizer::setTimeWalk(&m_timeWalk);
201  } else {
202  TimeDigitizer::setTimeWalk(0);
203  // B2FATAL("Time-walk parameters requested but not available for run "
204  B2WARNING("Time-walk parameters not available for run "
205  << evtMetaData->getRun()
206  << " of experiment " << evtMetaData->getExperiment());
207  }
208  } else if (m_useSampleTimeCalibration) {
209  if (not m_timebases.isValid()) {
210  B2FATAL("Sample time calibration constants requested but not available for run "
211  << evtMetaData->getRun()
212  << " of experiment " << evtMetaData->getExperiment());
213  }
214  }
215 
216  // check availability of front-end settings
217  if (not m_feSetting.isValid()) {
218  B2FATAL("Front-end settings are not available for run "
219  << evtMetaData->getRun()
220  << " of experiment " << evtMetaData->getExperiment());
221  }
222 
223  // pass a parameter to TimeDigitizer
224  TimeDigitizer::setReadoutWindows(m_feSetting->getReadoutWindows());
225  if ((evtMetaData->getExperiment() > 0 and evtMetaData->getExperiment() < 5) or
226  evtMetaData->getExperiment() == 1002) {
227  TimeDigitizer::maskSamples(true); // phase-2: mask samples at window boundaries
228  } else {
229  TimeDigitizer::maskSamples(false); // phase-3: no masking
230  }
231 
232  }
233 
234  void TOPDigitizerModule::event()
235  {
236 
237  // get or generate revo9 count
238  unsigned revo9cnt = 0;
239  if (m_simClockState.isValid()) {
240  revo9cnt = m_simClockState->getRevo9Count();
241  } else {
242  revo9cnt = gRandom->Integer(11520);
243  }
244 
245  // from revo9 count determine trigger time offset and the number of offset windows
246  double SSTfrac = (revo9cnt % 6) / 6.0;
247  double offset = m_feSetting->getOffset() / 24.0;
248  double trgTimeOffset = (SSTfrac + offset) * m_syncTimeBase; // in [ns]
249  int offsetWindows = m_feSetting->getWindowShift(revo9cnt);
250  TimeDigitizer::setOffsetWindows(offsetWindows);
251 
252  // from revo9 and write depths determine reference window, phase and storage depth
253  int SSTcnt = revo9cnt / 6;
254  int refWindow = SSTcnt * 2; // same as lastWriteAddr
255  const auto& writeDepths = m_feSetting->getWriteDepths();
256  if (writeDepths.empty()) {
257  B2ERROR("TOPDigitzer: vector of write depths is empty. No digitization possible");
258  return;
259  }
260  int lastDepth = writeDepths.back();
261  unsigned phase = 0;
262  for (auto depth : writeDepths) {
263  SSTcnt -= depth;
264  if (SSTcnt < 0) break;
265  phase++;
266  refWindow = SSTcnt * 2;
267  lastDepth = depth;
268  }
269  unsigned storageDepth = lastDepth * 2;
270  TimeDigitizer::setStorageDepth(storageDepth);
271 
272  // from reference window and lookback determine first of the readout windows
273  int lookBackWindows = m_feSetting->getLookbackWindows();
274  if (m_lookBackWindows > 0) lookBackWindows = m_lookBackWindows;
275  lookBackWindows -= m_feSetting->getExtraWindows();
276  int window = refWindow - lookBackWindows;
277  if (window < 0) window += storageDepth;
278  TimeDigitizer::setFirstWindow(window);
279 
280  // geometry and nominal data
281  const auto* geo = TOPGeometryPar::Instance()->getGeometry();
282 
283  // time range for digitization
284  double timeMin = geo->getSignalShape().getTMin() + offsetWindows * m_syncTimeBase / 2;
285  double timeMax = geo->getSignalShape().getTMax() +
286  (m_feSetting->getReadoutWindows() + offsetWindows) * m_syncTimeBase / 2;
287 
288  // simulate start time jitter
289  double startTimeJitter = gRandom->Gaus(0, m_timeZeroJitter);
290 
291  // get electronic efficiency
292  double electronicEfficiency = geo->getNominalTDC().getEfficiency();
293 
294  // define pixels with time digitizers
295  std::map<unsigned, TimeDigitizer> pixels;
296  typedef std::map<unsigned, TimeDigitizer>::iterator Iterator;
297 
298  // add simulated hits to time digitizers
299 
300  for (const auto& simHit : m_simHits) {
301  if (not m_useWaveforms) {
302  // simulate electronic efficiency
303  if (gRandom->Rndm() > electronicEfficiency) continue;
304  }
305  // do spatial digitization
306  double x = simHit.getX();
307  double y = simHit.getY();
308  int pmtID = simHit.getPmtID();
309  int moduleID = simHit.getModuleID();
310  if (not geo->isModuleIDValid(moduleID)) continue;
311  int pixelID = geo->getModule(moduleID).getPMTArray().getPixelID(x, y, pmtID);
312  if (pixelID == 0) continue;
313 
314  // add start time jitter and generated TTS to photon time
315  double time = simHit.getTime() + startTimeJitter;
316  if (m_simulateTTS) {
317  const auto& tts = TOPGeometryPar::Instance()->getTTS(moduleID, pmtID);
318  time += tts.generateTTS();
319  }
320 
321  // get time offset for a given pixel
322  auto timeOffset = getTimeOffset(trgTimeOffset, moduleID, pixelID);
323 
324  // time range cut (to speed up digitization)
325  if (time + timeOffset.value < timeMin + timeOffset.timeShift) continue;
326  if (time + timeOffset.value > timeMax + timeOffset.timeShift) continue;
327 
328  // generate pulse height
329  double pulseHeight = generatePulseHeight(moduleID, pixelID);
330  auto hitType = TimeDigitizer::c_Hit;
331 
332  // add time and pulse height to digitizer of a given pixel
333  TimeDigitizer digitizer(moduleID, pixelID, timeOffset.value, timeOffset.error,
334  timeOffset.windowShift, m_rmsNoise, m_sampleTimes);
335  if (not digitizer.isValid()) continue;
336  unsigned id = digitizer.getUniqueID();
337  Iterator it = pixels.insert(pair<unsigned, TimeDigitizer>(id, digitizer)).first;
338  it->second.addTimeOfHit(time, pulseHeight, hitType, &simHit);
339  }
340 
341  // add calibration pulses
342 
343  for (const auto& simCalPulses : m_simCalPulses) {
344  auto moduleID = simCalPulses.getModuleID();
345  auto pixelID = simCalPulses.getPixelID();
346  auto pulseHeight = simCalPulses.getAmplitude();
347  auto time = simCalPulses.getTime();
348  auto hitType = TimeDigitizer::c_CalPulse;
349 
350  // get time offset for a given pixel
351  auto timeOffset = getTimeOffset(trgTimeOffset, moduleID, pixelID);
352 
353  // time range cut (to speed up digitization)
354  if (time + timeOffset.value < timeMin + timeOffset.timeShift) continue;
355  if (time + timeOffset.value > timeMax + timeOffset.timeShift) continue;
356 
357  // add time and pulse height to digitizer of a given pixel
358  TimeDigitizer digitizer(moduleID, pixelID, timeOffset.value, timeOffset.error,
359  timeOffset.windowShift, m_rmsNoise, m_sampleTimes);
360  if (not digitizer.isValid()) continue;
361  unsigned id = digitizer.getUniqueID();
362  Iterator it = pixels.insert(pair<unsigned, TimeDigitizer>(id, digitizer)).first;
363  it->second.addTimeOfHit(time, pulseHeight, hitType);
364  }
365 
366  // add randomly distributed dark noise (maybe not needed anymore?)
367 
368  if (m_darkNoise > 0) {
369  int numModules = geo->getNumModules();
370  for (int moduleID = 1; moduleID <= numModules; moduleID++) {
371  int numPixels = geo->getModule(moduleID).getPMTArray().getNumPixels();
372  int numHits = gRandom->Poisson(m_darkNoise);
373  for (int i = 0; i < numHits; i++) {
374  int pixelID = int(gRandom->Rndm() * numPixels) + 1;
375  double time = (timeMax - timeMin) * gRandom->Rndm() + timeMin;
376  double pulseHeight = generatePulseHeight(moduleID, pixelID);
377  auto hitType = TimeDigitizer::c_Hit;
378  auto timeOffset = getTimeOffset(trgTimeOffset, moduleID, pixelID);
379  time += timeOffset.timeShift;
380  time -= timeOffset.value;
381  TimeDigitizer digitizer(moduleID, pixelID, timeOffset.value, timeOffset.error,
382  timeOffset.windowShift, m_rmsNoise, m_sampleTimes);
383  if (not digitizer.isValid()) continue;
384  unsigned id = digitizer.getUniqueID();
385  Iterator it = pixels.insert(pair<unsigned, TimeDigitizer>(id, digitizer)).first;
386  it->second.addTimeOfHit(time, pulseHeight, hitType);
387  }
388  }
389  }
390 
391  // if making waveforms for all channels, add missing digitizers.
392 
393  if (m_allChannels) {
394  int numModules = geo->getNumModules();
395  for (int moduleID = 1; moduleID <= numModules; moduleID++) {
396  int numPixels = geo->getModule(moduleID).getPMTArray().getNumPixels();
397  for (int pixelID = 1; pixelID <= numPixels; pixelID++) {
398  auto timeOffset = getTimeOffset(trgTimeOffset, moduleID, pixelID);
399  TimeDigitizer digitizer(moduleID, pixelID, timeOffset.value, timeOffset.error,
400  timeOffset.windowShift, m_rmsNoise, m_sampleTimes);
401  if (not digitizer.isValid()) continue;
402  unsigned id = digitizer.getUniqueID();
403  pixels.insert(pair<unsigned, TimeDigitizer>(id, digitizer));
404  }
405  }
406  }
407 
408  // replace equidistant time base with calibrated one if available
409 
410  if (m_useDatabase or m_useSampleTimeCalibration) {
411  for (auto& pixel : pixels) {
412  auto& digitizer = pixel.second;
413  const auto* sampleTimes = m_timebases->getSampleTimes(digitizer.getScrodID(),
414  digitizer.getChannel());
415  if (not sampleTimes) continue;
416  if (sampleTimes->isCalibrated()) digitizer.setSampleTimes(sampleTimes);
417  }
418  }
419 
420  // replace default noise level with channel dependent one if available,
421 
422  if (m_useDatabase) {
423  for (auto& pixel : pixels) {
424  auto& digitizer = pixel.second;
425  auto moduleID = digitizer.getModuleID();
426  auto channel = digitizer.getChannel();
427  auto rmsNoise = m_noises->getNoise(moduleID, channel);
428  if (rmsNoise > 0) {
429  digitizer.setNoise(rmsNoise);
430  }
431  }
432  }
433 
434  // digitize in time
435 
436  for (auto& pixel : pixels) {
437  const auto& digitizer = pixel.second;
438  int threshold = m_threshold;
439  if (m_useDatabase) { // use channel dependent ones
440  threshold = m_thresholds->getThr(digitizer.getModuleID(), digitizer.getChannel());
441  if (threshold <= 0) threshold = m_threshold; // not available, use the default
442  }
443  if (m_useWaveforms) {
444  digitizer.digitize(m_waveforms, m_rawDigits, m_digits,
445  threshold, m_hysteresis, m_thresholdCount);
446  } else {
447  digitizer.digitize(m_rawDigits, m_digits,
448  threshold, m_thresholdCount, m_electronicJitter);
449  }
450  }
451 
452  // set additional info
453 
454  for (auto& rawDigit : m_rawDigits) {
455  rawDigit.setRevo9Counter(revo9cnt);
456  rawDigit.setPhase(phase);
457  rawDigit.setLastWriteAddr(refWindow);
458  rawDigit.setLookBackWindows(lookBackWindows);
459  rawDigit.setOfflineFlag();
460  }
461 
462  for (auto& waveform : m_waveforms) {
463  waveform.setRevo9Counter(revo9cnt);
464  waveform.setOffsetWindows(offsetWindows);
465  }
466 
467  // set calibration flags
468 
469  if (m_useDatabase) {
470  for (auto& digit : m_digits) {
471  if (m_channelT0->isCalibrated(digit.getModuleID(), digit.getChannel())) {
472  digit.addStatus(TOPDigit::c_ChannelT0Calibrated);
473  }
474  if (m_moduleT0->isCalibrated(digit.getModuleID())) {
475  digit.addStatus(TOPDigit::c_ModuleT0Calibrated);
476  }
477  if (m_commonT0->isCalibrated()) {
478  digit.addStatus(TOPDigit::c_CommonT0Calibrated);
479  }
480  }
481  }
482 
483  // cut on product of pulse width and height, as for data in TOPRawDigitConverter
484 
485  for (auto& digit : m_digits) {
486  if (digit.getPulseWidth() * digit.getPulseHeight() < m_minWidthXheight)
487  digit.setHitQuality(TOPDigit::c_Junk);
488  }
489 
490  }
491 
492 
493  TOPDigitizerModule::TimeOffset TOPDigitizerModule::getTimeOffset(double trgOffset,
494  int moduleID,
495  int pixelID)
496  {
497  double timeOffset = trgOffset;
498  double calErrorSq = 0;
499  int winShift = 0;
500  double timeShift = 0;
501  if (m_useDatabase) {
502  const auto& channelMapper = TOPGeometryPar::Instance()->getChannelMapper();
503  auto channel = channelMapper.getChannel(pixelID);
504  if (m_channelT0->isCalibrated(moduleID, channel)) {
505  timeOffset += m_channelT0->getT0(moduleID, channel);
506  double err = m_channelT0->getT0Error(moduleID, channel);
507  calErrorSq += err * err;
508  }
509  auto asic = channel / 8;
510  if (m_asicShift->isCalibrated(moduleID, asic)) {
511  timeOffset += m_asicShift->getT0(moduleID, asic);
512  winShift = lround(m_asicShift->getT0(moduleID, asic) / m_syncTimeBase * 2);
513  timeShift = winShift * m_syncTimeBase / 2;
514  }
515  if (m_moduleT0->isCalibrated(moduleID)) {
516  timeOffset += m_moduleT0->getT0(moduleID);
517  double err = m_moduleT0->getT0Error(moduleID);
518  calErrorSq += err * err;
519  }
520  if (m_commonT0->isCalibrated()) {
521  timeOffset += m_commonT0->getT0();
522  double err = m_commonT0->getT0Error();
523  calErrorSq += err * err;
524  }
525  }
526  return TimeOffset(timeOffset, calErrorSq, winShift, timeShift);
527  }
528 
529 
530  double TOPDigitizerModule::generatePulseHeight(int moduleID, int pixelID) const
531  {
532  if (m_useDatabase) {
533  const auto& channelMapper = TOPGeometryPar::Instance()->getChannelMapper();
534  auto channel = channelMapper.getChannel(pixelID);
535  if (m_pulseHeights->isCalibrated(moduleID, channel)) {
536  const auto& par = m_pulseHeights->getParameters(moduleID, channel);
537  PulseHeightGenerator generator(par.x0, par.p1, par.p2, m_ADCmax);
538  return generator.generate();
539  }
540  }
541 
542  return m_pulseHeightGenerator.generate();
543  }
544 
546 } // end Belle2 namespace
547 
Base class for Modules.
Definition: Module.h:72
Type-safe access to single objects in the data store.
Definition: StoreObjPtr.h:95
Generates pulse height according to distribution: P(x) = (x/x0)^p1 * exp(-(x/x0)^p2),...
Time digitization of simulated hits in a single electronic channel.
Definition: TimeDigitizer.h:33
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
map< unsigned, const TOPSampleTimes * >::const_iterator Iterator
Iteratior for m_map.
Abstract base class for different kinds of events.
Utility structure for time offset.