SdSignalRecovery.cc
Go to the documentation of this file.
1 
8 #include "SdSignalRecovery.h"
9 #include <evt/Event.h>
10 #include <sevt/SEvent.h>
11 #include <sevt/Header.h>
12 
13 #include <sevt/StationRecData.h>
14 #include <sevt/Station.h>
15 #include <sevt/PMTCalibData.h>
16 #include <sevt/PMTRecData.h>
17 
18 #include <sdet/SDetector.h>
19 #include <sdet/Station.h>
20 
21 #include <fwk/CentralConfig.h>
22 #include <fwk/RunController.h>
23 #include <utl/Reader.h>
24 #include <utl/PhysicalConstants.h>
25 #include <utl/PhysicalFunctions.h>
26 #include <utl/ErrorLogger.h>
27 #include <utl/Math.h>
28 #include <TMinuit.h>
29 #include <cmath>
30 
31 using namespace SdSignalRecoveryKLT;
32 using namespace sevt;
33 using namespace evt;
34 using namespace fwk;
35 using namespace std;
36 using namespace utl;
37 
38 
39 namespace SdSignalRecoveryKLT {
40 
41  const unsigned int kTraceLength = 768;
43 
44  // constants for the attenuation curve
45  const double kMaxMoyal = Moyal(0);
46  double kSignalLimits[] = { 3209.62, 8059.1, 16107.5, 32043.5, 63697 };
47  double kAlphaSat[] = { 1, 0.301299, 0.0720609, 0.0203313, 0.00631851, 0.00151839 };
48  double kBetaSat[] = { 0, 4485.13, 8180.03, 9846.53, 10744.6, 11420.6 };
49 
50  const int kMinCounts = 100;
51 
52 
53  inline
54  double
55  AmplitudeFunction(const double i1, const double i2, const double sigma, const double x)
56  {
57  const double sqrt2 = sqrt(2.);
58  const double sigma2 = 2*sigma;
59  return sqrt(2*kPi) * sigma *
60  (erf(exp((x - i1)/sigma2)/sqrt2) - erf(exp((x - i2)/sigma2)/sqrt2));
61  }
62 
63 
64  inline
65  double
66  SigmaParametrization(const double value)
67  {
68  return (value < 974.5) ? 2.17 - 8.908e-4 * value :
69  1.28 + value*(2.63e-5 - 4.808e-9 * value);
70  }
71 
72 
73  inline
74  double
75  X0Parametrization(const double value)
76  {
77  return
78  (value < 878) ? 4.645444 + value*(-0.0134195 + 0.0000105245 * value) :
79  2.1 * (1 - exp(-7.2e-4 * value)) + pow(value, -3.8);
80  }
81 
82 
83  inline
84  double
85  ErrorParametrization(const double x)
86  {
87  const double sigma_fit = 0.17*(1 - exp(-5.21*x)) + 0.068;
88  const double sigma_sys = 0.05*exp(2.325*x);
89  const double total_sigma = sqrt(Sqr(sigma_fit)
90  + Sqr(sigma_sys));
91  return (total_sigma < 0.10) ? 0.10 : total_sigma;
92  }
93 
94 
95  inline
96  double
97  NonLinearityUncertainty(const double x)
98  {
99  return 0.44*pow(x, 7.12) + 0.036;
100  }
101 
102 }
103 
104 
105 bool
107 {
108  if (Length(kSignalLimits) != nL.fSignalLimits.size() ||
109  Length(kAlphaSat) != nL.fAlpha.size() ||
110  Length(kBetaSat) != nL.fBeta.size())
111  return false;
112 
113  for (unsigned int i = 0; i < Length(kSignalLimits); ++i)
114  kSignalLimits[i] = nL.fSignalLimits[i];
115  for (unsigned int i = 0; i < Length(kAlphaSat); ++i)
116  kAlphaSat[i] = nL.fAlpha[i];
117  for (unsigned int i = 0; i < Length(kBetaSat); ++i)
118  kBetaSat[i] = nL.fBeta[i];
119  return true;
120 }
121 
122 
125 {
126  const Branch topB =
127  CentralConfig::GetInstance()->GetTopBranch("SdSignalRecovery");
128  topB.GetChild("undershoot").GetData(fRecoverUnsaturatedSignal);
129  const Branch nlBranch = topB.GetChild("nonLinearity");
130  if (nlBranch) {
131  nlBranch.GetChild("intervals").GetData(fNonLinearity.fSignalLimits);
132  nlBranch.GetChild("alpha").GetData(fNonLinearity.fAlpha);
133  nlBranch.GetChild("beta").GetData(fNonLinearity.fBeta);
134  }
135 
136  const Branch nlBranchPl = topB.GetChild("nonLinearityPlusSigma");
137  if (nlBranchPl) {
138  nlBranchPl.GetChild("intervals").GetData(fNonLinearityPlusSigma.fSignalLimits);
139  nlBranchPl.GetChild("alpha").GetData(fNonLinearityPlusSigma.fAlpha);
140  nlBranchPl.GetChild("beta").GetData(fNonLinearityPlusSigma.fBeta);
141  }
142 
143  const Branch nlBranchMi = topB.GetChild("nonLinearityMinusSigma");
144  if (nlBranchMi) {
145  nlBranchMi.GetChild("intervals").GetData(fNonLinearityMinusSigma.fSignalLimits);
146  nlBranchMi.GetChild("alpha").GetData(fNonLinearityMinusSigma.fAlpha);
147  nlBranchMi.GetChild("beta").GetData(fNonLinearityMinusSigma.fBeta);
148  }
149 
150  return eSuccess;
151 }
152 
153 
156 {
157  if (!event.HasSEvent())
158  return eContinueLoop;
159 
160  auto& sEvent = event.GetSEvent();
161 
162  ostringstream info;
163 
164  int nSaturatedStations = 0;
165  for (auto& station : sEvent.StationsRange()) {
166 
167  if (!station.HasRecData())
168  continue;
169 
170  const auto& dStation = det::Detector::GetInstance().GetSDetector().GetStation(station);
171 
172  /* Until a proedure for signal recovery is validated for AugerPrime (and
173  beyond) electronics/hardware, skip all stations with IsUUB != 0. */
174  if (dStation.IsUUB())
175  continue;
176 
177  const unsigned int saturationValue = dStation.GetSaturationValue();
178 
179  if (station.IsLowGainSaturation()) {
180 
181  ++nSaturatedStations;
182 
183  info << "\nRecover saturated station " << station.GetId() << ':';
184 
185  int noOfPMTs = 0;
186  double recoveredSignal = 0;
187  double signalError = 0;
188 
190  for (const auto& pmt : station.PMTsRange()) {
191 
192  if (!pmt.HasRecData() ||
193  !pmt.GetRecData().HasVEMTrace() ||
194  !pmt.GetCalibData().IsTubeOk() ||
195  !pmt.GetCalibData().IsLowGainOk())
196  continue;
197 
198  if (!pmt.GetRecData().GetFADCSaturatedBins(sdet::PMTConstants::eLowGain)) {
199  info << "\n PMT" << pmt.GetId() << " is not saturated";
200  recoveredSignal += pmt.GetRecData().GetTotalCharge();
201  signalError += Sqr(pmt.GetRecData().GetTotalChargeError());
202  ++noOfPMTs;
203  continue;
204  }
205 
206  SetNonLinearityCurve(fNonLinearity);
207  if (!RecoverSignal(pmt, sbest, saturationValue)) {
208  info << "\n PMT" << pmt.GetId() << " signal "
209  << pmt.GetRecData().GetTotalCharge() << " VEM not recovered";
210  } else {
211  SaturationFitInterface sPlusSigma;
212  if (SetNonLinearityCurve(fNonLinearityPlusSigma)) {
213  if (!RecoverSignal(pmt, sPlusSigma, saturationValue)) {
214  info << "\n PMT" << pmt.GetId() << " could not calculate nL signal+sigma";
215  sPlusSigma.fSignal = 0;
216  }
217  }
218 
219  SaturationFitInterface sMinusSigma;
220  if (SetNonLinearityCurve(fNonLinearityMinusSigma)) {
221  if (!RecoverSignal(pmt, sMinusSigma, saturationValue)) {
222  info << "\n PMT" << pmt.GetId() << " could not calculate nL signal-sigma";
223  sMinusSigma.fSignal = 0;
224  }
225  }
226  if (!sMinusSigma.fSignal || !sPlusSigma.fSignal) {
227  sbest.fSignalErr = sqrt(Sqr(sbest.fSignalErr) + Sqr(sbest.fSignalErrNL));
228  } else {
229  // cout <<" PMT " << pmt.GetId() << ": " << sbest.fSignal
230  // << " +- " << sbest.fSignalErr
231  // << " (- " << sMinusSigma.fSignal
232  // << " + "<< sPlusSigma.fSignal
233  // << " )" << endl;
234  sbest.fSignal = 0.5*(sMinusSigma.fSignal + sPlusSigma.fSignal);
235  const double uncert = fabs(sMinusSigma.fSignal - sbest.fSignal);
236  sbest.fSignalErr = sqrt(Sqr(sbest.fSignalErr) + Sqr(uncert));
237  info << "\n PMT" << pmt.GetId() << ": signal "
238  << pmt.GetRecData().GetTotalCharge() << " recovered to "
239  << sbest.fSignal << " +- " << sbest.fSignalErr << " VEM";
240  }
241  recoveredSignal += sbest.fSignal;
242  signalError += Sqr(sbest.fSignalErr);
243  ++noOfPMTs;
244  }
245 
246  }
247 
248  if (!noOfPMTs) {
249  info << "\n No PMTs!";
250  continue;
251  }
252 
253  recoveredSignal /= noOfPMTs;
254 
255  sevt::StationRecData& sRec = station.GetRecData();
256 
257  const double recoveredSignalError = sqrt(signalError) / noOfPMTs;
258  info << "\n Signal in station " << station.GetId()
259  << " successfully recovered from " << sRec.GetTotalSignal() << " to "
260  << recoveredSignal << " +- " << recoveredSignalError << " VEM";
261  sRec.SetRecoveredSignal(recoveredSignal, recoveredSignalError);
262  }
263 
264  } // end loop over stations
265 
266  const auto& mess = info.str();
267  if (!mess.empty())
268  INFO(mess);
269 
270  if (nSaturatedStations)
271  ++RunController::GetInstance().GetRunData().GetNamedCounters()["SdSignalRecovery/SaturatedEvents"];
272 
273  return eSuccess;
274 }
275 
276 
277 bool
279  const int saturationValue)
280 {
281  const PMTRecData& pmtRec = pmt.GetRecData();
282  const TraceD& vemtrace = pmtRec.GetVEMTrace();
283  copy(vemtrace.Begin(), vemtrace.End(), fgVEMTrace);
284 
285  const TraceI& lowGainTrace = pmt.GetFADCTrace(sdet::PMTConstants::eLowGain);
286  for (unsigned int i = 0; i < lowGainTrace.GetSize(); ++i)
287  if (lowGainTrace[i] == saturationValue) {
288  sfi.fFirstSatBin = i;
289  break;
290  }
291 
292  sfi.fLastSatBin =
294  sfi.fVEMPeak = pmtRec.GetVEMPeak();
295  sfi.fVEMCharge = pmtRec.GetVEMCharge();
296  sfi.fGainRatio = pmtRec.GetGainRatio();
297  sfi.fUndershootValue =
298  RecoverSignalUndershoot(pmt.GetFADCTrace(sdet::PMTConstants::eHighGain),
300  saturationValue);
301 
302  if (sfi.fUndershootValue <= 0) {
303  WARNING("Negative undershoot");
304  return false;
305  }
306 
307  for (unsigned int i = 0; i < kTraceLength; ++i)
308  if (fgVEMTrace[i] > kMinCounts) {
309  sfi.fFirstBin = i;
310  break;
311  }
312 
313  sfi.fChi2 = SaturationFitDriver(sfi); // do the fitting
314  if (sfi.fChi2 < 0)
315  return false;
316  sfi.fAmpl = ComputeAmplitude(sfi); // recompute the amplitude
317 
318  sfi.fNdof = 0;
319  if (fgVEMTrace[sfi.fFirstBin] == sfi.fMaximum)
320  ++sfi.fNdof;
321 
322  const double x0 = sfi.fFirstBin + sfi.fx0;
323  double pmtSignal = 0;
324  double satSignal = 0;
325  for (unsigned int i = 0; i < kTraceLength; ++i) {
326  const double vemSignal = fgVEMTrace[i];
327  satSignal += vemSignal;
328 
329  // count Ndof
330  if (kMinCounts < vemSignal && vemSignal < sfi.fMaximum)
331  ++sfi.fNdof;
332 
333  if (lowGainTrace[i] == saturationValue) {
334  const double moyal =
335  sfi.fAmpl * Moyal((int(i) - x0) / sfi.fSigma);
336  pmtSignal += max(moyal, sfi.fMaximum);
337  } else
338  pmtSignal += vemSignal;
339  }
340 
341  const double vemCharge = pmtRec.GetVEMCharge();
342  const double peakOverCharge = sfi.fVEMPeak / vemCharge;
343  sfi.fSignal = pmtSignal * peakOverCharge;
344  satSignal *= peakOverCharge;
345  if (std::isnan(sfi.fSignal)) {
346  WARNING("NAN value in recovered signal");
347  return false;
348  }
349 
350  // this does not really happen, but if the undershootvalue was recovered,
351  // use it with the 10% uncertainty
352  const double under = sfi.fUndershootValue * sfi.fGainRatio / vemCharge;
353  if (sfi.fSignal < under)
354  sfi.fSignalErr = 0.1 * (sfi.fSignal = under);
355 
356  if (sfi.fSignal < satSignal) {
357  ostringstream warn;
358  warn << "Recovered signal " << sfi.fSignal
359  << " smaller than the saturated one " << satSignal;
360  WARNING(warn);
361  return false;
362  }
363  // two free paramters
364  sfi.fNdof -= 2;
365  const double relDiff = (sfi.fSignal - satSignal) / sfi.fSignal;
366  sfi.fSignalErr = sfi.fSignal * ErrorParametrization(relDiff);
367  sfi.fSignalErrNL = sfi.fSignal * NonLinearityUncertainty(relDiff);
368  // do not accept traces with reduced chi2 large
369  if (sfi.fChi2 / sfi.fNdof > 150) {
370  ostringstream warn;
371  warn << "chi2/ndof = " << sfi.fChi2 / sfi.fNdof << " is too large";
372  WARNING(warn);
373  return false;
374  }
375 
376  return true;
377 }
378 
379 
380 double
382  const
383 {
384  TMinuit minuit(7);
385  minuit.SetFCN(SignalRecovery::SaturationFitFnc);
386  int errFlag = 0;
387  double argList[10];
388  argList[0] = -1;
389 
390  minuit.mnexcm("SET PRINTOUT", argList, 1, errFlag);
391  minuit.mnexcm("SET NOWARNINGS", argList, 0, errFlag);
392  minuit.SetPrintLevel(-1);
393 
394  argList[0] = 1; // chi2
395  minuit.mnexcm("SET ERRORDEF", argList, 1, errFlag);
396  sfi.fMaximum = *max_element(fgVEMTrace, fgVEMTrace + kTraceLength);
397 
398  const double u_vem = sfi.fUndershootValue * sfi.fGainRatio / sfi.fVEMCharge;
399 
400  // if the parameters were well computed for the previous pmts, use them as initial value
401  minuit.DefineParameter(0, "ampl", (sfi.fAmpl && sfi.fChi2 > 0) ? sfi.fAmpl : sfi.fMaximum/0.6065,
402  10, sfi.fMaximum/0.70, 30000);
403  if (sfi.fSigma && sfi.fChi2 > 0)
404  minuit.DefineParameter(1, "sigma", sfi.fSigma, 0.01, sfi.fSigma-0.5, sfi.fSigma+0.5);
405  else {
406  const double sigmaParam = SigmaParametrization(u_vem);
407  const double sigmaParamLow = sigmaParam - 0.75;
408  const double sigmaParamHigh = sigmaParam + 0.75;
409  minuit.DefineParameter(1, "sigma", sigmaParam, 0.01,
410  (sigmaParamLow > 0) ? sigmaParamLow:0,
411  sigmaParamHigh);
412  }
413  const double x0_limit = (X0Parametrization(u_vem) > 3) ? X0Parametrization(u_vem) - 3 : 0;
414 
415  minuit.DefineParameter(2, "x0", X0Parametrization(u_vem), 1.,
416  x0_limit, X0Parametrization(u_vem)+3);
417  minuit.DefineParameter(3, "undershoot",sfi.fUndershootValue, 0, 0, 0);
418  minuit.DefineParameter(4, "calibCt", sfi.fGainRatio/sfi.fVEMPeak, 0, 0, 0);
419  minuit.DefineParameter(5, "firstSatBin", sfi.fFirstSatBin, 0, 0, 0);
420  minuit.DefineParameter(6, "lastSatBin", sfi.fLastSatBin, 0, 0, 0);
421 
422  minuit.FixParameter(0);
423  argList[0] = 1;
424  minuit.mnexcm("CALI", argList, 1, errFlag);
425  argList[0] = 5000; // max no of calls
426  argList[1] = 0.001; // tolerance
427  minuit.mnexcm("MINIMIZE", argList, 2, errFlag);
428  //minuit.mnexcm("MIGRAD", argList, 2, errFlag);
429 
430  if (errFlag) {
431  minuit.mnexcm("MINOS", argList, 2, errFlag);
432  if (errFlag)
433  return -1;
434  }
435 
436  minuit.GetParameter(0, sfi.fAmpl, sfi.fAmplErr);
437  minuit.GetParameter(1, sfi.fSigma, sfi.fSigmaErr);
438  minuit.GetParameter(2, sfi.fx0, sfi.fx0Err);
439 
440  // sanity checks
441  const double x0 = sfi.fx0 + sfi.fFirstBin;
442  if (sfi.fAmpl <= 0 || sfi.fSigma <= 0 ||
443  x0 < sfi.fFirstSatBin || x0 > sfi.fLastSatBin + 2) {
444  WARNING("Fit results are out of bounds.");
445 
446  ostringstream warn;
447  warn << "Sanity checks failed: ampl=" << sfi.fAmpl
448  << " sigma=" << sfi.fSigma
449  << " first=" << sfi.fFirstSatBin << " < x0=" << x0
450  << " < last=" << sfi.fLastSatBin;
451  WARNING(warn);
452 
453  return -1;
454  } else
455  return minuit.fAmin;
456 }
457 
458 
459 void
460 SignalRecovery::SaturationFitFnc(int& /*nPar*/, double* const /*grad*/, double& value,
461  double* par, const int flag)
462 {
463  // the fitting function, acoording to GAP2006-012
464  double& amplitude = par[0];
465  const double sigma = par[1];
466  const double x0 = par[2];
467  const double underValue = par[3];
468  const double calibCt = par[4];
469  const unsigned int firstSatBin = (unsigned int)(par[5]);
470  const unsigned int lastSatBin = (unsigned int)(par[6]);
471 
472  static unsigned int firstBin;
473  static double maximum;
474  static vector<double> bSignalNotSat;
475  static vector<int> bPosition;
476  static unsigned int nBins;
477 
478  if (flag == 1) { // fnc init
479  bSignalNotSat.clear();
480  bPosition.clear();
481 
482  unsigned int maxposition = 0;
483  maximum = fgVEMTrace[0];
484  firstBin = 0;
485  // find the first bin to be used in the fit
486  for (unsigned int i = 1; i < kTraceLength; ++i) {
487  const double vem = fgVEMTrace[i];
488  if (!firstBin && vem > kMinCounts)
489  firstBin = i;
490  if (firstBin > firstSatBin)
491  firstBin = firstSatBin;
492  if (maximum < vem) {
493  maximum = vem;
494  maxposition = i;
495  }
496  }
497  // find the last bin to be used in the fit
498  unsigned int lastBin = kTraceLength;
499  for (unsigned int i = lastSatBin+1; i < kTraceLength; ++i)
500  if (fgVEMTrace[i] <= kMinCounts) {
501  lastBin = i;
502  break;
503  }
504 
505  if (maxposition && fgVEMTrace[maxposition-1] < kMinCounts) {
506  bSignalNotSat.push_back(fgVEMTrace[maxposition]);
507  bPosition.push_back(int(maxposition) - firstBin);
508  }
509 
510  for (unsigned int i = 0; i <= firstBin; ++i) {
511  const double t = fgVEMTrace[i];
512  if (t > kMinCounts) {
513  bSignalNotSat.push_back(t);
514  bPosition.push_back(int(i) - firstBin);
515  }
516  }
517 
518  for (unsigned int i = lastSatBin+1; i <= lastBin; ++i) {
519  const double t = fgVEMTrace[i];
520  if (kMinCounts < t) {
521  bSignalNotSat.push_back(t);
522  bPosition.push_back(int(i) - firstBin);
523  }
524  }
525 
526  nBins = bSignalNotSat.size();
527  }
528 
529  vector<double> tin;
530  for (int iterations = 0; iterations < 10; ++iterations) {
531 
532  const double ampl = amplitude / calibCt;
533  tin.clear();
534  tin.push_back(-5);
535  // Moyal rise
536  for (unsigned int i = 0; i < Length(kSignalLimits); ++i) {
537  const double y = kSignalLimits[i] / ampl;
538  if (y < kMaxMoyal && y > 0)
539  tin.push_back(x0 + sigma * InverseMoyal<-1>(y));
540  }
541  // Moyal drop
542  for (int i = Length(kSignalLimits) - 1; i >= 0; --i) {
543  const double y = kSignalLimits[i] / ampl;
544  if (y < kMaxMoyal && y > 0)
545  tin.push_back(x0 + sigma * InverseMoyal<+1>(y));
546  }
547 
548  tin.push_back(double(kTraceLength) - firstBin);
549 
550  double alphasum = 0;
551  double betasum = 0;
552  const unsigned int n = tin.size();
553 
554  for (unsigned int i = 0; i < n/2; ++i) {
555  const double tin_i = tin[i];
556  const double tin_i1 = tin[i+1];
557  alphasum += kAlphaSat[i] * AmplitudeFunction(tin_i, tin_i1, sigma, x0);
558  betasum += kBetaSat[i] * (fabs(tin_i1 - tin_i));
559  }
560 
561  for (unsigned int i = 1; i < n/2; ++i) {
562  const int ni = n - i;
563  const double tin_ni = tin[ni];
564  const double tin_ni1 = tin[ni - 1];
565  alphasum += kAlphaSat[i-1] * AmplitudeFunction(tin_ni1, tin_ni, sigma, x0);
566  betasum += kBetaSat[i-1] * (fabs(tin_ni - tin_ni1));
567  }
568 
569  amplitude = (underValue - betasum*0.5) * calibCt / alphasum;
570 
571  }
572 
573  double chi2 = 0;
574  //changed for out chi2
575  for (unsigned int i = 0; i < nBins; ++i) {
576  const double landFunc = amplitude * Moyal((bPosition[i] - x0) / sigma);
577  const double sns = bSignalNotSat[i];
578  chi2 += Sqr(sns - landFunc) / sns;
579  }
580 
581  value = chi2;
582 }
583 
584 
585 double
587  const
588 {
589  // computes once again the amplitude, as the value from minuit is not the last one
590 
591  vector<double> tin;
592 
593  tin.push_back(0);
594 
595  const double dynAnOverPeak = sfi.fGainRatio / sfi.fVEMPeak;
596  const double ampl = sfi.fAmpl / dynAnOverPeak;
597 
598  if (ampl < 0) {
599  ERROR("This should not happen!");
600  return 1e10;
601  }
602 
603  // Moyal rise
604  for (unsigned int i = 0; i < Length(kSignalLimits); ++i) {
605  const double y = kSignalLimits[i]/ampl;
606  if (y < kMaxMoyal)
607  tin.push_back(sfi.fx0 + sfi.fSigma * InverseMoyal<-1>(y));
608  }
609  // Moyal drop
610  for (int i = Length(kSignalLimits) - 1; i >= 0; --i) {
611  const double y = kSignalLimits[i]/ampl;
612  if (y < kMaxMoyal)
613  tin.push_back(sfi.fx0 + sfi.fSigma * InverseMoyal<+1>(y));
614  }
615 
616  tin.push_back(double(kTraceLength) - sfi.fFirstBin);
617 
618  double alphasum = 0;
619  double betasum = 0;
620  const unsigned int n = tin.size();
621  for (unsigned int i = 0; i < n/2; ++i) {
622  const double tin_i = tin[i];
623  const double tin_i1 = tin[i+1];
624  alphasum += kAlphaSat[i] * AmplitudeFunction(tin_i, tin_i1, sfi.fSigma, sfi.fx0);
625  betasum += kBetaSat[i] * (fabs(tin_i1 - tin_i));
626  }
627 
628  for (unsigned int i = 1; i < n/2; ++i) {
629  const int ni = n - i;
630  const double tin_ni = tin[ni];
631  const double tin_ni1 = tin[ni - 1];
632  alphasum += kAlphaSat[i-1] * AmplitudeFunction(tin_ni1, tin_ni, sfi.fSigma, sfi.fx0);
633  betasum += kBetaSat[i-1] * (fabs(tin_ni - tin_ni1));
634  }
635 
636  return (sfi.fUndershootValue - betasum*0.5) * dynAnOverPeak / alphasum;
637 }
638 
639 
640 double
641 SignalRecovery::RecoverSignalUndershoot(const TraceI& dynodeTrace, // high gain
642  const TraceI& anodeTrace,
643  const int saturationValue) // low gain
644  const
645 {
646  // this function returns the undershoot value corresponding to the Lecce (GAP2006-075)
647  // or to Torino (GAP 2007-xx) parametrizations. Minimizing the uncertainties in the
648  // undershoot value, the average value between the 2 should be preferred
649 
650  vector<int> histoDynBefore(saturationValue + 1, 0);
651  vector<int> histoAnBefore(saturationValue + 1, 0);
652 
653  const unsigned int firstUnderBin = 100;
654  for (unsigned int j = 0; j < firstUnderBin; ++j) {
655  ++histoDynBefore[dynodeTrace[j]];
656  ++histoAnBefore[anodeTrace[j]];
657  }
658 
659  vector<int> histoDynAfter(saturationValue + 1, 0);
660  vector<int> histoAnAfter(saturationValue + 1, 0);
661 
662  const unsigned int lastUnderBin = 668; // this is dangerously close to 666!
663  for (unsigned int j = lastUnderBin; j < kTraceLength; ++j) {
664  ++histoDynAfter[dynodeTrace[j]];
665  ++histoAnAfter[anodeTrace[j]];
666  }
667 
668  int xMaxDynBefore = 0;
669  int maxDynBefore = histoDynBefore[0];
670  int xMaxAnBefore = 0;
671  int maxAnBefore = histoAnBefore[0];
672  int xMaxDynAfter = 0;
673  int maxDynAfter = histoDynAfter[0];
674  int xMaxAnAfter = 0;
675  int maxAnAfter = histoAnAfter[0];
676 
677  for (int j = 1; j <= saturationValue; ++j) {
678  if (histoAnBefore[j] > maxAnBefore) {
679  maxAnBefore = histoAnBefore[j];
680  xMaxAnBefore = j;
681  }
682  if (histoAnAfter[j] > maxAnAfter) {
683  maxAnAfter = histoAnAfter[j];
684  xMaxAnAfter = j;
685  }
686  if (histoDynBefore[j] > maxDynBefore) {
687  maxDynBefore = histoDynBefore[j];
688  xMaxDynBefore = j;
689  }
690  if (histoDynAfter[j] > maxDynAfter) {
691  maxDynAfter = histoDynAfter[j];
692  xMaxDynAfter = j;
693  }
694  }
695 
696  // dynode and anode baselines before the pulse
697  int sumDynBefore = 0;
698  int sumAnBefore = 0;
699  int k_Dyn = 0;
700  int k_An = 0;
701  // do not take into account peaks
702  const double maxBaselineVar = 4; // notice the not >=
703  for (unsigned int j = 0; j < firstUnderBin; ++j) {
704  const int dyn = dynodeTrace[j];
705  if (abs(dyn - xMaxDynBefore) < maxBaselineVar) {
706  sumDynBefore += dyn;
707  ++k_Dyn;
708  }
709  const int an = anodeTrace[j];
710  if (abs(an - xMaxAnBefore) < maxBaselineVar) {
711  sumAnBefore += an;
712  ++k_An;
713  }
714  }
715 
716  const double baseDynBefore = k_Dyn ? double(sumDynBefore)/k_Dyn : 0;
717  const double baseAnBefore = k_An ? double(sumAnBefore)/k_An : 0;
718 
719  // check if the baseline after the pulse is stabilized
720  int sumDynAfter = 0;
721  int sumAnAfter = 0;
722  k_Dyn = 0;
723  k_An = 0;
724  int interval1 = 0;
725  int interval2 = 0;
726  for (unsigned int j = lastUnderBin; j < kTraceLength; ++j) {
727  const int dyn = dynodeTrace[j];
728 
729  if (abs(dyn - xMaxDynAfter) < maxBaselineVar) {
730  sumDynAfter += dyn;
731  ++k_Dyn;
732  }
733  if (j < 718)
734  interval1 += dyn;
735  else
736  interval2 += dyn;
737 
738  const int an = anodeTrace[j];
739  if (abs(an - xMaxAnAfter) < maxBaselineVar) {
740  sumAnAfter += an;
741  ++k_An;
742  }
743  }
744 
745  const double baseDynAfter = k_Dyn ? double(sumDynAfter)/k_Dyn : 0;
746  const double baseAnAfter = k_An ? double(sumAnAfter)/k_An : 0;
747 
748  if (abs(interval1 - interval2) > 150) {
749  WARNING("Undershoot value could not be computed due to after pulse baseline instability");
750  return 0;
751  }
752 
753  const double anodeUndershoot = baseAnBefore - baseAnAfter;
754  const double dynodeUndershoot = baseDynBefore - baseDynAfter;
755 
756  int nSatDyn = 0;
757  double dynodeCharge = 0;
758  double anodeCharge = 0;
759  {
760  bool isDynSaturated = false;
761  const int saturationLimit = saturationValue - 3;
762  for (unsigned int j = 0; j < kTraceLength; ++j) {
763  const int dyn = dynodeTrace[j];
764  if (dyn == saturationValue)
765  isDynSaturated = true;
766  if (dyn > saturationLimit)
767  ++nSatDyn;
768  if (dyn - baseDynBefore > 1)
769  dynodeCharge += dyn - baseDynBefore;
770  if (anodeTrace[j] - baseAnBefore > 1)
771  anodeCharge += anodeTrace[j] - baseAnBefore;
772  }
773  if (!isDynSaturated) {
774  ERROR("No high gain saturation for this PMT");
775  return 0;
776  }
777  }
778 
779  // old Lecce parametrization
780  //double lE_RecAnodeCharge1 = 143.88*pow(dynodeUndershoot, 1.5310);
781 
782  // no recovery possible with negative undershoot values
783  if (dynodeUndershoot <= 0 && anodeUndershoot <= 0) {
784  ostringstream info;
785  info << "Negative undershoot values: anode " << anodeUndershoot << ", dynode "
786  << dynodeUndershoot;
787  WARNING(info);
788  return 0;
789  }
790  const bool useDynode = (dynodeUndershoot > 0);
791 
792  const double lE_RecAnodeCharge2 = (dynodeUndershoot < 30 && useDynode) ?
793  143.68 * pow(dynodeUndershoot, 1.5316) :
794  21276.60 * (anodeUndershoot - 0.06227);
795 
796  const double to_RecAnodeCharge = (anodeUndershoot <= 1 && useDynode) ?
797  770 * (dynodeUndershoot - (1.35e-4*dynodeCharge + 0.055*nSatDyn)) - 222 :
798  20000*anodeUndershoot - 2216;
799 
800  double recSignal = 0;
801 
802  switch (fRecoverUnsaturatedSignal) {
803  case 1:
804  recSignal = 0.5*(lE_RecAnodeCharge2 + to_RecAnodeCharge);
805  break;
806  case 2:
807  recSignal = to_RecAnodeCharge;
808  break;
809  case 3:
810  recSignal = lE_RecAnodeCharge2;
811  break;
812  };
813 
814  // use the anode charge if something went really wrong
815  if (recSignal < anodeCharge)
816  recSignal = anodeCharge;
817 
818  return recSignal;
819 }
double GetVEMCharge() const
Definition: PMTRecData.h:130
Branch GetTopBranch() const
Definition: Branch.cc:63
Class to access station level reconstructed data.
static void SaturationFitFnc(int &nPar, double *const grad, double &value, double *par, const int flag)
utl::TraceD & GetVEMTrace(const StationConstants::SignalComponent source=StationConstants::eTotal)
Traces calibrated in VEM Peak.
Definition: PMTRecData.h:46
constexpr T Sqr(const T &x)
class to hold data at PMT level
Definition: SEvent/PMT.h:28
double X0Parametrization(const double value)
PMTRecData & GetRecData()
Get object containing PMT reconstructed data.
Definition: SEvent/PMT.h:48
#define INFO(message)
Macro for logging informational messages.
Definition: ErrorLogger.h:161
double RecoverSignalUndershoot(const utl::TraceI &dynodeTrace, const utl::TraceI &anodeTrace, const int saturationValue) const
fwk::VModule::ResultFlag Init()
Initialize: invoked at beginning of run (NOT beginning of event)
double ComputeAmplitude(SaturationFitInterface &sfi) const
Branch GetChild(const std::string &childName) const
Get child of this Branch by child name.
Definition: Branch.cc:211
double pow(const double x, const unsigned int i)
int GetFADCSaturatedBins(const sdet::PMTConstants::PMTGain gain=sdet::PMTConstants::eHighGain) const
Definition: PMTRecData.h:161
fwk::VModule::ResultFlag Run(evt::Event &event)
Run: invoked once per event.
Iterator Begin()
Definition: Trace.h:75
#define max(a, b)
Class representing a document branch.
Definition: Branch.h:107
double SaturationFitDriver(SaturationFitInterface &sfi) const
class to hold reconstructed data at PMT level
Definition: PMTRecData.h:38
double ErrorParametrization(const double x)
bool SetNonLinearityCurve(const NonLinearity &nL)
constexpr double kPi
Definition: MathConstants.h:24
double abs(const SVector< n, T > &v)
double GetTotalSignal() const
Total integrated signal in VEM unit, averaged over pmts.
template double InverseMoyal<+1 >(const double y, const double eps)
#define WARNING(message)
Macro for logging warning messages.
Definition: ErrorLogger.h:163
void GetData(bool &b) const
Overloads of the GetData member template function.
Definition: Branch.cc:644
utl::TraceI & GetFADCTrace(const sdet::PMTConstants::PMTGain gain=sdet::PMTConstants::eHighGain, const StationConstants::SignalComponent source=StationConstants::eTotal)
Definition: SEvent/PMT.h:72
ResultFlag
Flag returned by module methods to the RunController.
Definition: VModule.h:60
const unsigned int kTraceLength
double NonLinearityUncertainty(const double x)
double Moyal(const double x)
Moyal function.
Definition: Moyal.h:20
double AmplitudeFunction(const double i1, const double i2, const double sigma, const double x)
Iterator End()
Definition: Trace.h:76
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
template double InverseMoyal<-1 >(const double y, const double eps)
double GetVEMPeak() const
Definition: PMTRecData.h:119
bool HasSEvent() const
double SigmaParametrization(const double value)
double GetGainRatio() const
Definition: PMTRecData.h:147
void SetRecoveredSignal(const double recSignal, const double recSignalErr)
Total recovered signal in VEM unit, averaged over pmts.

, generated on Tue Sep 26 2023.