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.at(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;
356 const double avgCrystalDimension)
360 if (projectedDigits.size() < 3.0)
return 0;
363 double maxEnergy(0), secondMaxEnergy(0);
364 unsigned int iMax(0), iSecondMax(0);
366 for (
unsigned int iProjecteDigit = 0; iProjecteDigit < projectedDigits.size(); iProjecteDigit++) {
367 if (projectedDigits[iProjecteDigit].energy > maxEnergy) {
368 secondMaxEnergy = maxEnergy;
370 maxEnergy = projectedDigits[iProjecteDigit].energy;
371 iMax = iProjecteDigit;
372 }
else if (projectedDigits[iProjecteDigit].energy > secondMaxEnergy) {
373 secondMaxEnergy = projectedDigits[iProjecteDigit].energy;
374 iSecondMax = iProjecteDigit;
380 for (
unsigned int iProjecteDigit = 0; iProjecteDigit < projectedDigits.size(); iProjecteDigit++) {
383 if (iProjecteDigit == iMax || iProjecteDigit == iSecondMax)
continue;
385 double rho = projectedDigits[iProjecteDigit].rho;
386 double rho2 = rho * rho;
387 double energy = projectedDigits[iProjecteDigit].energy;
388 sumE += energy * rho2;
392 const double r0sq = avgCrystalDimension * avgCrystalDimension;
393 return sumE / (sumE + r0sq * (maxEnergy + secondMaxEnergy));
399 if (n == 1 && m == 1)
return rho;
400 if (n == 2 && m == 0)
return 2.0 * rho * rho - 1.0;
401 if (n == 2 && m == 2)
return rho * rho;
402 if (n == 3 && m == 1)
return 3.0 * rho * rho * rho - 2.0 * rho;
403 if (n == 3 && m == 3)
return rho * rho * rho;
404 if (n == 4 && m == 0)
return 6.0 * rho * rho * rho * rho - 6.0 * rho * rho + 1.0;
405 if (n == 4 && m == 2)
return 4.0 * rho * rho * rho * rho - 3.0 * rho * rho;
406 if (n == 4 && m == 4)
return rho * rho * rho * rho;
407 if (n == 5 && m == 1)
return 10.0 * rho * rho * rho * rho * rho - 12.0 * rho * rho * rho + 3.0 * rho;
408 if (n == 5 && m == 3)
return 5.0 * rho * rho * rho * rho * rho - 4.0 * rho * rho * rho;
409 if (n == 5 && m == 5)
return rho * rho * rho * rho * rho;
412 double returnVal = 0;
413 for (
int idx = 0; idx <= (n - std::abs(m)) / 2; ++idx)
414 returnVal += std::pow(-1, idx) * TMath::Factorial(n - idx) / TMath::Factorial(idx)
415 / TMath::Factorial((n + std::abs(m)) / 2 - idx) / TMath::Factorial((n - std::abs(m)) / 2 - idx) * std::pow(rho, n - 2 * idx);
423 if (rho > 1.0)
return std::complex<double>(0, 0);
425 std::complex<double> i(0, 1);
426 std::complex<double> exponent = std::exp(i * std::complex<double>(m * alpha, 0));
427 return std::complex<double>(
Rnm(n, m, rho), 0) * exponent;
432 const double totalEnergy,
const int n,
const int m,
const double rho0)
const
434 if (totalEnergy <= 0.0)
return 0.0;
437 if (n < 0 || m < 0)
return 0.0;
438 if (m > n)
return 0.0;
440 std::complex<double> sum(0.0, 0.0);
442 for (
const auto projectedDigit : projectedDigits) {
443 double normalizedRho = projectedDigit.rho / rho0;
445 if (normalizedRho > 1.0) {
447 else normalizedRho = 1.0;
450 sum += projectedDigit.energy * std::conj(
zernikeValue(normalizedRho, projectedDigit.alpha, n, m));
452 return (n + 1.0) / TMath::Pi() * std::abs(sum) / totalEnergy;
456 const double totalEnergy)
458 if (totalEnergy <= 0.0)
return 0.0;
462 for (
const auto projectedDigit : projectedDigits) sum += projectedDigit.energy * projectedDigit.rho * projectedDigit.rho;
464 return sum / totalEnergy;
473 if (centralCellId == 0)
return 0.0;
476 const std::vector< short int > n9 =
m_neighbourMap9->getNeighbours(centralCellId);
478 double energy1 = 0.0;
479 double energy9 = 0.0;
483 for (
unsigned int iRel = 0; iRel < relatedDigitsPairs.size(); iRel++) {
484 const auto caldigit = relatedDigitsPairs.object(iRel);
485 const auto weight = relatedDigitsPairs.weight(iRel);
486 const auto energy = caldigit->getEnergy();
487 const int cellid = caldigit->getCellId();
490 if (cellid == centralCellId) {
491 energy1 = weight * energy;
495 const auto it9 = std::find(n9.begin(), n9.end(), cellid);
496 if (it9 != n9.end()) {
497 energy9 += weight * energy;
502 if (energy9 > 1e-9)
return energy1 / energy9;
510 if (centralCellId == 0)
return 0.0;
513 const std::vector< short int > n9 =
m_neighbourMap9->getNeighbours(centralCellId);
514 const std::vector< short int > n21 =
m_neighbourMap21->getNeighbours(centralCellId);
516 double energy9 = 0.0;
517 double energy21 = 0.0;
521 for (
unsigned int iRel = 0; iRel < relatedDigitsPairs.size(); iRel++) {
522 const auto caldigit = relatedDigitsPairs.object(iRel);
523 const auto weight = relatedDigitsPairs.weight(iRel);
524 const auto energy = caldigit->getEnergy();
525 const int cellid = caldigit->getCellId();
528 const auto it9 = std::find(n9.begin(), n9.end(), cellid);
529 if (it9 != n9.end()) {
530 energy9 += weight * energy;
534 const auto it21 = std::find(n21.begin(), n21.end(), cellid);
535 if (it21 != n21.end()) {
536 energy21 += weight * energy;
541 if (energy21 > 1e-9)
return energy9 / energy21;
549 for (
auto iType = 0; iType < 2; ++iType)
550 for (
auto iHypothesis = 0; iHypothesis < 10; ++iHypothesis)
555 const int type = correction.getType();
556 const int hypothesis = correction.getHypothesisId();
557 if (type < 0 or type > 1 or hypothesis < 1 or hypothesis > 9) {
558 B2FATAL(
"Invalid type or hypothesis for second moment corrections.");
569 B2FATAL(
"Missing corrections for second moments..");
576 double thetadeg = theta * TMath::RadToDeg();
579 if (thetadeg < 0.1) thetadeg = 0.1;
580 else if (thetadeg > 179.9) thetadeg = 179.9;
583 double phideg = phi * TMath::RadToDeg();
586 if (phideg < -179.9) phideg = -179.9;
587 else if (phideg > 179.9) phideg = 179.9;
591 if (hypothesis < 1 or hypothesis > 9) {
592 B2FATAL(
"Invalid hypothesis for second moment corrections.");
598 B2DEBUG(175,
"Second momen theta crrection = " << thetaCorrection <<
", phi correction = " << phiCorrection);
600 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.
static double computeLateralEnergy(const std::vector< ProjectedECLDigit > &projectedDigits, const double avgCrystalDimension)
Shower shape variable: Lateral energy.
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,...
std::vector< ProjectedECLDigit > projectECLDigits(const ECLShower &shower) const
Compute projections of the ECLCalDigits to the perpendicular plane.
ECLShowerShapeModule()
Constructor.
~ECLShowerShapeModule() override
Destructor.
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.
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 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.
static double computeSecondMoment(const std::vector< ProjectedECLDigit > &shower, const double totalEnergy)
Compute the second moment in the plane perpendicular to the direction of the shower.
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.
static double Rnm(const int n, const int m, const double rho)
The radial part of the Zernike polynomial n,m - Zernike polynomial rank rho - radial distance.
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 const 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