]> git.uio.no Git - u/mrichter/AliRoot.git/blob - RALICE/icepack/IcePandel.cxx
06-jun-2007 NvE Explicit tests on null pointers for returned TObjArray* from e.g...
[u/mrichter/AliRoot.git] / RALICE / icepack / IcePandel.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 IcePandel
20 // TTask derived class to perform track fitting via minimisation of a
21 // convoluted Pandel pdf.
22 //
23 // The code in this processor is based on the algorithms as developed
24 // by Oladipo Fadiran, George Japaridze (Clark Atlanta University, USA)
25 // and Nick van Eijndhoven (Utrecht University, The Netherlands).
26 //
27 // For the minimisation process the TFitter facility, which is basically Minuit,
28 // is used. Minimisation is performed by invokation of the SIMPLEX method,
29 // followed by an invokation of HESSE to determine the uncertainties on the results. 
30 // The statistics of the TFitter result are stored as an AliSignal object
31 // in the track, which can be obtained via the GetFitDetails memberfunction.
32 // In the minimisation procedure an overall plausibility for the fitted track
33 // is determined based on a convoluted Pandel pdf value for each used hit.
34 // This track plausibility is expressed in terms of a Bayesian psi value
35 // w.r.t. a Convoluted Pandel PDF.
36 // The Baysian psi value is defined as -loglikelihood in a decibel scale.
37 // This implies psi=-10*log10(L) where L=p(D|HI) being the likelihood of
38 // the data D under the hypothesis H and prior information I.
39 // Since all (associated) hits contribute independently to the Bayesian psi
40 // value, this psi value is built up by summation of the various hit contributions.
41 // As such, the FitDetails entries contain the statistics of all the different
42 // hit contributions, like PsiMedian, PsiMean, and PsiSigma.
43 // The Bayesian psi value is available in the fit details under the name "PsiSum".
44 // In addition the standard Minuit results like IERFIT, FCN, EDM etc... are
45 // also available from the FitDetails.
46 //
47 // The convoluted Pandel value is evaluated in various areas in the distance-time
48 // space as described in the CPandel writeup by O. Fadiran, G. Japaridze
49 // and N. van Eijndhoven.
50 // In case the distance-time point of a certain hit falls outside the
51 // validity rectangle, the point is moved onto the corresponding side location
52 // of the rectangle. For this new location the Pandel value is evaluated for
53 // this hit and an extra penalty is added to the corresponding psi value
54 // for this hit. 
55 // By default this penalty value amounts to 0 dB, but the user can
56 // modify this penalty value via the memberfunction SetPenalty.
57 // This allows investigation/tuning of the sensitivity to hits with
58 // extreme distance and/or time residual values.
59 //
60 // A separate treatment of the phase and group velocities is introduced
61 // which will provide more accurate time residuals due to the different
62 // velocities of the Cerenkov wave front (v_phase) and the actually detected
63 // photons (v_group).
64 // This distinction between v_phase and v_group can be (de)activated via the
65 // memberfunction SetVgroupUsage(). By default the distinction between v_phase
66 // and v_group is activated in the constructor of this class.
67 //
68 // Use the UseTracks memberfunction to specify the first guess tracks
69 // to be processed by the minimiser.
70 // By default only the first encountered IceDwalk track will be processed.
71 //
72 // Use the SelectHits memberfunction to specify the hits to be used.
73 // By default only the hits associated to the first guess track are used.
74 //
75 // Information about the actual parameter settings can be found in the event
76 // structure itself via the device named "IcePandel".
77 //
78 // The fit processor printlevel can be selected via the memberfunction SetPrintLevel.
79 // By default all printout is suppressed (i.e. level=-2).
80 // 
81 // An example of how to invoke this processor after Xtalk, hit cleaning
82 // and a direct walk first guess estimate can be found in the ROOT example
83 // macro icepandel.cc which resides in the /macros subdirectory.
84 // 
85 // The minimisation results are stored in the IceEvent structure as
86 // tracks with as default the name "IcePandel" (just like the first guess
87 // results of e.g. IceDwalk).
88 // This track name identifier can be modified by the user via the
89 // SetTrackName() memberfunction. This will allow unique identification
90 // of tracks which are produced when re-processing existing data with
91 // different criteria.
92 // By default the charge of the produced tracks is set to 0, since
93 // no distinction can be made between positive or negative tracks.
94 // However, the user can define the track charge by invokation
95 // of the memberfunction SetCharge().
96 // This facility may be used to distinguish tracks produced by the
97 // various reconstruction algorithms in a (3D) colour display
98 // (see the class AliHelix for further details).  
99 // A pointer to the first guess track which was used as input is available
100 // via the GetParentTrack facility of these "IcePandel" tracks.
101 // Furthermore, all the hits that were used in the minisation are available
102 // via the GetSignal facility of a certain track.
103 //
104 // An example of how the various data can be accessed is given below,
105 // where "evt" indicates the pointer to the IceEvent structure.
106 //
107 // Example for accessing data :
108 // ----------------------------
109 // TObjArray* tracks=evt->GetTracks("IcePandel");
110 // if (!tracks) return;
111 // AliPosition* r0=0;
112 // Float_t fcn=0;
113 // for (Int_t jtk=0; jtk<tracks->GetEntries(); jtk++)
114 // {
115 //  AliTrack* tx=(AliTrack*)tracks->At(jtk);
116 //  if (!tx) continue;
117 //  tx->Data("sph");
118 //  r0=tx->GetReferencePoint();
119 //  if (r0) r0->Data();
120 //  sx=(AliSignal*)tx->GetFitDetails();
121 //  if (sx) fcn=sx->GetSignal("FCN");
122 //  AliTrack* tx2=tx->GetParentTrack();
123 //  if (!tx2) continue;
124 //  tx2->Data("sph");
125 //  r0=tx2->GetReferencePoint();
126 //  if (r0) r0->Data();
127 // }
128 //
129 // Notes :
130 // -------
131 // 1) This processor only works properly on data which are Time and ADC
132 //    calibrated and contain tracks from first guess algorithms like
133 //    e.g. IceDwalk.
134 // 2) In view of the usage of TFitter/Minuit minimisation, a global pointer
135 //    to the instance of this class (gIcePandel) and a global static
136 //    wrapper function (IcePandelFCN) have been introduced, to allow the
137 //    actual minimisation to be performed via the memberfunction FitFCN.
138 //    This implies that in a certain processing job only 1 instance of
139 //    this IcePandel class may occur.
140 //
141 //--- Author: Nick van Eijndhoven 09-feb-2006 Utrecht University
142 //- Modified: NvE $Date$ Utrecht University
143 ///////////////////////////////////////////////////////////////////////////
144  
145 #include "IcePandel.h"
146 #include "Riostream.h"
147
148 // Global pointer to the instance of this object
149  IcePandel* gIcePandel=0;
150
151 // TFitter/Minuit interface to IcePandel::FitFCN
152  void IcePandelFCN(Int_t& npar,Double_t* gin,Double_t& f,Double_t* u,Int_t flag)
153  {
154   if (gIcePandel) gIcePandel->FitFCN(npar,gin,f,u,flag);
155  }
156
157 ClassImp(IcePandel) // Class implementation to enable ROOT I/O
158
159 IcePandel::IcePandel(const char* name,const char* title) : TTask(name,title)
160 {
161 // Default constructor.
162  fFirst=1;
163  fPrint=-2;
164  fSelhits=1;
165  fVgroup=1;
166  fEvt=0;
167  fUseNames=0;
168  fUseNtk=0;
169  fHits=0;
170  fFitter=0;
171  fTrackname="IcePandel";
172  fCharge=0;
173  fPenalty=0;
174  fTkfit=0;
175  fFitstats=0;
176
177  // Set the global pointer to this instance
178  gIcePandel=this;
179 }
180 ///////////////////////////////////////////////////////////////////////////
181 IcePandel::~IcePandel()
182 {
183 // Default destructor.
184  if (fUseNames)
185  {
186   delete fUseNames;
187   fUseNames=0;
188  }
189  if (fUseNtk)
190  {
191   delete fUseNtk;
192   fUseNtk=0;
193  }
194  if (fHits)
195  {
196   delete fHits;
197   fHits=0;
198  }
199  if (fFitter)
200  {
201   delete fFitter;
202   fFitter=0;
203  }
204  if (fTkfit)
205  {
206   delete fTkfit;
207   fTkfit=0;
208  }
209  if (fFitstats)
210  {
211   delete fFitstats;
212   fFitstats=0;
213  }
214 }
215 ///////////////////////////////////////////////////////////////////////////
216 void IcePandel::Exec(Option_t* opt)
217 {
218 // Implementation of the hit fitting procedure.
219
220  TString name=opt;
221  AliJob* parent=(AliJob*)(gROOT->GetListOfTasks()->FindObject(name.Data()));
222
223  if (!parent) return;
224
225  fEvt=(IceEvent*)parent->GetObject("IceEvent");
226  if (!fEvt) return;
227
228  // Storage of the used parameters in the IcePandel device
229  AliSignal params;
230  params.SetNameTitle("IcePandel","IcePandel processor parameters");
231  params.SetSlotName("Selhits",1);
232  params.SetSlotName("Penalty",2);
233  params.SetSlotName("Vgroup",3);
234
235  params.SetSignal(fSelhits,1);
236  params.SetSignal(fPenalty,2);
237  params.SetSignal(fVgroup,3);
238
239  fEvt->AddDevice(params);
240
241  if (!fUseNames) UseTracks("IceDwalk",1);
242
243  Int_t nclasses=fUseNames->GetEntries(); // Number of first guess classes to be processed
244  Int_t ntkmax=0; // Max. number of tracks for a certain class
245  TObjString* strx=0;
246  TString str;
247
248  if (fFirst)
249  {
250   cout << " *IcePandel* First guess selections to be processed (-1=all)." << endl;
251   for (Int_t i=0; i<nclasses; i++)
252   {
253    strx=(TObjString*)fUseNames->At(i);
254    if (!strx) continue;
255    str=strx->GetString();
256    ntkmax=fUseNtk->At(i);
257    cout << " Maximally " << ntkmax << " track(s) per event for procedure : " << str.Data() << endl;
258   }
259   cout << " *IcePandel* Hit selection mode : " << fSelhits << endl;
260   cout << " *IcePandel* Penalty value for minimiser : " << fPenalty << " dB." << endl;
261   cout << endl;
262
263   fPsistats.SetStoreMode(1);
264
265   fFirst=0;
266  }
267
268  const Double_t pi=acos(-1.);
269  const Double_t e=exp(1.);
270
271  // Initialisation of the minimisation processor
272  Double_t arglist[100];
273  if (!fFitter) fFitter=new TFitter();
274
275  // The number of reconstructed tracks already present in the event
276  Int_t ntkreco=fEvt->GetNtracks(1);
277
278  if (!fHits)
279  {
280   fHits=new TObjArray();
281  }
282  else
283  {
284   fHits->Clear();
285  }
286
287  // If selected, use all the good quality hits of the complete event
288  if (fSelhits==0)
289  {
290   TObjArray* hits=fEvt->GetHits("IceGOM");
291   if (!hits) return;
292   for (Int_t ih=0; ih<hits->GetEntries(); ih++)
293   {
294    AliSignal* sx=(AliSignal*)hits->At(ih);
295    if (!sx) continue;
296    if (sx->GetDeadValue("ADC") || sx->GetDeadValue("LE") || sx->GetDeadValue("TOT")) continue;
297    fHits->Add(sx);
298   }
299  }
300
301  // Track by track processing of the selected first guess classes
302  Float_t vec[3];
303  Float_t err[3];
304  Ali3Vector p;
305  AliPosition pos;
306  if (!fTkfit)
307  {
308   fTkfit=new AliTrack();
309   fTkfit->SetNameTitle(fTrackname.Data(),"IcePandel fit result");
310  }
311  if (!fFitstats)
312  {
313   fFitstats=new AliSignal();
314   fFitstats->SetNameTitle("Fitstats","TFitter stats for Pandel fit");
315   fFitstats->SetSlotName("IERFIT",1);
316   fFitstats->SetSlotName("FCN",2);
317   fFitstats->SetSlotName("EDM",3);
318   fFitstats->SetSlotName("NVARS",4);
319   fFitstats->SetSlotName("IERERR",5);
320   fFitstats->SetSlotName("PsiSum",6);
321   fFitstats->SetSlotName("PsiMedian",7);
322   fFitstats->SetSlotName("PsiSpread",8);
323   fFitstats->SetSlotName("PsiMean",9);
324   fFitstats->SetSlotName("PsiSigma",10);
325  }
326  Float_t x,y,z,theta,phi,t0;
327  Double_t amin,edm,errdef; // Minimisation stats
328  Int_t ierfit,iererr,nvpar,nparx;    // Minimisation stats
329  Int_t ntk=0;
330  Int_t nsig=0;
331  AliTrack* track=0;
332  for (Int_t iclass=0; iclass<nclasses; iclass++) // Loop over first guess classes
333  {
334   strx=(TObjString*)fUseNames->At(iclass);
335   if (!strx) continue;
336   str=strx->GetString();
337   ntkmax=fUseNtk->At(iclass);
338   TObjArray* tracks=fEvt->GetTracks(str);
339   ntk=0;
340   if (tracks) ntk=tracks->GetEntries();
341   if (ntkmax>0 && ntk>ntkmax) ntk=ntkmax;
342
343   for (Int_t jtk=0; jtk<ntk; jtk++) // Loop over tracks of a certain class
344   {
345    track=(AliTrack*)tracks->At(jtk);
346    if (!track) continue;
347
348    AliPosition* r0=track->GetReferencePoint();
349    if (!r0) continue;
350
351    AliTimestamp* tt0=r0->GetTimestamp();
352
353    // If selected, use only the first guess track associated hits
354    if (fSelhits==1)
355    {
356     fHits->Clear();
357     nsig=track->GetNsignals();
358     for (Int_t is=1; is<=nsig; is++)
359     {
360      AliSignal* sx=track->GetSignal(is);
361      if (!sx) continue;
362      if (!sx->GetDevice()->InheritsFrom("IceGOM")) continue;
363      if (sx->GetDeadValue("ADC") || sx->GetDeadValue("LE") || sx->GetDeadValue("TOT")) continue;
364      fHits->Add(sx);
365     }
366    }
367    
368    if (!fHits->GetEntries()) continue;
369
370    r0->GetVector(vec,"car");
371    r0->GetErrors(err,"car");
372
373    x=vec[0];
374    y=vec[1];
375    z=vec[2];
376
377    p=track->Get3Momentum();
378    p.GetVector(vec,"sph");
379
380    theta=vec[1];
381    phi=vec[2];
382
383    t0=fEvt->GetDifference(tt0,"ns");
384    
385    // Process this first guess track with its associated hits
386    fFitter->Clear();
387
388    // Set user selected TFitter printout level
389    arglist[0]=fPrint;
390    if (fPrint==-2) arglist[0]=-1;
391    fFitter->ExecuteCommand("SET PRINT",arglist,1);
392    if (fPrint==-2) fFitter->ExecuteCommand("SET NOWARNINGS",arglist,0);
393
394    fFitter->SetFitMethod("loglikelihood");
395
396    // Define errors to represent 1 sigma for this likelihood scale
397    arglist[0]=5.*log10(e);
398    fFitter->ExecuteCommand("SET ERRORDEF",arglist,1);
399
400    fFitter->SetParameter(0,"r0x",x,0.1,0,0);
401    fFitter->SetParameter(1,"r0y",y,0.1,0,0);
402    fFitter->SetParameter(2,"r0z",z,0.1,0,0);
403    fFitter->SetParameter(3,"theta",theta,0.001,0,pi);
404    fFitter->SetParameter(4,"phi",phi,0.001,0,2.*pi);
405    fFitter->SetParameter(5,"t0",t0,1.,0,32000);
406
407    fFitter->SetFCN(IcePandelFCN);
408
409    fTkfit->Reset();
410
411    arglist[0]=0;
412    ierfit=fFitter->ExecuteCommand("SIMPLEX",arglist,0);
413
414    fFitter->GetStats(amin,edm,errdef,nvpar,nparx);
415
416    fFitstats->Reset();
417    fFitstats->SetSignal(ierfit,1);
418    fFitstats->SetSignal(amin,2);
419    fFitstats->SetSignal(edm,3);
420    fFitstats->SetSignal(nvpar,4);
421
422    fFitstats->SetSignal(fPsistats.GetSum(1),6);
423    fFitstats->SetSignal(fPsistats.GetMedian(1),7);
424    fFitstats->SetSignal(fPsistats.GetSpread(1),8);
425    fFitstats->SetSignal(fPsistats.GetMean(1),9);
426    fFitstats->SetSignal(fPsistats.GetSigma(1),10);
427
428    iererr=fFitter->ExecuteCommand("HESSE",arglist,0);
429    fFitstats->SetSignal(iererr,5);
430
431    // Resulting parameters after minimisation and error calculation
432    vec[0]=fFitter->GetParameter(0);
433    vec[1]=fFitter->GetParameter(1);
434    vec[2]=fFitter->GetParameter(2);
435    err[0]=fFitter->GetParError(0);
436    err[1]=fFitter->GetParError(1);
437    err[2]=fFitter->GetParError(2);
438    pos.SetPosition(vec,"car");
439    pos.SetPositionErrors(err,"car");
440
441    vec[0]=1.;
442    vec[1]=fFitter->GetParameter(3);
443    vec[2]=fFitter->GetParameter(4);
444    err[0]=0.;
445    err[1]=fFitter->GetParError(3);
446    err[2]=fFitter->GetParError(4);
447    p.SetVector(vec,"sph");
448    p.SetErrors(err,"sph");
449
450    t0=fFitter->GetParameter(5);
451    AliTimestamp t0fit((AliTimestamp)(*fEvt));
452    t0fit.Add(0,0,int(t0));
453
454    // Enter the fit result as a track in the event structure
455    ntkreco++;
456    fTkfit->SetId(ntkreco);
457    fTkfit->SetCharge(fCharge);
458    fTkfit->SetParentTrack(track);
459    pos.SetTimestamp(t0fit);
460    fTkfit->SetTimestamp(t0fit);
461    fTkfit->SetReferencePoint(pos);
462    fTkfit->Set3Momentum(p);
463    for (Int_t ihit=0; ihit<fHits->GetEntries(); ihit++)
464    {
465     AliSignal* sx=(AliSignal*)fHits->At(ihit);
466     if (sx) fTkfit->AddSignal(*sx);
467    }
468    fTkfit->SetFitDetails(fFitstats);
469    fEvt->AddTrack(fTkfit);
470   } // End loop over tracks
471  } // End loop over first guess classes
472
473 }
474 ///////////////////////////////////////////////////////////////////////////
475 void IcePandel::SetPrintLevel(Int_t level)
476 {
477 // Set the fitter (Minuit) print level.
478 // See the TFitter and TMinuit docs for details.
479 //
480 // Note : level=-2 suppresses also all fit processor warnings.
481 //        
482 // The default in the constructor is level=-2. 
483
484  fPrint=level;
485 }
486 ///////////////////////////////////////////////////////////////////////////
487 void IcePandel::UseTracks(TString classname,Int_t n)
488 {
489 // Specification of the first guess tracks to be used.
490 //
491 // classname : Specifies the first guess algorithm (e.g. "IceDwalk");
492 // n : Specifies the max. number of these tracks to be used
493 //
494 // Note : n<0 will use all the existing tracks of the specified classname
495 //
496 // The default is n=-1.
497 //
498 // Consecutive invokations of this memberfunction with different classnames
499 // will result in an incremental effect.
500 //
501 // Example :
502 // ---------
503 // UseTracks("IceDwalk",5);
504 // UseTracks("IceLinefit",2);
505 // UseTracks("IceJams");
506 //
507 // This will use the first 5 IceDwalk, the first 2 IceLinefit and all the
508 // IceJams tracks which are encountered in the event structure.
509
510  if (!fUseNames)
511  {
512   fUseNames=new TObjArray();
513   fUseNames->SetOwner();
514  }
515  
516  if (!fUseNtk) fUseNtk=new TArrayI();
517
518  // Check if this classname has already been specified before 
519  TString s;
520  Int_t nen=fUseNames->GetEntries();
521  for (Int_t i=0; i<nen; i++)
522  {
523   TObjString* sx=(TObjString*)fUseNames->At(i);
524   if (!sx) continue;
525   s=sx->GetString();
526   if (s==classname) return;
527  }
528
529  // New classname to be added into the storage
530  if (nen >= fUseNames->GetSize()) fUseNames->Expand(nen+1);
531  if (nen >= fUseNtk->GetSize()) fUseNtk->Set(nen+1);
532  
533  TObjString* name=new TObjString();
534  name->SetString(classname);
535  fUseNames->Add(name);
536  fUseNtk->AddAt(n,nen);
537 }
538 ///////////////////////////////////////////////////////////////////////////
539 void IcePandel::SelectHits(Int_t mode)
540 {
541 // Specification of the hits to be used in the minimisation.
542 //
543 // mode = 0 : All hit cleaning survived hits of the complete event are used
544 //        1 : Only the associated hits are used for each first guess track
545 //
546 // The default is mode=1.
547
548  if (mode==0 || mode==1) fSelhits=mode;
549 }
550 ///////////////////////////////////////////////////////////////////////////
551 void IcePandel::SetVgroupUsage(Int_t flag)
552 {
553 // (De)activate the distinction between v_phase and v_group of the Cherenkov light.
554 //
555 // flag = 0 : No distinction between v_phase and v_group
556 //      = 1 : Separate treatment of v_phase and v_group
557 //
558 // By default the distinction between v_phase and v_group is activated
559 // in the constructor of this class.
560  fVgroup=flag;
561 }
562 ///////////////////////////////////////////////////////////////////////////
563 void IcePandel::SetTrackName(TString s)
564 {
565 // Set (alternative) name identifier for the produced tracks.
566 // This allows unique identification of (newly) produced pandel tracks
567 // in case of re-processing of existing data with different criteria.
568 // By default the produced tracks have the name "IcePandel" which is
569 // set in the constructor of this class.
570  fTrackname=s;
571 }
572 ///////////////////////////////////////////////////////////////////////////
573 void IcePandel::SetCharge(Float_t charge)
574 {
575 // Set user defined charge for the produced tracks.
576 // This allows identification of these tracks on color displays.
577 // By default the produced tracks have charge=0 which is set in the
578 // constructor of this class.
579  fCharge=charge;
580 }
581 ///////////////////////////////////////////////////////////////////////////
582 void IcePandel::SetPenalty(Float_t val)
583 {
584 // Set user defined psi penalty value (in dB) in the minimiser for
585 // distance-time points that fall outside the validity rectangle.
586 // This allows investigation/tuning of the sensitivity to hits with
587 // extreme distance and/or time residual values.
588 // By default the penalty val=0 is set in the constructor of this class.
589  fPenalty=val;
590 }
591 ///////////////////////////////////////////////////////////////////////////
592 void IcePandel::FitFCN(Int_t&,Double_t*,Double_t& f,Double_t* x,Int_t)
593 {
594 // Minimisation of the Bayesian psi value for a track w.r.t. a Convoluted Pandel PDF.
595 // The Baysian psi value is defined as -loglikelihood in a decibel scale.
596 // This implies psi=-10*log10(L) where L=p(D|HI) being the likelihood of
597 // the data D under the hypothesis H and prior information I.
598
599  const Float_t c=0.299792458;        // Light speed in vacuum in meters per ns
600  const Float_t npice=1.31768387;     // Phase refractive index (c/v_phase) of ice
601  const Float_t ngice=1.35075806;     // Group refractive index (c/v_group) of ice
602  const Float_t thetac=acos(1./npice);// Cherenkov angle (in radians)
603  const Float_t lambda=33.3;          // Light scattering length in ice
604  const Float_t labs=98;              // Light absorbtion length in ice
605  const Float_t cice=c/ngice;         // Light speed in ice in meters per ns
606  const Float_t tau=557;
607  const Double_t rho=((1./tau)+(cice/labs));
608  const Double_t pi=acos(-1.);
609
610  // Angular reduction of complement of thetac due to v_phase and v_group difference
611  Float_t alphac=0;
612  if (fVgroup) alphac=atan((1.-npice/ngice)/sqrt(npice*npice-1.));
613
614  // Assumed PMT timing jitter in ns
615  const Double_t sigma=10;
616
617  f=0;
618
619  // The new r0 and p vectors and t0 from the minimisation
620  Float_t vec[3];
621  
622  AliPosition r0;
623  vec[0]=x[0];
624  vec[1]=x[1];
625  vec[2]=x[2];
626  r0.SetPosition(vec,"car");
627
628  Ali3Vector p;
629  vec[0]=1;
630  vec[1]=x[3];
631  vec[2]=x[4];
632  p.SetVector(vec,"sph");
633
634  Float_t t0=x[5];
635
636  // Construct a track with the new values from the minimisation
637  fTkfit->SetReferencePoint(r0);
638  fTkfit->Set3Momentum(p);
639
640  Int_t nhits=fHits->GetEntries();
641
642  AliPosition rhit;
643  Ali3Vector r12;
644  Float_t d,dist,thit,tgeo;
645  Double_t tres,ksi,eta,pandel;
646  Double_t cpandel1,cpandel2,cpandel3,cpandel;
647  Double_t z,k,alpha,beta,phi,n1,n2,n3,u; // Function parameters for regions 3 and 4
648  Int_t ier;
649  Double_t psihit=0;
650  fPsistats.Reset();
651  for (Int_t i=0; i<nhits; i++)
652  {
653   AliSignal* sx=(AliSignal*)fHits->At(i);
654   if (!sx) continue;
655   IceGOM* omx=(IceGOM*)sx->GetDevice();
656   if (!omx) continue;
657   rhit=omx->GetPosition();
658   d=fTkfit->GetDistance(rhit);
659   ksi=d/lambda;
660   r12=rhit-r0;
661   dist=p.Dot(r12)+d/tan(pi/2.-thetac-alphac);
662   tgeo=t0+dist/c;
663   thit=sx->GetSignal("LE",7);
664   tres=thit-tgeo;
665
666   // The Convoluted Pandel function evaluation
667   // For definitions of functions and validity regions, see the
668   // CPandel writeup of O. Fadiran, G. Japaridze and N. van Eijndhoven
669
670   // Move points which are outside the validity rectangle in the (tres,ksi) space
671   // to the edge of the validity rectangle and signal the use of the penalty
672   ier=0;
673   if (tres<-25.*sigma)
674   {
675    tres=-25.*sigma;
676    ier=1;
677   }
678   if (tres>3500)
679   {
680    tres=3500;
681    ier=1;
682   }
683   if (ksi>50)
684   {
685    ksi=50;
686    ier=1;
687   }
688
689   eta=(rho*sigma)-(tres/sigma);
690
691   if (ksi<=0) // The zero distance (ksi=0) axis
692   {
693    cpandel=exp(-tres*tres/(2.*sigma*sigma))/(sigma*sqrt(2.*pi));
694   }
695   else if (ksi<=5 && tres>=-5.*sigma && tres<=30.*sigma) // The exact expression in region 1
696   {
697    cpandel1=pow(rho,ksi)*pow(sigma,ksi-1.)*exp(-tres*tres/(2.*sigma*sigma))/pow(2.,0.5*(1.+ksi));
698    cpandel2=ROOT::Math::conf_hyperg(ksi/2.,0.5,eta*eta/2.)/TMath::Gamma((ksi+1.)/2.);
699    cpandel3=sqrt(2.)*eta*ROOT::Math::conf_hyperg((ksi+1.)/2.,1.5,eta*eta/2.)/TMath::Gamma(ksi/2.);
700
701    cpandel=cpandel1*(cpandel2-cpandel3);
702   }
703   else if (ksi<=1 && tres>30.*sigma && tres<=3500) // Approximation in region 2
704   {
705    pandel=pow(rho,ksi)*pow(tres,(ksi-1.))*exp(-rho*tres)/TMath::Gamma(ksi);
706
707    cpandel=exp(rho*rho*sigma*sigma/2.)*pandel;
708   }
709   else if (ksi<=1 && tres<-5.*sigma && tres>=-25.*sigma) // Approximation in region 5
710   {
711    cpandel=pow(rho*sigma,ksi)*pow(eta,-ksi)*exp(-tres*tres/(2.*sigma*sigma))/(sigma*sqrt(2.*pi));
712   }
713   else if (ksi<=50 && tres>=0 && tres<=3500) // Approximation in region 3
714   {
715    z=-eta/sqrt(4.*ksi-2.);
716    k=0.5*(z*sqrt(1.+z*z)+log(z+sqrt(1.+z*z)));
717    alpha=-tres*tres/(2.*sigma*sigma)+eta*eta/4.-ksi/2.+0.25+k*(2.*ksi-1.);
718    alpha+=-log(1.+z*z)/4.-ksi*log(2.)/2.+(ksi-1.)*log(2.*ksi-1.)/2.+ksi*log(rho)+(ksi-1.)*log(sigma);
719    beta=0.5*(z/sqrt(1.+z*z)-1.);
720    n1=beta*(20.*beta*beta+30.*beta+9.)/12.;
721    n2=pow(beta,2.)*(6160.*pow(beta,4.)+18480.*pow(beta,3.)+19404.*pow(beta,2.)+8028.*beta+945.)/288.;
722    n3=27227200.*pow(beta,6.)+122522400.*pow(beta,5.)+220540320.*pow(beta,4.);
723    n3+=200166120.*pow(beta,3.)+94064328.*pow(beta,2.)+20546550.*beta+1403325.;
724    n3*=pow(beta,3.)/51840.;
725    phi=1.-n1/(2.*ksi-1.)+n2/pow(2.*ksi-1.,2.)-n3/pow(2.*ksi-1.,3.);
726    cpandel=exp(alpha)*phi/TMath::Gamma(ksi);
727   }
728   else if (ksi<=50 && tres<0 && tres>=-25.*sigma) // Approximation in region 4
729   {
730    z=eta/sqrt(4.*ksi-2.);
731    k=0.5*(z*sqrt(1.+z*z)+log(z+sqrt(1.+z*z)));
732    u=exp(ksi/2.-0.25)*pow(2.*ksi-1.,-ksi/2.)*pow(2.,(ksi-1.)/2.);
733    beta=0.5*(z/sqrt(1.+z*z)-1.);
734    n1=beta*(20.*beta*beta+30.*beta+9.)/12.;
735    n2=pow(beta,2.)*(6160.*pow(beta,4.)+18480.*pow(beta,3.)+19404.*pow(beta,2.)+8028.*beta+945.)/288.;
736    n3=27227200.*pow(beta,6.)+122522400.*pow(beta,5.)+220540320.*pow(beta,4.);
737    n3+=200166120.*pow(beta,3.)+94064328.*pow(beta,2.)+20546550.*beta+1403325.;
738    n3*=pow(beta,3.)/51840.;
739    phi=1.+n1/(2.*ksi-1.)+n2/pow(2.*ksi-1.,2.)+n3/pow(2.*ksi-1.,3.);
740    cpandel=pow(rho,ksi)*pow(sigma,ksi-1.)*exp(-pow(tres,2.)/(2.*pow(sigma,2.))+pow(eta,2.)/4.)/sqrt(2.*pi);
741    cpandel*=u*phi*exp(-k*(2.*ksi-1.))*pow(1.+z*z,-0.25);
742   }
743   else // (tres,ksi) outside validity rectangle
744   {
745    ier=1;
746    cout << " *IcePandel::FitFCN* Outside rectangle. We should never get here." << endl;
747   }
748
749   // Use 10*log10 expression to obtain intuitive dB scale
750   // Omit (small) negative values which are possible due to computer accuracy
751   psihit=0;
752   if (cpandel>0) psihit=-10.*log10(cpandel);
753
754   // Penalty in dB for (tres,ksi) points outside the validity rectangle
755   if (ier) psihit+=fPenalty; 
756
757   // Update the psi statistics for this hit
758   fPsistats.Enter(float(psihit));
759   f+=psihit;
760  }
761 }
762 ///////////////////////////////////////////////////////////////////////////