diff --git a/CMakeLists.txt b/CMakeLists.txt index e39c8b2068385282b99f14492d1c6b0be3758926..5b33a74c59bb59464203c9c8ce4fc8408d80cccb 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -137,6 +137,7 @@ lhcb_add_subdirectories( Phys/FunctorCache Utils/CatboostStandaloneEvaluator Vis/PhoenixAlgs + UT/UTMonitors ) gaudi_install(CMAKE diff --git a/Phys/TrackRefitting/src/UpdateVertexCoordinatesOffline.cpp b/Phys/TrackRefitting/src/UpdateVertexCoordinatesOffline.cpp index 0dc413eaa2382b8419c3a11c58e6a4314b2c26d5..b3b63dc66320f5391c480ad7bd06bab107185e88 100644 --- a/Phys/TrackRefitting/src/UpdateVertexCoordinatesOffline.cpp +++ b/Phys/TrackRefitting/src/UpdateVertexCoordinatesOffline.cpp @@ -148,4 +148,4 @@ namespace LHCb { }; DECLARE_COMPONENT_WITH_ID( UpdateVertexCoordinatesOffline, "UpdateVertexCoordinatesOffline" ) -} // namespace LHCb \ No newline at end of file +} // namespace LHCb diff --git a/Tr/TrackMonitors/CMakeLists.txt b/Tr/TrackMonitors/CMakeLists.txt index fffb741970034c08d70216e182d986be827493e4..f0304ba1e2a148a6b0e88ae699426f29c46c6daa 100644 --- a/Tr/TrackMonitors/CMakeLists.txt +++ b/Tr/TrackMonitors/CMakeLists.txt @@ -35,6 +35,8 @@ gaudi_add_module(TrackMonitors src/TrackVertexMonitor.cpp src/TrackVPOverlapMonitor.cpp src/UTTrackMonitor.cpp + src/UTTrackMonitor.cpp + src/UTGlobalEffMon.cpp src/VPTrackMonitor.cpp src/VPHitEfficiencyMonitor.cpp src/VertexCompare.cpp diff --git a/Tr/TrackMonitors/src/UTGlobalEffMon.cpp b/Tr/TrackMonitors/src/UTGlobalEffMon.cpp new file mode 100644 index 0000000000000000000000000000000000000000..476775432d22c421b955235c4957802771f783f7 --- /dev/null +++ b/Tr/TrackMonitors/src/UTGlobalEffMon.cpp @@ -0,0 +1,279 @@ +/*****************************************************************************\ +* (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/FitNode.h" +#include "Event/PrFitNode.h" +#include "Event/PrHits.h" +#include "Event/PrKalmanFitResult.h" +#include "Event/State.h" +#include "Event/Track.h" +#include "Gaudi/Accumulators/Histogram.h" +#include "GaudiKernel/Algorithm.h" +#include "GaudiKernel/PhysicalConstants.h" +#include "Kernel/LHCbID.h" +#include "Kernel/UTNames.h" +#include "LHCbAlgs/Consumer.h" +#include "TrackKernel/TrackFunctors.h" +#include "TrackMonitorTupleBase.h" +#include "UTDAQ/UTDAQHelper.h" +#include "UTDAQ/UTInfo.h" +#include <algorithm> +#include <bitset> +#include <cstdint> +#include <iomanip> +#include <string> +#include <vector> + +/** @class UTGlobalEffMon UTGlobalEffMon.h + */ + +using namespace LHCb; +using namespace Gaudi; + +template <typename T> +class Mutable { + mutable T m_t; + +public: + template <typename... Args> + requires std::is_constructible_v<T, Args...> + Mutable( Args&&... args ) : m_t{ std::forward<Args>( args )... } {} + + template <typename Arg> + decltype( auto ) operator[]( Arg&& arg ) const { + return m_t[std::forward<Arg>( arg )]; + } +}; + +namespace { + template <class TrackContainer, class Predicate> + std::vector<const LHCb::Track*> myselect( const TrackContainer& tracks, Predicate&& selector ) { + std::vector<const LHCb::Track*> rc; + std::copy_if( tracks.begin(), tracks.end(), std::back_inserter( rc ), std::forward<Predicate>( selector ) ); + return rc; + } + + auto TrackTypePredicate = []( LHCb::Track::Types atype ) { + return [=]( const LHCb::Track* track ) { return track->type() == atype; }; + }; + + auto TrackBackwardPredicate = []( const LHCb::Track* track ) { return track->isVeloBackward(); }; + + auto TrackForwardPredicate = []() { return [=]( const LHCb::Track* track ) { return !track->isVeloBackward(); }; }; + + auto TrackVeloSidePredicate = []( int asign ) { + // +1: left side only, 0: overlap track, -1: right side only + // for asign > 0 select left-side tracks only, for a < 0 select right-side tracks, reject overlap tracks + return [=]( const LHCb::Track* track ) { + int side = 0; + bool allhitsleft = true; + bool allhitsright = true; + + const std::vector<LHCb::LHCbID>& track_ids = track->lhcbIDs(); + for ( const auto& track_id : track_ids ) { + if ( !track_id.isVP() ) continue; + + auto vp_id = track_id.vpID(); + // 0 should be right, 1 left side + bool sidepos = ( vp_id.sidepos() == LHCb::Detector::VPChannelID::Side::A ); + + allhitsleft = allhitsleft && sidepos; + allhitsright = allhitsright && !sidepos; + } + if ( allhitsleft ) side = +1; + if ( allhitsright ) side = -1; + return side * asign > 0; + }; + }; + +} // namespace + +template <typename OWNER, typename K, typename H, typename A> +void buildHistogram( OWNER owner, std::map<K, H>& h, K k, std::string name, std::string labels, A axis ) { + h.emplace( std::piecewise_construct, std::forward_as_tuple( k ), std::forward_as_tuple( owner, name, labels, axis ) ); +} + +template <typename TFitResult, typename TNode> +class UTGlobalEffMon + : public LHCb::Algorithm::Consumer<void( LHCb::Track::Range const&, DetectorElement const& ), + LHCb::DetDesc::usesBaseAndConditions<TrackMonitorTupleBase, DetectorElement>> { + +public: + UTGlobalEffMon( const std::string& name, ISvcLocator* pSvcLocator ); + StatusCode initialize() override; + void operator()( LHCb::Track::Range const& tracks, DetectorElement const& ) const override; + +private: + mutable Gaudi::Accumulators::Histogram<1> m_h_ntracks{ + this, "h_ntracks", "Number of long track; Number of long tracks;Events", { 1000, 0, 5000 } }; + mutable Gaudi::Accumulators::Histogram<1> m_h_nutlay{ + this, "h_nutlay", "Number of UT layers per long track; Number of UT layers;Number of tracks", { 5, -0.5, 4.5 } }; + mutable Gaudi::Accumulators::Histogram<1> m_h_nutlay_high_mult{ + this, + "h_nutlay_high_mult", + "Number of UT layers per long track high mult ; Number of UT layers;Number of tracks high mult", + { 5, -0.5, 4.5 } }; + // full-width sensor 2D bins + mutable Gaudi::Accumulators::Histogram<2> m_h_all3{ this, + "h_all3", + "All tracks Y vs X UTbX-midplane;X [mm];Y [mm]", + { { 18, -860.4, +860.4 }, { 14, -669.2, +669.2 } } }; + mutable Gaudi::Accumulators::Histogram<2> m_h_pass3{ this, + "h_pass3", + "UT-matched tracks Y vs X UTbX-midplane;X [mm];Y [mm]", + { { 18, -860.4, +860.4 }, { 14, -669.2, +669.2 } } }; + + mutable Gaudi::Accumulators::Histogram<2> m_h_pass3_low_mult{ + this, + "h_pass3_low_mult", + "UT-matched tracks Y vs X UTbX-midplane low mult;X [mm];Y [mm]", + { { 18, -860.4, +860.4 }, { 14, -669.2, +669.2 } } }; + + mutable Gaudi::Accumulators::Histogram<1> m_h_residual_inner{ + this, + "h_residual_inner", + "UT track residual - inner detector;residual [mm];number of UT clusters", + { 1000, -0.5, 0.5 } }; + mutable Gaudi::Accumulators::Histogram<1> m_h_residual_outer{ + this, + "h_residual_outer", + "UT track residual - outer detector;residual [mm];number of UT clusters", + { 1000, -0.5, 0.5 } }; + + mutable Gaudi::Accumulators::Histogram<1> m_h_residualpull_inner{ + this, + "h_residualpull_inner", + "UT track residual pull - inner detector;pull;number of UT clusters", + { 100, -5, 5 } }; + mutable Gaudi::Accumulators::Histogram<1> m_h_residualpull_outer{ + this, + "h_residualpull_outer", + "UT track residual pull - outer detector;pull;number of UT clsuters", + { 100, -5, 5 } }; +}; + +using UTDataTrMonitor = UTGlobalEffMon<LHCb::TrackFitResult, LHCb::FitNode>; +using UTDataTrMonitor_PrKalman = UTGlobalEffMon<LHCb::PrKalmanFitResult, LHCb::Pr::Tracks::Fit::Node>; +DECLARE_COMPONENT_WITH_ID( UTDataTrMonitor, "UTGlobalEffMon" ) +DECLARE_COMPONENT_WITH_ID( UTDataTrMonitor_PrKalman, "UTGlobalEffMon_PrKalman" ) + +template <typename TFitResult, typename TNode> +StatusCode UTGlobalEffMon<TFitResult, TNode>::initialize() { + return Consumer::initialize().andThen( [&] {} ); +} + +template <typename TFitResult, typename TNode> +UTGlobalEffMon<TFitResult, TNode>::UTGlobalEffMon( const std::string& name, ISvcLocator* pSvcLocator ) + : Consumer( name, pSvcLocator, + { { "TracksInContainer", LHCb::TrackLocation::Default }, + { "StandardGeometryTop", LHCb::standard_geometry_top } } ) {} + +template <typename TFitResult, typename TNode> +void UTGlobalEffMon<TFitResult, TNode>::operator()( LHCb::Track::Range const& tracks, + const DetectorElement& lhcb ) const { + auto& geometry = *lhcb.geometry(); + const double zutlay3 = 2652.5; + const double UT_sensor_width = 95.6; // do not change this because you think this is not correct! + int nTracks = tracks.size(); + + if ( nTracks > 10000 ) { nTracks = 10000; } // saturation + ++m_h_ntracks[nTracks]; + + for ( const LHCb::Track* track : tracks ) { + auto fitResult = dynamic_cast<const TFitResult*>( track->fitResult() ); + if ( fitResult ) { + LHCb::StateVector aState; + extrapolator()->propagate( *track, zutlay3, aState, geometry ).ignore(); + if ( aState.x() < -9990 ) { continue; } // Skip unphysical tracks + + // tracks must be within UT layer 3 acceptance (exclude also generous region around the beam pipe ) + double tr2utlax3 = aState.x(); + double tr2utlay3 = aState.y(); + if ( fabs( tr2utlax3 ) > 9 * UT_sensor_width ) continue; + if ( fabs( tr2utlay3 ) > 7 * UT_sensor_width ) continue; + if ( sqrt( pow( tr2utlay3, 2 ) + pow( tr2utlax3, 2 ) ) < 45 ) continue; + + // PT cut + try { + + auto cState = track->closestToBeamState(); + if ( cState.pt() < 1000.0 ) continue; + + } catch ( ... ) { continue; } + + // all tracks passing the cuts (for efficiency map denominator) + ++m_h_all3[{ tr2utlax3, tr2utlay3 }]; + + const double kDummy = 1000.0; + + int _tr2ut[4] = { -1, -1, -1, -1 }; + double _tr2utr[4] = { kDummy, kDummy, kDummy, kDummy }; + double _tr2utchi[4] = { 0.0, 0.0, 0.0, 0.0 }; + + unsigned int utnode = 0; + for ( const auto& node : nodes( *fitResult ) ) { + if ( !( node.hasMeasurement() && node.isHitOnTrack() && node.isUT() ) ) continue; + if ( node.isOutlier() ) continue; + LHCb::LHCbID lhcbID = id( node ); + + Detector::UT::ChannelID chan = lhcbID.utID(); + + unsigned int chID = static_cast<unsigned int>( chan ); + + double res = node.residual(); + double chi = res / node.errResidual(); + double ares = fabs( res ); + unsigned int _layer = ( chID >> 18 ) & 3; + + // Loop over layers + if ( ares < fabs( _tr2utr[_layer] ) ) { + _tr2utr[_layer] = res; + _tr2utchi[_layer] = chi; + _tr2ut[_layer] = chID; + } + utnode++; + + } // nodes loop + + unsigned int nlay = 0; + for ( int layer = 0; layer < 4; ++layer ) { + if ( _tr2ut[layer] != -1 ) { + ++nlay; + // Extract values from the current layer + UInt_t _mo = ( _tr2ut[layer] >> 10 ) & 7; + UInt_t _st = ( _tr2ut[layer] >> 14 ) & 15; + bool inner = ( _st == 0 ) && ( _mo == 3 || _mo == 4 ); + if ( inner ) { + ++m_h_residual_inner[_tr2utr[layer]]; + ++m_h_residualpull_inner[_tr2utchi[layer]]; + } else { + ++m_h_residual_outer[_tr2utr[layer]]; + ++m_h_residualpull_outer[_tr2utchi[layer]]; + } + } + } + + // tracks matched to UT hits (for nominator of efficiency map) + if ( nlay > 0 ) ++m_h_pass3[{ tr2utlax3, tr2utlay3 }]; + + // change it for protons 2025 + ++m_h_nutlay_high_mult[nlay]; + + if ( nTracks > 4 && nTracks <= 100 ) { // you may want to move these limits to be configurable parameters + + // UT layer counting for efficiency and ghost rate estimates + ++m_h_nutlay[nlay]; + if ( nlay > 0 ) ++m_h_pass3_low_mult[{ tr2utlax3, tr2utlay3 }]; + } + } + } +} diff --git a/UT/UTMonitors/CMakeLists.txt b/UT/UTMonitors/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..b5e97abfb01b6e3b62e7504090be3cbfa7b9f1b5 --- /dev/null +++ b/UT/UTMonitors/CMakeLists.txt @@ -0,0 +1,39 @@ +############################################################################### +# (c) Copyright 2000-2021 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. # +############################################################################### +#[=======================================================================[.rst: +UT/UTMonitors +------------- +#]=======================================================================] + +gaudi_add_module(UTMonitors + SOURCES + src/UTDigitsMonitor.cpp + src/UTTAEMonitor.cpp + src/UTSuperTAEMonitor.cpp + src/UTErrorMonitor.cpp + src/UTNZSMonitor.cpp + src/UTBSMonitor.cpp + src/UTVeloUTCorrelationsMonitor.cpp + LINK + Boost::headers + Gaudi::GaudiAlgLib + Gaudi::GaudiKernel + Gaudi::GaudiUtilsLib + LHCb::DAQEventLib + LHCb::DigiEvent + LHCb::LHCbAlgsLib + LHCb::LHCbKernel + LHCb::RecEvent + LHCb::UTDetLib + LHCb::UTKernelLib + LHCb::UTTELL1Event + ROOT::Hist +) diff --git a/UT/UTMonitors/src/UTBSMonitor.cpp b/UT/UTMonitors/src/UTBSMonitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c6de3a6d6221356058c237f9f39769bd97b40ede --- /dev/null +++ b/UT/UTMonitors/src/UTBSMonitor.cpp @@ -0,0 +1,456 @@ +/***************************************************************************** \ +* (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 "Event/UTDigit.h" +#include "GaudiAlg/GaudiHistoAlg.h" +#include "Kernel/IUTReadoutTool.h" +#include "Kernel/UTDAQBoard.h" +#include "Kernel/UTDAQDefinitions.h" +#include "Kernel/UTDAQID.h" +#include "LHCbAlgs/Consumer.h" +#include "LHCbAlgs/Transformer.h" +#include "UTDAQ/UTCoordinatesMap.h" +#include "UTDAQ/UTDAQHelper.h" +#include "UTDAQ/UTInfo.h" +#include "UTDet/DeUTDetector.h" +#include <Event/RawBank.h> +#include <Event/RawEvent.h> +#include <Gaudi/Accumulators/Histogram.h> +#include <Gaudi/Accumulators/HistogramArray.h> +#include <Kernel/IUTReadoutTool.h> +#include <TMath.h> +#include <mutex> +#include <optional> +#include <string> + +/** @class UTBSMonitor UTBSMonitor.cpp + * -------------------------------------------------- + * Counters for additional monitoring of UT + * The algorithm produces several useful histograms + * @author Wojciech Krupa + * @date 07/06/2024 + * -------------------------------------------------- + **/ + +using namespace LHCb; +namespace GA = Gaudi::Accumulators; + +// Definition of axis +const GA::Axis<double> AxisADC = GA::Axis<double>( 64, -32.5, 31.5, "nADC" ); //-32, 31 +const GA::Axis<double> AxisADC_signed = GA::Axis<double>( 32, -0.5, 31.5, "nADC" ); // 0-31 +const GA::Axis<double> AxisDIGIT = GA::Axis<double>( 101, -0.5, 7000.5, "UTDigits (size of digit container)" ); +const GA::Axis<double> AxisHits = GA::Axis<double>( 100, -0.5, 1500.5, "Hits" ); +const GA::Axis<double> AxisUAChannels = GA::Axis<double>( 268288, 0, 268288, "GlobalChannel_ID" ); +const GA::Axis<double> AxisUCChannels = GA::Axis<double>( 268288, 268288, 536576, "GlobalChannel_ID" ); +const GA::Axis<double> AxisUAChips = GA::Axis<double>( 2096, -0.5, 2095.5, "GlobalASIC_ID" ); +const GA::Axis<double> AxisUCChips = GA::Axis<double>( 2096, 2095.5, 4191.5, "GlobalASIC_ID" ); +const GA::Axis<double> AxisThresholds = GA::Axis<double>( 32, -0.5, 31.5, "Threshold" ); +const GA::Axis<double> AxisChannels = GA::Axis<double>( 64, -0.5, 63.5, "Channels" ); +const GA::Axis<double> AxisUA_aUChips = GA::Axis<double>( 496, -0.5, 495.5, "GlobalASIC_ID" ); +const GA::Axis<double> AxisUA_aXChips = GA::Axis<double>( 496, 495.5, 991.5, "GlobalASIC_ID" ); +const GA::Axis<double> AxisUA_bVChips = GA::Axis<double>( 552, 991.5, 1543.5, "GlobalASIC_ID" ); +const GA::Axis<double> AxisUA_bXChips = GA::Axis<double>( 552, 1543.5, 2095.5, "GlobalASIC_ID" ); +const GA::Axis<double> AxisUC_aUChips = GA::Axis<double>( 496, 2095.5, 2591.5, "GlobalASIC_ID" ); +const GA::Axis<double> AxisUC_aXChips = GA::Axis<double>( 496, 2591.5, 3087.5, "GlobalASIC_ID" ); +const GA::Axis<double> AxisUC_bVChips = GA::Axis<double>( 552, 3087.5, 3639.5, "GlobalASIC_ID" ); +const GA::Axis<double> AxisUC_bXChips = GA::Axis<double>( 552, 3639.5, 4191.5, "GlobalASIC_ID" ); + +std::vector<unsigned int> m_channel_counter( 536577, 0 ); +std::vector<unsigned int> m_asic_counter( 4192, 0 ); +std::map<unsigned int, unsigned int> m_threshold_counter; +std::map<unsigned int, unsigned int> m_maxchannel_counter; + +// 2D plots +const double positionasic[4][3] = { + { -0.4375, -0.3125, -0.1875 }, // Chip 0 + { -0.1875, -0.0625, -0.0625 }, // Chip 1 + { +0.1875, +0.0625, +0.0625 }, // Chip 2 + { +0.4375, +0.3125, +0.1875 } // Chip 3 +}; + +// UT layers +const std::array<std::string, 4> UT_layers = { "UTaX", "UTaU", "UTbV", "UTbX" }; + +enum class masks { channel = 0x000001ff, lane = 0x00000e00, board = 0x000ff000 }; +template <masks m> +[[nodiscard]] static constexpr unsigned int extract( unsigned int i ) { + constexpr auto b = std::countr_zero( static_cast<unsigned int>( m ) ); + return ( i & static_cast<unsigned int>( m ) ) >> b; +} + +// Tools for booking histograms +namespace Utility { + template <typename T, typename OWNER> + void map_emplace( T& t, typename T::key_type key, OWNER* owner, std::string const& title, + Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis1 ) { + t.emplace( std::piecewise_construct, std::forward_as_tuple( key ), + std::forward_as_tuple( owner, key, title, axis1 ) ); + } +} // namespace Utility +/** ------------------------------------------------------------------------------------------------------------------------ */ + +class UTBSMonitor + : public LHCb::Algorithm::Consumer<void( UTDigits const&, UTDigits const&, DeUTDetector const& ), + LHCb::DetDesc::usesBaseAndConditions<GaudiHistoAlg, DeUTDetector>> { +public: + UTBSMonitor( const std::string& name, ISvcLocator* svcloc ) + : Consumer{ name, + svcloc, + { { "InputData", UTDigitLocation::UTDigits }, + { "InputErrorData", UTDigitLocation::UTDigits }, + { "UTLocation", DeUTDetLocation::location() } } } {} + ToolHandle<IUTReadoutTool> readoutTool{ this, "ReadoutTool", "UTReadoutTool" }; + + StatusCode initialize() override { + return Consumer::initialize().andThen( [&] { + // Set the top directory to UT + if ( histoTopDir().empty() ) setHistoTopDir( "" ); + + for ( const auto& module : UTMap.getModulesNames() ) { + for ( unsigned int i = 0; i < 4; i++ ) + Utility::map_emplace( m_1d_ADCCounter, module + ".Chip" + std::to_string( i ), this, + module + ".Chip" + std::to_string( i ), { 32, -0.5, 31.5 } ); + } + } ); + } + + void operator()( const UTDigits& digitsCont, const UTDigits& digitserrCont, DeUTDetector const& det ) const override { + + // General counters + const unsigned int nDigits = digitsCont.size() + digitserrCont.size(); + + if ( msgLevel( MSG::DEBUG ) ) debug() << "---------- Found nDigits: " << nDigits << endmsg; + ++m_nDigits[nDigits]; + + // Hitmaps plots and ADC + std::fill( m_channel_counter.begin(), m_channel_counter.end(), 0 ); + std::fill( m_asic_counter.begin(), m_asic_counter.end(), 0 ); + + // For maxADC plots + unsigned int prev_channel = 0; + unsigned int prev_adc = 0; + LHCb::UTDigit* aDigit_max; + + // UT bank + for ( const auto& d : digitsCont ) { + + fillHeatPlots( d ); + + // First event + if ( prev_channel == 0 ) { + prev_channel = d->strip(); + prev_adc = d->depositedCharge(); + aDigit_max = d; + continue; + } + // If cluster size >1 + if ( d->strip() - prev_channel == 1 ) + if ( d->depositedCharge() > prev_adc ) aDigit_max = d; // if ADC max in claster + // End of cluster + if ( d->strip() - prev_channel != 1 ) { + fillHeatPlots_MAX( aDigit_max ); + aDigit_max = d; + } + + prev_channel = d->strip(); + prev_adc = d->depositedCharge(); + } + + // UTError bank + for ( const auto& d : digitserrCont ) { + + fillHeatPlots( d ); + + // First event + if ( prev_channel == 0 ) { + prev_channel = d->strip(); + prev_adc = d->depositedCharge(); + aDigit_max = d; + continue; + } + // If cluster size >1 + if ( d->strip() - prev_channel == 1 ) + if ( d->depositedCharge() > prev_adc ) aDigit_max = d; // if ADC max in claster + // End of cluster + if ( d->strip() - prev_channel != 1 ) { + fillHeatPlots_MAX( aDigit_max ); + aDigit_max = d; + } + + prev_channel = d->strip(); + prev_adc = d->depositedCharge(); + } + + // Channel hit counters + for ( size_t i = 0; i < m_channel_counter.size(); ++i ) { + if ( i <= 268288 ) + hitRateChannels_UA[i] += m_channel_counter[i]; + else + hitRateChannels_UC[i] += m_channel_counter[i]; + } + + // Chip hit counters + for ( size_t i = 0; i < m_asic_counter.size(); ++i ) { + if ( i <= 495 ) { + hitRateChipAverage_UA_aU[i] += m_asic_counter[i]; + hitRateChipAverage_UA_aU_radial[UTMap.getRadialID( i )] += m_asic_counter[i]; + } else if ( i <= 991 ) { + hitRateChipAverage_UA_aX[i] += m_asic_counter[i]; + hitRateChipAverage_UA_aX_radial[UTMap.getRadialID( i )] += m_asic_counter[i]; + } else if ( i <= 1543 ) { + hitRateChipAverage_UA_bV[i] += m_asic_counter[i]; + hitRateChipAverage_UA_bV_radial[UTMap.getRadialID( i )] += m_asic_counter[i]; + } else if ( i <= 2095 ) { + hitRateChipAverage_UA_bX[i] += m_asic_counter[i]; + hitRateChipAverage_UA_bX_radial[UTMap.getRadialID( i )] += m_asic_counter[i]; + } else if ( i <= 2591 ) { + hitRateChipAverage_UC_aU[i] += m_asic_counter[i]; + hitRateChipAverage_UC_aU_radial[UTMap.getRadialID( i )] += m_asic_counter[i]; + } else if ( i <= 3087 ) { + hitRateChipAverage_UC_aX[i] += m_asic_counter[i]; + hitRateChipAverage_UC_aX_radial[UTMap.getRadialID( i )] += m_asic_counter[i]; + } else if ( i <= 3639 ) { + hitRateChipAverage_UC_bV[i] += m_asic_counter[i]; + hitRateChipAverage_UC_bV_radial[UTMap.getRadialID( i )] += m_asic_counter[i]; + } else if ( i <= 4191 ) { + hitRateChipAverage_UC_bX[i] += m_asic_counter[i]; + hitRateChipAverage_UC_bX_radial[UTMap.getRadialID( i )] += m_asic_counter[i]; + } + } + + std::map<unsigned int, unsigned int> count_th; // for summary plots + std::map<unsigned int, unsigned int> count_ch; + + for ( size_t i = 0; i < m_asic_counter.size(); ++i ) { + if ( m_asic_counter[i] > 0 ) { + if ( m_asic_counter[i] > m_maxchannel_counter[i] ) { + m_maxchannel_counter[i] = m_asic_counter[i]; + } // for profile histo + unsigned int channelID = + UTMap.getChannel( UTMap.getASICName( i ).substr( 0, UTMap.getASICName( i ).find( '.' ) ) ); + fillHeatMap( channelID + 128 * std::atoi( &UTMap.getASICName( i ).back() ), m_threshold_counter[i], + m_maxchannel_counter[i], det ); + + ++count_th[m_threshold_counter[i]]; + ++count_ch[m_maxchannel_counter[i]]; + } + } + + for ( const auto& entry : count_th ) { ZS_thresholds[entry.first] += entry.second; } + for ( const auto& entry : count_ch ) { ZS_channels[entry.first] += entry.second; } + } + +private: + mutable UTCoordinatesMap UTMap; + + // General plots + // Digits counter + mutable GA::Histogram<1> m_nDigits{ this, "nDigits", "Total nDigits", AxisDIGIT }; + // ADC counter + mutable GA::Histogram<1> m_nADC{ this, "nADC", "Total nADC", AxisADC_signed }; + + // Hitrate counters - channel + mutable GA::ProfileHistogram<1> hitRateChannels_UA{ this, "hitRateChannels_UA", "hitRateChannels_UA", + AxisUAChannels }; + mutable GA::ProfileHistogram<1> hitRateChannels_UC{ this, "hitRateChannels_UC", "hitRateChannels_UC", + AxisUCChannels }; + + // Hitrate counters - chips + mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_aU{ this, "Average_hitRate_UAaU", "Average_hitRate_UAaU", + AxisUA_aUChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_aX{ this, "Average_hitRate_UAaX", "Average_hitRate_UAaX", + AxisUA_aXChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_bV{ this, "Average_hitRate_UAbV", "Average_hitRate_UAbV", + AxisUA_bVChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_bX{ this, "Average_hitRate_UAbX", "Average_AhitRate_UAbX", + AxisUA_bXChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_aU{ this, "Average_hitRate_UCaU", "Average_hitRate_UCaU", + AxisUC_aUChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_aX{ this, "Average_hitRate_UCaX", "Average_hitRate_UCaX", + AxisUC_aXChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_bV{ this, "Average_hitRate_UCbV", "Average_hitRate_UCbV", + AxisUC_bVChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_bX{ this, "Average_hitRate_UCbX", "Average_hitRate_UCbX", + AxisUC_bXChips }; + + // Hitrate counters - chip radial + mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_aU_radial{ this, "Average_hitRate_UAaU_radial", + "Average_hitRate_UAaU_radial", AxisUA_aUChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_aX_radial{ this, "Average_hitRate_UAaX_radial", + "Average_hitRate_UAaX_radial", AxisUA_aXChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_bV_radial{ this, "Average_hitRate_UAbV_radial", + "Average_hitRate_UAbV_radial", AxisUA_bVChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UA_bX_radial{ this, "Average_hitRate_UAbX_radial", + "Average_AhitRate_UAbX_radial", AxisUA_bXChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_aU_radial{ this, "Average_hitRate_UCaU_radial", + "Average_hitRate_UCaU_radial", AxisUC_aUChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_aX_radial{ this, "Average_hitRate_UCaX_radial", + "Average_hitRate_UCaX_radial", AxisUC_aXChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_bV_radial{ this, "Average_hitRate_UCbV_radial", + "Average_hitRate_UCbV_radial", AxisUC_bVChips }; + mutable GA::ProfileHistogram<1> hitRateChipAverage_UC_bX_radial{ this, "Average_hitRate_UCbX_radial", + "Average_hitRate_UCbX_radial", AxisUC_bXChips }; + + // Special ADC counters - average ADC + mutable GA::ProfileHistogram<1> ADCChipAverage_UA_aU{ this, "Average_ADC_UAaU", "Average_ADC_UAaU", AxisUA_aUChips }; + mutable GA::ProfileHistogram<1> ADCChipAverage_UA_aX{ this, "Average_ADC_UAaX", "Average_ADC_UAaX", AxisUA_aXChips }; + mutable GA::ProfileHistogram<1> ADCChipAverage_UA_bV{ this, "Average_ADC_UAbV", "Average_ADC_UAbV", AxisUA_bVChips }; + mutable GA::ProfileHistogram<1> ADCChipAverage_UA_bX{ this, "Average_ADC_UAbX", "Average_ADC_UAbX", AxisUA_bXChips }; + mutable GA::ProfileHistogram<1> ADCChipAverage_UC_aU{ this, "Average_ADC_UCaU", "Average_ADC_UCaU", AxisUC_aUChips }; + mutable GA::ProfileHistogram<1> ADCChipAverage_UC_aX{ this, "Average_ADC_UCaX", "Average_ADC_UCaX", AxisUC_aXChips }; + mutable GA::ProfileHistogram<1> ADCChipAverage_UC_bV{ this, "Average_ADC_UCbV", "Average_ADC_UCbV", AxisUC_bVChips }; + mutable GA::ProfileHistogram<1> ADCChipAverage_UC_bX{ this, "Average_ADC_UCbX", "Average_ADC_UCbX", AxisUC_bXChips }; + + // Hitmap - channel limit & zs threshold per ASIC - 1D + mutable GA::ProfileHistogram<1> ZS_thresholds{ this, "ZS_thresholds", "ZS_thresholds", AxisThresholds }; + mutable GA::ProfileHistogram<1> ZS_channels{ this, "ZS_channels", "ZS_channels", AxisChannels }; + + // Hitmap - channel limit & zs threshold per ASIC - 2D + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::ProfileHistogram<2>, 4> m_2d_ZS_th_chips{ + this, + []( int i ) { return fmt::format( "UT_ZS_th_Chips_Layer{}", i ); }, + []( int i ) { return fmt::format( "zs_th of Layer{}", i ); }, + { 144, -9, 9, "Staves (ASICs)" }, + { 28, -7, 7, "Modules" } }; + + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::ProfileHistogram<2>, 4> m_2d_ZS_ch_chips{ + this, + []( int i ) { return fmt::format( "UT_ZS_ch_Chips_Layer{}", i ); }, + []( int i ) { return fmt::format( "zs_channel of Layer{}", i ); }, + { 144, -9, 9, "Staves (ASICs)" }, + { 28, -7, 7, "Modules" } }; + + // The container for pedestal per channel + mutable std::map<std::string, GA::Histogram<1>> m_1d_ADCCounter; + + void fillHeatPlots( const LHCb::UTDigit* aDigit ) const { + + // Let's find where we are + std::tuple<float, float, float, float, std::string, std::string> tuple = + UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() ); + std::string module_name = std::get<4>( tuple ); + std::string type = std::get<5>( tuple ); + + // What a beautiful line of code + unsigned int globalASIC_ID = UTMap.getASICNumber( UT_layers[aDigit->layer()] + "_" + module_name + ".Chip" + + std::to_string( aDigit->strip() / 128 ) ); + ++m_channel_counter[globalASIC_ID * 128 + aDigit->strip() % 128]; + ++m_asic_counter[globalASIC_ID]; + + if ( globalASIC_ID <= 495 ) + ADCChipAverage_UA_aU[globalASIC_ID] += aDigit->depositedCharge(); + else if ( globalASIC_ID <= 991 ) + ADCChipAverage_UA_aX[globalASIC_ID] += aDigit->depositedCharge(); + else if ( globalASIC_ID <= 1543 ) + ADCChipAverage_UA_bV[globalASIC_ID] += aDigit->depositedCharge(); + else if ( globalASIC_ID <= 2095 ) + ADCChipAverage_UA_bX[globalASIC_ID] += aDigit->depositedCharge(); + else if ( globalASIC_ID <= 2591 ) + ADCChipAverage_UC_aU[globalASIC_ID] += aDigit->depositedCharge(); + else if ( globalASIC_ID <= 3087 ) + ADCChipAverage_UC_aX[globalASIC_ID] += aDigit->depositedCharge(); + else if ( globalASIC_ID <= 3639 ) + ADCChipAverage_UC_bV[globalASIC_ID] += aDigit->depositedCharge(); + else if ( globalASIC_ID <= 4191 ) + ADCChipAverage_UC_bX[globalASIC_ID] += aDigit->depositedCharge(); + + if ( m_threshold_counter[globalASIC_ID] == 0 ) + m_threshold_counter[globalASIC_ID] = 31; // we can't start from zero to minimise and we can't init all asics with + // 31 because we want keep disabled empty + + if ( aDigit->depositedCharge() < m_threshold_counter[globalASIC_ID] ) + m_threshold_counter[globalASIC_ID] = aDigit->depositedCharge(); + + // ADC counter + ++m_nADC[aDigit->depositedCharge()]; + } + + void fillHeatPlots_MAX( const LHCb::UTDigit* aDigit ) const { + // Let's find where we are + std::tuple<float, float, float, float, std::string, std::string> tuple = + UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() ); + std::string module_name = std::get<4>( tuple ); + std::string type = std::get<5>( tuple ); + ++m_1d_ADCCounter.at( UT_layers[aDigit->layer()] + "_" + module_name + ".Chip" + + std::to_string( aDigit->strip() / 128 ) )[aDigit->depositedCharge()]; + } + + void fillHeatMap( unsigned int channel, unsigned int zs_th, unsigned int ch, DeUTDetector const& det ) const { + Detector::UT::ChannelID channelID( channel ); + + // Let's find where we are + auto tuple = + UTMap.getTuple( channelID.module(), channelID.face(), channelID.stave(), channelID.side(), channelID.sector() ); + + float x = std::get<0>( tuple ); + float y = std::get<1>( tuple ); + std::string module_name = std::get<4>( tuple ); + std::string type = std::get<5>( tuple ); + auto aSector = det.findSector( channelID ); + +#ifdef USE_DD4HEP + auto trajStrip0 = aSector.createTraj( 0, 0 ); + auto trajStrip511 = aSector.createTraj( 511, 0 ); + auto trajStrip127 = aSector.createTraj( 127, 0 ); +#else + auto trajStrip0 = aSector->trajectory( channelID, 0 ); + auto trajStrip511 = aSector->trajectory( LHCb::Detector::UT::ChannelID{ channelID + 511 }, 0 ); + auto trajStrip127 = aSector->trajectory( LHCb::Detector::UT::ChannelID{ channelID + 127 }, 0 ); +#endif + double stripx0 = -1 * ( trajStrip0.endPoint().x() + trajStrip0.beginPoint().x() ) * 0.5; + double stripx511 = -1 * ( trajStrip511.endPoint().x() + trajStrip511.beginPoint().x() ) * 0.5; + double stripx127 = -1 * ( trajStrip127.endPoint().x() + trajStrip127.beginPoint().x() ) * 0.5; + double stripy = ( trajStrip0.endPoint().y() + trajStrip0.beginPoint().y() ) * 0.5; + + double stripwidth = stripx511 - stripx0; + double stripwidth2 = stripx127 - stripx0; + int chip; + + if ( stripwidth > 0 ) + chip = int( channelID.strip() / 128 ); + else + chip = 3 - int( channelID.strip() / 128 ); + + double positionasic_one = positionasic[chip][0]; + double positionasic_two = positionasic[chip][1]; + double positionasic_thr = positionasic[chip][2]; + + // CHIPS Maps // + if ( ( channelID.stave() > 1 ) || ( fabs( y ) > 2 ) ) { + m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_one, y + 0.25 }] += zs_th; + m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_one, y - 0.25 }] += zs_th; + m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_two, y + 0.25 }] += zs_th; + m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_two, y - 0.25 }] += zs_th; + if ( ch > 0 ) { + m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_one, y + 0.25 }] += ch; + m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_one, y - 0.25 }] += ch; + m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_two, y + 0.25 }] += ch; + m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_two, y - 0.25 }] += ch; + } + } + if ( ( ( channelID.stave() == 1 ) && ( fabs( y ) < 2 ) ) || + ( ( channelID.stave() == 0 ) && ( fabs( y ) == 1.5 ) ) ) { + + m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_thr, y + 0.25 }] += zs_th; + m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_thr, y - 0.25 }] += zs_th; + if ( ch > 0 ) { + m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_thr, y + 0.25 }] += ch; + m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_thr, y - 0.25 }] += ch; + } + } + if ( ( channelID.stave() == 0 ) && ( fabs( y ) < 1 ) ) { + m_2d_ZS_th_chips[channelID.layer()][{ x + positionasic_thr, y }] += zs_th; + if ( ch > 0 ) m_2d_ZS_ch_chips[channelID.layer()][{ x + positionasic_thr, y }] += ch; + } + } + +}; // End of class UTBSMonitor +DECLARE_COMPONENT( UTBSMonitor ) diff --git a/UT/UTMonitors/src/UTDigitsMonitor.cpp b/UT/UTMonitors/src/UTDigitsMonitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..f1bfd7fe03f00ca80cc0e4835a8dbbe5f66f0f73 --- /dev/null +++ b/UT/UTMonitors/src/UTDigitsMonitor.cpp @@ -0,0 +1,668 @@ +/***************************************************************************** \ +* (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 "Event/UTDigit.h" +#include "GaudiAlg/GaudiHistoAlg.h" +#include "Kernel/IUTReadoutTool.h" +#include "Kernel/UTDAQBoard.h" +#include "Kernel/UTDAQDefinitions.h" +#include "Kernel/UTDAQID.h" +#include "LHCbAlgs/Consumer.h" +#include "LHCbAlgs/Transformer.h" +#include "UTDAQ/UTCoordinatesMap.h" +#include "UTDAQ/UTDAQHelper.h" +#include "UTDAQ/UTInfo.h" +#include "UTDet/DeUTDetector.h" +#include <Event/ODIN.h> +#include <Event/RawBank.h> +#include <Event/RawEvent.h> +#include <Gaudi/Accumulators/Histogram.h> +#include <Gaudi/Accumulators/HistogramArray.h> +#include <Kernel/IUTReadoutTool.h> +#include <mutex> +#include <optional> +#include <string> + +/** @class UTDigitsMonitor UTDigitsMonitor.cpp + * -------------------------------------------------- + * Counters for Monet online monitoring of the UT + * The algorithm produces several useful histograms + * @author Wojciech Krupa + * @date 2024/03/20 + * -------------------------------------------------- + **/ + +using namespace LHCb; +namespace GA = Gaudi::Accumulators; + +// Definition of axis +const GA::Axis<double> AxisADC = GA::Axis<double>( 64, -32.5, 31.5, "nADC" ); //-32, 31 +const GA::Axis<double> AxisADC_signed = GA::Axis<double>( 32, -0.5, 31.5, "nADC" ); // 0-31 +const GA::Axis<double> AxisBCID = GA::Axis<double>( 3565, -0.5, 3564.5, "BunchID" ); // +const GA::Axis<double> AxisDIGIT = GA::Axis<double>( 1000, -0.5, 100000.5, "UTDigits (size of digit container)" ); +const GA::Axis<double> AxisXnaturalunits = GA::Axis<double>( 36, -9, 9, "Staves (Sectors)" ); +const GA::Axis<double> AxisXnaturalunits4x = GA::Axis<double>( 144, -9, 9, "Staves (ASICs)" ); +const GA::Axis<double> AxisYnaturalunits = GA::Axis<double>( 28, -7, 7, "Modules" ); + +// Definition of histogram names +const std::array<std::string, 4> s_HistNames = { "UTA_Tell_40_vs_ASIC_Flow0", "UTA_Tell_40_vs_ASIC_Flow1", + "UTC_Tell_40_vs_ASIC_Flow0", "UTC_Tell_40_vs_ASIC_Flow1" }; + +const std::array<std::string, 4> s_HistNames_Counter = { + "UTA_Tell_40_EventsCounter_Flow0", "UTA_Tell_40_EventsCounter_Flow1", "UTC_Tell_40_EventsCounter_Flow0", + "UTC_Tell_40_EventsCounter_Flow1" }; + +const std::array<std::string, 6> s_HistNames_BXType = { "nADC", "nADC_A", "nADC_B", "nADC_C", "nADC_D" }; + +const std::array<std::string, 6> s_HistNames_BXType_labels = { + "Total nADC", "Total nADC - type A sensors", "Total nADC - type B sensors", "Total nADC - type C sensors", + "Total nADC - type D sensors" }; + +const std::array<std::string, 15> s_HistNames_EventType = { "NoBias", + "Lumi", + "Isolated", + "Leading", + "Trailing", + "Empty before isolated", + "Empty after isolated", + "Empty before leading", + "Empty after trailing", + "Empty after 19 clock from trailling", + "NoBias bb", + "Lumi bb", + "Isolated bb", + "Leading bb", + "Trailing bb" }; + +const std::array<std::string, 15> s_HistNames_EventType_labels = { "NoBias", + "Lumi", + "Isolated - ADC", + "Leading - ADC", + "Trailing - ADC", + "Empty before isolated - ADC", + "Empty after isolated - ADC", + "Empty before leading - ADC", + "Empty after trailing - ADC", + "Empty after 19 clock from trailling - ADC", + "NoBias bb - ADC", + "Lumi bb - ADC", + "Isolated bb - ADC", + "Leading bb - ADC", + "Trailing bb - ADC" }; + +// UT layers +const std::array<std::string, 4> UT_layers = { "UTaX", "UTaU", "UTbV", "UTbX" }; + +// 2D plots +const double positionasic[4][3] = { + { -0.4375, -0.3125, -0.1875 }, // Chip 0 + { -0.1875, -0.0625, -0.0625 }, // Chip 1 + { +0.1875, +0.0625, +0.0625 }, // Chip 2 + { +0.4375, +0.3125, +0.1875 } // Chip 3 +}; + +enum class masks { channel = 0x000001ff, lane = 0x00000e00, board = 0x000ff000 }; + +template <masks m> +[[nodiscard]] static constexpr unsigned int extract( unsigned int i ) { + constexpr auto b = std::countr_zero( static_cast<unsigned int>( m ) ); + return ( i & static_cast<unsigned int>( m ) ) >> b; +} + +/** ------------------------------------------------------------------------------------------------------------------------ */ + +class UTDigitsMonitor + : public LHCb::Algorithm::Consumer<void( UTDigits const&, UTDigits const&, DeUTDetector const&, LHCb::ODIN const& ), + LHCb::DetDesc::usesBaseAndConditions<GaudiHistoAlg, DeUTDetector>> { +public: + UTDigitsMonitor( const std::string& name, ISvcLocator* svcloc ) + : Consumer{ name, + svcloc, + { { "InputData", UTDigitLocation::UTDigits }, + { "InputErrorData", UTDigitLocation::UTDigits }, + { "UTLocation", DeUTDetLocation::location() }, + { "ODINLocation", LHCb::ODINLocation::Default } } } {} + ToolHandle<IUTReadoutTool> readoutTool{ this, "ReadoutTool", "UTReadoutTool" }; + + StatusCode initialize() override { + return Consumer::initialize().andThen( [&] { + // Set the top directory to UT + if ( histoTopDir().empty() ) setHistoTopDir( "" ); + } ); + } + + void operator()( const UTDigits& digitsCont, const UTDigits& errdigitsCont, DeUTDetector const& det, + LHCb::ODIN const& odin ) const override { + + // General counters + const unsigned int nDigits = digitsCont.size() + errdigitsCont.size(); + const unsigned int nBuncId = odin.bunchId(); + + if ( msgLevel( MSG::DEBUG ) ) debug() << "---------- Found nDigits: " << nDigits << endmsg; + ++m_nDigits[nDigits]; + ++m_nBuncId[nBuncId]; + ++m_nDigitsnBuncId[{ nBuncId, nDigits }]; + + if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing ) + ++m_nDigits_bb[nDigits]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 ) + ++m_nDigits_be[nDigits]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 ) + ++m_nDigits_eb[nDigits]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam ) + ++m_nDigits_ee[nDigits]; + + // ------------------------------------------------------- + // Loop on digits + // side==1 is Aside (left), side==0 is Cside (right) + // ------------------------------------------------------- + // Hitmaps plots and ADC + + // Commisioning per side histograms + unsigned int ua_size = 0; + unsigned int uc_size = 0; + unsigned int ua_error_size = 0; + unsigned int uc_error_size = 0; + + // For max ADC plots + unsigned int prev_channel = 0; + unsigned int prev_adc = 0; + LHCb::UTDigit* aDigit_max; + + // UT bank + for ( const auto& d : digitsCont ) { + fillHeatMap( d, det ); + fillADC( d, odin ); + + if ( d->face() == 0 ) + uc_size++; + else if ( d->face() == 1 ) + ua_size++; + + UTDAQID daqID = readoutTool->channelIDToDAQID( d->channelID() ); + auto board_ID = daqID.board(); + unsigned int sourceID = UTMap.getSourceIDfromBoardNumber( board_ID ); + + if ( sourceID != prevSourceID ) { fillBankHistograms( sourceID ); } + prevSourceID = sourceID; + + // Cluster max ADC counters + // First event + if ( prev_channel == 0 ) { + prev_channel = d->strip(); + prev_adc = d->depositedCharge(); + aDigit_max = d; + continue; + } + // If cluster size >1 + if ( d->strip() - prev_channel == 1 ) + if ( d->depositedCharge() > prev_adc ) aDigit_max = d; // if ADC max in claster + // End of cluster + if ( d->strip() - prev_channel != 1 ) { + fillADC_clusterMAX( aDigit_max ); + aDigit_max = d; + } + + prev_channel = d->strip(); + prev_adc = d->depositedCharge(); + } + + // UT error bank + for ( const auto& d : errdigitsCont ) { + fillHeatMap( d, det ); + fillADC( d, odin ); + + if ( d->face() == 0 ) + uc_error_size++; + else if ( d->face() == 1 ) + ua_error_size++; + + UTDAQID daqID = readoutTool->channelIDToDAQID( d->channelID() ); + auto board_ID = daqID.board(); + unsigned int sourceID = UTMap.getSourceIDfromBoardNumber( board_ID ); + + if ( sourceID != prevSourceID ) { fillBankHistograms( sourceID ); } + + prevSourceID = sourceID; + + // Cluster max ADC counters + // First event + if ( prev_channel == 0 ) { + prev_channel = d->strip(); + prev_adc = d->depositedCharge(); + aDigit_max = d; + continue; + } + // If cluster size >1 + if ( d->strip() - prev_channel == 1 ) + if ( d->depositedCharge() > prev_adc ) aDigit_max = d; // if ADC max in claster + // End of cluster + if ( d->strip() - prev_channel != 1 ) { + fillADC_clusterMAX( aDigit_max ); + aDigit_max = d; + } + + prev_channel = d->strip(); + prev_adc = d->depositedCharge(); + } + + // Commisioning per side histograms + ++m_nDigits_UA[ua_size + ua_error_size]; + ++m_nDigits_UC[uc_size + uc_error_size]; + } + +private: + mutable UTCoordinatesMap UTMap; + mutable unsigned int prevSourceID = 0; + + // General plots + mutable GA::Histogram<1> m_nBuncId{ this, "nBuncId", "Bunch IDs", AxisBCID }; + mutable GA::Histogram<2> m_nDigitsnBuncId{ this, "nDigitsnBuncId", "nBuncId vs BunchIDs", AxisBCID, AxisDIGIT }; + + // Digits counters + mutable GA::Histogram<1> m_nDigits{ this, "nDigits", "Total nDigits", AxisDIGIT }; + mutable GA::Histogram<1> m_nDigits_UA{ this, "nDigits_UA", "Total nDigits UA", AxisDIGIT }; + mutable GA::Histogram<1> m_nDigits_UC{ this, "nDigits_UC", "Total nDigits UC", AxisDIGIT }; + + // Split for event type + mutable GA::Histogram<1> m_nDigits_bb{ this, "nDigits_bb", "Total nDigits bb", AxisDIGIT }; + mutable GA::Histogram<1> m_nDigits_be{ this, "nDigits_be", "Total nDigits be", AxisDIGIT }; + mutable GA::Histogram<1> m_nDigits_eb{ this, "nDigits_eb", "Total nDigits eb", AxisDIGIT }; + mutable GA::Histogram<1> m_nDigits_ee{ this, "nDigits_ee", "Total nDigits ee", AxisDIGIT }; + + // ADC counters + mutable GA::Histogram<1> m_nADC{ this, "nADC", "Total nADC", AxisADC_signed }; + mutable GA::Histogram<1> m_nADC_A{ this, "nADC_A", "Total nADC - type A sensors", AxisADC_signed }; + mutable GA::Histogram<1> m_nADC_B{ this, "nADC_B", "Total nADC - type B sensors", AxisADC_signed }; + mutable GA::Histogram<1> m_nADC_C{ this, "nADC_C", "Total nADC - type C sensors", AxisADC_signed }; + mutable GA::Histogram<1> m_nADC_D{ this, "nADC_D", "Total nADC - type D sensors", AxisADC_signed }; + + // Cluster max counters + mutable GA::Histogram<1> m_nADC_clusterMAX{ this, "nADC_clusterMAX", "Total nADC", AxisADC_signed }; + mutable GA::Histogram<1> m_nADC_A_clusterMAX{ this, "nADC_A_clusterMAX", "Total nADC - type A sensors", + AxisADC_signed }; + mutable GA::Histogram<1> m_nADC_B_clusterMAX{ this, "nADC_B_clusterMAX", "Total nADC - type B sensors", + AxisADC_signed }; + mutable GA::Histogram<1> m_nADC_C_clusterMAX{ this, "nADC_C_clusterMAX", "Total nADC - type C sensors", + AxisADC_signed }; + mutable GA::Histogram<1> m_nADC_D_clusterMAX{ this, "nADC_D_clusterMAX", "Total nADC - type D sensors", + AxisADC_signed }; + + // Split for A/C side (comissioning) + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> m_1d_ua{ + this, + []( int i ) { return fmt::format( "{}", "UA_" + s_HistNames_BXType[i] ); }, + []( int i ) { return fmt::format( "{}", "UA " + s_HistNames_BXType_labels[i] ); }, + { 32, -0.5, 31.5, "nADC" } }; + + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> m_1d_uc{ + this, + []( int i ) { return fmt::format( "{}", "UC_" + s_HistNames_BXType[i] ); }, + []( int i ) { return fmt::format( "{}", "UC " + s_HistNames_BXType_labels[i] ); }, + { 32, -0.5, 31.5, "nADC" } }; + + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> m_1d_bb{ + this, + []( int i ) { return fmt::format( "{}_bb", s_HistNames_BXType[i] ); }, + []( int i ) { return fmt::format( "{}_bb", s_HistNames_BXType_labels[i] ); }, + { 32, -0.5, 31.5, "nADC" } }; + + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> m_1d_be{ + this, + []( int i ) { return fmt::format( "{}_be", s_HistNames_BXType[i] ); }, + []( int i ) { return fmt::format( "{}_be", s_HistNames_BXType_labels[i] ); }, + { 32, -0.5, 31.5, "nADC" } }; + + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> m_1d_eb{ + this, + []( int i ) { return fmt::format( "{}_eb", s_HistNames_BXType[i] ); }, + []( int i ) { return fmt::format( "{}_eb", s_HistNames_BXType_labels[i] ); }, + { 32, -0.5, 31.5, "nADC" } }; + + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 5> m_1d_ee{ + this, + []( int i ) { return fmt::format( "{}_ee", s_HistNames_BXType[i] ); }, + []( int i ) { return fmt::format( "{}_ee", s_HistNames_BXType_labels[i] ); }, + { 32, -0.5, 31.5, "nADC" } }; + + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, 15> m_1d_type{ + this, + []( int i ) { return fmt::format( "{}", s_HistNames_EventType[i] ); }, + []( int i ) { return fmt::format( "{}", s_HistNames_EventType_labels[i] ); }, + { 32, -0.5, 31.5, "nADC" } }; + + mutable GA::Histogram<2> m_nADCvsBCID{ this, "nADCvsBuncId", "nADC vs BuncId", AxisBCID, AxisADC_signed }; + + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, 4> m_2d_hitmap_sectors{ + this, + []( int i ) { return fmt::format( "UT_HitMap_Sectors_Layer{}", i ); }, + []( int i ) { return fmt::format( "Sectors of Layer{} (ASICs)", i ); }, + { 36, -9, 9, "Staves (ASICs)" }, + { 28, -7, 7, "Modules" } }; + + // UT Hitmaps per asic physical position + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, 4> m_2d_hitmap_chips{ + this, + []( int i ) { return fmt::format( "UT_HitMap_Chips_Layer{}", i ); }, + []( int i ) { return fmt::format( "ASICs of Layer{}", i ); }, + { 144, -9, 9, "Staves (ASICs)" }, + { 28, -7, 7, "Modules" } }; + + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, 4> m_2d_hitmap_tell40_eff{ + this, + []( int i ) { return fmt::format( "UT_HitMap_TELL40_Eff_Layer{}", i ); }, + []( int i ) { return fmt::format( "TELL40 of Layer{} (ASICs)", i ); }, + { 36, -9, 9, "Staves (ASICs)" }, + { 28, -7, 7, "Modules" } }; + + // TELL40 plots + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, s_HistNames.size()> + m_UT_Tell_40_vs_asic{ this, + []( int i ) { return fmt::format( "{}", s_HistNames[i] ); }, + []( int i ) { return fmt::format( "{}", s_HistNames[i] ); }, + { 54, 0, 54 }, + { 24, 0, 24 } }; + + // TELL40 plots - counter + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<1>, s_HistNames_Counter.size()> + m_UT_Tell_40_events{ this, + []( int i ) { return fmt::format( "{}", s_HistNames_Counter[i] ); }, + []( int i ) { return fmt::format( "{}", s_HistNames_Counter[i] ); }, + { 54, 0, 54 } }; + + void fillBankHistograms( const int sourceID ) const { + + int SourceID_ = sourceID / 1000; + + if ( UTMap.getTELL40( sourceID ).find( "UA" ) != std::string::npos ) { + if ( SourceID_ == 10 ) { + ++m_UT_Tell_40_events[0][UTMap.getTELL40Bin( sourceID )]; + } else if ( SourceID_ == 11 ) { + ++m_UT_Tell_40_events[1][UTMap.getTELL40Bin( sourceID )]; + } + + } else if ( UTMap.getTELL40( sourceID ).find( "UC" ) != std::string::npos ) { + if ( SourceID_ == 12 ) { + ++m_UT_Tell_40_events[2][UTMap.getTELL40Bin( sourceID )]; + } else if ( SourceID_ == 13 ) { + ++m_UT_Tell_40_events[3][UTMap.getTELL40Bin( sourceID )]; + } + } + + auto modules = UTMap.getNamesfromTELL40( UTMap.getTELL40( sourceID ) ); + for ( const auto& module : modules ) { + unsigned int channel = UTMap.getChannel( module ); + + Detector::UT::ChannelID channelID( channel ); + + // Let's find where we are + auto tuple = UTMap.getTuple( channelID.module(), channelID.face(), channelID.stave(), channelID.side(), + channelID.sector() ); + + float x = std::get<0>( tuple ); + float y = std::get<1>( tuple ); + std::string module_name = std::get<4>( tuple ); + std::string type = std::get<5>( tuple ); + + // SECTORS Maps // + if ( ( channelID.stave() > 1 ) || ( fabs( y ) > 2 ) ) { + ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x + 0.25, y + 0.25 }]; + ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x + 0.25, y - 0.25 }]; + ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x - 0.25, y + 0.25 }]; + ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x - 0.25, y - 0.25 }]; + } + if ( ( ( channelID.stave() == 1 ) && ( fabs( y ) < 2 ) ) || + ( ( channelID.stave() == 0 ) && ( fabs( y ) == 1.5 ) ) ) { + ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x, y + 0.25 }]; + ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x, y - 0.25 }]; + } + if ( ( channelID.stave() == 0 ) && ( fabs( y ) < 1 ) ) { ++m_2d_hitmap_tell40_eff[channelID.layer()][{ x, y }]; } + } + } + + void fillADC( const LHCb::UTDigit* aDigit, LHCb::ODIN const& odin ) const { + + ++m_nADCvsBCID[{ odin.bunchId(), aDigit->depositedCharge() }]; + std::string type = std::get<5>( + UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() ) ); + + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::NoBias ) ) ++m_1d_type[0][aDigit->depositedCharge()]; + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::Lumi ) ) ++m_1d_type[1][aDigit->depositedCharge()]; + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_08 ) ) ++m_1d_type[2][aDigit->depositedCharge()]; + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_09 ) ) ++m_1d_type[3][aDigit->depositedCharge()]; + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_10 ) ) ++m_1d_type[4][aDigit->depositedCharge()]; + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_11 ) ) ++m_1d_type[5][aDigit->depositedCharge()]; + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_12 ) ) ++m_1d_type[6][aDigit->depositedCharge()]; + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_13 ) ) ++m_1d_type[7][aDigit->depositedCharge()]; + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_14 ) ) ++m_1d_type[8][aDigit->depositedCharge()]; + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_15 ) ) ++m_1d_type[9][aDigit->depositedCharge()]; + + if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing ) { + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::NoBias ) ) ++m_1d_type[10][aDigit->depositedCharge()]; + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::Lumi ) ) ++m_1d_type[11][aDigit->depositedCharge()]; + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_08 ) ) ++m_1d_type[12][aDigit->depositedCharge()]; + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_09 ) ) ++m_1d_type[13][aDigit->depositedCharge()]; + if ( odin.eventTypeBit( LHCb::ODIN::EventTypes::et_bit_10 ) ) ++m_1d_type[14][aDigit->depositedCharge()]; + } + + ++m_nADC[aDigit->depositedCharge()]; + + if ( aDigit->face() == 0 ) { + ++m_1d_uc[0][aDigit->depositedCharge()]; + if ( type == "A" ) ++m_1d_uc[1][aDigit->depositedCharge()]; + if ( type == "B" ) ++m_1d_uc[2][aDigit->depositedCharge()]; + if ( type == "C" ) ++m_1d_uc[3][aDigit->depositedCharge()]; + if ( type == "D" ) ++m_1d_uc[4][aDigit->depositedCharge()]; + } else if ( aDigit->face() == 1 ) { + ++m_1d_ua[0][aDigit->depositedCharge()]; + if ( type == "A" ) ++m_1d_ua[1][aDigit->depositedCharge()]; + if ( type == "B" ) ++m_1d_ua[2][aDigit->depositedCharge()]; + if ( type == "C" ) ++m_1d_ua[3][aDigit->depositedCharge()]; + if ( type == "D" ) ++m_1d_ua[4][aDigit->depositedCharge()]; + } + + if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing ) + ++m_1d_bb[0][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 ) + ++m_1d_be[0][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 ) + ++m_1d_eb[0][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam ) + ++m_1d_ee[0][aDigit->depositedCharge()]; + + if ( type == "A" ) { + ++m_nADC_A[aDigit->depositedCharge()]; + if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing ) + ++m_1d_bb[1][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 ) + ++m_1d_be[1][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 ) + ++m_1d_eb[1][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam ) + ++m_1d_ee[1][aDigit->depositedCharge()]; + } else if ( type == "B" ) { + ++m_nADC_B[aDigit->depositedCharge()]; + if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing ) + ++m_1d_bb[2][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 ) + ++m_1d_be[2][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 ) + ++m_1d_eb[2][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam ) + ++m_1d_ee[2][aDigit->depositedCharge()]; + } else if ( type == "C" ) { + ++m_nADC_C[aDigit->depositedCharge()]; + if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing ) + ++m_1d_bb[3][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 ) + ++m_1d_be[3][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 ) + ++m_1d_eb[3][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam ) + ++m_1d_ee[3][aDigit->depositedCharge()]; + } else if ( type == "D" ) { + ++m_nADC_D[aDigit->depositedCharge()]; + if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing ) + ++m_1d_bb[4][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 ) + ++m_1d_be[4][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 ) + ++m_1d_eb[4][aDigit->depositedCharge()]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam ) + ++m_1d_ee[4][aDigit->depositedCharge()]; + } + + Detector::UT::ChannelID channelID = aDigit->channelID(); + UTDAQID daqID = readoutTool->channelIDToDAQID( channelID ); + auto board_ID = daqID.board(); + unsigned int sourceID = UTMap.getSourceIDfromBoardNumber( board_ID ); + int SourceID_ = sourceID / 1000; + + unsigned int asic_times_lane = int( aDigit->strip() / 128 ) + 4 * extract<masks::lane>( aDigit->getdaqID() ); + + // Sadly we (I) can't do it better. But at least its ok! + if ( UTMap.getTELL40( sourceID ).find( "UA" ) != std::string::npos ) { + if ( SourceID_ == 10 ) { + if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) { + ++m_UT_Tell_40_vs_asic[0][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }]; + } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" ) { + ++m_UT_Tell_40_vs_asic[0][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow0_Bin( asic_times_lane ) }]; + } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" ) { + ++m_UT_Tell_40_vs_asic[0][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }]; + } + } else if ( SourceID_ == 11 ) { + if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) { + if ( asic_times_lane == 16 || asic_times_lane == 17 ) + ++m_UT_Tell_40_vs_asic[0][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }]; + else + ++m_UT_Tell_40_vs_asic[1][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow1_Bin( asic_times_lane ) }]; + } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" ) + ++m_UT_Tell_40_vs_asic[1][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow1_Bin( asic_times_lane ) }]; + else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" ) + ++m_UT_Tell_40_vs_asic[1][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }]; + } + + } else if ( UTMap.getTELL40( sourceID ).find( "UC" ) != std::string::npos ) { + if ( SourceID_ == 12 ) { + if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) { + ++m_UT_Tell_40_vs_asic[2][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }]; + } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" ) { + ++m_UT_Tell_40_vs_asic[2][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow0_Bin( asic_times_lane ) }]; + } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" ) { + ++m_UT_Tell_40_vs_asic[2][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }]; + } + } + + else if ( SourceID_ == 13 ) { + if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) { + if ( asic_times_lane == 16 || asic_times_lane == 17 ) + ++m_UT_Tell_40_vs_asic[2][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }]; + else + ++m_UT_Tell_40_vs_asic[3][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow1_Bin( asic_times_lane ) }]; + } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" ) + ++m_UT_Tell_40_vs_asic[3][{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow1_Bin( asic_times_lane ) }]; + else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" ) + ++m_UT_Tell_40_vs_asic[3][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }]; + } + } + } + + // Fill clusterMAX counters + void fillADC_clusterMAX( const LHCb::UTDigit* aDigit ) const { + + std::string type = std::get<5>( + UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() ) ); + + ++m_nADC_clusterMAX[aDigit->depositedCharge()]; + + if ( type == "A" ) { + ++m_nADC_A_clusterMAX[aDigit->depositedCharge()]; + } else if ( type == "B" ) { + ++m_nADC_B_clusterMAX[aDigit->depositedCharge()]; + } else if ( type == "C" ) { + ++m_nADC_C_clusterMAX[aDigit->depositedCharge()]; + } else if ( type == "D" ) { + ++m_nADC_D_clusterMAX[aDigit->depositedCharge()]; + } + } + + void fillHeatMap( const LHCb::UTDigit* aDigit, DeUTDetector const& det ) const { + + // Let's find where we are + std::tuple<float, float, float, float, std::string, std::string> tuple = + UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() ); + float x = std::get<0>( tuple ); + float y = std::get<1>( tuple ); + std::string module_name = std::get<4>( tuple ); + std::string type = std::get<5>( tuple ); + auto chanID = aDigit->channelID(); + auto aSector = det.findSector( chanID ); + +// This may be useful but allows to get only position of hybrind. It can be used to get ASIC position. +#ifdef USE_DD4HEP + auto trajStrip0 = aSector.createTraj( 0, 0 ); + auto trajStrip511 = aSector.createTraj( 511, 0 ); +#else + auto trajStrip0 = aSector->trajectory( chanID, 0 ); + auto trajStrip511 = aSector->trajectory( LHCb::Detector::UT::ChannelID{ chanID + 511 }, 0 ); +#endif + double stripx0 = -1 * ( trajStrip0.endPoint().x() + trajStrip0.beginPoint().x() ) * 0.5; + double stripx511 = -1 * ( trajStrip511.endPoint().x() + trajStrip511.beginPoint().x() ) * 0.5; + double stripwidth = stripx511 - stripx0; + int chip; + + if ( stripwidth > 0 ) + chip = int( aDigit->strip() / 128 ); + else + chip = 3 - int( aDigit->strip() / 128 ); + + double positionasic_one = positionasic[chip][0]; + double positionasic_two = positionasic[chip][1]; + double positionasic_thr = positionasic[chip][2]; + + // Sectors Maps // + if ( ( aDigit->stave() > 1 ) || ( fabs( y ) > 2 ) ) { + ++m_2d_hitmap_sectors[aDigit->layer()][{ x + 0.25, y + 0.25 }]; + ++m_2d_hitmap_sectors[aDigit->layer()][{ x + 0.25, y - 0.25 }]; + ++m_2d_hitmap_sectors[aDigit->layer()][{ x - 0.25, y + 0.25 }]; + ++m_2d_hitmap_sectors[aDigit->layer()][{ x - 0.25, y - 0.25 }]; + } + if ( ( ( aDigit->stave() == 1 ) && ( fabs( y ) < 2 ) ) || ( ( aDigit->stave() == 0 ) && ( fabs( y ) == 1.5 ) ) ) { + ++m_2d_hitmap_sectors[aDigit->layer()][{ x, y + 0.25 }]; + ++m_2d_hitmap_sectors[aDigit->layer()][{ x, y - 0.25 }]; + } + if ( ( aDigit->stave() == 0 ) && ( fabs( y ) < 1 ) ) { ++m_2d_hitmap_sectors[aDigit->layer()][{ x, y }]; } + + // Chips Maps // + if ( ( aDigit->stave() > 1 ) || ( fabs( y ) > 2 ) ) { + ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_one, y + 0.25 }]; + ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_one, y - 0.25 }]; + ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_two, y + 0.25 }]; + ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_two, y - 0.25 }]; + } + if ( ( ( aDigit->stave() == 1 ) && ( fabs( y ) < 2 ) ) || ( ( aDigit->stave() == 0 ) && ( fabs( y ) == 1.5 ) ) ) { + ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_thr, y + 0.25 }]; + ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_thr, y - 0.25 }]; + } + if ( ( aDigit->stave() == 0 ) && ( fabs( y ) < 1 ) ) { + ++m_2d_hitmap_chips[aDigit->layer()][{ x + positionasic_thr, y }]; + } + } +}; // End of class UTDigitsMonitor +DECLARE_COMPONENT( UTDigitsMonitor ) diff --git a/UT/UTMonitors/src/UTErrorMonitor.cpp b/UT/UTMonitors/src/UTErrorMonitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..cd315fd0c04320c63584bd334f549f86d5353984 --- /dev/null +++ b/UT/UTMonitors/src/UTErrorMonitor.cpp @@ -0,0 +1,351 @@ +/*****************************************************************************\ +* (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 "Event/UTErrorASIC.h" +#include "GaudiAlg/GaudiHistoAlg.h" +#include "Kernel/IUTReadoutTool.h" +#include "LHCbAlgs/Consumer.h" +#include "UTDAQ/UTCoordinatesMap.h" +#include "UTDet/DeUTDetector.h" +#include <Gaudi/Accumulators/Histogram.h> +#include <Gaudi/Accumulators/HistogramArray.h> +#include <Kernel/UTDAQID.h> + +using namespace LHCb; + +/** @class UTErrorMonitor UTErrorMonitor.h + * -------------------------------------------------- + * Class for monitoring UTError and UTSpecial banks + * @author Wojciech Krupa (wokrupa@cern.ch) based on code by: + * @author C. Hadjivasiliou + * @date 2024/03/20 + * -------------------------------------------------- + */ + +enum class masks { channel = 0x000001ff, lane = 0x00000e00, board = 0x000ff000 }; /// Enumeration for use in bit packing + +// Definition of axis +namespace GA = Gaudi::Accumulators; +const GA::Axis<double> AxisADC = GA::Axis<double>( 64, -32.5, 31.5, "nADC" ); //-32, 31 +const GA::Axis<double> AxisCalib = GA::Axis<double>( 256, -0.5, 256.5, "CalibStep" ); +const GA::Axis<double> AxisBCID = GA::Axis<double>( 3565, -0.5, 3564.5, "BunchID" ); +const GA::Axis<double> AxisDIGIT = GA::Axis<double>( 101, -0.5, 100.5, "UTDigits (size of digit container)" ); +const GA::Axis<double> AxisXnaturalunits = GA::Axis<double>( 36, -9, 9, "Staves (Sectors)" ); +const GA::Axis<double> AxisXnaturalunits4x = GA::Axis<double>( 144, -9, 9, "Staves (ASICs)" ); +const GA::Axis<double> AxisYnaturalunits = GA::Axis<double>( 28, -7, 7, "Modules" ); + +// Definition of histogram names +const std::array<std::string, 4> s_HistBanksNames = { "UTA_BankType_vs_TELL40_Flow0", "UTA_BankType_vs_TELL40_Flow1", + "UTC_BankType_vs_TELL40_Flow0", "UTC_BankType_vs_TELL40_Flow1" }; + +const std::array<std::string, 4> s_HistBanksNames2 = { + "UTA_BankType_vs_TELL40_Error_Flow0", "UTA_BankType_vs_TELL40_Error_Flow1", "UTC_BankType_vs_TELL40_Error_Flow0", + "UTC_BankType_vs_TELL40_Error_Flow1" }; + +// UTError Types +const std::array<std::string, 12> s_SpecialSubtypeMap = { + "UTA_BusyEvent_vs_TELL40_Flow0", "UTA_BusyEvent_vs_TELL40_Flow1", "UTC_BusyEvent_vs_TELL40_Flow0", + "UTC_BusyEvent_vs_TELL40_Flow1", "UTA_BufferFull_vs_TELL40_Flow0", "UTA_BufferFull_vs_TELL40_Flow1", + "UTC_BufferFull_vs_TELL40_Flow0", "UTC_BufferFull_vs_TELL40_Flow1", "UTA_BufferFullN_vs_TELL40_Flow0", + "UTA_BufferFullN_vs_TELL40_Flow1", "UTC_BufferFullN_vs_TELL40_Flow0", "UTC_BufferFullN_vs_TELL40_Flow1" }; + +// 2D plots +const double positionasic[4][3] = { + { -0.4375, -0.3125, -0.1875 }, // Chip 0 + { -0.1875, -0.0625, -0.0625 }, // Chip 1 + { +0.1875, +0.0625, +0.0625 }, // Chip 2 + { +0.4375, +0.3125, +0.1875 } // Chip 3 +}; +using namespace LHCb; + +template <masks m> +[[nodiscard]] static constexpr unsigned int extract( unsigned int i ) { + constexpr auto b = std::countr_zero( static_cast<unsigned int>( m ) ); + return ( i & static_cast<unsigned int>( m ) ) >> b; +} + +class UTErrorMonitor + : public LHCb::Algorithm::Consumer<void( UTErrorASICs const&, DeUTDetector const& ), + LHCb::DetDesc::usesBaseAndConditions<GaudiHistoAlg, DeUTDetector>> { + +public: + /// constructer + UTErrorMonitor( const std::string& name, ISvcLocator* svcloc ) + : Consumer{ + name, + svcloc, + { { "InputData", UTErrorASICLocation::UTErrorASICs }, { "UTLocation", DeUTDetLocation::location() } } } {} + + ToolHandle<IUTReadoutTool> readoutTool{ this, "ReadoutTool", "UTReadoutTool" }; + + // UTCoordinations map + mutable UTCoordinatesMap UTMap; + + // TELL40 vs banktype plots + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, s_HistBanksNames.size()> + m_UT_Tell_40_vs_banktype{ this, + []( int i ) { return fmt::format( "{}", s_HistBanksNames[i] ); }, + []( int i ) { return fmt::format( "{}", s_HistBanksNames[i] ); }, + { 54, 0, 54 }, + { 19, 0, 19 } }; + + // TELL40 vs banktype plots + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, s_HistBanksNames2.size()> + m_UT_Tell_40_vs_banktype_error{ this, + []( int i ) { return fmt::format( "{}", s_HistBanksNames2[i] ); }, + []( int i ) { return fmt::format( "{}", s_HistBanksNames2[i] ); }, + { 54, 0, 54 }, + { 3, 0, 3 } }; + + // TELL40 vs packetID (UTError) plots + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, s_SpecialSubtypeMap.size()> + m_2d_packetype{ this, + []( int i ) { return fmt::format( "{}", s_SpecialSubtypeMap[i] ); }, + []( int i ) { return fmt::format( "{}", s_SpecialSubtypeMap[i] ); }, + { 54, 0, 54 }, + { 24, 0, 24 } }; + + // 2D plot for geometrical pos of UTErrorKnown + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, 4> m_asics_UTError_Known{ + this, + []( int i ) { return fmt::format( "UTError_Known_Layer{}", i ); }, + []( int i ) { return fmt::format( "UTError_Known_Layer{}", i ); }, + { 144, -9, 9, "Staves (ASICs)" }, + { 28, -7, 7, "Modules" } }; + + // Error type counters + mutable GA::Histogram<1> m_nErrorsTypeCounter{ this, "ErrorsTypeCounter", "ErrorsTypeCounter", { 19, 0, 19 } }; + // Number of ASICs in error per event - subtype 19 + mutable GA::Histogram<1> m_ASICs_nErrorsType19{ + this, "Number_ASICs_Error_19", "Number of ASICs in Error - subtype 19", { 1001, -0.5, 1000.5 } }; + // Number of ASICs in error per event - subtype 20 + mutable GA::Histogram<1> m_ASICs_nErrorsType20{ + this, "Number_ASICs_Error_20", "Number of ASICs in Error - subtype 20", { 1001, -0.5, 1000.5 } }; + // Number of ASICs in error per event - subtype 21 + mutable GA::Histogram<1> m_ASICs_nErrorsType21{ + this, "Number_ASICs_Error_21", "Number of ASICs in Error - subtype 21", { 1001, -0.5, 1000.5 } }; + + /// initialize + StatusCode initialize() override { + return Consumer::initialize().andThen( [&] { + // Set the top directory to UT + if ( histoTopDir().empty() ) setHistoTopDir( "" ); + } ); + } + + /// execute + void operator()( const UTErrorASICs& errorsCont, DeUTDetector const& det ) const override { + // number of errors + unsigned int asic_inError_19 = 0; + unsigned int asic_inError_20 = 0; + unsigned int asic_inError_21 = 0; + + plot( (double)errorsCont.size(), "nErrors", -0.5, 9999.5, 10000 ); + // histos per bank type + // histos per error + for ( const auto& e : errorsCont ) { + + // Handling UTError enocded in strip 1 + if ( e->strip() % 128 == 1 && e->packetId() == 19 ) asic_inError_19++; + if ( e->strip() % 128 == 1 && e->packetId() == 20 ) asic_inError_20++; + if ( e->strip() % 128 == 1 && e->packetId() == 21 ) asic_inError_21++; + + fillBankHistograms( e ); + fillHeatMap_Error( e, det ); + } + + // Bank type counter + if ( asic_inError_19 > 1000 ) asic_inError_19 = 1000; // saturation + if ( asic_inError_20 > 1000 ) asic_inError_20 = 1000; // saturation + if ( asic_inError_21 > 1000 ) asic_inError_21 = 1000; // saturation + + ++m_ASICs_nErrorsType19[asic_inError_19]; + ++m_ASICs_nErrorsType20[asic_inError_20]; + ++m_ASICs_nErrorsType21[asic_inError_21]; + } + +private: + void fillBankHistograms( const LHCb::UTErrorASIC* aError ) const { + // Channel and source ID + auto chanID = aError->channelID(); + UTDAQID daqID = readoutTool->channelIDToDAQID( chanID ); + auto board_ID = daqID.board(); + unsigned int sourceID = UTMap.getSourceIDfromBoardNumber( board_ID ); + int y = aError->bankType(); + auto LaneID = extract<masks::lane>( aError->getdaqID() ); + int SourceID_ = sourceID / 1000; + unsigned int asic_times_lane = aError->asic() + 4 * LaneID; + + // Bank type counter + ++m_nErrorsTypeCounter[y]; + + // Sadly we (I) can't do it better. But at least its ok! + // Normal ErrorType + if ( ( aError->strip() == 0 && LaneID == 0 ) || + ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" && aError->strip() == 256 && LaneID == 0 ) ) { + // TELL40 vs Bank Type + if ( UTMap.getTELL40( sourceID ).find( "UA" ) != std::string::npos ) { + if ( sourceID / 1000 == 10 ) // 10346-10269 + ++m_UT_Tell_40_vs_banktype[0][{ UTMap.getTELL40Bin( sourceID ), y }]; + else if ( sourceID / 1000 == 11 ) // 11370-11293 + ++m_UT_Tell_40_vs_banktype[1][{ UTMap.getTELL40Bin( sourceID ), y }]; + } else if ( UTMap.getTELL40( sourceID ).find( "UC" ) != std::string::npos ) { + if ( sourceID / 1000 == 12 ) // 12366-12291 + ++m_UT_Tell_40_vs_banktype[2][{ UTMap.getTELL40Bin( sourceID ), y }]; + else if ( sourceID / 1000 == 13 ) // 13371-13315 + ++m_UT_Tell_40_vs_banktype[3][{ UTMap.getTELL40Bin( sourceID ), y }]; + } + } + + if ( aError->strip() % 128 == 1 && + ( aError->packetId() == 19 || aError->packetId() == 20 || aError->packetId() == 21 ) ) { + + // TELL40 vs Bank Type + if ( UTMap.getTELL40( sourceID ).find( "UA" ) != std::string::npos ) { + if ( sourceID / 1000 == 10 ) // 10346-10269 + ++m_UT_Tell_40_vs_banktype_error[0][{ UTMap.getTELL40Bin( sourceID ), aError->packetId() - 19 }]; + else if ( sourceID / 1000 == 11 ) // 11370-11293 + ++m_UT_Tell_40_vs_banktype_error[1][{ UTMap.getTELL40Bin( sourceID ), aError->packetId() - 19 }]; + } else if ( UTMap.getTELL40( sourceID ).find( "UC" ) != std::string::npos ) { + if ( sourceID / 1000 == 12 ) // 12366-12291 + ++m_UT_Tell_40_vs_banktype_error[2][{ UTMap.getTELL40Bin( sourceID ), aError->packetId() - 19 }]; + else if ( sourceID / 1000 == 13 ) // 13371-13315 + ++m_UT_Tell_40_vs_banktype_error[3][{ UTMap.getTELL40Bin( sourceID ), aError->packetId() - 19 }]; + } + + // Suberror plots + if ( UTMap.getTELL40( sourceID ).find( "UA" ) != std::string::npos ) { + if ( SourceID_ == 10 ) { + if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) { + ++m_2d_packetype[( aError->packetId() - 19 ) * 4] + [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }]; + } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" ) { + ++m_2d_packetype[( aError->packetId() - 19 ) * 4] + [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow0_Bin( asic_times_lane ) }]; + } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" ) { + ++m_2d_packetype[( aError->packetId() - 19 ) * 4][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }]; + } + } else if ( SourceID_ == 11 ) { + if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) { + if ( asic_times_lane == 16 || asic_times_lane == 17 ) + ++m_2d_packetype[( aError->packetId() - 19 ) * 4] + [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }]; + else + ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 1] + [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow1_Bin( asic_times_lane ) }]; + } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" ) + ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 1] + [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow1_Bin( asic_times_lane ) }]; + else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" ) + ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 1][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }]; + } + + } else if ( UTMap.getTELL40( sourceID ).find( "UC" ) != std::string::npos ) { + if ( SourceID_ == 12 ) { + if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) { + ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 2] + [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }]; + } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" ) { + ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + +2] + [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow0_Bin( asic_times_lane ) }]; + } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" ) { + ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 2][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }]; + } + } + + else if ( SourceID_ == 13 ) { + if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x4" ) { + if ( asic_times_lane == 16 || asic_times_lane == 17 ) + ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 2] + [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow0_Bin( asic_times_lane ) }]; + else + ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 3] + [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_4_Flow1_Bin( asic_times_lane ) }]; + } else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x5" ) + ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 3] + [{ UTMap.getTELL40Bin( sourceID ), UTMap.get2_5_Flow1_Bin( asic_times_lane ) }]; + else if ( UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "4x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "2x3" || + UTMap.getFlavour( UTMap.getTELL40( sourceID ) ) == "Jx3" ) + ++m_2d_packetype[( aError->packetId() - 19 ) * 4 + 3][{ UTMap.getTELL40Bin( sourceID ), asic_times_lane }]; + } + } + } + } + + void incrementHistogram( GA::Histogram<2>& histogram, double x, double y, double positionasic_one, + double positionasic_two ) const { + ++histogram[{ x + positionasic_one, y + 0.25 }]; + ++histogram[{ x + positionasic_two, y + 0.25 }]; + ++histogram[{ x + positionasic_one, y - 0.25 }]; + ++histogram[{ x + positionasic_two, y - 0.25 }]; + } + + void fillHeatMap_Error( const LHCb::UTErrorASIC* aError, DeUTDetector const& det ) const { + + // Let's find out where we are in the UT, it should be reoptimised in the future + // To be checked if it works properly! + + if ( aError->strip() % 128 == 1 ) { + std::tuple<float, float, float, float, std::string, std::string> tuple = + UTMap.getTuple( aError->module(), aError->face(), aError->stave(), aError->side(), aError->sector() ); + + float x = std::get<0>( tuple ); + float y = std::get<1>( tuple ); + std::string module_name = std::get<4>( tuple ); + std::string type = std::get<5>( tuple ); + auto chanID = aError->channelID(); + auto aSector = det.findSector( chanID ); +#ifdef USE_DD4HEP + auto trajStrip0 = aSector.createTraj( 0, 0 ); + auto trajStrip511 = aSector.createTraj( 511, 0 ); +#else + auto trajStrip0 = aSector->trajectory( chanID, 0 ); + auto trajStrip511 = aSector->trajectory( LHCb::Detector::UT::ChannelID{ chanID + 511 }, 0 ); +#endif + double stripx0 = -1 * ( trajStrip0.endPoint().x() + trajStrip0.beginPoint().x() ) * 0.5; + double stripx511 = -1 * ( trajStrip511.endPoint().x() + trajStrip511.beginPoint().x() ) * 0.5; + double stripwidth = stripx511 - stripx0; + int chip; + + if ( stripwidth > 0 ) + chip = int( aError->strip() / 128 ); + else + chip = 3 - int( aError->strip() / 128 ); + + double positionasic_one = positionasic[chip][0]; + double positionasic_two = positionasic[chip][1]; + double positionasic_thr = positionasic[chip][2]; + + // Chip is in its correct geometrical order + // Sector map // + if ( ( aError->stave() > 1 ) || ( fabs( y ) > 2 ) ) { + assert( aError->layer() >= 0 && aError->layer() <= 3 ); + incrementHistogram( m_asics_UTError_Known[aError->layer()], x, y, positionasic_one, positionasic_two ); + } + + if ( ( ( aError->stave() == 1 ) && ( fabs( y ) < 2 ) ) || ( ( aError->stave() == 0 ) && ( fabs( y ) == 1.5 ) ) ) { + ++m_asics_UTError_Known[aError->layer() == 0][{ x + positionasic_thr, y + 0.25 }]; + ++m_asics_UTError_Known[aError->layer() == 0][{ x + positionasic_thr, y - 0.25 }]; + } + if ( ( aError->stave() == 0 ) && ( fabs( y ) < 1 ) ) { + ++m_asics_UTError_Known[aError->layer()][{ x + positionasic_thr, y }]; + } + } + } +}; + +DECLARE_COMPONENT( UTErrorMonitor ) diff --git a/UT/UTMonitors/src/UTNZSMonitor.cpp b/UT/UTMonitors/src/UTNZSMonitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..c24ffae4793c9f51f2b80ceb509c3e9bd3625025 --- /dev/null +++ b/UT/UTMonitors/src/UTNZSMonitor.cpp @@ -0,0 +1,372 @@ +/***************************************************************************** \ +* (c) Copyright 2000-2018 CERN for the benefit of the LHCb Collaboration * +* * +* This software is distributed under the teRMS_ADC 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 "Event/UTDigit.h" +#include "GaudiAlg/GaudiHistoAlg.h" +#include "Kernel/IUTReadoutTool.h" +#include "Kernel/UTDAQBoard.h" +#include "Kernel/UTDAQDefinitions.h" +#include "Kernel/UTDAQID.h" +#include "LHCbAlgs/Consumer.h" +#include "LHCbAlgs/Transformer.h" +#include "UTDAQ/UTCoordinatesMap.h" +#include "UTDAQ/UTDAQHelper.h" +#include "UTDAQ/UTInfo.h" +#include "UTDet/DeUTDetector.h" +#include <Event/ODIN.h> +#include <Event/RawBank.h> +#include <Event/RawEvent.h> +#include <Gaudi/Accumulators/Histogram.h> +#include <Gaudi/Accumulators/HistogramArray.h> +#include <Kernel/IUTReadoutTool.h> +#include <algorithm> +#include <mutex> +#include <optional> +#include <string> + +/** @class UTNZSMonitor UTNZSMonitor.cpp + * -------------------------------------------------- + * Class for monitoring UTNZS banks + * @author Wojciech Krupa (wokrupa@cern.ch): + * @date 2024/02/10 + * -------------------------------------------------- + */ +using namespace LHCb; +namespace GA = Gaudi::Accumulators; + +// Definition of axis +const GA::Axis<double> AxisADC = GA::Axis<double>( 64, -32.5, 31.5, "nADC" ); //-32, 31 +const GA::Axis<double> AxisCalib = GA::Axis<double>( 257, -0.5, 256.5, "CalibStep" ); +const GA::Axis<double> AxisBCID = GA::Axis<double>( 3565, -0.5, 3564.5, "BunchID" ); +const GA::Axis<double> AxisDIGIT = GA::Axis<double>( 101, -0.5, 100.5, "UTDigits (size of digit container)" ); +const GA::Axis<double> AxisXnaturalunits = GA::Axis<double>( 36, -9, 9, "Staves (Sectors)" ); +const GA::Axis<double> AxisXnaturalunits4x = GA::Axis<double>( 144, -9, 9, "Staves (ASICs)" ); +const GA::Axis<double> AxisYnaturalunits = GA::Axis<double>( 28, -7, 7, "Modules" ); +const GA::Axis<double> AxisModule = GA::Axis<double>( 512, 0, 512, "Strips" ); + +enum class masks { channel = 0x000001ff, lane = 0x00000e00, board = 0x000ff000 }; + +const std::array<std::string, 4> s_HistNames = { "UTA_Tell_40_vs_ASIC_Flow0", "UTA_Tell_40_vs_ASIC_Flow1", + "UTC_Tell_40_vs_ASIC_Flow0", "UTC_Tell_40_vs_ASIC_Flow1" }; + +const std::array<std::string, 4> s_HistBanksNames = { "UTA_BankType_vs_TELL40_Flow0", "UTA_BankType_vs_TELL40_Flow1", + "UTC_BankType_vs_TELL40_Flow0", "UTC_BankType_vs_TELL40_Flow1" }; + +// UT layers +const std::array<std::string, 4> UT_layers = { "UTaX", "UTaU", "UTbV", "UTbX" }; + +// 2D plots +const double positionasic[4][3] = { + { -0.4375, -0.3125, -0.1875 }, // Chip 0 + { -0.1875, -0.0625, -0.0625 }, // Chip 1 + { +0.1875, +0.0625, +0.0625 }, // Chip 2 + { +0.4375, +0.3125, +0.1875 } // Chip 3 +}; + +template <masks m> +[[nodiscard]] static constexpr unsigned int extract( unsigned int i ) { + constexpr auto b = std::countr_zero( static_cast<unsigned int>( m ) ); + return ( i & static_cast<unsigned int>( m ) ) >> b; +} + +// Tools for booking histograms +namespace Utility { + template <typename T, typename OWNER> + void map_emplace( T& t, typename T::key_type key, OWNER* owner, std::string const& title, + Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis1 ) { + t.emplace( std::piecewise_construct, std::forward_as_tuple( key ), + std::forward_as_tuple( owner, key, title, axis1 ) ); + } + template <typename T, typename OWNER> + void map_emplace( T& t, typename T::key_type key, OWNER* owner, std::string const& title, + Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis1, + Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis2 ) { + t.emplace( std::piecewise_construct, std::forward_as_tuple( key ), + std::forward_as_tuple( owner, key, title, axis1, axis2 ) ); + } + +} // namespace Utility + +/** ------------------------------------------------------------------------------------------------------------------------ */ +class UTNZSMonitor + : public LHCb::Algorithm::Consumer<void( UTDigits const&, UTDigits const&, DeUTDetector const&, LHCb::ODIN const& ), + LHCb::DetDesc::usesBaseAndConditions<GaudiHistoAlg, DeUTDetector>> { +public: + UTNZSMonitor( const std::string& name, ISvcLocator* svcloc ) + : Consumer{ name, + svcloc, + { { "InputData", UTDigitLocation::UTDigits }, + { "InputErrData", UTDigitLocation::UTDigits }, + { "UTLocation", DeUTDetLocation::location() }, + { "ODINLocation", LHCb::ODINLocation::Default } } } {} + ToolHandle<IUTReadoutTool> readoutTool{ this, "ReadoutTool", "UTReadoutTool" }; + + StatusCode initialize() override; + void operator()( const UTDigits&, const UTDigits&, DeUTDetector const&, LHCb::ODIN const& ) const override; + void fillADC( const LHCb::UTDigit*, LHCb::ODIN const& ) const; + void fillFlow( const LHCb::UTDigit* ) const; + void incrementHistogram( GA::ProfileHistogram<2>&, double, double, double, double, float ) const; + void fillHeatMap_naturalunits( const LHCb::UTDigit*, DeUTDetector const& ) const; + +private: + mutable UTCoordinatesMap UTMap; + + // TELL40 plots + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::Histogram<2>, s_HistNames.size()> + m_UT_Tell_40_vs_asic{ this, + []( int i ) { return fmt::format( "{}", s_HistNames[i] ); }, + []( int i ) { return fmt::format( "{}", s_HistNames[i] ); }, + { 54, 0, 54 }, + { 24, 0, 24 } }; + + // The container for Mean_ADC per channel + mutable std::map<std::string, GA::ProfileHistogram<1>> m_2d_Mean_ADCCounter; + mutable std::map<std::string, GA::ProfileHistogram<1>> m_2d_RMS_ADCCounter; + mutable std::map<std::string, GA::Histogram<2>> m_2d_ADCCounter; + + // 2D plots with MCMS value per chip + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::ProfileHistogram<2>, 4> m_UT_mcm_strip{ + this, + []( int i ) { return fmt::format( "MCM_strip_Layer{}", i ); }, + []( int i ) { return fmt::format( "MCM_strip_Layer{} (ASICs)", i ); }, + { 144, -9, 9, "Staves (ASICs)" }, + { 28, -7, 7, "Modules" } }; + + // 2D plots with MCMS strip per chip + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::ProfileHistogram<2>, 4> m_UT_mcm_mu{ + this, + []( int i ) { return fmt::format( "MCM_mu_Layer{}", i ); }, + []( int i ) { return fmt::format( "MCM_mu_Layer{} (ASICs)", i ); }, + { 144, -9, 9, "Staves (ASICs)" }, + { 28, -7, 7, "Modules" } }; + + // 2D plots with MCMS strip per chip + mutable Gaudi::Accumulators::HistogramArray<Gaudi::Accumulators::ProfileHistogram<2>, 4> m_UT_MCM_rms_ADC{ + this, + []( int i ) { return fmt::format( "MCM_rms_Layer{}", i ); }, + []( int i ) { return fmt::format( "MCM_rms_Layer{} (ASICs)", i ); }, + { 144, -9, 9, "Staves (ASICs)" }, + { 28, -7, 7, "Modules" } }; + + // 1D plots: ADC + mutable GA::Histogram<2> m_nADCvsBCID{ this, "ADCvsBuncId", "ADC vs BuncId", AxisBCID, AxisADC }; + mutable GA::Histogram<2> m_nADCvsCalibStep{ this, "ADCvsCalibStep", "ADC vs CalibStep", AxisCalib, AxisADC }; + + mutable std::map<std::string, Gaudi::Accumulators::SigmaCounter<>> RawADC; + mutable std::map<std::string, Gaudi::Accumulators::SigmaCounter<>> mcm; + +}; // End of class UTNZSMonitor +DECLARE_COMPONENT( UTNZSMonitor ) + +StatusCode UTNZSMonitor::initialize() { + return Consumer::initialize().andThen( [&] { + // Set the top directory to UT + if ( histoTopDir().empty() ) setHistoTopDir( "" ); + + for ( const auto& module : UTMap.getModulesNames() ) { + Utility::map_emplace( m_2d_Mean_ADCCounter, "Mean_ADC_" + module, this, "Mean_ADC_" + module, { 512, 0, 512 } ); + Utility::map_emplace( m_2d_RMS_ADCCounter, "RMS_ADC_" + module, this, "RMS_ADC_" + module, { 512, 0, 512 } ); + Utility::map_emplace( m_2d_ADCCounter, "ADC_" + module, this, "ADC_" + module, { 512, 0, 512 }, + { 64, -32.5, 31.5 } ); + } + } ); +} + +void UTNZSMonitor::operator()( const UTDigits& digitsCont, const UTDigits& errdigitsCont, DeUTDetector const& det, + LHCb::ODIN const& odin ) const { + + // General counters + // ------------------------------------------------------- + // Loop on digits + // side==1 is Aside (left), side==0 is Cside (right) + // ------------------------------------------------------- + for ( const UTDigit* d : digitsCont ) { + fillFlow( d ); + fillADC( d, odin ); + fillHeatMap_naturalunits( d, det ); + } + + for ( const UTDigit* d : errdigitsCont ) { + fillFlow( d ); + fillADC( d, odin ); + fillHeatMap_naturalunits( d, det ); + } + // ------------------------------------------------------- + // end loop on digits + // ------------------------------------------------------- +} // end execute + +void UTNZSMonitor::fillADC( const LHCb::UTDigit* aDigit, LHCb::ODIN const& odin ) const { + + // Summary plots + ++m_nADCvsBCID[{ odin.bunchId(), aDigit->depositedCharge() }]; + ++m_nADCvsCalibStep[{ odin.calibrationStep(), aDigit->depositedCharge() }]; + + // ADC counter + auto tuple = UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() ); + std::string module_name = std::get<4>( tuple ); + + RawADC[UT_layers[aDigit->layer()] + "_" + module_name + "_" + std::to_string( aDigit->strip() )] += + aDigit->depositedCharge(); + + m_2d_Mean_ADCCounter.at( "Mean_ADC_" + UT_layers[aDigit->layer()] + "_" + module_name )[aDigit->strip()] += + RawADC[UT_layers[aDigit->layer()] + "_" + module_name + "_" + std::to_string( aDigit->strip() )].mean(); + m_2d_RMS_ADCCounter.at( "RMS_ADC_" + UT_layers[aDigit->layer()] + "_" + module_name )[aDigit->strip()] += + RawADC[UT_layers[aDigit->layer()] + "_" + module_name + "_" + std::to_string( aDigit->strip() )] + .standard_deviation(); + ++m_2d_ADCCounter.at( "ADC_" + UT_layers[aDigit->layer()] + "_" + + module_name )[{ aDigit->strip(), aDigit->depositedCharge() }]; +} + +void UTNZSMonitor::fillFlow( const LHCb::UTDigit* aDigit ) const { + Detector::UT::ChannelID channelID = aDigit->channelID(); + UTDAQID daqID = readoutTool->channelIDToDAQID( channelID ); + unsigned int board_ID = daqID.board(); + unsigned int sourceID = UTMap.getSourceIDfromBoardNumber( board_ID ); + int SourceID_ = sourceID / 1000; + + unsigned int asic_times_lane = aDigit->asic() % 4 + 4 * extract<masks::lane>( aDigit->getdaqID() ); + + // Get TELL40 and Flavour only once + std::string tell40 = UTMap.getTELL40( sourceID ); + std::string flavour = UTMap.getFlavour( tell40 ); + int tell40Bin = UTMap.getTELL40Bin( sourceID ); + + // Check if the strip is aligned for filling + if ( aDigit->strip() % 128 == 0 ) { + if ( tell40.find( "UA" ) != std::string::npos ) { + // Handle UA sourceID_ == 10 and 11 + if ( SourceID_ == 10 ) { + if ( flavour == "2x4" ) { + ++m_UT_Tell_40_vs_asic[0][{ tell40Bin, UTMap.get2_4_Flow0_Bin( asic_times_lane ) }]; + } else if ( flavour == "2x5" ) { + ++m_UT_Tell_40_vs_asic[0][{ tell40Bin, UTMap.get2_5_Flow0_Bin( asic_times_lane ) }]; + } else if ( flavour == "4x3" || flavour == "2x3" || flavour == "Jx3" ) { + ++m_UT_Tell_40_vs_asic[0][{ tell40Bin, asic_times_lane }]; + } + } else if ( SourceID_ == 11 ) { + if ( flavour == "2x4" ) { + if ( asic_times_lane == 16 || asic_times_lane == 17 ) { + ++m_UT_Tell_40_vs_asic[0][{ tell40Bin, UTMap.get2_4_Flow0_Bin( asic_times_lane ) }]; + } else { + ++m_UT_Tell_40_vs_asic[1][{ tell40Bin, UTMap.get2_4_Flow1_Bin( asic_times_lane ) }]; + } + } else if ( flavour == "2x5" ) { + ++m_UT_Tell_40_vs_asic[1][{ tell40Bin, UTMap.get2_5_Flow1_Bin( asic_times_lane ) }]; + } else if ( flavour == "4x3" || flavour == "2x3" || flavour == "Jx3" ) { + ++m_UT_Tell_40_vs_asic[1][{ tell40Bin, asic_times_lane }]; + } + } + } else if ( tell40.find( "UC" ) != std::string::npos ) { + // Handle UC sourceID_ == 12 and 13 + if ( SourceID_ == 12 ) { + if ( flavour == "2x4" ) { + ++m_UT_Tell_40_vs_asic[2][{ tell40Bin, UTMap.get2_4_Flow0_Bin( asic_times_lane ) }]; + } else if ( flavour == "2x5" ) { + ++m_UT_Tell_40_vs_asic[2][{ tell40Bin, UTMap.get2_5_Flow0_Bin( asic_times_lane ) }]; + } else if ( flavour == "4x3" || flavour == "2x3" || flavour == "Jx3" ) { + ++m_UT_Tell_40_vs_asic[2][{ tell40Bin, asic_times_lane }]; + } + } else if ( SourceID_ == 13 ) { + if ( flavour == "2x4" ) { + if ( asic_times_lane == 16 || asic_times_lane == 17 ) { + ++m_UT_Tell_40_vs_asic[2][{ tell40Bin, UTMap.get2_4_Flow0_Bin( asic_times_lane ) }]; + } else { + ++m_UT_Tell_40_vs_asic[3][{ tell40Bin, UTMap.get2_4_Flow1_Bin( asic_times_lane ) }]; + } + } else if ( flavour == "2x5" ) { + ++m_UT_Tell_40_vs_asic[3][{ tell40Bin, UTMap.get2_5_Flow1_Bin( asic_times_lane ) }]; + } else if ( flavour == "4x3" || flavour == "2x3" || flavour == "Jx3" ) { + ++m_UT_Tell_40_vs_asic[3][{ tell40Bin, asic_times_lane }]; + } + } + } + } +} + +void UTNZSMonitor::incrementHistogram( GA::ProfileHistogram<2>& histogram, double x, double y, double positionasic_one, + double positionasic_two, float val ) const { + + histogram[{ x + positionasic_one, y + 0.25 }] += val; + histogram[{ x + positionasic_two, y + 0.25 }] += val; + histogram[{ x + positionasic_one, y - 0.25 }] += val; + histogram[{ x + positionasic_two, y - 0.25 }] += val; +} + +void UTNZSMonitor::fillHeatMap_naturalunits( const LHCb::UTDigit* aDigit, DeUTDetector const& det ) const { + + if ( aDigit->strip() % 128 == 0 ) { + + std::tuple<float, float, float, float, std::string, std::string> tuple = + UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() ); + + // Let's find out where we are + float x = std::get<0>( tuple ); + float y = std::get<1>( tuple ); + std::string module_name = std::get<4>( tuple ); + std::string type = std::get<5>( tuple ); + auto chanID = aDigit->channelID(); + auto aSector = det.findSector( chanID ); +#ifdef USE_DD4HEP + auto trajStrip0 = aSector.createTraj( 0, 0 ); + auto trajStrip511 = aSector.createTraj( 511, 0 ); +#else + auto trajStrip0 = aSector->trajectory( chanID, 0 ); + auto trajStrip511 = aSector->trajectory( LHCb::Detector::UT::ChannelID{ chanID + 511 }, 0 ); +#endif + double stripx0 = -1 * ( trajStrip0.endPoint().x() + trajStrip0.beginPoint().x() ) * 0.5; + double stripx511 = -1 * ( trajStrip511.endPoint().x() + trajStrip511.beginPoint().x() ) * 0.5; + double stripwidth = stripx511 - stripx0; + int chip; + + if ( stripwidth > 0 ) + chip = int( aDigit->strip() / 128 ); + else + chip = 3 - int( aDigit->strip() / 128 ); + + double positionasic_one = positionasic[chip][0]; + double positionasic_two = positionasic[chip][1]; + double positionasic_thr = positionasic[chip][2]; + + float mcm_val = aDigit->mcmVal() + 0.0001; // protection against 0 value in bin which caused problem in displaying + // histogram sometimes even bigger statistic the average is 0. Monet + // interpret it as empty bin what is not true. won't process more + // than O(4) NMZS events per run so it wont be a problem + + mcm[UT_layers[aDigit->layer()] + "_" + module_name + "_" + std::to_string( chip )] += mcm_val; + float mcm_mu = mcm[UT_layers[aDigit->layer()] + "_" + module_name + "_" + std::to_string( chip )].mean() + 0.0001; + float MCM_rms_ADC = + mcm[UT_layers[aDigit->layer()] + "_" + module_name + "_" + std::to_string( chip )].standard_deviation() + 0.001; + + // Sector map // + if ( ( aDigit->stave() > 1 ) || ( fabs( y ) > 2 ) ) { + incrementHistogram( m_UT_mcm_strip[aDigit->layer()], x, y, positionasic_one, positionasic_two, + aDigit->mcmStrip() ); + incrementHistogram( m_UT_mcm_mu[aDigit->layer()], x, y, positionasic_one, positionasic_two, mcm_mu ); + incrementHistogram( m_UT_MCM_rms_ADC[aDigit->layer()], x, y, positionasic_one, positionasic_two, MCM_rms_ADC ); + } + + if ( ( ( aDigit->stave() == 1 ) && ( fabs( y ) < 2 ) ) || ( ( aDigit->stave() == 0 ) && ( fabs( y ) == 1.5 ) ) ) { + m_UT_mcm_strip[aDigit->layer()][{ x + positionasic_thr, y + 0.25 }] += aDigit->mcmStrip(); + m_UT_mcm_strip[aDigit->layer()][{ x + positionasic_thr, y - 0.25 }] += aDigit->mcmStrip(); + m_UT_mcm_mu[aDigit->layer()][{ x + positionasic_thr, y + 0.25 }] += mcm_mu; + m_UT_mcm_mu[aDigit->layer()][{ x + positionasic_thr, y - 0.25 }] += mcm_mu; + m_UT_MCM_rms_ADC[aDigit->layer()][{ x + positionasic_thr, y + 0.25 }] += MCM_rms_ADC; + m_UT_MCM_rms_ADC[aDigit->layer()][{ x + positionasic_thr, y - 0.25 }] += MCM_rms_ADC; + } + + if ( ( aDigit->stave() == 0 ) && ( fabs( y ) < 1 ) ) { + { m_UT_mcm_strip[aDigit->layer()][{ x + positionasic_thr, y }] += aDigit->mcmStrip(); } + { m_UT_mcm_mu[aDigit->layer()][{ x + positionasic_thr, y }] += mcm_mu; } + { m_UT_MCM_rms_ADC[aDigit->layer()][{ x + positionasic_thr, y }] += MCM_rms_ADC; } + } + } +} diff --git a/UT/UTMonitors/src/UTSuperTAEMonitor.cpp b/UT/UTMonitors/src/UTSuperTAEMonitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..b22c76ed5fc63353095e074ec3178bf98ad39455 --- /dev/null +++ b/UT/UTMonitors/src/UTSuperTAEMonitor.cpp @@ -0,0 +1,179 @@ +/*****************************************************************************\ +* (c) Copyright 2020 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 "Event/ODIN.h" +#include "Event/TAEUtils.h" +#include "Event/UTDigit.h" +#include "Gaudi/Accumulators/Histogram.h" +#include "GaudiAlg/GaudiHistoAlg.h" +#include "GaudiKernel/GaudiException.h" +#include "GaudiKernel/PhysicalConstants.h" +#include "GaudiKernel/SystemOfUnits.h" +#include "Kernel/IUTReadoutTool.h" +#include "Kernel/STLExtensions.h" +#include "Kernel/UTDAQID.h" +#include "LHCbAlgs/Consumer.h" +#include "LHCbAlgs/MergingTransformer.h" +#include "UTDAQ/UTCoordinatesMap.h" +#include "UTDet/DeUTDetector.h" + +/** @class UTSuperTAEMonitor UTSuperTAEMonitor.cpp + * -------------------------------------------------- + * Counters for additional monitoring of UT (UT in global) + * The algorithm produces several useful histograms + * @author Wojciech Krupa + * @date 2024/10/07 + * -------------------------------------------------- + **/ + +using ODIN = LHCb::ODIN; +using ODINVector = Gaudi::Functional::vector_of_const_<ODIN const*>; +using InputVector = Gaudi::Functional::vector_of_const_<LHCb::UTDigits const*>; +using BXTypes = LHCb::ODIN::BXTypes; +using EventTypes = LHCb::ODIN::EventTypes; +using namespace LHCb; +namespace GA = Gaudi::Accumulators; + +// Tools for booking histograms +namespace Utility { + template <typename T, typename OWNER> + void map_emplace( T& t, typename T::key_type key, OWNER* owner, std::string const& title, + Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis1, + Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis2 ) { + t.emplace( std::piecewise_construct, std::forward_as_tuple( key ), + std::forward_as_tuple( owner, key, title, axis1, axis2 ) ); + } +} // namespace Utility + +class UTSuperTAEMonitor final : public LHCb::Algorithm::MergingConsumer<void( ODINVector const&, InputVector const& )> { + +public: + UTSuperTAEMonitor( const std::string&, ISvcLocator* ); + StatusCode initialize() override; + void operator()( ODINVector const&, InputVector const& ) const override; + void fillHistograms( const LHCb::UTDigit*, LHCb::ODIN const&, int ) const; + +private: + LHCb::TAE::Handler m_taeHandler{ this }; + mutable UTCoordinatesMap UTMap; + Gaudi::Property<signed int> m_HalfWindow{ this, "HalfWindow", 3 }; + + using Histogram1D = GA::Histogram<1, GA::atomicity::full, float>; + using Histogram2D = GA::Histogram<2, GA::atomicity::full, float>; + using ProfileHistogram = GA::ProfileHistogram<1, GA::atomicity::full, float>; + using ProfileHistogram2D = GA::ProfileHistogram<2, GA::atomicity::full, float>; + using Axis = GA::Axis<float>; + + // Definition of axis + const GA::Axis<double> AxisADC = GA::Axis<double>( 64, -32.5, 31.5, "nADC" ); //-32-31 + const GA::Axis<double> AxisADC_signed = GA::Axis<double>( 32, -0.5, 31.5, "nADC" ); // 0-31 + const GA::Axis<double> AxisBCID = GA::Axis<double>( 3565, -0.5, 3564.5, "BunchID" ); // + const GA::Axis<double> AxisDIGIT = GA::Axis<double>( 101, -0.5, 100.5, "UTDigits (size of digit container)" ); + const GA::Axis<double> AxisTAE = + GA::Axis<double>( 2 * m_HalfWindow + 1, -1 * m_HalfWindow - 0.5, m_HalfWindow + 0.5, "TAE index" ); + + const std::array<std::string, 4> UT_layers = { "UTaX", "UTaU", "UTbV", "UTbX" }; + + // General TAE counters + mutable Gaudi::Accumulators::Histogram<1> m_bxtype{ this, "BXType", "BXType", { 4, -0.5, 3.5 } }; + mutable Gaudi::Accumulators::Histogram<1> m_trgtype{ this, "TrgType", "TrgType", { 16, -0.5, 15.5 } }; + mutable Gaudi::Accumulators::Histogram<2> m_bcid_bxtype{ + this, "BXTypeVsBXID", "BXTypeVsBXID", AxisBCID, { 4, -0.5, 3.5 } }; + mutable Gaudi::Accumulators::Histogram<2> m_bcid_trgtype{ + this, "TrgTypeVsBXID", "TrgTypeVsBXID", AxisBCID, { 16, -0.5, 15.5 } }; + mutable Gaudi::Accumulators::WeightedHistogram<2> m_bcid_taesummary{ + this, "TAESummary", "TAE summary", AxisBCID, { 14, -0.5, -0.5 + 14 } }; + mutable Gaudi::Accumulators::Histogram<2> m_bcid_taeindex{ + this, "TAEIndex", "TAE index", AxisBCID, { 64, -0.5, -0.5 + 64 } }; + + // Counters TAE step vs ADC per DCB or per quadrant or UT + mutable std::map<std::string, GA::Histogram<2>> m_2d_DCB_tae_adc; + mutable std::map<std::string, GA::Histogram<2>> m_2d_DCBQuadrant_tae_adc; + mutable Gaudi::Accumulators::Histogram<2> m_2d_UT_tae_adc{ this, "UTTAE", "UTTAE", AxisTAE, AxisADC_signed }; +}; + +// Declaration of the Algorithm Factory +DECLARE_COMPONENT( UTSuperTAEMonitor ) + +UTSuperTAEMonitor::UTSuperTAEMonitor( const std::string& name, ISvcLocator* pSvcLocator ) + : LHCb::Algorithm::MergingConsumer<void( ODINVector const&, InputVector const& )>( + name, pSvcLocator, { KeyValues{ "ODINVector", {} }, KeyValues{ "InputVector", {} } } ){}; + +StatusCode UTSuperTAEMonitor::initialize() { + return base_class::initialize().andThen( [&] { + // Set the top directory to UT + // Initialise counters + for ( const auto& DCB : UTMap.getDCBsAside() ) { + Utility::map_emplace( m_2d_DCB_tae_adc, DCB, this, DCB, AxisTAE, AxisADC_signed ); + } + for ( const auto& DCB : UTMap.getDCBsCside() ) { + Utility::map_emplace( m_2d_DCB_tae_adc, DCB, this, DCB, AxisTAE, AxisADC_signed ); + } + for ( const auto& DCBQuadrant : UTMap.getDCBQuadrants() ) { + Utility::map_emplace( m_2d_DCBQuadrant_tae_adc, DCBQuadrant, this, DCBQuadrant, AxisTAE, AxisADC_signed ); + } + } ); +} + +void UTSuperTAEMonitor::operator()( ODINVector const& odinVector, InputVector const& inputVector ) const { + + auto taeEvents = m_taeHandler.arrangeTAE( odinVector, inputVector, 3 ); + + if ( taeEvents.empty() ) { return; } + + for ( auto it = taeEvents.begin(); it != taeEvents.end(); ++it ) { + int offset = it->first; // 0 is central, negative for Prev, positive for Next + const LHCb::ODIN& odin = it->second.first; + const LHCb::UTDigits& digitsCont = it->second.second; + + for ( auto d_it = digitsCont.begin(); d_it != digitsCont.end(); ++d_it ) { fillHistograms( *d_it, odin, offset ); } + } +} + +void UTSuperTAEMonitor::fillHistograms( const LHCb::UTDigit* aDigit, LHCb::ODIN const& odin, int offset ) const { + + // Let's find where we are in UT + auto tuple = UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() ); + std::string module_name = std::get<4>( tuple ); + auto y_p = std::get<1>( tuple ); + std::string position = ( y_p > 0 ) ? "T" : "B"; + std::string side = ( aDigit->side() == 0 ) ? "C" : "A"; + std::array<std::string, 4> layer = { "UTaX", "UTaU", "UTbV", "UTbX" }; + std::string station = ( aDigit->layer() < 2 ) ? "a" : "b"; + std::string dcb_name = UTMap.getDCB( layer[aDigit->layer()] + "_" + module_name ); + int TAEindex = offset; // + + // Let's find where we are in the LHCb + auto bcid = odin.bunchId(); + auto bx = static_cast<uint8_t>( odin.bunchCrossingType() ); + auto trgtype = odin.triggerType(); + + // simple counters + ++m_bxtype[bx]; + ++m_trgtype[trgtype]; + ++m_bcid_bxtype[{ bcid, bx }]; + ++m_bcid_trgtype[{ bcid, trgtype }]; + + // summary plots this should be replaced by odin.eventType soon as below + double weight = 1; + int offset2 = 0; + m_bcid_taesummary[{ bcid, offset2 + bx }] += weight; + offset2 += 4; + m_bcid_taesummary[{ bcid, offset2++ }] += weight * odin.isTAE(); + m_bcid_taesummary[{ bcid, offset2++ }] += weight * odin.timeAlignmentEventCentral(); + m_bcid_taesummary[{ bcid, offset2++ }] += weight * odin.timeAlignmentEventFirst(); + + ++m_bcid_taeindex[{ bcid, odin.timeAlignmentEventIndex() }]; + ++m_2d_UT_tae_adc[{ TAEindex, aDigit->depositedCharge() }]; + ++m_2d_DCB_tae_adc.at( dcb_name )[{ TAEindex, aDigit->depositedCharge() }]; + ++m_2d_DCBQuadrant_tae_adc.at( + UTMap.getDCBQuadrant( layer[aDigit->layer()] + "_" + module_name ) )[{ TAEindex, aDigit->depositedCharge() }]; +}; diff --git a/UT/UTMonitors/src/UTTAEMonitor.cpp b/UT/UTMonitors/src/UTTAEMonitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8d64c8f747f4f829a8b070df30330a19f4748c0a --- /dev/null +++ b/UT/UTMonitors/src/UTTAEMonitor.cpp @@ -0,0 +1,165 @@ +/*****************************************************************************\ +* (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 "Event/ODIN.h" +#include "Event/UTDigit.h" +#include "Gaudi/Accumulators/Histogram.h" +#include "GaudiAlg/GaudiHistoAlg.h" +#include "Kernel/IUTReadoutTool.h" +#include "Kernel/STLExtensions.h" +#include "Kernel/UTDAQID.h" +#include "LHCbAlgs/Consumer.h" +#include "UTDAQ/UTCoordinatesMap.h" + +/** @class UTTAEMonitor UTTAEMonitor.h + * -------------------------------------------------- + * Class for monitoring TAE in Monet (UT in local) + * @author W. Krupa + * @author C. Hadjivasiliou + * @date 2024/03/19 + * -------------------------------------------------- + **/ + +namespace GA = Gaudi::Accumulators; +using namespace LHCb; +using EventTypes = LHCb::ODIN::EventTypes; + +// Tools for booking histograms +namespace Utility { + template <typename T, typename OWNER> + void map_emplace( T& t, typename T::key_type key, OWNER* owner, std::string const& title, + Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis1, + Gaudi::Accumulators::Axis<typename T::mapped_type::AxisArithmeticType> axis2 ) { + t.emplace( std::piecewise_construct, std::forward_as_tuple( key ), + std::forward_as_tuple( owner, key, title, axis1, axis2 ) ); + } +} // namespace Utility + +const std::array<std::string, 4> UT_layers = { "UTaX", "UTaU", "UTbV", "UTbX" }; + +class UTTAEMonitor : public LHCb::Algorithm::Consumer<void( UTDigits const&, UTDigits const&, LHCb::ODIN const& ), + LHCb::DetDesc::usesBaseAndConditions<GaudiHistoAlg>> { + +public: + UTTAEMonitor( const std::string&, ISvcLocator* ); + StatusCode initialize() override; + void operator()( const UTDigits&, const UTDigits&, LHCb::ODIN const& ) const override; + void fillHistograms( const LHCb::UTDigit*, LHCb::ODIN const& ) const; + + ToolHandle<IUTReadoutTool> readoutTool{ this, "ReadoutTool", "UTReadoutTool" }; + mutable UTCoordinatesMap UTMap; + Gaudi::Property<signed int> m_HalfWindow{ this, "HalfWindow", 3 }; + + // Definition of axis + const GA::Axis<double> AxisADC = GA::Axis<double>( 64, -32.5, 31.5, "nADC" ); //-32-31 + const GA::Axis<double> AxisADC_signed = GA::Axis<double>( 32, -0.5, 31.5, "nADC" ); // 0-31 + const GA::Axis<double> AxisBCID = GA::Axis<double>( 3565, -0.5, 3564.5, "BunchID" ); // + const GA::Axis<double> AxisDIGIT = GA::Axis<double>( 101, -0.5, 100.5, "UTDigits (size of digit container)" ); + const GA::Axis<double> AxisTAE = GA::Axis<double>( 2 * m_HalfWindow + 1, -1 * m_HalfWindow - 0.5, m_HalfWindow + 0.5, + "TAE index" ); // for windows 3 - central is 4 +private: + // General counters + mutable Gaudi::Accumulators::Histogram<1> m_bxtype{ this, "BXType", "BXType", { 4, -0.5, 3.5 } }; + mutable Gaudi::Accumulators::Histogram<1> m_trgtype{ this, "TrgType", "TrgType", { 16, -0.5, 15.5 } }; + mutable Gaudi::Accumulators::Histogram<2> m_bcid_bxtype{ + this, "BXTypeVsBXID", "BXTypeVsBXID", AxisBCID, { 4, -0.5, 3.5 } }; + mutable Gaudi::Accumulators::Histogram<2> m_bcid_trgtype{ + this, "TrgTypeVsBXID", "TrgTypeVsBXID", AxisBCID, { 16, -0.5, 15.5 } }; + mutable Gaudi::Accumulators::WeightedHistogram<2> m_bcid_taesummary{ + this, "TAESummary", "TAE summary", AxisBCID, { 14, -0.5, -0.5 + 14 } }; + mutable Gaudi::Accumulators::Histogram<2> m_bcid_taeindex{ + this, "TAEIndex", "TAE index", AxisBCID, { 64, -0.5, -0.5 + 64 } }; + + // Counters TAE step vs ADC per DCB or per quadrant or UT + mutable std::map<std::string, GA::Histogram<2>> m_2d_DCB_tae_adc; + mutable std::map<std::string, GA::Histogram<2>> m_2d_DCBQuadrant_tae_adc; + mutable Gaudi::Accumulators::Histogram<2> m_2d_UT_tae_adc{ this, "UTTAE", "UTTAE", AxisTAE, AxisADC_signed }; +}; +DECLARE_COMPONENT( UTTAEMonitor ) + +UTTAEMonitor::UTTAEMonitor( const std::string& name, ISvcLocator* svcloc ) + : Consumer{ name, + svcloc, + { { "InputData", UTDigitLocation::UTDigits }, + { "InputErrorData", UTDigitLocation::UTDigits }, + { "ODINLocation", LHCb::ODINLocation::Default } } } {} + +StatusCode UTTAEMonitor::initialize() { + return Consumer::initialize().andThen( [&] { + // Set the top directory to UT + if ( histoTopDir().empty() ) setHistoTopDir( "UT/" ); + for ( const auto& DCB : UTMap.getDCBsAside() ) { + Utility::map_emplace( m_2d_DCB_tae_adc, DCB, this, DCB, AxisTAE, AxisADC_signed ); + } + for ( const auto& DCB : UTMap.getDCBsCside() ) { + Utility::map_emplace( m_2d_DCB_tae_adc, DCB, this, DCB, AxisTAE, AxisADC_signed ); + } + for ( const auto& DCBQuadrant : UTMap.getDCBQuadrants() ) { + Utility::map_emplace( m_2d_DCBQuadrant_tae_adc, DCBQuadrant, this, DCBQuadrant, AxisTAE, AxisADC_signed ); + } + } ); +} + +void UTTAEMonitor::operator()( const UTDigits& digitsCont, const UTDigits& errdigitsCont, + LHCb::ODIN const& odin ) const { + + // Loop over UTDigits + for ( const auto& d : digitsCont ) { fillHistograms( d, odin ); } + // Loop over UTErrDigits + for ( const auto& d : errdigitsCont ) { fillHistograms( d, odin ); } +} + +void UTTAEMonitor::fillHistograms( const LHCb::UTDigit* aDigit, LHCb::ODIN const& odin ) const { + + // Let's find where we are in UT + auto tuple = UTMap.getTuple( aDigit->module(), aDigit->face(), aDigit->stave(), aDigit->side(), aDigit->sector() ); + std::string module_name = std::get<4>( tuple ); + auto y_p = std::get<1>( tuple ); + std::string position = ( y_p > 0 ) ? "T" : "B"; + std::string side = ( aDigit->side() == 0 ) ? "C" : "A"; + std::array<std::string, 4> layer = { "UTaX", "UTaU", "UTbV", "UTbX" }; + std::string station = ( aDigit->layer() < 2 ) ? "a" : "b"; + std::string dcb_name = UTMap.getDCB( layer[aDigit->layer()] + "_" + module_name ); + unsigned int TAEindex = odin.timeAlignmentEventIndex(); + + // Let's find where we are in the LHCb + auto bcid = odin.bunchId(); + auto bx = static_cast<uint8_t>( odin.bunchCrossingType() ); + auto trgtype = odin.triggerType(); + + // simple counters + ++m_bxtype[bx]; + ++m_trgtype[trgtype]; + ++m_bcid_bxtype[{ bcid, bx }]; + ++m_bcid_trgtype[{ bcid, trgtype }]; + + // summary plots this should be replace by odin.eventType soon as below + double weight = 1; + int offset = 0; + m_bcid_taesummary[{ bcid, offset + bx }] += weight; + offset += 4; + m_bcid_taesummary[{ bcid, offset++ }] += weight * odin.isTAE(); + m_bcid_taesummary[{ bcid, offset++ }] += weight * odin.timeAlignmentEventCentral(); + m_bcid_taesummary[{ bcid, offset++ }] += weight * odin.timeAlignmentEventFirst(); + + // for ( auto const& et : TAEEventTypes ) { m_bcid_taesummary[{bcid, offset++}] += weight * odin.eventTypeBit( et + // );} + + ++m_bcid_taeindex[{ bcid, odin.timeAlignmentEventIndex() }]; + + if ( TAEindex != 0 ) { + int offset = TAEindex - m_HalfWindow - 1; // let's alligne that with superTAE + ++m_2d_UT_tae_adc[{ offset, aDigit->depositedCharge() }]; + ++m_2d_DCB_tae_adc.at( dcb_name )[{ offset, aDigit->depositedCharge() }]; + ++m_2d_DCBQuadrant_tae_adc.at( + UTMap.getDCBQuadrant( layer[aDigit->layer()] + "_" + module_name ) )[{ offset, aDigit->depositedCharge() }]; + } +}; diff --git a/UT/UTMonitors/src/UTVeloUTCorrelationsMonitor.cpp b/UT/UTMonitors/src/UTVeloUTCorrelationsMonitor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..eb9a0ad0f69b2be9c172d767ca3a06f889d45ac9 --- /dev/null +++ b/UT/UTMonitors/src/UTVeloUTCorrelationsMonitor.cpp @@ -0,0 +1,134 @@ +/***************************************************************************** \ +* (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 "Event/ODIN.h" +#include "Event/RawBank.h" +#include "Event/RawEvent.h" +#include "Event/UTDigit.h" +#include "Gaudi/Accumulators/Histogram.h" +#include "Gaudi/Accumulators/HistogramArray.h" +#include "GaudiAlg/GaudiHistoAlg.h" +#include "Kernel/IUTReadoutTool.h" +#include "Kernel/UTDAQBoard.h" +#include "Kernel/UTDAQDefinitions.h" +#include "Kernel/UTDAQID.h" +#include "LHCbAlgs/Consumer.h" +#include "LHCbAlgs/Transformer.h" +#include "UTDAQ/UTCoordinatesMap.h" +#include "UTDAQ/UTDAQHelper.h" +#include "UTDAQ/UTInfo.h" + +// Needed for VELO +#include "Event/VPFullCluster.h" +#include "Kernel/VPConstants.h" + +/** @class UTVeloUTCorrelationsMonitor UTVeloUTCorrelationsMonitor.cpp + * -------------------------------------------------- + * Counters for Monet: correlation monitors + * The algorithm produces correlation plots for hits + * @author Paolo Gandini + * @author Wojciech Krupa + * @date 2024/04/20 + * -------------------------------------------------- + **/ + +using namespace LHCb; +namespace GA = Gaudi::Accumulators; + +// Definition of axis +const GA::Axis<double> AxisADC = GA::Axis<double>( 64, -32.5, 31.5, "nADC" ); //-32, 31 +const GA::Axis<double> AxisADC_signed = GA::Axis<double>( 31, -0.5, 31.5, "nADC" ); // 0-31 +const GA::Axis<double> AxisBCID = GA::Axis<double>( 3565, -0.5, 3564.5, "BunchID" ); // +const GA::Axis<double> AxisDIGIT = GA::Axis<double>( 1001, -0.5, 10000.5, "# UT" ); +const GA::Axis<double> AxisVeloDIGIT = GA::Axis<double>( 1001, -0.5, 10000.5, "# Velo" ); + +// UT layers +const std::array<std::string, 4> UT_layers = { "UTaX", "UTaU", "UTbV", "UTbX" }; + +enum class masks { channel = 0x000001ff, lane = 0x00000e00, board = 0x000ff000 }; +template <masks m> +[[nodiscard]] static constexpr unsigned int extract( unsigned int i ) { + constexpr auto b = std::countr_zero( static_cast<unsigned int>( m ) ); + return ( i & static_cast<unsigned int>( m ) ) >> b; +} + +/** ------------------------------------------------------------------------------------------------------------------------ */ +class UTVeloUTCorrelationsMonitor + : public LHCb::Algorithm::Consumer<void( UTDigits const&, UTDigits const&, LHCb::ODIN const&, + std::vector<VPFullCluster> const& ), + LHCb::DetDesc::usesBaseAndConditions<GaudiHistoAlg>> { +public: + UTVeloUTCorrelationsMonitor( const std::string& name, ISvcLocator* svcloc ) + : Consumer{ name, + svcloc, + { + { "InputData", UTDigitLocation::UTDigits }, + { "InputErrorData", UTDigitLocation::UTDigits }, + { "ODINLocation", LHCb::ODINLocation::Default }, + { "VeloClusterLocation", LHCb::VPFullClusterLocation::Default }, + } } {} + ToolHandle<IUTReadoutTool> readoutTool{ this, "ReadoutTool", "UTReadoutTool" }; + + void operator()( const UTDigits&, const UTDigits&, LHCb::ODIN const&, + const std::vector<LHCb::VPFullCluster>& ) const override; + + StatusCode initialize() override { + return Consumer::initialize().andThen( [&] { + // Set the top directory to UT + if ( histoTopDir().empty() ) setHistoTopDir( "" ); + } ); + } + +private: + // General plots + mutable GA::Histogram<1> m_nUTDigits{ this, "nUTDigits", "Total UT Digits", AxisDIGIT }; + mutable GA::Histogram<1> m_nVeloDigits{ this, "nVeloDigits", "Total Velo Digits", AxisVeloDIGIT }; + mutable GA::Histogram<2> m_nUTnVeloDigits{ this, "nUTnVeloDigits", "nUT vs nVELO", AxisDIGIT, AxisVeloDIGIT }; + mutable GA::Histogram<2> m_nUTnVeloDigits_bb{ this, "nUTnVeloDigits_bb", "nUT vs nVELO - bb", AxisDIGIT, + AxisVeloDIGIT }; + mutable GA::Histogram<2> m_nUTnVeloDigits_be{ this, "nUTnVeloDigits_be", "nUT vs nVELO - be", AxisDIGIT, + AxisVeloDIGIT }; + mutable GA::Histogram<2> m_nUTnVeloDigits_eb{ this, "nUTnVeloDigits_eb", "nUT vs nVELO - eb", AxisDIGIT, + AxisVeloDIGIT }; + mutable GA::Histogram<2> m_nUTnVeloDigits_ee{ this, "nUTnVeloDigits_ee", "nUT vs nVELO - ee", AxisDIGIT, + AxisVeloDIGIT }; + + mutable GA::HistogramArray<GA::Histogram<2>, 4> m_nUTnVeloDigits_bx{ + this, + []( int i ) { return fmt::format( "nUTnVeloDigits_bx{}", i ); }, + []( int i ) { return fmt::format( "nUTnVeloDigits BXID{}", i ); }, + { 201, -0.5, 400.5, "# UT" }, + { 201, -0.5, 400.5, "# Velo" } }; + +}; // End of class UTVeloUTCorrelationsMonitor +DECLARE_COMPONENT( UTVeloUTCorrelationsMonitor ) + +void UTVeloUTCorrelationsMonitor::operator()( const UTDigits& digitsCont, const UTDigits& errdigitsCont, + LHCb::ODIN const& odin, + const std::vector<LHCb::VPFullCluster>& vpClusters ) const { + + // General counters + const unsigned int nBuncId = odin.bunchId(); + const int bunchCrossingType = (int)odin.bunchCrossingType(); + ++m_nUTDigits[digitsCont.size() + errdigitsCont.size()]; + ++m_nVeloDigits[vpClusters.size()]; + ++m_nUTnVeloDigits[{ digitsCont.size() + errdigitsCont.size(), vpClusters.size() }]; + ++m_nUTnVeloDigits_bx[bunchCrossingType][{ digitsCont.size() + errdigitsCont.size(), vpClusters.size() }]; + + if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::BeamCrossing ) + ++m_nUTnVeloDigits_bb[{ digitsCont.size() + errdigitsCont.size(), vpClusters.size() }]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam1 ) + ++m_nUTnVeloDigits_be[{ digitsCont.size() + errdigitsCont.size(), vpClusters.size() }]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::Beam2 ) + ++m_nUTnVeloDigits_eb[{ digitsCont.size() + errdigitsCont.size(), vpClusters.size() }]; + else if ( odin.bunchCrossingType() == LHCb::ODIN::BXTypes::NoBeam ) + ++m_nUTnVeloDigits_ee[{ digitsCont.size() + errdigitsCont.size(), vpClusters.size() }]; +}