]> git.uio.no Git - u/mrichter/AliRoot.git/blob - RALICE/icepack/IceDwalk.cxx
683601553420687535ed8e3c34e6999c1c661890
[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 // A further extension of the original Amanda method is the separate treatment
30 // of the phase and group velocities as introduced in collaboration with
31 // George Japaridze (Clark Atlanta University, USA) which will provide more
32 // accurate time residuals due to the different velocities of the Cerenkov
33 // wave front (v_phase) and the actually detected photons (v_group).
34 // This distinction between v_phase and v_group can be (de)activated via the
35 // memberfunction SetVgroupUsage(). By default the distinction between v_phase
36 // and v_group is activated in the constructor of this class.
37 // To prevent waisting CPU time in trying to reconstruct (high-energy) cascade
38 // events, or to select specifically reconstruction of low multiplicity events,
39 // the user may invoke the memberfunctions SetMaxModA() and SetMinModA().
40 // This allows selection of events for processing with a certain maximum and/or
41 // minimum number of good Amanda OMs firing.
42 // By default the minimum and maximum are set to 0 and 999, respectively,
43 // in the constructor, which implies no multiplicity selection. 
44 // The maximum number of good hits per Amanda OM to be used for the reconstruction
45 // can be specified via the memberfunction SetMaxHitsA().
46 // By default only the first good hit of each Amanda OM is used.
47 // Note that when all the good hits of an OM are used, this may lead to large
48 // processing time in case many noise and/or afterpulse signals are not
49 // recognised by the hit cleaning procedure.
50 //
51 // Information about the actual parameter settings can be found in the event
52 // structure itself via the device named "IceDwalk".
53 //
54 // The various reconstruction steps are summarised as follows :
55 //
56 // 1) Construction of track elements (TE's).
57 //    A track element is a straight line connecting two hits that
58 //    appeared at some minimum distance d and within some maximum
59 //    time difference dt, according to eq. (20) of the NIM article.
60 //    The default value for d is 75 meter, but this can be modified
61 //    via the memberfunction SetDmin().
62 //    By default dt=(hit distance)/c but an additional time margin
63 //    may be specified via the memberfunction SetDtmarg().    
64 //    The reference point r0 of the TE is taken as the center between
65 //    the two hit positions and the TE timestamp t0 at the position r0
66 //    is taken as the IceEvent timestamp increased by the average of the
67 //    two hit times. So, all timestamps contain the overall IceEvent
68 //    timestamp as a basis. This means that time differences can be
69 //    obtained via the AliTimestamp facilities (supporting upto picosecond
70 //    precision when available).
71 //    The TE direction is given by the relative position of the two hits.
72 //
73 // 2) Each TE will obtain so called associated hits.
74 //    A hit is associated to a TE when it fulfills both the conditions
75 //
76 //      -30 < tres < 300 ns
77 //      dhit/lambda < F
78 //
79 //    tres   : time residual
80 //             Difference between the observed hit time and the time expected
81 //             for a direct photon hit.     
82 //    dhit   : Distance between the hit and the TE
83 //    lambda : Photon scattering length in ice
84 //
85 //    By default F is set to 2, but this can be modified via the memberfunction
86 //    SetMaxDhit(). 
87 //
88 // 3) Construction of track candidates (TC's).
89 //    These are TE's that fulfill both the conditions
90 //
91 //     nah >= 1
92 //     qtc >= 0.8*qtcmax
93 //
94 //    where we have defined :
95 //
96 //    nah    : Number of associated hits for the specific TE.
97 //    qtc    : The track quality number (see hereafter).
98 //    qtcmax : Maximum quality number encountered for the TE's.
99 //
100 //    The track quality number qtc is defined as follows :
101 //
102 //     qtc=nah*(term1+term2)-term3-term4-term5
103 //
104 //    here we have defined :
105 //
106 //    term1=2*spread/span
107 //    term2=2*spreadL/spanL
108 //    term3=|spread-expspread|/spread
109 //    term4=|spreadL-expspreadL|/spreadL
110 //    term5=|medianT|/spreadT
111 //
112 //    The central observables here are the projected positions X on the track
113 //    of the various associated hits w.r.t. the track reference point r0.
114 //    Note that X can be negative as well as positive.
115 //    Therefore we also introduce XL=|X|. 
116 //
117 //    span       : max(X)-min(X)
118 //    spanL      : max(XL)-min(XL)
119 //    Xmedian    : median of X
120 //    XmedianL   : median of XL
121 //    spread     : < |X-Xmedian| >
122 //    spreadL    : < |XL-XmedianL| >
123 //    expspread  : expected spread in X for a flat distribution of nah hits over span
124 //    expspreadL : expected spread in XL for a flat distribution of nah hits over spanL
125 //    medianT    : median of tres
126 //    spreadT    : < |tres-medianT| >
127 //
128 //    However, if |Xmedian| > span/2 we set qtc=0 in order to always require
129 //    projected hits to appear on both sides of r0 on the track.
130 //
131 //    Note : The qtc quality number is used to define the norm of the momentum
132 //           of the track candidate. As such it serves as a weight for the jet
133 //           momentum (direction) after clustering of the TC's and lateron
134 //           merging of the jets (see hereafter).
135 //
136 // 4) The remaining track candidates are clustered into jets when their directions
137 //    are within a certain maximum opening angle.
138 //    In addition a track candidate must within a certain maximum distance
139 //    of the jet starting TC in order to get clustered. 
140 //    The latter criterion prevents clustering of (nearly) parallel track candidates
141 //    crossing the detector a very different locations (e.g. muon bundles).
142 //    The default maximum track opening angle is 15 degrees, but can be modified
143 //    via the SetTangmax memberfunction.
144 //    The default maximum track distance is 20 meters, but can be modified
145 //    via the SetTdistmax memberfunction. This memberfunction also allows to
146 //    specify whether the distance is determined within the detector volume or not.
147 //
148 //    The average of all the r0 and t0 values of the constituent TC's
149 //    of the jet will provide the r0 and t0 (i.e. reference point) of the jet.
150 //
151 //    The jet total momentum consists of the vector sum of the momenta of the
152 //    constituent TC's. This implies that the qtc quality numbers of the various
153 //    TC's define a weight for each track in the construction of the jet direction.
154 //    In addition it means that the total jet momentum represents the sum of the
155 //    qtc quality numbers of the constituent TC's weighted by the opening angles
156 //    between the various TC's.  
157 //    As such each jet is given an absolute quality number defined as :
158 //
159 //      qtcjet=|jet momentum|/ntracks 
160 //
161 //    This jet quality number is refined on basis of the number of hits
162 //    associated to the jet as :
163 //
164 //      qtcjet=qtcjet+0.2*(nah-nahmax)
165 //
166 //    where we have defined :
167 //
168 //    nah    : Number of associated hits for the specific jet.
169 //    nahmax : Maximum number of associated hits encountered for the jets.
170 //
171 //    This qtcjet value is then used to order the various jets w.r.t.
172 //    decreasing qtcjet quality number.
173 //
174 //    Note : The qtcjet value is stored as "energy" of the jet, such that
175 //           it is always available for each jet and can also be used for
176 //           ordering the jets according to this value using the generic
177 //           AliEvent::SortJets() facility. 
178 //
179 // 5) The jets (after having been ordered w.r.t. decreasing qtcjet value)
180 //    are merged when their directions are within a certain maximum opening angle.
181 //    In addition a jet must within a certain maximum distance of the starting jet
182 //    in order to get merged. 
183 //    The latter criterion prevents merging of (nearly) parallel tracks/jets
184 //    crossing the detector a very different locations (e.g. muon bundles).
185 //    The jet ordering before the merging process is essential, since the starting jet
186 //    will "eat up" the jets that will be merged into it. 
187 //    The jet ordering ensures that the jet with the highest quality number will
188 //    always initiate the merging process.
189 //    The default maximum opening angle is half the TC maximum opening angle,
190 //    but can be modified via the SetJangmax memberfunction. This memberfunction
191 //    also allows to specify whether jet merging will be performed iteratively or not.
192 //    In case iteration has been activated, the jet ordering is performed after each
193 //    iteration step. This has to be done because since the quality numbers of the
194 //    resulting merged jets have been automatically updated in the merging process.
195 //    
196 //    The default maximum jet distance is 30 meters, but can be modified
197 //    via the SetJdistmax memberfunction. This memberfunction also allows to
198 //    specify whether the distance is determined within the detector volume or not.
199 //
200 //    Note : Setting the maximum jet opening angle to <=0 will prevent
201 //           the merging of jets.
202 //
203 //    The average of all the r0 and t0 values of the merged jets will provide
204 //    the r0 and t0 (i.e. reference point) of the final jet.
205 //
206 // 6) The remaining (merged) jets are ordered w.r.t. decreasing jet quality number.
207 //    As such the jet with the highest quality number will be the first one
208 //    in the list, which will result in the fact that the final tracks are also
209 //    ordered w.r.t. decreasing quality number, as outlined hereafter.
210 //    Each remaining jet will provide the parameters (e.g. direction)
211 //    for a reconstructed track.
212 //    The track 3-momentum is set to the total jet 3-momentum, normalised
213 //    to 1 GeV. The mass and charge of the track are left 0.
214 //    The reference point data of the jet will provide the r0 and t0
215 //    (i.e. reference point) of the track.
216 //
217 //    All these tracks will be stored in the IceEvent structure with as
218 //    default "IceDwalk" as the name of the track.
219 //    This track name identifier can be modified by the user via the
220 //    SetTrackName() memberfunction. This will allow unique identification
221 //    of tracks which are produced when re-processing existing data with
222 //    different criteria.
223 //    By default the charge of the produced tracks is set to 0, since
224 //    no distinction can be made between positive or negative tracks.
225 //    However, the user can define the track charge by invokation
226 //    of the memberfunction SetCharge().
227 //    This facility may be used to distinguish tracks produced by the
228 //    various reconstruction algorithms in a (3D) colour display
229 //    (see the class AliHelix for further details).  
230 //
231 //    Note : In case the maximum jet opening angle was specified <0,
232 //           only the jet with the highest quality number will appear
233 //           as a reconstructed track in the IceEvent structure.
234 //           This will allow comparison with the standard Sieglinde
235 //           single track direct walk reconstruction results. 
236 //    
237 // For further details the user is referred to NIM A524 (2004) 169.
238 //
239 // Note : This algorithm works best on data which has been calibrated
240 //        (IceCalibrate), cross talk corrected (IceXtalk) and cleaned
241 //        from noise hits etc. (IceCleanHits).
242 //
243 //--- Author: Nick van Eijndhoven 07-oct-2005 Utrecht University
244 //- Modified: NvE $Date$ Utrecht University
245 ///////////////////////////////////////////////////////////////////////////
246  
247 #include "IceDwalk.h"
248 #include "Riostream.h"
249
250 ClassImp(IceDwalk) // Class implementation to enable ROOT I/O
251
252 IceDwalk::IceDwalk(const char* name,const char* title) : TTask(name,title)
253 {
254 // Default constructor.
255 // The various reconstruction parameters are initialised to the values
256 // as mentioned in NIM A524 (2004) 179-180.
257 // The newly introduced angular separation parameter for jet merging
258 // is initialised as half the value of the angular separation parameter
259 // for track candidate clustering.    
260  fDmin=75;
261  fDtmarg=0;
262  fMaxdhit=2;
263  fTangmax=15;
264  fTdistmax=20;
265  fTinvol=1;
266  fJangmax=fTangmax/2.;
267  fJiterate=1;
268  fJdistmax=30;
269  fJinvol=1;
270  fMaxmodA=999;
271  fMinmodA=0;
272  fMaxhitsA=1;
273  fVgroup=1;
274  fTrackname="IceDwalk";
275  fCharge=0;
276 }
277 ///////////////////////////////////////////////////////////////////////////
278 IceDwalk::~IceDwalk()
279 {
280 // Default destructor.
281 }
282 ///////////////////////////////////////////////////////////////////////////
283 void IceDwalk::SetDmin(Float_t d)
284 {
285 // Set minimum hit distance (in m) to form a track element.
286 // In the constructor the default has been set to 75 meter.
287  fDmin=d;
288 }
289 ///////////////////////////////////////////////////////////////////////////
290 void IceDwalk::SetDtmarg(Int_t dt)
291 {
292 // Set maximum hit time difference margin (in ns) for track elements.
293 // In the constructor the default has been set to 0 ns.
294  fDtmarg=dt;
295 }
296 ///////////////////////////////////////////////////////////////////////////
297 void IceDwalk::SetMaxDhit(Float_t d)
298 {
299 // Set maximum distance (in scattering length) for a hit to get associated.
300 // In the constructor the default has been set to 2 lambda_scat.
301  fMaxdhit=d;
302 }
303 ///////////////////////////////////////////////////////////////////////////
304 void IceDwalk::SetTangmax(Float_t ang)
305 {
306 // Set maximum angular separation (in deg) for track candidate clustering
307 // into jets.
308 // In the constructor the default has been set to 15 deg, in accordance
309 // to NIM A524 (2004) 180.
310 //
311 // Note : This function also sets automatically the value of the maximum
312 //        angular separation for jet merging into 1 single track to ang/2.
313 //        In order to specify a different max. jet merging separation angle,
314 //        one has to invoke the memberfunction SetJangmax afterwards.
315  
316  fTangmax=ang;
317  fJangmax=ang/2.;
318 }
319 ///////////////////////////////////////////////////////////////////////////
320 void IceDwalk::SetTdistmax(Float_t d,Int_t invol)
321 {
322 // Set maximum distance (in m) of the two track candidates in the track
323 // clustering process.
324 // The distance between the two tracks can be determined restricted to the
325 // detector volume (invol=1) or in the overall space (invol=0).  
326 // The former will prevent clustering of (nearly) parallel tracks which cross
327 // the detector volume at very different locations, whereas the latter will
328 // enable clustering of tracks with a common location of origin (e.g. muon
329 // bundles from an air shower) even if they cross the detector volume at
330 // very different locations. 
331 // At invokation of this memberfunction the default is invol=1.
332 // In the constructor the default has been set to 20 meter with invol=1.
333  
334  fTdistmax=d;
335  fTinvol=invol;
336 }
337 ///////////////////////////////////////////////////////////////////////////
338 void IceDwalk::SetJangmax(Float_t ang,Int_t iter)
339 {
340 // Set angular separation (in deg) within which jets are merged into 1
341 // single track.
342 // The merging process is a dynamic procedure and can be carried out by
343 // iteration (iter=1) until no further merging of the various jets occurs anymore.
344 // However, by specification of iter=0 the user can also select to go only
345 // once through all the jet combinations to check for mergers.
346 // For large events the latter will in general result in more track candidates.  
347 // At invokation of this memberfunction the default is iter=1.
348 // In the constructor the default angle has been set 7.5 deg, being half
349 // of the value of the default track candidate clustering separation angle.
350 // The iteration flag was set to 1 in the constructor.
351 //
352 // Notes :
353 // -------
354 // 1)  Setting ang=0 will prevent jet merging.
355 //     Consequently, every jet will appear as a separate track in the
356 //     reconstruction result.  
357 // 2)  Setting ang<0 will prevent jet merging.
358 //     In addition, only the jet with the maximum number of tracks will
359 //     appear as a track in the reconstruction result.
360 //     This situation resembles the standard Sieglinde direct walk processing
361 //     and as such can be used to perform comparison studies.
362
363  fJangmax=ang;
364  fJiterate=iter;
365 }
366 ///////////////////////////////////////////////////////////////////////////
367 void IceDwalk::SetJdistmax(Float_t d,Int_t invol)
368 {
369 // Set maximum distance (in m) of the two jets in the jet merging process.
370 // The distance between the two jets can be determined restricted to the
371 // detector volume (invol=1) or in the overall space (invol=0).  
372 // The former will prevent clustering of (nearly) parallel tracks which cross
373 // the detector volume at very different locations, whereas the latter will
374 // enable clustering of tracks with a common location of origin (e.g. muon
375 // bundles from an air shower) even if they cross the detector volume at
376 // very different locations. 
377 // At invokation of this memberfunction the default is invol=1.
378 // In the constructor the default has been set to 30 meter with invol=1.
379  
380  fJdistmax=d;
381  fJinvol=invol;
382 }
383 ///////////////////////////////////////////////////////////////////////////
384 void IceDwalk::SetMaxModA(Int_t nmax)
385 {
386 // Set the maximum number of good Amanda modules that may have fired
387 // in order to process this event.
388 // This allows suppression of processing (high-energy) cascade events
389 // with this direct walk tracking to prevent waisting cpu time for cases
390 // in which tracking doesn't make sense anyhow.
391 // Furthermore it allows selection of low multiplicity events for processing.
392 // By default the maximum number of Amanda modules is set to 999 in the ctor,
393 // which implies no selection on maximum module multiplicity.
394 // See also the memberfunction SetMinModA().
395  fMaxmodA=nmax;
396 }
397 ///////////////////////////////////////////////////////////////////////////
398 void IceDwalk::SetMinModA(Int_t nmin)
399 {
400 // Set the minimum number of good Amanda modules that must have fired
401 // in order to process this event.
402 // This allows selection of a minimal multiplicity for events to be processed.
403 // By default the minimum number of Amanda modules is set to 0 in the ctor,
404 // which implies no selection on minimum module multiplicity.
405 // See also the memberfunction SetMaxModA().
406  fMinmodA=nmin;
407 }
408 ///////////////////////////////////////////////////////////////////////////
409 void IceDwalk::SetMaxHitsA(Int_t nmax)
410 {
411 // Set the maximum number of good hits per Amanda module to be processed.
412 //
413 // Special values :
414 // nmax = 0 : No maximum limit set; all good hits will be processed
415 //      < 0 : No hits will be processed
416 //
417 // In case the user selects a maximum number of good hits per module, all the
418 // hits of each module will be ordered w.r.t. increasing hit time (LE).
419 // This allows selection of processing e.g. only the first hits etc...
420 // By default the maximum number of hits per Amanda modules is set to 1 in the ctor,
421 // which implies processing only the first good hit of each Amanda OM.
422  fMaxhitsA=nmax;
423 }
424 ///////////////////////////////////////////////////////////////////////////
425 void IceDwalk::SetVgroupUsage(Int_t flag)
426 {
427 // (De)activate the distinction between v_phase and v_group of the Cherenkov light.
428 //
429 // flag = 0 : No distinction between v_phase and v_group
430 //      = 1 : Separate treatment of v_phase and v_group
431 //
432 // By default the distinction between v_phase and v_group is activated
433 // in the constructor of this class.
434  fVgroup=flag;
435 }
436 ///////////////////////////////////////////////////////////////////////////
437 void IceDwalk::SetTrackName(TString s)
438 {
439 // Set (alternative) name identifier for the produced first guess tracks.
440 // This allows unique identification of (newly) produced direct walk tracks
441 // in case of re-processing of existing data with different criteria.
442 // By default the produced first guess tracks have the name "IceDwalk"
443 // which is set in the constructor of this class.
444  fTrackname=s;
445 }
446 ///////////////////////////////////////////////////////////////////////////
447 void IceDwalk::SetCharge(Float_t charge)
448 {
449 // Set user defined charge for the produced first guess tracks.
450 // This allows identification of these tracks on color displays.
451 // By default the produced first guess tracks have charge=0
452 // which is set in the constructor of this class.
453  fCharge=charge;
454 }
455 ///////////////////////////////////////////////////////////////////////////
456 void IceDwalk::Exec(Option_t* opt)
457 {
458 // Implementation of the direct walk track reconstruction.
459
460  TString name=opt;
461  AliJob* parent=(AliJob*)(gROOT->GetListOfTasks()->FindObject(name.Data()));
462
463  if (!parent) return;
464
465  IceEvent* evt=(IceEvent*)parent->GetObject("IceEvent");
466  if (!evt) return;
467
468  // Enter the reco parameters as a device in the event
469  AliSignal params;
470  params.SetNameTitle("IceDwalk","IceDwalk reco parameters");
471  params.SetSlotName("Dmin",1);
472  params.SetSlotName("Dtmarg",2);
473  params.SetSlotName("Maxdhit",3);
474  params.SetSlotName("Tangmax",4);
475  params.SetSlotName("Tdistmax",5);
476  params.SetSlotName("Tinvol",6);
477  params.SetSlotName("Jangmax",7);
478  params.SetSlotName("Jiterate",8);
479  params.SetSlotName("Jdistmax",9);
480  params.SetSlotName("Jinvol",10);
481  params.SetSlotName("MaxmodA",11);
482  params.SetSlotName("MinmodA",12);
483  params.SetSlotName("MaxhitsA",13);
484  params.SetSlotName("Vgroup",14);
485
486  params.SetSignal(fDmin,1);
487  params.SetSignal(fDtmarg,2);
488  params.SetSignal(fMaxdhit,3);
489  params.SetSignal(fTangmax,4);
490  params.SetSignal(fTdistmax,5);
491  params.SetSignal(fTinvol,6);
492  params.SetSignal(fJangmax,7);
493  params.SetSignal(fJiterate,8);
494  params.SetSignal(fJdistmax,9);
495  params.SetSignal(fJinvol,10);
496  params.SetSignal(fMaxmodA,11);
497  params.SetSignal(fMinmodA,12);
498  params.SetSignal(fMaxhitsA,13);
499  params.SetSignal(fVgroup,14);
500
501  evt->AddDevice(params);
502
503  if (fMaxhitsA<0) return;
504
505  // Fetch all fired Amanda OMs for this event
506  TObjArray* aoms=evt->GetDevices("IceAOM");
507  if (!aoms) return;
508  Int_t naoms=aoms->GetEntries();
509  if (!naoms) return;
510
511  // Check for the minimum and/or maximum number of good fired Amanda OMs
512  Int_t ngood=0;
513  for (Int_t iom=0; iom<naoms; iom++)
514  {
515   IceGOM* omx=(IceGOM*)aoms->At(iom);
516   if (!omx) continue;
517   if (omx->GetDeadValue("ADC") || omx->GetDeadValue("LE") || omx->GetDeadValue("TOT")) continue;
518   ngood++;
519  } 
520  if (ngood<fMinmodA || ngood>fMaxmodA) return;
521
522  const Float_t pi=acos(-1.);
523  const Float_t c=0.299792458;         // Light speed in vacuum in meters per ns
524  const Float_t npice=1.31768387;      // Phase refractive index (c/v_phase) of ice
525  const Float_t ngice=1.35075806;      // Group refractive index (c/v_group) of ice
526  const Float_t lambda=33.3;           // Light scattering length in ice
527  const Float_t thetac=acos(1./npice); // Cherenkov angle (in radians)
528
529  // Angular reduction of complement of thetac due to v_phase and v_group difference
530  Float_t alphac=0;
531  if (fVgroup) alphac=atan((1.-npice/ngice)/sqrt(npice*npice-1.));
532
533  // Storage of track elements.
534  TObjArray tes;
535  tes.SetOwner();
536
537  AliPosition r1;
538  AliPosition r2;
539  Ali3Vector r12;
540  Ali3Vector rsum;
541  AliPosition r0;
542  TObjArray hits1;
543  TObjArray hits2;
544  Int_t nh1,nh2;
545  AliSignal* sx1=0;
546  AliSignal* sx2=0;
547  Float_t dist=0,dist2=0;
548  Float_t t1,t2,dt,t0;
549  Float_t dtmax;
550  TObjArray hits;
551  TObjArray* ordered;
552
553  // Check the hits of Amanda OM pairs for posible track elements.
554  // Also all the good hits are stored in the meantime (to save CPU time)
555  // for hit association with the various track elements lateron.
556  AliTrack* te=0;
557  for (Int_t i1=0; i1<naoms; i1++) // First OM of the pair
558  {
559   IceGOM* omx1=(IceGOM*)aoms->At(i1);
560   if (!omx1) continue;
561   if (omx1->GetDeadValue("LE")) continue;
562   r1=omx1->GetPosition();
563   // Select all the good hits of this first OM
564   hits1.Clear();
565   // Determine the max. number of hits to be processed for this OM
566   ordered=0;
567   if (fMaxhitsA>0 && omx1->GetNhits()>fMaxhitsA) ordered=omx1->SortHits("LE",1,0,7);
568   nh1=0;
569   for (Int_t j1=1; j1<=omx1->GetNhits(); j1++)
570   {
571    if (ordered)
572    {
573     if (nh1>=fMaxhitsA) break;
574     sx1=(AliSignal*)ordered->At(j1-1);
575    }
576    else
577    {
578     sx1=omx1->GetHit(j1);
579    }
580    if (!sx1) continue;
581    if (sx1->GetDeadValue("ADC") || sx1->GetDeadValue("LE") || sx1->GetDeadValue("TOT")) continue;
582    hits1.Add(sx1);
583    // Also store all good hits in the total hit array
584    hits.Add(sx1);
585    nh1++;
586   }
587
588   // No further pair to be formed with the last OM in the list 
589   if (i1==(naoms-1)) break;
590
591   nh1=hits1.GetEntries();
592   if (!nh1) continue;
593
594   for (Int_t i2=i1+1; i2<naoms; i2++) // Second OM of the pair
595   {
596    IceGOM* omx2=(IceGOM*)aoms->At(i2);
597    if (!omx2) continue;
598    if (omx2->GetDeadValue("LE")) continue;
599    r2=omx2->GetPosition();
600    r12=r2-r1;
601    dist=r12.GetNorm();
602
603    if (dist<=fDmin) continue;
604
605    // Select all the good hits of this second OM
606    hits2.Clear();
607    // Determine the max. number of hits to be processed for this OM
608    ordered=0;
609    if (fMaxhitsA>0 && omx2->GetNhits()>fMaxhitsA) ordered=omx2->SortHits("LE",1,0,7);
610    nh2=0;
611    for (Int_t j2=1; j2<=omx2->GetNhits(); j2++)
612    {
613     if (ordered)
614     {
615      if (nh2>=fMaxhitsA) break;
616      sx2=(AliSignal*)ordered->At(j2-1);
617     }
618     else
619     {
620      sx2=omx2->GetHit(j2);
621     }
622     if (!sx2) continue;
623     if (sx2->GetDeadValue("ADC") || sx2->GetDeadValue("LE") || sx2->GetDeadValue("TOT")) continue;
624     hits2.Add(sx2);
625     nh2++;
626    }
627  
628    nh2=hits2.GetEntries();
629    if (!nh2) continue;
630
631    // Position r0 in between the two OMs and normalised relative direction r12
632    rsum=(r1+r2)/2.;
633    r0.SetPosition((Ali3Vector&)rsum);
634    r12/=dist;
635
636    // Check all hit pair combinations of these two OMs for possible track elements  
637    dtmax=dist/c+float(fDtmarg);
638    for (Int_t ih1=0; ih1<nh1; ih1++) // Hits of first OM
639    {
640     sx1=(AliSignal*)hits1.At(ih1);
641     if (!sx1) continue;
642     for (Int_t ih2=0; ih2<nh2; ih2++) // Hits of second OM
643     {
644      sx2=(AliSignal*)hits2.At(ih2);
645      if (!sx2) continue;
646      t1=sx1->GetSignal("LE",7);
647      t2=sx2->GetSignal("LE",7);
648      dt=t2-t1;
649      t0=(t1+t2)/2.;
650
651      if (fabs(dt)>=dtmax) continue;
652
653      te=new AliTrack();
654      tes.Add(te);
655      if (dt<0) r12*=-1.;
656      r0.SetTimestamp((AliTimestamp&)*evt);
657      AliTimestamp* tsx=r0.GetTimestamp();
658      tsx->Add(0,0,(int)t0);
659      te->SetReferencePoint(r0);
660      te->Set3Momentum(r12);
661     }
662    }
663   } // end of loop over the second OM of the pair
664  } // end of loop over first OM of the pair
665
666  // Association of hits to the various track elements.
667  Int_t nte=tes.GetEntries();
668  Int_t nh=hits.GetEntries();
669  Float_t d=0;
670  Ali3Vector p;
671  Float_t tgeo,tres;
672  AliSample levers;      // Statistics of the assoc. hit lever arms
673  levers.SetStoreMode(1);// Enable median calculation
674  AliSample hprojs;      // Statistics of the assoc. hit position projections on the track w.r.t. r0
675  hprojs.SetStoreMode(1);// Enable median calculation
676  AliSample times;       // Statistics of the time residuals of the associated hits
677  times.SetStoreMode(1); // Enable median calculation
678  AliSignal fit;         // Storage of Q value etc... for each track candidate
679  fit.AddNamedSlot("QTC");
680  fit.AddNamedSlot("SpanL");
681  fit.AddNamedSlot("MedianL");
682  fit.AddNamedSlot("MeanL");
683  fit.AddNamedSlot("SigmaL");
684  fit.AddNamedSlot("SpreadL");
685  fit.AddNamedSlot("ExpSpreadL");
686  fit.AddNamedSlot("Span");
687  fit.AddNamedSlot("Median");
688  fit.AddNamedSlot("Mean");
689  fit.AddNamedSlot("Sigma");
690  fit.AddNamedSlot("Spread");
691  fit.AddNamedSlot("ExpSpread");
692  fit.AddNamedSlot("MedianT");
693  fit.AddNamedSlot("MeanT");
694  fit.AddNamedSlot("SigmaT");
695  fit.AddNamedSlot("SpreadT");
696  fit.AddNamedSlot("term1");
697  fit.AddNamedSlot("term2");
698  fit.AddNamedSlot("term3");
699  fit.AddNamedSlot("term4");
700  fit.AddNamedSlot("term5");
701  Float_t qtc=0,qmax=0;
702  Int_t nah,nahmax=0; // Number of associated hits for a certain TE
703  Float_t lmin,lmax,spanl,medianl,meanl,sigmal,spreadl,expspreadl;
704  Float_t hproj,hprojmin,hprojmax,span,median,mean,sigma,spread,expspread;
705  Float_t mediant,meant,sigmat,spreadt;
706  Float_t term1,term2,term3,term4,term5;
707  for (Int_t jte=0; jte<nte; jte++)
708  {
709   te=(AliTrack*)tes.At(jte);
710   if (!te) continue;
711   AliPosition* tr0=te->GetReferencePoint();
712   AliTimestamp* tt0=tr0->GetTimestamp();
713   t0=evt->GetDifference(tt0,"ns");
714   p=te->Get3Momentum();
715   levers.Reset();
716   hprojs.Reset();
717   times.Reset();
718   for (Int_t jh=0; jh<nh; jh++)
719   {
720    sx1=(AliSignal*)hits.At(jh);
721    if (!sx1) continue;
722    IceGOM* omx=(IceGOM*)sx1->GetDevice();
723    if (!omx) continue;
724    r1=omx->GetPosition();
725    d=te->GetDistance(r1);
726    r12=r1-(*tr0);
727    hproj=p.Dot(r12);
728    dist=hproj+d/tan(pi/2.-thetac-alphac);
729    tgeo=t0+dist/c;
730    t1=sx1->GetSignal("LE",7);
731    tres=t1-tgeo;
732
733    if (tres<-30 || tres>300 || d>fMaxdhit*lambda) continue;
734
735    // Associate this hit to the TE
736    te->AddSignal(*sx1);
737    levers.Enter(fabs(hproj));
738    hprojs.Enter(hproj);
739    times.Enter(tres);
740   }
741
742   // Determine the Q quality of the various TE's.
743   // Good quality TE's will be called track candidates (TC's)
744   nah=te->GetNsignals();
745   if (nah>nahmax) nahmax=nah;
746   lmin=levers.GetMinimum(1);
747   lmax=levers.GetMaximum(1);
748   spanl=lmax-lmin;
749   medianl=levers.GetMedian(1);
750   meanl=levers.GetMean(1);
751   sigmal=levers.GetSigma(1);
752   spreadl=levers.GetSpread(1);
753   // Expected spread for a flat distribution
754   expspreadl=0;
755   if (spanl>0) expspreadl=(0.5*pow(lmin,2)+0.5*pow(lmax,2)+pow(medianl,2)-medianl*(lmin+lmax))/spanl;
756   hprojmin=hprojs.GetMinimum(1);
757   hprojmax=hprojs.GetMaximum(1);
758   span=hprojmax-hprojmin;
759   median=hprojs.GetMedian(1);
760   mean=hprojs.GetMean(1);
761   sigma=hprojs.GetSigma(1);
762   spread=hprojs.GetSpread(1);
763   // Expected spread for a flat distribution
764   expspread=0;
765   if (span>0) expspread=(0.5*pow(hprojmin,2)+0.5*pow(hprojmax,2)+pow(median,2)-median*(hprojmin+hprojmax))/span;
766   mediant=times.GetMedian(1);
767   meant=times.GetMean(1);
768   sigmat=times.GetSigma(1);
769   spreadt=times.GetSpread(1);
770
771   term1=0;
772   if (span>0) term1=2.*spread/span;
773
774   term2=0;
775   if (spanl>0) term2=2.*spreadl/spanl;
776
777   term3=0;
778   if (spread>0) term3=fabs(spread-expspread)/spread;
779
780   term4=0;
781   if (spreadl>0) term4=fabs(spreadl-expspreadl)/spreadl;
782
783   term5=0;
784   if (spreadt>0) term5=fabs(mediant)/spreadt;
785
786   qtc=float(nah)*(term1+term2)-term3-term4-term5;
787   if (fabs(median)>span/2.) qtc=0; // Require projected hits on both sides of r0
788
789   fit.SetSignal(qtc,"QTC");
790   fit.SetSignal(spanl,"SpanL");
791   fit.SetSignal(medianl,"MedianL");
792   fit.SetSignal(meanl,"MeanL");
793   fit.SetSignal(sigmal,"SigmaL");
794   fit.SetSignal(spreadl,"SpreadL");
795   fit.SetSignal(expspreadl,"ExpSpreadL");
796   fit.SetSignal(span,"Span");
797   fit.SetSignal(median,"Median");
798   fit.SetSignal(mean,"Mean");
799   fit.SetSignal(sigma,"Sigma");
800   fit.SetSignal(spread,"Spread");
801   fit.SetSignal(expspread,"ExpSpread");
802   fit.SetSignal(mediant,"MedianT");
803   fit.SetSignal(meant,"MeanT");
804   fit.SetSignal(sigmat,"SigmaT");
805   fit.SetSignal(spreadt,"SpreadT");
806   fit.SetSignal(term1,"term1");
807   fit.SetSignal(term2,"term2");
808   fit.SetSignal(term3,"term3");
809   fit.SetSignal(term4,"term4");
810   fit.SetSignal(term5,"term5");
811   te->SetFitDetails(fit);
812   if (qtc>qmax) qmax=qtc;
813  }
814
815  // Perform selection on Q value in case of multiple track candidates
816  for (Int_t jtc=0; jtc<nte; jtc++)
817  {
818   te=(AliTrack*)tes.At(jtc);
819   if (!te) continue;
820   nah=te->GetNsignals();
821   sx1=(AliSignal*)te->GetFitDetails();
822   qtc=-1;
823   if (sx1) qtc=sx1->GetSignal("QTC");
824   if (!nah || qtc<0.8*qmax)
825   {
826    tes.RemoveAt(jtc);
827    delete te;
828   }
829   else // Set Q value as momentum to provide a weight for jet clustering
830   {
831    if (qtc>0)
832    {
833     p=te->Get3Momentum();
834     p*=qtc;
835     te->Set3Momentum(p);
836    }
837   }
838  } 
839  tes.Compress();
840  nte=tes.GetEntries();
841
842  if (!nte) return;
843
844  // Cluster track candidates within a certain opening angle into jets.
845  // Also the track should be within a certain maximum distance of the
846  // starting track in order to get clustered.
847  // The latter prevents clustering of (nearly) parallel track candidates
848  // crossing the detector a very different locations (e.g. muon bundles).
849  // The average r0 and t0 of the constituent tracks will be taken as the
850  // jet reference point. 
851  TObjArray jets;
852  jets.SetOwner();
853  AliTrack* te2=0;
854  Float_t ang=0;
855  AliSample pos;
856  AliSample time;
857  Float_t vec[3],err[3];
858  nahmax=0; // Determine the max. number of associated hits for the jets
859  for (Int_t jtc1=0; jtc1<nte; jtc1++)
860  {
861   te=(AliTrack*)tes.At(jtc1);
862   if (!te) continue;
863   AliPosition* x1=te->GetReferencePoint();
864   if (!x1) continue;
865   AliTimestamp* ts1=x1->GetTimestamp();
866   if (!ts1) continue;
867   AliJet* jx=new AliJet();
868   jx->AddTrack(te);
869   pos.Reset();
870   time.Reset();
871   x1->GetPosition(vec,"car");
872   pos.Enter(vec[0],vec[1],vec[2]);
873   t0=evt->GetDifference(ts1,"ns");
874   time.Enter(t0);
875   for (Int_t jtc2=0; jtc2<nte; jtc2++)
876   {
877    if (jtc2==jtc1) continue;
878    te2=(AliTrack*)tes.At(jtc2);
879    if (!te2) continue;
880    ang=te->GetOpeningAngle(*te2,"deg");
881    if (ang<=fTangmax)
882    {
883     AliPosition* x2=te2->GetReferencePoint();
884     if (!x2) continue;
885     AliTimestamp* ts2=x2->GetTimestamp();
886     if (!ts2) continue;
887     if (!fTinvol)
888     {
889      dist=te->GetDistance(te2);
890     }
891     else
892     {
893      dist=te->GetDistance(x2);
894      dist2=te2->GetDistance(x1);
895      if (dist2<dist) dist=dist2;
896     }
897     if (dist<=fTdistmax)
898     {
899      x2->GetPosition(vec,"car");
900      pos.Enter(vec[0],vec[1],vec[2]);
901      t0=evt->GetDifference(ts2,"ns");
902      time.Enter(t0);
903      jx->AddTrack(te2);
904     }
905    }
906   }
907
908   // Set the reference point data for this jet
909   for (Int_t j=1; j<=3; j++)
910   {
911    vec[j-1]=pos.GetMean(j);
912    err[j-1]=pos.GetSigma(j);
913   }
914   r0.SetPosition(vec,"car");
915   r0.SetPositionErrors(err,"car");
916   r0.SetTimestamp((AliTimestamp&)*evt);
917   AliTimestamp* jt0=r0.GetTimestamp();
918   t0=time.GetMean(1);
919   jt0->Add(0,0,(int)t0);
920   jx->SetReferencePoint(r0);
921
922   // Store this jet for further processing if ntracks>1
923   if (jx->GetNtracks() > 1 || fTangmax<=0)
924   {
925    jets.Add(jx);
926    nah=jx->GetNsignals();
927    if (nah>nahmax) nahmax=nah;
928   }
929   else // Only keep single-track jets which have qtc=qmax 
930   {
931    sx1=(AliSignal*)te->GetFitDetails();
932    qtc=-1;
933    if (sx1) qtc=sx1->GetSignal("QTC");
934    if (qtc>=(qmax-1.e-10))
935    {
936     jets.Add(jx);
937     nah=jx->GetNsignals();
938     if (nah>nahmax) nahmax=nah;
939    }
940    else
941    {
942     delete jx;
943    }
944   }
945  }
946  Int_t njets=jets.GetEntries();
947
948  if (!njets) return;
949
950  // The sum of 0.15*(nah-nahmax) and average qtc value per track for this jet
951  // will be stored as the jet energy to enable sorting on this value lateron
952  Float_t sortval=0;
953  Int_t ntk=0;
954  for (Int_t ijet=0; ijet<njets; ijet++)
955  {
956   AliJet* jx=(AliJet*)jets.At(ijet);
957   if (!jx) continue;
958   nah=jx->GetNsignals();
959   ntk=jx->GetNtracks();
960   sortval=0.15*float(nah-nahmax);
961   if (ntk) sortval+=jx->GetMomentum()/float(ntk);
962   jx->SetScalar(sortval);
963  }
964
965  // Order the jets w.r.t. decreasing value of the above sortval
966  ordered=evt->SortJets(-2,&jets);
967  TObjArray jets2(*ordered);
968
969  // Merge jets within a certain opening to provide the final track(s).
970  // Also the jet should be within a certain maximum distance of the
971  // starting jet in order to get merged.
972  // The latter prevents merging of (nearly) parallel jets/tracks
973  // crossing the detector a very different locations (e.g. muon bundles).
974  // The average r0 and t0 of the constituent jets will be taken as the
975  // final reference point. 
976  AliJet* jx1=0;
977  AliJet* jx2=0;
978  Int_t merged=1;
979  if (fJangmax>=0)
980  {
981   while (merged)
982   {
983    merged=0;
984    nahmax=0;
985    for (Int_t jet1=0; jet1<njets; jet1++)
986    {
987     jx1=(AliJet*)jets2.At(jet1);
988     if (!jx1) continue;
989     AliPosition* x1=jx1->GetReferencePoint();
990     if (!x1) continue;
991     AliTimestamp* ts1=x1->GetTimestamp();
992     if (!ts1) continue;
993     pos.Reset();
994     time.Reset();
995     x1->GetPosition(vec,"car");
996     pos.Enter(vec[0],vec[1],vec[2]);
997     t0=evt->GetDifference(ts1,"ns");
998     time.Enter(t0);
999     for (Int_t jet2=0; jet2<njets; jet2++)
1000     {
1001      jx2=(AliJet*)jets2.At(jet2);
1002      if (!jx2 || jet2==jet1) continue;
1003      AliPosition* x2=jx2->GetReferencePoint();
1004      if (!x2) continue;
1005      AliTimestamp* ts2=x2->GetTimestamp();
1006      if (!ts2) continue;
1007      ang=jx1->GetOpeningAngle(*jx2,"deg");
1008      if (ang<=fJangmax)
1009      {
1010       if (!fJinvol)
1011       {
1012        dist=jx1->GetDistance(jx2);
1013       }
1014       else
1015       {
1016        dist=jx1->GetDistance(x2);
1017        dist2=jx2->GetDistance(x1);
1018        if (dist2<dist) dist=dist2;
1019       }
1020       if (dist<=fJdistmax)
1021       {
1022        x2->GetPosition(vec,"car");
1023        pos.Enter(vec[0],vec[1],vec[2]);
1024        t0=evt->GetDifference(ts2,"ns");
1025        time.Enter(t0);
1026        for (Int_t jtk=1; jtk<=jx2->GetNtracks(); jtk++)
1027        {
1028         te=jx2->GetTrack(jtk);
1029         if (!te) continue;
1030         jx1->AddTrack(te);
1031        }
1032        jets2.RemoveAt(jet2);
1033        if (fJiterate) merged=1;
1034       }
1035      }
1036     } // End of jet2 loop
1037
1038     // Set the reference point data for this jet
1039     for (Int_t k=1; k<=3; k++)
1040     {
1041      vec[k-1]=pos.GetMean(k);
1042      err[k-1]=pos.GetSigma(k);
1043     }
1044     r0.SetPosition(vec,"car");
1045     r0.SetPositionErrors(err,"car");
1046     r0.SetTimestamp((AliTimestamp&)*evt);
1047     AliTimestamp* jt0=r0.GetTimestamp();
1048     t0=time.GetMean(1);
1049     jt0->Add(0,0,(int)t0);
1050     jx1->SetReferencePoint(r0);
1051
1052     nah=jx1->GetNsignals();
1053     if (nah>nahmax) nahmax=nah;
1054    } // End of jet1 loop
1055
1056    jets2.Compress();
1057
1058    // The sum of 0.15*(nah-nahmax) and average qtc value per track for this jet
1059    // will be stored as the jet energy to enable sorting on this value lateron
1060    for (Int_t jjet=0; jjet<njets; jjet++)
1061    {
1062     AliJet* jx=(AliJet*)jets2.At(jjet);
1063     if (!jx) continue;
1064     nah=jx->GetNsignals();
1065     ntk=jx->GetNtracks();
1066     sortval=0.15*float(nah-nahmax);
1067     if (ntk) sortval+=jx->GetMomentum()/float(ntk);
1068     jx->SetScalar(sortval);
1069    }
1070
1071    // Order the jets w.r.t. decreasing value of the above sortval
1072    ordered=evt->SortJets(-2,&jets2);
1073    njets=ordered->GetEntries();
1074    jets2.Clear();
1075    for (Int_t icopy=0; icopy<njets; icopy++)
1076    {
1077     jets2.Add(ordered->At(icopy));
1078    }
1079   } // End of iterative while loop
1080  }
1081
1082  // Store every jet as a reconstructed track in the event structure.
1083  // The jet 3-momentum (normalised to 1) and reference point
1084  // (i.e.the average r0 and t0 of the constituent tracks) will make up
1085  // the final track parameters.
1086  // All the associated hits of all the constituent tracks of the jet
1087  // will be associated to the final track.
1088  // In case the jet angular separation was set <0, only the jet with
1089  // the maximum number of tracks (i.e. the first one in the array)
1090  // will be used to form a track. This will allow comparison with
1091  // the standard Sieglinde processing.
1092  AliTrack t; 
1093  t.SetNameTitle(fTrackname.Data(),"IceDwalk direct walk track");
1094  t.SetCharge(fCharge);
1095  for (Int_t jet=0; jet<njets; jet++)
1096  {
1097   AliJet* jx=(AliJet*)jets2.At(jet);
1098   if (!jx) continue;
1099   AliPosition* ref=jx->GetReferencePoint();
1100   if (!ref) continue;
1101   evt->AddTrack(t);
1102   AliTrack* trk=evt->GetTrack(evt->GetNtracks());
1103   if (!trk) continue;
1104   trk->SetId(evt->GetNtracks(1)+1);
1105   p=jx->Get3Momentum();
1106   p/=p.GetNorm();
1107   trk->Set3Momentum(p);
1108   trk->SetReferencePoint(*ref);
1109   AliTimestamp* tt0=ref->GetTimestamp();
1110   if (tt0) trk->SetTimestamp(*tt0);
1111   for (Int_t jt=1; jt<=jx->GetNtracks(); jt++)
1112   {
1113    AliTrack* tx=jx->GetTrack(jt);
1114    if (!tx) continue;
1115    for (Int_t is=1; is<=tx->GetNsignals(); is++)
1116    {
1117     sx1=tx->GetSignal(is);
1118     if (sx1) sx1->AddTrack(*trk);
1119    }
1120   }
1121
1122   // Only take the jet with the highest quality number
1123   // (i.e. the first jet in the list) when the user had selected
1124   // this reconstruction mode.
1125   if (fJangmax<0) break;
1126  }
1127 }
1128 ///////////////////////////////////////////////////////////////////////////