/* This file is part of MAUS: http://micewww.pp.rl.ac.uk:8080/projects/maus
*
* MAUS is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* MAUS is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with MAUS. If not, see .
*
*/
#include "src/common_cpp/Recon/EMR/TrackFitter.hh"
namespace TrackFitter {
void polynomial_fit(EMRSpacePointArray spacePointArray,
MAUS::EMRTrack& track,
unsigned int n) {
// Fill the arrays of spacepoints to fit
std::vector zx, zy, x, y, ex, ey, ez;
for (size_t iSP = 0; iSP < spacePointArray.size(); iSP++) {
int xOri = (spacePointArray[iSP]->GetChannel()/60)%2;
if ( !xOri ) {
zx.push_back(spacePointArray[iSP]->GetPosition().z());
ez.push_back(spacePointArray[iSP]->GetPositionErrors().z());
x.push_back(spacePointArray[iSP]->GetPosition().x());
ex.push_back(spacePointArray[iSP]->GetPositionErrors().x());
} else {
zy.push_back(spacePointArray[iSP]->GetPosition().z());
ez.push_back(spacePointArray[iSP]->GetPositionErrors().z());
y.push_back(spacePointArray[iSP]->GetPosition().y());
ey.push_back(spacePointArray[iSP]->GetPositionErrors().y());
}
}
// Fit the track in each projection with an order n polynomial
std::vector ax, ay; // Vector of parameters in the two projections
std::vector eax, eay; // Vector of parameters error in the two projections
double chi2x(-1.), chi2y(-1.); // Normalised chi squared in the two projections
if ( !x.size() ) {
for (size_t p = 0; p < n+1; p++)
ax.push_back(0.);
for (size_t p = 0; p < 2*(n+1); p++)
eax.push_back(0.);
chi2x = 0.;
} else if ( x.size() == 1 ) {
ax.push_back(x[0]);
eax.push_back(ex[0]*ex[0]);
for (size_t p = 1; p < n+1; p++)
ax.push_back(0.);
for (size_t p = 1; p < 2*(n+1); p++)
eax.push_back(0.);
chi2x = 0.;
} else {
polynomial_fit_2D(zx, x, ex, n, ax, eax, chi2x);
// minuit_fit(zx, x, ez, ex, n, ax, eax, chi2x);
// theil_sen_fit(zx, x, ex, ax, eax, chi2x);
}
if ( !y.size() ) {
for (size_t p = 0; p < n+1; p++)
ay.push_back(0.);
for (size_t p = 0; p < 2*(n+1); p++)
eay.push_back(0.);
chi2y = 0.;
} else if ( y.size() == 1 ) {
ay.push_back(y[0]);
eay.push_back(ey[0]*ey[0]);
for (size_t p = 1; p < n+1; p++)
ay.push_back(0.);
for (size_t p = 1; p < 2*(n+1); p++)
eay.push_back(0.);
chi2y = 0.;
} else {
polynomial_fit_2D(zy, y, ey, n, ay, eay, chi2y);
// minuit_fit(zy, y, ez, ey, n, ay, eay, chi2y);
// theil_sen_fit(zy, y, ey, ay, eay, chi2y);
}
// Compute the total chi square over number of degrees of freedom
double chi2(0.);
if ( x.size() > n+1 )
chi2 += chi2x/(x.size()-n-1);
if ( y.size() > n+1 )
chi2 += chi2y/(y.size()-n-1);
// Set the track parameters
track.SetParametersX(ax);
track.SetParametersErrorsX(eax);
track.SetParametersY(ay);
track.SetParametersErrorsY(eay);
track.SetChi2(chi2/2);
}
void polynomial_fit_2D(std::vector x,
std::vector y,
std::vector ey,
unsigned int n,
std::vector& a,
std::vector& ea,
double& chi2) {
// Set up the matrices
size_t N = x.size(); // Number of measurements
TMatrixD A(N, n+1); // Represents the powers of x: (x_i^0,x_i^1,...,x_i^n)
TMatrixD V_m(N, N); // Covariance matrix of measurements
TMatrixD Y(N, 1); // Measurements
for (size_t i = 0; i < N; ++i) {
for (size_t p = 0; p < n+1; p++)
A[i][p] = pow(x[i], p);
V_m[i][i] = (ey[i] * ey[i]);
Y[i][0] = y[i];
}
// Perform the inversions and multiplications which make up the least squares fit
double* det = NULL; // To hold the determinant
V_m.Invert(det); // Invert in place
TMatrixD At(A); // Copy A to At
At.T(); // Transpose At (leaving A unchanged)
TMatrixD V_p(At * V_m * A); // The covariance matrix of the parameters of model (inv)
V_p.Invert(det); // Invert in place
TMatrixD P(V_p * At * V_m * Y); // The least sqaures estimate of the parameters
// Extract the fit parameters and their covariance matrix
for (size_t p = 0; p < n+1; p++) {
a.push_back(P[p][0]);
for (size_t q = 0; q < n+1; q++)
ea.push_back(V_p[p][q]);
}
// Calculate the fit chisq
TMatrixD C(Y - (A * P)); // Residual vector
TMatrixD Ct(C); // Copy C to Ct
Ct.T(); // Transpose Ct (leaving C unchanged)
TMatrixD C2(Ct * V_m * C); // Total chi squared
chi2 = C2[0][0];
}
/*void polynomial_fit_2D(double* x, double* y,
double* ey, unsigned int n) {
// Set up the necessary averages
std::vector xp; // pth weighted moment of x, up to the 2*nth
std::vector xpy; // weighted average of x^p*y, up to x^n*y
for (size_t p = 0; p < n; p++) {
xp.push_back(0.);
xp.push_back(0.);
xpy.push_back(0.);
for (size_t i = 0; i < x.size(); i++) {
xp[p] += pow(x[i], p)/pow(ey[i], 2);
xp[p+1] += pow(x[i], p+1)/pow(ey[i], 2);
xpy[p] += pow(x[i], p)*y[i]/pow(ey[i], 2);
}
}
// Set up the matrices to find the least square fit
TMatrixD X(n+1, n+1); // Matrix of moments
TMatrixD XY[n+1]; // Matrix of moments with its pth column replaced by the x^p*y vector
for (size_t p = 0; p < n+1; p++) {
XY[p]->ResizeTo(n+1, n+1);
for (size_t q = 0; q < n+1; q++) {
X[p][q] = xp[p+q]; // p+qth moment
for (size_t r = 0; r < n+1; r++) {
if ( r != p ) {
XY[p][q][r] = xp[q+r];
} else {
XY[p][q][r] = xpy[p];
}
}
}
}
// If the determinant of X is zero, then the points are degenerated to one abscissa
if ( !X.Determinant() ) {
std::vector a;
for (size_t p = 0; p < n+1; p++) {
a.push_back(0.);
}
return a;
}
// Calculate the ai parameters of a polynomial of the form: a0+a1*x+...+an*x^n
std::vector a;
for (size_t p = 0; p < n+1; p++)
a.push_back(XY[p].Determinant()/X.Determinant());
return a;
}*/
/*void minuit_fit(std::vector x,
std::vector y,
std::vector ex,
std::vector ey,
unsigned int n,
std::vector& a,
std::vector& ea,
double& chi2) {
// Minimisation function computing the weighted chi square
auto Chi2Function = [&x, &y, &ex, &ey, &n](const double *par) {
double f = 0.;
double pol, dpol, v;
size_t i, j;
for (i = 0; i < x.size(); i++) {
// Compute the polynomial in x_i
pol = 0.;
for (j = 0; j < n+1; j++)
pol += par[j]*pow(x[i], j);
// Compute the derivate in x_i
dpol = 0.;
for (j = 0; j < n; j++)
dpol += par[j+1]*pow(x[i], j);
// Increment the chi squared
v = y[i] - pol;
f += v*v/(ey[i]*ey[i]+dpol*dpol*ex[i]*ex[i]);
}
return f;
};
// Wrap chi2 function in a function object for the fit
// n+1 is the number of fit parameters (size of array par)
ROOT::Math::Functor fcn(Chi2Function, n+1);
ROOT::Fit::Fitter fitter;
std::vector pStart(n+1, 1.);
fitter.SetFCN(fcn, &pStart[0]);
// Fit (Must handle fails, TODO)
bool ok = fitter.FitFCN();
if ( !ok ) {
a = {0, 0};
ea = {0, 0, 0, 0};
chi2 = 0.;
return;
}
// Copy the results into the parameter array
const ROOT::Fit::FitResult& result = fitter.Result();
result.Print(std::cout);
TMatrixD covmat(n+1, n+1);
result.GetCovarianceMatrix(covmat);
covmat.Invert();
size_t i, j;
for (i = 0; i < n+1; i++) {
a.push_back(result.Parameter(i));
for (j = 0; j < n+1; j++)
ea.push_back(covmat[i][j]);
}
chi2 = result.MinFcnValue();
}*/
void theil_sen_fit(std::vector x,
std::vector y,
std::vector ey,
std::vector& a,
std::vector& ea,
double& chi2) {
// The gradient of the slope is the median of the distribution of gradients
// of the lines joining all the possible combinations of points
std::vector gradient;
double gmean(0.), g2mean(0.);
for (size_t i = 0; i < x.size(); i++) {
for (size_t j = 0; j < x.size(); j++) {
if ( !(x[j]-x[i]) )
continue;
gradient.push_back((y[j]-y[i])/(x[j]-x[i]));
gmean += (y[j]-y[i])/(x[j]-x[i]);
g2mean += pow((y[j]-y[i])/(x[j]-x[i]), 2);
}
}
if ( !gradient.size() ) { // TODO handle it properly (infinite gradient)
a.push_back(0);
a.push_back(0);
ea.push_back(0);
ea.push_back(0);
chi2 = 0;
return;
}
std::sort(gradient.begin(), gradient.end());
double m = gradient.at(gradient.size()/2);
// The y intecept of the slope is the median of the distribution of
// y intercepts assuming m as a gradient for all points: y_i-m*x_i
std::vector intercept;
double imean(0.), i2mean(0.);
for (size_t i = 0; i < x.size(); i++) {
for (size_t j = 0; j < x.size(); j++) {
if ( i == j )
continue;
intercept.push_back(y[i] - m*x[i]);
imean += y[i] - m*x[i];
i2mean += pow(y[i] - m*x[i], 2);
}
}
std::sort(intercept.begin(), intercept.end());
double b = intercept.at(intercept.size()/2);
// The y intercept is the first parameter, the gradient the second
a.push_back(b);
a.push_back(m);
// Error propagation (uses the mean here, no such thing for the median)
ea.push_back(sqrt(i2mean/intercept.size()-pow(imean/intercept.size(), 2)));
ea.push_back(sqrt(g2mean/gradient.size()-pow(gmean/gradient.size(), 2)));
// Chi squared calculation
size_t N = x.size(); // Number of measurements
TMatrixD A(N, 2); // Represents the powers of x: (x_i^0, x_i^1)
TMatrixD V_m(N, N); // Covariance matrix of measurements
TMatrixD Y(N, 1); // Measurements
TMatrixD P(2, 1); // parameters
for (size_t i = 0; i < N; ++i) {
for (size_t p = 0; p < 2; p++)
A[i][p] = pow(x[i], p);
V_m[i][i] = (ey[i] * ey[i]);
Y[i][0] = y[i];
}
for (size_t p = 0; p < 2; p++)
P[p][0] = a[p];
TMatrixD C(Y - (A * P)); // Residual vector
TMatrixD Ct(C); // Copy C to Ct
Ct.T(); // Transpose Ct (leaving C unchanged)
TMatrixD C2(Ct * V_m * C); // Total chi squared
chi2 = C2[0][0];
}
double polar(double ax, double ay) {
return atan(sqrt(pow(ax, 2)+pow(ay, 2)));
}
double polar_error(double ax, double ay, double eax, double eay) {
if ( ax || ay )
return sqrt((pow(ax*eax, 2)+pow(ay*eay, 2))/((ax*ax+ay*ay)*(1+ax*ax+ay*ay)));
return 0;
}
double azimuth(double ax, double ay) {
if ( ay )
return (.5+(ay < 0))*M_PI - atan(ax/ay);
return (ax < 0)*M_PI;
}
double azimuth_error(double ax, double ay, double eax, double eay) {
if ( ax || ay )
return sqrt((pow(ay*eax, 2)+pow(ax*eay, 2))/(ax*ax+ay*ay));
return 0;
}
double pol(double x, std::vector par) {
double y(0.);
for (size_t p = 0; p < par.size(); p++)
y += par[p]*pow(x, p);
return y;
}
double pol_error(double x, double ex,
std::vector par, std::vector epar) {
// Build the parameters covariance matrix
TMatrixD V_p(par.size(), par.size());
for (size_t p = 0; p < par.size(); p++)
for (size_t q = 0; q < par.size(); q++)
V_p[p][q] = epar[p*par.size()+q];
// Calculate the variance on the measurement from the functional matrix
TMatrixD A(par.size(), 1);
for (size_t p = 0; p < par.size(); p++)
A[p][0] = pow(x, p);
TMatrixD At(A);
At.T();
TMatrix V_m(At*V_p*A);
double error2 = V_m[0][0];
// Error from the uncertainty on x, uncorrelated from the paramters
double dpol = dnpol(x, par, 1);
error2 += pow(dpol*ex, 2);
return sqrt(error2);
}
double dnpol(double x, std::vector par, unsigned int k) {
double y(0.);
for (size_t p = k; p < par.size(); p++)
y += (factorial(p)/factorial(p-k))*par[p]*pow(x, p-k);
return y;
}
double dnpol_error(double x, double ex,
std::vector par, std::vector epar, unsigned int k) {
// Build the parameters covariance matrix
TMatrixD V_p(par.size()-k, par.size()-k);
for (size_t p = k; p < par.size(); p++)
for (size_t q = k; q < par.size(); q++)
V_p[p-k][q-k] = epar[p*par.size()+q];
// Calculate the variance on the measurement from the functional matrix
TMatrixD A(par.size()-k, 1);
for (size_t p = k; p < par.size(); p++)
A[p-k][0] = (factorial(p)/factorial(p-k))*pow(x, p-k);
TMatrixD At(A);
At.T();
TMatrix V_m(At*V_p*A);
double error2 = V_m[0][0];
// Error from the uncertainty on x, uncorrelated from the parameters
double dpol(0.);
for (size_t p = k+1; p < par.size(); p++)
dpol += (factorial(p)/factorial(p-k-1))*pow(x, p-k-1);
error2 += pow(dpol*ex, 2);
return sqrt(error2);
}
double pol_closest(std::vector par, double xp, double yp, double xmin, double xmax) {
if ( par.size() == 1 ) {
return xp;
} else if ( par.size() == 2 ) {
return (xp + par[1]*(yp-par[0]))/(1. + par[1]*par[1]);
}
double p[par.size()+3];
p[0] = par.size();
for (size_t k = 0; k < par.size(); k++)
p[1+k] = par[k];
p[par.size()+1] = xp;
p[par.size()+2] = yp;
TF1 fdist("fdist", fdistance, xmin, xmax, par.size()+3);
fdist.SetParameters(p);
double xopt = fdist.GetMinimumX(xmin, xmax, pow(10, -3));
return xopt;
}
unsigned int factorial(unsigned int n) {
unsigned int f(1);
for (unsigned int i = n; i > 0; i--)
f *= i;
return f;
}
double fpol(double* x, double* par) {
double y(0.);
for (size_t p = 0; p < par[0]; p++)
y += par[p+1]*pow(x[0], p);
return y;
}
double fdistance(double *x, double *par) {
size_t n = par[0]; // Number of parameters
double pol = fpol(x, par); // Value of the polynomial
double xi(par[n+1]), yi(par[n+2]); // Coordinates of the point
return sqrt(pow(x[0]-xi, 2)+pow(pol-yi, 2));
}
} // namespace TrackFitter