ScintillatorLDFFinderKG/LikelihoodFunctions.h
Go to the documentation of this file.
1 #ifndef _ScintillatorLDFFinderKG_LikelihoodFunctions_h_
2 #define _ScintillatorLDFFinderKG_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 ScintillatorLDFFinderKG {
21 
22  extern utl::CoordinateSystemPtr gBaryCS; // defined in ScintillatorLDFFinder.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  inline
35  double
36  DistanceCut(const double showerSize, const double cosTheta)
37  {
38  const double s = std::log10(showerSize);
39  const double sec = 1. / cosTheta;
40 
41  const double f0 = 303.242;
42  const double f1 = -355.686;
43  const double f2 = 171.659;
44  const double f3 = 586.443;
45  const double f4 = 1309.454;
46  const double f5 = -544.919;
47  const double f6 = 156.465;
48  const double f7 = -558.186;
49  const double f8 = 236.191;
50 
51  const double distcut = f0 + f1*sec + f2*sec*sec
52  + (f3 + f4*sec + f5*sec*sec)*s
53  + (f6 + f7*sec + f8*sec*sec)*s*s;
54 
55  return distcut;
56  }
57 
59  eModeled = 0,
62  };
63 
64 
65  struct StationFitData {
66  unsigned int fId = 0;
67  bool fIsSaturated = false;
69  double fSignal = 0;
70  };
71 
72 
73  struct LDFFitConfig {
74  // add use silent
75  bool fFixCore;
78  std::vector<ParameterTreatment> fLDFShapeTreatment;
80  double deltaR;
81  };
82 
83 
84  class LDFLikelihoodFunction : public ROOT::Minuit2::FCNBase {
85 
86  public:
88  const utl::Vector& showerAxis,
89  const std::vector<StationFitData>& data)
90  : fConfig(config), fShowerAxis(showerAxis), fData(data) { }
91 
92  double
93  operator()(const std::vector<double>& pars)
94  const
95  {
96 
97  /*
98  The concept of Poisson Factor is also used for scintillator signals in order to obtain Poisson p.d.f.s for small signals.
99  The conversion from signal to effective number of particles is given by:
100 
101  Neff = p*S,
102 
103  where the factor p is derived in such a way that the observed signal uncertainties are formulated in terms of an effective particle number
104  under a Poisson assumption (see SD reconstrucion manual GAP2005_035).
105 
106  */
107 
108  const double showerSize = pars[0];
109 
110  const utl::Point core(pars[1], pars[2], 0, gBaryCS);
111  const double cosTheta = fShowerAxis.GetCosTheta(gBaryCS);
112 
113  std::vector<double> shape(pars.begin()+3, pars.end());
114  const std::vector<double> modeledShape =
115  fConfig.fLDF.ShapeModel(cosTheta, showerSize);
116  for (unsigned int i = 0, n = shape.size(); i < n; ++i)
118  shape[i] = modeledShape[i];
119 
120  const double sigmaFactor = utl::ssd::SignalUncertaintyFactor(fConfig.fSignalVarianceModel, cosTheta);
121  const double poissonFactor = utl::ssd::PoissonFactor(sigmaFactor);
122 
123  double logLikelihood = 0;
124 
125  for (std::vector<StationFitData>::const_iterator sIt = fData.begin(), end = fData.end();
126  sIt != end; ++sIt) {
127  const utl::Vector coreToStation = sIt->fPos - core;
128  const double rho = RPerp(fShowerAxis, coreToStation);
129 
130  const double signalPrediction = showerSize * fConfig.fLDF(rho, shape);
131  const double particlePrediction = poissonFactor * signalPrediction;
132 
133  if (sIt->fSignal > 0) {
134 
135  const double particlesInScintillator = poissonFactor * sIt->fSignal;
136  const double sigma = poissonFactor *
137  utl::ssd::SignalUncertainty(fConfig.fSignalVarianceModel, cosTheta, signalPrediction);
138 
139  if (sIt->fIsSaturated) // ToDo: treat saturated SSDs
140  continue;
141 
142  if (particlePrediction > 30) {
143  // large signals: approximately gaussian probability
144  logLikelihood += utl::LogarithmOfNormalPDF(particlesInScintillator, particlePrediction, sigma);
145  } else {
146  // low signals: Poisson probability
147  logLikelihood += particlesInScintillator * std::log(particlePrediction) - particlePrediction;
148  double logarithmicFactor = 0;
149  const int stop = int(particlesInScintillator + 0.5);
150  for (int j = 1; j <= stop; ++j)
151  logarithmicFactor += std::log(double(j));
152 
153  logLikelihood -= logarithmicFactor;
154 
155  }
156  } // if Signal > 0
157  } // loop over stations
158 
159  return std::isnan(logLikelihood) ? std::numeric_limits<double>::max() : -logLikelihood;
160  }
161 
162  double Up() const { return 0.5; }
163 
164  protected:
167  const std::vector<StationFitData>& fData;
168 
169  };
170 
171 class LDFLikelihoodFunctionTN : public ROOT::Minuit2::FCNBase {
172 
173  public:
175  const utl::Vector& showerAxis,
176  const std::vector<StationFitData>& data)
177  : fConfig(config), fShowerAxis(showerAxis), fData(data) { }
178 
179  double
180  operator()(const std::vector<double>& pars)
181  const
182  {
183 
184  /*
185  Usage of a truncated normal distribution for describing SSD signals.
186  As SSD is triggered by the WCD, SSD signals can be low as to integral
187  of baseline (~0 MIPs). A truncated normal distribution was proposed
188  as a p.d.f of the SSD signals and a corresponding likelihood function
189  is derived under the assumption that such p.d.f should work for both
190  small and large signals. No Poisson factor is needed.
191  */
192 
193  const double showerSize = pars[0];
194 
195  const utl::Point core(pars[1], pars[2], 0, gBaryCS);
196  const double cosTheta = fShowerAxis.GetCosTheta(gBaryCS);
197 
198  std::vector<double> shape(pars.begin()+3, pars.end());
199  const std::vector<double> modeledShape =
200  fConfig.fLDF.ShapeModel(cosTheta, showerSize);
201 
202  for (unsigned int i = 0, n = shape.size(); i < n; ++i)
204  shape[i] = modeledShape[i];
205 
206  double logLikelihood = 0;
207 
208  for (std::vector<StationFitData>::const_iterator sIt = fData.begin(), end = fData.end();
209  sIt != end; ++sIt) {
210  const utl::Vector coreToStation = sIt->fPos - core;
211  const double rho = RPerp(fShowerAxis, coreToStation);
212  const double signal = sIt->fSignal;
213  const double signalPrediction = showerSize * fConfig.fLDF(rho, shape);
214 
215  /*
216  Zero and negative signals should be properly handled in the likelihood.
217  We keep this like that until signal uncertainty and likelihood are
218  well understood for small signals. Until then, a distance cut is applied
219  to reject low or zero signals according to a parameterization in
220  lgS and sec theta.
221  */
222 
223  if (signal > 0) {
224 
225  double sigma = utl::ssd::SignalUncertainty(fConfig.fSignalVarianceModel, cosTheta, signalPrediction);
226  /*
227  If desired, the core uncertainties deltaR can be propagated via Gaussian error propagation as sigmaR
228  to the signal uncertainty.
229  */
230  const bool corePropagationEnabled = fConfig.fCorePropagationEnabled;
231  if (corePropagationEnabled){
232  const double deltaR = fConfig.deltaR;
233  const double sigmaR = deltaR * showerSize * fConfig.fLDF.FirstDerivative(rho,shape);
234  sigma = std::sqrt(utl::Sqr(sigma) + utl::Sqr(sigmaR));
235  }
236 
237  /*
238  Current LDF parameterization was determined using no saturated SSDs.
239  This would need further studies in the future.
240  */
241  if (sIt->fIsSaturated)
242  continue;
243 
244  logLikelihood += - 0.5*std::log(2*M_PI*utl::Sqr(sigma))
245  - 0.5*utl::Sqr((signal - signalPrediction) / sigma)
246  - std::log(0.5*(1 + std::erfc(signalPrediction / (std::sqrt(2)*sigma))));
247 
248  } // if Signal > 0
249  } // loop over stations
250 
251  return std::isnan(logLikelihood) ? std::numeric_limits<double>::max() : -logLikelihood;
252  }
253 
254  double Up() const { return 0.5; }
255 
256  protected:
259  const std::vector<StationFitData>& fData;
260 
261  };
262 
263 class LDFChi2Function : public ROOT::Minuit2::FCNBase {
264 
265  public:
267  const utl::Vector& showerAxis,
268  const std::vector<StationFitData>& data)
269  : fConfig(config), fShowerAxis(showerAxis), fData(data) { }
270 
271  double
272  operator()(const std::vector<double>& pars)
273  const
274  {
275  const double showerSize = pars[0];
276 
277  const utl::Point core(pars[1], pars[2], 0, gBaryCS);
278 
279  const double cosTheta = fShowerAxis.GetCosTheta(gBaryCS);
280  std::vector<double> shape(pars.begin()+3, pars.end());
281  const std::vector<double> modeledShape =
282  fConfig.fLDF.ShapeModel(cosTheta, showerSize);
283  for (unsigned int i = 0, n = shape.size(); i < n; ++i)
285  shape[i] = modeledShape[i];
286 
287  double chi2 = 0;
288 
289  for (std::vector<StationFitData>::const_iterator sIt = fData.begin(), end = fData.end();
290  sIt != end; ++sIt) {
291  const double rho = RPerp(fShowerAxis, sIt->fPos - core);
292 
293  if (sIt->fSignal > 0) { // candidate
294  const double predictedSignal = showerSize * fConfig.fLDF(rho, shape);
295  if (sIt->fIsSaturated)
296  continue;
297 
298  const double signal = sIt->fSignal;
299  const double sigma = utl::ssd::SignalUncertainty(fConfig.fSignalVarianceModel, cosTheta, predictedSignal);
300 
301  // we do NOT add any contribution to the chi2, as it would be a bias.
302  const double dev = (signal - predictedSignal) / sigma;
303  chi2 += utl::Sqr(dev);
304  } // signal > 0
305  } // loop over stations
306 
307  return std::isnan(chi2) ? std::numeric_limits<double>::max() : chi2;
308  }
309 
310  double Up() const { return 1; }
311 
312  protected:
315  const std::vector<StationFitData>& fData;
316 
317  };
318 }
319 
320 #endif
LDFLikelihoodFunctionTN(const LDFFitConfig &config, const utl::Vector &showerAxis, const std::vector< StationFitData > &data)
constexpr T Sqr(const T &x)
Point object.
Definition: Point.h:32
LDFLikelihoodFunction(const LDFFitConfig &config, const utl::Vector &showerAxis, const std::vector< StationFitData > &data)
std::vector< double > ShapeModel(const double cosTheta, const double showerSize) const
double GetCosTheta(const CoordinateSystemPtr &coordinateSystem) const
cos of zenith (theta) angle
Definition: BasicVector.h:251
double RPerp(const utl::Vector &axis, const utl::Vector &station)
double operator()(const std::vector< double > &pars) const
boost::shared_ptr< const CoordinateTransformer > CoordinateSystemPtr
Shared pointer for coordinate systems.
#define max(a, b)
constexpr double s
Definition: AugerUnits.h:163
double SignalUncertaintyFactor(const ESignalVarianceModel model, const double cosTheta)
LDFChi2Function(const LDFFitConfig &config, const utl::Vector &showerAxis, const std::vector< StationFitData > &data)
utl::CoordinateSystemPtr gBaryCS
rtlsdr_dev_t * dev
Definition: dump1090.h:243
double DistanceCut(const double showerSize, const double cosTheta)
double PoissonFactor(const double sigmaFactor)
uint16_t * data
Definition: dump1090.h:228
Vector object.
Definition: Vector.h:30
double SignalUncertainty(const ESignalVarianceModel model, const double cosTheta, const double signal)
double FirstDerivative(const double r, const std::vector< double > &shape) const
double LogarithmOfNormalPDF(const double x)
double operator()(const std::vector< double > &pars) const

, generated on Tue Sep 26 2023.