Belle II Software  release-06-01-15
ECLDigitizerModule.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 //This module
9 #include <ecl/modules/eclDigitizer/ECLDigitizerModule.h>
10 
11 // Root
12 #include <TRandom.h>
13 #include <TFile.h>
14 #include <TTree.h>
15 
16 //Framework
17 #include <framework/logging/Logger.h>
18 #include <framework/utilities/FileSystem.h>
19 #include <framework/gearbox/Unit.h>
20 
21 //ECL
22 #include <ecl/digitization/algorithms.h>
23 #include <ecl/digitization/shaperdsp.h>
24 #include <ecl/digitization/ECLCompress.h>
25 #include <ecl/geometry/ECLGeometryPar.h>
26 #include <ecl/dbobjects/ECLCrystalCalib.h>
27 #include <ecl/dbobjects/ECLWaveformData.h>
28 #include <ecl/dataobjects/ECLHit.h>
29 #include <ecl/dataobjects/ECLSimHit.h>
30 #include <ecl/dataobjects/ECLDigit.h>
31 #include <ecl/dataobjects/ECLDsp.h>
32 #include <ecl/dataobjects/ECLDspWithExtraMCInfo.h>
33 #include <ecl/dataobjects/ECLTrig.h>
34 #include <ecl/dataobjects/ECLWaveforms.h>
35 #include <ecl/utility/ECLDspUtilities.h>
36 
37 using namespace std;
38 using namespace Belle2;
39 using namespace ECL;
40 
41 //-----------------------------------------------------------------
42 // Register the Module
43 //-----------------------------------------------------------------
44 REG_MODULE(ECLDigitizer)
45 
46 //-----------------------------------------------------------------
47 // Implementation
48 //-----------------------------------------------------------------
49 
51 {
52  //Set module properties
53  setDescription("Creates ECLDigiHits from ECLHits.");
54  setPropertyFlags(c_ParallelProcessingCertified);
55  addParam("TriggerTime", m_trigTime,
56  "Flag to use crate trigger times from beam background overlay if there are any (default: false)", false);
57  addParam("Background", m_background, "Flag to use the Digitizer configuration with backgrounds (default: false)", false);
58  addParam("Calibration", m_calibration, "Flag to use the Digitizer for Waveform fit Covariance Matrix calibration (default: false)",
59  false);
60  addParam("DiodeDeposition", m_inter,
61  "Flag to take into account energy deposition in photodiodes; Default diode is sensitive detector (default: true)", true);
62  addParam("WaveformMaker", m_waveformMaker, "Flag to produce background waveform digits (default: false)", false);
63  addParam("CompressionAlgorithm", m_compAlgo, "Waveform compression algorithm (default: 0u)", 0u);
64  addParam("eclWaveformsName", m_eclWaveformsName, "Name of the output/input collection (digitized waveforms)", string(""));
65  addParam("HadronPulseShapes", m_HadronPulseShape, "Flag to include hadron component in pulse shape construction (default: true)",
66  true);
67  addParam("ADCThreshold", m_ADCThreshold, "ADC threshold for waveform fits (default: 25)", 25);
68  addParam("WaveformThresholdOverride", m_WaveformThresholdOverride,
69  "If gt 0 value is applied to all crystals for waveform saving threshold. If lt 0 dbobject is used. (GeV)", -1.0);
70  addParam("StoreDspWithExtraMCInfo", m_storeDspWithExtraMCInfo,
71  "Flag to store Dsp with extra MC information in addition to normal Dsp (default: false)", false);
72  addParam("DspWithExtraMCInfoThreshold", m_DspWithExtraMCInfoThreshold,
73  "Threshold above with to store Dsp with extra MC information [GeV]",
74  0.02);
75  addParam("DspDataTest", m_dspDataTest,
76  "Use DSP coefficients from the database for the processing. This "
77  "will significantly reduce performance so this option is to be "
78  "used for testing only.", false);
79 
80 }
81 
82 ECLDigitizerModule::~ECLDigitizerModule()
83 {
84 }
85 
86 void ECLDigitizerModule::initialize()
87 {
88  m_eclDsps.registerInDataStore();
89  if (m_storeDspWithExtraMCInfo) m_eclDspsWithExtraMCInfo.registerInDataStore();
90  m_eclDigits.registerInDataStore();
91  m_eclTrigs.registerInDataStore();
92 
93  if (m_HadronPulseShape) {
94  B2DEBUG(20,
95  "Hadron pulse shapes for ECL simulations are enabled. Pulse shape simulations use techniques validated with test beam data documented in: S. Longo and J. M. Roney 2018 JINST 13 P03018");
96  } else {
97  B2DEBUG(20, "Hadron pulse shapes for ECL simulations are disabled.");
98  }
99 
100  if (m_inter) {
101  B2DEBUG(20, "Diode-crossing pulse shapes for ECL simulations are enabled.");
102  } else {
103  B2DEBUG(20, "Diode-crossing pulse shapes for ECL simulations are disabled.");
104  }
105 
106  m_eclDiodeHits.registerInDataStore("ECLDiodeHits");
107 
108  m_eclDsps.registerRelationTo(m_eclDigits);
109  if (m_storeDspWithExtraMCInfo)
110  m_eclDspsWithExtraMCInfo.registerRelationTo(m_eclDigits);
111  m_eclDigits.registerRelationTo(m_eclHits);
112  if (m_waveformMaker)
113  m_eclWaveforms.registerInDataStore(m_eclWaveformsName);
114 
115  readDSPDB();
116 
117  m_adc.resize(EclConfiguration::m_nch);
118 
119  EclConfiguration::get().setBackground(m_background);
120 }
121 
122 void ECLDigitizerModule::beginRun()
123 {
124  const EclConfiguration& ec = EclConfiguration::get();
126  Ael("ECLCrystalElectronics"),
127  Aen("ECLCrystalEnergy"),
128  Tel("ECLCrystalElectronicsTime"),
129  Ten("ECLCrystalTimeOffset"),
130  Tct("ECLCrateTimeOffset"),
131  Tmc("ECLMCTimeOffset"),
132  Awave("ECL_FPGA_StoreWaveform");
133  double ns_per_tick = 1.0 / (4.0 * ec.m_rf) * 1e3;// ~0.49126819903043308239 ns/tick
134 
135  calibration_t def = {1, 0};
136  m_calib.assign(8736, def);
137 
138  if (Ael) for (int i = 0; i < 8736; i++) m_calib[i].ascale /= Ael->getCalibVector()[i];
139  if (Aen) for (int i = 0; i < 8736; i++) m_calib[i].ascale /= Aen->getCalibVector()[i] * 20000.0;
140 
141  if (Tel) for (int i = 0; i < 8736; i++) m_calib[i].tshift += Tel->getCalibVector()[i] * ns_per_tick;
142  if (Ten) for (int i = 0; i < 8736; i++) m_calib[i].tshift += Ten->getCalibVector()[i] * ns_per_tick;
143  if (Tct) for (int i = 0; i < 8736; i++) m_calib[i].tshift += Tct->getCalibVector()[i] * ns_per_tick;
144  if (Tmc) for (int i = 0; i < 8736; i++) m_calib[i].tshift += Tmc->getCalibVector()[i] * ns_per_tick;
145 
146  m_Awave.assign(8736, -1);
147  if (m_WaveformThresholdOverride < 0) {
148  if (Awave) for (int i = 0; i < 8736; i++) m_Awave[i] = Awave->getCalibVector()[i];
149  } else {
150  //If m_WaveformThresholdOverride > 0 override ECL_FPGA_StoreWaveform;
151  for (int i = 0; i < 8736; i++) m_Awave[i] = m_WaveformThresholdOverride * m_calib[i].ascale; // convert GeV to ADC
152  }
153 
154  if (m_HadronPulseShape) callbackHadronSignalShapes();
155 
156  // Initialize channel mapper at run start to account for possible
157  // changes in ECL mapping between runs.
158  if (!m_eclMapper.initFromDB()) {
159  B2FATAL("ECL Packer: Can't initialize eclChannelMapper!");
160  }
161 }
162 
163 // interface to C shape fitting function function
164 void ECLDigitizerModule::shapeFitterWrapper(const int j, const int* FitA, const int ttrig,
165  int& m_lar, int& m_ltr, int& m_lq, int& m_chi) const
166 {
167  const crystallinks_t& t = m_tbl[j]; //lookup table [0,8735]
168  const fitparams_t& r = m_fitparams[t.ifunc];
169 
170  ECLShapeFit result;
171 
172  if (!m_dspDataTest) {
173  short int* id = (short int*)m_idn[t.idn].id;
174 
175  int A0 = (int) * (id + 0) - 128;
176  int Askip = (int) * (id + 1) - 128;
177 
178  int Ahard = (int) * (id + 2);
179  int k_a = (int) * ((unsigned char*)id + 26);
180  int k_b = (int) * ((unsigned char*)id + 27);
181  int k_c = (int) * ((unsigned char*)id + 28);
182  int k_16 = (int) * ((unsigned char*)id + 29);
183  int k1_chi = (int) * ((unsigned char*)id + 24);
184  int k2_chi = (int) * ((unsigned char*)id + 25);
185 
186  int chi_thres = (int) * (id + 15);
187 
188  int trg_time = ttrig;
189 
190  result = lftda_((int*)r.f, (int*)r.f1, (int*)r.fg41, (int*)r.fg43,
191  (int*)r.fg31, (int*)r.fg32, (int*)r.fg33, (int*)FitA,
192  trg_time, A0, Ahard, Askip, k_a, k_b, k_c, k_16, k1_chi,
193  k2_chi, chi_thres);
194  } else {
195  std::vector<int> adc(31);
196  for (int i = 0; i < 31; i++) adc[i] = FitA[i];
197  // NOTE: ttrig_packed is any even number in set {8*Z, 8*Z + 2, 8*Z + 4}
198  // where Z is an integer number in 0..23 range
199  // ttrig = ttrig_packed - 2 * (ttrig_packed / 8);
200  // ttrig = 6*Z + q
201  int ttrig_packed = ttrig / 6 * 8 + ttrig % 6;
202  result = ECLDspUtilities::shapeFitter(j + 1, adc, ttrig_packed);
203  }
204 
205  m_lar = result.amp;
206  m_ltr = result.time;
207  m_lq = result.quality;
208  m_chi = result.chi2;
209 
210  //== Set precision of chi^2 to be the same as in the raw data.
211  int discarded_bits = 0;
212  if ((m_chi & 0x7800000) != 0) {
213  m_chi = 0x7800000;
214  } else if ((m_chi & 0x0600000) != 0) {
215  discarded_bits = 14;
216  } else if ((m_chi & 0x0180000) != 0) {
217  discarded_bits = 12;
218  } else if ((m_chi & 0x0060000) != 0) {
219  discarded_bits = 10;
220  } else if ((m_chi & 0x0018000) != 0) {
221  discarded_bits = 8;
222  } else if ((m_chi & 0x0006000) != 0) {
223  discarded_bits = 6;
224  } else if ((m_chi & 0x0001800) != 0) {
225  discarded_bits = 4;
226  } else if ((m_chi & 0x0000600) != 0) {
227  discarded_bits = 2;
228  }
229  if (discarded_bits > 0) {
230  m_chi >>= discarded_bits;
231  m_chi <<= discarded_bits;
232  }
233 }
234 
235 void ECLDigitizerModule::shapeSignals()
236 {
237  const EclConfiguration& ec = EclConfiguration::get();
238  ECLGeometryPar* eclp = ECLGeometryPar::Instance();
239 
240  const double trgtick = ec.s_clock / ec.m_rf / ec.m_ntrg; // trigger tick
241  const double tscale = 2 * trgtick, toff = ec.s_clock / (2 * ec.m_rf);
242 
243  // clear the storage for the event
244  memset(m_adc.data(), 0, m_adc.size()*sizeof(adccounts_t));
245 
246  const double E2GeV = 1 / Unit::GeV; // convert Geant energy units to GeV
247  const double T2us = 1 / Unit::us; // convert Geant time units to microseconds
248 
249  // emulate response for ECL hits after ADC measurements
250  for (const auto& hit : m_eclSimHits) {
251  int cellId = hit.getCellId(); // 1 .. 8736
252  int j = cellId - 1; // 0 .. 8735
253  int id = m_eclMapper.getCrateID(cellId) - 1; // 0 .. 51
254  double timeOffset = tscale * m_ttime[id] - toff;
255  double hitE = hit.getEnergyDep() * m_calib[j].ascale * E2GeV;
256  double hitTimeAve = (hit.getFlightTime() + m_calib[j].tshift + eclp->time2sensor(j, hit.getPosition())) * T2us;
257 
258  m_adc[j].energyConversion = m_calib[j].ascale * E2GeV * 20000;
259  m_adc[j].flighttime += hit.getFlightTime() * hit.getEnergyDep(); //true time weighted by energy
260  m_adc[j].timeshift += m_calib[j].tshift * hit.getEnergyDep();
261  m_adc[j].timetosensor += eclp->time2sensor(j, hit.getPosition()) * hit.getEnergyDep();
262  m_adc[j].totalHadronDep += hit.getHadronEnergyDep(); // true deposited energy hadron component (in GeV)
263  m_adc[j].totalDep += hit.getEnergyDep(); //true deposited energy (in GeV)
264 
265  if (m_HadronPulseShape == true) {
266  double hitHadronE = hit.getHadronEnergyDep() * m_calib[j].ascale * E2GeV;
267  m_adc[j].AddHit(hitE - hitHadronE, hitTimeAve + timeOffset, m_ss_HadronShapeSimulations[0]);//Gamma Component
268  m_adc[j].AddHit(hitHadronE, hitTimeAve + timeOffset, m_ss_HadronShapeSimulations[1]); //Hadron Component
269  } else {
270  m_adc[j].AddHit(hitE, hitTimeAve + timeOffset, m_ss[m_tbl[j].iss]);
271  }
272  }
273 
274  // add only background hits
275  for (const auto& hit : m_eclHits) {
276  if (hit.getBackgroundTag() == BackgroundMetaData::bg_none) continue;
277  int cellId = hit.getCellId(); // 1 .. 8736
278  int j = cellId - 1; // 0 .. 8735
279  int id = m_eclMapper.getCrateID(cellId) - 1; // 0 .. 51
280  double timeOffset = tscale * m_ttime[id] - toff;
281  double hitE = hit.getEnergyDep() * m_calib[j].ascale * E2GeV;
282  double hitTimeAve = (hit.getTimeAve() + m_calib[j].tshift) * T2us;
283  m_adc[j].AddHit(hitE, hitTimeAve + timeOffset, m_ss[m_tbl[j].iss]);
284  }
285 
286  // internuclear counter effect -- charged particle crosses diode and produces signal
287  if (m_inter) {
288  // ionisation energy in Si is I = 3.6x10^-6 MeV for electron-hole pair
289  // 5000 pairs in the diode per 1 MeV deposited in the crystal attached to the diode
290  // conversion factor to get equvalent energy deposition in the crystal to sum up it with deposition in crystal
291  const double diodeEdep2crystalEdep = E2GeV * (1 / (5000 * 3.6e-6));
292  for (const auto& hit : m_eclDiodeHits) {
293  int cellId = hit.getCellId(); // 1 .. 8736
294  int j = cellId - 1; // 0 .. 8735
295  int id = m_eclMapper.getCrateID(cellId) - 1; // 0 .. 51
296  double timeOffset = tscale * m_ttime[id] - toff;
297  double hitE = hit.getEnergyDep() * m_calib[j].ascale * diodeEdep2crystalEdep;
298  double hitTimeAve = (hit.getTimeAve() + m_calib[j].tshift) * T2us;
299 
300  adccounts_t& a = m_adc[j];
301  // cout << "internuclearcountereffect " << j << " " << hit.getEnergyDep() << " " << hit.getTimeAve() << " " << a.total << endl;
302  // for (int i = 0; i < ec.m_nsmp; i++) cout << i << " " << a.c[i] << endl;
303  if (m_HadronPulseShape) {
304  a.AddHit(hitE, hitTimeAve + timeOffset, m_ss_HadronShapeSimulations[2]); // diode component
305  } else {
306  a.AddHit(hitE, hitTimeAve + timeOffset, m_ss[1]); // m_ss[1] is the sampled diode response
307  }
308  // for (int i = 0; i < ec.m_nsmp; i++) cout << i << " " << a.c[i] << endl;
309  }
310  }
311 
312  if (m_calibration) {
313  // This has been added by Alex Bobrov for calibration
314  // of covariance matrix artificially generate 100 MeV in time for each crystal
315  double hitE = 0.1, hitTimeAve = 0.0;
316  for (int j = 0; j < ec.m_nch; j++) {
317  int cellId = j + 1; // 1 .. 8736
318  int id = m_eclMapper.getCrateID(cellId) - 1; // 0 .. 51
319  double timeOffset = tscale * m_ttime[id] - toff;
320  m_adc[j].AddHit(hitE, hitTimeAve + timeOffset, m_ss[m_tbl[j].iss]);
321  }
322  }
323 }
324 
325 void ECLDigitizerModule::makeElectronicNoiseAndPedestal(int J, int* FitA)
326 {
327  const EclConfiguration& ec = EclConfiguration::get();
328  float z[ec.m_nsmp], AdcNoise[ec.m_nsmp]; // buffers with electronic noise
329  // Noise generation
330  for (int i = 0; i < ec.m_nsmp; i++) z[i] = gRandom->Gaus(0, 1);
331  m_noise[m_tbl[J].inoise].generateCorrelatedNoise(z, AdcNoise);
332  for (int i = 0; i < ec.m_nsmp; i++) FitA[i] = 20 * AdcNoise[i] + 3000;
333 }
334 
335 void ECLDigitizerModule::makeWaveforms()
336 {
337  const EclConfiguration& ec = EclConfiguration::get();
338  BitStream out(ec.m_nch * ec.m_nsmp * 18 / 32);
339  out.putNBits(m_compAlgo & 0xff, 8);
340  ECLCompress* comp = selectAlgo(m_compAlgo & 0xff);
341  if (comp == nullptr)
342  B2FATAL("Unknown compression algorithm: " << m_compAlgo);
343 
344  int FitA[ec.m_nsmp]; // buffer for the waveform fitter
345  // loop over entire calorimeter
346  for (int j = 0; j < ec.m_nch; j++) {
347  adccounts_t& a = m_adc[j];
348  makeElectronicNoiseAndPedestal(j, FitA);
349  for (int i = 0; i < ec.m_nsmp; i++) {
350  int A = 20000 * a.c[i] + FitA[i];
351  FitA[i] = max(0, min(A, (1 << 18) - 1));
352  }
353  comp->compress(out, FitA);
354  }
355  out.resize();
356 
357  ECLWaveforms* wf = new ECLWaveforms;
358  m_eclWaveforms.assign(wf);
359 
360  std::swap(out.getStore(), wf->getStore());
361  if (comp) delete comp;
362 }
363 
364 void ECLDigitizerModule::event()
365 {
366  const EclConfiguration& ec = EclConfiguration::get();
367 
368  // make relation between cellid and eclhits
369  struct ch_t {int cell, id;};
370  vector<ch_t> hitmap;
371  for (const auto& hit : m_eclHits) {
372  int j = hit.getCellId() - 1; //0~8735
373  if (hit.getBackgroundTag() == BackgroundMetaData::bg_none) hitmap.push_back({j, hit.getArrayIndex()});
374  // cout<<"C:"<<hit.getBackgroundTag()<<" "<<hit.getCellId()<<" "<<hit.getEnergyDep()<<" "<<hit.getTimeAve()<<endl;
375  }
376 
377  bool isBGOverlay = m_eclWaveforms.isValid(), isTrigTime = false;
378  BitStream out;
379  ECLCompress* comp = nullptr;
380 
381  // check background overlay
382  if (isBGOverlay) {
383  std::swap(out.getStore(), m_eclWaveforms->getStore());
384  out.setPos(0);
385  unsigned int compAlgo = out.getNBits(8);
386  comp = selectAlgo(compAlgo & 0x7f);
387  if (comp == nullptr)
388  B2FATAL("Unknown compression algorithm: " << compAlgo);
389  isTrigTime = compAlgo >> 7; // crate trigger times are stored and retrived
390  if (isTrigTime) {
391  for (int i = 0; i < ECL::ECL_CRATES; i++) {
392  unsigned char t = out.getNBits(7); // [0..72)
393  m_ttime[i] = t;
394  }
395  }
396  }
397 
398  if (!m_trigTime || !isTrigTime) { // reproduce previous logic -- one trigger time for all crates
399  int DeltaT = gRandom->Uniform(0, double(ec.m_ntrg) * 0.5); // trigger decision can come in any time [0..72)
400  for (int id = 0; id < ECL::ECL_CRATES; id++) m_ttime[id] = DeltaT;
401  }
402 
403  int triggerTag0 = m_EventMetaData->getEvent();
404  for (int id = 0; id < ECL::ECL_CRATES; id++) {
405  auto eclTrig = m_eclTrigs.appendNew();
406  int triggerPhase0 = 2 * (m_ttime[id] + m_ttime[id] / 3);
407  eclTrig->setTrigId(id);
408  eclTrig->setTimeTrig(triggerPhase0);
409  eclTrig->setTrigTag(triggerTag0);
410  }
411 
412  shapeSignals();
413 
414  // We want to produce background waveforms in simulation first than
415  // dump to a disk, read from the disk to test before real data
416  if (m_waveformMaker) { makeWaveforms(); return; }
417 
418  int FitA[ec.m_nsmp]; // buffer for the waveform fitter
419 
420  // loop over entire calorimeter
421  for (int j = 0; j < ec.m_nch; j++) {
422  adccounts_t& a = m_adc[j];
423 
424  //normalize the MC true arrival times
425  if (m_adc[j].totalDep > 0) {
426  m_adc[j].flighttime /= m_adc[j].totalDep;
427  m_adc[j].timeshift /= m_adc[j].totalDep;
428  m_adc[j].timetosensor /= m_adc[j].totalDep;
429  }
430 
431  // if background waveform is here there is no need to generate
432  // electronic noise since it is already in the waveform
433  if (isBGOverlay) {
434  comp->uncompress(out, FitA);
435  } else {
436  // Signal amplitude should be above 100 keV
437  if (a.total < 0.0001) continue;
438  makeElectronicNoiseAndPedestal(j, FitA);
439  }
440 
441  for (int i = 0; i < ec.m_nsmp; i++) {
442  int A = 20000 * a.c[i] + FitA[i];
443  FitA[i] = max(0, min(A, (1 << 18) - 1));
444  }
445 
446  int energyFit = 0; // fit output : Amplitude 18 bits
447  int tFit = 0; // fit output : T_ave 12 bits
448  int qualityFit = 0; // fit output : quality 2 bits
449  int chi = 0; // fit output : chi square it is not available in the experiment
450 
451  int id = m_eclMapper.getCrateID(j + 1) - 1; // 0 .. 51
452  int ttrig = 2 * m_ttime[id];
453 
454  shapeFitterWrapper(j, FitA, ttrig, energyFit, tFit, qualityFit, chi);
455 
456  if (energyFit > m_ADCThreshold) {
457  int CellId = j + 1;
458 
459  //note energyFit and m_Awave is in ADC units
460  if (energyFit > m_Awave[CellId - 1]) {
461  //only save waveforms above ADC threshold
462  const auto eclDsp = m_eclDsps.appendNew();
463  eclDsp->setCellId(CellId);
464  eclDsp->setDspA(FitA);
465  }
466 
467  // only store extra MC info if requested and above threshold
468  if (m_storeDspWithExtraMCInfo and a.totalDep >= m_DspWithExtraMCInfoThreshold) {
469  const auto eclDspWithExtraMCInfo = m_eclDspsWithExtraMCInfo.appendNew();
470  eclDspWithExtraMCInfo->setCellId(CellId);
471  eclDspWithExtraMCInfo->setDspA(FitA);
472  eclDspWithExtraMCInfo->setEnergyDep(a.totalDep);
473  eclDspWithExtraMCInfo->setHadronEnergyDep(a.totalHadronDep);
474  eclDspWithExtraMCInfo->setFlightTime(a.flighttime);
475  eclDspWithExtraMCInfo->setTimeShift(a.timeshift);
476  eclDspWithExtraMCInfo->setTimeToSensor(a.timetosensor);
477  eclDspWithExtraMCInfo->setEnergyConversion(a.energyConversion * 20000);
478  }
479 
480  const auto eclDigit = m_eclDigits.appendNew();
481  eclDigit->setCellId(CellId); // cellId in range from 1 to 8736
482  eclDigit->setAmp(energyFit); // E (GeV) = energyFit/20000;
483  eclDigit->setTimeFit(tFit); // t0 (us)= (1520 - m_ltr)*24.*12/508/(3072/2) ;
484  eclDigit->setQuality(qualityFit);
485  if (qualityFit == 2)
486  eclDigit->setChi(chi);
487  else eclDigit->setChi(0);
488  for (const auto& hit : hitmap)
489  if (hit.cell == j) eclDigit->addRelationTo(m_eclHits[hit.id]);
490 
491  // set relation to DspWithExtraInfo
492  for (auto& DspWithExtraMCInfo : m_eclDspsWithExtraMCInfo) {
493  if (eclDigit->getCellId() == DspWithExtraMCInfo.getCellId()) DspWithExtraMCInfo.addRelationTo(eclDigit);
494  }
495  }
496  } //store each crystal hit
497  if (comp) delete comp;
498 }
499 
500 void ECLDigitizerModule::endRun()
501 {
502 }
503 
504 void ECLDigitizerModule::terminate()
505 {
506 }
507 
508 void ECLDigitizerModule::callbackHadronSignalShapes()
509 {
510 
511  if (m_waveformParametersMC.hasChanged()) {
512 
513  m_ss_HadronShapeSimulations.resize(3);
514 
515  //read MC templates from database
516  float photonParsPSD[10];
517  float hadronParsPSD[10];
518  float diodeParsPSD[10];
519  for (int i = 0; i < 10; i++) {
520  photonParsPSD[i] = m_waveformParametersMC->getPhotonParameters()[i + 1];
521  hadronParsPSD[i] = m_waveformParametersMC->getHadronParameters()[i + 1];
522  diodeParsPSD[i] = m_waveformParametersMC->getDiodeParameters()[i + 1];
523  }
524 
525  //Initialize templates for hadron shape simulations
526  m_ss_HadronShapeSimulations[0].InitSample(photonParsPSD, m_waveformParametersMC->getPhotonParameters()[0]);
527  m_ss_HadronShapeSimulations[1].InitSample(hadronParsPSD, m_waveformParametersMC->getHadronParameters()[0]);
528  m_ss_HadronShapeSimulations[2].InitSample(diodeParsPSD, m_waveformParametersMC->getDiodeParameters()[0]);
529 
530  }
531 
532 }
533 
534 void ECLDigitizerModule::readDSPDB()
535 {
536  const EclConfiguration& ec = EclConfiguration::get();
537 
538  string dataFileName;
539  if (m_background) {
540  dataFileName = FileSystem::findFile("/data/ecl/ECL-WF-BG.root");
541  B2DEBUG(150, "ECLDigitizer: Reading configuration data with background from: " << dataFileName);
542  } else {
543  dataFileName = FileSystem::findFile("/data/ecl/ECL-WF.root");
544  B2DEBUG(150, "ECLDigitizer: Reading configuration data without background from: " << dataFileName);
545  }
546  assert(! dataFileName.empty());
547 
548  TFile rootfile(dataFileName.c_str());
549  TTree* tree = (TTree*) rootfile.Get("EclWF");
550  TTree* tree2 = (TTree*) rootfile.Get("EclAlgo");
551  TTree* tree3 = (TTree*) rootfile.Get("EclNoise");
552 
553  if (tree == 0 || tree2 == 0 || tree3 == 0) B2FATAL("Data not found");
554 
555  m_tbl.resize(ec.m_nch);
556 
557  const int maxncellid = 512;
558  int ncellId;
559  vector<int> cellId(maxncellid);//[ncellId] buffer for crystal identification number
560 
561  tree->SetBranchAddress("ncellId", &ncellId);
562  tree->SetBranchAddress("cellId", cellId.data());
563 
564  vector<int> eclWaveformDataTable(ec.m_nch);
565  for (Long64_t j = 0, jmax = tree->GetEntries(); j < jmax; j++) {
566  tree->GetEntry(j);
567  assert(ncellId <= maxncellid);
568  for (int i = 0; i < ncellId; ++i)
569  eclWaveformDataTable[cellId[i] - 1] = j;
570  }
571  B2DEBUG(150, "ECLDigitizer: " << tree->GetEntries() << " sets of wave form covariance matricies will be used.");
572 
573  ECLWFAlgoParams* algo = new ECLWFAlgoParams;
574  tree2->SetBranchAddress("Algopars", &algo);
575  tree2->SetBranchAddress("ncellId", &ncellId);
576  tree2->SetBranchAddress("cellId", cellId.data());
577  Long64_t jmax2 = tree2->GetEntries();
578  vector<ECLWFAlgoParams> eclWFAlgoParams;
579  eclWFAlgoParams.reserve(jmax2);
580  for (Long64_t j = 0; j < jmax2; j++) {
581  tree2->GetEntry(j);
582  assert(ncellId <= maxncellid);
583  eclWFAlgoParams.push_back(*algo);
584  for (int i = 0; i < ncellId; ++i)
585  m_tbl[cellId[i] - 1].idn = j;
586  }
587  if (algo) delete algo;
588  B2DEBUG(150, "ECLDigitizer: " << eclWFAlgoParams.size() << " parameter sets of fitting algorithm were read.");
589 
590  ECLNoiseData* noise = new ECLNoiseData;
591  tree3->SetBranchAddress("NoiseM", &noise);
592  tree3->SetBranchAddress("ncellId", &ncellId);
593  tree3->SetBranchAddress("cellId", cellId.data());
594 
595  Long64_t jmax3 = tree3->GetEntries();
596  m_noise.reserve(jmax3);
597  for (Long64_t j = 0; j < jmax3; j++) {
598  tree3->GetEntry(j);
599  assert(ncellId <= maxncellid);
600  m_noise.push_back(*noise);
601  if (ncellId == 0) {
602  for (int i = 0; i < ec.m_nch; i++)
603  m_tbl[i].inoise = 0;
604  break;
605  } else {
606  for (int i = 0; i < ncellId; ++i)
607  m_tbl[cellId[i] - 1].inoise = j;
608  }
609  }
610  if (noise) delete noise;
611  B2DEBUG(150, "ECLDigitizer: " << eclWFAlgoParams.size() << " noise matricies were loaded.");
612 
613  // repack fitting algorithm parameters
614  m_idn.resize(eclWFAlgoParams.size());
615  for (int i = 0, imax = eclWFAlgoParams.size(); i < imax; i++)
616  repack(eclWFAlgoParams[i], m_idn[i]);
617 
618  vector<uint_pair_t> pairIdx;
619  for (int i = 0; i < ec.m_nch; i++) {
620  unsigned int wfIdx = eclWaveformDataTable[i];
621  unsigned int algoIdx = m_tbl[i].idn;
622  uint_pair_t p(wfIdx, algoIdx);
623  vector<uint_pair_t>::iterator ip = find(pairIdx.begin(), pairIdx.end(), p);
624  if (ip != pairIdx.end()) { // crystal i already have the same parameters as before
625  m_tbl[i].ifunc = ip - pairIdx.begin();
626  } else { // new combination of parameters
627  m_tbl[i].ifunc = pairIdx.size();
628  pairIdx.push_back(p);
629  }
630  }
631 
632  // now we know how many distinct elements of the (Waveform x AlgoParams) matrix should be
633  m_fitparams.resize(pairIdx.size());
634 
635  ECLWaveformData* eclWFData = new ECLWaveformData;
636  tree->SetBranchAddress("CovarianceM", &eclWFData);
637  tree->SetBranchStatus("ncellId", 0); // do not read it at the moment
638  tree->SetBranchStatus("cellId", 0); // do not read it at the moment
639 
640 // fill parameters for each (Waveform x AlgoParams) parameter set
641  for (unsigned int ip = 0; ip < pairIdx.size(); ip++) {
642  const uint_pair_t& p = pairIdx[ip];
643  tree->GetEntry(p.first); // retrieve data to eclWFData pointer
644  getfitparams(*eclWFData, eclWFAlgoParams[p.second], m_fitparams[ip]);
645  }
646  B2DEBUG(150, "ECLDigitizer: " << m_fitparams.size() << " fitting crystals groups were created.");
647 
648  // at the moment there is only one sampled signal shape in the pool
649  // since all shaper parameters are the same for all crystals
650  m_ss.resize(2);
651  float MP[10]; eclWFData->getWaveformParArray(MP);
652  m_ss[0].InitSample(MP, 27.7221);
653  // parameters vector from ps.dat file, time offset 0.5 usec added to
654  // have peak position with parameters from ps.dat roughly in the
655  // same place as in current MC
656  // double crystal_params[10] = {0.5, 0.301298, 0.631401, 0.470911, 0.903988, -0.11734200/19.5216, 2.26567, 0.675393, 0.683995, 0.0498786};
657  // m_ss[0].InitSample(crystal_params, 0.9999272*19.5216);
658  for (int i = 0; i < ec.m_nch; i++) m_tbl[i].iss = 0;
659  // one sampled diode response in the pool, parameters vector from
660  // pg.dat file, time offset 0.5 usec added to have peak position with
661  // parameters from ps.dat roughly in the same place as in current MC
662  double diode_params[] = {0 + 0.5, 0.100002, 0.756483, 0.456153, 0.0729031, 0.3906 / 9.98822, 2.85128, 0.842469, 0.854184, 0.110284};
663  m_ss[1].InitSample(diode_params, 0.9569100 * 9.98822);
664 
665  B2DEBUG(150, "ECLDigitizer: " << m_ss.size() << " sampled signal templates were created.");
666 
667  if (eclWFData) delete eclWFData;
668 
669  rootfile.Close();
670 }
671 
672 void ECLDigitizerModule::repack(const ECLWFAlgoParams& eclWFAlgo, algoparams_t& t)
673 {
674  // filling short int array
675  t.id[ 0] = eclWFAlgo.getlAT() + 128;
676  t.id[ 1] = eclWFAlgo.getsT() + 128;
677  t.id[ 2] = eclWFAlgo.gethT();
678  t.id[ 3] = 17952;
679  t.id[ 4] = 19529;
680  t.id[ 5] = 69;
681  t.id[ 6] = 0;
682  t.id[ 7] = 0;
683  t.id[ 8] = 257;
684  t.id[ 9] = -1;
685  t.id[10] = 0;
686  t.id[11] = 0;
687 
688  // filling unsigned char array
689  t.ic[12 * 2 + 0] = eclWFAlgo.getk1();
690  t.ic[12 * 2 + 1] = eclWFAlgo.getk2();
691  t.ic[13 * 2 + 0] = eclWFAlgo.getka();
692  t.ic[13 * 2 + 1] = eclWFAlgo.getkb();
693  t.ic[14 * 2 + 0] = eclWFAlgo.getkc();
694  t.ic[14 * 2 + 1] = eclWFAlgo.gety0s() - 16;
695 
696  // again, filling short int array
697  t.id[15] = eclWFAlgo.getcT();
698 }
699 
700 void ECLDigitizerModule::getfitparams(const ECLWaveformData& eclWFData, const ECLWFAlgoParams& eclWFAlgo, fitparams_t& p)
701 {
702  const EclConfiguration& ec = EclConfiguration::get();
703 
704  double ssd[16][16];
705  eclWFData.getMatrix(ssd);
706  vector<double> MP(10);
707  eclWFData.getWaveformParArray(MP.data());
708 
709  // shape function parameters
710  int iff = 1 << 14; //Alex Bobrov for correct chi square
711 
712  int ia = 1 << eclWFAlgo.getka();
713  int ib = 1 << eclWFAlgo.getkb();
714  int ic = 1 << eclWFAlgo.getkc();
715 
716  int_array_192x16_t& ref_f = p.f;
717  int_array_192x16_t& ref_f1 = p.f1;
718  int_array_192x16_t& ref_fg31 = p.fg31;
719  int_array_192x16_t& ref_fg32 = p.fg32;
720  int_array_192x16_t& ref_fg33 = p.fg33;
721  int_array_24x16_t& ref_fg41 = p.fg41;
722  int_array_24x16_t& ref_fg43 = p.fg43;
723 
724  ShaperDSP_t dsp(MP);
725  dsp.settimestride(ec.m_step);
726  dsp.setseedoffset(ec.m_step / ec.m_ndt);
727  dsp.settimeseed(-(ec.m_step - (ec.m_step / ec.m_ndt)));
728  vector<dd_t> f(16);
729  for (int k = 0; k < 2 * ec.m_ndt; k++, dsp.nextseed()) { // calculate fit parameters around 0 +- 1 ADC tick
730  dsp.fillvector(f);
731 
732  double g0g0 = 0, g0g1 = 0, g1g1 = 0, g0g2 = 0, g1g2 = 0, g2g2 = 0;
733  double sg0[16], sg1[16], sg2[16];
734  for (int j = 0; j < 16; j++) {
735  double g0 = 0, g1 = 0, g2 = 0;
736  for (int i = 0; i < 16; i++) {
737  g0 += ssd[j][i] * f[i].first;
738  g1 += ssd[j][i] * f[i].second;
739  g2 += ssd[j][i];
740  }
741  g0g0 += g0 * f[j].first;
742  g0g1 += g1 * f[j].first;
743  g1g1 += g1 * f[j].second;
744  g0g2 += g0;
745  g1g2 += g1;
746  g2g2 += g2;
747  sg0[j] = g0;
748  sg1[j] = g1;
749  sg2[j] = g2;
750  }
751 
752  double a00 = g1g1 * g2g2 - g1g2 * g1g2;
753  double a11 = g0g0 * g2g2 - g0g2 * g0g2;
754  double a22 = g0g0 * g1g1 - g0g1 * g0g1;
755  double a01 = g1g2 * g0g2 - g0g1 * g2g2;
756  double a02 = g0g1 * g1g2 - g1g1 * g0g2;
757  double a12 = g0g1 * g0g2 - g0g0 * g1g2;
758 
759  double igg2 = 1 / (a11 * g1g1 + g0g1 * a01 + g1g2 * a12);
760 
761  // to fixed point representation
762  const double isd = 3. / 4., sd = 1 / isd ; // conversion factor (???)
763  for (int i = 0; i < 16; i++) {
764  double w = i ? 1.0 : 1. / 16.;
765 
766  ref_f [k][i] = lrint(f[i].first * iff * w);
767  ref_f1 [k][i] = lrint(f[i].second * iff * w * sd);
768 
769  double fg31 = (a00 * sg0[i] + a01 * sg1[i] + a02 * sg2[i]) * igg2;
770  double fg32 = (a01 * sg0[i] + a11 * sg1[i] + a12 * sg2[i]) * igg2;
771  double fg33 = (a02 * sg0[i] + a12 * sg1[i] + a22 * sg2[i]) * igg2;
772 
773  ref_fg31[k][i] = lrint(fg31 * ia * w);
774  ref_fg32[k][i] = lrint(fg32 * ib * w * isd);
775  ref_fg33[k][i] = lrint(fg33 * ic * w);
776  }
777 
778  //first approximation without time correction
779  int jk = 23 + ((48 - k) >> 2);
780  if (jk >= 0 && jk < 24 && (48 - k) % 4 == 0) {
781 
782  double igg1 = 1 / a11;
783  // to fixed point
784  for (int i = 0; i < 16; i++) {
785  double w = i ? 1.0 : 1. / 16.;
786 
787  double fg41 = (g2g2 * sg0[i] - g0g2 * sg2[i]) * igg1;
788  double fg43 = (g0g0 * sg2[i] - g0g2 * sg0[i]) * igg1;
789  ref_fg41[jk][i] = lrint(fg41 * ia * w);
790  ref_fg43[jk][i] = lrint(fg43 * ic * w);
791  }
792  }
793  }
794 }
Class for accessing objects in the database.
Definition: DBObjPtr.h:21
DB object to store photon, hadron and diode shape parameters used in simulations.
The ECLDigitizer module.
std::pair< unsigned int, unsigned int > uint_pair_t
a pair of unsigned ints
fitparams_t::int_array_192x16_t int_array_192x16_t
weighting coefficients for time and amplitude calculation
fitparams_t::int_array_24x16_t int_array_24x16_t
weighting coefficients amplitude calculation.
Container for constant matrix used to generate electronic noise.
Container for constant parameters used in waveform fits.
int getkc() const
getter for multipliers power of 2 for fg33,fg43
int getlAT() const
getter for the threshold to calculate time
int getsT() const
getter for the threshold to send data to collector
int getcT() const
getter for the chi2 threshold for quality bit
int getk1() const
getter for the multipliers power of 2 for f
int getk2() const
getter for multipliers power of 2 for chi2 calculation
int getkb() const
getter for multipliers power of 2 for fg32
int gethT() const
getter for the hardware threshold
int gety0s() const
getter for the start point for pedestal calculation
int getka() const
getter for multipliers power of 2 for fg31 fg41
ECLWaveformData - container for inverse covariant matrix and shape parameters for time and amplitude ...
void getMatrix(float M[16][16]) const
Getter method for all matrix as two dimentional array (floats)
void getWaveformParArray(float P[10]) const
Getter method for waveform shape parameters as one dimentional array of floats.
Class to store ECL waveforms for entire calorimeter.
Definition: ECLWaveforms.h:21
std::vector< unsigned int > & getStore()
get data
Definition: ECLWaveforms.h:27
Bit stream struct.
Definition: ECLCompress.h:28
Abstract class (interface) for ECL waveform compression/decompression to/from the BitStream storage.
Definition: ECLCompress.h:107
virtual void uncompress(BitStream &in, int *adc)=0
Decompress the ECL waveform.
virtual void compress(BitStream &out, const int *adc)=0
Compress the ECL waveform.
The Class for ECL Geometry Parameters.
singleton class to hold the ECL configuration
static constexpr double m_step
time between points in internal units t_{asrto}*m_rf/2.
static constexpr double s_clock
digitization clock in RF units
static constexpr double m_rf
accelerating RF, http://ptep.oxfordjournals.org/content/2013/3/03A006.full.pdf
static constexpr int m_nch
total number of electronic channels (crystals) in calorimeter
static constexpr int m_ntrg
number of trigger counts per ADC clock tick
static constexpr int m_ndt
number of points per ADC tick where signal fit procedure parameters are evaluated
static constexpr int m_nsmp
number of ADC measurements for signal fitting
Class include function that calculate electronic response from energy deposit
Definition: shaperdsp.h:26
void fillvector(std::vector< double > &) const
fill vector with response function values and its derivative
Definition: shaperdsp.cc:416
void settimeseed(double)
set initial time
Definition: shaperdsp.cc:370
void settimestride(double)
set grid step for function calculation
Definition: shaperdsp.cc:360
void nextseed()
substruct toffset to tzero
Definition: shaperdsp.cc:375
void setseedoffset(double)
set timeoffset
Definition: shaperdsp.cc:365
Base class for Modules.
Definition: Module.h:72
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
Abstract base class for different kinds of events.
calibration constants per channel
ShaperDSP fit results from _lftda function.
a struct for the parameters of the algorithm
a struct for the fit parameters