KaliVeda
Toolkit for HIC analysis
KVFilterGroupReconstructor.cpp
1 #include "KVFilterGroupReconstructor.h"
2 #include "KVIDZAGrid.h"
3 #include "KVMultiDetArray.h"
4 #include "KVNucleusEvent.h"
5 
6 std::unordered_map<std::string,bool> KVFilterGroupReconstructor::audit_id_extensions;
7 
8 
13 
15 {
16  // We look for a simulated particle stopped in the same node with the same trajectory.
17  // We use the actual trajectory of the simulated particle to get the right reconstruction trajectory
18 
19  //Info("get_recon_traj","traj=%s node=%s",traj->GetPathString().Data(), node->GetName());
20  KVReconNucTrajectory* Rtraj{nullptr};
21  for (auto& n : EventIterator(fSimEvent.get())) {
22  TString stop = n.GetParameters()->GetTStringValue("STOPPING DETECTOR");
23  //Info("get_recon_traj", "stop=%s", stop.Data());
24  if (stop == node->GetName()) {
26  if (Rtraj) {
27  //Info("get_recon_traj","Rtraj=%s TRAJECTORY=%s",Rtraj->GetPathString().Data(),n.GetParameters()->GetStringValue("TRAJECTORY"));
28  auto traj_string = n.GetParameters()->GetTStringValue("TRAJECTORY");
29  auto traj_in_grp = (KVGeoDNTrajectory*)GetGroup()->GetTrajectories()->FindObject(traj_string);
30  if ((Rtraj->GetPathString() == traj_string) || (traj_in_grp && Rtraj->ContainsTrajectory(traj_in_grp))) {
31  // correspondence recon <-> simu
32  //Info("get_recon_traj","sim particle added to part_correspond");
34  // copy simulated particle parameters to reconstructed particle
35  n.GetParameters()->Copy(*current_nuc_recon->GetParameters());
36  }
37  }
38  }
39  }
40  if (!Rtraj) {
41  Info("get_recon_traj_for_particle", "%p : noRtraj for this: %s %s", this, node->GetName(), traj->GetPathString().Data());
42  if (!fSimEvent->GetMult()) {
43  Info("get_recon_traj_for_particle", "%p : sim event is empty", this);
44  }
45  }
46  return Rtraj;
47 }
48 
49 
50 
56 
58 {
59  // check associated simulated particle passes identification threshold
60  //
61  // if more than one simulated particle stopped in the same detector, we add the Z, A and E of each particle together
62  // and merge the lists of parameters into one.
63 
64  int multihit;
65  if ((multihit = part_correspond[&nuc].GetEntries()) > 1) {
66  KVNucleus sum_part;
67  TIter next(&part_correspond[&nuc]);
68  KVNucleus* N;
70  // the parameter lists are added to q because the KVNucleus operator+ does
71  // not retain any parameters from either of its arguments
72  while ((N = (KVNucleus*)next())) {
73  sum_part += *N;
74  q.Merge(*N->GetParameters());
75  }
76  sum_part.GetParameters()->Merge(q);
77  auto sim_part = (KVNucleus*)part_correspond[&nuc].First();
78  // replace properties of first nucleus in list with summed properties
79  part_correspond[&nuc].Clear();
80  sum_part.Copy(*sim_part);
81  part_correspond[&nuc].Add(sim_part);
82  sim_part->SetParameter("PILEUP", Form("%d particles in %s", multihit, nuc.GetStoppingDetector()->GetName()));
83  // check 'string' parameters (TRAJECTORY,STOPPING DETECTOR,DETECTED)
84  // 'merging' string parameters forms a comma-separated list of individual values
85  // these should be replaced with a single value - unless they are not all the same!?!
86  for (auto& param : *sim_part->GetParameters()) {
87  KVString unique_value;
88  if (param.IsString()) {
89  KVString sparam(param.GetString());
90  sparam.Begin(",");
91  auto next = sparam.Next();
92  if (unique_value.IsNull())
93  unique_value = next;
94  else {
95  if (next != unique_value) {
96  Warning("identify_particle", "different values of %s for %d-particle pileup in %s:%s, %s, ...",
97  param.GetName(), multihit, nuc.GetStoppingDetector()->GetName(), unique_value.Data(), next.Data());
98  }
99  }
100  param.Set(unique_value);
101  }
102  }
103  nuc.GetParameters()->Concatenate(*sim_part->GetParameters());
104  }
105  auto sim_part = (KVNucleus*)part_correspond[&nuc].First();
106 
107  IDR->SetIDType(idt->GetType());
108  IDR->IDattempted = true;
109 
110  // why does this happen???
111  if (!sim_part) {
112  Warning("identify_particle", "sim_part=nullptr; multihit=%d", multihit);
113  Warning("identify_particle", "sim_event contains:");
114  fSimEvent->Print();
115  Warning("identify_particle", "recon_nuc stopping detector is: %s", nuc.GetStoppingDetector()->GetName());
116  nuc.Print();
117  Error("identify_particle", "no sim_part for recon_nuc!");
118  IDR->IDOK=false;
119  return;
120  }
121 
122  check_identification_threshold(sim_part, idt, IDR);
123 
124  // special case: if particle ends up in a deadzone or punches through all detector layers, having a residual energy,
125  // it would not be possible to identify it correctly.
126 // if (sim_part->GetParameters()->HasDoubleParameter("RESIDUAL ENERGY")) {
127 // IDR->IDOK = false;
128 // IDR->IDcode = GetGroup()->GetParentStructure<KVMultiDetArray>()->GetBadIDCode();
129 // IDR->IDquality = KVIDZAGrid::kICODE8;
130 // IDR->SetComment(Form("particle incompletely detected, DETECTED=%s", sim_part->GetParameters()->GetStringValue("DETECTED")));
131 // }
132 }
133 
134 
135 
144 
146 {
147  // Check that identification is possible for the given nucleus with its Z, A and (total) kinetic energy.
148  // The nucleus in question may be from the simulation (KVSimNucleus) or from the reconstruction (KVReconstructedNucleus).
149  // The KVIdentificationResult (identification status of the nucleus) may be changed by this method:
150  // a particle previously thought identified may become unidentified here, as the reconstructed energy is
151  // lower than the simulated energy, and therefore below the experimental threshold, although the "real"
152  // energy of the simulated nucleus permitted its identification. If we don't do this, we end up with the
153  // inconsistent final result of identified particles with energies below the experimental identification threshold.
154 
155  if (fDataQAudit) {
156  // use experimental thresholds etc. to decide what can be identified
157  int A = nuc->GetA();
158  auto status = fDataQAudit->CanIdentify(get_id_telescope_name_for_audit(idt), nuc->GetZ(), A, nuc->GetEnergy());
159 
160  // if no Z as high as the one of this particle is included in the audit, and if it has been
161  // authorized for this kind of telescope, we will use the old method of theoretical threshold
162  // to determine whether it can be identified or not (see below)
165  {
166  std::stringstream comments;
167  comments << status;
168  if (status) {
169  IDR->IDOK = true;
170  IDR->Z = nuc->GetZ();
171  IDR->Zident = true;
172  IDR->A = A; // may have been changed in CanIdentify()
173  IDR->IDcode = idt->GetIDCode();
174  IDR->Aident = false;
180  IDR->Aident = true;
181  }
184  IDR->Aident = true;
185  }
187  IDR->Aident = true;
188  }
189  else {
190  IDR->IDOK = false;
199  )
200  Warning("identify_particle",
201  "%s : %s Z=%d A=%d E=%f", comments.str().c_str(),
202  idt->GetName(), nuc->GetZ(), nuc->GetA(), nuc->GetEnergy());
203  }
204  IDR->SetComment(comments.str().c_str());
205  return;
206  }
207  }
208 
209  // either no data quality audit is used, or no Z as high as the one of this particle is included in the audit,
210  // and it is authorized for this kind of telescope to try to extend the identification beyond the limit of the audit
211  if (idt->CanIdentify(nuc->GetZ(), nuc->GetA())) {
212  IDR->Z = nuc->GetZ();
213  IDR->A = nuc->GetA();
215  IDR->IDOK = true;
216  IDR->IDcode = idt->GetIDCode();
218  // set mass identification status depending on telescope, Z, A & energy of simulated particle
219  idt->SetIdentificationStatus(IDR, nuc);
220  }
221  else {
222  IDR->IDOK = false;
223  IDR->IDcode = GetGroup()->GetParentStructure<KVMultiDetArray>()->GetIDCodeForParticlesStoppingInFirstStageOfTelescopes();
225  IDR->SetComment("below threshold for identification");
226  }
227  }
228  else {
229  IDR->IDOK = false;
230  }
231 }
232 
233 
234 
237 
239 {
240  // After first round of identification in group, try to identify remaining particles
241 
242  int num_ident_0, num_ident;
243  int num_unident_0, num_unident;
244  do {
246  num_ident = num_ident_0 = GetNIdentifiedInGroup();
247  num_unident_0 = GetNUnidentifiedInGroup();
248  Reconstruct();
249  num_unident = GetNUnidentifiedInGroup();
250  if (num_unident > num_unident_0) {
251  Identify();
252  num_ident = GetNIdentifiedInGroup();
253  if (num_ident > num_ident_0) Calibrate();
254  }
255  }
256  while (num_ident > num_ident_0); // continue until no more new identifications occur
257 }
258 
259 
260 
261 
262 
264 
266 {
267  current_nuc_recon = part;
268 
269  try {
271  } catch (const std::exception& e) {
272  Error("ReconstructParticle", "Caught exception: %s", e.what());
273  throw;
274  }
275 
277  while (auto Nd = part->GetReconstructionTrajectory()->GetNextNode()) {
278  // energy_loss will contain the sum of energy losses (dE) measured in the *active* layer of each detector
279  // the contribution to the energy of each particle may be greater than this after correcting for
280  // dead layers etc.
281  energy_loss[Nd->GetName()] = Nd->GetDetector()->GetEnergyLoss();
282  ++number_uncalibrated[Nd->GetName()];
283  }
284 }
285 
286 
287 
289 
291 {
293 
294  if (PART.IsIdentified()) {
296  KVGeoDetectorNode* node;
297  while ((node = PART.GetReconstructionTrajectory()->GetNextNode())) {
298  --number_unidentified[node->GetName()];
299  }
300  }
301 }
302 
303 
304 
308 
310 {
311  // Calculate and set the energy of a (previously identified) reconstructed particle,
312  // including an estimate of the energy loss in the target.
313 
314  PART->SetIsCalibrated();
315  PART->SetECode(GetGroup()->GetParentStructure<KVMultiDetArray>()->GetNormalCalibrationCode()); // => general code for calibration with no problems, no calculated energy losses
316  double Einc = 0;
318  while (auto node = PART->GetReconstructionTrajectory()->GetNextNode()) {
319  double edet, dE;
320  auto det = node->GetDetector();
321  //Info("Calib", "det=%s", det->GetName());
322  //Info("Calib", "number_uncalibrated=%d, energy_loss=%f", number_uncalibrated[det->GetName()], energy_loss[det->GetName()]);
323  // deal with pileup in detectors
324  if (number_uncalibrated[det->GetName()] > 1 || (det != PART->GetStoppingDetector() && number_unidentified[det->GetName()] > 0)) {
325  //Info("Calib", "number_uncalib[%s]=%d, Einc=%f", det->GetName(), number_uncalibrated[det->GetName()], Einc);
326  // there are other uncalibrated particles which hit this detector
327  // the contribution for this particle must be calculated from the residual energy (Einc)
328  // deposited in all detectors so far (if any)
329  if (Einc > 0) {
330  // calculate dE in active layer for particle
331  dE = det->GetDeltaEFromERes(PART->GetZ(), PART->GetA(), Einc);
332  if (dE == 0) {
333  //Info("Calib", "dE from Eres gives 0! dead");
334  PART->SetECode(GetGroup()->GetParentStructure<KVMultiDetArray>()->GetNoCalibrationCode());
335  PART->SetIsUncalibrated();
336  break;
337  }
338  det->SetEResAfterDetector(Einc);
339  edet = det->GetCorrectedEnergy(PART, dE);
340  //Info("Calib", "calculated edet=%f from dE=%f", edet, dE);
341  // negative parameter for calculated contribution
342  PART->SetParameter(Form("%s.E%s", GetGroup()->GetParentStructure<KVMultiDetArray>()->GetName(), det->GetLabel()), -edet);
343  PART->SetECode(GetGroup()->GetParentStructure<KVMultiDetArray>()->GetCalculatedCalibrationCode()); // calculated
344  }
345  else {
346  //Info("Calib", "Einc=0, dead");
347  // nothing to do here
348  PART->SetECode(GetGroup()->GetParentStructure<KVMultiDetArray>()->GetNoCalibrationCode());
349  PART->SetIsUncalibrated();
350  break;
351  }
352  }
353  else if (det->GetNHits() > 1) {
354  // other particles hit this detector, but their contributions have already been calculated
355  // and subtracted. use remaining energy calculated in detector.
356  //Info("Calib", "det->nhits>1");
357  dE = energy_loss[det->GetName()];
358  det->SetEResAfterDetector(Einc);
359  edet = det->GetCorrectedEnergy(PART, dE);
360  // negative parameter for calculated contribution
361  PART->SetParameter(Form("%s.E%s", GetGroup()->GetParentStructure<KVMultiDetArray>()->GetName(), det->GetLabel()), -edet);
362  PART->SetECode(GetGroup()->GetParentStructure<KVMultiDetArray>()->GetCalculatedCalibrationCode()); // calculated
363  }
364  else {
365  dE = det->GetEnergyLoss();
366  det->SetEResAfterDetector(Einc);
367  edet = det->GetCorrectedEnergy(PART, -1., (Einc > 0));
368  //Info("Calib", "Simple normal dE=%f edet=%f", dE, edet);
369  PART->SetParameter(Form("%s.E%s", GetGroup()->GetParentStructure<KVMultiDetArray>()->GetName(), node->GetDetector()->GetLabel()), edet);
370  }
371  Einc += edet;
372  --number_uncalibrated[det->GetName()];
373  energy_loss[det->GetName()] -= dE;
374  det->SetEnergyLoss(energy_loss[det->GetName()]);
375  //Info("Calib", "After: number_uncalibrated=%d, energy_loss=%f", number_uncalibrated[det->GetName()], energy_loss[det->GetName()]);
376  //Info("Calib", "After: number_unidentified=%d", number_unidentified[det->GetName()]);
377  }
378  if (PART->GetECode() > 0) {
379  PART->SetEnergy(Einc);
380  // set particle momentum from telescope dimensions (random)
382  //Info("CalibrateParticle","Calibrated particle with %f MeV",Einc);
383  //add correction for target energy loss - moving charged particles only!
384  Double_t E_targ = 0.;
385  if (PART->GetZ() && PART->GetEnergy() > 0) {
386  E_targ = GetTargetEnergyLossCorrection(PART);
387  //Info("CalibrateParticle","Target energy loss %f MeV",E_targ);
388  PART->SetTargetEnergyLoss(E_targ);
389  }
390  Double_t E_tot = PART->GetEnergy() + E_targ;
391  PART->SetEnergy(E_tot);
392  //Info("CalibrateParticle","Total energy now %f MeV",E_tot);
393 
394  // to avoid inconsistencies in reconstructed data, now that we have 'calibrated' the particle,
395  // we re-check that its identification is consistent with its apparent energy if the latter
396  // is lower than the energy of the simulated particle
397 // if (E_tot < PART->GetParameters()->GetDoubleValue("SIM:ENERGY")) {
398 // //Warning("CalibrateParticle", "Apparent energy lower than real energy. Initial idendtification:");
399 // auto idt = PART->GetIdentifyingTelescope();
400 // auto IDR = PART->GetIdentificationResult(idt);
401 // //std::cout << std::endl; IDR->Print(); std::cout << std::endl;
402 // check_identification_threshold(PART, idt, IDR);
403 // //Warning("CalibrateParticle", "Revised idendtification:");
404 // //std::cout << std::endl; IDR->Print(); std::cout << std::endl;
405 // if (IDR->IDOK) {
406 // // Initial identification is still valid, but fine details may have changed...
407 // PART->SetIdentification(IDR, idt);
408 // // in case particle mass number changes, which changes the total (and kinetic) energy:
409 // // we keep the same energy as calculated from measured losses in detectors,
410 // // to stay coherent with the latter
411 // PART->SetEnergy(E_tot);
412 // }
413 // else {
414 // // initial identification is no longer valid. look for another?
415 // // set first successful identification as particle identification
416 // // as long as the id telescope concerned contains the stopping detector!
417 // const KVSeqCollection* idt_list = PART->GetReconstructionTrajectory()->GetIDTelescopes();
418 // TIter next(idt_list);
419 // Int_t id_no = 1;
420 // Bool_t ok = kFALSE;
421 // KVIdentificationResult* pid = PART->GetIdentificationResult(id_no);
422 // idt = (KVIDTelescope*)next();
423 // while (idt && idt->HasDetector(PART->GetStoppingDetector())) {
424 // if (pid->IDattempted && pid->IDOK) {
425 // ok = kTRUE;
426 // partID = *pid;
427 // identifying_telescope = idt;
428 // break;
429 // }
430 // ++id_no;
431 // pid = PART->GetIdentificationResult(id_no);
432 // idt = (KVIDTelescope*)next();
433 // }
434 // if (ok) { // particle was already identified
435 // PART->SetIdentification(&partID, identifying_telescope);
436 // // in case particle mass number changes, which changes the total (and kinetic) energy:
437 // // we keep the same energy as calculated from measured losses in detectors,
438 // // to stay coherent with the latter
439 // PART->SetEnergy(E_tot);
440 // }
441 // else {
442 // PART->SetIsUnidentified();
443 // PART->SetIDCode(GetGroup()->GetParentStructure<KVMultiDetArray>()->GetBadIDCode());
444 // }
445 // }
446 // }
447 
448  }
449 }
450 
451 
453 
454 
455 
#define e(i)
double Double_t
#define N
float * q
char * Form(const char *fmt,...)
Class for iterating over nuclei in events accessed through base pointer/reference.
virtual const Char_t * GetType() const
Definition: KVBase.h:176
void Error(const char *method, const char *msgfmt,...) const override
Definition: KVBase.cpp:1674
const Char_t * GetLabel() const
Definition: KVBase.h:198
void Warning(const char *method, const char *msgfmt,...) const override
Definition: KVBase.cpp:1661
IDStatus CanIdentify(const TString &tel_name, int Z, int &A, double E) const
Reconstruct particles in group of detectors after filtering simulated events.
bool can_extend_identification_beyond_audit(const std::string &id_label)
KVReconstructedNucleus * current_nuc_recon
temporary, store argument to ReconstructParticle
void IdentifyParticle(KVReconstructedNucleus &PART) override
static std::unordered_map< std::string, bool > audit_id_extensions
void PerformSecondaryAnalysis()
After first round of identification in group, try to identify remaining particles.
std::unique_ptr< KVEvent > fSimEvent
experimental data on identification thresholds & capabilities
std::unordered_map< std::string, double > energy_loss
energy losses in detectors
KVReconNucTrajectory * get_recon_traj_for_particle(const KVGeoDNTrajectory *, const KVGeoDetectorNode *node) override
std::unordered_map< KVReconstructedNucleus *, TList > part_correspond
correspondence between reconstructed and simulated particles
virtual TString get_id_telescope_name_for_audit(KVIDTelescope *idt) const
void ReconstructParticle(KVReconstructedNucleus *part, const KVGeoDNTrajectory *traj, const KVGeoDetectorNode *node) override
void check_identification_threshold(KVNucleus *, KVIDTelescope *idt, KVIdentificationResult *IDR)
std::unordered_map< std::string, int > number_uncalibrated
number of particles for which the energy contribution of detector has not yet been set
const KVDataQualityAudit * fDataQAudit
void identify_particle(KVIDTelescope *idt, KVIdentificationResult *IDR, KVReconstructedNucleus &nuc) override
std::unordered_map< std::string, int > number_unidentified
number of particles hitting detector aas yet unidentified
void CalibrateParticle(KVReconstructedNucleus *PART) override
Path taken by particles through multidetector geometry.
KVGeoDetectorNode * GetNextNode() const
KVString GetPathString() const
void IterateFrom(const KVGeoDetectorNode *node0=nullptr) const
Information on relative positions of detectors & particle trajectories.
const Char_t * GetName() const override
Name of node is same as name of associated detector.
KVGeoStrucElement * GetParentStructure(const Char_t *type, const Char_t *name="") const
virtual void ReconstructParticle(KVReconstructedNucleus *part, const KVGeoDNTrajectory *traj, const KVGeoDetectorNode *node)
KVGroup * GetGroup() const
void Calibrate()
Calculate and set energies of all Z-identified but uncalibrated charged particles in event.
Double_t GetTargetEnergyLossCorrection(KVReconstructedNucleus *ion)
virtual void IdentifyParticle(KVReconstructedNucleus &)
const TCollection * GetTrajectories() const
Definition: KVGroup.h:87
const KVGeoDNTrajectory * GetTrajectoryForReconstruction(const KVGeoDNTrajectory *t, const KVGeoDetectorNode *n) const
Definition: KVGroup.h:117
Base class for all detectors or associations of detectors in array which can identify charged particl...
Definition: KVIDTelescope.h:85
virtual Bool_t CheckTheoreticalIdentificationThreshold(KVNucleus *, Double_t=0.0)
virtual Bool_t CanIdentify(Int_t Z, Int_t)
virtual UShort_t GetIDCode()
virtual void SetIdentificationStatus(KVIdentificationResult *IDR, const KVNucleus *)
Full result of one attempted particle identification.
Bool_t IDattempted
=kTRUE if identification was attempted
Bool_t IDOK
general quality of identification, =kTRUE if acceptable identification made
void SetComment(const Char_t *c)
Bool_t Aident
= kTRUE if A of particle established
Int_t A
A of particle found (if Aident==kTRUE)
Int_t Z
Z of particle found (if Zident==kTRUE)
Int_t IDquality
specific quality code returned by identification procedure
Int_t IDcode
a general identification code for this type of identification
void SetIDType(const Char_t *t)
Bool_t Zident
=kTRUE if Z of particle established
Base class for describing the geometry of a detector array.
Handles lists of named parameters with different types, a list of KVNamedParameter objects.
void Merge(const KVNameValueList &)
Description of properties and kinematics of atomic nuclei.
Definition: KVNucleus.h:123
void Copy(TObject &) const override
Copy this KVNucleus into the KVNucleus object referenced by "obj".
Definition: KVNucleus.cpp:831
Int_t GetA() const
Definition: KVNucleus.cpp:796
Int_t GetZ() const
Return the number of proton / atomic number.
Definition: KVNucleus.cpp:767
KVNameValueList * GetParameters() const
Definition: KVParticle.h:818
Double_t GetEnergy() const
Definition: KVParticle.h:624
void SetParameter(const Char_t *name, ValType value) const
Definition: KVParticle.h:822
void SetEnergy(Double_t e)
Definition: KVParticle.h:602
Path through detector array used to reconstruct detected particle.
Nuclei reconstructed from data measured by a detector array .
virtual Int_t GetECode() const
const KVReconNucTrajectory * GetReconstructionTrajectory() const
KVDetector * GetStoppingDetector() const
void SetDetector(int i, KVDetector *);
virtual void SetTargetEnergyLoss(Double_t e)
virtual void GetAnglesFromReconstructionTrajectory(Option_t *opt="random")
void Print(Option_t *option="") const override
virtual void SetECode(UChar_t s)
Extension of ROOT TString class which allows backwards compatibility with ROOT v3....
Definition: KVString.h:73
void Begin(TString delim) const
Definition: KVString.cpp:565
KVString Next(Bool_t strip_whitespace=kFALSE) const
Definition: KVString.cpp:695
TObject * FindObject(const char *name) const override
const char * GetName() const override
virtual void Info(const char *method, const char *msgfmt,...) const
const char * Data() const
Bool_t IsNull() const
const Int_t n
ClassImp(TPyArg)