remoe duplicate QA initialisation and do ESD QA for same detectors as RecPoint QA
[u/mrichter/AliRoot.git] / MUON / AliMUONTrack.cxx
1 /**************************************************************************
2  * Copyright(c) 1998-1999, ALICE Experiment at CERN, All rights reserved. *
3  *                                                                        *
4  * Author: The ALICE Off-line Project.                                    *
5  * Contributors are mentioned in the code where appropriate.              *
6  *                                                                        *
7  * Permission to use, copy, modify and distribute this software and its   *
8  * documentation strictly for non-commercial purposes is hereby granted   *
9  * without fee, provided that the above copyright notice appears in all   *
10  * copies and that both the copyright notice and this permission notice   *
11  * appear in the supporting documentation. The authors make no claims     *
12  * about the suitability of this software for any purpose. It is          *
13  * provided "as is" without express or implied warranty.                  *
14  **************************************************************************/
15
16 /* $Id$ */
17
18 //-----------------------------------------------------------------------------
19 // Class AliMUONTrack
20 //-------------------
21 // Reconstructed track in ALICE dimuon spectrometer
22 //-----------------------------------------------------------------------------
23
24 #include "AliMUONTrack.h"
25
26 #include "AliMUONReconstructor.h"
27 #include "AliMUONRecoParam.h"
28 #include "AliMUONVCluster.h"
29 #include "AliMUONVClusterStore.h"
30 #include "AliMUONObjectPair.h"
31 #include "AliMUONConstants.h"
32 #include "AliMUONTrackExtrap.h"
33
34 #include "AliLog.h"
35
36 #include <TMath.h>
37 #include <TMatrixD.h>
38
39 #include <Riostream.h>
40
41 /// \cond CLASSIMP
42 ClassImp(AliMUONTrack) // Class implementation in ROOT context
43 /// \endcond
44
45 //__________________________________________________________________________
46 AliMUONTrack::AliMUONTrack()
47   : TObject(),
48     fTrackParamAtCluster(0x0),
49     fFitWithVertex(kFALSE),
50     fVertexErrXY2(),
51     fFitWithMCS(kFALSE),
52     fClusterWeightsNonBending(0x0),
53     fClusterWeightsBending(0x0),
54     fGlobalChi2(-1.),
55     fImproved(kFALSE),
56     fMatchTrigger(-1),
57     floTrgNum(-1),
58     fChi2MatchTrigger(0.),
59     fTrackID(0),
60     fTrackParamAtVertex(0x0),
61     fHitsPatternInTrigCh(0),
62     fLocalTrigger(0)
63 {
64   /// Default constructor
65   fVertexErrXY2[0] = 0.;
66   fVertexErrXY2[1] = 0.;
67 }
68
69   //__________________________________________________________________________
70 AliMUONTrack::AliMUONTrack(AliMUONObjectPair *segment)
71   : TObject(),
72     fTrackParamAtCluster(new TClonesArray("AliMUONTrackParam",10)),
73     fFitWithVertex(kFALSE),
74     fVertexErrXY2(),
75     fFitWithMCS(kFALSE),
76     fClusterWeightsNonBending(0x0),
77     fClusterWeightsBending(0x0),
78     fGlobalChi2(0.),
79     fImproved(kFALSE),
80     fMatchTrigger(-1),
81     floTrgNum(-1),    
82     fChi2MatchTrigger(0.),
83     fTrackID(0),
84     fTrackParamAtVertex(0x0),
85     fHitsPatternInTrigCh(0),
86     fLocalTrigger(0)
87 {
88   /// Constructor from two clusters
89   
90   fVertexErrXY2[0] = 0.;
91   fVertexErrXY2[1] = 0.;
92   
93   // Pointers to clusters from the segment
94   AliMUONVCluster* firstCluster = (AliMUONVCluster*) segment->First();
95   AliMUONVCluster* lastCluster = (AliMUONVCluster*) segment->Second();
96   
97   // Compute track parameters
98   Double_t z1 = firstCluster->GetZ();
99   Double_t z2 = lastCluster->GetZ();
100   Double_t dZ = z1 - z2;
101   // Non bending plane
102   Double_t nonBendingCoor1 = firstCluster->GetX();
103   Double_t nonBendingCoor2 = lastCluster->GetX();
104   Double_t nonBendingSlope = (nonBendingCoor1 - nonBendingCoor2) / dZ;
105   // Bending plane
106   Double_t bendingCoor1 = firstCluster->GetY();
107   Double_t bendingCoor2 = lastCluster->GetY();
108   Double_t bendingSlope = (bendingCoor1 - bendingCoor2) / dZ;
109   // Inverse bending momentum
110   Double_t bendingImpact = bendingCoor1 - z1 * bendingSlope;
111   Double_t inverseBendingMomentum = 1. / AliMUONTrackExtrap::GetBendingMomentumFromImpactParam(bendingImpact);
112   
113   // Set track parameters at first cluster
114   AliMUONTrackParam trackParamAtFirstCluster;
115   trackParamAtFirstCluster.SetZ(z1);
116   trackParamAtFirstCluster.SetNonBendingCoor(nonBendingCoor1);
117   trackParamAtFirstCluster.SetNonBendingSlope(nonBendingSlope);
118   trackParamAtFirstCluster.SetBendingCoor(bendingCoor1);
119   trackParamAtFirstCluster.SetBendingSlope(bendingSlope);
120   trackParamAtFirstCluster.SetInverseBendingMomentum(inverseBendingMomentum);
121   
122   // Set track parameters at last cluster
123   AliMUONTrackParam trackParamAtLastCluster;
124   trackParamAtLastCluster.SetZ(z2);
125   trackParamAtLastCluster.SetNonBendingCoor(nonBendingCoor2);
126   trackParamAtLastCluster.SetNonBendingSlope(nonBendingSlope);
127   trackParamAtLastCluster.SetBendingCoor(bendingCoor2);
128   trackParamAtLastCluster.SetBendingSlope(bendingSlope);
129   trackParamAtLastCluster.SetInverseBendingMomentum(inverseBendingMomentum);
130   
131   // Compute and set track parameters covariances at first cluster
132   TMatrixD paramCov(5,5);
133   paramCov.Zero();
134   // Non bending plane
135   paramCov(0,0) = firstCluster->GetErrX2();
136   paramCov(0,1) = firstCluster->GetErrX2() / dZ;
137   paramCov(1,0) = paramCov(0,1);
138   paramCov(1,1) = ( firstCluster->GetErrX2() + lastCluster->GetErrX2() ) / dZ / dZ;
139   // Bending plane
140   paramCov(2,2) = firstCluster->GetErrY2();
141   paramCov(2,3) = firstCluster->GetErrY2() / dZ;
142   paramCov(3,2) = paramCov(2,3);
143   paramCov(3,3) = ( firstCluster->GetErrY2() + lastCluster->GetErrY2() ) / dZ / dZ;
144   // Inverse bending momentum (vertex resolution + bending slope resolution + 10% error on dipole parameters+field)
145   paramCov(4,4) = ((AliMUONReconstructor::GetRecoParam()->GetBendingVertexDispersion() *
146                     AliMUONReconstructor::GetRecoParam()->GetBendingVertexDispersion() +
147                     (z1 * z1 * lastCluster->GetErrY2() + z2 * z2 * firstCluster->GetErrY2()) / dZ / dZ) /
148                    bendingImpact / bendingImpact + 0.1 * 0.1) * inverseBendingMomentum * inverseBendingMomentum;
149   paramCov(2,4) = - z2 * firstCluster->GetErrY2() * inverseBendingMomentum / bendingImpact / dZ;
150   paramCov(4,2) = paramCov(2,4);
151   paramCov(3,4) = - (z1 * lastCluster->GetErrY2() + z2 * firstCluster->GetErrY2()) * inverseBendingMomentum / bendingImpact / dZ / dZ;
152   paramCov(4,3) = paramCov(3,4);
153   
154   // Set covariances
155   trackParamAtFirstCluster.SetCovariances(paramCov);
156   
157   // Compute and set track parameters covariances at last cluster
158   paramCov(1,0) = - paramCov(1,0);
159   paramCov(0,1) = - paramCov(0,1);
160   paramCov(3,2) = - paramCov(3,2);
161   paramCov(2,3) = - paramCov(2,3);
162   paramCov(2,4) = z1 * lastCluster->GetErrY2() * inverseBendingMomentum / bendingImpact / dZ;
163   paramCov(4,2) = paramCov(2,4);
164   trackParamAtLastCluster.SetCovariances(paramCov);
165   
166   // Add track parameters at clusters
167   AddTrackParamAtCluster(trackParamAtFirstCluster,*firstCluster);
168   AddTrackParamAtCluster(trackParamAtLastCluster,*lastCluster);
169   
170 }
171
172 //__________________________________________________________________________
173 AliMUONTrack::AliMUONTrack(const AliMUONTrack& track)
174   : TObject(track),
175     fTrackParamAtCluster(0x0),
176     fFitWithVertex(track.fFitWithVertex),
177     fVertexErrXY2(),
178     fFitWithMCS(track.fFitWithMCS),
179     fClusterWeightsNonBending(0x0),
180     fClusterWeightsBending(0x0),
181     fGlobalChi2(track.fGlobalChi2),
182     fImproved(track.fImproved),
183     fMatchTrigger(track.fMatchTrigger),
184     floTrgNum(track.floTrgNum),    
185     fChi2MatchTrigger(track.fChi2MatchTrigger),
186     fTrackID(track.fTrackID),
187     fTrackParamAtVertex(0x0),
188     fHitsPatternInTrigCh(track.fHitsPatternInTrigCh),
189     fLocalTrigger(track.fLocalTrigger)
190 {
191   ///copy constructor
192   
193   // necessary to make a copy of the objects and not only the pointers in TClonesArray.
194   if (track.fTrackParamAtCluster) {
195     fTrackParamAtCluster = new TClonesArray("AliMUONTrackParam",10);
196     AliMUONTrackParam *trackParamAtCluster = (AliMUONTrackParam*) track.fTrackParamAtCluster->First();
197     while (trackParamAtCluster) {
198       new ((*fTrackParamAtCluster)[GetNClusters()]) AliMUONTrackParam(*trackParamAtCluster);
199       trackParamAtCluster = (AliMUONTrackParam*) track.fTrackParamAtCluster->After(trackParamAtCluster);
200     }
201   }
202   
203   // copy vertex resolution square used during the tracking procedure
204   fVertexErrXY2[0] = track.fVertexErrXY2[0];
205   fVertexErrXY2[1] = track.fVertexErrXY2[1];
206   
207   // copy cluster weights matrices if any
208   if (track.fClusterWeightsNonBending) fClusterWeightsNonBending = new TMatrixD(*(track.fClusterWeightsNonBending));
209   if (track.fClusterWeightsBending) fClusterWeightsBending = new TMatrixD(*(track.fClusterWeightsBending));
210   
211   // copy track parameters at vertex if any
212   if (track.fTrackParamAtVertex) fTrackParamAtVertex = new AliMUONTrackParam(*(track.fTrackParamAtVertex));
213   
214 }
215
216   //__________________________________________________________________________
217 AliMUONTrack & AliMUONTrack::operator=(const AliMUONTrack& track)
218 {
219   /// Asignment operator
220   // check assignement to self
221   if (this == &track)
222     return *this;
223
224   // base class assignement
225   TObject::operator=(track);
226   
227   // clear memory
228   Clear();
229   
230   // necessary to make a copy of the objects and not only the pointers in TClonesArray
231   if (track.fTrackParamAtCluster) {
232     fTrackParamAtCluster = new TClonesArray("AliMUONTrackParam",10);
233     AliMUONTrackParam *trackParamAtCluster = (AliMUONTrackParam*) track.fTrackParamAtCluster->First();
234     while (trackParamAtCluster) {
235       new ((*fTrackParamAtCluster)[GetNClusters()]) AliMUONTrackParam(*trackParamAtCluster);
236       trackParamAtCluster = (AliMUONTrackParam*) track.fTrackParamAtCluster->After(trackParamAtCluster);
237     }
238   }
239   
240   // copy cluster weights matrix if any
241   if (track.fClusterWeightsNonBending) {
242     if (fClusterWeightsNonBending) {
243       fClusterWeightsNonBending->ResizeTo(*(track.fClusterWeightsNonBending));
244       *fClusterWeightsNonBending = *(track.fClusterWeightsNonBending);
245     } else fClusterWeightsNonBending = new TMatrixD(*(track.fClusterWeightsNonBending));
246   }
247   
248   // copy cluster weights matrix if any
249   if (track.fClusterWeightsBending) {
250     if (fClusterWeightsBending) {
251       fClusterWeightsBending->ResizeTo(*(track.fClusterWeightsBending));
252       *fClusterWeightsBending = *(track.fClusterWeightsBending);
253     } else fClusterWeightsBending = new TMatrixD(*(track.fClusterWeightsBending));
254   }
255   
256   // copy track parameters at vertex if any
257   if (track.fTrackParamAtVertex) {
258     if (fTrackParamAtVertex) *fTrackParamAtVertex = *(track.fTrackParamAtVertex);
259     else fTrackParamAtVertex = new AliMUONTrackParam(*(track.fTrackParamAtVertex));
260   }
261   
262   fFitWithVertex      =  track.fFitWithVertex;
263   fVertexErrXY2[0]    =  track.fVertexErrXY2[0];
264   fVertexErrXY2[1]    =  track.fVertexErrXY2[1];
265   fFitWithMCS         =  track.fFitWithMCS;
266   fGlobalChi2         =  track.fGlobalChi2;
267   fImproved           =  track.fImproved;
268   fMatchTrigger       =  track.fMatchTrigger;
269   floTrgNum           =  track.floTrgNum;
270   fChi2MatchTrigger   =  track.fChi2MatchTrigger;
271   fTrackID            =  track.fTrackID; 
272   fHitsPatternInTrigCh = track.fHitsPatternInTrigCh;
273   fLocalTrigger        = track.fLocalTrigger;
274
275   return *this;
276 }
277
278   //__________________________________________________________________________
279 AliMUONTrack::~AliMUONTrack()
280 {
281   /// Destructor
282   delete fTrackParamAtCluster;
283   delete fClusterWeightsNonBending;
284   delete fClusterWeightsBending;
285   delete fTrackParamAtVertex;
286 }
287
288   //__________________________________________________________________________
289 void AliMUONTrack::Clear(Option_t* opt)
290 {
291   /// Clear arrays
292   if (opt && opt[0] == 'C' && fTrackParamAtCluster) fTrackParamAtCluster->Clear("C");
293   else {
294     delete fTrackParamAtCluster;
295     fTrackParamAtCluster = 0x0;
296   }
297   delete fClusterWeightsNonBending; fClusterWeightsNonBending = 0x0;
298   delete fClusterWeightsBending; fClusterWeightsBending = 0x0;
299   delete fTrackParamAtVertex; fTrackParamAtVertex = 0x0;
300 }
301
302   //__________________________________________________________________________
303 TClonesArray* AliMUONTrack::GetTrackParamAtCluster() const
304 {
305   /// return array of track parameters at cluster (create it if needed)
306   if (!fTrackParamAtCluster) fTrackParamAtCluster = new TClonesArray("AliMUONTrackParam",10);
307   return fTrackParamAtCluster;
308 }
309
310   //__________________________________________________________________________
311 void AliMUONTrack::AddTrackParamAtCluster(const AliMUONTrackParam &trackParam, AliMUONVCluster &cluster, Bool_t copy)
312 {
313   /// Copy given track parameters into a new TrackParamAtCluster
314   /// Link parameters with the associated cluster
315   /// If copy=kTRUE: the cluster is copied then passed the trackParam which become its owner 
316   ///     otherwise: make sure to do not delete the cluster until it is used by the track
317   
318   // check chamber ID of the associated cluster
319   if (cluster.GetChamberId() < 0 || cluster.GetChamberId() > AliMUONConstants::NTrackingCh()) {
320     AliError(Form("Chamber ID of the associated cluster is not valid (ChamberId=%d)",cluster.GetChamberId()));
321     return;
322   }
323   
324   // check whether track parameters are given at the correct cluster z position
325   if (cluster.GetZ() != trackParam.GetZ()) {
326     AliError("track parameters are given at a different z position than the one of the associated cluster");
327     return;
328   }
329   
330   // add parameters to the array of track parameters
331   if (!fTrackParamAtCluster) fTrackParamAtCluster = new TClonesArray("AliMUONTrackParam",10);
332   AliMUONTrackParam* trackParamAtCluster = new ((*fTrackParamAtCluster)[GetNClusters()]) AliMUONTrackParam(trackParam);
333   
334   // link parameters with the associated cluster or its copy
335   if (copy) {
336     AliMUONVCluster *clusterCopy = static_cast<AliMUONVCluster*>(cluster.Clone());
337     trackParamAtCluster->SetClusterPtr(clusterCopy, kTRUE);
338   } else trackParamAtCluster->SetClusterPtr(&cluster);
339   
340   // sort the array of track parameters
341   fTrackParamAtCluster->Sort();
342 }
343
344   //__________________________________________________________________________
345 void AliMUONTrack::RemoveTrackParamAtCluster(AliMUONTrackParam *trackParam)
346 {
347   /// Remove trackParam from the array of TrackParamAtCluster
348   if (!fTrackParamAtCluster || !fTrackParamAtCluster->Remove(trackParam)) {
349     AliWarning("object to remove does not exist in array fTrackParamAtCluster");
350     return;
351   }
352   
353   fTrackParamAtCluster->Compress();
354 }
355
356   //__________________________________________________________________________
357 void AliMUONTrack::UpdateTrackParamAtCluster()
358 {
359   /// Update track parameters at each attached cluster
360   
361   if (GetNClusters() == 0) {
362     AliWarning("no cluster attached to the track");
363     return;
364   }
365   
366   AliMUONTrackParam* startingTrackParam = (AliMUONTrackParam*) fTrackParamAtCluster->First();
367   AliMUONTrackParam* trackParamAtCluster = (AliMUONTrackParam*) fTrackParamAtCluster->After(startingTrackParam);
368   while (trackParamAtCluster) {
369     
370     // reset track parameters and their covariances
371     trackParamAtCluster->SetParameters(startingTrackParam->GetParameters());
372     trackParamAtCluster->SetZ(startingTrackParam->GetZ());
373     
374     // extrapolation to the given z
375     AliMUONTrackExtrap::ExtrapToZ(trackParamAtCluster, trackParamAtCluster->GetClusterPtr()->GetZ());
376     
377     // prepare next step
378     startingTrackParam = trackParamAtCluster;
379     trackParamAtCluster = (AliMUONTrackParam*) (fTrackParamAtCluster->After(trackParamAtCluster));
380   }
381
382 }
383
384   //__________________________________________________________________________
385 void AliMUONTrack::UpdateCovTrackParamAtCluster()
386 {
387   /// Update track parameters and their covariances at each attached cluster
388   /// Include effects of multiple scattering in chambers
389   
390   if (GetNClusters() == 0) {
391     AliWarning("no cluster attached to the track");
392     return;
393   }
394   
395   AliMUONTrackParam* startingTrackParam = (AliMUONTrackParam*) fTrackParamAtCluster->First();
396   AliMUONTrackParam* trackParamAtCluster = (AliMUONTrackParam*) fTrackParamAtCluster->After(startingTrackParam);
397   Int_t expectedChamber = startingTrackParam->GetClusterPtr()->GetChamberId() + 1;
398   Int_t currentChamber;
399   while (trackParamAtCluster) {
400     
401     // reset track parameters and their covariances
402     trackParamAtCluster->SetParameters(startingTrackParam->GetParameters());
403     trackParamAtCluster->SetZ(startingTrackParam->GetZ());
404     trackParamAtCluster->SetCovariances(startingTrackParam->GetCovariances());
405     
406     // add MCS effect
407     AliMUONTrackExtrap::AddMCSEffect(trackParamAtCluster,AliMUONConstants::ChamberThicknessInX0(),1.);
408     
409     // add MCS in missing chambers if any
410     currentChamber = trackParamAtCluster->GetClusterPtr()->GetChamberId();
411     while (currentChamber > expectedChamber) {
412       // extrapolation to the missing chamber
413       AliMUONTrackExtrap::ExtrapToZCov(trackParamAtCluster, AliMUONConstants::DefaultChamberZ(expectedChamber));
414       // add MCS effect
415       AliMUONTrackExtrap::AddMCSEffect(trackParamAtCluster,AliMUONConstants::ChamberThicknessInX0(),1.);
416       expectedChamber++;
417     }
418     
419     // extrapolation to the z of the current cluster
420     AliMUONTrackExtrap::ExtrapToZCov(trackParamAtCluster, trackParamAtCluster->GetClusterPtr()->GetZ());
421     
422     // prepare next step
423     expectedChamber = currentChamber + 1;
424     startingTrackParam = trackParamAtCluster;
425     trackParamAtCluster = (AliMUONTrackParam*) (fTrackParamAtCluster->After(trackParamAtCluster));
426   }
427   
428 }
429
430   //__________________________________________________________________________
431 Bool_t AliMUONTrack::IsValid()
432 {
433   /// check the validity of the current track (at least one cluster per requested station)
434   
435   Int_t nClusters = GetNClusters();
436   AliMUONTrackParam *trackParam;
437   Int_t currentStation = 0, expectedStation = 0;
438   
439   for (Int_t i = 0; i < nClusters; i++) {
440     trackParam = (AliMUONTrackParam*) fTrackParamAtCluster->UncheckedAt(i);
441     
442     // skip unrequested stations
443     while (expectedStation < AliMUONConstants::NTrackingSt() &&
444            !AliMUONReconstructor::GetRecoParam()->RequestStation(expectedStation)) expectedStation++;
445     
446     currentStation = trackParam->GetClusterPtr()->GetChamberId()/2;
447     
448     // missing station
449     if (currentStation > expectedStation) return kFALSE;
450     
451     // found station --> look for next one
452     if (currentStation == expectedStation) expectedStation++;
453     
454   }
455   
456   return expectedStation == AliMUONConstants::NTrackingSt();
457   
458 }
459
460   //__________________________________________________________________________
461 void AliMUONTrack::TagRemovableClusters() {
462   /// Identify clusters that can be removed from the track,
463   /// with the only requirement to have at least 1 cluster per requested station
464   
465   Int_t nClusters = GetNClusters();
466   AliMUONTrackParam *trackParam, *nextTrackParam;
467   Int_t currentCh, nextCh;
468   
469   // reset flags to kFALSE for all clusters in required station
470   for (Int_t i = 0; i < nClusters; i++) {
471     trackParam = (AliMUONTrackParam*) fTrackParamAtCluster->UncheckedAt(i);
472     if (AliMUONReconstructor::GetRecoParam()->RequestStation(trackParam->GetClusterPtr()->GetChamberId()/2))
473       trackParam->SetRemovable(kFALSE);
474     else trackParam->SetRemovable(kTRUE);
475   }
476   
477   // loop over track parameters
478   for (Int_t i = 0; i < nClusters; i++) {
479     trackParam = (AliMUONTrackParam*) fTrackParamAtCluster->UncheckedAt(i);
480     
481     currentCh = trackParam->GetClusterPtr()->GetChamberId();
482     
483     // loop over next track parameters
484     for (Int_t j = i+1; j < nClusters; j++) {
485       nextTrackParam = (AliMUONTrackParam*) fTrackParamAtCluster->UncheckedAt(j);
486       
487       nextCh = nextTrackParam->GetClusterPtr()->GetChamberId();
488       
489       // check if the 2 clusters are on the same station
490       if (nextCh/2 != currentCh/2) break;
491       
492       // set clusters in the same station as being removable
493       trackParam->SetRemovable(kTRUE);
494       nextTrackParam->SetRemovable(kTRUE);
495       
496     }
497     
498   }
499     
500 }
501
502   //__________________________________________________________________________
503 Bool_t AliMUONTrack::ComputeLocalChi2(Bool_t accountForMCS)
504 {
505   /// Compute each cluster contribution to the chi2 of the track
506   /// accounting for multiple scattering or not according to the flag
507   /// - Also recompute the weight matrices of the attached clusters if accountForMCS=kTRUE
508   /// - Assume that track parameters at each cluster are corrects
509   /// - Return kFALSE if computation failed
510   AliDebug(1,"Enter ComputeLocalChi2");
511   
512   if (!fTrackParamAtCluster) {
513     AliWarning("no cluster attached to this track");
514     return kFALSE;
515   }
516   
517   if (accountForMCS) { // Compute local chi2 taking into account multiple scattering effects
518       
519     // Compute MCS covariance matrix only once
520     Int_t nClusters = GetNClusters();
521     TMatrixD mcsCovariances(nClusters,nClusters);
522     ComputeMCSCovariances(mcsCovariances);
523     
524     // Make sure cluster weights are consistent with following calculations
525     if (!ComputeClusterWeights(&mcsCovariances)) {
526       AliWarning("cannot take into account the multiple scattering effects");
527       return ComputeLocalChi2(kFALSE);
528     }
529     
530     // Compute chi2 of the track
531     Double_t globalChi2 = ComputeGlobalChi2(kTRUE);
532     if (globalChi2 < 0.) return kFALSE;
533     
534     // Loop over removable clusters and compute their local chi2
535     AliMUONTrackParam* trackParamAtCluster1;
536     AliMUONVCluster *cluster, *discardedCluster;
537     Int_t iCluster1, iCluster2, iCurrentCluster1, iCurrentCluster2;
538     TMatrixD clusterWeightsNB(nClusters-1,nClusters-1);
539     TMatrixD clusterWeightsB(nClusters-1,nClusters-1);
540     Double_t *dX = new Double_t[nClusters-1];
541     Double_t *dY = new Double_t[nClusters-1];
542     Double_t globalChi2b;
543     AliMUONTrackParam* trackParamAtCluster = (AliMUONTrackParam*) fTrackParamAtCluster->First();
544     while (trackParamAtCluster) {
545       
546       discardedCluster = trackParamAtCluster->GetClusterPtr();
547       
548       // Recompute cluster weights without the current cluster
549       if (!ComputeClusterWeights(clusterWeightsNB, clusterWeightsB, &mcsCovariances, discardedCluster)) {
550         AliWarning("cannot take into account the multiple scattering effects");
551         delete [] dX;
552         delete [] dY;
553         return ComputeLocalChi2(kFALSE);
554       }
555       
556       // Compute track chi2 without the current cluster
557       globalChi2b = 0.;
558       iCurrentCluster1 = 0;
559       for (iCluster1 = 0; iCluster1 < nClusters ; iCluster1++) { 
560         trackParamAtCluster1 = (AliMUONTrackParam*) fTrackParamAtCluster->UncheckedAt(iCluster1);
561         cluster = trackParamAtCluster1->GetClusterPtr();
562         
563         if (cluster == discardedCluster) continue;
564         
565         // Compute and save residuals
566         dX[iCurrentCluster1] = cluster->GetX() - trackParamAtCluster1->GetNonBendingCoor();
567         dY[iCurrentCluster1] = cluster->GetY() - trackParamAtCluster1->GetBendingCoor();
568         
569         iCurrentCluster2 = 0;
570         for (iCluster2 = 0; iCluster2 < iCluster1; iCluster2++) {
571           cluster = ((AliMUONTrackParam*) fTrackParamAtCluster->UncheckedAt(iCluster2))->GetClusterPtr();
572           
573           if (cluster == discardedCluster) continue;
574           
575           // Add contribution from covariances
576           globalChi2b += (clusterWeightsNB(iCurrentCluster1, iCurrentCluster2) +
577                           clusterWeightsNB(iCurrentCluster2, iCurrentCluster1)) * dX[iCurrentCluster1] * dX[iCurrentCluster2] +
578                          (clusterWeightsB(iCurrentCluster1, iCurrentCluster2) +
579                           clusterWeightsB(iCurrentCluster2, iCurrentCluster1)) * dY[iCurrentCluster1] * dY[iCurrentCluster2];
580           
581           iCurrentCluster2++;
582         }
583         
584         // Add contribution from variances
585         globalChi2b += clusterWeightsNB(iCurrentCluster1, iCurrentCluster1) * dX[iCurrentCluster1] * dX[iCurrentCluster1] +
586                        clusterWeightsB(iCurrentCluster1, iCurrentCluster1) * dY[iCurrentCluster1] * dY[iCurrentCluster1];
587         
588         iCurrentCluster1++;
589       }
590
591       // Set local chi2
592       trackParamAtCluster->SetLocalChi2(globalChi2 - globalChi2b);
593       
594       trackParamAtCluster = (AliMUONTrackParam*) fTrackParamAtCluster->After(trackParamAtCluster);
595     }
596     
597     delete [] dX;
598     delete [] dY;
599     
600   } else { // without multiple scattering effects
601     
602     AliMUONVCluster *discardedCluster;
603     Double_t dX, dY;
604     AliMUONTrackParam* trackParamAtCluster = (AliMUONTrackParam*) fTrackParamAtCluster->First();
605     while (trackParamAtCluster) {
606       
607       discardedCluster = trackParamAtCluster->GetClusterPtr();
608       
609       // Compute residuals
610       dX = discardedCluster->GetX() - trackParamAtCluster->GetNonBendingCoor();
611       dY = discardedCluster->GetY() - trackParamAtCluster->GetBendingCoor();
612       
613       // Set local chi2
614       trackParamAtCluster->SetLocalChi2(dX * dX / discardedCluster->GetErrX2() + dY * dY / discardedCluster->GetErrY2());
615     
616       trackParamAtCluster = (AliMUONTrackParam*) fTrackParamAtCluster->After(trackParamAtCluster);
617     }
618   
619   }
620   
621   return kTRUE;
622   
623 }
624
625   //__________________________________________________________________________
626 Double_t AliMUONTrack::ComputeGlobalChi2(Bool_t accountForMCS)
627 {
628   /// Compute the chi2 of the track accounting for multiple scattering or not according to the flag
629   /// - Assume that track parameters at each cluster are corrects
630   /// - Assume the cluster weights matrices are corrects
631   /// - Return negative value if chi2 computation failed
632   AliDebug(1,"Enter ComputeGlobalChi2");
633   
634   if (!fTrackParamAtCluster) {
635     AliWarning("no cluster attached to this track");
636     return 1.e10;
637   }
638   
639   Double_t chi2 = 0.;
640   
641   if (accountForMCS) {
642     
643     // Check the weight matrices. If weight matrices are not available compute chi2 without MCS
644     if (!fClusterWeightsNonBending || !fClusterWeightsBending) {
645       AliWarning("cluster weights including multiple scattering effects are not available\n\t\t --> compute chi2 WITHOUT multiple scattering");
646       return ComputeGlobalChi2(kFALSE);
647     }
648     Int_t nClusters = GetNClusters();
649     if (fClusterWeightsNonBending->GetNrows() != nClusters || fClusterWeightsBending->GetNcols() != nClusters) {
650       AliWarning("cluster weights including multiple scattering effects are not available\n\t\t --> compute chi2 WITHOUT multiple scattering");
651       return ComputeGlobalChi2(kFALSE);
652     }
653     
654     // Compute chi2
655     AliMUONVCluster *cluster;
656     Double_t *dX = new Double_t[nClusters];
657     Double_t *dY = new Double_t[nClusters];
658     AliMUONTrackParam* trackParamAtCluster;
659     for (Int_t iCluster1 = 0; iCluster1 < nClusters; iCluster1++) { 
660       trackParamAtCluster = (AliMUONTrackParam*) fTrackParamAtCluster->UncheckedAt(iCluster1);
661       cluster = trackParamAtCluster->GetClusterPtr();
662       dX[iCluster1] = cluster->GetX() - trackParamAtCluster->GetNonBendingCoor();
663       dY[iCluster1] = cluster->GetY() - trackParamAtCluster->GetBendingCoor();
664       for (Int_t iCluster2 = 0; iCluster2 < iCluster1; iCluster2++) {
665         chi2 += ((*fClusterWeightsNonBending)(iCluster1, iCluster2) + (*fClusterWeightsNonBending)(iCluster2, iCluster1)) * dX[iCluster1] * dX[iCluster2] +
666                 ((*fClusterWeightsBending)(iCluster1, iCluster2) + (*fClusterWeightsBending)(iCluster2, iCluster1)) * dY[iCluster1] * dY[iCluster2];
667       }
668       chi2 += ((*fClusterWeightsNonBending)(iCluster1, iCluster1) * dX[iCluster1] * dX[iCluster1]) +
669               ((*fClusterWeightsBending)(iCluster1, iCluster1) * dY[iCluster1] * dY[iCluster1]);
670     }
671     delete [] dX;
672     delete [] dY;
673     
674   } else {
675     
676     AliMUONVCluster *cluster;
677     Double_t dX, dY;
678     AliMUONTrackParam* trackParamAtCluster;
679     Int_t nClusters = GetNClusters();
680     for (Int_t iCluster = 0; iCluster < nClusters ; iCluster++) { 
681       trackParamAtCluster = (AliMUONTrackParam*) fTrackParamAtCluster->UncheckedAt(iCluster);
682       cluster = trackParamAtCluster->GetClusterPtr();
683       dX = cluster->GetX() - trackParamAtCluster->GetNonBendingCoor();
684       dY = cluster->GetY() - trackParamAtCluster->GetBendingCoor();
685       chi2 += dX * dX / cluster->GetErrX2() + dY * dY / cluster->GetErrY2();
686     }
687     
688   }
689   
690   return chi2;
691   
692 }
693
694   //__________________________________________________________________________
695 Bool_t AliMUONTrack::ComputeClusterWeights(TMatrixD* mcsCovariances)
696 {
697   /// Compute the weight matrices of the attached clusters, in non bending and bending direction,
698   /// accounting for multiple scattering correlations and cluster resolution
699   /// - Use the provided MCS covariance matrix if any (otherwise build it temporarily)
700   /// - Assume that track parameters at each cluster are corrects
701   /// - Return kFALSE if computation failed
702   AliDebug(1,"Enter ComputeClusterWeights1");
703   
704   if (!fTrackParamAtCluster) {
705     AliWarning("no cluster attached to this track");
706     return kFALSE;
707   }
708   
709   // Alocate memory
710   Int_t nClusters = GetNClusters();
711   if (!fClusterWeightsNonBending) fClusterWeightsNonBending = new TMatrixD(nClusters,nClusters);
712   if (!fClusterWeightsBending) fClusterWeightsBending = new TMatrixD(nClusters,nClusters);
713   
714   // Compute weights matrices
715   if (!ComputeClusterWeights(*fClusterWeightsNonBending, *fClusterWeightsBending, mcsCovariances)) return kFALSE;
716   
717   return kTRUE;
718   
719 }
720
721   //__________________________________________________________________________
722 Bool_t AliMUONTrack::ComputeClusterWeights(TMatrixD& clusterWeightsNB, TMatrixD& clusterWeightsB,
723                                            TMatrixD* mcsCovariances, AliMUONVCluster* discardedCluster) const
724 {
725   /// Compute the weight matrices, in non bending and bending direction,
726   /// of the other attached clusters assuming the discarded one does not exist
727   /// accounting for multiple scattering correlations and cluster resolution
728   /// - Use the provided MCS covariance matrix if any (otherwise build it temporarily)
729   /// - Return kFALSE if computation failed
730   AliDebug(1,"Enter ComputeClusterWeights2");
731   
732   // Check MCS covariance matrix and recompute it if need
733   Int_t nClusters = GetNClusters();
734   Bool_t deleteMCSCov = kFALSE;
735   if (!mcsCovariances) {
736     mcsCovariances = new TMatrixD(nClusters,nClusters);
737     deleteMCSCov = kTRUE;
738     ComputeMCSCovariances(*mcsCovariances);
739   }
740   
741   // Resize the weights matrices; alocate memory
742   if (discardedCluster) {
743     clusterWeightsNB.ResizeTo(nClusters-1,nClusters-1);
744     clusterWeightsB.ResizeTo(nClusters-1,nClusters-1);
745   } else {
746     clusterWeightsNB.ResizeTo(nClusters,nClusters);
747     clusterWeightsB.ResizeTo(nClusters,nClusters);
748   }
749   
750   // Define variables
751   AliMUONVCluster *cluster1, *cluster2;
752   Int_t iCurrentCluster1, iCurrentCluster2;
753   
754   // Compute the covariance matrices
755   iCurrentCluster1 = 0;
756   for (Int_t iCluster1 = 0; iCluster1 < nClusters; iCluster1++) { 
757     cluster1 = ((AliMUONTrackParam*) fTrackParamAtCluster->UncheckedAt(iCluster1))->GetClusterPtr();
758     
759     if (cluster1 == discardedCluster) continue;
760     
761     // Loop over next clusters
762     iCurrentCluster2 = iCurrentCluster1;
763     for (Int_t iCluster2 = iCluster1; iCluster2 < nClusters; iCluster2++) {
764       cluster2 = ((AliMUONTrackParam*) fTrackParamAtCluster->UncheckedAt(iCluster2))->GetClusterPtr();
765       
766       if (cluster2 == discardedCluster) continue;
767       
768       // Fill with MCS covariances
769       clusterWeightsNB(iCurrentCluster1, iCurrentCluster2) = (*mcsCovariances)(iCluster1,iCluster2);
770       
771       // Equal contribution from multiple scattering in non bending and bending directions
772       clusterWeightsB(iCurrentCluster1, iCurrentCluster2) = clusterWeightsNB(iCurrentCluster1, iCurrentCluster2);
773       
774       // Add contribution from cluster resolution to diagonal element and symmetrize the matrix
775       if (iCurrentCluster1 == iCurrentCluster2) {
776         
777         // In non bending plane
778         clusterWeightsNB(iCurrentCluster1, iCurrentCluster1) += cluster1->GetErrX2();
779         // In bending plane
780         clusterWeightsB(iCurrentCluster1, iCurrentCluster1) += cluster1->GetErrY2();
781         
782       } else {
783         
784         // In non bending plane
785         clusterWeightsNB(iCurrentCluster2, iCurrentCluster1) = clusterWeightsNB(iCurrentCluster1, iCurrentCluster2);
786         // In bending plane
787         clusterWeightsB(iCurrentCluster2, iCurrentCluster1) = clusterWeightsB(iCurrentCluster1, iCurrentCluster2);
788         
789       }
790       
791       iCurrentCluster2++;
792     }
793     
794     iCurrentCluster1++;
795   }
796     
797   // Inversion of covariance matrices to get the weights
798   if (clusterWeightsNB.Determinant() != 0 && clusterWeightsB.Determinant() != 0) {
799     clusterWeightsNB.Invert();
800     clusterWeightsB.Invert();
801   } else {
802     AliWarning(" Determinant = 0");
803     clusterWeightsNB.ResizeTo(0,0);
804     clusterWeightsB.ResizeTo(0,0);
805     if(deleteMCSCov) delete mcsCovariances;
806     return kFALSE;
807   }
808   
809   if(deleteMCSCov) delete mcsCovariances;
810   
811   return kTRUE;
812   
813 }
814
815   //__________________________________________________________________________
816 void AliMUONTrack::ComputeMCSCovariances(TMatrixD& mcsCovariances) const
817 {
818   /// Compute the multiple scattering covariance matrix
819   /// (assume that track parameters at each cluster are corrects)
820   AliDebug(1,"Enter ComputeMCSCovariances");
821   
822   // Reset the size of the covariance matrix if needed
823   Int_t nClusters = GetNClusters();
824   if (mcsCovariances.GetNrows() != nClusters) mcsCovariances.ResizeTo(nClusters,nClusters);
825   
826   // Define variables
827   Int_t nChambers = AliMUONConstants::NTrackingCh();
828   AliMUONTrackParam* trackParamAtCluster;
829   AliMUONTrackParam extrapTrackParam;
830   Int_t currentChamber = 0, expectedChamber = 0, size = 0;
831   Double_t *mcsAngle2 = new Double_t[2*nChambers];
832   Double_t *zMCS = new Double_t[2*nChambers];
833   Int_t *indices = new Int_t[2*nClusters];
834   
835   // Compute multiple scattering dispersion angle at each chamber
836   // and save the z position where it is calculated
837   for (Int_t iCluster = 0; iCluster < nClusters; iCluster++) {
838     trackParamAtCluster = (AliMUONTrackParam*) fTrackParamAtCluster->UncheckedAt(iCluster);
839     
840     // look for missing chambers if any
841     currentChamber = trackParamAtCluster->GetClusterPtr()->GetChamberId();
842     while (currentChamber > expectedChamber) {
843       
844       // Save the z position where MCS dispersion is calculated
845       zMCS[size] = AliMUONConstants::DefaultChamberZ(expectedChamber);
846       
847       // Do not take into account MCS in chambers prior the first cluster
848       if (iCluster > 0) {
849         
850         // Get track parameters at missing chamber z
851         extrapTrackParam = *trackParamAtCluster;
852         AliMUONTrackExtrap::ExtrapToZ(&extrapTrackParam, zMCS[size]);
853         
854         // Save multiple scattering dispersion angle in missing chamber
855         mcsAngle2[size] = AliMUONTrackExtrap::GetMCSAngle2(extrapTrackParam,AliMUONConstants::ChamberThicknessInX0(),1.);
856         
857       } else mcsAngle2[size] = 0.;
858       
859       expectedChamber++;
860       size++;
861     }
862     
863     // Save z position where MCS dispersion is calculated
864     zMCS[size] = trackParamAtCluster->GetZ();
865     
866     // Save multiple scattering dispersion angle in current chamber
867     mcsAngle2[size] = AliMUONTrackExtrap::GetMCSAngle2(*trackParamAtCluster,AliMUONConstants::ChamberThicknessInX0(),1.);
868     
869     // Save indice in zMCS array corresponding to the current cluster
870     indices[iCluster] = size;
871     
872     expectedChamber = currentChamber + 1;
873     size++;
874   }
875   
876   // complete array of z if last cluster is on the last but one chamber
877   if (currentChamber != nChambers-1) zMCS[size++] = AliMUONConstants::DefaultChamberZ(nChambers-1);
878   
879   // Compute the covariance matrix
880   for (Int_t iCluster1 = 0; iCluster1 < nClusters; iCluster1++) { 
881     
882     for (Int_t iCluster2 = iCluster1; iCluster2 < nClusters; iCluster2++) {
883       
884       // Initialization to 0 (diagonal plus upper triangular part)
885       mcsCovariances(iCluster1,iCluster2) = 0.;
886       
887       // Compute contribution from multiple scattering in upstream chambers
888       for (Int_t k = 0; k < indices[iCluster1]; k++) {  
889         mcsCovariances(iCluster1,iCluster2) += (zMCS[indices[iCluster1]] - zMCS[k]) * (zMCS[indices[iCluster2]] - zMCS[k]) * mcsAngle2[k];
890       }
891       
892       // Symetrize the matrix
893       mcsCovariances(iCluster2,iCluster1) = mcsCovariances(iCluster1,iCluster2);
894     }
895     
896   }
897     
898   delete [] mcsAngle2;
899   delete [] zMCS;
900   delete [] indices;
901   
902 }
903
904   //__________________________________________________________________________
905 Int_t AliMUONTrack::ClustersInCommon(AliMUONTrack* track) const
906 {
907   /// Returns the number of clusters in common between the current track ("this")
908   /// and the track pointed to by "track".
909   if (!fTrackParamAtCluster || !this->fTrackParamAtCluster) return 0;
910   Int_t clustersInCommon = 0;
911   AliMUONTrackParam *trackParamAtCluster1, *trackParamAtCluster2;
912   // Loop over clusters of first track
913   trackParamAtCluster1 = (AliMUONTrackParam*) this->fTrackParamAtCluster->First();
914   while (trackParamAtCluster1) {
915     // Loop over clusters of second track
916     trackParamAtCluster2 = (AliMUONTrackParam*) track->fTrackParamAtCluster->First();
917     while (trackParamAtCluster2) {
918       // Increment "clustersInCommon" if both trackParamAtCluster1 & 2 point to the same cluster
919       if ((trackParamAtCluster1->GetClusterPtr()) == (trackParamAtCluster2->GetClusterPtr())) {
920         clustersInCommon++;
921         break;
922       }
923       trackParamAtCluster2 = (AliMUONTrackParam*) track->fTrackParamAtCluster->After(trackParamAtCluster2);
924     } // trackParamAtCluster2
925     trackParamAtCluster1 = (AliMUONTrackParam*) this->fTrackParamAtCluster->After(trackParamAtCluster1);
926   } // trackParamAtCluster1
927   return clustersInCommon;
928 }
929
930   //__________________________________________________________________________
931 Double_t AliMUONTrack::GetNormalizedChi2() const
932 {
933   /// return the chi2 value divided by the number of degrees of freedom (or 1.e10 if ndf < 0)
934   
935   Double_t numberOfDegFree = (2. * GetNClusters() - 5.);
936   if (numberOfDegFree > 0.) return fGlobalChi2 / numberOfDegFree;
937   else return fGlobalChi2; // system is under-constraint
938 }
939
940   //__________________________________________________________________________
941 Bool_t* AliMUONTrack::CompatibleTrack(AliMUONTrack *track, Double_t sigmaCut) const
942 {
943   /// for each chamber: return kTRUE (kFALSE) if clusters are compatible (not compatible)
944   AliMUONTrackParam *trackParamAtCluster1, *trackParamAtCluster2;
945   AliMUONVCluster *cluster1, *cluster2;
946   Double_t chi2, dX, dY, dZ;
947   Double_t chi2Max = sigmaCut * sigmaCut;
948   Double_t dZMax = 1.; // 1 cm
949   
950   Bool_t *compatibleCluster = new Bool_t[AliMUONConstants::NTrackingCh()]; 
951   for ( Int_t ch = 0; ch < AliMUONConstants::NTrackingCh(); ch++) compatibleCluster[ch] = kFALSE;
952
953   if (!fTrackParamAtCluster || !this->fTrackParamAtCluster) return compatibleCluster;
954   
955   // Loop over clusters of first track
956   trackParamAtCluster1 = (AliMUONTrackParam*) this->fTrackParamAtCluster->First();
957   while (trackParamAtCluster1) {
958     
959     cluster1 = trackParamAtCluster1->GetClusterPtr();
960     
961     // Loop over clusters of second track
962     trackParamAtCluster2 = (AliMUONTrackParam*) track->fTrackParamAtCluster->First();
963     while (trackParamAtCluster2) {
964       
965       cluster2 = trackParamAtCluster2->GetClusterPtr();
966       
967       //prepare next step
968       trackParamAtCluster2 = (AliMUONTrackParam*) track->fTrackParamAtCluster->After(trackParamAtCluster2);
969       
970       // z direction
971       dZ = cluster1->GetZ() - cluster2->GetZ();
972       if (dZ > dZMax) continue;
973       
974       // non bending direction
975       dX = cluster1->GetX() - cluster2->GetX();
976       chi2 = dX * dX / (cluster1->GetErrX2() + cluster2->GetErrX2());
977       if (chi2 > chi2Max) continue;
978       
979       // bending direction
980       dY = cluster1->GetY() - cluster2->GetY();
981       chi2 = dY * dY / (cluster1->GetErrY2() + cluster2->GetErrY2());
982       if (chi2 > chi2Max) continue;
983       
984       compatibleCluster[cluster1->GetChamberId()] = kTRUE;
985       break;
986     }
987     
988     trackParamAtCluster1 = (AliMUONTrackParam*) this->fTrackParamAtCluster->After(trackParamAtCluster1);
989   }
990   
991   return compatibleCluster;
992 }
993
994 //__________________________________________________________________________
995 void AliMUONTrack::SetTrackParamAtVertex(const AliMUONTrackParam* trackParam)
996 {
997   /// set track parameters at vertex
998   if (trackParam == 0x0) return;
999   if (fTrackParamAtVertex) *fTrackParamAtVertex = *trackParam;
1000   else fTrackParamAtVertex = new AliMUONTrackParam(*trackParam);
1001 }
1002
1003 //__________________________________________________________________________
1004 void AliMUONTrack::RecursiveDump() const
1005 {
1006   /// Recursive dump of AliMUONTrack, i.e. with dump of trackParamAtCluster and attached clusters
1007   AliMUONTrackParam *trackParamAtCluster;
1008   AliMUONVCluster *cluster;
1009   cout << "Recursive dump of Track: " << this << endl;
1010   // Track
1011   this->Dump();
1012   for (Int_t iCluster = 0; iCluster < GetNClusters(); iCluster++) {
1013     trackParamAtCluster = (AliMUONTrackParam*) ((*fTrackParamAtCluster)[iCluster]);
1014     // trackParamAtCluster
1015     cout << "trackParamAtCluster: " << trackParamAtCluster << " (index: " << iCluster << ")" << endl;
1016     trackParamAtCluster->Dump();
1017     cluster = trackParamAtCluster->GetClusterPtr();
1018     // cluster
1019     cout << "cluster: " << cluster << endl;
1020     cluster->Print();
1021   }
1022   return;
1023 }
1024   
1025 //_____________________________________________-
1026 void AliMUONTrack::Print(Option_t*) const
1027 {
1028   /// Printing Track information 
1029
1030   cout << "<AliMUONTrack> No.Clusters=" << setw(2)   << GetNClusters() << 
1031       ", Match2Trig=" << setw(1) << GetMatchTrigger()  << 
1032       ", LoTrgNum=" << setw(3) << GetLoTrgNum()  << 
1033     ", Chi2-tracking-trigger=" << setw(8) << setprecision(5) <<  GetChi2MatchTrigger();
1034   cout << Form(" HitTriggerPattern %x",fHitsPatternInTrigCh) << endl;
1035   if (fTrackParamAtCluster) fTrackParamAtCluster->First()->Print("FULL");
1036 }
1037
1038 //__________________________________________________________________________
1039 void AliMUONTrack::SetLocalTrigger(Int_t loCirc, Int_t loStripX, Int_t loStripY, Int_t loDev, Int_t loLpt, Int_t loHpt)
1040 {
1041   /// pack the local trigger information and store
1042
1043   if (loCirc < 0) return;
1044
1045   fLocalTrigger = 0;
1046   fLocalTrigger += loCirc;
1047   fLocalTrigger += loStripX << 8;
1048   fLocalTrigger += loStripY << 13;
1049   fLocalTrigger += loDev    << 17;
1050   fLocalTrigger += loLpt    << 22;
1051   fLocalTrigger += loHpt    << 24;
1052
1053 }
1054