10#include <ecl/modules/eclShowerShape/ECLShowerShapeModule.h>
16#include <framework/datastore/RelationVector.h>
17#include <framework/logging/Logger.h>
18#include <framework/geometry/B2Vector3.h>
21#include <mva/interface/Expert.h>
22#include <mva/interface/Weightfile.h>
23#include <mva/interface/Interface.h>
24#include <mva/dataobjects/DatabaseRepresentationOfWeightfile.h>
27#include <ecl/dataobjects/ECLCalDigit.h>
28#include <ecl/dataobjects/ECLConnectedRegion.h>
29#include <ecl/geometry/ECLGeometryPar.h>
30#include <ecl/dataobjects/ECLShower.h>
31#include <ecl/geometry/ECLNeighbours.h>
32#include <ecl/dbobjects/ECLShowerShapeSecondMomentCorrection.h>
52 setDescription(
"ECLShowerShapeModule: Calculate ECL shower shape variables (e.g. E9oE21)");
56 "Scaling factor for radial distances in a plane perpendicular to direction to shower for the n photon hypothesis in Zernike moment calculation.",
60 "Scaling factor for radial distances in a plane perpendicular to direction to shower for the neutral hadron hypothesis in Zernike moment calculation. ",
64 "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 std::string{
"ecl_showershape_zernike_mva_fwd"});
70 std::string{
"ecl_showershape_zernike_mva_brl"});
72 "The Zernike moment MVA database identifier for backward endcap.", std::string{
"ecl_showershape_zernike_mva_bwd"});
75 "Average crystal dimension used in lateral energy calculation.",
87 if (not(identifier.ends_with(
".root") or identifier.ends_with(
".xml"))) {
88 weightFileRepresentation = std::unique_ptr<DBObjPtr<DatabaseRepresentationOfWeightfile>>(
new
125 if (weightFileRepresentation) {
133 if (weightFileRepresentation->hasChanged()) {
134 std::stringstream ss((*weightFileRepresentation)->m_data);
144 weightfile.getOptions(general_options);
150 expert = supported_interfaces[general_options.m_method]->getExpert();
151 expert->load(weightfile);
155 std::vector<float> dummy(general_options.m_variables.size(), 0);
169 else B2ERROR(
"ECLShowerShapeModule::beginRun - Couldn't find second moment correction for current run");
177 std::vector<ProjectedECLDigit> projectedECLDigits =
projectECLDigits(*eclShower);
179 const double showerEnergy = eclShower->
getEnergy();
180 const double showerTheta = eclShower->
getTheta();
181 const double showerPhi = eclShower->
getPhi();
184 double sumEnergies = 0.0;
185 for (
const auto& projectedECLDigit : projectedECLDigits) sumEnergies += projectedECLDigit.energy;
194 B2DEBUG(175,
"Second moment angular correction: " << secondMomentCorrection <<
" (theta(rad)=" << showerTheta <<
", phi(rad)=" <<
195 showerPhi <<
",hypothesisId=" << hypothesisID <<
197 const double secondMoment =
computeSecondMoment(projectedECLDigits, showerEnergy) * secondMomentCorrection;
198 B2DEBUG(175,
"Second moment after correction: " << secondMoment);
207 for (
unsigned int n = 1; n <= 5; n++) {
208 for (
unsigned int m = 0; m <= n; m++) {
213 if (calculateZernikeMVA) {
253 N2shower = &eclShower;
260 bool found_N2shower =
true;
261 if (N2shower ==
nullptr) found_N2shower =
false;
263 double prodN1zernikeMVAs = 1.0;
269 bool calculateZernikeMVA =
true;
270 if (!found_N2shower || eclShower.getHypothesisId() !=
ECLShower::c_nPhotons) calculateZernikeMVA =
false;
274 if (eclShower.getHypothesisId() ==
ECLShower::c_nPhotons) prodN1zernikeMVAs *= eclShower.getZernikeMVA();
278 if (N2shower) N2shower->
setZernikeMVA(1.0 - prodN1zernikeMVAs);
284 std::vector<ProjectedECLDigit> tmpProjectedECLDigits;
290 const double showerR = shower.
getR();
291 const double showerTheta = shower.
getTheta();
292 const double showerPhi = shower.
getPhi();
298 const B2Vector3D showerDirection = (1.0 / showerPosition.
Mag()) * showerPosition;
305 xPrimeDirection *= 1.0 / xPrimeDirection.
Mag();
308 yPrimeDirection *= 1.0 / yPrimeDirection.
Mag();
316 for (
unsigned int iRelation = 0; iRelation < showerDigitRelations.size(); ++iRelation) {
317 const auto calDigit = showerDigitRelations.object(iRelation);
324 const auto weight = showerDigitRelations.weight(iRelation);
325 tmpProjectedDigit.
energy = weight * calDigit->getEnergy();
330 const int cellId = calDigit->getCellId();
335 const double angleDigitShower = calDigitPosition.
Angle(showerPosition);
336 tmpProjectedDigit.
rho = showerR * TMath::Tan(angleDigitShower);
343 B2Vector3D projectedDigitDirection = calDigitPosition - calDigitPosition.
Dot(showerDirection) * showerDirection;
344 tmpProjectedDigit.
alpha = projectedDigitDirection.
Angle(xPrimeDirection);
347 if (projectedDigitDirection.
Angle(yPrimeDirection) > TMath::Pi() / 2.0)
348 tmpProjectedDigit.
alpha = 2.0 * TMath::Pi() - tmpProjectedDigit.
alpha;
349 tmpProjectedECLDigits.push_back(tmpProjectedDigit);
352 return tmpProjectedECLDigits;
365 const double avgCrystalDimension)
const
369 if (projectedDigits.size() < 3.0)
return 0;
372 double maxEnergy(0), secondMaxEnergy(0);
373 unsigned int iMax(0), iSecondMax(0);
375 for (
unsigned int iProjecteDigit = 0; iProjecteDigit < projectedDigits.size(); iProjecteDigit++) {
376 if (projectedDigits[iProjecteDigit].energy > maxEnergy) {
377 secondMaxEnergy = maxEnergy;
379 maxEnergy = projectedDigits[iProjecteDigit].energy;
380 iMax = iProjecteDigit;
381 }
else if (projectedDigits[iProjecteDigit].energy > secondMaxEnergy) {
382 secondMaxEnergy = projectedDigits[iProjecteDigit].energy;
383 iSecondMax = iProjecteDigit;
389 for (
unsigned int iProjecteDigit = 0; iProjecteDigit < projectedDigits.size(); iProjecteDigit++) {
392 if (iProjecteDigit == iMax || iProjecteDigit == iSecondMax)
continue;
394 double rho = projectedDigits[iProjecteDigit].rho;
395 double rho2 = rho * rho;
396 double energy = projectedDigits[iProjecteDigit].energy;
397 sumE += energy * rho2;
401 const double r0sq = avgCrystalDimension * avgCrystalDimension;
402 return sumE / (sumE + r0sq * (maxEnergy + secondMaxEnergy));
408 if (n == 1 && m == 1)
return rho;
409 if (n == 2 && m == 0)
return 2.0 * rho * rho - 1.0;
410 if (n == 2 && m == 2)
return rho * rho;
411 if (n == 3 && m == 1)
return 3.0 * rho * rho * rho - 2.0 * rho;
412 if (n == 3 && m == 3)
return rho * rho * rho;
413 if (n == 4 && m == 0)
return 6.0 * rho * rho * rho * rho - 6.0 * rho * rho + 1.0;
414 if (n == 4 && m == 2)
return 4.0 * rho * rho * rho * rho - 3.0 * rho * rho;
415 if (n == 4 && m == 4)
return rho * rho * rho * rho;
416 if (n == 5 && m == 1)
return 10.0 * rho * rho * rho * rho * rho - 12.0 * rho * rho * rho + 3.0 * rho;
417 if (n == 5 && m == 3)
return 5.0 * rho * rho * rho * rho * rho - 4.0 * rho * rho * rho;
418 if (n == 5 && m == 5)
return rho * rho * rho * rho * rho;
421 double returnVal = 0;
422 for (
int idx = 0; idx <= (n - std::abs(m)) / 2; ++idx)
423 returnVal += std::pow(-1, idx) * TMath::Factorial(n - idx) / TMath::Factorial(idx)
424 / TMath::Factorial((n + std::abs(m)) / 2 - idx) / TMath::Factorial((n - std::abs(m)) / 2 - idx) * std::pow(rho, n - 2 * idx);
432 if (rho > 1.0)
return std::complex<double>(0, 0);
434 std::complex<double> i(0, 1);
435 std::complex<double> exponent = std::exp(i * std::complex<double>(m * alpha, 0));
436 return std::complex<double>(
Rnm(n, m, rho), 0) * exponent;
441 const double totalEnergy,
const int n,
const int m,
const double rho0)
const
443 if (totalEnergy <= 0.0)
return 0.0;
446 if (n < 0 || m < 0)
return 0.0;
447 if (m > n)
return 0.0;
449 std::complex<double> sum(0.0, 0.0);
451 for (
const auto projectedDigit : projectedDigits) {
452 double normalizedRho = projectedDigit.rho / rho0;
454 if (normalizedRho > 1.0) {
456 else normalizedRho = 1.0;
459 sum += projectedDigit.energy * std::conj(
zernikeValue(normalizedRho, projectedDigit.alpha, n, m));
461 return (n + 1.0) / TMath::Pi() * std::abs(sum) / totalEnergy;
465 const double totalEnergy)
const
467 if (totalEnergy <= 0.0)
return 0.0;
471 for (
const auto projectedDigit : projectedDigits) sum += projectedDigit.energy * projectedDigit.rho * projectedDigit.rho;
473 return sum / totalEnergy;
482 if (centralCellId == 0)
return 0.0;
485 const std::vector< short int > n9 =
m_neighbourMap9->getNeighbours(centralCellId);
487 double energy1 = 0.0;
488 double energy9 = 0.0;
492 for (
unsigned int iRel = 0; iRel < relatedDigitsPairs.size(); iRel++) {
493 const auto caldigit = relatedDigitsPairs.object(iRel);
494 const auto weight = relatedDigitsPairs.weight(iRel);
495 const auto energy = caldigit->getEnergy();
496 const int cellid = caldigit->getCellId();
499 if (cellid == centralCellId) {
500 energy1 = weight * energy;
504 const auto it9 = std::find(n9.begin(), n9.end(), cellid);
505 if (it9 != n9.end()) {
506 energy9 += weight * energy;
511 if (energy9 > 1e-9)
return energy1 / energy9;
519 if (centralCellId == 0)
return 0.0;
522 const std::vector< short int > n9 =
m_neighbourMap9->getNeighbours(centralCellId);
523 const std::vector< short int > n21 =
m_neighbourMap21->getNeighbours(centralCellId);
525 double energy9 = 0.0;
526 double energy21 = 0.0;
530 for (
unsigned int iRel = 0; iRel < relatedDigitsPairs.size(); iRel++) {
531 const auto caldigit = relatedDigitsPairs.object(iRel);
532 const auto weight = relatedDigitsPairs.weight(iRel);
533 const auto energy = caldigit->getEnergy();
534 const int cellid = caldigit->getCellId();
537 const auto it9 = std::find(n9.begin(), n9.end(), cellid);
538 if (it9 != n9.end()) {
539 energy9 += weight * energy;
543 const auto it21 = std::find(n21.begin(), n21.end(), cellid);
544 if (it21 != n21.end()) {
545 energy21 += weight * energy;
550 if (energy21 > 1e-9)
return energy9 / energy21;
558 for (
auto iType = 0; iType < 2; ++iType)
559 for (
auto iHypothesis = 0; iHypothesis < 10; ++iHypothesis)
564 const int type = correction.getType();
565 const int hypothesis = correction.getHypothesisId();
566 if (type < 0 or type > 1 or hypothesis < 1 or hypothesis > 9) {
567 B2FATAL(
"Invalid type or hypothesis for second moment corrections.");
578 B2FATAL(
"Missing corrections for second moments..");
585 double thetadeg = theta * TMath::RadToDeg();
588 if (thetadeg < 0.1) thetadeg = 0.1;
589 else if (thetadeg > 179.9) thetadeg = 179.9;
592 double phideg = phi * TMath::RadToDeg();
595 if (phideg < -179.9) phideg = -179.9;
596 else if (phideg > 179.9) phideg = 179.9;
600 if (hypothesis < 1 or hypothesis > 10) {
601 B2FATAL(
"Invalid hypothesis for second moment corrections.");
607 B2DEBUG(175,
"Second momen theta crrection = " << thetaCorrection <<
", phi correction = " << phiCorrection);
609 return thetaCorrection * phiCorrection;
B2Vector3< DataType > Cross(const B2Vector3< DataType > &p) const
Cross product.
void SetMagThetaPhi(DataType mag, DataType theta, DataType phi)
setter with mag, theta, phi
DataType X() const
access variable X (= .at(0) without boundary check)
DataType Y() const
access variable Y (= .at(1) without boundary check)
DataType Mag() const
The magnitude (rho in spherical coordinate system).
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.
std::unique_ptr< DBObjPtr< DatabaseRepresentationOfWeightfile > > m_weightfile_representation_BWD
Database pointer to the Database representation of the Zernike moment MVA weightfile for BWD.
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.
const double m_BRLthetaMin
Minimum theta of barrel used for choosing which Zernike MVA to apply.
virtual const char * eclShowerArrayName() const
We need names for the data objects to differentiate between PureCsI and default.
~ECLShowerShapeModule()
Destructor.
DBArray< ECLShowerShapeSecondMomentCorrection > m_secondMomentCorrectionArray
Shower shape corrections from DB.
std::string m_zernike_MVAidentifier_FWD
Zernike moment MVA - FWD endcap weight-file.
std::unique_ptr< MVA::Expert > m_expert_BRL
Pointer to the current MVA Expert for BRL.
StoreArray< ECLConnectedRegion > m_eclConnectedRegions
StoreArray ECLConnectedRegion.
std::unique_ptr< MVA::SingleDataset > m_dataset
Pointer to the current dataset.
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 ...
std::unique_ptr< DBObjPtr< DatabaseRepresentationOfWeightfile > > m_weightfile_representation_BRL
Database pointer to the Database representation of the Zernike moment MVA weightfile for BRL.
virtual void event() override
Event.
std::unique_ptr< ECL::ECLNeighbours > m_neighbourMap9
Neighbour map 9 neighbours, for E9oE21 and E1oE9.
double getSecondMomentCorrection(const double theta, const double phi, const int hypothesis) const
Get corrections for second moment.
bool m_zernike_useFarCrystals
Determines if to include or ignore crystals with rho > rho0 in perpendicular plane,...
virtual void endRun() override
End run.
std::vector< ProjectedECLDigit > projectECLDigits(const ECLShower &shower) const
Compute projections of the ECLCalDigits to the perpendicular plane.
virtual void terminate() override
Terminate.
ECLShowerShapeModule()
Constructor.
std::unique_ptr< MVA::Expert > m_expert_FWD
Pointer to the current MVA Expert for FWD.
const double m_BRLthetaMax
Maximum theta of barrel used for choosing which Zernike MVA to apply.
void initializeMVAweightFiles(const std::string &identifier, std::unique_ptr< DBObjPtr< DatabaseRepresentationOfWeightfile > > &weightFileRepresentation)
initialize MVA weight files from DB
const unsigned int m_numZernikeMVAvariables
number of variables expected in the Zernike MVA weightfile
std::unique_ptr< DBObjPtr< DatabaseRepresentationOfWeightfile > > m_weightfile_representation_FWD
Database pointer to the Database representation of the Zernike moment MVA weightfile for FWD.
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.
TGraph m_secondMomentCorrections[2][10]
TGraphs that hold the corrections.
std::unique_ptr< ECL::ECLNeighbours > m_neighbourMap21
Neighbour map 21 neighbours, for E9oE21.
@ c_phiType
type of phi identifier
@ c_thetaType
type of theta identifier
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.
double m_zernike_n1_rho0
Scaling factor for radial distances in perpendicular plane, used in Zernike moment calculation for N1...
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.
virtual const char * eclConnectedRegionArrayName() const
Default name ECLConnectedRegions.
double m_zernike_n2_rho0
Scaling factor for radial distances in perpendicular plane, used in Zernike moment calculation for N2...
std::string m_zernike_MVAidentifier_BRL
Zernike moment MVA - Barrel weight-file.
double m_avgCrystalDimension
Average crystal dimension [cm].
virtual const char * eclCalDigitArrayName() const
Default name ECLCalDigits.
std::string m_zernike_MVAidentifier_BWD
Zernike moment MVA - BWD endcap weight-file.
double computeLateralEnergy(const std::vector< ProjectedECLDigit > &projectedDigits, const double avgCrystalDimension) const
Shower shape variable: Lateral energy.
std::unique_ptr< MVA::Expert > m_expert_BWD
Pointer to the current MVA Expert for BWD.
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.
double getAbsZernikeMoment(unsigned int n, unsigned int m) const
Get absolute value of Zernike Moment nm.
int getHypothesisId() const
Get Hypothesis Id.
double getPhi() const
Get Phi.
double getEnergy() const
Get Energy.
void setSecondMoment(double secondMoment)
Set second moment.
double getR() const
Get R.
double getE9oE21() const
Get energy ratio E9oE21.
void setAbsZernikeMoment(unsigned int n, unsigned int m, double absZernikeMoment)
Set absolute value of Zernike Moment nm, for nm between 10 and 55.
@ 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 void initSupportedInterfaces()
Static function which initializes all supported interfaces, has to be called once before getSupported...
static std::map< std::string, AbstractInterface * > getSupportedInterfaces()
Returns interfaces supported by the MVA Interface.
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.
static Weightfile loadFromFile(const std::string &filename)
Static function which loads a Weightfile from a file.
void setDescription(const std::string &description)
Sets the description of the module.
void setPropertyFlags(unsigned int propertyFlags)
Sets the flags for the module properties.
@ c_ParallelProcessingCertified
This module can be run in parallel processing mode safely (All I/O must be done through the data stor...
RelationVector< TO > getRelationsTo(const std::string &name="", const std::string &namedRelation="") const
Get the relations that point from this object to another store array.
bool isRequired(const std::string &name="")
Ensure this array/object has been registered previously.
Accessor to arrays stored in the data store.
bool requireRelationTo(const StoreArray< TO > &toArray, DataStore::EDurability durability=DataStore::c_Event, const std::string &namedRelation="") const
Produce error if no relation from this array to 'toArray' has been registered.
static const double cm
Standard units with the value = 1.
void addParam(const std::string &name, T ¶mVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
B2Vector3< double > B2Vector3D
typedef for common usage with double
Common code concerning the geometry representation of the detector.
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