#ifndef __JFIT__JLINE1ZESTIMATOR__ #define __JFIT__JLINE1ZESTIMATOR__ #include "JPhysics/JConstants.hh" #include "JMath/JMatrix3S.hh" #include "JMath/JSVD3D.hh" #include "JFit/JMatrixNZ.hh" #include "JFit/JLine1Z.hh" #include "JFit/JEstimator.hh" /** * \file * Linear fit of JFIT::JLine1Z. * \author mdejong */ namespace JFIT {} namespace JPP { using namespace JFIT; } namespace JFIT { /** * Linear fit of straight line parallel to z-axis to set of hits (objects with position and time). * \f{center}\setlength{\unitlength}{0.7cm}\begin{picture}(8,12) \put( 1.0, 0.5){\vector(0,1){9}} \put( 1.0,10.0){\makebox(0,0){$z$}} \put( 1.0, 0.0){\makebox(0,0){muon}} \put( 1.0, 8.0){\line(1,0){6}} \put( 4.0, 8.5){\makebox(0,0)[c]{$R$}} \multiput( 1.0, 2.0)(0.5, 0.5){12}{\qbezier(0.0,0.0)(-0.1,0.35)(0.25,0.25)\qbezier(0.25,0.25)(0.6,0.15)(0.5,0.5)} \put( 4.5, 4.5){\makebox(0,0)[l]{photon}} \put( 1.0, 2.0){\circle*{0.2}} \put( 0.5, 2.0){\makebox(0,0)[r]{$(x_{0},y_{0},z_{0},t_{0})$}} \put( 1.0, 8.0){\circle*{0.2}} \put( 0.5, 8.0){\makebox(0,0)[r]{$(x_{0},y_{0},z_{j})$}} \put( 7.0, 8.0){\circle*{0.2}} \put( 7.0, 9.0){\makebox(0,0)[c]{PMT}} \put( 7.5, 8.0){\makebox(0,0)[l]{$(x_{j},y_{j},z_{j},t_{j})$}} \put( 1.1, 2.1){ \put(0.0,1.00){\vector(-1,0){0.1}} \qbezier(0.0,1.0)(0.25,1.0)(0.5,0.75) \put(0.5,0.75){\vector(1,-1){0.1}} \put(0.4,1.5){\makebox(0,0){$\theta_{c}$}} } \end{picture} \f} \f[ ct_j = ct_0 + (z_j - z_0) + \tan(\theta_{c}) \sqrt((x_j - x_0)^2 + (y_j - y_0)^2) \f] * * where: * \f{eqnarray*}{ x_0 & = & \textrm{x position of track (fit parameter)} \\ y_0 & = & \textrm{y position of track (fit parameter)} \\ z_0 & = & \textrm{z position of track} \\ t_0 & = & \textrm{time of track at } z = z_0 \textrm{ (fit parameter)} \\ \\ c & = & \textrm{speed of light in vacuum} \\ \theta_{c} & = & \textrm{Cherenkov angle} \\ \f} * * Defining: * \f{eqnarray*}{ t_j' & \equiv & ct_j / \tan(\theta_{c}) - (z_j - z_0)/\tan(\theta_{c}) \\ t_0' & \equiv & ct_0 / \tan(\theta_{c}) \\ \f} * \f[ \Rightarrow (t_j' - t_0')^2 = (x_j - x_0)^2 + (y_j - y_0)^2 \f] * * The parameters \f$ \{x_0, y_0, t_0\} \f$ are estimated in the constructor of this class based on * consecutive pairs of equations by which the quadratic terms in \f$ x_0 \f$, \f$ y_0 \f$ and \f$ t_0 \f$ are eliminated. */ template<> class JEstimator : public JLine1Z { public: /** * Default constructor. */ JEstimator() : JLine1Z() {} /** * Constructor. * * \param __begin begin of data * \param __end end of data */ template JEstimator(T __begin, T __end) : JLine1Z() { (*this)(__begin, __end); } /** * Fit. * * The template argument T refers to an iterator of a data structure which should have the following member methods: * - double %getX(); // [m] * - double %getY(); // [m] * - double %getZ(); // [m] * - double %getT(); // [ns] * * \param __begin begin of data * \param __end end of data * \return this fit */ template const JEstimator& operator()(T __begin, T __end) { using namespace std; using namespace JPP; const int N = distance(__begin, __end); if (N >= NUMBER_OF_PARAMETERS) { __x = 0.0; __y = 0.0; __z = 0.0; __t = 0.0; double t0 = 0.0; for (T i = __begin; i != __end; ++i) { __x += i->getX(); __y += i->getY(); __z += i->getZ(); t0 += i->getT(); } const double W = 1.0/N; __x *= W; __y *= W; __z *= W; t0 *= W; V.reset(); t0 *= getSpeedOfLight(); double y0 = 0.0; double y1 = 0.0; double y2 = 0.0; T j = __begin; double xi = j->getX() - getX(); double yi = j->getY() - getY(); double ti = (j->getT() * getSpeedOfLight() - t0 - j->getZ() + getZ()) / getKappaC(); for (bool done = false; !done; ) { if ((done = (++j == __end))) { j = __begin; } double xj = j->getX() - getX(); double yj = j->getY() - getY(); double tj = (j->getT() * getSpeedOfLight() - t0 - j->getZ() + getZ()) / getKappaC(); double dx = xj - xi; double dy = yj - yi; double dt = ti - tj; // opposite sign! const double y = ((xj + xi) * dx + (yj + yi) * dy + (tj + ti) * dt); dx *= 2; dy *= 2; dt *= 2; V.a00 += dx * dx; V.a01 += dx * dy; V.a02 += dx * dt; V.a11 += dy * dy; V.a12 += dy * dt; V.a22 += dt * dt; y0 += dx * y; y1 += dy * y; y2 += dt * y; xi = xj; yi = yj; ti = tj; } t0 *= getInverseSpeedOfLight(); V.a10 = V.a01; V.a20 = V.a02; V.a21 = V.a12; svd.decompose(V); if (fabs(svd.S.a11) < MINIMAL_SVD_WEIGHT * fabs(svd.S.a00)) { THROW(JValueOutOfRange, "JEstimator::JEstimator(): singular value " << svd.S.a11 << ' ' << svd.S.a00); } V = svd.invert(MINIMAL_SVD_WEIGHT); __x += V.a00 * y0 + V.a01 * y1 + V.a02 * y2; __y += V.a10 * y0 + V.a11 * y1 + V.a12 * y2; __t = V.a20 * y0 + V.a21 * y1 + V.a22 * y2; __t *= getKappaC() * getInverseSpeedOfLight(); __t += t0; } else { throw JValueOutOfRange("JEstimator::JEstimator(): Not enough data points."); } return *this; } /** * Update track parameters using updated co-variance matrix (e.g.\ matrix with one hit switched off). * * In this, it is assumed that the changes in x and y positions are small * compared to the actual distances between the track and the hits. * * The template argument T refers to an iterator of a data structure which should have the following member methods: * - double %getX(); // [m] * - double %getY(); // [m] * - double %getZ(); // [m] * - double %getT(); // [ns] * * \param __begin begin of data * \param __end end of data * \param A co-variance matrix of hits */ template void update(T __begin, T __end, const JMatrixNZ& A) { using namespace std; using namespace JPP; const int N = distance(__begin, __end); if (N != (int) A.size()) { THROW(JValueOutOfRange, "JEstimator::update(): Wrong number of points " << N << " != " << A.size()); } if (N >= NUMBER_OF_PARAMETERS) { double x1 = 0.0; double y1 = 0.0; double t1 = 0.0; V.reset(); T i = __begin; for (size_t row = 0; row != A.size(); ++row, ++i) { const double dx = i->getX() - getX(); const double dy = i->getY() - getY(); const double rt = sqrt(dx*dx + dy*dy); double xr = getKappaC() * getInverseSpeedOfLight(); double yr = getKappaC() * getInverseSpeedOfLight(); double tr = 1.0; if (rt != 0.0) { xr *= -dx / rt; yr *= -dy / rt; } T j = __begin; for (size_t col = 0; col != A.size(); ++col, ++j) { const double dx = j->getX() - getX(); const double dy = j->getY() - getY(); const double dz = j->getZ() - getZ(); const double rt = sqrt(dx*dx + dy*dy); double xc = getKappaC() * getInverseSpeedOfLight(); double yc = getKappaC() * getInverseSpeedOfLight(); double tc = 1.0; if (rt != 0.0) { xc *= -dx / rt; yc *= -dy / rt; } const double ts = j->getT() - (dz + rt * getKappaC()) * getInverseSpeedOfLight(); const double vs = A(row,col); x1 += xr * vs * ts; y1 += yr * vs * ts; t1 += tr * vs * ts; V.a00 += xr * vs * xc; V.a01 += xr * vs * yc; V.a02 += xr * vs * tc; V.a11 += yr * vs * yc; V.a12 += yr * vs * tc; V.a22 += tr * vs * tc; } } V.a10 = V.a01; V.a20 = V.a02; V.a21 = V.a12; svd.decompose(V); if (fabs(svd.S.a11) < MINIMAL_SVD_WEIGHT * fabs(svd.S.a00)) { THROW(JValueOutOfRange, "JEstimator::update(): singular value " << svd.S.a11 << ' ' << svd.S.a00); } V = svd.invert(MINIMAL_SVD_WEIGHT); __x += V.a00 * x1 + V.a01 * y1 + V.a02 * t1; __y += V.a10 * x1 + V.a11 * y1 + V.a12 * t1; __t = V.a20 * x1 + V.a21 * y1 + V.a22 * t1; } else { THROW(JValueOutOfRange, "JEstimator::update(): Not enough data points " << N); } } static const int NUMBER_OF_PARAMETERS = 3; //!< number of parameters of fit JMATH::JMatrix3S V; //!< co-variance matrix of fit parameters JMATH::JSVD3D svd; static double MINIMAL_SVD_WEIGHT; //!< minimal SVD weight. }; /** * Set default minimal SVD weight. */ double JEstimator::MINIMAL_SVD_WEIGHT = 1.0e-4; } #endif