cluster.cc

Go to the documentation of this file.
00001 /*
00002 A.Bazilevsky (IHEP, Protvino) - Dec. 1998
00003 (from March 99 - RIKEN-BNL Research Center)
00004 
00005 shura@bnl.gov
00006 
00007 Cluster-Shower reconstruction routines based on the A.Lednev's 
00008 method developed for GAMS (IHEP-CERN).
00009 
00010 The Classes and Basic Functions description is in cluster.h
00011 
00012 The method is based on the parameterized shower profile (energy deposited 
00013 in the tower as a function of the distance between tower center and shower 
00014 Center of Gravity, shower energy and impact angle). No difference between 
00015 the shower produced by electrons or gammas is assumed.
00016 
00017 ************ The shower reconstruction follows three steps: *********
00018 
00019 1.  Cluster search. A cluster is one or several neighboring cells 
00020 separated from the other clusters with zero cells. The cluster are analyzed 
00021 independently. 
00022 Implementation: Class "cluster". The list of clusters is obtained via 
00023 "Find_Clusters" function that has the list of hits as an input (look at 
00024 "hit" structure description in cluster.h). For the brief description of 
00025 the "cluster" methods look in cluster.h.
00026 
00027 2.  Each cluster may contain several peaks. A peak is located in a cell whose 
00028 deposited energy is higher then that of any adjoined cell. Peak regions are 
00029 calculated by sharing the energy deposited in each cell according the  
00030 energies expected from the gammas located in each peak region. This is 
00031 iterative procedure. 
00032 Implementation: Class "peakarea"; most of the methods are inherited from 
00033 "cluster". The array of peakareas for the given cluster is obtained via 
00034 "cluster::GetPeaks( peakarea*, hit* )" method. For the brief description of 
00035 the "peakarea" methods look in cluster.h.
00036 
00037 3.  Gamma reconstruction within the peak region. One or two gammas within a 
00038 peak region. Minimizes Hi**2 function in two-dimensional space ( X,Y shower 
00039 position ). If it is more then some limit, two-gamma hypothesis is taken. 
00040 The two-gamma separation is done by minimizing Hi**2 function with the 
00041 maximum slope method in three-dimensional space: a=(E1-E2)/E0, dX=X1-X2, 
00042 dY=Y1-Y2. 
00043 Implementation: Class "emshower". The array of emshower's for the given 
00044 peakarea is obtained via "peakarea::GetGammas( emshower* )" method. 
00045 For the brief description of the "emshower" methods look in cluster.h.
00046 
00047 ************* Main tips for using these library ***************
00048 
00049 1. Parameters setting
00050 
00051 SetThreshold(TowerThresh) - sets the threshold (GeV) in each tower; 
00052 needed for the calculation of sigmas of the energy fluctuation in the 
00053 towers; default value is 0.02 GeV;
00054 
00055 SetChi2Limit(i) - sets Chi2 limit for the splitting of peakarea onto 
00056 two showers; default value is 1;
00057 
00058 2. Program flow
00059 
00060 All sectors are processed independently. Everything starts with 
00061 Find_Clusters call which returns the list of clusters. It has 
00062 the list of hits as an input. Towers are counted from 0. 
00063 Before every Find_Cluster call user should set the sector geometry 
00064 and vertex position via SetGeometry( SecGeom Geom, float* pVert) call. 
00065 
00066 As soon as one gets the list of clusters, he can do whatever he wants: 
00067 get cluster's characteristics, split each of them onto peakarea's, 
00068 get peakarea's characteristics, split each of them onto emshower's, 
00069 get emshower's characteristics. 
00070 
00071 3. Shower Profile
00072 
00073 The parameterized Shower Profile is used when splitting cluster onto 
00074 peakareas and peakarea onto emshowers. The parameters for the Shower 
00075 Profile calculation are set via SetProfileParameters(...) function for 
00076 each peakarea object. The energy deposited in the particular tower is 
00077 obtained via EMCcell(...) function (based on the Shower Profile)
00078 
00079 */
00080 
00081 #include <math.h>
00082 #include <stdio.h>
00083 #include "cluster.h"
00084 
00085 // If it's defined the cluster with adjacent corner are glued;
00086 // otherwise the cluster with adjacent border are glued
00087 //#define GLUE_CLUSTER_CORNER
00088 
00089 // Minimal Peak energy when looking for peakarea's; used in cluster::GetPeaks
00090 #define MinPeakEnergy 0.08
00091 
00092 // Minimal shower energy when splitting peakarea onto showers; used in gamma(...) function
00093 #define MinShowerEnergy 0.1
00094 
00095 // Max number of clusters in sector; used in Find_Clusters(...)
00096 #define MaxLen 1000
00097 
00098 // Max number of peaks in cluster; used in cluster::GetPeaks(...)
00099 #define MaxNofPeaks 10
00100 
00101 // Used in cluster::GetPeaks(...)
00102 #define PeakIter 6
00103 
00104 // Emin cuts cluster energy: Ecl >= Epk1+Epk2 +...+Epkn !!!!!!!!!!!!!
00105 #define Emin 0.002
00106 
00107 // chisq=3 devides about 1% of single showers (now it isn't used)
00108 #define chisq 3.
00109 
00110 // Parameters for sigma in Hi2 calculations (p.36-37 v.3)
00111 float epar00 = 0.005;
00112 float epar0 = 0.0014;
00113 float epar1 = 0.03;
00114 float epar2 = -0.03;
00115 float epar3 = 0.;
00116 float epar4 = 4.0;
00117 
00118 // define meaningless values for (x,y)
00119 #define XABSURD -999999.
00120 #define YABSURD -999999.
00121 
00122 int EMCzSize = 72;
00123 int EMCySize = 36;
00124 float Tower_zSize = 5.535; // Tower size in z dir (cm)
00125 float Tower_ySize = 5.535; // Tower size in y dir (cm)
00126 
00127 float xVertex=0, yVertex=0, zVertex=0;
00128 float xSector=500, ySector=-97, zSector=-196;
00129 float normx=1, normy=0, normz=0;
00130 
00131 // Default level, now for the Conf Level: 1% for GEANT, 2%-5% for TestBeam
00132 float Chi2Level[50]={
00133     6.634899, 4.605171, 3.780564, 3.318915, 3.017103,    
00134     2.801872, 2.639259, 2.511249, 2.407341, 2.320967,    
00135     2.247720, 2.184744, 2.129863, 2.081515, 2.038526,    
00136     1.999994, 1.965214, 1.933627, 1.904781, 1.878311,    
00137     1.853912, 1.831334, 1.810365, 1.790825, 1.772564, 
00138     1.755449, 1.739367, 1.724222, 1.709926, 1.696406,    
00139     1.683593, 1.671430, 1.659864, 1.648850, 1.638344,    
00140     1.628311, 1.618716, 1.609528, 1.600721, 1.592268,    
00141     1.584148, 1.576338, 1.568822, 1.561579, 1.554596,    
00142     1.547856, 1.541346, 1.535055, 1.528968, 1.523077 };   
00143 
00144 // For the Conf Level: 1% for GEANT, 2%-5% for TestBeam
00145 float Chi2Level1[50]={
00146     6.634899, 4.605171, 3.780564, 3.318915, 3.017103,    
00147     2.801872, 2.639259, 2.511249, 2.407341, 2.320967,    
00148     2.247720, 2.184744, 2.129863, 2.081515, 2.038526,    
00149     1.999994, 1.965214, 1.933627, 1.904781, 1.878311,    
00150     1.853912, 1.831334, 1.810365, 1.790825, 1.772564, 
00151     1.755449, 1.739367, 1.724222, 1.709926, 1.696406,    
00152     1.683593, 1.671430, 1.659864, 1.648850, 1.638344,    
00153     1.628311, 1.618716, 1.609528, 1.600721, 1.592268,    
00154     1.584148, 1.576338, 1.568822, 1.561579, 1.554596,    
00155     1.547856, 1.541346, 1.535055, 1.528968, 1.523077 };   
00156 
00157 // For the Conf Level: 2% for GEANT, 4%-7% for TestBeam
00158 float Chi2Level2[50]={
00159     5.411895, 3.912024, 3.278443, 2.916812, 2.677547,    
00160     2.505458, 2.374582, 2.271008, 2.186567, 2.116065,    
00161     2.056169, 2.004491, 1.959343, 1.919481, 1.883964,    
00162     1.852072, 1.823237, 1.797008, 1.773021, 1.750981,    
00163     1.730640, 1.711795, 1.694274, 1.677931, 1.662643,    
00164     1.648301, 1.634814, 1.622101, 1.610093, 1.598727,    
00165     1.587948, 1.577709, 1.567968, 1.558684, 1.549824,    
00166     1.541357, 1.533256, 1.525494, 1.518051, 1.510903,    
00167     1.504033, 1.497424, 1.491059, 1.484924, 1.479006,    
00168     1.473292, 1.467771, 1.462433, 1.457267, 1.452265 };
00169 
00170 float sin4a, sinax, sinay;
00171 float ppar1, ppar2, ppar3, ppar4, pShiftx, pShifty;
00172 
00173 #define max(a,b)  ((a) > (b) ? (a) : (b))
00174 #define min(a,b)  ((a) < (b) ? (a) : (b))
00175 #define ABS(x)  ((x) < 0 ? -(x) : (x))
00176 #define lowint(x) ((x) < 0 ? int(x-1) : int(x))
00177 #define asinht(x) (log((x)+sqrt((x)*(x)+1)))
00178 #define sinht(x) ((exp(x)-exp(-(x)))/2.)
00179 
00180 int Hit_NCompare( const void*, const void* );
00181 int Hit_ACompare( const void*, const void* );
00182 void CopyVector( int*, int*, int );
00183 void CopyVector( hit*, hit*, int );
00184 void ZeroVector( int*, int );
00185 void ZeroVector( float*, int );
00186 void ZeroVector( hit*, int );
00187 void ResizeVector( int* , int, int );
00188 void CorrectPosition( float, float, float, float*, float* );
00189 void CalculateErrors( float e, float x, float y, float* pde, float* pdx, float* pdy, float* pdz);
00190 void GlobalToSector( float, float, float, float*, float*, float* );
00191 void SectorToGlobal( float xsec, float ysec, float zsec, float* px, float* py, float* pz );
00192 void SectorToGlobalErr( float dxsec, float dysec, float dzsec, float* pdx, float* pdy, float* pdz );
00193 void gamma(int, hit*, float*, float*, float*, float*, float*, float*, float*, float*);
00194 void twogamma(int, hit*, float*, float*, float*, float*, float*, float*, float*);
00195 void mom1(int, hit*, float*, float*, float*);
00196 void momenta(int, hit*, float*, float*, float*, float*, float*, float* );
00197 void c3to5(float, float, float, float, float, float, float*, float*, float*,  float*, float*, float*);
00198 void SetProfileParameters(int, float, float, float);
00199 float ClusterChisq(int, hit*, float, float, float);
00200 float Chi2Limit( int ND );
00201 
00202 void emshower::GetCorrPos( float* px, float* py )
00203 // Returns emshower corrected position in Sector (SM) frame
00204 {
00205    CorrectPosition( energy, xcg, ycg, px, py );
00206 }
00207 
00208 void emshower::GetGlobalPos( float* px, float* py, float* pz )
00209 // Returns emshower position in PHENIX global coord system
00210 {
00211    float xc, yc;
00212 
00213    GetCorrPos( &xc, &yc );
00214 // X in Sector coord is Z in Global coord !!
00215    SectorToGlobal( 0, yc, xc, px, py, pz );
00216 }
00217 
00218 void cluster::GetCorrPos( float* px, float* py )
00219 // Returns the cluster corrected position in Sector (SM) frame
00220 {
00221    float e, x, y, xx, yy, xy;
00222 
00223    e = GetTotalEnergy();
00224    GetMoments( &x, &y, &xx, &xy, &yy );
00225    CorrectPosition( e, x, y, px, py );
00226 }
00227 
00228 void cluster::GetGlobalPos( float* px, float* py, float* pz )
00229 // Returns the cluster position in PHENIX global coord system
00230 {
00231    float xc, yc;
00232 
00233    GetCorrPos( &xc, &yc );
00234 // X in Sector coord is Z in Global coord !!
00235    SectorToGlobal( 0, yc, xc, px, py, pz );
00236 }
00237 
00238 float cluster::GetTowerEnergy( int ich )
00239 // Returns the energy of the ich-tower (0 if ich not found in the HitList)
00240 {
00241      list<hit>::iterator ph;
00242      if( HitList.empty() ) return 0;
00243      ph = HitList.begin();
00244      while( ph != HitList.end() ) {
00245        if( (*ph).ich == ich ) return (*ph).amp;
00246         ph++;
00247      }
00248      return 0;
00249 }
00250 
00251 float cluster::GetTowerToF( int ich )
00252 // Returns the ToF of the ich-tower (0 if ich not found in the HitList)
00253 {
00254      list<hit>::iterator ph;
00255      if( HitList.empty() ) return 0;
00256      ph = HitList.begin();
00257      while( ph != HitList.end() ) {
00258        if( (*ph).ich == ich ) return (*ph).tof;
00259         ph++;
00260      }
00261      return 0;
00262 }
00263 
00264 float cluster::GetTotalEnergy()
00265 // Returns the cluster total energy
00266 {
00267      list<hit>::iterator ph;
00268      float et=0;
00269      if( HitList.empty() ) return 0;
00270      ph = HitList.begin();
00271      while( ph != HitList.end() ) { 
00272         et += (*ph).amp;
00273         ph++;
00274      }
00275      return et;
00276 }
00277 
00278 float cluster::GetE9( int ich )
00279 // Returns the energy in 3x3 towers around the tower ich
00280 {
00281      int i, ic, ixc, ix0, ichmax, ichmin, nhit;
00282      float e;
00283      hit *hlist, *vv;
00284      list<hit>::iterator ph;
00285 
00286      e=0;
00287 
00288      nhit = HitList.size();
00289 
00290      if( nhit <= 0 ) return e;
00291 
00292      hlist = new hit[nhit];
00293 
00294      ph = HitList.begin();
00295      vv = hlist;
00296      while( ph != HitList.end() ) *vv++ = *ph++;
00297 
00298      qsort( hlist, nhit, sizeof(hit), Hit_NCompare );
00299 
00300      ichmax = ich + EMCzSize + 1;
00301      ichmin = ich - EMCzSize - 1;
00302      ix0 = ich - ich/EMCzSize*EMCzSize;
00303 
00304      for( i=0; i<nhit; i++ ) 
00305         if( hlist[i].ich >= ichmin ) break;
00306      while( (i < nhit) && ( (ic=hlist[i].ich) <= ichmax) ) {
00307         ixc = ic - ic/EMCzSize*EMCzSize;
00308         if( abs(ixc-ix0) <= 1 ) e += hlist[i].amp;
00309         i++;
00310      }
00311         
00312      delete hlist;
00313      return e;
00314 }
00315 
00316 hit cluster::GetImpactTower()
00317 // Returns the hit corresponding to the reconstructed impact tower
00318 {
00319      float x, y;
00320      int ix, iy, ich;
00321      hit ht;
00322 
00323      GetCorrPos( &x, &y );
00324      ix = lowint(x/Tower_zSize + 0.5);
00325      iy = lowint(y/Tower_ySize + 0.5);
00326      if( ix < 0 || ix > EMCzSize-1 || iy < 0 || iy > EMCySize-1 ) {
00327         printf("????? EmcClusterChi2: Something wrong in GetImpactTower: (x,y)=(%f,%f)  (ix,iy)=(%d,%d) \n", x, y, ix, iy);
00328         ht.ich = -1;
00329         ht.amp = 0;
00330         ht.tof = 0;
00331         return ht;
00332      }
00333      else {
00334         ich = iy*EMCzSize + ix;
00335         ht.ich = ich;
00336         ht.amp = GetTowerEnergy( ich );
00337         ht.tof = GetTowerToF( ich );
00338 //      printf("Energy=%f  ich=%f\n",*pEnergy,*pich);
00339         return ht;
00340      }
00341 }
00342 
00343 hit cluster::GetMaxTower()
00344 // Returns the hit with the maximum energy
00345 {
00346      list<hit>::iterator ph;
00347      float emax=0;
00348      hit ht;
00349 
00350      ht.ich = -1;
00351      ht.amp = 0;
00352      ht.tof = 0;
00353      if( HitList.empty() ) return ht;
00354 
00355      ph = HitList.begin();
00356      while( ph != HitList.end() ) { 
00357         if( (*ph).amp > emax ) { emax = (*ph).amp; ht = *ph; }
00358         ph++;
00359      }
00360      return ht;
00361 }
00362 
00363 void cluster::GetHits(hit* phit, int n)
00364 // Returns n hits (sorted) with the maximum energy
00365 {
00366      int nhit;
00367      hit *hlist, *vv;
00368      list<hit>::iterator ph;
00369      list<hit> hl;
00370 
00371      ZeroVector( phit, n );
00372      nhit = HitList.size();
00373 
00374      if( nhit <= 0 ) return;
00375 
00376      hlist = new hit[nhit];
00377 
00378      ph = HitList.begin();
00379      vv = hlist;
00380      while( ph != HitList.end() ) *vv++ = *ph++;
00381 
00382      qsort( hlist, nhit, sizeof(hit), Hit_ACompare );
00383      for( int i=0; i<min(nhit,n); i++ ) phit[i]=hlist[i];
00384      delete hlist;
00385 }
00386 
00387 void cluster::GetMoments( float* px, float* py, float* pxx, float* pxy, float* pyy )
00388 //  Returns cluster 1-st (px,py) and 2-d momenta (pxx,pxy,pyy)
00389 {
00390      list<hit>::iterator ph;
00391      float e, x, y, xx, yy, xy;
00392      int nhit;
00393      hit *phit, *p;
00394 
00395      *px = XABSURD; *py = YABSURD;
00396      *pxx = 0; *pxy = 0; *pyy = 0;
00397      nhit=HitList.size();
00398      if( nhit <= 0 ) return;
00399 
00400      phit = new hit[nhit];
00401      ph = HitList.begin();
00402      p=phit;
00403      while( ph != HitList.end() ) { 
00404         p->ich = (*ph).ich;
00405         p->amp = (*ph).amp;
00406         ph++;
00407         p++;
00408      }
00409      momenta( nhit, phit, &e, &x, &y, &xx, &yy, &xy );
00410      *px = x*Tower_zSize;
00411      *py = y*Tower_ySize;
00412      *pxx = xx*Tower_zSize*Tower_zSize;
00413      *pxy = xy*Tower_zSize*Tower_ySize;
00414      *pyy = yy*Tower_ySize*Tower_ySize;
00415 //      cout<<*pxx<<"  "<<*pyy<<"  "<<*pxy<<'\n';
00416      delete phit;
00417 }
00418 
00419 void cluster::GetErrors( float* pde, float* pdx, float* pdy, float* pdz)
00420 //  Returns the errors for the reconstructed energy and position
00421 {
00422     float e, x, y;
00423 
00424     e = GetTotalEnergy();
00425     GetCorrPos( &x, &y );
00426     CalculateErrors( e, x, y, pde, pdx, pdy, pdz);
00427 }
00428 
00429 void emshower::GetErrors( float* pde, float* pdx, float* pdy, float* pdz)
00430 // Returns the errors for the reconstructed energy and position
00431 {
00432     float e, x, y;
00433 
00434     e = GetTotalEnergy();
00435     GetCorrPos( &x, &y );
00436     CalculateErrors( e, x, y, pde, pdx, pdy, pdz);
00437 }
00438 
00439 void cluster::GetChar(  float* pe, 
00440                         float* pxcg, float* pycg, 
00441                         float* pxc, float* pyc, 
00442                         float* pxg, float* pyg, float* pzg,
00443                         float* pxx, float* pxy, float* pyy,
00444                         float* pde, float* pdx, float* pdy, float* pdz  )
00445 // This method replaces methods GetTotalEnergy, GetMoments, 
00446 // GetCorrPos, GetGlobalPos, GetErrors
00447 {
00448     *pe = GetTotalEnergy();
00449     GetMoments( pxcg, pycg, pxx, pxy, pyy );
00450     CorrectPosition( *pe, *pxcg, *pycg, pxc, pyc );
00451     SectorToGlobal( 0, *pyc, *pxc, pxg, pyg, pzg );
00452     CalculateErrors( *pe, *pxc, *pyc, pde, pdx, pdy, pdz);
00453 }
00454 
00455 void peakarea::GetChar( float* pe, 
00456                         float* pxcg, float* pycg, 
00457                         float* pxcgmin, float* pycgmin, 
00458                         float* pxc, float* pyc, 
00459                         float* pxg, float* pyg, float* pzg,
00460                         float* pxx, float* pxy, float* pyy,
00461                         float* pchi,
00462                         float* pde, float* pdx, float* pdy, float* pdz  )
00463 // This method replaces "cluster" methods GetTotalEnergy, GetMoments,
00464 // GetCorrPos, GetGlobalPos, GetErrors and "peakarea" methods GetCGmin, GetChi2
00465 {
00466     float chi, chi0, chicorr;
00467     float e1, x1, y1, e2, x2, y2;
00468     int nh;
00469     hit *phit, *vv;
00470     list<hit>::iterator ph;
00471     list<hit> hl;
00472 
00473     *pe = 0;
00474     hl = HitList;
00475     nh = hl.size();
00476     if( nh <= 0 ) return;
00477 
00478     phit = new hit[nh];
00479 
00480     ph = hl.begin();
00481     vv = phit;
00482     while( ph != hl.end() ) *vv++ = *ph++;
00483 
00484     chi = chisq*1000;
00485     gamma(nh,phit,&chi,&chi0,&e1,&x1,&y1,&e2,&x2,&y2);
00486     chicorr = Chi2Correct(chi0,nh);
00487     //    chi0 = chicorr;
00488     *pchi = chi0;
00489     // Shower Center of Gravity after shower profile fit
00490     *pxcgmin = x1*Tower_zSize;
00491     *pycgmin = y1*Tower_ySize;
00492 
00493     *pe = GetTotalEnergy();
00494     GetMoments( pxcg, pycg, pxx, pxy, pyy );
00495 //    CorrectPosition( *pe, *pxcg, *pycg, pxc, pyc );
00496     CorrectPosition( *pe, *pxcgmin, *pycgmin, pxc, pyc );
00497     SectorToGlobal( 0, *pyc, *pxc, pxg, pyg, pzg );
00498     CalculateErrors( *pe, *pxc, *pyc, pde, pdx, pdy, pdz);
00499 
00500     delete phit;
00501 }
00502 
00503 
00504 void emshower::GetChar( float* pe, 
00505                         float* pxcg, float* pycg, 
00506                         float* pxc, float* pyc, 
00507                         float* pxg, float* pyg, float* pzg, 
00508                         float* pchi,
00509                         float* pde, float* pdx, float* pdy, float* pdz  )
00510 // This method replaces methods GetTotalEnergy, GetCG, 
00511 // GetCorrPos, GetGlobalPos, GetChi2, GetErrors
00512 {
00513     *pe = GetTotalEnergy();
00514     GetCG( pxcg, pycg );
00515     CorrectPosition( *pe, *pxcg, *pycg, pxc, pyc );
00516     SectorToGlobal( 0, *pyc, *pxc, pxg, pyg, pzg );
00517     CalculateErrors( *pe, *pxc, *pyc, pde, pdx, pdy, pdz);
00518     *pchi = GetChi2();
00519 }
00520 
00521 
00522 float peakarea::GetChi2()
00523 // Returns Hi2 after its minimization fluctuating CG position 
00524 // (i.e. after shower profile fit)
00525 {
00526     float chi, chi0, chicorr;
00527     float e1, x1, y1, e2, x2, y2;
00528     int nh;
00529     hit *phit, *vv;
00530     list<hit>::iterator ph;
00531     list<hit> hl;
00532 
00533     hl = HitList;
00534     nh = hl.size();
00535     if( nh <= 0 ) return 0;
00536 
00537     phit = new hit[nh];
00538 
00539     ph = hl.begin();
00540     vv = phit;
00541     while( ph != hl.end() ) *vv++ = *ph++;
00542 
00543     chi = chisq*1000;
00544     gamma(nh,phit,&chi,&chi0,&e1,&x1,&y1,&e2,&x2,&y2);
00545 
00546     chicorr = Chi2Correct(chi0,nh);
00547     //    chi0 = chicorr;
00548 
00549     delete phit;
00550     return chi0;
00551 }
00552 
00553 void peakarea::GetCGmin( float* px, float* py )
00554 // Gets CG coordinates corresponding to min Hi2 (after shower shape fit)
00555 {
00556     float chi, chi0;
00557     float e1, x1, y1, e2, x2, y2;
00558     int nh;
00559     hit *phit, *vv;
00560     list<hit>::iterator ph;
00561     list<hit> hl;
00562 
00563     *px = XABSURD;
00564     *py = YABSURD;
00565     hl = HitList;
00566     nh = hl.size();
00567     if( nh <= 0 ) return;
00568 
00569     phit = new hit[nh];
00570 
00571     ph = hl.begin();
00572     vv = phit;
00573     while( ph != hl.end() ) *vv++ = *ph++;
00574 
00575     chi = chisq*1000;
00576     gamma(nh,phit,&chi,&chi0,&e1,&x1,&y1,&e2,&x2,&y2);
00577     *px = x1*Tower_zSize;
00578     *py = y1*Tower_ySize;
00579 
00580     delete phit;
00581 }
00582 
00583 int peakarea::GetGammas( emshower* ShList)
00584 //
00585 // Splits the peakarea onto 1 or 2 emshower's
00586 // Returns Number of Showers (0, 1 or 2)
00587 //
00588 // If the Chi2 of the peakarea is less then the value determined 
00589 // in Chi2Limit(Number_of_Hits) function, 1-photon hypothesis is 
00590 // accepted, otherwise the 2-shower hypothesis is checked
00591 //
00592 {
00593      float chi, chi0, e1, x1, y1, e2, x2, y2, chicorr;
00594      int nh, ig;
00595      hit *phit, *vv;
00596      emshower sh1, sh2;
00597      list<hit>::iterator ph;
00598      list<hit> hl;
00599 
00600 //     (*ShList).erase( (*ShList).begin(), (*ShList).end() );
00601 
00602      hl = HitList;
00603      nh = hl.size();
00604      if( nh <= 0 ) return 0;
00605 
00606      phit = new hit[nh];
00607 
00608      ph = hl.begin();
00609      vv = phit;
00610      while( ph != hl.end() ) *vv++ = *ph++;
00611 
00612 //     chi=chisq;
00613      chi=Chi2Limit(nh);
00614      gamma(nh,phit,&chi,&chi0,&e1,&x1,&y1,&e2,&x2,&y2);
00615 //      printf("GetGammas: e,x,y=(%f,%f,%f) ,y=(%f,%f,%f) \n",e1,x1,y1,e2,x2,y2);
00616      chicorr = Chi2Correct(chi,nh);
00617      //     chi = chicorr;
00618      if( e1 > 0 )
00619          sh1.ReInitialize( e1, x1*Tower_zSize, y1*Tower_ySize, chi );
00620      else
00621          sh1.ReInitialize( 0, XABSURD, YABSURD, 0 );
00622 
00623      if( e2 > 0 )
00624          sh2.ReInitialize( e2, x2*Tower_zSize, y2*Tower_ySize, chi );
00625      else
00626          sh2.ReInitialize( 0, XABSURD, YABSURD, 0 );
00627 
00628      if( e2 > e1 ) {
00629        sh1.ReInitialize( e2, x2*Tower_zSize, y2*Tower_ySize, chi );
00630        sh2.ReInitialize( e1, x1*Tower_zSize, y1*Tower_ySize, chi );
00631      }
00632 
00633      ig = 0;
00634      if( e1>0 ) {
00635        ShList[ig++]=sh1;
00636        if( e2>0 ) {
00637          ShList[ig++]=sh2;
00638        }
00639      }
00640 
00641      delete phit;
00642      return ig;
00643 }
00644 
00645 int cluster::GetPeaks( peakarea* PkList, hit* ppeaks )
00646 //
00647 // Splits the cluster onto peakarea's
00648 // The number of peakarea's is equal to the number of Local Maxima 
00649 // in the cluster. Local Maxima can have the energy not less then 
00650 // defined in MinPeakEnergy
00651 //
00652 // Output: PkList - the array of peakarea's
00653 //         ppeaks - the array of peak hits (one for each peakarea)
00654 //
00655 // Returns: >= 0 Number of Peaks;
00656 //            -1 The number of Peaks is greater then MaxNofPeaks;
00657 //               (just increase parameter MaxNofPeaks)
00658 //
00659 {
00660      int npk, ipk, nhit;
00661      int ixypk, ixpk, iypk, in, nh, ic;
00662      int ixy, ix, iy, nn;
00663      int ig, ng, igmpk1[MaxNofPeaks], igmpk2[MaxNofPeaks];
00664      int PeakCh[MaxNofPeaks];
00665      float epk[MaxNofPeaks*2], xpk[MaxNofPeaks*2], ypk[MaxNofPeaks*2];
00666      float ratio, eg, dx, dy, a, chi, chi0;
00667      float *Energy[MaxNofPeaks], *totEnergy, *tmpEnergy;
00668      hit *ip;
00669      hit *phit, *hlist, *vv;
00670      peakarea peak;
00671      list<hit>::iterator ph;
00672      list<hit> hl;
00673 
00674 //     (*PkList).erase( (*PkList).begin(), (*PkList).end() );
00675      nhit = HitList.size();
00676 
00677      if( nhit <= 0 ) return 0;
00678 
00679      hlist = new hit[nhit];
00680 
00681      ph = HitList.begin();
00682      vv = hlist;
00683      while( ph != HitList.end() ) *vv++ = *ph++;
00684 
00685      qsort( hlist, nhit, sizeof(hit), Hit_NCompare );
00686 //
00687 //  Find peak (maximum) position (towers with local maximum amp)
00688 //
00689      npk=0;
00690      for( ic=0; ic<nhit; ic++ ) {
00691         float amp = hlist[ic].amp;
00692         if( amp > MinPeakEnergy ) {
00693            int ich = hlist[ic].ich;
00694            int ichmax = ich + EMCzSize + 1;
00695            int ichmin = ich - EMCzSize - 1;
00696            int ixc = ich - ich/EMCzSize*EMCzSize;
00697            int in = ic + 1;
00698            while( (in < nhit) && (hlist[in].ich <= ichmax) ) {
00699               int ix = hlist[in].ich - hlist[in].ich/EMCzSize*EMCzSize;
00700               if( (abs(ixc-ix) <= 1) && (hlist[in].amp >= amp) ) goto new_ic;
00701               in++;
00702            }
00703            in = ic - 1;
00704            while( (in >= 0) && (hlist[in].ich >= ichmin) ) {
00705               int ix = hlist[in].ich - hlist[in].ich/EMCzSize*EMCzSize;
00706               if( (abs(ixc-ix) <= 1) && (hlist[in].amp > amp) ) goto new_ic;
00707               in--;
00708            }
00709            if( npk >= MaxNofPeaks ) { delete hlist; return -1; }
00710            PeakCh[npk]=ic;
00711 //      cout << npk << "   "<<PeakCh[npk]<<"\n";
00712            npk++;
00713         }
00714 new_ic: continue;
00715      }
00716 
00717      if( npk <= 0 ) { delete hlist; return 0; }
00718      if( npk == 1 ) {
00719         *ppeaks = hlist[PeakCh[0]];
00720         hl.erase( hl.begin(), hl.end() );
00721         for( int ich=0; ich<nhit; ich++ ) hl.push_back(hlist[ich]);
00722         peak.ReInitialize(hl);
00723 //      PkList->push_back(peak);
00724         PkList[0]=peak;
00725         delete hlist;
00726         return 1;
00727      }
00728 
00729      for( ipk=0; ipk<npk; ipk++ ) { Energy[ipk] = new float[nhit]; }
00730      totEnergy = new float[nhit];
00731      tmpEnergy = new float[nhit];
00732 //
00733 // Divide energy in towers among photons positioned in every peak
00734 //
00735      ratio = 1.;
00736      for( int iter=0; iter<PeakIter; iter++ ) {
00737         ZeroVector( tmpEnergy, nhit );
00738         for( ipk=0; ipk<npk; ipk++ ) {
00739 
00740            ic = PeakCh[ipk];
00741            if( iter > 0 ) ratio = Energy[ipk][ic]/totEnergy[ic];
00742            eg = hlist[ic].amp * ratio;
00743            ixypk = hlist[ic].ich;
00744            iypk = ixypk/EMCzSize;
00745            ixpk = ixypk - iypk*EMCzSize;
00746            epk[ipk] = eg;
00747            xpk[ipk] = eg * ixpk;
00748            ypk[ipk] = eg * iypk;
00749 
00750            if( ic < nhit-1 ){
00751              for( in=ic+1; in<nhit; in++ ) {
00752                 ixy = hlist[in].ich;
00753                 iy = ixy/EMCzSize;
00754                 ix = ixy - iy*EMCzSize;
00755                 if( (ixy-ixypk) > EMCzSize+1 ) break;
00756                 if( abs(ix-ixpk) <= 1 ) {
00757                    if( iter > 0 ) ratio = Energy[ipk][in]/totEnergy[in];
00758                    eg = hlist[in].amp * ratio;
00759                    epk[ipk] += eg;
00760                    xpk[ipk] += eg*ix;
00761                    ypk[ipk] += eg*iy;
00762                 }
00763              }
00764            } // if ic
00765 
00766            if( ic >= 1 ){
00767              for( in=ic-1; in >= 0; in-- ) {
00768                 ixy = hlist[in].ich;
00769                 iy = ixy/EMCzSize;
00770                 ix = ixy - iy*EMCzSize;
00771                 if( (ixypk-ixy) > EMCzSize+1 ) break;
00772                 if( abs(ix-ixpk) <= 1 ) {
00773                    if( iter > 0 ) ratio = Energy[ipk][in]/totEnergy[in];
00774                    eg = hlist[in].amp * ratio;
00775                    epk[ipk] += eg;
00776                    xpk[ipk] += eg*ix;
00777                    ypk[ipk] += eg*iy;
00778                 }
00779              }
00780            } // if ic
00781 
00782            xpk[ipk] = xpk[ipk]/epk[ipk];
00783            ypk[ipk] = ypk[ipk]/epk[ipk];
00784            SetProfileParameters(0, epk[ipk], xpk[ipk], ypk[ipk]);
00785            for( in=0; in<nhit; in++ ) {
00786               ixy = hlist[in].ich;
00787               iy = ixy/EMCzSize;
00788               ix = ixy - iy*EMCzSize;
00789               dx = xpk[ipk]-ix;
00790               dy = ypk[ipk]-iy;
00791               a = 0;
00792               if( ABS(dx) < 2.5 && ABS(dy) < 2.5 ) a = epk[ipk]*EMCcell(dx, dy, -1);
00793               Energy[ipk][in] = a;
00794               tmpEnergy[in] += a;
00795            }
00796 
00797         } // for ipk
00798 
00799         for( in=0; in<nhit; in++ ) totEnergy[in] = max(0.000001,tmpEnergy[in]);
00800 
00801      } // for iter
00802 
00803      phit = new hit[nhit];
00804 
00805      ng=0;
00806      for( ipk=0; ipk<npk; ipk++ ) {
00807         nh=0;
00808         for( in=0; in<nhit; in++ ) {
00809            if( tmpEnergy[in] > 0 ) {
00810               ixy = hlist[in].ich;
00811               a = hlist[in].amp * Energy[ipk][in]/tmpEnergy[in];
00812               if( a > Emin ) {
00813                 phit[nh].ich=ixy;
00814                 phit[nh].amp=a;
00815                 phit[nh].tof= hlist[in].tof;  // Not necessary here
00816                 nh++;
00817               }
00818            }
00819         }
00820         if( nh>0 ) {
00821 //         chi=chisq*10;
00822            chi=Chi2Limit(nh)*10;
00823            gamma(nh,phit,&chi,&chi0,&epk[ng],&xpk[ng],&ypk[ng],&epk[ng+1],&xpk[ng+1],&ypk[ng+1]);
00824            igmpk1[ipk]=ng;
00825            igmpk2[ipk]=ng;
00826            if( epk[ng+1]>0 ) { ng++; igmpk2[ipk]=ng;}
00827            ng++;
00828         }
00829      }
00830 
00831      ZeroVector( tmpEnergy, nhit );
00832      for( ipk=0; ipk<npk; ipk++ ) {
00833         ig=igmpk1[ipk];
00834         SetProfileParameters(0, epk[ig], xpk[ig], ypk[ig]);
00835         for( in=0; in<nhit; in++ ) {
00836            Energy[ipk][in]=0;
00837            for(ig=igmpk1[ipk]; ig<=igmpk2[ipk]; ig++){
00838               ixy = hlist[in].ich;
00839               iy = ixy/EMCzSize;
00840               ix = ixy - iy*EMCzSize;
00841               dx = xpk[ig]-ix;
00842               dy = ypk[ig]-iy;
00843               a = epk[ig]*EMCcell(dx, dy, epk[ig]);
00844               Energy[ipk][in] += a;
00845               tmpEnergy[in] += a;
00846            }
00847         }
00848      }
00849 
00850      ip = ppeaks;
00851      nn=0;
00852      for( ipk=0; ipk<npk; ipk++ ) {
00853         nh=0;
00854         for( in=0; in<nhit; in++ ) {
00855            if( tmpEnergy[in] > 0 ) {
00856               ixy = hlist[in].ich;
00857               a = hlist[in].amp * Energy[ipk][in]/tmpEnergy[in];
00858               if( a > Emin ) {
00859                 phit[nh].ich=ixy;
00860                 phit[nh].amp=a;
00861                 phit[nh].tof=hlist[in].tof;
00862                 nh++;
00863               }
00864            }
00865         }
00866         if( nh>0 ) {
00867            *ip++ = hlist[PeakCh[ipk]];
00868            hl.erase( hl.begin(), hl.end() );
00869            for( in=0; in<nh; in++ ) hl.push_back(phit[in]);
00870            peak.ReInitialize(hl);
00871 //         PkList->push_back(peak);
00872            PkList[nn++]=peak;
00873         }
00874      }
00875 
00876      delete phit;
00877      delete hlist;
00878      for( ipk=0; ipk<npk; ipk++ ) delete Energy[ipk];
00879      delete totEnergy;
00880      delete tmpEnergy;
00881 
00882 //     return npk;
00883      return nn;
00884 }
00885 
00886 void gamma(int nh, hit* phit, float* pchi, float* pchi0, float* pe1, float* px1, float* py1, float* pe2, float* px2, float* py2)
00887 //
00888 // Tests for 1 or 2 photon hypothesis by minimizing the chi2.
00889 // If the energy of one of two showers is less then MinShowerEnergy 
00890 // they are merged
00891 //
00892 // Returns two shower parameters (pe1,px1,py1,pe2,px2,py2). 
00893 // If one shower hypothesis accepted -> *pe2=0
00894 // *pchi  - chi2 after splitting
00895 // *pchi0 - chi2 before splitting
00896 //
00897 {
00898      float e1, x1, y1, e2, x2, y2;
00899      float chi, chi0, chi00, chisq0, chisave;
00900      float chir, chil, chiu, chid;
00901      int dof;
00902      float x0, y0, d2, xm2;
00903      float stepx, stepy, parx, pary;
00904      const float dxy=0.06;
00905      const float stepmin=0.01;
00906      const float zTG=100;
00907      const float xmcut=0.0015; // (GeV), for overlapping showers separation
00908 
00909      *pe1=0;
00910      *px1=0;
00911      *py1=0;
00912      *pe2=0;
00913      *px2=0;
00914      *py2=0;
00915      if( nh <= 0 ) return;
00916      mom1(nh,phit,&e1,&x1,&y1);
00917      *pe1=e1;     
00918      if( e1 <= 0 ) return;
00919 
00920      SetProfileParameters(0, e1,x1,y1);
00921      chisave = *pchi;
00922      chi = *pchi;
00923      chi0 = ClusterChisq(nh,phit,e1,x1,y1);
00924 //      cout << " ChiSq=" << chi0 << "  " << e1 << "  " << x1 << "  " << y1 << '\n';
00925      chisq0 = chi0;
00926 //     dof = nh-2;
00927      dof = nh;
00928      if( dof < 1 ) dof=1;
00929      chi = chisq0/dof;
00930      x0 = x1;
00931      y0 = y1;
00932      for(;;){
00933         chir = ClusterChisq(nh,phit,e1,x0+dxy,y0);
00934         chil = ClusterChisq(nh,phit,e1,x0-dxy,y0);
00935         chiu = ClusterChisq(nh,phit,e1,x0,y0+dxy);
00936         chid = ClusterChisq(nh,phit,e1,x0,y0-dxy);
00937 
00938         if( (chi0 > chir) || (chi0 > chil) ) {
00939            stepx = dxy;
00940            if( chir > chil ) stepx = -stepx;
00941         }
00942         else {
00943            stepx = 0;
00944            parx = chir+chil-2*chi0;
00945            if( parx > 0 ) stepx = -dxy*(chir-chil)/2/parx;
00946         }
00947 
00948         if( (chi0 > chiu) || (chi0 > chid) ) {
00949            stepy = dxy;
00950            if( chiu > chid ) stepy = -stepy;
00951         }
00952         else {
00953            stepy = 0;
00954            pary = chiu+chid-2*chi0;
00955            if( pary > 0 ) stepy = -dxy*(chiu-chid)/2/pary;
00956         }
00957         if( (ABS(stepx) < stepmin) && (ABS(stepy) < stepmin) ) break;
00958         chi00 = ClusterChisq(nh,phit,e1,x0+stepx,y0+stepy);
00959         if( chi00 >= chi0 ) break;
00960         chi0 = chi00;
00961         x0 += stepx;
00962         y0 += stepy;
00963      }
00964      if( chi0 < chisq0 ) {
00965         x1 = x0;
00966         y1 = y0;
00967         chi = chi0/dof;
00968      }
00969 
00970      *pchi0 = chi;
00971      *pchi = chi;
00972      *px1 = x1;
00973      *py1 = y1;
00974 
00975      if( chi > chisave ) {
00976         twogamma(nh,phit,&chi,&e1,&x1,&y1,&e2,&x2,&y2);
00977         if( e2 > 0 ) {
00978            d2 = ((x1-x2)*(x1-x2) + (y1-y2)*(y1-y2))/zTG/zTG;
00979            xm2 = e1*e2*d2;
00980            if( xm2 > 0 ) xm2 = sqrt(xm2);
00981            if( xm2 > xmcut && e1 > MinShowerEnergy && e2 > MinShowerEnergy) {
00982                 *pe1 = e1;
00983                 *px1 = x1;
00984                 *py1 = y1;
00985                 *pe2 = e2;
00986                 *px2 = x2;
00987                 *py2 = y2;
00988                 *pchi = chi;
00989            }
00990         }       
00991      }
00992 //      printf("%f %f %f\n",*px1*Tower_zSize, *py1*Tower_ySize, *pchi);
00993 }
00994 
00995 void twogamma(int nh, hit* phit, float* pchi, float* pe1, float* px1, float* py1, float* pe2, float* px2, float* py2)
00996 {
00997      float e0, x0, y0, xx, yy, yx;
00998      float dxy, rsg2, rsq;
00999      float dxc, dyc, r, epsc;
01000      int ix, iy, ixy, in, iter, dof;
01001      float step, cosi, chisq2, u;
01002      float e1c, x1c, y1c, e2c, x2c, y2c;
01003      float eps0, eps1, eps2, chisqc, ex;
01004      float dx1, dy1, dx2, dy2, a0, d;
01005      float dchi, dchi0, dd, dchida, gr, a1, a2;
01006      float gre, grx, gry, grec, grxc, gryc, grc, gx1, gx2, gy1, gy2;
01007      float scal, dx0, dy0;
01008      const float epsmax=0.9999;
01009      const float stpmin=0.025;
01010      const float delch=2;
01011 
01012      momenta(nh,phit,&e0,&x0,&y0,&xx,&yy,&yx);
01013      *pe2 = 0;
01014      *px2 = 0;
01015      *py2 = 0;
01016      if( nh <= 0 ) return;
01017 //  choosing of the starting point
01018      dxy = xx-yy;
01019      rsg2 = dxy*dxy + 4*yx*yx;
01020      if( rsg2 < 1e-20 ) rsg2 = 1e-20;
01021      rsq = sqrt(rsg2);
01022      dxc = -sqrt((rsq+dxy)*2);
01023      dyc =  sqrt((rsq-dxy)*2);
01024      if( yx >= 0 ) dyc = -dyc;
01025      r = sqrt(dxc*dxc + dyc*dyc);
01026      epsc = 0;
01027      for( in=0; in<nh; in++ ) {
01028         ixy = phit[in].ich;
01029         iy = ixy/EMCzSize;
01030         ix = ixy - iy*EMCzSize;
01031         u = (ix-x0)*dxc/r + (iy-y0)*dyc/r;
01032         epsc -= phit[in].amp * u * ABS(u);
01033      }
01034      epsc /= (e0*rsq);
01035      if( epsc >  0.8 ) epsc = 0.8;
01036      if( epsc < -0.8 ) epsc =-0.8;
01037      dxc /= sqrt(1-epsc*epsc);
01038      dyc /= sqrt(1-epsc*epsc);
01039 //  Start of iterations
01040      step = 0.1;
01041      cosi = 0;
01042      chisq2 = 1.e35;
01043      for( iter=0; iter<100; iter++)
01044      {
01045         c3to5(e0,x0,y0,epsc,dxc,dyc,&e1c,&x1c,&y1c,&e2c,&x2c,&y2c);
01046         eps1 = (1+epsc)/2;
01047         eps2 = (1-epsc)/2;
01048         chisqc = 0;
01049         for( in=0; in<nh; in++ ) {
01050            ex = phit[in].amp;
01051            ixy = phit[in].ich;
01052            iy = ixy/EMCzSize;
01053            ix = ixy - iy*EMCzSize;
01054            dx1 = x1c - ix;
01055            dy1 = y1c - iy;
01056            dx2 = x2c - ix;
01057            dy2 = y2c - iy;
01058            a0 = e1c*EMCcell(dx1, dy1, e1c) + e2c*EMCcell(dx2, dy2, e2c);
01059            d = epar00*epar00 + e0*( epar1*a0/e0 + epar2*a0*a0/e0/e0 +epar3*a0*a0*a0/e0/e0/e0 ) + e0*sqrt(e0)*epar4*a0/e0*(1-a0/e0)*sin4a + e0*e0*epar0*epar0;
01060            chisqc += (a0-ex)*(a0-ex)/d;
01061         }
01062         if( chisqc >= chisq2 ) {
01063            if( iter > 0 ) {
01064                 dchi = chisqc-chisq2;
01065                 dchi0 = gr*step;
01066                 step /= (2*sqrt(1+dchi/dchi0));
01067            }
01068            step /= 2;
01069         }
01070         else {
01071 // Calculation of gradient
01072            grec = 0;
01073            grxc = 0;
01074            gryc = 0;
01075            for( in=0; in<nh; in++ ) {
01076                 ex = phit[in].amp;
01077                 ixy = phit[in].ich;
01078                 iy = ixy/EMCzSize;
01079                 ix = ixy - iy*EMCzSize;
01080                 dx1 = x1c - ix;
01081                 dy1 = y1c - iy;
01082                 dx2 = x2c - ix;
01083                 dy2 = y2c - iy;
01084                 a1 = e1c*EMCcell(dx1,dy1,e1c);
01085                 a2 = e2c*EMCcell(dx2,dy2,e2c);
01086                 a0 = a1 + a2;
01087                 d = epar00*epar00 + e0*( epar1*a0/e0 + epar2*a0*a0/e0/e0 +epar3*a0*a0*a0/e0/e0/e0 ) + e0*sqrt(e0)*epar4*a0/e0*(1-a0/e0)*sin4a + e0*e0*epar0*epar0;
01088                 dd = (a0-ex)/d;
01089                 dchida = dd*( 2 - dd*(epar1 + 2*epar2*a0/e0 + 3*epar3*a0*a0/e0/e0 + e0*sqrt(e0)*epar4*sin4a*(1-2*a0/e0) + 2*epar0*epar0*a0) );
01090                 gx1 = ( e1c*EMCcell(x1c+0.05-ix,dy1,e1c) - a1 )*20;
01091                 gx2 = ( e2c*EMCcell(x2c+0.05-ix,dy2,e2c) - a2 )*20;
01092                 gy1 = ( e1c*EMCcell(dx1, y1c+0.05-iy,e1c) - a1 )*20;
01093                 gy2 = ( e2c*EMCcell(dx2, y2c+0.05-iy,e2c) - a2 )*20;
01094                 grec += (dchida*((a1/e1c-a2/e2c)*e0 - (gx1+gx2)*dxc -(gy1+gy2)*dyc)/2);
01095                 grxc += (dchida*(gx1*eps2-gx2*eps1));
01096                 gryc += (dchida*(gy1*eps2-gy2*eps1));
01097            }
01098            grc = sqrt(grec*grec + grxc*grxc + gryc*gryc);
01099            if( grc < 1e-10 ) grc = 1e-10;
01100            if( iter > 0 ) {
01101                 cosi = (gre*grec + grx*grxc + gry*gryc ) / (gr*grc);
01102                 scal = ABS(gr/grc - cosi);
01103                 if( scal < 0.1 ) scal = 0.1;
01104                 step /= scal;
01105            }
01106            chisq2 = chisqc;
01107            eps0 = epsc;
01108            dx0 = dxc;
01109            dy0 = dyc;
01110            gre = grec;
01111            grx = grxc;
01112            gry = gryc;
01113            gr = grc;
01114         }
01115         epsc = eps0 - step*gre/gr;
01116         while( ABS(epsc) >= epsmax ) {
01117            step /= 2;
01118            epsc = eps0 - step*gre/gr;
01119         }
01120         dxc = dx0 - step*grx/gr;
01121         dyc = dy0 - step*gry/gr;
01122         if( step*gr < stpmin ) break;
01123      }
01124 //     if( (*pchi)*(nh-3)-chisq2 < delch ) return;
01125      if( (*pchi)*nh-chisq2 < delch ) return;
01126 //     dof = nh - 5;
01127      dof = nh;
01128      if( dof < 1 ) dof = 1;
01129      *pchi = chisq2/dof;
01130      c3to5(e0,x0,y0,eps0,dx0,dy0,pe1,px1,py1,pe2,px2,py2);
01131 }
01132 
01133 float ClusterChisq(int nh, hit* phit, float e, float x, float y)
01134 {
01135      float chi=0;
01136      int ixy, ix, iy;
01137      float et, a, d;
01138 
01139      for( int in=0; in<nh; in++ ) {
01140         ixy = phit[in].ich;
01141         iy = ixy/EMCzSize;
01142         ix = ixy - iy*EMCzSize;
01143         et = phit[in].amp;
01144         a = EMCcell(x-ix, y-iy, -1);
01145         d = epar00*epar00 + e*(epar1*a + epar2*a*a + epar3*a*a*a) + 
01146             e*sqrt(e)*epar4*a*(1-a)*sin4a + e*e*epar0*epar0;
01147         a *= e;
01148         chi += (et-a)*(et-a)/d;
01149 //      cout << nh << "  "<< in << "  "<< a/e << "  "<< x << "  "<< y << '\n';
01150      }
01151      return chi;
01152 }
01153 
01154 void mom1(int nh, hit* phit, float* pe, float* px, float* py)
01155 // First momentum calculation
01156 {
01157      float a, xw, yw, e;
01158      int ix, iy;
01159      hit* p;
01160 
01161      *pe=0;
01162      *px=0;
01163      *py=0;
01164      if( nh <= 0 ) return;
01165      p=phit;
01166      xw=0;
01167      yw=0;
01168      e=0;
01169      for( int i=0; i<nh; i++ ) {
01170         a = p->amp;
01171         iy = p->ich / EMCzSize;
01172         ix = p->ich - iy*EMCzSize;
01173         e += a;
01174         xw += ix*a;
01175         yw += iy*a;
01176         p++;
01177      }
01178      *pe = e;
01179      if( e <= 0 ) return;
01180      *px = xw/e;
01181      *py = yw/e;
01182 }
01183 
01184 void momenta(int nh, hit* phit, float* pe, float* px, float* py, float* pxx, float* pyy, float* pyx )
01185 // First and second momenta calculation
01186 {
01187      float a, x, y, e, xx, yy, yx;
01188      int ix, iy, i;
01189      hit* p;
01190 
01191      *pe=0;
01192      *px=0;
01193      *py=0;
01194      *pxx=0;
01195      *pyy=0;
01196      *pyx=0;
01197      if( nh <= 0 ) return;
01198 
01199      p=phit;
01200      x=0;
01201      y=0;
01202      e=0;
01203      xx=0;
01204      yy=0;
01205      yx=0;
01206      for( i=0; i<nh; i++ ) {
01207         a = p->amp;
01208         iy = p->ich / EMCzSize;
01209         ix = p->ich - iy*EMCzSize;
01210         e += a;
01211         x += ix*a;
01212         y += iy*a;
01213         xx += a*ix*ix;
01214         yy += a*iy*iy;
01215         yx += a*ix*iy;
01216         p++;
01217      }
01218      *pe = e;
01219      if( e <= 0 ) return;
01220      x /= e;
01221      y /= e;
01222      xx = xx/e - x*x;
01223      yy = yy/e - y*y;
01224      yx = yx/e - y*x;
01225 
01226      *px = x;
01227      *py = y;
01228      *pxx = xx;
01229      *pyy = yy;
01230      *pyx = yx;
01231 }
01232 
01233 void c3to5(float e0, float x0, float y0, float eps, float dx, float dy, float* pe1, float* px1, float* py1,  float* pe2, float* px2, float* py2)
01234 // 3 to 5-dim space conversion subroutine.
01235 // eps=(e1-e2)/e0,  (e0=e1+e2), x0*e0=x1*e1+x2*e2, dx=x1-x2
01236 {
01237      *pe1 = e0*(1+eps)/2;
01238      *pe2 = e0 - *pe1;
01239      *px1 = x0 + dx*(1-eps)/2;
01240      *py1 = y0 + dy*(1-eps)/2;
01241      *px2 = x0 - dx*(1+eps)/2;
01242      *py2 = y0 - dy*(1+eps)/2;
01243 }
01244 
01245 void SetProfileParameters( int sec, float Energy, float z, float y )
01246 //
01247 // Axis Z here means X in two dim Sector coord system !!! 
01248 // So one has to supply (x,y) coordinates in cell units (x instead of z)
01249 // If sec < 0 this routine changes only Energy dependent parameters - 
01250 // the angle dependent ones are set in the previous call
01251 //
01252 {
01253      float t;
01254      static float sin2ax, sin2ay, sin2a, lgE;
01255      float vx, vy, vz;
01256      float xVert, yVert, zVert;
01257      int sign;
01258 
01259      if( sec >= 0 ) {
01260 // Vertex coordinates in Sector coordinate system
01261         GlobalToSector( xVertex, yVertex, zVertex, &xVert, &yVert, &zVert );
01262         vx = xVert;
01263 // It is if we use the coordinates of the Sector Center (not zero-tower)
01264 //      vy = y - (EMCySize-1)*Tower_ySize/2 - yVert;
01265 //      vz = z - (EMCzSize-1)*Tower_zSize/2 - zVert;
01266         vy = y*Tower_ySize - yVert;
01267         vz = z*Tower_zSize - zVert;
01268 // From this point X coord in sector frame is Z coord in Global Coord System !!!
01269         sinax = vz/sqrt(vz*vz+vx*vx);
01270         sinay = vy/sqrt(vy*vy+vx*vx);
01271         if( ABS(sinax) > 0.5 || ABS(sinay) > 0.5 ) 
01272           printf("!!!!! mEmcClusterChi2: Something strange in SetProfileParameters: sinax=%f  sinay=%f\n",sinax, sinay);
01273 //      printf("Vertx=(%f,%f,%f)\n",xVert,yVert,zVert);
01274 //      printf("(Ax,Ay)=(%f,%f)\n", sinax, sinay);
01275         t = (vz*vz+vy*vy)/(vz*vz+vy*vy+vx*vx);
01276         sin2a=t;
01277         sin4a=t*t;
01278         sin2ax=sinax*sinax;
01279         sin2ay=sinay*sinay;
01280      }
01281 
01282      if( Energy <= 1.e-10 ) lgE=0;
01283      else lgE=log(Energy);
01284 
01285      ppar1=0.59-(1.45+0.13*lgE)*sin2a;
01286      ppar2=0.265+(0.80+0.32*lgE)*sin2a;
01287      ppar3=0.25+(0.45-0.036*lgE)*sin2a;
01288      ppar4=0.42;
01289 
01290      if( sinax > 0 ) sign = 1;
01291      else sign = -1;
01292      pShiftx = (1.05+0.12*lgE) * sin2ax * sign;
01293 //      pShiftx=0;
01294 
01295      if( sinay > 0 ) sign = 1;
01296      else sign = -1;
01297      pShifty = (1.05+0.12*lgE) * sin2ay * sign;
01298 //      pShifty=0;
01299 }
01300 
01301 
01302 float EMCcell( float xc, float yc, float en )
01303 //
01304 // Calculates the energy deposited in the tower, the distance between 
01305 // its center and shower Center of Gravity being (xc,yc)
01306 // en - shower energy
01307 // If en<0 -> no Shower Profile parameters change is needed
01308 //
01309 {
01310      float dx, dy, r1, r2, r3, e;
01311 
01312      if( en > 0 ) SetProfileParameters(-1,en,xc,yc);
01313      dx=ABS(xc-pShiftx);
01314      dy=ABS(yc-pShifty);
01315      e=0;
01316      r2=dx*dx+dy*dy;
01317      r1=sqrt(r2);
01318      r3=r2*r1;
01319      e=ppar1*exp(-r3/ppar2)+ppar3*exp(-r1/ppar4);
01320 
01321      return e;
01322 }
01323 
01324 //********************************************************************
01325 int Find_Clusters( list<hit> hlist, list<cluster>* pClList)
01326 //********************************************************************
01327 // Cluster search algorithm based on Lednev's one developed for GAMS.
01328 // Returns -1 if MaxLen parameter is too small (just increase it)
01329 {
01330         int nhit, nCl;
01331 //      static int MaxLen=1000;
01332 //      int* LenCl = new int[MaxLen];
01333         int LenCl[MaxLen];
01334         int next, ib, ie, ia, iab, iae, last, LastCl, leng, ich;
01335         hit *vv;
01336         hit *vhit, *vt;
01337         cluster Clt;
01338         list<hit>::iterator ph;
01339         list<hit> hl;
01340 
01341         (*pClList).erase(  (*pClList).begin(),  (*pClList).end() );
01342         nhit = hlist.size();
01343 
01344         if( nhit <= 0 ) return 0;
01345         if( nhit == 1 ) {
01346             Clt.ReInitialize( hlist );
01347             pClList->push_back(Clt);
01348             return 1;
01349         }
01350 
01351         vt = new hit[nhit];
01352         vhit = new hit[nhit];
01353 
01354         ph = hlist.begin();
01355         vv = vhit;
01356         while( ph != hlist.end() ) *vv++ = *ph++;
01357 
01358         qsort( vhit, nhit, sizeof(hit), Hit_NCompare );
01359 
01360         nCl=0;
01361         next = 0;
01362         for( ich=1; ich<nhit+1; ich++ ){
01363            if( ich<nhit ) ia=vhit[ich].ich;
01364            if( (ia-vhit[ich-1].ich > 1) || (ich >= nhit) || 
01365 // Protect against gluing together edges of the different rows
01366               (ia-ia/EMCzSize*EMCzSize == 0) ){
01367               ib=next;
01368               ie=ich-1;
01369               next=ich;
01370               if( nCl >= MaxLen ) {
01371                 return -1;
01372 //              ResizeVector(LenCl, MaxLen, MaxLen*2 );
01373 //              MaxLen *= 2;
01374               }
01375               nCl++;
01376               LenCl[nCl-1]=next-ib;
01377               if( nCl > 1 ) {
01378 // Job to glue the subclusters
01379                 iab=vhit[ib].ich;       // The last subcl begin
01380                 iae=vhit[ie].ich;       // The last subcl end
01381                 last=ib-1;              // The prelast subcl end
01382                 LastCl=nCl-2;
01383                 for( int iCl=LastCl; iCl>=0; iCl-- ){
01384                    leng=LenCl[iCl];
01385 #ifdef GLUE_CLUSTER_CORNER
01386 //*********** Begin: glue clusters with adjacent corner **********
01387 //                 if( (iab-vhit[last].ich > EMCzSize+1) ) goto new_ich;
01388                    if( (iab-vhit[last].ich > EMCzSize+1) || 
01389 // This is if iab-channel is on the left edge
01390                        ( (iab-vhit[last].ich == EMCzSize+1) &&
01391                          (iab-iab/EMCzSize*EMCzSize == 0) ) ) goto new_ich;
01392                    for( int ichc=last; ichc >= last-leng+1; ichc-- ) {
01393 //                      if( iab-vhit[ichc].ich >  EMCzSize+1 ) goto new_icl;
01394                         if( (iab-vhit[ichc].ich >  EMCzSize+1) ||  
01395 // This is if iab-channel is on the left edge
01396                             ( (iab-vhit[ichc].ich == EMCzSize+1) &&
01397                               (iab-iab/EMCzSize*EMCzSize == 0) ) ) goto new_icl;
01398 //                      if( iae-vhit[ichc].ich >= EMCzSize-1 ) {
01399                         if( (iae-vhit[ichc].ich >= EMCzSize-1) && 
01400 // This is if iae-channel is not on the right edge
01401                             ( (iae-vhit[ichc].ich != EMCzSize-1) || 
01402                               ((iae+1)-(iae+1)/EMCzSize*EMCzSize != 0) ) ) {
01403 //*********** End: glue clusters with adjacent corner **********
01404 #else
01405 //*********** Begin: glue clusters with adjacent edge **********
01406                    if( iab-vhit[last].ich > EMCzSize ) goto new_ich;
01407                    for( int ichc=last; ichc >= last-leng+1; ichc-- ) {
01408                         if( iab-vhit[ichc].ich > EMCzSize ) goto new_icl;
01409                         if( iae-vhit[ichc].ich >= EMCzSize ) {
01410 //*********** End: glue clusters with adjacent edge **********
01411 #endif
01412 //                        vt = new hit[leng];
01413                           CopyVector( &vhit[last+1-leng], vt, leng );
01414                           CopyVector( &vhit[last+1], &vhit[last+1-leng], ib-1-last );
01415                           CopyVector( vt, &vhit[ib-leng], leng );
01416                           for( int i=iCl; i<nCl-2; i++ ) LenCl[i]=LenCl[i+1];
01417                           ib -= leng;
01418                           LenCl[nCl-2]=LenCl[nCl-1]+leng;
01419                           nCl--;
01420                           goto new_icl;
01421                         }
01422                    }
01423 new_icl:           last=last-leng;
01424                 }
01425               }
01426            }
01427 new_ich:   continue;
01428         }
01429 //      cout << "NCluster=" << nCl << "\n";
01430 //      for( ich=0; ich<nhit; ich++ ) cout << vhit[ich].ich <<"\n";
01431         if( nCl > 0 ) {
01432            ib=0;
01433            for( int iCl=0; iCl<nCl; iCl++ ) { 
01434                 leng=LenCl[iCl];
01435                 hl.erase( hl.begin(), hl.end() );
01436                 for( ich=0; ich<leng; ich++ ) hl.push_back(vhit[ib+ich]);
01437                 Clt.ReInitialize(hl);
01438                 ib += LenCl[iCl];
01439                 pClList->push_back(Clt);
01440            }
01441         }
01442 //      delete LenCl;
01443         delete vhit;
01444         delete vt;
01445 
01446         return nCl;
01447 }
01448 
01449 
01450 void SetGeometry( SecGeom sg, float* Vertex )
01451 //
01452 // Sets the sector geometry and vertex position
01453 // Should be called before Find_Clusters(...)
01454 //
01455 {
01456 
01457 // The number of towers in Z and Y dir.
01458 
01459   EMCzSize = sg.nz;
01460   EMCySize = sg.ny;
01461 
01462 // Tower size in Z and Y dir.
01463 
01464   Tower_zSize = sg.Tower_zSize;
01465   Tower_ySize = sg.Tower_ySize;
01466 
01467 // Vertex coodinates (cm) in Global Coord System
01468 
01469    xVertex = Vertex[0];
01470    yVertex = Vertex[1];
01471    zVertex = Vertex[2];
01472 
01473 // Zero-Tower center coodinates (cm) in Global Coord System
01474 
01475    //   xSector=500;
01476    //   ySector=0;
01477    //   zSector=0;
01478    xSector = sg.xyz0[0];
01479    ySector = sg.xyz0[1];
01480    zSector = sg.xyz0[2];
01481 
01482 // Normal vector of the sector (tower?). For West Sectors it consides with Sector X' axis. We suppose, that Z global axis consides with Z' sector one
01483 
01484    //   normx=1;
01485    //   normy=0;
01486    //   normz=0;
01487    normx = sg.nxyz[0];
01488    normy = sg.nxyz[1];
01489    normz = sg.nxyz[2];
01490 
01491 //      printf(" ************ %d   %d   %f   %f\n", EMCzSize, EMCySize, Tower_zSize, Tower_ySize);
01492 
01493 }
01494 
01495 void GlobalToSector( float xgl, float ygl, float zgl, float* px, float* py, float* pz )
01496 {
01497     float x, y, z;
01498 
01499     x = xgl-xSector;
01500     y = ygl-ySector;
01501     z = zgl-zSector;
01502     *px =  normx*x + normy*y;
01503     *py = -normy*x + normx*y;
01504     *pz = z;
01505 }
01506 
01507 void SectorToGlobal( float xsec, float ysec, float zsec, float* px, float* py, float* pz ) 
01508 {
01509     *px =  normx*xsec - normy*ysec + xSector;
01510     *py =  normy*xsec + normx*ysec + ySector;
01511     *pz = zsec + zSector;
01512 }
01513 
01514 void SectorToGlobalErr( float dxsec, float dysec, float dzsec, float* pdx, float* pdy, float* pdz ) 
01515 {
01516     *pdx = sqrt( normx*normx*dxsec*dxsec + normy*normy*dysec*dysec );
01517     *pdy = sqrt( normy*normy*dxsec*dxsec + normx*normx*dysec*dysec );
01518     *pdz = dzsec;
01519 }
01520 
01521 void CalculateErrors( float e, float x, float y, float* pde, float* pdx, float* pdy, float* pdz)
01522 //
01523 // Returns the errors for the reconstructed energy and position 
01524 // (in the hypothesis of EM shower)
01525 // Should be called only just after CorrectPosition !!!
01526 //
01527 {
01528     float de, dy, dz, dxg, dyg, dzg;
01529     static float ae = 0.076, be = 0.022;        // de/E = a/sqrt(E)&b
01530     static float a = 0.57, b = 0.155, d = 1.6;  // dx = a/sqrt(E)+b (cm)
01531     // This is for hadrons
01532     //    static float a = 0.83, b = 0.95, d = 8.3;     // dx = a/sqrt(E)+b (cm)
01533     static float dx = 0.1;  // (cm)
01534 
01535 //      printf("Pos=(%f,%f) a=(%f,%f)  \n", x, y, sinax, sinay);
01536     de = sqrt( ae*ae*e + be*be*e*e );
01537     dz = a/sqrt(e) + b;
01538     dy = dz;
01539     dz = sqrt( dz*dz + d*d*sinax*sinax );
01540     dy = sqrt( dy*dy + d*d*sinay*sinay );
01541 
01542     SectorToGlobalErr( dx, dy, dz, &dxg, &dyg, &dzg );
01543 
01544     *pde = de;
01545     *pdx = dxg;
01546     *pdy = dyg;
01547     *pdz = dzg;
01548 }
01549 
01550 void CorrectPosition( float Energy, float x, float y, float* pxc, float* pyc )
01551 //
01552 // Corrects the Shower Center of Gravity for the systematic error due to 
01553 // the limited tower size and angle shift
01554 //
01555 // Everything here is in cm. 
01556 // (x,y) - CG position, (*pxc,*pyc) - corrected position
01557 //
01558 {
01559    float xShift, yShift, xZero, yZero, bx, by;
01560    float t, x0, y0;
01561    int ix0, iy0;
01562    int signx, signy;
01563 
01564    SetProfileParameters( 0, Energy, x/Tower_zSize, y/Tower_ySize );
01565    if( sinax > 0 ) signx =  1;
01566    else            signx = -1;
01567    if( sinay > 0 ) signy =  1;
01568    else            signy = -1;
01569    t = 1.93+0.383*log(Energy);
01570    // This is for hadrons !!!
01571    //   t = 3.5;
01572    xShift = t*sinax;
01573    yShift = t*sinay;
01574    xZero=xShift-(0.417*ABS(sinax)+1.500*sinax*sinax)*signx;
01575    yZero=yShift-(0.417*ABS(sinay)+1.500*sinay*sinay)*signy;
01576    t = 0.98 + 0.98*sqrt(Energy);
01577    bx = 0.16 + t*sinax*sinax;
01578    by = 0.16 + t*sinay*sinay;
01579    // This is for hadrons !!!
01580    //   bx = 0.5;
01581    //   by = 0.5;
01582 //      printf("Sin=(%f,%f) Shift=(%f,%f)  Zero=(%f,%f)\n",sinax,sinay,xShift,yShift,xZero,yZero);
01583 
01584    x0 = x/Tower_zSize;
01585    x0 = x0 - xShift + xZero;
01586    ix0 = lowint(x0 + 0.5);
01587    if( ABS(x0-ix0) <= 0.5 ) {
01588         x0 = (ix0-xZero)+bx*asinht( 2.*(x0-ix0)*sinht(0.5/bx) );
01589         *pxc = x0*Tower_zSize;
01590    }
01591    else {
01592         *pxc =  x - xShift*Tower_zSize;
01593         printf("????? mEmcClusterChi2: Something wrong in CorrectPosition: x=%f  dx=%f\n", x, x0-ix0);
01594    }
01595 
01596    y0 = y/Tower_ySize;
01597    y0 = y0 - yShift + yZero;
01598    iy0 = lowint(y0 + 0.5);
01599    if( ABS(y0-iy0) <= 0.5 ) {
01600         y0 = (iy0-yZero)+by*asinht( 2.*(y0-iy0)*sinht(0.5/by) );
01601         *pyc = y0*Tower_ySize;
01602    }
01603    else {
01604         *pyc = y - yShift*Tower_ySize;
01605         printf("????? mEmcClusterChi2: Something wrong in CorrectPosition: y=%f  dy=%f\n", y, y0-iy0);
01606    }
01607 
01608 }
01609 
01610 
01611 
01612 int Hit_NCompare( const void* h1, const void* h2 )
01613 {
01614      return ( ((hit*)h1)->ich - ((hit*)h2)->ich );
01615 }
01616 
01617 int Hit_ACompare( const void* h1, const void* h2 )
01618 {
01619      float amp1 = ((hit*)h1)->amp;
01620      float amp2 = ((hit*)h2)->amp;
01621      return (amp1<amp2) ? 1: (amp1>amp2) ? -1 : 0;
01622 }
01623 
01624 void ZeroVector( int* v, int N )
01625 {
01626         int* p = v;
01627         for( int i=0; i < N; i++ ) *p++ = 0;
01628 }
01629 
01630 void ZeroVector( float* v, int N )
01631 {
01632         float* p = v;
01633         for( int i=0; i < N; i++ ) *p++ = 0;
01634 }
01635 
01636 void ZeroVector( hit* v, int N )
01637 {
01638         for( int i=0; i < N; i++ ) { v[i].ich=0; v[i].amp=0; v[i].tof=0; }
01639 }
01640 
01641 void ResizeVector( int* Vector, int OldSize, int NewSize )
01642 {
01643         int* vsave;
01644 
01645         if( OldSize <= 0 ) { Vector = new int[NewSize]; return; }
01646         vsave = new int[OldSize];
01647         CopyVector( Vector, vsave, OldSize );
01648         delete Vector;
01649         Vector = new int[NewSize];
01650         if( NewSize > OldSize ) CopyVector( vsave, Vector, OldSize );
01651         else                    CopyVector( vsave, Vector, NewSize );
01652         delete vsave;
01653         return;
01654 }
01655 
01656 void CopyVector( int* from, int* to, int N )
01657 {
01658         if( N <= 0 ) return;
01659         for( int i=0; i<N; i++ ) to[i]=from[i];
01660 }
01661 
01662 void CopyVector( hit* from, hit* to, int N )
01663 {
01664         if( N <= 0 ) return;
01665         for( int i=0; i<N; i++ ) to[i]=from[i];
01666 }
01667 
01668 void SetChi2Limit( int limit )
01669 //
01670 // Sets the limit for PeakArea splitting onto 2 EMShowers:
01671 // limit=0 -> For the Conf Level: 0%
01672 // limit=1 -> For the Conf Level: 1% for GEANT, 2%-5% for TestBeam
01673 // limit=2 -> For the Conf Level: 2% for GEANT, 4%-7% for TestBeam
01674 //
01675 {
01676   int i;
01677 
01678   switch ( limit ) {
01679   case 0:
01680     for( i=0; i<50; i++ ) Chi2Level[i]=9999.;
01681     break;
01682   case 1:
01683     for( i=0; i<50; i++ ) Chi2Level[i]=Chi2Level1[i];
01684     break;
01685   case 2:
01686     for( i=0; i<50; i++ ) Chi2Level[i]=Chi2Level2[i];
01687     break;
01688   default:
01689     for( i=0; i<50; i++ ) Chi2Level[i]=Chi2Level1[i];
01690     break;
01691   }
01692 }
01693 
01694 
01695 float Chi2Limit( int ND )
01696 //  Here the reverse Chi2Correct function is used
01697 {
01698   float rn, a, b, chi2;
01699 
01700   if( ND < 1 ) return 9999.;
01701 
01702   chi2 = Chi2Level[min(ND,50)-1];
01703   if( chi2 > 100 ) return 9999.;
01704 
01705   rn = ND;
01706   b = 0.072*sqrt(rn);
01707   a = 6.21/(sqrt(rn)+4.7);
01708 
01709   return chi2*a/(1.-chi2*b);
01710 }
01711 
01712 
01713 float Chi2Correct( float Chi2, int ND )
01714 // Chi2 - is reduced Chi2: Chi2/ND !!
01715 {
01716   float rn, a, b, c;
01717 
01718   if( ND < 1 ) return 9999.;
01719 
01720   rn = ND;
01721   b = 0.072*sqrt(rn);
01722   a = 6.21/(sqrt(rn)+4.7);
01723   c = a + b*Chi2;
01724   if( c < 1 ) c=1;
01725 
01726   return Chi2/c;
01727 }
01728 
01729 void SetThreshold( float Thresh )
01730 {
01731   epar0 = Thresh*0.07;
01732   epar00 = max( Thresh/3, 0.005 );
01733 }
01734 
01735 
01736 /* Future improvements:
01737 
01738 1. Find_Clusters: to ensure that all hits are above energy threshold 
01739 set by SetThreshold routine (or default one)
01740 
01741 */