EnergyFitter.cc
Go to the documentation of this file.
1 #include "EnergyFitter.h"
2 #include "diagonalMatrix.h"
7 
8 #include <evt/Event.h>
9 #include <evt/ShowerRecData.h>
10 #include <evt/ShowerFRecData.h>
11 #include <evt/GaisserHillas4Parameter.h>
12 
13 #include <fevt/Eye.h>
14 #include <fevt/Telescope.h>
15 #include <fevt/Pixel.h>
16 #include <fevt/FEvent.h>
17 #include <fevt/EyeRecData.h>
18 
19 #include <fwk/CentralConfig.h>
20 
21 #include <utl/Reader.h>
22 #include <utl/AugerUnits.h>
23 #include <utl/ErrorLogger.h>
24 #include <utl/MathConstants.h>
25 #include <utl/PhysicalConstants.h>
26 #include <utl/PhysicalFunctions.h>
27 #include <utl/TabulatedFunctionErrors.h>
28 
29 #include <det/Detector.h>
30 #include <fdet/FDetector.h>
31 #include <fdet/Camera.h>
32 #include <fdet/Telescope.h>
33 #include <fdet/Pixel.h>
34 
35 #include <string>
36 #include <sstream>
37 #include <iostream>
38 #include <iomanip>
39 
40 #include <TMinuit.h>
41 #include <TMath.h>
42 
43 using namespace utl;
44 using namespace evt;
45 using namespace fevt;
46 using namespace fwk;
47 using namespace det;
48 using namespace fdet;
49 using namespace oBLAS;
50 using namespace std;
51 
52 
53 namespace FdProfileReconstructorKG {
54 
55  // static stuff for MINUIT
56 
57  EnergyFitter::EMinimizationType EnergyFitter::fMinimizationMethod = eModifiedLeastSquares;
58  EnergyFitter::EParConstraints EnergyFitter::fFixX0 = eFree;
59  EnergyFitter::EParConstraints EnergyFitter::fFixLambda = eFree;
60  EnergyFitter::EParConstraints EnergyFitter::fFixkUniv = eFree;
61  unsigned int EnergyFitter::fnBins = 0;
62  double* EnergyFitter::fdEdX = nullptr;
63  double* EnergyFitter::fvdEdX = nullptr;
64  double* EnergyFitter::fLight = nullptr;
65  double* EnergyFitter::fvLight = nullptr;
66  double* EnergyFitter::fvBackGround = nullptr;
67  double* EnergyFitter::fDepth = nullptr;
68  double* EnergyFitter::fChFlDepth = nullptr;
69  double EnergyFitter::fDefX0 = 0;
70  double EnergyFitter::fVarX0 = 0;
71  double EnergyFitter::fDefLambda = 0;
72  double EnergyFitter::fVarLambda = 0;
73 
74  double EnergyFitter::fDefA1 = 0;
75  double EnergyFitter::fDefA2 = 0;
76  double EnergyFitter::fVarK = 0;
77 
78  const oBLAS::lowerTriangularMatrix* EnergyFitter::fChFl = nullptr;
79  double EnergyFitter::fDiaphragmArea = 0;
80  double EnergyFitter::fGainVariance = 0;
81  double EnergyFitter::fPhotonToPhotoElectron = 0;
82 
83 
84  bool
86  {
87  Branch topB = fwk::CentralConfig::GetInstance()->GetTopBranch("FdProfileReconstructor");
88  Branch eCalcB = topB.GetChild("profileFitting");
89 
90  Branch x0Branch = eCalcB.GetChild("X0");
91  auto fitStrategy = x0Branch.GetChild("strategy").Get<string>();
92  fFixX0 = (fitStrategy == "constrained") ? eConstrained : ((fitStrategy == "free") ? eFree : eFixed);
93  x0Branch.GetChild("mean").GetData(fDefX0);
94  fDefX0 /= g/cm2;
95  const auto x0RMS = x0Branch.GetChild("sigma").Get<double>();
96  fVarX0 = Sqr(x0RMS / (g/cm2));
97 
98  Branch lambdaBranch = eCalcB.GetChild("lambda");
99  fitStrategy = lambdaBranch.GetChild("strategy").Get<string>();
100  fFixLambda = (fitStrategy == "constrained") ? eConstrained : ((fitStrategy == "free") ? eFree : eFixed);
101  lambdaBranch.GetChild("mean").GetData(fDefLambda);
102  fDefLambda /= g/cm2;
103  const double lambdaRMS = lambdaBranch.GetChild("sigma").Get<double>();
104  fVarLambda = Sqr(lambdaRMS / (g/cm2));
105 
106  Branch kUnivBranch = eCalcB.GetChild("kUniv");
107  fitStrategy = kUnivBranch.GetChild("strategy").Get<string>();
108  fFixkUniv = (fitStrategy == "constrained") ? eConstrained : ((fitStrategy == "free") ? eFree : eFixed);
109  kUnivBranch.GetChild("A1").GetData(fDefA1);
110  fDefA1 /= g/cm2;
111  kUnivBranch.GetChild("A2").GetData(fDefA2);
112  fDefA2 /= g/cm2;
113  const auto kRMS = kUnivBranch.GetChild("sigma").Get<double>();
114  fVarK = Sqr(kRMS / (g/cm2));
115 
116  eCalcB.GetChild("verbosity").GetData(fVerbosity);
117 
118  const auto miniMeth = eCalcB.GetChild("minimizationMethod").Get<string>();
119  if (miniMeth == "maximumLikelihood")
120  fMinimizationMethod = eMaximumLikelihood;
121  else if (miniMeth == "leastSquares")
122  fMinimizationMethod = eLeastSquares;
123  else if (miniMeth == "modifiedLeastSquares")
124  fMinimizationMethod = eModifiedLeastSquares;
125 
126  if (fVerbosity > -1) {
127  std::ostringstream info;
128  info << "\n\t X0 is ";
129  switch (fFixX0) {
130  case eFree:
131  info << "free\n";
132  break;
133  case eFixed:
134  info << "fixed at " << fDefX0 << " g/cm2\n";
135  break;
136  default:
137  info << "constrained at " << fDefX0 << " +/- " << sqrt(fVarX0) << " g/cm2\n";
138  break;
139  }
140 
141  info << "\t lambda is ";
142  switch (fFixLambda) {
143  case eFree:
144  info << "free\n";
145  break;
146  case eFixed:
147  info << "fixed at " << fDefLambda << " g/cm2\n";
148  break;
149  default:
150  info << "constrained at " << fDefLambda << " +/- " << sqrt(fVarLambda) << " g/cm2\n";
151  break;
152  }
153 
154  info << "\t kUniv is ";
155  switch (fFixkUniv) {
156  case eFree:
157  info << "free\n";
158  break;
159  case eFixed:
160  info << "fixed at " << fDefA1 << " g/cm2, " << fDefA2 << " g/cm2\n";
161  break;
162  default:
163  info << "constrained at (" << fDefA1 << " " << fDefA2 << ") +/- " << sqrt(fVarK) << " g/cm2\n";
164  break;
165  }
166 
167  info << "\t minimization method: " << miniMeth;
168 
169  INFO(info);
170  }
171 
172  return true;
173  }
174 
175 
176  // determines energy for each eye of "event"
177  bool
178  EnergyFitter::Run(fevt::Eye& eye, CherenkovFluorescenceMatrix* const chfl)
179  {
180  fChFlPtr = chfl;
181 
182  if (InitializeGHFit(eye, 0) && // starting parameters
183  FitProfile(eEnergyDeposit) && // fit two-parameter GH to dEdX profile
184  FitProfile(eLightAllParam)) { // fit four-parameter GH to light profile
185 
186  FillProfilesAtAperture(eye);
187 
188  if (CalculateEnergy(eye)) {
189  CleanUp();
190  return true;
191  }
192  }
193  CleanUp();
194  return false;
195  }
196 
197 
208  bool
209  EnergyFitter::InitializeGHFit(const fevt::Eye& eye, const int iMode)
210  {
211  // Cleanup() ?
212  fdEdX = nullptr;
213  fvdEdX = nullptr;
214  fDepth = nullptr;
215  fLight = nullptr;
216  fvLight = nullptr;
217  fvBackGround = nullptr;
218  fChFl = nullptr;
219  fChFlDepth = nullptr;
220  fChisqGH = 0;
221  fnDof = 0;
222 
223  const auto* depthPtr = fChFlPtr->GetDepth();
224  if (!depthPtr || depthPtr->size() < 1)
225  return false;
226 
227  const auto& depth = *depthPtr;
228 
229  fChFlDepth = new double[depth.size()];
230  for (unsigned int i = 0, n = depth.size(); i < n; ++i)
231  fChFlDepth[i] = depth[i];
232 
233  // fill light profile
234  if (!eye.GetRecData().HasLightFlux())
235  return false;
236 
237  const auto& lightFlux = eye.GetRecData().GetLightFlux(fevt::FdConstants::eTotal);
238  const auto& bgFlux = eye.GetRecData().GetLightFlux(fevt::FdConstants::eBackground);
239 
240  fnBins = lightFlux.GetNPoints();
241  fLight = new double[fnBins];
242  fvLight = new double[fnBins];
243  fvBackGround = new double[fnBins];
244  for (unsigned int i = 0; i < fnBins; ++i) {
245  fLight[i] = lightFlux.GetY(i);
246  fvLight[i] = Sqr(lightFlux.GetYErr(i));
247  fvBackGround[i] = Sqr(bgFlux.GetYErr(i));
248  }
249 
250  // fill dEdX profile
251  const auto& recShower = eye.GetRecData().GetFRecShower();
252  if (!recShower.HasEnergyDeposit())
253  return false;
254 
255  const auto& dedxProf = recShower.GetEnergyDeposit();
256 
257  if (fnBins != dedxProf.GetNPoints()) {
258  ostringstream err;
259  err << "Inconsistent binning dedx/light: " << fnBins
260  << ' ' << dedxProf.GetNPoints();
261  ERROR(err);
262  return false;
263  }
264 
265  fdEdX = new double[fnBins];
266  fvdEdX = new double[fnBins];
267  fDepth = new double[fnBins];
268 
269  // work with decent units
270  const double kXconvert = g/cm2;
271  const double kEconvert = PeV/kXconvert;
272  const double kEconvert2 = Sqr(kEconvert);
273 
274  for (unsigned int i = 0; i < fnBins; ++i) {
275  fdEdX[i] = dedxProf.GetY(i) / kEconvert;
276  fvdEdX[i] = dedxProf.GetYErr(i) * dedxProf.GetYErr(i) / kEconvert2;
277  fDepth[i] = dedxProf.GetX(i) / kXconvert;
278  }
279 
280  const auto& detFD = det::Detector::GetInstance().GetFDetector();
281  const double referenceLambda = detFD.GetReferenceLambda();
282 
283  for (auto tIt = eye.TelescopesBegin(ComponentSelector::eHasData),
284  end = eye.TelescopesEnd(ComponentSelector::eHasData);
285  tIt != end; ++tIt) {
286  double avgPEfactor = 0;
287  int countPix = 0;
288  for (auto pIt = tIt->PixelsBegin(ComponentSelector::eHasData),
289  end = tIt->PixelsEnd(ComponentSelector::eHasData);
290  pIt != end; ++pIt) {
291  const auto& detPixel = detFD.GetPixel(*pIt);
292  avgPEfactor += detPixel.GetDiaPhoton2PEFactor(referenceLambda);
293  ++countPix;
294  }
295 
296  if (countPix)
297  avgPEfactor /= countPix;
298 
299  const auto& detTel = detFD.GetTelescope(*tIt);
300  fDiaphragmArea = detTel.GetDiaphragmArea();
301  fGainVariance = detTel.GetCamera().GetGainVariance();
302  fPhotonToPhotoElectron = avgPEfactor;
303  break;
304  }
305 
306  if (iMode > 0)
307  return true;
308 
309  // combine to kAve bins for estimate of dEdXmax and Xmax
310  const int kAve = 10;
311  int nAv = int(double(fnBins) / kAve);
312  if (nAv < 1)
313  nAv = 1;
314 
315  bool firstTime = true;
316  double xMax = -1;
317  double dEdXmax = -1;
318  for (unsigned int i = 0; i < fnBins - nAv; ++i) {
319  double eSum = 0;
320  double wSum = 0;
321  double xSum = 0;
322  for (unsigned int j = i; j < i + nAv; ++j) {
323  const double w = 1 / fvdEdX[j];
324  eSum += fdEdX[j] * w;
325  wSum += w;
326  xSum += fDepth[j];
327  }
328 
329  const double eM = eSum / wSum;
330  const double xM = xSum / nAv;
331  if (firstTime || eM > dEdXmax) {
332  firstTime = false;
333  dEdXmax = eM;
334  xMax = xM;
335  }
336  }
337 
338  if (xMax < 0) {
339  if (fVerbosity > -1)
340  ERROR("InitializeGHFit - no initial values found! "
341  "Shouldn't happen! This is only a sanity check!");
342  return false;
343  } else {
344  fXmax = xMax;
345  fdEdXmax = dEdXmax;
346  fX0 = fDefX0;
347  fLambda = fDefLambda;
348 
349  if (fVerbosity > -1)
350  DumpCurrentParameters(eInitialize);
351 
352  return true;
353  }
354  }
355 
356 
357  // recalculates contributions at aperture and dEdX
358 
359  void
360  EnergyFitter::FillProfilesAtAperture(fevt::Eye& eye)
361  {
362  const double kXconvert = g/cm2;
363  const double kEconvert = PeV/kXconvert;
364 
365  // a) light at aperture
366  const unsigned int mSiz = fChFl->size1();
367  ublas::vector<double> dEdX(mSiz);
368 
369  for (unsigned int i = 0; i < mSiz; ++i) {
370  const double x = fChFlDepth[i] / kXconvert;
371  dEdX(i) = GaisserHillas(x, fX0, fXmax, fdEdXmax, fLambda) * kEconvert;
372  }
373 
374  const auto vFluo = ublas::prod(*fChFlPtr->GetFluorescenceMatrix(), dEdX);
375  const auto vChDir = ublas::prod(*fChFlPtr->GetDirectCherenkovMatrix(), dEdX);
376  const auto vChMScat = ublas::prod(*fChFlPtr->GetMieScatteredCherenkovMatrix(), dEdX);
377  const auto vChRScat = ublas::prod(*fChFlPtr->GetRayleighScatteredCherenkovMatrix(), dEdX);
378 
379  auto& eyeRecData = eye.GetRecData();
380  const auto& lightFlux = eyeRecData.GetLightFlux(fevt::FdConstants::eTotal);
381  auto& dirCher = eyeRecData.GetLightFlux(fevt::FdConstants::eCherDirect);
382  dirCher.Clear();
383  auto& scatMCher = eyeRecData.GetLightFlux(fevt::FdConstants::eCherMieScattered);
384  scatMCher.Clear();
385  auto& scatRCher = eyeRecData.GetLightFlux(fevt::FdConstants::eCherRayleighScattered);
386  scatRCher.Clear();
387  auto& cherMS = eyeRecData.GetLightFlux(fevt::FdConstants::eCherMultScattered);
388  cherMS.Clear();
389  auto& fluorTot = eyeRecData.GetLightFlux(fevt::FdConstants::eFluorTotal);
390  fluorTot.Clear();
391  auto& fluorDir = eyeRecData.GetLightFlux(fevt::FdConstants::eFluorDirect);
392  fluorDir.Clear();
393  auto& fluorMS = eyeRecData.GetLightFlux(fevt::FdConstants::eFluorMultScattered);
394  fluorMS.Clear();
395 
396  const auto& fluorMSFactors = fChFlPtr->GetFluorescenceMSFactor();
397  const auto& cherMSFactors = fChFlPtr->GetCherenkovMSFactor();
398 
399  for (unsigned int i = mSiz - fnBins, j = 0; i < mSiz; ++i, ++j) {
400  const double time = lightFlux.GetX(j);
401  const double fl = vFluo(i);
402  fluorTot.PushBack(time, 0, fl, 0);
403  const double fluoMSfac = fluorMSFactors[i];
404  const double scattFluo = fl * (1 - 1/fluoMSfac);
405  fluorDir.PushBack(time, 0, fl - scattFluo, 0);
406  fluorMS.PushBack(time, 0, scattFluo, 0);
407  const double cd = vChDir(i);
408  const double cherMSfac = cherMSFactors[i];
409  const double scattDirCher = cd * (1 - 1/cherMSfac);
410  dirCher.PushBack(time, 0, cd - scattDirCher, 0);
411  const double cms = vChMScat(i);
412  const double scattMieCher = cms * (1 - 1/cherMSfac);
413  scatMCher.PushBack(time, 0, cms - scattMieCher, 0);
414  const double crs = vChRScat(i);
415  const double scattRayCher = crs * (1 - 1/cherMSfac);
416  scatRCher.PushBack(time, 0, crs - scattRayCher, 0);
417  cherMS.PushBack(time, 0, scattDirCher + scattRayCher + scattMieCher, 0);
418  }
419 
420  // b) dEdX
421 
422  // add GH light to detected light vector outside FOV
423  ublas::vector<double> n(mSiz);
424  diagonalMatrix Vn(mSiz);
425  for (unsigned int i = 0; i < mSiz - fnBins; ++i) {
426  n(i) = vFluo(i) + vChDir(i) + vChMScat(i) + vChRScat(i);
427  Vn(i, i) = 0;
428  }
429 
430  for (unsigned int i = mSiz - fnBins, j = 0; i < mSiz; ++i, ++j) {
431  n(i) = lightFlux.GetY(j);
432  const double en = lightFlux.GetYErr(j);
433  Vn(i, i) = Sqr(en);
434  }
435 
436  auto& CF = *fChFl;
437  const lowerTriangularMatrix* CFinvPtr = nullptr;
438  try {
439  CFinvPtr = CF.GetInverse();
440  } catch (SingularMatrixException& s) {
441  ERROR("SingularMatrixException for CF");
442  return;
443  }
444  const auto& CFinv = *CFinvPtr;
445 
446  // calculate covariance matrix
447 
448  if (fVerbosity > 1)
449  cerr << "\n recalculating dEdX\n covariance matrix ... " << endl;
450 
451  // calculate diagonal elements of CF*V*(CF^T)^(-1)
452 
453  vector<double> covdEdX(mSiz);
454  for (unsigned int i = 0; i < mSiz; ++i) {
455  double sum = 0;
456  for (unsigned int k = i; k < mSiz; ++k)
457  sum += Sqr(CFinv(k, i)) * Vn(i, i);
458  covdEdX[i] = sum;
459  }
460  const auto dEdX2 = ublas::prod(CFinv, n);
461 
462  auto& dedxProf = eyeRecData.GetFRecShower().GetEnergyDeposit();
463  auto& longProf = eyeRecData.GetFRecShower().GetLongitudinalProfile();
464  const double Ecut = eyeRecData.GetFRecShower().GetEnergyCutoff();
465 
466  for (unsigned int i = 0, j = mSiz - fnBins; i < fnBins; ++i, ++j) {
467  double& eDep = dedxProf.GetY(i);
468  double& eDepErr = dedxProf.GetYErr(i);
469  double& Ne = longProf.GetY(i);
470  double& NeErr = longProf.GetYErr(i);
471  eDep = dEdX2(j);
472  eDepErr = sqrt(covdEdX[j]);
473  const double dEdXperElectron = utl::EnergyDeposit(fDepth[i], fXmax, Ecut);
474  Ne = eDep / dEdXperElectron;
475  NeErr = eDepErr / dEdXperElectron;
476  }
477  }
478 
479 
480  // simple Gaisser-Hillas MINUIT fit-function
481  void
482  EnergyFitter::dEdXFitFunction(int& /*npar*/, double* /*gin*/, double& f,
483  double* par, int /*iflag*/)
484  {
485  double chisq = 0;
486 
487  const double dEdXmax= par[0];
488  const double Xmax = par[1];
489  const double X0 = par[2];
490  const double lambda = par[3];
491 
492  if (X0 > Xmax) {
493  f = fnBins * 1e2;
494  return;
495  }
496 
497  for (unsigned int i = 0; i < fnBins; ++i) {
498  const double X = fDepth[i];
499  const double dEdX = fdEdX[i];
500  const double vdEdX = fvdEdX[i];
501  const double dedx = GaisserHillas(X, X0, Xmax, dEdXmax, lambda);
502  const double resid = dEdX - dedx;
503  chisq += Sqr(resid) / vdEdX;
504  }
505 
506  // add constraints
507  if (fFixX0 == eConstrained)
508  chisq += Sqr(X0 - fDefX0) / fVarX0;
509  if (fFixLambda == eConstrained)
510  chisq += Sqr(lambda - fDefLambda) / fVarLambda;
511 
512  f = chisq;
513  }
514 
515 
520  void
521  EnergyFitter::LightFitFunction(int& /*npar*/, double* /*gin*/, double& f,
522  double* par, int /*iflag*/)
523  {
524  const double dEdXmax = par[0];
525  const double Xmax = par[1];
526  const double X0 = par[2];
527  const double lambda = par[3];
528 
529  if (X0 > Xmax)
530  f = fnBins * 1e2;
531  else
532  f = (fMinimizationMethod == eMaximumLikelihood) ?
533  GaisserHillasLogLike(dEdXmax, Xmax, X0, lambda) :
534  GaisserHillasChi2(dEdXmax, Xmax, X0, lambda);
535  }
536 
537 
542  void
543  EnergyFitter::EnergyFitFunction(int& /*npar*/, double* /*gin*/, double& f,
544  double* par, int /*iflag*/)
545  {
546  const double Eem = par[0];
547  const double Xmax = par[1];
548  const double X0 = par[2];
549  const double lambda = par[3];
550 
551  if (X0 > Xmax) {
552  f = fnBins * 1e2;
553  return;
554  }
555 
558  const GaisserHillas4Parameter gh4(1., Xmax, xPar, lPar);
559 
560  const double dEdXmax = Eem / gh4.GetIntegral();
561 
562  f = (fMinimizationMethod == eMaximumLikelihood) ?
563  GaisserHillasLogLike(dEdXmax, Xmax, X0, lambda) :
564  GaisserHillasChi2(dEdXmax, Xmax, X0, lambda);
565  }
566 
567 
568  // calculates -2*log(L) of data wrt. forward folded Gaisser-Hillas function
569  double
570  EnergyFitter::GaisserHillasLogLike(const double dEdXmax,
571  const double Xmax,
572  const double X0,
573  const double lambda)
574  {
575  double logLikeSum = 0;
576 
577  // conversion factors
578  const double kXconvert = g/cm2;
579  const double kEconvert = PeV/kXconvert;
580  const double kFluxToPe = fDiaphragmArea * fPhotonToPhotoElectron;
581  const double kFluxToPe2 = kFluxToPe * kFluxToPe;
582 
583  // threshold for gaussian approximation
584  const double maxNpe = 30;
585  // maximum n for folding of Gauss and Poisson
586  const int maxSum = 100;
587 
588  // the matrix...
589  const auto& m = *fChFl;
590  const unsigned int mSiz = m.size1();
591  const unsigned int offSet = mSiz - fnBins;
592 
593  // vectors with GH-values and light at aperture values
594  vector<double> dedx(mSiz);
595  vector<double> lightSum(fnBins);
596 
597  for (unsigned int i = 0; i < mSiz; ++i) {
598  const double X = fChFlDepth[i] / kXconvert;
599  if (X > X0)
600  dedx[i] = GaisserHillas(X, X0, Xmax, dEdXmax, lambda);
601  else
602  dedx[i] = 0;
603 
604  if (i >= offSet) {
605  double sum = 0;
606  for (unsigned int k = 0; k <= i; ++k)
607  sum += dedx[k] * m(i, k);
608  lightSum[i - offSet] = sum*kEconvert;
609  }
610  }
611 
612  double nPeObsTot = 0;
613  double nPeExpTot = 0;
614 
615  // sum up log-likelihoods
616  for (unsigned int i = 0; i < fnBins; ++i) {
617 
618  // expected and observed photo electrons
619  const double nPeBackground = fvBackGround[i] * kFluxToPe2 / (1 + fGainVariance);
620  const double nPeSignalExp = lightSum[i]*kFluxToPe;
621  const double nPeExp = nPeSignalExp + nPeBackground;
622  const double nPeObs = fLight[i] * kFluxToPe + nPeBackground;
623 
624  nPeObsTot += nPeObs;
625  nPeExpTot += nPeExp;
626 
627  double logLike;
628  if (nPeObs < maxNpe) {
629 
630  const double gaussVar = nPeExp * fGainVariance;
631 
632  const double lnNpe = log(nPeExp);
633 
634  // do the Poisson/Gauss folding
635  double sum = 0;
636  for (int k = 0; k < maxSum; ++k) {
637  const double gaussArg = -0.5*Sqr(k - nPeObs) / gaussVar;
638  const double lnFaculty = LogGamma(k + 1);
639  const double expArg = gaussArg - nPeExp + k*lnNpe - lnFaculty;
640  const double value = exp(expArg);
641  sum += value;
642  }
643  logLike = log(sum) - 0.5*log(gaussVar);
644 
645  } else {
646 
647  // Gaussian approximation
648  const double gaussVar = nPeExp * (1 + fGainVariance);
649  logLike = -0.5 * Sqr(nPeExp - nPeObs) / gaussVar - 0.5 * log(gaussVar);
650 
651  }
652 
653  logLikeSum += logLike;
654 
655  }
656 
657  // extended logLike: total number of nPe's
658  const double gaussVar = nPeExpTot * (1 + fGainVariance);
659  logLikeSum +=-0.5 * Sqr(nPeExpTot - nPeObsTot) / gaussVar - 0.5 * log(gaussVar);
660 
661  // add constraints
662  if (fFixX0 == eConstrained)
663  logLikeSum += -0.5 * log(fVarX0) - 0.5 * Sqr(X0 - fDefX0) / fVarX0;
664  if (fFixLambda == eConstrained)
665  logLikeSum += -0.5 * log(fVarLambda) - 0.5 * Sqr(lambda - fDefLambda) / fVarLambda;
666 
667  // New constraints
668 
669  if (fFixkUniv == eConstrained) {
670  const double myEta = (Xmax - X0) / lambda;
671  const double myEcal = lambda * dEdXmax * pow(kE / myEta, myEta) * TMath::Gamma(myEta + 1);
672  const double fkUniv = myEcal / dEdXmax; // convert Ecal in PeV
673  const double fkUnivTrue = fDefA1 + fDefA2 * log10(myEcal * 1e15);
674 
675  logLikeSum += -0.5 * log(fVarK) - 0.5 * Sqr(fkUniv - fkUnivTrue) / fVarK;
676  }
677 
678  return -2 * logLikeSum;
679  }
680 
681 
682  // calculates chi^2 of data to Gaisser-Hillas function by forward folding of dEdX profile
683  double
684  EnergyFitter::GaisserHillasChi2(const double dEdXmax,
685  const double Xmax,
686  const double X0,
687  const double lambda)
688  {
689  double chisq = 0;
690 
691  const double kXconvert = g/cm2;
692  const double kEconvert = PeV/kXconvert;
693 
694  const auto& m = *fChFl;
695  const unsigned int mSiz = m.size1();
696  const unsigned int offSet = mSiz - fnBins;
697 
698  vector<double> dedx(mSiz);
699 
700  for (unsigned int i = 0; i < mSiz; ++i) {
701  const double X = fChFlDepth[i] / kXconvert;
702  if (X > X0)
703  dedx[i] = GaisserHillas(X, X0, Xmax, dEdXmax, lambda);
704  else
705  dedx[i] = 0;
706  }
707 
708  for (unsigned int i = 0; i < fnBins; ++i) {
709 
710  const unsigned int j = offSet + i;
711 
712  double lightSum = 0;
713  for (unsigned int k = 0; k <= j; ++k)
714  lightSum += dedx[k] * m(j, k);
715 
716  lightSum *= kEconvert;
717 
718  const double resid = lightSum - fLight[i];
719 
720  double variance;
721  if (fMinimizationMethod == eModifiedLeastSquares)
722  variance = fvLight[i];
723  else {
724  const double vLight = lightSum * (1 + fGainVariance) / (fDiaphragmArea * fPhotonToPhotoElectron);
725  const double bgVariance = fvBackGround[i];
726  variance = vLight+bgVariance;
727  }
728 
729  chisq += Sqr(resid) / variance;
730 
731  }
732 
733  // add constraints
734  if (fFixX0 == eConstrained)
735  chisq += Sqr(X0 - fDefX0) / fVarX0;
736  if (fFixLambda == eConstrained)
737  chisq += Sqr(lambda - fDefLambda) / fVarLambda;
738 
739  // New constraints
740  if (fFixkUniv == eConstrained) {
741  double myEta = (Xmax -X0) / lambda;
742  double myEcal = lambda * dEdXmax * pow(kE / myEta, myEta) * TMath::Gamma(myEta + 1);
743  double fkUniv = myEcal / dEdXmax; // convert Ecal in PeV
744  double fkUnivTrue = fDefA1 + fDefA2 * log10(myEcal * 1e15);
745 
746  chisq += Sqr(fkUniv - fkUnivTrue) / fVarK;
747  }
748 
749  return chisq;
750  }
751 
752 
762  bool
763  EnergyFitter::FitProfile(const EFitOption option)
764  {
765  // initialize MINUIT
766 
767  const int npar = eNGHParameters; // number of parameters
768  double arglist[2] = { 0 };
769  int ierflag = 0;
770 
771  TMinuit minuit(npar);
772 
773  // set appropriate fit function
774  switch (option) {
775  case eEnergyDeposit:
776  minuit.SetFCN(EnergyFitter::dEdXFitFunction);
777  break;
778  case eLightEnergy:
779  minuit.SetFCN(EnergyFitter::EnergyFitFunction);
780  break;
781  default:
782  minuit.SetFCN(EnergyFitter::LightFitFunction);
783  break;
784  }
785 
786  // update fluorescence cherenkov matrix after first fit
787  if (option == eLightAllParam && !UpDateFCM())
788  return false;
789 
790  fChFl = fChFlPtr->GetCherenkovFluorescenceMatrix();
791 
792  // MINUIT verbosity
793  if (fVerbosity == 2)
794  minuit.SetPrintLevel(0);
795  else if (fVerbosity >= 3)
796  minuit.SetPrintLevel(fVerbosity);
797  else {
798  minuit.SetPrintLevel(-1);
799  minuit.mnexcm("SET NOW", arglist, 0, ierflag);
800  }
801  arglist[0] = 1;
802  minuit.mnexcm("SET ERR", arglist, 1, ierflag);
803 
804  // set starting parameters
805  if (option == eLightEnergy)
806  minuit.mnparm(eEnergy,"Eem", fEem, 0.1*fabs(fEem), 0, 0, ierflag);
807  else
808  minuit.mnparm(eEnergy, "dEdXmax", fdEdXmax, 0.1*fabs(fdEdXmax), 0, 0, ierflag);
809  minuit.mnparm(eXmax, "Xmax", fXmax, 20, 0, 0, ierflag);
810  minuit.mnparm(eX0, "X0", fX0, 10, -1000, fDepth[0], ierflag);
811  minuit.mnparm(eLambda, "lambda", fLambda, 5, 10, 150, ierflag);
812 
813  if (fFixLambda == eFixed || option == eEnergyDeposit)
814  minuit.FixParameter(eLambda);
815  if (fFixX0 == eFixed || option == eEnergyDeposit)
816  minuit.FixParameter(eX0);
817 
818  // Perform minimization
819  arglist[0] = 1000;
820  arglist[1] = 1;
821  minuit.mnexcm("MIGRAD", arglist, 2, ierflag);
822 
823  if (ierflag) {
824  if (fVerbosity > -1)
825  cerr << " MIGRAD failed in dEdX fit " << ierflag << endl;
826  return false;
827  }
828 
829  double amin = 0;
830  double edm = 0;
831  double errdef = 0;
832  int nvpar = 0;
833  int nparx = 0;
834  int icstat = 0;
835  minuit.mnstat(amin, edm, errdef, nvpar, nparx, icstat);
836 
837  // store chi2, parameters at minimum and covariance
838 
839  fnDof = fnBins - 2;
840  if (fFixX0 == eFree)
841  fnDof -= 1;
842  if (fFixLambda == eFree)
843  fnDof -= 1;
844 
845  double p[eNGHParameters] = { 0 };
846  if (option != eLightEnergy)
847  minuit.GetParameter(eEnergy, fdEdXmax, p[eEnergy]);
848  else
849  minuit.GetParameter(eEnergy, fEem, p[eEnergy]);
850 
851  minuit.GetParameter(eXmax, fXmax, p[eXmax]);
852  minuit.GetParameter(eX0, fX0, p[eX0]);
853  minuit.GetParameter(eLambda, fLambda, p[eLambda]);
854  minuit.mnemat(&fCovariance[0][0], eNGHParameters);
855 
856  if (fMinimizationMethod == eMaximumLikelihood) {
857  fChisqGH = GaisserHillasChi2(fdEdXmax, fXmax, fX0, fLambda);
858  //PrintLightFluxes();
859  } else
860  fChisqGH = amin;
861 
862  if (fVerbosity > -1 )
863  DumpCurrentParameters(option);
864 
865  return true;
866  }
867 
868 
869  // calculates total energy and its error, returns true if successful
870  bool
871  EnergyFitter::CalculateEnergy(fevt::Eye& eye)
872  {
873  // create and set GH parameters
874  auto& recShower = eye.GetRecData().GetFRecShower();
875  if (!recShower.HasGHParameters()) {
877  recShower.MakeGHParameters(ghTmp);
878  }
879 
880  constexpr double gcm2 = g/cm2;
881  auto& gh = recShower.GetGHParameters();
882  gh.SetXMax(fXmax*gcm2, sqrt(fCovariance[eXmax][eXmax])*gcm2);
883  gh.SetNMax(fdEdXmax*PeV/gcm2, sqrt(fCovariance[eEnergy][eEnergy])*PeV/gcm2, true);
884  gh.SetChiSquare(fChisqGH, fnDof);
885 
886  auto* gh4 = dynamic_cast<GaisserHillas4Parameter*>(&gh);
887  if (gh4) {
888  gh4->SetShapeParameter(gh::eX0, fX0*gcm2, sqrt(fCovariance[eX0][eX0])*gcm2);
889  gh4->SetShapeParameter(gh::eLambda, fLambda*gcm2, sqrt(fCovariance[eLambda][eLambda])*gcm2);
890  }
891 
892  // fit energy to obtain realistic errors
893 
894  fEem = GetEmEnergy() / PeV;
895  if (!fEem) {
896  if (fVerbosity > -1)
897  ERROR("Error calculating gamma function");
898  recShower.SetEmEnergy(0, 0);
899  recShower.SetTotalEnergy(0, 0);
900  return false;
901  }
902 
903  if (FitProfile(eLightEnergy)) {
904  // electromagnetic energy
905  fEem *= PeV;
906  const double EemStatVar = fCovariance[eEnergy][eEnergy] * PeV*PeV;
907  recShower.SetEmEnergy(fEem, sqrt(EemStatVar), ShowerFRecData::eStatistical);
908  return true;
909  }
910 
911  return false;
912  }
913 
914 
915  // calculates Gaisser-Hillas integral for current parameters (a.k.a. calorimetric energy) Offline units are returned
916  double
917  EnergyFitter::GetEmEnergy()
918  const
919  {
922  const GaisserHillas4Parameter gh4(fdEdXmax, fXmax, xPar, lPar);
923  return gh4.GetIntegral()*PeV;
924  }
925 
926 
927  // updates Cherenkov-Fluorescence matrix given new fXmax value
928  bool
929  EnergyFitter::UpDateFCM()
930  {
931  const double kMaxXmaxDiff = 20;
932  if (std::fabs(fChFlPtr->GetXmax()/(g/cm2) - fXmax) > kMaxXmaxDiff &&
933  !fYieldRefit)
934  fChFlPtr->UpdateXmax(fXmax*(g/cm2));
935 
936  return true;
937  }
938 
939 
940  void
941  EnergyFitter::DumpCurrentParameters(const EFitOption option)
942  const
943  {
944  switch (option) {
945  case eInitialize:
946  cout << " X0 [g/cm^2] lambda [g/cm^2] "
947  "Xm [g/cm^2] dEdXm [PeV/(g/cm^2)] chi2/ndf \n"
948  " ---------------------------------------------"
949  "------------------------------------------\n"
950  " ==> init :";
951  break;
952  case eEnergyDeposit:
953  cout << " ==> prefit :";
954  break;
955  case eLightAllParam:
956  cout << " ==> final :";
957  break;
958  default:
959  return;
960  }
961  cout << setw(12) << fX0
962  << setw(15) << fLambda
963  << setw(15) << fXmax
964  << setw(17) << fdEdXmax
965  << setw(12) << int(fChisqGH) << "/"
966  << fnDof << endl;
967  }
968 
969 
970  // steering routine for profile refit
971  bool
972  EnergyFitter::ReFitProfile(const fevt::Eye& eye, CherenkovFluorescenceMatrix* const chfl)
973  {
974  fChFlPtr = chfl;
975 
976  if (InitializeGHFit(eye, 1) && FitProfile(EnergyFitter::eLightAllParam)) {
977  CleanUp();
978  return true;
979  }
980  CleanUp();
981  return false;
982  }
983 
984 
985  void
986  EnergyFitter::CleanUp()
987  {
988  delete[] fdEdX;
989  fdEdX = nullptr;
990 
991  delete[] fvdEdX;
992  fvdEdX = nullptr;
993 
994  delete[] fLight;
995  fLight = nullptr;
996 
997  delete[] fvLight;
998  fvLight = nullptr;
999 
1000  delete[] fvBackGround;
1001  fvBackGround = nullptr;
1002 
1003  delete[] fvdEdX;
1004  fvdEdX = nullptr;
1005 
1006  delete[] fDepth;
1007  fDepth = nullptr;
1008 
1009  delete[] fChFlDepth;
1010  fChFlDepth = nullptr;
1011  }
1012 
1013 
1014  // returns xmax in Offline units
1015  double
1016  EnergyFitter::GetXmax()
1017  const
1018  {
1019  return fXmax*g/cm2;
1020  }
1021 
1022 
1023  // returns dEdXmax in Offline units
1024  double
1025  EnergyFitter::GetdEdXmax()
1026  const
1027  {
1028  return fdEdXmax*PeV/g*cm2;
1029  }
1030 
1031 
1032  // returns X0 in Offline units
1033  double
1034  EnergyFitter::GetX0()
1035  const
1036  {
1037  return fX0*g/cm2;
1038  }
1039 
1040 
1041  // returns lambda in Offline units
1042  double
1043  EnergyFitter::GetLambda()
1044  const
1045  {
1046  return fLambda*g/cm2;
1047  }
1048 
1049 }
unsigned int GetNPoints() const
bool HasLightFlux(const FdConstants::LightSource source=FdConstants::eTotal) const
Check that light profile for source /par source is present.
Definition: EyeRecData.h:297
constexpr double kE
Definition: MathConstants.h:28
constexpr T Sqr(const T &x)
Fluorescence Detector Eye Event.
Definition: FEvent/Eye.h:29
constexpr double PeV
Definition: AugerUnits.h:189
std::pair< gh::EShapeParameter, double > ShapeParameter
#define INFO(message)
Macro for logging informational messages.
Definition: ErrorLogger.h:161
const unsigned int npar
Definition: UnivRec.h:75
void SetShapeParameter(const gh::EShapeParameter par, const double value, const double error)
Setters.
void Init()
Initialise the registry.
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)
const lowerTriangularMatrix * GetInverse() const
T Get() const
Definition: Branch.h:271
Class representing a document branch.
Definition: Branch.h:107
constexpr double s
Definition: AugerUnits.h:163
double LogGamma(const double x)
TelescopeIterator TelescopesEnd()
first available tel of status eHasData (DEPRECATED)
Definition: FEvent/Eye.cc:230
evt::ShowerFRecData & GetFRecShower()
Reconstructed shower info for this eye.
Definition: EyeRecData.h:323
constexpr double g
Definition: AugerUnits.h:200
void GetData(bool &b) const
Overloads of the GetData member template function.
Definition: Branch.cc:644
TelescopeIterator TelescopesBegin()
first available tel of status eHasData (DEPRECATED)
Definition: FEvent/Eye.cc:207
double GaisserHillas(const double x, const double x0, const double xMax, const double nMax, const double lambda)
Calculate the Gaisser-Hillas function.
static CentralConfig * GetInstance()
Use this the first time you get an instance of central configuration.
Calculation of Cherenkov and Fluorescence matrix.
double EnergyDeposit(const double age, const double enCut)
Parametrization for the average energy deposit per particle.
const double gcm2
utl::TabulatedFunctionErrors & GetLightFlux(const FdConstants::LightSource source=fevt::FdConstants::eTotal)
Light flux at diaphragm, photons/m^2 versus time in ns.
Definition: EyeRecData.h:286
double GetIntegral() const
calculate integral
Gaisser Hillas with 4 parameters.
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
constexpr double m
Definition: AugerUnits.h:121
utl::Branch GetTopBranch(const std::string &id)
Get top branch for moduleConfigLink with given id (XML files)
utl::TabulatedFunctionErrors & GetEnergyDeposit()
retrieve dE/dX
constexpr double cm2
Definition: AugerUnits.h:118
fevt::EyeRecData & GetRecData()
Reconstructed data for this eye.
Definition: FEvent/Eye.cc:130

, generated on Tue Sep 26 2023.