SdBaselineFinderKG.cc
Go to the documentation of this file.
1 #include "SdBaselineFinderKG.h"
2 #include <fwk/CentralConfig.h>
3 #include <evt/Event.h>
4 #include <sdet/SDetector.h>
5 #include <sevt/SEvent.h>
6 #include <sevt/EventTrigger.h>
7 #include <utl/Math.h>
8 #include <limits>
9 
10 using namespace fwk;
11 using namespace sevt;
12 using namespace utl;
13 using namespace std;
14 
15 
16 namespace SdBaselineFinderKG {
17 
18  namespace Const {
19 
20  inline
21  constexpr
22  int
23  MinLength(const bool isUUB)
24  {
25  return isUUB ? 90: 30;
26  }
27 
28 
29  inline
30  constexpr
31  unsigned int
32  UsefulBins(const bool isUUB)
33  {
34  return isUUB ? 600 : 200;
35  }
36 
37  }
38 
39 
40  // pair<> output helper
41  template<typename T1, typename T2>
42  inline
43  ostream&
44  operator<<(ostream& os, const pair<T1, T2>& p)
45  {
46  return os << '[' << p.first << ", " << p.second << ']';
47  }
48 
49 
50  template<typename T>
51  inline
52  constexpr
53  T
54  Get(const bool first, const pair<T, T>& p)
55  {
56  return first ? p.first : p.second;
57  }
58 
59 
60  // Read Parameters from XML Files
63  {
64  auto topB = CentralConfig::GetInstance()->GetTopBranch("SdBaselineFinderKG");
65 
66  topB.GetChild("numberOfIterations").GetData(fNumberOfIterations);
67 
68  auto ubParametersB = topB.GetChild("ubParameters");
69  ubParametersB.GetChild("baselineEstimationLengths").GetData(fUbBaselineEstimationLengths);
70  ubParametersB.GetChild("minimumPeakSize").GetData(fUbMinimumPeakSize);
71  ubParametersB.GetChild("negativeSignalFactors").GetData(fUbNegativeSignalFactors);
72 
73  auto ubSigmaLimitsB = ubParametersB.GetChild("sigmaLimits");
74  ubSigmaLimitsB.GetChild("upperSigma").GetData(fUbUpperSigma);
75  ubSigmaLimitsB.GetChild("lowerSigma").GetData(fUbLowerSigma);
76  ubSigmaLimitsB.GetChild("truncationSigma").GetData(fUbTruncationSigma);
77 
78  auto uubParametersB = topB.GetChild("uubParameters");
79  uubParametersB.GetChild("baselineEstimationLengths").GetData(fUubBaselineEstimationLengths);
80  uubParametersB.GetChild("minimumPeakSize").GetData(fUubMinimumPeakSize);
81  uubParametersB.GetChild("negativeSignalFactors").GetData(fUubNegativeSignalFactors);
82  uubParametersB.GetChild("decayConstant").GetData(fUubDecayConstant);
83 
84  auto uubSigmaLimitsB = uubParametersB.GetChild("sigmaLimits");
85  uubSigmaLimitsB.GetChild("upperSigma").GetData(fUubUpperSigma);
86  uubSigmaLimitsB.GetChild("lowerSigma").GetData(fUubLowerSigma);
87  uubSigmaLimitsB.GetChild("truncationSigma").GetData(fUubTruncationSigma);
88 
89  ostringstream info;
90  info << "Parameters:\n"
91  "ubParameters:\n"
92  " baselineEstimationLengths: " << fUbBaselineEstimationLengths << "\n"
93  " minimumPeakSize: " << fUbMinimumPeakSize << "\n"
94  " negativeSignalFactors: " << fUbNegativeSignalFactors << "\n"
95  "sigmaLimits:\n"
96  " upperSigma: " << fUbUpperSigma << "\n"
97  " lowerSigma: " << fUbLowerSigma << "\n"
98  " truncationSigma: " << fUbTruncationSigma << "\n"
99  "uubParameters:\n"
100  " baselineEstimationLengths: " << fUubBaselineEstimationLengths << "\n"
101  " minimumPeakSize: " << fUubMinimumPeakSize << "\n"
102  " negativeSignalFactors: " << fUubNegativeSignalFactors << "\n"
103  " decayConstant: " << fUubDecayConstant << "\n"
104  "sigmaLimits:\n"
105  " upperSigma: " << fUubUpperSigma << "\n"
106  " lowerSigma: " << fUubLowerSigma << "\n"
107  " truncationSigma: " << fUubTruncationSigma;
108  INFO(info);
109 
110  return eSuccess;
111  }
112 
113 
115  SdBaselineFinderKG::Run(evt::Event& event)
116  {
117  if (!event.HasSEvent())
118  return eSuccess;
119  auto& sEvent = event.GetSEvent();
120 
121  for (auto& station : sEvent.StationsRange()) {
122 
123  if (!station.HasTriggerData() ||
124  !station.HasCalibData() ||
125  !station.HasGPSData())
126  continue;
127 
128  const auto& trig = station.GetTriggerData();
129  if (trig.IsRandom() ||
130  (trig.GetErrorCode() & 0xff) || // for UUBs errorcodes are above 256.
131  station.GetCalibData().GetVersion() > 32000)
132  continue;
133 
134  // skip "bad compressed data"
135  if (sEvent.HasTrigger()) {
136  const int trigSecond = sEvent.GetTrigger().GetTime().GetGPSSecond();
137  const int timeDiff = int(station.GetGPSData().GetSecond()) - trigSecond;
138  if (abs(timeDiff) > 1)
139  continue;
140  }
141 
142  const auto& dStation = det::Detector::GetInstance().GetSDetector().GetStation(station);
143  const bool isUUB = dStation.IsUUB();
144 
145  fBaselineEstimationLengths = isUUB ? fUubBaselineEstimationLengths : fUbBaselineEstimationLengths;
146  fNegativeSignalFactors = isUUB ? fUubNegativeSignalFactors : fUbNegativeSignalFactors;
147  fMinimumPeakSize = isUUB ? fUubMinimumPeakSize : fUbMinimumPeakSize;
148  fUpperSigma = isUUB ? fUubUpperSigma : fUbUpperSigma;
149  fLowerSigma = isUUB ? fUubLowerSigma : fUbLowerSigma;
150  fTruncationSigma = isUUB ? fUubTruncationSigma : fUbTruncationSigma;
151 
152  if (!ComputeBaselines(station)) {
153  station.SetRejected(StationConstants::eBadCalib);
154  continue;
155  }
156 
157  }
158 
159  return eSuccess;
160  }
161 
162 
163  bool
164  SdBaselineFinderKG::ComputeBaselines(Station& station)
165  const
166  {
167  int doneSome = 0;
168  for (auto& pmt : station.PMTsRange(sdet::PMTConstants::eAnyType))
169  doneSome += ComputeBaseline(station, pmt, sdet::PMTConstants::eHighGain) +
170  ComputeBaseline(station, pmt, sdet::PMTConstants::eLowGain);
171 
172  return doneSome;
173  }
174 
175 
176  bool
177  SdBaselineFinderKG::ComputeBaseline(const Station& station,
179  const
180  {
181  if (!pmt.HasCalibData())
182  return false;
183 
184  if (!pmt.HasFADCTrace() ||
185  !pmt.GetCalibData().IsTubeOk() ||
187  return false;
188 
189  if (!pmt.HasRecData())
190  pmt.MakeRecData();
191  auto& pmtRec = pmt.GetRecData();
192 
193  if (!pmtRec.HasFADCBaseline(gain))
194  pmtRec.MakeFADCBaseline(gain);
195 
196  const bool isUUB = station.IsUUB();
197  const auto& dStation = det::Detector::GetInstance().GetSDetector().GetStation(station);
198  const auto& trace = pmt.GetFADCTrace(gain);
199  auto& baseline = pmtRec.GetFADCBaseline(gain);
200 
201  int traceLength = trace.GetSize();
202  // Fix for cyclon boards that send register data in the last 8 bins
203  // of FADC traces: solution is to set baseline equal to FADC values in
204  // these last bins so that the signal there becomes zero.
205  if (station.IsCyclone() &&
206  traceLength == int(dStation.GetFADCTraceLength())) {
207  const int cyclonBoardOffset = 8;
208  for (int i = traceLength - cyclonBoardOffset; i < traceLength; ++i)
209  baseline[i] = trace[i];
210  traceLength -= cyclonBoardOffset;
211  }
212 
213  const int baselineEstimationLength =
214  Get(pmt.GetType() != sdet::PMTConstants::eScintillator, fBaselineEstimationLengths);
215  const double negativeSignalFactor =
216  Get(gain == sdet::PMTConstants::eHighGain, fNegativeSignalFactors);
217  const double decayConstant = Get(gain == sdet::PMTConstants::eHighGain, fUubDecayConstant);
218  const double upperSigma = Get(gain == sdet::PMTConstants::eHighGain, fUpperSigma);
219 
220  const int endTracePoint = traceLength - baselineEstimationLength;
221  const int saturationValue = dStation.GetSaturationValue();
222 
223  // check for saturation
224  for (int i = 0; i < traceLength; ++i) {
225  if (trace[i] >= saturationValue) {
226  pmtRec.SetFADCSaturatedBins(-1, gain);
227  break;
228  }
229  }
230 
231  // get Initial baseline estimates for front and end baseline
232  const auto frontEstimates = ComputeBaselineEstimates(isUUB, trace, 0, baselineEstimationLength);
233  const auto endEstimates = ComputeBaselineEstimates(isUUB, trace, endTracePoint, baselineEstimationLength);
234 
235  // check if the estimated baseline is too short
236  const int minLen = Const::MinLength(isUUB);
237  if (frontEstimates[2] < minLen || endEstimates[2] < minLen) {
238  ostringstream warn;
239  warn << "Station " << station.GetId() << ", PMT " << pmt.GetId() << ", "
240  << (gain == sdet::PMTConstants::eHighGain ? "high" : "low") << " gain: "
241  "Trace too noisy, no baseline estimate possible.";
242  WARNING(warn);
243  if (gain == sdet::PMTConstants::eHighGain)
244  pmt.GetCalibData().SetIsTubeOk(false);
245  else
246  pmt.GetCalibData().SetIsLowGainOk(false);
247  return false;
248  }
249 
250  // calculate undershoot and fluctuation of baselines
251  const double deltaB = endEstimates[0] - frontEstimates[0];
252  const double sigmaDelta = sqrt(Sqr(frontEstimates[1]) + Sqr(endEstimates[1]));
253 
254  const auto first = &trace[0];
255  const auto from = &trace[baselineEstimationLength];
256  const auto to = &trace[endTracePoint];
257 
258  // decision on interpolation method
259  if (deltaB >= upperSigma * sigmaDelta) {
260  // end baseline estimate is way larger than expected
261  ostringstream warn;
262  warn << "Station " << station.GetId() << ", PMT " << pmt.GetId() << ", "
263  << (gain == sdet::PMTConstants::eHighGain ? "high" : "low") << " gain: "
264  "Anomalous baseline fluctuation, end estimate is larger than front estimate.";
265  WARNING(warn);
266  if (gain == sdet::PMTConstants::eHighGain)
267  pmt.GetCalibData().SetIsTubeOk(false);
268  else
269  pmt.GetCalibData().SetIsLowGainOk(false);
270  return false;
271  } else if (deltaB >= 0 && deltaB < upperSigma * sigmaDelta) {
272  // upwards fluctuation --> calculate robust constant
273  const auto robustConstant = ComputeBaselineEstimates(isUUB, trace, 0, traceLength);
274  for (int i = 0; i < traceLength; ++i)
275  baseline[i] = robustConstant[0];
276  } else if (deltaB >= -fLowerSigma * sigmaDelta && deltaB < 0) {
277  // small downwards fluctuation --> calculate step function
278  const int maxPointPosition = max_element(from, to) - first;
279  for (int i = 0; i < traceLength; ++i)
280  baseline[i] = (i < maxPointPosition) ? frontEstimates[0] : endEstimates[0];
281  } else {
282  // iterative procedure if maximum is large enough
283  const int maxPoint = *std::max_element(from, to);
284 
285  if (maxPoint - frontEstimates[0] > fMinimumPeakSize) {
286  // time-linear interpolation
287  for (int i = 0; i < baselineEstimationLength; ++i)
288  baseline[i] = frontEstimates[0];
289  const double db = deltaB / (endTracePoint - baselineEstimationLength);
290  for (int i = baselineEstimationLength; i < endTracePoint; ++i)
291  baseline[i] = frontEstimates[0] + (i - baselineEstimationLength) * db;
292  for (int i = endTracePoint; i < traceLength; ++i)
293  baseline[i] = endEstimates[0];
294 
295  // iterative procedure
296  const double decayFactor = std::exp(-trace.GetBinning() / decayConstant);
297  for (int k = 0; k < fNumberOfIterations; ++k) {
298  // calculate total charge
299  double totalCharge = 0;
300  for (int i = baselineEstimationLength; i < endTracePoint; ++i)
301  totalCharge += trace[i] - baseline[i];
302  if (totalCharge < 0) {
303  ostringstream warn;
304  warn << "Station " << station.GetId() << ", PMT " << pmt.GetId() << ", "
305  << (gain == sdet::PMTConstants::eHighGain ? "high" : "low") << " gain: "
306  "Failed to determine baseline, negative total charge detected.";
307  WARNING(warn);
308  if (gain == sdet::PMTConstants::eHighGain)
309  pmt.GetCalibData().SetIsTubeOk(false);
310  else
311  pmt.GetCalibData().SetIsLowGainOk(false);
312  return false;
313  }
314 
315  // iterative calculation of baseline
316  if (!isUUB) {
317  double charge = 0;
318  for (int i = baselineEstimationLength; i < endTracePoint; ++i) {
319  charge += trace[i] - baseline[i];
320  const double w = charge / totalCharge;
321  baseline[i] = frontEstimates[0] * (1 - w) + endEstimates[0] * w;
322  }
323  } else {
324  // calculation of cumulative charges (with decay kernel)
325  std::vector<double> cumulativeCharge;
326  cumulativeCharge.reserve(traceLength);
327  double charge = 0;
328  for (int i = baselineEstimationLength; i < traceLength; ++i) {
329  charge = trace[i] - baseline[i] + decayFactor * charge;
330  cumulativeCharge.push_back(charge);
331  }
332  // iterative calculation of UUB baseline
333  const double referenceCharge = cumulativeCharge[traceLength - 1.5 * baselineEstimationLength];
334  for (int i = baselineEstimationLength; i < traceLength; ++i) {
335  const double w = cumulativeCharge[i - baselineEstimationLength] / referenceCharge;
336  baseline[i] = frontEstimates[0] + deltaB * w;
337  }
338  }
339  }
340  } else {
341  // step function
342  const int maxPointPosition = max_element(from, to) - first;
343  for (int i = 0; i < traceLength; ++i)
344  baseline[i] = (i < maxPointPosition) ? frontEstimates[0] : endEstimates[0];
345  }
346  }
347  // Additional check for possible anomalous traces or poorly estimated baselines (see GAP-2022-045 and GAP-2023-007).
348  // Calculation of negative charge per bin of the trace.
349  double negativeCharge = 0;
350  for (int i = baselineEstimationLength; i < endTracePoint; ++i) {
351  const auto sig = trace[i] - baseline[i];
352  if (sig < 0)
353  negativeCharge += sig;
354  }
355  // The negative charge per bin should not exceed the RMS
356  const int interpolatedTraceLenght = traceLength - 2 * baselineEstimationLength;
357  if (negativeCharge < -negativeSignalFactor * interpolatedTraceLenght) {
358  ostringstream warn;
359  warn << "Station " << station.GetId() << ", PMT " << pmt.GetId() << ", "
360  << (gain == sdet::PMTConstants::eHighGain ? "high" : "low") << " gain: "
361  "Too much negative charge detected, trace might be anomalous.";
362  WARNING(warn);
363  if (gain == sdet::PMTConstants::eHighGain)
364  pmt.GetCalibData().SetIsTubeOk(false);
365  else
366  pmt.GetCalibData().SetIsLowGainOk(false);
367  return false;
368  }
369  return true;
370  }
371 
372 
373  /* Computes from a range of start to start+baselineLength of a trace the mean and std by truncation.
374  Something else than vector might be useful? */
375 
376  std::vector<double>
377  SdBaselineFinderKG::ComputeBaselineEstimates(const bool isUUB,
378  const TraceI& trace, const int start, const int baselineLength)
379  const
380  {
381  const int stop = start + baselineLength;
382  // make histogram of the bins in range
383  std::map<int, int> histogram;
384  int maxCount = 0;
385  for (int i = start; i < stop; ++i) {
386  const int val = trace[i];
387  const int cnt = ++histogram[val];
388  maxCount = std::max(maxCount, cnt);
389  }
390  // get mode of the bins in range
391  int mode = 0;
392  for (auto element : histogram) {
393  if (element.second == maxCount)
394  mode = element.first;
395  }
396 
397  // initialization of variables used in the while loop
398  double standardDeviation = 0;
399  double baselineMean = 0;
400  double deltaExcludedBins = 10; // if exluded bins are 0, loop ends.
401  double usedBaselineLength = baselineLength + 1; // number of used bins for baseline determination
402  int upperLimit = numeric_limits<int>::max(); // upper limit for truncation
403  int lowerLimit = 0; // lower limit for truncation
404 
405  while (deltaExcludedBins > 0) {
406 
407  double currentMean = 0;
408  double variance = 0;
409  int currentBaselineLength = 0;
410  // calculate mean for std calculation
411  for (int i = start; i < stop; ++i) {
412  const auto& ti = trace[i];
413  if (Is(ti).InRange(lowerLimit, upperLimit)) {
414  currentMean += ti;
415  ++currentBaselineLength;
416  }
417  }
418 
419  // if not enough bins for baseline calculation
420  if (currentBaselineLength < Const::MinLength(isUUB))
421  return {0, 0, 0};
422 
423  currentMean /= currentBaselineLength;
424 
425  // calculated std
426  for (int i = start; i < stop; ++i) {
427  const auto& ti = trace[i];
428  if (Is(ti).InRange(lowerLimit, upperLimit))
429  variance += Sqr(ti - currentMean);
430  }
431 
432  variance /= currentBaselineLength - 1;
433  standardDeviation = std::sqrt(variance);
434 
435  // calculate excluded bins
436  deltaExcludedBins = usedBaselineLength - currentBaselineLength;
437  usedBaselineLength = currentBaselineLength;
438 
439  // Set new exclusion limits
440  const double d = fTruncationSigma * standardDeviation;
441  upperLimit = std::ceil(mode + d);
442  lowerLimit = std::floor(mode - d);
443  baselineMean = currentMean;
444  }
445 
446  // If the error of the mean of the baseline is smaller than 0.5/sqrt(N),
447  // fall back on 0.5/sqrt(N) as conservative error.
448  const double errorOfMean = std::sqrt(Sqr(standardDeviation) / usedBaselineLength);
449  const double minimalError = 0.5 / std::sqrt(usedBaselineLength);
450  const double baselineError = (errorOfMean > minimalError) ? errorOfMean : minimalError;
451 
452  return {baselineMean, baselineError, usedBaselineLength};
453  }
454 
455 }
int GetId() const
Get the station Id.
bool HasRecData() const
Check for existenc of PMT reconstructed data object.
Definition: SEvent/PMT.h:53
constexpr T Sqr(const T &x)
class to hold data at PMT level
Definition: SEvent/PMT.h:28
IsProxy< T > Is(const T &x)
Definition: Is.h:46
bool IsTubeOk() const
Definition: PMTCalibData.h:29
PMTCalibData & GetCalibData()
Get object containing PMT calibration data.
Definition: SEvent/PMT.h:56
sdet::PMTConstants::PMTType GetType() const
Definition: SEvent/PMT.h:35
PMTRecData & GetRecData()
Get object containing PMT reconstructed data.
Definition: SEvent/PMT.h:48
bool HasCalibData() const
Check for existence of PMT calibration data object.
Definition: SEvent/PMT.h:61
#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
int gain
Definition: dump1090.h:241
void MakeFADCBaseline(const sdet::PMTConstants::PMTGain gain=sdet::PMTConstants::eHighGain)
Definition: PMTRecData.h:72
unsigned int GetId() const
Return Id of the PMT.
Definition: SEvent/PMT.h:32
void MakeRecData()
Make PMT reconstructed data object.
Definition: SEvent/PMT.cc:33
bool IsCyclone() const
#define max(a, b)
class to hold data at Station level
bool HasFADCTrace(const StationConstants::SignalComponent source=StationConstants::eTotal) const
Check if a FADC trace exists. Trace source may be specified.
Definition: SEvent/PMT.h:83
constexpr int MinLength(const bool isUUB)
double abs(const SVector< n, T > &v)
constexpr unsigned int UsefulBins(const bool isUUB)
SizeType GetSize() const
Definition: Trace.h:156
#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
bool IsUUB() const
utl::TraceI & GetFADCTrace(const sdet::PMTConstants::PMTGain gain=sdet::PMTConstants::eHighGain, const StationConstants::SignalComponent source=StationConstants::eTotal)
Definition: SEvent/PMT.h:72
bool IsLowGainOk() const
Definition: PMTCalibData.h:31
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.
void SetIsLowGainOk(const bool ok)
Definition: PMTCalibData.h:80
utl::CoordinateSystemPtr Get(const std::string &id)
Get a well-known Coordinate System.
bool HasSEvent() const
void SetIsTubeOk(const bool ok)
Definition: PMTCalibData.h:79
utl::Branch GetTopBranch(const std::string &id)
Get top branch for moduleConfigLink with given id (XML files)

, generated on Tue Sep 26 2023.