diff --git a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/TrkExSolenoidalIntersector/SolenoidParametrization.h b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/TrkExSolenoidalIntersector/SolenoidParametrization.h index 67dd6513807147a43a6e6ff4b4840d6754c51750..8340f1a2016c8013dd129f1ca57de40150c757dd 100755 --- a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/TrkExSolenoidalIntersector/SolenoidParametrization.h +++ b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/TrkExSolenoidalIntersector/SolenoidParametrization.h @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration */ /*************************************************************************** @@ -33,44 +33,75 @@ class SolenoidParametrization { public: - // private constructors (as singleton) - ~SolenoidParametrization (void); + class BinParameters + { + public: + BinParameters (const double zAtAxis, const double cotTheta) + : m_cotTheta (cotTheta), m_zAtAxis (zAtAxis) {} + BinParameters (const double r, const double z, const double cotTheta); + double m_signTheta = 1; + double m_cotTheta = 0; + double m_zAtAxis = 0; + double m_interpolateZ = 0; + double m_complementZ = 0; + double m_interpolateTheta = 0; + double m_complementTheta = 0; + }; + class Parameters + : public BinParameters + { + public: + Parameters (const SolenoidParametrization& spar, + const double r, const double z, const double cotTheta); + double m_fieldAtOrigin; + double m_quadraticTerm; + double m_cubicTerm; + }; + + SolenoidParametrization (MagField::IMagFieldSvc* magFieldSvc); // configuration from Intersector + ~SolenoidParametrization() = default; // forbidden copy constructor // forbidden assignment operator - static SolenoidParametrization* instance (MagField::IMagFieldSvc* magFieldSvc); // initialize singleton - static void clearInstance(void); // clear (to be used before reinitialization) - - double centralField (void) const; - double fieldComponent (double) const; // parametrized - perp to track in rz-plane + double centralField() const; + double fieldComponent (double z, + const Parameters& parms) const; // parametrized - perp to track in rz-plane double fieldComponent (double r, double z, double cotTheta) const; // from MagFieldSvc - void fieldIntegrals (double&, double&, double, double); - bool hasParametrization (void) const; - double maximumR (void) const; // param valid to maximumR - double maximumZ (void) const; // param valid to maximumZ - void printFieldIntegrals (void) const; - void printParametersForEtaLine (double, double); - void printResidualForEtaLine (double, double); - void setParameters (double, double, double); // set line in rz-plane - bool validOrigin(Amg::Vector3D origin) const; // param valid for this origin? + void fieldIntegrals (double& firstIntegral, + double& secondIntegral, + double zBegin, + double zEnd, + Parameters& parms) const; + double maximumR() const; // param valid to maximumR + double maximumZ() const; // param valid to maximumZ + void printFieldIntegrals() const; + void printParametersForEtaLine(double eta, double z_origin) const; + void printResidualForEtaLine (double eta, double zOrigin) const; + bool validOrigin(const Amg::Vector3D& origin) const; // param valid for this origin? + + // OK to use this parametrization for CURRENT? + bool currentMatches (double current) const; private: - // implementation does not work without magnetic field service - // SolenoidParametrization (void); // singleton constructor - SolenoidParametrization (MagField::IMagFieldSvc* magFieldSvc); // configuration from Intersector - int fieldKey(void); - void integrate(double&, double&, double, double) const; - double interpolate(int, int, int, int) const; - void parametrizeSolenoid (void); - void setTerms(int); + friend class Parameters; + + static int fieldKey(BinParameters& parms); + void integrate(double& firstIntegral, + double& secondIntegral, + double zBegin, + double zEnd, + const Parameters& parms) const; + double interpolate(int key1, int key2, + int key3, int key4, + const Parameters& parms) const; + void parametrizeSolenoid(); + void setTerms(int, Parameters& parms) const; - static SolenoidParametrization* s_instance; static const double s_binInvSizeTheta; static const double s_binInvSizeZ; static const double s_binZeroTheta; static const double s_binZeroZ; - static double s_centralField; static const double s_lightSpeed; static const int s_maxBinTheta; static const int s_maxBinZ; @@ -81,60 +112,52 @@ private: static const double s_rOuter; static const double s_zInner; static const double s_zOuter; - static double s_parameters[14688]; - double m_complementTheta; - double m_complementZ; - double m_cotTheta; - double m_cubicTerm; - double m_fieldAtOrigin; - bool m_hasParametrization; - double m_interpolateTheta; - double m_interpolateZ; MagField::IMagFieldSvc* m_magFieldSvc; - double m_quadraticTerm; - double m_signTheta; - double m_zAtAxis; + double m_currentMin; + double m_currentMax; + double m_centralField; + double m_parameters[14688]; // copy, assignment: no semantics, no implementation - SolenoidParametrization (const SolenoidParametrization&); - SolenoidParametrization &operator= (const SolenoidParametrization&); + SolenoidParametrization (const SolenoidParametrization&) = delete; + SolenoidParametrization &operator= (const SolenoidParametrization&) = delete; }; //<<<<<< INLINE PRIVATE MEMBER FUNCTIONS >>>>>> inline int -SolenoidParametrization::fieldKey(void) +SolenoidParametrization::fieldKey(BinParameters& parms) { - double z = m_zAtAxis - s_binZeroZ; + double z = parms.m_zAtAxis - s_binZeroZ; int zBin = static_cast<int>(s_binInvSizeZ*z); - m_interpolateZ = z*s_binInvSizeZ - double(zBin); + parms.m_interpolateZ = z*s_binInvSizeZ - double(zBin); if (zBin < 0) { - m_interpolateZ = 0.; + parms.m_interpolateZ = 0.; zBin = 0; } else if (zBin > s_maxBinZ - 2) { - m_interpolateZ = 0.; + parms.m_interpolateZ = 0.; zBin = s_maxBinZ - 1; } - m_complementZ = 1. - m_interpolateZ; + parms.m_complementZ = 1. - parms.m_interpolateZ; - int thetaBin = static_cast<int>(s_binInvSizeTheta*m_cotTheta); - m_interpolateTheta = m_cotTheta*s_binInvSizeTheta - double(thetaBin); + int thetaBin = static_cast<int>(s_binInvSizeTheta*parms.m_cotTheta); + parms.m_interpolateTheta = parms.m_cotTheta*s_binInvSizeTheta - double(thetaBin); if (thetaBin > s_maxBinTheta - 3) { - m_interpolateTheta = 0.; + parms.m_interpolateTheta = 0.; thetaBin = s_maxBinTheta - 2; } - m_complementTheta = 1. - m_interpolateTheta; - // std::cout << " m_zAtAxis " << m_zAtAxis - // << " m_cotTheta " << m_cotTheta + parms.m_complementTheta = 1. - parms.m_interpolateTheta; + // std::cout << " zAtAxis " << zAtAxis + // << " cotTheta " << cotTheta // << " zBin " << zBin - // << " interpolateZ " << m_interpolateZ + // << " interpolateZ " << parms.m_interpolateZ // << " thetaBin " << thetaBin - // << " interpolateTheta " << m_interpolateTheta << std::endl; + // << " interpolateTheta " << parms.m_interpolateTheta << std::endl; return 2*s_numberParameters*(s_maxBinTheta*zBin + thetaBin); } @@ -143,7 +166,8 @@ inline void SolenoidParametrization::integrate(double& firstIntegral, double& secondIntegral, double zBegin, - double zEnd) const + double zEnd, + const Parameters& parms) const { double zDiff = zEnd - zBegin; double zBeg2 = zBegin*zBegin; @@ -152,29 +176,30 @@ SolenoidParametrization::integrate(double& firstIntegral, double zEnd3 = zEnd2*zEnd; double zDiff4 = 0.25*(zEnd2 + zBeg2)*(zEnd2 - zBeg2); - firstIntegral += m_fieldAtOrigin*zDiff + - m_quadraticTerm*(zEnd3 - zBeg3)*0.333333333333 + - m_cubicTerm*zDiff4; + firstIntegral += parms.m_fieldAtOrigin*zDiff + + parms.m_quadraticTerm*(zEnd3 - zBeg3)*0.333333333333 + + parms.m_cubicTerm*zDiff4; double zDiffInv = 1./zDiff; - secondIntegral += m_fieldAtOrigin*zDiff + - m_quadraticTerm*(zDiffInv*zDiff4 - zBeg3)*0.666666666667 + - m_cubicTerm*(0.1*zDiffInv*(zEnd2*zEnd3 - zBeg2*zBeg3) - 0.5*zBeg2*zBeg2); + secondIntegral += parms.m_fieldAtOrigin*zDiff + + parms.m_quadraticTerm*(zDiffInv*zDiff4 - zBeg3)*0.666666666667 + + parms.m_cubicTerm*(0.1*zDiffInv*(zEnd2*zEnd3 - zBeg2*zBeg3) - 0.5*zBeg2*zBeg2); } inline double SolenoidParametrization::interpolate(int key1, - int key2, - int key3, - int key4) const + int key2, + int key3, + int key4, + const Parameters& parms) const { - return ((s_parameters[key1]*m_complementZ + - s_parameters[key2]*m_interpolateZ)*m_complementTheta + - (s_parameters[key3]*m_complementZ + - s_parameters[key4]*m_interpolateZ)*m_interpolateTheta); + return ((m_parameters[key1]*parms.m_complementZ + + m_parameters[key2]*parms.m_interpolateZ)*parms.m_complementTheta + + (m_parameters[key3]*parms.m_complementZ + + m_parameters[key4]*parms.m_interpolateZ)*parms.m_interpolateTheta); } inline void -SolenoidParametrization::setTerms(int key1) +SolenoidParametrization::setTerms(int key1, Parameters& parms) const { int key2 = key1 + s_numberParameters; int key3 = key2 + s_numberParameters; @@ -182,52 +207,34 @@ SolenoidParametrization::setTerms(int key1) assert (key1 >= 0); assert (key4 < 14688); - assert (s_parameters[key1] != 0.); - assert (s_parameters[key3] != 0.); - if (m_cotTheta < 7.) + assert (m_parameters[key1] != 0.); + assert (m_parameters[key3] != 0.); + if (parms.m_cotTheta < 7.) { - assert (s_parameters[key2] != 0.); - assert (s_parameters[key4] != 0.); + assert (m_parameters[key2] != 0.); + assert (m_parameters[key4] != 0.); } - m_fieldAtOrigin = interpolate(key1++,key2++,key3++,key4++); - m_quadraticTerm = interpolate(key1++,key2++,key3++,key4++); - m_cubicTerm = interpolate(key1,key2,key3,key4); + parms.m_fieldAtOrigin = interpolate(key1++,key2++,key3++,key4++,parms); + parms.m_quadraticTerm = interpolate(key1++,key2++,key3++,key4++,parms); + parms.m_cubicTerm = interpolate(key1,key2,key3,key4,parms); } //<<<<<< INLINE PUBLIC MEMBER FUNCTIONS >>>>>> -// class does not work without magnetic field service -//inline SolenoidParametrization* -//SolenoidParametrization::instance() -//{ -// if (s_instance == 0) s_instance = new SolenoidParametrization(); -// return s_instance; -//} - -inline SolenoidParametrization* -SolenoidParametrization::instance(MagField::IMagFieldSvc* magFieldSvc) -{ - // this method is provided to configure the initialization. - // It should not be called more than once without an intervening clearInstance(). - assert (s_instance == 0); - s_instance = new SolenoidParametrization(magFieldSvc); - return s_instance; -} - inline double SolenoidParametrization::centralField (void) const -{ return s_centralField; } +{ return m_centralField; } inline double -SolenoidParametrization::fieldComponent (double z) const +SolenoidParametrization::fieldComponent (double z, const Parameters& parms) const { - double z_local = m_signTheta*z - m_zAtAxis; + double z_local = parms.m_signTheta*z - parms.m_zAtAxis; double z_squared = z_local*z_local; - double value = m_fieldAtOrigin + - m_quadraticTerm*z_squared + - m_cubicTerm*z_squared*z_local; + double value = parms.m_fieldAtOrigin + + parms.m_quadraticTerm*z_squared + + parms.m_cubicTerm*z_squared*z_local; return value; } @@ -244,32 +251,29 @@ inline void SolenoidParametrization::fieldIntegrals(double& firstIntegral, double& secondIntegral, double zBegin, - double zEnd) + double zEnd, + Parameters& parms) const { - zBegin = m_signTheta*zBegin; - zEnd = m_signTheta*zEnd; + zBegin = parms.m_signTheta*zBegin; + zEnd = parms.m_signTheta*zEnd; if (zEnd < s_zInner || zBegin > s_zInner) { - integrate(firstIntegral,secondIntegral,zBegin-m_zAtAxis,zEnd-m_zAtAxis); + integrate(firstIntegral,secondIntegral,zBegin-parms.m_zAtAxis,zEnd-parms.m_zAtAxis, parms); } else { - integrate(firstIntegral,secondIntegral,zBegin-m_zAtAxis,s_zInner); - int key = fieldKey() + s_numberParameters/2; - setTerms(key); - integrate(firstIntegral,secondIntegral,s_zInner,zEnd-m_zAtAxis); + integrate(firstIntegral,secondIntegral,zBegin-parms.m_zAtAxis,s_zInner, parms); + int key = fieldKey(parms) + s_numberParameters/2; + setTerms(key, parms); + integrate(firstIntegral,secondIntegral,s_zInner,zEnd-parms.m_zAtAxis,parms); } // std::cout << " zBegin < 0. " << zBegin // << " zEnd " << zEnd - // << " m_signTheta " << m_signTheta - // << " m_zAtAxis " << m_zAtAxis << std::endl; + // << " m_signTheta " << parms.m_signTheta + // << " m_zAtAxis " << parms.m_zAtAxis << std::endl; } -inline bool -SolenoidParametrization::hasParametrization (void) const -{ return m_hasParametrization; } - inline double SolenoidParametrization::maximumR (void) const { return s_rInner; } @@ -278,32 +282,8 @@ inline double SolenoidParametrization::maximumZ (void) const { return s_zInner; } -inline void -SolenoidParametrization::setParameters (double r, double z, double cotTheta) -{ - if (cotTheta > 0.) - { - m_signTheta = 1.; - m_cotTheta = cotTheta; - m_zAtAxis = z - r*cotTheta; - } - else - { - m_signTheta = -1.; - m_cotTheta = -cotTheta; - m_zAtAxis = r*cotTheta - z; - } - int key = fieldKey(); - if (r > s_rInner || m_signTheta*z > s_zInner) - { - key += s_numberParameters/2; - } -// cout << "r " << r << " key " << key << endl; - setTerms(key); -} - inline bool -SolenoidParametrization::validOrigin(Amg::Vector3D origin) const +SolenoidParametrization::validOrigin(const Amg::Vector3D& origin) const { return (origin.perp() < s_maximumImpactAtOrigin && std::abs(origin.z()) < s_maximumZatOrigin); } diff --git a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/TrkExSolenoidalIntersector/SolenoidalIntersector.h b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/TrkExSolenoidalIntersector/SolenoidalIntersector.h index 94224bcc98782982bade1151291f54e1f128c6d3..1ef2b7b75f8379f3ce80c7fc9609e8f5ee4193a0 100755 --- a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/TrkExSolenoidalIntersector/SolenoidalIntersector.h +++ b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/TrkExSolenoidalIntersector/SolenoidalIntersector.h @@ -2,282 +2,287 @@ Copyright (C) 2002-2019 CERN for the benefit of the ATLAS collaboration */ -/////////////////////////////////////////////////////////////////// -// SolenoidalIntersector.h, (c) ATLAS Detector software -/////////////////////////////////////////////////////////////////// - -#ifndef TRKEXSOLENOIDALINTERSECTOR_SOLENOIDALINTERSECTOR_H -#define TRKEXSOLENOIDALINTERSECTOR_SOLENOIDALINTERSECTOR_H - -#include <cmath> -#include "AthenaBaseComps/AthAlgTool.h" -#include "GaudiKernel/IIncidentListener.h" -#include "GaudiKernel/ServiceHandle.h" -#include "GaudiKernel/ToolHandle.h" -#include "GeoPrimitives/GeoPrimitives.h" -#include "MagFieldInterfaces/IMagFieldSvc.h" -#include "TrkExInterfaces/IIntersector.h" -#include "TrkExUtils/TrackSurfaceIntersection.h" -#include "TrkExSolenoidalIntersector/SolenoidParametrization.h" - -class IIncidentSvc; -namespace Trk -{ - -class SolenoidalIntersector: public AthAlgTool, - virtual public IIntersector, virtual public IIncidentListener -{ - -public: - SolenoidalIntersector (const std::string& type, - const std::string& name, - const IInterface* parent); - ~SolenoidalIntersector (void); // destructor - - StatusCode initialize(); - StatusCode finalize(); - - /** handle for incident service */ - void handle(const Incident& inc) ; - - /**IIntersector interface method for general Surface type */ - const TrackSurfaceIntersection* intersectSurface(const Surface& surface, - const TrackSurfaceIntersection* trackTrackSurfaceIntersection, - const double qOverP); - - /**IIntersector interface method for specific Surface type : PerigeeSurface */ - const TrackSurfaceIntersection* approachPerigeeSurface(const PerigeeSurface& surface, - const TrackSurfaceIntersection* trackTrackSurfaceIntersection, - const double qOverP); - - /**IIntersector interface method for specific Surface type : StraightLineSurface */ - const TrackSurfaceIntersection* approachStraightLineSurface(const StraightLineSurface& surface, - const TrackSurfaceIntersection* trackTrackSurfaceIntersection, - const double qOverP); - - /**IIntersector interface method for specific Surface type : CylinderSurface */ - const TrackSurfaceIntersection* intersectCylinderSurface (const CylinderSurface& surface, - const TrackSurfaceIntersection* trackTrackSurfaceIntersection, - const double qOverP); - - /**IIntersector interface method for specific Surface type : DiscSurface */ - const TrackSurfaceIntersection* intersectDiscSurface (const DiscSurface& surface, - const TrackSurfaceIntersection* trackTrackSurfaceIntersection, - const double qOverP); - - /**IIntersector interface method for specific Surface type : PlaneSurface */ - const TrackSurfaceIntersection* intersectPlaneSurface(const PlaneSurface& surface, - const TrackSurfaceIntersection* trackTrackSurfaceIntersection, - const double qOverP); - - /**IIntersector interface method to check validity of parametrization within extrapolation range */ - bool isValid (Amg::Vector3D startPosition, - Amg::Vector3D endPosition) const; - - /** tabulate parametrization details */ - void validationAction() const; - -private: - double circularArcLength(double, double, double, double, double, - double, double&, double&); - double linearArcLength(double); - bool extrapolateToR(double endRadius); - bool extrapolateToZ(double endZ); - const TrackSurfaceIntersection* intersection(const Surface& surface); - void setParameters(const TrackSurfaceIntersection* intersection, - double qOverP); - - // services and tools: - ServiceHandle<IIncidentSvc> m_incidentSvc; //!< IncidentSvc to catch begin of event - ServiceHandle<MagField::IMagFieldSvc> m_magFieldSvc; - ToolHandle<IIntersector> m_rungeKuttaIntersector; - - double m_cotTheta; - float m_currentMax; - float m_currentMin; - double m_deltaPhiTolerance; - Amg::Vector3D m_direction; - unsigned m_intersectionNumber; - double m_oneOverSinTheta; - double m_pathLength; - Amg::Vector3D m_position; - double m_qOverP; - double m_qOverPt; - double m_radius; - double m_sinTheta; - SolenoidParametrization* m_solenoidParametrization; - double m_surfaceTolerance; - double m_validRadius; - double m_validZ; - - // counters - unsigned long long m_countExtrapolations; - unsigned long long m_countRKSwitches; - -}; - - -// arc length to intersect of 2 circles: circular track and circle at (0,0) with radius endRadius -inline double -SolenoidalIntersector::circularArcLength(double endRadius, - double radiusOfCurvature, - double xCentre, - double yCentre, - double cosPhi, - double sinPhi, - double& cosPhiIntersect, - double& sinPhiIntersect) -{ - int trapped = 0; - double radiusSquared = xCentre*xCentre + yCentre*yCentre; - double term = 0.5*(radiusSquared + - radiusOfCurvature*radiusOfCurvature - - endRadius*endRadius)/(radiusSquared*radiusOfCurvature); - if (std::abs(xCentre) < std::abs(yCentre)) - { - double dx2 = yCentre*yCentre * (1./(radiusSquared*term*term) - 1.); - if (dx2 < 0.) - { - trapped = 1; - } - else - { - if (yCentre*term > 0.) - { - sinPhiIntersect = term*(-xCentre+std::sqrt(dx2)); - } - else - { - sinPhiIntersect = term*(-xCentre-std::sqrt(dx2)); - } - cosPhiIntersect = (sinPhiIntersect*xCentre + radiusSquared*term)/yCentre; - } - } - else - { - double dy2 = xCentre*xCentre * (1./(radiusSquared*term*term) - 1.); - if (dy2 < 0.) - { - trapped = 1; - } - else - { - if (xCentre*term > 0.) - { - cosPhiIntersect = term*(yCentre+std::sqrt(dy2)); - } - else - { - cosPhiIntersect = term*(yCentre-std::sqrt(dy2)); - } - sinPhiIntersect = (cosPhiIntersect*yCentre - radiusSquared*term)/xCentre; - } - } - if (trapped == 0) - { - double deltaPhi; - double sinDeltaPhi = sinPhiIntersect*cosPhi - cosPhiIntersect*sinPhi; - if (std::abs(sinDeltaPhi) > 0.1) - { - deltaPhi = asin(sinDeltaPhi); - } - else - { - deltaPhi = sinDeltaPhi*(1. + 0.166667*sinDeltaPhi*sinDeltaPhi); - } - return (radiusOfCurvature*deltaPhi); - } - else - { - cosPhiIntersect = cosPhi; - sinPhiIntersect = sinPhi; - return 0.; - } -} - -// arc length to intersect of a line to a circle of radius endRadius centred at (0,0) -// +ve (-ve) endRadius selects the solution on the same (opposite) side of (0,0) -inline double -SolenoidalIntersector::linearArcLength(double endRadius) -{ - double arcLength = (-m_direction.x()*m_position.x() - m_direction.y()*m_position.y()) * - m_oneOverSinTheta; - double radiusSquared = endRadius*endRadius - m_radius*m_radius + arcLength*arcLength; - if (radiusSquared > 0.) - { - if (endRadius > 0.) - { - arcLength += std::sqrt(radiusSquared); - } - else - { - arcLength -= std::sqrt(radiusSquared); - } - } - return arcLength; -} - -inline const TrackSurfaceIntersection* -SolenoidalIntersector::intersection(const Surface& surface) -{ - Intersection SLIntersect = surface.straightLineIntersection(m_position, m_direction, false, false); - if (! SLIntersect.valid) return 0; - - const TrackSurfaceIntersection* intersection = new TrackSurfaceIntersection(SLIntersect.position, - m_direction, - m_pathLength); - // // validate - // if (! intersection) - // { - // ATH_MSG_WARNING(" this should never fail"); - // return 0; - // } - - // const Amg::Vector2D* localPos = surface.positionOnSurface(intersection->position(), false); - // if (! localPos) - // { - // ATH_MSG_INFO(" localPos fails surface type " << surface.type() - // << " at R,Z " << m_position.perp() << ", " << m_position.z() - // << " surface R " << surface.globalReferencePoint().perp()); - // return 0; - // } - - - // ATH_MSG_INFO(" serial diff " << intersection->serialNumber() - m_intersectionNumber - // << " at R,Z: " << m_radius << ", " << m_position.z()); - - m_intersectionNumber = intersection->serialNumber(); - return intersection; -} - -inline void -SolenoidalIntersector::setParameters(const TrackSurfaceIntersection* trackTrackSurfaceIntersection, double qOverP) -{ - if (trackTrackSurfaceIntersection->serialNumber() != m_intersectionNumber || qOverP != m_qOverP) - { - // ATH_MSG_INFO(" initialize parameters. Diff: " << trackTrackSurfaceIntersection->serialNumber()-m_intersectionNumber - // << " at R,Z: " << trackTrackSurfaceIntersection->position().perp() << ", " << trackTrackSurfaceIntersection->position().z()); - ++m_countExtrapolations; - m_position.x() = trackTrackSurfaceIntersection->position().x(); - m_position.y() = trackTrackSurfaceIntersection->position().y(); - m_position.z() = trackTrackSurfaceIntersection->position().z(); - m_radius = m_position.perp(); - m_direction.x() = trackTrackSurfaceIntersection->direction().x(); - m_direction.y() = trackTrackSurfaceIntersection->direction().y(); - m_direction.z() = trackTrackSurfaceIntersection->direction().z(); - m_sinTheta = m_direction.perp(); - m_oneOverSinTheta = 1./m_sinTheta; - m_cotTheta = m_direction.z() * m_oneOverSinTheta; - m_pathLength = trackTrackSurfaceIntersection->pathlength(); - m_qOverP = qOverP; - m_qOverPt = qOverP * m_oneOverSinTheta; - m_solenoidParametrization->setParameters(m_radius,m_position.z(),m_cotTheta); - } -} - -} // end of namespace - - -#endif // TRKEXSOLENOIDALINTERSECTOR_SOLENOIDALINTERSECTOR_H - - +/////////////////////////////////////////////////////////////////// +// SolenoidalIntersector.h, (c) ATLAS Detector software +/////////////////////////////////////////////////////////////////// + +#ifndef TRKEXSOLENOIDALINTERSECTOR_SOLENOIDALINTERSECTOR_H +#define TRKEXSOLENOIDALINTERSECTOR_SOLENOIDALINTERSECTOR_H + +#include "AthenaBaseComps/AthAlgTool.h" +#include "GaudiKernel/ServiceHandle.h" +#include "GaudiKernel/ToolHandle.h" +#include "GaudiKernel/ContextSpecificPtr.h" +#include "GeoPrimitives/GeoPrimitives.h" +#include "MagFieldInterfaces/IMagFieldSvc.h" +#include "TrkExInterfaces/IIntersector.h" +#include "TrkExUtils/TrackSurfaceIntersection.h" +#include "TrkExSolenoidalIntersector/SolenoidParametrization.h" +#include "CxxUtils/checker_macros.h" +#include <mutex> +#include <cmath> + +class IIncidentSvc; +namespace Trk +{ + +class SolenoidalIntersector: public extends<AthAlgTool, IIntersector> +{ + +public: + /** + * @brief Constants of motion and other cached values. + * + * There is some data we want to persist across calls to intersectSurface() + * for a single trajectory. We do this by attaching this structure + * as a cache block to TrackSurfaceIntersection. Some of these are + * actually constants of motion for a helical trajectory. We also + * include here the solenoidal parametrization. This gets updated + * as we step through the field; including it here avoids a reevaluation + * at the start of the next call. We also include the position at the + * end of the stepping phase, m_lastPosition. This is because before + * after we finish stepping but before we return, we improve the + * estimate of the intersection by calculating the straight line + * intersection with the surface. But if we then calcuate a further + * intersection with the same trajectory, we want to start at the + * point at the end of the previous stepping, not the position + * we returned. + */ + struct Constants + : public TrackSurfaceIntersection::IIntersectionCache + { + Constants (const SolenoidParametrization& solpar, + const TrackSurfaceIntersection& trackTrackSurfaceIntersection, + const double qOverP); + virtual std::unique_ptr<IIntersectionCache> clone() const override + { return std::make_unique<Constants> (*this); } + + double m_sinTheta; + double m_oneOverSinTheta; + double m_cotTheta; + double m_qOverPt; + const SolenoidParametrization& m_solPar; + Amg::Vector3D m_lastPosition; + SolenoidParametrization::Parameters m_solParams; + }; + SolenoidalIntersector (const std::string& type, + const std::string& name, + const IInterface* parent); + ~SolenoidalIntersector (void); // destructor + + StatusCode initialize(); + StatusCode finalize(); + + /**IIntersector interface method for general Surface type */ + const TrackSurfaceIntersection* intersectSurface(const Surface& surface, + const TrackSurfaceIntersection* trackTrackSurfaceIntersection, + const double qOverP); + + /**IIntersector interface method for specific Surface type : PerigeeSurface */ + const TrackSurfaceIntersection* approachPerigeeSurface(const PerigeeSurface& surface, + const TrackSurfaceIntersection* trackTrackSurfaceIntersection, + const double qOverP); + + /**IIntersector interface method for specific Surface type : StraightLineSurface */ + const TrackSurfaceIntersection* approachStraightLineSurface(const StraightLineSurface& surface, + const TrackSurfaceIntersection* trackTrackSurfaceIntersection, + const double qOverP); + + /**IIntersector interface method for specific Surface type : CylinderSurface */ + const TrackSurfaceIntersection* intersectCylinderSurface (const CylinderSurface& surface, + const TrackSurfaceIntersection* trackTrackSurfaceIntersection, + const double qOverP); + + /**IIntersector interface method for specific Surface type : DiscSurface */ + const TrackSurfaceIntersection* intersectDiscSurface (const DiscSurface& surface, + const TrackSurfaceIntersection* trackTrackSurfaceIntersection, + const double qOverP); + + /**IIntersector interface method for specific Surface type : PlaneSurface */ + const TrackSurfaceIntersection* intersectPlaneSurface(const PlaneSurface& surface, + const TrackSurfaceIntersection* trackTrackSurfaceIntersection, + const double qOverP); + + /**IIntersector interface method to check validity of parametrization within extrapolation range */ + bool isValid (Amg::Vector3D startPosition, + Amg::Vector3D endPosition) const; + + /** tabulate parametrization details */ + void validationAction() const; + +private: + const SolenoidParametrization* getSolenoidParametrization() const; + + double circularArcLength(double, double, double, double, double, + double, double&, double&) const; + double linearArcLength(const TrackSurfaceIntersection& isect, + const Constants& com, + const double radius2, + const double endRadius) const; + bool extrapolateToR(TrackSurfaceIntersection& isect, + double& radius2, + Constants& com, + const double endRadius) const; + bool extrapolateToZ(TrackSurfaceIntersection& isect, + Constants& com, + const double endZ) const; + const TrackSurfaceIntersection* intersection(std::unique_ptr<TrackSurfaceIntersection> isect, + Constants& com, + const Surface& surface) const; + + std::unique_ptr<TrackSurfaceIntersection> + newIntersection (const TrackSurfaceIntersection& oldIsect, + const SolenoidParametrization& solpar, + const double qOverP, + Constants*& com) const; + + ServiceHandle<MagField::IMagFieldSvc> m_magFieldSvc; + ToolHandle<IIntersector> m_rungeKuttaIntersector; + + double m_deltaPhiTolerance; + double m_surfaceTolerance; + + // counters + mutable std::atomic<unsigned long long> m_countExtrapolations; + mutable std::atomic<unsigned long long> m_countRKSwitches; + + mutable std::mutex m_mutex; + mutable Gaudi::Hive::ContextSpecificPtr<const SolenoidParametrization> m_lastSolenoidParametrization ATLAS_THREAD_SAFE; + // List of active solenoid parametrizations. Second element of the pair + // is a use count. + typedef std::list<std::pair<SolenoidParametrization, int> > Parmlist_t; + mutable Parmlist_t m_solenoidParametrizations ATLAS_THREAD_SAFE; +}; + + +// arc length to intersect of 2 circles: circular track and circle at (0,0) with radius endRadius +inline double +SolenoidalIntersector::circularArcLength(double endRadius, + double radiusOfCurvature, + double xCentre, + double yCentre, + double cosPhi, + double sinPhi, + double& cosPhiIntersect, + double& sinPhiIntersect) const +{ + int trapped = 0; + double radiusSquared = xCentre*xCentre + yCentre*yCentre; + double term = 0.5*(radiusSquared + + radiusOfCurvature*radiusOfCurvature - + endRadius*endRadius)/(radiusSquared*radiusOfCurvature); + if (std::abs(xCentre) < std::abs(yCentre)) + { + double dx2 = yCentre*yCentre * (1./(radiusSquared*term*term) - 1.); + if (dx2 < 0.) + { + trapped = 1; + } + else + { + if (yCentre*term > 0.) + { + sinPhiIntersect = term*(-xCentre+std::sqrt(dx2)); + } + else + { + sinPhiIntersect = term*(-xCentre-std::sqrt(dx2)); + } + cosPhiIntersect = (sinPhiIntersect*xCentre + radiusSquared*term)/yCentre; + } + } + else + { + double dy2 = xCentre*xCentre * (1./(radiusSquared*term*term) - 1.); + if (dy2 < 0.) + { + trapped = 1; + } + else + { + if (xCentre*term > 0.) + { + cosPhiIntersect = term*(yCentre+std::sqrt(dy2)); + } + else + { + cosPhiIntersect = term*(yCentre-std::sqrt(dy2)); + } + sinPhiIntersect = (cosPhiIntersect*yCentre - radiusSquared*term)/xCentre; + } + } + if (trapped == 0) + { + double deltaPhi; + double sinDeltaPhi = sinPhiIntersect*cosPhi - cosPhiIntersect*sinPhi; + if (std::abs(sinDeltaPhi) > 0.1) + { + deltaPhi = asin(sinDeltaPhi); + } + else + { + deltaPhi = sinDeltaPhi*(1. + 0.166667*sinDeltaPhi*sinDeltaPhi); + } + return (radiusOfCurvature*deltaPhi); + } + else + { + cosPhiIntersect = cosPhi; + sinPhiIntersect = sinPhi; + return 0.; + } +} + +// arc length to intersect of a line to a circle of radius endRadius centred at (0,0) +// +ve (-ve) endRadius selects the solution on the same (opposite) side of (0,0) +inline double +SolenoidalIntersector::linearArcLength(const TrackSurfaceIntersection& isect, + const Constants& com, + const double radius2, + const double endRadius) const +{ + const Amg::Vector3D& pos = isect.position(); + const Amg::Vector3D& dir = isect.direction(); + + double arcLength = (-dir.x()*pos.x() - dir.y()*pos.y()) * + com.m_oneOverSinTheta; + double radiusSquared = endRadius*endRadius - radius2 + arcLength*arcLength; + if (radiusSquared > 0.) + { + if (endRadius > 0.) + { + arcLength += std::sqrt(radiusSquared); + } + else + { + arcLength -= std::sqrt(radiusSquared); + } + } + return arcLength; +} + +inline const TrackSurfaceIntersection* +SolenoidalIntersector::intersection(std::unique_ptr<TrackSurfaceIntersection> isect, + Constants& com, + const Surface& surface) const +{ + // Improve the estimate of the intersection by calculating + // the straight-line intersection. + Intersection SLIntersect = surface.straightLineIntersection(isect->position(), isect->direction(), false, false); + if (SLIntersect.valid) + { + // But first save our current position, so that we can + // start from here on the next call. + com.m_lastPosition = isect->position(); + isect->position() = SLIntersect.position; + return isect.release(); + } + + return nullptr; +} + +} // end of namespace + + +#endif // TRKEXSOLENOIDALINTERSECTOR_SOLENOIDALINTERSECTOR_H + diff --git a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/share/SolenoidalIntersector_test.ref b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/share/SolenoidalIntersector_test.ref index ed9b945fd7666d51c4696af3ae0592c981a261cb..e22da5001735f61591b92f6c498f47418c906d1c 100644 --- a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/share/SolenoidalIntersector_test.ref +++ b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/share/SolenoidalIntersector_test.ref @@ -10,7 +10,7 @@ JobOptionsSvc INFO Job options successfully read in from ../share/Solenoi ApplicationMgr SUCCESS ==================================================================================================================================== Welcome to ApplicationMgr (GaudiCoreSvc v27r1p99) - running on karma on Tue Jan 29 12:11:05 2019 + running on karma on Sun Feb 3 21:41:27 2019 ==================================================================================================================================== ApplicationMgr INFO Application Manager Configured successfully AtlasFieldSvc INFO initialize() ... @@ -34,9 +34,8 @@ AtlasFieldSvc INFO Initialized the field map from /home/sss/nobackup/atla AtlasFieldSvc INFO Currents imported and map initialized AtlasFieldSvc INFO BeginRun incident handled AtlasFieldSvc INFO incidents handled successfully - SolenoidParametrization: centralField 2T - please be patient while the solenoid is parametrised !! -ToolSvc.Trk::So... INFO SolenoidParametrization current: 7730 valid radius,Z : 570, 2150 test_plane + SolenoidParametrization: centralField 2T - please be patient while the solenoid is parametrised !! test_line test_cylinder test_disc diff --git a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/src/SolenoidParametrization.cxx b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/src/SolenoidParametrization.cxx index 21236565d5b8b139c0d055b0afb51cdfeed15012..3dc8e769667d14ca16f5b9fadfd3160c09d83289 100755 --- a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/src/SolenoidParametrization.cxx +++ b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/src/SolenoidParametrization.cxx @@ -1,5 +1,5 @@ /* - Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2017, 2019 CERN for the benefit of the ATLAS collaboration */ /*************************************************************************** @@ -46,8 +46,6 @@ const double SolenoidParametrization::s_binInvSizeTheta = 1./0.1; const double SolenoidParametrization::s_binInvSizeZ = 1./20.*Gaudi::Units::mm; const double SolenoidParametrization::s_binZeroTheta = 0.; const double SolenoidParametrization::s_binZeroZ = -160.*Gaudi::Units::mm; -double SolenoidParametrization::s_centralField = 0.; -SolenoidParametrization* SolenoidParametrization::s_instance = 0; const double SolenoidParametrization::s_lightSpeed = -1.*299792458*Gaudi::Units::m/Gaudi::Units::s; const int SolenoidParametrization::s_maxBinTheta = 72; const int SolenoidParametrization::s_maxBinZ = 17; @@ -58,38 +56,74 @@ const double SolenoidParametrization::s_rInner = 570.*Gaudi::Units::mm; const double SolenoidParametrization::s_rOuter = 1050.*Gaudi::Units::mm; const double SolenoidParametrization::s_zInner = 2150.0*Gaudi::Units::mm; // just after wheel #7 const double SolenoidParametrization::s_zOuter = 2800.0*Gaudi::Units::mm; // just after wheel #9 -double SolenoidParametrization::s_parameters[] = {14688*0.}; + + +SolenoidParametrization::BinParameters::BinParameters (const double r, + const double z, + const double cotTheta) +{ + if (cotTheta > 0) { + m_signTheta = 1; + m_cotTheta = cotTheta; + m_zAtAxis = z - r*cotTheta; + } + else { + m_signTheta = -1; + m_cotTheta = -cotTheta; + m_zAtAxis = r*cotTheta - z; + } +} + + +SolenoidParametrization::Parameters::Parameters (const SolenoidParametrization& spar, + const double r, + const double z, + const double cotTheta) + : BinParameters (r, z, cotTheta) +{ + int key = fieldKey(*this); + if (r > s_rInner || m_signTheta*z > s_zInner) + { + key += s_numberParameters/2; + } + spar.setTerms (key, *this); +} + //<<<<<< CLASS STRUCTURE INITIALIZATION >>>>>> // note: both constructors are private // SolenoidParametrization::SolenoidParametrization(void) -// : m_hasParametrization (false) // { // // field component not defined without a m_magFieldSvc // // get central field value in units required for fast tracking -// s_centralField = fieldComponent(0.,0.,0.); +// m_centralField = fieldComponent(0.,0.,0.); // // // note field = B*c // RestoreIOSFlags restore(std::cout); // std::cout << " SolenoidParametrization: centralField " -// << std::setw(6) << std::setprecision(3) << s_centralField/s_lightSpeed /Gaudi::Units::tesla +// << std::setw(6) << std::setprecision(3) << m_centralField/s_lightSpeed /Gaudi::Units::tesla // << "T" << std::endl; //} SolenoidParametrization::SolenoidParametrization(MagField::IMagFieldSvc* magFieldSvc) - : m_hasParametrization (false), - m_magFieldSvc (magFieldSvc) + : m_magFieldSvc (magFieldSvc), + m_parameters () { - // get central field value in units required for fast tracking (i.e. field = B*c) + // allow 0.1% fluctuation in current. + double current = magFieldSvc->solenoidCurrent(); + m_currentMax = 1.001*current; + m_currentMin = 0.999*current; + + // get central field value in units required for fast tracking (i.e. field = B*c) if (!m_magFieldSvc) throw std::logic_error("fieldComponent not defined without magnetic field service."); - s_centralField = fieldComponent(0.,0.,0.); + m_centralField = fieldComponent(0.,0.,0.); RestoreIOSFlags restore(std::cout); std::cout << " SolenoidParametrization: centralField " - << std::setw(6) << std::setprecision(3) << s_centralField/s_lightSpeed /Gaudi::Units::tesla + << std::setw(6) << std::setprecision(3) << m_centralField/s_lightSpeed /Gaudi::Units::tesla << "T"; - + // now parametrise field - if requested //if (magFieldSvc) { @@ -104,31 +138,28 @@ SolenoidParametrization::SolenoidParametrization(MagField::IMagFieldSvc* magFiel void SolenoidParametrization::parametrizeSolenoid(void) { - m_hasParametrization = true; - // set parametrisation granularity (up to cotTheta = 7.) // get value of cubic term for approx: Bz = Bcentral*(1 - term * z^3) // 'fit' to average over cotTheta lines - m_signTheta = 1.; double smallOffset = 0.0000000000001; // avoid FPE - m_zAtAxis = s_binZeroZ; // + smallOffset ? + double zAtAxis = s_binZeroZ; // + smallOffset ? for (int binZ = 0; binZ < s_maxBinZ; ++binZ) { - m_cotTheta = smallOffset; + double cotTheta = smallOffset; for (int binTheta = 0; binTheta < s_maxBinTheta - 1; ++binTheta) { double r = 0.; - double z = m_zAtAxis; + double z = zAtAxis; int n = 200; double dr; - if (m_cotTheta < s_zOuter/s_rOuter) + if (cotTheta < s_zOuter/s_rOuter) { dr = s_rOuter/double(n); } else { - dr = s_zOuter/(m_cotTheta*double(n)); + dr = s_zOuter/(cotTheta*double(n)); } Amg::VectorX difference(n); @@ -136,9 +167,9 @@ SolenoidParametrization::parametrizeSolenoid(void) for (int k = 0; k < n; ++k) { r += dr; - z += dr*m_cotTheta; + z += dr*cotTheta; double w = (n - k)*(n - k); - double zLocal = z - m_zAtAxis; + double zLocal = z - zAtAxis; if (r > s_rInner || z > s_zInner) { // derivative(k,0) = 0.; @@ -157,31 +188,32 @@ SolenoidParametrization::parametrizeSolenoid(void) // derivative(k,4) = 0.; // derivative(k,5) = 0.; } - difference(k) = w*(fieldComponent(r,z,m_cotTheta) - s_centralField); + difference(k) = w*(fieldComponent(r,z,cotTheta) - m_centralField); } // solve for parametrization coefficients Amg::VectorX solution = derivative.colPivHouseholderQr().solve(difference); + BinParameters parms (zAtAxis, cotTheta); - int key = fieldKey(); - assert (s_parameters[key] == 0.); - s_parameters[key++] = s_centralField + solution(0); - s_parameters[key++] = solution(1); - s_parameters[key++] = solution(2); - s_parameters[key++] = s_centralField + solution(3); - s_parameters[key++] = solution(4); - s_parameters[key++] = solution(5); + int key = fieldKey(parms); + assert (m_parameters[key] == 0.); + m_parameters[key++] = m_centralField + solution(0); + m_parameters[key++] = solution(1); + m_parameters[key++] = solution(2); + m_parameters[key++] = m_centralField + solution(3); + m_parameters[key++] = solution(4); + m_parameters[key++] = solution(5); // duplicate last z-bin for contiguous neighbour lookup if (binZ == s_maxBinZ - 1) { - assert (s_parameters[key] == 0.); - s_parameters[key++] = s_centralField + solution(0); - s_parameters[key++] = solution(1); - s_parameters[key++] = solution(2); - s_parameters[key++] = s_centralField + solution(3); - s_parameters[key++] = solution(4); - s_parameters[key++] = solution(5); + assert (m_parameters[key] == 0.); + m_parameters[key++] = m_centralField + solution(0); + m_parameters[key++] = solution(1); + m_parameters[key++] = solution(2); + m_parameters[key++] = m_centralField + solution(3); + m_parameters[key++] = solution(4); + m_parameters[key++] = solution(5); key -= s_numberParameters; } @@ -189,13 +221,13 @@ SolenoidParametrization::parametrizeSolenoid(void) if (binZ > 0) { key -= 2*s_numberParameters*s_maxBinTheta; - assert (s_parameters[key] == 0.); - s_parameters[key++] = s_centralField + solution(0); - s_parameters[key++] = solution(1); - s_parameters[key++] = solution(2); - s_parameters[key++] = s_centralField + solution(3); - s_parameters[key++] = solution(4); - s_parameters[key++] = solution(5); + assert (m_parameters[key] == 0.); + m_parameters[key++] = m_centralField + solution(0); + m_parameters[key++] = solution(1); + m_parameters[key++] = solution(2); + m_parameters[key++] = m_centralField + solution(3); + m_parameters[key++] = solution(4); + m_parameters[key++] = solution(5); } // some debug print @@ -204,66 +236,54 @@ SolenoidParametrization::parametrizeSolenoid(void) // || key >= s_numberParameters*s_maxBinTheta*(s_maxBinZ - 2)) // { // double z_max; -// if (m_cotTheta < s_zInner/s_rInner) +// if (cotTheta < s_zInner/s_rInner) // { -// z_max = s_rInner*m_cotTheta + m_zAtAxis; +// z_max = s_rInner*cotTheta + zAtAxis; // } // else // { // z_max = s_zInner; // } // cout << std::setiosflags(std::ios::fixed) << key -// << " cotTheta " << std::setw(7) << std::setprecision(2) << m_cotTheta +// << " cotTheta " << std::setw(7) << std::setprecision(2) << cotTheta // << " inner terms: z0 "<< std::setw(6) << std::setprecision(3) -// << s_parameters[key]/s_centralField +// << m_parameters[key]/m_centralField // << " z^2 "<< std::setw(6) << std::setprecision(3) -// << s_parameters[key+1]*z_max*z_max/s_centralField +// << m_parameters[key+1]*z_max*z_max/m_centralField // << " z^3 " << std::setw(6) << std::setprecision(3) -// << s_parameters[key+2]*z_max*z_max*z_max/s_centralField +// << m_parameters[key+2]*z_max*z_max*z_max/m_centralField // << " outer terms: z0 "<< std::setw(6) << std::setprecision(3) -// << s_parameters[key+3]/s_centralField +// << m_parameters[key+3]/m_centralField // << " z^2 "<< std::setw(6) << std::setprecision(3) -// << s_parameters[key+4]*z_max*z_max/s_centralField +// << m_parameters[key+4]*z_max*z_max/m_centralField // << " z^3 " << std::setw(6) << std::setprecision(3) -// << s_parameters[key+5]*z_max*z_max*z_max/s_centralField +// << m_parameters[key+5]*z_max*z_max*z_max/m_centralField // << std::resetiosflags(std::ios::fixed) << endl; // } - m_cotTheta += 1./s_binInvSizeTheta; + cotTheta += 1./s_binInvSizeTheta; } - m_zAtAxis += 1./s_binInvSizeZ; + zAtAxis += 1./s_binInvSizeZ; } // duplicate end theta-bins for contiguous neighbour lookup - m_zAtAxis = s_binZeroZ; // + smallOffset ?? + zAtAxis = s_binZeroZ; // + smallOffset ?? for (int binZ = 0; binZ < s_maxBinZ; ++binZ) { - m_cotTheta = double(s_maxBinTheta)/s_binInvSizeTheta; - int key = fieldKey(); + double cotTheta = double(s_maxBinTheta)/s_binInvSizeTheta; + BinParameters parms (zAtAxis, cotTheta); + int key = fieldKey(parms); for (int k = 0; k < 2*s_numberParameters; ++k) { - assert (s_parameters[key+2*s_numberParameters] == 0.); - s_parameters[key+2*s_numberParameters] = s_parameters[key]; + assert (m_parameters[key+2*s_numberParameters] == 0.); + m_parameters[key+2*s_numberParameters] = m_parameters[key]; ++key; } - m_zAtAxis += 1./s_binInvSizeZ; + zAtAxis += 1./s_binInvSizeZ; } } //<<<<<< PUBLIC MEMBER FUNCTION DEFINITIONS >>>>>> -SolenoidParametrization::~SolenoidParametrization() -{} - -void -SolenoidParametrization::clearInstance(void) -{ - delete s_instance; - s_instance = 0; - std::fill (s_parameters, - s_parameters + sizeof(s_parameters)/sizeof(s_parameters[0]), - 0); -} - void SolenoidParametrization::printFieldIntegrals (void) const { @@ -392,16 +412,15 @@ SolenoidParametrization::printFieldIntegrals (void) const } void -SolenoidParametrization::printParametersForEtaLine (double eta, double z_origin) +SolenoidParametrization::printParametersForEtaLine (double eta, double z_origin) const { - m_signTheta = 1.; - m_zAtAxis = z_origin; - m_cotTheta = 1./std::tan(2.*std::atan(1./std::exp(eta))); - int key = fieldKey(); + double cotTheta = 1./std::tan(2.*std::atan(1./std::exp(eta))); + BinParameters parms (z_origin, cotTheta); + int key = fieldKey(parms); double z_max; - if (m_cotTheta < s_zInner/s_rInner) + if (cotTheta < s_zInner/s_rInner) { - z_max = s_rInner*m_cotTheta; + z_max = s_rInner*cotTheta; } else { @@ -411,25 +430,23 @@ SolenoidParametrization::printParametersForEtaLine (double eta, double z_origin) << "SolenoidParametrization: line with eta " << std::setw(6) << std::setprecision(2) << eta << " from (r,z) 0.0," << std::setw(6) << std::setprecision(1) << z_origin << " inner terms: z0 "<< std::setw(6) << std::setprecision(2) - << s_parameters[key]/s_centralField + << m_parameters[key]/m_centralField << " z^2 "<< std::setw(6) << std::setprecision(3) - << s_parameters[key+1]*z_max*z_max/s_centralField + << m_parameters[key+1]*z_max*z_max/m_centralField << " z^3 " << std::setw(6) << std::setprecision(3) - << s_parameters[key+2]*z_max*z_max*z_max/s_centralField + << m_parameters[key+2]*z_max*z_max*z_max/m_centralField << " outer terms: z0 "<< std::setw(6) << std::setprecision(3) - << s_parameters[key+3]/s_centralField + << m_parameters[key+3]/m_centralField << " z^2 "<< std::setw(6) << std::setprecision(3) - << s_parameters[key+4]*z_max*z_max/s_centralField + << m_parameters[key+4]*z_max*z_max/m_centralField << " z^3 " << std::setw(6) << std::setprecision(3) - << s_parameters[key+5]*z_max*z_max*z_max/s_centralField + << m_parameters[key+5]*z_max*z_max*z_max/m_centralField << std::resetiosflags(std::ios::fixed) << std::endl; } void -SolenoidParametrization::printResidualForEtaLine (double eta, double zOrigin) +SolenoidParametrization::printResidualForEtaLine (double eta, double zOrigin) const { - m_signTheta = 1.; - m_zAtAxis = zOrigin; double cotTheta = 1./std::tan(2.*std::atan(1./std::exp(std::abs(eta)))); double z = zOrigin; double r = 0.; @@ -455,8 +472,8 @@ SolenoidParametrization::printResidualForEtaLine (double eta, double zOrigin) for (int k = 0; k < n; ++k) { double b = fieldComponent(r,z,cotTheta); - setParameters(r,z,cotTheta); - double diff = (fieldComponent(z) - b)/s_lightSpeed; + Parameters parms (*this, r, z, cotTheta); + double diff = (fieldComponent(z, parms) - b)/s_lightSpeed; if (r > s_rInner || z > s_zInner) { @@ -472,7 +489,7 @@ SolenoidParametrization::printResidualForEtaLine (double eta, double zOrigin) if (std::abs(diff) > worstDiff) { worstDiff = std::abs(diff); - worstBCalc = fieldComponent(z); + worstBCalc = fieldComponent(z, parms); worstBTrue = b; worstR = r; worstZ = z; @@ -496,6 +513,14 @@ SolenoidParametrization::printResidualForEtaLine (double eta, double zOrigin) << std::resetiosflags(std::ios::fixed) << std::endl; } + +bool SolenoidParametrization::currentMatches (double current) const +{ + // allow 0.1% fluctuation + return current >= m_currentMin && current < m_currentMax; +} + + } // end of namespace diff --git a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/src/SolenoidalIntersector.cxx b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/src/SolenoidalIntersector.cxx index a39a899db30b6fe10d306662872735892fd36af6..dea5a22d9103975fc2c2eb0d509ee58bd2c23d35 100755 --- a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/src/SolenoidalIntersector.cxx +++ b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/src/SolenoidalIntersector.cxx @@ -1,466 +1,545 @@ /* - Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration + Copyright (C) 2002-2017, 2019 CERN for the benefit of the ATLAS collaboration */ -/////////////////////////////////////////////////////////////////// -// SolenoidalIntersector.cxx, (c) ATLAS Detector software -/////////////////////////////////////////////////////////////////// - -#include "GaudiKernel/Incident.h" -#include "GaudiKernel/SystemOfUnits.h" -#include "TrkExSolenoidalIntersector/SolenoidalIntersector.h" -#include "TrkParameters/TrackParameters.h" -#include "TrkSurfaces/CylinderSurface.h" -#include "TrkSurfaces/DiscSurface.h" -#include "TrkSurfaces/PerigeeSurface.h" -#include "TrkSurfaces/PlaneSurface.h" -#include "TrkSurfaces/StraightLineSurface.h" -#include "TrkSurfaces/Surface.h" - -namespace Trk -{ -SolenoidalIntersector::SolenoidalIntersector (const std::string& type, - const std::string& name, - const IInterface* parent) - : AthAlgTool (type, name, parent), - m_incidentSvc ("IncidentSvc", name), - m_magFieldSvc ("MagField::AtlasFieldSvc/AtlasFieldSvc", name), - m_rungeKuttaIntersector ("Trk::RungeKuttaIntersector/RungeKuttaIntersector"), - m_cotTheta (0.), - m_currentMax (0.), - m_currentMin (0.), - m_deltaPhiTolerance (0.01), // upper limit for small angle approx - m_intersectionNumber (0), - m_oneOverSinTheta (1.), - m_pathLength (0.), - m_qOverP (0.), - m_qOverPt (0.), - m_radius (0.), - m_sinTheta (1.), - m_solenoidParametrization (0), - m_surfaceTolerance (2.0*Gaudi::Units::micrometer), - m_validRadius (0.), - m_validZ (0.), - m_countExtrapolations (0), - m_countRKSwitches (0) -{ - declareInterface<Trk::IIntersector>(this); - declareProperty("MagFieldSvc", m_magFieldSvc ); - declareProperty("RungeKuttaIntersector", m_rungeKuttaIntersector); - declareProperty("SurfaceTolerance", m_surfaceTolerance); -} - -SolenoidalIntersector::~SolenoidalIntersector (void) -{} - -StatusCode -SolenoidalIntersector::initialize() -{ - // print name and package version - ATH_MSG_INFO( "SolenoidalIntersector::initialize() - package version " << PACKAGE_VERSION ); - - if (m_incidentSvc.retrieve().isFailure()) - { - ATH_MSG_FATAL( "Failed to retrieve service " << m_incidentSvc ); - return StatusCode::FAILURE; - } - // register to the incident service: - // BeginEvent needed to ensure the solenoid field parametrization is still valid - m_incidentSvc->addListener( this, IncidentType::BeginEvent); - - if (! m_magFieldSvc.empty()) - { - if (m_magFieldSvc.retrieve().isFailure()) - { - ATH_MSG_FATAL( "Failed to retrieve service " << m_magFieldSvc ); - return StatusCode::FAILURE; - } - else - { - ATH_MSG_INFO( "Retrieved service " << m_magFieldSvc ); - } - } - - if (m_rungeKuttaIntersector.retrieve().isFailure()) - { - ATH_MSG_FATAL( "Failed to retrieve tool " << m_rungeKuttaIntersector ); - return StatusCode::FAILURE; - } - else - { - ATH_MSG_INFO( "Retrieved tool " << m_rungeKuttaIntersector ); - } - - return StatusCode::SUCCESS; -} - -StatusCode -SolenoidalIntersector::finalize() -{ - ATH_MSG_INFO( "finalized after " << m_countExtrapolations << " extrapolations with " - << m_countRKSwitches << " switches to RK integration"); - - return StatusCode::SUCCESS; -} - -/** handle for incident service */ -void -SolenoidalIntersector::handle(const Incident& inc) -{ - // (re)parametrize the solenoidal field whenever necessary at BeginEvent - if (inc.type() == IncidentType::BeginEvent ) - { - // intersector invalid when field off - if (! m_magFieldSvc->solenoidOn()) - { - m_currentMax = 0.; - m_validRadius = 0.; - m_validZ = 0.; - return; - } - - if (m_magFieldSvc->solenoidCurrent() > m_currentMax - || m_magFieldSvc->solenoidCurrent() < m_currentMin) - { - // allow 0.1% fluctuation - m_currentMax = 1.001*m_magFieldSvc->solenoidCurrent(); - m_currentMin = 0.999*m_magFieldSvc->solenoidCurrent(); - - // instantiate the field parametrisation - SolenoidParametrization::clearInstance(); - m_solenoidParametrization = SolenoidParametrization::instance(&*m_magFieldSvc); - - // set limit of validity (cylinder bounds) - if (! m_solenoidParametrization) - { - m_validRadius = 0.; - m_validZ = 0.; - ATH_MSG_WARNING(" mag field parametrization fails" ); - return; - } - m_validRadius = m_solenoidParametrization->maximumR(); - m_validZ = m_solenoidParametrization->maximumZ(); - ATH_MSG_INFO( " SolenoidParametrization current: " << m_magFieldSvc->solenoidCurrent() - << " valid radius,Z : " << m_validRadius << ", " << m_validZ); - - if (msgLvl(MSG::DEBUG)) validationAction(); - } - } -} - -/**IIntersector interface method for general Surface type */ -const Trk::TrackSurfaceIntersection* -SolenoidalIntersector::intersectSurface(const Surface& surface, - const TrackSurfaceIntersection* trackIntersection, - const double qOverP) -{ - const PlaneSurface* plane = dynamic_cast<const PlaneSurface*>(&surface); - if (plane) return intersectPlaneSurface(*plane,trackIntersection,qOverP); - - const StraightLineSurface* straightLine = dynamic_cast<const StraightLineSurface*>(&surface); - if (straightLine) return m_rungeKuttaIntersector->approachStraightLineSurface - (*straightLine, trackIntersection, qOverP); - - const CylinderSurface* cylinder = dynamic_cast<const CylinderSurface*>(&surface); - if (cylinder) return intersectCylinderSurface(*cylinder,trackIntersection,qOverP); - - const DiscSurface* disc = dynamic_cast<const DiscSurface*>(&surface); - if (disc) return intersectDiscSurface(*disc,trackIntersection,qOverP); - - const PerigeeSurface* perigee = dynamic_cast<const PerigeeSurface*>(&surface); - if (perigee) return m_rungeKuttaIntersector->approachPerigeeSurface - (*perigee, trackIntersection, qOverP); - - ATH_MSG_WARNING( " unrecognized Surface" ); - return 0; -} - -/**IIntersector interface method for specific Surface type : PerigeeSurface */ -const Trk::TrackSurfaceIntersection* -SolenoidalIntersector::approachPerigeeSurface(const PerigeeSurface& surface, - const TrackSurfaceIntersection* trackIntersection, - const double qOverP) -{ return m_rungeKuttaIntersector->approachPerigeeSurface(surface, trackIntersection, qOverP); } - -/**IIntersector interface method for specific Surface type : StraightLineSurface */ -const Trk::TrackSurfaceIntersection* -SolenoidalIntersector::approachStraightLineSurface(const StraightLineSurface& surface, - const TrackSurfaceIntersection* trackIntersection, - const double qOverP) -{ return m_rungeKuttaIntersector->approachStraightLineSurface(surface, trackIntersection, qOverP); } - -/**IIntersector interface method for specific Surface type : CylinderSurface */ -const Trk::TrackSurfaceIntersection* -SolenoidalIntersector::intersectCylinderSurface(const CylinderSurface& surface, - const TrackSurfaceIntersection* trackIntersection, - const double qOverP) -{ - double endRadius = surface.globalReferencePoint().perp(); - if (endRadius > m_validRadius) - return m_rungeKuttaIntersector->intersectCylinderSurface(surface, trackIntersection, qOverP); - - // set member data unless extrapolation from existing parameters - setParameters(trackIntersection, qOverP); - - if (std::abs(endRadius -trackIntersection->position().perp()) > m_surfaceTolerance - && ! extrapolateToR(endRadius)) return 0; - return intersection(surface); -} - -/**IIntersector interface method for specific Surface type : DiscSurface */ -const Trk::TrackSurfaceIntersection* -SolenoidalIntersector::intersectDiscSurface (const DiscSurface& surface, - const TrackSurfaceIntersection* trackIntersection, - const double qOverP) -{ - double endZ = surface.center().z(); - if (std::abs(endZ) > m_validZ) - return m_rungeKuttaIntersector->intersectDiscSurface(surface, trackIntersection, qOverP); - - // set member data unless extrapolation from existing parameters - setParameters(trackIntersection, qOverP); - if (std::abs(endZ -trackIntersection->position().z()) > m_surfaceTolerance - && ! extrapolateToZ(endZ)) - { - return 0; - } - - return intersection(surface); -} - -/**IIntersector interface method for specific Surface type : PlaneSurface */ -const Trk::TrackSurfaceIntersection* -SolenoidalIntersector::intersectPlaneSurface(const PlaneSurface& surface, - const TrackSurfaceIntersection* trackIntersection, - const double qOverP) -{ - if (std::abs(surface.center().z()) > m_validZ - || surface.center().perp() > m_validRadius) - return m_rungeKuttaIntersector->intersectPlaneSurface(surface, trackIntersection, qOverP); - - // set member data unless extrapolation from existing parameters - setParameters(trackIntersection, qOverP); - - // step until sufficiently near to plane surface - // this gives wrong answer! - // DistanceSolution solution = surface.straightLineDistanceEstimate(m_position,m_direction); - // if (! solution.signedDistance()) return 0; - // double distance = solution.currentDistance(true); - - int numberSteps = 0; - double dot = surface.normal().dot(m_direction); - double offset = surface.normal().dot(surface.center() - m_position); - - while (std::abs(offset) > m_surfaceTolerance*std::abs(dot)) - { - // take care if grazing incidence - quit if looper - if (std::abs(dot) < 0.0001) return 0; - double distance = offset/dot; - - // extrapolate - if (m_sinTheta < 0.9) - { - if (! extrapolateToZ(m_position.z()+distance*m_direction.z())) return 0; - } - else - { - if (! extrapolateToR((m_position+distance*m_direction).perp())) return 0; - } - - // check we are getting closer to the plane, switch to RK in case of difficulty - dot = surface.normal().dot(m_direction); - offset = surface.normal().dot(surface.center() - m_position); - if (std::abs(offset) > m_surfaceTolerance*std::abs(dot) - && (++numberSteps > 5 || std::abs(offset) > 0.5*std::abs(distance*dot))) - { - ++m_countRKSwitches; - ATH_MSG_DEBUG(" switch to RK after " << numberSteps << " steps at offset " - << offset << " dot " << surface.normal().dot(m_direction)); - - return m_rungeKuttaIntersector->intersectPlaneSurface(surface, trackIntersection, qOverP); - } - }; - - return intersection(surface); -} - -/**IIntersector interface method to check validity of parametrization within extrapolation range */ -bool -SolenoidalIntersector::isValid (Amg::Vector3D startPosition, Amg::Vector3D endPosition) const -{ - // check cylinder bounds for valid parametrization - if (std::abs(endPosition.z()) < m_validZ - && endPosition.perp() < m_validRadius - && m_solenoidParametrization->validOrigin(startPosition)) - { - // ATH_MSG_INFO(" choose solenoidal"); - - return true; - } - - // ATH_MSG_INFO(" choose rungeKutta"); - - return false; -} - -/** tabulate parametrization details */ -void -SolenoidalIntersector::validationAction() const -{ - // validate parametrization - if (m_solenoidParametrization) - { - for (int ieta = 0; ieta != 27; ++ieta) - { - double eta = 0.05 + 0.1*static_cast<double>(ieta); - m_solenoidParametrization->printParametersForEtaLine(+eta,0.); - m_solenoidParametrization->printParametersForEtaLine(-eta,0.); - } - for (int ieta = 0; ieta != 27; ++ieta) - { - double eta = 0.05 + 0.1*static_cast<double>(ieta); - m_solenoidParametrization->printResidualForEtaLine(+eta,0.); - m_solenoidParametrization->printResidualForEtaLine(-eta,0.); - } - m_solenoidParametrization->printFieldIntegrals(); - } -} - -// private methods - - -bool -SolenoidalIntersector::extrapolateToR(double endR) -{ - // ATH_MSG_INFO(" extrapolateToR endR " << endR << " from " << m_position.z() - // << " r " << m_radius << " cotTheta " << m_cotTheta); - - double fieldComponent = m_solenoidParametrization->fieldComponent(m_position.z()); - double curvature = fieldComponent*m_qOverPt; - double arcLength = linearArcLength(endR); - if (std::abs(arcLength*curvature) > m_deltaPhiTolerance) - { - double radiusOfCurvature = 1./curvature; - double xCentre = m_position.x() - - radiusOfCurvature*m_direction.y()*m_oneOverSinTheta; - double yCentre = m_position.y() + - radiusOfCurvature*m_direction.x()*m_oneOverSinTheta; - double cosPhi; - double sinPhi; - arcLength = circularArcLength(endR, - radiusOfCurvature, - xCentre, - yCentre, - m_direction.x() * m_oneOverSinTheta, - m_direction.y() * m_oneOverSinTheta, - cosPhi, - sinPhi); - if (std::abs(arcLength*m_cotTheta) < m_surfaceTolerance) - { - m_position.x() = xCentre + radiusOfCurvature*sinPhi; - m_position.y() = yCentre - radiusOfCurvature*cosPhi; - m_position.z() += arcLength*m_cotTheta; - m_radius = endR; - m_direction.x() = cosPhi*m_sinTheta; - m_direction.y() = sinPhi*m_sinTheta; - m_pathLength += arcLength*m_oneOverSinTheta; - arcLength = 0.; - } - } - - double deltaZ = arcLength*m_cotTheta; - if (std::abs(deltaZ) < m_surfaceTolerance) // avoid FPE with constant curvature parabolic approx - { - double cosPhi = m_direction.x()*m_oneOverSinTheta; - double sinPhi = m_direction.y()*m_oneOverSinTheta; - if (std::abs(arcLength) > m_surfaceTolerance) - { - double sinDPhi = 0.5*arcLength*curvature; - double cosDPhi = 1. - 0.5*sinDPhi*sinDPhi * - (1.0+0.25*sinDPhi*sinDPhi); - double temp = cosPhi; - cosPhi = temp*cosDPhi - sinPhi*sinDPhi; - sinPhi = temp*sinDPhi + sinPhi*cosDPhi; - m_direction.x() = (cosPhi*cosDPhi - sinPhi*sinDPhi)*m_sinTheta; - m_direction.y() = (sinPhi*cosDPhi + cosPhi*sinDPhi)*m_sinTheta; - } - - m_position.x() += arcLength*cosPhi; - m_position.y() += arcLength*sinPhi; - m_position.z() += arcLength*m_cotTheta; - m_radius = endR; - m_pathLength += arcLength*m_oneOverSinTheta; - } - else - { - extrapolateToZ(m_position.z() + deltaZ); - if (std::abs(endR - m_radius) > m_surfaceTolerance) - { - deltaZ = linearArcLength(endR) * m_cotTheta; - extrapolateToZ(m_position.z() + deltaZ); - } - } - - return true; -} - -bool -SolenoidalIntersector::extrapolateToZ(double endZ) -{ - double firstIntegral = 0.; - double secondIntegral = 0.; - m_solenoidParametrization->fieldIntegrals(firstIntegral, - secondIntegral, - m_position.z(), - endZ); - // ATH_MSG_INFO(" extrapolateToZ firstIntegral, secondIntegral " << 1.E6*firstIntegral - // << ", " << 1.E6*secondIntegral); - double DFiMax = 0.1; - double cosPhi = m_direction.x()*m_oneOverSinTheta; - double sinPhi = m_direction.y()*m_oneOverSinTheta; - double cosBeta; - double sinBeta; - double deltaPhi2 = secondIntegral*m_qOverPt/std::abs(m_cotTheta); - if (std::abs(deltaPhi2) < DFiMax) - { - double deltaPhi2Squared= deltaPhi2*deltaPhi2; - sinBeta = 1. - 0.166667*deltaPhi2Squared; - cosBeta = -0.5*deltaPhi2*(1.-0.083333*deltaPhi2Squared); - } - else if (2.*std::abs(deltaPhi2) < M_PI) - { - sinBeta = std::sin(deltaPhi2) / deltaPhi2; - cosBeta = (std::cos(deltaPhi2) - 1.) / deltaPhi2; - } - else - { - return false; - } - - double radialDistance = (endZ - m_position.z()) / m_cotTheta; - m_position.x() += radialDistance*(sinPhi*cosBeta + cosPhi*sinBeta); - m_position.y() += radialDistance*(sinPhi*sinBeta - cosPhi*cosBeta); - m_position.z() = endZ; - m_radius = m_position.perp(); - m_pathLength += radialDistance*m_oneOverSinTheta; - - double cosAlpha; - double sinAlpha; - double deltaPhi1 = firstIntegral*m_qOverPt / std::abs(m_cotTheta); - if (std::abs(deltaPhi1) < DFiMax) - { - double deltaPhi1Squared= deltaPhi1*deltaPhi1; - sinAlpha = deltaPhi1*(1. - 0.166667*deltaPhi1Squared); - cosAlpha = 1. - 0.5*deltaPhi1Squared*(1.-0.083333*deltaPhi1Squared); - } - else - { - sinAlpha = std::sin(deltaPhi1); - cosAlpha = std::cos(deltaPhi1); - } - m_direction.x() = (cosPhi*cosAlpha - sinPhi*sinAlpha)*m_sinTheta; - m_direction.y() = (sinPhi*cosAlpha + cosPhi*sinAlpha)*m_sinTheta;; - return true; -} - -} // end of namespace +/////////////////////////////////////////////////////////////////// +// SolenoidalIntersector.cxx, (c) ATLAS Detector software +/////////////////////////////////////////////////////////////////// + +#include "GaudiKernel/SystemOfUnits.h" +#include "TrkExSolenoidalIntersector/SolenoidalIntersector.h" +#include "TrkParameters/TrackParameters.h" +#include "TrkSurfaces/CylinderSurface.h" +#include "TrkSurfaces/DiscSurface.h" +#include "TrkSurfaces/PerigeeSurface.h" +#include "TrkSurfaces/PlaneSurface.h" +#include "TrkSurfaces/StraightLineSurface.h" +#include "TrkSurfaces/Surface.h" + +namespace Trk +{ + +SolenoidalIntersector::Constants::Constants (const SolenoidParametrization& solpar, + const TrackSurfaceIntersection& trackTrackSurfaceIntersection, + const double qOverP) + : m_sinTheta (trackTrackSurfaceIntersection.direction().perp()), + m_oneOverSinTheta (1./m_sinTheta), + m_cotTheta (trackTrackSurfaceIntersection.direction().z() * m_oneOverSinTheta), + m_qOverPt (qOverP * m_oneOverSinTheta), + m_solPar (solpar), + m_lastPosition (trackTrackSurfaceIntersection.position()), + m_solParams (solpar, + trackTrackSurfaceIntersection.position().perp(), + trackTrackSurfaceIntersection.position().z(), + m_cotTheta) +{ +} + + +SolenoidalIntersector::SolenoidalIntersector (const std::string& type, + const std::string& name, + const IInterface* parent) + : base_class (type, name, parent), + m_magFieldSvc ("MagField::AtlasFieldSvc/AtlasFieldSvc", name), + m_rungeKuttaIntersector ("Trk::RungeKuttaIntersector/RungeKuttaIntersector"), + m_deltaPhiTolerance (0.01), // upper limit for small angle approx + m_surfaceTolerance (2.0*Gaudi::Units::micrometer), + m_countExtrapolations (0), + m_countRKSwitches (0) +{ + declareInterface<Trk::IIntersector>(this); + declareProperty("MagFieldSvc", m_magFieldSvc ); + declareProperty("RungeKuttaIntersector", m_rungeKuttaIntersector); + declareProperty("SurfaceTolerance", m_surfaceTolerance); +} + +SolenoidalIntersector::~SolenoidalIntersector (void) +{} + +StatusCode +SolenoidalIntersector::initialize() +{ + // print name and package version + ATH_MSG_INFO( "SolenoidalIntersector::initialize() - package version " << PACKAGE_VERSION ); + + if (! m_magFieldSvc.empty()) + { + if (m_magFieldSvc.retrieve().isFailure()) + { + ATH_MSG_FATAL( "Failed to retrieve service " << m_magFieldSvc ); + return StatusCode::FAILURE; + } + else + { + ATH_MSG_INFO( "Retrieved service " << m_magFieldSvc ); + } + } + + if (m_rungeKuttaIntersector.retrieve().isFailure()) + { + ATH_MSG_FATAL( "Failed to retrieve tool " << m_rungeKuttaIntersector ); + return StatusCode::FAILURE; + } + else + { + ATH_MSG_INFO( "Retrieved tool " << m_rungeKuttaIntersector ); + } + + return StatusCode::SUCCESS; +} + +StatusCode +SolenoidalIntersector::finalize() +{ + ATH_MSG_INFO( "finalized after " << m_countExtrapolations << " extrapolations with " + << m_countRKSwitches << " switches to RK integration"); + + return StatusCode::SUCCESS; +} + + +const SolenoidParametrization* +SolenoidalIntersector::getSolenoidParametrization() const +{ + double current = m_magFieldSvc->solenoidCurrent(); + + // Check to see if the last one we used is ok. + const SolenoidParametrization* lastpar = m_lastSolenoidParametrization.get(); + if (lastpar && lastpar->currentMatches (current)) { + return lastpar; + } + + std::lock_guard<std::mutex> lock (m_mutex); + + // Search for a new one, and release the reference count on the old one. + const SolenoidParametrization* thispar = nullptr; + Parmlist_t::iterator todel = m_solenoidParametrizations.end(); + for (Parmlist_t::iterator it = m_solenoidParametrizations.begin(); + it != m_solenoidParametrizations.end() && (lastpar || !thispar); + ++it) + { + if (&it->first == lastpar) { + lastpar = nullptr; + if (--it->second <= 0) { + todel = it; + } + } + else if (!thispar && it->first.currentMatches (current)) { + thispar = &it->first; + ++it->second; + } + } + + if (todel != m_solenoidParametrizations.end()) { + m_solenoidParametrizations.erase (todel); + } + + if (!thispar) { + // Didn't find one; make a new one. + m_solenoidParametrizations.emplace_back (&*m_magFieldSvc, 1); + thispar = &m_solenoidParametrizations.back().first; + } + + // Remember which one we used last. + m_lastSolenoidParametrization.set (thispar); + + return thispar; +} + + +/**IIntersector interface method for general Surface type */ +const Trk::TrackSurfaceIntersection* +SolenoidalIntersector::intersectSurface(const Surface& surface, + const TrackSurfaceIntersection* trackIntersection, + const double qOverP) +{ + const PlaneSurface* plane = dynamic_cast<const PlaneSurface*>(&surface); + if (plane) return intersectPlaneSurface(*plane,trackIntersection,qOverP); + + const StraightLineSurface* straightLine = dynamic_cast<const StraightLineSurface*>(&surface); + if (straightLine) return m_rungeKuttaIntersector->approachStraightLineSurface + (*straightLine, trackIntersection, qOverP); + + const CylinderSurface* cylinder = dynamic_cast<const CylinderSurface*>(&surface); + if (cylinder) return intersectCylinderSurface(*cylinder,trackIntersection,qOverP); + + const DiscSurface* disc = dynamic_cast<const DiscSurface*>(&surface); + if (disc) return intersectDiscSurface(*disc,trackIntersection,qOverP); + + const PerigeeSurface* perigee = dynamic_cast<const PerigeeSurface*>(&surface); + if (perigee) return m_rungeKuttaIntersector->approachPerigeeSurface + (*perigee, trackIntersection, qOverP); + + ATH_MSG_WARNING( " unrecognized Surface" ); + return 0; +} + +/**IIntersector interface method for specific Surface type : PerigeeSurface */ +const Trk::TrackSurfaceIntersection* +SolenoidalIntersector::approachPerigeeSurface(const PerigeeSurface& surface, + const TrackSurfaceIntersection* trackIntersection, + const double qOverP) +{ return m_rungeKuttaIntersector->approachPerigeeSurface(surface, trackIntersection, qOverP); } + +/**IIntersector interface method for specific Surface type : StraightLineSurface */ +const Trk::TrackSurfaceIntersection* +SolenoidalIntersector::approachStraightLineSurface(const StraightLineSurface& surface, + const TrackSurfaceIntersection* trackIntersection, + const double qOverP) +{ return m_rungeKuttaIntersector->approachStraightLineSurface(surface, trackIntersection, qOverP); } + +/**IIntersector interface method for specific Surface type : CylinderSurface */ +const Trk::TrackSurfaceIntersection* +SolenoidalIntersector::intersectCylinderSurface(const CylinderSurface& surface, + const TrackSurfaceIntersection* trackIntersection, + const double qOverP) +{ + const SolenoidParametrization* solenoidParametrization = + getSolenoidParametrization(); + + double endRadius = surface.globalReferencePoint().perp(); + if (!solenoidParametrization || endRadius > solenoidParametrization->maximumR()) + return m_rungeKuttaIntersector->intersectCylinderSurface(surface, trackIntersection, qOverP); + + Constants* com = nullptr; + std::unique_ptr<TrackSurfaceIntersection> isect = + newIntersection (*trackIntersection, + *solenoidParametrization, + qOverP, + com); + ++m_countExtrapolations; + + double radius2 = isect->position().perp2(); + if (std::abs(endRadius - sqrt(radius2)) > m_surfaceTolerance + && ! extrapolateToR(*isect, radius2, *com, endRadius)) return 0; + return intersection(std::move(isect), *com, surface); +} + +/**IIntersector interface method for specific Surface type : DiscSurface */ +const Trk::TrackSurfaceIntersection* +SolenoidalIntersector::intersectDiscSurface (const DiscSurface& surface, + const TrackSurfaceIntersection* trackIntersection, + const double qOverP) +{ + const SolenoidParametrization* solenoidParametrization = + getSolenoidParametrization(); + + double endZ = surface.center().z(); + if (!solenoidParametrization || std::abs(endZ) > solenoidParametrization->maximumZ()) + return m_rungeKuttaIntersector->intersectDiscSurface(surface, trackIntersection, qOverP); + + Constants* com = nullptr; + std::unique_ptr<TrackSurfaceIntersection> isect = + newIntersection (*trackIntersection, + *solenoidParametrization, + qOverP, + com); + + ++m_countExtrapolations; + + if (std::abs(endZ -trackIntersection->position().z()) > m_surfaceTolerance + && ! extrapolateToZ(*isect, *com, endZ)) + { + return 0; + } + + return intersection(std::move(isect), *com, surface); +} + +/**IIntersector interface method for specific Surface type : PlaneSurface */ +const Trk::TrackSurfaceIntersection* +SolenoidalIntersector::intersectPlaneSurface(const PlaneSurface& surface, + const TrackSurfaceIntersection* trackIntersection, + const double qOverP) +{ + const SolenoidParametrization* solenoidParametrization = + getSolenoidParametrization(); + + if (!solenoidParametrization || + std::abs(surface.center().z()) > solenoidParametrization->maximumZ() + || surface.center().perp() > solenoidParametrization->maximumR()) + return m_rungeKuttaIntersector->intersectPlaneSurface(surface, trackIntersection, qOverP); + + Constants* com = nullptr; + std::unique_ptr<TrackSurfaceIntersection> isect = + newIntersection (*trackIntersection, + *solenoidParametrization, + qOverP, + com); + Amg::Vector3D& pos = isect->position(); + Amg::Vector3D& dir = isect->direction(); + ++m_countExtrapolations; + double radius2 = pos.perp2(); + + // step until sufficiently near to plane surface + // this gives wrong answer! + // DistanceSolution solution = surface.straightLineDistanceEstimate(pos,dir); + // if (! solution.signedDistance()) return 0; + // double distance = solution.currentDistance(true); + + int numberSteps = 0; + double dot = surface.normal().dot(dir); + double offset = surface.normal().dot(surface.center() - pos); + + while (std::abs(offset) > m_surfaceTolerance*std::abs(dot)) + { + // take care if grazing incidence - quit if looper + if (std::abs(dot) < 0.0001) return 0; + double distance = offset/dot; + + // extrapolate + if (com->m_sinTheta < 0.9) + { + if (! extrapolateToZ(*isect, *com, pos.z()+distance*dir.z())) return 0; + radius2 = pos.perp2(); + } + else + { + if (! extrapolateToR(*isect, radius2, *com, (pos+distance*dir).perp())) return 0; + } + + // check we are getting closer to the plane, switch to RK in case of difficulty + dot = surface.normal().dot(dir); + offset = surface.normal().dot(surface.center() - pos); + if (std::abs(offset) > m_surfaceTolerance*std::abs(dot) + && (++numberSteps > 5 || std::abs(offset) > 0.5*std::abs(distance*dot))) + { + ++m_countRKSwitches; + ATH_MSG_DEBUG(" switch to RK after " << numberSteps << " steps at offset " + << offset << " dot " << surface.normal().dot(dir)); + + return m_rungeKuttaIntersector->intersectPlaneSurface(surface, trackIntersection, qOverP); + } + }; + + return intersection(std::move(isect), *com, surface); +} + +/**IIntersector interface method to check validity of parametrization within extrapolation range */ +bool +SolenoidalIntersector::isValid (Amg::Vector3D startPosition, Amg::Vector3D endPosition) const +{ + const SolenoidParametrization* solenoidParametrization = + getSolenoidParametrization(); + + // check cylinder bounds for valid parametrization + if (solenoidParametrization && + std::abs(endPosition.z()) < solenoidParametrization->maximumZ() + && endPosition.perp() < solenoidParametrization->maximumR() + && getSolenoidParametrization()->validOrigin(startPosition)) + { + // ATH_MSG_INFO(" choose solenoidal"); + + return true; + } + + // ATH_MSG_INFO(" choose rungeKutta"); + + return false; +} + +/** tabulate parametrization details */ +void +SolenoidalIntersector::validationAction() const +{ + const SolenoidParametrization* solenoidParametrization = + getSolenoidParametrization(); + + // validate parametrization + if (solenoidParametrization) + { + for (int ieta = 0; ieta != 27; ++ieta) + { + double eta = 0.05 + 0.1*static_cast<double>(ieta); + solenoidParametrization->printParametersForEtaLine(+eta,0.); + solenoidParametrization->printParametersForEtaLine(-eta,0.); + } + for (int ieta = 0; ieta != 27; ++ieta) + { + double eta = 0.05 + 0.1*static_cast<double>(ieta); + solenoidParametrization->printResidualForEtaLine(+eta,0.); + solenoidParametrization->printResidualForEtaLine(-eta,0.); + } + solenoidParametrization->printFieldIntegrals(); + } +} + +// private methods + + +bool +SolenoidalIntersector::extrapolateToR(TrackSurfaceIntersection& isect, + double& radius2, + Constants& com, + const double endR) const +{ + Amg::Vector3D& pos = isect.position(); + Amg::Vector3D& dir = isect.direction(); + + // ATH_MSG_INFO(" extrapolateToR endR " << endR << " from " << pos.z() + // << " r " << sqrt(radius2) << " cotTheta " << com.m_cotTheta); + + double fieldComponent = com.m_solPar.fieldComponent(pos.z(), com.m_solParams); + double curvature = fieldComponent*com.m_qOverPt; + double arcLength = linearArcLength(isect, com, radius2, endR); + if (std::abs(arcLength*curvature) > m_deltaPhiTolerance) + { + double radiusOfCurvature = 1./curvature; + double xCentre = pos.x() - + radiusOfCurvature*dir.y()*com.m_oneOverSinTheta; + double yCentre = pos.y() + + radiusOfCurvature*dir.x()*com.m_oneOverSinTheta; + double cosPhi; + double sinPhi; + arcLength = circularArcLength(endR, + radiusOfCurvature, + xCentre, + yCentre, + dir.x() * com.m_oneOverSinTheta, + dir.y() * com.m_oneOverSinTheta, + cosPhi, + sinPhi); + if (std::abs(arcLength*com.m_cotTheta) < m_surfaceTolerance) + { + pos.x() = xCentre + radiusOfCurvature*sinPhi; + pos.y() = yCentre - radiusOfCurvature*cosPhi; + pos.z() += arcLength*com.m_cotTheta; + radius2 = endR*endR; + dir.x() = cosPhi*com.m_sinTheta; + dir.y() = sinPhi*com.m_sinTheta; + isect.pathlength() += arcLength*com.m_oneOverSinTheta; + arcLength = 0.; + } + } + + double deltaZ = arcLength*com.m_cotTheta; + if (std::abs(deltaZ) < m_surfaceTolerance) // avoid FPE with constant curvature parabolic approx + { + double cosPhi = dir.x()*com.m_oneOverSinTheta; + double sinPhi = dir.y()*com.m_oneOverSinTheta; + if (std::abs(arcLength) > m_surfaceTolerance) + { + double sinDPhi = 0.5*arcLength*curvature; + double cosDPhi = 1. - 0.5*sinDPhi*sinDPhi * + (1.0+0.25*sinDPhi*sinDPhi); + double temp = cosPhi; + cosPhi = temp*cosDPhi - sinPhi*sinDPhi; + sinPhi = temp*sinDPhi + sinPhi*cosDPhi; + dir.x() = (cosPhi*cosDPhi - sinPhi*sinDPhi)*com.m_sinTheta; + dir.y() = (sinPhi*cosDPhi + cosPhi*sinDPhi)*com.m_sinTheta; + } + + pos.x() += arcLength*cosPhi; + pos.y() += arcLength*sinPhi; + pos.z() += arcLength*com.m_cotTheta; + radius2 = endR*endR; + isect.pathlength() += arcLength*com.m_oneOverSinTheta; + } + else + { + extrapolateToZ(isect, com, pos.z() + deltaZ); + radius2 = pos.perp2(); + if (std::abs(endR - sqrt(radius2)) > m_surfaceTolerance) + { + deltaZ = linearArcLength(isect, com, radius2, endR) * com.m_cotTheta; + extrapolateToZ(isect, com, pos.z() + deltaZ); + radius2 = pos.perp2(); + } + } + + return true; +} + +bool +SolenoidalIntersector::extrapolateToZ(TrackSurfaceIntersection& isect, + Constants& com, + const double endZ) const +{ + Amg::Vector3D& pos = isect.position(); + Amg::Vector3D& dir = isect.direction(); + + double firstIntegral = 0.; + double secondIntegral = 0.; + com.m_solPar.fieldIntegrals(firstIntegral, + secondIntegral, + pos.z(), + endZ, + com.m_solParams); + // ATH_MSG_INFO(" extrapolateToZ firstIntegral, secondIntegral " << 1.E6*firstIntegral + // << ", " << 1.E6*secondIntegral); + double DFiMax = 0.1; + double cosPhi = dir.x()*com.m_oneOverSinTheta; + double sinPhi = dir.y()*com.m_oneOverSinTheta; + double cosBeta; + double sinBeta; + double deltaPhi2 = secondIntegral*com.m_qOverPt/std::abs(com.m_cotTheta); + if (std::abs(deltaPhi2) < DFiMax) + { + double deltaPhi2Squared= deltaPhi2*deltaPhi2; + sinBeta = 1. - 0.166667*deltaPhi2Squared; + cosBeta = -0.5*deltaPhi2*(1.-0.083333*deltaPhi2Squared); + } + else if (2.*std::abs(deltaPhi2) < M_PI) + { + sinBeta = std::sin(deltaPhi2) / deltaPhi2; + cosBeta = (std::cos(deltaPhi2) - 1.) / deltaPhi2; + } + else + { + return false; + } + + double radialDistance = (endZ - pos.z()) / com.m_cotTheta; + pos.x() += radialDistance*(sinPhi*cosBeta + cosPhi*sinBeta); + pos.y() += radialDistance*(sinPhi*sinBeta - cosPhi*cosBeta); + pos.z() = endZ; + isect.pathlength() += radialDistance*com.m_oneOverSinTheta; + + double cosAlpha; + double sinAlpha; + double deltaPhi1 = firstIntegral*com.m_qOverPt / std::abs(com.m_cotTheta); + if (std::abs(deltaPhi1) < DFiMax) + { + double deltaPhi1Squared= deltaPhi1*deltaPhi1; + sinAlpha = deltaPhi1*(1. - 0.166667*deltaPhi1Squared); + cosAlpha = 1. - 0.5*deltaPhi1Squared*(1.-0.083333*deltaPhi1Squared); + } + else + { + sinAlpha = std::sin(deltaPhi1); + cosAlpha = std::cos(deltaPhi1); + } + dir.x() = (cosPhi*cosAlpha - sinPhi*sinAlpha)*com.m_sinTheta; + dir.y() = (sinPhi*cosAlpha + cosPhi*sinAlpha)*com.m_sinTheta; + return true; +} + + +std::unique_ptr<TrackSurfaceIntersection> +SolenoidalIntersector::newIntersection (const TrackSurfaceIntersection& isect, + const SolenoidParametrization& solpar, + const double qOverP, + Constants*& com) const +{ + const TrackSurfaceIntersection::IIntersectionCache* oldCache = isect.cache(); + std::unique_ptr<Constants> cache; + const Amg::Vector3D* lastPosition = nullptr; + if (oldCache && typeid(*oldCache) == typeid(Constants)) { + cache = std::make_unique<Constants> (*static_cast<const Constants*> (oldCache)); + lastPosition = &cache->m_lastPosition; + } + else { + cache = std::make_unique<Constants> (solpar, isect, qOverP); + } + + com = cache.get(); + auto newIsect = std::make_unique<TrackSurfaceIntersection> (isect, + std::move (cache)); + if (lastPosition) { + newIsect->position() = *lastPosition; + } + return newIsect; +} + + +} // end of namespace diff --git a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/test/SolenoidParametrization_test.cxx b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/test/SolenoidParametrization_test.cxx index 305f0173d80e76b6190b54109fe5c47f84100bd0..24348ab7dbe290620b4e3864a7c4c7705419c04a 100644 --- a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/test/SolenoidParametrization_test.cxx +++ b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/test/SolenoidParametrization_test.cxx @@ -32,9 +32,7 @@ void test1 (Trk::SolenoidParametrization& sol) { std::cout << "test1\n"; - assert (sol.hasParametrization()); - sol.setParameters (/*r=*/30*cm, /*z=*/123*cm, /*cotTheta=*/1); - assert (sol.hasParametrization()); + Trk::SolenoidParametrization::Parameters parms (sol, /*r=*/30*cm, /*z=*/123*cm, /*cotTheta=*/1); assert( sol.validOrigin (Amg::Vector3D ({10, 10, 10})) ); assert( !sol.validOrigin (Amg::Vector3D ({50, 10, 10})) ); @@ -44,14 +42,14 @@ void test1 (Trk::SolenoidParametrization& sol) assert( Athena_test::isEqual (sol.maximumZ(), 2150) ); assert( Athena_test::isEqual (sol.centralField(), -0.598746) ); - assert( Athena_test::isEqual (sol.fieldComponent (123*cm), -0.592178) ); + assert( Athena_test::isEqual (sol.fieldComponent (123*cm, parms), -0.592178) ); assert( Athena_test::isEqual (sol.fieldComponent (30*cm, 123*cm, 1), -0.551903) ); double firstIntegral = 0; double secondIntegral = 0; sol.fieldIntegrals (firstIntegral, secondIntegral, - 100*cm, 150*cm); + 100*cm, 150*cm, parms); assert( Athena_test::isEqual (firstIntegral, -295.526) ); assert( Athena_test::isEqual (secondIntegral, -296.768) ); } @@ -66,10 +64,8 @@ int main() Incident inc_br ("test", IncidentType::BeginRun); dynamic_cast<IIncidentListener*>(&*field)->handle (inc_br); - Trk::SolenoidParametrization* sol = - Trk::SolenoidParametrization::instance (&*field); - - test1 (*sol); + Trk::SolenoidParametrization sol (&*field); + test1 (sol); return 0; } diff --git a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/test/SolenoidalIntersector_test.cxx b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/test/SolenoidalIntersector_test.cxx index 8e972966b1d0fa8becfd881baad5c031308e84d3..b505fe9d4581da7428311dc74e378ae77d673592 100644 --- a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/test/SolenoidalIntersector_test.cxx +++ b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/test/SolenoidalIntersector_test.cxx @@ -290,8 +290,6 @@ int main() ServiceHandle<MagField::IMagFieldSvc> field ("MagField::AtlasFieldSvc/AtlasFieldSvc", "test"); Incident inc_br ("test", IncidentType::BeginRun); dynamic_cast<IIncidentListener*>(&*field)->handle (inc_br); - Incident inc_be ("test", IncidentType::BeginEvent); - dynamic_cast<IIncidentListener*>(&*tool)->handle (inc_be); test_plane (*tool); test_line (*tool);