StRoot  1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
StFcsPulseAna.cxx
1 #include "StFcsPulseAna.h"
2 
3 ClassImp(StFcsPulseAna)
4 
5 double StFcsPulseAna::MaxwellBoltzmannDist(double* x, double* p)
6 {
7  if( x[0]<p[1] ){return p[3];}//Important condition for Maxwell Boltzmann distribution
8  double xoff2 = (x[0]-p[1])*(x[0]-p[1]);
9  return p[0]*TMath::Sqrt( 2.0/TMath::Pi() )
10  *( xoff2 * exp((-1.0*xoff2)/(2.0*p[2]*p[2])) )
11  / (p[2]*p[2]*p[2])
12  + p[3];
13 }
14 
16 {
17  mName = "StFcsPulseAna";
18  Init();
19 }
20 
22 {
23  mName = name;
24  Init();
25 }
26 
27 StFcsPulseAna::StFcsPulseAna(TGraph* Sig, std::string name):PeakAna(Sig)
28 {
29  mName = name;
30  Init();
31 }
32 
33 StFcsPulseAna::StFcsPulseAna(const StFcsPulseAna& old, const char* post_name, TGraph* graph):PeakAna(old,graph)
34 {
35  if( post_name ){ mName = old.mName+post_name; }
36  else{ mName = old.mName+"_copy"; }
37 
38  if( old.BaselineHist()!=0 ){mH1_Baseline = (TH1F*)old.BaselineHist()->Clone( (mName+"_H1_Basline").c_str() );}
39  if( old.BaselineFit()!=0 ){mF1_BaselineFit = (TF1*)old.BaselineFit()->Clone( (mName+"_F1_BaselineFit").c_str() );}
40  if( old.SignalFit()!=0 ){mF1_SignalFit = (TF1*)old.SignalFit()->Clone( (mName+"_F1_SignalFit").c_str() );}
41 }
42 
44 {
45  if( this == &rhs ){ return *this; }
46  //Note that the graph is not cloned because PeakAna should not be changing the contents of the data
48  mName = rhs.Name()+"_copy";
49 
50  if( rhs.BaselineHist()!=0 ){mH1_Baseline = (TH1F*)rhs.BaselineHist()->Clone( (mName+"_H1_Basline").c_str() );}
51  if( rhs.BaselineFit()!=0 ){mF1_BaselineFit = (TF1*)rhs.BaselineFit()->Clone( (mName+"_F1_BaselineFit").c_str() );}
52  if( rhs.SignalFit()!=0 ){ mF1_SignalFit = (TF1*)rhs.SignalFit()->Clone( (mName+"_F1_SignalFit").c_str() ); }
53 
54  return *this;
55 }
56 
58 {
59  mDbPulse = 0;
60 
61  mWindowSum = false; mSumAdc=0;
62  mFitSum = false; mSumAdcFit=0.0;
63 
64  mH1_Baseline = 0;
65  mF1_BaselineFit = 0;
66 
67  mF1_SignalFit = 0;
68 }
69 
70 void StFcsPulseAna::Copy(TObject& obj) const
71 {
72  PeakAna::Copy(obj);
73 
74  ((StFcsPulseAna&)obj).mDbPulse = mDbPulse;
75  ((StFcsPulseAna&)obj).mWindowSum = mWindowSum;
76  ((StFcsPulseAna&)obj).mSumAdc = mSumAdc;
77  ((StFcsPulseAna&)obj).mFitSum = mFitSum;
78  ((StFcsPulseAna&)obj).mSumAdcFit = mSumAdcFit;
79 
80  ((StFcsPulseAna&)obj).mH1_Baseline = 0;
81  ((StFcsPulseAna&)obj).mF1_BaselineFit = 0;
82  ((StFcsPulseAna&)obj).mF1_SignalFit = 0;
83 }
84 
85 TObject* StFcsPulseAna::Clone(const char* newname) const
86 {
87  TGraph* cloneg = (TGraph*)this->GetData()->Clone();
88  StFcsPulseAna* ana = 0;
89 
90  if( !strlen(newname) ){ ana = new StFcsPulseAna(cloneg,mName+"_copy"); }
91  else{ ana = new StFcsPulseAna(cloneg,newname); }
92  Copy(*ana);
93  ana->ForceInternal();//Cloned graph should be deleted by cloned object
94 
95  if( FoundPeakIndex()>=0 ){
96  //If mComputedIndex>=0 this means AnalyzeForPeak() was called and need to copy the peaks. This is preffered over re-running the analysis as copy should be faster than re-running the data
98  for( UInt_t i=0; i<mPeaks.size(); ++i ){ ana->mPeaks.push_back( mPeaks.at(i) ); }
99  ana->mFoundPeak = mFoundPeak;
100  }
101 
102  if( mH1_Baseline!=0 ){ ana->mH1_Baseline = (TH1F*)mH1_Baseline->Clone((ana->Name()+"_H1_Baseline").c_str()); }
103  if( mF1_BaselineFit!=0 ){ ana->mF1_BaselineFit = (TF1*)mF1_BaselineFit->Clone((ana->Name()+"_F1_Baseline").c_str()); }
104  if( mF1_SignalFit!=0 ){ ana->mF1_SignalFit = (TF1*)mF1_SignalFit->Clone((ana->Name()+"_F1_SignalFit").c_str()); }
105 
106  return ana;
107 }
108 
110 {
111  delete mH1_Baseline;
112  delete mF1_SignalFit;
113  delete mF1_BaselineFit;
114 
115 }
116 
118 {
119  PeakAna::SetData((TGraph*)0);//Effectively deletes and sets graph to zero and destroys found peaks
120  ResetBaseline();
121  ResetSum();
122 }
123 
125 {
126  mBaseline = -5.0;
127  mBaselineSigma = 0.75;
128  if( mH1_Baseline!=0 ){
129  mH1_Baseline->Reset();
130  mH1_Baseline->GetXaxis()->SetRangeUser(-0.5,4095.5);//Needs to go back to default since calling reset doesn't revert axes to original values
131  }
132  if( mF1_BaselineFit!=0 ){mF1_BaselineFit->ResetAttLine();}//Values from old fit will be replaced when 'AnalyzeForBaseline' gets called
133 }
134 
136 {
137  if( mF1_SignalFit!=0 ){ delete mF1_SignalFit; mF1_SignalFit=0; }//This was for plotting but still good to do just in case
138 
139  mWindowSum=false; mSumAdc = 0;
140  mFitSum=false; mSumAdcFit = 0.0;
141 }
142 
144 {
146  if( mTunnelThreshold<0 ){
147  mTunnelThreshold *= -1.0;
148  if( mTunnelThreshold<=1.0 ){
149  std::vector<PeakWindow> merged;
150  this->MergeByProbability(merged);
151  mPeaks.swap( merged );
152  mTunnelThreshold *= -1.0;//Restore to old value
154  }
155  }
156  if( mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ mFoundPeak = mPeaks.at(mComputedIndex); }
157  else{mFoundPeak.SetWindow(mXRangeMax+1,mXRangeMax+1);}//If no valid index then set found peak to greater than max X values so it is obvious something's wrong
158 
159  return mComputedIndex;
160 }
161 
162 void StFcsPulseAna::MergeByProbability(std::vector<PeakWindow>& merged) const
163 {
164  if( !merged.empty() ){ merged.clear(); }
165  for( UInt_t i=0; i<mPeaks.size(); ++i){
166  PeakWindow win = mPeaks.at(i);
167  if( merged.empty() ){ merged.push_back(win); continue; }
168  if( win.mStartX-merged.back().mEndX > 2.1 ){ merged.push_back(win); continue;}//Skip merging peaks that are more than 2 timebins away
169 
170  if( PeakTunnel(win) ){
171  if( merged.empty() ){ merged.push_back(win); continue; }//vector is empty so add first found peak
172  Double_t thisprob = merged.back().PeakTunnelProb(mG_Data,mTunnelScale,mTunnelSigma);
173  Double_t nextprob = win.PeakTunnelProb(mG_Data,mTunnelScale,mTunnelSigma);
174  merged.back().Combine(win,thisprob<=nextprob?true:false);//Take peak with highest probability
175  }
176  else{ merged.push_back(win); }
177  }
178 }
179 
181 {
182  ResetBaseline();//Resets histogram and function if they exist
183  if( mH1_Baseline==0){mH1_Baseline = new TH1F( (mName+"_"+"H1_Baseline").c_str(),"",4096,-0.5,4095.5); mH1_Baseline->Sumw2(); mH1_Baseline->SetTitle("Baseline Histogram");}
184  //Fill histogram of ADC values
185  for( int ipoint=0; ipoint<this->GetData()->GetN(); ++ipoint)
186  {
187  Double_t tb; Double_t adc;
188  Int_t check = this->GetData()->GetPoint(ipoint,tb,adc);
189  if( check<0 ){ LOG_WARN << "Unable to find point " << ipoint << " in Signal graph" << endm; continue;}
190  if( adc<0.1 ){continue;} //skip adc=0 from histogram since this is most likely missing data in non-pedestal subtracted data
191  mH1_Baseline->Fill(adc);
192  }
193  /*May 19, 2021: Fitting with too few data points gives bad baselines so just use max of histogram for now*/
194  mBaseline = mH1_Baseline->GetMaximumBin()-1;//Offset by one to account for bin counting
195  mBaselineSigma = 0.75;
196 
197  //Create a function to fit to histogram of ADC values
198  if( mF1_BaselineFit==0 ){ mF1_BaselineFit = new TF1((mName+"_"+"F1_BaselineFit").c_str(),"gaus(0)",
199  mH1_Baseline->GetXaxis()->GetXmin(),
200  mH1_Baseline->GetXaxis()->GetXmax()
201  );
202  }
203  if( FindBaseline() ){return;}
204  else
205  {
206  LOG_WARN << "Unable to find a proper baseline" << endm;
207  if( GetDebug()>2 ){ LOG_DEBUG << "->";Print();}
208  else{LOG_INFO << "->Found baseline:"<< mBaseline << " Found BaselineSigma:"<<mBaselineSigma << endm
209  << "->Setting baseline to 0 and sigma to 0.75 to prevent T0 algorithm from failing" << endm;}
210  mBaseline=0;
211  mBaselineSigma=0.75;
212  return;
213  }
214 }
215 
217 {
218  if( mBaseline > -0.001 )
219  {
220  if( GetDebug()>2 )
221  {
222  LOG_DEBUG << "|BaselineValue:"<<mBaseline;
223  LOG_DEBUG << "|BaselineSigma:"<<mBaselineSigma;
224  LOG_DEBUG << endm;
225  }
226  return true;
227  }
228  if( mF1_BaselineFit==0 ){ return false; }
229  if( mH1_Baseline==0 || mH1_Baseline->GetEntries()==0 ){mBaseline = -5.0; return false;}
230  //For nonpedestal data use full range for pedestal data find largest bin and use a +-3 bin buffer for the fit
231  Int_t XStartVal = mH1_Baseline->GetMaximumBin()-1;//Offset by one to make the value correct
232  if( GetDebug()>2 ){ LOG_DEBUG << "|Height:"<<mH1_Baseline->GetBinContent(XStartVal+1) << "|XStartVal:"<< XStartVal << "|Range:"<<3 <<"|StartSigma:"<<0.9 << endm; }
233  mF1_BaselineFit->SetRange(XStartVal-3,XStartVal+3);//Only do within this range
234  mF1_BaselineFit->SetParameter(0,mH1_Baseline->GetBinContent(XStartVal+1));
235  mF1_BaselineFit->SetParameter(1,XStartVal);//Use maximum instead of mean for more accurate center
236  mF1_BaselineFit->SetParameter(2,0.9);//Right now fixed but may need changing in the future?
237 
238  //Base line is mean of the gaussian fit
239  char opt[5] = "NRQ";
240  if( GetDebug()>2 ){ opt[2] = '\0';}//Hack to to not quiet fit output
241  Int_t FitStatus = mH1_Baseline->Fit(mF1_BaselineFit,opt);
242  if( GetDebug()>1 ){ LOG_DEBUG << "|FitStatus:"<< FitStatus << endm; }
243  if( FitStatus == 0 )
244  {
245  Double_t ratio = mF1_BaselineFit->GetChisquare()/static_cast<Double_t>(mF1_BaselineFit->GetNDF());
246  if( ratio > 10.0 ){ mBaseline = XStartVal; return true; }//Take care of bad baseline fits by setting baseline to maximum
247  else
248  {
249  mBaseline = static_cast<Double_t>(mF1_BaselineFit->GetParameter(1));
250  mBaselineSigma = static_cast<Double_t>( fabs(mF1_BaselineFit->GetParameter(2)) );//fabs in case sigma is negative
251  return true;
252  }
253  }
254  else if( FitStatus > 0 )
255  {
256  //Max works better
257  mBaseline = mH1_Baseline->GetMaximumBin()-1;//Offset by one to account for bin counting
258  mBaselineSigma = 0.75;
259  if(GetDebug()>1){LOG_WARN << "Fit failed to converge setting baseline to mean of histogram and of sigma to 0.75" << endm;}
260  return true;
261  }
262  else{return false;}
263 }
264 
265 void StFcsPulseAna::FillAdc(TGraphAsymmErrors* g, unsigned short& counter, int Start, unsigned short* adcdata)
266 {
267  //adcdata must be size 8 array
268  if( counter != 0 ){return;}//counter must start with zero size
269  unsigned int startpoint = 0;
270  double tb = 0;
271  double adc = 0;
272  for( unsigned int ipoint=0; static_cast<int>(ipoint)<g->GetN() && counter<8; ++ipoint ){
273  g->GetPoint(ipoint,tb,adc);
274  if( int(tb)>=Start ){ startpoint=ipoint; break; }
275  }
276 
277  if( startpoint==0 || static_cast<Int_t>(startpoint+8)>=g->GetN() ){return;}
278  while( int(tb-(Start+counter)) < int(8-counter) ){
279  counter = static_cast<unsigned short>(tb-Start);
280  adcdata[counter]=adc;
281  g->GetPoint(++startpoint,tb,adc);
282  }
283 }
284 
285 //Copied on Nov. 01, 2021
286 int StFcsPulseAna::SumDep0(TGraphAsymmErrors* gdata, int Start, int ped)
287 {
288  unsigned short Size = 0;//Must start with zero size
289  unsigned short AdcData[8] = {0};//Must be 8 timebin array
290  StFcsPulseAna::FillAdc(gdata,Size,Start,AdcData);
291  if( Size==0 ){return 0;}
292 
293  //double ped = Baseline();
294  int sum = 0 ;
295  int peak = 0 ;
296  int last = 0 ;
297 
298  for(int tb=0;tb<8;tb++) {
299 
300  unsigned short radc = AdcData[tb] ;
301 
302  switch(tb) {
303  case 0 :
304  last = radc ;
305  sum = radc ;
306  peak = 0 ;
307  break ;
308  case 1 :
309  if(radc>sum) peak = 1 ;
310  sum += radc ;
311  last = radc ;
312  break ;
313  case 2 :
314  sum += radc ;
315  last = radc ;
316  break ;
317  case 3 :
318  sum += radc ;
319  last = radc ;
320  break ;
321  case 4 :
322  sum += radc ;
323  last = radc ;
324  break ;
325  case 5 :
326  sum += radc ;
327  last = radc ;
328  break ;
329  case 6 :
330  sum += radc ;
331  last = radc ;
332  break ;
333  case 7 :
334  default :
335  //printf("radc %d, last %d, peak %d\n",radc,last,peak) ;
336 
337  if(radc>=last && peak==0) {
338  sum = 0 ;
339  }
340  else {
341  sum += radc ;
342 
343  sum -= ped*8 ; // ped is now only 3*ch_ped!
344  if(sum < 0) sum = 0 ;
345  }
346 
347  break ;
348  }
349  }
350  return sum;
351 
352 }
353 
354 int StFcsPulseAna::SumDep0Mod(TGraphAsymmErrors* gdata, int Start, int ped){
355  unsigned short Size = 0;//Must start with zero size
356  unsigned short AdcData[8] = {0};//Must be 8 timebin array
357  StFcsPulseAna::FillAdc(gdata,Size,Start,AdcData);
358  if( Size==0 ){return 0;}
359 
360  //double ped = Baseline();
361  int sum = 0 ;
362  int peak = 0 ;
363  int last = 0 ;
364 
365  for(int tb=0;tb<8;tb++) {
366 
367  unsigned short radc = AdcData[tb] ;
368 
369  switch(tb) {
370  case 0 :
371  last = radc ;
372  sum = radc ;
373  peak = 0 ;
374  break ;
375  case 1 :
376  if(radc>sum) peak = 1 ;
377  sum += radc ;
378  last = radc ;
379  break ;
380  case 2 :
381  sum += radc ;
382  last = radc ;
383  break ;
384  case 3 :
385  sum += radc ;
386  last = radc ;
387  break ;
388  case 4 :
389  sum += radc ;
390  last = radc ;
391  break ;
392  case 5 :
393  sum += radc ;
394  last = radc ;
395  break ;
396  case 6 :
397  if( peak!=0 || radc<last) peak = -1 ;
398  sum += radc ;
399  last = radc ;
400  break ;
401  case 7 :
402  default :
403  if( peak!=0 || radc<last ) peak = -1 ;//Needed to check last point
404  if( peak>0 ){ sum=0; }
405  else {
406  sum += radc ;
407 
408  sum -= ped*8 ; // ped is now only 3*ch_ped!
409  if(sum < 0) sum = 0 ;
410  }
411 
412  break ;
413  }
414  }
415  return sum;
416 
417 }
418 
419 Int_t StFcsPulseAna::Sum(Int_t Start, Int_t End)
420 {
421  Int_t SumAdc = 0;
422  Double_t base = Baseline();//Necessary to check if pedestal value has been set.
423  if( base < -4.0 ){return SumAdc;}
424  if( mG_Data==0 || mG_Data->GetN()==0 ){return SumAdc;}
425  for( Int_t ismp=0; ismp<this->GetData()->GetN(); ++ismp )
426  {
427  Double_t tb; Double_t adc;
428  GetData()->GetPoint(ismp,tb,adc);
429  if( tb < Start ){continue;}
430  if( tb > End ){ break; }
431  SumAdc += adc;
432  }
433  if( GetDebug()>1 ){LOG_DEBUG << "|Start:"<<Start << "|End:"<<End << "|SumAdcB:"<< SumAdc; }
434  SumAdc -= base*abs(End-Start+1);//Add one to account for the fact that the adc value at start and end is being summed
435  if( GetDebug()>1 ){LOG_DEBUG << "|SumAdcA:"<< SumAdc << "|BaseArea:"<<base*abs(End-Start+1) << endm; }
436  return SumAdc;
437 }
438 
440 {
441  if( mWindowSum ){ return mSumAdc; }
442  mSumAdc = 0;
443  //Upon failure to find a window Start and End value will be equal. In this case just set some default window. For run 2019 it will be 35-60 and triggered crossing was at 50
444  //For 2019 Ecal Cosmic running signal window was between 110-140
445  //if( SignalStart()==SignalEnd() ){ SetWindow(110,140); }
446  if( !GoodWindow() ){ return mSumAdc; }
447  mSumAdc = Sum( PeakStart(), PeakEnd() );
448  mWindowSum = true;
449  return mSumAdc;
450 }
451 
452 void StFcsPulseAna::GetMBPars(const double& xpeak, const double& xrise, const double& yh, const double& ped, double& height, double& scale )
453 {
454  /* For Clarification purposes
455  Double_t Xh = mFoundPeak.mPeakX; //X Position of peak
456  rise = mFoundPeak.mStartX; //X position where graph start to rise
457  scale = (Xh-Xrise)/TMath::Sqrt(2.0); //Scale factor
458  Double_t Yh = PeakY(); //Y height of peak
459  ped = Baseline(); //Y-Offset
460  height = ((Yh-ped)*TMath::Sqrt(TMath::Pi()/2.0)*scale*TMath::E())/2.0;//Height of Maxwell Boltzmann
461  */
462  scale = (xpeak-xrise)/TMath::Sqrt2();//Scale of Maxwell Boltzmann
463  height = ((yh-ped)*TMath::Sqrt(TMath::Pi()/2.0)*scale*TMath::E())/2.0;//Height of Maxwell Boltzmann
464  return;
465 }
466 
467 void StFcsPulseAna::SignalMBPars(double& height, double& scale)
468 {
469  if( !GoodWindow() ){return;}
471 }
472 
474 {
475  if( !GoodWindow() ){ return 0;}
476  double scale=0; double height=0;
477  SignalMBPars(height,scale);
478  return height;//This is the integral of the Maxwell-Boltzmann distribution above
479  //I=\int_0^\infty x^2*e^{-x^2/a^2} = \sqrt(\pi)*a^3*0.25 (a is scale factor;source(Mar1,2021):https://en.wikipedia.org/wiki/Gaussian_integral)
480  //For Maxwell boltzmann a=\sqrt(2)*scale and 0 goes to x0 (rise time)
481  //Using this and subsistitiution the integral becomes I=\sqrt(2*\pi)*scale^3*0.5*PreFactor
482  //Where PreFactor = height*\sqrt(2/\pi)/scale^3
483  //Putting it all together and simplifying I=height
484 }
485 
486 TF1* StFcsPulseAna::SignalFit(const char option)
487 {
488  switch(option)
489  {
490  case 'g': if( !mFitSum ){ GausFit(); } break;
491  case 'm': if( !mFitSum ){ MBFit(); } break;
492  case 'p': if( !mFitSum ){ PulseFit(); } break;
493  default: break;
494  }
495  return mF1_SignalFit;
496 }
497 
498 Double_t StFcsPulseAna::GausFit(Int_t Start,Int_t End)
499 {
500  if( mFitSum ){ return mSumAdcFit; }
501  mSumAdcFit = 0.0;
502  Double_t base = Baseline();//Necessary to check if pedestal value has been set
503  if( base < -4.0 ){ return mSumAdcFit; }
504  if( mG_Data==0 || mG_Data->GetN()==0 ){return mSumAdcFit;}
505  if( Start==0 && End==0 ){Start=PeakStart(); End=PeakEnd();}//Default case
506  //Upon failure to find a window 'Start' and 'End' value will be equal. In this case just set some default window. For run 2019 it will be 35-60 and triggered crossing was at 50
507  //For 2019 Ecal Cosmic running signal window was between 110-140
508  //if( Start==End ){ SetWindow(110,140); Start=115; End=135; }//40 and 55 here since I add to range later
509  //Sanity Checks
510  if( !GoodWindow() ){ return mSumAdcFit; }
511  if( mF1_SignalFit!=0 ){ delete mF1_SignalFit; mF1_SignalFit=0; }
512  mF1_SignalFit = new TF1( (mName+"_Gaus_"+"mF1_SignalFit").c_str(),"gaus(0)+[3]",Start,End);
513 
514  //mG_Data->GetXaxis()->SetRangeUser(Start-5,End+5);
515  mF1_SignalFit->SetParameter( 0, MaxY() );
516  mF1_SignalFit->SetParameter( 1, MaxX() );
517  mF1_SignalFit->SetParameter( 2, static_cast<Double_t>(End-Start)*0.2 );//Seemed to work most of the time with 0.3, 0.5 seemed too much
518  mF1_SignalFit->SetParameter( 3, base );
519  char opt[5] = "NRQ";
520  if( GetDebug()>2 ){ opt[2] = '\0';}//Hack to to not quiet fit output
521  Int_t fitstatus = this->GetData()->Fit( mF1_SignalFit, opt );
522  if( fitstatus >= 0 )
523  {
524  //Fix to 4 sigma for now in the future make it a variable?
525  Double_t FitStart = mF1_SignalFit->GetParameter(1) - 4.0*fabs(mF1_SignalFit->GetParameter(2));
526  Double_t FitEnd = mF1_SignalFit->GetParameter(1) + 4.0*fabs(mF1_SignalFit->GetParameter(2));
527  mSumAdcFit = mF1_SignalFit->Integral(FitStart,FitEnd);//Sum only in the range and subtract the area under the baseline
528  if( GetDebug()>2 ){LOG_DEBUG << "|FitStart:"<<FitStart << "|FitEnd:"<<FitEnd << "|FitSumB:"<<mSumAdcFit; }
529  mSumAdcFit -= ((FitEnd-FitStart)*base);
530  if( GetDebug()>2 ){LOG_DEBUG << "|FitSumA:"<<mSumAdcFit << "|BaseArea:"<<((FitEnd-FitStart)*base) << endm; }
531  mFitSum = true;
532  return mSumAdcFit;
533  }
534  else{ mFitSum = true; return mSumAdcFit; }//If fit failed return 0 don't do a histogram sum
535 }
536 
537 Double_t StFcsPulseAna::MBFit(Int_t Start,Int_t End)
538 {
539  if( mFitSum ){ return mSumAdcFit; }
540  mSumAdcFit = 0.0;
541  Double_t base = Baseline();//Necessary to check if pedestal value has been set
542  if( base < -4.0 ){ return mSumAdcFit; }
543  if( mG_Data==0 || mG_Data->GetN()==0 ){return mSumAdcFit;}
544  if( Start==0 && End==0 ){Start=PeakStart(); End=PeakEnd();}//Default case
545  //Upon failure to find a window 'Start' and 'End' value will be equal. In this case just set some default window. For run 2019 it will be 35-60 and triggered crossing was at 50
546  //For 2019 Ecal Cosmic running signal window was between 110-140
547  //if( Start==End ){ SetWindow(110,140); Start=115; End=135; }//40 and 55 here since I add to range later
548  //Sanity Checks
549  if( !GoodWindow() ){ return mSumAdcFit; }
550  if( mF1_SignalFit!=0 ){ delete mF1_SignalFit; mF1_SignalFit=0; }
551  mF1_SignalFit = new TF1( (mName+"_MB_"+"mF1_SignalFit").c_str(),MaxwellBoltzmannDist,Start,End,4);
552  //mG_Data->GetXaxis()->SetRangeUser(Start-5,End+5);
553  Double_t height=1; Double_t scale=1;
554  SignalMBPars(height,scale);
555  mF1_SignalFit->SetParameter( 0, height );
556  mF1_SignalFit->SetParameter( 1, mFoundPeak.mStartX );
557  mF1_SignalFit->SetParameter( 2, scale );
558  mF1_SignalFit->SetParameter( 3, base );
559  mF1_SignalFit->SetParName(0,"Amplitude");//Amplitude
560  mF1_SignalFit->SetParName(1,"X-Offset"); //x-offset (peak rise location)
561  mF1_SignalFit->SetParName(2,"Scale"); //scale parameter
562  mF1_SignalFit->SetParName(3,"Y-Offset"); //y-offset
563  mF1_SignalFit->FixParameter(3,base);//Works best when y-offset not allowed to change
564 
565  char opt[10] = "BNRQ";
566  if( GetDebug()>2 ){ opt[3] = '\0';}//Hack to to not quiet fit output
567  Int_t fitstatus = this->GetData()->Fit( mF1_SignalFit, opt );
568  if( fitstatus >= 0 )
569  {
570  mSumAdcFit = mF1_SignalFit->Integral(Start,End);//Sum only in the range and subtract the area under the baseline
571  if( GetDebug()>2 ){LOG_DEBUG << "|FitStart:"<<Start << "|FitEnd:"<<End << "|FitSumB:"<<mSumAdcFit; }
572  mSumAdcFit -= ((End-Start)*base);
573  if( GetDebug()>2 ){LOG_DEBUG << "|FitSumA:"<<mSumAdcFit << "|BaseArea:"<<((End-Start)*base) << endm; }
574  mFitSum = true;
575  return mSumAdcFit;
576  }
577  else{ mFitSum = true; return mSumAdcFit; }//If fit failed return 0 don't do a histogram sum
578 }
579 
580 Double_t StFcsPulseAna::PulseFit(Int_t Start, Int_t End)
581 {
582  if( mFitSum ){ return mSumAdcFit; }
583  mSumAdcFit = 0.0;
584  if( mDbPulse==0 ){LOG_ERROR <<"No pulse object"<<endm; return mSumAdcFit;}
585  Double_t base = Baseline();//Necessary to check if pedestal value has been set
586  if( base < -4.0 ){ return mSumAdcFit; }
587  if( mG_Data==0 || mG_Data->GetN()==0 ){return mSumAdcFit;}
588  if( Start==0 && End==0 ){Start=PeakStart(); End=PeakEnd();}//Default case
589  //Upon failure to find a window 'Start' and 'End' value will be equal. In this case just set some default window.
590  //Sanity Checks
591  if( !GoodWindow() ){ return mSumAdcFit; }
592  if( mF1_SignalFit!=0 ){ delete mF1_SignalFit; mF1_SignalFit=0; }
593  //Only care about one found peak for now in future can change to include more peaks ??
594  int npeak = 1;
595  double para[5] = {static_cast<double>(npeak),base,PeakY(),PeakX(),mDbPulse->GSigma()};
596  mF1_SignalFit = new TF1( (mName+"_Pulse_"+"mF1_SignalFit").c_str(), mDbPulse,&StFcsDbPulse::multiPulseShape,Start,End,2+npeak*3);
597  mF1_SignalFit->SetParameters(para);
598  mF1_SignalFit->FixParameter(0,npeak);
599  mF1_SignalFit->FixParameter(1,base);
600  for(int i=0; i<npeak; i++){
601  mF1_SignalFit->SetParLimits(1+i*3+1,0.0,40000.0); //limit peak not to go negative
602  int j=1+i*3+2;
603  mF1_SignalFit->SetParLimits(j,para[j]-2.0,para[j]+2.0); //limit peak position to +- 2TB
604  mF1_SignalFit->SetParLimits(1+i*3+3,0.01,10.0); //limit sigma to go too narrow or wide (Note(May 5, 2021):orig 0.5 to 10.0)
605  }
606 
607  char opt[10] = "BNRQ";
608  if( GetDebug()>2 ){ opt[3] = '\0';}//Hack to to not quiet fit output
609  Int_t fitstatus = this->GetData()->Fit( mF1_SignalFit, opt );
610  //Int_t fitstatus = 0;//For testing
611  if( fitstatus >= 0 )
612  {
613  mSumAdcFit = mF1_SignalFit->Integral(Start,End);//Sum only in the range and subtract the area under the baseline
614  if( GetDebug()>2 ){LOG_DEBUG << "|FitStart:"<<Start << "|FitEnd:"<<End << "|FitSumB:"<<mSumAdcFit; }
615  mSumAdcFit -= ((End-Start)*base);
616  if( GetDebug()>2 ){LOG_DEBUG << "|FitSumA:"<<mSumAdcFit << "|BaseArea:"<<((End-Start)*base) << endm; }
617  mFitSum = true;
618  return mSumAdcFit;
619  }
620  else{ mFitSum = true; return mSumAdcFit; }//If fit failed return 0 don't do a histogram sum
621 }
622 
623 void StFcsPulseAna::SetFitPars(TF1*& func, int start, int end)
624 {
625  if( func!=0 ){
626  func->FixParameter(0,0);
627  func->FixParameter(1,0);
628  }
629  if( mDbPulse==0 ){ return; }
630  if( FoundPeakIndex()<0 ){ this->AnalyzeForPeak(); }
631  int npeaks = NPeaks();
632  if( start>=npeaks ){ return; }
633  if( end>=npeaks ){ return; }
634  if( start<0 ){ start = 0; }
635  if( end<0 ){ end = npeaks-1; }
636  if( start>end ){ return; }
637 
638  npeaks = end-start+1; //Number of peaks to be used in function is end-start+1 since including end
639  if( func==0 ){
640  func = mDbPulse->createPulse(mXRangeMin,mXRangeMax,2+npeaks*3);
641  }
642  if( npeaks>33 ){return;}//Since parameter array has 101 this is (101-2)/3 max peaks
643  double para[101] = {0};
644  para[0] = npeaks;
645  para[1] = Baseline();
646  func->SetParName(0,"NPeaks");
647  func->SetParName(1,"Ped");
648  for( int i=0; i<npeaks; ++i ){
649  int j = 1+i*3+1;
650  char name[40];
651  sprintf(name,"P%d_A",start+i);
652  func->SetParName(j,name);
653  para[j++] = GetPeak(start+i).mPeakY;
654  sprintf(name,"P%d_M",start+i);
655  func->SetParName(j,name);
656  para[j++] = GetPeak(start+i).mPeakX;
657  sprintf(name,"P%d_S",start+i);
658  func->SetParName(j,name);
659  para[j] = mDbPulse->GSigma();
660  }
661  func->SetParameters(para);
662 
663  func->FixParameter(0,npeaks);
664  func->FixParameter(1,Baseline());
665  for(int i=0; i<npeaks; i++){
666  int j=1+i*3+1;
667  func->SetParLimits(j++,0.0,50000.0); //limit peak not to go negative
668  func->SetParLimits(j,para[j]-2.0,para[j]+2.0); //limit peak position to +- 2TB
669  func->SetParLimits(++j,0.5,10.0); //limit sigma to go too narrow or wide
670  }
671 }
672 
673 StFcsPulseAna* StFcsPulseAna::DrawCopy(Option_t* opt, const char* name_postfix, TGraph* graph) const
674 {
675  StFcsPulseAna* ana = new StFcsPulseAna(*this,name_postfix,graph);//Don't want to call "Clone" since that will clone the graph which you may not want to do in some cases. If graph==0 then it will be cloned
676  ana->SetBit(kCanDelete);
677  ana->AnalyzeForPeak();//Since vector of peaks is not copied re-run analysis; this is in case a filter was used which may replace the graph or a different graph is used
678  ana->AppendPad(opt);//Only append to pad don't draw yet
679 
680  return ana;
681 }
682 
683 void StFcsPulseAna::Print(Option_t* opt) const
684 {
685  TString option(opt);
686  option.ToLower();
687  std::cout << "|Name:"<<mName;
688  if( option.Contains("ana") ){ PeakAna::Print(); }
689  if( option.Contains("debug") ){
690  std::cout << "|P::Graph:"<<this->GetData();
691  std::cout << "|P::SignalFit:"<<mF1_SignalFit;
692  std::cout << "|P::BaselineHist:"<<mH1_Baseline;
693  std::cout << "|P::BaselineFit:"<<mF1_BaselineFit;
694  std::cout << "|P::DbPulse:"<<mDbPulse;
695  }
696  std::cout << "|Flag::WindowSum:"<<mWindowSum;
697  std::cout << "|Flag::FitSum:"<<mFitSum;
698  std::cout << "|Base:"<<mBaseline;
699  std::cout << "|BaselineSigma:"<<mBaselineSigma;
700  std::cout << "|SearchWindow::"; mSearch.Print();
701  std::cout << "|FoundWindow::"; mFoundPeak.Print();
702  std::cout << "|AdcSum:"<<mSumAdc;
703  std::cout << "|FitSum:"<<mSumAdcFit;
704  std::cout << std::endl;
705 }
706 
virtual Int_t SearchForPeak(const std::vector< PeakWindow > &PossiblePeaks)
searches a vector of PeakWindow&#39;s for a specific peak based on input search criteria ...
Definition: PeakAna.cxx:741
Int_t mComputedIndex
Index in mPeaks where a peak was found.
Definition: PeakAna.h:436
virtual TGraphAsymmErrors * GetData() const
Overwrite from PeakAna to type convert for StFcsWaveformFitMaker.
Definition: StFcsPulseAna.h:57
void SignalMBPars(double &height, double &scale)
Figure out the height and scale of a Maxwell-Boltzmann distribution that approximates the signal...
StFcsDbPulse * mDbPulse
Pointer to StFcsDbPulse.
UInt_t GetDebug() const
Definition: PeakAna.h:227
virtual TObject * Clone(const char *newname) const
Clones internal graph as opposed to just copying the pointer.
static int SumDep0Mod(TGraphAsymmErrors *gdata, int Start, int ped=0)
Test my modified sum method to DEP board.
TF1 * createPulse(double xlow=0, double xhigh=1, int npars=5)
Function to create pulse shape for FCS, 5 parameters is minimum.
Double_t MBFit(Int_t Start=0, Int_t End=0)
Fit a Maxwell-Boltzmann distribution to mFoundPeak and return the integral minus the baseline...
Double_t mBaselineSigma
Error on PeakAna::mBaseline.
Definition: PeakAna.h:463
const PeakWindow & GetPeak(UInt_t peakidx) const
Definition: PeakAna.h:228
Int_t FoundPeakIndex() const
Definition: PeakAna.h:233
Double_t PeakY()
y-value of found signal peak
Definition: PeakAna.cxx:245
void SetWindow(Double_t s, Double_t e)
Definition: PeakWindow.cxx:70
virtual void SetData(TGraph *graph)
sets new data for PeakAna
Definition: PeakAna.cxx:302
virtual void MergeByProbability(std::vector< PeakWindow > &merged) const
Overwritten from PeakAna::MergeByProbability() to change merge criteria.
void ResetSum()
Only resets variables related to finding the sum.
std::string & Name()
Definition: StFcsPulseAna.h:60
TGraph * mG_Data
TGraph that stores the x,y data.
Definition: PeakAna.h:485
Double_t PeakX()
x-value of found signal peak
Definition: PeakAna.cxx:238
TF1 * mF1_BaselineFit
Gaussian function to fit to mH1_Baseline to determine baseline.
Double_t mXRangeMax
Absolute possible x-value maximum of data.
Definition: PeakAna.h:489
Double_t PulseFit(Int_t Start=0, Int_t End=0)
Fit the pulse shape defined in StFcsDbPulse::multiPulseShape() to all peaks and return the integral o...
Double_t PeakEnd()
Found Signal ending x-value.
Definition: PeakAna.h:250
Double_t GausFit(Int_t Start=0, Int_t End=0)
Do a Gaussian fit on mFoundPeak and return the integral subtracted by the baseline.
PeakWindow mFoundPeak
Copy of found peak in mPeaks.
Definition: PeakAna.h:444
virtual void Copy(TObject &obj) const
Internal method use Clone instead.
Definition: PeakAna.cxx:97
void AnalyzeForPedestal()
Analyze graph data to determine baseline internally.
static void GetMBPars(const double &xpeak, const double &xrise, const double &yh, const double &ped, double &height, double &scale)
Get parameters for a Maxwell Boltzmann distribution from above based on the 4 const parameters...
Double_t mPeakX
x-value of peak position as determined by SetPeak()
Definition: PeakWindow.h:76
Double_t mTunnelSigma
Sigma for Gaussian for determining tunneling probability (default is 1) see #PeakWindow::PeakTunelPro...
Definition: PeakAna.h:494
static void FillAdc(TGraphAsymmErrors *g, unsigned short &counter, int Start, unsigned short *adcdata)
Needed for SumDep0, et al. It basically fills in tb vs. adc sequentially so all timebins from a given...
virtual void Copy(TObject &obj) const
Internal method use Clone instead.
virtual Int_t AnalyzeForPeak()
Main analysis method for finding peaks.
Definition: PeakAna.cxx:153
Double_t MaxX() const
Definition: PeakAna.h:183
virtual void Print(Option_t *opt="") const
Prints information about PeakWindow.
Definition: PeakWindow.cxx:96
virtual StFcsPulseAna * DrawCopy(Option_t *opt="", const char *name_postfix="_copy", TGraph *graph=0) const
Clone and draw, see PeakAna::Draw() for options.
Double_t PeakStart()
Found Signal starting x-value.
Definition: PeakAna.h:249
void ResetFinder()
Resets all histograms and values.
TF1 * SignalFit() const
Definition: StFcsPulseAna.h:92
Int_t SumWindow()
Sum the ADC in the peak defined by mFoundPeak and subtract the baseline.
void Init()
Initialize everything to zero except signal and background histograms.
virtual void Print(Option_t *opt="") const
Print peak information.
Definition: PeakAna.cxx:259
void SetFitPars(TF1 *&func, int start=-1, int end=-1)
Set the parameters of an external TF1 function that has the form of StFcsDbPulse::multiPulseShape(), optionally only set fit paramaters for peaks from index start up to and including end.
virtual ~StFcsPulseAna()
Destructor.
bool FindBaseline()
Does Gaussian fitting to mH1_Baseline to determine baseline.
Double_t MaxY() const
Definition: PeakAna.h:184
TH1F * BaselineHist() const
Definition: StFcsPulseAna.h:90
virtual void Print(Option_t *opt="") const
Print class variables.
double multiPulseShape(double *x, double *p)
Multi-pulse shape function constant+gaus+xexp+xexp for many pulses.
static int SumDep0(TGraphAsymmErrors *gdata, int Start, int ped=0)
Test of sum method in DEP board.
std::vector< PeakWindow > mPeaks
vector that stores all the found peaks in the data
Definition: PeakAna.h:438
Double_t mStartX
x value for start of the peak window
Definition: PeakWindow.h:71
StFcsPulseAna()
Default constructor.
bool PeakTunnel(const PeakWindow &window) const
test whether a given peak satisifies peak tunnel parameters
Definition: PeakAna.cxx:619
TF1 * mF1_SignalFit
Function to fit to the data.
virtual Int_t AnalyzeForPeak()
Overwritten from PeakAna to process peak tunneling after finding all peaks.
Double_t Baseline() const
Definition: PeakAna.h:178
Double_t mTunnelScale
Scale on exponential for determining tunneling probability (default is 1) see #PeakWindow::PeakTunelP...
Definition: PeakAna.h:493
int NPeaks() const
Definition: PeakAna.h:246
void ResetBaseline()
Resets baseline values.
StFcsPulseAna & operator=(const StFcsPulseAna &rhs)
Assignment operator.
TF1 * BaselineFit() const
Definition: StFcsPulseAna.h:93
Double_t mBaseline
Minimum threshold to search for a peak.
Definition: PeakAna.h:456
Double_t mTunnelThreshold
Cutoff probability for peak tunneling method. If threshold less than 0 (default) then skip peak tunne...
Definition: PeakAna.h:495
double GSigma() const
Definition: StFcsDbPulse.h:66
PeakAna & operator=(const PeakAna &rhs)
Assignment operator doesn&#39;t clone graph.
Definition: PeakAna.cxx:83
double SumMB()
Integral of an approximated Maxwell-Boltzmann distribution minus the baseline.
Double_t mPeakY
y-value at mP_Peak
Definition: PeakWindow.h:77
TH1F * mH1_Baseline
Histogram that holds projection of mG_Data for baseline determination.
Int_t Sum(Int_t Start, Int_t End)
Add raw ADC within a given range and subtract the baseline.
virtual Double_t PeakTunnelProb(TGraph *graph, Double_t scale=1.0, Double_t sigma=1.0) const
Compute probablity that a given PeakWindow is a real peak.
Definition: PeakWindow.cxx:247
Double_t mXRangeMin
Absolute possible x-value minimum of data.
Definition: PeakAna.h:488
PeakWindow mSearch
Variable that defines peak search parameters.
Definition: PeakAna.h:450
bool GoodWindow()
Check if found peak is inside mXRangeMin, mYRangeMin, mXRangeMax, mYRangeMax and has logical values...
Definition: PeakAna.cxx:210
static double MaxwellBoltzmannDist(double *x, double *p)
Maxwell Boltzmann Distribution function.