Back to home page

sPhenix code displayed by LXR

 
 

    


File indexing completed on 2025-08-05 08:18:30

0001 /* Copyright 2008-2013, Technische Universitaet Muenchen, Ludwig-Maximilians-Universität München
0002    Authors: Christian Hoeppner & Sebastian Neubert & Johannes Rauch & Tobias Schlüter
0003 
0004    This file is part of GENFIT.
0005 
0006    GENFIT is free software: you can redistribute it and/or modify
0007    it under the terms of the GNU Lesser General Public License as published
0008    by the Free Software Foundation, either version 3 of the License, or
0009    (at your option) any later version.
0010 
0011    GENFIT is distributed in the hope that it will be useful,
0012    but WITHOUT ANY WARRANTY; without even the implied warranty of
0013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
0014    GNU Lesser General Public License for more details.
0015 
0016    You should have received a copy of the GNU Lesser General Public License
0017    along with GENFIT.  If not, see <http://www.gnu.org/licenses/>.
0018 */
0019 
0020 #include "RKTrackRep.h"
0021 #include "IO.h"
0022 
0023 #include <Exception.h>
0024 #include <FieldManager.h>
0025 #include <MaterialEffects.h>
0026 #include <MeasuredStateOnPlane.h>
0027 #include <MeasurementOnPlane.h>
0028 
0029 #include <TBuffer.h>
0030 #include <TDecompLU.h>
0031 #include <TMath.h>
0032 
0033 #include <algorithm>
0034 
0035 #define MINSTEP 0.001   // minimum step [cm] for Runge Kutta and iteration to POCA
0036 
0037 namespace {
0038   // Use fast inversion instead of LU decomposition?
0039   const bool useInvertFast = false;
0040 }
0041 
0042 namespace genfit {
0043 
0044 
0045 RKTrackRep::RKTrackRep() :
0046   AbsTrackRep(),
0047   lastStartState_(this),
0048   lastEndState_(this),
0049   RKStepsFXStart_(0),
0050   RKStepsFXStop_(0),
0051   fJacobian_(5,5),
0052   fNoise_(5),
0053   useCache_(false),
0054   cachePos_(0)
0055 {
0056   initArrays();
0057 }
0058 
0059 
0060 RKTrackRep::RKTrackRep(int pdgCode, char propDir) :
0061   AbsTrackRep(pdgCode, propDir),
0062   lastStartState_(this),
0063   lastEndState_(this),
0064   RKStepsFXStart_(0),
0065   RKStepsFXStop_(0),
0066   fJacobian_(5,5),
0067   fNoise_(5),
0068   useCache_(false),
0069   cachePos_(0)
0070 {
0071   initArrays();
0072 }
0073 
0074 
0075 RKTrackRep::~RKTrackRep() {
0076   ;
0077 }
0078 
0079 
0080 double RKTrackRep::extrapolateToPlane(StateOnPlane& state,
0081     const SharedPlanePtr& plane,
0082     bool stopAtBoundary,
0083     bool calcJacobianNoise) const {
0084 
0085   if (debugLvl_ > 0) {
0086     debugOut << "RKTrackRep::extrapolateToPlane()\n";
0087   }
0088 
0089 
0090   if (state.getPlane() == plane) {
0091     if (debugLvl_ > 0) {
0092       debugOut << "state is already defined at plane. Do nothing! \n";
0093     }
0094     return 0;
0095   }
0096 
0097   checkCache(state, &plane);
0098 
0099   // to 7D
0100   M1x7 state7 = {{0, 0, 0, 0, 0, 0, 0}};
0101   getState7(state, state7);
0102 
0103   TMatrixDSym* covPtr(nullptr);
0104   bool fillExtrapSteps(false);
0105   if (dynamic_cast<MeasuredStateOnPlane*>(&state) != nullptr) {
0106     covPtr = &(static_cast<MeasuredStateOnPlane*>(&state)->getCov());
0107     fillExtrapSteps = true;
0108   }
0109   else if (calcJacobianNoise)
0110     fillExtrapSteps = true;
0111 
0112   // actual extrapolation
0113   bool isAtBoundary(false);
0114   double flightTime( 0. );
0115   double coveredDistance( Extrap(*(state.getPlane()), *plane, getCharge(state), getMass(state), isAtBoundary, state7, flightTime, fillExtrapSteps, covPtr, false, stopAtBoundary) );
0116 
0117   if (stopAtBoundary && isAtBoundary) {
0118     state.setPlane(SharedPlanePtr(new DetPlane(TVector3(state7[0], state7[1], state7[2]),
0119                                                 TVector3(state7[3], state7[4], state7[5]))));
0120   }
0121   else {
0122     state.setPlane(plane);
0123   }
0124 
0125   // back to 5D
0126   getState5(state, state7);
0127   setTime(state, getTime(state) + flightTime);
0128 
0129   lastEndState_ = state;
0130 
0131   return coveredDistance;
0132 }
0133 
0134 
0135 double RKTrackRep::extrapolateToLine(StateOnPlane& state,
0136     const TVector3& linePoint,
0137     const TVector3& lineDirection,
0138     bool stopAtBoundary,
0139     bool calcJacobianNoise) const {
0140 
0141   if (debugLvl_ > 0) {
0142     debugOut << "RKTrackRep::extrapolateToLine()\n";
0143   }
0144 
0145   checkCache(state, nullptr);
0146 
0147   static const unsigned int maxIt(1000);
0148 
0149   // to 7D
0150   M1x7 state7;
0151   getState7(state, state7);
0152 
0153   bool fillExtrapSteps(false);
0154   if (dynamic_cast<MeasuredStateOnPlane*>(&state) != nullptr) {
0155     fillExtrapSteps = true;
0156   }
0157   else if (calcJacobianNoise)
0158     fillExtrapSteps = true;
0159 
0160   // cppcheck-suppress unreadVariable
0161   double step(0.), lastStep(0.), maxStep(1.E99), angle(0), distToPoca(0), tracklength(0);
0162   double charge = getCharge(state);
0163   double mass = getMass(state);
0164   double flightTime = 0;
0165   TVector3 dir(state7[3], state7[4], state7[5]);
0166   TVector3 lastDir(0,0,0);
0167   TVector3 poca, poca_onwire;
0168   bool isAtBoundary(false);
0169 
0170   DetPlane startPlane(*(state.getPlane()));
0171   SharedPlanePtr plane(new DetPlane(linePoint, dir.Cross(lineDirection), lineDirection));
0172   unsigned int iterations(0);
0173 
0174   while(true){
0175     if(++iterations == maxIt) {
0176       Exception exc("RKTrackRep::extrapolateToLine ==> extrapolation to line failed, maximum number of iterations reached",__LINE__,__FILE__);
0177       exc.setFatal();
0178       throw exc;
0179     }
0180 
0181     lastStep = step;
0182     lastDir = dir;
0183 
0184     step = this->Extrap(startPlane, *plane, charge, mass, isAtBoundary, state7, flightTime, false, nullptr, true, stopAtBoundary, maxStep);
0185     tracklength += step;
0186 
0187     dir.SetXYZ(state7[3], state7[4], state7[5]);
0188     poca.SetXYZ(state7[0], state7[1], state7[2]);
0189     poca_onwire = pocaOnLine(linePoint, lineDirection, poca);
0190 
0191     // check break conditions
0192     if (stopAtBoundary && isAtBoundary) {
0193       plane->setON(dir, poca);
0194       break;
0195     }
0196 
0197     angle = fabs(dir.Angle((poca_onwire-poca))-TMath::PiOver2()); // angle between direction and connection to point - 90 deg
0198     distToPoca = (poca_onwire-poca).Mag();
0199     if (angle*distToPoca < 0.1*MINSTEP) break;
0200 
0201     // if lastStep and step have opposite sign, the real normal vector lies somewhere between the last two normal vectors (i.e. the directions)
0202     // -> try mean value of the two (normalization not needed)
0203     if (lastStep*step < 0){
0204       dir += lastDir;
0205       maxStep = 0.5*fabs(lastStep); // make it converge!
0206     }
0207 
0208     startPlane = *plane;
0209     plane->setU(dir.Cross(lineDirection));
0210   }
0211 
0212   if (fillExtrapSteps) { // now do the full extrapolation with covariance matrix
0213     // make use of the cache
0214     lastEndState_.setPlane(plane);
0215     getState5(lastEndState_, state7);
0216 
0217     tracklength = extrapolateToPlane(state, plane, false, true);
0218     lastEndState_.getAuxInfo()(1) = state.getAuxInfo()(1); // Flight time
0219   }
0220   else {
0221     state.setPlane(plane);
0222     getState5(state, state7);
0223     state.getAuxInfo()(1) += flightTime;
0224   }
0225 
0226   if (debugLvl_ > 0) {
0227     debugOut << "RKTrackRep::extrapolateToLine(): Reached POCA after " << iterations+1 << " iterations. Distance: " << (poca_onwire-poca).Mag() << " cm. Angle deviation: " << dir.Angle((poca_onwire-poca))-TMath::PiOver2() << " rad \n";
0228   }
0229 
0230   lastEndState_ = state;
0231 
0232   return tracklength;
0233 }
0234 
0235 
0236 double RKTrackRep::extrapToPoint(StateOnPlane& state,
0237     const TVector3& point,
0238     const TMatrixDSym* G,
0239     bool stopAtBoundary,
0240     bool calcJacobianNoise) const {
0241 
0242   if (debugLvl_ > 0) {
0243     debugOut << "RKTrackRep::extrapolateToPoint()\n";
0244   }
0245 
0246   checkCache(state, nullptr);
0247 
0248   static const unsigned int maxIt(1000);
0249 
0250   // to 7D
0251   M1x7 state7;
0252   getState7(state, state7);
0253 
0254   bool fillExtrapSteps(false);
0255   if (dynamic_cast<MeasuredStateOnPlane*>(&state) != nullptr) {
0256     fillExtrapSteps = true;
0257   }
0258   else if (calcJacobianNoise)
0259     fillExtrapSteps = true;
0260 
0261   // cppcheck-suppress unreadVariable
0262   double step(0.), lastStep(0.), maxStep(1.E99), angle(0), distToPoca(0), tracklength(0);
0263   TVector3 dir(state7[3], state7[4], state7[5]);
0264   if (G != nullptr) {
0265     if(G->GetNrows() != 3) {
0266       Exception exc("RKTrackRep::extrapolateToLine ==> G is not 3x3",__LINE__,__FILE__);
0267       exc.setFatal();
0268       throw exc;
0269     }
0270     dir = TMatrix(*G) * dir;
0271   }
0272   TVector3 lastDir(0,0,0);
0273 
0274   TVector3 poca;
0275   bool isAtBoundary(false);
0276 
0277   DetPlane startPlane(*(state.getPlane()));
0278   SharedPlanePtr plane(new DetPlane(point, dir));
0279   unsigned int iterations(0);
0280   double charge = getCharge(state);
0281   double mass = getMass(state);
0282   double flightTime = 0;
0283 
0284   while(true){
0285     if(++iterations == maxIt) {
0286       Exception exc("RKTrackRep::extrapolateToPoint ==> extrapolation to point failed, maximum number of iterations reached",__LINE__,__FILE__);
0287       exc.setFatal();
0288       throw exc;
0289     }
0290 
0291     lastStep = step;
0292     lastDir = dir;
0293 
0294     step = this->Extrap(startPlane, *plane, charge, mass, isAtBoundary, state7, flightTime, false, nullptr, true, stopAtBoundary, maxStep);
0295     tracklength += step;
0296 
0297     dir.SetXYZ(state7[3], state7[4], state7[5]);
0298     if (G != nullptr) {
0299       dir = TMatrix(*G) * dir;
0300     }
0301     poca.SetXYZ(state7[0], state7[1], state7[2]);
0302 
0303     // check break conditions
0304     if (stopAtBoundary && isAtBoundary) {
0305       plane->setON(dir, poca);
0306       break;
0307     }
0308 
0309     angle = fabs(dir.Angle((point-poca))-TMath::PiOver2()); // angle between direction and connection to point - 90 deg
0310     distToPoca = (point-poca).Mag();
0311     if (angle*distToPoca < 0.1*MINSTEP) break;
0312 
0313     // if lastStep and step have opposite sign, the real normal vector lies somewhere between the last two normal vectors (i.e. the directions)
0314     // -> try mean value of the two
0315     if (lastStep*step < 0){
0316       if (G != nullptr) { // after multiplication with G, dir has not length 1 anymore in general
0317         dir.SetMag(1.);
0318         lastDir.SetMag(1.);
0319       }
0320       dir += lastDir;
0321       maxStep = 0.5*fabs(lastStep); // make it converge!
0322     }
0323 
0324     startPlane = *plane;
0325     plane->setNormal(dir);
0326   }
0327 
0328   if (fillExtrapSteps) { // now do the full extrapolation with covariance matrix
0329     // make use of the cache
0330     lastEndState_.setPlane(plane);
0331     getState5(lastEndState_, state7);
0332 
0333     tracklength = extrapolateToPlane(state, plane, false, true);
0334     lastEndState_.getAuxInfo()(1) = state.getAuxInfo()(1); // Flight time
0335   }
0336   else {
0337     state.setPlane(plane);
0338     getState5(state, state7);
0339     state.getAuxInfo()(1) += flightTime;
0340   }
0341 
0342 
0343   if (debugLvl_ > 0) {
0344     debugOut << "RKTrackRep::extrapolateToPoint(): Reached POCA after " << iterations+1 << " iterations. Distance: " << (point-poca).Mag() << " cm. Angle deviation: " << dir.Angle((point-poca))-TMath::PiOver2() << " rad \n";
0345   }
0346 
0347   lastEndState_ = state;
0348 
0349   return tracklength;
0350 }
0351 
0352 
0353 double RKTrackRep::extrapolateToCylinder(StateOnPlane& state,
0354     double radius,
0355     const TVector3& linePoint,
0356     const TVector3& lineDirection,
0357     bool stopAtBoundary,
0358     bool calcJacobianNoise) const {
0359 
0360   if (debugLvl_ > 0) {
0361     debugOut << "RKTrackRep::extrapolateToCylinder()\n";
0362   }
0363 
0364   checkCache(state, nullptr);
0365 
0366   static const unsigned int maxIt(1000);
0367 
0368   // to 7D
0369   M1x7 state7;
0370   getState7(state, state7);
0371 
0372   bool fillExtrapSteps(false);
0373   if (dynamic_cast<MeasuredStateOnPlane*>(&state) != nullptr) {
0374     fillExtrapSteps = true;
0375   }
0376   else if (calcJacobianNoise)
0377     fillExtrapSteps = true;
0378 
0379   double tracklength(0.), maxStep(1.E99);
0380 
0381   TVector3 dest, pos, dir;
0382 
0383   bool isAtBoundary(false);
0384 
0385   DetPlane startPlane(*(state.getPlane()));
0386   SharedPlanePtr plane(new DetPlane());
0387   unsigned int iterations(0);
0388   double charge = getCharge(state);
0389   double mass = getMass(state);
0390   double flightTime = 0;
0391 
0392   while(true){
0393     if(++iterations == maxIt) {
0394       Exception exc("RKTrackRep::extrapolateToCylinder ==> maximum number of iterations reached",__LINE__,__FILE__);
0395       exc.setFatal();
0396       throw exc;
0397     }
0398 
0399     pos.SetXYZ(state7[0], state7[1], state7[2]);
0400     dir.SetXYZ(state7[3], state7[4], state7[5]);
0401 
0402     // solve quadratic equation
0403     TVector3 AO = (pos - linePoint);
0404     TVector3 AOxAB = (AO.Cross(lineDirection));
0405     TVector3 VxAB  = (dir.Cross(lineDirection));
0406     float ab2    = (lineDirection * lineDirection);
0407     float a      = (VxAB * VxAB);
0408     float b      = 2 * (VxAB * AOxAB);
0409     float c      = (AOxAB * AOxAB) - (radius*radius * ab2);
0410     double arg = b*b - 4.*a*c;
0411     if(arg < 0) {
0412       Exception exc("RKTrackRep::extrapolateToCylinder ==> cannot solve",__LINE__,__FILE__);
0413       exc.setFatal();
0414       throw exc;
0415     }
0416     double term = sqrt(arg);
0417     double k1, k2;
0418     if (b<0) {
0419       k1 = (-b + term)/(2.*a);
0420       k2 = 2.*c/(-b + term);
0421     }
0422     else {
0423       k1 = 2.*c/(-b - term);
0424       k2 = (-b - term)/(2.*a);
0425     }
0426 
0427     // select smallest absolute solution -> closest cylinder surface
0428     double k = k1;
0429     if (fabs(k2)<fabs(k))
0430     k = k2;
0431 
0432     if (debugLvl_ > 0) {
0433       debugOut << "RKTrackRep::extrapolateToCylinder(); k = " << k << "\n";
0434     }
0435 
0436     dest = pos + k * dir;
0437 
0438     plane->setO(dest);
0439     plane->setUV((dest-linePoint).Cross(lineDirection), lineDirection);
0440 
0441     tracklength += this->Extrap(startPlane, *plane, charge, mass, isAtBoundary, state7, flightTime, false, nullptr, true, stopAtBoundary, maxStep);
0442 
0443     // check break conditions
0444     if (stopAtBoundary && isAtBoundary) {
0445       pos.SetXYZ(state7[0], state7[1], state7[2]);
0446       dir.SetXYZ(state7[3], state7[4], state7[5]);
0447       plane->setO(pos);
0448       plane->setUV((pos-linePoint).Cross(lineDirection), lineDirection);
0449       break;
0450     }
0451 
0452     if(fabs(k)<MINSTEP) break;
0453 
0454     startPlane = *plane;
0455 
0456   }
0457 
0458   if (fillExtrapSteps) { // now do the full extrapolation with covariance matrix
0459     // make use of the cache
0460     lastEndState_.setPlane(plane);
0461     getState5(lastEndState_, state7);
0462 
0463     tracklength = extrapolateToPlane(state, plane, false, true);
0464     lastEndState_.getAuxInfo()(1) = state.getAuxInfo()(1); // Flight time
0465   }
0466   else {
0467     state.setPlane(plane);
0468     getState5(state, state7);
0469     state.getAuxInfo()(1) += flightTime;
0470   }
0471 
0472   lastEndState_ = state;
0473 
0474   return tracklength;
0475 }
0476 
0477   
0478 double RKTrackRep::extrapolateToCone(StateOnPlane& state,
0479     double openingAngle,
0480     const TVector3& conePoint,
0481     const TVector3& coneDirection,
0482     bool stopAtBoundary,
0483     bool calcJacobianNoise) const {
0484 
0485   if (debugLvl_ > 0) {
0486     debugOut << "RKTrackRep::extrapolateToCone()\n";
0487   }
0488 
0489   checkCache(state, nullptr);
0490 
0491   static const unsigned int maxIt(1000);
0492 
0493   // to 7D
0494   M1x7 state7;
0495   getState7(state, state7);
0496 
0497   bool fillExtrapSteps(false);
0498   if (dynamic_cast<MeasuredStateOnPlane*>(&state) != nullptr) {
0499     fillExtrapSteps = true;
0500   }
0501   else if (calcJacobianNoise)
0502     fillExtrapSteps = true;
0503 
0504   double tracklength(0.), maxStep(1.E99);
0505 
0506   TVector3 dest, pos, dir;
0507 
0508   bool isAtBoundary(false);
0509 
0510   DetPlane startPlane(*(state.getPlane()));
0511   SharedPlanePtr plane(new DetPlane());
0512   unsigned int iterations(0);
0513   double charge = getCharge(state);
0514   double mass = getMass(state);
0515   double flightTime = 0;
0516 
0517   while(true){
0518     if(++iterations == maxIt) {
0519       Exception exc("RKTrackRep::extrapolateToCone ==> maximum number of iterations reached",__LINE__,__FILE__);
0520       exc.setFatal();
0521       throw exc;
0522     }
0523 
0524     pos.SetXYZ(state7[0], state7[1], state7[2]);
0525     dir.SetXYZ(state7[3], state7[4], state7[5]);
0526 
0527     // solve quadratic equation a k^2 + 2 b k + c = 0
0528     // a = (U . D)^2 - cos^2 alpha * U^2
0529     // b = (Delta . D) * (U . D) - cos^2 alpha * (U . Delta)
0530     // c = (Delta . D)^2 - cos^2 alpha * Delta^2
0531     // Delta = P - V, P track point, U track direction, V cone point, D cone direction, alpha opening angle of cone
0532     TVector3 cDirection = coneDirection.Unit();
0533     TVector3 Delta = (pos - conePoint);
0534     double DirDelta = cDirection * Delta;
0535     double Delta2 = Delta*Delta;
0536     double UDir = dir * cDirection;
0537     double UDelta = dir * Delta;
0538     double U2 = dir * dir;
0539     double cosAngle2 = cos(openingAngle)*cos(openingAngle);
0540     double a = UDir*UDir - cosAngle2*U2;
0541     double b = UDir*DirDelta - cosAngle2*UDelta;
0542     double c = DirDelta*DirDelta - cosAngle2*Delta2;
0543     
0544     double arg = b*b - a*c;
0545     if(arg < -1e-9) {
0546       Exception exc("RKTrackRep::extrapolateToCone ==> cannot solve",__LINE__,__FILE__);
0547       exc.setFatal();
0548       throw exc;
0549     } else if(arg < 0) {
0550       arg = 0;
0551     }
0552 
0553     double term = sqrt(arg);
0554     double k1, k2;
0555     k1 = (-b + term) / a;
0556     k2 = (-b - term) / a;
0557 
0558     // select smallest absolute solution -> closest cone surface
0559     double k = k1;
0560     if(fabs(k2) < fabs(k)) {
0561       k = k2;
0562     }
0563 
0564     if (debugLvl_ > 0) {
0565       debugOut << "RKTrackRep::extrapolateToCone(); k = " << k << "\n";
0566     }
0567 
0568     dest = pos + k * dir;
0569     // debugOut << "In cone extrapolation ";
0570     // dest.Print();
0571 
0572     plane->setO(dest);
0573     plane->setUV((dest-conePoint).Cross(coneDirection), dest-conePoint);
0574 
0575     tracklength += this->Extrap(startPlane, *plane, charge, mass, isAtBoundary, state7, flightTime, false, nullptr, true, stopAtBoundary, maxStep);
0576 
0577     // check break conditions
0578     if (stopAtBoundary && isAtBoundary) {
0579       pos.SetXYZ(state7[0], state7[1], state7[2]);
0580       dir.SetXYZ(state7[3], state7[4], state7[5]);
0581       plane->setO(pos);
0582       plane->setUV((pos-conePoint).Cross(coneDirection), pos-conePoint);
0583       break;
0584     }
0585 
0586     if(fabs(k)<MINSTEP) break;
0587 
0588     startPlane = *plane;
0589 
0590   }
0591 
0592   if (fillExtrapSteps) { // now do the full extrapolation with covariance matrix
0593     // make use of the cache
0594     lastEndState_.setPlane(plane);
0595     getState5(lastEndState_, state7);
0596 
0597     tracklength = extrapolateToPlane(state, plane, false, true);
0598     lastEndState_.getAuxInfo()(1) = state.getAuxInfo()(1); // Flight time
0599   }
0600   else {
0601     state.setPlane(plane);
0602     getState5(state, state7);
0603     state.getAuxInfo()(1) += flightTime;
0604   }
0605 
0606   lastEndState_ = state;
0607 
0608   return tracklength;
0609 }
0610 
0611 
0612 double RKTrackRep::extrapolateToSphere(StateOnPlane& state,
0613     double radius,
0614     const TVector3& point, // center
0615     bool stopAtBoundary,
0616     bool calcJacobianNoise) const {
0617 
0618   if (debugLvl_ > 0) {
0619     debugOut << "RKTrackRep::extrapolateToSphere()\n";
0620   }
0621 
0622   checkCache(state, nullptr);
0623 
0624   static const unsigned int maxIt(1000);
0625 
0626   // to 7D
0627   M1x7 state7;
0628   getState7(state, state7);
0629 
0630   bool fillExtrapSteps(false);
0631   if (dynamic_cast<MeasuredStateOnPlane*>(&state) != nullptr) {
0632     fillExtrapSteps = true;
0633   }
0634   else if (calcJacobianNoise)
0635     fillExtrapSteps = true;
0636 
0637   double tracklength(0.), maxStep(1.E99);
0638 
0639   TVector3 dest, pos, dir;
0640 
0641   bool isAtBoundary(false);
0642 
0643   DetPlane startPlane(*(state.getPlane()));
0644   SharedPlanePtr plane(new DetPlane());
0645   unsigned int iterations(0);
0646   double charge = getCharge(state);
0647   double mass = getMass(state);
0648   double flightTime = 0;
0649 
0650   while(true){
0651     if(++iterations == maxIt) {
0652       Exception exc("RKTrackRep::extrapolateToSphere ==> maximum number of iterations reached",__LINE__,__FILE__);
0653       exc.setFatal();
0654       throw exc;
0655     }
0656 
0657     pos.SetXYZ(state7[0], state7[1], state7[2]);
0658     dir.SetXYZ(state7[3], state7[4], state7[5]);
0659 
0660     // solve quadratic equation
0661     TVector3 AO = (pos - point);
0662     double dirAO = dir * AO;
0663     double arg = dirAO*dirAO - AO*AO + radius*radius;
0664     if(arg < 0) {
0665       Exception exc("RKTrackRep::extrapolateToSphere ==> cannot solve",__LINE__,__FILE__);
0666       exc.setFatal();
0667       throw exc;
0668     }
0669     double term = sqrt(arg);
0670     double k1, k2;
0671     k1 = -dirAO + term;
0672     k2 = -dirAO - term;
0673 
0674     // select smallest absolute solution -> closest cylinder surface
0675     double k = k1;
0676     if (fabs(k2)<fabs(k))
0677     k = k2;
0678 
0679     if (debugLvl_ > 0) {
0680       debugOut << "RKTrackRep::extrapolateToSphere(); k = " << k << "\n";
0681     }
0682 
0683     dest = pos + k * dir;
0684 
0685     plane->setON(dest, dest-point);
0686 
0687     tracklength += this->Extrap(startPlane, *plane, charge, mass, isAtBoundary, state7, flightTime, false, nullptr, true, stopAtBoundary, maxStep);
0688 
0689     // check break conditions
0690     if (stopAtBoundary && isAtBoundary) {
0691       pos.SetXYZ(state7[0], state7[1], state7[2]);
0692       dir.SetXYZ(state7[3], state7[4], state7[5]);
0693       plane->setON(pos, pos-point);
0694       break;
0695     }
0696 
0697     if(fabs(k)<MINSTEP) break;
0698 
0699     startPlane = *plane;
0700 
0701   }
0702 
0703   if (fillExtrapSteps) { // now do the full extrapolation with covariance matrix
0704     // make use of the cache
0705     lastEndState_.setPlane(plane);
0706     getState5(lastEndState_, state7);
0707 
0708     tracklength = extrapolateToPlane(state, plane, false, true);
0709     lastEndState_.getAuxInfo()(1) = state.getAuxInfo()(1); // Flight time
0710   }
0711   else {
0712     state.setPlane(plane);
0713     getState5(state, state7);
0714     state.getAuxInfo()(1) += flightTime;
0715   }
0716 
0717   lastEndState_ = state;
0718 
0719   return tracklength;
0720 }
0721 
0722 
0723 double RKTrackRep::extrapolateBy(StateOnPlane& state,
0724     double step,
0725     bool stopAtBoundary,
0726     bool calcJacobianNoise) const {
0727 
0728   if (debugLvl_ > 0) {
0729     debugOut << "RKTrackRep::extrapolateBy()\n";
0730   }
0731 
0732   checkCache(state, nullptr);
0733 
0734   static const unsigned int maxIt(1000);
0735 
0736   // to 7D
0737   M1x7 state7;
0738   getState7(state, state7);
0739 
0740   bool fillExtrapSteps(false);
0741   if (dynamic_cast<MeasuredStateOnPlane*>(&state) != nullptr) {
0742     fillExtrapSteps = true;
0743   }
0744   else if (calcJacobianNoise)
0745     fillExtrapSteps = true;
0746 
0747   double tracklength(0.);
0748 
0749   TVector3 dest, pos, dir;
0750 
0751   bool isAtBoundary(false);
0752 
0753   DetPlane startPlane(*(state.getPlane()));
0754   SharedPlanePtr plane(new DetPlane());
0755   unsigned int iterations(0);
0756   double charge = getCharge(state);
0757   double mass = getMass(state);
0758   double flightTime = 0;
0759 
0760   while(true){
0761     if(++iterations == maxIt) {
0762       Exception exc("RKTrackRep::extrapolateBy ==> maximum number of iterations reached",__LINE__,__FILE__);
0763       exc.setFatal();
0764       throw exc;
0765     }
0766 
0767     pos.SetXYZ(state7[0], state7[1], state7[2]);
0768     dir.SetXYZ(state7[3], state7[4], state7[5]);
0769 
0770     dest = pos + 1.5*(step-tracklength) * dir;
0771 
0772     plane->setON(dest, dir);
0773 
0774     tracklength += this->Extrap(startPlane, *plane, charge, mass, isAtBoundary, state7, flightTime, false, nullptr, true, stopAtBoundary, (step-tracklength));
0775 
0776     // check break conditions
0777     if (stopAtBoundary && isAtBoundary) {
0778       pos.SetXYZ(state7[0], state7[1], state7[2]);
0779       dir.SetXYZ(state7[3], state7[4], state7[5]);
0780       plane->setON(pos, dir);
0781       break;
0782     }
0783 
0784     if (fabs(tracklength-step) < MINSTEP) {
0785       if (debugLvl_ > 0) {
0786         debugOut << "RKTrackRep::extrapolateBy(): reached after " << iterations << " iterations. \n";
0787       }
0788       pos.SetXYZ(state7[0], state7[1], state7[2]);
0789       dir.SetXYZ(state7[3], state7[4], state7[5]);
0790       plane->setON(pos, dir);
0791       break;
0792     }
0793 
0794     startPlane = *plane;
0795 
0796   }
0797 
0798   if (fillExtrapSteps) { // now do the full extrapolation with covariance matrix
0799     // make use of the cache
0800     lastEndState_.setPlane(plane);
0801     getState5(lastEndState_, state7);
0802 
0803     tracklength = extrapolateToPlane(state, plane, false, true);
0804     lastEndState_.getAuxInfo()(1) = state.getAuxInfo()(1); // Flight time
0805   }
0806   else {
0807     state.setPlane(plane);
0808     getState5(state, state7);
0809     state.getAuxInfo()(1) += flightTime;
0810   }
0811 
0812   lastEndState_ = state;
0813 
0814   return tracklength;
0815 }
0816 
0817 
0818 TVector3 RKTrackRep::getPos(const StateOnPlane& state) const {
0819   M1x7 state7;
0820   getState7(state, state7);
0821 
0822   return TVector3(state7[0], state7[1], state7[2]);
0823 }
0824 
0825 
0826 TVector3 RKTrackRep::getMom(const StateOnPlane& state) const {
0827   M1x7 state7 = {{0, 0, 0, 0, 0, 0, 0}};
0828   getState7(state, state7);
0829 
0830   TVector3 mom(state7[3], state7[4], state7[5]);
0831   mom.SetMag(getCharge(state)/state7[6]);
0832   return mom;
0833 }
0834 
0835 
0836 void RKTrackRep::getPosMom(const StateOnPlane& state, TVector3& pos, TVector3& mom) const {
0837   M1x7 state7 = {{0, 0, 0, 0, 0, 0, 0}};
0838   getState7(state, state7);
0839 
0840   pos.SetXYZ(state7[0], state7[1], state7[2]);
0841   mom.SetXYZ(state7[3], state7[4], state7[5]);
0842   mom.SetMag(getCharge(state)/state7[6]);
0843 }
0844 
0845 
0846 void RKTrackRep::getPosMomCov(const MeasuredStateOnPlane& state, TVector3& pos, TVector3& mom, TMatrixDSym& cov) const {
0847   getPosMom(state, pos, mom);
0848   cov.ResizeTo(6,6);
0849   transformPM6(state, *((M6x6*) cov.GetMatrixArray()));
0850 }
0851 
0852 
0853 TMatrixDSym RKTrackRep::get6DCov(const MeasuredStateOnPlane& state) const {
0854   TMatrixDSym cov(6);
0855   transformPM6(state, *((M6x6*) cov.GetMatrixArray()));
0856 
0857   return cov;
0858 }
0859 
0860 
0861 double RKTrackRep::getCharge(const StateOnPlane& state) const {
0862 
0863   if (dynamic_cast<const MeasurementOnPlane*>(&state) != nullptr) {
0864     Exception exc("RKTrackRep::getCharge - cannot get charge from MeasurementOnPlane",__LINE__,__FILE__);
0865     exc.setFatal();
0866     throw exc;
0867   }
0868 
0869   double pdgCharge( this->getPDGCharge() );
0870 
0871   // return pdgCharge with sign of q/p
0872   if (state.getState()(0) * pdgCharge < 0)
0873     return -pdgCharge;
0874   else
0875     return pdgCharge;
0876 }
0877 
0878 
0879 double RKTrackRep::getMomMag(const StateOnPlane& state) const {
0880   // p = q / qop
0881   double p = getCharge(state)/state.getState()(0);
0882   assert (p>=0);
0883   return p;
0884 }
0885 
0886 
0887 double RKTrackRep::getMomVar(const MeasuredStateOnPlane& state) const {
0888 
0889   if (dynamic_cast<const MeasurementOnPlane*>(&state) != nullptr) {
0890     Exception exc("RKTrackRep::getMomVar - cannot get momVar from MeasurementOnPlane",__LINE__,__FILE__);
0891     exc.setFatal();
0892     throw exc;
0893   }
0894 
0895   // p(qop) = q/qop
0896   // dp/d(qop) = - q / (qop^2)
0897   // (delta p) = (delta qop) * |dp/d(qop)| = delta qop * |q / (qop^2)|
0898   // (var p) = (var qop) * q^2 / (qop^4)
0899 
0900   // delta means sigma
0901   // cov(0,0) is sigma^2
0902 
0903   return state.getCov()(0,0) * pow(getCharge(state), 2)  / pow(state.getState()(0), 4);
0904 }
0905 
0906 
0907 double RKTrackRep::getSpu(const StateOnPlane& state) const {
0908 
0909   if (dynamic_cast<const MeasurementOnPlane*>(&state) != nullptr) {
0910     Exception exc("RKTrackRep::getSpu - cannot get spu from MeasurementOnPlane",__LINE__,__FILE__);
0911     exc.setFatal();
0912     throw exc;
0913   }
0914 
0915   const TVectorD& auxInfo = state.getAuxInfo();
0916   if (auxInfo.GetNrows() == 2
0917       || auxInfo.GetNrows() == 1) // backwards compatibility with old RKTrackRep
0918     return state.getAuxInfo()(0);
0919   else
0920     return 1.;
0921 }
0922 
0923 double RKTrackRep::getTime(const StateOnPlane& state) const {
0924 
0925   const TVectorD& auxInfo = state.getAuxInfo();
0926   if (auxInfo.GetNrows() == 2)
0927     return state.getAuxInfo()(1);
0928   else
0929     return 0.;
0930 }
0931 
0932 
0933 void RKTrackRep::calcForwardJacobianAndNoise(const M1x7& startState7, const DetPlane& startPlane,
0934                          const M1x7& destState7, const DetPlane& destPlane) const {
0935 
0936   if (debugLvl_ > 0) {
0937     debugOut << "RKTrackRep::calcForwardJacobianAndNoise " << std::endl;
0938   }
0939 
0940   if (ExtrapSteps_.size() == 0) {
0941     Exception exc("RKTrackRep::calcForwardJacobianAndNoise ==> cache is empty. Extrapolation must run with a MeasuredStateOnPlane.",__LINE__,__FILE__);
0942     throw exc;
0943   }
0944 
0945   // The Jacobians returned from RKutta are transposed.
0946   TMatrixD jac(TMatrixD::kTransposed, TMatrixD(7, 7, ExtrapSteps_.back().jac7_.begin()));
0947   TMatrixDSym noise(7, ExtrapSteps_.back().noise7_.begin());
0948   for (int i = ExtrapSteps_.size() - 2; i >= 0; --i) {
0949     noise += TMatrixDSym(7, ExtrapSteps_[i].noise7_.begin()).Similarity(jac);
0950     jac *= TMatrixD(TMatrixD::kTransposed, TMatrixD(7, 7, ExtrapSteps_[i].jac7_.begin()));
0951   }
0952 
0953   // Project into 5x5 space.
0954   M1x3 pTilde = {{startState7[3], startState7[4], startState7[5]}};
0955   const TVector3& normal = startPlane.getNormal();
0956   double pTildeW = pTilde[0] * normal.X() + pTilde[1] * normal.Y() + pTilde[2] * normal.Z();
0957   double spu = pTildeW > 0 ? 1 : -1;
0958   for (unsigned int i=0; i<3; ++i) {
0959     pTilde[i] *= spu/pTildeW; // | pTilde * W | has to be 1 (definition of pTilde)
0960   }
0961   M5x7 J_pM;
0962   calcJ_pM_5x7(J_pM, startPlane.getU(), startPlane.getV(), pTilde, spu);
0963   M7x5 J_Mp;
0964   calcJ_Mp_7x5(J_Mp, destPlane.getU(), destPlane.getV(), destPlane.getNormal(), *((M1x3*) &destState7[3]));
0965   jac.Transpose(jac); // Because the helper function wants transposed input.
0966   RKTools::J_pMTTxJ_MMTTxJ_MpTT(J_Mp, *(M7x7 *)jac.GetMatrixArray(),
0967                 J_pM, *(M5x5 *)fJacobian_.GetMatrixArray());
0968   RKTools::J_MpTxcov7xJ_Mp(J_Mp, *(M7x7 *)noise.GetMatrixArray(),
0969                *(M5x5 *)fNoise_.GetMatrixArray());
0970 
0971   if (debugLvl_ > 0) {
0972     debugOut << "total jacobian : "; fJacobian_.Print();
0973     debugOut << "total noise : "; fNoise_.Print();
0974   }
0975 
0976 }
0977 
0978 
0979 void RKTrackRep::getForwardJacobianAndNoise(TMatrixD& jacobian, TMatrixDSym& noise, TVectorD& deltaState) const {
0980 
0981   jacobian.ResizeTo(5,5);
0982   jacobian = fJacobian_;
0983 
0984   noise.ResizeTo(5,5);
0985   noise = fNoise_;
0986 
0987   // lastEndState_ = jacobian * lastStartState_  + deltaState
0988   deltaState.ResizeTo(5);
0989   // Calculate this without temporaries:
0990   //deltaState = lastEndState_.getState() - jacobian * lastStartState_.getState()
0991   deltaState = lastStartState_.getState();
0992   deltaState *= jacobian;
0993   deltaState -= lastEndState_.getState();
0994   deltaState *= -1;
0995 
0996 
0997   if (debugLvl_ > 0) {
0998     debugOut << "delta state : "; deltaState.Print();
0999   }
1000 }
1001 
1002 
1003 void RKTrackRep::getBackwardJacobianAndNoise(TMatrixD& jacobian, TMatrixDSym& noise, TVectorD& deltaState) const {
1004 
1005   if (debugLvl_ > 0) {
1006     debugOut << "RKTrackRep::getBackwardJacobianAndNoise " << std::endl;
1007   }
1008 
1009   if (ExtrapSteps_.size() == 0) {
1010     Exception exc("RKTrackRep::getBackwardJacobianAndNoise ==> cache is empty. Extrapolation must run with a MeasuredStateOnPlane.",__LINE__,__FILE__);
1011     throw exc;
1012   }
1013 
1014   jacobian.ResizeTo(5,5);
1015   jacobian = fJacobian_;
1016   if (!useInvertFast) {
1017     bool status = TDecompLU::InvertLU(jacobian, 0.0);
1018     if(status == 0){
1019       Exception e("cannot invert matrix, status = 0", __LINE__,__FILE__);
1020       e.setFatal();
1021       throw e;
1022     }
1023   } else {
1024     double det;
1025     jacobian.InvertFast(&det);
1026     if(det < 1e-80){
1027       Exception e("cannot invert matrix, status = 0", __LINE__,__FILE__);
1028       e.setFatal();
1029       throw e;
1030     }
1031   }
1032 
1033   noise.ResizeTo(5,5);
1034   noise = fNoise_;
1035   noise.Similarity(jacobian);
1036 
1037   // lastStartState_ = jacobian * lastEndState_  + deltaState
1038   deltaState.ResizeTo(5);
1039   deltaState = lastStartState_.getState() - jacobian * lastEndState_.getState();
1040 }
1041 
1042 
1043 std::vector<genfit::MatStep> RKTrackRep::getSteps() const {
1044 
1045   // Todo: test
1046 
1047   if (RKSteps_.size() == 0) {
1048     Exception exc("RKTrackRep::getSteps ==> cache is empty.",__LINE__,__FILE__);
1049     throw exc;
1050   }
1051 
1052   std::vector<MatStep> retVal;
1053   retVal.reserve(RKSteps_.size());
1054 
1055   for (unsigned int i = 0; i<RKSteps_.size(); ++i) {
1056     retVal.push_back(RKSteps_[i].matStep_);
1057   }
1058 
1059   return retVal;
1060 }
1061 
1062 
1063 double RKTrackRep::getRadiationLenght() const {
1064 
1065   // Todo: test
1066 
1067   if (RKSteps_.size() == 0) {
1068     Exception exc("RKTrackRep::getRadiationLenght ==> cache is empty.",__LINE__,__FILE__);
1069     throw exc;
1070   }
1071 
1072   double radLen(0);
1073 
1074   for (unsigned int i = 0; i<RKSteps_.size(); ++i) {
1075     radLen += RKSteps_.at(i).matStep_.stepSize_ / RKSteps_.at(i).matStep_.material_.radiationLength;
1076   }
1077 
1078   return radLen;
1079 }
1080 
1081 
1082 
1083 void RKTrackRep::setPosMom(StateOnPlane& state, const TVector3& pos, const TVector3& mom) const {
1084 
1085   if (state.getRep() != this){
1086     Exception exc("RKTrackRep::setPosMom ==> state is defined wrt. another TrackRep",__LINE__,__FILE__);
1087     throw exc;
1088   }
1089 
1090   if (dynamic_cast<MeasurementOnPlane*>(&state) != nullptr) {
1091     Exception exc("RKTrackRep::setPosMom - cannot set pos/mom of a MeasurementOnPlane",__LINE__,__FILE__);
1092     exc.setFatal();
1093     throw exc;
1094   }
1095 
1096   if (mom.Mag2() == 0) {
1097     Exception exc("RKTrackRep::setPosMom - momentum is 0",__LINE__,__FILE__);
1098     exc.setFatal();
1099     throw exc;
1100   }
1101 
1102   // init auxInfo if that has not yet happened
1103   TVectorD& auxInfo = state.getAuxInfo();
1104   if (auxInfo.GetNrows() != 2) {
1105     bool alreadySet = auxInfo.GetNrows() == 1;  // backwards compatibility: don't overwrite old setting
1106     auxInfo.ResizeTo(2);
1107     if (!alreadySet)
1108       setSpu(state, 1.);
1109   }
1110 
1111   if (state.getPlane() != nullptr && state.getPlane()->distance(pos) < MINSTEP) { // pos is on plane -> do not change plane!
1112 
1113     M1x7 state7;
1114 
1115     state7[0] = pos.X();
1116     state7[1] = pos.Y();
1117     state7[2] = pos.Z();
1118 
1119     state7[3] = mom.X();
1120     state7[4] = mom.Y();
1121     state7[5] = mom.Z();
1122 
1123     // normalize dir
1124     double norm = 1. / sqrt(state7[3]*state7[3] + state7[4]*state7[4] + state7[5]*state7[5]);
1125     for (unsigned int i=3; i<6; ++i)
1126       state7[i] *= norm;
1127 
1128     state7[6] = getCharge(state) * norm;
1129 
1130     getState5(state, state7);
1131 
1132   }
1133   else { // pos is not on plane -> create new plane!
1134 
1135     // TODO: Raise Warning that a new plane has been created!
1136     SharedPlanePtr plane(new DetPlane(pos, mom));
1137     state.setPlane(plane);
1138 
1139     TVectorD& state5(state.getState());
1140 
1141     state5(0) = getCharge(state)/mom.Mag(); // q/p
1142     state5(1) = 0.; // u'
1143     state5(2) = 0.; // v'
1144     state5(3) = 0.; // u
1145     state5(4) = 0.; // v
1146 
1147     setSpu(state, 1.);
1148   }
1149 
1150 }
1151 
1152 
1153 void RKTrackRep::setPosMom(StateOnPlane& state, const TVectorD& state6) const {
1154   if (state6.GetNrows()!=6){
1155     Exception exc("RKTrackRep::setPosMom ==> state has to be 6d (x, y, z, px, py, pz)",__LINE__,__FILE__);
1156     throw exc;
1157   }
1158   setPosMom(state, TVector3(state6(0), state6(1), state6(2)), TVector3(state6(3), state6(4), state6(5)));
1159 }
1160 
1161 
1162 void RKTrackRep::setPosMomErr(MeasuredStateOnPlane& state, const TVector3& pos, const TVector3& mom, const TVector3& posErr, const TVector3& momErr) const {
1163 
1164   // TODO: test!
1165 
1166   setPosMom(state, pos, mom);
1167 
1168   const TVector3& U(state.getPlane()->getU());
1169   const TVector3& V(state.getPlane()->getV());
1170   TVector3 W(state.getPlane()->getNormal());
1171 
1172   double pw = mom * W;
1173   double pu = mom * U;
1174   double pv = mom * V;
1175 
1176   TMatrixDSym& cov(state.getCov());
1177 
1178   cov(0,0) = pow(getCharge(state), 2) / pow(mom.Mag(), 6) *
1179              (mom.X()*mom.X() * momErr.X()*momErr.X()+
1180               mom.Y()*mom.Y() * momErr.Y()*momErr.Y()+
1181               mom.Z()*mom.Z() * momErr.Z()*momErr.Z());
1182 
1183   cov(1,1) = pow((U.X()/pw - W.X()*pu/(pw*pw)),2.) * momErr.X()*momErr.X() +
1184              pow((U.Y()/pw - W.Y()*pu/(pw*pw)),2.) * momErr.Y()*momErr.Y() +
1185              pow((U.Z()/pw - W.Z()*pu/(pw*pw)),2.) * momErr.Z()*momErr.Z();
1186 
1187   cov(2,2) = pow((V.X()/pw - W.X()*pv/(pw*pw)),2.) * momErr.X()*momErr.X() +
1188              pow((V.Y()/pw - W.Y()*pv/(pw*pw)),2.) * momErr.Y()*momErr.Y() +
1189              pow((V.Z()/pw - W.Z()*pv/(pw*pw)),2.) * momErr.Z()*momErr.Z();
1190 
1191   cov(3,3) = posErr.X()*posErr.X() * U.X()*U.X() +
1192              posErr.Y()*posErr.Y() * U.Y()*U.Y() +
1193              posErr.Z()*posErr.Z() * U.Z()*U.Z();
1194 
1195   cov(4,4) = posErr.X()*posErr.X() * V.X()*V.X() +
1196              posErr.Y()*posErr.Y() * V.Y()*V.Y() +
1197              posErr.Z()*posErr.Z() * V.Z()*V.Z();
1198 
1199 }
1200 
1201 
1202 
1203 
1204 void RKTrackRep::setPosMomCov(MeasuredStateOnPlane& state, const TVector3& pos, const TVector3& mom, const TMatrixDSym& cov6x6) const {
1205 
1206   if (cov6x6.GetNcols()!=6 || cov6x6.GetNrows()!=6){
1207     Exception exc("RKTrackRep::setPosMomCov ==> cov has to be 6x6 (x, y, z, px, py, pz)",__LINE__,__FILE__);
1208     throw exc;
1209   }
1210 
1211   setPosMom(state, pos, mom); // charge does not change!
1212 
1213   M1x7 state7;
1214   getState7(state, state7);
1215 
1216   const M6x6& cov6x6_( *((M6x6*) cov6x6.GetMatrixArray()) );
1217 
1218   transformM6P(cov6x6_, state7, state);
1219 
1220 }
1221 
1222 void RKTrackRep::setPosMomCov(MeasuredStateOnPlane& state, const TVectorD& state6, const TMatrixDSym& cov6x6) const {
1223 
1224   if (state6.GetNrows()!=6){
1225     Exception exc("RKTrackRep::setPosMomCov ==> state has to be 6d (x, y, z, px, py, pz)",__LINE__,__FILE__);
1226     throw exc;
1227   }
1228 
1229   if (cov6x6.GetNcols()!=6 || cov6x6.GetNrows()!=6){
1230     Exception exc("RKTrackRep::setPosMomCov ==> cov has to be 6x6 (x, y, z, px, py, pz)",__LINE__,__FILE__);
1231     throw exc;
1232   }
1233 
1234   TVector3 pos(state6(0), state6(1), state6(2));
1235   TVector3 mom(state6(3), state6(4), state6(5));
1236   setPosMom(state, pos, mom); // charge does not change!
1237 
1238   M1x7 state7;
1239   getState7(state, state7);
1240 
1241   const M6x6& cov6x6_( *((M6x6*) cov6x6.GetMatrixArray()) );
1242 
1243   transformM6P(cov6x6_, state7, state);
1244 
1245 }
1246 
1247 
1248 void RKTrackRep::setChargeSign(StateOnPlane& state, double charge) const {
1249 
1250   if (dynamic_cast<MeasurementOnPlane*>(&state) != nullptr) {
1251     Exception exc("RKTrackRep::setChargeSign - cannot set charge of a MeasurementOnPlane",__LINE__,__FILE__);
1252     exc.setFatal();
1253     throw exc;
1254   }
1255 
1256   if (state.getState()(0) * charge < 0) {
1257     state.getState()(0) *= -1.;
1258   }
1259 }
1260 
1261 
1262 void RKTrackRep::setSpu(StateOnPlane& state, double spu) const {
1263   state.getAuxInfo().ResizeTo(2);
1264   (state.getAuxInfo())(0) = spu;
1265 }
1266 
1267 void RKTrackRep::setTime(StateOnPlane& state, double time) const {
1268   state.getAuxInfo().ResizeTo(2);
1269   (state.getAuxInfo())(1) = time;
1270 }
1271 
1272 
1273 
1274 double RKTrackRep::RKPropagate(M1x7& state7,
1275                         M7x7* jacobianT,
1276                         M1x3& SA,
1277                         double S,
1278                         bool varField,
1279                         bool calcOnlyLastRowOfJ) const {
1280   // The algorithm is
1281   //  E Lund et al 2009 JINST 4 P04001 doi:10.1088/1748-0221/4/04/P04001
1282   //  "Track parameter propagation through the application of a new adaptive Runge-Kutta-Nyström method in the ATLAS experiment"
1283   //  http://inspirehep.net/search?ln=en&ln=en&p=10.1088/1748-0221/4/04/P04001&of=hb&action_search=Search&sf=earliestdate&so=d&rm=&rg=25&sc=0
1284   // where the transport of the Jacobian is described in
1285   //   L. Bugge, J. Myrheim  Nucl.Instrum.Meth. 160 (1979) 43-48
1286   //   "A Fast Runge-kutta Method For Fitting Tracks In A Magnetic Field"
1287   //   http://inspirehep.net/record/145692
1288   // and
1289   //   L. Bugge, J. Myrheim  Nucl.Instrum.Meth. 179 (1981) 365-381
1290   //   "Tracking And Track Fitting"
1291   //   http://inspirehep.net/record/160548
1292 
1293   // important fixed numbers
1294   static const double EC  ( 0.000149896229 );  // c/(2*10^12) resp. c/2Tera
1295   static const double P3  ( 1./3. );           // 1/3
1296   static const double DLT ( .0002 );           // max. deviation for approximation-quality test
1297   // Aux parameters
1298   M1x3&   R           = *((M1x3*) &state7[0]);       // Start coordinates  [cm]  (x,  y,  z)
1299   M1x3&   A           = *((M1x3*) &state7[3]);       // Start directions         (ax, ay, az);   ax^2+ay^2+az^2=1
1300   double  S3(0), S4(0), PS2(0);
1301   M1x3     H0 = {{0.,0.,0.}}, H1 = {{0.,0.,0.}}, H2 = {{0.,0.,0.}};
1302   M1x3     r = {{0.,0.,0.}};
1303   // Variables for Runge Kutta solver
1304   double   A0(0), A1(0), A2(0), A3(0), A4(0), A5(0), A6(0);
1305   double   B0(0), B1(0), B2(0), B3(0), B4(0), B5(0), B6(0);
1306   double   C0(0), C1(0), C2(0), C3(0), C4(0), C5(0), C6(0);
1307 
1308   //
1309   // Runge Kutta Extrapolation
1310   //
1311   S3 = P3*S;
1312   S4 = 0.25*S;
1313   PS2 = state7[6]*EC * S;
1314 
1315   // First point
1316   r[0] = R[0];           r[1] = R[1];           r[2]=R[2];
1317   FieldManager::getInstance()->getFieldVal(r[0], r[1], r[2], H0[0], H0[1], H0[2]);       // magnetic field in 10^-1 T = kGauss
1318   H0[0] *= PS2; H0[1] *= PS2; H0[2] *= PS2;     // H0 is PS2*(Hx, Hy, Hz) @ R0
1319   A0 = A[1]*H0[2]-A[2]*H0[1]; B0 = A[2]*H0[0]-A[0]*H0[2]; C0 = A[0]*H0[1]-A[1]*H0[0]; // (ax, ay, az) x H0
1320   A2 = A[0]+A0              ; B2 = A[1]+B0              ; C2 = A[2]+C0              ; // (A0, B0, C0) + (ax, ay, az)
1321   A1 = A2+A[0]              ; B1 = B2+A[1]              ; C1 = C2+A[2]              ; // (A0, B0, C0) + 2*(ax, ay, az)
1322 
1323   // Second point
1324   if (varField) {
1325     r[0] += A1*S4;         r[1] += B1*S4;         r[2] += C1*S4;
1326     FieldManager::getInstance()->getFieldVal(r[0], r[1], r[2], H1[0], H1[1], H1[2]);
1327     H1[0] *= PS2; H1[1] *= PS2; H1[2] *= PS2; // H1 is PS2*(Hx, Hy, Hz) @ (x, y, z) + 0.25*S * [(A0, B0, C0) + 2*(ax, ay, az)]
1328   }
1329   else { H1 = H0; };
1330   A3 = B2*H1[2]-C2*H1[1]+A[0]; B3 = C2*H1[0]-A2*H1[2]+A[1]; C3 = A2*H1[1]-B2*H1[0]+A[2]; // (A2, B2, C2) x H1 + (ax, ay, az)
1331   A4 = B3*H1[2]-C3*H1[1]+A[0]; B4 = C3*H1[0]-A3*H1[2]+A[1]; C4 = A3*H1[1]-B3*H1[0]+A[2]; // (A3, B3, C3) x H1 + (ax, ay, az)
1332   A5 = A4-A[0]+A4            ; B5 = B4-A[1]+B4            ; C5 = C4-A[2]+C4            ; //    2*(A4, B4, C4) - (ax, ay, az)
1333 
1334   // Last point
1335   if (varField) {
1336     r[0]=R[0]+S*A4;         r[1]=R[1]+S*B4;         r[2]=R[2]+S*C4;  //setup.Field(r,H2);
1337     FieldManager::getInstance()->getFieldVal(r[0], r[1], r[2], H2[0], H2[1], H2[2]);
1338     H2[0] *= PS2; H2[1] *= PS2; H2[2] *= PS2; // H2 is PS2*(Hx, Hy, Hz) @ (x, y, z) + 0.25*S * (A4, B4, C4)
1339   }
1340   else { H2 = H0; };
1341   A6 = B5*H2[2]-C5*H2[1]; B6 = C5*H2[0]-A5*H2[2]; C6 = A5*H2[1]-B5*H2[0]; // (A5, B5, C5) x H2
1342 
1343 
1344   //
1345   // Derivatives of track parameters
1346   //
1347   if(jacobianT != nullptr){
1348 
1349     // jacobianT
1350     // 1 0 0 0 0 0 0  x
1351     // 0 1 0 0 0 0 0  y
1352     // 0 0 1 0 0 0 0  z
1353     // x x x x x x 0  a_x
1354     // x x x x x x 0  a_y
1355     // x x x x x x 0  a_z
1356     // x x x x x x 1  q/p
1357     M7x7& J = *jacobianT;
1358 
1359     double   dA0(0), dA2(0), dA3(0), dA4(0), dA5(0), dA6(0);
1360     double   dB0(0), dB2(0), dB3(0), dB4(0), dB5(0), dB6(0);
1361     double   dC0(0), dC2(0), dC3(0), dC4(0), dC5(0), dC6(0);
1362 
1363     int start(0);
1364 
1365     if (!calcOnlyLastRowOfJ) {
1366 
1367       if (!varField) {
1368         // d(x, y, z)/d(x, y, z) submatrix is unit matrix
1369         J(0, 0) = 1;  J(1, 1) = 1;  J(2, 2) = 1;
1370         // d(ax, ay, az)/d(ax, ay, az) submatrix is 0
1371         // start with d(x, y, z)/d(ax, ay, az)
1372         start = 3;
1373       }
1374 
1375       for(int i=start; i<6; ++i) {
1376 
1377         //first point
1378         dA0 = H0[2]*J(i, 4)-H0[1]*J(i, 5);    // dA0/dp }
1379         dB0 = H0[0]*J(i, 5)-H0[2]*J(i, 3);    // dB0/dp  } = dA x H0
1380         dC0 = H0[1]*J(i, 3)-H0[0]*J(i, 4);    // dC0/dp }
1381 
1382         dA2 = dA0+J(i, 3);        // }
1383         dB2 = dB0+J(i, 4);        //  } = (dA0, dB0, dC0) + dA
1384         dC2 = dC0+J(i, 5);        // }
1385 
1386         //second point
1387         dA3 = J(i, 3)+dB2*H1[2]-dC2*H1[1];    // dA3/dp }
1388         dB3 = J(i, 4)+dC2*H1[0]-dA2*H1[2];    // dB3/dp  } = dA + (dA2, dB2, dC2) x H1
1389         dC3 = J(i, 5)+dA2*H1[1]-dB2*H1[0];    // dC3/dp }
1390 
1391         dA4 = J(i, 3)+dB3*H1[2]-dC3*H1[1];    // dA4/dp }
1392         dB4 = J(i, 4)+dC3*H1[0]-dA3*H1[2];    // dB4/dp  } = dA + (dA3, dB3, dC3) x H1
1393         dC4 = J(i, 5)+dA3*H1[1]-dB3*H1[0];    // dC4/dp }
1394 
1395         //last point
1396         dA5 = dA4+dA4-J(i, 3);      // }
1397         dB5 = dB4+dB4-J(i, 4);      //  } =  2*(dA4, dB4, dC4) - dA
1398         dC5 = dC4+dC4-J(i, 5);      // }
1399 
1400         dA6 = dB5*H2[2]-dC5*H2[1];      // dA6/dp }
1401         dB6 = dC5*H2[0]-dA5*H2[2];      // dB6/dp  } = (dA5, dB5, dC5) x H2
1402         dC6 = dA5*H2[1]-dB5*H2[0];      // dC6/dp }
1403 
1404         // this gives the same results as multiplying the old with the new Jacobian
1405         J(i, 0) += (dA2+dA3+dA4)*S3;  J(i, 3) = ((dA0+2.*dA3)+(dA5+dA6))*P3; // dR := dR + S3*[(dA2, dB2, dC2) +   (dA3, dB3, dC3) + (dA4, dB4, dC4)]
1406         J(i, 1) += (dB2+dB3+dB4)*S3;  J(i, 4) = ((dB0+2.*dB3)+(dB5+dB6))*P3; // dA :=     1/3*[(dA0, dB0, dC0) + 2*(dA3, dB3, dC3) + (dA5, dB5, dC5) + (dA6, dB6, dC6)]
1407         J(i, 2) += (dC2+dC3+dC4)*S3;  J(i, 5) = ((dC0+2.*dC3)+(dC5+dC6))*P3;
1408       }
1409 
1410     } // end if (!calcOnlyLastRowOfJ)
1411 
1412     J(6, 3) *= state7[6]; J(6, 4) *= state7[6]; J(6, 5) *= state7[6];
1413 
1414     //first point
1415     dA0 = H0[2]*J(6, 4)-H0[1]*J(6, 5) + A0;    // dA0/dp }
1416     dB0 = H0[0]*J(6, 5)-H0[2]*J(6, 3) + B0;    // dB0/dp  } = dA x H0 + (A0, B0, C0)
1417     dC0 = H0[1]*J(6, 3)-H0[0]*J(6, 4) + C0;    // dC0/dp }
1418 
1419     dA2 = dA0+J(6, 3);        // }
1420     dB2 = dB0+J(6, 4);        //  } = (dA0, dB0, dC0) + dA
1421     dC2 = dC0+J(6, 5);        // }
1422 
1423     //second point
1424     dA3 = J(6, 3)+dB2*H1[2]-dC2*H1[1] + (A3-A[0]);    // dA3/dp }
1425     dB3 = J(6, 4)+dC2*H1[0]-dA2*H1[2] + (B3-A[1]);    // dB3/dp  } = dA + (dA2, dB2, dC2) x H1
1426     dC3 = J(6, 5)+dA2*H1[1]-dB2*H1[0] + (C3-A[2]);    // dC3/dp }
1427 
1428     dA4 = J(6, 3)+dB3*H1[2]-dC3*H1[1] + (A4-A[0]);    // dA4/dp }
1429     dB4 = J(6, 4)+dC3*H1[0]-dA3*H1[2] + (B4-A[1]);    // dB4/dp  } = dA + (dA3, dB3, dC3) x H1
1430     dC4 = J(6, 5)+dA3*H1[1]-dB3*H1[0] + (C4-A[2]);    // dC4/dp }
1431 
1432     //last point
1433     dA5 = dA4+dA4-J(6, 3);      // }
1434     dB5 = dB4+dB4-J(6, 4);      //  } =  2*(dA4, dB4, dC4) - dA
1435     dC5 = dC4+dC4-J(6, 5);      // }
1436 
1437     dA6 = dB5*H2[2]-dC5*H2[1] + A6;      // dA6/dp }
1438     dB6 = dC5*H2[0]-dA5*H2[2] + B6;      // dB6/dp  } = (dA5, dB5, dC5) x H2 + (A6, B6, C6)
1439     dC6 = dA5*H2[1]-dB5*H2[0] + C6;      // dC6/dp }
1440 
1441     // this gives the same results as multiplying the old with the new Jacobian
1442     J(6, 0) += (dA2+dA3+dA4)*S3/state7[6];  J(6, 3) = ((dA0+2.*dA3)+(dA5+dA6))*P3/state7[6]; // dR := dR + S3*[(dA2, dB2, dC2) +   (dA3, dB3, dC3) + (dA4, dB4, dC4)]
1443     J(6, 1) += (dB2+dB3+dB4)*S3/state7[6];  J(6, 4) = ((dB0+2.*dB3)+(dB5+dB6))*P3/state7[6]; // dA :=     1/3*[(dA0, dB0, dC0) + 2*(dA3, dB3, dC3) + (dA5, dB5, dC5) + (dA6, dB6, dC6)]
1444     J(6, 2) += (dC2+dC3+dC4)*S3/state7[6];  J(6, 5) = ((dC0+2.*dC3)+(dC5+dC6))*P3/state7[6];
1445 
1446   }
1447 
1448   //
1449   // Track parameters in last point
1450   //
1451   R[0] += (A2+A3+A4)*S3;   A[0] += (SA[0]=((A0+2.*A3)+(A5+A6))*P3-A[0]);  // R  = R0 + S3*[(A2, B2, C2) +   (A3, B3, C3) + (A4, B4, C4)]
1452   R[1] += (B2+B3+B4)*S3;   A[1] += (SA[1]=((B0+2.*B3)+(B5+B6))*P3-A[1]);  // A  =     1/3*[(A0, B0, C0) + 2*(A3, B3, C3) + (A5, B5, C5) + (A6, B6, C6)]
1453   R[2] += (C2+C3+C4)*S3;   A[2] += (SA[2]=((C0+2.*C3)+(C5+C6))*P3-A[2]);  // SA = A_new - A_old
1454 
1455   // normalize A
1456   double CBA ( 1./sqrt(A[0]*A[0]+A[1]*A[1]+A[2]*A[2]) ); // 1/|A|
1457   A[0] *= CBA; A[1] *= CBA; A[2] *= CBA;
1458 
1459 
1460   // Test approximation quality on given step
1461   double EST ( fabs((A1+A6)-(A3+A4)) +
1462                fabs((B1+B6)-(B3+B4)) +
1463                fabs((C1+C6)-(C3+C4))  );  // EST = ||(ABC1+ABC6)-(ABC3+ABC4)||_1  =  ||(axzy x H0 + ABC5 x H2) - (ABC2 x H1 + ABC3 x H1)||_1
1464   if (debugLvl_ > 0) {
1465     debugOut << "    RKTrackRep::RKPropagate. Step = "<< S << "; quality EST = " << EST  << " \n";
1466   }
1467 
1468   // Prevent the step length increase from getting too large, this is
1469   // just the point where it becomes 10.
1470   if (EST < DLT*1e-5)
1471     return 10;
1472 
1473   // Step length increase for a fifth order Runge-Kutta, see e.g. 17.2
1474   // in Numerical Recipes.  FIXME: move to caller.
1475   return pow(DLT/EST, 1./5.);
1476 }
1477 
1478 
1479 
1480 void RKTrackRep::initArrays() const {
1481   std::fill(noiseArray_.begin(), noiseArray_.end(), 0);
1482   std::fill(noiseProjection_.begin(), noiseProjection_.end(), 0);
1483   for (unsigned int i=0; i<7; ++i) // initialize as diagonal matrix
1484     noiseProjection_[i*8] = 1;
1485   std::fill(J_MMT_.begin(), J_MMT_.end(), 0);
1486 
1487   fJacobian_.UnitMatrix();
1488   fNoise_.Zero();
1489   limits_.reset();
1490 
1491   RKSteps_.reserve(100);
1492   ExtrapSteps_.reserve(100);
1493 
1494   lastStartState_.getAuxInfo().ResizeTo(2);
1495   lastEndState_.getAuxInfo().ResizeTo(2);
1496 }
1497 
1498 
1499 void RKTrackRep::getState7(const StateOnPlane& state, M1x7& state7) const {
1500 
1501   if (dynamic_cast<const MeasurementOnPlane*>(&state) != nullptr) {
1502     Exception exc("RKTrackRep::getState7 - cannot get pos or mom from a MeasurementOnPlane",__LINE__,__FILE__);
1503     exc.setFatal();
1504     throw exc;
1505   }
1506 
1507   const TVector3& U(state.getPlane()->getU());
1508   const TVector3& V(state.getPlane()->getV());
1509   const TVector3& O(state.getPlane()->getO());
1510   const TVector3& W(state.getPlane()->getNormal());
1511 
1512   assert(state.getState().GetNrows() == 5);
1513   const double* state5 = state.getState().GetMatrixArray();
1514 
1515   double spu = getSpu(state);
1516 
1517   state7[0] = O.X() + state5[3]*U.X() + state5[4]*V.X(); // x
1518   state7[1] = O.Y() + state5[3]*U.Y() + state5[4]*V.Y(); // y
1519   state7[2] = O.Z() + state5[3]*U.Z() + state5[4]*V.Z(); // z
1520 
1521   state7[3] = spu * (W.X() + state5[1]*U.X() + state5[2]*V.X()); // a_x
1522   state7[4] = spu * (W.Y() + state5[1]*U.Y() + state5[2]*V.Y()); // a_y
1523   state7[5] = spu * (W.Z() + state5[1]*U.Z() + state5[2]*V.Z()); // a_z
1524 
1525   // normalize dir
1526   double norm = 1. / sqrt(state7[3]*state7[3] + state7[4]*state7[4] + state7[5]*state7[5]);
1527   for (unsigned int i=3; i<6; ++i) state7[i] *= norm;
1528 
1529   state7[6] = state5[0]; // q/p
1530 }
1531 
1532 
1533 void RKTrackRep::getState5(StateOnPlane& state, const M1x7& state7) const {
1534 
1535   // state5: (q/p, u', v'. u, v)
1536 
1537   double spu(1.);
1538 
1539   const TVector3& O(state.getPlane()->getO());
1540   const TVector3& U(state.getPlane()->getU());
1541   const TVector3& V(state.getPlane()->getV());
1542   const TVector3& W(state.getPlane()->getNormal());
1543 
1544   // force A to be in normal direction and set spu accordingly
1545   double AtW( state7[3]*W.X() + state7[4]*W.Y() + state7[5]*W.Z() );
1546   if (AtW < 0.) {
1547     //fDir *= -1.;
1548     //AtW *= -1.;
1549     spu = -1.;
1550   }
1551 
1552   double* state5 = state.getState().GetMatrixArray();
1553 
1554   state5[0] = state7[6]; // q/p
1555   state5[1] = (state7[3]*U.X() + state7[4]*U.Y() + state7[5]*U.Z()) / AtW; // u' = (A * U) / (A * W)
1556   state5[2] = (state7[3]*V.X() + state7[4]*V.Y() + state7[5]*V.Z()) / AtW; // v' = (A * V) / (A * W)
1557   state5[3] = ((state7[0]-O.X())*U.X() +
1558                (state7[1]-O.Y())*U.Y() +
1559                (state7[2]-O.Z())*U.Z()); // u = (pos - O) * U
1560   state5[4] = ((state7[0]-O.X())*V.X() +
1561                (state7[1]-O.Y())*V.Y() +
1562                (state7[2]-O.Z())*V.Z()); // v = (pos - O) * V
1563 
1564   setSpu(state, spu);
1565 
1566 }
1567 
1568 void RKTrackRep::calcJ_pM_5x7(M5x7& J_pM, const TVector3& U, const TVector3& V, const M1x3& pTilde, double spu) const {
1569   /*if (debugLvl_ > 1) {
1570     debugOut << "RKTrackRep::calcJ_pM_5x7 \n";
1571     debugOut << "  U = "; U.Print();
1572     debugOut << "  V = "; V.Print();
1573     debugOut << "  pTilde = "; RKTools::printDim(pTilde, 3,1);
1574     debugOut << "  spu = " << spu << "\n";
1575   }*/
1576 
1577   std::fill(J_pM.begin(), J_pM.end(), 0);
1578 
1579   const double pTildeMag = sqrt(pTilde[0]*pTilde[0] + pTilde[1]*pTilde[1] + pTilde[2]*pTilde[2]);
1580   const double pTildeMag2 = pTildeMag*pTildeMag;
1581 
1582   const double utpTildeOverpTildeMag2 = (U.X()*pTilde[0] + U.Y()*pTilde[1] + U.Z()*pTilde[2]) / pTildeMag2;
1583   const double vtpTildeOverpTildeMag2 = (V.X()*pTilde[0] + V.Y()*pTilde[1] + V.Z()*pTilde[2]) / pTildeMag2;
1584 
1585   //J_pM matrix is d(x,y,z,ax,ay,az,q/p) / d(q/p,u',v',u,v)   (out is 7x7)
1586 
1587    // d(x,y,z)/d(u)
1588   J_pM[21] = U.X(); // [3][0]
1589   J_pM[22] = U.Y(); // [3][1]
1590   J_pM[23] = U.Z(); // [3][2]
1591   // d(x,y,z)/d(v)
1592   J_pM[28] = V.X(); // [4][0]
1593   J_pM[29] = V.Y(); // [4][1]
1594   J_pM[30] = V.Z(); // [4][2]
1595   // d(q/p)/d(q/p)
1596   J_pM[6] = 1.; // not needed for array matrix multiplication
1597   // d(ax,ay,az)/d(u')
1598   double fact = spu / pTildeMag;
1599   J_pM[10] = fact * ( U.X() - pTilde[0]*utpTildeOverpTildeMag2 ); // [1][3]
1600   J_pM[11] = fact * ( U.Y() - pTilde[1]*utpTildeOverpTildeMag2 ); // [1][4]
1601   J_pM[12] = fact * ( U.Z() - pTilde[2]*utpTildeOverpTildeMag2 ); // [1][5]
1602   // d(ax,ay,az)/d(v')
1603   J_pM[17] = fact * ( V.X() - pTilde[0]*vtpTildeOverpTildeMag2 ); // [2][3]
1604   J_pM[18] = fact * ( V.Y() - pTilde[1]*vtpTildeOverpTildeMag2 ); // [2][4]
1605   J_pM[19] = fact * ( V.Z() - pTilde[2]*vtpTildeOverpTildeMag2 ); // [2][5]
1606 
1607   /*if (debugLvl_ > 1) {
1608     debugOut << "  J_pM_5x7_ = "; RKTools::printDim(J_pM_5x7_, 5,7);
1609   }*/
1610 }
1611 
1612 
1613 void RKTrackRep::transformPM6(const MeasuredStateOnPlane& state,
1614                               M6x6& out6x6) const {
1615 
1616   // get vectors and aux variables
1617   const TVector3& U(state.getPlane()->getU());
1618   const TVector3& V(state.getPlane()->getV());
1619   const TVector3& W(state.getPlane()->getNormal());
1620 
1621   const TVectorD& state5(state.getState());
1622   double spu(getSpu(state));
1623 
1624   M1x3 pTilde;
1625   pTilde[0] = spu * (W.X() + state5(1)*U.X() + state5(2)*V.X()); // a_x
1626   pTilde[1] = spu * (W.Y() + state5(1)*U.Y() + state5(2)*V.Y()); // a_y
1627   pTilde[2] = spu * (W.Z() + state5(1)*U.Z() + state5(2)*V.Z()); // a_z
1628 
1629   const double pTildeMag = sqrt(pTilde[0]*pTilde[0] + pTilde[1]*pTilde[1] + pTilde[2]*pTilde[2]);
1630   const double pTildeMag2 = pTildeMag*pTildeMag;
1631 
1632   const double utpTildeOverpTildeMag2 = (U.X()*pTilde[0] + U.Y()*pTilde[1] + U.Z()*pTilde[2]) / pTildeMag2;
1633   const double vtpTildeOverpTildeMag2 = (V.X()*pTilde[0] + V.Y()*pTilde[1] + V.Z()*pTilde[2]) / pTildeMag2;
1634 
1635   //J_pM matrix is d(x,y,z,px,py,pz) / d(q/p,u',v',u,v)       (out is 6x6)
1636 
1637   const double qop = state5(0);
1638   const double p = getCharge(state)/qop; // momentum
1639 
1640   M5x6 J_pM_5x6;
1641   std::fill(J_pM_5x6.begin(), J_pM_5x6.end(), 0);
1642 
1643   // d(px,py,pz)/d(q/p)
1644   double fact = -1. * p / (pTildeMag * qop);
1645   J_pM_5x6[3] = fact * pTilde[0]; // [0][3]
1646   J_pM_5x6[4] = fact * pTilde[1]; // [0][4]
1647   J_pM_5x6[5] = fact * pTilde[2]; // [0][5]
1648   // d(px,py,pz)/d(u')
1649   fact = p * spu / pTildeMag;
1650   J_pM_5x6[9]  = fact * ( U.X() - pTilde[0]*utpTildeOverpTildeMag2 ); // [1][3]
1651   J_pM_5x6[10] = fact * ( U.Y() - pTilde[1]*utpTildeOverpTildeMag2 ); // [1][4]
1652   J_pM_5x6[11] = fact * ( U.Z() - pTilde[2]*utpTildeOverpTildeMag2 ); // [1][5]
1653   // d(px,py,pz)/d(v')
1654   J_pM_5x6[15] = fact * ( V.X() - pTilde[0]*vtpTildeOverpTildeMag2 ); // [2][3]
1655   J_pM_5x6[16] = fact * ( V.Y() - pTilde[1]*vtpTildeOverpTildeMag2 ); // [2][4]
1656   J_pM_5x6[17] = fact * ( V.Z() - pTilde[2]*vtpTildeOverpTildeMag2 ); // [2][5]
1657   // d(x,y,z)/d(u)
1658   J_pM_5x6[18] = U.X(); // [3][0]
1659   J_pM_5x6[19] = U.Y(); // [3][1]
1660   J_pM_5x6[20] = U.Z(); // [3][2]
1661   // d(x,y,z)/d(v)
1662   J_pM_5x6[24] = V.X(); // [4][0]
1663   J_pM_5x6[25] = V.Y(); // [4][1]
1664   J_pM_5x6[26] = V.Z(); // [4][2]
1665 
1666 
1667   // do the transformation
1668   // out = J_pM^T * in5x5 * J_pM
1669   const M5x5& in5x5_ = *((M5x5*) state.getCov().GetMatrixArray());
1670   RKTools::J_pMTxcov5xJ_pM(J_pM_5x6, in5x5_, out6x6);
1671 
1672 }
1673 
1674 void RKTrackRep::calcJ_Mp_7x5(M7x5& J_Mp, const TVector3& U, const TVector3& V, const TVector3& W, const M1x3& A) const {
1675 
1676   /*if (debugLvl_ > 1) {
1677     debugOut << "RKTrackRep::calcJ_Mp_7x5 \n";
1678     debugOut << "  U = "; U.Print();
1679     debugOut << "  V = "; V.Print();
1680     debugOut << "  W = "; W.Print();
1681     debugOut << "  A = "; RKTools::printDim(A, 3,1);
1682   }*/
1683 
1684   std::fill(J_Mp.begin(), J_Mp.end(), 0);
1685 
1686   const double AtU = A[0]*U.X() + A[1]*U.Y() + A[2]*U.Z();
1687   const double AtV = A[0]*V.X() + A[1]*V.Y() + A[2]*V.Z();
1688   const double AtW = A[0]*W.X() + A[1]*W.Y() + A[2]*W.Z();
1689 
1690   // J_Mp matrix is d(q/p,u',v',u,v) / d(x,y,z,ax,ay,az,q/p)   (in is 7x7)
1691 
1692   // d(u')/d(ax,ay,az)
1693   double fact = 1./(AtW*AtW);
1694   J_Mp[16] = fact * (U.X()*AtW - W.X()*AtU); // [3][1]
1695   J_Mp[21] = fact * (U.Y()*AtW - W.Y()*AtU); // [4][1]
1696   J_Mp[26] = fact * (U.Z()*AtW - W.Z()*AtU); // [5][1]
1697   // d(v')/d(ax,ay,az)
1698   J_Mp[17] = fact * (V.X()*AtW - W.X()*AtV); // [3][2]
1699   J_Mp[22] = fact * (V.Y()*AtW - W.Y()*AtV); // [4][2]
1700   J_Mp[27] = fact * (V.Z()*AtW - W.Z()*AtV); // [5][2]
1701   // d(q/p)/d(q/p)
1702   J_Mp[30] = 1.; // [6][0]  - not needed for array matrix multiplication
1703   //d(u)/d(x,y,z)
1704   J_Mp[3]  = U.X(); // [0][3]
1705   J_Mp[8]  = U.Y(); // [1][3]
1706   J_Mp[13] = U.Z(); // [2][3]
1707   //d(v)/d(x,y,z)
1708   J_Mp[4]  = V.X(); // [0][4]
1709   J_Mp[9]  = V.Y(); // [1][4]
1710   J_Mp[14] = V.Z(); // [2][4]
1711 
1712   /*if (debugLvl_ > 1) {
1713     debugOut << "  J_Mp_7x5_ = "; RKTools::printDim(J_Mp, 7,5);
1714   }*/
1715 
1716 }
1717 
1718 
1719 void RKTrackRep::transformM6P(const M6x6& in6x6,
1720                               const M1x7& state7,
1721                               MeasuredStateOnPlane& state) const { // plane and charge must already be set!
1722 
1723   // get vectors and aux variables
1724   const TVector3& U(state.getPlane()->getU());
1725   const TVector3& V(state.getPlane()->getV());
1726   const TVector3& W(state.getPlane()->getNormal());
1727 
1728   const double AtU = state7[3]*U.X() + state7[4]*U.Y() + state7[5]*U.Z();
1729   const double AtV = state7[3]*V.X() + state7[4]*V.Y() + state7[5]*V.Z();
1730   const double AtW = state7[3]*W.X() + state7[4]*W.Y() + state7[5]*W.Z();
1731 
1732   // J_Mp matrix is d(q/p,u',v',u,v) / d(x,y,z,px,py,pz)       (in is 6x6)
1733 
1734   const double qop = state7[6];
1735   const double p = getCharge(state)/qop; // momentum
1736 
1737   M6x5 J_Mp_6x5;
1738   std::fill(J_Mp_6x5.begin(), J_Mp_6x5.end(), 0);
1739 
1740   //d(u)/d(x,y,z)
1741   J_Mp_6x5[3]  = U.X(); // [0][3]
1742   J_Mp_6x5[8]  = U.Y(); // [1][3]
1743   J_Mp_6x5[13] = U.Z(); // [2][3]
1744   //d(v)/d(x,y,z)
1745   J_Mp_6x5[4]  = V.X(); // [0][4]
1746   J_Mp_6x5[9]  = V.Y(); // [1][4]
1747   J_Mp_6x5[14] = V.Z(); // [2][4]
1748   // d(q/p)/d(px,py,pz)
1749   double fact = (-1.) * qop / p;
1750   J_Mp_6x5[15] = fact * state7[3]; // [3][0]
1751   J_Mp_6x5[20] = fact * state7[4]; // [4][0]
1752   J_Mp_6x5[25] = fact * state7[5]; // [5][0]
1753   // d(u')/d(px,py,pz)
1754   fact = 1./(p*AtW*AtW);
1755   J_Mp_6x5[16] = fact * (U.X()*AtW - W.X()*AtU); // [3][1]
1756   J_Mp_6x5[21] = fact * (U.Y()*AtW - W.Y()*AtU); // [4][1]
1757   J_Mp_6x5[26] = fact * (U.Z()*AtW - W.Z()*AtU); // [5][1]
1758   // d(v')/d(px,py,pz)
1759   J_Mp_6x5[17] = fact * (V.X()*AtW - W.X()*AtV); // [3][2]
1760   J_Mp_6x5[22] = fact * (V.Y()*AtW - W.Y()*AtV); // [4][2]
1761   J_Mp_6x5[27] = fact * (V.Z()*AtW - W.Z()*AtV); // [5][2]
1762 
1763   // do the transformation
1764   // out5x5 = J_Mp^T * in * J_Mp
1765   M5x5& out5x5_ = *((M5x5*) state.getCov().GetMatrixArray());
1766   RKTools::J_MpTxcov6xJ_Mp(J_Mp_6x5, in6x6, out5x5_);
1767 
1768 }
1769 
1770 
1771 //
1772 // Runge-Kutta method for tracking a particles through a magnetic field.
1773 // Uses Nystroem algorithm (See Handbook Nat. Bur. of Standards, procedure 25.5.20)
1774 // in the way described in
1775 //  E Lund et al 2009 JINST 4 P04001 doi:10.1088/1748-0221/4/04/P04001
1776 //  "Track parameter propagation through the application of a new adaptive Runge-Kutta-Nyström method in the ATLAS experiment"
1777 //  http://inspirehep.net/search?ln=en&ln=en&p=10.1088/1748-0221/4/04/P04001&of=hb&action_search=Search&sf=earliestdate&so=d&rm=&rg=25&sc=0
1778 //
1779 // Input parameters:
1780 //    SU     - plane parameters
1781 //    SU[0]  - direction cosines normal to surface Ex
1782 //    SU[1]  -          -------                    Ey
1783 //    SU[2]  -          -------                    Ez; Ex*Ex+Ey*Ey+Ez*Ez=1
1784 //    SU[3]  - distance to surface from (0,0,0) > 0 cm
1785 //
1786 //    state7 - initial parameters (coordinates(cm), direction,
1787 //             charge/momentum (Gev-1)
1788 //    cov      and derivatives this parameters  (7x7)
1789 //
1790 //    X         Y         Z         Ax        Ay        Az        q/P
1791 //    state7[0] state7[1] state7[2] state7[3] state7[4] state7[5] state7[6]
1792 //
1793 //    dX/dp     dY/dp     dZ/dp     dAx/dp    dAy/dp    dAz/dp    d(q/P)/dp
1794 //    cov[ 0]   cov[ 1]   cov[ 2]   cov[ 3]   cov[ 4]   cov[ 5]   cov[ 6]               d()/dp1
1795 //
1796 //    cov[ 7]   cov[ 8]   cov[ 9]   cov[10]   cov[11]   cov[12]   cov[13]               d()/dp2
1797 //    ............................................................................    d()/dpND
1798 //
1799 // Authors: R.Brun, M.Hansroul, V.Perevoztchikov (Geant3)
1800 //
1801 bool RKTrackRep::RKutta(const M1x4& SU,
1802                         const DetPlane& plane,
1803                         double charge,
1804                         double mass,
1805                         M1x7& state7,
1806                         M7x7* jacobianT,
1807                         M1x7* J_MMT_unprojected_lastRow,
1808                         double& coveredDistance,
1809                         double& flightTime,
1810                         bool& checkJacProj,
1811                         M7x7& noiseProjection,
1812                         StepLimits& limits,
1813                         bool onlyOneStep,
1814                         bool calcOnlyLastRowOfJ) const {
1815 
1816   // limits, check-values, etc. Can be tuned!
1817   static const double Wmax           ( 3000. );           // max. way allowed [cm]
1818   static const double AngleMax       ( 6.3 );           // max. total angle change of momentum. Prevents extrapolating a curler round and round if no active plane is found.
1819   static const double Pmin           ( 4.E-3 );           // minimum momentum for propagation [GeV]
1820   static const unsigned int maxNumIt ( 1000 );    // maximum number of iterations in main loop
1821   // Aux parameters
1822   M1x3&   R          ( *((M1x3*) &state7[0]) );  // Start coordinates  [cm]  (x,  y,  z)
1823   M1x3&   A          ( *((M1x3*) &state7[3]) );  // Start directions         (ax, ay, az);   ax^2+ay^2+az^2=1
1824   M1x3    SA         = {{0.,0.,0.}};             // Start directions derivatives dA/S
1825   double  Way        ( 0. );                     // Sum of absolute values of all extrapolation steps [cm]
1826   double  momentum   ( fabs(charge/state7[6]) ); // momentum [GeV]
1827   double  relMomLoss ( 0 );                      // relative momentum loss in RKutta
1828   double  deltaAngle ( 0. );                     // total angle by which the momentum has changed during extrapolation
1829   // cppcheck-suppress unreadVariable
1830   double  An(0), S(0), Sl(0), CBA(0);
1831 
1832 
1833   if (debugLvl_ > 0) {
1834     debugOut << "RKTrackRep::RKutta \n";
1835     debugOut << "position: "; TVector3(R[0], R[1], R[2]).Print();
1836     debugOut << "direction: "; TVector3(A[0], A[1], A[2]).Print();
1837     debugOut << "momentum: " << momentum << " GeV\n";
1838     debugOut << "destination: "; plane.Print();
1839   }
1840 
1841   checkJacProj = false;
1842 
1843   // check momentum
1844   if(momentum < Pmin){
1845     std::ostringstream sstream;
1846     sstream << "RKTrackRep::RKutta ==> momentum too low: " << momentum*1000. << " MeV";
1847     Exception exc(sstream.str(),__LINE__,__FILE__);
1848     exc.setFatal();
1849     throw exc;
1850   }
1851 
1852   unsigned int counter(0);
1853 
1854   // Step estimation (signed)
1855   S = estimateStep(state7, SU, plane, charge, relMomLoss, limits);
1856 
1857   //
1858   // Main loop of Runge-Kutta method
1859   //
1860   while (fabs(S) >= MINSTEP || counter == 0) {
1861 
1862     if(++counter > maxNumIt){
1863       Exception exc("RKTrackRep::RKutta ==> maximum number of iterations exceeded",__LINE__,__FILE__);
1864       exc.setFatal();
1865       throw exc;
1866     }
1867 
1868     if (debugLvl_ > 0) {
1869       debugOut << "------ RKutta main loop nr. " << counter-1 << " ------\n";
1870     }
1871 
1872     M1x3 ABefore = {{ A[0], A[1], A[2] }};
1873     RKPropagate(state7, jacobianT, SA, S, true, calcOnlyLastRowOfJ); // the actual Runge Kutta propagation
1874 
1875     // update paths
1876     coveredDistance += S;       // add stepsize to way (signed)
1877     Way  += fabs(S);
1878 
1879     double beta = 1/hypot(1, mass*state7[6]/charge);
1880     flightTime += S / beta / 29.9792458; // in ns
1881 
1882     // check way limit
1883     if(Way > Wmax){
1884       std::ostringstream sstream;
1885       sstream << "RKTrackRep::RKutta ==> Total extrapolation length is longer than length limit : " << Way << " cm !";
1886       Exception exc(sstream.str(),__LINE__,__FILE__);
1887       exc.setFatal();
1888       throw exc;
1889     }
1890 
1891     if (onlyOneStep) return(true);
1892 
1893     // if stepsize has been limited by material, break the loop and return. No linear extrapolation!
1894     if (limits.getLowestLimit().first == stp_momLoss) {
1895       if (debugLvl_ > 0) {
1896         debugOut<<" momLossExceeded -> return(true); \n";
1897       }
1898       return(true);
1899     }
1900 
1901     // if stepsize has been limited by material boundary, break the loop and return. No linear extrapolation!
1902     if (limits.getLowestLimit().first == stp_boundary) {
1903       if (debugLvl_ > 0) {
1904         debugOut<<" at boundary -> return(true); \n";
1905       }
1906       return(true);
1907     }
1908 
1909 
1910     // estimate Step for next loop or linear extrapolation
1911     Sl = S; // last S used
1912     limits.removeLimit(stp_fieldCurv);
1913     limits.removeLimit(stp_momLoss);
1914     limits.removeLimit(stp_boundary);
1915     limits.removeLimit(stp_plane);
1916     S = estimateStep(state7, SU, plane, charge, relMomLoss, limits);
1917 
1918     if (limits.getLowestLimit().first == stp_plane &&
1919         fabs(S) < MINSTEP) {
1920       if (debugLvl_ > 0) {
1921         debugOut<<" (at Plane && fabs(S) < MINSTEP) -> break and do linear extrapolation \n";
1922       }
1923       break;
1924     }
1925     if (limits.getLowestLimit().first == stp_momLoss &&
1926         fabs(S) < MINSTEP) {
1927       if (debugLvl_ > 0) {
1928         debugOut<<" (momLossExceeded && fabs(S) < MINSTEP) -> return(true), no linear extrapolation; \n";
1929       }
1930       RKSteps_.erase(RKSteps_.end()-1);
1931       --RKStepsFXStop_;
1932       return(true); // no linear extrapolation!
1933     }
1934 
1935     // check if total angle is bigger than AngleMax. Can happen if a curler should be fitted and it does not hit the active area of the next plane.
1936     double arg = ABefore[0]*A[0] + ABefore[1]*A[1] + ABefore[2]*A[2];
1937     arg = arg > 1 ? 1 : arg;
1938     arg = arg < -1 ? -1 : arg;
1939     deltaAngle += acos(arg);
1940     if (fabs(deltaAngle) > AngleMax){
1941       std::ostringstream sstream;
1942       sstream << "RKTrackRep::RKutta ==> Do not get to an active plane! Already extrapolated " << deltaAngle * 180 / TMath::Pi() << "°.";
1943       Exception exc(sstream.str(),__LINE__,__FILE__);
1944       exc.setFatal();
1945       throw exc;
1946     }
1947 
1948     // check if we went back and forth multiple times -> we don't come closer to the plane!
1949     if (counter > 3){
1950       if (S                            *RKSteps_.at(counter-1).matStep_.stepSize_ < 0 &&
1951           RKSteps_.at(counter-1).matStep_.stepSize_*RKSteps_.at(counter-2).matStep_.stepSize_ < 0 &&
1952           RKSteps_.at(counter-2).matStep_.stepSize_*RKSteps_.at(counter-3).matStep_.stepSize_ < 0){
1953         Exception exc("RKTrackRep::RKutta ==> Do not get closer to plane!",__LINE__,__FILE__);
1954         exc.setFatal();
1955         throw exc;
1956       }
1957     }
1958 
1959   } //end of main loop
1960 
1961 
1962   //
1963   // linear extrapolation to plane
1964   //
1965   if (limits.getLowestLimit().first == stp_plane) {
1966 
1967     if (fabs(Sl) > 0.001*MINSTEP){
1968       if (debugLvl_ > 0) {
1969         debugOut << " RKutta - linear extrapolation to surface\n";
1970       }
1971       Sl = 1./Sl;        // Sl = inverted last Stepsize Sl
1972 
1973       // normalize SA
1974       SA[0]*=Sl;  SA[1]*=Sl;  SA[2]*=Sl; // SA/Sl = delta A / delta way; local derivative of A with respect to the length of the way
1975 
1976       // calculate A
1977       A[0] += SA[0]*S;    // S  = distance to surface
1978       A[1] += SA[1]*S;    // A = A + S * SA*Sl
1979       A[2] += SA[2]*S;
1980 
1981       // normalize A
1982       CBA = 1./sqrt(A[0]*A[0]+A[1]*A[1]+A[2]*A[2]);  // 1/|A|
1983       A[0] *= CBA; A[1] *= CBA; A[2] *= CBA;
1984 
1985       R[0] += S*(A[0]-0.5*S*SA[0]);    // R = R + S*(A - 0.5*S*SA); approximation for final point on surface
1986       R[1] += S*(A[1]-0.5*S*SA[1]);
1987       R[2] += S*(A[2]-0.5*S*SA[2]);
1988 
1989 
1990       coveredDistance += S;
1991       // cppcheck-suppress unreadVariable
1992       Way  += fabs(S);
1993 
1994       double beta = 1/hypot(1, mass*state7[6]/charge);
1995       flightTime += S / beta / 29.9792458; // in ns;
1996     }
1997     else if (debugLvl_ > 0)  {
1998       debugOut << " RKutta - last stepsize too small -> can't do linear extrapolation! \n";
1999     }
2000 
2001     //
2002     // Project Jacobian of extrapolation onto destination plane
2003     //
2004     if (jacobianT != nullptr) {
2005 
2006       // projected jacobianT
2007       // x x x x x x 0
2008       // x x x x x x 0
2009       // x x x x x x 0
2010       // x x x x x x 0
2011       // x x x x x x 0
2012       // x x x x x x 0
2013       // x x x x x x 1
2014 
2015       if (checkJacProj && RKSteps_.size()>0){
2016         Exception exc("RKTrackRep::Extrap ==> covariance is projected onto destination plane again",__LINE__,__FILE__);
2017         throw exc;
2018       }
2019 
2020       if (debugLvl_ > 0) {
2021         //debugOut << "  Jacobian^T of extrapolation before Projection:\n";
2022         //RKTools::printDim(*jacobianT, 7,7);
2023         debugOut << "  Project Jacobian of extrapolation onto destination plane\n";
2024       }
2025       An = A[0]*SU[0] + A[1]*SU[1] + A[2]*SU[2];
2026       An = (fabs(An) > 1.E-7 ? 1./An : 0); // 1/A_normal
2027       double norm;
2028       int i=0;
2029       if (calcOnlyLastRowOfJ)
2030         i = 42;
2031 
2032       double* jacPtr = jacobianT->begin();
2033 
2034       for(unsigned int j=42; j<49; j+=7) {
2035         (*J_MMT_unprojected_lastRow)[j-42] = jacPtr[j];
2036       }
2037 
2038       for(; i<49; i+=7) {
2039         norm = (jacPtr[i]*SU[0] + jacPtr[i+1]*SU[1] + jacPtr[i+2]*SU[2]) * An;  // dR_normal / A_normal
2040         jacPtr[i]   -= norm*A [0];   jacPtr[i+1] -= norm*A [1];   jacPtr[i+2] -= norm*A [2];
2041         jacPtr[i+3] -= norm*SA[0];   jacPtr[i+4] -= norm*SA[1];   jacPtr[i+5] -= norm*SA[2];
2042       }
2043       checkJacProj = true;
2044 
2045 
2046       if (debugLvl_ > 0) {
2047         //debugOut << "  Jacobian^T of extrapolation after Projection:\n";
2048         //RKTools::printDim(*jacobianT, 7,7);
2049       }
2050 
2051       if (!calcOnlyLastRowOfJ) {
2052         for (int iRow = 0; iRow < 3; ++iRow) {
2053           for (int iCol = 0; iCol < 3; ++iCol) {
2054             noiseProjection[iRow*7 + iCol]       = (iRow == iCol) - An * SU[iCol] * A[iRow];
2055             noiseProjection[(iRow + 3)*7 + iCol] =                - An * SU[iCol] * SA[iRow];
2056           }
2057         }
2058 
2059         // noiseProjection will look like this:
2060         // x x x 0 0 0 0
2061         // x x x 0 0 0 0
2062         // x x x 0 0 0 0
2063         // x x x 1 0 0 0
2064         // x x x 0 1 0 0
2065         // x x x 0 0 1 0
2066         // 0 0 0 0 0 0 1
2067       }
2068 
2069     }
2070   } // end of linear extrapolation to surface
2071 
2072   return(true);
2073 
2074 }
2075 
2076 
2077 double RKTrackRep::estimateStep(const M1x7& state7,
2078                                 const M1x4& SU,
2079                                 const DetPlane& plane,
2080                                 const double& charge,
2081                                 double& relMomLoss,
2082                                 StepLimits& limits) const {
2083 
2084   if (useCache_) {
2085     if (cachePos_ >= RKSteps_.size()) {
2086       useCache_ = false;
2087     }
2088     else {
2089       if (RKSteps_.at(cachePos_).limits_.getLowestLimit().first == stp_plane) {
2090         // we need to step exactly to the plane, so don't use the cache!
2091         useCache_ = false;
2092         RKSteps_.erase(RKSteps_.begin() + cachePos_, RKSteps_.end());
2093       }
2094       else {
2095         if (debugLvl_ > 0) {
2096           debugOut << " RKTrackRep::estimateStep: use stepSize " << cachePos_ << " from cache: " << RKSteps_.at(cachePos_).matStep_.stepSize_ << "\n";
2097         }
2098         //for(int n = 0; n < 1*7; ++n) RKSteps_[cachePos_].state7_[n] = state7[n];
2099         ++RKStepsFXStop_;
2100         limits = RKSteps_.at(cachePos_).limits_;
2101         return RKSteps_.at(cachePos_++).matStep_.stepSize_;
2102       }
2103     }
2104   }
2105 
2106   limits.setLimit(stp_sMax, 25.); // max. step allowed [cm]
2107 
2108   if (debugLvl_ > 0) {
2109     debugOut << " RKTrackRep::estimateStep \n";
2110     debugOut << "  position:  "; TVector3(state7[0], state7[1], state7[2]).Print();
2111     debugOut << "  direction: "; TVector3(state7[3], state7[4], state7[5]).Print();
2112   }
2113 
2114   // calculate SL distance to surface
2115   double Dist ( SU[3] - (state7[0]*SU[0] +
2116                          state7[1]*SU[1] +
2117                          state7[2]*SU[2])  );  // Distance between start coordinates and surface
2118   double An ( state7[3]*SU[0] +
2119               state7[4]*SU[1] +
2120               state7[5]*SU[2]   );              // An = dir * N;  component of dir normal to surface
2121 
2122   double SLDist; // signed
2123   if (fabs(An) > 1.E-10)
2124     SLDist = Dist/An;
2125   else {
2126     SLDist = Dist*1.E10;
2127     if (An<0) SLDist *= -1.;
2128   }
2129 
2130   limits.setLimit(stp_plane, SLDist);
2131   limits.setStepSign(SLDist);
2132 
2133   if (debugLvl_ > 0) {
2134     debugOut << "  Distance to plane: " << Dist << "\n";
2135     debugOut << "  SL distance to plane: " << SLDist << "\n";
2136     if (limits.getStepSign()>0) 
2137       debugOut << "  Direction is  pointing towards surface.\n";
2138     else  
2139       debugOut << "  Direction is pointing away from surface.\n";
2140   }
2141   // DONE calculate SL distance to surface
2142 
2143   //
2144   // Limit according to curvature and magnetic field inhomogenities
2145   // and improve stepsize estimation to reach plane
2146   //
2147   double fieldCurvLimit( limits.getLowestLimitSignedVal() ); // signed
2148   std::pair<double, double> distVsStep (9.E99, 9.E99); // first: smallest straight line distances to plane; second: RK steps
2149 
2150   static const unsigned int maxNumIt = 10;
2151   unsigned int counter(0);
2152 
2153   while (fabs(fieldCurvLimit) > MINSTEP) {
2154 
2155     if(++counter > maxNumIt){
2156       // if max iterations are reached, take a safe value
2157       // (in previous iteration, fieldCurvLimit has been not more than doubled)
2158       // and break.
2159       fieldCurvLimit *= 0.5;
2160       break;
2161     }
2162 
2163     M1x7 state7_temp = state7;
2164     M1x3 SA = {{0, 0, 0}};
2165 
2166     double q ( RKPropagate(state7_temp, nullptr, SA, fieldCurvLimit, true) );
2167     if (debugLvl_ > 0) {
2168       debugOut << "  maxStepArg = " << fieldCurvLimit << "; q = " << q  << " \n";
2169     }
2170 
2171     // remember steps and resulting SL distances to plane for stepsize improvement
2172     // calculate distance to surface
2173     Dist = SU[3] - (state7_temp[0] * SU[0] +
2174                     state7_temp[1] * SU[1] +
2175                     state7_temp[2] * SU[2]); // Distance between position and surface
2176 
2177     An = state7_temp[3] * SU[0] +
2178          state7_temp[4] * SU[1] +
2179          state7_temp[5] * SU[2];    // An = dir * N;  component of dir normal to surface
2180 
2181     if (fabs(Dist/An) < fabs(distVsStep.first)) {
2182       distVsStep.first = Dist/An;
2183       distVsStep.second = fieldCurvLimit;
2184     }
2185 
2186     // resize limit according to q never grow step size more than
2187     // two-fold to avoid infinite grow-shrink loops with strongly
2188     // inhomogeneous fields.
2189     if (q>2) {
2190       fieldCurvLimit *= 2;
2191       break;
2192     }
2193 
2194     fieldCurvLimit *= q * 0.95;
2195 
2196     if (fabs(q-1) < 0.25 || // good enough!
2197         fabs(fieldCurvLimit) > limits.getLowestLimitVal()) // other limits are lower!
2198       break;
2199   }
2200   if (fabs(fieldCurvLimit) < MINSTEP)
2201     limits.setLimit(stp_fieldCurv, MINSTEP);
2202   else
2203     limits.setLimit(stp_fieldCurv, fieldCurvLimit);
2204 
2205   double stepToPlane(limits.getLimitSigned(stp_plane));
2206   if (fabs(distVsStep.first) < 8.E99) {
2207     stepToPlane = distVsStep.first + distVsStep.second;
2208   }
2209   limits.setLimit(stp_plane, stepToPlane);
2210 
2211 
2212   //
2213   // Select direction
2214   //
2215   // auto select
2216   if (propDir_ == 0 || !plane.isFinite()){
2217     if (debugLvl_ > 0) {
2218       debugOut << "  auto select direction";
2219       if (!plane.isFinite()) debugOut << ", plane is not finite";
2220       debugOut << ".\n";
2221     }
2222   }
2223   // see if straight line approximation is ok
2224   else if ( limits.getLimit(stp_plane) < 0.2*limits.getLimit(stp_fieldCurv) ){
2225     if (debugLvl_ > 0) {
2226       debugOut << "  straight line approximation is fine.\n";
2227     }
2228 
2229     // if direction is pointing to active part of surface
2230     if( plane.isInActive(state7[0], state7[1], state7[2],  state7[3], state7[4], state7[5]) ) {
2231       if (debugLvl_ > 0) {
2232         debugOut << "  direction is pointing to active part of surface. \n";
2233       }
2234     }
2235     // if we are near the plane, but not pointing to the active area, make a big step!
2236     else {
2237       limits.removeLimit(stp_plane);
2238       limits.setStepSign(propDir_);
2239       if (debugLvl_ > 0) {
2240         debugOut << "  we are near the plane, but not pointing to the active area. make a big step! \n";
2241       }
2242     }
2243   }
2244   // propDir_ is set and we are not pointing to an active part of a plane -> propDir_ decides!
2245   else {
2246     if (limits.getStepSign() * propDir_ < 0){
2247       limits.removeLimit(stp_plane);
2248       limits.setStepSign(propDir_);
2249       if (debugLvl_ > 0) {
2250         debugOut << "  invert Step according to propDir_ and make a big step. \n";
2251       }
2252     }
2253   }
2254 
2255 
2256   // call stepper and reduce stepsize if step not too small
2257   static const RKStep defaultRKStep;
2258   RKSteps_.push_back( defaultRKStep );
2259   std::vector<RKStep>::iterator lastStep = RKSteps_.end() - 1;
2260   lastStep->state7_ = state7;
2261   ++RKStepsFXStop_;
2262 
2263   if(limits.getLowestLimitVal() > MINSTEP){ // only call stepper if step estimation big enough
2264     M1x7 state7_temp = {{ state7[0], state7[1], state7[2], state7[3], state7[4], state7[5], state7[6] }};
2265 
2266     MaterialEffects::getInstance()->stepper(this,
2267                                             state7_temp,
2268                                             charge/state7[6], // |p|
2269                                             relMomLoss,
2270                                             pdgCode_,
2271                                             lastStep->matStep_.material_,
2272                                             limits,
2273                                             true);
2274   } else { //assume material has not changed
2275     if  (RKSteps_.size()>1) {
2276       lastStep->matStep_.material_ = (lastStep - 1)->matStep_.material_;
2277     }
2278   }
2279 
2280   if (debugLvl_ > 0) {
2281     debugOut << "   final limits:\n";
2282     limits.Print();
2283   }
2284 
2285   double finalStep = limits.getLowestLimitSignedVal();
2286 
2287   lastStep->matStep_.stepSize_ = finalStep;
2288   lastStep->limits_ = limits;
2289 
2290   if (debugLvl_ > 0) {
2291     debugOut << "  --> Step to be used: " << finalStep << "\n";
2292   }
2293 
2294   return finalStep;
2295 
2296 }
2297 
2298 
2299 TVector3 RKTrackRep::pocaOnLine(const TVector3& linePoint, const TVector3& lineDirection, const TVector3& point) const {
2300 
2301   TVector3 retVal(lineDirection);
2302 
2303   double t = 1./(retVal.Mag2()) * ((point*retVal) - (linePoint*retVal));
2304   retVal *= t;
2305   retVal += linePoint;
2306   return retVal; // = linePoint + t*lineDirection
2307 
2308 }
2309 
2310 
2311 double RKTrackRep::Extrap(const DetPlane& startPlane,
2312                           const DetPlane& destPlane,
2313                           double charge,
2314                           double mass,
2315                           bool& isAtBoundary,
2316                           M1x7& state7,
2317                           double& flightTime,
2318                           bool fillExtrapSteps,
2319                           TMatrixDSym* cov, // 5D
2320                           bool onlyOneStep,
2321                           bool stopAtBoundary,
2322                           double maxStep) const
2323 {
2324 
2325   static const unsigned int maxNumIt(500);
2326   unsigned int numIt(0);
2327 
2328   double coveredDistance(0.);
2329   // cppcheck-suppress unreadVariable
2330   double dqop(0.);
2331 
2332   const TVector3 W(destPlane.getNormal());
2333   M1x4 SU = {{W.X(), W.Y(), W.Z(), destPlane.distance(0., 0., 0.)}};
2334 
2335   // make SU vector point away from origin
2336   if (W*destPlane.getO() < 0) {
2337     SU[0] *= -1;
2338     SU[1] *= -1;
2339     SU[2] *= -1;
2340   }
2341 
2342 
2343   M1x7 startState7 = state7;
2344 
2345   while(true){
2346 
2347     if (debugLvl_ > 0) {
2348       debugOut << "\n============ RKTrackRep::Extrap loop nr. " << numIt << " ============\n";
2349       debugOut << "Start plane: "; startPlane.Print();
2350       debugOut << "fillExtrapSteps " << fillExtrapSteps << "\n";
2351     }
2352 
2353     if(++numIt > maxNumIt){
2354       Exception exc("RKTrackRep::Extrap ==> maximum number of iterations exceeded",__LINE__,__FILE__);
2355       exc.setFatal();
2356       throw exc;
2357     }
2358 
2359     // initialize jacobianT with unit matrix
2360     for(int i = 0; i < 7*7; ++i) J_MMT_[i] = 0;
2361     for(int i=0; i<7; ++i) J_MMT_[8*i] = 1.;
2362 
2363     M7x7* noise = nullptr;
2364     isAtBoundary = false;
2365 
2366     // propagation
2367     bool checkJacProj = false;
2368     limits_.reset();
2369     limits_.setLimit(stp_sMaxArg, maxStep-fabs(coveredDistance));
2370 
2371     M1x7 J_MMT_unprojected_lastRow = {{0, 0, 0, 0, 0, 0, 1}};
2372 
2373     if( ! RKutta(SU, destPlane, charge, mass, state7, &J_MMT_, &J_MMT_unprojected_lastRow,
2374          coveredDistance, flightTime, checkJacProj, noiseProjection_,
2375          limits_, onlyOneStep, !fillExtrapSteps) ) {
2376       Exception exc("RKTrackRep::Extrap ==> Runge Kutta propagation failed",__LINE__,__FILE__);
2377       exc.setFatal();
2378       throw exc;
2379     }
2380 
2381     bool atPlane(limits_.getLowestLimit().first == stp_plane);
2382     if (limits_.getLowestLimit().first == stp_boundary)
2383       isAtBoundary = true;
2384 
2385 
2386     if (debugLvl_ > 0) {
2387       debugOut<<"RKSteps \n";
2388       for (std::vector<RKStep>::iterator it = RKSteps_.begin(); it != RKSteps_.end(); ++it){
2389         debugOut << "stepSize = " << it->matStep_.stepSize_ << "\t";
2390         it->matStep_.material_.Print();
2391       }
2392       debugOut<<"\n";
2393     }
2394 
2395 
2396 
2397     // call MatFX
2398     if(fillExtrapSteps) {
2399       noise = &noiseArray_;
2400       for(int i = 0; i < 7*7; ++i) noiseArray_[i] = 0; // set noiseArray_ to 0
2401     }
2402 
2403     unsigned int nPoints(RKStepsFXStop_ - RKStepsFXStart_);
2404     if (/*!fNoMaterial &&*/ nPoints>0){
2405       // momLoss has a sign - negative loss means momentum gain
2406       double momLoss = MaterialEffects::getInstance()->effects(RKSteps_,
2407                                                                RKStepsFXStart_,
2408                                                                RKStepsFXStop_,
2409                                                                fabs(charge/state7[6]), // momentum
2410                                                                pdgCode_,
2411                                                                noise);
2412 
2413       RKStepsFXStart_ = RKStepsFXStop_;
2414 
2415       if (debugLvl_ > 0) {
2416         debugOut << "momLoss: " << momLoss << " GeV; relative: " << momLoss/fabs(charge/state7[6])
2417             << "; coveredDistance = " << coveredDistance << "\n";
2418         if (debugLvl_ > 1 && noise != nullptr) {
2419           debugOut << "7D noise: \n";
2420           RKTools::printDim(noise->begin(), 7, 7);
2421         }
2422       }
2423 
2424       // do momLoss only for defined 1/momentum .ne.0
2425       if(fabs(state7[6])>1.E-10) {
2426 
2427         if (debugLvl_ > 0) {
2428           debugOut << "correct state7 with dx/dqop, dy/dqop ...\n";
2429         }
2430 
2431         dqop = charge/(fabs(charge/state7[6])-momLoss) - state7[6];
2432 
2433         // Correct coveredDistance and flightTime and momLoss if checkJacProj == true
2434         // The idea is to calculate the state correction (based on the mometum loss) twice:
2435         // Once with the unprojected Jacobian (which preserves coveredDistance),
2436         // and once with the projected Jacobian (which is constrained to the plane and does NOT preserve coveredDistance).
2437         // The difference of these two corrections can then be used to calculate a correction factor.
2438         if (checkJacProj && fabs(coveredDistance) > MINSTEP) {
2439           M1x3 state7_correction_unprojected = {{0, 0, 0}};
2440           for (unsigned int i=0; i<3; ++i) {
2441             state7_correction_unprojected[i] = 0.5 * dqop * J_MMT_unprojected_lastRow[i];
2442             //debugOut << "J_MMT_unprojected_lastRow[i] " << J_MMT_unprojected_lastRow[i] << "\n";
2443             //debugOut << "state7_correction_unprojected[i] " << state7_correction_unprojected[i] << "\n";
2444           }
2445 
2446           M1x3 state7_correction_projected = {{0, 0, 0}};
2447           for (unsigned int i=0; i<3; ++i) {
2448             state7_correction_projected[i] = 0.5 * dqop * J_MMT_[6*7 + i];
2449             //debugOut << "J_MMT_[6*7 + i] " << J_MMT_[6*7 + i] << "\n";
2450             //debugOut << "state7_correction_projected[i] " << state7_correction_projected[i] << "\n";
2451           }
2452 
2453           // delta distance
2454           M1x3 delta_state = {{0, 0, 0}};
2455           for (unsigned int i=0; i<3; ++i) {
2456             delta_state[i] = state7_correction_unprojected[i] - state7_correction_projected[i];
2457           }
2458 
2459           double Dist( sqrt(delta_state[0]*delta_state[0]
2460               + delta_state[1]*delta_state[1]
2461               + delta_state[2]*delta_state[2] )  );
2462 
2463           // sign: delta * a
2464           if (delta_state[0]*state7[3] + delta_state[1]*state7[4] + delta_state[2]*state7[5] > 0)
2465             Dist *= -1.;
2466 
2467           double correctionFactor( 1. + Dist / coveredDistance );
2468           flightTime *= correctionFactor;
2469           momLoss *= correctionFactor;
2470           coveredDistance = coveredDistance + Dist;
2471 
2472           if (debugLvl_ > 0) {
2473             debugOut << "correctionFactor-1 = " << correctionFactor-1. << "; Dist = " << Dist << "\n";
2474             debugOut << "corrected momLoss: " << momLoss << " GeV; relative: " << momLoss/fabs(charge/state7[6])
2475                 << "; corrected coveredDistance = " << coveredDistance << "\n";
2476           }
2477         }
2478 
2479         // correct state7 with dx/dqop, dy/dqop ... Greatly improves extrapolation accuracy!
2480         double qop( charge/(fabs(charge/state7[6])-momLoss) );
2481         dqop = qop - state7[6];
2482         state7[6] = qop;
2483 
2484         for (unsigned int i=0; i<6; ++i) {
2485           state7[i] += 0.5 * dqop * J_MMT_[6*7 + i];
2486         }
2487         // normalize direction, just to make sure
2488         double norm( 1. / sqrt(state7[3]*state7[3] + state7[4]*state7[4] + state7[5]*state7[5]) );
2489         for (unsigned int i=3; i<6; ++i)
2490           state7[i] *= norm;
2491 
2492       }
2493     } // finished MatFX
2494 
2495 
2496     // fill ExtrapSteps_
2497     if (fillExtrapSteps) {
2498       static const ExtrapStep defaultExtrapStep;
2499       ExtrapSteps_.push_back(defaultExtrapStep);
2500       std::vector<ExtrapStep>::iterator lastStep = ExtrapSteps_.end() - 1;
2501 
2502       // Store Jacobian of this step for final calculation.
2503       lastStep->jac7_ = J_MMT_;
2504 
2505       if( checkJacProj == true ){
2506         //project the noise onto the destPlane
2507         RKTools::Np_N_NpT(noiseProjection_, noiseArray_);
2508 
2509         if (debugLvl_ > 1) {
2510           debugOut << "7D noise projected onto plane: \n";
2511           RKTools::printDim(noiseArray_.begin(), 7, 7);
2512         }
2513       }
2514 
2515       // Store this step's noise for final calculation.
2516       lastStep->noise7_ = noiseArray_;
2517 
2518       if (debugLvl_ > 2) {
2519         debugOut<<"ExtrapSteps \n";
2520         for (std::vector<ExtrapStep>::iterator it = ExtrapSteps_.begin(); it != ExtrapSteps_.end(); ++it){
2521           debugOut << "7D Jacobian: "; RKTools::printDim((it->jac7_.begin()), 5,5);
2522           debugOut << "7D noise:    "; RKTools::printDim((it->noise7_.begin()), 5,5);
2523         }
2524         debugOut<<"\n";
2525       }
2526     }
2527 
2528 
2529 
2530     // check if at boundary
2531     if (stopAtBoundary and isAtBoundary) {
2532       if (debugLvl_ > 0) {
2533         debugOut << "stopAtBoundary -> break; \n ";
2534       }
2535       break;
2536     }
2537 
2538     if (onlyOneStep) {
2539       if (debugLvl_ > 0) {
2540         debugOut << "onlyOneStep -> break; \n ";
2541       }
2542       break;
2543     }
2544 
2545     //break if we arrived at destPlane
2546     if(atPlane) {
2547       if (debugLvl_ > 0) {
2548         debugOut << "arrived at destPlane with a distance of  " << destPlane.distance(state7[0], state7[1], state7[2]) << " cm left. ";
2549         if (destPlane.isInActive(state7[0], state7[1], state7[2],  state7[3], state7[4], state7[5]))
2550           debugOut << "In active area of destPlane. \n";
2551         else
2552           debugOut << "NOT in active area of plane. \n";
2553 
2554         debugOut << "  position:  "; TVector3(state7[0], state7[1], state7[2]).Print();
2555         debugOut << "  direction: "; TVector3(state7[3], state7[4], state7[5]).Print();
2556       }
2557       break;
2558     }
2559 
2560   }
2561 
2562   if (fillExtrapSteps) {
2563     // propagate cov and add noise
2564     calcForwardJacobianAndNoise(startState7, startPlane, state7, destPlane);
2565 
2566     if (cov != nullptr) {
2567       cov->Similarity(fJacobian_);
2568       *cov += fNoise_;
2569     }
2570 
2571     if (debugLvl_ > 0) {
2572       if (cov != nullptr) {
2573         debugOut << "final covariance matrix after Extrap: "; cov->Print();
2574       }
2575     }
2576   }
2577 
2578   return coveredDistance;
2579 }
2580 
2581 
2582 void RKTrackRep::checkCache(const StateOnPlane& state, const SharedPlanePtr* plane) const {
2583 
2584   if (state.getRep() != this){
2585     Exception exc("RKTrackRep::checkCache ==> state is defined wrt. another TrackRep",__LINE__,__FILE__);
2586     exc.setFatal();
2587     throw exc;
2588   }
2589 
2590   if (dynamic_cast<const MeasurementOnPlane*>(&state) != nullptr) {
2591     Exception exc("RKTrackRep::checkCache - cannot extrapolate MeasurementOnPlane",__LINE__,__FILE__);
2592     exc.setFatal();
2593     throw exc;
2594   }
2595 
2596   cachePos_ = 0;
2597   RKStepsFXStart_ = 0;
2598   RKStepsFXStop_ = 0;
2599   ExtrapSteps_.clear();
2600   initArrays();
2601 
2602 
2603   if (plane &&
2604       lastStartState_.getPlane() &&
2605       lastEndState_.getPlane() &&
2606       state.getPlane() == lastStartState_.getPlane() &&
2607       state.getState() == lastStartState_.getState() &&
2608       (*plane)->distance(getPos(lastEndState_)) <= MINSTEP) {
2609     useCache_ = true;
2610 
2611     // clean up cache. Only use steps with same sign.
2612     double firstStep(0);
2613     for (unsigned int i=0; i<RKSteps_.size(); ++i) {
2614       if (i == 0) {
2615         firstStep = RKSteps_.at(0).matStep_.stepSize_;
2616         continue;
2617       }
2618       if (RKSteps_.at(i).matStep_.stepSize_ * firstStep < 0) {
2619         if (RKSteps_.at(i-1).matStep_.material_ == RKSteps_.at(i).matStep_.material_) {
2620           RKSteps_.at(i-1).matStep_.stepSize_ += RKSteps_.at(i).matStep_.stepSize_;
2621         }
2622         RKSteps_.erase(RKSteps_.begin()+i, RKSteps_.end());
2623       }
2624     }
2625 
2626     if (debugLvl_ > 0) {
2627         debugOut << "RKTrackRep::checkCache: use cached material and step values.\n";
2628     }
2629   }
2630   else {
2631 
2632     if (debugLvl_ > 0) {
2633       debugOut << "RKTrackRep::checkCache: can NOT use cached material and step values.\n";
2634 
2635       if (plane != nullptr) {
2636         if (state.getPlane() != lastStartState_.getPlane()) {
2637           debugOut << "state.getPlane() != lastStartState_.getPlane()\n";
2638         }
2639         else {
2640           if (! (state.getState() == lastStartState_.getState())) {
2641             debugOut << "state.getState() != lastStartState_.getState()\n";
2642           }
2643           else if (lastEndState_.getPlane().get() != nullptr) {
2644             debugOut << "distance " << (*plane)->distance(getPos(lastEndState_)) << "\n";
2645           }
2646         }
2647       }
2648     }
2649 
2650     useCache_ = false;
2651     RKSteps_.clear();
2652 
2653     lastStartState_.setStatePlane(state.getState(), state.getPlane());
2654   }
2655 }
2656 
2657 
2658 double RKTrackRep::momMag(const M1x7& state7) const {
2659   // FIXME given this interface this function cannot work for charge =/= +-1
2660   return fabs(1/state7[6]);
2661 }
2662 
2663 
2664 bool RKTrackRep::isSameType(const AbsTrackRep* other) {
2665   if (dynamic_cast<const RKTrackRep*>(other) == nullptr)
2666     return false;
2667 
2668   return true;
2669 }
2670 
2671 
2672 bool RKTrackRep::isSame(const AbsTrackRep* other) {
2673   if (getPDG() != other->getPDG())
2674     return false;
2675 
2676   return isSameType(other);
2677 }
2678 
2679 
2680 void RKTrackRep::Streamer(TBuffer &R__b)
2681 {
2682    // Stream an object of class genfit::RKTrackRep.
2683 
2684    //This works around a msvc bug and should be harmless on other platforms
2685    typedef ::genfit::RKTrackRep thisClass;
2686    UInt_t R__s, R__c;
2687    if (R__b.IsReading()) {
2688       ::genfit::AbsTrackRep::Streamer(R__b);
2689       Version_t R__v = R__b.ReadVersion(&R__s, &R__c); if (R__v) { }
2690       R__b.CheckByteCount(R__s, R__c, thisClass::IsA());
2691       lastStartState_.setRep(this);
2692       lastEndState_.setRep(this);
2693    } else {
2694       ::genfit::AbsTrackRep::Streamer(R__b);
2695       R__c = R__b.WriteVersion(thisClass::IsA(), kTRUE);
2696       R__b.SetByteCount(R__c, kTRUE);
2697    }
2698 }
2699 
2700 } /* End of namespace genfit */