KaliVeda
Toolkit for HIC analysis
Particle identification

Identification telescopes

Particle identification in KaliVeda is handled by the KVIDTelescope class family. A basic KVIDTelescope associates 2 successive detectors on any of the particle trajectories through the array which in principle can provide \(\Delta E\)- \(E\) identification. Specialised classes are available to handle single-detector identification methods as required (such as PSA in silicon detectors for FAZIA (KVFAZIAIDSiPSA), or fast-slow identification in CsI(Tl) crystals for INDRA (KVIDINDRACsI)).

The actual identification procedure can be handled in different ways, but the method which attempts to identify whatever passed through the detector(s) is

double Double_t
virtual Bool_t Identify(KVIdentificationResult *, Double_t x=-1., Double_t y=-1.)
Full result of one attempted particle identification.

x and y here refer to coordinates in a 2-D identification map (for \(\Delta E\)- \(E\) identification we would have x= \(E\) and y= \(\Delta E\)). The results of the identification attempt are returned in the KVIdentificationResult object passed as first argument. This is a very detailed report on how well (or not) the identification went (see KVIdentificationResult for details).

As the x and y arguments have default values, this method can be called without providing their values, and indeed this is how it is used most of the time. In this case the methods

Base class for particle identification in a 2D map.
Definition: KVIDGraph.h:32
Double_t GetIDGridXCoord(KVIDGraph *, bool &) const
bool GetIDGridCoords(Double_t &X, Double_t &Y, KVIDGraph *grid, Double_t x=-1, Double_t y=-1)
Double_t GetIDGridYCoord(KVIDGraph *, bool &) const

are used to provide the coordinates for the identification map. As will be seen below, the values to be used for the coordinates are automatically computed even if several identification grids are assigned to the same telescope.

Identification grids

The most common way of implementing the identification of particles is by defining identification grids, which are a collection of lines and contours in a 2-D map which can be used to reject or accept particles, assign them a value of \(Z\) and/or \(A\), depending on where they fall in the map, and this in turn depends on how we assign \((x,y)\) coordinates to each particle.

X- and Y-coordinates

This decision is made when histograms are prepared from data in order to draw the identification grid. Naively, for a simple \(\Delta E\)- \(E\) telescope, one would think to fill this histogram as follows:

// supposing TH2F* myhisto (pointer to predefined 2-D histogram)
TH2F* myhisto;
// and KVIDTelescope* idt (pointer to current DE-E identification telescope)
myhisto->Fill(idt->GetDetector(2)->GetDetectorSignalValue("Energy"),
idt->GetDetector(1)->GetDetectorSignalValue("Energy"));
Double_t GetDetectorSignalValue(const KVString &type, const KVNameValueList &params="") const
Definition: KVDetector.h:494
Base class for all detectors or associations of detectors in array which can identify charged particl...
Definition: KVIDTelescope.h:85
KVDetector * GetDetector(UInt_t n) const
virtual Int_t Fill(const char *namex, const char *namey, Double_t w)

using the calibrated energy of each of the two detectors (see Detector signals and calibrations). However this is unlikely and discouraged for two reasons:

  • we don't want to wait for detector calibration before implementing the identifications
  • if we use a calibration to set up identification maps, what happens if the calibrations are later changed? Redo the identification!

One is therefore more likely to use some 'raw' data signal for each detector, or a combination of them (the following example is just to illustrate the possibilities, not a serious suggestion):

double X = idt->GetDetector(2)->GetDetectorSignalValue("Q3.Amplitude")
/idt->GetDetector(2)->GetDetectorSignalValue("Q3.RiseTime");
double Y = idt->GetDetector(1)->GetDetectorSignalValue("Q2.FPGAEnergy")
+idt->GetDetector(1)->GetDetectorSignalValue("I2.Amplitude");
myhisto->Fill(X,Y);
#define X(type, name)

assuming of course that all of the above signals exist for each of the two detectors (see Detector signals and calibrations).

Next you draw your grid(s), and as long as you define the VARX, VARY parameters (using the graphical interface, KVIDGridEditor) to be

VARX : Q3.Amplitude/Q3.RiseTime
VARY : Q2.FPGAEnergy+I2.Amplitude

then when the grid will be loaded by the identification telescope using

// assuming KVIDGraph* mygrid is a pointer to your grid
KVIDGraph* mygrid;
idt->SetGrid(mygrid);

the default KVIDTelescope::GetIDGridCoords() method will automatically return the correct values for your grid, according to the expressions in VARX and VARY.

Multiple grids for the same telescope

Some identifications require several grids for them to be correctly handled: for example, when high gain and low gain signals are available for one or both of the detectors concerned. This can be handled by KVIDTelescope, you just need to give the order in which the grids should be tried in an environment variable (.kvrootrc file):

[dataset].[array name].[ID type].GridOrder: [VARY1]_[VARX1],[VARY2]_[VARX2],[VARY3]_[VARX3],...
#Example:
INDRAFAZIA.E789.FAZIA.Si-Si.GridOrder: QL1.Amplitude_Q2.FPGAEnergy,QH1.FPGAEnergy_Q2.FPGAEnergy

Identification with each grid will be attempted in the order given; the first successful identification (with quality code < 4; see KVIDZAGrid) will be accepted.

Debugging identifications for reconstructed data

When data is reconstructed, each reconstructed nucleus in each event retains all informations on each and every identification which was attempted for it, including of course the one that was finally chosen to identify it. You can check how it all went by inspecting each reconstructed nucleus in the following ways.

Assuming that you have opened a ROOT file containing a TTree with reconstructed events, connected a KVReconstructedEvent to the appropriate branch in the TTree, and built the detector array corresponding to the data with all parameters for the corresponding run being set (see 'Debugging reconstructed events'), let us consider the following reconstructed nucleus:

KVReconstructedEvent* recev; // pointer to event read from TTree
auto rnuc = recev->GetParticle(1);
rnuc->Print("data");
SI1-141 -- DetTag=0 | GTTag=0 | QH1.Amplitude=0 | QH1.RawAmplitude=0 | QH1.FPGAEnergy=3130.78 | QH1.RiseTime=0 | QH1.BaseLine=-7507.81 | QH1.SigmaBaseLine=0 | I1.Amplitude=7193.11 | I1.RawAmplitude=8414 | I1.BaseLine=-5153.43 | I1.SigmaBaseLine=7.31065 | QL1.Amplitude=14207.6 | QL1.RawAmplitude=14232 | QL1.RiseTime=4.09013 | QL1.BaseLine=-6016.56 | QL1.SigmaBaseLine=9.4977 | Energy=880.72 |
SI2-141 -- DetTag=0 | GTTag=0 | Q2.Amplitude=5763.84 | Q2.RawAmplitude=5794 | Q2.FPGAEnergy=5791.77 | Q2.RiseTime=135.313 | Q2.BaseLine=-7640.44 | Q2.SigmaBaseLine=1.88319 | I2.Amplitude=2155.14 | I2.RawAmplitude=2264 | I2.BaseLine=-4255.83 | I2.SigmaBaseLine=7.78067 | Energy=1551.57 |
Identification #1 - Type:Si-Si => SUCCESS
Grid used: QH1.FPGAEnergy_Q2.FPGAEnergy
ID code = 12 Quality code = 0 (ok)
Z identified = 30 A returned = 65 PID = 30.093945
Identification #2 - Type:SiPSA => SUCCESS
Grid used: QH1.FPGAEnergy_I1.Amplitude
ID code = 11 Quality code = 0 (ok)
Z identified = 21 A returned = 43 PID = 21.274088
Info flags:
QH1.FPGAEnergy_I1.Amplitude : MassID
STOPPED IN : SI2-141
IDENTIFIED IN : ID_SI1_SI2_141
=======> Z=30 A=65 Zreal=30.0939 Total Energy = 2433.47 MeV, Theta=1.98989 Phi=89.8088
Target energy loss correction : 1.17673 MeV
RECONSTRUCTION STATUS :
Particle alone in group, or identification independently of other particles in group is directly possible.
GDNTraj_583_SI2-141 : SI2-141/SI1-141/
Identifications [2/2] :
ID_SI1_SI2_141 (1)
ID_SI1_141 (1)
KVNameValueList::ParticleParameters : Parameters associated with a particle in an event (0x55fb3d0e2348)
<ARRAY=FAZIA>
<DetTag=7967>
<GTTag=8035>
<SI2-141.Q2.Amplitude=5763.84>
<SI2-141.Q2.RawAmplitude=5794>
<SI2-141.Q2.FPGAEnergy=5791.77>
...
etc. etc.
Handles lists of named parameters with different types, a list of KVNamedParameter objects.
Event containing KVReconstructedNucleus nuclei reconstructed from hits in detectors.
void Print(Option_t *option="") const override
Particle * GetParticle(Int_t npart) const
RooCmdArg Parameters(const RooArgSet &params)
Type
void Info(const char *location, const char *fmt,...)
const long double MeV
energies
Definition: KVUnits.h:91

This reconstructed nucleus was detected by the FAZIA array, apparently stopping in a SI2 detector and identified as a \(Z=30\) (zinc) nucleus by a SI1-SI2 identification telescope:

STOPPED IN : SI2-141
IDENTIFIED IN : ID_SI1_SI2_141
=======> Z=30 A=65 Zreal=30.0939 Total Energy = 2433.47 MeV, Theta=1.98989 Phi=89.8088
Target energy loss correction : 1.17673 MeV

As can be seen at the top of the KVReconstructedNucleus::Print() output, there were 2 identification attempts made for this nucleus:

Identification #1 - Type:Si-Si => SUCCESS
Grid used: QH1.FPGAEnergy_Q2.FPGAEnergy
ID code = 12 Quality code = 0 (ok)
Z identified = 30 A returned = 65 PID = 30.093945
Identification #2 - Type:SiPSA => SUCCESS
Grid used: QH1.FPGAEnergy_I1.Amplitude
ID code = 11 Quality code = 0 (ok)
Z identified = 21 A returned = 43 PID = 21.274088
Info flags:
QH1.FPGAEnergy_I1.Amplitude : MassID

The order of these identification results represents the order in which the identification attempts were made during the reconstruction: first of all, the first identification telescope containing the detector in which the nucleus stopped (but not any detector placed 'behind' i.e. further away from the target than the 'stopping detector'), and then each identification telescope in turn moving up the particle's reconstruction trajectory towards the target. In this case, the particle punches through the SI1 detector in which pulse shape analysis (PSA) identification is used, therefore it is not surprising that the identification using this method is not coherent with the one from the \(\Delta E-E\) telescope, SI1-SI2. Unless (unexpected) inconsistencies are found between the different identification methods, it is the first successful identification which wins, as is the case here.

Retrieving detailed information on each identification attempt

To look in more detail at the identifications, we need to retrieve the KVIdentificationResult for each one in the list above. For the 'winning' identification, this can be done like so:

KVReconstructedNucleus* rnuc; // pointer retrieved from event previously
auto idr_1 = rnuc->GetIdentificationResult( rnuc->GetIdentifyingTelescope() );
Nuclei reconstructed from data measured by a detector array .
KVIdentificationResult * GetIdentificationResult(Int_t i)
KVIDTelescope * GetIdentifyingTelescope() const

as KVReconstructedNucleus::GetIdentifyingTelescope() returns the address of the KVIDTelescope in which the particle was identified. For the other identification result in the list, you can do one of:

KVReconstructedNucleus* rnuc; // pointer retrieved from event previously
auto idr_2 = rnuc->GetIdentificationResult( 2 );// get 2nd in list
auto idr_2a = rnuc->GetIdentificationResult( "SiPSA" );// get result for identification of given type

The equivalent of these last two for the 'identifying telescope' would be:

KVReconstructedNucleus* rnuc; // pointer retrieved from event previously
auto idr_1a = rnuc->GetIdentificationResult( 1 );// get 1st in list
auto idr_1b = rnuc->GetIdentificationResult( "Si-Si" );// get result for identification of given type
auto idr_1c = rnuc->GetIdentificationResult( rnuc->GetIdentifyingTelescope()->GetType() );// idem
virtual const Char_t * GetType() const
Definition: KVBase.h:176

See the KVIdentificationResult class documentation for the list of informations which are available for each identification attempt

Checking the coordinates in the identification grid

Each identification is associated with a grid used to deduce particle identity from the correlations in a 2-dimensional map. In order to retrieve each grid, each identification result contains the name of the grid that was used for identification (there can be more than one grid for certain identification telescopes):

KVReconstructedNucleus* rnuc; // pointer retrieved from event previously
// get identification result for identification which was used to identify the particle
// retrieve identification grid used by telescope for the identification
auto idg = rnuc->GetIdentifyingTelescope()->GetIDGrid( idr->GetGridName() );
KVIDGraph * GetIDGrid() const

For identifications other than the 'winning' identification, you need to retrieve the KVIDTelescope from the particle's reconstruction trajectory using the type which is stored in the identification result:

KVReconstructedNucleus* rnuc; // pointer retrieved from event previously
// get identification result for 2nd identification attempt
auto idr = rnuc->GetIdentificationResult( 2 );
// retrieve identification telescope from reconstruction trajectory
auto idt = rnuc->GetReconstructionTrajectory()->GetIDTelescope( idr->GetIDType() );
// retrieve identification grid used by telescope for the identification
auto idg = idt->GetIDGrid( idr->GetGridName() );
KVIDTelescope * GetIDTelescope(const TString &) const
const KVReconNucTrajectory * GetReconstructionTrajectory() const

Once you have pointers to the grid and the identification telescope, you can retrieve the coordinates used for the present identification. First of all, we can ask for the symbolic names of the \(x-\) and \(y-\)coordinates, which usually correspond to names of signals (KVDetectorSignal) associated with the detectors in the telescope:

idg->GetVarX();
"I1.Amplitude"
idg->GetVarY();
"QH1.FPGAEnergy"

Now, because when reconstructed events are read in from a file, we set the raw signal values of the corresponding detectors in the array to the values measured in the event, you can retrieve the coordinates used in the identification map to identify this particle by doing:

double x,y;
idt->GetIDGridCoords(x,y,idg);
std::cout << "x=" << x << ", y=" << y << std::endl;
x=7193.11, y=3130.78
Double_t y[n]
Double_t x[n]

If you look back to when we first printed all the informations on this particle, you will see that these are indeed the values of the I1.Amplitude and QH1.FPGAEnergy signals for the detector SI1-141 in the event.