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