//-*-mode: C++; c-basic-offset: 2; -*- /* Copyright 2013 Authors: Sergey Yashchenko and Tadeas Bilka This file is part of GENFIT. GENFIT is free software: you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version. GENFIT 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with GENFIT. If not, see . */ #include "GFGbl.h" #include "GblTrajectory.h" #include "GblPoint.h" #include "AbsMeasurement.h" #include "KalmanFitterInfo.h" #include "KalmanFitStatus.h" #include "KalmanFittedStateOnPlane.h" #include "MyDebugTools.h" #include "Track.h" #include #include #include #include #define DEBUG #define OUTPUT #ifdef DEBUG ofstream ofs_debug("gbl.debug"); #endif #ifdef OUTPUT ofstream ofs_output("gbl.output"); #endif // Millepede Binary File for output of GBL trajectories for alignment gbl::MilleBinary *milleFile; using namespace gbl; using namespace std; using namespace genfit; GFGbl::GFGbl() : AbsFitter() { milleFile = new MilleBinary("vxd.dat"); } /** * @brief Simple Jacobian in the limit of q/p -> 0 for magnetic field B = (0, 0, Bz) * * @param ds Length of path of the particle passed during extrapolation * @param Bz Magnetic field component in z-coord * @param cosLambda Cosinus of "lambda" angle ... got from getCosLambda(particle momentum at plane where extrap. starts) * @return TMatrixD */ TMatrixD getSimpleJac(double ds, double Bz, double cosLambda) { TMatrixD j(5, 5); j.UnitMatrix(); j(1, 0) = - Bz * ds * cosLambda; j(3, 0) = - Bz * 0.5 * ds * ds; j(3, 1) = ds; j(4, 2) = ds; return j; } /** * @brief Extracts basf2's VXD GeoCache sensorId from geometry node name * * @param path Path of the node of active volume of the sensor * @return int */ int getSensorIdFromPath(const char *path) { // Inspired by http://stackoverflow.com/questions/236129/splitting-a-string-in-c // Answer #562 cout << path << endl; std::string l(path); std::replace(l.begin(), l.end(), '_', ' '); std::istringstream stm(l); std::vector tokens; for (;;) { std::string word; if (!(stm >> word)) break; tokens.push_back(word); } int res = atoi(tokens[tokens.size()-1].c_str()); if (res < 1000 && tokens.size()>=2) res = atoi(tokens[tokens.size()-2].c_str()); return res; } /** * @brief Returns squared variance of the scattering angle * * @param momMag Particle's momentum magnitude [GeV] * @param XX0 Fraction of radiation length (x/X0) passed in sensor material * @return double */ double getScattSigma2(double momMag, double XX0) { double momFactor = (0.0136 / momMag); double x0Factor = sqrt(XX0) * (1. + 0.038 * log(XX0)); double scatSigma = momFactor * x0Factor; return scatSigma * scatSigma; } /** * @brief Returns sinus of angle "phi" of track projection into x-y (R-Phi) plane * * @param trackMom ... * @return double */ double getSinPhi(TVector3 trackMom) { return sin(atan(trackMom[1]/trackMom[0])); } /** * @brief Returns cosinus of the angle "lambda" of track projection into z-plane * * @param trackMom Vector of track momentum * @return double */ double getCosLambda(TVector3 trackMom) { double cosLambda = 1.; double pt = sqrt(trackMom[0] * trackMom[0] + trackMom[1] * trackMom[1]); double dzds = trackMom[2] / pt; cosLambda = 1. / sqrt(1. + dzds * dzds); return cosLambda; } /** * @brief Returns squared cosinus of the angle "lambda" of track projection into z-plane * * @param trackMom Vector of track momentum * @return double */ double getCosLambda2(TVector3 trackMom) { double cosLambda = getCosLambda(trackMom); return cosLambda*cosLambda; } /** * @brief Jacobian for transformation from local to curvilinear (co-moving) frame. B field is retrived at sensor plane * See paper from Strandlie & Wittek * Nuclear Instruments and Methods in Physics Research * Derivation of Jacobians for the propagation of covariance matrices of track parameters in homogeneous magnetic fields * A 566 (2006) 687-698 * page 697, formula A.24 * * @param plane SharedPlanePtr for extraction of normal & measurement directions * @param mom TVector3 vector of particle momentum at sensor plane * @param charge +1 or -1 ... charge of particle * @return TMatrixD */ TMatrixD jacLocal2Curvilinear(SharedPlanePtr plane, TVector3 mom, int charge, TVector3 pos) { TVector3 B = 0.3 / 100. * genfit::FieldManager::getInstance()->getFieldVal(pos); TMatrixD j(5, 5); // normal to plane TVector3 I = plane->getNormal(); // U direction TVector3 J = plane->getU(); // V direction TVector3 K = plane->getV(); TVector3 H = B * (1. / B.Mag()); TVector3 T = mom * (1. / mom.Mag()); double alpha = (H.Cross(T)).Mag(); TVector3 Z(0., 0., 1.); TVector3 U = Z.Cross(T) * (1. / (Z.Cross(T)).Mag()); TVector3 V = T.Cross(U); TVector3 N = H.Cross(T) * (1. / alpha); double cosLambda = getCosLambda(mom); double Q = - B.Mag() * charge / mom.Mag(); j(0, 0) = 1.; j(0, 1) = 0.; j(0, 2) = 0.; j(0, 3) = 0.; j(0, 4) = 0.; j(1, 0) = 0.; j(1, 1) = (T.Dot(I)) * (V.Dot(J)); j(1, 2) = (T.Dot(I)) * (V.Dot(K)); j(1, 3) = - alpha * Q * (T.Dot(J)) * (V.Dot(N)); j(1, 4) = - alpha * Q * (T.Dot(K)) * (V.Dot(N)); j(2, 0) = 0.; j(2, 1) = (T.Dot(I)) * (U.Dot(J)) / cosLambda; j(2, 2) = (T.Dot(I)) * (U.Dot(K)) / cosLambda; j(2, 3) = - alpha * Q * (T.Dot(J)) * (U.Dot(N)) / cosLambda; j(2, 4) = - alpha * Q * (T.Dot(K)) * (U.Dot(N)) / cosLambda; j(3, 0) = 0.; j(3, 1) = 0.; j(3, 2) = 0.; j(3, 3) = U.Dot(J); j(3, 4) = U.Dot(K); j(4, 0) = 0.; j(4, 1) = 0.; j(4, 2) = 0.; j(4, 3) = V.Dot(J); j(4, 4) = V.Dot(K); return j; } /** * @brief Projection matrix from Curvilinear to Local(measurement) coordinates * See paper from Strandlie & Wittek * Nuclear Instruments and Methods in Physics Research * Derivation of Jacobians for the propagation of covariance matrices of track parameters in homogeneous magnetic fields * A 566 (2006) 687-698 * page 697, formula A.25 ... bottom right corner of the matrix * @param plane ... * @param mom ... * @return TMatrixD */ TMatrixD projCurv2Meas(SharedPlanePtr plane, TVector3 mom) { TMatrixD p(2, 2); //NOTE Following lines are version from Claus' python toy simulation /*TVector3 normal = plane->getNormal(); double ey = normal[0]; double ex = - normal[1]; double ez = 1.; //proL2m[0][0] = HitHMatrix[0][3]; //proL2m[1][1] = HitHMatrix[1][4]; double cosLambda = getCosLambda(mom); double sinPhi = getSinPhi(mom); double sinLambda = sqrt(1. - cosLambda * cosLambda); double cosPhi = sqrt(1. - sinPhi * sinPhi); TMatrixD uvDir(2, 3); uvDir(0, 0) = - sinPhi; uvDir(0, 1) = cosPhi; uvDir(0, 2) = 0.; uvDir(1, 0) = -sinLambda * cosPhi; uvDir(1, 1) = -sinLambda * sinPhi; uvDir(1, 2) = cosLambda; TMatrixD mDir(2, 3); mDir(0, 0) = ex; mDir(0, 1) = ey; mDir(0, 2) = 0.; mDir(1, 0) = 0.; mDir(1, 1) = 0.; mDir(1, 2) = ez; p = uvDir * (mDir.T()); return p.Invert(); */ // normal to plane TVector3 I = plane->getNormal(); // U direction TVector3 J = plane->getU(); // V direction TVector3 K = plane->getV(); TVector3 T = mom * (1. / mom.Mag()); TVector3 Z(0., 0., 1.); TVector3 U = Z.Cross(T) * (1. / (Z.Cross(T)).Mag());// TVector3 V = T.Cross(U);// double invTI = 1. / T.Dot(I); // p(0, 0) = V.Dot(K) * invTI; p(0, 1) = - U.Dot(K) * invTI; p(1, 0) = - V.Dot(J) * invTI; p(1, 1) = U.Dot(J) * invTI; return p; } void GFGbl::processTrackWithRep(Track* trk, const AbsTrackRep* rep, bool resortHits) { //TGeoManager *gGeoManager; // flag for checking if desired sensor for alignment has been hitted bool flag = false; bool simple = false; TrackPoint* point_meas; AbsMeasurement* raw_meas; TVectorD measPrec(2); int npoints_meas = trk->getNumPointsWithMeasurement(); std::vector listOfPoints; unsigned int seedLabel = 0; // Add internal/external seed option in the future TMatrixDSym clCov(5), clSeed(5); TMatrixD jacPointToPoint(5, 5); jacPointToPoint.UnitMatrix(); int n_gbl_points = 0; double Chi2 = 0.; int Ndf = 0; double lostWeight = 0.; genfit::FitStatus* fs = trk->getFitStatus(rep); genfit::KalmanFitStatus* kfs = dynamic_cast(fs); for (int ipoint_meas = 0; ipoint_meas < npoints_meas; ipoint_meas++) { point_meas = trk->getPointWithMeasurement(ipoint_meas); KalmanFitterInfo* fi = static_cast(point_meas->getFitterInfo(rep)); // Current detector plane SharedPlanePtr plane = fi->getPlane(); // StateOnPlane for Jacobian calculation and determination of length passed in sensor ReferenceStateOnPlane* reference = new ReferenceStateOnPlane(*fi->getReferenceState()); //StateOnPlane* reference = new StateOnPlane(*fi->getFittedState()); TVectorD state = reference->getState(); // Get sensorId and material radiation length from geometry TVector3 o = plane->getO(); gGeoManager->FindNode(o.x(), o.y(), o.z()); int sensorId = getSensorIdFromPath(gGeoManager->GetPath()); double radLength = gGeoManager->GetCurrentNode()->GetMedium()->GetMaterial()->GetRadLen(); cout << "sensorId: " << sensorId << endl; raw_meas = point_meas->getRawMeasurement(); TVectorD raw_coor = raw_meas->getRawHitCoords(); // Here misalignment can be introduced to specific sensor if (sensorId == 8512) { raw_coor[0] += 0.100; raw_coor[1] -= 0.100; } // Covariance matrix of measurements const TMatrixDSym& raw_cov = raw_meas->getRawHitCov(); // Projection matrix from repository state to measurement coords boost::scoped_ptr HitHMatrix(raw_meas->constructHMatrix(rep)); // Residual between measured position and reference track position TVectorD residual = raw_coor - HitHMatrix->Hv(state); // Extrapolate to middle of the sensor rep->extrapolateToPlane(*reference, plane, false, false); // I only get one half of the path inside sensor // ... should be good approximation double sensorHalfLenPassed = 0.; if (ipoint_meas < npoints_meas - 1) { // if not last point, extrapolate in track direction and stop at boundary sensorHalfLenPassed = rep->extrapolateToPlane(*reference, trk->getPoint(ipoint_meas+1)->getFitterInfo(rep)->getPlane(), true, false); } else { // if last sensor, extrapolate to the previous // (and stop at boundary - of THIS sensor, not the one we extrapolate to!) sensorHalfLenPassed = rep->extrapolateToPlane(*reference, trk->getPoint(ipoint_meas-1)->getFitterInfo(rep)->getPlane(), true, false); } // ensure positive length and double to get total path length inside sensor double sensorLenPassed = abs(sensorHalfLenPassed*2.); // For safety, extrapolate again to first sensor centre (no need to calculate Jacbian/Noise) // ... after this representation state is at this sensor plane rep->extrapolateToPlane(*reference, plane, false, false); // track direction at plane (in global coords) TVector3 trackDir = rep->getDir(*reference); // track momentum vector at plane (in global coords) TVector3 trackMom = rep->getMom(*reference); // track position vector at plane (in global coords) TVector3 trackPos = rep->getPos(*reference); // track momentum magnitude double trackMomMag = trackMom.Mag(); // variable to store extrapolation length between this and next sensor double extLen = 1.; if (ipoint_meas < npoints_meas - 1) { TMatrixDSym noise; TVectorD deltaState; // Extrapolation to next plane with Jacobian calculation extLen = rep->extrapolateToPlane(*reference, trk->getPoint(ipoint_meas+1)->getFitterInfo(rep)->getPlane(), false, true); // Get the Jacobian from this plane to the following from the extrapolation rep->getForwardJacobianAndNoise(jacPointToPoint, noise, deltaState); } // Following code is to get jacobians for coordinate transformation /* // Jacobian local->curvilinear TMatrixD jacLoc2Cur(5, 5); // Jacobian curvilinear->local TMatrixD jacCur2Loc(5, 5); jacCur2Loc = jacLocal2Curvilinear(plane, trackMom, rep->getCharge(reference) / abs(rep->getCharge(reference)), trackPos); jacCur2Loc.Invert(); if (ipoint_meas < npoints_meas -1){ // Now we are extrapolated at next plane, so get field and mom at that plane // First get non-inverted jacobian of type local->curvilinear jacLoc2Cur = jacLocal2Curvilinear(trk->getPoint(ipoint_meas+1)->getFitterInfo(rep)->getPlane(), rep->getMom(reference), rep->getCharge(reference) / abs(rep->getCharge(reference)), rep->getPos(reference) ); } else { jacCur2Loc.UnitMatrix(); jacLoc2Cur.UnitMatrix(); } jacPointToPoint = jacLoc2Cur * (jacPointToPoint * jacCur2Loc); */ // I now use only the simple jacobian if (simple) jacPointToPoint = getSimpleJac(extLen, 0.3*1.5/100, getCosLambda(trackMom)); GblPoint point(jacPointToPoint); // Measurement precision = inverse covariance matrix (of sensor measurements) measPrec[0] = 1.0 / raw_cov(0,0); measPrec[1] = 1.0 / raw_cov(1,1); // Projection from local track coordinates (co-moving frame) to measurement coordinates TMatrixD proL2m(2, 2); // Projection from measurement to curvilinear track coordinates (co-moving frame) // ... old stuff if (simple) { // Get the projection from curvilinear sys. to meas. sys. proL2m = projCurv2Meas(plane, trackMom); } else { TMatrixD proM2L(2,2); proM2L.Zero(); proM2L.Zero(); proM2L(0,0) = HitHMatrix->getMatrix()(0,3); proM2L(1,1) = HitHMatrix->getMatrix()(1,4); proL2m = proM2L.Invert(); } // Initial kinks = [0, 0] TVectorD scatResidual = TVectorD(2); scatResidual(0) = 0.; scatResidual(1) = 0.; // Passed fraction of radiation length in sensor double sensorXX0 = sensorLenPassed/radLength; //TVectorD scatPrecision = TVectorD(2); //scatPrecision(0) = 1./ getScattSigma2(trackMomMag, sensorXX0); //scatPrecision(1) = 1./ getScattSigma2(trackMomMag, sensorXX0) * getCosLambda2(trackMom); TMatrixDSym scatCov(2); if (simple) { scatCov(0, 0) = getScattSigma2(trackMomMag, sensorXX0); scatCov(1, 1) = getScattSigma2(trackMomMag, sensorXX0); scatCov(0, 1) = 0.; scatCov(1, 0) = 0.; } else { double c1 = trackDir.Dot(plane->getU()); double c2 = trackDir.Dot(plane->getV()); scatCov(0, 0) = 1. - c2*c2; scatCov(1, 1) = 1. - c1*c1; scatCov(0, 1) = c1*c2; scatCov(1, 0) = c1*c2; scatCov *= getScattSigma2(trackMomMag, sensorXX0) / (1. - c1 * c1 - c2 * c2) / (1. - c1 * c1 - c2 * c2) ; } point.addMeasurement(proL2m, residual, measPrec); point.addScatterer(scatResidual, scatCov.Invert()); //point.addScatterer(scatResidual, scatPrecision); //Add global derivatives to the point // sensor label = sensorID * 10, then last digit is label for global derivative for the sensor int label = sensorId * 10; cout << label << endl; // values for global derivatives TMatrixD derGlobal(2, 6); // labels for global derivatives std::vector labGlobal; // track direction in global coords TVector3 tDir = trackDir; // sensor u direction in global coords TVector3 uDir = plane->getU(); // sensor v direction in global coords TVector3 vDir = plane->getV(); // sensor normal direction in global coords TVector3 nDir = plane->getNormal(); // track direction in local sensor system TVector3 tLoc = TVector3(uDir.Dot(tDir), vDir.Dot(tDir), nDir.Dot(tDir)); // track u-slope in local sensor system double uSlope = tLoc[0] / tLoc[2]; // track v-slope in local sensor system double vSlope = tLoc[1] / tLoc[2]; // Measured track u-position in local sensor system double uPos = raw_coor[0]; // Measured track v-position in local sensor system double vPos = raw_coor[1]; //WARNING This now aligns olny one specified sensor if (sensorId == 8512 && kfs->getBackwardNdf() == 7) { flag = true; //Global derivatives for alignment in sensor local coordinates derGlobal(0, 0) = 1.0; derGlobal(0, 1) = 0.0; derGlobal(0, 2) = - uSlope; derGlobal(0, 3) = vPos * uSlope; derGlobal(0, 4) = -uPos * uSlope; derGlobal(0, 5) = vPos; derGlobal(1, 0) = 0.0; derGlobal(1, 1) = 1.0; derGlobal(1, 2) = - vSlope; derGlobal(1, 3) = vPos * vSlope; derGlobal(1, 4) = -uPos * vSlope; derGlobal(1, 5) = -uPos; labGlobal.push_back(label + 1); // u labGlobal.push_back(label + 2); // v labGlobal.push_back(label + 3); // w labGlobal.push_back(label + 4); // alpha labGlobal.push_back(label + 5); // beta labGlobal.push_back(label + 6); // gamma //TODO Global derivatives continuation // ... needs changes in labels and positions of derivatives values in matrix /* // Global derivatives for movement of whole detector system in global coordinates //TODO: Usage of this would require Hierarchy Constraints to be provided to MP2 // sensor centre position in global system TVector3 detPos = plane->getO(); cout << "detPos" << endl; detPos.Print(); // global prediction from raw measurement TVector3 pred = detPos + uPos * uDir + vPos * vDir; cout << "pred" << endl; pred.Print(); double xPred = pred[0]; double yPred = pred[1]; double zPred = pred[2]; // scalar product of sensor normal and track direction double tn = tDir.Dot(nDir); cout << "tn" << endl; cout << tn << endl; // derivatives of local residuals versus measurements TMatrixD drdm(3, 3); drdm.UnitMatrix(); for(int row = 0; row < 3; row++) for(int col = 0; col < 3; col++) drdm(row, col) -= tDir[row] * nDir[col] / tn; cout << "drdm" << endl; drdm.Print(); // derivatives of measurements versus global alignment parameters TMatrixD dmdg(3, 6); dmdg.Zero(); dmdg(0, 0) = 1.; dmdg(0, 4) = -zPred; dmdg(0, 5) = yPred; dmdg(1, 1) = 1.; dmdg(1, 3) = zPred; dmdg(1, 5) = -xPred; dmdg(2, 2) = 1.; dmdg(2, 3) = -yPred; dmdg(2, 4) = xPred; cout << "dmdg" << endl; dmdg.Print(); // derivatives of local residuals versus global alignment parameters TMatrixD drldrg(3, 3); drldrg.Zero(); drldrg(0, 0) = uDir[0]; drldrg(0, 1) = uDir[1]; drldrg(0, 2) = uDir[2]; drldrg(1, 0) = vDir[0]; drldrg(1, 1) = vDir[1]; drldrg(1, 2) = vDir[2]; cout << "drldrg" << endl; drldrg.Print(); cout << "drdm * dmdg" << endl; (drdm * dmdg).Print(); // derivatives of local residuals versus rigid body parameters TMatrixD drldg(3, 6); drldg = drldrg * (drdm * dmdg); cout << "drldg" << endl; drldg.Print(); // offset to determine labels for sensor sets or individual layers // 0: PXD, TODO 1: SVD, or individual layers int offset = 0; derGlobal(0, 5) = drldg(0, 0); labGlobal(0, 5) = offset + 1; derGlobal(0, 6) = drldg(0, 1); labGlobal(0, 6) = offset + 2; derGlobal(0, 7) = drldg(0, 2); labGlobal(0, 7) = offset + 3; derGlobal(0, 8) = drldg(0, 3); labGlobal(0, 8) = offset + 4; derGlobal(0, 9) = drldg(0, 4); labGlobal(0, 9) = offset + 5; derGlobal(0,10) = drldg(0, 5); labGlobal(0,10) = offset + 6; derGlobal(1, 5) = drldg(1, 0); labGlobal(1, 5) = offset + 1; derGlobal(1, 6) = drldg(1, 1); labGlobal(1, 6) = offset + 2; derGlobal(1, 7) = drldg(1, 2); labGlobal(1, 7) = offset + 3; derGlobal(1, 8) = drldg(1, 3); labGlobal(1, 8) = offset + 4; derGlobal(1, 9) = drldg(1, 4); labGlobal(1, 9) = offset + 5; derGlobal(1,10) = drldg(1, 5); labGlobal(1,10) = offset + 6; */ point.addGlobals(labGlobal, derGlobal); } listOfPoints.push_back(point); n_gbl_points++; // Free memory on the heap delete reference; } if (flag && kfs->getBackwardNdf() == 7) { //if (n_gbl_points >= 2 && flag) { GblTrajectory * traj = 0; try { traj = new GblTrajectory(listOfPoints, seedLabel, clSeed); traj->fit(Chi2, Ndf, lostWeight); } catch(...) { // Gbl failed critically (usually GblPoint::getDerivatives ... singular matrix inversion) delete traj; return; } /* //TODO Second GBL external iteration ... for electrons with Brehmstrahlung for (unsigned int p = 0; p < listOfPoints.size(); p++){ unsigned int label = p + 1; unsigned int numRes; TVectorD residuals(2); TVectorD measErrors(2); TVectorD resErrors(2); TVectorD downWeights(2); traj->getScatResults(label, numRes, residuals, measErrors, resErrors, downWeights); resErrors[0] = 1. / resErrors[0] / resErrors[0]; resErrors[1] = 1. / resErrors[1] / resErrors[1]; // TODO: in case of more iterations here must be also kinks already added to points // in previous iteration residuals[0] = - residuals[0]; residuals[1] = - residuals[1]; listOfPoints[p].addScatterer(residuals, resErrors); } delete traj; Chi2 = 0.; Ndf = 0; lostWeight = 0.; try{ traj = new GblTrajectory(listOfPoints, seedLabel, clSeed); traj->fit(Chi2, Ndf, lostWeight, "C"); }catch(...){ // Gbl failed critically (usually GblPoint::getDerivatives ... singular matrix inversion delete traj; return; }*/ traj->milleOut(*milleFile); delete traj; #ifdef DEBUG ofs_output << kfs->getBackwardNdf() << " " << kfs->getBackwardChi2() << " " << Ndf << " " << Chi2 << std::endl; #endif } }