/* 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
#include
#include
#include
// ROOT headers
#include "TLinearFitter.h"
#include "TMatrixTBase.h"
#include "TMath.h"
#include "Math/Functor.h"
#include "Fit/Fitter.h"
#include "Minuit2/Minuit2Minimizer.h"
// MAUS headers
#include "src/common_cpp/Recon/SciFi/RootFitter.hh"
#define PI 3.14159265
namespace RootFitter {
bool FitLineLinear(const std::vector& x, const std::vector& y,
const std::vector& yerr, MAUS::SimpleLine& line, TMatrixD& cov_matrix) {
TLinearFitter lf(1);
lf.SetFormula("pol1");
// Have to copy data into new elements as AssignData wants non-const arguments for some reason
std::vector x1 = x;
std::vector y1 = y;
std::vector yerr1 = yerr;
lf.AssignData(x.size(), 1, &x1[0], &y1[0], &yerr1[0]);
lf.Eval();
TVectorD params;
TVectorD errors;
lf.GetParameters(params);
lf.GetErrors(errors);
line.set_c(params[0]);
line.set_m(params[1]);
line.set_c_err(errors[0]);
line.set_m_err(errors[1]);
line.set_chisq(lf.GetChisquare());
lf.GetCovarianceMatrix(cov_matrix);
return true;
}
bool FitCircleMinuit(const std::vector& x, const std::vector& y,
const std::vector& err,
MAUS::SimpleCircle& circ, TMatrixD& cov_matrix) {
auto Chi2Function = [&x, &y, &err](const double *par) {
// Minimisation function computing the sum of squares of residuals
// looping over the points
double f = 0.0;
for (size_t i = 0; i < x.size(); i++) {
double u = (x[i] - par[0]);
double v = y[i] - par[1];
double dr = par[2] - std::sqrt(u*u+v*v);
f += (dr*dr) / (err[i]*err[i]);
}
return f;
};
// Wrap chi2 function in a function object for the fit
// 3 is the number of fit parameters (size of array par)
ROOT::Math::Functor fcn(Chi2Function, 3);
ROOT::Fit::Fitter fitter;
double pStart[3] = {0, 0, 1};
fitter.SetFCN(fcn, pStart);
fitter.Config().ParSettings(0).SetName("x0");
fitter.Config().ParSettings(1).SetName("y0");
fitter.Config().ParSettings(2).SetName("R");
// do the fit
bool ok = fitter.FitFCN();
if (!ok) {
return ok;
}
const ROOT::Fit::FitResult & result = fitter.Result();
result.Print(std::cout);
// Create a circle object with the results
circ = MAUS::SimpleCircle(result.Parameter(0), result.Parameter(1),
result.Parameter(2));
circ.set_x0_err(result.Error(0));
circ.set_y0_err(result.Error(1));
circ.set_R_err(result.Error(2));
// circ.set_chisq(result.Chi2());
circ.set_chisq(result.MinFcnValue());
circ.set_pvalue(result.Prob());
result.GetCovarianceMatrix(cov_matrix);
return true;
}
bool FitHelixMinuit(const std::vector& x, const std::vector& y,
const std::vector& z, const std::vector& err,
const double* pStart, const MinuitSZParameters& cfg,
MAUS::SimpleHelix& helix) {
// Pull out x and y at the tracker reference station
double x0 = x[0];
double y0 = y[0];
// This is a C++11 lambda function, which defines the quantity to be minimised by MINUIT,
// using a sum over squared residuals looping over the seed spacepoints
std::function //NOLINT(*)
Chi2Function = [&x, &y, &z, &err, &x0, &y0](const double *par) {
double xc = par[0];
double yc = par[1];
double rad = par[2];
// double dsdz = par[3];
double dphidz = par[3];
double chisq = 0.0;
for (size_t i = 0; i < x.size(); i++) {
double theta = (z[i]*dphidz);
double f = xc + (x0 - xc)*cos(theta) - (y0 - yc)*sin(theta); // x model prediction
double g = yc + (y0 - yc)*cos(theta) + (x0 - xc)*sin(theta); // y model prediction
// double theta = z[i]*dphidz + phi0;
// double f = xc + rad*cos(theta); // x model prediction
// double g = yc + rad*sin(theta); // y model prediction
double dx = f - x[i];
double dy = g - y[i];
double dchisq = ((dx*dx) + (dy*dy)) / (err[i]*err[i]);
chisq += dchisq;
}
return chisq;
};
// Set up the minimiser - ROOT::Math::Minimizer interface to MINUIT2, Migrad algorithm
ROOT::Minuit2::Minuit2Minimizer min(ROOT::Minuit2::kMigrad);
min.SetPrintLevel(0); // Keep Quiet!
min.SetMaxFunctionCalls(cfg.maxFunctionCalls);
min.SetMaxIterations(cfg.maxIterations);
min.SetTolerance(cfg.tolerance);
ROOT::Math::Functor fcn(Chi2Function, 4);
min.SetFunction(fcn);
// Determine the approximate dsdz value which corresponds to the global minimum of the chisq func
double dphidz_seed = LocateGlobalChiSqMinimum(pStart, cfg, Chi2Function);
// Set the variables to be minimized
min.SetFixedVariable(0, "xc", pStart[0]);
min.SetFixedVariable(1, "yc", pStart[1]);
min.SetFixedVariable(2, "R", pStart[2]);
double step[4] = {cfg.stepSize, cfg.stepSize, cfg.stepSize, cfg.stepSize};
min.SetLimitedVariable(3, "dphidz", dphidz_seed, 0.00000001, -0.018, 0.018);
// min.SetLimitedVariable(4, "phi0", dsdz_seed, 0.000001, -3.14159265, 3.14159265);
// Do the fit
bool ok = min.Minimize();
if (!ok) {
// std::cerr << "Full helix fit did not converge\n";
return ok;
}
const double *xs = min.X();
const double *errors = min.Errors();
// Calculate s0
double phi = atan2((y0 - xs[1]), (x0 - xs[0])); // Use atan2 not atan, or lose half circle range
if (phi < 0.0) {
phi = phi + 2*PI;
}
double s0 = xs[2] * phi;
// Pull out the covariance matrix
TMatrixD cov(4, 4);
for (size_t i = 0; i < 4; ++i) {
for (size_t j = 0; j < 4; ++j) {
cov(i, j) = min.CovMatrix(i, j);
}
}
// Populate the helix
std::vector params = {xs[0], xs[1], xs[2], xs[2]*xs[3], s0};
std::vector errs = {errors[0], errors[1], xs[2]*errors[2], errors[3], 0.0};
int zsize = static_cast(z.size());
std::vector ndfs = {(2*zsize - 3), (2*zsize - 1), (2*zsize - 5)};
std::vector chisqs = {-1.0, min.MinValue(), -1.0};
helix = MAUS::SimpleHelix(params, errs, ndfs, chisqs, -1.0, cov);
return true;
}
double LocateGlobalChiSqMinimum(const double* pStart, const MinuitSZParameters& cfg,
std::function Chi2Function) { //NOLINT(*)
double nsteps = 50.0;
double lower_limit = -0.01125; // 80MeV/c
double upper_limit = 0.01125;
if (cfg.handedness == 1) {
lower_limit = 0.003; // 300MeV/c
} else if (cfg.handedness == -1) {
upper_limit = -0.003;
}
double threshold = 100.0;
// double step_size = (upper_limit - lower_limit) / nsteps;
double step_size = 2.0e-4;
double min_chisq = 1000.0;
double dphidz_seed = pStart[3];
std::vector candidate_seeds;
double params[4] = {pStart[0], pStart[1], pStart[2], lower_limit};
double x_0 = Chi2Function(params);
params[3] = lower_limit+step_size;
double x_1 = Chi2Function(params);
params[3] = lower_limit+step_size+step_size;
double x_2 = Chi2Function(params);
params[3] = lower_limit+step_size+step_size+step_size;
double x_3 = Chi2Function(params);
std::vector> found_seeds;
TMatrixD X(4, 3);
X(0, 0) = 1.0;
X(1, 0) = 1.0;
X(2, 0) = 1.0;
X(3, 0) = 1.0;
TMatrixD Y(4, 1);
double seed_0 = lower_limit;
double seed_1 = lower_limit + step_size;
double seed_2 = lower_limit + step_size + step_size;
min_chisq = threshold;
for (double seed = lower_limit+step_size*4;
seed < upper_limit + step_size + step_size + step_size; seed += step_size) {
double seed_avg = 0.5*(x_1 + x_2);
X(0, 1) = seed_0;
X(1, 1) = seed_1;
X(2, 1) = seed_2;
X(3, 1) = seed;
X(0, 2) = seed_0*seed_0;
X(1, 2) = seed_1*seed_1;
X(2, 2) = seed_2*seed_2;
X(3, 2) = seed*seed;
TMatrixD XT(TMatrixD::kTransposed, X);
Y(0, 0) = x_0;
Y(1, 0) = x_1;
Y(2, 0) = x_2;
Y(3, 0) = x_3;
TMatrixD temp = (XT * X).Invert();
TMatrixD beta = temp * XT * Y;
double alpha = beta(2, 0);
double minimum = - 0.5 * ( beta(1, 0) / beta(2, 0) );
double value = beta(0, 0) + beta(1, 0)*minimum + beta(2, 0)*minimum*minimum;
if ((value < threshold) && (minimum > seed_1) && (minimum <= seed_2)) {
found_seeds.push_back(std::make_pair(minimum, value));
if (value < min_chisq) {
min_chisq = value;
dphidz_seed = minimum;
}
}
params[3] = seed;
x_0 = x_1;
x_1 = x_2;
x_2 = x_3;
x_3 = Chi2Function(params);
seed_0 = seed_1;
seed_1 = seed_2;
seed_2 = seed;
}
return dphidz_seed;
}
}