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)");
59 "Scaling factor for radial distances in a plane perpendicular to direction to shower for the n photon hypothesis in Zernike moment calculation.",
63 "Scaling factor for radial distances in a plane perpendicular to direction to shower for the neutral hadron hypothesis in Zernike moment calculation. ",
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.",
71 std::string{
"ecl_showershape_zernike_mva_fwd"});
73 std::string{
"ecl_showershape_zernike_mva_brl"});
75 "The Zernike moment MVA database identifier for backward endcap.", std::string{
"ecl_showershape_zernike_mva_bwd"});
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
128 if (weightFileRepresentation) {
136 if (weightFileRepresentation->hasChanged()) {
137 std::stringstream ss((*weightFileRepresentation)->m_data);
153 expert = supported_interfaces[general_options.m_method]->getExpert();
154 expert->load(weightfile);
158 std::vector<float> dummy(general_options.m_variables.size(), 0);
172 else B2ERROR(
"ECLShowerShapeModule::beginRun - Couldn't find second moment correction for current run");
180 std::vector<ProjectedECLDigit> projectedECLDigits =
projectECLDigits(*eclShower);
182 const double showerEnergy = eclShower->
getEnergy();
183 const double showerTheta = eclShower->
getTheta();
184 const double showerPhi = eclShower->
getPhi();
187 double sumEnergies = 0.0;
188 for (
const auto& projectedECLDigit : projectedECLDigits) sumEnergies += projectedECLDigit.energy;
197 B2DEBUG(175,
"Second moment angular correction: " << secondMomentCorrection <<
" (theta(rad)=" << showerTheta <<
", phi(rad)=" <<
198 showerPhi <<
",hypothesisId=" << hypothesisID <<
200 const double secondMoment =
computeSecondMoment(projectedECLDigits, showerEnergy) * secondMomentCorrection;
201 B2DEBUG(175,
"Second moment after correction: " << secondMoment);
210 for (
unsigned int n = 1; n <= 5; n++) {
211 for (
unsigned int m = 0; m <= n; m++) {
216 if (calculateZernikeMVA) {
256 N2shower = &eclShower;
263 bool found_N2shower =
true;
264 if (N2shower ==
nullptr) found_N2shower =
false;
266 double prodN1zernikeMVAs = 1.0;
272 bool calculateZernikeMVA =
true;
273 if (!found_N2shower || eclShower.getHypothesisId() !=
ECLShower::c_nPhotons) calculateZernikeMVA =
false;
277 if (eclShower.getHypothesisId() ==
ECLShower::c_nPhotons) prodN1zernikeMVAs *= eclShower.getZernikeMVA();
281 if (N2shower) N2shower->
setZernikeMVA(1.0 - prodN1zernikeMVAs);
287 std::vector<ProjectedECLDigit> tmpProjectedECLDigits;
293 const double showerR = shower.
getR();
294 const double showerTheta = shower.
getTheta();
295 const double showerPhi = shower.
getPhi();
301 const B2Vector3D showerDirection = (1.0 / showerPosition.
Mag()) * showerPosition;
308 xPrimeDirection *= 1.0 / xPrimeDirection.
Mag();
311 yPrimeDirection *= 1.0 / yPrimeDirection.
Mag();
319 for (
unsigned int iRelation = 0; iRelation < showerDigitRelations.size(); ++iRelation) {
320 const auto calDigit = showerDigitRelations.object(iRelation);
327 const auto weight = showerDigitRelations.weight(iRelation);
328 tmpProjectedDigit.
energy = weight * calDigit->getEnergy();
333 const int cellId = calDigit->getCellId();
334 B2Vector3D calDigitPosition = geometry->GetCrystalPos(cellId - 1);
338 const double angleDigitShower = calDigitPosition.
Angle(showerPosition);
339 tmpProjectedDigit.
rho = showerR * TMath::Tan(angleDigitShower);
346 B2Vector3D projectedDigitDirection = calDigitPosition - calDigitPosition.
Dot(showerDirection) * showerDirection;
347 tmpProjectedDigit.
alpha = projectedDigitDirection.
Angle(xPrimeDirection);
350 if (projectedDigitDirection.
Angle(yPrimeDirection) > TMath::Pi() / 2.0)
351 tmpProjectedDigit.
alpha = 2.0 * TMath::Pi() - tmpProjectedDigit.
alpha;
352 tmpProjectedECLDigits.push_back(tmpProjectedDigit);
355 return tmpProjectedECLDigits;
368 const double avgCrystalDimension)
const
372 if (projectedDigits.size() < 3.0)
return 0;
375 double maxEnergy(0), secondMaxEnergy(0);
376 unsigned int iMax(0), iSecondMax(0);
378 for (
unsigned int iProjecteDigit = 0; iProjecteDigit < projectedDigits.size(); iProjecteDigit++) {
379 if (projectedDigits[iProjecteDigit].energy > maxEnergy) {
380 secondMaxEnergy = maxEnergy;
382 maxEnergy = projectedDigits[iProjecteDigit].energy;
383 iMax = iProjecteDigit;
384 }
else if (projectedDigits[iProjecteDigit].energy > secondMaxEnergy) {
385 secondMaxEnergy = projectedDigits[iProjecteDigit].energy;
386 iSecondMax = iProjecteDigit;
392 for (
unsigned int iProjecteDigit = 0; iProjecteDigit < projectedDigits.size(); iProjecteDigit++) {
395 if (iProjecteDigit == iMax || iProjecteDigit == iSecondMax)
continue;
397 double rho = projectedDigits[iProjecteDigit].rho;
398 double rho2 = rho * rho;
399 double energy = projectedDigits[iProjecteDigit].energy;
400 sumE += energy * rho2;
404 const double r0sq = avgCrystalDimension * avgCrystalDimension;
405 return sumE / (sumE + r0sq * (maxEnergy + secondMaxEnergy));
411 if (n == 1 && m == 1)
return rho;
412 if (n == 2 && m == 0)
return 2.0 * rho * rho - 1.0;
413 if (n == 2 && m == 2)
return rho * rho;
414 if (n == 3 && m == 1)
return 3.0 * rho * rho * rho - 2.0 * rho;
415 if (n == 3 && m == 3)
return rho * rho * rho;
416 if (n == 4 && m == 0)
return 6.0 * rho * rho * rho * rho - 6.0 * rho * rho + 1.0;
417 if (n == 4 && m == 2)
return 4.0 * rho * rho * rho * rho - 3.0 * rho * rho;
418 if (n == 4 && m == 4)
return rho * rho * rho * rho;
419 if (n == 5 && m == 1)
return 10.0 * rho * rho * rho * rho * rho - 12.0 * rho * rho * rho + 3.0 * rho;
420 if (n == 5 && m == 3)
return 5.0 * rho * rho * rho * rho * rho - 4.0 * rho * rho * rho;
421 if (n == 5 && m == 5)
return rho * rho * rho * rho * rho;
424 double returnVal = 0;
425 for (
int idx = 0; idx <= (n - std::abs(m)) / 2; ++idx)
426 returnVal += std::pow(-1, idx) * TMath::Factorial(n - idx) / TMath::Factorial(idx)
427 / TMath::Factorial((n + std::abs(m)) / 2 - idx) / TMath::Factorial((n - std::abs(m)) / 2 - idx) * std::pow(rho, n - 2 * idx);
435 if (rho > 1.0)
return std::complex<double>(0, 0);
437 std::complex<double> i(0, 1);
438 std::complex<double> exponent = std::exp(i * std::complex<double>(m * alpha, 0));
439 return std::complex<double>(
Rnm(n, m, rho), 0) * exponent;
444 const double totalEnergy,
const int n,
const int m,
const double rho0)
const
446 if (totalEnergy <= 0.0)
return 0.0;
449 if (n < 0 || m < 0)
return 0.0;
450 if (m > n)
return 0.0;
452 std::complex<double> sum(0.0, 0.0);
454 for (
const auto projectedDigit : projectedDigits) {
455 double normalizedRho = projectedDigit.rho / rho0;
457 if (normalizedRho > 1.0) {
459 else normalizedRho = 1.0;
462 sum += projectedDigit.energy * std::conj(
zernikeValue(normalizedRho, projectedDigit.alpha, n, m));
464 return (n + 1.0) / TMath::Pi() * std::abs(sum) / totalEnergy;
468 const double totalEnergy)
const
470 if (totalEnergy <= 0.0)
return 0.0;
474 for (
const auto projectedDigit : projectedDigits) sum += projectedDigit.energy * projectedDigit.rho * projectedDigit.rho;
476 return sum / totalEnergy;
485 if (centralCellId == 0)
return 0.0;
488 const std::vector< short int > n9 =
m_neighbourMap9->getNeighbours(centralCellId);
490 double energy1 = 0.0;
491 double energy9 = 0.0;
495 for (
unsigned int iRel = 0; iRel < relatedDigitsPairs.size(); iRel++) {
496 const auto caldigit = relatedDigitsPairs.object(iRel);
497 const auto weight = relatedDigitsPairs.weight(iRel);
498 const auto energy = caldigit->getEnergy();
499 const int cellid = caldigit->getCellId();
502 if (cellid == centralCellId) {
503 energy1 = weight * energy;
507 const auto it9 = std::find(n9.begin(), n9.end(), cellid);
508 if (it9 != n9.end()) {
509 energy9 += weight * energy;
514 if (energy9 > 1e-9)
return energy1 / energy9;
522 if (centralCellId == 0)
return 0.0;
525 const std::vector< short int > n9 =
m_neighbourMap9->getNeighbours(centralCellId);
526 const std::vector< short int > n21 =
m_neighbourMap21->getNeighbours(centralCellId);
528 double energy9 = 0.0;
529 double energy21 = 0.0;
533 for (
unsigned int iRel = 0; iRel < relatedDigitsPairs.size(); iRel++) {
534 const auto caldigit = relatedDigitsPairs.object(iRel);
535 const auto weight = relatedDigitsPairs.weight(iRel);
536 const auto energy = caldigit->getEnergy();
537 const int cellid = caldigit->getCellId();
540 const auto it9 = std::find(n9.begin(), n9.end(), cellid);
541 if (it9 != n9.end()) {
542 energy9 += weight * energy;
546 const auto it21 = std::find(n21.begin(), n21.end(), cellid);
547 if (it21 != n21.end()) {
548 energy21 += weight * energy;
553 if (energy21 > 1e-9)
return energy9 / energy21;
561 for (
auto iType = 0; iType < 2; ++iType)
562 for (
auto iHypothesis = 0; iHypothesis < 10; ++iHypothesis)
567 const int type = correction.getType();
568 const int hypothesis = correction.getHypothesisId();
569 if (type < 0 or type > 1 or hypothesis < 1 or hypothesis > 9) {
570 B2FATAL(
"Invalid type or hypothesis for second moment corrections.");
581 B2FATAL(
"Missing corrections for second moments..");
588 double thetadeg = theta * TMath::RadToDeg();
591 if (thetadeg < 0.1) thetadeg = 0.1;
592 else if (thetadeg > 179.9) thetadeg = 179.9;
595 double phideg = phi * TMath::RadToDeg();
598 if (phideg < -179.9) phideg = -179.9;
599 else if (phideg > 179.9) phideg = 179.9;
603 if (hypothesis < 1 or hypothesis > 10) {
604 B2FATAL(
"Invalid hypothesis for second moment corrections.");
610 B2DEBUG(175,
"Second momen theta crrection = " << thetaCorrection <<
", phi correction = " << phiCorrection);
612 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 initliazes all supported interfaces, has to be called once before getSupportedI...
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.
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.
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
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