// ----------------------------------------------------------------------------
// $Id: LCDSwimTraj.cxx,v 1.1 2001/05/10 17:56:05 toshi Exp $
// ----------------------------------------------------------------------------
//
// $Log: LCDSwimTraj.cxx,v $
// Revision 1.1  2001/05/10 17:56:05  toshi
// Changes C++ name convention from *.C to *.cxx to allow Windows usage.
//
//
// Many thanks to Rasmus Munk Larsen of Stanford for the numerically-stable
// algorithm for finding the intersection of 2 circles.


#include "LCDSwimTraj.h"
#include <iostream.h>

#define VERY_SMALL    0.00001
#define SMALL_MOM     0.00001
#define SBIG          9999999.0
#define A_LITTLE_WAYS 0.10

//______________________________________________________________________
//
// SwimTraj
//
// This class does the work of propagating particles.

ClassImp(LCDSwimTraj)

//______________________________________________________________________
 LCDSwimTraj::LCDSwimTraj() {
}
//______________________________________________________________________

//______________________________________________________________________
LCDSwimTraj::PropagateRet LCDSwimTraj::HelixToCyl(LCDDetectorVolume *pCur, 
						  Double_t *pS) {
  // Propagate along a suitable helix until it intersects the surface of a
  // cylindrical tube with inner radius rCylIn, outer radius rCylOut,
  // and ends at z = +- zCyl.  Return code indicates whether intersection was 
  // with -z end, +z end,  inner barrel surface, outer barrel surface
  // or never happened.  Return path length as well as final position 
  // and momentum vectors.
  // Initial position must be inside the tube.

  //Must have a charge...

  //cout<<"HelixToCyl:q="<<q<<endl;

  if (q == 0) return INTERSECT_ERROR;

  PropagateRet ret = INTERSECT_NONE;   // unknown at this point
  *pS = 0.0;

  // Propagate "a little ways" and then check that we're inside volume
  // (might be on the boundary to begin with).
  // A side effect of this is to compute radius, etc.
  PropagateRet ret1 = HelixPath(pCur->field, A_LITTLE_WAYS);

  if (!CheckInside(pCur)) {
    //cout<<"HelixToCyl:checkInside error xtrj="<<xtrj.Perp()<<","
    //<<xtrj.Z()<<endl;
    return INTERSECT_ERROR;
  }
  Update();

  Double_t p = ptrj.Mag();
  Double_t pt= ptrj.Pt();

  Double_t sinLambda = p > 0 ? ptrj.Pz()/p : 0.0;
  Double_t cosLambda = p > 0 ? pt       /p : 0.0;
  
  //Check ... does the helix hit the endcap?
  Double_t sEndcap=SBIG;
  Double_t zCyl = ptrj.Pz() > 0 ? pCur->outerZ : -pCur->outerZ ;
  if (sinLambda != 0) { 
    sEndcap = TMath::Abs((zCyl - xtrj.Z())/sinLambda);
    HelixPath(sEndcap-A_LITTLE_WAYS);
    ret = xnew.Z() > 0 ? INTERSECT_ZPLUS : INTERSECT_ZMINUS ;
  }

  TVector3 sol1, sol2;
  // Find s for outer barrel
  Double_t rCylOut = pCur->outerR;
  Int_t nSol = CircleIntersect(rCylOut, sol1, sol2);

  Double_t sBarrelOut=SBIG;
  Double_t omega=m_tkpar[2];
  Double_t s0out=SBIG,s1out=SBIG;
  switch (nSol) {
  case 0: 
  case 1:
    sBarrelOut = SBIG;
    break;
  case 2:   // Bunch of stuff to do here
    s0out = PhaseChange(sol1.XYvector())/omega/cosLambda;
    s1out = PhaseChange(sol2.XYvector())/omega/cosLambda;
    // Now use this to calculate path length
    sBarrelOut = s0out < s1out ? s0out : s1out;
    break;
  default:  // anything else is screwy
    printf(" LCDSwimTraj::HelixToCyl: Bad # intersections (%d) with outer volumen", nSol);
    return INTERSECT_ERROR;
    break;
  }

  // .. and similarly for inner barrel -- if rCylIn isn't zero
  Double_t rCylIn    = pCur->innerR;
  Double_t sBarrelIn = SBIG;
  Double_t s0in=SBIG,s1in=SBIG;
  if (rCylIn > .01)   { 
    Int_t nSol = CircleIntersect(rCylIn, sol1, sol2);

    switch (nSol) {
    case 0: 
    case 1:
      sBarrelIn = SBIG;
      break;
    case 2:   // Bunch of stuff to do here
      s0in = PhaseChange(sol1.XYvector())/omega/cosLambda;
      s1in = PhaseChange(sol2.XYvector())/omega/cosLambda;
      // Now use this to calculate path length
      sBarrelIn = s0in < s1in ? s0in : s1in;
      break;
    default:  // anything else is screwy
      printf(" LCDSwimTraj::HelixToCyl: Bad # intersections (%d) with inner volumen", nSol);
      return INTERSECT_ERROR;
      break;
    }
  }

  Double_t s=0;
  ret1 = ShortestPath(sEndcap, sBarrelIn, sBarrelOut, &s);
  if (ret1 != INTERSECT_ZPLUS) ret = ret1;

  if (s == SBIG) return INTERSECT_NONE;

  // In any case need to calculate all returned values
  deltaS = s;
  *pS = s + A_LITTLE_WAYS;
  HelixPath(s);

  return ret;
}
//______________________________________________________________________

//______________________________________________________________________
LCDSwimTraj::PropagateRet LCDSwimTraj::HelixPath(Double_t fld, Double_t s) {
  // Propagate along a suitable helix for length s, starting with input
  // position and momentum vectors (by default use vectors specified in
  // previous call to SetParticle). Return final position and momentum.

  if (q == 0) return INTERSECT_UNCHARGED;

  // Invoke utility to compute some useful things.
  CalcTrackParameters(fld);

  return HelixPath(s);
}
//______________________________________________________________________

//______________________________________________________________________
LCDSwimTraj::PropagateRet LCDSwimTraj::HelixPath(Double_t s) {
  // assume helix parameter was already set...

  if (q == 0) return INTERSECT_UNCHARGED;

  Double_t d0   = m_tkpar[0];
  Double_t phi0 = m_tkpar[1];
  Double_t omega= m_tkpar[2];
  Double_t z0   = m_tkpar[3];
  Double_t tanl = m_tkpar[4];

  Double_t phi  = phi0 + omega*s/TMath::Sqrt(1.0+tanl*tanl);

  // Position vector
  xnew.SetXYZ(xtrj.X() - (1.0/omega + d0)*TMath::Sin(phi0)
	      + 1.0/omega*TMath::Sin(phi),
	      xtrj.Y() + (1.0/omega + d0)*TMath::Cos(phi0)
	      - 1.0/omega*TMath::Cos(phi),
	      xtrj.Z() + z0 + s*tanl/TMath::Sqrt(1.0+tanl*tanl));
  
  // Momemtum vector
  Double_t pt=ptrj.Pt();
  pnew.SetXYZ(pt*TMath::Cos(phi),pt*TMath::Sin(phi),ptrj.Pz());

  deltaS = s;
  return INTERSECT_PATHOK;
}
//______________________________________________________________________

//______________________________________________________________________
 void LCDSwimTraj::CalcTrackParameters(Double_t bfld_z) {
  Double_t pT = ptrj.Pt();
  if (pT < SMALL_MOM) pT=SMALL_MOM;
  m_tkpar[0]= 0.0;
  m_tkpar[1]= ptrj.Phi();
  m_tkpar[2]=-q*bfld_z/333.567/pT;
  m_tkpar[3]= 0.0;
  m_tkpar[4]= ptrj.Z()/pT;
}
//______________________________________________________________________

//______________________________________________________________________
 Int_t LCDSwimTraj::CircleIntersect(Double_t rv,    // radius of cylinder
				   TVector3& p1,   // (x,y) of 1st solution
				   TVector3& p2)   // (x,y) of 2nd solution
{
  // This utility is used by SwimTraj::HelixToCy.
  //
  // Given two circles, one centered about the origin, find their points of
  // intersection.  The return value is the number of solutions found.
  // In this application, one circle is the (xy-projection) of a volume
  // boundary, always centered about the origin.  The other the helix which
  // is typically not centered about the origin.
  // This algorithm is due to Rasmus Munk Larsen.

  // Returns the number of intersections or error code:

  //   -2: Invalid input.
  //   -1: Infinitely many intersections - the two circles are identical.
  //      0: No intersection.
  //      1: 1 point of intersection, (p1[0],p1[1]) contains the 
  //           x- and y-coordinates.
  //      2: 2 point of intersection, (p1[0],p1[1]) and (p2[0],p2[1])
  //           contains the x- and y-coordinates

  Double_t d0   = m_tkpar[0];
  Double_t phi0 = m_tkpar[1];
  Double_t omega= m_tkpar[2];
  TVector3 xc(xtrj.X() - (1.0/omega + d0)*TMath::Sin(phi0),
	      xtrj.Y() + (1.0/omega + d0)*TMath::Cos(phi0),
	      0.0);
  Double_t r2   = TMath::Abs(1.0/omega);     // radius of helix
  Double_t r1   = rv;      // radius of detector volume

  Int_t    nGood = 0;

  // Check for negative radii. 
  if (r1<=0.0 || r2<=0.0) {
    fprintf(stderr,"Negative radii passed to circle_intersect:");
    fprintf(stderr,"r1 = %g, r2=%g omega=%gn", r1, r2, omega);
    return -2;
  }
  
  // Now decide how many points of intersection there are: 

  if (xc.X()==0 && xc.Y()==0 && r1==r2)
    return -1; // The two circles are identical! 


  Double_t d=xc.Perp();   // distance between circle centers

  // scale factor used in checking for solutions
  Double_t mag = TMath::Abs(r1) + TMath::Abs(r2) + TMath::Abs(d);

  if ( d > r1 +r2 + 10 * DBL_EPSILON * mag  ||  
       d < TMath::Abs(r1-r2) - 10 * DBL_EPSILON * mag) 
    return 0; // No intersections! 

  if ( TMath::Abs(d-r1-r2) <= 10*DBL_EPSILON*mag  ||  
       TMath::Abs(d-TMath::Abs(r1-r2)) <= 10*DBL_EPSILON*mag) {
    // (Maybe!) One point of intersection!
    TVector3 nxc(xc.Unit());
    p1=r1*nxc;
    p2=r1*nxc;
    nGood = 1;
  } else {
    // Two points of intersection! 
    Double_t csfi=(d*d + r2*r2 - r1*r1)/2/r2/d;
    Double_t snfi=-q/TMath::Abs(q)*TMath::Sqrt(1.0 - csfi*csfi);
    TVector3 nco(-xc.Unit());
    TVector3 ezXnco(-nco.Y(),nco.X(),0.0);
    p1=xc + r2*csfi*nco + r2*snfi*ezXnco;
    p2=xc + r2*csfi*nco - r2*snfi*ezXnco;
    nGood = 2;
  }

  return nGood;
}
//______________________________________________________________________

//______________________________________________________________________
 Double_t LCDSwimTraj::PhaseChange(TVector2 newP) {
  // Return change in phase needed to get to (newX, newY) from
  // initial position

  // ATan2 returns a value in the range -pi to pi

  Double_t d0    = m_tkpar[0];
  Double_t phi0  = m_tkpar[1];
  Double_t omega = m_tkpar[2];

  TVector2 xc(xtrj.X() - (1.0/omega + d0)*TMath::Sin(phi0),
	      xtrj.Y() + (1.0/omega + d0)*TMath::Cos(phi0));
  TVector2 xtrjxc(xtrj.X() - xc.X(), xtrj.Y() - xc.Y());
  Double_t dphi = (newP - xc).DeltaPhi(xtrjxc);
  if        (dphi < 0 && omega > 0) {
    dphi += 2*TMath::Pi(); 
  } else if (dphi > 0 && omega < 0) {
    dphi -= 2*TMath::Pi(); 
  }
  return dphi;
}
//______________________________________________________________________

//______________________________________________________________________
LCDSwimTraj::PropagateRet LCDSwimTraj::ShortestPath(Double_t sEnd, 
						    Double_t sIn, 
						    Double_t sOut, 
						    Double_t* pS) {
  // Given values for for paths to endcap, inner barrel, outer barrel,
  // put the smallest in return path argument and return an appropriate
  // status code.  This routine can't distinguish between +z and -z
  // endcap, so caller has to fix up return code afterwards in this case.

  // Shortest path gives us the surface we hit first
  if (sEnd < sOut) {
    if (sEnd < sIn) {
      *pS = sEnd;
      return LCDSwimTraj::INTERSECT_ZPLUS;       // Caller may have to fix
    } else {
      *pS = sIn;
      return LCDSwimTraj::INTERSECT_CYLIN;
    }
  } else if (sIn < sOut) {
    *pS = sIn;
    return LCDSwimTraj::INTERSECT_CYLIN;
  } else {
    *pS = sOut;
    return LCDSwimTraj::INTERSECT_CYLOUT;
  }
}
//______________________________________________________________________

//______________________________________________________________________
LCDSwimTraj::PropagateRet LCDSwimTraj::LineToCyl(LCDDetectorVolume *pCur, 
						   Double_t *pS) {
  // Propagate along straight line to cylinder.

  *pS = 0.0;

  // Must start out inside
  PropagateRet ret1 = LinePath(A_LITTLE_WAYS);
  if (!CheckInside(pCur)) {
    return INTERSECT_ERROR;
  }

  Update();

  pnew = ptrj;

  if (pnew.Mag2() < .000001) return INTERSECT_NONE;

  PropagateRet ret = INTERSECT_NONE;

  // First check to see if we hit an endcap
  Double_t zCyl = pCur->outerZ;
  TVector3 delta_X(0.,0.,0.);
  Double_t sEnd=SBIG;
  if (TMath::Abs(pnew.Z()) > SMALL_MOM) { // worth computing
    if (pnew.Z() < -SMALL_MOM) { 
      delta_X.SetZ(-zCyl - xtrj.Z() );
      ret = INTERSECT_ZMINUS;
    } else {
      delta_X.SetZ( zCyl - xtrj.Z() );
      ret = INTERSECT_ZPLUS;
    }
    delta_X.SetX(delta_X.Z()*pnew.X()/pnew.Z());
    delta_X.SetY(delta_X.Z()*pnew.Y()/pnew.Z());
    sEnd = delta_X.Mag();
  }


  Double_t rCylIn  = pCur->innerR;
  Double_t rCylOut = pCur->outerR;
  Double_t sIn  = SBIG;
  Double_t sOut = SBIG;
  if ( pnew.Pt() > SMALL_MOM) {
    Double_t t;
    Bool_t   quadStatus;
    if (rCylIn > 0.0) {
      // Find time t such that we've reached the inner barrel, that is
      // (x0 + t*px)**2 + (y0 + t*py)**2  = rCyl**2
      // Reduces to finding the positive root, if any, of the quadratic with
      //     A = px*px + py*py, B = 2(px*x0 + py*y0)
      //     C = x0*x0 + y0*y0 - rCyl*rCyl
      t = QuadRoot(pnew.Pt()*pnew.Pt(),
		   2.0*(pnew.X()*xtrj.X() + pnew.Y()*xtrj.Y()),
		   xtrj.Perp()*xtrj.Perp() - rCylIn*rCylIn, 
		   &quadStatus);

      if ((quadStatus) && (t > 0.0)) { // OK
	delta_X = t* pnew;
	sIn = delta_X.Mag();
      }
    }        // Done with inner barrel
    // Do same for outer barrel
    t = QuadRoot(pnew.Pt()*pnew.Pt(),
		 2.0*(pnew.X()*xtrj.Y() + pnew.Y()*xtrj.X()),
		 xtrj.Perp()*xtrj.Perp() - rCylOut*rCylOut, 
		 &quadStatus);

    if ((quadStatus) && (t > 0.0)) { // OK
      delta_X = t*pnew;
      sOut = delta_X.Mag();
    }
  }

  Double_t s=0;

  ret1 = ShortestPath(sEnd, sIn, sOut, &s);
  if (s == SBIG) return INTERSECT_NONE;
 
  if (ret1 != INTERSECT_ZPLUS) ret = ret1;

  // Good status.  Fill return arguments
  delta_X = (s/pnew.Mag())* pnew;
  xnew = xtrj + delta_X;

  deltaS = s;
  *pS = s + A_LITTLE_WAYS;

  return ret;
}
//______________________________________________________________________


//______________________________________________________________________
LCDSwimTraj::PropagateRet LCDSwimTraj::LinePath(Double_t s) {
  // Propagate along a line.  Same call interface as HelixPath 
  // except for fld.  

  // Momentum doesn't change in model with no energy loss.
  pnew = ptrj;

  Double_t momMag = pnew.Mag();
  if (momMag == 0) { // nothing happens
    xnew = xtrj;
  } else {
    xnew = xtrj + (s/momMag)*pnew;
  }
  deltaS = s;

  return INTERSECT_PATHOK;
}
//______________________________________________________________________

//______________________________________________________________________
 Double_t LCDSwimTraj::QuadRoot(const Double_t a, const Double_t b, 
				const Double_t c, Bool_t* status) {
  // Utility used when swimming linearly to cylinder.  Return
  // Smallest positive root, if any.

  Double_t disc = b*b - 4 * a * c;

  if ((disc < 0.0) || ((a < VERY_SMALL) && (-a > - VERY_SMALL))) {
    *status = kFALSE;
    return 0.0;
  } else {
    *status = kTRUE;
  }

  Double_t r1, r2;

  disc = TMath::Sqrt(disc);

  r1 = (-b + disc) / (2 * a);
  r2 = (-b - disc) / (2 * a);

  if (r1 < 0.0) {
    if (r2 < 0.0) {
      *status = kFALSE;  
    }
    return r2;
  }
  else if (r2 < 0.0) return r1;      
  else return (r1 > r2) ? r2 : r1;  // both positive
}
//______________________________________________________________________

//______________________________________________________________________
 void LCDSwimTraj::SetParticleParams(const TVector3& pos0, const TVector3& mom0,
				     Int_t charge)
{
  // Compute and cache some information about this particle
  
  totalS = 0.0;
  xtrj   = pos0;
  ptrj   = mom0;
  xnew   = pos0;
  pnew   = mom0;

  q = charge;
}
//______________________________________________________________________

//______________________________________________________________________
 void  LCDSwimTraj::Update() {
  // Invoke this routine when the result of the last request to propagate is 
  // is deemed OK.
  xtrj = xnew;
  ptrj = pnew;
  totalS += deltaS;
}
//______________________________________________________________________
  




ROOT page - Class index - Top of the page

This page has been automatically generated. If you have any comments or suggestions about the page layout send a mail to ROOT support, or contact the developers with any questions or problems regarding ROOT.