diff --git a/Tr/TrackMonitors/CMakeLists.txt b/Tr/TrackMonitors/CMakeLists.txt index abb9c9a236aff2c8d0c79149513d4f456a27ad05..4340a040cd3802737ae67b6fe1b5105128af6616 100644 --- a/Tr/TrackMonitors/CMakeLists.txt +++ b/Tr/TrackMonitors/CMakeLists.txt @@ -35,6 +35,7 @@ gaudi_add_module(TrackMonitors src/TrackVPOverlapMonitor.cpp src/UTHitEfficiencyMonitor.cpp src/UTTrackMonitor.cpp + src/UTTrackResidualMonitor.cpp src/UTGlobalEffMon.cpp src/VPTrackMonitor.cpp src/VPHitEfficiencyMonitor.cpp diff --git a/Tr/TrackMonitors/src/UTTrackResidualMonitor.cpp b/Tr/TrackMonitors/src/UTTrackResidualMonitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..5d151bf6fa798828138f6c168c2144970284e39f --- /dev/null +++ b/Tr/TrackMonitors/src/UTTrackResidualMonitor.cpp @@ -0,0 +1,304 @@ +/*****************************************************************************\ +* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration * +* * +* This software is distributed under the terms of the GNU General Public * +* Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". * +* * +* In applying this licence, CERN does not waive the privileges and immunities * +* granted to it by virtue of its status as an Intergovernmental Organization * +* or submit itself to any jurisdiction. * +\*****************************************************************************/ +#include "DetDesc/DetectorElement.h" +#include "Event/ITrackFitResult.h" +#include "Event/PrHits.h" +#include "Event/PrKalmanFitResult.h" +#include "Event/Track.h" +#include "Event/TrackFitResult.h" +#include "Gaudi/Accumulators/Histogram.h" +#include "UTDAQ/UTInfo.h" +#include "UTDet/DeUTDetector.h" + +#include "LHCbAlgs/Consumer.h" +#include "TrackInterfaces/ITrackExtrapolator.h" + +#include +/** + * Class for monitoring unbiased UT hits residual of long track + * @author Hangyi Wu + * @date 15-3-2024 + */ + +namespace LHCb::UT { + template + class Mutable { + mutable T m_t; + + public: + template >> + Mutable( Args&&... args ) : m_t{ std::forward( args )... } {} + + template + decltype( auto ) operator[]( Arg&& arg ) const { + return m_t[std::forward( arg )]; + } + }; + + class UTTrackResidualMonitor + : public LHCb::Algorithm::Consumer> { + + public: + UTTrackResidualMonitor( const std::string& name, ISvcLocator* pSvcLocator ); + StatusCode initialize() override; + void operator()( LHCb::Track::Range const&, const LHCb::Pr::UT::Hits&, DetectorElement const&, + const DeUTDetector& ) const override; + + private: + void fillHistograms( LHCb::Track const& track, const LHCb::Pr::UT::Hits& utHits, IGeometryInfo const& geometry, + const DeUTDetector& deUT ) const; + std::optional getResidual( Track const& track, LHCb::Detector::UT::ChannelID hitChanID, + IGeometryInfo const& geometry, const DeUTDetector& deUT ) const; + using Histo1D = Mutable>; + using Histo2D = Mutable>; + using Axis = Gaudi::Accumulators::Axis; + using H2DArg = Gaudi::Accumulators::HistoInputType; + mutable Gaudi::Accumulators::Histogram<2> m_xy{ + this, "xy", "x vs y (mm)", { { 500, -1000, 1000 }, { 500, -1000, 1000 } } }; + + mutable Gaudi::Accumulators::Histogram<1> m_ut_unbiased{ this, "UT_Unbiased", "UT", { 200, -5., 5. } }; + std::map m_sides_unbiased; + std::map m_layers_unbiased; + std::map m_halflayers_unbiased; + std::map m_staves_unbiased; + std::map m_modules_unbiased; + + mutable Gaudi::Accumulators::Histogram<1> m_ut_biased{ this, "UT_Biased", "UT", { 200, -5., 5. } }; + std::map m_sides_biased; + std::map m_layers_biased; + std::map m_halflayers_biased; + std::map m_staves_biased; + std::map m_modules_biased; + + ToolHandle m_extrapolator{ this, "Extrapolator", + "TrackMasterExtrapolator" }; ///< Pointer to extrapolator + Gaudi::Property m_refZ{ this, "ReferenceZ", 2485.0, "midpoint of UT" }; + Gaudi::Property m_trueUnbiased{ this, "TrueUnbiased", false, + "Whether tracks contain UT hits (false) or not (true)." }; + void buildHistogramMaps( const DeUTDetector& deUT ); + }; + + template + void buildHistogram( OWNER owner, std::map& h, K k, std::string name, std::string labels, A axis ) { + h.try_emplace( k, owner, name, labels, axis ); + } + + DECLARE_COMPONENT_WITH_ID( UTTrackResidualMonitor, "UTTrackResidualMonitor" ) + + void UTTrackResidualMonitor::buildHistogramMaps( const DeUTDetector& deUT ) { + + const char* title_unbiased = m_trueUnbiased ? "UnbiasedResidual_True" : "UnbiasedResidual"; + float xmin = m_trueUnbiased ? -10.f : -1.f; + float xmax = -xmin; + unsigned int nbin = 400; + const Axis axis{ nbin, xmin, xmax }; + + // create histograms + std::string sideNames[2] = { "Cside", "Aside" }; + std::string layerNames[4] = { "UTaX", "UTaU", "UTbV", "UTbX" }; + for ( unsigned int i = 0; i < 4; i++ ) { + buildHistogram( this, m_layers_unbiased, i, layerNames[i] + '/' + title_unbiased, title_unbiased, axis ); + buildHistogram( this, m_layers_biased, i, layerNames[i] + '/' + "Residual", "Residual", axis ); + } + for ( const auto& side : deUT.sides() ) { +#ifdef USE_DD4HEP + auto sideID = side.channelID().side(); +#else + auto sideID = side->channelID().side(); +#endif + buildHistogram( this, m_sides_unbiased, sideID, sideNames[sideID] + '/' + title_unbiased, title_unbiased, axis ); + buildHistogram( this, m_sides_biased, sideID, sideNames[sideID] + '/' + "Residual", "Residual", axis ); +#ifdef USE_DD4HEP + for ( const auto& layer : side.layers() ) { + auto layerID = layer.channelID().layer(); + auto layerUniqueID = layer.channelID().uniqueLayer(); +#else + for ( const auto& layer : side->layers() ) { + auto layerID = layer->channelID().layer(); + auto layerUniqueID = layer->channelID().uniqueLayer(); +#endif + std::string layerPath = sideNames[sideID] + "/" + layerNames[layerID]; + buildHistogram( this, m_halflayers_unbiased, layerUniqueID, layerPath + '/' + title_unbiased, title_unbiased, + axis ); + buildHistogram( this, m_halflayers_biased, layerUniqueID, layerPath + '/' + "Residual", "Residual", axis ); +#ifdef USE_DD4HEP + for ( const auto& stave : layer.staves() ) { + auto staveID = stave.channelID().stave(); + auto staveUniqueID = stave.channelID().uniqueStave(); +#else + for ( const auto& stave : layer->staves() ) { + auto staveID = stave->channelID().stave(); + auto staveUniqueID = stave->channelID().uniqueStave(); +#endif + std::string stavePath = + sideNames[sideID] + "/" + layerNames[layerID] + "/" + "Stave" + std::to_string( staveID ); + buildHistogram( this, m_staves_unbiased, staveUniqueID, stavePath + '/' + title_unbiased, title_unbiased, + axis ); + buildHistogram( this, m_staves_biased, staveUniqueID, stavePath + '/' + "Residual", "Residual", axis ); +#ifdef USE_DD4HEP + for ( const auto& face : stave.faces() ) { + auto faceID = face.channelID().face(); + for ( const auto& module : face.modules() ) { + auto moduleID = module.channelID().module(); + auto moduleUniqueID = module.channelID().uniqueModule(); +#else + for ( const auto& face : stave->faces() ) { + auto faceID = face->channelID().face(); + for ( const auto& module : face->modules() ) { + auto moduleID = module->channelID().module(); + auto moduleUniqueID = module->channelID().uniqueModule(); +#endif + std::string modulePath = sideNames[sideID] + "/" + layerNames[layerID] + "/Stave" + + std::to_string( staveID ) + "/Face" + std::to_string( faceID ) + "/Module" + + std::to_string( moduleID ); + buildHistogram( this, m_modules_unbiased, moduleUniqueID, modulePath + '/' + title_unbiased, + title_unbiased, axis ); + buildHistogram( this, m_modules_biased, moduleUniqueID, modulePath + '/' + "Residual", "Residual", axis ); + } + } + } + } + } + } + + StatusCode UTTrackResidualMonitor::initialize() { + return Consumer::initialize().andThen( [&] { + addConditionDerivation( { DeUTDetLocation::location() }, std::string{}, [this]( DeUTDetector const& det ) { + this->buildHistogramMaps( det ); + return ""; + } ); + return StatusCode::SUCCESS; + } ); + } + + UTTrackResidualMonitor::UTTrackResidualMonitor( const std::string& name, ISvcLocator* pSvcLocator ) + : Consumer( name, pSvcLocator, + { { "TracksInContainer", LHCb::TrackLocation::Default }, + { "UTHitsLocation", UTInfo::HitLocation }, + { "StandardGeometryTop", LHCb::standard_geometry_top }, + { "DeUT", DeUTDetLocation::location() } } ) {} + + void UTTrackResidualMonitor::operator()( LHCb::Track::Range const& tracks, const LHCb::Pr::UT::Hits& utHits, + const DetectorElement& lhcb, const DeUTDetector& deUT ) const { + auto& geometry = *lhcb.geometry(); + + // histograms per track + for ( const LHCb::Track* track : tracks ) { + // find the IT hits on the track + fillHistograms( *track, utHits, geometry, deUT ); + } + } + + void UTTrackResidualMonitor::fillHistograms( LHCb::Track const& track, const LHCb::Pr::UT::Hits& utHits, + IGeometryInfo const& geometry, const DeUTDetector& deUT ) const { + if ( m_trueUnbiased ) { + // track parameters at some reference z + LHCb::StateVector aState; + m_extrapolator->propagate( track, m_refZ, aState, geometry ).ignore(); + ++m_xy[{ aState.x() / Gaudi::Units::mm, aState.y() / Gaudi::Units::mm }]; + + const auto hits = utHits.scalar(); + for ( auto const hit : hits ) { + const auto simd_chanid = hit.template get(); + auto const chanID = LHCb::Detector::UT::ChannelID( simd_chanid.cast() ); + auto residualX = getResidual( track, chanID, geometry, deUT ); + if ( residualX ) { + ++m_ut_unbiased[*residualX]; + ++m_sides_unbiased.at( chanID.side() )[*residualX]; + ++m_layers_unbiased.at( chanID.layer() )[*residualX]; + ++m_halflayers_unbiased.at( chanID.uniqueLayer() )[*residualX]; + ++m_staves_unbiased.at( chanID.uniqueStave() )[*residualX]; + ++m_modules_unbiased.at( chanID.uniqueModule() )[*residualX]; + } + } + } else { + dispatch( *track.fitResult(), [&]( const auto& fr ) { + for ( const auto& node : nodes( fr ) ) { + if ( !( node.hasMeasurement() && node.isHitOnTrack() && node.isUT() ) ) continue; + if ( node.isOutlier() ) continue; + if ( node.errResidual() == 0.0 ) continue; + + LHCb::LHCbID lhcbID = id( node ); + LHCb::Detector::UT::ChannelID chanID = lhcbID.utID(); + auto unbiasedResidual = node.unbiasedResidual(); + auto biasedResidual = node.residual(); + + ++m_ut_unbiased[unbiasedResidual]; + ++m_sides_unbiased.at( chanID.side() )[unbiasedResidual]; + ++m_layers_unbiased.at( chanID.layer() )[unbiasedResidual]; + ++m_halflayers_unbiased.at( chanID.uniqueLayer() )[unbiasedResidual]; + ++m_staves_unbiased.at( chanID.uniqueStave() )[unbiasedResidual]; + ++m_modules_unbiased.at( chanID.uniqueModule() )[unbiasedResidual]; + + ++m_ut_biased[biasedResidual]; + ++m_sides_biased.at( chanID.side() )[biasedResidual]; + ++m_layers_biased.at( chanID.layer() )[biasedResidual]; + ++m_halflayers_biased.at( chanID.uniqueLayer() )[biasedResidual]; + ++m_staves_biased.at( chanID.uniqueStave() )[biasedResidual]; + ++m_modules_biased.at( chanID.uniqueModule() )[biasedResidual]; + } + } ); + } + } + std::optional UTTrackResidualMonitor::getResidual( LHCb::Track const& track, + LHCb::Detector::UT::ChannelID hitChanID, + IGeometryInfo const& geometry, + const DeUTDetector& deUT ) const { + auto aSector = deUT.findSector( hitChanID ); +#ifdef USE_DD4HEP + if ( !aSector.isValid() ) { +#else + if ( !aSector ) { +#endif + warning() << "Sector not found with" << hitChanID << endmsg; + return std::nullopt; + } +#ifdef USE_DD4HEP + auto aStrip = aSector.createTraj( hitChanID.strip(), 0 ); +#else + auto aStrip = aSector->trajectory( hitChanID, 0 ); +#endif + ROOT::Math::XYZPoint g1 = aStrip.beginPoint(); + ROOT::Math::XYZPoint g2 = aStrip.endPoint(); + ROOT::Math::XYZPoint hitPos = g1 + ( g2 - g1 ) * 0.5; + auto hitX = hitPos.X(); + auto hitY = hitPos.Y(); + auto hitZ = hitPos.Z(); + + // obtain dxdy + double dxdy, dummy; +#ifdef USE_DD4HEP + aSector.trajectory( hitChanID.strip(), 0, dxdy, dummy, dummy, dummy, dummy, dummy ); +#else + aSector->trajectory( hitChanID.strip(), 0, dxdy, dummy, dummy, dummy, dummy, dummy ); +#endif + + LHCb::StateVector aState; + m_extrapolator->propagate( track, hitZ, aState, geometry ).ignore(); +#ifdef USE_DD4HEP + if ( aSector.sensor().sensorType() == 'A' || aSector.sensor().sensorType() == 'B' ) +#else + if ( aSector->sensor( 0 ).sensorType() == 'A' || aSector->sensor( 0 ).sensorType() == 'B' ) +#endif + if ( std::abs( hitY - aState.y() ) > 60.0 ) return std::nullopt; +#ifdef USE_DD4HEP + if ( aSector.sensor().sensorType() == 'C' || aSector.sensor().sensorType() == 'D' ) +#else + if ( aSector->sensor( 0 ).sensorType() == 'C' || aSector->sensor( 0 ).sensorType() == 'D' ) +#endif + if ( std::abs( hitY - aState.y() ) > 30.0 ) return std::nullopt; + + return hitX - ( aState.x() + ( hitY - aState.y() ) * dxdy ); + } + +} // namespace LHCb::UT