diff --git a/L0/L0Calo/CMakeLists.txt b/L0/L0Calo/CMakeLists.txt index 732572119cf485485d41800fab3d82d819fef385..8f951b0802f625301870dce8f6be9529ddac6ab9 100644 --- a/L0/L0Calo/CMakeLists.txt +++ b/L0/L0Calo/CMakeLists.txt @@ -3,6 +3,11 @@ ################################################################################ gaudi_subdir(L0Calo v11r16) +# hide warnings from some external projects +find_package(Boost) +find_package(ROOT) +include_directories(SYSTEM ${Boost_INCLUDE_DIRS} ${ROOT_INCLUDE_DIRS}) + gaudi_depends_on_subdirs(Calo/CaloDAQ Calo/CaloUtils Event/LinkerEvent @@ -16,5 +21,8 @@ gaudi_add_module(L0Calo INCLUDE_DIRS AIDA Calo/CaloDAQ LINK_LIBRARIES CaloUtils LinkerEvent L0Base DAQKernelLib) +gaudi_install_python_modules() + gaudi_env(SET L0CALOOPTS \${L0CALOROOT}/options) +gaudi_add_test(QMTest QMTEST) diff --git a/L0/L0Calo/python/L0Calo/Configuration.py b/L0/L0Calo/python/L0Calo/Configuration.py new file mode 100644 index 0000000000000000000000000000000000000000..47b10ac8ea1400397d5b6d6ef81db56b098de8b5 --- /dev/null +++ b/L0/L0Calo/python/L0Calo/Configuration.py @@ -0,0 +1,127 @@ +""" +Configuration tools for L0Calo +""" + +from Configurables import LHCbConfigurableUser +from Configurables import L0CaloAlg, DataOnDemandSvc, L0CaloCandidatesFromRaw +from Configurables import L0CaloCandidatesFromRawBank, ToolSvc +from Gaudi.Configuration import appendPostConfigAction, log + +## @class L0CaloFix2016 +# Configurable to configure L0Calo to handle 2016 electron bug +# Definitions: +# GOOD = the expected correct constants, depend on cell position +# BAD = the wrong constants loaded in the CALO electronics in 2016, +# equal to 127 everywhere +# Use cases: +# +# #1 Simulation was done with GOOD constants (eg Sim09b) and the DST contains +# the CALO RAW banks: in order to represent 2016 data taken with BAD +# constants, use L0CaloFix2016 with options ApproximateMethod = False +# and Simulation = True. Set the TCK property to the one you want to +# simulate (best to use the one that was used for the simulation, +# ie '0x160F' for most cases. In the DaVinci options, set the DB tags to +# these used for the simulation. +# +# #2 Simulation was done with GOOD constants (Sim09b) and the DST do not contain +# the CALO RAW banks: in order to represent 2016 data taken with BAD +# constants, use L0CaloFix2016 with options ApproximateMethod = True +# and Simulation = True. Set the TCK property to the one you want to +# simulate (best to use the one that was used for the simulation, +# ie '0x160F' for most cases.In the DaVinci options, set the DB tags to +# these used for the simulation. +# +# #3 Data was recorded in 2016 with BAD constants and you want to re-run +# L0 with the GOOD constants in order to reject the extra events that +# were triggered because of the BAD constants, in order to obtain a +# coherent sample with 2015, 2017 or 2018. Use ApproximateMethod = False +# and Simulation = False. +# +# NB: Check for messages when running: if there are error messages due +# to missing CALO banks, do not trust the results. Use the approximate +# method instead. +# NB: Only L0 is redone, not HLT. If the HLT depends on L0, it has to be +# ran again. +# +# @author Patrick Robbe <robbe@lal.in2p3.fr> +# @date 2018/03/12 + +class L0CaloFix2016(LHCbConfigurableUser): + + __slots__ = { + # Properties + "ApproximateMethod" : False, + "Simulation" : True , + "TCK" : '0x160F' + } + + __propertyDocDct = { + # Properties + "ApproximateMethod" : """Use approximation on L0Calo to correct energy, otherwise use full method on Calo banks. Useful when Calo banks are not available.""" , + "Simulation" : """True for MC, False for data.""", + "TCK": """The TCK number to emulate""" + } + + def __apply_configuration__(self): + def fixL0Calo( approximate , simulation , tck ): + dod = DataOnDemandSvc() + if not approximate: + l0calo = L0CaloAlg() + l0calo.WriteBanks = False + l0calo.WriteOnTES = True + l0calo.L0CaloADCTool = "CaloTriggerAdcsFromCaloRaw" + from Configurables import CaloTriggerAdcsFromCaloRaw + l0calo.addTool( CaloTriggerAdcsFromCaloRaw , + "EcalTriggerAdcTool" ) + if simulation: + l0calo.EcalTriggerAdcTool.FixFor2016 = True + else: + # for data, take what is in database for the constants + l0calo.EcalTriggerAdcTool.FixFor2016 = False + + dod.AlgMap[ 'Trig/L0/FullCalo' ] = l0calo + dod.AlgMap[ 'Trig/L0/Calo' ] = l0calo + else: + l0calo = L0CaloCandidatesFromRaw("L0CaloFromRaw") + ToolSvc().addTool( L0CaloCandidatesFromRawBank ) + ToolSvc().L0CaloCandidatesFromRawBank.FixFor2016 = True + if simulation: + ToolSvc().L0CaloCandidatesFromRawBank.Simulation = True + else: + # assumes that the correct database tag is given + ToolSvc().L0CaloCandidatesFromRawBank.Simulation = False + l0calo.WriteProcData = True + dod.AlgMap[ 'Trig/L0/FullCalo' ] = l0calo + dod.AlgMap[ 'Trig/L0/Calo' ] = l0calo + + from Configurables import L0DUAlg, L0DUFromRawAlg, GaudiSequencer, L0DUFromRawTool + l0du = L0DUAlg() + l0du.WriteBanks = False + l0du.WriteOnTES = True + if not approximate: + dod.AlgMap[ 'Trig/L0/L0DUCaloData' ] = l0calo + l0du.ProcessorDataLocations = [ 'Trig/L0/L0DUCaloData' , 'Trig/L0/L0DUData' ] + else: + dod.AlgMap[ 'Trig/L0/L0DUL0CaloData' ] = l0calo + l0du.ProcessorDataLocations = [ 'Trig/L0/L0DUL0CaloData' , 'Trig/L0/L0DUData' ] + + l0du.TCK = tck + # CALO + l0seq = GaudiSequencer("L0Seq") + l0raw = L0DUFromRawAlg( WriteProcData = True , WriteOnTES = False ) + l0raw.addTool( L0DUFromRawTool ) + l0raw.L0DUFromRawTool.Emulate = False + l0seq.Members = [ l0raw , l0du ] + dod.AlgMap[ 'Trig/L0/L0DUReport' ] = l0seq + + log.warning( "Apply 2016 L0Calo fix" ) + if self.getProp( "ApproximateMethod" ): + if self.getProp( "Simulation" ): + appendPostConfigAction( lambda tck = self.getProp( "TCK" ): fixL0Calo(True,True,tck) ) + else: + appendPostConfigAction( lambda tck = self.getProp( "TCK" ): fixL0Calo(True,False,tck) ) + else: + if self.getProp( "Simulation" ): + appendPostConfigAction( lambda tck = self.getProp( "TCK" ): fixL0Calo(False,True,tck) ) + else: + appendPostConfigAction( lambda tck = self.getProp( "TCK" ): fixL0Calo(False,False,tck) ) diff --git a/L0/L0Calo/python/L0Calo/__init__.py b/L0/L0Calo/python/L0Calo/__init__.py new file mode 100644 index 0000000000000000000000000000000000000000..e69de29bb2d1d6434b8b29ae775ad8c2e48c5391 diff --git a/L0/L0Calo/src/CaloTriggerAdcsFromCaloRaw.cpp b/L0/L0Calo/src/CaloTriggerAdcsFromCaloRaw.cpp index 9f688c66b079c4e6497860d88398ae2d5ce8787e..8ff53227bc29b3064276fe507ca54ae09f433a41 100644 --- a/L0/L0Calo/src/CaloTriggerAdcsFromCaloRaw.cpp +++ b/L0/L0Calo/src/CaloTriggerAdcsFromCaloRaw.cpp @@ -26,8 +26,16 @@ CaloTriggerAdcsFromCaloRaw::CaloTriggerAdcsFromCaloRaw( const std::string& type, { declareInterface<ICaloTriggerAdcsFromRaw>(this); declareProperty( "DoubleScale" , m_doubleScale = false ) ; + // fix L0Calo to reproduce 2016 bug + declareProperty( "FixFor2016" , m_fixFor2016 = false ) ; + declareProperty( "RawEventLocations" , m_rawEventLocations ) ; int index = name.find_first_of(".",0) +1 ; m_detectorName = name.substr( index, 4 ); + if( "Ecal" == m_detectorName ){ + m_bank = LHCb::RawBank::EcalPacked; + } else if( "Hcal" == m_detectorName ){ + m_bank = LHCb::RawBank::HcalPacked; + } m_data.clear() ; } @@ -53,6 +61,15 @@ StatusCode CaloTriggerAdcsFromCaloRaw::initialize() { return StatusCode::FAILURE ; } + // Initialise the RawEvent locations + if (std::find(m_rawEventLocations.begin(), m_rawEventLocations.end(), + LHCb::RawEventLocation::Default) + == m_rawEventLocations.end()) { + // append the defaults to the search path + m_rawEventLocations.push_back(LHCb::RawEventLocation::Calo); + m_rawEventLocations.push_back(LHCb::RawEventLocation::Default); + } + long nCells = m_calo -> numberOfCells() ; m_data.reserve( nCells ) ; m_data.clear() ; @@ -62,6 +79,20 @@ StatusCode CaloTriggerAdcsFromCaloRaw::initialize() { //============================================================================= const std::vector< LHCb::L0CaloAdc > & CaloTriggerAdcsFromCaloRaw::adcs( ) { + // Retrieve the RawEvent: + LHCb::RawEvent* rawEvt = nullptr ; + for (auto p = m_rawEventLocations.begin(); p != m_rawEventLocations.end() && + ! rawEvt; ++p) { + rawEvt = getIfExists<LHCb::RawEvent>(*p); + } + + if( rawEvt == nullptr ) + Exception( "No CALO Raw bank in the event" ) ; + + bool bank = ( 0 == (rawEvt->banks( m_bank )).size() ) ? false : true ; + if ( ! bank ) + Exception( "No Calo packed bank in the event" ) ; + m_data.clear() ; const CaloVector< LHCb::CaloAdc >& adcs = m_adcs -> adcs( -1 ) ; std::transform( adcs.begin(), adcs.end(), std::back_inserter(m_data), @@ -96,6 +127,9 @@ int CaloTriggerAdcsFromCaloRaw::l0adcFromAdc( const int adc , unsigned long calibCte = m_calo -> cellParam( id ).l0Constant() ; if ( m_doubleScale ) calibCte = calibCte / 2 ; + // for 2016, all constants were set to 127 in ECAL + if ( ( m_fixFor2016 ) && ( id.calo() == 2 ) ) calibCte = 127 ; + int theAdc = adc ; if ( calibCte > 255 ) { diff --git a/L0/L0Calo/src/CaloTriggerAdcsFromCaloRaw.h b/L0/L0Calo/src/CaloTriggerAdcsFromCaloRaw.h index 45fb0c462698a201b9340a4694e3b53bc59e9877..b83bfc29fbdfd0633d9ce33038d0055976594789 100644 --- a/L0/L0Calo/src/CaloTriggerAdcsFromCaloRaw.h +++ b/L0/L0Calo/src/CaloTriggerAdcsFromCaloRaw.h @@ -50,6 +50,10 @@ private: DeCalorimeter * m_calo = nullptr ; bool m_doubleScale ; + bool m_fixFor2016 ; + LHCb::RawBank::BankType m_bank; + /// List of locations in the transient store to search the RawEvent object. + std::vector<std::string> m_rawEventLocations; int l0adcFromAdc( const int adc , const LHCb::CaloCellID & id ) const ; }; diff --git a/L0/L0Calo/src/L0CaloAlg.cpp b/L0/L0Calo/src/L0CaloAlg.cpp index c40d70f983ae7d913a94590ecefee236e9f5b2a0..f0964bb6c52f8b1c238a234aea6a12a09f3866ff 100755 --- a/L0/L0Calo/src/L0CaloAlg.cpp +++ b/L0/L0Calo/src/L0CaloAlg.cpp @@ -324,7 +324,11 @@ StatusCode L0CaloAlg::initialize() { //============================================================================= StatusCode L0CaloAlg::execute() { // Get the ECAL data, store them in the Front-End card - sumEcalData( ); + try { sumEcalData( ); } + catch ( const GaudiException & exc ) { + fatal() << "The L0Calo trigger will be wrong, dont rely on it" << endmsg ; + return StatusCode::FAILURE ; + } // Get Spd+Prs data m_PrsSpdIds = m_bitsFromRaw -> prsSpdCells( ) ; @@ -414,8 +418,12 @@ StatusCode L0CaloAlg::execute() { } // eCard // Do a similar processing for HCAL Data - sumHcalData( ); - + try { sumHcalData( ); } + catch ( const GaudiException & exc ) { + fatal() << "The L0Calo trigger will be wrong, dont rely on it" << endmsg ; + return StatusCode::FAILURE ; + } + // Add the highest ECAL energy in matching cards and store // the candidates (one per Selection Board input, in 3 vectors // one vector per SB: (0 - master, 1 - slave1, 2 - slave2) diff --git a/L0/L0Calo/src/L0CaloCandidatesFromRaw.cpp b/L0/L0Calo/src/L0CaloCandidatesFromRaw.cpp index 1ee607f1442879fe064eea6f34a9953db8337913..20b6bbe91fc5645c0ba0608d29473d2784468f33 100644 --- a/L0/L0Calo/src/L0CaloCandidatesFromRaw.cpp +++ b/L0/L0Calo/src/L0CaloCandidatesFromRaw.cpp @@ -66,12 +66,6 @@ StatusCode L0CaloCandidatesFromRaw::execute() { readoutStatus.addStatus( 0 , LHCb::RawBankReadoutStatus::Status::OK ) ; readoutStatus.addStatus( 1 , LHCb::RawBankReadoutStatus::Status::OK ) ; - // Scan the list of input location and select the first existing one. - // no need to do this any longer, the base class takes care of it - //std::string rawEventLocation; - //if ( selectRawEventLocation(rawEventLocation).isFailure() ) - // return Error("No valid raw event location found",StatusCode::SUCCESS,50); - LHCb::L0CaloCandidates * outFull = new LHCb::L0CaloCandidates( ) ; LHCb::L0CaloCandidates * out = new LHCb::L0CaloCandidates() ; diff --git a/L0/L0Calo/src/L0CaloCandidatesFromRaw.h b/L0/L0Calo/src/L0CaloCandidatesFromRaw.h index 2994b0f088811c712730e02ddfdc3a855e17f7ab..60eaadbe986814e19273d36b48968bc0c4427449 100755 --- a/L0/L0Calo/src/L0CaloCandidatesFromRaw.h +++ b/L0/L0Calo/src/L0CaloCandidatesFromRaw.h @@ -67,6 +67,5 @@ private: } return false; } - }; #endif // L0CALOCANDIDATESFROMRAW_H diff --git a/L0/L0Calo/src/L0CaloCandidatesFromRawBank.cpp b/L0/L0Calo/src/L0CaloCandidatesFromRawBank.cpp index 276811fd70e46e9e167707b81fe2e8c666d34116..b709145e6168a1df28ae936f62a0e66a7085612c 100755 --- a/L0/L0Calo/src/L0CaloCandidatesFromRawBank.cpp +++ b/L0/L0Calo/src/L0CaloCandidatesFromRawBank.cpp @@ -36,6 +36,10 @@ L0CaloCandidatesFromRawBank::L0CaloCandidatesFromRawBank // Store also the intermediate informations for debugging the Selection boards // such as the partial results of the hadron Selection Boards declareProperty( "DebugDecoding" , m_doDebugDecoding = false ) ; + // Correct the L0electron and photon energies to reflect 2016 bug + declareProperty( "FixFor2016" , m_fixFor2016 = false ) ; + // Data or simulation (relevant for FixFor2016 only !) + declareProperty( "Simulation" , m_simulation = false ) ; } //============================================================================= @@ -311,6 +315,13 @@ void L0CaloCandidatesFromRawBank::convertRawBankToTES } } } else { + // ECAL trigger + // Apply 2016 correction here if requested + if ( m_fixFor2016 ) { + et = correctedEnergy( et , id ) ; + if ( et > 255 ) et = 255 ; + } + myL0Cand = new LHCb::L0CaloCandidate ( type, id, et, et * m_etScale, center, tol ); @@ -389,3 +400,15 @@ void L0CaloCandidatesFromRawBank::convertRawBankToTES outFull -> add( *it ) ; } //============================================================================= +// Function to correct ECAL energy (approximatively) for 2016 bug +//============================================================================= +int L0CaloCandidatesFromRawBank::correctedEnergy( const int oldEnergy , + const LHCb::CaloCellID & id ) + const +{ + // Get correct calib constant + unsigned long calibCte = m_ecal -> cellParam( id ).l0Constant() ; + // compute energy with wrong calib constant (127) + if ( m_simulation ) return (int) ( (double) oldEnergy * 127. / ( (double) calibCte ) ) ; + return (int) ( (double) oldEnergy * ( (double) calibCte ) / 127. ) ; +} diff --git a/L0/L0Calo/src/L0CaloCandidatesFromRawBank.h b/L0/L0Calo/src/L0CaloCandidatesFromRawBank.h index d66e60313b663e285aa1557e3578c631b006e72e..ec5825426cec47017a573c82d93f16e7aaa6c17b 100755 --- a/L0/L0Calo/src/L0CaloCandidatesFromRawBank.h +++ b/L0/L0Calo/src/L0CaloCandidatesFromRawBank.h @@ -53,5 +53,9 @@ private: DeCalorimeter* m_hcal; ///< HCAL detector element double m_etScale; ///< Conversion int -> Et. bool m_doDebugDecoding ; ///< Store intermediate information for debug + bool m_fixFor2016 ; ///< Fix energy to correspond to 2016 conditions + bool m_simulation ; ///< Data or MC, relevant only for fixFor2016 + + int correctedEnergy( const int oldEnergy , const LHCb::CaloCellID & id ) const ; }; #endif // L0CALO_L0CALOCANDIDATESFROMRAWBANK_H diff --git a/L0/L0Calo/src/L0Candidate.h b/L0/L0Calo/src/L0Candidate.h index da9e24037ed1ceee6b979fbbc7a0b2b39e43be7e..a3bb0bc3a18fd6476d696a007367164fd41cb58d 100644 --- a/L0/L0Calo/src/L0Candidate.h +++ b/L0/L0Calo/src/L0Candidate.h @@ -49,12 +49,15 @@ public: */ void saveCandidate( L0DUBase::Fiber::Type type , LHCb::L0ProcessorDatas* L0Calo ) { - unsigned int word = 0x10000 | - ( m_ID.index() << L0DUBase::Calo::Address::Shift) | - ( m_et << L0DUBase::Calo::Et::Shift ) ; - if( type == L0DUBase::Fiber::CaloSumEt || type == L0DUBase::Fiber::CaloSpdMult) - word = 0x10000 | m_et << L0DUBase::Calo::Sum::Shift; - + unsigned int word = 0 ; + if ( L0DUBase::Fiber::CaloSumEt == type ) + word = 0x10000 + ( m_et << L0DUBase::Calo::Sum::Shift ) ; + else if ( L0DUBase::Fiber::CaloSpdMult == type ) + word = 0x10000 + ( m_et << L0DUBase::Calo::Sum::Shift ) ; + else + word = 0x10000 | + ( m_ID.index() << L0DUBase::Calo::Address::Shift) | + ( m_et << L0DUBase::Calo::Et::Shift ) ; LHCb::L0ProcessorData * temp = new LHCb::L0ProcessorData ( type , word ) ; L0Calo -> add( temp ); } diff --git a/L0/L0Calo/tests/qmtest/l0calo.qms/testL0CaloFix2016.qmt b/L0/L0Calo/tests/qmtest/l0calo.qms/testL0CaloFix2016.qmt new file mode 100644 index 0000000000000000000000000000000000000000..9245d5387eaa68c1a4abec4363bfbb0ca1078b92 --- /dev/null +++ b/L0/L0Calo/tests/qmtest/l0calo.qms/testL0CaloFix2016.qmt @@ -0,0 +1,9 @@ +<?xml version="1.0" ?><!DOCTYPE extension PUBLIC '-//QM/2.3/Extension//EN' 'http://www.codesourcery.com/qm/dtds/2.3/-//qm/2.3/extension//en.dtd'> +<extension class="GaudiTest.GaudiExeTest" kind="test"> +<argument name="program"><text>../scripts/testL0CaloFix2016.py</text></argument> +<argument name="validator"><text> +findReferenceBlock(""" +Number of L0 electron decisions = 515 +""") +</text></argument> +</extension> diff --git a/L0/L0Calo/tests/scripts/testL0CaloFix2016.py b/L0/L0Calo/tests/scripts/testL0CaloFix2016.py new file mode 100644 index 0000000000000000000000000000000000000000..d3b62644e61ff89a41660d5ef2a38d0c4819303a --- /dev/null +++ b/L0/L0Calo/tests/scripts/testL0CaloFix2016.py @@ -0,0 +1,24 @@ +from Configurables import LHCbApp +from PRConfig import TestFileDB +TestFileDB.test_file_db['2016-lb2l0gamma.strip.dst'].run(configurable=LHCbApp()) + +from Configurables import L0Conf, DataOnDemandSvc, ApplicationMgr +ApplicationMgr().ExtSvc += [ DataOnDemandSvc() ] +L0Conf( FullL0MuonDecoding = True , EnableL0DecodingOnDemand = True ) + +from Configurables import L0CaloFix2016 +L0CaloFix2016( ApproximateMethod = False , Simulation = True , TCK = '0x160F' ) + +import GaudiPython +appMgr = GaudiPython.AppMgr() +evt = appMgr.evtsvc() + +counter_electron = 0 + +for i in xrange( 1000 ) : + appMgr.run(1) + l0du = evt['Trig/L0/L0DUReport'] + if l0du.channelDecisionByName( "Electron" ): + counter_electron += 1 + +print "Number of L0 electron decisions = ", counter_electron