LDFFinderKG/LikelihoodFunctions.h
Go to the documentation of this file.
1 #ifndef _LDFFinderKG_LikelihoodFunctions_h_
2 #define _LDFFinderKG_LikelihoodFunctions_h_
3 
4 #include <iostream>
5 #include <cmath>
6 #include <vector>
7 #include <limits>
8 
9 #include <Minuit2/FCNBase.h>
10 
11 #include <utl/CoordinateSystemPtr.h>
12 #include <utl/Vector.h>
13 #include <utl/Point.h>
14 #include <utl/Math.h>
15 #include <utl/PhysicalFunctions.h>
16 
17 #include "LDF.h"
18 
19 
20 namespace LDFFinderKG {
21 
22  extern utl::CoordinateSystemPtr gBaryCS; // defined in LDFFinder.h
23 
24 
25  // perpendicular distance from the axis
26  inline
27  double
28  RPerp(const utl::Vector& axis, const utl::Vector& station)
29  {
30  const double scal = axis*station;
31  return std::sqrt(station*station - scal*scal);
32  }
33 
34 
35  enum CoreType {
36  eMC,
39  };
40 
41 
43  eModeled = 0,
46  };
47 
48 
49  struct StationFitData {
50  unsigned int fId = 0;
51  bool fIsSaturated = false;
53  double fSignal = 0;
54  double fRecoveredSignal = 0;
55  double fRecoveredSignalErr = 0;
56  double fCTime = 0;
57  double fT50 = 0;
58  double fGPSTimeVariance = 0;
59  };
60 
61 
62  struct LDFFitConfig {
63  bool fUseSilentStations = false;
64  bool fUseSaturationRecovery = false;
65  bool fFixCore = false;
66  double fSilentMaxRadius = 0;
69  double fRecoveryThreshold = 0;
72  std::vector<ParameterTreatment> fLDFShapeTreatment;
74  };
75 
76 
77  class LDFLikelihoodFunction : public ROOT::Minuit2::FCNBase {
78 
79  public:
81  const utl::Vector& showerAxis,
82  const std::vector<StationFitData>& data)
83  : fConfig(config), fShowerAxis(showerAxis), fData(data) { }
84 
85  double
86  operator()(const std::vector<double>& pars)
87  const
88  {
89  // One comment on the following conversion factor called PoissonFactor:
90  // The factor reflects the electron-gamma to muon ratio and gives rise to
91  // criticism of the algorithm. But as long as we would like to include
92  // "active" Zero stations, we have to assume a conversion factor. Otherwise we
93  // cannot employ a maximum likelihood method! By definion a \chi^2 method gives
94  // biased results (more strongly stated: in a mathematical sense they are
95  // wrong) !
96  // This factor has to be studied in detail by means of simulated AND real
97  // data. See Haverah Park analysis for more details (Lapikens? or England?)
98 
99  const double showerSize = pars[0];
100 
101  const utl::Point core(pars[1], pars[2], 0, gBaryCS);
102  const double cosTheta = fShowerAxis.GetCosTheta(gBaryCS);
103 
104  std::vector<double> shape(pars.begin()+3, pars.end());
105  const std::vector<double> modeledShape =
106  fConfig.fLDF.ShapeModel(cosTheta, showerSize);
107  for (unsigned int i = 0, n = shape.size(); i < n; ++i)
109  shape[i] = modeledShape[i];
110 
111  const double sigmaFactor = utl::wcd::SignalUncertaintyFactor(fConfig.fSignalVarianceModel, cosTheta);
112  const double poissonFactor = utl::wcd::PoissonFactor(sigmaFactor);
113 
114  double logLikelihood = 0;
115 
116  for (std::vector<StationFitData>::const_iterator sIt = fData.begin(), end = fData.end();
117  sIt != end; ++sIt) {
118  const utl::Vector coreToStation = sIt->fPos - core;
119  const double rho = RPerp(fShowerAxis, coreToStation);
120 
121  const double signalPrediction = showerSize * fConfig.fLDF(rho, shape); // TODO: add asymmetry
122  const double particlePrediction = poissonFactor * signalPrediction;
123 
124  if (sIt->fSignal > 0) { // candidate
125 
126  const double particlesInStation = poissonFactor * sIt->fSignal;
127  const double sigma = poissonFactor *
128  utl::wcd::SignalUncertainty(fConfig.fSignalVarianceModel, cosTheta, signalPrediction);
129 
130  if (sIt->fIsSaturated) {
131  if (fConfig.fUseSaturationRecovery && sIt->fRecoveredSignal > 0 && sIt->fRecoveredSignalErr > 0) {
132  // use recovered signal, add uncertainty of saturation recovery to signal uncertainty
133  const double n = poissonFactor * sIt->fRecoveredSignal;
134  const double dn = poissonFactor * sIt->fRecoveredSignalErr;
135  const double s = std::sqrt(utl::Sqr(sigma) + utl::Sqr(dn));
136  logLikelihood += utl::LogarithmOfNormalPDF(n, particlePrediction, s);
137  } else {
138  const double signal = (sIt->fRecoveredSignal > sIt->fSignal) ?
139  (sIt->fRecoveredSignal - sIt->fRecoveredSignalErr) : sIt->fSignal;
140  const double n = poissonFactor * signal;
141  logLikelihood += utl::LogarithmOfNormalComplementCDF(n, particlePrediction, sigma);
142  }
143  } else {
144  if (particlesInStation > 30) {
145 
146  // Stations with large signals: approximately gaussian probability
147  logLikelihood += utl::LogarithmOfNormalPDF(particlesInStation, particlePrediction, sigma);
148 
149  } else {
150 
151  // Stations with low signal: Poisson probability
152  logLikelihood += particlesInStation * std::log(particlePrediction) - particlePrediction;
153 
154  double logarithmicFactor = 0;
155  const int stop = int(particlesInStation + 0.5);
156  for (int j = 1; j <= stop; ++j)
157  logarithmicFactor += std::log(double(j));
158 
159  logLikelihood -= logarithmicFactor;
160 
161  }
162  }
163  } else if (fConfig.fUseSilentStations) { // silent stations
164 
165  logLikelihood += particlePrediction > 0.03 ?
166  -particlePrediction +
167  std::log(1 + // 0
168  particlePrediction * (1 + // 1
169  0.5*particlePrediction * (1 + // 2
170  (1./3)*particlePrediction // 3
171  )))
172  :
173  -(1./24)*utl::Sqr(utl::Sqr(particlePrediction));
174 
175  // this threshold above is too large
176  // it should be replaced with the 1 - trigger probability but depending on
177  // whether new triggers are silenced or not...
178 
179  }
180 
181  } // loop over stations
182 
183  // max(LogLikelihood) = min(-LogLikelihood)
184  return std::isnan(logLikelihood) ? std::numeric_limits<double>::max() : -logLikelihood;
185  }
186 
187  double Up() const { return 0.5; }
188 
189  protected:
192  const std::vector<StationFitData>& fData;
193 
194  };
195 
196 
197  class CurvatureChi2Function : public ROOT::Minuit2::FCNBase {
198 
199  public:
201  const std::vector<StationFitData>& data)
202  : fCore(core), fData(data), fTimeVarModel(sdet::STimeVariance::GetInstance()) { }
203 
204  double
205  operator()(const std::vector<double>& pars)
206  const
207  {
208  const double& pu = pars[0];
209  const double& pv = pars[1];
210  const double& rCore = pars[2];
211  const double& cTCore = pars[3];
212 
213  const double pw = std::sqrt(1 + pu*pu + pv*pv);
214  const double u = pu/pw;
215  const double v = pv/pw;
216 
217  const double w = std::sqrt(1 - u*u - v*v);
218 
219  const utl::Vector axis(u,v,w, gBaryCS);
220 
221  double chi2 = 0;
222 
223  for (const auto& st : fData) {
224  if (!st.fSignal)
225  continue;
226 
227  const utl::Vector stationToCore = fCore - st.fPos;
228  const utl::Vector stationToCenter = stationToCore + rCore*axis;
229  const double rStation = stationToCenter.GetMag();
230 
231  const double cosThetaStation = stationToCenter.GetZ(gBaryCS) / rStation;
232 
233  const double sigma2 =
234  fTimeVarModel.GetTimeSigma2(st.fSignal, st.fT50, cosThetaStation, RPerp(axis, stationToCore)) +
235  st.fGPSTimeVariance;
236 
237  const double cdt = (st.fCTime - rStation) - (cTCore - rCore);
238  chi2 += utl::Sqr(cdt) / sigma2;
239  }
240 
241  return std::isnan(chi2) ? std::numeric_limits<double>::max() : chi2 / utl::Sqr(utl::kSpeedOfLight);
242  }
243 
244  double Up() const { return 1; }
245 
246  protected:
248  const std::vector<StationFitData>& fData;
250 
251  };
252 
253 
254  class GlobalFitFunction : public ROOT::Minuit2::FCNBase {
255 
256  public:
257  GlobalFitFunction(const LDFFitConfig& config, const std::vector<StationFitData>& data)
258  : fCurvPart(fCore, data), fLDFPart(config, fShowerAxis, data) { }
259 
260  double
261  operator()(const std::vector<double>& pars)
262  const
263  {
264  const double u = pars[0];
265  const double v = pars[1];
266  const double w = std::sqrt(1 - u*u - v*v);
267 
268  fShowerAxis = utl::Vector(u, v, w, gBaryCS);
269 
270  fCore = utl::Point(pars[5], pars[6], 0, gBaryCS);
271 
272  const std::vector<double> curvPars(pars.begin(), pars.begin()+4);
273  const std::vector<double> ldfPars(pars.begin()+4, pars.end());
274  return 0.5*fCurvPart(curvPars) + fLDFPart(ldfPars);
275  }
276 
277  double Up() const { return 0.5; }
278 
279  protected:
281  mutable utl::Point fCore;
284 
285  };
286 
287 
288  class LDFChi2Function : public ROOT::Minuit2::FCNBase {
289 
290  public:
292  const utl::Vector& showerAxis,
293  const std::vector<StationFitData>& data)
294  : fConfig(config), fShowerAxis(showerAxis), fData(data) { }
295 
296  double
297  operator()(const std::vector<double>& pars)
298  const
299  {
300  const double showerSize = pars[0];
301 
302  const utl::Point core(pars[1], pars[2], 0, gBaryCS);
303 
304  const double cosTheta = fShowerAxis.GetCosTheta(gBaryCS);
305  std::vector<double> shape(pars.begin()+3, pars.end());
306  const std::vector<double> modeledShape =
307  fConfig.fLDF.ShapeModel(cosTheta, showerSize);
308  for (unsigned int i = 0, n = shape.size(); i < n; ++i)
310  shape[i] = modeledShape[i];
311 
312  double chi2 = 0;
313 
314  for (std::vector<StationFitData>::const_iterator sIt = fData.begin(), end = fData.end();
315  sIt != end; ++sIt) {
316  const double rho = RPerp(fShowerAxis, sIt->fPos - core);
317 
318  if (sIt->fSignal > 0) { // candidate
319  const double predictedSignal = showerSize * fConfig.fLDF(rho, shape);
320  const bool useRecovery = fConfig.fUseSaturationRecovery &&
321  sIt->fRecoveredSignal > 0 &&
322  sIt->fRecoveredSignalErr > 0;
323 
324  if (sIt->fIsSaturated && !useRecovery)
325  continue;
326 
327  const double signal = sIt->fIsSaturated ? sIt->fRecoveredSignal : sIt->fSignal;
328  const double sigmaNotSat = utl::wcd::SignalUncertainty(fConfig.fSignalVarianceModel, cosTheta, predictedSignal);
329 
330  const double sigma = sIt->fIsSaturated ?
331  std::sqrt(utl::Sqr(sIt->fRecoveredSignalErr) + utl::Sqr(sigmaNotSat)) : sigmaNotSat;
332 
333  // we do NOT add any contribution to the chi2, as it would be a bias.
334  const double dev = (signal - predictedSignal) / sigma;
335  chi2 += utl::Sqr(dev);
336  } else if (fConfig.fUseSilentStations) { // silent
337  // Warning: the treatment of silent stations in the Chi2 is deprecated
338  // and only kept for making comparisons with CDAS
339  chi2 +=
341  showerSize * fConfig.fLDF(rho, shape) / fConfig.fSilentSignalThreshold;
342  }
343  } // loop over stations
344 
345  return std::isnan(chi2) ? std::numeric_limits<double>::max() : chi2;
346  }
347 
348  double Up() const { return 1; }
349 
350  protected:
353  const std::vector<StationFitData>& fData;
354 
355  };
356 
357 
358 }
359 
360 
361 #endif
double LogarithmOfNormalComplementCDF(const double x)
constexpr T Sqr(const T &x)
Point object.
Definition: Point.h:32
double RPerp(const utl::Vector &axis, const utl::Vector &station)
double GetCosTheta(const CoordinateSystemPtr &coordinateSystem) const
cos of zenith (theta) angle
Definition: BasicVector.h:251
double GetMag() const
Definition: Vector.h:58
std::vector< double > ShapeModel(const double cosTheta, const double showerSize) const
utl::CoordinateSystemPtr gBaryCS
const std::vector< StationFitData > & fData
boost::shared_ptr< const CoordinateTransformer > CoordinateSystemPtr
Shared pointer for coordinate systems.
#define max(a, b)
double Fermi(const double x)
constexpr double s
Definition: AugerUnits.h:163
GlobalFitFunction(const LDFFitConfig &config, const std::vector< StationFitData > &data)
LDFLikelihoodFunction(const LDFFitConfig &config, const utl::Vector &showerAxis, const std::vector< StationFitData > &data)
const std::vector< StationFitData > & fData
std::vector< ParameterTreatment > fLDFShapeTreatment
CurvatureChi2Function(const utl::Point &core, const std::vector< StationFitData > &data)
constexpr double kSpeedOfLight
double operator()(const std::vector< double > &pars) const
const std::vector< StationFitData > & fData
double SignalUncertainty(const ESignalVarianceModel model, const double cosTheta, const double signal)
utl::wcd::ESignalVarianceModel fSignalVarianceModel
rtlsdr_dev_t * dev
Definition: dump1090.h:243
uint16_t * data
Definition: dump1090.h:228
Vector object.
Definition: Vector.h:30
double operator()(const std::vector< double > &pars) const
double SignalUncertaintyFactor(const ESignalVarianceModel model, const double cosTheta)
LDFChi2Function(const LDFFitConfig &config, const utl::Vector &showerAxis, const std::vector< StationFitData > &data)
double GetZ(const CoordinateSystemPtr &coordinateSystem) const
Definition: BasicVector.h:212
double LogarithmOfNormalPDF(const double x)
double PoissonFactor(const double sigmaFactor)
double operator()(const std::vector< double > &pars) const
double operator()(const std::vector< double > &pars) const
double GetTimeSigma2(const double signal, const double t50, const double cosTheta, const double distance=0) const

, generated on Tue Sep 26 2023.