File indexing completed on 2024-04-06 12:25:50
0001 #include <climits>
0002 #include <cmath>
0003 #include <iostream>
0004 #include <memory>
0005
0006 #include "RecoLocalCalo/HcalRecAlgos/interface/PulseShapeFitOOTPileupCorrection.h"
0007 #include "FWCore/Utilities/interface/isFinite.h"
0008 #include "CalibCalorimetry/HcalAlgos/interface/HcalTimeSlew.h"
0009
0010 PulseShapeFitOOTPileupCorrection::PulseShapeFitOOTPileupCorrection()
0011 : cntsetPulseShape(0),
0012 psfPtr_(nullptr),
0013 spfunctor_(nullptr),
0014 dpfunctor_(nullptr),
0015 tpfunctor_(nullptr),
0016 TSMin_(0),
0017 TSMax_(0),
0018 vts4Chi2_(0),
0019 pedestalConstraint_(false),
0020 timeConstraint_(false),
0021 addPulseJitter_(false),
0022 applyTimeSlew_(false),
0023 ts4Min_(0),
0024 vts4Max_(0),
0025 pulseJitter_(0),
0026 timeMean_(0),
0027 timeSig_(0),
0028 pedMean_(0) {
0029 hybridfitter = new PSFitter::HybridMinimizer(ROOT::Minuit2::kMigrad);
0030 iniTimesArr = {{-100, -75, -50, -25, 0, 25, 50, 75, 100, 125}};
0031 }
0032
0033 PulseShapeFitOOTPileupCorrection::~PulseShapeFitOOTPileupCorrection() {
0034 if (hybridfitter)
0035 delete hybridfitter;
0036 }
0037
0038 void PulseShapeFitOOTPileupCorrection::setPUParams(bool iPedestalConstraint,
0039 bool iTimeConstraint,
0040 bool iAddPulseJitter,
0041 bool iApplyTimeSlew,
0042 double iTS4Min,
0043 const std::vector<double> &iTS4Max,
0044 double iPulseJitter,
0045 double iTimeMean,
0046 double iTimeSigHPD,
0047 double iTimeSigSiPM,
0048 double iPedMean,
0049 double iTMin,
0050 double iTMax,
0051 const std::vector<double> &its4Chi2,
0052 HcalTimeSlew::BiasSetting slewFlavor,
0053 int iFitTimes) {
0054 TSMin_ = iTMin;
0055 TSMax_ = iTMax;
0056
0057 vts4Chi2_ = its4Chi2;
0058 pedestalConstraint_ = iPedestalConstraint;
0059 timeConstraint_ = iTimeConstraint;
0060 addPulseJitter_ = iAddPulseJitter;
0061 applyTimeSlew_ = iApplyTimeSlew;
0062 ts4Min_ = iTS4Min;
0063
0064 vts4Max_ = iTS4Max;
0065 pulseJitter_ = iPulseJitter * iPulseJitter;
0066 timeMean_ = iTimeMean;
0067
0068 timeSigHPD_ = iTimeSigHPD;
0069 timeSigSiPM_ = iTimeSigSiPM;
0070 pedMean_ = iPedMean;
0071 slewFlavor_ = slewFlavor;
0072 fitTimes_ = iFitTimes;
0073 }
0074
0075 void PulseShapeFitOOTPileupCorrection::setPulseShapeTemplate(const HcalPulseShapes::Shape &ps,
0076 bool isHPD,
0077 unsigned nSamples,
0078 const HcalTimeSlew *hcalTimeSlewDelay) {
0079
0080
0081 if (!(&ps == currentPulseShape_ && isHPD == isCurrentChannelHPD_)) {
0082 resetPulseShapeTemplate(ps, nSamples);
0083
0084
0085 if (!isHPD)
0086 psfPtr_->setinverttimeSig2(1. / (timeSigSiPM_ * timeSigSiPM_));
0087 else
0088 psfPtr_->setinverttimeSig2(1. / (timeSigHPD_ * timeSigHPD_));
0089
0090 currentPulseShape_ = &ps;
0091 isCurrentChannelHPD_ = isHPD;
0092
0093 hcalTimeSlewDelay_ = hcalTimeSlewDelay;
0094 tsDelay1GeV_ = hcalTimeSlewDelay->delay(1.0, slewFlavor_);
0095 }
0096 }
0097
0098 void PulseShapeFitOOTPileupCorrection::resetPulseShapeTemplate(const HcalPulseShapes::Shape &ps, unsigned nSamples) {
0099 ++cntsetPulseShape;
0100 psfPtr_ = std::make_unique<FitterFuncs::PulseShapeFunctor>(
0101 ps, pedestalConstraint_, timeConstraint_, addPulseJitter_, pulseJitter_, timeMean_, pedMean_, nSamples);
0102 spfunctor_ =
0103 std::make_unique<ROOT::Math::Functor>(psfPtr_.get(), &FitterFuncs::PulseShapeFunctor::singlePulseShapeFunc, 3);
0104 dpfunctor_ =
0105 std::make_unique<ROOT::Math::Functor>(psfPtr_.get(), &FitterFuncs::PulseShapeFunctor::doublePulseShapeFunc, 5);
0106 tpfunctor_ =
0107 std::make_unique<ROOT::Math::Functor>(psfPtr_.get(), &FitterFuncs::PulseShapeFunctor::triplePulseShapeFunc, 7);
0108 }
0109
0110 constexpr char const *varNames[] = {"time", "energy", "time1", "energy1", "time2", "energy2", "ped"};
0111
0112 int PulseShapeFitOOTPileupCorrection::pulseShapeFit(const double *energyArr,
0113 const double *pedenArr,
0114 const double *chargeArr,
0115 const double *pedArr,
0116 const double *gainArr,
0117 const double tsTOTen,
0118 std::vector<float> &fitParsVec,
0119 const double *noiseArrSq,
0120 unsigned int soi) const {
0121 double tsMAX = 0;
0122 double tmpx[hcal::constants::maxSamples], tmpy[hcal::constants::maxSamples], tmperry[hcal::constants::maxSamples],
0123 tmperry2[hcal::constants::maxSamples], tmpslew[hcal::constants::maxSamples];
0124 double tstrig = 0;
0125 for (unsigned int i = 0; i < hcal::constants::maxSamples; ++i) {
0126 tmpx[i] = i;
0127 tmpy[i] = energyArr[i] - pedenArr[i];
0128
0129 tmpslew[i] = 0;
0130 if (applyTimeSlew_) {
0131 if (chargeArr[i] <= 1.0)
0132 tmpslew[i] = tsDelay1GeV_;
0133 else
0134 tmpslew[i] = hcalTimeSlewDelay_->delay(chargeArr[i], slewFlavor_);
0135 }
0136
0137
0138 tmperry2[i] = noiseArrSq[i];
0139
0140
0141 tmperry2[i] *= (gainArr[i] * gainArr[i]);
0142 tmperry[i] = sqrt(tmperry2[i]);
0143
0144 if (std::abs(energyArr[i]) > tsMAX)
0145 tsMAX = std::abs(tmpy[i]);
0146 if (i == soi || i == (soi + 1)) {
0147 tstrig += chargeArr[i] - pedArr[i];
0148 }
0149 }
0150 psfPtr_->setpsFitx(tmpx);
0151 psfPtr_->setpsFity(tmpy);
0152 psfPtr_->setpsFiterry(tmperry);
0153 psfPtr_->setpsFiterry2(tmperry2);
0154 psfPtr_->setpsFitslew(tmpslew);
0155
0156
0157 float timevalfit = 0;
0158 float chargevalfit = 0;
0159 float pedvalfit = 0;
0160 float chi2 = 999;
0161 bool fitStatus = false;
0162 bool useTriple = false;
0163
0164 unsigned BX[3] = {soi, soi + 1, soi - 1};
0165 if (ts4Chi2_ != 0)
0166 fit(1, timevalfit, chargevalfit, pedvalfit, chi2, fitStatus, tsMAX, tsTOTen, tmpy, BX);
0167
0168 if (tmpy[soi - 2] > 3. * tmpy[soi - 1])
0169 BX[2] = soi - 2;
0170
0171 if (chi2 > ts4Chi2_ && tstrig < ts4Max_) {
0172 fit(3, timevalfit, chargevalfit, pedvalfit, chi2, fitStatus, tsMAX, tsTOTen, tmpy, BX);
0173 useTriple = true;
0174 }
0175
0176 timevalfit -= (int(soi) - hcal::constants::shiftTS) * hcal::constants::nsPerBX;
0177
0178
0179
0180
0181
0182
0183
0184
0185
0186 int outfitStatus = (fitStatus ? 1 : 0);
0187 fitParsVec.clear();
0188 fitParsVec.push_back(chargevalfit);
0189 fitParsVec.push_back(timevalfit);
0190 fitParsVec.push_back(pedvalfit);
0191 fitParsVec.push_back(chi2);
0192 fitParsVec.push_back(useTriple);
0193 return outfitStatus;
0194 }
0195
0196 void PulseShapeFitOOTPileupCorrection::fit(int iFit,
0197 float &timevalfit,
0198 float &chargevalfit,
0199 float &pedvalfit,
0200 float &chi2,
0201 bool &fitStatus,
0202 double &iTSMax,
0203 const double &iTSTOTEn,
0204 double *iEnArr,
0205 unsigned (&iBX)[3]) const {
0206 int n = 3;
0207 if (iFit == 2)
0208 n = 5;
0209 if (iFit == 3)
0210 n = 7;
0211
0212 float pedMax = iTSMax;
0213 float tMin = TSMin_;
0214 float tMax = TSMax_;
0215
0216 if (pedMax < 1.)
0217 pedMax = 1.;
0218
0219 double vstart[n];
0220 for (int i = 0; i < int((n - 1) / 2); i++) {
0221 vstart[2 * i + 0] = iniTimesArr[iBX[i]] + timeMean_;
0222 vstart[2 * i + 1] = iEnArr[iBX[i]];
0223 }
0224 vstart[n - 1] = pedMean_;
0225
0226 double step[n];
0227 for (int i = 0; i < n; i++)
0228 step[i] = 0.1;
0229
0230 if (iFit == 1)
0231 hybridfitter->SetFunction(*spfunctor_);
0232 if (iFit == 2)
0233 hybridfitter->SetFunction(*dpfunctor_);
0234 if (iFit == 3)
0235 hybridfitter->SetFunction(*tpfunctor_);
0236 hybridfitter->Clear();
0237
0238 for (int i = 0; i < int((n - 1) / 2); i++) {
0239 hybridfitter->SetLimitedVariable(0 + i * 2,
0240 varNames[2 * i + 0],
0241 vstart[0 + i * 2],
0242 step[0 + i * 2],
0243 iniTimesArr[iBX[i]] + tMin,
0244 iniTimesArr[iBX[i]] + tMax);
0245 hybridfitter->SetLimitedVariable(
0246 1 + i * 2, varNames[2 * i + 1], vstart[1 + i * 2], step[1 + i * 2], 0, 1.2 * iTSTOTEn);
0247
0248 if (timeSig_ < 0)
0249 hybridfitter->SetFixedVariable(0 + i * 2, varNames[2 * i + 0], vstart[0 + i * 2]);
0250 }
0251
0252 if (vstart[n - 1] > std::abs(pedMax))
0253 vstart[n - 1] = pedMax;
0254 hybridfitter->SetLimitedVariable(n - 1, varNames[n - 1], vstart[n - 1], step[n - 1], -pedMax, pedMax);
0255
0256 if (pedSig_ < 0)
0257 hybridfitter->SetFixedVariable(n - 1, varNames[n - 1], vstart[n - 1]);
0258
0259 chi2 = -1;
0260
0261 const double *results = nullptr;
0262 for (int tries = 0; tries <= 3; ++tries) {
0263 if (fitTimes_ != 2 || tries != 1) {
0264 hybridfitter->SetMinimizerType(ROOT::Minuit2::kMigrad);
0265 fitStatus = hybridfitter->Minimize();
0266 }
0267 double chi2valfit = hybridfitter->MinValue();
0268 const double *newresults = hybridfitter->X();
0269 if (chi2 == -1 || chi2 > chi2valfit + 0.01) {
0270 results = newresults;
0271 chi2 = chi2valfit;
0272 if (tries == 0 && fitTimes_ == 1)
0273 break;
0274 if (tries == 1 && (fitTimes_ == 2 || fitTimes_ == 3))
0275 break;
0276 if (tries == 2 && fitTimes_ == 4)
0277 break;
0278 if (tries == 3 && fitTimes_ == 5)
0279 break;
0280
0281 if (timeSig_ < 0 || pedSig_ < 0)
0282 break;
0283 if (tries == 0) {
0284 hybridfitter->SetMinimizerType(ROOT::Minuit2::kScan);
0285 fitStatus = hybridfitter->Minimize();
0286 } else if (tries == 1) {
0287 hybridfitter->SetStrategy(1);
0288 } else if (tries == 2) {
0289 hybridfitter->SetStrategy(2);
0290 }
0291 } else {
0292 break;
0293 }
0294 }
0295 assert(results);
0296
0297 timevalfit = results[0];
0298 chargevalfit = results[1];
0299 pedvalfit = results[n - 1];
0300 }
0301
0302 void PulseShapeFitOOTPileupCorrection::phase1Apply(const HBHEChannelInfo &channelData,
0303 float &reconstructedEnergy,
0304 float &reconstructedTime,
0305 bool &useTriple,
0306 float &chi2) const {
0307 psfPtr_->setDefaultcntNANinfit();
0308
0309 const unsigned cssize = channelData.nSamples();
0310 const unsigned int soi = channelData.soi();
0311
0312
0313 double chargeArr[hcal::constants::maxSamples] = {}, pedArr[hcal::constants::maxSamples] = {},
0314 gainArr[hcal::constants::maxSamples] = {};
0315 double energyArr[hcal::constants::maxSamples] = {}, pedenArr[hcal::constants::maxSamples] = {};
0316 double noiseADCArr[hcal::constants::maxSamples] = {};
0317 double noiseArrSq[hcal::constants::maxSamples] = {};
0318 double noisePHArr[hcal::constants::maxSamples] = {};
0319 double tstrig = 0;
0320 double tsTOTen = 0;
0321
0322
0323 for (unsigned int ip = 0; ip < cssize; ++ip) {
0324 if (ip >= (unsigned)hcal::constants::maxSamples)
0325 continue;
0326
0327
0328 double charge = channelData.tsRawCharge(ip);
0329 double ped = channelData.tsPedestal(ip);
0330 double gain = channelData.tsGain(ip);
0331
0332 double energy = charge * gain;
0333 double peden = ped * gain;
0334
0335 chargeArr[ip] = charge;
0336 pedArr[ip] = ped;
0337 gainArr[ip] = gain;
0338 energyArr[ip] = energy;
0339 pedenArr[ip] = peden;
0340
0341
0342 noiseADCArr[ip] = (1. / sqrt(12)) * channelData.tsDFcPerADC(ip);
0343
0344
0345
0346
0347
0348 noisePHArr[ip] = 0;
0349 if ((charge - ped) > channelData.tsPedestalWidth(ip)) {
0350 noisePHArr[ip] = sqrt((charge - ped) * channelData.fcByPE());
0351 }
0352
0353
0354 noiseArrSq[ip] = noiseADCArr[ip] * noiseADCArr[ip] +
0355 channelData.tsPedestalWidth(ip) * channelData.tsPedestalWidth(ip) +
0356 noisePHArr[ip] * noisePHArr[ip];
0357
0358 tsTOTen += energy - peden;
0359 if (ip == soi || ip == soi + 1) {
0360 tstrig += charge - ped;
0361 }
0362 }
0363
0364 double averagePedSig2GeV =
0365 0.25 *
0366 (channelData.tsPedestalWidth(0) * channelData.tsPedestalWidth(0) * channelData.tsGain(0) * channelData.tsGain(0) +
0367 channelData.tsPedestalWidth(1) * channelData.tsPedestalWidth(1) * channelData.tsGain(1) * channelData.tsGain(1) +
0368 channelData.tsPedestalWidth(2) * channelData.tsPedestalWidth(2) * channelData.tsGain(2) * channelData.tsGain(2) +
0369 channelData.tsPedestalWidth(3) * channelData.tsPedestalWidth(3) * channelData.tsGain(3) * channelData.tsGain(3));
0370
0371
0372 psfPtr_->setinvertpedSig2(1. / (averagePedSig2GeV));
0373
0374 if (channelData.hasTimeInfo()) {
0375 ts4Chi2_ = vts4Chi2_[1];
0376 if (channelData.id().depth() == 1)
0377 ts4Max_ = vts4Max_[1];
0378 else
0379 ts4Max_ = vts4Max_[2];
0380
0381 } else {
0382 ts4Max_ = vts4Max_[0];
0383 ts4Chi2_ = vts4Chi2_[0];
0384 }
0385
0386 std::vector<float> fitParsVec;
0387 if (tstrig >= ts4Min_ && tsTOTen > 0.) {
0388 pulseShapeFit(energyArr, pedenArr, chargeArr, pedArr, gainArr, tsTOTen, fitParsVec, noiseArrSq, channelData.soi());
0389 } else {
0390 fitParsVec.clear();
0391 fitParsVec.push_back(0.);
0392 fitParsVec.push_back(-9999);
0393 fitParsVec.push_back(0.);
0394 fitParsVec.push_back(-9999);
0395 fitParsVec.push_back(false);
0396 }
0397
0398 reconstructedEnergy = fitParsVec[0];
0399 reconstructedTime = fitParsVec[1];
0400 chi2 = fitParsVec[3];
0401 useTriple = fitParsVec[4];
0402 }