From 9656e128b280a413ecf33c40c8de438fbcc11b0f Mon Sep 17 00:00:00 2001 From: scott snyder <sss@karma> Date: Thu, 21 Feb 2019 15:47:41 +0100 Subject: [PATCH] TrkExSolenoidalIntersector: Preparing to make interfaces const. The tool was caching event information in member variables. Rework to remove some of this information and assocciate the rest with the TrackSurfaceIntersection object. Results are identical, as per RunTier0Tests. Also changed CRLF line endings to LF. --- .../SolenoidParametrization.h | 262 ++--- .../SolenoidalIntersector.h | 563 ++++----- .../share/SolenoidalIntersector_test.ref | 5 +- .../src/SolenoidParametrization.cxx | 215 ++-- .../src/SolenoidalIntersector.cxx | 1005 +++++++++-------- .../test/SolenoidParametrization_test.cxx | 14 +- .../test/SolenoidalIntersector_test.cxx | 2 - 7 files changed, 1074 insertions(+), 992 deletions(-) diff --git a/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/TrkExSolenoidalIntersector/SolenoidParametrization.h b/Tracking/TrkExtrapolation/TrkExSolenoidalIntersector/TrkExSolenoidalIntersector/SolenoidParametrization.h index 67dd6513807..8340f1a2016 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 94224bcc987..1ef2b7b75f8 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 ed9b945fd76..e22da500173 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 21236565d5b..3dc8e769667 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 a39a899db30..dea5a22d910 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 305f0173d80..24348ab7dbe 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 8e972966b1d..b505fe9d458 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); -- GitLab