LDFFitFunction.cc
Go to the documentation of this file.
1 #include "LDFFitFunction.h"
2 
3 #include <utl/RadioGeometryUtilities.h>
4 #include <utl/CoordinateSystemPtr.h>
5 #include <utl/AugerUnits.h>
6 #include <utl/Integrator.h>
7 #include <utl/AxialVector.h>
8 
9 #include <fwk/LocalCoordinateSystem.h>
10 
11 #include <iostream>
12 #include <cmath>
13 #include <vector>
14 
15 #include <TMath.h>
16 
17 using namespace utl;
18 
19 
20 namespace RdHASLDFFitter {
21 
22  double
23  LDFFitFunction::GeomagneticEmissionFactor(const double ceFraction,
24  const double sineAlpha,
25  const double cosAzimuth)
26  {
27  return std::pow(1 + cosAzimuth * std::sqrt(ceFraction) / std::abs(sineAlpha), -2);
28  }
29 
30 
31  double
32  LDFFitFunction::GeomagneticEmissionFactor(const double ceFraction,
33  const double sineAlpha,
34  const utl::Vector& showerAxis,
35  const utl::Vector& magneticFieldVector,
36  const utl::Point& showerCore,
37  const utl::Point& stationPosition)
38  {
39  const utl::Vector vxB = Cross(-showerAxis, magneticFieldVector);
40  const utl::Vector coreStation = stationPosition - showerCore;
41  const utl::Vector stationInShowerPlane = coreStation - (showerAxis * coreStation) * showerAxis;
42  const double cosAzimuth = CosAngle(vxB, stationInShowerPlane);
43 
44  return GeomagneticEmissionFactor(ceFraction, sineAlpha, cosAzimuth);
45  }
46 
47 
48  double
49  LDFFitFunction::ChargeExcessFractionParamICRC19(const double axisDistance,
50  const double distanceToXmax,
51  const double densityXmax)
52  {
53  /* Returns charee excess fraction for each observer defined as a = sin(alpha) ** 2 * f_ce / f_geo
54  See pos(ICRC2019)294 for more information */
55  const double a = 0.37313183;
56  const double b = 1.31124484; // / meter
57  const double p0 = 0.1889835;
58  const double p1 = 6.71403002; // / (kg / m3);
59  const double densityAvg = 0.4; // * (kg / m3);
60 
61  return a * (axisDistance / distanceToXmax) *
62  std::exp(b * axisDistance / 1000) * (std::exp(p1 * (densityXmax - densityAvg)) - p0);
63  }
64 
65 
66  double
67  LDFFitFunction::ChargeExcessFractionParamICRC21(const double axisDistance,
68  const double distanceToXmax,
69  const double densityXmax)
70  {
71  // Returns charee excess fraction for each observer defined as a = sin(alpha) ** 2 * f_ce / f_geo
72  const double a1 = -1.17523609e-06;
73  const double a2 = 0.348154734;
74  const double b = 1.6068519502678418; // / meter
75  const double p0 = 1.66965694e+01;
76  const double p1 = 3.31954904e+00;
77  const double p2 = -5.73577715e-03;
78 
79  const double a = a1 * distanceToXmax + a2;
80  return a * (axisDistance / distanceToXmax) *
81  std::exp(b * axisDistance / 1000) * (p0 * std::pow(densityXmax, p1) + p2);
82  }
83 
84 
85  double
86  LDFFitFunction::GetChargeExcessFraction(const double axisDistance)
87  const
88  {
89  /* Charge-excess fraction from parametrization.
90  fDistanceToXmax has to be in meters
91  fDensityAtXmax has to be in kg/m3 (which is taken care of in UpdateParameterParam) */
92 
93  double ceFraction = ChargeExcessFractionParamICRC21(axisDistance, fDistanceToXmax, fDensityAtXmax);
94 
95  /* For to low densityXmax resp. to large distanceToXmax / zenith angle ceFraction can become negative.
96  Set ceFraction = 0 as resonable approximation.
97  This happen for ChargeExcessFractionParamICRC19 with densityXmax <~0.15 kg/m3.
98  This can exclude the shower maxima of showers with a zenith angle ~> 82 deg.
99  This happen for ChargeExcessFractionParamICRC21 with densityXmax <~0.09 kg/m3.
100  This does not exclude any more shower maxima of showers < 85 deg.
101  (With an imperfect fitted fDistanceToXmax this can still a < 0 can still occur) */
102 
103  if (ceFraction < 0)
104  ceFraction = 0;
105 
106  return ceFraction;
107  }
108 
109 
110  double
111  LDFFitFunction::FGeomagneticParam(const double xvBvvB, const double yvBvvB, const double efvxB, const double cEarlyLate)
112  const
113  {
114  // xvBvvB, yvBvvB have to be in fitted core system
115 
116  // calculate axis distance
117  const double offAxisDistance = std::sqrt(Sqr(xvBvvB) + Sqr(yvBvvB)) / cEarlyLate;
118 
119  const double polarAngle = atan2(yvBvvB, xvBvvB);
120 
121  // get charge excess fraction from parametrization. set 0 beyond 80 deg and if get negative
122  const double ceFraction = GetChargeExcessFraction(offAxisDistance);
123 
124  // factor to get geomagnetic emission from f_vxB (f_geo = f_vxB * c_geo)
125  const double cGeo = GeomagneticEmissionFactor(ceFraction, fShowerData.fSineAlpha, cos(polarAngle));
126 
127  // get symmetrized geomagnetic emission
128  const double efGeoMeas = efvxB * cGeo * Sqr(cEarlyLate);
129 
130  return efGeoMeas;
131  }
132 
133 
134  double
135  LDFFitFunction::FVxBPrediction(const double xvBvvB, const double yvBvvB, const double cEarlyLate)
136  const
137  {
138  // xvBvvB, yvBvvB have to be in fitted core system
139 
140  // calculate axis distance
141  const double offAxisDistance = std::sqrt(Sqr(xvBvvB) + Sqr(yvBvvB)) / cEarlyLate;
142 
143  const double polarAngle = std::atan2(yvBvvB, xvBvvB);
144 
145  // get charge excess fraction from parametrization. set 0 beyond 80 deg and if get negative
146  const double ceFraction = GetChargeExcessFraction(offAxisDistance);
147 
148  // get symmetric signal prediction for the geomagnetic emission (at corrected position)
149  //const double efGeoModel = FABCD(offAxisDistance, fr0, fA, fB, fC, fD);
150  const double efGeoModel = FEgeo(offAxisDistance);
151 
152  // factor to get geomagnetic emission from f_vxB (f_geo = f_vxB * c_geo)
153  const double cGeo = GeomagneticEmissionFactor(ceFraction, fShowerData.fSineAlpha, std::cos(polarAngle));
154 
155  // add charge-excess to predicted geomagnetic model + 'revert' early late correction
156  const double efvxBPredict = efGeoModel / (cGeo * Sqr(cEarlyLate));
157 
158  return efvxBPredict;
159  }
160 
161 
162  double
163  LDFFitFunction::FGeomagneticPos(const double xvBvvB, const double yvBvvB, const double cEarlyLate, const StationFitData& station)
164  const
165  {
166  // xvBvvB, yvBvvB have to be in fitted core system
167  const double polarAngle = std::atan2(yvBvvB, xvBvvB);
168 
169  // get symmetrized geomagnetic emission
170  const double efGeoPos =
171  Sqr((std::sqrt(station.fEnergyFluenceVxB) - (cos(polarAngle) / std::abs(sin(polarAngle))) * std::sqrt(station.fEnergyFluenceVxVxB)) * cEarlyLate);
172 
173  // if station is to close at or on the vxB axis reject its from minimization
174  if (std::abs(std::sin(polarAngle)) < 0.1)
175  station.fRejected = true;
176  else
177  station.fRejected = false;
178 
179  return efGeoPos;
180  }
181 
182 
183  double
184  LDFFitFunction::FGeomagneticPosErr(const double xvBvvB, const double yvBvvB, const double efGeoPos, const StationFitData station)
185  const
186  {
187  // xvBvvB, yvBvvB have to be in fitted core system
188  const double polarAngle = std::atan2(yvBvvB, xvBvvB);
189 
190  // get symmetrized geomagnetic emission
191  const double efGeoPosErr = sqrt(efGeoPos * (std::pow(station.fEnergyFluenceVxBErr, 2) / station.fEnergyFluenceVxB +
192  std::pow((std::cos(polarAngle) * station.fEnergyFluenceVxVxBErr) / (std::abs(std::sin(polarAngle)) * std::sqrt(station.fEnergyFluenceVxVxB)), 2)));
193  // std::cout << efGeoPosErr / efGeoPos << ' ' << (cos(polarAngle) / abs(sin(polarAngle))) << ' ' << station.fRejected << ' ' << abs(sin(polarAngle)) << std::endl;
194  return efGeoPosErr;
195  }
196 
197 
199  LDFFitFunction::GetFittedRadioCoreTransformation()
200  const
201  {
202  // this "includes" the core fit
203  const utl::Point radioCore = fTransformationRefCore.GetVectorFromShowerPlaneVxB(fCoreX, fCoreY);
204  return RadioGeometryUtilities(fShowerData.fRefShowerAxis, fwk::LocalCoordinateSystem::Create(radioCore),
205  fShowerData.fMagneticField);
206  }
207 
208 
209  double
210  LDFFitFunction::operator()(const std::vector<double>& pars)
211  const
212  {
213  // update parameter according to parametrization
214  UpdateParameterParam(pars);
215 
216  // this "includes" the core fit
217  utl::RadioGeometryUtilities transformationRadioCore = GetFittedRadioCoreTransformation();
218 
219  double loss = 0;
220  for (const auto& s : fStationData) {
221 
222  double xvxB = 0;
223  double yvxB = 0;
224  double zvxB = 0;
225  transformationRadioCore.GetVectorInShowerPlaneVxB(xvxB, yvxB, zvxB, s.fPosition);
226 
227  const double cEarlyLate =
229  s.fCEarlyLate = cEarlyLate;
230 
231  const double offAxisDistance = std::sqrt(Sqr(xvxB) + Sqr(yvxB)) / cEarlyLate;
232 
233  if (fFitConfig.fUseParam) {
234  const double efvxBPredict = FVxBPrediction(xvxB, yvxB, cEarlyLate);
235  const double efGeoParam = FGeomagneticParam(xvxB, yvxB, s.fEnergyFluenceVxB, cEarlyLate);
236  const double ceFraction = GetChargeExcessFraction(offAxisDistance);
237 
238  s.fEnergyFluenceVxBPredict = efvxBPredict;
239  s.fCeFraction = ceFraction;
240  s.fEnergyFluenceGeomagnetic = efGeoParam;
241 
242  // Not Correct: Not considering uncertainty on fitted distance to Xmax, core, arrival direction
243  // (Only stored for plotting, not used in fit)
244  s.fEnergyFluenceGeomagneticErr = FGeomagneticParam(xvxB, yvxB, s.fEnergyFluenceVxBErr, cEarlyLate);
245 
246  // Calculate any quantity also for rejected stations, however ignore them in the minimization.
247  if (s.fRejected)
248  continue;
249 
250  loss += GetLoss(efvxBPredict, s.fEnergyFluenceVxB, s.fEnergyFluenceVxBErr);
251  } else {
252  const double efGeoPos = FGeomagneticPos(xvxB, yvxB, cEarlyLate, s);
253  const double efGeoPosErr = FGeomagneticPosErr(xvxB, yvxB, efGeoPos, s);
254  const double offAxisDistance = std::sqrt(Sqr(xvxB) + Sqr(yvxB)) / cEarlyLate;
255  const double efGeoModel = FEgeo(offAxisDistance);
256 
257  s.fEnergyFluenceGeomagnetic = efGeoPos;
258  s.fEnergyFluenceGeomagneticErr = efGeoPosErr;
259 
260  // Calculate any quantity also for rejected stations, however ignore them in the
261  // minimization. FGeomagneticPos is rejected if abs(sin(polarAngle)) < 0.1
262  if (s.fRejected)
263  continue;
264 
265  loss += GetLoss(efGeoModel, efGeoPos, efGeoPosErr);
266  }
267 
268  }
269 
270  return std::isnan(loss) ? std::numeric_limits<double>::max() : loss;
271  }
272 
273 
274  void
275  LDFFitFunction::GetPrediction(const std::vector<double>& pars)
276  {
277  this->operator()(pars);
278  }
279 
280 
281  double
282  LDFFitFunction::GetLossLog(const double model,
283  const double data,
284  const double uncertainty)
285  const
286  {
287  return Sqr((std::log(model) - std::log(data)) / (uncertainty / data)); // check if that makes sence
288  }
289 
290 
291  double
292  LDFFitFunction::GetLoss(const double model,
293  const double data,
294  const double uncertainty)
295  const
296  {
297  return Sqr((model - data) / uncertainty);
298  }
299 
300 
301  double
302  LDFFitFunction::GetChi2(const std::vector<double>& pars)
303  {
304  return this->operator()(pars);
305  }
306 
307 
308  int
309  LDFFitFunction::GetNDF()
310  {
311  double ndf = 0;
312 
313  // loop over all stations
314  for (const auto& s : fStationData) {
315  if (s.fRejected)
316  continue;
317  ++ndf;
318  }
319 
320  return ndf - GetNFreeParameters();
321  }
322 
323 
324  /*void
325  LDFFitFunction::TestStations(const std::vector<double>& pars, const double chi2_mean)
326  {
327  double chi2_station = 0;
328  for (const auto& s : fStationData) {
329  chi2_station = GetChi2HASLDFModel1(s.off_axis_distance, pars, s.f_geo, s.f_geo_err);
330  if (chi2_station > 10 * chi2_mean) {
331  std::cout << "Station " << s.id << " doesnt fit: " << chi2_station << " > 10 * "
332  << chi2_mean << " (f= " << s.f_geo << " +- " << s.f_geo_err << ")" << std::endl;
333  }
334  }
335  // can reject stations
336  }*/
337 
338 
339  // ----------------------- LDFFitFunctionPoly3 -------------------------//
340  double
341  LDFFitFunctionPoly3::GetNormalization()
342  const
343  {
344  const double B = fB;
345  const double C = fC;
346  const double D = fD;
347  const double r0 = fr0;
348 
349  auto LDFnorm = [&B, &C, &D, &r0](const double r)
350  { return kTwoPi * r * FABCD(r, r0, 1, B, C, D); };
351 
352  return MakeIntegrator(LDFnorm).GetIntegral(0, fRnorm) * fEgeoCorr;
353  }
354 
355 
356  double
357  LDFFitFunctionPoly3::FEgeo(const double r)
358  const
359  {
360  return fEgeo * FABCD(r, fr0, 1, fB, fC, fD) / fNorm;
361  }
362 
363 
364  double
365  LDFFitFunctionPoly3::FABCD(const double r, const double r0, const double a,
366  const double b, const double c, const double d)
367  {
368  const double x = r / r0;
369  return a * std::exp(x * (-b + x * (std::exp(c) - x * std::exp(d))));
370  }
371 
372 
373  // ------------------- LDFFitFunctionGaussSigmoid ----------------------//
374  double
375  LDFFitFunctionGaussSigmoid::CalculateR0()
376  const
377  {
378  const double refracIndexAtXmax = fShowerData.fRefracFromHeight.Y(fHeight);
379  const double cherenkovAngle = std::acos(1 / refracIndexAtXmax);
380  return std::tan(cherenkovAngle) * fDistanceToXmax;
381  }
382 
383 
384  double
385  LDFFitFunctionGaussSigmoid::GetNormalization()
386  const
387  {
388  const double r0 = fR0;
389  const double sig = fSig;
390  const double p = fP;
391  const double r02 = fR02;
392  const double arel = fArel;
393  const double slope = fSlope;
394 
395  if (fFitConfig.fUseSoftExponent) {
396  auto LDFnorm = [&r0, &sig, &p, &arel, &slope, &r02](const double r)
397  { return kTwoPi * r * FGaussSigmoidSoft(r, r0, sig, p, arel, slope, r02); };
398 
399  return MakeIntegrator(LDFnorm).GetIntegral(0, fRnorm) * fEgeoCorr;
400  } else {
401  auto LDFnorm = [&r0, &sig, &p, &arel, &slope, &r02](const double r)
402  { return kTwoPi * r * FGaussSigmoidHard(r, r0, sig, p, arel, slope, r02); };
403 
404  return MakeIntegrator(LDFnorm).GetIntegral(0, fRnorm) * fEgeoCorr;
405  }
406  }
407 
408 
409  double
410  LDFFitFunctionGaussSigmoid::FEgeo(const double r)
411  const
412  {
413  return fEgeo * FGaussSigmoid(r, fR0, fSig, fP, fArel, fSlope, fR02) / fNorm;
414  }
415 
416 
417  double
418  LDFFitFunctionGaussSigmoid::FGaussSigmoidSoft(
419  const double r, const double r0, const double sig, double p,
420  const double arel, const double slope, const double r02)
421  {
422  const double pEff = (r > r0) ? 2 * std::pow(r0 / r, p / 1000) : 2;
423  return std::exp(-std::pow((r - r0) / sig, pEff)) + arel / (1 + std::exp(slope * (r / r0 - r02)));
424  }
425 
426  double
427  LDFFitFunctionGaussSigmoid::FGaussSigmoidHard(
428  const double r, const double r0, const double sig, double p,
429  const double arel, const double slope, const double r02)
430  {
431  const double pEff = (r > r0) ? p : 2;
432  return std::exp(-std::pow((r - r0) / sig, pEff)) + arel / (1 + std::exp(slope * (r / r0 - r02)));
433  }
434 
435 }
AxialVector Cross(const Vector &l, const Vector &r)
Definition: OperationsAV.h:25
constexpr T Sqr(const T &x)
Point object.
Definition: Point.h:32
static double GetEarlyLateCorrectionFactor(const utl::Point &showerCore, const utl::Point &stationPosition, const utl::Point &showerMax, const utl::Vector &showerAxis)
double pow(const double x, const unsigned int i)
#define max(a, b)
constexpr double s
Definition: AugerUnits.h:163
double abs(const SVector< n, T > &v)
constexpr double kTwoPi
Definition: MathConstants.h:27
void GetVectorInShowerPlaneVxB(double &x, double &y, double &z, const utl::Point &point) const
in case of positions, the positions has to be relative to the core positions!!!
uint16_t * data
Definition: dump1090.h:228
Vector object.
Definition: Vector.h:30
double CosAngle(const Vector &l, const Vector &r)
Definition: OperationsVV.h:71
static Policy::type Create(const utl::Point &theOrigin)
Create the standard local coordinate system for a Point.
Integrator< Functor > MakeIntegrator(Functor &f)
convenience factory
Definition: Integrator.h:236

, generated on Tue Sep 26 2023.