Belle II Software  release-05-01-25
ECLSplitterN1Module.cc
1 /**************************************************************************
2  * BASF2 (Belle Analysis Framework 2) *
3  * Copyright(C) 2016 - Belle II Collaboration *
4  * *
5  * Main reconstruction splitter code for the n photon hypothesis. *
6  * Based on a connected region (CR) we look for local maxima and *
7  * create one shower for each local maximum (LM). In case of multiple *
8  * LM in one CR the energy is shared between the showers based on *
9  * their exponentially weighted distance in an iterative procedure. If *
10  * a CR has no LM the highest energetic digit in the CR is taken as LM. *
11  * The position is reconstructed using logarithmic weights for not too *
12  * small shower and linear weights otherwise ('lilo'). *
13  * *
14  * Author: The Belle II Collaboration *
15  * Contributors: Torben Ferber (ferber@physics.ubc.ca) (TF) *
16  * *
17  * This software is provided "as is" without any warranty. *
18  **************************************************************************/
19 
20 // THIS MODULE
21 #include <ecl/modules/eclSplitterN1/ECLSplitterN1Module.h>
22 
23 //STL
24 #include <string>
25 #include <utility> // std::pair
26 #include <algorithm> // std::find
27 
28 //Root
29 #include "TFile.h"
30 #include "TGraph2D.h"
31 #include "TH1F.h"
32 
33 // FRAMEWORK
34 #include <framework/logging/Logger.h>
35 #include <framework/utilities/FileSystem.h>
36 #include <framework/geometry/B2Vector3.h>
37 
38 // ECL
39 #include <ecl/utility/Position.h>
40 #include <ecl/dataobjects/ECLCalDigit.h>
41 #include <ecl/dataobjects/ECLConnectedRegion.h>
42 #include <ecl/dataobjects/ECLLocalMaximum.h>
43 #include <ecl/dataobjects/ECLShower.h>
44 #include <ecl/geometry/ECLNeighbours.h>
45 #include <ecl/geometry/ECLGeometryPar.h>
46 
47 // MDST
48 #include <mdst/dataobjects/EventLevelClusteringInfo.h>
49 
50 // NAMESPACES
51 using namespace Belle2;
52 using namespace ECL;
53 
54 //-----------------------------------------------------------------
55 // Register the Module(s)
56 //-----------------------------------------------------------------
57 REG_MODULE(ECLSplitterN1)
58 REG_MODULE(ECLSplitterN1PureCsI)
59 
60 //-----------------------------------------------------------------
61 // Implementation
62 //-----------------------------------------------------------------
63 
65  m_eclCalDigits(eclCalDigitArrayName()),
66  m_eclConnectedRegions(eclConnectedRegionArrayName()),
67  m_eclShowers(eclShowerArrayName()),
68  m_eclLocalMaximums(eclLocalMaximumArrayName()),
69  m_eventLevelClusteringInfo(eventLevelClusteringInfoName())
70 {
71  // Set description.
72  setDescription("ECLSplitterN1Module: Baseline reconstruction splitter code for the n photon hypothesis.");
73  addParam("fullBkgdCount", m_fullBkgdCount,
74  "Number of background digits at full background (as provided by EventLevelClusteringInfo).",
75  182);
76 
77  // Set module parameters.
78 
79  // Splitter.
80  addParam("threshold", m_threshold, "Threshold energy after splitting.", 7.5 * Belle2::Unit::MeV);
81  addParam("expConstant", m_expConstant, "Constant a from exp(-a*dist/RM), typical: 1.5 to 3.5?", 2.5); // to be optimized!
82  addParam("maxIterations", m_maxIterations, "Maximum number of iterations for centroid shifts.", 100);
83  addParam("shiftTolerance", m_shiftTolerance, "Tolerance level for centroid shifts.", 1.0 * Belle2::Unit::mm);
84  addParam("minimumSharedEnergy", m_minimumSharedEnergy, "Minimum shared energy.", 25.0 * Belle2::Unit::keV);
85  addParam("maxSplits", m_maxSplits, "Maximum number of splits within one connected region.", 10);
86  addParam("cutDigitEnergyForEnergy", m_cutDigitEnergyForEnergy,
87  "Minimum digit energy to be included in the shower energy calculation. (NOT USED)", 0.5 * Belle2::Unit::MeV);
88  addParam("cutDigitTimeResidualForEnergy", m_cutDigitTimeResidualForEnergy,
89  "Maximum time residual to be included in the shower energy calculation. (NOT USED)", 5.0);
90 
91  // Neighbour definitions
92  addParam("useOptimalNumberOfDigitsForEnergy", m_useOptimalNumberOfDigitsForEnergy,
93  "Optimize the number of digits for energy calculations.", 1);
94  addParam("fileBackgroundNormName", m_fileBackgroundNormName, "Background filename.",
95  FileSystem::findFile("/data/ecl/background_norm.root"));
96  addParam("fileNOptimalFWDName", m_fileNOptimalFWDName, "FWD number of optimal neighbours filename.",
97  FileSystem::findFile("/data/ecl/noptimal_fwd.root"));
98  addParam("fileNOptimalBarrelName", m_fileNOptimalBarrelName, "Barrel number of optimal neighbours filename.",
99  FileSystem::findFile("/data/ecl/noptimal_barrel.root"));
100  addParam("fileNOptimalBWDName", m_fileNOptimalBWDName, "BWD number of optimal neighbours filename.",
101  FileSystem::findFile("/data/ecl/noptimal_bwd.root"));
102 
103  // Position.
104  addParam("positionMethod", m_positionMethod, "Position determination method.", std::string("lilo"));
105  addParam("liloParameterA", m_liloParameterA, "Position determination linear-log. parameter A.", 4.0);
106  addParam("liloParameterB", m_liloParameterB, "Position determination linear-log. parameter B.", 0.0);
107  addParam("liloParameterC", m_liloParameterC, "Position determination linear-log. parameter C.", 0.0);
108 
109  // Set parallel processing flag.
110  setPropertyFlags(c_ParallelProcessingCertified);
111 }
112 
114 {
115  // do not delete objects here, do it in terminate()!
116 }
117 
119 {
120 
121  // Geometry instance.
122  m_geom = ECLGeometryPar::Instance();
123 
124  // Check and format user input.
125  m_liloParameters.resize(3);
126  m_liloParameters.at(0) = m_liloParameterA;
127  m_liloParameters.at(1) = m_liloParameterB;
128  m_liloParameters.at(2) = m_liloParameterC;
129 
130  // ECL dataobjects.
131  m_eclCalDigits.registerInDataStore(eclCalDigitArrayName());
132  m_eclConnectedRegions.registerInDataStore(eclConnectedRegionArrayName());
133  m_eclShowers.registerInDataStore(eclShowerArrayName());
134  m_eclLocalMaximums.registerInDataStore(eclLocalMaximumArrayName());
135  m_eventLevelClusteringInfo.registerInDataStore(eventLevelClusteringInfoName());
136 
137  // Register relations (we probably dont need all, but keep them for now for debugging).
138  m_eclShowers.registerRelationTo(m_eclConnectedRegions);
139  m_eclShowers.registerRelationTo(m_eclCalDigits);
140  m_eclShowers.registerRelationTo(m_eclLocalMaximums);
141  m_eclLocalMaximums.registerRelationTo(m_eclCalDigits);
142 
143  // Initialize neighbour maps (we will optimize the endcaps later, there is more than just a certain energy containment to be considered)
144  m_NeighbourMap9 = new ECLNeighbours("N", 1); // N: 3x3 = 9
145  m_NeighbourMap21 = new ECLNeighbours("NC", 2); // NC: 5x5 excluding corners = 21
146 
147  // initialize the vector that gives the relation between cellid and store array position
148  m_StoreArrPosition.resize(8736 + 1);
149  m_StoreArrPositionLM.resize(8736 + 1);
150 
151  // read the Background correction factors (for full background)
152  m_fileBackgroundNorm = new TFile(m_fileBackgroundNormName.c_str(), "READ");
153  if (!m_fileBackgroundNorm) B2FATAL("Could not find file: " << m_fileBackgroundNormName);
154  m_th1fBackgroundNorm = dynamic_cast<TH1F*>(m_fileBackgroundNorm->Get("background_norm"));
155  if (!m_th1fBackgroundNorm) B2FATAL("Could not find m_th1fBackgroundNorm");
156 
157  // read the optimal neighbour maps
158  m_fileNOptimalFWD = new TFile(m_fileNOptimalFWDName.c_str(), "READ");
159  if (!m_fileNOptimalFWD) B2FATAL("Could not find file: " << m_fileNOptimalFWDName);
160  for (unsigned t = 0; t < 13; ++t) {
161  for (unsigned s = 0; s < c_nSectorCellIdFWD[t]; ++s) {
162  m_tg2dNOptimalFWD[t][s] = dynamic_cast<TGraph2D*>(m_fileNOptimalFWD->Get(Form("thetaid-%i_sectorcellid-%i", t, s)));
163  if (!m_tg2dNOptimalFWD[t][s]) B2FATAL("Could not find TGraph2D m_tg2dNOptimalFWD!");
164  }
165  }
166 
167  m_fileNOptimalBarrel = new TFile(m_fileNOptimalBarrelName.c_str(), "READ");
168  if (!m_fileNOptimalBarrel) B2FATAL("Could not find file: " << m_fileNOptimalBarrelName);
169 
170  m_tg2dNOptimalBarrel = dynamic_cast<TGraph2D*>(m_fileNOptimalBarrel->Get("thetaid-50_sectorcellid-8"));
171  if (!m_tg2dNOptimalBarrel) B2FATAL("Could not find TGraph2D m_tg2dNOptimalBarrel!");
172 
173  m_fileNOptimalBWD = new TFile(m_fileNOptimalBWDName.c_str(), "READ");
174  if (!m_fileNOptimalBWD) B2FATAL("Could not find file: " << m_fileNOptimalBWDName);
175  for (unsigned t = 0; t < 10; ++t) {
176  for (unsigned s = 0; s < c_nSectorCellIdBWD[t]; ++s) {
177  m_tg2dNOptimalBWD[t][s] = dynamic_cast<TGraph2D*>(m_fileNOptimalBWD->Get(Form("thetaid-%i_sectorcellid-%i", t + 59, s)));
178  if (!m_tg2dNOptimalBWD[t][s]) B2FATAL("Could not find TGraph2D m_tg2dNOptimalBWD!");
179  }
180  }
181 
182 }
183 
185 {
186  ;
187 }
188 
190 {
191  B2DEBUG(175, "ECLCRSplitterModule::event()");
192 
193  // Fill a vector that can be used to map cellid -> store array position for eclCalDigits.
194  memset(&m_StoreArrPosition[0], -1, m_StoreArrPosition.size() * sizeof m_StoreArrPosition[0]);
195  for (int i = 0; i < m_eclCalDigits.getEntries(); i++) {
196  m_StoreArrPosition[m_eclCalDigits[i]->getCellId()] = i;
197  }
198 
199  // Fill a vector that can be used to map cellid -> store array position for eclLocalMaximums.
200  memset(&m_StoreArrPositionLM[0], -1, m_StoreArrPositionLM.size() * sizeof m_StoreArrPositionLM[0]);
201  for (int i = 0; i < m_eclLocalMaximums.getEntries(); i++) {
202  m_StoreArrPositionLM[m_eclLocalMaximums[i]->getCellId()] = i;
203  }
204 
205  // Loop over all connected regions
206  for (auto& aCR : m_eclConnectedRegions) {
207  // list theat will hold all cellids in this connected region
208  m_cellIdInCR.clear();
209 
210  const unsigned int entries = (aCR.getRelationsWith<ECLCalDigit>(eclCalDigitArrayName())).size();
211 
212  m_cellIdInCR.resize(entries);
213 
214  // Fill all calDigits ids in this CR into a vector to make them 'find'-able.
215  int i = 0;
216  for (const auto& caldigit : aCR.getRelationsWith<ECLCalDigit>(eclCalDigitArrayName())) {
217  m_cellIdInCR[i] = caldigit.getCellId();
218  ++i;
219  }
220 
221  // Split and reconstruct the showers in this connected regions.
222  splitConnectedRegion(aCR);
223 
224  } // end auto& aCR
225 
226 }
227 
228 
230 {
231 // if (m_tg2OptimalNumberOfDigitsForEnergy) delete m_tg2OptimalNumberOfDigitsForEnergy;
232 }
233 
234 
236 {
237  // delete open TFiles
238  if (m_fileBackgroundNorm) delete m_fileBackgroundNorm;
239  if (m_fileNOptimalFWD) delete m_fileNOptimalFWD;
240  if (m_fileNOptimalBarrel) delete m_fileNOptimalBarrel;
241  if (m_fileNOptimalBWD) delete m_fileNOptimalBWD;
242 
243  if (m_NeighbourMap9) delete m_NeighbourMap9;
244  if (m_NeighbourMap21) delete m_NeighbourMap21;
245 }
246 
248 {
249 
250  // Get the event background level
251  const int bkgdcount = m_eventLevelClusteringInfo->getNECLCalDigitsOutOfTime();
252  double backgroundLevel = 0.0; // from out of time digit counting
253  if (m_fullBkgdCount > 0) {
254  backgroundLevel = static_cast<double>(bkgdcount) / static_cast<double>(m_fullBkgdCount);
255  }
256 
257  // Get the number of LMs in this CR
258  const int nLocalMaximums = aCR.getRelationsWith<ECLLocalMaximum>(eclLocalMaximumArrayName()).size();
259 
260  B2DEBUG(175, "ECLCRSplitterModule::splitConnectedRegion: nLocalMaximums = " << nLocalMaximums);
261 
262  // Three cases:
263  // 1) There is no local maximum (most likely in presence of high background) or there are too many:
264  // Make one photon around the highest largest energy deposition in this CR.
265  // 2) There is exactly one local maximum, this is the easiest (and most likely for true photons) case.
266  // 3) There are more than one, typically two or three, local maxima and we have to share energy between them.
267 
268  // ---------------------------------------------------------------------
269  if (nLocalMaximums == 1 or nLocalMaximums >= m_maxSplits) {
270 
271  // Create a shower.
272  const auto aECLShower = m_eclShowers.appendNew();
273 
274  // Add relation to the CR.
275  aECLShower->addRelationTo(&aCR);
276 
277  // Find the highest energetic crystal in this CR or use the LM.
278  double weightSum = 0.0;
279 
280  // Add relation to the LM.
281  RelationVector<ECLLocalMaximum> locmaxvector = aCR.getRelationsWith<ECLLocalMaximum>(eclLocalMaximumArrayName());
282  aECLShower->addRelationTo(locmaxvector[0]);
283 
284  const int locmaxcellid = locmaxvector[0]->getCellId();
285  const int pos = m_StoreArrPosition[locmaxcellid];
286  double highestEnergyID = (m_eclCalDigits[pos])->getCellId();
287  double highestEnergy = (m_eclCalDigits[pos])->getEnergy();
288  double highestEnergyTime = (m_eclCalDigits[pos])->getTime();
289  double highestEnergyTimeResolution = (m_eclCalDigits[pos])->getTimeResolution();
290 
291  // Get a first estimation of the energy using 3x3 neighbours.
292  const double energyEstimation = estimateEnergy(highestEnergyID);
293 
294  // Check if 21 would be better in the present background conditions:
295  ECLNeighbours* neighbourMap; // FIXME pointer needed?
296  int nNeighbours = getNeighbourMap(energyEstimation, backgroundLevel);
297  if (nNeighbours == 9 and !m_useOptimalNumberOfDigitsForEnergy) neighbourMap = m_NeighbourMap9;
298  else neighbourMap = m_NeighbourMap21;
299 
300  // Add neighbours and weights for the shower.
301  std::vector<ECLCalDigit> digits;
302  std::vector<double> weights;
303  for (auto& neighbourId : neighbourMap->getNeighbours(highestEnergyID)) {
304  const auto it = std::find(m_cellIdInCR.begin(), m_cellIdInCR.end(),
305  neighbourId); // check if the neighbour is in the list for this CR
306  if (it == m_cellIdInCR.end()) continue; // not in this CR
307 
308  const int neighbourpos = m_StoreArrPosition[neighbourId];
309  digits.push_back(*m_eclCalDigits[neighbourpos]); // list of digits for position reconstruction
310  weights.push_back(1.0); // list of weights (all 1 in this case for now)
311  weightSum += 1.0;
312 
313  aECLShower->addRelationTo(m_eclCalDigits[neighbourpos], 1.0); // add digits to this shower, weight = 1
314  }
315 
316  // Get position.
317  const B2Vector3D& showerposition = Belle2::ECL::computePositionLiLo(digits, weights, m_liloParameters);
318  aECLShower->setTheta(showerposition.Theta());
319  aECLShower->setPhi(showerposition.Phi());
320  aECLShower->setR(showerposition.Mag());
321 
322  // Get Energy, if requested, set some weights to zero for energy calculation.
323  double showerEnergy = 0.0;
324  if (m_useOptimalNumberOfDigitsForEnergy) {
325 
326  // Get the optimal number of neighbours as function of raw energy and background level
327  const unsigned int nOptimal = getOptimalNumberOfDigits(highestEnergyID, energyEstimation, backgroundLevel);
328  aECLShower->setNominalNumberOfCrystalsForEnergy(static_cast<double>(nOptimal));
329 
330  // Get the list of crystals used for the energy calculation
331  std::vector< std::pair<unsigned int, double>> listCrystalPairs; // cell id and weighted reconstructed energy
332  listCrystalPairs.resize(digits.size()); //resize to number of all crystals in cluster
333 
334  std::vector < std::pair<double, double> > weighteddigits;
335  weighteddigits.resize(digits.size());
336  for (unsigned int i = 0; i < digits.size(); ++i) {
337  weighteddigits.at(i) = std::make_pair((digits.at(i)).getEnergy(), weights.at(i));
338  listCrystalPairs.at(i) = std::make_pair((digits.at(i)).getCellId(), weights.at(i) * (digits.at(i)).getEnergy());
339  }
340 
341  // sort the listCrystals and keep the n highest in descending order
342  std::sort(listCrystalPairs.begin(), listCrystalPairs.end(), [](auto & left, auto & right) {
343  return left.second < right.second;
344  });
345  std::vector< unsigned int> listCrystals; //cell id
346 
347  for (unsigned int i = 0; i < digits.size(); ++i) {
348  if (i < nOptimal) {
349  listCrystals.push_back(listCrystalPairs[i].first);
350  }
351  }
352 
353  aECLShower->setNumberOfCrystalsForEnergy(static_cast<double>(listCrystals.size()));
354  aECLShower->setListOfCrystalsForEnergy(listCrystals);
355 
356  showerEnergy = getEnergySum(weighteddigits, nOptimal);
357  B2DEBUG(150, "Shower Energy (1): " << showerEnergy);
358 
359  } else {
360  showerEnergy = Belle2::ECL::computeEnergySum(digits, weights);
361  }
362 
363  aECLShower->setEnergy(showerEnergy);
364  aECLShower->setEnergyRaw(showerEnergy);
365  aECLShower->setEnergyHighestCrystal(highestEnergy);
366  aECLShower->setTime(highestEnergyTime);
367  aECLShower->setDeltaTime99(highestEnergyTimeResolution);
368  aECLShower->setNumberOfCrystals(weightSum);
369  aECLShower->setCentralCellId(highestEnergyID);
370 
371  B2DEBUG(175, "theta = " << showerposition.Theta());
372  B2DEBUG(175, "phi = " << showerposition.Phi());
373  B2DEBUG(175, "R = " << showerposition.Mag());
374  B2DEBUG(175, "energy = " << showerEnergy);
375  B2DEBUG(175, "time = " << highestEnergyTime);
376  B2DEBUG(175, "time resolution = " << highestEnergyTimeResolution);
377  B2DEBUG(175, "neighbours = " << nNeighbours);
378  B2DEBUG(175, "backgroundLevel = " << backgroundLevel);
379 
380  // Fill shower Ids
381  aECLShower->setShowerId(1); // always one (only this single shower in the CR)
382  aECLShower->setHypothesisId(Belle2::ECLShower::c_nPhotons);
383  aECLShower->setConnectedRegionId(aCR.getCRId());
384 
385  // Add relations of all CalDigits of the CR to the local maximum (here: all weights = 1).
386  const int posLM = m_StoreArrPositionLM[locmaxcellid];
387  for (const auto& aDigit : aCR.getRelationsWith<ECLCalDigit>()) {
388  const int posDigit = m_StoreArrPosition[aDigit.getCellId()];
389  m_eclLocalMaximums[posLM]->addRelationTo(m_eclCalDigits[posDigit], 1.0);
390  }
391 
392  } // end case with one LM
393  else { // this is the really interesing part where showers are split. this alogorithm is based on BaBar code.
394 
395  std::vector<ECLCalDigit> digits;
396  std::vector<double> weights;
397  std::map<int, B2Vector3D> centroidList; // key = cellid, value = centroid position
398  std::map<int, double> centroidEnergyList; // key = cellid, value = centroid position
399  std::map<int, B2Vector3D> allPoints; // key = cellid, value = digit position
400  std::map<int, std::vector < double > > weightMap; // key = locmaxid, value = vector of weights
401  std::vector < ECLCalDigit > digitVector; // the order of weights in weightMap must be the same
402 
403  // Fill the maxima positions in a map
404  std::map<int, B2Vector3D> localMaximumsPoints; // key = locmaxid, value = maximum position
405  std::map<int, B2Vector3D> centroidPoints; // key = locmaxid (as index), value = centroid position
406  for (auto& aLocalMaximum : aCR.getRelationsWith<ECLLocalMaximum>(eclLocalMaximumArrayName())) {
407 
408  int cellid = aLocalMaximum.getCellId();
409 
410  // Get the position of this crystal and fill it in two maps.
411  B2Vector3D vectorPosition = m_geom->GetCrystalPos(cellid - 1);
412  localMaximumsPoints.insert(std::map<int, B2Vector3D>::value_type(cellid, vectorPosition));
413  centroidPoints.insert(std::map<int, B2Vector3D>::value_type(cellid, vectorPosition));
414  }
415 
416  // The following will be done iteratively. Empty clusters after splitting will be removed, and the procedure will be repeated.
417  bool iterateclusters = true;
418  do {
419  digits.clear();
420  weights.clear();
421  centroidList.clear();
422  centroidEnergyList.clear();
423  allPoints.clear();
424  weightMap.clear();
425  digitVector.clear();
426 
427  // Fill all digits from this CR in a map
428  for (auto& aCalDigit : aCR.getRelationsWith<ECLCalDigit>(eclCalDigitArrayName())) {
429  const int cellid = aCalDigit.getCellId();
430  // get the position of this crystal and fill them in a map
431  B2Vector3D vectorPosition = m_geom->GetCrystalPos(cellid - 1);
432  allPoints.insert(std::map<int, B2Vector3D>::value_type(cellid, vectorPosition));
433  digits.push_back(aCalDigit);
434  }
435 
436  for (const auto& digitpoint : allPoints) {
437  const int cellid = digitpoint.first;
438  const int pos = m_StoreArrPosition[cellid];
439  digitVector.push_back(*m_eclCalDigits[pos]);
440  }
441 
442 
443  // -----------------------------------------------------------------------------------------
444  // The 'heart' of the splitter
445  // Start with each local maximum and set it to the first centroid position. Then iterate over all ECLCalDigits
446  // in this CR and calculate the weighted distances to the local maximum
447  int nIterations = 0;
448  double centroidShiftAverage = 0.0;
449  // double lastcentroidShiftAverage = 0.0;
450 
451  do {
452  B2DEBUG(175, "Iteration: #" << nIterations << " (of max. " << m_maxIterations << ")");
453 
454  centroidShiftAverage = 0.0;
455  if (nIterations == 0) {
456  centroidList.clear();
457  centroidEnergyList.clear();
458  weightMap.clear();
459  }
460 
461  // Loop over all local maximums points, each one will become a shower!
462  for (const auto& locmaxpoint : localMaximumsPoints) {
463 
464  // cell id of this local maximum
465  const int locmaxcellid = locmaxpoint.first;
466 
467  // clear weights vector
468  weights.clear();
469 
470  // if this is the first iteration the shower energy is not know, take the local maximum energy * 1.5.
471  if (nIterations == 0) {
472  const int pos = m_StoreArrPosition[locmaxcellid];
473  const double locmaxenergy = m_eclCalDigits[pos]->getEnergy();
474  centroidEnergyList[locmaxcellid] = 1.5 * locmaxenergy;
475  }
476 
477  B2DEBUG(175, "local maximum cellid: " << locmaxcellid);
478 
479  //-------------------------------------------------------------------
480  // Loop over all digits. They get a weight using the distance to the respective centroid.
481  int nDigit = 0;
482  for (const auto& digitpoint : allPoints) {
483 
484  // cellid and position of this digit
485  const int digitcellid = digitpoint.first;
486  B2Vector3D digitpos = digitpoint.second;
487 
488  const int pos = m_StoreArrPosition[digitcellid];
489  const double digitenergy = m_eclCalDigits[pos]->getEnergy();
490 
491  double weight = 0.0;
492  double energy = 0.0;
493  double distance = 0.0;
494  double distanceEnergySum = 0.0;
495 
496  // Loop over all centroids
497  for (const auto& centroidpoint : centroidPoints) {
498 
499  // cell id and position of this centroid
500  const int centroidcellid = centroidpoint.first;
501  B2Vector3D centroidpos = centroidpoint.second;
502 
503  double thisdistance = 0.;
504 
505  // in the first iteration, this distance is really zero, avoid floating point problems
506  if (nIterations == 0 and digitcellid == centroidcellid) {
507  thisdistance = 0.0;
508  } else {
509  B2Vector3D vectorDistance = ((centroidpos) - (digitpos));
510  thisdistance = vectorDistance.Mag();
511  }
512 
513  // energy of the centroid aka locmax
514  const int thispos = m_StoreArrPosition[centroidcellid];
515  const double thisenergy = m_eclCalDigits[thispos]->getEnergy();
516 
517  // Not the most efficienct way to get this information, but not worth the thinking yet:
518  if (locmaxcellid == centroidcellid) {
519  distance = thisdistance;
520  energy = thisenergy;
521  }
522 
523  // Get the product of distance and energy.
524  const double expfactor = exp(-m_expConstant * thisdistance / c_molierRadius);
525  distanceEnergySum += (thisenergy * expfactor);
526 
527  } // end centroidPoints
528 
529  // Calculate the weight for this digits for this local maximum.
530  if (distanceEnergySum > 0.0) {
531  const double expfactor = exp(-m_expConstant * distance / c_molierRadius);
532  weight = energy * expfactor / distanceEnergySum;
533  } else {
534  weight = 0.0;
535  }
536 
537  // Check if the weighted energy is above threshold
538  if ((digitenergy * weight) < m_minimumSharedEnergy) {
539  weight = 0.0;
540  }
541 
542  // Check for rounding problems larger than unity
543  if (weight > 1.0) {
544  B2WARNING("ECLCRSplitterModule::splitConnectedRegion: Floating point glitch, weight for this digit " << weight <<
545  ", resetting it to 1.0.");
546  weight = 1.0;
547  }
548 
549  // Fill the weight for this digits and this local maximum.
550  B2DEBUG(175, " cellid: " << digitcellid << ", energy: " << digitenergy << ", weight: " << weight << ", distance: " << distance);
551  weights.push_back(weight);
552  ++nDigit;
553 
554  } // end allPoints
555 
556  // Get the old centroid position.
557  B2Vector3D oldCentroidPos = (centroidPoints.find(locmaxcellid))->second;
558 
559  // Calculate the new centroid position.
560  B2Vector3D newCentroidPos = Belle2::ECL::computePositionLiLo(digits, weights, m_liloParameters);
561 
562  // Calculate new energy
563  const double newEnergy = Belle2::ECL::computeEnergySum(digits, weights);
564 
565  // Calculate the shift of the centroid position for this local maximum.
566  const B2Vector3D centroidShift = (oldCentroidPos - newCentroidPos);
567 
568  // Save the new centroid position (but dont update yet!), also save the weights and energy.
569  centroidList[locmaxcellid] = newCentroidPos;
570  weightMap[locmaxcellid] = weights;
571 
572  B2DEBUG(175, "--> inserting new energy: " << newEnergy << " for local maximum " << locmaxcellid);
573  centroidEnergyList[locmaxcellid] = newEnergy;
574  double showerenergy = (*centroidEnergyList.find(locmaxcellid)).second / Belle2::Unit::MeV;
575  B2DEBUG(175, "--> new energy = " << showerenergy << " MeV");
576 
577  // Add this to the average centroid shift.
578  centroidShiftAverage += centroidShift.Mag();
579 
580  // Debugging output
581  B2DEBUG(175, " old centroid: " << oldCentroidPos.x() << " cm, " << oldCentroidPos.y() << " cm, " << oldCentroidPos.z() <<
582  "cm");
583  B2DEBUG(175, " new centroid: " << newCentroidPos.x() << " cm, " << newCentroidPos.y() << " cm, " << newCentroidPos.z() <<
584  "cm");
585  B2DEBUG(175, " centroid shift: " << centroidShift.Mag() << " cm");
586 
587  } // end localMaximumsPoints
588 
589  // Get the average centroid shift.
590  centroidShiftAverage /= static_cast<double>(nLocalMaximums);
591  // lastcentroidShiftAverage = centroidShiftAverage;
592  B2DEBUG(175, "--> average centroid shift: " << centroidShiftAverage << " cm (tolerance is " << m_shiftTolerance << " cm)");
593 
594  // Update centroid positions for the next round
595  for (const auto& locmaxpoint : localMaximumsPoints) {
596  centroidPoints[locmaxpoint.first] = (centroidList.find(locmaxpoint.first))->second;
597  }
598 
599  ++nIterations;
600 
601  } while (nIterations < m_maxIterations and centroidShiftAverage > m_shiftTolerance);
602  // DONE!
603 
604  // check that local maxima are still local maxima
605  std::vector<int> markfordeletion;
606  iterateclusters = false;
607  for (const auto& locmaxpoint : localMaximumsPoints) {
608 
609  // Get locmax cellid
610  const int locmaxcellid = locmaxpoint.first;
611  const int pos = m_StoreArrPosition[locmaxcellid];
612 
613  B2DEBUG(175, "locmaxcellid: " << locmaxcellid);
614  const double lmenergy = m_eclCalDigits[pos]->getEnergy();
615  B2DEBUG(175, "ok: ");
616 
617  // Get the weight vector.
618  std::vector < double > myWeights = (*weightMap.find(locmaxcellid)).second;
619 
620  for (unsigned int i = 0; i < digitVector.size(); ++i) {
621 
622  const ECLCalDigit dig = digitVector[i];
623  const double weight = myWeights[i];
624  const int cellid = dig.getCellId();
625  const double energy = dig.getEnergy();
626 
627  // two ways to fail:
628  // 1) another cell has more energy: energy*weight > lmenergy and cellid != locmaxcellid
629  // 2) local maximum has cell has less than threshold energy left: energy*weight < m_threshold and cellid == locmaxcellid
630  if ((energy * weight > lmenergy and cellid != locmaxcellid) or (energy * weight < m_threshold and cellid == locmaxcellid)) {
631  markfordeletion.push_back(locmaxcellid);
632  iterateclusters = true;
633  continue;
634  }
635  }
636  }
637 
638  // delete LMs
639  for (const auto lmid : markfordeletion) {
640  localMaximumsPoints.erase(lmid);
641  centroidPoints.erase(lmid);
642  }
643 
644  } while (iterateclusters);
645 
646  // Create the ECLShower objects, one per LocalMaximumPoints
647  unsigned int iShower = 1;
648  for (const auto& locmaxpoint : localMaximumsPoints) {
649 
650  const int locmaxcellid = locmaxpoint.first;
651  const int posLM = m_StoreArrPositionLM[locmaxcellid];
652 
653  // Create a shower
654  const auto aECLShower = m_eclShowers.appendNew();
655 
656  // Use the same method for the estimate (3x3).
657  const double energyEstimation = estimateEnergy(locmaxcellid);
658 
659  // Get the neighbour list.
660  ECLNeighbours* neighbourMap; // FIXME need pointer?
661  int nNeighbours = getNeighbourMap(energyEstimation, backgroundLevel);
662  if (nNeighbours == 9 and !m_useOptimalNumberOfDigitsForEnergy) neighbourMap = m_NeighbourMap9;
663  else neighbourMap = m_NeighbourMap21;
664 
665  // Get the neighbour list.
666  std::vector<short int> neighbourlist = neighbourMap->getNeighbours(locmaxcellid);
667 
668  // Get the weight vector.
669  std::vector < double > myWeights = (*weightMap.find(locmaxcellid)).second;
670 
671  // Loop over all digits.
672  std::vector<ECLCalDigit> newdigits;
673  std::vector<double> newweights;
674  double highestEnergy = 0.;
675  double highestEnergyTime = 0.;
676  double highestEnergyTimeResolution = 0.;
677  double weightSum = 0.0;
678 
679  for (unsigned int i = 0; i < digitVector.size(); ++i) {
680 
681  const ECLCalDigit dig = digitVector[i];
682  const double weight = myWeights[i];
683 
684  const int cellid = dig.getCellId();
685  const int pos = m_StoreArrPosition[cellid];
686 
687  // Add weighted relations of all CalDigits to the local maximum.
688  m_eclLocalMaximums[posLM]->addRelationTo(m_eclCalDigits[pos], weight);
689 
690  // Positive weight and in allowed neighbour list?
691  if (weight > 0.0) {
692  if (std::find(neighbourlist.begin(), neighbourlist.end(), cellid) != neighbourlist.end()) {
693 
694  aECLShower->addRelationTo(m_eclCalDigits[pos], weight);
695 
696  newdigits.push_back(dig);
697  newweights.push_back(weight);
698 
699  weightSum += weight;
700 
701  const double energy = dig.getEnergy();
702 
703  if (energy * weight > highestEnergy) {
704  highestEnergy = energy * weight;
705  highestEnergyTime = dig.getTime();
706  highestEnergyTimeResolution = dig.getTimeResolution();
707  }
708  }
709  }
710  }
711 
712  // Old position:
713  B2Vector3D* oldshowerposition = new B2Vector3D((centroidList.find(locmaxcellid))->second);
714 
715  B2DEBUG(175, "old theta: " << oldshowerposition->Theta());
716  B2DEBUG(175, "old phi: " << oldshowerposition->Phi());
717  B2DEBUG(175, "old R: " << oldshowerposition->Mag());
718  B2DEBUG(175, "old energy: " << energyEstimation);
719  delete oldshowerposition;
720 
721  // New position (with reduced number of neighbours)
722  // There are some cases where high backgrounds fake local maxima and the splitted centroid position is far
723  // away from the original LM cell... this will throw a (non fatal) error, and create a cluster with zero energy now).
724  B2Vector3D* showerposition = new B2Vector3D(Belle2::ECL::computePositionLiLo(newdigits, newweights, m_liloParameters));
725  aECLShower->setTheta(showerposition->Theta());
726  aECLShower->setPhi(showerposition->Phi());
727  aECLShower->setR(showerposition->Mag());
728 
729  B2DEBUG(175, "new theta: " << showerposition->Theta());
730  B2DEBUG(175, "new phi: " << showerposition->Phi());
731  B2DEBUG(175, "new R: " << showerposition->Mag());
732  delete showerposition;
733 
734  // Get Energy, if requested, set weights to zero for energy calculation.
735  double showerEnergy = 0.0;
736  if (m_useOptimalNumberOfDigitsForEnergy) {
737 
738  // Get the optimal number of neighbours as function of raw energy and background level
739  const unsigned int nOptimal = getOptimalNumberOfDigits(locmaxcellid, energyEstimation, backgroundLevel);
740  aECLShower->setNominalNumberOfCrystalsForEnergy(static_cast<double>(nOptimal));
741 
742  // Get the list of crystals used for the energy calculation
743  std::vector< std::pair<unsigned int, double>> listCrystalPairs; // cell id and weighted reconstructed energy
744  listCrystalPairs.resize(newdigits.size()); //resize to number of all crystals in cluster
745 
746  std::vector < std::pair<double, double> > weighteddigits;
747  weighteddigits.resize(newdigits.size());
748  for (unsigned int i = 0; i < newdigits.size(); ++i) {
749  weighteddigits.at(i) = std::make_pair((newdigits.at(i)).getEnergy(), newweights.at(i));
750  listCrystalPairs.at(i) = std::make_pair((newdigits.at(i)).getCellId(), newweights.at(i) * (newdigits.at(i)).getEnergy());
751  }
752 
753  // sort the listCrystals and keep the n highest in descending order
754  std::sort(listCrystalPairs.begin(), listCrystalPairs.end(), [](auto & left, auto & right) {
755  return left.second < right.second;
756  });
757 
758  std::vector< unsigned int> listCrystals; //cell id
759 
760  for (unsigned int i = 0; i < newdigits.size(); ++i) {
761  if (i < nOptimal) {
762  listCrystals.push_back(listCrystalPairs[i].first);
763  }
764  }
765 
766  aECLShower->setNumberOfCrystalsForEnergy(static_cast<double>(listCrystals.size()));
767  aECLShower->setListOfCrystalsForEnergy(listCrystals);
768 
769  showerEnergy = getEnergySum(weighteddigits, nOptimal);
770  B2DEBUG(150, "Shower Energy (2): " << showerEnergy);
771 
772  } else {
773  showerEnergy = Belle2::ECL::computeEnergySum(newdigits, newweights);
774  }
775 
776  aECLShower->setEnergy(showerEnergy);
777  aECLShower->setEnergyRaw(showerEnergy);
778  aECLShower->setEnergyHighestCrystal(highestEnergy);
779  aECLShower->setTime(highestEnergyTime);
780  aECLShower->setDeltaTime99(highestEnergyTimeResolution);
781  aECLShower->setNumberOfCrystals(weightSum);
782  aECLShower->setCentralCellId(locmaxcellid);
783 
784  B2DEBUG(175, "new energy: " << showerEnergy);
785 
786  // Get unique ID
787  aECLShower->setShowerId(iShower);
788  ++iShower;
789  aECLShower->setHypothesisId(Belle2::ECLShower::c_nPhotons);
790  aECLShower->setConnectedRegionId(aCR.getCRId());
791 
792  // Add relation to the CR.
793  aECLShower->addRelationTo(&aCR);
794 
795  // Add relation to the LM.
796  aECLShower->addRelationTo(m_eclLocalMaximums[posLM]);
797  }
798  }
799 }
800 
801 int ECLSplitterN1Module::getNeighbourMap(const double energy, const double background)
802 {
803  if (background <= 0.1) return 21;
804  else {
805  if (energy > 0.06 + 0.4 * background) return 21; // based on preliminary study TF, valid in barrel only (TF).
806  else return 9;
807  }
808 }
809 
810 unsigned int ECLSplitterN1Module::getOptimalNumberOfDigits(const int cellid, const double energy, const double bg)
811 {
812  int nOptimalNeighbours = 21;
813 
814  // Get the corrected background level
815  const double bgCorrected = bg * m_th1fBackgroundNorm->GetBinContent(cellid);
816 
817  // For very small background levels, we always use 21 neighbours.
818  if (bgCorrected > 0.025) {
819  // Some checks to be within the limits of the tgraph2ds.
820  double energyChecked = energy;
821  double bgChecked = bgCorrected;
822  if (bgCorrected > 1.0) bgChecked = 1.0;
823  if (energyChecked < 30.0 * Belle2::Unit::MeV) energyChecked = 30.0 * Belle2::Unit::MeV;
824  if (energyChecked > 1.95 * Belle2::Unit::GeV) energyChecked = 1.95 * Belle2::Unit::GeV;
825 
826  // Find detector region and sector phi Id
827  m_geom->Mapping(cellid - 1);
828  const int thetaId = m_geom->GetThetaID();
829  const int crystalsPerSector = c_crystalsPerRing[thetaId] / 16;
830  const int phiIdInSector = m_geom->GetPhiID() % crystalsPerSector;
831 
832  if (thetaId < 13) { //FWD
833  nOptimalNeighbours = static_cast<unsigned int>(m_tg2dNOptimalFWD[thetaId][phiIdInSector]->Interpolate(energyChecked, bgChecked));
834  } else if (thetaId < 59) { //Barrel
835  nOptimalNeighbours = static_cast<unsigned int>(m_tg2dNOptimalBarrel->Interpolate(energyChecked, bgChecked));
836  } else { // BWD
837  nOptimalNeighbours = static_cast<unsigned int>(m_tg2dNOptimalBWD[thetaId - 59][phiIdInSector]->Interpolate(energyChecked,
838  bgChecked));
839  }
840  B2DEBUG(175, "ECLSplitterN1Module::getOptimalNumberOfDigits: theta ID: " << thetaId << ", bg: " << bg << " (after corr.: " <<
841  bgCorrected << "), energy: " << energy << ", n: " << nOptimalNeighbours);
842  } else B2DEBUG(175, "ECLSplitterN1Module::getOptimalNumberOfDigits: small bg: " << bg << " (after corr.: " << bgCorrected <<
843  "), energy: " << energy << ", n: " << nOptimalNeighbours);
844 
845  return nOptimalNeighbours;
846 
847 }
848 
849 double ECLSplitterN1Module::getEnergySum(std::vector < std::pair<double, double> >& weighteddigits, const unsigned int n)
850 {
851 
852  double energysum = 0.;
853 
854  std::sort(weighteddigits.begin(), weighteddigits.end(), std::greater<std::pair<double, double>>());
855 
856  unsigned int min = n;
857  if (weighteddigits.size() < n) min = weighteddigits.size();
858 
859  for (unsigned int i = 0; i < min; ++i) {
860  B2DEBUG(150, "getEnergySum: " << weighteddigits.at(i).first << " " << weighteddigits.at(i).second);
861  energysum += (weighteddigits.at(i).first * weighteddigits.at(i).second);
862  }
863  B2DEBUG(150, "getEnergySum: energysum=" << energysum);
864 
865  return energysum;
866 }
867 
868 
869 double ECLSplitterN1Module::estimateEnergy(const int centerid)
870 {
871 
872  double energyEstimation = 0.0;
873 
874  for (auto& neighbourId : m_NeighbourMap9->getNeighbours(centerid)) {
875 
876  // Check if this neighbour is in this CR
877  const auto it = std::find(m_cellIdInCR.begin(), m_cellIdInCR.end(),
878  neighbourId); // check if the neighbour is in the list for this CR
879  if (it == m_cellIdInCR.end()) continue; // not in this CR
880 
881  const int pos = m_StoreArrPosition[neighbourId];
882  const double energyNeighbour = m_eclCalDigits[pos]->getEnergy();
883 
884  energyEstimation += energyNeighbour;
885  }
886 
887  return energyEstimation;
888 }
Belle2::ECLCalDigit::getEnergy
double getEnergy() const
Get Calibrated Energy.
Definition: ECLCalDigit.h:134
Belle2::ECLSplitterN1Module::event
virtual void event() override
Event.
Definition: ECLSplitterN1Module.cc:189
Belle2::ECLSplitterN1Module
Class to perform the shower correction.
Definition: ECLSplitterN1Module.h:55
Belle2::ECLSplitterN1Module::estimateEnergy
double estimateEnergy(const int centerid)
Estimate energy using 3x3 around central crystal.
Definition: ECLSplitterN1Module.cc:869
Belle2::RelationsInterface::getRelationsWith
RelationVector< T > getRelationsWith(const std::string &name="", const std::string &namedRelation="") const
Get the relations between this object and another store array.
Definition: RelationsObject.h:232
Belle2::ECLCalDigit
Class to store calibrated ECLDigits: ECLCalDigits.
Definition: ECLCalDigit.h:38
Belle2::ECLSplitterN1Module::getEnergySum
double getEnergySum(std::vector< std::pair< double, double > > &weighteddigits, const unsigned int n)
Get energy sum for weighted entries.
Definition: ECLSplitterN1Module.cc:849
Belle2::ECLCalDigit::getCellId
int getCellId() const
Get Cell ID.
Definition: ECLCalDigit.h:129
REG_MODULE
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:652
Belle2::ECLSplitterN1Module::beginRun
virtual void beginRun() override
Begin run.
Definition: ECLSplitterN1Module.cc:184
Belle2::B2Vector3::x
DataType x() const
access variable X (= .at(0) without boundary check)
Definition: B2Vector3.h:424
Belle2::ECLConnectedRegion::getCRId
int getCRId() const
Get CR identifieer.
Definition: ECLConnectedRegion.h:84
Belle2::RelationsInterface::addRelationTo
void addRelationTo(const RelationsInterface< BASE > *object, float weight=1.0, const std::string &namedRelation="") const
Add a relation from this object to another object (with caching).
Definition: RelationsObject.h:144
Belle2::ECLCalDigit::getTimeResolution
double getTimeResolution() const
Get Calibrated Time Resolution.
Definition: ECLCalDigit.h:179
Belle2::ECLSplitterN1Module::~ECLSplitterN1Module
~ECLSplitterN1Module()
Destructor.
Definition: ECLSplitterN1Module.cc:113
Belle2::B2Vector3< double >
Belle2::Module
Base class for Modules.
Definition: Module.h:74
Belle2::ECL::ECLGeometryPar::Instance
static ECLGeometryPar * Instance()
Static method to get a reference to the ECLGeometryPar instance.
Definition: ECLGeometryPar.cc:151
Belle2::Unit::MeV
static const double MeV
[megaelectronvolt]
Definition: Unit.h:124
Belle2::Unit::keV
static const double keV
[kiloelectronvolt]
Definition: Unit.h:123
Belle2::ECL::ECLNeighbours
Class to get the neighbours for a given cell id.
Definition: ECLNeighbours.h:38
Belle2::RelationVector
Class for type safe access to objects that are referred to in relations.
Definition: DataStore.h:38
Belle2::B2Vector3::Phi
DataType Phi() const
The azimuth angle.
Definition: B2Vector3.h:150
Belle2::B2Vector3D
B2Vector3< double > B2Vector3D
typedef for common usage with double
Definition: B2Vector3.h:507
Belle2::ECLCalDigit::getTime
double getTime() const
Get Calibrated Time.
Definition: ECLCalDigit.h:174
Belle2
Abstract base class for different kinds of events.
Definition: MillepedeAlgorithm.h:19
Belle2::ECLSplitterN1Module::endRun
virtual void endRun() override
End run.
Definition: ECLSplitterN1Module.cc:229
Belle2::B2Vector3::y
DataType y() const
access variable Y (= .at(1) without boundary check)
Definition: B2Vector3.h:426
Belle2::ECLSplitterN1Module::getOptimalNumberOfDigits
unsigned int getOptimalNumberOfDigits(const int cellid, const double energy, const double background)
Get optimal number of digits (out of 21) based on first energy estimation and background level per ev...
Definition: ECLSplitterN1Module.cc:810
Belle2::ECLLocalMaximum
Class to store local maxima (LM)
Definition: ECLLocalMaximum.h:41
Belle2::ECLShower::c_nPhotons
@ c_nPhotons
CR is split into n photons (N1)
Definition: ECLShower.h:54
Belle2::ECLSplitterN1Module::getNeighbourMap
int getNeighbourMap(const double energy, const double background)
Get number of neighbours based on first energy estimation and background level per event.
Definition: ECLSplitterN1Module.cc:801
Belle2::Unit::mm
static const double mm
[millimeters]
Definition: Unit.h:80
Belle2::ECLSplitterN1Module::splitConnectedRegion
void splitConnectedRegion(ECLConnectedRegion &aCR)
Split connected region into showers.
Definition: ECLSplitterN1Module.cc:247
Belle2::B2Vector3::z
DataType z() const
access variable Z (= .at(2) without boundary check)
Definition: B2Vector3.h:428
Belle2::ECLConnectedRegion
Class to store connected regions (CRs)
Definition: ECLConnectedRegion.h:31
Belle2::FileSystem::findFile
static std::string findFile(const std::string &path, bool silent=false)
Search for given file or directory in local or central release directory, and return absolute path if...
Definition: FileSystem.cc:147
Belle2::ECL::ECLNeighbours::getNeighbours
const std::vector< short int > & getNeighbours(short int cid) const
Return the neighbours for a given cell ID.
Definition: ECLNeighbours.cc:318
Belle2::ECLSplitterN1Module::initialize
virtual void initialize() override
Initialize.
Definition: ECLSplitterN1Module.cc:118
Belle2::Unit::GeV
static const double GeV
Standard of [energy, momentum, mass].
Definition: Unit.h:61
Belle2::ECLSplitterN1Module::terminate
virtual void terminate() override
Terminate.
Definition: ECLSplitterN1Module.cc:235
Belle2::B2Vector3::Theta
DataType Theta() const
The polar angle.
Definition: B2Vector3.h:152
Belle2::B2Vector3::Mag
DataType Mag() const
The magnitude (rho in spherical coordinate system).
Definition: B2Vector3.h:158