// $Header: SmearTraj.cxx $


#include "TMath.h"

#include "SmearTraj.h"
#define  false  0
#define  true   1

static const Double_t SBIG = 9999999.0;
static const Double_t A_LITTLE_WAYS = 0.10;   // a millimeter.

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

ClassImp(SmearTraj)

 void SmearTraj::SetParticle(McPart* pP, Int_t partIx) {
  // Compute and cache some information about this particle

  Float_t *pIpos = pP->GetPosition();
  Float_t *pImom = pP->GetMomentum();

  totalS = 0.0;
  particleIx = partIx;
  for (int ix = 0; ix < 3; ix++) {
    iP[ix] = *pIpos++;
    iM[ix] = *pImom++;  
    newP[ix] = 0.0;
    newM[ix] = 0.0;
  }
  momMag = xyMomMag = 0.0;
  sinLambda = cosLambda = 0.0;
  rad = radQ = xCenter = yCenter = rCenter = 0.0;
  phiCenter = fieldCnv = 0.0;

   q = (int) pP->GetCharge();

  pPart = pP;
}

 void  SmearTraj::Update() {
  // Invoke this routine when the result of the last request to propagate is 
  // is deemed OK.

  for (int i = 0; i < 3; i++) {
    iP[i] = newP[i];
    iM[i] = newM[i];
  }
  totalS += deltaS;
}
  
 void SmearTraj::makeHelixQuantities(Double_t fld) {
  // For a given field (and momementum magnitude of particle which,
  // for current implementation, is constant throughoutn its life)
  // compute and store some useful quantities.

  static const Double_t VERY_SMALL_COSLAMBDA = 0.0000010;
  fieldCnv = fld * 0.003;

  momMag = TMath::Sqrt(iM[0]*iM[0] + iM[1]*iM[1] + iM[2]*iM[2]);
  

  xyMomMag = TMath::Sqrt(iM[0]*iM[0] + iM[1]*iM[1]);
  sinLambda = iM[2] / momMag;
  cosLambda = xyMomMag / momMag;
  
  if (q == 0) {
    // propagate helix stuff is irrelevant
    xCenter = yCenter = rCenter = phiCenter = rad = radQ = 0;
  }
  else {
    if (cosLambda < VERY_SMALL_COSLAMBDA) { // essentially just moves in z
      xCenter = iP[0];
      yCenter = iP[1];
      rad = 0;
      radQ = 0;
      phiCenter = 0;   // hardly defined
    }
    else {
      Int_t signQ = (q > 0) ? 1 : -1;

      radQ = xyMomMag / (fieldCnv * q);
      rad = radQ * signQ;

      xCenter = iP[0] + radQ * iM[1] / xyMomMag;
      yCenter = iP[1] - radQ * iM[0] / xyMomMag;
      phiCenter = TMath::ATan2(yCenter, xCenter);
    }
    rCenter = TMath::Sqrt(xCenter*xCenter + yCenter*yCenter);
  }
}

 Double_t SmearTraj::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;
  static const Double_t VERY_SMALL = 0.00001;

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

  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 = false;  
    }
    return r2;
  }
  else if (r2 < 0.0) return r1;      
  else return (r1 > r2) ? r2 : r1;  // both positive
}

 Int_t SmearTraj::circleIntersect(Double_t rv,    // radius of cylinder
				 Double_t* x0,   // array of solution x-coord
				 Double_t* y0)   // array of solution y-coord
{
  // This utility is used by SmearTraj::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 routine shouldn't find more than two solutions, but can't 
  // possibly find more than four, so to be safe x0, y0 should be pointers
  // to arrays of 4 doubles.
  static Double_t eps = .0000001;
  Double_t xh = xCenter;
  Double_t yh = yCenter;
  Double_t rh = rad;
  Double_t dh2;  // square of distance of center of helix from origin
  Double_t dh = rCenter;
  Double_t k;    // convenient to have for calculations
  Double_t discrHalf;
  Double_t xTry, yTry;
  Int_t    nGood = 0;

  dh2 = dh * dh;

  // First check that we have any solutions at all; i.e., see if helix
  // circle is either totally inside or totally outside volume circle
  // In particular, there is no solution if dh = 0.
  if ((dh + rh < rv) || TMath::Abs((dh - rh) > rv)) return nGood;

  // For completeness should also do something about the case where the
  // 2 circles coincide (dh = 0, rv = rh)

  // Want solutions (x0, y0) such that x0**2 + y0**2 = rv**2 and
  // (x0-xh)**2 + (y0-yh)**2 = rh**2.
  // Letting...
  k = 0.5 * (rv*rv + dh2 - rh*rh);

  // .. gives   k - x0*xh = y0*yh.  Square both sides and replace
  // y0**2 with rv**2 - x0**2 to get quadratic in x0 with 
  // coefficients a = dh2, b = (-2k*xh), c = (k**2 - yh**2 * rv**2)
  discrHalf =  TMath::Sqrt(k*k*xh*xh - dh2*(k*k - yh*yh*rv*rv));
  xTry = (k * xh + discrHalf) / dh2;

  // Check xTry with positive y 
  yTry = TMath::Sqrt(rv * rv - xTry * xTry);
  if (TMath::Abs((xTry - xh) * (xTry - xh) + 
		 (yTry - yh)  * (yTry - yh)    -rh * rh) < eps) {
    *x0 = xTry; *y0 = yTry;
    nGood++;
  }
  if (yTry > eps) {  // try its opposite as well
    if (TMath::Abs((xTry - xh) * (xTry - xh) + 
		   (yTry + yh)  * (yTry + yh)    -rh * rh) < eps) {
      *(x0 + nGood) = xTry; *(y0 + nGood) = -yTry;
      nGood++;
    }
  }

  // Similarly for other possible x (don't bother if discr very small)
  if (discrHalf < eps) return nGood;

  xTry = (k * xh - discrHalf) / dh2;

  yTry = TMath::Sqrt(rv * rv - xTry * xTry);
  if (TMath::Abs((xTry - xh) * (xTry - xh) + 
		 (yTry - yh)  * (yTry - yh)    -rh * rh) < eps) {
    *(x0 + nGood) = xTry; *(y0 + nGood) = yTry;
    nGood++;
  }
  if (yTry > eps) {  // try its opposite as well
    if (TMath::Abs((xTry - xh) * (xTry - xh) + 
		   (yTry + yh)  * (yTry + yh)    -rh * rh) < eps) {
      *(x0 + nGood) = xTry; *(y0 + nGood) = -yTry;
      nGood++;
    }
  }

  return nGood;
}

 Double_t SmearTraj::phaseChange(Double_t newX, Double_t newY) {
  // Return change in phase needed to get to (newX, newY) from
  // initial position

  Double_t iniPhi, newPhi, delPhi;

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

  // Might want to cache iniPhi in Update method rather than re-computing
  iniPhi = TMath::ATan2(iP[1] - yCenter, iP[0] - xCenter); 
  
  newPhi = TMath::ATan2(newY - yCenter, newX - xCenter);
  
  // Which gets subtracted from which depends on sign of particle.
  // I *think* neg. charge goes "forwards"
  delPhi = newPhi - iniPhi;          // assume forward
  if (q > 0) delPhi = - delPhi;    // whoops, wrong guess
  if (delPhi < 0.0) delPhi += 2.0 * TMath::Pi();  // normalize
  return delPhi;
}

SmearTraj::PropagateRet 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 SmearTraj::INTERSECT_ZPLUS;       // Caller may have to fix
    }
    else {
      *pS = sIn;
      return SmearTraj::INTERSECT_CYLIN;
    }
  }
  else if (sIn < sOut) {
    *pS = sIn;
    return SmearTraj::INTERSECT_CYLIN;
  }
  else {
    *pS = sOut;
    return SmearTraj::INTERSECT_CYLOUT;
  }
}

SmearTraj::PropagateRet SmearTraj::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.

  Double_t iMomPhi;

  if (q == 0) return INTERSECT_UNCHARGED;

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

  iMomPhi = TMath::ATan2(iM[1], iM[0]);

  Double_t  phiMom = s * cosLambda / radQ - iMomPhi;
  newP[0] = xCenter + radQ * TMath::Sin(phiMom);       // x
  newP[1] = yCenter + radQ * TMath::Cos(phiMom); // y
  newP[2] = iP[2] + s * sinLambda;
  
  // Momemtum vector

  newM[0] = momMag * cosLambda * TMath::Cos(phiMom);        // x
  newM[1] = -momMag * cosLambda * TMath::Sin(phiMom); // y
  newM[2] = iM[2];                              // z - doesn't change

  deltaS = s;
  return INTERSECT_PATHOK;
}


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

  Double_t xMom = iM[0];
  Double_t yMom = iM[1];
  Double_t zMom = iM[2];

  // Momentum doesn't change in model with no energy loss.
  newM[0] = iM[0];
  newM[1] = iM[1];
  newM[2] = iM[2];

  momMag = TMath::Sqrt(xMom*xMom + yMom*yMom + zMom*zMom);

  if (momMag == 0) { // nothing happens
    newP[0] = iP[0];
    newP[1] = iP[1];
    newP[2] = iP[2];
  }
  newP[0] = iP[0] + xMom * s / momMag;
  newP[1] = iP[1] + yMom * s / momMag;
  newP[2] = iP[2] + zMom * s / momMag;
  deltaS = s;

  return INTERSECT_PATHOK;
}
SmearTraj::PropagateRet SmearTraj::HelixToCyl(SmearVolume *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.
  Double_t      iMomPhi;
  Double_t      s;
  PropagateRet  ret, ret1;
  Double_t      zCyl = pCur->outerZ;
  Double_t      rCylIn = pCur->innerR;
  Double_t      rCylOut = pCur->outerR;

  // Perhaps only for diagnostic purposes, keep coordinates of 
  // "best" intersection point with inner and outer cylinders.
  Double_t      xInner, yInner, xOuter, yOuter;
  Double_t      xSol[4], ySol[4];

  if (q == 0) return INTERSECT_ERROR;
  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.
  ret1 = HelixPath(pCur->field, A_LITTLE_WAYS);

  if (!checkInside(pCur))   return INTERSECT_ERROR;
  else Update();

  iMomPhi = TMath::ATan2(iM[1], iM[0]);

  // Which endcap, if any, are we headed towards?  Find s for endcap hit.
  Double_t  sEndcap, sBarrelOut, sBarrelIn;
  Double_t  smallSinLambda = zCyl / SBIG;

  if (sinLambda > smallSinLambda) {
    ret = INTERSECT_ZPLUS;
    sEndcap = (zCyl - iP[2]) / sinLambda;
  }
  else if (sinLambda < -smallSinLambda) {
    ret = INTERSECT_ZMINUS;
    sEndcap = -(zCyl + iP[2]) / sinLambda;
  }  
  else {   // we're headed for barrel, but set variables to something anyway
    sEndcap = SBIG;
  }

  // Find s for outer barrel
  { 
    Int_t nSol = circleIntersect(rCylOut, xSol, ySol);

    switch (nSol) {
    case 0: 
    case 1:
      sBarrelOut = SBIG;
      break;
    case 2:   // Bunch of stuff to do here
      {
	Double_t  delPhi0, delPhi1;
	delPhi0 = phaseChange(xSol[0], ySol[0]);
	delPhi1 = phaseChange(xSol[1], ySol[1]);

	if (delPhi1 < delPhi0) delPhi0 = delPhi1;
	// Now use this to calculate path length

	sBarrelOut = delPhi0 * rad / cosLambda;
	break;
      }
    default:  // anything else is screwy
      printf(" SmearTraj::HelixToCyl: Bad # intersections (%d) with outer volumen", nSol);
      return INTERSECT_ERROR;
      break;
    }
  }

  // .. and similarly for inner barrel -- if rCylIn isn't zero
  if (rCylIn > .01)   { 
    Int_t nSol = circleIntersect(rCylIn, xSol, ySol);

    switch (nSol) {
    case 0: 
    case 1:
      sBarrelIn = SBIG;
      break;
    case 2:   // Bunch of stuff to do here
      {
	Double_t  delPhi0, delPhi1;
	delPhi0 = phaseChange(xSol[0], ySol[0]);
	delPhi1 = phaseChange(xSol[1], ySol[1]);

	if (delPhi1 < delPhi0) delPhi0 = delPhi1;
	// Now use this to calculate path length

	sBarrelIn = delPhi0 * rad / cosLambda;
	break;
      }
    default:  // anything else is screwy
      printf(" SmearTraj::HelixToCyl: Bad # intersections (%d) with inner volumen", nSol);
      return INTERSECT_ERROR;
      break;
    }
  }
  else sBarrelIn = SBIG;

  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
  Double_t  phiMom = s * cosLambda / radQ - iMomPhi;
  newP[0] = xCenter + radQ * TMath::Sin(phiMom);  // x
  newP[1] = yCenter + radQ * TMath::Cos(phiMom);
  newP[2] = iP[2] + s * sinLambda;

  deltaS = s;
  *pS = s + A_LITTLE_WAYS;
  
  // Momemtum vector

  newM[0] = momMag * cosLambda * TMath::Cos(phiMom);        // x
  newM[1] = -momMag * cosLambda * TMath::Sin(phiMom); // y
  newM[2] = iM[2];                              // z - doesn't change

  return ret;
}

SmearTraj::PropagateRet SmearTraj::LineToCyl(SmearVolume *pCur, Double_t *pS)
{
  // Propagate along straight line to cylinder.

  Double_t xMom, yMom, zMom;
  Double_t ix, iy, iz;
  Double_t x, y, z;
  Double_t zCyl = pCur->outerZ;
  Double_t rCylIn = pCur->innerR;
  Double_t rCylOut = pCur->outerR;

  static const Double_t SMALL_MOM = .00001;
  PropagateRet  ret, ret1;
  Double_t delX, delY, delZ;
  Double_t sEnd, sIn, sOut, s;

  *pS = 0.0;

  // Must start out inside
  ret1 = LinePath(A_LITTLE_WAYS);
  if (!checkInside(pCur)) {
    return INTERSECT_ERROR;
  }
  else Update();

  newM[0] = xMom = iM[0]; 
  newM[1] = yMom = iM[1]; 
  newM[2] = zMom = iM[2]; 

  if (xMom*xMom + yMom*yMom + zMom*zMom < .000001) return INTERSECT_NONE;

  ix = iP[0]; iy = iP[1]; iz = iP[2];

  ret = INTERSECT_NONE;

  // First check to see if we hit an endcap
  if (TMath::Abs(zMom) > SMALL_MOM) { // worth computing

    if (zMom < -SMALL_MOM) { 
      delZ = -zCyl - iz;
      ret = INTERSECT_ZMINUS;
    }
    else {
      delZ = zCyl - iz;
      ret = INTERSECT_ZPLUS;
    }
    delX =  delZ * xMom / zMom;
    delY =  delZ * yMom / zMom;
    sEnd = TMath::Sqrt(delX*delX + delY*delY + delZ*delZ);
  }
  else sEnd = SBIG;

  if (xMom*xMom + yMom*yMom < SMALL_MOM*SMALL_MOM) {
    sIn = sOut = SBIG;
  } 
  else { // Compute barrel stuff
    Double_t t;
    Bool_t   quadStatus;
    if (rCylIn <= 0.0) {  // No inner barrel.  We have solid cylinder.
      sIn = SBIG;
    }
    //    else { // if radius isn't decreasing, can't possibly go inwards
    //if (x0 * xMom + y0 * yMom <= 0.0) sIn = SBIG;
    else { 
      // 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(xMom * xMom + yMom * yMom,
		   2.0 * (xMom * ix + yMom * iy),
		   ix * ix + iy * iy - rCylIn*rCylIn, &quadStatus);

      if ((quadStatus) && (t > 0.0)) { // OK
	delX = t * xMom;
	delY = t * yMom;
	delZ = t * zMom;
	sIn = TMath::Sqrt(delX*delX + delY*delY + delZ*delZ);
      }
      else sIn = SBIG;
    }        // Done with inner barrel
    // Do same for outer barrel
    t = QuadRoot(xMom * xMom + yMom * yMom,
		 2.0 * (xMom * iy + yMom * ix),
		 ix * ix + iy * iy - rCylOut*rCylOut, &quadStatus);

    if ((quadStatus) && (t > 0.0)) { // OK
      delX = t * xMom;
      delY = t * yMom;
      delZ = t * zMom;
      sOut = TMath::Sqrt(delX*delX + delY*delY + delZ*delZ);
    }
    else sOut = SBIG;
  }

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

  // Good status.  Fill return arguments
  Double_t  absMom = TMath::Sqrt(xMom*xMom + yMom*yMom + zMom*zMom);
  delX = s * xMom / absMom;
  delY = s * yMom / absMom;
  delZ = s * zMom / absMom;

  newP[0] = ix + delX;
  newP[1]  = iy + delY;
  newP[2] = iz + delZ;

  deltaS = s;
  *pS = s + A_LITTLE_WAYS;

  return ret;
}



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.