SdPlaneFit.cc
Go to the documentation of this file.
1 #include <cmath>
2 
3 #include <fwk/CentralConfig.h>
4 #include <fwk/RunController.h>
5 #include <fwk/LocalCoordinateSystem.h>
6 
7 #include <det/Detector.h>
8 
9 #include <sdet/SDetector.h>
10 #include <sdet/STimeVariance.h>
11 
12 #include <evt/Event.h>
13 #include <evt/ShowerRecData.h>
14 #include <evt/ShowerSRecData.h>
15 #include <evt/ShowerSimData.h>
16 
17 #include <sevt/SEvent.h>
18 #include <sevt/EventTrigger.h>
19 #include <sevt/Station.h>
20 #include <sevt/StationRecData.h>
21 
22 #include <utl/Reader.h>
23 #include <utl/ErrorLogger.h>
24 #include <utl/PhysicalConstants.h>
25 #include <utl/AxialVector.h>
26 #include <utl/Math.h>
27 #include <utl/String.h>
28 #include <utl/config.h>
29 
30 #include <CLHEP/Matrix/Matrix.h>
31 #include <CLHEP/Matrix/Vector.h>
32 
33 #include <TMinuit.h>
34 
35 #include "SdPlaneFit.h"
36 
37 using namespace SdPlaneFitOG;
38 using namespace fwk;
39 using namespace evt;
40 using namespace sevt;
41 using namespace utl;
42 using namespace std;
43 
44 using CLHEP::HepMatrix;
45 using CLHEP::HepVector;
46 
47 
48 #define OUT(_x_) if ((_x_) <= fInfoLevel) cerr
49 
50 
51 // global variables
56 
57 
58 namespace SdPlaneFitOG {
59 
60  // square of the perpendicular distance from the axis
61  inline
62  double
63  RPerp2(const Vector& axis, const Vector& station)
64  {
65  const double scal = axis * station;
66  return station*station - scal*scal;
67  }
68 
69 
72  {
73  const auto topB = CentralConfig::GetInstance()->GetTopBranch("SdPlaneFit");
74 
75  topB.GetChild("infoLevel").GetData(fInfoLevel);
76  if (fInfoLevel < eNone || fInfoLevel > eMinuit) {
77  INFO("<infoLevel> out of bounds");
78  return eFailure;
79  }
80  fMinuitOutput = (fInfoLevel >= eMinuit);
81 
82  topB.GetChild("minNumberOfStations").GetData(fMinNumberOfStations);
83  if (fMinNumberOfStations <= 2) {
84  INFO("<minNumberOfStations> must be > 2");
85  return eFailure;
86  }
87 
88  return eSuccess;
89  }
90 
91 
92  // analytic linear fit in local CS; approximation by neglecting
93  // z-coordinate, since z ~ 0 in local bary coordinate system,
94  // therefore losing the w component of the axis. it is funny
95  // though, that stations living on a plane can not distinguish
96  // between upwards or downwards-going showers.
97 
99  SdPlaneFit::LinearFit(FitParameters& fit, const bool reuseFit)
100  const
101  {
102  const double cosTheta = (reuseFit ? fit.w : 1);
103  const Vector approxAxis =
104  (reuseFit ? Vector(fit.u, fit.v, fit.w, fgBarycenterCS) : Vector());
105  const auto& sDetector = det::Detector::GetInstance().GetSDetector();
106  const auto& timeVariance = sdet::STimeVariance::GetInstance();
107 
108  double s1 = 0;
109  double sx = 0;
110  double sy = 0;
111  double st = 0;
112  double sxx = 0;
113  double sxy = 0;
114  double syy = 0;
115  double sxt = 0;
116  double syt = 0;
117  //double stt = 0;
118 
119  for (const auto& s : fgCurrentSEvent->CandidateStationsRange()) {
120  const Vector sPos = sDetector.GetStation(s).GetPosition() - fgBarycenter;
121  const double x = sPos.GetX(fgBarycenterCS);
122  const double y = sPos.GetY(fgBarycenterCS);
123  const auto& sRec = s.GetRecData();
124  const double t = (sRec.GetSignalStartTime() - fgBaryTime).GetInterval();
125  const double sigma2 =
126  timeVariance.GetTimeSigma2(sRec.GetTotalSignal(), sRec.GetT50(), cosTheta, sqrt(RPerp2(approxAxis, sPos))) +
127  sRec.GetGPSTimeVariance();
128  const double invSigma2 = 1 / sigma2;
129  s1 += invSigma2;
130  const double x_s = x * invSigma2;
131  sx += x_s;
132  sxx += x*x_s;
133  sxy += y*x_s;
134  sxt += t*x_s;
135  const double y_s = y * invSigma2;
136  sy += y_s;
137  syy += y*y_s;
138  syt += t*y_s;
139  st += t * invSigma2;
140  //stt += t*t;
141  }
142 
143  st *= kSpeedOfLight;
144  sxt *= kSpeedOfLight;
145  syt *= kSpeedOfLight;
146  //stt *= kSpeedOfLight*kSpeedOfLight;
147 
148  HepMatrix mat(3, 3);
149 
150  mat[0][0] = sxx; mat[0][1] = sxy; mat[0][2] = -sx;
151  mat[1][0] = sxy; mat[1][1] = syy; mat[1][2] = -sy;
152  mat[2][0] = -sx; mat[2][1] = -sy; mat[2][2] = s1;
153 
154  int ierr = 0;
155  mat.invert(ierr);
156 
157  if (ierr) {
158  INFO("Matrix inversion failed.");
159  return eFailure;
160  }
161 
162  HepVector k(3);
163 
164  k[0] = -sxt;
165  k[1] = -syt;
166  k[2] = st;
167 
168  const HepVector p = mat*k;
169 
170  const double w2 = 1 - Sqr(p[0]) - Sqr(p[1]);
171 
172  if (w2 < 0) {
173  INFO("Unphysical direction cosines");
174  return eFailure;
175  }
176 
177  fit.u = p[0];
178  fit.v = p[1];
179  fit.w = sqrt(w2);
180  fit.ct0 = p[2];
181 
182  fit.sigmaU2 = mat[0][0];
183  fit.sigmaV2 = mat[1][1];
184  fit.sigmaUV = mat[0][1];
185  fit.sigmact02 = mat[2][2];
186 
187  fit.stage = (reuseFit ? ShowerSRecData::kPlaneFitLinear2 : ShowerSRecData::kPlaneFitLinear);
188 
189  return eSuccess;
190  }
191 
192 
195  {
196  // nothing to do if there is no SEvent
197  if (!event.HasSEvent())
198  return eContinueLoop;
199 
200  fgCurrentSEvent = &event.GetSEvent();
201  const auto& sEvent = *fgCurrentSEvent;
202 
203  const auto& detector = det::Detector::GetInstance();
204  const auto& sDetector = detector.GetSDetector();
205 
206  // calculate barycenter and bary-time and determine the # of relevant stations
207 
208  const auto& siteCS = detector.GetSiteCoordinateSystem();
209 
210  // the absolute points in time and space
211  const Point siteOrigin(0,0,0, siteCS);
212 
213  if (!event.GetSEvent().HasTrigger())
214  return eContinueLoop;
215 
216  const auto& eventTime = event.GetSEvent().GetTrigger().GetTime();
217 
218  // weighted sums
219  Vector barySum(0,0,0, siteCS);
220  TimeInterval timeSum = 0;
221  double weightSum = 0;
222  int nStations = 0;
223  for (const auto& s : sEvent.CandidateStationsRange()) {
224  const auto& dStation = sDetector.GetStation(s);
225  const auto& sRec = s.GetRecData();
226  const double weight = sqrt(sRec.GetTotalSignal());
227  weightSum += weight;
228  barySum += weight * (dStation.GetPosition() - siteOrigin);
229  timeSum += weight * (sRec.GetSignalStartTime() - eventTime);
230  ++nStations;
231  }
232 
233  OUT(eIntermediate)
234  << "# candidate stations = " << nStations << endl;
235 
236  // not worth it
237  if (nStations < fMinNumberOfStations) {
238  OUT(eFinal)
239  << "Not enough stations." << endl;
240  return eContinueLoop;
241  }
242 
243  if (!weightSum) {
244  OUT(eObscure)
245  << "sum of weights = 0" << endl;
246  return eContinueLoop;
247  }
248 
249  barySum /= weightSum;
250  timeSum /= weightSum;
251  fgBarycenter = siteOrigin + barySum;
252  fgBaryTime = eventTime + timeSum;
253  fgBarycenterCS = LocalCoordinateSystem::Create(fgBarycenter);
254 
255  OUT(eIntermediate)
256  << "barycenter = (" << String::Make(fgBarycenter, siteCS, meter, ", ") << ") m (site)\n"
257  "bary time = " << timeSum/nanosecond << " ns (to event time)\n";
258 
259  OUT(eObscure)
260  << "local/site zenith angle diff = "
261  << Angle(Vector(0,0,1, fgBarycenterCS), Vector(0,0,1, siteCS))/degree
262  << " deg\n";
263 
264  if (event.HasSimShower()) {
265  const auto mcAxis = -event.GetSimShower().GetDirection();
266  OUT(eIntermediate)
267  << "MC axis = (" << String::Make(mcAxis, fgBarycenterCS, 1, ", ") << ") (bary)\n";
268  }
269 
270  // this will point to the final result
271  FitParameters* fit = nullptr;
272 
273  // first approximation
274  FitParameters fitLin;
275  if (LinearFit(fitLin) != eSuccess)
276  return eContinueLoop;
277  // try again with previous values as approxiamtions for sigma
278  LinearFit(fitLin, true);
279 
280  // with first approximation on u and v, the nonlinear fit is attempted
281  FitParameters fitNonlin = fitLin;
282  if (PlaneFit3DDriver(fitNonlin) == eSuccess) {
283  //fitNonlin.stage = ShowerSRecData::kPlaneFit3d;
284  OUT(eIntermediate)
285  << "Stage " << fitLin.stage << ": linear plane fit\n"
286  " axis = (" << fitLin.u << ", " << fitLin.v << ", " << fitLin.w << ") (bary)\n"
287  " ct0 = " << fitLin.ct0/meter << " m" << endl;
288  fit = &fitNonlin;
289  } else {
290  OUT(eIntermediate)
291  << " Nonlinear fit failed, using linear approximation." << endl;
292  fit = &fitLin;
293  }
294 
295  // Fill the event
296  if (!event.HasRecShower())
297  event.MakeRecShower();
298 
299  if (!event.GetRecShower().HasSRecShower())
300  event.GetRecShower().MakeSRecShower();
301 
302  auto& showerSRec = event.GetRecShower().GetSRecShower();
303 
304  showerSRec.SetBarycenter(fgBarycenter);
305  showerSRec.SetBarytime(fgBaryTime);
306 
307  const Vector axis(fit->u, fit->v, fit->w, fgBarycenterCS);
308  showerSRec.SetAxis(axis);
309 
310  showerSRec.SetAngleErrors(axis.GetCoordinates(fgBarycenterCS),
311  Vector::Triple(fit->sigmaU2, fit->sigmaUV, fit->sigmaV2));
312 
313  showerSRec.SetCurvature(0, 0);
314  showerSRec.SetCorePosition(fgBarycenter);
315  showerSRec.SetCoreTime(fgBaryTime + TimeInterval(fit->ct0/kSpeedOfLight),
316  TimeInterval(sqrt(fit->sigmact02)));
317 
318  double timeMean3d = 0;
319  double timeSpread3d = 0;
320  double chi2 = 0;
321  CalculateTimeResidual3D(axis, fit->ct0, timeMean3d, timeSpread3d, chi2);
322 
323  showerSRec.SetTimeResidualMean(timeMean3d);
324  showerSRec.SetTimeResidualSpread(timeSpread3d);
325  const int angleNDOF = nStations - 3;
326  showerSRec.SetAngleChi2(chi2, angleNDOF);
327 
328  showerSRec.SetLDFRecStage(fit->stage);
329 
330  OUT(eFinal)
331  << "Stage " << fit->stage << ": "
332  << (fit == &fitLin ? "linear" : "3d" )
333  << " plane fit\n"
334  " axis = (" << fit->u << ", " << fit->v << ", " << fit->w << ") (bary)\n"
335  " theta = " << acos(fit->w)/degree << " +/- "
336  << showerSRec.GetThetaError()/degree << " deg (bary)\n"
337  " phi = " << atan2(fit->v, fit->u)/degree << " +/- "
338  << showerSRec.GetPhiError()/degree << " deg\n"
339  " ct0 = " << fit->ct0/meter << " m\n"
340  " time chi2 = " << chi2 << " / " << angleNDOF << "\n"
341  " time residual = " << timeMean3d << " +/- "
342  << timeSpread3d << endl;
343  if (fit != &fitLin)
344  OUT(eFinal)
345  << " axis diff = " << Angle(Vector(fitLin.u, fitLin.v, fitLin.w, fgBarycenterCS), axis)/degree
346  << " deg (to stage " << fitLin.stage << ")\n";
347 
348  // fill the new structure for plane-front reconstruction
349  if (!showerSRec.HasPlaneFrontRecData())
350  showerSRec.MakePlaneFrontRecData();
351  auto& planeFrontRec = showerSRec.GetPlaneFrontRecData();
352 
353  planeFrontRec.SetBarycenter(fgBarycenter);
354  planeFrontRec.SetBarycenterTime(fgBaryTime + TimeInterval(fit->ct0/kSpeedOfLight));
355  planeFrontRec.SetAxis(axis);
356  planeFrontRec.SetAngleErrors(axis.GetCoordinates(fgBarycenterCS),
357  Vector::Triple(fit->sigmaU2, fit->sigmaUV, fit->sigmaV2));
358  planeFrontRec.SetAngleChi2Ndof(chi2, angleNDOF);
359  planeFrontRec.SetTimeResidualMean(timeMean3d, timeSpread3d);
360 
361  ++RunController::GetInstance().GetRunData().GetNamedCounters()["SdPlaneFit/Axis"];
362 
363  return eSuccess;
364  }
365 
366 
367  void
368  SdPlaneFit::CalculateTimeResidual3D(const Vector& axis, const double ct0,
369  double& mean, double& spread, double& chi2)
370  const
371  {
372  const Vector axis_c = axis / kSpeedOfLight;
373  const double t0 = ct0 / kSpeedOfLight;
374 
375  const auto& sDetector = det::Detector::GetInstance().GetSDetector();
376  const auto& timeVariance = sdet::STimeVariance::GetInstance();
377 
378  const double cosTheta = axis.GetCosTheta(fgBarycenterCS);
379 
380  double sum = 0;
381  chi2 = 0;
382  int n = 0;
383  for (auto& s : fgCurrentSEvent->CandidateStationsRange()) {
384 
385  const Vector vx = sDetector.GetStation(s).GetPosition() - fgBarycenter;
386  const double t = (s.GetRecData().GetSignalStartTime() - fgBaryTime).GetInterval();
387 
388  auto& sRec = s.GetRecData();
389  const double sigma2 =
390  timeVariance.GetTimeSigma2(sRec.GetTotalSignal(), sRec.GetT50(), cosTheta, sqrt(RPerp2(axis, vx))) +
391  sRec.GetGPSTimeVariance();
392  const double residual = (t - t0 + vx*axis_c) / std::sqrt(sigma2);
393  sRec.SetResidual(residual);
394  sum += residual;
395  chi2 += Sqr(residual);
396  ++n;
397  }
398 
399  mean = sum / n;
400  spread = sqrt(chi2/(n - 1));
401  }
402 
403 
406  const
407  {
408  TMinuit minuit(3);
409  int errFlag = 0;
410  double argList[10];
411  argList[0] = -1;
412  if (!fMinuitOutput) {
413  minuit.mnexcm("SET PRINTOUT", argList, 1, errFlag);
414  minuit.mnexcm("SET NOWARNINGS", argList, 0, errFlag);
415  minuit.SetPrintLevel(-1);
416  }
417 
418  minuit.SetFCN(SdPlaneFit::PlaneFit3DFnc);
419  argList[0] = 1; // chi2
420  minuit.mnexcm("SET ERRORDEF", argList, 1, errFlag);
421 
422  minuit.mnparm(0, "u", fit.u, 0.01, 0, 0, errFlag);
423  minuit.mnparm(1, "v", fit.v, 0.01, 0, 0, errFlag);
424  minuit.mnparm(2, "ct0", fit.ct0, 10*meter, 0, 0, errFlag);
425 
426  // init fnc
427  argList[0] = 1;
428  minuit.mnexcm("CALI", argList, 1, errFlag);
429 
430  argList[0] = 500;
431  minuit.mnexcm("MINIMIZE", argList, 1, errFlag);
432  if (errFlag) {
433  OUT(eObscure)
434  << " minuit minimize error: " << errFlag << '\n';
435  minuit.mnexcm("MINOS", argList, 0, errFlag);
436  if (errFlag) {
437  OUT(eObscure)
438  << " minuit minos error: " << errFlag << '\n';
439  return eFailure;
440  }
441  }
442 
443  // Get results
444  double foo = 0;
445  minuit.GetParameter(0, fit.u, foo);
446  minuit.GetParameter(1, fit.v, foo);
447  const double w2 = 1 - Sqr(fit.u) - Sqr(fit.v);
448  if (w2 < 0) {
449  OUT(eIntermediate)
450  << " Fit failed due to unphysical angle cosines." << endl;
451  return eFailure;
452  }
453  fit.w = sqrt(w2);
454  minuit.GetParameter(2, fit.ct0, foo);
455 
456  // error matrix
457  double emat[3][3];
458  minuit.mnemat(&emat[0][0], 3);
459  fit.sigmaU2 = emat[0][0];
460  fit.sigmaV2 = emat[1][1];
461  fit.sigmaUV = emat[0][1];
462  fit.sigmact02 = emat[2][2];
463 
464  fit.stage = ShowerSRecData::kPlaneFit3d;
465 
466  return eSuccess;
467  }
468 
469 
470  void
471  SdPlaneFit::PlaneFit3DFnc(int& /*nPar*/, double* const /*grad*/, double& value,
472  double* const par, const int flag)
473  {
474  const double& u = par[0];
475  const double& v = par[1];
476  const double& ct0 = par[2];
477 
478  struct StationData {
479  double fCTiming;
480  double fT50;
481  double fGPSVar;
482  double fSignal;
483  Vector fPosition;
484  };
485 
486  static vector<StationData> sData;
487  static const sdet::STimeVariance* timeVariance = nullptr;
488 
489  if (flag == 1) { // fnc init
490  const auto& sDetector = det::Detector::GetInstance().GetSDetector();
491  sData.clear();
492  for (const auto& s : fgCurrentSEvent->CandidateStationsRange()) {
493  const auto& sRec = s.GetRecData();
494  sData.emplace_back(StationData{
495  kSpeedOfLight * (sRec.GetSignalStartTime() - fgBaryTime).GetInterval(),
496  sRec.GetT50(),
497  sRec.GetGPSTimeVariance(),
498  sRec.GetTotalSignal(),
499  sDetector.GetStation(s).GetPosition() - fgBarycenter
500  });
501  }
502  timeVariance = &sdet::STimeVariance::GetInstance();
503  }
504 
505  const double w2 = 1 - Sqr(u) - Sqr(v);
506 
507  if (w2 < 0) {
508  WARNING("complex w");
509  value = 1e30;
510  return;
511  }
512 
513  const double w = sqrt(w2);
514 
515  const Vector axis(u,v,w, fgBarycenterCS);
516 
517  double chi2 = 0;
518 
519  for (const auto& d : sData) {
520  chi2 +=
521  Sqr(d.fCTiming - ct0 + axis * d.fPosition) /
522  (timeVariance->GetTimeSigma2(d.fSignal, d.fT50, w, std::sqrt(RPerp2(axis, d.fPosition))) + d.fGPSVar);
523  }
524 
525  value = chi2 / kSpeedOfLight2;
526  }
527 
528 }
std::string Make(const Geo &g, const CS &cs, const double unit=1, const std::string &sep=" ")
Definition: String.h:190
const double degree
constexpr T Sqr(const T &x)
Point object.
Definition: Point.h:32
double Angle(const double theta1, const double phi1, const double theta2, const double phi2)
#define OUT(_x_)
Definition: SdPlaneFit.cc:48
bool HasRecShower() const
Interface class to access to the SD part of an event.
Definition: SEvent.h:39
ShowerRecData & GetRecShower()
double GetCosTheta(const CoordinateSystemPtr &coordinateSystem) const
cos of zenith (theta) angle
Definition: BasicVector.h:251
bool HasSimShower() const
const double meter
Definition: GalacticUnits.h:29
#define INFO(message)
Macro for logging informational messages.
Definition: ErrorLogger.h:161
static void PlaneFit3DFnc(int &nPar, double *const grad, double &value, double *const par, const int flag)
Definition: SdPlaneFit.cc:471
double RPerp2(const Vector &axis, const Vector &station)
Definition: SdPlaneFit.cc:63
A TimeStamp holds GPS second and nanosecond for some event.
Definition: TimeStamp.h:110
static utl::CoordinateSystemPtr fgBarycenterCS
Definition: SdPlaneFit.h:76
boost::shared_ptr< const CoordinateTransformer > CoordinateSystemPtr
Shared pointer for coordinate systems.
boost::tuple< double, double, double > Triple
Coordinate triple for easy getting or setting of coordinates.
Definition: Triple.h:15
constexpr double s
Definition: AugerUnits.h:163
double GetX(const CoordinateSystemPtr &coordinateSystem) const
Definition: BasicVector.h:206
constexpr double nanosecond
Definition: AugerUnits.h:143
bool HasTrigger() const
check whether the central trigger object exists
Definition: SEvent.h:153
static utl::TimeStamp fgBaryTime
Definition: SdPlaneFit.h:75
fwk::VModule::ResultFlag LinearFit(FitParameters &fit, const bool reuseFit=false) const
Definition: SdPlaneFit.cc:99
void CalculateTimeResidual3D(const utl::Vector &axis, const double ct0, double &mean, double &spread, double &chi2) const
Definition: SdPlaneFit.cc:368
#define WARNING(message)
Macro for logging warning messages.
Definition: ErrorLogger.h:163
static utl::Point fgBarycenter
Definition: SdPlaneFit.h:74
double GetY(const CoordinateSystemPtr &coordinateSystem) const
Definition: BasicVector.h:209
constexpr double kSpeedOfLight
fwk::VModule::ResultFlag Run(evt::Event &event) override
Run: invoked once per event.
Definition: SdPlaneFit.cc:194
A TimeInterval is used to represent time elapsed between two events.
Definition: TimeInterval.h:43
ResultFlag
Flag returned by module methods to the RunController.
Definition: VModule.h:60
fwk::VModule::ResultFlag PlaneFit3DDriver(FitParameters &fit) const
Definition: SdPlaneFit.cc:405
Vector object.
Definition: Vector.h:30
static sevt::SEvent * fgCurrentSEvent
Definition: SdPlaneFit.h:73
constexpr double kSpeedOfLight2
void LinearFit(const vector< double > &x, const vector< double > &y, const vector< double > &ey, double &a0, double &a1, double &chi2)
Do a linear fit and return coefficients and chi2.
bool HasSRecShower() const
sevt::SEvent & GetSEvent()
bool HasSEvent() const
fwk::VModule::ResultFlag Init() override
Initialize: invoked at beginning of run (NOT beginning of event)
Definition: SdPlaneFit.cc:71
double GetTimeSigma2(const double signal, const double t50, const double cosTheta, const double distance=0) const

, generated on Tue Sep 26 2023.