CMS 3D CMS Logo

/data/refman/pasoursint/CMSSW_4_1_8_patch12/src/RecoLocalCalo/HcalRecAlgos/src/HBHEPulseShapeFlag.cc

Go to the documentation of this file.
00001 //---------------------------------------------------------------------------
00002 #include <string>
00003 #include <vector>
00004 #include <iostream>
00005 #include <algorithm>
00006 #include <fstream>
00007 #include <cmath>
00008 using namespace std;
00009 //---------------------------------------------------------------------------
00010 #include "RecoLocalCalo/HcalRecAlgos/interface/HBHEPulseShapeFlag.h"
00011 #include "RecoLocalCalo/HcalRecAlgos/interface/HcalCaloFlagLabels.h"
00012 
00013 #include "FWCore/MessageLogger/interface/MessageLogger.h"
00014 
00015 #include "DataFormats/HcalDigi/interface/HBHEDataFrame.h"
00016 #include "DataFormats/HcalRecHit/interface/HcalRecHitCollections.h"
00017 #include "DataFormats/HcalRecHit/interface/HBHERecHit.h"
00018 
00019 #include "CalibFormats/HcalObjects/interface/HcalCalibrations.h"
00020 #include "CalibFormats/HcalObjects/interface/HcalCoderDb.h"
00021 #include "CalibCalorimetry/HcalAlgos/interface/HcalPulseShapes.h"
00022 //---------------------------------------------------------------------------
00023 HBHEPulseShapeFlagSetter::HBHEPulseShapeFlagSetter()
00024 {
00025    //
00026    // Argumentless constructor; should not be used
00027    // 
00028    // If arguments not properly specified for the constructor, I don't think
00029    // we'd trust the flagging algorithm.
00030    // Set the minimum charge threshold large enough so that nothing will be flagged.
00031    // 
00032 
00033    mMinimumChargeThreshold = 99999999;
00034 }
00035 //---------------------------------------------------------------------------
00036 HBHEPulseShapeFlagSetter::HBHEPulseShapeFlagSetter(double MinimumChargeThreshold,
00037    unsigned int TrianglePeakTS,
00038    vector<double> LinearThreshold, 
00039    vector<double> LinearCut,
00040    vector<double> RMS8MaxThreshold, 
00041    vector<double> RMS8MaxCut,
00042    vector<double> LeftSlopeThreshold, 
00043    vector<double> LeftSlopeCut,
00044    vector<double> RightSlopeThreshold, 
00045    vector<double> RightSlopeCut,
00046    vector<double> RightSlopeSmallThreshold, 
00047    vector<double> RightSlopeSmallCut,
00048    bool UseDualFit, 
00049    bool TriangleIgnoreSlow)
00050 {
00051    //
00052    // The constructor that should be used
00053    //
00054    // Copies various thresholds and limits and parameters to the class for future use.
00055    // Also calls the Initialize() function
00056    //
00057 
00058    mMinimumChargeThreshold = MinimumChargeThreshold;
00059    mTrianglePeakTS = TrianglePeakTS;
00060    mTriangleIgnoreSlow = TriangleIgnoreSlow;
00061 
00062    for(int i = 0; i < (int)LinearThreshold.size() && i < (int)LinearCut.size(); i++)
00063       mLambdaLinearCut.push_back(pair<double, double>(LinearThreshold[i], LinearCut[i]));
00064    sort(mLambdaLinearCut.begin(), mLambdaLinearCut.end());
00065 
00066    for(int i = 0; i < (int)RMS8MaxThreshold.size() && i < (int)RMS8MaxCut.size(); i++)
00067       mLambdaRMS8MaxCut.push_back(pair<double, double>(RMS8MaxThreshold[i], RMS8MaxCut[i]));
00068    sort(mLambdaRMS8MaxCut.begin(), mLambdaRMS8MaxCut.end());
00069 
00070    for(int i = 0; i < (int)LeftSlopeThreshold.size() && i < (int)LeftSlopeCut.size(); i++)
00071       mLeftSlopeCut.push_back(pair<double, double>(LeftSlopeThreshold[i], LeftSlopeCut[i]));
00072    sort(mLeftSlopeCut.begin(), mLeftSlopeCut.end());
00073 
00074    for(int i = 0; i < (int)RightSlopeThreshold.size() && i < (int)RightSlopeCut.size(); i++)
00075       mRightSlopeCut.push_back(pair<double, double>(RightSlopeThreshold[i], RightSlopeCut[i]));
00076    sort(mRightSlopeCut.begin(), mRightSlopeCut.end());
00077 
00078    for(int i = 0; i < (int)RightSlopeSmallThreshold.size() && i < (int)RightSlopeSmallCut.size(); i++)
00079       mRightSlopeSmallCut.push_back(pair<double, double>(RightSlopeSmallThreshold[i], RightSlopeSmallCut[i]));
00080    sort(mRightSlopeSmallCut.begin(), mRightSlopeSmallCut.end());
00081 
00082    mUseDualFit = UseDualFit;
00083 
00084    Initialize();
00085 }
00086 //---------------------------------------------------------------------------
00087 HBHEPulseShapeFlagSetter::~HBHEPulseShapeFlagSetter()
00088 {
00089    // Dummy destructor - there's nothing to destruct by hand here
00090 }
00091 //---------------------------------------------------------------------------
00092 void HBHEPulseShapeFlagSetter::Clear()
00093 {
00094    // Dummy function in case something needs to be cleaned....but none right now
00095 }
00096 //---------------------------------------------------------------------------
00097 void HBHEPulseShapeFlagSetter::SetPulseShapeFlags(HBHERecHit &hbhe, 
00098    const HBHEDataFrame &digi,
00099    const HcalCoder &coder, 
00100    const HcalCalibrations &calib)
00101 {
00102    //
00103    // Decide if a digi/pulse is good or bad using fit-based discriminants
00104    //
00105    // SetPulseShapeFlags determines the total charge in the digi.
00106    // If the charge is above the minimum threshold, the code then
00107    // runs the flag-setting algorithms to determine whether the
00108    // flags should be set.
00109    //
00110 
00111    CaloSamples Tool;
00112    coder.adc2fC(digi, Tool);
00113 
00114    mCharge.clear();  // mCharge is a vector of (pedestal-subtracted) Charge values vs. time slice
00115    mCharge.resize(digi.size());
00116 
00117    double TotalCharge = 0;
00118 
00119    for(int i = 0; i < (int)digi.size(); ++i)
00120    {
00121       mCharge[i] = Tool[i] - calib.pedestal(digi.sample(i).capid());
00122       TotalCharge += mCharge[i];
00123    }
00124 
00125    // No flagging if TotalCharge is less than threshold
00126    if(TotalCharge < mMinimumChargeThreshold)
00127       return;
00128 
00129    double NominalChi2 = 0; 
00130    if (mUseDualFit == true)
00131      NominalChi2=PerformDualNominalFit(mCharge); 
00132    else
00133      NominalChi2=PerformNominalFit(mCharge);
00134 
00135    double LinearChi2 = PerformLinearFit(mCharge);
00136 
00137    double RMS8Max = CalculateRMS8Max(mCharge);
00138    TriangleFitResult TriangleResult = PerformTriangleFit(mCharge);
00139 
00140    // Set the HBHEFlatNoise and HBHESpikeNoise flags
00141    if(CheckPassFilter(TotalCharge, log(LinearChi2) - log(NominalChi2), mLambdaLinearCut, -1) == false)
00142       hbhe.setFlagField(1, HcalCaloFlagLabels::HBHEFlatNoise);
00143    if(CheckPassFilter(TotalCharge, log(RMS8Max) * 2 - log(NominalChi2), mLambdaRMS8MaxCut, -1) == false)
00144       hbhe.setFlagField(1, HcalCaloFlagLabels::HBHESpikeNoise);
00145 
00146    // Set the HBHETriangleNoise flag
00147    if ((int)mCharge.size() >= mTrianglePeakTS)  // can't compute flag if peak TS isn't present; revise this at some point?
00148    {
00149      // initial values
00150      double TS4Left = 1000;
00151      double TS4Right = 1000;
00152  
00153      // Use 'if' statements to protect against slopes that are either 0 or very small
00154      if (TriangleResult.LeftSlope>1e-5)
00155        TS4Left = mCharge[mTrianglePeakTS] / TriangleResult.LeftSlope;
00156      if (TriangleResult.RightSlope<-1e-5)
00157        TS4Right = mCharge[mTrianglePeakTS] / -TriangleResult.RightSlope;
00158      
00159      if(TS4Left > 1000 || TS4Left < -1000)
00160        TS4Left = 1000;
00161      if(TS4Right > 1000 || TS4Right < -1000)
00162        TS4Right = 1000;
00163      
00164      if(mTriangleIgnoreSlow == false)   // the slow-rising and slow-dropping edges won't be useful in 50ns/75ns
00165        {
00166          if(CheckPassFilter(mCharge[mTrianglePeakTS], TS4Left, mLeftSlopeCut, 1) == false)
00167            hbhe.setFlagField(1, HcalCaloFlagLabels::HBHETriangleNoise);
00168          else if(CheckPassFilter(mCharge[mTrianglePeakTS], TS4Right, mRightSlopeCut, 1) == false)
00169            hbhe.setFlagField(1, HcalCaloFlagLabels::HBHETriangleNoise);
00170        }
00171      
00172      // fast-dropping ones should be checked in any case
00173      if(CheckPassFilter(mCharge[mTrianglePeakTS], TS4Right, mRightSlopeSmallCut, -1) == false)
00174        hbhe.setFlagField(1, HcalCaloFlagLabels::HBHETriangleNoise);
00175    }
00176 }
00177 //---------------------------------------------------------------------------
00178 void HBHEPulseShapeFlagSetter::Initialize()
00179 {
00180    // 
00181    // Initialization: whatever preprocess is needed
00182    //
00183    // 1. Get the ideal pulse shape from CMSSW
00184    //
00185    //    Since the HcalPulseShapes class stores the ideal pulse shape in terms of 256 numbers,
00186    //    each representing 1ns integral of the ideal pulse, here I'm taking out the vector
00187    //    by calling at() function.
00188    //
00189    //    A cumulative distribution is formed and stored to save some time doing integration to TS later on
00190    //
00191    // 2. Reserve space for vector
00192    //
00193 
00194    vector<double> PulseShape;
00195 
00196    HcalPulseShapes Shapes;
00197    HcalPulseShapes::Shape HPDShape = Shapes.hbShape();
00198 
00199    PulseShape.reserve(350);
00200    for(int i = 0; i < 200; i++)
00201       PulseShape.push_back(HPDShape.at(i));
00202    PulseShape.insert(PulseShape.begin(), 150, 0);   // Safety margin of a lot of zeros in the beginning
00203 
00204    CumulativeIdealPulse.reserve(350);
00205    CumulativeIdealPulse.clear();
00206    CumulativeIdealPulse.push_back(0);
00207    for(unsigned int i = 1; i < PulseShape.size(); i++)
00208       CumulativeIdealPulse.push_back(CumulativeIdealPulse[i-1] + PulseShape[i]);
00209 
00210    // reserve space for vector
00211    mCharge.reserve(10);
00212 }
00213 //---------------------------------------------------------------------------
00214 TriangleFitResult HBHEPulseShapeFlagSetter::PerformTriangleFit(const vector<double> &Charge)
00215 {
00216    //
00217    // Perform a "triangle fit", and extract the slopes
00218    //
00219    // Left-hand side and right-hand side are not correlated to each other - do them separately
00220    //
00221 
00222    TriangleFitResult result;
00223    result.Chi2 = 0;
00224    result.LeftSlope = 0;
00225    result.RightSlope = 0;
00226 
00227    int DigiSize = Charge.size();
00228 
00229    // right side, starting from TS4
00230    double MinimumRightChi2 = 1000000;
00231    double Numerator = 0;
00232    double Denominator = 0;
00233 
00234    for(int iTS = mTrianglePeakTS + 2; iTS <= DigiSize; iTS++)   // the place where first TS center in flat line
00235    {
00236       // fit a straight line to the triangle part
00237       Numerator = 0;
00238       Denominator = 0;
00239 
00240       for(int i = mTrianglePeakTS + 1; i < iTS; i++)
00241       {
00242          Numerator += (i - mTrianglePeakTS) * (Charge[i] - Charge[mTrianglePeakTS]);
00243          Denominator += (i - mTrianglePeakTS) * (i - mTrianglePeakTS);
00244       }
00245 
00246       double BestSlope = 0;
00247       if (Denominator!=0) BestSlope = Numerator / Denominator;
00248       if(BestSlope > 0)
00249          BestSlope = 0;
00250 
00251       // check if the slope is reasonable
00252       if(iTS != DigiSize)
00253         {
00254           // iTS starts at mTrianglePeak+2; denominator never zero
00255           if(BestSlope > -1 * Charge[mTrianglePeakTS] / (iTS - mTrianglePeakTS))
00256             BestSlope = -1 * Charge[mTrianglePeakTS] / (iTS - mTrianglePeakTS);
00257           if(BestSlope < -1 * Charge[mTrianglePeakTS] / (iTS - 1 - mTrianglePeakTS))
00258             BestSlope = -1 * Charge[mTrianglePeakTS] / (iTS - 1 - mTrianglePeakTS);
00259         }
00260       else
00261         {
00262           // iTS starts at mTrianglePeak+2; denominator never zero
00263           if(BestSlope < -1 * Charge[mTrianglePeakTS] / (iTS - 1 - mTrianglePeakTS)) 
00264             BestSlope = -1 * Charge[mTrianglePeakTS] / (iTS - 1 - mTrianglePeakTS);
00265         }
00266       
00267       // calculate partial chi2
00268 
00269       // The shape I'm fitting is more like a tent than a triangle.
00270       // After the end of triangle edge assume a flat line
00271 
00272       double Chi2 = 0;
00273       for(int i = mTrianglePeakTS + 1; i < iTS; i++)
00274          Chi2 += (Charge[mTrianglePeakTS] - Charge[i] + (i - mTrianglePeakTS) * BestSlope)
00275             * (Charge[mTrianglePeakTS] - Charge[i] + (i - mTrianglePeakTS) * BestSlope);
00276       for(int i = iTS; i < DigiSize; i++)    // Assumes fit line = 0 for iTS > fit
00277          Chi2 += Charge[i] * Charge[i];
00278 
00279       if(Chi2 < MinimumRightChi2)
00280       {
00281          MinimumRightChi2 = Chi2;
00282          result.RightSlope = BestSlope;
00283       }
00284    }   // end of right-hand side loop
00285 
00286    // left side
00287    double MinimumLeftChi2 = 1000000;
00288 
00289    for(int iTS = 0; iTS < (int)mTrianglePeakTS; iTS++)   // the first time after linear fit ends
00290    {
00291       // fit a straight line to the triangle part
00292       Numerator = 0;
00293       Denominator = 0;
00294       for(int i = iTS; i < (int)mTrianglePeakTS; i++)
00295       {
00296          Numerator = Numerator + (i - mTrianglePeakTS) * (Charge[i] - Charge[mTrianglePeakTS]);
00297          Denominator = Denominator + (i - mTrianglePeakTS) * (i - mTrianglePeakTS);
00298       }
00299 
00300       double BestSlope = 0;
00301       if (Denominator!=0) BestSlope = Numerator / Denominator;
00302       if (BestSlope < 0)
00303         BestSlope = 0;
00304 
00305       // check slope
00306       if(iTS != 0)
00307         {
00308           // iTS must be >0 and < mTrianglePeakTS; slope never 0
00309           if(BestSlope > Charge[mTrianglePeakTS] / (mTrianglePeakTS - iTS))
00310             BestSlope = Charge[mTrianglePeakTS] / (mTrianglePeakTS - iTS);
00311           if(BestSlope < Charge[mTrianglePeakTS] / (mTrianglePeakTS + 1 - iTS))
00312             BestSlope = Charge[mTrianglePeakTS] / (mTrianglePeakTS + 1 - iTS);
00313         }
00314       else
00315         {
00316           if(BestSlope > Charge[mTrianglePeakTS] / (mTrianglePeakTS - iTS))
00317             BestSlope = Charge[mTrianglePeakTS] / (mTrianglePeakTS - iTS);
00318         }
00319       
00320       // calculate minimum chi2
00321       double Chi2 = 0;
00322       for(int i = 0; i < iTS; i++)
00323          Chi2 += Charge[i] * Charge[i];
00324       for(int i = iTS; i < (int)mTrianglePeakTS; i++)
00325          Chi2 += (Charge[mTrianglePeakTS] - Charge[i] + (i - mTrianglePeakTS) * BestSlope)
00326             * (Charge[mTrianglePeakTS] - Charge[i] + (i - mTrianglePeakTS) * BestSlope);
00327 
00328       if(MinimumLeftChi2 > Chi2)
00329       {
00330          MinimumLeftChi2 = Chi2;
00331          result.LeftSlope = BestSlope;
00332       }
00333    }   // end of left-hand side loop
00334 
00335    result.Chi2 = MinimumLeftChi2 + MinimumRightChi2;
00336 
00337    return result;
00338 }
00339 //---------------------------------------------------------------------------
00340 double HBHEPulseShapeFlagSetter::PerformNominalFit(const vector<double> &Charge)
00341 {
00342    //
00343    // Performs a fit to the ideal pulse shape.  Returns best chi2
00344    //
00345    // A scan over different timing offset (for the ideal pulse) is carried out,
00346    //    and for each offset setting a one-parameter fit is performed
00347    //
00348 
00349    int DigiSize = Charge.size();
00350 
00351    double MinimumChi2 = 100000;
00352 
00353    double F = 0;
00354 
00355    double SumF2 = 0;
00356    double SumTF = 0;
00357    double SumT2 = 0;
00358 
00359    for(int i = 0; i + 250 < (int)CumulativeIdealPulse.size(); i++)
00360    {
00361       if(CumulativeIdealPulse[i+250] - CumulativeIdealPulse[i] < 1e-5)
00362          continue;
00363 
00364       SumF2 = 0;
00365       SumTF = 0;
00366       SumT2 = 0;
00367 
00368       double ErrorTemp=0;
00369       for(int j = 0; j < DigiSize; j++)
00370         {
00371           // get ideal pulse component for this time slice....
00372           F = CumulativeIdealPulse[i+j*25+25] - CumulativeIdealPulse[i+j*25];
00373           
00374           ErrorTemp=Charge[j];
00375           if (ErrorTemp<1) // protection against small charges
00376             ErrorTemp=1;
00377           // ...and increment various summations
00378           SumF2 += F * F / ErrorTemp;
00379           SumTF += F * Charge[j] / ErrorTemp;
00380           SumT2 += fabs(Charge[j]);
00381         }
00382       
00383       /* 
00384          chi2= sum((Charge[j]-aF)^2/|Charge[j]|
00385          ( |Charge[j]| = assumed sigma^2 for Charge[j]; a bit wonky for Charge[j]<1 )
00386          chi2 = sum(|Charge[j]|) - 2*sum(aF*Charge[j]/|Charge[j]|) +sum( a^2*F^2/|Charge[j]|)
00387          chi2 minimimized when d(chi2)/da = 0:
00388          a = sum(F*Charge[j])/sum(F^2)
00389          ...
00390          chi2= sum(|Q[j]|) - sum(Q[j]/|Q[j]|*F)*sum(Q[j]/|Q[j]|*F)/sum(F^2/|Q[j]|), where Q = Charge
00391          chi2 = SumT2 - SumTF*SumTF/SumF2
00392       */
00393       
00394       double Chi2 = SumT2 - SumTF * SumTF / SumF2;
00395       
00396       if(Chi2 < MinimumChi2)
00397         MinimumChi2 = Chi2;
00398    }
00399    
00400    // safety protection in case of perfect fit - don't want the log(...) to explode
00401    if(MinimumChi2 < 1e-5)
00402       MinimumChi2 = 1e-5;
00403 
00404    return MinimumChi2;
00405 }
00406 //---------------------------------------------------------------------------
00407 double HBHEPulseShapeFlagSetter::PerformDualNominalFit(const vector<double> &Charge)
00408 {
00409    //
00410    // Perform dual nominal fit and returns the chi2
00411    // 
00412    // In this function we do a scan over possible "distance" (number of time slices between two components)
00413    //    and overall offset for the two components; first coarse, then finer
00414    // All the fitting is done in the DualNominalFitSingleTry function
00415    //
00416 
00417    double OverallMinimumChi2 = 1000000;
00418 
00419    int AvailableDistance[] = {-100, -75, -50, 50, 75, 100};
00420 
00421    // loop over possible pulse distances between two components
00422    for(int k = 0; k < 6; k++)
00423    {
00424       double SingleMinimumChi2 = 1000000;
00425       int MinOffset = 0;
00426 
00427       // scan coarsely through different offsets and find the minimum
00428       for(int i = 0; i + 250 < (int)CumulativeIdealPulse.size(); i += 10)
00429       {
00430          double Chi2 = DualNominalFitSingleTry(Charge, i, AvailableDistance[k]);
00431 
00432          if(Chi2 < SingleMinimumChi2)
00433          {
00434             SingleMinimumChi2 = Chi2;
00435             MinOffset = i;
00436          }
00437       }
00438 
00439       // around the minimum, scan finer for better a better minimum
00440       for(int i = MinOffset - 15; i + 250 < (int)CumulativeIdealPulse.size() && i < MinOffset + 15; i++)
00441       {
00442          double Chi2 = DualNominalFitSingleTry(Charge, i, AvailableDistance[k]);
00443          if(Chi2 < SingleMinimumChi2)
00444             SingleMinimumChi2 = Chi2;
00445       }
00446 
00447       // update overall minimum chi2
00448       if(SingleMinimumChi2 < OverallMinimumChi2)
00449          OverallMinimumChi2 = SingleMinimumChi2;
00450    }
00451 
00452    return OverallMinimumChi2;
00453 }
00454 //---------------------------------------------------------------------------
00455 double HBHEPulseShapeFlagSetter::DualNominalFitSingleTry(const vector<double> &Charge, int Offset, int Distance)
00456 {
00457    //
00458    // Does a fit to dual signal pulse hypothesis given offset and distance of the two target pulses
00459    //
00460    // The only parameters to fit here are the two pulse heights of in-time and out-of-time components
00461    //    since offset is given
00462    // The calculation here is based from writing down the Chi2 formula and minimize against the two parameters,
00463    //    ie., Chi2 = Sum{((T[i] - a1 * F1[i] - a2 * F2[i]) / (Sigma[i]))^2}, where T[i] is the input pulse shape,
00464    //    and F1[i], F2[i] are the two ideal pulse components
00465    //
00466 
00467    int DigiSize = Charge.size();
00468 
00469    if(Offset < 0 || Offset + 250 >= (int)CumulativeIdealPulse.size())
00470       return 1000000;
00471    if(CumulativeIdealPulse[Offset+250] - CumulativeIdealPulse[Offset] < 1e-5)
00472       return 1000000;
00473 
00474    static vector<double> F1;
00475    static vector<double> F2;
00476 
00477    F1.resize(DigiSize);
00478    F2.resize(DigiSize);
00479 
00480    double SumF1F1 = 0;
00481    double SumF1F2 = 0;
00482    double SumF2F2 = 0;
00483    double SumTF1 = 0;
00484    double SumTF2 = 0;
00485 
00486    double Error = 0;
00487 
00488    for(int j = 0; j < DigiSize; j++)
00489    {
00490       // this is the TS value for in-time component - no problem we can do a subtraction directly
00491       F1[j] = CumulativeIdealPulse[Offset+j*25+25] - CumulativeIdealPulse[Offset+j*25];
00492 
00493       // However for the out-of-time component the index might go out-of-bound.
00494       // Let's protect against this.
00495 
00496       int OffsetTemp = Offset + j * 25 + Distance;
00497       
00498       double C1 = 0;   // lower-indexed value in the cumulative pulse shape
00499       double C2 = 0;   // higher-indexed value in the cumulative pulse shape
00500       
00501       if(OffsetTemp + 25 < (int)CumulativeIdealPulse.size() && OffsetTemp + 25 >= 0)
00502          C1 = CumulativeIdealPulse[OffsetTemp+25];
00503       if(OffsetTemp + 25 >= (int)CumulativeIdealPulse.size())
00504          C1 = CumulativeIdealPulse[CumulativeIdealPulse.size()-1];
00505       if(OffsetTemp < (int)CumulativeIdealPulse.size() && OffsetTemp >= 0)
00506          C2 = CumulativeIdealPulse[OffsetTemp];
00507       if(OffsetTemp >= (int)CumulativeIdealPulse.size())
00508          C2 = CumulativeIdealPulse[CumulativeIdealPulse.size()-1];
00509       F2[j] = C1 - C2;
00510 
00511       Error = Charge[j];
00512       if(Error < 1)
00513          Error = 1;
00514 
00515       SumF1F1 += F1[j] * F1[j] / Error;
00516       SumF1F2 += F1[j] * F2[j] / Error; 
00517       SumF2F2 += F2[j] * F2[j] / Error;
00518       SumTF1  += F1[j] * Charge[j] / Error; 
00519       SumTF2  += F2[j] * Charge[j] / Error; 
00520    }
00521 
00522    double Height  = 0;
00523    double Height2 = 0;
00524      if (fabs(SumF1F2*SumF1F2-SumF1F1*SumF2F2)>1e-5)
00525        {
00526          Height  = (SumF1F2 * SumTF2 - SumF2F2 * SumTF1) / (SumF1F2 * SumF1F2 - SumF1F1 * SumF2F2);
00527          Height2 = (SumF1F2 * SumTF1 - SumF1F1 * SumTF2) / (SumF1F2 * SumF1F2 - SumF1F1 * SumF2F2);
00528        }
00529 
00530    double Chi2 = 0;
00531    for(int j = 0; j < DigiSize; j++)
00532    {
00533       double Error = Charge[j];
00534       if(Error < 1)
00535          Error = 1;
00536 
00537       double Residual = Height * F1[j] + Height2 * F2[j] - Charge[j];  
00538       Chi2 += Residual * Residual / Error;                             
00539    } 
00540 
00541    // Safety protection in case of zero
00542    if(Chi2 < 1e-5)
00543       Chi2 = 1e-5;
00544 
00545    return Chi2;
00546 }
00547 //---------------------------------------------------------------------------
00548 double HBHEPulseShapeFlagSetter::CalculateRMS8Max(const vector<double> &Charge)
00549 {
00550    //
00551    // CalculateRMS8Max
00552    //
00553    // returns "RMS" divided by the largest charge in the time slices
00554    //    "RMS" is calculated using all but the two largest time slices.
00555    //    The "RMS" is not quite the actual RMS (see note below), but the value is only
00556    //    used for determining max values, and is not quoted as the actual RMS anywhere.
00557    //
00558 
00559    int DigiSize = Charge.size();
00560 
00561    if (DigiSize<=2)  return 1e-5;  // default statement when DigiSize is too small for useful RMS calculation
00562   // Copy Charge vector again, since we are passing references around
00563    vector<double> TempCharge = Charge;
00564 
00565    // Sort TempCharge vector from smallest to largest charge
00566    sort(TempCharge.begin(), TempCharge.end());
00567 
00568    double Total = 0;
00569    double Total2 = 0;
00570    for(int i = 0; i < DigiSize - 2; i++)
00571    {
00572       Total = Total + TempCharge[i];
00573       Total2 = Total2 + TempCharge[i] * TempCharge[i];
00574    }
00575 
00576    // This isn't quite the RMS (both Total2 and Total*Total need to be
00577    // divided by an extra (DigiSize-2) within the sqrt to get the RMS.)
00578    // We're only using this value for relative comparisons, though; we
00579    // aren't explicitly interpreting it as the RMS.  It might be nice
00580    // to either change the calculation or rename the variable in the future, though.
00581 
00582    double RMS = sqrt(Total2 - Total * Total / (DigiSize - 2));
00583 
00584    double RMS8Max = RMS / TempCharge[DigiSize-1];
00585    if(RMS8Max < 1e-5)   // protection against zero
00586       RMS8Max = 1e-5;
00587 
00588    return RMS / TempCharge[DigiSize-1];
00589 }
00590 //---------------------------------------------------------------------------
00591 double HBHEPulseShapeFlagSetter::PerformLinearFit(const vector<double> &Charge)
00592 {
00593    //
00594    // Performs a straight-line fit over all time slices, and returns the chi2 value
00595    //
00596    // The calculation here is based from writing down the formula for chi2 and minimize
00597    //    with respect to the parameters in the fit, ie., slope and intercept of the straight line
00598    // By doing two differentiation, we will get two equations, and the best parameters are determined by these
00599    //
00600 
00601    int DigiSize = Charge.size();
00602 
00603    double SumTS2OverTi = 0;
00604    double SumTSOverTi = 0;
00605    double SumOverTi = 0;
00606    double SumTiTS = 0;
00607    double SumTi = 0;
00608 
00609    double Error2 = 0;
00610    for(int i = 0; i < DigiSize; i++)
00611    {
00612       Error2 = Charge[i];
00613       if(Charge[i] < 1)
00614          Error2 = 1;
00615 
00616       SumTS2OverTi += 1.* i * i / Error2;
00617       SumTSOverTi  += 1.* i / Error2;
00618       SumOverTi    += 1. / Error2;
00619       SumTiTS      += Charge[i] * i / Error2;
00620       SumTi        += Charge[i] / Error2;
00621    }
00622 
00623    double CM1 = SumTS2OverTi;   // Coefficient in front of slope in equation 1
00624    double CM2 = SumTSOverTi;   // Coefficient in front of slope in equation 2
00625    double CD1 = SumTSOverTi;   // Coefficient in front of intercept in equation 1
00626    double CD2 = SumOverTi;   // Coefficient in front of intercept in equation 2
00627    double C1 = SumTiTS;   // Constant coefficient in equation 1
00628    double C2 = SumTi;   // Constant coefficient in equation 2
00629 
00630    // Denominators always non-zero by construction
00631    double Slope = (C1 * CD2 - C2 * CD1) / (CM1 * CD2 - CM2 * CD1);
00632    double Intercept = (C1 * CM2 - C2 * CM1) / (CD1 * CM2 - CD2 * CM1);
00633 
00634    // now that the best parameters are found, calculate chi2 from those
00635    double Chi2 = 0;
00636    for(int i = 0; i < DigiSize; i++)
00637    {
00638       double Deviation = Slope * i + Intercept - Charge[i];
00639       double Error2 = Charge[i];
00640       if(Charge[i] < 1)
00641          Error2 = 1;
00642       Chi2 += Deviation * Deviation / Error2;  
00643    }
00644 
00645    // safety protection in case of perfect fit
00646    if(Chi2 < 1e-5)
00647       Chi2 = 1e-5;
00648 
00649    return Chi2;
00650 }
00651 //---------------------------------------------------------------------------
00652 bool HBHEPulseShapeFlagSetter::CheckPassFilter(double Charge,
00653                                                double Discriminant,
00654                                                vector<pair<double, double> > &Cuts, 
00655                                                int Side)
00656 {
00657    //
00658    // Checks whether Discriminant value passes Cuts for the specified Charge.  True if pulse is good.
00659    //
00660    // The "Cuts" pairs are assumed to be sorted in terms of size from small to large,
00661    //    where each "pair" = (Charge, Discriminant)
00662    // "Side" is either positive or negative, which determines whether to discard the pulse if discriminant
00663    //    is greater or smaller than the cut value
00664    //
00665 
00666    if(Cuts.size() == 0)   // safety check that there are some cuts defined
00667       return true;
00668 
00669    if(Charge <= Cuts[0].first)   // too small to cut on
00670       return true;
00671 
00672    int IndexLargerThanCharge = -1;   // find the range it is falling in
00673    for(int i = 1; i < (int)Cuts.size(); i++)
00674    {
00675       if(Cuts[i].first > Charge)
00676       {
00677          IndexLargerThanCharge = i;
00678          break;
00679       }
00680    }
00681 
00682    double limit = 1000000;
00683 
00684    if(IndexLargerThanCharge == -1)   // if charge is greater than the last entry, assume flat line
00685       limit = Cuts[Cuts.size()-1].second;
00686    else   // otherwise, do a linear interpolation to find the cut position
00687    {
00688       double C1 = Cuts[IndexLargerThanCharge].first;
00689       double C2 = Cuts[IndexLargerThanCharge-1].first;
00690       double L1 = Cuts[IndexLargerThanCharge].second;
00691       double L2 = Cuts[IndexLargerThanCharge-1].second;
00692 
00693       limit = (Charge - C1) / (C2 - C1) * (L2 - L1) + L1;
00694    }
00695 
00696    if(Side > 0 && Discriminant > limit)
00697       return false;
00698    if(Side < 0 && Discriminant < limit)
00699       return false;
00700 
00701    return true;
00702 }
00703 //---------------------------------------------------------------------------
00704 
00705