Belle II Software  release-05-02-19
SVDClusterizerDirectModule.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2010 - Belle II Collaboration *
4  * *
5  * Author: The Belle II Collaboration *
6  * Contributors: Peter Kvasnicka *
7  * *
8  * This software is provided "as is" without any warranty. *
9  **************************************************************************/
10 
11 #include <svd/modules/svdReconstruction/SVDClusterizerDirectModule.h>
12 
13 #include <framework/datastore/StoreArray.h>
14 #include <framework/logging/Logger.h>
15 
16 #include <vxd/geometry/GeoCache.h>
17 #include <svd/geometry/SensorInfo.h>
18 
19 #include <mdst/dataobjects/MCParticle.h>
20 #include <svd/dataobjects/SVDTrueHit.h>
21 #include <svd/dataobjects/SVDShaperDigit.h>
22 #include <svd/dataobjects/SVDCluster.h>
23 #include <mva/dataobjects/DatabaseRepresentationOfWeightfile.h>
24 
25 #include <svd/reconstruction/NNWaveFitTool.h>
26 
27 #include <unordered_map>
28 #include <algorithm>
29 #include <numeric>
30 #include <functional>
31 #include <cassert>
32 
33 using namespace std;
34 using namespace Belle2;
35 using namespace Belle2::SVD;
36 
37 //-----------------------------------------------------------------
38 // Register the Module
39 //-----------------------------------------------------------------
40 REG_MODULE(SVDClusterizerDirect)
41 
42 //-----------------------------------------------------------------
43 // Implementation
44 //-----------------------------------------------------------------
45 
47 {
48  B2DEBUG(200, "SVDClusterizerDirectModule ctor");
49  //Set module properties
50  setDescription("Clusterize SVDShaperDigits and reconstruct hits");
51  setPropertyFlags(c_ParallelProcessingCertified);
52 
53  // 1. Collections.
54  addParam("Digits", m_storeShaperDigitsName,
55  "6-digits collection name", string(""));
56  addParam("Clusters", m_storeClustersName,
57  "Cluster collection name", string(""));
58  addParam("TrueHits", m_storeTrueHitsName,
59  "TrueHit collection name", string(""));
60  addParam("MCParticles", m_storeMCParticlesName,
61  "MCParticles collection name", string(""));
62  addParam("SVDEventInfo", m_svdEventInfoName,
63  "SVDEventInfo name", string(""));
64 
65  // 2. Calibration and time fitter sources
66  addParam("TimeFitterName", m_timeFitterName,
67  "Name of time fitter data file", string("SVDTimeNet_6samples"));
68  addParam("CalibratePeak", m_calibratePeak, "Use calibrattion (vs. default) for peak widths and positions", bool(false));
69 
70  // 3. Clustering
71  // FIXME: Idiotic names of parameters kept for compatibility with the old clusterizer.
72  addParam("NoiseSN", m_cutAdjacent,
73  "SN for digits to be considered for clustering", m_cutAdjacent);
74  addParam("SeedSN", m_cutSeed,
75  "SN for digits to be considered as seed", m_cutSeed);
76  addParam("ClusterSN", m_cutCluster,
77  "Minimum SN for clusters", m_cutCluster);
78  addParam("HeadTailSize", m_sizeHeadTail,
79  "Cluster size at which to switch to Analog head tail algorithm", m_sizeHeadTail);
80 
81 }
82 
83 void SVDClusterizerDirectModule::initialize()
84 {
85  //Register collections
86  StoreArray<SVDCluster> storeClusters(m_storeClustersName);
87  StoreArray<SVDShaperDigit> storeShaperDigits(m_storeShaperDigitsName);
88  StoreArray<SVDTrueHit> storeTrueHits(m_storeTrueHitsName);
89  StoreArray<MCParticle> storeMCParticles(m_storeMCParticlesName);
90 
91  storeClusters.registerInDataStore();
92  storeShaperDigits.isRequired();
93  storeTrueHits.isOptional();
94  storeMCParticles.isOptional();
95  m_storeSVDEvtInfo.isRequired();
96 
97  if (!m_storeSVDEvtInfo.isOptional(m_svdEventInfoName)) m_svdEventInfoName = "SVDEventInfoSim";
98  m_storeSVDEvtInfo.isRequired(m_svdEventInfoName);
99 
100  RelationArray relClusterShaperDigits(storeClusters, storeShaperDigits);
101  RelationArray relClusterTrueHits(storeClusters, storeTrueHits);
102  RelationArray relClusterMCParticles(storeClusters, storeMCParticles);
103  RelationArray relShaperDigitTrueHits(storeShaperDigits, storeTrueHits);
104  RelationArray relShaperDigitMCParticles(storeShaperDigits, storeMCParticles);
105 
106  relClusterShaperDigits.registerInDataStore();
107  //Relations to simulation objects only if the ancestor relations exist
108  if (relShaperDigitTrueHits.isOptional())
109  relClusterTrueHits.registerInDataStore();
110  if (relShaperDigitMCParticles.isOptional())
111  relClusterMCParticles.registerInDataStore();
112 
113  //Store names to speed up creation later
114  m_storeClustersName = storeClusters.getName();
115  m_storeShaperDigitsName = storeShaperDigits.getName();
116  m_storeTrueHitsName = storeTrueHits.getName();
117  m_storeMCParticlesName = storeMCParticles.getName();
118 
119  m_relClusterShaperDigitName = relClusterShaperDigits.getName();
120  m_relClusterTrueHitName = relClusterTrueHits.getName();
121  m_relClusterMCParticleName = relClusterMCParticles.getName();
122  m_relShaperDigitTrueHitName = relShaperDigitTrueHits.getName();
123  m_relShaperDigitMCParticleName = relShaperDigitMCParticles.getName();
124 
125  B2INFO(" 1. COLLECTIONS:");
126  B2INFO(" --> MCParticles: " << m_storeMCParticlesName);
127  B2INFO(" --> Digits: " << m_storeShaperDigitsName);
128  B2INFO(" --> Clusters: " << m_storeClustersName);
129  B2INFO(" --> TrueHits: " << m_storeTrueHitsName);
130  B2INFO(" --> DigitMCRel: " << m_relShaperDigitMCParticleName);
131  B2INFO(" --> ClusterMCRel: " << m_relClusterMCParticleName);
132  B2INFO(" --> ClusterDigitRel: " << m_relClusterShaperDigitName);
133  B2INFO(" --> DigitTrueRel: " << m_relShaperDigitTrueHitName);
134  B2INFO(" --> ClusterTrueRel: " << m_relClusterTrueHitName);
135  B2INFO(" 2. CALIBRATION DATA:");
136  B2INFO(" --> Time NN: " << m_timeFitterName);
137  B2INFO(" 4. CLUSTERING:");
138  B2INFO(" --> Neighbour cut: " << m_cutAdjacent);
139  B2INFO(" --> Seed cut: " << m_cutSeed);
140  B2INFO(" --> Cluster charge cut: " << m_cutCluster);
141  B2INFO(" --> HT for clusters >: " << m_sizeHeadTail);
142 
143  // Properly initialize the NN time fitter
144  // FIXME: Should be moved to beginRun
145  // FIXME: No support for 3/6 sample switching within a run/event
146  DBObjPtr<DatabaseRepresentationOfWeightfile> dbXml(m_timeFitterName);
147  m_fitter.setNetwrok(dbXml->m_data);
148 }
149 
150 void SVDClusterizerDirectModule::createRelationLookup(const RelationArray& relation,
151  RelationLookup& lookup, size_t digits)
152 {
153  lookup.clear();
154  //If we don't have a relation we don't build a lookuptable
155  if (!relation) return;
156  //Resize to number of digits and set all values
157  lookup.resize(digits);
158  for (const auto& element : relation) {
159  lookup[element.getFromIndex()] = &element;
160  }
161 }
162 
163 void SVDClusterizerDirectModule::fillRelationMap(const RelationLookup& lookup,
164  std::map<unsigned int, float>& relation, unsigned int index)
165 {
166  //If the lookup table is not empty and the element is set
167  if (!lookup.empty() && lookup[index]) {
168  const RelationElement& element = *lookup[index];
169  const unsigned int size = element.getSize();
170  //Add all Relations to the map
171  for (unsigned int i = 0; i < size; ++i) {
172  //negative weights are from ignored particles, we don't like them and
173  //thus ignore them :D
174  if (element.getWeight(i) < 0) continue;
175  relation[element.getToIndex(i)] += element.getWeight(i);
176  }
177  }
178 }
179 
180 void SVDClusterizerDirectModule::event()
181 {
182 
183  const StoreArray<SVDShaperDigit> storeShaperDigits(m_storeShaperDigitsName);
184  // If no digits or no SVDEventInfo, nothing to do
185  if (!storeShaperDigits || !storeShaperDigits.getEntries() || !m_storeSVDEvtInfo.isValid()) return;
186 
187  SVDModeByte modeByte = m_storeSVDEvtInfo->getModeByte();
188 
189  size_t nDigits = storeShaperDigits.getEntries();
190  B2DEBUG(90, "Initial size of StoreDigits array: " << nDigits);
191 
192  const StoreArray<MCParticle> storeMCParticles(m_storeMCParticlesName);
193  const StoreArray<SVDTrueHit> storeTrueHits(m_storeTrueHitsName);
194 
195  RelationArray relShaperDigitMCParticle(storeShaperDigits, storeMCParticles, m_relShaperDigitMCParticleName);
196  RelationArray relShaperDigitTrueHit(storeShaperDigits, storeTrueHits, m_relShaperDigitTrueHitName);
197 
198  StoreArray<SVDCluster> storeClusters(m_storeClustersName);
199  storeClusters.clear();
200 
201  RelationArray relClusterMCParticle(storeClusters, storeMCParticles,
202  m_relClusterMCParticleName);
203  if (relClusterMCParticle) relClusterMCParticle.clear();
204 
205  RelationArray relClusterShaperDigit(storeClusters, storeShaperDigits,
206  m_relClusterShaperDigitName);
207  if (relClusterShaperDigit) relClusterShaperDigit.clear();
208 
209  RelationArray relClusterTrueHit(storeClusters, storeTrueHits,
210  m_relClusterTrueHitName);
211  if (relClusterTrueHit) relClusterTrueHit.clear();
212 
213  //Build lookup tables for relations
214  createRelationLookup(relShaperDigitMCParticle, m_mcRelation, nDigits);
215  createRelationLookup(relShaperDigitTrueHit, m_trueRelation, nDigits);
216 
217  // Create fit tool object
218  NNWaveFitTool fitTool = m_fitter.getFitTool();
219 
220  // I. Group digits by sensor/side.
221  vector<pair<unsigned short, unsigned short> > sensorDigits;
222  VxdID lastSensorID(0);
223  size_t firstSensorDigit = 0;
224  for (size_t iDigit = 0; iDigit < nDigits; ++iDigit) {
225  const SVDShaperDigit& adigit = *storeShaperDigits[iDigit];
226  VxdID sensorID = adigit.getSensorID();
227  sensorID.setSegmentNumber(adigit.isUStrip() ? 1 : 0);
228  if (sensorID != lastSensorID) { // we have a new sensor side
229  sensorDigits.push_back(make_pair(firstSensorDigit, iDigit));
230  firstSensorDigit = iDigit;
231  lastSensorID = sensorID;
232  }
233  }
234  // save last VxdID
235  sensorDigits.push_back(make_pair(firstSensorDigit, nDigits));
236 
237  // ICYCLE OVER SENSORS
238  for (auto id_indices : sensorDigits) {
239  // Retrieve parameters from sensorDigits
240  unsigned int firstDigit = id_indices.first;
241  unsigned int lastDigit = id_indices.second;
242  // Get VXDID from the first digit
243  const SVDShaperDigit& sampleDigit = *storeShaperDigits[firstDigit];
244  VxdID sensorID = sampleDigit.getSensorID();
245  bool isU = sampleDigit.isUStrip();
246 
247  // Retrieve sensor parameters from GeoCache
248  const SensorInfo& info = dynamic_cast<const SensorInfo&>(VXD::GeoCache::get(sensorID));
249  double pitch = isU ? info.getUPitch() : info.getVPitch();
250 
251  // 4. Cycle through digits and form clusters on the way.
252 
253  // Identify contiguous groups of strips
254  // We maintain two pointers: firstDigit and lastDigit. We also keep track of last seen
255  // strip number.
256  // We start with firstDigit = lastDigit = 0
257  // If there is a gap in strip numbers or the current digit/strip is invalid, we save the
258  // current group (fd, cd), unless empty, and set fd = cd.
259  // If current strip is valid, we include it: ld = cd+1.
260 
261  vector<pair<size_t, size_t> > stripGroups;
262  unordered_map<size_t, apvSamples> storedNormedSamples;
263  size_t firstClusterDigit = firstDigit;
264  size_t lastClusterDigit = firstDigit;
265  short lastStrip = -2;
266 
267  B2DEBUG(300, "Clustering digits " << firstDigit << " to " << lastDigit);
268  for (size_t iDigit = firstDigit; iDigit < lastDigit; ++iDigit) {
269 
270  const SVDShaperDigit& digit = *storeShaperDigits[iDigit];
271  unsigned short currentStrip = digit.getCellID();
272  B2DEBUG(300, "Digit " << iDigit << ", strip: " << currentStrip << ", lastStrip: " << lastStrip);
273  B2DEBUG(300, "First CD: " << firstClusterDigit << " Last CD: " << lastClusterDigit);
274  // Get calibration
275  bool validDigit = true; // no local run mask
276  float stripNoiseADU = m_noiseCal.getNoise(sensorID, isU, currentStrip);
277 
278  // If the strip is not masked away, save normalized samples (sample/stripNoise)
279  apvSamples normedSamples;
280  if (validDigit) {
281  auto samples = digit.getSamples();
282  transform(samples.begin(), samples.end(), normedSamples.begin(),
283  bind2nd(divides<float>(), stripNoiseADU));
284  validDigit = validDigit && pass3Samples(normedSamples, m_cutAdjacent);
285  }
286 
287  // If this is a valid digit, store normed samples.
288  // It's not just that we don't have to normalize again - we don't need other data than strip
289  // number and normed samples from the digits!
290  if (validDigit) {
291  zeroSuppress(normedSamples, m_cutAdjacent);
292  storedNormedSamples.insert(make_pair(iDigit, normedSamples));
293  }
294 
295  // See if we have a gap in strip numbers.
296  bool consecutive = ((currentStrip - lastStrip) == 1);
297  lastStrip = currentStrip;
298 
299  B2DEBUG(300, (validDigit ? "Valid " : "Invalid ") << (consecutive ? "consecutive" : "gap"));
300 
301  // If this is not a valid digit or we have a gap, we save if there is something to save
302  if ((!validDigit || !consecutive) && (firstClusterDigit < lastClusterDigit)) {
303  B2DEBUG(300, "Saving (" << firstClusterDigit << ", " << lastClusterDigit << ")");
304  stripGroups.emplace_back(firstClusterDigit, lastClusterDigit);
305  }
306 
307  // Update counters
308  if (validDigit) {
309  if (consecutive) {// consecutive and valid : include in cluster
310  lastClusterDigit = iDigit + 1;
311  } else { // valid after a gap = new cluster
312  firstClusterDigit = iDigit;
313  lastClusterDigit = iDigit + 1;
314  }
315  } else { // invalid digit : skip
316  firstClusterDigit = iDigit + 1;
317  lastClusterDigit = iDigit + 1;
318  }
319  } // for digits
320  // save last if any
321  if (firstClusterDigit < lastClusterDigit) {
322  B2DEBUG(300, "Saving (" << firstClusterDigit << ", " << lastDigit << ")");
323  stripGroups.emplace_back(firstClusterDigit, lastDigit);
324  }
325 
326  ostringstream os;
327  os << sensorID << "NormedSamples: " << endl;
328  for (auto item : storedNormedSamples) {
329  os << item.first << ": ";
330  copy(item.second.begin(), item.second.end(), ostream_iterator<double>(os, " "));
331  os << endl;
332  }
333  os << "StripGroups: " << endl;
334  for (auto item : stripGroups) {
335  os << "(" << item.first << ", " << item.second << "), ";
336  }
337  B2DEBUG(300, os.str());
338 
339  // 5. Now we loop through stripGroups and form clusters.
340  // We are still keeping digit indices, so we are keeping hold on the relations as wwell.
341 
342  // FIXME: Pretty stupid. There should be ClusterCandidate type to hold all this, as
343  // well as normed samples. It could calculate a lot of things by itself and make the
344  // code cleaner.
345  vector<unsigned short> stripNumbers;
346  vector<float> stripPositions;
347  vector<float> stripNoises;
348  vector<float> timeShifts;
349  vector<float> waveWidths;
350 
351  for (auto clusterBounds : stripGroups) {
352 
353  unsigned short clusterSize = clusterBounds.second - clusterBounds.first;
354  assert(clusterSize > 0);
355 
356  stripNumbers.clear();
357  stripPositions.clear();
358  stripNoises.clear();
359  timeShifts.clear();
360  waveWidths.clear();
361 
362  for (size_t iDigit = clusterBounds.first; iDigit < clusterBounds.second; ++iDigit) {
363  // Now we take the normed samples and calculate the common
364  // cluster probability distribution and, from it, the time and its error, and
365  // the probability of the hit being from beam background.
366  const SVDShaperDigit& digit = *storeShaperDigits[iDigit];
367 
368  unsigned short stripNo = digit.getCellID();
369  stripNumbers.push_back(stripNo);
370  // Is the calibrations interface sensible?
371  double stripNoiseADU = m_noiseCal.getNoise(sensorID, isU, stripNo);
372  stripNoises.push_back(
373  m_pulseShapeCal.getChargeFromADC(sensorID, isU, stripNo, stripNoiseADU)
374  );
375  // Some calibrations magic.
376  // FIXME: Only use calibration on real data. Until simulations correspond to
377  // default calibrtion, we cannot use it.
378  double peakWidth = 270;
379  double timeShift = isU ? 2.5 : -2.2;
380  if (m_calibratePeak) {
381  peakWidth = 1.988 * m_pulseShapeCal.getWidth(sensorID, isU, stripNo);
382  timeShift = m_pulseShapeCal.getPeakTime(sensorID, isU, stripNo)
383  - 0.25 * peakWidth;
384  }
385  // Correct with trigger bin information
386  const double triggerBinSep = 4 * 1.96516; //in ns
387  double apvPhase = triggerBinSep * (0.5 + static_cast<int>(modeByte.getTriggerBin()));
388  timeShift = timeShift + apvPhase;
389  waveWidths.push_back(peakWidth);
390  timeShifts.push_back(timeShift);
391  stripPositions.push_back(
392  isU ? info.getUCellPosition(stripNo) : info.getVCellPosition(stripNo)
393  );
394  stripPositions.push_back(
395  isU ? info.getUCellPosition(stripNo) : info.getVCellPosition(stripNo)
396  );
397  }
398  // The formulas don't take into account differences in strip noises. So we take RMS
399  // strip noise as cluster noise.
400  // cluster noise is in e-!
401  float clusterNoise = sqrt(
402  1.0 / clusterSize
403  * inner_product(stripNoises.begin(), stripNoises.end(), stripNoises.begin(), 0.0)
404  );
405  B2DEBUG(200, "RMS cluster noise: " << clusterNoise);
406 
407  // This will hold component pdfs. We may want to rememeber them to study
408  // homogeneity of cluster times.
409  shared_ptr<nnFitterBinData> pStrip;
410  // This will aggregate the components pdfs to get cluster time pdf
411  nnFitterBinData pCluster(m_fitter.getBinCenters().size());
412  fill(pCluster.begin(), pCluster.end(), double(1.0));
413  for (size_t iClusterStrip = 0; iClusterStrip < clusterSize; ++iClusterStrip) {
414  size_t iDigit = clusterBounds.first + iClusterStrip;
415  ostringstream os1;
416  os1 << "Input to NNFitter: iDigit = " << iDigit << endl << "Samples: ";
417  copy(storedNormedSamples[iDigit].begin(), storedNormedSamples[iDigit].end(),
418  ostream_iterator<double>(os1, " "));
419  os1 << endl;
420  pStrip = m_fitter.getFit(storedNormedSamples[iDigit], waveWidths[iClusterStrip]);
421  os1 << "Output from NNWaveFitter: " << endl;
422  copy(pStrip->begin(), pStrip->end(), ostream_iterator<double>(os1, " "));
423  os1 << endl;
424  // Apply strip time shift to pdf
425  fitTool.shiftInTime(*pStrip, -timeShifts[iClusterStrip]);
426  fitTool.multiply(pCluster, *pStrip);
427  os1 << "Accummulated: " << endl;
428  copy(pCluster.begin(), pCluster.end(), ostream_iterator<double>(os1, " "));
429  B2DEBUG(200, os1.str());
430  }
431  // We can now calculate clsuter time and its error.
432  double clusterTime, clusterTimeErr;
433  tie(clusterTime, clusterTimeErr) = fitTool.getTimeShift(pCluster);
434  B2DEBUG(200, "Time: " << clusterTime << " +/- " << clusterTimeErr);
435  // Now we have the cluster time pdf, so we can calculate amplitudes.
436  // In the next cycle thrugh cluster's digits, we calculate ampltidues and their
437  // errors.
438  vector<double> stripAmplitudes(stripNoises.size());
439  vector<double> stripAmplitudeErrors(stripNoises.size());
440  double clusterChi2 = 0.0;
441  for (size_t iClusterStrip = 0; iClusterStrip < clusterSize; ++iClusterStrip) {
442  size_t iDigit = clusterBounds.first + iClusterStrip;
443  double snAmp, snAmpError, chi2;
444  tie(snAmp, snAmpError, chi2) =
445  fitTool.getAmplitudeChi2(storedNormedSamples[iDigit], clusterTime, waveWidths[iClusterStrip]);
446  // We have to re-normalize: Multiply amplitudes in SN units by noises in electrons.
447  stripAmplitudes[iClusterStrip] =
448  stripNoises[iClusterStrip] * snAmp;
449  stripAmplitudeErrors[iClusterStrip] =
450  stripNoises[iClusterStrip] * snAmpError;
451  clusterChi2 += chi2;
452  B2DEBUG(200, "Digit " << iDigit << " Noise: " << stripNoises[iClusterStrip]
453  << " Amplitude: " << stripAmplitudes[iClusterStrip]
454  << " +/- " << stripAmplitudeErrors[iClusterStrip]
455  << " Chi2: " << chi2
456  );
457  }
458  // Cluster charge and S/N
459  float clusterCharge = accumulate(stripAmplitudes.begin(), stripAmplitudes.end(), 0.0);
460  float clusterChargeError = sqrt(
461  inner_product(stripAmplitudeErrors.begin(), stripAmplitudeErrors.end(),
462  stripAmplitudeErrors.begin(), 0.0)
463  );
464  float clusterSN = (clusterChargeError > 0) ? clusterCharge / clusterChargeError : clusterCharge;
465  clusterChi2 /= clusterSize;
466  // Cluster seed
467  size_t seedIndex = distance(stripAmplitudes.begin(), max_element(
468  stripAmplitudes.begin(), stripAmplitudes.end()));
469  //unsigned short clusterSeedStrip = stripNumbers[seedIndex];
470  float clusterSeedCharge = stripAmplitudes[seedIndex];
471  B2DEBUG(200, "Cluster parameters:");
472  B2DEBUG(200, "Charge: " << clusterCharge << " +/- " << clusterChargeError);
473  B2DEBUG(200, "Seed: " << clusterSeedCharge << " +/- " << stripAmplitudeErrors[seedIndex]);
474  B2DEBUG(200, "S/N: " << clusterSN);
475 
476 
477  // Hit position
478  float clusterPosition, clusterPositionError;
479 
480  // Now censoring:
481  // NB: We don't censor strip charges again. The requirement of 3 consecutive samples
482  // above threshold is a good enough safeguard.
483  if ((clusterCharge < clusterNoise * m_cutCluster) || (clusterSeedCharge < clusterNoise * m_cutSeed))
484  continue;
485 
486  if (clusterSize < m_sizeHeadTail) { // centre of gravity
487  clusterPosition = 1.0 / clusterCharge * inner_product(
488  stripAmplitudes.begin(), stripAmplitudes.end(), stripPositions.begin(), 0.0
489  );
490  // Strip charge equal to zero-suppression threshold
491  float phantomCharge = m_cutAdjacent * clusterNoise;
492  if (clusterSize == 1) {
493  clusterPositionError = pitch * phantomCharge / (clusterCharge + phantomCharge);
494  } else {
495  clusterPositionError = pitch * phantomCharge / clusterCharge;
496  }
497  } else { // Head-tail
498  float leftStripCharge = stripAmplitudes.front();
499  float leftPos = stripPositions.front();
500  float rightStripCharge = stripAmplitudes.back();
501  float rightPos = stripPositions.back();
502  float centreCharge = (clusterCharge - leftStripCharge - rightStripCharge) / (clusterSize - 2);
503  leftStripCharge = (leftStripCharge < centreCharge) ? leftStripCharge : centreCharge;
504  rightStripCharge = (rightStripCharge < centreCharge) ? rightStripCharge : centreCharge;
505  clusterPosition = 0.5 * (leftPos + rightPos)
506  + 0.5 * (rightStripCharge - leftStripCharge) / centreCharge * pitch;
507  float sn = centreCharge / m_cutAdjacent / clusterNoise;
508  // Rough estimates of Landau noise
509  float landauHead = leftStripCharge / centreCharge;
510  double landauTail = rightStripCharge / centreCharge;
511  clusterPositionError = 0.5 * pitch * sqrt(1.0 / sn / sn
512  + 0.5 * landauHead * landauHead + 0.5 * landauTail * landauTail);
513  }
514  //Lorentz shift correction
515  clusterPosition -= info.getLorentzShift(isU, clusterPosition);
516 
517  // Finally, we save the cluster and its relations.
518  map<unsigned int, float> mc_relations;
519  map<unsigned int, float> truehit_relations;
520  vector<pair<unsigned int, float> > digit_weights;
521  digit_weights.reserve(clusterSize);
522 
523  for (size_t iDigit = clusterBounds.first; iDigit < clusterBounds.second; ++iDigit) {
524  // Store relations to MCParticles and SVDTrueHits
525  fillRelationMap(m_mcRelation, mc_relations, iDigit);
526  fillRelationMap(m_trueRelation, truehit_relations, iDigit);
527  //Add digit to the Cluster->Digit relation list
528  digit_weights.emplace_back(iDigit, stripAmplitudes[iDigit - clusterBounds.first]);
529  }
530 
531  //Store the cluster into Datastore ...
532  int clsIndex = storeClusters.getEntries();
533  VxdID clusterSensorID = sensorID;
534  clusterSensorID.setSegmentNumber(0);
535  storeClusters.appendNew(
536  SVDCluster(sensorID, isU, clusterPosition, clusterPositionError, clusterTime,
537  clusterTimeErr, clusterCharge, clusterSeedCharge, clusterSize, clusterSN, clusterChi2)
538  );
539 
540  //Create relations to the cluster
541  if (!mc_relations.empty()) {
542  relClusterMCParticle.add(clsIndex, mc_relations.begin(), mc_relations.end());
543  }
544  if (!truehit_relations.empty()) {
545  relClusterTrueHit.add(clsIndex, truehit_relations.begin(), truehit_relations.end());
546  }
547  relClusterShaperDigit.add(clsIndex, digit_weights.begin(), digit_weights.end());
548  } // CYCLE OVER CLUSTERS for stripGroups
549 
550  } // CYCLE OVER SENSORS for items in sensorDigits
551 
552  B2DEBUG(100, "Number of clusters: " << storeClusters.getEntries());
553 
554 } // event()
555 
556 
Belle2::StoreArray::appendNew
T * appendNew()
Construct a new T object at the end of the array.
Definition: StoreArray.h:256
Belle2::RelationArray::clear
void clear() override
Clear all elements from the relation.
Definition: RelationArray.h:279
Belle2::RelationArray
Low-level class to create/modify relations between StoreArrays.
Definition: RelationArray.h:72
Belle2::VxdID
Class to uniquely identify a any structure of the PXD and SVD.
Definition: VxdID.h:43
Belle2::SVDModeByte::getTriggerBin
baseType getTriggerBin() const
Get the triggerBin id.
Definition: SVDModeByte.h:150
Belle2::SVDShaperDigit::getSamples
APVFloatSamples getSamples() const
Get array of samples.
Definition: SVDShaperDigit.h:137
Belle2::SVD::SVDClusterizerDirectModule::RelationLookup
std::vector< const RelationElement * > RelationLookup
Container for a RelationArray Lookup table.
Definition: SVDClusterizerDirectModule.h:52
REG_MODULE
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:652
Belle2::SVDShaperDigit::getSensorID
VxdID getSensorID() const
Get the sensor ID.
Definition: SVDShaperDigit.h:116
Belle2::StoreAccessorBase::isOptional
bool isOptional(const std::string &name="")
Tell the DataStore about an optional input.
Definition: StoreAccessorBase.h:95
Belle2::StoreAccessorBase::getName
const std::string & getName() const
Return name under which the object is saved in the DataStore.
Definition: StoreAccessorBase.h:130
Belle2::SVDModeByte
Class to store SVD mode information.
Definition: SVDModeByte.h:79
Belle2::SVD::SensorInfo
Specific implementation of SensorInfo for SVD Sensors which provides additional sensor specific infor...
Definition: SensorInfo.h:35
Belle2::StoreAccessorBase::registerInDataStore
bool registerInDataStore(DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut)
Register the object/array in the DataStore.
Definition: StoreAccessorBase.h:54
Belle2::SVD::SVDClusterizerDirectModule
SVD Direct Clusterizer.
Definition: SVDClusterizerDirectModule.h:47
Belle2::SVD::NNWaveFitTool::shiftInTime
void shiftInTime(nnFitterBinData &p, double timeShift)
Shift the probability array in time.
Definition: NNWaveFitTool.cc:54
Belle2::SVDShaperDigit
The SVD ShaperDigit class.
Definition: SVDShaperDigit.h:46
Belle2::DBObjPtr
Class for accessing objects in the database.
Definition: DBObjPtr.h:31
Belle2::Module
Base class for Modules.
Definition: Module.h:74
Belle2::RelationElement
Class to store a single element of a relation.
Definition: RelationElement.h:33
Belle2::StoreArray::clear
void clear() override
Delete all entries in this array.
Definition: StoreArray.h:217
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::SVD::apvSamples
std::array< apvSampleBaseType, nAPVSamples > apvSamples
vector od apvSample BaseType objects
Definition: SVDSimulationTools.h:41
Belle2::SVD::pass3Samples
bool pass3Samples(const T &a, double thr)
pass 3-samples
Definition: SVDSimulationTools.h:143
Belle2::VxdID::setSegmentNumber
void setSegmentNumber(baseType segment)
Set the sensor segment.
Definition: VxdID.h:123
Belle2::SVD
Namespace to encapsulate code needed for simulation and reconstrucion of the SVD.
Definition: GeoSVDCreator.h:35
Belle2::RelationArray::add
void add(index_type from, index_type to, weight_type weight=1.0)
Add a new element to the relation.
Definition: RelationArray.h:291
Belle2::SVD::NNWaveFitTool::multiply
void multiply(nnFitterBinData &p, const nnFitterBinData &p1)
Multiply probabilities.
Definition: NNWaveFitTool.h:131
Belle2::SVD::NNWaveFitTool::getAmplitudeChi2
std::tuple< double, double, double > getAmplitudeChi2(const apvSamples &samples, double timeShift, double tau)
Return std::tuple with signal amplitude, its error, and chi2 of the fit.
Definition: NNWaveFitTool.cc:29
Belle2::SVDCluster
The SVD Cluster class This class stores all information about reconstructed SVD clusters.
Definition: SVDCluster.h:38
Belle2::SVDShaperDigit::getCellID
short int getCellID() const
Get strip ID.
Definition: SVDShaperDigit.h:132
Belle2::StoreArray< SVDCluster >
Belle2::SVD::NNWaveFitTool
The class holds arrays of bins and bin centers, and a wave generator object containing information on...
Definition: NNWaveFitTool.h:93
Belle2::SVD::nnFitterBinData
std::vector< double > nnFitterBinData
Vector of values defined for bins, such as bin times or bin probabilities.
Definition: NNWaveFitTool.h:32
Belle2::SVDShaperDigit::isUStrip
bool isUStrip() const
Get strip direction.
Definition: SVDShaperDigit.h:127
Belle2::StoreArray::getEntries
int getEntries() const
Get the number of objects in the array.
Definition: StoreArray.h:226
Belle2::SVD::NNWaveFitTool::getTimeShift
std::tuple< double, double > getTimeShift(const nnFitterBinData &p)
Return std::tuple containing time shift and its error.
Definition: NNWaveFitTool.cc:18