AliRsnReader:
[u/mrichter/AliRoot.git] / PWG2 / RESONANCES / AliRsnReader.cxx
1 //
2 // Class AliRsnReader
3 //
4 // This is the universal converter from any kind of source event
5 // (i.e. ESD, standard AOD, MC) into the internal non-standard
6 // AOD format used by RSN package.
7 // ---
8 // This class reads all tracks in an input event and converts them
9 // into AliRsnDaughters, and computes preliminarily the PID probabilities
10 // by doing the Bayesian combination of a set of assigned prior probabilities
11 // with the PID weights defined in each track.
12 // ---
13 // When filling the output event (AliRsnEvent), some arrays of indexes
14 // are created in order to organize tracks according to their PID and charge,
15 // which will then be used in further analysis steps.
16 //
17 // author: A. Pulvirenti (alberto.pulvirenti@ct.infn.it)
18 //
19
20 #include <Riostream.h>
21 #include <TString.h>
22
23 #include "AliLog.h"
24
25 #include "AliVEvent.h"
26
27 #include "AliESDEvent.h"
28 #include "AliESDtrack.h"
29 #include "AliESDVertex.h"
30 #include "AliESDtrackCuts.h"
31
32 #include "AliAODEvent.h"
33 #include "AliAODTrack.h"
34 #include "AliAODVertex.h"
35
36 #include "AliStack.h"
37 #include "AliMCEvent.h"
38 #include "AliMCParticle.h"
39 #include "AliGenEventHeader.h"
40
41 #include "AliRsnMCInfo.h"
42 #include "AliRsnDaughter.h"
43 #include "AliRsnEvent.h"
44 #include "AliRsnPIDDefESD.h"
45 #include "AliRsnCut.h"
46
47 #include "AliRsnReader.h"
48
49 ClassImp(AliRsnReader)
50
51 //_____________________________________________________________________________
52 AliRsnReader::AliRsnReader() :
53   fCheckSplit(kFALSE),
54   fRejectFakes(kFALSE),
55   fTPCOnly(kFALSE),
56   fUseESDTrackCuts(kFALSE),
57   fUseRsnTrackCuts(kFALSE),
58   fCheckVertexStatus(kFALSE),
59   fMinNContributors(0),
60   fPIDDef(),
61   fITSClusters(0),
62   fTPCClusters(0),
63   fTRDClusters(0),
64   fTrackRefs(0),
65   fTrackRefsITS(0),
66   fTrackRefsTPC(0),
67   fESDTrackCuts(),
68   fRsnTrackCuts("primaryCuts")
69 {
70 //
71 // Constructor.
72 //
73 }
74
75 //_____________________________________________________________________________
76 Bool_t AliRsnReader::AreSplitted(AliESDtrack *track1, AliESDtrack *track2)
77 {
78 //
79 // Checks if two tracks are splitted.
80 // Currently, two split tracks are the ones which
81 // have equal GEANT labels.
82 // On real data, this criterion will have to change
83 // into an "experimental" one.
84 //
85
86   Int_t lab1 = TMath::Abs(track1->GetLabel());
87   Int_t lab2 = TMath::Abs(track2->GetLabel());
88
89   return (lab1 == lab2);
90 }
91
92 //_____________________________________________________________________________
93 Bool_t AliRsnReader::ResolveSplit(AliESDtrack *track1, AliESDtrack *track2)
94 {
95 //
96 // Resolves a split of two tracks.
97 // Only two values can be returned:
98 //  kTRUE  --> accept "track1" and reject "track2"
99 //  kFALSE --> accept "track2" and reject "track1"
100 //
101
102   Double_t chiSq1 = track1->GetConstrainedChi2();
103   Double_t chiSq2 = track2->GetConstrainedChi2();
104
105   if (chiSq1 < chiSq2) return 1; else return 2;
106 }
107
108 //_____________________________________________________________________________
109 void AliRsnReader::SetTPCOnly(Bool_t doit)
110 {
111 //
112 // Set the flag for TPC only.
113 // If this is true, exclude all other detectors from PID
114 //
115
116   fTPCOnly = doit;
117
118   if (fTPCOnly) {
119     fPIDDef.ExcludeAll();
120     fPIDDef.IncludeDet(AliRsnPIDDefESD::kTPC);
121     fPIDDef.SetDivValue(AliRsnPIDDefESD::kTPC, 0.0);
122   }
123 }
124
125 //_____________________________________________________________________________
126 Bool_t AliRsnReader::ConvertTrack(AliRsnDaughter *daughter, AliESDtrack *esdTrack)
127 {
128 //
129 // Translates an ESD track into a RSN daughter
130 //
131
132   if (!esdTrack) {
133     AliError("Passed NULL object: nothing done");
134     return kFALSE;
135   }
136
137   Double_t p[3], v[3], pid[AliRsnPID::kSpecies];
138
139   // copy values which don't need treatment:
140   // momentum, vertex, chi2, flags, charge, number of TPC and ITS clusters
141   esdTrack->GetPxPyPz(p);
142   daughter->SetP(p[0], p[1], p[2]);
143   esdTrack->GetXYZ(v);
144   daughter->SetV(v[0], v[1], v[2]);
145   daughter->SetChi2(esdTrack->GetConstrainedChi2());
146   daughter->SetFlags(esdTrack->GetStatus());
147   daughter->SetCharge((Short_t)esdTrack->Charge());
148   daughter->SetNumberOfITSClusters(esdTrack->GetITSclusters(0x0));
149   daughter->SetNumberOfTPCClusters(esdTrack->GetTPCclusters(0x0));
150
151   // define the kink index:
152   //  0 = no kink
153   //  1 = kink daughter
154   // -1 = kink mother
155   Int_t i, ik[3];
156   for (i = 0; i < 3; i++) ik[i] = esdTrack->GetKinkIndex(i);
157   if (ik[0] < 0 || ik[1] < 0 || ik[2] < 0) daughter->SetKinkMother();
158   else if (ik[0] > 0 || ik[1] > 0 || ik[2] > 0) daughter->SetKinkDaughter();
159   else daughter->SetNoKink();
160
161   // store PID weights according to definition
162   // check: if the sum of all weights is null, the adoption fails
163   Double_t sum = 0.0;
164   fPIDDef.ComputeWeights(esdTrack, pid);
165   for (i = 0; i < AliRsnPID::kSpecies; i++)
166   {
167     daughter->SetPIDWeight(i, pid[i]);
168     sum += pid[i];
169   }
170   if (sum <= 0.0) return kFALSE;
171
172   // calculate N sigma to vertex
173   AliESDtrackCuts trkCut;
174   daughter->SetNSigmaToVertex(trkCut.GetSigmaToVertex(esdTrack));
175
176   return kTRUE;
177 }
178
179 //_____________________________________________________________________________
180 Bool_t AliRsnReader::ConvertTrack(AliRsnDaughter *daughter, AliAODTrack *aodTrack)
181 {
182 //
183 // Translates an AOD track into a RSN daughter
184 //
185
186   if (!aodTrack)
187   {
188     AliError("Passed NULL object: nothing done");
189     return kFALSE;
190   }
191
192   // copy momentum  and vertex
193   daughter->SetP(aodTrack->Px(), aodTrack->Py(), aodTrack->Pz());
194   daughter->SetV(aodTrack->Xv(), aodTrack->Yv(), aodTrack->Zv());
195
196   // chi2
197   daughter->SetChi2(aodTrack->Chi2perNDF());
198
199   // copy PID weights
200   Int_t i;
201   for (i = 0; i < 5; i++) daughter->SetPIDWeight(i, aodTrack->PID()[i]);
202
203   // copy flags
204   daughter->SetFlags(aodTrack->GetStatus());
205
206   // copy sign
207   daughter->SetCharge(aodTrack->Charge());
208
209   return kTRUE;
210 }
211
212 //_____________________________________________________________________________
213 Bool_t AliRsnReader::ConvertTrack(AliRsnDaughter *daughter, TParticle *particle)
214 {
215 //
216 // Translates an MC particle into a RSN daughter
217 //
218
219   if (!particle) return kFALSE;
220
221   // copy other MC info (mother PDG code cannot be retrieved here)
222   daughter->InitMCInfo(particle);
223
224   // copy momentum  and vertex
225   daughter->SetP(particle->Px(), particle->Py(), particle->Pz());
226   daughter->SetV(particle->Vx(), particle->Vy(), particle->Vz());
227
228   // recognize charge sign from PDG code sign
229   Int_t pdg = particle->GetPdgCode();
230   Int_t absPDG = TMath::Abs(pdg);
231   if (absPDG == 11 || absPDG == 13)
232   {
233     if (pdg > 0) daughter->SetCharge(-1); else daughter->SetCharge(1);
234   }
235   else if (absPDG == 211 || absPDG == 321 || absPDG == 2212)
236   {
237     if (pdg > 0) daughter->SetCharge(1); else daughter->SetCharge(-1);
238   }
239   else
240   {
241     // when trying to "adopt" a neutral track (photon, neutron, etc.)
242     // for the moment a "failed" message is returned
243     return kFALSE;
244   }
245
246   // flags and PID weights make no sense with MC tracks
247   daughter->SetFlags(0);
248   for (pdg = 0; pdg < AliRsnPID::kSpecies; pdg++) daughter->SetPIDWeight(pdg, 0.0);
249   daughter->SetPIDWeight(AliRsnPID::InternalType(absPDG), 1.0);
250
251   return kTRUE;
252 }
253
254 //_____________________________________________________________________________
255 Bool_t AliRsnReader::Fill
256 (AliRsnEvent *rsn, AliVEvent *event, AliMCEvent *mc)
257 {
258 //
259 // According to the class type of event and the selected source
260 // recalls one of the private reading methods to fill the RsnEvent
261 // passed by reference as first argument.
262 //
263
264   Bool_t success = kFALSE;
265   TString str(event->ClassName());
266
267   if (!str.CompareTo("AliESDEvent")) {
268     AliDebug(1, "Reading an ESD event");
269     success = FillFromESD(rsn, (AliESDEvent*)event, mc);
270   }
271   else if (!str.CompareTo("AliAODEvent")) {
272     AliDebug(1, "Reading an AOD event");
273     success = FillFromAOD(rsn, (AliAODEvent*)event, mc);
274   }
275   else if (!str.CompareTo("AliMCEvent")) {
276     AliDebug(1, "Reading an MC event");
277     success = FillFromMC(rsn, (AliMCEvent*)event);
278   }
279   else {
280     AliError(Form("ClassName '%s' not recognized as possible source data: aborting.", str.Data()));
281     return kFALSE;
282   }
283
284   return success;
285 }
286
287 //_____________________________________________________________________________
288 Bool_t AliRsnReader::FillFromESD(AliRsnEvent *rsn, AliESDEvent *esd, AliMCEvent *mc)
289 {
290 //
291 // Filler from an ESD event.
292 // Stores all tracks (if a filter is defined, it will store
293 // only the ones which survive the cuts).
294 // If a reference MC event is provided, it is used to store
295 // the MC informations for each track (true PDG code,
296 // GEANT label of mother, PDG code of mother, if any).
297 // When this is used, the 'source' flag of the output
298 // AliRsnEvent object will be set to 'kESD'.
299 //
300
301   // retrieve stack (if possible)
302   AliStack *stack = 0x0;
303   if (mc) stack = mc->Stack();
304
305   // set MC primary vertex if present
306   TArrayF fvertex(3);
307   Double_t mcvx = 0., mcvy = 0., mcvz = 0.;
308   if (mc) {
309     mc->GenEventHeader()->PrimaryVertex(fvertex);
310     mcvx = (Double_t)fvertex[0];
311     mcvy = (Double_t)fvertex[1];
312     mcvz = (Double_t)fvertex[2];
313     rsn->SetPrimaryVertexMC(mcvx, mcvy, mcvz);
314   }
315
316   // get number of tracks
317   Int_t ntracks = esd->GetNumberOfTracks();
318   if (!ntracks) return kFALSE;
319
320   // if required with the flag, scans the event
321   // and searches all split tracks (= 2 tracks with the same label);
322   // for each pair of split tracks, only the better (best chi2) is kept
323   // and the other is rejected: this info is stored into a Boolean array
324   Int_t i, i1, i2;
325   Bool_t *accept = new Bool_t[ntracks];
326   for (i = 0; i < ntracks; i++) accept[i] = kTRUE;
327   if (fCheckSplit) {
328     for (i1 = 0; i1 < ntracks; i1++) {
329       AliESDtrack *track1 = esd->GetTrack(i1);
330       for (i2 = i1 + 1; i2 < ntracks; i2++) {
331         AliESDtrack *track2 = esd->GetTrack(i2);
332         if (AreSplitted(track1, track2)) {
333           if (ResolveSplit(track1, track2)) accept[i2] = kFALSE;
334           else accept[i1] = kFALSE;
335         }
336       }
337     }
338   }
339
340   // get primary vertex
341   Double_t vertex[3];
342   if (!fTPCOnly) {
343     // when taking vertex from ESD event there are two options:
344     // if a vertex with tracks was successfully reconstructed,
345     // it is used for computing DCA;
346     // otherwise, the one computed with SPD is used.
347     // This is known from the "Status" parameter of the vertex itself.
348     const AliESDVertex *v = esd->GetPrimaryVertex();
349     if (!v->GetStatus()) v = esd->GetPrimaryVertexSPD();
350     if (fCheckVertexStatus && v->GetStatus() == kFALSE) return kFALSE;
351     if (fMinNContributors > 0 && v->GetNContributors() < fMinNContributors) return kFALSE;
352
353     // get primary vertex
354     vertex[0] = (Double_t)v->GetXv();
355     vertex[1] = (Double_t)v->GetYv();
356     vertex[2] = (Double_t)v->GetZv();
357   }
358   else {
359     const AliESDVertex *v = esd->GetPrimaryVertexTPC();
360     if (fCheckVertexStatus && v->GetStatus() == kFALSE) return kFALSE;
361     if (fMinNContributors > 0 && v->GetNContributors() < fMinNContributors) return kFALSE;
362
363     // get primary vertex
364     vertex[0] = (Double_t)v->GetXv();
365     vertex[1] = (Double_t)v->GetYv();
366     vertex[2] = (Double_t)v->GetZv();
367   }
368   rsn->SetPrimaryVertex(vertex[0], vertex[1], vertex[2]);
369
370   // store tracks from ESD
371   Float_t p[2], cov[3];
372   Int_t   index, label, labmum;
373   Bool_t  check;
374   AliRsnDaughter temp;
375   AliESDtrack *esdTrack = 0x0, esdTrackTmp;
376   for (index = 0; index < ntracks; index++) {
377     // skip track recognized as the worse one in a splitted pair
378     if (!accept[index]) {
379       AliDebug(10, Form("Rejecting split track #%d in this event", index));
380       continue;
381     }
382     // get ESD track
383     esdTrack = esd->GetTrack(index);
384     // check for ESD track cuts (if required)
385     if (fUseESDTrackCuts && (!fESDTrackCuts.AcceptTrack(esdTrack))) continue;
386     // check for fake tracks
387     label = esdTrack->GetLabel();
388     if (fRejectFakes && (label < 0)) continue;
389     // check for minimum number of clusters in ITS, TPC and TRD
390     if (fITSClusters > 0 && (esdTrack->GetITSclusters(0x0) < fITSClusters)) if (!fTPCOnly) continue;
391     if (fTPCClusters > 0 && (esdTrack->GetTPCclusters(0x0) < fTPCClusters)) continue;
392     if (fTRDClusters > 0 && (esdTrack->GetTRDclusters(0x0) < fTRDClusters)) if (!fTPCOnly) continue;
393     // switch to TPC data if required
394     if (fTPCOnly) {
395       esdTrack->GetImpactParametersTPC(p, cov);
396       if (p[0] == 0. && p[1] == 0.) {
397         esdTrack->RelateToVertexTPC(esd->GetPrimaryVertexTPC(), esd->GetMagneticField(), kVeryBig);
398       }
399       check = esdTrack->FillTPCOnlyTrack(esdTrackTmp);
400       if (check) esdTrack = &esdTrackTmp; else continue;
401     }
402     // try to get information from this track
403     // output value tells if this was successful or not
404     check = ConvertTrack(&temp, esdTrack);
405     if (!check) {
406       AliDebug(10, Form("Failed adopting track #%d", index));
407       continue;
408     }
409
410     // if stack is present, copy MC info
411     if (stack) {
412       TParticle *part = stack->Particle(TMath::Abs(label));
413       if (part) {
414         temp.InitMCInfo(part);
415         labmum = part->GetFirstMother();
416         if (labmum >= 0) {
417           TParticle *mum = stack->Particle(labmum);
418           temp.GetMCInfo()->SetMotherPDG(mum->GetPdgCode());
419         }
420       }
421     }
422
423     // set index and label
424     temp.SetIndex(index);
425     temp.SetLabel(label);
426
427     // check this object against the Rsn cuts (if required)
428     if (fUseRsnTrackCuts) {
429       if (!fRsnTrackCuts.IsSelected(AliRsnCut::kParticle, &temp)) continue;
430     }
431
432     // try to add track to collection and returns an error in case of problems
433     AliRsnDaughter *ptr = rsn->AddTrack(temp);
434     if (!ptr) AliWarning(Form("Failed storing track#%d", index));
435   }
436
437   // compute total multiplicity
438   rsn->MakeComputations();
439   if (rsn->GetMultiplicity() <= 0) {
440     AliDebug(1, "Zero Multiplicity in this event");
441     return kFALSE;
442   }
443
444   // sort tracks w.r. to Pt (from largest to smallest)
445   //rsn->SortTracks();
446
447   // correct tracks for primary vertex
448   //rsn->CorrectTracks();
449
450   return kTRUE;
451 }
452
453 //_____________________________________________________________________________
454 Bool_t AliRsnReader::FillFromAOD(AliRsnEvent *rsn, AliAODEvent *aod, AliMCEvent *mc)
455 {
456 //
457 // Filler from an AOD event.
458 // Stores all tracks (if a filter is defined, it will store
459 // only the ones which survive the cuts).
460 // If a reference MC event is provided, it is used to store
461 // the MC informations for each track (true PDG code,
462 // GEANT label of mother, PDG code of mother, if any).
463 // When this is used, the 'source' flag of the output
464 // AliRsnEvent object will be set to 'kAOD'.
465 //
466
467   // retrieve stack (if possible)
468   AliStack *stack = 0x0;
469   if (mc) stack = mc->Stack();
470
471   // get number of tracks
472   Int_t ntracks = aod->GetNTracks();
473   if (!ntracks) return kFALSE;
474
475   // get primary vertex
476   Double_t vertex[3];
477   vertex[0] = aod->GetPrimaryVertex()->GetX();
478   vertex[1] = aod->GetPrimaryVertex()->GetY();
479   vertex[2] = aod->GetPrimaryVertex()->GetZ();
480   rsn->SetPrimaryVertex(vertex[0], vertex[1], vertex[2]);
481
482   // set MC primary vertex if present
483   TArrayF fvertex(3);
484   Double_t mcvx = 0., mcvy = 0., mcvz = 0.;
485   if (mc) {
486     mc->GenEventHeader()->PrimaryVertex(fvertex);
487     mcvx = (Double_t)fvertex[0];
488     mcvy = (Double_t)fvertex[1];
489     mcvz = (Double_t)fvertex[2];
490     rsn->SetPrimaryVertexMC(mcvx, mcvy, mcvz);
491   }
492
493   // store tracks from ESD
494   Int_t  index, label, labmum;
495   Bool_t check;
496   AliAODTrack *aodTrack = 0;
497   AliRsnDaughter temp;
498   TObjArrayIter iter(aod->GetTracks());
499   while ((aodTrack = (AliAODTrack*)iter.Next()))
500   {
501       // retrieve index
502       index = aod->GetTracks()->IndexOf(aodTrack);
503       label = aodTrack->GetLabel();
504       if (fRejectFakes && (label < 0)) continue;
505       // copy ESD track data into RsnDaughter
506       // if unsuccessful, this track is skipped
507       check = ConvertTrack(&temp, aodTrack);
508       if (!check) continue;
509       // if stack is present, copy MC info
510       if (stack)
511       {
512           TParticle *part = stack->Particle(TMath::Abs(label));
513           if (part)
514           {
515               temp.InitMCInfo(part);
516               labmum = part->GetFirstMother();
517               if (labmum >= 0)
518               {
519                   TParticle *mum = stack->Particle(labmum);
520                   temp.GetMCInfo()->SetMotherPDG(mum->GetPdgCode());
521               }
522           }
523       }
524       // set index and label and add this object to the output container
525       temp.SetIndex(index);
526       temp.SetLabel(label);
527
528       // check this object against the Rsn cuts (if required)
529       if (fUseRsnTrackCuts) {
530         if (!fRsnTrackCuts.IsSelected(AliRsnCut::kParticle, &temp)) continue;
531       }
532
533       AliRsnDaughter *ptr = rsn->AddTrack(temp);
534       // if problems occurred while storin, that pointer is NULL
535       if (!ptr) AliWarning(Form("Failed storing track#%d"));
536   }
537
538   // compute total multiplicity
539   rsn->MakeComputations();
540   if (rsn->GetMultiplicity() <= 0)
541   {
542       AliDebug(1, "Zero multiplicity in this event");
543       return kFALSE;
544   }
545
546   // correct tracks for primary vertex
547   rsn->CorrectTracks();
548
549   return kTRUE;
550 }
551
552 //_____________________________________________________________________________
553 Bool_t AliRsnReader::FillFromMC(AliRsnEvent *rsn, AliMCEvent *mc)
554 {
555 //
556 // Filler from an ESD event.
557 // Stores all tracks which generate at least one
558 // TrackReference (a point in a sensitive volume).
559 // In this case, the MC info is stored by default and
560 // perfect particle identification is the unique available.
561 // When this is used, the 'source' flag of the output
562 // AliRsnEvent object will be set to 'kMC'.
563 //
564
565   // get number of tracks
566   Int_t ntracks = mc->GetNumberOfTracks();
567   if (!ntracks) return kFALSE;
568
569   AliStack *stack = mc->Stack();
570
571   // get primary vertex
572   TArrayF fvertex(3);
573   Double_t vx, vy, vz;
574   mc->GenEventHeader()->PrimaryVertex(fvertex);
575   vx = (Double_t)fvertex[0];
576   vy = (Double_t)fvertex[1];
577   vz = (Double_t)fvertex[2];
578   rsn->SetPrimaryVertex(vx, vy, vz);
579   rsn->SetPrimaryVertexMC(vx, vy, vz);
580
581   // store tracks from MC
582   Int_t  i, index, labmum, nRef, nRefITS, nRefTPC, detectorID;
583   AliRsnDaughter temp;
584   for (index = 0; index < ntracks; index++)
585   {
586     // get MC track & take index and label
587     AliMCParticle *mcTrack = mc->GetTrack(index);
588     temp.SetIndex(index);
589     temp.SetLabel(mcTrack->Label());
590
591     // check particle track references
592     nRef = mcTrack->GetNumberOfTrackReferences();
593     for (i = 0, nRefITS = 0, nRefTPC = 0; i < nRef; i++) {
594       AliTrackReference *trackRef = mcTrack->GetTrackReference(i);
595       if (!trackRef) continue;
596       detectorID = trackRef->DetectorId();
597       switch (detectorID) {
598         case AliTrackReference::kITS  : nRefITS++; break;
599         case AliTrackReference::kTPC  : nRefTPC++; break;
600         default: break;
601       }
602     }
603     if (fTrackRefs > 0 && nRef < fTrackRefs) continue;
604     if (fTrackRefsITS > 0 && nRefITS < fTrackRefsITS) continue;
605     if (fTrackRefsTPC > 0 && nRefTPC < fTrackRefsTPC) continue;
606
607     // try to insert in the RsnDaughter its data
608     TParticle *mcPart = mcTrack->Particle();
609     if (!ConvertTrack(&temp, mcPart)) continue;
610
611     // assign mother label and PDG code (if any)
612     labmum = temp.GetMCInfo()->Mother();
613     if (labmum >= 0) {
614       TParticle *mum = stack->Particle(labmum);
615       temp.GetMCInfo()->SetMotherPDG(mum->GetPdgCode());
616     }
617
618     // check this object against the Rsn cuts (if required)
619     if (fUseRsnTrackCuts) {
620       if (!fRsnTrackCuts.IsSelected(AliRsnCut::kParticle, &temp)) continue;
621     }
622
623     AliRsnDaughter *ptr = rsn->AddTrack(temp);
624     if (!ptr) AliWarning(Form("Failed storing track #%d", index));
625   }
626
627   // compute total multiplicity
628   rsn->MakeComputations();
629   if (rsn->GetMultiplicity() <= 0)
630   {
631     AliDebug(1, "Zero multiplicity in this event");
632     return kFALSE;
633   }
634
635   // sort tracks w.r. to Pt (from largest to smallest)
636   rsn->SortTracks();
637
638   // correct tracks for primary vertex
639   rsn->CorrectTracks();
640
641   return kTRUE;
642 }