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