KaliVeda  1.12/06
Heavy-Ion Analysis Toolkit
KVFAZIAGroupReconstructor.cpp
Go to the documentation of this file.
2 
3 #include <KVFAZIA.h>
4 #include <KVFAZIADetector.h>
5 #include <KVSignal.h>
6 #include <KVLightEnergyCsIFull.h>
7 #include <KVLightEnergyCsI.h>
8 #include <KVCalibrator.h>
9 #include <KVIDGCsI.h>
10 #include <KVCalibratedSignal.h>
11 
13 
14 
15 
18 void KVFAZIAGroupReconstructor::CalibrateParticle(KVReconstructedNucleus* PART)
19 {
20  // Perform energy calibration of (previously identified) charged particle
21 
22  KVFAZIADetector* det = (KVFAZIADetector*)PART->GetStoppingDetector();
23 
24  PART->SetIsUncalibrated();
25  PART->SetECode(KVFAZIA::ECodes::NO_CALIBRATION_ATTEMPTED);
26 
27  if (det == csi && PART->GetIDCode() == KVFAZIA::IDCodes::ID_GAMMA) { // gammas
28  // "Gamma" particles identified in CsI are given the equivalent energy of proton,
29  // if the CsI detector is calibrated
30  if (det->IsCalibrated()) {
31  double Ep = det->GetDetectorSignalValue("Energy", "Z=1,A=1");
32  if (Ep > 0) {
33  PART->SetEnergy(Ep);
34  SetCalibrationStatus(*PART, KVFAZIA::ECodes::NORMAL_CALIBRATION);
35  PART->SetParameter("FAZIA.ECSI", Ep);
36  }
37  }
38  }
39 
40  if (PART->GetIDCode() == KVFAZIA::IDCodes::ID_STOPPED_IN_FIRST_STAGE) { // stopped in SI1, no PSA identification
41  if (det->IsCalibrated()) {
42  double Ep = det->GetEnergy();
43  PART->SetEnergy(Ep);
44  SetCalibrationStatus(*PART, KVFAZIA::ECodes::NORMAL_CALIBRATION);
45  PART->SetParameter("FAZIA.ESI1", Ep);
46  }
47  }
48 
49  // particle identified in Si1 PSA, detector is calibrated
50  if (PART->GetIDCode() == KVFAZIA::IDCodes::ID_SI1_PSA) {
51  if (si1->IsCalibrated()) {
52  double e1 = si1->GetCorrectedEnergy(PART, -1., kFALSE);
53  if (e1 <= 0) {
54  Warning("CalibrateParticle",
55  "IDCODE=11 Z=%d A=%d calibrated SI1 E=%f",
56  PART->GetZ(), PART->GetA(), e1);
57  return;
58  }
59  PART->SetParameter("FAZIA.ESI1", e1);
60  PART->SetEnergy(e1);
61  SetCalibrationStatus(*PART, KVFAZIA::ECodes::NORMAL_CALIBRATION);
62  }
63  }
64 
65  // particle identified in Si1-Si2
66  if (PART->GetIDCode() == KVFAZIA::IDCodes::ID_SI1_SI2
67  || PART->GetIDCode() == KVFAZIA::IDCodes::ID_SI1_SI2_MAYBE_PUNCH_THROUGH
68  || PART->GetIDCode() == KVFAZIA::IDCodes::ID_SI1_SI2_PUNCH_THROUGH) {
69  if (si1->IsCalibrated() && si2->IsCalibrated()) { // with both detectors calibrated
70  double e1(0), e2(0);
71  bool calc_e1(false), calc_e2(false);
72  if ((e2 = si2->GetEnergy()) > 0) {
73  if (!((e1 = si1->GetEnergy()) > 0)) { // if SI1 not fired, we calculate from SI2 energy
74  e1 = si1->GetDeltaEFromERes(PART->GetZ(), PART->GetA(), e2);
75  calc_e1 = true;
76  }
77  }
78  else if ((e1 = si1->GetEnergy()) > 0) {
79  // if SI2 not fired, we calculate from SI1 energy
80  e2 = si1->GetEResFromDeltaE(PART->GetZ(), PART->GetA());
81  calc_e2 = true;
82  }
83 
84  PART->SetParameter("FAZIA.ESI1", (calc_e1 ? -e1 : e1));
85  PART->SetParameter("FAZIA.ESI2", (calc_e2 ? -e2 : e2));
86  if (e1 > 0 && e2 > 0) {
87  PART->SetEnergy(e1 + e2);
88  SetCalibrationStatus(*PART, (calc_e1 || calc_e2) ? KVFAZIA::ECodes::SOME_ENERGY_LOSSES_CALCULATED : KVFAZIA::ECodes::NORMAL_CALIBRATION);
89  }
90  }
91  }
92 
93  // particle identified in Si2-CsI
94  if (PART->GetIDCode() == KVFAZIA::IDCodes::ID_SI2_CSI) {
95 
96  KVNameValueList part_id(Form("Z=%d,A=%d", PART->GetZ(), PART->GetA()));
97 
98  if (csi->IsCalibrated(part_id) && si1->IsCalibrated() && si2->IsCalibrated()) {
99  // treat case of all detectors calibrated
100  double esi1 = si1->GetEnergy();
101  double esi2 = si2->GetEnergy();
102  double ecsi = csi->GetDetectorSignalValue("Energy", part_id);
103  PART->SetParameter("FAZIA.ESI1", esi1);
104  PART->SetParameter("FAZIA.ESI2", esi2);
105  PART->SetParameter("FAZIA.ECSI", ecsi);
106  PART->SetEnergy(esi1 + esi2 + ecsi);
107  SetCalibrationStatus(*PART, KVFAZIA::ECodes::NORMAL_CALIBRATION); // all energies calibrated
108  }
109  else {
110  // treat case of uncalibrated CsI detector
111  // case where SI1 && SI2 are calibrated & present in event
112  // and if NOTHING ELSE STOPPED IN SI1
113  // and only for Z>2 (because silicon energy losses are too small to give correct
114  // estimation for Z=1 and Z=2):
115  bool si1_pileup = PART->GetParameters()->GetBoolValue("si1_pileup");
116 
117  if (si1->IsCalibrated() && si1->GetEnergy() && si2->IsCalibrated() && si2->GetEnergy()
118  && !si1_pileup && PART->GetZ() > 2) {
119  // calculate total delta-E in (SI1+SI2) then use to calculate CsI energy
120  double deltaE = si1->GetEnergy() + si2->GetEnergy();
121  KVDetector si1si2("Si", si1->GetThickness() + si2->GetThickness());
122  double ecsi = si1si2.GetEResFromDeltaE(PART->GetZ(), PART->GetA(), deltaE);
123  PART->SetParameter("FAZIA.ESI1", si1->GetEnergy());
124  PART->SetParameter("FAZIA.ESI2", si2->GetEnergy());
125  PART->SetParameter("FAZIA.ECSI", -ecsi);
126  PART->SetEnergy(deltaE + ecsi);
127  SetCalibrationStatus(*PART, KVFAZIA::ECodes::SOME_ENERGY_LOSSES_CALCULATED); // CsI energy calculated
128  }
129  }
130  }
131  // particle identified in CsI
132  if (PART->GetIDCode() == KVFAZIA::IDCodes::ID_CSI_PSA) {
133 
134  bool si1_pileup = PART->GetParameters()->GetBoolValue("si1_pileup");
135  bool si2_pileup = PART->GetParameters()->GetBoolValue("si2_pileup");
136 
137  KVNameValueList part_id(Form("Z=%d,A=%d", PART->GetZ(), PART->GetA()));
138 
139  double esi1 = -1;
140  double esi2 = -1;
141  double ecsi = -1;
142  std::unordered_map<std::string, bool> is_calculated;
143  is_calculated["csi"] = is_calculated["si2"] = is_calculated["si1"] = false;
144  if (csi->IsCalibrated(part_id)) ecsi = csi->GetDetectorSignalValue("Energy", part_id);
145  if (!si2_pileup && si2->IsCalibrated()) esi2 = si2->GetEnergy();
146  if (!si1_pileup && si1->IsCalibrated()) esi1 = si1->GetEnergy();
147  // pile-up in Si2 with calibrated CsI: calculate esi1 & esi2
148  if (si2_pileup && csi->IsCalibrated(part_id) && ecsi > 0) {
149  esi2 = si2->GetDeltaEFromERes(PART->GetZ(), PART->GetA(), ecsi);
150  is_calculated["si2"] = true;
151  esi1 = si1->GetDeltaEFromERes(PART->GetZ(), PART->GetA(), esi2 + ecsi);
152  is_calculated["si1"] = true;
153  }
154  // pile-up in Si1 with calibrated CsI & calibrated Si2: calculate esi1
155  else if (si1_pileup && csi->IsCalibrated(part_id) && si2->IsCalibrated() && ecsi > 0 && esi2 > 0) {
156  esi1 = si1->GetDeltaEFromERes(PART->GetZ(), PART->GetA(), esi2 + ecsi);
157  is_calculated["si1"] = true;
158  }
159  // treat case of uncalibrated CsI detector
160  // case where SI1 && SI2 are calibrated & present in event & no pileup detected in Si1/Si2
161  // and only for Z>2 (because silicon energy losses are too small to give correct
162  // estimation for Z=1 and Z=2):
163  if (!csi->IsCalibrated(part_id) &&
164  (si1->IsCalibrated() && si1->GetEnergy() && si2->IsCalibrated() && si2->GetEnergy())
165  && !si1_pileup && !si2_pileup && PART->GetZ() > 2) {
166  // calculate total delta-E in (SI1+SI2) then use to calculate CsI energy
167  double deltaE = esi1 + esi2;
168  KVDetector si1si2("Si", si1->GetThickness() + si2->GetThickness());
169  ecsi = si1si2.GetEResFromDeltaE(PART->GetZ(), PART->GetA(), deltaE);
170  is_calculated["csi"] = true;
171  }
172 
173  if (ecsi > 0 && esi1 > 0 && esi2 > 0) {
174  if (is_calculated["si1"] || is_calculated["si2"] || is_calculated["csi"])
175  SetCalibrationStatus(*PART, KVFAZIA::ECodes::SOME_ENERGY_LOSSES_CALCULATED);
176  else
177  SetCalibrationStatus(*PART, KVFAZIA::ECodes::NORMAL_CALIBRATION);
178  PART->SetEnergy(esi1 + esi2 + ecsi);
179  PART->SetParameter("FAZIA.ESI1", is_calculated["si1"] ? -esi1 : esi1);
180  PART->SetParameter("FAZIA.ESI2", is_calculated["si2"] ? -esi2 : esi2);
181  PART->SetParameter("FAZIA.ECSI", is_calculated["csi"] ? -ecsi : ecsi);
182  }
183  }
184 
185  if (PART->IsCalibrated()) {
186 
187  //add correction for target energy loss - moving charged particles only!
188  Double_t E_targ = 0.;
189  if (PART->GetZ() && PART->GetEnergy() > 0) {
190  E_targ = GetTargetEnergyLossCorrection(PART);
191  PART->SetTargetEnergyLoss(E_targ);
192  }
193  Double_t E_tot = PART->GetEnergy() + E_targ;
194  PART->SetEnergy(E_tot);
195 
196  // set particle momentum from telescope dimensions (random)
197  PART->GetAnglesFromReconstructionTrajectory();
198 
199  // check for energy loss coherency
200  KVNucleus avatar;
201  avatar.SetZAandE(PART->GetZ(), PART->GetA(), PART->GetKE());
202 
203  int ndet = 0;
204  KVGeoDetectorNode* node = 0;
205  // iterating over detectors starting from the target
206  // compute the theoretical energy loss of the avatar
207  // compare to the calibrated/calculated energy
208  // remove this energy from the avatar energy
209  PART->GetReconstructionTrajectory()->IterateBackFrom();
210  while ((node = PART->GetReconstructionTrajectory()->GetNextNode())) {
211  det = (KVFAZIADetector*)node->GetDetector();
212  Double_t temp = det->GetELostByParticle(&avatar);
213  PART->SetParameter(Form("FAZIA.avatar.E%s", det->GetLabel()), temp);
214  avatar.SetKE(avatar.GetKE() - temp);
215  ndet++;
216  }
217  }
218 }
219 
220 
221 
229 
231 {
232  // Calibration routine for particles added in AddCoherencyParticles() method.
233  //
234  // Only particles identified in Si1-Si2 or Si1-PSA are treated.
235  //
236  // We take into account, where possible, the calculated energy losses in SI1 and SI2
237  // of the "parent" nucleus (i.e. the one which stopped in the CSI).
238 
239  PART->SetIsUncalibrated();
240  PART->SetECode(KVFAZIA::ECodes::NO_CALIBRATION_ATTEMPTED);
241 
242  // particle identified in Si1 PSA, detector is calibrated
243  if (PART->GetIDCode() == KVFAZIA::IDCodes::ID_SI1_PSA) {
244  if (si1->IsCalibrated()) {
245  double e1 = si1->GetCalibratedEnergy();
246  if (e1 <= 0) {
247  Warning("CalibrateParticle",
248  "IDCODE=11 Z=%d A=%d calibrated SI1 E=%f",
249  PART->GetZ(), PART->GetA(), e1);
250  return;
251  }
252  PART->SetParameter("FAZIA.ESI1", -e1); // energy loss is calculated in this case
253  PART->SetEnergy(e1);
254  SetCalibrationStatus(*PART, KVFAZIA::ECodes::SOME_ENERGY_LOSSES_CALCULATED); // energy loss is calculated in this case
255  }
256  }
257 
258  // particle identified in Si1-Si2
259  if (PART->GetIDCode() == KVFAZIA::IDCodes::ID_SI1_SI2) {
260  if (si1->IsCalibrated() && si2->IsCalibrated()) { // with both detectors calibrated
261  double e1(0), e2(0);
262  if ((e2 = si2->GetCalibratedEnergy()) > 0) {
263  if (!((e1 = si1->GetCalibratedEnergy()) > 0)) { // if SI1 not fired, we calculate from SI2 energy
264  e1 = si1->GetDeltaEFromERes(PART->GetZ(), PART->GetA(), e2);
265  }
266  }
267  else if ((e1 = si1->GetCalibratedEnergy()) > 0) {
268  // if SI2 not fired, we calculate from SI1 energy
269  e2 = si1->GetEResFromDeltaE(PART->GetZ(), PART->GetA(), e1);
270  }
271 
272  PART->SetParameter("FAZIA.ESI1", -e1); // always calculated
273  PART->SetParameter("FAZIA.ESI2", -e2); // always calculated
274  if (e1 > 0 && e2 > 0) {
275  PART->SetEnergy(e1 + e2);
276  SetCalibrationStatus(*PART, KVFAZIA::ECodes::SOME_ENERGY_LOSSES_CALCULATED); // always calculated
277  }
278  }
279  }
280 
281  if (PART->IsCalibrated()) {
282 
283  //add correction for target energy loss - moving charged particles only!
284  Double_t E_targ = 0.;
285  if (PART->GetZ() && PART->GetEnergy() > 0) {
286  E_targ = GetTargetEnergyLossCorrection(PART);
287  PART->SetTargetEnergyLoss(E_targ);
288  }
289  Double_t E_tot = PART->GetEnergy() + E_targ;
290  PART->SetEnergy(E_tot);
291 
292  // set particle momentum from telescope dimensions (random)
294 
295  // check for energy loss coherency
296  KVNucleus avatar;
297  avatar.SetZAandE(PART->GetZ(), PART->GetA(), PART->GetKE());
298 
299  int ndet = 0;
300  KVGeoDetectorNode* node = 0;
301  // iterating over detectors starting from the target
302  // compute the theoretical energy loss of the avatar
303  // compare to the calibrated/calculated energy
304  // remove this energy from the avatar energy
306  while ((node = PART->GetReconstructionTrajectory()->GetNextNode())) {
307  auto det = (KVFAZIADetector*)node->GetDetector();
308  Double_t temp = det->GetELostByParticle(&avatar);
309  PART->SetParameter(Form("FAZIA.avatar.E%s", det->GetLabel()), temp);
310  avatar.SetKE(avatar.GetKE() - temp);
311  ndet++;
312  }
313  }
314 }
315 
316 
317 
321 
323 {
324  // Copy FPGA energy values to reconstructed particle parameter lists
325  // Set values in detectors for identification/calibration procedures
326 
327  for (KVEvent::Iterator it = GetEventFragment()->begin(); it != GetEventFragment()->end(); ++it) {
328  KVReconstructedNucleus* rnuc = it.get_pointer<KVReconstructedNucleus>();
329 
331 
332  KVGeoDetectorNode* node;
333  while ((node = rnuc->GetReconstructionTrajectory()->GetNextNode())) {
334 
336 
337  // now copy all detector signals to reconstructed particle parameter list...
338  // they are stored with format "[detname].[signal_name]" except for
339  // DetTag and GTTag which are the same for all detectors of the same telescope
340  // and so are only stored once with name "DetTag" or "GTTag".
341  TIter it(&det->GetListOfDetectorSignals());
342  KVDetectorSignal* ds;
343  while ((ds = (KVDetectorSignal*)it())) {
344  if (ds->IsRaw() && !ds->IsExpression())
345  // only store raw data, excluding any expressions based only on raw data
346  {
347  TString pname;
348  // Only store non-zero parameters
349  if (ds->GetValue() != 0) {
350  if (TString(ds->GetName()) != "DetTag" && TString(ds->GetName()) != "GTTag")
351  pname = Form("%s.%s", det->GetName(), ds->GetName());
352  else
353  pname = ds->GetName();
354  rnuc->GetParameters()->SetValue(pname, ds->GetValue());
355  }
356  }
357  }
358  }
359  }
360 }
361 
362 
363 
372 
374 {
375  // Coherency codes (CCode):
376  // -1 no modification du to coherency checks
377  // 1 CsI id = gamma + good in Si-Si -> Si-Si
378  // 2 CsI id -> Si-CsI id
379  // 3 Si-CsI id -> Si-Si id because Z(sicsi)<Z(sisi)
380  // 4 stopped in CsI (no id) + good id Si-Si -> Si-Si
381  // 5 stopped in SI2 (no id) + good id Si1PSA -> Si1PSA
382 
384 
385  // check for unvetoed punch-through particles in Si1-Si2 identification
386  auto si1si2 = id_by_type.find("Si-Si");
387  if (si1si2 != id_by_type.end()) HandleSI1SI2PunchThrough(si1si2->second, PART);
388 
389  bool si1_pileup(false), si2_pileup(false);
390 
391  // check for failed PSA identification for particles stopped in Si1
392  // change status => KVReconstructedNucleus::kStatusStopFirstStage
393  // estimation of minimum Z from energy loss (if Si1 calibrated)
394  if (PART.GetStoppingDetector() == si1 && !PART.IsIdentified()) {
397  return;
398  }
399 
400  // Coherency checks for identifications
401  if (PART.IsIdentified()) {
402  /********** PARTICLES initially stopped/identified in the CSI *****************/
403  if (partID.IsType("CsI")) {
404  bool coherency_particle_added_in_si1si2 = false;
405  if (partID.IDcode == KVFAZIA::IDCodes::ID_GAMMA) { // gammas
406  // look at Si1-Si2 identification.
407  // if a correct identification was obtained, we ignore the gamma in CsI and change to Si1-Si2
408  if (si1si2 != id_by_type.end()) {
409  if (si1si2->second->IDOK) {
410  // Info("IdentifyParticle","Got GAMMA in CsI, changing to Si1Si2 identification [Z=%d A=%d]",
411  // si1si2->second->Z, si1si2->second->A);
413  partID = *(si1si2->second);
414  PART.SetIsIdentified();
416  PART.GetParameters()->SetValue("CCode", 1);
417  }
418  }
419  }
420  else if (partID.IDOK) {
421  // good identification in CsI (light charged particle)
422  // check for pile-up in Si2 (and beyond) - in this case, measured energy loss in Si2+Si1
423  // should not be attributed to this particle
424  auto si2csi = id_by_type.find("Si-CsI");
425  if (si2csi != id_by_type.end()) {
426  // detect a pile-up in Si2 in coincidence with particule detected in CsI
427  // the following covers the following cases:
428  // - good ID Si-CsI (IDquality < kICODE4) with Z>Zcsi
429  // - ID "between the lines" in Si-CsI (IDquality=kICODE4,kICODE5) with Z>Zcsi
430  // - point above last Z line in Si-CsI grid (kICODE7) with Z>Zcsi
431  // In these cases, there is a second particle stopped in Si2: and
432  // hence the measured Si1 energy loss is not to be used either
433  if ((si2csi->second->Z > partID.Z)) {
434  si2_pileup = true;
435  si1_pileup = true;
436  }
437  }
438  if (si1si2 != id_by_type.end()) {
439  // detect a pile-up in Si1/2 in coincidence with particule detected in CsI
440  // the following covers the following cases:
441  // - good ID Si1-Si2 (IDquality < kICODE4) with Z>Zcsi
442  // - ID "between the lines" in Si1-Si2 (IDquality=kICODE4,kICODE5) with Z>Zcsi
443  // - point above last Z line in Si1-Si2 grid (kICODE7) with Z>Zcsi
444  // In these cases, there is a second particle stopped in Si2: and
445  // hence the measured Si1 energy loss is not to be used either
446  if (si1si2->second->Z > partID.Z) {
447  si2_pileup = true;
448  si1_pileup = true;
449  if (si1si2->second->IDOK) {
450  // if in addition the si1si2 identification is OK, we need to add a new particle
451  // to the event corresponding to this fragment stopped in SI2 in coincidence with
452  // the LCP in the CsI
453  coherency_particles.push_back({
454  &PART, si2->GetNode(), theTrajectory,
456  (Int_t)si1si2->second->GetNumber(), PART.GetNumberOfIdentificationResults()
457  }
458  );
459  coherency_particle_added_in_si1si2 = true;
460  }
461  }
462  }
463  auto sipsa = id_by_type.find("SiPSA");
464  if (sipsa != id_by_type.end()) {
465  // detect a pile-up in Si1 in coincidence with particule detected in CsI
466  if ((sipsa->second->Z > partID.Z)) {
467  si1_pileup = true;
468  if (sipsa->second->IDOK && !coherency_particle_added_in_si1si2) {
469  // if in addition the si1psa identification is OK, and if we have not already added
470  // a new particle stopping in SI2 after looking at Si1-Si2 identification,
471  // we need to add a new particle to the event corresponding to this fragment stopped in SI1
472  // in coincidence with the LCP in the CsI
473  coherency_particles.push_back({
474  &PART, si1->GetNode(), theTrajectory,
476  (Int_t)sipsa->second->GetNumber(), PART.GetNumberOfIdentificationResults()
477  }
478  );
479  }
480  }
481  }
482 
483  }
484  }
485  /********** PARTICLES initially identified in SI2-CSI *****************/
486  else if (partID.IsType("Si-CsI")) {
487  // ions correctly identified in Si2-CsI should have coherent identification in Si1-Si2: as the particle punches
488  // through Si2 the Si1-Si2 identification should underestimate the A and/or Z of the ion, i.e. either the A
489  // or the Z from Si1-Si2 identification should be smaller than from Si2-CsI identification.
490  //
491  // the following is to treat the case (quite typical for fazia) where a particle stops in Si2 (id in Si1Si2)
492  // but a small noise in the CsI makes it look like a particle stopped in CsI and identified in Si2-CsI
493  int zz = partID.Z;
494  if (si1si2 != id_by_type.end()) {
495  KVIdentificationResult* idr_si1si2 = si1si2->second;
496  if (idr_si1si2->IDOK && idr_si1si2->IDcode == KVFAZIA::IDCodes::ID_SI1_SI2) { // only change if there is no suspicion of punch-through in si1-si2
497  if (zz < idr_si1si2->Z) {
498  // Info("IdentifyParticle","SiCsI identification [Z=%d A=%d] changed to SiSi identification [Z=%d A=%d]",
499  // PART.GetZ(),PART.GetA(),si1si2->second->Z,si1si2->second->A);
501  partID = *(si1si2->second);
503  PART.GetParameters()->SetValue("CCode", 3);
504  }
505  }
506  }
507  }
508  }
509  else {
510  // particle not identified, apparently stopped in CsI, with Si1-Si2 identification?
511  if (PART.GetStoppingDetector()->IsLabelled("CSI")) {
512  if (si1si2 != id_by_type.end()) {
513  if (si1si2->second->IDOK) {
514  // stopping detector becomes Si2, identification Si1-Si2 accepted
515  // Info("IdentifyParticle","Unidentified, stopped in CsI, good Si1Si2 identification [Z=%d A=%d]",
516  // si1si2->second->Z,si1si2->second->A);
518  partID = *(si1si2->second);
519  PART.SetIsIdentified();
521  PART.GetParameters()->SetValue("CCode", 4);
522  }
523  }
524  }
525  // particle not identified, apparently stopped in SI2, with Si1-PSA identification?
526  if (PART.GetStoppingDetector()->IsLabelled("SI2")) {
527  auto sipsa = id_by_type.find("SiPSA");
528  if (sipsa != id_by_type.end()) {
529  if (sipsa->second->IDOK) {
530  // stopping detector becomes Si1
532  partID = *(sipsa->second);
533  PART.SetIsIdentified();
534  auto sipsa_idtel = (KVIDTelescope*)PART.GetReconstructionTrajectory()->GetIDTelescopes()->Last();
535  PART.SetIdentification(&partID, sipsa_idtel);
536  PART.GetParameters()->SetValue("CCode", 5);
537  }
538  }
539  }
540  }
541  PART.SetParameter("si1_pileup", si1_pileup);
542  PART.SetParameter("si2_pileup", si2_pileup);
543 }
544 
545 
546 
554 
556 {
557  // Change the reconstruction trajectory & the stopping detector for the particle.
558  //
559  // The stopping detector is moved 1 closer to the target, i.e.
560  //
561  // - initial stopping detector=CSI with trajectory CSI/SI2/SI1: final stopping=SI2, trajectory=SI2/SI1
562  // - initial stopping detector=SI2 with trajectory SI2/SI1: final stopping=SI1, trajectory=SI1
563 
569  PART.ModifyReconstructionTrajectory(newtraj);
570 }
571 
572 
573 
593 
595 {
596  // SI1-SI2 identification may suffer from unvetoed punch-through particles.
597  //
598  // In this case informational cuts/contours have been defined to indicate when a particle
599  // is identified in a region which may be polluted by punch-through.
600  //
601  // In this case, 2 cases are treated:
602  //
603  // a) for well-identified particles (IDquality<4), there is an ambiguity to their identification:
604  // it may well be truly a particle with the deduced Z & A which stopped in SI2, or it may
605  // in fact be a particle with larger Z &/or A which punched-through.
606  //
607  // For these particles, we change the general IDCode to FAZIAIDCodes::ID_SI1_SI2_MAYBE_PUNCH_THROUGH
608  //
609  // b) particles which are not well-identified because between the identification lines, too far to be
610  // considered identified, idquality=4,5, are in this case most likely to be punching through particles
611  // with a Z at least equal to 1 more than that given by the identification routine (Zmin).
612  //
613  // For these particles, we change the general IDCode to FAZIAIDCodes::ID_SI1_SI2_PUNCH_THROUGH
614 
615  if (idr->IdentifyingGridHasFlagWhichBegins("punch_through")) {
616  KVString pt_flag = idr->IdentifyingGridGetFlagWhichBegins("punch_through");
617  bool treat_punch_through = true;
618  if (pt_flag.GetNValues("|") == 2) {
619  // Z range specified as : "punch_through|Z=1-13"
620  pt_flag.Begin("|=");
621  pt_flag.Next();
622  pt_flag.Next();
623  assert(!pt_flag.End());// this should never happen
624  KVNumberList zrange(pt_flag.Next());
625  // only particles with Z in given range are treated for punch-through
626  treat_punch_through = zrange.Contains(idr->Z);
627  }
628  if (treat_punch_through) {
629  bool pt_treated = false;
630  if (idr->IDquality < KVIDZAGrid::kICODE4) {
631  // change general IDcode
632  idr->IDcode = KVFAZIA::IDCodes::ID_SI1_SI2_MAYBE_PUNCH_THROUGH;
633  idr->SetComment("Apparently well-identified particle, but could be punching through to CsI (in which case Z is a minimum)");
634  pt_treated = true;
635  }
636  else if (idr->IDquality < KVIDZAGrid::kICODE6) {
637  // change general IDcode & identification status
638  idr->IDcode = KVFAZIA::IDCodes::ID_SI1_SI2_PUNCH_THROUGH;
639  idr->SetComment("Particle punching through SI2, identified Z is only a minimum estimation");
640  idr->IDOK = kTRUE; // this will previously have been false
641  idr->Zident = kFALSE;
642  pt_treated = true;
643  }
644  if (pt_treated && (PART.GetStoppingDetector() == si2)) {
645  // For any particles stopped (at least apparently) in SI2, we may (potentially) change the identification of particle,
646  // or at least its IDCode
647  PART.SetIsIdentified();
649  }
650  }
651  }
652 }
653 
654 
655 
657 
659 {
661  csi = (KVFAZIADetector*)g->GetDetectorByType("CsI");
662  assert(csi != nullptr);
663  // set unique trajectory for group
668  assert(si1 != nullptr);
669  assert(si2 != nullptr);
671  assert(fSi1Si2IDTelescope != nullptr);
672 }
673 
674 
675 
683 
685 {
686  // Called to add any nuclei not included in the initial reconstruction, but "revealed"
687  // by consistency checks between identifications and calibrations of other nuclei
688  //
689  // These particles have a (string) parameter "COHERENCY"
690 
691  //std::cout << "===========================================================================================\n" << std::endl;
692  //Info("AddCoherencyParticles", "There are %d particles to add to this event\n", (int)coherency_particles.size());
693  for (auto& part : coherency_particles) {
694 
695  auto rnuc = GetEventFragment()->AddParticle();
696 
697 // Info("AddCoherencyParticle", "Adding particle in pile-up with Z=%d A=%d E=%f in %s",
698 // part.original_particle->GetZ(), part.original_particle->GetA(), part.original_particle->GetE(), part.original_particle->GetStoppingDetector()->GetName());
699 
700  auto ESI1_parent = part.original_particle->GetParameters()->HasDoubleParameter("FAZIA.ESI1") ?
701  TMath::Abs(part.original_particle->GetParameters()->GetDoubleValue("FAZIA.ESI1"))
702  : 0;
703  auto ESI2_parent = part.original_particle->GetParameters()->HasDoubleParameter("FAZIA.ESI2") ?
704  TMath::Abs(part.original_particle->GetParameters()->GetDoubleValue("FAZIA.ESI2"))
705  : 0;
706 // std::cout << "ESI1 = " << ESI1_parent << " [" << si1->GetDetectorSignalValue("Energy") <<
707 // "] ESI2 = " << ESI2_parent << " [" << si2->GetDetectorSignalValue("Energy") << "]" << std::endl;
708 
709  // reconstruction
710  auto Rtraj = (const KVReconNucTrajectory*)GetGroup()->GetTrajectoryForReconstruction(part.stopping_trajectory, part.stopping_detector_node);
711 
712 // std::cout << "SI1: " << (si1->IsCalibrated() ? "CALIB." : "NOT CALIB.")
713 // << " SI2: " << (si2->IsCalibrated() ? "CALIB." : "NOT CALIB.")
714 // << " CSI: " << (csi->IsCalibrated(
715 // Form("Z=%d,A=%d", part.original_particle->GetZ(), part.original_particle->GetA())
716 // ) ? "CALIB." : "NOT CALIB.") << std::endl;
717 
718  rnuc->SetReconstructionTrajectory(Rtraj);
719  rnuc->SetParameter("ARRAY", GetGroup()->GetArray()->GetName());
720  rnuc->SetParameter("COHERENCY",
721  "Particle added to event after consistency checks between identifications and calibrations of other nuclei");
722  // identification
723  Int_t idnumber = 1;
724  for (int i = part.first_id_result_to_copy; i <= part.max_id_result_index; ++i) {
725  auto IDR = rnuc->GetIdentificationResult(idnumber++);
726  // copy existing identification results from "parent" particle
727  part.original_particle->GetIdentificationResult(i)->Copy(*IDR);
728  }
729  rnuc->SetIsIdentified();
730  rnuc->SetIdentification(rnuc->GetIdentificationResult(1), part.identifying_telescope);
731 // Info("AddCoherencyParticle", "Initial ident of particle to add: Z=%d A=%d identified in %s",
732 // rnuc->GetZ(), rnuc->GetA(), rnuc->GetIdentifyingTelescope()->GetType());
733 // rnuc->GetIdentificationResult(1)->Print();
734  // with both silicons calibrated, we can try to subtract the contributions of the parent
735  // particle (using inverse calibrations and calculated energy losses of parent),
736  // then try a new identification (only for Si1-Si2: we don't know how to recalculate the PSA parameters for SI1)
737  // as E789 Si1-Si2 identifications are implemented with 2 grids, first a low range
738  // QL1.Amplitude vs. Q2.FPGAEnergy grid (upto Z=8), then full range QH1.FPGAEnergy vs.
739  // Q2.FPGAEnergy, and calibrations for SI1 & SI2 use QH1.FPGAEnergy and Q2.FPGAEnergy
740  // respectively, we set QL1.Amplitude=0 in order to force the use of the recalculated
741  // QH1.FPGAEnergy and Q2.FPGAEnergy in the full range grid.
742  // *actually, SI1 may have also an "Energy-QL1" calibration from QL1.Amplitude,
743  // in which case we can modify this as well
744  if (si1->IsCalibrated() && si2->IsCalibrated()) {
745  // calculate new values of raw parameters
746  double new_q2{0}, new_qh1{0}, new_ql1{0};
747  auto ESI1_qh1 = si1->GetDetectorSignalValue("Energy");
748  auto ESI1_ql1 = si1->GetDetectorSignalValue("Energy-QL1");
749  auto ESI2 = si2->GetEnergy();
750  new_qh1 = si1->GetInverseDetectorSignalValue("Energy", TMath::Max(0., ESI1_qh1 - ESI1_parent), "QH1.FPGAEnergy");
751  if (si1->HasDetectorSignalValue("Energy-QL1")) {
752  new_ql1 = si1->GetInverseDetectorSignalValue("Energy-QL1", TMath::Max(0., ESI1_ql1 - ESI1_parent), "QL1.Amplitude");
753  }
754 // Info("AddCoherencyParticle", "Changing raw data parameters:");
755 // std::cout << " QL1: " << si1->GetDetectorSignalValue("QL1.Amplitude") << " ==> " << new_ql1 << std::endl;
756 // std::cout << " QH1: " << si1->GetDetectorSignalValue("QH1.FPGAEnergy") << " ==> " << new_qh1 << std::endl;
757  si1->SetDetectorSignalValue("QH1.FPGAEnergy", new_qh1);
758  si1->SetDetectorSignalValue("QL1.Amplitude", new_ql1);
759 
760  if (rnuc->GetIDCode() == KVFAZIA::IDCodes::ID_SI1_SI2) {
761  new_q2 = si2->GetInverseDetectorSignalValue("Energy", TMath::Max(0., ESI2 - ESI2_parent), "Q2.FPGAEnergy");
762 // std::cout << " Q2: " << si2->GetDetectorSignalValue("Q2.FPGAEnergy") << " ==> " << new_q2 << std::endl;
763  si2->SetDetectorSignalValue("Q2.FPGAEnergy", new_q2);
764 
765  // now retry the identification
767  IDR.SetNumber(1);
768  part.identifying_telescope->Identify(&IDR);
769 // rnuc->GetIdentificationResult(1)->Print();
770  if (IDR.IDOK) {
771 // Info("AddCoherencyParticle", "Achieved new identification for particle:");
772  *(rnuc->GetIdentificationResult(1)) = IDR;
773  rnuc->SetIdentification(rnuc->GetIdentificationResult(1), part.identifying_telescope);
774  // check we are not in punch-through region
775  HandleSI1SI2PunchThrough(rnuc->GetIdentificationResult(1), *rnuc);
776  }
777  else {
778 // Info("AddCoherencyParticle", "Identification has failed for particle");
779  if (new_q2 < 1) {
780 // Info("AddCoherencyParticle", "Try SI1-PSA identification?");
781 // part.original_particle->GetIdentificationResult(5)->Print();
782  // subtraction of original particle leaves nothing in SI2: try SI1-PSA ?
783  if (part.original_particle->GetIdentificationResult(5)->IDOK) {
784  // modify reconstruction trajectory: now starts on SI1 not SI2
785  Rtraj = (const KVReconNucTrajectory*)GetGroup()->GetTrajectoryForReconstruction(part.stopping_trajectory, si1->GetNode());
786  rnuc->ModifyReconstructionTrajectory(Rtraj);
787  part.original_particle->GetIdentificationResult(5)->Copy(*rnuc->GetIdentificationResult(1));
788  auto idt = (KVIDTelescope*)Rtraj->GetIDTelescopes()->First();
789  rnuc->SetIdentification(rnuc->GetIdentificationResult(1), idt);
790  }
791  else {
792  // leave original estimation of identification Si1-Si2
793  // but check we are not in punch-through region
794  HandleSI1SI2PunchThrough(rnuc->GetIdentificationResult(1), *rnuc);
795  }
796  }
797  else {
798  // leave original estimation of identification Si1-Si2
799  // but check we are not in punch-through region
800  HandleSI1SI2PunchThrough(rnuc->GetIdentificationResult(1), *rnuc);
801  }
802  }
803  }
804  }
805  // calibration
806  if (rnuc->IsIdentified()) {
808 // Info("AddCoherencyParticle", "Added particle with Z=%d A=%d E=%f identified in %s",
809 // rnuc->GetZ(), rnuc->GetA(), rnuc->GetE(), rnuc->GetIdentifyingTelescope()->GetType());
810 // std::cout << "ESI1 = " << rnuc->GetParameters()->GetDoubleValue("FAZIA.ESI1")
811 // << " [" << rnuc->GetParameters()->GetDoubleValue("FAZIA.avatar.ESI1") << "]"
812 // << " ESI2 = " << rnuc->GetParameters()->GetDoubleValue("FAZIA.ESI2")
813 // << " [" << rnuc->GetParameters()->GetDoubleValue("FAZIA.avatar.ESI2") << "]"
814 // << std::endl << std::endl;
815  }
816  }
817 // std::cout << "===========================================================================================\n\n" << std::endl;
818 }
819 
820 
int Int_t
ClassImp(KVPartitionList) void KVPartitionList
Initialisation.
const Bool_t kFALSE
double Double_t
const Bool_t kTRUE
char * Form(const char *fmt,...)
virtual Bool_t IsType(const Char_t *typ) const
Definition: KVBase.h:178
const Char_t * GetLabel() const
Definition: KVBase.h:192
virtual void SetNumber(UInt_t num)
Definition: KVBase.h:209
Bool_t IsLabelled(const Char_t *l) const
Definition: KVBase.h:200
UInt_t GetNumber() const
Definition: KVBase.h:213
Output signal data produced by a detector.
virtual Double_t GetValue(const KVNameValueList &="") const
virtual Bool_t IsExpression() const
virtual Bool_t IsRaw() const
Base class for detector geometry description, interface to energy-loss calculations.
Definition: KVDetector.h:121
virtual Double_t GetELostByParticle(KVNucleus *, TVector3 *norm=0)
Definition: KVDetector.cpp:305
virtual Double_t GetEnergy() const
Definition: KVDetector.h:308
Double_t GetDetectorSignalValue(const TString &type, const KVNameValueList &params="") const
Definition: KVDetector.h:426
void SetDetectorSignalValue(const TString &type, Double_t val) const
Definition: KVDetector.h:440
Bool_t IsCalibrated() const
Definition: KVDetector.h:359
virtual Double_t GetCalibratedEnergy() const
Definition: KVDetector.h:303
Double_t GetInverseDetectorSignalValue(const TString &output, Double_t value, const TString &input, const KVNameValueList &params="") const
Definition: KVDetector.h:448
const KVSeqCollection & GetListOfDetectorSignals() const
Definition: KVDetector.h:732
KVGeoDetectorNode * GetNode()
Definition: KVDetector.h:285
Bool_t HasDetectorSignalValue(const TString &type) const
Definition: KVDetector.h:470
virtual Double_t GetDeltaEFromERes(Int_t Z, Int_t A, Double_t Eres)
Iterator end() const
Definition: KVEvent.h:428
Base class for FAZIA detectors.
Reconstruction of particles detected in FAZIA telescopes.
KVGeoDNTrajectory * theTrajectory
1 FAZIA group = 1 telescope with 1 unique trajectory SI1 - SI2 - CSI
KVIDTelescope * fSi1Si2IDTelescope
SI1-SI2 ID Telescope.
void CalibrateCoherencyParticle(KVReconstructedNucleus *PART)
void ChangeReconstructedTrajectory(KVReconstructedNucleus &PART)
void IdentifyParticle(KVReconstructedNucleus &PART)
void HandleSI1SI2PunchThrough(KVIdentificationResult *, KVReconstructedNucleus &)
Path taken by particles through multidetector geometry.
KVGeoDetectorNode * GetNextNode() const
void IterateFrom(const KVGeoDetectorNode *node0=nullptr) const
const KVSeqCollection * GetIDTelescopes() const
void IterateBackFrom(const KVGeoDetectorNode *node0=nullptr) const
void Copy(TObject &obj) const
Make a copy of this object.
Information on relative positions of detectors & particle trajectories.
KVSeqCollection * GetTrajectories() const
KVSeqCollection * GetForwardTrajectories() const
KVDetector * GetDetector() const
KVGroup * GetGroup() const
std::unordered_map< std::string, KVIdentificationResult * > id_by_type
identification results by type for current particle
KVReconstructedEvent * GetEventFragment() const
virtual void IdentifyParticle(KVReconstructedNucleus &PART)
Double_t GetTargetEnergyLossCorrection(KVReconstructedNucleus *ion)
void SetCalibrationStatus(KVReconstructedNucleus &PART, UShort_t code)
KVIdentificationResult partID
identification to be applied to current particle
void TreatStatusStopFirstStage(KVReconstructedNucleus &)
virtual void SetGroup(KVGroup *g)
std::vector< particle_to_add_from_coherency_analysis > coherency_particles
Group of detectors which can be treated independently of all others in array.
Definition: KVGroup.h:19
KVGeoStrucElement * GetArray() const
Definition: KVGroup.h:44
const KVGeoDNTrajectory * GetTrajectoryForReconstruction(const KVGeoDNTrajectory *t, const KVGeoDetectorNode *n) const
Definition: KVGroup.h:103
Base class for all detectors or associations of detectors in array which can identify charged particl...
Definition: KVIDTelescope.h:88
Full result of one attempted particle identification.
Bool_t IDOK
general quality of identification, =kTRUE if acceptable identification made
void SetComment(const Char_t *c)
Bool_t IdentifyingGridHasFlagWhichBegins(TString flag_beginning)
TString IdentifyingGridGetFlagWhichBegins(TString flag_beginning)
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
Bool_t Zident
=kTRUE if Z of particle established
virtual Double_t GetEResFromDeltaE(Int_t Z, Int_t A, Double_t dE=-1.0, enum SolType type=kEmax)
Definition: KVMaterial.cpp:750
Handles lists of named parameters with different types, a list of KVNamedParameter objects.
void SetValue(const Char_t *name, value_type value)
Bool_t HasDoubleParameter(const Char_t *name) const
Description of properties and kinematics of atomic nuclei.
Definition: KVNucleus.h:125
Int_t GetA() const
Definition: KVNucleus.cpp:799
void SetZAandE(Int_t z, Int_t a, Double_t ekin)
Set atomic number, mass number, and kinetic energy in MeV.
Definition: KVNucleus.cpp:733
Int_t GetZ() const
Return the number of proton / atomic number.
Definition: KVNucleus.cpp:770
Strings used to represent a set of ranges of values.
Definition: KVNumberList.h:83
Bool_t Contains(Int_t val) const
returns kTRUE if the value 'val' is contained in the ranges defined by the number list
KVNameValueList * GetParameters() const
Definition: KVParticle.h:735
Double_t GetEnergy() const
Definition: KVParticle.h:582
void SetKE(Double_t ecin)
Definition: KVParticle.cpp:230
void SetParameter(const Char_t *name, ValType value) const
Definition: KVParticle.h:739
Double_t GetKE() const
Definition: KVParticle.h:575
void SetEnergy(Double_t e)
Definition: KVParticle.h:560
Path through detector array used to reconstruct detected particle.
KVReconstructedNucleus * AddParticle()
Nuclei reconstructed from data measured by a detector array ,.
Int_t GetNumberOfIdentificationResults() const
const KVReconNucTrajectory * GetReconstructionTrajectory() const
void SetIdentification(KVIdentificationResult *, KVIDTelescope *)
virtual Int_t GetIDCode() const
KVDetector * GetStoppingDetector() const
void SetDetector(int i, KVDetector *);
virtual void SetTargetEnergyLoss(Double_t e)
void ModifyReconstructionTrajectory(const KVReconNucTrajectory *t)
virtual void GetAnglesFromReconstructionTrajectory(Option_t *opt="random")
@ kStatusStopFirstStage
(arbitrarily) between this and the other particle(s) with Status=2
virtual TObject * Last() const
virtual TObject * First() const
virtual TObject * FindObjectByType(const Char_t *) const
Extension of ROOT TString class which allows backwards compatibility with ROOT v3....
Definition: KVString.h:72
void Begin(TString delim) const
Definition: KVString.cpp:562
Bool_t End() const
Definition: KVString.cpp:625
KVString Next(Bool_t strip_whitespace=kFALSE) const
Definition: KVString.cpp:675
Int_t GetNValues(TString delim) const
Definition: KVString.cpp:859
virtual const char * GetName() const
virtual void Warning(const char *method, const char *msgfmt,...) const
const long double g
masses
Definition: KVUnits.h:72
void Warning(const char *location, const char *va_(fmt),...)
Double_t Abs(Double_t d)
Double_t Max(Double_t a, Double_t b)