Belle II Software  release-06-01-15
CsIDigitizerModule.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 #include <beast/csi/geometry/CsiGeometryPar.h>
10 #include <beast/csi/modules/CsIDigitizerModule.h>
11 #include <beast/csi/dataobjects/CsiDigiHit.h>
12 #include <beast/csi/dataobjects/CsiSimHit.h>
13 #include <beast/csi/dataobjects/CsiHit.h>
14 #include <framework/dataobjects/EventMetaData.h>
15 #include <framework/datastore/StoreObjPtr.h>
16 
17 #include <vector>
18 #include <TGraph.h>
19 #include <TH1F.h>
20 #include <TH1I.h>
21 #include <framework/core/RandomNumbers.h>
22 #include <TVector3.h>
23 #include <math.h>
24 
25 #define PI 3.14159265358979323846
26 
27 using namespace std;
28 using namespace Belle2;
29 using namespace csi;
30 //-----------------------------------------------------------------
31 // Register the Module
32 //-----------------------------------------------------------------
33 REG_MODULE(CsIDigitizer)
34 
35 //-----------------------------------------------------------------
36 // Implementation
37 //-----------------------------------------------------------------
38 
39 //Calibration constants (hard-coded for now, put in the xml file for later)
41  m_TrueEdep(0.0),
42  m_nWFcounter(0),
43  m_aDigiHit("CsiDigiHits"),
44  m_calibConstants(16, 5),
45  m_noiseLevels(16, 0.25e-3),
46  m_LY(16, 40e6),
47  m_tRatio(16, 0),
48  m_tFast(16, 1),
49  m_tSlow(16, 1),
50  m_LCE(16, 0.1),
51  m_PmtQE(16, 0.05),
52  m_PmtGain(16, 1e5)
53 {
54  // Set module properties
55  setDescription("Digitizer for the BEAST CsI system");
56 
57  // Parameter definitions
58  addParam("Resolution", m_Resolution, "Resolution (in mV) of the ACD", 4.8828e-4);
59  addParam("SampleRate", m_SampleRate, "Sample rate (in samples/sec) of the ADC", 250e6);
60  addParam("nWaveforms", m_nWaveforms, "Number of waveforms to save. 0: none, -1: all ", 0);
61 }
62 
63 CsIDigitizerModule::~CsIDigitizerModule()
64 {
65 }
66 
67 void CsIDigitizerModule::initialize()
68 {
69 
70  B2DEBUG(100, "Initializing ");
71 
72  //m_aHit.isRequired();
73  m_aSimHit.isRequired();
74  m_aDigiHit.registerInDataStore();
75 
76  //Calculation of the derived paramaters
77  m_dt = 1e9 / m_SampleRate;
78  setnSamples(8192);
79 
80  //Get crystal and PMT constants from xml files
81  CsiGeometryPar* csip = CsiGeometryPar::Instance();
82  for (uint i = 0; i != m_LY.size(); ++i) {
83  csip->Print(i, 80);
84 
85  m_LY.at(i) = 1;//1e3 * csip->GetMaterialProperty(i, "SCINTILLATIONYIELD");
86  m_tFast.at(i) = 1;//csip->GetMaterialProperty(i, "FASTTIMECONSTANT");
87  m_tSlow.at(i) = 1;//csip->GetMaterialProperty(i, "SLOWTIMECONSTANT");
88  m_tRatio.at(i) = 1;//csip->GetMaterialProperty(i, "YIELDRATIO");
89  }
90 
91  //Operate the pure CsI at higher gain to compensate for lower light yield
92  for (int i = 8; i < 16; i++) {
93  m_PmtGain[i] = 35e5;
94  }
95 
96 }
97 
98 void CsIDigitizerModule::beginRun()
99 {
100  //Signal tempo = genTimeSignal(10, 10, 2, 0, 1);
101 }
102 
103 void CsIDigitizerModule::event()
104 {
105  StoreObjPtr<EventMetaData> eventMetaDataPtr;
106  int m_currentEventNumber = eventMetaDataPtr->getEvent();
107 
108  B2DEBUG(80, "Digitingevent " << m_currentEventNumber);
109 
110  //Loop over CsiSimHits
111  if (m_aSimHit.getEntries() > 0) {
112  int hitNum = m_aSimHit.getEntries();
114  // double E_tmp[16] = {0}; /**< Sum energy deposited in each cell */
115  // double edepSum = 0; /**< Sum energy deposited in all cells */
116 
117  for (int i = 0 ; i < 16 ; i++) {
118  m_SimHitTimes[i].clear();
119  m_SimHitEdeps[i].clear();
120  }
121 
122 
123  B2DEBUG(150, "Looping over CsISimHits");
124  for (int i = 0; i < hitNum; i++) { // Loop over CsISimHits
125  CsiSimHit* aCsISimHit = m_aSimHit[i];
126  int m_cellID = aCsISimHit->getCellId();
127  double edep = aCsISimHit->getEnergyDep();
128  double tof = aCsISimHit->getFlightTime();
129  // double hitTime = aCsISimHit->getTimeAve(); /**< Time average of the hit*/
130  // double hitTimeRMS = sqrt( aCsISimHit->getTimeVar()/aCsISimHit->getEnergyDep()); /**< Time rms of the hit*/
131  CsiGeometryPar* csip = CsiGeometryPar::Instance();
132 
133  TVector3 hitPos = aCsISimHit->getPosition();
134  TVector3 cellPos = csip->GetPositionTV3(m_cellID);
135  TVector3 cellAngle = csip->GetOrientationTV3(m_cellID);
136 
137  double localPos = (15. - (hitPos - cellPos) *
138  cellAngle);
140  // 0.06 is the speed of light in CsI(Tl)
141  double propagTime = m_SampleRate *
142  (0.0600 * localPos + (tof / CLHEP::ns)) * 1E-9;
145  m_SimHitTimes[m_cellID].push_back(propagTime);
146  m_SimHitEdeps[m_cellID].push_back(edep);
147 
148  /*
149  if (i<10){
150  B2INFO("Hit No = : " << i );
151  B2INFO("Deposited energy = : " << edep );
152  B2INFO("Average time = : " << hitTime );
153  B2INFO("Time RMS = : " << hitTimeRMS );
154 
155  csip->Print(m_cellID);
156  }
157  */
158 
159  }
160 
161  for (int iCh = 0; iCh < 16; iCh++) {
162 
163  int n = m_SimHitTimes[iCh].size();
164 
165  if (n > 0) {
166 
167  B2DEBUG(140, "Generating Time signal");
168  Signal tempSignal;
169  m_TrueEdep = genTimeSignal(&tempSignal, m_SimHitEdeps[iCh], m_SimHitTimes[iCh], iCh, m_dt, m_nSamples, false);
170 
171 
172  B2DEBUG(140, "Launching Charge integration");
173  bool recordWaveform = false;
174  if ((m_nWaveforms == -1) || (m_nWFcounter < m_nWaveforms)) {
175  m_nWFcounter++;
176  recordWaveform = true;
177  B2DEBUG(80, "Recording WF");
178  }
179  uint16_t max = doChargeIntegration(tempSignal, 128, &m_Baseline, &m_Charge, &m_Time, &m_Waveform,
180  &m_DPPCIBits, 5, 1.2e4, 1e4, 1e3, recordWaveform);
181 
182  if (m_Charge > 0) {
183  m_aDigiHit.appendNew();
184  m_hitNum = m_aDigiHit.getEntries() - 1;
185  m_aDigiHit[m_hitNum]->setCellId(iCh);
186  m_aDigiHit[m_hitNum]->setCharge(m_Charge);
187  m_aDigiHit[m_hitNum]->setTime(m_Time);
188  m_aDigiHit[m_hitNum]->setBaseline(m_Baseline);
189  m_aDigiHit[m_hitNum]->setTrueEdep(m_TrueEdep);
190 
191  m_aDigiHit[m_hitNum]->setMaxVal(max);
192 
193  m_aDigiHit[m_hitNum]->setWaveform(&m_Waveform);
194  m_aDigiHit[m_hitNum]->setStatusBits(&m_DPPCIBits);
195  }
196  }
197  }
198  }
199 }
200 
201 
202 void CsIDigitizerModule::endRun()
203 {
204 }
205 
206 void CsIDigitizerModule::terminate()
207 {
208 }
209 
210 uint16_t CsIDigitizerModule::doChargeIntegration(Signal _u, int _NsamBL, uint16_t* _BSL, uint32_t* _Q,
211  uint32_t* _t, vector<uint16_t>* _Waveform,
212  vector<uint8_t>* _DPPCIBits, int _Treshold,
213  double _TriggerHoldoff, double _GateWidth,
214  double _GateOffset, bool _recordTraces)
215 {
216 
217  B2DEBUG(80, "Arguments: " << &_u << ", " << _NsamBL << ", " << _BSL << ", " << _Q << ", " << _t
218  << ", " << _Waveform << ", " << _Treshold << ", " << _TriggerHoldoff << ", " << _GateWidth
219  << ", " << _GateOffset << ", " << _recordTraces);
220 
221  vector<int> x = doDigitization(_u, m_Resolution);
222  int nSam = x.size();
223 
224  // Plots for debugging (see CAEN DPP-CI figs 2.2,2.3
225  TH1I h_trigger("h_trigger", "Trigger", nSam, 0, nSam - 1);
226  TH1I h_gate("h_gate", "Gate", nSam, 0, nSam - 1);
227  TH1I h_holdoff("h_holdoff", "Holdoff", nSam, 0, nSam - 1);
228  TH1I h_baseline("h_baseline", "Baseline", nSam, 0, nSam - 1);
229  TH1I h_charge("h_charge", "Charge", nSam, 0, nSam - 1);
230  TH1F h_signal("h_signal", "Continous signal", nSam, 0, nSam - 1);
231  TH1I h_digsig("h_digsig", "Digital signal", nSam, 0, nSam - 1);
232 
233 
234  int max_gated = (int) floor(_GateWidth / m_dt);
235  int gate_offset = (int) floor(_GateOffset / m_dt);
236  int max_holdoff = (int) floor(_TriggerHoldoff / m_dt);
237 
238  int baseline = 0;
239  int charge = 0;
240  int n_holdoff = 0;
241  int n_gated = 0;
242 
243  bool gate = false;
244  bool holdoff = false;
245  bool stop = false;
246  bool trigger = false;
247 
248  // Find trigger position
249  int i = 0;
250  int iFirstTrigger = 0;
251  list<int> baselineBuffer;
252  vector<int>::iterator it;
253  float tempBaseline;
254 
255  // Saving inverse number of samples used for baseline averaging (avoid division)
256  const double invMaxNavgBL = 1.0 / _NsamBL;
257  double invNavgBL;
259  _Waveform->resize(nSam, 0);
260  _DPPCIBits->resize(nSam, 0);
261 
262  B2DEBUG(140, "Scanning vector: all should have nSam=" << nSam);
263 
264  uint16_t maxval = 0;
265 
266  for (it = x.begin(); (it != x.end()); ++it, ++i) {
267 
268  if (*it > maxval)
269  maxval = *it;
270 
271  _Waveform->at(i) = *it;
272  _DPPCIBits->at(i) = trigger + (gate << 1) + (holdoff << 2) + (stop << 3);
273 
274  if (_recordTraces) {
275 
276  h_trigger.Fill(i, (int) trigger) ;
277  h_gate.Fill(i, (int) gate) ;
278  h_holdoff.Fill(i, (int) holdoff) ;
279  h_baseline.Fill(i, baseline) ;
280  h_charge.Fill(i, charge) ;
281  h_digsig.Fill(i, *it) ;
282  h_signal.Fill(i, _u.at(i)) ;
283  }
284 
285  if (!gate && !holdoff) {
286 
287  baselineBuffer.push_back(*it);
288 
289 
290  if ((i + 1) > _NsamBL) {
291  baselineBuffer.pop_front();
292  invNavgBL = invMaxNavgBL;
293  } else {
294  invNavgBL = (1.0 / i);
295  }
296 
297  tempBaseline = 0;
298  for (list<int>::iterator itbl = baselineBuffer.begin(); itbl != baselineBuffer.end(); ++itbl)
299  tempBaseline += (float) * itbl;
300 
301  tempBaseline *= invNavgBL;
302 
303  baseline = (int) round(tempBaseline);
304 
305  trigger = (*it - baseline) > _Treshold;
306 
307  //first time we see a trigger
308  if (trigger && !iFirstTrigger)
309  iFirstTrigger = i;
310 
311  } else {
312 
313  if (gate) {
314  charge += (*(it - gate_offset) - baseline);
315  *_BSL = baseline;
316  n_gated++;
317  }
318 
319  if (holdoff) {
320  n_holdoff++;
321  }
322 
323  trigger = false;
324  }
325 
326  holdoff = trigger || (holdoff && (n_holdoff < max_holdoff));
327  gate = trigger || (gate && (n_gated < max_gated));
328 
329  stop = iFirstTrigger && !gate && !holdoff;
330  }
331 
332  *_Q = charge;
333  // from the doc: 2 sample uncertainty.
334  *_t = (uint)(iFirstTrigger + 2.0 * (gRandom->Rndm() - 0.5));
335 
336  if (not(_recordTraces)) {
337  _Waveform->clear();
338  _DPPCIBits->clear();
339  } else {
340  //Below is obsolete: recording now done in the study module
341  /*
342  char rootfilename[100];
343  sprintf(rootfilename, "output/BEAST/plots/dpp-ci_WF%i.root", m_nWFcounter);
344 
345  B2INFO("Writing to " << rootfilename);
346  TFile fs(rootfilename, "recreate");
347 
348  h_trigger.Write();
349  h_gate.Write();
350  h_holdoff.Write();
351  h_baseline.Write();
352  h_charge.Write();
353  h_signal.Write();
354  h_digsig.Write();
355 
356  TVectorD Edep(1);
357  Edep[0] = m_TrueEdep;
358  Edep.Write("Edep");
359 
360  fs.Close();
361  */
362  }
363 
364  return maxval;
365 }
366 
367 vector<int> CsIDigitizerModule::doDigitization(Signal _v, double _LSB)
368 {
369  vector<int> output(_v.size(), 0);
370  double invLSB = 1.0 / _LSB;
371 
372  int i = 0;
373  for (Signal::iterator it = _v.begin() ; it != _v.end(); ++it, ++i) {
374  output.at(i) = (int) round(*it * invLSB);
375  }
376 
377  return output;
378 }
379 
380 double CsIDigitizerModule::genTimeSignal(Signal* _output, Signal _energies, Signal _times, int _iChannel, int _dt, int _nsam,
381  bool _save)
382 {
383 
384  double invdt = 1.0 / _dt;
385 
386  double tf = 0;
387  double t0 = 1e9;
388  double sumEnergies = 0.0;
389 
390  for (Signal::iterator it = _times.begin() ; it != _times.end(); ++it) {
391  if (*it < t0)
392  t0 = *it;
393 
394  if (*it > tf)
395  tf = *it;
396  }
397 
398  Signal edepos(_nsam, 0.0);
399 
400  int i = 0;
401  int ioffset = floor(_nsam * 0.25);
402  B2DEBUG(150, "Filling edepos vector. Container length is " << _nsam);
403 
404  for (Signal::iterator it = _times.begin() ; it != _times.end(); ++it, ++i) {
405  sumEnergies += _energies.at(i);
406  // time index +/- 1 time bin
407  int timeIndex = ((int)(*it - t0) * invdt + ioffset);
408  if ((timeIndex - 1) > (int) edepos.size()) {
409  B2WARNING(" genTimeSignal: TimeIndex greater than length of signal container. Skipping deposit of " << _energies.at(i) << "GeV");
410  } else {
411  edepos.at(timeIndex) += _energies.at(i);
412  }
413  }
414 
415  B2DEBUG(80, "Generating time responses for channel " << _iChannel);
416  B2DEBUG(80, " Fast time constant " << m_tFast[_iChannel]);
417  B2DEBUG(80, " Slow time constant " << m_tSlow[_iChannel]);
418  /*
419  Signal Qcathode = firstOrderResponse(m_LY[_iChannel] * m_LCE[_iChannel] * m_PmtQE[_iChannel],
420  edepos, 0, _dt, m_tSlow[_iChannel], 0.0, m_tRatio[_iChannel], m_tFast[_iChannel]);
421  */
422 
423  Signal Qcathode = firstOrderResponse((1 - m_tRatio[_iChannel]) * m_LY[_iChannel] * m_LCE[_iChannel] * m_PmtQE[_iChannel], edepos, 0,
424  _dt, m_tSlow[_iChannel], 0.0);
425 
426 
427  if (m_tRatio[_iChannel]) {
428  Signal QcathodeF = firstOrderResponse(m_tRatio[_iChannel] * m_LY[_iChannel] * m_LCE[_iChannel] * m_PmtQE[_iChannel], edepos, 0, _dt,
429  m_tFast[_iChannel], 0.0);
430 
431  int j = 0;
432  for (Signal::iterator it = Qcathode.begin() ; it != Qcathode.end(); ++it, ++j)
433  *it += QcathodeF.at(j);
434  }
435 
436  Signal Vanode = firstOrderResponse(1.602e-10 * m_Zl * invdt * m_PmtGain[_iChannel], Qcathode, 0, _dt, m_tRisePMT, m_tTransitPMT);
437 
438  i = 0;
439  B2DEBUG(150, "Adding noise. Container length is " << Vanode.size());
440  addNoise(&Vanode, m_noiseLevels[_iChannel], 5e-3 * gRandom->Rndm() + 1e-2);
441 
442  if (_save) {
443  Signal t(_nsam, 0);
444  t.at(0) = t0;
445 
446  for (i = 1; i < _nsam; i++)
447  t.at(i) = t.at(i - 1) + _dt;
448 
449  TGraph gPlot1(_nsam, &t[0], &edepos[0]);
450  gPlot1.SaveAs("EdeposOut.root");
451 
452  TGraph gPlot2(_nsam, &t[0], &Qcathode[0]);
453  gPlot2.SaveAs("QOut.root");
454 
455  TGraph gPlot3(_nsam, &t[0], &Vanode[0]);
456  gPlot3.SaveAs("VOut.root");
457  }
458 
459  *_output = Vanode;
460 
461  return sumEnergies;
462 }
463 
464 
465 int CsIDigitizerModule::addNoise(Signal* y, double _rms, double _offset)
466 {
467  for (Signal::iterator it = y->begin() ; it != y->end(); ++it)
468  *it += _offset + gRandom->Gaus(0, _rms);
469 
470  return y->size();
471 }
472 
473 /*
474 Signal CsIDigitizerModule::firstOrderResponse(double _gain, Signal _u, double _y0, double _dt, double _tSlow, double _delay, double _tRatio, double _tFast)
475 {
476 
477  Signal slowLight = firstOrderResponse(_gain*(1-_tRatio), _u, _y0, _dt, _tFast, _delay);
478 
479  if (_tRatio>0) {
480  Signal fastLight = firstOrderResponse(_gain* _tRatio , _u, _y0, _dt, _tFast, _delay);
481  int i=0;
482  for (Signal::iterator it = slowLight.begin() ; it != slowLight.end(); ++it, ++i)
483  *it += fastLight.at(i);
484  }
485  B2WARNING("You shouldn't see this!");
486  return slowLight;
487 
488 }
489 */
490 
491 Signal CsIDigitizerModule::firstOrderResponse(double _gain, Signal _u, double _y0, double _dt, double _tau, double _delay)
492 {
493 
494  B2DEBUG(80, "Generating 1st order response with arguments");
495  B2DEBUG(80, " _gain: " << _gain);
496  B2DEBUG(80, " length(_u): " << _u.size());
497  B2DEBUG(80, " _y0: " << _y0);
498  B2DEBUG(80, " _dt: " << _dt);
499  B2DEBUG(80, " _tau: " << _tau);
500  B2DEBUG(80, " _delay: " << _delay);
501 
502 
503  // First skip everything if the time constant in infinitely short.
504  if (_tau == 0)
505  return _u;
506 
507  int n = _u.size();
508  Signal y;
509  double k[4] = {0};
510 
511  static const double invSix = 1.0 / 6.0;
512  double invtau = 1.0 / _tau;
513  y.push_back(_y0);
514 
515  // Apply delay to input
516  int n_delay = (int) round(_delay / _dt);
517  Signal::iterator it = _u.begin();
518  double _u_0 = _u.front();
519  _u.insert(it , n_delay, _u_0);
520  _u.resize(n);
521 
522  // Apply that input to the good old Runge-Kutta 4 routine.
523  for (int i = 0, j = 0; i < (n - 1); i++) {
524  j = i + 1;
525  k[0] = f(i , _u[i], _u[j], y[i] , invtau);
526  k[1] = f(i + 0.5, _u[i], _u[j], y[i] + 0.5 * _dt * k[0], invtau);
527  k[2] = f(i + 0.5, _u[i], _u[j], y[i] + 0.5 * _dt * k[1], invtau);
528  k[3] = f(j , _u[i], _u[j], y[i] + _dt * k[2], invtau);
529 
530  y.push_back(y[i] + _dt * invSix * (k[0] + 2 * k[1] + 2 * k[2] + k[3]));
531  }
532 
533 
534  // Apply gain
535  if (_gain != 1) {
536  for (Signal::iterator it2 = y.begin() ; it2 != y.end(); ++it2)
537  *it2 *= _gain;
538  }
539 
540  return y;
541 }
542 
543 
544 double CsIDigitizerModule::f(double fi, double u_i, double u_j, double y, double invtau)
545 {
546  //linear interpolation of the input at fractional index fi
547  double u = u_i * (fi - floor(fi)) + u_j * (ceil(fi) - fi);
548 
549  return u - invtau * y;
550 }
551 
ClassCsiSimHit - Geant4 simulated hits in CsI crystals in BEAST.
Definition: CsiSimHit.h:31
int getCellId() const
Get Cell ID.
Definition: CsiSimHit.h:87
double getFlightTime() const
Get Flight time from IP.
Definition: CsiSimHit.h:102
double getEnergyDep() const
Get Deposit energy.
Definition: CsiSimHit.h:107
TVector3 getPosition() const
Get Position.
Definition: CsiSimHit.h:122
Base class for Modules.
Definition: Module.h:72
Type-safe access to single objects in the data store.
Definition: StoreObjPtr.h:95
Digitizer for the BEAST CsI system.
The Class for CSI Geometry Parameters.
void Print(const int cid, int debuglevel=80)
Print crystal information.
TVector3 GetOrientationTV3(int cid)
Get the orientation of the crystal in a root TVector3.
TVector3 GetPositionTV3(int cid)
Get the position of the crystal in a root TVector3.
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
std::vector< double > Signal
Designed to hold a "continuous" (in time and amplitude) signal
Abstract base class for different kinds of events.