Analysis Software
Documentation for sPHENIX simulation software
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Groups Pages
KFParticle_eventReconstruction.cc
Go to the documentation of this file. Or view the newest version in sPHENIX GitHub for file KFParticle_eventReconstruction.cc
1 /*
2  * This file is part of KFParticle package
3  * Copyright ( C ) 2007-2019 FIAS Frankfurt Institute for Advanced Studies
4  * 2007-2019 Goethe University of Frankfurt
5  * 2007-2019 Ivan Kisel <I.Kisel@compeng.uni-frankfurt.de>
6  * 2007-2019 Maksym Zyzak
7  *
8  * KFParticle is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * ( at your option ) any later version.
12  *
13  * KFParticle is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program. If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 /*****************/
23 /* Cameron Dean */
24 /* LANL 2020 */
25 /* cdean@bnl.gov */
26 /*****************/
27 
29 #include "KFParticle_Tools.h"
30 
31 // KFParticle stuff
32 #include <KFParticle.h>
33 
34 #include <algorithm>
35 #include <cassert>
36 #include <iterator> // for begin, distance, end
37 #include <map>
38 #include <memory> // for allocator_traits<>::value_type
39 #include <string> // for string
40 #include <tuple> // for tie, tuple
41 #include <utility> // for pair
42 
45 
48  : m_constrain_to_vertex(true)
49  , m_constrain_int_mass(false)
50  , m_use_fake_pv(false)
51 {
52 }
53 
54 void KFParticle_eventReconstruction::createDecay(PHCompositeNode* topNode, std::vector<KFParticle>& selectedMother, std::vector<KFParticle>& selectedVertex,
55  std::vector<std::vector<KFParticle>>& selectedDaughters,
56  std::vector<std::vector<KFParticle>>& selectedIntermediates,
57  int& nPVs, int& multiplicity)
58 {
59  std::vector<KFParticle> primaryVertices;
60  if (m_use_fake_pv)
61  {
62  primaryVertices.push_back(createFakePV());
63  }
64  else
65  {
66  primaryVertices = makeAllPrimaryVertices(topNode, m_vtx_map_node_name);
67  }
68 
69  std::vector<KFParticle> daughterParticles = makeAllDaughterParticles(topNode);
70 
71  nPVs = primaryVertices.size();
72  multiplicity = daughterParticles.size();
73 
74  std::vector<int> goodTrackIndex = findAllGoodTracks(daughterParticles, primaryVertices);
75 
77  {
78  buildBasicChain(selectedMother, selectedVertex, selectedDaughters, daughterParticles, goodTrackIndex, primaryVertices);
79  }
80  else
81  {
82  buildChain(selectedMother, selectedVertex, selectedDaughters, selectedIntermediates, daughterParticles, goodTrackIndex, primaryVertices);
83  }
84 }
85 
86 /*
87  * This function is used to build a basic n-body decay without any intermediate particles such as D's or J/psi's
88  */
89 void KFParticle_eventReconstruction::buildBasicChain(std::vector<KFParticle>& selectedMotherBasic,
90  std::vector<KFParticle>& selectedVertexBasic,
91  std::vector<std::vector<KFParticle>>& selectedDaughtersBasic,
92  const std::vector<KFParticle>& daughterParticlesBasic,
93  const std::vector<int>& goodTrackIndexBasic,
94  const std::vector<KFParticle>& primaryVerticesBasic)
95 {
96  std::vector<std::vector<int>> goodTracksThatMeet = findTwoProngs(daughterParticlesBasic, goodTrackIndexBasic, m_num_tracks);
97  for (int p = 3; p < m_num_tracks + 1; ++p)
98  {
99  goodTracksThatMeet = findNProngs(daughterParticlesBasic, goodTrackIndexBasic, goodTracksThatMeet, m_num_tracks, p);
100  }
101 
102  getCandidateDecay(selectedMotherBasic, selectedVertexBasic, selectedDaughtersBasic, daughterParticlesBasic,
103  goodTracksThatMeet, primaryVerticesBasic, 0, m_num_tracks, false, 0, true);
104 }
105 
106 /*
107  * This function is used to build a more complicated decay with intermediate particles such as D's or J/psi's
108  */
109 void KFParticle_eventReconstruction::buildChain(std::vector<KFParticle>& selectedMotherAdv,
110  std::vector<KFParticle>& selectedVertexAdv,
111  std::vector<std::vector<KFParticle>>& selectedDaughtersAdv,
112  std::vector<std::vector<KFParticle>>& selectedIntermediatesAdv,
113  const std::vector<KFParticle>& daughterParticlesAdv,
114  const std::vector<int>& goodTrackIndexAdv,
115  const std::vector<KFParticle>& primaryVerticesAdv)
116 {
117  int track_start = 0;
118  int track_stop = m_num_tracks_from_intermediate[0];
119 
120  std::vector<KFParticle> goodCandidates;
121  std::vector<KFParticle> goodVertex;
122  std::vector<KFParticle> goodDaughters[m_num_tracks];
123  std::vector<KFParticle> goodIntermediates[m_num_intermediate_states];
124  std::vector<KFParticle> potentialIntermediates[m_num_intermediate_states];
125  std::vector<std::vector<KFParticle>> potentialDaughters[m_num_intermediate_states];
126 
127  for (int i = 0; i < m_num_intermediate_states; ++i)
128  {
129  std::vector<KFParticle> vertices;
130 
131  std::vector<std::vector<int>> goodTracksThatMeet = findTwoProngs(daughterParticlesAdv, goodTrackIndexAdv, m_num_tracks_from_intermediate[i]);
132  for (int p = 3; p <= m_num_tracks_from_intermediate[i]; ++p)
133  {
134  goodTracksThatMeet = findNProngs(daughterParticlesAdv,
135  goodTrackIndexAdv,
136  goodTracksThatMeet,
138  }
139 
140  getCandidateDecay(potentialIntermediates[i], vertices, potentialDaughters[i], daughterParticlesAdv,
141  goodTracksThatMeet, primaryVerticesAdv, track_start, track_stop, true, i, m_constrain_int_mass);
142 
143  track_start += track_stop;
144  track_stop += m_num_tracks_from_intermediate[i + 1];
145  }
146 
147  int num_tracks_used_by_intermediates = 0;
148  for (int i = 0; i < m_num_intermediate_states; ++i)
149  {
150  num_tracks_used_by_intermediates += m_num_tracks_from_intermediate[i];
151  }
152 
153  int num_remaining_tracks = m_num_tracks - num_tracks_used_by_intermediates;
154  unsigned int num_pot_inter_a, num_pot_inter_b, num_pot_inter_c, num_pot_inter_d; // Number of potential intermediates found
155  num_pot_inter_a = potentialIntermediates[0].size();
156  num_pot_inter_b = m_num_intermediate_states < 2 ? 1 : potentialIntermediates[1].size(); // Ensure the code inside the loop below is executed
157  num_pot_inter_c = m_num_intermediate_states < 3 ? 1 : potentialIntermediates[2].size();
158  num_pot_inter_d = m_num_intermediate_states < 4 ? 1 : potentialIntermediates[3].size();
159 
160  for (unsigned int a = 0; a < num_pot_inter_a; ++a)
161  {
162  for (unsigned int b = 0; b < num_pot_inter_b; ++b)
163  {
164  for (unsigned int c = 0; c < num_pot_inter_c; ++c)
165  {
166  for (unsigned int d = 0; d < num_pot_inter_d; ++d)
167  {
168  KFParticle candidate;
169  bool isGood = false;
170  const unsigned int matchIterators[4] = {a, b, c, d};
171 
172  int num_mother_decay_products = m_num_intermediate_states + num_remaining_tracks;
173  assert(num_mother_decay_products > 0);
174  KFParticle motherDecayProducts[num_mother_decay_products];
175  std::vector<KFParticle> finalTracks = potentialDaughters[0][a];
176 
177  for (int i = 0; i < m_num_intermediate_states; ++i)
178  {
179  motherDecayProducts[i] = potentialIntermediates[i][matchIterators[i]];
180  }
181  for (int j = 1; j < m_num_intermediate_states; ++j)
182  {
183  finalTracks.insert(finalTracks.end(), potentialDaughters[j][matchIterators[j]].begin(), potentialDaughters[j][matchIterators[j]].end());
184  }
185 
186  // If there are daughter tracks coming from the mother not an intermediate, need to ensure that the intermeditate decay tracks aren't used again
187  std::vector<int> goodTrackIndexAdv_withoutIntermediates = goodTrackIndexAdv;
188  for (int m = 0; m < num_tracks_used_by_intermediates; ++m)
189  {
190  int trackID_to_remove = finalTracks[m].Id();
191  int trackElement_to_remove = -1;
192 
193  auto it = std::find_if(daughterParticlesAdv.begin(), daughterParticlesAdv.end(),
194  [&trackID_to_remove](const KFParticle& obj)
195  { return obj.Id() == trackID_to_remove; });
196 
197  if (it != daughterParticlesAdv.end())
198  {
199  trackElement_to_remove = std::distance(daughterParticlesAdv.begin(), it);
200  }
201 
202  goodTrackIndexAdv_withoutIntermediates.erase(remove(goodTrackIndexAdv_withoutIntermediates.begin(),
203  goodTrackIndexAdv_withoutIntermediates.end(), trackElement_to_remove),
204  goodTrackIndexAdv_withoutIntermediates.end());
205  }
206 
207  float required_unique_vertexID = 0;
208  for (int n = 0; n < m_num_intermediate_states; ++n)
209  {
210  required_unique_vertexID += m_intermediate_charge[n] * kfp_Tools_evtReco.getParticleMass(m_intermediate_name[n].c_str());
211  }
212 
213  std::vector<std::vector<std::string>> uniqueCombinations;
214  std::vector<std::vector<int>> listOfTracksToAppend;
215 
216  if (num_remaining_tracks != 0)
217  {
218  for (int i = num_tracks_used_by_intermediates; i < m_num_tracks; ++i)
219  {
220  required_unique_vertexID += m_daughter_charge[i] * kfp_Tools_evtReco.getParticleMass(m_daughter_name[i].c_str());
221  }
222 
223  uniqueCombinations = findUniqueDaughterCombinations(num_tracks_used_by_intermediates, m_num_tracks); // Unique comb of remaining trackIDs
224 
225  listOfTracksToAppend = appendTracksToIntermediates(motherDecayProducts, daughterParticlesAdv, goodTrackIndexAdv_withoutIntermediates, num_remaining_tracks);
226 
227  for (auto& uniqueCombination : uniqueCombinations)
228  {
229  uniqueCombination.insert(begin(uniqueCombination), begin(m_intermediate_name), end(m_intermediate_name));
230  }
231  }
232  else
233  {
234  uniqueCombinations.push_back(m_intermediate_name);
235  listOfTracksToAppend.push_back({0});
236  }
237 
238  for (auto& n_tracks : listOfTracksToAppend)
239  {
240  for (int n_trackID = 0; n_trackID < num_remaining_tracks; ++n_trackID)
241  {
242  int mDP_trackElem = m_num_intermediate_states + n_trackID;
243  int dP_trackElem = n_tracks[n_trackID];
244  motherDecayProducts[mDP_trackElem] = daughterParticlesAdv[dP_trackElem];
245  }
246 
247  for (auto& uniqueCombination : uniqueCombinations)
248  {
249  for (const auto& i_pv : primaryVerticesAdv)
250  {
251  std::tie(candidate, isGood) = getCombination(motherDecayProducts, &uniqueCombination[0], i_pv,
252  m_constrain_to_vertex, false, 0, num_mother_decay_products, m_constrain_int_mass, required_unique_vertexID);
253  if (isGood)
254  {
255  goodCandidates.push_back(candidate);
257  {
258  goodVertex.push_back(i_pv);
259  }
260  for (int k = 0; k < m_num_intermediate_states; ++k)
261  {
262  goodIntermediates[k].push_back(motherDecayProducts[k]);
263  }
264  for (int k = 0; k < num_tracks_used_by_intermediates; ++k)
265  {
266  goodDaughters[k].push_back(finalTracks[k]);
267  }
268  for (int k = 0; k < num_remaining_tracks; ++k)
269  { // Need to deal with track mass and PID assignment for extra tracks
270  int trackArrayID = k + m_num_intermediate_states;
271  KFParticle slowTrack;
272  slowTrack.Create(motherDecayProducts[trackArrayID].Parameters(),
273  motherDecayProducts[trackArrayID].CovarianceMatrix(),
274  (Int_t) motherDecayProducts[trackArrayID].GetQ(),
275  kfp_Tools_evtReco.getParticleMass(uniqueCombination[trackArrayID].c_str()));
276  slowTrack.NDF() = motherDecayProducts[trackArrayID].GetNDF();
277  slowTrack.Chi2() = motherDecayProducts[trackArrayID].GetChi2();
278  slowTrack.SetId(motherDecayProducts[trackArrayID].Id());
279  slowTrack.SetPDG(motherDecayProducts[trackArrayID].GetQ() * kfp_Tools_evtReco.getParticleID(uniqueCombination[trackArrayID].c_str()));
280  goodDaughters[k + num_tracks_used_by_intermediates].push_back(slowTrack);
281  }
282  }
283  }
284  }
285 
286  if (goodCandidates.size() != 0)
287  {
288  int bestCombinationIndex = selectBestCombination(m_constrain_to_vertex, false, goodCandidates, goodVertex);
289 
290  selectedMotherAdv.push_back(goodCandidates[bestCombinationIndex]);
292  {
293  selectedVertexAdv.push_back(goodVertex[bestCombinationIndex]);
294  }
295  std::vector<KFParticle> intermediates;
296  intermediates.reserve(m_num_intermediate_states);
297  for (int i = 0; i < m_num_intermediate_states; ++i)
298  {
299  intermediates.push_back(goodIntermediates[i][bestCombinationIndex]);
300  }
301  selectedIntermediatesAdv.push_back(intermediates);
302  std::vector<KFParticle> particles;
303  particles.reserve(m_num_tracks);
304  for (int i = 0; i < m_num_tracks; ++i)
305  {
306  particles.push_back(goodDaughters[i][bestCombinationIndex]);
307  }
308  selectedDaughtersAdv.push_back(particles);
309  }
310  goodCandidates.clear();
311  goodVertex.clear();
312  for (int j = 0; j < m_num_intermediate_states; ++j)
313  {
314  goodIntermediates[j].clear();
315  }
316  for (int j = 0; j < m_num_tracks; ++j)
317  {
318  goodDaughters[j].clear();
319  }
320  }
321  } // Close forth intermediate
322  } // Close third intermediate
323  } // Close second intermediate
324  } // Close first intermediate
325 }
326 
327 void KFParticle_eventReconstruction::getCandidateDecay(std::vector<KFParticle>& selectedMotherCand,
328  std::vector<KFParticle>& selectedVertexCand,
329  std::vector<std::vector<KFParticle>>& selectedDaughtersCand,
330  std::vector<KFParticle> daughterParticlesCand,
331  std::vector<std::vector<int>> goodTracksThatMeetCand,
332  std::vector<KFParticle> primaryVerticesCand,
333  int n_track_start, int n_track_stop,
334  bool isIntermediate, int intermediateNumber, bool constrainMass)
335 {
336  int nTracks = n_track_stop - n_track_start;
337  std::vector<std::vector<std::string>> uniqueCombinations = findUniqueDaughterCombinations(n_track_start, n_track_stop);
338  std::vector<KFParticle> goodCandidates, goodVertex, goodDaughters[nTracks];
339  KFParticle candidate;
340  bool isGood;
341  bool fixToPV = m_constrain_to_vertex && !isIntermediate;
342 
343  float required_unique_vertexID = 0;
344  for (int i = n_track_start; i < n_track_stop; ++i)
345  {
346  required_unique_vertexID += m_daughter_charge[i] * kfp_Tools_evtReco.getParticleMass(m_daughter_name[i].c_str());
347  }
348 
349  for (auto& i_comb : goodTracksThatMeetCand) // Loop over all good track combinations
350  {
351  KFParticle daughterTracks[nTracks];
352 
353  for (int i_track = 0; i_track < nTracks; ++i_track)
354  {
355  daughterTracks[i_track] = daughterParticlesCand[i_comb[i_track]];
356  } // Build array of the good tracks in that combination
357 
358  for (auto& uniqueCombination : uniqueCombinations) // Loop over unique track PID assignments
359  {
360  for (unsigned int i_pv = 0; i_pv < primaryVerticesCand.size(); ++i_pv) // Loop over all PVs in the event
361  {
362  std::string* names = &uniqueCombination[0];
363  std::tie(candidate, isGood) = getCombination(daughterTracks, names, primaryVerticesCand[i_pv], m_constrain_to_vertex,
364  isIntermediate, intermediateNumber, nTracks, constrainMass, required_unique_vertexID);
365 
366  if (isIntermediate && isGood)
367  {
368  float min_ip = 0;
369  float min_ipchi2 = 0;
370  calcMinIP(candidate, primaryVerticesCand, min_ip, min_ipchi2);
371  if (!isInRange(m_intermediate_min_ip[intermediateNumber], min_ip, m_intermediate_max_ip[intermediateNumber]) || !isInRange(m_intermediate_min_ipchi2[intermediateNumber], min_ipchi2, m_intermediate_max_ipchi2[intermediateNumber]))
372  {
373  isGood = false;
374  }
375  }
376 
377  if (isGood)
378  {
379  goodCandidates.push_back(candidate);
380  goodVertex.push_back(primaryVerticesCand[i_pv]);
381  for (int i = 0; i < nTracks; ++i)
382  {
383  KFParticle intParticle;
384  intParticle.Create(daughterTracks[i].Parameters(),
385  daughterTracks[i].CovarianceMatrix(),
386  (Int_t) daughterTracks[i].GetQ(),
387  kfp_Tools_evtReco.getParticleMass(names[i]));
388  intParticle.NDF() = daughterTracks[i].GetNDF();
389  intParticle.Chi2() = daughterTracks[i].GetChi2();
390  intParticle.SetId(daughterTracks[i].Id());
391  intParticle.SetPDG(daughterTracks[i].GetQ() * kfp_Tools_evtReco.getParticleID(names[i]));
392  goodDaughters[i].push_back(intParticle);
393  }
394  }
395  }
396  }
397 
398  if (goodCandidates.size() != 0)
399  {
400  int bestCombinationIndex = selectBestCombination(fixToPV, isIntermediate, goodCandidates, goodVertex);
401 
402  selectedMotherCand.push_back(goodCandidates[bestCombinationIndex]);
403  if (fixToPV)
404  {
405  selectedVertexCand.push_back(goodVertex[bestCombinationIndex]);
406  }
407  std::vector<KFParticle> particles;
408  particles.reserve(nTracks);
409  for (int i = 0; i < nTracks; ++i)
410  {
411  particles.push_back(goodDaughters[i][bestCombinationIndex]);
412  }
413  selectedDaughtersCand.push_back(particles);
414 
415  goodCandidates.clear();
416  goodVertex.clear();
417  for (int j = 0; j < nTracks; ++j)
418  {
419  goodDaughters[j].clear();
420  }
421  }
422  }
423 }
424 
425 int KFParticle_eventReconstruction::selectBestCombination(bool PVconstraint, bool isAnInterMother,
426  std::vector<KFParticle> possibleCandidates,
427  std::vector<KFParticle> possibleVertex)
428 {
429  KFParticle smallestMassError = possibleCandidates[0];
430  int bestCombinationIndex = 0;
431  for (unsigned int i = 1; i < possibleCandidates.size(); ++i)
432  {
433  if (PVconstraint && !isAnInterMother)
434  {
435  if (possibleCandidates[i].GetDeviationFromVertex(possibleVertex[i]) <
436  smallestMassError.GetDeviationFromVertex(possibleVertex[bestCombinationIndex]))
437  {
438  smallestMassError = possibleCandidates[i];
439  bestCombinationIndex = i;
440  }
441  }
442  else
443  {
444  if (possibleCandidates[i].GetErrMass() < smallestMassError.GetErrMass())
445  {
446  smallestMassError = possibleCandidates[i];
447  bestCombinationIndex = i;
448  }
449  }
450  }
451 
452  return bestCombinationIndex;
453 }
454 
456 {
457  float f_vertexParameters[6] = {0};
458 
459  float f_vertexCovariance[21] = {0};
460 
461  KFParticle kfp_vertex;
462  kfp_vertex.Create(f_vertexParameters, f_vertexCovariance, 0, -1);
463  kfp_vertex.NDF() = 0;
464  kfp_vertex.Chi2() = 0;
465  kfp_vertex.SetId(0);
466 
467  return kfp_vertex;
468 }