Belle II Software  release-06-00-14
ECLShowerShapeModule.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 // THIS MODULE
10 #include <ecl/modules/eclShowerShape/ECLShowerShapeModule.h>
11 
12 //BOOST
13 #include <boost/algorithm/string/predicate.hpp>
14 
15 // ROOT
16 #include <TMath.h>
17 
18 // FRAMEWORK
19 #include <framework/datastore/RelationVector.h>
20 #include <framework/logging/Logger.h>
21 #include <framework/geometry/B2Vector3.h>
22 
23 //MVA
24 #include <mva/interface/Expert.h>
25 #include <mva/interface/Weightfile.h>
26 #include <mva/interface/Interface.h>
27 #include <mva/dataobjects/DatabaseRepresentationOfWeightfile.h>
28 
29 // ECL
30 #include <ecl/dataobjects/ECLCalDigit.h>
31 #include <ecl/dataobjects/ECLConnectedRegion.h>
32 #include <ecl/geometry/ECLGeometryPar.h>
33 #include <ecl/dataobjects/ECLShower.h>
34 #include <ecl/geometry/ECLNeighbours.h>
35 #include <ecl/dbobjects/ECLShowerShapeSecondMomentCorrection.h>
36 
37 using namespace Belle2;
38 using namespace ECL;
39 
40 //-----------------------------------------------------------------
41 // Register the Module
42 //-----------------------------------------------------------------
43 REG_MODULE(ECLShowerShape)
44 REG_MODULE(ECLShowerShapePureCsI)
45 
46 //-----------------------------------------------------------------
47 // Implementation
48 //-----------------------------------------------------------------
49 
51  m_eclConnectedRegions(eclConnectedRegionArrayName()),
52  m_secondMomentCorrectionArray("ecl_shower_shape_second_moment_corrections")
53 {
54  // Set description
55  setDescription("ECLShowerShapeModule: Calculate ECL shower shape variables (e.g. E9oE21)");
56  setPropertyFlags(c_ParallelProcessingCertified);
57 
58  addParam("zernike_n1_rho0", m_zernike_n1_rho0,
59  "Scaling factor for radial distances in a plane perpendicular to direction to shower for the n photon hypothesis in Zernike moment calculation.",
60  10.0 * Unit::cm);
61 
62  addParam("zernike_n2_rho0", m_zernike_n2_rho0,
63  "Scaling factor for radial distances in a plane perpendicular to direction to shower for the neutral hadron hypothesis in Zernike moment calculation. ",
64  20.0 * Unit::cm);
65 
66  addParam("zernike_useFarCrystals", m_zernike_useFarCrystals,
67  "Determines if Digits with rho > rho0 are used for the zernike moment calculation. If they are, their radial distance will be set to rho0.",
68  true);
69 
70  addParam("zernike_MVAidentifier_FWD", m_zernike_MVAidentifier_FWD, "The Zernike moment MVA database identifier for forward endcap.",
71  std::string{"ecl_showershape_zernike_mva_fwd"});
72  addParam("zernike_MVAidentifier_BRL", m_zernike_MVAidentifier_BRL, "The Zernike moment MVA database identifier for barrel.",
73  std::string{"ecl_showershape_zernike_mva_brl"});
74  addParam("zernike_MVAidentifier_BWD", m_zernike_MVAidentifier_BWD,
75  "The Zernike moment MVA database identifier for backward endcap.", std::string{"ecl_showershape_zernike_mva_bwd"});
76 
77  addParam("avgCrystalDimension", m_avgCrystalDimension,
78  "Average crystal dimension used in lateral energy calculation.",
79  5.0 * Unit::cm);
80 
81 }
82 
84 {
85 }
86 
87 void ECLShowerShapeModule::initializeMVAweightFiles(const std::string& identifier,
88  std::unique_ptr<DBObjPtr<DatabaseRepresentationOfWeightfile>>& weightFileRepresentation)
89 {
90  if (not(boost::ends_with(identifier, ".root") or boost::ends_with(identifier, ".xml"))) {
91  weightFileRepresentation = std::unique_ptr<DBObjPtr<DatabaseRepresentationOfWeightfile>>(new
93  }
95 }
96 
98 {
99 
100  // Initialize neighbour maps.
101  m_neighbourMap9 = std::unique_ptr<ECL::ECLNeighbours>(new ECL::ECLNeighbours("N", 1));
102  m_neighbourMap21 = std::unique_ptr<ECL::ECLNeighbours>(new ECL::ECLNeighbours("NC", 2));
103 
104  initializeMVAweightFiles(m_zernike_MVAidentifier_FWD, m_weightfile_representation_FWD);
105  initializeMVAweightFiles(m_zernike_MVAidentifier_BRL, m_weightfile_representation_BRL);
106  initializeMVAweightFiles(m_zernike_MVAidentifier_BWD, m_weightfile_representation_BWD);
107 
108 
109  //Add callback to fill m_secondMomentCorrections when m_secondMomentCorrectionArray changes
110  //21-Oct-2016 - The callback doesn't seem to be called at the begining of the run so I commented it out and added a call to prepareSecondMomentCorrectionsCallback in the beginRun
111 // m_secondMomentCorrectionArray.addCallback(this, &ECLShowerShapeModule::prepareSecondMomentCorrectionsCallback);
112 // prepareSecondMomentCorrectionsCallback();
113 
114 }
115 
116 void ECLShowerShapeModule::initializeMVA(const std::string& identifier,
117  std::unique_ptr<DBObjPtr<DatabaseRepresentationOfWeightfile>>& weightFileRepresentation, std::unique_ptr<MVA::Expert>& expert)
118 {
119  MVA::Weightfile weightfile;
120  //Load MVA weight file
121  if (weightFileRepresentation) {
122 
123  //If multiple sources of conditions DB have been configured (the regular case), then the IOV of each payload will be "artificially" set to the current run only.
124  //This is so that in the next run, the payload can be taken from a different DB source.
125  //For example, payload of current run will be taken from central DB and payload of next run will be taken from local DB, if it appears there.
126  //In this case weightFileRepresentation->hasChanged() will be true at the beginning of each run, even though the IOV of the payload is greater than a single run.
127  //This is true as of 2017-06-01, functionality of hasChanged() might be changed in future.
128 
129  if (weightFileRepresentation->hasChanged()) {
130  std::stringstream ss((*weightFileRepresentation)->m_data);
131  weightfile = MVA::Weightfile::loadFromStream(ss);
132  } else
133  return;
134  } else {
135  weightfile = MVA::Weightfile::loadFromFile(identifier);
136  }
137 
138  auto supported_interfaces = MVA::AbstractInterface::getSupportedInterfaces();
139  MVA::GeneralOptions general_options;
140  weightfile.getOptions(general_options);
141 
142  //Check number of variables in weight file
143  if (m_numZernikeMVAvariables != general_options.m_variables.size())
144  B2FATAL("Expecting " << m_numZernikeMVAvariables << " varibales, found " << general_options.m_variables.size());
145 
146  expert = supported_interfaces[general_options.m_method]->getExpert();
147  expert->load(weightfile);
148 
149  //create new dataset, if this is the barrel MVA (assumes FWD and BWD datasets are same size)
150  if (weightFileRepresentation == m_weightfile_representation_BRL) {
151  std::vector<float> dummy(general_options.m_variables.size(), 0);
152  m_dataset = std::unique_ptr<MVA::SingleDataset>(new MVA::SingleDataset(general_options, dummy, 0));
153  }
154 }
155 
157 {
158  initializeMVA(m_zernike_MVAidentifier_FWD, m_weightfile_representation_FWD, m_expert_FWD);
159  initializeMVA(m_zernike_MVAidentifier_BRL, m_weightfile_representation_BRL, m_expert_BRL);
160  initializeMVA(m_zernike_MVAidentifier_BWD, m_weightfile_representation_BWD, m_expert_BWD);
161 
162  //This is a hack because the callback doesn't seem to be called at the begining of the run
163  if (m_secondMomentCorrectionArray.hasChanged()) {
164  if (m_secondMomentCorrectionArray) prepareSecondMomentCorrectionsCallback();
165  else B2ERROR("ECLShowerShapeModule::beginRun - Couldn't find second moment correction for current run");
166  }
167 }
168 
169 
170 void ECLShowerShapeModule::setShowerShapeVariables(ECLShower* eclShower, const bool calculateZernikeMVA) const
171 {
172  //Project the digits on the plane perpendicular to the shower direction
173  std::vector<ProjectedECLDigit> projectedECLDigits = projectECLDigits(*eclShower);
174 
175  const double showerEnergy = eclShower->getEnergy();
176  const double showerTheta = eclShower->getTheta();
177  const double showerPhi = eclShower->getPhi();
178 
179  //sum crystal energies
180  double sumEnergies = 0.0;
181  for (const auto& projectedECLDigit : projectedECLDigits) sumEnergies += projectedECLDigit.energy;
182 
183  //Choose rho0 according to shower hypothesis
184  double rho0 = 0.0;
185  const int hypothesisID = eclShower->getHypothesisId();
186  if (hypothesisID == ECLShower::c_nPhotons) rho0 = m_zernike_n1_rho0;
187  else if (hypothesisID == ECLShower::c_neutralHadron) rho0 = m_zernike_n2_rho0;
188 
189  const double absZernike40 = computeAbsZernikeMoment(projectedECLDigits, sumEnergies, 4, 0, rho0);
190  const double absZernike51 = computeAbsZernikeMoment(projectedECLDigits, sumEnergies, 5, 1, rho0);
191 
192  const double secondMomentCorrection = getSecondMomentCorrection(showerTheta, showerPhi, hypothesisID);
193  B2DEBUG(175, "Second moment angular correction: " << secondMomentCorrection << " (theta(rad)=" << showerTheta << ", phi(rad)=" <<
194  showerPhi << ",hypothesisId=" << hypothesisID <<
195  ")");
196  const double secondMoment = computeSecondMoment(projectedECLDigits, showerEnergy) * secondMomentCorrection;
197  B2DEBUG(175, "Second moment after correction: " << secondMoment);
198 
199  const double LATenergy = computeLateralEnergy(projectedECLDigits, m_avgCrystalDimension);
200 
201  // Set shower shape variables.
202  eclShower->setAbsZernike40(absZernike40);
203  eclShower->setAbsZernike51(absZernike51);
204  eclShower->setSecondMoment(secondMoment);
205  eclShower->setLateralEnergy(LATenergy);
206  eclShower->setE1oE9(computeE1oE9(*eclShower));
207  if (eclShower->getE9oE21() < 1e-9) eclShower->setE9oE21(computeE9oE21(*eclShower));
208 
209  if (calculateZernikeMVA) {
210  //Set Zernike moments that will be used in MVA calculation
211  // m_dataset holds 22 entries, 11 Zernike moments of N2 shower, followed by 11 Zernike moments of N1 shower
212  int indexOffset = 0;//Offset entries depending on hypothesis type
213  if (hypothesisID == ECLShower::c_nPhotons) indexOffset = (m_numZernikeMVAvariables / 2);
214  else if (hypothesisID == ECLShower::c_neutralHadron) indexOffset = 0;
215 
216  m_dataset->m_input[0 + indexOffset] = computeAbsZernikeMoment(projectedECLDigits, sumEnergies, 1, 1, rho0);
217  m_dataset->m_input[1 + indexOffset] = computeAbsZernikeMoment(projectedECLDigits, sumEnergies, 2, 0, rho0);
218  m_dataset->m_input[2 + indexOffset] = computeAbsZernikeMoment(projectedECLDigits, sumEnergies, 2, 2, rho0);
219  m_dataset->m_input[3 + indexOffset] = computeAbsZernikeMoment(projectedECLDigits, sumEnergies, 3, 1, rho0);
220  m_dataset->m_input[4 + indexOffset] = computeAbsZernikeMoment(projectedECLDigits, sumEnergies, 3, 3, rho0);
221  m_dataset->m_input[5 + indexOffset] = absZernike40;
222  m_dataset->m_input[6 + indexOffset] = computeAbsZernikeMoment(projectedECLDigits, sumEnergies, 4, 2, rho0);
223  m_dataset->m_input[7 + indexOffset] = computeAbsZernikeMoment(projectedECLDigits, sumEnergies, 4, 4, rho0);
224  m_dataset->m_input[8 + indexOffset] = absZernike51;
225  m_dataset->m_input[9 + indexOffset] = computeAbsZernikeMoment(projectedECLDigits, sumEnergies, 5, 3, rho0);
226  m_dataset->m_input[10 + indexOffset] = computeAbsZernikeMoment(projectedECLDigits, sumEnergies, 5, 5, rho0);
227  //Set zernikeMVA for N1 showers
228  //This assumes that the N2 zernike moments have already been set in m_dataset!!!!
229  if (hypothesisID == ECLShower::c_nPhotons) {
230  //FWD
231  if (eclShower->getTheta() < m_BRLthetaMin) eclShower->setZernikeMVA(m_expert_FWD->apply(*m_dataset)[0]);
232  //BWD
233  else if (eclShower->getTheta() > m_BRLthetaMax) eclShower->setZernikeMVA(m_expert_BWD->apply(*m_dataset)[0]);
234  //BRL
235  else eclShower->setZernikeMVA(m_expert_BRL->apply(*m_dataset)[0]);
236  }
237  } else eclShower->setZernikeMVA(0.0);
238 }
239 
241 {
242  for (auto& eclCR : m_eclConnectedRegions) {
243 
244  //Start by finding the N2 shower and calculating it's shower shape variables
245  //Assumes that there is only 1 N2 Shower per CR!!!!!!
246  ECLShower* N2shower = nullptr;
247  for (auto& eclShower : eclCR.getRelationsWith<ECLShower>(eclShowerArrayName())) {
248  if (eclShower.getHypothesisId() == ECLShower::c_neutralHadron) {
249  N2shower = &eclShower;
250  setShowerShapeVariables(N2shower, true);
251  break;
252  }
253  }
254 
255  //If couldn't find N2 shower, don't calculate zernikeMVA
256  bool found_N2shower = true;
257  if (N2shower == nullptr) found_N2shower = false;
258 
259  double prodN1zernikeMVAs = 1.0;
260  //Calculate shower shape variables for the rest of the showers
261  for (auto& eclShower : eclCR.getRelationsWith<ECLShower>(eclShowerArrayName())) {
262  if (eclShower.getHypothesisId() == ECLShower::c_neutralHadron)
263  continue; //shower shape variables already calculated for neutral hadrons
264 
265  bool calculateZernikeMVA = true;
266  if (!found_N2shower || eclShower.getHypothesisId() != ECLShower::c_nPhotons) calculateZernikeMVA = false;
267 
268  setShowerShapeVariables(&eclShower, calculateZernikeMVA);
269 
270  if (eclShower.getHypothesisId() == ECLShower::c_nPhotons) prodN1zernikeMVAs *= eclShower.getZernikeMVA();
271  }
272 
273  //Set zernikeMVA for the N2 shower
274  if (N2shower) N2shower->setZernikeMVA(1.0 - prodN1zernikeMVAs);
275  }
276 }
277 
278 std::vector<ECLShowerShapeModule::ProjectedECLDigit> ECLShowerShapeModule::projectECLDigits(const ECLShower& shower) const
279 {
280  std::vector<ProjectedECLDigit> tmpProjectedECLDigits; //Will be returned at the end of the function
281  auto showerDigitRelations = shower.getRelationsTo<ECLCalDigit>(eclCalDigitArrayName());
282 // tmpProjectedECLDigits.resize( showerDigitRelations.size() );
283  //---------------------------------------------------------------------
284  // Get shower parameters.
285  //---------------------------------------------------------------------
286  const double showerR = shower.getR();
287  const double showerTheta = shower.getTheta();
288  const double showerPhi = shower.getPhi();
289 
290  B2Vector3D showerPosition;
291  showerPosition.SetMagThetaPhi(showerR, showerTheta, showerPhi);
292 
293  // Unit vector pointing in shower direction.
294  const B2Vector3D showerDirection = (1.0 / showerPosition.Mag()) * showerPosition;
295 
296  //---------------------------------------------------------------------
297  // Calculate axes that span the perpendicular plane.
298  //---------------------------------------------------------------------
299  //xPrimeDirection = showerdirection.cross(zAxis)
300  B2Vector3D xPrimeDirection = B2Vector3D(showerPosition.y(), -showerPosition.x(), 0.0);
301  xPrimeDirection *= 1.0 / xPrimeDirection.Mag();
302 
303  B2Vector3D yPrimeDirection = xPrimeDirection.Cross(showerDirection);
304  yPrimeDirection *= 1.0 / yPrimeDirection.Mag();
305 
306  //---------------------------------------------------------------------
307  // Loop on CalDigits in shower and calculate the projection.
308  //---------------------------------------------------------------------
309 
311 
312  for (unsigned int iRelation = 0; iRelation < showerDigitRelations.size(); ++iRelation) {
313  const auto calDigit = showerDigitRelations.object(iRelation);
314 
315  ProjectedECLDigit tmpProjectedDigit;
316 
317  //---------------------------------------------------------------------
318  // Projected digit energy.
319  //---------------------------------------------------------------------
320  const auto weight = showerDigitRelations.weight(iRelation);
321  tmpProjectedDigit.energy = weight * calDigit->getEnergy();
322 
323  //---------------------------------------------------------------------
324  // Projected digit radial distance.
325  //---------------------------------------------------------------------
326  const int cellId = calDigit->getCellId();
327  B2Vector3D calDigitPosition = geometry->GetCrystalPos(cellId - 1);
328 
329  // Angle between vector pointing to shower and vector pointing to CalDigit,
330  //where the orgin is the detector origin (implicitly assuming IP = detector origin)
331  const double angleDigitShower = calDigitPosition.Angle(showerPosition);
332  tmpProjectedDigit.rho = showerR * TMath::Tan(angleDigitShower);
333 
334  //---------------------------------------------------------------------
335  // Projected digit polar angle
336  //---------------------------------------------------------------------
337  // Vector perpendicular to the vector pointing to the shower position, pointing to the CalDigit.
338  // It's length is not rho. Not normalized!!! We only care about the angle between in and xPrime.
339  B2Vector3D projectedDigitDirection = calDigitPosition - calDigitPosition.Dot(showerDirection) * showerDirection;
340  tmpProjectedDigit.alpha = projectedDigitDirection.Angle(xPrimeDirection);
341 
342  // adjust so that alpha spans 0..2pi
343  if (projectedDigitDirection.Angle(yPrimeDirection) > TMath::Pi() / 2.0)
344  tmpProjectedDigit.alpha = 2.0 * TMath::Pi() - tmpProjectedDigit.alpha;
345  tmpProjectedECLDigits.push_back(tmpProjectedDigit);
346  }
347 
348  return tmpProjectedECLDigits;
349 }
350 
352 {
353 }
354 
356 {
357 
358 }
359 
360 double ECLShowerShapeModule::computeLateralEnergy(const std::vector<ProjectedECLDigit>& projectedDigits,
361  const double avgCrystalDimension) const
362 {
363 
364 // auto relatedDigitsPairs = shower.getRelationsTo<ECLCalDigit>();
365  if (projectedDigits.size() < 3.0) return 0;
366 
367  // Find the two projected digits with the maximum energy.
368  double maxEnergy(0), secondMaxEnergy(0);
369  unsigned int iMax(0), iSecondMax(0);
370 
371  for (unsigned int iProjecteDigit = 0; iProjecteDigit < projectedDigits.size(); iProjecteDigit++) {
372  if (projectedDigits[iProjecteDigit].energy > maxEnergy) {
373  secondMaxEnergy = maxEnergy;
374  iSecondMax = iMax;
375  maxEnergy = projectedDigits[iProjecteDigit].energy;
376  iMax = iProjecteDigit;
377  } else if (projectedDigits[iProjecteDigit].energy > secondMaxEnergy) {
378  secondMaxEnergy = projectedDigits[iProjecteDigit].energy;
379  iSecondMax = iProjecteDigit;
380  }
381  }
382 
383  //Calculate lateral energy
384  double sumE = 0;
385  for (unsigned int iProjecteDigit = 0; iProjecteDigit < projectedDigits.size(); iProjecteDigit++) {
386 
387  //2 highest energies are considered differently than the rest
388  if (iProjecteDigit == iMax || iProjecteDigit == iSecondMax) continue;
389 
390  double rho = projectedDigits[iProjecteDigit].rho;
391  double rho2 = rho * rho;
392  double energy = projectedDigits[iProjecteDigit].energy;
393  sumE += energy * rho2;
394 
395  }
396 
397  const double r0sq = avgCrystalDimension * avgCrystalDimension; // average crystal dimension squared.
398  return sumE / (sumE + r0sq * (maxEnergy + secondMaxEnergy));
399 }
400 
401 double ECLShowerShapeModule::Rnm(const int n, const int m, const double rho) const
402 {
403  // Some explicit polynomials.
404  if (n == 1 && m == 1) return rho;
405  if (n == 2 && m == 0) return 2.0 * rho * rho - 1.0;
406  if (n == 2 && m == 2) return rho * rho;
407  if (n == 3 && m == 1) return 3.0 * rho * rho * rho - 2.0 * rho;
408  if (n == 3 && m == 3) return rho * rho * rho;
409  if (n == 4 && m == 0) return 6.0 * rho * rho * rho * rho - 6.0 * rho * rho + 1.0;
410  if (n == 4 && m == 2) return 4.0 * rho * rho * rho * rho - 3.0 * rho * rho;
411  if (n == 4 && m == 4) return rho * rho * rho * rho;
412  if (n == 5 && m == 1) return 10.0 * rho * rho * rho * rho * rho - 12.0 * rho * rho * rho + 3.0 * rho;
413  if (n == 5 && m == 3) return 5.0 * rho * rho * rho * rho * rho - 4.0 * rho * rho * rho;
414  if (n == 5 && m == 5) return rho * rho * rho * rho * rho;
415 
416  // Otherwise compute explicitely.
417  double returnVal = 0;
418  for (int idx = 0; idx <= (n - std::abs(m)) / 2; ++idx)
419  returnVal += std::pow(-1, idx) * TMath::Factorial(n - idx) / TMath::Factorial(idx)
420  / TMath::Factorial((n + std::abs(m)) / 2 - idx) / TMath::Factorial((n - std::abs(m)) / 2 - idx) * std::pow(rho, n - 2 * idx);
421 
422  return returnVal;
423 }
424 
425 std::complex<double> ECLShowerShapeModule::zernikeValue(const double rho, const double alpha, const int n, const int m) const
426 {
427  // Zernike moment defined only on the unit cercile (rho < 1).
428  if (rho > 1.0) return std::complex<double>(0, 0);
429 
430  std::complex<double> i(0, 1);
431  std::complex<double> exponent = std::exp(i * std::complex<double>(m * alpha, 0));
432  return std::complex<double>(Rnm(n, m, rho), 0) * exponent;
433 }
434 
435 
436 double ECLShowerShapeModule::computeAbsZernikeMoment(const std::vector<ProjectedECLDigit>& projectedDigits,
437  const double totalEnergy, const int n, const int m, const double rho0) const
438 {
439  if (totalEnergy <= 0.0) return 0.0;
440 
441  // Make sure n,m are valid
442  if (n < 0 || m < 0) return 0.0;
443  if (m > n) return 0.0;
444 
445  std::complex<double> sum(0.0, 0.0);
446 
447  for (const auto projectedDigit : projectedDigits) {
448  double normalizedRho = projectedDigit.rho / rho0; // Normalize radial distance according to rho0.
449  //Ignore crystals with rho > rho0, if requested
450  if (normalizedRho > 1.0) {
451  if (!m_zernike_useFarCrystals) continue;
452  else normalizedRho = 1.0; //crystals with rho > rho0 are scaled to rho0 instead of discarded
453  }
454 
455  sum += projectedDigit.energy * std::conj(zernikeValue(normalizedRho, projectedDigit.alpha, n, m));
456  }
457  return (n + 1.0) / TMath::Pi() * std::abs(sum) / totalEnergy;
458 }
459 
460 double ECLShowerShapeModule::computeSecondMoment(const std::vector<ProjectedECLDigit>& projectedDigits,
461  const double totalEnergy) const
462 {
463  if (totalEnergy <= 0.0) return 0.0;
464 
465  double sum = 0.0;
466 
467  for (const auto projectedDigit : projectedDigits) sum += projectedDigit.energy * projectedDigit.rho * projectedDigit.rho;
468 
469  return sum / totalEnergy;
470 }
471 
472 
474 {
475 
476  // get central id
477  const int centralCellId = shower.getCentralCellId();
478  if (centralCellId == 0) return 0.0; //cell id starts at 1
479 
480  // get list of 9 neighbour ids
481  const std::vector< short int > n9 = m_neighbourMap9->getNeighbours(centralCellId);
482 
483  double energy1 = 0.0; // to check: 'highest energy' data member may not always be the right one
484  double energy9 = 0.0;
485 
486  auto relatedDigitsPairs = shower.getRelationsTo<ECLCalDigit>(eclCalDigitArrayName());
487 
488  for (unsigned int iRel = 0; iRel < relatedDigitsPairs.size(); iRel++) {
489  const auto caldigit = relatedDigitsPairs.object(iRel);
490  const auto weight = relatedDigitsPairs.weight(iRel);
491  const auto energy = caldigit->getEnergy();
492  const int cellid = caldigit->getCellId();
493 
494  // get central cell id energy
495  if (cellid == centralCellId) {
496  energy1 = weight * energy;
497  }
498 
499  // check if this is contained in the 9 neighbours
500  const auto it9 = std::find(n9.begin(), n9.end(), cellid);
501  if (it9 != n9.end()) {
502  energy9 += weight * energy;
503  }
504 
505  }
506 
507  if (energy9 > 1e-9) return energy1 / energy9;
508  else return 0.0;
509 }
510 
512 {
513  // get central id
514  const int centralCellId = shower.getCentralCellId();
515  if (centralCellId == 0) return 0.0; //cell id starts at 1
516 
517  // get list of 9 and 21 neighbour ids
518  const std::vector< short int > n9 = m_neighbourMap9->getNeighbours(centralCellId);
519  const std::vector< short int > n21 = m_neighbourMap21->getNeighbours(centralCellId);
520 
521  double energy9 = 0.0;
522  double energy21 = 0.0;
523 
524  auto relatedDigitsPairs = shower.getRelationsTo<ECLCalDigit>(eclCalDigitArrayName());
525 
526  for (unsigned int iRel = 0; iRel < relatedDigitsPairs.size(); iRel++) {
527  const auto caldigit = relatedDigitsPairs.object(iRel);
528  const auto weight = relatedDigitsPairs.weight(iRel);
529  const auto energy = caldigit->getEnergy();
530  const int cellid = caldigit->getCellId();
531 
532  // check if this is contained in the 9 neighbours
533  const auto it9 = std::find(n9.begin(), n9.end(), cellid);
534  if (it9 != n9.end()) {
535  energy9 += weight * energy;
536  }
537 
538  // check if this is contained in the 21 neighbours
539  const auto it21 = std::find(n21.begin(), n21.end(), cellid);
540  if (it21 != n21.end()) {
541  energy21 += weight * energy;
542  }
543 
544  }
545 
546  if (energy21 > 1e-9) return energy9 / energy21;
547  else return 0.0;
548 
549 }
550 
552 {
553  //Clear m_secondMomentCorrections array
554  for (auto iType = 0; iType < 2; ++iType)
555  for (auto iHypothesis = 0; iHypothesis < 10; ++iHypothesis)
556  m_secondMomentCorrections[iType][iHypothesis] = TGraph();
557 
558  // Read all corrections.
559  for (const ECLShowerShapeSecondMomentCorrection& correction : m_secondMomentCorrectionArray) {
560  const int type = correction.getType();
561  const int hypothesis = correction.getHypothesisId();
562  if (type < 0 or type > 1 or hypothesis < 1 or hypothesis > 9) {
563  B2FATAL("Invalid type or hypothesis for second moment corrections.");
564  }
565 
566  m_secondMomentCorrections[type][hypothesis] = correction.getCorrection();
567  }
568 
569  // Check that all corrections are there
570  if (m_secondMomentCorrections[c_thetaType][ECLShower::c_nPhotons].GetN() == 0 or
571  m_secondMomentCorrections[c_phiType][ECLShower::c_nPhotons].GetN() == 0 or
572  m_secondMomentCorrections[c_thetaType][ECLShower::c_neutralHadron].GetN() == 0 or
573  m_secondMomentCorrections[c_phiType][ECLShower::c_neutralHadron].GetN() == 0) {
574  B2FATAL("Missing corrections for second moments..");
575  }
576 }
577 
578 double ECLShowerShapeModule::getSecondMomentCorrection(const double theta, const double phi, const int hypothesis) const
579 {
580  // convert to deg.
581  double thetadeg = theta * TMath::RadToDeg();
582 
583  // protect angular range.
584  if (thetadeg < 0.1) thetadeg = 0.1;
585  else if (thetadeg > 179.9) thetadeg = 179.9;
586 
587  //Convert phi
588  double phideg = phi * TMath::RadToDeg();
589 
590  //Protect phi
591  if (phideg < -179.9) phideg = -179.9;
592  else if (phideg > 179.9) phideg = 179.9;
593 
594 
595  // protect hypothesis.
596  if (hypothesis < 1 or hypothesis > 10) {
597  B2FATAL("Invalid hypothesis for second moment corrections.");
598  }
599 
600  const double thetaCorrection = m_secondMomentCorrections[c_thetaType][hypothesis].Eval(thetadeg);
601  const double phiCorrection = m_secondMomentCorrections[c_phiType][hypothesis].Eval(phideg);
602 
603  B2DEBUG(175, "Second momen theta crrection = " << thetaCorrection << ", phi correction = " << phiCorrection);
604 
605  return thetaCorrection * phiCorrection;
606 }
B2Vector3< DataType > Cross(const B2Vector3< DataType > &p) const
Cross product.
Definition: B2Vector3.h:281
DataType y() const
access variable Y (= .at(1) without boundary check)
Definition: B2Vector3.h:412
void SetMagThetaPhi(DataType mag, DataType theta, DataType phi)
setter with mag, theta, phi
Definition: B2Vector3.h:244
DataType Mag() const
The magnitude (rho in spherical coordinate system).
Definition: B2Vector3.h:144
DataType x() const
access variable X (= .at(0) without boundary check)
Definition: B2Vector3.h:410
DataType Dot(const B2Vector3< DataType > &p) const
Scalar product.
Definition: B2Vector3.h:275
DataType Angle(const B2Vector3< DataType > &q) const
The angle w.r.t.
Definition: B2Vector3.h:287
Class for accessing objects in the database.
Definition: DBObjPtr.h:21
Class to store calibrated ECLDigits: ECLCalDigits.
Definition: ECLCalDigit.h:23
Class to perform the shower correction.
double computeAbsZernikeMoment(const std::vector< ProjectedECLDigit > &projectedDigits, const double totalEnergy, const int n, const int m, const double rho) const
Compute the absolute value of the complex Zernike moment Znm.
virtual void initialize() override
Initialize.
double computeE1oE9(const ECLShower &) const
Shower shape variable: E1oE9 The energy ratio is calculated taking the weighted central (=1) and the ...
virtual void event() override
Event.
double getSecondMomentCorrection(const double theta, const double phi, const int hypothesis) const
Get corrections for second moment.
virtual void endRun() override
End run.
std::vector< ProjectedECLDigit > projectECLDigits(const ECLShower &shower) const
Compute projections of the ECLCalDigits to the perpendicular plane.
void initializeMVAweightFiles(const std::string &identifier, std::unique_ptr< DBObjPtr< DatabaseRepresentationOfWeightfile >> &weightFileRepresentation)
initialize MVA weight files from DB
virtual void terminate() override
Terminate.
double computeSecondMoment(const std::vector< ProjectedECLDigit > &shower, const double totalEnergy) const
Compute the second moment in the plane perpendicular to the direction of the shower.
virtual void beginRun() override
Begin run.
std::complex< double > zernikeValue(const double rho, const double alpha, const int n, const int m) const
Return the complex value of the Zernike polynomial of rank n,m.
double Rnm(const int n, const int m, const double rho) const
The radial part of the Zernike polynomial n,m - Zernike polynomial rank rho - radial distance
void prepareSecondMomentCorrectionsCallback()
Prepare corrections for second moment Will be called whenever the m_secondMomentCorrectionArray get u...
void setShowerShapeVariables(ECLShower *eclShower, const bool calculateZernikeMVA) const
Set showr shape variables.
double computeLateralEnergy(const std::vector< ProjectedECLDigit > &projectedDigits, const double avgCrystalDimension) const
Shower shape variable: Lateral energy.
double computeE9oE21(const ECLShower &) const
Shower shape variable: E9oE21 The energy ratio is calculated taking the weighted 3x3 (=9) and the wei...
void initializeMVA(const std::string &identifier, std::unique_ptr< DBObjPtr< DatabaseRepresentationOfWeightfile >> &weightFileRepresentation, std::unique_ptr< MVA::Expert > &expert)
Load MVA weight file and set pointer of expert.
Corrections to the second moment shower shape.
Class to store ECL Showers.
Definition: ECLShower.h:25
void setE9oE21(double E9oE21)
Set energy ration E9 over E21.
Definition: ECLShower.h:209
void setLateralEnergy(double lateralEnergy)
Set Lateral Energy.
Definition: ECLShower.h:169
int getHypothesisId() const
Get Hypothesis Id.
Definition: ECLShower.h:259
double getPhi() const
Get Phi.
Definition: ECLShower.h:284
void setAbsZernike51(double absZernike51)
Set absolute value of Zernike moment 51.
Definition: ECLShower.h:193
double getEnergy() const
Get Energy.
Definition: ECLShower.h:269
void setSecondMoment(double secondMoment)
Set second moment.
Definition: ECLShower.h:201
double getR() const
Get R.
Definition: ECLShower.h:289
void setAbsZernike40(double absZernike40)
Set absolute value of Zernike moment 40.
Definition: ECLShower.h:189
double getE9oE21() const
Get energy ratio E9oE21.
Definition: ECLShower.h:384
@ c_neutralHadron
CR is reconstructed as a neutral hadron (N2)
Definition: ECLShower.h:39
@ c_nPhotons
CR is split into n photons (N1)
Definition: ECLShower.h:37
int getCentralCellId() const
Get central cell Id.
Definition: ECLShower.h:264
void setE1oE9(double E1oE9)
Set energy ration E1 over E9.
Definition: ECLShower.h:205
void setZernikeMVA(double zernikeMVA)
SetZernike MVA value.
Definition: ECLShower.h:197
double getTheta() const
Get Theta.
Definition: ECLShower.h:279
The Class for ECL Geometry Parameters.
static ECLGeometryPar * Instance()
Static method to get a reference to the ECLGeometryPar instance.
Class to get the neighbours for a given cell id.
Definition: ECLNeighbours.h:23
static std::map< std::string, AbstractInterface * > getSupportedInterfaces()
Returns interfaces supported by the MVA Interface.
Definition: Interface.h:53
static void initSupportedInterfaces()
Static function which initliazes all supported interfaces, has to be called once before getSupportedI...
Definition: Interface.cc:45
General options which are shared by all MVA trainings.
Definition: Options.h:62
Wraps the data of a single event into a Dataset.
Definition: Dataset.h:133
The Weightfile class serializes all information about a training into an xml tree.
Definition: Weightfile.h:38
static Weightfile loadFromStream(std::istream &stream)
Static function which deserializes a Weightfile from a stream.
Definition: Weightfile.cc:250
void getOptions(Options &options) const
Fills an Option object from the xml tree.
Definition: Weightfile.cc:66
static Weightfile loadFromFile(const std::string &filename)
Static function which loads a Weightfile from a file.
Definition: Weightfile.cc:205
Base class for Modules.
Definition: Module.h:72
RelationVector< TO > getRelationsTo(const std::string &name="", const std::string &namedRelation="") const
Get the relations that point from this object to another store array.
static const double cm
Standard units with the value = 1.
Definition: Unit.h:47
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
B2Vector3< double > B2Vector3D
typedef for common usage with double
Definition: B2Vector3.h:493
Abstract base class for different kinds of events.
Struct used to hold information of the digits projected to a plane perpendicular to the shower direct...