]> git.uio.no Git - u/mrichter/AliRoot.git/blob - RALICE/icepack/IceDwalk.cxx
093f3c4b267615faf72bc8c93e30335ce3f0d1d9
[u/mrichter/AliRoot.git] / RALICE / icepack / IceDwalk.cxx
1 /*******************************************************************************
2  * Copyright(c) 2003, IceCube Experiment at the South Pole. All rights reserved.
3  *
4  * Author: The IceCube RALICE-based Offline 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.
12  * The authors make no claims about the suitability of this software for
13  * any purpose. It is provided "as is" without express or implied warranty.
14  *******************************************************************************/
15
16 // $Id$
17
18 ///////////////////////////////////////////////////////////////////////////
19 // Class IceDwalk
20 // TTask derived class to perform direct walk track reconstruction.
21 // The procedure is based on the method described in the Amanda publication
22 // in Nuclear Instruments and Methods A524 (2004) 179-180.
23 // However, the Amanda method has been extended with the intention to
24 // take also multiple (muon) tracks within 1 event into account.
25 // This will not only provide a means to reconstruct muon bundles and
26 // multiple track events in IceCube, but will also allow to reduce the
27 // background of faked upgoing muons as a result of multiple downgoing
28 // muons hitting the top and bottom parts of the detector.
29 // To prevent waisting CPU time in trying to reconstruct (high-energy) cascade
30 // events, or to select specifically reconstruction of low multiplicity events,
31 // the user may invoke the memberfunctions SetMaxModA() and SetMinModA().
32 // This allows selection of events for processing with a certain maximum and/or
33 // minimum number of good Amanda OMs firing.
34 // By default the minimum and maximum are set to 0 and 999, respectively,
35 // in the constructor, which implies no multiplicity selection. 
36 // The various reconstruction steps are summarised as follows :
37 //
38 // 1) Construction of track elements (TE's).
39 //    A track element is a straight line connecting two hits that
40 //    appeared at some minimum distance d and within some maximum
41 //    time difference dt.
42 //    The default values for d and dt are given in eq. (20) of the
43 //    NIM article, but can be modified by the appropriate Set functions.
44 //    For dt a default margin of 30 ns is used (according to eq. (20)),
45 //    but also this margin may be modified via the appropriate Set function.    
46 //    The reference point r0 of the TE is taken as the center between
47 //    the two hit positions and the TE timestamp t0 at the position r0
48 //    is taken as the IceEvent timestamp increased by the average of the
49 //    two hit times. So, all timestamps contain the overall IceEvent
50 //    timestamp as a basis. This means that time differences can be
51 //    obtained via the AliTimestamp facilities (supporting upto picosecond
52 //    precision when available).
53 //    The TE direction is given by the relative position of the two hits.
54 //
55 // 2) Each TE will obtain so called associated hits.
56 //    A hit is associated to a TE when it fulfills both the conditions
57 //
58 //      -30 < tres < 300 ns
59 //      dhit < 25*(tres+30)^(1/4) meter
60 //
61 //    tres : time residual
62 //           Difference between the observed hit time and the time expected
63 //           for a direct photon hit.     
64 //    dhit : Distance between the hit and the TE
65 //
66 // 3) Construction of track candidates (TC's).
67 //    These are TE's that fulfill the condition (see eq. (21) in the NIM article)
68 //
69 //     qtc >= 0.7*qtcmax
70 //
71 //     qtc=min(nah,0.3*sigmal+7)
72 //     qtcmax=max(qtc)
73 //
74 //    where we have used the observables :
75 //
76 //    nah    : Number of associated hits.
77 //    sigmal : rms variance of the distances between r0 and the point on
78 //             the track which is closest to the various associated hits. 
79 //
80 //    Note : The following additional quality selection as indicated
81 //           in the NIM article is not used anymore. 
82 //
83 //      nah >= 10
84 //      sigmal >= 20 meter
85 // 
86 //
87 // 4) The remaining track candidates are clustered into jets when their directions
88 //    are within a certain maximum opening angle.
89 //    In addition the distance between their r0's must be below a certain maximum
90 //    or the relative r0 direction must fall within a certain maximum opening angle
91 //    w.r.t. the jet-starting track candidate.
92 //    The latter criterion prevents clustering of (nearly) parallel track candidates
93 //    crossing the detector a very different locations (e.g. muon bundles).
94 //    The default maximum track opening angle is 15 degrees, but can be modified
95 //    via the SetTangmax memberfunction.
96 //    The remaining parameters related to the r0 criteria can be modified via
97 //    the SetRtdmax and SetRtangmax memberfunctions.
98 //    See the corresponding docs for defaults etc...
99 //
100 //    The average of all the r0 and t0 values of the constituent TC's
101 //    of the jet will provide the r0 and t0 (i.e. reference point) of the jet.
102 //
103 // 5) The jets are merged when their directions are within a certain maximum
104 //    opening angle. 
105 //    In addition the distance between their r0's must be below a certain maximum
106 //    or the relative r0 direction must fall within a certain maximum opening angle
107 //    w.r.t. the starting jet.
108 //    The latter criterion prevents merging of (nearly) parallel tracks/jets
109 //    crossing the detector a very different locations (e.g. muon bundles).
110 //    The default maximum opening angle is half the TC maximum opening angle,
111 //    but can be modified via the SetJangmax memberfunction.
112 //    The remaining parameters related to the r0 criteria can be modified via
113 //    the SetRjdmax and SetRjangmax memberfunctions.
114 //    See the corresponding docs for defaults etc...
115 //
116 //    Note : Setting the maximum jet opening angle to <=0 will prevent
117 //           the merging of jets.
118 //
119 //    The average of all the r0 and t0 values of the merged jets will provide
120 //    the r0 and t0 (i.e. reference point) of the final jet.
121 //
122 // 6) The remaining jets are ordered w.r.t. decreasing number of tracks.
123 //    Each remaining jet will provide the parameters (e.g. direction)
124 //    for a reconstructed track.
125 //    The track 3-momentum is set to the total jet 3-momentum, normalised
126 //    to 1 GeV. The mass and charge of the track are left 0.
127 //    The reference point data of the jet will provide the r0 and t0
128 //    (i.e. reference point) of the track.
129 //
130 //    All these tracks will be stored in the IceEvent structure with as
131 //    default "IceDwalk" as the name of the track.
132 //    This track name identifier can be modified by the user via the
133 //    SetTrackName() memberfunction. This will allow unique identification
134 //    of tracks which are produced when re-processing existing data with
135 //    different criteria.
136 //    By default the charge of the produced tracks is set to 0, since
137 //    no distinction can be made between positive or negative tracks.
138 //    However, the user can define the track charge by invokation
139 //    of the memberfunction SetCharge().
140 //    This facility may be used to distinguish tracks produced by the
141 //    various reconstruction algorithms in a (3D) colour display
142 //    (see the class AliHelix for further details).  
143 //
144 //    Note : In case the maximum jet opening angle was specified <0,
145 //           only the jet with the maximum number of tracks will appear
146 //           as a reconstructed track in the IceEvent structure.
147 //           This will allow comparison with the standard Sieglinde
148 //           single track direct walk reconstruction results. 
149 //    
150 // For further details the user is referred to NIM A524 (2004) 169.
151 //
152 // Note : This algorithm works best on data which has been calibrated
153 //        (IceCalibrate), cross talk corrected (IceXtalk) and cleaned
154 //        from noise hits etc. (IceCleanHits).
155 //
156 //--- Author: Nick van Eijndhoven 07-oct-2005 Utrecht University
157 //- Modified: NvE $Date$ Utrecht University
158 ///////////////////////////////////////////////////////////////////////////
159  
160 #include "IceDwalk.h"
161 #include "Riostream.h"
162
163 ClassImp(IceDwalk) // Class implementation to enable ROOT I/O
164
165 IceDwalk::IceDwalk(const char* name,const char* title) : TTask(name,title)
166 {
167 // Default constructor.
168 // The various reconstruction parameters are initialised to the values
169 // as mentioned in NIM A524 (2004) 179-180.
170 // The newly introduced angular separation parameter for jet merging
171 // is initialised as half the value of the angular separation parameter
172 // for track candidate clustering.    
173  fDmin=50;
174  fDtmarg=30;
175  fTangmax=15;
176  fRtangmax=fTangmax;
177  fRtdmax=0;
178  fJangmax=fTangmax/2.;
179  fRjangmax=fRtangmax;
180  fRjdmax=fDmin;
181  fMaxmodA=999;
182  fMinmodA=0;
183  fTrackname="IceDwalk";
184  fCharge=0;
185 }
186 ///////////////////////////////////////////////////////////////////////////
187 IceDwalk::~IceDwalk()
188 {
189 // Default destructor.
190 }
191 ///////////////////////////////////////////////////////////////////////////
192 void IceDwalk::SetDmin(Float_t d)
193 {
194 // Set minimum hit distance (in m) to form a track element.
195 // In the constructor the default has been set to 50 meter, in accordance
196 // to eq.(20) of NIM A524 (2004) 179.
197  fDmin=d;
198 }
199 ///////////////////////////////////////////////////////////////////////////
200 void IceDwalk::SetDtmarg(Int_t dt)
201 {
202 // Set maximum hit time difference margin (in ns) for track elements.
203 // In the constructor the default has been set to 30 ns, in accordance
204 // to eq.(20) of NIM A524 (2004) 179.
205  fDtmarg=dt;
206 }
207 ///////////////////////////////////////////////////////////////////////////
208 void IceDwalk::SetTangmax(Float_t ang)
209 {
210 // Set maximum angular separation (in deg) for track candidate clustering
211 // into jets.
212 // In the constructor the default has been set to 15 deg, in accordance
213 // to NIM A524 (2004) 180.
214 //
215 // Note : This function also sets automatically the value of the maximum
216 //        angular separation for jet merging into 1 single track to ang/2.
217 //        In order to specify a different max. jet merging separation angle,
218 //        one has to invoke the memberfunction SetJangmax afterwards.
219  
220  fTangmax=ang;
221  fJangmax=ang/2.;
222 }
223 ///////////////////////////////////////////////////////////////////////////
224 void IceDwalk::SetRtangmax(Float_t ang)
225 {
226 // Set maximum angular separation (in deg) for the relative direction of the
227 // two r0's of two track candidates (w.r.t. the direction of the starting
228 // track candidate) in the track clustering process.
229 // In the constructor the default has been set to 15 deg, corresponding
230 // to the default max. angular separation for track candidate clustering.
231 //
232 // Note : This function also sets automatically the value of the maximum
233 //        angular separation for the relative direction of the two r0's
234 //        of two jets (w.r.t. the direction of the starting jet)
235 //        in the jet merging process.
236 //        In order to specify a different value related to jet merging,
237 //        one has to invoke the memberfunction SetRjangmax afterwards.
238  
239  fRtangmax=ang;
240  fRjangmax=ang;
241 }
242 ///////////////////////////////////////////////////////////////////////////
243 void IceDwalk::SetRtdmax(Float_t d)
244 {
245 // Set maximum distance (in m) of the two r0's of two track candidates
246 // in the track clustering process.
247 // This will allow clustering of tracks with very close r0's, of which
248 // their relative direction may point in any direction.
249 // In the constructor the default has been set 0 until further tuning
250 // of this parameter has been achieved.
251 //
252 // Note : In case the distance between the two r0's exceeds the maximum,
253 //        the track candidates will still be clustered if the relative
254 //        direction of the two r0's falls within the maximum separation
255 //        angle w.r.t. the starting track direction.
256  
257  fRtdmax=d;
258 }
259 ///////////////////////////////////////////////////////////////////////////
260 void IceDwalk::SetJangmax(Float_t ang)
261 {
262 // Set angular separation (in deg) within which jets are merged into 1
263 // single track.
264 // In the constructor the default has been set 7.5 deg, being half of the
265 // value of the default track candidate clustering separation angle. 
266 //
267 // Notes :
268 // -------
269 // 1)  Setting ang=0 will prevent jet merging.
270 //     Consequently, every jet will appear as a separate track in the
271 //     reconstruction result.  
272 // 2)  Setting ang<0 will prevent jet merging.
273 //     In addition, only the jet with the maximum number of tracks will
274 //     appear as a track in the reconstruction result.
275 //     This situation resembles the standard Sieglinde direct walk processing
276 //     and as such can be used to perform comparison studies.
277
278  fJangmax=ang;
279 }
280 ///////////////////////////////////////////////////////////////////////////
281 void IceDwalk::SetRjangmax(Float_t ang)
282 {
283 // Set maximum angular separation (in deg) for the relative direction of the
284 // two r0's of two jets (w.r.t. the direction of the starting jet)
285 // in the jet merging process.
286 // In the constructor the default has been set to the corresponding value
287 // of the same parameter related to the track clustering.
288  
289  fRjangmax=ang;
290 }
291 ///////////////////////////////////////////////////////////////////////////
292 void IceDwalk::SetRjdmax(Float_t d)
293 {
294 // Set maximum distance (in m) of the two r0's of two jets in the
295 // jet merging process.
296 // This will allow merging of jets with rather close r0's, of which
297 // their relative direction may point in any direction.
298 // In the constructor the default has been set to 50 meter, corresponding
299 // to the value of the minimum hit distance to form a track element.
300 //
301 // Note : In case the distance between the two r0's exceeds the maximum,
302 //        the jets will still be merged if the relative direction of the
303 //        two r0's falls within the maximum separation angle w.r.t. the
304 //        starting jet direction.
305  
306  fRjdmax=d;
307 }
308 ///////////////////////////////////////////////////////////////////////////
309 void IceDwalk::SetMaxModA(Int_t nmax)
310 {
311 // Set the maximum number of good Amanda modules that may have fired
312 // in order to process this event.
313 // This allows suppression of processing (high-energy) cascade events
314 // with this direct walk tracking to prevent waisting cpu time for cases
315 // in which tracking doesn't make sense anyhow.
316 // Furthermore it allows selection of low multiplicity events for processing.
317 // By default the maximum number of Amanda modules is set to 999 in the ctor,
318 // which implies no selection on maximum module multiplicity.
319 // See also the memberfunction SetMinModA().
320  fMaxmodA=nmax;
321 }
322 ///////////////////////////////////////////////////////////////////////////
323 void IceDwalk::SetMinModA(Int_t nmin)
324 {
325 // Set the minimum number of good Amanda modules that must have fired
326 // in order to process this event.
327 // This allows selection of a minimal multiplicity for events to be processed.
328 // By default the minimum number of Amanda modules is set to 0 in the ctor,
329 // which implies no selection on minimum module multiplicity.
330 // See also the memberfunction SetMaxModA().
331  fMinmodA=nmin;
332 }
333 ///////////////////////////////////////////////////////////////////////////
334 void IceDwalk::SetTrackName(TString s)
335 {
336 // Set (alternative) name identifier for the produced first guess tracks.
337 // This allows unique identification of (newly) produced direct walk tracks
338 // in case of re-processing of existing data with different criteria.
339 // By default the produced first guess tracks have the name "IceDwalk"
340 // which is set in the constructor of this class.
341  fTrackname=s;
342 }
343 ///////////////////////////////////////////////////////////////////////////
344 void IceDwalk::SetCharge(Float_t charge)
345 {
346 // Set user defined charge for the produced first guess tracks.
347 // This allows identification of these tracks on color displays.
348 // By default the produced first guess tracks have charge=0
349 // which is set in the constructor of this class.
350  fCharge=charge;
351 }
352 ///////////////////////////////////////////////////////////////////////////
353 void IceDwalk::Exec(Option_t* opt)
354 {
355 // Implementation of the direct walk track reconstruction.
356
357  TString name=opt;
358  AliJob* parent=(AliJob*)(gROOT->GetListOfTasks()->FindObject(name.Data()));
359
360  if (!parent) return;
361
362  IceEvent* evt=(IceEvent*)parent->GetObject("IceEvent");
363  if (!evt) return;
364
365  // Fetch all fired Amanda OMs for this event
366  TObjArray* aoms=evt->GetDevices("IceAOM");
367  Int_t naoms=aoms->GetEntries();
368  if (!naoms) return;
369
370  // Check for the minimum and/or maximum number of good fired Amanda OMs
371  Int_t ngood=0;
372  for (Int_t iom=0; iom<naoms; iom++)
373  {
374   IceGOM* omx=(IceGOM*)aoms->At(iom);
375   if (!omx) continue;
376   if (omx->GetDeadValue("ADC") || omx->GetDeadValue("LE") || omx->GetDeadValue("TOT")) continue;
377   ngood++;
378  } 
379  if (ngood<fMinmodA || ngood>fMaxmodA) return;
380
381  const Float_t c=0.3;                // Light speed in vacuum in meters per ns
382  const Float_t nice=1.33;            // Refractive index of ice
383  const Float_t thetac=acos(1./nice); // Cherenkov angle (in radians)
384
385  // Storage of track elements with various time difference margins.
386  // temap(i,j) holds the i-th track element (TE) with a time difference margin
387  // of less than j*3 nanoseconds. Currently we use a maximum margin of 30 ns.
388  TObjArray tes;
389  tes.SetOwner();
390  AliObjMatrix temap;
391
392  // Counter of TEs for each 3 ns margin slot
393  TArrayI ntes(fDtmarg/3);
394  if (ntes.GetSize()==0) ntes.Set(1);
395
396  AliPosition r1;
397  AliPosition r2;
398  Ali3Vector r12;
399  Ali3Vector rsum;
400  AliPosition r0;
401  TObjArray hits1;
402  TObjArray hits2;
403  Int_t nh1,nh2;
404  AliSignal* sx1=0;
405  AliSignal* sx2=0;
406  Float_t dist=0;
407  Float_t t1,t2,dt,t0;
408  Float_t dtmax,dttest;
409  TObjArray hits;
410
411  // Check the hits of Amanda OM pairs for posible track elements.
412  // Also all the good hits are stored in the meantime (to save CPU time)
413  // for hit association with the various track elements lateron.
414  AliTrack* te=0;
415  Int_t ite=0;
416  for (Int_t i1=0; i1<naoms; i1++) // First OM of the pair
417  {
418   IceGOM* omx1=(IceGOM*)aoms->At(i1);
419   if (!omx1) continue;
420   if (omx1->GetDeadValue("LE")) continue;
421   r1=omx1->GetPosition();
422   // Select all the good hits of this first OM
423   hits1.Clear();
424   for (Int_t j1=1; j1<=omx1->GetNhits(); j1++)
425   {
426    sx1=omx1->GetHit(j1);
427    if (!sx1) continue;
428    if (sx1->GetDeadValue("ADC") || sx1->GetDeadValue("LE") || sx1->GetDeadValue("TOT")) continue;
429    hits1.Add(sx1);
430    // Also store all good hits in the total hit array
431    hits.Add(sx1);
432   }
433
434   // No further pair to be formed with the last OM in the list 
435   if (i1==(naoms-1)) break;
436
437   nh1=hits1.GetEntries();
438   if (!nh1) continue;
439
440   for (Int_t i2=i1+1; i2<naoms; i2++) // Second OM of the pair
441   {
442    IceGOM* omx2=(IceGOM*)aoms->At(i2);
443    if (!omx2) continue;
444    if (omx2->GetDeadValue("LE")) continue;
445    r2=omx2->GetPosition();
446    r12=r2-r1;
447    dist=r12.GetNorm();
448
449    if (dist<=fDmin) continue;
450
451    // Select all the good hits of this second OM
452    hits2.Clear();
453    for (Int_t j2=1; j2<=omx2->GetNhits(); j2++)
454    {
455     sx2=omx2->GetHit(j2);
456     if (!sx2) continue;
457     if (sx2->GetDeadValue("ADC") || sx2->GetDeadValue("LE") || sx2->GetDeadValue("TOT")) continue;
458     hits2.Add(sx2);
459    }
460  
461    nh2=hits2.GetEntries();
462    if (!nh2) continue;
463
464    // Position r0 in between the two OMs and normalised relative direction r12
465    rsum=(r1+r2)/2.;
466    r0.SetPosition((Ali3Vector&)rsum);
467    r12/=dist;
468
469    // Check all hit pair combinations of these two OMs for possible track elements  
470    dtmax=dist/c+float(fDtmarg);
471    for (Int_t ih1=0; ih1<nh1; ih1++) // Hits of first OM
472    {
473     sx1=(AliSignal*)hits1.At(ih1);
474     if (!sx1) continue;
475     for (Int_t ih2=0; ih2<nh2; ih2++) // Hits of second OM
476     {
477      sx2=(AliSignal*)hits2.At(ih2);
478      if (!sx2) continue;
479      t1=sx1->GetSignal("LE",7);
480      t2=sx2->GetSignal("LE",7);
481      dt=t2-t1;
482      t0=(t1+t2)/2.;
483
484      if (fabs(dt)>=dtmax) continue;
485
486      te=new AliTrack();
487      tes.Add(te);
488      ite++;
489      if (dt<0) r12*=-1.;
490      r0.SetTimestamp((AliTimestamp&)*evt);
491      AliTimestamp* tsx=r0.GetTimestamp();
492      tsx->Add(0,0,(int)t0);
493      te->SetReferencePoint(r0);
494      te->Set3Momentum(r12);
495      dttest=dtmax;
496      for (Int_t jt=ntes.GetSize(); jt>0; jt--)
497      {
498       if (fabs(dt)>=dttest) break;
499       temap.EnterObject(ite,jt,te);
500       ntes.AddAt(ntes.At(jt-1)+1,jt-1);
501       dttest-=3.;
502      }
503     }
504    }
505   } // end of loop over the second OM of the pair
506  } // end of loop over first OM of the pair
507
508  // Association of hits to the various track elements
509  // For the time being all track elements will be treated,
510  // but in a later stage one could select only the TE's of a certain
511  // 3 ns margin slot in the TE map to save CPU time.
512  Int_t nte=tes.GetEntries();
513  Int_t nh=hits.GetEntries();
514  Float_t d=0;
515  Ali3Vector p;
516  Float_t tgeo,tres;
517  AliSample levers;  // Statistics of the assoc. hit lever arms
518  AliSignal fit;     // Storage of Q value etc... for each track candidate
519  fit.SetSlotName("QTC",1);
520  fit.SetSlotName("SIGMAL",2);
521  Float_t qtc=0,qmax=0;
522  Int_t nah;      // Number of associated hits for a certain TE
523  Float_t sigmal; // The mean lever arm of the various associated hits
524  for (Int_t jte=0; jte<nte; jte++)
525  {
526   te=(AliTrack*)tes.At(jte);
527   if (!te) continue;
528   AliPosition* tr0=te->GetReferencePoint();
529   AliTimestamp* tt0=tr0->GetTimestamp();
530   t0=evt->GetDifference(tt0,"ns");
531   p=te->Get3Momentum();
532   levers.Reset();
533   for (Int_t jh=0; jh<nh; jh++)
534   {
535    sx1=(AliSignal*)hits.At(jh);
536    if (!sx1) continue;
537    IceGOM* omx=(IceGOM*)sx1->GetDevice();
538    if (!omx) continue;
539    r1=omx->GetPosition();
540    d=tr0->GetDistance(r1);
541    d*=sin(thetac);
542    r12=r1-(*tr0);
543    dist=p.Dot(r12)+d*tan(thetac);
544    tgeo=t0+dist/c;
545    t1=sx1->GetSignal("LE",7);
546    tres=t1-tgeo;
547
548    if (tres<-30 || tres>300 || d>25.*pow(tres+30.,0.25)) continue;
549
550    // Associate this hit to the TE
551    te->AddSignal(*sx1);
552    levers.Enter(d/tan(thetac));
553   }
554
555   // Determine the Q quality of the various TE's.
556   // Good quality TE's will be called track candidates (TC's)
557   nah=te->GetNsignals();
558   sigmal=levers.GetSigma(1);
559   qtc=0.3*sigmal+7.;
560   if (qtc>nah) qtc=nah;
561   fit.SetSignal(qtc,"QTC");
562   fit.SetSignal(sigmal,"SIGMAL");
563   te->SetFitDetails(fit);
564   if (qtc>qmax) qmax=qtc;
565  }
566
567  // Perform selection on Q value in case of multiple track candidates
568  for (Int_t jtc=0; jtc<nte; jtc++)
569  {
570   te=(AliTrack*)tes.At(jtc);
571   if (!te) continue;
572   sx1=(AliSignal*)te->GetFitDetails();
573   qtc=-1;
574   if (sx1) qtc=sx1->GetSignal("QTC");
575   if (qtc<0.7*qmax)
576   {
577    temap.RemoveObjects(te);
578    tes.RemoveAt(jtc);
579    delete te;
580   }
581  } 
582  tes.Compress();
583  nte=tes.GetEntries();
584
585  if (!nte) return;
586
587  // Order the track candidates w.r.t. decreasing number of associated hits
588  TObjArray* ordered=0;
589  ordered=evt->SortTracks(-1,&tes);
590  TObjArray tcs(*ordered);
591
592  // Cluster track candidates within a certain opening angle into jets.
593  // Also the relative direction of the both r0's of the track candidates
594  // should be within a certain opening angle w.r.t. the starting track direction,
595  // unless the distance between the two r0's is below a certain maximum.
596  // The latter prevents clustering of (nearly) parallel track candidates
597  // crossing the detector a very different locations (e.g. muon bundles).
598  // The average r0 and t0 of the constituent tracks will be taken as the
599  // jet reference point. 
600  TObjArray jets;
601  jets.SetOwner();
602  AliTrack* te2=0;
603  Float_t ang=0;
604  AliSample pos;
605  AliSample time;
606  Float_t vec[3],err[3];
607  Float_t edist=0;
608  for (Int_t jtc1=0; jtc1<nte; jtc1++)
609  {
610   te=(AliTrack*)tcs.At(jtc1);
611   if (!te) continue;
612   AliPosition* x1=te->GetReferencePoint();
613   if (!x1) continue;
614   AliTimestamp* ts1=x1->GetTimestamp();
615   if (!ts1) continue;
616   AliJet* jx=new AliJet();
617   jx->AddTrack(te);
618   pos.Reset();
619   time.Reset();
620   x1->GetPosition(vec,"car");
621   pos.Enter(vec[0],vec[1],vec[2]);
622   t0=evt->GetDifference(ts1,"ns");
623   time.Enter(t0);
624   for (Int_t jtc2=0; jtc2<nte; jtc2++)
625   {
626    if (jtc2==jtc1) continue;
627    te2=(AliTrack*)tcs.At(jtc2);
628    if (!te2) continue;
629    ang=te->GetOpeningAngle(*te2,"deg");
630    if (ang<fTangmax)
631    {
632     AliPosition* x2=te2->GetReferencePoint();
633     if (!x2) continue;
634     AliTimestamp* ts2=x2->GetTimestamp();
635     if (!ts2) continue;
636     dist=x1->GetDistance(x2);
637     edist=x1->GetResultError();
638     dt=ts1->GetDifference(ts2,"ns");
639     if (dist>0)
640     {
641      r12=(*x2)-(*x1);
642      if (dt<0) r12*=-1.;
643      ang=te->GetOpeningAngle(r12,"deg");
644      if (ang<fRtangmax || dist<(fRtdmax+edist))
645      {
646       x2->GetPosition(vec,"car");
647       pos.Enter(vec[0],vec[1],vec[2]);
648       t0=evt->GetDifference(ts2,"ns");
649       time.Enter(t0);
650       jx->AddTrack(te2);
651      }
652     }
653    }
654   }
655
656   // Set the reference point data for this jet
657   for (Int_t j=1; j<=3; j++)
658   {
659    vec[j-1]=pos.GetMean(j);
660    err[j-1]=pos.GetSigma(j);
661   }
662   r0.SetPosition(vec,"car");
663   r0.SetPositionErrors(err,"car");
664   r0.SetTimestamp((AliTimestamp&)*evt);
665   AliTimestamp* jt0=r0.GetTimestamp();
666   t0=time.GetMean(1);
667   jt0->Add(0,0,(int)t0);
668   jx->SetReferencePoint(r0);
669
670   // Store this jet for further processing if ntracks>1
671   if (jx->GetNtracks() > 1 || fTangmax<=0 || fRtangmax<=0)
672   {
673    jets.Add(jx);
674   }
675   else // Only keep single-track jets which have qtc=qmax 
676   {
677    sx1=(AliSignal*)te->GetFitDetails();
678    qtc=-1;
679    if (sx1) qtc=sx1->GetSignal("QTC");
680    if (qtc>=(qmax-1.e-10))
681    {
682     jets.Add(jx);
683    }
684    else
685    {
686     delete jx;
687    }
688   }
689  }
690  Int_t njets=jets.GetEntries();
691
692  if (!njets) return;
693
694  // Order the jets w.r.t. decreasing number of tracks
695  ordered=evt->SortJets(-1,&jets);
696  TObjArray jets2(*ordered);
697
698  // Merge jets within a certain opening to provide the final track(s).
699  AliJet* jx1=0;
700  AliJet* jx2=0;
701  if (fJangmax>0)
702  {
703   for (Int_t jet1=0; jet1<njets; jet1++)
704   {
705    jx1=(AliJet*)jets2.At(jet1);
706    if (!jx1) continue;
707    AliPosition* x1=jx1->GetReferencePoint();
708    if (!x1) continue;
709    AliTimestamp* ts1=x1->GetTimestamp();
710    if (!ts1) continue;
711    pos.Reset();
712    time.Reset();
713    x1->GetPosition(vec,"car");
714    pos.Enter(vec[0],vec[1],vec[2]);
715    t0=evt->GetDifference(ts1,"ns");
716    time.Enter(t0);
717    for (Int_t jet2=jet1+1; jet2<njets; jet2++)
718    {
719     jx2=(AliJet*)jets2.At(jet2);
720     if (!jx2) continue;
721     AliPosition* x2=jx2->GetReferencePoint();
722     if (!x2) continue;
723     AliTimestamp* ts2=x2->GetTimestamp();
724     if (!ts2) continue;
725     ang=jx1->GetOpeningAngle(*jx2,"deg");
726     if (ang<fJangmax)
727     {
728      dist=x1->GetDistance(x2);
729      edist=x1->GetResultError();
730      dt=ts1->GetDifference(ts2,"ns");
731      r12=(*x2)-(*x1);
732      if (dt<0) r12*=-1.;
733      ang=jx1->GetOpeningAngle(r12,"deg");
734      if (ang<fRjangmax || dist<(fRjdmax+edist))
735      {
736       x2->GetPosition(vec,"car");
737       pos.Enter(vec[0],vec[1],vec[2]);
738       t0=evt->GetDifference(ts2,"ns");
739       time.Enter(t0);
740       for (Int_t jtk=1; jtk<=jx2->GetNtracks(); jtk++)
741       {
742        te=jx2->GetTrack(jtk);
743        if (!te) continue;
744        jx1->AddTrack(te);
745       }
746       jets2.RemoveAt(jet2);
747      }
748     }
749    }
750    // Set the reference point data for this jet
751    for (Int_t k=1; k<=3; k++)
752    {
753     vec[k-1]=pos.GetMean(k);
754     err[k-1]=pos.GetSigma(k);
755    }
756    r0.SetPosition(vec,"car");
757    r0.SetPositionErrors(err,"car");
758    r0.SetTimestamp((AliTimestamp&)*evt);
759    AliTimestamp* jt0=r0.GetTimestamp();
760    t0=time.GetMean(1);
761    jt0->Add(0,0,(int)t0);
762    jx1->SetReferencePoint(r0);
763   }
764   jets2.Compress();
765   njets=jets2.GetEntries();
766  }
767
768  // Store every jet as a reconstructed track in the event structure.
769  // The jet 3-momentum (normalised to 1) and reference point
770  // (i.e.the average r0 and t0 of the constituent tracks) will make up
771  // the final track parameters.
772  // All the associated hits of all the constituent tracks of the jet
773  // will be associated to the final track.
774  // In case the jet angular separation was set <0, only the jet with
775  // the maximum number of tracks (i.e. the first one in the array)
776  // will be used to form a track. This will allow comparison with
777  // the standard Sieglinde processing.
778  AliTrack t; 
779  t.SetNameTitle(fTrackname.Data(),"IceDwalk direct walk track");
780  t.SetCharge(fCharge);
781  for (Int_t jet=0; jet<njets; jet++)
782  {
783   AliJet* jx=(AliJet*)jets2.At(jet);
784   if (!jx) continue;
785   AliPosition* ref=jx->GetReferencePoint();
786   if (!ref) continue;
787   evt->AddTrack(t);
788   AliTrack* trk=evt->GetTrack(evt->GetNtracks());
789   if (!trk) continue;
790   trk->SetId(evt->GetNtracks(1)+1);
791   p=jx->Get3Momentum();
792   p/=p.GetNorm();
793   trk->Set3Momentum(p);
794   trk->SetReferencePoint(*ref);
795   AliTimestamp* tt0=ref->GetTimestamp();
796   if (tt0) trk->SetTimestamp(*tt0);
797   for (Int_t jt=1; jt<=jx->GetNtracks(); jt++)
798   {
799    AliTrack* tx=jx->GetTrack(jt);
800    if (!tx) continue;
801    for (Int_t is=1; is<=tx->GetNsignals(); is++)
802    {
803     sx1=tx->GetSignal(is);
804     if (sx1) sx1->AddLink(trk);
805    }
806   }
807
808   // Only take the jet with the maximum number of tracks
809   // (i.e. the first jet in the list) when the user had selected
810   // this reconstruction mode.
811   if (fJangmax<0) break;
812  }
813 }
814 ///////////////////////////////////////////////////////////////////////////