KaliVeda  1.13/01
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 or in (Si1+Si2)-CsI
94  if (PART->GetIDCode() == KVFAZIA::IDCodes::ID_SI2_CSI || PART->GetIDCode() == KVFAZIA::IDCodes::ID_SI12_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  //
114  // For Z<=2 the calibration code KVFAZIA::ECodes::ENERGY_LOSSES_TENTATIVELY_CALCULATED is given
115  // (because silicon energy losses may be too small to give correct estimation for Z=1 and Z=2):
116  bool si1_pileup = PART->GetParameters()->GetBoolValue("si1_pileup");
117 
118  if (si1->IsCalibrated() && si1->GetEnergy() && si2->IsCalibrated() && si2->GetEnergy() && !si1_pileup) {
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  if (PART->GetZ() > 2) SetCalibrationStatus(*PART, KVFAZIA::ECodes::SOME_ENERGY_LOSSES_CALCULATED); // CsI energy calculated
128  else SetCalibrationStatus(*PART, KVFAZIA::ECodes::ENERGY_LOSSES_TENTATIVELY_CALCULATED); // CsI energy calculated
129  }
130  }
131  }
132 
133  // particle identified in CsI
134  if (PART->GetIDCode() == KVFAZIA::IDCodes::ID_CSI_PSA) {
135 
136  bool si1_pileup = PART->GetParameters()->GetBoolValue("si1_pileup");
137  bool si2_pileup = PART->GetParameters()->GetBoolValue("si2_pileup");
138 
139  KVNameValueList part_id(Form("Z=%d,A=%d", PART->GetZ(), PART->GetA()));
140 
141  double esi1 = -1;
142  double esi2 = -1;
143  double ecsi = -1;
144  std::unordered_map<std::string, bool> is_calculated;
145  is_calculated["csi"] = is_calculated["si2"] = is_calculated["si1"] = false;
146  if (csi->IsCalibrated(part_id)) ecsi = csi->GetDetectorSignalValue("Energy", part_id);
147  if (!si2_pileup && si2->IsCalibrated()) esi2 = si2->GetEnergy();
148  if (!si1_pileup && si1->IsCalibrated()) esi1 = si1->GetEnergy();
149  // pile-up in Si2 with calibrated CsI: calculate esi1 & esi2
150  if (si2_pileup && csi->IsCalibrated(part_id) && ecsi > 0) {
151  esi2 = si2->GetDeltaEFromERes(PART->GetZ(), PART->GetA(), ecsi);
152  is_calculated["si2"] = true;
153  esi1 = si1->GetDeltaEFromERes(PART->GetZ(), PART->GetA(), esi2 + ecsi);
154  is_calculated["si1"] = true;
155  }
156  // pile-up in Si1 with calibrated CsI & calibrated Si2: calculate esi1
157  else if (si1_pileup && csi->IsCalibrated(part_id) && si2->IsCalibrated() && ecsi > 0 && esi2 > 0) {
158  esi1 = si1->GetDeltaEFromERes(PART->GetZ(), PART->GetA(), esi2 + ecsi);
159  is_calculated["si1"] = true;
160  }
161  // treat case of uncalibrated CsI detector
162  // case where SI1 && SI2 are calibrated & present in event & no pileup detected in Si1/Si2
163  // and only for Z>2 (because silicon energy losses are too small to give correct
164  // estimation for Z=1 and Z=2):
165  if (!csi->IsCalibrated(part_id) &&
166  (si1->IsCalibrated() && si1->GetEnergy() && si2->IsCalibrated() && si2->GetEnergy())
167  && !si1_pileup && !si2_pileup && PART->GetZ() > 2) {
168  // calculate total delta-E in (SI1+SI2) then use to calculate CsI energy
169  double deltaE = esi1 + esi2;
170  KVDetector si1si2("Si", si1->GetThickness() + si2->GetThickness());
171  ecsi = si1si2.GetEResFromDeltaE(PART->GetZ(), PART->GetA(), deltaE);
172  is_calculated["csi"] = true;
173  }
174 
175  if (ecsi > 0 && esi1 > 0 && esi2 > 0) {
176  if (is_calculated["si1"] || is_calculated["si2"] || is_calculated["csi"])
177  SetCalibrationStatus(*PART, KVFAZIA::ECodes::SOME_ENERGY_LOSSES_CALCULATED);
178  else
179  SetCalibrationStatus(*PART, KVFAZIA::ECodes::NORMAL_CALIBRATION);
180  PART->SetEnergy(esi1 + esi2 + ecsi);
181  PART->SetParameter("FAZIA.ESI1", is_calculated["si1"] ? -esi1 : esi1);
182  PART->SetParameter("FAZIA.ESI2", is_calculated["si2"] ? -esi2 : esi2);
183  PART->SetParameter("FAZIA.ECSI", is_calculated["csi"] ? -ecsi : ecsi);
184  }
185  }
186 
187  if (PART->IsCalibrated()) {
188 
189  //add correction for target energy loss - moving charged particles only!
190  Double_t E_targ = 0.;
191  if (PART->GetZ() && PART->GetEnergy() > 0) {
192  E_targ = GetTargetEnergyLossCorrection(PART);
193  PART->SetTargetEnergyLoss(E_targ);
194  }
195  Double_t E_tot = PART->GetEnergy() + E_targ;
196  PART->SetEnergy(E_tot);
197 
198  // set particle momentum from telescope dimensions (random)
199  PART->GetAnglesFromReconstructionTrajectory();
200 
201  // check for energy loss coherency
202  KVNucleus avatar;
203  avatar.SetZAandE(PART->GetZ(), PART->GetA(), PART->GetKE());
204 
205  int ndet = 0;
206  KVGeoDetectorNode* node = 0;
207  // iterating over detectors starting from the target
208  // compute the theoretical energy loss of the avatar
209  // compare to the calibrated/calculated energy
210  // remove this energy from the avatar energy
211  PART->GetReconstructionTrajectory()->IterateBackFrom();
212  while ((node = PART->GetReconstructionTrajectory()->GetNextNode())) {
213  det = (KVFAZIADetector*)node->GetDetector();
214  Double_t temp = det->GetELostByParticle(&avatar);
215  PART->SetParameter(Form("FAZIA.avatar.E%s", det->GetLabel()), temp);
216  avatar.SetKE(avatar.GetKE() - temp);
217  ndet++;
218  }
219  }
220 }
221 
222 
223 
231 
233 {
234  // Calibration routine for particles added in AddCoherencyParticles() method.
235  //
236  // Only particles identified in Si1-Si2 or Si1-PSA are treated.
237  //
238  // We take into account, where possible, the calculated energy losses in SI1 and SI2
239  // of the "parent" nucleus (i.e. the one which stopped in the CSI).
240 
241  PART->SetIsUncalibrated();
242  PART->SetECode(KVFAZIA::ECodes::NO_CALIBRATION_ATTEMPTED);
243 
244  // particle identified in Si1 PSA, detector is calibrated
245  if (PART->GetIDCode() == KVFAZIA::IDCodes::ID_SI1_PSA) {
246  if (si1->IsCalibrated()) {
247  double e1 = si1->GetCalibratedEnergy();
248  if (e1 <= 0) {
249  Warning("CalibrateParticle",
250  "IDCODE=11 Z=%d A=%d calibrated SI1 E=%f",
251  PART->GetZ(), PART->GetA(), e1);
252  return;
253  }
254  PART->SetParameter("FAZIA.ESI1", -e1); // energy loss is calculated in this case
255  PART->SetEnergy(e1);
256  SetCalibrationStatus(*PART, KVFAZIA::ECodes::SOME_ENERGY_LOSSES_CALCULATED); // energy loss is calculated in this case
257  }
258  }
259 
260  // particle identified in Si1-Si2
261  if (PART->GetIDCode() == KVFAZIA::IDCodes::ID_SI1_SI2) {
262  if (si1->IsCalibrated() && si2->IsCalibrated()) { // with both detectors calibrated
263  double e1(0), e2(0);
264  if ((e2 = si2->GetCalibratedEnergy()) > 0) {
265  if (!((e1 = si1->GetCalibratedEnergy()) > 0)) { // if SI1 not fired, we calculate from SI2 energy
266  e1 = si1->GetDeltaEFromERes(PART->GetZ(), PART->GetA(), e2);
267  }
268  }
269  else if ((e1 = si1->GetCalibratedEnergy()) > 0) {
270  // if SI2 not fired, we calculate from SI1 energy
271  e2 = si1->GetEResFromDeltaE(PART->GetZ(), PART->GetA(), e1);
272  }
273 
274  PART->SetParameter("FAZIA.ESI1", -e1); // always calculated
275  PART->SetParameter("FAZIA.ESI2", -e2); // always calculated
276  if (e1 > 0 && e2 > 0) {
277  PART->SetEnergy(e1 + e2);
278  SetCalibrationStatus(*PART, KVFAZIA::ECodes::SOME_ENERGY_LOSSES_CALCULATED); // always calculated
279  }
280  }
281  }
282 
283  if (PART->IsCalibrated()) {
284 
285  //add correction for target energy loss - moving charged particles only!
286  Double_t E_targ = 0.;
287  if (PART->GetZ() && PART->GetEnergy() > 0) {
288  E_targ = GetTargetEnergyLossCorrection(PART);
289  PART->SetTargetEnergyLoss(E_targ);
290  }
291  Double_t E_tot = PART->GetEnergy() + E_targ;
292  PART->SetEnergy(E_tot);
293 
294  // set particle momentum from telescope dimensions (random)
296 
297  // check for energy loss coherency
298  KVNucleus avatar;
299  avatar.SetZAandE(PART->GetZ(), PART->GetA(), PART->GetKE());
300 
301  int ndet = 0;
302  KVGeoDetectorNode* node = 0;
303  // iterating over detectors starting from the target
304  // compute the theoretical energy loss of the avatar
305  // compare to the calibrated/calculated energy
306  // remove this energy from the avatar energy
308  while ((node = PART->GetReconstructionTrajectory()->GetNextNode())) {
309  auto det = (KVFAZIADetector*)node->GetDetector();
310  Double_t temp = det->GetELostByParticle(&avatar);
311  PART->SetParameter(Form("FAZIA.avatar.E%s", det->GetLabel()), temp);
312  avatar.SetKE(avatar.GetKE() - temp);
313  ndet++;
314  }
315  }
316 }
317 
318 
319 
323 
325 {
326  // Copy FPGA energy values to reconstructed particle parameter lists
327  // Set values in detectors for identification/calibration procedures
328 
329  for (KVReconstructedEvent::Iterator it = GetEventFragment()->begin(); it != GetEventFragment()->end(); ++it) {
330  KVReconstructedNucleus* rnuc = it.get_pointer<KVReconstructedNucleus>();
331 
333 
334  KVGeoDetectorNode* node;
335  while ((node = rnuc->GetReconstructionTrajectory()->GetNextNode())) {
336 
338 
339  // now copy all detector signals to reconstructed particle parameter list...
340  // they are stored with format "[detname].[signal_name]" except for
341  // DetTag and GTTag which are the same for all detectors of the same telescope
342  // and so are only stored once with name "DetTag" or "GTTag".
343  TIter it(&det->GetListOfDetectorSignals());
344  KVDetectorSignal* ds;
345  while ((ds = (KVDetectorSignal*)it())) {
346  if (ds->IsRaw() && !ds->IsExpression())
347  // only store raw data, excluding any expressions based only on raw data
348  {
349  TString pname;
350  // Only store non-zero parameters
351  if (ds->GetValue() != 0) {
352  if (TString(ds->GetName()) != "DetTag" && TString(ds->GetName()) != "GTTag")
353  pname = Form("%s.%s", det->GetName(), ds->GetName());
354  else
355  pname = ds->GetName();
356  rnuc->GetParameters()->SetValue(pname, ds->GetValue());
357  }
358  }
359  }
360  }
361  }
362 }
363 
364 
365 
374 
376 {
377  // Coherency codes (CCode):
378  // -1 no modification du to coherency checks
379  // 1 CsI id = gamma + good in Si-Si -> Si-Si
380  // 2 CsI id -> Si-CsI id
381  // 3 Si-CsI id -> Si-Si id because Z(sicsi)<Z(sisi)
382  // 4 stopped in CsI (no id) + good id Si-Si -> Si-Si
383  // 5 stopped in SI2 (no id) + good id Si1PSA -> Si1PSA
384 
386 
387  // check for unvetoed punch-through particles in Si1-Si2 identification
388  auto si1si2 = id_by_type.find("Si-Si");
389  if (si1si2 != id_by_type.end()) HandleSI1SI2PunchThrough(si1si2->second, PART);
390 
391  bool si1_pileup(false), si2_pileup(false);
392 
393  // check for failed PSA identification for particles stopped in Si1
394  // change status => KVReconstructedNucleus::kStatusStopFirstStage
395  // estimation of minimum Z from energy loss (if Si1 calibrated)
396  if (PART.GetStoppingDetector() == si1 && !PART.IsIdentified()) {
399  return;
400  }
401 
402  // Coherency checks for identifications
403  if (PART.IsIdentified()) {
404  /********** PARTICLES initially stopped/identified in the CSI *****************/
405  if (partID.IsType("CsI")) {
406  bool coherency_particle_added_in_si1si2 = false;
407  if (partID.IDcode == KVFAZIA::IDCodes::ID_GAMMA) { // gammas
408  // look at Si1-Si2 identification.
409  // if a correct identification was obtained, we ignore the gamma in CsI and change to Si1-Si2
410  if (si1si2 != id_by_type.end()) {
411  if (si1si2->second->IDOK) {
412  // Info("IdentifyParticle","Got GAMMA in CsI, changing to Si1Si2 identification [Z=%d A=%d]",
413  // si1si2->second->Z, si1si2->second->A);
415  partID = *(si1si2->second);
416  PART.SetIsIdentified();
418  PART.GetParameters()->SetValue("CCode", 1);
419  }
420  }
421  }
422  else if (partID.IDOK) {
423  // good identification in CsI (light charged particle)
424  // check for pile-up in Si2 (and beyond) - in this case, measured energy loss in Si2+Si1
425  // should not be attributed to this particle
426  auto si2csi = id_by_type.find("Si-CsI");
427  if (si2csi != id_by_type.end()) {
428  // detect a pile-up in Si2 in coincidence with particule detected in CsI
429  // the following covers the following cases:
430  // - good ID Si-CsI (IDquality < kICODE4) with Z>Zcsi
431  // - ID "between the lines" in Si-CsI (IDquality=kICODE4,kICODE5) with Z>Zcsi
432  // - point above last Z line in Si-CsI grid (kICODE7) with Z>Zcsi
433  // In these cases, there is a second particle stopped in Si2: and
434  // hence the measured Si1 energy loss is not to be used either
435  if ((si2csi->second->Z > partID.Z)) {
436  si2_pileup = true;
437  si1_pileup = true;
438  }
439  }
440  if (si1si2 != id_by_type.end()) {
441  // detect a pile-up in Si1/2 in coincidence with particule detected in CsI
442  // the following covers the following cases:
443  // - good ID Si1-Si2 (IDquality < kICODE4) with Z>Zcsi
444  // - ID "between the lines" in Si1-Si2 (IDquality=kICODE4,kICODE5) with Z>Zcsi
445  // - point above last Z line in Si1-Si2 grid (kICODE7) with Z>Zcsi
446  // In these cases, there is a second particle stopped in Si2: and
447  // hence the measured Si1 energy loss is not to be used either
448  if (si1si2->second->Z > partID.Z) {
449  si2_pileup = true;
450  si1_pileup = true;
451  if (si1si2->second->IDOK) {
452  // if in addition the si1si2 identification is OK, we need to add a new particle
453  // to the event corresponding to this fragment stopped in SI2 in coincidence with
454  // the LCP in the CsI
455  coherency_particles.push_back({
456  &PART, si2->GetNode(), theTrajectory,
458  (Int_t)si1si2->second->GetNumber(), PART.GetNumberOfIdentificationResults()
459  }
460  );
461  coherency_particle_added_in_si1si2 = true;
462  }
463  }
464  }
465  auto sipsa = id_by_type.find("SiPSA");
466  if (sipsa != id_by_type.end()) {
467  // detect a pile-up in Si1 in coincidence with particule detected in CsI
468  if ((sipsa->second->Z > partID.Z)) {
469  si1_pileup = true;
470  if (sipsa->second->IDOK && !coherency_particle_added_in_si1si2) {
471  // if in addition the si1psa identification is OK, and if we have not already added
472  // a new particle stopping in SI2 after looking at Si1-Si2 identification,
473  // we need to add a new particle to the event corresponding to this fragment stopped in SI1
474  // in coincidence with the LCP in the CsI
475  coherency_particles.push_back({
476  &PART, si1->GetNode(), theTrajectory,
478  (Int_t)sipsa->second->GetNumber(), PART.GetNumberOfIdentificationResults()
479  }
480  );
481  }
482  }
483  }
484 
485  }
486  }
487  /********** PARTICLES initially identified in SI2-CSI *****************/
488  else if (partID.IsType("Si-CsI")) {
489  // ions correctly identified in Si2-CsI should have coherent identification in Si1-Si2: as the particle punches
490  // through Si2 the Si1-Si2 identification should underestimate the A and/or Z of the ion, i.e. either the A
491  // or the Z from Si1-Si2 identification should be smaller than from Si2-CsI identification.
492  //
493  // the following is to treat the case (quite typical for fazia) where a particle stops in Si2 (id in Si1Si2)
494  // but a small noise in the CsI makes it look like a particle stopped in CsI and identified in Si2-CsI
495  int zz = partID.Z;
496  if (si1si2 != id_by_type.end()) {
497  KVIdentificationResult* idr_si1si2 = si1si2->second;
498  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
499  if (zz < idr_si1si2->Z) {
500  // Info("IdentifyParticle","SiCsI identification [Z=%d A=%d] changed to SiSi identification [Z=%d A=%d]",
501  // PART.GetZ(),PART.GetA(),si1si2->second->Z,si1si2->second->A);
503  partID = *(si1si2->second);
505  PART.GetParameters()->SetValue("CCode", 3);
506  }
507  }
508  }
509  }
510  }
511  else {
512  // particle not identified, apparently stopped in CsI, with Si1-Si2 identification?
513  if (PART.GetStoppingDetector()->IsLabelled("CSI")) {
514  if (si1si2 != id_by_type.end()) {
515  if (si1si2->second->IDOK) {
516  // stopping detector becomes Si2, identification Si1-Si2 accepted
517  // Info("IdentifyParticle","Unidentified, stopped in CsI, good Si1Si2 identification [Z=%d A=%d]",
518  // si1si2->second->Z,si1si2->second->A);
520  partID = *(si1si2->second);
521  PART.SetIsIdentified();
523  PART.GetParameters()->SetValue("CCode", 4);
524  }
525  }
526  }
527  // particle not identified, apparently stopped in SI2, with Si1-PSA identification?
528  // Such particles were added to the event in 1.12/05, but they (mostly) correspond to
529  // punch-through in SI1 and give bad "accumulations" of data in e.g. Z-V plots.
530  // So they are best left unidentified! (we still give the CCode value 5)
531  // They receive idcode KVFAZIA::ID_SI1_PUNCH_THROUGH, are identified, but not in Z (or A)
532  if (PART.GetStoppingDetector()->IsLabelled("SI2")) {
533  auto sipsa = id_by_type.find("SiPSA");
534  if (sipsa != id_by_type.end()) {
535  if (sipsa->second->IDOK) {
536  // stopping detector becomes Si1
538  partID = *(sipsa->second);
539  PART.SetIsIdentified();
540  auto sipsa_idtel = (KVIDTelescope*)PART.GetReconstructionTrajectory()->GetIDTelescopes()->Last();
541  partID.Zident = false;
542  partID.Aident = false;
543  partID.SetComment("particle partially identified by pulse shape analysis in SI1, although it is punching through (no SI2 signal or SI1-SI2 id)");
544  PART.SetIdentification(&partID, sipsa_idtel);
545  PART.GetParameters()->SetValue("CCode", 5);
546  }
547  }
548  }
549  }
550  PART.SetParameter("si1_pileup", si1_pileup);
551  PART.SetParameter("si2_pileup", si2_pileup);
552 }
553 
554 
555 
563 
565 {
566  // Change the reconstruction trajectory & the stopping detector for the particle.
567  //
568  // The stopping detector is moved 1 closer to the target, i.e.
569  //
570  // - initial stopping detector=CSI with trajectory CSI/SI2/SI1: final stopping=SI2, trajectory=SI2/SI1
571  // - initial stopping detector=SI2 with trajectory SI2/SI1: final stopping=SI1, trajectory=SI1
572 
578  PART.ModifyReconstructionTrajectory(newtraj);
579 }
580 
581 
582 
602 
604 {
605  // SI1-SI2 identification may suffer from unvetoed punch-through particles.
606  //
607  // In this case informational cuts/contours have been defined to indicate when a particle
608  // is identified in a region which may be polluted by punch-through.
609  //
610  // In this case, 2 cases are treated:
611  //
612  // a) for well-identified particles (IDquality<4), there is an ambiguity to their identification:
613  // it may well be truly a particle with the deduced Z & A which stopped in SI2, or it may
614  // in fact be a particle with larger Z &/or A which punched-through.
615  //
616  // For these particles, we change the general IDCode to FAZIAIDCodes::ID_SI1_SI2_MAYBE_PUNCH_THROUGH
617  //
618  // b) particles which are not well-identified because between the identification lines, too far to be
619  // considered identified, idquality=4,5, are in this case most likely to be punching through particles
620  // with a Z at least equal to 1 more than that given by the identification routine (Zmin).
621  //
622  // For these particles, we change the general IDCode to FAZIAIDCodes::ID_SI1_SI2_PUNCH_THROUGH
623 
624  if (idr->IdentifyingGridHasFlagWhichBegins("punch_through")) {
625  KVString pt_flag = idr->IdentifyingGridGetFlagWhichBegins("punch_through");
626  bool treat_punch_through = true;
627  if (pt_flag.GetNValues("|") == 2) {
628  // Z range specified as : "punch_through|Z=1-13"
629  pt_flag.Begin("|=");
630  pt_flag.Next();
631  pt_flag.Next();
632  assert(!pt_flag.End());// this should never happen
633  KVNumberList zrange(pt_flag.Next());
634  // only particles with Z in given range are treated for punch-through
635  treat_punch_through = zrange.Contains(idr->Z);
636  }
637  if (treat_punch_through) {
638  bool pt_treated = false;
639  if (idr->IDquality < KVIDZAGrid::kICODE4) {
640  // change general IDcode
641  idr->IDcode = KVFAZIA::IDCodes::ID_SI1_SI2_MAYBE_PUNCH_THROUGH;
642  idr->SetComment("Apparently well-identified particle, but could be punching through to CsI (in which case Z is a minimum)");
643  pt_treated = true;
644  }
645  else if (idr->IDquality < KVIDZAGrid::kICODE6) {
646  // change general IDcode & identification status
647  idr->IDcode = KVFAZIA::IDCodes::ID_SI1_SI2_PUNCH_THROUGH;
648  idr->SetComment("Particle punching through SI2, identified Z is only a minimum estimation");
649  idr->IDOK = kTRUE; // this will previously have been false
650  idr->Zident = kFALSE;
651  pt_treated = true;
652  }
653  if (pt_treated && (PART.GetStoppingDetector() == si2)) {
654  // For any particles stopped (at least apparently) in SI2, we may (potentially) change the identification of particle,
655  // or at least its IDCode
656  PART.SetIsIdentified();
658  }
659  }
660  }
661 }
662 
663 
664 
666 
668 {
670  csi = (KVFAZIADetector*)g->GetDetectorByType("CsI");
671  assert(csi != nullptr);
672  // set unique trajectory for group
677  assert(si1 != nullptr);
678  assert(si2 != nullptr);
680  assert(fSi1Si2IDTelescope != nullptr);
681 }
682 
683 
684 
692 
694 {
695  // Called to add any nuclei not included in the initial reconstruction, but "revealed"
696  // by consistency checks between identifications and calibrations of other nuclei
697  //
698  // These particles have a (string) parameter "COHERENCY"
699 
700  //std::cout << "===========================================================================================\n" << std::endl;
701  //Info("AddCoherencyParticles", "There are %d particles to add to this event\n", (int)coherency_particles.size());
702  for (auto& part : coherency_particles) {
703 
704  auto rnuc = GetEventFragment()->AddParticle();
705 
706 // Info("AddCoherencyParticle", "Adding particle in pile-up with Z=%d A=%d E=%f in %s",
707 // part.original_particle->GetZ(), part.original_particle->GetA(), part.original_particle->GetE(), part.original_particle->GetStoppingDetector()->GetName());
708 
709  auto ESI1_parent = part.original_particle->GetParameters()->HasDoubleParameter("FAZIA.ESI1") ?
710  TMath::Abs(part.original_particle->GetParameters()->GetDoubleValue("FAZIA.ESI1"))
711  : 0;
712  auto ESI2_parent = part.original_particle->GetParameters()->HasDoubleParameter("FAZIA.ESI2") ?
713  TMath::Abs(part.original_particle->GetParameters()->GetDoubleValue("FAZIA.ESI2"))
714  : 0;
715 // std::cout << "ESI1 = " << ESI1_parent << " [" << si1->GetDetectorSignalValue("Energy") <<
716 // "] ESI2 = " << ESI2_parent << " [" << si2->GetDetectorSignalValue("Energy") << "]" << std::endl;
717 
718  // reconstruction
719  auto Rtraj = (const KVReconNucTrajectory*)GetGroup()->GetTrajectoryForReconstruction(part.stopping_trajectory, part.stopping_detector_node);
720 
721 // std::cout << "SI1: " << (si1->IsCalibrated() ? "CALIB." : "NOT CALIB.")
722 // << " SI2: " << (si2->IsCalibrated() ? "CALIB." : "NOT CALIB.")
723 // << " CSI: " << (csi->IsCalibrated(
724 // Form("Z=%d,A=%d", part.original_particle->GetZ(), part.original_particle->GetA())
725 // ) ? "CALIB." : "NOT CALIB.") << std::endl;
726 
727  rnuc->SetReconstructionTrajectory(Rtraj);
728  rnuc->SetParameter("ARRAY", GetGroup()->GetArray()->GetName());
729  rnuc->SetParameter("COHERENCY",
730  "Particle added to event after consistency checks between identifications and calibrations of other nuclei");
731  // identification
732  Int_t idnumber = 1;
733  for (int i = part.first_id_result_to_copy; i <= part.max_id_result_index; ++i) {
734  auto IDR = rnuc->GetIdentificationResult(idnumber++);
735  // copy existing identification results from "parent" particle
736  part.original_particle->GetIdentificationResult(i)->Copy(*IDR);
737  }
738  rnuc->SetIsIdentified();
739  rnuc->SetIdentification(rnuc->GetIdentificationResult(1), part.identifying_telescope);
740 // Info("AddCoherencyParticle", "Initial ident of particle to add: Z=%d A=%d identified in %s",
741 // rnuc->GetZ(), rnuc->GetA(), rnuc->GetIdentifyingTelescope()->GetType());
742 // rnuc->GetIdentificationResult(1)->Print();
743  // with both silicons calibrated, we can try to subtract the contributions of the parent
744  // particle (using inverse calibrations and calculated energy losses of parent),
745  // then try a new identification (only for Si1-Si2: we don't know how to recalculate the PSA parameters for SI1)
746  // as E789 Si1-Si2 identifications are implemented with 2 grids, first a low range
747  // QL1.Amplitude vs. Q2.FPGAEnergy grid (upto Z=8), then full range QH1.FPGAEnergy vs.
748  // Q2.FPGAEnergy, and calibrations for SI1 & SI2 use QH1.FPGAEnergy and Q2.FPGAEnergy
749  // respectively, we set QL1.Amplitude=0 in order to force the use of the recalculated
750  // QH1.FPGAEnergy and Q2.FPGAEnergy in the full range grid.
751  // *actually, SI1 may have also an "Energy-QL1" calibration from QL1.Amplitude,
752  // in which case we can modify this as well
753  if (si1->IsCalibrated() && si2->IsCalibrated()) {
754  // calculate new values of raw parameters
755  double new_q2{0}, new_qh1{0}, new_ql1{0};
756  auto ESI1_qh1 = si1->GetDetectorSignalValue("Energy");
757  auto ESI1_ql1 = si1->GetDetectorSignalValue("Energy-QL1");
758  auto ESI2 = si2->GetEnergy();
759  new_qh1 = si1->GetInverseDetectorSignalValue("Energy", TMath::Max(0., ESI1_qh1 - ESI1_parent), "QH1.FPGAEnergy");
760  if (si1->HasDetectorSignal("Energy-QL1")) {
761  new_ql1 = si1->GetInverseDetectorSignalValue("Energy-QL1", TMath::Max(0., ESI1_ql1 - ESI1_parent), "QL1.Amplitude");
762  }
763 // Info("AddCoherencyParticle", "Changing raw data parameters:");
764 // std::cout << " QL1: " << si1->GetDetectorSignalValue("QL1.Amplitude") << " ==> " << new_ql1 << std::endl;
765 // std::cout << " QH1: " << si1->GetDetectorSignalValue("QH1.FPGAEnergy") << " ==> " << new_qh1 << std::endl;
766  si1->SetDetectorSignalValue("QH1.FPGAEnergy", new_qh1);
767  si1->SetDetectorSignalValue("QL1.Amplitude", new_ql1);
768 
769  if (rnuc->GetIDCode() == KVFAZIA::IDCodes::ID_SI1_SI2) {
770  new_q2 = si2->GetInverseDetectorSignalValue("Energy", TMath::Max(0., ESI2 - ESI2_parent), "Q2.FPGAEnergy");
771 // std::cout << " Q2: " << si2->GetDetectorSignalValue("Q2.FPGAEnergy") << " ==> " << new_q2 << std::endl;
772  si2->SetDetectorSignalValue("Q2.FPGAEnergy", new_q2);
773 
774  // now retry the identification
776  IDR.SetNumber(1);
777  part.identifying_telescope->Identify(&IDR);
778 // rnuc->GetIdentificationResult(1)->Print();
779  if (IDR.IDOK) {
780 // Info("AddCoherencyParticle", "Achieved new identification for particle:");
781  *(rnuc->GetIdentificationResult(1)) = IDR;
782  rnuc->SetIdentification(rnuc->GetIdentificationResult(1), part.identifying_telescope);
783  // check we are not in punch-through region
784  HandleSI1SI2PunchThrough(rnuc->GetIdentificationResult(1), *rnuc);
785  }
786  else {
787 // Info("AddCoherencyParticle", "Identification has failed for particle");
788  if (new_q2 < 1) {
789 // Info("AddCoherencyParticle", "Try SI1-PSA identification?");
790 // part.original_particle->GetIdentificationResult(5)->Print();
791  // subtraction of original particle leaves nothing in SI2: try SI1-PSA ?
792  if (part.original_particle->GetIdentificationResult(5)->IDOK) {
793  // modify reconstruction trajectory: now starts on SI1 not SI2
794  Rtraj = (const KVReconNucTrajectory*)GetGroup()->GetTrajectoryForReconstruction(part.stopping_trajectory, si1->GetNode());
795  rnuc->ModifyReconstructionTrajectory(Rtraj);
796  part.original_particle->GetIdentificationResult(5)->Copy(*rnuc->GetIdentificationResult(1));
797  auto idt = (KVIDTelescope*)Rtraj->GetIDTelescopes()->First();
798  rnuc->SetIdentification(rnuc->GetIdentificationResult(1), idt);
799  }
800  else {
801  // leave original estimation of identification Si1-Si2
802  // but check we are not in punch-through region
803  HandleSI1SI2PunchThrough(rnuc->GetIdentificationResult(1), *rnuc);
804  }
805  }
806  else {
807  // leave original estimation of identification Si1-Si2
808  // but check we are not in punch-through region
809  HandleSI1SI2PunchThrough(rnuc->GetIdentificationResult(1), *rnuc);
810  }
811  }
812  }
813  }
814  // calibration
815  if (rnuc->IsIdentified()) {
817 // Info("AddCoherencyParticle", "Added particle with Z=%d A=%d E=%f identified in %s",
818 // rnuc->GetZ(), rnuc->GetA(), rnuc->GetE(), rnuc->GetIdentifyingTelescope()->GetType());
819 // std::cout << "ESI1 = " << rnuc->GetParameters()->GetDoubleValue("FAZIA.ESI1")
820 // << " [" << rnuc->GetParameters()->GetDoubleValue("FAZIA.avatar.ESI1") << "]"
821 // << " ESI2 = " << rnuc->GetParameters()->GetDoubleValue("FAZIA.ESI2")
822 // << " [" << rnuc->GetParameters()->GetDoubleValue("FAZIA.avatar.ESI2") << "]"
823 // << std::endl << std::endl;
824  }
825  }
826 // std::cout << "===========================================================================================\n\n" << std::endl;
827 }
828 
829 
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:184
const Char_t * GetLabel() const
Definition: KVBase.h:198
virtual void SetNumber(UInt_t num)
Definition: KVBase.h:215
Bool_t IsLabelled(const Char_t *l) const
Definition: KVBase.h:206
UInt_t GetNumber() const
Definition: KVBase.h:219
Base class for output signal data produced by a detector.
virtual Bool_t IsExpression() const
virtual Bool_t IsRaw() const
virtual Double_t GetValue(const KVNameValueList &params="") const
Base class for detector geometry description.
Definition: KVDetector.h:159
void SetDetectorSignalValue(const KVString &type, Double_t val) const
Definition: KVDetector.h:505
Double_t GetInverseDetectorSignalValue(const KVString &output, Double_t value, const KVString &input, const KVNameValueList &params="") const
Definition: KVDetector.h:516
virtual Double_t GetELostByParticle(KVNucleus *, TVector3 *norm=0)
Definition: KVDetector.cpp:276
virtual Double_t GetEnergy() const
Definition: KVDetector.h:348
Bool_t HasDetectorSignal(const KVString &type) const
Definition: KVDetector.h:541
Bool_t IsCalibrated() const
Definition: KVDetector.h:389
Double_t GetDetectorSignalValue(const KVString &type, const KVNameValueList &params="") const
Definition: KVDetector.h:492
virtual Double_t GetCalibratedEnergy() const
Definition: KVDetector.h:343
const KVSeqCollection & GetListOfDetectorSignals() const
Definition: KVDetector.h:785
KVGeoDetectorNode * GetNode()
Definition: KVDetector.h:325
virtual Double_t GetDeltaEFromERes(Int_t Z, Int_t A, Double_t Eres)
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:83
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)
Bool_t Aident
= kTRUE if A of particle established
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)
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:816
Double_t GetEnergy() const
Definition: KVParticle.h:623
void SetKE(Double_t ecin)
Definition: KVParticle.cpp:230
void SetParameter(const Char_t *name, ValType value) const
Definition: KVParticle.h:820
Double_t GetKE() const
Definition: KVParticle.h:616
void SetEnergy(Double_t e)
Definition: KVParticle.h:601
Path through detector array used to reconstruct detected particle.
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:565
Bool_t End() const
Definition: KVString.cpp:634
KVString Next(Bool_t strip_whitespace=kFALSE) const
Definition: KVString.cpp:695
Int_t GetNValues(TString delim) const
Definition: KVString.cpp:886
Iterator end() const
Particle * AddParticle()
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)