UnivV2Rec.cc
Go to the documentation of this file.
1 #include "UnivV2Rec.h"
2 
3 #include <cmath>
4 #include <cstdlib>
5 #include <fstream>
6 #include <iostream>
7 #include <random>
8 #include <string>
9 #include <vector>
10 
11 #include <boost/filesystem.hpp>
12 #include <boost/format.hpp>
13 
14 #include <fwk/CentralConfig.h>
15 #include <fwk/RunController.h>
16 
17 #include <evt/Event.h>
18 #include <evt/ShowerFRecData.h>
19 #include <evt/ShowerRecData.h>
20 #include <evt/ShowerSRecData.h>
21 #include <evt/ShowerSimData.h>
22 #include <evt/ShowerUnivRecData.h>
23 #include <fevt/Eye.h>
24 #include <fevt/EyeRecData.h>
25 #include <fevt/FEvent.h>
26 #include <sdet/SDetector.h>
27 #include <sdet/Station.h>
28 #include <sevt/SEvent.h>
29 
30 #include <utl/AugerUnits.h>
31 #include <utl/PhysicalConstants.h>
32 #include <utl/PhysicalFunctions.h>
33 #include <utl/UTCDateTime.h>
34 
35 // local
37 #include <src/GetSignal.h>
38 #include <src/PowerConfig.h>
39 
40 // fd energy long term correction
42 
43 using namespace utl;
44 using namespace std;
45 
46 namespace un2 {
47 
48 void SimpleReco(evt::Event &event) {
49 
50  // ------------------------------- PROLOG ---------------
51  // ------------------------------------------------------
52  //
53  // the managment advises to listen to some relaxing music:
54  // https://www.youtube.com/watch?v=cSkqscB9j4I
55  //
56  const CoordinateSystemPtr augerCS =
57  det::Detector::GetInstance().GetReferenceCoordinateSystem();
58  const auto &sDet = det::Detector::GetInstance().GetSDetector();
59  const bool verbose = false;
60  const Branch topB =
61  fwk::CentralConfig::GetInstance()->GetTopBranch("UniversalityFitterV2");
62 
63  string hadrIntModel;
64  topB.GetChild("HadronicInteractionModel").GetData(hadrIntModel);
65 
66  bool fixXmaxToFd;
67  bool fixEnergyToFd;
68  topB.GetChild("FixXmaxToFd").GetData(fixXmaxToFd);
69  topB.GetChild("FixEnergyToFd").GetData(fixEnergyToFd);
70 
71  INFO(boost::format("Fix Xmax to FD: %b") % (fixXmaxToFd));
72  INFO(boost::format("Fix energy to FD: %b") % (fixEnergyToFd));
73 
74  if (!event.GetRecShower().HasUnivRecShower())
76 
77  const double gcm = gram / cm2;
78 
79  if (!event.HasRecShower()) {
80  ERROR("No RecShower found!");
81  return;
82  }
83 
84  // reconstruction algorithm of the universality-v2 framework
85  // based on the model developed in this PhD thesis:
86  // https://publikationen.bibliothek.kit.edu/1000144727
87  //
88  // PLEASE CONSIDER PULLING THE FULL REPOSITORY
89  // https://gitlab.iap.kit.edu/shower-universality/universality-v2.git
90  //
91  // the reconstruction algorithm creates an object for each considered detector
92  // in a station. the detectors are evaluated for saturation and are eventually
93  // removed. for each detector a best-fit for the start time according to an
94  // estimate of xmax is evaluated for the later fit, to tackle uncertainties
95  // from the geometry that affect the timing. detectors with bad chi^2 are
96  // removed. only detectors within 2000m in the shower plane are considered,
97  // more conservative cuts are applied for time-trace fitting.
98  //
99  // trace fit is done on the integrated trace to achieve smoothing of the data,
100  // which is one of the main differences in this approach. this allows you to
101  // write down a signal ucnertainty model more easily than bin-wise for a trace
102  //
103  // energy is constrained to MC energy +/- 10% (15%) or taken directly either
104  // from FD or SD reconstruction in case of data (instead of simulation data).
105  // Rmu is evaluated using a fixed energy estimate from a first fit stage
106  // and should never be fit simultaneously with the primary energy!
107  // Xmax is esimated mostly from the trace information, using a constraint fit
108  // around expected Xmax values based on Rmu (this can be removed, if needed.
109  // removing this constraint does not change precision for protons, but for
110  // iron!). lnA is estimated from results of Rmu and Xmax. data is stored in
111  // ADST and python-like dictionary text file (open using the ast module).
112  //
113  // MC value for Rmu is calculated from weighted average of dense stations.
114  // please pay attention that eHistory has to be available for the corsika
115  // shower you are considering for Rmu to be calculated! If no dense stations
116  // are available, Rmu is set to 666!
117 
118  // INTRODUCTION
119  // ---------------------------------------------------------------------------------
120  const bool realEvent = !event.HasSimShower();
121  const auto &recShower = event.GetRecShower().GetSRecShower();
122  double recEnergy =
123  !realEvent ? event.GetSimShower().GetEnergy() : recShower.GetEnergy();
124  double avgXmax = avgXmax0(log10(recEnergy), realEvent) - 45.;
125  const Vector &recAxis = recShower.GetAxis();
126  const Point &recCore = recShower.GetCorePosition();
127  const double recTheta = recAxis.GetTheta(augerCS);
128  const bool realAtmosphere = !event.HasSimShower();
129  double realXmax = event.HasSimShower()
130  ? event.GetSimShower().GetGHParameters().GetXMax() / gcm
131  : avgXmax;
132  const unsigned int pId =
133  event.HasSimShower() ? event.GetSimShower().GetPrimaryParticle() : 666;
134  const int month =
135  UTCDateTime(det::Detector::GetInstance().GetTime()).GetMonth();
136  const double recPhi = recAxis.GetPhi(augerCS);
137  const double recAzimuth = (recPhi < 0) ? recPhi + 2 * M_PI : recPhi;
138 
139  vector<vector<double>> FdXmax = {};
140  double FdMeanXmax = 0;
141  double FdMeanEnergy = 0;
142  double FdMeanLgEnergy = 0;
143  double wEnergySum = 0;
144  double wXmaxSum = 0;
145 
146  if (event.HasFEvent()) {
147 
148  int nRecEyes = 0;
151  eye != event.GetFEvent().EyesEnd(fevt::ComponentSelector::eHasData);
152  ++eye) {
153 
154  if (!(event.GetFEvent().HasEye(eye->GetId()) && eye->HasRecData()))
155  continue;
156 
157  if (!eye->GetRecData().HasFRecShower())
158  continue;
159 
160  const auto &eyeRec = eye->GetRecData();
161  if (!eyeRec.GetFRecShower().HasGHParameters())
162  continue;
163 
164  double eyeEnergy = eyeRec.GetFRecShower().GetTotalEnergy();
165  const double eyeXmax = eyeRec.GetFRecShower().GetGHParameters().GetXMax();
166  const double eyeXmaxErr =
167  eyeRec.GetFRecShower().GetGHParameters().GetXMaxError();
168  const double eyeEnergyErr = eyeRec.GetFRecShower().GetTotalEnergyError();
169 
170  if (true /* correct long term drift fd energy*/) {
171  const double energyRatioSdFd = recEnergy / eyeEnergy;
172  const double corrFactor = LongTermCorrectionPerEye(energyRatioSdFd, event.GetHeader().GetTime().GetGPSSecond() / pow(10, 6), eye->GetId() - 1);
173  eyeEnergy = eyeEnergy * energyRatioSdFd/corrFactor;
174  INFO(boost::format("eye (%i) correction factor %.2f") % eye->GetId() % energyRatioSdFd);
175  }
176 
177  FdXmax.push_back({eyeXmax / gcm, eyeXmaxErr / gcm});
178  nRecEyes += 1;
179  FdMeanXmax += eyeXmax / gcm / pow(eyeXmaxErr / gcm, 2);
180  FdMeanEnergy += eyeEnergy / pow(eyeEnergyErr, 2);
181  wXmaxSum += 1 / pow(eyeXmaxErr / gcm, 2);
182  wEnergySum += 1 / pow(eyeEnergyErr, 2);
183  // cout << eyeXmax / gcm << endl;
184  }
185  FdMeanXmax /= wXmaxSum;
186  FdMeanEnergy /= wEnergySum;
187  FdMeanLgEnergy = log10(FdMeanEnergy);
188  INFO(boost::format("FD mean Xmax %.2f g/cm^2") % FdMeanXmax);
189  INFO(boost::format("FD mean lgE/eV %.2f") % FdMeanLgEnergy);
190  }
191 
192  if (fixXmaxToFd && FdMeanXmax > 0)
193  avgXmax = FdMeanXmax;
194 
195  if (fixXmaxToFd && FdMeanXmax > 0 && realEvent)
196  realXmax = FdMeanXmax;
197 
198  if (fixEnergyToFd && FdMeanEnergy > 0)
199  recEnergy = FdMeanEnergy;
200 
201  INFO(boost::format("lg(E/eV) = %.2f θ = %.2f°") % (log10(recEnergy)) %
202  un2::Rad2Deg(recTheta));
203 
204  if (log10(recEnergy) < 18.5) {
205  WARNING("Energy too low for universality reconstruction!");
206  WARNING("Energy too low for universality reconstruction!");
207  WARNING("Energy too low for universality reconstruction!");
208  return; // speedup
209  }
210 
211  auto &univRecShower = event.GetRecShower().GetUnivRecShower();
212 
213  univRecShower.SetCorePosition(recCore);
214  univRecShower.SetAxis(recAxis);
215 
216  const auto &sEvent = event.GetSEvent();
217 
218  double RmuSum = 0;
219  double wRmuSum = 0;
220 
223 
224  // -----------------------------------------------------
225  // PREAMBLE---------------------------------------------
226  RmuXmaxFit powerFit;
227  LDFFit LdfFit;
228  LDFFit EnergyFit;
229  SignalRatioFit RatioFit;
230 
231  double useCorrelationPenalty;
232  bool normalizeCDF;
233  topB.GetChild("UseCorrelation").GetData(useCorrelationPenalty);
234  topB.GetChild("NormalizeCDF").GetData(normalizeCDF);
235 
236  powerFit.AddMetaData(avgXmax, log10(recEnergy), recTheta,
237  event.HasSimShower(), month,
238  normalizeCDF, // normalized CDFs
239  useCorrelationPenalty); // penalty
240  LdfFit.AddMetaData(avgXmax, log10(recEnergy), recTheta, event.HasSimShower(),
241  month);
242  EnergyFit.AddMetaData(avgXmax, log10(recEnergy), recTheta,
243  event.HasSimShower(), month);
244 
245  RatioFit.AddMetaData(avgXmax, log10(recEnergy), recTheta,
246  event.HasSimShower(), month);
247 
248  unsigned short int numDet = 0;
249  unsigned short int numEnDet = 0;
250  unsigned short int numTimeDet = 0;
251  unsigned short int numDenseDet = 0;
252  unsigned short int numSatDet = 0;
253  const int numDenseDetTot =
254  sDet.GetDenseStationList().size() * 2; // AugerPrime
255 
256  const string basepath = ".";
257  const string dumpfileId =
258  event.GetHeader().GetId(); // boost::str(boost::format("%08X") %
259  // (distribution(generator)));
260  const string pythonDumpFilepath =
261  boost::str(boost::format("%s/event_dump_universalityv2_%s.txt") %
262  basepath % dumpfileId);
263 
264  INFO(boost::format("Dumpfile will be at %s") % pythonDumpFilepath);
265  // ofstream pythonDump(pythonDumpFilepath.c_str());
266  ofstream pythonDump("dumpfile.txt");
267 
268  pythonDump << "{\n";
269  pythonDump << boost::format("\"ID\": \"%s\",\n") % (dumpfileId);
270  pythonDump << boost::format("\"GPSSecond\": %i,\n") %
271  event.GetHeader().GetTime().GetGPSSecond();
272  pythonDump << boost::format("\"reclgE\": %.5f,\n") % (log10(recEnergy));
273  pythonDump << boost::format("\"recTheta\": %.5f,\n") % recTheta;
274  pythonDump << boost::format("\"recPhi\": %.5f,\n") % recPhi;
275  pythonDump << boost::format("\"recAzimuth\": %.5f,\n") % recAzimuth;
276  pythonDump << boost::format("\"realXmax\": %.5f,\n") % realXmax;
277  pythonDump << boost::format("\"particleId\": %i,\n") % pId;
278  pythonDump << boost::format("\"detectors\":\n[\n");
279 
280  vector<un2Detector> un2Detectors;
281 
282  // stations iteration
283  for (const auto &station : boost::make_iterator_range(sEvent.StationsBegin(),
284  sEvent.StationsEnd())) {
285 
286  // avoid dereferencing NULL
287  if (!station.HasRecData())
288  continue;
289 
290  const unsigned int stationId = station.GetId();
291  const bool isDense = sDet.GetStation(stationId).IsDense();
292  const auto &statRecData = station.GetRecData();
293  const double rsp = statRecData.GetSPDistance();
294 
295  // hard cut needed, universality parametrizations are
296  // not valid beyond ~ 2000 m
297  if (rsp > 2000 * utl::m)
298  continue;
299 
300  const double az = statRecData.GetAzimuthShowerPlane();
301  const double height =
302  sDet.GetStation(stationId).GetPosition().GetZ(augerCS) + 1400.;
303 
304  const size_t sigStartSlot = statRecData.GetSignalStartSlot();
305  const size_t sigStopSlot = statRecData.GetSignalEndSlot();
306 
307  const double coreTime = recShower.GetCoreTime().GetGPSNanoSecond();
308  const double stationTime =
309  statRecData.GetSignalStartTime().GetGPSNanoSecond();
310  const double stationPFTime =
311  coreTime - 1. / 0.2998 * (cos(az) * rsp * tan(recTheta));
312  // station.HasSimData()
313  // ? station.GetSimData().GetPlaneFrontTime().GetGPSNanoSecond()
314  // : coreTime - 1. / 0.2998 * (cos(az) * rsp * tan(recTheta));
315  const double planeFrontTimeResidual = stationPFTime - stationTime;
316 
317  un2Detector un2WCD(station.GetId(), station, "WCD", rsp, az, height,
318  planeFrontTimeResidual, isDense);
319  un2WCD.signalStartSlot = sigStartSlot;
320  un2WCD.signalStopSlot = sigStopSlot;
321  un2WCD.referenceDt40 =
322  GetTimeQuantile(recTheta, rsp, az, height, realXmax, "WCD", "t40",
323  month, realAtmosphere)[0];
324  un2WCD.referenceDt0 =
325  GetTimeQuantile(recTheta, rsp, az, height, realXmax, "WCD", "t0", month,
326  realAtmosphere)[0];
327 
328  un2Detectors.push_back(un2WCD);
329 
330  // add a similar block of code for further detectors
331  if (station.HasScintillator() && station.GetScintillator().HasRecData()) {
332  un2Detector un2SSD(station.GetId(), station, "SSD", rsp, az, height,
333  planeFrontTimeResidual, isDense);
334  un2SSD.signalStartSlot = sigStartSlot;
335  un2SSD.signalStopSlot = sigStopSlot; // not part of constructor because
336  // these might change in the future!
337  un2SSD.referenceDt40 =
338  GetTimeQuantile(recTheta, rsp, az, height, realXmax, "SSD", "t40",
339  month, realAtmosphere)[0];
340  un2SSD.referenceDt0 =
341  GetTimeQuantile(recTheta, rsp, az, height, realXmax, "SSD", "t0",
342  month, realAtmosphere)[0];
343 
344  un2Detectors.push_back(un2SSD);
345 
346  }
347  }
348 
349  for (auto &un2det : un2Detectors) {
350 
351  INFO(boost::format("Detector %s %i") %un2det.type %un2det.Id);
352  un2det.CalcUnit();
353 
354  if (0==un2det.signalUnit) {
355  WARNING(boost::format("Could not recover signal unit! Skipping Detector!"));
356  continue;
357  }
358 
359  const auto &trace = un2det.GetTrace(); // not part of the detector object
360 
361  if ("WCD" == un2det.type)
362  un2det.totalSignal = un2det.station.GetRecData().GetTotalSignal();
363 
364  if ("SSD" == un2det.type)
365  un2det.totalSignal =
366  un2det.station.GetScintillator().GetRecData().GetTotalSignal();
367 
368  un2det.stationNSTime =
369  un2det.station.GetRecData().GetSignalStartTime().GetGPSNanoSecond();
370 
371  un2det.referenceSignal =
372  GetSignal(hadrIntModel, log10(recEnergy), recTheta, un2det.spRadius,
373  un2det.spAzimuth, un2det.heightAboveSL, realXmax, 1.,
374  un2det.type, month, realAtmosphere, true)[0];
375  un2det.uncertaintySignal =
376  ("WCD" == un2det.type)
377  ? utl::wcd::SignalUncertainty(stdWCDmodel, cos(recTheta),
378  un2det.totalSignal)
379  : utl::ssd::SignalUncertainty(stdSSDmodel, cos(recTheta),
380  un2det.totalSignal);
381 
382  // small signals have close to zero uncertainty in SSDs, which
383  // results in crazy outliers in dense ring Rmu calculation using the
384  // weighted signal sum, so here I set a low threshold until SSD
385  // signal uncertainty model is fixed
386  if ("SSD" == un2det.type && un2det.uncertaintySignal < 1)
387  un2det.uncertaintySignal = 1;
388 
389  un2det.DX =
390  CalcDX(realAtmosphere, false /*useDL*/, un2det.spRadius,
391  un2det.spAzimuth, un2det.heightAboveSL, realXmax /*estimator*/,
392  recTheta, month, false /*silent*/);
393 
394  un2det.nsBin = trace.GetBinning(); // depends on electronics!
395 
396  if (un2det.isVirtual) {
397 
398  un2det.mcSpRadius = sDet.GetStation(un2det.Id).GetPosition().GetRho(
400 
401  const auto &muonTrace =
402  un2det.HasTrace(sevt::StationConstants::eMuon)
403  ? un2det.GetTrace(sevt::StationConstants::eMuon)
404  : un2det.GetTrace();
405  const double muonSignal =
406  accumulate(muonTrace.Begin(), muonTrace.End(), 0.);
407 
408  // SCALING PROBLEM NEEDS TO BE FIXED
409  // Rmu from PURE muonic signal is not Rmu using tot signal
410  const double muonPrediction =
411  un2det.HasTrace(sevt::StationConstants::eMuon)
412  ? GetSignal(hadrIntModel, log10(event.GetSimShower().GetEnergy()), recTheta,
413  un2det.mcSpRadius, un2det.spAzimuth,
414  un2det.heightAboveSL, realXmax, 1., un2det.type,
415  month, realAtmosphere, true)[2]
416  : GetSignal(hadrIntModel, log10(event.GetSimShower().GetEnergy()), recTheta,
417  un2det.mcSpRadius, un2det.spAzimuth,
418  un2det.heightAboveSL, realXmax, 1., un2det.type,
419  month, realAtmosphere, true)[0];
420 
421  const double stationRmu = muonSignal * un2det.signalUnit / muonPrediction;
422 
423  RmuSum += stationRmu / pow(un2det.uncertaintySignal, 2);
424  wRmuSum += 1 / pow(un2det.uncertaintySignal, 2);
425 
426  INFO(boost::format(
427  "Station level Rmu for %i - %s: %.2f +/- %.2f at %.2f m") %
428  un2det.Id % un2det.type % stationRmu %
429  (un2det.uncertaintySignal / un2det.referenceSignal) %
430  un2det.mcSpRadius);
431 
432  numDenseDet += 1;
433  }
434 
435  if (un2det.station.IsLowGainSaturation() && !un2det.isVirtual)
436  numSatDet += 1;
437 
438  vector<double> predIntegratedSignal;
439  vector<double> fitIntegratedSignal;
440  vector<double> stdIntegratedSignal;
441 
442  // detector selection
443  if (!un2det.isVirtual && !un2det.station.IsLowGainSaturation() &&
444  un2det.referenceSignal > 3 &&
445  un2det.totalSignal > 1) { // this is needed because the SSD signal
446  // uncertainty model is fucked up for small signals
447  if (un2det.type == "SSD")
448  un2det.weight = 1.;
449 
450  vector<double> timeVec = {0};
451 
452  // traces should be of the SAME LENGTH! otherwise distant stations (short
453  // signals) do not get accounted correctly, use 4500ns, that is roughly
454  // 540 (or 180) bins
455  for (unsigned int i = un2det.signalStartSlot;
456  i < (un2det.signalStartSlot + 4500 / un2det.nsBin); ++i) {
457 
458  timeVec.push_back(timeVec.back() + un2det.nsBin);
459 
460  // use all signal between start/stop slots
461  if (i < trace.GetSize()-1 && i < un2det.signalStopSlot) {
462 
463  un2det.integratedSignal.push_back(un2det.integratedSignal.back() +
464  trace[i] * un2det.signalUnit);
465  un2det.integratedNormalizedSignal.push_back(
466  un2det.integratedNormalizedSignal.back() +
467  trace[i] * un2det.signalUnit / un2det.totalSignal);
468 
469  } else { // this ensures uniform length
470 
471  un2det.integratedSignal.push_back(un2det.integratedSignal.back());
472  un2det.integratedNormalizedSignal.push_back(
473  un2det.integratedNormalizedSignal.back());
474  }
475 
476  if (un2det.integratedSignal.back() < 0.4 * un2det.totalSignal)
477  un2det.t40RelToStart =
478  timeVec.back(); // reassign t40 until final value is reached
479  }
480 
481  un2det.Dt40 = un2det.t40RelToStart - un2det.pfTimeResidual;
482 
483  // FEEDING THE FIT MACHINE
484  const string detT0ParName =
485  "t0_" + un2det.type + "_" + to_string(un2det.Id);
486 
487  // ALL detectors here go into LDF fit, however only converging stations go
488  // to power fit
489  if (true) {
490  INFO(boost::format("Detector %s %i added to LDFFit") %un2det.type %un2det.Id);
491  LdfFit.AddDetectorData(un2det);
492  numDet += 1;
493  }
494 
495  if (true) {
496  INFO(boost::format("Detector %s %i added to EnergyFit") %un2det.type %un2det.Id);
497  EnergyFit.AddDetectorData(un2det);
498  numEnDet += 1;
499  }
500 
501  if (un2det.referenceSignal > 5) { // really just setting arbitrary limits
502  // here because I think the SSD uncertainty model is not working!
503  // really large signals needed, otherweise = crap
504  INFO(boost::format("Detector %s %i added to RatioFit") %un2det.type %un2det.Id);
505  RatioFit.AddDetector(un2det);
506  }
507 
508  if (un2det.spRadius < 1800. * utl::m && un2det.referenceSignal > 5) {
509 
510  // single station level check
511  RmuXmaxFit detFit;
512 
513  detFit.SetParameterDefs({{"Xmax", 750., 5.0, 650., 1050., false},
514  {"Rmu", 1.10, 0.1, 0., 4., true},
515  {"lgE", log10(recEnergy), .1, 18, 21., true}});
516 
517  detFit.AddMetaData(avgXmax, log10(recEnergy), recTheta,
518  event.HasSimShower(), month, true, 0.);
519  detFit.AddDetectorData(un2det);
520 
521  const double t0start = un2det.referenceDt0[0] + un2det.pfTimeResidual;
522  const double t0sig = un2det.referenceDt0[1];
523  auto &parDefs = detFit.GetParameterDefs();
524  parDefs.push_back({detT0ParName, t0start, 25., t0start - t0sig,
525  t0start + 0.5 * t0sig, false});
526 
527  utl::Minou::Minimizer<RmuXmaxFit> detMin(detFit);
528  detMin.SetVerbose(verbose);
529  detMin.Minimize(500000);
530 
531  const auto detRes = detMin.GetParametersAndErrors();
532 
533  vector<double> timeVecDet = {};
534  const double t0 = detRes.at(3).first;
535 
536  for (unsigned int t = 0; t < un2det.integratedSignal.size(); ++t) {
537  timeVecDet.push_back(t * un2det.nsBin - t0);
538  }
539 
540  // predIntegratedSignal = GetIntegratedTotalSignalSum(
541  predIntegratedSignal = GetIntegratedTotalSignalSum(
542  timeVec, hadrIntModel, log10(recEnergy), recTheta, un2det.spRadius,
543  un2det.spAzimuth, un2det.heightAboveSL, realXmax, 1.0, un2det.type,
544  month, realAtmosphere, true, un2det.pfTimeResidual, un2det.nsBin,
545  /*"cdf", 0, */ un2det.integratedSignal.back());
546 
547  fitIntegratedSignal = GetIntegratedTotalSignalSum(
548  timeVecDet, hadrIntModel, log10(recEnergy), recTheta,
549  un2det.spRadius, un2det.spAzimuth, un2det.heightAboveSL,
550  detRes.at(0).first, detRes.at(1).first, un2det.type, month,
551  realAtmosphere, true, un2det.pfTimeResidual, un2det.nsBin,
552  /*"cdf", 0, */ un2det.integratedSignal.back());
553 
554  stdIntegratedSignal = GetIntegratedTotalSignal(
555  timeVec, hadrIntModel, log10(recEnergy), recTheta, un2det.spRadius,
556  un2det.spAzimuth, un2det.heightAboveSL, realXmax, 1.0, un2det.type,
557  month, realAtmosphere, true, un2det.pfTimeResidual, un2det.nsBin,
558  "unc", 0, un2det.integratedSignal.back());
559 
560  for (unsigned int i = 0; i < timeVecDet.size(); ++i) {
561 
562  un2det.detChiSq +=
563  utl::Sqr((fitIntegratedSignal[i] - un2det.integratedSignal[i]) /
564  stdIntegratedSignal[i]);
565  }
566 
567  un2det.detChiSq =
568  (un2det.detChiSq == un2det.detChiSq) ? un2det.detChiSq : FLT_MAX;
569 
570  // some output to check that everything is within a physically
571  // reasonable range of values
572  ostringstream msg;
573  msg << boost::format("%s %i\nr = %.1f m ψ = %.0f°\n") % un2det.type %
574  un2det.Id % un2det.spRadius % Rad2Deg(un2det.spAzimuth);
575  msg << boost::format("reconstructed Signal / pred. reference Signal: "
576  "%.2f / %.2f ± %.2f\n") %
577  un2det.totalSignal % un2det.referenceSignal %
578  un2det.uncertaintySignal;
579  msg << boost::format("reference ΔX = %.2f g/cm²\n") % un2det.DX;
580  // msg << boost::format("PlaneFrontTimeResidual = %.2f ns \n") %
581  //(-1. * un2det.pfTimeResidual);
582  msg << boost::format("Δt0 = %.2f ns (ref.: %.2f ± %.2f ns)\n") %
583  (-1. * un2det.pfTimeResidual) % un2det.referenceDt0[0] %
584  un2det.referenceDt40[1];
585  msg << boost::format("Δt40 = %.2f ns (ref.: %.2f ± %.2f ns)\n") %
586  (un2det.Dt40) % un2det.referenceDt40[0] %
587  un2det.referenceDt40[1];
588  msg << boost::format("Detector level Xmax: %.2f ± %.4f g/cm²\n") %
589  detRes.at(0).first % detRes.at(0).second;
590  msg << boost::format("Detector level Rmu: %.2f ± %.4f g/cm²\n") %
591  detRes.at(1).first % detRes.at(1).second;
592  msg << boost::format("Shift t0 = %.2f ns\n") % detRes.at(3).first;
593  msg << boost::format("CDF chi squared = %.2f ") % un2det.detChiSq;
594 
595  // final cut, remove station if the fit remained on the start value
596  if (detRes.at(0).first == 780. || un2det.detChiSq > 5.e2 * un2det.referenceSignal/20.) {
597 
598  INFO("CDF fit did not converge, not adding detector to PowerFit!");
599 
600  } else {
601 
602  INFO(msg);
603  INFO(boost::format("Detector %s %i added to PowerFit") %un2det.type %un2det.Id);
604 
605  // station level quantities that may not be needed in future
606  // versions of this fitting algorithm
607  un2det.detBestFitXmax = detRes.at(0).first;
608  un2det.detBestFitRmu = detRes.at(1).first;
609  /* detRes.at(2) skipped, energy is fixed */
610  un2det.detBestFitT0 = detRes.at(3).first;
611 
612  powerFit.AddDetectorData(un2det);
613 
614  auto &parDefsPow = powerFit.GetParameterDefs();
615 
616  // MUST be fixed
617  parDefsPow.push_back({detT0ParName, t0, 0.1, t0 - 10, t0 + 10, true});
618 
619  numTimeDet += 1;
620 
621  }
622  }
623  }
624 
625  // PYTHON DEBUG AND PLOTTY-PLOT OUTPUT
626  pythonDump << boost::format(" {\n");
627  pythonDump << boost::format(" \"type\": \"%s\", \n") % un2det.type;
628  pythonDump << boost::format(" \"isVirtual\": \"%s\", \n") %
629  un2det.isVirtual;
630  pythonDump << boost::format(" \"isSat\": \"%s\", \n") %
631  un2det.station.IsLowGainSaturation();
632  pythonDump << boost::format(" \"id\": %i, \n") % un2det.Id;
633  pythonDump << boost::format(" \"totalSignal\": %.3f, \n") %
634  un2det.totalSignal;
635  pythonDump << boost::format(" \"uncertaintySignal\": %.3f, \n") %
636  un2det.uncertaintySignal;
637  pythonDump << boost::format(" \"referenceSignal\": %.3f, \n") %
638  un2det.referenceSignal;
639  pythonDump << boost::format(" \"referencet40\": %.3f, \n") %
640  un2det.referenceDt40[0];
641  pythonDump << boost::format(" \"referencet0\": %.3f, \n") %
642  un2det.referenceDt0[0];
643  pythonDump << boost::format(" \"r\": %.2f, \n") % un2det.spRadius;
644  pythonDump << boost::format(" \"psi\": %.2f, \n") % un2det.spAzimuth;
645  pythonDump << boost::format(" \"h\": %.2f, \n") % un2det.heightAboveSL;
646  pythonDump << boost::format(" \"nsBin\": %.5f, \n") % un2det.nsBin;
647  pythonDump << boost::format(" \"sigStartSlot\": %.5f, \n") %
648  un2det.signalStartSlot;
649  pythonDump << boost::format(" \"sigStopSlot\": %.5f, \n") %
650  un2det.signalStopSlot;
651  pythonDump << boost::format(" \"stationNSTime\": %.5f, \n") %
652  un2det.stationNSTime;
653  pythonDump << boost::format(" \"stationPFTimeResidual\": %.5f, \n") %
654  un2det.pfTimeResidual;
655  pythonDump << boost::format(" \"AoP\": %.5f, \n") % un2det.avgAoP;
656  pythonDump << boost::format(" \"nPMTs\": %.5f, \n") %
657  un2det.station.GetNPMTs();
658  pythonDump
659  << boost::format(" \"commissionTimeGPSSecond\": %.2f, \n") %
660  sDet.GetStation(un2det.Id).GetCommissionTime().GetGPSSecond();
661 
662 
663  if (!un2det.HasTrace(sevt::StationConstants::eTotal)) {
664  pythonDump << boost::format(" }\n,");
665  break;
666  }
667 
668  // this part is buggy, i don't know why the trace throws an
669  // exception here from time to time (this was not always the case)
670  const utl::TraceD& thisTrace = un2det.GetTrace();
671  pythonDump << boost::format(" \"trace\": [");
672  for (unsigned int i = 0; i < thisTrace.GetSize()-1; ++i)
673  pythonDump << boost::format("%.4f, ") % thisTrace[i];
674  pythonDump << boost::format(" ] ,\n");
675 
676  pythonDump << boost::format(" \"integratedSignal\": [");
677  for (unsigned int i = 0; i < un2det.integratedSignal.size(); ++i)
678  pythonDump << boost::format("%.4f, ") % un2det.integratedSignal[i];
679  pythonDump << boost::format(" ] ,\n");
680 
681  pythonDump << boost::format(" \"bestFitIntegratedSignal\": [");
682  for (unsigned int i = 0; i < fitIntegratedSignal.size(); ++i)
683  pythonDump << boost::format("%.4f, ") % fitIntegratedSignal[i];
684  pythonDump << boost::format(" ] ,\n");
685 
686  pythonDump << boost::format(" \"predIntegratedSignal\": [");
687  for (unsigned int i = 0; i < predIntegratedSignal.size(); ++i)
688  pythonDump << boost::format("%.4f, ") % predIntegratedSignal[i];
689  pythonDump << boost::format(" ] ,\n");
690 
691  pythonDump << boost::format(" \"stdIntegratedSignal\": [");
692  for (unsigned int i = 0; i < stdIntegratedSignal.size(); ++i)
693  pythonDump << boost::format("%.4f, ") % stdIntegratedSignal[i];
694  pythonDump << boost::format(" ] ,\n");
695 
696  pythonDump << boost::format(" \"t0\": %.5f, \n") % un2det.detBestFitT0;
697  pythonDump << boost::format(" \"n\": %.5f, \n") % un2det.detBestFitN;
698  pythonDump << boost::format(" \"signalUnit\": %.5f, \n") %
699  un2det.signalUnit;
700  pythonDump << boost::format(" \"bestFitXmax\": %.5f, \n") %
701  un2det.detBestFitXmax;
702  pythonDump << boost::format(" \"bestFitRmu\": %.5f \n") %
703  un2det.detBestFitRmu;
704  pythonDump << boost::format(" }\n,");
705 
706  } // DETECTOR ITERATOR ENDS HERE
707 
708  pythonDump << boost::format("], \n");
709 
710  const double realRmu = isnan(RmuSum / wRmuSum) ? 666 : (RmuSum / wRmuSum);
711 
712  INFO(boost::format("dense ring Rmu: %.2f") % realRmu);
713  INFO(boost::format("Found %i regular and %i/%i virtual dense detectors.") %
714  numDet % numDenseDet % numDenseDetTot);
715  INFO(boost::format("Found %i detectors for time fit.") % numTimeDet);
716 
717  double XmaxResult = 0.;
718  double XmaxResultErr = 10000.;
719  double RmuResult = 0.;
720  double RmuResultErr = 10000.;
721  double lgEResult = 0;
722  double lgEResultErr = 10000.;
723 
724  // check whether to apply bias correctino to the later fit results
725  bool biasCorrection;
726  topB.GetChild("ApplyBiasCorrection").GetData(biasCorrection);
727 
728  // ENERGY FIT
729  // this fit is only performed properly for simulations, for data the recEnergy
730  // is used
731  auto &newEnergyDefs = EnergyFit.GetParameterDefs();
732  newEnergyDefs[0] = {"Xmax", avgXmax, .1, avgXmax - 50, avgXmax + 50, true};
733  newEnergyDefs[1] = {"Rmu", 1.1, .01, .9, 1.3, true};
734 
735 
736  double deltaLgEnergy;
737  topB.GetChild("EnergyConstraint").GetData(deltaLgEnergy);
738 
739  if (0.05 > deltaLgEnergy && !(fixEnergyToFd)) {
740  INFO("You are running in BENCHMARK MODE! Energy is tightly constrained to MC or S38 value!");
741  }
742 
743  const double startLgRecEnergy = (fixEnergyToFd && FdMeanLgEnergy > 0) ? FdMeanLgEnergy : log10(recShower.GetEnergy());
744  newEnergyDefs[2] = {"lgE", startLgRecEnergy, .015,
745  log10(recEnergy) - deltaLgEnergy, log10(recEnergy) + deltaLgEnergy, fixEnergyToFd};
746 
747  EnergyFit.SetParameterDefs(newEnergyDefs);
748 
749  if (0 == numEnDet) {
750  WARNING("ENERGY CANNOT BE ESTIMATED!");
751  }
752 
753  {
754 
755  utl::Minou::Minimizer<LDFFit> EnergyMin(EnergyFit);
756  EnergyMin.SetVerbose(verbose);
757  EnergyMin.Minimize(50000000);
758 
759  const auto energyResult = EnergyMin.GetParametersAndErrors();
760  lgEResult = energyResult.at(2).first;
761  lgEResultErr = energyResult.at(2).second;
762  INFO(boost::format("LDF lgE fit result: %.2f ± %.2f") % lgEResult %
763  lgEResultErr);
764 
765  }
766 
767  // LDF FIT
768  auto &newLDFDefs = LdfFit.GetParameterDefs();
769  // constrained, let fluctuate downwards for iron
770  // avgXmax is set to FD value if the respective flag is set
771  newLDFDefs[0] = {"Xmax", avgXmax, 3.,
772  avgXmax - 70, avgXmax + 40, true};
773  newLDFDefs[1] = {"Rmu", 1.15, .05, 0., 3., false};
774  newLDFDefs[2] = {"lgE", lgEResult, .005, lgEResult - 0.041, lgEResult + 0.041,
775  true};
776 
777  LdfFit.SetParameterDefs(newLDFDefs);
778  double XmaxResultLDFOnly, RmuResultLDFOnly;
779 
780  {
781 
782  utl::Minou::Minimizer<LDFFit> LdfMin(LdfFit);
783  LdfMin.SetVerbose(verbose);
784  LdfMin.Minimize(50000000);
785 
786  const auto LdfResult = LdfMin.GetParametersAndErrors();
787 
788  INFO(boost::format("LDF Xmax fit result: %.2f g/cm² ± %.2f g/cm²") %
789  LdfResult.at(0).first % LdfResult.at(0).second);
790  INFO(boost::format("LDF Rmu fit result: %.2f ± %.2f") %
791  LdfResult.at(1).first % LdfResult.at(1).second);
792 
793  XmaxResult = LdfResult.at(0).first;
794 
795  RmuResult = LdfResult.at(1).first;
796  RmuResultErr = LdfResult.at(1).second;
797 
798  lgEResult = LdfResult.at(2).first;
799  // lgEResultErr = LdfResult.at(2).second; // nonsense, since it should be tightly
800  // constrained
801 
802  XmaxResultLDFOnly = LdfResult.at(0).first;
803  RmuResultLDFOnly = LdfResult.at(1).first;
804 
805  }
806 
807 
808  // AugerPrime ratio fit
809  RatioFit.GeneratePairs();
810  auto &newRatioDefs = RatioFit.GetParameterDefs();
811  newRatioDefs[0] = {"Xmax", XmaxResult, 2., 600, 1200, true};
812  newRatioDefs[1] = {"Rmu", RmuResult, .05, 0, 3, false};
813  // fixed
814  newRatioDefs[2] = {"lgE", lgEResult, .001, lgEResult - 0.02, lgEResult + 0.02,
815  true};
816  powerFit.SetParameterDefs(newRatioDefs);
817 
818  if (2 < RatioFit.NumPairs()) {
819  utl::Minou::Minimizer<SignalRatioFit> RatioMin(RatioFit);
820  RatioMin.SetVerbose(verbose);
821  RatioMin.Minimize(50000000);
822 
823  const auto RatioResult = RatioMin.GetParametersAndErrors();
824 
825  INFO(boost::format("Ratio Rmu fit result: %.2f ± %.2f") %
826  RatioResult.at(1).first % RatioResult.at(1).second);
827 
828  RmuResult = RatioResult.at(1).first;
829  RmuResultErr = RatioResult.at(1).second;
830 
831  }
832 
833  if (biasCorrection) {
834  RmuResult +=
835  BiasCorrectionRmu(utl::Sqr(sin(recTheta)), log10(recEnergy), realEvent);
836  }
837 
838 
839  // POWER FIT
840  auto &newPowerDefs = powerFit.GetParameterDefs();
841  newPowerDefs[0] = {"Xmax", avgXmax, 5., 600, 1200, fixXmaxToFd};
842  // MUST be fixed in this stage if correlation is used
843  newPowerDefs[1] = {"Rmu", RmuResult, .01, RmuResult - 0.05, RmuResult + 0.05,
844  true};
845  // fixed
846  newPowerDefs[2] = {"lgE", lgEResult, .001, lgEResult - 0.02, lgEResult + 0.02,
847  true};
848  powerFit.SetParameterDefs(newPowerDefs);
849 
850  {
851 
852  utl::Minou::Minimizer<RmuXmaxFit> powerMin(powerFit);
853  powerMin.SetVerbose(verbose);
854  powerMin.Minimize(50000000);
855 
856  const auto powerResult = powerMin.GetParametersAndErrors();
857 
858  XmaxResult = powerResult.at(0).first;
859  XmaxResultErr = powerResult.at(0).second;
860 
861  // RmuResult = powerResult.at(1).first;
862  // RmuResultErr = powerResult.at(1).second;
863 
864  lgEResult = powerResult.at(2).first;
865  // lgEResultErr = powerResult.at(2).second;
866 
867  }
868 
869  if (biasCorrection) {
870  XmaxResult += BiasCorrectionXmax(utl::Sqr(sin(recTheta)), log10(recEnergy),
871  realEvent);
872  }
873 
874  const double recLnA = lnA(RmuResult, XmaxResult, log10(recEnergy), realEvent);
875 
876  pythonDump << boost::format("\"numDet\": %i,\n") % numDet;
877  pythonDump << boost::format("\"numEnDet\": %i,\n") % numEnDet;
878  pythonDump << boost::format("\"numTimeDet\": %i,\n") % numTimeDet;
879  pythonDump << boost::format("\"numSatDet\": %i,\n") % numSatDet;
880  pythonDump << boost::format("\"T5Status\": %i,\n") %
881  event.GetRecShower().GetSRecShower().GetT5Trigger();
882  pythonDump << boost::format("\"realRmu\": %.5f,\n") % realRmu;
883  pythonDump << boost::format("\"resultRmu\": %.5f,\n") % RmuResult;
884  pythonDump << boost::format("\"resultXmax\": %.5f,\n") % XmaxResult;
885  pythonDump << boost::format("\"resultlgE\": %.5f,\n") % lgEResult;
886  pythonDump << boost::format("\"uncRmu\": %.5f,\n") % RmuResultErr;
887  pythonDump << boost::format("\"uncXmax\": %.5f,\n") % XmaxResultErr;
888  pythonDump << boost::format("\"LdfRmu\": %.5f,\n") % RmuResultLDFOnly;
889  pythonDump << boost::format("\"LdfXmax\": %.5f,\n") % XmaxResultLDFOnly;
890  pythonDump << boost::format("\"recLnA\": %.5f,\n") % recLnA;
891  pythonDump << boost::format("\"FdMeanXmax\": %.5f,\n") % FdMeanXmax;
892  pythonDump << boost::format("\"FdXmax\": [");
893  for (unsigned int i = 0; i < FdXmax.size(); ++i)
894  pythonDump << boost::format("(%.4f, %.4f), ") % FdXmax[i].at(0) %
895  FdXmax[i].at(1);
896  pythonDump << boost::format("] ,\n");
897  pythonDump << boost::format("}\n");
898  pythonDump.close();
899 
900  INFO(boost::format("MC/average Xmax = %.2f g/cm²") % (realXmax));
901  INFO(boost::format("Combined Xmax fit result: %.2f g/cm² ± %.2f g/cm²") %
902  XmaxResult % XmaxResultErr);
903  INFO(boost::format("Combined Rmu fit result: %.2f ± %.2f") % RmuResult %
904  RmuResultErr);
905  INFO(boost::format("Xmax/Rmu correlation lnA: %.2f ") % recLnA);
906 
907  // EPILOG
908  // -----------------------------------------------------------------------------------
909 
910  if (event.HasSimShower())
911  event.GetSimShower().SetNmu(realRmu);
912 
913  event.GetRecShower().GetUnivRecShower().SetGoodRec(XmaxResultErr < 300. && RmuResultErr < 1.);
914  event.GetRecShower().GetUnivRecShower().SetNmu(RmuResult, RmuResultErr);
915  event.GetRecShower().GetUnivRecShower().SetXmax(XmaxResult * gcm,
916  XmaxResultErr * gcm);
917  event.GetRecShower().GetUnivRecShower().SetEnergy(pow(10., lgEResult),
918  pow(10, lgEResultErr));
919  event.GetRecShower().GetUnivRecShower().SetLnA(
920  recLnA, 0. /*error propagation needed*/);
921 
922  INFO(boost::format("Ending un2 reconstruction."));
923 }
924 
925 } // namespace un2
void SetVerbose(const bool verbose)
Definition: Minou.h:234
double CalcDX(const bool realAtmosphere, const bool useDL, const double spRadius, const double spPsi, const double groundHeight, const double xMax, const double theta, const int month, const bool silent)
double BiasCorrectionXmax(const double sst, const double lgE, const bool realEvent)
Definition: Functions.cc:384
double BiasCorrectionRmu(const double sst, const double lgE, const bool realEvent)
Definition: Functions.cc:404
bool HasUnivRecShower() const
constexpr double Rad2Deg(const double rad)
Definition: Functions.h:23
constexpr T Sqr(const T &x)
Point object.
Definition: Point.h:32
double GetPhi(const CoordinateSystemPtr &coordinateSystem) const
azimuth (phi) angle in spherical and cylindrical coordinates
Definition: BasicVector.h:254
total (shower and background)
evt::Header & GetHeader()
void GetTimeQuantile(const float *const trace, const float sum, const int nT, const float timeUnit, const float f, float &tQ, float &tQ_err)
boost::filter_iterator< ComponentSelector, ConstAllEyeIterator > ConstEyeIterator
Definition: FEvent.h:56
bool HasRecShower() const
bool HasFEvent() const
double GetTheta(const CoordinateSystemPtr &coordinateSystem) const
zenith (theta) angle in spherical coordinates
Definition: BasicVector.h:248
ShowerRecData & GetRecShower()
bool HasSimShower() const
#define INFO(message)
Macro for logging informational messages.
Definition: ErrorLogger.h:161
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 utl::TimeStamp & GetTime() const
Definition: Event/Header.h:33
boost::shared_ptr< const CoordinateTransformer > CoordinateSystemPtr
Shared pointer for coordinate systems.
Class representing a document branch.
Definition: Branch.h:107
double LongTermCorrectionPerEye(double EsdOverFd, double gpsmegatime, unsigned int eye)
EyeIterator EyesBegin(const ComponentSelector::Status status)
Definition: FEvent.h:58
ShowerSimData & GetSimShower()
vector< double > GetSignal(string model, double lgE, double zenith, double r, double psi, double height, double Xmax, double Rmu, string detector, int month, bool realAtmosphere, bool useThreshold)
Definition: GetSignal.cc:24
double GetEnergy() const
Get the energy of the shower primary particle.
Definition: ShowerSimData.h:89
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
std::vector< ParameterDef > & GetParameterDefs()
Definition: Minou.h:97
unsigned int signalStartSlot
Definition: UnivV2Rec.h:82
fevt::FEvent & GetFEvent()
int GetMonth() const
Definition: UTCDate.h:46
bool HasEye(const unsigned int eyeId, const ComponentSelector::Status status=ComponentSelector::eHasData) const
Definition: FEvent.cc:57
void SetNmu(const double Nmu)
double lnA(const double Rmu, const double Xmax, const double lgE, const bool realEvent)
Definition: Functions.cc:505
int Minimize(const int n=500)
Definition: Minou.h:250
double SignalUncertainty(const ESignalVarianceModel model, const double cosTheta, const double signal)
unsigned long GetGPSSecond() const
GPS second.
Definition: TimeStamp.h:124
static CentralConfig * GetInstance()
Use this the first time you get an instance of central configuration.
double avgXmax0(const double lgE, const bool realEvent)
Definition: Functions.cc:434
Vector object.
Definition: Vector.h:30
vector< double > GetIntegratedTotalSignalSum(vector< double > t, string model, double lgE, double zenith, double r, double psi, double height, double Xmax, double Rmu, string detector, int month, bool realAtmosphere, bool useThreshold, double planeFrontTimeResidual, double nsBin, double normalized)
Definition: GetSignal.cc:476
vector< double > GetIntegratedTotalSignal(vector< double > t, string model, double lgE, double zenith, double r, double psi, double height, double Xmax, double Rmu, string detector, int month, bool realAtmosphere, bool useThreshold, double planeFrontTimeResidual, double nsBin, string returnValue, int component, double normalized)
Definition: GetSignal.cc:393
double SignalUncertainty(const ESignalVarianceModel model, const double cosTheta, const double signal)
void SimpleReco(evt::Event &event)
Definition: UnivV2Rec.cc:48
#define ERROR(message)
Macro for logging error messages.
Definition: ErrorLogger.h:165
constexpr double m
Definition: AugerUnits.h:121
void SetParameterDefs(const std::vector< ParameterDef > &defs)
Definition: Minou.h:99
constexpr double gram
Definition: AugerUnits.h:195
mu+ and mu- (including signal from mu decay electrons) from shower
utl::Branch GetTopBranch(const std::string &id)
Get top branch for moduleConfigLink with given id (XML files)
utl::CoordinateSystemPtr GetShowerCoordinateSystem() const
constexpr double cm2
Definition: AugerUnits.h:118
r push_back(GetParameter(i))

, generated on Tue Sep 26 2023.