StRoot  1
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Groups Pages
PeakAna.cxx
1 #include "PeakAna.h"
2 #include "PeakAnaPainter.h"
3 
4 ClassImp(PeakAna)
5 
7 {
8  mG_Data = 0;
9  Init();
10 }
11 
13 {
14  mG_Data = data;//Set graph object now so doesn't get created in 'Init'
15  Init();//This sets internal signal to true so set it false after call
16  mInternalSignal=false;
17 }
18 
19 PeakAna::PeakAna( int size, double* xvals, double* yvals )
20 {
21  mG_Data = new TGraph(size,xvals,yvals);
22  Init();
23 }
24 
25 PeakAna::PeakAna( TH1* hist )
26 {
28  Init();
29 }
30 
32 {
33  mDEBUG = 0;
35 
36  mComputedIndex = -1;//This will keep from having to determine peaks every time
37  mInternalSignal = true;
38  mBaseline = -5.0;
39  mBaselineSigma = 0.75;
40  mBaselineSigmaScale = 4.0;
41 
42  mMaxX = -5.0;
43  mMaxY = -5.0;
44 
45  if( mG_Data==0 ){ mG_Data = new TGraph(); }
46 
47  mXRangeMin = 0;
48  mXRangeMax = 1;
49  mYRangeMin = 0;
50  mYRangeMax = 1;
52 
53  mTunnelScale = 1;//1 is default
54  mTunnelSigma = 1;//1 is default
55  mTunnelThreshold = -1;//Turn off by default
56 
57  mChiralityPeakScale = 1;//default is scale by 1
58  mChiralityScale = 1;//default is peaks must be centered in window
59  mChiralityProbScale = 1;
60  mChiralityThreshold = -1;//Turn off by default
61 
62  mDeltaX = -1.0;//Turn off by default
63  mFilter = 0;
64  mFilterScale = 1;
65  mFilterWeights = 0;
66 
67  mPainter = 0;
68 }
69 
70 PeakAna::PeakAna(const PeakAna &OldAna,TGraph* graph)
71 {
72  //Note that the graph is not cloned because PeakAna should not be changing the contents of the data
73  if( graph!=0 ){ mG_Data = graph; }//Set graph object now so doesn't get created in 'Init'
74  else{ mG_Data = OldAna.GetData(); }//If no graph object given then clone the graph
75  if( mG_Data!=0 ){ mG_Data = (TGraph*)mG_Data->Clone(); }//In case 'OldAna' graph is zero then don't call clone, since if it is zero Init() will create a new graph object
76  Init();
77  if( graph!=0 ){mInternalSignal=false;}//Init() sets internal signal to true so set it false after call if external graph object given
78 
79  ((PeakAna&)OldAna).Copy(*this);
80 
81 }
82 
84 {
85  if( this == &rhs ){ return *this; }
86  //Note that the graph is not cloned because PeakAna should not be changing the contents of the data
87  mG_Data = rhs.GetData();//Set graph object now so doesn't get created in 'Init'
88  Init();//This sets internal signal to true so set it false after call
89  mInternalSignal=false;//Since graph is not copied don't treat as an internal signal
90 
91  ((PeakAna&)rhs).Copy(*this);
92 
93  return *this;
94 }
95 
96 //Note that this function doesn't copy the graph object or a copies the vector of peaks. This is done on purpose becuase the idea is that the copy should run its own analysis and the algorithm is robust enough to generate the same results. To copy all objects use TObject::Clone
97 void PeakAna::Copy(TObject& obj) const
98 {
99  TObject::Copy(obj);//Copy TObject bits
100 
101  ((PeakAna&)obj).mMaxX = mMaxX;
102  ((PeakAna&)obj).mMaxY = mMaxY;
103 
104  ((PeakAna&)obj).mXRangeMin = mXRangeMin;
105  ((PeakAna&)obj).mYRangeMin = mYRangeMin;
106  ((PeakAna&)obj).mXRangeMax = mXRangeMax;
107  ((PeakAna&)obj).mYRangeMax = mYRangeMax;
108  ((PeakAna&)obj).mBaseline = mBaseline;
109  ((PeakAna&)obj).mBaselineSigma = mBaselineSigma;
110  ((PeakAna&)obj).mBaselineSigmaScale = mBaselineSigmaScale;
111  ((PeakAna&)obj).mSearch.SetWindow(SearchPeak(),SearchWidth());
112  ((PeakAna&)obj).mDeltaX = mDeltaX;
113  ((PeakAna&)obj).mFilter = mFilter;
114  ((PeakAna&)obj).mFilterScale = mFilterScale;
115  //((PeakAna&)obj).mFilterSigma = mFilterSigma;
116  if( ((PeakAna&)obj).mFilterWeights!=0 ){ delete [] ((PeakAna&)obj).mFilterWeights; ((PeakAna&)obj).mFilterWeights = 0; }
117  if( mFilterWeights!=0 && mFilterScale>0 ){
118  ((PeakAna&)obj).mFilterWeights = new double[2*mFilterScale+1];
120  }
121  else{ ((PeakAna&)obj).mFilterWeights = 0; }
122  ((PeakAna&)obj).mTunnelScale = mTunnelScale;
123  ((PeakAna&)obj).mTunnelSigma = mTunnelSigma;
124  ((PeakAna&)obj).mTunnelThreshold = mTunnelThreshold;
125 
126  ((PeakAna&)obj).mPainter = 0;//Dont' copy painter
127 }
128 
129 TObject* PeakAna::Clone(const char* newname) const
130 {
131  TGraph* cloneg = (TGraph*)GetData()->Clone(newname);
132  PeakAna* ana = new PeakAna(cloneg);
133  Copy(*ana);
134  ana->ForceInternal();//Cloned graphs should always be internal
135  if( FoundPeakIndex()>=0 ){
136  //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
138  for( UInt_t i=0; i<mPeaks.size(); ++i ){
139  ana->mPeaks.push_back( mPeaks.at(i) );
140  }
141  ana->mFoundPeak = mFoundPeak;
142  }
143  return ana;
144 }
145 
147 {
148  if(mInternalSignal ){delete mG_Data;}
149  delete [] mFilterWeights;
150  delete mPainter;
151 }
152 
154 {
155  ResetPeak();
156  if( mFilter == 1 ){ MeanFilter(mFilterScale,false); }
157  if( mFilter == 2 ){ GausFilter(mFilterScale,false); }
158  this->GetPossiblePeaks();
159  //Doesn't make sense to merge for number of peaks <= 1
160  if( mChiralityThreshold>=0 && mPeaks.size()>1 ){
161  std::vector<PeakWindow> mergedpeaks;
162  this->MergeByChirality(mergedpeaks);
163  mPeaks.swap( mergedpeaks );
164  };
165  mComputedIndex = this->SearchForPeak( mPeaks );//Returns a valid index for the possible peak vector or the size of the vector for an invalid peak
166  if( mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ mFoundPeak = mPeaks.at(mComputedIndex); }
167  else{
168  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
169  }
170  //@[May 10, 2022]>mFoundPeak is a copy of the one in the vector. When drawing the windows the one in the vector is accessed not 'mFoundPeak' which is why the color for the one in the vector is changed and not mFoundPeak.
171  return mComputedIndex;
172 }
173 
174 Int_t PeakAna::AnalyzeForPeak(Double_t peak, Double_t width)
175 {
176  SetSearchWindow(peak,width);
177  return this->AnalyzeForPeak();
178 }
179 
180 Int_t PeakAna::AnalyzeForNoisyPeak()
181 {
182  ResetPeak();
183  this->GetPossiblePeaks();
184  PeakAna noisyana = PeakAna::ConvertPeaksToAna(*this);
185  noisyana.SetTunnelThreshold(-1);//Don't use peak tunneling for this kind of peak finding
186  noisyana.AnalyzeForPeak();
187  //Replace old peak values with new one (maybe use swap in future??)
188  mPeaks.clear();
189  for( Int_t inoisy=0; inoisy<noisyana.NPeaks(); ++inoisy )
190  {
191  mPeaks.push_back( noisyana.GetPeak(inoisy) );
192  }
193  mComputedIndex = this->SearchForPeak( mPeaks );//Returns a valid index for the possible peak vector or the size of the vector for an invalid peak
194  if( mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){
195  mFoundPeak = mPeaks.at(mComputedIndex); //No offset for now rethink in the future??
196  //@[May 10, 2022]>mFoundPeak is a copy of the one in the vector. When drawing the windows the one in the vector is accessed not 'mFoundPeak' which is why the color for the one in the vector is changed and not mFoundPeak.
197  //mPeaks.at(mComputedIndex).SetStartLineColor(kRed);
198  //mPeaks.at(mComputedIndex).SetEndLineColor(kOrange);
199  }
200  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
201  return mComputedIndex;
202 }
203 
205 {
206  if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){return true;}
207  else{return false;}
208 }
209 
211 {
212  if( mComputedIndex<0 ){this->AnalyzeForPeak();}
213  //First check if peak values are within the specified range
214  if( mFoundPeak.mStartX<mXRangeMin || mFoundPeak.mStartX>mXRangeMax ){return false;}
215  else if( mFoundPeak.mEndX<mXRangeMin || mFoundPeak.mEndX > mXRangeMax ){return false;}
216  //Next check if values make physical sense
217  else if( mFoundPeak.mStartX==mFoundPeak.mEndX ){return false;}
218  else if( mFoundPeak.mStartX > mFoundPeak.mEndX ){return false;}
219  else{return true;}
220 }
221 
222 void PeakAna::GetXYMax(Double_t xmin, Double_t xmax)
223 {
224  if( mG_Data==0 ){ return; }
225  for( int i=0; i<mG_Data->GetN(); ++i ){
226  Double_t X; Double_t Y;
227  mG_Data->GetPoint(i,X,Y);
228  if( X<xmin || X>xmax ){continue;}
229  if( Y>mMaxY ){ mMaxY=Y; mMaxX=X; }
230  }
231  if( mMaxY<0 && mMaxX<0 ){
232  LOG_WARN << "Unable to find a maximum adc\nSetting to impossible values" << endm;
233  mMaxY = mYRangeMax;
234  mMaxX = mXRangeMax;
235  }
236 }
237 
238 Double_t PeakAna::PeakX()
239 {
240  if( mComputedIndex<0 ){ this->AnalyzeForPeak(); }
241  if( mFoundPeak.mP_Peak<0){ return 0; }
242  else{ Double_t x,y; mG_Data->GetPoint(mFoundPeak.mP_Peak,x,y); return x;}
243 }
244 
245 Double_t PeakAna::PeakY()
246 {
247  if( mComputedIndex<0 ){this->AnalyzeForPeak();}
248  if( mFoundPeak.mP_Peak<0){ return 0; }
249  else{ Double_t x,y; mG_Data->GetPoint(mFoundPeak.mP_Peak,x,y); return y;}
250 }
251 
252 void PeakAna::PeakXY(Double_t &xval, Double_t &yval)
253 {
254  if( mComputedIndex<0 ){this->AnalyzeForPeak();}
255  if( mFoundPeak.mP_Peak<0 ){ xval=0; yval=0; }
256  else{ mG_Data->GetPoint(mFoundPeak.mP_Peak,xval,yval); }
257 }
258 
259 void PeakAna::Print(Option_t* opt) const
260 {
261  std::cout << "|P::Graph:"<<mG_Data;
262  std::cout << "|RangeX:["<<mXRangeMin<<","<<mXRangeMax<<"]";
263  std::cout << "|RangeY:["<<mYRangeMin<<","<<mYRangeMax<<"]";
264  std::cout << "|Flag::Internal:"<<mInternalSignal;
265  std::cout << "|Base:"<<mBaseline;
266  std::cout << "|BaselineSigma:"<<mBaselineSigma;
267  std::cout << "|TunnelA:"<<mTunnelScale << "|S:" << mTunnelSigma << "|T:" << mTunnelThreshold;
268  std::cout << std::endl;
269  std::cout << "|SearchWindow::"; mSearch.Print();
270  std::cout << std::endl;
271  std::cout << "|FoundWindow::"; mFoundPeak.Print();
272  std::cout << std::endl;
273  std::cout << "|MaxX:"<<mMaxX;
274  std::cout << "|MaxY:"<<mMaxY;
275  std::cout << std::endl;
276  std::cout << "Weights:" << mFilterWeights << "|";
277  if( mFilterWeights!=0 ){ for( int i=0; i<mFilterScale*2+1; ++i ){ std::cout << mFilterWeights[i] << ","; } }
278  std::cout << std::endl;
279  std::cout << " - Peaks:" << std::endl;
280  for( UInt_t i=0; i<mPeaks.size(); ++i ){
281  std::cout << " + |i:"<<i;
282  mPeaks.at(i).Print();
283  std::cout << std::endl
284  << " + |Prob:"<< mPeaks.at(i).PeakTunnelProb(mG_Data,mTunnelScale,mTunnelSigma)
285  //<< "|Chir:"<< mPeaks.at(i).PeakChirality(mChiralityPeakScale, mChiralityScale)
286  //<< "|ChirProb:"<< mPeaks.at(i).PeakChiralityProb(mChiralityProbScale,mChiralityPeakScale,mChiralityScale)
287  //<< "|ChirVal:"<< this->MergeLeftOrRight(i)
288  << std::endl;
289  }
290 }
291 
293 {
294  //If computed index is negative then analyzeforpeak was never called and it is not needed to reset anything
295  if( mComputedIndex>=0 ){
296  mComputedIndex = -1;
297  mPeaks.clear();
299  }
300 }
301 
302 void PeakAna::SetData(TGraph* graph)
303 {
304  if( graph==0 ){LOG_ERROR << "PeaAna::SetData - Graph cannot be 0!" << endm; return;}
305  ResetPeak();
306  if( mInternalSignal && mG_Data!=0 ){delete mG_Data;}
307  mG_Data = graph;
308  mInternalSignal = false;
309 }
310 
311 void PeakAna::SetData(TH1* hist, UInt_t numavgs)
312 {
313  if( hist==0 ){LOG_ERROR << "PeakAna::SetData - Histogram cannot be 0" << endm; return;}
314  ResetPeak();
315  if( mInternalSignal ){delete mG_Data;}
316  mG_Data = PeakAna::ConvertHistToGraph(hist,numavgs);
317  mInternalSignal = true;
318 }
319 
320 void PeakAna::SetFilter( UInt_t filter, Int_t scale, Double_t sigma )
321 {
322  mFilter = filter;
323  mFilterScale=scale;
324  if( scale<=0 ){ return; }
325  if( sigma==0 ){ sigma=scale/2.0; }//if sigma is zero use scale/2
326  if( mFilterWeights!=0 ){ delete [] mFilterWeights; mFilterWeights=0; }
327  if( filter==2 ){ mFilterWeights = GaussianMatrix2D(scale,sigma); }
328 }
329 
330 void PeakAna::SetBaseline(Double_t value, Double_t sigma)
331 {
332  if( sigma<0){ LOG_WARN << "PeakAna::SetBaseline - Baseline sigma should not be less than zero\n" << endm; }
333  mBaseline = value;
334  mBaselineSigma = sigma;
335 }
336 
337 void PeakAna::SetBaselineSigmaScale(Double_t scale)
338 {
339  mBaselineSigmaScale = fabs(scale);
340 }
341 
342 void PeakAna::SetRange( Double_t xmin, Double_t ymin, Double_t xmax, Double_t ymax)
343 {
344  mXRangeMin = xmin;
345  mYRangeMin = ymin;
346  mXRangeMax = xmax;
347  mYRangeMax = ymax;
348 }
349 void PeakAna::SetSearchWindow(Double_t peak, Double_t width)
350 {
351  mSearch.SetWindow(peak,width);
352  ResetPeak();//reset since old found peak should be invalid
353 }
354 
355 void PeakAna::SetPeak(const Int_t peakpoint, const Double_t peakx )
356 { mFoundPeak.mP_Peak=peakpoint; mFoundPeak.mPeakX=peakx; }
357 void PeakAna::SetWindow(const Int_t start, const Int_t end )
358 { mFoundPeak.SetWindow(start,end); }
359 
361 { mFoundPeak=window; }
362 
363 void PeakAna::SetTunnelScale(Double_t value)
364 {
365  if( value>=0 ){mTunnelScale = value;}
366 }
367 void PeakAna::SetTunnelSigma(Double_t value)
368 {
369  if( value>0 ){mTunnelSigma = value;}
370 }
371 void PeakAna::SetTunnelPars(Double_t scale, Double_t sigma)
372 {
373  SetTunnelScale(scale);
374  SetTunnelSigma(sigma);
375 }
376 void PeakAna::SetTunnelThreshold(Double_t value)
377 {
378  if( value<=1 ){ mTunnelThreshold=value; }
379 }
380 
381 
382 TGraph* PeakAna::ConvertHistToGraph(TH1* hist, UInt_t numavgs)
383 {
384  Int_t nbins = hist->GetNbinsX();
385  //Only consider averaging when number of bins to average are greater than or equal to 1 or less than the number of bins
386  if( numavgs<1 || static_cast<Int_t>(numavgs)>nbins ){return 0;}//strictly greater than since GetNbinsX()+1 is overflow
387  TGraph* AvgGr = new TGraph();
388  for( Int_t ibin=1; ibin<=nbins; ibin+=numavgs ){
389  Double_t sum=0;
390  UInt_t counter=0;//In case number of averages is not same as how many sums were performed
391  for(Int_t i=ibin; i<ibin+static_cast<Int_t>(numavgs) && i<=nbins; ++i ){ sum += hist->GetBinContent(i); ++counter; }
392  Double_t avg = sum/static_cast<Double_t>(counter);
393  //Set graph's point to center of x range
394  Double_t xlow = hist->GetBinLowEdge(ibin);
395  Double_t xhigh = hist->GetBinLowEdge(ibin+counter);
396  AvgGr->SetPoint(AvgGr->GetN(),(xlow+xhigh)/2.0,avg);//This is for knowing the x-value
397  }
398  return AvgGr;
399 }
400 
401 TGraph* PeakAna::ConvertPeaksToGraph(const std::vector<PeakWindow> &Peaks)
402 {
403  if( Peaks.size()==0 ){return 0;}
404  TGraph* graph = new TGraph();
405  for( UInt_t ipeak=0; ipeak<Peaks.size(); ++ipeak )
406  {
407  graph->SetPoint( ipeak,Peaks.at(ipeak).mPeakX,Peaks.at(ipeak).MidPoint() );
408  }
409  return graph;
410 }
411 
413 {
414  TGraph* graph = new TGraph();
415  for( UInt_t ipeak=0; ipeak<mPeaks.size(); ++ipeak )
416  {
417  graph->SetPoint( ipeak,mPeaks.at(ipeak).mPeakX,mPeaks.at(ipeak).MidPoint() );
418  }
419  SetData(graph);
420  ForceInternal();//new graph replaces old one so needs to be deleted
421  return;
422 }
423 
425 {
426  TGraph* graph = new TGraph();
427  for( Int_t ipeak=0; ipeak<Ana.NPeaks(); ++ipeak ){
428  graph->SetPoint(ipeak, Ana.GetPeak(ipeak).mPeakX, Ana.GetPeak(ipeak).MidPoint() );
429  }
430  PeakAna NewAna(Ana,graph);
431  NewAna.ForceInternal();//new graph so needs to be deleted
432  return NewAna;
433 }
434 
435 PeakAna* PeakAna::MeanFilter( Int_t sizeavgs, bool copy )
436 {
437  if( GetData()==0 ){return this;}
438  const int npoints = GetData()->GetN();
439  if( sizeavgs==0 || npoints<=1 ){ return this; }
440  if( sizeavgs<0 ){ sizeavgs = -sizeavgs; }
441  double ynew[npoints];
442  double* xdata = GetData()->GetX();
443  double* ydata = GetData()->GetY();
444  for( int ipoint=0; ipoint<npoints; ++ipoint ){
445  double sumweights = 1;
446  if( mFilterWeights!=0 ){ sumweights = mFilterWeights[sizeavgs]; }
447  double sumvalues = sumweights*ydata[ipoint];
448  int nextpoint_m = ipoint;
449  int nextpoint_p = ipoint;
450  double lastx_m = xdata[ipoint];
451  double lastx_p = xdata[ipoint];
452  for( int sizecounter=0; sizecounter<sizeavgs; ++sizecounter ){
453  //@[July 6, 2022]>Also need to check minimum and maximum x-values??
454  Double_t delxerr = 0.001*mDeltaX;//Since comparing doubles add 0.1% tolerance to mDeltaX
455  if( (nextpoint_m-1)>=0 ){
456  if( mDeltaX<=0 || ((mDeltaX-delxerr)<=fabs(xdata[nextpoint_m-1]-lastx_m) && fabs(xdata[nextpoint_m-1]-lastx_m)<=(mDeltaX+delxerr)) ){
457  //Next point is mDeltaX away so valid point
458  nextpoint_m -= 1;
459  lastx_m = xdata[nextpoint_m];
460  if( mFilterWeights!=0 ){ sumvalues += mFilterWeights[sizeavgs-sizecounter-1]*ydata[nextpoint_m]; }
461  else{ sumvalues += ydata[nextpoint_m]; }
462  }
463  else{
464  //Next point is not mDeltaX away so invalid point and add baseline and decrement lastx value
465  lastx_m -= mDeltaX;
466  if( mFilterWeights!=0 ){ sumvalues += mFilterWeights[sizeavgs-sizecounter-1]*Baseline(); }
467  else{ sumvalues += Baseline(); }
468  }
469  //Some y-value was added so increment number of points/weights
470  if( mFilterWeights!=0 ){ sumweights += mFilterWeights[sizeavgs-sizecounter-1]; }
471  else{ sumweights += 1; }
472  }
473  if( (nextpoint_p+1)<npoints ){
474  if( mDeltaX<=0 || ((mDeltaX-delxerr)<=fabs(xdata[nextpoint_p+1]-lastx_p) && fabs(xdata[nextpoint_p+1]-lastx_p)<=(mDeltaX+delxerr)) ){
475  nextpoint_p += 1;
476  lastx_p = xdata[nextpoint_p];
477  if( mFilterWeights!=0 ){ sumvalues += mFilterWeights[sizeavgs+sizecounter+1]*ydata[nextpoint_p]; }
478  else{ sumvalues += ydata[nextpoint_p]; }
479  }
480  else{
481  lastx_p += mDeltaX;
482  if( mFilterWeights!=0 ){ sumvalues += mFilterWeights[sizeavgs+sizecounter+1]*Baseline(); }
483  else{ sumvalues += Baseline(); }
484  }
485  if( mFilterWeights!=0 ){ sumweights += mFilterWeights[sizeavgs+sizecounter+1]; }
486  else{ sumweights += 1; }
487  }
488  }
489  ynew[ipoint] = sumvalues/sumweights;
490  if( GetDebug()>2 ){ std::cout << " - |i:"<<ipoint<<"|ynew:"<<ynew[ipoint] << std::endl; }
491  }
492 
493  if( copy ){
494  TGraph* filtered = new TGraph(npoints,xdata,ynew);
495  PeakAna* ana = new PeakAna(*this,filtered);
496  ana->ForceInternal();
497  if( GetDebug()>1 ){ std::cout << "PeakAna::MeanFilter:Copied PeakAna" << std::endl; }
498  return ana;
499  }
500  if( mInternalSignal ){
501  //@[July 6, 2022] > (Copy arrays in c++ )[https://stackoverflow.com/questions/16137953/is-there-a-function-to-copy-an-array-in-c-c]
502  std::copy( ynew, ynew+npoints, ydata);
503  //[July 3, 2022]>Taken from TGraph CtorAllocate(). Setting minimum and maximum to -1111 effectivley resets the minimum and maximum.
504  GetData()->SetMinimum(-1111);
505  GetData()->SetMaximum(-1111);
506  if( GetDebug()>1 ){ std::cout << "PeakAna::MeanFilter:Internal Graph Modified" << std::endl; }
507  }
508  else{
509  TGraph* graph = new TGraph(npoints,xdata,ynew );
510  SetData(graph);
511  ForceInternal();
512  if( GetDebug()>1 ){ std::cout << "PeakAna::MeanFilter:Created New Internal Graph" << std::endl; }
513  }
514  ResetPeak();
515  return this;
516 }
517 
518 PeakAna* PeakAna::GausFilter( Int_t sizeavgs, bool copy )
519 {
520  if( GetData()==0 ){return this;}
521  const int npoints = GetData()->GetN();
522  if( sizeavgs==0 || npoints<=1 ){ return this; }
523  if( sizeavgs<0 ){ sizeavgs = -sizeavgs; }
524  double ynew[npoints];
525  double* xdata = GetData()->GetX();
526  double* ydata = GetData()->GetY();
527  for( int ipoint=0; ipoint<npoints; ++ipoint ){
528  double sumweights = 1;
529  if( mFilterWeights!=0 ){ sumweights = mFilterWeights[sizeavgs]; }
530  double sumvalues = sumweights*ydata[ipoint];
531  int nextpoint_m = ipoint;
532  int nextpoint_p = ipoint;
533  double lastx_m = xdata[ipoint];
534  double lastx_p = xdata[ipoint];
535  for( int sizecounter=0; sizecounter<sizeavgs; ++sizecounter ){
536  //Also need to check minimum and maximum x-values??
537  Double_t delxerr = 0.001*mDeltaX;//Since comparing doubles add 0.1% tolerance to mDeltaX
538  if( (nextpoint_m-1)>=0 ){
539  if( mDeltaX<=0 || ((mDeltaX-delxerr)<=fabs(xdata[nextpoint_m-1]-lastx_m) && fabs(xdata[nextpoint_m-1]-lastx_m)<=(mDeltaX+delxerr)) ){
540  //Next point is mDeltaX away so valid point
541  nextpoint_m -= 1;
542  lastx_m = xdata[nextpoint_m];
543  if( mFilterWeights!=0 ){ sumvalues += mFilterWeights[sizeavgs-sizecounter-1]*ydata[nextpoint_m]; }
544  else{ sumvalues+=ydata[nextpoint_m]; }
545  }
546  else{
547  //Next point is not mDeltaX away so invalid point and add baseline and decrement lastx value
548  //I should add baselines even if nextpoint+-1 is outside array (is this padding)?
549  lastx_m -= mDeltaX;
550  if( mFilterWeights!=0 ){ sumvalues += mFilterWeights[sizeavgs-sizecounter-1]*Baseline(); }
551  else{ sumvalues += Baseline(); }
552  }
553  }
554  else{
555  //nextpoint_m is now negative so add padding by copying first point
556  if( mFilterWeights!=0 ){ sumvalues += mFilterWeights[sizeavgs-sizecounter-1]*ydata[0]; }
557  else{ sumvalues += ydata[0]; }
558  }
559  if( (nextpoint_p+1)<npoints ){
560  if( mDeltaX<=0 || ((mDeltaX-delxerr)<=fabs(xdata[nextpoint_p+1]-lastx_p) && fabs(xdata[nextpoint_p+1]-lastx_p)<=(mDeltaX+delxerr)) ){
561  nextpoint_p += 1;
562  lastx_p = xdata[nextpoint_p];
563  if( mFilterWeights!=0 ){ sumvalues += mFilterWeights[sizeavgs+sizecounter+1]*ydata[nextpoint_p]; }
564  else{ sumvalues += ydata[nextpoint_p]; }
565  }
566  else{
567  lastx_p += mDeltaX;
568  if( mFilterWeights!=0 ){ sumvalues += mFilterWeights[sizeavgs+sizecounter+1]*Baseline(); }
569  else{ sumvalues += Baseline(); }
570  }
571  }
572  else{
573  //nextpoint_p is now >=npoints so add padding by copying last point
574  if( mFilterWeights!=0 ){ sumvalues += mFilterWeights[sizeavgs+sizecounter+1]*ydata[npoints-1]; }
575  else{ sumvalues += ydata[npoints-1]; }
576  }
577  //Some y-value was added for either case so increment number of points/weights accordingly
578  if( mFilterWeights!=0 ){
579  sumweights += mFilterWeights[sizeavgs-sizecounter-1];
580  sumweights += mFilterWeights[sizeavgs+sizecounter+1];
581  }
582  else{ sumweights += 2; }
583  }
584  ynew[ipoint] = sumvalues/sumweights;
585  if( GetDebug()>2 ){ std::cout << " - |i:"<<ipoint<<"|ynew:"<<ynew[ipoint] << std::endl; }
586  }
587 
588 
589  if( copy ){
590  TGraph* filtered = new TGraph(npoints,xdata,ynew);
591  PeakAna* ana = new PeakAna(*this,filtered);
592  ana->ForceInternal();
593  if( GetDebug()>1 ){ std::cout << "PeakAna::MeanFilter:Copied PeakAna" << std::endl; }
594  return ana;
595  }
596  if( mInternalSignal ){
597  //@[July 6, 2022] > (Copy arrays in c++ )[https://stackoverflow.com/questions/16137953/is-there-a-function-to-copy-an-array-in-c-c]
598  std::copy( ynew, ynew+npoints, ydata);
599  //[July 3, 2022]>Taken from TGraph CtorAllocate(). Setting minimum and maximum to -1111 effectivley resets the minimum and maximum.
600  GetData()->SetMinimum(-1111);
601  GetData()->SetMaximum(-1111);
602  if( GetDebug()>1 ){ std::cout << "PeakAna::MeanFilter:Internal Graph Modified" << std::endl; }
603  }
604  else{
605  TGraph* graph = new TGraph(npoints,xdata,ynew );
606  SetData(graph);
607  ForceInternal();
608  if( GetDebug()>1 ){ std::cout << "PeakAna::MeanFilter:Created New Internal Graph" << std::endl; }
609  }
610  ResetPeak();
611  return this;
612 }
613 
615 {
616  return PeakAna::ConvertPeaksToAna(*this);
617 }
618 
619 bool PeakAna::PeakTunnel(const PeakWindow &window) const
620 {
621  if( mTunnelThreshold>=0 ){
622  Double_t prob = window.PeakTunnelProb(mG_Data,mTunnelScale,mTunnelSigma);
623  if( GetDebug()>1 ){
624  LOG_DEBUG << "PeakAna::PeakTunnel - |prob:"<<prob << "|thr:"<<mTunnelThreshold;
625  if( GetDebug()>2 ){ LOG_DEBUG << "|scale:"<<mTunnelScale << "|sigma:"<< mTunnelSigma; }
626  LOG_DEBUG << endm;
627  }
628  if( prob>mTunnelThreshold ){return true; }
629  else{ return false; }
630 
631  }
632  return false;
633 }
634 
635 Double_t PeakAna::PeakProb(const PeakWindow &window, Double_t scale, Double_t sigma ) const
636 {
637  return window.PeakTunnelProb(mG_Data,scale,sigma);
638 }
639 
641 {
642  std::vector<PeakWindow> PossiblePeak;//Gather all the possible occurances when signal is larger than Sigma
643  if( GetDebug()>1 ){LOG_DEBUG << "PeakAna - In GetPossiblePeaks" << endm; }
644 
645  Double_t baseline = Baseline();//Already checked baseline above
646  Double_t slopecutoff = BaselineSigma()*BaselineSigmaScale();
647  if( GetDebug() > 2){
648  LOG_DEBUG << "|baseline:"<<baseline << "|slopecutoff:"<<slopecutoff << endm;
649  }
650  PeakWindow peak(mXRangeMin-1,mXRangeMax+1);//peak values must be less than and greater than range for algorithm to work
651  Double_t LocalMax=mXRangeMin-1;//Variable to help keep track of when slope changes (has to be less than minimum)
652  if( GetDebug()>1 ){LOG_DEBUG << "Finding Peak for cutoff " << slopecutoff << endm;}
653  //The idea is that you start with all things negative and loop through all the ADC values. When the slope is greater than the cutoff you save it and then the signal will rise and then fall so slope will eventually go negative and this is when you keep track of the end values. Once signal goes positive again or you no longer pass the slope cutoff stop and save the start and stop values.
654  if( GetDebug()>2 ){LOG_DEBUG << "PeakAna::GetPossiblePeaks:Start graph reading loop" << endm;}
655  Int_t npoints = mG_Data->GetN();
656  for( Int_t ismp=0; ismp<npoints-1; ++ismp ){
657  Double_t LX; Double_t LY;//L for left (actually current point)
658  Double_t RX; Double_t RY;//R for right (actually next point)
659  mG_Data->GetPoint(ismp,LX,LY);
660  mG_Data->GetPoint(ismp+1,RX,RY);
661  if( GetDebug()>1 ){LOG_DEBUG << " - |P:"<<ismp <<"|LX:"<<LX << "|LY:"<<LY<< "|RX:"<<RX << "|RY:"<<RY << endm;}
662 
663  //Reject values outside the range
664  if( LX < mXRangeMin ){if( GetDebug()>2 ){LOG_DEBUG << "'LX' too small"<<endm;} continue; }
665  if( LX > mXRangeMax ){if( GetDebug()>2 ){LOG_DEBUG << "'LX' too large"<<endm;} continue; }
666  if( LY < mYRangeMin || LY > mYRangeMax){if( GetDebug()>2 ){LOG_DEBUG << "'LY' outside y range"<<endm;} continue; }
667  if( RY < mYRangeMin || RY > mYRangeMax){if( GetDebug()>2 ){LOG_DEBUG << "'RY' outside y range"<<endm;} continue; }
668  Double_t Slope = (RY-LY)/(RX-LX);
669  if( mDeltaX>0 ){
670  Double_t delxerr = 0.001*mDeltaX;//Since comparing doubles add 0.1% tolerance to mDeltaX
671  if( !((mDeltaX-delxerr)<=(RX-LX) && (RX-LX)<=(mDeltaX+delxerr)) ){
672  if( GetDebug()>2 ){ LOG_DEBUG << " - |DeltaX:"<<mDeltaX << "|RX-LX:"<<RX-LX << endm;}
673  RX=LX+mDeltaX;
674  RY=mYRangeMin;//Setting RY=mYRangeMin here is a flag that indicates a discontinuity
675  Slope = -1.0;//Force slope to be negative to stop peak search or to prevent start of search
676  }
677  }
678  if( GetDebug()>2 ){LOG_DEBUG << " - |Slope:"<<Slope << endm;}
679  //The purpose of this if statement is to ensure that any cases when there is no start time and no end time and the 'Ladc'==0 then we skip those points. The additional nested if statement is for the case when there is a start and end time (like when the alogithm is working on negative slopes) and the 'Ladc' will still be >0; however since I dynamically change the "end" time until the slope changes to positive this statement ensures that 'continue' only gets called on positive slope results and not negative slopes when it is trying to find the correct end time.
680  if( GetDebug()>2 ){LOG_DEBUG << " - peak|ismp:"<<ismp << "|start:"<<peak.mStartX << "|end:"<<peak.mEndX << endm;}
681  //Check above will skip bad values
682  if( peak.mStartX<mXRangeMin ){//No start time yet
683  if( GetDebug()>1 ){LOG_DEBUG << " + No StartTime" << endm;}
684  if( Slope>0){
685  if( LY>baseline+slopecutoff || RY>baseline+slopecutoff ){
686  //Needs to be checked sequentially since we need to reject any points below the baseline+cutoff that may have a large slope
687  if( GetDebug()>1 ){LOG_DEBUG << " + Passed Slope and baselineCutoff setting as start time" << endm;}
688  peak.mStartX=LX;//Set start x-value
689  peak.mStartY=LY;//Set start y-value
690  LocalMax=LX;//Start checking local maximums
691  }
692  }
693  //If didn't pass thresholds for start time then continue to next point
694  }
695  else{//Found a suitable start time which indicates positve slope above some thershold
696  //The statements below will now check subsequent values and if the slope is >= 0 then we have wrong local max so set local max to next value. Note this means that localmax is not greater than End which is mXRangeMax+1. Once slope goes below 0 End will take that value and local max will be less than End. Once the slope changes again LocalMax will now be greater than End
697  if( GetDebug()>1 ){LOG_DEBUG << " + Found StartTime:"<< peak.mStartX << "|LocalMax:"<<LocalMax<<endm;}
698  if( Slope >= 0 ){
699  LocalMax=LX; //keep moving local max as long slope>=0
700  if( Slope==0 && peak.mP_Peak>=0 ){ peak.mEndX=LX; } //if slope==0 and a peak was found i.e. mP_Peak>=0 then don't end finding but keep moving the end point i.e. peak.mEndX=LX. This condition is needed because sometimes a slope is zero and then will start decreasing again later. The importance of finding a peak is because it establishes the fact that the curve is convave and zero slope is ambiguous in which direction it will go. If slopes keep decreasing then expands peak window, if slope changes sign at next point algorithm will naturally stop. This is the desired behavior. Using the condition `Slope>0` does not produce the same the effect because sometimes this kind of zero slope behavior happens on the increasing slope side of the data and this should not be stopping the search because the next one could be positive.
701  if( GetDebug()>2){LOG_DEBUG<<" + Slope>=0|"<<Slope<<endm;}
702  }
703  else{ //Slope < 0
704  peak.mEndX=LX;
705  if(peak.mP_Peak<0){
706  peak.mP_Peak=ismp;
707  mG_Data->GetPoint(ismp,peak.mPeakX,peak.mPeakY);
708  }
709  if(GetDebug()>2){LOG_DEBUG<<" + Slope<0"<<endm;}
710  }
711  if( LocalMax>peak.mEndX || LY<baseline+slopecutoff || ismp==npoints-2 || RY<=mYRangeMin ){//Slope is now positive again or there was a postive slope to negative but did not fall below threshold
712  if( ismp==npoints-2 ){ peak.mEndX=RX; peak.mEndY=RY; if(peak.mP_Peak<0){peak.mP_Peak=ismp+1;} }//last point is actually RX,RY not LX,LY and if no peak found then set last point as found peak
713  else{peak.mEndX=LX; peak.mEndY = LY;}//all others should be current point
714  if( GetDebug()>2 ){LOG_DEBUG << " + Slope positive again|StartX:"<<peak.mStartX << "|StartY:"<<peak.mStartY << "|EndX:"<<peak.mEndX << "|EndY:"<<peak.mEndY << "|P:"<<peak.mP_Peak <<"|PeakX:"<<peak.mPeakX << "|PeakY:"<<peak.mPeakY << "|PeakProb:"<< peak.PeakTunnelProb(mG_Data,mTunnelScale,mTunnelSigma) << endm;}
715  if( PeakTunnel(peak) ){
716  Int_t n = PossiblePeak.size();
717  if( n==0 ){ PossiblePeak.push_back(peak); }//In case vector is empty and tunnel probablity is true
718  else if( n>0 ){
719  Double_t leftprob = PossiblePeak.back().PeakTunnelProb(mG_Data,mTunnelScale,mTunnelSigma);
720  Double_t rightprob = peak.PeakTunnelProb(mG_Data,mTunnelScale,mTunnelSigma);
721  PeakWindow newpeak = PeakWindow::Combine(PossiblePeak.back(),peak, leftprob<=rightprob?true:false);//Take peak with highest probability
722  PossiblePeak[n-1] = newpeak;
723  }
724  }
725  else{ PossiblePeak.push_back(peak); }//Store windows
726  PossiblePeak.back().SetPeak(mG_Data);
727  if( GetDebug()>1){LOG_DEBUG << " * |Found:"; PossiblePeak.back().Print("debug"); LOG_DEBUG<<endm;}
728  peak.Reset(mXRangeMin-1,mXRangeMax+1);//Reset variables
729  if( GetDebug()>2){LOG_DEBUG << " * |peakreset:"; peak.Print("debug"); LOG_DEBUG<<endm;}
730  LocalMax=mXRangeMin-1;
731  --ismp;//Go back one point in case new signal starts at where slope was positive again
732  }
733  }
734  }
735  if( GetDebug()>0 ){ LOG_DEBUG << "PeakAna::GetPossiblePeaks:End graph reading loop|SizePeaks:" <<PossiblePeak.size() << endm; }
736 
737  mPeaks.swap(PossiblePeak);
738 }
739 
740 //Returns index that corresponds to the serach criteria
741 Int_t PeakAna::SearchForPeak(const std::vector<PeakWindow> &PossiblePeaks)
742 {
743  if( PossiblePeaks.size()==0 ){
744  if( GetDebug()>0 ){LOG_DEBUG << "PeakAna::SearchForPeak - Error:Unable to find a valid peak\nReturning impossible index" << endm; }
745  return PossiblePeaks.size();
746  }
747  else{
748  if( GetDebug()>0 ){
749  LOG_DEBUG << "|Size of Possible peaks:"<<PossiblePeaks.size();
750  LOG_DEBUG << "|Search Peak:"<<mSearch.mStartX;
751  LOG_DEBUG << "|Search Width:"<<mSearch.mEndX;
752  LOG_DEBUG << endm;
753  }
754  Short_t peakindex=-1;
755  for( UShort_t ipeak=0; ipeak<PossiblePeaks.size(); ++ipeak ){
756  if( GetDebug()>1 ){
757  LOG_DEBUG << " - ";
758  LOG_DEBUG << "|Index:"<<ipeak;
759  PossiblePeaks.at(ipeak).Print("debug");
760  LOG_DEBUG << endm;
761  }
762  Double_t PeakLoc = PossiblePeaks.at(ipeak).mPeakX;
763  if( mSearch.mStartX-mSearch.mEndX<=PeakLoc && PeakLoc <= mSearch.mStartX+mSearch.mEndX ){
764  peakindex=ipeak;
765  if( GetDebug()>1 ){
766  LOG_DEBUG << " + ";
767  LOG_DEBUG << "|TrueIndex:"<<peakindex;
768  LOG_DEBUG << endm;
769  }
770  }
771  }
772  if( peakindex>=0 && mSearch.mStartX>0 && PossiblePeaks.at(peakindex).mP_Peak>0 ){
773  if(GetDebug()>1){PossiblePeaks.at(peakindex).Print("debug");LOG_DEBUG<<endm;}
774  return peakindex;
775  }
776  else{ return PossiblePeaks.size();}
777  }
778 }
779 
780 Int_t PeakAna::SearchForPeak(const std::vector<PeakWindow> &PossiblePeaks, const PeakWindow& search)
781 {
782  mSearch = search;
783  return this->SearchForPeak(PossiblePeaks);
784 }
785 
786 Int_t PeakAna::SearchForPeak(const std::vector<PeakWindow> &PossiblePeaks, Double_t peak, Double_t width)
787 {
788  SetSearchWindow(peak, width);
789  return this->SearchForPeak(PossiblePeaks);
790 }
791 
792 void PeakAna::MergeByProbability(std::vector<PeakWindow>& newpeaks) const
793 {
794  if( !newpeaks.empty() ){ newpeaks.clear(); }
795  for( UInt_t i=0; i<mPeaks.size(); ++i){
796  if( PeakTunnel(mPeaks.at(i)) ){
797  if( newpeaks.empty() ){ newpeaks.push_back(mPeaks.at(i)); continue; }//vector is empty so add first found peak
798  Double_t thisprob = newpeaks.back().PeakTunnelProb(mG_Data,mTunnelScale,mTunnelSigma);
799  Double_t nextprob = mPeaks.at(i).PeakTunnelProb(mG_Data,mTunnelScale,mTunnelSigma);
800  newpeaks.back().Combine(mPeaks.at(i),thisprob<=nextprob?true:false);//Take peak with highest probability
801  }
802  else{ newpeaks.push_back(mPeaks.at(i)); }
803  }
804 }
805 
806 void PeakAna::MergeByChirality(std::vector<PeakWindow>& newpeaks) const
807 {
808  //In order to check peaks to merge globally make a vector that keeps the order of the peaks vector and whose value will be +1 to merge with right peak or -1 to merge with left peak, 0 is neutral.
809  //The idea is consecutive +1 would merge with consecutive -1. The zero is special since a +1 0 -1 pattern should be merged into a single peak but a 1 0 0 -1 pattern should not be
810  //std::cout << "PeakAna::MergeByChirality - Start" << std::endl;
811  std::vector<short> mergelist;
812  for( UInt_t i=0; i<mPeaks.size(); ++i ){
813  //std::cout << "PeakAna::MergeByChirality - index:" << i << std::endl;
814  //Double_t probchir = mPeaks.at(i).PeakChiralityProb(mChiralityProbScale,mChiralityScale);
815  //if( probchir > mChiralityThreshold ){ mergelist.push_back(0); }
816  //else{ mergelist.push_back( this->MergeLeftOrRight(i) ); }
817  mergelist.push_back( this->MergeLeftOrRight(i) );
818  }
819 
820  //std::cout << "PeakAna::MergeByChirality - peaksize:" << mPeaks.size() << std::endl;
821  //std::cout << "PeakAna::MergeByChirality - mergesize:" << mergelist.size() << std::endl;
822  std::vector< std::pair<int,int> > coupledindexs = this->MergeIndices(mergelist);
823 
824  newpeaks.clear();//Clear any data in the peak
825  for( UInt_t i=0; i<mPeaks.size(); ++i ){
826  bool combined = false;
827  for( UInt_t j=0; j<coupledindexs.size(); ++j ){
828  if(static_cast<int>(i)==coupledindexs.at(j).first){
829  PeakWindow combpeak=mPeaks.at( i );
830  for( int k=coupledindexs.at(j).first+1; k<=coupledindexs.at(j).second; ++k ){
831  Double_t thisprob = combpeak.PeakTunnelProb(mG_Data,mTunnelScale,mTunnelSigma);
832  Double_t otherprob = mPeaks.at(k).PeakTunnelProb(mG_Data,mTunnelScale,mTunnelSigma);
833  combpeak.Combine(mPeaks.at(k),thisprob<=otherprob?true:false);
834  }//k loop
835  newpeaks.push_back(combpeak);
836  i=coupledindexs.at(j).second;
837  combined = true;
838  }
839  }
840  if( !combined ){ newpeaks.push_back( mPeaks.at(i) ); }
841  }
842  //std::cout << "PeakAna::MergeByChirality - newpeaksize:" << newpeaks.size() << std::endl;
843 }
844 
845 short PeakAna::MergeLeftOrRight(UInt_t index) const
846 {
847  if( index>=mPeaks.size() ){return 0;}//invalid index
848  if( mPeaks.size()==1 ){return 0;}//Only 1 peak don't need to merge
849  //PeakWindow combined = mPeaks.at(index);
850  //if( mPeaks.at(index).PeakChiralityProb(mChiralityProbScale,mChiralityScale) > mChiralityThreshold ){return 0;}
851  Double_t leftchir = 0;
852  Double_t thischir = mPeaks.at(index).PeakChirality(mChiralityPeakScale,mChiralityScale);
853  Double_t rightchir = 0;
854  if( thischir==0 || thischir==1 ){return 0;}//Sanity check [Feb, 28 2022]>(If slope exactly zero then chirality is 1 for now, maybe use small fluctation around 1 since 0 should mean nonexistent exclusively at least when chirality only depnds on mse??)
855 
856  if( index==0 ){
857  rightchir = mPeaks.at(index+1).PeakChirality(mChiralityPeakScale,mChiralityScale);
858  if( mPeaks.at(index+1).PeakChiralityProb(mChiralityProbScale,rightchir) > mChiralityThreshold ){
859  rightchir = 0; }
860  }
861  else if( index+1==mPeaks.size() ){
862  leftchir = mPeaks.at(index-1).PeakChirality(mChiralityPeakScale,mChiralityScale);
863  if( mPeaks.at(index-1).PeakChiralityProb(mChiralityProbScale,leftchir) > mChiralityThreshold ){
864  leftchir = 0; }
865  }
866  else if( 0<index && index<mPeaks.size() ){
867  leftchir = mPeaks.at(index-1).PeakChirality(mChiralityPeakScale,mChiralityScale);
868  rightchir = mPeaks.at(index+1).PeakChirality(mChiralityPeakScale,mChiralityScale);
869  if( mPeaks.at(index-1).PeakChiralityProb(mChiralityProbScale,leftchir) > mChiralityThreshold ){
870  leftchir = 0;
871  }
872  if( mPeaks.at(index+1).PeakChiralityProb(mChiralityProbScale,rightchir) > mChiralityThreshold ){
873  rightchir = 0;
874  }
875  }
876  else{ std::cout << "PeakAna::MergeLeftOrRight - WARNING:Invalid index" << std::endl; }
877 
878  //Check the 4 different cases
879  if( leftchir==0 && rightchir==0 ){ return 0; }//don't merge since left and right did not pass thresholds or do not exist
880  else if( leftchir!=0 && rightchir==0 ){//ignore right chirality
881  if( thischir*leftchir<0 ){return -1;}//opposite signs so merge with left
882  else{ return 0; }
883  }
884  else if(
885  leftchir==0 && rightchir!=0 ){//ignore left chirality
886  if( thischir*rightchir<0 ){return 1;}//opposite signs so merge with right
887  else{ return 0; }
888  }
889  else{//leftchir!=0 && rightchir!=0
890  bool leftoppsign=false;//is left and this chirality opposite signs
891  if( (leftchir*rightchir)<0 ){leftoppsign=true;}
892  bool rightoppsign=false;//is right and this chirality opposite signs
893  if( (rightchir*thischir)<0 ){rightoppsign=true;}
894 
895  Double_t leftchirprob = mPeaks.at(index-1).PeakChiralityProb(mChiralityProbScale,leftchir);
896  Double_t rightchirprob = mPeaks.at(index+1).PeakChiralityProb(mChiralityProbScale,rightchir);
897 
898  if( leftoppsign ){
899  if( rightoppsign ){//both left and right have opposite signs
900  //Merge with the lower chirality probability favoring left for equality so they will get merged??
901  //or maybe need to keep going recursively by calling this function in the direction of the chirality, this way it will stop when chiralities are on longer going back and forth??
902  if( leftchirprob<rightchirprob ){ return -1; } else{ return 1; }
903  }
904  else{ return -1; }//only left peak has opposite sign
905  }
906  else{
907  if( rightoppsign ){ return 1; }//only right peak has opposite sign
908  else{//both left and right have same sign
909  //Merge with the lower chirality favoring the left for equality so they will get merged??
910  //For same sign merge in the direction of chirality??
911  if( thischir>0 ){return 1;}
912  else{ return -1; }
913  //if( leftchirprob<rightchirprob ){ return -1; }else{return 1;}
914  }
915  }
916  }
917  //if( MergeLeft(leftchir,thischir,rightchir) ){ return -1; }
918  //else{ return 1; }
919 }
920 
921 bool PeakAna::MergeLeft(Double_t leftchir, Double_t thischir, Double_t rightchir) const
922 {
923  //Here it is the magnitude that should matter since a lower negative will still be less than any positive one, but zero should be treated as special case since don't compute for zero??
924  bool leftoppsign=false;//is left and this chirality opposite signs?
925  if( (leftchir*rightchir)<0 ){leftoppsign=true;}
926  bool rightoppsign=false;//is right and this chirality opposite signs?
927  if( (rightchir*thischir)<0 ){rightoppsign=true;}
928 
929  if( leftoppsign ){
930  if( rightoppsign ){//both left and right have opposite signs
931  //Merge with the lower chirality favoring right for equality
932  if( leftchir<rightchir ){ return true; } else{ return false; }
933  }
934  else{ return true; }//only left peak has opposite sign
935  }
936  else{
937  if( rightoppsign ){ return false; }//only right peak has opposite sign
938  else{//both left and right have same sign
939  //Merge with the lower chirality favoring the left for equality
940  if( leftchir<=rightchir ){ return true; }else{return true;}
941  }
942  }
943 }
944 
945 std::vector< std::pair<int,int> > PeakAna::MergeIndices(std::vector<short>& vec) const
946 {
947  int firstpos_index = -1;
948  int lastneg_index = -1;
949  int zero_count = 0;//counting number of consecutive zeros when firstpos_index is not
950  std::vector< std::pair<int,int> > mergeindexs;
951  for( int i=0; i<static_cast<int>(vec.size()); ++i ){
952  //std::cout << "entrance|("<<i<<")"<<vec.at(i)<<"|firstpos:"<<firstpos_index << "|lastneg:"<<lastneg_index << std::endl;
953  if( vec.at(i)>=0 ){
954  if( firstpos_index<0 ){ if( vec.at(i)>0 ){firstpos_index = i;} }
955  else{
956  if( lastneg_index>firstpos_index ){
957  //store
958  std::pair<int,int> mergepair(firstpos_index,lastneg_index);
959  mergeindexs.push_back(mergepair);
960  //std::cout << "store|firstpos:"<<firstpos_index << "|lastneg:"<<lastneg_index << std::endl;
961  if( vec.at(i)==0 ){firstpos_index = -1;}
962  else{ firstpos_index=i; }
963  lastneg_index = -1;
964  zero_count = 0;
965  continue;
966  }
967  if( vec.at(i)==0 ){
968  zero_count++;
969  if( zero_count < 1 ){ firstpos_index=-1; lastneg_index=-1; zero_count=0; }
970  continue;
971  }
972  //std::cout << "reset|firstpos:"<<firstpos_index << "|lastneg:"<<lastneg_index << std::endl;
973  }
974  }
975  if( firstpos_index>=0 ){
976  if( vec.at(i)<0 ){
977  lastneg_index = i;
978  }
979  }
980  }
981  return mergeindexs;
982 }
983 
984 void PeakAna::Draw(Option_t* opt)
985 {
986  AppendPad(opt);
987  //gPad->IncrementPaletteColor(1, mOption); (Doesn't work with ROOT v5)
988 }
989 
990 void PeakAna::Paint(Option_t* opt)
991 {
992  GetPainter(opt);
993 
994  if (mPainter) {
995  if (strlen(opt) > 0){ mPainter->Paint(opt); }
996  else{ mPainter->Paint(mOption.Data()); }
997  }
998 }
999 
1000 PeakAnaPainter* PeakAna::GetPainter(Option_t *opt)
1001 {
1002  if( mPainter==0 ){
1003  mPainter = new PeakAnaPainter();
1004  mPainter->SetPeakAna(this);
1005  }
1006  mOption = opt;
1007  return mPainter;
1008 }
1009 
1010 void PeakAna::AddPeakStats(TPaveText* pave, const char* opt)
1011 {
1012  if( pave==0 ){ return; }
1013  TString option(opt);
1014  bool statsalloption = false;
1015  bool statsdetailoption = false;
1016 
1017  option.ToLower();
1018  if( option.Contains("a") ){ statsalloption = true; }
1019  if( option.Contains("d") ){ statsdetailoption = true; }
1020 
1021  pave->AddText("--Found Peaks--");
1022 
1023  if( mComputedIndex<0 ){ this->AnalyzeForPeak(); }
1024  if( mComputedIndex == static_cast<Int_t>(mPeaks.size()) ){if( !statsalloption ){return;}}//if only writing stats for found peak don't process no found peak case
1025 
1026  //In future base the precision of the values based on the x/y range, and tunnel thershold value ??
1027  if( !statsalloption ){
1028  TText* t = pave->AddText( Form("I:%u|S:[%2.2f,%2.2f]|E[%2.2f,%2.2f]|P(%d,%2.2f,%2.2f)",
1030  (GetPeak(mComputedIndex)).mStartX,
1031  (GetPeak(mComputedIndex)).mStartY,
1032  (GetPeak(mComputedIndex)).mEndX,
1033  (GetPeak(mComputedIndex)).mEndY,
1034  (GetPeak(mComputedIndex)).mP_Peak,
1035  (GetPeak(mComputedIndex)).mPeakX,
1036  (GetPeak(mComputedIndex)).mPeakY
1037  ) );
1038  t->SetTextAlign(11);
1039  if( statsdetailoption ){
1040  TText* t2 = pave->AddText( Form(" + |Prob:%1.4f|Chir:%3.1f|ChirProb:%1.4f",
1041  (GetPeak(mComputedIndex)).PeakTunnelProb(GetData(),TunnelScale(),TunnelSigma()),
1042  (GetPeak(mComputedIndex)).PeakChirality(ChiralityPeakScale(),ChiralityScale()),
1043  (GetPeak(mComputedIndex)).PeakChiralityProb(ChiralityProbScale(),ChiralityPeakScale(),ChiralityScale())
1044  ) );
1045  t2->SetTextAlign(11);
1046  }
1047  }
1048  else{
1049  //pave->AddText( Form("|X:[%2.2f,%2.2f]|Y:[%2.2f,%2.2f]|BBS:(%d,%2.2f)|ProbSST:()") );
1050  for( Int_t ipeak=0; ipeak<NPeaks(); ++ipeak ){
1051  TText* t = pave->AddText( Form("|I:%u|S[%2.2f,%2.2f]|E[%2.2f,%2.2f]",
1052  ipeak,
1053  (GetPeak(ipeak)).mStartX,
1054  (GetPeak(ipeak)).mStartY,
1055  (GetPeak(ipeak)).mEndX,
1056  (GetPeak(ipeak)).mEndY
1057  ) );
1058  t->SetTextAlign(11);
1059  if( mComputedIndex==ipeak ){ t->SetTextColor(kRed+1); }
1060  if( statsdetailoption ){
1061  //|Chir:%3.1f|ChirProb:%1.4f
1062  //(GetPeak(ipeak)).PeakChirality(ChiralityPeakScale(),ChiralityScale()),
1063  //(GetPeak(ipeak)).PeakChiralityProb(ChiralityProbScale(),ChiralityPeakScale(),ChiralityScale())
1064  TText* t2 = pave->AddText( Form(" + |P(%d,%2.2f,%2.2f)|Prob:%1.4f",
1065  (GetPeak(ipeak)).mP_Peak,
1066  (GetPeak(ipeak)).mPeakX,
1067  (GetPeak(ipeak)).mPeakY,
1068  (GetPeak(ipeak)).PeakTunnelProb(GetData(),TunnelScale(),TunnelSigma())
1069  ) );
1070  t2->SetTextAlign(11);
1071  if( mComputedIndex==ipeak ){ t2->SetTextColor(kRed+1); }
1072  }
1073  }
1074  }
1075 
1076 }
1077 
1078 //Styling for graph
1079 Color_t PeakAna::GetLineColor() const
1080 { if(mG_Data!=0 ){return mG_Data->GetLineColor();} return 0; }
1081 Style_t PeakAna::GetLineStyle() const
1082 { if(mG_Data!=0 ){return mG_Data->GetLineStyle();} return 0; }
1083 Width_t PeakAna::GetLineWidth() const
1084 { if(mG_Data!=0 ){return mG_Data->GetLineWidth();} return 0; }
1085 
1086 Color_t PeakAna::GetFillColor() const
1087 { if(mG_Data!=0 ){return mG_Data->GetFillColor();} return 0; }
1088 Style_t PeakAna::GetFillStyle() const
1089 { if(mG_Data!=0 ){return mG_Data->GetFillStyle();} return 0; }
1090 
1091 Color_t PeakAna::GetMarkerColor() const
1092 { if(mG_Data!=0 ){return mG_Data->GetMarkerColor();} return 0; }
1093 Size_t PeakAna::GetMarkerSize() const
1094 { if(mG_Data!=0 ){return mG_Data->GetMarkerSize();} return 0; }
1095 Style_t PeakAna::GetMarkerStyle() const
1096 { if(mG_Data!=0 ){return mG_Data->GetMarkerStyle();} return 0; }
1097 
1098 void PeakAna::SetLineColor(Color_t color)
1099 { if(mG_Data!=0 ){mG_Data->SetLineColor(color);} }
1100 void PeakAna::SetLineColorAlpha(Color_t color, Float_t alpha)
1101 { if(mG_Data!=0 ){mG_Data->SetLineColorAlpha(color,alpha);} }
1102 void PeakAna::SetLineStyle(Style_t style)
1103 { if(mG_Data!=0 ){mG_Data->SetLineStyle(style);} }
1104 void PeakAna::SetLineWidth(Width_t width)
1105 { if(mG_Data!=0 ){mG_Data->SetLineWidth(width);} }
1106 
1107 void PeakAna::SetFillColor(Color_t color)
1108 { if(mG_Data!=0 ){mG_Data->SetFillColor(color);} }
1109 void PeakAna::SetFillColorAlpha(Color_t color, Float_t alpha)
1110 { if(mG_Data!=0 ){mG_Data->SetFillColorAlpha(color,alpha);} }
1111 void PeakAna::SetFillStyle(Style_t style)
1112 { if(mG_Data!=0 ){mG_Data->SetFillStyle(style);} }
1113 
1114 void PeakAna::SetMarkerColor(Color_t color)
1115 { if(mG_Data!=0 ){mG_Data->SetMarkerColor(color);} }
1116 void PeakAna::SetMarkerColorAlpha(Color_t color, Float_t alpha)
1117 { if(mG_Data!=0 ){mG_Data->SetMarkerColorAlpha(color,alpha);} }
1118 void PeakAna::SetMarkerSize(Size_t size)
1119 { if(mG_Data!=0 ){mG_Data->SetMarkerSize(size);} }
1120 void PeakAna::SetMarkerStyle(Style_t style)
1121 { if(mG_Data!=0 ){mG_Data->SetMarkerStyle(style);} }
1122 
1123 //Styling for peak painter
1124 Color_t PeakAna::GetBaseLineColor() const
1125 { if( mPainter!=0 ){ return mPainter->GetBaseLineColor(); } return 0;}
1126 Style_t PeakAna::GetBaseLineStyle() const
1127 { if( mPainter!=0 ){ return mPainter->GetBaseLineStyle(); } return 0;}
1128 Width_t PeakAna::GetBaseLineWidth() const
1129 { if( mPainter!=0 ){ return mPainter->GetBaseLineWidth(); } return 0;}
1130 
1131 Color_t PeakAna::GetHitLineColor() const
1132 { if( mPainter!=0 ){ return mPainter->GetHitLineColor(); } return 0;}
1133 Style_t PeakAna::GetHitLineStyle() const
1134 { if( mPainter!=0 ){ return mPainter->GetHitLineStyle(); } return 0;}
1135 Width_t PeakAna::GetHitLineWidth() const
1136 { if( mPainter!=0 ){ return mPainter->GetHitLineWidth(); } return 0;}
1137 
1138 void PeakAna::SetBaseLineColor(Color_t color)
1139 {GetPainter()->SetBaseLineColor(color);}
1140 void PeakAna::SetBaseLineColorAlpha(Color_t color,Float_t alpha)
1141 {GetPainter()->SetBaseLineColorAlpha(color,alpha);}
1142 void PeakAna::SetBaseLineStyle(Style_t style)
1143 {GetPainter()->SetBaseLineStyle(style);}
1144 void PeakAna::SetBaseLineWidth(Width_t width)
1145 {GetPainter()->SetBaseLineWidth(width);}
1146 
1147 void PeakAna::SetHitLineColor(Color_t color)
1148 {GetPainter()->SetHitLineColor(color);}
1149 void PeakAna::SetHitLineColorAlpha(Color_t color,Float_t alpha)
1150 {GetPainter()->SetHitLineColorAlpha(color,alpha);}
1151 void PeakAna::SetHitLineStyle(Style_t style)
1152 {GetPainter()->SetHitLineStyle(style);}
1153 void PeakAna::SetHitLineWidth(Width_t width)
1154 {GetPainter()->SetHitLineWidth(width);}
1155 
1156 Color_t PeakAna::GetPeakLineColorS(UInt_t peakidx) const
1157 {return GetPeak(peakidx).GetStartLineColor();}
1158 Color_t PeakAna::GetPeakLineColorE(UInt_t peakidx) const
1159 {return GetPeak(peakidx).GetEndLineColor();}
1160 Style_t PeakAna::GetPeakStyleS(UInt_t peakidx) const
1161 {return GetPeak(peakidx).GetStartLineStyle();}
1162 Style_t PeakAna::GetPeakStyleE(UInt_t peakidx) const
1163 {return GetPeak(peakidx).GetEndLineStyle();}
1164 Width_t PeakAna::GetPeakWidthS(UInt_t peakidx) const
1165 {return GetPeak(peakidx).GetStartLineWidth();}
1166 Width_t PeakAna::GetPeakWidthE(UInt_t peakidx) const
1167 {return GetPeak(peakidx).GetEndLineWidth();}
1168 
1169 Color_t PeakAna::GetFoundPeakLineColorS() const
1170 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ return GetPeak(mComputedIndex).GetStartLineColor(); } return 0; }
1171 Color_t PeakAna::GetFoundPeakLineColorE() const
1172 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ return GetPeak(mComputedIndex).GetEndLineColor(); } return 0; }
1173 Style_t PeakAna::GetFoundPeakStyleS() const
1174 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ return GetPeak(mComputedIndex).GetStartLineStyle(); } return 0; }
1175 Style_t PeakAna::GetFoundPeakStyleE() const
1176 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ return GetPeak(mComputedIndex).GetEndLineStyle(); } return 0; }
1177 Width_t PeakAna::GetFoundPeakWidthS() const
1178 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ return GetPeak(mComputedIndex).GetStartLineWidth(); } return 0; }
1179 Width_t PeakAna::GetFoundPeakWidthE() const
1180 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ return GetPeak(mComputedIndex).GetEndLineWidth(); } return 0; }
1181 
1182 void PeakAna::SetFoundPeakLineColorS(Color_t s_color)
1183 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ GetPeak(mComputedIndex).SetStartLineColor(s_color); } }
1184 void PeakAna::SetFoundPeakLineColorE(Color_t e_color)
1185 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ GetPeak(mComputedIndex).SetEndLineColor(e_color); } }
1186 void PeakAna::SetFoundPeakLineColor(Color_t s_color, Color_t e_color)
1187 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ GetPeak(mComputedIndex).SetStartLineColor(s_color); GetPeak(mComputedIndex).SetEndLineColor(e_color); } }
1188 void PeakAna::SetFoundPeakStyleS(Style_t s_style)
1189 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ GetPeak(mComputedIndex).SetStartLineStyle(s_style); } }
1190 void PeakAna::SetFoundPeakStyleE(Style_t e_style)
1191 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ GetPeak(mComputedIndex).SetEndLineStyle(e_style); } }
1192 void PeakAna::SetFoundPeakStyle(Style_t s_style,Style_t e_style)
1193 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ GetPeak(mComputedIndex).SetStartLineStyle(s_style); GetPeak(mComputedIndex).SetEndLineStyle(e_style); } }
1194 void PeakAna::SetFoundPeakWidthS(Width_t s_width)
1195 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ GetPeak(mComputedIndex).SetStartLineWidth(s_width); } }
1196 void PeakAna::SetFoundPeakWidthE(Width_t e_width)
1197 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ GetPeak(mComputedIndex).SetEndLineWidth(e_width); } }
1198 void PeakAna::SetFoundPeakWidth(Width_t s_width, Width_t e_width)
1199 { if( 0<=mComputedIndex && mComputedIndex<static_cast<Int_t>(mPeaks.size()) ){ GetPeak(mComputedIndex).SetStartLineWidth(s_width); GetPeak(mComputedIndex).SetEndLineWidth(e_width); } }
1200 
1201 void PeakAna::SetPeakLineColorS(UInt_t peakidx, Color_t s_color)
1202 { GetPeak(peakidx).SetStartLineColor(s_color); }
1203 void PeakAna::SetPeakLineColorE(UInt_t peakidx, Color_t e_color)
1204 { GetPeak(peakidx).SetStartLineColor(e_color); }
1205 void PeakAna::SetPeakLineColor(UInt_t peakidx, Color_t s_color, Color_t e_color)
1206 { GetPeak(peakidx).SetStartLineColor(s_color); GetPeak(peakidx).SetStartLineColor(e_color); }
1207 void PeakAna::SetPeakLineColorAlphaS(UInt_t peakidx, Color_t s_color,Float_t s_alpha)
1208 { GetPeak(peakidx).SetStartLineColorAlpha( s_color, s_alpha); }
1209 void PeakAna::SetPeakLineColorAlphaE(UInt_t peakidx, Color_t e_color,Float_t e_alpha)
1210 { GetPeak(peakidx).SetEndLineColorAlpha( e_color, e_alpha); }
1211 void PeakAna::SetPeakLineColorAlpha(UInt_t peakidx, Color_t s_color,Float_t s_alpha, Color_t e_color, Float_t e_alpha)
1212 { GetPeak(peakidx).SetStartLineColorAlpha( s_color, s_alpha); GetPeak(peakidx).SetEndLineColorAlpha( e_color, e_alpha); }
1213 void PeakAna::SetPeakLineStyleS(UInt_t peakidx, Style_t s_style)
1214 { GetPeak(peakidx).SetStartLineStyle(s_style); }
1215 void PeakAna::SetPeakLineStyleE(UInt_t peakidx, Style_t e_style)
1216 { GetPeak(peakidx).SetEndLineStyle(e_style); }
1217 void PeakAna::SetPeakLineStyle(UInt_t peakidx, Style_t s_style, Style_t e_style)
1218 { GetPeak(peakidx).SetStartLineStyle(s_style); GetPeak(peakidx).SetEndLineStyle(e_style); }
1219 void PeakAna::SetPeakLineWidthS(UInt_t peakidx, Width_t s_width)
1220 { GetPeak(peakidx).SetStartLineWidth(s_width); }
1221 void PeakAna::SetPeakLineWidthE(UInt_t peakidx, Width_t e_width)
1222 { GetPeak(peakidx).SetEndLineWidth(e_width); }
1223 void PeakAna::SetPeakLineWidth(UInt_t peakidx, Width_t s_width, Width_t e_width)
1224 { GetPeak(peakidx).SetStartLineWidth(s_width); GetPeak(peakidx).SetEndLineWidth(e_width); }
1225 
1226 void PeakAna::SetAllPeakLineColor(Color_t s_color, Color_t e_color)
1227 {
1228  for( UInt_t ipeak=0; ipeak<mPeaks.size(); ++ipeak ){
1229  mPeaks.at(ipeak).SetStartLineColor(s_color);
1230  mPeaks.at(ipeak).SetEndLineColor(e_color);
1231  }
1232 }
1233 void PeakAna::SetAllPeakLineStyle(Style_t s_style, Style_t e_style)
1234 {
1235  for( UInt_t ipeak=0; ipeak<mPeaks.size(); ++ipeak ){
1236  mPeaks.at(ipeak).SetStartLineStyle(s_style);
1237  mPeaks.at(ipeak).SetEndLineStyle(e_style);
1238  }
1239 }
1240 void PeakAna::SetAllPeakLineWidth(Width_t s_width, Width_t e_width)
1241 {
1242  for( UInt_t ipeak=0; ipeak<mPeaks.size(); ++ipeak ){
1243  mPeaks.at(ipeak).SetStartLineWidth(s_width);
1244  mPeaks.at(ipeak).SetEndLineWidth(e_width);
1245  }
1246 }
1247 
1248 double* PeakAna::GaussianMatrix2D(int rx, double sx, int ry, double sy, bool kNorm)
1249 {
1250  if( rx<0 ){rx = -rx;}
1251  if( ry<0 ){ry = -ry;}
1252  if( sx==0 ){ sx = static_cast<double>(rx)*0.5; }
1253  if( sy==0 ){ sy = static_cast<double>(ry)*0.5; }
1254  const int nsizex = (2*rx+1);
1255  const int nsizey = (2*ry+1);
1256 
1257  double GM_sum = 0;
1258  //@[June 7, 2022]>[How to create 2D dynamic arrays](https://stackoverflow.com/questions/936687/how-do-i-declare-a-2d-array-in-c-using-new)
1259  double* GM_2D = new double[nsizex*nsizey];
1260  for( int ix=0; ix<nsizex; ++ix ){
1261  for( int jy=0; jy<nsizey; ++jy ){
1262  int xi = ix-rx;
1263  int yi = jy-ry;
1264  double product = 1.0;
1265  if( sx!=0 ){ product *= exp( (-1.0*xi*xi)/(2.0*sx*sx) )/(sqrt(2.0*3.14159265358979323846)*sx); }
1266  if( sy!=0 ){ product *= exp( (-1.0*yi*yi)/(2.0*sy*sy) )/(sqrt(2.0*3.14159265358979323846)*sy); }
1267  GM_2D[ix*nsizey+jy] = product;
1268  GM_sum += product;
1269  }
1270  }
1271 
1272  if( kNorm ){
1273  double invsum = 1.0/GM_sum;
1274  for( int i=0; i<nsizex; ++i ){
1275  for( int j=0; j<nsizey; ++j ){
1276  GM_2D[i*nsizey+j] *= invsum;
1277  }
1278  }
1279  }
1280  return GM_2D;
1281 }
1282 
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
void SetTunnelPars(Double_t scale, Double_t sigma)
Definition: PeakAna.cxx:371
Int_t mComputedIndex
Index in mPeaks where a peak was found.
Definition: PeakAna.h:436
void SetWindow(const Int_t start, const Int_t end)
Overwrite peak start and end values in mFoundPeak. WARNING doesn&#39;t set peak values nor overwrite mCom...
Definition: PeakAna.cxx:357
virtual ~PeakAna()
Destructor.
Definition: PeakAna.cxx:146
static PeakWindow Combine(const PeakWindow &leftpeak, const PeakWindow &rightpeak, bool keepleft=true)
combine two PeakWindow objects
Definition: PeakWindow.cxx:156
UInt_t GetDebug() const
Definition: PeakAna.h:227
static double * GaussianMatrix2D(int rx, double sx=0, int ry=0, double sy=0, bool kNorm=true)
Generates up to a 2D matrix of Gaussian weights.
Definition: PeakAna.cxx:1248
Double_t SearchPeak() const
Definition: PeakAna.h:185
virtual void MergeByProbability(std::vector< PeakWindow > &newpeaks) const
Merges peaks in mPeaks using peak probability parameters.
Definition: PeakAna.cxx:792
virtual void SetPeakAna(PeakAna *ana)
virtual TObject * Clone(const char *newname) const
Clones internal graph as opposed to just copying the pointer.
Definition: PeakAna.cxx:129
Double_t mBaselineSigma
Error on PeakAna::mBaseline.
Definition: PeakAna.h:463
const PeakWindow & GetPeak(UInt_t peakidx) const
Definition: PeakAna.h:228
static TGraph * ConvertHistToGraph(TH1 *graph, UInt_t numavgs=1)
Converts a histogram to a graph.
Definition: PeakAna.cxx:382
Int_t FoundPeakIndex() const
Definition: PeakAna.h:233
void SetSearchWindow(Double_t peak, Double_t width)
Sets peak search parameters.
Definition: PeakAna.cxx:349
Double_t PeakY()
y-value of found signal peak
Definition: PeakAna.cxx:245
void SetTunnelThreshold(Double_t value)
Definition: PeakAna.cxx:376
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
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
virtual TGraph * GetData() const
Definition: PeakAna.h:226
PeakAna * MeanFilter(Int_t sizeavgs=0, bool copy=true)
Apply a Mean filter to the data.
Definition: PeakAna.cxx:435
Double_t mXRangeMax
Absolute possible x-value maximum of data.
Definition: PeakAna.h:489
void PeakXY(Double_t &xval, Double_t &yval)
get peak x, and y value directly by reference
Definition: PeakAna.cxx:252
PeakAna ConvertPeaksToAna()
same as ConvertPeaksToAna() but returns new PeakAna with same settings without modifying current one ...
Definition: PeakAna.cxx:614
virtual void Draw(Option_t *opt="")
Draw method for PeakAna.
Definition: PeakAna.cxx:984
TString mOption
Drawing option.
Definition: PeakAna.h:530
virtual void GetPossiblePeaks()
Finds all possible Peaks in signal with some criteria.
Definition: PeakAna.cxx:640
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
virtual void GetXYMax(Double_t xmin, Double_t xmax)
Finds and sets mMaxX and mMaxY.
Definition: PeakAna.cxx:222
void SetBaseline(Double_t base, Double_t sigma)
Definition: PeakAna.cxx:330
Double_t mPeakX
x-value of peak position as determined by SetPeak()
Definition: PeakWindow.h:76
void SetRange(Double_t xmin, Double_t ymin, Double_t xmax, Double_t ymax)
Sets the absolute range of the data.
Definition: PeakAna.cxx:342
Double_t mYRangeMin
Absolute possible y-value minimum of data.
Definition: PeakAna.h:490
Double_t mTunnelSigma
Sigma for Gaussian for determining tunneling probability (default is 1) see #PeakWindow::PeakTunelPro...
Definition: PeakAna.h:494
void ResetPeak()
Resets values associated with peak finding.
Definition: PeakAna.cxx:292
Int_t mP_Peak
Point Number of peak in a TGraph object (P for point), point is such that slope with previous point w...
Definition: PeakWindow.h:75
virtual Int_t AnalyzeForPeak()
Main analysis method for finding peaks.
Definition: PeakAna.cxx:153
Double_t mMaxX
x-value where global maximum occurs
Definition: PeakAna.h:471
virtual void Print(Option_t *opt="") const
Prints information about PeakWindow.
Definition: PeakWindow.cxx:96
void SetTunnelScale(Double_t value)
Definition: PeakAna.cxx:363
Double_t SearchWidth() const
Definition: PeakAna.h:186
void SetBaselineSigmaScale(Double_t scale)
Definition: PeakAna.cxx:337
virtual void AddPeakStats(TPaveText *pave, const char *opt="")
Add peak information to a &quot;statistics&quot; box.
Definition: PeakAna.cxx:1010
PeakAnaPainter * mPainter
Painter for this class.
Definition: PeakAna.h:513
virtual void Print(Option_t *opt="") const
Print peak information.
Definition: PeakAna.cxx:259
Double_t BaselineSigma() const
Definition: PeakAna.h:179
virtual void Reset(Double_t start, Double_t end)
Reset PeakWindow to constructor state.
Definition: PeakWindow.cxx:79
Double_t mStartY
y value associated with mStartX
Definition: PeakWindow.h:73
PeakAna * GausFilter(Int_t sizeavgs=0, bool copy=true)
Apply a Gaussian filter to the data.
Definition: PeakAna.cxx:518
Double_t * mFilterWeights
Array of weights to use in filtering, size is 2*mFilterScale+1.
Definition: PeakAna.h:510
Double_t TunnelScale() const
Definition: PeakAna.h:187
Double_t MidPoint(TGraph *graph=0) const
Computes the the line formed by the points (mStartX,mStartY) and (mEndX,mEndY) and evaluates that lin...
Definition: PeakWindow.cxx:194
Double_t mMaxY
y-value of global maximum
Definition: PeakAna.h:472
Double_t mBaselineSigmaScale
scale on PeakAna::mBaselineSigma to determine final threshold
Definition: PeakAna.h:469
PeakAna()
Default constructor that always creates a new TGraph.
Definition: PeakAna.cxx:6
void SetFilter(UInt_t filter, Int_t scale, Double_t sigma=0)
Set the filter to use when peak finding.
Definition: PeakAna.cxx:320
Double_t PeakProb(const PeakWindow &window, Double_t scale, Double_t sigma) const
compute a given PeakWindow probability using internal graph
Definition: PeakAna.cxx:635
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
bool PeakTunnel(const PeakWindow &window) const
test whether a given peak satisifies peak tunnel parameters
Definition: PeakAna.cxx:619
void SetPeak(TGraph *gdata)
sets mPeakX based on mP_Peak using line of slopes from points (mP_Peak-1,mP_Peak) and (mP_Peak...
Definition: PeakWindow.cxx:112
Double_t Baseline() const
Definition: PeakAna.h:178
bool ValidPeakIdx() const
Definition: PeakAna.cxx:204
Double_t mTunnelScale
Scale on exponential for determining tunneling probability (default is 1) see #PeakWindow::PeakTunelP...
Definition: PeakAna.h:493
UInt_t mFilter
Filter to use in AnalyzeForPeak()
Definition: PeakAna.h:509
void Init()
Initialize internal varaibles.
Definition: PeakAna.cxx:31
int NPeaks() const
Definition: PeakAna.h:246
Int_t mFilterScale
How many points to group together when applying filter.
Definition: PeakAna.h:511
Double_t mYRangeMax
Absolute possible y-value maximum of data.
Definition: PeakAna.h:491
Double_t mEndX
x value for end of the peak window
Definition: PeakWindow.h:72
Double_t mBaseline
Minimum threshold to search for a peak.
Definition: PeakAna.h:456
Double_t BaselineSigmaScale() const
Definition: PeakAna.h:180
Double_t mTunnelThreshold
Cutoff probability for peak tunneling method. If threshold less than 0 (default) then skip peak tunne...
Definition: PeakAna.h:495
Double_t mDeltaX
graph known delta-x
Definition: PeakAna.h:478
PeakAna & operator=(const PeakAna &rhs)
Assignment operator doesn&#39;t clone graph.
Definition: PeakAna.cxx:83
Double_t mEndY
y value associated with mEndX
Definition: PeakWindow.h:74
Double_t mPeakY
y-value at mP_Peak
Definition: PeakWindow.h:77
void ConvertPeaksToGraph()
same as ConvertPeaksToGraph() but creates a new graph that replaces old one
Definition: PeakAna.cxx:412
Double_t TunnelSigma() const
Definition: PeakAna.h:188
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
void SetTunnelSigma(Double_t value)
Definition: PeakAna.cxx:367
void SetPeak(const Int_t peakpoint, const Double_t peakx)
Overwrite peak location in #mFoundpeak. WARNING doesn&#39;t set start and end values nor overwrite mCompu...
Definition: PeakAna.cxx:355
bool GoodWindow()
Check if found peak is inside mXRangeMin, mYRangeMin, mXRangeMax, mYRangeMax and has logical values...
Definition: PeakAna.cxx:210