EIC 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 
30 //KFParticle stuff
31 #include <KFPTrack.h>
32 #include <KFParticle.h>
33 #include <KFParticleDatabase.h>
34 #include <KFVertex.h>
35 
36 #include <algorithm>
37 #include <assert.h>
38 #include <map>
39 
41 typedef std::pair<int, float> particle_pair;
43 
44 //Particle masses are in GeV
45 std::map<std::string, particle_pair> particleMasses_evtReco = kfp_particleList_evtReco.getParticleList();
46 
49  : m_constrain_to_vertex(true)
50  , m_constrain_int_mass(false)
51  , m_use_fake_pv(false)
52 {
53 }
54 
55 void KFParticle_eventReconstruction::createDecay(PHCompositeNode* topNode, std::vector<KFParticle>& selectedMother, std::vector<KFParticle>& selectedVertex,
56  std::vector<std::vector<KFParticle>>& selectedDaughters,
57  std::vector<std::vector<KFParticle>>& selectedIntermediates,
58  int& nPVs, int& multiplicity)
59 {
60  //ALICE field is 0.5T but they set it to -5 in KFParticle? Checked momentum sums and -1.4 is more accurate
61  KFParticle::SetField(-1.4e0);
62 
63  std::vector<KFParticle> primaryVertices;
64  if (m_use_fake_pv) primaryVertices.push_back(createFakePV());
65  else primaryVertices = makeAllPrimaryVertices(topNode, m_vtx_map_node_name);
66 
67  std::vector<KFParticle> daughterParticles = makeAllDaughterParticles(topNode);
68 
69  nPVs = primaryVertices.size();
70  multiplicity = daughterParticles.size();
71 
72  std::vector<int> goodTrackIndex = findAllGoodTracks(daughterParticles, primaryVertices);
73 
75  buildBasicChain(selectedMother, selectedVertex, selectedDaughters, daughterParticles, goodTrackIndex, primaryVertices);
76  else
77  buildChain(selectedMother, selectedVertex, selectedDaughters, selectedIntermediates, daughterParticles, goodTrackIndex, primaryVertices);
78 }
79 
80 /*
81  * This function is used to build a basic n-body decay without any intermediate particles such as D's or J/psi's
82  */
83 void KFParticle_eventReconstruction::buildBasicChain(std::vector<KFParticle>& selectedMotherBasic,
84  std::vector<KFParticle>& selectedVertexBasic,
85  std::vector<std::vector<KFParticle>>& selectedDaughtersBasic,
86  const std::vector<KFParticle>& daughterParticlesBasic,
87  const std::vector<int>& goodTrackIndexBasic,
88  const std::vector<KFParticle>& primaryVerticesBasic)
89 {
90  std::vector<std::vector<int>> goodTracksThatMeet = findTwoProngs(daughterParticlesBasic, goodTrackIndexBasic, m_num_tracks);
91  for (int p = 3; p < m_num_tracks + 1; ++p) goodTracksThatMeet = findNProngs(daughterParticlesBasic, goodTrackIndexBasic, goodTracksThatMeet, m_num_tracks, p);
92 
93  getCandidateDecay(selectedMotherBasic, selectedVertexBasic, selectedDaughtersBasic, daughterParticlesBasic,
94  goodTracksThatMeet, primaryVerticesBasic, 0, m_num_tracks, false, 0, true);
95 }
96 
97 /*
98  * This function is used to build a more complicated decay with intermediate particles such as D's or J/psi's
99  */
100 void KFParticle_eventReconstruction::buildChain(std::vector<KFParticle>& selectedMotherAdv,
101  std::vector<KFParticle>& selectedVertexAdv,
102  std::vector<std::vector<KFParticle>>& selectedDaughtersAdv,
103  std::vector<std::vector<KFParticle>>& selectedIntermediatesAdv,
104  const std::vector<KFParticle>& daughterParticlesAdv,
105  const std::vector<int>& goodTrackIndexAdv,
106  const std::vector<KFParticle>& primaryVerticesAdv)
107 {
108  int track_start = 0;
109  int track_stop = m_num_tracks_from_intermediate[0];
110 
111  std::vector<KFParticle> goodCandidates;
112  std::vector<KFParticle> goodVertex;
113  std::vector<KFParticle> goodDaughters[m_num_tracks];
114  std::vector<KFParticle> goodIntermediates[m_num_intermediate_states];
115  std::vector<KFParticle> potentialIntermediates[m_num_intermediate_states];
116  std::vector<std::vector<KFParticle>> potentialDaughters[m_num_intermediate_states];
117 
118  for (int i = 0; i < m_num_intermediate_states; ++i)
119  {
120  std::vector<KFParticle> vertices;
121 
122  std::vector<std::vector<int>> goodTracksThatMeet = findTwoProngs(daughterParticlesAdv, goodTrackIndexAdv, m_num_tracks_from_intermediate[i]);
123  for (int p = 3; p <= m_num_tracks_from_intermediate[i]; ++p) goodTracksThatMeet = findNProngs(daughterParticlesAdv,
124  goodTrackIndexAdv,
125  goodTracksThatMeet,
127 
128  getCandidateDecay(potentialIntermediates[i], vertices, potentialDaughters[i], daughterParticlesAdv,
129  goodTracksThatMeet, primaryVerticesAdv, track_start, track_stop, true, i, m_constrain_int_mass);
130 
131  track_start += track_stop;
132  track_stop += m_num_tracks_from_intermediate[i + 1];
133  }
134 
135  int num_tracks_used_by_intermediates = 0;
136  for (int i = 0; i < m_num_intermediate_states; ++i) num_tracks_used_by_intermediates += m_num_tracks_from_intermediate[i];
137 
138  int num_remaining_tracks = m_num_tracks - num_tracks_used_by_intermediates;
139  unsigned int num_pot_inter_a, num_pot_inter_b, num_pot_inter_c, num_pot_inter_d; //Number of potential intermediates found
140  num_pot_inter_a = potentialIntermediates[0].size();
141  num_pot_inter_b = m_num_intermediate_states < 2 ? 1 : potentialIntermediates[1].size(); //Ensure the code inside the loop below is executed
142  num_pot_inter_c = m_num_intermediate_states < 3 ? 1 : potentialIntermediates[2].size();
143  num_pot_inter_d = m_num_intermediate_states < 4 ? 1 : potentialIntermediates[3].size();
144 
145  for (unsigned int a = 0; a < num_pot_inter_a; ++a)
146  {
147  for (unsigned int b = 0; b < num_pot_inter_b; ++b)
148  {
149  for (unsigned int c = 0; c < num_pot_inter_c; ++c)
150  {
151  for (unsigned int d = 0; d < num_pot_inter_d; ++d)
152  {
153  KFParticle candidate;
154  bool isGood = false;
155  unsigned int matchIterators[4] = {a, b, c, d};
156 
157  int num_mother_decay_products = m_num_intermediate_states + num_remaining_tracks;
158  assert(num_mother_decay_products > 0);
159  KFParticle motherDecayProducts[num_mother_decay_products];
160  std::vector<KFParticle> finalTracks = potentialDaughters[0][a];
161 
162  for (int i = 0; i < m_num_intermediate_states; ++i) motherDecayProducts[i] = potentialIntermediates[i][matchIterators[i]];
163  for (int j = 1; j < m_num_intermediate_states; ++j)
164  {
165  finalTracks.insert(finalTracks.end(), potentialDaughters[j][matchIterators[j]].begin(), potentialDaughters[j][matchIterators[j]].end());
166  }
167 
168  // If there are daughter tracks coming from the mother not an intermediate, need to ensure that the intermeditate decay tracks aren't used again
169  std::vector<int> goodTrackIndexAdv_withoutIntermediates = goodTrackIndexAdv;
170  for (int m = 0; m < num_tracks_used_by_intermediates; ++m)
171  {
172  int trackID_to_remove = finalTracks[m].Id();
173  int trackElement_to_remove = -1;
174 
175  auto it = std::find_if(daughterParticlesAdv.begin(), daughterParticlesAdv.end(),
176  [&trackID_to_remove](const KFParticle& obj)
177  {return obj.Id() == trackID_to_remove;});
178 
179  if (it != daughterParticlesAdv.end())
180  {
181  trackElement_to_remove = std::distance(daughterParticlesAdv.begin(), it);
182  }
183 
184  goodTrackIndexAdv_withoutIntermediates.erase(remove(goodTrackIndexAdv_withoutIntermediates.begin(),
185  goodTrackIndexAdv_withoutIntermediates.end(), trackElement_to_remove),
186  goodTrackIndexAdv_withoutIntermediates.end());
187  }
188 
189  float required_unique_vertexID = 0;
190  for (int n = 0; n < m_num_intermediate_states; ++n)
191  required_unique_vertexID += m_intermediate_charge[n] * particleMasses_evtReco.find(m_intermediate_name[n].c_str())->second.second;
192 
193  std::vector<std::vector<std::string>> uniqueCombinations;
194  std::vector<std::vector<int>> listOfTracksToAppend;
195 
196  if (num_remaining_tracks != 0)
197  {
198  for (int i = num_tracks_used_by_intermediates; i < m_num_tracks; ++i)
199  {
200  required_unique_vertexID += m_daughter_charge[i] * particleMasses_evtReco.find(m_daughter_name[i].c_str())->second.second;
201  }
202 
203  uniqueCombinations = findUniqueDaughterCombinations(num_tracks_used_by_intermediates, m_num_tracks); //Unique comb of remaining trackIDs
204 
205  listOfTracksToAppend = appendTracksToIntermediates(motherDecayProducts, daughterParticlesAdv, goodTrackIndexAdv_withoutIntermediates, num_remaining_tracks);
206 
207  for (unsigned int n_names = 0; n_names < uniqueCombinations.size(); ++n_names)
208  {
209  uniqueCombinations[n_names].insert(begin(uniqueCombinations[n_names]), begin(m_intermediate_name), end(m_intermediate_name));
210  }
211  }
212  else
213  {
214  uniqueCombinations.push_back(m_intermediate_name);
215  listOfTracksToAppend.push_back({0});
216  }
217 
218  for (unsigned int n_tracks = 0; n_tracks < listOfTracksToAppend.size(); ++n_tracks)
219  {
220  for (int n_trackID = 0; n_trackID < num_remaining_tracks; ++n_trackID)
221  {
222  int mDP_trackElem = m_num_intermediate_states + n_trackID;
223  int dP_trackElem = listOfTracksToAppend[n_tracks][n_trackID];
224  motherDecayProducts[mDP_trackElem] = daughterParticlesAdv[dP_trackElem];
225  }
226 
227  for (unsigned int n_names = 0; n_names < uniqueCombinations.size(); ++n_names)
228  {
229  for (unsigned int i_pv = 0; i_pv < primaryVerticesAdv.size(); ++i_pv)
230  {
231  std::tie(candidate, isGood) = getCombination(motherDecayProducts, &uniqueCombinations[n_names][0], primaryVerticesAdv[i_pv],
232  m_constrain_to_vertex, false, 0, num_mother_decay_products, m_constrain_int_mass, required_unique_vertexID);
233  if (isGood)
234  {
235  goodCandidates.push_back(candidate);
236  if (m_constrain_to_vertex) goodVertex.push_back(primaryVerticesAdv[i_pv]);
237  for (int k = 0; k < m_num_intermediate_states; ++k) goodIntermediates[k].push_back(motherDecayProducts[k]);
238  for (int k = 0; k < num_tracks_used_by_intermediates; ++k) goodDaughters[k].push_back(finalTracks[k]);
239  for (int k = 0; k < num_remaining_tracks; ++k)
240  { //Need to deal with track mass and PID assignment for extra tracks
241  int trackArrayID = k + m_num_intermediate_states;
242  KFParticle slowTrack;
243  slowTrack.Create(motherDecayProducts[trackArrayID].Parameters(),
244  motherDecayProducts[trackArrayID].CovarianceMatrix(),
245  (Int_t) motherDecayProducts[trackArrayID].GetQ(),
246  particleMasses_evtReco.find(uniqueCombinations[n_names][trackArrayID].c_str())->second.second);
247  slowTrack.NDF() = motherDecayProducts[trackArrayID].GetNDF();
248  slowTrack.Chi2() = motherDecayProducts[trackArrayID].GetChi2();
249  slowTrack.SetId(motherDecayProducts[trackArrayID].Id());
250  slowTrack.SetPDG(motherDecayProducts[trackArrayID].GetQ() * particleMasses_evtReco.find(uniqueCombinations[n_names][trackArrayID].c_str())->second.first);
251  goodDaughters[k + num_tracks_used_by_intermediates].push_back(slowTrack);
252  }
253  }
254  }
255  }
256 
257  if (goodCandidates.size() != 0)
258  {
259  int bestCombinationIndex = selectBestCombination(m_constrain_to_vertex, false, goodCandidates, goodVertex);
260 
261  selectedMotherAdv.push_back(goodCandidates[bestCombinationIndex]);
262  if (m_constrain_to_vertex) selectedVertexAdv.push_back(goodVertex[bestCombinationIndex]);
263  std::vector<KFParticle> intermediates;
264  for (int i = 0; i < m_num_intermediate_states; ++i) intermediates.push_back(goodIntermediates[i][bestCombinationIndex]);
265  selectedIntermediatesAdv.push_back(intermediates);
266  std::vector<KFParticle> particles;
267  for (int i = 0; i < m_num_tracks; ++i) particles.push_back(goodDaughters[i][bestCombinationIndex]);
268  selectedDaughtersAdv.push_back(particles);
269  }
270  goodCandidates.clear();
271  goodVertex.clear();
272  for (int j = 0; j < m_num_intermediate_states; ++j) goodIntermediates[j].clear();
273  for (int j = 0; j < m_num_tracks; ++j) goodDaughters[j].clear();
274  }
275  } //Close forth intermediate
276  } //Close third intermediate
277  } //Close second intermediate
278  } //Close first intermediate
279 }
280 
281 void KFParticle_eventReconstruction::getCandidateDecay(std::vector<KFParticle>& selectedMotherCand,
282  std::vector<KFParticle>& selectedVertexCand,
283  std::vector<std::vector<KFParticle>>& selectedDaughtersCand,
284  std::vector<KFParticle> daughterParticlesCand,
285  std::vector<std::vector<int>> goodTracksThatMeetCand,
286  std::vector<KFParticle> primaryVerticesCand,
287  int n_track_start, int n_track_stop,
288  bool isIntermediate, int intermediateNumber, bool constrainMass)
289 {
290  int nTracks = n_track_stop - n_track_start;
291  std::vector<std::vector<std::string>> uniqueCombinations = findUniqueDaughterCombinations(n_track_start, n_track_stop);
292  std::vector<KFParticle> goodCandidates, goodVertex, goodDaughters[nTracks];
293  KFParticle candidate;
294  bool isGood;
295  bool fixToPV = m_constrain_to_vertex && !isIntermediate;
296 
297  float required_unique_vertexID = 0;
298  for (int i = n_track_start; i < n_track_stop; ++i) required_unique_vertexID += m_daughter_charge[i] * particleMasses_evtReco.find(m_daughter_name[i].c_str())->second.second;
299 
300  for (unsigned int i_comb = 0; i_comb < goodTracksThatMeetCand.size(); ++i_comb) //Loop over all good track combinations
301  {
302  KFParticle daughterTracks[nTracks];
303 
304  for (int i_track = 0; i_track < nTracks; ++i_track)
305  {
306  daughterTracks[i_track] = daughterParticlesCand[goodTracksThatMeetCand[i_comb][i_track]];
307  } //Build array of the good tracks in that combination
308 
309  for (unsigned int i_uc = 0; i_uc < uniqueCombinations.size(); ++i_uc) //Loop over unique track PID assignments
310  {
311  for (unsigned int i_pv = 0; i_pv < primaryVerticesCand.size(); ++i_pv) //Loop over all PVs in the event
312  {
313  std::string* names = &uniqueCombinations[i_uc][0];
314  std::tie(candidate, isGood) = getCombination(daughterTracks, names, primaryVerticesCand[i_pv], m_constrain_to_vertex,
315  isIntermediate, intermediateNumber, nTracks, constrainMass, required_unique_vertexID);
316 
317  float min_ip = 0;
318  float min_ipchi2 = 0;
319  if (isIntermediate && isGood)
320  {
321  calcMinIP(candidate, primaryVerticesCand, min_ip, min_ipchi2);
322  if (!isInRange(m_intermediate_min_ip[intermediateNumber], min_ip, m_intermediate_max_ip[intermediateNumber])
323  || !isInRange(m_intermediate_min_ipchi2[intermediateNumber], min_ipchi2, m_intermediate_max_ipchi2[intermediateNumber]))
324  isGood = false;
325  }
326 
327  if (isGood)
328  {
329  goodCandidates.push_back(candidate);
330  goodVertex.push_back(primaryVerticesCand[i_pv]);
331  for (int i = 0; i < nTracks; ++i)
332  {
333  KFParticle intParticle;
334  intParticle.Create(daughterTracks[i].Parameters(),
335  daughterTracks[i].CovarianceMatrix(),
336  (Int_t) daughterTracks[i].GetQ(),
337  particleMasses_evtReco.find(names[i].c_str())->second.second);
338  intParticle.NDF() = daughterTracks[i].GetNDF();
339  intParticle.Chi2() = daughterTracks[i].GetChi2();
340  intParticle.SetId(daughterTracks[i].Id());
341  intParticle.SetPDG(daughterTracks[i].GetQ() * particleMasses_evtReco.find(names[i].c_str())->second.first);
342  goodDaughters[i].push_back(intParticle);
343  }
344  }
345  }
346  }
347 
348  if (goodCandidates.size() != 0)
349  {
350  int bestCombinationIndex = selectBestCombination(fixToPV, isIntermediate, goodCandidates, goodVertex);
351 
352  selectedMotherCand.push_back(goodCandidates[bestCombinationIndex]);
353  if (fixToPV) selectedVertexCand.push_back(goodVertex[bestCombinationIndex]);
354  std::vector<KFParticle> particles;
355  for (int i = 0; i < nTracks; ++i) particles.push_back(goodDaughters[i][bestCombinationIndex]);
356  selectedDaughtersCand.push_back(particles);
357 
358  goodCandidates.clear();
359  goodVertex.clear();
360  for (int j = 0; j < nTracks; ++j) goodDaughters[j].clear();
361  }
362  }
363 }
364 
365 int KFParticle_eventReconstruction::selectBestCombination(bool PVconstraint, bool isAnInterMother,
366  std::vector<KFParticle> possibleCandidates,
367  std::vector<KFParticle> possibleVertex)
368 {
369  KFParticle smallestMassError = possibleCandidates[0];
370  int bestCombinationIndex = 0;
371  for (unsigned int i = 1; i < possibleCandidates.size(); ++i)
372  {
373  if (PVconstraint && !isAnInterMother)
374  {
375  if (possibleCandidates[i].GetDeviationFromVertex(possibleVertex[i]) <
376  smallestMassError.GetDeviationFromVertex(possibleVertex[bestCombinationIndex]))
377  {
378  smallestMassError = possibleCandidates[i];
379  bestCombinationIndex = i;
380  }
381  }
382  else
383  {
384  if (possibleCandidates[i].GetErrMass() < smallestMassError.GetErrMass())
385  {
386  smallestMassError = possibleCandidates[i];
387  bestCombinationIndex = i;
388  }
389  }
390  }
391 
392  return bestCombinationIndex;
393 }
394 
396 {
397  float f_vertexParameters[6] = {0};
398 
399  float f_vertexCovariance[21] = {0};
400 
401  KFParticle kfp_vertex;
402  kfp_vertex.Create(f_vertexParameters, f_vertexCovariance, 0, -1);
403  kfp_vertex.NDF() = 0;
404  kfp_vertex.Chi2() = 0;
405  kfp_vertex.SetId(0);
406 
407  return kfp_vertex;
408 }