/* * GblTrajectory.cpp * * Created on: Aug 18, 2011 * Author: kleinwrt */ /** \mainpage General information * * \section intro_sec Introduction * * For a track with an initial trajectory from a prefit of the * measurements (internal seed) or an external prediction * (external seed) the description of multiple scattering is * added by offsets in a local system. Along the initial * trajectory points are defined with can describe a measurement * or a (thin) scatterer or both. Measurements are arbitrary * functions of the local track parameters at a point (e.g. 2D: * position, 4D: direction+position). The refit provides corrections * to the local track parameters (in the local system) and the * corresponding covariance matrix at any of those points. * Non-diagonal covariance matrices will be diagonalized internally. * Outliers can be down-weighted by use of M-estimators. * * The broken lines trajectory is defined by (2D) offsets at the * first and last point and all points with a scatterer. The * prediction for a measurement is obtained by interpolation of * the enclosing offsets and for triplets of adjacent offsets * kink angles are determined. This requires for all points the * jacobians for propagation to the previous and next offset. * These are calculated from the point-to-point jacobians along * the initial trajectory. The sequence of points has to be * strictly monotonic in arc-length. * * Additional local or global parameters can be added and the * trajectories can be written to special binary files for * calibration and alignment with Millepede-II. * (V. Blobel, NIM A, 566 (2006), pp. 5-13). * * Besides simple trajectories describing the path of a single * particle composed trajectories are supported. These are * constructed from the trajectories of multiple particles and * some external parameters (like those describing a decay) * and transformations at the first points from the external * to the local track parameters. * * The conventions for the coordinate systems follow: * Derivation of Jacobians for the propagation of covariance * matrices of track parameters in homogeneous magnetic fields * A. Strandlie, W. Wittek, NIM A, 566 (2006) 687-698. * * \section call_sec Calling sequence * * -# Create list of points on initial trajectory:\n * std::vector list * -# For all points on initial trajectory: * - Create points and add appropriate attributes:\n * - point = gbl::GblPoint(..) * - point.addMeasurement(..) * - Add additional local or global parameters to measurement:\n * - point.addLocals(..) * - point.addGlobals(..) * - point.addScatterer(..) * - Add point (ordered by arc length) to list:\n * list.push_back(point) * -# Create (simple) trajectory from list of points:\n * traj = gbl::GblTrajectory (list) * -# Optionally with external seed:\n * traj = gbl::GblTrajectory (list,seed) * -# Optionally check validity of trajectory:\n * if (not traj.isValid()) .. //abort * -# Fit trajectory, return error code, * get Chi2, Ndf (and weight lost by M-estimators):\n * ierr = traj.fit(..) * -# For any point on initial trajectory: * - Get corrections and covariance matrix for track parameters:\n * [..] = traj.getResults(label) * -# Optionally write trajectory to MP binary file:\n * traj.milleOut(..) * * \section loc_sec Local system and local parameters * At each point on the trajectory a local coordinate system with local track * parameters has to be defined. The first of the five parameters describes * the bending, the next two the direction and the last two the position (offsets). * The curvilinear system (T,U,V) with parameters (q/p, lambda, phi, x_t, y_t) * is well suited. * * \section impl_sec Implementation * * Matrices are implemented with ROOT (root.cern.ch). User input or output is in the * form of TMatrices. Internally SMatrices are used for fixes sized and simple matrices * based on std::vector<> for variable sized matrices. * * \section ref_sec References * - V. Blobel, C. Kleinwort, F. Meier, * Fast alignment of a complex tracking detector using advanced track models, * Computer Phys. Communications (2011), doi:10.1016/j.cpc.2011.03.017 * - C. Kleinwort, General Broken Lines as advanced track fitting method, * NIM A, 673 (2012), 107-110, doi:10.1016/j.nima.2012.01.024 */ #include "GblTrajectory.h" //! Namespace for the general broken lines package namespace gbl { /// Create new (simple) trajectory from list of points. /** * Curved trajectory in space (default) or without curvature (q/p) or in one * plane (u-direction) only. * \param [in] aPointList List of points * \param [in] flagCurv Use q/p * \param [in] flagU1dir Use in u1 direction * \param [in] flagU2dir Use in u2 direction */ GblTrajectory::GblTrajectory(const std::vector &aPointList, bool flagCurv, bool flagU1dir, bool flagU2dir) : numAllPoints(aPointList.size()), numPoints(), numOffsets(0), numInnerTrans( 0), numCurvature(flagCurv ? 1 : 0), numParameters(0), numLocals( 0), numMeasurements(0), externalPoint(0), theDimension(0), thePoints(), theData(), measDataIndex(), scatDataIndex(), externalIndex(), externalSeed(), innerTransformations(), externalDerivatives(), externalMeasurements(), externalPrecisions() { if (flagU1dir) theDimension.push_back(0); if (flagU2dir) theDimension.push_back(1); // simple (single) trajectory thePoints.push_back(aPointList); numPoints.push_back(numAllPoints); construct(); // construct trajectory } /// Create new (simple) trajectory from list of points with external seed. /** * Curved trajectory in space (default) or without curvature (q/p) or in one * plane (u-direction) only. * \param [in] aPointList List of points * \param [in] aLabel (Signed) label of point for external seed * (<0: in front, >0: after point, slope changes at scatterer!) * \param [in] aSeed Precision matrix of external seed * \param [in] flagCurv Use q/p * \param [in] flagU1dir Use in u1 direction * \param [in] flagU2dir Use in u2 direction */ GblTrajectory::GblTrajectory(const std::vector &aPointList, unsigned int aLabel, const TMatrixDSym &aSeed, bool flagCurv, bool flagU1dir, bool flagU2dir) : numAllPoints(aPointList.size()), numPoints(), numOffsets(0), numInnerTrans( 0), numCurvature(flagCurv ? 1 : 0), numParameters(0), numLocals( 0), numMeasurements(0), externalPoint(aLabel), theDimension(0), thePoints(), theData(), measDataIndex(), scatDataIndex(), externalIndex(), externalSeed( aSeed), innerTransformations(), externalDerivatives(), externalMeasurements(), externalPrecisions() { if (flagU1dir) theDimension.push_back(0); if (flagU2dir) theDimension.push_back(1); // simple (single) trajectory thePoints.push_back(aPointList); numPoints.push_back(numAllPoints); construct(); // construct trajectory } /// Create new composed trajectory from list of points and transformations. /** * Composed of curved trajectory in space. * \param [in] aPointsAndTransList List containing pairs with list of points and transformation (at inner (first) point) */ GblTrajectory::GblTrajectory( const std::vector, TMatrixD> > &aPointsAndTransList) : numAllPoints(), numPoints(), numOffsets(0), numInnerTrans( aPointsAndTransList.size()), numParameters(0), numLocals(0), numMeasurements( 0), externalPoint(0), theDimension(0), thePoints(), theData(), measDataIndex(), scatDataIndex(), externalIndex(), externalSeed(), innerTransformations(), externalDerivatives(), externalMeasurements(), externalPrecisions() { for (unsigned int iTraj = 0; iTraj < aPointsAndTransList.size(); ++iTraj) { thePoints.push_back(aPointsAndTransList[iTraj].first); numPoints.push_back(thePoints.back().size()); numAllPoints += numPoints.back(); innerTransformations.push_back(aPointsAndTransList[iTraj].second); } theDimension.push_back(0); theDimension.push_back(1); numCurvature = innerTransformations[0].GetNcols(); construct(); // construct (composed) trajectory } /// Create new composed trajectory from list of points and transformations with (independent) external measurements. /** * Composed of curved trajectory in space. * \param [in] aPointsAndTransList List containing pairs with list of points and transformation (at inner (first) point) * \param [in] extDerivatives Derivatives of external measurements vs external parameters * \param [in] extMeasurements External measurements (residuals) * \param [in] extPrecisions Precision of external measurements */ GblTrajectory::GblTrajectory( const std::vector, TMatrixD> > &aPointsAndTransList, const TMatrixD &extDerivatives, const TVectorD &extMeasurements, const TVectorD &extPrecisions) : numAllPoints(), numPoints(), numOffsets(0), numInnerTrans( aPointsAndTransList.size()), numParameters(0), numLocals(0), numMeasurements( 0), externalPoint(0), theDimension(0), thePoints(), theData(), measDataIndex(), scatDataIndex(), externalIndex(), externalSeed(), innerTransformations(), externalDerivatives( extDerivatives), externalMeasurements(extMeasurements), externalPrecisions( extPrecisions) { for (unsigned int iTraj = 0; iTraj < aPointsAndTransList.size(); ++iTraj) { thePoints.push_back(aPointsAndTransList[iTraj].first); numPoints.push_back(thePoints.back().size()); numAllPoints += numPoints.back(); innerTransformations.push_back(aPointsAndTransList[iTraj].second); } theDimension.push_back(0); theDimension.push_back(1); numCurvature = innerTransformations[0].GetNcols(); construct(); // construct (composed) trajectory } GblTrajectory::~GblTrajectory() { } /// Retrieve validity of trajectory bool GblTrajectory::isValid() const { return constructOK; } /// Retrieve number of point from trajectory unsigned int GblTrajectory::getNumPoints() const { return numAllPoints; } /// Construct trajectory from list of points. /** * Trajectory is prepared for fit or output to binary file, may consists of sub-trajectories. */ void GblTrajectory::construct() { constructOK = false; fitOK = false; unsigned int aLabel = 0; // loop over trajectories numTrajectories = thePoints.size(); //std::cout << " numTrajectories: " << numTrajectories << ", "<< innerTransformations.size() << std::endl; for (unsigned int iTraj = 0; iTraj < numTrajectories; ++iTraj) { std::vector::iterator itPoint; for (itPoint = thePoints[iTraj].begin(); itPoint < thePoints[iTraj].end(); ++itPoint) { numLocals = std::max(numLocals, itPoint->getNumLocals()); numMeasurements += itPoint->hasMeasurement(); itPoint->setLabel(++aLabel); } } defineOffsets(); calcJacobians(); try { prepare(); } catch (std::overflow_error &e) { std::cout << " GblTrajectory construction failed: " << e.what() << std::endl; return; } constructOK = true; // number of fit parameters numParameters = (numOffsets - 2 * numInnerTrans) * theDimension.size() + numCurvature + numLocals; } /// Define offsets from list of points. /** * Define offsets at points with scatterers and first and last point. * All other points need interpolation from adjacent points with offsets. */ void GblTrajectory::defineOffsets() { // loop over trajectories for (unsigned int iTraj = 0; iTraj < numTrajectories; ++iTraj) { // first point is offset thePoints[iTraj].front().setOffset(numOffsets++); // intermediate scatterers are offsets std::vector::iterator itPoint; for (itPoint = thePoints[iTraj].begin() + 1; itPoint < thePoints[iTraj].end() - 1; ++itPoint) { if (itPoint->hasScatterer()) { itPoint->setOffset(numOffsets++); } else { itPoint->setOffset(-numOffsets); } } // last point is offset thePoints[iTraj].back().setOffset(numOffsets++); } } /// Calculate Jacobians to previous/next scatterer from point to point ones. void GblTrajectory::calcJacobians() { SMatrix55 scatJacobian; // loop over trajectories for (unsigned int iTraj = 0; iTraj < numTrajectories; ++iTraj) { // forward propagation (all) GblPoint* previousPoint = &thePoints[iTraj].front(); unsigned int numStep = 0; std::vector::iterator itPoint; for (itPoint = thePoints[iTraj].begin() + 1; itPoint < thePoints[iTraj].end(); ++itPoint) { if (numStep == 0) { scatJacobian = itPoint->getP2pJacobian(); } else { scatJacobian = itPoint->getP2pJacobian() * scatJacobian; } numStep++; itPoint->addPrevJacobian(scatJacobian); // iPoint -> previous scatterer if (itPoint->getOffset() >= 0) { previousPoint->addNextJacobian(scatJacobian); // lastPoint -> next scatterer numStep = 0; previousPoint = &(*itPoint); } } // backward propagation (without scatterers) for (itPoint = thePoints[iTraj].end() - 1; itPoint > thePoints[iTraj].begin(); --itPoint) { if (itPoint->getOffset() >= 0) { scatJacobian = itPoint->getP2pJacobian(); continue; // skip offsets } itPoint->addNextJacobian(scatJacobian); // iPoint -> next scatterer scatJacobian = scatJacobian * itPoint->getP2pJacobian(); } } } /// Get jacobian for transformation from fit to track parameters at point. /** * Jacobian broken lines (q/p,..,u_i,u_i+1..) to track (q/p,u',u) parameters * including additional local parameters. * \param [in] aSignedLabel (Signed) label of point for external seed * (<0: in front, >0: after point, slope changes at scatterer!) * \return List of fit parameters with non zero derivatives and * corresponding transformation matrix */ std::pair, TMatrixD> GblTrajectory::getJacobian( int aSignedLabel) const { unsigned int nDim = theDimension.size(); unsigned int nCurv = numCurvature; unsigned int nLocals = numLocals; unsigned int nBorder = nCurv + nLocals; unsigned int nParBRL = nBorder + 2 * nDim; unsigned int nParLoc = nLocals + 5; std::vector anIndex; anIndex.reserve(nParBRL); TMatrixD aJacobian(nParLoc, nParBRL); aJacobian.Zero(); unsigned int aLabel = abs(aSignedLabel); unsigned int firstLabel = 1; unsigned int lastLabel = 0; unsigned int aTrajectory = 0; // loop over trajectories for (unsigned int iTraj = 0; iTraj < numTrajectories; ++iTraj) { aTrajectory = iTraj; lastLabel += numPoints[iTraj]; if (aLabel <= lastLabel) break; if (iTraj < numTrajectories - 1) firstLabel += numPoints[iTraj]; } int nJacobian; // 0: prev, 1: next // check consistency of (index, direction) if (aSignedLabel > 0) { nJacobian = 1; if (aLabel >= lastLabel) { aLabel = lastLabel; nJacobian = 0; } } else { nJacobian = 0; if (aLabel <= firstLabel) { aLabel = firstLabel; nJacobian = 1; } } const GblPoint aPoint = thePoints[aTrajectory][aLabel - firstLabel]; std::vector labDer(5); SMatrix55 matDer; getFitToLocalJacobian(labDer, matDer, aPoint, 5, nJacobian); // from local parameters for (unsigned int i = 0; i < nLocals; ++i) { aJacobian(i + 5, i) = 1.0; anIndex.push_back(i + 1); } // from trajectory parameters unsigned int iCol = nLocals; for (unsigned int i = 0; i < 5; ++i) { if (labDer[i] > 0) { anIndex.push_back(labDer[i]); for (unsigned int j = 0; j < 5; ++j) { aJacobian(j, iCol) = matDer(j, i); } ++iCol; } } return std::make_pair(anIndex, aJacobian); } /// Get (part of) jacobian for transformation from (trajectory) fit to track parameters at point. /** * Jacobian broken lines (q/p,..,u_i,u_i+1..) to local (q/p,u',u) parameters. * \param [out] anIndex List of fit parameters with non zero derivatives * \param [out] aJacobian Corresponding transformation matrix * \param [in] aPoint Point to use * \param [in] measDim Dimension of 'measurement' * (<=2: calculate only offset part, >2: complete matrix) * \param [in] nJacobian Direction (0: to previous offset, 1: to next offset) */ void GblTrajectory::getFitToLocalJacobian(std::vector &anIndex, SMatrix55 &aJacobian, const GblPoint &aPoint, unsigned int measDim, unsigned int nJacobian) const { unsigned int nDim = theDimension.size(); unsigned int nCurv = numCurvature; unsigned int nLocals = numLocals; int nOffset = aPoint.getOffset(); if (nOffset < 0) // need interpolation { SMatrix22 prevW, prevWJ, nextW, nextWJ, matN; SVector2 prevWd, nextWd; int ierr; aPoint.getDerivatives(0, prevW, prevWJ, prevWd); // W-, W- * J-, W- * d- aPoint.getDerivatives(1, nextW, nextWJ, nextWd); // W-, W- * J-, W- * d- const SMatrix22 sumWJ(prevWJ + nextWJ); matN = sumWJ.Inverse(ierr); // N = (W- * J- + W+ * J+)^-1 // derivatives for u_int const SMatrix22 prevNW(matN * prevW); // N * W- const SMatrix22 nextNW(matN * nextW); // N * W+ const SVector2 prevNd(matN * prevWd); // N * W- * d- const SVector2 nextNd(matN * nextWd); // N * W+ * d+ unsigned int iOff = nDim * (-nOffset - 1) + nLocals + nCurv + 1; // first offset ('i' in u_i) // local offset if (nCurv > 0) { aJacobian.Place_in_col(-prevNd - nextNd, 3, 0); // from curvature anIndex[0] = nLocals + 1; } aJacobian.Place_at(prevNW, 3, 1); // from 1st Offset aJacobian.Place_at(nextNW, 3, 3); // from 2nd Offset for (unsigned int i = 0; i < nDim; ++i) { anIndex[1 + theDimension[i]] = iOff + i; anIndex[3 + theDimension[i]] = iOff + nDim + i; } // local slope and curvature if (measDim > 2) { // derivatives for u'_int const SMatrix22 prevWPN(nextWJ * prevNW); // W+ * J+ * N * W- const SMatrix22 nextWPN(prevWJ * nextNW); // W- * J- * N * W+ const SVector2 prevWNd(nextWJ * prevNd); // W+ * J+ * N * W- * d- const SVector2 nextWNd(prevWJ * nextNd); // W- * J- * N * W+ * d+ if (nCurv > 0) { aJacobian(0, 0) = 1.0; aJacobian.Place_in_col(prevWNd - nextWNd, 1, 0); // from curvature } aJacobian.Place_at(-prevWPN, 1, 1); // from 1st Offset aJacobian.Place_at(nextWPN, 1, 3); // from 2nd Offset } } else { // at point // anIndex must be sorted // forward : iOff2 = iOff1 + nDim, index1 = 1, index2 = 3 // backward: iOff2 = iOff1 - nDim, index1 = 3, index2 = 1 unsigned int iOff1 = nDim * nOffset + nCurv + nLocals + 1; // first offset ('i' in u_i) unsigned int index1 = 3 - 2 * nJacobian; // index of first offset unsigned int iOff2 = iOff1 + nDim * (nJacobian * 2 - 1); // second offset ('i' in u_i) unsigned int index2 = 1 + 2 * nJacobian; // index of second offset // local offset aJacobian(3, index1) = 1.0; // from 1st Offset aJacobian(4, index1 + 1) = 1.0; for (unsigned int i = 0; i < nDim; ++i) { anIndex[index1 + theDimension[i]] = iOff1 + i; } // local slope and curvature if (measDim > 2) { SMatrix22 matW, matWJ; SVector2 vecWd; aPoint.getDerivatives(nJacobian, matW, matWJ, vecWd); // W, W * J, W * d double sign = (nJacobian > 0) ? 1. : -1.; if (nCurv > 0) { aJacobian(0, 0) = 1.0; aJacobian.Place_in_col(-sign * vecWd, 1, 0); // from curvature anIndex[0] = nLocals + 1; } aJacobian.Place_at(-sign * matWJ, 1, index1); // from 1st Offset aJacobian.Place_at(sign * matW, 1, index2); // from 2nd Offset for (unsigned int i = 0; i < nDim; ++i) { anIndex[index2 + theDimension[i]] = iOff2 + i; } } } } /// Get jacobian for transformation from (trajectory) fit to kink parameters at point. /** * Jacobian broken lines (q/p,..,u_i-1,u_i,u_i+1..) to kink (du') parameters. * \param [out] anIndex List of fit parameters with non zero derivatives * \param [out] aJacobian Corresponding transformation matrix * \param [in] aPoint Point to use */ void GblTrajectory::getFitToKinkJacobian(std::vector &anIndex, SMatrix27 &aJacobian, const GblPoint &aPoint) const { unsigned int nDim = theDimension.size(); unsigned int nCurv = numCurvature; unsigned int nLocals = numLocals; int nOffset = aPoint.getOffset(); SMatrix22 prevW, prevWJ, nextW, nextWJ; SVector2 prevWd, nextWd; aPoint.getDerivatives(0, prevW, prevWJ, prevWd); // W-, W- * J-, W- * d- aPoint.getDerivatives(1, nextW, nextWJ, nextWd); // W-, W- * J-, W- * d- const SMatrix22 sumWJ(prevWJ + nextWJ); // W- * J- + W+ * J+ const SVector2 sumWd(prevWd + nextWd); // W+ * d+ + W- * d- unsigned int iOff = (nOffset - 1) * nDim + nCurv + nLocals + 1; // first offset ('i' in u_i) // local offset if (nCurv > 0) { aJacobian.Place_in_col(-sumWd, 0, 0); // from curvature anIndex[0] = nLocals + 1; } aJacobian.Place_at(prevW, 0, 1); // from 1st Offset aJacobian.Place_at(-sumWJ, 0, 3); // from 2nd Offset aJacobian.Place_at(nextW, 0, 5); // from 1st Offset for (unsigned int i = 0; i < nDim; ++i) { anIndex[1 + theDimension[i]] = iOff + i; anIndex[3 + theDimension[i]] = iOff + nDim + i; anIndex[5 + theDimension[i]] = iOff + nDim * 2 + i; } } /// Get fit results at point. /** * Get corrections and covariance matrix for local track and additional parameters * in forward or backward direction. * \param [in] aSignedLabel (Signed) label of point on trajectory * (<0: in front, >0: after point, slope changes at scatterer!) * \param [out] localPar Corrections for local parameters * \param [out] localCov Covariance for local parameters * \return error code (non-zero if trajectory not fitted successfully) */ unsigned int GblTrajectory::getResults(int aSignedLabel, TVectorD &localPar, TMatrixDSym &localCov) const { if (not fitOK) return 1; std::pair, TMatrixD> indexAndJacobian = getJacobian(aSignedLabel); unsigned int nParBrl = indexAndJacobian.first.size(); TVectorD aVec(nParBrl); // compressed vector for (unsigned int i = 0; i < nParBrl; ++i) { aVec[i] = theVector(indexAndJacobian.first[i] - 1); } TMatrixDSym aMat = theMatrix.getBlockMatrix(indexAndJacobian.first); // compressed matrix localPar = indexAndJacobian.second * aVec; localCov = aMat.Similarity(indexAndJacobian.second); return 0; } /// Get residuals at point from measurement. /** * Get (diagonalized) residual, error of measurement and residual and down-weighting * factor for measurement at point * * \param [in] aLabel Label of point on trajectory * \param [out] numData Number of data blocks from measurement at point * \param [out] aResiduals Measurements-Predictions * \param [out] aMeasErrors Errors of Measurements * \param [out] aResErrors Errors of Residuals (including correlations from track fit) * \param [out] aDownWeights Down-Weighting factors * \return error code (non-zero if trajectory not fitted successfully) */ unsigned int GblTrajectory::getMeasResults(unsigned int aLabel, unsigned int &numData, TVectorD &aResiduals, TVectorD &aMeasErrors, TVectorD &aResErrors, TVectorD &aDownWeights) { numData = 0; if (not fitOK) return 1; unsigned int firstData = measDataIndex[aLabel - 1]; // first data block with measurement numData = measDataIndex[aLabel] - firstData; // number of data blocks for (unsigned int i = 0; i < numData; ++i) { getResAndErr(firstData + i, aResiduals[i], aMeasErrors[i], aResErrors[i], aDownWeights[i]); } return 0; } /// Get (kink) residuals at point from scatterer. /** * Get (diagonalized) residual, error of measurement and residual and down-weighting * factor for scatterering kinks at point * * \param [in] aLabel Label of point on trajectory * \param [out] numData Number of data blocks from scatterer at point * \param [out] aResiduals (kink)Measurements-(kink)Predictions * \param [out] aMeasErrors Errors of (kink)Measurements * \param [out] aResErrors Errors of Residuals (including correlations from track fit) * \param [out] aDownWeights Down-Weighting factors * \return error code (non-zero if trajectory not fitted successfully) */ unsigned int GblTrajectory::getScatResults(unsigned int aLabel, unsigned int &numData, TVectorD &aResiduals, TVectorD &aMeasErrors, TVectorD &aResErrors, TVectorD &aDownWeights) { numData = 0; if (not fitOK) return 1; unsigned int firstData = scatDataIndex[aLabel - 1]; // first data block with scatterer numData = scatDataIndex[aLabel] - firstData; // number of data blocks for (unsigned int i = 0; i < numData; ++i) { getResAndErr(firstData + i, aResiduals[i], aMeasErrors[i], aResErrors[i], aDownWeights[i]); } return 0; } /// Get (list of) labels of points on (simple) trajectory /** * \param [out] aLabelList List of labels (aLabelList[i] = i+1) */ void GblTrajectory::getLabels(std::vector &aLabelList) { unsigned int aLabel = 0; unsigned int nPoint = thePoints[0].size(); aLabelList.resize(nPoint); for (unsigned i = 0; i < nPoint; ++i) { aLabelList[i] = ++aLabel; } } /// Get (list of lists of) labels of points on (composed) trajectory /** * \param [out] aLabelList List of of lists of labels */ void GblTrajectory::getLabels( std::vector > &aLabelList) { unsigned int aLabel = 0; aLabelList.resize(numTrajectories); for (unsigned int iTraj = 0; iTraj < numTrajectories; ++iTraj) { unsigned int nPoint = thePoints[iTraj].size(); aLabelList[iTraj].resize(nPoint); for (unsigned i = 0; i < nPoint; ++i) { aLabelList[iTraj][i] = ++aLabel; } } } /// Get residual and errors from data block. /** * Get residual, error of measurement and residual and down-weighting * factor for (single) data block * \param [in] aData Label of data block * \param [out] aResidual Measurement-Prediction * \param [out] aMeasError Error of Measurement * \param [out] aResError Error of Residual (including correlations from track fit) * \param [out] aDownWeight Down-Weighting factor */ void GblTrajectory::getResAndErr(unsigned int aData, double &aResidual, double &aMeasError, double &aResError, double &aDownWeight) { double aMeasVar; std::vector* indLocal; std::vector* derLocal; theData[aData].getResidual(aResidual, aMeasVar, aDownWeight, indLocal, derLocal); unsigned int nParBrl = (*indLocal).size(); TVectorD aVec(nParBrl); // compressed vector of derivatives for (unsigned int j = 0; j < nParBrl; ++j) { aVec[j] = (*derLocal)[j]; } TMatrixDSym aMat = theMatrix.getBlockMatrix(*indLocal); // compressed (covariance) matrix double aFitVar = aMat.Similarity(aVec); // variance from track fit aMeasError = sqrt(aMeasVar); // error of measurement aResError = (aFitVar < aMeasVar ? sqrt(aMeasVar - aFitVar) : 0.); // error of residual } /// Build linear equation system from data (blocks). void GblTrajectory::buildLinearEquationSystem() { unsigned int nBorder = numCurvature + numLocals; theVector.resize(numParameters); theMatrix.resize(numParameters, nBorder); double aValue, aWeight; std::vector* indLocal; std::vector* derLocal; std::vector::iterator itData; for (itData = theData.begin(); itData < theData.end(); ++itData) { itData->getLocalData(aValue, aWeight, indLocal, derLocal); for (unsigned int j = 0; j < indLocal->size(); ++j) { theVector((*indLocal)[j] - 1) += (*derLocal)[j] * aWeight * aValue; } theMatrix.addBlockMatrix(aWeight, indLocal, derLocal); } } /// Prepare fit for simple or composed trajectory /** * Generate data (blocks) from measurements, kinks, external seed and measurements. */ void GblTrajectory::prepare() { unsigned int nDim = theDimension.size(); // upper limit unsigned int maxData = numMeasurements + nDim * (numOffsets - 2) + externalSeed.GetNrows(); theData.reserve(maxData); measDataIndex.resize(numAllPoints + 3); // include external seed and measurements scatDataIndex.resize(numAllPoints + 1); unsigned int nData = 0; std::vector innerTransDer; std::vector > innerTransLab; // composed trajectory ? if (numInnerTrans > 0) { //std::cout << "composed trajectory" << std::endl; for (unsigned int iTraj = 0; iTraj < numTrajectories; ++iTraj) { // innermost point GblPoint* innerPoint = &thePoints[iTraj].front(); // transformation fit to local track parameters std::vector firstLabels(5); SMatrix55 matFitToLocal, matLocalToFit; getFitToLocalJacobian(firstLabels, matFitToLocal, *innerPoint, 5); // transformation local track to fit parameters int ierr; matLocalToFit = matFitToLocal.Inverse(ierr); TMatrixD localToFit(5, 5); for (unsigned int i = 0; i < 5; ++i) { for (unsigned int j = 0; j < 5; ++j) { localToFit(i, j) = matLocalToFit(i, j); } } // transformation external to fit parameters at inner (first) point innerTransDer.push_back(localToFit * innerTransformations[iTraj]); innerTransLab.push_back(firstLabels); } } // measurements SMatrix55 matP; // loop over trajectories std::vector::iterator itPoint; for (unsigned int iTraj = 0; iTraj < numTrajectories; ++iTraj) { for (itPoint = thePoints[iTraj].begin(); itPoint < thePoints[iTraj].end(); ++itPoint) { SVector5 aMeas, aPrec; unsigned int nLabel = itPoint->getLabel(); unsigned int measDim = itPoint->hasMeasurement(); if (measDim) { const TMatrixD localDer = itPoint->getLocalDerivatives(); const std::vector globalLab = itPoint->getGlobalLabels(); const TMatrixD globalDer = itPoint->getGlobalDerivatives(); TMatrixD transDer; itPoint->getMeasurement(matP, aMeas, aPrec); unsigned int iOff = 5 - measDim; // first active component std::vector labDer(5); SMatrix55 matDer, matPDer; unsigned int nJacobian = (itPoint < thePoints[iTraj].end() - 1) ? 1 : 0; // last point needs backward propagation getFitToLocalJacobian(labDer, matDer, *itPoint, measDim, nJacobian); if (measDim > 2) { matPDer = matP * matDer; } else { // 'shortcut' for position measurements matPDer.Place_at( matP.Sub(3, 3) * matDer.Sub(3, 0), 3, 0); } if (numInnerTrans > 0) { // transform for external parameters TMatrixD proDer(measDim, 5); // match parameters unsigned int ifirst = 0; unsigned int ilabel = 0; while (ilabel < 5) { if (labDer[ilabel] > 0) { while (innerTransLab[iTraj][ifirst] != labDer[ilabel] and ifirst < 5) { ++ifirst; } if (ifirst >= 5) { labDer[ilabel] -= 2 * nDim * (iTraj + 1); // adjust label } else { // match labDer[ilabel] = 0; // mark as related to external parameters for (unsigned int k = iOff; k < 5; ++k) { proDer(k - iOff, ifirst) = matPDer(k, ilabel); } } } ++ilabel; } transDer.ResizeTo(measDim, numCurvature); transDer = proDer * innerTransDer[iTraj]; } for (unsigned int i = iOff; i < 5; ++i) { if (aPrec(i) > 0.) { GblData aData(nLabel, aMeas(i), aPrec(i)); aData.addDerivatives(i, labDer, matPDer, iOff, localDer, globalLab, globalDer, numLocals, transDer); theData.push_back(aData); nData++; } } } measDataIndex[nLabel] = nData; } } // pseudo measurements from kinks SMatrix22 matT; scatDataIndex[0] = nData; scatDataIndex[1] = nData; // loop over trajectories for (unsigned int iTraj = 0; iTraj < numTrajectories; ++iTraj) { for (itPoint = thePoints[iTraj].begin() + 1; itPoint < thePoints[iTraj].end() - 1; ++itPoint) { SVector2 aMeas, aPrec; unsigned int nLabel = itPoint->getLabel(); if (itPoint->hasScatterer()) { itPoint->getScatterer(matT, aMeas, aPrec); TMatrixD transDer; std::vector labDer(7); SMatrix27 matDer, matTDer; getFitToKinkJacobian(labDer, matDer, *itPoint); matTDer = matT * matDer; if (numInnerTrans > 0) { // transform for external parameters TMatrixD proDer(nDim, 5); // match parameters unsigned int ifirst = 0; unsigned int ilabel = 0; while (ilabel < 7) { if (labDer[ilabel] > 0) { while (innerTransLab[iTraj][ifirst] != labDer[ilabel] and ifirst < 5) { ++ifirst; } if (ifirst >= 5) { labDer[ilabel] -= 2 * nDim * (iTraj + 1); // adjust label } else { // match labDer[ilabel] = 0; // mark as related to external parameters for (unsigned int k = 0; k < nDim; ++k) { proDer(k, ifirst) = matTDer(k, ilabel); } } } ++ilabel; } transDer.ResizeTo(nDim, numCurvature); transDer = proDer * innerTransDer[iTraj]; } for (unsigned int i = 0; i < nDim; ++i) { unsigned int iDim = theDimension[i]; if (aPrec(iDim) > 0.) { GblData aData(nLabel, aMeas(iDim), aPrec(iDim)); aData.addDerivatives(iDim, labDer, matTDer, numLocals, transDer); theData.push_back(aData); nData++; } } } scatDataIndex[nLabel] = nData; } scatDataIndex[thePoints[iTraj].back().getLabel()] = nData; } // external seed if (externalPoint > 0) { std::pair, TMatrixD> indexAndJacobian = getJacobian(externalPoint); externalIndex = indexAndJacobian.first; std::vector vExternalDerivatives(externalIndex.size()); const TMatrixDSymEigen externalEigen(externalSeed); const TVectorD valEigen = externalEigen.GetEigenValues(); TMatrixD vecEigen = externalEigen.GetEigenVectors(); vecEigen = vecEigen.T() * indexAndJacobian.second; for (int i = 0; i < externalSeed.GetNrows(); ++i) { if (valEigen(i) > 0.) { for (int j = 0; j < externalSeed.GetNcols(); ++j) { vExternalDerivatives[j] = vecEigen(i, j); } GblData aData(externalPoint, 0., valEigen(i)); aData.addDerivatives(externalIndex, vExternalDerivatives); theData.push_back(aData); nData++; } } } measDataIndex[numAllPoints + 1] = nData; // external measurements unsigned int nExt = externalMeasurements.GetNrows(); if (nExt > 0) { std::vector index(numCurvature); std::vector derivatives(numCurvature); for (unsigned int iExt = 0; iExt < nExt; ++iExt) { for (unsigned int iCol = 0; iCol < numCurvature; ++iCol) { index[iCol] = iCol + 1; derivatives[iCol] = externalDerivatives(iExt, iCol); } GblData aData(1U, externalMeasurements(iExt), externalPrecisions(iExt)); aData.addDerivatives(index, derivatives); theData.push_back(aData); nData++; } } measDataIndex[numAllPoints + 2] = nData; } /// Calculate predictions for all points. void GblTrajectory::predict() { std::vector::iterator itData; for (itData = theData.begin(); itData < theData.end(); ++itData) { itData->setPrediction(theVector); } } /// Down-weight all points. /** * \param [in] aMethod M-estimator (1: Tukey, 2:Huber, 3:Cauchy) */ double GblTrajectory::downWeight(unsigned int aMethod) { double aLoss = 0.; std::vector::iterator itData; for (itData = theData.begin(); itData < theData.end(); ++itData) { aLoss += (1. - itData->setDownWeighting(aMethod)); } return aLoss; } /// Perform fit of trajectory. /** * Optionally iterate for outlier down-weighting. * \param [out] Chi2 Chi2 sum (corrected for down-weighting) * \param [out] Ndf Number of degrees of freedom * \param [out] lostWeight Sum of weights lost due to down-weighting * \param [in] optionList Iterations for down-weighting * (One character per iteration: t,h,c (or T,H,C) for Tukey, Huber or Cauchy function) * \return Error code (non zero value indicates failure of fit) */ unsigned int GblTrajectory::fit(double &Chi2, int &Ndf, double &lostWeight, std::string optionList) { const double normChi2[4] = { 1.0, 0.8737, 0.9326, 0.8228 }; const std::string methodList = "TtHhCc"; Chi2 = 0.; Ndf = -1; lostWeight = 0.; if (not constructOK) return 10; unsigned int aMethod = 0; buildLinearEquationSystem(); lostWeight = 0.; unsigned int ierr = 0; try { theMatrix.solveAndInvertBorderedBand(theVector, theVector); predict(); for (unsigned int i = 0; i < optionList.size(); ++i) // down weighting iterations { size_t aPosition = methodList.find(optionList[i]); if (aPosition != std::string::npos) { aMethod = aPosition / 2 + 1; lostWeight = downWeight(aMethod); buildLinearEquationSystem(); theMatrix.solveAndInvertBorderedBand(theVector, theVector); predict(); } } Ndf = theData.size() - numParameters; Chi2 = 0.; for (unsigned int i = 0; i < theData.size(); ++i) { Chi2 += theData[i].getChi2(); } Chi2 /= normChi2[aMethod]; fitOK = true; } catch (int e) { std::cout << " GblTrajectory::fit exception " << e << std::endl; ierr = e; } return ierr; } /// Write trajectory to Millepede-II binary file. void GblTrajectory::milleOut(MilleBinary &aMille) { float fValue; float fErr; std::vector* indLocal; std::vector* derLocal; std::vector* labGlobal; std::vector* derGlobal; if (not constructOK) return; // data: measurements, kinks and external seed std::vector::iterator itData; for (itData = theData.begin(); itData != theData.end(); ++itData) { itData->getAllData(fValue, fErr, indLocal, derLocal, labGlobal, derGlobal); aMille.addData(fValue, fErr, *indLocal, *derLocal, *labGlobal, *derGlobal); } aMille.writeRecord(); } /// Print GblTrajectory /** * \param [in] level print level (0: minimum, >0: more) */ void GblTrajectory::printTrajectory(unsigned int level) { if (numInnerTrans) { std::cout << "Composed GblTrajectory, " << numInnerTrans << " subtrajectories" << std::endl; } else { std::cout << "Simple GblTrajectory" << std::endl; } if (theDimension.size() < 2) { std::cout << " 2D-trajectory" << std::endl; } std::cout << " Number of GblPoints : " << numAllPoints << std::endl; std::cout << " Number of points with offsets: " << numOffsets << std::endl; std::cout << " Number of fit parameters : " << numParameters << std::endl; std::cout << " Number of measurements : " << numMeasurements << std::endl; if (externalMeasurements.GetNrows()) { std::cout << " Number of ext. measurements : " << externalMeasurements.GetNrows() << std::endl; } if (externalPoint) { std::cout << " Label of point with ext. seed: " << externalPoint << std::endl; } if (constructOK) { std::cout << " Constructed OK " << std::endl; } if (fitOK) { std::cout << " Fitted OK " << std::endl; } if (level > 0) { if (numInnerTrans) { std::cout << " Inner transformations" << std::endl; for (unsigned int i = 0; i < numInnerTrans; ++i) { innerTransformations[i].Print(); } } if (externalMeasurements.GetNrows()) { std::cout << " External measurements" << std::endl; std::cout << " Measurements:" << std::endl; externalMeasurements.Print(); std::cout << " Precisions:" << std::endl; externalPrecisions.Print(); std::cout << " Derivatives:" << std::endl; externalDerivatives.Print(); } if (externalPoint) { std::cout << " External seed:" << std::endl; externalSeed.Print(); } if (fitOK) { std::cout << " Fit results" << std::endl; std::cout << " Parameters:" << std::endl; theVector.print(); std::cout << " Covariance matrix (bordered band part):" << std::endl; theMatrix.printMatrix(); } } } /// Print \link GblPoint GblPoints \endlink on trajectory /** * \param [in] level print level (0: minimum, >0: more) */ void GblTrajectory::printPoints(unsigned int level) { std::cout << "GblPoints " << std::endl; for (unsigned int iTraj = 0; iTraj < numTrajectories; ++iTraj) { std::vector::iterator itPoint; for (itPoint = thePoints[iTraj].begin(); itPoint < thePoints[iTraj].end(); ++itPoint) { itPoint->printPoint(level); } } } /// Print GblData blocks for trajectory void GblTrajectory::printData() { std::cout << "GblData blocks " << std::endl; std::vector::iterator itData; for (itData = theData.begin(); itData < theData.end(); ++itData) { itData->printData(); } } }