13 #include <ecl/modules/eclChargedPID/ECLChargedPIDModule.h>
22 setDescription(
"The module implements charged particle identification using ECL-related observables. For each Track matched with a suitable ECLShower, likelihoods for each particle hypothesis are obtained from pdfs stored in a conditions database payload, and then get stored in an ECLPidLikelihood object. The dimensionality of the likelihood depends on how many variables are stored in the payload. The baseline method could be a simple univariate likelihood based on E/p PDFs, but it could be extended to include more ECL quantitites (e.g. shower shape variables, w/ proper decorrelation).");
24 setPropertyFlags(c_ParallelProcessingCertified);
26 addParam(
"applyClusterTimingSel",
27 m_applyClusterTimingSel,
28 "Set true if you want to apply a abs(clusterTiming)/clusterTimingError<1 cut on clusters. This cut is optimised to achieve 99% timing efficiency for true photons from the IP.",
47 if (!
m_pdfs) { B2FATAL(
"No ECL charged PID PDFs payload found in database!"); }
69 if (fitRes ==
nullptr)
continue;
70 const auto relShowers = track.getRelationsTo<
ECLShower>();
71 if (relShowers.size() == 0)
continue;
73 const double p = fitRes->getMomentum().Mag();
74 const double theta = fitRes->getMomentum().Theta();
75 const auto charge = fitRes->getChargeSign();
77 double shEnergy(0.0), maxEnergy(0.0);
79 const ECLShower* mostEnergeticShower =
nullptr;
81 for (
const auto& eclShower : relShowers) {
85 if (abs(eclShower.getTime()) > eclShower.getDeltaTime99())
continue;
89 if (shEnergy > maxEnergy) {
91 mostEnergeticShower = &eclShower;
96 double showerTheta = (mostEnergeticShower) ? mostEnergeticShower->
getTheta() : -999.0;
97 int showerReg = (mostEnergeticShower) ? mostEnergeticShower->
getDetectorRegion() : -1;
113 B2DEBUG(20,
"-------------------------------");
114 B2DEBUG(20,
"TRACK properties:");
115 B2DEBUG(20,
"p = " << p <<
" [GeV/c]");
116 B2DEBUG(30,
"theta = " << theta <<
" [rad]");
117 B2DEBUG(20,
"charge = " << charge);
118 B2DEBUG(20,
"-------------------------------");
119 B2DEBUG(20,
"SHOWER properties:");
120 B2DEBUG(20,
"showerTheta = " << showerTheta <<
" [rad], showerReg = " << showerReg);
122 B2DEBUG(30,
"varid: " <<
static_cast<unsigned int>(varid) <<
" = " << var);
124 B2DEBUG(20,
"-------------------------------");
132 if (mostEnergeticShower && showerReg && std::abs(charge)) {
142 unsigned int hypo_idx = hypo.getIndex();
144 auto pdgId = hypo.getPDGCode();
146 B2DEBUG(20,
"\n\thypo[" << hypo_idx <<
"] = " << pdgId <<
", hypo[" << hypo_idx <<
"] * reco_charge = " << pdgId * charge);
149 auto varids =
m_pdfs->getVars(pdgId, charge, p, showerTheta);
152 std::vector<double> variables;
153 for (
const auto& varid : *varids) {
158 if (
m_pdfs->doVarsTransfo()) {
167 for (
unsigned int idx(0); idx < variables.size(); idx++) {
169 auto var = variables.at(idx);
170 auto varid = varids->at(idx);
172 const TF1* pdf =
m_pdfs->getPdf(pdgId, charge, p, showerTheta, varid);
177 B2DEBUG(30,
"\t\tL(" << pdgId * charge <<
") = " << prod
178 <<
" * pdf(varid: " <<
static_cast<unsigned int>(varid) <<
") = "
179 << prod <<
" * " << ipdfval
180 <<
" = " << prod * ipdfval);
190 B2DEBUG(20,
"\tL(" << pdgId * charge <<
") = " << pdfval);
192 likelihoods[hypo_idx] = (std::isnormal(pdfval) && pdfval > 0) ? log(pdfval) :
c_dummyLogL;
194 B2DEBUG(20,
"\tlog(L(" << pdgId * charge <<
")) = " << likelihoods[hypo_idx]);
200 track.addRelationTo(eclPidLikelihood);
210 double y, xmin, xmax;
211 pdf->GetRange(xmin, xmax);
213 if (x <= xmin) { y = pdf->Eval(xmin + 1e-9); }
214 else if (x >= xmax) { y = pdf->Eval(xmax - 1e-9); }
215 else { y = pdf->Eval(x); }
217 return (y) ? y : 1e-9;
221 std::vector<double>& variables)
224 unsigned int nvars = variables.size();
227 auto vts =
m_pdfs->getVTS(pdg, charge, p, theta);
230 B2DEBUG(30,
"\tclass path: " << vts->classPath);
231 B2DEBUG(30,
"\tgbin = " << vts->gbin <<
", (theta,p) = (" << vts->jth <<
"," << vts->ip <<
")");
232 B2DEBUG(30,
"\tnvars: " << nvars);
234 auto varids =
m_pdfs->getVars(pdg, charge, p, theta);
236 for (
unsigned int ivar(0); ivar < nvars; ivar++) {
237 unsigned int ndivs = vts->nDivisions[ivar];
238 B2DEBUG(30,
"\tvarid: " <<
static_cast<unsigned int>(varids->at(ivar)) <<
" = " << variables.at(ivar) <<
", nsteps = " << ndivs);
239 for (
unsigned int jdiv(0); jdiv < ndivs; jdiv++) {
240 auto ij =
linIndex(ivar, jdiv, vts->nDivisionsMax);
241 B2DEBUG(30,
"\t\tx[" << ivar <<
"][" << jdiv <<
"] = x[" << ij <<
"] = " << vts->x[ij]);
242 B2DEBUG(30,
"\t\tcumulDist[" << ivar <<
"][" << jdiv <<
"] = cumulDist[" << ij <<
"] = " << vts->cumulDist[ij]);
246 std::vector<double> vtransfo_gauss;
247 vtransfo_gauss.reserve(nvars);
249 for (
unsigned int ivar(0); ivar < nvars; ivar++) {
251 int ndivs = vts->nDivisions[ivar];
254 auto ij =
linIndex(ivar, jdiv, vts->nDivisionsMax);
255 while (variables.at(ivar) > vts->x[ij]) {
257 ij =
linIndex(ivar, jdiv, vts->nDivisionsMax);
260 if (jdiv < 0) { jdiv = 0; }
261 if (jdiv >= ndivs) { jdiv = ndivs - 1; }
263 if ((variables.at(ivar) > vts->x[ij] && jdiv != ndivs - 1) || jdiv == 0) {
268 auto ijnext =
linIndex(ivar, jnextdiv, vts->nDivisionsMax);
270 double dx = vts->x[ij] - vts->x[ijnext];
271 double dy = vts->cumulDist[ij] - vts->cumulDist[ijnext];
272 double cumulant = vts->cumulDist[ij] + (variables.at(ivar) - vts->x[ijnext]) * dy / dx;
274 cumulant = std::min(cumulant, 1.0 - 10e-10);
275 cumulant = std::max(cumulant, 10e-10);
277 double maxErfInvArgRange = 0.99999999;
278 double arg = 2.0 * cumulant - 1.0;
280 arg = std::min(maxErfInvArgRange, arg);
281 arg = std::max(-maxErfInvArgRange, arg);
283 vtransfo_gauss.at(ivar) =
c_sqrt2 * TMath::ErfInverse(arg);
286 B2DEBUG(30,
"\tSHOWER properties (Gaussian-transformed):");
287 for (
unsigned int idx(0); idx < nvars; idx++) {
288 B2DEBUG(30,
"\tvarid: " <<
static_cast<unsigned int>(varids->at(idx)) <<
" = " << vtransfo_gauss.at(idx));
290 B2DEBUG(30,
"\t-------------------------------");
292 std::vector<double> vtransfo_decorr;
293 vtransfo_decorr.reserve(nvars);
295 for (
unsigned int i(0); i < nvars; i++) {
296 double vartransfo(0);
297 for (
unsigned int j(0); j < nvars; j++) {
299 vartransfo += vtransfo_gauss[j] * vts->covMatrix[ij];
301 vtransfo_decorr.push_back(vartransfo);
304 B2DEBUG(30,
"\tSHOWER properties (Decorrelation-transformed):");
305 for (
unsigned int idx(0); idx < nvars; idx++) {
306 B2DEBUG(30,
"\tvarid: " <<
static_cast<unsigned int>(varids->at(idx)) <<
" = " << vtransfo_decorr.at(idx));
308 B2DEBUG(30,
"\t-------------------------------");
311 for (
unsigned int i(0); i < nvars; i++) {
312 variables[i] = vtransfo_decorr.at(i);
static const unsigned int c_SetSize
Number of elements (for use in array bounds etc.)
static const ParticleSet chargedStableSet
set of charged stable particles
static const ChargedStable pion
charged pion particle
The module implements charged particle identification using ECL-related observables.
virtual ~ECLChargedPIDModule()
Destructor, use to clean up anything you created in the constructor.
virtual void initialize() override
Use this to initialize resources or memory your module needs.
DBObjPtr< ECLChargedPidPDFs > m_pdfs
Interface to get the DB payload for ECL charged PID PDFs.
virtual void event() override
Called once for each event.
virtual void endRun() override
Called once when a run ends.
virtual void terminate() override
Clean up anything you created in initialize().
double getPdfVal(const double &x, const TF1 *pdf)
Extract the PDF value for a given variable from the TF1 object.
static constexpr double c_sqrt2
Defintion of sqrt(2)
int linIndex(int i, int j, int m)
Get the index corresponding to element i,j in a linearised n*m array.
StoreObjPtr< EventMetaData > m_eventMetaData
The event information.
static constexpr double c_dummyLogL
Dummy value of Log Likelihood for a particle hypothesis.
void transfoGaussDecorr(const unsigned int pdg, const int charge, const double &p, const double &theta, std::vector< double > &variables)
Transform input variables according to:
void checkPdfsDB()
Check the PDFs payload for consistency everytime they change in the database.
virtual void beginRun() override
Called once before a new run begins.
std::unordered_map< ECLChargedPidPDFs::InputVar, double > m_variables
Map to contain ECL shower observables.
StoreArray< Track > m_tracks
Array of Track objects.
bool m_applyClusterTimingSel
Apply cluster timing selection.
StoreArray< ECLPidLikelihood > m_eclPidLikelihoods
Array of ECLPidLikelihood objects.
@ c_DeltaL
DeltaL (track depth)
@ c_Z40
Zernike moment 40.
@ c_E
Energy of maxE shower.
@ c_Z51
Zernike moment 51.
@ c_LAT
Lateral shower shape.
Class to store ECL Showers.
double getPulseShapeDiscriminationMVA() const
Get shower hadron intensity.
double getLateralEnergy() const
Get Lateral Energy in Shower.
double getE1oE9() const
Get energy ratio E1oE9.
double getEnergy() const
Get Energy.
double getE9oE21() const
Get energy ratio E9oE21.
int getDetectorRegion() const
Return detector region: 0: below acceptance, 1: FWD, 2: BRL, 3: BWD, 11: FWDGAP, 13: BWDGAP.
@ c_nPhotons
CR is split into n photons (N1)
double getTrkDepth() const
path on track extrapolation to POCA to average cluster direction
double getZernikeMVA() const
Get Zernike MVA.
double getAbsZernike40() const
Get absolute value of Zernike moment 40.
double getTheta() const
Get Theta.
double getAbsZernike51() const
Get absolute value of Zernike moment 51.
Values of the result of a track fit with a given particle hypothesis.
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Abstract base class for different kinds of events.