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