10 #include <ecl/modules/eclShowerShape/ECLShowerShapeModule.h>
13 #include <boost/algorithm/string/predicate.hpp>
19 #include <framework/datastore/RelationVector.h>
20 #include <framework/logging/Logger.h>
21 #include <framework/geometry/B2Vector3.h>
24 #include <mva/interface/Expert.h>
25 #include <mva/interface/Weightfile.h>
26 #include <mva/interface/Interface.h>
27 #include <mva/dataobjects/DatabaseRepresentationOfWeightfile.h>
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>
51 m_eclConnectedRegions(eclConnectedRegionArrayName()),
52 m_secondMomentCorrectionArray("ecl_shower_shape_second_moment_corrections")
55 setDescription(
"ECLShowerShapeModule: Calculate ECL shower shape variables (e.g. E9oE21)");
56 setPropertyFlags(c_ParallelProcessingCertified);
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.",
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. ",
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.",
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"});
77 addParam(
"avgCrystalDimension", m_avgCrystalDimension,
78 "Average crystal dimension used in lateral energy calculation.",
90 if (not(boost::ends_with(identifier,
".root") or boost::ends_with(identifier,
".xml"))) {
91 weightFileRepresentation = std::unique_ptr<DBObjPtr<DatabaseRepresentationOfWeightfile>>(
new
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));
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);
121 if (weightFileRepresentation) {
129 if (weightFileRepresentation->hasChanged()) {
130 std::stringstream ss((*weightFileRepresentation)->m_data);
143 if (m_numZernikeMVAvariables != general_options.m_variables.size())
144 B2FATAL(
"Expecting " << m_numZernikeMVAvariables <<
" varibales, found " << general_options.m_variables.size());
146 expert = supported_interfaces[general_options.m_method]->getExpert();
147 expert->load(weightfile);
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));
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);
163 if (m_secondMomentCorrectionArray.hasChanged()) {
164 if (m_secondMomentCorrectionArray) prepareSecondMomentCorrectionsCallback();
165 else B2ERROR(
"ECLShowerShapeModule::beginRun - Couldn't find second moment correction for current run");
173 std::vector<ProjectedECLDigit> projectedECLDigits = projectECLDigits(*eclShower);
175 const double showerEnergy = eclShower->
getEnergy();
176 const double showerTheta = eclShower->
getTheta();
177 const double showerPhi = eclShower->
getPhi();
180 double sumEnergies = 0.0;
181 for (
const auto& projectedECLDigit : projectedECLDigits) sumEnergies += projectedECLDigit.energy;
189 const double absZernike40 = computeAbsZernikeMoment(projectedECLDigits, sumEnergies, 4, 0, rho0);
190 const double absZernike51 = computeAbsZernikeMoment(projectedECLDigits, sumEnergies, 5, 1, rho0);
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 <<
196 const double secondMoment = computeSecondMoment(projectedECLDigits, showerEnergy) * secondMomentCorrection;
197 B2DEBUG(175,
"Second moment after correction: " << secondMoment);
199 const double LATenergy = computeLateralEnergy(projectedECLDigits, m_avgCrystalDimension);
206 eclShower->
setE1oE9(computeE1oE9(*eclShower));
209 if (calculateZernikeMVA) {
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);
231 if (eclShower->
getTheta() < m_BRLthetaMin) eclShower->
setZernikeMVA(m_expert_FWD->apply(*m_dataset)[0]);
233 else if (eclShower->
getTheta() > m_BRLthetaMax) eclShower->
setZernikeMVA(m_expert_BWD->apply(*m_dataset)[0]);
235 else eclShower->
setZernikeMVA(m_expert_BRL->apply(*m_dataset)[0]);
242 for (
auto& eclCR : m_eclConnectedRegions) {
247 for (
auto& eclShower : eclCR.getRelationsWith<
ECLShower>(eclShowerArrayName())) {
249 N2shower = &eclShower;
250 setShowerShapeVariables(N2shower,
true);
256 bool found_N2shower =
true;
257 if (N2shower ==
nullptr) found_N2shower =
false;
259 double prodN1zernikeMVAs = 1.0;
261 for (
auto& eclShower : eclCR.getRelationsWith<
ECLShower>(eclShowerArrayName())) {
265 bool calculateZernikeMVA =
true;
266 if (!found_N2shower || eclShower.getHypothesisId() !=
ECLShower::c_nPhotons) calculateZernikeMVA =
false;
268 setShowerShapeVariables(&eclShower, calculateZernikeMVA);
270 if (eclShower.getHypothesisId() ==
ECLShower::c_nPhotons) prodN1zernikeMVAs *= eclShower.getZernikeMVA();
274 if (N2shower) N2shower->
setZernikeMVA(1.0 - prodN1zernikeMVAs);
280 std::vector<ProjectedECLDigit> tmpProjectedECLDigits;
286 const double showerR = shower.
getR();
287 const double showerTheta = shower.
getTheta();
288 const double showerPhi = shower.
getPhi();
294 const B2Vector3D showerDirection = (1.0 / showerPosition.
Mag()) * showerPosition;
301 xPrimeDirection *= 1.0 / xPrimeDirection.
Mag();
304 yPrimeDirection *= 1.0 / yPrimeDirection.
Mag();
312 for (
unsigned int iRelation = 0; iRelation < showerDigitRelations.size(); ++iRelation) {
313 const auto calDigit = showerDigitRelations.object(iRelation);
320 const auto weight = showerDigitRelations.weight(iRelation);
321 tmpProjectedDigit.
energy = weight * calDigit->getEnergy();
326 const int cellId = calDigit->getCellId();
327 B2Vector3D calDigitPosition = geometry->GetCrystalPos(cellId - 1);
331 const double angleDigitShower = calDigitPosition.
Angle(showerPosition);
332 tmpProjectedDigit.
rho = showerR * TMath::Tan(angleDigitShower);
339 B2Vector3D projectedDigitDirection = calDigitPosition - calDigitPosition.
Dot(showerDirection) * showerDirection;
340 tmpProjectedDigit.
alpha = projectedDigitDirection.
Angle(xPrimeDirection);
343 if (projectedDigitDirection.
Angle(yPrimeDirection) > TMath::Pi() / 2.0)
344 tmpProjectedDigit.
alpha = 2.0 * TMath::Pi() - tmpProjectedDigit.
alpha;
345 tmpProjectedECLDigits.push_back(tmpProjectedDigit);
348 return tmpProjectedECLDigits;
361 const double avgCrystalDimension)
const
365 if (projectedDigits.size() < 3.0)
return 0;
368 double maxEnergy(0), secondMaxEnergy(0);
369 unsigned int iMax(0), iSecondMax(0);
371 for (
unsigned int iProjecteDigit = 0; iProjecteDigit < projectedDigits.size(); iProjecteDigit++) {
372 if (projectedDigits[iProjecteDigit].energy > maxEnergy) {
373 secondMaxEnergy = maxEnergy;
375 maxEnergy = projectedDigits[iProjecteDigit].energy;
376 iMax = iProjecteDigit;
377 }
else if (projectedDigits[iProjecteDigit].energy > secondMaxEnergy) {
378 secondMaxEnergy = projectedDigits[iProjecteDigit].energy;
379 iSecondMax = iProjecteDigit;
385 for (
unsigned int iProjecteDigit = 0; iProjecteDigit < projectedDigits.size(); iProjecteDigit++) {
388 if (iProjecteDigit == iMax || iProjecteDigit == iSecondMax)
continue;
390 double rho = projectedDigits[iProjecteDigit].rho;
391 double rho2 = rho * rho;
392 double energy = projectedDigits[iProjecteDigit].energy;
393 sumE += energy * rho2;
397 const double r0sq = avgCrystalDimension * avgCrystalDimension;
398 return sumE / (sumE + r0sq * (maxEnergy + secondMaxEnergy));
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;
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);
428 if (rho > 1.0)
return std::complex<double>(0, 0);
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;
437 const double totalEnergy,
const int n,
const int m,
const double rho0)
const
439 if (totalEnergy <= 0.0)
return 0.0;
442 if (n < 0 || m < 0)
return 0.0;
443 if (m > n)
return 0.0;
445 std::complex<double> sum(0.0, 0.0);
447 for (
const auto projectedDigit : projectedDigits) {
448 double normalizedRho = projectedDigit.rho / rho0;
450 if (normalizedRho > 1.0) {
451 if (!m_zernike_useFarCrystals)
continue;
452 else normalizedRho = 1.0;
455 sum += projectedDigit.energy * std::conj(zernikeValue(normalizedRho, projectedDigit.alpha, n, m));
457 return (n + 1.0) / TMath::Pi() * std::abs(sum) / totalEnergy;
461 const double totalEnergy)
const
463 if (totalEnergy <= 0.0)
return 0.0;
467 for (
const auto projectedDigit : projectedDigits) sum += projectedDigit.energy * projectedDigit.rho * projectedDigit.rho;
469 return sum / totalEnergy;
478 if (centralCellId == 0)
return 0.0;
481 const std::vector< short int > n9 = m_neighbourMap9->getNeighbours(centralCellId);
483 double energy1 = 0.0;
484 double energy9 = 0.0;
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();
495 if (cellid == centralCellId) {
496 energy1 = weight * energy;
500 const auto it9 = std::find(n9.begin(), n9.end(), cellid);
501 if (it9 != n9.end()) {
502 energy9 += weight * energy;
507 if (energy9 > 1e-9)
return energy1 / energy9;
515 if (centralCellId == 0)
return 0.0;
518 const std::vector< short int > n9 = m_neighbourMap9->getNeighbours(centralCellId);
519 const std::vector< short int > n21 = m_neighbourMap21->getNeighbours(centralCellId);
521 double energy9 = 0.0;
522 double energy21 = 0.0;
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();
533 const auto it9 = std::find(n9.begin(), n9.end(), cellid);
534 if (it9 != n9.end()) {
535 energy9 += weight * energy;
539 const auto it21 = std::find(n21.begin(), n21.end(), cellid);
540 if (it21 != n21.end()) {
541 energy21 += weight * energy;
546 if (energy21 > 1e-9)
return energy9 / energy21;
554 for (
auto iType = 0; iType < 2; ++iType)
555 for (
auto iHypothesis = 0; iHypothesis < 10; ++iHypothesis)
556 m_secondMomentCorrections[iType][iHypothesis] = TGraph();
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.");
566 m_secondMomentCorrections[type][hypothesis] = correction.getCorrection();
574 B2FATAL(
"Missing corrections for second moments..");
581 double thetadeg = theta * TMath::RadToDeg();
584 if (thetadeg < 0.1) thetadeg = 0.1;
585 else if (thetadeg > 179.9) thetadeg = 179.9;
588 double phideg = phi * TMath::RadToDeg();
591 if (phideg < -179.9) phideg = -179.9;
592 else if (phideg > 179.9) phideg = 179.9;
596 if (hypothesis < 1 or hypothesis > 10) {
597 B2FATAL(
"Invalid hypothesis for second moment corrections.");
600 const double thetaCorrection = m_secondMomentCorrections[c_thetaType][hypothesis].Eval(thetadeg);
601 const double phiCorrection = m_secondMomentCorrections[c_phiType][hypothesis].Eval(phideg);
603 B2DEBUG(175,
"Second momen theta crrection = " << thetaCorrection <<
", phi correction = " << phiCorrection);
605 return thetaCorrection * phiCorrection;
B2Vector3< DataType > Cross(const B2Vector3< DataType > &p) const
Cross product.
DataType y() const
access variable Y (= .at(1) without boundary check)
void SetMagThetaPhi(DataType mag, DataType theta, DataType phi)
setter with mag, theta, phi
DataType Mag() const
The magnitude (rho in spherical coordinate system).
DataType x() const
access variable X (= .at(0) without boundary check)
DataType Dot(const B2Vector3< DataType > &p) const
Scalar product.
DataType Angle(const B2Vector3< DataType > &q) const
The angle w.r.t.
Class for accessing objects in the database.
Class to store calibrated ECLDigits: ECLCalDigits.
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.
~ECLShowerShapeModule()
Destructor.
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.
void setE9oE21(double E9oE21)
Set energy ration E9 over E21.
void setLateralEnergy(double lateralEnergy)
Set Lateral Energy.
int getHypothesisId() const
Get Hypothesis Id.
double getPhi() const
Get Phi.
void setAbsZernike51(double absZernike51)
Set absolute value of Zernike moment 51.
double getEnergy() const
Get Energy.
void setSecondMoment(double secondMoment)
Set second moment.
double getR() const
Get R.
void setAbsZernike40(double absZernike40)
Set absolute value of Zernike moment 40.
double getE9oE21() const
Get energy ratio E9oE21.
@ c_neutralHadron
CR is reconstructed as a neutral hadron (N2)
@ c_nPhotons
CR is split into n photons (N1)
int getCentralCellId() const
Get central cell Id.
void setE1oE9(double E1oE9)
Set energy ration E1 over E9.
void setZernikeMVA(double zernikeMVA)
SetZernike MVA value.
double getTheta() const
Get Theta.
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.
static std::map< std::string, AbstractInterface * > getSupportedInterfaces()
Returns interfaces supported by the MVA Interface.
static void initSupportedInterfaces()
Static function which initliazes all supported interfaces, has to be called once before getSupportedI...
General options which are shared by all MVA trainings.
Wraps the data of a single event into a Dataset.
The Weightfile class serializes all information about a training into an xml tree.
static Weightfile loadFromStream(std::istream &stream)
Static function which deserializes a Weightfile from a stream.
void getOptions(Options &options) const
Fills an Option object from the xml tree.
static Weightfile loadFromFile(const std::string &filename)
Static function which loads a Weightfile from a file.
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.
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
B2Vector3< double > B2Vector3D
typedef for common usage with double
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...
double energy
weighted energy
double rho
radial distance