Belle II Software  light-2205-abys
TrackIsoCalculatorModule.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 #include <analysis/modules/TrackIsoCalculator/TrackIsoCalculatorModule.h>
10 #include <analysis/DecayDescriptor/DecayDescriptorParticle.h>
11 
12 #include <cmath>
13 #include <iomanip>
14 
15 using namespace Belle2;
16 
17 REG_MODULE(TrackIsoCalculator);
18 
20 {
21  // Set module properties
23  R"DOC(Calculate track isolation variables on the charged stable particles, or selected charged daughters, of the input ParticleList.)DOC");
24 
25  // Parameter definitions
26  addParam("decayString",
28  "The name of the input charged stable particle list, e.g. ``mu+:all``, or a composite particle w/ charged stable daughters for which distances are to be calculated, e.g. ``D0 -> ^K- pi+``. Note that in the latter case we allow only one daughter to be selected in the decay string per module instance.");
29  addParam("particleListReference",
31  "The name of the input ParticleList of reference tracks. Must be a charged stable particle as defined in Const::chargedStableSet.");
32  addParam("detectorSurface",
34  "The name of the detector at whose (cylindrical) surface(s) we extrapolate each helix's polar and azimuthal angle. Allowed values: {CDC, TOP, ARICH, ECL, KLM}.");
35  addParam("useHighestProbMassForExt",
37  "If this option is set, the helix extrapolation for the target and reference particles will use the track fit result for the most probable mass hypothesis, namely, the one that gives the highest chi2Prob of the fit.",
38  bool(false));
39 }
40 
42 {
43 }
44 
46 {
47  m_event_metadata.isRequired();
48 
49  bool valid = m_decaydescriptor.init(m_decayString);
50  if (!valid) {
51  B2ERROR("Decay string " << m_decayString << " is invalid.");
52  }
53 
55 
56  m_pListTarget.isRequired(mother->getFullName());
58 
59  if (m_nSelectedDaughters > 1) {
60  B2ERROR("More than one daughter is selected in the decay string " << m_decayString << ".");
61  }
62 
64 
65  B2INFO("Calculating track-based isolation variables at " << m_detSurface << " surface for the decay string "
66  << m_decayString << " using the reference ParticleList " << m_pListReferenceName << ".");
67 
69  B2ERROR("Selected ParticleList in decay string " << m_decayString << " and/or reference ParticleList " << m_pListReferenceName <<
70  " is not that of a valid particle in Const::chargedStableSet!");
71  }
72 
74  B2INFO("Will use track fit result for the most probable mass hypothesis in helix extrapolation.");
75  }
76 
77  // Define the name of the variable to be stored as extraInfo.
78  m_extraInfoName = "distToClosestTrkAt" + m_detSurface + "_VS_" + m_pListReferenceName;
80  m_extraInfoName += "__useHighestProbMassForExt";
81  }
82 
83  m_isSurfaceInDet.insert({"CDC", m_detSurface.find("CDC") != std::string::npos});
84  m_isSurfaceInDet.insert({"TOP", m_detSurface.find("TOP") != std::string::npos});
85  m_isSurfaceInDet.insert({"ARICH", m_detSurface.find("ARICH") != std::string::npos});
86  m_isSurfaceInDet.insert({"ECL", m_detSurface.find("ECL") != std::string::npos});
87  m_isSurfaceInDet.insert({"KLM", m_detSurface.find("KLM") != std::string::npos});
88 }
89 
91 {
92  B2DEBUG(11, "Start processing EVENT: " << m_event_metadata->getEvent());
93 
94  const auto nMotherParticles = m_pListTarget->getListSize();
95 
96  // Fill transient container of all selected charged particles in the decay
97  // for which the distance to nearest neighbour is to be calculated.
98  // If the input ParticleList is that of standard charged particles, just copy
99  // the whole list over, otherwise loop over the selected charged daughters.
100  std::unordered_map<unsigned int, const Particle*> targetParticles;
101  targetParticles.reserve(nMotherParticles);
102 
103  for (unsigned int iPart(0); iPart < nMotherParticles; ++iPart) {
104 
105  auto iParticle = m_pListTarget->getParticle(iPart);
106 
107  if (m_nSelectedDaughters) {
108  for (auto* iDaughter : m_decaydescriptor.getSelectionParticles(iParticle)) {
109  // Check if the distance for this target particle has been set already,
110  // e.g. by a previous instance of this module.
111  if (iDaughter->hasExtraInfo(m_extraInfoName)) {
112  continue;
113  }
114  targetParticles.insert({iDaughter->getMdstArrayIndex(), iDaughter});
115  }
116  } else {
117  if (iParticle->hasExtraInfo(m_extraInfoName)) {
118  continue;
119  }
120  targetParticles.insert({iParticle->getMdstArrayIndex(), iParticle});
121  }
122  }
123 
124  const auto nParticlesTarget = targetParticles.size();
125  const auto nParticlesReference = m_pListReference->getListSize();
126 
127  B2DEBUG(11, "Detector surface: " << m_detSurface << "\n"
128  << "nMotherParticles: " << nMotherParticles << "\n"
129  << "nParticlesTarget: " << nParticlesTarget << "\n"
130  << "nParticlesReference: " << nParticlesReference);
131 
132  double dummyDist(-1.0);
133 
134  // Store the pair-wise distances in a map,
135  // where the keys are pairs of mdst indexes.
136  std::map<std::pair<unsigned int, unsigned int>, double> particleMdstIdxPairsToDist;
137 
138  for (const auto& targetParticle : targetParticles) {
139  auto iMdstIdx = targetParticle.first;
140  auto iParticle = targetParticle.second;
141 
142  for (unsigned int jPart(0); jPart < nParticlesReference; ++jPart) {
143 
144  auto jParticle = m_pListReference->getParticle(jPart);
145  auto jMdstIdx = jParticle->getMdstArrayIndex();
146 
147  auto partMdstIdxPair = std::make_pair(iMdstIdx, jMdstIdx);
148 
149  // Set dummy distance if same particle.
150  if (iMdstIdx == jMdstIdx) {
151  particleMdstIdxPairsToDist[partMdstIdxPair] = dummyDist;
152  continue;
153  }
154  // If:
155  //
156  // - the mass hypothesis of the best fit is used, OR
157  // - the mass hypothesis of the 'default' fit of the two particles is the same,
158  //
159  // avoid re-doing the calculation if a pair with the flipped mdst indexes in the map already exists.
160  if (m_useHighestProbMassForExt || (iParticle->getPDGCodeUsedForFit() == jParticle->getPDGCodeUsedForFit())) {
161  if (particleMdstIdxPairsToDist.count({jMdstIdx, iMdstIdx})) {
162  particleMdstIdxPairsToDist[partMdstIdxPair] = particleMdstIdxPairsToDist[ {jMdstIdx, iMdstIdx}];
163  continue;
164  }
165  }
166  // Calculate the pair-wise distance.
167  particleMdstIdxPairsToDist[partMdstIdxPair] = this->getDistAtDetSurface(iParticle, jParticle);
168  }
169  }
170 
171  // For each particle in the input list, find the minimum among all distances to the reference particles.
172  for (const auto& targetParticle : targetParticles) {
173  auto iMdstIdx = targetParticle.first;
174  auto iParticle = targetParticle.second;
175 
176  std::vector<double> iDistances;
177  for (const auto& [mdstIdxs, dist] : particleMdstIdxPairsToDist) {
178  if (mdstIdxs.first == iMdstIdx) {
179  if (!std::isnan(dist) && dist >= 0) {
180  iDistances.push_back(dist);
181  }
182  }
183  }
184 
185  if (!iDistances.size()) {
186  continue;
187  }
188 
189  const auto minDist = *std::min_element(std::begin(iDistances), std::end(iDistances));
190 
191  B2DEBUG(10, "Particle w/ mdstIndex[" << iMdstIdx << "] (PDG = "
192  << iParticle->getPDGCode() << "). Closest charged partner at "
193  << m_detSurface << " surface is found at D = " << minDist << " [cm]"
194  << "\n"
195  << "Storing ExtraInfo w/ name: " << m_extraInfoName);
196 
197  m_particles[iParticle->getArrayIndex()]->addExtraInfo(m_extraInfoName, minDist);
198 
199  }
200 
201  B2DEBUG(11, "Finished processing EVENT: " << m_event_metadata->getEvent());
202 }
203 
205 {
206 }
207 
208 double TrackIsoCalculatorModule::getDistAtDetSurface(const Particle* iParticle, const Particle* jParticle)
209 {
210 
211  // Radius and z boundaries of the cylinder describing this detector's surface.
212  const auto rho = m_detSurfBoundaries[m_detSurface].m_rho;
213  const auto zfwd = m_detSurfBoundaries[m_detSurface].m_zfwd;
214  const auto zbwd = m_detSurfBoundaries[m_detSurface].m_zbwd;
215  // Polar angle boundaries between barrel and endcaps.
216  const auto th_fwd = m_detSurfBoundaries[m_detSurface].m_th_fwd;
217  const auto th_fwd_brl = m_detSurfBoundaries[m_detSurface].m_th_fwd_brl;
218  const auto th_bwd_brl = m_detSurfBoundaries[m_detSurface].m_th_bwd_brl;
219  const auto th_bwd = m_detSurfBoundaries[m_detSurface].m_th_bwd;
220 
221  std::string nameExtTheta = "helixExtTheta(" + std::to_string(rho) + "," + std::to_string(zfwd) + "," + std::to_string(zbwd) + ")";
223  nameExtTheta.replace(nameExtTheta.size() - 1, 1, ", 1)");
224  }
225  const auto iExtTheta = std::get<double>(Variable::Manager::Instance().getVariable(nameExtTheta)->function(iParticle));
226  const auto jExtTheta = std::get<double>(Variable::Manager::Instance().getVariable(nameExtTheta)->function(jParticle));
227 
228  std::string nameExtPhi = "helixExtPhi(" + std::to_string(rho) + "," + std::to_string(zfwd) + "," + std::to_string(zbwd) + ")";
230  nameExtPhi.replace(nameExtPhi.size() - 1, 1, ", 1)");
231  }
232  const auto iExtPhi = std::get<double>(Variable::Manager::Instance().getVariable(nameExtPhi)->function(iParticle));
233  const auto jExtPhi = std::get<double>(Variable::Manager::Instance().getVariable(nameExtPhi)->function(jParticle));
234 
235  const auto iExtInBarrel = (iExtTheta >= th_fwd_brl && iExtTheta < th_bwd_brl);
236  const auto jExtInBarrel = (jExtTheta >= th_fwd_brl && jExtTheta < th_bwd_brl);
237 
238  const auto iExtInFWDEndcap = (iExtTheta >= th_fwd && iExtTheta < th_fwd_brl);
239  const auto jExtInFWDEndcap = (jExtTheta >= th_fwd && jExtTheta < th_fwd_brl);
240 
241  const auto iExtInBWDEndcap = (iExtTheta >= th_bwd_brl && iExtTheta < th_bwd);
242  const auto jExtInBWDEndcap = (jExtTheta >= th_bwd_brl && jExtTheta < th_bwd);
243 
244  if (m_isSurfaceInDet["CDC"] || m_isSurfaceInDet["TOP"]) {
245 
246  // If any of the two extrapolated tracks is not in the barrel region of the CDC/TOP, the distance is undefined.
247  if (!iExtInBarrel || !jExtInBarrel) {
248  return std::numeric_limits<double>::quiet_NaN();
249  }
250 
251  // For CDC and TOP, we calculate the distance between the points where the two input
252  // extrapolated track helices cross the input detector's cylindrical surface
253  // on the (rho, phi) plane. Namely, this is the cord length of the arc
254  // that subtends deltaPhi.
255  auto diffPhi = jExtPhi - iExtPhi;
256  if (std::abs(diffPhi) > M_PI) {
257  diffPhi = (diffPhi > M_PI) ? diffPhi - 2 * M_PI : 2 * M_PI + diffPhi;
258  }
259 
260  return 2.0 * rho * sin(std::abs(diffPhi) / 2.0);
261 
262  } else {
263 
264  if (m_isSurfaceInDet["ARICH"]) {
265  // If any of the two tracks is not in the ARICH theta acceptance, the distance is undefined.
266  if (!iExtInFWDEndcap || !jExtInFWDEndcap) {
267  return std::numeric_limits<double>::quiet_NaN();
268  }
269  }
270  if (m_isSurfaceInDet["ECL"] || m_isSurfaceInDet["KLM"]) {
271 
272  // For ECL and KLM, we require track pairs to be both in the barrel,
273  // both in the FWD endcap, or both in the FWD endcap. Otherwise, the distance is undefined.
274  if (
275  !(iExtInBarrel && jExtInBarrel) &&
276  !(iExtInFWDEndcap && jExtInFWDEndcap) &&
277  !(iExtInBWDEndcap && jExtInBWDEndcap)
278  ) {
279  return std::numeric_limits<double>::quiet_NaN();
280  }
281  }
282 
283  // Ok, we know theta and phi.
284  // Let's extract (spherical) R by using the transformation:
285  //
286  // 1. rho = r * sin(theta)
287  // 2. phi = phi
288  // 3. z = r * cos(theta)
289  //
290  // The formula to be inverted depends on where each extrapolated track's theta is found:
291  // if in barrel, use 1. (rho is known), if in fwd/bwd endcap, use 3. (zfwd/zbwd is known).
292 
293  const auto iExtR = (iExtTheta >= th_fwd_brl && iExtTheta < th_bwd_brl) ? rho / sin(iExtTheta) : ((iExtTheta >= th_fwd
294  && iExtTheta < th_fwd_brl) ? zfwd / cos(iExtTheta) : zbwd / cos(iExtTheta));
295  const auto jExtR = (jExtTheta >= th_fwd_brl && jExtTheta < th_bwd_brl) ? rho / sin(jExtTheta) : ((jExtTheta >= th_fwd
296  && jExtTheta < th_fwd_brl) ? zfwd / cos(jExtTheta) : zbwd / cos(jExtTheta));
297 
298  return sqrt((iExtR * iExtR) + (jExtR * jExtR) - 2 * iExtR * jExtR * (sin(iExtTheta) * sin(jExtTheta) * cos(iExtPhi - jExtPhi)
299  + cos(iExtTheta) * cos(jExtTheta)));
300 
301  }
302 
303  return std::numeric_limits<double>::quiet_NaN();
304 }
305 
307 {
308 
309  bool checkPList = false;
310 
311  if (!m_nSelectedDaughters) {
313  } else {
314  for (int pdgcode : m_decaydescriptor.getSelectionPDGCodes()) {
315  checkPList = Const::chargedStableSet.find(abs(pdgcode)) != Const::invalidParticle;
316  if (!checkPList) {
317  break;
318  }
319  }
320  }
321 
322  DecayDescriptor dd;
323  bool checkPListReference = false;
324  if (dd.init(m_pListReferenceName)) {
325  checkPListReference = Const::chargedStableSet.find(abs(dd.getMother()->getPDGCode())) != Const::invalidParticle;
326  }
327 
328  return (checkPList and checkPListReference);
329 };
const ParticleType & find(int pdg) const
Returns particle in set with given PDG code, or invalidParticle if not found.
Definition: Const.h:452
static const ParticleSet chargedStableSet
set of charged stable particles
Definition: Const.h:499
static const ParticleType invalidParticle
Invalid particle, used internally.
Definition: Const.h:561
Represents a particle in the DecayDescriptor.
int getPDGCode() const
Return PDG code.
std::string getFullName() const
returns the full name of the particle full_name = name:label
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.
std::vector< int > getSelectionPDGCodes()
Return list of PDG codes of selected particles.
std::vector< std::string > getSelectionNames()
Return list of human readable names of selected particles.
std::vector< const Particle * > getSelectionParticles(const Particle *particle)
Get a vector of pointers with selected daughters in the decay tree.
Base class for Modules.
Definition: Module.h:72
void setDescription(const std::string &description)
Sets the description of the module.
Definition: Module.cc:214
Class to store reconstructed particles.
Definition: Particle.h:74
std::unordered_map< std::string, DetSurfCylBoundaries > m_detSurfBoundaries
Map that associates to each detector its valid cylindrical surface layer's boundaries.
StoreObjPtr< EventMetaData > m_event_metadata
The event information.
~TrackIsoCalculatorModule() override
Destructor, use this to clean up anything you created in the constructor.
void initialize() override
Use this to initialize resources or memory your module needs.
void event() override
Called once for each event.
std::string m_decayString
The name of the input charged stable particle list, or composite particle w/ charged stable daughters...
StoreObjPtr< ParticleList > m_pListTarget
The input ParticleList object for which distances are to be calculated.
StoreArray< Particle > m_particles
StoreArray of Particles.
void terminate() override
Module terminate().
std::string m_pListReferenceName
The name of the input ParticleList of reference tracks.
bool onlySelectedStdChargedInDecay()
Check whether input particle list and reference list are of a valid charged stable particle.
DecayDescriptor m_decaydescriptor
< Decay descriptor of decays to look for.
std::unordered_map< std::string, bool > m_isSurfaceInDet
Associate the detector flag to a boolean flag to quickly tell which detector it belongs too.
TrackIsoCalculatorModule()
Constructor: Sets the description, the properties and the parameters of the module.
std::string m_extraInfoName
The name of the distance variable to be added to each particle as extraInfo.
double getDistAtDetSurface(const Particle *iParticle, const Particle *jParticle)
Calculate the distance between the points where the two input extrapolated track helices cross the gi...
unsigned short m_nSelectedDaughters
The number of selected daughters in the decay string.
StoreObjPtr< ParticleList > m_pListReference
The input ParticleList object of reference tracks.
std::string m_detSurface
The name of the detector at whose inner (cylindrical) surface we extrapolate each track's polar and a...
bool m_useHighestProbMassForExt
If this option is set, the helix extrapolation for the target and reference particles will use the tr...
static Manager & Instance()
get singleton instance.
Definition: Manager.cc:25
REG_MODULE(B2BIIConvertBeamParams)
Register the module.
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
Abstract base class for different kinds of events.
Definition: ClusterUtils.h:23