CMS 3D CMS Logo

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