Belle II Software  release-08-01-10
BremsFinderModule.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 // Own header.
10 #include <analysis/modules/BremsCorrection/BremsFinderModule.h>
11 
12 // framework aux
13 #include <framework/gearbox/Const.h>
14 #include <framework/logging/Logger.h>
15 #include <framework/datastore/RelationArray.h>
16 #include <framework/datastore/RelationVector.h>
17 
18 // dataobjects
19 #include <mdst/dataobjects/Track.h>
20 #include <mdst/dataobjects/ECLCluster.h>
21 
22 // utilities
23 #include <analysis/DecayDescriptor/ParticleListName.h>
24 #include <analysis/DecayDescriptor/DecayDescriptor.h>
25 
26 // variables
27 #include <analysis/variables/ECLVariables.h>
28 
29 #include <cmath>
30 #include <algorithm>
31 #include <TMatrixFSym.h>
32 
33 using namespace std;
34 using namespace Belle2;
35 
36 //-----------------------------------------------------------------
37 // Register module
38 //-----------------------------------------------------------------
39 
40 REG_MODULE(BremsFinder);
41 
42 //-----------------------------------------------------------------
43 // Implementation
44 //-----------------------------------------------------------------
45 
46 BremsFinderModule::BremsFinderModule() :
47  Module(), m_pdgCode(0)
48 {
49  // set module description (e.g. insert text)
50  setDescription(R"DOC(
51  This module copies each particle in the ``inputList`` to the ``outputList`` and uses
52  the results of the **eclTrackBremFinder** module to look for possible bremsstrahlung photons; if these
53  photons exist, it adds their four momentum to the particle in the ``outputList``.
54  It also adds the original particle and these photons as daughters of the new, corrected particle.
55  Track and PID information of the original particle are copied onto the new one to facilitate their access
56  in the analysis scripts.
57 
58  The **eclTrackBremFinder** module uses the lepton track PXD and SVD hits and extrapolates them to the ECL;
59  then looks for ECL clusters with energies between 0.02 and 1 times the track energy and without associated
60  tracks, and checks if the normalized distance between each of these clusters
61  and the extrapolated hit is smaller than 0.05. If it is, a *Bremsstrahlung* weighted relation
62  between said cluster and the track is established. The weight is determined as
63 
64  .. math::
65 
66  \text{max}\left(\frac{\left|\phi_{\text{cluster}}-\phi_{\text{hit}}\right|}{\Delta\phi_{\text{cluster}}+\Delta\phi_{\text{hit}}}, \, \frac{\left|\theta_{\text{cluster}}-\theta_{\text{hit}}\right|}{\Delta\theta_{\text{cluster}}+\Delta\theta_{\text{hit}}}\right)
67 
68  where :math:`\phi_i` and :math:`\theta_i` are the azimuthal and polar angles of the ECL cluster and the
69  extrapolated hit, and :math:`\Delta x` represents the uncertainty of the value :math:`x`. The details of the
70  calculation of these quantities are `here`_. By default, only relations with a weight smaller than 3.0 are stored.
71  The user can further reduce the maximally allowed value of this weight to remove unwanted photons from the
72  bremsstrahlung correction.
73 
74  This module looks for photons in the ``gammaList`` whose clusters have a *Bremsstrahlung* relation with the track
75  of one of the particles in the ``inputList``, and adds their 4-momentum to the particle's one. It also stores the value
76  of each relation weight as ``extraInfo`` of the corrected particle, under the name ``"bremsWeightWithPhotonN"``, where
77  N is the index of the photon as daughter of the corrected particle; thus ``"bremsWeightWithPhoton0"`` gives the weight
78  of the Bremsstrahlung relation between the new, corrected particle, and the first photon daughter.
79 
80  Warning:
81  Even in the event of no bremsstrahlung photons found, a new particle is still created, and the original one is still
82  added as its daughter.
83 
84  Warning:
85  Studies have shown that the requirements that the energy of the photon must be between 0.2 and 1 times the track energy and
86  that only track-photon relations with a weight below three are considered, are too tight. Until these are relaxed and a new
87  processing is done (MC15 and proc 13) it might be better to use the alternative `BelleBremRecovery` module.
88 
89  See also:
90  `eclTrackBremFinder module`_
91 
92  .. _eclTrackBremFinder module: https://gitlab.desy.de/belle2/software/basf2/-/tree/main/ecl/modules/eclTrackBremFinder
93  .. _here: https://gitlab.desy.de/belle2/software/basf2/-/tree/main/ecl/modules/eclTrackBremFinder/src/BremFindingMatchCompute.cc)DOC");
95 
96  // Add parameters
97  addParam("outputList", m_outputListName, "The output particle list name.");
98  addParam("inputList", m_inputListName,
99  R"DOC(The initial particle list name containing the particles to correct. *It should already exist* and *the particles must have an associated track.*)DOC");
100  addParam("gammaList", m_gammaListName,
101  R"DOC(The photon list containing the preselected bremsstrahlung candidates. *It should already exist* and *the particles in the list must be photons*)DOC");
102  addParam("maximumAcceptance", m_maximumAcceptance,
103  "The maximum value of the relation weight between a bremsstrahlung cluster and a particle track",
105  addParam("multiplePhotons", m_addMultiplePhotons, "If true, use all possible photons to correct the particle's 4-momentum",
107  addParam("usePhotonOnlyOnce", m_usePhotonOnlyOnce,
108  R"DOC(If true, each brems candidate is used to correct maximum 1 particle (the one with the lowest relation weight among all in the ``inputList``).)DOC",
110  addParam("writeOut", m_writeOut,
111  R"DOC(If true, the output ``ParticleList`` will be saved by `RootOutput`. If false, it will be ignored when writing the file.)DOC",
112  m_writeOut);
113 }
114 
116 {
117  //Get input lepton particle list
118  m_inputList.isRequired(m_inputListName);
119 
120  DecayDescriptor decDes;
121  decDes.init(m_inputListName);
122 
123  m_pdgCode = decDes.getMother()->getPDGCode();
124  //Check for the particles in the lepton list to have a track associated
126  B2ERROR("[BremsFinderModule] Invalid particle list. the particles in " << m_inputListName << " should have an associated track.");
127 
128  //Get input photon particle list
129  m_gammaList.isRequired(m_gammaListName);
130 
131  decDes.init(m_gammaListName);
132  int temp_pdg = decDes.getMother()->getPDGCode();
133 
134  //Check that this is a gamma list
135  if (temp_pdg != Const::photon.getPDGCode())
136  B2ERROR("[BremsFinderModule] Invalid particle list. the particles in " << m_gammaListName << " should be photons!");
137 
138  decDes.init(m_outputListName);
139  temp_pdg = decDes.getMother()->getPDGCode();
140  // check the validity of output ParticleList name
142  B2ERROR("[BremsFinderModule] Input and output particle list names are the same: " << m_inputListName);
143  else if (temp_pdg != m_pdgCode) {
144  B2ERROR("[BremsFinderModule] The input and output particle list correspond to different particles: " << m_inputListName << " --> "
145  << m_outputListName);
146  }
147 
148  // output particle
150 
151  // make output lists
153  m_outputList.registerInDataStore(m_outputListName, flags);
154  m_outputAntiList.registerInDataStore(m_outputAntiListName, flags);
155 
156  m_particles.registerRelationTo(m_pidlikelihoods);
157 }
158 
160 {
161  RelationArray particlesToMCParticles(m_particles, m_mcParticles);
162 
163  // new output particle list
164  m_outputList.create();
166 
167  m_outputAntiList.create();
169  m_outputAntiList->bindAntiParticleList(*(m_outputList));
170 
171  // Number of photons (calculate it here only once)
172  const unsigned int nGamma = m_gammaList->getListSize();
173 
174  // Number of leptons (calculate it here only once)
175  const unsigned int nLep = m_inputList->getListSize();
176 
177  const std::string relationName = "Bremsstrahlung";
178 
179  //In the case of only one track per photon
180  if (m_usePhotonOnlyOnce) {
181  for (unsigned n = 0; n < nGamma; n++) {
182  Particle* gamma = m_gammaList->getParticle(n);
183  //Skip this if it the best match has already been assigned (pathological case: happens only if you use the same gamma list
184  //to correct more than once. Performance studies, in which the same list is used with different options, are an example
185  if (gamma->hasExtraInfo("bestMatchIndex")) continue;
186 
187  auto cluster = gamma->getECLCluster();
188  //Get the tracks related to each photon...
189  RelationVector<Track> relatedTracks = cluster->getRelationsTo<Track>("", relationName);
190  double bestWeight = m_maximumAcceptance;
191  unsigned bestMatchIndex = 0;
192  unsigned trkIndex = 0;
193  //Loop over the related tracks...
194  for (auto trk = relatedTracks.begin(); trk != relatedTracks.end(); trk++, trkIndex++) {
195  //... and over the input particles' tracks...
196  for (unsigned i = 0; i < nLep; i++) {
197  const Particle* lepton = m_inputList->getParticle(i);
198  auto leptonTrack = lepton->getTrack();
199  //... check that the particle track corresponds to the related track....
200  if (leptonTrack->getArrayIndex() == trk->getArrayIndex()) {
201  double weight = relatedTracks.weight(trkIndex);
202  if (weight < bestWeight) {
203  bestWeight = weight;
204  //... and only select the best match among the tracks in the input list
205  bestMatchIndex = trk->getArrayIndex();
206  }
207  break; //If the particle corresponding to the related track is found, break the loop over the particles and go for the next related track
208  }
209  }
210  }
211  //... finally, add the best match index as an extra info for the photon
212  gamma->addExtraInfo("bestMatchIndex", bestMatchIndex);
213  }
214  }
215 
216  // loop over charged particles, correct them and add them to the output list
217 
218  for (unsigned i = 0; i < nLep; i++) {
219  const Particle* lepton = m_inputList->getParticle(i);
220 
221  //Get the track of this lepton...
222  auto track = lepton->getTrack();
223 
224  //... and get the bremsstrahlung clusters related to this track
225  RelationVector<ECLCluster> bremClusters = track->getRelationsFrom<ECLCluster>("", relationName);
226 
227  std::vector<std::pair <double, Particle*> > selectedGammas;
228 
229  unsigned j = 0;
230  for (auto bremCluster = bremClusters.begin(); bremCluster != bremClusters.end(); bremCluster++, j++) {
231  double weight = bremClusters.weight(j);
232 
233  if (weight > m_maximumAcceptance) continue;
234 
235  for (unsigned k = 0; k < nGamma; k++) {
236 
237  Particle* gamma = m_gammaList->getParticle(k);
238  auto cluster = gamma->getECLCluster();
239 
240  if (bremCluster->getClusterId() == cluster->getClusterId()) {
241  if (m_usePhotonOnlyOnce) { //If only one track per photon should be used...
242  if (track->getArrayIndex() == static_cast<int>
243  (gamma->getExtraInfo("bestMatchIndex"))) //... check if this track is the best match ...
244  selectedGammas.push_back(std::make_pair(weight, gamma)); //... and if it is, add it to the selected gammas
245  } else {
246  selectedGammas.push_back(std::make_pair(weight, gamma));
247  }
248  }
249 
250  } // Closes for loop on gammas
251 
252  } // Closes for loop on brem clusters
253 
254  //The 4-momentum of the new lepton in the output particle list
255  ROOT::Math::PxPyPzEVector new4Vec = lepton->get4Vector();
256 
257  //Sort weight-particle pairs by weight. Smaller weights go first
258  std::sort(selectedGammas.begin(), selectedGammas.end());
259 
260  //Add to this 4-momentum those of the selected photon(s)
261  for (auto const& bremsPair : selectedGammas) {
262  Particle* g = bremsPair.second;
263  new4Vec += g->get4Vector();
264  if (! m_addMultiplePhotons) break; //stop after adding the first photon
265  }
266 
267  //Create the new particle with the 4-momentum calculated before
268  Particle correctedLepton(new4Vec, lepton->getPDGCode(), Particle::EFlavorType::c_Flavored, Particle::c_Track,
269  track->getArrayIndex());
270 
271  //And add the original lepton as its daughter
272  correctedLepton.appendDaughter(lepton, false);
273 
274  const TMatrixFSym& lepErrorMatrix = lepton->getMomentumVertexErrorMatrix();
275  TMatrixFSym corLepMatrix(lepErrorMatrix);
276 
277  double bremsGammaEnergySum = 0.0;
278  //Now, if there are any, add the brems photons as daughters as well. As before, we distinguish between the multiple and only one brems photon cases
279  int photonIndex = 0;
280  for (auto const& bremsPair : selectedGammas) {
281  //Add the weights as extra info of the mother
282  Particle* bremsGamma = bremsPair.second;
283  std::string extraInfoName = "bremsWeightWithPhoton" + std::to_string(photonIndex);
284  correctedLepton.addExtraInfo(extraInfoName, bremsPair.first);
285  photonIndex++;
286  bremsGammaEnergySum += Variable::eclClusterE(bremsGamma);
287 
288  const TMatrixFSym& gammaErrorMatrix = bremsGamma->getMomentumVertexErrorMatrix();
289  for (int irow = 0; irow <= 3; irow++) {
290  for (int icol = irow; icol <= 3; icol++) corLepMatrix(irow, icol) += gammaErrorMatrix(irow, icol);
291  }
292  correctedLepton.appendDaughter(bremsGamma, false);
293  B2DEBUG(10, "[BremsFinderModule] Found a bremsstrahlung gamma and added its 4-vector to the charged particle");
294  if (! m_addMultiplePhotons) break; //stop after adding the first photon
295  }
296 
297  correctedLepton.setMomentumVertexErrorMatrix(corLepMatrix);
298 
299  // add the info from original lepton to the new lepton
300  correctedLepton.setVertex(lepton->getVertex());
301  correctedLepton.setPValue(lepton->getPValue());
302  correctedLepton.addExtraInfo("bremsCorrected", float(selectedGammas.size() > 0));
303  correctedLepton.addExtraInfo("bremsCorrectedPhotonEnergy", bremsGammaEnergySum);
304 
305  // add the mc relation
306  Particle* newLepton = m_particles.appendNew(correctedLepton);
307  const MCParticle* mcLepton = lepton->getRelated<MCParticle>();
308  const PIDLikelihood* pid = lepton->getPIDLikelihood();
309 
310  if (pid) newLepton->addRelationTo(pid);
311 
312  if (mcLepton != nullptr) newLepton->addRelationTo(mcLepton);
313 
314  m_outputList->addParticle(newLepton);
315 
316  } //Closes for loop on leptons
317 
318 } //Close event()
virtual void initialize() override
Use this to initialize resources or memory your module needs.
double m_maximumAcceptance
photons whose clusters have relation weights higher than this will not be used for bremsstrahlung cor...
std::string m_gammaListName
input gamma list name
virtual void event() override
Called once for each event.
bool m_addMultiplePhotons
In case there is more than one brems photon, use only the best one (based on the weight of the relati...
StoreObjPtr< ParticleList > m_outputAntiList
StoreObjptr for output antiparticlelist.
StoreArray< Particle > m_particles
StoreArray of Particle objects.
StoreObjPtr< ParticleList > m_outputList
StoreObjptr for output particlelist.
StoreArray< PIDLikelihood > m_pidlikelihoods
StoreArray of PIDLikelihood objects.
StoreObjPtr< ParticleList > m_inputList
StoreObjptr for input charged particle list.
StoreObjPtr< ParticleList > m_gammaList
StoreObjptr for gamma list.
bool m_writeOut
Write the output particle list in the final file?
StoreArray< MCParticle > m_mcParticles
StoreArray of MCParticle objects.
bool m_usePhotonOnlyOnce
Each brems photon can be used to correct only one particle (the one with the smallest relation weight...
int m_pdgCode
PDG code of the particle to be corrected.
std::string m_outputAntiListName
output anti-particle list name
std::string m_inputListName
input particle list name
std::string m_outputListName
output particle list name
static const ParticleSet chargedStableSet
set of charged stable particles
Definition: Const.h:609
static const ParticleType invalidParticle
Invalid particle, used internally.
Definition: Const.h:672
static const ParticleType photon
photon particle
Definition: Const.h:664
EStoreFlags
Flags describing behaviours of objects etc.
Definition: DataStore.h:69
@ c_WriteOut
Object/array should be saved by output modules.
Definition: DataStore.h:70
@ c_DontWriteOut
Object/array should be NOT saved by output modules.
Definition: DataStore.h:71
int getPDGCode() const
Return PDG code.
The DecayDescriptor stores information about a decay tree or parts of a decay tree.
bool init(const std::string &str)
Initialise the DecayDescriptor from given string.
const DecayDescriptorParticle * getMother() const
return mother.
ECL cluster data.
Definition: ECLCluster.h:27
A Class to store the Monte Carlo particle information.
Definition: MCParticle.h:32
Base class for Modules.
Definition: Module.h:72
void setDescription(const std::string &description)
Sets the description of the module.
Definition: Module.cc:214
void setPropertyFlags(unsigned int propertyFlags)
Sets the flags for the module properties.
Definition: Module.cc:208
@ c_ParallelProcessingCertified
This module can be run in parallel processing mode safely (All I/O must be done through the data stor...
Definition: Module.h:80
Class to collect log likelihoods from TOP, ARICH, dEdx, ECL and KLM aimed for output to mdst includes...
Definition: PIDLikelihood.h:26
Class to store reconstructed particles.
Definition: Particle.h:75
const Track * getTrack() const
Returns the pointer to the Track object that was used to create this Particle (ParticleType == c_Trac...
Definition: Particle.cc:849
void appendDaughter(const Particle *daughter, const bool updateType=true, const int daughterProperty=c_Ordinary)
Appends index of daughter to daughters index array.
Definition: Particle.cc:680
void setVertex(const ROOT::Math::XYZVector &vertex)
Sets position (decay vertex)
Definition: Particle.h:295
double getPValue() const
Returns chi^2 probability of fit if done or -1.
Definition: Particle.h:625
ROOT::Math::XYZVector getVertex() const
Returns vertex position (POCA for charged, IP for neutral FS particles)
Definition: Particle.h:589
const PIDLikelihood * getPIDLikelihood() const
Returns the pointer to the PIDLikelihood object that is related to the Track, which was used to creat...
Definition: Particle.cc:875
int getPDGCode(void) const
Returns PDG code.
Definition: Particle.h:426
ROOT::Math::PxPyPzEVector get4Vector() const
Returns Lorentz vector.
Definition: Particle.h:517
void addExtraInfo(const std::string &name, double value)
Sets the user-defined data of given name to the given value.
Definition: Particle.cc:1337
void setMomentumVertexErrorMatrix(const TMatrixFSym &errMatrix)
Sets 7x7 error matrix.
Definition: Particle.cc:397
void setPValue(double pValue)
Sets chi^2 probability of fit.
Definition: Particle.h:338
TMatrixFSym getMomentumVertexErrorMatrix() const
Returns 7x7 error matrix.
Definition: Particle.cc:424
Low-level class to create/modify relations between StoreArrays.
Definition: RelationArray.h:62
Class for type safe access to objects that are referred to in relations.
iterator end()
Return iterator to last entry +1.
iterator begin()
Return iterator to first entry.
float weight(int index) const
Get weight with index.
void addRelationTo(const RelationsInterface< BASE > *object, float weight=1.0, const std::string &namedRelation="") const
Add a relation from this object to another object (with caching).
T * getRelated(const std::string &name="", const std::string &namedRelation="") const
Get the object to or from which this object has a relation.
Class that bundles various TrackFitResults.
Definition: Track.h:25
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition: Module.h:560
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
std::string antiParticleListName(const std::string &listName)
Returns name of anti-particle-list corresponding to listName.
Abstract base class for different kinds of events.