11 #include <svd/modules/svdReconstruction/SVDClusterizerDirectModule.h>
13 #include <framework/datastore/StoreArray.h>
14 #include <framework/logging/Logger.h>
16 #include <vxd/geometry/GeoCache.h>
17 #include <svd/geometry/SensorInfo.h>
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>
25 #include <svd/reconstruction/NNWaveFitTool.h>
27 #include <unordered_map>
48 B2DEBUG(200,
"SVDClusterizerDirectModule ctor");
50 setDescription(
"Clusterize SVDShaperDigits and reconstruct hits");
51 setPropertyFlags(c_ParallelProcessingCertified);
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(
""));
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));
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);
83 void SVDClusterizerDirectModule::initialize()
91 storeClusters.registerInDataStore();
92 storeShaperDigits.isRequired();
93 storeTrueHits.isOptional();
94 storeMCParticles.isOptional();
95 m_storeSVDEvtInfo.isRequired();
97 if (!m_storeSVDEvtInfo.isOptional(m_svdEventInfoName)) m_svdEventInfoName =
"SVDEventInfoSim";
98 m_storeSVDEvtInfo.isRequired(m_svdEventInfoName);
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);
114 m_storeClustersName = storeClusters.getName();
115 m_storeShaperDigitsName = storeShaperDigits.getName();
116 m_storeTrueHitsName = storeTrueHits.getName();
117 m_storeMCParticlesName = storeMCParticles.getName();
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();
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);
147 m_fitter.setNetwrok(dbXml->m_data);
150 void SVDClusterizerDirectModule::createRelationLookup(
const RelationArray& relation,
155 if (!relation)
return;
157 lookup.resize(digits);
158 for (
const auto& element : relation) {
159 lookup[element.getFromIndex()] = &element;
164 std::map<unsigned int, float>& relation,
unsigned int index)
167 if (!lookup.empty() && lookup[index]) {
169 const unsigned int size = element.getSize();
171 for (
unsigned int i = 0; i < size; ++i) {
174 if (element.getWeight(i) < 0)
continue;
175 relation[element.getToIndex(i)] += element.getWeight(i);
180 void SVDClusterizerDirectModule::event()
185 if (!storeShaperDigits || !storeShaperDigits.
getEntries() || !m_storeSVDEvtInfo.isValid())
return;
187 SVDModeByte modeByte = m_storeSVDEvtInfo->getModeByte();
189 size_t nDigits = storeShaperDigits.
getEntries();
190 B2DEBUG(90,
"Initial size of StoreDigits array: " << nDigits);
195 RelationArray relShaperDigitMCParticle(storeShaperDigits, storeMCParticles, m_relShaperDigitMCParticleName);
196 RelationArray relShaperDigitTrueHit(storeShaperDigits, storeTrueHits, m_relShaperDigitTrueHitName);
199 storeClusters.
clear();
201 RelationArray relClusterMCParticle(storeClusters, storeMCParticles,
202 m_relClusterMCParticleName);
203 if (relClusterMCParticle) relClusterMCParticle.
clear();
205 RelationArray relClusterShaperDigit(storeClusters, storeShaperDigits,
206 m_relClusterShaperDigitName);
207 if (relClusterShaperDigit) relClusterShaperDigit.
clear();
209 RelationArray relClusterTrueHit(storeClusters, storeTrueHits,
210 m_relClusterTrueHitName);
211 if (relClusterTrueHit) relClusterTrueHit.
clear();
214 createRelationLookup(relShaperDigitMCParticle, m_mcRelation, nDigits);
215 createRelationLookup(relShaperDigitTrueHit, m_trueRelation, nDigits);
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) {
228 if (sensorID != lastSensorID) {
229 sensorDigits.push_back(make_pair(firstSensorDigit, iDigit));
230 firstSensorDigit = iDigit;
231 lastSensorID = sensorID;
235 sensorDigits.push_back(make_pair(firstSensorDigit, nDigits));
238 for (
auto id_indices : sensorDigits) {
240 unsigned int firstDigit = id_indices.first;
241 unsigned int lastDigit = id_indices.second;
243 const SVDShaperDigit& sampleDigit = *storeShaperDigits[firstDigit];
249 double pitch = isU ? info.getUPitch() : info.getVPitch();
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;
267 B2DEBUG(300,
"Clustering digits " << firstDigit <<
" to " << lastDigit);
268 for (
size_t iDigit = firstDigit; iDigit < lastDigit; ++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);
275 bool validDigit =
true;
276 float stripNoiseADU = m_noiseCal.getNoise(sensorID, isU, currentStrip);
282 transform(samples.begin(), samples.end(), normedSamples.begin(),
283 bind2nd(divides<float>(), stripNoiseADU));
284 validDigit = validDigit &&
pass3Samples(normedSamples, m_cutAdjacent);
291 zeroSuppress(normedSamples, m_cutAdjacent);
292 storedNormedSamples.insert(make_pair(iDigit, normedSamples));
296 bool consecutive = ((currentStrip - lastStrip) == 1);
297 lastStrip = currentStrip;
299 B2DEBUG(300, (validDigit ?
"Valid " :
"Invalid ") << (consecutive ?
"consecutive" :
"gap"));
302 if ((!validDigit || !consecutive) && (firstClusterDigit < lastClusterDigit)) {
303 B2DEBUG(300,
"Saving (" << firstClusterDigit <<
", " << lastClusterDigit <<
")");
304 stripGroups.emplace_back(firstClusterDigit, lastClusterDigit);
310 lastClusterDigit = iDigit + 1;
312 firstClusterDigit = iDigit;
313 lastClusterDigit = iDigit + 1;
316 firstClusterDigit = iDigit + 1;
317 lastClusterDigit = iDigit + 1;
321 if (firstClusterDigit < lastClusterDigit) {
322 B2DEBUG(300,
"Saving (" << firstClusterDigit <<
", " << lastDigit <<
")");
323 stripGroups.emplace_back(firstClusterDigit, lastDigit);
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,
" "));
333 os <<
"StripGroups: " << endl;
334 for (
auto item : stripGroups) {
335 os <<
"(" << item.first <<
", " << item.second <<
"), ";
337 B2DEBUG(300, os.str());
345 vector<unsigned short> stripNumbers;
346 vector<float> stripPositions;
347 vector<float> stripNoises;
348 vector<float> timeShifts;
349 vector<float> waveWidths;
351 for (
auto clusterBounds : stripGroups) {
353 unsigned short clusterSize = clusterBounds.second - clusterBounds.first;
354 assert(clusterSize > 0);
356 stripNumbers.clear();
357 stripPositions.clear();
362 for (
size_t iDigit = clusterBounds.first; iDigit < clusterBounds.second; ++iDigit) {
368 unsigned short stripNo = digit.
getCellID();
369 stripNumbers.push_back(stripNo);
371 double stripNoiseADU = m_noiseCal.getNoise(sensorID, isU, stripNo);
372 stripNoises.push_back(
373 m_pulseShapeCal.getChargeFromADC(sensorID, isU, stripNo, stripNoiseADU)
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)
386 const double triggerBinSep = 4 * 1.96516;
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)
394 stripPositions.push_back(
395 isU ? info.getUCellPosition(stripNo) : info.getVCellPosition(stripNo)
401 float clusterNoise = sqrt(
403 * inner_product(stripNoises.begin(), stripNoises.end(), stripNoises.begin(), 0.0)
405 B2DEBUG(200,
"RMS cluster noise: " << clusterNoise);
409 shared_ptr<nnFitterBinData> pStrip;
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;
416 os1 <<
"Input to NNFitter: iDigit = " << iDigit << endl <<
"Samples: ";
417 copy(storedNormedSamples[iDigit].begin(), storedNormedSamples[iDigit].end(),
418 ostream_iterator<double>(os1,
" "));
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,
" "));
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());
432 double clusterTime, clusterTimeErr;
433 tie(clusterTime, clusterTimeErr) = fitTool.
getTimeShift(pCluster);
434 B2DEBUG(200,
"Time: " << clusterTime <<
" +/- " << clusterTimeErr);
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]);
447 stripAmplitudes[iClusterStrip] =
448 stripNoises[iClusterStrip] * snAmp;
449 stripAmplitudeErrors[iClusterStrip] =
450 stripNoises[iClusterStrip] * snAmpError;
452 B2DEBUG(200,
"Digit " << iDigit <<
" Noise: " << stripNoises[iClusterStrip]
453 <<
" Amplitude: " << stripAmplitudes[iClusterStrip]
454 <<
" +/- " << stripAmplitudeErrors[iClusterStrip]
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)
464 float clusterSN = (clusterChargeError > 0) ? clusterCharge / clusterChargeError : clusterCharge;
465 clusterChi2 /= clusterSize;
467 size_t seedIndex = distance(stripAmplitudes.begin(), max_element(
468 stripAmplitudes.begin(), stripAmplitudes.end()));
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);
478 float clusterPosition, clusterPositionError;
483 if ((clusterCharge < clusterNoise * m_cutCluster) || (clusterSeedCharge < clusterNoise * m_cutSeed))
486 if (clusterSize < m_sizeHeadTail) {
487 clusterPosition = 1.0 / clusterCharge * inner_product(
488 stripAmplitudes.begin(), stripAmplitudes.end(), stripPositions.begin(), 0.0
491 float phantomCharge = m_cutAdjacent * clusterNoise;
492 if (clusterSize == 1) {
493 clusterPositionError = pitch * phantomCharge / (clusterCharge + phantomCharge);
495 clusterPositionError = pitch * phantomCharge / clusterCharge;
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;
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);
515 clusterPosition -= info.getLorentzShift(isU, clusterPosition);
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);
523 for (
size_t iDigit = clusterBounds.first; iDigit < clusterBounds.second; ++iDigit) {
525 fillRelationMap(m_mcRelation, mc_relations, iDigit);
526 fillRelationMap(m_trueRelation, truehit_relations, iDigit);
528 digit_weights.emplace_back(iDigit, stripAmplitudes[iDigit - clusterBounds.first]);
533 VxdID clusterSensorID = sensorID;
536 SVDCluster(sensorID, isU, clusterPosition, clusterPositionError, clusterTime,
537 clusterTimeErr, clusterCharge, clusterSeedCharge, clusterSize, clusterSN, clusterChi2)
541 if (!mc_relations.empty()) {
542 relClusterMCParticle.
add(clsIndex, mc_relations.begin(), mc_relations.end());
544 if (!truehit_relations.empty()) {
545 relClusterTrueHit.
add(clsIndex, truehit_relations.begin(), truehit_relations.end());
547 relClusterShaperDigit.
add(clsIndex, digit_weights.begin(), digit_weights.end());
552 B2DEBUG(100,
"Number of clusters: " << storeClusters.
getEntries());