// $Header: SmearTraj.cxx $
// For V3.0 of FastMC, April 2, 1999 J. Bogart
//
// Many thanks to Rasmus Munk Larsen of Stanford for the numerically-stable
// algorithm for finding the intersection of 2 circles.
#include "TMath.h"
#include <float.h> // for DBL_EPSILON
#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 = 0.0;
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 = pythagPlus(iM[0], iM[1]);
sinLambda = iM[2] / momMag;
cosLambda = xyMomMag / momMag;
if (q == 0) {
// propagate helix stuff is irrelevant
xCenter = yCenter = rad = radQ = 0;
}
else {
if (cosLambda < VERY_SMALL_COSLAMBDA) { // essentially just moves in z
xCenter = iP[0];
yCenter = iP[1];
radQ = 0;
}
else {
Double_t fieldCnvQ = fieldCnv * q;
radQ = xyMomMag / fieldCnvQ;
rad = TMath::Abs(radQ);
// xCenter = iP[0] + radQ * iM[1] / xyMomMag;
xCenter = iP[0] + iM[1] / fieldCnvQ;
// yCenter = iP[1] - radQ * iM[0] / xyMomMag;
yCenter = iP[1] - iM[0] / fieldCnvQ;
}
}
}
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 p1[], // (x,y) of 1st solution
Double_t p2[]) // (x,y) of 2nd solution
{
// 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 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 x0 = xCenter;
Double_t y0 = yCenter;
Double_t r2 = rad; // radius of helix
Double_t r1 = rv; // radius of detector volume
Int_t nGood = 0;
Double_t d; // distance between circle centers
Double_t h; // distance from point of intersection of the two circles
// to line between centers
Double_t l; // distance from origin to intersection of two perp. lines:
// the line between centers and the line connecting two
// intersections (solutions)
Double_t v1, v2; // (v1, v2) is unit vector along line connecting centers.
Double_t mag; // scale factor used in checking for solutions
// Check for negative radii.
if (r1<=0.0 || r2<=0.0) {
fprintf(stderr,"Negative radii passed to circle_intersect:");
fprintf(stderr,"r1 = %g, r2=%gn", r1, r2);
return -2;
}
// Now decide how many points of intersection there are:
if ( x0==0 && y0==0 && r1==r2)
return -1; // The two circles are identical!
d = pythagPlus(x0,y0);
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!
v1 = x0/d;
v2 = y0/d;
p1[0] = r1*v1;
p2[0] = r1*v2;
nGood = 1;
}
else {
// Two points of intersection!
l = (d + (r1*(r1/d) - r2*(r2/d)))/2;
h = pythagMinus(r1, l);
v1 = x0/d;
v2 = y0/d;
// First point (x1,y1):
p1[0] = l*v1 + h*v2;
p1[1] = l*v2 - h*v1;
// Second point (x2,y2):
p2[0] = l*v1 - h*v2;
p2[1] = l*v2 + h*v1;
nGood = 2;
}
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" in a positive field
delPhi = newPhi - iniPhi; // assume forward
if ((q * fieldCnv) > 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 sol1[2], sol2[2];
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, sol1, sol2);
switch (nSol) {
case 0:
case 1:
sBarrelOut = SBIG;
break;
case 2: // Bunch of stuff to do here
{
Double_t delPhi0, delPhi1;
delPhi0 = phaseChange(sol1[0], sol1[1]);
delPhi1 = phaseChange(sol2[0], sol2[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, sol1, sol2);
switch (nSol) {
case 0:
case 1:
sBarrelIn = SBIG;
break;
case 2: // Bunch of stuff to do here
{
Double_t delPhi0, delPhi1;
delPhi0 = phaseChange(sol1[0], sol1[1]);
delPhi1 = phaseChange(sol2[0], sol2[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;
}
Double_t SmearTraj::pythagPlus(const Double_t x, Double_t y) {
// Compute sqrt(x*x + y*y) in maximally stable fashion
double t, p;
if (TMath::Abs(x) > TMath::Abs(y)) {
t = y/x;
p = x * TMath::Sqrt(1.0 + t*t);
}
else {
t = x/y;
p = y*TMath::Sqrt(t*t + 1.0);
}
// return p;
return TMath::Abs(p);
}
Double_t SmearTraj::pythagMinus(const Double_t x, Double_t y) {
// Compute sqrt(x*x - y*y) maximally stable fashion
double t, p;
if (TMath::Abs(x) > TMath::Abs(y)) {
t = y/x;
p = x * TMath::Sqrt(1.0 - t*t);
}
else {
t = x/y;
p = y*TMath::Sqrt(t*t - 1.0);
}
// return p;
return TMath::Abs(p);
}
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.