View Javadoc

1   package org.lcsim.recon.tracking.trfcyl;
2   import org.lcsim.recon.tracking.trfutil.TRFMath;
3   import org.lcsim.recon.tracking.trfutil.Assert;
4   
5   import org.lcsim.recon.tracking.trfbase.TrackDerivative;
6   import org.lcsim.recon.tracking.trfbase.TrackVector;
7   import org.lcsim.recon.tracking.trfbase.Propagator;
8   import org.lcsim.recon.tracking.trfbase.PropDirected;
9   import org.lcsim.recon.tracking.trfbase.PropDir;
10  import org.lcsim.recon.tracking.trfbase.PropStat;
11  import org.lcsim.recon.tracking.trfbase.Surface;
12  import org.lcsim.recon.tracking.trfbase.VTrack;
13  import org.lcsim.recon.tracking.trfbase.ETrack;
14  /**
15   * Propagates tracks from one cylinder to another in a constant solenoidal magnetic field.
16   *<p>
17   * Propagation will fail if either the origin or destination is
18   * not a cylinder.
19   *<p>
20   * Propagation more than halfway around one loop is not allowed and
21   * will result in failure if attempted.
22   *
23   *@author Norman A. Graf
24   *@version 1.0
25   *
26   */
27  public class PropCyl extends PropDirected
28  {
29      
30      private static final int IPHI = SurfCylinder.IPHI;
31      private static final int IZ   = SurfCylinder.IZ;
32      private static final int IALF = SurfCylinder.IALF;
33      private static final int ITLM = SurfCylinder.ITLM;
34      private static final int IQPT  = SurfCylinder.IQPT;
35      
36      // attributes
37      // Factor connecting curvature to momentum:
38      // C = 1/Rc = BFAC * B * q/pT
39      // where B = magnetic field in Tesla, q = charge in natural units
40      // (electron has -1) and pT = transverse momentum in GeV/c.
41      
42      // BFAC * bfield
43      private double _bfac;
44      
45      //
46      
47      /**
48       *Construct an instance from a constant solenoidal magnetic field in Tesla.
49       *
50       * @param   bfield The magnetic field strength in Tesla.
51       */
52      public PropCyl(double bfield)
53      {
54          _bfac = TRFMath.BFAC*bfield;
55      }
56      
57      //
58      
59      
60      /**
61       *Clone an instance.
62       *
63       * @return A Clone of this instance.
64       */
65      public Propagator newPropagator()
66      {
67          return new PropCyl( bField() );
68      }
69      
70      // print
71      
72      /**
73       *output stream
74       *
75       * @return  A String representation of this instance.
76       */
77      public String toString()
78      {
79          return "Cylinder propagation with constant "
80                  + bField() + " Tesla field \n";
81      }
82      //
83      //
84      
85      /**
86       *Propagate a track without error in the specified direction.
87       *
88       * The track parameters for a cylinder are:
89       * phi z alpha tan(lambda) curvature
90       *
91       * @param   trv The VTrack to propagate.
92       * @param   srf The Surface to which to propagate.
93       * @param   dir The direction in which to propagate.
94       * @return The propagation status.
95       */
96      public PropStat vecDirProp(VTrack trv, Surface srf, PropDir dir)
97      {
98          TrackDerivative deriv = null;
99          return vecDirProp(trv, srf, dir, deriv);
100     }
101     
102     
103     /**
104      *Propagate a track without error in the specified direction
105      *and return the derivative matrix in deriv.
106      *
107      * The track parameters for a cylinder are:
108      * phi z alpha tan(lambda) curvature
109      *
110      * @param   trv The VTrack to propagate.
111      * @param   srf The Surface to which to propagate.
112      * @param   dir The direction in which to propagate.
113      * @param   deriv The track derivatives to update at the surface srf.
114      * @return The propagation status.
115      */
116     public PropStat vecDirProp(VTrack trv, Surface srf, PropDir dir, TrackDerivative deriv)
117     {
118         
119         // construct return status
120         PropStat pstat = new PropStat();
121         
122         // fetch the originating surface and vector
123         Surface srf1 = trv.surface();
124         TrackVector vec1 = trv.vector();
125         
126         // Check origin is a cylinder.
127         //need checks here
128         Assert.assertTrue( srf1.pureType().equals(SurfCylinder.staticType()) );
129         if ( !srf1.pureType( ).equals(SurfCylinder.staticType()) )
130             return pstat;
131         SurfCylinder scy1 = (SurfCylinder) srf1;
132         // Check destination is a cylinder.
133         Assert.assertTrue( srf.pureType().equals(SurfCylinder.staticType()) );
134         if (! srf.pureType( ).equals(SurfCylinder.staticType()) )
135             return pstat;
136         SurfCylinder scy2 = ( SurfCylinder) srf;
137         // Separate the move status from the direction.
138         //need a clone here...
139         //PropDir rdir = new PropDir(dir);
140         PropDir rdir = dir;
141         boolean move = reduceDirection(rdir);
142         if(move) rdir = reduce(rdir);
143         // If surfaces are the same, we can return now.
144         if ( srf.pureEqual(srf1) && !move )
145         {
146             
147             if ( deriv != null )
148             {
149                 
150                 deriv.setIdentity();
151                 
152             }
153             
154             pstat.setSame();
155             return pstat;
156         }
157         
158         // Fetch the radii and the starting track vector.
159         int irad = SurfCylinder.RADIUS;
160         double r1 = scy1.parameter(irad);
161         double r2 = scy2.parameter(irad);
162         TrackVector vec = trv.vector();
163         double phi1 = vec.get(SurfCylinder.IPHI);               // phi position
164         double z1 = vec.get(SurfCylinder.IZ);                   // z
165         double alf1 = vec.get(SurfCylinder.IALF);               // phi_dir - phi_pos
166         double tlam1 = vec.get(SurfCylinder.ITLM);              // tan(lambda)
167         double qpt1 = vec.get(SurfCylinder.IQPT);               // q/pT
168         
169         // Check alpha range.
170         alf1 = TRFMath.fmod2( alf1, TRFMath.TWOPI );
171         Assert.assertTrue( Math.abs(alf1) <= Math.PI );
172         if ( trv.isForward() ) Assert.assertTrue( Math.abs(alf1) <= TRFMath.PI2 );
173         else Assert.assertTrue( Math.abs(alf1) > TRFMath.PI2 );
174         
175         // Calculate the cosine of lambda.
176         double clam1 = 1.0/Math.sqrt(1+tlam1*tlam1);
177         
178         // Calculate the curvature = _bfac*(q/pt)
179         double dcrv1_dqpt1 = _bfac;
180         double crv1 = dcrv1_dqpt1*qpt1;
181         double dcrv1_dtlam1 = 0.0;
182         
183         // Evaluate the new track vector.
184         
185         // lambda and curvature do not change
186         double tlam2 = tlam1;
187         double crv2 = crv1;
188         double qpt2 = qpt1;
189         
190         // We can evaluate sin(alf2), leaving two possibilities for alf2
191         // 1st solution: alf21, phi21, phid21, tht21
192         // 2nd solution: alf22, phi22, phid22, tht22
193         // evaluate phi2 to choose
194         double salf1 = Math.sin( alf1 );
195         double calf1 = Math.cos( alf1 );
196         double salf2 = r1/r2*salf1 + 0.5*crv1/r2*(r2*r2-r1*r1);
197         // If salf2 is close to 1 or -1, set it to that value.
198         double diff = Math.abs( Math.abs(salf2) - 1.0 );
199         if ( diff < 1.e-10 ) salf2 = salf2>0 ? 1.0 : -1.0;
200         // if salf2 > 1, track does not cross cylinder
201         if ( Math.abs(salf2) > 1.0 ) return pstat;
202         double alf21 = Math.asin( salf2 );
203         double alf22 = alf21>0 ? Math.PI-alf21 : -Math.PI-alf21;
204         double calf21 = Math.cos( alf21 );
205         double calf22 = Math.cos( alf22 );
206         double phi20 = phi1 + Math.atan2( salf1-r1*crv1, calf1 );
207         double phi21 = phi20 - Math.atan2( salf2-r2*crv2, calf21 );   // phi position
208         double phi22 = phi20 - Math.atan2( salf2-r2*crv2, calf22 );
209         // Construct an sT object for each solution.
210         STCalc sto1 = new STCalc(r1,phi1,alf1,crv1,r2,phi21,alf21);
211         STCalc sto2 = new STCalc(r1,phi1,alf1,crv1,r2,phi22,alf22);
212         // Check the two solutions are nonzero and have opposite sign
213         // or at least one is nonzero.
214         
215         // Extract the two sT-values.
216         double st1 = sto1.st();
217         double st2 = sto2.st();
218         double ast1 = Math.abs(st1);
219         double ast2 = Math.abs(st2);
220         
221         // If both solutions have the same sign, then the most distant one
222         // should actually go the other way along a full cicle minus the
223         // calculated value of s.  We discard this solution by setting it
224         // to zero.
225         if ( st1*st2 > 0.0 )
226         {
227             if ( ast1 > ast2 )
228             {
229                 st1 = 0.0;
230             }
231             else
232             {
233                 st2 = 0.0;
234             }
235         }
236         // Choose the correct solution
237         boolean use_first_solution;
238         
239         if (rdir.equals(PropDir.NEAREST))
240         {
241             use_first_solution = st1!=0 && (st2==0.0 || ast1<ast2);
242         }
243         else if (rdir.equals(PropDir.FORWARD))
244         {
245             if ( st1 > 0.0 )
246             {
247                 use_first_solution = true;
248             }
249             else if ( st2 > 0.0 )
250             {
251                 use_first_solution = false;
252             }
253             else
254             {
255                 return pstat;
256             }
257         }
258         else if (rdir.equals(PropDir.BACKWARD))
259         {
260             if ( st1 < 0.0 )
261             {
262                 use_first_solution = true;
263             }
264             else if ( st2 < 0.0 )
265             {
266                 use_first_solution = false;
267             }
268             else
269             {
270                 return pstat;
271             }
272         }
273         else
274         {
275             throw new IllegalArgumentException("PropCyl._vec_propagate: Unknown direction.");
276         }
277         // Assign phi2, alf2 and sto2 for the chosen solution.
278         double phi2, alf2;
279         STCalc sto;
280         double calf2;
281         if ( use_first_solution )
282         {
283             sto = sto1;
284             phi2 = phi21;
285             alf2 = alf21;
286             calf2 = calf21;
287         }
288         else
289         {
290             sto = sto2;
291             phi2 = phi22;
292             alf2 = alf22;
293             calf2 = calf22;
294         }
295         
296         // fetch sT.
297         double st = sto.st();
298         
299         if ( st == 0.0 )
300         {
301             return pstat;
302         }
303         
304         // use sT to evaluate z2
305         double z2 = z1 + tlam1*st;
306         
307         // Check alpha range.
308         Assert.assertTrue( Math.abs(alf2) <= Math.PI );
309         
310         // put new values in vec
311         vec.set(SurfCylinder.IPHI, phi2);
312         vec.set(SurfCylinder.IZ,   z2);
313         vec.set(SurfCylinder.IALF, alf2);
314         vec.set(SurfCylinder.ITLM, tlam2);
315         vec.set(SurfCylinder.IQPT, qpt2);
316         
317         // Update trv
318         trv.setSurface( srf );
319         trv.setVector(vec);
320         if ( Math.abs(alf2) <= TRFMath.PI2 ) trv.setForward();
321         else trv.setBackward();
322         
323         // Set the return status.
324         //st > 0 ? pstat.set_forward() : pstat.set_backward();
325         double s = st/clam1;
326         pstat.setPathDistance(s);
327         
328         // exit now if user did not ask for error matrix.
329         if ( deriv == null ) return pstat;
330         
331         // Calculate derivatives.
332         
333         // alpha_2
334         double da2da1 = r1*calf1/r2/calf2;
335         double da2dc1 = (r2*r2-r1*r1)*0.5/r2/calf2;
336         
337         // phi2
338         double rcsal1 = r1*crv1*salf1;
339         double rcsal2 = r2*crv2*salf2;
340         double den1 = 1.0 + r1*r1*crv1*crv1 - 2.0*rcsal1;
341         double den2 = 1.0 + r2*r2*crv2*crv2 - 2.0*rcsal2;
342         double dp2dp1 = 1.0;
343         double dp2da1 = (1.0-rcsal1)/den1 - (1.0-rcsal2)/den2*da2da1;
344         double dp2dc1 = -r1*calf1/den1 + r2*calf2/den2
345                 - (1.0-rcsal2)/den2*da2dc1;
346         
347         // z2
348         double dz2dz1 = 1.0;
349         double dz2dl1 = st;
350         double dz2da1 = tlam1*sto.d_st_dalf1(dp2da1, da2da1);
351         double dz2dc1 = tlam1*sto.d_st_dcrv1(dp2dc1, da2dc1);
352         
353         // Build derivative matrix.
354         
355         deriv.set(IPHI, IPHI ,  dp2dp1);
356         deriv.set(IPHI, IALF ,  dp2da1);
357         deriv.set(IPHI, ITLM ,  dp2dc1*dcrv1_dtlam1);
358         deriv.set(IPHI, IQPT ,  dp2dc1*dcrv1_dqpt1);
359         deriv.set(IZ, IZ   ,  dz2dz1);
360         deriv.set(IZ, IALF ,  dz2da1);
361         deriv.set(IZ, ITLM ,  dz2dl1 + dz2dc1*dcrv1_dtlam1);
362         deriv.set(IZ, IQPT ,  dz2dc1*dcrv1_dqpt1);
363         deriv.set(IALF, IALF ,  da2da1);
364         deriv.set(IALF, ITLM ,  da2dc1*dcrv1_dtlam1);
365         deriv.set(IALF, IQPT ,  da2dc1*dcrv1_dqpt1);
366         deriv.set(ITLM, ITLM ,  1.0);
367         deriv.set(IQPT, IQPT ,  1.0);
368         return pstat;
369     }
370     
371     
372     
373     //
374     
375     /**
376      *Return the strength of the magnetic field in Tesla.
377      *
378      * @return The strength of the magnetic field in Tesla.
379      */
380     public double bField()
381     {
382         return _bfac/TRFMath.BFAC;
383     }
384     
385     // Private class STCalc.
386     //
387     // An STCalc object calculates sT (the signed transverse path length)
388     // and its derivatives w.r.t. alf1 and crv1.  It is constructed from
389     // the starting (r1, phi1, alf1, crv1) and final track parameters
390     // (r2, phi2, alf2) assuming these are consistent.  Methods are
391     // provided to retrieve sT and the two derivatives.
392     //
393     class STCalc
394     {
395         
396         private boolean _big_crv;
397         private double _st;
398         private double _dst_dphi21;
399         private double _dst_dcrv1;
400         public double _crv1;
401         
402         // constructor
403         public STCalc()
404         {
405         }
406         public STCalc(double r1, double phi1, double alf1, double crv1,
407                 double r2, double phi2, double alf2)
408         {
409             _crv1 = crv1;
410             Assert.assertTrue( r1 > 0.0 );
411             Assert.assertTrue( r2 > 0.0 );
412             double rmax = r1+r2;
413             
414             // Calculate the change in xy direction
415             double phi_dir_diff = TRFMath.fmod2(phi2+alf2-phi1-alf1,TRFMath.TWOPI);
416             Assert.assertTrue( Math.abs(phi_dir_diff) <= Math.PI );
417             
418             // Evaluate whether |C| is" big"
419             _big_crv = rmax*Math.abs(crv1) > 0.001;
420             
421             // If the curvature is big we can use
422             // sT = (phi_dir2 - phi_dir1)/crv1
423             if ( _big_crv )
424             {
425                 Assert.assertTrue( crv1 != 0.0 );
426                 _st = phi_dir_diff/crv1;
427             }
428             
429             // Otherwise, we calculate the straight-line distance
430             // between the points and use an approximate correction
431             // for the (small) curvature.
432             else
433             {
434                 
435                 // evaluate the distance
436                 double d = Math.sqrt( r1*r1 + r2*r2 - 2.0*r1*r2*Math.cos(phi2-phi1) );
437                 double arg = 0.5*d*crv1;
438                 double arg2 = arg*arg;
439                 double st_minus_d = d*arg2*( 1.0/6.0 + 3.0/40.0*arg2 );
440                 _st = d + st_minus_d;
441                 
442                 // evaluate the sign
443                 // We define a metric xsign = abs( (dphid-d*C)/(d*C) ).
444                 // Because sT*C = dphid and d = abs(sT):
445                 // xsign = 0 for sT > 0
446                 // xsign = 2 for sT < 0
447                 // Numerical roundoff will smear these predictions.
448                 double sign = 0.0;
449                 if ( crv1!= 0. )
450                 {
451                     double xsign = Math.abs( (phi_dir_diff - _st*crv1) / (_st*crv1) );
452                     if ( xsign < 0.5 ) sign = 1.0;
453                     if ( xsign > 1.5  &&  xsign < 3.0 ) sign = -1.0;
454                 }
455                 // If the above is indeterminate, assume zero curvature.
456                 // In this case abs(alpha) decreases monotonically
457                 // with sT.  Track passing through origin has alpha = 0 on one
458                 // side and alpha = +/-pi on the other.  If both points are on
459                 // the same side, we use dr/ds > 0 for |alpha|<pi/2.
460                 if ( sign==0. )
461                 {
462                     sign = 1.0;
463                     if ( Math.abs(alf2) > Math.abs(alf1) ) sign = -1.0;
464                     if ( Math.abs(alf2) == Math.abs(alf1) )
465                     {
466                         if ( Math.abs(alf2) < TRFMath.PI2 )
467                         {
468                             if ( r2 < r1 ) sign = -1.0;
469                         }
470                         else
471                         {
472                             if ( r2 > r1 ) sign = -1.0;
473                         }
474                     }
475                 }
476                 
477                 // Correct _st using the above sign.
478                 Assert.assertTrue( Math.abs(sign) == 1.0 );
479                 _st = sign*_st;
480                 
481                 // save derivatives
482                 _dst_dcrv1 = sign*d*d*arg*( 1.0/6.0 + 3.0/20.0*arg2);
483                 double root = (1.0 + 0.5*arg*arg + 3.0/8.0*arg*arg*arg*arg );
484                 _dst_dphi21 = sign*(r1*r2*Math.sin(phi2-phi1))*root/d;
485                 
486             }
487             
488         }
489         
490         public double st()
491         { return _st;
492         }
493         
494         public double d_st_dalf1(double dphi2_dalf1, double dalf2_dalf1 )
495         {
496             if ( _big_crv ) return ( dphi2_dalf1 + dalf2_dalf1 - 1.0 ) / _crv1;
497             else return _dst_dphi21 * dphi2_dalf1;
498         }
499         
500         public double d_st_dcrv1(double dphi2_dcrv1, double dalf2_dcrv1 )
501         {
502             if ( _big_crv ) return ( dphi2_dcrv1 + dalf2_dcrv1 - _st ) / _crv1;
503             else return _dst_dcrv1 + _dst_dphi21*dphi2_dcrv1;
504         }
505         
506     }
507     
508     
509     
510     
511 }