View Javadoc

1   package org.lcsim.util.swim;
2   
3   import static java.lang.Math.abs;
4   import static java.lang.Math.asin;
5   import static java.lang.Math.atan2;
6   import static java.lang.Math.cos;
7   import static java.lang.Math.signum;
8   import static java.lang.Math.sin;
9   import static java.lang.Math.sqrt;
10  import hep.physics.vec.BasicHep3Vector;
11  import hep.physics.vec.Hep3Vector;
12  import hep.physics.vec.VecOp;
13  
14  import org.lcsim.spacegeom.CartesianPoint;
15  import org.lcsim.spacegeom.CartesianVector;
16  import org.lcsim.spacegeom.SpacePoint;
17  import org.lcsim.spacegeom.SpaceVector;
18  
19  /**
20   * This class represents a helix with its axis aligned along Z. All quantities
21   * in this class are dimensionless. It has no dependencies except for Hep3Vector
22   * (which could easily be removed).
23   * <p>
24   * For more info on swimming see <a href="doc-files/transport.pdf">this paper</a>
25   * by Paul Avery.
26   * 
27   * @author tonyj
28   * @version $Id: Helix.java,v 1.30 2010/04/29 13:41:37 cassell Exp $
29   */
30  public class Helix implements Trajectory {
31      /**
32       * Creates a new instance of Helix. Parameters according to <a
33       * href="doc-files/L3_helix.pdf">the L3 conventions</a><br />
34       * Please also have a look at <img src="doc-files/Helix-1.png"
35       * alt="Helix-1.png"> <img src="doc-files/Helix-2.png" alt="Helix-2.png">
36       * 
37       * @param origin
38       *                A point on the helix
39       * @param radius
40       *                The <em>signed</em> radius of curvature of the helix.
41       *                The conventions is such that for <em>positive</em>
42       *                radii, the direction is <em>clockwise</em>.
43       * @param phi
44       *                The polar angle of the helix <em>momentum</em> in x-y
45       *                plane w/rt positive x-axis at the origin
46       * @param lambda
47       *                The dip angle w/rt positive part of the x-y plane
48       */
49      public Helix(Hep3Vector org, double r, double p, double lambda) {
50          // if (abs(lambda) > PI/2.)
51          // throw new IllegalArgumentException("lambda = " + lambda + " is
52          // outside of -PI/2<lambda<PI/2");
53          origin = org;
54          radius = r;
55          phi = p;
56  
57          // Calculate some useful quantities
58          cosPhi = cos(phi);
59          sinPhi = sin(phi);
60          cosLambda = cos(lambda);
61          sinLambda = sin(lambda);
62          xCenter = origin.x() + radius * sinPhi;
63          yCenter = origin.y() - radius * cosPhi;
64          setSpatialParameters();
65      }
66  
67      /**
68       * returns a point on the Helix at a distance alpha from the origin along
69       * the trajectory. alpha == 2*PI*radius/cos(lambda) is one rotation in the
70       * x-y plane
71       */
72      public SpacePoint getPointAtDistance(double alpha) {
73          double darg = alpha * cosLambda / radius - phi;
74          double x = xCenter + radius * sin(darg);
75          double y = yCenter + radius * cos(darg);
76          double z = origin.z() + alpha * sinLambda;
77          return new CartesianPoint(x, y, z);
78      }
79  
80      public double getRadius() {
81          return radius;
82      }
83  
84      public Hep3Vector getCenterXY() {
85          return new BasicHep3Vector(xCenter, yCenter, 0);
86      }
87  
88      public double getDistanceToZPlane(double z) {
89          return (z - origin.z()) / sinLambda;
90      }
91  
92      /**
93       * Calculates the distance along the Helix until it hits a cylinder centered
94       * along z
95       * 
96       * @param r
97       *                the radius of the cylinder
98       * @return the distance along the trajectory or Double.NAN if the cylinder
99       *         can never be hit
100      */
101     public double getDistanceToInfiniteCylinder(double r) {
102         double phiToCenter = atan2(yCenter, xCenter);
103         double radiusOfCenter = sqrt(xCenter * xCenter + yCenter * yCenter);
104         // Negative radius doesn't make sense
105         if (r < 0)
106             throw new IllegalArgumentException("radius " + r + "<0");
107         double darg = r * r / (2. * radius * radiusOfCenter) - radiusOfCenter
108                 / (2. * radius) - radius / (2. * radiusOfCenter);
109         double deltaphi = phi - phiToCenter;
110         if (deltaphi < -Math.PI)
111             deltaphi += 2. * Math.PI;
112         if (deltaphi > Math.PI)
113             deltaphi -= 2. * Math.PI;
114         double diff = asin(darg) + deltaphi;
115         double result = (radius / cosLambda) * diff;
116         while (result < 0)
117             result += Math.abs(radius / cosLambda) * 2 * Math.PI;
118         return result;
119     }
120     public double getDistanceToPolyhedra(double r, int nsides)
121     {
122         double mins = 9999999.;
123         double period = Math.abs(2.*Math.PI*radius/cosLambda);
124         for(int i=0;i<nsides;i++)
125         {
126             double dphi = i*2.*Math.PI/nsides;
127             double beta = (r - Math.cos(dphi)*xCenter - Math.sin(dphi)*yCenter)/radius;
128             if(Math.abs(beta) <= 1.)
129             {
130                 double s1 = radius/cosLambda*(Math.asin(beta) - dphi + phi);
131                 double s2 = radius/cosLambda*(Math.PI - Math.asin(beta) - dphi + phi);
132                 while(s1 < 0.){s1 += period;}
133                 while(s2 < 0.){s2 += period;}
134                 s1 = s1%period;
135                 s2 = s2%period;
136                 double s = Math.min(s1,s2);
137                 if(s1 < mins)mins = s1;
138             }
139         }
140         if(mins == 9999999.)return Double.NaN;
141         return mins;
142     }
143 
144     public double getDistanceToPoint(Hep3Vector point) {
145 
146         // Set starting position and direction unit vector
147         Hep3Vector pos = new BasicHep3Vector(origin.v());
148         Hep3Vector u = VecOp.unit(new BasicHep3Vector(px, py, pz));
149         Hep3Vector zhat = new BasicHep3Vector(0., 0., 1.);
150 
151         //  First estimate distance using z coordinate of the point
152         double s = 0.;
153         if(Math.abs(sinLambda) > .00001)s = getDistanceToZPlane(point.z());
154         double stot = s;
155 
156         //  Propagate to the estimated point
157         Hep3Vector unew = propagateDirection(u, s);
158         Hep3Vector posnew = propagatePosition(pos, u, s);
159         u = unew;
160         pos = posnew;
161         
162         //  Calculate how far we are from the desired point
163         Hep3Vector delta = VecOp.sub(pos, point);
164 
165         int count = 0;
166         int maxcount = 20;
167         //  Iteratively close in on the point of closest approach
168         while (delta.magnitude() > eps) {
169             count++;
170             //  Calculate the coefficients of the indicial equations a*s^2 + b*s + c = 0
171             double c = VecOp.dot(u, delta);
172             double b = 1. - rho * VecOp.dot(VecOp.cross(u, zhat), delta);
173             double a = -0.5 * rho*rho * (c - delta.z()*u.z());
174 
175             //  Find the two solutions
176             double arg = b*b - 4 * a * c;
177             double s1 = (-b + Math.sqrt(arg)) / (2. * a);
178             double s2 = (-b - Math.sqrt(arg)) / (2. * a);
179 
180             //  Find the position and distance from desired point for both solutions
181             Hep3Vector pos1 = propagatePosition(pos, u, s1);
182             double d1 = VecOp.sub(pos1, point).magnitude();
183             Hep3Vector pos2 = propagatePosition(pos, u, s2);
184             double d2 = VecOp.sub(pos2, point).magnitude();
185 
186             //  Pick the closest solution and update the position, direction unit vector, and
187             //  path length.  If the change in position is small, we have converged.
188             if (d1 < d2) {
189                 unew = propagateDirection(u, s1);
190                 u = unew;
191                 pos = pos1;
192                 stot += s1;
193                 if (Math.abs(s1) < eps) return stot;
194             } else {
195                 unew = propagateDirection(u, s2);
196                 u = unew;
197                 pos = pos2;
198                 stot += s2;
199                 if (Math.abs(s2) < eps) return stot;
200             }
201 
202             if(count > maxcount)return stot;
203             //  Update how far we are from the specified point
204             delta = VecOp.sub(pos, point);
205         }
206 
207         //  If we get here, we found a point within the desired precision
208         return stot;
209     }
210 
211     /**
212      * Swims the helix along its trajectory to the point of closest approach to
213      * the given SpacePoint. For more info on swimming see <a
214      * href=doc-files/fitting/transport.pdf> Paul Avery's excellent text</a>
215      * 
216      * @param point
217      *                Point in Space to swim to.
218      * @return the length Parameter alpha
219      */
220     public double getDistanceToXYPosition(Hep3Vector point) {
221         double tanLambda = sinLambda / cosLambda;
222         double pMag = sqrt(px * px + py * py + pz * pz);
223         Hep3Vector p0 = new BasicHep3Vector(px, py, pz);
224         Hep3Vector pCrossB = new BasicHep3Vector(py, -px, 0);
225 
226         // first, the point needs to be translated into the first period
227         Hep3Vector xDiff = VecOp.sub(origin, point);
228         int addedQuarterPeriods = 0;
229         if (abs(tanLambda) > 1e-10) {
230             double zPos = xDiff.z();
231             while (abs(zPos) > abs(radius * tanLambda * Math.PI)) {
232                 zPos -= signum(zPos) * abs(radius * tanLambda * Math.PI);
233                 ++addedQuarterPeriods;
234             }
235             // Make sure the helix is in the right quadrant for the atan
236             if (zPos > 0 && addedQuarterPeriods > 0)
237                 addedQuarterPeriods *= -1;
238             if (addedQuarterPeriods % 2 != 0)
239                 addedQuarterPeriods += signum(addedQuarterPeriods);
240             xDiff = new BasicHep3Vector(xDiff.x(), xDiff.y(), zPos);
241         }
242         double factorA1 = pMag - pz * pz / pMag - (VecOp.dot(xDiff, pCrossB))
243                 * rho;
244         double factorA2 = (VecOp.dot(xDiff, p0) - xDiff.z() * pz) * rho;
245         // System.err.print("addedQuarterPeriods: " + addedQuarterPeriods);
246         // System.err.printf("result:%.3f + %.3f\n",
247         // addedQuarterPeriods*(radius/cosLambda*Math.PI/2),
248         // Math.atan(factorA2/factorA1) / -rho);
249         return addedQuarterPeriods * abs(radius / cosLambda * Math.PI)
250                 - Math.atan2(factorA2, factorA1) / rho;
251     }
252 
253     /**
254      * Calculates the <em>signed</em> distance in mm between the Helix and an
255      * arbitrary point in Space
256      * 
257      * @param point
258      *                the point in space to calculate the distance to
259      * @return the distance in mm between the point and the helix at the point
260      *         of closest approach
261      */
262     public double getSignedClosestDifferenceToPoint(Hep3Vector point) {
263         double tanLambda = sinLambda / cosLambda;
264         Hep3Vector pCrossB = new BasicHep3Vector(py, -px, 0);
265         Hep3Vector xDiff = VecOp.sub(origin, point);
266         double pMag = sqrt(px * px + py * py + pz * pz);
267 
268         // translate along Z because algorithm can handle only numbers in the
269         // first quadrant
270         double zPos = xDiff.z();
271         zPos = Math.IEEEremainder(zPos, abs(radius * tanLambda * Math.PI / 4));
272         if (zPos < 0) zPos += abs(radius * tanLambda * Math.PI / 4);
273 
274         if (zPos/abs(radius * tanLambda * Math.PI / 4) < 0 || zPos/abs(radius * tanLambda * Math.PI / 4) > 1)
275         {
276             System.out.println("Valid range of zPos/abs(radius*tanLambda*Math.PI/4) [0 and 1], value is: "+zPos/abs(radius * tanLambda * Math.PI / 4));
277         }
278         
279         xDiff = new BasicHep3Vector(xDiff.x(), xDiff.y(), zPos);
280 
281         double numerator = (-2 * VecOp.dot(xDiff, pCrossB) + pMag * rho
282                 * (xDiff.magnitudeSquared() - xDiff.z() * xDiff.z()))
283                 / radius;
284         double denominator = 1 + sqrt(1 - 2 * rho * pMag
285                 * VecOp.dot(xDiff, pCrossB) / radius / radius + pMag * pMag
286                 * rho * rho
287                 * (xDiff.magnitudeSquared() - xDiff.z() * xDiff.z()) / radius
288                 / radius);
289         return numerator / denominator;
290     }
291 
292     // Returns the "momentum" at the length s from the starting point.
293     // This uses the definition in
294     // http://www.phys.ufl.edu/~avery/fitting/transport.pdf
295     public Hep3Vector getTangentAtDistance(double alpha) {
296         double p0x = px * cos(rho * alpha) - py * sin(rho * alpha);
297         double p0y = py * cos(rho * alpha) + px * sin(rho * alpha);
298         double p0z = pz;
299         return new BasicHep3Vector(p0x, p0y, p0z);
300     }
301 
302     // added by Nick Sinev
303     public double getSecondDistanceToInfiniteCylinder(double r) {
304         double result = getDistanceToInfiniteCylinder(r);
305         SpacePoint first = getPointAtDistance(result);
306         double angto0 = Math.atan2(-yCenter, -xCenter);
307         double angtofirst = Math
308                 .atan2(first.y() - yCenter, first.x() - xCenter);
309         double dang = angtofirst - angto0;
310         if (dang < -Math.PI)
311             dang += 2. * Math.PI;
312         if (dang > Math.PI)
313             dang -= 2. * Math.PI;
314         double angofarc = 2. * (Math.PI - Math.abs(dang));
315         double arc = Math.abs(radius) * angofarc / cosLambda;
316         return result + arc;
317     }
318 
319     public double getZPeriod() {
320         return Math.PI * radius * 2. * sinLambda / cosLambda;
321     }
322 
323     /**
324      * Sets the parameterization in terms of "momentum" and charge
325      * 
326      */
327     private void setSpatialParameters() {
328         abs_r = abs(radius);
329         px = abs_r * cosPhi;
330         py = abs_r * sinPhi;
331         pz = abs_r * sinLambda / cosLambda;
332         rho = -cosLambda / radius;
333     }
334 
335     /**
336      * Propagates the direction unit vector a distance s along the helix
337      * 
338      * @param u initial direction unit vector
339      * @param s distance to propagate
340      * @return propagated direction unit vector
341      */
342     private Hep3Vector propagateDirection(Hep3Vector u, double s) {
343         double ux = u.x();
344         double uy = u.y();
345         double uz = u.z();
346         double carg = Math.cos(rho * s);
347         double sarg = Math.sin(rho * s);
348         double uxnew = ux * carg -uy * sarg;
349         double uynew = uy * carg + ux * sarg;
350         double uznew = uz;
351         return new BasicHep3Vector (uxnew, uynew, uznew);
352     }
353 
354     /**
355      * Propagate a point on the helix by a specified distance
356      *
357      * @param pos starting position
358      * @param u starting direction unit vector
359      * @param s distance to propagate
360      * @return propagated point on helix
361      */
362     private Hep3Vector propagatePosition(Hep3Vector pos, Hep3Vector u, double s) {
363         double x = pos.x();
364         double y = pos.y();
365         double z = pos.z();
366         double ux = u.x();
367         double uy = u.y();
368         double uz = u.z();
369         double carg = Math.cos(rho * s);
370         double sarg = Math.sin(rho * s);
371         double xnew = x + ux * sarg / rho - uy * (1 - carg) / rho;
372         double ynew = y + uy * sarg / rho + ux * (1 - carg) / rho;
373         double znew = z + uz * s;
374         return new BasicHep3Vector(xnew, ynew, znew);
375     }
376 
377     /**
378      * @param alpha
379      *                The distance along the trajectory in the x-y plane
380      * @return The unit vector of the momentum
381      */
382     public SpaceVector getUnitTangentAtLength(double alpha) {
383         double angle = phi + alpha * rho;
384         return new CartesianVector(cos(angle), sin(angle), sinLambda
385                 / cosLambda);
386     }
387 
388     Hep3Vector origin;
389     double xCenter;
390     double yCenter;
391     protected double radius;
392     protected double sinLambda;
393     protected double cosLambda;
394     protected double sinPhi;
395     protected double cosPhi;
396     protected double phi;
397 
398     // parameterization in terms of 'momentum'
399     // A helix is a mathematical object and doesn't have "momentum",
400     // but unfortunately some of the used algorithms are expressed in terms of
401     // it.
402     // That's OK, it's a private variable.
403     private double px;
404     private double py;
405     private double pz;
406     private double abs_r;
407     private double rho;
408 
409     //  Set the desired precision in finding the point closest to the track
410     private double eps = 1.e-6;  //  1 nm ought to be good enough for government work!
411     public void setExtrapToPointPrecision(double prec){eps = prec;}
412 }