]> git.uio.no Git - u/mrichter/AliRoot.git/blob - MUON/AliMUONTrackReconstructor.cxx
Fixing compiler warnings
[u/mrichter/AliRoot.git] / MUON / AliMUONTrackReconstructor.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 AliMUONTrackReconstructor
20 /// MUON track reconstructor using the original method
21 ///
22 /// This class contains as data:
23 /// - the parameters for the track reconstruction
24 ///
25 /// It contains as methods, among others:
26 /// - MakeTracks to build the tracks
27 //-----------------------------------------------------------------------------
28
29 #include "AliMUONTrackReconstructor.h"
30
31 #include "AliMUONConstants.h"
32 #include "AliMUONVCluster.h"
33 #include "AliMUONVClusterServer.h"
34 #include "AliMUONVClusterStore.h"
35 #include "AliMUONTrack.h"
36 #include "AliMUONTrackParam.h"
37 #include "AliMUONTrackExtrap.h"
38 #include "AliMUONRecoParam.h"
39
40 #include "AliMpArea.h"
41
42 #include "AliLog.h"
43
44 #include <TMinuit.h>
45 #include <Riostream.h>
46 #include <TMath.h>
47 #include <TMatrixD.h>
48
49 // Functions to be minimized with Minuit
50 void TrackChi2(Int_t &nParam, Double_t *gradient, Double_t &chi2, Double_t *param, Int_t flag);
51
52 /// \cond CLASSIMP
53 ClassImp(AliMUONTrackReconstructor) // Class implementation in ROOT context
54 /// \endcond
55
56   //__________________________________________________________________________
57 AliMUONTrackReconstructor::AliMUONTrackReconstructor(const AliMUONRecoParam* recoParam, AliMUONVClusterServer* clusterServer)
58   : AliMUONVTrackReconstructor(recoParam,clusterServer)
59 {
60   /// Constructor
61 }
62
63   //__________________________________________________________________________
64 AliMUONTrackReconstructor::~AliMUONTrackReconstructor()
65 {
66 /// Destructor
67
68
69   //__________________________________________________________________________
70 Bool_t AliMUONTrackReconstructor::MakeTrackCandidates(AliMUONVClusterStore& clusterStore)
71 {
72   /// To make track candidates (assuming linear propagation if the flag fgkMakeTrackCandidatesFast is set to kTRUE):
73   /// Start with segments station(1..) 4 or 5 then follow track in station 5 or 4.
74   /// Good candidates are made of at least three clusters.
75   /// Keep only best candidates or all of them according to the flag fgkTrackAllTracks.
76   
77   TClonesArray *segments;
78   AliMUONTrack *track;
79   Int_t iCandidate = 0;
80   Bool_t clusterFound;
81
82   AliDebug(1,"Enter MakeTrackCandidates");
83
84   // Unless we're doing combined tracking, we'll clusterize all stations at once
85   Int_t firstChamber(0);
86   Int_t lastChamber(9);
87   
88   if (GetRecoParam()->CombineClusterTrackReco()) {
89     // ... Here's the exception : ask the clustering to reconstruct
90     // clusters *only* in station 4 and 5 for combined tracking
91     firstChamber = 6;
92   }
93
94   for (Int_t i = firstChamber; i <= lastChamber; ++i ) 
95   {
96     if (fClusterServer && GetRecoParam()->UseChamber(i)) fClusterServer->Clusterize(i, clusterStore, AliMpArea(), GetRecoParam());
97   }
98   
99   // Loop over stations(1..) 5 and 4 and make track candidates
100   for (Int_t istat=4; istat>=3; istat--) {
101     
102     // Make segments in the station
103     segments = MakeSegmentsBetweenChambers(clusterStore, 2*istat, 2*istat+1);
104     
105     // Loop over segments
106     for (Int_t iseg=0; iseg<segments->GetEntriesFast(); iseg++) 
107     {
108       AliDebug(1,Form("Making primary candidate(1..) %d",++iCandidate));
109       
110       // Transform segments to tracks and put them at the end of fRecTracksPtr
111       track = new ((*fRecTracksPtr)[fRecTracksPtr->GetLast()+1]) AliMUONTrack((AliMUONObjectPair*)((*segments)[iseg]),GetRecoParam()->GetBendingVertexDispersion());
112       fNRecTracks++;
113       
114       // Look for compatible cluster(s) in the other station
115       if (GetRecoParam()->MakeTrackCandidatesFast())
116         clusterFound = FollowLinearTrackInStation(*track, clusterStore, 7-istat);
117       else clusterFound = FollowTrackInStation(*track, clusterStore, 7-istat);
118       
119       // Remove track if no cluster found on a requested station
120       // or abort tracking if there are too many candidates
121       if (GetRecoParam()->RequestStation(7-istat)) {
122         if (!clusterFound) {
123           fRecTracksPtr->Remove(track);
124           fNRecTracks--;
125         } else if (fNRecTracks > GetRecoParam()->GetMaxTrackCandidates()) {
126           AliError(Form("Too many track candidates (%d tracks). Abort tracking.", fNRecTracks));
127           delete segments;
128           return kFALSE;
129         }
130       } else {
131         if ((fNRecTracks + segments->GetEntriesFast() - iseg - 1) > GetRecoParam()->GetMaxTrackCandidates()) {
132           AliError(Form("Too many track candidates (%d tracks). Abort tracking.", fNRecTracks + segments->GetEntriesFast() - iseg - 1));
133           delete segments;
134           return kFALSE;
135         }
136       }
137       
138     }
139     
140     // delete the array of segments
141     delete segments;
142   }
143   
144   // Keep all different tracks or only the best ones as required
145   if (GetRecoParam()->TrackAllTracks()) RemoveIdenticalTracks();
146   else RemoveDoubleTracks();
147   
148   AliDebug(1,Form("Number of good candidates = %d",fNRecTracks));
149   
150   return kTRUE;
151   
152 }
153
154   //__________________________________________________________________________
155 Bool_t AliMUONTrackReconstructor::MakeMoreTrackCandidates(AliMUONVClusterStore& clusterStore)
156 {
157   /// To make extra track candidates (assuming linear propagation if the flag fgkMakeTrackCandidatesFast is set to kTRUE):
158   /// clustering is supposed to be already done
159   /// Start with segments made of 1 cluster in each of the stations 4 and 5 then follow track in remaining chambers.
160   /// Good candidates are made of at least three clusters if both station are requested (two otherwise).
161   /// Keep only best candidates or all of them according to the flag fgkTrackAllTracks.
162   
163   TClonesArray *segments;
164   AliMUONObjectPair *segment;
165   AliMUONTrack *track;
166   Int_t iCandidate = 0, iCurrentTrack, nCurrentTracks;
167   Bool_t clusterFound;
168   
169   AliDebug(1,"Enter MakeMoreTrackCandidates");
170   
171   // Double loop over chambers in stations(1..) 4 and 5 to make track candidates
172   for (Int_t ich1 = 6; ich1 <= 7; ich1++) {
173     for (Int_t ich2 = 8; ich2 <= 9; ich2++) {
174       
175       // Make segments in the station
176       segments = MakeSegmentsBetweenChambers(clusterStore, ich1, ich2);
177       
178       /// Remove segments already attached to a track
179       RemoveUsedSegments(*segments);
180       
181       // Loop over segments
182       for (Int_t iSegment=0; iSegment<segments->GetEntriesFast(); iSegment++)
183       {
184         AliDebug(1,Form("Making primary candidate(1..) %d",++iCandidate));
185         segment = (AliMUONObjectPair*) segments->UncheckedAt(iSegment);
186         
187         // Transform segments to tracks and put them at the end of fRecTracksPtr
188         iCurrentTrack = fRecTracksPtr->GetLast()+1;
189         track = new ((*fRecTracksPtr)[iCurrentTrack]) AliMUONTrack(segment,GetRecoParam()->GetBendingVertexDispersion());
190         fNRecTracks++;
191         
192         // Look for compatible cluster(s) in the second chamber of station 5
193         clusterFound = FollowLinearTrackInChamber(*track, clusterStore, 17-ich2);
194         
195         // skip the original track in case it has been removed
196         if (GetRecoParam()->TrackAllTracks() && clusterFound) iCurrentTrack++;
197         
198         // loop over every new tracks
199         nCurrentTracks = fRecTracksPtr->GetLast()+1;
200         while (iCurrentTrack < nCurrentTracks) {
201           track = (AliMUONTrack*) fRecTracksPtr->UncheckedAt(iCurrentTrack);
202           
203           // Look for compatible cluster(s) in the second chamber of station 4
204           FollowLinearTrackInChamber(*track, clusterStore, 13-ich1);
205           
206           iCurrentTrack++;
207         }
208         
209         // abort tracking if there are too many candidates
210         if ((fNRecTracks + segments->GetEntriesFast() - iSegment - 1) > GetRecoParam()->GetMaxTrackCandidates()) {
211           AliError(Form("Too many track candidates (%d tracks). Abort tracking.", fNRecTracks + segments->GetEntriesFast() - iSegment - 1));
212           delete segments;
213           return kFALSE;
214         }
215         
216       }
217       
218       // delete the array of segments
219       delete segments;
220     }
221   }
222   
223   // Keep only the best tracks if required
224   if (!GetRecoParam()->TrackAllTracks()) RemoveDoubleTracks();
225   else fRecTracksPtr->Compress();
226   
227   AliDebug(1,Form("Number of good candidates = %d",fNRecTracks));
228   
229   return kTRUE;
230   
231 }
232
233   //__________________________________________________________________________
234 Bool_t AliMUONTrackReconstructor::FollowTracks(AliMUONVClusterStore& clusterStore)
235 {
236   /// Follow tracks in stations(1..) 3, 2 and 1
237   AliDebug(1,"Enter FollowTracks");
238   
239   AliMUONTrack *track, *nextTrack;
240   AliMUONTrackParam *trackParam, *nextTrackParam;
241   Int_t currentNRecTracks;
242   
243   Double_t sigmaCut2 = GetRecoParam()->GetSigmaCutForTracking() *
244                        GetRecoParam()->GetSigmaCutForTracking();
245   
246   for (Int_t station = 2; station >= 0; station--) {
247     
248     // Save the actual number of reconstructed track in case of
249     // tracks are added or suppressed during the tracking procedure
250     // !! Do not compress fRecTracksPtr until the end of the loop over tracks !!
251     currentNRecTracks = fNRecTracks;
252     
253     for (Int_t iRecTrack = 0; iRecTrack <currentNRecTracks; iRecTrack++) {
254       AliDebug(1,Form("FollowTracks: track candidate(1..) %d", iRecTrack+1));
255       
256       track = (AliMUONTrack*) fRecTracksPtr->UncheckedAt(iRecTrack);
257       
258       // Fit the track:
259       // Do not take into account the multiple scattering to speed up the fit
260       // Calculate the track parameter covariance matrix
261       // If there is no cluster out of station 4 or 5 then use the vertex to better constrain the fit
262       if (((AliMUONTrackParam*) track->GetTrackParamAtCluster()->First())->GetClusterPtr()->GetChamberId() > 5)
263         Fit(*track, kFALSE, kTRUE, kTRUE);
264       else Fit(*track, kFALSE, kFALSE, kTRUE);
265       
266       // remove tracks out of limits
267       if (!IsAcceptable(*((AliMUONTrackParam*)track->GetTrackParamAtCluster()->First()))) {
268         fRecTracksPtr->Remove(track);
269         fNRecTracks--;
270         continue;
271       }
272       
273       // remove track if the normalized chi2 is too high
274       if (track->GetNormalizedChi2() > sigmaCut2) {
275         fRecTracksPtr->Remove(track);
276         fNRecTracks--;
277         continue;
278       }
279       
280       // save parameters from fit into smoothed parameters to complete track afterward
281       if (GetRecoParam()->ComplementTracks()) {
282         
283         if (station==2) { // save track parameters on stations 4 and 5
284           
285           // extrapolate track parameters and covariances at each cluster
286           track->UpdateCovTrackParamAtCluster();
287           
288           // save them
289           trackParam = (AliMUONTrackParam*) track->GetTrackParamAtCluster()->First();
290           while (trackParam) {
291             trackParam->SetSmoothParameters(trackParam->GetParameters());
292             trackParam->SetSmoothCovariances(trackParam->GetCovariances());
293             trackParam = (AliMUONTrackParam*) track->GetTrackParamAtCluster()->After(trackParam);
294           }
295           
296         } else { // or save track parameters on last station only
297           
298           trackParam = (AliMUONTrackParam*) track->GetTrackParamAtCluster()->First();
299           if (trackParam->GetClusterPtr()->GetChamberId() < 2*(station+2)) {
300             
301             // save parameters from fit
302             trackParam->SetSmoothParameters(trackParam->GetParameters());
303             trackParam->SetSmoothCovariances(trackParam->GetCovariances());
304             
305             // save parameters extrapolated to the second chamber of the same station if it has been hit
306             nextTrackParam = (AliMUONTrackParam*) track->GetTrackParamAtCluster()->After(trackParam);
307             if (nextTrackParam->GetClusterPtr()->GetChamberId() < 2*(station+2)) {
308               
309               // reset parameters and covariances
310               nextTrackParam->SetParameters(trackParam->GetParameters());
311               nextTrackParam->SetZ(trackParam->GetZ());
312               nextTrackParam->SetCovariances(trackParam->GetCovariances());
313               
314               // extrapolate them to the z of the corresponding cluster
315               AliMUONTrackExtrap::ExtrapToZCov(nextTrackParam, nextTrackParam->GetClusterPtr()->GetZ());
316               
317               // save them
318               nextTrackParam->SetSmoothParameters(nextTrackParam->GetParameters());
319               nextTrackParam->SetSmoothCovariances(nextTrackParam->GetCovariances());
320               
321             }
322             
323           }
324           
325         }
326         
327       }
328       
329       // Look for compatible cluster(s) in station(0..) "station"
330       if (!FollowTrackInStation(*track, clusterStore, station)) {
331         
332         // Try to recover track if required
333         if (GetRecoParam()->RecoverTracks()) {
334           
335           // work on a copy of the track if this station is not required
336           // to keep the case where no cluster is reconstructed as a possible candidate
337           if (!GetRecoParam()->RequestStation(station)) {
338             track = new ((*fRecTracksPtr)[fRecTracksPtr->GetLast()+1]) AliMUONTrack(*track);
339             fNRecTracks++;
340           }
341           
342           // try to recover
343           if (!RecoverTrack(*track, clusterStore, station)) {
344             // remove track if no cluster found
345             fRecTracksPtr->Remove(track);
346             fNRecTracks--;
347           }
348           
349         } else if (GetRecoParam()->RequestStation(station)) {
350           // remove track if no cluster found
351           fRecTracksPtr->Remove(track);
352           fNRecTracks--;
353         } 
354         
355       }
356       
357       // abort tracking if there are too many candidates
358       if (fNRecTracks > GetRecoParam()->GetMaxTrackCandidates()) {
359         AliError(Form("Too many track candidates (%d tracks). Abort tracking.", fNRecTracks));
360         return kFALSE;
361       }
362       
363     }
364     
365     // Compress fRecTracksPtr for the next step
366     fRecTracksPtr->Compress();
367     
368     // Keep only the best tracks if required
369     if (!GetRecoParam()->TrackAllTracks()) RemoveDoubleTracks();
370     
371   }
372   
373   // Last fit of track candidates with all stations
374   // Take into account the multiple scattering and remove bad tracks
375   Int_t trackIndex = -1;
376   track = (AliMUONTrack*) fRecTracksPtr->First();
377   while (track) {
378     
379     trackIndex++;
380     nextTrack = (AliMUONTrack*) fRecTracksPtr->After(track); // prepare next track
381     
382     Fit(*track, kTRUE, kFALSE, kTRUE);
383     
384     // Printout for debuging
385     if (AliLog::GetGlobalDebugLevel() >= 3) {
386       cout << "FollowTracks: track candidate(0..) " << trackIndex << " after final fit" << endl;
387       track->RecursiveDump();
388     } 
389     
390     // Remove the track if the normalized chi2 is too high
391     if (track->GetNormalizedChi2() > sigmaCut2) {
392       fRecTracksPtr->Remove(track);
393       fNRecTracks--;
394       track = nextTrack;
395       continue;
396     }
397     
398     // save parameters from fit into smoothed parameters to complete track afterward
399     if (GetRecoParam()->ComplementTracks()) {
400       
401       trackParam = (AliMUONTrackParam*) track->GetTrackParamAtCluster()->First();
402       if (trackParam->GetClusterPtr()->GetChamberId() < 2) {
403         
404         // save parameters from fit
405         trackParam->SetSmoothParameters(trackParam->GetParameters());
406         trackParam->SetSmoothCovariances(trackParam->GetCovariances());
407         
408         // save parameters extrapolated to the second chamber of the same station if it has been hit
409         nextTrackParam = (AliMUONTrackParam*) track->GetTrackParamAtCluster()->After(trackParam);
410         if (nextTrackParam->GetClusterPtr()->GetChamberId() < 2) {
411           
412           // reset parameters and covariances
413           nextTrackParam->SetParameters(trackParam->GetParameters());
414           nextTrackParam->SetZ(trackParam->GetZ());
415           nextTrackParam->SetCovariances(trackParam->GetCovariances());
416           
417           // extrapolate them to the z of the corresponding cluster
418           AliMUONTrackExtrap::ExtrapToZCov(nextTrackParam, nextTrackParam->GetClusterPtr()->GetZ());
419           
420           // save them
421           nextTrackParam->SetSmoothParameters(nextTrackParam->GetParameters());
422           nextTrackParam->SetSmoothCovariances(nextTrackParam->GetCovariances());
423           
424         }
425         
426       }
427       
428     }
429     
430     track = nextTrack;
431     
432   }
433   
434   fRecTracksPtr->Compress();
435   
436   return kTRUE;
437   
438 }
439
440   //__________________________________________________________________________
441 Bool_t AliMUONTrackReconstructor::FollowTrackInChamber(AliMUONTrack &trackCandidate, AliMUONVClusterStore& clusterStore, Int_t nextChamber)
442 {
443   /// Follow trackCandidate in chamber(0..) nextChamber and search for compatible cluster(s)
444   /// Keep all possibilities or only the best one(s) according to the flag fgkTrackAllTracks:
445   /// kTRUE:  duplicate "trackCandidate" if there are several possibilities and add the new tracks at the end of
446   ///         fRecTracksPtr to avoid conficts with other track candidates at this current stage of the tracking procedure.
447   ///         Remove the obsolete "trackCandidate" at the end.
448   /// kFALSE: add only the best cluster(s) to the "trackCandidate". Try to add a couple of clusters in priority.
449   AliDebug(1,Form("Enter FollowTrackInChamber(1..) %d", nextChamber+1));
450   
451   Double_t chi2WithOneCluster = 1.e10;
452   Double_t maxChi2WithOneCluster = 2. * GetRecoParam()->GetSigmaCutForTracking() *
453                                         GetRecoParam()->GetSigmaCutForTracking(); // 2 because 2 quantities in chi2
454   Double_t bestChi2WithOneCluster = maxChi2WithOneCluster;
455   Bool_t foundOneCluster = kFALSE;
456   AliMUONTrack *newTrack = 0x0;
457   AliMUONVCluster *cluster;
458   AliMUONTrackParam extrapTrackParam;
459   AliMUONTrackParam extrapTrackParamAtCh;
460   AliMUONTrackParam extrapTrackParamAtCluster;
461   AliMUONTrackParam bestTrackParamAtCluster;
462   
463   // Get track parameters according to the propagation direction
464   if (nextChamber > 7) extrapTrackParamAtCh = *(AliMUONTrackParam*)trackCandidate.GetTrackParamAtCluster()->Last();
465   else extrapTrackParamAtCh = *(AliMUONTrackParam*)trackCandidate.GetTrackParamAtCluster()->First();
466   
467   // Printout for debuging
468   if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 2) || (AliLog::GetGlobalDebugLevel() >= 2)) {
469     cout<<endl<<"Track parameters and covariances at first cluster:"<<endl;
470     extrapTrackParamAtCh.GetParameters().Print();
471     extrapTrackParamAtCh.GetCovariances().Print();
472   }
473   
474   // Add MCS effect
475   AliMUONTrackExtrap::AddMCSEffect(&extrapTrackParamAtCh,AliMUONConstants::ChamberThicknessInX0(),1.);
476   
477   // Add MCS in the missing chamber(s) if any
478   Int_t currentChamber = extrapTrackParamAtCh.GetClusterPtr()->GetChamberId();
479   while (currentChamber > nextChamber + 1) {
480     // extrapolation to the missing chamber
481     currentChamber--;
482     AliMUONTrackExtrap::ExtrapToZCov(&extrapTrackParamAtCh, AliMUONConstants::DefaultChamberZ(currentChamber));
483     // add MCS effect
484     AliMUONTrackExtrap::AddMCSEffect(&extrapTrackParamAtCh,AliMUONConstants::ChamberThicknessInX0(),1.);
485   }
486   
487   //Extrapolate trackCandidate to chamber
488   AliMUONTrackExtrap::ExtrapToZCov(&extrapTrackParamAtCh, AliMUONConstants::DefaultChamberZ(nextChamber));
489   
490   // Printout for debuging
491   if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 2) || (AliLog::GetGlobalDebugLevel() >= 2)) {
492     cout<<endl<<"Track parameters and covariances at first cluster extrapolated to z = "<<AliMUONConstants::DefaultChamberZ(nextChamber)<<":"<<endl;
493     extrapTrackParamAtCh.GetParameters().Print();
494     extrapTrackParamAtCh.GetCovariances().Print();
495   }
496   
497   // Printout for debuging
498   if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
499     cout << "FollowTrackInStation: look for clusters in chamber(1..): " << nextChamber+1 << endl;
500   }
501   
502   // Ask the clustering to reconstruct new clusters around the track position in the current chamber
503   // except for station 4 and 5 that are already entirely clusterized
504   if (GetRecoParam()->CombineClusterTrackReco()) {
505     if (nextChamber < 6) AskForNewClustersInChamber(extrapTrackParamAtCh, clusterStore, nextChamber);
506   }
507   
508   // Create iterators to loop over clusters in both chambers
509   TIter next(clusterStore.CreateChamberIterator(nextChamber,nextChamber));
510   
511   // look for cluster in chamber
512   while ( ( cluster = static_cast<AliMUONVCluster*>(next()) ) ) {
513     
514     // try to add the current cluster fast
515     if (!TryOneClusterFast(extrapTrackParamAtCh, cluster)) continue;
516     
517     // try to add the current cluster accuratly
518     chi2WithOneCluster = TryOneCluster(extrapTrackParamAtCh, cluster, extrapTrackParamAtCluster);
519     
520     // if good chi2 then consider to add cluster
521     if (chi2WithOneCluster < maxChi2WithOneCluster) {
522       foundOneCluster = kTRUE;
523       
524       // Printout for debuging
525       if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
526         cout << "FollowTrackInStation: found one cluster in chamber(1..): " << nextChamber+1
527         << " (Chi2 = " << chi2WithOneCluster << ")" << endl;
528         cluster->Print();
529       }
530       
531       if (GetRecoParam()->TrackAllTracks()) {
532         // copy trackCandidate into a new track put at the end of fRecTracksPtr and add the new cluster
533         newTrack = new ((*fRecTracksPtr)[fRecTracksPtr->GetLast()+1]) AliMUONTrack(trackCandidate);
534         UpdateTrack(*newTrack,extrapTrackParamAtCluster);
535         fNRecTracks++;
536         
537         // Printout for debuging
538         if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
539           cout << "FollowTrackInStation: added one cluster in chamber(1..): " << nextChamber+1 << endl;
540           if (AliLog::GetGlobalDebugLevel() >= 3) newTrack->RecursiveDump();
541         }
542         
543       } else if (chi2WithOneCluster < bestChi2WithOneCluster) {
544         // keep track of the best single cluster except if a couple of clusters has already been found
545         bestChi2WithOneCluster = chi2WithOneCluster;
546         bestTrackParamAtCluster = extrapTrackParamAtCluster;
547       }
548       
549     }
550     
551   }
552   
553   // fill out the best track if required else clean up the fRecTracksPtr array
554   if (!GetRecoParam()->TrackAllTracks()) {
555     if (foundOneCluster) {
556       UpdateTrack(trackCandidate,bestTrackParamAtCluster);
557       
558       // Printout for debuging
559       if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
560         cout << "FollowTrackInStation: added the best cluster in chamber(1..): " << bestTrackParamAtCluster.GetClusterPtr()->GetChamberId()+1 << endl;
561         if (AliLog::GetGlobalDebugLevel() >= 3) newTrack->RecursiveDump();
562       }
563       
564     } else return kFALSE;
565     
566   } else if (foundOneCluster) {
567     
568     // remove obsolete track
569     fRecTracksPtr->Remove(&trackCandidate);
570     fNRecTracks--;
571     
572   } else return kFALSE;
573   
574   return kTRUE;
575   
576 }
577
578   //__________________________________________________________________________
579 Bool_t AliMUONTrackReconstructor::FollowTrackInStation(AliMUONTrack &trackCandidate, AliMUONVClusterStore& clusterStore, Int_t nextStation)
580 {
581   /// Follow trackCandidate in station(0..) nextStation and search for compatible cluster(s)
582   /// Keep all possibilities or only the best one(s) according to the flag fgkTrackAllTracks:
583   /// kTRUE:  duplicate "trackCandidate" if there are several possibilities and add the new tracks at the end of
584   ///         fRecTracksPtr to avoid conficts with other track candidates at this current stage of the tracking procedure.
585   ///         Remove the obsolete "trackCandidate" at the end.
586   /// kFALSE: add only the best cluster(s) to the "trackCandidate". Try to add a couple of clusters in priority.
587   AliDebug(1,Form("Enter FollowTrackInStation(1..) %d", nextStation+1));
588   
589   // Order the chamber according to the propagation direction (tracking starts with chamber 2):
590   // - nextStation == station(1...) 5 => forward propagation
591   // - nextStation < station(1...) 5 => backward propagation
592   Int_t ch1, ch2;
593   if (nextStation==4) {
594     ch1 = 2*nextStation+1;
595     ch2 = 2*nextStation;
596   } else {
597     ch1 = 2*nextStation;
598     ch2 = 2*nextStation+1;
599   }
600   
601   Double_t chi2WithOneCluster = 1.e10;
602   Double_t chi2WithTwoClusters = 1.e10;
603   Double_t maxChi2WithOneCluster = 2. * GetRecoParam()->GetSigmaCutForTracking() *
604                                         GetRecoParam()->GetSigmaCutForTracking(); // 2 because 2 quantities in chi2
605   Double_t maxChi2WithTwoClusters = 4. * GetRecoParam()->GetSigmaCutForTracking() *
606                                          GetRecoParam()->GetSigmaCutForTracking(); // 4 because 4 quantities in chi2
607   Double_t bestChi2WithOneCluster = maxChi2WithOneCluster;
608   Double_t bestChi2WithTwoClusters = maxChi2WithTwoClusters;
609   Bool_t foundOneCluster = kFALSE;
610   Bool_t foundTwoClusters = kFALSE;
611   AliMUONTrack *newTrack = 0x0;
612   AliMUONVCluster *clusterCh1, *clusterCh2;
613   AliMUONTrackParam extrapTrackParam;
614   AliMUONTrackParam extrapTrackParamAtCh;
615   AliMUONTrackParam extrapTrackParamAtCluster1;
616   AliMUONTrackParam extrapTrackParamAtCluster2;
617   AliMUONTrackParam bestTrackParamAtCluster1;
618   AliMUONTrackParam bestTrackParamAtCluster2;
619   
620   Int_t nClusters = clusterStore.GetSize();
621   Bool_t *clusterCh1Used = new Bool_t[nClusters];
622   for (Int_t i = 0; i < nClusters; i++) clusterCh1Used[i] = kFALSE;
623   Int_t iCluster1;
624   
625   // Get track parameters according to the propagation direction
626   if (nextStation==4) extrapTrackParamAtCh = *(AliMUONTrackParam*)trackCandidate.GetTrackParamAtCluster()->Last();
627   else extrapTrackParamAtCh = *(AliMUONTrackParam*)trackCandidate.GetTrackParamAtCluster()->First();
628   
629   // Printout for debuging
630   if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 2) || (AliLog::GetGlobalDebugLevel() >= 2)) {
631     cout<<endl<<"Track parameters and covariances at first cluster:"<<endl;
632     extrapTrackParamAtCh.GetParameters().Print();
633     extrapTrackParamAtCh.GetCovariances().Print();
634   }
635   
636   // Add MCS effect
637   AliMUONTrackExtrap::AddMCSEffect(&extrapTrackParamAtCh,AliMUONConstants::ChamberThicknessInX0(),1.);
638   
639   // Add MCS in the missing chamber(s) if any
640   Int_t currentChamber = extrapTrackParamAtCh.GetClusterPtr()->GetChamberId();
641   while (ch1 < ch2 && currentChamber > ch2 + 1) {
642     // extrapolation to the missing chamber
643     currentChamber--;
644     AliMUONTrackExtrap::ExtrapToZCov(&extrapTrackParamAtCh, AliMUONConstants::DefaultChamberZ(currentChamber));
645     // add MCS effect
646     AliMUONTrackExtrap::AddMCSEffect(&extrapTrackParamAtCh,AliMUONConstants::ChamberThicknessInX0(),1.);
647   }
648   
649   //Extrapolate trackCandidate to chamber "ch2"
650   AliMUONTrackExtrap::ExtrapToZCov(&extrapTrackParamAtCh, AliMUONConstants::DefaultChamberZ(ch2));
651   
652   // Printout for debuging
653   if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 2) || (AliLog::GetGlobalDebugLevel() >= 2)) {
654     cout<<endl<<"Track parameters and covariances at first cluster extrapolated to z = "<<AliMUONConstants::DefaultChamberZ(ch2)<<":"<<endl;
655     extrapTrackParamAtCh.GetParameters().Print();
656     extrapTrackParamAtCh.GetCovariances().Print();
657   }
658   
659   // Printout for debuging
660   if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
661     cout << "FollowTrackInStation: look for clusters in chamber(1..): " << ch2+1 << endl;
662   }
663   
664   // Ask the clustering to reconstruct new clusters around the track position in the current station
665   // except for station 4 and 5 that are already entirely clusterized
666   if (GetRecoParam()->CombineClusterTrackReco()) {
667     if (nextStation < 3) AskForNewClustersInStation(extrapTrackParamAtCh, clusterStore, nextStation);
668   }
669   
670   // Create iterators to loop over clusters in both chambers
671   TIter nextInCh1(clusterStore.CreateChamberIterator(ch1,ch1));
672   TIter nextInCh2(clusterStore.CreateChamberIterator(ch2,ch2));
673   
674   // look for candidates in chamber 2
675   while ( ( clusterCh2 = static_cast<AliMUONVCluster*>(nextInCh2()) ) ) {
676     
677     // try to add the current cluster fast
678     if (!TryOneClusterFast(extrapTrackParamAtCh, clusterCh2)) continue;
679     
680     // try to add the current cluster accuratly
681     chi2WithOneCluster = TryOneCluster(extrapTrackParamAtCh, clusterCh2, extrapTrackParamAtCluster2);
682     
683     // if good chi2 then try to attach a cluster in the other chamber too
684     if (chi2WithOneCluster < maxChi2WithOneCluster) {
685       Bool_t foundSecondCluster = kFALSE;
686       
687       // Printout for debuging
688       if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
689         cout << "FollowTrackInStation: found one cluster in chamber(1..): " << ch2+1
690              << " (Chi2 = " << chi2WithOneCluster << ")" << endl;
691         clusterCh2->Print();
692         cout << "                      look for second clusters in chamber(1..): " << ch1+1 << " ..." << endl;
693       }
694       
695       // add MCS effect for next step
696       AliMUONTrackExtrap::AddMCSEffect(&extrapTrackParamAtCluster2,AliMUONConstants::ChamberThicknessInX0(),1.);
697       
698       // copy new track parameters for next step
699       extrapTrackParam = extrapTrackParamAtCluster2;
700       
701       //Extrapolate track parameters to chamber "ch1"
702       AliMUONTrackExtrap::ExtrapToZ(&extrapTrackParam, AliMUONConstants::DefaultChamberZ(ch1));
703       
704       // reset cluster iterator of chamber 1
705       nextInCh1.Reset();
706       iCluster1 = -1;
707       
708       // look for second candidates in chamber 1
709       while ( ( clusterCh1 = static_cast<AliMUONVCluster*>(nextInCh1()) ) ) {
710         iCluster1++;
711         
712         // try to add the current cluster fast
713         if (!TryOneClusterFast(extrapTrackParam, clusterCh1)) continue;
714         
715         // try to add the current cluster accuratly
716         chi2WithTwoClusters = TryTwoClusters(extrapTrackParamAtCluster2, clusterCh1, extrapTrackParamAtCluster1);
717         
718         // if good chi2 then create a new track by adding the 2 clusters to the "trackCandidate"
719         if (chi2WithTwoClusters < maxChi2WithTwoClusters) {
720           foundSecondCluster = kTRUE;
721           foundTwoClusters = kTRUE;
722           
723           // Printout for debuging
724           if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
725             cout << "FollowTrackInStation: found second cluster in chamber(1..): " << ch1+1
726                  << " (Global Chi2 = " << chi2WithTwoClusters << ")" << endl;
727             clusterCh1->Print();
728           }
729           
730           if (GetRecoParam()->TrackAllTracks()) {
731             // copy trackCandidate into a new track put at the end of fRecTracksPtr and add the new clusters
732             newTrack = new ((*fRecTracksPtr)[fRecTracksPtr->GetLast()+1]) AliMUONTrack(trackCandidate);
733             UpdateTrack(*newTrack,extrapTrackParamAtCluster1,extrapTrackParamAtCluster2);
734             fNRecTracks++;
735             
736             // Tag clusterCh1 as used
737             clusterCh1Used[iCluster1] = kTRUE;
738             
739             // Printout for debuging
740             if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
741               cout << "FollowTrackInStation: added two clusters in station(1..): " << nextStation+1 << endl;
742               if (AliLog::GetGlobalDebugLevel() >= 3) newTrack->RecursiveDump();
743             }
744             
745           } else if (chi2WithTwoClusters < bestChi2WithTwoClusters) {
746             // keep track of the best couple of clusters
747             bestChi2WithTwoClusters = chi2WithTwoClusters;
748             bestTrackParamAtCluster1 = extrapTrackParamAtCluster1;
749             bestTrackParamAtCluster2 = extrapTrackParamAtCluster2;
750           }
751           
752         }
753         
754       }
755       
756       // if no clusterCh1 found then consider to add clusterCh2 only
757       if (!foundSecondCluster) {
758         foundOneCluster = kTRUE;
759         
760         if (GetRecoParam()->TrackAllTracks()) {
761           // copy trackCandidate into a new track put at the end of fRecTracksPtr and add the new cluster
762           newTrack = new ((*fRecTracksPtr)[fRecTracksPtr->GetLast()+1]) AliMUONTrack(trackCandidate);
763           UpdateTrack(*newTrack,extrapTrackParamAtCluster2);
764           fNRecTracks++;
765           
766           // Printout for debuging
767           if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
768             cout << "FollowTrackInStation: added one cluster in chamber(1..): " << ch2+1 << endl;
769             if (AliLog::GetGlobalDebugLevel() >= 3) newTrack->RecursiveDump();
770           }
771           
772         } else if (!foundTwoClusters && chi2WithOneCluster < bestChi2WithOneCluster) {
773           // keep track of the best single cluster except if a couple of clusters has already been found
774           bestChi2WithOneCluster = chi2WithOneCluster;
775           bestTrackParamAtCluster1 = extrapTrackParamAtCluster2;
776         }
777         
778       }
779       
780     }
781     
782   }
783   
784   // look for candidates in chamber 1 not already attached to a track
785   // if we want to keep all possible tracks or if no good couple of clusters has been found
786   if (GetRecoParam()->TrackAllTracks() || !foundTwoClusters) {
787     
788     // add MCS effect for next step
789     AliMUONTrackExtrap::AddMCSEffect(&extrapTrackParamAtCh,AliMUONConstants::ChamberThicknessInX0(),1.);
790     
791     //Extrapolate trackCandidate to chamber "ch1"
792     AliMUONTrackExtrap::ExtrapToZCov(&extrapTrackParamAtCh, AliMUONConstants::DefaultChamberZ(ch1));
793     
794     // Printout for debuging
795     if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 2) || (AliLog::GetGlobalDebugLevel() >= 2)) {
796       cout<<endl<<"Track parameters and covariances at first cluster extrapolated to z = "<<AliMUONConstants::DefaultChamberZ(ch1)<<":"<<endl;
797       extrapTrackParamAtCh.GetParameters().Print();
798       extrapTrackParamAtCh.GetCovariances().Print();
799     }
800     
801     // Printout for debuging
802     if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
803       cout << "FollowTrackInStation: look for single clusters in chamber(1..): " << ch1+1 << endl;
804     }
805     
806     // reset cluster iterator of chamber 1
807     nextInCh1.Reset();
808     iCluster1 = -1;
809     
810     // look for second candidates in chamber 1
811     while ( ( clusterCh1 = static_cast<AliMUONVCluster*>(nextInCh1()) ) ) {
812       iCluster1++;
813       
814       if (clusterCh1Used[iCluster1]) continue; // Skip cluster already used
815       
816       // try to add the current cluster fast
817       if (!TryOneClusterFast(extrapTrackParamAtCh, clusterCh1)) continue;
818       
819       // try to add the current cluster accuratly
820       chi2WithOneCluster = TryOneCluster(extrapTrackParamAtCh, clusterCh1, extrapTrackParamAtCluster1);
821       
822       // if good chi2 then consider to add clusterCh1
823       // We do not try to attach a cluster in the other chamber too since it has already been done above
824       if (chi2WithOneCluster < maxChi2WithOneCluster) {
825         foundOneCluster = kTRUE;
826         
827         // Printout for debuging
828         if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
829           cout << "FollowTrackInStation: found one cluster in chamber(1..): " << ch1+1
830                << " (Chi2 = " << chi2WithOneCluster << ")" << endl;
831           clusterCh1->Print();
832         }
833         
834         if (GetRecoParam()->TrackAllTracks()) {
835           // copy trackCandidate into a new track put at the end of fRecTracksPtr and add the new cluster
836           newTrack = new ((*fRecTracksPtr)[fRecTracksPtr->GetLast()+1]) AliMUONTrack(trackCandidate);
837           UpdateTrack(*newTrack,extrapTrackParamAtCluster1);
838           fNRecTracks++;
839           
840           // Printout for debuging
841           if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
842             cout << "FollowTrackInStation: added one cluster in chamber(1..): " << ch1+1 << endl;
843             if (AliLog::GetGlobalDebugLevel() >= 3) newTrack->RecursiveDump();
844           }
845           
846         } else if (chi2WithOneCluster < bestChi2WithOneCluster) {
847           // keep track of the best single cluster except if a couple of clusters has already been found
848           bestChi2WithOneCluster = chi2WithOneCluster;
849           bestTrackParamAtCluster1 = extrapTrackParamAtCluster1;
850         }
851         
852       }
853       
854     }
855     
856   }
857   
858   // fill out the best track if required else clean up the fRecTracksPtr array
859   if (!GetRecoParam()->TrackAllTracks()) {
860     if (foundTwoClusters) {
861       UpdateTrack(trackCandidate,bestTrackParamAtCluster1,bestTrackParamAtCluster2);
862       
863       // Printout for debuging
864       if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
865         cout << "FollowTrackInStation: added the two best clusters in station(1..): " << nextStation+1 << endl;
866         if (AliLog::GetGlobalDebugLevel() >= 3) newTrack->RecursiveDump();
867       }
868       
869     } else if (foundOneCluster) {
870       UpdateTrack(trackCandidate,bestTrackParamAtCluster1);
871       
872       // Printout for debuging
873       if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
874         cout << "FollowTrackInStation: added the best cluster in chamber(1..): " << bestTrackParamAtCluster1.GetClusterPtr()->GetChamberId()+1 << endl;
875         if (AliLog::GetGlobalDebugLevel() >= 3) newTrack->RecursiveDump();
876       }
877       
878     } else {
879       delete [] clusterCh1Used;
880       return kFALSE;
881     }
882     
883   } else if (foundOneCluster || foundTwoClusters) {
884     
885     // remove obsolete track
886     fRecTracksPtr->Remove(&trackCandidate);
887     fNRecTracks--;
888     
889   } else {
890     delete [] clusterCh1Used;
891     return kFALSE;
892   }
893   
894   delete [] clusterCh1Used;
895   return kTRUE;
896   
897 }
898
899   //__________________________________________________________________________
900 Double_t AliMUONTrackReconstructor::TryTwoClusters(const AliMUONTrackParam &trackParamAtCluster1, AliMUONVCluster* cluster2,
901                                                    AliMUONTrackParam &trackParamAtCluster2)
902 {
903 /// Test the compatibility between the track and the 2 clusters together (using trackParam's covariance matrix):
904 /// return the corresponding Chi2 accounting for covariances between the 2 clusters
905 /// return trackParamAtCluster1 & 2
906   
907   // extrapolate track parameters at the z position of the second cluster (no need to extrapolate the covariances)
908   trackParamAtCluster2.SetParameters(trackParamAtCluster1.GetParameters());
909   trackParamAtCluster2.SetZ(trackParamAtCluster1.GetZ());
910   AliMUONTrackExtrap::ExtrapToZ(&trackParamAtCluster2, cluster2->GetZ());
911   
912   // set pointer to cluster2 into trackParamAtCluster2
913   trackParamAtCluster2.SetClusterPtr(cluster2);
914   
915   // Set differences between track and the 2 clusters in the bending and non bending directions
916   AliMUONVCluster* cluster1 = trackParamAtCluster1.GetClusterPtr();
917   TMatrixD dPos(4,1);
918   dPos(0,0) = cluster1->GetX() - trackParamAtCluster1.GetNonBendingCoor();
919   dPos(1,0) = cluster1->GetY() - trackParamAtCluster1.GetBendingCoor();
920   dPos(2,0) = cluster2->GetX() - trackParamAtCluster2.GetNonBendingCoor();
921   dPos(3,0) = cluster2->GetY() - trackParamAtCluster2.GetBendingCoor();
922   
923   // Calculate the error matrix from the track parameter covariances at first cluster
924   TMatrixD error(4,4);
925   error.Zero();
926   if (trackParamAtCluster1.CovariancesExist()) {
927     // Save track parameters at first cluster
928     TMatrixD paramAtCluster1Save(trackParamAtCluster1.GetParameters());
929     
930     // Save track coordinates at second cluster
931     Double_t nonBendingCoor2 = trackParamAtCluster2.GetNonBendingCoor();
932     Double_t bendingCoor2    = trackParamAtCluster2.GetBendingCoor();
933     
934     // copy track parameters at first cluster for jacobian calculation
935     AliMUONTrackParam trackParam(trackParamAtCluster1);
936     
937     // add MCS effect to the covariance matrix at first cluster
938     AliMUONTrackExtrap::AddMCSEffect(&trackParam,AliMUONConstants::ChamberThicknessInX0(),1.);
939     
940     // Get the pointer to the parameter covariance matrix at first cluster
941     const TMatrixD& kParamCov = trackParam.GetCovariances();
942     
943     // Calculate the jacobian related to the transformation between track parameters
944     // at first cluster and track coordinates at the 2 cluster z-positions
945     TMatrixD jacob(4,5);
946     jacob.Zero();
947     // first derivative at the first cluster:
948     jacob(0,0) = 1.; // dx1/dx
949     jacob(1,2) = 1.; // dy1/dy
950     // first derivative at the second cluster:
951     TMatrixD dParam(5,1);
952     Double_t direction[5] = {-1.,-1.,1.,1.,-1.};
953     for (Int_t i=0; i<5; i++) {
954       // Skip jacobian calculation for parameters with no associated error
955       if (kParamCov(i,i) == 0.) continue;
956       // Small variation of parameter i only
957       for (Int_t j=0; j<5; j++) {
958         if (j==i) {
959           dParam(j,0) = TMath::Sqrt(kParamCov(i,i));
960           dParam(j,0) *= TMath::Sign(1.,direction[j]*paramAtCluster1Save(j,0)); // variation always in the same direction
961         } else dParam(j,0) = 0.;
962       }
963       
964       // Set new track parameters at first cluster
965       trackParam.SetParameters(paramAtCluster1Save);
966       trackParam.AddParameters(dParam);
967       trackParam.SetZ(cluster1->GetZ());
968       
969       // Extrapolate new track parameters to the z position of the second cluster
970       AliMUONTrackExtrap::ExtrapToZ(&trackParam,cluster2->GetZ());
971       
972       // Calculate the jacobian
973       jacob(2,i) = (trackParam.GetNonBendingCoor() - nonBendingCoor2) / dParam(i,0); // dx2/dParami
974       jacob(3,i) = (trackParam.GetBendingCoor()    - bendingCoor2   ) / dParam(i,0); // dy2/dParami
975     }
976     
977     // Calculate the error matrix
978     TMatrixD tmp(jacob,TMatrixD::kMult,kParamCov);
979     error = TMatrixD(tmp,TMatrixD::kMultTranspose,jacob);
980   }
981   
982   // Add cluster resolution to the error matrix
983   error(0,0) += cluster1->GetErrX2();
984   error(1,1) += cluster1->GetErrY2();
985   error(2,2) += cluster2->GetErrX2();
986   error(3,3) += cluster2->GetErrY2();
987   
988   // invert the error matrix for Chi2 calculation
989   if (error.Determinant() != 0) {
990     error.Invert();
991   } else {
992     AliWarning(" Determinant error=0");
993     return 1.e10;
994   }
995   
996   // Compute the Chi2 value
997   TMatrixD tmp2(dPos,TMatrixD::kTransposeMult,error);
998   TMatrixD result(tmp2,TMatrixD::kMult,dPos);
999   
1000   return result(0,0);
1001   
1002 }
1003
1004   //__________________________________________________________________________
1005 void AliMUONTrackReconstructor::UpdateTrack(AliMUONTrack &track, AliMUONTrackParam &trackParamAtCluster)
1006 {
1007   /// Add 1 cluster to the track candidate
1008   /// Update chi2 of the track 
1009   
1010   // Compute local chi2
1011   AliMUONVCluster* cluster = trackParamAtCluster.GetClusterPtr();
1012   Double_t deltaX = trackParamAtCluster.GetNonBendingCoor() - cluster->GetX();
1013   Double_t deltaY = trackParamAtCluster.GetBendingCoor() - cluster->GetY();
1014   Double_t localChi2 = deltaX*deltaX / cluster->GetErrX2() +
1015                        deltaY*deltaY / cluster->GetErrY2();
1016   
1017   // Flag cluster as being not removable
1018   if (GetRecoParam()->RequestStation(cluster->GetChamberId()/2))
1019     trackParamAtCluster.SetRemovable(kFALSE);
1020   else trackParamAtCluster.SetRemovable(kTRUE);
1021   trackParamAtCluster.SetLocalChi2(0.); // --> Local chi2 not used
1022   
1023   // Update the chi2 of the new track
1024   track.SetGlobalChi2(track.GetGlobalChi2() + localChi2);
1025   
1026   // Update TrackParamAtCluster
1027   track.AddTrackParamAtCluster(trackParamAtCluster,*cluster);
1028   
1029 }
1030
1031   //__________________________________________________________________________
1032 void AliMUONTrackReconstructor::UpdateTrack(AliMUONTrack &track, AliMUONTrackParam &trackParamAtCluster1, AliMUONTrackParam &trackParamAtCluster2)
1033 {
1034   /// Add 2 clusters to the track candidate
1035   /// Update track and local chi2
1036   
1037   // Update local chi2 at first cluster
1038   AliMUONVCluster* cluster1 = trackParamAtCluster1.GetClusterPtr();
1039   Double_t deltaX = trackParamAtCluster1.GetNonBendingCoor() - cluster1->GetX();
1040   Double_t deltaY = trackParamAtCluster1.GetBendingCoor() - cluster1->GetY();
1041   Double_t localChi2AtCluster1 = deltaX*deltaX / cluster1->GetErrX2() +
1042                                  deltaY*deltaY / cluster1->GetErrY2();
1043   trackParamAtCluster1.SetLocalChi2(localChi2AtCluster1);
1044   
1045   // Flag first cluster as being removable
1046   trackParamAtCluster1.SetRemovable(kTRUE);
1047   
1048   // Update local chi2 at second cluster
1049   AliMUONVCluster* cluster2 = trackParamAtCluster2.GetClusterPtr();
1050   deltaX = trackParamAtCluster2.GetNonBendingCoor() - cluster2->GetX();
1051   deltaY = trackParamAtCluster2.GetBendingCoor() - cluster2->GetY();
1052   Double_t localChi2AtCluster2 = deltaX*deltaX / cluster2->GetErrX2() +
1053                                  deltaY*deltaY / cluster2->GetErrY2();
1054   trackParamAtCluster2.SetLocalChi2(localChi2AtCluster2);
1055   
1056   // Flag first cluster as being removable
1057   trackParamAtCluster2.SetRemovable(kTRUE);
1058   
1059   // Update the chi2 of the new track
1060   track.SetGlobalChi2(track.GetGlobalChi2() + localChi2AtCluster1 + localChi2AtCluster2);
1061   
1062   // Update TrackParamAtCluster
1063   track.AddTrackParamAtCluster(trackParamAtCluster1,*cluster1);
1064   track.AddTrackParamAtCluster(trackParamAtCluster2,*cluster2);
1065   
1066 }
1067
1068   //__________________________________________________________________________
1069 Bool_t AliMUONTrackReconstructor::RecoverTrack(AliMUONTrack &trackCandidate, AliMUONVClusterStore& clusterStore, Int_t nextStation)
1070 {
1071   /// Try to recover the track candidate in the next station
1072   /// by removing the worst of the two clusters attached in the current station
1073   /// Return kTRUE if recovering succeeds
1074   AliDebug(1,"Enter RecoverTrack");
1075   
1076   // Do not try to recover track until we have attached cluster(s) on station(1..) 3
1077   if (nextStation > 1) return kFALSE;
1078   
1079   Int_t worstClusterNumber = -1;
1080   Double_t localChi2, worstLocalChi2 = -1.;
1081   
1082   // Look for the cluster to remove
1083   for (Int_t clusterNumber = 0; clusterNumber < 2; clusterNumber++) {
1084     AliMUONTrackParam *trackParamAtCluster = (AliMUONTrackParam*)trackCandidate.GetTrackParamAtCluster()->UncheckedAt(clusterNumber);
1085     
1086     // check if current cluster is in the previous station
1087     if (trackParamAtCluster->GetClusterPtr()->GetChamberId()/2 != nextStation+1) break;
1088     
1089     // check if current cluster is removable
1090     if (!trackParamAtCluster->IsRemovable()) return kFALSE;
1091     
1092     // reset the current cluster as beig not removable if it is on a required station
1093     if (GetRecoParam()->RequestStation(nextStation+1)) trackParamAtCluster->SetRemovable(kFALSE);
1094     
1095     // Pick up cluster with the worst chi2
1096     localChi2 = trackParamAtCluster->GetLocalChi2();
1097     if (localChi2 > worstLocalChi2) {
1098       worstLocalChi2 = localChi2;
1099       worstClusterNumber = clusterNumber;
1100     }
1101     
1102   }
1103   
1104   // check if worst cluster found
1105   if (worstClusterNumber < 0) return kFALSE;
1106   
1107   // Remove the worst cluster
1108   trackCandidate.RemoveTrackParamAtCluster((AliMUONTrackParam*)trackCandidate.GetTrackParamAtCluster()->UncheckedAt(worstClusterNumber));
1109   
1110   // Re-fit the track:
1111   // Do not take into account the multiple scattering to speed up the fit
1112   // Calculate the track parameter covariance matrix
1113   Fit(trackCandidate, kFALSE, kFALSE, kTRUE);
1114   
1115   // skip track out of limits
1116   if (!IsAcceptable(*((AliMUONTrackParam*)trackCandidate.GetTrackParamAtCluster()->First()))) return kFALSE;
1117   
1118   // Look for new cluster(s) in next station
1119   return FollowTrackInStation(trackCandidate,clusterStore,nextStation);
1120   
1121 }
1122
1123   //__________________________________________________________________________
1124 void AliMUONTrackReconstructor::SetVertexErrXY2ForFit(AliMUONTrack &trackCandidate)
1125 {
1126   /// Compute the vertex resolution square from natural vertex dispersion and
1127   /// multiple scattering effets according to trackCandidate path in absorber
1128   /// It is necessary to account for multiple scattering effects here instead of during the fit of
1129   /// the "trackCandidate" to do not influence the result by changing track resolution at vertex
1130   AliDebug(1,"Enter SetVertexForFit");
1131   
1132   Double_t nonBendingReso2 = GetRecoParam()->GetNonBendingVertexDispersion() *
1133                              GetRecoParam()->GetNonBendingVertexDispersion();
1134   Double_t bendingReso2 = GetRecoParam()->GetBendingVertexDispersion() *
1135                           GetRecoParam()->GetBendingVertexDispersion();
1136   
1137   // add multiple scattering effets
1138   AliMUONTrackParam paramAtVertex(*((AliMUONTrackParam*)(trackCandidate.GetTrackParamAtCluster()->First())));
1139   paramAtVertex.DeleteCovariances(); // to be sure to account only for multiple scattering
1140   AliMUONTrackExtrap::ExtrapToVertexUncorrected(&paramAtVertex,0.);
1141   const TMatrixD& kParamCov = paramAtVertex.GetCovariances();
1142   nonBendingReso2 += kParamCov(0,0);
1143   bendingReso2 += kParamCov(2,2);
1144   
1145   // Set the vertex resolution square
1146   trackCandidate.SetVertexErrXY2(nonBendingReso2,bendingReso2);
1147 }
1148
1149   //__________________________________________________________________________
1150 void AliMUONTrackReconstructor::Fit(AliMUONTrack &track, Bool_t includeMCS, Bool_t fitWithVertex, Bool_t calcCov)
1151 {
1152   /// Fit the track
1153   /// w/wo multiple Coulomb scattering according to "includeMCS".
1154   /// w/wo constraining the vertex according to "fitWithVertex".
1155   /// calculating or not the covariance matrix according to "calcCov".
1156   AliDebug(1,"Enter Fit");
1157   
1158   Double_t benC, errorParam, invBenP, nonBenC, x, y;
1159   AliMUONTrackParam *trackParam;
1160   Double_t arg[1], fedm, errdef, globalChi2;
1161   Int_t npari, nparx;
1162   Int_t status, covStatus;
1163   
1164   // Instantiate gMinuit if not already done
1165   if (!gMinuit) gMinuit = new TMinuit(6);
1166   // Clear MINUIT parameters
1167   gMinuit->mncler();
1168   // Give the fitted track to MINUIT
1169   gMinuit->SetObjectFit(&track);
1170   if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 2) || (AliLog::GetGlobalDebugLevel() >= 2)) {
1171     // Define print level
1172     arg[0] = 1;
1173     gMinuit->mnexcm("SET PRI", arg, 1, status);
1174     // Print covariance matrix
1175     gMinuit->mnexcm("SHO COV", arg, 0, status);
1176   } else {
1177     arg[0] = -1;
1178     gMinuit->mnexcm("SET PRI", arg, 1, status);
1179   }
1180   // No warnings
1181   gMinuit->mnexcm("SET NOW", arg, 0, status);
1182   // Define strategy
1183   //arg[0] = 2;
1184   //gMinuit->mnexcm("SET STR", arg, 1, status);
1185   
1186   // set flag w/wo multiple scattering according to "includeMCS"
1187   track.FitWithMCS(includeMCS);
1188   if (includeMCS) {
1189     // compute cluster weights only once
1190     if (!track.ComputeClusterWeights()) {
1191       AliWarning("cannot take into account the multiple scattering effects");
1192       track.FitWithMCS(kFALSE);
1193     }
1194   }
1195   
1196   track.FitWithVertex(fitWithVertex);
1197   if (fitWithVertex) SetVertexErrXY2ForFit(track);
1198   
1199   // Set fitting function
1200   gMinuit->SetFCN(TrackChi2);
1201   
1202   // Set fitted parameters (!! The order is very important for the covariance matrix !!)
1203   // Mandatory limits to avoid NaN values of parameters
1204   trackParam = (AliMUONTrackParam*) (track.GetTrackParamAtCluster()->First());
1205   Double_t maxIBM = 1. / GetRecoParam()->GetMinBendingMomentum();
1206   gMinuit->mnparm(0, "X", trackParam->GetNonBendingCoor(), 0.03, -500.0, 500.0, status);
1207   gMinuit->mnparm(1, "NonBenS", trackParam->GetNonBendingSlope(), 0.001, -1., 1., status);
1208   gMinuit->mnparm(2, "Y", trackParam->GetBendingCoor(), 0.10, -500.0, 500.0, status);
1209   gMinuit->mnparm(3, "BenS", trackParam->GetBendingSlope(), 0.001, -1.5, 1.5, status);
1210   gMinuit->mnparm(4, "InvBenP", trackParam->GetInverseBendingMomentum(), 0.003, -maxIBM, maxIBM, status);
1211   
1212   // minimization
1213   gMinuit->mnexcm("MIGRAD", arg, 0, status);
1214   
1215   // Calculate the covariance matrix more accurately if required
1216   if (calcCov) gMinuit->mnexcm("HESSE", arg, 0, status);
1217    
1218   // get results into "invBenP", "benC", "nonBenC" ("x", "y")
1219   gMinuit->GetParameter(0, x, errorParam);
1220   trackParam->SetNonBendingCoor(x);
1221   gMinuit->GetParameter(1, nonBenC, errorParam);
1222   trackParam->SetNonBendingSlope(nonBenC);
1223   gMinuit->GetParameter(2, y, errorParam);
1224   trackParam->SetBendingCoor(y);
1225   gMinuit->GetParameter(3, benC, errorParam);
1226   trackParam->SetBendingSlope(benC);
1227   gMinuit->GetParameter(4, invBenP, errorParam);
1228   trackParam->SetInverseBendingMomentum(invBenP);
1229   
1230   // global result of the fit
1231   gMinuit->mnstat(globalChi2, fedm, errdef, npari, nparx, covStatus);
1232   track.SetGlobalChi2(globalChi2);
1233   
1234   // Get the covariance matrix if required
1235   if (calcCov) {
1236     // Covariance matrix according to HESSE status
1237     // If problem then keep only the diagonal terms (variances)
1238     Double_t matrix[5][5];
1239     gMinuit->mnemat(&matrix[0][0],5);
1240     if (covStatus == 3) trackParam->SetCovariances(matrix);
1241     else trackParam->SetVariances(matrix);
1242   } else trackParam->DeleteCovariances();
1243   
1244 }
1245
1246   //__________________________________________________________________________
1247 void TrackChi2(Int_t & /*nParam*/, Double_t * /*gradient*/, Double_t &chi2, Double_t *param, Int_t /*flag*/)
1248 {
1249   /// Return the "Chi2" to be minimized with Minuit for track fitting.
1250   /// Assumes that the trackParamAtCluster are sorted according to increasing Z.
1251   /// Track parameters at each cluster are updated accordingly.
1252   /// Vertex is used according to the flag "trackBeingFitted->GetFitWithVertex()".
1253   /// Multiple Coulomb scattering is taken into account according to the flag "trackBeingFitted->GetFitWithMCS()".
1254   
1255   AliMUONTrack *trackBeingFitted = (AliMUONTrack*) gMinuit->GetObjectFit();
1256   AliMUONTrackParam* trackParamAtCluster = (AliMUONTrackParam*) trackBeingFitted->GetTrackParamAtCluster()->First();
1257   Double_t dX, dY;
1258   chi2 = 0.; // initialize chi2
1259   
1260   // update track parameters
1261   trackParamAtCluster->SetNonBendingCoor(param[0]);
1262   trackParamAtCluster->SetNonBendingSlope(param[1]);
1263   trackParamAtCluster->SetBendingCoor(param[2]);
1264   trackParamAtCluster->SetBendingSlope(param[3]);
1265   trackParamAtCluster->SetInverseBendingMomentum(param[4]);
1266   trackBeingFitted->UpdateTrackParamAtCluster();
1267   
1268   // Take the vertex into account in the fit if required
1269   if (trackBeingFitted->FitWithVertex()) {
1270     Double_t nonBendingReso2,bendingReso2;
1271     trackBeingFitted->GetVertexErrXY2(nonBendingReso2,bendingReso2);
1272     if (nonBendingReso2 == 0. || bendingReso2 == 0.) chi2 += 1.e10;
1273     else {
1274       AliMUONTrackParam paramAtVertex(*trackParamAtCluster);
1275       AliMUONTrackExtrap::ExtrapToZ(&paramAtVertex, 0.); // vextex position = (0,0,0)
1276       dX = paramAtVertex.GetNonBendingCoor();
1277       dY = paramAtVertex.GetBendingCoor();
1278       chi2 += dX * dX / nonBendingReso2 + dY * dY / bendingReso2;
1279     }
1280   }
1281   
1282   // compute chi2 w/wo multiple scattering
1283   chi2 += trackBeingFitted->ComputeGlobalChi2(trackBeingFitted->FitWithMCS());
1284   
1285 }
1286
1287   //__________________________________________________________________________
1288 Bool_t AliMUONTrackReconstructor::ComplementTracks(const AliMUONVClusterStore& clusterStore)
1289 {
1290   /// Complete tracks by adding missing clusters (if there is an overlap between
1291   /// two detection elements, the track may have two clusters in the same chamber).
1292   /// Re-fit track parameters and covariances.
1293   /// Return kTRUE if one or more tracks have been complemented.
1294   AliDebug(1,"Enter ComplementTracks");
1295   
1296   Int_t chamberId, detElemId;
1297   Double_t chi2OfCluster, bestChi2OfCluster;
1298   Double_t sigmaCut2 = GetRecoParam()->GetSigmaCutForTracking() *
1299                        GetRecoParam()->GetSigmaCutForTracking();
1300   Bool_t foundOneCluster, trackModified, hasChanged = kFALSE;
1301   AliMUONVCluster* cluster;
1302   AliMUONTrackParam *trackParam, *nextTrackParam, copyOfTrackParam, trackParamAtCluster, bestTrackParamAtCluster;
1303   
1304   AliMUONTrack *track = (AliMUONTrack*) fRecTracksPtr->First();
1305   while (track) {
1306     trackModified = kFALSE;
1307     
1308     trackParam = (AliMUONTrackParam*)track->GetTrackParamAtCluster()->First();
1309     while (trackParam) {
1310       foundOneCluster = kFALSE;
1311       bestChi2OfCluster = 2. * sigmaCut2; // 2 because 2 quantities in chi2
1312       chamberId = trackParam->GetClusterPtr()->GetChamberId();
1313       detElemId = trackParam->GetClusterPtr()->GetDetElemId();
1314       
1315       // prepare nextTrackParam before adding new cluster because of the sorting
1316       nextTrackParam = (AliMUONTrackParam*)track->GetTrackParamAtCluster()->After(trackParam);
1317       
1318       // recover track parameters from local fit and put them into a copy of trackParam
1319       copyOfTrackParam.SetZ(trackParam->GetZ());
1320       copyOfTrackParam.SetParameters(trackParam->GetSmoothParameters());
1321       copyOfTrackParam.SetCovariances(trackParam->GetSmoothCovariances());
1322       
1323       // Create iterators to loop over clusters in current chamber
1324       TIter nextInCh(clusterStore.CreateChamberIterator(chamberId,chamberId));
1325       
1326       // look for one second candidate in the same chamber
1327       while ( ( cluster = static_cast<AliMUONVCluster*>(nextInCh()) ) ) {
1328         
1329         // look for a cluster in another detection element
1330         if (cluster->GetDetElemId() == detElemId) continue;
1331         
1332         // try to add the current cluster fast
1333         if (!TryOneClusterFast(copyOfTrackParam, cluster)) continue;
1334         
1335         // try to add the current cluster accurately
1336         chi2OfCluster = TryOneCluster(copyOfTrackParam, cluster, trackParamAtCluster);
1337         
1338         // if better chi2 then prepare to add this cluster to the track
1339         if (chi2OfCluster < bestChi2OfCluster) {
1340           bestChi2OfCluster = chi2OfCluster;
1341           bestTrackParamAtCluster = trackParamAtCluster;
1342           foundOneCluster = kTRUE;
1343         }
1344         
1345       }
1346       
1347       // add new cluster if any
1348       if (foundOneCluster) {
1349         
1350         // Printout for debuging
1351         if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
1352           cout << "ComplementTracks: found one cluster in chamber(1..): " << chamberId+1 << endl;
1353           bestTrackParamAtCluster.GetClusterPtr()->Print();
1354           cout<<endl<<"Track parameters and covariances at cluster:"<<endl;
1355           bestTrackParamAtCluster.GetParameters().Print();
1356           bestTrackParamAtCluster.GetCovariances().Print();
1357         }
1358         
1359         trackParam->SetRemovable(kTRUE);
1360         bestTrackParamAtCluster.SetRemovable(kTRUE);
1361         track->AddTrackParamAtCluster(bestTrackParamAtCluster,*(bestTrackParamAtCluster.GetClusterPtr()));
1362         trackModified = kTRUE;
1363         hasChanged = kTRUE;
1364       }
1365       
1366       trackParam = nextTrackParam;
1367     }
1368     
1369     // re-fit track parameters if needed
1370     if (trackModified) Fit(*track, kTRUE, kFALSE, kTRUE);
1371     
1372     track = (AliMUONTrack*) fRecTracksPtr->After(track);
1373   }
1374   
1375   return hasChanged;
1376   
1377 }
1378
1379   //__________________________________________________________________________
1380 void AliMUONTrackReconstructor::ImproveTrack(AliMUONTrack &track)
1381 {
1382   /// Improve the given track by removing clusters with local chi2 highter than the defined cut
1383   /// Recompute track parameters and covariances at the remaining clusters
1384   AliDebug(1,"Enter ImproveTrack");
1385   
1386   Double_t localChi2, worstLocalChi2;
1387   AliMUONTrackParam *trackParamAtCluster, *worstTrackParamAtCluster;
1388   Double_t sigmaCut2 = GetRecoParam()->GetSigmaCutForImprovement() *
1389                        GetRecoParam()->GetSigmaCutForImprovement();
1390   
1391   while (!track.IsImproved()) {
1392     
1393     // identify removable clusters
1394     track.TagRemovableClusters(GetRecoParam()->RequestedStationMask());
1395     
1396     // Update track parameters and covariances
1397     track.UpdateCovTrackParamAtCluster();
1398     
1399     // Compute local chi2 of each clusters
1400     track.ComputeLocalChi2(kTRUE);
1401     
1402     // Look for the cluster to remove
1403     worstTrackParamAtCluster = NULL;
1404     worstLocalChi2 = 0.;
1405     trackParamAtCluster = (AliMUONTrackParam*) track.GetTrackParamAtCluster()->First();
1406     while (trackParamAtCluster) {
1407       
1408       // Pick up cluster with the worst chi2
1409       localChi2 = trackParamAtCluster->GetLocalChi2();
1410       if (localChi2 > worstLocalChi2) {
1411         worstLocalChi2 = localChi2;
1412         worstTrackParamAtCluster = trackParamAtCluster;
1413       }
1414       
1415     trackParamAtCluster = (AliMUONTrackParam*) track.GetTrackParamAtCluster()->After(trackParamAtCluster);
1416     }
1417     
1418     // Check if worst cluster found
1419     if (!worstTrackParamAtCluster) {
1420       AliWarning("Bad local chi2 values?");
1421       break;
1422     }
1423     
1424     // Check whether the worst chi2 is under requirement or not
1425     if (worstLocalChi2 < 2. * sigmaCut2) { // 2 because 2 quantities in chi2
1426       track.SetImproved(kTRUE);
1427       break;
1428     }
1429     
1430     // if the worst cluster is not removable then stop improvement
1431     if (!worstTrackParamAtCluster->IsRemovable()) break;
1432     
1433     // Remove the worst cluster
1434     track.RemoveTrackParamAtCluster(worstTrackParamAtCluster);
1435     
1436     // Re-fit the track:
1437     // Take into account the multiple scattering
1438     // Calculate the track parameter covariance matrix
1439     Fit(track, kTRUE, kFALSE, kTRUE);
1440     
1441     // Printout for debuging
1442     if ((AliLog::GetDebugLevel("MUON","AliMUONTrackReconstructor") >= 1) || (AliLog::GetGlobalDebugLevel() >= 1)) {
1443       cout << "ImproveTracks: track " << fRecTracksPtr->IndexOf(&track)+1 << " improved " << endl;
1444     }
1445     
1446   }
1447   
1448 }
1449
1450   //__________________________________________________________________________
1451 void AliMUONTrackReconstructor::FinalizeTrack(AliMUONTrack &track)
1452 {
1453   /// Recompute track parameters and covariances at each attached cluster
1454   /// from those at the first one, if not already done
1455   AliDebug(1,"Enter FinalizeTrack");
1456   if (!track.IsImproved()) track.UpdateCovTrackParamAtCluster();
1457 }
1458
1459   //__________________________________________________________________________
1460 Bool_t AliMUONTrackReconstructor::RefitTrack(AliMUONTrack &track, Bool_t enableImprovement)
1461 {
1462   /// re-fit the given track
1463   
1464   // check validity of the track
1465   if (track.GetNClusters() < 3) {
1466     AliWarning("the track does not contain enough clusters --> unable to refit");
1467     return kFALSE;
1468   }
1469   
1470   // reset the seed (i.e. parameters at first cluster) before fitting
1471   AliMUONTrackParam* firstTrackParam = (AliMUONTrackParam*) track.GetTrackParamAtCluster()->First();
1472   if (firstTrackParam->GetInverseBendingMomentum() == 0.) {
1473     AliWarning("track parameters at first chamber are not initialized --> unable to refit");
1474     return kFALSE;
1475   }
1476   
1477   // compute track parameters at each cluster from parameters at the first one
1478   // necessary to compute multiple scattering effect during refitting
1479   track.UpdateTrackParamAtCluster();
1480   
1481   // Re-fit the track:
1482   // Take into account the multiple scattering
1483   // Calculate the track parameter covariance matrix
1484   Fit(track, kTRUE, kFALSE, kTRUE);
1485   
1486   // Improve the reconstructed tracks if required
1487   track.SetImproved(kFALSE);
1488   if (enableImprovement && GetRecoParam()->ImproveTracks()) ImproveTrack(track);
1489   
1490   // Fill AliMUONTrack data members
1491   FinalizeTrack(track);
1492   
1493   return kTRUE;
1494   
1495 }
1496