CMS 3D CMS Logo

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