13 #include "art_root_io/TFileService.h" 43 std::size_t iGaus = 0;
49 auto* pF =
new TF1(func_name.c_str(), formula.c_str());
50 pF->SetParName(iGaus * 3,
"baseline");
60 void findPeakParameters(
const std::vector<float>&,
95 void SetFitParameters(TF1& Gaus,
97 const unsigned int nGaus,
99 const float startTime,
100 const float roiSize)
const;
102 void GetFitParameters(
const TF1& Gaus,
104 const unsigned int nGaus,
105 const float startTime,
110 const TF1& fittedGaus,
111 const std::vector<float>& waveform,
117 std::pair<ICandidateHitFinder::HitCandidate, PeakFitParams_t> FindShiftedGaussian(
125 : fMinWidth(pset.
get<double>(
"MinWidth", 0.5))
126 , fMaxWidthMult(pset.
get<double>(
"MaxWidthMult", 3.))
127 , fPeakRange(pset.
get<double>(
"PeakRangeFact", 2.))
128 , fAmpRange(pset.
get<double>(
"PeakAmpRange", 2.))
129 , fFloatBaseline(pset.
get<bool>(
"FloatBaseline", false))
130 , fOutputHistograms(pset.
get<bool>(
"OutputHistograms", false))
131 , fRefit(pset.
get<bool>(
"Refit"))
132 , fRefitThreshold(pset.
get<double>(
"RefitThreshold"))
133 , fRefitImprovement(pset.
get<double>(
"RefitImprovement"))
135 fHistogram = TH1F(
"PeakFitterHitSignal",
"", 500, 0., 500.);
139 std::string
function =
"Gaus(0)";
148 art::TFileDirectory
dir = tfs->mkdir(
"PeakFit");
150 fNumCandHitsHist = dir.make<TH1F>(
"NumCandHits",
"# Candidate Hits", 100, 0., 100.);
151 fROISizeHist = dir.make<TH1F>(
"ROISize",
"ROI Size", 400, 0., 400.);
153 fCandPeakWidHist = dir.make<TH1F>(
"CPeadWidth",
"Peak Width", 100, 0., 25.);
157 fFitPeakWidHist = dir.make<TH1F>(
"FPeadWidth",
"Peak Width", 100, 0., 25.);
167 const std::vector<float>& roiSignalVec,
179 if (hitCandidateVec.empty())
return;
182 chi2PerNDF = std::numeric_limits<double>::infinity();
184 int startTime = hitCandidateVec.front().startTick;
185 int endTime = hitCandidateVec.back().stopTick;
186 int roiSize = endTime - startTime;
190 std::string histName =
"PeakFitterHitSignal_" +
std::to_string(roiSize);
191 fHistogram = TH1F(histName.c_str(),
"", roiSize, 0., roiSize);
195 for (
int idx = 0; idx < roiSize; idx++)
196 fHistogram.SetBinContent(idx + 1, roiSignalVec[startTime + idx]);
199 unsigned int const nGaus = hitCandidateVec.size();
212 SetFitParameters(Gaus, hitCandidateVec, nGaus, baseline, startTime, roiSize);
217 fitResult =
fHistogram.Fit(&Gaus,
"QNWB",
"", 0., roiSize);
232 chi2PerNDF < std::numeric_limits<double>::infinity()) {
235 FindRefitCand(Gaus, roiSignalVec, startTime, roiSize, hitCandidateVec, peakParamsVec);
240 newHitCandidateVec.push_back(refitParams);
243 unsigned int const nGausNew = nGaus + 1;
247 SetFitParameters(Gaus, newHitCandidateVec, nGausNew, baseline, startTime, roiSize);
249 int newFitResult{-1};
251 newFitResult =
fHistogram.Fit(&Gaus,
"QNWB",
"", 0., roiSize);
259 const float newChi2PerNDF = Gaus.GetChisquare() / Gaus.GetNDF();
261 peakParamsVec.clear();
262 GetFitParameters(Gaus, peakParamsVec, nGausNew, startTime, chi2PerNDF, NDF);
276 const unsigned int nGaus,
277 const float baseline,
278 const float startTime,
279 const float roiSize)
const 282 Gaus.SetParameter(nGaus * 3, baseline);
283 Gaus.SetParLimits(nGaus * 3, baseline - 12., baseline + 12.);
286 Gaus.FixParameter(nGaus * 3, baseline);
289 for (
auto const& candidateHit : hitCandidateVec) {
290 double const peakMean = candidateHit.hitCenter - startTime;
291 double const peakWidth = candidateHit.hitSigma;
292 double const amplitude = candidateHit.hitHeight - baseline;
293 double const meanLowLim = std::max(peakMean -
fPeakRange * peakWidth, 0.);
294 double const meanHiLim = std::min(peakMean +
fPeakRange * peakWidth,
double(roiSize));
302 Gaus.SetParameter(parIdx, amplitude);
303 Gaus.SetParameter(1 + parIdx, peakMean);
304 Gaus.SetParameter(2 + parIdx, peakWidth);
305 Gaus.SetParLimits(parIdx, 0.1 * amplitude,
fAmpRange * amplitude);
306 Gaus.SetParLimits(1 + parIdx, meanLowLim, meanHiLim);
316 const unsigned int nGaus,
317 const float startTime,
321 chi2PerNDF = (Gaus.GetChisquare() / Gaus.GetNDF());
325 for (
size_t idx = 0; idx < nGaus; idx++) {
330 peakParams.
peakCenter = Gaus.GetParameter(parIdx + 1) + startTime;
332 peakParams.
peakSigma = Gaus.GetParameter(parIdx + 2);
341 peakParamsVec.emplace_back(peakParams);
348 const TF1& fittedGaus,
349 const std::vector<float>& waveform,
357 std::pair<ICandidateHitFinder::HitCandidate, PeakFitParams_t> shiftedHitCanPair =
361 float offset(shiftedHitCanPair.second.peakCenter - shiftedHitCanPair.first.hitCenter);
364 if (
std::abs(offset) > 1.
f && hitCandidateVec.size() == 1) {
366 offset = std::min(offset, roiSize / 8.
f);
370 std::min(shiftedHitCanPair.first.hitCenter + 4.f * offset,
371 (shiftedHitCanPair.first.hitCenter + shiftedHitCanPair.first.stopTick) / 2.f) :
372 std::max(shiftedHitCanPair.first.hitCenter + 4.f * offset,
373 (shiftedHitCanPair.first.hitCenter + shiftedHitCanPair.first.startTick) / 2.f));
381 float(candPos + startTime),
383 0.5f * waveform[candPos]};
387 int maxDiffPos(std::numeric_limits<int>::max());
389 for (
int i = 0; i < roiSize; i++) {
390 float diff(waveform[startTime + i] - fittedGaus.Eval(i));
393 if (diff > 0) diff *= 1.25;
398 float peakDist(std::numeric_limits<float>::max());
399 for (
auto const& hitCandidate : hitCandidateVec)
400 peakDist = std::min(peakDist,
std::abs(hitCandidate.hitCenter -
float(startTime) - i));
403 if (peakDist < 3)
continue;
405 diff *= std::log(peakDist);
413 if (maxDiffPos == std::numeric_limits<int>::max())
420 std::numeric_limits<float>::lowest(),
421 std::numeric_limits<float>::lowest(),
422 std::numeric_limits<float>::lowest()};
425 maxDiff = (waveform[startTime + maxDiffPos] - fittedGaus.Eval(maxDiffPos));
427 int lowLim(maxDiffPos);
428 int highLim(maxDiffPos);
429 const bool useMax(maxDiff > 0);
433 while (lowLim > 0 && waveform[startTime + lowLim] > fittedGaus.Eval(lowLim))
436 while (highLim < roiSize && waveform[startTime + highLim] > fittedGaus.Eval(highLim))
440 while (lowLim > 0 && waveform[startTime + lowLim] < fittedGaus.Eval(lowLim))
443 while (highLim < roiSize && waveform[startTime + highLim] < fittedGaus.Eval(highLim))
447 const float amplitude(std::max(
std::abs(2 * maxDiff), waveform[startTime + maxDiffPos] / 2.
f));
450 0, 0, 0, 0, 0, 0, float(maxDiffPos + startTime), 0.5f * (highLim - lowLim), amplitude};
453 std::pair<ICandidateHitFinder::HitCandidate, IPeakFitter::PeakFitParams_t>
458 float minDiff(std::numeric_limits<float>::max());
462 for (
auto const& hitCand : hitCandidateVec) {
463 for (
auto const& fittedPeak : fittedPeakVec) {
464 const float offset(hitCand.hitCenter - fittedPeak.peakCenter);
468 minPeak = &fittedPeak;
474 return std::pair<ICandidateHitFinder::HitCandidate, PeakFitParams_t>(*minHit, *minPeak);
Utilities related to art service access.
constexpr auto abs(T v)
Returns the absolute value of the argument.
A set of TF1 linear sum of base functions (Gaussians)
decltype(auto) constexpr to_string(T &&obj)
ADL-aware version of std::to_string.
virtual TF1 * Get(size_t nFunc)
Returns a function sum of nFunc base functions.
Description of geometry of one entire detector.
GausFitCache(std::string new_name="GausFitCache")
Constructor; optionally set the name of the repository.
Detector simulation of raw signals on wires.
decltype(auto) get(T &&obj)
ADL-aware version of std::to_string.
This provides an interface for tools which are tasked with fitting peaks on input waveforms...
virtual std::string FunctionName(size_t nFunc) const
Returns a name for the function with nFunc base functions.
MaybeLogger_< ELseverityLevel::ELsev_warning, false > LogWarning
Provide caches for TF1 functions to be used with ROOT fitters.
art framework interface to geometry description