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