SdHistogramFitterOG.cc
Go to the documentation of this file.
1 #include "SdHistogramFitterOG.h"
2 
3 #include <fwk/CentralConfig.h>
4 #include <evt/Event.h>
5 #include <sdet/SDetector.h>
6 #include <sevt/SEvent.h>
7 #include <sevt/EventTrigger.h>
8 #include <sevt/StationCalibData.h>
9 #include <utl/QuadraticFitter.h>
10 #include <utl/ExponentialFitter.h>
11 
12 using namespace fwk;
13 using namespace sevt;
14 using namespace utl;
15 using namespace std;
16 
17 
18 namespace SdHistogramFitterOG {
19 
20  // pair<> output helper
21  template<typename T1, typename T2>
22  inline ostream&
23  operator<<(ostream& os, pair<T1, T2>& p)
24  {
25  return os << '[' << p.first << ", " << p.second << ']';
26  }
27 
28 
31  {
32  auto topB = CentralConfig::GetInstance()->GetTopBranch("SdHistogramFitterOG");
33 
34  topB.GetChild("peakFitRange").GetData(fPeakFitRange);
35  topB.GetChild("peakFitChi2Accept").GetData(fPeakFitChi2Accept);
36  topB.GetChild("peakOnlineToVEMFactor").GetData(fPeakOnlineToVEMFactor);
37  topB.GetChild("peakHistogramToVEMFactor").GetData(fPeakHistogramToVEMFactor);
38  topB.GetChild("peakOnlineToMIPFactor").GetData(fPeakOnlineToMIPFactor);
39  topB.GetChild("peakHistogramToMIPFactor").GetData(fPeakHistogramToMIPFactor);
40  topB.GetChild("chargeFitShoulderHeadRatio").GetData(fChargeFitShoulderHeadRatio);
41  topB.GetChild("chargeFitChi2Accept").GetData(fChargeFitChi2Accept);
42  topB.GetChild("chargeOnlineToVEMFactor").GetData(fChargeOnlineToVEMFactor);
43  topB.GetChild("chargeHistogramToVEMFactor").GetData(fChargeHistogramToVEMFactor);
44  topB.GetChild("chargeOnlineToMIPFactor").GetData(fChargeOnlineToMIPFactor);
45  topB.GetChild("chargeHistogramToMIPFactor").GetData(fChargeHistogramToMIPFactor);
46 
47  auto shapeFitB = topB.GetChild("shapeFitRange");
48  shapeFitB.GetChild("beforeCalibrationVersion12").GetData(fShapeFitRangeBefore12);
49  shapeFitB.GetChild("sinceCalibrationVersion12").GetData(fShapeFitRangeSince12);
50 
51  ostringstream info;
52  info << "Parameters:\n"
53  "peakFitRange: " << fPeakFitRange << "\n"
54  "peakFitChi2Accept: " << fPeakFitChi2Accept << "\n"
55  "peakOnlineToVEMFactor: " << fPeakOnlineToVEMFactor << "\n"
56  "peakHistogramToVEMFactor: " << fPeakHistogramToVEMFactor << "\n"
57  "peakOnlineToMIPFactor: " << fPeakOnlineToMIPFactor << "\n"
58  "peakHistogramToMIPFactor: " << fPeakHistogramToMIPFactor << "\n"
59  "chargeFitShoulderHeadRatio: " << fChargeFitShoulderHeadRatio << "\n"
60  "chargeFitChi2Accept: " << fChargeFitChi2Accept << "\n"
61  "chargeOnlineToVEMFactor: " << fChargeOnlineToVEMFactor << "\n"
62  "chargeHistogramToVEMFactor: " << fChargeHistogramToVEMFactor << "\n"
63  "chargeOnlineToMIPFactor: " << fChargeOnlineToMIPFactor << "\n"
64  "chargeHistogramToMIPFactor: " << fChargeHistogramToMIPFactor << "\n"
65  "shapeFitRange:\n"
66  " beforeCalibrationVersion12: " << fShapeFitRangeBefore12 << "\n"
67  " sinceCalibrationVersion12: " << fShapeFitRangeSince12;
68  INFO(info);
69 
70  return eSuccess;
71  }
72 
73 
75  SdHistogramFitter::Run(evt::Event& event)
76  {
77  if (!event.HasSEvent())
78  return eSuccess;
79  auto& sEvent = event.GetSEvent();
80 
81  fToldYaPeak = false;
82  fToldYaCharge = false;
83  fToldYaShape = false;
84 
85  // loop on stations
86  for (auto& station : sEvent.StationsRange()) {
87 
88  if (!station.HasTriggerData() ||
89  !station.HasCalibData() ||
90  !station.HasGPSData())
91  continue;
92 
93  const auto& trig = station.GetTriggerData();
94  if (trig.IsRandom() ||
95  (trig.GetErrorCode() & 0xff) || // for UUBs errorcodes are above 256.
96  station.GetCalibData().GetVersion() > 32000)
97  continue;
98 
99  // skip "bad compressed data"
100  if (sEvent.HasTrigger()) {
101  const int trigSecond = sEvent.GetTrigger().GetTime().GetGPSSecond();
102  const int timeDiff = int(station.GetGPSData().GetSecond()) - trigSecond;
103  if (abs(timeDiff) > 1)
104  continue;
105  }
106 
107  const auto& dStation = det::Detector::GetInstance().GetSDetector().GetStation(station);
108  fIsUub = dStation.IsUUB();
109 
110  CalculatePeakAndCharge(station);
111 
112  // calibration for small pmt
113  if (fIsUub && station.HasSmallPMTData()) {
114  if (CopySmallPMTCalibData(station)) {
115  station.GetSmallPMTData().SetIsTubeOk();
116  } else {
117  station.GetSmallPMTData().SetIsTubeOk(false);
118  station.GetSmallPMT().GetCalibData().SetIsTubeOk(false);
119  ostringstream warn;
120  warn << "Station " << station.GetId() << ": No valid SmallPMT calibration."
121  " Setting IsTubeOk as false for SmallPMT.";
122  WARNING(warn);
123  }
124  }
125 
126  }
127 
128  return eSuccess;
129  }
130 
131 
132  void
133  SdHistogramFitter::CalculatePeakAndCharge(Station& station)
134  {
135  const auto& stationCalib = station.GetCalibData();
136  const auto calibVersion = stationCalib.GetVersion();
137 
138  const auto& dStation = det::Detector::GetInstance().GetSDetector().GetStation(station);
139 
140  typedef VariableBinHistogramWrap<double, int> CalibHistogram;
141 
142  // loop over station PMTs
143  for (auto& pmt : station.PMTsRange(sdet::PMTConstants::eAnyType)) {
144 
145  const auto type = pmt.GetType();
146 
147  // No muon histograms for SmallPMT.
148  // It has its own dedicated function "CopySmallPMTCalibData"
149  // to fill VEM values in RecData from SmallPMTCalibData class.
151  continue;
152 
153  if (!pmt.HasCalibData())
154  continue;
155 
156  const auto& pmtCalib = pmt.GetCalibData();
157 
158  if (!pmtCalib.IsTubeOk())
159  continue;
160 
161  if (!pmt.HasRecData())
162  pmt.MakeRecData();
163  auto& pmtRec = pmt.GetRecData();
164 
165  // muon peak
166  if (!pmtRec.GetVEMPeak()) {
167  // first approximation is corrected online value
168  const double onlineFactor =
169  (type == sdet::PMTConstants::eScintillator) ? fPeakOnlineToMIPFactor : fPeakOnlineToVEMFactor;
170  double vemPeak = pmtCalib.GetOnlinePeak() * onlineFactor;
171  double vemPeakErr = 0;
172  pmtRec.SetOnlineVEMPeak(vemPeak, vemPeakErr);
173  if (!vemPeak)
174  vemPeak = dStation.GetPMT(pmt.GetId()).GetNominalVEMPeak();
175  bool vemPeakFromHisto = false;
176 
177  // try to improve values above with histogram fitting
178  if (calibVersion > 8) {
179  const auto& peakHistoBinning = dStation.GetMuonPeakHistogramBinning<short>(type, calibVersion);
180  const int muonPeakHistoSize = pmtCalib.GetMuonPeakHisto().size();
181  if (!muonPeakHistoSize || int(peakHistoBinning.size())-1 != muonPeakHistoSize) {
182  if (!(fToldYaPeak || pmt.HasSimData())) {
183  WARNING("According to the LS calibration version there should be a muon peak histogram... Will not tell you again!");
184  fToldYaPeak = true;
185  }
186  } else {
187  const VariableBinHistogramWrap<short, int> peakHisto(peakHistoBinning, pmtCalib.GetMuonPeakHisto());
188  const double histoFactor =
189  (type == sdet::PMTConstants::eScintillator) ? fPeakHistogramToMIPFactor : fPeakHistogramToVEMFactor;
190  const double predictedHistoPeak = vemPeak / histoFactor;
191  if (predictedHistoPeak > 0 && peakHisto.GetMaximum() > 200) {
192  const double baseEstimate = pmtCalib.GetBaseline() - pmtCalib.GetMuonPeakHistoOffset();
193  const double base = (fabs(baseEstimate) < 20) ? baseEstimate : 0;
194  const double peakLow = fPeakFitRange.first * predictedHistoPeak;
195  const double peakHigh = fPeakFitRange.second * predictedHistoPeak;
196 
197  if (peakHigh - peakLow >= 5) {
198  try {
199  // note: x axis is offset by base, in order to be the same
200  // for all histograms
201  QuadraticFitData qf;
202  MakeQuadraticFitter(peakHisto,
203  peakLow + base, peakHigh + base).GetFitData(qf);
204  pmtRec.GetMuonPeakFitData() = qf;
205  const double fittedPeak = qf.GetExtremePosition() - base;
206  // reasonable limits for the result
207  if (peakLow <= fittedPeak && fittedPeak <= peakHigh &&
208  qf.GetChi2() <= fPeakFitChi2Accept*qf.GetNdof()) {
209  vemPeak = fittedPeak * histoFactor;
210  vemPeakErr = qf.GetExtremePositionError() * histoFactor;
211  vemPeakFromHisto = true;
212  }
213  } catch (OutOfBoundException& ex) {
214  WARNING(ex.GetMessage());
215  }
216  }
217  }
218  }
219  }
220  pmtRec.SetVEMPeak(vemPeak, vemPeakErr);
221  pmtRec.SetIsVEMPeakFromHistogram(vemPeakFromHisto);
222  }
223 
224  if (!pmtRec.GetVEMCharge()) {
225  // The charge conversion factor is the correction for the difference between the mode
226  // of charge histograms for the VEM (vertical-centered muons) or MIP (vertical muons)
227  // and the mode of charge histogram for omni-directional background particles.
228  // charge conversion factor depends on PMT type, so far eScintillator, and eWaterCherenkovLarge;
229  const double onlineFactor =
230  (type == sdet::PMTConstants::eScintillator) ? fChargeOnlineToMIPFactor : fChargeOnlineToVEMFactor;
231  double vemCharge = pmtCalib.GetOnlineCharge() * onlineFactor;
232  double vemChargeErr = 20 * onlineFactor;
233  pmtRec.SetOnlineVEMCharge(vemCharge, vemChargeErr);
234  bool vemChargeFromHisto = false;
235  double muChargeSlope = 0;
236 
237  // try to improve values above with histogram fitting
238  if (calibVersion > 8) {
239  const auto& chargeHistoBinning = dStation.GetMuonChargeHistogramBinning<double>(type, calibVersion);
240  const int muonChargeHistoSize = pmtCalib.GetMuonChargeHisto().size();
241  // analyze charge histogram
242  if (!muonChargeHistoSize || int(chargeHistoBinning.size()) - 1 != muonChargeHistoSize) {
243  if (!fToldYaCharge) {
244  WARNING("According to the LS calibration version there should be a muon charge histogram... Will not tell you again!");
245  fToldYaCharge = true;
246  }
247  } else {
248  const CalibHistogram chargeHisto(chargeHistoBinning, pmtCalib.GetMuonChargeHisto());
249  // apparently there were 19 bins in calibration version 13
250  const double baseEstimate = pmtCalib.GetBaseline() *
251  (calibVersion == 13 ? 19 : 20) - pmtCalib.GetMuonChargeHistoOffset();
252  const double base = (fabs(baseEstimate) < 20) ? baseEstimate : 0;
253 
254  if (chargeHisto.GetMaximum() > 500) {
255 
256  // skip 5 last bins and any small values at the high end
257  const int size = chargeHisto.GetNBins();
258  int start = (size - 1) - 5;
259 
260  {
261  const auto small = fIsUub ? 40 : 300;
262  for ( ; start >= 2 && chargeHisto.GetBinAverage(start) < small; --start)
263  ;
264  }
265 
266  // find "head-and-shoulder": from the upper side of the histogram,
267  // search for local maximum (head), surrounded by drops (shoulders)
268  // with shoulder/head value ratio of less than
269  // fChargeWindowShoulderHeadRatio
270  int maxBin = start;
271  double maxValue = chargeHisto.GetBinAverage(start);
272  int shoulderLow = 0;
273  {
274  double value = chargeHisto.GetBinAverage(start - 1);
275  double value1 = chargeHisto.GetBinAverage(start - 2);
276  for (int pos = start - 1; pos >= 2; --pos) {
277  if (maxValue < value) {
278  maxValue = value;
279  maxBin = pos;
280  }
281  const double reducedMax = maxValue * fChargeFitShoulderHeadRatio;
282  // require 3 consecutive values to be lower than reducedMax
283  // to qualify as a shoulder
284  const double value2 = chargeHisto.GetBinAverage(pos - 2);
285  if (value <= reducedMax && value1 <= reducedMax && value2 <= reducedMax) {
286  shoulderLow = pos;
287  break;
288  }
289  value = value1;
290  value1 = value2;
291  }
292  }
293  if (shoulderLow) {
294  // find upper shoulder
295  const double reducedMax = maxValue * fChargeFitShoulderHeadRatio;
296  const int size2 = size - 2;
297 
298  int shoulderHigh = 0;
299  {
300  double value = chargeHisto.GetBinAverage(maxBin + 1);
301  double value1 = chargeHisto.GetBinAverage(maxBin + 2);
302  for (int pos = maxBin + 1; pos < size2; ++pos) {
303  const double value2 = chargeHisto.GetBinAverage(pos+2);
304  if (value <= reducedMax && value1 <= reducedMax && value2 <= reducedMax) {
305  shoulderHigh = pos;
306  break;
307  }
308  value = value1;
309  value1 = value2;
310  }
311  }
312 
313  if (shoulderHigh) {
314  const double chargeLow = chargeHisto.GetBinCenter(shoulderLow);
315  const double chargeHigh = chargeHisto.GetBinCenter(shoulderHigh);
316  const double histoFactor =
317  (type == sdet::PMTConstants::eScintillator) ? fChargeHistogramToMIPFactor : fChargeHistogramToVEMFactor;
318  const double predictedHistoCharge = vemCharge / histoFactor;
319  if (chargeLow <= predictedHistoCharge && predictedHistoCharge <= chargeHigh) {
320  try {
321  // now fit in shoulder window
322  QuadraticFitData qf;
323  MakeQuadraticFitter(chargeHisto, chargeLow, chargeHigh).GetFitData(qf);
324  pmtRec.GetMuonChargeFitData() = qf;
325  const double fittedCharge = qf.GetExtremePosition() - base;
326 
327  // reasonable limits for the result
328  if (chargeLow <= fittedCharge && fittedCharge <= chargeHigh &&
329  qf.GetChi2() <= fChargeFitChi2Accept*qf.GetNdof()) {
330  vemCharge = fittedCharge * histoFactor;
331  vemChargeErr = qf.GetExtremePositionError() * histoFactor;
332  pmtRec.SetHistogramVEMCharge(vemCharge, vemChargeErr);
333  vemChargeFromHisto = true;
334  }
335  } catch (OutOfBoundException& ex) {
336  ostringstream warn;
337  warn << ex.GetMessage() << "\n"
338  "Quadratic fit between bins " << chargeLow << " and " << chargeHigh
339  << " failed on this charge histogram:\n";
340  for (unsigned int i = 0, n = chargeHisto.GetNBins(); i < n; ++i)
341  warn << chargeHisto.GetBinCenter(i) << ' ' << chargeHisto.GetBinAverage(i) << '\n';
342  WARNING(warn);
343  }
344 
345  try {
346  // slope of muon charge
348  const double start = chargeHigh + 10;
349  const double stop = min(2*start, 950.);
350  if (start+10 < stop) {
351  MakeExponentialFitter(chargeHisto, start, stop).GetFit(ef);
352  pmtRec.GetMuonChargeSlopeFitData() = ef;
353 
354  const double slope = vemCharge * ef.GetSlope();
355 
356  if (slope < -0.5)
357  muChargeSlope = slope;
358  }
359  } catch (OutOfBoundException& ex) {
360  WARNING(ex.GetMessage());
361  }
362  }
363  }
364  }
365  } // if max > 500
366  }
367  }
368  pmtRec.SetVEMCharge(vemCharge, vemChargeErr);
369  pmtRec.SetIsVEMChargeFromHistogram(vemChargeFromHisto);
370  pmtRec.SetMuonChargeSlope(muChargeSlope);
371  }
372 
373  // shape histogram
374  double muDecayTime = 0;
375  double muDecayTimeErr = 0;
376  if (calibVersion > 8) {
377  // muon decay time from muon shape histogram
378  if (pmtCalib.GetMuonShapeHisto().empty()) {
379  if (!fToldYaShape) {
380  WARNING("According to the LS calibration version there should be a muon shape histogram... Will not tell you again!");
381  fToldYaShape = true;
382  }
383  } else {
384  const auto& muShapeHisto = pmtCalib.GetMuonShapeHisto();
385  const unsigned int size = muShapeHisto.size();
386  if (size) {
387  try {
389 
390  typedef VariableBinHistogramWrap<double, int> ShapeHistogram;
391  const auto& shapeHistoBinning = dStation.GetMuonShapeHistogramBinning(calibVersion);
392  const double subtract = muShapeHisto[0];
394  ShapeHistogram(shapeHistoBinning, muShapeHisto),
395  calibVersion < 12 ? fShapeFitRangeBefore12 : fShapeFitRangeSince12
396  ).GetFit(ef, subtract);
397  pmtRec.GetMuonShapeFitData() = ef;
398 
399  const double slope = ef.GetSlope();
400  const double slopeErr = ef.GetSlopeError();
401 
402  if (slope) {
403  const double decayTime = -1 / slope;
404  if (0 <= decayTime && decayTime < 1000*nanosecond) {
405  muDecayTime = decayTime;
406  muDecayTimeErr = slopeErr / Sqr(slope);
407  }
408  }
409  } catch (OutOfBoundException& ex) {
410  WARNING(ex.GetMessage());
411  }
412  }
413  }
414  }
415  pmtRec.SetMuonPulseDecayTime(muDecayTime, muDecayTimeErr);
416 
417  }
418  }
419 
420 
421  bool
422  SdHistogramFitter::CopySmallPMTCalibData(Station& station)
423  {
424  // Check specific SmallPMT quantities
425  const auto& spmt = station.GetSmallPMTData();
426  if (!spmt.HasCalibData())
427  return false;
428 
429  const auto& spmtCalib = spmt.GetCalibData();
430 
431  if (!spmtCalib.IsTubeOk())
432  return false;
433 
434  // Check standard PMT quantities
435  auto& pmt = station.GetSmallPMT();
436  if (!pmt.HasCalibData())
437  return false;
438 
439  const auto& pmtCalib = pmt.GetCalibData();
440 
441  if (!pmtCalib.IsTubeOk())
442  return false;
443 
444  if (!pmt.HasRecData())
445  pmt.MakeRecData();
446  auto& pmtRec = pmt.GetRecData();
447 
448  const double spmtVemCharge = 1 / (spmtCalib.GetBeta() * spmtCalib.GetCorrectionFactor());
449  const double spmtVemChargeErr =
450  spmtCalib.GetBetaError() * spmtCalib.GetCorrectionFactor() * Sqr(spmtVemCharge);
451 
452  if (std::isnan(spmtVemCharge) || std::isinf(spmtVemCharge) ||
453  spmtVemCharge <= 0 || spmtVemChargeErr <= 0)
454  return false;
455 
456  pmtRec.SetVEMCharge(spmtVemCharge, spmtVemChargeErr);
457 
458  // SmallPMT "VEMpeak" estimation
459  double muonAreaOverPeak = 0;
460  for (const auto& lpmt : station.PMTsRange()) {
461  if (lpmt.HasCalibData() &&
462  lpmt.GetCalibData().IsTubeOk() &&
463  lpmt.HasRecData() &&
464  lpmt.GetRecData().GetVEMPeak() > 0)
465  muonAreaOverPeak +=
466  lpmt.GetRecData().GetVEMCharge() / lpmt.GetRecData().GetVEMPeak();
467  }
468  if (muonAreaOverPeak > 0)
469  pmtRec.SetVEMPeak(spmtVemCharge / muonAreaOverPeak, 0);
470  else {
471  ostringstream warn;
472  warn << "Station " << station.GetId()
473  << ": Cannot calculate properly SmallPMT VEMpeak. Using default value = 1/3";
474  WARNING(warn);
475  pmtRec.SetVEMPeak(1./3, 0);
476  }
477 
478  ostringstream info;
479  info << "SmallPMT calibration for station " << station.GetId() << "\n"
480  << " beta = " << spmtCalib.GetBeta() << " +- " << spmtCalib.GetBetaError()
481  << " ---> VEMcharge = " << pmtRec.GetVEMCharge() << " +- " << pmtRec.GetVEMChargeError()
482  << " (VEMpeak = " << pmtRec.GetVEMPeak() << ')';
483  INFO(info);
484 
485  pmtRec.SetIsVEMChargeFromHistogram(false);
486  pmtRec.SetIsVEMPeakFromHistogram(false);
487 
488  pmtRec.SetMuonChargeSlope(0);
489  pmtRec.SetMuonPulseDecayTime(0, 0);
490  pmtRec.SetGainRatio(1);
491 
492  return true;
493  }
494 
495 }
int GetId() const
Get the station Id.
constexpr T Sqr(const T &x)
Holds result of the quadratic fit.
PMTCalibData & GetCalibData()
Get object containing PMT calibration data.
Definition: SEvent/PMT.h:56
double GetExtremePositionError() const
#define INFO(message)
Macro for logging informational messages.
Definition: ErrorLogger.h:161
void Init()
Initialise the registry.
Branch GetChild(const std::string &childName) const
Get child of this Branch by child name.
Definition: Branch.cc:211
Exception for reporting variable out of valid range.
SmallPMTCalibData & GetCalibData()
Definition: SmallPMTData.h:21
class to hold data at Station level
ExponentialFitter< Histogram > MakeExponentialFitter(const Histogram &histogram, const T &start, const T &stop)
constexpr double nanosecond
Definition: AugerUnits.h:143
double abs(const SVector< n, T > &v)
SmallPMTData & GetSmallPMTData()
#define WARNING(message)
Macro for logging warning messages.
Definition: ErrorLogger.h:163
QuadraticFitter< Histogram, ErrorPolicy > MakeQuadraticFitter(const Histogram &h, const ErrorPolicy)
void GetData(bool &b) const
Overloads of the GetData member template function.
Definition: Branch.cc:644
sevt::StationCalibData & GetCalibData()
Get calibration data for the station.
double GetChi2() const
ResultFlag
Flag returned by module methods to the RunController.
Definition: VModule.h:60
static CentralConfig * GetInstance()
Use this the first time you get an instance of central configuration.
double GetExtremePosition() const
int GetVersion() const
version of the onboard calibration
PMT & GetSmallPMT()
double GetSlopeError() const
bool HasSEvent() const
const std::string & GetMessage() const
Retrieve the message from the exception.
utl::Branch GetTopBranch(const std::string &id)
Get top branch for moduleConfigLink with given id (XML files)

, generated on Tue Sep 26 2023.