diff --git a/ForwardDetectors/ZDC/ZdcAnalysis/CMakeLists.txt b/ForwardDetectors/ZDC/ZdcAnalysis/CMakeLists.txt index 166a31ed57aaaa8f56bcef3b9ae4c660b228210a..5d264fdd2efae82b371adc640df134ce22cd59fa 100644 --- a/ForwardDetectors/ZDC/ZdcAnalysis/CMakeLists.txt +++ b/ForwardDetectors/ZDC/ZdcAnalysis/CMakeLists.txt @@ -12,7 +12,7 @@ atlas_add_library( ZdcAnalysisLib PUBLIC_HEADERS ZdcAnalysis INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} LINK_LIBRARIES ${ROOT_LIBRARIES} AsgDataHandlesLib AsgTools CxxUtils xAODEventInfo xAODForward xAODTrigL1Calo - PRIVATE_LINK_LIBRARIES PathResolver ) + PRIVATE_LINK_LIBRARIES PathResolver ) atlas_add_component( ZdcAnalysis src/components/*.cxx diff --git a/ForwardDetectors/ZDC/ZdcRec/CMakeLists.txt b/ForwardDetectors/ZDC/ZdcRec/CMakeLists.txt index 06f389fad1be09ac5841aa5e53e00d05e90aa7c4..8de1b11b3b9611ab33e591806ee83f64ba502602 100644 --- a/ForwardDetectors/ZDC/ZdcRec/CMakeLists.txt +++ b/ForwardDetectors/ZDC/ZdcRec/CMakeLists.txt @@ -13,7 +13,7 @@ atlas_add_library( ZdcRecLib PUBLIC_HEADERS ZdcRec INCLUDE_DIRS ${GSL_INCLUDE_DIRS} PRIVATE_INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} - LINK_LIBRARIES ${GSL_LIBRARIES} AsgTools AthenaBaseComps xAODForward xAODTrigL1Calo ZdcEvent GaudiKernel StoreGateLib ZdcAnalysisLib ZdcByteStreamLib + LINK_LIBRARIES ${GSL_LIBRARIES} AsgTools AthenaBaseComps xAODForward xAODTrigL1Calo ZdcEvent GaudiKernel StoreGateLib ZdcAnalysisLib ZdcByteStreamLib ZdcTrigValidLib PRIVATE_LINK_LIBRARIES ${ROOT_LIBRARIES} CxxUtils ZdcByteStreamLib ZdcConditions ZdcIdentifier ) atlas_add_component( ZdcRec diff --git a/ForwardDetectors/ZDC/ZdcRec/ZdcRec/ZdcRecRun3.h b/ForwardDetectors/ZDC/ZdcRec/ZdcRec/ZdcRecRun3.h index 828803c5db9e0f88ed2608a1dd05ebd7b1e29e5d..505aebdd534b0477b001fd1c8422201b54b9a25c 100755 --- a/ForwardDetectors/ZDC/ZdcRec/ZdcRec/ZdcRecRun3.h +++ b/ForwardDetectors/ZDC/ZdcRec/ZdcRec/ZdcRecRun3.h @@ -28,6 +28,7 @@ class ZdcRecChannelToolLucrod; #include "xAODForward/ZdcModuleContainer.h" #include "xAODForward/ZdcModuleAuxContainer.h" #include "ZdcAnalysis/ZdcAnalysisTool.h" +#include "ZdcTrigValid/ZdcTrigValidTool.h" /** @class ZdcRecRun3 @@ -64,7 +65,8 @@ private: ToolHandle<ZdcRecChannelToolLucrod> m_ChannelTool { this, "ChannelTool", "ZdcRecChannelToolLucrod", "" }; - + ToolHandle<ZDC::IZdcTrigValidTool> m_trigValTool + { this, "TrigValid", "ZdcTrigValidTool",""}; ToolHandleArray<ZDC::IZdcAnalysisTool> m_zdcTools{ this, "ZdcAnalysisTools",{} }; diff --git a/ForwardDetectors/ZDC/ZdcRec/python/ZdcRecConfig.py b/ForwardDetectors/ZDC/ZdcRec/python/ZdcRecConfig.py index 824e7c1f3c78fac9413e1ac2c9251201cc9bc631..840796291e0511be08b9bff223689c666913ebf5 100644 --- a/ForwardDetectors/ZDC/ZdcRec/python/ZdcRecConfig.py +++ b/ForwardDetectors/ZDC/ZdcRec/python/ZdcRecConfig.py @@ -14,6 +14,9 @@ from OutputStreamAthenaPool.OutputStreamConfig import addToESD from TriggerJobOpts.TriggerByteStreamConfig import ByteStreamReadCfg +from TrigConfigSvc.TriggerConfigAccess import getL1MenuAccess +from TrigDecisionTool.TrigDecisionToolConfig import TrigDecisionToolCfg + def ZdcRecOutputCfg(flags): acc = ComponentAccumulator() @@ -45,6 +48,26 @@ def ZdcAnalysisToolCfg(flags, run, config="LHCf2022", DoCalib=False, DoTimeCalib LHCRun = run )) return acc + +def ZdcTrigValToolCfg(flags, config = 'LHCf2022'): + acc = ComponentAccumulator() + + acc.merge(TrigDecisionToolCfg(flags)) + + trigValTool = CompFactory.ZDC.ZdcTrigValidTool( + name = 'ZdcTrigValTool', + WriteAux = True, + AuxSuffix = "", + filepath_LUT = "TrigT1ZDC/zdcRun3T1LUT_v1_30_05_2023.json") + + trigValTool.TrigDecisionTool = acc.getPublicTool('TrigDecisionTool') + + trigValTool.triggerList = [c for c in getL1MenuAccess(flags) if 'L1_ZDC_BIT' in c] + + acc.setPrivateTools(trigValTool) + + return acc + def ZdcRecRun2Cfg(flags): acc = ComponentAccumulator() config = "default" @@ -95,16 +118,21 @@ def ZdcRecRun3Cfg(flags): config = "LHCf2022" elif flags.Input.ProjectName == "data23_hi": config = "PbPb2023" + elif flags.Input.ProjectName == "data22_hi": + config = "PbPb2023" acc.merge(ByteStreamReadCfg(flags, type_names=['xAOD::TriggerTowerContainer/ZdcTriggerTowers', 'xAOD::TriggerTowerAuxContainer/ZdcTriggerTowersAux.'])) acc.addEventAlgo(CompFactory.ZdcByteStreamLucrodData()) anaTool = acc.popToolsAndMerge(ZdcAnalysisToolCfg(flags,3,config,doCalib,doTimeCalib,doTrigEff)) + trigTool = acc.popToolsAndMerge(ZdcTrigValToolCfg(flags,config)) + + zdcTools = [] # expand list as needed + zdcTools += [anaTool] - zdcTools = [anaTool] # expand list as needed - - zdcAlg = CompFactory.ZdcRecRun3("ZdcRecRun3",ZdcAnalysisTools=zdcTools) + + zdcAlg = CompFactory.ZdcRecRun3("ZdcRecRun3",ZdcAnalysisTools=zdcTools, TrigValid = trigTool) acc.addEventAlgo(zdcAlg, primary=True) return acc diff --git a/ForwardDetectors/ZDC/ZdcRec/src/ZdcRecRun3.cxx b/ForwardDetectors/ZDC/ZdcRec/src/ZdcRecRun3.cxx index 4e1e658fdebff44598120579430789186773a3ef..11c880d9ebdf9051c099c4ed9885d78fc6c61d8e 100644 --- a/ForwardDetectors/ZDC/ZdcRec/src/ZdcRecRun3.cxx +++ b/ForwardDetectors/ZDC/ZdcRec/src/ZdcRecRun3.cxx @@ -46,6 +46,9 @@ StatusCode ZdcRecRun3::initialize() // Reconstruction Tool ATH_CHECK( m_ChannelTool.retrieve() ); + + // Trigger Validation Tool + ATH_CHECK( m_trigValTool.retrieve() ); // Reconstruction Tool @@ -117,6 +120,9 @@ StatusCode ZdcRecRun3::execute() // eventually reconstruct RPD, using ML libraries // ATH_CHECK( m_rpdTool...) + + //Use Trigger Validaiton Tool + ATH_CHECK( m_trigValTool->addTrigStatus(*moduleContainer.get(), *moduleSumContainer.get())); SG::WriteHandle<xAOD::ZdcModuleContainer> moduleContainerH (m_zdcModuleContainerName, ctx); ATH_CHECK( moduleContainerH.record (std::move(moduleContainer), diff --git a/ForwardDetectors/ZDC/ZdcTrigValid/CMakeLists.txt b/ForwardDetectors/ZDC/ZdcTrigValid/CMakeLists.txt new file mode 100644 index 0000000000000000000000000000000000000000..dba75310ba9dc9591d8794aead1892c623d1a33e --- /dev/null +++ b/ForwardDetectors/ZDC/ZdcTrigValid/CMakeLists.txt @@ -0,0 +1,20 @@ +# Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration + +# Declare the package name: +atlas_subdir( ZdcTrigValid ) + +# External dependencies: +find_package( ROOT COMPONENTS Core Tree MathCore Hist RIO Minuit Minuit2 HistPainter Graf ) + +# Component(s) in the package: +atlas_add_library( ZdcTrigValidLib + Root/*.cxx + PUBLIC_HEADERS ZdcTrigValid + INCLUDE_DIRS ${ROOT_INCLUDE_DIRS} + LINK_LIBRARIES ${ROOT_LIBRARIES} AsgDataHandlesLib AsgTools TrigDecisionInterface TrigDecisionToolLib CxxUtils xAODEventInfo xAODForward xAODTrigL1Calo ZdcUtilsLib + PRIVATE_LINK_LIBRARIES PathResolver ) + +atlas_add_component( ZdcTrigValid + src/components/*.cxx + LINK_LIBRARIES ZdcTrigValidLib ) + diff --git a/ForwardDetectors/ZDC/ZdcTrigValid/Root/ZdcTrigValidTool.cxx b/ForwardDetectors/ZDC/ZdcTrigValid/Root/ZdcTrigValidTool.cxx new file mode 100644 index 0000000000000000000000000000000000000000..8d8fe6fc8f8c23bd3da3dc5b243e1bfb487e787c --- /dev/null +++ b/ForwardDetectors/ZDC/ZdcTrigValid/Root/ZdcTrigValidTool.cxx @@ -0,0 +1,133 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +#include "ZdcTrigValid/ZdcTrigValidTool.h" +#include "PathResolver/PathResolver.h" +#include <fstream> +#include <bitset> +#include <stdexcept> + +using json = nlohmann::json; + +//********************************************************************** +namespace ZDC{ +ZdcTrigValidTool::ZdcTrigValidTool(const std::string& name) + : asg::AsgTool(name), m_name(name) + { + +#ifndef XAOD_STANDALONE + declareInterface<IZdcTrigValidTool>(this); +#endif + declareProperty("Message", m_msg = ""); + declareProperty("WriteAux", m_writeAux = true); + declareProperty("AuxSuffix", m_auxSuffix = ""); +} + +//********************************************************************** + +ZdcTrigValidTool::~ZdcTrigValidTool() +{ + ATH_MSG_DEBUG("Deleting ZdcTrigValidTool named " << m_name); +} + +StatusCode ZdcTrigValidTool::initialize() { + + ATH_MSG_INFO("Initialising tool " << m_name); + ATH_CHECK(m_trigDecTool.retrieve()); + ATH_MSG_INFO("TDT retrieved"); + + // Find the full path to filename: + std::string file = PathResolverFindCalibFile(m_lutFile); + ATH_MSG_INFO("Reading file " << file); + std::ifstream fin(file.c_str()); + if(!fin){ + ATH_MSG_ERROR("Can not read file: " << file); + return StatusCode::FAILURE; + } + json data = json::parse(fin); + + // Obtain LUTs from Calibration Area + // A data member to hold the side A LUT values + std::array<unsigned int, 4096> sideALUT = data["LucrodLowGain"]["LUTs"]["sideA"]; + // A data member to hold the side C LUT values + std::array<unsigned int, 4096> sideCLUT = data["LucrodLowGain"]["LUTs"]["sideC"]; + // A data member to hold the Combined LUT values + std::array<unsigned int, 256> combLUT = data["LucrodLowGain"]["LUTs"]["comb"]; + + //Construct Trigger Map + m_triggerMap.insert({"L1_ZDC_BIT0",0}); + m_triggerMap.insert({"L1_ZDC_BIT1",1}); + m_triggerMap.insert({"L1_ZDC_BIT2",2}); + + // Construct Simulation Objects + m_modInputs_p = std::make_shared<ZDCTriggerSim::ModuleAmplInputsFloat>(ZDCTriggerSim::ModuleAmplInputsFloat()); + m_simTrig = std::make_shared<ZDCTriggerSimModuleAmpls>(ZDCTriggerSimModuleAmpls(sideALUT, sideCLUT, combLUT)); + ATH_MSG_INFO(m_name<<" Initialised"); + + return StatusCode::SUCCESS; + +} + + +StatusCode ZdcTrigValidTool::addTrigStatus(const xAOD::ZdcModuleContainer& moduleContainer, const xAOD::ZdcModuleContainer& moduleSumContainer) +{ + std::vector<float> moduleEnergy = {0., 0., 0., 0., 0., 0., 0., 0.}; + + bool trigMatch = false; + for (const auto zdcModule : moduleContainer) { + if (zdcModule->zdcType() == 1) continue; + + // Side A + if (zdcModule->zdcSide() > 0) { + moduleEnergy.at(zdcModule->zdcModule()) = + zdcModule->auxdataConst<float>("Amplitude" + m_auxSuffix); + } + + // Side C + if (zdcModule->zdcSide() < 0) { + moduleEnergy.at(zdcModule->zdcModule() + 4) = + zdcModule->auxdataConst<float>("Amplitude" + m_auxSuffix); + } + } + // Get Output as an integer (0-7) + m_modInputs_p->setData(moduleEnergy); + + // call ZDCTriggerSim to actually get ZDC Bits + unsigned int wordOut = m_simTrig->simLevel1Trig(ZDCTriggerSim::SimDataCPtr(m_modInputs_p)); + + // convert int to bitset + std::bitset<3> bin(wordOut); + + // get trigger decision tool + const auto &trigDecTool = m_trigDecTool; + + // iterate through zdc bit output from CTP, validate that they match above bitset + for (const auto &trig : m_triggerList) + { + + if (m_triggerMap.find(trig) == m_triggerMap.end()) + continue; + if (not trigDecTool->isPassed(trig, TrigDefs::requireDecision)) { + ATH_MSG_DEBUG("Chain " << trig << " is passed: NO"); + if (bin[m_triggerMap[trig]] == 0) + trigMatch = true; + continue; + } + ATH_MSG_DEBUG("Chain " << trig << " is passed: YES"); + if (bin[m_triggerMap[trig]] == 1 ) + trigMatch = true; + } + +// write 1 if decision from ZDC firmware matches CTP, 0 otherwize +for(const auto zdc_sum : moduleSumContainer){ + if(m_writeAux) + zdc_sum->auxdecor<unsigned int>("TrigValStatus"+m_auxSuffix) = trigMatch; + } + +ATH_MSG_DEBUG("ZDC Trigger Status: " + << trigMatch); + +return StatusCode::SUCCESS; +} +} //namespace ZDC diff --git a/ForwardDetectors/ZDC/ZdcTrigValid/ZdcTrigValid/ATLAS_CHECK_THREAD_SAFETY b/ForwardDetectors/ZDC/ZdcTrigValid/ZdcTrigValid/ATLAS_CHECK_THREAD_SAFETY new file mode 100644 index 0000000000000000000000000000000000000000..55b6c7652523b80ff618c303e9ddc47f76c26487 --- /dev/null +++ b/ForwardDetectors/ZDC/ZdcTrigValid/ZdcTrigValid/ATLAS_CHECK_THREAD_SAFETY @@ -0,0 +1 @@ +ForwardDetectors/ZDC/ZdcTrigValid diff --git a/ForwardDetectors/ZDC/ZdcTrigValid/ZdcTrigValid/IZdcTrigValidTool.h b/ForwardDetectors/ZDC/ZdcTrigValid/ZdcTrigValid/IZdcTrigValidTool.h new file mode 100644 index 0000000000000000000000000000000000000000..54854e5926b45c880d5a601a21baef801570500f --- /dev/null +++ b/ForwardDetectors/ZDC/ZdcTrigValid/ZdcTrigValid/IZdcTrigValidTool.h @@ -0,0 +1,25 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef IZDCTRIGVALIDTOOL_H__ +#define IZDCTRIGVALIDTOOL_H__ + +#include "AsgTools/IAsgTool.h" +#include "xAODForward/ZdcModuleContainer.h" + +namespace ZDC +{ + +class IZdcTrigValidTool : virtual public asg::IAsgTool +{ + ASG_TOOL_INTERFACE( ZDC::IZdcTrigValidTool ) + + public: + + virtual StatusCode addTrigStatus(const xAOD::ZdcModuleContainer& moduleContainer, const xAOD::ZdcModuleContainer& moduleSumContainer) = 0; + +}; + +} +#endif diff --git a/ForwardDetectors/ZDC/ZdcTrigValid/ZdcTrigValid/ZdcTrigValidTool.h b/ForwardDetectors/ZDC/ZdcTrigValid/ZdcTrigValid/ZdcTrigValidTool.h new file mode 100644 index 0000000000000000000000000000000000000000..ac9cd0ec5efd1201df3516834844a1fc2b1b2465 --- /dev/null +++ b/ForwardDetectors/ZDC/ZdcTrigValid/ZdcTrigValid/ZdcTrigValidTool.h @@ -0,0 +1,58 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +#ifndef ZDCTRIGVALID_ZDCTRIGVALIDTOOL_H +#define ZDCTRIGVALID_ZDCTRIGVALIDTOOL_H + +// Matthew Hoppesch. +// July 2023 +// +// This is a simple tool to compare L1 ZDC decions made in CTP to those made using ZDC firmware +// Used for validation of ZDC firmware in Run3, +// WriteAux Property Determines if Trigger Validation Status is written to xAOD::ZdcModuleContainer + +#include <xAODForward/ZdcModuleAuxContainer.h> +#include "ZdcTrigValid/IZdcTrigValidTool.h" +#include "AsgTools/AsgTool.h" +#include "TrigDecisionTool/TrigDecisionTool.h" +#include "ZdcUtils/ZDCTriggerSim.h" + +#include "nlohmann/json.hpp" + +namespace ZDC { +class ATLAS_NOT_THREAD_SAFE ZdcTrigValidTool : public virtual IZdcTrigValidTool, public asg::AsgTool +{ + ASG_TOOL_CLASS(ZdcTrigValidTool, ZDC::IZdcTrigValidTool) + + public: + ZdcTrigValidTool(const std::string& name); + virtual ~ZdcTrigValidTool() override; + StatusCode initialize() override; + + StatusCode addTrigStatus(const xAOD::ZdcModuleContainer& moduleContainer, const xAOD::ZdcModuleContainer& moduleSumContainer) override; + + protected: + PublicToolHandle<Trig::TrigDecisionTool> m_trigDecTool {this, "TrigDecisionTool",""}; ///< Tool to tell whether a specific trigger is passed + private: + /* properties */ + Gaudi::Property<std::vector<std::string>> m_triggerList{ + this, "triggerList", {}, "Add triggers to this to be monitored"}; + Gaudi::Property<std::string> m_lutFile{this, "filepath_LUT", "TrigT1ZDC/zdcRun3T1LUT_v1_30_05_2023.json", "path to LUT file"}; + + /** A data member to hold the ZDCTrigger Object that stores input floats: shared ptr to ensure cleanup */ + std::shared_ptr<ZDCTriggerSim::ModuleAmplInputsFloat> m_modInputs_p; + + /** A data member to hold the ZDCTrigger Object that computes the LUT logic: shared ptr to ensure cleanup */ + std::shared_ptr<ZDCTriggerSimModuleAmpls> m_simTrig; + + std::string m_msg; + std::map<std::string, unsigned int > m_triggerMap; + std::string m_auxSuffix; + std::string m_name; + bool m_writeAux; + + +}; +} +#endif diff --git a/ForwardDetectors/ZDC/ZdcTrigValid/src/components/ZdcTrigValid_entries.cxx b/ForwardDetectors/ZDC/ZdcTrigValid/src/components/ZdcTrigValid_entries.cxx new file mode 100644 index 0000000000000000000000000000000000000000..f73a37704ea49fb935337ee9ff773b82a6091d51 --- /dev/null +++ b/ForwardDetectors/ZDC/ZdcTrigValid/src/components/ZdcTrigValid_entries.cxx @@ -0,0 +1,7 @@ +#ifndef XAOD_STANDALONE + +#include "ZdcTrigValid/ZdcTrigValidTool.h" + +DECLARE_COMPONENT(ZDC::ZdcTrigValidTool) + +#endif diff --git a/ForwardDetectors/ZDC/ZdcUtils/Root/ZDCTriggerSim.cxx b/ForwardDetectors/ZDC/ZdcUtils/Root/ZDCTriggerSim.cxx new file mode 100644 index 0000000000000000000000000000000000000000..51e45407fdaaccc8286bb1ea5bec4e872c3f5681 --- /dev/null +++ b/ForwardDetectors/ZDC/ZdcUtils/Root/ZDCTriggerSim.cxx @@ -0,0 +1,93 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ + +#include "ZdcUtils/ZDCTriggerSim.h" + +#include <iostream> +#include <stdexcept> + +// dump stream data +void ZDCTriggerSimBase::dump(std::ostream& strm) const { + for (auto entry : m_stack) { + strm << entry->getType() << ": "; + entry->dump(strm); + strm << std::endl; + } +} + +// Obtain 3 bit output from 4*2 bit arm trigger decisions +void ZDCTriggerSimCombLUT::doSimStage() { + ZDCTriggerSim::SimDataCPtr ptr = stackTopData(); + if (ptr->getNumData() != 2 || ptr->getNumBits() != 4) + throw std::logic_error("Invalid input data in ZDCTriggerSimCombLUT"); + + unsigned int bitsSideA = ptr->getValueTrunc(0); + unsigned int bitsSideC = ptr->getValueTrunc(1); + + unsigned int address = (bitsSideC << 4) + bitsSideA; + unsigned int comLUTvalue = m_combLUT.at(address); + + // ZDCTriggerSim::SimDataPtr uses shared_ptr semantics so cleanup is + // guaranteed + // + ZDCTriggerSim::SimDataPtr lutOut_p(new ZDCTriggerSim::CombLUTOutput()); + static_cast<ZDCTriggerSim::CombLUTOutput*>(lutOut_p.get()) + ->setDatum(comLUTvalue); + + stackPush(lutOut_p); +} + +// Obtain 4x2 bit output from arm energy sums +void ZDCTriggerSimAllLUTs::doSimStage() { + ZDCTriggerSim::SimDataCPtr ptr = stackTopData(); + if (ptr->getNumData() != 2 || ptr->getNumBits() != 12) + throw std::logic_error("Invalid input data in ZDCTriggerSimAllLUTs"); + ; + + unsigned int inputSideA = ptr->getValueTrunc(0); + unsigned int inputSideC = ptr->getValueTrunc(1); + + unsigned int valueA = m_LUTA.at(inputSideA); + unsigned int valueC = m_LUTC.at(inputSideC); + + // ZDCTriggerSim::SimDataPtr uses shared_ptr semantics so cleanup is + // guaranteed + // + ZDCTriggerSim::SimDataPtr inputs_p(new ZDCTriggerSim::CombLUTInputsInt); + static_cast<ZDCTriggerSim::CombLUTInputsInt*>(inputs_p.get()) + ->setData({valueA, valueC}); + + stackPush(ZDCTriggerSim::SimDataCPtr(inputs_p)); + ZDCTriggerSimCombLUT::doSimStage(); +} + +// Obtain arm energy sums from Module by Module Calibrated energies +void ZDCTriggerSimModuleAmpls::doSimStage() { + ZDCTriggerSim::SimDataCPtr ptr = stackTopData(); + if (ptr->getNumData() != 8 || ptr->getNumBits() != 12) + throw std::logic_error("Invalid input data in ZDCTriggerSimModuleAmpls"); + + unsigned int sumA = 0; + for (size_t i = 0; i < 4; i++) { + sumA += ptr->getValueTrunc(i); + } + + unsigned int sumC = 0; + for (size_t i = 4; i < 8; i++) { + sumC += ptr->getValueTrunc(i); + } + + // The sums get divided by 4 + // + sumA /= 4; + sumC /= 4; + + ZDCTriggerSim::SimDataPtr inputs_p(new ZDCTriggerSim::SideLUTInputsInt); + static_cast<ZDCTriggerSim::SideLUTInputsInt*>(inputs_p.get()) + ->setData({sumA, sumC}); + + stackPush(ZDCTriggerSim::SimDataCPtr(inputs_p)); + + ZDCTriggerSimAllLUTs::doSimStage(); +} diff --git a/ForwardDetectors/ZDC/ZdcUtils/ZdcUtils/ZDCTriggerSim.h b/ForwardDetectors/ZDC/ZdcUtils/ZdcUtils/ZDCTriggerSim.h new file mode 100644 index 0000000000000000000000000000000000000000..c8b6780570bc588523407bc16916d257563c28b3 --- /dev/null +++ b/ForwardDetectors/ZDC/ZdcUtils/ZdcUtils/ZDCTriggerSim.h @@ -0,0 +1,287 @@ +/* + Copyright (C) 2002-2023 CERN for the benefit of the ATLAS collaboration +*/ +/** + * @file ZdcUtils/ZDCTriggerSim.h + * @author Brian Cole <bcole@cern.ch>, Matthew Hoppesch <mhoppesc@cern.ch> + * @date May 2023 + * @brief A tool to make L1 decision using LUTs + */ + +#ifndef ZDCTRIGVALID__ZDCTriggerSim__h +#define ZDCTRIGVALID__ZDCTriggerSim__h + +#include <array> +#include <cmath> +#include <list> +#include <memory> +#include <ostream> +#include <stdexcept> +#include <type_traits> +#include <vector> + +namespace ZDCTriggerSim { +enum DataType { TCombLUTOutput, TCombLUTInput, TSideLUTsInput, TModAmplsInput }; + +const std::vector<std::string> TypeStrings = {"CombLUTOutput", "CombLUTInput", + "SideLUTsInput", "ModAmplsInput"}; +} // namespace ZDCTriggerSim + +// +// Base class for simulation data. Primarily defines interface and virtual +// destructor +// +class ZDCTriggerSimDataBase { + public: + virtual ~ZDCTriggerSimDataBase() = default; + + virtual unsigned int getNumBits() const = 0; + virtual unsigned int getNumData() const = 0; + virtual ZDCTriggerSim::DataType getType() const = 0; + + virtual unsigned int getValueTrunc(unsigned int idx = 0) const = 0; + virtual void dump(std::ostream& strm) const = 0; +}; + +// Template class that defines data of type T -- usually but not always unsigned +// integer +// with NData different values and which will be truncated to NBits +// +// The class also allows the use of conversion factors that are applied to the +// input values +// when the data is actually used. This allows translation between (e.g.) +// energies and ADC values or any other kind of required conversion. +// +// +template <typename T, unsigned int NData, unsigned int NBits, + ZDCTriggerSim::DataType Type> +class ZDCTriggerSimData : public ZDCTriggerSimDataBase { + bool m_doConvert; + std::vector<float> m_convertFactors; + + std::vector<T> m_data; + bool m_haveData; + + unsigned int doConvTrunc(const T& inValue) const { + unsigned int value = inValue; + + if (m_doConvert) { + value = std::floor(inValue * m_convertFactors.at(0)); + } + + unsigned int valueTruncZero = std::max(static_cast<unsigned int>(0), value); + unsigned int valueTruncBits = + std::min(valueTruncZero, static_cast<unsigned int>((1 << NBits) - 1)); + return valueTruncBits; + } + + // static const unsigned int maskBits = (2<<NBits) - 1; + public: + ZDCTriggerSimData() + : m_doConvert(false), m_data(NData, 0), m_haveData(false) { + static_assert(NData > 0, "ZDCTriggerSimData requires at least one datum"); + static_assert(NBits > 0, "ZDCTriggerSimData requires at least 1 bit"); + } + + ZDCTriggerSimData(const std::vector<float>& conversionFactors) + : m_doConvert(true), + m_convertFactors(conversionFactors), + m_data(NData, 0), + m_haveData(false) { + static_assert(NData > 0, "ZDCTriggerSimData requires at least one datum"); + static_assert(NBits > 0, "ZDCTriggerSimData requires at least 1 bit"); + } + + virtual ~ZDCTriggerSimData() override {} + + unsigned int getNumBits() const override { return NBits; } + unsigned int getNumData() const override { return NData; } + virtual ZDCTriggerSim::DataType getType() const override { return Type; } + + virtual unsigned int getValueTrunc(unsigned int idx = 0) const override { + if (!m_haveData) + throw std::logic_error("No data available for ZDCTriggerSimData"); + return doConvTrunc(m_data.at(idx)); + } + + void setDatum(T datum) { + if (NData != 1) + throw std::logic_error( + "ZDCTriggerSimData setDatum called with NData > 1"); + ; + m_haveData = true; + m_data[0] = datum; + } + + void setData(const std::vector<T>& inData) { + m_data = inData; + m_haveData = true; + } + + void clearData() { m_haveData = false; } + + virtual void dump(std::ostream& strm) const override { + for (auto datum : m_data) { + strm << doConvTrunc(datum) << " "; + } + } +}; + +namespace ZDCTriggerSim { +// The usual way we provide the module amplitudes +// +typedef ZDCTriggerSimData<unsigned int, 8, 12, TModAmplsInput> + ModuleAmplInputsInt; + +// The usual way we provide the module amplitudes +// +typedef ZDCTriggerSimData<float, 8, 12, TModAmplsInput> ModuleAmplInputsFloat; + +// The usual way we provide input to the side LUTs +// +typedef ZDCTriggerSimData<unsigned int, 2, 12, TSideLUTsInput> SideLUTInputsInt; + +// In case we want to be able to convert from energies or other floating input +// +typedef ZDCTriggerSimData<float, 2, 12, TSideLUTsInput> SideLUTInputsFloat; + +// The "usual" way we provide inputs to the combined LUT -- with unsigned +// integers +// +typedef ZDCTriggerSimData<unsigned int, 2, 4, TCombLUTInput> CombLUTInputsInt; + +// In case we want to be able to convert from energies or other float +// to the integer inputs to the combined LUT +// +// typedef ZDCTriggerSimData<float, 2, 4> CombLUTInputsFloat; + +// The combined LUT produces 3 output bits +// +typedef ZDCTriggerSimData<unsigned int, 1, 3, TCombLUTOutput> CombLUTOutput; + +typedef std::shared_ptr<const ZDCTriggerSimDataBase> SimDataCPtr; +typedef std::shared_ptr<ZDCTriggerSimDataBase> SimDataPtr; +} // namespace ZDCTriggerSim + +// Base class for the ZDC trigger simulation. +// +// It is an abstract base that also provides the stack holding the intermediate +// results +// +// +class ZDCTriggerSimBase { + private: + typedef std::list<ZDCTriggerSim::SimDataCPtr> SimStack; + + SimStack m_stack; + + protected: + void stackClear() { m_stack.clear(); } + + void stackPush(const ZDCTriggerSim::SimDataCPtr& ptr) { + m_stack.push_back(SimStack::value_type(ptr)); + } + + const ZDCTriggerSim::SimDataCPtr& stackTopData() const { + return m_stack.back(); + } + + // Take the data on the "top" of the stack and use it as input, adding new + // data to the stack + // + virtual void doSimStage() = 0; + + public: + ZDCTriggerSimBase() = default; + virtual ~ZDCTriggerSimBase() = default; + + // Every implementation of the base should ultimately produce the L1 bits + // possibly (usually) through recursion + // + virtual unsigned int simLevel1Trig( + const ZDCTriggerSim::SimDataCPtr& data) = 0; + + void dump(std::ostream& strm) const; +}; + +class ZDCTriggerSimCombLUT : virtual public ZDCTriggerSimBase { + std::array<unsigned int, 256> m_combLUT; + + protected: + // + // The data on the top of the stack should be the two 4 bit inputs + // to the combined LUT. The output is the combined LUT output + // + virtual void doSimStage() override; + + public: + ZDCTriggerSimCombLUT(const std::array<unsigned int, 256>& inLUT) + : m_combLUT(inLUT) {} + + virtual unsigned int simLevel1Trig( + const ZDCTriggerSim::SimDataCPtr& inputBits) override { + stackClear(); + stackPush(inputBits); + + doSimStage(); + return stackTopData()->getValueTrunc(); + } +}; + +class ZDCTriggerSimAllLUTs : virtual public ZDCTriggerSimBase, + public ZDCTriggerSimCombLUT { + std::array<unsigned int, 4096> m_LUTA; + std::array<unsigned int, 4096> m_LUTC; + + protected: + // + // The data on the top of the stack should be the two 12 bit inputs + // to each of the side LUT. The output is the two side LUT outputs. + // + // After we excute the side LUT, we call the CombLUT doSimStage(); + // + virtual void doSimStage() override; + + public: + ZDCTriggerSimAllLUTs(const std::array<unsigned int, 4096>& sideALUT, + const std::array<unsigned int, 4096>& sideCLUT, + const std::array<unsigned int, 256>& inCombLUT) + : ZDCTriggerSimCombLUT(inCombLUT), m_LUTA(sideALUT), m_LUTC(sideCLUT) {} + + virtual unsigned int simLevel1Trig( + const ZDCTriggerSim::SimDataCPtr& inputData) override { + stackClear(); + stackPush(inputData); + + doSimStage(); + return stackTopData()->getValueTrunc(); + } +}; + +class ZDCTriggerSimModuleAmpls : virtual public ZDCTriggerSimBase, + public ZDCTriggerSimAllLUTs { + protected: + // + // The data on the top of the stack should be the two 12 bit inputs + // to each of the side LUT. The output is the two side LUT outputs. + // + // After we excute the side LUT, we call the CombLUT doSimStage(); + // + virtual void doSimStage() override; + + public: + ZDCTriggerSimModuleAmpls(const std::array<unsigned int, 4096>& sideALUT, + const std::array<unsigned int, 4096>& sideCLUT, + const std::array<unsigned int, 256>& inCombLUT) + : ZDCTriggerSimAllLUTs(sideALUT, sideCLUT, inCombLUT) {} + + virtual unsigned int simLevel1Trig( + const ZDCTriggerSim::SimDataCPtr& inputData) override { + stackClear(); + stackPush(inputData); + + doSimStage(); + return stackTopData()->getValueTrunc(); + } +}; +#endif diff --git a/ForwardDetectors/ZDC/ZdcUtils/src/components/ZdcUtils_entries.cxx b/ForwardDetectors/ZDC/ZdcUtils/src/components/ZdcUtils_entries.cxx index bb8b1bc02261bc26f3e20da37f647f3b506f1b93..d83d0bed9c79ccf65eb2d8eb796df44c876fbde1 100644 --- a/ForwardDetectors/ZDC/ZdcUtils/src/components/ZdcUtils_entries.cxx +++ b/ForwardDetectors/ZDC/ZdcUtils/src/components/ZdcUtils_entries.cxx @@ -1,4 +1,5 @@ #include "ZdcUtils/ZDCWaveform.h" #include "ZdcUtils/ZDCWaveformFermiExp.h" #include "ZdcUtils/ZDCWaveformLTLinStep.h" -#include "ZdcUtils/ZDCWaveformSampler.h" \ No newline at end of file +#include "ZdcUtils/ZDCWaveformSampler.h" +#include "ZdcUtils/ZDCTriggerSim.h"