Belle II Software  release-05-02-19
SVDNNShapeReconstructorModule.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/SVDNNShapeReconstructorModule.h>
12 
13 #include <framework/datastore/DataStore.h>
14 #include <framework/datastore/StoreArray.h>
15 #include <framework/logging/Logger.h>
16 
17 #include <mdst/dataobjects/MCParticle.h>
18 #include <svd/dataobjects/SVDTrueHit.h>
19 #include <svd/dataobjects/SVDShaperDigit.h>
20 #include <svd/dataobjects/SVDRecoDigit.h>
21 #include <mva/dataobjects/DatabaseRepresentationOfWeightfile.h>
22 
23 #include <svd/reconstruction/NNWaveFitTool.h>
24 
25 #include <algorithm>
26 #include <functional>
27 
28 using namespace std;
29 using namespace Belle2;
30 using namespace Belle2::SVD;
31 
32 //-----------------------------------------------------------------
33 // Register the Module
34 //-----------------------------------------------------------------
35 REG_MODULE(SVDNNShapeReconstructor)
36 
37 //-----------------------------------------------------------------
38 // Implementation
39 //-----------------------------------------------------------------
40 
42 {
43  B2DEBUG(200, "Now in SVDNNShapeReconstructorModule ctor");
44  //Set module properties
45  setDescription("Reconstruct signals on SVD strips.");
46  setPropertyFlags(c_ParallelProcessingCertified);
47 
48  // 1. Collections.
49  addParam("Digits", m_storeShaperDigitsName,
50  "Shaperdigits collection name", string(""));
51  addParam("RecoDigits", m_storeRecoDigitsName,
52  "RecoDigits collection name", string(""));
53  addParam("TrueHits", m_storeTrueHitsName,
54  "TrueHits collection name", string(""));
55  addParam("MCParticles", m_storeMCParticlesName,
56  "MCParticles collection name", string(""));
57  addParam("WriteRecoDigits", m_writeRecoDigits,
58  "Write RecoDigits to output?", m_writeRecoDigits);
59  addParam("SVDEventInfo", m_svdEventInfoName,
60  "SVDEventInfo name", string(""));
61  // 2. Calibration and time fitter sources
62  addParam("TimeFitterName", m_timeFitterName,
63  "Name of time fitter data file", string("SVDTimeNet_6samples"));
64  addParam("CalibratePeak", m_calibratePeak, "Use calibrattion (vs. default) for peak widths and positions", bool(false));
65  // 3. Zero suppression
66  addParam("ZeroSuppressionCut", m_cutAdjacent, "Zero-suppression cut on digits",
67  m_cutAdjacent);
68 }
69 
70 void SVDNNShapeReconstructorModule::initialize()
71 {
72  //Register collections
73  StoreArray<SVDRecoDigit> storeRecoDigits(m_storeRecoDigitsName);
74  StoreArray<SVDShaperDigit> storeShaperDigits(m_storeShaperDigitsName);
75  StoreArray<SVDTrueHit> storeTrueHits(m_storeTrueHitsName);
76  StoreArray<MCParticle> storeMCParticles(m_storeMCParticlesName);
77 
78  if (!m_writeRecoDigits)
79  storeRecoDigits.registerInDataStore(DataStore::c_DontWriteOut);
80  else
81  storeRecoDigits.registerInDataStore();
82 
83  storeShaperDigits.isRequired();
84  storeTrueHits.isOptional();
85  storeMCParticles.isOptional();
86  m_storeSVDEvtInfo.isRequired();
87 
88  if (!m_storeSVDEvtInfo.isOptional(m_svdEventInfoName)) m_svdEventInfoName = "SVDEventInfoSim";
89  m_storeSVDEvtInfo.isRequired(m_svdEventInfoName);
90 
91  RelationArray relRecoDigitShaperDigits(storeRecoDigits, storeShaperDigits);
92  RelationArray relRecoDigitTrueHits(storeRecoDigits, storeTrueHits);
93  RelationArray relRecoDigitMCParticles(storeRecoDigits, storeMCParticles);
94  RelationArray relShaperDigitTrueHits(storeShaperDigits, storeTrueHits);
95  RelationArray relShaperDigitMCParticles(storeShaperDigits, storeMCParticles);
96 
97  if (!m_writeRecoDigits)
98  relRecoDigitShaperDigits.registerInDataStore(DataStore::c_DontWriteOut);
99  else
100  relRecoDigitShaperDigits.registerInDataStore();
101  //Relations to simulation objects only if the ancestor relations exist
102  if (relShaperDigitTrueHits.isOptional())
103  relRecoDigitTrueHits.registerInDataStore();
104  if (relShaperDigitMCParticles.isOptional())
105  relRecoDigitMCParticles.registerInDataStore();
106 
107  //Store names to speed up creation later
108  m_storeRecoDigitsName = storeRecoDigits.getName();
109  m_storeShaperDigitsName = storeShaperDigits.getName();
110  m_storeTrueHitsName = storeTrueHits.getName();
111  m_storeMCParticlesName = storeMCParticles.getName();
112 
113  m_relRecoDigitShaperDigitName = relRecoDigitShaperDigits.getName();
114  m_relRecoDigitTrueHitName = relRecoDigitTrueHits.getName();
115  m_relRecoDigitMCParticleName = relRecoDigitMCParticles.getName();
116  m_relShaperDigitTrueHitName = relShaperDigitTrueHits.getName();
117  m_relShaperDigitMCParticleName = relShaperDigitMCParticles.getName();
118 
119  B2INFO(" 1. COLLECTIONS:");
120  B2INFO(" --> MCParticles: " << m_storeMCParticlesName);
121  B2INFO(" --> Digits: " << m_storeShaperDigitsName);
122  B2INFO(" --> RecoDigits: " << m_storeRecoDigitsName);
123  B2INFO(" --> TrueHits: " << m_storeTrueHitsName);
124  B2INFO(" --> DigitMCRel: " << m_relShaperDigitMCParticleName);
125  B2INFO(" --> RecoDigitMCRel: " << m_relRecoDigitMCParticleName);
126  B2INFO(" --> RecoDigitDigitRel: " << m_relRecoDigitShaperDigitName);
127  B2INFO(" --> DigitTrueRel: " << m_relShaperDigitTrueHitName);
128  B2INFO(" --> RecoDigitTrueRel: " << m_relRecoDigitTrueHitName);
129  B2INFO(" --> Save RecoDigits? " << (m_writeRecoDigits ? "Y" : "N"));
130  B2INFO(" 2. CALIBRATION:");
131  B2INFO(" --> Time NN: " << m_timeFitterName);
132 
133  // Properly initialize the NN time fitter
134  // FIXME: Should be moved to beginRun
135  // FIXME: No support for 3/6 sample switching within a run/event
136  DBObjPtr<DatabaseRepresentationOfWeightfile> dbXml(m_timeFitterName);
137  m_fitter.setNetwrok(dbXml->m_data);
138 }
139 
140 void SVDNNShapeReconstructorModule::createRelationLookup(const RelationArray& relation,
141  RelationLookup& lookup, size_t digits)
142 {
143  lookup.clear();
144  //If we don't have a relation we don't build a lookuptable
145  if (!relation) return;
146  //Resize to number of digits and set all values
147  lookup.resize(digits);
148  for (const auto& element : relation) {
149  lookup[element.getFromIndex()] = &element;
150  }
151 }
152 
153 void SVDNNShapeReconstructorModule::fillRelationMap(const RelationLookup& lookup,
154  std::map<unsigned int, float>& relation, unsigned int index)
155 {
156  //If the lookup table is not empty and the element is set
157  if (!lookup.empty() && lookup[index]) {
158  const RelationElement& element = *lookup[index];
159  const unsigned int size = element.getSize();
160  //Add all Relations to the map
161  for (unsigned int i = 0; i < size; ++i) {
162  //negative weights are from ignored particles, we don't like them and
163  //thus ignore them :D
164  if (element.getWeight(i) < 0) continue;
165  relation[element.getToIndex(i)] += element.getWeight(i);
166  }
167  }
168 }
169 
170 void SVDNNShapeReconstructorModule::event()
171 {
172 
173  const StoreArray<SVDShaperDigit> storeShaperDigits(m_storeShaperDigitsName);
174  // If no digits or no SVDEventInfo, nothing to do
175  if (!storeShaperDigits || !storeShaperDigits.getEntries() || !m_storeSVDEvtInfo.isValid()) return;
176 
177  SVDModeByte modeByte = m_storeSVDEvtInfo->getModeByte();
178 
179  size_t nDigits = storeShaperDigits.getEntries();
180  B2DEBUG(90, "Initial size of StoreDigits array: " << nDigits);
181 
182  const StoreArray<MCParticle> storeMCParticles(m_storeMCParticlesName);
183  const StoreArray<SVDTrueHit> storeTrueHits(m_storeTrueHitsName);
184 
185  RelationArray relShaperDigitMCParticle(storeShaperDigits, storeMCParticles, m_relShaperDigitMCParticleName);
186  RelationArray relShaperDigitTrueHit(storeShaperDigits, storeTrueHits, m_relShaperDigitTrueHitName);
187 
188  StoreArray<SVDRecoDigit> storeRecoDigits(m_storeRecoDigitsName);
189  storeRecoDigits.clear();
190 
191  RelationArray relRecoDigitMCParticle(storeRecoDigits, storeMCParticles,
192  m_relRecoDigitMCParticleName);
193  if (relRecoDigitMCParticle) relRecoDigitMCParticle.clear();
194 
195  RelationArray relRecoDigitShaperDigit(storeRecoDigits, storeShaperDigits,
196  m_relRecoDigitShaperDigitName);
197  if (relRecoDigitShaperDigit) relRecoDigitShaperDigit.clear();
198 
199  RelationArray relRecoDigitTrueHit(storeRecoDigits, storeTrueHits,
200  m_relRecoDigitTrueHitName);
201  if (relRecoDigitTrueHit) relRecoDigitTrueHit.clear();
202 
203  //Build lookup tables for relations
204  createRelationLookup(relShaperDigitMCParticle, m_mcRelation, nDigits);
205  createRelationLookup(relShaperDigitTrueHit, m_trueRelation, nDigits);
206 
207  // Create fit tool object
208  NNWaveFitTool fitTool = m_fitter.getFitTool();
209 
210  // I. Group digits by sensor/side.
211  vector<pair<unsigned short, unsigned short> > sensorDigits;
212  VxdID lastSensorID(0);
213  size_t firstSensorDigit = 0;
214  for (size_t iDigit = 0; iDigit < nDigits; ++iDigit) {
215  const SVDShaperDigit& digit = *storeShaperDigits[iDigit];
216  VxdID sensorID = digit.getSensorID();
217  sensorID.setSegmentNumber(digit.isUStrip() ? 1 : 0);
218  if (sensorID != lastSensorID) { // we have a new sensor side
219  sensorDigits.push_back(make_pair(firstSensorDigit, iDigit));
220  firstSensorDigit = iDigit;
221  lastSensorID = sensorID;
222  }
223  }
224  // save last VxdID
225  sensorDigits.push_back(make_pair(firstSensorDigit, nDigits));
226 
227  // ICYCLE OVER SENSORS
228  for (auto id_indices : sensorDigits) {
229  // Retrieve parameters from sensorDigits
230  unsigned int firstDigit = id_indices.first;
231  unsigned int lastDigit = id_indices.second;
232  // Get VXDID and side from the first digit
233  const SVDShaperDigit& exampleDigit = *storeShaperDigits[firstDigit];
234  VxdID sensorID = exampleDigit.getSensorID();
235  bool isU = exampleDigit.isUStrip();
236 
237  // 2. Cycle through digits and form recodigits on the way.
238 
239  B2DEBUG(300, "Reconstructing digits " << firstDigit << " to " << lastDigit);
240  for (size_t iDigit = firstDigit; iDigit < lastDigit; ++iDigit) {
241 
242  const SVDShaperDigit& shaperDigit = *storeShaperDigits[iDigit];
243  unsigned short stripNo = shaperDigit.getCellID();
244  bool validDigit = true; // FIXME: We don't care about local run bad strips for now.
245  const double triggerBinSep = 4 * 1.96516; //in ns
246  double apvPhase = triggerBinSep * (0.5 + static_cast<int>(modeByte.getTriggerBin()));
247  // Get things from the database.
248  // Noise is good as it comes.
249  float stripNoiseADU = m_noiseCal.getNoise(sensorID, isU, stripNo);
250  // Some calibrations magic.
251  // FIXME: Only use calibration on real data. Until simulations correspond to
252  // default calibrtion, we cannot use it.
253  double stripSignalWidth = 270;
254  double stripT0 = isU ? 2.5 : -2.2;
255  if (m_calibratePeak) {
256  stripSignalWidth = 1.988 * m_pulseShapeCal.getWidth(sensorID, isU, stripNo);
257  stripT0 = m_pulseShapeCal.getPeakTime(sensorID, isU, stripNo)
258  - 0.25 * stripSignalWidth;
259  }
260 
261  B2DEBUG(300, "Strip parameters: stripNoiseADU: " << stripNoiseADU <<
262  " Width: " << stripSignalWidth <<
263  " T0: " << stripT0);
264 
265  // If the strip is not masked away, normalize samples (sample/stripNoise)
266  apvSamples normedSamples;
267  if (validDigit) {
268  auto samples = shaperDigit.getSamples();
269  transform(samples.begin(), samples.end(), normedSamples.begin(),
270  bind2nd(divides<float>(), stripNoiseADU));
271  // FIXME: This won't work in 3 sample mode, we have no control over the number of non-zero samples.
272  validDigit = validDigit && pass3Samples(normedSamples, m_cutAdjacent);
273  }
274 
275  if (validDigit) {
276  zeroSuppress(normedSamples, m_cutAdjacent);
277  } else // only now we give up on the diigit
278  continue;
279 
280  // 3. Now we create and save the RecoDigit
281 
282  ostringstream os;
283  os << "Input to NNFitter: iDigit = " << iDigit << endl << "Samples: ";
284  copy(normedSamples.begin(), normedSamples.end(), ostream_iterator<double>(os, " "));
285  os << endl;
286  std::shared_ptr<nnFitterBinData> pStrip = m_fitter.getFit(normedSamples, stripSignalWidth);
287  os << "Output from NNWaveFitter: " << endl;
288  copy(pStrip->begin(), pStrip->end(), ostream_iterator<double>(os, " "));
289  os << endl;
290  // Apply strip time shift to pdf
291  fitTool.shiftInTime(*pStrip, -apvPhase - stripT0);
292  B2DEBUG(200, os.str());
293  // Calculate time and its error, amplitude and its error, and chi2
294  double stripTime, stripTimeError;
295  tie(stripTime, stripTimeError) = fitTool.getTimeShift(*pStrip);
296  // Now we have the cluster time pdf, so we can calculate amplitudes.
297  double stripAmplitude, stripAmplitudeError, stripChi2;
298  tie(stripAmplitude, stripAmplitudeError, stripChi2) =
299  fitTool.getAmplitudeChi2(normedSamples, stripTime, stripSignalWidth);
300  //De-normalize amplitudes and convert to electrons.
301  stripAmplitude = m_pulseShapeCal.getChargeFromADC(sensorID, isU, stripNo, stripAmplitude * stripNoiseADU);
302  stripAmplitudeError = m_pulseShapeCal.getChargeFromADC(sensorID, isU, stripNo, stripAmplitudeError * stripNoiseADU);
303  B2DEBUG(200, "RecoDigit " << iDigit << " Noise: " << m_pulseShapeCal.getChargeFromADC(sensorID, isU, stripNo, stripNoiseADU)
304  << " Time: " << stripTime << " +/- " << stripTimeError
305  << " Amplitude: " << stripAmplitude << " +/- " << stripAmplitudeError
306  << " Chi2: " << stripChi2
307  );
308 
309  // Finally, we save the RecoDigit and its relations.
310  map<unsigned int, float> mc_relations;
311  map<unsigned int, float> truehit_relations;
312  vector<pair<unsigned int, float> > digit_weights;
313  digit_weights.reserve(1);
314 
315  // Store relations to MCParticles and SVDTrueHits
316  fillRelationMap(m_mcRelation, mc_relations, iDigit);
317  fillRelationMap(m_trueRelation, truehit_relations, iDigit);
318  //Add digit to the RecoDigit->ShaperDigit relation list
319  digit_weights.emplace_back(iDigit, 1.0);
320 
321  //Store the RecoDigit into Datastore ...
322  int recoDigitIndex = storeRecoDigits.getEntries();
323  storeRecoDigits.appendNew(
324  SVDRecoDigit(sensorID, isU, shaperDigit.getCellID(), stripAmplitude,
325  stripAmplitudeError, stripTime, stripTimeError, *pStrip, stripChi2,
326  modeByte)
327  );
328 
329  //Create relations to RecoDigits
330  if (!mc_relations.empty()) {
331  relRecoDigitMCParticle.add(recoDigitIndex, mc_relations.begin(), mc_relations.end());
332  }
333  if (!truehit_relations.empty()) {
334  relRecoDigitTrueHit.add(recoDigitIndex, truehit_relations.begin(), truehit_relations.end());
335  }
336  relRecoDigitShaperDigit.add(recoDigitIndex, digit_weights.begin(), digit_weights.end());
337  } // CYCLE OVER SHAPERDIGITS
338 
339  } // CYCLE OVER SENSORS for items in sensorDigits
340 
341  B2DEBUG(100, "Number of RecoDigits: " << storeRecoDigits.getEntries());
342 
343 } // event()
344 
345 
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::SVD::SVDNNShapeReconstructorModule
The SVD NNShapeReconstructor.
Definition: SVDNNShapeReconstructorModule.h:45
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
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::StoreAccessorBase::registerInDataStore
bool registerInDataStore(DataStore::EStoreFlags storeFlags=DataStore::c_WriteOut)
Register the object/array in the DataStore.
Definition: StoreAccessorBase.h:54
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::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::SVDShaperDigit::getCellID
short int getCellID() const
Get strip ID.
Definition: SVDShaperDigit.h:132
Belle2::StoreArray
Accessor to arrays stored in the data store.
Definition: ECLMatchingPerformanceExpertModule.h:33
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::SVDRecoDigit
The SVD RecoDigit class.
Definition: SVDRecoDigit.h:54
Belle2::SVDShaperDigit::isUStrip
bool isUStrip() const
Get strip direction.
Definition: SVDShaperDigit.h:127
Belle2::SVD::SVDNNShapeReconstructorModule::RelationLookup
std::vector< const RelationElement * > RelationLookup
Container for a RelationArray Lookup table.
Definition: SVDNNShapeReconstructorModule.h:50
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