diff --git a/Core/include/Core/DeIOV.h b/Core/include/Core/DeIOV.h index 4836f563f7028e13864a6dcd5e62a3c78a00834b..f4eabb3d79fcbc599828a8295265113d4f1bdf43 100644 --- a/Core/include/Core/DeIOV.h +++ b/Core/include/Core/DeIOV.h @@ -66,6 +66,8 @@ namespace LHCb::Detector { dd4hep::DetElement detector() const { return this->access()->detector; } /// Accessor to the geometry structure of this detector element dd4hep::PlacedVolume geometry() const { return this->access()->geometry; } + /// Accessor to the geometry structure of this detector element + std::string lVolumeName() const { return geometry().volume().name(); } /// Access to the alignmant object to transformideal coordinates dd4hep::Alignment detectorAlignment() const { return this->access()->detectorAlignment; } diff --git a/Detector/FT/include/Detector/FT/DeFT.h b/Detector/FT/include/Detector/FT/DeFT.h index aa0af29a686a314fdb36f42fc4b9fe10c09778cd..a58199df1828abd4e0b3946a42ccaa417c8eeb6d 100644 --- a/Detector/FT/include/Detector/FT/DeFT.h +++ b/Detector/FT/include/Detector/FT/DeFT.h @@ -1,21 +1,25 @@ /*****************************************************************************\ -* (c) Copyright 2000-2019 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. * + * (c) Copyright 2000-2019 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. * \*****************************************************************************/ #pragma once #include "Detector/FT/DeFTStation.h" +#include "Detector/FT/FTChannelID.h" +#include "Detector/FT/FTConstants.h" +namespace DeFTLocation { + // FT location defined in the XmlDDDB + inline const std::string Default = "/dd/Structure/LHCb/AfterMagnetRegion/T/FT"; +} // namespace DeFTLocation namespace LHCb::Detector { - namespace detail { - /** * FT detector element data * \author Markus Frank @@ -23,18 +27,182 @@ namespace LHCb::Detector { * \version 1.0 */ struct DeFTObject : DeIOVObject { - int version{dd4hep::_toInt( "FT:version" )}; // FT Geometry Version - int nModulesT1{dd4hep::_toInt( "FT:nModulesT1" )}; // Number of modules in T1 - int nModulesT2{dd4hep::_toInt( "FT:nModulesT2" )}; // Number of modules in T2 - int nModulesT3{dd4hep::_toInt( "FT:nModulesT3" )}; // Number of modules in T3 - int nLayers{dd4hep::_toInt( "FT:nLayers" )}; // Number of layers per station - int nQuarters{dd4hep::_toInt( "FT:nQuarters" )}; // Number of quarters per layer - int nChannelsInModule{dd4hep::_toInt( "FT:nChannelsInModule" )}; // Number of channels per SiPM - std::array stations; + int m_version{dd4hep::_toInt( "FT:version" )}; // FT Geometry Version + int m_nModulesT1{dd4hep::_toInt( "FT:nModulesT1" )}; // Number of modules in T1 + int m_nModulesT2{dd4hep::_toInt( "FT:nModulesT2" )}; // Number of modules in T2 + int m_nModulesT3{dd4hep::_toInt( "FT:nModulesT3" )}; // Number of modules in T3 + int m_nStations{dd4hep::_toInt( "FT:nStations" )}; // Number of stations + int m_nLayers{dd4hep::_toInt( "FT:nLayers" )}; // Number of layers per station + int m_nQuarters{dd4hep::_toInt( "FT:nQuarters" )}; // Number of quarters per layer + int m_nChannelsInModule{dd4hep::_toInt( "FT:nChannelsInModule" )}; // Number of channels per SiPM + int m_nTotQuarters = m_nStations * m_nLayers * m_nQuarters; + int m_nTotModules = m_nModulesT1 + m_nModulesT2 + m_nModulesT3; + int m_nTotChannels = m_nTotModules * m_nChannelsInModule; + + std::array m_stations; DeFTObject( const dd4hep::DetElement& de, dd4hep::cond::ConditionUpdateContext& ctxt ); + void applyToAllChildren( const std::function& func ) const override { + for ( auto& station : m_stations ) { func( LHCb::Detector::DeIOV{&station} ); }; + }; + void applyToAllLayers( const std::function& func ) const { + for ( auto& station : m_stations ) { station.applyToAllChildren( func ); }; + }; + void applyToAllMats( const std::function )>& func ) const { + for ( auto& station : m_stations ) + for ( auto& layer : station.m_layers ) + for ( auto& quarter : layer.m_quarters ) + for ( auto& module : quarter.m_modules ) module.applyToAllChildren( func ); + } + const DeFTMatObject& firstMat() const { return m_stations[0].m_layers[0].m_quarters[0].m_modules[0].m_mats[0]; }; + }; // End namespace detail + } // namespace detail + /** + * FT interface + * + * \author Louis Henry + * \date 2021-09-21 + * \version 1.0 + */ + template + struct DeFTElement : DeIOVElement { + using DeIOVElement::DeIOVElement; + int version() const { return this->access()->m_version; }; + const std::array stations() const { + const auto obj = this->access(); + return {obj->m_stations[0], obj->m_stations[1], obj->m_stations[2]}; + }; + void applyToAllLayers( const std::function )>& func ) const { + this->access()->applyToAllLayers( func ); }; - } // End namespace detail + void applyToAllMats( const std::function )>& func ) const { + this->access()->applyToAllMats( func ); + } + const DeFTMat& firstMat() const { return &( this->access()->firstMat() ); } + + int nStations() const { return this->access()->m_nStations; } + int nChannels() const { return this->access()->m_nTotChannels; } + + /** Find the FT Station corresponding to the point + * @return Pointer to the relevant station + */ + [[nodiscard]] const std::optional findStation( const ROOT::Math::XYZPoint& aPoint ) const { + const auto iS = std::find_if( + std::begin( this->access()->m_stations ), std::end( this->access()->m_stations ), + [&aPoint]( detail::DeFTStationObject const& s ) { return DeFTStation{&s}.isInside( aPoint ); } ); + return iS != this->access()->m_stations.end() ? std::optional{iS} : std::optional{}; + } + + /// Find the layer for a given XYZ point + [[nodiscard]] const std::optional findLayer( const ROOT::Math::XYZPoint& aPoint ) const { + const auto s = findStation( aPoint ); + return s ? s->findLayer( aPoint ) : std::optional{}; + } + + /// Find the quarter for a given XYZ point + [[nodiscard]] const std::optional findQuarter( const ROOT::Math::XYZPoint& aPoint ) const { + const auto l = findLayer( aPoint ); + return l ? l->findQuarter( aPoint ) : std::optional{}; + } + + /// Find the module for a given XYZ point + [[nodiscard]] const std::optional findModule( const ROOT::Math::XYZPoint& aPoint ) const { + const auto l = findLayer( aPoint ); // is faster than via DeFTQuarter + return l ? l->findModule( aPoint ) : std::optional{}; + } + + /// Find the mat for a given XYZ point + [[nodiscard]] const std::optional findMat( const ROOT::Math::XYZPoint& aPoint ) const { + const auto m = findModule( aPoint ); + return m ? m->findMat( aPoint ) : std::optional{}; + } + + /** Find the FT Station corresponding to the channel id + * @return Pointer to the relevant station + */ + [[nodiscard]] const std::optional findStation( const FTChannelID& aChannel ) const { + return ( to_unsigned( aChannel.station() ) < this->access()->m_stations.size() ) + ? &( this->access()->m_stations[to_unsigned( aChannel.station() )] ) + : std::optional{}; + } + + /** Find the FT Layer corresponding to the channel id + * @return Pointer to the relevant layer + */ + [[nodiscard]] const std::optional findLayer( const FTChannelID& aChannel ) const { + const auto s = findStation( aChannel ); + return s ? s->findLayer( aChannel ) : std::optional{}; + } + + /** Find the FT Quarter corresponding to the channel id + * @return Pointer to the relevant quarter + */ + [[nodiscard]] const std::optional findQuarter( const FTChannelID& aChannel ) const { + const auto l = findLayer( aChannel ); + return l ? l->findQuarter( aChannel ) : std::optional{}; + } + + /** Find the FT Module corresponding to the channel id + * @return Pointer to the relevant module + */ + [[nodiscard]] const std::optional findModule( const FTChannelID& aChannel ) const { + const auto q = findQuarter( aChannel ); + return q ? q->findModule( aChannel ) : std::optional{}; + } + + /** Find the FT Mat corresponding to the channel id + * @return Pointer to the relevant module + */ + [[nodiscard]] const std::optional findMat( const FTChannelID& aChannel ) const { + const auto m = findModule( aChannel ); + return m ? m->findMat( aChannel ) : std::optional{}; + } + + int sensitiveVolumeID( const ROOT::Math::XYZPoint& point ) const { + const auto& mat = findMat( point ); + return mat ? mat->sensitiveVolumeID( point ) : -1; + } + + /// Get a random FTChannelID (useful for the thermal noise, which is ~flat) + FTChannelID getRandomChannelFromSeed( const float seed ) const { + if ( seed < 0.f || seed > 1.f ) return FTChannelID::kInvalidChannel(); + const auto& obj = this->access(); + unsigned int flatChannel = int( seed * obj->m_nTotChannels ); + unsigned int channelInModule = flatChannel & ( obj->m_nChannelsInModule - 1u ); + flatChannel /= obj->m_nChannelsInModule; + unsigned int quarter = flatChannel & ( obj->m_nQuarters - 1u ); + flatChannel /= obj->m_nQuarters; + unsigned int layer = flatChannel & ( obj->m_nLayers - 1u ); + flatChannel /= obj->m_nLayers; + unsigned int station = 1; + unsigned int module = flatChannel; + if ( flatChannel >= obj->m_nModulesT1 + obj->m_nModulesT2 ) { + station = 3; + module = flatChannel - obj->m_nModulesT1 - obj->m_nModulesT2; + } else if ( flatChannel >= obj->m_nModulesT1 ) { + station = 2; + module = flatChannel - obj->m_nModulesT1; + } + return FTChannelID( FTChannelID::StationID{station}, FTChannelID::LayerID{layer}, FTChannelID::QuarterID{quarter}, + FTChannelID::ModuleID{module}, channelInModule ); + } + + /// Get a random FTChannelID from a pseudoChannel (useful for the AP noise) + FTChannelID getRandomChannelFromPseudo( const int pseudoChannel, const float seed ) const { + if ( seed < 0.f || seed > 1.f ) return FTChannelID::kInvalidChannel(); + const auto& obj = this->access(); + unsigned int flatQuarter = int( seed * obj->m_nTotQuarters ); + auto quarter = FTChannelID::QuarterID{flatQuarter & ( obj->m_nQuarters - 1u )}; + flatQuarter /= obj->m_nQuarters; + auto layer = FTChannelID::LayerID{flatQuarter & ( obj->m_nLayers - 1u )}; + flatQuarter /= obj->m_nLayers; + auto station = FTChannelID::StationID{( flatQuarter & obj->m_stations.size() ) + 1u}; + + auto module = FTChannelID::ModuleID{pseudoChannel / obj->m_nChannelsInModule}; + const auto& moduleDet = findModule( FTChannelID( station, layer, quarter, module, 0u ) ); + return moduleDet->channelFromPseudo( pseudoChannel & ( obj->m_nChannelsInModule - 1u ) ); + } + }; - using DeFT = DeIOVElement; + using DeFT = DeFTElement; } // End namespace LHCb::Detector diff --git a/Detector/FT/include/Detector/FT/DeFTLayer.h b/Detector/FT/include/Detector/FT/DeFTLayer.h index 56459c5d37737bad30048050b3f5c1627fa55f39..56fce7559adc1102c65976f77f3f5e6c9628e42b 100644 --- a/Detector/FT/include/Detector/FT/DeFTLayer.h +++ b/Detector/FT/include/Detector/FT/DeFTLayer.h @@ -1,12 +1,12 @@ /*****************************************************************************\ -* (c) Copyright 2000-2019 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. * + * (c) Copyright 2000-2019 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. * \*****************************************************************************/ #pragma once @@ -27,11 +27,82 @@ namespace LHCb::Detector { */ struct DeFTLayerObject : DeIOVObject { /// Reference to the static information of the quarters - std::array quarters; - DeFTLayerObject( const dd4hep::DetElement& de, dd4hep::cond::ConditionUpdateContext& ctxt ); + /// FIXME: LayerID does not mean the same thing as in DetDesc. Here it is a global layer ID. + unsigned int m_layerID; ///< layer ID number + float m_globalZ; ///< Global z position of layer closest to y-axis + ROOT::Math::Plane3D m_plane; ///< xy-plane in the z-middle of the layer + float m_dzdy; ///< dz/dy of the layer (tan of the beam angle) + float m_stereoAngle; // = {dd4hep::_toFloat("FT::stereoAngle")}; ///< stereo angle of the layer // FIXME + float m_sizeX = {dd4hep::_toFloat( "FT:StationSizeX" )}; ///< Size of the layer in x // FIXME + float m_sizeY = {dd4hep::_toFloat( "FT:StationSizeY" )}; ///< Size of the layer in y // FIXME + float m_dxdy = tan( m_stereoAngle ); ///< dx/dy of the layer (ie. tan(m_stereoAngle)) + std::array m_quarters; + DeFTLayerObject( const dd4hep::DetElement& de, dd4hep::cond::ConditionUpdateContext& ctxt, unsigned int iLayer, + unsigned int stationID ); + void applyToAllChildren( const std::function& func ) const override { + for ( auto& quarter : m_quarters ) { func( LHCb::Detector::DeIOV{&quarter} ); }; + }; + void applyToAllMats( const std::function& func ) const { + for ( auto& quarter : m_quarters ) { + for ( auto& module : quarter.m_modules ) module.applyToAllChildren( func ); + } + }; + ROOT::Math::XYZPoint toLocal( const ROOT::Math::XYZPoint& p ) const { + return ROOT::Math::XYZPoint( + toLHCbUnits( this->detectorAlignment.worldToLocal( toDD4hepUnits( ROOT::Math::XYZVector( p ) ) ) ) ); + } + ROOT::Math::XYZVector toLocal( const ROOT::Math::XYZVector& v ) const { + return toLHCbUnits( this->detectorAlignment.worldToLocal( toDD4hepUnits( v ) ) ); + } + ROOT::Math::XYZPoint toGlobal( const ROOT::Math::XYZPoint& p ) const { + return ROOT::Math::XYZPoint( this->detectorAlignment.localToWorld( ROOT::Math::XYZVector( p ) ) ); + } + ROOT::Math::XYZVector toGlobal( const ROOT::Math::XYZVector& v ) const { + return this->detectorAlignment.localToWorld( v ); + } }; } // End namespace detail - using DeFTLayer = DeIOVElement; + template + struct DeFTLayerElement : DeIOVElement { + using DeIOVElement::DeIOVElement; + unsigned int layerID() const { return this->access()->m_layerID; } + + /** Find the FT Quarter corresponding to the point + * @return Pointer to the relevant quarter + */ + [[nodiscard]] const std::optional findQuarter( const ROOT::Math::XYZPoint& aPoint ) const { + const auto iQ = std::find_if( + this->access()->m_quarters.begin(), this->access()->m_quarters.end(), + [&aPoint]( const detail::DeFTQuarterObject& q ) { return DeFTQuarter{&q}.isInside( aPoint ); } ); + return iQ != this->access()->m_quarters.end() ? iQ : std::optional{}; // DeFTQuarter{}; + } + + /// Find the module for a given XYZ point + [[nodiscard]] const std::optional findModule( const ROOT::Math::XYZPoint& aPoint ) const { + const auto iQ = findQuarter( aPoint ); + return iQ ? iQ->findModule( aPoint ) : std::optional{}; // DeFTModule{}; + } + + /** Const method to return the layer for a given channel id + * @param aChannel an FT channel id + * @return pointer to detector element + */ + [[nodiscard]] const std::optional findQuarter( const FTChannelID& id ) const { + return ( to_unsigned( id.quarter() ) < this->access()->m_quarters.size() ) + ? &( this->access()->m_quarters[to_unsigned( id.quarter() )] ) + : std::optional{}; + } + + float globalZ() const { return this->access()->m_globalZ; } // FIXME + ROOT::Math::Plane3D plane() const { return this->access()->m_plane; } // FIXME + float dxdy() const { return this->access()->m_dxdy; } // FIXME + float stereoAngle() const { return this->access()->m_stereoAngle; } // FIXME + float dzdy() const { return this->access()->m_dzdy; } // FIXME + float sizeX() const { return this->access()->m_sizeX; } // FIXME + float sizeY() const { return this->access()->m_sizeY; } // FIXME + }; + + using DeFTLayer = DeFTLayerElement; } // End namespace LHCb::Detector diff --git a/Detector/FT/include/Detector/FT/DeFTMat.h b/Detector/FT/include/Detector/FT/DeFTMat.h index 28f93d2cd29afdae7b0c50b3795884b7d7b3967d..d23f63faf108603059aca46c7205937cfc0e1e69 100644 --- a/Detector/FT/include/Detector/FT/DeFTMat.h +++ b/Detector/FT/include/Detector/FT/DeFTMat.h @@ -11,6 +11,8 @@ #pragma once #include "Core/DeIOV.h" +#include "Core/LineTraj.h" +#include "Detector/FT/FTChannelID.h" #include "Detector/FT/FTConstants.h" #include @@ -26,17 +28,210 @@ namespace LHCb::Detector { * \version 1.0 */ struct DeFTMatObject : DeIOVObject { - double airGap{}; - double deadRegion{}; - double channelPitch{}; - double dieGap{}; - int nChannelsInSiPM{}; - int nSiPMsInMat{}; - int nDiesInSiPM{}; - using DeIOVObject::DeIOVObject; + + FTChannelID m_elementID; ///< element ID + unsigned int m_matID; // FIXME: needed? + // dd4hep::_toDouble + int m_nChannelsInSiPM = {dd4hep::_toInt( "FT::nChannelsInSiPM" )}; ///< number of channels per SiPM // FIXME: it + ///< is constant + int m_nChannelsInDie; ///< number of channels per die + int m_nSiPMsInMat = FT::nSiPM; ///< number of SiPM arrays per mat // FIXME: it is constant + int m_nDiesInSiPM = FT::nChannels; ///< number of dies per SiPM + + ROOT::Math::Plane3D m_plane; ///< xy-plane in the z-middle of the module + ROOT::Math::XYZPointF m_sipmPoint; ///< Location of end of fibres at x=z=0 + float m_globalZ; ///< Global z position of module closest to y-axis + float m_airGap = {dd4hep::_toFloat( "FT::airGap" )}; ///< air gap + float m_deadRegion = {dd4hep::_toFloat( "FT::deadRegion" )}; ///< dead region + float m_channelPitch = {dd4hep::_toFloat( "FT::channelPitch" )}; ///< readout channel pitch (250 micron) + float m_diePitch; ///< pitch between dies in SiPM + float m_sizeX = dd4hep::_toFloat( "FT:MatSizeX" ); ///< Width in x of the mat + float m_sizeY = dd4hep::_toFloat( "FT:MatSizeY" ); ///< Length in y of the fibre in the mat + float m_sizeZ = dd4hep::_toFloat( "FT:MatSizeZ" ); ///< Thickness of the fibre mat (nominal: 1.3 mm) + + // Parameters needed for decoding + ROOT::Math::XYZPointF m_mirrorPoint; ///< Location of end of fibres at x=z=0 + ROOT::Math::XYZVectorF m_ddx; ///< Global direction vector for a local displacement in unit x + float m_uBegin; ///< start in local u-coordinate of sensitive SiPM + float m_halfChannelPitch; ///< half of the readout channel pitch (125 micron) + float m_dieGap = {dd4hep::_toFloat( "FT::dieGap" )}; ///< gap between channel 63 and 64 + float m_sipmPitch; ///< pitch between SiPMs in mat + float m_dxdy; ///< Global slope dx/dy for a fibre mat + float m_dzdy; ///< Global slope dz/dy for a fibre mat + float m_globaldy; ///< Length of a fibre projected along global y + + DeFTMatObject( const dd4hep::DetElement& de, dd4hep::cond::ConditionUpdateContext& ctxt, unsigned int iMat, + unsigned int iModule ); + ROOT::Math::XYZPoint toLocal( const ROOT::Math::XYZPoint& p ) const { + return ROOT::Math::XYZPoint( + toLHCbUnits( this->detectorAlignment.worldToLocal( toDD4hepUnits( ROOT::Math::XYZVector( p ) ) ) ) ); + } + ROOT::Math::XYZVector toLocal( const ROOT::Math::XYZVector& v ) const { + return toLHCbUnits( this->detectorAlignment.worldToLocal( toDD4hepUnits( v ) ) ); + } + ROOT::Math::XYZPoint toGlobal( const ROOT::Math::XYZPoint& p ) const { + return ROOT::Math::XYZPoint( this->detectorAlignment.localToWorld( ROOT::Math::XYZVector( p ) ) ); + } + ROOT::Math::XYZVector toGlobal( const ROOT::Math::XYZVector& v ) const { + return this->detectorAlignment.localToWorld( v ); + } }; } // End namespace detail + template + struct DeFTMatElement : DeIOVElement { + using DeIOVElement::DeIOVElement; + + // Getters + FTChannelID elementID() const { return this->access()->m_elementID; } + float airGap() const { return this->access()->m_airGap; } + float deadRegion() const { return this->access()->m_deadRegion; } + float channelPitch() const { return this->access()->m_channelPitch; } + int sensitiveVolumeID( const ROOT::Math::XYZPoint& ) const { return this->access()->m_elementID; } + float sipmPitch() const { return this->access()->m_sipmPitch; }; + float halfChannelPitch() const { return this->access()->m_halfChannelPitch; }; + float dieGap() const { return this->access()->m_dieGap; } + ROOT::Math::XYZPointF mirrorPoint() const { return this->access()->m_mirrorPoint; } + ROOT::Math::XYZVectorF ddx() const { return this->access()->m_ddx; } + float dxdy() const { return this->access()->m_dxdy; } + float dzdy() const { return this->access()->m_dzdy; } + float globaldy() const { return this->access()->m_globaldy; } + float globalZ() const { return this->access()->m_globalZ; } + float uBegin() const { return this->access()->m_uBegin; } + /** Returns the width of the fibre mat */ + [[nodiscard]] float fibreMatWidth() const { return this->access()->m_sizeX; } + + /** Get the length of the fibre in this mat */ + [[nodiscard]] float fibreLength() const { return this->access()->m_sizeY; } + + /** Returns the thickness of the fibre mat (1.3 mm) */ + [[nodiscard]] float fibreMatThickness() const { return this->access()->m_sizeZ; } + + // Element ID operations + FTChannelID::StationID stationID() const { return this->elementID().station(); } + FTChannelID::LayerID layerID() const { return this->elementID().layer(); } + FTChannelID::QuarterID quarterID() const { return this->elementID().quarter(); } + FTChannelID::ModuleID moduleID() const { return this->elementID().module(); } + FTChannelID::MatID matID() const { return FTChannelID::MatID{this->access()->m_matID}; } + bool isBottom() const { return this->access()->m_elementID.isBottom(); } + bool isTop() const { return this->access()->m_elementID.isTop(); } + bool hasGapLeft( const FTChannelID thisChannel ) const { + return ( thisChannel.channel() == 0u || int( thisChannel.channel() ) == this->access()->m_nChannelsInDie ); + } + bool hasGapRight( const FTChannelID thisChannel ) const { + return ( int( thisChannel.channel() ) == this->access()->m_nChannelsInSiPM - 1 || + int( thisChannel.channel() ) == this->access()->m_nChannelsInDie - 1 ); + } + + // Geometrical operations + ROOT::Math::XYZPoint toLocal( const ROOT::Math::XYZPoint& p ) const { return this->access()->toLocal( p ); } + ROOT::Math::XYZVector toLocal( const ROOT::Math::XYZVector& v ) const { return this->access()->toLocal( v ); } + ROOT::Math::XYZPoint toGlobal( const ROOT::Math::XYZPoint& p ) const { return this->access()->toGlobal( p ); } + ROOT::Math::XYZVector toGlobal( const ROOT::Math::XYZVector& v ) const { return this->access()->toGlobal( v ); } + LineTraj trajectory( FTChannelID channelID, float frac ) const { + float localX = localXfromChannel( channelID, frac ); + auto obj = this->access(); + ROOT::Math::XYZPoint mirrorPoint( obj->m_mirrorPoint.x() + localX * obj->m_ddx.x(), + obj->m_mirrorPoint.y() + localX * obj->m_ddx.y(), + obj->m_mirrorPoint.z() + localX * obj->m_ddx.z() ); + ROOT::Math::XYZPoint sipmPoint( obj->m_sipmPoint.x() + localX * obj->m_ddx.x(), + obj->m_sipmPoint.y() + localX * obj->m_ddx.y(), + obj->m_sipmPoint.z() + localX * obj->m_ddx.z() ); + return {mirrorPoint, sipmPoint}; + } + + // Others + FTChannelID calculateChannelAndFrac( float x, float frac ) const { + const auto& obj = this->access(); + // Correct for the starting point of the sensitive area + float xInMat = x - obj->m_uBegin; + + // Find the sipm that is hit and the local position within the sipm + int hitSiPM = std::clamp( int( xInMat / obj->m_sipmPitch ), 0, obj->m_nSiPMsInMat - 1 ); + float xInSiPM = fma( -obj->m_sipmPitch, hitSiPM, xInMat ); + + // Find the die that is hit and the local position within the die + int hitDie = std::clamp( int( xInSiPM / obj->m_diePitch ), 0, obj->m_nDiesInSiPM - 1 ); + float chanInDie = fma( -obj->m_diePitch, hitDie, xInSiPM ) / obj->m_channelPitch; + + // Find the channel that is hit and the local position within the channel + int hitChan = std::clamp( int( chanInDie ), 0, obj->m_nChannelsInDie - 1 ); + frac = chanInDie - hitChan - 0.5f; + // Construct channelID + return FTChannelID( stationID(), layerID(), quarterID(), moduleID(), matID(), hitSiPM, + hitChan + ( hitDie * obj->m_nChannelsInDie ) ); + } + [[nodiscard]] std::vector> calculateChannels( FTChannelID thisChannel, + FTChannelID endChannel ) const { + const auto& obj = this->access(); + // Reserve memory + std::vector> channelsAndLeftEdges; + channelsAndLeftEdges.reserve( endChannel - thisChannel ); + + // Loop over the intermediate channels + bool keepAdding = true; + while ( keepAdding ) { + float channelLeftEdge = localXfromChannel( thisChannel, -0.5f ); + // Add channel and left edge to output vector. + channelsAndLeftEdges.emplace_back( thisChannel, channelLeftEdge ); + if ( thisChannel == endChannel ) keepAdding = false; + thisChannel.advance(); + } + return channelsAndLeftEdges; + } + + /** Get the list of SiPM channels traversed by the hit. + * The particle trajectory is a straight line defined by: + * @param provide local entry and exit point + * @param provide the number of additional channels to add + * Fills a vector of FTChannelIDs, and a vector of the + * corresponding left edges (along x) in the local frame. + */ + [[nodiscard]] std::vector> + calculateChannels( const float localEntry, const float localExit, + const unsigned int numOfAdditionalChannels ) const { + // set ordering in increasing local x + float xBegin = std::min( localEntry, localExit ); + float xEnd = std::max( localEntry, localExit ); + + // Find the first and last channels that are involved + float xOffset = numOfAdditionalChannels * this->access()->m_channelPitch; + float fracBegin = 0.0, fracEnd = 0.0; + FTChannelID thisChannel = calculateChannelAndFrac( xBegin - xOffset, fracBegin ); + FTChannelID endChannel = calculateChannelAndFrac( xEnd + xOffset, fracEnd ); + + // return empty vector when both channels are the same gap + if ( thisChannel.channelID() == endChannel.channelID() && std::abs( fracBegin ) > 0.5f && + std::abs( fracEnd ) > 0.5f && fracBegin * fracEnd > 0.25f ) + return std::vector>(); + + return this->calculateChannels( thisChannel, endChannel ); + } + + /** Get the local x from a channelID and its fraction */ + [[nodiscard]] float localXfromChannel( const FTChannelID channelID, const int frac ) const { + const auto& obj = this->access(); + float uFromChannel = obj->m_uBegin + ( 2 * channelID.channel() + 1 + frac ) * obj->m_halfChannelPitch; + if ( channelID.die() ) uFromChannel += obj->m_dieGap; + uFromChannel += channelID.sipm() * obj->m_sipmPitch; + return uFromChannel; + } + + // Get the distance between a 3D global point and a channel+fraction + float distancePointToChannel( const ROOT::Math::XYZPoint& globalPoint, const FTChannelID channelID, + const float frac ) const { + ROOT::Math::XYZPoint localPoint = toLocal( globalPoint ); + return localXfromChannel( channelID, frac ) - localPoint.x(); + } + + /** Get the distance from the hit to the SiPM + * @param localPoint is the position of the half module in local coordinates + * @return the distance to the SiPM + */ + [[nodiscard]] float distanceToSiPM( const ROOT::Math::XYZPoint& localPoint ) const { + return 0.5f * this->access()->m_sizeY - localPoint.y(); + }; + }; - using DeFTMat = DeIOVElement; + using DeFTMat = DeFTMatElement; } // End namespace LHCb::Detector diff --git a/Detector/FT/include/Detector/FT/DeFTModule.h b/Detector/FT/include/Detector/FT/DeFTModule.h index 3f0c416c12049457300771f273feba5dbb001e5c..e6fe442bba765c4e8dc794bbd85377800f472b4c 100644 --- a/Detector/FT/include/Detector/FT/DeFTModule.h +++ b/Detector/FT/include/Detector/FT/DeFTModule.h @@ -27,11 +27,74 @@ namespace LHCb::Detector { */ struct DeFTModuleObject : DeIOVObject { /// Reference to the static information of mats - std::array mats; - DeFTModuleObject( const dd4hep::DetElement& de, dd4hep::cond::ConditionUpdateContext& ctxt ); + unsigned int m_id; + int m_nChannelsInModule{dd4hep::_toInt( "FT:nChannelsInModule" )}; + std::array m_mats; + ROOT::Math::Plane3D m_plane; ///< xy-plane in the z-middle of the module + bool m_reversed; + FTChannelID m_elementID; + DeFTModuleObject( const dd4hep::DetElement& de, dd4hep::cond::ConditionUpdateContext& ctxt, unsigned int iModule, + unsigned int quarterID ); + /// Convert local position to global position + ROOT::Math::XYZPoint toGlobal( const ROOT::Math::XYZPoint& point ) const { + return ROOT::Math::XYZPoint( this->detectorAlignment.localToWorld( ROOT::Math::XYZVector( point ) ) ); + } + void applyToAllChildren( const std::function& func ) const override { + for ( auto& mat : m_mats ) { func( LHCb::Detector::DeIOV{&mat} ); }; + }; + FTChannelID::StationID stationID() const { return m_elementID.station(); } + FTChannelID::ModuleID moduleID() const { return FTChannelID::ModuleID{m_id}; } + + /// Get the pseudo-channel for a FTChannelID (useful in the monitoring) + int pseudoChannel( const FTChannelID channelID ) const { + int channelInModule = channelID.channelID() & ( m_nChannelsInModule - 1u ); + if ( m_reversed ) { channelInModule = m_nChannelsInModule - 1 - channelInModule; } + return channelInModule + to_unsigned( moduleID() ) * m_nChannelsInModule; + } + + FTChannelID channelFromPseudo( const int pseudoChannel ) const { + int channelInModule = pseudoChannel & ( m_nChannelsInModule - 1u ); + if ( m_reversed ) { channelInModule = m_nChannelsInModule - 1 - channelInModule; } + return FTChannelID( m_elementID + channelInModule ); + } }; } // End namespace detail + template + struct DeFTModuleElement : DeIOVElement { + using DeIOVElement::DeIOVElement; + FTChannelID::ModuleID moduleID() const { return this->access()->moduleID(); } + FTChannelID::StationID stationID() const { return this->access()->stationID(); } + + /// Find the layer for a given XYZ point + const std::optional findMat( const ROOT::Math::XYZPoint& aPoint ) const { + /// Find the layer and return a pointer to the layer from XYZ point + const auto iter = + std::find_if( this->access()->m_mats.begin(), this->access()->m_mats.end(), + [&aPoint]( const detail::DeFTMatObject& m ) { return DeFTMat{&m}.isInside( aPoint ); } ); + return iter != this->access()->m_mats.end() ? iter : std::optional{}; // DeFTMat{}; + } + + const std::optional findMat( const FTChannelID& id ) const { + return ( to_unsigned( id.mat() ) < this->access()->m_mats.size() ) + ? &( this->access()->m_mats[to_unsigned( id.mat() )] ) + : std::optional{}; + } + + [[nodiscard]] FTChannelID elementID() const { return this->access()->m_elementID; } + + [[nodiscard]] int pseudoChannel( const FTChannelID channelID ) const { + return this->access()->pseudoChannel( channelID ); + } + + [[nodiscard]] FTChannelID channelFromPseudo( const int pseudoChannel ) const { + const auto& obj = this->access(); + int channelInModule = pseudoChannel & ( obj->m_nChannelsInModule - 1u ); + if ( obj->m_reversed ) { channelInModule = obj->m_nChannelsInModule - 1 - channelInModule; } + return FTChannelID( this->elementID() + channelInModule ); + } + ROOT::Math::Plane3D plane() const { return this->access()->m_plane; } + }; - using DeFTModule = DeIOVElement; + using DeFTModule = DeFTModuleElement; } // End namespace LHCb::Detector diff --git a/Detector/FT/include/Detector/FT/DeFTQuarter.h b/Detector/FT/include/Detector/FT/DeFTQuarter.h index cc14408ddb2ecdea6275c18cb34537e122492c01..17352989fd9c6168ab3c106918977324c4108a78 100644 --- a/Detector/FT/include/Detector/FT/DeFTQuarter.h +++ b/Detector/FT/include/Detector/FT/DeFTQuarter.h @@ -1,12 +1,12 @@ /*****************************************************************************\ -* (c) Copyright 2000-2019 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. * + * (c) Copyright 2000-2019 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. * \*****************************************************************************/ #pragma once @@ -25,11 +25,46 @@ namespace LHCb::Detector { */ struct DeFTQuarterObject : DeIOVObject { /// Reference to the static information of modules - std::array modules; - DeFTQuarterObject( const dd4hep::DetElement& de, dd4hep::cond::ConditionUpdateContext& ctxt ); + unsigned int m_id; + std::array m_modules; + DeFTQuarterObject( const dd4hep::DetElement& de, dd4hep::cond::ConditionUpdateContext& ctxt, + unsigned int iQuarter, unsigned int layerID ); + void applyToAllChildren( const std::function& func ) const override { + for ( auto& module : m_modules ) { func( LHCb::Detector::DeIOV{&module} ); }; + }; }; } // End namespace detail - using DeFTQuarter = DeIOVElement; + template + struct DeFTQuarterElement : DeIOVElement { + using DeIOVElement::DeIOVElement; + unsigned int quarterID() const { return this->access()->m_id; } + + /** Find the FT Module corresponding to the point + * @return Pointer to the relevant module + */ + [[nodiscard]] const std::optional findModule( const ROOT::Math::XYZPoint& aPoint ) const { + auto iM = + std::find_if( this->access()->m_modules.begin(), this->access()->m_modules.end(), + [&aPoint]( const detail::DeFTModuleObject& m ) { return DeFTModule{&m}.isInside( aPoint ); } ); + return iM != this->access()->m_modules.end() ? iM : std::optional{}; // DeFTModule{}; + } + /** Const method to return the module for a given channel id + * @param aChannel an FT channel id + * @return pointer to detector element + */ + [[nodiscard]] const std::optional findModule( const FTChannelID& id ) const { + return ( to_unsigned( id.module() ) < this->access()->m_modules.size() ) + ? &( this->access()->m_modules[to_unsigned( id.mat() )] ) + : std::optional{}; + } + + /** Flat vector of all FT modules + * @return vector of modules + */ + [[nodiscard]] const auto& modules() const { return this->access()->m_modules; } + }; + + using DeFTQuarter = DeFTQuarterElement; } // End namespace LHCb::Detector diff --git a/Detector/FT/include/Detector/FT/DeFTStation.h b/Detector/FT/include/Detector/FT/DeFTStation.h index c1ca656b437bc3fe415e75ae47f437e687f22541..174543ac178d9aeeb8657f46960131f72f1010c1 100644 --- a/Detector/FT/include/Detector/FT/DeFTStation.h +++ b/Detector/FT/include/Detector/FT/DeFTStation.h @@ -1,12 +1,12 @@ /*****************************************************************************\ -* (c) Copyright 2000-2019 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. * + * (c) Copyright 2000-2019 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. * \*****************************************************************************/ #pragma once @@ -25,11 +25,42 @@ namespace LHCb::Detector { */ struct DeFTStationObject : DeIOVObject { /// Reference to the static information of the layers - std::array layers; - DeFTStationObject( const dd4hep::DetElement& de, dd4hep::cond::ConditionUpdateContext& ctxt ); + unsigned int m_id; + std::array m_layers; + DeFTStationObject( const dd4hep::DetElement& de, dd4hep::cond::ConditionUpdateContext& ctxt, + unsigned int iStation ); + void applyToAllChildren( const std::function& func ) const override { + for ( auto& layer : m_layers ) { func( LHCb::Detector::DeIOV{&layer} ); }; + }; }; } // End namespace detail - using DeFTStation = DeIOVElement; + template + struct DeFTStationElement : DeIOVElement { + using DeIOVElement::DeIOVElement; + unsigned int stationID() const { return this->access()->m_id; } + + /** Find the FT Layer corresponding to the point + * @return Pointer to the relevant layer + */ + [[nodiscard]] const std::optional findLayer( const ROOT::Math::XYZPoint& aPoint ) const { + auto iter = + std::find_if( this->access()->m_layers.begin(), this->access()->m_layers.end(), + [&aPoint]( const detail::DeFTLayerObject& l ) { return DeFTLayer{&l}.isInside( aPoint ); } ); + return iter != this->access()->m_layers.end() ? iter : std::optional{}; // DeFTLayer{}; + } + + /** Const method to return the layer for a given channel id + * @param aChannel an FT channel id + * @return pointer to detector element + */ + [[nodiscard]] const std::optional findLayer( const FTChannelID& id ) const { + return ( to_unsigned( id.layer() ) < this->access()->m_layers.size() ) + ? &( this->access()->m_layers[to_unsigned( id.layer() )] ) + : std::optional{}; + } + }; + + using DeFTStation = DeFTStationElement; } // End namespace LHCb::Detector diff --git a/Detector/FT/include/Detector/FT/FTChannelID.h b/Detector/FT/include/Detector/FT/FTChannelID.h new file mode 100644 index 0000000000000000000000000000000000000000..696a0e536f9ae49062f9c7f9f1f3a1007e6ab41e --- /dev/null +++ b/Detector/FT/include/Detector/FT/FTChannelID.h @@ -0,0 +1,234 @@ +/*****************************************************************************\ +* (c) Copyright 2000-2019 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. * +\*****************************************************************************/ +#pragma once +#include +#include + +namespace LHCb::Detector { + + /** @class FTChannelID FTChannelID.h + * + * Channel ID for the Fibre Tracker (LHCb Upgrade) + * + * @author FT software team + * + */ + + class FTChannelID final { + + /// Bitmasks for bitfield channelID + enum struct Mask : unsigned { + channel = 0x7f, + sipm = 0x180, + mat = 0x600, + module = 0x3800, + quarter = 0xc000, + layer = 0x30000, + station = 0xc0000, + uniqueLayer = layer | station, + uniqueQuarter = quarter | uniqueLayer, + uniqueModule = module | uniqueQuarter, + uniqueMat = mat | uniqueModule, + uniqueSiPM = sipm | uniqueMat, + die = 0x40, + sipmInModule = mat | sipm, + channelInModule = channel | sipmInModule + }; + + template + [[nodiscard]] static constexpr unsigned int extract( unsigned int i ) { + constexpr auto b = + __builtin_ctz( static_cast( m ) ); // FIXME: C++20 replace __builtin_ctz with std::countr_zero + return ( i & static_cast( m ) ) >> b; + } + + template + [[nodiscard]] static constexpr unsigned int shift( unsigned int i ) { + constexpr auto b = + __builtin_ctz( static_cast( m ) ); // FIXME: C++20 replace __builtin_ctz with std::countr_zero + auto v = ( i << static_cast( b ) ); + assert( extract( v ) == i ); + return v; + } + + template + [[nodiscard]] static constexpr unsigned int shift( T i ) { + return shift( to_unsigned( i ) ); + } + + public: + enum struct StationID : unsigned int {}; + [[nodiscard]] friend constexpr unsigned int to_unsigned( StationID id ) { return static_cast( id ); } + + enum struct LayerID : unsigned int {}; + [[nodiscard]] friend constexpr unsigned int to_unsigned( LayerID id ) { return static_cast( id ); } + [[nodiscard]] friend constexpr bool is_X( LayerID id ) { return id == LayerID{0} || id == LayerID{3}; } + + enum struct QuarterID : unsigned int {}; + [[nodiscard]] friend constexpr unsigned int to_unsigned( QuarterID id ) { return static_cast( id ); } + [[nodiscard]] friend constexpr bool is_bottom( QuarterID id ) { return id == QuarterID{0} || id == QuarterID{1}; } + [[nodiscard]] friend constexpr bool is_top( QuarterID id ) { return id == QuarterID{2} || id == QuarterID{3}; } + + enum struct ModuleID : unsigned int {}; + [[nodiscard]] friend constexpr unsigned int to_unsigned( ModuleID id ) { return static_cast( id ); } + + enum struct MatID : unsigned int {}; + [[nodiscard]] friend constexpr unsigned int to_unsigned( MatID id ) { return static_cast( id ); } + + /// Default Constructor + constexpr FTChannelID() : FTChannelID{kInvalidChannel()} {}; + + /// Partial constructor using the unique sipm and the channel + constexpr FTChannelID( unsigned int uniqueSiPM, unsigned int channel ) + : m_channelID{shift( uniqueSiPM ) | shift( channel )} {}; + + /// Constructor from int + constexpr explicit FTChannelID( unsigned int id ) : m_channelID{id} {} + + /// Explicit constructor from the geometrical location + constexpr FTChannelID( StationID station, LayerID layer, QuarterID quarter, ModuleID module, MatID mat, + unsigned int sipm, unsigned int channel ) + : FTChannelID{shift( station ) | shift( layer ) | shift( quarter ) | + shift( module ) | shift( mat ) | shift( sipm ) | + shift( channel )} {} + + /// Explicit constructor from the geometrical location + constexpr FTChannelID( StationID station, LayerID layer, QuarterID quarter, ModuleID module, + unsigned int channelInModule ) + : FTChannelID{shift( station ) | shift( layer ) | shift( quarter ) | + shift( module ) | shift( channelInModule )} {} + + /// Operator overload, to cast channel ID to unsigned int. Used by linkers where the key (channel id) is an int + constexpr operator unsigned int() const { return m_channelID; } + + /// Comparison equality + constexpr friend bool operator==( FTChannelID lhs, FTChannelID rhs ) { return lhs.channelID() == rhs.channelID(); } + + /// Comparison < + constexpr friend bool operator<( FTChannelID lhs, FTChannelID rhs ) { return lhs.channelID() < rhs.channelID(); } + + /// Comparison > + constexpr friend bool operator>( FTChannelID lhs, FTChannelID rhs ) { return rhs < lhs; } + + /// Useful for decoding: maximum number of SiPMs + static unsigned int maxNumberOfSiPMs(); + + // FTChannelID with channelID set to kInvalid bits + static constexpr FTChannelID kInvalidChannel() { + return {StationID{0u}, LayerID{0u}, QuarterID{0u}, ModuleID{7u}, MatID{0u}, 0, 0}; + }; + + // FTChannelID with channelID set to kInvalid bits + static constexpr unsigned int kInvalidChannelID() { return kInvalidChannel().channelID(); }; + + /// Increment the channelID + constexpr FTChannelID& advance() { + ++m_channelID; + return *this; + } + + /// Return the SiPM number within the module (0-15) + [[nodiscard]] constexpr unsigned int sipmInModule() const { return extract( m_channelID ); } + + /// Return the die number (0 or 1) + [[nodiscard]] constexpr unsigned int die() const { return extract( m_channelID ); } + + /// Return true if channelID is in x-layer + [[nodiscard]] constexpr bool isX() const { return is_X( layer() ); } + + /// Return true if channelID is in bottom part of detector + [[nodiscard]] constexpr bool isBottom() const { return is_bottom( quarter() ); } + + /// Return true if channelID is in top part of detector + [[nodiscard]] constexpr bool isTop() const { return is_top( quarter() ); } + + /// Retrieve const FT Channel ID + [[nodiscard]] constexpr unsigned int channelID() const { return m_channelID; } + + /// Retrieve Channel in the 128 channel SiPM + [[nodiscard]] constexpr unsigned int channel() const { return extract( m_channelID ); } + + /// Retrieve ID of the SiPM in the mat + [[nodiscard]] constexpr unsigned int sipm() const { return extract( m_channelID ); } + + /// Retrieve ID of the mat in the module + [[nodiscard]] constexpr MatID mat() const { return MatID{extract( m_channelID )}; } + + /// Retrieve Module id (0 - 5 or 0 - 6) + [[nodiscard]] constexpr ModuleID module() const { return ModuleID{extract( m_channelID )}; } + + /// Retrieve Quarter ID (0 - 3) + [[nodiscard]] constexpr QuarterID quarter() const { return QuarterID{extract( m_channelID )}; } + + /// Retrieve Layer id + [[nodiscard]] constexpr LayerID layer() const { return LayerID{extract( m_channelID )}; } + + /// Retrieve Station id + [[nodiscard]] constexpr StationID station() const { return StationID{extract( m_channelID )}; } + + /// Retrieve unique layer + [[nodiscard]] constexpr unsigned int uniqueLayer() const { return extract( m_channelID ); } + + /// Retrieve unique quarter + [[nodiscard]] constexpr unsigned int uniqueQuarter() const { return extract( m_channelID ); } + + /// Retrieve unique module + [[nodiscard]] constexpr unsigned int uniqueModule() const { return extract( m_channelID ); } + + /// Retrieve unique mat + [[nodiscard]] constexpr unsigned int uniqueMat() const { return extract( m_channelID ); } + + /// Retrieve unique SiPM + [[nodiscard]] constexpr unsigned int uniqueSiPM() const { return extract( m_channelID ); } + + /// Retrieve moduleID for monitoring + [[nodiscard]] constexpr unsigned int moniModuleID() const { + return 6 * ( 4 * ( 4 * ( to_unsigned( station() ) - 1 ) + to_unsigned( layer() ) ) + to_unsigned( quarter() ) ) + + to_unsigned( module() ); + } + + /// Retrieve moduleID unique per station for monitoring + [[nodiscard]] constexpr unsigned int moniModuleIDstation() const { + return to_unsigned( module() ) + 4 * to_unsigned( quarter() ) + 16 * to_unsigned( layer() ); + } + + /// Retrieve quarterID for monitoring + [[nodiscard]] constexpr unsigned int moniQuarterID() const { + return 4 * to_unsigned( layer() ) + to_unsigned( quarter() ) + 16 * ( to_unsigned( station() ) - 1 ); + } + + /// Retrieve SiPMID for monitoring + [[nodiscard]] constexpr unsigned int moniSiPMID() const { + return sipm() + 4 * to_unsigned( mat() ) + 20 * to_unsigned( module() ); + } + + /// Retrieve channelID for monitoring + [[nodiscard]] constexpr unsigned int moniChannelID() const { + return to_unsigned( mat() ) + 4 * sipm() + 16 * channel(); + } + + friend std::ostream& operator<<( std::ostream& s, const FTChannelID& obj ) { + return s << "{ FTChannelID : " + << " channel =" << obj.channel() << " sipm =" << obj.sipm() << " mat =" << to_unsigned( obj.mat() ) + << " module=" << to_unsigned( obj.module() ) << " quarter=" << to_unsigned( obj.quarter() ) + << " layer=" << to_unsigned( obj.layer() ) << " station=" << to_unsigned( obj.station() ) << " }"; + } + + private: + unsigned int m_channelID{0}; ///< FT Channel ID + + }; // class FTChannelID + +} // namespace LHCb::Detector + +// ----------------------------------------------------------------------------- +// end of class +// ----------------------------------------------------------------------------- diff --git a/Detector/FT/include/Detector/FT/FTConstants.h b/Detector/FT/include/Detector/FT/FTConstants.h index 04aa0798112202b4c6cdd20b7b728da30c5c7128..c53e55cc98afff2cd3e22a3481cf689ad0a13bc1 100644 --- a/Detector/FT/include/Detector/FT/FTConstants.h +++ b/Detector/FT/include/Detector/FT/FTConstants.h @@ -20,6 +20,13 @@ namespace LHCb::Detector { MODULE = 1 << 4, MAT = 1 << 5 }; - } + constexpr unsigned int nStations = 3; + constexpr unsigned int nLayers = 4; + constexpr unsigned int nQuarters = 4; + constexpr unsigned int nModules = 6; + constexpr unsigned int nMats = 4; + constexpr unsigned int nSiPM = 4; + constexpr unsigned int nChannels = 128; + } // namespace FT } // End namespace LHCb::Detector diff --git a/Detector/FT/src/DeFT.cpp b/Detector/FT/src/DeFT.cpp index 31ecf941018a8088f14f8c56b866ef14114c22df..d9243215f2a1cdb5f7bfdafd5ba949bf6f6bafc1 100644 --- a/Detector/FT/src/DeFT.cpp +++ b/Detector/FT/src/DeFT.cpp @@ -11,40 +11,133 @@ // Framework include files #include "Detector/FT/DeFT.h" +#include "Detector/FT/FTChannelID.h" + #include "DD4hep/Printout.h" +// FIXME: ROOT vs LHCB lengths + LHCb::Detector::detail::DeFTObject::DeFTObject( const dd4hep::DetElement& de, dd4hep::cond::ConditionUpdateContext& ctxt ) : DeIOVObject( de, ctxt ) - , stations{{{de.child( "T1" ), ctxt}, {de.child( "T2" ), ctxt}, {de.child( "T3" ), ctxt}}} {} + , m_stations{{{de.child( "T1" ), ctxt, 1}, {de.child( "T2" ), ctxt, 2}, {de.child( "T3" ), ctxt, 3}}} {} LHCb::Detector::detail::DeFTStationObject::DeFTStationObject( const dd4hep::DetElement& de, - dd4hep::cond::ConditionUpdateContext& ctxt ) + dd4hep::cond::ConditionUpdateContext& ctxt, + unsigned int iStation ) : DeIOVObject( de, ctxt ) - , layers{{{de.child( "X1" ), ctxt}, {de.child( "U" ), ctxt}, {de.child( "V" ), ctxt}, {de.child( "X2" ), ctxt}}} {} + , m_id{iStation} + , m_layers{{{de.child( "X1" ), ctxt, 0, m_id}, + {de.child( "U" ), ctxt, 1, m_id}, + {de.child( "V" ), ctxt, 2, m_id}, + {de.child( "X2" ), ctxt, 3, m_id}}} {} LHCb::Detector::detail::DeFTLayerObject::DeFTLayerObject( const dd4hep::DetElement& de, - dd4hep::cond::ConditionUpdateContext& ctxt ) + dd4hep::cond::ConditionUpdateContext& ctxt, + unsigned int iLayer, unsigned int stationID ) : DeIOVObject( de, ctxt ) - , quarters{ - {{de.child( "Q0" ), ctxt}, {de.child( "Q1" ), ctxt}, {de.child( "Q2" ), ctxt}, {de.child( "Q3" ), ctxt}}} {} + , m_layerID{( stationID * FT::nLayers + iLayer )} + , m_quarters{{{de.child( "Q0" ), ctxt, 0, m_layerID}, + {de.child( "Q1" ), ctxt, 1, m_layerID}, + {de.child( "Q2" ), ctxt, 2, m_layerID}, + {de.child( "Q3" ), ctxt, 3, m_layerID}}} { + // Get the global z position of the layer + ROOT::Math::XYZPoint globalPoint = this->toGlobal( ROOT::Math::XYZPoint( 0., 0., 0. ) ); + m_globalZ = globalPoint.z(); + + // Get the boundaries of the layer + // FIXME + // const dd4hep::Box* box = dynamic_cast( geometry()->lvolume()->solid()->coverTop() ); + // m_sizeX = box->xsize(); + // m_sizeY = box->ysize(); + + // Make the plane for the layer + const ROOT::Math::XYZPoint g1 = this->toGlobal( ROOT::Math::XYZPoint( 0., 0., 0. ) ); + const ROOT::Math::XYZPoint g2 = this->toGlobal( ROOT::Math::XYZPoint( 1., 0., 0. ) ); + const ROOT::Math::XYZPoint g3 = this->toGlobal( ROOT::Math::XYZPoint( 0., 1., 0. ) ); + m_plane = ROOT::Math::Plane3D( g1, g2, g3 ); + m_dzdy = ( g3.z() - g1.z() ) / ( g3.y() - g1.y() ); + // m_dxdy = -tan( m_stereoAngle ); + m_dxdy = ( g3.x() - g1.x() ) / ( g3.y() - g1.y() ); // FIXME: to check + m_stereoAngle = -atan( m_dxdy ); +} LHCb::Detector::detail::DeFTQuarterObject::DeFTQuarterObject( const dd4hep::DetElement& de, - dd4hep::cond::ConditionUpdateContext& ctxt ) + dd4hep::cond::ConditionUpdateContext& ctxt, + unsigned int iQuarter, unsigned int layerID ) : DeIOVObject( de, ctxt ) - , - // XXX no always 6 modules ? - modules{{{de.child( "M0" ), ctxt}, - {de.child( "M1" ), ctxt}, - {de.child( "M2" ), ctxt}, - {de.child( "M3" ), ctxt}, - {de.child( "M4" ), ctxt}, - {de.child( "M5" ), ctxt}}} {} + , m_id{layerID * FT::nQuarters + iQuarter} // FIXME no always 6 modules ? + , m_modules{{{de.child( "M0" ), ctxt, 0, m_id}, + {de.child( "M1" ), ctxt, 1, m_id}, + {de.child( "M2" ), ctxt, 2, m_id}, + {de.child( "M3" ), ctxt, 3, m_id}, + {de.child( "M4" ), ctxt, 4, m_id}, + {de.child( "M5" ), ctxt, 5, m_id}}} {} LHCb::Detector::detail::DeFTModuleObject::DeFTModuleObject( const dd4hep::DetElement& de, - dd4hep::cond::ConditionUpdateContext& ctxt ) + dd4hep::cond::ConditionUpdateContext& ctxt, + unsigned int iModule, unsigned int quarterID ) : DeIOVObject( de, ctxt ) - , mats{{{de.child( "Mat0" ), ctxt}, - {de.child( "Mat1" ), ctxt}, - {de.child( "Mat2" ), ctxt}, - {de.child( "Mat3" ), ctxt}}} {} + , m_id{quarterID * FT::nModules + iModule} + , m_mats{{{de.child( "Mat0" ), ctxt, 0, m_id}, + {de.child( "Mat1" ), ctxt, 1, m_id}, + {de.child( "Mat2" ), ctxt, 2, m_id}, + {de.child( "Mat3" ), ctxt, 3, m_id}}} { + // Is reversed? + const auto firstPoint = this->toGlobal( {-1, 0, 0} ); + const auto lastPoint = this->toGlobal( {1, 0, 0} ); + m_reversed = std::abs( firstPoint.x() ) > std::abs( lastPoint.x() ); + // Make the plane for the module + const ROOT::Math::XYZPoint g1 = this->toGlobal( ROOT::Math::XYZPoint( 0., 0., 0. ) ); + const ROOT::Math::XYZPoint g2 = this->toGlobal( ROOT::Math::XYZPoint( 1., 0., 0. ) ); + const ROOT::Math::XYZPoint g3 = this->toGlobal( ROOT::Math::XYZPoint( 0., 1., 0. ) ); + m_plane = ROOT::Math::Plane3D( g1, g2, g3 ); + + // Build the FTChannelID + auto localModuleID = FTChannelID::ModuleID{static_cast( m_id % FT::nModules )}; + auto localQuarterID = FTChannelID::QuarterID{static_cast( m_id % ( FT::nModules * FT::nQuarters ) )}; + auto localLayerID = + FTChannelID::LayerID{static_cast( m_id % ( FT::nModules * FT::nQuarters * FT::nLayers ) )}; + auto localStationID = + FTChannelID::StationID{static_cast( m_id / ( FT::nModules * FT::nQuarters * FT::nLayers ) )}; + m_elementID = FTChannelID( localStationID, localLayerID, localQuarterID, localModuleID, 0 ); +} + +LHCb::Detector::detail::DeFTMatObject::DeFTMatObject( const dd4hep::DetElement& de, + dd4hep::cond::ConditionUpdateContext& ctxt, unsigned int iMat, + unsigned int moduleID ) + : DeIOVObject( de, ctxt ), m_elementID{iMat}, m_matID{moduleID * FT::nMats + iMat} { + // FIXME: see VP + // Get some useful geometric parameters from the database + m_halfChannelPitch = 0.5f * m_channelPitch; + + m_sipmPitch = m_nChannelsInSiPM * m_channelPitch + m_dieGap + 2 * m_airGap + 2 * m_deadRegion; + m_nChannelsInDie = m_nChannelsInSiPM / m_nDiesInSiPM; + m_diePitch = m_nChannelsInDie * m_channelPitch + m_dieGap; + m_uBegin = m_airGap + m_deadRegion - 2.f * m_sipmPitch; + + // Update cache + // Get the central points of the fibres at the mirror and at the SiPM locations + m_mirrorPoint = this->toGlobal( ROOT::Math::XYZPoint( 0, -0.5f * m_sizeY, 0 ) ); + m_sipmPoint = this->toGlobal( ROOT::Math::XYZPoint( 0, +0.5f * m_sizeY, 0 ) ); + + // Define the global z position to be at the point closest to the mirror + m_globalZ = m_mirrorPoint.z(); + + // Define the global length in y of the mat + m_globaldy = m_sipmPoint.y() - m_mirrorPoint.y(); + + // Make the plane for the mat + const ROOT::Math::XYZPoint g1 = this->toGlobal( ROOT::Math::XYZPoint( 0., 0., 0. ) ); + const ROOT::Math::XYZPoint g2 = this->toGlobal( ROOT::Math::XYZPoint( 1., 0., 0. ) ); + const ROOT::Math::XYZPoint g3 = this->toGlobal( ROOT::Math::XYZPoint( 0., 1., 0. ) ); + m_plane = ROOT::Math::Plane3D( g1, g2, g3 ); + + // Get the slopes in units of local delta x + m_ddx = ROOT::Math::XYZVectorF( g2 - g1 ); + + // Get the slopes in units of delta y (needed by PrFTHit, mind the sign) + ROOT::Math::XYZVectorF deltaY( g1 - g3 ); + m_dxdy = deltaY.x() / deltaY.y(); + m_dzdy = deltaY.z() / deltaY.y(); +} diff --git a/compact/trunk/FT/parameters.xml b/compact/trunk/FT/parameters.xml index c51896f06a1d186ebf0e91394aea72712d1367dc..a275b3cf8a2d69ba0b2bc4cb9b81c3992837cd0d 100644 --- a/compact/trunk/FT/parameters.xml +++ b/compact/trunk/FT/parameters.xml @@ -169,6 +169,7 @@ +