Brief description
After the discussion with @yradkhorrami we discovered some issues in the HelixClass which I will describe here below. All problems are connected to the fact that HelixClass initializers all rely on the assumption that reference point = (0, 0, 0) even in the situations when it is really not supposed to.
This might lead to ambiguity and misunderstanding all over the place.
HelixClass::Initialize_Canonical()
Issue
A helix in the canonical representation is defined by the reference point and 5 parameters: phi0, omega, tanL, d0, z0.
This initializer is useful to define the helix from the trackState which has all parameters defined.
However this initializer doesn't take reference point as an input argument and assumes reference point always to be (0,0,0)
which can lead to a confusion:
auto ts = track->getTrackState(AtLastHit);
HelixClass helix;
helix.Initialize_Canonical( ts->getPhi0(), ts->getD0(), ts->getZ0(), ts->getOmega(), ts->geTanLambda(), 3.5);
Here the helixClass treats passed d0, z0 arguments as with respect to the (0, 0, 0). However in the trackState they are written with respect to the arbitrary reference_point=ts->getReferencePoint() which is not necessarily (0, 0, 0).
This will result with a helix of the same curvature and dip, but completely different positioning as one would expect from the trackState.
By simply copying d0 and z0 parameters we completely transform their geometrical meaning:
helix.getD0() == ts.getD0() // true
helix.getZ0() == ts.getZ0() // true
As the ts.getZ0() is the distance along z between helix PCA and the ts->getReferencePoint()[2]
While the helix.getZ0() is the distance along z between helix PCA and the z=0.
So they shouldn't be identical if we talk about "the same" helix.
In the current state to get the desired helix one would have to be aware to recalculate d0 and z0 from the track state assuming reference point is (0, 0, 0), e.g.:
auto ts = track->getTrackState(AtLastHit);
HelixClass helix;
double newD0 = getD0AtIP(ts->getReferencePoint(), ts->Phi0(), ts->getD0(), ts->getZ0), ...);
double newZ0 = getZ0AtIP(ts->getReferencePoint(), ts->Phi0(), ts->getD0(), ts->getZ0), ...);
helix.Initialize_Canonical( ts->getPhi0(), newD0, newZ0, ts->getOmega(), ts->geTanLambda(), bField);
which is not necessarily trivial...
Potential way to improve
Provide reference point as an input argument to avoid ambiguity.
HelixClass::Initialize_VP()
Issue
This initializer is useful if one has:
- (x, y, z) coordinates of the point on the helix
- momentum of the helix at the given point
It uses the given point as a reference point to construct the helix:
void HelixClass::Initialize_VP(float * pos, float * mom, float q, float B) {
_referencePoint[0] = pos[0];
_referencePoint[1] = pos[1];
_referencePoint[2] = pos[2];
One would expect that output d0 and z0 paremeters must be 0 as the reference point is on the helix by definition.
However, while calculating d0 and z0 initializer comes backs to assuming reference point as (0,0,0) without mentioning it anywhere:
_d0 = double(q)*radius - q_sign * double(sqrt(xCentre*xCentre+yCentre*yCentre)); // not 0
_z0 = pos[2] + _radius*_tanLambda*q*(deltaPhi + _const_2pi*nCircles); // not 0
This would fail the user, if he plans to use d0, z0 helix parameters further in the program, e.g. to draw the helix,
which @yradkhorrami has experienced in the following example:
float pos[3] = getVertexPosition();
float mom[3] = getMomentum();
HelixClass helix;
helix.Initialize_VP(pos, mom, charge, bField);
double Px = pt * std::cos( helix->getPhi() );
double Py = pt * std::sin( helix->getPhi() );
double Pz = pt * helix->getTanLambda() ;
double Xs = helix->getReferencePoint()[0] - helix->getD0() * std::sin( helix->getPhi() ) ; // d0 should be 0
double Ys = helix->getReferencePoint()[1] + helix->getD0() * std::cos( helix->getPhi() ) ; // d0 should be 0
double Zs = helix->getReferencePoint()[2] + helix->getZ0() ; // z0 should be 0
DDMarlinCED::drawHelix(bField, charge, Xs, Ys, Zs, Px, Py, Pz, ...);
// results in the different helix from:
// DDMarlinCED::drawHelix(bField, charge, pos[0], pos[1], pos[2], mom[0], mom[1], mom[2], ...);
Potential way to improve
Cross check all the equations and remove any assumptions of reference point as 0,0,0 where it is definitely shouldn't be and set d0 and z0 as 0.
HelixClass::Initialize_BZ()
Issue
This initializer is good when we know exact geometrical properties of the helix:
- Coordinates of the center:
xCentre, yCentre
- Radius
radius
- Dip
bZ
- starting phase of the helix
phi0
One should immediately note very inconvenient naming of the phi0 parameter.
Inside the Initialize_BZ() the phi0 parameter is a phase shift of the initial helix along z and is NOT a track parameter as in previous two initializers, which is a great point to become confused in this context.
We also see inconsistency between helix.getReferencePoint() which seems to be ON the helix:
_referencePoint[2] = zBegin;
_referencePoint[0] = xCentre + radius*cos(bZ*zBegin+phi0);
_referencePoint[1] = yCentre + radius*sin(bZ*zBegin+phi0);
and output d0 and z0 parameters that have nothing to do with the helix reference point. It is hard to understand for me from the equations with respect to what point d0 and z0 are actually calculated in this case...
_d0 = -_xAtPCA*sin(_phi0) + _yAtPCA*cos(_phi0);
_z0 = _referencePoint[2] - (deltaPhi + _const_2pi*nCircles)/bZ;
I am very confused about the most of the equations in the Initialize_BZ(). So some discussion with experts would be nice. E.g.:
_phiAtPCA = atan2(-_yCentre,-_xCentre); does it make sense?
Potential way to improve
Cross check all of the equations, rename phi0 to something different, e.g. phase0
HelixClass_double ?
This is just a comment to bring a discussion on why we have separate class for the HelixClass with double. Is it bad to transit HelixClass from float to double? Overload/template? Isn't there any better way to implement this?
List of packages using HelixClass
- Garlic/GarlicExtendedTrack
providing a comment:
(231) // get helix-plane intersection by hand (the one from HelixClass seems strange/buggy...)
- LCFIVertex/ConversionTagger
- FPCCDSiliconTracking_MarlinTrk
- V0Finder
- (Its quite a lot, I will finish it later at some point...)
Any thoughts, comments @tmadlener, @gaede?
Brief description
After the discussion with @yradkhorrami we discovered some issues in the HelixClass which I will describe here below. All problems are connected to the fact that HelixClass initializers all rely on the assumption that
reference point = (0, 0, 0)even in the situations when it is really not supposed to.This might lead to ambiguity and misunderstanding all over the place.
HelixClass::Initialize_Canonical()
Issue
A helix in the canonical representation is defined by the
reference pointand 5 parameters:phi0,omega,tanL,d0,z0.This initializer is useful to define the helix from the
trackStatewhich has all parameters defined.However this initializer doesn't take
reference pointas an input argument and assumesreference pointalways to be(0,0,0)which can lead to a confusion:
Here the
helixClasstreats passedd0,z0arguments as with respect to the(0, 0, 0). However in thetrackStatethey are written with respect to the arbitraryreference_point=ts->getReferencePoint()which is not necessarily(0, 0, 0).This will result with a helix of the same curvature and dip, but completely different positioning as one would expect from the trackState.
By simply copying
d0andz0parameters we completely transform their geometrical meaning:As the
ts.getZ0()is the distance alongzbetween helix PCA and thets->getReferencePoint()[2]While the
helix.getZ0()is the distance alongzbetween helix PCA and thez=0.So they shouldn't be identical if we talk about "the same" helix.
In the current state to get the desired helix one would have to be aware to recalculate
d0andz0from the track state assumingreference pointis(0, 0, 0), e.g.:which is not necessarily trivial...
Potential way to improve
Provide reference point as an input argument to avoid ambiguity.
HelixClass::Initialize_VP()
Issue
This initializer is useful if one has:
It uses the given point as a
reference pointto construct the helix:One would expect that output
d0andz0paremeters must be 0 as the reference point is on the helix by definition.However, while calculating
d0andz0initializer comes backs to assumingreference pointas(0,0,0)without mentioning it anywhere:This would fail the user, if he plans to use
d0,z0helix parameters further in the program, e.g. to draw the helix,which @yradkhorrami has experienced in the following example:
Potential way to improve
Cross check all the equations and remove any assumptions of
reference pointas0,0,0where it is definitely shouldn't be and setd0andz0as 0.HelixClass::Initialize_BZ()
Issue
This initializer is good when we know exact geometrical properties of the helix:
xCentre,yCentreradiusbZphi0One should immediately note very inconvenient naming of the
phi0parameter.Inside the
Initialize_BZ()thephi0parameter is a phase shift of the initial helix alongzand is NOT a track parameter as in previous two initializers, which is a great point to become confused in this context.We also see inconsistency between
helix.getReferencePoint()which seems to be ON the helix:and output
d0andz0parameters that have nothing to do with the helixreference point. It is hard to understand for me from the equations with respect to what pointd0andz0are actually calculated in this case...I am very confused about the most of the equations in the
Initialize_BZ(). So some discussion with experts would be nice. E.g.:_phiAtPCA = atan2(-_yCentre,-_xCentre);does it make sense?Potential way to improve
Cross check all of the equations, rename
phi0to something different, e.g.phase0HelixClass_double ?
This is just a comment to bring a discussion on why we have separate class for the
HelixClasswithdouble. Is it bad to transitHelixClassfromfloattodouble? Overload/template? Isn't there any better way to implement this?List of packages using HelixClass
providing a comment:
(231) // get helix-plane intersection by hand (the one from HelixClass seems strange/buggy...)Any thoughts, comments @tmadlener, @gaede?