Belle II Software  release-06-02-00
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 include
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 
35 namespace Belle2 {
40 //-----------------------------------------------------------------
41 // Register module
42 //-----------------------------------------------------------------
43 
44  REG_MODULE(BremsFinder)
45 
46 //-----------------------------------------------------------------
47 // Implementation
48 //-----------------------------------------------------------------
49 
51  Module(), m_pdgCode(0)
52  {
53  // set module description (e.g. insert text)
54  setDescription(R"DOC(
55  This module copies each particle in the ``inputList`` to the ``outputList`` and uses
56  the results of the **eclTrackBremFinder** module to look for possible bremsstrahlung photons; if these
57  photons exist, it adds their four momentum to the particle in the ``outputList``.
58  It also adds the original particle and these photons as daughters of the new, corrected particle.
59  Track and PID information of the original particle are copied onto the new one to facilitate their access
60  in the analysis scripts.
61 
62  The **eclTrackBremFinder** module uses the lepton track PXD and SVD hits and extrapolates them to the ECL;
63  then looks for ECL clusters with energies between 0.02 and 1 times the track energy and without associated
64  tracks, and checks if the normalized distance between each of these clusters
65  and the extrapolated hit is smaller than 0.05. If it is, a *Bremsstrahlung* weighted relation
66  between said cluster and the track is established. The weight is determined as
67 
68  .. math::
69 
70  \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)
71 
72  where :math:`\phi_i` and :math:`\theta_i` are the azimuthal and polar angles of the ECL cluster and the
73  extrapolated hit, and :math:`\Delta x` represents the uncertainty of the value :math:`x`. The details of the
74  calculation of these quantities are `here`_. By default, only relations with a weight smaller than 3.0 are stored.
75  The user can further reduce the maximally allowed value of this weight to remove unwanted photons from the
76  bremsstrahlung correction.
77 
78  This module looks for photons in the ``gammaList`` whose clusters have a *Bremsstrahlung* relation with the track
79  of one of the particles in the ``inputList``, and adds their 4-momentum to the particle's one. It also stores the value
80  of each relation weight as ``extraInfo`` of the corrected particle, under the name ``"bremsWeightWithPhotonN"``, where
81  N is the index of the photon as daughter of the corrected particle; thus ``"bremsWeightWithPhoton0"`` gives the weight
82  of the Bremsstrahlung relation between the new, corrected particle, and the first photon daughter.
83 
84  Warning:
85  Even in the event of no bremsstrahlung photons found, a new particle is still created, and the original one is still
86  added as its daughter.
87 
88  Warning:
89  Studies have shown that the requirements that the energy of the photon must be between 0.2 and 1 times the track energy and
90  that only track-photon relations with a weight below three are considered, are too tight. Until these are relaxed and a new
91  processing is done (MC15 and proc 13) it might be better to use the alternative `BelleBremRecovery` module.
92 
93  See also:
94  `eclTrackBremFinder module`_
95 
96  .. _eclTrackBremFinder module: https://stash.desy.de/projects/B2/repos/basf2/browse/ecl/modules/eclTrackBremFinder
97  .. _here: https://stash.desy.de/projects/B2/repos/basf2/browse/ecl/modules/eclTrackBremFinder/src/BremFindingMatchCompute.cc)DOC");
98  setPropertyFlags(c_ParallelProcessingCertified);
99 
100  // Add parameters
101  addParam("outputList", m_outputListName, "The output particle list name.");
102  addParam("inputList", m_inputListName,
103  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");
104  addParam("gammaList", m_gammaListName,
105  R"DOC(The photon list containing the preselected bremsstrahlung candidates. *It should already exist* and *the particles in the list must be photons*)DOC");
106  addParam("maximumAcceptance", m_maximumAcceptance,
107  "The maximum value of the relation weight between a bremsstrahlung cluster and a particle track",
108  m_maximumAcceptance);
109  addParam("multiplePhotons", m_addMultiplePhotons, "If true, use all possible photons to correct the particle's 4-momentum",
110  m_addMultiplePhotons);
111  addParam("usePhotonOnlyOnce", m_usePhotonOnlyOnce,
112  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",
113  m_usePhotonOnlyOnce);
114  addParam("writeOut", m_writeOut,
115  R"DOC(If true, the output ``ParticleList`` will be saved by `RootOutput`. If false, it will be ignored when writing the file.)DOC",
116  m_writeOut);
117  }
118 
119  void BremsFinderModule::initialize()
120  {
121  //Get input lepton particle list
122  m_inputList.isRequired(m_inputListName);
123 
124  DecayDescriptor decDes;
125  decDes.init(m_inputListName);
126 
127  m_pdgCode = decDes.getMother()->getPDGCode();
128  //Check for the particles in the lepton list to have a track associated
129  if (Const::chargedStableSet.find(abs(m_pdgCode)) == Const::invalidParticle)
130  B2ERROR("[BremsFinderModule] Invalid particle list. the particles in " << m_inputListName << " should have an associated track.");
131 
132  //Get input photon particle list
133  m_gammaList.isRequired(m_gammaListName);
134 
135  decDes.init(m_gammaListName);
136  int temp_pdg = decDes.getMother()->getPDGCode();
137 
138  //Check that this is a gamma list
139  if (temp_pdg != Const::photon.getPDGCode())
140  B2ERROR("[BremsFinderModule] Invalid particle list. the particles in " << m_gammaListName << " should be photons!");
141 
142  decDes.init(m_outputListName);
143  temp_pdg = decDes.getMother()->getPDGCode();
144  // check the validity of output ParticleList name
145  if (m_inputListName == m_outputListName)
146  B2ERROR("[BremsFinderModule] Input and output particle list names are the same: " << m_inputListName);
147  else if (temp_pdg != m_pdgCode) {
148  B2ERROR("[BremsFinderModule] The input and output particle list correspond to different particles: " << m_inputListName << " --> "
149  << m_outputListName);
150  }
151 
152  // output particle
153  m_outputAntiListName = ParticleListName::antiParticleListName(m_outputListName);
154 
155  // make output lists
156  DataStore::EStoreFlags flags = m_writeOut ? DataStore::c_WriteOut : DataStore::c_DontWriteOut;
157  m_outputList.registerInDataStore(m_outputListName, flags);
158  m_outputAntiList.registerInDataStore(m_outputAntiListName, flags);
159 
160  m_particles.registerRelationTo(m_pidlikelihoods);
161  }
162 
163  void BremsFinderModule::event()
164  {
165  RelationArray particlesToMCParticles(m_particles, m_mcParticles);
166 
167  // new output particle list
168  m_outputList.create();
169  m_outputList->initialize(m_pdgCode, m_outputListName);
170 
171  m_outputAntiList.create();
172  m_outputAntiList->initialize(-1 * m_pdgCode, m_outputAntiListName);
173  m_outputAntiList->bindAntiParticleList(*(m_outputList));
174 
175  // Number of photons (calculate it here only once)
176  const unsigned int nGamma = m_gammaList->getListSize();
177 
178  // Number of leptons (calculate it here only once)
179  const unsigned int nLep = m_inputList->getListSize();
180 
181  const std::string relationName = "Bremsstrahlung";
182 
183  //In the case of only one track per photon
184  if (m_usePhotonOnlyOnce) {
185  for (unsigned n = 0; n < nGamma; n++) {
186  Particle* gamma = m_gammaList->getParticle(n);
187  //Skip this if it the best match has already been assigned (pathological case: happens only if you use the same gamma list
188  //to correct more than once. Performance studies, in which the same list is used with different options, are an example
189  if (gamma->hasExtraInfo("bestMatchIndex")) continue;
190 
191  auto cluster = gamma->getECLCluster();
192  //Get the tracks related to each photon...
193  RelationVector<Track> relatedTracks = cluster->getRelationsTo<Track>("", relationName);
194  double bestWeight = m_maximumAcceptance;
195  unsigned bestMatchIndex = 0;
196  unsigned trkIndex = 0;
197  //Loop over the related tracks...
198  for (auto trk = relatedTracks.begin(); trk != relatedTracks.end(); trk++, trkIndex++) {
199  //... and over the input particles' tracks...
200  for (unsigned i = 0; i < nLep; i++) {
201  const Particle* lepton = m_inputList->getParticle(i);
202  auto leptonTrack = lepton->getTrack();
203  //... check that the particle track corresponds to the related track....
204  if (leptonTrack->getArrayIndex() == trk->getArrayIndex()) {
205  double weight = relatedTracks.weight(trkIndex);
206  if (weight < bestWeight) {
207  bestWeight = weight;
208  //... and only select the best match among the tracks in the input list
209  bestMatchIndex = trk->getArrayIndex();
210  }
211  break; //If the particle corresponding to the related track is found, break the loop over the particles and go for the next related track
212  }
213  }
214  }
215  //... finally, add the best match index as an extra info for the photon
216  gamma->addExtraInfo("bestMatchIndex", bestMatchIndex);
217  }
218  }
219 
220  // loop over charged particles, correct them and add them to the output list
221 
222  for (unsigned i = 0; i < nLep; i++) {
223  const Particle* lepton = m_inputList->getParticle(i);
224 
225  //Get the track of this lepton...
226  auto track = lepton->getTrack();
227 
228  //... and get the bremsstrahlung clusters related to this track
229  RelationVector<ECLCluster> bremClusters = track->getRelationsFrom<ECLCluster>("", relationName);
230 
231  std::vector<std::pair <double, Particle*> > selectedGammas;
232 
233  unsigned j = 0;
234  for (auto bremCluster = bremClusters.begin(); bremCluster != bremClusters.end(); bremCluster++, j++) {
235  double weight = bremClusters.weight(j);
236 
237  if (weight > m_maximumAcceptance) continue;
238 
239  for (unsigned k = 0; k < nGamma; k++) {
240 
241  Particle* gamma = m_gammaList->getParticle(k);
242  auto cluster = gamma->getECLCluster();
243 
244  if (bremCluster->getClusterId() == cluster->getClusterId()) {
245  if (m_usePhotonOnlyOnce) { //If only one track per photon should be used...
246  if (track->getArrayIndex() == static_cast<int>
247  (gamma->getExtraInfo("bestMatchIndex"))) //... check if this track is the best match ...
248  selectedGammas.push_back(std::make_pair(weight, gamma)); //... and if it is, add it to the selected gammas
249  } else {
250  selectedGammas.push_back(std::make_pair(weight, gamma));
251  }
252  }
253 
254  } // Closes for loop on gammas
255 
256  } // Closes for loop on brem clusters
257 
258  //The 4-momentum of the new lepton in the output particle list
259  TLorentzVector new4Vec = lepton->get4Vector();
260 
261  //Sort weight-particle pairs by weight. Smaller weights go first
262  std::sort(selectedGammas.begin(), selectedGammas.end());
263 
264  //Add to this 4-momentum those of the selected photon(s)
265  for (auto const& bremsPair : selectedGammas) {
266  Particle* g = bremsPair.second;
267  new4Vec += g->get4Vector();
268  if (! m_addMultiplePhotons) break; //stop after adding the first photon
269  }
270 
271  //Create the new particle with the 4-momentum calculated before
272  Particle correctedLepton(new4Vec, lepton->getPDGCode(), Particle::EFlavorType::c_Flavored, Particle::c_Track,
273  track->getArrayIndex());
274 
275  //And add the original lepton as its daughter
276  correctedLepton.appendDaughter(lepton, false);
277 
278  const TMatrixFSym& lepErrorMatrix = lepton->getMomentumVertexErrorMatrix();
279  TMatrixFSym corLepMatrix(lepErrorMatrix);
280 
281  double bremsGammaEnergySum = 0.0;
282  //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
283  int photonIndex = 0;
284  for (auto const& bremsPair : selectedGammas) {
285  //Add the weights as extra info of the mother
286  Particle* bremsGamma = bremsPair.second;
287  std::string extraInfoName = "bremsWeightWithPhoton" + std::to_string(photonIndex);
288  correctedLepton.addExtraInfo(extraInfoName, bremsPair.first);
289  photonIndex++;
290  bremsGammaEnergySum += Variable::eclClusterE(bremsGamma);
291 
292  const TMatrixFSym& gammaErrorMatrix = bremsGamma->getMomentumVertexErrorMatrix();
293  for (int irow = 0; irow <= 3; irow++) {
294  for (int icol = irow; icol <= 3; icol++) corLepMatrix(irow, icol) += gammaErrorMatrix(irow, icol);
295  }
296  correctedLepton.appendDaughter(bremsGamma, false);
297  B2DEBUG(10, "[BremsFinderModule] Found a bremsstrahlung gamma and added its 4-vector to the charged particle");
298  if (! m_addMultiplePhotons) break; //stop after adding the first photon
299  }
300 
301  correctedLepton.setMomentumVertexErrorMatrix(corLepMatrix);
302 
303  // add the info from original lepton to the new lepton
304  correctedLepton.setVertex(lepton->getVertex());
305  correctedLepton.setPValue(lepton->getPValue());
306  correctedLepton.addExtraInfo("bremsCorrected", float(selectedGammas.size() > 0));
307  correctedLepton.addExtraInfo("bremsCorrectedPhotonEnergy", bremsGammaEnergySum);
308 
309  // add the mc relation
310  Particle* newLepton = m_particles.appendNew(correctedLepton);
311  const MCParticle* mcLepton = lepton->getRelated<MCParticle>();
312  const PIDLikelihood* pid = lepton->getPIDLikelihood();
313 
314  if (pid) newLepton->addRelationTo(pid);
315 
316  if (mcLepton != nullptr) newLepton->addRelationTo(mcLepton);
317 
318  m_outputList->addParticle(newLepton);
319 
320  } //Closes for loop on leptons
321 
322  } //Close event()
323 
324 
326 } // end Belle2 namespace
327 
Bremsstrahlung finder correction module For each lepton in the input particle list,...
EStoreFlags
Flags describing behaviours of objects etc.
Definition: DataStore.h:69
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
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:74
const Track * getTrack() const
Returns the pointer to the Track object that was used to create this Particle (ParticleType == c_Trac...
Definition: Particle.cc:814
void setPValue(float pValue)
Sets chi^2 probability of fit.
Definition: Particle.h:320
void setVertex(const TVector3 &vertex)
Sets position (decay vertex)
Definition: Particle.h:288
TVector3 getVertex() const
Returns vertex position (POCA for charged, IP for neutral FS particles)
Definition: Particle.h:551
float getPValue() const
Returns chi^2 probability of fit if done or -1.
Definition: Particle.h:587
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:840
int getPDGCode(void) const
Returns PDG code.
Definition: Particle.h:392
void addExtraInfo(const std::string &name, float value)
Sets the user-defined data of given name to the given value.
Definition: Particle.cc:1289
TLorentzVector get4Vector() const
Returns Lorentz vector.
Definition: Particle.h:477
void setMomentumVertexErrorMatrix(const TMatrixFSym &errMatrix)
Sets 7x7 error matrix.
Definition: Particle.cc:403
TMatrixFSym getMomentumVertexErrorMatrix() const
Returns 7x7 error matrix.
Definition: Particle.cc:430
void appendDaughter(const Particle *daughter, const bool updateType=true)
Appends index of daughter to daughters index array.
Definition: Particle.cc:677
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
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
Abstract base class for different kinds of events.