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