Print

Print


Commit in trf++ on MAIN
include/trfxyp/PropXYXYRK.h+105added 1.1
src/trfxyp/PropXYXYRK.cpp+938added 1.1
+1043
2 added files
RungeKutta Propagators

trf++/include/trfxyp
PropXYXYRK.h added at 1.1
diff -N PropXYXYRK.h
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ PropXYXYRK.h	29 Jul 2011 00:09:17 -0000	1.1
@@ -0,0 +1,105 @@
+// PropXYXYRK.h
+
+#ifndef PropXYXYRK_H
+#define PropXYXYRK_H
+
+// Propagates tracks from one xy-plane to another in a general magnetic 
+// field.  Solve equations of motion using fourth order Runge-Kutta method.
+//
+// Propagation will fail if either the origin or destination is 
+// not a xy-plane.
+//
+// ObjStream example:
+//
+// [ myprop PropXYXYRK type="mc" precision=1.e-7 derivprec=1.e-7 ]
+//
+// Parameter type can be "mc", "tosca", "D0", "constant", or the name
+// of an obs object.
+//
+// Optional parameter precision specifies the target relative precision
+// for the track parameters (default value 1-e.7).
+//
+// Optional parameter derivprec specifies the target relative precision
+// for derivatives (which contribute to propagation of errors).
+//
+// The optional parameter dir may be used to specify the default
+// direction for propagation.  It must have one of the following
+// values: "forward", "backward" or "nearest".  E.g.
+// [ myprop PropCylRK type="constant" dir="forward" ]
+//
+// Optional parameters scale1 and scale2 may be used to control the 
+// strength and sign of the solenoid and toroid (for type "mc") or 
+// scale1 may be used to control the overall strength and sign (for type
+// "constant" and "mag".  Scale factor scale1=1. always corresponds to
+// a 2 Tesla magnetic field.
+
+#include "trfbase/PropDirected.h"
+#include "mag_field/AbstractMagneticField.hpp"
+//#include "trfbase/ObjMagFieldPtr.h"
+
+namespace trf {
+
+class PropXYXYRK : public PropDirected {
+
+public:  // static methods
+
+  // Return the type name.
+  static TypeName get_type_name() { return "PropXYXYRK"; }
+
+  // Return the creator.
+  static ObjCreator get_creator();
+
+  // Return the type.
+  static Type get_static_type() { return get_creator(); }
+
+private:  // attributes
+
+  // The field.
+  //ObjMagFieldPtr _bfield_map;
+  const AbstractMagneticField* _bfield;
+
+  // Construction data (save for methods write_data and ostr).
+  std::string _type;
+  double _scale1;
+  double _scale2;
+  double _precision;
+  double _derivprec;
+
+private:
+
+  // output stream
+  void ostr(std::ostream& stream) const;
+
+public:
+
+  // constructor for a general megnetic field.
+  PropXYXYRK(const std::string& type, double precision=1.e-7, 
+	     double scale1=1., double scale2=1., double derivprec=1.e-7);
+
+  // destructor
+  ~PropXYXYRK();
+
+  // Clone.
+  Propagator* new_propagator( ) const;
+
+  // Return the type.
+  Type get_type() const { return get_static_type(); }
+
+  // Write the object data.
+  ObjData write_data() const;
+
+  // propagate a track without error in the specified direction
+  PropStat
+  vec_dir_prop(VTrack& trv, const Surface& srf, PropDir dir,
+               TrackDerivative* pder =0) const;
+
+  // return Bz at the origin.
+  double get_bfield() const;
+
+  // return the magnetic field map.
+  //const ObjMagFieldPtr get_bfield_map() const {return _bfield_map;}
+};
+
+}  // end namespace trf
+
+#endif

trf++/src/trfxyp
PropXYXYRK.cpp added at 1.1
diff -N PropXYXYRK.cpp
--- /dev/null	1 Jan 1970 00:00:00 -0000
+++ PropXYXYRK.cpp	29 Jul 2011 00:09:17 -0000	1.1
@@ -0,0 +1,938 @@
+// PropXYXYRK.cpp
+
+#include "trfxyp/PropXYXYRK.h"
+#include <iostream>
+#include <cassert>
+#include <fstream>
+#include <stdexcept>
+#include "objstream/ObjData.hpp"
+#include "objstream/ObjTable.hpp"
+#include "trfutil/trfstream.h"
+#include "trfbase/PropStat.h"
+#include "trfutil/TRFMath.h"
+#include "trfxyp/SurfXYPlane.h"
+#include "trfbase/VTrack.h"
+#include "trfbase/ETrack.h"
+#include "mag_field/ConstantMagneticField.hpp"
+#include "mag_field/MCField.hpp"
+//#include "mag_field/ToscaField.hpp"
+#include "static_la/array.h"
+//#include "trfbase/MagFieldCacher.h"
+
+using std::cerr;
+using std::endl;
+using std::ifstream;
+using std::runtime_error;
+//using mgf::MagFieldPtr;
+
+#ifndef DEFECT_CMATH_NOT_STD
+using std::sqrt;
+using std::cos;
+using std::sin;
+using std::atan2;
+using std::asin;
+using std::abs;
+using std::max;
+using std::pow;
+#endif
+
+using trf::PI;
+using trf::TWOPI;
+using trf::PI2;
+using trf::BFAC;
+using trf::fmod2;
+using trf::Surface;
+using trf::TrackVector;
+using trf::TrackDerivative;
+using trf::VTrack;
+using trf::ETrack;
+using trf::PropStat;
+using trf::Propagator;
+using trf::SurfXYPlane;
+using trf::PropXYXYRK;
+//using trf::MagFieldCacher;
+using static_la::array;
+
+//**********************************************************************
+// Free functions.
+//**********************************************************************
+
+namespace {
+
+  // Assign track parameter indices.
+  enum {
+    IV = SurfXYPlane::IV,
+    IZ   = SurfXYPlane::IZ,
+    IDVDU = SurfXYPlane::IDVDU,
+    IDZDU = SurfXYPlane::IDZDU,
+    IQP  = SurfXYPlane::IQP
+  };
+
+  typedef array<double,25> TrackPar;
+
+  // Creator.
+
+  ObjPtr create(const ObjData& data) {
+    assert( data.get_object_type() == "PropXYXYRK" );
+    string type;
+    if(data.has("type"))
+      type = data.get_string("type");
+    double precision=1.e-7;
+    if(data.has("precision"))
+      precision=data.get_double("precision");
+    double derivprec=precision;
+    if(data.has("derivprec"))
+      derivprec=data.get_double("derivprec");
+    double scale1 = 1.;
+    if(data.has("scale1"))
+      scale1 = data.get_double("scale1");
+    double scale2 = 1.;
+    if(data.has("scale2"))
+      scale1 = data.get_double("scale2");
+    Ptr<PropXYXYRK,LocalSharedDeletePolicy>
+      pobj(new PropXYXYRK(type, precision, scale1, scale2, derivprec));
+    pobj->set_direction(data);
+    return pobj;
+  }
+
+  // Equations of motion in terms of track parameters v, z, dvdu, dzdu,
+  // curvature (q/p), and path length and the magnetic field.
+  
+  TrackPar motion(const TrackPar& par, double u, double sinphi, double cosphi,
+		  double crv, double sign_du, 
+		  const AbstractMagneticField* bfield) {
+
+    TrackPar result(par.get_size());
+    bool deriv = par.get_size() > 5;
+
+    // Extract track parameters.
+
+    double v = par(0);
+    double z = par(1);
+    double vv = par(2);
+    double zv = par(3);
+    double s = par(4);
+    double x = u*cosphi - v*sinphi;
+    double y = u*sinphi + v*cosphi;
+
+    // Declare derivative matrix.
+
+    double dvdv0;
+    double dvdz0;
+    double dvdvv0;
+    double dvdzv0;
+    double dvdcrv;
+
+    double dzdv0;
+    double dzdz0;
+    double dzdvv0;
+    double dzdzv0;
+    double dzdcrv;
+
+    double dvvdv0;
+    double dvvdz0;
+    double dvvdvv0;
+    double dvvdzv0;
+    double dvvdcrv;
+
+    double dzvdv0;
+    double dzvdz0;
+    double dzvdvv0;
+    double dzvdzv0;
+    double dzvdcrv;
+
+    if(deriv) {
+
+      // Extract derivative matrix.
+
+      dvdv0 = par(5);
+      dvdz0 = par(6);
+      dvdvv0 = par(7);
+      dvdzv0 = par(8);
+      dvdcrv = par(9);
+
+      dzdv0 = par(10);
+      dzdz0 = par(11);
+      dzdvv0 = par(12);
+      dzdzv0 = par(13);
+      dzdcrv = par(14);
+
+      dvvdv0 = par(15);
+      dvvdz0 = par(16);
+      dvvdvv0 = par(17);
+      dvvdzv0 = par(18);
+      dvvdcrv = par(19);
+
+      dzvdv0 = par(20);
+      dzvdz0 = par(21);
+      dzvdvv0 = par(22);
+      dzvdzv0 = par(23);
+      dzvdcrv = par(24);
+    }
+    double dmax = 1.e30;
+    if(!(vv < dmax && vv > -dmax))
+      throw runtime_error("Floating point exception");
+    if(!(zv < dmax && zv > -dmax))
+      throw runtime_error("Floating point exception");
+    double dsdu2 = 1. + vv*vv + zv*zv;
+    double dsdu = sign_du * sqrt(dsdu2);
+
+    // Get the rotated Cartesian components of magnetic field & derivatives.
+
+    double bu;
+    double bv;
+    double bz;
+    double dbudv;
+    double dbudz;
+    double dbvdv;
+    double dbvdz;
+    double dbzdv;
+    double dbzdz;
+    SpacePointVector bf;
+    if(deriv) {
+      SpacePointTensor t;
+      bf = bfield->field(CartesianPoint(x, y, z), &t);
+      double dbxdx = t.t_x_x();
+      double dbxdy = t.t_x_y();
+      double dbxdz = t.t_x_z();
+      double dbydx = t.t_y_x();
+      double dbydy = t.t_y_y();
+      double dbydz = t.t_y_z();
+      double dbzdx = t.t_z_x();
+      double dbzdy = t.t_z_y();
+      dbzdz = t.t_z_z();
+      dbudv = (dbydy - dbxdx)*cosphi*sinphi
+	- dbydx*sinphi*sinphi + dbxdy*cosphi*cosphi;
+      dbudz = dbxdz*cosphi + dbydz*sinphi;
+      dbvdv = dbxdx*sinphi*sinphi + dbydy*cosphi*cosphi
+	- (dbydx + dbxdy)*cosphi*sinphi;
+      dbvdz = -dbxdz*sinphi + dbydz*cosphi;
+      dbzdv = -dbzdx*sinphi + dbzdy*cosphi;
+    }
+    else
+      bf = bfield->field(CartesianPoint(x, y, z));
+    double bx = bf.v_x();
+    double by = bf.v_y();
+    bz = bf.v_z();
+    bu = bx*cosphi + by*sinphi;
+    bv = -bx*sinphi + by*cosphi;
+
+    // dv/du.
+
+    result(0) = vv;
+
+    // dz/du.
+
+    result(1) = zv;
+
+    // dvv/du.
+
+    double dvvdu_nocrv = BFAC * dsdu * (zv*bu + vv*zv*bv - (1. + vv*vv)*bz);
+    result(2) = dvvdu_nocrv * crv;
+
+    // dzv/du.
+
+    double dzvdu_nocrv = BFAC * dsdu * (-vv*bu + (1. + zv*zv)*bv - vv*zv*bz);
+    result(3) = dzvdu_nocrv * crv;
+
+    // ds/dz.
+
+    result(4) = dsdu;
+
+    if(deriv) {
+
+      // d(dv/d v0)/du.
+
+      result(5) = dvvdv0;
+
+      // d(dv/d z0)/du.
+
+      result(6) = dvvdz0;
+
+      // d(dv/d vv0)/du.
+
+      result(7) = dvvdvv0;
+
+      // d(dv/d zv0)/du.
+
+      result(8) = dvvdzv0;
+
+      // d(dv/d crv)/du.
+
+      result(9) = dvvdcrv;
+
+      // d(dz/d v0)/du.
+
+      result(10) = dzvdv0;
+
+      // d(dz/d z0)/du.
+
+      result(11) = dzvdz0;
+
+      // d(dz/d vv0)/du.
+
+      result(12) = dzvdvv0;
+
+      // d(dz/d zv0)/du.
+
+      result(13) = dzvdzv0;
+
+      // d(dz/d crv)/du.
+
+      result(14) = dzvdcrv;
+
+      // d(d vv/d v0)/du.
+
+      result(15) = result(2) * (vv*dvvdv0 + zv*dzvdv0) / dsdu2
+	+ BFAC * crv * dsdu * 
+	(dvvdv0*zv*bv + vv*dzvdv0*bv + vv*zv*(dbvdv*dvdv0 + dbvdz*dzdv0)
+	 -2.*vv*dvvdv0*bz - (1. + vv*vv)*(dbzdv*dvdv0 + dbzdz*dzdv0)
+	 +dzvdv0*bu + zv*(dbudv*dvdv0 + dbudz*dzdv0));
+
+      // d(d vv/d z0)/du.
+
+      result(16) = result(2) * (vv*dvvdz0 + zv*dzvdz0) / dsdu2
+	+ BFAC * crv * dsdu * 
+	(dvvdz0*zv*bv + vv*dzvdz0*bv + vv*zv*(dbvdv*dvdz0 + dbvdz*dzdz0)
+	 -2.*vv*dvvdz0*bz - (1. + vv*vv)*(dbzdv*dvdz0 + dbzdz*dzdz0)
+	 +dzvdz0*bu + zv*(dbudv*dvdz0 + dbudz*dzdz0));
+
+      // d(d vv/d vv0)/du.
+
+      result(17) = result(2) * (vv*dvvdvv0 + zv*dzvdvv0) / dsdu2
+	+ BFAC * crv * dsdu * 
+	(dvvdvv0*zv*bv + vv*dzvdvv0*bv + vv*zv*(dbvdv*dvdvv0 + dbvdz*dzdvv0)
+	 -2.*vv*dvvdvv0*bz - (1. + vv*vv)*(dbzdv*dvdvv0 + dbzdz*dzdvv0)
+	 +dzvdvv0*bu + zv*(dbudv*dvdvv0 + dbudz*dzdvv0));
+
+      // d(d vv/d zv0)/du.
+
+      result(18) = result(2) * (vv*dvvdzv0 + zv*dzvdzv0) / dsdu2
+	+ BFAC * crv * dsdu * 
+	(dvvdzv0*zv*bv + vv*dzvdzv0*bv + vv*zv*(dbvdv*dvdzv0 + dbvdz*dzdzv0)
+	 -2.*vv*dvvdzv0*bz - (1. + vv*vv)*(dbzdv*dvdzv0 + dbzdz*dzdzv0)
+	 +dzvdzv0*bu + zv*(dbudv*dvdzv0 + dbudz*dzdzv0));
+
+      // d(d vv/d crv)/du.
+
+      result(19) = dvvdu_nocrv * (1. + crv*(vv*dvvdcrv + zv*dzvdcrv) / dsdu2)
+	+ BFAC * crv * dsdu * 
+	(dvvdcrv*zv*bv + vv*dzvdcrv*bv + vv*zv*(dbvdv*dvdcrv + dbvdz*dzdcrv)
+	 -2.*vv*dvvdcrv*bz - (1. + vv*vv)*(dbzdv*dvdcrv + dbzdz*dzdcrv)
+	 +dzvdcrv*bu + zv*(dbudv*dvdcrv + dbudz*dzdcrv));
+
+
+      // d(d zv/d v0)/dr
+
+      result(20) = result(3) * (vv*dvvdv0 + zv*dzvdv0) / dsdu2
+	+ BFAC * crv * dsdu *
+	(2.*zv*dzvdv0*bv + (1. + zv*zv)*(dbvdv*dvdv0 + dbvdz*dzdv0)
+	 -dvvdv0*zv*bz - vv*dzvdv0*bz - vv*zv*(dbzdv*dvdv0 + dbzdz*dzdv0)
+	 -dvvdv0*bu - vv*(dbudv*dvdv0 + dbudz*dzdv0));
+
+      // d(d zv/d z0)/dr
+
+      result(21) = result(3) * (vv*dvvdz0 + zv*dzvdz0) / dsdu2
+	+ BFAC * crv * dsdu *
+	(2.*zv*dzvdz0*bv + (1. + zv*zv)*(dbvdv*dvdz0 + dbvdz*dzdz0)
+	 -dvvdz0*zv*bz - vv*dzvdz0*bz - vv*zv*(dbzdv*dvdz0 + dbzdz*dzdz0)
+	 -dvvdz0*bu - vv*(dbudv*dvdz0 + dbudz*dzdz0));
+
+      // d(d zv/d vv0)/dr
+
+      result(22) = result(3) * (vv*dvvdvv0 + zv*dzvdvv0) / dsdu2
+	+ BFAC * crv * dsdu *
+	(2.*zv*dzvdvv0*bv + (1. + zv*zv)*(dbvdv*dvdvv0 + dbvdz*dzdvv0)
+	 -dvvdvv0*zv*bz - vv*dzvdvv0*bz - vv*zv*(dbzdv*dvdvv0 + dbzdz*dzdvv0)
+	 -dvvdvv0*bu - vv*(dbudv*dvdvv0 + dbudz*dzdvv0));
+
+      // d(d zv/d zv0/dr
+
+      result(23) = result(3) * (vv*dvvdzv0 + zv*dzvdzv0) / dsdu2
+	+ BFAC * crv * dsdu *
+	(2.*zv*dzvdzv0*bv + (1. + zv*zv)*(dbvdv*dvdzv0 + dbvdz*dzdzv0)
+	 -dvvdzv0*zv*bz - vv*dzvdzv0*bz - vv*zv*(dbzdv*dvdzv0 + dbzdz*dzdzv0)
+	 -dvvdzv0*bu - vv*(dbudv*dvdzv0 + dbudz*dzdzv0));
+
+      // d(d zv/d crv)/dr
+
+      result(24) = dzvdu_nocrv * (1. + crv*(vv*dvvdcrv + zv*dzvdcrv) / dsdu2)
+	+ BFAC * crv * dsdu *
+	(2.*zv*dzvdcrv*bv + (1. + zv*zv)*(dbvdv*dvdcrv + dbvdz*dzdcrv)
+	 -dvvdcrv*zv*bz - vv*dzvdcrv*bz - vv*zv*(dbzdv*dvdcrv + dbzdz*dzdcrv)
+	 -dvvdcrv*bu - vv*(dbudv*dvdcrv + dbudz*dzdcrv));
+    }
+    return result;
+  }
+
+  // Function to evolve track paramters using a single fourth order 
+  // Runge-Kutta step from u1 to u2.
+  void rk4(TrackPar& par, double& u, double h, double sinphi, double cosphi,
+	   double crv, double sign_du, const AbstractMagneticField* bfield,
+	   TrackPar& k1, bool reuse) {
+    int size = par.get_size();
+    TrackPar k2(size);
+    TrackPar k3(size);
+    TrackPar k4(size);
+    if(!reuse)
+      k1 = h * motion(par, u, sinphi, cosphi, crv, sign_du, bfield);
+    k2 = h * motion(par + 0.5*k1, u + 0.5*h, sinphi, cosphi, crv, sign_du, 
+		    bfield);
+    k3 = h * motion(par + 0.5*k2, u + 0.5*h, sinphi, cosphi, crv, sign_du,
+		    bfield);
+    k4 = h * motion(par + k3, u + h, sinphi, cosphi, crv, sign_du, bfield);
+    par += (1./6.)*(k1 + 2.*k2 + 2.*k3 + k4);
+    u += h;
+    double dmax = 1.e30;
+    for(int i=0; i<par.get_size(); ++i) {
+      double x = par(i);
+      if(!(x < dmax && x > -dmax))
+	throw runtime_error("Floating point exception");
+    }
+  }
+
+  // Function to calculate the relative difference between two sets
+  // of track parameters.  The result is scaled so that a difference
+  // of less than one is "good."
+  double pardiff(const TrackPar& par1, const TrackPar& par2, 
+		 double derivscale) {
+    int n = par1.get_size();
+    assert(par2.get_size() == n);
+    double epsmax = 0.;
+    for(int i=0; i<n; ++i) {
+      double p1 = par1(i);
+      double p2 = par2(i);
+      double eps = abs(p2-p1)/max(max(abs(p1), abs(p2)), 10.);
+      if(i >= 5)
+	eps *= derivscale;
+      if(eps > epsmax)
+	epsmax = eps;
+    }
+    return epsmax;
+  }
+
+  // Function to evolve track parameters using fourth order Runge-Kutta
+  // with a variable step size.  The starting step size is as specified,
+  // which may be reduced until the error is estimated to be small enough.
+  // The z coordinate is updated to reflect the actual step size.  The 
+  // estimated next step size is returned as the value of the function.
+
+  double rkv(TrackPar& par, double& u, double h, double sinphi, double cosphi,
+	     double crv, double sign_du, const AbstractMagneticField* bfield, 
+	     double precision, double derivprec) {
+
+    double derivscale = precision / derivprec;
+
+    // Calculate the minimum allowed step, which is the lesser of 
+    // either 1 cm or the initial step.
+
+    double hmin = abs(h) / h;
+    if(abs(hmin) > abs(h))
+      hmin = h;
+    double hnext = h;
+    for(;;) {
+
+      // Reduce the target precision for short steps to control the 
+      // accumulation of roundoff error.
+
+      double maxdiff = precision * sqrt(0.1*abs(h));
+      TrackPar par1 = par;
+      TrackPar par2 = par;
+
+      // Try step using h.
+
+      double u1 = u;
+      TrackPar k1(par.get_size());
+      rk4(par1, u1, h, sinphi, cosphi, crv, sign_du, bfield, k1, false);
+
+      // In short hop situations, quit here.
+
+      if(abs(hmin) <= 0.01) {
+	par = par1;
+	u = u1;
+	break;
+      }
+
+      // Try two steps using hh.
+
+      double u2 = u;
+      double hh = 0.5 * h;
+      k1 = 0.5 * k1;
+      rk4(par2, u2, hh, sinphi, cosphi, crv, sign_du, bfield, k1, true);
+      rk4(par2, u2, hh, sinphi, cosphi, crv, sign_du, bfield, k1, false);
+
+      // Calculate difference and get the next step size.
+
+      double eps = pardiff(par1, par2, derivscale);
+      if(eps <= maxdiff || abs(h) <= abs(hmin)) {
+
+	// Check for catastrophic loss of accuracy.
+
+	//	if(eps > 100000.*maxdiff)
+	//	  throw runtime_error("Catastrophic loss of accuracy");
+
+	// Current step is accurate enough.  Adjust the step size.
+	// and return current propagation.
+
+	if(eps != 0) {
+	  hnext = 0.8 * h * pow(maxdiff/eps, 0.25);
+	  if(abs(hnext) > 4. * abs(h))
+	    hnext = 4. * h;
+	}
+	else
+	  hnext = 4. * h;
+	par = (16./15.)*par2 - (1./15.)*par1;
+	u = u1;
+	break;
+      }
+      else {
+
+	// Not accurate enough.  Shrink the step size and try again.
+	// Don't let the step get too small.
+
+	hnext = 0.8 * h * pow(maxdiff/eps, 0.25);
+	if(abs(hnext) < abs(hmin))
+	  hnext = hmin;
+	h = hnext;
+      }
+    }
+
+    // Final check on minimum step size.
+
+    if(abs(hnext) < abs(hmin))
+      hnext = hmin;
+    return hnext;
+  }
+
+  // This routine uses adaptive step size control to make the specified
+  // step using one or several steps.
+
+  void rka(TrackPar& par, double& u, double h, double sinphi, double cosphi,
+	   double crv, double sign_du, const AbstractMagneticField* bfield,
+	   double precision, double derivprec) {
+    assert(h != 0);
+    double htry = h;
+    double hnext;
+    double u2 = u + h;
+    while(h > 0 && u < u2 || h < 0 && u > u2) {
+      double hmax = abs(u2 - u);
+      if (hmax < 1e-6)
+	break;
+      if(abs(htry) > hmax)
+	htry = u2 - u;
+      hnext = rkv(par, u, htry, sinphi, cosphi, crv, sign_du, bfield, 
+		  precision, derivprec);
+      assert(hnext * htry > 0);
+      assert(u + hnext != u);
+      htry = hnext;
+    }
+  }
+}
+
+//************************************************************************
+// PropXYXYRK member functions.
+//************************************************************************
+
+// output stream
+
+void PropXYXYRK::ostr(ostream& stream) const {
+  stream << begin_object;
+  stream << "XY Plane RK propagation with magnetic field type " << _type 
+	 << "," << new_line 
+	 << "precision = " << _precision
+	 << ", scale factor 1 = " << _scale1
+	 << ", scale factor 2 = " << _scale2
+	 << ", derivprec = " << _derivprec;
+  stream << end_object;
+}
+
+//************************************************************************
+
+// Constructor.
+
+PropXYXYRK::PropXYXYRK(const string& type, double precision, double scale1, 
+		       double scale2, double derivprec) :
+  _type(type),
+  _scale1(scale1),
+  _scale2(scale2),
+  _precision(precision),
+  _derivprec(derivprec)
+{
+  assert(precision > 0. && precision <= 0.01);
+  assert(derivprec > 0. && derivprec <= 0.1);
+  if(type.size() == 0 || type == "constant")
+    _bfield = new ConstantMagneticField(-2.*scale1);
+
+  // MCField.
+/*
+  else if(type == "mc")
+    _bfield_map = new ObjMagField(new MCField(scale1, scale2));
+  else if(type == "tosca")
+    _bfield_map = new ObjMagField(new ToscaField(scale1, scale2));
+  else if(type == "D0") {
+    MagFieldMgr* mgr = MagFieldMgr::get_instance();
+    _bfield_map = new ObjMagField(mgr->get_field());
+  }
+  else {
+    ObjTable::Status status = ObjTable::get_object(type, _bfield_map);
+    if(status != ObjTable::OK) {
+      cerr << "PropXYXYRK: Error fetching field map global object " 
+	   << type << endl;
+      abort();
+    }
+  }
+ */
+  //_bfield = new MagFieldCacher(_bfield_map);
+}
+
+//************************************************************************
+
+// Destructor.
+
+PropXYXYRK::~PropXYXYRK() {
+}
+
+//************************************************************************
+
+// Clone.
+
+Propagator* PropXYXYRK::new_propagator() const {
+  return new PropXYXYRK(_type, _precision, _scale1, _scale2, _derivprec);
+}
+
+//**********************************************************************
+
+// Return the creator.
+
+ObjCreator PropXYXYRK::get_creator() {
+  return create;
+}
+
+//**********************************************************************
+
+// Write the object data.
+
+ObjData PropXYXYRK::write_data() const {
+  ObjData data( get_type_name() );
+  data.add_string( "type", _type );
+  data.add_double( "precision", _precision );
+  data.add_double( "scale1", _scale1 );
+  data.add_double( "scale2", _scale2 );
+  data.add_double( "derivprec", _derivprec );
+  return data;
+}
+
+//************************************************************************
+
+// Propagate a track without error in the specified direction.
+//
+// The track parameters for a xy-plane are:
+// v z dv/du dz/du curvature=q/p
+//
+// If pder is nonzero, return the derivative matrix there.
+
+PropStat PropXYXYRK::
+vec_dir_prop(VTrack& trv, const Surface& srf, PropDir dir,
+             TrackDerivative* pder) const {
+
+  // construct return status
+  PropStat pstat;
+
+  // fetch the originating surface.
+  const Surface& srf1 = *trv.get_surface();
+
+  // Check origin is an xy plane.
+  if ( srf1.get_pure_type( ) != SurfXYPlane::get_static_type() )
+    return pstat;
+  const SurfXYPlane& sxy1 = (const SurfXYPlane&) srf1;
+
+  // Check destination is an xy plane.
+  assert( srf.get_pure_type() == SurfXYPlane::get_static_type() );
+  if ( srf.get_pure_type( ) != SurfXYPlane::get_static_type() )
+    return pstat;
+  const SurfXYPlane& sxy2 = (const SurfXYPlane&) srf;
+
+  // Check that original track has defined direction.
+  if(!trv.is_forward() && !trv.is_backward())
+    return pstat;
+
+  // Separate the move status from the direction.
+  PropDirected::PropDir rdir(dir);
+  bool move = Propagator::reduce_direction(rdir);
+
+  // This flag indicates the new surface is only a short hop away.
+  bool short_hop = false;
+
+  // If surfaces are the same and move is not set, then we leave the
+  // track where it is.
+  if ( srf.pure_equal(srf1) && !move ) {
+    if ( pder != 0 ) {
+      TrackDerivative& deriv = *pder;
+      deriv.fill( 0.0 );
+      deriv(0,0) = 1.0;
+      deriv(1,1) = 1.0;
+      deriv(2,2) = 1.0;
+      deriv(3,3) = 1.0;
+      deriv(4,4) = 1.0;
+    }
+    pstat.set_same();
+    return pstat;
+  }
+
+  // Fetch the originating and destination plane parameters.
+  int iu = SurfXYPlane::DISTNORM;
+  int iphi = SurfXYPlane::NORMPHI;
+  double u0 = sxy1.get_parameter(iu);
+  double phi0 = sxy1.get_parameter(iphi);
+  double u2 = sxy2.get_parameter(iu);
+  double phi2 = sxy2.get_parameter(iphi);
+
+  // Fetch the starting track vector.
+  TrackVector vec = trv.get_vector();
+  double v0 = vec(IV);                   // v
+  double z1 = vec(IZ);                   // z
+  double vv0 = vec(IDVDU);               // dv/du
+  double zv0 = vec(IDZDU);               // dv/du
+  double crv = vec(IQP);                 // q/p
+
+  // Transform the initial VTrack onto a surface that is parallel
+  // to the destination surface.  
+  TrackDerivative deriv1;
+  if(pder)
+    deriv1.fill(0.);
+  double u1;
+  double v1;
+  double vv1;
+  double zv1;
+  double sign_du = 0.;
+  if(trv.is_forward())
+    sign_du = 1.;
+  else if(trv.is_backward())
+    sign_du = -1.;
+  else
+    return pstat;
+  if(phi0 != phi2) {
+    double sindphi = sin(phi2-phi0);
+    double cosdphi = cos(phi2-phi0);
+    u1 = u0*cosdphi + v0*sindphi;
+    v1 = -u0*sindphi + v0*cosdphi;
+    double du1du0 = vv0*sindphi + cosdphi;
+    vv1 = (vv0*cosdphi - sindphi)/du1du0;
+    zv1 = zv0/du1du0;
+    if(du1du0 < 0)
+      sign_du = -sign_du;
+    else if (du1du0 == 0)
+      return pstat;
+    if(pder) {
+
+      // Get components of the magnetic field.
+
+      double cosphi2 = cos(phi2);
+      double sinphi2 = sin(phi2);
+      double x = u1 * cosphi2 - v1 * sinphi2;
+      double y = u1 * sinphi2 + v1 * cosphi2;
+      SpacePointVector bf = _bfield->field(CartesianPoint(x, y, z1));
+      double bx = bf.v_x();
+      double by = bf.v_y();
+      double bz = bf.v_z();
+      double bu = bx * cosphi2 + by * sinphi2;
+      double bv = -bx * cosphi2 + by * cosphi2;
+
+      double dsdu1 = sqrt(1. + vv1*vv1 + zv1*zv1);
+
+      deriv1(IV,IV) = cosdphi - sindphi*vv1;
+      deriv1(IV,IZ) = 0.;
+      deriv1(IV,IDVDU) = 0.;
+      deriv1(IV,IDZDU) = 0.;
+      deriv1(IZ,IV) = -sindphi*zv1;
+      deriv1(IZ,IZ) = 1.;
+      deriv1(IZ,IDVDU) = 0.;
+      deriv1(IZ,IDZDU) = 0.;
+      deriv1(IZ,IQP) = 0.;
+      deriv1(IDVDU,IV) = -sign_du * sindphi * BFAC * crv * dsdu1 * 
+	(zv1*bu + vv1*zv1*bv - (1. + vv1*vv1)*bz);
+      deriv1(IDVDU,IZ) = 0.;
+      deriv1(IDVDU,IDVDU) = 1. / (du1du0*du1du0);
+      deriv1(IDVDU,IDZDU) = 0.;
+      deriv1(IDVDU,IQP) = 0.;
+      deriv1(IDZDU,IV) = -sign_du * sindphi * BFAC * crv * dsdu1 *
+	(-vv1*bu + (1. + zv1*zv1)*bv - vv1*zv1*bz);
+      deriv1(IDZDU,IZ) = 0.;
+      deriv1(IDZDU,IDVDU) = -zv0 * sindphi / (du1du0*du1du0);
+      deriv1(IDZDU,IDZDU) = 1. / du1du0;
+      deriv1(IDZDU,IQP) = 0.;
+      deriv1(IQP,IV) = 0.;
+      deriv1(IQP,IZ) = 0.;
+      deriv1(IQP,IDVDU) = 0.;
+      deriv1(IQP,IDZDU) = 0.;
+      deriv1(IQP,IQP) = 1.;
+    }
+  }
+  else {
+    u1 = u0;
+    v1 = v0;
+    vv1 = vv0;
+    zv1 = zv0;
+    if(pder) {
+      deriv1(IV,IV) = 1.;
+      deriv1(IZ,IZ) = 1.;
+      deriv1(IDVDU,IDVDU) = 1.;
+      deriv1(IDZDU,IDZDU) = 1.;
+      deriv1(IQP,IQP) = 1.;
+    }
+  }
+  bool forward = sign_du > 0.;
+  bool backward = sign_du < 0.;
+  assert(forward || backward);
+  assert(!(forward && backward));
+
+  // Check the consistency of the initial and final u positions, the track 
+  // direction and the propagation direction.  Four of the eight possible
+  // forward/backward combinations are illegal.  
+
+  if(u2 == u1) {
+    if(move) {
+      cerr << "PropXYXYRK: Invalid move" << endl;
+      return pstat;
+    }
+  }
+  else if(u2 > u1) {
+    if(rdir == Propagator::FORWARD && backward ||
+       rdir == Propagator::BACKWARD && forward) {
+      cerr << "PropXYXYRK: Wrong direction" << endl;
+      return pstat;
+    }
+  }
+  else {  // u2 < u1
+    if(rdir == Propagator::FORWARD && forward ||
+       rdir == Propagator::BACKWARD && backward) {
+      cerr << "PropXYXYRK: Wrong direction" << endl;
+      return pstat;
+    }
+  }
+
+  // We have all non-trivial mathematical operations in a try block
+  // so we can catch floating point exceptions.
+
+  try {
+
+    // Fill initial parameter vector.
+
+    TrackDerivative deriv2;
+    int size = 5;
+    if(pder)
+      size = 25;
+    TrackPar par(size);
+    par.fill(0.);
+    par(0) = v1;       // v
+    par(1) = z1;       // z
+    par(2) = vv1;      // dv/du
+    par(3) = zv1;      // dz/du
+    par(4) = 0.;       // Path length.
+    if(pder) {
+      par(5) = 1.;     // d v / d v0
+      par(11) = 1.;    // d z / d z0
+      par(17) = 1.;    // d vv / d vv0
+      par(23) = 1.;    // d zv / d zv0
+    }
+
+    // Propagate parameter vector.
+
+    double u = u1;
+    double h = u2 - u1;
+    if(abs(h) < 1.e-10)
+      u = u2;
+    else
+      rka(par, u, h, sin(phi2), cos(phi2), crv, sign_du, _bfield, 
+	  _precision, _derivprec);
+
+    // Calculate final parameters and put them back into vec.
+
+    double dmax = 1.e30;
+    double v2 = par(0);
+    double z2 = par(1);
+    double vv2 = par(2);
+    double zv2 = par(3);
+    double s = par(4);
+    vec(IV) = v2;
+    vec(IZ) = z2;
+    vec(IDVDU) = vv2;
+    vec(IDZDU) = zv2;
+    vec(IQP) = crv;
+    if(!(v2 < dmax && v2 > -dmax))
+      throw runtime_error("Floating point exception");
+    if(!(z2 < dmax && z2 > -dmax))
+      throw runtime_error("Floating point exception");
+    if(!(vv2 < dmax && vv2 > -dmax))
+      throw runtime_error("Floating point exception");
+    if(!(zv2 < dmax && zv2 > -dmax))
+      throw runtime_error("Floating point exception");
+    if(!(s < dmax && s > -dmax))
+      throw runtime_error("Floating point exception");
+    if(pder) {
+
+      // Calculate final derivative matrix.
+
+      deriv2(0,0) = par(5);                // dv / d v0
+      deriv2(0,1) = par(6);                // dv / d z0
+      deriv2(0,2) = par(7);                // dv / d vv0
+      deriv2(0,3) = par(8);                // dv / d zv0
+      deriv2(0,4) = par(9);                // dv / d crv
+      deriv2(1,0) = par(10);               // dz / d v0
+      deriv2(1,1) = par(11);               // dz / d z0
+      deriv2(1,2) = par(12);               // dz / d vv0
+      deriv2(1,3) = par(13);               // dz / d zv0
+      deriv2(1,4) = par(14);               // dz / d crv
+      deriv2(2,0) = par(15);               // d vv / d v0
+      deriv2(2,1) = par(16);               // d vv / d z0
+      deriv2(2,2) = par(17);               // d vv / d vv0
+      deriv2(2,3) = par(18);               // d vv / d zv0
+      deriv2(2,4) = par(19);               // d vv / d crv
+      deriv2(3,0) = par(20);               // d zv / d v0
+      deriv2(3,1) = par(21);               // d zv / d z0
+      deriv2(3,2) = par(22);               // d zv / d vv0
+      deriv2(3,3) = par(23);               // d zv / d zv0
+      deriv2(3,4) = par(24);               // d zv / d crv
+      deriv2(4,0) = 0.;                    // d crv / d v0
+      deriv2(4,1) = 0.;                    // d crv / d z0
+      deriv2(4,2) = 0.;                    // d crv / d vv0
+      deriv2(4,3) = 0.;                    // d crv / d zv0
+      deriv2(4,4) = 1.;                    // d crv / d crv
+      for(int i=0; i<4; ++i) {
+	for(int j=0; j<5; ++j) {
+	  double x = deriv2(i,j);
+	  if(!(x < dmax && x > -dmax))
+	    throw runtime_error("Floating point exception");
+	}
+      }
+    }
+
+    // Update trv
+    trv.set_surface( SurfacePtr(srf.new_pure_surface()) );
+    trv.set_vector(vec);
+    if(forward)
+      trv.set_forward();
+    else if(backward)
+      trv.set_backward();
+
+    // Set the return status.
+    pstat.set_path_distance(s);
+
+    // Update derivative matrix.
+    if(pder)
+      *pder = deriv2 * deriv1;
+  }
+  catch(runtime_error x) {}
+  return pstat;
+}
+
+//**********************************************************************
+
+// return Bz at the origin.
+double PropXYXYRK::get_bfield() const
+{
+  SpacePointVector bf = _bfield->field(CartesianPoint(0., 0., 0.));
+  return bf.v_z();
+}
+
+//**********************************************************************
CVSspam 0.2.8